From 28f0d2d71f83bf4f9dbf462976646d17df1fa49c Mon Sep 17 00:00:00 2001 From: Louis Geer Date: Sat, 4 Jan 2025 17:51:17 +0900 Subject: [PATCH] Editor: confirm deletion of images/sounds that are used in map layers, fix crash when nonexistent image is added thru history/redo --- src/game/editor/editor.cpp | 43 +++++++++++++++++++++++++++++ src/game/editor/editor.h | 5 +++- src/game/editor/editor_actions.cpp | 8 +++--- src/game/editor/popups.cpp | 44 +++++++++++++++++++++++++++--- 4 files changed, 91 insertions(+), 9 deletions(-) diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index d2a740ad81b..c3ff40a34e7 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -4703,6 +4703,49 @@ bool CEditor::ReplaceSoundCallback(const char *pFileName, int StorageType, void return static_cast(pUser)->ReplaceSound(pFileName, StorageType, true); } +bool CEditor::IsAssetUsed(int FileType, int Index, void *pUser) +{ + CEditor *pEditor = (CEditor *)pUser; + for(int g = 0; g < (int)pEditor->m_Map.m_vpGroups.size(); g++) + { + for(int i = 0; i < (int)pEditor->m_Map.m_vpGroups[g]->m_vpLayers.size(); i++) + { + int LayerType = pEditor->m_Map.m_vpGroups[g]->m_vpLayers[i]->m_Type; + if(FileType == FILETYPE_IMG) + { + if(LayerType == LAYERTYPE_TILES) + { + std::shared_ptr pTiles = std::static_pointer_cast(pEditor->m_Map.m_vpGroups[g]->m_vpLayers[i]); + if(pTiles->m_Image == Index) + { + return true; + } + } + else if(LayerType == LAYERTYPE_QUADS) + { + std::shared_ptr pQuads = std::static_pointer_cast(pEditor->m_Map.m_vpGroups[g]->m_vpLayers[i]); + if(pQuads->m_Image == Index) + { + return true; + } + } + } + else if(FileType == FILETYPE_SOUND) + { + if(LayerType == LAYERTYPE_SOUNDS) + { + std::shared_ptr pSounds = std::static_pointer_cast(pEditor->m_Map.m_vpGroups[g]->m_vpLayers[i]); + if(pSounds->m_Sound == pEditor->m_SelectedImage) + { + return true; + } + } + } + } + } + return false; +} + void CEditor::SelectGameLayer() { for(size_t g = 0; g < m_Map.m_vpGroups.size(); g++) diff --git a/src/game/editor/editor.h b/src/game/editor/editor.h index 359631d036e..9d566126f33 100644 --- a/src/game/editor/editor.h +++ b/src/game/editor/editor.h @@ -608,7 +608,9 @@ class CEditor : public IEditor POPEVENT_PLACE_BORDER_TILES, POPEVENT_PIXELART_BIG_IMAGE, POPEVENT_PIXELART_MANY_COLORS, - POPEVENT_PIXELART_TOO_MANY_COLORS + POPEVENT_PIXELART_TOO_MANY_COLORS, + POPEVENT_REMOVE_USED_IMAGE, + POPEVENT_REMOVE_USED_SOUND, }; int m_PopupEventType; @@ -1009,6 +1011,7 @@ class CEditor : public IEditor static bool ReplaceSoundCallback(const char *pFileName, int StorageType, void *pUser); static bool AddImage(const char *pFilename, int StorageType, void *pUser); static bool AddSound(const char *pFileName, int StorageType, void *pUser); + static bool IsAssetUsed(int FileType, int Index, void *pUser); bool IsEnvelopeUsed(int EnvelopeIndex) const; void RemoveUnusedEnvelopes(); diff --git a/src/game/editor/editor_actions.cpp b/src/game/editor/editor_actions.cpp index 8f4334d04b9..86b8e4d0b1d 100644 --- a/src/game/editor/editor_actions.cpp +++ b/src/game/editor/editor_actions.cpp @@ -905,7 +905,7 @@ void CEditorActionEditLayerTilesProp::Undo() } else if(m_Prop == ETilesProp::PROP_IMAGE) { - if(m_Previous == -1) + if(m_Previous == -1 || m_pEditor->m_Map.m_vpImages.empty()) { pLayerTiles->m_Image = -1; } @@ -985,7 +985,7 @@ void CEditorActionEditLayerTilesProp::Redo() } else if(m_Prop == ETilesProp::PROP_IMAGE) { - if(m_Current == -1) + if(m_Current == -1 || m_pEditor->m_Map.m_vpImages.empty()) { pLayerTiles->m_Image = -1; } @@ -1085,7 +1085,7 @@ void CEditorActionEditLayerQuadsProp::Apply(int Value) std::shared_ptr pLayerQuads = std::static_pointer_cast(m_pLayer); if(m_Prop == ELayerQuadsProp::PROP_IMAGE) { - if(Value >= 0) + if(Value >= 0 && !m_pEditor->m_Map.m_vpImages.empty()) pLayerQuads->m_Image = Value % m_pEditor->m_Map.m_vpImages.size(); else pLayerQuads->m_Image = -1; @@ -1730,7 +1730,7 @@ void CEditorActionEditLayerSoundsProp::Apply(int Value) std::shared_ptr pLayerSounds = std::static_pointer_cast(m_pLayer); if(m_Prop == ELayerSoundsProp::PROP_SOUND) { - if(Value >= 0) + if(Value >= 0 && !m_pEditor->m_Map.m_vpSounds.empty()) pLayerSounds->m_Sound = Value % m_pEditor->m_Map.m_vpSounds.size(); else pLayerSounds->m_Sound = -1; diff --git a/src/game/editor/popups.cpp b/src/game/editor/popups.cpp index 11801145b23..b9b1b85919b 100644 --- a/src/game/editor/popups.cpp +++ b/src/game/editor/popups.cpp @@ -1732,8 +1732,16 @@ CUi::EPopupMenuFunctionResult CEditor::PopupImage(void *pContext, CUIRect View, View.HSplitTop(RowHeight, &Slot, &View); if(pEditor->DoButton_MenuItem(&s_RemoveButton, "Remove", 0, &Slot, 0, "Removes the image from the map")) { - pEditor->m_Map.m_vpImages.erase(pEditor->m_Map.m_vpImages.begin() + pEditor->m_SelectedImage); - pEditor->m_Map.ModifyImageIndex(gs_ModifyIndexDeleted(pEditor->m_SelectedImage)); + if(IsAssetUsed(FILETYPE_IMG, pEditor->m_SelectedImage, pEditor)) + { + pEditor->m_PopupEventType = POPEVENT_REMOVE_USED_IMAGE; + pEditor->m_PopupEventActivated = true; + } + else + { + pEditor->m_Map.m_vpImages.erase(pEditor->m_Map.m_vpImages.begin() + pEditor->m_SelectedImage); + pEditor->m_Map.ModifyImageIndex(gs_ModifyIndexDeleted(pEditor->m_SelectedImage)); + } return CUi::POPUP_CLOSE_CURRENT; } @@ -1834,8 +1842,16 @@ CUi::EPopupMenuFunctionResult CEditor::PopupSound(void *pContext, CUIRect View, View.HSplitTop(RowHeight, &Slot, &View); if(pEditor->DoButton_MenuItem(&s_RemoveButton, "Remove", 0, &Slot, 0, "Removes the sound from the map")) { - pEditor->m_Map.m_vpSounds.erase(pEditor->m_Map.m_vpSounds.begin() + pEditor->m_SelectedSound); - pEditor->m_Map.ModifySoundIndex(gs_ModifyIndexDeleted(pEditor->m_SelectedSound)); + if(IsAssetUsed(FILETYPE_SOUND, pEditor->m_SelectedImage, pEditor)) + { + pEditor->m_PopupEventType = POPEVENT_REMOVE_USED_SOUND; + pEditor->m_PopupEventActivated = true; + } + else + { + pEditor->m_Map.m_vpSounds.erase(pEditor->m_Map.m_vpSounds.begin() + pEditor->m_SelectedSound); + pEditor->m_Map.ModifySoundIndex(gs_ModifyIndexDeleted(pEditor->m_SelectedSound)); + } return CUi::POPUP_CLOSE_CURRENT; } @@ -2066,6 +2082,16 @@ CUi::EPopupMenuFunctionResult CEditor::PopupEvent(void *pContext, CUIRect View, pTitle = "Too many colors"; pMessage = "The client only supports 64 images but more would be needed to add the selected image as tileart."; } + else if(pEditor->m_PopupEventType == POPEVENT_REMOVE_USED_IMAGE) + { + pTitle = "Remove image"; + pMessage = "This image is used in the map. Removing it will reset all layers that use this image to their default.\n\nRemove anyway?"; + } + else if(pEditor->m_PopupEventType == POPEVENT_REMOVE_USED_SOUND) + { + pTitle = "Remove sound"; + pMessage = "This sound is used in the map. Removing it will reset all layers that use this sound to their default.\n\nRemove anyway?"; + } else { dbg_assert(false, "m_PopupEventType invalid"); @@ -2172,6 +2198,16 @@ CUi::EPopupMenuFunctionResult CEditor::PopupEvent(void *pContext, CUIRect View, { pEditor->AddTileart(); } + else if(pEditor->m_PopupEventType == POPEVENT_REMOVE_USED_IMAGE) + { + pEditor->m_Map.m_vpImages.erase(pEditor->m_Map.m_vpImages.begin() + pEditor->m_SelectedImage); + pEditor->m_Map.ModifyImageIndex(gs_ModifyIndexDeleted(pEditor->m_SelectedImage)); + } + else if(pEditor->m_PopupEventType == POPEVENT_REMOVE_USED_SOUND) + { + pEditor->m_Map.m_vpSounds.erase(pEditor->m_Map.m_vpSounds.begin() + pEditor->m_SelectedSound); + pEditor->m_Map.ModifySoundIndex(gs_ModifyIndexDeleted(pEditor->m_SelectedSound)); + } pEditor->m_PopupEventWasActivated = false; return CUi::POPUP_CLOSE_CURRENT; }