// copyright (c) 2007 magnus auvinen, see licence.txt for more info #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ed_editor.h" #include #include int CEditor::ms_CheckerTexture; int CEditor::ms_BackgroundTexture; int CEditor::ms_CursorTexture; int CEditor::ms_EntitiesTexture; const void* CEditor::ms_pUiGotContext; enum { BUTTON_CONTEXT=1, }; CEditorImage::~CEditorImage() { m_pEditor->Graphics()->UnloadTexture(m_TexId); } CLayerGroup::CLayerGroup() { m_pName = ""; m_Visible = true; m_GameGroup = false; m_OffsetX = 0; m_OffsetY = 0; m_ParallaxX = 100; m_ParallaxY = 100; m_UseClipping = 0; m_ClipX = 0; m_ClipY = 0; m_ClipW = 0; m_ClipH = 0; } CLayerGroup::~CLayerGroup() { Clear(); } void CLayerGroup::Convert(CUIRect *pRect) { pRect->x += m_OffsetX; pRect->y += m_OffsetY; } void CLayerGroup::Mapping(float *pPoints) { m_pMap->m_pEditor->RenderTools()->MapscreenToWorld( m_pMap->m_pEditor->m_WorldOffsetX, m_pMap->m_pEditor->m_WorldOffsetY, m_ParallaxX/100.0f, m_ParallaxY/100.0f, m_OffsetX, m_OffsetY, m_pMap->m_pEditor->Graphics()->ScreenAspect(), m_pMap->m_pEditor->m_WorldZoom, pPoints); pPoints[0] += m_pMap->m_pEditor->m_EditorOffsetX; pPoints[1] += m_pMap->m_pEditor->m_EditorOffsetY; pPoints[2] += m_pMap->m_pEditor->m_EditorOffsetX; pPoints[3] += m_pMap->m_pEditor->m_EditorOffsetY; } void CLayerGroup::MapScreen() { float aPoints[4]; Mapping(aPoints); m_pMap->m_pEditor->Graphics()->MapScreen(aPoints[0], aPoints[1], aPoints[2], aPoints[3]); } void CLayerGroup::Render() { MapScreen(); IGraphics *pGraphics = m_pMap->m_pEditor->Graphics(); if(m_UseClipping) { float aPoints[4]; m_pMap->m_pGameGroup->Mapping(aPoints); float x0 = (m_ClipX - aPoints[0]) / (aPoints[2]-aPoints[0]); float y0 = (m_ClipY - aPoints[1]) / (aPoints[3]-aPoints[1]); float x1 = ((m_ClipX+m_ClipW) - aPoints[0]) / (aPoints[2]-aPoints[0]); float y1 = ((m_ClipY+m_ClipH) - aPoints[1]) / (aPoints[3]-aPoints[1]); pGraphics->ClipEnable((int)(x0*pGraphics->ScreenWidth()), (int)(y0*pGraphics->ScreenHeight()), (int)((x1-x0)*pGraphics->ScreenWidth()), (int)((y1-y0)*pGraphics->ScreenHeight())); } for(int i = 0; i < m_lLayers.size(); i++) { if(m_lLayers[i]->m_Visible && m_lLayers[i] != m_pMap->m_pGameLayer) { if(m_pMap->m_pEditor->m_ShowDetail || !(m_lLayers[i]->m_Flags&LAYERFLAG_DETAIL)) m_lLayers[i]->Render(); } } pGraphics->ClipDisable(); } void CLayerGroup::DeleteLayer(int Index) { if(Index < 0 || Index >= m_lLayers.size()) return; delete m_lLayers[Index]; m_lLayers.remove_index(Index); } void CLayerGroup::GetSize(float *w, float *h) { *w = 0; *h = 0; for(int i = 0; i < m_lLayers.size(); i++) { float lw, lh; m_lLayers[i]->GetSize(&lw, &lh); *w = max(*w, lw); *h = max(*h, lh); } } int CLayerGroup::SwapLayers(int Index0, int Index1) { if(Index0 < 0 || Index0 >= m_lLayers.size()) return Index0; if(Index1 < 0 || Index1 >= m_lLayers.size()) return Index0; if(Index0 == Index1) return Index0; swap(m_lLayers[Index0], m_lLayers[Index1]); return Index1; } void CEditorImage::AnalyseTileFlags() { mem_zero(m_aTileFlags, sizeof(m_aTileFlags)); int tw = m_Width/16; // tilesizes int th = m_Height/16; if ( tw == th ) { unsigned char *pPixelData = (unsigned char *)m_pData; int TileId = 0; for(int ty = 0; ty < 16; ty++) for(int tx = 0; tx < 16; tx++, TileId++) { bool Opaque = true; for(int x = 0; x < tw; x++) for(int y = 0; y < th; y++) { int p = (ty*tw+y)*m_Width + tx*tw+x; if(pPixelData[p*4+3] < 250) { Opaque = false; break; } } if(Opaque) m_aTileFlags[TileId] |= TILEFLAG_OPAQUE; } } } /******************************************************** OTHER *********************************************************/ // copied from gc_menu.cpp, should be more generalized //extern int ui_do_edit_box(void *id, const CUIRect *rect, char *str, int str_size, float font_size, bool hidden=false); int CEditor::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrSize, float FontSize, bool Hidden) { int Inside = UI()->MouseInside(pRect); int ReturnValue = 0; static int s_AtIndex = 0; if(UI()->LastActiveItem() == pID) { int Len = str_length(pStr); if(Inside && UI()->MouseButton(0)) { int MxRel = (int)(UI()->MouseX() - pRect->x); for (int i = 1; i <= Len; i++) { if (TextRender()->TextWidth(0, FontSize, pStr, i) + 10 > MxRel) { s_AtIndex = i - 1; break; } if (i == Len) s_AtIndex = Len; } } for(int i = 0; i < Input()->NumEvents(); i++) { Len = str_length(pStr); CLineInput::Manipulate(Input()->GetEvent(i), pStr, StrSize, &Len, &s_AtIndex); } } bool JustGotActive = false; if(UI()->ActiveItem() == pID) { if(!UI()->MouseButton(0)) UI()->SetActiveItem(0); } else if(UI()->HotItem() == pID) { if(UI()->MouseButton(0)) { if (UI()->LastActiveItem() != pID) JustGotActive = true; UI()->SetActiveItem(pID); } } if(Inside) UI()->SetHotItem(pID); CUIRect Textbox = *pRect; RenderTools()->DrawUIRect(&Textbox, vec4(1,1,1,0.5f), CUI::CORNER_ALL, 3.0f); Textbox.VMargin(3.0f, &Textbox); const char *pDisplayStr = pStr; char aStars[128]; if(Hidden) { unsigned s = str_length(pStr); if(s >= sizeof(aStars)) s = sizeof(aStars)-1; for(unsigned int i = 0; i < s; ++i) aStars[i] = '*'; aStars[s] = 0; pDisplayStr = aStars; } UI()->DoLabel(&Textbox, pDisplayStr, FontSize, -1); //TODO: make it blink if(UI()->LastActiveItem() == pID && !JustGotActive) { float w = TextRender()->TextWidth(0, FontSize, pDisplayStr, s_AtIndex); Textbox = *pRect; Textbox.VSplitLeft(2.0f, 0, &Textbox); Textbox.x += w*UI()->Scale(); Textbox.y -= FontSize/10.f; UI()->DoLabel(&Textbox, "|", FontSize*1.1f, -1); } return ReturnValue; } vec4 CEditor::ButtonColorMul(const void *pId) { if(UI()->ActiveItem() == pId) return vec4(1,1,1,0.5f); else if(UI()->HotItem() == pId) return vec4(1,1,1,1.5f); return vec4(1,1,1,1); } float CEditor::UiDoScrollbarV(const void *pId, const CUIRect *pRect, float Current) { CUIRect Handle; static float s_OffsetY; pRect->HSplitTop(33, &Handle, 0); Handle.y += (pRect->h-Handle.h)*Current; // logic float Ret = Current; int Inside = UI()->MouseInside(&Handle); if(UI()->ActiveItem() == pId) { if(!UI()->MouseButton(0)) UI()->SetActiveItem(0); float Min = pRect->y; float Max = pRect->h-Handle.h; float Cur = UI()->MouseY()-s_OffsetY; Ret = (Cur-Min)/Max; if(Ret < 0.0f) Ret = 0.0f; if(Ret > 1.0f) Ret = 1.0f; } else if(UI()->HotItem() == pId) { if(UI()->MouseButton(0)) { UI()->SetActiveItem(pId); s_OffsetY = UI()->MouseY()-Handle.y; } } if(Inside) UI()->SetHotItem(pId); // render CUIRect Rail; pRect->VMargin(5.0f, &Rail); RenderTools()->DrawUIRect(&Rail, vec4(1,1,1,0.25f), 0, 0.0f); CUIRect Slider = Handle; Slider.w = Rail.x-Slider.x; RenderTools()->DrawUIRect(&Slider, vec4(1,1,1,0.25f), CUI::CORNER_L, 2.5f); Slider.x = Rail.x+Rail.w; RenderTools()->DrawUIRect(&Slider, vec4(1,1,1,0.25f), CUI::CORNER_R, 2.5f); Slider = Handle; Slider.Margin(5.0f, &Slider); RenderTools()->DrawUIRect(&Slider, vec4(1,1,1,0.25f)*ButtonColorMul(pId), CUI::CORNER_ALL, 2.5f); return Ret; } vec4 CEditor::GetButtonColor(const void *pId, int Checked) { if(Checked < 0) return vec4(0,0,0,0.5f); if(Checked > 0) { if(UI()->HotItem() == pId) return vec4(1,0,0,0.75f); return vec4(1,0,0,0.5f); } if(UI()->HotItem() == pId) return vec4(1,1,1,0.75f); return vec4(1,1,1,0.5f); } int CEditor::DoButton_Editor_Common(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip) { if(UI()->MouseInside(pRect)) { if(Flags&BUTTON_CONTEXT) ms_pUiGotContext = pID; if(m_pTooltip) m_pTooltip = pToolTip; } if(UI()->HotItem() == pID && pToolTip) m_pTooltip = (const char *)pToolTip; return UI()->DoButtonLogic(pID, pText, Checked, pRect); // Draw here //return UI()->DoButton(id, text, checked, r, draw_func, 0); } int CEditor::DoButton_Editor(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip) { RenderTools()->DrawUIRect(pRect, GetButtonColor(pID, Checked), CUI::CORNER_ALL, 3.0f); CUIRect NewRect = *pRect; NewRect.y += NewRect.h/2.0f-7.0f; UI()->DoLabel(&NewRect, pText, 10, 0, -1); return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip); } int CEditor::DoButton_File(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip) { if(UI()->HotItem() == pID) RenderTools()->DrawUIRect(pRect, GetButtonColor(pID, Checked), CUI::CORNER_ALL, 3.0f); CUIRect t = *pRect; t.VMargin(5.0f, &t); UI()->DoLabel(&t, pText, 10, -1, -1); return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip); } int CEditor::DoButton_Menu(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip) { CUIRect r = *pRect; RenderTools()->DrawUIRect(&r, vec4(0.5f, 0.5f, 0.5f, 1.0f), CUI::CORNER_T, 3.0f); r = *pRect; r.VMargin(5.0f, &r); UI()->DoLabel(&r, pText, 10, -1, -1); return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip); } int CEditor::DoButton_MenuItem(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip) { if(UI()->HotItem() == pID || Checked) RenderTools()->DrawUIRect(pRect, GetButtonColor(pID, Checked), CUI::CORNER_ALL, 3.0f); CUIRect t = *pRect; t.VMargin(5.0f, &t); UI()->DoLabel(&t, pText, 10, -1, -1); return DoButton_Editor_Common(pID, pText, Checked, pRect, 0, 0); } int CEditor::DoButton_Tab(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip) { RenderTools()->DrawUIRect(pRect, GetButtonColor(pID, Checked), CUI::CORNER_T, 5.0f); CUIRect NewRect = *pRect; NewRect.y += NewRect.h/2.0f-7.0f; UI()->DoLabel(&NewRect, pText, 10, 0, -1); return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip); } int CEditor::DoButton_Ex(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip, int Corners) { RenderTools()->DrawUIRect(pRect, GetButtonColor(pID, Checked), Corners, 3.0f); CUIRect NewRect = *pRect; NewRect.y += NewRect.h/2.0f-7.0f; UI()->DoLabel(&NewRect, pText, 10, 0, -1); return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip); } int CEditor::DoButton_ButtonInc(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip) { RenderTools()->DrawUIRect(pRect, GetButtonColor(pID, Checked), CUI::CORNER_R, 3.0f); UI()->DoLabel(pRect, pText?pText:"+", 10, 0, -1); return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip); } int CEditor::DoButton_ButtonDec(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip) { RenderTools()->DrawUIRect(pRect, GetButtonColor(pID, Checked), CUI::CORNER_L, 3.0f); UI()->DoLabel(pRect, pText?pText:"-", 10, 0, -1); return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip); } void CEditor::RenderBackground(CUIRect View, int Texture, float Size, float Brightness) { Graphics()->TextureSet(Texture); Graphics()->BlendNormal(); Graphics()->QuadsBegin(); Graphics()->SetColor(Brightness, Brightness, Brightness, 1.0f); Graphics()->QuadsSetSubset(0,0, View.w/Size, View.h/Size); IGraphics::CQuadItem QuadItem(View.x, View.y, View.w, View.h); Graphics()->QuadsDrawTL(&QuadItem, 1); Graphics()->QuadsEnd(); } int CEditor::UiDoValueSelector(void *pId, CUIRect *r, const char *pLabel, int Current, int Min, int Max, float Scale) { // logic static float s_Value; int Ret = 0; int Inside = UI()->MouseInside(r); if(UI()->ActiveItem() == pId) { if(!UI()->MouseButton(0)) { if(Inside) Ret = 1; m_LockMouse = false; UI()->SetActiveItem(0); } else { if(Input()->KeyPressed(KEY_LSHIFT) || Input()->KeyPressed(KEY_RSHIFT)) s_Value += m_MouseDeltaX*0.05f; else s_Value += m_MouseDeltaX; if(absolute(s_Value) > Scale) { int Count = (int)(s_Value/Scale); s_Value = fmod(s_Value, Scale); Current += Count; if(Current < Min) Current = Min; if(Current > Max) Current = Max; } } } else if(UI()->HotItem() == pId) { if(UI()->MouseButton(0)) { m_LockMouse = true; s_Value = 0; UI()->SetActiveItem(pId); } } if(Inside) UI()->SetHotItem(pId); // render char aBuf[128]; str_format(aBuf, sizeof(aBuf),"%s %d", pLabel, Current); RenderTools()->DrawUIRect(r, GetButtonColor(pId, 0), CUI::CORNER_ALL, 5.0f); r->y += r->h/2.0f-7.0f; UI()->DoLabel(r, aBuf, 10, 0, -1); return Current; } CLayerGroup *CEditor::GetSelectedGroup() { if(m_SelectedGroup >= 0 && m_SelectedGroup < m_Map.m_lGroups.size()) return m_Map.m_lGroups[m_SelectedGroup]; return 0x0; } CLayer *CEditor::GetSelectedLayer(int Index) { CLayerGroup *pGroup = GetSelectedGroup(); if(!pGroup) return 0x0; if(m_SelectedLayer >= 0 && m_SelectedLayer < m_Map.m_lGroups[m_SelectedGroup]->m_lLayers.size()) return pGroup->m_lLayers[m_SelectedLayer]; return 0x0; } CLayer *CEditor::GetSelectedLayerType(int Index, int Type) { CLayer *p = GetSelectedLayer(Index); if(p && p->m_Type == Type) return p; return 0x0; } CQuad *CEditor::GetSelectedQuad() { CLayerQuads *ql = (CLayerQuads *)GetSelectedLayerType(0, LAYERTYPE_QUADS); if(!ql) return 0; if(m_SelectedQuad >= 0 && m_SelectedQuad < ql->m_lQuads.size()) return &ql->m_lQuads[m_SelectedQuad]; return 0; } static void CallbackOpenMap(const char *pFileName, void *pUser) { if(((CEditor*)pUser)->Load(pFileName)) str_copy(((CEditor*)pUser)->m_aFileName, pFileName, 512); } static void CallbackAppendMap(const char *pFileName, void *pUser) { if(((CEditor*)pUser)->Append(pFileName)) ((CEditor*)pUser)->m_aFileName[0] = 0; } static void CallbackSaveMap(const char *pFileName, void *pUser){ if(((CEditor*)pUser)->Save(pFileName)) str_copy(((CEditor*)pUser)->m_aFileName, pFileName, 512); } void CEditor::DoToolbar(CUIRect ToolBar) { CUIRect TB_Top, TB_Bottom; CUIRect Button; ToolBar.HSplitTop(ToolBar.h/2.0f, &TB_Top, &TB_Bottom); TB_Top.HSplitBottom(2.5f, &TB_Top, 0); TB_Bottom.HSplitTop(2.5f, 0, &TB_Bottom); // ctrl+o to open if(Input()->KeyDown('o') && (Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL))) InvokeFileDialog(IStorage::TYPE_ALL, Localize("Open map"), Localize("Open"), "maps/", "", CallbackOpenMap, this); // ctrl+s to save if(Input()->KeyDown('s') && (Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL))) { if(m_aFileName[0]) Save(m_aFileName); else InvokeFileDialog(IStorage::TYPE_SAVE, Localize("Save map"), Localize("Save"), "maps/", "", CallbackSaveMap, this); } // detail button TB_Top.VSplitLeft(30.0f, &Button, &TB_Top); static int s_HqButton = 0; if(DoButton_Editor(&s_HqButton, Localize("HD"), m_ShowDetail, &Button, 0, Localize("[ctrl+h] Toggle High Detail")) || (Input()->KeyDown('h') && (Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL)))) { m_ShowDetail = !m_ShowDetail; } TB_Top.VSplitLeft(5.0f, 0, &TB_Top); // animation button TB_Top.VSplitLeft(40.0f, &Button, &TB_Top); static int s_AnimateButton = 0; if(DoButton_Editor(&s_AnimateButton, Localize("Anim"), m_Animate, &Button, 0, ("[ctrl+m] Toggle animation")) || (Input()->KeyDown('m') && (Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL)))) { m_AnimateStart = time_get(); m_Animate = !m_Animate; } TB_Top.VSplitLeft(5.0f, 0, &TB_Top); // proof button TB_Top.VSplitLeft(40.0f, &Button, &TB_Top); static int s_ProofButton = 0; if(DoButton_Editor(&s_ProofButton, Localize("Proof"), m_ProofBorders, &Button, 0, Localize("[ctrl+p] Toggles proof borders. These borders represent what a player maximum can see.")) || (Input()->KeyDown('p') && (Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL)))) { m_ProofBorders = !m_ProofBorders; } TB_Top.VSplitLeft(15.0f, 0, &TB_Top); // zoom group TB_Top.VSplitLeft(30.0f, &Button, &TB_Top); static int s_ZoomOutButton = 0; if(DoButton_Ex(&s_ZoomOutButton, Localize("ZO"), 0, &Button, 0, Localize("[NumPad-] Zoom out"), CUI::CORNER_L) || Input()->KeyDown(KEY_KP_MINUS)) m_ZoomLevel += 50; TB_Top.VSplitLeft(30.0f, &Button, &TB_Top); static int s_ZoomNormalButton = 0; if(DoButton_Ex(&s_ZoomNormalButton, "1:1", 0, &Button, 0, Localize("[NumPad*] Zoom to normal and remove editor offset"), 0) || Input()->KeyDown(KEY_KP_MULTIPLY)) { m_EditorOffsetX = 0; m_EditorOffsetY = 0; m_ZoomLevel = 100; } TB_Top.VSplitLeft(30.0f, &Button, &TB_Top); static int s_ZoomInButton = 0; if(DoButton_Ex(&s_ZoomInButton, Localize("ZI"), 0, &Button, 0, Localize("[NumPad+] Zoom in"), CUI::CORNER_R) || Input()->KeyDown(KEY_KP_PLUS)) m_ZoomLevel -= 50; TB_Top.VSplitLeft(10.0f, 0, &TB_Top); // animation speed TB_Top.VSplitLeft(30.0f, &Button, &TB_Top); static int s_AnimFasterButton = 0; if(DoButton_Ex(&s_AnimFasterButton, "A+", 0, &Button, 0, Localize("Increase animation speed"), CUI::CORNER_L)) m_AnimateSpeed += 0.5f; TB_Top.VSplitLeft(30.0f, &Button, &TB_Top); static int s_AnimNormalButton = 0; if(DoButton_Ex(&s_AnimNormalButton, "1", 0, &Button, 0, Localize("Normal animation speed"), 0)) m_AnimateSpeed = 1.0f; TB_Top.VSplitLeft(30.0f, &Button, &TB_Top); static int s_AnimSlowerButton = 0; if(DoButton_Ex(&s_AnimSlowerButton, "A-", 0, &Button, 0, Localize("Decrease animation speed"), CUI::CORNER_R)) { if(m_AnimateSpeed > 0.5f) m_AnimateSpeed -= 0.5f; } if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP) && m_Dialog == DIALOG_NONE) m_ZoomLevel -= 20; if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN) && m_Dialog == DIALOG_NONE) m_ZoomLevel += 20; if(m_ZoomLevel < 50) m_ZoomLevel = 50; m_WorldZoom = m_ZoomLevel/100.0f; TB_Top.VSplitLeft(10.0f, &Button, &TB_Top); // brush manipulation { int Enabled = m_Brush.IsEmpty()?-1:0; // flip buttons TB_Top.VSplitLeft(30.0f, &Button, &TB_Top); static int s_FlipXButton = 0; if(DoButton_Ex(&s_FlipXButton, "X/X", Enabled, &Button, 0, Localize("[N] Flip brush horizontal"), CUI::CORNER_L) || Input()->KeyDown('n')) { for(int i = 0; i < m_Brush.m_lLayers.size(); i++) m_Brush.m_lLayers[i]->BrushFlipX(); } TB_Top.VSplitLeft(30.0f, &Button, &TB_Top); static int s_FlipyButton = 0; if(DoButton_Ex(&s_FlipyButton, "Y/Y", Enabled, &Button, 0, Localize("[M] Flip brush vertical"), CUI::CORNER_R) || Input()->KeyDown('m')) { for(int i = 0; i < m_Brush.m_lLayers.size(); i++) m_Brush.m_lLayers[i]->BrushFlipY(); } // rotate buttons TB_Top.VSplitLeft(15.0f, &Button, &TB_Top); TB_Top.VSplitLeft(30.0f, &Button, &TB_Top); static int s_RotationAmount = 90; s_RotationAmount = UiDoValueSelector(&s_RotationAmount, &Button, "", s_RotationAmount, 1, 360, 2.0f); TB_Top.VSplitLeft(5.0f, &Button, &TB_Top); TB_Top.VSplitLeft(30.0f, &Button, &TB_Top); static int s_CcwButton = 0; if(DoButton_Ex(&s_CcwButton, Localize("CCW"), Enabled, &Button, 0, Localize("[R] Rotates the brush counter clockwise"), CUI::CORNER_L) || Input()->KeyDown('r')) { for(int i = 0; i < m_Brush.m_lLayers.size(); i++) m_Brush.m_lLayers[i]->BrushRotate(-s_RotationAmount/360.0f*pi*2); } TB_Top.VSplitLeft(30.0f, &Button, &TB_Top); static int s_CwButton = 0; if(DoButton_Ex(&s_CwButton, Localize("CW"), Enabled, &Button, 0, Localize("[T] Rotates the brush clockwise"), CUI::CORNER_R) || Input()->KeyDown('t')) { for(int i = 0; i < m_Brush.m_lLayers.size(); i++) m_Brush.m_lLayers[i]->BrushRotate(s_RotationAmount/360.0f*pi*2); } } // quad manipulation { // do add button TB_Top.VSplitLeft(10.0f, &Button, &TB_Top); TB_Top.VSplitLeft(60.0f, &Button, &TB_Top); static int s_NewButton = 0; CLayerQuads *pQLayer = (CLayerQuads *)GetSelectedLayerType(0, LAYERTYPE_QUADS); //CLayerTiles *tlayer = (CLayerTiles *)get_selected_layer_type(0, LAYERTYPE_TILES); if(DoButton_Editor(&s_NewButton, Localize("Add Quad"), pQLayer?0:-1, &Button, 0, Localize("Adds a new quad"))) { if(pQLayer) { float Mapping[4]; CLayerGroup *g = GetSelectedGroup(); g->Mapping(Mapping); int AddX = f2fx(Mapping[0] + (Mapping[2]-Mapping[0])/2); int AddY = f2fx(Mapping[1] + (Mapping[3]-Mapping[1])/2); CQuad *q = pQLayer->NewQuad(); for(int i = 0; i < 5; i++) { q->m_aPoints[i].x += AddX; q->m_aPoints[i].y += AddY; } } } } // tile manipulation { TB_Bottom.VSplitLeft(40.0f, &Button, &TB_Bottom); static int s_BorderBut = 0; CLayerTiles *pT = (CLayerTiles *)GetSelectedLayerType(0, LAYERTYPE_TILES); if(DoButton_Editor(&s_BorderBut, Localize("Border"), pT?0:-1, &Button, 0, Localize("Border"))) { if(pT) DoMapBorder(); } } } static void Rotate(CPoint *pCenter, CPoint *pPoint, float Rotation) { int x = pPoint->x - pCenter->x; int y = pPoint->y - pCenter->y; pPoint->x = (int)(x * cosf(Rotation) - y * sinf(Rotation) + pCenter->x); pPoint->y = (int)(x * sinf(Rotation) + y * cosf(Rotation) + pCenter->y); } void CEditor::DoQuad(CQuad *q, int Index) { enum { OP_NONE=0, OP_MOVE_ALL, OP_MOVE_PIVOT, OP_ROTATE, OP_CONTEXT_MENU, }; // some basic values void *pId = &q->m_aPoints[4]; // use pivot addr as id static CPoint s_RotatePoints[4]; static float s_LastWx; static float s_LastWy; static int s_Operation = OP_NONE; static float s_RotateAngle = 0; float wx = UI()->MouseWorldX(); float wy = UI()->MouseWorldY(); // get pivot float CenterX = fx2f(q->m_aPoints[4].x); float CenterY = fx2f(q->m_aPoints[4].y); float dx = (CenterX - wx); float dy = (CenterY - wy); if(dx*dx+dy*dy < 10*10) UI()->SetHotItem(pId); // draw selection background if(m_SelectedQuad == Index) { Graphics()->SetColor(0,0,0,1); IGraphics::CQuadItem QuadItem(CenterX, CenterY, 7.0f, 7.0f); Graphics()->QuadsDraw(&QuadItem, 1); } if(UI()->ActiveItem() == pId) { // check if we only should move pivot if(s_Operation == OP_MOVE_PIVOT) { q->m_aPoints[4].x += f2fx(wx-s_LastWx); q->m_aPoints[4].y += f2fx(wy-s_LastWy); } else if(s_Operation == OP_MOVE_ALL) { // move all points including pivot for(int v = 0; v < 5; v++) { q->m_aPoints[v].x += f2fx(wx-s_LastWx); q->m_aPoints[v].y += f2fx(wy-s_LastWy); } } else if(s_Operation == OP_ROTATE) { for(int v = 0; v < 4; v++) { q->m_aPoints[v] = s_RotatePoints[v]; Rotate(&q->m_aPoints[4], &q->m_aPoints[v], s_RotateAngle); } } s_RotateAngle += (m_MouseDeltaX) * 0.002f; s_LastWx = wx; s_LastWy = wy; if(s_Operation == OP_CONTEXT_MENU) { if(!UI()->MouseButton(1)) { static int s_QuadPopupId = 0; UiInvokePopupMenu(&s_QuadPopupId, 0, UI()->MouseX(), UI()->MouseY(), 120, 150, PopupQuad); m_LockMouse = false; s_Operation = OP_NONE; UI()->SetActiveItem(0); } } else { if(!UI()->MouseButton(0)) { m_LockMouse = false; s_Operation = OP_NONE; UI()->SetActiveItem(0); } } Graphics()->SetColor(1,1,1,1); } else if(UI()->HotItem() == pId) { ms_pUiGotContext = pId; Graphics()->SetColor(1,1,1,1); m_pTooltip = Localize("Left mouse button to move. Hold shift to move pivot. Hold ctrl to rotate."); if(UI()->MouseButton(0)) { if(Input()->KeyPressed(KEY_LSHIFT) || Input()->KeyPressed(KEY_RSHIFT)) s_Operation = OP_MOVE_PIVOT; else if(Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL)) { m_LockMouse = true; s_Operation = OP_ROTATE; s_RotateAngle = 0; s_RotatePoints[0] = q->m_aPoints[0]; s_RotatePoints[1] = q->m_aPoints[1]; s_RotatePoints[2] = q->m_aPoints[2]; s_RotatePoints[3] = q->m_aPoints[3]; } else s_Operation = OP_MOVE_ALL; UI()->SetActiveItem(pId); m_SelectedQuad = Index; s_LastWx = wx; s_LastWy = wy; } if(UI()->MouseButton(1)) { m_SelectedQuad = Index; s_Operation = OP_CONTEXT_MENU; UI()->SetActiveItem(pId); } } else Graphics()->SetColor(0,1,0,1); IGraphics::CQuadItem QuadItem(CenterX, CenterY, 5.0f, 5.0f); Graphics()->QuadsDraw(&QuadItem, 1); } void CEditor::DoQuadPoint(CQuad *q, int QuadIndex, int v) { void *pId = &q->m_aPoints[v]; float wx = UI()->MouseWorldX(); float wy = UI()->MouseWorldY(); float px = fx2f(q->m_aPoints[v].x); float py = fx2f(q->m_aPoints[v].y); float dx = (px - wx); float dy = (py - wy); if(dx*dx+dy*dy < 10*10) UI()->SetHotItem(pId); // draw selection background if(m_SelectedQuad == QuadIndex && m_SelectedPoints&(1<SetColor(0,0,0,1); IGraphics::CQuadItem QuadItem(px, py, 7.0f, 7.0f); Graphics()->QuadsDraw(&QuadItem, 1); } enum { OP_NONE=0, OP_MOVEPOINT, OP_MOVEUV, OP_CONTEXT_MENU }; static bool s_Moved; static int s_Operation = OP_NONE; if(UI()->ActiveItem() == pId) { float dx = m_MouseDeltaWx; float dy = m_MouseDeltaWy; if(!s_Moved) { if(dx*dx+dy*dy > 0.5f) s_Moved = true; } if(s_Moved) { if(s_Operation == OP_MOVEPOINT) { for(int m = 0; m < 4; m++) if(m_SelectedPoints&(1<m_aPoints[m].x += f2fx(dx); q->m_aPoints[m].y += f2fx(dy); } } else if(s_Operation == OP_MOVEUV) { for(int m = 0; m < 4; m++) if(m_SelectedPoints&(1<m_aTexcoords[m].x += f2fx(dx*0.001f); q->m_aTexcoords[m].y += f2fx(dy*0.001f); } } } if(s_Operation == OP_CONTEXT_MENU) { if(!UI()->MouseButton(1)) { static int s_PointPopupId = 0; UiInvokePopupMenu(&s_PointPopupId, 0, UI()->MouseX(), UI()->MouseY(), 120, 150, PopupPoint); UI()->SetActiveItem(0); } } else { if(!UI()->MouseButton(0)) { if(!s_Moved) { if(Input()->KeyPressed(KEY_LSHIFT) || Input()->KeyPressed(KEY_RSHIFT)) m_SelectedPoints ^= 1<SetActiveItem(0); } } Graphics()->SetColor(1,1,1,1); } else if(UI()->HotItem() == pId) { ms_pUiGotContext = pId; Graphics()->SetColor(1,1,1,1); m_pTooltip = Localize("Left mouse button to move. Hold shift to move the texture."); if(UI()->MouseButton(0)) { UI()->SetActiveItem(pId); s_Moved = false; if(Input()->KeyPressed(KEY_LSHIFT) || Input()->KeyPressed(KEY_RSHIFT)) { s_Operation = OP_MOVEUV; m_LockMouse = true; } else s_Operation = OP_MOVEPOINT; if(!(m_SelectedPoints&(1<KeyPressed(KEY_LSHIFT) || Input()->KeyPressed(KEY_RSHIFT)) m_SelectedPoints |= 1<MouseButton(1)) { s_Operation = OP_CONTEXT_MENU; m_SelectedQuad = QuadIndex; UI()->SetActiveItem(pId); if(!(m_SelectedPoints&(1<KeyPressed(KEY_LSHIFT) || Input()->KeyPressed(KEY_RSHIFT)) m_SelectedPoints |= 1<SetColor(1,0,0,1); IGraphics::CQuadItem QuadItem(px, py, 5.0f, 5.0f); Graphics()->QuadsDraw(&QuadItem, 1); } void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar) { //UI()->ClipEnable(&view); bool ShowPicker = Input()->KeyPressed(KEY_SPACE) != 0 && m_Dialog == DIALOG_NONE; // render all good stuff if(!ShowPicker) { for(int g = 0; g < m_Map.m_lGroups.size(); g++) { if(m_Map.m_lGroups[g]->m_Visible) m_Map.m_lGroups[g]->Render(); //UI()->ClipEnable(&view); } // render the game above everything else if(m_Map.m_pGameGroup->m_Visible && m_Map.m_pGameLayer->m_Visible) { m_Map.m_pGameGroup->MapScreen(); m_Map.m_pGameLayer->Render(); } } static void *s_pEditorId = (void *)&s_pEditorId; int Inside = UI()->MouseInside(&View); // fetch mouse position float wx = UI()->MouseWorldX(); float wy = UI()->MouseWorldY(); float mx = UI()->MouseX(); float my = UI()->MouseY(); static float s_StartWx = 0; static float s_StartWy = 0; static float s_StartMx = 0; static float s_StartMy = 0; enum { OP_NONE=0, OP_BRUSH_GRAB, OP_BRUSH_DRAW, OP_BRUSH_PAINT, OP_PAN_WORLD, OP_PAN_EDITOR, }; // remap the screen so it can display the whole tileset if(ShowPicker) { CUIRect Screen = *UI()->Screen(); float Size = 32.0*16.0f; float w = Size*(Screen.w/View.w); float h = Size*(Screen.h/View.h); float x = -(View.x/Screen.w)*w; float y = -(View.y/Screen.h)*h; wx = x+w*mx/Screen.w; wy = y+h*my/Screen.h; Graphics()->MapScreen(x, y, x+w, y+h); CLayerTiles *t = (CLayerTiles *)GetSelectedLayerType(0, LAYERTYPE_TILES); if(t) { m_TilesetPicker.m_Image = t->m_Image; m_TilesetPicker.m_TexId = t->m_TexId; m_TilesetPicker.Render(); } } static int s_Operation = OP_NONE; // draw layer borders CLayer *pEditLayers[16]; int NumEditLayers = 0; NumEditLayers = 0; if(ShowPicker) { pEditLayers[0] = &m_TilesetPicker; NumEditLayers++; } else { pEditLayers[0] = GetSelectedLayer(0); if(pEditLayers[0]) NumEditLayers++; CLayerGroup *g = GetSelectedGroup(); if(g) { g->MapScreen(); for(int i = 0; i < NumEditLayers; i++) { if(pEditLayers[i]->m_Type != LAYERTYPE_TILES) continue; float w, h; pEditLayers[i]->GetSize(&w, &h); IGraphics::CLineItem Array[4] = { IGraphics::CLineItem(0, 0, w, 0), IGraphics::CLineItem(w, 0, w, h), IGraphics::CLineItem(w, h, 0, h), IGraphics::CLineItem(0, h, 0, 0)}; Graphics()->TextureSet(-1); Graphics()->LinesBegin(); Graphics()->LinesDraw(Array, 4); Graphics()->LinesEnd(); } } } if(Inside) { UI()->SetHotItem(s_pEditorId); // do global operations like pan and zoom if(UI()->ActiveItem() == 0 && (UI()->MouseButton(0) || UI()->MouseButton(2))) { s_StartWx = wx; s_StartWy = wy; s_StartMx = mx; s_StartMy = my; if(Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL) || UI()->MouseButton(2)) { if(Input()->KeyPressed(KEY_LSHIFT)) s_Operation = OP_PAN_EDITOR; else s_Operation = OP_PAN_WORLD; UI()->SetActiveItem(s_pEditorId); } } // brush editing if(UI()->HotItem() == s_pEditorId) { if(m_Brush.IsEmpty()) m_pTooltip = Localize("Use left mouse button to drag and create a brush."); else m_pTooltip = Localize("Use left mouse button to paint with the brush. Right button clears the brush."); if(UI()->ActiveItem() == s_pEditorId) { CUIRect r; r.x = s_StartWx; r.y = s_StartWy; r.w = wx-s_StartWx; r.h = wy-s_StartWy; if(r.w < 0) { r.x += r.w; r.w = -r.w; } if(r.h < 0) { r.y += r.h; r.h = -r.h; } if(s_Operation == OP_BRUSH_DRAW) { if(!m_Brush.IsEmpty()) { // draw with brush for(int k = 0; k < NumEditLayers; k++) { if(pEditLayers[k]->m_Type == m_Brush.m_lLayers[0]->m_Type) pEditLayers[k]->BrushDraw(m_Brush.m_lLayers[0], wx, wy); } } } else if(s_Operation == OP_BRUSH_GRAB) { if(!UI()->MouseButton(0)) { // grab brush dbg_msg("editor", "grabbing %f %f %f %f", r.x, r.y, r.w, r.h); // TODO: do all layers int Grabs = 0; for(int k = 0; k < NumEditLayers; k++) Grabs += pEditLayers[k]->BrushGrab(&m_Brush, r); if(Grabs == 0) m_Brush.Clear(); } else { //editor.map.groups[selected_group]->mapscreen(); for(int k = 0; k < NumEditLayers; k++) pEditLayers[k]->BrushSelecting(r); Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h); } } else if(s_Operation == OP_BRUSH_PAINT) { if(!UI()->MouseButton(0)) { for(int k = 0; k < NumEditLayers; k++) pEditLayers[k]->FillSelection(m_Brush.IsEmpty(), m_Brush.m_lLayers[0], r); } else { //editor.map.groups[selected_group]->mapscreen(); for(int k = 0; k < NumEditLayers; k++) pEditLayers[k]->BrushSelecting(r); Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h); } } } else { if(UI()->MouseButton(1)) m_Brush.Clear(); if(UI()->MouseButton(0) && s_Operation == OP_NONE) { UI()->SetActiveItem(s_pEditorId); if(m_Brush.IsEmpty()) s_Operation = OP_BRUSH_GRAB; else { s_Operation = OP_BRUSH_DRAW; for(int k = 0; k < NumEditLayers; k++) { if(pEditLayers[k]->m_Type == m_Brush.m_lLayers[0]->m_Type) pEditLayers[k]->BrushPlace(m_Brush.m_lLayers[0], wx, wy); } } CLayerTiles *pLayer = (CLayerTiles*)GetSelectedLayerType(0, LAYERTYPE_TILES); if((Input()->KeyPressed(KEY_LSHIFT) || Input()->KeyPressed(KEY_RSHIFT)) && pLayer) s_Operation = OP_BRUSH_PAINT; } if(!m_Brush.IsEmpty()) { m_Brush.m_OffsetX = -(int)wx; m_Brush.m_OffsetY = -(int)wy; for(int i = 0; i < m_Brush.m_lLayers.size(); i++) { if(m_Brush.m_lLayers[i]->m_Type == LAYERTYPE_TILES) { m_Brush.m_OffsetX = -(int)(wx/32.0f)*32; m_Brush.m_OffsetY = -(int)(wy/32.0f)*32; break; } } CLayerGroup *g = GetSelectedGroup(); m_Brush.m_OffsetX += g->m_OffsetX; m_Brush.m_OffsetY += g->m_OffsetY; m_Brush.m_ParallaxX = g->m_ParallaxX; m_Brush.m_ParallaxY = g->m_ParallaxY; m_Brush.Render(); float w, h; m_Brush.GetSize(&w, &h); IGraphics::CLineItem Array[4] = { IGraphics::CLineItem(0, 0, w, 0), IGraphics::CLineItem(w, 0, w, h), IGraphics::CLineItem(w, h, 0, h), IGraphics::CLineItem(0, h, 0, 0)}; Graphics()->TextureSet(-1); Graphics()->LinesBegin(); Graphics()->LinesDraw(Array, 4); Graphics()->LinesEnd(); } } } // quad editing { if(!ShowPicker && m_Brush.IsEmpty()) { // fetch layers CLayerGroup *g = GetSelectedGroup(); if(g) g->MapScreen(); for(int k = 0; k < NumEditLayers; k++) { if(pEditLayers[k]->m_Type == LAYERTYPE_QUADS) { CLayerQuads *pLayer = (CLayerQuads *)pEditLayers[k]; Graphics()->TextureSet(-1); Graphics()->QuadsBegin(); for(int i = 0; i < pLayer->m_lQuads.size(); i++) { for(int v = 0; v < 4; v++) DoQuadPoint(&pLayer->m_lQuads[i], i, v); DoQuad(&pLayer->m_lQuads[i], i); } Graphics()->QuadsEnd(); } } Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h); } // do panning if(UI()->ActiveItem() == s_pEditorId) { if(s_Operation == OP_PAN_WORLD) { m_WorldOffsetX -= m_MouseDeltaX*m_WorldZoom; m_WorldOffsetY -= m_MouseDeltaY*m_WorldZoom; } else if(s_Operation == OP_PAN_EDITOR) { m_EditorOffsetX -= m_MouseDeltaX*m_WorldZoom; m_EditorOffsetY -= m_MouseDeltaY*m_WorldZoom; } // release mouse if(!UI()->MouseButton(0)) { s_Operation = OP_NONE; UI()->SetActiveItem(0); } } } } if(GetSelectedGroup() && GetSelectedGroup()->m_UseClipping) { CLayerGroup *g = m_Map.m_pGameGroup; g->MapScreen(); Graphics()->TextureSet(-1); Graphics()->LinesBegin(); CUIRect r; r.x = GetSelectedGroup()->m_ClipX; r.y = GetSelectedGroup()->m_ClipY; r.w = GetSelectedGroup()->m_ClipW; r.h = GetSelectedGroup()->m_ClipH; IGraphics::CLineItem Array[4] = { IGraphics::CLineItem(r.x, r.y, r.x+r.w, r.y), IGraphics::CLineItem(r.x+r.w, r.y, r.x+r.w, r.y+r.h), IGraphics::CLineItem(r.x+r.w, r.y+r.h, r.x, r.y+r.h), IGraphics::CLineItem(r.x, r.y+r.h, r.x, r.y)}; Graphics()->SetColor(1,0,0,1); Graphics()->LinesDraw(Array, 4); Graphics()->LinesEnd(); } // render screen sizes if(m_ProofBorders) { CLayerGroup *g = m_Map.m_pGameGroup; g->MapScreen(); Graphics()->TextureSet(-1); Graphics()->LinesBegin(); float aLastPoints[4]; float Start = 1.0f; //9.0f/16.0f; float End = 16.0f/9.0f; const int NumSteps = 20; for(int i = 0; i <= NumSteps; i++) { float aPoints[4]; float Aspect = Start + (End-Start)*(i/(float)NumSteps); RenderTools()->MapscreenToWorld( m_WorldOffsetX, m_WorldOffsetY, 1.0f, 1.0f, 0.0f, 0.0f, Aspect, 1.0f, aPoints); if(i == 0) { IGraphics::CLineItem Array[2] = { IGraphics::CLineItem(aPoints[0], aPoints[1], aPoints[2], aPoints[1]), IGraphics::CLineItem(aPoints[0], aPoints[3], aPoints[2], aPoints[3])}; Graphics()->LinesDraw(Array, 2); } if(i != 0) { IGraphics::CLineItem Array[4] = { IGraphics::CLineItem(aPoints[0], aPoints[1], aLastPoints[0], aLastPoints[1]), IGraphics::CLineItem(aPoints[2], aPoints[1], aLastPoints[2], aLastPoints[1]), IGraphics::CLineItem(aPoints[0], aPoints[3], aLastPoints[0], aLastPoints[3]), IGraphics::CLineItem(aPoints[2], aPoints[3], aLastPoints[2], aLastPoints[3])}; Graphics()->LinesDraw(Array, 4); } if(i == NumSteps) { IGraphics::CLineItem Array[2] = { IGraphics::CLineItem(aPoints[0], aPoints[1], aPoints[0], aPoints[3]), IGraphics::CLineItem(aPoints[2], aPoints[1], aPoints[2], aPoints[3])}; Graphics()->LinesDraw(Array, 2); } mem_copy(aLastPoints, aPoints, sizeof(aPoints)); } if(1) { Graphics()->SetColor(1,0,0,1); for(int i = 0; i < 2; i++) { float aPoints[4]; float aAspects[] = {4.0f/3.0f, 16.0f/10.0f, 5.0f/4.0f, 16.0f/9.0f}; float Aspect = aAspects[i]; RenderTools()->MapscreenToWorld( m_WorldOffsetX, m_WorldOffsetY, 1.0f, 1.0f, 0.0f, 0.0f, Aspect, 1.0f, aPoints); CUIRect r; r.x = aPoints[0]; r.y = aPoints[1]; r.w = aPoints[2]-aPoints[0]; r.h = aPoints[3]-aPoints[1]; IGraphics::CLineItem Array[4] = { IGraphics::CLineItem(r.x, r.y, r.x+r.w, r.y), IGraphics::CLineItem(r.x+r.w, r.y, r.x+r.w, r.y+r.h), IGraphics::CLineItem(r.x+r.w, r.y+r.h, r.x, r.y+r.h), IGraphics::CLineItem(r.x, r.y+r.h, r.x, r.y)}; Graphics()->LinesDraw(Array, 4); Graphics()->SetColor(0,1,0,1); } } Graphics()->LinesEnd(); } Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h); //UI()->ClipDisable(); } int CEditor::DoProperties(CUIRect *pToolBox, CProperty *pProps, int *pIds, int *pNewVal) { int Change = -1; for(int i = 0; pProps[i].m_pName; i++) { CUIRect Slot; pToolBox->HSplitTop(13.0f, &Slot, pToolBox); CUIRect Label, Shifter; Slot.VSplitMid(&Label, &Shifter); Shifter.HMargin(1.0f, &Shifter); UI()->DoLabel(&Label, pProps[i].m_pName, 10.0f, -1, -1); if(pProps[i].m_Type == PROPTYPE_INT_STEP) { CUIRect Inc, Dec; char aBuf[64]; Shifter.VSplitRight(10.0f, &Shifter, &Inc); Shifter.VSplitLeft(10.0f, &Dec, &Shifter); str_format(aBuf, sizeof(aBuf),"%d", pProps[i].m_Value); RenderTools()->DrawUIRect(&Shifter, vec4(1,1,1,0.5f), 0, 0.0f); UI()->DoLabel(&Shifter, aBuf, 10.0f, 0, -1); if(DoButton_ButtonDec(&pIds[i], 0, 0, &Dec, 0, Localize("Decrease"))) { if(Input()->KeyPressed(KEY_LSHIFT) || Input()->KeyPressed(KEY_RSHIFT)) *pNewVal = pProps[i].m_Value-5; else *pNewVal = pProps[i].m_Value-1; Change = i; } if(DoButton_ButtonInc(((char *)&pIds[i])+1, 0, 0, &Inc, 0, Localize("Increase"))) { if(Input()->KeyPressed(KEY_LSHIFT) || Input()->KeyPressed(KEY_RSHIFT)) *pNewVal = pProps[i].m_Value+5; else *pNewVal = pProps[i].m_Value+1; Change = i; } } else if(pProps[i].m_Type == PROPTYPE_BOOL) { CUIRect No, Yes; Shifter.VSplitMid(&No, &Yes); if(DoButton_ButtonDec(&pIds[i], Localize("No"), !pProps[i].m_Value, &No, 0, "")) { *pNewVal = 0; Change = i; } if(DoButton_ButtonInc(((char *)&pIds[i])+1, Localize("Yes"), pProps[i].m_Value, &Yes, 0, "")) { *pNewVal = 1; Change = i; } } else if(pProps[i].m_Type == PROPTYPE_INT_SCROLL) { int NewValue = UiDoValueSelector(&pIds[i], &Shifter, "", pProps[i].m_Value, pProps[i].m_Min, pProps[i].m_Max, 1.0f); if(NewValue != pProps[i].m_Value) { *pNewVal = NewValue; Change = i; } } else if(pProps[i].m_Type == PROPTYPE_COLOR) { static const char *s_paTexts[4] = {"R", "G", "B", "A"}; static int s_aShift[] = {24, 16, 8, 0}; int NewColor = 0; for(int c = 0; c < 4; c++) { int v = (pProps[i].m_Value >> s_aShift[c])&0xff; NewColor |= UiDoValueSelector(((char *)&pIds[i])+c, &Shifter, s_paTexts[c], v, 0, 255, 1.0f)<HSplitTop(13.0f, &Slot, pToolBox); Slot.VSplitMid(0, &Shifter); Shifter.HMargin(1.0f, &Shifter); } } if(NewColor != pProps[i].m_Value) { *pNewVal = NewColor; Change = i; } } else if(pProps[i].m_Type == PROPTYPE_IMAGE) { char aBuf[64]; if(pProps[i].m_Value < 0) str_copy(aBuf, Localize("None"), sizeof(aBuf)); else str_format(aBuf, sizeof(aBuf),"%s", m_Map.m_lImages[pProps[i].m_Value]->m_aName); if(DoButton_Editor(&pIds[i], aBuf, 0, &Shifter, 0, 0)) PopupSelectImageInvoke(pProps[i].m_Value, UI()->MouseX(), UI()->MouseY()); int r = PopupSelectImageResult(); if(r >= -1) { *pNewVal = r; Change = i; } } } return Change; } void CEditor::RenderLayers(CUIRect ToolBox, CUIRect ToolBar, CUIRect View) { CUIRect LayersBox = ToolBox; if(!m_GuiActive) return; CUIRect Slot, Button; char aBuf[64]; int ValidGroup = 0; int ValidLayer = 0; if(m_SelectedGroup >= 0 && m_SelectedGroup < m_Map.m_lGroups.size()) ValidGroup = 1; if(ValidGroup && m_SelectedLayer >= 0 && m_SelectedLayer < m_Map.m_lGroups[m_SelectedGroup]->m_lLayers.size()) ValidLayer = 1; // render layers { for(int g = 0; g < m_Map.m_lGroups.size(); g++) { CUIRect VisibleToggle; LayersBox.HSplitTop(12.0f, &Slot, &LayersBox); Slot.VSplitLeft(12, &VisibleToggle, &Slot); if(DoButton_Ex(&m_Map.m_lGroups[g]->m_Visible, m_Map.m_lGroups[g]->m_Visible?"V":"H", 0, &VisibleToggle, 0, Localize("Toggle group visibility"), CUI::CORNER_L)) m_Map.m_lGroups[g]->m_Visible = !m_Map.m_lGroups[g]->m_Visible; str_format(aBuf, sizeof(aBuf),"#%d %s", g, m_Map.m_lGroups[g]->m_pName); if(int Result = DoButton_Ex(&m_Map.m_lGroups[g], aBuf, g==m_SelectedGroup, &Slot, BUTTON_CONTEXT, Localize("Select group. Right click for properties."), CUI::CORNER_R)) { m_SelectedGroup = g; m_SelectedLayer = 0; static int s_GroupPopupId = 0; if(Result == 2) UiInvokePopupMenu(&s_GroupPopupId, 0, UI()->MouseX(), UI()->MouseY(), 120, 200, PopupGroup); } LayersBox.HSplitTop(2.0f, &Slot, &LayersBox); for(int i = 0; i < m_Map.m_lGroups[g]->m_lLayers.size(); i++) { //visible LayersBox.HSplitTop(12.0f, &Slot, &LayersBox); Slot.VSplitLeft(12.0f, 0, &Button); Button.VSplitLeft(15, &VisibleToggle, &Button); if(DoButton_Ex(&m_Map.m_lGroups[g]->m_lLayers[i]->m_Visible, m_Map.m_lGroups[g]->m_lLayers[i]->m_Visible?"V":"H", 0, &VisibleToggle, 0, Localize("Toggle layer visibility"), CUI::CORNER_L)) m_Map.m_lGroups[g]->m_lLayers[i]->m_Visible = !m_Map.m_lGroups[g]->m_lLayers[i]->m_Visible; str_format(aBuf, sizeof(aBuf),"#%d %s ", i, m_Map.m_lGroups[g]->m_lLayers[i]->m_pTypeName); if(int Result = DoButton_Ex(m_Map.m_lGroups[g]->m_lLayers[i], aBuf, g==m_SelectedGroup&&i==m_SelectedLayer, &Button, BUTTON_CONTEXT, Localize("Select layer. Right click for properties."), CUI::CORNER_R)) { m_SelectedLayer = i; m_SelectedGroup = g; static int s_LayerPopupId = 0; if(Result == 2) UiInvokePopupMenu(&s_LayerPopupId, 0, UI()->MouseX(), UI()->MouseY(), 120, 150, PopupLayer); } LayersBox.HSplitTop(2.0f, &Slot, &LayersBox); } LayersBox.HSplitTop(5.0f, &Slot, &LayersBox); } } { LayersBox.HSplitTop(12.0f, &Slot, &LayersBox); static int s_NewGroupButton = 0; if(DoButton_Editor(&s_NewGroupButton, Localize("Add group"), 0, &Slot, 0, Localize("Adds a new group"))) { m_Map.NewGroup(); m_SelectedGroup = m_Map.m_lGroups.size()-1; } } LayersBox.HSplitTop(5.0f, &Slot, &LayersBox); } static void ExtractName(const char *pFileName, char *pName) { int Len = str_length(pFileName); int Start = Len; int End = Len; while(Start > 0) { Start--; if(pFileName[Start] == '/' || pFileName[Start] == '\\') { Start++; break; } } End = Start; for(int i = Start; i < Len; i++) { if(pFileName[i] == '.') End = i; } if(End == Start) End = Len; int FinalLen = End-Start; mem_copy(pName, &pFileName[Start], FinalLen); pName[FinalLen] = 0; dbg_msg("", "%s %s %d %d", pFileName, pName, Start, End); } void CEditor::ReplaceImage(const char *pFileName, void *pUser) { CEditor *pEditor = (CEditor *)pUser; CEditorImage ImgInfo(pEditor); if(!pEditor->Graphics()->LoadPNG(&ImgInfo, pFileName)) return; CEditorImage *pImg = pEditor->m_Map.m_lImages[pEditor->m_SelectedImage]; pEditor->Graphics()->UnloadTexture(pImg->m_TexId); *pImg = ImgInfo; ExtractName(pFileName, pImg->m_aName); pImg->m_TexId = pEditor->Graphics()->LoadTextureRaw(ImgInfo.m_Width, ImgInfo.m_Height, ImgInfo.m_Format, ImgInfo.m_pData, CImageInfo::FORMAT_AUTO, 0); } void CEditor::AddImage(const char *pFileName, void *pUser) { CEditor *pEditor = (CEditor *)pUser; CEditorImage ImgInfo(pEditor); if(!pEditor->Graphics()->LoadPNG(&ImgInfo, pFileName)) return; CEditorImage *pImg = new CEditorImage(pEditor); *pImg = ImgInfo; pImg->m_TexId = pEditor->Graphics()->LoadTextureRaw(ImgInfo.m_Width, ImgInfo.m_Height, ImgInfo.m_Format, ImgInfo.m_pData, CImageInfo::FORMAT_AUTO, 0); pImg->m_External = 1; // external by default ExtractName(pFileName, pImg->m_aName); for(int i = 0; i < pEditor->m_Map.m_lImages.size(); ++i) { if(!str_comp(pEditor->m_Map.m_lImages[i]->m_aName, pImg->m_aName)) return; } pEditor->m_Map.m_lImages.add(pImg); } static int gs_ModifyIndexDeletedIndex; static void ModifyIndexDeleted(int *pIndex) { if(*pIndex == gs_ModifyIndexDeletedIndex) *pIndex = -1; else if(*pIndex > gs_ModifyIndexDeletedIndex) *pIndex = *pIndex - 1; } int CEditor::PopupImage(CEditor *pEditor, CUIRect View) { static int s_ReplaceButton = 0; static int s_RemoveButton = 0; CUIRect Slot; View.HSplitTop(2.0f, &Slot, &View); View.HSplitTop(12.0f, &Slot, &View); CEditorImage *pImg = pEditor->m_Map.m_lImages[pEditor->m_SelectedImage]; static int s_ExternalButton = 0; if(pImg->m_External) { if(pEditor->DoButton_MenuItem(&s_ExternalButton, Localize("Embed"), 0, &Slot, 0, Localize("Embeds the image into the map file."))) { pImg->m_External = 0; return 1; } } else { if(pEditor->DoButton_MenuItem(&s_ExternalButton, Localize("Make external"), 0, &Slot, 0, Localize("Removes the image from the map file."))) { pImg->m_External = 1; return 1; } } View.HSplitTop(10.0f, &Slot, &View); View.HSplitTop(12.0f, &Slot, &View); if(pEditor->DoButton_MenuItem(&s_ReplaceButton, Localize("Replace"), 0, &Slot, 0, Localize("Replaces the image with a new one"))) { pEditor->InvokeFileDialog(IStorage::TYPE_ALL, Localize("Replace Image"), Localize("Replace"), "mapres/", "", ReplaceImage, pEditor); return 1; } View.HSplitTop(10.0f, &Slot, &View); View.HSplitTop(12.0f, &Slot, &View); if(pEditor->DoButton_MenuItem(&s_RemoveButton, Localize("Remove"), 0, &Slot, 0, Localize("Removes the image from the map"))) { delete pImg; pEditor->m_Map.m_lImages.remove_index(pEditor->m_SelectedImage); gs_ModifyIndexDeletedIndex = pEditor->m_SelectedImage; pEditor->m_Map.ModifyImageIndex(ModifyIndexDeleted); return 1; } return 0; } void CEditor::RenderImages(CUIRect ToolBox, CUIRect ToolBar, CUIRect View) { for(int e = 0; e < 2; e++) // two passes, first embedded, then external { CUIRect Slot; ToolBox.HSplitTop(15.0f, &Slot, &ToolBox); if(e == 0) UI()->DoLabel(&Slot, Localize("Embedded"), 12.0f, 0); else UI()->DoLabel(&Slot, Localize("External"), 12.0f, 0); for(int i = 0; i < m_Map.m_lImages.size(); i++) { if((e && !m_Map.m_lImages[i]->m_External) || (!e && m_Map.m_lImages[i]->m_External)) { continue; } char aBuf[128]; str_copy(aBuf, m_Map.m_lImages[i]->m_aName, sizeof(aBuf)); ToolBox.HSplitTop(12.0f, &Slot, &ToolBox); if(int Result = DoButton_Editor(&m_Map.m_lImages[i], aBuf, m_SelectedImage == i, &Slot, BUTTON_CONTEXT, Localize("Select image"))) { m_SelectedImage = i; static int s_PopupImageId = 0; if(Result == 2) UiInvokePopupMenu(&s_PopupImageId, 0, UI()->MouseX(), UI()->MouseY(), 120, 80, PopupImage); } ToolBox.HSplitTop(2.0f, 0, &ToolBox); // render image if(m_SelectedImage == i) { CUIRect r; View.Margin(10.0f, &r); if(r.h < r.w) r.w = r.h; else r.h = r.w; Graphics()->TextureSet(m_Map.m_lImages[i]->m_TexId); Graphics()->BlendNormal(); Graphics()->QuadsBegin(); IGraphics::CQuadItem QuadItem(r.x, r.y, r.w, r.h); Graphics()->QuadsDrawTL(&QuadItem, 1); Graphics()->QuadsEnd(); } } } CUIRect Slot; ToolBox.HSplitTop(5.0f, &Slot, &ToolBox); // new image static int s_NewImageButton = 0; ToolBox.HSplitTop(10.0f, &Slot, &ToolBox); ToolBox.HSplitTop(12.0f, &Slot, &ToolBox); if(DoButton_Editor(&s_NewImageButton, "Add", 0, &Slot, 0, Localize("Load a new image to use in the map"))) InvokeFileDialog(IStorage::TYPE_ALL, Localize("Add Image"), Localize("Add"), "mapres/", "", AddImage, this); } static int gs_FileDialogDirTypes = 0; static const char *gs_pFileDialogTitle = 0; static const char *gs_pFileDialogButtonText = 0; static void (*gs_pfnFileDialogFunc)(const char *pFileName, void *pUser); static void *gs_pFileDialogUser = 0; static char gs_FileDialogFileName[512] = {0}; static char gs_aFileDialogPath[512] = {0}; static char gs_aFileDialogCompleteFilename[512] = {0}; static int gs_FilesNum = 0; static sorted_array gs_FileList; int g_FilesStartAt = 0; int g_FilesCur = 0; int g_FilesStopAt = 999; static void EditorListdirCallback(const char *pName, int IsDir, void *pUser) { if(pName[0] == '.' || IsDir) // skip this shit! return; gs_FileList.add(string(pName)); } void CEditor::AddFileDialogEntry(const char *pName, CUIRect *pView) { if(g_FilesCur > gs_FilesNum) gs_FilesNum = g_FilesCur; g_FilesCur++; if(g_FilesCur-1 < g_FilesStartAt || g_FilesCur > g_FilesStopAt) return; CUIRect Button; pView->HSplitTop(15.0f, &Button, pView); pView->HSplitTop(2.0f, 0, pView); //char buf[512]; if(DoButton_File((void*)(10+(int)Button.y), pName, 0, &Button, 0, 0)) { str_copy(gs_FileDialogFileName, pName, sizeof(gs_FileDialogFileName)); gs_aFileDialogCompleteFilename[0] = 0; str_append(gs_aFileDialogCompleteFilename, gs_aFileDialogPath, sizeof(gs_aFileDialogCompleteFilename)); str_append(gs_aFileDialogCompleteFilename, gs_FileDialogFileName, sizeof(gs_aFileDialogCompleteFilename)); if(Input()->MouseDoubleClick()) { if(gs_pfnFileDialogFunc) gs_pfnFileDialogFunc(gs_aFileDialogCompleteFilename, this); m_Dialog = DIALOG_NONE; } } } void CEditor::RenderFileDialog() { // GUI coordsys Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h); CUIRect View = *UI()->Screen(); RenderTools()->DrawUIRect(&View, vec4(0,0,0,0.25f), 0, 0); View.VMargin(150.0f, &View); View.HMargin(50.0f, &View); RenderTools()->DrawUIRect(&View, vec4(0,0,0,0.75f), CUI::CORNER_ALL, 5.0f); View.Margin(10.0f, &View); CUIRect Title, FileBox, FileBoxLabel, ButtonBar, Scroll; View.HSplitTop(18.0f, &Title, &View); View.HSplitTop(5.0f, 0, &View); // some spacing View.HSplitBottom(14.0f, &View, &ButtonBar); View.HSplitBottom(10.0f, &View, 0); // some spacing View.HSplitBottom(14.0f, &View, &FileBox); FileBox.VSplitLeft(55.0f, &FileBoxLabel, &FileBox); View.VSplitRight(15.0f, &View, &Scroll); // title RenderTools()->DrawUIRect(&Title, vec4(1, 1, 1, 0.25f), CUI::CORNER_ALL, 4.0f); Title.VMargin(10.0f, &Title); UI()->DoLabel(&Title, gs_pFileDialogTitle, 12.0f, -1, -1); // filebox static int s_FileBoxId = 0; UI()->DoLabel(&FileBoxLabel, Localize("Filename:"), 10.0f, -1, -1); DoEditBox(&s_FileBoxId, &FileBox, gs_FileDialogFileName, sizeof(gs_FileDialogFileName), 10.0f); gs_aFileDialogCompleteFilename[0] = 0; str_append(gs_aFileDialogCompleteFilename, gs_aFileDialogPath, sizeof(gs_aFileDialogCompleteFilename)); str_append(gs_aFileDialogCompleteFilename, gs_FileDialogFileName, sizeof(gs_aFileDialogCompleteFilename)); int Num = (int)(View.h/17.0); static float s_ScrollValue = 0; static int ScrollBar = 0; Scroll.HMargin(5.0f, &Scroll); s_ScrollValue = UiDoScrollbarV(&ScrollBar, &Scroll, s_ScrollValue); int ScrollNum = gs_FilesNum-Num+10; if(ScrollNum > 0) { if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP)) s_ScrollValue -= 3.0f/ScrollNum; if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN)) s_ScrollValue += 3.0f/ScrollNum; if(s_ScrollValue < 0) s_ScrollValue = 0; if(s_ScrollValue > 1) s_ScrollValue = 1; } else ScrollNum = 0; g_FilesStartAt = (int)(ScrollNum*s_ScrollValue); if(g_FilesStartAt < 0) g_FilesStartAt = 0; g_FilesStopAt = g_FilesStartAt+Num; g_FilesCur = 0; // set clipping UI()->ClipEnable(&View); // TODO: lazy ass coding, should store the interface pointer somewere Kernel()->RequestInterface()->ListDirectory(gs_FileDialogDirTypes, gs_aFileDialogPath, EditorListdirCallback, 0); for(int i = 0; i < gs_FileList.size(); i++) AddFileDialogEntry(gs_FileList[i].cstr(), &View); gs_FileList.clear(); // disable clipping again UI()->ClipDisable(); // the buttons static int s_OkButton = 0; static int s_CancelButton = 0; CUIRect Button; ButtonBar.VSplitRight(50.0f, &ButtonBar, &Button); if(DoButton_Editor(&s_OkButton, gs_pFileDialogButtonText, 0, &Button, 0, 0) || Input()->KeyPressed(KEY_RETURN)) { if(gs_pfnFileDialogFunc) gs_pfnFileDialogFunc(gs_aFileDialogCompleteFilename, gs_pFileDialogUser); m_Dialog = DIALOG_NONE; } ButtonBar.VSplitRight(40.0f, &ButtonBar, &Button); ButtonBar.VSplitRight(50.0f, &ButtonBar, &Button); if(DoButton_Editor(&s_CancelButton, Localize("Cancel"), 0, &Button, 0, 0) || Input()->KeyPressed(KEY_ESCAPE)) m_Dialog = DIALOG_NONE; } void CEditor::InvokeFileDialog(int ListDirTypes, const char *pTitle, const char *pButtonText, const char *pBasePath, const char *pDefaultName, void (*pfnFunc)(const char *pFileName, void *pUser), void *pUser) { gs_FileDialogDirTypes = ListDirTypes; gs_pFileDialogTitle = pTitle; gs_pFileDialogButtonText = pButtonText; gs_pfnFileDialogFunc = pfnFunc; gs_pFileDialogUser = pUser; gs_FileDialogFileName[0] = 0; gs_aFileDialogPath[0] = 0; if(pDefaultName) str_copy(gs_FileDialogFileName, pDefaultName, sizeof(gs_FileDialogFileName)); if(pBasePath) str_copy(gs_aFileDialogPath, pBasePath, sizeof(gs_aFileDialogPath)); m_Dialog = DIALOG_FILE; } void CEditor::RenderModebar(CUIRect View) { CUIRect Button; // mode buttons { View.VSplitLeft(65.0f, &Button, &View); Button.HSplitTop(30.0f, 0, &Button); static int s_Button = 0; const char *pButName = m_Mode == MODE_LAYERS ? Localize("Layers") : Localize("Images"); if(DoButton_Tab(&s_Button, pButName, 0, &Button, 0, Localize("Switch between images and layers managment."))) { if(m_Mode == MODE_LAYERS) m_Mode = MODE_IMAGES; else m_Mode = MODE_LAYERS; } } View.VSplitLeft(5.0f, 0, &View); } void CEditor::RenderStatusbar(CUIRect View) { CUIRect Button; View.VSplitRight(60.0f, &View, &Button); static int s_EnvelopeButton = 0; if(DoButton_Editor(&s_EnvelopeButton, Localize("Envelopes"), m_ShowEnvelopeEditor, &Button, 0, Localize("Toggles the envelope editor."))) m_ShowEnvelopeEditor = (m_ShowEnvelopeEditor+1)%4; if(m_pTooltip) { if(ms_pUiGotContext && ms_pUiGotContext == UI()->HotItem()) { char aBuf[512]; str_format(aBuf, sizeof(aBuf), Localize("%s Right click for context menu."), m_pTooltip); UI()->DoLabel(&View, aBuf, 10.0f, -1, -1); } else UI()->DoLabel(&View, m_pTooltip, 10.0f, -1, -1); } } void CEditor::RenderEnvelopeEditor(CUIRect View) { if(m_SelectedEnvelope < 0) m_SelectedEnvelope = 0; if(m_SelectedEnvelope >= m_Map.m_lEnvelopes.size()) m_SelectedEnvelope--; CEnvelope *pEnvelope = 0; if(m_SelectedEnvelope >= 0 && m_SelectedEnvelope < m_Map.m_lEnvelopes.size()) pEnvelope = m_Map.m_lEnvelopes[m_SelectedEnvelope]; bool ShowColorBar = false; if(pEnvelope && pEnvelope->m_Channels == 4) ShowColorBar = true; CUIRect ToolBar, CurveBar, ColorBar; View.HSplitTop(15.0f, &ToolBar, &View); View.HSplitTop(15.0f, &CurveBar, &View); ToolBar.Margin(2.0f, &ToolBar); CurveBar.Margin(2.0f, &CurveBar); if(ShowColorBar) { View.HSplitTop(20.0f, &ColorBar, &View); ColorBar.Margin(2.0f, &ColorBar); RenderBackground(ColorBar, ms_CheckerTexture, 16.0f, 1.0f); } RenderBackground(View, ms_CheckerTexture, 32.0f, 0.1f); // do the toolbar { CUIRect Button; CEnvelope *pNewEnv = 0; ToolBar.VSplitRight(50.0f, &ToolBar, &Button); static int s_New4dButton = 0; if(DoButton_Editor(&s_New4dButton, Localize("Color+"), 0, &Button, 0, Localize("Creates a new color envelope"))) pNewEnv = m_Map.NewEnvelope(4); ToolBar.VSplitRight(5.0f, &ToolBar, &Button); ToolBar.VSplitRight(50.0f, &ToolBar, &Button); static int s_New2dButton = 0; if(DoButton_Editor(&s_New2dButton, Localize("Pos.+"), 0, &Button, 0, Localize("Creates a new pos envelope"))) pNewEnv = m_Map.NewEnvelope(3); if(pNewEnv) // add the default points { if(pNewEnv->m_Channels == 4) { pNewEnv->AddPoint(0, 1,1,1,1); pNewEnv->AddPoint(1000, 1,1,1,1); } else { pNewEnv->AddPoint(0, 0); pNewEnv->AddPoint(1000, 0); } } CUIRect Shifter, Inc, Dec; ToolBar.VSplitLeft(60.0f, &Shifter, &ToolBar); Shifter.VSplitRight(15.0f, &Shifter, &Inc); Shifter.VSplitLeft(15.0f, &Dec, &Shifter); char aBuf[512]; str_format(aBuf, sizeof(aBuf),"%d/%d", m_SelectedEnvelope+1, m_Map.m_lEnvelopes.size()); RenderTools()->DrawUIRect(&Shifter, vec4(1,1,1,0.5f), 0, 0.0f); UI()->DoLabel(&Shifter, aBuf, 10.0f, 0, -1); static int s_PrevButton = 0; if(DoButton_ButtonDec(&s_PrevButton, 0, 0, &Dec, 0, Localize("Previous Envelope"))) m_SelectedEnvelope--; static int s_NextButton = 0; if(DoButton_ButtonInc(&s_NextButton, 0, 0, &Inc, 0, Localize("Next Envelope"))) m_SelectedEnvelope++; if(pEnvelope) { ToolBar.VSplitLeft(15.0f, &Button, &ToolBar); ToolBar.VSplitLeft(35.0f, &Button, &ToolBar); UI()->DoLabel(&Button, Localize("Name:"), 10.0f, -1, -1); ToolBar.VSplitLeft(80.0f, &Button, &ToolBar); static int s_NameBox = 0; DoEditBox(&s_NameBox, &Button, pEnvelope->m_aName, sizeof(pEnvelope->m_aName), 10.0f); } } if(pEnvelope) { static array Selection; static int sEnvelopeEditorId = 0; static int s_ActiveChannels = 0xf; if(pEnvelope) { CUIRect Button; ToolBar.VSplitLeft(15.0f, &Button, &ToolBar); static const char *s_paNames[4][4] = { {"X", "", "", ""}, {"X", "Y", "", ""}, {"X", "Y", "R", ""}, {"R", "G", "B", "A"}, }; static int s_aChannelButtons[4] = {0}; int Bit = 1; //ui_draw_button_func draw_func; for(int i = 0; i < pEnvelope->m_Channels; i++, Bit<<=1) { ToolBar.VSplitLeft(15.0f, &Button, &ToolBar); /*if(i == 0) draw_func = draw_editor_button_l; else if(i == envelope->channels-1) draw_func = draw_editor_button_r; else draw_func = draw_editor_button_m;*/ if(DoButton_Editor(&s_aChannelButtons[i], s_paNames[pEnvelope->m_Channels-1][i], s_ActiveChannels&Bit, &Button, 0, 0)) s_ActiveChannels ^= Bit; } } float EndTime = pEnvelope->EndTime(); if(EndTime < 1) EndTime = 1; pEnvelope->FindTopBottom(s_ActiveChannels); float Top = pEnvelope->m_Top; float Bottom = pEnvelope->m_Bottom; if(Top < 1) Top = 1; if(Bottom >= 0) Bottom = 0; float TimeScale = EndTime/View.w; float ValueScale = (Top-Bottom)/View.h; if(UI()->MouseInside(&View)) UI()->SetHotItem(&sEnvelopeEditorId); if(UI()->HotItem() == &sEnvelopeEditorId) { // do stuff if(pEnvelope) { if(UI()->MouseButtonClicked(1)) { // add point int Time = (int)(((UI()->MouseX()-View.x)*TimeScale)*1000.0f); //float env_y = (UI()->MouseY()-view.y)/TimeScale; float aChannels[4]; pEnvelope->Eval(Time, aChannels); pEnvelope->AddPoint(Time, f2fx(aChannels[0]), f2fx(aChannels[1]), f2fx(aChannels[2]), f2fx(aChannels[3])); } m_pTooltip = Localize("Press right mouse button to create a new point"); } } vec3 aColors[] = {vec3(1,0.2f,0.2f), vec3(0.2f,1,0.2f), vec3(0.2f,0.2f,1), vec3(1,1,0.2f)}; // render lines { UI()->ClipEnable(&View); Graphics()->TextureSet(-1); Graphics()->LinesBegin(); for(int c = 0; c < pEnvelope->m_Channels; c++) { if(s_ActiveChannels&(1<SetColor(aColors[c].r,aColors[c].g,aColors[c].b,1); else Graphics()->SetColor(aColors[c].r*0.5f,aColors[c].g*0.5f,aColors[c].b*0.5f,1); float PrevX = 0; float aResults[4]; pEnvelope->Eval(0.000001f, aResults); float PrevValue = aResults[c]; int Steps = (int)((View.w/UI()->Screen()->w) * Graphics()->ScreenWidth()); for(int i = 1; i <= Steps; i++) { float a = i/(float)Steps; pEnvelope->Eval(a*EndTime, aResults); float v = aResults[c]; v = (v-Bottom)/(Top-Bottom); IGraphics::CLineItem LineItem(View.x + PrevX*View.w, View.y+View.h - PrevValue*View.h, View.x + a*View.w, View.y+View.h - v*View.h); Graphics()->LinesDraw(&LineItem, 1); PrevX = a; PrevValue = v; } } Graphics()->LinesEnd(); UI()->ClipDisable(); } // render curve options { for(int i = 0; i < pEnvelope->m_lPoints.size()-1; i++) { float t0 = pEnvelope->m_lPoints[i].m_Time/1000.0f/EndTime; float t1 = pEnvelope->m_lPoints[i+1].m_Time/1000.0f/EndTime; //dbg_msg("", "%f", end_time); CUIRect v; v.x = CurveBar.x + (t0+(t1-t0)*0.5f) * CurveBar.w; v.y = CurveBar.y; v.h = CurveBar.h; v.w = CurveBar.h; v.x -= v.w/2; void *pId = &pEnvelope->m_lPoints[i].m_Curvetype; const char *paTypeName[] = { "N", "L", "S", "F", "M" }; if(DoButton_Editor(pId, paTypeName[pEnvelope->m_lPoints[i].m_Curvetype], 0, &v, 0, Localize("Switch curve type"))) pEnvelope->m_lPoints[i].m_Curvetype = (pEnvelope->m_lPoints[i].m_Curvetype+1)%NUM_CURVETYPES; } } // render colorbar if(ShowColorBar) { Graphics()->TextureSet(-1); Graphics()->QuadsBegin(); for(int i = 0; i < pEnvelope->m_lPoints.size()-1; i++) { float r0 = fx2f(pEnvelope->m_lPoints[i].m_aValues[0]); float g0 = fx2f(pEnvelope->m_lPoints[i].m_aValues[1]); float b0 = fx2f(pEnvelope->m_lPoints[i].m_aValues[2]); float a0 = fx2f(pEnvelope->m_lPoints[i].m_aValues[3]); float r1 = fx2f(pEnvelope->m_lPoints[i+1].m_aValues[0]); float g1 = fx2f(pEnvelope->m_lPoints[i+1].m_aValues[1]); float b1 = fx2f(pEnvelope->m_lPoints[i+1].m_aValues[2]); float a1 = fx2f(pEnvelope->m_lPoints[i+1].m_aValues[3]); IGraphics::CColorVertex Array[4] = {IGraphics::CColorVertex(0, r0, g0, b0, a0), IGraphics::CColorVertex(1, r1, g1, b1, a1), IGraphics::CColorVertex(2, r1, g1, b1, a1), IGraphics::CColorVertex(3, r0, g0, b0, a0)}; Graphics()->SetColorVertex(Array, 4); float x0 = pEnvelope->m_lPoints[i].m_Time/1000.0f/EndTime; // float y0 = (fx2f(envelope->points[i].values[c])-bottom)/(top-bottom); float x1 = pEnvelope->m_lPoints[i+1].m_Time/1000.0f/EndTime; //float y1 = (fx2f(envelope->points[i+1].values[c])-bottom)/(top-bottom); CUIRect v; v.x = ColorBar.x + x0*ColorBar.w; v.y = ColorBar.y; v.w = (x1-x0)*ColorBar.w; v.h = ColorBar.h; IGraphics::CQuadItem QuadItem(v.x, v.y, v.w, v.h); Graphics()->QuadsDrawTL(&QuadItem, 1); } Graphics()->QuadsEnd(); } // render handles { static bool s_Move = false; int CurrentValue = 0, CurrentTime = 0; Graphics()->TextureSet(-1); Graphics()->QuadsBegin(); for(int c = 0; c < pEnvelope->m_Channels; c++) { if(!(s_ActiveChannels&(1<m_lPoints.size(); i++) { float x0 = pEnvelope->m_lPoints[i].m_Time/1000.0f/EndTime; float y0 = (fx2f(pEnvelope->m_lPoints[i].m_aValues[c])-Bottom)/(Top-Bottom); CUIRect Final; Final.x = View.x + x0*View.w; Final.y = View.y+View.h - y0*View.h; Final.x -= 2.0f; Final.y -= 2.0f; Final.w = 4.0f; Final.h = 4.0f; void *pId = &pEnvelope->m_lPoints[i].m_aValues[c]; if(UI()->MouseInside(&Final)) UI()->SetHotItem(pId); float ColorMod = 1.0f; if(UI()->ActiveItem() == pId) { if(!UI()->MouseButton(0)) { UI()->SetActiveItem(0); s_Move = false; } else { pEnvelope->m_lPoints[i].m_aValues[c] -= f2fx(m_MouseDeltaY*ValueScale); if(Input()->KeyPressed(KEY_LSHIFT) || Input()->KeyPressed(KEY_RSHIFT)) { if(i != 0) { pEnvelope->m_lPoints[i].m_Time += (int)((m_MouseDeltaX*TimeScale)*1000.0f); if(pEnvelope->m_lPoints[i].m_Time < pEnvelope->m_lPoints[i-1].m_Time) pEnvelope->m_lPoints[i].m_Time = pEnvelope->m_lPoints[i-1].m_Time + 1; if(i+1 != pEnvelope->m_lPoints.size() && pEnvelope->m_lPoints[i].m_Time > pEnvelope->m_lPoints[i+1].m_Time) pEnvelope->m_lPoints[i].m_Time = pEnvelope->m_lPoints[i+1].m_Time - 1; } } } ColorMod = 100.0f; Graphics()->SetColor(1,1,1,1); } else if(UI()->HotItem() == pId) { if(UI()->MouseButton(0)) { Selection.clear(); Selection.add(i); UI()->SetActiveItem(pId); } // remove point if(UI()->MouseButtonClicked(1)) pEnvelope->m_lPoints.remove_index(i); ColorMod = 100.0f; Graphics()->SetColor(1,0.75f,0.75f,1); m_pTooltip = Localize("Left mouse to drag. Hold shift to alter time point aswell. Right click to delete."); } if(UI()->ActiveItem() == pId || UI()->HotItem() == pId) { CurrentTime = pEnvelope->m_lPoints[i].m_Time; CurrentValue = pEnvelope->m_lPoints[i].m_aValues[c]; } Graphics()->SetColor(aColors[c].r*ColorMod, aColors[c].g*ColorMod, aColors[c].b*ColorMod, 1.0f); IGraphics::CQuadItem QuadItem(Final.x, Final.y, Final.w, Final.h); Graphics()->QuadsDrawTL(&QuadItem, 1); } } Graphics()->QuadsEnd(); char aBuf[512]; str_format(aBuf, sizeof(aBuf),"%.3f %.3f", CurrentTime/1000.0f, fx2f(CurrentValue)); UI()->DoLabel(&ToolBar, aBuf, 10.0f, 0, -1); } } } int CEditor::PopupMenuFile(CEditor *pEditor, CUIRect View) { static int s_NewMapButton = 0; static int s_SaveButton = 0; static int s_SaveAsButton = 0; static int s_OpenButton = 0; static int s_AppendButton = 0; static int s_ExitButton = 0; CUIRect Slot; View.HSplitTop(2.0f, &Slot, &View); View.HSplitTop(12.0f, &Slot, &View); if(pEditor->DoButton_MenuItem(&s_NewMapButton, Localize("New"), 0, &Slot, 0, Localize("Creates a new map"))) { pEditor->Reset(); pEditor->m_aFileName[0] = 0; return 1; } View.HSplitTop(10.0f, &Slot, &View); View.HSplitTop(12.0f, &Slot, &View); if(pEditor->DoButton_MenuItem(&s_OpenButton, Localize("Open"), 0, &Slot, 0, Localize("Opens a map for editing"))) { pEditor->InvokeFileDialog(IStorage::TYPE_ALL, Localize("Open map"), Localize("Open"), "maps/", "", CallbackOpenMap, pEditor); return 1; } View.HSplitTop(10.0f, &Slot, &View); View.HSplitTop(12.0f, &Slot, &View); if(pEditor->DoButton_MenuItem(&s_AppendButton, Localize("Append"), 0, &Slot, 0, Localize("Opens a map and adds everything from that map to the current one"))) { pEditor->InvokeFileDialog(IStorage::TYPE_ALL, Localize("Append map"), Localize("Append"), "maps/", "", CallbackAppendMap, pEditor); return 1; } View.HSplitTop(10.0f, &Slot, &View); View.HSplitTop(12.0f, &Slot, &View); if(pEditor->DoButton_MenuItem(&s_SaveButton, Localize("Save"), 0, &Slot, 0, Localize("Saves the current map"))) { if(pEditor->m_aFileName[0]) pEditor->Save(pEditor->m_aFileName); else pEditor->InvokeFileDialog(IStorage::TYPE_SAVE, Localize("Save Map"), Localize("Save"), "maps/", "", CallbackSaveMap, pEditor); return 1; } View.HSplitTop(2.0f, &Slot, &View); View.HSplitTop(12.0f, &Slot, &View); if(pEditor->DoButton_MenuItem(&s_SaveAsButton, Localize("Save As"), 0, &Slot, 0, Localize("Saves the current map under a new name"))) { pEditor->InvokeFileDialog(IStorage::TYPE_SAVE, Localize("Save Map"), Localize("Save"), "maps/", "", CallbackSaveMap, pEditor); return 1; } View.HSplitTop(10.0f, &Slot, &View); View.HSplitTop(12.0f, &Slot, &View); if(pEditor->DoButton_MenuItem(&s_ExitButton, "Exit", 0, &Slot, 0, Localize("Exits from the editor"))) { g_Config.m_ClEditor = 0; return 1; } return 0; } void CEditor::RenderMenubar(CUIRect MenuBar) { static CUIRect s_File /*, view, help*/; MenuBar.VSplitLeft(60.0f, &s_File, &MenuBar); if(DoButton_Menu(&s_File, "File", 0, &s_File, 0, 0)) UiInvokePopupMenu(&s_File, 1, s_File.x, s_File.y+s_File.h-1.0f, 120, 150, PopupMenuFile, this); /* menubar.VSplitLeft(5.0f, 0, &menubar); menubar.VSplitLeft(60.0f, &view, &menubar); if(do_editor_button(&view, "View", 0, &view, draw_editor_button_menu, 0, 0)) (void)0; menubar.VSplitLeft(5.0f, 0, &menubar); menubar.VSplitLeft(60.0f, &help, &menubar); if(do_editor_button(&help, "Help", 0, &help, draw_editor_button_menu, 0, 0)) (void)0; */ } void CEditor::Render() { // basic start Graphics()->Clear(1.0f, 0.0f, 1.0f); CUIRect View = *UI()->Screen(); Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h); // reset tip m_pTooltip = 0; // render checker RenderBackground(View, ms_CheckerTexture, 32.0f, 1.0f); CUIRect MenuBar, CModeBar, ToolBar, StatusBar, EnvelopeEditor, ToolBox; if(m_GuiActive) { View.HSplitTop(16.0f, &MenuBar, &View); View.HSplitTop(53.0f, &ToolBar, &View); View.VSplitLeft(80.0f, &ToolBox, &View); View.HSplitBottom(16.0f, &View, &StatusBar); if(m_ShowEnvelopeEditor) { float size = 125.0f; if(m_ShowEnvelopeEditor == 2) size *= 2.0f; else if(m_ShowEnvelopeEditor == 3) size *= 3.0f; View.HSplitBottom(size, &View, &EnvelopeEditor); } } // a little hack for now if(m_Mode == MODE_LAYERS) DoMapEditor(View, ToolBar); if(m_GuiActive) { float Brightness = 0.25f; RenderBackground(MenuBar, ms_BackgroundTexture, 128.0f, Brightness*0); MenuBar.Margin(2.0f, &MenuBar); RenderBackground(ToolBox, ms_BackgroundTexture, 128.0f, Brightness); ToolBox.Margin(2.0f, &ToolBox); RenderBackground(ToolBar, ms_BackgroundTexture, 128.0f, Brightness); ToolBar.Margin(2.0f, &ToolBar); ToolBar.VSplitLeft(100.0f, &CModeBar, &ToolBar); RenderBackground(StatusBar, ms_BackgroundTexture, 128.0f, Brightness); StatusBar.Margin(2.0f, &StatusBar); // do the toolbar if(m_Mode == MODE_LAYERS) DoToolbar(ToolBar); if(m_ShowEnvelopeEditor) { RenderBackground(EnvelopeEditor, ms_BackgroundTexture, 128.0f, Brightness); EnvelopeEditor.Margin(2.0f, &EnvelopeEditor); } } if(m_Mode == MODE_LAYERS) RenderLayers(ToolBox, ToolBar, View); else if(m_Mode == MODE_IMAGES) RenderImages(ToolBox, ToolBar, View); Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h); if(m_GuiActive) { RenderMenubar(MenuBar); RenderModebar(CModeBar); if(m_ShowEnvelopeEditor) RenderEnvelopeEditor(EnvelopeEditor); } if(m_Dialog == DIALOG_FILE) { static int s_NullUiTarget = 0; UI()->SetHotItem(&s_NullUiTarget); RenderFileDialog(); } UiDoPopupMenu(); if(m_GuiActive) RenderStatusbar(StatusBar); // if(g_Config.m_EdShowkeys) { Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h); CTextCursor Cursor; TextRender()->SetCursor(&Cursor, View.x+10, View.y+View.h-24-10, 24.0f, TEXTFLAG_RENDER); int NKeys = 0; for(int i = 0; i < KEY_LAST; i++) { if(Input()->KeyPressed(i)) { if(NKeys) TextRender()->TextEx(&Cursor, " + ", -1); TextRender()->TextEx(&Cursor, Input()->KeyName(i), -1); NKeys++; } } } if(m_ShowMousePointer) { // render butt ugly mouse cursor float mx = UI()->MouseX(); float my = UI()->MouseY(); Graphics()->TextureSet(ms_CursorTexture); Graphics()->QuadsBegin(); if(ms_pUiGotContext == UI()->HotItem()) Graphics()->SetColor(1,0,0,1); IGraphics::CQuadItem QuadItem(mx,my, 16.0f, 16.0f); Graphics()->QuadsDrawTL(&QuadItem, 1); Graphics()->QuadsEnd(); } } void CEditor::Reset(bool CreateDefault) { m_Map.Clean(); // create default layers if(CreateDefault) m_Map.CreateDefault(ms_EntitiesTexture); /* { }*/ m_SelectedLayer = 0; m_SelectedGroup = 0; m_SelectedQuad = -1; m_SelectedPoints = 0; m_SelectedEnvelope = 0; m_SelectedImage = 0; } void CEditorMap::MakeGameLayer(CLayer *pLayer) { m_pGameLayer = (CLayerGame *)pLayer; m_pGameLayer->m_pEditor = m_pEditor; m_pGameLayer->m_TexId = m_pEditor->ms_EntitiesTexture; } void CEditorMap::MakeGameGroup(CLayerGroup *pGroup) { m_pGameGroup = pGroup; m_pGameGroup->m_GameGroup = true; m_pGameGroup->m_pName = "Game"; } void CEditorMap::Clean() { m_lGroups.delete_all(); m_lEnvelopes.delete_all(); m_lImages.delete_all(); m_pGameLayer = 0x0; m_pGameGroup = 0x0; } void CEditorMap::CreateDefault(int EntitiesTexture) { MakeGameGroup(NewGroup()); MakeGameLayer(new CLayerGame(50, 50)); m_pGameGroup->AddLayer(m_pGameLayer); } void CEditor::Init() { m_pInput = Kernel()->RequestInterface(); m_pClient = Kernel()->RequestInterface(); m_pGraphics = Kernel()->RequestInterface(); m_pTextRender = Kernel()->RequestInterface(); m_RenderTools.m_pGraphics = m_pGraphics; m_RenderTools.m_pUI = &m_UI; m_UI.SetGraphics(m_pGraphics, m_pTextRender); m_Map.m_pEditor = this; ms_CheckerTexture = Graphics()->LoadTexture("editor/checker.png", CImageInfo::FORMAT_AUTO, 0); ms_BackgroundTexture = Graphics()->LoadTexture("editor/background.png", CImageInfo::FORMAT_AUTO, 0); ms_CursorTexture = Graphics()->LoadTexture("editor/cursor.png", CImageInfo::FORMAT_AUTO, 0); ms_EntitiesTexture = Graphics()->LoadTexture("editor/entities.png", CImageInfo::FORMAT_AUTO, 0); m_TilesetPicker.m_pEditor = this; m_TilesetPicker.MakePalette(); m_TilesetPicker.m_Readonly = true; m_Brush.m_pMap = &m_Map; Reset(); } void CEditor::DoMapBorder() { CLayerTiles *pT = (CLayerTiles *)GetSelectedLayerType(0, LAYERTYPE_TILES); for(int i = 0; i < pT->m_Width*2; ++i) pT->m_pTiles[i].m_Index = 1; for(int i = 0; i < pT->m_Width*pT->m_Height; ++i) { if(i%pT->m_Width < 2 || i%pT->m_Width > pT->m_Width-3) pT->m_pTiles[i].m_Index = 1; } for(int i = ((pT->m_Width-2)*pT->m_Height); i < pT->m_Width*pT->m_Height; ++i) pT->m_pTiles[i].m_Index = 1; } void CEditor::UpdateAndRender() { static int s_MouseX = 0; static int s_MouseY = 0; if(m_Animate) m_AnimateTime = (time_get()-m_AnimateStart)/(float)time_freq(); else m_AnimateTime = 0; ms_pUiGotContext = 0; // handle mouse movement float mx, my, Mwx, Mwy; int rx, ry; { Input()->MouseRelative(&rx, &ry); m_MouseDeltaX = rx; m_MouseDeltaY = ry; if(!m_LockMouse) { s_MouseX += rx; s_MouseY += ry; } if(s_MouseX < 0) s_MouseX = 0; if(s_MouseY < 0) s_MouseY = 0; if(s_MouseX > UI()->Screen()->w) s_MouseX = (int)UI()->Screen()->w; if(s_MouseY > UI()->Screen()->h) s_MouseY = (int)UI()->Screen()->h; // update the ui mx = s_MouseX; my = s_MouseY; Mwx = 0; Mwy = 0; // fix correct world x and y CLayerGroup *g = GetSelectedGroup(); if(g) { float aPoints[4]; g->Mapping(aPoints); float WorldWidth = aPoints[2]-aPoints[0]; float WorldHeight = aPoints[3]-aPoints[1]; Mwx = aPoints[0] + WorldWidth * (s_MouseX/UI()->Screen()->w); Mwy = aPoints[1] + WorldHeight * (s_MouseY/UI()->Screen()->h); m_MouseDeltaWx = m_MouseDeltaX*(WorldWidth / UI()->Screen()->w); m_MouseDeltaWy = m_MouseDeltaY*(WorldHeight / UI()->Screen()->h); } int Buttons = 0; if(Input()->KeyPressed(KEY_MOUSE_1)) Buttons |= 1; if(Input()->KeyPressed(KEY_MOUSE_2)) Buttons |= 2; if(Input()->KeyPressed(KEY_MOUSE_3)) Buttons |= 4; UI()->Update(mx,my,Mwx,Mwy,Buttons); } // toggle gui if(Input()->KeyDown(KEY_TAB)) m_GuiActive = !m_GuiActive; if(Input()->KeyDown(KEY_F5)) Save("maps/debug_test2.map"); if(Input()->KeyDown(KEY_F6)) Load("maps/debug_test2.map"); if(Input()->KeyDown(KEY_F8)) Load("maps/debug_test.map"); if(Input()->KeyDown(KEY_F7)) Save("maps/quicksave.map"); if(Input()->KeyDown(KEY_F10)) m_ShowMousePointer = false; Render(); if(Input()->KeyDown(KEY_F10)) { Graphics()->TakeScreenshot(); m_ShowMousePointer = true; } Input()->ClearEvents(); } IEditor *CreateEditor() { return new CEditor; }