feat: m1 vertikaler durchstich — mod-lader, registries, attributberechnung, screen-stack
Ersetzt src/ vollstaendig: print_box, include_str! und enum ItemType fallen, wie Ticket 01-03 es vorgezeichnet haben. - data/: Mod-Lader mit zwei Suchpfaden, selbstheilender load_order.toml, drei Phasen und zweistufiger Deserialisierung (03/04) - game/: Attributkatalog, Creature, Game::attribute ueber mehrere Quellen, Command mit Outcome (01/07/11/12) - ui/: Screen-Stack mit Response, Grid-Widget, Press-Filter (06/11/12) - mods/core/: echte TOML-Dateien auf der Platte statt include_str! 10 Tests, darunter der Borrow-Beweis fuer Registries-neben-Game, den Ticket 12 nicht fuehren konnte.
This commit is contained in:
116
src/data/registries.rs
Normal file
116
src/data/registries.rs
Normal file
@@ -0,0 +1,116 @@
|
||||
//! Die Definitionen, die aus TOML geladen werden — Ticket 02.
|
||||
//!
|
||||
//! Einige Felder liest in M1 noch niemand: `base_price` wartet auf den Handel,
|
||||
//! `stackable` und `consumed_on_use` aufs Inventar, `symbol` auf Gegenstaende,
|
||||
//! die auf dem Boden liegen. Sie stehen trotzdem hier, weil sie **das
|
||||
//! Datenmodell** sind und `mods/core` sie bereits schreibt — sie erst spaeter
|
||||
//! einzufuehren hiesse, die TOML-Dateien zweimal anzufassen.
|
||||
//!
|
||||
//! ponytail: modulweites `allow` — es versteckt ab M2 auch echte Leichen.
|
||||
//! Raus, sobald Handel und Inventar die Felder tatsaechlich lesen.
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::game::Modifier;
|
||||
|
||||
/// Ein Ding im Spiel, **einheitlich strukturiert**. Es gibt keinen Item-Typ:
|
||||
/// `enum ItemType` vermischte Slot, Verhalten und Kategorie (Ticket 02).
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct Item {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
#[serde(default)]
|
||||
pub base_price: u32,
|
||||
/// Leer = nicht ausruestbar.
|
||||
pub slot: Option<String>,
|
||||
#[serde(default)]
|
||||
pub stackable: bool,
|
||||
#[serde(default)]
|
||||
pub consumed_on_use: bool,
|
||||
#[serde(default = "default_symbol")]
|
||||
pub symbol: char,
|
||||
#[serde(default)]
|
||||
pub color: Option<String>,
|
||||
#[serde(default)]
|
||||
pub modifiers: Vec<Modifier>,
|
||||
/// Ersetzt den genannten Eintrag und uebernimmt dessen ID (Ticket 03).
|
||||
#[serde(default)]
|
||||
pub overrides: Option<String>,
|
||||
// TODO(M3+): `actions`, `abilities`, `triggers` — brauchen erst Kampf und
|
||||
// Event-System.
|
||||
}
|
||||
|
||||
fn default_symbol() -> char {
|
||||
'?'
|
||||
}
|
||||
|
||||
/// Eine benannte Stelle am Charakter. Moddbarer Inhalt, kein fester
|
||||
/// Aufzaehlungswert (Ticket 02).
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct Slot {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
#[serde(default)]
|
||||
pub order: i32,
|
||||
#[serde(default)]
|
||||
pub overrides: Option<String>,
|
||||
}
|
||||
|
||||
/// Kategorie von Charakteroptionen — selbst Inhalt, mit `pick = N`.
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct Category {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
#[serde(default)]
|
||||
pub order: i32,
|
||||
#[serde(default = "one")]
|
||||
pub pick: u32,
|
||||
#[serde(default)]
|
||||
pub overrides: Option<String>,
|
||||
}
|
||||
|
||||
fn one() -> u32 {
|
||||
1
|
||||
}
|
||||
|
||||
/// Eine Wahl beim Charakterbau. Rasse und Klasse sind beides davon.
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct CharacterOption {
|
||||
pub id: String,
|
||||
pub category: String,
|
||||
pub name: String,
|
||||
#[serde(default)]
|
||||
pub description: String,
|
||||
#[serde(default)]
|
||||
pub modifiers: Vec<Modifier>,
|
||||
#[serde(default)]
|
||||
pub overrides: Option<String>,
|
||||
// TODO(M3): `abilities` und `on_create` (Ticket 02) — Faehigkeiten und
|
||||
// Startausruestung brauchen Kampf bzw. Inventar.
|
||||
}
|
||||
|
||||
/// Alle geladenen Definitionen. Liegt **neben** `Game`, nicht darin — als Feld
|
||||
/// kollidierte `&mut game.player` mit `&game.registries` bei jedem
|
||||
/// Methodenaufruf (Ticket 07).
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Registries {
|
||||
pub items: HashMap<String, Item>,
|
||||
pub slots: HashMap<String, Slot>,
|
||||
pub categories: HashMap<String, Category>,
|
||||
pub options: HashMap<String, CharacterOption>,
|
||||
// TODO(M2+): `affixes`, `abilities`, `quests`, `creatures`, `prefabs`,
|
||||
// `floors`, `loot_tables`, `scripts` — zwoelf sind es am Ende.
|
||||
}
|
||||
|
||||
impl Registries {
|
||||
/// Slots in Anzeigereihenfolge. Der Charakterbogen schleift darueber,
|
||||
/// statt ein Layout zu mauern (Ticket 02).
|
||||
pub fn slots_ordered(&self) -> Vec<&Slot> {
|
||||
let mut v: Vec<&Slot> = self.slots.values().collect();
|
||||
v.sort_by_key(|s| (s.order, s.id.clone()));
|
||||
v
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user