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#include "smooth_value.h"
15
16#include <base/bezier.h>
17#include <base/fs.h>
18
19#include <engine/editor.h>
20#include <engine/graphics.h>
21
22#include <game/client/ui.h>
23#include <game/client/ui_listbox.h>
24#include <game/editor/enums.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 CLayerSelector m_LayerSelector;
124 CFileBrowser m_FileBrowser;
125 CPrompt m_Prompt;
126 CFontTyper m_FontTyper;
127 CQuadKnife m_QuadKnife;
128
129 bool m_EditorWasUsedBefore = false;
130
131 IGraphics::CTextureHandle m_EntitiesTexture;
132
133 IGraphics::CTextureHandle m_FrontTexture;
134 IGraphics::CTextureHandle m_TeleTexture;
135 IGraphics::CTextureHandle m_SpeedupTexture;
136 IGraphics::CTextureHandle m_SwitchTexture;
137 IGraphics::CTextureHandle m_TuneTexture;
138
139 enum EPreviewState
140 {
141 PREVIEW_UNLOADED,
142 PREVIEW_LOADED,
143 PREVIEW_ERROR,
144 };
145
146 std::shared_ptr<CLayerGroup> m_apSavedBrushes[10];
147 static constexpr ColorRGBA ms_DefaultPropColor = ColorRGBA(1, 1, 1, 0.5f);
148
149public:
150 class IInput *Input() const { return m_pInput; }
151 class IClient *Client() const { return m_pClient; }
152 class IConfigManager *ConfigManager() const { return m_pConfigManager; }
153 class CConfig *Config() const { return m_pConfig; }
154 class IEngine *Engine() const { return m_pEngine; }
155 class IGraphics *Graphics() const { return m_pGraphics; }
156 class ISound *Sound() const { return m_pSound; }
157 class ITextRender *TextRender() const { return m_pTextRender; }
158 class IStorage *Storage() const { return m_pStorage; }
159 CUi *Ui() { return &m_UI; }
160 CRenderMap *RenderMap() { return &m_RenderMap; }
161
162 CEditorMap *Map() { return &m_Map; }
163 const CEditorMap *Map() const { return &m_Map; }
164 CMapView *MapView() { return &m_MapView; }
165 const CMapView *MapView() const { return &m_MapView; }
166 CQuadKnife *QuadKnife() { return &m_QuadKnife; }
167 const CQuadKnife *QuadKnife() const { return &m_QuadKnife; }
168 CLayerSelector *LayerSelector() { return &m_LayerSelector; }
169
170 void FillGameTiles(EGameTileOp FillTile) const;
171 bool CanFillGameTiles() const;
172 void AddQuadOrSound();
173 void AddGroup();
174 void AddSoundLayer();
175 void AddTileLayer();
176 void AddQuadsLayer();
177 void AddSwitchLayer();
178 void AddFrontLayer();
179 void AddTuneLayer();
180 void AddSpeedupLayer();
181 void AddTeleLayer();
182 void DeleteSelectedLayer();
183 void LayerSelectImage();
184 bool IsNonGameTileLayerSelected() const;
185 void MapDetails();
186 void TestMapLocally();
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_ZoomEnvelopeX(1.0f, 0.1f, 600.0f),
196 m_ZoomEnvelopeY(640.0f, 0.1f, 32000.0f),
197 m_MapSettingsCommandContext(m_MapSettingsBackend.NewContext(pLineInput: &m_SettingsCommandInput)),
198 m_Map(this)
199 {
200 m_EntitiesTexture.Invalidate();
201 m_FrontTexture.Invalidate();
202 m_TeleTexture.Invalidate();
203 m_SpeedupTexture.Invalidate();
204 m_SwitchTexture.Invalidate();
205 m_TuneTexture.Invalidate();
206
207 m_Mode = MODE_LAYERS;
208 m_Dialog = 0;
209
210 m_BrushColorEnabled = true;
211
212 m_aFilenamePendingLoad[0] = '\0';
213
214 m_PopupEventActivated = false;
215 m_PopupEventWasActivated = false;
216
217 m_ToolbarPreviewSound = -1;
218
219 m_SelectEntitiesImage = "DDNet";
220
221 m_ResetZoomEnvelope = true;
222 m_OffsetEnvelopeX = 0.1f;
223 m_OffsetEnvelopeY = 0.5f;
224
225 m_ShowMousePointer = true;
226
227 m_GuiActive = true;
228 m_PreviewZoom = false;
229
230 m_ShowTileInfo = SHOW_TILE_OFF;
231 m_ShowDetail = true;
232
233 for(size_t i = 0; i < std::size(m_aSavedColors); ++i)
234 {
235 m_aSavedColors[i] = color_cast<ColorRGBA>(hsl: ColorHSLA(i / (float)std::size(m_aSavedColors), 1.0f, 0.5f));
236 }
237
238 m_CheckerTexture.Invalidate();
239 for(auto &CursorTexture : m_aCursorTextures)
240 CursorTexture.Invalidate();
241
242 m_CursorType = CURSOR_NORMAL;
243
244 // DDRace
245
246 m_TeleNumber = 1;
247 m_TeleCheckpointNumber = 1;
248 m_ViewTeleNumber = 0;
249
250 m_TuningNumber = 1;
251 m_ViewTuning = 0;
252
253 m_SwitchNumber = 1;
254 m_SwitchDelay = 0;
255 m_SpeedupForce = 50;
256 m_SpeedupMaxSpeed = 0;
257 m_SpeedupAngle = 0;
258 m_LargeLayerWasWarned = false;
259 m_PreventUnusedTilesWasWarned = false;
260 m_AllowPlaceUnusedTiles = EUnusedEntities::NOT_ALLOWED;
261 m_BrushDrawDestructive = true;
262 }
263
264 void Init() override;
265 void OnUpdate() override;
266 void OnRender() override;
267 void OnActivate() override;
268 void OnWindowResize() override;
269 void OnClose() override;
270 void OnDialogClose();
271 bool HasUnsavedData() const override { return Map()->m_Modified; }
272 void UpdateMentions() override { m_Mentions++; }
273 void ResetMentions() override { m_Mentions = 0; }
274 void OnIngameMoved() override { m_IngameMoved = true; }
275 void ResetIngameMoved() override { m_IngameMoved = false; }
276
277 void HandleCursorMovement();
278 void OnInput(const IInput::CEvent &Event);
279 void MouseAxisLock(vec2 &CursorRel);
280 vec2 m_MouseAxisInitialPos = vec2(0.0f, 0.0f);
281 enum class EAxisLock
282 {
283 START,
284 NONE,
285 HORIZONTAL,
286 VERTICAL,
287 } m_MouseAxisLockState = EAxisLock::START;
288
289 /**
290 * Global time when the autosave was last updated in the @link HandleAutosave @endlink function.
291 * This is used so that the autosave does not immediately activate when reopening the editor after
292 * a longer time of inactivity, as autosaves are only updated while the editor is open.
293 */
294 float m_LastAutosaveUpdateTime = -1.0f;
295 void HandleAutosave();
296 void HandleWriterFinishJobs();
297
298 // TODO: The name of the ShowFileDialogError function is not accurate anymore, this is used for generic error messages.
299 // Popups in UI should be shared_ptrs to make this even more generic.
300 class CStringKeyComparator
301 {
302 public:
303 bool operator()(const char *pLhs, const char *pRhs) const;
304 };
305 std::map<const char *, CUi::SMessagePopupContext *, CStringKeyComparator> m_PopupMessageContexts;
306 [[gnu::format(printf, 2, 3)]] void ShowFileDialogError(const char *pFormat, ...);
307
308 void Reset(bool CreateDefault = true);
309 bool Save(const char *pFilename) override;
310 bool Load(const char *pFilename, int StorageType) override;
311 bool HandleMapDrop(const char *pFilename, int StorageType) override;
312 void LoadCurrentMap();
313 void Render();
314
315 void UpdateBrushPicker();
316 void RenderPressedKeys(CUIRect View);
317 void RenderSavingIndicator(CUIRect View);
318 void FreeDynamicPopupMenus();
319 void UpdateColorPipette();
320 void RenderMousePointer();
321 void RenderIngameEntities(const CLayerGroup &Group, const CLayerTiles &TilesLayer);
322
323 template<typename E>
324 SEditResult<E> DoPropertiesWithState(CUIRect *pToolbox, CProperty *pProps, int *pIds, int *pNewVal, const std::vector<ColorRGBA> &vColors = {});
325 int DoProperties(CUIRect *pToolbox, CProperty *pProps, int *pIds, int *pNewVal, const std::vector<ColorRGBA> &vColors = {});
326
327 CUi::SColorPickerPopupContext m_ColorPickerPopupContext;
328 const void *m_pColorPickerPopupActiveId = nullptr;
329 void DoColorPickerButton(const void *pId, const CUIRect *pRect, ColorRGBA Color, const std::function<void(ColorRGBA Color)> &SetColor);
330
331 int m_Mode;
332 int m_Dialog;
333 char m_aTooltip[256] = "";
334
335 bool m_BrushColorEnabled;
336
337 /**
338 * File which is pending to be loaded by @link POPEVENT_LOADDROP @endlink.
339 */
340 char m_aFilenamePendingLoad[IO_MAX_PATH_LENGTH] = "";
341
342 enum
343 {
344 POPEVENT_EXIT = 0,
345 POPEVENT_LOAD,
346 POPEVENT_LOADCURRENT,
347 POPEVENT_LOADDROP,
348 POPEVENT_NEW,
349 POPEVENT_LARGELAYER,
350 POPEVENT_PREVENTUNUSEDTILES,
351 POPEVENT_IMAGEDIV16,
352 POPEVENT_IMAGE_MAX,
353 POPEVENT_SOUND_MAX,
354 POPEVENT_PLACE_BORDER_TILES,
355 POPEVENT_TILE_ART_BIG_IMAGE,
356 POPEVENT_TILE_ART_MANY_COLORS,
357 POPEVENT_TILE_ART_TOO_MANY_COLORS,
358 POPEVENT_QUAD_ART_BIG_IMAGE,
359 POPEVENT_REMOVE_USED_IMAGE,
360 POPEVENT_REMOVE_USED_SOUND,
361 POPEVENT_RESTART_SERVER,
362 POPEVENT_RESTARTING_SERVER,
363 };
364
365 int m_PopupEventType;
366 int m_PopupEventActivated;
367 int m_PopupEventWasActivated;
368 bool m_LargeLayerWasWarned;
369 bool m_PreventUnusedTilesWasWarned;
370
371 enum class EUnusedEntities
372 {
373 ALLOWED_IMPLICIT = -1,
374 NOT_ALLOWED = 0,
375 ALLOWED_EXPLICIT = 1,
376 };
377 EUnusedEntities m_AllowPlaceUnusedTiles;
378 bool IsAllowPlaceUnusedTiles() const;
379
380 bool m_BrushDrawDestructive;
381
382 int m_Mentions = 0;
383 bool m_IngameMoved = false;
384
385 int m_ToolbarPreviewSound;
386
387 std::vector<std::string> m_vSelectEntitiesFiles;
388 std::string m_SelectEntitiesImage;
389
390 // Zooming
391 CSmoothValue m_ZoomEnvelopeX;
392 CSmoothValue m_ZoomEnvelopeY;
393
394 bool m_ResetZoomEnvelope;
395
396 float m_OffsetEnvelopeX;
397 float m_OffsetEnvelopeY;
398
399 bool m_ShowMousePointer;
400 bool m_GuiActive;
401
402 bool m_PreviewZoom;
403 const void *m_pContainerPanned;
404 const void *m_pContainerPannedLast;
405
406 enum EShowTile
407 {
408 SHOW_TILE_OFF,
409 SHOW_TILE_DECIMAL,
410 SHOW_TILE_HEXADECIMAL
411 };
412 EShowTile m_ShowTileInfo;
413 bool m_ShowDetail;
414
415 enum EExtraEditor
416 {
417 EXTRAEDITOR_NONE = -1,
418 EXTRAEDITOR_ENVELOPES,
419 EXTRAEDITOR_SERVER_SETTINGS,
420 EXTRAEDITOR_HISTORY,
421 NUM_EXTRAEDITORS,
422 };
423 EExtraEditor m_ActiveExtraEditor = EXTRAEDITOR_NONE;
424 float m_aExtraEditorSplits[NUM_EXTRAEDITORS] = {250.0f, 250.0f, 250.0f};
425 float m_ToolBoxWidth = 100.0f;
426
427 bool m_ShowEnvelopePreview = false;
428 enum class EEnvelopePreview
429 {
430 NONE,
431 SELECTED,
432 ALL,
433 };
434 EEnvelopePreview m_ActiveEnvelopePreview = EEnvelopePreview::NONE;
435 enum class EQuadEnvelopePointOperation
436 {
437 NONE = 0,
438 MOVE,
439 ROTATE,
440 };
441 EQuadEnvelopePointOperation m_QuadEnvelopePointOperation = EQuadEnvelopePointOperation::NONE;
442
443 bool m_ShowPicker = false;
444 bool m_ShowPickerToggle = false;
445
446 // Color palette and pipette
447 ColorRGBA m_aSavedColors[8];
448 ColorRGBA m_PipetteColor = ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f);
449 bool m_ColorPipetteActive = false;
450
451 IGraphics::CTextureHandle m_CheckerTexture;
452
453 enum ECursorType
454 {
455 CURSOR_NORMAL,
456 CURSOR_RESIZE_V,
457 CURSOR_RESIZE_H,
458 NUM_CURSORS
459 };
460 IGraphics::CTextureHandle m_aCursorTextures[ECursorType::NUM_CURSORS];
461 ECursorType m_CursorType;
462
463 IGraphics::CTextureHandle GetEntitiesTexture();
464
465 std::shared_ptr<CLayerGroup> m_pBrush;
466 std::shared_ptr<CLayerTiles> m_pTilesetPicker;
467 std::shared_ptr<CLayerQuads> m_pQuadsetPicker;
468
469 const void *m_pUiGotContext = nullptr;
470
471 std::deque<std::shared_ptr<CDataFileWriterFinishJob>> m_WriterFinishJobs;
472
473 CLineInputBuffered<256> m_SettingsCommandInput;
474 CMapSettingsBackend m_MapSettingsBackend;
475 CMapSettingsBackend::CContext m_MapSettingsCommandContext;
476
477 // editor_ui.cpp
478 void UpdateTooltip(const void *pId, const CUIRect *pRect, const char *pToolTip);
479 ColorRGBA GetButtonColor(const void *pId, int Checked);
480 int DoButtonLogic(const void *pId, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip);
481 int DoButton_Editor(const void *pId, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip);
482 int DoButton_Env(const void *pId, const char *pText, int Checked, const CUIRect *pRect, const char *pToolTip, ColorRGBA Color, int Corners);
483 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);
484 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);
485 int DoButton_MenuItem(const void *pId, const char *pText, int Checked, const CUIRect *pRect, int Flags = BUTTONFLAG_LEFT, const char *pToolTip = nullptr);
486 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);
487 bool DoEditBox(CLineInput *pLineInput, const CUIRect *pRect, float FontSize, int Corners = IGraphics::CORNER_ALL, const char *pToolTip = nullptr, const std::vector<STextColorSplit> &vColorSplits = {});
488 bool DoClearableEditBox(CLineInput *pLineInput, const CUIRect *pRect, float FontSize, int Corners = IGraphics::CORNER_ALL, const char *pToolTip = nullptr, const std::vector<STextColorSplit> &vColorSplits = {});
489 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);
490 void RenderBackground(CUIRect View, IGraphics::CTextureHandle Texture, float Size, float Brightness) const;
491
492 // editor_server_settings.cpp
493 void DoMapSettingsEditBox(CMapSettingsBackend::CContext *pContext, const CUIRect *pRect, float FontSize, float DropdownMaxHeight, int Corners = IGraphics::CORNER_ALL, const char *pToolTip = nullptr);
494 template<typename T>
495 int DoEditBoxDropdown(SEditBoxDropdownContext *pDropdown, CLineInput *pLineInput, const CUIRect *pEditBoxRect, int x, float MaxHeight, bool AutoWidth, const std::vector<T> &vData, const FDropdownRenderCallback<T> &pfnMatchCallback);
496 template<typename T>
497 int RenderEditBoxDropdown(SEditBoxDropdownContext *pDropdown, CUIRect View, CLineInput *pLineInput, int x, float MaxHeight, bool AutoWidth, const std::vector<T> &vData, const FDropdownRenderCallback<T> &pfnMatchCallback);
498
499 // For tile art popups
500 CImageInfo m_TileArtImageInfo;
501 char m_aTileArtFilename[IO_MAX_PATH_LENGTH];
502 void TileArtCheckColors();
503
504 // For quad art popups
505 CImageInfo m_QuadArtImageInfo;
506 CQuadArtParameters m_QuadArtParameters;
507
508 static CUi::EPopupMenuFunctionResult PopupMenuFile(void *pContext, CUIRect View, bool Active);
509 static CUi::EPopupMenuFunctionResult PopupMenuTools(void *pContext, CUIRect View, bool Active);
510 static CUi::EPopupMenuFunctionResult PopupMenuSettings(void *pContext, CUIRect View, bool Active);
511 static CUi::EPopupMenuFunctionResult PopupGroup(void *pContext, CUIRect View, bool Active);
512 struct SLayerPopupContext : public SPopupMenuId
513 {
514 CEditor *m_pEditor;
515 std::vector<std::shared_ptr<CLayerTiles>> m_vpLayers;
516 std::vector<int> m_vLayerIndices;
517 CLayerTiles::SCommonPropState m_CommonPropState;
518 };
519 static CUi::EPopupMenuFunctionResult PopupLayer(void *pContext, CUIRect View, bool Active);
520 class CQuadPopupContext : public SPopupMenuId
521 {
522 public:
523 CEditor *m_pEditor;
524 int m_SelectedQuadIndex;
525 int m_Color;
526 };
527 CQuadPopupContext m_QuadPopupContext;
528 static CUi::EPopupMenuFunctionResult PopupQuad(void *pContext, CUIRect View, bool Active);
529 static CUi::EPopupMenuFunctionResult PopupSource(void *pContext, CUIRect View, bool Active);
530 class CPointPopupContext : public SPopupMenuId
531 {
532 public:
533 CEditor *m_pEditor;
534 int m_SelectedQuadPoint;
535 int m_SelectedQuadIndex;
536 };
537 CPointPopupContext m_PointPopupContext;
538 static CUi::EPopupMenuFunctionResult PopupPoint(void *pContext, CUIRect View, bool Active);
539 static CUi::EPopupMenuFunctionResult PopupEnvPoint(void *pContext, CUIRect View, bool Active);
540 static CUi::EPopupMenuFunctionResult PopupEnvPointMulti(void *pContext, CUIRect View, bool Active);
541 static CUi::EPopupMenuFunctionResult PopupEnvPointCurveType(void *pContext, CUIRect View, bool Active);
542 static CUi::EPopupMenuFunctionResult PopupImage(void *pContext, CUIRect View, bool Active);
543 static CUi::EPopupMenuFunctionResult PopupSound(void *pContext, CUIRect View, bool Active);
544 static CUi::EPopupMenuFunctionResult PopupMapInfo(void *pContext, CUIRect View, bool Active);
545 static CUi::EPopupMenuFunctionResult PopupEvent(void *pContext, CUIRect View, bool Active);
546 static CUi::EPopupMenuFunctionResult PopupSelectImage(void *pContext, CUIRect View, bool Active);
547 static CUi::EPopupMenuFunctionResult PopupSelectSound(void *pContext, CUIRect View, bool Active);
548 static CUi::EPopupMenuFunctionResult PopupSelectGametileOp(void *pContext, CUIRect View, bool Active);
549 static CUi::EPopupMenuFunctionResult PopupSelectConfigAutoMap(void *pContext, CUIRect View, bool Active);
550 static CUi::EPopupMenuFunctionResult PopupSelectAutoMapReference(void *pContext, CUIRect View, bool Active);
551 static CUi::EPopupMenuFunctionResult PopupTele(void *pContext, CUIRect View, bool Active);
552 static CUi::EPopupMenuFunctionResult PopupSpeedup(void *pContext, CUIRect View, bool Active);
553 static CUi::EPopupMenuFunctionResult PopupSwitch(void *pContext, CUIRect View, bool Active);
554 static CUi::EPopupMenuFunctionResult PopupTune(void *pContext, CUIRect View, bool Active);
555 static CUi::EPopupMenuFunctionResult PopupGoto(void *pContext, CUIRect View, bool Active);
556 static CUi::EPopupMenuFunctionResult PopupEntities(void *pContext, CUIRect View, bool Active);
557 static CUi::EPopupMenuFunctionResult PopupProofMode(void *pContext, CUIRect View, bool Active);
558 static CUi::EPopupMenuFunctionResult PopupAnimateSettings(void *pContext, CUIRect View, bool Active);
559 int m_PopupEnvelopeSelectedPoint = -1;
560 static CUi::EPopupMenuFunctionResult PopupEnvelopeCurvetype(void *pContext, CUIRect View, bool Active);
561 static CUi::EPopupMenuFunctionResult PopupQuadArt(void *pContext, CUIRect View, bool Active);
562
563 static bool CallbackOpenMap(const char *pFilename, int StorageType, void *pUser);
564 static bool CallbackAppendMap(const char *pFilename, int StorageType, void *pUser);
565 static bool CallbackSaveMap(const char *pFilename, int StorageType, void *pUser);
566 static bool CallbackSaveCopyMap(const char *pFilename, int StorageType, void *pUser);
567 static bool CallbackAddTileArt(const char *pFilepath, int StorageType, void *pUser);
568 static bool CallbackAddQuadArt(const char *pFilepath, int StorageType, void *pUser);
569 static bool CallbackSaveImage(const char *pFilename, int StorageType, void *pUser);
570 static bool CallbackSaveSound(const char *pFilename, int StorageType, void *pUser);
571 static bool CallbackCustomEntities(const char *pFilename, int StorageType, void *pUser);
572
573 void PopupSelectImageInvoke(int Current, float x, float y);
574 int PopupSelectImageResult();
575
576 void PopupSelectGametileOpInvoke(float x, float y);
577 int PopupSelectGameTileOpResult();
578
579 void PopupSelectConfigAutoMapInvoke(int Current, float x, float y);
580 int PopupSelectConfigAutoMapResult();
581
582 void PopupSelectSoundInvoke(int Current, float x, float y);
583 int PopupSelectSoundResult();
584
585 void PopupSelectAutoMapReferenceInvoke(int Current, float x, float y);
586 int PopupSelectAutoMapReferenceResult();
587
588 void DoQuadEnvelopes(const CLayerQuads *pLayerQuads);
589 void DoQuadEnvPoint(const CQuad *pQuad, CEnvelope *pEnvelope, int QuadIndex, int PointIndex);
590 void DoQuadPoint(int LayerIndex, const std::shared_ptr<CLayerQuads> &pLayer, CQuad *pQuad, int QuadIndex, int v);
591 void UpdateHotQuadPoint(const CLayerQuads *pLayer);
592
593 void DoSoundSource(int LayerIndex, CSoundSource *pSource, int Index);
594 void UpdateHotSoundSource(const CLayerSounds *pLayer);
595
596 enum class EAxis
597 {
598 NONE = 0,
599 X,
600 Y,
601 };
602 struct SAxisAlignedBoundingBox
603 {
604 enum
605 {
606 POINT_TL = 0,
607 POINT_TR,
608 POINT_BL,
609 POINT_BR,
610 POINT_CENTER,
611 NUM_POINTS
612 };
613 CPoint m_aPoints[NUM_POINTS];
614 };
615 void DoToolbarLayers(CUIRect Toolbar);
616 void DoToolbarImages(CUIRect Toolbar);
617 void DoToolbarSounds(CUIRect Toolbar);
618 void DoQuad(int LayerIndex, const std::shared_ptr<CLayerQuads> &pLayer, CQuad *pQuad, int Index);
619 void PreparePointDrag(const CQuad *pQuad, int QuadIndex, int PointIndex);
620 void DoPointDrag(CQuad *pQuad, int QuadIndex, int PointIndex, ivec2 Offset);
621 EAxis GetDragAxis(ivec2 Offset) const;
622 void DrawAxis(EAxis Axis, CPoint &OriginalPoint, CPoint &Point) const;
623 void DrawAABB(const SAxisAlignedBoundingBox &AABB, ivec2 Offset) const;
624
625 // Alignment methods
626 // These methods take `OffsetX` and `OffsetY` because the calculations are made with the original positions
627 // of the quad(s), before we started dragging. This allows us to edit `OffsetX` and `OffsetY` based on the previously
628 // calculated alignments.
629 struct SAlignmentInfo
630 {
631 CPoint m_AlignedPoint; // The "aligned" point, which we want to align/snap to
632 union
633 {
634 // 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
635 // we aligned the Y values (X axis aligned => Y values are the same, Y axis aligned => X values are the same).
636 int m_X;
637 int m_Y;
638 };
639 EAxis m_Axis; // The axis we are aligning on
640 int m_PointIndex; // The point index we are aligning
641 int m_Diff; // Store the difference
642 };
643 void ComputePointAlignments(const std::shared_ptr<CLayerQuads> &pLayer, CQuad *pQuad, int QuadIndex, int PointIndex, ivec2 Offset, std::vector<SAlignmentInfo> &vAlignments, bool Append = false) const;
644 void ComputePointsAlignments(const std::shared_ptr<CLayerQuads> &pLayer, bool Pivot, ivec2 Offset, std::vector<SAlignmentInfo> &vAlignments) const;
645 void ComputeAABBAlignments(const std::shared_ptr<CLayerQuads> &pLayer, const SAxisAlignedBoundingBox &AABB, ivec2 Offset, std::vector<SAlignmentInfo> &vAlignments) const;
646 void DrawPointAlignments(const std::vector<SAlignmentInfo> &vAlignments, ivec2 Offset) const;
647 void QuadSelectionAABB(const std::shared_ptr<CLayerQuads> &pLayer, SAxisAlignedBoundingBox &OutAABB);
648 void ApplyAlignments(const std::vector<SAlignmentInfo> &vAlignments, ivec2 &Offset);
649 void ApplyAxisAlignment(ivec2 &Offset) const;
650
651 bool ReplaceImage(const char *pFilename, int StorageType, bool CheckDuplicate);
652 static bool ReplaceImageCallback(const char *pFilename, int StorageType, void *pUser);
653 bool ReplaceSound(const char *pFilename, int StorageType, bool CheckDuplicate);
654 static bool ReplaceSoundCallback(const char *pFilename, int StorageType, void *pUser);
655 static bool AddImage(const char *pFilename, int StorageType, void *pUser);
656 static bool AddSound(const char *pFilename, int StorageType, void *pUser);
657
658 static bool IsVanillaImage(const char *pImage);
659
660 enum class ELayerOperation
661 {
662 NONE,
663 CLICK,
664 LAYER_DRAG,
665 GROUP_DRAG,
666 };
667 class CRenderLayersState
668 {
669 public:
670 CScrollRegion m_ScrollRegion;
671 ELayerOperation m_Operation;
672 ELayerOperation m_PreviousOperation;
673 const void *m_pDraggedButton;
674 float m_InitialMouseY;
675 float m_InitialCutHeight;
676 bool m_ScrollToSelectionNext;
677 int m_InitialGroupIndex;
678 std::vector<int> m_vInitialLayerIndices;
679 const char m_AddGroupButtonId = 0;
680 const char m_CollapseAllButtonId = 0;
681 const SPopupMenuId m_PopupGroupId = {};
682 SLayerPopupContext m_LayerPopupContext;
683
684 void Reset();
685 };
686 CRenderLayersState m_RenderLayersState;
687 void RenderLayers(CUIRect LayersBox);
688
689 void RenderImagesList(CUIRect Toolbox);
690 void RenderSelectedImage(CUIRect View) const;
691 void RenderSounds(CUIRect Toolbox);
692 void RenderModebar(CUIRect View);
693 void RenderStatusbar(CUIRect View, CUIRect *pTooltipRect);
694 void RenderTooltip(CUIRect TooltipRect);
695
696 void RenderEnvelopeEditor(CUIRect View);
697 void RenderEnvelopeEditorColorBar(CUIRect ColorBar, const std::shared_ptr<CEnvelope> &pEnvelope);
698
699 void RenderMapSettingsErrorDialog();
700 void RenderServerSettingsEditor(CUIRect View, bool ShowServerSettingsEditorLast);
701 static void MapSettingsDropdownRenderCallback(const SPossibleValueMatch &Match, char (&aOutput)[128], std::vector<STextColorSplit> &vColorSplits);
702
703 void RenderEditorHistory(CUIRect View);
704
705 enum class EDragSide // Which side is the drag bar on
706 {
707 BOTTOM,
708 LEFT,
709 TOP,
710 RIGHT,
711 };
712 void DoEditorDragBar(CUIRect View, CUIRect *pDragBar, EDragSide Side, float *pValue, float MinValue = 100.0f, float MaxValue = 400.0f);
713
714 void UpdateHotEnvelopeObject(const CUIRect &View, const CEnvelope *pEnvelope, int ActiveChannels);
715
716 void RenderMenubar(CUIRect Menubar);
717 void ShowHelp();
718
719 void DoAudioPreview(CUIRect View, const void *pPlayPauseButtonId, const void *pStopButtonId, const void *pSeekBarId, int SampleId);
720
721 // Zooming
722 void ZoomAdaptOffsetX(float ZoomFactor, const CUIRect &View);
723 void UpdateZoomEnvelopeX(const CUIRect &View);
724
725 void ZoomAdaptOffsetY(float ZoomFactor, const CUIRect &View);
726 void UpdateZoomEnvelopeY(const CUIRect &View);
727
728 void ResetZoomEnvelope(const std::shared_ptr<CEnvelope> &pEnvelope, int ActiveChannels);
729 void RemoveTimeOffsetEnvelope(const std::shared_ptr<CEnvelope> &pEnvelope);
730 float ScreenToEnvelopeX(const CUIRect &View, float x) const;
731 float EnvelopeToScreenX(const CUIRect &View, float x) const;
732 float ScreenToEnvelopeY(const CUIRect &View, float y) const;
733 float EnvelopeToScreenY(const CUIRect &View, float y) const;
734 float ScreenToEnvelopeDX(const CUIRect &View, float DeltaX);
735 float ScreenToEnvelopeDY(const CUIRect &View, float DeltaY);
736
737 // DDRace
738
739 IGraphics::CTextureHandle GetFrontTexture();
740 IGraphics::CTextureHandle GetTeleTexture();
741 IGraphics::CTextureHandle GetSpeedupTexture();
742 IGraphics::CTextureHandle GetSwitchTexture();
743 IGraphics::CTextureHandle GetTuneTexture();
744
745 unsigned char m_TeleNumber;
746 unsigned char m_TeleCheckpointNumber;
747 unsigned char m_ViewTeleNumber;
748
749 unsigned char m_TuningNumber;
750 unsigned char m_ViewTuning;
751
752 unsigned char m_SpeedupForce;
753 unsigned char m_SpeedupMaxSpeed;
754 short m_SpeedupAngle;
755
756 unsigned char m_SwitchNumber;
757 unsigned char m_SwitchDelay;
758 unsigned char m_ViewSwitch;
759
760 // AdjustValue must be -1, 0 or 1
761 void AdjustBrushSpecialTiles(bool UseNextFree, int AdjustModifiers, int AdjustValue);
762
763private:
764 CEditorMap m_Map;
765
766 CEditorHistory &ActiveHistory();
767
768 std::map<int, CPoint[5]> m_QuadDragOriginalPoints;
769};
770
771#endif
772