fixed quad selection via envelope preview
[twcon.git] / src / game / editor / editor.cpp
blob2c3aa5506bfc4ffca3f610987dd818fea381c49f
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/input.h>
12 #include <engine/keys.h>
13 #include <engine/storage.h>
14 #include <engine/textrender.h>
16 #include <game/gamecore.h>
17 #include <game/localization.h>
18 #include <game/client/lineinput.h>
19 #include <game/client/render.h>
20 #include <game/client/ui.h>
21 #include <game/generated/client_data.h>
23 #include "auto_map.h"
24 #include "editor.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_aName[0] = 0;
45 m_Visible = true;
46 m_SaveToMap = true;
47 m_Collapse = false;
48 m_GameGroup = false;
49 m_OffsetX = 0;
50 m_OffsetY = 0;
51 m_ParallaxX = 100;
52 m_ParallaxY = 100;
54 m_UseClipping = 0;
55 m_ClipX = 0;
56 m_ClipY = 0;
57 m_ClipW = 0;
58 m_ClipH = 0;
61 CLayerGroup::~CLayerGroup()
63 Clear();
66 void CLayerGroup::Convert(CUIRect *pRect)
68 pRect->x += m_OffsetX;
69 pRect->y += m_OffsetY;
72 void CLayerGroup::Mapping(float *pPoints)
74 m_pMap->m_pEditor->RenderTools()->MapscreenToWorld(
75 m_pMap->m_pEditor->m_WorldOffsetX, m_pMap->m_pEditor->m_WorldOffsetY,
76 m_ParallaxX/100.0f, m_ParallaxY/100.0f,
77 m_OffsetX, m_OffsetY,
78 m_pMap->m_pEditor->Graphics()->ScreenAspect(), m_pMap->m_pEditor->m_WorldZoom, pPoints);
80 pPoints[0] += m_pMap->m_pEditor->m_EditorOffsetX;
81 pPoints[1] += m_pMap->m_pEditor->m_EditorOffsetY;
82 pPoints[2] += m_pMap->m_pEditor->m_EditorOffsetX;
83 pPoints[3] += m_pMap->m_pEditor->m_EditorOffsetY;
86 void CLayerGroup::MapScreen()
88 float aPoints[4];
89 Mapping(aPoints);
90 m_pMap->m_pEditor->Graphics()->MapScreen(aPoints[0], aPoints[1], aPoints[2], aPoints[3]);
93 void CLayerGroup::Render()
95 MapScreen();
96 IGraphics *pGraphics = m_pMap->m_pEditor->Graphics();
98 if(m_UseClipping)
100 float aPoints[4];
101 m_pMap->m_pGameGroup->Mapping(aPoints);
102 float x0 = (m_ClipX - aPoints[0]) / (aPoints[2]-aPoints[0]);
103 float y0 = (m_ClipY - aPoints[1]) / (aPoints[3]-aPoints[1]);
104 float x1 = ((m_ClipX+m_ClipW) - aPoints[0]) / (aPoints[2]-aPoints[0]);
105 float y1 = ((m_ClipY+m_ClipH) - aPoints[1]) / (aPoints[3]-aPoints[1]);
107 pGraphics->ClipEnable((int)(x0*pGraphics->ScreenWidth()), (int)(y0*pGraphics->ScreenHeight()),
108 (int)((x1-x0)*pGraphics->ScreenWidth()), (int)((y1-y0)*pGraphics->ScreenHeight()));
111 for(int i = 0; i < m_lLayers.size(); i++)
113 if(m_lLayers[i]->m_Visible && m_lLayers[i] != m_pMap->m_pGameLayer)
115 if(m_pMap->m_pEditor->m_ShowDetail || !(m_lLayers[i]->m_Flags&LAYERFLAG_DETAIL))
116 m_lLayers[i]->Render();
120 pGraphics->ClipDisable();
123 void CLayerGroup::AddLayer(CLayer *l)
125 m_pMap->m_Modified = true;
126 m_lLayers.add(l);
129 void CLayerGroup::DeleteLayer(int Index)
131 if(Index < 0 || Index >= m_lLayers.size()) return;
132 delete m_lLayers[Index];
133 m_lLayers.remove_index(Index);
134 m_pMap->m_Modified = true;
137 void CLayerGroup::GetSize(float *w, float *h)
139 *w = 0; *h = 0;
140 for(int i = 0; i < m_lLayers.size(); i++)
142 float lw, lh;
143 m_lLayers[i]->GetSize(&lw, &lh);
144 *w = max(*w, lw);
145 *h = max(*h, lh);
150 int CLayerGroup::SwapLayers(int Index0, int Index1)
152 if(Index0 < 0 || Index0 >= m_lLayers.size()) return Index0;
153 if(Index1 < 0 || Index1 >= m_lLayers.size()) return Index0;
154 if(Index0 == Index1) return Index0;
155 m_pMap->m_Modified = true;
156 swap(m_lLayers[Index0], m_lLayers[Index1]);
157 return Index1;
160 void CEditorImage::AnalyseTileFlags()
162 mem_zero(m_aTileFlags, sizeof(m_aTileFlags));
164 int tw = m_Width/16; // tilesizes
165 int th = m_Height/16;
166 if ( tw == th )
168 unsigned char *pPixelData = (unsigned char *)m_pData;
170 int TileID = 0;
171 for(int ty = 0; ty < 16; ty++)
172 for(int tx = 0; tx < 16; tx++, TileID++)
174 bool Opaque = true;
175 for(int x = 0; x < tw; x++)
176 for(int y = 0; y < th; y++)
178 int p = (ty*tw+y)*m_Width + tx*tw+x;
179 if(pPixelData[p*4+3] < 250)
181 Opaque = false;
182 break;
186 if(Opaque)
187 m_aTileFlags[TileID] |= TILEFLAG_OPAQUE;
193 void CEditor::EnvelopeEval(float TimeOffset, int Env, float *pChannels, void *pUser)
195 CEditor *pThis = (CEditor *)pUser;
196 if(Env < 0 || Env >= pThis->m_Map.m_lEnvelopes.size())
198 pChannels[0] = 0;
199 pChannels[1] = 0;
200 pChannels[2] = 0;
201 pChannels[3] = 0;
202 return;
205 CEnvelope *e = pThis->m_Map.m_lEnvelopes[Env];
206 float t = pThis->m_AnimateTime+TimeOffset;
207 t *= pThis->m_AnimateSpeed;
208 e->Eval(t, pChannels);
211 /********************************************************
212 OTHER
213 *********************************************************/
215 // copied from gc_menu.cpp, should be more generalized
216 //extern int ui_do_edit_box(void *id, const CUIRect *rect, char *str, int str_size, float font_size, bool hidden=false);
218 int CEditor::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrSize, float FontSize, float *Offset, bool Hidden, int Corners)
220 int Inside = UI()->MouseInside(pRect);
221 bool ReturnValue = false;
222 bool UpdateOffset = false;
223 static int s_AtIndex = 0;
224 static bool s_DoScroll = false;
225 static float s_ScrollStart = 0.0f;
227 FontSize *= UI()->Scale();
229 if(UI()->LastActiveItem() == pID)
231 int Len = str_length(pStr);
232 if(Len == 0)
233 s_AtIndex = 0;
235 if(Inside && UI()->MouseButton(0))
237 s_DoScroll = true;
238 s_ScrollStart = UI()->MouseX();
239 int MxRel = (int)(UI()->MouseX() - pRect->x);
241 for(int i = 1; i <= Len; i++)
243 if(TextRender()->TextWidth(0, FontSize, pStr, i) - *Offset > MxRel)
245 s_AtIndex = i - 1;
246 break;
249 if(i == Len)
250 s_AtIndex = Len;
253 else if(!UI()->MouseButton(0))
254 s_DoScroll = false;
255 else if(s_DoScroll)
257 // do scrolling
258 if(UI()->MouseX() < pRect->x && s_ScrollStart-UI()->MouseX() > 10.0f)
260 s_AtIndex = max(0, s_AtIndex-1);
261 s_ScrollStart = UI()->MouseX();
262 UpdateOffset = true;
264 else if(UI()->MouseX() > pRect->x+pRect->w && UI()->MouseX()-s_ScrollStart > 10.0f)
266 s_AtIndex = min(Len, s_AtIndex+1);
267 s_ScrollStart = UI()->MouseX();
268 UpdateOffset = true;
272 for(int i = 0; i < Input()->NumEvents(); i++)
274 Len = str_length(pStr);
275 ReturnValue |= CLineInput::Manipulate(Input()->GetEvent(i), pStr, StrSize, &Len, &s_AtIndex);
279 bool JustGotActive = false;
281 if(UI()->ActiveItem() == pID)
283 if(!UI()->MouseButton(0))
285 s_AtIndex = min(s_AtIndex, str_length(pStr));
286 s_DoScroll = false;
287 UI()->SetActiveItem(0);
290 else if(UI()->HotItem() == pID)
292 if(UI()->MouseButton(0))
294 if (UI()->LastActiveItem() != pID)
295 JustGotActive = true;
296 UI()->SetActiveItem(pID);
300 if(Inside)
301 UI()->SetHotItem(pID);
303 CUIRect Textbox = *pRect;
304 RenderTools()->DrawUIRect(&Textbox, vec4(1, 1, 1, 0.5f), Corners, 3.0f);
305 Textbox.VMargin(2.0f, &Textbox);
307 const char *pDisplayStr = pStr;
308 char aStars[128];
310 if(Hidden)
312 unsigned s = str_length(pStr);
313 if(s >= sizeof(aStars))
314 s = sizeof(aStars)-1;
315 for(unsigned int i = 0; i < s; ++i)
316 aStars[i] = '*';
317 aStars[s] = 0;
318 pDisplayStr = aStars;
321 // check if the text has to be moved
322 if(UI()->LastActiveItem() == pID && !JustGotActive && (UpdateOffset || Input()->NumEvents()))
324 float w = TextRender()->TextWidth(0, FontSize, pDisplayStr, s_AtIndex);
325 if(w-*Offset > Textbox.w)
327 // move to the left
328 float wt = TextRender()->TextWidth(0, FontSize, pDisplayStr, -1);
331 *Offset += min(wt-*Offset-Textbox.w, Textbox.w/3);
333 while(w-*Offset > Textbox.w);
335 else if(w-*Offset < 0.0f)
337 // move to the right
340 *Offset = max(0.0f, *Offset-Textbox.w/3);
342 while(w-*Offset < 0.0f);
345 UI()->ClipEnable(pRect);
346 Textbox.x -= *Offset;
348 UI()->DoLabel(&Textbox, pDisplayStr, FontSize, -1);
350 // render the cursor
351 if(UI()->LastActiveItem() == pID && !JustGotActive)
353 float w = TextRender()->TextWidth(0, FontSize, pDisplayStr, s_AtIndex);
354 Textbox = *pRect;
355 Textbox.VSplitLeft(2.0f, 0, &Textbox);
356 Textbox.x += (w-*Offset-TextRender()->TextWidth(0, FontSize, "|", -1)/2);
358 if((2*time_get()/time_freq()) % 2) // make it blink
359 UI()->DoLabel(&Textbox, "|", FontSize, -1);
361 UI()->ClipDisable();
363 return ReturnValue;
366 vec4 CEditor::ButtonColorMul(const void *pID)
368 if(UI()->ActiveItem() == pID)
369 return vec4(1,1,1,0.5f);
370 else if(UI()->HotItem() == pID)
371 return vec4(1,1,1,1.5f);
372 return vec4(1,1,1,1);
375 float CEditor::UiDoScrollbarV(const void *pID, const CUIRect *pRect, float Current)
377 CUIRect Handle;
378 static float s_OffsetY;
379 pRect->HSplitTop(33, &Handle, 0);
381 Handle.y += (pRect->h-Handle.h)*Current;
383 // logic
384 float Ret = Current;
385 int Inside = UI()->MouseInside(&Handle);
387 if(UI()->ActiveItem() == pID)
389 if(!UI()->MouseButton(0))
390 UI()->SetActiveItem(0);
392 float Min = pRect->y;
393 float Max = pRect->h-Handle.h;
394 float Cur = UI()->MouseY()-s_OffsetY;
395 Ret = (Cur-Min)/Max;
396 if(Ret < 0.0f) Ret = 0.0f;
397 if(Ret > 1.0f) Ret = 1.0f;
399 else if(UI()->HotItem() == pID)
401 if(UI()->MouseButton(0))
403 UI()->SetActiveItem(pID);
404 s_OffsetY = UI()->MouseY()-Handle.y;
408 if(Inside)
409 UI()->SetHotItem(pID);
411 // render
412 CUIRect Rail;
413 pRect->VMargin(5.0f, &Rail);
414 RenderTools()->DrawUIRect(&Rail, vec4(1,1,1,0.25f), 0, 0.0f);
416 CUIRect Slider = Handle;
417 Slider.w = Rail.x-Slider.x;
418 RenderTools()->DrawUIRect(&Slider, vec4(1,1,1,0.25f), CUI::CORNER_L, 2.5f);
419 Slider.x = Rail.x+Rail.w;
420 RenderTools()->DrawUIRect(&Slider, vec4(1,1,1,0.25f), CUI::CORNER_R, 2.5f);
422 Slider = Handle;
423 Slider.Margin(5.0f, &Slider);
424 RenderTools()->DrawUIRect(&Slider, vec4(1,1,1,0.25f)*ButtonColorMul(pID), CUI::CORNER_ALL, 2.5f);
426 return Ret;
429 vec4 CEditor::GetButtonColor(const void *pID, int Checked)
431 if(Checked < 0)
432 return vec4(0,0,0,0.5f);
434 if(Checked > 0)
436 if(UI()->HotItem() == pID)
437 return vec4(1,0,0,0.75f);
438 return vec4(1,0,0,0.5f);
441 if(UI()->HotItem() == pID)
442 return vec4(1,1,1,0.75f);
443 return vec4(1,1,1,0.5f);
446 int CEditor::DoButton_Editor_Common(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip)
448 if(UI()->MouseInside(pRect))
450 if(Flags&BUTTON_CONTEXT)
451 ms_pUiGotContext = pID;
452 if(m_pTooltip)
453 m_pTooltip = pToolTip;
456 if(UI()->HotItem() == pID && pToolTip)
457 m_pTooltip = (const char *)pToolTip;
459 return UI()->DoButtonLogic(pID, pText, Checked, pRect);
461 // Draw here
462 //return UI()->DoButton(id, text, checked, r, draw_func, 0);
466 int CEditor::DoButton_Editor(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip)
468 RenderTools()->DrawUIRect(pRect, GetButtonColor(pID, Checked), CUI::CORNER_ALL, 3.0f);
469 CUIRect NewRect = *pRect;
470 NewRect.y += NewRect.h/2.0f-7.0f;
471 float tw = min(TextRender()->TextWidth(0, 10.0f, pText, -1), NewRect.w);
472 CTextCursor Cursor;
473 TextRender()->SetCursor(&Cursor, NewRect.x + NewRect.w/2-tw/2, NewRect.y - 1.0f, 10.0f, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END);
474 Cursor.m_LineWidth = NewRect.w;
475 TextRender()->TextEx(&Cursor, pText, -1);
476 return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip);
479 int CEditor::DoButton_File(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip)
481 if(Checked)
482 RenderTools()->DrawUIRect(pRect, GetButtonColor(pID, Checked), CUI::CORNER_ALL, 3.0f);
484 CUIRect t = *pRect;
485 t.VMargin(5.0f, &t);
486 UI()->DoLabel(&t, pText, 10, -1, -1);
487 return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip);
490 int CEditor::DoButton_Menu(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip)
492 CUIRect r = *pRect;
493 RenderTools()->DrawUIRect(&r, vec4(0.5f, 0.5f, 0.5f, 1.0f), CUI::CORNER_T, 3.0f);
495 r = *pRect;
496 r.VMargin(5.0f, &r);
497 UI()->DoLabel(&r, pText, 10, -1, -1);
498 return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip);
501 int CEditor::DoButton_MenuItem(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip)
503 if(UI()->HotItem() == pID || Checked)
504 RenderTools()->DrawUIRect(pRect, GetButtonColor(pID, Checked), CUI::CORNER_ALL, 3.0f);
506 CUIRect t = *pRect;
507 t.VMargin(5.0f, &t);
508 CTextCursor Cursor;
509 TextRender()->SetCursor(&Cursor, t.x, t.y - 1.0f, 10.0f, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END);
510 Cursor.m_LineWidth = t.w;
511 TextRender()->TextEx(&Cursor, pText, -1);
512 return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip);
515 int CEditor::DoButton_Tab(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip)
517 RenderTools()->DrawUIRect(pRect, GetButtonColor(pID, Checked), CUI::CORNER_T, 5.0f);
518 CUIRect NewRect = *pRect;
519 NewRect.y += NewRect.h/2.0f-7.0f;
520 UI()->DoLabel(&NewRect, pText, 10, 0, -1);
521 return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip);
524 int CEditor::DoButton_Ex(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip, int Corners, float FontSize)
526 RenderTools()->DrawUIRect(pRect, GetButtonColor(pID, Checked), Corners, 3.0f);
527 CUIRect NewRect = *pRect;
528 NewRect.HMargin(NewRect.h/2.0f-FontSize/2.0f-1.0f, &NewRect);
529 UI()->DoLabel(&NewRect, pText, FontSize, 0, -1);
530 return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip);
533 int CEditor::DoButton_ButtonInc(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip)
535 RenderTools()->DrawUIRect(pRect, GetButtonColor(pID, Checked), CUI::CORNER_R, 3.0f);
536 UI()->DoLabel(pRect, pText?pText:"+", 10, 0, -1);
537 return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip);
540 int CEditor::DoButton_ButtonDec(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip)
542 RenderTools()->DrawUIRect(pRect, GetButtonColor(pID, Checked), CUI::CORNER_L, 3.0f);
543 UI()->DoLabel(pRect, pText?pText:"-", 10, 0, -1);
544 return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip);
547 void CEditor::RenderGrid(CLayerGroup *pGroup)
549 if(!m_GridActive)
550 return;
552 float aGroupPoints[4];
553 pGroup->Mapping(aGroupPoints);
555 float w = UI()->Screen()->w;
556 float h = UI()->Screen()->h;
558 int LineDistance = GetLineDistance();
560 int XOffset = aGroupPoints[0]/LineDistance;
561 int YOffset = aGroupPoints[1]/LineDistance;
562 int XGridOffset = XOffset % m_GridFactor;
563 int YGridOffset = YOffset % m_GridFactor;
565 Graphics()->TextureSet(-1);
566 Graphics()->LinesBegin();
568 for(int i = 0; i < (int)w; i++)
570 if((i+YGridOffset) % m_GridFactor == 0)
571 Graphics()->SetColor(1.0f, 0.3f, 0.3f, 0.3f);
572 else
573 Graphics()->SetColor(1.0f, 1.0f, 1.0f, 0.15f);
575 IGraphics::CLineItem Line = IGraphics::CLineItem(LineDistance*XOffset, LineDistance*i+LineDistance*YOffset, w+aGroupPoints[2], LineDistance*i+LineDistance*YOffset);
576 Graphics()->LinesDraw(&Line, 1);
578 if((i+XGridOffset) % m_GridFactor == 0)
579 Graphics()->SetColor(1.0f, 0.3f, 0.3f, 0.3f);
580 else
581 Graphics()->SetColor(1.0f, 1.0f, 1.0f, 0.15f);
583 Line = IGraphics::CLineItem(LineDistance*i+LineDistance*XOffset, LineDistance*YOffset, LineDistance*i+LineDistance*XOffset, h+aGroupPoints[3]);
584 Graphics()->LinesDraw(&Line, 1);
586 Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f);
587 Graphics()->LinesEnd();
590 void CEditor::RenderBackground(CUIRect View, int Texture, float Size, float Brightness)
592 Graphics()->TextureSet(Texture);
593 Graphics()->BlendNormal();
594 Graphics()->QuadsBegin();
595 Graphics()->SetColor(Brightness, Brightness, Brightness, 1.0f);
596 Graphics()->QuadsSetSubset(0,0, View.w/Size, View.h/Size);
597 IGraphics::CQuadItem QuadItem(View.x, View.y, View.w, View.h);
598 Graphics()->QuadsDrawTL(&QuadItem, 1);
599 Graphics()->QuadsEnd();
602 int CEditor::UiDoValueSelector(void *pID, CUIRect *pRect, const char *pLabel, int Current, int Min, int Max, int Step, float Scale, const char *pToolTip)
604 // logic
605 static float s_Value;
606 int Inside = UI()->MouseInside(pRect);
608 if(UI()->ActiveItem() == pID)
610 if(!UI()->MouseButton(0))
612 m_LockMouse = false;
613 UI()->SetActiveItem(0);
615 else
617 if(Input()->KeyPressed(KEY_LSHIFT) || Input()->KeyPressed(KEY_RSHIFT))
618 s_Value += m_MouseDeltaX*0.05f;
619 else
620 s_Value += m_MouseDeltaX;
622 if(absolute(s_Value) > Scale)
624 int Count = (int)(s_Value/Scale);
625 s_Value = fmod(s_Value, Scale);
626 Current += Step*Count;
627 if(Current < Min)
628 Current = Min;
629 if(Current > Max)
630 Current = Max;
633 if(pToolTip)
634 m_pTooltip = pToolTip;
636 else if(UI()->HotItem() == pID)
638 if(UI()->MouseButton(0))
640 m_LockMouse = true;
641 s_Value = 0;
642 UI()->SetActiveItem(pID);
644 if(pToolTip)
645 m_pTooltip = pToolTip;
648 if(Inside)
649 UI()->SetHotItem(pID);
651 // render
652 char aBuf[128];
653 str_format(aBuf, sizeof(aBuf),"%s %d", pLabel, Current);
654 RenderTools()->DrawUIRect(pRect, GetButtonColor(pID, 0), CUI::CORNER_ALL, 5.0f);
655 pRect->y += pRect->h/2.0f-7.0f;
656 UI()->DoLabel(pRect, aBuf, 10, 0, -1);
658 return Current;
661 CLayerGroup *CEditor::GetSelectedGroup()
663 if(m_SelectedGroup >= 0 && m_SelectedGroup < m_Map.m_lGroups.size())
664 return m_Map.m_lGroups[m_SelectedGroup];
665 return 0x0;
668 CLayer *CEditor::GetSelectedLayer(int Index)
670 CLayerGroup *pGroup = GetSelectedGroup();
671 if(!pGroup)
672 return 0x0;
674 if(m_SelectedLayer >= 0 && m_SelectedLayer < m_Map.m_lGroups[m_SelectedGroup]->m_lLayers.size())
675 return pGroup->m_lLayers[m_SelectedLayer];
676 return 0x0;
679 CLayer *CEditor::GetSelectedLayerType(int Index, int Type)
681 CLayer *p = GetSelectedLayer(Index);
682 if(p && p->m_Type == Type)
683 return p;
684 return 0x0;
687 CQuad *CEditor::GetSelectedQuad()
689 CLayerQuads *ql = (CLayerQuads *)GetSelectedLayerType(0, LAYERTYPE_QUADS);
690 if(!ql)
691 return 0;
692 if(m_SelectedQuad >= 0 && m_SelectedQuad < ql->m_lQuads.size())
693 return &ql->m_lQuads[m_SelectedQuad];
694 return 0;
697 void CEditor::CallbackOpenMap(const char *pFileName, int StorageType, void *pUser)
699 CEditor *pEditor = (CEditor*)pUser;
700 if(pEditor->Load(pFileName, StorageType))
702 str_copy(pEditor->m_aFileName, pFileName, 512);
703 pEditor->m_ValidSaveFilename = StorageType == IStorage::TYPE_SAVE && pEditor->m_pFileDialogPath == pEditor->m_aFileDialogCurrentFolder;
704 pEditor->SortImages();
705 pEditor->m_Dialog = DIALOG_NONE;
706 pEditor->m_Map.m_Modified = false;
709 void CEditor::CallbackAppendMap(const char *pFileName, int StorageType, void *pUser)
711 CEditor *pEditor = (CEditor*)pUser;
712 if(pEditor->Append(pFileName, StorageType))
713 pEditor->m_aFileName[0] = 0;
714 else
715 pEditor->SortImages();
717 pEditor->m_Dialog = DIALOG_NONE;
719 void CEditor::CallbackSaveMap(const char *pFileName, int StorageType, void *pUser)
721 CEditor *pEditor = static_cast<CEditor*>(pUser);
722 char aBuf[1024];
723 const int Length = str_length(pFileName);
724 // add map extension
725 if(Length <= 4 || pFileName[Length-4] != '.' || str_comp_nocase(pFileName+Length-3, "map"))
727 str_format(aBuf, sizeof(aBuf), "%s.map", pFileName);
728 pFileName = aBuf;
731 if(pEditor->Save(pFileName))
733 str_copy(pEditor->m_aFileName, pFileName, sizeof(pEditor->m_aFileName));
734 pEditor->m_ValidSaveFilename = StorageType == IStorage::TYPE_SAVE && pEditor->m_pFileDialogPath == pEditor->m_aFileDialogCurrentFolder;
735 pEditor->m_Map.m_Modified = false;
738 pEditor->m_Dialog = DIALOG_NONE;
741 void CEditor::DoToolbar(CUIRect ToolBar)
743 CUIRect TB_Top, TB_Bottom;
744 CUIRect Button;
746 ToolBar.HSplitTop(ToolBar.h/2.0f, &TB_Top, &TB_Bottom);
748 TB_Top.HSplitBottom(2.5f, &TB_Top, 0);
749 TB_Bottom.HSplitTop(2.5f, 0, &TB_Bottom);
751 // ctrl+o to open
752 if(Input()->KeyDown('o') && (Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL)) && m_Dialog == DIALOG_NONE)
754 if(HasUnsavedData())
756 if(!m_PopupEventWasActivated)
758 m_PopupEventType = POPEVENT_LOAD;
759 m_PopupEventActivated = true;
762 else
763 InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_MAP, "Load map", "Load", "maps", "", CallbackOpenMap, this);
766 // ctrl+s to save
767 if(Input()->KeyDown('s') && (Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL)) && m_Dialog == DIALOG_NONE)
769 if(m_aFileName[0] && m_ValidSaveFilename)
771 if(!m_PopupEventWasActivated)
773 str_copy(m_aFileSaveName, m_aFileName, sizeof(m_aFileSaveName));
774 m_PopupEventType = POPEVENT_SAVE;
775 m_PopupEventActivated = true;
778 else
779 InvokeFileDialog(IStorage::TYPE_SAVE, FILETYPE_MAP, "Save map", "Save", "maps", "", CallbackSaveMap, this);
782 // detail button
783 TB_Top.VSplitLeft(30.0f, &Button, &TB_Top);
784 static int s_HqButton = 0;
785 if(DoButton_Editor(&s_HqButton, "HD", m_ShowDetail, &Button, 0, "[ctrl+h] Toggle High Detail") ||
786 (Input()->KeyDown('h') && (Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL))))
788 m_ShowDetail = !m_ShowDetail;
791 TB_Top.VSplitLeft(5.0f, 0, &TB_Top);
793 // animation button
794 TB_Top.VSplitLeft(40.0f, &Button, &TB_Top);
795 static int s_AnimateButton = 0;
796 if(DoButton_Editor(&s_AnimateButton, "Anim", m_Animate, &Button, 0, "[ctrl+m] Toggle animation") ||
797 (Input()->KeyDown('m') && (Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL))))
799 m_AnimateStart = time_get();
800 m_Animate = !m_Animate;
803 TB_Top.VSplitLeft(5.0f, 0, &TB_Top);
805 // proof button
806 TB_Top.VSplitLeft(40.0f, &Button, &TB_Top);
807 static int s_ProofButton = 0;
808 if(DoButton_Editor(&s_ProofButton, "Proof", m_ProofBorders, &Button, 0, "[ctrl+p] Toggles proof borders. These borders represent what a player maximum can see.") ||
809 (Input()->KeyDown('p') && (Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL))))
811 m_ProofBorders = !m_ProofBorders;
814 TB_Top.VSplitLeft(5.0f, 0, &TB_Top);
816 // tile info button
817 TB_Top.VSplitLeft(40.0f, &Button, &TB_Top);
818 static int s_TileInfoButton = 0;
819 if(DoButton_Editor(&s_TileInfoButton, "Info", m_ShowTileInfo, &Button, 0, "[ctrl+i] Show tile informations") ||
820 (Input()->KeyDown('i') && (Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL))))
822 m_ShowTileInfo = !m_ShowTileInfo;
823 m_ShowEnvelopePreview = 0;
826 TB_Top.VSplitLeft(15.0f, 0, &TB_Top);
828 // zoom group
829 TB_Top.VSplitLeft(30.0f, &Button, &TB_Top);
830 static int s_ZoomOutButton = 0;
831 if(DoButton_Ex(&s_ZoomOutButton, "ZO", 0, &Button, 0, "[NumPad-] Zoom out", CUI::CORNER_L) || Input()->KeyDown(KEY_KP_MINUS))
832 m_ZoomLevel += 50;
834 TB_Top.VSplitLeft(30.0f, &Button, &TB_Top);
835 static int s_ZoomNormalButton = 0;
836 if(DoButton_Ex(&s_ZoomNormalButton, "1:1", 0, &Button, 0, "[NumPad*] Zoom to normal and remove editor offset", 0) || Input()->KeyDown(KEY_KP_MULTIPLY))
838 m_EditorOffsetX = 0;
839 m_EditorOffsetY = 0;
840 m_ZoomLevel = 100;
843 TB_Top.VSplitLeft(30.0f, &Button, &TB_Top);
844 static int s_ZoomInButton = 0;
845 if(DoButton_Ex(&s_ZoomInButton, "ZI", 0, &Button, 0, "[NumPad+] Zoom in", CUI::CORNER_R) || Input()->KeyDown(KEY_KP_PLUS))
846 m_ZoomLevel -= 50;
848 TB_Top.VSplitLeft(10.0f, 0, &TB_Top);
850 // animation speed
851 TB_Top.VSplitLeft(30.0f, &Button, &TB_Top);
852 static int s_AnimFasterButton = 0;
853 if(DoButton_Ex(&s_AnimFasterButton, "A+", 0, &Button, 0, "Increase animation speed", CUI::CORNER_L))
854 m_AnimateSpeed += 0.5f;
856 TB_Top.VSplitLeft(30.0f, &Button, &TB_Top);
857 static int s_AnimNormalButton = 0;
858 if(DoButton_Ex(&s_AnimNormalButton, "1", 0, &Button, 0, "Normal animation speed", 0))
859 m_AnimateSpeed = 1.0f;
861 TB_Top.VSplitLeft(30.0f, &Button, &TB_Top);
862 static int s_AnimSlowerButton = 0;
863 if(DoButton_Ex(&s_AnimSlowerButton, "A-", 0, &Button, 0, "Decrease animation speed", CUI::CORNER_R))
865 if(m_AnimateSpeed > 0.5f)
866 m_AnimateSpeed -= 0.5f;
869 m_WorldZoom = m_ZoomLevel/100.0f;
871 TB_Top.VSplitLeft(10.0f, &Button, &TB_Top);
874 // brush manipulation
876 int Enabled = m_Brush.IsEmpty()?-1:0;
878 // flip buttons
879 TB_Top.VSplitLeft(30.0f, &Button, &TB_Top);
880 static int s_FlipXButton = 0;
881 if(DoButton_Ex(&s_FlipXButton, "X/X", Enabled, &Button, 0, "[N] Flip brush horizontal", CUI::CORNER_L) || Input()->KeyDown('n'))
883 for(int i = 0; i < m_Brush.m_lLayers.size(); i++)
884 m_Brush.m_lLayers[i]->BrushFlipX();
887 TB_Top.VSplitLeft(30.0f, &Button, &TB_Top);
888 static int s_FlipyButton = 0;
889 if(DoButton_Ex(&s_FlipyButton, "Y/Y", Enabled, &Button, 0, "[M] Flip brush vertical", CUI::CORNER_R) || Input()->KeyDown('m'))
891 for(int i = 0; i < m_Brush.m_lLayers.size(); i++)
892 m_Brush.m_lLayers[i]->BrushFlipY();
895 // rotate buttons
896 TB_Top.VSplitLeft(15.0f, &Button, &TB_Top);
898 TB_Top.VSplitLeft(30.0f, &Button, &TB_Top);
899 static int s_RotationAmount = 90;
900 bool TileLayer = false;
901 // check for tile layers in brush selection
902 for(int i = 0; i < m_Brush.m_lLayers.size(); i++)
903 if(m_Brush.m_lLayers[i]->m_Type == LAYERTYPE_TILES)
905 TileLayer = true;
906 s_RotationAmount = max(90, (s_RotationAmount/90)*90);
907 break;
909 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.");
911 TB_Top.VSplitLeft(5.0f, &Button, &TB_Top);
912 TB_Top.VSplitLeft(30.0f, &Button, &TB_Top);
913 static int s_CcwButton = 0;
914 if(DoButton_Ex(&s_CcwButton, "CCW", Enabled, &Button, 0, "[R] Rotates the brush counter clockwise", CUI::CORNER_L) || Input()->KeyDown('r'))
916 for(int i = 0; i < m_Brush.m_lLayers.size(); i++)
917 m_Brush.m_lLayers[i]->BrushRotate(-s_RotationAmount/360.0f*pi*2);
920 TB_Top.VSplitLeft(30.0f, &Button, &TB_Top);
921 static int s_CwButton = 0;
922 if(DoButton_Ex(&s_CwButton, "CW", Enabled, &Button, 0, "[T] Rotates the brush clockwise", CUI::CORNER_R) || Input()->KeyDown('t'))
924 for(int i = 0; i < m_Brush.m_lLayers.size(); i++)
925 m_Brush.m_lLayers[i]->BrushRotate(s_RotationAmount/360.0f*pi*2);
929 // quad manipulation
931 // do add button
932 TB_Top.VSplitLeft(10.0f, &Button, &TB_Top);
933 TB_Top.VSplitLeft(60.0f, &Button, &TB_Top);
934 static int s_NewButton = 0;
936 CLayerQuads *pQLayer = (CLayerQuads *)GetSelectedLayerType(0, LAYERTYPE_QUADS);
937 //CLayerTiles *tlayer = (CLayerTiles *)get_selected_layer_type(0, LAYERTYPE_TILES);
938 if(DoButton_Editor(&s_NewButton, "Add Quad", pQLayer?0:-1, &Button, 0, "Adds a new quad"))
940 if(pQLayer)
942 float Mapping[4];
943 CLayerGroup *g = GetSelectedGroup();
944 g->Mapping(Mapping);
945 int AddX = f2fx(Mapping[0] + (Mapping[2]-Mapping[0])/2);
946 int AddY = f2fx(Mapping[1] + (Mapping[3]-Mapping[1])/2);
948 CQuad *q = pQLayer->NewQuad();
949 for(int i = 0; i < 5; i++)
951 q->m_aPoints[i].x += AddX;
952 q->m_aPoints[i].y += AddY;
958 // tile manipulation
960 TB_Bottom.VSplitLeft(40.0f, &Button, &TB_Bottom);
961 static int s_BorderBut = 0;
962 CLayerTiles *pT = (CLayerTiles *)GetSelectedLayerType(0, LAYERTYPE_TILES);
964 if(DoButton_Editor(&s_BorderBut, "Border", pT?0:-1, &Button, 0, "Adds border tiles"))
966 if(pT)
967 DoMapBorder();
971 TB_Bottom.VSplitLeft(5.0f, 0, &TB_Bottom);
973 // refocus button
974 TB_Bottom.VSplitLeft(50.0f, &Button, &TB_Bottom);
975 static int s_RefocusButton = 0;
976 if(DoButton_Editor(&s_RefocusButton, "Refocus", m_WorldOffsetX&&m_WorldOffsetY?0:-1, &Button, 0, "[HOME] Restore map focus") || Input()->KeyDown(KEY_HOME))
978 m_WorldOffsetX = 0;
979 m_WorldOffsetY = 0;
982 TB_Bottom.VSplitLeft(5.0f, 0, &TB_Bottom);
984 // grid button
985 TB_Bottom.VSplitLeft(50.0f, &Button, &TB_Bottom);
986 static int s_GridButton = 0;
987 if(DoButton_Editor(&s_GridButton, "Grid", m_GridActive, &Button, 0, "Toggle Grid"))
989 m_GridActive = !m_GridActive;
992 TB_Bottom.VSplitLeft(30.0f, 0, &TB_Bottom);
994 // grid zoom
995 TB_Bottom.VSplitLeft(30.0f, &Button, &TB_Bottom);
996 static int s_GridIncreaseButton = 0;
997 if(DoButton_Ex(&s_GridIncreaseButton, "G-", 0, &Button, 0, "Decrease grid", CUI::CORNER_L))
999 if(m_GridFactor > 1)
1000 m_GridFactor--;
1003 TB_Bottom.VSplitLeft(30.0f, &Button, &TB_Bottom);
1004 static int s_GridNormalButton = 0;
1005 if(DoButton_Ex(&s_GridNormalButton, "1", 0, &Button, 0, "Normal grid", 0))
1006 m_GridFactor = 1;
1008 TB_Bottom.VSplitLeft(30.0f, &Button, &TB_Bottom);
1010 static int s_GridDecreaseButton = 0;
1011 if(DoButton_Ex(&s_GridDecreaseButton, "G+", 0, &Button, 0, "Increase grid", CUI::CORNER_R))
1013 if(m_GridFactor < 15)
1014 m_GridFactor++;
1018 static void Rotate(CPoint *pCenter, CPoint *pPoint, float Rotation)
1020 int x = pPoint->x - pCenter->x;
1021 int y = pPoint->y - pCenter->y;
1022 pPoint->x = (int)(x * cosf(Rotation) - y * sinf(Rotation) + pCenter->x);
1023 pPoint->y = (int)(x * sinf(Rotation) + y * cosf(Rotation) + pCenter->y);
1026 void CEditor::DoQuad(CQuad *q, int Index)
1028 enum
1030 OP_NONE=0,
1031 OP_MOVE_ALL,
1032 OP_MOVE_PIVOT,
1033 OP_ROTATE,
1034 OP_CONTEXT_MENU,
1037 // some basic values
1038 void *pID = &q->m_aPoints[4]; // use pivot addr as id
1039 static CPoint s_RotatePoints[4];
1040 static float s_LastWx;
1041 static float s_LastWy;
1042 static int s_Operation = OP_NONE;
1043 static float s_RotateAngle = 0;
1044 float wx = UI()->MouseWorldX();
1045 float wy = UI()->MouseWorldY();
1047 // get pivot
1048 float CenterX = fx2f(q->m_aPoints[4].x);
1049 float CenterY = fx2f(q->m_aPoints[4].y);
1051 float dx = (CenterX - wx)/m_WorldZoom;
1052 float dy = (CenterY - wy)/m_WorldZoom;
1053 if(dx*dx+dy*dy < 50)
1054 UI()->SetHotItem(pID);
1056 // draw selection background
1057 if(m_SelectedQuad == Index)
1059 Graphics()->SetColor(0,0,0,1);
1060 IGraphics::CQuadItem QuadItem(CenterX, CenterY, 7.0f, 7.0f);
1061 Graphics()->QuadsDraw(&QuadItem, 1);
1064 if(UI()->ActiveItem() == pID)
1066 if(m_MouseDeltaWx*m_MouseDeltaWx+m_MouseDeltaWy*m_MouseDeltaWy > 0.5f)
1068 // check if we only should move pivot
1069 if(s_Operation == OP_MOVE_PIVOT)
1071 if(m_GridActive)
1073 int LineDistance = GetLineDistance();
1075 float x = 0.0f;
1076 float y = 0.0f;
1077 if(wx >= 0)
1078 x = (int)((wx+(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
1079 else
1080 x = (int)((wx-(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
1081 if(wy >= 0)
1082 y = (int)((wy+(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
1083 else
1084 y = (int)((wy-(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
1086 q->m_aPoints[4].x = f2fx(x);
1087 q->m_aPoints[4].y = f2fx(y);
1089 else
1091 q->m_aPoints[4].x += f2fx(wx-s_LastWx);
1092 q->m_aPoints[4].y += f2fx(wy-s_LastWy);
1095 else if(s_Operation == OP_MOVE_ALL)
1097 // move all points including pivot
1098 if(m_GridActive)
1100 int LineDistance = GetLineDistance();
1102 float x = 0.0f;
1103 float y = 0.0f;
1104 if(wx >= 0)
1105 x = (int)((wx+(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
1106 else
1107 x = (int)((wx-(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
1108 if(wy >= 0)
1109 y = (int)((wy+(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
1110 else
1111 y = (int)((wy-(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
1113 int OldX = q->m_aPoints[4].x;
1114 int OldY = q->m_aPoints[4].y;
1115 q->m_aPoints[4].x = f2fx(x);
1116 q->m_aPoints[4].y = f2fx(y);
1117 int DiffX = q->m_aPoints[4].x - OldX;
1118 int DiffY = q->m_aPoints[4].y - OldY;
1120 for(int v = 0; v < 4; v++)
1122 q->m_aPoints[v].x += DiffX;
1123 q->m_aPoints[v].y += DiffY;
1126 else
1128 for(int v = 0; v < 5; v++)
1130 q->m_aPoints[v].x += f2fx(wx-s_LastWx);
1131 q->m_aPoints[v].y += f2fx(wy-s_LastWy);
1135 else if(s_Operation == OP_ROTATE)
1137 for(int v = 0; v < 4; v++)
1139 q->m_aPoints[v] = s_RotatePoints[v];
1140 Rotate(&q->m_aPoints[4], &q->m_aPoints[v], s_RotateAngle);
1145 s_RotateAngle += (m_MouseDeltaX) * 0.002f;
1146 s_LastWx = wx;
1147 s_LastWy = wy;
1149 if(s_Operation == OP_CONTEXT_MENU)
1151 if(!UI()->MouseButton(1))
1153 static int s_QuadPopupID = 0;
1154 UiInvokePopupMenu(&s_QuadPopupID, 0, UI()->MouseX(), UI()->MouseY(), 120, 180, PopupQuad);
1155 m_LockMouse = false;
1156 s_Operation = OP_NONE;
1157 UI()->SetActiveItem(0);
1160 else
1162 if(!UI()->MouseButton(0))
1164 m_LockMouse = false;
1165 s_Operation = OP_NONE;
1166 UI()->SetActiveItem(0);
1170 Graphics()->SetColor(1,1,1,1);
1172 else if(UI()->HotItem() == pID)
1174 ms_pUiGotContext = pID;
1176 Graphics()->SetColor(1,1,1,1);
1177 m_pTooltip = "Left mouse button to move. Hold shift to move pivot. Hold ctrl to rotate.";
1179 if(UI()->MouseButton(0))
1181 if(Input()->KeyPressed(KEY_LSHIFT) || Input()->KeyPressed(KEY_RSHIFT))
1182 s_Operation = OP_MOVE_PIVOT;
1183 else if(Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL))
1185 m_LockMouse = true;
1186 s_Operation = OP_ROTATE;
1187 s_RotateAngle = 0;
1188 s_RotatePoints[0] = q->m_aPoints[0];
1189 s_RotatePoints[1] = q->m_aPoints[1];
1190 s_RotatePoints[2] = q->m_aPoints[2];
1191 s_RotatePoints[3] = q->m_aPoints[3];
1193 else
1194 s_Operation = OP_MOVE_ALL;
1196 UI()->SetActiveItem(pID);
1197 if(m_SelectedQuad != Index)
1198 m_SelectedPoints = 0;
1199 m_SelectedQuad = Index;
1200 s_LastWx = wx;
1201 s_LastWy = wy;
1204 if(UI()->MouseButton(1))
1206 if(m_SelectedQuad != Index)
1207 m_SelectedPoints = 0;
1208 m_SelectedQuad = Index;
1209 s_Operation = OP_CONTEXT_MENU;
1210 UI()->SetActiveItem(pID);
1213 else
1214 Graphics()->SetColor(0,1,0,1);
1216 IGraphics::CQuadItem QuadItem(CenterX, CenterY, 5.0f*m_WorldZoom, 5.0f*m_WorldZoom);
1217 Graphics()->QuadsDraw(&QuadItem, 1);
1220 void CEditor::DoQuadPoint(CQuad *pQuad, int QuadIndex, int V)
1222 void *pID = &pQuad->m_aPoints[V];
1224 float wx = UI()->MouseWorldX();
1225 float wy = UI()->MouseWorldY();
1227 float px = fx2f(pQuad->m_aPoints[V].x);
1228 float py = fx2f(pQuad->m_aPoints[V].y);
1230 float dx = (px - wx)/m_WorldZoom;
1231 float dy = (py - wy)/m_WorldZoom;
1232 if(dx*dx+dy*dy < 50)
1233 UI()->SetHotItem(pID);
1235 // draw selection background
1236 if(m_SelectedQuad == QuadIndex && m_SelectedPoints&(1<<V))
1238 Graphics()->SetColor(0,0,0,1);
1239 IGraphics::CQuadItem QuadItem(px, py, 7.0f, 7.0f);
1240 Graphics()->QuadsDraw(&QuadItem, 1);
1243 enum
1245 OP_NONE=0,
1246 OP_MOVEPOINT,
1247 OP_MOVEUV,
1248 OP_CONTEXT_MENU
1251 static bool s_Moved;
1252 static int s_Operation = OP_NONE;
1254 if(UI()->ActiveItem() == pID)
1256 float dx = m_MouseDeltaWx;
1257 float dy = m_MouseDeltaWy;
1258 if(!s_Moved)
1260 if(dx*dx+dy*dy > 0.5f)
1261 s_Moved = true;
1264 if(s_Moved)
1266 if(s_Operation == OP_MOVEPOINT)
1268 if(m_GridActive)
1270 for(int m = 0; m < 4; m++)
1271 if(m_SelectedPoints&(1<<m))
1273 int LineDistance = GetLineDistance();
1275 float x = 0.0f;
1276 float y = 0.0f;
1277 if(wx >= 0)
1278 x = (int)((wx+(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
1279 else
1280 x = (int)((wx-(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
1281 if(wy >= 0)
1282 y = (int)((wy+(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
1283 else
1284 y = (int)((wy-(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
1286 pQuad->m_aPoints[m].x = f2fx(x);
1287 pQuad->m_aPoints[m].y = f2fx(y);
1290 else
1292 for(int m = 0; m < 4; m++)
1293 if(m_SelectedPoints&(1<<m))
1295 pQuad->m_aPoints[m].x += f2fx(dx);
1296 pQuad->m_aPoints[m].y += f2fx(dy);
1300 else if(s_Operation == OP_MOVEUV)
1302 for(int m = 0; m < 4; m++)
1303 if(m_SelectedPoints&(1<<m))
1305 // 0,2;1,3 - line x
1306 // 0,1;2,3 - line y
1308 pQuad->m_aTexcoords[m].x += f2fx(dx*0.001f);
1309 pQuad->m_aTexcoords[(m+2)%4].x += f2fx(dx*0.001f);
1311 pQuad->m_aTexcoords[m].y += f2fx(dy*0.001f);
1312 pQuad->m_aTexcoords[m^1].y += f2fx(dy*0.001f);
1317 if(s_Operation == OP_CONTEXT_MENU)
1319 if(!UI()->MouseButton(1))
1321 static int s_PointPopupID = 0;
1322 UiInvokePopupMenu(&s_PointPopupID, 0, UI()->MouseX(), UI()->MouseY(), 120, 150, PopupPoint);
1323 UI()->SetActiveItem(0);
1326 else
1328 if(!UI()->MouseButton(0))
1330 if(!s_Moved)
1332 if(Input()->KeyPressed(KEY_LSHIFT) || Input()->KeyPressed(KEY_RSHIFT))
1333 m_SelectedPoints ^= 1<<V;
1334 else
1335 m_SelectedPoints = 1<<V;
1337 m_LockMouse = false;
1338 UI()->SetActiveItem(0);
1342 Graphics()->SetColor(1,1,1,1);
1344 else if(UI()->HotItem() == pID)
1346 ms_pUiGotContext = pID;
1348 Graphics()->SetColor(1,1,1,1);
1349 m_pTooltip = "Left mouse button to move. Hold shift to move the texture.";
1351 if(UI()->MouseButton(0))
1353 UI()->SetActiveItem(pID);
1354 s_Moved = false;
1355 if(Input()->KeyPressed(KEY_LSHIFT) || Input()->KeyPressed(KEY_RSHIFT))
1357 s_Operation = OP_MOVEUV;
1358 m_LockMouse = true;
1360 else
1361 s_Operation = OP_MOVEPOINT;
1363 if(!(m_SelectedPoints&(1<<V)))
1365 if(Input()->KeyPressed(KEY_LSHIFT) || Input()->KeyPressed(KEY_RSHIFT))
1366 m_SelectedPoints |= 1<<V;
1367 else
1368 m_SelectedPoints = 1<<V;
1371 m_SelectedQuad = QuadIndex;
1373 else if(UI()->MouseButton(1))
1375 s_Operation = OP_CONTEXT_MENU;
1376 m_SelectedQuad = QuadIndex;
1377 UI()->SetActiveItem(pID);
1378 if(!(m_SelectedPoints&(1<<V)))
1380 if(Input()->KeyPressed(KEY_LSHIFT) || Input()->KeyPressed(KEY_RSHIFT))
1381 m_SelectedPoints |= 1<<V;
1382 else
1383 m_SelectedPoints = 1<<V;
1384 s_Moved = true;
1388 else
1389 Graphics()->SetColor(1,0,0,1);
1391 IGraphics::CQuadItem QuadItem(px, py, 5.0f*m_WorldZoom, 5.0f*m_WorldZoom);
1392 Graphics()->QuadsDraw(&QuadItem, 1);
1395 void CEditor::DoQuadEnvelopes(CQuad *pQuad, int Index, int TexID)
1397 CEnvelope *pEnvelope = 0x0;
1398 if(pQuad->m_PosEnv >= 0 && pQuad->m_PosEnv < m_Map.m_lEnvelopes.size())
1399 pEnvelope = m_Map.m_lEnvelopes[pQuad->m_PosEnv];
1400 if (!pEnvelope)
1401 return;
1403 //QuadParams
1404 CPoint *pPoints = pQuad->m_aPoints;
1406 //Draw Lines
1407 Graphics()->TextureSet(-1);
1408 Graphics()->LinesBegin();
1409 Graphics()->SetColor(80.0f/255, 150.0f/255, 230.f/255, 0.5f);
1410 for(int i = 0; i < pEnvelope->m_lPoints.size()-1; i++)
1412 float OffsetX = fx2f(pEnvelope->m_lPoints[i].m_aValues[0]);
1413 float OffsetY = fx2f(pEnvelope->m_lPoints[i].m_aValues[1]);
1414 vec2 Pos0 = vec2(fx2f(pPoints[4].x)+OffsetX, fx2f(pPoints[4].y)+OffsetY);
1416 OffsetX = fx2f(pEnvelope->m_lPoints[i+1].m_aValues[0]);
1417 OffsetY = fx2f(pEnvelope->m_lPoints[i+1].m_aValues[1]);
1418 vec2 Pos1 = vec2(fx2f(pPoints[4].x)+OffsetX, fx2f(pPoints[4].y)+OffsetY);
1420 IGraphics::CLineItem Line = IGraphics::CLineItem(Pos0.x, Pos0.y, Pos1.x, Pos1.y);
1421 Graphics()->LinesDraw(&Line, 1);
1423 Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f);
1424 Graphics()->LinesEnd();
1426 //Draw Quads
1427 for(int i = 0; i < pEnvelope->m_lPoints.size(); i++)
1429 Graphics()->TextureSet(TexID);
1430 Graphics()->QuadsBegin();
1432 //Calc Env Position
1433 float OffsetX = fx2f(pEnvelope->m_lPoints[i].m_aValues[0]);
1434 float OffsetY = fx2f(pEnvelope->m_lPoints[i].m_aValues[1]);
1435 float Rot = fx2f(pEnvelope->m_lPoints[i].m_aValues[2])/360.0f*pi*2;
1437 //Set Colours
1438 float Alpha = (m_SelectedQuadEnvelope == pQuad->m_PosEnv && m_SelectedEnvelopePoint == i) ? 0.65f : 0.35f;
1439 IGraphics::CColorVertex aArray[4] = {
1440 IGraphics::CColorVertex(0, pQuad->m_aColors[0].r, pQuad->m_aColors[0].g, pQuad->m_aColors[0].b, Alpha),
1441 IGraphics::CColorVertex(1, pQuad->m_aColors[1].r, pQuad->m_aColors[1].g, pQuad->m_aColors[1].b, Alpha),
1442 IGraphics::CColorVertex(2, pQuad->m_aColors[2].r, pQuad->m_aColors[2].g, pQuad->m_aColors[2].b, Alpha),
1443 IGraphics::CColorVertex(3, pQuad->m_aColors[3].r, pQuad->m_aColors[3].g, pQuad->m_aColors[3].b, Alpha)};
1444 Graphics()->SetColorVertex(aArray, 4);
1446 //Rotation
1447 if(Rot != 0)
1449 static CPoint aRotated[4];
1450 aRotated[0] = pQuad->m_aPoints[0];
1451 aRotated[1] = pQuad->m_aPoints[1];
1452 aRotated[2] = pQuad->m_aPoints[2];
1453 aRotated[3] = pQuad->m_aPoints[3];
1454 pPoints = aRotated;
1456 Rotate(&pQuad->m_aPoints[4], &aRotated[0], Rot);
1457 Rotate(&pQuad->m_aPoints[4], &aRotated[1], Rot);
1458 Rotate(&pQuad->m_aPoints[4], &aRotated[2], Rot);
1459 Rotate(&pQuad->m_aPoints[4], &aRotated[3], Rot);
1462 //Set Texture Coords
1463 Graphics()->QuadsSetSubsetFree(
1464 fx2f(pQuad->m_aTexcoords[0].x), fx2f(pQuad->m_aTexcoords[0].y),
1465 fx2f(pQuad->m_aTexcoords[1].x), fx2f(pQuad->m_aTexcoords[1].y),
1466 fx2f(pQuad->m_aTexcoords[2].x), fx2f(pQuad->m_aTexcoords[2].y),
1467 fx2f(pQuad->m_aTexcoords[3].x), fx2f(pQuad->m_aTexcoords[3].y)
1470 //Set Quad Coords & Draw
1471 IGraphics::CFreeformItem Freeform(
1472 fx2f(pPoints[0].x)+OffsetX, fx2f(pPoints[0].y)+OffsetY,
1473 fx2f(pPoints[1].x)+OffsetX, fx2f(pPoints[1].y)+OffsetY,
1474 fx2f(pPoints[2].x)+OffsetX, fx2f(pPoints[2].y)+OffsetY,
1475 fx2f(pPoints[3].x)+OffsetX, fx2f(pPoints[3].y)+OffsetY);
1476 Graphics()->QuadsDrawFreeform(&Freeform, 1);
1478 Graphics()->QuadsEnd();
1480 Graphics()->TextureSet(-1);
1481 Graphics()->QuadsBegin();
1482 DoQuadEnvPoint(pQuad, Index, i);
1483 Graphics()->QuadsEnd();
1487 void CEditor::DoQuadEnvPoint(CQuad *pQuad, int QIndex, int PIndex)
1489 enum
1491 OP_NONE=0,
1492 OP_MOVE,
1493 OP_ROTATE,
1496 // some basic values
1497 static float s_LastWx;
1498 static float s_LastWy;
1499 static int s_Operation = OP_NONE;
1500 float wx = UI()->MouseWorldX();
1501 float wy = UI()->MouseWorldY();
1502 CEnvelope *pEnvelope = m_Map.m_lEnvelopes[pQuad->m_PosEnv];
1503 void *pID = &pEnvelope->m_lPoints[PIndex];
1504 static int s_ActQIndex = -1;
1506 // get pivot
1507 float CenterX = fx2f(pQuad->m_aPoints[4].x)+fx2f(pEnvelope->m_lPoints[PIndex].m_aValues[0]);
1508 float CenterY = fx2f(pQuad->m_aPoints[4].y)+fx2f(pEnvelope->m_lPoints[PIndex].m_aValues[1]);
1510 float dx = (CenterX - wx)/m_WorldZoom;
1511 float dy = (CenterY - wy)/m_WorldZoom;
1512 if(dx*dx+dy*dy < 50.0f && UI()->ActiveItem() == 0)
1514 UI()->SetHotItem(pID);
1515 s_ActQIndex = QIndex;
1518 if(UI()->ActiveItem() == pID && s_ActQIndex == QIndex)
1520 if(s_Operation == OP_MOVE)
1522 if(m_GridActive)
1524 int LineDistance = GetLineDistance();
1526 float x = 0.0f;
1527 float y = 0.0f;
1528 if(wx >= 0)
1529 x = (int)((wx+(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
1530 else
1531 x = (int)((wx-(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
1532 if(wy >= 0)
1533 y = (int)((wy+(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
1534 else
1535 y = (int)((wy-(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
1537 pEnvelope->m_lPoints[PIndex].m_aValues[0] = f2fx(x);
1538 pEnvelope->m_lPoints[PIndex].m_aValues[1] = f2fx(y);
1540 else
1542 pEnvelope->m_lPoints[PIndex].m_aValues[0] += f2fx(wx-s_LastWx);
1543 pEnvelope->m_lPoints[PIndex].m_aValues[1] += f2fx(wy-s_LastWy);
1546 else if(s_Operation == OP_ROTATE)
1547 pEnvelope->m_lPoints[PIndex].m_aValues[2] += 10*m_MouseDeltaX;
1549 s_LastWx = wx;
1550 s_LastWy = wy;
1552 if(!UI()->MouseButton(0))
1554 m_LockMouse = false;
1555 s_Operation = OP_NONE;
1556 UI()->SetActiveItem(0);
1559 Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f);
1561 else if(UI()->HotItem() == pID && s_ActQIndex == QIndex)
1563 ms_pUiGotContext = pID;
1565 Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f);
1566 m_pTooltip = "Left mouse button to move. Hold ctrl to rotate.";
1568 if(UI()->MouseButton(0))
1570 if(Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL))
1572 m_LockMouse = true;
1573 s_Operation = OP_ROTATE;
1575 else
1576 s_Operation = OP_MOVE;
1578 m_SelectedEnvelopePoint = PIndex;
1579 m_SelectedQuadEnvelope = pQuad->m_PosEnv;
1581 UI()->SetActiveItem(pID);
1582 if(m_SelectedQuad != QIndex)
1583 m_SelectedPoints = 0;
1584 m_SelectedQuad = QIndex;
1585 s_LastWx = wx;
1586 s_LastWy = wy;
1588 else
1590 m_SelectedEnvelopePoint = -1;
1591 m_SelectedQuadEnvelope = -1;
1594 else
1595 Graphics()->SetColor(0.0f, 1.0f, 0.0f, 1.0f);
1597 IGraphics::CQuadItem QuadItem(CenterX, CenterY, 5.0f*m_WorldZoom, 5.0f*m_WorldZoom);
1598 Graphics()->QuadsDraw(&QuadItem, 1);
1601 void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar)
1603 // render all good stuff
1604 if(!m_ShowPicker)
1606 for(int g = 0; g < m_Map.m_lGroups.size(); g++)
1608 if(m_Map.m_lGroups[g]->m_Visible)
1609 m_Map.m_lGroups[g]->Render();
1610 //UI()->ClipEnable(&view);
1613 // render the game above everything else
1614 if(m_Map.m_pGameGroup->m_Visible && m_Map.m_pGameLayer->m_Visible)
1616 m_Map.m_pGameGroup->MapScreen();
1617 m_Map.m_pGameLayer->Render();
1620 CLayerTiles *pT = static_cast<CLayerTiles *>(GetSelectedLayerType(0, LAYERTYPE_TILES));
1621 if(m_ShowTileInfo && pT && pT->m_Visible && m_ZoomLevel <= 300)
1623 GetSelectedGroup()->MapScreen();
1624 pT->ShowInfo();
1628 static void *s_pEditorID = (void *)&s_pEditorID;
1629 int Inside = UI()->MouseInside(&View);
1631 // fetch mouse position
1632 float wx = UI()->MouseWorldX();
1633 float wy = UI()->MouseWorldY();
1634 float mx = UI()->MouseX();
1635 float my = UI()->MouseY();
1637 static float s_StartWx = 0;
1638 static float s_StartWy = 0;
1640 enum
1642 OP_NONE=0,
1643 OP_BRUSH_GRAB,
1644 OP_BRUSH_DRAW,
1645 OP_BRUSH_PAINT,
1646 OP_PAN_WORLD,
1647 OP_PAN_EDITOR,
1650 // remap the screen so it can display the whole tileset
1651 if(m_ShowPicker)
1653 CUIRect Screen = *UI()->Screen();
1654 float Size = 32.0*16.0f;
1655 float w = Size*(Screen.w/View.w);
1656 float h = Size*(Screen.h/View.h);
1657 float x = -(View.x/Screen.w)*w;
1658 float y = -(View.y/Screen.h)*h;
1659 wx = x+w*mx/Screen.w;
1660 wy = y+h*my/Screen.h;
1661 Graphics()->MapScreen(x, y, x+w, y+h);
1662 CLayerTiles *t = (CLayerTiles *)GetSelectedLayerType(0, LAYERTYPE_TILES);
1663 if(t)
1665 m_TilesetPicker.m_Image = t->m_Image;
1666 m_TilesetPicker.m_TexID = t->m_TexID;
1667 m_TilesetPicker.Render();
1668 if(m_ShowTileInfo)
1669 m_TilesetPicker.ShowInfo();
1673 static int s_Operation = OP_NONE;
1675 // draw layer borders
1676 CLayer *pEditLayers[16];
1677 int NumEditLayers = 0;
1678 NumEditLayers = 0;
1680 if(m_ShowPicker)
1682 pEditLayers[0] = &m_TilesetPicker;
1683 NumEditLayers++;
1685 else
1687 pEditLayers[0] = GetSelectedLayer(0);
1688 if(pEditLayers[0])
1689 NumEditLayers++;
1691 CLayerGroup *g = GetSelectedGroup();
1692 if(g)
1694 g->MapScreen();
1696 RenderGrid(g);
1698 for(int i = 0; i < NumEditLayers; i++)
1700 if(pEditLayers[i]->m_Type != LAYERTYPE_TILES)
1701 continue;
1703 float w, h;
1704 pEditLayers[i]->GetSize(&w, &h);
1706 IGraphics::CLineItem Array[4] = {
1707 IGraphics::CLineItem(0, 0, w, 0),
1708 IGraphics::CLineItem(w, 0, w, h),
1709 IGraphics::CLineItem(w, h, 0, h),
1710 IGraphics::CLineItem(0, h, 0, 0)};
1711 Graphics()->TextureSet(-1);
1712 Graphics()->LinesBegin();
1713 Graphics()->LinesDraw(Array, 4);
1714 Graphics()->LinesEnd();
1719 if(Inside)
1721 UI()->SetHotItem(s_pEditorID);
1723 // do global operations like pan and zoom
1724 if(UI()->ActiveItem() == 0 && (UI()->MouseButton(0) || UI()->MouseButton(2)))
1726 s_StartWx = wx;
1727 s_StartWy = wy;
1729 if(Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL) || UI()->MouseButton(2))
1731 if(Input()->KeyPressed(KEY_LSHIFT))
1732 s_Operation = OP_PAN_EDITOR;
1733 else
1734 s_Operation = OP_PAN_WORLD;
1735 UI()->SetActiveItem(s_pEditorID);
1739 // brush editing
1740 if(UI()->HotItem() == s_pEditorID)
1742 if(m_Brush.IsEmpty())
1743 m_pTooltip = "Use left mouse button to drag and create a brush.";
1744 else
1745 m_pTooltip = "Use left mouse button to paint with the brush. Right button clears the brush.";
1747 if(UI()->ActiveItem() == s_pEditorID)
1749 CUIRect r;
1750 r.x = s_StartWx;
1751 r.y = s_StartWy;
1752 r.w = wx-s_StartWx;
1753 r.h = wy-s_StartWy;
1754 if(r.w < 0)
1756 r.x += r.w;
1757 r.w = -r.w;
1760 if(r.h < 0)
1762 r.y += r.h;
1763 r.h = -r.h;
1766 if(s_Operation == OP_BRUSH_DRAW)
1768 if(!m_Brush.IsEmpty())
1770 // draw with brush
1771 for(int k = 0; k < NumEditLayers; k++)
1773 if(pEditLayers[k]->m_Type == m_Brush.m_lLayers[0]->m_Type)
1774 pEditLayers[k]->BrushDraw(m_Brush.m_lLayers[0], wx, wy);
1778 else if(s_Operation == OP_BRUSH_GRAB)
1780 if(!UI()->MouseButton(0))
1782 // grab brush
1783 char aBuf[256];
1784 str_format(aBuf, sizeof(aBuf),"grabbing %f %f %f %f", r.x, r.y, r.w, r.h);
1785 Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "editor", aBuf);
1787 // TODO: do all layers
1788 int Grabs = 0;
1789 for(int k = 0; k < NumEditLayers; k++)
1790 Grabs += pEditLayers[k]->BrushGrab(&m_Brush, r);
1791 if(Grabs == 0)
1792 m_Brush.Clear();
1794 else
1796 //editor.map.groups[selected_group]->mapscreen();
1797 for(int k = 0; k < NumEditLayers; k++)
1798 pEditLayers[k]->BrushSelecting(r);
1799 Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h);
1802 else if(s_Operation == OP_BRUSH_PAINT)
1804 if(!UI()->MouseButton(0))
1806 for(int k = 0; k < NumEditLayers; k++)
1807 pEditLayers[k]->FillSelection(m_Brush.IsEmpty(), m_Brush.m_lLayers[0], r);
1809 else
1811 //editor.map.groups[selected_group]->mapscreen();
1812 for(int k = 0; k < NumEditLayers; k++)
1813 pEditLayers[k]->BrushSelecting(r);
1814 Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h);
1818 else
1820 if(UI()->MouseButton(1))
1821 m_Brush.Clear();
1823 if(UI()->MouseButton(0) && s_Operation == OP_NONE)
1825 UI()->SetActiveItem(s_pEditorID);
1827 if(m_Brush.IsEmpty())
1828 s_Operation = OP_BRUSH_GRAB;
1829 else
1831 s_Operation = OP_BRUSH_DRAW;
1832 for(int k = 0; k < NumEditLayers; k++)
1834 if(pEditLayers[k]->m_Type == m_Brush.m_lLayers[0]->m_Type)
1835 pEditLayers[k]->BrushPlace(m_Brush.m_lLayers[0], wx, wy);
1840 CLayerTiles *pLayer = (CLayerTiles*)GetSelectedLayerType(0, LAYERTYPE_TILES);
1841 if((Input()->KeyPressed(KEY_LSHIFT) || Input()->KeyPressed(KEY_RSHIFT)) && pLayer)
1842 s_Operation = OP_BRUSH_PAINT;
1845 if(!m_Brush.IsEmpty())
1847 m_Brush.m_OffsetX = -(int)wx;
1848 m_Brush.m_OffsetY = -(int)wy;
1849 for(int i = 0; i < m_Brush.m_lLayers.size(); i++)
1851 if(m_Brush.m_lLayers[i]->m_Type == LAYERTYPE_TILES)
1853 m_Brush.m_OffsetX = -(int)(wx/32.0f)*32;
1854 m_Brush.m_OffsetY = -(int)(wy/32.0f)*32;
1855 break;
1859 CLayerGroup *g = GetSelectedGroup();
1860 if(g)
1862 m_Brush.m_OffsetX += g->m_OffsetX;
1863 m_Brush.m_OffsetY += g->m_OffsetY;
1864 m_Brush.m_ParallaxX = g->m_ParallaxX;
1865 m_Brush.m_ParallaxY = g->m_ParallaxY;
1866 m_Brush.Render();
1867 float w, h;
1868 m_Brush.GetSize(&w, &h);
1870 IGraphics::CLineItem Array[4] = {
1871 IGraphics::CLineItem(0, 0, w, 0),
1872 IGraphics::CLineItem(w, 0, w, h),
1873 IGraphics::CLineItem(w, h, 0, h),
1874 IGraphics::CLineItem(0, h, 0, 0)};
1875 Graphics()->TextureSet(-1);
1876 Graphics()->LinesBegin();
1877 Graphics()->LinesDraw(Array, 4);
1878 Graphics()->LinesEnd();
1884 // quad editing
1886 if(!m_ShowPicker && m_Brush.IsEmpty())
1888 // fetch layers
1889 CLayerGroup *g = GetSelectedGroup();
1890 if(g)
1891 g->MapScreen();
1893 for(int k = 0; k < NumEditLayers; k++)
1895 if(pEditLayers[k]->m_Type == LAYERTYPE_QUADS)
1897 CLayerQuads *pLayer = (CLayerQuads *)pEditLayers[k];
1899 if(!m_ShowEnvelopePreview)
1900 m_ShowEnvelopePreview = 2;
1902 Graphics()->TextureSet(-1);
1903 Graphics()->QuadsBegin();
1904 for(int i = 0; i < pLayer->m_lQuads.size(); i++)
1906 for(int v = 0; v < 4; v++)
1907 DoQuadPoint(&pLayer->m_lQuads[i], i, v);
1909 DoQuad(&pLayer->m_lQuads[i], i);
1911 Graphics()->QuadsEnd();
1915 Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h);
1918 // do panning
1919 if(UI()->ActiveItem() == s_pEditorID)
1921 if(s_Operation == OP_PAN_WORLD)
1923 m_WorldOffsetX -= m_MouseDeltaX*m_WorldZoom;
1924 m_WorldOffsetY -= m_MouseDeltaY*m_WorldZoom;
1926 else if(s_Operation == OP_PAN_EDITOR)
1928 m_EditorOffsetX -= m_MouseDeltaX*m_WorldZoom;
1929 m_EditorOffsetY -= m_MouseDeltaY*m_WorldZoom;
1932 // release mouse
1933 if(!UI()->MouseButton(0))
1935 s_Operation = OP_NONE;
1936 UI()->SetActiveItem(0);
1941 else if(UI()->ActiveItem() == s_pEditorID)
1943 // release mouse
1944 if(!UI()->MouseButton(0))
1946 s_Operation = OP_NONE;
1947 UI()->SetActiveItem(0);
1951 if(GetSelectedGroup() && GetSelectedGroup()->m_UseClipping)
1953 CLayerGroup *g = m_Map.m_pGameGroup;
1954 g->MapScreen();
1956 Graphics()->TextureSet(-1);
1957 Graphics()->LinesBegin();
1959 CUIRect r;
1960 r.x = GetSelectedGroup()->m_ClipX;
1961 r.y = GetSelectedGroup()->m_ClipY;
1962 r.w = GetSelectedGroup()->m_ClipW;
1963 r.h = GetSelectedGroup()->m_ClipH;
1965 IGraphics::CLineItem Array[4] = {
1966 IGraphics::CLineItem(r.x, r.y, r.x+r.w, r.y),
1967 IGraphics::CLineItem(r.x+r.w, r.y, r.x+r.w, r.y+r.h),
1968 IGraphics::CLineItem(r.x+r.w, r.y+r.h, r.x, r.y+r.h),
1969 IGraphics::CLineItem(r.x, r.y+r.h, r.x, r.y)};
1970 Graphics()->SetColor(1,0,0,1);
1971 Graphics()->LinesDraw(Array, 4);
1973 Graphics()->LinesEnd();
1976 // render screen sizes
1977 if(m_ProofBorders)
1979 CLayerGroup *g = m_Map.m_pGameGroup;
1980 g->MapScreen();
1982 Graphics()->TextureSet(-1);
1983 Graphics()->LinesBegin();
1985 float aLastPoints[4];
1986 float Start = 1.0f; //9.0f/16.0f;
1987 float End = 16.0f/9.0f;
1988 const int NumSteps = 20;
1989 for(int i = 0; i <= NumSteps; i++)
1991 float aPoints[4];
1992 float Aspect = Start + (End-Start)*(i/(float)NumSteps);
1994 RenderTools()->MapscreenToWorld(
1995 m_WorldOffsetX, m_WorldOffsetY,
1996 1.0f, 1.0f, 0.0f, 0.0f, Aspect, 1.0f, aPoints);
1998 if(i == 0)
2000 IGraphics::CLineItem Array[2] = {
2001 IGraphics::CLineItem(aPoints[0], aPoints[1], aPoints[2], aPoints[1]),
2002 IGraphics::CLineItem(aPoints[0], aPoints[3], aPoints[2], aPoints[3])};
2003 Graphics()->LinesDraw(Array, 2);
2006 if(i != 0)
2008 IGraphics::CLineItem Array[4] = {
2009 IGraphics::CLineItem(aPoints[0], aPoints[1], aLastPoints[0], aLastPoints[1]),
2010 IGraphics::CLineItem(aPoints[2], aPoints[1], aLastPoints[2], aLastPoints[1]),
2011 IGraphics::CLineItem(aPoints[0], aPoints[3], aLastPoints[0], aLastPoints[3]),
2012 IGraphics::CLineItem(aPoints[2], aPoints[3], aLastPoints[2], aLastPoints[3])};
2013 Graphics()->LinesDraw(Array, 4);
2016 if(i == NumSteps)
2018 IGraphics::CLineItem Array[2] = {
2019 IGraphics::CLineItem(aPoints[0], aPoints[1], aPoints[0], aPoints[3]),
2020 IGraphics::CLineItem(aPoints[2], aPoints[1], aPoints[2], aPoints[3])};
2021 Graphics()->LinesDraw(Array, 2);
2024 mem_copy(aLastPoints, aPoints, sizeof(aPoints));
2027 if(1)
2029 Graphics()->SetColor(1,0,0,1);
2030 for(int i = 0; i < 2; i++)
2032 float aPoints[4];
2033 float aAspects[] = {4.0f/3.0f, 16.0f/10.0f, 5.0f/4.0f, 16.0f/9.0f};
2034 float Aspect = aAspects[i];
2036 RenderTools()->MapscreenToWorld(
2037 m_WorldOffsetX, m_WorldOffsetY,
2038 1.0f, 1.0f, 0.0f, 0.0f, Aspect, 1.0f, aPoints);
2040 CUIRect r;
2041 r.x = aPoints[0];
2042 r.y = aPoints[1];
2043 r.w = aPoints[2]-aPoints[0];
2044 r.h = aPoints[3]-aPoints[1];
2046 IGraphics::CLineItem Array[4] = {
2047 IGraphics::CLineItem(r.x, r.y, r.x+r.w, r.y),
2048 IGraphics::CLineItem(r.x+r.w, r.y, r.x+r.w, r.y+r.h),
2049 IGraphics::CLineItem(r.x+r.w, r.y+r.h, r.x, r.y+r.h),
2050 IGraphics::CLineItem(r.x, r.y+r.h, r.x, r.y)};
2051 Graphics()->LinesDraw(Array, 4);
2052 Graphics()->SetColor(0,1,0,1);
2056 Graphics()->LinesEnd();
2059 if (!m_ShowPicker && m_ShowTileInfo && m_ShowEnvelopePreview != 0 && GetSelectedLayer(0) && GetSelectedLayer(0)->m_Type == LAYERTYPE_QUADS)
2061 GetSelectedGroup()->MapScreen();
2063 CLayerQuads *pLayer = (CLayerQuads*)GetSelectedLayer(0);
2064 int TexID = -1;
2065 if(pLayer->m_Image >= 0 && pLayer->m_Image < m_Map.m_lImages.size())
2066 TexID = m_Map.m_lImages[pLayer->m_Image]->m_TexID;
2068 for(int i = 0; i < pLayer->m_lQuads.size(); i++)
2070 if((m_ShowEnvelopePreview == 1 && pLayer->m_lQuads[i].m_PosEnv == m_SelectedEnvelope) || m_ShowEnvelopePreview == 2)
2071 DoQuadEnvelopes(&pLayer->m_lQuads[i], i, TexID);
2074 m_ShowEnvelopePreview = 0;
2077 Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h);
2078 //UI()->ClipDisable();
2082 int CEditor::DoProperties(CUIRect *pToolBox, CProperty *pProps, int *pIDs, int *pNewVal)
2084 int Change = -1;
2086 for(int i = 0; pProps[i].m_pName; i++)
2088 CUIRect Slot;
2089 pToolBox->HSplitTop(13.0f, &Slot, pToolBox);
2090 CUIRect Label, Shifter;
2091 Slot.VSplitMid(&Label, &Shifter);
2092 Shifter.HMargin(1.0f, &Shifter);
2093 UI()->DoLabel(&Label, pProps[i].m_pName, 10.0f, -1, -1);
2095 if(pProps[i].m_Type == PROPTYPE_INT_STEP)
2097 CUIRect Inc, Dec;
2098 char aBuf[64];
2100 Shifter.VSplitRight(10.0f, &Shifter, &Inc);
2101 Shifter.VSplitLeft(10.0f, &Dec, &Shifter);
2102 str_format(aBuf, sizeof(aBuf),"%d", pProps[i].m_Value);
2103 RenderTools()->DrawUIRect(&Shifter, vec4(1,1,1,0.5f), 0, 0.0f);
2104 UI()->DoLabel(&Shifter, aBuf, 10.0f, 0, -1);
2106 if(DoButton_ButtonDec(&pIDs[i], 0, 0, &Dec, 0, "Decrease"))
2108 *pNewVal = pProps[i].m_Value-1;
2109 Change = i;
2111 if(DoButton_ButtonInc(((char *)&pIDs[i])+1, 0, 0, &Inc, 0, "Increase"))
2113 *pNewVal = pProps[i].m_Value+1;
2114 Change = i;
2117 else if(pProps[i].m_Type == PROPTYPE_BOOL)
2119 CUIRect No, Yes;
2120 Shifter.VSplitMid(&No, &Yes);
2121 if(DoButton_ButtonDec(&pIDs[i], "No", !pProps[i].m_Value, &No, 0, ""))
2123 *pNewVal = 0;
2124 Change = i;
2126 if(DoButton_ButtonInc(((char *)&pIDs[i])+1, "Yes", pProps[i].m_Value, &Yes, 0, ""))
2128 *pNewVal = 1;
2129 Change = i;
2132 else if(pProps[i].m_Type == PROPTYPE_INT_SCROLL)
2134 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.");
2135 if(NewValue != pProps[i].m_Value)
2137 *pNewVal = NewValue;
2138 Change = i;
2141 else if(pProps[i].m_Type == PROPTYPE_COLOR)
2143 static const char *s_paTexts[4] = {"R", "G", "B", "A"};
2144 static int s_aShift[] = {24, 16, 8, 0};
2145 int NewColor = 0;
2147 for(int c = 0; c < 4; c++)
2149 int v = (pProps[i].m_Value >> s_aShift[c])&0xff;
2150 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];
2152 if(c != 3)
2154 pToolBox->HSplitTop(13.0f, &Slot, pToolBox);
2155 Slot.VSplitMid(0, &Shifter);
2156 Shifter.HMargin(1.0f, &Shifter);
2160 if(NewColor != pProps[i].m_Value)
2162 *pNewVal = NewColor;
2163 Change = i;
2166 else if(pProps[i].m_Type == PROPTYPE_IMAGE)
2168 char aBuf[64];
2169 if(pProps[i].m_Value < 0)
2170 str_copy(aBuf, "None", sizeof(aBuf));
2171 else
2172 str_format(aBuf, sizeof(aBuf),"%s", m_Map.m_lImages[pProps[i].m_Value]->m_aName);
2174 if(DoButton_Editor(&pIDs[i], aBuf, 0, &Shifter, 0, 0))
2175 PopupSelectImageInvoke(pProps[i].m_Value, UI()->MouseX(), UI()->MouseY());
2177 int r = PopupSelectImageResult();
2178 if(r >= -1)
2180 *pNewVal = r;
2181 Change = i;
2184 else if(pProps[i].m_Type == PROPTYPE_SHIFT)
2186 CUIRect Left, Right, Up, Down;
2187 Shifter.VSplitMid(&Left, &Up);
2188 Left.VSplitRight(1.0f, &Left, 0);
2189 Up.VSplitLeft(1.0f, 0, &Up);
2190 Left.VSplitLeft(10.0f, &Left, &Shifter);
2191 Shifter.VSplitRight(10.0f, &Shifter, &Right);
2192 RenderTools()->DrawUIRect(&Shifter, vec4(1,1,1,0.5f), 0, 0.0f);
2193 UI()->DoLabel(&Shifter, "X", 10.0f, 0, -1);
2194 Up.VSplitLeft(10.0f, &Up, &Shifter);
2195 Shifter.VSplitRight(10.0f, &Shifter, &Down);
2196 RenderTools()->DrawUIRect(&Shifter, vec4(1,1,1,0.5f), 0, 0.0f);
2197 UI()->DoLabel(&Shifter, "Y", 10.0f, 0, -1);
2198 if(DoButton_ButtonDec(&pIDs[i], "-", 0, &Left, 0, "Left"))
2200 *pNewVal = 1;
2201 Change = i;
2203 if(DoButton_ButtonInc(((char *)&pIDs[i])+3, "+", 0, &Right, 0, "Right"))
2205 *pNewVal = 2;
2206 Change = i;
2208 if(DoButton_ButtonDec(((char *)&pIDs[i])+1, "-", 0, &Up, 0, "Up"))
2210 *pNewVal = 4;
2211 Change = i;
2213 if(DoButton_ButtonInc(((char *)&pIDs[i])+2, "+", 0, &Down, 0, "Down"))
2215 *pNewVal = 8;
2216 Change = i;
2221 return Change;
2224 void CEditor::RenderLayers(CUIRect ToolBox, CUIRect ToolBar, CUIRect View)
2226 CUIRect LayersBox = ToolBox;
2228 if(!m_GuiActive)
2229 return;
2231 CUIRect Slot, Button;
2232 char aBuf[64];
2234 float LayersHeight = 12.0f; // Height of AddGroup button
2235 static int s_ScrollBar = 0;
2236 static float s_ScrollValue = 0;
2238 for(int g = 0; g < m_Map.m_lGroups.size(); g++)
2240 // Each group is 19.0f
2241 // Each layer is 14.0f
2242 LayersHeight += 19.0f;
2243 if(!m_Map.m_lGroups[g]->m_Collapse)
2244 LayersHeight += m_Map.m_lGroups[g]->m_lLayers.size() * 14.0f;
2247 float ScrollDifference = LayersHeight - LayersBox.h;
2249 if(LayersHeight > LayersBox.h) // Do we even need a scrollbar?
2251 CUIRect Scroll;
2252 LayersBox.VSplitRight(15.0f, &LayersBox, &Scroll);
2253 LayersBox.VSplitRight(3.0f, &LayersBox, 0); // extra spacing
2254 Scroll.HMargin(5.0f, &Scroll);
2255 s_ScrollValue = UiDoScrollbarV(&s_ScrollBar, &Scroll, s_ScrollValue);
2257 if(UI()->MouseInside(&Scroll) || UI()->MouseInside(&LayersBox))
2259 int ScrollNum = (int)((LayersHeight-LayersBox.h)/15.0f)+1;
2260 if(ScrollNum > 0)
2262 if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP))
2263 s_ScrollValue = clamp(s_ScrollValue - 1.0f/ScrollNum, 0.0f, 1.0f);
2264 if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN))
2265 s_ScrollValue = clamp(s_ScrollValue + 1.0f/ScrollNum, 0.0f, 1.0f);
2267 else
2268 ScrollNum = 0;
2272 float LayerStartAt = ScrollDifference * s_ScrollValue;
2273 if(LayerStartAt < 0.0f)
2274 LayerStartAt = 0.0f;
2276 float LayerStopAt = LayersHeight - ScrollDifference * (1 - s_ScrollValue);
2277 float LayerCur = 0;
2279 // render layers
2281 for(int g = 0; g < m_Map.m_lGroups.size(); g++)
2283 if(LayerCur > LayerStopAt)
2284 break;
2285 else if(LayerCur + m_Map.m_lGroups[g]->m_lLayers.size() * 14.0f + 19.0f < LayerStartAt)
2287 LayerCur += m_Map.m_lGroups[g]->m_lLayers.size() * 14.0f + 19.0f;
2288 continue;
2291 CUIRect VisibleToggle, SaveCheck;
2292 if(LayerCur >= LayerStartAt)
2294 LayersBox.HSplitTop(12.0f, &Slot, &LayersBox);
2295 Slot.VSplitLeft(12, &VisibleToggle, &Slot);
2296 if(DoButton_Ex(&m_Map.m_lGroups[g]->m_Visible, m_Map.m_lGroups[g]->m_Visible?"V":"H", m_Map.m_lGroups[g]->m_Collapse ? 1 : 0, &VisibleToggle, 0, "Toggle group visibility", CUI::CORNER_L))
2297 m_Map.m_lGroups[g]->m_Visible = !m_Map.m_lGroups[g]->m_Visible;
2299 Slot.VSplitRight(12.0f, &Slot, &SaveCheck);
2300 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))
2301 if(!m_Map.m_lGroups[g]->m_GameGroup)
2302 m_Map.m_lGroups[g]->m_SaveToMap = !m_Map.m_lGroups[g]->m_SaveToMap;
2304 str_format(aBuf, sizeof(aBuf),"#%d %s", g, m_Map.m_lGroups[g]->m_aName);
2305 float FontSize = 10.0f;
2306 while(TextRender()->TextWidth(0, FontSize, aBuf, -1) > Slot.w)
2307 FontSize--;
2308 if(int Result = DoButton_Ex(&m_Map.m_lGroups[g], aBuf, g==m_SelectedGroup, &Slot,
2309 BUTTON_CONTEXT, m_Map.m_lGroups[g]->m_Collapse ? "Select group. Double click to expand." : "Select group. Double click to collapse.", 0, FontSize))
2311 m_SelectedGroup = g;
2312 m_SelectedLayer = 0;
2314 static int s_GroupPopupId = 0;
2315 if(Result == 2)
2316 UiInvokePopupMenu(&s_GroupPopupId, 0, UI()->MouseX(), UI()->MouseY(), 145, 220, PopupGroup);
2318 if(m_Map.m_lGroups[g]->m_lLayers.size() && Input()->MouseDoubleClick())
2319 m_Map.m_lGroups[g]->m_Collapse ^= 1;
2321 LayersBox.HSplitTop(2.0f, &Slot, &LayersBox);
2323 LayerCur += 14.0f;
2325 for(int i = 0; i < m_Map.m_lGroups[g]->m_lLayers.size(); i++)
2327 if(LayerCur > LayerStopAt)
2328 break;
2329 else if(LayerCur < LayerStartAt)
2331 LayerCur += 14.0f;
2332 continue;
2335 if(m_Map.m_lGroups[g]->m_Collapse)
2336 continue;
2338 //visible
2339 LayersBox.HSplitTop(12.0f, &Slot, &LayersBox);
2340 Slot.VSplitLeft(12.0f, 0, &Button);
2341 Button.VSplitLeft(15, &VisibleToggle, &Button);
2343 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))
2344 m_Map.m_lGroups[g]->m_lLayers[i]->m_Visible = !m_Map.m_lGroups[g]->m_lLayers[i]->m_Visible;
2346 Button.VSplitRight(12.0f, &Button, &SaveCheck);
2347 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))
2348 if(m_Map.m_lGroups[g]->m_lLayers[i] != m_Map.m_pGameLayer)
2349 m_Map.m_lGroups[g]->m_lLayers[i]->m_SaveToMap = !m_Map.m_lGroups[g]->m_lLayers[i]->m_SaveToMap;
2351 if(m_Map.m_lGroups[g]->m_lLayers[i]->m_aName[0])
2352 str_format(aBuf, sizeof(aBuf), "%s", m_Map.m_lGroups[g]->m_lLayers[i]->m_aName);
2353 else if(m_Map.m_lGroups[g]->m_lLayers[i]->m_Type == LAYERTYPE_TILES)
2354 str_copy(aBuf, "Tiles", sizeof(aBuf));
2355 else
2356 str_copy(aBuf, "Quads", sizeof(aBuf));
2358 float FontSize = 10.0f;
2359 while(TextRender()->TextWidth(0, FontSize, aBuf, -1) > Button.w)
2360 FontSize--;
2361 if(int Result = DoButton_Ex(m_Map.m_lGroups[g]->m_lLayers[i], aBuf, g==m_SelectedGroup&&i==m_SelectedLayer, &Button,
2362 BUTTON_CONTEXT, "Select layer.", 0, FontSize))
2364 m_SelectedLayer = i;
2365 m_SelectedGroup = g;
2366 static int s_LayerPopupID = 0;
2367 if(Result == 2)
2368 UiInvokePopupMenu(&s_LayerPopupID, 0, UI()->MouseX(), UI()->MouseY(), 120, 245, PopupLayer);
2371 LayerCur += 14.0f;
2372 LayersBox.HSplitTop(2.0f, &Slot, &LayersBox);
2374 if(LayerCur > LayerStartAt && LayerCur < LayerStopAt)
2375 LayersBox.HSplitTop(5.0f, &Slot, &LayersBox);
2376 LayerCur += 5.0f;
2380 if(LayerCur <= LayerStopAt)
2382 LayersBox.HSplitTop(12.0f, &Slot, &LayersBox);
2384 static int s_NewGroupButton = 0;
2385 if(DoButton_Editor(&s_NewGroupButton, "Add group", 0, &Slot, 0, "Adds a new group"))
2387 m_Map.NewGroup();
2388 m_SelectedGroup = m_Map.m_lGroups.size()-1;
2393 void CEditor::ReplaceImage(const char *pFileName, int StorageType, void *pUser)
2395 CEditor *pEditor = (CEditor *)pUser;
2396 CEditorImage ImgInfo(pEditor);
2397 if(!pEditor->Graphics()->LoadPNG(&ImgInfo, pFileName, StorageType))
2398 return;
2400 CEditorImage *pImg = pEditor->m_Map.m_lImages[pEditor->m_SelectedImage];
2401 int External = pImg->m_External;
2402 pEditor->Graphics()->UnloadTexture(pImg->m_TexID);
2403 *pImg = ImgInfo;
2404 pImg->m_External = External;
2405 pEditor->ExtractName(pFileName, pImg->m_aName, sizeof(pImg->m_aName));
2406 pImg->m_AutoMapper.Load(pImg->m_aName);
2407 pImg->m_TexID = pEditor->Graphics()->LoadTextureRaw(ImgInfo.m_Width, ImgInfo.m_Height, ImgInfo.m_Format, ImgInfo.m_pData, CImageInfo::FORMAT_AUTO, 0);
2408 pEditor->SortImages();
2409 for(int i = 0; i < pEditor->m_Map.m_lImages.size(); ++i)
2411 if(!str_comp(pEditor->m_Map.m_lImages[i]->m_aName, pImg->m_aName))
2412 pEditor->m_SelectedImage = i;
2414 pEditor->m_Dialog = DIALOG_NONE;
2417 void CEditor::AddImage(const char *pFileName, int StorageType, void *pUser)
2419 CEditor *pEditor = (CEditor *)pUser;
2420 CEditorImage ImgInfo(pEditor);
2421 if(!pEditor->Graphics()->LoadPNG(&ImgInfo, pFileName, StorageType))
2422 return;
2424 // check if we have that image already
2425 char aBuf[128];
2426 ExtractName(pFileName, aBuf, sizeof(aBuf));
2427 for(int i = 0; i < pEditor->m_Map.m_lImages.size(); ++i)
2429 if(!str_comp(pEditor->m_Map.m_lImages[i]->m_aName, aBuf))
2430 return;
2433 CEditorImage *pImg = new CEditorImage(pEditor);
2434 *pImg = ImgInfo;
2435 pImg->m_TexID = pEditor->Graphics()->LoadTextureRaw(ImgInfo.m_Width, ImgInfo.m_Height, ImgInfo.m_Format, ImgInfo.m_pData, CImageInfo::FORMAT_AUTO, 0);
2436 pImg->m_External = 1; // external by default
2437 str_copy(pImg->m_aName, aBuf, sizeof(pImg->m_aName));
2438 pImg->m_AutoMapper.Load(pImg->m_aName);
2439 pEditor->m_Map.m_lImages.add(pImg);
2440 pEditor->SortImages();
2441 if(pEditor->m_SelectedImage > -1 && pEditor->m_SelectedImage < pEditor->m_Map.m_lImages.size())
2443 for(int i = 0; i <= pEditor->m_SelectedImage; ++i)
2444 if(!str_comp(pEditor->m_Map.m_lImages[i]->m_aName, aBuf))
2446 pEditor->m_SelectedImage++;
2447 break;
2450 pEditor->m_Dialog = DIALOG_NONE;
2454 static int gs_ModifyIndexDeletedIndex;
2455 static void ModifyIndexDeleted(int *pIndex)
2457 if(*pIndex == gs_ModifyIndexDeletedIndex)
2458 *pIndex = -1;
2459 else if(*pIndex > gs_ModifyIndexDeletedIndex)
2460 *pIndex = *pIndex - 1;
2463 int CEditor::PopupImage(CEditor *pEditor, CUIRect View)
2465 static int s_ReplaceButton = 0;
2466 static int s_RemoveButton = 0;
2468 CUIRect Slot;
2469 View.HSplitTop(2.0f, &Slot, &View);
2470 View.HSplitTop(12.0f, &Slot, &View);
2471 CEditorImage *pImg = pEditor->m_Map.m_lImages[pEditor->m_SelectedImage];
2473 static int s_ExternalButton = 0;
2474 if(pImg->m_External)
2476 if(pEditor->DoButton_MenuItem(&s_ExternalButton, "Embed", 0, &Slot, 0, "Embeds the image into the map file."))
2478 pImg->m_External = 0;
2479 return 1;
2482 else
2484 if(pEditor->DoButton_MenuItem(&s_ExternalButton, "Make external", 0, &Slot, 0, "Removes the image from the map file."))
2486 pImg->m_External = 1;
2487 return 1;
2491 View.HSplitTop(10.0f, &Slot, &View);
2492 View.HSplitTop(12.0f, &Slot, &View);
2493 if(pEditor->DoButton_MenuItem(&s_ReplaceButton, "Replace", 0, &Slot, 0, "Replaces the image with a new one"))
2495 pEditor->InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_IMG, "Replace Image", "Replace", "mapres", "", ReplaceImage, pEditor);
2496 return 1;
2499 View.HSplitTop(10.0f, &Slot, &View);
2500 View.HSplitTop(12.0f, &Slot, &View);
2501 if(pEditor->DoButton_MenuItem(&s_RemoveButton, "Remove", 0, &Slot, 0, "Removes the image from the map"))
2503 delete pImg;
2504 pEditor->m_Map.m_lImages.remove_index(pEditor->m_SelectedImage);
2505 gs_ModifyIndexDeletedIndex = pEditor->m_SelectedImage;
2506 pEditor->m_Map.ModifyImageIndex(ModifyIndexDeleted);
2507 return 1;
2510 return 0;
2513 static int CompareImageName(const void *pObject1, const void *pObject2)
2515 CEditorImage *pImage1 = *(CEditorImage**)pObject1;
2516 CEditorImage *pImage2 = *(CEditorImage**)pObject2;
2518 return str_comp(pImage1->m_aName, pImage2->m_aName);
2521 static int *gs_pSortedIndex = 0;
2522 static void ModifySortedIndex(int *pIndex)
2524 if(*pIndex > -1)
2525 *pIndex = gs_pSortedIndex[*pIndex];
2528 void CEditor::SortImages()
2530 bool Sorted = true;
2531 for(int i = 1; i < m_Map.m_lImages.size(); i++)
2532 if( str_comp(m_Map.m_lImages[i]->m_aName, m_Map.m_lImages[i-1]->m_aName) < 0 )
2534 Sorted = false;
2535 break;
2538 if(!Sorted)
2540 array<CEditorImage*> lTemp = array<CEditorImage*>(m_Map.m_lImages);
2541 gs_pSortedIndex = new int[lTemp.size()];
2543 qsort(m_Map.m_lImages.base_ptr(), m_Map.m_lImages.size(), sizeof(CEditorImage*), CompareImageName);
2545 for(int OldIndex = 0; OldIndex < lTemp.size(); OldIndex++)
2546 for(int NewIndex = 0; NewIndex < m_Map.m_lImages.size(); NewIndex++)
2547 if(lTemp[OldIndex] == m_Map.m_lImages[NewIndex])
2548 gs_pSortedIndex[OldIndex] = NewIndex;
2550 m_Map.ModifyImageIndex(ModifySortedIndex);
2552 delete [] gs_pSortedIndex;
2553 gs_pSortedIndex = 0;
2558 void CEditor::RenderImages(CUIRect ToolBox, CUIRect ToolBar, CUIRect View)
2560 static int s_ScrollBar = 0;
2561 static float s_ScrollValue = 0;
2562 float ImagesHeight = 30.0f + 14.0f * m_Map.m_lImages.size() + 27.0f;
2563 float ScrollDifference = ImagesHeight - ToolBox.h;
2565 if(ImagesHeight > ToolBox.h) // Do we even need a scrollbar?
2567 CUIRect Scroll;
2568 ToolBox.VSplitRight(15.0f, &ToolBox, &Scroll);
2569 ToolBox.VSplitRight(3.0f, &ToolBox, 0); // extra spacing
2570 Scroll.HMargin(5.0f, &Scroll);
2571 s_ScrollValue = UiDoScrollbarV(&s_ScrollBar, &Scroll, s_ScrollValue);
2573 if(UI()->MouseInside(&Scroll) || UI()->MouseInside(&ToolBox))
2575 int ScrollNum = (int)((ImagesHeight-ToolBox.h)/14.0f)+1;
2576 if(ScrollNum > 0)
2578 if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP))
2579 s_ScrollValue = clamp(s_ScrollValue - 1.0f/ScrollNum, 0.0f, 1.0f);
2580 if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN))
2581 s_ScrollValue = clamp(s_ScrollValue + 1.0f/ScrollNum, 0.0f, 1.0f);
2583 else
2584 ScrollNum = 0;
2588 float ImageStartAt = ScrollDifference * s_ScrollValue;
2589 if(ImageStartAt < 0.0f)
2590 ImageStartAt = 0.0f;
2592 float ImageStopAt = ImagesHeight - ScrollDifference * (1 - s_ScrollValue);
2593 float ImageCur = 0.0f;
2595 for(int e = 0; e < 2; e++) // two passes, first embedded, then external
2597 CUIRect Slot;
2599 if(ImageCur > ImageStopAt)
2600 break;
2601 else if(ImageCur >= ImageStartAt)
2604 ToolBox.HSplitTop(15.0f, &Slot, &ToolBox);
2605 if(e == 0)
2606 UI()->DoLabel(&Slot, "Embedded", 12.0f, 0);
2607 else
2608 UI()->DoLabel(&Slot, "External", 12.0f, 0);
2610 ImageCur += 15.0f;
2612 for(int i = 0; i < m_Map.m_lImages.size(); i++)
2614 if((e && !m_Map.m_lImages[i]->m_External) ||
2615 (!e && m_Map.m_lImages[i]->m_External))
2617 continue;
2620 if(ImageCur > ImageStopAt)
2621 break;
2622 else if(ImageCur < ImageStartAt)
2624 ImageCur += 14.0f;
2625 continue;
2627 ImageCur += 14.0f;
2629 char aBuf[128];
2630 str_copy(aBuf, m_Map.m_lImages[i]->m_aName, sizeof(aBuf));
2631 ToolBox.HSplitTop(12.0f, &Slot, &ToolBox);
2633 if(int Result = DoButton_Editor(&m_Map.m_lImages[i], aBuf, m_SelectedImage == i, &Slot,
2634 BUTTON_CONTEXT, "Select image"))
2636 m_SelectedImage = i;
2638 static int s_PopupImageID = 0;
2639 if(Result == 2)
2640 UiInvokePopupMenu(&s_PopupImageID, 0, UI()->MouseX(), UI()->MouseY(), 120, 80, PopupImage);
2643 ToolBox.HSplitTop(2.0f, 0, &ToolBox);
2645 // render image
2646 if(m_SelectedImage == i)
2648 CUIRect r;
2649 View.Margin(10.0f, &r);
2650 if(r.h < r.w)
2651 r.w = r.h;
2652 else
2653 r.h = r.w;
2654 float Max = (float)(max(m_Map.m_lImages[i]->m_Width, m_Map.m_lImages[i]->m_Height));
2655 r.w *= m_Map.m_lImages[i]->m_Width/Max;
2656 r.h *= m_Map.m_lImages[i]->m_Height/Max;
2657 Graphics()->TextureSet(m_Map.m_lImages[i]->m_TexID);
2658 Graphics()->BlendNormal();
2659 Graphics()->QuadsBegin();
2660 IGraphics::CQuadItem QuadItem(r.x, r.y, r.w, r.h);
2661 Graphics()->QuadsDrawTL(&QuadItem, 1);
2662 Graphics()->QuadsEnd();
2667 // separator
2668 ToolBox.HSplitTop(5.0f, &Slot, &ToolBox);
2669 ImageCur += 5.0f;
2670 IGraphics::CLineItem LineItem(Slot.x, Slot.y+Slot.h/2, Slot.x+Slot.w, Slot.y+Slot.h/2);
2671 Graphics()->TextureSet(-1);
2672 Graphics()->LinesBegin();
2673 Graphics()->LinesDraw(&LineItem, 1);
2674 Graphics()->LinesEnd();
2677 if(ImageCur + 27.0f > ImageStopAt)
2678 return;
2680 CUIRect Slot;
2681 ToolBox.HSplitTop(5.0f, &Slot, &ToolBox);
2683 // new image
2684 static int s_NewImageButton = 0;
2685 ToolBox.HSplitTop(12.0f, &Slot, &ToolBox);
2686 if(DoButton_Editor(&s_NewImageButton, "Add", 0, &Slot, 0, "Load a new image to use in the map"))
2687 InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_IMG, "Add Image", "Add", "mapres", "", AddImage, this);
2691 static int EditorListdirCallback(const char *pName, int IsDir, int StorageType, void *pUser)
2693 CEditor *pEditor = (CEditor*)pUser;
2694 int Length = str_length(pName);
2695 if((pName[0] == '.' && (pName[1] == 0 ||
2696 (pName[1] == '.' && pName[2] == 0 && (!str_comp(pEditor->m_pFileDialogPath, "maps") || !str_comp(pEditor->m_pFileDialogPath, "mapres"))))) ||
2697 (!IsDir && ((pEditor->m_FileDialogFileType == CEditor::FILETYPE_MAP && (Length < 4 || str_comp(pName+Length-4, ".map"))) ||
2698 (pEditor->m_FileDialogFileType == CEditor::FILETYPE_IMG && (Length < 4 || str_comp(pName+Length-4, ".png"))))))
2699 return 0;
2701 CEditor::CFilelistItem Item;
2702 str_copy(Item.m_aFilename, pName, sizeof(Item.m_aFilename));
2703 if(IsDir)
2704 str_format(Item.m_aName, sizeof(Item.m_aName), "%s/", pName);
2705 else
2706 str_copy(Item.m_aName, pName, min(static_cast<int>(sizeof(Item.m_aName)), Length-3));
2707 Item.m_IsDir = IsDir != 0;
2708 Item.m_IsLink = false;
2709 Item.m_StorageType = StorageType;
2710 pEditor->m_FileList.add(Item);
2712 return 0;
2715 void CEditor::AddFileDialogEntry(int Index, CUIRect *pView)
2717 m_FilesCur++;
2718 if(m_FilesCur-1 < m_FilesStartAt || m_FilesCur >= m_FilesStopAt)
2719 return;
2721 CUIRect Button, FileIcon;
2722 pView->HSplitTop(15.0f, &Button, pView);
2723 pView->HSplitTop(2.0f, 0, pView);
2724 Button.VSplitLeft(Button.h, &FileIcon, &Button);
2725 Button.VSplitLeft(5.0f, 0, &Button);
2727 Graphics()->TextureSet(g_pData->m_aImages[IMAGE_FILEICONS].m_Id);
2728 Graphics()->QuadsBegin();
2729 RenderTools()->SelectSprite(m_FileList[Index].m_IsDir?SPRITE_FILE_FOLDER:SPRITE_FILE_MAP2);
2730 IGraphics::CQuadItem QuadItem(FileIcon.x, FileIcon.y, FileIcon.w, FileIcon.h);
2731 Graphics()->QuadsDrawTL(&QuadItem, 1);
2732 Graphics()->QuadsEnd();
2734 if(DoButton_File(&m_FileList[Index], m_FileList[Index].m_aName, m_FilesSelectedIndex == Index, &Button, 0, 0))
2736 if(!m_FileList[Index].m_IsDir)
2737 str_copy(m_aFileDialogFileName, m_FileList[Index].m_aFilename, sizeof(m_aFileDialogFileName));
2738 else
2739 m_aFileDialogFileName[0] = 0;
2740 m_FilesSelectedIndex = Index;
2742 if(Input()->MouseDoubleClick())
2743 m_aFileDialogActivate = true;
2747 void CEditor::RenderFileDialog()
2749 // GUI coordsys
2750 Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h);
2751 CUIRect View = *UI()->Screen();
2752 float Width = View.w, Height = View.h;
2754 RenderTools()->DrawUIRect(&View, vec4(0,0,0,0.25f), 0, 0);
2755 View.VMargin(150.0f, &View);
2756 View.HMargin(50.0f, &View);
2757 RenderTools()->DrawUIRect(&View, vec4(0,0,0,0.75f), CUI::CORNER_ALL, 5.0f);
2758 View.Margin(10.0f, &View);
2760 CUIRect Title, FileBox, FileBoxLabel, ButtonBar, Scroll;
2761 View.HSplitTop(18.0f, &Title, &View);
2762 View.HSplitTop(5.0f, 0, &View); // some spacing
2763 View.HSplitBottom(14.0f, &View, &ButtonBar);
2764 View.HSplitBottom(10.0f, &View, 0); // some spacing
2765 View.HSplitBottom(14.0f, &View, &FileBox);
2766 FileBox.VSplitLeft(55.0f, &FileBoxLabel, &FileBox);
2767 View.HSplitBottom(10.0f, &View, 0); // some spacing
2768 View.VSplitRight(15.0f, &View, &Scroll);
2770 // title
2771 RenderTools()->DrawUIRect(&Title, vec4(1, 1, 1, 0.25f), CUI::CORNER_ALL, 4.0f);
2772 Title.VMargin(10.0f, &Title);
2773 UI()->DoLabel(&Title, m_pFileDialogTitle, 12.0f, -1, -1);
2775 // filebox
2776 if(m_FileDialogStorageType == IStorage::TYPE_SAVE)
2778 static float s_FileBoxID = 0;
2779 UI()->DoLabel(&FileBoxLabel, "Filename:", 10.0f, -1, -1);
2780 if(DoEditBox(&s_FileBoxID, &FileBox, m_aFileDialogFileName, sizeof(m_aFileDialogFileName), 10.0f, &s_FileBoxID))
2782 // remove '/' and '\'
2783 for(int i = 0; m_aFileDialogFileName[i]; ++i)
2784 if(m_aFileDialogFileName[i] == '/' || m_aFileDialogFileName[i] == '\\')
2785 str_copy(&m_aFileDialogFileName[i], &m_aFileDialogFileName[i+1], (int)(sizeof(m_aFileDialogFileName))-i);
2786 m_FilesSelectedIndex = -1;
2790 int Num = (int)(View.h/17.0f)+1;
2791 static int ScrollBar = 0;
2792 Scroll.HMargin(5.0f, &Scroll);
2793 m_FileDialogScrollValue = UiDoScrollbarV(&ScrollBar, &Scroll, m_FileDialogScrollValue);
2795 int ScrollNum = m_FileList.size()-Num+1;
2796 if(ScrollNum > 0)
2798 if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP))
2799 m_FileDialogScrollValue -= 3.0f/ScrollNum;
2800 if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN))
2801 m_FileDialogScrollValue += 3.0f/ScrollNum;
2803 else
2804 ScrollNum = 0;
2806 if(m_FilesSelectedIndex > -1)
2808 for(int i = 0; i < Input()->NumEvents(); i++)
2810 int NewIndex = -1;
2811 if(Input()->GetEvent(i).m_Flags&IInput::FLAG_PRESS)
2813 if(Input()->GetEvent(i).m_Key == KEY_DOWN) NewIndex = m_FilesSelectedIndex + 1;
2814 if(Input()->GetEvent(i).m_Key == KEY_UP) NewIndex = m_FilesSelectedIndex - 1;
2816 if(NewIndex > -1 && NewIndex < m_FileList.size())
2818 //scroll
2819 float IndexY = View.y - m_FileDialogScrollValue*ScrollNum*17.0f + NewIndex*17.0f;
2820 int Scroll = View.y > IndexY ? -1 : View.y+View.h < IndexY+17.0f ? 1 : 0;
2821 if(Scroll)
2823 if(Scroll < 0)
2824 m_FileDialogScrollValue = ((float)(NewIndex)+0.5f)/ScrollNum;
2825 else
2826 m_FileDialogScrollValue = ((float)(NewIndex-Num)+2.5f)/ScrollNum;
2829 if(!m_FileList[NewIndex].m_IsDir)
2830 str_copy(m_aFileDialogFileName, m_FileList[NewIndex].m_aFilename, sizeof(m_aFileDialogFileName));
2831 else
2832 m_aFileDialogFileName[0] = 0;
2833 m_FilesSelectedIndex = NewIndex;
2838 for(int i = 0; i < Input()->NumEvents(); i++)
2840 if(Input()->GetEvent(i).m_Flags&IInput::FLAG_PRESS)
2842 if(Input()->GetEvent(i).m_Key == KEY_RETURN || Input()->GetEvent(i).m_Key == KEY_KP_ENTER)
2843 m_aFileDialogActivate = true;
2847 if(m_FileDialogScrollValue < 0) m_FileDialogScrollValue = 0;
2848 if(m_FileDialogScrollValue > 1) m_FileDialogScrollValue = 1;
2850 m_FilesStartAt = (int)(ScrollNum*m_FileDialogScrollValue);
2851 if(m_FilesStartAt < 0)
2852 m_FilesStartAt = 0;
2854 m_FilesStopAt = m_FilesStartAt+Num;
2856 m_FilesCur = 0;
2858 // set clipping
2859 UI()->ClipEnable(&View);
2861 for(int i = 0; i < m_FileList.size(); i++)
2862 AddFileDialogEntry(i, &View);
2864 // disable clipping again
2865 UI()->ClipDisable();
2867 // the buttons
2868 static int s_OkButton = 0;
2869 static int s_CancelButton = 0;
2870 static int s_NewFolderButton = 0;
2872 CUIRect Button;
2873 ButtonBar.VSplitRight(50.0f, &ButtonBar, &Button);
2874 bool IsDir = m_FilesSelectedIndex >= 0 && m_FileList[m_FilesSelectedIndex].m_IsDir;
2875 if(DoButton_Editor(&s_OkButton, IsDir ? "Open" : m_pFileDialogButtonText, 0, &Button, 0, 0) || m_aFileDialogActivate)
2877 m_aFileDialogActivate = false;
2878 if(IsDir) // folder
2880 if(str_comp(m_FileList[m_FilesSelectedIndex].m_aFilename, "..") == 0) // parent folder
2882 if(fs_parent_dir(m_pFileDialogPath))
2883 m_pFileDialogPath = m_aFileDialogCurrentFolder; // leave the link
2885 else // sub folder
2887 if(m_FileList[m_FilesSelectedIndex].m_IsLink)
2889 m_pFileDialogPath = m_aFileDialogCurrentLink; // follow the link
2890 str_copy(m_aFileDialogCurrentLink, m_FileList[m_FilesSelectedIndex].m_aFilename, sizeof(m_aFileDialogCurrentLink));
2892 else
2894 char aTemp[MAX_PATH_LENGTH];
2895 str_copy(aTemp, m_pFileDialogPath, sizeof(aTemp));
2896 str_format(m_pFileDialogPath, MAX_PATH_LENGTH, "%s/%s", aTemp, m_FileList[m_FilesSelectedIndex].m_aFilename);
2899 FilelistPopulate(!str_comp(m_pFileDialogPath, "maps") || !str_comp(m_pFileDialogPath, "mapres") ? m_FileDialogStorageType :
2900 m_FileList[m_FilesSelectedIndex].m_StorageType);
2901 if(m_FilesSelectedIndex >= 0 && !m_FileList[m_FilesSelectedIndex].m_IsDir)
2902 str_copy(m_aFileDialogFileName, m_FileList[m_FilesSelectedIndex].m_aFilename, sizeof(m_aFileDialogFileName));
2903 else
2904 m_aFileDialogFileName[0] = 0;
2906 else // file
2908 str_format(m_aFileSaveName, sizeof(m_aFileSaveName), "%s/%s", m_pFileDialogPath, m_aFileDialogFileName);
2909 if(!str_comp(m_pFileDialogButtonText, "Save"))
2911 IOHANDLE File = Storage()->OpenFile(m_aFileSaveName, IOFLAG_READ, IStorage::TYPE_SAVE);
2912 if(File)
2914 io_close(File);
2915 m_PopupEventType = POPEVENT_SAVE;
2916 m_PopupEventActivated = true;
2918 else
2919 if(m_pfnFileDialogFunc)
2920 m_pfnFileDialogFunc(m_aFileSaveName, m_FilesSelectedIndex >= 0 ? m_FileList[m_FilesSelectedIndex].m_StorageType : m_FileDialogStorageType, m_pFileDialogUser);
2922 else
2923 if(m_pfnFileDialogFunc)
2924 m_pfnFileDialogFunc(m_aFileSaveName, m_FilesSelectedIndex >= 0 ? m_FileList[m_FilesSelectedIndex].m_StorageType : m_FileDialogStorageType, m_pFileDialogUser);
2928 ButtonBar.VSplitRight(40.0f, &ButtonBar, &Button);
2929 ButtonBar.VSplitRight(50.0f, &ButtonBar, &Button);
2930 if(DoButton_Editor(&s_CancelButton, "Cancel", 0, &Button, 0, 0) || Input()->KeyPressed(KEY_ESCAPE))
2931 m_Dialog = DIALOG_NONE;
2933 if(m_FileDialogStorageType == IStorage::TYPE_SAVE)
2935 ButtonBar.VSplitLeft(40.0f, 0, &ButtonBar);
2936 ButtonBar.VSplitLeft(70.0f, &Button, &ButtonBar);
2937 if(DoButton_Editor(&s_NewFolderButton, "New folder", 0, &Button, 0, 0))
2939 m_FileDialogNewFolderName[0] = 0;
2940 m_FileDialogErrString[0] = 0;
2941 static int s_NewFolderPopupID = 0;
2942 UiInvokePopupMenu(&s_NewFolderPopupID, 0, Width/2.0f-200.0f, Height/2.0f-100.0f, 400.0f, 200.0f, PopupNewFolder);
2943 UI()->SetActiveItem(0);
2948 void CEditor::FilelistPopulate(int StorageType)
2950 m_FileList.clear();
2951 if(m_FileDialogStorageType != IStorage::TYPE_SAVE && !str_comp(m_pFileDialogPath, "maps"))
2953 CFilelistItem Item;
2954 str_copy(Item.m_aFilename, "downloadedmaps", sizeof(Item.m_aFilename));
2955 str_copy(Item.m_aName, "downloadedmaps/", sizeof(Item.m_aName));
2956 Item.m_IsDir = true;
2957 Item.m_IsLink = true;
2958 Item.m_StorageType = IStorage::TYPE_SAVE;
2959 m_FileList.add(Item);
2961 Storage()->ListDirectory(StorageType, m_pFileDialogPath, EditorListdirCallback, this);
2962 m_FilesSelectedIndex = m_FileList.size() ? 0 : -1;
2963 m_aFileDialogActivate = false;
2966 void CEditor::InvokeFileDialog(int StorageType, int FileType, const char *pTitle, const char *pButtonText,
2967 const char *pBasePath, const char *pDefaultName,
2968 void (*pfnFunc)(const char *pFileName, int StorageType, void *pUser), void *pUser)
2970 m_FileDialogStorageType = StorageType;
2971 m_pFileDialogTitle = pTitle;
2972 m_pFileDialogButtonText = pButtonText;
2973 m_pfnFileDialogFunc = pfnFunc;
2974 m_pFileDialogUser = pUser;
2975 m_aFileDialogFileName[0] = 0;
2976 m_aFileDialogCurrentFolder[0] = 0;
2977 m_aFileDialogCurrentLink[0] = 0;
2978 m_pFileDialogPath = m_aFileDialogCurrentFolder;
2979 m_FileDialogFileType = FileType;
2980 m_FileDialogScrollValue = 0.0f;
2982 if(pDefaultName)
2983 str_copy(m_aFileDialogFileName, pDefaultName, sizeof(m_aFileDialogFileName));
2984 if(pBasePath)
2985 str_copy(m_aFileDialogCurrentFolder, pBasePath, sizeof(m_aFileDialogCurrentFolder));
2987 FilelistPopulate(m_FileDialogStorageType);
2989 m_Dialog = DIALOG_FILE;
2994 void CEditor::RenderModebar(CUIRect View)
2996 CUIRect Button;
2998 // mode buttons
3000 View.VSplitLeft(65.0f, &Button, &View);
3001 Button.HSplitTop(30.0f, 0, &Button);
3002 static int s_Button = 0;
3003 const char *pButName = m_Mode == MODE_LAYERS ? "Layers" : "Images";
3004 if(DoButton_Tab(&s_Button, pButName, 0, &Button, 0, "Switch between images and layers managment."))
3006 if(m_Mode == MODE_LAYERS)
3007 m_Mode = MODE_IMAGES;
3008 else
3009 m_Mode = MODE_LAYERS;
3013 View.VSplitLeft(5.0f, 0, &View);
3016 void CEditor::RenderStatusbar(CUIRect View)
3018 CUIRect Button;
3019 View.VSplitRight(60.0f, &View, &Button);
3020 static int s_EnvelopeButton = 0;
3021 if(DoButton_Editor(&s_EnvelopeButton, "Envelopes", m_ShowEnvelopeEditor, &Button, 0, "Toggles the envelope editor."))
3022 m_ShowEnvelopeEditor = (m_ShowEnvelopeEditor+1)%4;
3024 if(m_pTooltip)
3026 if(ms_pUiGotContext && ms_pUiGotContext == UI()->HotItem())
3028 char aBuf[512];
3029 str_format(aBuf, sizeof(aBuf), "%s Right click for context menu.", m_pTooltip);
3030 UI()->DoLabel(&View, aBuf, 10.0f, -1, -1);
3032 else
3033 UI()->DoLabel(&View, m_pTooltip, 10.0f, -1, -1);
3037 void CEditor::RenderEnvelopeEditor(CUIRect View)
3039 if(m_SelectedEnvelope < 0) m_SelectedEnvelope = 0;
3040 if(m_SelectedEnvelope >= m_Map.m_lEnvelopes.size()) m_SelectedEnvelope = m_Map.m_lEnvelopes.size()-1;
3042 CEnvelope *pEnvelope = 0;
3043 if(m_SelectedEnvelope >= 0 && m_SelectedEnvelope < m_Map.m_lEnvelopes.size())
3044 pEnvelope = m_Map.m_lEnvelopes[m_SelectedEnvelope];
3046 CUIRect ToolBar, CurveBar, ColorBar;
3047 View.HSplitTop(15.0f, &ToolBar, &View);
3048 View.HSplitTop(15.0f, &CurveBar, &View);
3049 ToolBar.Margin(2.0f, &ToolBar);
3050 CurveBar.Margin(2.0f, &CurveBar);
3052 // do the toolbar
3054 CUIRect Button;
3055 CEnvelope *pNewEnv = 0;
3057 ToolBar.VSplitRight(50.0f, &ToolBar, &Button);
3058 static int s_New4dButton = 0;
3059 if(DoButton_Editor(&s_New4dButton, "Color+", 0, &Button, 0, "Creates a new color envelope"))
3061 m_Map.m_Modified = true;
3062 pNewEnv = m_Map.NewEnvelope(4);
3065 ToolBar.VSplitRight(5.0f, &ToolBar, &Button);
3066 ToolBar.VSplitRight(50.0f, &ToolBar, &Button);
3067 static int s_New2dButton = 0;
3068 if(DoButton_Editor(&s_New2dButton, "Pos.+", 0, &Button, 0, "Creates a new pos envelope"))
3070 m_Map.m_Modified = true;
3071 pNewEnv = m_Map.NewEnvelope(3);
3074 // Delete button
3075 if(m_SelectedEnvelope >= 0)
3077 ToolBar.VSplitRight(10.0f, &ToolBar, &Button);
3078 ToolBar.VSplitRight(50.0f, &ToolBar, &Button);
3079 static int s_DelButton = 0;
3080 if(DoButton_Editor(&s_DelButton, "Delete", 0, &Button, 0, "Delete this envelope"))
3082 m_Map.m_Modified = true;
3083 m_Map.DeleteEnvelope(m_SelectedEnvelope);
3084 if(m_SelectedEnvelope >= m_Map.m_lEnvelopes.size())
3085 m_SelectedEnvelope = m_Map.m_lEnvelopes.size()-1;
3086 pEnvelope = m_SelectedEnvelope >= 0 ? m_Map.m_lEnvelopes[m_SelectedEnvelope] : 0;
3090 if(pNewEnv) // add the default points
3092 if(pNewEnv->m_Channels == 4)
3094 pNewEnv->AddPoint(0, 1,1,1,1);
3095 pNewEnv->AddPoint(1000, 1,1,1,1);
3097 else
3099 pNewEnv->AddPoint(0, 0);
3100 pNewEnv->AddPoint(1000, 0);
3104 CUIRect Shifter, Inc, Dec;
3105 ToolBar.VSplitLeft(60.0f, &Shifter, &ToolBar);
3106 Shifter.VSplitRight(15.0f, &Shifter, &Inc);
3107 Shifter.VSplitLeft(15.0f, &Dec, &Shifter);
3108 char aBuf[512];
3109 str_format(aBuf, sizeof(aBuf),"%d/%d", m_SelectedEnvelope+1, m_Map.m_lEnvelopes.size());
3110 RenderTools()->DrawUIRect(&Shifter, vec4(1,1,1,0.5f), 0, 0.0f);
3111 UI()->DoLabel(&Shifter, aBuf, 10.0f, 0, -1);
3113 static int s_PrevButton = 0;
3114 if(DoButton_ButtonDec(&s_PrevButton, 0, 0, &Dec, 0, "Previous Envelope"))
3115 m_SelectedEnvelope--;
3117 static int s_NextButton = 0;
3118 if(DoButton_ButtonInc(&s_NextButton, 0, 0, &Inc, 0, "Next Envelope"))
3119 m_SelectedEnvelope++;
3121 if(pEnvelope)
3123 ToolBar.VSplitLeft(15.0f, &Button, &ToolBar);
3124 ToolBar.VSplitLeft(35.0f, &Button, &ToolBar);
3125 UI()->DoLabel(&Button, "Name:", 10.0f, -1, -1);
3127 ToolBar.VSplitLeft(80.0f, &Button, &ToolBar);
3129 static float s_NameBox = 0;
3130 if(DoEditBox(&s_NameBox, &Button, pEnvelope->m_aName, sizeof(pEnvelope->m_aName), 10.0f, &s_NameBox))
3131 m_Map.m_Modified = true;
3135 bool ShowColorBar = false;
3136 if(pEnvelope && pEnvelope->m_Channels == 4)
3138 ShowColorBar = true;
3139 View.HSplitTop(20.0f, &ColorBar, &View);
3140 ColorBar.Margin(2.0f, &ColorBar);
3141 RenderBackground(ColorBar, ms_CheckerTexture, 16.0f, 1.0f);
3144 RenderBackground(View, ms_CheckerTexture, 32.0f, 0.1f);
3146 if(pEnvelope)
3148 static array<int> Selection;
3149 static int sEnvelopeEditorID = 0;
3150 static int s_ActiveChannels = 0xf;
3152 if(pEnvelope)
3154 CUIRect Button;
3156 ToolBar.VSplitLeft(15.0f, &Button, &ToolBar);
3158 static const char *s_paNames[2][4] = {
3159 {"X", "Y", "R", ""},
3160 {"R", "G", "B", "A"},
3163 const char *paDescriptions[2][4] = {
3164 {"X-axis of the envelope", "Y-axis of the envelope", "Rotation of the envelope", ""},
3165 {"Red value of the envelope", "Green value of the envelope", "Blue value of the envelope", "Alpha value of the envelope"},
3168 static int s_aChannelButtons[4] = {0};
3169 int Bit = 1;
3170 //ui_draw_button_func draw_func;
3172 for(int i = 0; i < pEnvelope->m_Channels; i++, Bit<<=1)
3174 ToolBar.VSplitLeft(15.0f, &Button, &ToolBar);
3176 /*if(i == 0) draw_func = draw_editor_button_l;
3177 else if(i == envelope->channels-1) draw_func = draw_editor_button_r;
3178 else draw_func = draw_editor_button_m;*/
3180 if(DoButton_Editor(&s_aChannelButtons[i], s_paNames[pEnvelope->m_Channels-3][i], s_ActiveChannels&Bit, &Button, 0, paDescriptions[pEnvelope->m_Channels-3][i]))
3181 s_ActiveChannels ^= Bit;
3185 float EndTime = pEnvelope->EndTime();
3186 if(EndTime < 1)
3187 EndTime = 1;
3189 pEnvelope->FindTopBottom(s_ActiveChannels);
3190 float Top = pEnvelope->m_Top;
3191 float Bottom = pEnvelope->m_Bottom;
3193 if(Top < 1)
3194 Top = 1;
3195 if(Bottom >= 0)
3196 Bottom = 0;
3198 float TimeScale = EndTime/View.w;
3199 float ValueScale = (Top-Bottom)/View.h;
3201 if(UI()->MouseInside(&View))
3202 UI()->SetHotItem(&sEnvelopeEditorID);
3204 if(UI()->HotItem() == &sEnvelopeEditorID)
3206 // do stuff
3207 if(pEnvelope)
3209 if(UI()->MouseButtonClicked(1))
3211 // add point
3212 int Time = (int)(((UI()->MouseX()-View.x)*TimeScale)*1000.0f);
3213 //float env_y = (UI()->MouseY()-view.y)/TimeScale;
3214 float aChannels[4];
3215 pEnvelope->Eval(Time, aChannels);
3216 pEnvelope->AddPoint(Time,
3217 f2fx(aChannels[0]), f2fx(aChannels[1]),
3218 f2fx(aChannels[2]), f2fx(aChannels[3]));
3219 m_Map.m_Modified = true;
3222 m_ShowEnvelopePreview = 1;
3223 m_pTooltip = "Press right mouse button to create a new point";
3227 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)};
3229 // render lines
3231 UI()->ClipEnable(&View);
3232 Graphics()->TextureSet(-1);
3233 Graphics()->LinesBegin();
3234 for(int c = 0; c < pEnvelope->m_Channels; c++)
3236 if(s_ActiveChannels&(1<<c))
3237 Graphics()->SetColor(aColors[c].r,aColors[c].g,aColors[c].b,1);
3238 else
3239 Graphics()->SetColor(aColors[c].r*0.5f,aColors[c].g*0.5f,aColors[c].b*0.5f,1);
3241 float PrevX = 0;
3242 float aResults[4];
3243 pEnvelope->Eval(0.000001f, aResults);
3244 float PrevValue = aResults[c];
3246 int Steps = (int)((View.w/UI()->Screen()->w) * Graphics()->ScreenWidth());
3247 for(int i = 1; i <= Steps; i++)
3249 float a = i/(float)Steps;
3250 pEnvelope->Eval(a*EndTime, aResults);
3251 float v = aResults[c];
3252 v = (v-Bottom)/(Top-Bottom);
3254 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);
3255 Graphics()->LinesDraw(&LineItem, 1);
3256 PrevX = a;
3257 PrevValue = v;
3260 Graphics()->LinesEnd();
3261 UI()->ClipDisable();
3264 // render curve options
3266 for(int i = 0; i < pEnvelope->m_lPoints.size()-1; i++)
3268 float t0 = pEnvelope->m_lPoints[i].m_Time/1000.0f/EndTime;
3269 float t1 = pEnvelope->m_lPoints[i+1].m_Time/1000.0f/EndTime;
3271 //dbg_msg("", "%f", end_time);
3273 CUIRect v;
3274 v.x = CurveBar.x + (t0+(t1-t0)*0.5f) * CurveBar.w;
3275 v.y = CurveBar.y;
3276 v.h = CurveBar.h;
3277 v.w = CurveBar.h;
3278 v.x -= v.w/2;
3279 void *pID = &pEnvelope->m_lPoints[i].m_Curvetype;
3280 const char *paTypeName[] = {
3281 "N", "L", "S", "F", "M"
3284 if(DoButton_Editor(pID, paTypeName[pEnvelope->m_lPoints[i].m_Curvetype], 0, &v, 0, "Switch curve type"))
3285 pEnvelope->m_lPoints[i].m_Curvetype = (pEnvelope->m_lPoints[i].m_Curvetype+1)%NUM_CURVETYPES;
3289 // render colorbar
3290 if(ShowColorBar)
3292 Graphics()->TextureSet(-1);
3293 Graphics()->QuadsBegin();
3294 for(int i = 0; i < pEnvelope->m_lPoints.size()-1; i++)
3296 float r0 = fx2f(pEnvelope->m_lPoints[i].m_aValues[0]);
3297 float g0 = fx2f(pEnvelope->m_lPoints[i].m_aValues[1]);
3298 float b0 = fx2f(pEnvelope->m_lPoints[i].m_aValues[2]);
3299 float a0 = fx2f(pEnvelope->m_lPoints[i].m_aValues[3]);
3300 float r1 = fx2f(pEnvelope->m_lPoints[i+1].m_aValues[0]);
3301 float g1 = fx2f(pEnvelope->m_lPoints[i+1].m_aValues[1]);
3302 float b1 = fx2f(pEnvelope->m_lPoints[i+1].m_aValues[2]);
3303 float a1 = fx2f(pEnvelope->m_lPoints[i+1].m_aValues[3]);
3305 IGraphics::CColorVertex Array[4] = {IGraphics::CColorVertex(0, r0, g0, b0, a0),
3306 IGraphics::CColorVertex(1, r1, g1, b1, a1),
3307 IGraphics::CColorVertex(2, r1, g1, b1, a1),
3308 IGraphics::CColorVertex(3, r0, g0, b0, a0)};
3309 Graphics()->SetColorVertex(Array, 4);
3311 float x0 = pEnvelope->m_lPoints[i].m_Time/1000.0f/EndTime;
3312 // float y0 = (fx2f(envelope->points[i].values[c])-bottom)/(top-bottom);
3313 float x1 = pEnvelope->m_lPoints[i+1].m_Time/1000.0f/EndTime;
3314 //float y1 = (fx2f(envelope->points[i+1].values[c])-bottom)/(top-bottom);
3315 CUIRect v;
3316 v.x = ColorBar.x + x0*ColorBar.w;
3317 v.y = ColorBar.y;
3318 v.w = (x1-x0)*ColorBar.w;
3319 v.h = ColorBar.h;
3321 IGraphics::CQuadItem QuadItem(v.x, v.y, v.w, v.h);
3322 Graphics()->QuadsDrawTL(&QuadItem, 1);
3324 Graphics()->QuadsEnd();
3327 // render handles
3329 int CurrentValue = 0, CurrentTime = 0;
3331 Graphics()->TextureSet(-1);
3332 Graphics()->QuadsBegin();
3333 for(int c = 0; c < pEnvelope->m_Channels; c++)
3335 if(!(s_ActiveChannels&(1<<c)))
3336 continue;
3338 for(int i = 0; i < pEnvelope->m_lPoints.size(); i++)
3340 float x0 = pEnvelope->m_lPoints[i].m_Time/1000.0f/EndTime;
3341 float y0 = (fx2f(pEnvelope->m_lPoints[i].m_aValues[c])-Bottom)/(Top-Bottom);
3342 CUIRect Final;
3343 Final.x = View.x + x0*View.w;
3344 Final.y = View.y+View.h - y0*View.h;
3345 Final.x -= 2.0f;
3346 Final.y -= 2.0f;
3347 Final.w = 4.0f;
3348 Final.h = 4.0f;
3350 void *pID = &pEnvelope->m_lPoints[i].m_aValues[c];
3352 if(UI()->MouseInside(&Final))
3353 UI()->SetHotItem(pID);
3355 float ColorMod = 1.0f;
3357 if(UI()->ActiveItem() == pID)
3359 if(!UI()->MouseButton(0))
3361 m_SelectedQuadEnvelope = -1;
3362 m_SelectedEnvelopePoint = -1;
3364 UI()->SetActiveItem(0);
3366 else
3368 if(Input()->KeyPressed(KEY_LSHIFT) || Input()->KeyPressed(KEY_RSHIFT))
3370 if(i != 0)
3372 if((Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL)))
3373 pEnvelope->m_lPoints[i].m_Time += (int)((m_MouseDeltaX));
3374 else
3375 pEnvelope->m_lPoints[i].m_Time += (int)((m_MouseDeltaX*TimeScale)*1000.0f);
3376 if(pEnvelope->m_lPoints[i].m_Time < pEnvelope->m_lPoints[i-1].m_Time)
3377 pEnvelope->m_lPoints[i].m_Time = pEnvelope->m_lPoints[i-1].m_Time + 1;
3378 if(i+1 != pEnvelope->m_lPoints.size() && pEnvelope->m_lPoints[i].m_Time > pEnvelope->m_lPoints[i+1].m_Time)
3379 pEnvelope->m_lPoints[i].m_Time = pEnvelope->m_lPoints[i+1].m_Time - 1;
3382 else
3384 if((Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL)))
3385 pEnvelope->m_lPoints[i].m_aValues[c] -= f2fx(m_MouseDeltaY*0.001f);
3386 else
3387 pEnvelope->m_lPoints[i].m_aValues[c] -= f2fx(m_MouseDeltaY*ValueScale);
3390 m_SelectedQuadEnvelope = m_SelectedEnvelope;
3391 m_ShowEnvelopePreview = 1;
3392 m_SelectedEnvelopePoint = i;
3393 m_Map.m_Modified = true;
3396 ColorMod = 100.0f;
3397 Graphics()->SetColor(1,1,1,1);
3399 else if(UI()->HotItem() == pID)
3401 if(UI()->MouseButton(0))
3403 Selection.clear();
3404 Selection.add(i);
3405 UI()->SetActiveItem(pID);
3408 // remove point
3409 if(UI()->MouseButtonClicked(1))
3411 pEnvelope->m_lPoints.remove_index(i);
3412 m_Map.m_Modified = true;
3415 m_ShowEnvelopePreview = 1;
3416 ColorMod = 100.0f;
3417 Graphics()->SetColor(1,0.75f,0.75f,1);
3418 m_pTooltip = "Left mouse to drag. Hold ctrl to be more precise. Hold shift to alter time point aswell. Right click to delete.";
3421 if(UI()->ActiveItem() == pID || UI()->HotItem() == pID)
3423 CurrentTime = pEnvelope->m_lPoints[i].m_Time;
3424 CurrentValue = pEnvelope->m_lPoints[i].m_aValues[c];
3427 if (m_SelectedQuadEnvelope == m_SelectedEnvelope && m_SelectedEnvelopePoint == i)
3428 Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f);
3429 else
3430 Graphics()->SetColor(aColors[c].r*ColorMod, aColors[c].g*ColorMod, aColors[c].b*ColorMod, 1.0f);
3431 IGraphics::CQuadItem QuadItem(Final.x, Final.y, Final.w, Final.h);
3432 Graphics()->QuadsDrawTL(&QuadItem, 1);
3435 Graphics()->QuadsEnd();
3437 char aBuf[512];
3438 str_format(aBuf, sizeof(aBuf),"%.3f %.3f", CurrentTime/1000.0f, fx2f(CurrentValue));
3439 UI()->DoLabel(&ToolBar, aBuf, 10.0f, 0, -1);
3444 int CEditor::PopupMenuFile(CEditor *pEditor, CUIRect View)
3446 static int s_NewMapButton = 0;
3447 static int s_SaveButton = 0;
3448 static int s_SaveAsButton = 0;
3449 static int s_OpenButton = 0;
3450 static int s_AppendButton = 0;
3451 static int s_ExitButton = 0;
3453 CUIRect Slot;
3454 View.HSplitTop(2.0f, &Slot, &View);
3455 View.HSplitTop(12.0f, &Slot, &View);
3456 if(pEditor->DoButton_MenuItem(&s_NewMapButton, "New", 0, &Slot, 0, "Creates a new map"))
3458 if(pEditor->HasUnsavedData())
3460 pEditor->m_PopupEventType = POPEVENT_NEW;
3461 pEditor->m_PopupEventActivated = true;
3463 else
3465 pEditor->Reset();
3466 pEditor->m_aFileName[0] = 0;
3468 return 1;
3471 View.HSplitTop(10.0f, &Slot, &View);
3472 View.HSplitTop(12.0f, &Slot, &View);
3473 if(pEditor->DoButton_MenuItem(&s_OpenButton, "Load", 0, &Slot, 0, "Opens a map for editing"))
3475 if(pEditor->HasUnsavedData())
3477 pEditor->m_PopupEventType = POPEVENT_LOAD;
3478 pEditor->m_PopupEventActivated = true;
3480 else
3481 pEditor->InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_MAP, "Load map", "Load", "maps", "", pEditor->CallbackOpenMap, pEditor);
3482 return 1;
3485 View.HSplitTop(10.0f, &Slot, &View);
3486 View.HSplitTop(12.0f, &Slot, &View);
3487 if(pEditor->DoButton_MenuItem(&s_AppendButton, "Append", 0, &Slot, 0, "Opens a map and adds everything from that map to the current one"))
3489 pEditor->InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_MAP, "Append map", "Append", "maps", "", pEditor->CallbackAppendMap, pEditor);
3490 return 1;
3493 View.HSplitTop(10.0f, &Slot, &View);
3494 View.HSplitTop(12.0f, &Slot, &View);
3495 if(pEditor->DoButton_MenuItem(&s_SaveButton, "Save", 0, &Slot, 0, "Saves the current map"))
3497 if(pEditor->m_aFileName[0] && pEditor->m_ValidSaveFilename)
3499 str_copy(pEditor->m_aFileSaveName, pEditor->m_aFileName, sizeof(pEditor->m_aFileSaveName));
3500 pEditor->m_PopupEventType = POPEVENT_SAVE;
3501 pEditor->m_PopupEventActivated = true;
3503 else
3504 pEditor->InvokeFileDialog(IStorage::TYPE_SAVE, FILETYPE_MAP, "Save map", "Save", "maps", "", pEditor->CallbackSaveMap, pEditor);
3505 return 1;
3508 View.HSplitTop(2.0f, &Slot, &View);
3509 View.HSplitTop(12.0f, &Slot, &View);
3510 if(pEditor->DoButton_MenuItem(&s_SaveAsButton, "Save As", 0, &Slot, 0, "Saves the current map under a new name"))
3512 pEditor->InvokeFileDialog(IStorage::TYPE_SAVE, FILETYPE_MAP, "Save map", "Save", "maps", "", pEditor->CallbackSaveMap, pEditor);
3513 return 1;
3516 View.HSplitTop(10.0f, &Slot, &View);
3517 View.HSplitTop(12.0f, &Slot, &View);
3518 if(pEditor->DoButton_MenuItem(&s_ExitButton, "Exit", 0, &Slot, 0, "Exits from the editor"))
3520 if(pEditor->HasUnsavedData())
3522 pEditor->m_PopupEventType = POPEVENT_EXIT;
3523 pEditor->m_PopupEventActivated = true;
3525 else
3526 g_Config.m_ClEditor = 0;
3527 return 1;
3530 return 0;
3533 void CEditor::RenderMenubar(CUIRect MenuBar)
3535 static CUIRect s_File /*, view, help*/;
3537 MenuBar.VSplitLeft(60.0f, &s_File, &MenuBar);
3538 if(DoButton_Menu(&s_File, "File", 0, &s_File, 0, 0))
3539 UiInvokePopupMenu(&s_File, 1, s_File.x, s_File.y+s_File.h-1.0f, 120, 150, PopupMenuFile, this);
3542 menubar.VSplitLeft(5.0f, 0, &menubar);
3543 menubar.VSplitLeft(60.0f, &view, &menubar);
3544 if(do_editor_button(&view, "View", 0, &view, draw_editor_button_menu, 0, 0))
3545 (void)0;
3547 menubar.VSplitLeft(5.0f, 0, &menubar);
3548 menubar.VSplitLeft(60.0f, &help, &menubar);
3549 if(do_editor_button(&help, "Help", 0, &help, draw_editor_button_menu, 0, 0))
3550 (void)0;
3553 MenuBar.VSplitLeft(40.0f, 0, &MenuBar);
3554 char aBuf[128];
3555 str_format(aBuf, sizeof(aBuf), "File: %s", m_aFileName);
3556 UI()->DoLabel(&MenuBar, aBuf, 10.0f, -1, -1);
3559 void CEditor::Render()
3561 // basic start
3562 Graphics()->Clear(1.0f, 0.0f, 1.0f);
3563 CUIRect View = *UI()->Screen();
3564 Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h);
3566 float Width = View.w;
3567 float Height = View.h;
3569 // reset tip
3570 m_pTooltip = 0;
3572 // render checker
3573 RenderBackground(View, ms_CheckerTexture, 32.0f, 1.0f);
3575 CUIRect MenuBar, CModeBar, ToolBar, StatusBar, EnvelopeEditor, ToolBox;
3576 m_ShowPicker = Input()->KeyPressed(KEY_SPACE) != 0 && m_Dialog == DIALOG_NONE;
3578 if(m_GuiActive)
3581 View.HSplitTop(16.0f, &MenuBar, &View);
3582 View.HSplitTop(53.0f, &ToolBar, &View);
3583 View.VSplitLeft(100.0f, &ToolBox, &View);
3584 View.HSplitBottom(16.0f, &View, &StatusBar);
3586 if(m_ShowEnvelopeEditor && !m_ShowPicker)
3588 float size = 125.0f;
3589 if(m_ShowEnvelopeEditor == 2)
3590 size *= 2.0f;
3591 else if(m_ShowEnvelopeEditor == 3)
3592 size *= 3.0f;
3593 View.HSplitBottom(size, &View, &EnvelopeEditor);
3597 // a little hack for now
3598 if(m_Mode == MODE_LAYERS)
3599 DoMapEditor(View, ToolBar);
3601 // do the scrolling
3602 if(m_Dialog == DIALOG_NONE && UI()->MouseInside(&View))
3604 if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP))
3605 m_ZoomLevel -= 20;
3607 if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN))
3608 m_ZoomLevel += 20;
3610 m_ZoomLevel = clamp(m_ZoomLevel, 50, 2000);
3613 if(m_GuiActive)
3615 float Brightness = 0.25f;
3616 RenderBackground(MenuBar, ms_BackgroundTexture, 128.0f, Brightness*0);
3617 MenuBar.Margin(2.0f, &MenuBar);
3619 RenderBackground(ToolBox, ms_BackgroundTexture, 128.0f, Brightness);
3620 ToolBox.Margin(2.0f, &ToolBox);
3622 RenderBackground(ToolBar, ms_BackgroundTexture, 128.0f, Brightness);
3623 ToolBar.Margin(2.0f, &ToolBar);
3624 ToolBar.VSplitLeft(100.0f, &CModeBar, &ToolBar);
3626 RenderBackground(StatusBar, ms_BackgroundTexture, 128.0f, Brightness);
3627 StatusBar.Margin(2.0f, &StatusBar);
3629 // do the toolbar
3630 if(m_Mode == MODE_LAYERS)
3631 DoToolbar(ToolBar);
3633 if(m_ShowEnvelopeEditor)
3635 RenderBackground(EnvelopeEditor, ms_BackgroundTexture, 128.0f, Brightness);
3636 EnvelopeEditor.Margin(2.0f, &EnvelopeEditor);
3641 if(m_Mode == MODE_LAYERS)
3642 RenderLayers(ToolBox, ToolBar, View);
3643 else if(m_Mode == MODE_IMAGES)
3644 RenderImages(ToolBox, ToolBar, View);
3646 Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h);
3648 if(m_GuiActive)
3650 RenderMenubar(MenuBar);
3652 RenderModebar(CModeBar);
3653 if(m_ShowEnvelopeEditor)
3654 RenderEnvelopeEditor(EnvelopeEditor);
3657 if(m_Dialog == DIALOG_FILE)
3659 static int s_NullUiTarget = 0;
3660 UI()->SetHotItem(&s_NullUiTarget);
3661 RenderFileDialog();
3664 if(m_PopupEventActivated)
3666 static int s_PopupID = 0;
3667 UiInvokePopupMenu(&s_PopupID, 0, Width/2.0f-200.0f, Height/2.0f-100.0f, 400.0f, 200.0f, PopupEvent);
3668 m_PopupEventActivated = false;
3669 m_PopupEventWasActivated = true;
3673 UiDoPopupMenu();
3675 if(m_GuiActive)
3676 RenderStatusbar(StatusBar);
3679 if(g_Config.m_EdShowkeys)
3681 Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h);
3682 CTextCursor Cursor;
3683 TextRender()->SetCursor(&Cursor, View.x+10, View.y+View.h-24-10, 24.0f, TEXTFLAG_RENDER);
3685 int NKeys = 0;
3686 for(int i = 0; i < KEY_LAST; i++)
3688 if(Input()->KeyPressed(i))
3690 if(NKeys)
3691 TextRender()->TextEx(&Cursor, " + ", -1);
3692 TextRender()->TextEx(&Cursor, Input()->KeyName(i), -1);
3693 NKeys++;
3698 if(m_ShowMousePointer)
3700 // render butt ugly mouse cursor
3701 float mx = UI()->MouseX();
3702 float my = UI()->MouseY();
3703 Graphics()->TextureSet(ms_CursorTexture);
3704 Graphics()->QuadsBegin();
3705 if(ms_pUiGotContext == UI()->HotItem())
3706 Graphics()->SetColor(1,0,0,1);
3707 IGraphics::CQuadItem QuadItem(mx,my, 16.0f, 16.0f);
3708 Graphics()->QuadsDrawTL(&QuadItem, 1);
3709 Graphics()->QuadsEnd();
3713 void CEditor::Reset(bool CreateDefault)
3715 m_Map.Clean();
3717 // create default layers
3718 if(CreateDefault)
3719 m_Map.CreateDefault(ms_EntitiesTexture);
3725 m_SelectedLayer = 0;
3726 m_SelectedGroup = 0;
3727 m_SelectedQuad = -1;
3728 m_SelectedPoints = 0;
3729 m_SelectedEnvelope = 0;
3730 m_SelectedImage = 0;
3732 m_WorldOffsetX = 0;
3733 m_WorldOffsetY = 0;
3734 m_EditorOffsetX = 0.0f;
3735 m_EditorOffsetY = 0.0f;
3737 m_WorldZoom = 1.0f;
3738 m_ZoomLevel = 200;
3740 m_MouseDeltaX = 0;
3741 m_MouseDeltaY = 0;
3742 m_MouseDeltaWx = 0;
3743 m_MouseDeltaWy = 0;
3745 m_Map.m_Modified = false;
3747 m_ShowEnvelopePreview = 0;
3750 int CEditor::GetLineDistance()
3752 int LineDistance = 512;
3754 if(m_ZoomLevel <= 100)
3755 LineDistance = 16;
3756 else if(m_ZoomLevel <= 250)
3757 LineDistance = 32;
3758 else if(m_ZoomLevel <= 450)
3759 LineDistance = 64;
3760 else if(m_ZoomLevel <= 850)
3761 LineDistance = 128;
3762 else if(m_ZoomLevel <= 1550)
3763 LineDistance = 256;
3765 return LineDistance;
3768 void CEditorMap::DeleteEnvelope(int Index)
3770 if(Index < 0 || Index >= m_lEnvelopes.size())
3771 return;
3773 m_Modified = true;
3775 // fix links between envelopes and quads
3776 for(int i = 0; i < m_lGroups.size(); ++i)
3777 for(int j = 0; j < m_lGroups[i]->m_lLayers.size(); ++j)
3778 if(m_lGroups[i]->m_lLayers[j]->m_Type == LAYERTYPE_QUADS)
3780 CLayerQuads *Layer = static_cast<CLayerQuads *>(m_lGroups[i]->m_lLayers[j]);
3781 for(int k = 0; k < Layer->m_lQuads.size(); ++k)
3783 if(Layer->m_lQuads[k].m_PosEnv == Index)
3784 Layer->m_lQuads[k].m_PosEnv = -1;
3785 else if(Layer->m_lQuads[k].m_PosEnv > Index)
3786 Layer->m_lQuads[k].m_PosEnv--;
3787 if(Layer->m_lQuads[k].m_ColorEnv == Index)
3788 Layer->m_lQuads[k].m_ColorEnv = -1;
3789 else if(Layer->m_lQuads[k].m_ColorEnv > Index)
3790 Layer->m_lQuads[k].m_ColorEnv--;
3794 m_lEnvelopes.remove_index(Index);
3797 void CEditorMap::MakeGameLayer(CLayer *pLayer)
3799 m_pGameLayer = (CLayerGame *)pLayer;
3800 m_pGameLayer->m_pEditor = m_pEditor;
3801 m_pGameLayer->m_TexID = m_pEditor->ms_EntitiesTexture;
3804 void CEditorMap::MakeGameGroup(CLayerGroup *pGroup)
3806 m_pGameGroup = pGroup;
3807 m_pGameGroup->m_GameGroup = true;
3808 str_copy(m_pGameGroup->m_aName, "Game", sizeof(m_pGameGroup->m_aName));
3813 void CEditorMap::Clean()
3815 m_lGroups.delete_all();
3816 m_lEnvelopes.delete_all();
3817 m_lImages.delete_all();
3819 m_pGameLayer = 0x0;
3820 m_pGameGroup = 0x0;
3822 m_Modified = false;
3825 void CEditorMap::CreateDefault(int EntitiesTexture)
3827 // add background
3828 CLayerGroup *pGroup = NewGroup();
3829 pGroup->m_ParallaxX = 0;
3830 pGroup->m_ParallaxY = 0;
3831 CLayerQuads *pLayer = new CLayerQuads;
3832 pLayer->m_pEditor = m_pEditor;
3833 CQuad *pQuad = pLayer->NewQuad();
3834 const int Width = 800000;
3835 const int Height = 600000;
3836 pQuad->m_aPoints[0].x = pQuad->m_aPoints[2].x = -Width;
3837 pQuad->m_aPoints[1].x = pQuad->m_aPoints[3].x = Width;
3838 pQuad->m_aPoints[0].y = pQuad->m_aPoints[1].y = -Height;
3839 pQuad->m_aPoints[2].y = pQuad->m_aPoints[3].y = Height;
3840 pQuad->m_aColors[0].r = pQuad->m_aColors[1].r = 94;
3841 pQuad->m_aColors[0].g = pQuad->m_aColors[1].g = 132;
3842 pQuad->m_aColors[0].b = pQuad->m_aColors[1].b = 174;
3843 pQuad->m_aColors[2].r = pQuad->m_aColors[3].r = 204;
3844 pQuad->m_aColors[2].g = pQuad->m_aColors[3].g = 232;
3845 pQuad->m_aColors[2].b = pQuad->m_aColors[3].b = 255;
3846 pGroup->AddLayer(pLayer);
3848 // add game layer
3849 MakeGameGroup(NewGroup());
3850 MakeGameLayer(new CLayerGame(50, 50));
3851 m_pGameGroup->AddLayer(m_pGameLayer);
3854 void CEditor::Init()
3856 m_pInput = Kernel()->RequestInterface<IInput>();
3857 m_pClient = Kernel()->RequestInterface<IClient>();
3858 m_pConsole = Kernel()->RequestInterface<IConsole>();
3859 m_pGraphics = Kernel()->RequestInterface<IGraphics>();
3860 m_pTextRender = Kernel()->RequestInterface<ITextRender>();
3861 m_pStorage = Kernel()->RequestInterface<IStorage>();
3862 m_RenderTools.m_pGraphics = m_pGraphics;
3863 m_RenderTools.m_pUI = &m_UI;
3864 m_UI.SetGraphics(m_pGraphics, m_pTextRender);
3865 m_Map.m_pEditor = this;
3867 ms_CheckerTexture = Graphics()->LoadTexture("editor/checker.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, 0);
3868 ms_BackgroundTexture = Graphics()->LoadTexture("editor/background.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, 0);
3869 ms_CursorTexture = Graphics()->LoadTexture("editor/cursor.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, 0);
3870 ms_EntitiesTexture = Graphics()->LoadTexture("editor/entities.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, 0);
3872 m_TilesetPicker.m_pEditor = this;
3873 m_TilesetPicker.MakePalette();
3874 m_TilesetPicker.m_Readonly = true;
3876 m_Brush.m_pMap = &m_Map;
3878 Reset();
3879 m_Map.m_Modified = false;
3882 void CEditor::DoMapBorder()
3884 CLayerTiles *pT = (CLayerTiles *)GetSelectedLayerType(0, LAYERTYPE_TILES);
3886 for(int i = 0; i < pT->m_Width*2; ++i)
3887 pT->m_pTiles[i].m_Index = 1;
3889 for(int i = 0; i < pT->m_Width*pT->m_Height; ++i)
3891 if(i%pT->m_Width < 2 || i%pT->m_Width > pT->m_Width-3)
3892 pT->m_pTiles[i].m_Index = 1;
3895 for(int i = (pT->m_Width*(pT->m_Height-2)); i < pT->m_Width*pT->m_Height; ++i)
3896 pT->m_pTiles[i].m_Index = 1;
3899 void CEditor::UpdateAndRender()
3901 static float s_MouseX = 0.0f;
3902 static float s_MouseY = 0.0f;
3904 if(m_Animate)
3905 m_AnimateTime = (time_get()-m_AnimateStart)/(float)time_freq();
3906 else
3907 m_AnimateTime = 0;
3908 ms_pUiGotContext = 0;
3910 // handle mouse movement
3911 float mx, my, Mwx, Mwy;
3912 float rx, ry;
3914 Input()->MouseRelative(&rx, &ry);
3915 UI()->ConvertMouseMove(&rx, &ry);
3916 m_MouseDeltaX = rx;
3917 m_MouseDeltaY = ry;
3919 if(!m_LockMouse)
3921 s_MouseX += rx;
3922 s_MouseY += ry;
3925 s_MouseX = clamp(s_MouseX, 0.0f, UI()->Screen()->w);
3926 s_MouseY = clamp(s_MouseY, 0.0f, UI()->Screen()->h);
3928 // update the ui
3929 mx = s_MouseX;
3930 my = s_MouseY;
3931 Mwx = 0;
3932 Mwy = 0;
3934 // fix correct world x and y
3935 CLayerGroup *g = GetSelectedGroup();
3936 if(g)
3938 float aPoints[4];
3939 g->Mapping(aPoints);
3941 float WorldWidth = aPoints[2]-aPoints[0];
3942 float WorldHeight = aPoints[3]-aPoints[1];
3944 Mwx = aPoints[0] + WorldWidth * (s_MouseX/UI()->Screen()->w);
3945 Mwy = aPoints[1] + WorldHeight * (s_MouseY/UI()->Screen()->h);
3946 m_MouseDeltaWx = m_MouseDeltaX*(WorldWidth / UI()->Screen()->w);
3947 m_MouseDeltaWy = m_MouseDeltaY*(WorldHeight / UI()->Screen()->h);
3950 int Buttons = 0;
3951 if(Input()->KeyPressed(KEY_MOUSE_1)) Buttons |= 1;
3952 if(Input()->KeyPressed(KEY_MOUSE_2)) Buttons |= 2;
3953 if(Input()->KeyPressed(KEY_MOUSE_3)) Buttons |= 4;
3955 UI()->Update(mx,my,Mwx,Mwy,Buttons);
3958 // toggle gui
3959 if(Input()->KeyDown(KEY_TAB))
3960 m_GuiActive = !m_GuiActive;
3962 if(Input()->KeyDown(KEY_F10))
3963 m_ShowMousePointer = false;
3965 Render();
3967 if(Input()->KeyDown(KEY_F10))
3969 Graphics()->TakeScreenshot(0);
3970 m_ShowMousePointer = true;
3973 Input()->ClearEvents();
3976 IEditor *CreateEditor() { return new CEditor; }