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