/
dieoska
/
ddnet
Обзор
Документация
Войти
/
dieoska
/
ddnet
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/game/editor/editor_actions.cpp
2 146 строк
68 KB
Robert Müller
Consistently name editor variables `m_PointIndex`/`m_EnvelopeIndex`
14 дек 2025, 19:41
14 дек 2025, 19:41
98697d1
Код
Авторство
О чём код?
#include "editor_actions.h" #include <game/editor/editor.h> #include <game/editor/mapitems.h> #include <game/editor/mapitems/image.h> #include <game/editor/mapitems/layer.h> #include <game/editor/mapitems/layer_front.h> #include <game/editor/mapitems/layer_group.h> #include <game/editor/mapitems/layer_quads.h> #include <game/editor/mapitems/layer_sounds.h> #include <game/editor/mapitems/map.h> CEditorBrushDrawAction::CEditorBrushDrawAction(CEditorMap *pMap, int Group) : IEditorAction(pMap), m_Group(Group) { for(size_t k = 0; k < Map()->m_vpGroups[Group]->m_vpLayers.size(); k++) { auto pLayer = Map()->m_vpGroups[Group]->m_vpLayers[k]; if(pLayer->m_Type == LAYERTYPE_TILES) { auto pLayerTiles = std::static_pointer_cast<CLayerTiles>(pLayer); if(pLayer == Map()->m_pTeleLayer) { if(!Map()->m_pTeleLayer->m_History.empty()) { m_TeleTileChanges = std::map(Map()->m_pTeleLayer->m_History); Map()->m_pTeleLayer->ClearHistory(); } } else if(pLayer == Map()->m_pTuneLayer) { if(!Map()->m_pTuneLayer->m_History.empty()) { m_TuneTileChanges = std::map(Map()->m_pTuneLayer->m_History); Map()->m_pTuneLayer->ClearHistory(); } } else if(pLayer == Map()->m_pSwitchLayer) { if(!Map()->m_pSwitchLayer->m_History.empty()) { m_SwitchTileChanges = std::map(Map()->m_pSwitchLayer->m_History); Map()->m_pSwitchLayer->ClearHistory(); } } else if(pLayer == Map()->m_pSpeedupLayer) { if(!Map()->m_pSpeedupLayer->m_History.empty()) { m_SpeedupTileChanges = std::map(Map()->m_pSpeedupLayer->m_History); Map()->m_pSpeedupLayer->ClearHistory(); } } if(!pLayerTiles->m_TilesHistory.empty()) { m_vTileChanges.emplace_back(k, std::map(pLayerTiles->m_TilesHistory)); pLayerTiles->ClearHistory(); } } } SetInfos(); str_format(m_aDisplayText, sizeof(m_aDisplayText), "Brush draw (x%d) on %d layers", m_TotalTilesDrawn, m_TotalLayers); } void CEditorBrushDrawAction::SetInfos() { m_TotalTilesDrawn = 0; m_TotalLayers = 0; // Process normal tiles for(auto const &Pair : m_vTileChanges) { int Layer = Pair.first; std::shared_ptr<CLayer> pLayer = Map()->m_vpGroups[m_Group]->m_vpLayers[Layer]; m_TotalLayers++; if(pLayer->m_Type == LAYERTYPE_TILES) { std::shared_ptr<CLayerTiles> pLayerTiles = std::static_pointer_cast<CLayerTiles>(pLayer); auto Changes = Pair.second; for(auto &Change : Changes) { m_TotalTilesDrawn += Change.second.size(); } } } // Process speedup tiles for(auto const &SpeedupChange : m_SpeedupTileChanges) { m_TotalTilesDrawn += SpeedupChange.second.size(); } // Process tele tiles for(auto const &TeleChange : m_TeleTileChanges) { m_TotalTilesDrawn += TeleChange.second.size(); } // Process switch tiles for(auto const &SwitchChange : m_SwitchTileChanges) { m_TotalTilesDrawn += SwitchChange.second.size(); } // Process tune tiles for(auto const &TuneChange : m_TuneTileChanges) { m_TotalTilesDrawn += TuneChange.second.size(); } m_TotalLayers += !m_SpeedupTileChanges.empty(); m_TotalLayers += !m_SwitchTileChanges.empty(); m_TotalLayers += !m_TeleTileChanges.empty(); m_TotalLayers += !m_TuneTileChanges.empty(); } bool CEditorBrushDrawAction::IsEmpty() { return m_vTileChanges.empty() && m_SpeedupTileChanges.empty() && m_SwitchTileChanges.empty() && m_TeleTileChanges.empty() && m_TuneTileChanges.empty(); } void CEditorBrushDrawAction::Undo() { Apply(true); } void CEditorBrushDrawAction::Redo() { Apply(false); } void CEditorBrushDrawAction::Apply(bool Undo) { // Process normal tiles for(auto const &Pair : m_vTileChanges) { int Layer = Pair.first; std::shared_ptr<CLayer> pLayer = Map()->m_vpGroups[m_Group]->m_vpLayers[Layer]; if(pLayer->m_Type == LAYERTYPE_TILES) { std::shared_ptr<CLayerTiles> pLayerTiles = std::static_pointer_cast<CLayerTiles>(pLayer); auto Changes = Pair.second; for(auto &Change : Changes) { int y = Change.first; auto Line = Change.second; for(auto &Tile : Line) { int x = Tile.first; STileStateChange State = Tile.second; pLayerTiles->SetTileIgnoreHistory(x, y, Undo ? State.m_Previous : State.m_Current); } } } } // Process speedup tiles for(auto const &SpeedupChange : m_SpeedupTileChanges) { int y = SpeedupChange.first; auto Line = SpeedupChange.second; for(auto &Tile : Line) { int x = Tile.first; int Index = y * Map()->m_pSpeedupLayer->m_Width + x; SSpeedupTileStateChange State = Tile.second; SSpeedupTileStateChange::SData Data = Undo ? State.m_Previous : State.m_Current; Map()->m_pSpeedupLayer->m_pSpeedupTile[Index].m_Force = Data.m_Force; Map()->m_pSpeedupLayer->m_pSpeedupTile[Index].m_MaxSpeed = Data.m_MaxSpeed; Map()->m_pSpeedupLayer->m_pSpeedupTile[Index].m_Angle = Data.m_Angle; Map()->m_pSpeedupLayer->m_pSpeedupTile[Index].m_Type = Data.m_Type; Map()->m_pSpeedupLayer->m_pTiles[Index].m_Index = Data.m_Index; } } // Process tele tiles for(auto const &TeleChange : m_TeleTileChanges) { int y = TeleChange.first; auto Line = TeleChange.second; for(auto &Tile : Line) { int x = Tile.first; int Index = y * Map()->m_pTeleLayer->m_Width + x; STeleTileStateChange State = Tile.second; STeleTileStateChange::SData Data = Undo ? State.m_Previous : State.m_Current; Map()->m_pTeleLayer->m_pTeleTile[Index].m_Number = Data.m_Number; Map()->m_pTeleLayer->m_pTeleTile[Index].m_Type = Data.m_Type; Map()->m_pTeleLayer->m_pTiles[Index].m_Index = Data.m_Index; } } // Process switch tiles for(auto const &SwitchChange : m_SwitchTileChanges) { int y = SwitchChange.first; auto Line = SwitchChange.second; for(auto &Tile : Line) { int x = Tile.first; int Index = y * Map()->m_pSwitchLayer->m_Width + x; SSwitchTileStateChange State = Tile.second; SSwitchTileStateChange::SData Data = Undo ? State.m_Previous : State.m_Current; Map()->m_pSwitchLayer->m_pSwitchTile[Index].m_Number = Data.m_Number; Map()->m_pSwitchLayer->m_pSwitchTile[Index].m_Type = Data.m_Type; Map()->m_pSwitchLayer->m_pSwitchTile[Index].m_Flags = Data.m_Flags; Map()->m_pSwitchLayer->m_pSwitchTile[Index].m_Delay = Data.m_Delay; Map()->m_pSwitchLayer->m_pTiles[Index].m_Index = Data.m_Index; } } // Process tune tiles for(auto const &TuneChange : m_TuneTileChanges) { int y = TuneChange.first; auto Line = TuneChange.second; for(auto &Tile : Line) { int x = Tile.first; int Index = y * Map()->m_pTuneLayer->m_Width + x; STuneTileStateChange State = Tile.second; STuneTileStateChange::SData Data = Undo ? State.m_Previous : State.m_Current; Map()->m_pTuneLayer->m_pTuneTile[Index].m_Number = Data.m_Number; Map()->m_pTuneLayer->m_pTuneTile[Index].m_Type = Data.m_Type; Map()->m_pTuneLayer->m_pTiles[Index].m_Index = Data.m_Index; } } } // ------------------------------------------- CEditorActionQuadPlace::CEditorActionQuadPlace(CEditorMap *pMap, int GroupIndex, int LayerIndex, std::vector<CQuad> &vBrush) : CEditorActionLayerBase(pMap, GroupIndex, LayerIndex), m_vBrush(vBrush) { str_format(m_aDisplayText, sizeof(m_aDisplayText), "Quad place (x%d)", (int)m_vBrush.size()); } void CEditorActionQuadPlace::Undo() { std::shared_ptr<CLayerQuads> pLayerQuads = std::static_pointer_cast<CLayerQuads>(m_pLayer); for(size_t k = 0; k < m_vBrush.size(); k++) pLayerQuads->m_vQuads.pop_back(); Map()->OnModify(); } void CEditorActionQuadPlace::Redo() { std::shared_ptr<CLayerQuads> pLayerQuads = std::static_pointer_cast<CLayerQuads>(m_pLayer); for(auto &Brush : m_vBrush) pLayerQuads->m_vQuads.push_back(Brush); Map()->OnModify(); } CEditorActionSoundPlace::CEditorActionSoundPlace(CEditorMap *pMap, int GroupIndex, int LayerIndex, std::vector<CSoundSource> &vBrush) : CEditorActionLayerBase(pMap, GroupIndex, LayerIndex), m_vBrush(vBrush) { str_format(m_aDisplayText, sizeof(m_aDisplayText), "Sound place (x%d)", (int)m_vBrush.size()); } void CEditorActionSoundPlace::Undo() { std::shared_ptr<CLayerSounds> pLayerSounds = std::static_pointer_cast<CLayerSounds>(m_pLayer); for(size_t k = 0; k < m_vBrush.size(); k++) pLayerSounds->m_vSources.pop_back(); Map()->OnModify(); } void CEditorActionSoundPlace::Redo() { std::shared_ptr<CLayerSounds> pLayerSounds = std::static_pointer_cast<CLayerSounds>(m_pLayer); for(auto &Brush : m_vBrush) pLayerSounds->m_vSources.push_back(Brush); Map()->OnModify(); } // --------------------------------------------------------------------------------------- CEditorActionDeleteQuad::CEditorActionDeleteQuad(CEditorMap *pMap, int GroupIndex, int LayerIndex, std::vector<int> const &vQuadsIndices, std::vector<CQuad> const &vDeletedQuads) : CEditorActionLayerBase(pMap, GroupIndex, LayerIndex), m_vQuadsIndices(vQuadsIndices), m_vDeletedQuads(vDeletedQuads) { str_format(m_aDisplayText, sizeof(m_aDisplayText), "Delete quad (x%d)", (int)m_vDeletedQuads.size()); } void CEditorActionDeleteQuad::Undo() { std::shared_ptr<CLayerQuads> pLayerQuads = std::static_pointer_cast<CLayerQuads>(m_pLayer); for(size_t k = 0; k < m_vQuadsIndices.size(); k++) { pLayerQuads->m_vQuads.insert(pLayerQuads->m_vQuads.begin() + m_vQuadsIndices[k], m_vDeletedQuads[k]); } } void CEditorActionDeleteQuad::Redo() { std::shared_ptr<CLayerQuads> pLayerQuads = std::static_pointer_cast<CLayerQuads>(m_pLayer); std::vector<int> vQuads(m_vQuadsIndices); for(int i = 0; i < (int)vQuads.size(); ++i) { pLayerQuads->m_vQuads.erase(pLayerQuads->m_vQuads.begin() + vQuads[i]); for(int j = i + 1; j < (int)vQuads.size(); ++j) if(vQuads[j] > vQuads[i]) vQuads[j]--; vQuads.erase(vQuads.begin() + i); i--; } } // --------------------------------------------------------------------------------------- CEditorActionEditQuadPoint::CEditorActionEditQuadPoint(CEditorMap *pMap, int GroupIndex, int LayerIndex, int QuadIndex, std::vector<CPoint> const &vPreviousPoints, std::vector<CPoint> const &vCurrentPoints) : CEditorActionLayerBase(pMap, GroupIndex, LayerIndex), m_QuadIndex(QuadIndex), m_vPreviousPoints(vPreviousPoints), m_vCurrentPoints(vCurrentPoints) { str_copy(m_aDisplayText, "Edit quad points"); } void CEditorActionEditQuadPoint::Undo() { Apply(m_vPreviousPoints); } void CEditorActionEditQuadPoint::Redo() { Apply(m_vCurrentPoints); } void CEditorActionEditQuadPoint::Apply(const std::vector<CPoint> &vValue) { std::shared_ptr<CLayerQuads> pLayerQuads = std::static_pointer_cast<CLayerQuads>(m_pLayer); CQuad &Quad = pLayerQuads->m_vQuads[m_QuadIndex]; dbg_assert(std::size(Quad.m_aPoints) == vValue.size(), "Expected %d values, got %d", (int)std::size(Quad.m_aPoints), (int)vValue.size()); std::copy_n(vValue.begin(), std::size(Quad.m_aPoints), Quad.m_aPoints); } CEditorActionEditQuadColor::CEditorActionEditQuadColor(CEditorMap *pMap, int GroupIndex, int LayerIndex, int QuadIndex, std::vector<CColor> const &vPreviousColors, std::vector<CColor> const &vCurrentColors) : CEditorActionLayerBase(pMap, GroupIndex, LayerIndex), m_QuadIndex(QuadIndex), m_vPreviousColors(vPreviousColors), m_vCurrentColors(vCurrentColors) { str_copy(m_aDisplayText, "Edit quad point colors"); } void CEditorActionEditQuadColor::Undo() { Apply(m_vPreviousColors); } void CEditorActionEditQuadColor::Redo() { Apply(m_vCurrentColors); } void CEditorActionEditQuadColor::Apply(std::vector<CColor> &vValue) { std::shared_ptr<CLayerQuads> pLayerQuads = std::static_pointer_cast<CLayerQuads>(m_pLayer); CQuad &Quad = pLayerQuads->m_vQuads[m_QuadIndex]; dbg_assert(std::size(Quad.m_aColors) == vValue.size(), "Expected %d values, got %d", (int)std::size(Quad.m_aColors), (int)vValue.size()); std::copy_n(vValue.begin(), std::size(Quad.m_aColors), Quad.m_aColors); } CEditorActionEditQuadProp::CEditorActionEditQuadProp(CEditorMap *pMap, int GroupIndex, int LayerIndex, int QuadIndex, EQuadProp Prop, int Previous, int Current) : CEditorActionLayerBase(pMap, GroupIndex, LayerIndex), m_QuadIndex(QuadIndex), m_Prop(Prop), m_Previous(Previous), m_Current(Current) { static const char *s_apNames[] = { "order", "pos X", "pos Y", "pos env", "pos env offset", nullptr, // color "color env", "color env offset"}; static_assert(std::size(s_apNames) == (size_t)EQuadProp::NUM_PROPS); dbg_assert(Prop != EQuadProp::PROP_COLOR, "Color prop implemented by CEditorActionEditQuadPointProp"); str_format(m_aDisplayText, sizeof(m_aDisplayText), "Edit quad %s property in layer %d of group %d", s_apNames[(int)m_Prop], m_LayerIndex, m_GroupIndex); } void CEditorActionEditQuadProp::Undo() { Apply(m_Previous); } void CEditorActionEditQuadProp::Redo() { Apply(m_Current); } void CEditorActionEditQuadProp::Apply(int Value) { std::shared_ptr<CLayerQuads> pLayerQuads = std::static_pointer_cast<CLayerQuads>(m_pLayer); CQuad &Quad = pLayerQuads->m_vQuads[m_QuadIndex]; if(m_Prop == EQuadProp::PROP_POS_ENV) Quad.m_PosEnv = Value; else if(m_Prop == EQuadProp::PROP_POS_ENV_OFFSET) Quad.m_PosEnvOffset = Value; else if(m_Prop == EQuadProp::PROP_COLOR_ENV) Quad.m_ColorEnv = Value; else if(m_Prop == EQuadProp::PROP_COLOR_ENV_OFFSET) Quad.m_ColorEnvOffset = Value; } CEditorActionEditQuadPointProp::CEditorActionEditQuadPointProp(CEditorMap *pMap, int GroupIndex, int LayerIndex, int QuadIndex, int PointIndex, EQuadPointProp Prop, int Previous, int Current) : CEditorActionLayerBase(pMap, GroupIndex, LayerIndex), m_QuadIndex(QuadIndex), m_PointIndex(PointIndex), m_Prop(Prop), m_Previous(Previous), m_Current(Current) { static const char *s_apNames[] = { "pos X", "pos Y", "color", "tex U", "tex V"}; static_assert(std::size(s_apNames) == (size_t)EQuadPointProp::NUM_PROPS); str_format(m_aDisplayText, sizeof(m_aDisplayText), "Edit quad point %s property in layer %d of group %d", s_apNames[(int)m_Prop], m_LayerIndex, m_GroupIndex); } void CEditorActionEditQuadPointProp::Undo() { Apply(m_Previous); } void CEditorActionEditQuadPointProp::Redo() { Apply(m_Current); } void CEditorActionEditQuadPointProp::Apply(int Value) { std::shared_ptr<CLayerQuads> pLayerQuads = std::static_pointer_cast<CLayerQuads>(m_pLayer); CQuad &Quad = pLayerQuads->m_vQuads[m_QuadIndex]; if(m_Prop == EQuadPointProp::PROP_COLOR) { const ColorRGBA ColorPick = ColorRGBA::UnpackAlphaLast<ColorRGBA>(Value); Quad.m_aColors[m_PointIndex].r = ColorPick.r * 255.0f; Quad.m_aColors[m_PointIndex].g = ColorPick.g * 255.0f; Quad.m_aColors[m_PointIndex].b = ColorPick.b * 255.0f; Quad.m_aColors[m_PointIndex].a = ColorPick.a * 255.0f; Editor()->m_ColorPickerPopupContext.m_RgbaColor = ColorPick; Editor()->m_ColorPickerPopupContext.m_HslaColor = color_cast<ColorHSLA>(ColorPick); Editor()->m_ColorPickerPopupContext.m_HsvaColor = color_cast<ColorHSVA>(Editor()->m_ColorPickerPopupContext.m_HslaColor); } else if(m_Prop == EQuadPointProp::PROP_TEX_U) { Quad.m_aTexcoords[m_PointIndex].x = Value; } else if(m_Prop == EQuadPointProp::PROP_TEX_V) { Quad.m_aTexcoords[m_PointIndex].y = Value; } } // --------------------------------------------------------------------------------------- CEditorActionBulk::CEditorActionBulk(CEditorMap *pMap, const std::vector<std::shared_ptr<IEditorAction>> &vpActions, const char *pDisplay, bool Reverse) : IEditorAction(pMap), m_vpActions(vpActions), m_Reverse(Reverse) { // Assuming we only use bulk for actions of same type, if no display was provided if(!pDisplay) { const char *pBaseDisplay = m_vpActions[0]->DisplayText(); if(m_vpActions.size() == 1) str_copy(m_aDisplayText, pBaseDisplay); else str_format(m_aDisplayText, sizeof(m_aDisplayText), "%s (x%d)", pBaseDisplay, (int)m_vpActions.size()); } else { str_copy(m_aDisplayText, pDisplay); } } void CEditorActionBulk::Undo() { if(m_Reverse) { // reverse_view is not supported in gcc 10 for(auto pIt = m_vpActions.rbegin(); pIt != m_vpActions.rend(); pIt++) // NOLINT: modernize-loop-convert { auto &pAction = *pIt; pAction->Undo(); } } else { for(auto &pAction : m_vpActions) { pAction->Undo(); } } } void CEditorActionBulk::Redo() { for(auto &pAction : m_vpActions) { pAction->Redo(); } } // --------- CEditorActionTileChanges::CEditorActionTileChanges(CEditorMap *pMap, int GroupIndex, int LayerIndex, const char *pAction, const EditorTileStateChangeHistory<STileStateChange> &Changes) : CEditorActionLayerBase(pMap, GroupIndex, LayerIndex), m_Changes(Changes) { ComputeInfos(); str_format(m_aDisplayText, sizeof(m_aDisplayText), "%s (x%d)", pAction, m_TotalChanges); } void CEditorActionTileChanges::Undo() { Apply(true); } void CEditorActionTileChanges::Redo() { Apply(false); } void CEditorActionTileChanges::Apply(bool Undo) { std::shared_ptr<CLayerTiles> pLayerTiles = std::static_pointer_cast<CLayerTiles>(m_pLayer); for(auto &Change : m_Changes) { int y = Change.first; auto Line = Change.second; for(auto &Tile : Line) { int x = Tile.first; STileStateChange State = Tile.second; pLayerTiles->SetTileIgnoreHistory(x, y, Undo ? State.m_Previous : State.m_Current); } } Map()->OnModify(); } void CEditorActionTileChanges::ComputeInfos() { m_TotalChanges = 0; for(auto &Line : m_Changes) m_TotalChanges += Line.second.size(); } // --------- CEditorActionLayerBase::CEditorActionLayerBase(CEditorMap *pMap, int GroupIndex, int LayerIndex) : IEditorAction(pMap), m_GroupIndex(GroupIndex), m_LayerIndex(LayerIndex) { m_pLayer = Map()->m_vpGroups[GroupIndex]->m_vpLayers[LayerIndex]; } // ---------- CEditorActionAddLayer::CEditorActionAddLayer(CEditorMap *pMap, int GroupIndex, int LayerIndex, bool Duplicate) : CEditorActionLayerBase(pMap, GroupIndex, LayerIndex), m_Duplicate(Duplicate) { str_format(m_aDisplayText, sizeof(m_aDisplayText), "%s %s layer in group %d", m_Duplicate ? "Duplicate" : "New", m_pLayer->TypeName(), m_GroupIndex); } void CEditorActionAddLayer::Undo() { // Undo: remove layer from vector but keep it in case we want to add it back auto &vLayers = Map()->m_vpGroups[m_GroupIndex]->m_vpLayers; if(m_pLayer->m_Type == LAYERTYPE_TILES) { std::shared_ptr<CLayerTiles> pLayerTiles = std::static_pointer_cast<CLayerTiles>(m_pLayer); if(pLayerTiles->m_HasFront) Map()->m_pFrontLayer = nullptr; else if(pLayerTiles->m_HasTele) Map()->m_pTeleLayer = nullptr; else if(pLayerTiles->m_HasSpeedup) Map()->m_pSpeedupLayer = nullptr; else if(pLayerTiles->m_HasSwitch) Map()->m_pSwitchLayer = nullptr; else if(pLayerTiles->m_HasTune) Map()->m_pTuneLayer = nullptr; } vLayers.erase(vLayers.begin() + m_LayerIndex); Map()->m_vpGroups[m_GroupIndex]->m_Collapse = false; if(m_LayerIndex >= (int)vLayers.size()) Editor()->SelectLayer(vLayers.size() - 1, m_GroupIndex); Map()->OnModify(); } void CEditorActionAddLayer::Redo() { // Redo: add back the removed layer contained in this class auto &vLayers = Map()->m_vpGroups[m_GroupIndex]->m_vpLayers; if(m_pLayer->m_Type == LAYERTYPE_TILES) { std::shared_ptr<CLayerTiles> pLayerTiles = std::static_pointer_cast<CLayerTiles>(m_pLayer); if(pLayerTiles->m_HasFront) Map()->m_pFrontLayer = std::static_pointer_cast<CLayerFront>(m_pLayer); else if(pLayerTiles->m_HasTele) Map()->m_pTeleLayer = std::static_pointer_cast<CLayerTele>(m_pLayer); else if(pLayerTiles->m_HasSpeedup) Map()->m_pSpeedupLayer = std::static_pointer_cast<CLayerSpeedup>(m_pLayer); else if(pLayerTiles->m_HasSwitch) Map()->m_pSwitchLayer = std::static_pointer_cast<CLayerSwitch>(m_pLayer); else if(pLayerTiles->m_HasTune) Map()->m_pTuneLayer = std::static_pointer_cast<CLayerTune>(m_pLayer); } vLayers.insert(vLayers.begin() + m_LayerIndex, m_pLayer); Map()->m_vpGroups[m_GroupIndex]->m_Collapse = false; Editor()->SelectLayer(m_LayerIndex, m_GroupIndex); Map()->OnModify(); } CEditorActionDeleteLayer::CEditorActionDeleteLayer(CEditorMap *pMap, int GroupIndex, int LayerIndex) : CEditorActionLayerBase(pMap, GroupIndex, LayerIndex) { str_format(m_aDisplayText, sizeof(m_aDisplayText), "Delete %s layer of group %d", m_pLayer->TypeName(), m_GroupIndex); } void CEditorActionDeleteLayer::Redo() { // Redo: remove layer from vector but keep it in case we want to add it back auto &vLayers = Map()->m_vpGroups[m_GroupIndex]->m_vpLayers; if(m_pLayer->m_Type == LAYERTYPE_TILES) { std::shared_ptr<CLayerTiles> pLayerTiles = std::static_pointer_cast<CLayerTiles>(m_pLayer); if(pLayerTiles->m_HasFront) Map()->m_pFrontLayer = nullptr; else if(pLayerTiles->m_HasTele) Map()->m_pTeleLayer = nullptr; else if(pLayerTiles->m_HasSpeedup) Map()->m_pSpeedupLayer = nullptr; else if(pLayerTiles->m_HasSwitch) Map()->m_pSwitchLayer = nullptr; else if(pLayerTiles->m_HasTune) Map()->m_pTuneLayer = nullptr; } Map()->m_vpGroups[m_GroupIndex]->DeleteLayer(m_LayerIndex); Map()->m_vpGroups[m_GroupIndex]->m_Collapse = false; if(m_LayerIndex >= (int)vLayers.size()) Editor()->SelectLayer(vLayers.size() - 1, m_GroupIndex); Map()->OnModify(); } void CEditorActionDeleteLayer::Undo() { // Undo: add back the removed layer contained in this class auto &vLayers = Map()->m_vpGroups[m_GroupIndex]->m_vpLayers; if(m_pLayer->m_Type == LAYERTYPE_TILES) { std::shared_ptr<CLayerTiles> pLayerTiles = std::static_pointer_cast<CLayerTiles>(m_pLayer); if(pLayerTiles->m_HasFront) Map()->m_pFrontLayer = std::static_pointer_cast<CLayerFront>(m_pLayer); else if(pLayerTiles->m_HasTele) Map()->m_pTeleLayer = std::static_pointer_cast<CLayerTele>(m_pLayer); else if(pLayerTiles->m_HasSpeedup) Map()->m_pSpeedupLayer = std::static_pointer_cast<CLayerSpeedup>(m_pLayer); else if(pLayerTiles->m_HasSwitch) Map()->m_pSwitchLayer = std::static_pointer_cast<CLayerSwitch>(m_pLayer); else if(pLayerTiles->m_HasTune) Map()->m_pTuneLayer = std::static_pointer_cast<CLayerTune>(m_pLayer); } vLayers.insert(vLayers.begin() + m_LayerIndex, m_pLayer); Map()->m_vpGroups[m_GroupIndex]->m_Collapse = false; Editor()->SelectLayer(m_LayerIndex, m_GroupIndex); Map()->OnModify(); } CEditorActionGroup::CEditorActionGroup(CEditorMap *pMap, int GroupIndex, bool Delete) : IEditorAction(pMap), m_GroupIndex(GroupIndex), m_Delete(Delete) { m_pGroup = Map()->m_vpGroups[GroupIndex]; if(m_Delete) str_format(m_aDisplayText, sizeof(m_aDisplayText), "Delete group %d", m_GroupIndex); else str_copy(m_aDisplayText, "New group", sizeof(m_aDisplayText)); } void CEditorActionGroup::Undo() { if(m_Delete) { // Undo: add back the group Map()->m_vpGroups.insert(Map()->m_vpGroups.begin() + m_GroupIndex, m_pGroup); Editor()->m_SelectedGroup = m_GroupIndex; Map()->OnModify(); } else { // Undo: delete the group Map()->DeleteGroup(m_GroupIndex); Editor()->m_SelectedGroup = maximum(0, m_GroupIndex - 1); } Map()->OnModify(); } void CEditorActionGroup::Redo() { if(!m_Delete) { // Redo: add back the group Map()->m_vpGroups.insert(Map()->m_vpGroups.begin() + m_GroupIndex, m_pGroup); Editor()->m_SelectedGroup = m_GroupIndex; } else { // Redo: delete the group Map()->DeleteGroup(m_GroupIndex); Editor()->m_SelectedGroup = maximum(0, m_GroupIndex - 1); } Map()->OnModify(); } CEditorActionEditGroupProp::CEditorActionEditGroupProp(CEditorMap *pMap, int GroupIndex, EGroupProp Prop, int Previous, int Current) : IEditorAction(pMap), m_GroupIndex(GroupIndex), m_Prop(Prop), m_Previous(Previous), m_Current(Current) { static const char *s_apNames[] = { "order", "pos X", "pos Y", "para X", "para Y", "use clipping", "clip X", "clip Y", "clip W", "clip H"}; static_assert(std::size(s_apNames) == (size_t)EGroupProp::NUM_PROPS); str_format(m_aDisplayText, sizeof(m_aDisplayText), "Edit group %d %s property", m_GroupIndex, s_apNames[(int)Prop]); } void CEditorActionEditGroupProp::Undo() { auto pGroup = Map()->m_vpGroups[m_GroupIndex]; if(m_Prop == EGroupProp::PROP_ORDER) { Editor()->m_SelectedGroup = Map()->MoveGroup(m_Current, m_Previous); } else Apply(m_Previous); } void CEditorActionEditGroupProp::Redo() { auto pGroup = Map()->m_vpGroups[m_GroupIndex]; if(m_Prop == EGroupProp::PROP_ORDER) { Editor()->m_SelectedGroup = Map()->MoveGroup(m_Previous, m_Current); } else Apply(m_Current); } void CEditorActionEditGroupProp::Apply(int Value) { auto pGroup = Map()->m_vpGroups[m_GroupIndex]; if(m_Prop == EGroupProp::PROP_POS_X) pGroup->m_OffsetX = Value; if(m_Prop == EGroupProp::PROP_POS_Y) pGroup->m_OffsetY = Value; if(m_Prop == EGroupProp::PROP_PARA_X) pGroup->m_ParallaxX = Value; if(m_Prop == EGroupProp::PROP_PARA_Y) pGroup->m_ParallaxY = Value; if(m_Prop == EGroupProp::PROP_USE_CLIPPING) pGroup->m_UseClipping = Value; if(m_Prop == EGroupProp::PROP_CLIP_X) pGroup->m_ClipX = Value; if(m_Prop == EGroupProp::PROP_CLIP_Y) pGroup->m_ClipY = Value; if(m_Prop == EGroupProp::PROP_CLIP_W) pGroup->m_ClipW = Value; if(m_Prop == EGroupProp::PROP_CLIP_H) pGroup->m_ClipH = Value; Map()->OnModify(); } template<typename E> CEditorActionEditLayerPropBase<E>::CEditorActionEditLayerPropBase(CEditorMap *pMap, int GroupIndex, int LayerIndex, E Prop, int Previous, int Current) : CEditorActionLayerBase(pMap, GroupIndex, LayerIndex), m_Prop(Prop), m_Previous(Previous), m_Current(Current) { } CEditorActionEditLayerProp::CEditorActionEditLayerProp(CEditorMap *pMap, int GroupIndex, int LayerIndex, ELayerProp Prop, int Previous, int Current) : CEditorActionEditLayerPropBase(pMap, GroupIndex, LayerIndex, Prop, Previous, Current) { static const char *s_apNames[] = { "group", "order", "HQ"}; static_assert(std::size(s_apNames) == (size_t)ELayerProp::NUM_PROPS); str_format(m_aDisplayText, sizeof(m_aDisplayText), "Edit layer %d in group %d %s property", m_LayerIndex, m_GroupIndex, s_apNames[(int)m_Prop]); } void CEditorActionEditLayerProp::Undo() { auto pCurrentGroup = Map()->m_vpGroups[m_GroupIndex]; if(m_Prop == ELayerProp::PROP_ORDER) { Editor()->SelectLayer(pCurrentGroup->MoveLayer(m_Current, m_Previous)); } else Apply(m_Previous); } void CEditorActionEditLayerProp::Redo() { auto pCurrentGroup = Map()->m_vpGroups[m_GroupIndex]; if(m_Prop == ELayerProp::PROP_ORDER) { Editor()->SelectLayer(pCurrentGroup->MoveLayer(m_Previous, m_Current)); } else Apply(m_Current); } void CEditorActionEditLayerProp::Apply(int Value) { if(m_Prop == ELayerProp::PROP_GROUP) { auto pCurrentGroup = Map()->m_vpGroups[Value == m_Previous ? m_Current : m_Previous]; auto pPreviousGroup = Map()->m_vpGroups[Value]; pCurrentGroup->m_vpLayers.erase(pCurrentGroup->m_vpLayers.begin() + pCurrentGroup->m_vpLayers.size() - 1); if(Value == m_Previous) pPreviousGroup->m_vpLayers.insert(pPreviousGroup->m_vpLayers.begin() + m_LayerIndex, m_pLayer); else pPreviousGroup->m_vpLayers.push_back(m_pLayer); Editor()->m_SelectedGroup = Value; Editor()->SelectLayer(m_LayerIndex); } else if(m_Prop == ELayerProp::PROP_HQ) { m_pLayer->m_Flags = Value; } Map()->OnModify(); } CEditorActionEditLayerTilesProp::CEditorActionEditLayerTilesProp(CEditorMap *pMap, int GroupIndex, int LayerIndex, ETilesProp Prop, int Previous, int Current) : CEditorActionEditLayerPropBase(pMap, GroupIndex, LayerIndex, Prop, Previous, Current) { static const char *s_apNames[] = { "width", "height", "shift", "shift by", "image", "color", "color env", "color env offset", "automapper", "automapper reference", "live gametiles", "seed"}; static_assert(std::size(s_apNames) == (size_t)ETilesProp::NUM_PROPS); str_format(m_aDisplayText, sizeof(m_aDisplayText), "Edit tiles layer %d in group %d %s property", m_LayerIndex, m_GroupIndex, s_apNames[(int)Prop]); } void CEditorActionEditLayerTilesProp::SetSavedLayers(const std::map<int, std::shared_ptr<CLayer>> &SavedLayers) { m_SavedLayers = std::map(SavedLayers); } void CEditorActionEditLayerTilesProp::Undo() { std::shared_ptr<CLayerTiles> pLayerTiles = std::static_pointer_cast<CLayerTiles>(m_pLayer); std::shared_ptr<CLayerTiles> pSavedLayerTiles = nullptr; if(m_Prop == ETilesProp::PROP_WIDTH || m_Prop == ETilesProp::PROP_HEIGHT) { if(m_Prop == ETilesProp::PROP_HEIGHT) pLayerTiles->Resize(pLayerTiles->m_Width, m_Previous); else if(m_Prop == ETilesProp::PROP_WIDTH) pLayerTiles->Resize(m_Previous, pLayerTiles->m_Height); RestoreLayer(LAYERTYPE_TILES, pLayerTiles); if(pLayerTiles->m_HasGame || pLayerTiles->m_HasFront || pLayerTiles->m_HasSwitch || pLayerTiles->m_HasSpeedup || pLayerTiles->m_HasTune) { if(Map()->m_pFrontLayer && !pLayerTiles->m_HasFront) RestoreLayer(LAYERTYPE_FRONT, Map()->m_pFrontLayer); if(Map()->m_pTeleLayer && !pLayerTiles->m_HasTele) RestoreLayer(LAYERTYPE_TELE, Map()->m_pTeleLayer); if(Map()->m_pSwitchLayer && !pLayerTiles->m_HasSwitch) RestoreLayer(LAYERTYPE_SWITCH, Map()->m_pSwitchLayer); if(Map()->m_pSpeedupLayer && !pLayerTiles->m_HasSpeedup) RestoreLayer(LAYERTYPE_SPEEDUP, Map()->m_pSpeedupLayer); if(Map()->m_pTuneLayer && !pLayerTiles->m_HasTune) RestoreLayer(LAYERTYPE_TUNE, Map()->m_pTuneLayer); if(!pLayerTiles->m_HasGame) RestoreLayer(LAYERTYPE_GAME, Map()->m_pGameLayer); } } else if(m_Prop == ETilesProp::PROP_SHIFT) { RestoreLayer(LAYERTYPE_TILES, pLayerTiles); } else if(m_Prop == ETilesProp::PROP_SHIFT_BY) { Map()->m_ShiftBy = m_Previous; } else if(m_Prop == ETilesProp::PROP_IMAGE) { if(m_Previous == -1 || Map()->m_vpImages.empty()) { pLayerTiles->m_Image = -1; } else { pLayerTiles->m_Image = m_Previous % Map()->m_vpImages.size(); pLayerTiles->m_AutoMapperConfig = -1; } } else if(m_Prop == ETilesProp::PROP_COLOR) { const ColorRGBA ColorPick = ColorRGBA::UnpackAlphaLast<ColorRGBA>(m_Previous); pLayerTiles->m_Color.r = ColorPick.r * 255.0f; pLayerTiles->m_Color.g = ColorPick.g * 255.0f; pLayerTiles->m_Color.b = ColorPick.b * 255.0f; pLayerTiles->m_Color.a = ColorPick.a * 255.0f; Editor()->m_ColorPickerPopupContext.m_RgbaColor = ColorPick; Editor()->m_ColorPickerPopupContext.m_HslaColor = color_cast<ColorHSLA>(ColorPick); Editor()->m_ColorPickerPopupContext.m_HsvaColor = color_cast<ColorHSVA>(Editor()->m_ColorPickerPopupContext.m_HslaColor); } else if(m_Prop == ETilesProp::PROP_COLOR_ENV) { pLayerTiles->m_ColorEnv = m_Previous; } else if(m_Prop == ETilesProp::PROP_COLOR_ENV_OFFSET) { pLayerTiles->m_ColorEnvOffset = m_Previous; } else if(m_Prop == ETilesProp::PROP_AUTOMAPPER) { pLayerTiles->m_AutoMapperConfig = m_Previous; } else if(m_Prop == ETilesProp::PROP_LIVE_GAMETILES) { pLayerTiles->m_LiveGameTiles = m_Previous; } else if(m_Prop == ETilesProp::PROP_SEED) { pLayerTiles->m_Seed = m_Previous; } Map()->OnModify(); } void CEditorActionEditLayerTilesProp::Redo() { std::shared_ptr<CLayerTiles> pLayerTiles = std::static_pointer_cast<CLayerTiles>(m_pLayer); if(m_Prop == ETilesProp::PROP_WIDTH || m_Prop == ETilesProp::PROP_HEIGHT) { if(m_Prop == ETilesProp::PROP_HEIGHT) pLayerTiles->Resize(pLayerTiles->m_Width, m_Current); else if(m_Prop == ETilesProp::PROP_WIDTH) pLayerTiles->Resize(m_Current, pLayerTiles->m_Height); if(pLayerTiles->m_HasGame || pLayerTiles->m_HasFront || pLayerTiles->m_HasSwitch || pLayerTiles->m_HasSpeedup || pLayerTiles->m_HasTune) { if(Map()->m_pFrontLayer && !pLayerTiles->m_HasFront) Map()->m_pFrontLayer->Resize(pLayerTiles->m_Width, pLayerTiles->m_Height); if(Map()->m_pTeleLayer && !pLayerTiles->m_HasTele) Map()->m_pTeleLayer->Resize(pLayerTiles->m_Width, pLayerTiles->m_Height); if(Map()->m_pSwitchLayer && !pLayerTiles->m_HasSwitch) Map()->m_pSwitchLayer->Resize(pLayerTiles->m_Width, pLayerTiles->m_Height); if(Map()->m_pSpeedupLayer && !pLayerTiles->m_HasSpeedup) Map()->m_pSpeedupLayer->Resize(pLayerTiles->m_Width, pLayerTiles->m_Height); if(Map()->m_pTuneLayer && !pLayerTiles->m_HasTune) Map()->m_pTuneLayer->Resize(pLayerTiles->m_Width, pLayerTiles->m_Height); if(!pLayerTiles->m_HasGame) Map()->m_pGameLayer->Resize(pLayerTiles->m_Width, pLayerTiles->m_Height); } } else if(m_Prop == ETilesProp::PROP_SHIFT) { pLayerTiles->Shift((EShiftDirection)m_Current); } else if(m_Prop == ETilesProp::PROP_SHIFT_BY) { Map()->m_ShiftBy = m_Current; } else if(m_Prop == ETilesProp::PROP_IMAGE) { if(m_Current == -1 || Map()->m_vpImages.empty()) { pLayerTiles->m_Image = -1; } else { pLayerTiles->m_Image = m_Current % Map()->m_vpImages.size(); pLayerTiles->m_AutoMapperConfig = -1; } } else if(m_Prop == ETilesProp::PROP_COLOR) { const ColorRGBA ColorPick = ColorRGBA::UnpackAlphaLast<ColorRGBA>(m_Current); pLayerTiles->m_Color.r = ColorPick.r * 255.0f; pLayerTiles->m_Color.g = ColorPick.g * 255.0f; pLayerTiles->m_Color.b = ColorPick.b * 255.0f; pLayerTiles->m_Color.a = ColorPick.a * 255.0f; Editor()->m_ColorPickerPopupContext.m_RgbaColor = ColorPick; Editor()->m_ColorPickerPopupContext.m_HslaColor = color_cast<ColorHSLA>(ColorPick); Editor()->m_ColorPickerPopupContext.m_HsvaColor = color_cast<ColorHSVA>(Editor()->m_ColorPickerPopupContext.m_HslaColor); } else if(m_Prop == ETilesProp::PROP_COLOR_ENV) { pLayerTiles->m_ColorEnv = m_Current; } else if(m_Prop == ETilesProp::PROP_COLOR_ENV_OFFSET) { pLayerTiles->m_ColorEnvOffset = m_Current; } else if(m_Prop == ETilesProp::PROP_AUTOMAPPER) { pLayerTiles->m_AutoMapperConfig = m_Current; } else if(m_Prop == ETilesProp::PROP_LIVE_GAMETILES) { pLayerTiles->m_LiveGameTiles = m_Current; } else if(m_Prop == ETilesProp::PROP_SEED) { pLayerTiles->m_Seed = m_Current; } Map()->OnModify(); } void CEditorActionEditLayerTilesProp::RestoreLayer(int Layer, const std::shared_ptr<CLayerTiles> &pLayerTiles) { if(m_SavedLayers[Layer] != nullptr) { std::shared_ptr<CLayerTiles> pSavedLayerTiles = std::static_pointer_cast<CLayerTiles>(m_SavedLayers[Layer]); mem_copy(pLayerTiles->m_pTiles, pSavedLayerTiles->m_pTiles, (size_t)pLayerTiles->m_Width * pLayerTiles->m_Height * sizeof(CTile)); if(pLayerTiles->m_HasTele) { std::shared_ptr<CLayerTele> pLayerTele = std::static_pointer_cast<CLayerTele>(pLayerTiles); std::shared_ptr<CLayerTele> pSavedLayerTele = std::static_pointer_cast<CLayerTele>(pSavedLayerTiles); mem_copy(pLayerTele->m_pTeleTile, pSavedLayerTele->m_pTeleTile, (size_t)pLayerTiles->m_Width * pLayerTiles->m_Height * sizeof(CTeleTile)); } else if(pLayerTiles->m_HasSpeedup) { std::shared_ptr<CLayerSpeedup> pLayerSpeedup = std::static_pointer_cast<CLayerSpeedup>(pLayerTiles); std::shared_ptr<CLayerSpeedup> pSavedLayerSpeedup = std::static_pointer_cast<CLayerSpeedup>(pSavedLayerTiles); mem_copy(pLayerSpeedup->m_pSpeedupTile, pSavedLayerSpeedup->m_pSpeedupTile, (size_t)pLayerTiles->m_Width * pLayerTiles->m_Height * sizeof(CSpeedupTile)); } else if(pLayerTiles->m_HasSwitch) { std::shared_ptr<CLayerSwitch> pLayerSwitch = std::static_pointer_cast<CLayerSwitch>(pLayerTiles); std::shared_ptr<CLayerSwitch> pSavedLayerSwitch = std::static_pointer_cast<CLayerSwitch>(pSavedLayerTiles); mem_copy(pLayerSwitch->m_pSwitchTile, pSavedLayerSwitch->m_pSwitchTile, (size_t)pLayerTiles->m_Width * pLayerTiles->m_Height * sizeof(CSwitchTile)); } else if(pLayerTiles->m_HasTune) { std::shared_ptr<CLayerTune> pLayerTune = std::static_pointer_cast<CLayerTune>(pLayerTiles); std::shared_ptr<CLayerTune> pSavedLayerTune = std::static_pointer_cast<CLayerTune>(pSavedLayerTiles); mem_copy(pLayerTune->m_pTuneTile, pSavedLayerTune->m_pTuneTile, (size_t)pLayerTiles->m_Width * pLayerTiles->m_Height * sizeof(CTuneTile)); } } } CEditorActionEditLayerQuadsProp::CEditorActionEditLayerQuadsProp(CEditorMap *pMap, int GroupIndex, int LayerIndex, ELayerQuadsProp Prop, int Previous, int Current) : CEditorActionEditLayerPropBase(pMap, GroupIndex, LayerIndex, Prop, Previous, Current) { static const char *s_apNames[] = { "image"}; static_assert(std::size(s_apNames) == (size_t)ELayerQuadsProp::NUM_PROPS); str_format(m_aDisplayText, sizeof(m_aDisplayText), "Edit quads layer %d in group %d %s property", m_LayerIndex, m_GroupIndex, s_apNames[(int)m_Prop]); } void CEditorActionEditLayerQuadsProp::Undo() { Apply(m_Previous); } void CEditorActionEditLayerQuadsProp::Redo() { Apply(m_Current); } void CEditorActionEditLayerQuadsProp::Apply(int Value) { std::shared_ptr<CLayerQuads> pLayerQuads = std::static_pointer_cast<CLayerQuads>(m_pLayer); if(m_Prop == ELayerQuadsProp::PROP_IMAGE) { if(Value >= 0 && !Map()->m_vpImages.empty()) pLayerQuads->m_Image = Value % Map()->m_vpImages.size(); else pLayerQuads->m_Image = -1; } Map()->OnModify(); } // -------------------------------------------------------------- CEditorActionEditLayersGroupAndOrder::CEditorActionEditLayersGroupAndOrder(CEditorMap *pMap, int GroupIndex, const std::vector<int> &LayerIndices, int NewGroupIndex, const std::vector<int> &NewLayerIndices) : IEditorAction(pMap), m_GroupIndex(GroupIndex), m_LayerIndices(LayerIndices), m_NewGroupIndex(NewGroupIndex), m_NewLayerIndices(NewLayerIndices) { std::sort(m_LayerIndices.begin(), m_LayerIndices.end()); std::sort(m_NewLayerIndices.begin(), m_NewLayerIndices.end()); str_format(m_aDisplayText, sizeof(m_aDisplayText), "Edit layers group and order (x%d)", (int)m_LayerIndices.size()); } void CEditorActionEditLayersGroupAndOrder::Undo() { // Undo : restore group and order auto &pCurrentGroup = Map()->m_vpGroups[m_NewGroupIndex]; auto &pPreviousGroup = Map()->m_vpGroups[m_GroupIndex]; std::vector<std::shared_ptr<CLayer>> vpLayers; vpLayers.reserve(m_NewLayerIndices.size()); for(auto &LayerIndex : m_NewLayerIndices) vpLayers.push_back(pCurrentGroup->m_vpLayers[LayerIndex]); int k = 0; for(auto &pLayer : vpLayers) { pCurrentGroup->m_vpLayers.erase(std::find(pCurrentGroup->m_vpLayers.begin(), pCurrentGroup->m_vpLayers.end(), pLayer)); pPreviousGroup->m_vpLayers.insert(pPreviousGroup->m_vpLayers.begin() + m_LayerIndices[k++], pLayer); } Editor()->m_vSelectedLayers = m_LayerIndices; Editor()->m_SelectedGroup = m_GroupIndex; } void CEditorActionEditLayersGroupAndOrder::Redo() { // Redo : move layers auto &pCurrentGroup = Map()->m_vpGroups[m_GroupIndex]; auto &pPreviousGroup = Map()->m_vpGroups[m_NewGroupIndex]; std::vector<std::shared_ptr<CLayer>> vpLayers; vpLayers.reserve(m_LayerIndices.size()); for(auto &LayerIndex : m_LayerIndices) vpLayers.push_back(pCurrentGroup->m_vpLayers[LayerIndex]); int k = 0; for(auto &pLayer : vpLayers) { pCurrentGroup->m_vpLayers.erase(std::find(pCurrentGroup->m_vpLayers.begin(), pCurrentGroup->m_vpLayers.end(), pLayer)); pPreviousGroup->m_vpLayers.insert(pPreviousGroup->m_vpLayers.begin() + m_NewLayerIndices[k++], pLayer); } Editor()->m_vSelectedLayers = m_NewLayerIndices; Editor()->m_SelectedGroup = m_NewGroupIndex; } // ----------------------------------- CEditorActionAppendMap::CEditorActionAppendMap(CEditorMap *pMap, const char *pMapName, const SPrevInfo &PrevInfo, std::vector<int> &vImageIndexMap) : IEditorAction(pMap), m_PrevInfo(PrevInfo), m_vImageIndexMap(vImageIndexMap) { str_copy(m_aMapName, pMapName); str_format(m_aDisplayText, sizeof(m_aDisplayText), "Append %s", m_aMapName); } void CEditorActionAppendMap::Undo() { // Undo append: // - delete added groups // - delete added envelopes // - delete added images // - delete added sounds // Delete added groups while((int)Map()->m_vpGroups.size() != m_PrevInfo.m_Groups) { Map()->m_vpGroups.pop_back(); } // Delete added envelopes while((int)Map()->m_vpEnvelopes.size() != m_PrevInfo.m_Envelopes) { Map()->m_vpEnvelopes.pop_back(); } // Delete added sounds while((int)Map()->m_vpSounds.size() != m_PrevInfo.m_Sounds) { Map()->m_vpSounds.pop_back(); } // Delete added images // Images are sorted when appending, so we need to revert sorting before deleting the images if(!m_vImageIndexMap.empty()) { std::vector<int> vReverseIndexMap; vReverseIndexMap.resize(m_vImageIndexMap.size()); for(int k = 0; k < (int)m_vImageIndexMap.size(); k++) vReverseIndexMap[m_vImageIndexMap[k]] = k; std::vector<std::shared_ptr<CEditorImage>> vpRevertedImages; vpRevertedImages.resize(Map()->m_vpImages.size()); for(int k = 0; k < (int)vReverseIndexMap.size(); k++) { vpRevertedImages[vReverseIndexMap[k]] = Map()->m_vpImages[k]; } Map()->m_vpImages = vpRevertedImages; Map()->ModifyImageIndex([vReverseIndexMap](int *pIndex) { if(*pIndex >= 0) { *pIndex = vReverseIndexMap[*pIndex]; } }); } while((int)Map()->m_vpImages.size() != m_PrevInfo.m_Images) { Map()->m_vpImages.pop_back(); } } void CEditorActionAppendMap::Redo() { // Redo is just re-appending the same map Editor()->Append(m_aMapName, IStorage::TYPE_ALL, true); } // --------------------------- CEditorActionTileArt::CEditorActionTileArt(CEditorMap *pMap, int PreviousImageCount, const char *pTileArtFile, std::vector<int> &vImageIndexMap) : IEditorAction(pMap), m_PreviousImageCount(PreviousImageCount), m_vImageIndexMap(vImageIndexMap) { str_copy(m_aTileArtFile, pTileArtFile); str_copy(m_aDisplayText, "Tile art"); } void CEditorActionTileArt::Undo() { // Delete added group Map()->m_vpGroups.pop_back(); // Delete added images // Images are sorted when appending, so we need to revert sorting before deleting the images if(!m_vImageIndexMap.empty()) { std::vector<int> vReverseIndexMap; vReverseIndexMap.resize(m_vImageIndexMap.size()); for(int k = 0; k < (int)m_vImageIndexMap.size(); k++) vReverseIndexMap[m_vImageIndexMap[k]] = k; std::vector<std::shared_ptr<CEditorImage>> vpRevertedImages; vpRevertedImages.resize(Map()->m_vpImages.size()); for(int k = 0; k < (int)vReverseIndexMap.size(); k++) { vpRevertedImages[vReverseIndexMap[k]] = Map()->m_vpImages[k]; } Map()->m_vpImages = vpRevertedImages; Map()->ModifyImageIndex([vReverseIndexMap](int *pIndex) { if(*pIndex >= 0) { *pIndex = vReverseIndexMap[*pIndex]; } }); } while((int)Map()->m_vpImages.size() != m_PreviousImageCount) { Map()->m_vpImages.pop_back(); } } void CEditorActionTileArt::Redo() { if(!Graphics()->LoadPng(Editor()->m_TileartImageInfo, m_aTileArtFile, IStorage::TYPE_ALL)) { Editor()->ShowFileDialogError("Failed to load image from file '%s'.", m_aTileArtFile); return; } IStorage::StripPathAndExtension(m_aTileArtFile, Editor()->m_aTileartFilename, sizeof(Editor()->m_aTileartFilename)); Editor()->AddTileart(true); } // --------------------------- CEditorActionQuadArt::CEditorActionQuadArt(CEditorMap *pMap, CQuadArtParameters Parameters) : IEditorAction(pMap), m_Parameters(Parameters) { str_copy(m_aDisplayText, "Create Quadart"); } void CEditorActionQuadArt::Undo() { // Delete added group Map()->m_vpGroups.pop_back(); } void CEditorActionQuadArt::Redo() { Editor()->m_QuadArtParameters = m_Parameters; str_copy(Editor()->m_QuadArtParameters.m_aFilename, m_Parameters.m_aFilename, sizeof(Editor()->m_QuadArtParameters.m_aFilename)); if(!Graphics()->LoadPng(Editor()->m_QuadArtImageInfo, Editor()->m_QuadArtParameters.m_aFilename, IStorage::TYPE_ALL)) { Editor()->ShowFileDialogError("Failed to load image from file '%s'.", Editor()->m_QuadArtParameters.m_aFilename); return; } Editor()->AddQuadArt(true); } // --------------------------------- CEditorCommandAction::CEditorCommandAction(CEditorMap *pMap, EType Type, int *pSelectedCommandIndex, int CommandIndex, const char *pPreviousCommand, const char *pCurrentCommand) : IEditorAction(pMap), m_Type(Type), m_pSelectedCommandIndex(pSelectedCommandIndex), m_CommandIndex(CommandIndex) { if(pPreviousCommand != nullptr) m_PreviousCommand = std::string(pPreviousCommand); if(pCurrentCommand != nullptr) m_CurrentCommand = std::string(pCurrentCommand); switch(m_Type) { case EType::ADD: str_copy(m_aDisplayText, "Add command"); break; case EType::EDIT: str_format(m_aDisplayText, sizeof(m_aDisplayText), "Edit command %d", m_CommandIndex); break; case EType::DELETE: str_format(m_aDisplayText, sizeof(m_aDisplayText), "Delete command %d", m_CommandIndex); break; case EType::MOVE_UP: str_format(m_aDisplayText, sizeof(m_aDisplayText), "Move command %d up", m_CommandIndex); break; case EType::MOVE_DOWN: str_format(m_aDisplayText, sizeof(m_aDisplayText), "Move command %d down", m_CommandIndex); break; default: str_format(m_aDisplayText, sizeof(m_aDisplayText), "Edit command %d", m_CommandIndex); break; } } void CEditorCommandAction::Undo() { switch(m_Type) { case EType::DELETE: { Map()->m_vSettings.insert(Map()->m_vSettings.begin() + m_CommandIndex, m_PreviousCommand.c_str()); *m_pSelectedCommandIndex = m_CommandIndex; break; } case EType::ADD: { Map()->m_vSettings.erase(Map()->m_vSettings.begin() + m_CommandIndex); *m_pSelectedCommandIndex = Map()->m_vSettings.size() - 1; break; } case EType::EDIT: { str_copy(Map()->m_vSettings[m_CommandIndex].m_aCommand, m_PreviousCommand.c_str()); *m_pSelectedCommandIndex = m_CommandIndex; break; } case EType::MOVE_DOWN: { std::swap(Map()->m_vSettings[m_CommandIndex], Map()->m_vSettings[m_CommandIndex + 1]); *m_pSelectedCommandIndex = m_CommandIndex; break; } case EType::MOVE_UP: { std::swap(Map()->m_vSettings[m_CommandIndex], Map()->m_vSettings[m_CommandIndex - 1]); *m_pSelectedCommandIndex = m_CommandIndex; break; } } } void CEditorCommandAction::Redo() { switch(m_Type) { case EType::DELETE: { Map()->m_vSettings.erase(Map()->m_vSettings.begin() + m_CommandIndex); *m_pSelectedCommandIndex = Map()->m_vSettings.size() - 1; break; } case EType::ADD: { Map()->m_vSettings.insert(Map()->m_vSettings.begin() + m_CommandIndex, m_PreviousCommand.c_str()); *m_pSelectedCommandIndex = m_CommandIndex; break; } case EType::EDIT: { str_copy(Map()->m_vSettings[m_CommandIndex].m_aCommand, m_CurrentCommand.c_str()); *m_pSelectedCommandIndex = m_CommandIndex; break; } case EType::MOVE_DOWN: { std::swap(Map()->m_vSettings[m_CommandIndex], Map()->m_vSettings[m_CommandIndex + 1]); *m_pSelectedCommandIndex = m_CommandIndex; break; } case EType::MOVE_UP: { std::swap(Map()->m_vSettings[m_CommandIndex], Map()->m_vSettings[m_CommandIndex - 1]); *m_pSelectedCommandIndex = m_CommandIndex; break; } } } // ------------------------------------------------ CEditorActionEnvelopeAdd::CEditorActionEnvelopeAdd(CEditorMap *pMap, CEnvelope::EType EnvelopeType) : IEditorAction(pMap), m_EnvelopeType(EnvelopeType) { str_format(m_aDisplayText, sizeof(m_aDisplayText), "Add new %s envelope", EnvelopeType == CEnvelope::EType::COLOR ? "color" : (EnvelopeType == CEnvelope::EType::POSITION ? "position" : "sound")); m_PreviousSelectedEnvelope = Editor()->m_SelectedEnvelope; } void CEditorActionEnvelopeAdd::Undo() { // Undo is removing the envelope, which was added at the back of the list Map()->m_vpEnvelopes.pop_back(); Map()->OnModify(); Editor()->m_SelectedEnvelope = m_PreviousSelectedEnvelope; } void CEditorActionEnvelopeAdd::Redo() { // Redo is adding a new envelope at the back of the list Map()->NewEnvelope(m_EnvelopeType); Editor()->m_SelectedEnvelope = Map()->m_vpEnvelopes.size() - 1; } CEditorActionEnvelopeDelete::CEditorActionEnvelopeDelete(CEditorMap *pMap, int EnvelopeIndex, std::vector<std::shared_ptr<IEditorEnvelopeReference>> &vpObjectReferences, std::shared_ptr<CEnvelope> &pEnvelope) : IEditorAction(pMap), m_EnvelopeIndex(EnvelopeIndex), m_pEnv(pEnvelope), m_vpObjectReferences(vpObjectReferences) { str_format(m_aDisplayText, sizeof(m_aDisplayText), "Delete envelope %d", m_EnvelopeIndex); } void CEditorActionEnvelopeDelete::Undo() { // Undo is adding back the envelope Map()->InsertEnvelope(m_EnvelopeIndex, m_pEnv); Map()->UpdateEnvelopeReferences(m_EnvelopeIndex, m_pEnv, m_vpObjectReferences); } void CEditorActionEnvelopeDelete::Redo() { // Redo is erasing the same envelope index Map()->DeleteEnvelope(m_EnvelopeIndex); } CEditorActionEnvelopeEdit::CEditorActionEnvelopeEdit(CEditorMap *pMap, int EnvelopeIndex, EEditType EditType, int Previous, int Current) : IEditorAction(pMap), m_EnvelopeIndex(EnvelopeIndex), m_EditType(EditType), m_Previous(Previous), m_Current(Current), m_pEnv(Map()->m_vpEnvelopes[EnvelopeIndex]) { static const char *s_apNames[] = { "sync", "order"}; str_format(m_aDisplayText, sizeof(m_aDisplayText), "Edit envelope %d %s", m_EnvelopeIndex, s_apNames[(int)m_EditType]); } void CEditorActionEnvelopeEdit::Undo() { switch(m_EditType) { case EEditType::ORDER: { Map()->MoveEnvelope(m_Current, m_Previous); break; } case EEditType::SYNC: { m_pEnv->m_Synchronized = m_Previous; break; } } Map()->OnModify(); Editor()->m_SelectedEnvelope = m_EnvelopeIndex; } void CEditorActionEnvelopeEdit::Redo() { switch(m_EditType) { case EEditType::ORDER: { Map()->MoveEnvelope(m_Previous, m_Current); break; } case EEditType::SYNC: { m_pEnv->m_Synchronized = m_Current; break; } } Map()->OnModify(); Editor()->m_SelectedEnvelope = m_EnvelopeIndex; } CEditorActionEnvelopeEditPointTime::CEditorActionEnvelopeEditPointTime(CEditorMap *pMap, int EnvelopeIndex, int PointIndex, CFixedTime Previous, CFixedTime Current) : IEditorAction(pMap), m_EnvelopeIndex(EnvelopeIndex), m_PointIndex(PointIndex), m_Previous(Previous), m_Current(Current), m_pEnv(Map()->m_vpEnvelopes[EnvelopeIndex]) { str_format(m_aDisplayText, sizeof(m_aDisplayText), "Edit time of point %d of env %d", m_PointIndex, m_EnvelopeIndex); } void CEditorActionEnvelopeEditPointTime::Undo() { Apply(m_Previous); } void CEditorActionEnvelopeEditPointTime::Redo() { Apply(m_Current); } void CEditorActionEnvelopeEditPointTime::Apply(CFixedTime Value) { m_pEnv->m_vPoints[m_PointIndex].m_Time = Value; Map()->OnModify(); } CEditorActionEnvelopeEditPoint::CEditorActionEnvelopeEditPoint(CEditorMap *pMap, int EnvelopeIndex, int PointIndex, int Channel, EEditType EditType, int Previous, int Current) : IEditorAction(pMap), m_EnvelopeIndex(EnvelopeIndex), m_PointIndex(PointIndex), m_Channel(Channel), m_EditType(EditType), m_Previous(Previous), m_Current(Current), m_pEnv(Map()->m_vpEnvelopes[EnvelopeIndex]) { static const char *s_apNames[] = { "value", "curve type"}; str_format(m_aDisplayText, sizeof(m_aDisplayText), "Edit %s of point %d (channel %d) of env %d", s_apNames[(int)m_EditType], m_PointIndex, m_Channel, m_EnvelopeIndex); } void CEditorActionEnvelopeEditPoint::Undo() { Apply(m_Previous); } void CEditorActionEnvelopeEditPoint::Redo() { Apply(m_Current); } void CEditorActionEnvelopeEditPoint::Apply(int Value) { if(m_EditType == EEditType::VALUE) { m_pEnv->m_vPoints[m_PointIndex].m_aValues[m_Channel] = Value; if(m_pEnv->GetChannels() == 4) { Editor()->m_ColorPickerPopupContext.m_RgbaColor = m_pEnv->m_vPoints[m_PointIndex].ColorValue(); Editor()->m_ColorPickerPopupContext.m_HslaColor = color_cast<ColorHSLA>(Editor()->m_ColorPickerPopupContext.m_RgbaColor); Editor()->m_ColorPickerPopupContext.m_HsvaColor = color_cast<ColorHSVA>(Editor()->m_ColorPickerPopupContext.m_HslaColor); } } else if(m_EditType == EEditType::CURVE_TYPE) { m_pEnv->m_vPoints[m_PointIndex].m_Curvetype = Value; } Map()->OnModify(); } // ---- CEditorActionEditEnvelopePointValue::CEditorActionEditEnvelopePointValue(CEditorMap *pMap, int EnvelopeIndex, int PointIndex, int Channel, EType Type, CFixedTime OldTime, int OldValue, CFixedTime NewTime, int NewValue) : 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) { str_format(m_aDisplayText, sizeof(m_aDisplayText), "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); } void CEditorActionEditEnvelopePointValue::Undo() { Apply(true); } void CEditorActionEditEnvelopePointValue::Redo() { Apply(false); } void CEditorActionEditEnvelopePointValue::Apply(bool Undo) { float CurrentValue = fx2f(Undo ? m_OldValue : m_NewValue); CFixedTime CurrentTime = (Undo ? m_OldTime : m_NewTime); std::shared_ptr<CEnvelope> pEnvelope = Map()->m_vpEnvelopes[m_EnvelopeIndex]; if(m_Type == EType::TANGENT_IN) { pEnvelope->m_vPoints[m_PointIndex].m_Bezier.m_aInTangentDeltaX[m_Channel] = std::min(CurrentTime - pEnvelope->m_vPoints[m_PointIndex].m_Time, CFixedTime(0)); pEnvelope->m_vPoints[m_PointIndex].m_Bezier.m_aInTangentDeltaY[m_Channel] = f2fx(CurrentValue) - pEnvelope->m_vPoints[m_PointIndex].m_aValues[m_Channel]; } else if(m_Type == EType::TANGENT_OUT) { pEnvelope->m_vPoints[m_PointIndex].m_Bezier.m_aOutTangentDeltaX[m_Channel] = std::max(CurrentTime - pEnvelope->m_vPoints[m_PointIndex].m_Time, CFixedTime(0)); pEnvelope->m_vPoints[m_PointIndex].m_Bezier.m_aOutTangentDeltaY[m_Channel] = f2fx(CurrentValue) - pEnvelope->m_vPoints[m_PointIndex].m_aValues[m_Channel]; } else { if(pEnvelope->GetChannels() == 1 || pEnvelope->GetChannels() == 4) CurrentValue = std::clamp(CurrentValue, 0.0f, 1.0f); pEnvelope->m_vPoints[m_PointIndex].m_aValues[m_Channel] = f2fx(CurrentValue); if(m_PointIndex != 0) { pEnvelope->m_vPoints[m_PointIndex].m_Time = CurrentTime; if(pEnvelope->m_vPoints[m_PointIndex].m_Time < pEnvelope->m_vPoints[m_PointIndex - 1].m_Time) pEnvelope->m_vPoints[m_PointIndex].m_Time = pEnvelope->m_vPoints[m_PointIndex - 1].m_Time + CFixedTime(1); 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) pEnvelope->m_vPoints[m_PointIndex].m_Time = pEnvelope->m_vPoints[m_PointIndex + 1].m_Time - CFixedTime(1); } else { pEnvelope->m_vPoints[m_PointIndex].m_Time = CFixedTime(0); } } Map()->OnModify(); Editor()->m_UpdateEnvPointInfo = true; } // --------------------- CEditorActionResetEnvelopePointTangent::CEditorActionResetEnvelopePointTangent(CEditorMap *pMap, int EnvelopeIndex, int PointIndex, int Channel, bool In) : IEditorAction(pMap), m_EnvelopeIndex(EnvelopeIndex), m_PointIndex(PointIndex), m_Channel(Channel), m_In(In) { std::shared_ptr<CEnvelope> pEnvelope = Map()->m_vpEnvelopes[EnvelopeIndex]; if(In) { m_OldTime = pEnvelope->m_vPoints[PointIndex].m_Bezier.m_aInTangentDeltaX[Channel]; m_OldValue = pEnvelope->m_vPoints[PointIndex].m_Bezier.m_aInTangentDeltaY[Channel]; } else { m_OldTime = pEnvelope->m_vPoints[PointIndex].m_Bezier.m_aOutTangentDeltaX[Channel]; m_OldValue = pEnvelope->m_vPoints[PointIndex].m_Bezier.m_aOutTangentDeltaY[Channel]; } str_format(m_aDisplayText, sizeof(m_aDisplayText), "Reset point %d of env %d tangent %s", m_PointIndex, m_EnvelopeIndex, m_In ? "in" : "out"); } void CEditorActionResetEnvelopePointTangent::Undo() { std::shared_ptr<CEnvelope> pEnvelope = Map()->m_vpEnvelopes[m_EnvelopeIndex]; if(m_In) { pEnvelope->m_vPoints[m_PointIndex].m_Bezier.m_aInTangentDeltaX[m_Channel] = m_OldTime; pEnvelope->m_vPoints[m_PointIndex].m_Bezier.m_aInTangentDeltaY[m_Channel] = m_OldValue; } else { pEnvelope->m_vPoints[m_PointIndex].m_Bezier.m_aOutTangentDeltaX[m_Channel] = m_OldTime; pEnvelope->m_vPoints[m_PointIndex].m_Bezier.m_aOutTangentDeltaY[m_Channel] = m_OldValue; } Map()->OnModify(); } void CEditorActionResetEnvelopePointTangent::Redo() { std::shared_ptr<CEnvelope> pEnvelope = Map()->m_vpEnvelopes[m_EnvelopeIndex]; if(m_In) { pEnvelope->m_vPoints[m_PointIndex].m_Bezier.m_aInTangentDeltaX[m_Channel] = CFixedTime(0); pEnvelope->m_vPoints[m_PointIndex].m_Bezier.m_aInTangentDeltaY[m_Channel] = 0.0f; } else { pEnvelope->m_vPoints[m_PointIndex].m_Bezier.m_aOutTangentDeltaX[m_Channel] = CFixedTime(0); pEnvelope->m_vPoints[m_PointIndex].m_Bezier.m_aOutTangentDeltaY[m_Channel] = 0.0f; } Map()->OnModify(); } // ------------------ CEditorActionAddEnvelopePoint::CEditorActionAddEnvelopePoint(CEditorMap *pMap, int EnvelopeIndex, CFixedTime Time, ColorRGBA Channels) : IEditorAction(pMap), m_EnvelopeIndex(EnvelopeIndex), m_Time(Time), m_Channels(Channels) { str_format(m_aDisplayText, sizeof(m_aDisplayText), "Add new point in envelope %d at time %f", m_EnvelopeIndex, Time.AsSeconds()); } void CEditorActionAddEnvelopePoint::Undo() { // Delete added point auto pEnvelope = Map()->m_vpEnvelopes[m_EnvelopeIndex]; auto pIt = std::find_if(pEnvelope->m_vPoints.begin(), pEnvelope->m_vPoints.end(), [this](const CEnvPoint_runtime &Point) { return Point.m_Time == m_Time; }); if(pIt != pEnvelope->m_vPoints.end()) { pEnvelope->m_vPoints.erase(pIt); } Map()->OnModify(); } void CEditorActionAddEnvelopePoint::Redo() { auto pEnvelope = Map()->m_vpEnvelopes[m_EnvelopeIndex]; pEnvelope->AddPoint(m_Time, {f2fx(m_Channels.r), f2fx(m_Channels.g), f2fx(m_Channels.b), f2fx(m_Channels.a)}); Map()->OnModify(); } CEditorActionDeleteEnvelopePoint::CEditorActionDeleteEnvelopePoint(CEditorMap *pMap, int EnvelopeIndex, int PointIndex) : IEditorAction(pMap), m_EnvelopeIndex(EnvelopeIndex), m_PointIndex(PointIndex), m_Point(Map()->m_vpEnvelopes[EnvelopeIndex]->m_vPoints[PointIndex]) { str_format(m_aDisplayText, sizeof(m_aDisplayText), "Delete point %d of envelope %d", m_PointIndex, m_EnvelopeIndex); } void CEditorActionDeleteEnvelopePoint::Undo() { std::shared_ptr<CEnvelope> pEnvelope = Map()->m_vpEnvelopes[m_EnvelopeIndex]; pEnvelope->m_vPoints.insert(pEnvelope->m_vPoints.begin() + m_PointIndex, m_Point); Map()->OnModify(); } void CEditorActionDeleteEnvelopePoint::Redo() { std::shared_ptr<CEnvelope> pEnvelope = Map()->m_vpEnvelopes[m_EnvelopeIndex]; pEnvelope->m_vPoints.erase(pEnvelope->m_vPoints.begin() + m_PointIndex); auto pSelectedPointIt = std::find_if(Editor()->m_vSelectedEnvelopePoints.begin(), Editor()->m_vSelectedEnvelopePoints.end(), [this](const std::pair<int, int> Pair) { return Pair.first == m_PointIndex; }); if(pSelectedPointIt != Editor()->m_vSelectedEnvelopePoints.end()) Editor()->m_vSelectedEnvelopePoints.erase(pSelectedPointIt); Map()->OnModify(); } // ------------------------------- CEditorActionEditLayerSoundsProp::CEditorActionEditLayerSoundsProp(CEditorMap *pMap, int GroupIndex, int LayerIndex, ELayerSoundsProp Prop, int Previous, int Current) : CEditorActionEditLayerPropBase(pMap, GroupIndex, LayerIndex, Prop, Previous, Current) { static const char *s_apNames[] = { "sound"}; static_assert(std::size(s_apNames) == (size_t)ELayerSoundsProp::NUM_PROPS); str_format(m_aDisplayText, sizeof(m_aDisplayText), "Edit sounds layer %d in group %d %s property", m_LayerIndex, m_GroupIndex, s_apNames[(int)m_Prop]); } void CEditorActionEditLayerSoundsProp::Undo() { Apply(m_Previous); } void CEditorActionEditLayerSoundsProp::Redo() { Apply(m_Current); } void CEditorActionEditLayerSoundsProp::Apply(int Value) { std::shared_ptr<CLayerSounds> pLayerSounds = std::static_pointer_cast<CLayerSounds>(m_pLayer); if(m_Prop == ELayerSoundsProp::PROP_SOUND) { if(Value >= 0 && !Map()->m_vpSounds.empty()) pLayerSounds->m_Sound = Value % Map()->m_vpSounds.size(); else pLayerSounds->m_Sound = -1; } Map()->OnModify(); } // --- CEditorActionDeleteSoundSource::CEditorActionDeleteSoundSource(CEditorMap *pMap, int GroupIndex, int LayerIndex, int SourceIndex) : CEditorActionLayerBase(pMap, GroupIndex, LayerIndex), m_SourceIndex(SourceIndex) { std::shared_ptr<CLayerSounds> pLayerSounds = std::static_pointer_cast<CLayerSounds>(m_pLayer); m_Source = pLayerSounds->m_vSources[SourceIndex]; str_format(m_aDisplayText, sizeof(m_aDisplayText), "Delete sound source %d in layer %d of group %d", SourceIndex, LayerIndex, GroupIndex); } void CEditorActionDeleteSoundSource::Undo() { std::shared_ptr<CLayerSounds> pLayerSounds = std::static_pointer_cast<CLayerSounds>(m_pLayer); pLayerSounds->m_vSources.insert(pLayerSounds->m_vSources.begin() + m_SourceIndex, m_Source); Editor()->m_SelectedSource = m_SourceIndex; Map()->OnModify(); } void CEditorActionDeleteSoundSource::Redo() { std::shared_ptr<CLayerSounds> pLayerSounds = std::static_pointer_cast<CLayerSounds>(m_pLayer); pLayerSounds->m_vSources.erase(pLayerSounds->m_vSources.begin() + m_SourceIndex); Editor()->m_SelectedSource--; Map()->OnModify(); } // --------------- CEditorActionEditSoundSourceShape::CEditorActionEditSoundSourceShape(CEditorMap *pMap, int GroupIndex, int LayerIndex, int SourceIndex, int Value) : CEditorActionLayerBase(pMap, GroupIndex, LayerIndex), m_SourceIndex(SourceIndex), m_CurrentValue(Value) { Save(); static const char *const SHAPE_NAMES[] = { "rectangle", "circle", }; static_assert(std::size(SHAPE_NAMES) == (size_t)CSoundShape::NUM_SHAPES); str_format(m_aDisplayText, sizeof(m_aDisplayText), "Edit shape of sound source %d in layer %d of group %d to %s", SourceIndex, LayerIndex, GroupIndex, SHAPE_NAMES[Value]); } void CEditorActionEditSoundSourceShape::Undo() { std::shared_ptr<CLayerSounds> pLayerSounds = std::static_pointer_cast<CLayerSounds>(m_pLayer); CSoundSource *pSource = &pLayerSounds->m_vSources[m_SourceIndex]; pSource->m_Shape = m_SavedShape; Map()->OnModify(); } void CEditorActionEditSoundSourceShape::Redo() { std::shared_ptr<CLayerSounds> pLayerSounds = std::static_pointer_cast<CLayerSounds>(m_pLayer); CSoundSource *pSource = &pLayerSounds->m_vSources[m_SourceIndex]; pSource->m_Shape.m_Type = m_CurrentValue; // set default values switch(pSource->m_Shape.m_Type) { case CSoundShape::SHAPE_CIRCLE: { pSource->m_Shape.m_Circle.m_Radius = 1000; break; } case CSoundShape::SHAPE_RECTANGLE: { pSource->m_Shape.m_Rectangle.m_Width = f2fx(1000.0f); pSource->m_Shape.m_Rectangle.m_Height = f2fx(800.0f); break; } } Map()->OnModify(); } void CEditorActionEditSoundSourceShape::Save() { std::shared_ptr<CLayerSounds> pLayerSounds = std::static_pointer_cast<CLayerSounds>(m_pLayer); m_SavedShape = pLayerSounds->m_vSources[m_SourceIndex].m_Shape; } // ----- CEditorActionEditSoundSourceProp::CEditorActionEditSoundSourceProp(CEditorMap *pMap, int GroupIndex, int LayerIndex, int SourceIndex, ESoundProp Prop, int Previous, int Current) : CEditorActionEditLayerPropBase(pMap, GroupIndex, LayerIndex, Prop, Previous, Current), m_SourceIndex(SourceIndex) { static const char *s_apNames[] = { "pos X", "pos Y", "loop", "pan", "time delay", "falloff", "pos env", "pos env offset", "sound env", "sound env offset"}; static_assert(std::size(s_apNames) == (size_t)ESoundProp::NUM_PROPS); str_format(m_aDisplayText, sizeof(m_aDisplayText), "Edit sound source %d in layer %d of group %d %s property", SourceIndex, LayerIndex, GroupIndex, s_apNames[(int)Prop]); } void CEditorActionEditSoundSourceProp::Undo() { Apply(m_Previous); } void CEditorActionEditSoundSourceProp::Redo() { Apply(m_Current); } void CEditorActionEditSoundSourceProp::Apply(int Value) { std::shared_ptr<CLayerSounds> pLayerSounds = std::static_pointer_cast<CLayerSounds>(m_pLayer); CSoundSource *pSource = &pLayerSounds->m_vSources[m_SourceIndex]; if(m_Prop == ESoundProp::PROP_POS_X) { pSource->m_Position.x = Value; } else if(m_Prop == ESoundProp::PROP_POS_Y) { pSource->m_Position.y = Value; } else if(m_Prop == ESoundProp::PROP_LOOP) { pSource->m_Loop = Value; } else if(m_Prop == ESoundProp::PROP_PAN) { pSource->m_Pan = Value; } else if(m_Prop == ESoundProp::PROP_TIME_DELAY) { pSource->m_TimeDelay = Value; } else if(m_Prop == ESoundProp::PROP_FALLOFF) { pSource->m_Falloff = Value; } else if(m_Prop == ESoundProp::PROP_POS_ENV) { pSource->m_PosEnv = Value; } else if(m_Prop == ESoundProp::PROP_POS_ENV_OFFSET) { pSource->m_PosEnvOffset = Value; } else if(m_Prop == ESoundProp::PROP_SOUND_ENV) { pSource->m_SoundEnv = Value; } else if(m_Prop == ESoundProp::PROP_SOUND_ENV_OFFSET) { pSource->m_SoundEnvOffset = Value; } Map()->OnModify(); } CEditorActionEditRectSoundSourceShapeProp::CEditorActionEditRectSoundSourceShapeProp(CEditorMap *pMap, int GroupIndex, int LayerIndex, int SourceIndex, ERectangleShapeProp Prop, int Previous, int Current) : CEditorActionEditLayerPropBase(pMap, GroupIndex, LayerIndex, Prop, Previous, Current), m_SourceIndex(SourceIndex) { static const char *s_apNames[] = { "width", "height"}; static_assert(std::size(s_apNames) == (size_t)ERectangleShapeProp::NUM_PROPS); str_format(m_aDisplayText, sizeof(m_aDisplayText), "Edit sound source %d in layer %d of group %d sound shape %s property", m_SourceIndex, m_LayerIndex, m_GroupIndex, s_apNames[(int)Prop]); } void CEditorActionEditRectSoundSourceShapeProp::Undo() { Apply(m_Previous); } void CEditorActionEditRectSoundSourceShapeProp::Redo() { Apply(m_Current); } void CEditorActionEditRectSoundSourceShapeProp::Apply(int Value) { std::shared_ptr<CLayerSounds> pLayerSounds = std::static_pointer_cast<CLayerSounds>(m_pLayer); CSoundSource *pSource = &pLayerSounds->m_vSources[m_SourceIndex]; if(m_Prop == ERectangleShapeProp::PROP_RECTANGLE_WIDTH) { pSource->m_Shape.m_Rectangle.m_Width = Value; } else if(m_Prop == ERectangleShapeProp::PROP_RECTANGLE_HEIGHT) { pSource->m_Shape.m_Rectangle.m_Height = Value; } Map()->OnModify(); } CEditorActionEditCircleSoundSourceShapeProp::CEditorActionEditCircleSoundSourceShapeProp(CEditorMap *pMap, int GroupIndex, int LayerIndex, int SourceIndex, ECircleShapeProp Prop, int Previous, int Current) : CEditorActionEditLayerPropBase(pMap, GroupIndex, LayerIndex, Prop, Previous, Current), m_SourceIndex(SourceIndex) { static const char *s_apNames[] = { "radius"}; static_assert(std::size(s_apNames) == (size_t)ECircleShapeProp::NUM_PROPS); str_format(m_aDisplayText, sizeof(m_aDisplayText), "Edit sound source %d in layer %d of group %d sound shape %s property", m_SourceIndex, m_LayerIndex, m_GroupIndex, s_apNames[(int)Prop]); } void CEditorActionEditCircleSoundSourceShapeProp::Undo() { Apply(m_Previous); } void CEditorActionEditCircleSoundSourceShapeProp::Redo() { Apply(m_Current); } void CEditorActionEditCircleSoundSourceShapeProp::Apply(int Value) { std::shared_ptr<CLayerSounds> pLayerSounds = std::static_pointer_cast<CLayerSounds>(m_pLayer); CSoundSource *pSource = &pLayerSounds->m_vSources[m_SourceIndex]; if(m_Prop == ECircleShapeProp::PROP_CIRCLE_RADIUS) { pSource->m_Shape.m_Circle.m_Radius = Value; } Map()->OnModify(); } // -------------------------- CEditorActionNewEmptySound::CEditorActionNewEmptySound(CEditorMap *pMap, int GroupIndex, int LayerIndex, int x, int y) : CEditorActionLayerBase(pMap, GroupIndex, LayerIndex), m_X(x), m_Y(y) { str_format(m_aDisplayText, sizeof(m_aDisplayText), "New sound in layer %d of group %d", LayerIndex, GroupIndex); } void CEditorActionNewEmptySound::Undo() { // Undo is simply deleting the added source std::shared_ptr<CLayerSounds> pLayerSounds = std::static_pointer_cast<CLayerSounds>(m_pLayer); pLayerSounds->m_vSources.pop_back(); Map()->OnModify(); } void CEditorActionNewEmptySound::Redo() { std::shared_ptr<CLayerSounds> pLayerSounds = std::static_pointer_cast<CLayerSounds>(m_pLayer); pLayerSounds->NewSource(m_X, m_Y); Map()->OnModify(); } CEditorActionNewEmptyQuad::CEditorActionNewEmptyQuad(CEditorMap *pMap, int GroupIndex, int LayerIndex, int x, int y) : CEditorActionLayerBase(pMap, GroupIndex, LayerIndex), m_X(x), m_Y(y) { str_format(m_aDisplayText, sizeof(m_aDisplayText), "New quad in layer %d of group %d", LayerIndex, GroupIndex); } void CEditorActionNewEmptyQuad::Undo() { // Undo is simply deleting the added quad std::shared_ptr<CLayerQuads> pLayerQuads = std::static_pointer_cast<CLayerQuads>(m_pLayer); pLayerQuads->m_vQuads.pop_back(); Map()->OnModify(); } void CEditorActionNewEmptyQuad::Redo() { std::shared_ptr<CLayerQuads> pLayerQuads = std::static_pointer_cast<CLayerQuads>(m_pLayer); int Width = 64; int Height = 64; if(pLayerQuads->m_Image >= 0) { Width = Map()->m_vpImages[pLayerQuads->m_Image]->m_Width; Height = Map()->m_vpImages[pLayerQuads->m_Image]->m_Height; } pLayerQuads->NewQuad(m_X, m_Y, Width, Height); Map()->OnModify(); } // ------------- CEditorActionNewQuad::CEditorActionNewQuad(CEditorMap *pMap, int GroupIndex, int LayerIndex) : CEditorActionLayerBase(pMap, GroupIndex, LayerIndex) { std::shared_ptr<CLayerQuads> pLayerQuads = std::static_pointer_cast<CLayerQuads>(m_pLayer); m_Quad = pLayerQuads->m_vQuads[pLayerQuads->m_vQuads.size() - 1]; str_format(m_aDisplayText, sizeof(m_aDisplayText), "New quad in layer %d of group %d", LayerIndex, GroupIndex); } void CEditorActionNewQuad::Undo() { std::shared_ptr<CLayerQuads> pLayerQuads = std::static_pointer_cast<CLayerQuads>(m_pLayer); pLayerQuads->m_vQuads.pop_back(); } void CEditorActionNewQuad::Redo() { std::shared_ptr<CLayerQuads> pLayerQuads = std::static_pointer_cast<CLayerQuads>(m_pLayer); pLayerQuads->m_vQuads.emplace_back(m_Quad); } // -------------- CEditorActionMoveSoundSource::CEditorActionMoveSoundSource(CEditorMap *pMap, int GroupIndex, int LayerIndex, int SourceIndex, CPoint OriginalPosition, CPoint CurrentPosition) : CEditorActionLayerBase(pMap, GroupIndex, LayerIndex), m_SourceIndex(SourceIndex), m_OriginalPosition(OriginalPosition), m_CurrentPosition(CurrentPosition) { str_format(m_aDisplayText, sizeof(m_aDisplayText), "Move sound source %d of layer %d in group %d", SourceIndex, LayerIndex, GroupIndex); } void CEditorActionMoveSoundSource::Undo() { dbg_assert(m_pLayer->m_Type == LAYERTYPE_SOUNDS, "Layer type does not match a sound layer"); std::static_pointer_cast<CLayerSounds>(m_pLayer)->m_vSources[m_SourceIndex].m_Position = m_OriginalPosition; } void CEditorActionMoveSoundSource::Redo() { dbg_assert(m_pLayer->m_Type == LAYERTYPE_SOUNDS, "Layer type does not match a sound layer"); std::static_pointer_cast<CLayerSounds>(m_pLayer)->m_vSources[m_SourceIndex].m_Position = m_CurrentPosition; }