feat: m4 teil 1 — beute, inventar und affixe

- data/: [[loot_tables]] und [[affixes]] als Registries; LootEntry mit
  guaranteed, nothing, table-in-table, count und affixes
- game/loot.rs: EINE Ziehmaschinerie fuer Gegenstaende und Affixe,
  Tiefendeckel gegen wechselseitigen Bezug
- PickUp zieht erst beim Aufheben (Ticket 16) — moeglich nur, weil alle
  Haufen dasselbe Symbol zeigen
- UseItem/Equip/Unequip/Drop, Inventar-Screen als Registry-Schleife
- Affixe im Attributpfad, in der Resistenz und in der Trigger-Suche;
  item_label stellt Affixnamen voran, item_price multipliziert Faktoren
- Room haelt eine loot_table (nicht je Haufen eine), Prefab-loot
  ueberschreibt die Etagentabelle

53 Tests. Offen aus M4: Handel.
This commit is contained in:
2026-08-12 17:56:28 +02:00
parent cdcabcce21
commit 197f62ce33
16 changed files with 849 additions and 19 deletions

46
mods/core/affixes.toml Normal file
View File

@@ -0,0 +1,46 @@
# Affixe (Ticket 07/16): eine benannte Modifikatorliste, die an einem EINZELNEN
# Exemplar haengt. Strukturell dasselbe wie eine Charakteroption — der
# Unterschied ist, woran sie haengt.
#
# Abstufungen sind EIGENE EINTRAEGE, keine Zahl am Exemplar: Eine Staerke je
# Exemplar haette Save-Format (ID-Liste), Preisformel und Stapelregel auf einmal
# gekostet. Mit Stufen kann der Modder seine Abstufungen benennen.
[[affixes]]
id = "sharp"
name = "Scharfes"
price_factor = 1.3
modifiers = [ { attribute = "damage", flat = 2 } ]
[[affixes]]
id = "flaming"
name = "Flammendes"
price_factor = 1.6
modifiers = [ { attribute = "damage", flat = 3 } ]
triggers = [
{ on = "attack_hit", chance = 0.25,
actions = [ { action = "damage", amount = 4, damage_type = "fire", target = "target" } ] },
]
[[affixes]]
id = "greater_flaming"
name = "Stark flammendes"
price_factor = 2.4
modifiers = [ { attribute = "damage", flat = 6 } ]
triggers = [
{ on = "attack_hit", chance = 0.4,
actions = [ { action = "damage", amount = 9, damage_type = "fire", target = "target" } ] },
]
[[affixes]]
id = "sturdy"
name = "Robuste"
price_factor = 1.4
modifiers = [ { attribute = "max_health", flat = 12 } ]
[[affixes]]
id = "warding"
name = "Schirmende"
price_factor = 1.8
modifiers = [ { attribute = "resistance", damage_type = "fire", flat = 20 },
{ attribute = "resistance", damage_type = "ice", flat = 20 } ]

View File

@@ -16,6 +16,7 @@ rooms = 7
required = ["core:entrance", "core:stairs", "core:merchant"]
prefabs = [ { tag = "core:normal", weight = 10 } ]
creatures = [ { id = "core:rat", weight = 10 } ]
loot = "core:floor_1_table"
[[floors]]
id = "floor_2"
@@ -26,6 +27,7 @@ required = ["core:entrance", "core:stairs", "core:merchant"]
prefabs = [ { tag = "core:normal", weight = 10 },
{ tag = "core:treasure", weight = 2 } ]
creatures = [ { id = "core:rat", weight = 6 }, { id = "core:skeleton", weight = 6 } ]
loot = "core:floor_1_table"
[[floors]]
id = "floor_3"
@@ -36,6 +38,7 @@ required = ["core:entrance", "core:stairs", "core:merchant"]
prefabs = [ { tag = "core:normal", weight = 10 },
{ tag = "core:treasure", weight = 3 } ]
creatures = [ { id = "core:rat", weight = 3 }, { id = "core:skeleton", weight = 8 }, { id = "core:ghoul", weight = 3 } ]
loot = "core:floor_3_table"
[[floors]]
id = "floor_4"
@@ -46,6 +49,7 @@ required = ["core:entrance", "core:stairs", "core:merchant"]
prefabs = [ { tag = "core:normal", weight = 10 },
{ tag = "core:treasure", weight = 3 } ]
creatures = [ { id = "core:skeleton", weight = 6 }, { id = "core:ghoul", weight = 8 } ]
loot = "core:floor_3_table"
[[floors]]
id = "floor_5"
@@ -56,3 +60,4 @@ required = ["core:entrance", "core:stairs", "core:merchant"]
prefabs = [ { tag = "core:normal", weight = 10 },
{ tag = "core:treasure", weight = 4 } ]
creatures = [ { id = "core:ghoul", weight = 8 }, { id = "core:bone_lord", weight = 2 } ]
loot = "core:floor_5_table"

