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