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