1 | /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ |
2 | /* If you are missing that file, acquire a complete release at teeworlds.com. */ |
3 | #include "layer_tiles.h" |
4 | |
5 | #include <engine/keys.h> |
6 | #include <engine/shared/map.h> |
7 | #include <game/editor/editor.h> |
8 | #include <game/editor/editor_actions.h> |
9 | |
10 | #include <iterator> |
11 | #include <numeric> |
12 | |
13 | #include "image.h" |
14 | |
15 | CLayerTiles::CLayerTiles(CEditor *pEditor, int w, int h) : |
16 | CLayer(pEditor) |
17 | { |
18 | m_Type = LAYERTYPE_TILES; |
19 | m_aName[0] = '\0'; |
20 | m_Width = w; |
21 | m_Height = h; |
22 | m_Image = -1; |
23 | m_Game = 0; |
24 | m_Color.r = 255; |
25 | m_Color.g = 255; |
26 | m_Color.b = 255; |
27 | m_Color.a = 255; |
28 | m_ColorEnv = -1; |
29 | m_ColorEnvOffset = 0; |
30 | |
31 | m_Tele = 0; |
32 | m_Speedup = 0; |
33 | m_Front = 0; |
34 | m_Switch = 0; |
35 | m_Tune = 0; |
36 | m_AutoMapperConfig = -1; |
37 | m_Seed = 0; |
38 | m_AutoAutoMap = false; |
39 | |
40 | m_pTiles = new CTile[m_Width * m_Height]; |
41 | mem_zero(block: m_pTiles, size: (size_t)m_Width * m_Height * sizeof(CTile)); |
42 | } |
43 | |
44 | CLayerTiles::CLayerTiles(const CLayerTiles &Other) : |
45 | CLayer(Other) |
46 | { |
47 | m_Width = Other.m_Width; |
48 | m_Height = Other.m_Height; |
49 | m_pTiles = new CTile[m_Width * m_Height]; |
50 | mem_copy(dest: m_pTiles, source: Other.m_pTiles, size: (size_t)m_Width * m_Height * sizeof(CTile)); |
51 | |
52 | m_Image = Other.m_Image; |
53 | m_Game = Other.m_Game; |
54 | m_Color = Other.m_Color; |
55 | m_ColorEnv = Other.m_ColorEnv; |
56 | m_ColorEnvOffset = Other.m_ColorEnvOffset; |
57 | |
58 | m_AutoMapperConfig = Other.m_AutoMapperConfig; |
59 | m_Seed = Other.m_Seed; |
60 | m_AutoAutoMap = Other.m_AutoAutoMap; |
61 | m_Tele = Other.m_Tele; |
62 | m_Speedup = Other.m_Speedup; |
63 | m_Front = Other.m_Front; |
64 | m_Switch = Other.m_Switch; |
65 | m_Tune = Other.m_Tune; |
66 | |
67 | str_copy(dst&: m_aFileName, src: Other.m_aFileName); |
68 | } |
69 | |
70 | CLayerTiles::~CLayerTiles() |
71 | { |
72 | delete[] m_pTiles; |
73 | } |
74 | |
75 | CTile CLayerTiles::GetTile(int x, int y) |
76 | { |
77 | return m_pTiles[y * m_Width + x]; |
78 | } |
79 | |
80 | void CLayerTiles::SetTile(int x, int y, CTile Tile) |
81 | { |
82 | auto CurrentTile = m_pTiles[y * m_Width + x]; |
83 | SetTileIgnoreHistory(x, y, Tile); |
84 | RecordStateChange(x, y, Previous: CurrentTile, Tile); |
85 | } |
86 | |
87 | void CLayerTiles::SetTileIgnoreHistory(int x, int y, CTile Tile) const |
88 | { |
89 | m_pTiles[y * m_Width + x] = Tile; |
90 | } |
91 | |
92 | void CLayerTiles::RecordStateChange(int x, int y, CTile Previous, CTile Tile) |
93 | { |
94 | if(!m_TilesHistory[y][x].m_Changed) |
95 | m_TilesHistory[y][x] = STileStateChange{.m_Changed: true, .m_Previous: Previous, .m_Current: Tile}; |
96 | else |
97 | m_TilesHistory[y][x].m_Current = Tile; |
98 | } |
99 | |
100 | void CLayerTiles::PrepareForSave() |
101 | { |
102 | for(int y = 0; y < m_Height; y++) |
103 | for(int x = 0; x < m_Width; x++) |
104 | m_pTiles[y * m_Width + x].m_Flags &= TILEFLAG_XFLIP | TILEFLAG_YFLIP | TILEFLAG_ROTATE; |
105 | |
106 | if(m_Image != -1 && m_Color.a == 255) |
107 | { |
108 | for(int y = 0; y < m_Height; y++) |
109 | for(int x = 0; x < m_Width; x++) |
110 | m_pTiles[y * m_Width + x].m_Flags |= m_pEditor->m_Map.m_vpImages[m_Image]->m_aTileFlags[m_pTiles[y * m_Width + x].m_Index]; |
111 | } |
112 | } |
113 | |
114 | void CLayerTiles::(int TilemapItemVersion, const CTile *pSavedTiles, size_t SavedTilesSize) const |
115 | { |
116 | const size_t DestSize = (size_t)m_Width * m_Height; |
117 | if(TilemapItemVersion >= CMapItemLayerTilemap::TILE_SKIP_MIN_VERSION) |
118 | CMap::ExtractTiles(pDest: m_pTiles, DestSize, pSrc: pSavedTiles, SrcSize: SavedTilesSize); |
119 | else if(SavedTilesSize >= DestSize) |
120 | mem_copy(dest: m_pTiles, source: pSavedTiles, size: DestSize * sizeof(CTile)); |
121 | } |
122 | |
123 | void CLayerTiles::MakePalette() const |
124 | { |
125 | for(int y = 0; y < m_Height; y++) |
126 | for(int x = 0; x < m_Width; x++) |
127 | m_pTiles[y * m_Width + x].m_Index = y * 16 + x; |
128 | } |
129 | |
130 | void CLayerTiles::Render(bool Tileset) |
131 | { |
132 | IGraphics::CTextureHandle Texture; |
133 | if(m_Image >= 0 && (size_t)m_Image < m_pEditor->m_Map.m_vpImages.size()) |
134 | Texture = m_pEditor->m_Map.m_vpImages[m_Image]->m_Texture; |
135 | else if(m_Game) |
136 | Texture = m_pEditor->GetEntitiesTexture(); |
137 | else if(m_Front) |
138 | Texture = m_pEditor->GetFrontTexture(); |
139 | else if(m_Tele) |
140 | Texture = m_pEditor->GetTeleTexture(); |
141 | else if(m_Speedup) |
142 | Texture = m_pEditor->GetSpeedupTexture(); |
143 | else if(m_Switch) |
144 | Texture = m_pEditor->GetSwitchTexture(); |
145 | else if(m_Tune) |
146 | Texture = m_pEditor->GetTuneTexture(); |
147 | Graphics()->TextureSet(Texture); |
148 | |
149 | ColorRGBA ColorEnv = ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f); |
150 | CEditor::EnvelopeEval(TimeOffsetMillis: m_ColorEnvOffset, Env: m_ColorEnv, Result&: ColorEnv, Channels: 4, pUser: m_pEditor); |
151 | const ColorRGBA Color = ColorRGBA(m_Color.r / 255.0f, m_Color.g / 255.0f, m_Color.b / 255.0f, m_Color.a / 255.0f).Multiply(Other: ColorEnv); |
152 | |
153 | Graphics()->BlendNone(); |
154 | m_pEditor->RenderTools()->RenderTilemap(pTiles: m_pTiles, w: m_Width, h: m_Height, Scale: 32.0f, Color, RenderFlags: LAYERRENDERFLAG_OPAQUE); |
155 | Graphics()->BlendNormal(); |
156 | m_pEditor->RenderTools()->RenderTilemap(pTiles: m_pTiles, w: m_Width, h: m_Height, Scale: 32.0f, Color, RenderFlags: LAYERRENDERFLAG_TRANSPARENT); |
157 | |
158 | // Render DDRace Layers |
159 | if(!Tileset) |
160 | { |
161 | if(m_Tele) |
162 | m_pEditor->RenderTools()->RenderTeleOverlay(pTele: static_cast<CLayerTele *>(this)->m_pTeleTile, w: m_Width, h: m_Height, Scale: 32.0f); |
163 | if(m_Speedup) |
164 | m_pEditor->RenderTools()->RenderSpeedupOverlay(pSpeedup: static_cast<CLayerSpeedup *>(this)->m_pSpeedupTile, w: m_Width, h: m_Height, Scale: 32.0f); |
165 | if(m_Switch) |
166 | m_pEditor->RenderTools()->RenderSwitchOverlay(pSwitch: static_cast<CLayerSwitch *>(this)->m_pSwitchTile, w: m_Width, h: m_Height, Scale: 32.0f); |
167 | if(m_Tune) |
168 | m_pEditor->RenderTools()->RenderTuneOverlay(pTune: static_cast<CLayerTune *>(this)->m_pTuneTile, w: m_Width, h: m_Height, Scale: 32.0f); |
169 | } |
170 | } |
171 | |
172 | int CLayerTiles::ConvertX(float x) const { return (int)(x / 32.0f); } |
173 | int CLayerTiles::ConvertY(float y) const { return (int)(y / 32.0f); } |
174 | |
175 | void CLayerTiles::Convert(CUIRect Rect, RECTi *pOut) const |
176 | { |
177 | pOut->x = ConvertX(x: Rect.x); |
178 | pOut->y = ConvertY(y: Rect.y); |
179 | pOut->w = ConvertX(x: Rect.x + Rect.w + 31) - pOut->x; |
180 | pOut->h = ConvertY(y: Rect.y + Rect.h + 31) - pOut->y; |
181 | } |
182 | |
183 | void CLayerTiles::Snap(CUIRect *pRect) const |
184 | { |
185 | RECTi Out; |
186 | Convert(Rect: *pRect, pOut: &Out); |
187 | pRect->x = Out.x * 32.0f; |
188 | pRect->y = Out.y * 32.0f; |
189 | pRect->w = Out.w * 32.0f; |
190 | pRect->h = Out.h * 32.0f; |
191 | } |
192 | |
193 | void CLayerTiles::Clamp(RECTi *pRect) const |
194 | { |
195 | if(pRect->x < 0) |
196 | { |
197 | pRect->w += pRect->x; |
198 | pRect->x = 0; |
199 | } |
200 | |
201 | if(pRect->y < 0) |
202 | { |
203 | pRect->h += pRect->y; |
204 | pRect->y = 0; |
205 | } |
206 | |
207 | if(pRect->x + pRect->w > m_Width) |
208 | pRect->w = m_Width - pRect->x; |
209 | |
210 | if(pRect->y + pRect->h > m_Height) |
211 | pRect->h = m_Height - pRect->y; |
212 | |
213 | if(pRect->h < 0) |
214 | pRect->h = 0; |
215 | if(pRect->w < 0) |
216 | pRect->w = 0; |
217 | } |
218 | |
219 | bool CLayerTiles::IsEntitiesLayer() const |
220 | { |
221 | return m_pEditor->m_Map.m_pGameLayer.get() == this || m_pEditor->m_Map.m_pTeleLayer.get() == this || m_pEditor->m_Map.m_pSpeedupLayer.get() == this || m_pEditor->m_Map.m_pFrontLayer.get() == this || m_pEditor->m_Map.m_pSwitchLayer.get() == this || m_pEditor->m_Map.m_pTuneLayer.get() == this; |
222 | } |
223 | |
224 | bool CLayerTiles::IsEmpty(const std::shared_ptr<CLayerTiles> &pLayer) |
225 | { |
226 | for(int y = 0; y < pLayer->m_Height; y++) |
227 | { |
228 | for(int x = 0; x < pLayer->m_Width; x++) |
229 | { |
230 | int Index = pLayer->GetTile(x, y).m_Index; |
231 | if(Index) |
232 | { |
233 | if(pLayer->m_Game) |
234 | { |
235 | if(m_pEditor->m_AllowPlaceUnusedTiles || IsValidGameTile(Index)) |
236 | return false; |
237 | } |
238 | else if(pLayer->m_Front) |
239 | { |
240 | if(m_pEditor->m_AllowPlaceUnusedTiles || IsValidFrontTile(Index)) |
241 | return false; |
242 | } |
243 | else |
244 | return false; |
245 | } |
246 | } |
247 | } |
248 | |
249 | return true; |
250 | } |
251 | |
252 | void CLayerTiles::BrushSelecting(CUIRect Rect) |
253 | { |
254 | Graphics()->TextureClear(); |
255 | Graphics()->QuadsBegin(); |
256 | Graphics()->SetColor(r: 1.0f, g: 1.0f, b: 1.0f, a: 0.4f); |
257 | Snap(pRect: &Rect); |
258 | IGraphics::CQuadItem QuadItem(Rect.x, Rect.y, Rect.w, Rect.h); |
259 | Graphics()->QuadsDrawTL(pArray: &QuadItem, Num: 1); |
260 | Graphics()->QuadsEnd(); |
261 | char aBuf[16]; |
262 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%d⨯%d" , ConvertX(x: Rect.w), ConvertY(y: Rect.h)); |
263 | TextRender()->Text(x: Rect.x + 3.0f, y: Rect.y + 3.0f, Size: m_pEditor->m_ShowPicker ? 15.0f : m_pEditor->MapView()->ScaleLength(Value: 15.0f), pText: aBuf, LineWidth: -1.0f); |
264 | } |
265 | |
266 | template<typename T> |
267 | static void InitGrabbedLayer(std::shared_ptr<T> pLayer, CLayerTiles *pThisLayer) |
268 | { |
269 | pLayer->m_pEditor = pThisLayer->m_pEditor; |
270 | pLayer->m_Image = pThisLayer->m_Image; |
271 | pLayer->m_Game = pThisLayer->m_Game; |
272 | pLayer->m_Front = pThisLayer->m_Front; |
273 | pLayer->m_Tele = pThisLayer->m_Tele; |
274 | pLayer->m_Speedup = pThisLayer->m_Speedup; |
275 | pLayer->m_Switch = pThisLayer->m_Switch; |
276 | pLayer->m_Tune = pThisLayer->m_Tune; |
277 | if(pThisLayer->m_pEditor->m_BrushColorEnabled) |
278 | { |
279 | pLayer->m_Color = pThisLayer->m_Color; |
280 | pLayer->m_Color.a = 255; |
281 | } |
282 | }; |
283 | |
284 | int CLayerTiles::BrushGrab(std::shared_ptr<CLayerGroup> pBrush, CUIRect Rect) |
285 | { |
286 | RECTi r; |
287 | Convert(Rect, pOut: &r); |
288 | Clamp(pRect: &r); |
289 | |
290 | if(!r.w || !r.h) |
291 | return 0; |
292 | |
293 | // create new layers |
294 | if(this->m_Tele) |
295 | { |
296 | std::shared_ptr<CLayerTele> pGrabbed = std::make_shared<CLayerTele>(args&: m_pEditor, args&: r.w, args&: r.h); |
297 | InitGrabbedLayer(pLayer: pGrabbed, pThisLayer: this); |
298 | |
299 | pBrush->AddLayer(pLayer: pGrabbed); |
300 | |
301 | // copy the tiles |
302 | for(int y = 0; y < r.h; y++) |
303 | for(int x = 0; x < r.w; x++) |
304 | pGrabbed->m_pTiles[y * pGrabbed->m_Width + x] = GetTile(x: r.x + x, y: r.y + y); |
305 | |
306 | // copy the tele data |
307 | if(!m_pEditor->Input()->KeyIsPressed(Key: KEY_SPACE)) |
308 | for(int y = 0; y < r.h; y++) |
309 | for(int x = 0; x < r.w; x++) |
310 | { |
311 | pGrabbed->m_pTeleTile[y * pGrabbed->m_Width + x] = static_cast<CLayerTele *>(this)->m_pTeleTile[(r.y + y) * m_Width + (r.x + x)]; |
312 | if(IsValidTeleTile(Index: pGrabbed->m_pTeleTile[y * pGrabbed->m_Width + x].m_Type)) |
313 | { |
314 | if(IsTeleTileNumberUsed(Index: pGrabbed->m_pTeleTile[y * pGrabbed->m_Width + x].m_Type, Checkpoint: false)) |
315 | m_pEditor->m_TeleNumber = pGrabbed->m_pTeleTile[y * pGrabbed->m_Width + x].m_Number; |
316 | else if(IsTeleTileNumberUsed(Index: pGrabbed->m_pTeleTile[y * pGrabbed->m_Width + x].m_Type, Checkpoint: true)) |
317 | m_pEditor->m_TeleCheckpointNumber = pGrabbed->m_pTeleTile[y * pGrabbed->m_Width + x].m_Number; |
318 | } |
319 | } |
320 | |
321 | pGrabbed->m_TeleNum = m_pEditor->m_TeleNumber; |
322 | pGrabbed->m_TeleCheckpointNum = m_pEditor->m_TeleCheckpointNumber; |
323 | |
324 | str_copy(dst&: pGrabbed->m_aFileName, src: m_pEditor->m_aFileName); |
325 | } |
326 | else if(this->m_Speedup) |
327 | { |
328 | std::shared_ptr<CLayerSpeedup> pGrabbed = std::make_shared<CLayerSpeedup>(args&: m_pEditor, args&: r.w, args&: r.h); |
329 | InitGrabbedLayer(pLayer: pGrabbed, pThisLayer: this); |
330 | |
331 | pBrush->AddLayer(pLayer: pGrabbed); |
332 | |
333 | // copy the tiles |
334 | for(int y = 0; y < r.h; y++) |
335 | for(int x = 0; x < r.w; x++) |
336 | pGrabbed->m_pTiles[y * pGrabbed->m_Width + x] = GetTile(x: r.x + x, y: r.y + y); |
337 | |
338 | // copy the speedup data |
339 | if(!m_pEditor->Input()->KeyIsPressed(Key: KEY_SPACE)) |
340 | for(int y = 0; y < r.h; y++) |
341 | for(int x = 0; x < r.w; x++) |
342 | { |
343 | pGrabbed->m_pSpeedupTile[y * pGrabbed->m_Width + x] = static_cast<CLayerSpeedup *>(this)->m_pSpeedupTile[(r.y + y) * m_Width + (r.x + x)]; |
344 | if(IsValidSpeedupTile(Index: pGrabbed->m_pSpeedupTile[y * pGrabbed->m_Width + x].m_Type)) |
345 | { |
346 | m_pEditor->m_SpeedupAngle = pGrabbed->m_pSpeedupTile[y * pGrabbed->m_Width + x].m_Angle; |
347 | m_pEditor->m_SpeedupForce = pGrabbed->m_pSpeedupTile[y * pGrabbed->m_Width + x].m_Force; |
348 | m_pEditor->m_SpeedupMaxSpeed = pGrabbed->m_pSpeedupTile[y * pGrabbed->m_Width + x].m_MaxSpeed; |
349 | } |
350 | } |
351 | pGrabbed->m_SpeedupForce = m_pEditor->m_SpeedupForce; |
352 | pGrabbed->m_SpeedupMaxSpeed = m_pEditor->m_SpeedupMaxSpeed; |
353 | pGrabbed->m_SpeedupAngle = m_pEditor->m_SpeedupAngle; |
354 | str_copy(dst&: pGrabbed->m_aFileName, src: m_pEditor->m_aFileName); |
355 | } |
356 | else if(this->m_Switch) |
357 | { |
358 | std::shared_ptr<CLayerSwitch> pGrabbed = std::make_shared<CLayerSwitch>(args&: m_pEditor, args&: r.w, args&: r.h); |
359 | InitGrabbedLayer(pLayer: pGrabbed, pThisLayer: this); |
360 | |
361 | pBrush->AddLayer(pLayer: pGrabbed); |
362 | |
363 | // copy the tiles |
364 | for(int y = 0; y < r.h; y++) |
365 | for(int x = 0; x < r.w; x++) |
366 | pGrabbed->m_pTiles[y * pGrabbed->m_Width + x] = GetTile(x: r.x + x, y: r.y + y); |
367 | |
368 | // copy the switch data |
369 | if(!m_pEditor->Input()->KeyIsPressed(Key: KEY_SPACE)) |
370 | for(int y = 0; y < r.h; y++) |
371 | for(int x = 0; x < r.w; x++) |
372 | { |
373 | pGrabbed->m_pSwitchTile[y * pGrabbed->m_Width + x] = static_cast<CLayerSwitch *>(this)->m_pSwitchTile[(r.y + y) * m_Width + (r.x + x)]; |
374 | if(IsValidSwitchTile(Index: pGrabbed->m_pSwitchTile[y * pGrabbed->m_Width + x].m_Type)) |
375 | { |
376 | m_pEditor->m_SwitchNum = pGrabbed->m_pSwitchTile[y * pGrabbed->m_Width + x].m_Number; |
377 | m_pEditor->m_SwitchDelay = pGrabbed->m_pSwitchTile[y * pGrabbed->m_Width + x].m_Delay; |
378 | } |
379 | } |
380 | pGrabbed->m_SwitchNumber = m_pEditor->m_SwitchNum; |
381 | pGrabbed->m_SwitchDelay = m_pEditor->m_SwitchDelay; |
382 | str_copy(dst&: pGrabbed->m_aFileName, src: m_pEditor->m_aFileName); |
383 | } |
384 | |
385 | else if(this->m_Tune) |
386 | { |
387 | std::shared_ptr<CLayerTune> pGrabbed = std::make_shared<CLayerTune>(args&: m_pEditor, args&: r.w, args&: r.h); |
388 | InitGrabbedLayer(pLayer: pGrabbed, pThisLayer: this); |
389 | |
390 | pBrush->AddLayer(pLayer: pGrabbed); |
391 | |
392 | // copy the tiles |
393 | for(int y = 0; y < r.h; y++) |
394 | for(int x = 0; x < r.w; x++) |
395 | pGrabbed->m_pTiles[y * pGrabbed->m_Width + x] = GetTile(x: r.x + x, y: r.y + y); |
396 | |
397 | // copy the tiles |
398 | if(!m_pEditor->Input()->KeyIsPressed(Key: KEY_SPACE)) |
399 | for(int y = 0; y < r.h; y++) |
400 | for(int x = 0; x < r.w; x++) |
401 | { |
402 | pGrabbed->m_pTuneTile[y * pGrabbed->m_Width + x] = static_cast<CLayerTune *>(this)->m_pTuneTile[(r.y + y) * m_Width + (r.x + x)]; |
403 | if(IsValidTuneTile(Index: pGrabbed->m_pTuneTile[y * pGrabbed->m_Width + x].m_Type)) |
404 | { |
405 | m_pEditor->m_TuningNum = pGrabbed->m_pTuneTile[y * pGrabbed->m_Width + x].m_Number; |
406 | } |
407 | } |
408 | pGrabbed->m_TuningNumber = m_pEditor->m_TuningNum; |
409 | str_copy(dst&: pGrabbed->m_aFileName, src: m_pEditor->m_aFileName); |
410 | } |
411 | else if(this->m_Front) |
412 | { |
413 | std::shared_ptr<CLayerFront> pGrabbed = std::make_shared<CLayerFront>(args&: m_pEditor, args&: r.w, args&: r.h); |
414 | InitGrabbedLayer(pLayer: pGrabbed, pThisLayer: this); |
415 | |
416 | pBrush->AddLayer(pLayer: pGrabbed); |
417 | |
418 | // copy the tiles |
419 | for(int y = 0; y < r.h; y++) |
420 | for(int x = 0; x < r.w; x++) |
421 | pGrabbed->m_pTiles[y * pGrabbed->m_Width + x] = GetTile(x: r.x + x, y: r.y + y); |
422 | str_copy(dst&: pGrabbed->m_aFileName, src: m_pEditor->m_aFileName); |
423 | } |
424 | else |
425 | { |
426 | std::shared_ptr<CLayerTiles> pGrabbed = std::make_shared<CLayerFront>(args&: m_pEditor, args&: r.w, args&: r.h); |
427 | InitGrabbedLayer(pLayer: pGrabbed, pThisLayer: this); |
428 | |
429 | pBrush->AddLayer(pLayer: pGrabbed); |
430 | |
431 | // copy the tiles |
432 | for(int y = 0; y < r.h; y++) |
433 | for(int x = 0; x < r.w; x++) |
434 | pGrabbed->m_pTiles[y * pGrabbed->m_Width + x] = GetTile(x: r.x + x, y: r.y + y); |
435 | str_copy(dst&: pGrabbed->m_aFileName, src: m_pEditor->m_aFileName); |
436 | } |
437 | |
438 | return 1; |
439 | } |
440 | |
441 | void CLayerTiles::FillSelection(bool Empty, std::shared_ptr<CLayer> pBrush, CUIRect Rect) |
442 | { |
443 | if(m_Readonly || (!Empty && pBrush->m_Type != LAYERTYPE_TILES)) |
444 | return; |
445 | |
446 | Snap(pRect: &Rect); |
447 | |
448 | int sx = ConvertX(x: Rect.x); |
449 | int sy = ConvertY(y: Rect.y); |
450 | int w = ConvertX(x: Rect.w); |
451 | int h = ConvertY(y: Rect.h); |
452 | |
453 | std::shared_ptr<CLayerTiles> pLt = std::static_pointer_cast<CLayerTiles>(r: pBrush); |
454 | |
455 | bool Destructive = m_pEditor->m_BrushDrawDestructive || Empty || IsEmpty(pLayer: pLt); |
456 | |
457 | for(int y = 0; y < h; y++) |
458 | { |
459 | for(int x = 0; x < w; x++) |
460 | { |
461 | int fx = x + sx; |
462 | int fy = y + sy; |
463 | |
464 | if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height) |
465 | continue; |
466 | |
467 | bool HasTile = GetTile(x: fx, y: fy).m_Index; |
468 | if(!Empty && pLt->GetTile(x: x % pLt->m_Width, y: y % pLt->m_Height).m_Index == TILE_THROUGH_CUT) |
469 | { |
470 | if(m_Game && m_pEditor->m_Map.m_pFrontLayer) |
471 | { |
472 | HasTile = HasTile || m_pEditor->m_Map.m_pFrontLayer->GetTile(x: fx, y: fy).m_Index; |
473 | } |
474 | else if(m_Front) |
475 | { |
476 | HasTile = HasTile || m_pEditor->m_Map.m_pGameLayer->GetTile(x: fx, y: fy).m_Index; |
477 | } |
478 | } |
479 | |
480 | if(!Destructive && HasTile) |
481 | continue; |
482 | |
483 | SetTile(x: fx, y: fy, Tile: Empty ? CTile{.m_Index: TILE_AIR} : pLt->m_pTiles[(y * pLt->m_Width + x % pLt->m_Width) % (pLt->m_Width * pLt->m_Height)]); |
484 | } |
485 | } |
486 | FlagModified(x: sx, y: sy, w, h); |
487 | } |
488 | |
489 | void CLayerTiles::BrushDraw(std::shared_ptr<CLayer> pBrush, float wx, float wy) |
490 | { |
491 | if(m_Readonly) |
492 | return; |
493 | |
494 | // |
495 | std::shared_ptr<CLayerTiles> pTileLayer = std::static_pointer_cast<CLayerTiles>(r: pBrush); |
496 | int sx = ConvertX(x: wx); |
497 | int sy = ConvertY(y: wy); |
498 | |
499 | bool Destructive = m_pEditor->m_BrushDrawDestructive || IsEmpty(pLayer: pTileLayer); |
500 | |
501 | for(int y = 0; y < pTileLayer->m_Height; y++) |
502 | for(int x = 0; x < pTileLayer->m_Width; x++) |
503 | { |
504 | int fx = x + sx; |
505 | int fy = y + sy; |
506 | |
507 | if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height) |
508 | continue; |
509 | |
510 | bool HasTile = GetTile(x: fx, y: fy).m_Index; |
511 | if(pTileLayer->GetTile(x, y).m_Index == TILE_THROUGH_CUT) |
512 | { |
513 | if(m_Game && m_pEditor->m_Map.m_pFrontLayer) |
514 | { |
515 | HasTile = HasTile || m_pEditor->m_Map.m_pFrontLayer->GetTile(x: fx, y: fy).m_Index; |
516 | } |
517 | else if(m_Front) |
518 | { |
519 | HasTile = HasTile || m_pEditor->m_Map.m_pGameLayer->GetTile(x: fx, y: fy).m_Index; |
520 | } |
521 | } |
522 | |
523 | if(!Destructive && HasTile) |
524 | continue; |
525 | |
526 | SetTile(x: fx, y: fy, Tile: pTileLayer->GetTile(x, y)); |
527 | } |
528 | |
529 | FlagModified(x: sx, y: sy, w: pTileLayer->m_Width, h: pTileLayer->m_Height); |
530 | } |
531 | |
532 | void CLayerTiles::BrushFlipX() |
533 | { |
534 | BrushFlipXImpl(pTiles: m_pTiles); |
535 | |
536 | if(m_Tele || m_Speedup || m_Tune) |
537 | return; |
538 | |
539 | bool Rotate = !(m_Game || m_Front || m_Switch) || m_pEditor->m_AllowPlaceUnusedTiles; |
540 | for(int y = 0; y < m_Height; y++) |
541 | for(int x = 0; x < m_Width; x++) |
542 | if(!Rotate && !IsRotatableTile(Index: m_pTiles[y * m_Width + x].m_Index)) |
543 | m_pTiles[y * m_Width + x].m_Flags = 0; |
544 | else |
545 | m_pTiles[y * m_Width + x].m_Flags ^= (m_pTiles[y * m_Width + x].m_Flags & TILEFLAG_ROTATE) ? TILEFLAG_YFLIP : TILEFLAG_XFLIP; |
546 | } |
547 | |
548 | void CLayerTiles::BrushFlipY() |
549 | { |
550 | BrushFlipYImpl(pTiles: m_pTiles); |
551 | |
552 | if(m_Tele || m_Speedup || m_Tune) |
553 | return; |
554 | |
555 | bool Rotate = !(m_Game || m_Front || m_Switch) || m_pEditor->m_AllowPlaceUnusedTiles; |
556 | for(int y = 0; y < m_Height; y++) |
557 | for(int x = 0; x < m_Width; x++) |
558 | if(!Rotate && !IsRotatableTile(Index: m_pTiles[y * m_Width + x].m_Index)) |
559 | m_pTiles[y * m_Width + x].m_Flags = 0; |
560 | else |
561 | m_pTiles[y * m_Width + x].m_Flags ^= (m_pTiles[y * m_Width + x].m_Flags & TILEFLAG_ROTATE) ? TILEFLAG_XFLIP : TILEFLAG_YFLIP; |
562 | } |
563 | |
564 | void CLayerTiles::BrushRotate(float Amount) |
565 | { |
566 | int Rotation = (round_to_int(f: 360.0f * Amount / (pi * 2)) / 90) % 4; // 0=0°, 1=90°, 2=180°, 3=270° |
567 | if(Rotation < 0) |
568 | Rotation += 4; |
569 | |
570 | if(Rotation == 1 || Rotation == 3) |
571 | { |
572 | // 90° rotation |
573 | CTile *pTempData = new CTile[m_Width * m_Height]; |
574 | mem_copy(dest: pTempData, source: m_pTiles, size: (size_t)m_Width * m_Height * sizeof(CTile)); |
575 | CTile *pDst = m_pTiles; |
576 | bool Rotate = !(m_Game || m_Front) || m_pEditor->m_AllowPlaceUnusedTiles; |
577 | for(int x = 0; x < m_Width; ++x) |
578 | for(int y = m_Height - 1; y >= 0; --y, ++pDst) |
579 | { |
580 | *pDst = pTempData[y * m_Width + x]; |
581 | if(!Rotate && !IsRotatableTile(Index: pDst->m_Index)) |
582 | pDst->m_Flags = 0; |
583 | else |
584 | { |
585 | if(pDst->m_Flags & TILEFLAG_ROTATE) |
586 | pDst->m_Flags ^= (TILEFLAG_YFLIP | TILEFLAG_XFLIP); |
587 | pDst->m_Flags ^= TILEFLAG_ROTATE; |
588 | } |
589 | } |
590 | |
591 | std::swap(a&: m_Width, b&: m_Height); |
592 | delete[] pTempData; |
593 | } |
594 | |
595 | if(Rotation == 2 || Rotation == 3) |
596 | { |
597 | BrushFlipX(); |
598 | BrushFlipY(); |
599 | } |
600 | } |
601 | |
602 | std::shared_ptr<CLayer> CLayerTiles::Duplicate() const |
603 | { |
604 | return std::make_shared<CLayerTiles>(args: *this); |
605 | } |
606 | |
607 | const char *CLayerTiles::TypeName() const |
608 | { |
609 | return "tiles" ; |
610 | } |
611 | |
612 | void CLayerTiles::Resize(int NewW, int NewH) |
613 | { |
614 | CTile *pNewData = new CTile[NewW * NewH]; |
615 | mem_zero(block: pNewData, size: (size_t)NewW * NewH * sizeof(CTile)); |
616 | |
617 | // copy old data |
618 | for(int y = 0; y < minimum(a: NewH, b: m_Height); y++) |
619 | mem_copy(dest: &pNewData[y * NewW], source: &m_pTiles[y * m_Width], size: minimum(a: m_Width, b: NewW) * sizeof(CTile)); |
620 | |
621 | // replace old |
622 | delete[] m_pTiles; |
623 | m_pTiles = pNewData; |
624 | m_Width = NewW; |
625 | m_Height = NewH; |
626 | |
627 | // resize tele layer if available |
628 | if(m_Game && m_pEditor->m_Map.m_pTeleLayer && (m_pEditor->m_Map.m_pTeleLayer->m_Width != NewW || m_pEditor->m_Map.m_pTeleLayer->m_Height != NewH)) |
629 | m_pEditor->m_Map.m_pTeleLayer->Resize(NewW, NewH); |
630 | |
631 | // resize speedup layer if available |
632 | if(m_Game && m_pEditor->m_Map.m_pSpeedupLayer && (m_pEditor->m_Map.m_pSpeedupLayer->m_Width != NewW || m_pEditor->m_Map.m_pSpeedupLayer->m_Height != NewH)) |
633 | m_pEditor->m_Map.m_pSpeedupLayer->Resize(NewW, NewH); |
634 | |
635 | // resize front layer |
636 | if(m_Game && m_pEditor->m_Map.m_pFrontLayer && (m_pEditor->m_Map.m_pFrontLayer->m_Width != NewW || m_pEditor->m_Map.m_pFrontLayer->m_Height != NewH)) |
637 | m_pEditor->m_Map.m_pFrontLayer->Resize(NewW, NewH); |
638 | |
639 | // resize switch layer if available |
640 | if(m_Game && m_pEditor->m_Map.m_pSwitchLayer && (m_pEditor->m_Map.m_pSwitchLayer->m_Width != NewW || m_pEditor->m_Map.m_pSwitchLayer->m_Height != NewH)) |
641 | m_pEditor->m_Map.m_pSwitchLayer->Resize(NewW, NewH); |
642 | |
643 | // resize tune layer if available |
644 | if(m_Game && m_pEditor->m_Map.m_pTuneLayer && (m_pEditor->m_Map.m_pTuneLayer->m_Width != NewW || m_pEditor->m_Map.m_pTuneLayer->m_Height != NewH)) |
645 | m_pEditor->m_Map.m_pTuneLayer->Resize(NewW, NewH); |
646 | } |
647 | |
648 | void CLayerTiles::Shift(int Direction) |
649 | { |
650 | ShiftImpl(pTiles: m_pTiles, Direction, ShiftBy: m_pEditor->m_ShiftBy); |
651 | } |
652 | |
653 | void CLayerTiles::ShowInfo() |
654 | { |
655 | float ScreenX0, ScreenY0, ScreenX1, ScreenY1; |
656 | Graphics()->GetScreen(pTopLeftX: &ScreenX0, pTopLeftY: &ScreenY0, pBottomRightX: &ScreenX1, pBottomRightY: &ScreenY1); |
657 | Graphics()->TextureSet(Texture: m_pEditor->Client()->GetDebugFont()); |
658 | Graphics()->QuadsBegin(); |
659 | |
660 | int StartY = maximum(a: 0, b: (int)(ScreenY0 / 32.0f) - 1); |
661 | int StartX = maximum(a: 0, b: (int)(ScreenX0 / 32.0f) - 1); |
662 | int EndY = minimum(a: (int)(ScreenY1 / 32.0f) + 1, b: m_Height); |
663 | int EndX = minimum(a: (int)(ScreenX1 / 32.0f) + 1, b: m_Width); |
664 | |
665 | for(int y = StartY; y < EndY; y++) |
666 | for(int x = StartX; x < EndX; x++) |
667 | { |
668 | int c = x + y * m_Width; |
669 | if(m_pTiles[c].m_Index) |
670 | { |
671 | char aBuf[4]; |
672 | if(m_pEditor->m_ShowTileInfo == CEditor::SHOW_TILE_HEXADECIMAL) |
673 | { |
674 | str_hex(dst: aBuf, dst_size: sizeof(aBuf), data: &m_pTiles[c].m_Index, data_size: 1); |
675 | aBuf[2] = '\0'; // would otherwise be a space |
676 | } |
677 | else |
678 | { |
679 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%d" , m_pTiles[c].m_Index); |
680 | } |
681 | m_pEditor->Graphics()->QuadsText(x: x * 32, y: y * 32, Size: 16.0f, pText: aBuf); |
682 | |
683 | char aFlags[4] = {m_pTiles[c].m_Flags & TILEFLAG_XFLIP ? 'X' : ' ', |
684 | m_pTiles[c].m_Flags & TILEFLAG_YFLIP ? 'Y' : ' ', |
685 | m_pTiles[c].m_Flags & TILEFLAG_ROTATE ? 'R' : ' ', |
686 | 0}; |
687 | m_pEditor->Graphics()->QuadsText(x: x * 32, y: y * 32 + 16, Size: 16.0f, pText: aFlags); |
688 | } |
689 | x += m_pTiles[c].m_Skip; |
690 | } |
691 | |
692 | Graphics()->QuadsEnd(); |
693 | Graphics()->MapScreen(TopLeftX: ScreenX0, TopLeftY: ScreenY0, BottomRightX: ScreenX1, BottomRightY: ScreenY1); |
694 | } |
695 | |
696 | CUi::EPopupMenuFunctionResult CLayerTiles::RenderProperties(CUIRect *pToolBox) |
697 | { |
698 | CUIRect Button; |
699 | |
700 | const bool EntitiesLayer = IsEntitiesLayer(); |
701 | |
702 | std::shared_ptr<CLayerGroup> pGroup = m_pEditor->m_Map.m_vpGroups[m_pEditor->m_SelectedGroup]; |
703 | |
704 | // Game tiles can only be constructed if the layer is relative to the game layer |
705 | if(!EntitiesLayer && !(pGroup->m_OffsetX % 32) && !(pGroup->m_OffsetY % 32) && pGroup->m_ParallaxX == 100 && pGroup->m_ParallaxY == 100) |
706 | { |
707 | pToolBox->HSplitBottom(Cut: 12.0f, pTop: pToolBox, pBottom: &Button); |
708 | static int s_GameTilesButton = 0; |
709 | if(m_pEditor->DoButton_Editor(pId: &s_GameTilesButton, pText: "Game tiles" , Checked: 0, pRect: &Button, Flags: 0, pToolTip: "Constructs game tiles from this layer" )) |
710 | m_pEditor->PopupSelectGametileOpInvoke(x: m_pEditor->Ui()->MouseX(), y: m_pEditor->Ui()->MouseY()); |
711 | const int Selected = m_pEditor->PopupSelectGameTileOpResult(); |
712 | int Result = Selected; |
713 | switch(Selected) |
714 | { |
715 | case 4: |
716 | Result = TILE_THROUGH_CUT; |
717 | break; |
718 | case 5: |
719 | Result = TILE_FREEZE; |
720 | break; |
721 | case 6: |
722 | Result = TILE_UNFREEZE; |
723 | break; |
724 | case 7: |
725 | Result = TILE_DFREEZE; |
726 | break; |
727 | case 8: |
728 | Result = TILE_DUNFREEZE; |
729 | break; |
730 | case 9: |
731 | Result = TILE_TELECHECKIN; |
732 | break; |
733 | case 10: |
734 | Result = TILE_TELECHECKINEVIL; |
735 | break; |
736 | case 11: |
737 | Result = TILE_LFREEZE; |
738 | break; |
739 | case 12: |
740 | Result = TILE_LUNFREEZE; |
741 | break; |
742 | default: |
743 | break; |
744 | } |
745 | if(Result > -1) |
746 | { |
747 | const int OffsetX = -pGroup->m_OffsetX / 32; |
748 | const int OffsetY = -pGroup->m_OffsetY / 32; |
749 | |
750 | static const char *s_apGametileOpNames[] = { |
751 | "Air" , |
752 | "Hookable" , |
753 | "Death" , |
754 | "Unhookable" , |
755 | "Hookthrough" , |
756 | "Freeze" , |
757 | "Unfreeze" , |
758 | "Deep Freeze" , |
759 | "Deep Unfreeze" , |
760 | "Blue Check-Tele" , |
761 | "Red Check-Tele" , |
762 | "Live Freeze" , |
763 | "Live Unfreeze" , |
764 | }; |
765 | |
766 | std::vector<std::shared_ptr<IEditorAction>> vpActions; |
767 | std::shared_ptr<CLayerTiles> pGLayer = m_pEditor->m_Map.m_pGameLayer; |
768 | int GameLayerIndex = std::find(first: m_pEditor->m_Map.m_pGameGroup->m_vpLayers.begin(), last: m_pEditor->m_Map.m_pGameGroup->m_vpLayers.end(), val: pGLayer) - m_pEditor->m_Map.m_pGameGroup->m_vpLayers.begin(); |
769 | |
770 | if(Result != TILE_TELECHECKIN && Result != TILE_TELECHECKINEVIL) |
771 | { |
772 | if(pGLayer->m_Width < m_Width + OffsetX || pGLayer->m_Height < m_Height + OffsetY) |
773 | { |
774 | std::map<int, std::shared_ptr<CLayer>> savedLayers; |
775 | savedLayers[LAYERTYPE_TILES] = pGLayer->Duplicate(); |
776 | savedLayers[LAYERTYPE_GAME] = savedLayers[LAYERTYPE_TILES]; |
777 | |
778 | int PrevW = pGLayer->m_Width; |
779 | int PrevH = pGLayer->m_Height; |
780 | const int NewW = pGLayer->m_Width < m_Width + OffsetX ? m_Width + OffsetX : pGLayer->m_Width; |
781 | const int NewH = pGLayer->m_Height < m_Height + OffsetY ? m_Height + OffsetY : pGLayer->m_Height; |
782 | pGLayer->Resize(NewW, NewH); |
783 | vpActions.push_back(x: std::make_shared<CEditorActionEditLayerTilesProp>(args&: m_pEditor, args&: m_pEditor->m_SelectedGroup, args&: GameLayerIndex, args: ETilesProp::PROP_WIDTH, args&: PrevW, args: NewW)); |
784 | const std::shared_ptr<CEditorActionEditLayerTilesProp> &Action1 = std::static_pointer_cast<CEditorActionEditLayerTilesProp>(r: vpActions[vpActions.size() - 1]); |
785 | vpActions.push_back(x: std::make_shared<CEditorActionEditLayerTilesProp>(args&: m_pEditor, args&: m_pEditor->m_SelectedGroup, args&: GameLayerIndex, args: ETilesProp::PROP_HEIGHT, args&: PrevH, args: NewH)); |
786 | const std::shared_ptr<CEditorActionEditLayerTilesProp> &Action2 = std::static_pointer_cast<CEditorActionEditLayerTilesProp>(r: vpActions[vpActions.size() - 1]); |
787 | |
788 | Action1->SetSavedLayers(savedLayers); |
789 | Action2->SetSavedLayers(savedLayers); |
790 | } |
791 | |
792 | int Changes = 0; |
793 | for(int y = OffsetY < 0 ? -OffsetY : 0; y < m_Height; y++) |
794 | { |
795 | for(int x = OffsetX < 0 ? -OffsetX : 0; x < m_Width; x++) |
796 | { |
797 | if(GetTile(x, y).m_Index) |
798 | { |
799 | const CTile ResultTile = {.m_Index: (unsigned char)Result}; |
800 | pGLayer->SetTile(x: x + OffsetX, y: y + OffsetY, Tile: ResultTile); |
801 | Changes++; |
802 | } |
803 | } |
804 | } |
805 | |
806 | vpActions.push_back(x: std::make_shared<CEditorBrushDrawAction>(args&: m_pEditor, args&: m_pEditor->m_SelectedGroup)); |
807 | char aDisplay[256]; |
808 | str_format(buffer: aDisplay, buffer_size: sizeof(aDisplay), format: "Construct '%s' game tiles (x%d)" , s_apGametileOpNames[Selected], Changes); |
809 | m_pEditor->m_EditorHistory.RecordAction(pAction: std::make_shared<CEditorActionBulk>(args&: m_pEditor, args&: vpActions, args&: aDisplay, args: true)); |
810 | } |
811 | else |
812 | { |
813 | if(!m_pEditor->m_Map.m_pTeleLayer) |
814 | { |
815 | std::shared_ptr<CLayerTele> pLayer = std::make_shared<CLayerTele>(args&: m_pEditor, args&: m_Width, args&: m_Height); |
816 | m_pEditor->m_Map.MakeTeleLayer(pLayer); |
817 | m_pEditor->m_Map.m_pGameGroup->AddLayer(pLayer); |
818 | |
819 | vpActions.push_back(x: std::make_shared<CEditorActionAddLayer>(args&: m_pEditor, args&: m_pEditor->m_SelectedGroup, args: m_pEditor->m_Map.m_pGameGroup->m_vpLayers.size() - 1)); |
820 | |
821 | if(m_Width != pGLayer->m_Width || m_Height > pGLayer->m_Height) |
822 | { |
823 | std::map<int, std::shared_ptr<CLayer>> savedLayers; |
824 | savedLayers[LAYERTYPE_TILES] = pGLayer->Duplicate(); |
825 | savedLayers[LAYERTYPE_GAME] = savedLayers[LAYERTYPE_TILES]; |
826 | |
827 | int NewW = pGLayer->m_Width; |
828 | int NewH = pGLayer->m_Height; |
829 | if(m_Width > pGLayer->m_Width) |
830 | { |
831 | NewW = m_Width; |
832 | } |
833 | if(m_Height > pGLayer->m_Height) |
834 | { |
835 | NewH = m_Height; |
836 | } |
837 | |
838 | int PrevW = pGLayer->m_Width; |
839 | int PrevH = pGLayer->m_Height; |
840 | pLayer->Resize(NewW, NewH); |
841 | vpActions.push_back(x: std::make_shared<CEditorActionEditLayerTilesProp>(args&: m_pEditor, args&: m_pEditor->m_SelectedGroup, args&: GameLayerIndex, args: ETilesProp::PROP_WIDTH, args&: PrevW, args&: NewW)); |
842 | const std::shared_ptr<CEditorActionEditLayerTilesProp> &Action1 = std::static_pointer_cast<CEditorActionEditLayerTilesProp>(r: vpActions[vpActions.size() - 1]); |
843 | vpActions.push_back(x: std::make_shared<CEditorActionEditLayerTilesProp>(args&: m_pEditor, args&: m_pEditor->m_SelectedGroup, args&: GameLayerIndex, args: ETilesProp::PROP_HEIGHT, args&: PrevH, args&: NewH)); |
844 | const std::shared_ptr<CEditorActionEditLayerTilesProp> &Action2 = std::static_pointer_cast<CEditorActionEditLayerTilesProp>(r: vpActions[vpActions.size() - 1]); |
845 | |
846 | Action1->SetSavedLayers(savedLayers); |
847 | Action2->SetSavedLayers(savedLayers); |
848 | } |
849 | } |
850 | |
851 | std::shared_ptr<CLayerTele> pTLayer = m_pEditor->m_Map.m_pTeleLayer; |
852 | int TeleLayerIndex = std::find(first: m_pEditor->m_Map.m_pGameGroup->m_vpLayers.begin(), last: m_pEditor->m_Map.m_pGameGroup->m_vpLayers.end(), val: pTLayer) - m_pEditor->m_Map.m_pGameGroup->m_vpLayers.begin(); |
853 | |
854 | if(pTLayer->m_Width < m_Width + OffsetX || pTLayer->m_Height < m_Height + OffsetY) |
855 | { |
856 | std::map<int, std::shared_ptr<CLayer>> savedLayers; |
857 | savedLayers[LAYERTYPE_TILES] = pTLayer->Duplicate(); |
858 | savedLayers[LAYERTYPE_TELE] = savedLayers[LAYERTYPE_TILES]; |
859 | |
860 | int PrevW = pTLayer->m_Width; |
861 | int PrevH = pTLayer->m_Height; |
862 | int NewW = pTLayer->m_Width < m_Width + OffsetX ? m_Width + OffsetX : pTLayer->m_Width; |
863 | int NewH = pTLayer->m_Height < m_Height + OffsetY ? m_Height + OffsetY : pTLayer->m_Height; |
864 | pTLayer->Resize(NewW, NewH); |
865 | std::shared_ptr<CEditorActionEditLayerTilesProp> Action1, Action2; |
866 | vpActions.push_back(x: Action1 = std::make_shared<CEditorActionEditLayerTilesProp>(args&: m_pEditor, args&: m_pEditor->m_SelectedGroup, args&: TeleLayerIndex, args: ETilesProp::PROP_WIDTH, args&: PrevW, args&: NewW)); |
867 | vpActions.push_back(x: Action2 = std::make_shared<CEditorActionEditLayerTilesProp>(args&: m_pEditor, args&: m_pEditor->m_SelectedGroup, args&: TeleLayerIndex, args: ETilesProp::PROP_HEIGHT, args&: PrevH, args&: NewH)); |
868 | |
869 | Action1->SetSavedLayers(savedLayers); |
870 | Action2->SetSavedLayers(savedLayers); |
871 | } |
872 | |
873 | int Changes = 0; |
874 | for(int y = OffsetY < 0 ? -OffsetY : 0; y < m_Height; y++) |
875 | { |
876 | for(int x = OffsetX < 0 ? -OffsetX : 0; x < m_Width; x++) |
877 | { |
878 | if(GetTile(x, y).m_Index) |
879 | { |
880 | auto TileIndex = (y + OffsetY) * pTLayer->m_Width + x + OffsetX; |
881 | Changes++; |
882 | |
883 | STeleTileStateChange::SData Previous{ |
884 | .m_Number: pTLayer->m_pTeleTile[TileIndex].m_Number, |
885 | .m_Type: pTLayer->m_pTeleTile[TileIndex].m_Type, |
886 | .m_Index: pTLayer->m_pTiles[TileIndex].m_Index}; |
887 | |
888 | pTLayer->m_pTiles[TileIndex].m_Index = TILE_AIR + Result; |
889 | pTLayer->m_pTeleTile[TileIndex].m_Number = 1; |
890 | pTLayer->m_pTeleTile[TileIndex].m_Type = TILE_AIR + Result; |
891 | |
892 | STeleTileStateChange::SData Current{ |
893 | .m_Number: pTLayer->m_pTeleTile[TileIndex].m_Number, |
894 | .m_Type: pTLayer->m_pTeleTile[TileIndex].m_Type, |
895 | .m_Index: pTLayer->m_pTiles[TileIndex].m_Index}; |
896 | |
897 | pTLayer->RecordStateChange(x, y, Previous, Current); |
898 | } |
899 | } |
900 | } |
901 | |
902 | vpActions.push_back(x: std::make_shared<CEditorBrushDrawAction>(args&: m_pEditor, args&: m_pEditor->m_SelectedGroup)); |
903 | char aDisplay[256]; |
904 | str_format(buffer: aDisplay, buffer_size: sizeof(aDisplay), format: "Construct 'tele' game tiles (x%d)" , Changes); |
905 | m_pEditor->m_EditorHistory.RecordAction(pAction: std::make_shared<CEditorActionBulk>(args&: m_pEditor, args&: vpActions, args&: aDisplay, args: true)); |
906 | } |
907 | } |
908 | } |
909 | |
910 | if(m_pEditor->m_Map.m_pGameLayer.get() != this) |
911 | { |
912 | if(m_Image >= 0 && (size_t)m_Image < m_pEditor->m_Map.m_vpImages.size() && m_pEditor->m_Map.m_vpImages[m_Image]->m_AutoMapper.IsLoaded() && m_AutoMapperConfig != -1) |
913 | { |
914 | pToolBox->HSplitBottom(Cut: 2.0f, pTop: pToolBox, pBottom: nullptr); |
915 | pToolBox->HSplitBottom(Cut: 12.0f, pTop: pToolBox, pBottom: &Button); |
916 | if(m_Seed != 0) |
917 | { |
918 | CUIRect ButtonAuto; |
919 | Button.VSplitRight(Cut: 16.0f, pLeft: &Button, pRight: &ButtonAuto); |
920 | Button.VSplitRight(Cut: 2.0f, pLeft: &Button, pRight: nullptr); |
921 | static int s_AutoMapperButtonAuto = 0; |
922 | if(m_pEditor->DoButton_Editor(pId: &s_AutoMapperButtonAuto, pText: "A" , Checked: m_AutoAutoMap, pRect: &ButtonAuto, Flags: 0, pToolTip: "Automatically run automap after modifications." )) |
923 | { |
924 | m_AutoAutoMap = !m_AutoAutoMap; |
925 | FlagModified(x: 0, y: 0, w: m_Width, h: m_Height); |
926 | if(!m_TilesHistory.empty()) // Sometimes pressing that button causes the automap to run so we should be able to undo that |
927 | { |
928 | // record undo |
929 | m_pEditor->m_EditorHistory.RecordAction(pAction: std::make_shared<CEditorActionTileChanges>(args&: m_pEditor, args&: m_pEditor->m_SelectedGroup, args&: m_pEditor->m_vSelectedLayers[0], args: "Auto map" , args&: m_TilesHistory)); |
930 | ClearHistory(); |
931 | } |
932 | } |
933 | } |
934 | |
935 | static int s_AutoMapperButton = 0; |
936 | if(m_pEditor->DoButton_Editor(pId: &s_AutoMapperButton, pText: "Automap" , Checked: 0, pRect: &Button, Flags: 0, pToolTip: "Run the automapper" )) |
937 | { |
938 | m_pEditor->m_Map.m_vpImages[m_Image]->m_AutoMapper.Proceed(pLayer: this, ConfigId: m_AutoMapperConfig, Seed: m_Seed); |
939 | // record undo |
940 | m_pEditor->m_EditorHistory.RecordAction(pAction: std::make_shared<CEditorActionTileChanges>(args&: m_pEditor, args&: m_pEditor->m_SelectedGroup, args&: m_pEditor->m_vSelectedLayers[0], args: "Auto map" , args&: m_TilesHistory)); |
941 | ClearHistory(); |
942 | return CUi::POPUP_CLOSE_CURRENT; |
943 | } |
944 | } |
945 | } |
946 | |
947 | int Color = PackColor(Color: m_Color); |
948 | |
949 | CProperty aProps[] = { |
950 | {.m_pName: "Width" , .m_Value: m_Width, .m_Type: PROPTYPE_INT, .m_Min: 1, .m_Max: 100000}, |
951 | {.m_pName: "Height" , .m_Value: m_Height, .m_Type: PROPTYPE_INT, .m_Min: 1, .m_Max: 100000}, |
952 | {.m_pName: "Shift" , .m_Value: 0, .m_Type: PROPTYPE_SHIFT, .m_Min: 0, .m_Max: 0}, |
953 | {.m_pName: "Shift by" , .m_Value: m_pEditor->m_ShiftBy, .m_Type: PROPTYPE_INT, .m_Min: 1, .m_Max: 100000}, |
954 | {.m_pName: "Image" , .m_Value: m_Image, .m_Type: PROPTYPE_IMAGE, .m_Min: 0, .m_Max: 0}, |
955 | {.m_pName: "Color" , .m_Value: Color, .m_Type: PROPTYPE_COLOR, .m_Min: 0, .m_Max: 0}, |
956 | {.m_pName: "Color Env" , .m_Value: m_ColorEnv + 1, .m_Type: PROPTYPE_ENVELOPE, .m_Min: 0, .m_Max: 0}, |
957 | {.m_pName: "Color TO" , .m_Value: m_ColorEnvOffset, .m_Type: PROPTYPE_INT, .m_Min: -1000000, .m_Max: 1000000}, |
958 | {.m_pName: "Auto Rule" , .m_Value: m_AutoMapperConfig, .m_Type: PROPTYPE_AUTOMAPPER, .m_Min: m_Image, .m_Max: 0}, |
959 | {.m_pName: "Seed" , .m_Value: m_Seed, .m_Type: PROPTYPE_INT, .m_Min: 0, .m_Max: 1000000000}, |
960 | {.m_pName: nullptr}, |
961 | }; |
962 | |
963 | if(EntitiesLayer) // remove the image and color properties if this is a game layer |
964 | { |
965 | aProps[(int)ETilesProp::PROP_IMAGE].m_pName = nullptr; |
966 | aProps[(int)ETilesProp::PROP_COLOR].m_pName = nullptr; |
967 | aProps[(int)ETilesProp::PROP_AUTOMAPPER].m_pName = nullptr; |
968 | } |
969 | if(m_Image == -1) |
970 | { |
971 | aProps[(int)ETilesProp::PROP_AUTOMAPPER].m_pName = nullptr; |
972 | aProps[(int)ETilesProp::PROP_SEED].m_pName = nullptr; |
973 | } |
974 | |
975 | static int s_aIds[(int)ETilesProp::NUM_PROPS] = {0}; |
976 | int NewVal = 0; |
977 | auto [State, Prop] = m_pEditor->DoPropertiesWithState<ETilesProp>(pToolbox: pToolBox, pProps: aProps, pIds: s_aIds, pNewVal: &NewVal); |
978 | |
979 | static CLayerTilesPropTracker s_Tracker(m_pEditor); |
980 | s_Tracker.Begin(pObject: this, Prop, State); |
981 | m_pEditor->m_EditorHistory.BeginBulk(); |
982 | |
983 | if(Prop == ETilesProp::PROP_WIDTH && NewVal > 1) |
984 | { |
985 | if(NewVal > 1000 && !m_pEditor->m_LargeLayerWasWarned) |
986 | { |
987 | m_pEditor->m_PopupEventType = CEditor::POPEVENT_LARGELAYER; |
988 | m_pEditor->m_PopupEventActivated = true; |
989 | m_pEditor->m_LargeLayerWasWarned = true; |
990 | } |
991 | Resize(NewW: NewVal, NewH: m_Height); |
992 | } |
993 | else if(Prop == ETilesProp::PROP_HEIGHT && NewVal > 1) |
994 | { |
995 | if(NewVal > 1000 && !m_pEditor->m_LargeLayerWasWarned) |
996 | { |
997 | m_pEditor->m_PopupEventType = CEditor::POPEVENT_LARGELAYER; |
998 | m_pEditor->m_PopupEventActivated = true; |
999 | m_pEditor->m_LargeLayerWasWarned = true; |
1000 | } |
1001 | Resize(NewW: m_Width, NewH: NewVal); |
1002 | } |
1003 | else if(Prop == ETilesProp::PROP_SHIFT) |
1004 | { |
1005 | Shift(Direction: NewVal); |
1006 | } |
1007 | else if(Prop == ETilesProp::PROP_SHIFT_BY) |
1008 | { |
1009 | m_pEditor->m_ShiftBy = NewVal; |
1010 | } |
1011 | else if(Prop == ETilesProp::PROP_IMAGE) |
1012 | { |
1013 | m_Image = NewVal; |
1014 | if(NewVal == -1) |
1015 | { |
1016 | m_Image = -1; |
1017 | } |
1018 | else |
1019 | { |
1020 | m_Image = NewVal % m_pEditor->m_Map.m_vpImages.size(); |
1021 | m_AutoMapperConfig = -1; |
1022 | |
1023 | if(m_pEditor->m_Map.m_vpImages[m_Image]->m_Width % 16 != 0 || m_pEditor->m_Map.m_vpImages[m_Image]->m_Height % 16 != 0) |
1024 | { |
1025 | m_pEditor->m_PopupEventType = CEditor::POPEVENT_IMAGEDIV16; |
1026 | m_pEditor->m_PopupEventActivated = true; |
1027 | m_Image = -1; |
1028 | } |
1029 | } |
1030 | } |
1031 | else if(Prop == ETilesProp::PROP_COLOR) |
1032 | { |
1033 | m_Color.r = (NewVal >> 24) & 0xff; |
1034 | m_Color.g = (NewVal >> 16) & 0xff; |
1035 | m_Color.b = (NewVal >> 8) & 0xff; |
1036 | m_Color.a = NewVal & 0xff; |
1037 | } |
1038 | else if(Prop == ETilesProp::PROP_COLOR_ENV) |
1039 | { |
1040 | int Index = clamp(val: NewVal - 1, lo: -1, hi: (int)m_pEditor->m_Map.m_vpEnvelopes.size() - 1); |
1041 | const int Step = (Index - m_ColorEnv) % 2; |
1042 | if(Step != 0) |
1043 | { |
1044 | for(; Index >= -1 && Index < (int)m_pEditor->m_Map.m_vpEnvelopes.size(); Index += Step) |
1045 | { |
1046 | if(Index == -1 || m_pEditor->m_Map.m_vpEnvelopes[Index]->GetChannels() == 4) |
1047 | { |
1048 | m_ColorEnv = Index; |
1049 | break; |
1050 | } |
1051 | } |
1052 | } |
1053 | } |
1054 | else if(Prop == ETilesProp::PROP_COLOR_ENV_OFFSET) |
1055 | { |
1056 | m_ColorEnvOffset = NewVal; |
1057 | } |
1058 | else if(Prop == ETilesProp::PROP_SEED) |
1059 | { |
1060 | m_Seed = NewVal; |
1061 | } |
1062 | else if(Prop == ETilesProp::PROP_AUTOMAPPER) |
1063 | { |
1064 | if(m_Image >= 0 && m_pEditor->m_Map.m_vpImages[m_Image]->m_AutoMapper.ConfigNamesNum() > 0 && NewVal >= 0) |
1065 | m_AutoMapperConfig = NewVal % m_pEditor->m_Map.m_vpImages[m_Image]->m_AutoMapper.ConfigNamesNum(); |
1066 | else |
1067 | m_AutoMapperConfig = -1; |
1068 | } |
1069 | |
1070 | s_Tracker.End(Prop, State); |
1071 | |
1072 | // Check if modified property could have an effect on automapper |
1073 | if((State == EEditState::END || State == EEditState::ONE_GO) && HasAutomapEffect(Prop)) |
1074 | { |
1075 | FlagModified(x: 0, y: 0, w: m_Width, h: m_Height); |
1076 | |
1077 | // Record undo if automapper was ran |
1078 | if(m_AutoAutoMap && !m_TilesHistory.empty()) |
1079 | { |
1080 | m_pEditor->m_EditorHistory.RecordAction(pAction: std::make_shared<CEditorActionTileChanges>(args&: m_pEditor, args&: m_pEditor->m_SelectedGroup, args&: m_pEditor->m_vSelectedLayers[0], args: "Auto map" , args&: m_TilesHistory)); |
1081 | ClearHistory(); |
1082 | } |
1083 | } |
1084 | |
1085 | // End undo bulk, taking the first action display as the displayed text in the history |
1086 | // This is usually the resulting text of the edit layer tiles prop action |
1087 | // Since we may also squeeze a tile changes action, we want both to appear as one, thus using a bulk |
1088 | m_pEditor->m_EditorHistory.EndBulk(DisplayToUse: 0); |
1089 | |
1090 | return CUi::POPUP_KEEP_OPEN; |
1091 | } |
1092 | |
1093 | CUi::EPopupMenuFunctionResult CLayerTiles::RenderCommonProperties(SCommonPropState &State, CEditor *pEditor, CUIRect *pToolbox, std::vector<std::shared_ptr<CLayerTiles>> &vpLayers, std::vector<int> &vLayerIndices) |
1094 | { |
1095 | if(State.m_Modified) |
1096 | { |
1097 | CUIRect Commit; |
1098 | pToolbox->HSplitBottom(Cut: 20.0f, pTop: pToolbox, pBottom: &Commit); |
1099 | static int s_CommitButton = 0; |
1100 | if(pEditor->DoButton_Editor(pId: &s_CommitButton, pText: "Commit" , Checked: 0, pRect: &Commit, Flags: 0, pToolTip: "Applies the changes" )) |
1101 | { |
1102 | bool HasModifiedSize = (State.m_Modified & SCommonPropState::MODIFIED_SIZE) != 0; |
1103 | bool HasModifiedColor = (State.m_Modified & SCommonPropState::MODIFIED_COLOR) != 0; |
1104 | |
1105 | std::vector<std::shared_ptr<IEditorAction>> vpActions; |
1106 | int j = 0; |
1107 | int GroupIndex = pEditor->m_SelectedGroup; |
1108 | for(auto &pLayer : vpLayers) |
1109 | { |
1110 | int LayerIndex = vLayerIndices[j++]; |
1111 | if(HasModifiedSize) |
1112 | { |
1113 | std::map<int, std::shared_ptr<CLayer>> SavedLayers; |
1114 | SavedLayers[LAYERTYPE_TILES] = pLayer->Duplicate(); |
1115 | if(pLayer->m_Game || pLayer->m_Front || pLayer->m_Switch || pLayer->m_Speedup || pLayer->m_Tune || pLayer->m_Tele) |
1116 | { // Need to save all entities layers when any entity layer |
1117 | if(pEditor->m_Map.m_pFrontLayer && !pLayer->m_Front) |
1118 | SavedLayers[LAYERTYPE_FRONT] = pEditor->m_Map.m_pFrontLayer->Duplicate(); |
1119 | if(pEditor->m_Map.m_pTeleLayer && !pLayer->m_Tele) |
1120 | SavedLayers[LAYERTYPE_TELE] = pEditor->m_Map.m_pTeleLayer->Duplicate(); |
1121 | if(pEditor->m_Map.m_pSwitchLayer && !pLayer->m_Switch) |
1122 | SavedLayers[LAYERTYPE_SWITCH] = pEditor->m_Map.m_pSwitchLayer->Duplicate(); |
1123 | if(pEditor->m_Map.m_pSpeedupLayer && !pLayer->m_Speedup) |
1124 | SavedLayers[LAYERTYPE_SPEEDUP] = pEditor->m_Map.m_pSpeedupLayer->Duplicate(); |
1125 | if(pEditor->m_Map.m_pTuneLayer && !pLayer->m_Tune) |
1126 | SavedLayers[LAYERTYPE_TUNE] = pEditor->m_Map.m_pTuneLayer->Duplicate(); |
1127 | if(!pLayer->m_Game) |
1128 | SavedLayers[LAYERTYPE_GAME] = pEditor->m_Map.m_pGameLayer->Duplicate(); |
1129 | } |
1130 | |
1131 | int PrevW = pLayer->m_Width; |
1132 | int PrevH = pLayer->m_Height; |
1133 | pLayer->Resize(NewW: State.m_Width, NewH: State.m_Height); |
1134 | |
1135 | if(PrevW != State.m_Width) |
1136 | { |
1137 | std::shared_ptr<CEditorActionEditLayerTilesProp> pAction; |
1138 | vpActions.push_back(x: pAction = std::make_shared<CEditorActionEditLayerTilesProp>(args&: pEditor, args&: GroupIndex, args&: LayerIndex, args: ETilesProp::PROP_WIDTH, args&: PrevW, args&: State.m_Width)); |
1139 | pAction->SetSavedLayers(SavedLayers); |
1140 | } |
1141 | |
1142 | if(PrevH != State.m_Height) |
1143 | { |
1144 | std::shared_ptr<CEditorActionEditLayerTilesProp> pAction; |
1145 | vpActions.push_back(x: pAction = std::make_shared<CEditorActionEditLayerTilesProp>(args&: pEditor, args&: GroupIndex, args&: LayerIndex, args: ETilesProp::PROP_HEIGHT, args&: PrevH, args&: State.m_Height)); |
1146 | pAction->SetSavedLayers(SavedLayers); |
1147 | } |
1148 | } |
1149 | |
1150 | if(HasModifiedColor && !pLayer->IsEntitiesLayer()) |
1151 | { |
1152 | int Color = 0; |
1153 | Color |= pLayer->m_Color.r << 24; |
1154 | Color |= pLayer->m_Color.g << 16; |
1155 | Color |= pLayer->m_Color.b << 8; |
1156 | Color |= pLayer->m_Color.a; |
1157 | |
1158 | pLayer->m_Color.r = (State.m_Color >> 24) & 0xff; |
1159 | pLayer->m_Color.g = (State.m_Color >> 16) & 0xff; |
1160 | pLayer->m_Color.b = (State.m_Color >> 8) & 0xff; |
1161 | pLayer->m_Color.a = State.m_Color & 0xff; |
1162 | |
1163 | vpActions.push_back(x: std::make_shared<CEditorActionEditLayerTilesProp>(args&: pEditor, args&: GroupIndex, args&: LayerIndex, args: ETilesProp::PROP_COLOR, args&: Color, args&: State.m_Color)); |
1164 | } |
1165 | |
1166 | pLayer->FlagModified(x: 0, y: 0, w: pLayer->m_Width, h: pLayer->m_Height); |
1167 | } |
1168 | State.m_Modified = 0; |
1169 | |
1170 | char aDisplay[256]; |
1171 | str_format(buffer: aDisplay, buffer_size: sizeof(aDisplay), format: "Edit %d layers common properties: %s" , (int)vpLayers.size(), HasModifiedColor && HasModifiedSize ? "color, size" : (HasModifiedColor ? "color" : "size" )); |
1172 | pEditor->m_EditorHistory.RecordAction(pAction: std::make_shared<CEditorActionBulk>(args&: pEditor, args&: vpActions, args&: aDisplay)); |
1173 | } |
1174 | } |
1175 | else |
1176 | { |
1177 | for(auto &pLayer : vpLayers) |
1178 | { |
1179 | if(pLayer->m_Width > State.m_Width) |
1180 | State.m_Width = pLayer->m_Width; |
1181 | if(pLayer->m_Height > State.m_Height) |
1182 | State.m_Height = pLayer->m_Height; |
1183 | } |
1184 | } |
1185 | |
1186 | { |
1187 | CUIRect Warning; |
1188 | pToolbox->HSplitTop(Cut: 13.0f, pTop: &Warning, pBottom: pToolbox); |
1189 | Warning.HMargin(Cut: 0.5f, pOtherRect: &Warning); |
1190 | |
1191 | pEditor->TextRender()->TextColor(rgb: ColorRGBA(1.0f, 0.0f, 0.0f, 1.0f)); |
1192 | SLabelProperties Props; |
1193 | Props.m_MaxWidth = Warning.w; |
1194 | pEditor->Ui()->DoLabel(pRect: &Warning, pText: "Editing multiple layers" , Size: 9.0f, Align: TEXTALIGN_ML, LabelProps: Props); |
1195 | pEditor->TextRender()->TextColor(rgb: ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f)); |
1196 | pToolbox->HSplitTop(Cut: 2.0f, pTop: nullptr, pBottom: pToolbox); |
1197 | } |
1198 | |
1199 | CProperty aProps[] = { |
1200 | {.m_pName: "Width" , .m_Value: State.m_Width, .m_Type: PROPTYPE_INT, .m_Min: 1, .m_Max: 100000}, |
1201 | {.m_pName: "Height" , .m_Value: State.m_Height, .m_Type: PROPTYPE_INT, .m_Min: 1, .m_Max: 100000}, |
1202 | {.m_pName: "Shift" , .m_Value: 0, .m_Type: PROPTYPE_SHIFT, .m_Min: 0, .m_Max: 0}, |
1203 | {.m_pName: "Shift by" , .m_Value: pEditor->m_ShiftBy, .m_Type: PROPTYPE_INT, .m_Min: 1, .m_Max: 100000}, |
1204 | {.m_pName: "Color" , .m_Value: State.m_Color, .m_Type: PROPTYPE_COLOR, .m_Min: 0, .m_Max: 0}, |
1205 | {.m_pName: nullptr}, |
1206 | }; |
1207 | |
1208 | static int s_aIds[(int)ETilesCommonProp::NUM_PROPS] = {0}; |
1209 | int NewVal = 0; |
1210 | auto [PropState, Prop] = pEditor->DoPropertiesWithState<ETilesCommonProp>(pToolbox, pProps: aProps, pIds: s_aIds, pNewVal: &NewVal); |
1211 | |
1212 | static CLayerTilesCommonPropTracker s_Tracker(pEditor); |
1213 | s_Tracker.m_vpLayers = vpLayers; |
1214 | s_Tracker.m_vLayerIndices = vLayerIndices; |
1215 | |
1216 | s_Tracker.Begin(pObject: nullptr, Prop, State: PropState); |
1217 | |
1218 | if(Prop == ETilesCommonProp::PROP_WIDTH && NewVal > 1) |
1219 | { |
1220 | if(NewVal > 1000 && !pEditor->m_LargeLayerWasWarned) |
1221 | { |
1222 | pEditor->m_PopupEventType = CEditor::POPEVENT_LARGELAYER; |
1223 | pEditor->m_PopupEventActivated = true; |
1224 | pEditor->m_LargeLayerWasWarned = true; |
1225 | } |
1226 | State.m_Width = NewVal; |
1227 | } |
1228 | else if(Prop == ETilesCommonProp::PROP_HEIGHT && NewVal > 1) |
1229 | { |
1230 | if(NewVal > 1000 && !pEditor->m_LargeLayerWasWarned) |
1231 | { |
1232 | pEditor->m_PopupEventType = CEditor::POPEVENT_LARGELAYER; |
1233 | pEditor->m_PopupEventActivated = true; |
1234 | pEditor->m_LargeLayerWasWarned = true; |
1235 | } |
1236 | State.m_Height = NewVal; |
1237 | } |
1238 | else if(Prop == ETilesCommonProp::PROP_SHIFT) |
1239 | { |
1240 | for(auto &pLayer : vpLayers) |
1241 | pLayer->Shift(Direction: NewVal); |
1242 | } |
1243 | else if(Prop == ETilesCommonProp::PROP_SHIFT_BY) |
1244 | { |
1245 | pEditor->m_ShiftBy = NewVal; |
1246 | } |
1247 | else if(Prop == ETilesCommonProp::PROP_COLOR) |
1248 | { |
1249 | State.m_Color = NewVal; |
1250 | } |
1251 | |
1252 | s_Tracker.End(Prop, State: PropState); |
1253 | |
1254 | if(PropState == EEditState::END || PropState == EEditState::ONE_GO) |
1255 | { |
1256 | if(Prop == ETilesCommonProp::PROP_WIDTH || Prop == ETilesCommonProp::PROP_HEIGHT) |
1257 | { |
1258 | State.m_Modified |= SCommonPropState::MODIFIED_SIZE; |
1259 | } |
1260 | else if(Prop == ETilesCommonProp::PROP_COLOR) |
1261 | { |
1262 | State.m_Modified |= SCommonPropState::MODIFIED_COLOR; |
1263 | } |
1264 | } |
1265 | |
1266 | return CUi::POPUP_KEEP_OPEN; |
1267 | } |
1268 | |
1269 | void CLayerTiles::FlagModified(int x, int y, int w, int h) |
1270 | { |
1271 | m_pEditor->m_Map.OnModify(); |
1272 | if(m_Seed != 0 && m_AutoMapperConfig != -1 && m_AutoAutoMap && m_Image >= 0) |
1273 | { |
1274 | m_pEditor->m_Map.m_vpImages[m_Image]->m_AutoMapper.ProceedLocalized(pLayer: this, ConfigId: m_AutoMapperConfig, Seed: m_Seed, X: x, Y: y, Width: w, Height: h); |
1275 | } |
1276 | } |
1277 | |
1278 | void CLayerTiles::ModifyImageIndex(FIndexModifyFunction Func) |
1279 | { |
1280 | Func(&m_Image); |
1281 | } |
1282 | |
1283 | void CLayerTiles::ModifyEnvelopeIndex(FIndexModifyFunction Func) |
1284 | { |
1285 | Func(&m_ColorEnv); |
1286 | } |
1287 | |
1288 | void CLayerTiles::ShowPreventUnusedTilesWarning() |
1289 | { |
1290 | if(!m_pEditor->m_PreventUnusedTilesWasWarned) |
1291 | { |
1292 | m_pEditor->m_PopupEventType = CEditor::POPEVENT_PREVENTUNUSEDTILES; |
1293 | m_pEditor->m_PopupEventActivated = true; |
1294 | m_pEditor->m_PreventUnusedTilesWasWarned = true; |
1295 | } |
1296 | } |
1297 | |
1298 | bool CLayerTiles::HasAutomapEffect(ETilesProp Prop) |
1299 | { |
1300 | switch(Prop) |
1301 | { |
1302 | case ETilesProp::PROP_WIDTH: |
1303 | case ETilesProp::PROP_HEIGHT: |
1304 | case ETilesProp::PROP_SHIFT: |
1305 | case ETilesProp::PROP_IMAGE: |
1306 | case ETilesProp::PROP_AUTOMAPPER: |
1307 | case ETilesProp::PROP_SEED: |
1308 | return true; |
1309 | default: |
1310 | return false; |
1311 | } |
1312 | return false; |
1313 | } |
1314 | |