1#include "editor_actions.h"
2
3#include <base/log.h>
4
5#include <game/editor/editor.h>
6#include <game/editor/mapitems.h>
7#include <game/editor/mapitems/image.h>
8#include <game/editor/mapitems/layer.h>
9#include <game/editor/mapitems/layer_front.h>
10#include <game/editor/mapitems/layer_group.h>
11#include <game/editor/mapitems/layer_quads.h>
12#include <game/editor/mapitems/layer_sounds.h>
13#include <game/editor/mapitems/map.h>
14
15CEditorBrushDrawAction::CEditorBrushDrawAction(CEditorMap *pMap, int Group) :
16 IEditorAction(pMap), m_Group(Group)
17{
18 for(size_t k = 0; k < Map()->m_vpGroups[Group]->m_vpLayers.size(); k++)
19 {
20 auto pLayer = Map()->m_vpGroups[Group]->m_vpLayers[k];
21
22 if(pLayer->m_Type == LAYERTYPE_TILES)
23 {
24 auto pLayerTiles = std::static_pointer_cast<CLayerTiles>(r: pLayer);
25
26 if(pLayer == Map()->m_pTeleLayer)
27 {
28 if(!Map()->m_pTeleLayer->m_History.empty())
29 {
30 m_TeleTileChanges = std::map(Map()->m_pTeleLayer->m_History);
31 Map()->m_pTeleLayer->ClearHistory();
32 }
33 }
34 else if(pLayer == Map()->m_pTuneLayer)
35 {
36 if(!Map()->m_pTuneLayer->m_History.empty())
37 {
38 m_TuneTileChanges = std::map(Map()->m_pTuneLayer->m_History);
39 Map()->m_pTuneLayer->ClearHistory();
40 }
41 }
42 else if(pLayer == Map()->m_pSwitchLayer)
43 {
44 if(!Map()->m_pSwitchLayer->m_History.empty())
45 {
46 m_SwitchTileChanges = std::map(Map()->m_pSwitchLayer->m_History);
47 Map()->m_pSwitchLayer->ClearHistory();
48 }
49 }
50 else if(pLayer == Map()->m_pSpeedupLayer)
51 {
52 if(!Map()->m_pSpeedupLayer->m_History.empty())
53 {
54 m_SpeedupTileChanges = std::map(Map()->m_pSpeedupLayer->m_History);
55 Map()->m_pSpeedupLayer->ClearHistory();
56 }
57 }
58
59 if(!pLayerTiles->m_TilesHistory.empty())
60 {
61 m_vTileChanges.emplace_back(args&: k, args: std::map(pLayerTiles->m_TilesHistory));
62 pLayerTiles->ClearHistory();
63 }
64 }
65 }
66
67 SetInfos();
68 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Brush draw (x%d) on %d layers", m_TotalTilesDrawn, m_TotalLayers);
69}
70
71void CEditorBrushDrawAction::SetInfos()
72{
73 m_TotalTilesDrawn = 0;
74 m_TotalLayers = 0;
75
76 // Process normal tiles
77 for(auto const &Pair : m_vTileChanges)
78 {
79 int Layer = Pair.first;
80 std::shared_ptr<CLayer> pLayer = Map()->m_vpGroups[m_Group]->m_vpLayers[Layer];
81 m_TotalLayers++;
82
83 if(pLayer->m_Type == LAYERTYPE_TILES)
84 {
85 auto Changes = Pair.second;
86 for(auto &Change : Changes)
87 {
88 m_TotalTilesDrawn += Change.second.size();
89 }
90 }
91 }
92
93 // Process speedup tiles
94 for(auto const &SpeedupChange : m_SpeedupTileChanges)
95 {
96 m_TotalTilesDrawn += SpeedupChange.second.size();
97 }
98
99 // Process tele tiles
100 for(auto const &TeleChange : m_TeleTileChanges)
101 {
102 m_TotalTilesDrawn += TeleChange.second.size();
103 }
104
105 // Process switch tiles
106 for(auto const &SwitchChange : m_SwitchTileChanges)
107 {
108 m_TotalTilesDrawn += SwitchChange.second.size();
109 }
110
111 // Process tune tiles
112 for(auto const &TuneChange : m_TuneTileChanges)
113 {
114 m_TotalTilesDrawn += TuneChange.second.size();
115 }
116
117 m_TotalLayers += !m_SpeedupTileChanges.empty();
118 m_TotalLayers += !m_SwitchTileChanges.empty();
119 m_TotalLayers += !m_TeleTileChanges.empty();
120 m_TotalLayers += !m_TuneTileChanges.empty();
121}
122
123bool CEditorBrushDrawAction::IsEmpty()
124{
125 return m_vTileChanges.empty() && m_SpeedupTileChanges.empty() && m_SwitchTileChanges.empty() && m_TeleTileChanges.empty() && m_TuneTileChanges.empty();
126}
127
128void CEditorBrushDrawAction::Undo()
129{
130 Apply(Undo: true);
131}
132
133void CEditorBrushDrawAction::Redo()
134{
135 Apply(Undo: false);
136}
137
138void CEditorBrushDrawAction::Apply(bool Undo)
139{
140 // Process normal tiles
141 for(auto const &Pair : m_vTileChanges)
142 {
143 int Layer = Pair.first;
144 std::shared_ptr<CLayer> pLayer = Map()->m_vpGroups[m_Group]->m_vpLayers[Layer];
145
146 if(pLayer->m_Type == LAYERTYPE_TILES)
147 {
148 std::shared_ptr<CLayerTiles> pLayerTiles = std::static_pointer_cast<CLayerTiles>(r: pLayer);
149 auto Changes = Pair.second;
150 for(auto &Change : Changes)
151 {
152 int y = Change.first;
153 auto Line = Change.second;
154 for(auto &Tile : Line)
155 {
156 int x = Tile.first;
157 STileStateChange State = Tile.second;
158 pLayerTiles->SetTileIgnoreHistory(x, y, Tile: Undo ? State.m_Previous : State.m_Current);
159 }
160 }
161 }
162 }
163
164 // Process speedup tiles
165 for(auto const &SpeedupChange : m_SpeedupTileChanges)
166 {
167 int y = SpeedupChange.first;
168 auto Line = SpeedupChange.second;
169 for(auto &Tile : Line)
170 {
171 int x = Tile.first;
172 int Index = y * Map()->m_pSpeedupLayer->m_Width + x;
173 SSpeedupTileStateChange State = Tile.second;
174 SSpeedupTileStateChange::SData Data = Undo ? State.m_Previous : State.m_Current;
175
176 Map()->m_pSpeedupLayer->m_pSpeedupTile[Index].m_Force = Data.m_Force;
177 Map()->m_pSpeedupLayer->m_pSpeedupTile[Index].m_MaxSpeed = Data.m_MaxSpeed;
178 Map()->m_pSpeedupLayer->m_pSpeedupTile[Index].m_Angle = Data.m_Angle;
179 Map()->m_pSpeedupLayer->m_pSpeedupTile[Index].m_Type = Data.m_Type;
180 Map()->m_pSpeedupLayer->m_pTiles[Index].m_Index = Data.m_Index;
181 }
182 }
183
184 // Process tele tiles
185 for(auto const &TeleChange : m_TeleTileChanges)
186 {
187 int y = TeleChange.first;
188 auto Line = TeleChange.second;
189 for(auto &Tile : Line)
190 {
191 int x = Tile.first;
192 int Index = y * Map()->m_pTeleLayer->m_Width + x;
193 STeleTileStateChange State = Tile.second;
194 STeleTileStateChange::SData Data = Undo ? State.m_Previous : State.m_Current;
195
196 Map()->m_pTeleLayer->m_pTeleTile[Index].m_Number = Data.m_Number;
197 Map()->m_pTeleLayer->m_pTeleTile[Index].m_Type = Data.m_Type;
198 Map()->m_pTeleLayer->m_pTiles[Index].m_Index = Data.m_Index;
199 }
200 }
201
202 // Process switch tiles
203 for(auto const &SwitchChange : m_SwitchTileChanges)
204 {
205 int y = SwitchChange.first;
206 auto Line = SwitchChange.second;
207 for(auto &Tile : Line)
208 {
209 int x = Tile.first;
210 int Index = y * Map()->m_pSwitchLayer->m_Width + x;
211 SSwitchTileStateChange State = Tile.second;
212 SSwitchTileStateChange::SData Data = Undo ? State.m_Previous : State.m_Current;
213
214 Map()->m_pSwitchLayer->m_pSwitchTile[Index].m_Number = Data.m_Number;
215 Map()->m_pSwitchLayer->m_pSwitchTile[Index].m_Type = Data.m_Type;
216 Map()->m_pSwitchLayer->m_pSwitchTile[Index].m_Flags = Data.m_Flags;
217 Map()->m_pSwitchLayer->m_pSwitchTile[Index].m_Delay = Data.m_Delay;
218 Map()->m_pSwitchLayer->m_pTiles[Index].m_Index = Data.m_Index;
219 }
220 }
221
222 // Process tune tiles
223 for(auto const &TuneChange : m_TuneTileChanges)
224 {
225 int y = TuneChange.first;
226 auto Line = TuneChange.second;
227 for(auto &Tile : Line)
228 {
229 int x = Tile.first;
230 int Index = y * Map()->m_pTuneLayer->m_Width + x;
231 STuneTileStateChange State = Tile.second;
232 STuneTileStateChange::SData Data = Undo ? State.m_Previous : State.m_Current;
233
234 Map()->m_pTuneLayer->m_pTuneTile[Index].m_Number = Data.m_Number;
235 Map()->m_pTuneLayer->m_pTuneTile[Index].m_Type = Data.m_Type;
236 Map()->m_pTuneLayer->m_pTiles[Index].m_Index = Data.m_Index;
237 }
238 }
239}
240
241// -------------------------------------------
242
243CEditorActionQuadPlace::CEditorActionQuadPlace(CEditorMap *pMap, int GroupIndex, int LayerIndex, std::vector<CQuad> &vBrush) :
244 CEditorActionLayerBase(pMap, GroupIndex, LayerIndex), m_vBrush(vBrush)
245{
246 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Quad place (x%d)", (int)m_vBrush.size());
247}
248
249void CEditorActionQuadPlace::Undo()
250{
251 std::shared_ptr<CLayerQuads> pLayerQuads = std::static_pointer_cast<CLayerQuads>(r: m_pLayer);
252 for(size_t k = 0; k < m_vBrush.size(); k++)
253 pLayerQuads->m_vQuads.pop_back();
254
255 Map()->OnModify();
256}
257void CEditorActionQuadPlace::Redo()
258{
259 std::shared_ptr<CLayerQuads> pLayerQuads = std::static_pointer_cast<CLayerQuads>(r: m_pLayer);
260 for(auto &Brush : m_vBrush)
261 pLayerQuads->m_vQuads.push_back(x: Brush);
262
263 Map()->OnModify();
264}
265
266CEditorActionSoundPlace::CEditorActionSoundPlace(CEditorMap *pMap, int GroupIndex, int LayerIndex, std::vector<CSoundSource> &vBrush) :
267 CEditorActionLayerBase(pMap, GroupIndex, LayerIndex), m_vBrush(vBrush)
268{
269 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Sound place (x%d)", (int)m_vBrush.size());
270}
271
272void CEditorActionSoundPlace::Undo()
273{
274 std::shared_ptr<CLayerSounds> pLayerSounds = std::static_pointer_cast<CLayerSounds>(r: m_pLayer);
275 for(size_t k = 0; k < m_vBrush.size(); k++)
276 pLayerSounds->m_vSources.pop_back();
277
278 Map()->OnModify();
279}
280
281void CEditorActionSoundPlace::Redo()
282{
283 std::shared_ptr<CLayerSounds> pLayerSounds = std::static_pointer_cast<CLayerSounds>(r: m_pLayer);
284 for(auto &Brush : m_vBrush)
285 pLayerSounds->m_vSources.push_back(x: Brush);
286
287 Map()->OnModify();
288}
289
290// ---------------------------------------------------------------------------------------
291
292CEditorActionDeleteQuad::CEditorActionDeleteQuad(CEditorMap *pMap, int GroupIndex, int LayerIndex) :
293 CEditorActionLayerBase(pMap, GroupIndex, LayerIndex)
294{
295 std::shared_ptr<CLayerQuads> pLayerQuads = std::static_pointer_cast<CLayerQuads>(r: m_pLayer);
296 m_vQuadsIndices = Map()->m_vSelectedQuads;
297
298 // make sure the indices are descending
299 std::sort(first: m_vQuadsIndices.begin(), last: m_vQuadsIndices.end(), comp: std::greater<>());
300
301 dbg_assert(m_vQuadsIndices[0] < (int)pLayerQuads->m_vQuads.size(), "Tried to delete quad with Id %d, while the layer only contains %d quads", m_vQuadsIndices[0], (int)pLayerQuads->m_vQuads.size());
302 dbg_assert(m_vQuadsIndices.back() >= 0, "Tried to delete quad with negative Id %d", m_vQuadsIndices.back());
303
304 m_vDeletedQuads.reserve(n: Map()->m_vSelectedQuads.size());
305 for(int QuadId : m_vQuadsIndices)
306 {
307 m_vDeletedQuads.emplace_back(args&: pLayerQuads->m_vQuads[QuadId]);
308 }
309
310 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Delete quad (x%d)", (int)m_vDeletedQuads.size());
311}
312
313void CEditorActionDeleteQuad::Undo()
314{
315 std::shared_ptr<CLayerQuads> pLayerQuads = std::static_pointer_cast<CLayerQuads>(r: m_pLayer);
316
317 // Quad indices are in descending order, so we add them back in ascending order
318 for(int IndexId = (int)m_vQuadsIndices.size() - 1; IndexId >= 0; --IndexId)
319 {
320 pLayerQuads->m_vQuads.insert(position: pLayerQuads->m_vQuads.begin() + m_vQuadsIndices[IndexId], x: m_vDeletedQuads[IndexId]);
321 }
322 Map()->m_vSelectedQuads = m_vQuadsIndices;
323
324 Map()->OnModify();
325}
326
327void CEditorActionDeleteQuad::Redo()
328{
329 std::shared_ptr<CLayerQuads> pLayerQuads = std::static_pointer_cast<CLayerQuads>(r: m_pLayer);
330
331 // Quad indices are in descending order
332 for(const int &QuadId : m_vQuadsIndices)
333 {
334 pLayerQuads->m_vQuads.erase(position: pLayerQuads->m_vQuads.begin() + QuadId);
335 }
336 Map()->m_vSelectedQuads.clear();
337
338 Map()->OnModify();
339}
340
341// ---------------------------------------------------------------------------------------
342
343CEditorActionEditQuadPoint::CEditorActionEditQuadPoint(CEditorMap *pMap, int GroupIndex, int LayerIndex, int QuadIndex, std::vector<CPoint> const &vPreviousPoints, std::vector<CPoint> const &vCurrentPoints) :
344 CEditorActionLayerBase(pMap, GroupIndex, LayerIndex), m_QuadIndex(QuadIndex), m_vPreviousPoints(vPreviousPoints), m_vCurrentPoints(vCurrentPoints)
345{
346 str_copy(dst&: m_aDisplayText, src: "Edit quad points");
347}
348
349void CEditorActionEditQuadPoint::Undo()
350{
351 Apply(vValue: m_vPreviousPoints);
352}
353
354void CEditorActionEditQuadPoint::Redo()
355{
356 Apply(vValue: m_vCurrentPoints);
357}
358
359void CEditorActionEditQuadPoint::Apply(const std::vector<CPoint> &vValue)
360{
361 std::shared_ptr<CLayerQuads> pLayerQuads = std::static_pointer_cast<CLayerQuads>(r: m_pLayer);
362 CQuad &Quad = pLayerQuads->m_vQuads[m_QuadIndex];
363 dbg_assert(std::size(Quad.m_aPoints) == vValue.size(), "Expected %d values, got %d", (int)std::size(Quad.m_aPoints), (int)vValue.size());
364 std::copy_n(first: vValue.begin(), n: std::size(Quad.m_aPoints), result: Quad.m_aPoints);
365}
366
367CEditorActionEditQuadColor::CEditorActionEditQuadColor(CEditorMap *pMap, int GroupIndex, int LayerIndex, int QuadIndex, std::vector<CColor> const &vPreviousColors, std::vector<CColor> const &vCurrentColors) :
368 CEditorActionLayerBase(pMap, GroupIndex, LayerIndex), m_QuadIndex(QuadIndex), m_vPreviousColors(vPreviousColors), m_vCurrentColors(vCurrentColors)
369{
370 str_copy(dst&: m_aDisplayText, src: "Edit quad point colors");
371}
372
373void CEditorActionEditQuadColor::Undo()
374{
375 Apply(vValue&: m_vPreviousColors);
376}
377
378void CEditorActionEditQuadColor::Redo()
379{
380 Apply(vValue&: m_vCurrentColors);
381}
382
383void CEditorActionEditQuadColor::Apply(std::vector<CColor> &vValue)
384{
385 std::shared_ptr<CLayerQuads> pLayerQuads = std::static_pointer_cast<CLayerQuads>(r: m_pLayer);
386 CQuad &Quad = pLayerQuads->m_vQuads[m_QuadIndex];
387 dbg_assert(std::size(Quad.m_aColors) == vValue.size(), "Expected %d values, got %d", (int)std::size(Quad.m_aColors), (int)vValue.size());
388 std::copy_n(first: vValue.begin(), n: std::size(Quad.m_aColors), result: Quad.m_aColors);
389}
390
391CEditorActionEditQuadProp::CEditorActionEditQuadProp(CEditorMap *pMap, int GroupIndex, int LayerIndex, int QuadIndex, EQuadProp Prop, int Previous, int Current) :
392 CEditorActionLayerBase(pMap, GroupIndex, LayerIndex), m_QuadIndex(QuadIndex), m_Prop(Prop), m_Previous(Previous), m_Current(Current)
393{
394 static const char *s_apNames[] = {
395 "order",
396 "pos X",
397 "pos Y",
398 "pos env",
399 "pos env offset",
400 nullptr, // color
401 "color env",
402 "color env offset"};
403 static_assert(std::size(s_apNames) == (size_t)EQuadProp::NUM_PROPS);
404 dbg_assert(Prop != EQuadProp::COLOR, "Color prop implemented by CEditorActionEditQuadPointProp");
405 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Edit quad %s property in layer %d of group %d", s_apNames[(int)m_Prop], m_LayerIndex, m_GroupIndex);
406}
407
408void CEditorActionEditQuadProp::Undo()
409{
410 Apply(Value: m_Previous);
411}
412
413void CEditorActionEditQuadProp::Redo()
414{
415 Apply(Value: m_Current);
416}
417
418void CEditorActionEditQuadProp::Apply(int Value)
419{
420 std::shared_ptr<CLayerQuads> pLayerQuads = std::static_pointer_cast<CLayerQuads>(r: m_pLayer);
421 CQuad &Quad = pLayerQuads->m_vQuads[m_QuadIndex];
422 if(m_Prop == EQuadProp::POS_ENV)
423 Quad.m_PosEnv = Value;
424 else if(m_Prop == EQuadProp::POS_ENV_OFFSET)
425 Quad.m_PosEnvOffset = Value;
426 else if(m_Prop == EQuadProp::COLOR_ENV)
427 Quad.m_ColorEnv = Value;
428 else if(m_Prop == EQuadProp::COLOR_ENV_OFFSET)
429 Quad.m_ColorEnvOffset = Value;
430}
431
432CEditorActionEditQuadPointProp::CEditorActionEditQuadPointProp(CEditorMap *pMap, int GroupIndex, int LayerIndex, int QuadIndex, int PointIndex, EQuadPointProp Prop, int Previous, int Current) :
433 CEditorActionLayerBase(pMap, GroupIndex, LayerIndex), m_QuadIndex(QuadIndex), m_PointIndex(PointIndex), m_Prop(Prop), m_Previous(Previous), m_Current(Current)
434{
435 static const char *s_apNames[] = {
436 "pos X",
437 "pos Y",
438 "color",
439 "tex U",
440 "tex V"};
441 static_assert(std::size(s_apNames) == (size_t)EQuadPointProp::NUM_PROPS);
442 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Edit quad point %s property in layer %d of group %d", s_apNames[(int)m_Prop], m_LayerIndex, m_GroupIndex);
443}
444
445void CEditorActionEditQuadPointProp::Undo()
446{
447 Apply(Value: m_Previous);
448}
449
450void CEditorActionEditQuadPointProp::Redo()
451{
452 Apply(Value: m_Current);
453}
454
455void CEditorActionEditQuadPointProp::Apply(int Value)
456{
457 std::shared_ptr<CLayerQuads> pLayerQuads = std::static_pointer_cast<CLayerQuads>(r: m_pLayer);
458 CQuad &Quad = pLayerQuads->m_vQuads[m_QuadIndex];
459
460 if(m_Prop == EQuadPointProp::COLOR)
461 {
462 const ColorRGBA ColorPick = ColorRGBA::UnpackAlphaLast<ColorRGBA>(Color: Value);
463
464 Quad.m_aColors[m_PointIndex].r = ColorPick.r * 255.0f;
465 Quad.m_aColors[m_PointIndex].g = ColorPick.g * 255.0f;
466 Quad.m_aColors[m_PointIndex].b = ColorPick.b * 255.0f;
467 Quad.m_aColors[m_PointIndex].a = ColorPick.a * 255.0f;
468
469 Editor()->m_ColorPickerPopupContext.m_RgbaColor = ColorPick;
470 Editor()->m_ColorPickerPopupContext.m_HslaColor = color_cast<ColorHSLA>(rgb: ColorPick);
471 Editor()->m_ColorPickerPopupContext.m_HsvaColor = color_cast<ColorHSVA>(hsl: Editor()->m_ColorPickerPopupContext.m_HslaColor);
472 }
473 else if(m_Prop == EQuadPointProp::TEX_U)
474 {
475 Quad.m_aTexcoords[m_PointIndex].x = Value;
476 }
477 else if(m_Prop == EQuadPointProp::TEX_V)
478 {
479 Quad.m_aTexcoords[m_PointIndex].y = Value;
480 }
481}
482
483// ---------------------------------------------------------------------------------------
484
485CEditorActionBulk::CEditorActionBulk(CEditorMap *pMap, const std::vector<std::shared_ptr<IEditorAction>> &vpActions, const char *pDisplay, bool Reverse) :
486 IEditorAction(pMap), m_vpActions(vpActions), m_Reverse(Reverse)
487{
488 // Assuming we only use bulk for actions of same type, if no display was provided
489 if(!pDisplay)
490 {
491 const char *pBaseDisplay = m_vpActions[0]->DisplayText();
492 if(m_vpActions.size() == 1)
493 str_copy(dst&: m_aDisplayText, src: pBaseDisplay);
494 else
495 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "%s (x%d)", pBaseDisplay, (int)m_vpActions.size());
496 }
497 else
498 {
499 str_copy(dst&: m_aDisplayText, src: pDisplay);
500 }
501}
502
503void CEditorActionBulk::Undo()
504{
505 if(m_Reverse)
506 {
507 // reverse_view is not supported in gcc 10
508 for(auto pIt = m_vpActions.rbegin(); pIt != m_vpActions.rend(); pIt++) // NOLINT: modernize-loop-convert
509 {
510 auto &pAction = *pIt;
511 pAction->Undo();
512 }
513 }
514 else
515 {
516 for(auto &pAction : m_vpActions)
517 {
518 pAction->Undo();
519 }
520 }
521}
522
523void CEditorActionBulk::Redo()
524{
525 for(auto &pAction : m_vpActions)
526 {
527 pAction->Redo();
528 }
529}
530
531// ---------
532
533CEditorActionTileChanges::CEditorActionTileChanges(CEditorMap *pMap, int GroupIndex, int LayerIndex, const char *pAction, const EditorTileStateChangeHistory<STileStateChange> &Changes) :
534 CEditorActionLayerBase(pMap, GroupIndex, LayerIndex), m_Changes(Changes)
535{
536 ComputeInfos();
537 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "%s (x%d)", pAction, m_TotalChanges);
538}
539
540void CEditorActionTileChanges::Undo()
541{
542 Apply(Undo: true);
543}
544
545void CEditorActionTileChanges::Redo()
546{
547 Apply(Undo: false);
548}
549
550void CEditorActionTileChanges::Apply(bool Undo)
551{
552 std::shared_ptr<CLayerTiles> pLayerTiles = std::static_pointer_cast<CLayerTiles>(r: m_pLayer);
553 for(auto &Change : m_Changes)
554 {
555 int y = Change.first;
556 auto Line = Change.second;
557 for(auto &Tile : Line)
558 {
559 int x = Tile.first;
560 STileStateChange State = Tile.second;
561 pLayerTiles->SetTileIgnoreHistory(x, y, Tile: Undo ? State.m_Previous : State.m_Current);
562 }
563 }
564
565 Map()->OnModify();
566}
567
568void CEditorActionTileChanges::ComputeInfos()
569{
570 m_TotalChanges = 0;
571 for(auto &Line : m_Changes)
572 m_TotalChanges += Line.second.size();
573}
574
575// ---------
576
577CEditorActionLayerBase::CEditorActionLayerBase(CEditorMap *pMap, int GroupIndex, int LayerIndex) :
578 IEditorAction(pMap), m_GroupIndex(GroupIndex), m_LayerIndex(LayerIndex)
579{
580 m_pLayer = Map()->m_vpGroups[GroupIndex]->m_vpLayers[LayerIndex];
581}
582
583// ----------
584
585CEditorActionAddLayer::CEditorActionAddLayer(CEditorMap *pMap, int GroupIndex, int LayerIndex, bool Duplicate) :
586 CEditorActionLayerBase(pMap, GroupIndex, LayerIndex), m_Duplicate(Duplicate)
587{
588 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "%s %s layer in group %d", m_Duplicate ? "Duplicate" : "New", m_pLayer->TypeName(), m_GroupIndex);
589}
590
591void CEditorActionAddLayer::Undo()
592{
593 // Undo: remove layer from vector but keep it in case we want to add it back
594 auto &vLayers = Map()->m_vpGroups[m_GroupIndex]->m_vpLayers;
595
596 if(m_pLayer->m_Type == LAYERTYPE_TILES)
597 {
598 std::shared_ptr<CLayerTiles> pLayerTiles = std::static_pointer_cast<CLayerTiles>(r: m_pLayer);
599 if(pLayerTiles->m_HasFront)
600 Map()->m_pFrontLayer = nullptr;
601 else if(pLayerTiles->m_HasTele)
602 Map()->m_pTeleLayer = nullptr;
603 else if(pLayerTiles->m_HasSpeedup)
604 Map()->m_pSpeedupLayer = nullptr;
605 else if(pLayerTiles->m_HasSwitch)
606 Map()->m_pSwitchLayer = nullptr;
607 else if(pLayerTiles->m_HasTune)
608 Map()->m_pTuneLayer = nullptr;
609 }
610
611 vLayers.erase(position: vLayers.begin() + m_LayerIndex);
612
613 Map()->m_vpGroups[m_GroupIndex]->m_Collapse = false;
614 if(m_LayerIndex >= (int)vLayers.size())
615 Map()->SelectLayer(LayerIndex: vLayers.size() - 1, GroupIndex: m_GroupIndex);
616
617 Map()->OnModify();
618}
619
620void CEditorActionAddLayer::Redo()
621{
622 // Redo: add back the removed layer contained in this class
623 auto &vLayers = Map()->m_vpGroups[m_GroupIndex]->m_vpLayers;
624
625 if(m_pLayer->m_Type == LAYERTYPE_TILES)
626 {
627 std::shared_ptr<CLayerTiles> pLayerTiles = std::static_pointer_cast<CLayerTiles>(r: m_pLayer);
628 if(pLayerTiles->m_HasFront)
629 Map()->m_pFrontLayer = std::static_pointer_cast<CLayerFront>(r: m_pLayer);
630 else if(pLayerTiles->m_HasTele)
631 Map()->m_pTeleLayer = std::static_pointer_cast<CLayerTele>(r: m_pLayer);
632 else if(pLayerTiles->m_HasSpeedup)
633 Map()->m_pSpeedupLayer = std::static_pointer_cast<CLayerSpeedup>(r: m_pLayer);
634 else if(pLayerTiles->m_HasSwitch)
635 Map()->m_pSwitchLayer = std::static_pointer_cast<CLayerSwitch>(r: m_pLayer);
636 else if(pLayerTiles->m_HasTune)
637 Map()->m_pTuneLayer = std::static_pointer_cast<CLayerTune>(r: m_pLayer);
638 }
639
640 vLayers.insert(position: vLayers.begin() + m_LayerIndex, x: m_pLayer);
641
642 Map()->m_vpGroups[m_GroupIndex]->m_Collapse = false;
643 Map()->SelectLayer(LayerIndex: m_LayerIndex, GroupIndex: m_GroupIndex);
644 Map()->OnModify();
645}
646
647CEditorActionDeleteLayer::CEditorActionDeleteLayer(CEditorMap *pMap, int GroupIndex, int LayerIndex) :
648 CEditorActionLayerBase(pMap, GroupIndex, LayerIndex)
649{
650 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Delete %s layer of group %d", m_pLayer->TypeName(), m_GroupIndex);
651}
652
653void CEditorActionDeleteLayer::Redo()
654{
655 // Redo: remove layer from vector but keep it in case we want to add it back
656 auto &vLayers = Map()->m_vpGroups[m_GroupIndex]->m_vpLayers;
657
658 if(m_pLayer->m_Type == LAYERTYPE_TILES)
659 {
660 std::shared_ptr<CLayerTiles> pLayerTiles = std::static_pointer_cast<CLayerTiles>(r: m_pLayer);
661 if(pLayerTiles->m_HasFront)
662 Map()->m_pFrontLayer = nullptr;
663 else if(pLayerTiles->m_HasTele)
664 Map()->m_pTeleLayer = nullptr;
665 else if(pLayerTiles->m_HasSpeedup)
666 Map()->m_pSpeedupLayer = nullptr;
667 else if(pLayerTiles->m_HasSwitch)
668 Map()->m_pSwitchLayer = nullptr;
669 else if(pLayerTiles->m_HasTune)
670 Map()->m_pTuneLayer = nullptr;
671 }
672
673 Map()->m_vpGroups[m_GroupIndex]->DeleteLayer(Index: m_LayerIndex);
674
675 Map()->m_vpGroups[m_GroupIndex]->m_Collapse = false;
676 if(m_LayerIndex >= (int)vLayers.size())
677 Map()->SelectLayer(LayerIndex: vLayers.size() - 1, GroupIndex: m_GroupIndex);
678
679 Map()->OnModify();
680}
681
682void CEditorActionDeleteLayer::Undo()
683{
684 // Undo: add back the removed layer contained in this class
685 auto &vLayers = Map()->m_vpGroups[m_GroupIndex]->m_vpLayers;
686
687 if(m_pLayer->m_Type == LAYERTYPE_TILES)
688 {
689 std::shared_ptr<CLayerTiles> pLayerTiles = std::static_pointer_cast<CLayerTiles>(r: m_pLayer);
690 if(pLayerTiles->m_HasFront)
691 Map()->m_pFrontLayer = std::static_pointer_cast<CLayerFront>(r: m_pLayer);
692 else if(pLayerTiles->m_HasTele)
693 Map()->m_pTeleLayer = std::static_pointer_cast<CLayerTele>(r: m_pLayer);
694 else if(pLayerTiles->m_HasSpeedup)
695 Map()->m_pSpeedupLayer = std::static_pointer_cast<CLayerSpeedup>(r: m_pLayer);
696 else if(pLayerTiles->m_HasSwitch)
697 Map()->m_pSwitchLayer = std::static_pointer_cast<CLayerSwitch>(r: m_pLayer);
698 else if(pLayerTiles->m_HasTune)
699 Map()->m_pTuneLayer = std::static_pointer_cast<CLayerTune>(r: m_pLayer);
700 }
701
702 vLayers.insert(position: vLayers.begin() + m_LayerIndex, x: m_pLayer);
703
704 Map()->m_vpGroups[m_GroupIndex]->m_Collapse = false;
705 Map()->SelectLayer(LayerIndex: m_LayerIndex, GroupIndex: m_GroupIndex);
706 Map()->OnModify();
707}
708
709CEditorActionGroup::CEditorActionGroup(CEditorMap *pMap, int GroupIndex, bool Delete) :
710 IEditorAction(pMap), m_GroupIndex(GroupIndex), m_Delete(Delete)
711{
712 m_pGroup = Map()->m_vpGroups[GroupIndex];
713 if(m_Delete)
714 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Delete group %d", m_GroupIndex);
715 else
716 str_copy(dst&: m_aDisplayText, src: "New group");
717}
718
719void CEditorActionGroup::Undo()
720{
721 if(m_Delete)
722 {
723 // Undo: add back the group
724 Map()->m_vpGroups.insert(position: Map()->m_vpGroups.begin() + m_GroupIndex, x: m_pGroup);
725 Map()->m_SelectedGroup = m_GroupIndex;
726 Map()->OnModify();
727 }
728 else
729 {
730 // Undo: delete the group
731 Map()->DeleteGroup(Index: m_GroupIndex);
732 Map()->m_SelectedGroup = std::max(a: 0, b: m_GroupIndex - 1);
733 }
734
735 Map()->OnModify();
736}
737
738void CEditorActionGroup::Redo()
739{
740 if(!m_Delete)
741 {
742 // Redo: add back the group
743 Map()->m_vpGroups.insert(position: Map()->m_vpGroups.begin() + m_GroupIndex, x: m_pGroup);
744 Map()->m_SelectedGroup = m_GroupIndex;
745 }
746 else
747 {
748 // Redo: delete the group
749 Map()->DeleteGroup(Index: m_GroupIndex);
750 Map()->m_SelectedGroup = std::max(a: 0, b: m_GroupIndex - 1);
751 }
752
753 Map()->OnModify();
754}
755
756CEditorActionEditGroupProp::CEditorActionEditGroupProp(CEditorMap *pMap, int GroupIndex, EGroupProp Prop, int Previous, int Current) :
757 IEditorAction(pMap), m_GroupIndex(GroupIndex), m_Prop(Prop), m_Previous(Previous), m_Current(Current)
758{
759 static const char *s_apNames[] = {
760 "order",
761 "pos X",
762 "pos Y",
763 "para X",
764 "para Y",
765 "use clipping",
766 "clip X",
767 "clip Y",
768 "clip W",
769 "clip H"};
770 static_assert(std::size(s_apNames) == (size_t)EGroupProp::NUM_PROPS);
771
772 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Edit group %d %s property", m_GroupIndex, s_apNames[(int)Prop]);
773}
774
775void CEditorActionEditGroupProp::Undo()
776{
777 if(m_Prop == EGroupProp::ORDER)
778 {
779 Map()->m_SelectedGroup = Map()->MoveGroup(IndexFrom: m_Current, IndexTo: m_Previous);
780 }
781 else
782 Apply(Value: m_Previous);
783}
784
785void CEditorActionEditGroupProp::Redo()
786{
787 if(m_Prop == EGroupProp::ORDER)
788 {
789 Map()->m_SelectedGroup = Map()->MoveGroup(IndexFrom: m_Previous, IndexTo: m_Current);
790 }
791 else
792 Apply(Value: m_Current);
793}
794
795void CEditorActionEditGroupProp::Apply(int Value)
796{
797 auto pGroup = Map()->m_vpGroups[m_GroupIndex];
798
799 if(m_Prop == EGroupProp::POS_X)
800 pGroup->m_OffsetX = Value;
801 if(m_Prop == EGroupProp::POS_Y)
802 pGroup->m_OffsetY = Value;
803 if(m_Prop == EGroupProp::PARA_X)
804 pGroup->m_ParallaxX = Value;
805 if(m_Prop == EGroupProp::PARA_Y)
806 pGroup->m_ParallaxY = Value;
807 if(m_Prop == EGroupProp::USE_CLIPPING)
808 pGroup->m_UseClipping = Value;
809 if(m_Prop == EGroupProp::CLIP_X)
810 pGroup->m_ClipX = Value;
811 if(m_Prop == EGroupProp::CLIP_Y)
812 pGroup->m_ClipY = Value;
813 if(m_Prop == EGroupProp::CLIP_W)
814 pGroup->m_ClipW = Value;
815 if(m_Prop == EGroupProp::CLIP_H)
816 pGroup->m_ClipH = Value;
817
818 Map()->OnModify();
819}
820
821template<typename E>
822CEditorActionEditLayerPropBase<E>::CEditorActionEditLayerPropBase(CEditorMap *pMap, int GroupIndex, int LayerIndex, E Prop, int Previous, int Current) :
823 CEditorActionLayerBase(pMap, GroupIndex, LayerIndex), m_Prop(Prop), m_Previous(Previous), m_Current(Current)
824{
825}
826
827CEditorActionEditLayerProp::CEditorActionEditLayerProp(CEditorMap *pMap, int GroupIndex, int LayerIndex, ELayerProp Prop, int Previous, int Current) :
828 CEditorActionEditLayerPropBase(pMap, GroupIndex, LayerIndex, Prop, Previous, Current)
829{
830 static const char *s_apNames[] = {
831 "group",
832 "order",
833 "HQ"};
834 static_assert(std::size(s_apNames) == (size_t)ELayerProp::NUM_PROPS);
835
836 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Edit layer %d in group %d %s property", m_LayerIndex, m_GroupIndex, s_apNames[(int)m_Prop]);
837}
838
839void CEditorActionEditLayerProp::Undo()
840{
841 auto pCurrentGroup = Map()->m_vpGroups[m_GroupIndex];
842
843 if(m_Prop == ELayerProp::ORDER)
844 {
845 Map()->SelectLayer(LayerIndex: pCurrentGroup->MoveLayer(IndexFrom: m_Current, IndexTo: m_Previous));
846 }
847 else
848 Apply(Value: m_Previous);
849}
850
851void CEditorActionEditLayerProp::Redo()
852{
853 auto pCurrentGroup = Map()->m_vpGroups[m_GroupIndex];
854
855 if(m_Prop == ELayerProp::ORDER)
856 {
857 Map()->SelectLayer(LayerIndex: pCurrentGroup->MoveLayer(IndexFrom: m_Previous, IndexTo: m_Current));
858 }
859 else
860 Apply(Value: m_Current);
861}
862
863void CEditorActionEditLayerProp::Apply(int Value)
864{
865 if(m_Prop == ELayerProp::GROUP)
866 {
867 auto pCurrentGroup = Map()->m_vpGroups[Value == m_Previous ? m_Current : m_Previous];
868 auto pPreviousGroup = Map()->m_vpGroups[Value];
869 pCurrentGroup->m_vpLayers.erase(position: pCurrentGroup->m_vpLayers.begin() + pCurrentGroup->m_vpLayers.size() - 1);
870 if(Value == m_Previous)
871 pPreviousGroup->m_vpLayers.insert(position: pPreviousGroup->m_vpLayers.begin() + m_LayerIndex, x: m_pLayer);
872 else
873 pPreviousGroup->m_vpLayers.push_back(x: m_pLayer);
874 Map()->m_SelectedGroup = Value;
875 Map()->SelectLayer(LayerIndex: m_LayerIndex);
876 }
877 else if(m_Prop == ELayerProp::HQ)
878 {
879 m_pLayer->m_Flags = Value;
880 }
881
882 Map()->OnModify();
883}
884
885CEditorActionEditLayerTilesProp::CEditorActionEditLayerTilesProp(CEditorMap *pMap, int GroupIndex, int LayerIndex, ETilesProp Prop, int Previous, int Current) :
886 CEditorActionEditLayerPropBase(pMap, GroupIndex, LayerIndex, Prop, Previous, Current)
887{
888 static const char *s_apNames[] = {
889 "width",
890 "height",
891 "shift",
892 "shift by",
893 "image",
894 "color",
895 "color env",
896 "color env offset",
897 "automapper",
898 "automapper reference",
899 "live gametiles",
900 "seed"};
901 static_assert(std::size(s_apNames) == (size_t)ETilesProp::NUM_PROPS);
902
903 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Edit tiles layer %d in group %d %s property", m_LayerIndex, m_GroupIndex, s_apNames[(int)Prop]);
904}
905
906void CEditorActionEditLayerTilesProp::SetSavedLayers(const std::map<int, std::shared_ptr<CLayer>> &SavedLayers)
907{
908 m_SavedLayers = std::map(SavedLayers);
909}
910
911void CEditorActionEditLayerTilesProp::Undo()
912{
913 std::shared_ptr<CLayerTiles> pLayerTiles = std::static_pointer_cast<CLayerTiles>(r: m_pLayer);
914 std::shared_ptr<CLayerTiles> pSavedLayerTiles = nullptr;
915
916 if(m_Prop == ETilesProp::WIDTH || m_Prop == ETilesProp::HEIGHT)
917 {
918 if(m_Prop == ETilesProp::HEIGHT)
919 pLayerTiles->Resize(NewW: pLayerTiles->m_Width, NewH: m_Previous);
920 else if(m_Prop == ETilesProp::WIDTH)
921 pLayerTiles->Resize(NewW: m_Previous, NewH: pLayerTiles->m_Height);
922
923 RestoreLayer(Layer: LAYERTYPE_TILES, pLayerTiles);
924 if(pLayerTiles->m_HasGame || pLayerTiles->m_HasFront || pLayerTiles->m_HasSwitch || pLayerTiles->m_HasSpeedup || pLayerTiles->m_HasTune)
925 {
926 if(Map()->m_pFrontLayer && !pLayerTiles->m_HasFront)
927 RestoreLayer(Layer: LAYERTYPE_FRONT, pLayerTiles: Map()->m_pFrontLayer);
928 if(Map()->m_pTeleLayer && !pLayerTiles->m_HasTele)
929 RestoreLayer(Layer: LAYERTYPE_TELE, pLayerTiles: Map()->m_pTeleLayer);
930 if(Map()->m_pSwitchLayer && !pLayerTiles->m_HasSwitch)
931 RestoreLayer(Layer: LAYERTYPE_SWITCH, pLayerTiles: Map()->m_pSwitchLayer);
932 if(Map()->m_pSpeedupLayer && !pLayerTiles->m_HasSpeedup)
933 RestoreLayer(Layer: LAYERTYPE_SPEEDUP, pLayerTiles: Map()->m_pSpeedupLayer);
934 if(Map()->m_pTuneLayer && !pLayerTiles->m_HasTune)
935 RestoreLayer(Layer: LAYERTYPE_TUNE, pLayerTiles: Map()->m_pTuneLayer);
936 if(!pLayerTiles->m_HasGame)
937 RestoreLayer(Layer: LAYERTYPE_GAME, pLayerTiles: Map()->m_pGameLayer);
938 }
939 }
940 else if(m_Prop == ETilesProp::SHIFT)
941 {
942 RestoreLayer(Layer: LAYERTYPE_TILES, pLayerTiles);
943 }
944 else if(m_Prop == ETilesProp::SHIFT_BY)
945 {
946 Map()->m_ShiftBy = m_Previous;
947 }
948 else if(m_Prop == ETilesProp::IMAGE)
949 {
950 if(m_Previous == -1 || Map()->m_vpImages.empty())
951 {
952 pLayerTiles->m_Image = -1;
953 }
954 else
955 {
956 pLayerTiles->m_Image = m_Previous % Map()->m_vpImages.size();
957 pLayerTiles->m_AutomapperConfig = -1;
958 }
959 }
960 else if(m_Prop == ETilesProp::COLOR)
961 {
962 const ColorRGBA ColorPick = ColorRGBA::UnpackAlphaLast<ColorRGBA>(Color: m_Previous);
963
964 pLayerTiles->m_Color.r = ColorPick.r * 255.0f;
965 pLayerTiles->m_Color.g = ColorPick.g * 255.0f;
966 pLayerTiles->m_Color.b = ColorPick.b * 255.0f;
967 pLayerTiles->m_Color.a = ColorPick.a * 255.0f;
968
969 Editor()->m_ColorPickerPopupContext.m_RgbaColor = ColorPick;
970 Editor()->m_ColorPickerPopupContext.m_HslaColor = color_cast<ColorHSLA>(rgb: ColorPick);
971 Editor()->m_ColorPickerPopupContext.m_HsvaColor = color_cast<ColorHSVA>(hsl: Editor()->m_ColorPickerPopupContext.m_HslaColor);
972 }
973 else if(m_Prop == ETilesProp::COLOR_ENV)
974 {
975 pLayerTiles->m_ColorEnv = m_Previous;
976 }
977 else if(m_Prop == ETilesProp::COLOR_ENV_OFFSET)
978 {
979 pLayerTiles->m_ColorEnvOffset = m_Previous;
980 }
981 else if(m_Prop == ETilesProp::AUTOMAPPER)
982 {
983 pLayerTiles->m_AutomapperConfig = m_Previous;
984 }
985 else if(m_Prop == ETilesProp::LIVE_GAMETILES)
986 {
987 pLayerTiles->m_LiveGameTiles = m_Previous;
988 }
989 else if(m_Prop == ETilesProp::SEED)
990 {
991 pLayerTiles->m_Seed = m_Previous;
992 }
993
994 Map()->OnModify();
995}
996
997void CEditorActionEditLayerTilesProp::Redo()
998{
999 std::shared_ptr<CLayerTiles> pLayerTiles = std::static_pointer_cast<CLayerTiles>(r: m_pLayer);
1000
1001 if(m_Prop == ETilesProp::WIDTH || m_Prop == ETilesProp::HEIGHT)
1002 {
1003 if(m_Prop == ETilesProp::HEIGHT)
1004 pLayerTiles->Resize(NewW: pLayerTiles->m_Width, NewH: m_Current);
1005 else if(m_Prop == ETilesProp::WIDTH)
1006 pLayerTiles->Resize(NewW: m_Current, NewH: pLayerTiles->m_Height);
1007
1008 if(pLayerTiles->m_HasGame || pLayerTiles->m_HasFront || pLayerTiles->m_HasSwitch || pLayerTiles->m_HasSpeedup || pLayerTiles->m_HasTune)
1009 {
1010 if(Map()->m_pFrontLayer && !pLayerTiles->m_HasFront)
1011 Map()->m_pFrontLayer->Resize(NewW: pLayerTiles->m_Width, NewH: pLayerTiles->m_Height);
1012 if(Map()->m_pTeleLayer && !pLayerTiles->m_HasTele)
1013 Map()->m_pTeleLayer->Resize(NewW: pLayerTiles->m_Width, NewH: pLayerTiles->m_Height);
1014 if(Map()->m_pSwitchLayer && !pLayerTiles->m_HasSwitch)
1015 Map()->m_pSwitchLayer->Resize(NewW: pLayerTiles->m_Width, NewH: pLayerTiles->m_Height);
1016 if(Map()->m_pSpeedupLayer && !pLayerTiles->m_HasSpeedup)
1017 Map()->m_pSpeedupLayer->Resize(NewW: pLayerTiles->m_Width, NewH: pLayerTiles->m_Height);
1018 if(Map()->m_pTuneLayer && !pLayerTiles->m_HasTune)
1019 Map()->m_pTuneLayer->Resize(NewW: pLayerTiles->m_Width, NewH: pLayerTiles->m_Height);
1020 if(!pLayerTiles->m_HasGame)
1021 Map()->m_pGameLayer->Resize(NewW: pLayerTiles->m_Width, NewH: pLayerTiles->m_Height);
1022 }
1023 }
1024 else if(m_Prop == ETilesProp::SHIFT)
1025 {
1026 pLayerTiles->Shift(Direction: (EShiftDirection)m_Current);
1027 }
1028 else if(m_Prop == ETilesProp::SHIFT_BY)
1029 {
1030 Map()->m_ShiftBy = m_Current;
1031 }
1032 else if(m_Prop == ETilesProp::IMAGE)
1033 {
1034 if(m_Current == -1 || Map()->m_vpImages.empty())
1035 {
1036 pLayerTiles->m_Image = -1;
1037 }
1038 else
1039 {
1040 pLayerTiles->m_Image = m_Current % Map()->m_vpImages.size();
1041 pLayerTiles->m_AutomapperConfig = -1;
1042 }
1043 }
1044 else if(m_Prop == ETilesProp::COLOR)
1045 {
1046 const ColorRGBA ColorPick = ColorRGBA::UnpackAlphaLast<ColorRGBA>(Color: m_Current);
1047
1048 pLayerTiles->m_Color.r = ColorPick.r * 255.0f;
1049 pLayerTiles->m_Color.g = ColorPick.g * 255.0f;
1050 pLayerTiles->m_Color.b = ColorPick.b * 255.0f;
1051 pLayerTiles->m_Color.a = ColorPick.a * 255.0f;
1052
1053 Editor()->m_ColorPickerPopupContext.m_RgbaColor = ColorPick;
1054 Editor()->m_ColorPickerPopupContext.m_HslaColor = color_cast<ColorHSLA>(rgb: ColorPick);
1055 Editor()->m_ColorPickerPopupContext.m_HsvaColor = color_cast<ColorHSVA>(hsl: Editor()->m_ColorPickerPopupContext.m_HslaColor);
1056 }
1057 else if(m_Prop == ETilesProp::COLOR_ENV)
1058 {
1059 pLayerTiles->m_ColorEnv = m_Current;
1060 }
1061 else if(m_Prop == ETilesProp::COLOR_ENV_OFFSET)
1062 {
1063 pLayerTiles->m_ColorEnvOffset = m_Current;
1064 }
1065 else if(m_Prop == ETilesProp::AUTOMAPPER)
1066 {
1067 pLayerTiles->m_AutomapperConfig = m_Current;
1068 }
1069 else if(m_Prop == ETilesProp::LIVE_GAMETILES)
1070 {
1071 pLayerTiles->m_LiveGameTiles = m_Current;
1072 }
1073 else if(m_Prop == ETilesProp::SEED)
1074 {
1075 pLayerTiles->m_Seed = m_Current;
1076 }
1077
1078 Map()->OnModify();
1079}
1080
1081void CEditorActionEditLayerTilesProp::RestoreLayer(int Layer, const std::shared_ptr<CLayerTiles> &pLayerTiles)
1082{
1083 if(m_SavedLayers[Layer] != nullptr)
1084 {
1085 std::shared_ptr<CLayerTiles> pSavedLayerTiles = std::static_pointer_cast<CLayerTiles>(r: m_SavedLayers[Layer]);
1086 mem_copy(dest: pLayerTiles->m_pTiles, source: pSavedLayerTiles->m_pTiles, size: (size_t)pLayerTiles->m_Width * pLayerTiles->m_Height * sizeof(CTile));
1087
1088 if(pLayerTiles->m_HasTele)
1089 {
1090 std::shared_ptr<CLayerTele> pLayerTele = std::static_pointer_cast<CLayerTele>(r: pLayerTiles);
1091 std::shared_ptr<CLayerTele> pSavedLayerTele = std::static_pointer_cast<CLayerTele>(r: pSavedLayerTiles);
1092 mem_copy(dest: pLayerTele->m_pTeleTile, source: pSavedLayerTele->m_pTeleTile, size: (size_t)pLayerTiles->m_Width * pLayerTiles->m_Height * sizeof(CTeleTile));
1093 }
1094 else if(pLayerTiles->m_HasSpeedup)
1095 {
1096 std::shared_ptr<CLayerSpeedup> pLayerSpeedup = std::static_pointer_cast<CLayerSpeedup>(r: pLayerTiles);
1097 std::shared_ptr<CLayerSpeedup> pSavedLayerSpeedup = std::static_pointer_cast<CLayerSpeedup>(r: pSavedLayerTiles);
1098 mem_copy(dest: pLayerSpeedup->m_pSpeedupTile, source: pSavedLayerSpeedup->m_pSpeedupTile, size: (size_t)pLayerTiles->m_Width * pLayerTiles->m_Height * sizeof(CSpeedupTile));
1099 }
1100 else if(pLayerTiles->m_HasSwitch)
1101 {
1102 std::shared_ptr<CLayerSwitch> pLayerSwitch = std::static_pointer_cast<CLayerSwitch>(r: pLayerTiles);
1103 std::shared_ptr<CLayerSwitch> pSavedLayerSwitch = std::static_pointer_cast<CLayerSwitch>(r: pSavedLayerTiles);
1104 mem_copy(dest: pLayerSwitch->m_pSwitchTile, source: pSavedLayerSwitch->m_pSwitchTile, size: (size_t)pLayerTiles->m_Width * pLayerTiles->m_Height * sizeof(CSwitchTile));
1105 }
1106 else if(pLayerTiles->m_HasTune)
1107 {
1108 std::shared_ptr<CLayerTune> pLayerTune = std::static_pointer_cast<CLayerTune>(r: pLayerTiles);
1109 std::shared_ptr<CLayerTune> pSavedLayerTune = std::static_pointer_cast<CLayerTune>(r: pSavedLayerTiles);
1110 mem_copy(dest: pLayerTune->m_pTuneTile, source: pSavedLayerTune->m_pTuneTile, size: (size_t)pLayerTiles->m_Width * pLayerTiles->m_Height * sizeof(CTuneTile));
1111 }
1112 }
1113}
1114
1115CEditorActionEditLayerQuadsProp::CEditorActionEditLayerQuadsProp(CEditorMap *pMap, int GroupIndex, int LayerIndex, ELayerQuadsProp Prop, int Previous, int Current) :
1116 CEditorActionEditLayerPropBase(pMap, GroupIndex, LayerIndex, Prop, Previous, Current)
1117{
1118 static const char *s_apNames[] = {
1119 "image"};
1120 static_assert(std::size(s_apNames) == (size_t)ELayerQuadsProp::NUM_PROPS);
1121 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Edit quads layer %d in group %d %s property", m_LayerIndex, m_GroupIndex, s_apNames[(int)m_Prop]);
1122}
1123
1124void CEditorActionEditLayerQuadsProp::Undo()
1125{
1126 Apply(Value: m_Previous);
1127}
1128
1129void CEditorActionEditLayerQuadsProp::Redo()
1130{
1131 Apply(Value: m_Current);
1132}
1133
1134void CEditorActionEditLayerQuadsProp::Apply(int Value)
1135{
1136 std::shared_ptr<CLayerQuads> pLayerQuads = std::static_pointer_cast<CLayerQuads>(r: m_pLayer);
1137 if(m_Prop == ELayerQuadsProp::IMAGE)
1138 {
1139 if(Value >= 0 && !Map()->m_vpImages.empty())
1140 pLayerQuads->m_Image = Value % Map()->m_vpImages.size();
1141 else
1142 pLayerQuads->m_Image = -1;
1143 }
1144
1145 Map()->OnModify();
1146}
1147
1148// --------------------------------------------------------------
1149
1150CEditorActionEditLayersGroupAndOrder::CEditorActionEditLayersGroupAndOrder(CEditorMap *pMap, int GroupIndex, const std::vector<int> &LayerIndices, int NewGroupIndex, const std::vector<int> &NewLayerIndices) :
1151 IEditorAction(pMap), m_GroupIndex(GroupIndex), m_LayerIndices(LayerIndices), m_NewGroupIndex(NewGroupIndex), m_NewLayerIndices(NewLayerIndices)
1152{
1153 std::sort(first: m_LayerIndices.begin(), last: m_LayerIndices.end());
1154 std::sort(first: m_NewLayerIndices.begin(), last: m_NewLayerIndices.end());
1155
1156 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Edit layers group and order (x%d)", (int)m_LayerIndices.size());
1157}
1158
1159void CEditorActionEditLayersGroupAndOrder::Undo()
1160{
1161 // Undo : restore group and order
1162 auto &pCurrentGroup = Map()->m_vpGroups[m_NewGroupIndex];
1163 auto &pPreviousGroup = Map()->m_vpGroups[m_GroupIndex];
1164 std::vector<std::shared_ptr<CLayer>> vpLayers;
1165 vpLayers.reserve(n: m_NewLayerIndices.size());
1166 for(auto &LayerIndex : m_NewLayerIndices)
1167 vpLayers.push_back(x: pCurrentGroup->m_vpLayers[LayerIndex]);
1168
1169 int k = 0;
1170 for(auto &pLayer : vpLayers)
1171 {
1172 pCurrentGroup->m_vpLayers.erase(position: std::find(first: pCurrentGroup->m_vpLayers.begin(), last: pCurrentGroup->m_vpLayers.end(), val: pLayer));
1173 pPreviousGroup->m_vpLayers.insert(position: pPreviousGroup->m_vpLayers.begin() + m_LayerIndices[k++], x: pLayer);
1174 }
1175
1176 Map()->m_vSelectedLayers = m_LayerIndices;
1177 Map()->m_SelectedGroup = m_GroupIndex;
1178}
1179
1180void CEditorActionEditLayersGroupAndOrder::Redo()
1181{
1182 // Redo : move layers
1183 auto &pCurrentGroup = Map()->m_vpGroups[m_GroupIndex];
1184 auto &pPreviousGroup = Map()->m_vpGroups[m_NewGroupIndex];
1185 std::vector<std::shared_ptr<CLayer>> vpLayers;
1186 vpLayers.reserve(n: m_LayerIndices.size());
1187 for(auto &LayerIndex : m_LayerIndices)
1188 vpLayers.push_back(x: pCurrentGroup->m_vpLayers[LayerIndex]);
1189
1190 int k = 0;
1191 for(auto &pLayer : vpLayers)
1192 {
1193 pCurrentGroup->m_vpLayers.erase(position: std::find(first: pCurrentGroup->m_vpLayers.begin(), last: pCurrentGroup->m_vpLayers.end(), val: pLayer));
1194 pPreviousGroup->m_vpLayers.insert(position: pPreviousGroup->m_vpLayers.begin() + m_NewLayerIndices[k++], x: pLayer);
1195 }
1196
1197 Map()->m_vSelectedLayers = m_NewLayerIndices;
1198 Map()->m_SelectedGroup = m_NewGroupIndex;
1199}
1200
1201// -----------------------------------
1202
1203CEditorActionAppendMap::CEditorActionAppendMap(CEditorMap *pMap, const char *pMapName, const SPrevInfo &PrevInfo, std::vector<int> &vImageIndexMap) :
1204 IEditorAction(pMap), m_PrevInfo(PrevInfo), m_vImageIndexMap(vImageIndexMap)
1205{
1206 str_copy(dst&: m_aMapName, src: pMapName);
1207 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Append %s", m_aMapName);
1208}
1209
1210void CEditorActionAppendMap::Undo()
1211{
1212 // Undo append:
1213 // - delete added groups
1214 // - delete added envelopes
1215 // - delete added images
1216 // - delete added sounds
1217
1218 // Delete added groups
1219 while((int)Map()->m_vpGroups.size() != m_PrevInfo.m_Groups)
1220 {
1221 Map()->m_vpGroups.pop_back();
1222 }
1223
1224 // Delete added envelopes
1225 while((int)Map()->m_vpEnvelopes.size() != m_PrevInfo.m_Envelopes)
1226 {
1227 Map()->m_vpEnvelopes.pop_back();
1228 }
1229
1230 // Delete added sounds
1231 while((int)Map()->m_vpSounds.size() != m_PrevInfo.m_Sounds)
1232 {
1233 Map()->m_vpSounds.pop_back();
1234 }
1235
1236 // Delete added images
1237 // Images are sorted when appending, so we need to revert sorting before deleting the images
1238 if(!m_vImageIndexMap.empty())
1239 {
1240 std::vector<int> vReverseIndexMap;
1241 vReverseIndexMap.resize(sz: m_vImageIndexMap.size());
1242
1243 for(int k = 0; k < (int)m_vImageIndexMap.size(); k++)
1244 vReverseIndexMap[m_vImageIndexMap[k]] = k;
1245
1246 std::vector<std::shared_ptr<CEditorImage>> vpRevertedImages;
1247 vpRevertedImages.resize(sz: Map()->m_vpImages.size());
1248
1249 for(int k = 0; k < (int)vReverseIndexMap.size(); k++)
1250 {
1251 vpRevertedImages[vReverseIndexMap[k]] = Map()->m_vpImages[k];
1252 }
1253 Map()->m_vpImages = vpRevertedImages;
1254
1255 Map()->ModifyImageIndex(IndexModifyFunction: [vReverseIndexMap](int *pIndex) {
1256 if(*pIndex >= 0)
1257 {
1258 *pIndex = vReverseIndexMap[*pIndex];
1259 }
1260 });
1261 }
1262
1263 while((int)Map()->m_vpImages.size() != m_PrevInfo.m_Images)
1264 {
1265 Map()->m_vpImages.pop_back();
1266 }
1267
1268 Map()->OnModify();
1269}
1270
1271void CEditorActionAppendMap::Redo()
1272{
1273 const auto &&ErrorHandler = [this](const char *pErrorMessage) {
1274 Editor()->ShowFileDialogError(pFormat: "%s", pErrorMessage);
1275 log_error("editor/append", "%s", pErrorMessage);
1276 };
1277 // Redo is just re-appending the same map
1278 Map()->Append(pFilename: m_aMapName, StorageType: IStorage::TYPE_ALL, IgnoreHistory: true, ErrorHandler);
1279}
1280
1281// ---------------------------
1282
1283CEditorActionTileArt::CEditorActionTileArt(CEditorMap *pMap, int PreviousImageCount, const char *pFilename, std::vector<int> &vImageIndexMap) :
1284 IEditorAction(pMap), m_PreviousImageCount(PreviousImageCount), m_vImageIndexMap(vImageIndexMap)
1285{
1286 str_copy(dst&: m_aFilename, src: pFilename);
1287 str_copy(dst&: m_aDisplayText, src: "Add tile art");
1288}
1289
1290void CEditorActionTileArt::Undo()
1291{
1292 // Delete added group
1293 Map()->m_vpGroups.pop_back();
1294
1295 // Delete added images
1296 // Images are sorted when appending, so we need to revert sorting before deleting the images
1297 if(!m_vImageIndexMap.empty())
1298 {
1299 std::vector<int> vReverseIndexMap;
1300 vReverseIndexMap.resize(sz: m_vImageIndexMap.size());
1301
1302 for(int k = 0; k < (int)m_vImageIndexMap.size(); k++)
1303 vReverseIndexMap[m_vImageIndexMap[k]] = k;
1304
1305 std::vector<std::shared_ptr<CEditorImage>> vpRevertedImages;
1306 vpRevertedImages.resize(sz: Map()->m_vpImages.size());
1307
1308 for(int k = 0; k < (int)vReverseIndexMap.size(); k++)
1309 {
1310 vpRevertedImages[vReverseIndexMap[k]] = Map()->m_vpImages[k];
1311 }
1312 Map()->m_vpImages = vpRevertedImages;
1313
1314 Map()->ModifyImageIndex(IndexModifyFunction: [vReverseIndexMap](int *pIndex) {
1315 if(*pIndex >= 0)
1316 {
1317 *pIndex = vReverseIndexMap[*pIndex];
1318 }
1319 });
1320 }
1321
1322 while((int)Map()->m_vpImages.size() != m_PreviousImageCount)
1323 {
1324 Map()->m_vpImages.pop_back();
1325 }
1326}
1327
1328void CEditorActionTileArt::Redo()
1329{
1330 CImageInfo Image;
1331 if(!Graphics()->LoadPng(Image, pFilename: m_aFilename, StorageType: IStorage::TYPE_ALL))
1332 {
1333 Editor()->ShowFileDialogError(pFormat: "Failed to load image from file '%s'.", m_aFilename);
1334 return;
1335 }
1336 Map()->AddTileArt(Image: std::move(Image), pFilename: m_aFilename, IgnoreHistory: true);
1337}
1338
1339// ---------------------------
1340
1341CEditorActionQuadArt::CEditorActionQuadArt(CEditorMap *pMap, const std::shared_ptr<CLayerGroup> &pGroup) :
1342 IEditorAction(pMap), m_pGroup(pGroup)
1343{
1344 str_copy(dst&: m_aDisplayText, src: "Add quad art");
1345}
1346
1347void CEditorActionQuadArt::Undo()
1348{
1349 // Delete added group (keep pointer for redo)
1350 auto &vGroups = Map()->m_vpGroups;
1351 auto It = std::find(first: vGroups.begin(), last: vGroups.end(), val: m_pGroup);
1352 if(It != vGroups.end())
1353 vGroups.erase(position: It);
1354}
1355
1356void CEditorActionQuadArt::Redo()
1357{
1358 auto &vGroups = Map()->m_vpGroups;
1359 if(std::find(first: vGroups.begin(), last: vGroups.end(), val: m_pGroup) == vGroups.end())
1360 vGroups.push_back(x: m_pGroup);
1361}
1362
1363// ---------------------------------
1364
1365CEditorCommandAction::CEditorCommandAction(CEditorMap *pMap, EType Type, int *pSelectedCommandIndex, int CommandIndex, const char *pPreviousCommand, const char *pCurrentCommand) :
1366 IEditorAction(pMap), m_Type(Type), m_pSelectedCommandIndex(pSelectedCommandIndex), m_CommandIndex(CommandIndex)
1367{
1368 if(pPreviousCommand != nullptr)
1369 m_PreviousCommand = std::string(pPreviousCommand);
1370 if(pCurrentCommand != nullptr)
1371 m_CurrentCommand = std::string(pCurrentCommand);
1372
1373 switch(m_Type)
1374 {
1375 case EType::ADD:
1376 str_copy(dst&: m_aDisplayText, src: "Add command");
1377 break;
1378 case EType::EDIT:
1379 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Edit command %d", m_CommandIndex);
1380 break;
1381 case EType::DELETE:
1382 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Delete command %d", m_CommandIndex);
1383 break;
1384 case EType::MOVE_UP:
1385 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Move command %d up", m_CommandIndex);
1386 break;
1387 case EType::MOVE_DOWN:
1388 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Move command %d down", m_CommandIndex);
1389 break;
1390 default:
1391 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Edit command %d", m_CommandIndex);
1392 break;
1393 }
1394}
1395
1396void CEditorCommandAction::Undo()
1397{
1398 switch(m_Type)
1399 {
1400 case EType::DELETE:
1401 {
1402 Map()->m_vSettings.insert(position: Map()->m_vSettings.begin() + m_CommandIndex, x: m_PreviousCommand.c_str());
1403 *m_pSelectedCommandIndex = m_CommandIndex;
1404 break;
1405 }
1406 case EType::ADD:
1407 {
1408 Map()->m_vSettings.erase(position: Map()->m_vSettings.begin() + m_CommandIndex);
1409 *m_pSelectedCommandIndex = Map()->m_vSettings.size() - 1;
1410 break;
1411 }
1412 case EType::EDIT:
1413 {
1414 str_copy(dst&: Map()->m_vSettings[m_CommandIndex].m_aCommand, src: m_PreviousCommand.c_str());
1415 *m_pSelectedCommandIndex = m_CommandIndex;
1416 break;
1417 }
1418 case EType::MOVE_DOWN:
1419 {
1420 std::swap(a&: Map()->m_vSettings[m_CommandIndex], b&: Map()->m_vSettings[m_CommandIndex + 1]);
1421 *m_pSelectedCommandIndex = m_CommandIndex;
1422 break;
1423 }
1424 case EType::MOVE_UP:
1425 {
1426 std::swap(a&: Map()->m_vSettings[m_CommandIndex], b&: Map()->m_vSettings[m_CommandIndex - 1]);
1427 *m_pSelectedCommandIndex = m_CommandIndex;
1428 break;
1429 }
1430 }
1431}
1432
1433void CEditorCommandAction::Redo()
1434{
1435 switch(m_Type)
1436 {
1437 case EType::DELETE:
1438 {
1439 Map()->m_vSettings.erase(position: Map()->m_vSettings.begin() + m_CommandIndex);
1440 *m_pSelectedCommandIndex = Map()->m_vSettings.size() - 1;
1441 break;
1442 }
1443 case EType::ADD:
1444 {
1445 Map()->m_vSettings.insert(position: Map()->m_vSettings.begin() + m_CommandIndex, x: m_PreviousCommand.c_str());
1446 *m_pSelectedCommandIndex = m_CommandIndex;
1447 break;
1448 }
1449 case EType::EDIT:
1450 {
1451 str_copy(dst&: Map()->m_vSettings[m_CommandIndex].m_aCommand, src: m_CurrentCommand.c_str());
1452 *m_pSelectedCommandIndex = m_CommandIndex;
1453 break;
1454 }
1455 case EType::MOVE_DOWN:
1456 {
1457 std::swap(a&: Map()->m_vSettings[m_CommandIndex], b&: Map()->m_vSettings[m_CommandIndex + 1]);
1458 *m_pSelectedCommandIndex = m_CommandIndex;
1459 break;
1460 }
1461 case EType::MOVE_UP:
1462 {
1463 std::swap(a&: Map()->m_vSettings[m_CommandIndex], b&: Map()->m_vSettings[m_CommandIndex - 1]);
1464 *m_pSelectedCommandIndex = m_CommandIndex;
1465 break;
1466 }
1467 }
1468}
1469
1470// ------------------------------------------------
1471
1472CEditorActionEnvelopeAdd::CEditorActionEnvelopeAdd(CEditorMap *pMap, CEnvelope::EType EnvelopeType) :
1473 IEditorAction(pMap),
1474 m_EnvelopeType(EnvelopeType)
1475{
1476 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Add new %s envelope", EnvelopeType == CEnvelope::EType::COLOR ? "color" : (EnvelopeType == CEnvelope::EType::POSITION ? "position" : "sound"));
1477 m_PreviousSelectedEnvelope = Map()->m_SelectedEnvelope;
1478}
1479
1480void CEditorActionEnvelopeAdd::Undo()
1481{
1482 // Undo is removing the envelope, which was added at the back of the list
1483 Map()->m_vpEnvelopes.pop_back();
1484 Map()->OnModify();
1485 Map()->m_SelectedEnvelope = m_PreviousSelectedEnvelope;
1486}
1487
1488void CEditorActionEnvelopeAdd::Redo()
1489{
1490 // Redo is adding a new envelope at the back of the list
1491 Map()->NewEnvelope(Type: m_EnvelopeType);
1492 Map()->m_SelectedEnvelope = Map()->m_vpEnvelopes.size() - 1;
1493}
1494
1495CEditorActionEnvelopeDelete::CEditorActionEnvelopeDelete(CEditorMap *pMap, int EnvelopeIndex, std::vector<std::shared_ptr<IEditorEnvelopeReference>> &vpObjectReferences, std::shared_ptr<CEnvelope> &pEnvelope) :
1496 IEditorAction(pMap), m_EnvelopeIndex(EnvelopeIndex), m_pEnv(pEnvelope), m_vpObjectReferences(vpObjectReferences)
1497{
1498 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Delete envelope %d", m_EnvelopeIndex);
1499}
1500
1501void CEditorActionEnvelopeDelete::Undo()
1502{
1503 // Undo is adding back the envelope
1504 Map()->InsertEnvelope(Index: m_EnvelopeIndex, pEnvelope&: m_pEnv);
1505 Map()->UpdateEnvelopeReferences(Index: m_EnvelopeIndex, pEnvelope&: m_pEnv, vpEditorObjectReferences&: m_vpObjectReferences);
1506}
1507
1508void CEditorActionEnvelopeDelete::Redo()
1509{
1510 // Redo is erasing the same envelope index
1511 Map()->DeleteEnvelope(Index: m_EnvelopeIndex);
1512}
1513
1514CEditorActionEnvelopeEdit::CEditorActionEnvelopeEdit(CEditorMap *pMap, int EnvelopeIndex, EEditType EditType, int Previous, int Current) :
1515 IEditorAction(pMap), m_EnvelopeIndex(EnvelopeIndex), m_EditType(EditType), m_Previous(Previous), m_Current(Current)
1516{
1517 static const char *s_apNames[] = {
1518 "sync",
1519 "order"};
1520 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Edit envelope %d %s", m_EnvelopeIndex, s_apNames[(int)m_EditType]);
1521}
1522
1523void CEditorActionEnvelopeEdit::Undo()
1524{
1525 switch(m_EditType)
1526 {
1527 case EEditType::ORDER:
1528 {
1529 Map()->MoveEnvelope(IndexFrom: m_Current, IndexTo: m_Previous);
1530 break;
1531 }
1532 case EEditType::SYNC:
1533 {
1534 Map()->m_vpEnvelopes[m_EnvelopeIndex]->m_Synchronized = m_Previous;
1535 break;
1536 }
1537 }
1538 Map()->OnModify();
1539 Map()->m_SelectedEnvelope = m_EnvelopeIndex;
1540}
1541
1542void CEditorActionEnvelopeEdit::Redo()
1543{
1544 switch(m_EditType)
1545 {
1546 case EEditType::ORDER:
1547 {
1548 Map()->MoveEnvelope(IndexFrom: m_Previous, IndexTo: m_Current);
1549 break;
1550 }
1551 case EEditType::SYNC:
1552 {
1553 Map()->m_vpEnvelopes[m_EnvelopeIndex]->m_Synchronized = m_Current;
1554 break;
1555 }
1556 }
1557 Map()->OnModify();
1558 Map()->m_SelectedEnvelope = m_EnvelopeIndex;
1559}
1560
1561CEditorActionEnvelopeEditPointTime::CEditorActionEnvelopeEditPointTime(CEditorMap *pMap, int EnvelopeIndex, int PointIndex, CFixedTime Previous, CFixedTime Current) :
1562 IEditorAction(pMap), m_EnvelopeIndex(EnvelopeIndex), m_PointIndex(PointIndex), m_Previous(Previous), m_Current(Current)
1563{
1564 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Edit time of point %d of env %d", m_PointIndex, m_EnvelopeIndex);
1565}
1566
1567void CEditorActionEnvelopeEditPointTime::Undo()
1568{
1569 Apply(Value: m_Previous);
1570}
1571
1572void CEditorActionEnvelopeEditPointTime::Redo()
1573{
1574 Apply(Value: m_Current);
1575}
1576
1577void CEditorActionEnvelopeEditPointTime::Apply(CFixedTime Value)
1578{
1579 Map()->m_vpEnvelopes[m_EnvelopeIndex]->m_vPoints[m_PointIndex].m_Time = Value;
1580 Map()->OnModify();
1581}
1582
1583CEditorActionEnvelopeEditPoint::CEditorActionEnvelopeEditPoint(CEditorMap *pMap, int EnvelopeIndex, int PointIndex, int Channel, EEditType EditType, int Previous, int Current) :
1584 IEditorAction(pMap), m_EnvelopeIndex(EnvelopeIndex), m_PointIndex(PointIndex), m_Channel(Channel), m_EditType(EditType), m_Previous(Previous), m_Current(Current)
1585{
1586 static const char *s_apNames[] = {
1587 "value",
1588 "curve type"};
1589 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Edit %s of point %d (channel %d) of env %d", s_apNames[(int)m_EditType], m_PointIndex, m_Channel, m_EnvelopeIndex);
1590}
1591
1592void CEditorActionEnvelopeEditPoint::Undo()
1593{
1594 Apply(Value: m_Previous);
1595}
1596
1597void CEditorActionEnvelopeEditPoint::Redo()
1598{
1599 Apply(Value: m_Current);
1600}
1601
1602void CEditorActionEnvelopeEditPoint::Apply(int Value)
1603{
1604 auto pEnvelope = Map()->m_vpEnvelopes[m_EnvelopeIndex];
1605
1606 if(m_EditType == EEditType::VALUE)
1607 {
1608 pEnvelope->m_vPoints[m_PointIndex].m_aValues[m_Channel] = Value;
1609
1610 if(pEnvelope->GetChannels() == 4)
1611 {
1612 Editor()->m_ColorPickerPopupContext.m_RgbaColor = pEnvelope->m_vPoints[m_PointIndex].ColorValue();
1613 Editor()->m_ColorPickerPopupContext.m_HslaColor = color_cast<ColorHSLA>(rgb: Editor()->m_ColorPickerPopupContext.m_RgbaColor);
1614 Editor()->m_ColorPickerPopupContext.m_HsvaColor = color_cast<ColorHSVA>(hsl: Editor()->m_ColorPickerPopupContext.m_HslaColor);
1615 }
1616 }
1617 else if(m_EditType == EEditType::CURVE_TYPE)
1618 {
1619 pEnvelope->m_vPoints[m_PointIndex].m_Curvetype = Value;
1620 }
1621
1622 Map()->OnModify();
1623}
1624
1625// ----
1626
1627CEditorActionEditEnvelopePointValue::CEditorActionEditEnvelopePointValue(CEditorMap *pMap, int EnvelopeIndex, int PointIndex, int Channel, EType Type, CFixedTime OldTime, int OldValue, CFixedTime NewTime, int NewValue) :
1628 IEditorAction(pMap), m_EnvelopeIndex(EnvelopeIndex), m_PointIndex(PointIndex), m_Channel(Channel), m_Type(Type), m_OldTime(OldTime), m_OldValue(OldValue), m_NewTime(NewTime), m_NewValue(NewValue)
1629{
1630 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Edit point %d%s value (envelope %d, channel %d)", PointIndex, m_Type == EType::TANGENT_IN ? "tangent in" : (m_Type == EType::TANGENT_OUT ? "tangent out" : ""), m_EnvelopeIndex, m_Channel);
1631}
1632
1633void CEditorActionEditEnvelopePointValue::Undo()
1634{
1635 Apply(Undo: true);
1636}
1637
1638void CEditorActionEditEnvelopePointValue::Redo()
1639{
1640 Apply(Undo: false);
1641}
1642
1643void CEditorActionEditEnvelopePointValue::Apply(bool Undo)
1644{
1645 float CurrentValue = fx2f(v: Undo ? m_OldValue : m_NewValue);
1646 CFixedTime CurrentTime = (Undo ? m_OldTime : m_NewTime);
1647
1648 std::shared_ptr<CEnvelope> pEnvelope = Map()->m_vpEnvelopes[m_EnvelopeIndex];
1649 if(m_Type == EType::TANGENT_IN)
1650 {
1651 pEnvelope->m_vPoints[m_PointIndex].m_Bezier.m_aInTangentDeltaX[m_Channel] = std::min(a: CurrentTime - pEnvelope->m_vPoints[m_PointIndex].m_Time, b: CFixedTime(0));
1652 pEnvelope->m_vPoints[m_PointIndex].m_Bezier.m_aInTangentDeltaY[m_Channel] = f2fx(v: CurrentValue) - pEnvelope->m_vPoints[m_PointIndex].m_aValues[m_Channel];
1653 }
1654 else if(m_Type == EType::TANGENT_OUT)
1655 {
1656 pEnvelope->m_vPoints[m_PointIndex].m_Bezier.m_aOutTangentDeltaX[m_Channel] = std::max(a: CurrentTime - pEnvelope->m_vPoints[m_PointIndex].m_Time, b: CFixedTime(0));
1657 pEnvelope->m_vPoints[m_PointIndex].m_Bezier.m_aOutTangentDeltaY[m_Channel] = f2fx(v: CurrentValue) - pEnvelope->m_vPoints[m_PointIndex].m_aValues[m_Channel];
1658 }
1659 else
1660 {
1661 if(pEnvelope->GetChannels() == 1 || pEnvelope->GetChannels() == 4)
1662 CurrentValue = std::clamp(val: CurrentValue, lo: 0.0f, hi: 1.0f);
1663 pEnvelope->m_vPoints[m_PointIndex].m_aValues[m_Channel] = f2fx(v: CurrentValue);
1664
1665 if(m_PointIndex != 0)
1666 {
1667 pEnvelope->m_vPoints[m_PointIndex].m_Time = CurrentTime;
1668
1669 if(pEnvelope->m_vPoints[m_PointIndex].m_Time < pEnvelope->m_vPoints[m_PointIndex - 1].m_Time)
1670 pEnvelope->m_vPoints[m_PointIndex].m_Time = pEnvelope->m_vPoints[m_PointIndex - 1].m_Time + CFixedTime(1);
1671 if(static_cast<size_t>(m_PointIndex) + 1 != pEnvelope->m_vPoints.size() && pEnvelope->m_vPoints[m_PointIndex].m_Time > pEnvelope->m_vPoints[m_PointIndex + 1].m_Time)
1672 pEnvelope->m_vPoints[m_PointIndex].m_Time = pEnvelope->m_vPoints[m_PointIndex + 1].m_Time - CFixedTime(1);
1673 }
1674 else
1675 {
1676 pEnvelope->m_vPoints[m_PointIndex].m_Time = CFixedTime(0);
1677 }
1678 }
1679
1680 Map()->OnModify();
1681 Map()->m_UpdateEnvPointInfo = true;
1682}
1683
1684// ---------------------
1685
1686CEditorActionResetEnvelopePointTangent::CEditorActionResetEnvelopePointTangent(CEditorMap *pMap, int EnvelopeIndex, int PointIndex, int Channel, bool In, CFixedTime OldTime, int OldValue) :
1687 CEditorActionEditEnvelopePointValue(pMap, EnvelopeIndex, PointIndex, Channel, In ? EType::TANGENT_IN : EType::TANGENT_OUT, OldTime, OldValue, CFixedTime(0), 0)
1688{
1689 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Reset point %d of env %d tangent %s", PointIndex, EnvelopeIndex, In ? "in" : "out");
1690}
1691
1692// ------------------
1693
1694CEditorActionAddEnvelopePoint::CEditorActionAddEnvelopePoint(CEditorMap *pMap, int EnvelopeIndex, CFixedTime Time, ColorRGBA Channels) :
1695 IEditorAction(pMap), m_EnvelopeIndex(EnvelopeIndex), m_Time(Time), m_Channels(Channels)
1696{
1697 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Add new point in envelope %d at time %f", m_EnvelopeIndex, Time.AsSeconds());
1698}
1699
1700void CEditorActionAddEnvelopePoint::Undo()
1701{
1702 // Delete added point
1703 auto pEnvelope = Map()->m_vpEnvelopes[m_EnvelopeIndex];
1704 auto pIt = std::find_if(first: pEnvelope->m_vPoints.begin(), last: pEnvelope->m_vPoints.end(), pred: [this](const CEnvPoint_runtime &Point) {
1705 return Point.m_Time == m_Time;
1706 });
1707 if(pIt != pEnvelope->m_vPoints.end())
1708 {
1709 pEnvelope->m_vPoints.erase(position: pIt);
1710 }
1711
1712 Map()->OnModify();
1713}
1714
1715void CEditorActionAddEnvelopePoint::Redo()
1716{
1717 auto pEnvelope = Map()->m_vpEnvelopes[m_EnvelopeIndex];
1718 pEnvelope->AddPoint(Time: m_Time, aValues: {f2fx(v: m_Channels.r), f2fx(v: m_Channels.g), f2fx(v: m_Channels.b), f2fx(v: m_Channels.a)});
1719
1720 Map()->OnModify();
1721}
1722
1723CEditorActionDeleteEnvelopePoint::CEditorActionDeleteEnvelopePoint(CEditorMap *pMap, int EnvelopeIndex, int PointIndex) :
1724 IEditorAction(pMap), m_EnvelopeIndex(EnvelopeIndex), m_PointIndex(PointIndex), m_Point(Map()->m_vpEnvelopes[EnvelopeIndex]->m_vPoints[PointIndex])
1725{
1726 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Delete point %d of envelope %d", m_PointIndex, m_EnvelopeIndex);
1727}
1728
1729void CEditorActionDeleteEnvelopePoint::Undo()
1730{
1731 std::shared_ptr<CEnvelope> pEnvelope = Map()->m_vpEnvelopes[m_EnvelopeIndex];
1732 pEnvelope->m_vPoints.insert(position: pEnvelope->m_vPoints.begin() + m_PointIndex, x: m_Point);
1733
1734 Map()->OnModify();
1735}
1736
1737void CEditorActionDeleteEnvelopePoint::Redo()
1738{
1739 std::shared_ptr<CEnvelope> pEnvelope = Map()->m_vpEnvelopes[m_EnvelopeIndex];
1740 pEnvelope->m_vPoints.erase(position: pEnvelope->m_vPoints.begin() + m_PointIndex);
1741
1742 auto pSelectedPointIt = std::find_if(first: Map()->m_vSelectedEnvelopePoints.begin(), last: Map()->m_vSelectedEnvelopePoints.end(), pred: [this](const std::pair<int, int> &Pair) {
1743 return Pair.first == m_PointIndex;
1744 });
1745
1746 if(pSelectedPointIt != Map()->m_vSelectedEnvelopePoints.end())
1747 Map()->m_vSelectedEnvelopePoints.erase(position: pSelectedPointIt);
1748
1749 Map()->OnModify();
1750}
1751
1752// -------------------------------
1753
1754CEditorActionEditLayerSoundsProp::CEditorActionEditLayerSoundsProp(CEditorMap *pMap, int GroupIndex, int LayerIndex, ELayerSoundsProp Prop, int Previous, int Current) :
1755 CEditorActionEditLayerPropBase(pMap, GroupIndex, LayerIndex, Prop, Previous, Current)
1756{
1757 static const char *s_apNames[] = {
1758 "sound"};
1759 static_assert(std::size(s_apNames) == (size_t)ELayerSoundsProp::NUM_PROPS);
1760 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Edit sounds layer %d in group %d %s property", m_LayerIndex, m_GroupIndex, s_apNames[(int)m_Prop]);
1761}
1762
1763void CEditorActionEditLayerSoundsProp::Undo()
1764{
1765 Apply(Value: m_Previous);
1766}
1767
1768void CEditorActionEditLayerSoundsProp::Redo()
1769{
1770 Apply(Value: m_Current);
1771}
1772
1773void CEditorActionEditLayerSoundsProp::Apply(int Value)
1774{
1775 std::shared_ptr<CLayerSounds> pLayerSounds = std::static_pointer_cast<CLayerSounds>(r: m_pLayer);
1776 if(m_Prop == ELayerSoundsProp::SOUND)
1777 {
1778 if(Value >= 0 && !Map()->m_vpSounds.empty())
1779 pLayerSounds->m_Sound = Value % Map()->m_vpSounds.size();
1780 else
1781 pLayerSounds->m_Sound = -1;
1782 }
1783
1784 Map()->OnModify();
1785}
1786
1787// ---
1788
1789CEditorActionDeleteSoundSource::CEditorActionDeleteSoundSource(CEditorMap *pMap, int GroupIndex, int LayerIndex, int SourceIndex) :
1790 CEditorActionLayerBase(pMap, GroupIndex, LayerIndex), m_SourceIndex(SourceIndex)
1791{
1792 std::shared_ptr<CLayerSounds> pLayerSounds = std::static_pointer_cast<CLayerSounds>(r: m_pLayer);
1793 m_Source = pLayerSounds->m_vSources[SourceIndex];
1794
1795 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Delete sound source %d in layer %d of group %d", SourceIndex, LayerIndex, GroupIndex);
1796}
1797
1798void CEditorActionDeleteSoundSource::Undo()
1799{
1800 std::shared_ptr<CLayerSounds> pLayerSounds = std::static_pointer_cast<CLayerSounds>(r: m_pLayer);
1801 pLayerSounds->m_vSources.insert(position: pLayerSounds->m_vSources.begin() + m_SourceIndex, x: m_Source);
1802 Map()->m_SelectedSoundSource = m_SourceIndex;
1803 Map()->OnModify();
1804}
1805
1806void CEditorActionDeleteSoundSource::Redo()
1807{
1808 std::shared_ptr<CLayerSounds> pLayerSounds = std::static_pointer_cast<CLayerSounds>(r: m_pLayer);
1809 pLayerSounds->m_vSources.erase(position: pLayerSounds->m_vSources.begin() + m_SourceIndex);
1810 Map()->m_SelectedSoundSource--;
1811 Map()->OnModify();
1812}
1813
1814// ---------------
1815
1816CEditorActionEditSoundSourceShape::CEditorActionEditSoundSourceShape(CEditorMap *pMap, int GroupIndex, int LayerIndex, int SourceIndex, int Value) :
1817 CEditorActionLayerBase(pMap, GroupIndex, LayerIndex), m_SourceIndex(SourceIndex), m_CurrentValue(Value)
1818{
1819 Save();
1820
1821 static const char *const SHAPE_NAMES[] = {
1822 "rectangle",
1823 "circle",
1824 };
1825 static_assert(std::size(SHAPE_NAMES) == (size_t)CSoundShape::NUM_SHAPES);
1826 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText),
1827 format: "Edit shape of sound source %d in layer %d of group %d to %s",
1828 SourceIndex, LayerIndex, GroupIndex, SHAPE_NAMES[Value]);
1829}
1830
1831void CEditorActionEditSoundSourceShape::Undo()
1832{
1833 std::shared_ptr<CLayerSounds> pLayerSounds = std::static_pointer_cast<CLayerSounds>(r: m_pLayer);
1834 CSoundSource *pSource = &pLayerSounds->m_vSources[m_SourceIndex];
1835
1836 pSource->m_Shape = m_SavedShape;
1837
1838 Map()->OnModify();
1839}
1840
1841void CEditorActionEditSoundSourceShape::Redo()
1842{
1843 std::shared_ptr<CLayerSounds> pLayerSounds = std::static_pointer_cast<CLayerSounds>(r: m_pLayer);
1844 CSoundSource *pSource = &pLayerSounds->m_vSources[m_SourceIndex];
1845
1846 pSource->m_Shape.m_Type = m_CurrentValue;
1847
1848 // set default values
1849 switch(pSource->m_Shape.m_Type)
1850 {
1851 case CSoundShape::SHAPE_CIRCLE:
1852 {
1853 pSource->m_Shape.m_Circle.m_Radius = 1000;
1854 break;
1855 }
1856 case CSoundShape::SHAPE_RECTANGLE:
1857 {
1858 pSource->m_Shape.m_Rectangle.m_Width = f2fx(v: 1000.0f);
1859 pSource->m_Shape.m_Rectangle.m_Height = f2fx(v: 800.0f);
1860 break;
1861 }
1862 }
1863
1864 Map()->OnModify();
1865}
1866
1867void CEditorActionEditSoundSourceShape::Save()
1868{
1869 std::shared_ptr<CLayerSounds> pLayerSounds = std::static_pointer_cast<CLayerSounds>(r: m_pLayer);
1870 m_SavedShape = pLayerSounds->m_vSources[m_SourceIndex].m_Shape;
1871}
1872
1873// -----
1874
1875CEditorActionEditSoundSourceProp::CEditorActionEditSoundSourceProp(CEditorMap *pMap, int GroupIndex, int LayerIndex, int SourceIndex, ESoundProp Prop, int Previous, int Current) :
1876 CEditorActionEditLayerPropBase(pMap, GroupIndex, LayerIndex, Prop, Previous, Current), m_SourceIndex(SourceIndex)
1877{
1878 static const char *s_apNames[] = {
1879 "pos X",
1880 "pos Y",
1881 "loop",
1882 "pan",
1883 "time delay",
1884 "falloff",
1885 "pos env",
1886 "pos env offset",
1887 "sound env",
1888 "sound env offset"};
1889 static_assert(std::size(s_apNames) == (size_t)ESoundProp::NUM_PROPS);
1890 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Edit sound source %d in layer %d of group %d %s property", SourceIndex, LayerIndex, GroupIndex, s_apNames[(int)Prop]);
1891}
1892
1893void CEditorActionEditSoundSourceProp::Undo()
1894{
1895 Apply(Value: m_Previous);
1896}
1897
1898void CEditorActionEditSoundSourceProp::Redo()
1899{
1900 Apply(Value: m_Current);
1901}
1902
1903void CEditorActionEditSoundSourceProp::Apply(int Value)
1904{
1905 std::shared_ptr<CLayerSounds> pLayerSounds = std::static_pointer_cast<CLayerSounds>(r: m_pLayer);
1906 CSoundSource *pSource = &pLayerSounds->m_vSources[m_SourceIndex];
1907
1908 if(m_Prop == ESoundProp::POS_X)
1909 {
1910 pSource->m_Position.x = Value;
1911 }
1912 else if(m_Prop == ESoundProp::POS_Y)
1913 {
1914 pSource->m_Position.y = Value;
1915 }
1916 else if(m_Prop == ESoundProp::LOOP)
1917 {
1918 pSource->m_Loop = Value;
1919 }
1920 else if(m_Prop == ESoundProp::PAN)
1921 {
1922 pSource->m_Pan = Value;
1923 }
1924 else if(m_Prop == ESoundProp::TIME_DELAY)
1925 {
1926 pSource->m_TimeDelay = Value;
1927 }
1928 else if(m_Prop == ESoundProp::FALLOFF)
1929 {
1930 pSource->m_Falloff = Value;
1931 }
1932 else if(m_Prop == ESoundProp::POS_ENV)
1933 {
1934 pSource->m_PosEnv = Value;
1935 }
1936 else if(m_Prop == ESoundProp::POS_ENV_OFFSET)
1937 {
1938 pSource->m_PosEnvOffset = Value;
1939 }
1940 else if(m_Prop == ESoundProp::SOUND_ENV)
1941 {
1942 pSource->m_SoundEnv = Value;
1943 }
1944 else if(m_Prop == ESoundProp::SOUND_ENV_OFFSET)
1945 {
1946 pSource->m_SoundEnvOffset = Value;
1947 }
1948
1949 Map()->OnModify();
1950}
1951
1952CEditorActionEditRectSoundSourceShapeProp::CEditorActionEditRectSoundSourceShapeProp(CEditorMap *pMap, int GroupIndex, int LayerIndex, int SourceIndex, ERectangleShapeProp Prop, int Previous, int Current) :
1953 CEditorActionEditLayerPropBase(pMap, GroupIndex, LayerIndex, Prop, Previous, Current), m_SourceIndex(SourceIndex)
1954{
1955 static const char *s_apNames[] = {
1956 "width",
1957 "height"};
1958 static_assert(std::size(s_apNames) == (size_t)ERectangleShapeProp::NUM_PROPS);
1959 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Edit sound source %d in layer %d of group %d sound shape %s property", m_SourceIndex, m_LayerIndex, m_GroupIndex, s_apNames[(int)Prop]);
1960}
1961
1962void CEditorActionEditRectSoundSourceShapeProp::Undo()
1963{
1964 Apply(Value: m_Previous);
1965}
1966
1967void CEditorActionEditRectSoundSourceShapeProp::Redo()
1968{
1969 Apply(Value: m_Current);
1970}
1971
1972void CEditorActionEditRectSoundSourceShapeProp::Apply(int Value)
1973{
1974 std::shared_ptr<CLayerSounds> pLayerSounds = std::static_pointer_cast<CLayerSounds>(r: m_pLayer);
1975 CSoundSource *pSource = &pLayerSounds->m_vSources[m_SourceIndex];
1976
1977 if(m_Prop == ERectangleShapeProp::RECTANGLE_WIDTH)
1978 {
1979 pSource->m_Shape.m_Rectangle.m_Width = Value;
1980 }
1981 else if(m_Prop == ERectangleShapeProp::RECTANGLE_HEIGHT)
1982 {
1983 pSource->m_Shape.m_Rectangle.m_Height = Value;
1984 }
1985
1986 Map()->OnModify();
1987}
1988
1989CEditorActionEditCircleSoundSourceShapeProp::CEditorActionEditCircleSoundSourceShapeProp(CEditorMap *pMap, int GroupIndex, int LayerIndex, int SourceIndex, ECircleShapeProp Prop, int Previous, int Current) :
1990 CEditorActionEditLayerPropBase(pMap, GroupIndex, LayerIndex, Prop, Previous, Current), m_SourceIndex(SourceIndex)
1991{
1992 static const char *s_apNames[] = {
1993 "radius"};
1994 static_assert(std::size(s_apNames) == (size_t)ECircleShapeProp::NUM_PROPS);
1995 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Edit sound source %d in layer %d of group %d sound shape %s property", m_SourceIndex, m_LayerIndex, m_GroupIndex, s_apNames[(int)Prop]);
1996}
1997
1998void CEditorActionEditCircleSoundSourceShapeProp::Undo()
1999{
2000 Apply(Value: m_Previous);
2001}
2002
2003void CEditorActionEditCircleSoundSourceShapeProp::Redo()
2004{
2005 Apply(Value: m_Current);
2006}
2007
2008void CEditorActionEditCircleSoundSourceShapeProp::Apply(int Value)
2009{
2010 std::shared_ptr<CLayerSounds> pLayerSounds = std::static_pointer_cast<CLayerSounds>(r: m_pLayer);
2011 CSoundSource *pSource = &pLayerSounds->m_vSources[m_SourceIndex];
2012
2013 if(m_Prop == ECircleShapeProp::CIRCLE_RADIUS)
2014 {
2015 pSource->m_Shape.m_Circle.m_Radius = Value;
2016 }
2017
2018 Map()->OnModify();
2019}
2020
2021// --------------------------
2022
2023CEditorActionNewEmptySound::CEditorActionNewEmptySound(CEditorMap *pMap, int GroupIndex, int LayerIndex, int x, int y) :
2024 CEditorActionLayerBase(pMap, GroupIndex, LayerIndex), m_X(x), m_Y(y)
2025{
2026 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "New sound in layer %d of group %d", LayerIndex, GroupIndex);
2027}
2028
2029void CEditorActionNewEmptySound::Undo()
2030{
2031 // Undo is simply deleting the added source
2032 std::shared_ptr<CLayerSounds> pLayerSounds = std::static_pointer_cast<CLayerSounds>(r: m_pLayer);
2033 pLayerSounds->m_vSources.pop_back();
2034
2035 Map()->OnModify();
2036}
2037
2038void CEditorActionNewEmptySound::Redo()
2039{
2040 std::shared_ptr<CLayerSounds> pLayerSounds = std::static_pointer_cast<CLayerSounds>(r: m_pLayer);
2041 pLayerSounds->NewSource(x: m_X, y: m_Y);
2042
2043 Map()->OnModify();
2044}
2045
2046CEditorActionNewEmptyQuad::CEditorActionNewEmptyQuad(CEditorMap *pMap, int GroupIndex, int LayerIndex, int x, int y) :
2047 CEditorActionLayerBase(pMap, GroupIndex, LayerIndex), m_X(x), m_Y(y)
2048{
2049 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "New quad in layer %d of group %d", LayerIndex, GroupIndex);
2050}
2051
2052void CEditorActionNewEmptyQuad::Undo()
2053{
2054 // Undo is simply deleting the added quad
2055 std::shared_ptr<CLayerQuads> pLayerQuads = std::static_pointer_cast<CLayerQuads>(r: m_pLayer);
2056 pLayerQuads->m_vQuads.pop_back();
2057
2058 Map()->OnModify();
2059}
2060
2061void CEditorActionNewEmptyQuad::Redo()
2062{
2063 std::shared_ptr<CLayerQuads> pLayerQuads = std::static_pointer_cast<CLayerQuads>(r: m_pLayer);
2064
2065 int Width = 64;
2066 int Height = 64;
2067 if(pLayerQuads->m_Image >= 0)
2068 {
2069 Width = Map()->m_vpImages[pLayerQuads->m_Image]->m_Width;
2070 Height = Map()->m_vpImages[pLayerQuads->m_Image]->m_Height;
2071 }
2072
2073 pLayerQuads->NewQuad(x: m_X, y: m_Y, Width, Height);
2074
2075 Map()->OnModify();
2076}
2077
2078// -------------
2079
2080CEditorActionNewQuad::CEditorActionNewQuad(CEditorMap *pMap, int GroupIndex, int LayerIndex) :
2081 CEditorActionLayerBase(pMap, GroupIndex, LayerIndex)
2082{
2083 std::shared_ptr<CLayerQuads> pLayerQuads = std::static_pointer_cast<CLayerQuads>(r: m_pLayer);
2084 m_Quad = pLayerQuads->m_vQuads[pLayerQuads->m_vQuads.size() - 1];
2085
2086 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "New quad in layer %d of group %d", LayerIndex, GroupIndex);
2087}
2088
2089void CEditorActionNewQuad::Undo()
2090{
2091 std::shared_ptr<CLayerQuads> pLayerQuads = std::static_pointer_cast<CLayerQuads>(r: m_pLayer);
2092 pLayerQuads->m_vQuads.pop_back();
2093}
2094
2095void CEditorActionNewQuad::Redo()
2096{
2097 std::shared_ptr<CLayerQuads> pLayerQuads = std::static_pointer_cast<CLayerQuads>(r: m_pLayer);
2098 pLayerQuads->m_vQuads.emplace_back(args&: m_Quad);
2099}
2100
2101// --------------
2102
2103CEditorActionMoveSoundSource::CEditorActionMoveSoundSource(CEditorMap *pMap, int GroupIndex, int LayerIndex, int SourceIndex, CPoint OriginalPosition, CPoint CurrentPosition) :
2104 CEditorActionLayerBase(pMap, GroupIndex, LayerIndex), m_SourceIndex(SourceIndex), m_OriginalPosition(OriginalPosition), m_CurrentPosition(CurrentPosition)
2105{
2106 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Move sound source %d of layer %d in group %d", SourceIndex, LayerIndex, GroupIndex);
2107}
2108
2109void CEditorActionMoveSoundSource::Undo()
2110{
2111 dbg_assert(m_pLayer->m_Type == LAYERTYPE_SOUNDS, "Layer type does not match a sound layer");
2112 std::static_pointer_cast<CLayerSounds>(r: m_pLayer)->m_vSources[m_SourceIndex].m_Position = m_OriginalPosition;
2113}
2114
2115void CEditorActionMoveSoundSource::Redo()
2116{
2117 dbg_assert(m_pLayer->m_Type == LAYERTYPE_SOUNDS, "Layer type does not match a sound layer");
2118 std::static_pointer_cast<CLayerSounds>(r: m_pLayer)->m_vSources[m_SourceIndex].m_Position = m_CurrentPosition;
2119}
2120