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