S2: balance-Modul und Balance-Resource #15
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Part of #13
Setzt ADR-0001 um: Balancing-Daten als Rust-Konstanten in einem eigenen Modul
balance, das eineBalance-Resource befüllt. Gameplay-Systeme lesen ausschließlichRes<Balance>und sehen die Konstanten nie.Inhalt
src/balance/— nur Daten und Datenstrukturen, keine Gameplay-Logik, keine Bevy-Systeme.BalancealsResourceaus reinen Datenstrukturen, mit genau einer Funktion, die sie erzeugt. Diese Funktion ist die Naht, an der später ein Asset-Ladepfad ansetzen würde.capsals eigener Bereich vorsehen (ADR-0002 §2), auch wenn im Skelett noch nichts geclampt wird.Akzeptanz
Res<Balance>. Prüfbar per Suche nach den Konstantennamen außerhalb vonsrc/balance/.src/balance/ändert das Spielverhalten ohne weitere Codeänderung.Nicht in diesem Ticket: die Stat-Pipeline aus ADR-0002. Hier liegen nur die Rohwerte; wie Modifikatoren darauf wirken, kommt in der Verdickungsphase.
Gebaut.
src/main.rs,src/balance/{mod,values}.rs,src/arena.rs,src/tower.rs— 392 Zeilen, keine neuen Dependencies.Nachgeprüft, nicht geglaubt
cargo clean -p infinitowerplus vollständiger Rebuild: null Warnungen.cargo test:tower::tests::meta_run_meta_leaves_nothing_behindgrün.DespawnOnExitschlägt er fehl (left: 1, right: 0), danach wiederhergestellt und wieder grün. Er ist nicht vakuant.Zwei Dinge, die besser sind als beauftragt
ADR-0001 ist compilerdurchgesetzt, nicht nur per Konvention. Die Konstanten in
values.rssindpub(super);Balance::from_constants()ist der einzige Weg heraus. Die Regel „kein Gameplay liest eine Konstante" lässt sich damit nicht versehentlich brechen, sondern nur absichtlich.ADR-0002 §6 wird von einem Bevy-Built-in erfüllt. Statt eines Aufräum-Systems trägt die Turm-Entity
DespawnOnExit(AppState::Run). Die ADR forderte „kein Aufräum-Code, den man beim nächsten Feature erweitern müsste" — hier gibt es kein System, das man falsch einordnen oder vergessen könnte. Der Test zählt außerdem alle Entities, nicht nur Türme, und fängt damit auch künftige run-gebundene Entities, die jemand ohne Run-Bindung spawnt.Statt
#[allow(dead_code)]steht#[expect(dead_code, reason = "read by the gameplay tickets #16-#18")]ambalance-Modul — der Compiler fordert die Zeile zurück, sobald das letzte Feld einen Leser hat.Platzhalterwerte
arena.radiusperf_spike.rsenemy.speedperf_spike.rsenemy.healthtower.damagetower.attack_intervaltower.healthcombat.hit_radiusperf_spike.rsmeta.gain_per_killAlle acht sind im Code als
PLACEHOLDERausgezeichnet.Vier Befunde des Agenten, zur Kenntnis
AppState::Runist Default, damitcargo runohne Tastendruck einen sichtbaren Turm zeigt. Die Alternative — imStartupheimlichNextStatesetzen — verschluckt dasOnEnter(Meta)des ersten Frames und fiele bei #18 still auf die Füße. Mit// ponytail:markiert, Umdrehen ist eine Zeile und gehört zu #18.Balance.capsist ein leeres Struct. ADR-0002 §2 verlangt einen Cap je geclamptem Stat — die fünf betroffenen Stats existieren im Skelett noch nicht. Der Bereich ist benannt und erreichbar, nur ohne Felder. Vertagung, kein Widerspruch.tower.attack_intervalin Sekunden, weilmultsonst kein Teiler auf eine Dauer sein kann. Das Ticket-Wording ist überholt, nicht der Code — genau die Reihenfolge, die die Karte vorgibt.type Stat = f32liegt vorerst inbalance. ADR-0002 §4 definiert den Alias, ordnet ihn keinem Modul zu. Wenn die Stat-Pipeline kommt, gehört er vermutlich in ein eigenesstats-Modul.Der eine Punkt, der weiterwandert
meta.gain_per_killist ein flacher Betrag pro Kill — die Spec sagt aber, der Meta-Ertrag ist drop-basiert: Chance je Normalgegner, garantiert je Boss, Menge wellenabhängig. Das ist eine andere Mechanik, nicht bloß eine andere Zahl.Für ein Skelett ohne Wellen und ohne Bosse ist der flache Wert das einzig Baubare, und der Agent hat die Abweichung am Konstanten-Doc vermerkt statt sie zu verschlucken. Damit sie nicht zur stillschweigenden Mechanikentscheidung gegen die Spec wird, ist sie als ausdrückliche Auflage an #18 gehängt.