1#include "layer_tune.h"
2
3#include <game/editor/editor.h>
4
5CLayerTune::CLayerTune(CEditor *pEditor, int w, int h) :
6 CLayerTiles(pEditor, w, h)
7{
8 str_copy(dst&: m_aName, src: "Tune");
9 m_Tune = 1;
10
11 m_pTuneTile = new CTuneTile[w * h];
12 mem_zero(block: m_pTuneTile, size: (size_t)w * h * sizeof(CTuneTile));
13}
14
15CLayerTune::CLayerTune(const CLayerTune &Other) :
16 CLayerTiles(Other)
17{
18 str_copy(dst&: m_aName, src: "Tune copy");
19 m_Tune = 1;
20
21 m_pTuneTile = new CTuneTile[m_Width * m_Height];
22 mem_copy(dest: m_pTuneTile, source: Other.m_pTuneTile, size: (size_t)m_Width * m_Height * sizeof(CTuneTile));
23}
24
25CLayerTune::~CLayerTune()
26{
27 delete[] m_pTuneTile;
28}
29
30void CLayerTune::Resize(int NewW, int NewH)
31{
32 // resize Tune data
33 CTuneTile *pNewTuneData = new CTuneTile[NewW * NewH];
34 mem_zero(block: pNewTuneData, size: (size_t)NewW * NewH * sizeof(CTuneTile));
35
36 // copy old data
37 for(int y = 0; y < minimum(a: NewH, b: m_Height); y++)
38 mem_copy(dest: &pNewTuneData[y * NewW], source: &m_pTuneTile[y * m_Width], size: minimum(a: m_Width, b: NewW) * sizeof(CTuneTile));
39
40 // replace old
41 delete[] m_pTuneTile;
42 m_pTuneTile = pNewTuneData;
43
44 // resize tile data
45 CLayerTiles::Resize(NewW, NewH);
46
47 // resize gamelayer too
48 if(m_pEditor->m_Map.m_pGameLayer->m_Width != NewW || m_pEditor->m_Map.m_pGameLayer->m_Height != NewH)
49 m_pEditor->m_Map.m_pGameLayer->Resize(NewW, NewH);
50}
51
52void CLayerTune::Shift(int Direction)
53{
54 CLayerTiles::Shift(Direction);
55 ShiftImpl(pTiles: m_pTuneTile, Direction, ShiftBy: m_pEditor->m_ShiftBy);
56}
57
58bool CLayerTune::IsEmpty(const std::shared_ptr<CLayerTiles> &pLayer)
59{
60 for(int y = 0; y < pLayer->m_Height; y++)
61 for(int x = 0; x < pLayer->m_Width; x++)
62 if(m_pEditor->m_AllowPlaceUnusedTiles || IsValidTuneTile(Index: pLayer->GetTile(x, y).m_Index))
63 return false;
64
65 return true;
66}
67
68void CLayerTune::BrushDraw(std::shared_ptr<CLayer> pBrush, float wx, float wy)
69{
70 if(m_Readonly)
71 return;
72
73 std::shared_ptr<CLayerTune> pTuneLayer = std::static_pointer_cast<CLayerTune>(r: pBrush);
74 int sx = ConvertX(x: wx);
75 int sy = ConvertY(y: wy);
76 if(str_comp(a: pTuneLayer->m_aFileName, b: m_pEditor->m_aFileName))
77 {
78 m_pEditor->m_TuningNum = pTuneLayer->m_TuningNumber;
79 }
80
81 bool Destructive = m_pEditor->m_BrushDrawDestructive || IsEmpty(pLayer: pTuneLayer);
82
83 for(int y = 0; y < pTuneLayer->m_Height; y++)
84 for(int x = 0; x < pTuneLayer->m_Width; x++)
85 {
86 int fx = x + sx;
87 int fy = y + sy;
88
89 if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height)
90 continue;
91
92 if(!Destructive && GetTile(x: fx, y: fy).m_Index)
93 continue;
94
95 int Index = fy * m_Width + fx;
96 STuneTileStateChange::SData Previous{
97 .m_Number: m_pTuneTile[Index].m_Number,
98 .m_Type: m_pTuneTile[Index].m_Type,
99 .m_Index: m_pTiles[Index].m_Index};
100
101 if((m_pEditor->m_AllowPlaceUnusedTiles || IsValidTuneTile(Index: pTuneLayer->m_pTiles[y * pTuneLayer->m_Width + x].m_Index)) && pTuneLayer->m_pTiles[y * pTuneLayer->m_Width + x].m_Index != TILE_AIR)
102 {
103 if(m_pEditor->m_TuningNum != pTuneLayer->m_TuningNumber)
104 {
105 m_pTuneTile[Index].m_Number = m_pEditor->m_TuningNum;
106 }
107 else if(pTuneLayer->m_pTuneTile[y * pTuneLayer->m_Width + x].m_Number)
108 m_pTuneTile[Index].m_Number = pTuneLayer->m_pTuneTile[y * pTuneLayer->m_Width + x].m_Number;
109 else
110 {
111 if(!m_pEditor->m_TuningNum)
112 {
113 m_pTuneTile[Index].m_Number = 0;
114 m_pTuneTile[Index].m_Type = 0;
115 m_pTiles[Index].m_Index = 0;
116 continue;
117 }
118 else
119 m_pTuneTile[Index].m_Number = m_pEditor->m_TuningNum;
120 }
121
122 m_pTuneTile[Index].m_Type = pTuneLayer->m_pTiles[y * pTuneLayer->m_Width + x].m_Index;
123 m_pTiles[Index].m_Index = pTuneLayer->m_pTiles[y * pTuneLayer->m_Width + x].m_Index;
124 }
125 else
126 {
127 m_pTuneTile[Index].m_Number = 0;
128 m_pTuneTile[Index].m_Type = 0;
129 m_pTiles[Index].m_Index = 0;
130
131 if(pTuneLayer->m_pTiles[y * pTuneLayer->m_Width + x].m_Index != TILE_AIR)
132 ShowPreventUnusedTilesWarning();
133 }
134
135 STuneTileStateChange::SData Current{
136 .m_Number: m_pTuneTile[Index].m_Number,
137 .m_Type: m_pTuneTile[Index].m_Type,
138 .m_Index: m_pTiles[Index].m_Index};
139
140 RecordStateChange(x: fx, y: fy, Previous, Current);
141 }
142 FlagModified(x: sx, y: sy, w: pTuneLayer->m_Width, h: pTuneLayer->m_Height);
143}
144
145void CLayerTune::RecordStateChange(int x, int y, STuneTileStateChange::SData Previous, STuneTileStateChange::SData Current)
146{
147 if(!m_History[y][x].m_Changed)
148 m_History[y][x] = STuneTileStateChange{.m_Changed: true, .m_Previous: Previous, .m_Current: Current};
149 else
150 m_History[y][x].m_Current = Current;
151}
152
153void CLayerTune::BrushFlipX()
154{
155 CLayerTiles::BrushFlipX();
156 BrushFlipXImpl(pTiles: m_pTuneTile);
157}
158
159void CLayerTune::BrushFlipY()
160{
161 CLayerTiles::BrushFlipY();
162 BrushFlipYImpl(pTiles: m_pTuneTile);
163}
164
165void CLayerTune::BrushRotate(float Amount)
166{
167 int Rotation = (round_to_int(f: 360.0f * Amount / (pi * 2)) / 90) % 4; // 0=0°, 1=90°, 2=180°, 3=270°
168 if(Rotation < 0)
169 Rotation += 4;
170
171 if(Rotation == 1 || Rotation == 3)
172 {
173 // 90° rotation
174 CTuneTile *pTempData1 = new CTuneTile[m_Width * m_Height];
175 CTile *pTempData2 = new CTile[m_Width * m_Height];
176 mem_copy(dest: pTempData1, source: m_pTuneTile, size: (size_t)m_Width * m_Height * sizeof(CTuneTile));
177 mem_copy(dest: pTempData2, source: m_pTiles, size: (size_t)m_Width * m_Height * sizeof(CTile));
178 CTuneTile *pDst1 = m_pTuneTile;
179 CTile *pDst2 = m_pTiles;
180 for(int x = 0; x < m_Width; ++x)
181 for(int y = m_Height - 1; y >= 0; --y, ++pDst1, ++pDst2)
182 {
183 *pDst1 = pTempData1[y * m_Width + x];
184 *pDst2 = pTempData2[y * m_Width + x];
185 }
186
187 std::swap(a&: m_Width, b&: m_Height);
188 delete[] pTempData1;
189 delete[] pTempData2;
190 }
191
192 if(Rotation == 2 || Rotation == 3)
193 {
194 BrushFlipX();
195 BrushFlipY();
196 }
197}
198
199void CLayerTune::FillSelection(bool Empty, std::shared_ptr<CLayer> pBrush, CUIRect Rect)
200{
201 if(m_Readonly || (!Empty && pBrush->m_Type != LAYERTYPE_TILES))
202 return;
203
204 Snap(pRect: &Rect); // corrects Rect; no need of <=
205
206 int sx = ConvertX(x: Rect.x);
207 int sy = ConvertY(y: Rect.y);
208 int w = ConvertX(x: Rect.w);
209 int h = ConvertY(y: Rect.h);
210
211 std::shared_ptr<CLayerTune> pLt = std::static_pointer_cast<CLayerTune>(r: pBrush);
212
213 bool Destructive = m_pEditor->m_BrushDrawDestructive || Empty || IsEmpty(pLayer: pLt);
214
215 for(int y = 0; y < h; y++)
216 {
217 for(int x = 0; x < w; x++)
218 {
219 int fx = x + sx;
220 int fy = y + sy;
221
222 if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height)
223 continue;
224
225 if(!Destructive && GetTile(x: fx, y: fy).m_Index)
226 continue;
227
228 const int SrcIndex = Empty ? 0 : (y * pLt->m_Width + x % pLt->m_Width) % (pLt->m_Width * pLt->m_Height);
229 const int TgtIndex = fy * m_Width + fx;
230
231 STuneTileStateChange::SData Previous{
232 .m_Number: m_pTuneTile[TgtIndex].m_Number,
233 .m_Type: m_pTuneTile[TgtIndex].m_Type,
234 .m_Index: m_pTiles[TgtIndex].m_Index};
235
236 if(Empty || (!m_pEditor->m_AllowPlaceUnusedTiles && !IsValidTuneTile(Index: (pLt->m_pTiles[SrcIndex]).m_Index)))
237 {
238 m_pTiles[TgtIndex].m_Index = 0;
239 m_pTuneTile[TgtIndex].m_Type = 0;
240 m_pTuneTile[TgtIndex].m_Number = 0;
241
242 if(!Empty)
243 ShowPreventUnusedTilesWarning();
244 }
245 else
246 {
247 m_pTiles[TgtIndex] = pLt->m_pTiles[SrcIndex];
248 if(pLt->m_Tune && m_pTiles[TgtIndex].m_Index > 0)
249 {
250 m_pTuneTile[TgtIndex].m_Type = m_pTiles[fy * m_Width + fx].m_Index;
251
252 if((pLt->m_pTuneTile[SrcIndex].m_Number == 0 && m_pEditor->m_TuningNum) || m_pEditor->m_TuningNum != pLt->m_TuningNumber)
253 m_pTuneTile[TgtIndex].m_Number = m_pEditor->m_TuningNum;
254 else
255 m_pTuneTile[TgtIndex].m_Number = pLt->m_pTuneTile[SrcIndex].m_Number;
256 }
257 }
258
259 STuneTileStateChange::SData Current{
260 .m_Number: m_pTuneTile[TgtIndex].m_Number,
261 .m_Type: m_pTuneTile[TgtIndex].m_Type,
262 .m_Index: m_pTiles[TgtIndex].m_Index};
263
264 RecordStateChange(x: fx, y: fy, Previous, Current);
265 }
266 }
267
268 FlagModified(x: sx, y: sy, w, h);
269}
270
271std::shared_ptr<CLayer> CLayerTune::Duplicate() const
272{
273 return std::make_shared<CLayerTune>(args: *this);
274}
275
276const char *CLayerTune::TypeName() const
277{
278 return "tune";
279}
280