feat(save): Speicherstand als Game-Baum

Autosave beim Betreten einer Etage und beim Beenden, ein Slot, bei Tod
geloescht. Der Save ist der Baum selbst — keine eigenen Save-Structs, die
man zweimal pflegt.

- Kacheln als ASCII-Zeilen wie ein Prefab, nicht 3200 Enum-Eintraege
- atomar geschrieben: tmp plus rename, der einzige Stand ueberlebt einen
  Abbruch
- fremdes Schema wird abgelehnt, die Datei bleibt liegen
- fehlende Mods verwerfen Eintraege, nie den Lauf
- Confirm-Screen vor dem Spiel, wenn der Save Eintraege verloren hat

Fuenf Befunde: die Ereigniswarteschlange gehoert nicht in den Save, die
Feldreihenfolge ist Teil des TOML-Formats, Serialize fuer Floor muss von
Hand, Confirm braucht on_no, und die Rueckfrage galt zuerst auch fuer
Mod-Ladewarnungen.
This commit is contained in:
2026-08-12 22:01:12 +02:00
parent 7fdd8bbb00
commit d09fef7069
15 changed files with 698 additions and 64 deletions

View File

@@ -3,12 +3,12 @@
//! Der Katalog ist **fest** und Engine-Vokabular ohne Namensraum. Ein Modder
//! kann ihn nicht erweitern; das ist Absicht (siehe Glossar, „Attribut").
use serde::Deserialize;
use serde::{Deserialize, Serialize};
/// Fester Katalog aus Ticket 01. `level` steht hier, weil Bedingungen und
/// Formeln es lesen — die Quelle fuer Aufstiegspunkte ist aber `base.level`
/// und nicht dieser gelesene Wert (Ticket 15).
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[repr(usize)]
pub enum Attribute {
@@ -88,7 +88,7 @@ impl Attribute {
/// Die Grundwerte eines Wesens. Bewusst ein Array statt vierzehn benannter
/// Felder: `get` und `set` sind damit je eine Zeile statt je eines
/// vierzehnarmigen `match`.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Attributes([f32; Attribute::COUNT]);
impl Attributes {