View File

@@ -53,3 +53,13 @@ triggers = [
when = { damage_type = "fire" },
actions = [ { action = "damage", amount = 3, damage_type = "fire", target = "target" } ] },
]
[[items]]
id = "bone_blade"
name = "Knochenklinge"
base_price = 210
slot = "core:hand"
symbol = "/"
color = "magenta"
modifiers = [ { attribute = "damage", flat = 11 },
{ attribute = "critical_chance", flat = 8 } ]

88
mods/core/loot.toml Normal file
View File

@@ -0,0 +1,88 @@
# Beutetabellen (Ticket 16). EINE Maschinerie fuer Gegenstaende und Affixe:
# dieselben Gewichte, dieselbe Tabelle-in-Tabelle, dieselben Nieten.
#
# Gewichte statt Prozent — Prozente muessten sich auf 100 summieren, und ein
# Eintrag, den ein Mod hinzufuegt, macht die Summe kaputt.
#
# Der MOD-EINHAENGEPUNKT ist Tabelle-in-Tabelle: Ein Mod legt seine eigene
# Tabelle an, bindet `core:floor_3_table` als Untertabelle ein und haengt die
# Etage um. Zwei gewoehnliche Ueberschreibungen, kein zweiter Mechanismus.
# --- Affixe ---------------------------------------------------------------
# Abstufungen sind EIGENE Eintraege mit kleinerem Gewicht, keine Zahl am
# Exemplar. Deshalb erhoeht ein zusaetzlicher Zug auch die Chance auf die
# staerkere Stufe.
[[loot_tables]]
id = "weapon_affixes"
rolls = "0-1"
entries = [
{ affix = "core:sharp", weight = 6 },
{ affix = "core:flaming", weight = 3 },
{ affix = "core:greater_flaming", weight = 1 },
{ nothing = true, weight = 14 },
]
[[loot_tables]]
id = "armor_affixes"
rolls = "0-1"
entries = [
{ affix = "core:sturdy", weight = 5 },
{ affix = "core:warding", weight = 2 },
{ nothing = true, weight = 16 },
]
# --- Etagen ---------------------------------------------------------------
[[loot_tables]]
id = "floor_1_table"
rolls = "1-2"
entries = [
{ item = "core:health_potion", weight = 12, count = "1-2" },
{ item = "core:gold", weight = 10, count = "3-9" },
{ item = "core:iron_sword", weight = 2, affixes = "core:weapon_affixes" },
{ item = "core:leather_cap", weight = 2, affixes = "core:armor_affixes" },
{ nothing = true, weight = 8 },
]
[[loot_tables]]
id = "floor_3_table"
rolls = "1-3"
entries = [
{ item = "core:health_potion", weight = 10, count = "1-2" },
{ item = "core:gold", weight = 10, count = "6-18" },
{ item = "core:iron_sword", weight = 3, affixes = "core:weapon_affixes" },
{ item = "core:thorn_mail", weight = 1, affixes = "core:armor_affixes" },
{ table = "core:rare_table", weight = 2 },
{ nothing = true, weight = 6 },
]
[[loot_tables]]
id = "floor_5_table"
rolls = "2-3"
entries = [
{ item = "core:health_potion", weight = 8, count = "2-3" },
{ item = "core:gold", weight = 10, count = "15-40" },
{ item = "core:iron_sword", weight = 4, affixes = "core:weapon_affixes" },
{ item = "core:thorn_mail", weight = 3, affixes = "core:armor_affixes" },
{ table = "core:rare_table", weight = 4 },
]
[[loot_tables]]
id = "rare_table"
rolls = "1-1"
entries = [
{ item = "core:bone_blade", weight = 3, affixes = "core:weapon_affixes" },
{ item = "core:thorn_mail", weight = 4, affixes = "core:armor_affixes" },
]
# `guaranteed` nimmt an der Gewichtung nicht teil und kommt immer — mit
# Gewichten allein laesst sich "sicher" nicht ausdruecken.
[[loot_tables]]
id = "boss_table"
rolls = "1-2"
entries = [
{ item = "core:bone_blade", guaranteed = true, affixes = "core:weapon_affixes" },
{ item = "core:gold", weight = 10, count = "50-120" },
{ item = "core:health_potion", weight = 6, count = "2-4" },
]