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. */
3#ifndef GAME_EDITOR_EDITOR_H
4#define GAME_EDITOR_EDITOR_H
5
6#include "editor_history.h"
7#include "editor_server_settings.h"
8#include "editor_trackers.h"
9#include "editor_ui.h"
10#include "font_typer.h"
11#include "layer_selector.h"
12#include "map_view.h"
13#include "quad_art.h"
14
15#include <base/bezier.h>
16#include <base/fs.h>
17
18#include <engine/editor.h>
19#include <engine/graphics.h>
20
21#include <game/client/ui.h>
22#include <game/client/ui_listbox.h>
23#include <game/editor/enums.h>
24#include <game/editor/envelope_editor.h>
25#include <game/editor/file_browser.h>
26#include <game/editor/mapitems/envelope.h>
27#include <game/editor/mapitems/layer.h>
28#include <game/editor/mapitems/layer_front.h>
29#include <game/editor/mapitems/layer_game.h>
30#include <game/editor/mapitems/layer_group.h>
31#include <game/editor/mapitems/layer_quads.h>
32#include <game/editor/mapitems/layer_sounds.h>
33#include <game/editor/mapitems/layer_speedup.h>
34#include <game/editor/mapitems/layer_switch.h>
35#include <game/editor/mapitems/layer_tele.h>
36#include <game/editor/mapitems/layer_tiles.h>
37#include <game/editor/mapitems/layer_tune.h>
38#include <game/editor/mapitems/map.h>
39#include <game/editor/prompt.h>
40#include <game/editor/quick_action.h>
41#include <game/mapitems.h>
42
43#include <deque>
44#include <functional>
45#include <map>
46#include <memory>
47#include <string>
48#include <vector>
49
50template<typename T>
51using FDropdownRenderCallback = std::function<void(const T &, char (&aOutput)[128], std::vector<STextColorSplit> &)>;
52
53// CEditor SPECIFIC
54enum
55{
56 MODE_LAYERS = 0,
57 MODE_IMAGES,
58 MODE_SOUNDS,
59
60 NUM_MODES,
61};
62
63enum
64{
65 DIALOG_NONE = 0,
66 DIALOG_FILE,
67 DIALOG_MAPSETTINGS_ERROR,
68 DIALOG_QUICK_PROMPT,
69
70 // The font typer component sets m_Dialog
71 // while it is active to make sure no other component
72 // interprets the key presses
73 DIALOG_PSEUDO_FONT_TYPER,
74};
75
76class CProperty
77{
78public:
79 CProperty(const char *pName, int Value, int Type, int Min, int Max) :
80 m_pName(pName), m_Value(Value), m_Type(Type), m_Min(Min), m_Max(Max) {}
81
82 CProperty(std::nullptr_t) :
83 m_pName(nullptr), m_Value(0), m_Type(0), m_Min(0), m_Max(0) {}
84
85 const char *m_pName;
86 int m_Value;
87 int m_Type;
88 int m_Min;
89 int m_Max;
90};
91
92enum
93{
94 PROPTYPE_NULL = 0,
95 PROPTYPE_BOOL,
96 PROPTYPE_INT,
97 PROPTYPE_ANGLE_SCROLL,
98 PROPTYPE_COLOR,
99 PROPTYPE_IMAGE,
100 PROPTYPE_ENVELOPE,
101 PROPTYPE_SHIFT,
102 PROPTYPE_SOUND,
103 PROPTYPE_AUTOMAPPER,
104 PROPTYPE_AUTOMAPPER_REFERENCE,
105};
106
107class CEditor : public IEditor
108{
109 class IInput *m_pInput = nullptr;
110 class IClient *m_pClient = nullptr;
111 class IConfigManager *m_pConfigManager = nullptr;
112 class CConfig *m_pConfig = nullptr;
113 class IEngine *m_pEngine = nullptr;
114 class IGraphics *m_pGraphics = nullptr;
115 class ITextRender *m_pTextRender = nullptr;
116 class ISound *m_pSound = nullptr;
117 class IStorage *m_pStorage = nullptr;
118 CRenderMap m_RenderMap;
119 CUi m_UI;
120
121 std::vector<std::reference_wrapper<CEditorComponent>> m_vComponents;
122 CMapView m_MapView;
123 CEnvelopeEditor m_EnvelopeEditor;
124 CLayerSelector m_LayerSelector;
125 CFileBrowser m_FileBrowser;
126 CPrompt m_Prompt;
127 CFontTyper m_FontTyper;
128 CQuadKnife m_QuadKnife;
129
130 IGraphics::CTextureHandle m_EntitiesTexture;
131
132 IGraphics::CTextureHandle m_FrontTexture;
133 IGraphics::CTextureHandle m_TeleTexture;
134 IGraphics::CTextureHandle m_SpeedupTexture;
135 IGraphics::CTextureHandle m_SwitchTexture;
136 IGraphics::CTextureHandle m_TuneTexture;
137
138 enum EPreviewState
139 {
140 PREVIEW_UNLOADED,
141 PREVIEW_LOADED,
142 PREVIEW_ERROR,
143 };
144
145 std::shared_ptr<CLayerGroup> m_apSavedBrushes[10];
146 static constexpr ColorRGBA ms_DefaultPropColor = ColorRGBA(1, 1, 1, 0.5f);
147
148public:
149 class IInput *Input() const { return m_pInput; }
150 class IClient *Client() const { return m_pClient; }
151 class IConfigManager *ConfigManager() const { return m_pConfigManager; }
152 class CConfig *Config() const { return m_pConfig; }
153 class IEngine *Engine() const { return m_pEngine; }
154 class IGraphics *Graphics() const { return m_pGraphics; }
155 class ISound *Sound() const { return m_pSound; }
156 class ITextRender *TextRender() const { return m_pTextRender; }
157 class IStorage *Storage() const { return m_pStorage; }
158 CUi *Ui() { return &m_UI; }
159 CRenderMap *RenderMap() { return &m_RenderMap; }
160
161 CEditorMap *Map();
162 const CEditorMap *Map() const;
163 CMapView *MapView() { return &m_MapView; }
164 const CMapView *MapView() const { return &m_MapView; }
165 CQuadKnife *QuadKnife() { return &m_QuadKnife; }
166 const CQuadKnife *QuadKnife() const { return &m_QuadKnife; }
167 CLayerSelector *LayerSelector() { return &m_LayerSelector; }
168
169 void FillGameTiles(EGameTileOp FillTile) const;
170 bool CanFillGameTiles() const;
171 void AddQuadOrSound();
172 void AddGroup();
173 void AddSoundLayer();
174 void AddTileLayer();
175 void AddQuadsLayer();
176 void AddSwitchLayer();
177 void AddFrontLayer();
178 void AddTuneLayer();
179 void AddSpeedupLayer();
180 void AddTeleLayer();
181 void DeleteSelectedLayer();
182 void LayerSelectImage();
183 bool IsNonGameTileLayerSelected() const;
184 void MapDetails();
185 void TestMapLocally();
186 void GotoPosition();
187#define REGISTER_QUICK_ACTION(name, text, callback, disabled, active, button_color, description) CQuickAction m_QuickAction##name;
188#include <game/editor/quick_actions.h>
189#undef REGISTER_QUICK_ACTION
190
191 CEditor() :
192#define REGISTER_QUICK_ACTION(name, text, callback, disabled, active, button_color, description) m_QuickAction##name(text, description, callback, disabled, active, button_color),
193#include <game/editor/quick_actions.h>
194#undef REGISTER_QUICK_ACTION
195 m_Dialog(DIALOG_NONE)
196 {
197 m_EntitiesTexture.Invalidate();
198 m_FrontTexture.Invalidate();
199 m_TeleTexture.Invalidate();
200 m_SpeedupTexture.Invalidate();
201 m_SwitchTexture.Invalidate();
202 m_TuneTexture.Invalidate();
203
204 m_Mode = MODE_LAYERS;
205
206 m_BrushColorEnabled = true;
207
208 m_PopupEventActivated = false;
209 m_PopupEventWasActivated = false;
210
211 m_ToolbarPreviewSound = -1;
212
213 m_SelectEntitiesImage = "DDNet";
214
215 m_ShowMousePointer = true;
216
217 m_GuiActive = true;
218
219 m_ShowTileInfo = SHOW_TILE_OFF;
220
221 for(size_t i = 0; i < std::size(m_aSavedColors); ++i)
222 {
223 m_aSavedColors[i] = color_cast<ColorRGBA>(hsl: ColorHSLA(i / (float)std::size(m_aSavedColors), 1.0f, 0.5f));
224 }
225
226 m_CheckerTexture.Invalidate();
227 for(auto &CursorTexture : m_aCursorTextures)
228 CursorTexture.Invalidate();
229
230 m_CursorType = CURSOR_NORMAL;
231
232 // DDRace
233
234 m_TeleNumber = 1;
235 m_TeleCheckpointNumber = 1;
236 m_ViewTeleNumber = 0;
237
238 m_TuningNumber = 1;
239 m_ViewTuning = 0;
240
241 m_SwitchNumber = 1;
242 m_SwitchDelay = 0;
243 m_SpeedupForce = 50;
244 m_SpeedupMaxSpeed = 0;
245 m_SpeedupAngle = 0;
246 m_LargeLayerWasWarned = false;
247 m_PreventUnusedTilesWasWarned = false;
248 m_AllowPlaceUnusedTiles = EUnusedEntities::NOT_ALLOWED;
249 m_BrushDrawDestructive = true;
250 }
251
252 void Init() override;
253 void OnUpdate() override;
254 void OnRender() override;
255 void OnActivate() override;
256 void OnWindowResize() override;
257 void OnClose() override;
258 void OnDialogClose();
259 bool HasUnsavedData() const override;
260 void UpdateMentions() override { m_Mentions++; }
261 void ResetMentions() override { m_Mentions = 0; }
262 void OnIngameMoved() override { m_IngameMoved = true; }
263 void ResetIngameMoved() override { m_IngameMoved = false; }
264
265 void HandleCursorMovement();
266 void OnInput(const IInput::CEvent &Event);
267 void MouseAxisLock(vec2 &CursorRel);
268 vec2 m_MouseAxisInitialPos = vec2(0.0f, 0.0f);
269 enum class EAxisLock
270 {
271 START,
272 NONE,
273 HORIZONTAL,
274 VERTICAL,
275 } m_MouseAxisLockState = EAxisLock::START;
276
277 /**
278 * Global time when the autosave was last updated in the @link HandleAutosave @endlink function.
279 * This is used so that the autosave does not immediately activate when reopening the editor after
280 * a longer time of inactivity, as autosaves are only updated while the editor is open.
281 */
282 float m_LastAutosaveUpdateTime = -1.0f;
283 void HandleAutosave();
284 std::deque<std::shared_ptr<CDataFileWriterFinishJob>> m_WriterFinishJobs;
285 void HandleWriterFinishJobs();
286 bool IsSaving(const char *pFilename) const;
287 void UpdateMapDisplayNames();
288
289 // TODO: The name of the ShowFileDialogError function is not accurate anymore, this is used for generic error messages.
290 // Popups in UI should be shared_ptrs to make this even more generic.
291 class CStringKeyComparator
292 {
293 public:
294 bool operator()(const char *pLhs, const char *pRhs) const;
295 };
296 std::map<const char *, CUi::SMessagePopupContext *, CStringKeyComparator> m_PopupMessageContexts;
297 [[gnu::format(printf, 2, 3)]] void ShowFileDialogError(const char *pFormat, ...);
298
299 void Reset();
300 void AddDefaultMap();
301 void CloseMap(size_t Index, bool Confirm);
302 bool Save(const char *pFilename) override;
303 bool Load(const char *pFilename, int StorageType) override;
304 bool HandleMapDrop(const char *pFilename, int StorageType) override;
305 void LoadIngameMap();
306 void Render();
307
308 void UpdateBrushPicker();
309 void RenderPressedKeys(CUIRect View);
310 void RenderSavingIndicator(CUIRect View);
311 void FreeDynamicPopupMenus();
312 void UpdateColorPipette();
313 void RenderMousePointer();
314 void RenderIngameEntities(const CLayerGroup &Group, const CLayerTiles &TilesLayer);
315
316 template<typename E>
317 SEditResult<E> DoPropertiesWithState(CUIRect *pToolbox, CProperty *pProps, int *pIds, int *pNewVal, const std::vector<ColorRGBA> &vColors = {});
318 int DoProperties(CUIRect *pToolbox, CProperty *pProps, int *pIds, int *pNewVal, const std::vector<ColorRGBA> &vColors = {});
319
320 CUi::SColorPickerPopupContext m_ColorPickerPopupContext;
321 const void *m_pColorPickerPopupActiveId = nullptr;
322 void DoColorPickerButton(const void *pId, const CUIRect *pRect, ColorRGBA Color, const std::function<void(ColorRGBA Color)> &SetColor);
323
324 int m_Mode;
325 int m_Dialog;
326 char m_aTooltip[256] = "";
327
328 bool m_BrushColorEnabled;
329
330 enum
331 {
332 POPEVENT_EXIT = 0,
333 POPEVENT_CLOSE_MAP,
334 POPEVENT_LARGELAYER,
335 POPEVENT_PREVENTUNUSEDTILES,
336 POPEVENT_IMAGEDIV16,
337 POPEVENT_IMAGE_MAX,
338 POPEVENT_SOUND_MAX,
339 POPEVENT_PLACE_BORDER_TILES,
340 POPEVENT_TILE_ART_BIG_IMAGE,
341 POPEVENT_TILE_ART_MANY_COLORS,
342 POPEVENT_TILE_ART_TOO_MANY_COLORS,
343 POPEVENT_QUAD_ART_BIG_IMAGE,
344 POPEVENT_REMOVE_USED_IMAGE,
345 POPEVENT_REMOVE_USED_SOUND,
346 POPEVENT_RESTART_SERVER,
347 POPEVENT_RESTARTING_SERVER,
348 };
349
350 int m_PopupEventType;
351 int m_PopupEventActivated;
352 int m_PopupEventWasActivated;
353 bool m_LargeLayerWasWarned;
354 bool m_PreventUnusedTilesWasWarned;
355
356 enum class EUnusedEntities
357 {
358 ALLOWED_IMPLICIT = -1,
359 NOT_ALLOWED = 0,
360 ALLOWED_EXPLICIT = 1,
361 };
362 EUnusedEntities m_AllowPlaceUnusedTiles;
363 bool IsAllowPlaceUnusedTiles() const;
364
365 bool m_BrushDrawDestructive;
366
367 int m_Mentions = 0;
368 bool m_IngameMoved = false;
369
370 int m_ToolbarPreviewSound;
371
372 std::vector<std::string> m_vSelectEntitiesFiles;
373 std::string m_SelectEntitiesImage;
374
375 bool m_ShowMousePointer;
376 bool m_GuiActive;
377
378 const void *m_pContainerPanned;
379 const void *m_pContainerPannedLast;
380
381 enum EShowTile
382 {
383 SHOW_TILE_OFF,
384 SHOW_TILE_DECIMAL,
385 SHOW_TILE_HEXADECIMAL
386 };
387 EShowTile m_ShowTileInfo;
388
389 enum EExtraEditor
390 {
391 EXTRAEDITOR_NONE = -1,
392 EXTRAEDITOR_ENVELOPES,
393 EXTRAEDITOR_SERVER_SETTINGS,
394 EXTRAEDITOR_HISTORY,
395 NUM_EXTRAEDITORS,
396 };
397 EExtraEditor m_ActiveExtraEditor = EXTRAEDITOR_NONE;
398 float m_aExtraEditorSplits[NUM_EXTRAEDITORS] = {250.0f, 250.0f, 250.0f};
399 float m_ToolBoxWidth = 100.0f;
400
401 bool m_ShowEnvelopePreview = false;
402 enum class EEnvelopePreview
403 {
404 NONE,
405 SELECTED,
406 ALL,
407 };
408 EEnvelopePreview m_ActiveEnvelopePreview = EEnvelopePreview::NONE;
409 enum class EQuadEnvelopePointOperation
410 {
411 NONE = 0,
412 MOVE,
413 ROTATE,
414 };
415 EQuadEnvelopePointOperation m_QuadEnvelopePointOperation = EQuadEnvelopePointOperation::NONE;
416
417 bool m_ShowPicker = false;
418 bool m_ShowPickerToggle = false;
419
420 // Color palette and pipette
421 ColorRGBA m_aSavedColors[8];
422 ColorRGBA m_PipetteColor = ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f);
423 bool m_ColorPipetteActive = false;
424
425 IGraphics::CTextureHandle m_CheckerTexture;
426
427 enum ECursorType
428 {
429 CURSOR_NORMAL,
430 CURSOR_RESIZE_V,
431 CURSOR_RESIZE_H,
432 NUM_CURSORS
433 };
434 IGraphics::CTextureHandle m_aCursorTextures[ECursorType::NUM_CURSORS];
435 ECursorType m_CursorType;
436
437 IGraphics::CTextureHandle GetEntitiesTexture();
438
439 std::unique_ptr<CEditorMap> m_pToolsMap;
440 std::shared_ptr<CLayerGroup> m_pBrush;
441 std::shared_ptr<CLayerTiles> m_pTilesetPicker;
442 std::shared_ptr<CLayerQuads> m_pQuadsetPicker;
443
444 const void *m_pUiGotContext = nullptr;
445
446 CMapSettingsBackend m_MapSettingsBackend;
447
448 // editor_ui.cpp
449 void UpdateTooltip(const void *pId, const CUIRect *pRect, const char *pToolTip);
450 ColorRGBA GetButtonColor(const void *pId, int Checked);
451 int DoButtonLogic(const void *pId, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip);
452 int DoButton_Editor(const void *pId, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip);
453 int DoButton_Env(const void *pId, const char *pText, int Checked, const CUIRect *pRect, const char *pToolTip, ColorRGBA Color, int Corners);
454 int DoButton_Ex(const void *pId, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip, int Corners, float FontSize = EditorFontSizes::MENU, int Align = TEXTALIGN_MC);
455 int DoButton_FontIcon(const void *pId, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip, int Corners, float FontSize = 10.0f);
456 int DoButton_MenuItem(const void *pId, const char *pText, int Checked, const CUIRect *pRect, int Flags = BUTTONFLAG_LEFT, const char *pToolTip = nullptr);
457 int DoButton_DraggableEx(const void *pId, const char *pText, int Checked, const CUIRect *pRect, bool *pClicked, bool *pAbrupted, int Flags, const char *pToolTip = nullptr, int Corners = IGraphics::CORNER_ALL, float FontSize = 10.0f);
458 bool DoEditBox(CLineInput *pLineInput, const CUIRect *pRect, float FontSize, int Corners = IGraphics::CORNER_ALL, const char *pToolTip = nullptr, const std::vector<STextColorSplit> &vColorSplits = {});
459 bool DoClearableEditBox(CLineInput *pLineInput, const CUIRect *pRect, float FontSize, int Corners = IGraphics::CORNER_ALL, const char *pToolTip = nullptr, const std::vector<STextColorSplit> &vColorSplits = {});
460 SEditResult<int> UiDoValueSelector(const void *pId, CUIRect *pRect, const char *pLabel, int Current, int Min, int Max, int Step, float Scale, const char *pToolTip, bool IsDegree = false, bool IsHex = false, int Corners = IGraphics::CORNER_ALL, const ColorRGBA *pColor = nullptr, bool ShowValue = true);
461 void RenderBackground(CUIRect View, IGraphics::CTextureHandle Texture, float Size, float Brightness) const;
462
463 // editor_server_settings.cpp
464 void DoMapSettingsEditBox(CMapSettingsBackend::CContextWithInput *pContext, const CUIRect *pRect, float FontSize, float DropdownMaxHeight, int Corners = IGraphics::CORNER_ALL, const char *pToolTip = nullptr);
465 template<typename T>
466 int DoEditBoxDropdown(SEditBoxDropdownContext *pDropdown, CLineInput *pLineInput, const CUIRect *pEditBoxRect, int x, float MaxHeight, bool AutoWidth, const std::vector<T> &vData, const FDropdownRenderCallback<T> &pfnMatchCallback);
467 template<typename T>
468 int RenderEditBoxDropdown(SEditBoxDropdownContext *pDropdown, CUIRect View, CLineInput *pLineInput, int x, float MaxHeight, bool AutoWidth, const std::vector<T> &vData, const FDropdownRenderCallback<T> &pfnMatchCallback);
469
470 // For tile art popups
471 CImageInfo m_TileArtImageInfo;
472 char m_aTileArtFilename[IO_MAX_PATH_LENGTH];
473 void TileArtCheckColors();
474
475 // For quad art popups
476 CImageInfo m_QuadArtImageInfo;
477 CQuadArtParameters m_QuadArtParameters;
478
479 static CUi::EPopupMenuFunctionResult PopupMenuFile(void *pContext, CUIRect View, bool Active);
480 static CUi::EPopupMenuFunctionResult PopupMenuTools(void *pContext, CUIRect View, bool Active);
481 static CUi::EPopupMenuFunctionResult PopupMenuSettings(void *pContext, CUIRect View, bool Active);
482 class CPopupMapTab : public SPopupMenuId
483 {
484 public:
485 CEditor *m_pEditor;
486 size_t m_SelectedMap;
487 static CUi::EPopupMenuFunctionResult Render(void *pContext, CUIRect View, bool Active);
488
489 private:
490 const char m_CloseButtonId = 0;
491 const char m_CopyNameButtonId = 0;
492 const char m_CopyPathButtonId = 0;
493 const char m_ShowFileButtonId = 0;
494 };
495 CPopupMapTab m_PopupMapTab;
496 static CUi::EPopupMenuFunctionResult PopupGroup(void *pContext, CUIRect View, bool Active);
497 struct SLayerPopupContext : public SPopupMenuId
498 {
499 CEditor *m_pEditor;
500 std::vector<std::shared_ptr<CLayerTiles>> m_vpLayers;
501 std::vector<int> m_vLayerIndices;
502 CLayerTiles::SCommonPropState m_CommonPropState;
503 };
504 static CUi::EPopupMenuFunctionResult PopupLayer(void *pContext, CUIRect View, bool Active);
505 class CQuadPopupContext : public SPopupMenuId
506 {
507 public:
508 CEditor *m_pEditor;
509 int m_SelectedQuadIndex;
510 int m_Color;
511 };
512 CQuadPopupContext m_QuadPopupContext;
513 static CUi::EPopupMenuFunctionResult PopupQuad(void *pContext, CUIRect View, bool Active);
514 static CUi::EPopupMenuFunctionResult PopupSource(void *pContext, CUIRect View, bool Active);
515 class CPointPopupContext : public SPopupMenuId
516 {
517 public:
518 CEditor *m_pEditor;
519 int m_SelectedQuadPoint;
520 int m_SelectedQuadIndex;
521 };
522 CPointPopupContext m_PointPopupContext;
523 static CUi::EPopupMenuFunctionResult PopupPoint(void *pContext, CUIRect View, bool Active);
524 static CUi::EPopupMenuFunctionResult PopupImage(void *pContext, CUIRect View, bool Active);
525 static CUi::EPopupMenuFunctionResult PopupSound(void *pContext, CUIRect View, bool Active);
526 static CUi::EPopupMenuFunctionResult PopupMapInfo(void *pContext, CUIRect View, bool Active);
527 static CUi::EPopupMenuFunctionResult PopupEvent(void *pContext, CUIRect View, bool Active);
528 static CUi::EPopupMenuFunctionResult PopupSelectImage(void *pContext, CUIRect View, bool Active);
529 static CUi::EPopupMenuFunctionResult PopupSelectSound(void *pContext, CUIRect View, bool Active);
530 static CUi::EPopupMenuFunctionResult PopupSelectGametileOp(void *pContext, CUIRect View, bool Active);
531 static CUi::EPopupMenuFunctionResult PopupSelectAutomapperConfig(void *pContext, CUIRect View, bool Active);
532 static CUi::EPopupMenuFunctionResult PopupSelectAutomapperReference(void *pContext, CUIRect View, bool Active);
533 static CUi::EPopupMenuFunctionResult PopupTele(void *pContext, CUIRect View, bool Active);
534 static CUi::EPopupMenuFunctionResult PopupSpeedup(void *pContext, CUIRect View, bool Active);
535 static CUi::EPopupMenuFunctionResult PopupSwitch(void *pContext, CUIRect View, bool Active);
536 static CUi::EPopupMenuFunctionResult PopupTune(void *pContext, CUIRect View, bool Active);
537 static CUi::EPopupMenuFunctionResult PopupGoto(void *pContext, CUIRect View, bool Active);
538 static CUi::EPopupMenuFunctionResult PopupEntities(void *pContext, CUIRect View, bool Active);
539 static CUi::EPopupMenuFunctionResult PopupProofMode(void *pContext, CUIRect View, bool Active);
540 static CUi::EPopupMenuFunctionResult PopupAnimateSettings(void *pContext, CUIRect View, bool Active);
541 static CUi::EPopupMenuFunctionResult PopupQuadArt(void *pContext, CUIRect View, bool Active);
542
543 static bool CallbackOpenMap(const char *pFilename, int StorageType, void *pUser);
544 static bool CallbackAppendMap(const char *pFilename, int StorageType, void *pUser);
545 static bool CallbackSaveMap(const char *pFilename, int StorageType, void *pUser);
546 static bool CallbackSaveCopyMap(const char *pFilename, int StorageType, void *pUser);
547 static bool CallbackAddTileArt(const char *pFilepath, int StorageType, void *pUser);
548 static bool CallbackAddQuadArt(const char *pFilepath, int StorageType, void *pUser);
549 static bool CallbackSaveImage(const char *pFilename, int StorageType, void *pUser);
550 static bool CallbackSaveSound(const char *pFilename, int StorageType, void *pUser);
551 static bool CallbackCustomEntities(const char *pFilename, int StorageType, void *pUser);
552
553 void PopupSelectImageInvoke(int Current, float x, float y);
554 int PopupSelectImageResult();
555
556 void PopupSelectGametileOpInvoke(float x, float y);
557 int PopupSelectGameTileOpResult();
558
559 void PopupSelectAutomapperConfigInvoke(int Current, float x, float y);
560 int PopupSelectAutomapperConfigResult();
561
562 void PopupSelectSoundInvoke(int Current, float x, float y);
563 int PopupSelectSoundResult();
564
565 void PopupSelectAutomapperReferenceInvoke(int Current, float x, float y);
566 int PopupSelectAutomapperReferenceResult();
567
568 void DoQuadEnvelopes(const CLayerQuads *pLayerQuads);
569 void DoQuadEnvPoint(const CQuad *pQuad, CEnvelope *pEnvelope, int QuadIndex, int PointIndex);
570 void DoQuadPoint(int LayerIndex, const std::shared_ptr<CLayerQuads> &pLayer, CQuad *pQuad, int QuadIndex, int v);
571 void UpdateHotQuadPoint(const CLayerQuads *pLayer);
572
573 void DoSoundSource(int LayerIndex, CSoundSource *pSource, int Index);
574 void UpdateHotSoundSource(const CLayerSounds *pLayer);
575
576 enum class EAxis
577 {
578 NONE = 0,
579 X,
580 Y,
581 };
582 struct SAxisAlignedBoundingBox
583 {
584 enum
585 {
586 POINT_TL = 0,
587 POINT_TR,
588 POINT_BL,
589 POINT_BR,
590 POINT_CENTER,
591 NUM_POINTS
592 };
593 CPoint m_aPoints[NUM_POINTS];
594 };
595
596 CScrollRegion m_MapTabsScrollRegion;
597 bool m_MapTabsRevealSelected = false;
598 void DoMapTabs(CUIRect MapTabs);
599
600 void DoToolbarLayers(CUIRect Toolbar);
601 void DoToolbarImages(CUIRect Toolbar);
602 void DoToolbarSounds(CUIRect Toolbar);
603 void DoQuad(int LayerIndex, const std::shared_ptr<CLayerQuads> &pLayer, CQuad *pQuad, int Index);
604 void PreparePointDrag(const CQuad *pQuad, int QuadIndex, int PointIndex);
605 void DoPointDrag(CQuad *pQuad, int QuadIndex, int PointIndex, ivec2 Offset);
606 EAxis GetDragAxis(ivec2 Offset) const;
607 void DrawAxis(EAxis Axis, CPoint &OriginalPoint, CPoint &Point) const;
608 void DrawAABB(const SAxisAlignedBoundingBox &AABB, ivec2 Offset) const;
609
610 // Alignment methods
611 // These methods take `OffsetX` and `OffsetY` because the calculations are made with the original positions
612 // of the quad(s), before we started dragging. This allows us to edit `OffsetX` and `OffsetY` based on the previously
613 // calculated alignments.
614 struct SAlignmentInfo
615 {
616 CPoint m_AlignedPoint; // The "aligned" point, which we want to align/snap to
617 union
618 {
619 // The current changing value when aligned to this point. When aligning to a point on the X axis, then the X value is changing because
620 // we aligned the Y values (X axis aligned => Y values are the same, Y axis aligned => X values are the same).
621 int m_X;
622 int m_Y;
623 };
624 EAxis m_Axis; // The axis we are aligning on
625 int m_PointIndex; // The point index we are aligning
626 int m_Diff; // Store the difference
627 };
628 void ComputePointAlignments(const std::shared_ptr<CLayerQuads> &pLayer, CQuad *pQuad, int QuadIndex, int PointIndex, ivec2 Offset, std::vector<SAlignmentInfo> &vAlignments, bool Append = false) const;
629 void ComputePointsAlignments(const std::shared_ptr<CLayerQuads> &pLayer, bool Pivot, ivec2 Offset, std::vector<SAlignmentInfo> &vAlignments) const;
630 void ComputeAABBAlignments(const std::shared_ptr<CLayerQuads> &pLayer, const SAxisAlignedBoundingBox &AABB, ivec2 Offset, std::vector<SAlignmentInfo> &vAlignments) const;
631 void DrawPointAlignments(const std::vector<SAlignmentInfo> &vAlignments, ivec2 Offset) const;
632 void QuadSelectionAABB(const std::shared_ptr<CLayerQuads> &pLayer, SAxisAlignedBoundingBox &OutAABB);
633 void ApplyAlignments(const std::vector<SAlignmentInfo> &vAlignments, ivec2 &Offset);
634 void ApplyAxisAlignment(ivec2 &Offset) const;
635
636 bool ReplaceImage(const char *pFilename, int StorageType, bool CheckDuplicate);
637 static bool ReplaceImageCallback(const char *pFilename, int StorageType, void *pUser);
638 bool ReplaceSound(const char *pFilename, int StorageType, bool CheckDuplicate);
639 static bool ReplaceSoundCallback(const char *pFilename, int StorageType, void *pUser);
640 static bool AddImage(const char *pFilename, int StorageType, void *pUser);
641 static bool AddSound(const char *pFilename, int StorageType, void *pUser);
642
643 static bool IsVanillaImage(const char *pImage);
644
645 enum class ELayerOperation
646 {
647 NONE,
648 CLICK,
649 LAYER_DRAG,
650 GROUP_DRAG,
651 };
652 class CRenderLayersState
653 {
654 public:
655 ELayerOperation m_Operation;
656 ELayerOperation m_PreviousOperation;
657 const void *m_pDraggedButton;
658 float m_InitialMouseY;
659 float m_InitialCutHeight;
660 bool m_ScrollToSelectionNext;
661 int m_InitialGroupIndex;
662 std::vector<int> m_vInitialLayerIndices;
663 const char m_AddGroupButtonId = 0;
664 const char m_CollapseAllButtonId = 0;
665 const SPopupMenuId m_PopupGroupId = {};
666 SLayerPopupContext m_LayerPopupContext;
667
668 void Reset();
669 };
670 CRenderLayersState m_RenderLayersState;
671 void RenderLayers(CUIRect LayersBox);
672
673 void RenderImagesList(CUIRect Toolbox);
674 void RenderSelectedImage(CUIRect View) const;
675 void RenderSounds(CUIRect Toolbox);
676 void RenderModebar(CUIRect View);
677 void RenderStatusbar(CUIRect View, CUIRect *pTooltipRect);
678 void RenderTooltip(CUIRect TooltipRect);
679
680 void RenderMapSettingsErrorDialog();
681 void RenderServerSettingsEditor(CUIRect View, bool ShowServerSettingsEditorLast);
682 static void MapSettingsDropdownRenderCallback(const SPossibleValueMatch &Match, char (&aOutput)[128], std::vector<STextColorSplit> &vColorSplits);
683
684 void RenderEditorHistory(CUIRect View);
685
686 enum class EDragSide // Which side is the drag bar on
687 {
688 BOTTOM,
689 LEFT,
690 TOP,
691 RIGHT,
692 };
693 void DoEditorDragBar(CUIRect View, CUIRect *pDragBar, EDragSide Side, float *pValue, float MinValue = 100.0f, float MaxValue = 400.0f);
694
695 void RenderMenubar(CUIRect Menubar);
696 void ShowHelp();
697 void Exit();
698
699 void DoAudioPreview(CUIRect View, const void *pPlayPauseButtonId, const void *pStopButtonId, const void *pSeekBarId, int SampleId);
700
701 // DDRace
702
703 IGraphics::CTextureHandle GetFrontTexture();
704 IGraphics::CTextureHandle GetTeleTexture();
705 IGraphics::CTextureHandle GetSpeedupTexture();
706 IGraphics::CTextureHandle GetSwitchTexture();
707 IGraphics::CTextureHandle GetTuneTexture();
708
709 unsigned char m_TeleNumber;
710 unsigned char m_TeleCheckpointNumber;
711 unsigned char m_ViewTeleNumber;
712
713 unsigned char m_TuningNumber;
714 unsigned char m_ViewTuning;
715
716 unsigned char m_SpeedupForce;
717 unsigned char m_SpeedupMaxSpeed;
718 short m_SpeedupAngle;
719
720 unsigned char m_SwitchNumber;
721 unsigned char m_SwitchDelay;
722 unsigned char m_ViewSwitch;
723
724 // AdjustValue must be -1, 0 or 1
725 void AdjustBrushSpecialTiles(bool UseNextFree, int AdjustModifiers, int AdjustValue);
726
727private:
728 std::vector<std::unique_ptr<CEditorMap>> m_vpMaps;
729 size_t m_SelectedMap;
730
731 CEditorHistory &ActiveHistory();
732
733 std::map<int, CPoint[5]> m_QuadDragOriginalPoints;
734};
735
736#endif
737