feat: m3 kampf, events und trigger
Der Dungeon hat jetzt Gegner, und man kann sterben.
- game/action.rs: sieben Schadenstypen, fuenf der zehn Aktionen, Target
- game/condition.rs: Bedingungsgrammatik (Liste = UND, not je Eintrag)
- game/event.rs: zwoelf Events, Trigger als vierte Wirkungsform
- game/combat.rs: Treffer- und Schadensformel mit drei Klemmbereichen
- Game::apply, drain_events (FIFO), Initiative, Rundenablauf aus Ticket 14
- data/: abilities-, creatures- und modifiers-Registry, [balance] feldweise
- ui/: Combat-Screen mit Faehigkeitsmenue und Sperrgrund
Drei Befunde:
- Die Trefferformel war toter Code — jeder Angriff traf. Nur der Compiler
hat es gemeldet ('hit_chance is never used'). Test haelt sie am Leben.
- Die Warteschlange war ein Stack (pop statt pop_front), obwohl Ticket 08
ausdruecklich 'haengt es hinten an' sagt. Jetzt VecDeque.
- Benannte Modifikatoren (core:poisoned) hatten keine Registry — Ticket 01
laesst apply_modifier per ID referenzieren, aber keine der zwoelf
definierte sie. Das ist die dreizehnte.
43 Tests.
This commit is contained in:
58
mods/core/abilities.toml
Normal file
58
mods/core/abilities.toml
Normal file
@@ -0,0 +1,58 @@
|
||||
# Eine Faehigkeit ist ein benanntes, wiederholt ausloesbares ANGEBOT an den
|
||||
# Spieler, das Aktionen ausfuehrt (Ticket 02) — kein Gegenstand im Inventar,
|
||||
# sonst koennte ein Magier seine Zauber verkaufen.
|
||||
#
|
||||
# Angriff und Flucht sind bewusst keine Engine-Sonderfaelle, sondern gewoehnliche
|
||||
# Faehigkeiten (Ticket 11/14). Ein Mod kann beide ersetzen.
|
||||
#
|
||||
# Menge = amount + scale * damage-Attribut(Verursacher) — eine Formel fuer
|
||||
# Basisangriff, Feuerball und Wuchtschlag (Nachtrag aus Ticket 14).
|
||||
|
||||
[[abilities]]
|
||||
id = "attack"
|
||||
name = "Angriff"
|
||||
cost = 0
|
||||
actions = [ { action = "damage", scale = 1.0, damage_type = "slash", target = "target" } ]
|
||||
|
||||
[[abilities]]
|
||||
id = "flee"
|
||||
name = "Fliehen"
|
||||
cost = 10
|
||||
actions = [ { action = "flee" } ]
|
||||
|
||||
[[abilities]]
|
||||
id = "power_strike"
|
||||
name = "Wuchtschlag"
|
||||
description = "Holt weit aus. Kostet Luft."
|
||||
cost = 15
|
||||
cooldown = 3
|
||||
actions = [ { action = "damage", scale = 1.5, damage_type = "blunt", target = "target" } ]
|
||||
|
||||
[[abilities]]
|
||||
id = "bone_throw"
|
||||
name = "Knochenwurf"
|
||||
cost = 8
|
||||
cooldown = 2
|
||||
actions = [ { action = "damage", amount = 6, damage_type = "blunt", target = "target" } ]
|
||||
|
||||
[[abilities]]
|
||||
id = "rat_bite"
|
||||
name = "Biss"
|
||||
cost = 0
|
||||
actions = [ { action = "damage", scale = 1.0, damage_type = "slash", target = "target" },
|
||||
{ action = "apply_modifier", modifier = "core:poisoned", target = "target" } ]
|
||||
|
||||
[[abilities]]
|
||||
id = "ghoul_claw"
|
||||
name = "Klauenhieb"
|
||||
cost = 0
|
||||
actions = [ { action = "damage", scale = 1.2, damage_type = "slash", target = "target" } ]
|
||||
|
||||
[[abilities]]
|
||||
id = "second_wind"
|
||||
name = "Zweiter Atem"
|
||||
description = "Sammelt sich, wenn es eng wird."
|
||||
cost = 0
|
||||
cooldown = 8
|
||||
condition = [ { check = "health_below", who = "self", value = 0.4 } ]
|
||||
actions = [ { action = "change_attribute", attribute = "health", amount = 25, target = "self" } ]
|
||||
20
mods/core/balance.toml
Normal file
20
mods/core/balance.toml
Normal file
@@ -0,0 +1,20 @@
|
||||
# Die Stellknoepfe aus Ticket 14 und 16.
|
||||
#
|
||||
# `[balance]` ist KEINE Registry: ein einzelner Block mit festem Schema, der
|
||||
# FELDWEISE verschmilzt statt als ganzer Eintrag ersetzt zu werden — die
|
||||
# ausdrueckliche Ausnahme zur Ganz-oder-gar-nicht-Regel aus Ticket 03.
|
||||
#
|
||||
# Die Begruendung bleibt eng: Das Schema ist der Engine bekannt. Genau das
|
||||
# fehlte bei Inhalt mit offenen Feldern, und das war Ticket 03s Argument gegen
|
||||
# feldweises Patchen.
|
||||
#
|
||||
# Ein Mod, der einen Knopf dreht, schreibt EINE Zeile. Standardwerte liegen in
|
||||
# der Engine — was hier fehlt, gilt trotzdem.
|
||||
|
||||
[balance]
|
||||
ausweich = 0.02 # wie stark dexterity des Ziels die Trefferchance senkt
|
||||
flucht = 0.05 # wie stark der Geschicklichkeitsunterschied die Flucht traegt
|
||||
stamina_regen = 4.0 # pro Zug — gibt `warten` seinen Zweck
|
||||
resistance_cap = 0.8 # 1.0 waere Immunitaet, und das ist ein Spielabbruch
|
||||
hit_floor = 0.05
|
||||
hit_ceiling = 0.95
|
||||
63
mods/core/creatures.toml
Normal file
63
mods/core/creatures.toml
Normal file
@@ -0,0 +1,63 @@
|
||||
# Gegner. Die Luecke lag seit Ticket 02 offen — `Creature` war der Laufzeittyp,
|
||||
# woher seine Werte kommen, hat nie jemand gesagt (Ticket 14).
|
||||
#
|
||||
# `ai` ist eine GEORDNETE Liste: der erste passende UND verfuegbare Eintrag
|
||||
# gewinnt. Ein Eintrag ohne `condition` greift immer und gehoert ans Ende —
|
||||
# fehlt er, setzt der Gegner gelegentlich aus.
|
||||
|
||||
[[creatures]]
|
||||
id = "rat"
|
||||
name = "Riesenratte"
|
||||
symbol = "r"
|
||||
color = "yellow"
|
||||
tags = ["core:beast"]
|
||||
base = { max_health = 14, damage = 3, hit_chance = 0.65, dexterity = 4, critical_chance = 5 }
|
||||
abilities = ["core:rat_bite"]
|
||||
ai = [ { ability = "core:rat_bite" } ]
|
||||
exp_reward = 4
|
||||
|
||||
[[creatures]]
|
||||
id = "skeleton"
|
||||
name = "Skelett"
|
||||
symbol = "s"
|
||||
color = "white"
|
||||
tags = ["core:undead"]
|
||||
base = { max_health = 26, damage = 6, hit_chance = 0.7, dexterity = 3, critical_chance = 5 }
|
||||
abilities = ["core:attack", "core:bone_throw"]
|
||||
ai = [
|
||||
{ condition = [ { check = "health_below", who = "self", value = 0.35 } ],
|
||||
ability = "core:bone_throw" },
|
||||
{ ability = "core:attack" },
|
||||
]
|
||||
exp_reward = 9
|
||||
|
||||
[[creatures]]
|
||||
id = "ghoul"
|
||||
name = "Ghul"
|
||||
symbol = "G"
|
||||
color = "magenta"
|
||||
tags = ["core:undead"]
|
||||
base = { max_health = 40, damage = 9, hit_chance = 0.72, dexterity = 5, critical_chance = 10 }
|
||||
abilities = ["core:ghoul_claw", "core:second_wind"]
|
||||
ai = [
|
||||
{ condition = [ { check = "health_below", who = "self", value = 0.4 } ],
|
||||
ability = "core:second_wind" },
|
||||
{ ability = "core:ghoul_claw" },
|
||||
]
|
||||
exp_reward = 18
|
||||
|
||||
[[creatures]]
|
||||
id = "bone_lord"
|
||||
name = "Knochenfürst"
|
||||
symbol = "K"
|
||||
color = "red"
|
||||
tags = ["core:undead", "core:boss"]
|
||||
base = { max_health = 90, damage = 14, hit_chance = 0.78, dexterity = 6, critical_chance = 12, critical_factor = 2.0 }
|
||||
abilities = ["core:attack", "core:power_strike", "core:second_wind"]
|
||||
ai = [
|
||||
{ condition = [ { check = "health_below", who = "self", value = 0.3 } ],
|
||||
ability = "core:second_wind" },
|
||||
{ ability = "core:power_strike" },
|
||||
{ ability = "core:attack" },
|
||||
]
|
||||
exp_reward = 60
|
||||
@@ -15,6 +15,7 @@ size = [72, 34]
|
||||
rooms = 7
|
||||
required = ["core:entrance", "core:stairs", "core:merchant"]
|
||||
prefabs = [ { tag = "core:normal", weight = 10 } ]
|
||||
creatures = [ { id = "core:rat", weight = 10 } ]
|
||||
|
||||
[[floors]]
|
||||
id = "floor_2"
|
||||
@@ -24,6 +25,7 @@ rooms = 8
|
||||
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 } ]
|
||||
|
||||
[[floors]]
|
||||
id = "floor_3"
|
||||
@@ -33,6 +35,7 @@ rooms = 9
|
||||
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 } ]
|
||||
|
||||
[[floors]]
|
||||
id = "floor_4"
|
||||
@@ -42,6 +45,7 @@ rooms = 10
|
||||
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 } ]
|
||||
|
||||
[[floors]]
|
||||
id = "floor_5"
|
||||
@@ -51,3 +55,4 @@ rooms = 11
|
||||
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 } ]
|
||||
|
||||
43
mods/core/modifiers.toml
Normal file
43
mods/core/modifiers.toml
Normal file
@@ -0,0 +1,43 @@
|
||||
# Benannte, befristete Modifikatoren — die dreizehnte Registry.
|
||||
#
|
||||
# Ticket 01 laesst `apply_modifier` einen Modifikator per ID referenzieren
|
||||
# (`core:poisoned`), aber keine der zwoelf Registries definierte sie. Aufgefallen
|
||||
# beim Bauen von M3.
|
||||
#
|
||||
# Der Schadenstyp `poison` ist NICHT der Modifikator `core:poisoned`: Der Typ
|
||||
# sagt, wie gemindert wird; der Modifikator ist ein Zustand, der ueber Zuege
|
||||
# tickt (Ticket 01).
|
||||
#
|
||||
# Eine Uhr: der Zug. Debuffs ticken auch beim Erkunden, damit "ich warte den
|
||||
# Debuff draussen ab" eine Entscheidung mit Kosten bleibt.
|
||||
|
||||
[[modifiers]]
|
||||
id = "poisoned"
|
||||
name = "Vergiftet"
|
||||
symbol = "☠"
|
||||
color = "green"
|
||||
turns = 4
|
||||
attributes = [ { attribute = "damage", percent = -15 } ]
|
||||
triggers = [
|
||||
{ on = "turn_started",
|
||||
actions = [ { action = "damage", amount = 2, damage_type = "poison", target = "self" } ] },
|
||||
]
|
||||
|
||||
[[modifiers]]
|
||||
id = "blessed"
|
||||
name = "Gesegnet"
|
||||
symbol = "+"
|
||||
color = "yellow"
|
||||
turns = 6
|
||||
attributes = [ { attribute = "damage", percent = 20 },
|
||||
{ attribute = "critical_chance", flat = 10 } ]
|
||||
|
||||
[[modifiers]]
|
||||
id = "warded"
|
||||
name = "Abgeschirmt"
|
||||
symbol = "o"
|
||||
color = "cyan"
|
||||
turns = 5
|
||||
# Resistenz ist typ-indiziert und der EINZIGE Minderungsmechanismus (Ticket 01).
|
||||
attributes = [ { attribute = "resistance", damage_type = "fire", flat = 40 },
|
||||
{ attribute = "resistance", damage_type = "ice", flat = 40 } ]
|
||||
Reference in New Issue
Block a user