added a different mouse sens for menus/editor
[twcon.git] / src / game / editor / editor.cpp
blobbc00c4a2a9fe6c112347f7eb074441582bc5d8ea
1 /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
2 /* If you are missing that file, acquire a complete release at teeworlds.com. */
4 #include <base/system.h>
6 #include <engine/shared/datafile.h>
7 #include <engine/shared/config.h>
8 #include <engine/client.h>
9 #include <engine/console.h>
10 #include <engine/graphics.h>
11 #include <engine/textrender.h>
12 #include <engine/input.h>
13 #include <engine/keys.h>
14 #include <engine/storage.h>
16 #include <game/client/ui.h>
17 #include <game/gamecore.h>
18 #include <game/client/render.h>
19 #include <game/generated/client_data.h>
21 #include "editor.h"
22 #include <game/client/lineinput.h>
24 #include <game/localization.h>
26 int CEditor::ms_CheckerTexture;
27 int CEditor::ms_BackgroundTexture;
28 int CEditor::ms_CursorTexture;
29 int CEditor::ms_EntitiesTexture;
30 const void* CEditor::ms_pUiGotContext;
32 enum
34 BUTTON_CONTEXT=1,
37 CEditorImage::~CEditorImage()
39 m_pEditor->Graphics()->UnloadTexture(m_TexID);
42 CLayerGroup::CLayerGroup()
44 m_pName = "";
45 m_Visible = true;
46 m_SaveToMap = true;
47 m_GameGroup = false;
48 m_OffsetX = 0;
49 m_OffsetY = 0;
50 m_ParallaxX = 100;
51 m_ParallaxY = 100;
53 m_UseClipping = 0;
54 m_ClipX = 0;
55 m_ClipY = 0;
56 m_ClipW = 0;
57 m_ClipH = 0;
60 CLayerGroup::~CLayerGroup()
62 Clear();
65 void CLayerGroup::Convert(CUIRect *pRect)
67 pRect->x += m_OffsetX;
68 pRect->y += m_OffsetY;
71 void CLayerGroup::Mapping(float *pPoints)
73 m_pMap->m_pEditor->RenderTools()->MapscreenToWorld(
74 m_pMap->m_pEditor->m_WorldOffsetX, m_pMap->m_pEditor->m_WorldOffsetY,
75 m_ParallaxX/100.0f, m_ParallaxY/100.0f,
76 m_OffsetX, m_OffsetY,
77 m_pMap->m_pEditor->Graphics()->ScreenAspect(), m_pMap->m_pEditor->m_WorldZoom, pPoints);
79 pPoints[0] += m_pMap->m_pEditor->m_EditorOffsetX;
80 pPoints[1] += m_pMap->m_pEditor->m_EditorOffsetY;
81 pPoints[2] += m_pMap->m_pEditor->m_EditorOffsetX;
82 pPoints[3] += m_pMap->m_pEditor->m_EditorOffsetY;
85 void CLayerGroup::MapScreen()
87 float aPoints[4];
88 Mapping(aPoints);
89 m_pMap->m_pEditor->Graphics()->MapScreen(aPoints[0], aPoints[1], aPoints[2], aPoints[3]);
92 void CLayerGroup::Render()
94 MapScreen();
95 IGraphics *pGraphics = m_pMap->m_pEditor->Graphics();
97 if(m_UseClipping)
99 float aPoints[4];
100 m_pMap->m_pGameGroup->Mapping(aPoints);
101 float x0 = (m_ClipX - aPoints[0]) / (aPoints[2]-aPoints[0]);
102 float y0 = (m_ClipY - aPoints[1]) / (aPoints[3]-aPoints[1]);
103 float x1 = ((m_ClipX+m_ClipW) - aPoints[0]) / (aPoints[2]-aPoints[0]);
104 float y1 = ((m_ClipY+m_ClipH) - aPoints[1]) / (aPoints[3]-aPoints[1]);
106 pGraphics->ClipEnable((int)(x0*pGraphics->ScreenWidth()), (int)(y0*pGraphics->ScreenHeight()),
107 (int)((x1-x0)*pGraphics->ScreenWidth()), (int)((y1-y0)*pGraphics->ScreenHeight()));
110 for(int i = 0; i < m_lLayers.size(); i++)
112 if(m_lLayers[i]->m_Visible && m_lLayers[i] != m_pMap->m_pGameLayer)
114 if(m_pMap->m_pEditor->m_ShowDetail || !(m_lLayers[i]->m_Flags&LAYERFLAG_DETAIL))
115 m_lLayers[i]->Render();
119 pGraphics->ClipDisable();
122 void CLayerGroup::AddLayer(CLayer *l)
124 m_pMap->m_Modified = true;
125 m_lLayers.add(l);
128 void CLayerGroup::DeleteLayer(int Index)
130 if(Index < 0 || Index >= m_lLayers.size()) return;
131 delete m_lLayers[Index];
132 m_lLayers.remove_index(Index);
133 m_pMap->m_Modified = true;
136 void CLayerGroup::GetSize(float *w, float *h)
138 *w = 0; *h = 0;
139 for(int i = 0; i < m_lLayers.size(); i++)
141 float lw, lh;
142 m_lLayers[i]->GetSize(&lw, &lh);
143 *w = max(*w, lw);
144 *h = max(*h, lh);
149 int CLayerGroup::SwapLayers(int Index0, int Index1)
151 if(Index0 < 0 || Index0 >= m_lLayers.size()) return Index0;
152 if(Index1 < 0 || Index1 >= m_lLayers.size()) return Index0;
153 if(Index0 == Index1) return Index0;
154 m_pMap->m_Modified = true;
155 swap(m_lLayers[Index0], m_lLayers[Index1]);
156 return Index1;
159 void CEditorImage::AnalyseTileFlags()
161 mem_zero(m_aTileFlags, sizeof(m_aTileFlags));
163 int tw = m_Width/16; // tilesizes
164 int th = m_Height/16;
165 if ( tw == th )
167 unsigned char *pPixelData = (unsigned char *)m_pData;
169 int TileID = 0;
170 for(int ty = 0; ty < 16; ty++)
171 for(int tx = 0; tx < 16; tx++, TileID++)
173 bool Opaque = true;
174 for(int x = 0; x < tw; x++)
175 for(int y = 0; y < th; y++)
177 int p = (ty*tw+y)*m_Width + tx*tw+x;
178 if(pPixelData[p*4+3] < 250)
180 Opaque = false;
181 break;
185 if(Opaque)
186 m_aTileFlags[TileID] |= TILEFLAG_OPAQUE;
192 /********************************************************
193 OTHER
194 *********************************************************/
196 // copied from gc_menu.cpp, should be more generalized
197 //extern int ui_do_edit_box(void *id, const CUIRect *rect, char *str, int str_size, float font_size, bool hidden=false);
199 int CEditor::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrSize, float FontSize, bool Hidden)
201 int Inside = UI()->MouseInside(pRect);
202 bool ReturnValue = false;
203 static int s_AtIndex = 0;
205 if(UI()->LastActiveItem() == pID)
207 int Len = str_length(pStr);
209 if(Inside && UI()->MouseButton(0))
211 int MxRel = (int)(UI()->MouseX() - pRect->x);
213 for (int i = 1; i <= Len; i++)
215 if (TextRender()->TextWidth(0, FontSize, pStr, i) + 10 > MxRel)
217 s_AtIndex = i - 1;
218 break;
221 if (i == Len)
222 s_AtIndex = Len;
226 for(int i = 0; i < Input()->NumEvents(); i++)
228 Len = str_length(pStr);
229 ReturnValue |= CLineInput::Manipulate(Input()->GetEvent(i), pStr, StrSize, &Len, &s_AtIndex);
233 bool JustGotActive = false;
235 if(UI()->ActiveItem() == pID)
237 if(!UI()->MouseButton(0))
238 UI()->SetActiveItem(0);
240 else if(UI()->HotItem() == pID)
242 if(UI()->MouseButton(0))
244 if (UI()->LastActiveItem() != pID)
245 JustGotActive = true;
246 UI()->SetActiveItem(pID);
250 if(Inside)
251 UI()->SetHotItem(pID);
253 CUIRect Textbox = *pRect;
254 RenderTools()->DrawUIRect(&Textbox, vec4(1,1,1,0.5f), CUI::CORNER_ALL, 3.0f);
255 Textbox.VMargin(3.0f, &Textbox);
257 const char *pDisplayStr = pStr;
258 char aStars[128];
260 if(Hidden)
262 unsigned s = str_length(pStr);
263 if(s >= sizeof(aStars))
264 s = sizeof(aStars)-1;
265 for(unsigned int i = 0; i < s; ++i)
266 aStars[i] = '*';
267 aStars[s] = 0;
268 pDisplayStr = aStars;
271 UI()->DoLabel(&Textbox, pDisplayStr, FontSize, -1);
273 //TODO: make it blink
274 if(UI()->LastActiveItem() == pID && !JustGotActive)
276 float w = TextRender()->TextWidth(0, FontSize, pDisplayStr, s_AtIndex);
277 Textbox = *pRect;
278 Textbox.VSplitLeft(2.0f, 0, &Textbox);
279 Textbox.x += w*UI()->Scale();
280 Textbox.y -= FontSize/10.f;
282 UI()->DoLabel(&Textbox, "|", FontSize*1.1f, -1);
285 return ReturnValue;
288 vec4 CEditor::ButtonColorMul(const void *pID)
290 if(UI()->ActiveItem() == pID)
291 return vec4(1,1,1,0.5f);
292 else if(UI()->HotItem() == pID)
293 return vec4(1,1,1,1.5f);
294 return vec4(1,1,1,1);
297 float CEditor::UiDoScrollbarV(const void *pID, const CUIRect *pRect, float Current)
299 CUIRect Handle;
300 static float s_OffsetY;
301 pRect->HSplitTop(33, &Handle, 0);
303 Handle.y += (pRect->h-Handle.h)*Current;
305 // logic
306 float Ret = Current;
307 int Inside = UI()->MouseInside(&Handle);
309 if(UI()->ActiveItem() == pID)
311 if(!UI()->MouseButton(0))
312 UI()->SetActiveItem(0);
314 float Min = pRect->y;
315 float Max = pRect->h-Handle.h;
316 float Cur = UI()->MouseY()-s_OffsetY;
317 Ret = (Cur-Min)/Max;
318 if(Ret < 0.0f) Ret = 0.0f;
319 if(Ret > 1.0f) Ret = 1.0f;
321 else if(UI()->HotItem() == pID)
323 if(UI()->MouseButton(0))
325 UI()->SetActiveItem(pID);
326 s_OffsetY = UI()->MouseY()-Handle.y;
330 if(Inside)
331 UI()->SetHotItem(pID);
333 // render
334 CUIRect Rail;
335 pRect->VMargin(5.0f, &Rail);
336 RenderTools()->DrawUIRect(&Rail, vec4(1,1,1,0.25f), 0, 0.0f);
338 CUIRect Slider = Handle;
339 Slider.w = Rail.x-Slider.x;
340 RenderTools()->DrawUIRect(&Slider, vec4(1,1,1,0.25f), CUI::CORNER_L, 2.5f);
341 Slider.x = Rail.x+Rail.w;
342 RenderTools()->DrawUIRect(&Slider, vec4(1,1,1,0.25f), CUI::CORNER_R, 2.5f);
344 Slider = Handle;
345 Slider.Margin(5.0f, &Slider);
346 RenderTools()->DrawUIRect(&Slider, vec4(1,1,1,0.25f)*ButtonColorMul(pID), CUI::CORNER_ALL, 2.5f);
348 return Ret;
351 vec4 CEditor::GetButtonColor(const void *pID, int Checked)
353 if(Checked < 0)
354 return vec4(0,0,0,0.5f);
356 if(Checked > 0)
358 if(UI()->HotItem() == pID)
359 return vec4(1,0,0,0.75f);
360 return vec4(1,0,0,0.5f);
363 if(UI()->HotItem() == pID)
364 return vec4(1,1,1,0.75f);
365 return vec4(1,1,1,0.5f);
368 int CEditor::DoButton_Editor_Common(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip)
370 if(UI()->MouseInside(pRect))
372 if(Flags&BUTTON_CONTEXT)
373 ms_pUiGotContext = pID;
374 if(m_pTooltip)
375 m_pTooltip = pToolTip;
378 if(UI()->HotItem() == pID && pToolTip)
379 m_pTooltip = (const char *)pToolTip;
381 return UI()->DoButtonLogic(pID, pText, Checked, pRect);
383 // Draw here
384 //return UI()->DoButton(id, text, checked, r, draw_func, 0);
388 int CEditor::DoButton_Editor(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip)
390 RenderTools()->DrawUIRect(pRect, GetButtonColor(pID, Checked), CUI::CORNER_ALL, 3.0f);
391 CUIRect NewRect = *pRect;
392 NewRect.y += NewRect.h/2.0f-7.0f;
393 float tw = min(TextRender()->TextWidth(0, 10.0f, pText, -1), NewRect.w);
394 CTextCursor Cursor;
395 TextRender()->SetCursor(&Cursor, NewRect.x + NewRect.w/2-tw/2, NewRect.y - 1.0f, 10.0f, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END);
396 Cursor.m_LineWidth = NewRect.w;
397 TextRender()->TextEx(&Cursor, pText, -1);
398 return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip);
401 int CEditor::DoButton_File(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip)
403 if(Checked)
404 RenderTools()->DrawUIRect(pRect, GetButtonColor(pID, Checked), CUI::CORNER_ALL, 3.0f);
406 CUIRect t = *pRect;
407 t.VMargin(5.0f, &t);
408 UI()->DoLabel(&t, pText, 10, -1, -1);
409 return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip);
412 int CEditor::DoButton_Menu(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip)
414 CUIRect r = *pRect;
415 RenderTools()->DrawUIRect(&r, vec4(0.5f, 0.5f, 0.5f, 1.0f), CUI::CORNER_T, 3.0f);
417 r = *pRect;
418 r.VMargin(5.0f, &r);
419 UI()->DoLabel(&r, pText, 10, -1, -1);
420 return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip);
423 int CEditor::DoButton_MenuItem(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip)
425 if(UI()->HotItem() == pID || Checked)
426 RenderTools()->DrawUIRect(pRect, GetButtonColor(pID, Checked), CUI::CORNER_ALL, 3.0f);
428 CUIRect t = *pRect;
429 t.VMargin(5.0f, &t);
430 CTextCursor Cursor;
431 TextRender()->SetCursor(&Cursor, t.x, t.y - 1.0f, 10.0f, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END);
432 Cursor.m_LineWidth = t.w;
433 TextRender()->TextEx(&Cursor, pText, -1);
434 return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip);
437 int CEditor::DoButton_Tab(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip)
439 RenderTools()->DrawUIRect(pRect, GetButtonColor(pID, Checked), CUI::CORNER_T, 5.0f);
440 CUIRect NewRect = *pRect;
441 NewRect.y += NewRect.h/2.0f-7.0f;
442 UI()->DoLabel(&NewRect, pText, 10, 0, -1);
443 return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip);
446 int CEditor::DoButton_Ex(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip, int Corners)
448 RenderTools()->DrawUIRect(pRect, GetButtonColor(pID, Checked), Corners, 3.0f);
449 CUIRect NewRect = *pRect;
450 NewRect.y += NewRect.h/2.0f-7.0f;
451 UI()->DoLabel(&NewRect, pText, 10, 0, -1);
452 return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip);
455 int CEditor::DoButton_ButtonInc(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip)
457 RenderTools()->DrawUIRect(pRect, GetButtonColor(pID, Checked), CUI::CORNER_R, 3.0f);
458 UI()->DoLabel(pRect, pText?pText:"+", 10, 0, -1);
459 return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip);
462 int CEditor::DoButton_ButtonDec(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip)
464 RenderTools()->DrawUIRect(pRect, GetButtonColor(pID, Checked), CUI::CORNER_L, 3.0f);
465 UI()->DoLabel(pRect, pText?pText:"-", 10, 0, -1);
466 return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip);
469 void CEditor::RenderBackground(CUIRect View, int Texture, float Size, float Brightness)
471 Graphics()->TextureSet(Texture);
472 Graphics()->BlendNormal();
473 Graphics()->QuadsBegin();
474 Graphics()->SetColor(Brightness, Brightness, Brightness, 1.0f);
475 Graphics()->QuadsSetSubset(0,0, View.w/Size, View.h/Size);
476 IGraphics::CQuadItem QuadItem(View.x, View.y, View.w, View.h);
477 Graphics()->QuadsDrawTL(&QuadItem, 1);
478 Graphics()->QuadsEnd();
481 int CEditor::UiDoValueSelector(void *pID, CUIRect *pRect, const char *pLabel, int Current, int Min, int Max, int Step, float Scale, const char *pToolTip)
483 // logic
484 static float s_Value;
485 int Inside = UI()->MouseInside(pRect);
487 if(UI()->ActiveItem() == pID)
489 if(!UI()->MouseButton(0))
491 m_LockMouse = false;
492 UI()->SetActiveItem(0);
494 else
496 if(Input()->KeyPressed(KEY_LSHIFT) || Input()->KeyPressed(KEY_RSHIFT))
497 s_Value += m_MouseDeltaX*0.05f;
498 else
499 s_Value += m_MouseDeltaX;
501 if(absolute(s_Value) > Scale)
503 int Count = (int)(s_Value/Scale);
504 s_Value = fmod(s_Value, Scale);
505 Current += Step*Count;
506 if(Current < Min)
507 Current = Min;
508 if(Current > Max)
509 Current = Max;
512 if(pToolTip)
513 m_pTooltip = pToolTip;
515 else if(UI()->HotItem() == pID)
517 if(UI()->MouseButton(0))
519 m_LockMouse = true;
520 s_Value = 0;
521 UI()->SetActiveItem(pID);
523 if(pToolTip)
524 m_pTooltip = pToolTip;
527 if(Inside)
528 UI()->SetHotItem(pID);
530 // render
531 char aBuf[128];
532 str_format(aBuf, sizeof(aBuf),"%s %d", pLabel, Current);
533 RenderTools()->DrawUIRect(pRect, GetButtonColor(pID, 0), CUI::CORNER_ALL, 5.0f);
534 pRect->y += pRect->h/2.0f-7.0f;
535 UI()->DoLabel(pRect, aBuf, 10, 0, -1);
537 return Current;
540 CLayerGroup *CEditor::GetSelectedGroup()
542 if(m_SelectedGroup >= 0 && m_SelectedGroup < m_Map.m_lGroups.size())
543 return m_Map.m_lGroups[m_SelectedGroup];
544 return 0x0;
547 CLayer *CEditor::GetSelectedLayer(int Index)
549 CLayerGroup *pGroup = GetSelectedGroup();
550 if(!pGroup)
551 return 0x0;
553 if(m_SelectedLayer >= 0 && m_SelectedLayer < m_Map.m_lGroups[m_SelectedGroup]->m_lLayers.size())
554 return pGroup->m_lLayers[m_SelectedLayer];
555 return 0x0;
558 CLayer *CEditor::GetSelectedLayerType(int Index, int Type)
560 CLayer *p = GetSelectedLayer(Index);
561 if(p && p->m_Type == Type)
562 return p;
563 return 0x0;
566 CQuad *CEditor::GetSelectedQuad()
568 CLayerQuads *ql = (CLayerQuads *)GetSelectedLayerType(0, LAYERTYPE_QUADS);
569 if(!ql)
570 return 0;
571 if(m_SelectedQuad >= 0 && m_SelectedQuad < ql->m_lQuads.size())
572 return &ql->m_lQuads[m_SelectedQuad];
573 return 0;
576 void CEditor::CallbackOpenMap(const char *pFileName, int StorageType, void *pUser)
578 CEditor *pEditor = (CEditor*)pUser;
579 if(pEditor->Load(pFileName, StorageType))
581 str_copy(pEditor->m_aFileName, pFileName, 512);
582 pEditor->m_ValidSaveFilename = StorageType == IStorage::TYPE_SAVE && pEditor->m_pFileDialogPath == pEditor->m_aFileDialogCurrentFolder;
583 pEditor->SortImages();
584 pEditor->m_Dialog = DIALOG_NONE;
585 pEditor->m_Map.m_Modified = false;
588 void CEditor::CallbackAppendMap(const char *pFileName, int StorageType, void *pUser)
590 CEditor *pEditor = (CEditor*)pUser;
591 if(pEditor->Append(pFileName, StorageType))
592 pEditor->m_aFileName[0] = 0;
593 else
594 pEditor->SortImages();
596 pEditor->m_Dialog = DIALOG_NONE;
598 void CEditor::CallbackSaveMap(const char *pFileName, int StorageType, void *pUser)
600 CEditor *pEditor = static_cast<CEditor*>(pUser);
601 char aBuf[1024];
602 const int Length = str_length(pFileName);
603 // add map extension
604 if(Length <= 4 || pFileName[Length-4] != '.' || str_comp_nocase(pFileName+Length-3, "map"))
606 str_format(aBuf, sizeof(aBuf), "%s.map", pFileName);
607 pFileName = aBuf;
610 if(pEditor->Save(pFileName))
612 str_copy(pEditor->m_aFileName, pFileName, sizeof(pEditor->m_aFileName));
613 pEditor->m_ValidSaveFilename = StorageType == IStorage::TYPE_SAVE && pEditor->m_pFileDialogPath == pEditor->m_aFileDialogCurrentFolder;
614 pEditor->m_Map.m_Modified = false;
617 pEditor->m_Dialog = DIALOG_NONE;
620 void CEditor::DoToolbar(CUIRect ToolBar)
622 CUIRect TB_Top, TB_Bottom;
623 CUIRect Button;
625 ToolBar.HSplitTop(ToolBar.h/2.0f, &TB_Top, &TB_Bottom);
627 TB_Top.HSplitBottom(2.5f, &TB_Top, 0);
628 TB_Bottom.HSplitTop(2.5f, 0, &TB_Bottom);
630 // ctrl+o to open
631 if(Input()->KeyDown('o') && (Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL)))
633 if(HasUnsavedData())
635 m_PopupEventType = POPEVENT_LOAD;
636 m_PopupEventActivated = true;
638 else
639 InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_MAP, "Load map", "Load", "maps", "", CallbackOpenMap, this);
642 // ctrl+s to save
643 if(Input()->KeyDown('s') && (Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL)))
645 if(m_aFileName[0] && m_ValidSaveFilename)
647 str_copy(m_aFileSaveName, m_aFileName, sizeof(m_aFileSaveName));
648 m_PopupEventType = POPEVENT_SAVE;
649 m_PopupEventActivated = true;
651 else
652 InvokeFileDialog(IStorage::TYPE_SAVE, FILETYPE_MAP, "Save map", "Save", "maps", "", CallbackSaveMap, this);
655 // detail button
656 TB_Top.VSplitLeft(30.0f, &Button, &TB_Top);
657 static int s_HqButton = 0;
658 if(DoButton_Editor(&s_HqButton, "HD", m_ShowDetail, &Button, 0, "[ctrl+h] Toggle High Detail") ||
659 (Input()->KeyDown('h') && (Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL))))
661 m_ShowDetail = !m_ShowDetail;
664 TB_Top.VSplitLeft(5.0f, 0, &TB_Top);
666 // animation button
667 TB_Top.VSplitLeft(40.0f, &Button, &TB_Top);
668 static int s_AnimateButton = 0;
669 if(DoButton_Editor(&s_AnimateButton, "Anim", m_Animate, &Button, 0, "[ctrl+m] Toggle animation") ||
670 (Input()->KeyDown('m') && (Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL))))
672 m_AnimateStart = time_get();
673 m_Animate = !m_Animate;
676 TB_Top.VSplitLeft(5.0f, 0, &TB_Top);
678 // proof button
679 TB_Top.VSplitLeft(40.0f, &Button, &TB_Top);
680 static int s_ProofButton = 0;
681 if(DoButton_Editor(&s_ProofButton, "Proof", m_ProofBorders, &Button, 0, "[ctrl+p] Toggles proof borders. These borders represent what a player maximum can see.") ||
682 (Input()->KeyDown('p') && (Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL))))
684 m_ProofBorders = !m_ProofBorders;
687 TB_Top.VSplitLeft(5.0f, 0, &TB_Top);
689 // tile info button
690 TB_Top.VSplitLeft(40.0f, &Button, &TB_Top);
691 static int s_TileInfoButton = 0;
692 if(DoButton_Editor(&s_TileInfoButton, "Info", m_ShowTileInfo, &Button, 0, "[ctrl+i] Show tile informations") ||
693 (Input()->KeyDown('i') && (Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL))))
695 m_ShowTileInfo = !m_ShowTileInfo;
698 TB_Top.VSplitLeft(15.0f, 0, &TB_Top);
700 // zoom group
701 TB_Top.VSplitLeft(30.0f, &Button, &TB_Top);
702 static int s_ZoomOutButton = 0;
703 if(DoButton_Ex(&s_ZoomOutButton, "ZO", 0, &Button, 0, "[NumPad-] Zoom out", CUI::CORNER_L) || Input()->KeyDown(KEY_KP_MINUS))
704 m_ZoomLevel += 50;
706 TB_Top.VSplitLeft(30.0f, &Button, &TB_Top);
707 static int s_ZoomNormalButton = 0;
708 if(DoButton_Ex(&s_ZoomNormalButton, "1:1", 0, &Button, 0, "[NumPad*] Zoom to normal and remove editor offset", 0) || Input()->KeyDown(KEY_KP_MULTIPLY))
710 m_EditorOffsetX = 0;
711 m_EditorOffsetY = 0;
712 m_ZoomLevel = 100;
715 TB_Top.VSplitLeft(30.0f, &Button, &TB_Top);
716 static int s_ZoomInButton = 0;
717 if(DoButton_Ex(&s_ZoomInButton, "ZI", 0, &Button, 0, "[NumPad+] Zoom in", CUI::CORNER_R) || Input()->KeyDown(KEY_KP_PLUS))
718 m_ZoomLevel -= 50;
720 TB_Top.VSplitLeft(10.0f, 0, &TB_Top);
722 // animation speed
723 TB_Top.VSplitLeft(30.0f, &Button, &TB_Top);
724 static int s_AnimFasterButton = 0;
725 if(DoButton_Ex(&s_AnimFasterButton, "A+", 0, &Button, 0, "Increase animation speed", CUI::CORNER_L))
726 m_AnimateSpeed += 0.5f;
728 TB_Top.VSplitLeft(30.0f, &Button, &TB_Top);
729 static int s_AnimNormalButton = 0;
730 if(DoButton_Ex(&s_AnimNormalButton, "1", 0, &Button, 0, "Normal animation speed", 0))
731 m_AnimateSpeed = 1.0f;
733 TB_Top.VSplitLeft(30.0f, &Button, &TB_Top);
734 static int s_AnimSlowerButton = 0;
735 if(DoButton_Ex(&s_AnimSlowerButton, "A-", 0, &Button, 0, "Decrease animation speed", CUI::CORNER_R))
737 if(m_AnimateSpeed > 0.5f)
738 m_AnimateSpeed -= 0.5f;
741 if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP) && m_Dialog == DIALOG_NONE)
742 m_ZoomLevel -= 20;
744 if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN) && m_Dialog == DIALOG_NONE)
745 m_ZoomLevel += 20;
747 m_ZoomLevel = clamp(m_ZoomLevel, 50, 2000);
748 m_WorldZoom = m_ZoomLevel/100.0f;
750 TB_Top.VSplitLeft(10.0f, &Button, &TB_Top);
753 // brush manipulation
755 int Enabled = m_Brush.IsEmpty()?-1:0;
757 // flip buttons
758 TB_Top.VSplitLeft(30.0f, &Button, &TB_Top);
759 static int s_FlipXButton = 0;
760 if(DoButton_Ex(&s_FlipXButton, "X/X", Enabled, &Button, 0, "[N] Flip brush horizontal", CUI::CORNER_L) || Input()->KeyDown('n'))
762 for(int i = 0; i < m_Brush.m_lLayers.size(); i++)
763 m_Brush.m_lLayers[i]->BrushFlipX();
766 TB_Top.VSplitLeft(30.0f, &Button, &TB_Top);
767 static int s_FlipyButton = 0;
768 if(DoButton_Ex(&s_FlipyButton, "Y/Y", Enabled, &Button, 0, "[M] Flip brush vertical", CUI::CORNER_R) || Input()->KeyDown('m'))
770 for(int i = 0; i < m_Brush.m_lLayers.size(); i++)
771 m_Brush.m_lLayers[i]->BrushFlipY();
774 // rotate buttons
775 TB_Top.VSplitLeft(15.0f, &Button, &TB_Top);
777 TB_Top.VSplitLeft(30.0f, &Button, &TB_Top);
778 static int s_RotationAmount = 90;
779 bool TileLayer = false;
780 // check for tile layers in brush selection
781 for(int i = 0; i < m_Brush.m_lLayers.size(); i++)
782 if(m_Brush.m_lLayers[i]->m_Type == LAYERTYPE_TILES)
784 TileLayer = true;
785 s_RotationAmount = max(90, (s_RotationAmount/90)*90);
786 break;
788 s_RotationAmount = UiDoValueSelector(&s_RotationAmount, &Button, "", s_RotationAmount, TileLayer?90:1, 359, TileLayer?90:1, TileLayer?10.0f:2.0f, "Rotation of the brush in degrees. Use left mouse button to drag and change the value. Hold shift to be more precise.");
790 TB_Top.VSplitLeft(5.0f, &Button, &TB_Top);
791 TB_Top.VSplitLeft(30.0f, &Button, &TB_Top);
792 static int s_CcwButton = 0;
793 if(DoButton_Ex(&s_CcwButton, "CCW", Enabled, &Button, 0, "[R] Rotates the brush counter clockwise", CUI::CORNER_L) || Input()->KeyDown('r'))
795 for(int i = 0; i < m_Brush.m_lLayers.size(); i++)
796 m_Brush.m_lLayers[i]->BrushRotate(-s_RotationAmount/360.0f*pi*2);
799 TB_Top.VSplitLeft(30.0f, &Button, &TB_Top);
800 static int s_CwButton = 0;
801 if(DoButton_Ex(&s_CwButton, "CW", Enabled, &Button, 0, "[T] Rotates the brush clockwise", CUI::CORNER_R) || Input()->KeyDown('t'))
803 for(int i = 0; i < m_Brush.m_lLayers.size(); i++)
804 m_Brush.m_lLayers[i]->BrushRotate(s_RotationAmount/360.0f*pi*2);
808 // quad manipulation
810 // do add button
811 TB_Top.VSplitLeft(10.0f, &Button, &TB_Top);
812 TB_Top.VSplitLeft(60.0f, &Button, &TB_Top);
813 static int s_NewButton = 0;
815 CLayerQuads *pQLayer = (CLayerQuads *)GetSelectedLayerType(0, LAYERTYPE_QUADS);
816 //CLayerTiles *tlayer = (CLayerTiles *)get_selected_layer_type(0, LAYERTYPE_TILES);
817 if(DoButton_Editor(&s_NewButton, "Add Quad", pQLayer?0:-1, &Button, 0, "Adds a new quad"))
819 if(pQLayer)
821 float Mapping[4];
822 CLayerGroup *g = GetSelectedGroup();
823 g->Mapping(Mapping);
824 int AddX = f2fx(Mapping[0] + (Mapping[2]-Mapping[0])/2);
825 int AddY = f2fx(Mapping[1] + (Mapping[3]-Mapping[1])/2);
827 CQuad *q = pQLayer->NewQuad();
828 for(int i = 0; i < 5; i++)
830 q->m_aPoints[i].x += AddX;
831 q->m_aPoints[i].y += AddY;
837 // tile manipulation
839 TB_Bottom.VSplitLeft(40.0f, &Button, &TB_Bottom);
840 static int s_BorderBut = 0;
841 CLayerTiles *pT = (CLayerTiles *)GetSelectedLayerType(0, LAYERTYPE_TILES);
843 if(DoButton_Editor(&s_BorderBut, "Border", pT?0:-1, &Button, 0, "Adds border tiles"))
845 if(pT)
846 DoMapBorder();
850 TB_Bottom.VSplitLeft(5.0f, 0, &TB_Bottom);
852 // refocus button
853 TB_Bottom.VSplitLeft(50.0f, &Button, &TB_Bottom);
854 static int s_RefocusButton = 0;
855 if(DoButton_Editor(&s_RefocusButton, "Refocus", m_WorldOffsetX&&m_WorldOffsetY?0:-1, &Button, 0, "[HOME] Restore map focus") || Input()->KeyDown(KEY_HOME))
857 m_WorldOffsetX = 0;
858 m_WorldOffsetY = 0;
862 static void Rotate(CPoint *pCenter, CPoint *pPoint, float Rotation)
864 int x = pPoint->x - pCenter->x;
865 int y = pPoint->y - pCenter->y;
866 pPoint->x = (int)(x * cosf(Rotation) - y * sinf(Rotation) + pCenter->x);
867 pPoint->y = (int)(x * sinf(Rotation) + y * cosf(Rotation) + pCenter->y);
870 void CEditor::DoQuad(CQuad *q, int Index)
872 enum
874 OP_NONE=0,
875 OP_MOVE_ALL,
876 OP_MOVE_PIVOT,
877 OP_ROTATE,
878 OP_CONTEXT_MENU,
881 // some basic values
882 void *pID = &q->m_aPoints[4]; // use pivot addr as id
883 static CPoint s_RotatePoints[4];
884 static float s_LastWx;
885 static float s_LastWy;
886 static int s_Operation = OP_NONE;
887 static float s_RotateAngle = 0;
888 float wx = UI()->MouseWorldX();
889 float wy = UI()->MouseWorldY();
891 // get pivot
892 float CenterX = fx2f(q->m_aPoints[4].x);
893 float CenterY = fx2f(q->m_aPoints[4].y);
895 float dx = (CenterX - wx)/m_WorldZoom;
896 float dy = (CenterY - wy)/m_WorldZoom;
897 if(dx*dx+dy*dy < 50)
898 UI()->SetHotItem(pID);
900 // draw selection background
901 if(m_SelectedQuad == Index)
903 Graphics()->SetColor(0,0,0,1);
904 IGraphics::CQuadItem QuadItem(CenterX, CenterY, 7.0f, 7.0f);
905 Graphics()->QuadsDraw(&QuadItem, 1);
908 if(UI()->ActiveItem() == pID)
910 // check if we only should move pivot
911 if(s_Operation == OP_MOVE_PIVOT)
913 q->m_aPoints[4].x += f2fx(wx-s_LastWx);
914 q->m_aPoints[4].y += f2fx(wy-s_LastWy);
916 else if(s_Operation == OP_MOVE_ALL)
918 // move all points including pivot
919 for(int v = 0; v < 5; v++)
921 q->m_aPoints[v].x += f2fx(wx-s_LastWx);
922 q->m_aPoints[v].y += f2fx(wy-s_LastWy);
925 else if(s_Operation == OP_ROTATE)
927 for(int v = 0; v < 4; v++)
929 q->m_aPoints[v] = s_RotatePoints[v];
930 Rotate(&q->m_aPoints[4], &q->m_aPoints[v], s_RotateAngle);
934 s_RotateAngle += (m_MouseDeltaX) * 0.002f;
935 s_LastWx = wx;
936 s_LastWy = wy;
938 if(s_Operation == OP_CONTEXT_MENU)
940 if(!UI()->MouseButton(1))
942 static int s_QuadPopupID = 0;
943 UiInvokePopupMenu(&s_QuadPopupID, 0, UI()->MouseX(), UI()->MouseY(), 120, 180, PopupQuad);
944 m_LockMouse = false;
945 s_Operation = OP_NONE;
946 UI()->SetActiveItem(0);
949 else
951 if(!UI()->MouseButton(0))
953 m_LockMouse = false;
954 s_Operation = OP_NONE;
955 UI()->SetActiveItem(0);
959 Graphics()->SetColor(1,1,1,1);
961 else if(UI()->HotItem() == pID)
963 ms_pUiGotContext = pID;
965 Graphics()->SetColor(1,1,1,1);
966 m_pTooltip = "Left mouse button to move. Hold shift to move pivot. Hold ctrl to rotate.";
968 if(UI()->MouseButton(0))
970 if(Input()->KeyPressed(KEY_LSHIFT) || Input()->KeyPressed(KEY_RSHIFT))
971 s_Operation = OP_MOVE_PIVOT;
972 else if(Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL))
974 m_LockMouse = true;
975 s_Operation = OP_ROTATE;
976 s_RotateAngle = 0;
977 s_RotatePoints[0] = q->m_aPoints[0];
978 s_RotatePoints[1] = q->m_aPoints[1];
979 s_RotatePoints[2] = q->m_aPoints[2];
980 s_RotatePoints[3] = q->m_aPoints[3];
982 else
983 s_Operation = OP_MOVE_ALL;
985 UI()->SetActiveItem(pID);
986 m_SelectedQuad = Index;
987 s_LastWx = wx;
988 s_LastWy = wy;
991 if(UI()->MouseButton(1))
993 m_SelectedQuad = Index;
994 s_Operation = OP_CONTEXT_MENU;
995 UI()->SetActiveItem(pID);
998 else
999 Graphics()->SetColor(0,1,0,1);
1001 IGraphics::CQuadItem QuadItem(CenterX, CenterY, 5.0f*m_WorldZoom, 5.0f*m_WorldZoom);
1002 Graphics()->QuadsDraw(&QuadItem, 1);
1005 void CEditor::DoQuadPoint(CQuad *pQuad, int QuadIndex, int V)
1007 void *pID = &pQuad->m_aPoints[V];
1009 float wx = UI()->MouseWorldX();
1010 float wy = UI()->MouseWorldY();
1012 float px = fx2f(pQuad->m_aPoints[V].x);
1013 float py = fx2f(pQuad->m_aPoints[V].y);
1015 float dx = (px - wx)/m_WorldZoom;
1016 float dy = (py - wy)/m_WorldZoom;
1017 if(dx*dx+dy*dy < 50)
1018 UI()->SetHotItem(pID);
1020 // draw selection background
1021 if(m_SelectedQuad == QuadIndex && m_SelectedPoints&(1<<V))
1023 Graphics()->SetColor(0,0,0,1);
1024 IGraphics::CQuadItem QuadItem(px, py, 7.0f, 7.0f);
1025 Graphics()->QuadsDraw(&QuadItem, 1);
1028 enum
1030 OP_NONE=0,
1031 OP_MOVEPOINT,
1032 OP_MOVEUV,
1033 OP_CONTEXT_MENU
1036 static bool s_Moved;
1037 static int s_Operation = OP_NONE;
1039 if(UI()->ActiveItem() == pID)
1041 float dx = m_MouseDeltaWx;
1042 float dy = m_MouseDeltaWy;
1043 if(!s_Moved)
1045 if(dx*dx+dy*dy > 0.5f)
1046 s_Moved = true;
1049 if(s_Moved)
1051 if(s_Operation == OP_MOVEPOINT)
1053 for(int m = 0; m < 4; m++)
1054 if(m_SelectedPoints&(1<<m))
1056 pQuad->m_aPoints[m].x += f2fx(dx);
1057 pQuad->m_aPoints[m].y += f2fx(dy);
1060 else if(s_Operation == OP_MOVEUV)
1062 for(int m = 0; m < 4; m++)
1063 if(m_SelectedPoints&(1<<m))
1065 // 0,2;1,3 - line x
1066 // 0,1;2,3 - line y
1068 pQuad->m_aTexcoords[m].x += f2fx(dx*0.001f);
1069 pQuad->m_aTexcoords[(m+2)%4].x += f2fx(dx*0.001f);
1071 pQuad->m_aTexcoords[m].y += f2fx(dy*0.001f);
1072 pQuad->m_aTexcoords[m^1].y += f2fx(dy*0.001f);
1077 if(s_Operation == OP_CONTEXT_MENU)
1079 if(!UI()->MouseButton(1))
1081 static int s_PointPopupID = 0;
1082 UiInvokePopupMenu(&s_PointPopupID, 0, UI()->MouseX(), UI()->MouseY(), 120, 150, PopupPoint);
1083 UI()->SetActiveItem(0);
1086 else
1088 if(!UI()->MouseButton(0))
1090 if(!s_Moved)
1092 if(Input()->KeyPressed(KEY_LSHIFT) || Input()->KeyPressed(KEY_RSHIFT))
1093 m_SelectedPoints ^= 1<<V;
1094 else
1095 m_SelectedPoints = 1<<V;
1097 m_LockMouse = false;
1098 UI()->SetActiveItem(0);
1102 Graphics()->SetColor(1,1,1,1);
1104 else if(UI()->HotItem() == pID)
1106 ms_pUiGotContext = pID;
1108 Graphics()->SetColor(1,1,1,1);
1109 m_pTooltip = "Left mouse button to move. Hold shift to move the texture.";
1111 if(UI()->MouseButton(0))
1113 UI()->SetActiveItem(pID);
1114 s_Moved = false;
1115 if(Input()->KeyPressed(KEY_LSHIFT) || Input()->KeyPressed(KEY_RSHIFT))
1117 s_Operation = OP_MOVEUV;
1118 m_LockMouse = true;
1120 else
1121 s_Operation = OP_MOVEPOINT;
1123 if(!(m_SelectedPoints&(1<<V)))
1125 if(Input()->KeyPressed(KEY_LSHIFT) || Input()->KeyPressed(KEY_RSHIFT))
1126 m_SelectedPoints |= 1<<V;
1127 else
1128 m_SelectedPoints = 1<<V;
1129 s_Moved = true;
1132 m_SelectedQuad = QuadIndex;
1134 else if(UI()->MouseButton(1))
1136 s_Operation = OP_CONTEXT_MENU;
1137 m_SelectedQuad = QuadIndex;
1138 UI()->SetActiveItem(pID);
1139 if(!(m_SelectedPoints&(1<<V)))
1141 if(Input()->KeyPressed(KEY_LSHIFT) || Input()->KeyPressed(KEY_RSHIFT))
1142 m_SelectedPoints |= 1<<V;
1143 else
1144 m_SelectedPoints = 1<<V;
1145 s_Moved = true;
1149 else
1150 Graphics()->SetColor(1,0,0,1);
1152 IGraphics::CQuadItem QuadItem(px, py, 5.0f*m_WorldZoom, 5.0f*m_WorldZoom);
1153 Graphics()->QuadsDraw(&QuadItem, 1);
1156 void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar)
1158 // render all good stuff
1159 if(!m_ShowPicker)
1161 for(int g = 0; g < m_Map.m_lGroups.size(); g++)
1163 if(m_Map.m_lGroups[g]->m_Visible)
1164 m_Map.m_lGroups[g]->Render();
1165 //UI()->ClipEnable(&view);
1168 // render the game above everything else
1169 if(m_Map.m_pGameGroup->m_Visible && m_Map.m_pGameLayer->m_Visible)
1171 m_Map.m_pGameGroup->MapScreen();
1172 m_Map.m_pGameLayer->Render();
1175 CLayerTiles *pT = static_cast<CLayerTiles *>(GetSelectedLayerType(0, LAYERTYPE_TILES));
1176 if(m_ShowTileInfo && pT && pT->m_Visible && m_ZoomLevel <= 300)
1178 GetSelectedGroup()->MapScreen();
1179 pT->ShowInfo();
1183 static void *s_pEditorID = (void *)&s_pEditorID;
1184 int Inside = UI()->MouseInside(&View);
1186 // fetch mouse position
1187 float wx = UI()->MouseWorldX();
1188 float wy = UI()->MouseWorldY();
1189 float mx = UI()->MouseX();
1190 float my = UI()->MouseY();
1192 static float s_StartWx = 0;
1193 static float s_StartWy = 0;
1195 enum
1197 OP_NONE=0,
1198 OP_BRUSH_GRAB,
1199 OP_BRUSH_DRAW,
1200 OP_BRUSH_PAINT,
1201 OP_PAN_WORLD,
1202 OP_PAN_EDITOR,
1205 // remap the screen so it can display the whole tileset
1206 if(m_ShowPicker)
1208 CUIRect Screen = *UI()->Screen();
1209 float Size = 32.0*16.0f;
1210 float w = Size*(Screen.w/View.w);
1211 float h = Size*(Screen.h/View.h);
1212 float x = -(View.x/Screen.w)*w;
1213 float y = -(View.y/Screen.h)*h;
1214 wx = x+w*mx/Screen.w;
1215 wy = y+h*my/Screen.h;
1216 Graphics()->MapScreen(x, y, x+w, y+h);
1217 CLayerTiles *t = (CLayerTiles *)GetSelectedLayerType(0, LAYERTYPE_TILES);
1218 if(t)
1220 m_TilesetPicker.m_Image = t->m_Image;
1221 m_TilesetPicker.m_TexID = t->m_TexID;
1222 m_TilesetPicker.Render();
1223 if(m_ShowTileInfo)
1224 m_TilesetPicker.ShowInfo();
1228 static int s_Operation = OP_NONE;
1230 // draw layer borders
1231 CLayer *pEditLayers[16];
1232 int NumEditLayers = 0;
1233 NumEditLayers = 0;
1235 if(m_ShowPicker)
1237 pEditLayers[0] = &m_TilesetPicker;
1238 NumEditLayers++;
1240 else
1242 pEditLayers[0] = GetSelectedLayer(0);
1243 if(pEditLayers[0])
1244 NumEditLayers++;
1246 CLayerGroup *g = GetSelectedGroup();
1247 if(g)
1249 g->MapScreen();
1251 for(int i = 0; i < NumEditLayers; i++)
1253 if(pEditLayers[i]->m_Type != LAYERTYPE_TILES)
1254 continue;
1256 float w, h;
1257 pEditLayers[i]->GetSize(&w, &h);
1259 IGraphics::CLineItem Array[4] = {
1260 IGraphics::CLineItem(0, 0, w, 0),
1261 IGraphics::CLineItem(w, 0, w, h),
1262 IGraphics::CLineItem(w, h, 0, h),
1263 IGraphics::CLineItem(0, h, 0, 0)};
1264 Graphics()->TextureSet(-1);
1265 Graphics()->LinesBegin();
1266 Graphics()->LinesDraw(Array, 4);
1267 Graphics()->LinesEnd();
1272 if(Inside)
1274 UI()->SetHotItem(s_pEditorID);
1276 // do global operations like pan and zoom
1277 if(UI()->ActiveItem() == 0 && (UI()->MouseButton(0) || UI()->MouseButton(2)))
1279 s_StartWx = wx;
1280 s_StartWy = wy;
1282 if(Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL) || UI()->MouseButton(2))
1284 if(Input()->KeyPressed(KEY_LSHIFT))
1285 s_Operation = OP_PAN_EDITOR;
1286 else
1287 s_Operation = OP_PAN_WORLD;
1288 UI()->SetActiveItem(s_pEditorID);
1292 // brush editing
1293 if(UI()->HotItem() == s_pEditorID)
1295 if(m_Brush.IsEmpty())
1296 m_pTooltip = "Use left mouse button to drag and create a brush.";
1297 else
1298 m_pTooltip = "Use left mouse button to paint with the brush. Right button clears the brush.";
1300 if(UI()->ActiveItem() == s_pEditorID)
1302 CUIRect r;
1303 r.x = s_StartWx;
1304 r.y = s_StartWy;
1305 r.w = wx-s_StartWx;
1306 r.h = wy-s_StartWy;
1307 if(r.w < 0)
1309 r.x += r.w;
1310 r.w = -r.w;
1313 if(r.h < 0)
1315 r.y += r.h;
1316 r.h = -r.h;
1319 if(s_Operation == OP_BRUSH_DRAW)
1321 if(!m_Brush.IsEmpty())
1323 // draw with brush
1324 for(int k = 0; k < NumEditLayers; k++)
1326 if(pEditLayers[k]->m_Type == m_Brush.m_lLayers[0]->m_Type)
1327 pEditLayers[k]->BrushDraw(m_Brush.m_lLayers[0], wx, wy);
1331 else if(s_Operation == OP_BRUSH_GRAB)
1333 if(!UI()->MouseButton(0))
1335 // grab brush
1336 char aBuf[256];
1337 str_format(aBuf, sizeof(aBuf),"grabbing %f %f %f %f", r.x, r.y, r.w, r.h);
1338 Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "editor", aBuf);
1340 // TODO: do all layers
1341 int Grabs = 0;
1342 for(int k = 0; k < NumEditLayers; k++)
1343 Grabs += pEditLayers[k]->BrushGrab(&m_Brush, r);
1344 if(Grabs == 0)
1345 m_Brush.Clear();
1347 else
1349 //editor.map.groups[selected_group]->mapscreen();
1350 for(int k = 0; k < NumEditLayers; k++)
1351 pEditLayers[k]->BrushSelecting(r);
1352 Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h);
1355 else if(s_Operation == OP_BRUSH_PAINT)
1357 if(!UI()->MouseButton(0))
1359 for(int k = 0; k < NumEditLayers; k++)
1360 pEditLayers[k]->FillSelection(m_Brush.IsEmpty(), m_Brush.m_lLayers[0], r);
1362 else
1364 //editor.map.groups[selected_group]->mapscreen();
1365 for(int k = 0; k < NumEditLayers; k++)
1366 pEditLayers[k]->BrushSelecting(r);
1367 Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h);
1371 else
1373 if(UI()->MouseButton(1))
1374 m_Brush.Clear();
1376 if(UI()->MouseButton(0) && s_Operation == OP_NONE)
1378 UI()->SetActiveItem(s_pEditorID);
1380 if(m_Brush.IsEmpty())
1381 s_Operation = OP_BRUSH_GRAB;
1382 else
1384 s_Operation = OP_BRUSH_DRAW;
1385 for(int k = 0; k < NumEditLayers; k++)
1387 if(pEditLayers[k]->m_Type == m_Brush.m_lLayers[0]->m_Type)
1388 pEditLayers[k]->BrushPlace(m_Brush.m_lLayers[0], wx, wy);
1393 CLayerTiles *pLayer = (CLayerTiles*)GetSelectedLayerType(0, LAYERTYPE_TILES);
1394 if((Input()->KeyPressed(KEY_LSHIFT) || Input()->KeyPressed(KEY_RSHIFT)) && pLayer)
1395 s_Operation = OP_BRUSH_PAINT;
1398 if(!m_Brush.IsEmpty())
1400 m_Brush.m_OffsetX = -(int)wx;
1401 m_Brush.m_OffsetY = -(int)wy;
1402 for(int i = 0; i < m_Brush.m_lLayers.size(); i++)
1404 if(m_Brush.m_lLayers[i]->m_Type == LAYERTYPE_TILES)
1406 m_Brush.m_OffsetX = -(int)(wx/32.0f)*32;
1407 m_Brush.m_OffsetY = -(int)(wy/32.0f)*32;
1408 break;
1412 CLayerGroup *g = GetSelectedGroup();
1413 if(g)
1415 m_Brush.m_OffsetX += g->m_OffsetX;
1416 m_Brush.m_OffsetY += g->m_OffsetY;
1417 m_Brush.m_ParallaxX = g->m_ParallaxX;
1418 m_Brush.m_ParallaxY = g->m_ParallaxY;
1419 m_Brush.Render();
1420 float w, h;
1421 m_Brush.GetSize(&w, &h);
1423 IGraphics::CLineItem Array[4] = {
1424 IGraphics::CLineItem(0, 0, w, 0),
1425 IGraphics::CLineItem(w, 0, w, h),
1426 IGraphics::CLineItem(w, h, 0, h),
1427 IGraphics::CLineItem(0, h, 0, 0)};
1428 Graphics()->TextureSet(-1);
1429 Graphics()->LinesBegin();
1430 Graphics()->LinesDraw(Array, 4);
1431 Graphics()->LinesEnd();
1437 // quad editing
1439 if(!m_ShowPicker && m_Brush.IsEmpty())
1441 // fetch layers
1442 CLayerGroup *g = GetSelectedGroup();
1443 if(g)
1444 g->MapScreen();
1446 for(int k = 0; k < NumEditLayers; k++)
1448 if(pEditLayers[k]->m_Type == LAYERTYPE_QUADS)
1450 CLayerQuads *pLayer = (CLayerQuads *)pEditLayers[k];
1452 Graphics()->TextureSet(-1);
1453 Graphics()->QuadsBegin();
1454 for(int i = 0; i < pLayer->m_lQuads.size(); i++)
1456 for(int v = 0; v < 4; v++)
1457 DoQuadPoint(&pLayer->m_lQuads[i], i, v);
1459 DoQuad(&pLayer->m_lQuads[i], i);
1461 Graphics()->QuadsEnd();
1465 Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h);
1468 // do panning
1469 if(UI()->ActiveItem() == s_pEditorID)
1471 if(s_Operation == OP_PAN_WORLD)
1473 m_WorldOffsetX -= m_MouseDeltaX*m_WorldZoom;
1474 m_WorldOffsetY -= m_MouseDeltaY*m_WorldZoom;
1476 else if(s_Operation == OP_PAN_EDITOR)
1478 m_EditorOffsetX -= m_MouseDeltaX*m_WorldZoom;
1479 m_EditorOffsetY -= m_MouseDeltaY*m_WorldZoom;
1482 // release mouse
1483 if(!UI()->MouseButton(0))
1485 s_Operation = OP_NONE;
1486 UI()->SetActiveItem(0);
1491 else if(UI()->ActiveItem() == s_pEditorID)
1493 // release mouse
1494 if(!UI()->MouseButton(0))
1496 s_Operation = OP_NONE;
1497 UI()->SetActiveItem(0);
1501 if(GetSelectedGroup() && GetSelectedGroup()->m_UseClipping)
1503 CLayerGroup *g = m_Map.m_pGameGroup;
1504 g->MapScreen();
1506 Graphics()->TextureSet(-1);
1507 Graphics()->LinesBegin();
1509 CUIRect r;
1510 r.x = GetSelectedGroup()->m_ClipX;
1511 r.y = GetSelectedGroup()->m_ClipY;
1512 r.w = GetSelectedGroup()->m_ClipW;
1513 r.h = GetSelectedGroup()->m_ClipH;
1515 IGraphics::CLineItem Array[4] = {
1516 IGraphics::CLineItem(r.x, r.y, r.x+r.w, r.y),
1517 IGraphics::CLineItem(r.x+r.w, r.y, r.x+r.w, r.y+r.h),
1518 IGraphics::CLineItem(r.x+r.w, r.y+r.h, r.x, r.y+r.h),
1519 IGraphics::CLineItem(r.x, r.y+r.h, r.x, r.y)};
1520 Graphics()->SetColor(1,0,0,1);
1521 Graphics()->LinesDraw(Array, 4);
1523 Graphics()->LinesEnd();
1526 // render screen sizes
1527 if(m_ProofBorders)
1529 CLayerGroup *g = m_Map.m_pGameGroup;
1530 g->MapScreen();
1532 Graphics()->TextureSet(-1);
1533 Graphics()->LinesBegin();
1535 float aLastPoints[4];
1536 float Start = 1.0f; //9.0f/16.0f;
1537 float End = 16.0f/9.0f;
1538 const int NumSteps = 20;
1539 for(int i = 0; i <= NumSteps; i++)
1541 float aPoints[4];
1542 float Aspect = Start + (End-Start)*(i/(float)NumSteps);
1544 RenderTools()->MapscreenToWorld(
1545 m_WorldOffsetX, m_WorldOffsetY,
1546 1.0f, 1.0f, 0.0f, 0.0f, Aspect, 1.0f, aPoints);
1548 if(i == 0)
1550 IGraphics::CLineItem Array[2] = {
1551 IGraphics::CLineItem(aPoints[0], aPoints[1], aPoints[2], aPoints[1]),
1552 IGraphics::CLineItem(aPoints[0], aPoints[3], aPoints[2], aPoints[3])};
1553 Graphics()->LinesDraw(Array, 2);
1556 if(i != 0)
1558 IGraphics::CLineItem Array[4] = {
1559 IGraphics::CLineItem(aPoints[0], aPoints[1], aLastPoints[0], aLastPoints[1]),
1560 IGraphics::CLineItem(aPoints[2], aPoints[1], aLastPoints[2], aLastPoints[1]),
1561 IGraphics::CLineItem(aPoints[0], aPoints[3], aLastPoints[0], aLastPoints[3]),
1562 IGraphics::CLineItem(aPoints[2], aPoints[3], aLastPoints[2], aLastPoints[3])};
1563 Graphics()->LinesDraw(Array, 4);
1566 if(i == NumSteps)
1568 IGraphics::CLineItem Array[2] = {
1569 IGraphics::CLineItem(aPoints[0], aPoints[1], aPoints[0], aPoints[3]),
1570 IGraphics::CLineItem(aPoints[2], aPoints[1], aPoints[2], aPoints[3])};
1571 Graphics()->LinesDraw(Array, 2);
1574 mem_copy(aLastPoints, aPoints, sizeof(aPoints));
1577 if(1)
1579 Graphics()->SetColor(1,0,0,1);
1580 for(int i = 0; i < 2; i++)
1582 float aPoints[4];
1583 float aAspects[] = {4.0f/3.0f, 16.0f/10.0f, 5.0f/4.0f, 16.0f/9.0f};
1584 float Aspect = aAspects[i];
1586 RenderTools()->MapscreenToWorld(
1587 m_WorldOffsetX, m_WorldOffsetY,
1588 1.0f, 1.0f, 0.0f, 0.0f, Aspect, 1.0f, aPoints);
1590 CUIRect r;
1591 r.x = aPoints[0];
1592 r.y = aPoints[1];
1593 r.w = aPoints[2]-aPoints[0];
1594 r.h = aPoints[3]-aPoints[1];
1596 IGraphics::CLineItem Array[4] = {
1597 IGraphics::CLineItem(r.x, r.y, r.x+r.w, r.y),
1598 IGraphics::CLineItem(r.x+r.w, r.y, r.x+r.w, r.y+r.h),
1599 IGraphics::CLineItem(r.x+r.w, r.y+r.h, r.x, r.y+r.h),
1600 IGraphics::CLineItem(r.x, r.y+r.h, r.x, r.y)};
1601 Graphics()->LinesDraw(Array, 4);
1602 Graphics()->SetColor(0,1,0,1);
1606 Graphics()->LinesEnd();
1609 Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h);
1610 //UI()->ClipDisable();
1614 int CEditor::DoProperties(CUIRect *pToolBox, CProperty *pProps, int *pIDs, int *pNewVal)
1616 int Change = -1;
1618 for(int i = 0; pProps[i].m_pName; i++)
1620 CUIRect Slot;
1621 pToolBox->HSplitTop(13.0f, &Slot, pToolBox);
1622 CUIRect Label, Shifter;
1623 Slot.VSplitMid(&Label, &Shifter);
1624 Shifter.HMargin(1.0f, &Shifter);
1625 UI()->DoLabel(&Label, pProps[i].m_pName, 10.0f, -1, -1);
1627 if(pProps[i].m_Type == PROPTYPE_INT_STEP)
1629 CUIRect Inc, Dec;
1630 char aBuf[64];
1632 Shifter.VSplitRight(10.0f, &Shifter, &Inc);
1633 Shifter.VSplitLeft(10.0f, &Dec, &Shifter);
1634 str_format(aBuf, sizeof(aBuf),"%d", pProps[i].m_Value);
1635 RenderTools()->DrawUIRect(&Shifter, vec4(1,1,1,0.5f), 0, 0.0f);
1636 UI()->DoLabel(&Shifter, aBuf, 10.0f, 0, -1);
1638 if(DoButton_ButtonDec(&pIDs[i], 0, 0, &Dec, 0, "Decrease"))
1640 *pNewVal = pProps[i].m_Value-1;
1641 Change = i;
1643 if(DoButton_ButtonInc(((char *)&pIDs[i])+1, 0, 0, &Inc, 0, "Increase"))
1645 *pNewVal = pProps[i].m_Value+1;
1646 Change = i;
1649 else if(pProps[i].m_Type == PROPTYPE_BOOL)
1651 CUIRect No, Yes;
1652 Shifter.VSplitMid(&No, &Yes);
1653 if(DoButton_ButtonDec(&pIDs[i], "No", !pProps[i].m_Value, &No, 0, ""))
1655 *pNewVal = 0;
1656 Change = i;
1658 if(DoButton_ButtonInc(((char *)&pIDs[i])+1, "Yes", pProps[i].m_Value, &Yes, 0, ""))
1660 *pNewVal = 1;
1661 Change = i;
1664 else if(pProps[i].m_Type == PROPTYPE_INT_SCROLL)
1666 int NewValue = UiDoValueSelector(&pIDs[i], &Shifter, "", pProps[i].m_Value, pProps[i].m_Min, pProps[i].m_Max, 1, 1.0f, "Use left mouse button to drag and change the value. Hold shift to be more precise.");
1667 if(NewValue != pProps[i].m_Value)
1669 *pNewVal = NewValue;
1670 Change = i;
1673 else if(pProps[i].m_Type == PROPTYPE_COLOR)
1675 static const char *s_paTexts[4] = {"R", "G", "B", "A"};
1676 static int s_aShift[] = {24, 16, 8, 0};
1677 int NewColor = 0;
1679 for(int c = 0; c < 4; c++)
1681 int v = (pProps[i].m_Value >> s_aShift[c])&0xff;
1682 NewColor |= UiDoValueSelector(((char *)&pIDs[i])+c, &Shifter, s_paTexts[c], v, 0, 255, 1, 1.0f, "Use left mouse button to drag and change the color value. Hold shift to be more precise.")<<s_aShift[c];
1684 if(c != 3)
1686 pToolBox->HSplitTop(13.0f, &Slot, pToolBox);
1687 Slot.VSplitMid(0, &Shifter);
1688 Shifter.HMargin(1.0f, &Shifter);
1692 if(NewColor != pProps[i].m_Value)
1694 *pNewVal = NewColor;
1695 Change = i;
1698 else if(pProps[i].m_Type == PROPTYPE_IMAGE)
1700 char aBuf[64];
1701 if(pProps[i].m_Value < 0)
1702 str_copy(aBuf, "None", sizeof(aBuf));
1703 else
1704 str_format(aBuf, sizeof(aBuf),"%s", m_Map.m_lImages[pProps[i].m_Value]->m_aName);
1706 if(DoButton_Editor(&pIDs[i], aBuf, 0, &Shifter, 0, 0))
1707 PopupSelectImageInvoke(pProps[i].m_Value, UI()->MouseX(), UI()->MouseY());
1709 int r = PopupSelectImageResult();
1710 if(r >= -1)
1712 *pNewVal = r;
1713 Change = i;
1716 else if(pProps[i].m_Type == PROPTYPE_SHIFT)
1718 CUIRect Left, Right, Up, Down;
1719 Shifter.VSplitMid(&Left, &Up);
1720 Left.VSplitRight(1.0f, &Left, 0);
1721 Up.VSplitLeft(1.0f, 0, &Up);
1722 Left.VSplitLeft(10.0f, &Left, &Shifter);
1723 Shifter.VSplitRight(10.0f, &Shifter, &Right);
1724 RenderTools()->DrawUIRect(&Shifter, vec4(1,1,1,0.5f), 0, 0.0f);
1725 UI()->DoLabel(&Shifter, "X", 10.0f, 0, -1);
1726 Up.VSplitLeft(10.0f, &Up, &Shifter);
1727 Shifter.VSplitRight(10.0f, &Shifter, &Down);
1728 RenderTools()->DrawUIRect(&Shifter, vec4(1,1,1,0.5f), 0, 0.0f);
1729 UI()->DoLabel(&Shifter, "Y", 10.0f, 0, -1);
1730 if(DoButton_ButtonDec(&pIDs[i], "-", 0, &Left, 0, "Left"))
1732 *pNewVal = 1;
1733 Change = i;
1735 if(DoButton_ButtonInc(((char *)&pIDs[i])+3, "+", 0, &Right, 0, "Right"))
1737 *pNewVal = 2;
1738 Change = i;
1740 if(DoButton_ButtonDec(((char *)&pIDs[i])+1, "-", 0, &Up, 0, "Up"))
1742 *pNewVal = 4;
1743 Change = i;
1745 if(DoButton_ButtonInc(((char *)&pIDs[i])+2, "+", 0, &Down, 0, "Down"))
1747 *pNewVal = 8;
1748 Change = i;
1753 return Change;
1756 void CEditor::RenderLayers(CUIRect ToolBox, CUIRect ToolBar, CUIRect View)
1758 CUIRect LayersBox = ToolBox;
1760 if(!m_GuiActive)
1761 return;
1763 CUIRect Slot, Button;
1764 char aBuf[64];
1766 float LayersHeight = 12.0f; // Height of AddGroup button
1767 static int s_ScrollBar = 0;
1768 static float s_ScrollValue = 0;
1770 for(int g = 0; g < m_Map.m_lGroups.size(); g++)
1771 // Each group is 19.0f
1772 // Each layer is 14.0f
1773 LayersHeight += 19.0f + m_Map.m_lGroups[g]->m_lLayers.size() * 14.0f;
1775 float ScrollDifference = LayersHeight - LayersBox.h;
1777 if(LayersHeight > LayersBox.h) // Do we even need a scrollbar?
1779 CUIRect Scroll;
1780 LayersBox.VSplitRight(15.0f, &LayersBox, &Scroll);
1781 LayersBox.VSplitRight(3.0f, &LayersBox, 0); // extra spacing
1782 Scroll.HMargin(5.0f, &Scroll);
1783 s_ScrollValue = UiDoScrollbarV(&s_ScrollBar, &Scroll, s_ScrollValue);
1786 float LayerStartAt = ScrollDifference * s_ScrollValue;
1787 if(LayerStartAt < 0.0f)
1788 LayerStartAt = 0.0f;
1790 float LayerStopAt = LayersHeight - ScrollDifference * (1 - s_ScrollValue);
1791 float LayerCur = 0;
1793 // render layers
1795 for(int g = 0; g < m_Map.m_lGroups.size(); g++)
1797 if(LayerCur > LayerStopAt)
1798 break;
1799 else if(LayerCur + m_Map.m_lGroups[g]->m_lLayers.size() * 14.0f + 19.0f < LayerStartAt)
1801 LayerCur += m_Map.m_lGroups[g]->m_lLayers.size() * 14.0f + 19.0f;
1802 continue;
1805 CUIRect VisibleToggle, SaveCheck;
1806 if(LayerCur >= LayerStartAt)
1808 LayersBox.HSplitTop(12.0f, &Slot, &LayersBox);
1809 Slot.VSplitLeft(12, &VisibleToggle, &Slot);
1810 if(DoButton_Ex(&m_Map.m_lGroups[g]->m_Visible, m_Map.m_lGroups[g]->m_Visible?"V":"H", 0, &VisibleToggle, 0, "Toggle group visibility", CUI::CORNER_L))
1811 m_Map.m_lGroups[g]->m_Visible = !m_Map.m_lGroups[g]->m_Visible;
1813 Slot.VSplitRight(12.0f, &Slot, &SaveCheck);
1814 if(DoButton_Ex(&m_Map.m_lGroups[g]->m_SaveToMap, "S", m_Map.m_lGroups[g]->m_SaveToMap, &SaveCheck, 0, "Enable/disable group for saving", CUI::CORNER_R))
1815 if(!m_Map.m_lGroups[g]->m_GameGroup)
1816 m_Map.m_lGroups[g]->m_SaveToMap = !m_Map.m_lGroups[g]->m_SaveToMap;
1818 str_format(aBuf, sizeof(aBuf),"#%d %s", g, m_Map.m_lGroups[g]->m_pName);
1819 if(int Result = DoButton_Ex(&m_Map.m_lGroups[g], aBuf, g==m_SelectedGroup, &Slot,
1820 BUTTON_CONTEXT, "Select group. Right click for properties.", 0))
1822 m_SelectedGroup = g;
1823 m_SelectedLayer = 0;
1825 static int s_GroupPopupId = 0;
1826 if(Result == 2)
1827 UiInvokePopupMenu(&s_GroupPopupId, 0, UI()->MouseX(), UI()->MouseY(), 120, 200, PopupGroup);
1829 LayersBox.HSplitTop(2.0f, &Slot, &LayersBox);
1831 LayerCur += 14.0f;
1833 for(int i = 0; i < m_Map.m_lGroups[g]->m_lLayers.size(); i++)
1835 if(LayerCur > LayerStopAt)
1836 break;
1837 else if(LayerCur < LayerStartAt)
1839 LayerCur += 14.0f;
1840 continue;
1843 //visible
1844 LayersBox.HSplitTop(12.0f, &Slot, &LayersBox);
1845 Slot.VSplitLeft(12.0f, 0, &Button);
1846 Button.VSplitLeft(15, &VisibleToggle, &Button);
1848 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, "Toggle layer visibility", CUI::CORNER_L))
1849 m_Map.m_lGroups[g]->m_lLayers[i]->m_Visible = !m_Map.m_lGroups[g]->m_lLayers[i]->m_Visible;
1851 Button.VSplitRight(12.0f, &Button, &SaveCheck);
1852 if(DoButton_Ex(&m_Map.m_lGroups[g]->m_lLayers[i]->m_SaveToMap, "S", m_Map.m_lGroups[g]->m_lLayers[i]->m_SaveToMap, &SaveCheck, 0, "Enable/disable layer for saving", CUI::CORNER_R))
1853 if(m_Map.m_lGroups[g]->m_lLayers[i] != m_Map.m_pGameLayer)
1854 m_Map.m_lGroups[g]->m_lLayers[i]->m_SaveToMap = !m_Map.m_lGroups[g]->m_lLayers[i]->m_SaveToMap;
1856 str_format(aBuf, sizeof(aBuf),"#%d %s ", i, m_Map.m_lGroups[g]->m_lLayers[i]->m_pTypeName);
1857 if(int Result = DoButton_Ex(m_Map.m_lGroups[g]->m_lLayers[i], aBuf, g==m_SelectedGroup&&i==m_SelectedLayer, &Button,
1858 BUTTON_CONTEXT, "Select layer. Right click for properties.", 0))
1860 m_SelectedLayer = i;
1861 m_SelectedGroup = g;
1862 static int s_LayerPopupID = 0;
1863 if(Result == 2)
1864 UiInvokePopupMenu(&s_LayerPopupID, 0, UI()->MouseX(), UI()->MouseY(), 120, 220, PopupLayer);
1867 LayerCur += 14.0f;
1868 LayersBox.HSplitTop(2.0f, &Slot, &LayersBox);
1870 if(LayerCur > LayerStartAt && LayerCur < LayerStopAt)
1871 LayersBox.HSplitTop(5.0f, &Slot, &LayersBox);
1872 LayerCur += 5.0f;
1876 if(LayerCur <= LayerStopAt)
1878 LayersBox.HSplitTop(12.0f, &Slot, &LayersBox);
1880 static int s_NewGroupButton = 0;
1881 if(DoButton_Editor(&s_NewGroupButton, "Add group", 0, &Slot, 0, "Adds a new group"))
1883 m_Map.NewGroup();
1884 m_SelectedGroup = m_Map.m_lGroups.size()-1;
1889 void CEditor::ReplaceImage(const char *pFileName, int StorageType, void *pUser)
1891 CEditor *pEditor = (CEditor *)pUser;
1892 CEditorImage ImgInfo(pEditor);
1893 if(!pEditor->Graphics()->LoadPNG(&ImgInfo, pFileName, StorageType))
1894 return;
1896 CEditorImage *pImg = pEditor->m_Map.m_lImages[pEditor->m_SelectedImage];
1897 int External = pImg->m_External;
1898 pEditor->Graphics()->UnloadTexture(pImg->m_TexID);
1899 *pImg = ImgInfo;
1900 pImg->m_External = External;
1901 pEditor->ExtractName(pFileName, pImg->m_aName, sizeof(pImg->m_aName));
1902 pImg->m_TexID = pEditor->Graphics()->LoadTextureRaw(ImgInfo.m_Width, ImgInfo.m_Height, ImgInfo.m_Format, ImgInfo.m_pData, CImageInfo::FORMAT_AUTO, 0);
1903 pEditor->SortImages();
1904 for(int i = 0; i < pEditor->m_Map.m_lImages.size(); ++i)
1906 if(!str_comp(pEditor->m_Map.m_lImages[i]->m_aName, pImg->m_aName))
1907 pEditor->m_SelectedImage = i;
1909 pEditor->m_Dialog = DIALOG_NONE;
1912 void CEditor::AddImage(const char *pFileName, int StorageType, void *pUser)
1914 CEditor *pEditor = (CEditor *)pUser;
1915 CEditorImage ImgInfo(pEditor);
1916 if(!pEditor->Graphics()->LoadPNG(&ImgInfo, pFileName, StorageType))
1917 return;
1919 // check if we have that image already
1920 char aBuf[128];
1921 ExtractName(pFileName, aBuf, sizeof(aBuf));
1922 for(int i = 0; i < pEditor->m_Map.m_lImages.size(); ++i)
1924 if(!str_comp(pEditor->m_Map.m_lImages[i]->m_aName, aBuf))
1925 return;
1928 CEditorImage *pImg = new CEditorImage(pEditor);
1929 *pImg = ImgInfo;
1930 pImg->m_TexID = pEditor->Graphics()->LoadTextureRaw(ImgInfo.m_Width, ImgInfo.m_Height, ImgInfo.m_Format, ImgInfo.m_pData, CImageInfo::FORMAT_AUTO, 0);
1931 pImg->m_External = 1; // external by default
1932 str_copy(pImg->m_aName, aBuf, sizeof(pImg->m_aName));
1933 pEditor->m_Map.m_lImages.add(pImg);
1934 pEditor->SortImages();
1935 if(pEditor->m_SelectedImage > -1 && pEditor->m_SelectedImage < pEditor->m_Map.m_lImages.size())
1937 for(int i = 0; i <= pEditor->m_SelectedImage; ++i)
1938 if(!str_comp(pEditor->m_Map.m_lImages[i]->m_aName, aBuf))
1940 pEditor->m_SelectedImage++;
1941 break;
1944 pEditor->m_Dialog = DIALOG_NONE;
1948 static int gs_ModifyIndexDeletedIndex;
1949 static void ModifyIndexDeleted(int *pIndex)
1951 if(*pIndex == gs_ModifyIndexDeletedIndex)
1952 *pIndex = -1;
1953 else if(*pIndex > gs_ModifyIndexDeletedIndex)
1954 *pIndex = *pIndex - 1;
1957 int CEditor::PopupImage(CEditor *pEditor, CUIRect View)
1959 static int s_ReplaceButton = 0;
1960 static int s_RemoveButton = 0;
1962 CUIRect Slot;
1963 View.HSplitTop(2.0f, &Slot, &View);
1964 View.HSplitTop(12.0f, &Slot, &View);
1965 CEditorImage *pImg = pEditor->m_Map.m_lImages[pEditor->m_SelectedImage];
1967 static int s_ExternalButton = 0;
1968 if(pImg->m_External)
1970 if(pEditor->DoButton_MenuItem(&s_ExternalButton, "Embed", 0, &Slot, 0, "Embeds the image into the map file."))
1972 pImg->m_External = 0;
1973 return 1;
1976 else
1978 if(pEditor->DoButton_MenuItem(&s_ExternalButton, "Make external", 0, &Slot, 0, "Removes the image from the map file."))
1980 pImg->m_External = 1;
1981 return 1;
1985 View.HSplitTop(10.0f, &Slot, &View);
1986 View.HSplitTop(12.0f, &Slot, &View);
1987 if(pEditor->DoButton_MenuItem(&s_ReplaceButton, "Replace", 0, &Slot, 0, "Replaces the image with a new one"))
1989 pEditor->InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_IMG, "Replace Image", "Replace", "mapres", "", ReplaceImage, pEditor);
1990 return 1;
1993 View.HSplitTop(10.0f, &Slot, &View);
1994 View.HSplitTop(12.0f, &Slot, &View);
1995 if(pEditor->DoButton_MenuItem(&s_RemoveButton, "Remove", 0, &Slot, 0, "Removes the image from the map"))
1997 delete pImg;
1998 pEditor->m_Map.m_lImages.remove_index(pEditor->m_SelectedImage);
1999 gs_ModifyIndexDeletedIndex = pEditor->m_SelectedImage;
2000 pEditor->m_Map.ModifyImageIndex(ModifyIndexDeleted);
2001 return 1;
2004 return 0;
2007 static int CompareImageName(const void *pObject1, const void *pObject2)
2009 CEditorImage *pImage1 = *(CEditorImage**)pObject1;
2010 CEditorImage *pImage2 = *(CEditorImage**)pObject2;
2012 return str_comp(pImage1->m_aName, pImage2->m_aName);
2015 static int *gs_pSortedIndex = 0;
2016 static void ModifySortedIndex(int *pIndex)
2018 if(*pIndex > -1)
2019 *pIndex = gs_pSortedIndex[*pIndex];
2022 void CEditor::SortImages()
2024 bool Sorted = true;
2025 for(int i = 1; i < m_Map.m_lImages.size(); i++)
2026 if( str_comp(m_Map.m_lImages[i]->m_aName, m_Map.m_lImages[i-1]->m_aName) < 0 )
2028 Sorted = false;
2029 break;
2032 if(!Sorted)
2034 array<CEditorImage*> lTemp = array<CEditorImage*>(m_Map.m_lImages);
2035 gs_pSortedIndex = new int[lTemp.size()];
2037 qsort(m_Map.m_lImages.base_ptr(), m_Map.m_lImages.size(), sizeof(CEditorImage*), CompareImageName);
2039 for(int OldIndex = 0; OldIndex < lTemp.size(); OldIndex++)
2040 for(int NewIndex = 0; NewIndex < m_Map.m_lImages.size(); NewIndex++)
2041 if(lTemp[OldIndex] == m_Map.m_lImages[NewIndex])
2042 gs_pSortedIndex[OldIndex] = NewIndex;
2044 m_Map.ModifyImageIndex(ModifySortedIndex);
2046 delete [] gs_pSortedIndex;
2047 gs_pSortedIndex = 0;
2052 void CEditor::RenderImages(CUIRect ToolBox, CUIRect ToolBar, CUIRect View)
2054 static int s_ScrollBar = 0;
2055 static float s_ScrollValue = 0;
2056 float ImagesHeight = 30.0f + 14.0f * m_Map.m_lImages.size() + 27.0f;
2057 float ScrollDifference = ImagesHeight - ToolBox.h;
2059 if(ImagesHeight > ToolBox.h) // Do we even need a scrollbar?
2061 CUIRect Scroll;
2062 ToolBox.VSplitRight(15.0f, &ToolBox, &Scroll);
2063 ToolBox.VSplitRight(3.0f, &ToolBox, 0); // extra spacing
2064 Scroll.HMargin(5.0f, &Scroll);
2065 s_ScrollValue = UiDoScrollbarV(&s_ScrollBar, &Scroll, s_ScrollValue);
2068 float ImageStartAt = ScrollDifference * s_ScrollValue;
2069 if(ImageStartAt < 0.0f)
2070 ImageStartAt = 0.0f;
2072 float ImageStopAt = ImagesHeight - ScrollDifference * (1 - s_ScrollValue);
2073 float ImageCur = 0.0f;
2075 for(int e = 0; e < 2; e++) // two passes, first embedded, then external
2077 CUIRect Slot;
2079 if(ImageCur > ImageStopAt)
2080 break;
2081 else if(ImageCur >= ImageStartAt)
2084 ToolBox.HSplitTop(15.0f, &Slot, &ToolBox);
2085 if(e == 0)
2086 UI()->DoLabel(&Slot, "Embedded", 12.0f, 0);
2087 else
2088 UI()->DoLabel(&Slot, "External", 12.0f, 0);
2090 ImageCur += 15.0f;
2092 for(int i = 0; i < m_Map.m_lImages.size(); i++)
2094 if((e && !m_Map.m_lImages[i]->m_External) ||
2095 (!e && m_Map.m_lImages[i]->m_External))
2097 continue;
2100 if(ImageCur > ImageStopAt)
2101 break;
2102 else if(ImageCur < ImageStartAt)
2104 ImageCur += 14.0f;
2105 continue;
2107 ImageCur += 14.0f;
2109 char aBuf[128];
2110 str_copy(aBuf, m_Map.m_lImages[i]->m_aName, sizeof(aBuf));
2111 ToolBox.HSplitTop(12.0f, &Slot, &ToolBox);
2113 if(int Result = DoButton_Editor(&m_Map.m_lImages[i], aBuf, m_SelectedImage == i, &Slot,
2114 BUTTON_CONTEXT, "Select image"))
2116 m_SelectedImage = i;
2118 static int s_PopupImageID = 0;
2119 if(Result == 2)
2120 UiInvokePopupMenu(&s_PopupImageID, 0, UI()->MouseX(), UI()->MouseY(), 120, 80, PopupImage);
2123 ToolBox.HSplitTop(2.0f, 0, &ToolBox);
2125 // render image
2126 if(m_SelectedImage == i)
2128 CUIRect r;
2129 View.Margin(10.0f, &r);
2130 if(r.h < r.w)
2131 r.w = r.h;
2132 else
2133 r.h = r.w;
2134 Graphics()->TextureSet(m_Map.m_lImages[i]->m_TexID);
2135 Graphics()->BlendNormal();
2136 Graphics()->QuadsBegin();
2137 IGraphics::CQuadItem QuadItem(r.x, r.y, r.w, r.h);
2138 Graphics()->QuadsDrawTL(&QuadItem, 1);
2139 Graphics()->QuadsEnd();
2145 if(ImageCur + 27.0f > ImageStopAt)
2146 return;
2148 CUIRect Slot;
2149 ToolBox.HSplitTop(5.0f, &Slot, &ToolBox);
2151 // new image
2152 static int s_NewImageButton = 0;
2153 ToolBox.HSplitTop(10.0f, &Slot, &ToolBox);
2154 ToolBox.HSplitTop(12.0f, &Slot, &ToolBox);
2155 if(DoButton_Editor(&s_NewImageButton, "Add", 0, &Slot, 0, "Load a new image to use in the map"))
2156 InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_IMG, "Add Image", "Add", "mapres", "", AddImage, this);
2160 static int EditorListdirCallback(const char *pName, int IsDir, int StorageType, void *pUser)
2162 CEditor *pEditor = (CEditor*)pUser;
2163 int Length = str_length(pName);
2164 if((pName[0] == '.' && (pName[1] == 0 ||
2165 (pName[1] == '.' && pName[2] == 0 && (!str_comp(pEditor->m_pFileDialogPath, "maps") || !str_comp(pEditor->m_pFileDialogPath, "mapres"))))) ||
2166 (!IsDir && ((pEditor->m_FileDialogFileType == CEditor::FILETYPE_MAP && (Length < 4 || str_comp(pName+Length-4, ".map"))) ||
2167 (pEditor->m_FileDialogFileType == CEditor::FILETYPE_IMG && (Length < 4 || str_comp(pName+Length-4, ".png"))))))
2168 return 0;
2170 CEditor::CFilelistItem Item;
2171 str_copy(Item.m_aFilename, pName, sizeof(Item.m_aFilename));
2172 if(IsDir)
2173 str_format(Item.m_aName, sizeof(Item.m_aName), "%s/", pName);
2174 else
2175 str_copy(Item.m_aName, pName, min(static_cast<int>(sizeof(Item.m_aName)), Length-3));
2176 Item.m_IsDir = IsDir != 0;
2177 Item.m_IsLink = false;
2178 Item.m_StorageType = StorageType;
2179 pEditor->m_FileList.add(Item);
2181 return 0;
2184 void CEditor::AddFileDialogEntry(int Index, CUIRect *pView)
2186 m_FilesCur++;
2187 if(m_FilesCur-1 < m_FilesStartAt || m_FilesCur >= m_FilesStopAt)
2188 return;
2190 CUIRect Button, FileIcon;
2191 pView->HSplitTop(15.0f, &Button, pView);
2192 pView->HSplitTop(2.0f, 0, pView);
2193 Button.VSplitLeft(Button.h, &FileIcon, &Button);
2194 Button.VSplitLeft(5.0f, 0, &Button);
2196 Graphics()->TextureSet(g_pData->m_aImages[IMAGE_FILEICONS].m_Id);
2197 Graphics()->QuadsBegin();
2198 RenderTools()->SelectSprite(m_FileList[Index].m_IsDir?SPRITE_FILE_FOLDER:SPRITE_FILE_MAP2);
2199 IGraphics::CQuadItem QuadItem(FileIcon.x, FileIcon.y, FileIcon.w, FileIcon.h);
2200 Graphics()->QuadsDrawTL(&QuadItem, 1);
2201 Graphics()->QuadsEnd();
2203 if(DoButton_File(&m_FileList[Index], m_FileList[Index].m_aName, m_FilesSelectedIndex == Index, &Button, 0, 0))
2205 if(!m_FileList[Index].m_IsDir)
2206 str_copy(m_aFileDialogFileName, m_FileList[Index].m_aFilename, sizeof(m_aFileDialogFileName));
2207 else
2208 m_aFileDialogFileName[0] = 0;
2209 m_FilesSelectedIndex = Index;
2211 if(Input()->MouseDoubleClick())
2212 m_aFileDialogActivate = true;
2216 void CEditor::RenderFileDialog()
2218 // GUI coordsys
2219 Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h);
2220 CUIRect View = *UI()->Screen();
2221 float Width = View.w, Height = View.h;
2223 RenderTools()->DrawUIRect(&View, vec4(0,0,0,0.25f), 0, 0);
2224 View.VMargin(150.0f, &View);
2225 View.HMargin(50.0f, &View);
2226 RenderTools()->DrawUIRect(&View, vec4(0,0,0,0.75f), CUI::CORNER_ALL, 5.0f);
2227 View.Margin(10.0f, &View);
2229 CUIRect Title, FileBox, FileBoxLabel, ButtonBar, Scroll;
2230 View.HSplitTop(18.0f, &Title, &View);
2231 View.HSplitTop(5.0f, 0, &View); // some spacing
2232 View.HSplitBottom(14.0f, &View, &ButtonBar);
2233 View.HSplitBottom(10.0f, &View, 0); // some spacing
2234 View.HSplitBottom(14.0f, &View, &FileBox);
2235 FileBox.VSplitLeft(55.0f, &FileBoxLabel, &FileBox);
2236 View.HSplitBottom(10.0f, &View, 0); // some spacing
2237 View.VSplitRight(15.0f, &View, &Scroll);
2239 // title
2240 RenderTools()->DrawUIRect(&Title, vec4(1, 1, 1, 0.25f), CUI::CORNER_ALL, 4.0f);
2241 Title.VMargin(10.0f, &Title);
2242 UI()->DoLabel(&Title, m_pFileDialogTitle, 12.0f, -1, -1);
2244 // filebox
2245 if(m_FileDialogStorageType == IStorage::TYPE_SAVE)
2247 static int s_FileBoxID = 0;
2248 UI()->DoLabel(&FileBoxLabel, "Filename:", 10.0f, -1, -1);
2249 if(DoEditBox(&s_FileBoxID, &FileBox, m_aFileDialogFileName, sizeof(m_aFileDialogFileName), 10.0f))
2251 // remove '/' and '\'
2252 for(int i = 0; m_aFileDialogFileName[i]; ++i)
2253 if(m_aFileDialogFileName[i] == '/' || m_aFileDialogFileName[i] == '\\')
2254 str_copy(&m_aFileDialogFileName[i], &m_aFileDialogFileName[i+1], (int)(sizeof(m_aFileDialogFileName))-i);
2255 m_FilesSelectedIndex = -1;
2259 int Num = (int)(View.h/17.0f)+1;
2260 static int ScrollBar = 0;
2261 Scroll.HMargin(5.0f, &Scroll);
2262 m_FileDialogScrollValue = UiDoScrollbarV(&ScrollBar, &Scroll, m_FileDialogScrollValue);
2264 int ScrollNum = m_FileList.size()-Num+1;
2265 if(ScrollNum > 0)
2267 if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP))
2268 m_FileDialogScrollValue -= 3.0f/ScrollNum;
2269 if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN))
2270 m_FileDialogScrollValue += 3.0f/ScrollNum;
2272 else
2273 ScrollNum = 0;
2275 if(m_FilesSelectedIndex > -1)
2277 for(int i = 0; i < Input()->NumEvents(); i++)
2279 int NewIndex = -1;
2280 if(Input()->GetEvent(i).m_Flags&IInput::FLAG_PRESS)
2282 if(Input()->GetEvent(i).m_Key == KEY_DOWN) NewIndex = m_FilesSelectedIndex + 1;
2283 if(Input()->GetEvent(i).m_Key == KEY_UP) NewIndex = m_FilesSelectedIndex - 1;
2285 if(NewIndex > -1 && NewIndex < m_FileList.size())
2287 //scroll
2288 float IndexY = View.y - m_FileDialogScrollValue*ScrollNum*17.0f + NewIndex*17.0f;
2289 int Scroll = View.y > IndexY ? -1 : View.y+View.h < IndexY+17.0f ? 1 : 0;
2290 if(Scroll)
2292 if(Scroll < 0)
2293 m_FileDialogScrollValue = ((float)(NewIndex)+0.5f)/ScrollNum;
2294 else
2295 m_FileDialogScrollValue = ((float)(NewIndex-Num)+2.5f)/ScrollNum;
2298 if(!m_FileList[NewIndex].m_IsDir)
2299 str_copy(m_aFileDialogFileName, m_FileList[NewIndex].m_aFilename, sizeof(m_aFileDialogFileName));
2300 else
2301 m_aFileDialogFileName[0] = 0;
2302 m_FilesSelectedIndex = NewIndex;
2307 for(int i = 0; i < Input()->NumEvents(); i++)
2309 if(Input()->GetEvent(i).m_Flags&IInput::FLAG_PRESS)
2311 if(Input()->GetEvent(i).m_Key == KEY_RETURN || Input()->GetEvent(i).m_Key == KEY_KP_ENTER)
2312 m_aFileDialogActivate = true;
2316 if(m_FileDialogScrollValue < 0) m_FileDialogScrollValue = 0;
2317 if(m_FileDialogScrollValue > 1) m_FileDialogScrollValue = 1;
2319 m_FilesStartAt = (int)(ScrollNum*m_FileDialogScrollValue);
2320 if(m_FilesStartAt < 0)
2321 m_FilesStartAt = 0;
2323 m_FilesStopAt = m_FilesStartAt+Num;
2325 m_FilesCur = 0;
2327 // set clipping
2328 UI()->ClipEnable(&View);
2330 for(int i = 0; i < m_FileList.size(); i++)
2331 AddFileDialogEntry(i, &View);
2333 // disable clipping again
2334 UI()->ClipDisable();
2336 // the buttons
2337 static int s_OkButton = 0;
2338 static int s_CancelButton = 0;
2339 static int s_NewFolderButton = 0;
2341 CUIRect Button;
2342 ButtonBar.VSplitRight(50.0f, &ButtonBar, &Button);
2343 bool IsDir = m_FilesSelectedIndex >= 0 && m_FileList[m_FilesSelectedIndex].m_IsDir;
2344 if(DoButton_Editor(&s_OkButton, IsDir ? "Open" : m_pFileDialogButtonText, 0, &Button, 0, 0) || m_aFileDialogActivate)
2346 m_aFileDialogActivate = false;
2347 if(IsDir) // folder
2349 if(str_comp(m_FileList[m_FilesSelectedIndex].m_aFilename, "..") == 0) // parent folder
2351 if(fs_parent_dir(m_pFileDialogPath))
2352 m_pFileDialogPath = m_aFileDialogCurrentFolder; // leave the link
2354 else // sub folder
2356 if(m_FileList[m_FilesSelectedIndex].m_IsLink)
2358 m_pFileDialogPath = m_aFileDialogCurrentLink; // follow the link
2359 str_copy(m_aFileDialogCurrentLink, m_FileList[m_FilesSelectedIndex].m_aFilename, sizeof(m_aFileDialogCurrentLink));
2361 else
2363 char aTemp[MAX_PATH_LENGTH];
2364 str_copy(aTemp, m_pFileDialogPath, sizeof(aTemp));
2365 str_format(m_pFileDialogPath, MAX_PATH_LENGTH, "%s/%s", aTemp, m_FileList[m_FilesSelectedIndex].m_aFilename);
2368 FilelistPopulate(!str_comp(m_pFileDialogPath, "maps") || !str_comp(m_pFileDialogPath, "mapres") ? m_FileDialogStorageType :
2369 m_FileList[m_FilesSelectedIndex].m_StorageType);
2370 if(m_FilesSelectedIndex >= 0 && !m_FileList[m_FilesSelectedIndex].m_IsDir)
2371 str_copy(m_aFileDialogFileName, m_FileList[m_FilesSelectedIndex].m_aFilename, sizeof(m_aFileDialogFileName));
2372 else
2373 m_aFileDialogFileName[0] = 0;
2375 else // file
2377 str_format(m_aFileSaveName, sizeof(m_aFileSaveName), "%s/%s", m_pFileDialogPath, m_aFileDialogFileName);
2378 if(!str_comp(m_pFileDialogButtonText, "Save"))
2380 IOHANDLE File = Storage()->OpenFile(m_aFileSaveName, IOFLAG_READ, IStorage::TYPE_SAVE);
2381 if(File)
2383 io_close(File);
2384 m_PopupEventType = POPEVENT_SAVE;
2385 m_PopupEventActivated = true;
2387 else
2388 if(m_pfnFileDialogFunc)
2389 m_pfnFileDialogFunc(m_aFileSaveName, m_FilesSelectedIndex >= 0 ? m_FileList[m_FilesSelectedIndex].m_StorageType : m_FileDialogStorageType, m_pFileDialogUser);
2391 else
2392 if(m_pfnFileDialogFunc)
2393 m_pfnFileDialogFunc(m_aFileSaveName, m_FilesSelectedIndex >= 0 ? m_FileList[m_FilesSelectedIndex].m_StorageType : m_FileDialogStorageType, m_pFileDialogUser);
2397 ButtonBar.VSplitRight(40.0f, &ButtonBar, &Button);
2398 ButtonBar.VSplitRight(50.0f, &ButtonBar, &Button);
2399 if(DoButton_Editor(&s_CancelButton, "Cancel", 0, &Button, 0, 0) || Input()->KeyPressed(KEY_ESCAPE))
2400 m_Dialog = DIALOG_NONE;
2402 if(m_FileDialogStorageType == IStorage::TYPE_SAVE)
2404 ButtonBar.VSplitLeft(40.0f, 0, &ButtonBar);
2405 ButtonBar.VSplitLeft(70.0f, &Button, &ButtonBar);
2406 if(DoButton_Editor(&s_NewFolderButton, "New folder", 0, &Button, 0, 0))
2408 m_FileDialogNewFolderName[0] = 0;
2409 m_FileDialogErrString[0] = 0;
2410 static int s_NewFolderPopupID = 0;
2411 UiInvokePopupMenu(&s_NewFolderPopupID, 0, Width/2.0f-200.0f, Height/2.0f-100.0f, 400.0f, 200.0f, PopupNewFolder);
2412 UI()->SetActiveItem(0);
2417 void CEditor::FilelistPopulate(int StorageType)
2419 m_FileList.clear();
2420 if(m_FileDialogStorageType != IStorage::TYPE_SAVE && !str_comp(m_pFileDialogPath, "maps"))
2422 CFilelistItem Item;
2423 str_copy(Item.m_aFilename, "downloadedmaps", sizeof(Item.m_aFilename));
2424 str_copy(Item.m_aName, "downloadedmaps/", sizeof(Item.m_aName));
2425 Item.m_IsDir = true;
2426 Item.m_IsLink = true;
2427 Item.m_StorageType = IStorage::TYPE_SAVE;
2428 m_FileList.add(Item);
2430 Storage()->ListDirectory(StorageType, m_pFileDialogPath, EditorListdirCallback, this);
2431 m_FilesSelectedIndex = m_FileList.size() ? 0 : -1;
2432 m_aFileDialogActivate = false;
2435 void CEditor::InvokeFileDialog(int StorageType, int FileType, const char *pTitle, const char *pButtonText,
2436 const char *pBasePath, const char *pDefaultName,
2437 void (*pfnFunc)(const char *pFileName, int StorageType, void *pUser), void *pUser)
2439 m_FileDialogStorageType = StorageType;
2440 m_pFileDialogTitle = pTitle;
2441 m_pFileDialogButtonText = pButtonText;
2442 m_pfnFileDialogFunc = pfnFunc;
2443 m_pFileDialogUser = pUser;
2444 m_aFileDialogFileName[0] = 0;
2445 m_aFileDialogCurrentFolder[0] = 0;
2446 m_aFileDialogCurrentLink[0] = 0;
2447 m_pFileDialogPath = m_aFileDialogCurrentFolder;
2448 m_FileDialogFileType = FileType;
2449 m_FileDialogScrollValue = 0.0f;
2451 if(pDefaultName)
2452 str_copy(m_aFileDialogFileName, pDefaultName, sizeof(m_aFileDialogFileName));
2453 if(pBasePath)
2454 str_copy(m_aFileDialogCurrentFolder, pBasePath, sizeof(m_aFileDialogCurrentFolder));
2456 FilelistPopulate(m_FileDialogStorageType);
2458 m_Dialog = DIALOG_FILE;
2463 void CEditor::RenderModebar(CUIRect View)
2465 CUIRect Button;
2467 // mode buttons
2469 View.VSplitLeft(65.0f, &Button, &View);
2470 Button.HSplitTop(30.0f, 0, &Button);
2471 static int s_Button = 0;
2472 const char *pButName = m_Mode == MODE_LAYERS ? "Layers" : "Images";
2473 if(DoButton_Tab(&s_Button, pButName, 0, &Button, 0, "Switch between images and layers managment."))
2475 if(m_Mode == MODE_LAYERS)
2476 m_Mode = MODE_IMAGES;
2477 else
2478 m_Mode = MODE_LAYERS;
2482 View.VSplitLeft(5.0f, 0, &View);
2485 void CEditor::RenderStatusbar(CUIRect View)
2487 CUIRect Button;
2488 View.VSplitRight(60.0f, &View, &Button);
2489 static int s_EnvelopeButton = 0;
2490 if(DoButton_Editor(&s_EnvelopeButton, "Envelopes", m_ShowEnvelopeEditor, &Button, 0, "Toggles the envelope editor."))
2491 m_ShowEnvelopeEditor = (m_ShowEnvelopeEditor+1)%4;
2493 if(m_pTooltip)
2495 if(ms_pUiGotContext && ms_pUiGotContext == UI()->HotItem())
2497 char aBuf[512];
2498 str_format(aBuf, sizeof(aBuf), "%s Right click for context menu.", m_pTooltip);
2499 UI()->DoLabel(&View, aBuf, 10.0f, -1, -1);
2501 else
2502 UI()->DoLabel(&View, m_pTooltip, 10.0f, -1, -1);
2506 void CEditor::RenderEnvelopeEditor(CUIRect View)
2508 if(m_SelectedEnvelope < 0) m_SelectedEnvelope = 0;
2509 if(m_SelectedEnvelope >= m_Map.m_lEnvelopes.size()) m_SelectedEnvelope = m_Map.m_lEnvelopes.size()-1;
2511 CEnvelope *pEnvelope = 0;
2512 if(m_SelectedEnvelope >= 0 && m_SelectedEnvelope < m_Map.m_lEnvelopes.size())
2513 pEnvelope = m_Map.m_lEnvelopes[m_SelectedEnvelope];
2515 CUIRect ToolBar, CurveBar, ColorBar;
2516 View.HSplitTop(15.0f, &ToolBar, &View);
2517 View.HSplitTop(15.0f, &CurveBar, &View);
2518 ToolBar.Margin(2.0f, &ToolBar);
2519 CurveBar.Margin(2.0f, &CurveBar);
2521 // do the toolbar
2523 CUIRect Button;
2524 CEnvelope *pNewEnv = 0;
2526 ToolBar.VSplitRight(50.0f, &ToolBar, &Button);
2527 static int s_New4dButton = 0;
2528 if(DoButton_Editor(&s_New4dButton, "Color+", 0, &Button, 0, "Creates a new color envelope"))
2530 m_Map.m_Modified = true;
2531 pNewEnv = m_Map.NewEnvelope(4);
2534 ToolBar.VSplitRight(5.0f, &ToolBar, &Button);
2535 ToolBar.VSplitRight(50.0f, &ToolBar, &Button);
2536 static int s_New2dButton = 0;
2537 if(DoButton_Editor(&s_New2dButton, "Pos.+", 0, &Button, 0, "Creates a new pos envelope"))
2539 m_Map.m_Modified = true;
2540 pNewEnv = m_Map.NewEnvelope(3);
2543 // Delete button
2544 if(m_SelectedEnvelope >= 0)
2546 ToolBar.VSplitRight(10.0f, &ToolBar, &Button);
2547 ToolBar.VSplitRight(50.0f, &ToolBar, &Button);
2548 static int s_DelButton = 0;
2549 if(DoButton_Editor(&s_DelButton, "Delete", 0, &Button, 0, "Delete this envelope"))
2551 m_Map.m_Modified = true;
2552 m_Map.DeleteEnvelope(m_SelectedEnvelope);
2553 if(m_SelectedEnvelope >= m_Map.m_lEnvelopes.size())
2554 m_SelectedEnvelope = m_Map.m_lEnvelopes.size()-1;
2555 pEnvelope = m_SelectedEnvelope >= 0 ? m_Map.m_lEnvelopes[m_SelectedEnvelope] : 0;
2559 if(pNewEnv) // add the default points
2561 if(pNewEnv->m_Channels == 4)
2563 pNewEnv->AddPoint(0, 1,1,1,1);
2564 pNewEnv->AddPoint(1000, 1,1,1,1);
2566 else
2568 pNewEnv->AddPoint(0, 0);
2569 pNewEnv->AddPoint(1000, 0);
2573 CUIRect Shifter, Inc, Dec;
2574 ToolBar.VSplitLeft(60.0f, &Shifter, &ToolBar);
2575 Shifter.VSplitRight(15.0f, &Shifter, &Inc);
2576 Shifter.VSplitLeft(15.0f, &Dec, &Shifter);
2577 char aBuf[512];
2578 str_format(aBuf, sizeof(aBuf),"%d/%d", m_SelectedEnvelope+1, m_Map.m_lEnvelopes.size());
2579 RenderTools()->DrawUIRect(&Shifter, vec4(1,1,1,0.5f), 0, 0.0f);
2580 UI()->DoLabel(&Shifter, aBuf, 10.0f, 0, -1);
2582 static int s_PrevButton = 0;
2583 if(DoButton_ButtonDec(&s_PrevButton, 0, 0, &Dec, 0, "Previous Envelope"))
2584 m_SelectedEnvelope--;
2586 static int s_NextButton = 0;
2587 if(DoButton_ButtonInc(&s_NextButton, 0, 0, &Inc, 0, "Next Envelope"))
2588 m_SelectedEnvelope++;
2590 if(pEnvelope)
2592 ToolBar.VSplitLeft(15.0f, &Button, &ToolBar);
2593 ToolBar.VSplitLeft(35.0f, &Button, &ToolBar);
2594 UI()->DoLabel(&Button, "Name:", 10.0f, -1, -1);
2596 ToolBar.VSplitLeft(80.0f, &Button, &ToolBar);
2598 static int s_NameBox = 0;
2599 if(DoEditBox(&s_NameBox, &Button, pEnvelope->m_aName, sizeof(pEnvelope->m_aName), 10.0f))
2600 m_Map.m_Modified = true;
2604 bool ShowColorBar = false;
2605 if(pEnvelope && pEnvelope->m_Channels == 4)
2607 ShowColorBar = true;
2608 View.HSplitTop(20.0f, &ColorBar, &View);
2609 ColorBar.Margin(2.0f, &ColorBar);
2610 RenderBackground(ColorBar, ms_CheckerTexture, 16.0f, 1.0f);
2613 RenderBackground(View, ms_CheckerTexture, 32.0f, 0.1f);
2615 if(pEnvelope)
2617 static array<int> Selection;
2618 static int sEnvelopeEditorID = 0;
2619 static int s_ActiveChannels = 0xf;
2621 if(pEnvelope)
2623 CUIRect Button;
2625 ToolBar.VSplitLeft(15.0f, &Button, &ToolBar);
2627 static const char *s_paNames[2][4] = {
2628 {"X", "Y", "R", ""},
2629 {"R", "G", "B", "A"},
2632 const char *paDescriptions[2][4] = {
2633 {"X-axis of the envelope", "Y-axis of the envelope", "Rotation of the envelope", ""},
2634 {"Red value of the envelope", "Green value of the envelope", "Blue value of the envelope", "Alpha value of the envelope"},
2637 static int s_aChannelButtons[4] = {0};
2638 int Bit = 1;
2639 //ui_draw_button_func draw_func;
2641 for(int i = 0; i < pEnvelope->m_Channels; i++, Bit<<=1)
2643 ToolBar.VSplitLeft(15.0f, &Button, &ToolBar);
2645 /*if(i == 0) draw_func = draw_editor_button_l;
2646 else if(i == envelope->channels-1) draw_func = draw_editor_button_r;
2647 else draw_func = draw_editor_button_m;*/
2649 if(DoButton_Editor(&s_aChannelButtons[i], s_paNames[pEnvelope->m_Channels-3][i], s_ActiveChannels&Bit, &Button, 0, paDescriptions[pEnvelope->m_Channels-3][i]))
2650 s_ActiveChannels ^= Bit;
2654 float EndTime = pEnvelope->EndTime();
2655 if(EndTime < 1)
2656 EndTime = 1;
2658 pEnvelope->FindTopBottom(s_ActiveChannels);
2659 float Top = pEnvelope->m_Top;
2660 float Bottom = pEnvelope->m_Bottom;
2662 if(Top < 1)
2663 Top = 1;
2664 if(Bottom >= 0)
2665 Bottom = 0;
2667 float TimeScale = EndTime/View.w;
2668 float ValueScale = (Top-Bottom)/View.h;
2670 if(UI()->MouseInside(&View))
2671 UI()->SetHotItem(&sEnvelopeEditorID);
2673 if(UI()->HotItem() == &sEnvelopeEditorID)
2675 // do stuff
2676 if(pEnvelope)
2678 if(UI()->MouseButtonClicked(1))
2680 // add point
2681 int Time = (int)(((UI()->MouseX()-View.x)*TimeScale)*1000.0f);
2682 //float env_y = (UI()->MouseY()-view.y)/TimeScale;
2683 float aChannels[4];
2684 pEnvelope->Eval(Time, aChannels);
2685 pEnvelope->AddPoint(Time,
2686 f2fx(aChannels[0]), f2fx(aChannels[1]),
2687 f2fx(aChannels[2]), f2fx(aChannels[3]));
2688 m_Map.m_Modified = true;
2691 m_pTooltip = "Press right mouse button to create a new point";
2695 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)};
2697 // render lines
2699 UI()->ClipEnable(&View);
2700 Graphics()->TextureSet(-1);
2701 Graphics()->LinesBegin();
2702 for(int c = 0; c < pEnvelope->m_Channels; c++)
2704 if(s_ActiveChannels&(1<<c))
2705 Graphics()->SetColor(aColors[c].r,aColors[c].g,aColors[c].b,1);
2706 else
2707 Graphics()->SetColor(aColors[c].r*0.5f,aColors[c].g*0.5f,aColors[c].b*0.5f,1);
2709 float PrevX = 0;
2710 float aResults[4];
2711 pEnvelope->Eval(0.000001f, aResults);
2712 float PrevValue = aResults[c];
2714 int Steps = (int)((View.w/UI()->Screen()->w) * Graphics()->ScreenWidth());
2715 for(int i = 1; i <= Steps; i++)
2717 float a = i/(float)Steps;
2718 pEnvelope->Eval(a*EndTime, aResults);
2719 float v = aResults[c];
2720 v = (v-Bottom)/(Top-Bottom);
2722 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);
2723 Graphics()->LinesDraw(&LineItem, 1);
2724 PrevX = a;
2725 PrevValue = v;
2728 Graphics()->LinesEnd();
2729 UI()->ClipDisable();
2732 // render curve options
2734 for(int i = 0; i < pEnvelope->m_lPoints.size()-1; i++)
2736 float t0 = pEnvelope->m_lPoints[i].m_Time/1000.0f/EndTime;
2737 float t1 = pEnvelope->m_lPoints[i+1].m_Time/1000.0f/EndTime;
2739 //dbg_msg("", "%f", end_time);
2741 CUIRect v;
2742 v.x = CurveBar.x + (t0+(t1-t0)*0.5f) * CurveBar.w;
2743 v.y = CurveBar.y;
2744 v.h = CurveBar.h;
2745 v.w = CurveBar.h;
2746 v.x -= v.w/2;
2747 void *pID = &pEnvelope->m_lPoints[i].m_Curvetype;
2748 const char *paTypeName[] = {
2749 "N", "L", "S", "F", "M"
2752 if(DoButton_Editor(pID, paTypeName[pEnvelope->m_lPoints[i].m_Curvetype], 0, &v, 0, "Switch curve type"))
2753 pEnvelope->m_lPoints[i].m_Curvetype = (pEnvelope->m_lPoints[i].m_Curvetype+1)%NUM_CURVETYPES;
2757 // render colorbar
2758 if(ShowColorBar)
2760 Graphics()->TextureSet(-1);
2761 Graphics()->QuadsBegin();
2762 for(int i = 0; i < pEnvelope->m_lPoints.size()-1; i++)
2764 float r0 = fx2f(pEnvelope->m_lPoints[i].m_aValues[0]);
2765 float g0 = fx2f(pEnvelope->m_lPoints[i].m_aValues[1]);
2766 float b0 = fx2f(pEnvelope->m_lPoints[i].m_aValues[2]);
2767 float a0 = fx2f(pEnvelope->m_lPoints[i].m_aValues[3]);
2768 float r1 = fx2f(pEnvelope->m_lPoints[i+1].m_aValues[0]);
2769 float g1 = fx2f(pEnvelope->m_lPoints[i+1].m_aValues[1]);
2770 float b1 = fx2f(pEnvelope->m_lPoints[i+1].m_aValues[2]);
2771 float a1 = fx2f(pEnvelope->m_lPoints[i+1].m_aValues[3]);
2773 IGraphics::CColorVertex Array[4] = {IGraphics::CColorVertex(0, r0, g0, b0, a0),
2774 IGraphics::CColorVertex(1, r1, g1, b1, a1),
2775 IGraphics::CColorVertex(2, r1, g1, b1, a1),
2776 IGraphics::CColorVertex(3, r0, g0, b0, a0)};
2777 Graphics()->SetColorVertex(Array, 4);
2779 float x0 = pEnvelope->m_lPoints[i].m_Time/1000.0f/EndTime;
2780 // float y0 = (fx2f(envelope->points[i].values[c])-bottom)/(top-bottom);
2781 float x1 = pEnvelope->m_lPoints[i+1].m_Time/1000.0f/EndTime;
2782 //float y1 = (fx2f(envelope->points[i+1].values[c])-bottom)/(top-bottom);
2783 CUIRect v;
2784 v.x = ColorBar.x + x0*ColorBar.w;
2785 v.y = ColorBar.y;
2786 v.w = (x1-x0)*ColorBar.w;
2787 v.h = ColorBar.h;
2789 IGraphics::CQuadItem QuadItem(v.x, v.y, v.w, v.h);
2790 Graphics()->QuadsDrawTL(&QuadItem, 1);
2792 Graphics()->QuadsEnd();
2795 // render handles
2797 int CurrentValue = 0, CurrentTime = 0;
2799 Graphics()->TextureSet(-1);
2800 Graphics()->QuadsBegin();
2801 for(int c = 0; c < pEnvelope->m_Channels; c++)
2803 if(!(s_ActiveChannels&(1<<c)))
2804 continue;
2806 for(int i = 0; i < pEnvelope->m_lPoints.size(); i++)
2808 float x0 = pEnvelope->m_lPoints[i].m_Time/1000.0f/EndTime;
2809 float y0 = (fx2f(pEnvelope->m_lPoints[i].m_aValues[c])-Bottom)/(Top-Bottom);
2810 CUIRect Final;
2811 Final.x = View.x + x0*View.w;
2812 Final.y = View.y+View.h - y0*View.h;
2813 Final.x -= 2.0f;
2814 Final.y -= 2.0f;
2815 Final.w = 4.0f;
2816 Final.h = 4.0f;
2818 void *pID = &pEnvelope->m_lPoints[i].m_aValues[c];
2820 if(UI()->MouseInside(&Final))
2821 UI()->SetHotItem(pID);
2823 float ColorMod = 1.0f;
2825 if(UI()->ActiveItem() == pID)
2827 if(!UI()->MouseButton(0))
2829 UI()->SetActiveItem(0);
2831 else
2833 if(Input()->KeyPressed(KEY_LSHIFT) || Input()->KeyPressed(KEY_RSHIFT))
2835 if(i != 0)
2837 if((Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL)))
2838 pEnvelope->m_lPoints[i].m_Time += (int)((m_MouseDeltaX));
2839 else
2840 pEnvelope->m_lPoints[i].m_Time += (int)((m_MouseDeltaX*TimeScale)*1000.0f);
2841 if(pEnvelope->m_lPoints[i].m_Time < pEnvelope->m_lPoints[i-1].m_Time)
2842 pEnvelope->m_lPoints[i].m_Time = pEnvelope->m_lPoints[i-1].m_Time + 1;
2843 if(i+1 != pEnvelope->m_lPoints.size() && pEnvelope->m_lPoints[i].m_Time > pEnvelope->m_lPoints[i+1].m_Time)
2844 pEnvelope->m_lPoints[i].m_Time = pEnvelope->m_lPoints[i+1].m_Time - 1;
2847 else
2849 if((Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL)))
2850 pEnvelope->m_lPoints[i].m_aValues[c] -= f2fx(m_MouseDeltaY*0.001f);
2851 else
2852 pEnvelope->m_lPoints[i].m_aValues[c] -= f2fx(m_MouseDeltaY*ValueScale);
2854 m_Map.m_Modified = true;
2857 ColorMod = 100.0f;
2858 Graphics()->SetColor(1,1,1,1);
2860 else if(UI()->HotItem() == pID)
2862 if(UI()->MouseButton(0))
2864 Selection.clear();
2865 Selection.add(i);
2866 UI()->SetActiveItem(pID);
2869 // remove point
2870 if(UI()->MouseButtonClicked(1))
2872 pEnvelope->m_lPoints.remove_index(i);
2873 m_Map.m_Modified = true;
2876 ColorMod = 100.0f;
2877 Graphics()->SetColor(1,0.75f,0.75f,1);
2878 m_pTooltip = "Left mouse to drag. Hold ctrl to be more precise. Hold shift to alter time point aswell. Right click to delete.";
2881 if(UI()->ActiveItem() == pID || UI()->HotItem() == pID)
2883 CurrentTime = pEnvelope->m_lPoints[i].m_Time;
2884 CurrentValue = pEnvelope->m_lPoints[i].m_aValues[c];
2887 Graphics()->SetColor(aColors[c].r*ColorMod, aColors[c].g*ColorMod, aColors[c].b*ColorMod, 1.0f);
2888 IGraphics::CQuadItem QuadItem(Final.x, Final.y, Final.w, Final.h);
2889 Graphics()->QuadsDrawTL(&QuadItem, 1);
2892 Graphics()->QuadsEnd();
2894 char aBuf[512];
2895 str_format(aBuf, sizeof(aBuf),"%.3f %.3f", CurrentTime/1000.0f, fx2f(CurrentValue));
2896 UI()->DoLabel(&ToolBar, aBuf, 10.0f, 0, -1);
2901 int CEditor::PopupMenuFile(CEditor *pEditor, CUIRect View)
2903 static int s_NewMapButton = 0;
2904 static int s_SaveButton = 0;
2905 static int s_SaveAsButton = 0;
2906 static int s_OpenButton = 0;
2907 static int s_AppendButton = 0;
2908 static int s_ExitButton = 0;
2910 CUIRect Slot;
2911 View.HSplitTop(2.0f, &Slot, &View);
2912 View.HSplitTop(12.0f, &Slot, &View);
2913 if(pEditor->DoButton_MenuItem(&s_NewMapButton, "New", 0, &Slot, 0, "Creates a new map"))
2915 if(pEditor->HasUnsavedData())
2917 pEditor->m_PopupEventType = POPEVENT_NEW;
2918 pEditor->m_PopupEventActivated = true;
2920 else
2922 pEditor->Reset();
2923 pEditor->m_aFileName[0] = 0;
2925 return 1;
2928 View.HSplitTop(10.0f, &Slot, &View);
2929 View.HSplitTop(12.0f, &Slot, &View);
2930 if(pEditor->DoButton_MenuItem(&s_OpenButton, "Load", 0, &Slot, 0, "Opens a map for editing"))
2932 if(pEditor->HasUnsavedData())
2934 pEditor->m_PopupEventType = POPEVENT_LOAD;
2935 pEditor->m_PopupEventActivated = true;
2937 else
2938 pEditor->InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_MAP, "Load map", "Load", "maps", "", pEditor->CallbackOpenMap, pEditor);
2939 return 1;
2942 View.HSplitTop(10.0f, &Slot, &View);
2943 View.HSplitTop(12.0f, &Slot, &View);
2944 if(pEditor->DoButton_MenuItem(&s_AppendButton, "Append", 0, &Slot, 0, "Opens a map and adds everything from that map to the current one"))
2946 pEditor->InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_MAP, "Append map", "Append", "maps", "", pEditor->CallbackAppendMap, pEditor);
2947 return 1;
2950 View.HSplitTop(10.0f, &Slot, &View);
2951 View.HSplitTop(12.0f, &Slot, &View);
2952 if(pEditor->DoButton_MenuItem(&s_SaveButton, "Save", 0, &Slot, 0, "Saves the current map"))
2954 if(pEditor->m_aFileName[0] && pEditor->m_ValidSaveFilename)
2956 str_copy(pEditor->m_aFileSaveName, pEditor->m_aFileName, sizeof(pEditor->m_aFileSaveName));
2957 pEditor->m_PopupEventType = POPEVENT_SAVE;
2958 pEditor->m_PopupEventActivated = true;
2960 else
2961 pEditor->InvokeFileDialog(IStorage::TYPE_SAVE, FILETYPE_MAP, "Save map", "Save", "maps", "", pEditor->CallbackSaveMap, pEditor);
2962 return 1;
2965 View.HSplitTop(2.0f, &Slot, &View);
2966 View.HSplitTop(12.0f, &Slot, &View);
2967 if(pEditor->DoButton_MenuItem(&s_SaveAsButton, "Save As", 0, &Slot, 0, "Saves the current map under a new name"))
2969 pEditor->InvokeFileDialog(IStorage::TYPE_SAVE, FILETYPE_MAP, "Save map", "Save", "maps", "", pEditor->CallbackSaveMap, pEditor);
2970 return 1;
2973 View.HSplitTop(10.0f, &Slot, &View);
2974 View.HSplitTop(12.0f, &Slot, &View);
2975 if(pEditor->DoButton_MenuItem(&s_ExitButton, "Exit", 0, &Slot, 0, "Exits from the editor"))
2977 if(pEditor->HasUnsavedData())
2979 pEditor->m_PopupEventType = POPEVENT_EXIT;
2980 pEditor->m_PopupEventActivated = true;
2982 else
2983 g_Config.m_ClEditor = 0;
2984 return 1;
2987 return 0;
2990 void CEditor::RenderMenubar(CUIRect MenuBar)
2992 static CUIRect s_File /*, view, help*/;
2994 MenuBar.VSplitLeft(60.0f, &s_File, &MenuBar);
2995 if(DoButton_Menu(&s_File, "File", 0, &s_File, 0, 0))
2996 UiInvokePopupMenu(&s_File, 1, s_File.x, s_File.y+s_File.h-1.0f, 120, 150, PopupMenuFile, this);
2999 menubar.VSplitLeft(5.0f, 0, &menubar);
3000 menubar.VSplitLeft(60.0f, &view, &menubar);
3001 if(do_editor_button(&view, "View", 0, &view, draw_editor_button_menu, 0, 0))
3002 (void)0;
3004 menubar.VSplitLeft(5.0f, 0, &menubar);
3005 menubar.VSplitLeft(60.0f, &help, &menubar);
3006 if(do_editor_button(&help, "Help", 0, &help, draw_editor_button_menu, 0, 0))
3007 (void)0;
3010 MenuBar.VSplitLeft(40.0f, 0, &MenuBar);
3011 char aBuf[128];
3012 str_format(aBuf, sizeof(aBuf), "File: %s", m_aFileName);
3013 UI()->DoLabel(&MenuBar, aBuf, 10.0f, -1, -1);
3016 void CEditor::Render()
3018 // basic start
3019 Graphics()->Clear(1.0f, 0.0f, 1.0f);
3020 CUIRect View = *UI()->Screen();
3021 Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h);
3023 float Width = View.w;
3024 float Height = View.h;
3026 // reset tip
3027 m_pTooltip = 0;
3029 // render checker
3030 RenderBackground(View, ms_CheckerTexture, 32.0f, 1.0f);
3032 CUIRect MenuBar, CModeBar, ToolBar, StatusBar, EnvelopeEditor, ToolBox;
3033 m_ShowPicker = Input()->KeyPressed(KEY_SPACE) != 0 && m_Dialog == DIALOG_NONE;
3035 if(m_GuiActive)
3038 View.HSplitTop(16.0f, &MenuBar, &View);
3039 View.HSplitTop(53.0f, &ToolBar, &View);
3040 View.VSplitLeft(100.0f, &ToolBox, &View);
3041 View.HSplitBottom(16.0f, &View, &StatusBar);
3043 if(m_ShowEnvelopeEditor && !m_ShowPicker)
3045 float size = 125.0f;
3046 if(m_ShowEnvelopeEditor == 2)
3047 size *= 2.0f;
3048 else if(m_ShowEnvelopeEditor == 3)
3049 size *= 3.0f;
3050 View.HSplitBottom(size, &View, &EnvelopeEditor);
3054 // a little hack for now
3055 if(m_Mode == MODE_LAYERS)
3056 DoMapEditor(View, ToolBar);
3058 if(m_GuiActive)
3060 float Brightness = 0.25f;
3061 RenderBackground(MenuBar, ms_BackgroundTexture, 128.0f, Brightness*0);
3062 MenuBar.Margin(2.0f, &MenuBar);
3064 RenderBackground(ToolBox, ms_BackgroundTexture, 128.0f, Brightness);
3065 ToolBox.Margin(2.0f, &ToolBox);
3067 RenderBackground(ToolBar, ms_BackgroundTexture, 128.0f, Brightness);
3068 ToolBar.Margin(2.0f, &ToolBar);
3069 ToolBar.VSplitLeft(100.0f, &CModeBar, &ToolBar);
3071 RenderBackground(StatusBar, ms_BackgroundTexture, 128.0f, Brightness);
3072 StatusBar.Margin(2.0f, &StatusBar);
3074 // do the toolbar
3075 if(m_Mode == MODE_LAYERS)
3076 DoToolbar(ToolBar);
3078 if(m_ShowEnvelopeEditor)
3080 RenderBackground(EnvelopeEditor, ms_BackgroundTexture, 128.0f, Brightness);
3081 EnvelopeEditor.Margin(2.0f, &EnvelopeEditor);
3086 if(m_Mode == MODE_LAYERS)
3087 RenderLayers(ToolBox, ToolBar, View);
3088 else if(m_Mode == MODE_IMAGES)
3089 RenderImages(ToolBox, ToolBar, View);
3091 Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h);
3093 if(m_GuiActive)
3095 RenderMenubar(MenuBar);
3097 RenderModebar(CModeBar);
3098 if(m_ShowEnvelopeEditor)
3099 RenderEnvelopeEditor(EnvelopeEditor);
3102 if(m_Dialog == DIALOG_FILE)
3104 static int s_NullUiTarget = 0;
3105 UI()->SetHotItem(&s_NullUiTarget);
3106 RenderFileDialog();
3109 if(m_PopupEventActivated)
3111 static int s_PopupID = 0;
3112 UiInvokePopupMenu(&s_PopupID, 0, Width/2.0f-200.0f, Height/2.0f-100.0f, 400.0f, 200.0f, PopupEvent);
3113 m_PopupEventActivated = false;
3117 UiDoPopupMenu();
3119 if(m_GuiActive)
3120 RenderStatusbar(StatusBar);
3123 if(g_Config.m_EdShowkeys)
3125 Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h);
3126 CTextCursor Cursor;
3127 TextRender()->SetCursor(&Cursor, View.x+10, View.y+View.h-24-10, 24.0f, TEXTFLAG_RENDER);
3129 int NKeys = 0;
3130 for(int i = 0; i < KEY_LAST; i++)
3132 if(Input()->KeyPressed(i))
3134 if(NKeys)
3135 TextRender()->TextEx(&Cursor, " + ", -1);
3136 TextRender()->TextEx(&Cursor, Input()->KeyName(i), -1);
3137 NKeys++;
3142 if(m_ShowMousePointer)
3144 // render butt ugly mouse cursor
3145 float mx = UI()->MouseX();
3146 float my = UI()->MouseY();
3147 Graphics()->TextureSet(ms_CursorTexture);
3148 Graphics()->QuadsBegin();
3149 if(ms_pUiGotContext == UI()->HotItem())
3150 Graphics()->SetColor(1,0,0,1);
3151 IGraphics::CQuadItem QuadItem(mx,my, 16.0f, 16.0f);
3152 Graphics()->QuadsDrawTL(&QuadItem, 1);
3153 Graphics()->QuadsEnd();
3158 void CEditor::Reset(bool CreateDefault)
3160 m_Map.Clean();
3162 // create default layers
3163 if(CreateDefault)
3164 m_Map.CreateDefault(ms_EntitiesTexture);
3170 m_SelectedLayer = 0;
3171 m_SelectedGroup = 0;
3172 m_SelectedQuad = -1;
3173 m_SelectedPoints = 0;
3174 m_SelectedEnvelope = 0;
3175 m_SelectedImage = 0;
3177 m_WorldOffsetX = 0;
3178 m_WorldOffsetY = 0;
3179 m_EditorOffsetX = 0.0f;
3180 m_EditorOffsetY = 0.0f;
3182 m_WorldZoom = 1.0f;
3183 m_ZoomLevel = 200;
3185 m_MouseDeltaX = 0;
3186 m_MouseDeltaY = 0;
3187 m_MouseDeltaWx = 0;
3188 m_MouseDeltaWy = 0;
3190 m_Map.m_Modified = false;
3193 void CEditorMap::DeleteEnvelope(int Index)
3195 if(Index < 0 || Index >= m_lEnvelopes.size())
3196 return;
3198 m_Modified = true;
3200 // fix links between envelopes and quads
3201 for(int i = 0; i < m_lGroups.size(); ++i)
3202 for(int j = 0; j < m_lGroups[i]->m_lLayers.size(); ++j)
3203 if(m_lGroups[i]->m_lLayers[j]->m_Type == LAYERTYPE_QUADS)
3205 CLayerQuads *Layer = static_cast<CLayerQuads *>(m_lGroups[i]->m_lLayers[j]);
3206 for(int k = 0; k < Layer->m_lQuads.size(); ++k)
3208 if(Layer->m_lQuads[k].m_PosEnv == Index)
3209 Layer->m_lQuads[k].m_PosEnv = -1;
3210 else if(Layer->m_lQuads[k].m_PosEnv > Index)
3211 Layer->m_lQuads[k].m_PosEnv--;
3212 if(Layer->m_lQuads[k].m_ColorEnv == Index)
3213 Layer->m_lQuads[k].m_ColorEnv = -1;
3214 else if(Layer->m_lQuads[k].m_ColorEnv > Index)
3215 Layer->m_lQuads[k].m_ColorEnv--;
3219 m_lEnvelopes.remove_index(Index);
3222 void CEditorMap::MakeGameLayer(CLayer *pLayer)
3224 m_pGameLayer = (CLayerGame *)pLayer;
3225 m_pGameLayer->m_pEditor = m_pEditor;
3226 m_pGameLayer->m_TexID = m_pEditor->ms_EntitiesTexture;
3229 void CEditorMap::MakeGameGroup(CLayerGroup *pGroup)
3231 m_pGameGroup = pGroup;
3232 m_pGameGroup->m_GameGroup = true;
3233 m_pGameGroup->m_pName = "Game";
3238 void CEditorMap::Clean()
3240 m_lGroups.delete_all();
3241 m_lEnvelopes.delete_all();
3242 m_lImages.delete_all();
3244 m_pGameLayer = 0x0;
3245 m_pGameGroup = 0x0;
3247 m_Modified = false;
3250 void CEditorMap::CreateDefault(int EntitiesTexture)
3252 // add background
3253 CLayerGroup *pGroup = NewGroup();
3254 pGroup->m_ParallaxX = 0;
3255 pGroup->m_ParallaxY = 0;
3256 CLayerQuads *pLayer = new CLayerQuads;
3257 pLayer->m_pEditor = m_pEditor;
3258 CQuad *pQuad = pLayer->NewQuad();
3259 const int Width = 800000;
3260 const int Height = 600000;
3261 pQuad->m_aPoints[0].x = pQuad->m_aPoints[2].x = -Width;
3262 pQuad->m_aPoints[1].x = pQuad->m_aPoints[3].x = Width;
3263 pQuad->m_aPoints[0].y = pQuad->m_aPoints[1].y = -Height;
3264 pQuad->m_aPoints[2].y = pQuad->m_aPoints[3].y = Height;
3265 pQuad->m_aColors[0].r = pQuad->m_aColors[1].r = 94;
3266 pQuad->m_aColors[0].g = pQuad->m_aColors[1].g = 132;
3267 pQuad->m_aColors[0].b = pQuad->m_aColors[1].b = 174;
3268 pQuad->m_aColors[2].r = pQuad->m_aColors[3].r = 204;
3269 pQuad->m_aColors[2].g = pQuad->m_aColors[3].g = 232;
3270 pQuad->m_aColors[2].b = pQuad->m_aColors[3].b = 255;
3271 pGroup->AddLayer(pLayer);
3273 // add game layer
3274 MakeGameGroup(NewGroup());
3275 MakeGameLayer(new CLayerGame(50, 50));
3276 m_pGameGroup->AddLayer(m_pGameLayer);
3279 void CEditor::Init()
3281 m_pInput = Kernel()->RequestInterface<IInput>();
3282 m_pClient = Kernel()->RequestInterface<IClient>();
3283 m_pConsole = Kernel()->RequestInterface<IConsole>();
3284 m_pGraphics = Kernel()->RequestInterface<IGraphics>();
3285 m_pTextRender = Kernel()->RequestInterface<ITextRender>();
3286 m_pStorage = Kernel()->RequestInterface<IStorage>();
3287 m_RenderTools.m_pGraphics = m_pGraphics;
3288 m_RenderTools.m_pUI = &m_UI;
3289 m_UI.SetGraphics(m_pGraphics, m_pTextRender);
3290 m_Map.m_pEditor = this;
3292 ms_CheckerTexture = Graphics()->LoadTexture("editor/checker.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, 0);
3293 ms_BackgroundTexture = Graphics()->LoadTexture("editor/background.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, 0);
3294 ms_CursorTexture = Graphics()->LoadTexture("editor/cursor.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, 0);
3295 ms_EntitiesTexture = Graphics()->LoadTexture("editor/entities.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, 0);
3297 m_TilesetPicker.m_pEditor = this;
3298 m_TilesetPicker.MakePalette();
3299 m_TilesetPicker.m_Readonly = true;
3301 m_Brush.m_pMap = &m_Map;
3303 Reset();
3304 m_Map.m_Modified = false;
3307 void CEditor::DoMapBorder()
3309 CLayerTiles *pT = (CLayerTiles *)GetSelectedLayerType(0, LAYERTYPE_TILES);
3311 for(int i = 0; i < pT->m_Width*2; ++i)
3312 pT->m_pTiles[i].m_Index = 1;
3314 for(int i = 0; i < pT->m_Width*pT->m_Height; ++i)
3316 if(i%pT->m_Width < 2 || i%pT->m_Width > pT->m_Width-3)
3317 pT->m_pTiles[i].m_Index = 1;
3320 for(int i = (pT->m_Width*(pT->m_Height-2)); i < pT->m_Width*pT->m_Height; ++i)
3321 pT->m_pTiles[i].m_Index = 1;
3324 void CEditor::UpdateAndRender()
3326 static float s_MouseX = 0.0f;
3327 static float s_MouseY = 0.0f;
3329 if(m_Animate)
3330 m_AnimateTime = (time_get()-m_AnimateStart)/(float)time_freq();
3331 else
3332 m_AnimateTime = 0;
3333 ms_pUiGotContext = 0;
3335 // handle mouse movement
3336 float mx, my, Mwx, Mwy;
3337 float rx, ry;
3339 Input()->MouseRelative(&rx, &ry);
3340 UI()->ConvertMouseMove(&rx, &ry);
3341 m_MouseDeltaX = rx;
3342 m_MouseDeltaY = ry;
3344 if(!m_LockMouse)
3346 s_MouseX += rx;
3347 s_MouseY += ry;
3350 s_MouseX = clamp(s_MouseX, 0.0f, UI()->Screen()->w);
3351 s_MouseY = clamp(s_MouseY, 0.0f, UI()->Screen()->h);
3353 // update the ui
3354 mx = s_MouseX;
3355 my = s_MouseY;
3356 Mwx = 0;
3357 Mwy = 0;
3359 // fix correct world x and y
3360 CLayerGroup *g = GetSelectedGroup();
3361 if(g)
3363 float aPoints[4];
3364 g->Mapping(aPoints);
3366 float WorldWidth = aPoints[2]-aPoints[0];
3367 float WorldHeight = aPoints[3]-aPoints[1];
3369 Mwx = aPoints[0] + WorldWidth * (s_MouseX/UI()->Screen()->w);
3370 Mwy = aPoints[1] + WorldHeight * (s_MouseY/UI()->Screen()->h);
3371 m_MouseDeltaWx = m_MouseDeltaX*(WorldWidth / UI()->Screen()->w);
3372 m_MouseDeltaWy = m_MouseDeltaY*(WorldHeight / UI()->Screen()->h);
3375 int Buttons = 0;
3376 if(Input()->KeyPressed(KEY_MOUSE_1)) Buttons |= 1;
3377 if(Input()->KeyPressed(KEY_MOUSE_2)) Buttons |= 2;
3378 if(Input()->KeyPressed(KEY_MOUSE_3)) Buttons |= 4;
3380 UI()->Update(mx,my,Mwx,Mwy,Buttons);
3383 // toggle gui
3384 if(Input()->KeyDown(KEY_TAB))
3385 m_GuiActive = !m_GuiActive;
3387 if(Input()->KeyDown(KEY_F10))
3388 m_ShowMousePointer = false;
3390 Render();
3392 if(Input()->KeyDown(KEY_F10))
3394 Graphics()->TakeScreenshot(0);
3395 m_ShowMousePointer = true;
3398 Input()->ClearEvents();
3401 IEditor *CreateEditor() { return new CEditor; }