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