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 "quadart.h"
14#include "smooth_value.h"
15
16#include <base/bezier.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/file_browser.h>
25#include <game/editor/mapitems/envelope.h>
26#include <game/editor/mapitems/layer.h>
27#include <game/editor/mapitems/layer_front.h>
28#include <game/editor/mapitems/layer_game.h>
29#include <game/editor/mapitems/layer_group.h>
30#include <game/editor/mapitems/layer_quads.h>
31#include <game/editor/mapitems/layer_sounds.h>
32#include <game/editor/mapitems/layer_speedup.h>
33#include <game/editor/mapitems/layer_switch.h>
34#include <game/editor/mapitems/layer_tele.h>
35#include <game/editor/mapitems/layer_tiles.h>
36#include <game/editor/mapitems/layer_tune.h>
37#include <game/editor/mapitems/map.h>
38#include <game/editor/prompt.h>
39#include <game/editor/quick_action.h>
40#include <game/map/render_interfaces.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, public IEnvelopeEval
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 IConsole *m_pConsole = nullptr;
114 class IEngine *m_pEngine = nullptr;
115 class IGraphics *m_pGraphics = nullptr;
116 class ITextRender *m_pTextRender = nullptr;
117 class ISound *m_pSound = nullptr;
118 class IStorage *m_pStorage = nullptr;
119 CRenderMap m_RenderMap;
120 CUi m_UI;
121
122 std::vector<std::reference_wrapper<CEditorComponent>> m_vComponents;
123 CMapView m_MapView;
124 CLayerSelector m_LayerSelector;
125 CFileBrowser m_FileBrowser;
126 CPrompt m_Prompt;
127 CFontTyper m_FontTyper;
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 int GetTextureUsageFlag() const;
140
141 enum EPreviewState
142 {
143 PREVIEW_UNLOADED,
144 PREVIEW_LOADED,
145 PREVIEW_ERROR,
146 };
147
148 std::shared_ptr<CLayerGroup> m_apSavedBrushes[10];
149 static constexpr ColorRGBA ms_DefaultPropColor = ColorRGBA(1, 1, 1, 0.5f);
150
151public:
152 class IInput *Input() const { return m_pInput; }
153 class IClient *Client() const { return m_pClient; }
154 class IConfigManager *ConfigManager() const { return m_pConfigManager; }
155 class CConfig *Config() const { return m_pConfig; }
156 class IConsole *Console() const { return m_pConsole; }
157 class IEngine *Engine() const { return m_pEngine; }
158 class IGraphics *Graphics() const { return m_pGraphics; }
159 class ISound *Sound() const { return m_pSound; }
160 class ITextRender *TextRender() const { return m_pTextRender; }
161 class IStorage *Storage() const { return m_pStorage; }
162 CUi *Ui() { return &m_UI; }
163 CRenderMap *RenderMap() { return &m_RenderMap; }
164
165 CMapView *MapView() { return &m_MapView; }
166 const CMapView *MapView() const { return &m_MapView; }
167 CLayerSelector *LayerSelector() { return &m_LayerSelector; }
168
169 void SelectNextLayer();
170 void SelectPreviousLayer();
171
172 void FillGameTiles(EGameTileOp FillTile) const;
173 bool CanFillGameTiles() const;
174 void AddQuadOrSound();
175 void AddGroup();
176 void AddSoundLayer();
177 void AddTileLayer();
178 void AddQuadsLayer();
179 void AddSwitchLayer();
180 void AddFrontLayer();
181 void AddTuneLayer();
182 void AddSpeedupLayer();
183 void AddTeleLayer();
184 void DeleteSelectedLayer();
185 void LayerSelectImage();
186 bool IsNonGameTileLayerSelected() const;
187 void MapDetails();
188 void TestMapLocally();
189#define REGISTER_QUICK_ACTION(name, text, callback, disabled, active, button_color, description) CQuickAction m_QuickAction##name;
190#include <game/editor/quick_actions.h>
191#undef REGISTER_QUICK_ACTION
192
193 CEditor() :
194#define REGISTER_QUICK_ACTION(name, text, callback, disabled, active, button_color, description) m_QuickAction##name(text, description, callback, disabled, active, button_color),
195#include <game/editor/quick_actions.h>
196#undef REGISTER_QUICK_ACTION
197 m_ZoomEnvelopeX(1.0f, 0.1f, 600.0f),
198 m_ZoomEnvelopeY(640.0f, 0.1f, 32000.0f),
199 m_Map(this),
200 m_MapSettingsCommandContext(m_MapSettingsBackend.NewContext(pLineInput: &m_SettingsCommandInput))
201 {
202 m_EntitiesTexture.Invalidate();
203 m_FrontTexture.Invalidate();
204 m_TeleTexture.Invalidate();
205 m_SpeedupTexture.Invalidate();
206 m_SwitchTexture.Invalidate();
207 m_TuneTexture.Invalidate();
208
209 m_Mode = MODE_LAYERS;
210 m_Dialog = 0;
211
212 m_BrushColorEnabled = true;
213
214 m_aFilename[0] = '\0';
215 m_aFilenamePending[0] = '\0';
216 m_ValidSaveFilename = false;
217
218 m_PopupEventActivated = false;
219 m_PopupEventWasActivated = false;
220
221 m_ToolbarPreviewSound = -1;
222
223 m_SelectEntitiesImage = "DDNet";
224
225 m_ResetZoomEnvelope = true;
226 m_OffsetEnvelopeX = 0.1f;
227 m_OffsetEnvelopeY = 0.5f;
228
229 m_ShowMousePointer = true;
230
231 m_GuiActive = true;
232 m_PreviewZoom = false;
233
234 m_ShowTileInfo = SHOW_TILE_OFF;
235 m_ShowDetail = true;
236 m_Animate = false;
237 m_AnimateStart = 0;
238 m_AnimateTime = 0;
239 m_AnimateSpeed = 1;
240 m_AnimateUpdatePopup = false;
241
242 m_SelectedQuadEnvelope = -1;
243
244 m_vSelectedEnvelopePoints = {};
245 m_UpdateEnvPointInfo = false;
246 m_SelectedTangentInPoint = std::pair(-1, -1);
247 m_SelectedTangentOutPoint = std::pair(-1, -1);
248 m_CurrentQuadIndex = -1;
249
250 m_QuadKnifeActive = false;
251 m_QuadKnifeCount = 0;
252 m_QuadKnifeSelectedQuadIndex = -1;
253 std::fill(first: std::begin(arr&: m_aQuadKnifePoints), last: std::end(arr&: m_aQuadKnifePoints), value: vec2(0.0f, 0.0f));
254
255 for(size_t i = 0; i < std::size(m_aSavedColors); ++i)
256 {
257 m_aSavedColors[i] = color_cast<ColorRGBA>(hsl: ColorHSLA(i / (float)std::size(m_aSavedColors), 1.0f, 0.5f));
258 }
259
260 m_CheckerTexture.Invalidate();
261 for(auto &CursorTexture : m_aCursorTextures)
262 CursorTexture.Invalidate();
263
264 m_CursorType = CURSOR_NORMAL;
265
266 // DDRace
267
268 m_TeleNumber = 1;
269 m_TeleCheckpointNumber = 1;
270 m_ViewTeleNumber = 0;
271
272 m_TuningNumber = 1;
273 m_ViewTuning = 0;
274
275 m_SwitchNumber = 1;
276 m_SwitchDelay = 0;
277 m_SpeedupForce = 50;
278 m_SpeedupMaxSpeed = 0;
279 m_SpeedupAngle = 0;
280 m_LargeLayerWasWarned = false;
281 m_PreventUnusedTilesWasWarned = false;
282 m_AllowPlaceUnusedTiles = EUnusedEntities::NOT_ALLOWED;
283 m_BrushDrawDestructive = true;
284 }
285
286 class CHoverTile
287 {
288 public:
289 CHoverTile(int Group, int Layer, int x, int y, const CTile Tile) :
290 m_Group(Group),
291 m_Layer(Layer),
292 m_X(x),
293 m_Y(y),
294 m_Tile(Tile)
295 {
296 }
297
298 int m_Group;
299 int m_Layer;
300 int m_X;
301 int m_Y;
302 const CTile m_Tile;
303 };
304 std::vector<CHoverTile> m_vHoverTiles;
305 const std::vector<CHoverTile> &HoverTiles() const { return m_vHoverTiles; }
306
307 void Init() override;
308 void OnUpdate() override;
309 void OnRender() override;
310 void OnActivate() override;
311 void OnWindowResize() override;
312 void OnClose() override;
313 void OnDialogClose();
314 bool HasUnsavedData() const override { return m_Map.m_Modified; }
315 void UpdateMentions() override { m_Mentions++; }
316 void ResetMentions() override { m_Mentions = 0; }
317 void OnIngameMoved() override { m_IngameMoved = true; }
318 void ResetIngameMoved() override { m_IngameMoved = false; }
319
320 void HandleCursorMovement();
321 void OnMouseMove(vec2 MousePos);
322 void MouseAxisLock(vec2 &CursorRel);
323 vec2 m_MouseAxisInitialPos = vec2(0.0f, 0.0f);
324 enum class EAxisLock
325 {
326 Start,
327 None,
328 Horizontal,
329 Vertical
330 } m_MouseAxisLockState = EAxisLock::Start;
331
332 /**
333 * Global time when the autosave was last updated in the @link HandleAutosave @endlink function.
334 * This is used so that the autosave does not immediately activate when reopening the editor after
335 * a longer time of inactivity, as autosaves are only updated while the editor is open.
336 */
337 float m_LastAutosaveUpdateTime = -1.0f;
338 void HandleAutosave();
339 bool PerformAutosave();
340 void HandleWriterFinishJobs();
341
342 // TODO: The name of the ShowFileDialogError function is not accurate anymore, this is used for generic error messages.
343 // Popups in UI should be shared_ptrs to make this even more generic.
344 class CStringKeyComparator
345 {
346 public:
347 bool operator()(const char *pLhs, const char *pRhs) const;
348 };
349 std::map<const char *, CUi::SMessagePopupContext *, CStringKeyComparator> m_PopupMessageContexts;
350 [[gnu::format(printf, 2, 3)]] void ShowFileDialogError(const char *pFormat, ...);
351
352 void Reset(bool CreateDefault = true);
353 bool Save(const char *pFilename) override;
354 bool Load(const char *pFilename, int StorageType) override;
355 bool HandleMapDrop(const char *pFilename, int StorageType) override;
356 bool Append(const char *pFilename, int StorageType, bool IgnoreHistory = false);
357 void LoadCurrentMap();
358 void Render();
359
360 void RenderPressedKeys(CUIRect View);
361 void RenderSavingIndicator(CUIRect View);
362 void FreeDynamicPopupMenus();
363 void UpdateColorPipette();
364 void RenderMousePointer();
365 void RenderGameEntities(const std::shared_ptr<CLayerTiles> &pTiles);
366 void RenderSwitchEntities(const std::shared_ptr<CLayerTiles> &pTiles);
367
368 std::vector<CQuad *> GetSelectedQuads();
369 std::shared_ptr<CLayer> GetSelectedLayerType(int Index, int Type) const;
370 std::shared_ptr<CLayer> GetSelectedLayer(int Index) const;
371 std::shared_ptr<CLayerGroup> GetSelectedGroup() const;
372 CSoundSource *GetSelectedSource() const;
373 void SelectLayer(int LayerIndex, int GroupIndex = -1);
374 void AddSelectedLayer(int LayerIndex);
375 void SelectQuad(int Index);
376 void ToggleSelectQuad(int Index);
377 void DeselectQuads();
378 void DeselectQuadPoints();
379 void SelectQuadPoint(int QuadIndex, int Index);
380 void ToggleSelectQuadPoint(int QuadIndex, int Index);
381 void DeleteSelectedQuads();
382 bool IsQuadSelected(int Index) const;
383 bool IsQuadCornerSelected(int Index) const;
384 bool IsQuadPointSelected(int QuadIndex, int Index) const;
385 int FindSelectedQuadIndex(int Index) const;
386
387 int FindEnvPointIndex(int Index, int Channel) const;
388 void SelectEnvPoint(int Index);
389 void SelectEnvPoint(int Index, int Channel);
390 void ToggleEnvPoint(int Index, int Channel);
391 bool IsEnvPointSelected(int Index, int Channel) const;
392 bool IsEnvPointSelected(int Index) const;
393 void DeselectEnvPoints();
394 void SelectTangentOutPoint(int Index, int Channel);
395 bool IsTangentOutPointSelected(int Index, int Channel) const;
396 void SelectTangentInPoint(int Index, int Channel);
397 bool IsTangentInPointSelected(int Index, int Channel) const;
398 bool IsTangentInSelected() const;
399 bool IsTangentOutSelected() const;
400 bool IsTangentSelected() const;
401 std::pair<CFixedTime, int> EnvGetSelectedTimeAndValue() const;
402
403 template<typename E>
404 SEditResult<E> DoPropertiesWithState(CUIRect *pToolbox, CProperty *pProps, int *pIds, int *pNewVal, const std::vector<ColorRGBA> &vColors = {});
405 int DoProperties(CUIRect *pToolbox, CProperty *pProps, int *pIds, int *pNewVal, const std::vector<ColorRGBA> &vColors = {});
406
407 CUi::SColorPickerPopupContext m_ColorPickerPopupContext;
408 const void *m_pColorPickerPopupActiveId = nullptr;
409 void DoColorPickerButton(const void *pId, const CUIRect *pRect, ColorRGBA Color, const std::function<void(ColorRGBA Color)> &SetColor);
410
411 int m_Mode;
412 int m_Dialog;
413 char m_aTooltip[256] = "";
414
415 bool m_BrushColorEnabled;
416
417 char m_aFilename[IO_MAX_PATH_LENGTH];
418 char m_aFilenamePending[IO_MAX_PATH_LENGTH];
419 bool m_ValidSaveFilename;
420
421 enum
422 {
423 POPEVENT_EXIT = 0,
424 POPEVENT_LOAD,
425 POPEVENT_LOADCURRENT,
426 POPEVENT_LOADDROP,
427 POPEVENT_NEW,
428 POPEVENT_LARGELAYER,
429 POPEVENT_PREVENTUNUSEDTILES,
430 POPEVENT_IMAGEDIV16,
431 POPEVENT_IMAGE_MAX,
432 POPEVENT_SOUND_MAX,
433 POPEVENT_PLACE_BORDER_TILES,
434 POPEVENT_TILEART_BIG_IMAGE,
435 POPEVENT_TILEART_MANY_COLORS,
436 POPEVENT_TILEART_TOO_MANY_COLORS,
437 POPEVENT_QUADART_BIG_IMAGE,
438 POPEVENT_REMOVE_USED_IMAGE,
439 POPEVENT_REMOVE_USED_SOUND,
440 POPEVENT_RESTART_SERVER,
441 POPEVENT_RESTARTING_SERVER,
442 };
443
444 int m_PopupEventType;
445 int m_PopupEventActivated;
446 int m_PopupEventWasActivated;
447 bool m_LargeLayerWasWarned;
448 bool m_PreventUnusedTilesWasWarned;
449
450 enum class EUnusedEntities
451 {
452 ALLOWED_IMPLICIT = -1,
453 NOT_ALLOWED = 0,
454 ALLOWED_EXPLICIT = 1,
455 };
456 EUnusedEntities m_AllowPlaceUnusedTiles;
457 bool IsAllowPlaceUnusedTiles() const;
458
459 bool m_BrushDrawDestructive;
460
461 int m_Mentions = 0;
462 bool m_IngameMoved = false;
463
464 int m_ToolbarPreviewSound;
465
466 std::vector<std::string> m_vSelectEntitiesFiles;
467 std::string m_SelectEntitiesImage;
468
469 // Zooming
470 CSmoothValue m_ZoomEnvelopeX;
471 CSmoothValue m_ZoomEnvelopeY;
472
473 bool m_ResetZoomEnvelope;
474
475 float m_OffsetEnvelopeX;
476 float m_OffsetEnvelopeY;
477
478 bool m_ShowMousePointer;
479 bool m_GuiActive;
480
481 bool m_PreviewZoom;
482 float m_MouseWorldScale = 1.0f; // Mouse (i.e. UI) scale relative to the World (selected Group)
483 vec2 m_MouseWorldPos = vec2(0.0f, 0.0f);
484 vec2 m_MouseWorldNoParaPos = vec2(0.0f, 0.0f);
485 vec2 m_MouseDeltaWorld = vec2(0.0f, 0.0f);
486 const void *m_pContainerPanned;
487 const void *m_pContainerPannedLast;
488 char m_MapEditorId; // UI element ID for the main map editor
489
490 enum EShowTile
491 {
492 SHOW_TILE_OFF,
493 SHOW_TILE_DECIMAL,
494 SHOW_TILE_HEXADECIMAL
495 };
496 EShowTile m_ShowTileInfo;
497 bool m_ShowDetail;
498
499 bool m_Animate;
500 int64_t m_AnimateStart;
501 float m_AnimateTime;
502 float m_AnimateSpeed;
503 bool m_AnimateUpdatePopup;
504
505 enum EExtraEditor
506 {
507 EXTRAEDITOR_NONE = -1,
508 EXTRAEDITOR_ENVELOPES,
509 EXTRAEDITOR_SERVER_SETTINGS,
510 EXTRAEDITOR_HISTORY,
511 NUM_EXTRAEDITORS,
512 };
513 EExtraEditor m_ActiveExtraEditor = EXTRAEDITOR_NONE;
514 float m_aExtraEditorSplits[NUM_EXTRAEDITORS] = {250.0f, 250.0f, 250.0f};
515 float m_ToolBoxWidth = 100.0f;
516
517 bool m_ShowEnvelopePreview = false;
518 enum class EEnvelopePreview
519 {
520 NONE,
521 SELECTED,
522 ALL,
523 };
524 EEnvelopePreview m_ActiveEnvelopePreview = EEnvelopePreview::NONE;
525 enum class EQuadEnvelopePointOperation
526 {
527 NONE = 0,
528 MOVE,
529 ROTATE,
530 };
531 EQuadEnvelopePointOperation m_QuadEnvelopePointOperation = EQuadEnvelopePointOperation::NONE;
532
533 bool m_ShowPicker;
534
535 std::vector<int> m_vSelectedLayers;
536 std::vector<int> m_vSelectedQuads;
537 int m_SelectedGroup;
538 int m_SelectedQuadPoints;
539 int m_SelectedEnvelope;
540 std::vector<std::pair<int, int>> m_vSelectedEnvelopePoints;
541 int m_SelectedQuadEnvelope;
542 int m_CurrentQuadIndex;
543 int m_SelectedSource;
544 std::pair<int, int> m_SelectedTangentInPoint;
545 std::pair<int, int> m_SelectedTangentOutPoint;
546 bool m_UpdateEnvPointInfo;
547
548 bool m_QuadKnifeActive;
549 int m_QuadKnifeCount;
550 int m_QuadKnifeSelectedQuadIndex;
551 vec2 m_aQuadKnifePoints[4];
552
553 // Color palette and pipette
554 ColorRGBA m_aSavedColors[8];
555 ColorRGBA m_PipetteColor = ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f);
556 bool m_ColorPipetteActive = false;
557
558 IGraphics::CTextureHandle m_CheckerTexture;
559
560 enum ECursorType
561 {
562 CURSOR_NORMAL,
563 CURSOR_RESIZE_V,
564 CURSOR_RESIZE_H,
565 NUM_CURSORS
566 };
567 IGraphics::CTextureHandle m_aCursorTextures[ECursorType::NUM_CURSORS];
568 ECursorType m_CursorType;
569
570 IGraphics::CTextureHandle GetEntitiesTexture();
571
572 std::shared_ptr<CLayerGroup> m_pBrush;
573 std::shared_ptr<CLayerTiles> m_pTilesetPicker;
574 std::shared_ptr<CLayerQuads> m_pQuadsetPicker;
575
576 const void *m_pUiGotContext = nullptr;
577
578 CEditorMap m_Map;
579 std::deque<std::shared_ptr<CDataFileWriterFinishJob>> m_WriterFinishJobs;
580
581 int m_ShiftBy;
582
583 void EnvelopeEval(int TimeOffsetMillis, int Env, ColorRGBA &Result, size_t Channels) override;
584
585 CLineInputBuffered<256> m_SettingsCommandInput;
586 CMapSettingsBackend m_MapSettingsBackend;
587 CMapSettingsBackend::CContext m_MapSettingsCommandContext;
588
589 CImageInfo m_TileartImageInfo;
590 void AddTileart(bool IgnoreHistory = false);
591 char m_aTileartFilename[IO_MAX_PATH_LENGTH];
592 void TileartCheckColors();
593
594 CImageInfo m_QuadArtImageInfo;
595 CQuadArtParameters m_QuadArtParameters;
596 void AddQuadArt(bool IgnoreHistory = false);
597
598 void PlaceBorderTiles();
599
600 // editor_ui.cpp
601 void UpdateTooltip(const void *pId, const CUIRect *pRect, const char *pToolTip);
602 ColorRGBA GetButtonColor(const void *pId, int Checked);
603 int DoButtonLogic(const void *pId, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip);
604 int DoButton_Editor(const void *pId, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip);
605 int DoButton_Env(const void *pId, const char *pText, int Checked, const CUIRect *pRect, const char *pToolTip, ColorRGBA Color, int Corners);
606 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);
607 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);
608 int DoButton_MenuItem(const void *pId, const char *pText, int Checked, const CUIRect *pRect, int Flags = BUTTONFLAG_LEFT, const char *pToolTip = nullptr);
609 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);
610 bool DoEditBox(CLineInput *pLineInput, const CUIRect *pRect, float FontSize, int Corners = IGraphics::CORNER_ALL, const char *pToolTip = nullptr, const std::vector<STextColorSplit> &vColorSplits = {});
611 bool DoClearableEditBox(CLineInput *pLineInput, const CUIRect *pRect, float FontSize, int Corners = IGraphics::CORNER_ALL, const char *pToolTip = nullptr, const std::vector<STextColorSplit> &vColorSplits = {});
612 SEditResult<int> UiDoValueSelector(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);
613 void RenderBackground(CUIRect View, IGraphics::CTextureHandle Texture, float Size, float Brightness) const;
614
615 // editor_server_settings.cpp
616 void DoMapSettingsEditBox(CMapSettingsBackend::CContext *pContext, const CUIRect *pRect, float FontSize, float DropdownMaxHeight, int Corners = IGraphics::CORNER_ALL, const char *pToolTip = nullptr);
617 template<typename T>
618 int DoEditBoxDropdown(SEditBoxDropdownContext *pDropdown, CLineInput *pLineInput, const CUIRect *pEditBoxRect, int x, float MaxHeight, bool AutoWidth, const std::vector<T> &vData, const FDropdownRenderCallback<T> &pfnMatchCallback);
619 template<typename T>
620 int RenderEditBoxDropdown(SEditBoxDropdownContext *pDropdown, CUIRect View, CLineInput *pLineInput, int x, float MaxHeight, bool AutoWidth, const std::vector<T> &vData, const FDropdownRenderCallback<T> &pfnMatchCallback);
621
622 static CUi::EPopupMenuFunctionResult PopupMenuFile(void *pContext, CUIRect View, bool Active);
623 static CUi::EPopupMenuFunctionResult PopupMenuTools(void *pContext, CUIRect View, bool Active);
624 static CUi::EPopupMenuFunctionResult PopupMenuSettings(void *pContext, CUIRect View, bool Active);
625 static CUi::EPopupMenuFunctionResult PopupGroup(void *pContext, CUIRect View, bool Active);
626 struct SLayerPopupContext : public SPopupMenuId
627 {
628 CEditor *m_pEditor;
629 std::vector<std::shared_ptr<CLayerTiles>> m_vpLayers;
630 std::vector<int> m_vLayerIndices;
631 CLayerTiles::SCommonPropState m_CommonPropState;
632 };
633 static CUi::EPopupMenuFunctionResult PopupLayer(void *pContext, CUIRect View, bool Active);
634 class CQuadPopupContext : public SPopupMenuId
635 {
636 public:
637 CEditor *m_pEditor;
638 int m_SelectedQuadIndex;
639 int m_Color;
640 };
641 CQuadPopupContext m_QuadPopupContext;
642 static CUi::EPopupMenuFunctionResult PopupQuad(void *pContext, CUIRect View, bool Active);
643 static CUi::EPopupMenuFunctionResult PopupSource(void *pContext, CUIRect View, bool Active);
644 class CPointPopupContext : public SPopupMenuId
645 {
646 public:
647 CEditor *m_pEditor;
648 int m_SelectedQuadPoint;
649 int m_SelectedQuadIndex;
650 };
651 CPointPopupContext m_PointPopupContext;
652 static CUi::EPopupMenuFunctionResult PopupPoint(void *pContext, CUIRect View, bool Active);
653 static CUi::EPopupMenuFunctionResult PopupEnvPoint(void *pContext, CUIRect View, bool Active);
654 static CUi::EPopupMenuFunctionResult PopupEnvPointMulti(void *pContext, CUIRect View, bool Active);
655 static CUi::EPopupMenuFunctionResult PopupEnvPointCurveType(void *pContext, CUIRect View, bool Active);
656 static CUi::EPopupMenuFunctionResult PopupImage(void *pContext, CUIRect View, bool Active);
657 static CUi::EPopupMenuFunctionResult PopupSound(void *pContext, CUIRect View, bool Active);
658 static CUi::EPopupMenuFunctionResult PopupMapInfo(void *pContext, CUIRect View, bool Active);
659 static CUi::EPopupMenuFunctionResult PopupEvent(void *pContext, CUIRect View, bool Active);
660 static CUi::EPopupMenuFunctionResult PopupSelectImage(void *pContext, CUIRect View, bool Active);
661 static CUi::EPopupMenuFunctionResult PopupSelectSound(void *pContext, CUIRect View, bool Active);
662 static CUi::EPopupMenuFunctionResult PopupSelectGametileOp(void *pContext, CUIRect View, bool Active);
663 static CUi::EPopupMenuFunctionResult PopupSelectConfigAutoMap(void *pContext, CUIRect View, bool Active);
664 static CUi::EPopupMenuFunctionResult PopupSelectAutoMapReference(void *pContext, CUIRect View, bool Active);
665 static CUi::EPopupMenuFunctionResult PopupTele(void *pContext, CUIRect View, bool Active);
666 static CUi::EPopupMenuFunctionResult PopupSpeedup(void *pContext, CUIRect View, bool Active);
667 static CUi::EPopupMenuFunctionResult PopupSwitch(void *pContext, CUIRect View, bool Active);
668 static CUi::EPopupMenuFunctionResult PopupTune(void *pContext, CUIRect View, bool Active);
669 static CUi::EPopupMenuFunctionResult PopupGoto(void *pContext, CUIRect View, bool Active);
670 static CUi::EPopupMenuFunctionResult PopupEntities(void *pContext, CUIRect View, bool Active);
671 static CUi::EPopupMenuFunctionResult PopupProofMode(void *pContext, CUIRect View, bool Active);
672 static CUi::EPopupMenuFunctionResult PopupAnimateSettings(void *pContext, CUIRect View, bool Active);
673 int m_PopupEnvelopeSelectedPoint = -1;
674 static CUi::EPopupMenuFunctionResult PopupEnvelopeCurvetype(void *pContext, CUIRect View, bool Active);
675 static CUi::EPopupMenuFunctionResult PopupQuadArt(void *pContext, CUIRect View, bool Active);
676
677 static bool CallbackOpenMap(const char *pFilename, int StorageType, void *pUser);
678 static bool CallbackAppendMap(const char *pFilename, int StorageType, void *pUser);
679 static bool CallbackSaveMap(const char *pFilename, int StorageType, void *pUser);
680 static bool CallbackSaveCopyMap(const char *pFilename, int StorageType, void *pUser);
681 static bool CallbackAddTileart(const char *pFilepath, int StorageType, void *pUser);
682 static bool CallbackAddQuadArt(const char *pFilepath, int StorageType, void *pUser);
683 static bool CallbackSaveImage(const char *pFilename, int StorageType, void *pUser);
684 static bool CallbackSaveSound(const char *pFilename, int StorageType, void *pUser);
685 static bool CallbackCustomEntities(const char *pFilename, int StorageType, void *pUser);
686
687 void PopupSelectImageInvoke(int Current, float x, float y);
688 int PopupSelectImageResult();
689
690 void PopupSelectGametileOpInvoke(float x, float y);
691 int PopupSelectGameTileOpResult();
692
693 void PopupSelectConfigAutoMapInvoke(int Current, float x, float y);
694 int PopupSelectConfigAutoMapResult();
695
696 void PopupSelectSoundInvoke(int Current, float x, float y);
697 int PopupSelectSoundResult();
698
699 void PopupSelectAutoMapReferenceInvoke(int Current, float x, float y);
700 int PopupSelectAutoMapReferenceResult();
701
702 void DoQuadEnvelopes(const CLayerQuads *pLayerQuads);
703 void DoQuadEnvPoint(const CQuad *pQuad, CEnvelope *pEnvelope, int QuadIndex, int PointIndex);
704 void DoQuadPoint(int LayerIndex, const std::shared_ptr<CLayerQuads> &pLayer, CQuad *pQuad, int QuadIndex, int v);
705 void UpdateHotQuadPoint(const CLayerQuads *pLayer);
706
707 float TriangleArea(vec2 A, vec2 B, vec2 C);
708 bool IsInTriangle(vec2 Point, vec2 A, vec2 B, vec2 C);
709 void DoQuadKnife(int QuadIndex);
710
711 void DoSoundSource(int LayerIndex, CSoundSource *pSource, int Index);
712 void UpdateHotSoundSource(const CLayerSounds *pLayer);
713
714 enum class EAxis
715 {
716 AXIS_NONE = 0,
717 AXIS_X,
718 AXIS_Y
719 };
720 struct SAxisAlignedBoundingBox
721 {
722 enum
723 {
724 POINT_TL = 0,
725 POINT_TR,
726 POINT_BL,
727 POINT_BR,
728 POINT_CENTER,
729 NUM_POINTS
730 };
731 CPoint m_aPoints[NUM_POINTS];
732 };
733 void DoMapEditor(CUIRect View);
734 void DoToolbarLayers(CUIRect Toolbar);
735 void DoToolbarImages(CUIRect Toolbar);
736 void DoToolbarSounds(CUIRect Toolbar);
737 void DoQuad(int LayerIndex, const std::shared_ptr<CLayerQuads> &pLayer, CQuad *pQuad, int Index);
738 void PreparePointDrag(const CQuad *pQuad, int QuadIndex, int PointIndex);
739 void DoPointDrag(CQuad *pQuad, int QuadIndex, int PointIndex, ivec2 Offset);
740 EAxis GetDragAxis(ivec2 Offset) const;
741 void DrawAxis(EAxis Axis, CPoint &OriginalPoint, CPoint &Point) const;
742 void DrawAABB(const SAxisAlignedBoundingBox &AABB, ivec2 Offset) const;
743
744 // Alignment methods
745 // These methods take `OffsetX` and `OffsetY` because the calculations are made with the original positions
746 // of the quad(s), before we started dragging. This allows us to edit `OffsetX` and `OffsetY` based on the previously
747 // calculated alignments.
748 struct SAlignmentInfo
749 {
750 CPoint m_AlignedPoint; // The "aligned" point, which we want to align/snap to
751 union
752 {
753 // 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
754 // we aligned the Y values (X axis aligned => Y values are the same, Y axis aligned => X values are the same).
755 int m_X;
756 int m_Y;
757 };
758 EAxis m_Axis; // The axis we are aligning on
759 int m_PointIndex; // The point index we are aligning
760 int m_Diff; // Store the difference
761 };
762 void ComputePointAlignments(const std::shared_ptr<CLayerQuads> &pLayer, CQuad *pQuad, int QuadIndex, int PointIndex, ivec2 Offset, std::vector<SAlignmentInfo> &vAlignments, bool Append = false) const;
763 void ComputePointsAlignments(const std::shared_ptr<CLayerQuads> &pLayer, bool Pivot, ivec2 Offset, std::vector<SAlignmentInfo> &vAlignments) const;
764 void ComputeAABBAlignments(const std::shared_ptr<CLayerQuads> &pLayer, const SAxisAlignedBoundingBox &AABB, ivec2 Offset, std::vector<SAlignmentInfo> &vAlignments) const;
765 void DrawPointAlignments(const std::vector<SAlignmentInfo> &vAlignments, ivec2 Offset) const;
766 void QuadSelectionAABB(const std::shared_ptr<CLayerQuads> &pLayer, SAxisAlignedBoundingBox &OutAABB);
767 void ApplyAlignments(const std::vector<SAlignmentInfo> &vAlignments, ivec2 &Offset);
768 void ApplyAxisAlignment(ivec2 &Offset) const;
769
770 bool ReplaceImage(const char *pFilename, int StorageType, bool CheckDuplicate);
771 static bool ReplaceImageCallback(const char *pFilename, int StorageType, void *pUser);
772 bool ReplaceSound(const char *pFilename, int StorageType, bool CheckDuplicate);
773 static bool ReplaceSoundCallback(const char *pFilename, int StorageType, void *pUser);
774 static bool AddImage(const char *pFilename, int StorageType, void *pUser);
775 static bool AddSound(const char *pFilename, int StorageType, void *pUser);
776
777 bool IsEnvelopeUsed(int EnvelopeIndex) const;
778 void RemoveUnusedEnvelopes();
779
780 static bool IsVanillaImage(const char *pImage);
781
782 void RenderLayers(CUIRect LayersBox);
783 void RenderImagesList(CUIRect Toolbox);
784 void RenderSelectedImage(CUIRect View) const;
785 void RenderSounds(CUIRect Toolbox);
786 void RenderModebar(CUIRect View);
787 void RenderStatusbar(CUIRect View, CUIRect *pTooltipRect);
788 void RenderTooltip(CUIRect TooltipRect);
789
790 void RenderEnvelopeEditor(CUIRect View);
791 void RenderEnvelopeEditorColorBar(CUIRect ColorBar, const std::shared_ptr<CEnvelope> &pEnvelope);
792
793 void RenderMapSettingsErrorDialog();
794 void RenderServerSettingsEditor(CUIRect View, bool ShowServerSettingsEditorLast);
795 static void MapSettingsDropdownRenderCallback(const SPossibleValueMatch &Match, char (&aOutput)[128], std::vector<STextColorSplit> &vColorSplits);
796
797 void RenderEditorHistory(CUIRect View);
798
799 enum class EDragSide // Which side is the drag bar on
800 {
801 SIDE_BOTTOM,
802 SIDE_LEFT,
803 SIDE_TOP,
804 SIDE_RIGHT
805 };
806 void DoEditorDragBar(CUIRect View, CUIRect *pDragBar, EDragSide Side, float *pValue, float MinValue = 100.0f, float MaxValue = 400.0f);
807
808 void UpdateHotEnvelopePoint(const CUIRect &View, const CEnvelope *pEnvelope, int ActiveChannels);
809
810 void RenderMenubar(CUIRect Menubar);
811
812 void SelectGameLayer();
813
814 void DoAudioPreview(CUIRect View, const void *pPlayPauseButtonId, const void *pStopButtonId, const void *pSeekBarId, int SampleId);
815
816 // Zooming
817 void ZoomAdaptOffsetX(float ZoomFactor, const CUIRect &View);
818 void UpdateZoomEnvelopeX(const CUIRect &View);
819
820 void ZoomAdaptOffsetY(float ZoomFactor, const CUIRect &View);
821 void UpdateZoomEnvelopeY(const CUIRect &View);
822
823 void ResetZoomEnvelope(const std::shared_ptr<CEnvelope> &pEnvelope, int ActiveChannels);
824 void RemoveTimeOffsetEnvelope(const std::shared_ptr<CEnvelope> &pEnvelope);
825 float ScreenToEnvelopeX(const CUIRect &View, float x) const;
826 float EnvelopeToScreenX(const CUIRect &View, float x) const;
827 float ScreenToEnvelopeY(const CUIRect &View, float y) const;
828 float EnvelopeToScreenY(const CUIRect &View, float y) const;
829 float ScreenToEnvelopeDX(const CUIRect &View, float DeltaX);
830 float ScreenToEnvelopeDY(const CUIRect &View, float DeltaY);
831
832 // DDRace
833
834 IGraphics::CTextureHandle GetFrontTexture();
835 IGraphics::CTextureHandle GetTeleTexture();
836 IGraphics::CTextureHandle GetSpeedupTexture();
837 IGraphics::CTextureHandle GetSwitchTexture();
838 IGraphics::CTextureHandle GetTuneTexture();
839
840 unsigned char m_TeleNumber;
841 unsigned char m_TeleCheckpointNumber;
842 unsigned char m_ViewTeleNumber;
843
844 unsigned char m_TuningNumber;
845 unsigned char m_ViewTuning;
846
847 unsigned char m_SpeedupForce;
848 unsigned char m_SpeedupMaxSpeed;
849 short m_SpeedupAngle;
850
851 unsigned char m_SwitchNumber;
852 unsigned char m_SwitchDelay;
853 unsigned char m_ViewSwitch;
854
855 void AdjustBrushSpecialTiles(bool UseNextFree, int Adjust = 0);
856
857private:
858 CEditorHistory &ActiveHistory();
859
860 std::map<int, CPoint[5]> m_QuadDragOriginalPoints;
861};
862
863#endif
864