diff --git a/src/gui/preview/PreviewView.cpp b/src/gui/preview/PreviewView.cpp index 401ce109a..05506539a 100644 --- a/src/gui/preview/PreviewView.cpp +++ b/src/gui/preview/PreviewView.cpp @@ -488,10 +488,24 @@ void PreviewView::ShowLoadError() void PreviewView::ShowMissingCustomElements() { StringBuilder sb; - sb << "This save uses custom elements that are not currently available. Make sure that you use the mod and/or have all the scripts the save requires to fully load. A list of identifiers of missing custom elements follows, which may help you determine how to fix this problem.\n"; - for (auto &identifier : missingElementTypes) + sb << "This save uses custom elements that are not currently available. Make sure that you use the mod and/or have all the scripts the save requires to fully load."; + auto remainingIds = missingElements.ids; + if (missingElements.identifiers.size()) { - sb << "\n - " << identifier.FromUtf8(); + sb << "\n\nA list of identifiers of missing custom elements follows, which may help you determine how to fix this problem.\n"; + for (auto &[ identifier, id ] : missingElements.identifiers) + { + sb << "\n - " << identifier.FromUtf8(); + remainingIds.erase(id); // remove ids from the missing id set that are already covered by unknown identifiers + } + } + if (remainingIds.size()) + { + sb << "\n\nA list of element IDs of missing custom elements with no identifier associated follows. This can only be fixed by the author of the save.\n"; + for (auto id : remainingIds) + { + sb << "\n - " << id; + } } new InformationMessage("Missing custom elements", sb.Build(), true); } @@ -556,10 +570,10 @@ void PreviewView::NotifySaveChanged(PreviewModel * sender) if(save->GetGameSave()) { - std::tie(savePreview, missingElementTypes) = SaveRenderer::Ref().Render(save->GetGameSave(), false, true); + std::tie(savePreview, missingElements) = SaveRenderer::Ref().Render(save->GetGameSave(), false, true); if (savePreview) savePreview->ResizeToFit(RES / 2, true); - missingElementsButton->Visible = missingElementTypes.size(); + missingElementsButton->Visible = missingElements.identifiers.size() || missingElements.ids.size(); UpdateLoadStatus(); } else if (!sender->GetCanOpen()) diff --git a/src/gui/preview/PreviewView.h b/src/gui/preview/PreviewView.h index 6fb40e2f1..e01a09163 100644 --- a/src/gui/preview/PreviewView.h +++ b/src/gui/preview/PreviewView.h @@ -4,6 +4,7 @@ #include #include "common/String.h" #include "gui/interface/Window.h" +#include "simulation/MissingElements.h" namespace http { @@ -27,7 +28,7 @@ class PreviewController; class PreviewView: public ui::Window { PreviewController *c{}; - std::vector missingElementTypes; + MissingElements missingElements; std::unique_ptr savePreview; ui::Button *openButton{}; ui::Button *browserOpenButton{}; diff --git a/src/simulation/MissingElements.h b/src/simulation/MissingElements.h new file mode 100644 index 000000000..6d6980c4a --- /dev/null +++ b/src/simulation/MissingElements.h @@ -0,0 +1,10 @@ +#pragma once +#include +#include +#include "common/String.h" + +struct MissingElements +{ + std::map identifiers; + std::set ids; +}; diff --git a/src/simulation/SaveRenderer.cpp b/src/simulation/SaveRenderer.cpp index 5626b45df..ad6709dae 100644 --- a/src/simulation/SaveRenderer.cpp +++ b/src/simulation/SaveRenderer.cpp @@ -20,7 +20,7 @@ void SaveRenderer::Flush(int begin, int end) std::fill(ren->graphicscache + begin, ren->graphicscache + end, gcache_item()); } -std::pair, std::vector> SaveRenderer::Render(const GameSave *save, bool decorations, bool fire, Renderer *renderModeSource) +std::pair, MissingElements> SaveRenderer::Render(const GameSave *save, bool decorations, bool fire, Renderer *renderModeSource) { std::lock_guard gx(renderMutex); diff --git a/src/simulation/SaveRenderer.h b/src/simulation/SaveRenderer.h index 68df598a3..d2a245339 100644 --- a/src/simulation/SaveRenderer.h +++ b/src/simulation/SaveRenderer.h @@ -5,6 +5,7 @@ #include #include "common/ExplicitSingleton.h" #include "common/String.h" +#include "MissingElements.h" class GameSave; class VideoBuffer; @@ -18,7 +19,7 @@ class SaveRenderer: public ExplicitSingleton { std::mutex renderMutex; public: SaveRenderer(); - std::pair, std::vector> Render(const GameSave *save, bool decorations = true, bool fire = true, Renderer *renderModeSource = nullptr); + std::pair, MissingElements> Render(const GameSave *save, bool decorations = true, bool fire = true, Renderer *renderModeSource = nullptr); void Flush(int begin, int end); virtual ~SaveRenderer(); }; diff --git a/src/simulation/Simulation.cpp b/src/simulation/Simulation.cpp index 84102eee5..74a0fa132 100644 --- a/src/simulation/Simulation.cpp +++ b/src/simulation/Simulation.cpp @@ -19,9 +19,9 @@ extern int Element_LOLZ_lolz[XRES/9][YRES/9]; extern int Element_LOVE_RuleTable[9][9]; extern int Element_LOVE_love[XRES/9][YRES/9]; -std::vector Simulation::Load(const GameSave *save, bool includePressure, Vec2 blockP) // block coordinates +MissingElements Simulation::Load(const GameSave *save, bool includePressure, Vec2 blockP) // block coordinates { - std::vector missingElementTypes; + MissingElements missingElements; auto partP = blockP * CELL; unsigned int pmapmask = (1<pmapbits)-1; @@ -54,11 +54,23 @@ std::vector Simulation::Load(const GameSave *save, bool includePress } else { - missingElementTypes.push_back(pi.first); + missingElements.identifiers.insert(pi); } } } } + auto paletteLookup = [&partMap, &missingElements](int type) { + if (type > 0 && type < PT_NUM) + { + auto carriedType = partMap[type]; + if (!carriedType) // type is not 0 so this shouldn't be 0 either + { + missingElements.ids.insert(type); + } + type = carriedType; + } + return type; + }; RecalcFreeParticles(false); @@ -86,7 +98,7 @@ std::vector Simulation::Load(const GameSave *save, bool includePress continue; } - tempPart.type = partMap[tempPart.type]; + tempPart.type = paletteLookup(tempPart.type); for (auto index : possiblyCarriesType) { if (elements[tempPart.type].CarriesTypeIn & (1U << index)) @@ -94,10 +106,7 @@ std::vector Simulation::Load(const GameSave *save, bool includePress auto *prop = reinterpret_cast(reinterpret_cast(&tempPart) + properties[index].Offset); auto carriedType = *prop & int(pmapmask); auto extra = *prop >> save->pmapbits; - if (carriedType >= 0 && carriedType < PT_NUM) - { - carriedType = partMap[carriedType]; - } + carriedType = paletteLookup(carriedType); *prop = PMAP(extra, carriedType); } } @@ -329,7 +338,7 @@ std::vector Simulation::Load(const GameSave *save, bool includePress air->ApproximateBlockAirMaps(); } - return missingElementTypes; + return missingElements; } std::unique_ptr Simulation::Save(bool includePressure, Rect partR) // particle coordinates diff --git a/src/simulation/Simulation.h b/src/simulation/Simulation.h index 2189f5a3f..194d197ed 100644 --- a/src/simulation/Simulation.h +++ b/src/simulation/Simulation.h @@ -11,6 +11,7 @@ #include "common/tpt-rand.h" #include "Element.h" #include "SimulationConfig.h" +#include "MissingElements.h" #include #include #include @@ -121,7 +122,7 @@ public: uint64_t frameCount; bool ensureDeterminism; - std::vector Load(const GameSave *save, bool includePressure, Vec2 blockP); // block coordinates + MissingElements Load(const GameSave *save, bool includePressure, Vec2 blockP); // block coordinates std::unique_ptr Save(bool includePressure, Rect partR); // particle coordinates void SaveSimOptions(GameSave &gameSave); SimulationSample GetSample(int x, int y);