1#ifndef GAME_EDITOR_MAPITEMS_LAYER_TILES_H
2#define GAME_EDITOR_MAPITEMS_LAYER_TILES_H
3
4#include "layer.h"
5
6#include <base/dbg.h>
7#include <base/mem.h>
8
9#include <game/editor/editor_trackers.h>
10#include <game/editor/enums.h>
11
12#include <map>
13
14struct STileStateChange
15{
16 bool m_Changed;
17 CTile m_Previous;
18 CTile m_Current;
19};
20
21template<typename T>
22using EditorTileStateChangeHistory = std::map<int, std::map<int, T>>;
23
24/**
25 * Represents a direction to shift a tile layer with the CLayerTiles::Shift function.
26 * The underlying type is `int` as this is also used with the CEditor::DoPropertiesWithState function.
27 */
28enum class EShiftDirection : int
29{
30 LEFT,
31 RIGHT,
32 UP,
33 DOWN,
34};
35
36class CIntRect
37{
38public:
39 int x, y;
40 int w, h;
41};
42
43class CLayerTiles : public CLayer
44{
45protected:
46 template<typename T>
47 void ShiftImpl(T *pTiles, EShiftDirection Direction, int ShiftBy)
48 {
49 switch(Direction)
50 {
51 case EShiftDirection::LEFT:
52 ShiftBy = minimum(a: ShiftBy, b: m_Width);
53 for(int y = 0; y < m_Height; ++y)
54 {
55 if(ShiftBy < m_Width)
56 mem_move(&pTiles[y * m_Width], &pTiles[y * m_Width + ShiftBy], (m_Width - ShiftBy) * sizeof(T));
57 mem_zero(&pTiles[y * m_Width + (m_Width - ShiftBy)], ShiftBy * sizeof(T));
58 }
59 break;
60 case EShiftDirection::RIGHT:
61 ShiftBy = minimum(a: ShiftBy, b: m_Width);
62 for(int y = 0; y < m_Height; ++y)
63 {
64 if(ShiftBy < m_Width)
65 mem_move(&pTiles[y * m_Width + ShiftBy], &pTiles[y * m_Width], (m_Width - ShiftBy) * sizeof(T));
66 mem_zero(&pTiles[y * m_Width], ShiftBy * sizeof(T));
67 }
68 break;
69 case EShiftDirection::UP:
70 ShiftBy = minimum(a: ShiftBy, b: m_Height);
71 for(int y = ShiftBy; y < m_Height; ++y)
72 {
73 mem_copy(&pTiles[(y - ShiftBy) * m_Width], &pTiles[y * m_Width], m_Width * sizeof(T));
74 }
75 for(int y = m_Height - ShiftBy; y < m_Height; ++y)
76 {
77 mem_zero(&pTiles[y * m_Width], m_Width * sizeof(T));
78 }
79 break;
80 case EShiftDirection::DOWN:
81 ShiftBy = minimum(a: ShiftBy, b: m_Height);
82 for(int y = m_Height - ShiftBy - 1; y >= 0; --y)
83 {
84 mem_copy(&pTiles[(y + ShiftBy) * m_Width], &pTiles[y * m_Width], m_Width * sizeof(T));
85 }
86 for(int y = 0; y < ShiftBy; ++y)
87 {
88 mem_zero(&pTiles[y * m_Width], m_Width * sizeof(T));
89 }
90 break;
91 default:
92 dbg_assert_failed("Direction invalid: %d", (int)Direction);
93 }
94 }
95 template<typename T>
96 void BrushFlipXImpl(T *pTiles)
97 {
98 for(int y = 0; y < m_Height; y++)
99 for(int x = 0; x < m_Width / 2; x++)
100 std::swap(pTiles[y * m_Width + x], pTiles[(y + 1) * m_Width - 1 - x]);
101 }
102 template<typename T>
103 void BrushFlipYImpl(T *pTiles)
104 {
105 for(int y = 0; y < m_Height / 2; y++)
106 for(int x = 0; x < m_Width; x++)
107 std::swap(pTiles[y * m_Width + x], pTiles[(m_Height - 1 - y) * m_Width + x]);
108 }
109
110public:
111 CLayerTiles(CEditorMap *pMap, int w, int h);
112 CLayerTiles(const CLayerTiles &Other);
113 ~CLayerTiles() override;
114
115 [[nodiscard]] virtual CTile GetTile(int x, int y) const;
116 virtual void SetTile(int x, int y, CTile Tile);
117 void SetTileIgnoreHistory(int x, int y, CTile Tile) const;
118
119 virtual void Resize(int NewW, int NewH);
120 virtual void Shift(EShiftDirection Direction);
121
122 void MakePalette() const;
123 void Render(bool Tileset = false) override;
124
125 int ConvertX(float x) const;
126 int ConvertY(float y) const;
127 void Convert(CUIRect Rect, CIntRect *pOut) const;
128 void Snap(CUIRect *pRect) const;
129 void Clamp(CIntRect *pRect) const;
130
131 bool IsEntitiesLayer() const override;
132
133 [[nodiscard]] virtual bool IsEmpty() const;
134 void BrushSelecting(CUIRect Rect) override;
135 int BrushGrab(CLayerGroup *pBrush, CUIRect Rect) override;
136 void FillSelection(bool Empty, CLayer *pBrush, CUIRect Rect) override;
137 void FillGameTiles(EGameTileOp Fill);
138 bool CanFillGameTiles() const;
139 void BrushDraw(CLayer *pBrush, vec2 WorldPos) override;
140 void BrushFlipX() override;
141 void BrushFlipY() override;
142 void BrushRotate(float Amount) override;
143
144 std::shared_ptr<CLayer> Duplicate() const override;
145 const char *TypeName() const override;
146
147 virtual void ShowInfo();
148 CUi::EPopupMenuFunctionResult RenderProperties(CUIRect *pToolbox) override;
149
150 struct SCommonPropState
151 {
152 enum
153 {
154 MODIFIED_SIZE = 1 << 0,
155 MODIFIED_COLOR = 1 << 1,
156 };
157 int m_Modified = 0;
158 int m_Width = -1;
159 int m_Height = -1;
160 int m_Color = 0;
161 };
162 static CUi::EPopupMenuFunctionResult RenderCommonProperties(SCommonPropState &State, CEditorMap *pEditorMap, CUIRect *pToolbox, std::vector<std::shared_ptr<CLayerTiles>> &vpLayers, std::vector<int> &vLayerIndices);
163
164 void ModifyImageIndex(const FIndexModifyFunction &IndexModifyFunction) override;
165 void ModifyEnvelopeIndex(const FIndexModifyFunction &IndexModifyFunction) override;
166
167 void PrepareForSave();
168 void ExtractTiles(const CTile *pSavedTiles, size_t SavedTilesSize) const;
169
170 void GetSize(float *pWidth, float *pHeight) override
171 {
172 *pWidth = m_Width * 32.0f;
173 *pHeight = m_Height * 32.0f;
174 }
175
176 void FlagModified(int x, int y, int w, int h);
177
178 bool m_HasGame;
179 int m_Image;
180 int m_Width;
181 int m_Height;
182 CColor m_Color;
183 int m_ColorEnv;
184 int m_ColorEnvOffset;
185 CTile *m_pTiles;
186
187 // DDRace
188
189 int m_FillGameTile = -1;
190 bool m_LiveGameTiles = false;
191 int m_AutoMapperConfig;
192 int m_AutoMapperReference;
193 int m_Seed;
194 bool m_AutoAutoMap;
195 bool m_HasTele;
196 bool m_HasSpeedup;
197 bool m_HasFront;
198 bool m_HasSwitch;
199 bool m_HasTune;
200 char m_aFilename[IO_MAX_PATH_LENGTH];
201 bool m_KnownTextModeLayer = false;
202
203 EditorTileStateChangeHistory<STileStateChange> m_TilesHistory;
204 virtual void ClearHistory() { m_TilesHistory.clear(); }
205
206 static bool HasAutomapEffect(ETilesProp Prop);
207
208protected:
209 void RecordStateChange(int x, int y, CTile Previous, CTile Tile);
210
211 void ShowPreventUnusedTilesWarning();
212
213 friend class CAutoMapper;
214};
215
216#endif
217