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