1 | #include "layer_speedup.h" |
2 | |
3 | #include <game/editor/editor.h> |
4 | |
5 | CLayerSpeedup::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 | |
15 | CLayerSpeedup::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 | |
25 | CLayerSpeedup::~CLayerSpeedup() |
26 | { |
27 | delete[] m_pSpeedupTile; |
28 | } |
29 | |
30 | void 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 | |
52 | void CLayerSpeedup::Shift(int Direction) |
53 | { |
54 | CLayerTiles::Shift(Direction); |
55 | ShiftImpl(pTiles: m_pSpeedupTile, Direction, ShiftBy: m_pEditor->m_ShiftBy); |
56 | } |
57 | |
58 | bool 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 | |
68 | void 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 | |
164 | void 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 | |
172 | void CLayerSpeedup::BrushFlipX() |
173 | { |
174 | CLayerTiles::BrushFlipX(); |
175 | BrushFlipXImpl(pTiles: m_pSpeedupTile); |
176 | } |
177 | |
178 | void CLayerSpeedup::BrushFlipY() |
179 | { |
180 | CLayerTiles::BrushFlipY(); |
181 | BrushFlipYImpl(pTiles: m_pSpeedupTile); |
182 | } |
183 | |
184 | void 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 | |
218 | void 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 | |
305 | std::shared_ptr<CLayer> CLayerSpeedup::Duplicate() const |
306 | { |
307 | return std::make_shared<CLayerSpeedup>(args: *this); |
308 | } |
309 | |
310 | const char *CLayerSpeedup::TypeName() const |
311 | { |
312 | return "speedup" ; |
313 | } |
314 | |