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