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");
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 = std::max(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 = std::max(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 Map()->OnModify();
1253}
1254
1255void CEditorActionAppendMap::Redo()
1256{
1257 const auto &&ErrorHandler = [this](const char *pErrorMessage) {
1258 Editor()->ShowFileDialogError(pFormat: "%s", pErrorMessage);
1259 log_error("editor/append", "%s", pErrorMessage);
1260 };
1261 // Redo is just re-appending the same map
1262 Map()->Append(pFilename: m_aMapName, StorageType: IStorage::TYPE_ALL, IgnoreHistory: true, ErrorHandler);
1263}
1264
1265// ---------------------------
1266
1267CEditorActionTileArt::CEditorActionTileArt(CEditorMap *pMap, int PreviousImageCount, const char *pFilename, std::vector<int> &vImageIndexMap) :
1268 IEditorAction(pMap), m_PreviousImageCount(PreviousImageCount), m_vImageIndexMap(vImageIndexMap)
1269{
1270 str_copy(dst&: m_aFilename, src: pFilename);
1271 str_copy(dst&: m_aDisplayText, src: "Add tile art");
1272}
1273
1274void CEditorActionTileArt::Undo()
1275{
1276 // Delete added group
1277 Map()->m_vpGroups.pop_back();
1278
1279 // Delete added images
1280 // Images are sorted when appending, so we need to revert sorting before deleting the images
1281 if(!m_vImageIndexMap.empty())
1282 {
1283 std::vector<int> vReverseIndexMap;
1284 vReverseIndexMap.resize(sz: m_vImageIndexMap.size());
1285
1286 for(int k = 0; k < (int)m_vImageIndexMap.size(); k++)
1287 vReverseIndexMap[m_vImageIndexMap[k]] = k;
1288
1289 std::vector<std::shared_ptr<CEditorImage>> vpRevertedImages;
1290 vpRevertedImages.resize(sz: Map()->m_vpImages.size());
1291
1292 for(int k = 0; k < (int)vReverseIndexMap.size(); k++)
1293 {
1294 vpRevertedImages[vReverseIndexMap[k]] = Map()->m_vpImages[k];
1295 }
1296 Map()->m_vpImages = vpRevertedImages;
1297
1298 Map()->ModifyImageIndex(IndexModifyFunction: [vReverseIndexMap](int *pIndex) {
1299 if(*pIndex >= 0)
1300 {
1301 *pIndex = vReverseIndexMap[*pIndex];
1302 }
1303 });
1304 }
1305
1306 while((int)Map()->m_vpImages.size() != m_PreviousImageCount)
1307 {
1308 Map()->m_vpImages.pop_back();
1309 }
1310}
1311
1312void CEditorActionTileArt::Redo()
1313{
1314 CImageInfo Image;
1315 if(!Graphics()->LoadPng(Image, pFilename: m_aFilename, StorageType: IStorage::TYPE_ALL))
1316 {
1317 Editor()->ShowFileDialogError(pFormat: "Failed to load image from file '%s'.", m_aFilename);
1318 return;
1319 }
1320 Map()->AddTileArt(Image: std::move(Image), pFilename: m_aFilename, IgnoreHistory: true);
1321}
1322
1323// ---------------------------
1324
1325CEditorActionQuadArt::CEditorActionQuadArt(CEditorMap *pMap, const std::shared_ptr<CLayerGroup> &pGroup) :
1326 IEditorAction(pMap), m_pGroup(pGroup)
1327{
1328 str_copy(dst&: m_aDisplayText, src: "Add quad art");
1329}
1330
1331void CEditorActionQuadArt::Undo()
1332{
1333 // Delete added group (keep pointer for redo)
1334 auto &vGroups = Map()->m_vpGroups;
1335 auto It = std::find(first: vGroups.begin(), last: vGroups.end(), val: m_pGroup);
1336 if(It != vGroups.end())
1337 vGroups.erase(position: It);
1338}
1339
1340void CEditorActionQuadArt::Redo()
1341{
1342 auto &vGroups = Map()->m_vpGroups;
1343 if(std::find(first: vGroups.begin(), last: vGroups.end(), val: m_pGroup) == vGroups.end())
1344 vGroups.push_back(x: m_pGroup);
1345}
1346
1347// ---------------------------------
1348
1349CEditorCommandAction::CEditorCommandAction(CEditorMap *pMap, EType Type, int *pSelectedCommandIndex, int CommandIndex, const char *pPreviousCommand, const char *pCurrentCommand) :
1350 IEditorAction(pMap), m_Type(Type), m_pSelectedCommandIndex(pSelectedCommandIndex), m_CommandIndex(CommandIndex)
1351{
1352 if(pPreviousCommand != nullptr)
1353 m_PreviousCommand = std::string(pPreviousCommand);
1354 if(pCurrentCommand != nullptr)
1355 m_CurrentCommand = std::string(pCurrentCommand);
1356
1357 switch(m_Type)
1358 {
1359 case EType::ADD:
1360 str_copy(dst&: m_aDisplayText, src: "Add command");
1361 break;
1362 case EType::EDIT:
1363 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Edit command %d", m_CommandIndex);
1364 break;
1365 case EType::DELETE:
1366 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Delete command %d", m_CommandIndex);
1367 break;
1368 case EType::MOVE_UP:
1369 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Move command %d up", m_CommandIndex);
1370 break;
1371 case EType::MOVE_DOWN:
1372 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Move command %d down", m_CommandIndex);
1373 break;
1374 default:
1375 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Edit command %d", m_CommandIndex);
1376 break;
1377 }
1378}
1379
1380void CEditorCommandAction::Undo()
1381{
1382 switch(m_Type)
1383 {
1384 case EType::DELETE:
1385 {
1386 Map()->m_vSettings.insert(position: Map()->m_vSettings.begin() + m_CommandIndex, x: m_PreviousCommand.c_str());
1387 *m_pSelectedCommandIndex = m_CommandIndex;
1388 break;
1389 }
1390 case EType::ADD:
1391 {
1392 Map()->m_vSettings.erase(position: Map()->m_vSettings.begin() + m_CommandIndex);
1393 *m_pSelectedCommandIndex = Map()->m_vSettings.size() - 1;
1394 break;
1395 }
1396 case EType::EDIT:
1397 {
1398 str_copy(dst&: Map()->m_vSettings[m_CommandIndex].m_aCommand, src: m_PreviousCommand.c_str());
1399 *m_pSelectedCommandIndex = m_CommandIndex;
1400 break;
1401 }
1402 case EType::MOVE_DOWN:
1403 {
1404 std::swap(a&: Map()->m_vSettings[m_CommandIndex], b&: Map()->m_vSettings[m_CommandIndex + 1]);
1405 *m_pSelectedCommandIndex = m_CommandIndex;
1406 break;
1407 }
1408 case EType::MOVE_UP:
1409 {
1410 std::swap(a&: Map()->m_vSettings[m_CommandIndex], b&: Map()->m_vSettings[m_CommandIndex - 1]);
1411 *m_pSelectedCommandIndex = m_CommandIndex;
1412 break;
1413 }
1414 }
1415}
1416
1417void CEditorCommandAction::Redo()
1418{
1419 switch(m_Type)
1420 {
1421 case EType::DELETE:
1422 {
1423 Map()->m_vSettings.erase(position: Map()->m_vSettings.begin() + m_CommandIndex);
1424 *m_pSelectedCommandIndex = Map()->m_vSettings.size() - 1;
1425 break;
1426 }
1427 case EType::ADD:
1428 {
1429 Map()->m_vSettings.insert(position: Map()->m_vSettings.begin() + m_CommandIndex, x: m_PreviousCommand.c_str());
1430 *m_pSelectedCommandIndex = m_CommandIndex;
1431 break;
1432 }
1433 case EType::EDIT:
1434 {
1435 str_copy(dst&: Map()->m_vSettings[m_CommandIndex].m_aCommand, src: m_CurrentCommand.c_str());
1436 *m_pSelectedCommandIndex = m_CommandIndex;
1437 break;
1438 }
1439 case EType::MOVE_DOWN:
1440 {
1441 std::swap(a&: Map()->m_vSettings[m_CommandIndex], b&: Map()->m_vSettings[m_CommandIndex + 1]);
1442 *m_pSelectedCommandIndex = m_CommandIndex;
1443 break;
1444 }
1445 case EType::MOVE_UP:
1446 {
1447 std::swap(a&: Map()->m_vSettings[m_CommandIndex], b&: Map()->m_vSettings[m_CommandIndex - 1]);
1448 *m_pSelectedCommandIndex = m_CommandIndex;
1449 break;
1450 }
1451 }
1452}
1453
1454// ------------------------------------------------
1455
1456CEditorActionEnvelopeAdd::CEditorActionEnvelopeAdd(CEditorMap *pMap, CEnvelope::EType EnvelopeType) :
1457 IEditorAction(pMap),
1458 m_EnvelopeType(EnvelopeType)
1459{
1460 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"));
1461 m_PreviousSelectedEnvelope = Map()->m_SelectedEnvelope;
1462}
1463
1464void CEditorActionEnvelopeAdd::Undo()
1465{
1466 // Undo is removing the envelope, which was added at the back of the list
1467 Map()->m_vpEnvelopes.pop_back();
1468 Map()->OnModify();
1469 Map()->m_SelectedEnvelope = m_PreviousSelectedEnvelope;
1470}
1471
1472void CEditorActionEnvelopeAdd::Redo()
1473{
1474 // Redo is adding a new envelope at the back of the list
1475 Map()->NewEnvelope(Type: m_EnvelopeType);
1476 Map()->m_SelectedEnvelope = Map()->m_vpEnvelopes.size() - 1;
1477}
1478
1479CEditorActionEnvelopeDelete::CEditorActionEnvelopeDelete(CEditorMap *pMap, int EnvelopeIndex, std::vector<std::shared_ptr<IEditorEnvelopeReference>> &vpObjectReferences, std::shared_ptr<CEnvelope> &pEnvelope) :
1480 IEditorAction(pMap), m_EnvelopeIndex(EnvelopeIndex), m_pEnv(pEnvelope), m_vpObjectReferences(vpObjectReferences)
1481{
1482 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Delete envelope %d", m_EnvelopeIndex);
1483}
1484
1485void CEditorActionEnvelopeDelete::Undo()
1486{
1487 // Undo is adding back the envelope
1488 Map()->InsertEnvelope(Index: m_EnvelopeIndex, pEnvelope&: m_pEnv);
1489 Map()->UpdateEnvelopeReferences(Index: m_EnvelopeIndex, pEnvelope&: m_pEnv, vpEditorObjectReferences&: m_vpObjectReferences);
1490}
1491
1492void CEditorActionEnvelopeDelete::Redo()
1493{
1494 // Redo is erasing the same envelope index
1495 Map()->DeleteEnvelope(Index: m_EnvelopeIndex);
1496}
1497
1498CEditorActionEnvelopeEdit::CEditorActionEnvelopeEdit(CEditorMap *pMap, int EnvelopeIndex, EEditType EditType, int Previous, int Current) :
1499 IEditorAction(pMap), m_EnvelopeIndex(EnvelopeIndex), m_EditType(EditType), m_Previous(Previous), m_Current(Current)
1500{
1501 static const char *s_apNames[] = {
1502 "sync",
1503 "order"};
1504 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Edit envelope %d %s", m_EnvelopeIndex, s_apNames[(int)m_EditType]);
1505}
1506
1507void CEditorActionEnvelopeEdit::Undo()
1508{
1509 switch(m_EditType)
1510 {
1511 case EEditType::ORDER:
1512 {
1513 Map()->MoveEnvelope(IndexFrom: m_Current, IndexTo: m_Previous);
1514 break;
1515 }
1516 case EEditType::SYNC:
1517 {
1518 Map()->m_vpEnvelopes[m_EnvelopeIndex]->m_Synchronized = m_Previous;
1519 break;
1520 }
1521 }
1522 Map()->OnModify();
1523 Map()->m_SelectedEnvelope = m_EnvelopeIndex;
1524}
1525
1526void CEditorActionEnvelopeEdit::Redo()
1527{
1528 switch(m_EditType)
1529 {
1530 case EEditType::ORDER:
1531 {
1532 Map()->MoveEnvelope(IndexFrom: m_Previous, IndexTo: m_Current);
1533 break;
1534 }
1535 case EEditType::SYNC:
1536 {
1537 Map()->m_vpEnvelopes[m_EnvelopeIndex]->m_Synchronized = m_Current;
1538 break;
1539 }
1540 }
1541 Map()->OnModify();
1542 Map()->m_SelectedEnvelope = m_EnvelopeIndex;
1543}
1544
1545CEditorActionEnvelopeEditPointTime::CEditorActionEnvelopeEditPointTime(CEditorMap *pMap, int EnvelopeIndex, int PointIndex, CFixedTime Previous, CFixedTime Current) :
1546 IEditorAction(pMap), m_EnvelopeIndex(EnvelopeIndex), m_PointIndex(PointIndex), m_Previous(Previous), m_Current(Current)
1547{
1548 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Edit time of point %d of env %d", m_PointIndex, m_EnvelopeIndex);
1549}
1550
1551void CEditorActionEnvelopeEditPointTime::Undo()
1552{
1553 Apply(Value: m_Previous);
1554}
1555
1556void CEditorActionEnvelopeEditPointTime::Redo()
1557{
1558 Apply(Value: m_Current);
1559}
1560
1561void CEditorActionEnvelopeEditPointTime::Apply(CFixedTime Value)
1562{
1563 Map()->m_vpEnvelopes[m_EnvelopeIndex]->m_vPoints[m_PointIndex].m_Time = Value;
1564 Map()->OnModify();
1565}
1566
1567CEditorActionEnvelopeEditPoint::CEditorActionEnvelopeEditPoint(CEditorMap *pMap, int EnvelopeIndex, int PointIndex, int Channel, EEditType EditType, int Previous, int Current) :
1568 IEditorAction(pMap), m_EnvelopeIndex(EnvelopeIndex), m_PointIndex(PointIndex), m_Channel(Channel), m_EditType(EditType), m_Previous(Previous), m_Current(Current)
1569{
1570 static const char *s_apNames[] = {
1571 "value",
1572 "curve type"};
1573 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);
1574}
1575
1576void CEditorActionEnvelopeEditPoint::Undo()
1577{
1578 Apply(Value: m_Previous);
1579}
1580
1581void CEditorActionEnvelopeEditPoint::Redo()
1582{
1583 Apply(Value: m_Current);
1584}
1585
1586void CEditorActionEnvelopeEditPoint::Apply(int Value)
1587{
1588 auto pEnvelope = Map()->m_vpEnvelopes[m_EnvelopeIndex];
1589
1590 if(m_EditType == EEditType::VALUE)
1591 {
1592 pEnvelope->m_vPoints[m_PointIndex].m_aValues[m_Channel] = Value;
1593
1594 if(pEnvelope->GetChannels() == 4)
1595 {
1596 Editor()->m_ColorPickerPopupContext.m_RgbaColor = pEnvelope->m_vPoints[m_PointIndex].ColorValue();
1597 Editor()->m_ColorPickerPopupContext.m_HslaColor = color_cast<ColorHSLA>(rgb: Editor()->m_ColorPickerPopupContext.m_RgbaColor);
1598 Editor()->m_ColorPickerPopupContext.m_HsvaColor = color_cast<ColorHSVA>(hsl: Editor()->m_ColorPickerPopupContext.m_HslaColor);
1599 }
1600 }
1601 else if(m_EditType == EEditType::CURVE_TYPE)
1602 {
1603 pEnvelope->m_vPoints[m_PointIndex].m_Curvetype = Value;
1604 }
1605
1606 Map()->OnModify();
1607}
1608
1609// ----
1610
1611CEditorActionEditEnvelopePointValue::CEditorActionEditEnvelopePointValue(CEditorMap *pMap, int EnvelopeIndex, int PointIndex, int Channel, EType Type, CFixedTime OldTime, int OldValue, CFixedTime NewTime, int NewValue) :
1612 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)
1613{
1614 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);
1615}
1616
1617void CEditorActionEditEnvelopePointValue::Undo()
1618{
1619 Apply(Undo: true);
1620}
1621
1622void CEditorActionEditEnvelopePointValue::Redo()
1623{
1624 Apply(Undo: false);
1625}
1626
1627void CEditorActionEditEnvelopePointValue::Apply(bool Undo)
1628{
1629 float CurrentValue = fx2f(v: Undo ? m_OldValue : m_NewValue);
1630 CFixedTime CurrentTime = (Undo ? m_OldTime : m_NewTime);
1631
1632 std::shared_ptr<CEnvelope> pEnvelope = Map()->m_vpEnvelopes[m_EnvelopeIndex];
1633 if(m_Type == EType::TANGENT_IN)
1634 {
1635 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));
1636 pEnvelope->m_vPoints[m_PointIndex].m_Bezier.m_aInTangentDeltaY[m_Channel] = f2fx(v: CurrentValue) - pEnvelope->m_vPoints[m_PointIndex].m_aValues[m_Channel];
1637 }
1638 else if(m_Type == EType::TANGENT_OUT)
1639 {
1640 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));
1641 pEnvelope->m_vPoints[m_PointIndex].m_Bezier.m_aOutTangentDeltaY[m_Channel] = f2fx(v: CurrentValue) - pEnvelope->m_vPoints[m_PointIndex].m_aValues[m_Channel];
1642 }
1643 else
1644 {
1645 if(pEnvelope->GetChannels() == 1 || pEnvelope->GetChannels() == 4)
1646 CurrentValue = std::clamp(val: CurrentValue, lo: 0.0f, hi: 1.0f);
1647 pEnvelope->m_vPoints[m_PointIndex].m_aValues[m_Channel] = f2fx(v: CurrentValue);
1648
1649 if(m_PointIndex != 0)
1650 {
1651 pEnvelope->m_vPoints[m_PointIndex].m_Time = CurrentTime;
1652
1653 if(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 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)
1656 pEnvelope->m_vPoints[m_PointIndex].m_Time = pEnvelope->m_vPoints[m_PointIndex + 1].m_Time - CFixedTime(1);
1657 }
1658 else
1659 {
1660 pEnvelope->m_vPoints[m_PointIndex].m_Time = CFixedTime(0);
1661 }
1662 }
1663
1664 Map()->OnModify();
1665 Map()->m_UpdateEnvPointInfo = true;
1666}
1667
1668// ---------------------
1669
1670CEditorActionResetEnvelopePointTangent::CEditorActionResetEnvelopePointTangent(CEditorMap *pMap, int EnvelopeIndex, int PointIndex, int Channel, bool In, CFixedTime OldTime, int OldValue) :
1671 CEditorActionEditEnvelopePointValue(pMap, EnvelopeIndex, PointIndex, Channel, In ? EType::TANGENT_IN : EType::TANGENT_OUT, OldTime, OldValue, CFixedTime(0), 0)
1672{
1673 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Reset point %d of env %d tangent %s", PointIndex, EnvelopeIndex, In ? "in" : "out");
1674}
1675
1676// ------------------
1677
1678CEditorActionAddEnvelopePoint::CEditorActionAddEnvelopePoint(CEditorMap *pMap, int EnvelopeIndex, CFixedTime Time, ColorRGBA Channels) :
1679 IEditorAction(pMap), m_EnvelopeIndex(EnvelopeIndex), m_Time(Time), m_Channels(Channels)
1680{
1681 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Add new point in envelope %d at time %f", m_EnvelopeIndex, Time.AsSeconds());
1682}
1683
1684void CEditorActionAddEnvelopePoint::Undo()
1685{
1686 // Delete added point
1687 auto pEnvelope = Map()->m_vpEnvelopes[m_EnvelopeIndex];
1688 auto pIt = std::find_if(first: pEnvelope->m_vPoints.begin(), last: pEnvelope->m_vPoints.end(), pred: [this](const CEnvPoint_runtime &Point) {
1689 return Point.m_Time == m_Time;
1690 });
1691 if(pIt != pEnvelope->m_vPoints.end())
1692 {
1693 pEnvelope->m_vPoints.erase(position: pIt);
1694 }
1695
1696 Map()->OnModify();
1697}
1698
1699void CEditorActionAddEnvelopePoint::Redo()
1700{
1701 auto pEnvelope = Map()->m_vpEnvelopes[m_EnvelopeIndex];
1702 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)});
1703
1704 Map()->OnModify();
1705}
1706
1707CEditorActionDeleteEnvelopePoint::CEditorActionDeleteEnvelopePoint(CEditorMap *pMap, int EnvelopeIndex, int PointIndex) :
1708 IEditorAction(pMap), m_EnvelopeIndex(EnvelopeIndex), m_PointIndex(PointIndex), m_Point(Map()->m_vpEnvelopes[EnvelopeIndex]->m_vPoints[PointIndex])
1709{
1710 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Delete point %d of envelope %d", m_PointIndex, m_EnvelopeIndex);
1711}
1712
1713void CEditorActionDeleteEnvelopePoint::Undo()
1714{
1715 std::shared_ptr<CEnvelope> pEnvelope = Map()->m_vpEnvelopes[m_EnvelopeIndex];
1716 pEnvelope->m_vPoints.insert(position: pEnvelope->m_vPoints.begin() + m_PointIndex, x: m_Point);
1717
1718 Map()->OnModify();
1719}
1720
1721void CEditorActionDeleteEnvelopePoint::Redo()
1722{
1723 std::shared_ptr<CEnvelope> pEnvelope = Map()->m_vpEnvelopes[m_EnvelopeIndex];
1724 pEnvelope->m_vPoints.erase(position: pEnvelope->m_vPoints.begin() + m_PointIndex);
1725
1726 auto pSelectedPointIt = std::find_if(first: Map()->m_vSelectedEnvelopePoints.begin(), last: Map()->m_vSelectedEnvelopePoints.end(), pred: [this](const std::pair<int, int> &Pair) {
1727 return Pair.first == m_PointIndex;
1728 });
1729
1730 if(pSelectedPointIt != Map()->m_vSelectedEnvelopePoints.end())
1731 Map()->m_vSelectedEnvelopePoints.erase(position: pSelectedPointIt);
1732
1733 Map()->OnModify();
1734}
1735
1736// -------------------------------
1737
1738CEditorActionEditLayerSoundsProp::CEditorActionEditLayerSoundsProp(CEditorMap *pMap, int GroupIndex, int LayerIndex, ELayerSoundsProp Prop, int Previous, int Current) :
1739 CEditorActionEditLayerPropBase(pMap, GroupIndex, LayerIndex, Prop, Previous, Current)
1740{
1741 static const char *s_apNames[] = {
1742 "sound"};
1743 static_assert(std::size(s_apNames) == (size_t)ELayerSoundsProp::NUM_PROPS);
1744 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]);
1745}
1746
1747void CEditorActionEditLayerSoundsProp::Undo()
1748{
1749 Apply(Value: m_Previous);
1750}
1751
1752void CEditorActionEditLayerSoundsProp::Redo()
1753{
1754 Apply(Value: m_Current);
1755}
1756
1757void CEditorActionEditLayerSoundsProp::Apply(int Value)
1758{
1759 std::shared_ptr<CLayerSounds> pLayerSounds = std::static_pointer_cast<CLayerSounds>(r: m_pLayer);
1760 if(m_Prop == ELayerSoundsProp::SOUND)
1761 {
1762 if(Value >= 0 && !Map()->m_vpSounds.empty())
1763 pLayerSounds->m_Sound = Value % Map()->m_vpSounds.size();
1764 else
1765 pLayerSounds->m_Sound = -1;
1766 }
1767
1768 Map()->OnModify();
1769}
1770
1771// ---
1772
1773CEditorActionDeleteSoundSource::CEditorActionDeleteSoundSource(CEditorMap *pMap, int GroupIndex, int LayerIndex, int SourceIndex) :
1774 CEditorActionLayerBase(pMap, GroupIndex, LayerIndex), m_SourceIndex(SourceIndex)
1775{
1776 std::shared_ptr<CLayerSounds> pLayerSounds = std::static_pointer_cast<CLayerSounds>(r: m_pLayer);
1777 m_Source = pLayerSounds->m_vSources[SourceIndex];
1778
1779 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Delete sound source %d in layer %d of group %d", SourceIndex, LayerIndex, GroupIndex);
1780}
1781
1782void CEditorActionDeleteSoundSource::Undo()
1783{
1784 std::shared_ptr<CLayerSounds> pLayerSounds = std::static_pointer_cast<CLayerSounds>(r: m_pLayer);
1785 pLayerSounds->m_vSources.insert(position: pLayerSounds->m_vSources.begin() + m_SourceIndex, x: m_Source);
1786 Map()->m_SelectedSoundSource = m_SourceIndex;
1787 Map()->OnModify();
1788}
1789
1790void CEditorActionDeleteSoundSource::Redo()
1791{
1792 std::shared_ptr<CLayerSounds> pLayerSounds = std::static_pointer_cast<CLayerSounds>(r: m_pLayer);
1793 pLayerSounds->m_vSources.erase(position: pLayerSounds->m_vSources.begin() + m_SourceIndex);
1794 Map()->m_SelectedSoundSource--;
1795 Map()->OnModify();
1796}
1797
1798// ---------------
1799
1800CEditorActionEditSoundSourceShape::CEditorActionEditSoundSourceShape(CEditorMap *pMap, int GroupIndex, int LayerIndex, int SourceIndex, int Value) :
1801 CEditorActionLayerBase(pMap, GroupIndex, LayerIndex), m_SourceIndex(SourceIndex), m_CurrentValue(Value)
1802{
1803 Save();
1804
1805 static const char *const SHAPE_NAMES[] = {
1806 "rectangle",
1807 "circle",
1808 };
1809 static_assert(std::size(SHAPE_NAMES) == (size_t)CSoundShape::NUM_SHAPES);
1810 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText),
1811 format: "Edit shape of sound source %d in layer %d of group %d to %s",
1812 SourceIndex, LayerIndex, GroupIndex, SHAPE_NAMES[Value]);
1813}
1814
1815void CEditorActionEditSoundSourceShape::Undo()
1816{
1817 std::shared_ptr<CLayerSounds> pLayerSounds = std::static_pointer_cast<CLayerSounds>(r: m_pLayer);
1818 CSoundSource *pSource = &pLayerSounds->m_vSources[m_SourceIndex];
1819
1820 pSource->m_Shape = m_SavedShape;
1821
1822 Map()->OnModify();
1823}
1824
1825void CEditorActionEditSoundSourceShape::Redo()
1826{
1827 std::shared_ptr<CLayerSounds> pLayerSounds = std::static_pointer_cast<CLayerSounds>(r: m_pLayer);
1828 CSoundSource *pSource = &pLayerSounds->m_vSources[m_SourceIndex];
1829
1830 pSource->m_Shape.m_Type = m_CurrentValue;
1831
1832 // set default values
1833 switch(pSource->m_Shape.m_Type)
1834 {
1835 case CSoundShape::SHAPE_CIRCLE:
1836 {
1837 pSource->m_Shape.m_Circle.m_Radius = 1000;
1838 break;
1839 }
1840 case CSoundShape::SHAPE_RECTANGLE:
1841 {
1842 pSource->m_Shape.m_Rectangle.m_Width = f2fx(v: 1000.0f);
1843 pSource->m_Shape.m_Rectangle.m_Height = f2fx(v: 800.0f);
1844 break;
1845 }
1846 }
1847
1848 Map()->OnModify();
1849}
1850
1851void CEditorActionEditSoundSourceShape::Save()
1852{
1853 std::shared_ptr<CLayerSounds> pLayerSounds = std::static_pointer_cast<CLayerSounds>(r: m_pLayer);
1854 m_SavedShape = pLayerSounds->m_vSources[m_SourceIndex].m_Shape;
1855}
1856
1857// -----
1858
1859CEditorActionEditSoundSourceProp::CEditorActionEditSoundSourceProp(CEditorMap *pMap, int GroupIndex, int LayerIndex, int SourceIndex, ESoundProp Prop, int Previous, int Current) :
1860 CEditorActionEditLayerPropBase(pMap, GroupIndex, LayerIndex, Prop, Previous, Current), m_SourceIndex(SourceIndex)
1861{
1862 static const char *s_apNames[] = {
1863 "pos X",
1864 "pos Y",
1865 "loop",
1866 "pan",
1867 "time delay",
1868 "falloff",
1869 "pos env",
1870 "pos env offset",
1871 "sound env",
1872 "sound env offset"};
1873 static_assert(std::size(s_apNames) == (size_t)ESoundProp::NUM_PROPS);
1874 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]);
1875}
1876
1877void CEditorActionEditSoundSourceProp::Undo()
1878{
1879 Apply(Value: m_Previous);
1880}
1881
1882void CEditorActionEditSoundSourceProp::Redo()
1883{
1884 Apply(Value: m_Current);
1885}
1886
1887void CEditorActionEditSoundSourceProp::Apply(int Value)
1888{
1889 std::shared_ptr<CLayerSounds> pLayerSounds = std::static_pointer_cast<CLayerSounds>(r: m_pLayer);
1890 CSoundSource *pSource = &pLayerSounds->m_vSources[m_SourceIndex];
1891
1892 if(m_Prop == ESoundProp::POS_X)
1893 {
1894 pSource->m_Position.x = Value;
1895 }
1896 else if(m_Prop == ESoundProp::POS_Y)
1897 {
1898 pSource->m_Position.y = Value;
1899 }
1900 else if(m_Prop == ESoundProp::LOOP)
1901 {
1902 pSource->m_Loop = Value;
1903 }
1904 else if(m_Prop == ESoundProp::PAN)
1905 {
1906 pSource->m_Pan = Value;
1907 }
1908 else if(m_Prop == ESoundProp::TIME_DELAY)
1909 {
1910 pSource->m_TimeDelay = Value;
1911 }
1912 else if(m_Prop == ESoundProp::FALLOFF)
1913 {
1914 pSource->m_Falloff = Value;
1915 }
1916 else if(m_Prop == ESoundProp::POS_ENV)
1917 {
1918 pSource->m_PosEnv = Value;
1919 }
1920 else if(m_Prop == ESoundProp::POS_ENV_OFFSET)
1921 {
1922 pSource->m_PosEnvOffset = Value;
1923 }
1924 else if(m_Prop == ESoundProp::SOUND_ENV)
1925 {
1926 pSource->m_SoundEnv = Value;
1927 }
1928 else if(m_Prop == ESoundProp::SOUND_ENV_OFFSET)
1929 {
1930 pSource->m_SoundEnvOffset = Value;
1931 }
1932
1933 Map()->OnModify();
1934}
1935
1936CEditorActionEditRectSoundSourceShapeProp::CEditorActionEditRectSoundSourceShapeProp(CEditorMap *pMap, int GroupIndex, int LayerIndex, int SourceIndex, ERectangleShapeProp Prop, int Previous, int Current) :
1937 CEditorActionEditLayerPropBase(pMap, GroupIndex, LayerIndex, Prop, Previous, Current), m_SourceIndex(SourceIndex)
1938{
1939 static const char *s_apNames[] = {
1940 "width",
1941 "height"};
1942 static_assert(std::size(s_apNames) == (size_t)ERectangleShapeProp::NUM_PROPS);
1943 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]);
1944}
1945
1946void CEditorActionEditRectSoundSourceShapeProp::Undo()
1947{
1948 Apply(Value: m_Previous);
1949}
1950
1951void CEditorActionEditRectSoundSourceShapeProp::Redo()
1952{
1953 Apply(Value: m_Current);
1954}
1955
1956void CEditorActionEditRectSoundSourceShapeProp::Apply(int Value)
1957{
1958 std::shared_ptr<CLayerSounds> pLayerSounds = std::static_pointer_cast<CLayerSounds>(r: m_pLayer);
1959 CSoundSource *pSource = &pLayerSounds->m_vSources[m_SourceIndex];
1960
1961 if(m_Prop == ERectangleShapeProp::RECTANGLE_WIDTH)
1962 {
1963 pSource->m_Shape.m_Rectangle.m_Width = Value;
1964 }
1965 else if(m_Prop == ERectangleShapeProp::RECTANGLE_HEIGHT)
1966 {
1967 pSource->m_Shape.m_Rectangle.m_Height = Value;
1968 }
1969
1970 Map()->OnModify();
1971}
1972
1973CEditorActionEditCircleSoundSourceShapeProp::CEditorActionEditCircleSoundSourceShapeProp(CEditorMap *pMap, int GroupIndex, int LayerIndex, int SourceIndex, ECircleShapeProp Prop, int Previous, int Current) :
1974 CEditorActionEditLayerPropBase(pMap, GroupIndex, LayerIndex, Prop, Previous, Current), m_SourceIndex(SourceIndex)
1975{
1976 static const char *s_apNames[] = {
1977 "radius"};
1978 static_assert(std::size(s_apNames) == (size_t)ECircleShapeProp::NUM_PROPS);
1979 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]);
1980}
1981
1982void CEditorActionEditCircleSoundSourceShapeProp::Undo()
1983{
1984 Apply(Value: m_Previous);
1985}
1986
1987void CEditorActionEditCircleSoundSourceShapeProp::Redo()
1988{
1989 Apply(Value: m_Current);
1990}
1991
1992void CEditorActionEditCircleSoundSourceShapeProp::Apply(int Value)
1993{
1994 std::shared_ptr<CLayerSounds> pLayerSounds = std::static_pointer_cast<CLayerSounds>(r: m_pLayer);
1995 CSoundSource *pSource = &pLayerSounds->m_vSources[m_SourceIndex];
1996
1997 if(m_Prop == ECircleShapeProp::CIRCLE_RADIUS)
1998 {
1999 pSource->m_Shape.m_Circle.m_Radius = Value;
2000 }
2001
2002 Map()->OnModify();
2003}
2004
2005// --------------------------
2006
2007CEditorActionNewEmptySound::CEditorActionNewEmptySound(CEditorMap *pMap, int GroupIndex, int LayerIndex, int x, int y) :
2008 CEditorActionLayerBase(pMap, GroupIndex, LayerIndex), m_X(x), m_Y(y)
2009{
2010 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "New sound in layer %d of group %d", LayerIndex, GroupIndex);
2011}
2012
2013void CEditorActionNewEmptySound::Undo()
2014{
2015 // Undo is simply deleting the added source
2016 std::shared_ptr<CLayerSounds> pLayerSounds = std::static_pointer_cast<CLayerSounds>(r: m_pLayer);
2017 pLayerSounds->m_vSources.pop_back();
2018
2019 Map()->OnModify();
2020}
2021
2022void CEditorActionNewEmptySound::Redo()
2023{
2024 std::shared_ptr<CLayerSounds> pLayerSounds = std::static_pointer_cast<CLayerSounds>(r: m_pLayer);
2025 pLayerSounds->NewSource(x: m_X, y: m_Y);
2026
2027 Map()->OnModify();
2028}
2029
2030CEditorActionNewEmptyQuad::CEditorActionNewEmptyQuad(CEditorMap *pMap, int GroupIndex, int LayerIndex, int x, int y) :
2031 CEditorActionLayerBase(pMap, GroupIndex, LayerIndex), m_X(x), m_Y(y)
2032{
2033 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "New quad in layer %d of group %d", LayerIndex, GroupIndex);
2034}
2035
2036void CEditorActionNewEmptyQuad::Undo()
2037{
2038 // Undo is simply deleting the added quad
2039 std::shared_ptr<CLayerQuads> pLayerQuads = std::static_pointer_cast<CLayerQuads>(r: m_pLayer);
2040 pLayerQuads->m_vQuads.pop_back();
2041
2042 Map()->OnModify();
2043}
2044
2045void CEditorActionNewEmptyQuad::Redo()
2046{
2047 std::shared_ptr<CLayerQuads> pLayerQuads = std::static_pointer_cast<CLayerQuads>(r: m_pLayer);
2048
2049 int Width = 64;
2050 int Height = 64;
2051 if(pLayerQuads->m_Image >= 0)
2052 {
2053 Width = Map()->m_vpImages[pLayerQuads->m_Image]->m_Width;
2054 Height = Map()->m_vpImages[pLayerQuads->m_Image]->m_Height;
2055 }
2056
2057 pLayerQuads->NewQuad(x: m_X, y: m_Y, Width, Height);
2058
2059 Map()->OnModify();
2060}
2061
2062// -------------
2063
2064CEditorActionNewQuad::CEditorActionNewQuad(CEditorMap *pMap, int GroupIndex, int LayerIndex) :
2065 CEditorActionLayerBase(pMap, GroupIndex, LayerIndex)
2066{
2067 std::shared_ptr<CLayerQuads> pLayerQuads = std::static_pointer_cast<CLayerQuads>(r: m_pLayer);
2068 m_Quad = pLayerQuads->m_vQuads[pLayerQuads->m_vQuads.size() - 1];
2069
2070 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "New quad in layer %d of group %d", LayerIndex, GroupIndex);
2071}
2072
2073void CEditorActionNewQuad::Undo()
2074{
2075 std::shared_ptr<CLayerQuads> pLayerQuads = std::static_pointer_cast<CLayerQuads>(r: m_pLayer);
2076 pLayerQuads->m_vQuads.pop_back();
2077}
2078
2079void CEditorActionNewQuad::Redo()
2080{
2081 std::shared_ptr<CLayerQuads> pLayerQuads = std::static_pointer_cast<CLayerQuads>(r: m_pLayer);
2082 pLayerQuads->m_vQuads.emplace_back(args&: m_Quad);
2083}
2084
2085// --------------
2086
2087CEditorActionMoveSoundSource::CEditorActionMoveSoundSource(CEditorMap *pMap, int GroupIndex, int LayerIndex, int SourceIndex, CPoint OriginalPosition, CPoint CurrentPosition) :
2088 CEditorActionLayerBase(pMap, GroupIndex, LayerIndex), m_SourceIndex(SourceIndex), m_OriginalPosition(OriginalPosition), m_CurrentPosition(CurrentPosition)
2089{
2090 str_format(buffer: m_aDisplayText, buffer_size: sizeof(m_aDisplayText), format: "Move sound source %d of layer %d in group %d", SourceIndex, LayerIndex, GroupIndex);
2091}
2092
2093void CEditorActionMoveSoundSource::Undo()
2094{
2095 dbg_assert(m_pLayer->m_Type == LAYERTYPE_SOUNDS, "Layer type does not match a sound layer");
2096 std::static_pointer_cast<CLayerSounds>(r: m_pLayer)->m_vSources[m_SourceIndex].m_Position = m_OriginalPosition;
2097}
2098
2099void CEditorActionMoveSoundSource::Redo()
2100{
2101 dbg_assert(m_pLayer->m_Type == LAYERTYPE_SOUNDS, "Layer type does not match a sound layer");
2102 std::static_pointer_cast<CLayerSounds>(r: m_pLayer)->m_vSources[m_SourceIndex].m_Position = m_CurrentPosition;
2103}
2104