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