feat(editor): auto-save settings UI, multi-tile zone.json, toast notify

- Auto-save toast notification when auto-save fires
- Edit > Auto-Save Settings: enable/disable toggle, interval slider
  (60-900s), countdown timer display
- Zone manifest now scans output directory for all exported ADT tiles
  and includes them in zone.json (adjacent tiles no longer orphaned)
- Auto-save interval and enabled state exposed via EditorApp accessors
This commit is contained in:
Kelsi 2026-05-05 13:58:07 -07:00
parent a7e34ad102
commit 97da4c38f0
3 changed files with 35 additions and 0 deletions

View file

@ -101,6 +101,7 @@ void EditorApp::run() {
if (autoSaveTimer_ >= autoSaveInterval_) { if (autoSaveTimer_ >= autoSaveInterval_) {
autoSaveTimer_ = 0.0f; autoSaveTimer_ = 0.0f;
quickSave(); quickSave();
showToast("Auto-saved", 2.0f);
LOG_INFO("Auto-saved zone"); LOG_INFO("Auto-saved zone");
} }
} }
@ -969,10 +970,28 @@ void EditorApp::exportZone(const std::string& outputDir) {
} }
// Write zone manifest (for client loading) // Write zone manifest (for client loading)
// Scan output directory for all exported tiles (includes adjacent tiles)
ZoneManifest manifest; ZoneManifest manifest;
manifest.mapName = loadedMap_; manifest.mapName = loadedMap_;
manifest.displayName = loadedMap_; manifest.displayName = loadedMap_;
manifest.tiles.push_back({loadedTileX_, loadedTileY_}); manifest.tiles.push_back({loadedTileX_, loadedTileY_});
namespace fs = std::filesystem;
if (fs::exists(base)) {
for (auto& entry : fs::directory_iterator(base)) {
if (entry.path().extension() != ".adt") continue;
std::string stem = entry.path().stem().string();
auto lastU = stem.rfind('_');
auto prevU = stem.rfind('_', lastU - 1);
if (lastU != std::string::npos && prevU != std::string::npos) {
try {
int tx = std::stoi(stem.substr(prevU + 1, lastU - prevU - 1));
int ty = std::stoi(stem.substr(lastU + 1));
if (tx == loadedTileX_ && ty == loadedTileY_) continue;
manifest.tiles.push_back({tx, ty});
} catch (...) {}
}
}
}
manifest.hasCreatures = (npcSpawner_.spawnCount() > 0); manifest.hasCreatures = (npcSpawner_.spawnCount() > 0);
manifest.baseHeight = terrain_.chunks[0].position[2]; manifest.baseHeight = terrain_.chunks[0].position[2];
manifest.save(base + "/zone.json"); manifest.save(base + "/zone.json");

View file

@ -149,6 +149,11 @@ public:
void showToast(const std::string& msg, float duration = 3.0f); void showToast(const std::string& msg, float duration = 3.0f);
const std::vector<Toast>& getToasts() const { return toasts_; } const std::vector<Toast>& getToasts() const { return toasts_; }
const std::vector<RecentZone>& getRecentZones() const { return recentZones_; } const std::vector<RecentZone>& getRecentZones() const { return recentZones_; }
bool isAutoSaveEnabled() const { return autoSaveEnabled_; }
void setAutoSaveEnabled(bool v) { autoSaveEnabled_ = v; }
float getAutoSaveInterval() const { return autoSaveInterval_; }
void setAutoSaveInterval(float s) { autoSaveInterval_ = std::clamp(s, 60.0f, 900.0f); }
float getAutoSaveTimeRemaining() const { return autoSaveInterval_ - autoSaveTimer_; }
void updateToasts(float dt); void updateToasts(float dt);
private: private:
size_t lastObjCount_ = 0; size_t lastObjCount_ = 0;

View file

@ -375,6 +375,17 @@ void EditorUI::renderMenuBar(EditorApp& app) {
auto& te = app.getTerrainEditor(); auto& te = app.getTerrainEditor();
if (ImGui::MenuItem("Undo", "Ctrl+Z", false, te.history().canUndo())) te.undo(); if (ImGui::MenuItem("Undo", "Ctrl+Z", false, te.history().canUndo())) te.undo();
if (ImGui::MenuItem("Redo", "Ctrl+Shift+Z", false, te.history().canRedo())) te.redo(); if (ImGui::MenuItem("Redo", "Ctrl+Shift+Z", false, te.history().canRedo())) te.redo();
ImGui::Separator();
if (ImGui::BeginMenu("Auto-Save Settings")) {
bool enabled = app.isAutoSaveEnabled();
if (ImGui::Checkbox("Enabled", &enabled)) app.setAutoSaveEnabled(enabled);
float interval = app.getAutoSaveInterval();
if (ImGui::SliderFloat("Interval (sec)", &interval, 60.0f, 900.0f, "%.0fs"))
app.setAutoSaveInterval(interval);
ImGui::TextColored(ImVec4(0.6f, 0.6f, 0.6f, 1),
"Next in: %.0fs", app.getAutoSaveTimeRemaining());
ImGui::EndMenu();
}
ImGui::EndMenu(); ImGui::EndMenu();
} }
if (ImGui::BeginMenu("View")) { if (ImGui::BeginMenu("View")) {