158 lines
9.5 KiB
Markdown
158 lines
9.5 KiB
Markdown
# Elements Autochess
|
||
|
||
Auto-Battler nach TFT-Vorbild in Godot 4.7, in 3D.
|
||
Primitive Assets: Hexfelder sind Zylinder mit 6 Segmenten, Einheiten sind
|
||
Würfel (Tank), Kegel (DPS) und Kugeln (Heiler). Alles wird im Code erzeugt.
|
||
|
||
## Starten
|
||
|
||
godot --path . scenes/Main.tscn
|
||
|
||
## Steuern
|
||
|
||
- Shop-Karte anklicken = kaufen. Neutrale Avatare fragen dabei nach der
|
||
Spezialisierung, Element-Avatare kommen fertig.
|
||
- Die Bank sind die 9 Hexplätze vor dem eigenen Spielfeld, ebenfalls in 3D.
|
||
- Avatare per Drag & Drop verschieben: Bank -> Feld, Feld -> Bank, Feld -> Feld
|
||
und Bank -> Bank. Ist das Ziel belegt, tauschen die beiden die Plätze.
|
||
- Klick ohne Ziehen = auswählen. Info rechts, dort auch `Aufwerten`.
|
||
- Rechtsklick auf dem Feld = zurück auf die Bank, auf der Bank = verkaufen.
|
||
- Klicks treffen über einen Strahl von der Kamera auf die Feldebene.
|
||
- `D` neu würfeln, `F` Shop einfrieren, `X` XP kaufen, `Leertaste` Kampf, `P` Pause, `Esc` Auswahl lösen
|
||
- Rechtsklick auf eine neutrale Shop-Karte kauft direkt mit der zuletzt gewählten Spezialisierung
|
||
- Spielername rechts anklicken = sein Brett oben ansehen. Der nächste Gegner steht schon in der Planung fest.
|
||
- `Q`/`E` oder mittlere Maustaste drehen die Kamera, das Mausrad zoomt
|
||
- Die Planung dauert 30 Sekunden, dann startet der Kampf von selbst
|
||
- Startbildschirm: Bots leicht/normal/schwer, Lautstärke. Beides wird in `user://settings.cfg` gespeichert.
|
||
- Maus über einer Einheit oder Shop-Karte zeigt Werte und Fähigkeit, auch bei Gegnern im Kampf.
|
||
- In den ersten zwei Runden steht unten ein Tipp, was als Nächstes dran ist.
|
||
- Wer ausscheidet, kann zuschauen (Runden laufen dann in 3 s) oder neu starten.
|
||
- `1`–`5` kaufen den jeweiligen Shop-Platz. `Z` macht den letzten Verkauf 5 s lang rückgängig.
|
||
- Die gewählte Einheit zeigt ihre Reichweite auf den Feldern: grün Heilung, orange Angriff.
|
||
- Runde 1 und 2 gehen gegen die Wildnis, danach gegeneinander.
|
||
- Der Spielstand wird zu jedem Planungsbeginn und bei Pause gespeichert. Der Startbildschirm
|
||
bietet dann „Weiterspielen". Am Spielende steht eine Statistik.
|
||
|
||
## Regeln
|
||
|
||
| Anforderung | Umsetzung |
|
||
| --- | --- |
|
||
| 1-Star bis 3-Star, zweimal aufwertbar | `Avatar.star`, `Data.MAX_STAR` |
|
||
| Tank / DPS / Heiler | `Data.Role`, eigene Werte und Fähigkeiten |
|
||
| Spezialisierung beim Kauf | Popup beim Kauf eines neutralen Avatars |
|
||
| Weitere Spezialisierung beim Aufwerten | Popup beim Upgrade, bis zu 3 Elemente |
|
||
| Shop startet elementlos | `PlayerState.unlocked` beginnt leer |
|
||
| Aufgestellte Elemente kommen in den Shop | `PlayerState.unlock()` beim Aufstellen |
|
||
| Mein Shop zeigt nur meine Elemente und Neutral | `PlayerState.shop_elements()` je Spieler |
|
||
| Neutrale Chance -25% je aufgestelltem Element | `Data.neutral_chance()`, multiplikativ |
|
||
| Bank ist 3D am Spielfeld | `BoardView3D.bench_pos()`, 9 Hexplätze |
|
||
| Avatare zwischen Bank und Board verschieben | `PlayerState.move_slot()` |
|
||
| Drag & Drop | `Main._begin_drag()` / `_end_drag()`, Avatar klebt flach am Zeiger |
|
||
| Aufwertung zeigt, was sich ändert | Popup nennt die neue Fähigkeit, je Auswahl eine Zeile |
|
||
| Zinsen je 10 Gold bis 50 Gold | `Data.interest()`, max +5 |
|
||
| Neutral 1 Gold, Element 2 Gold | `Data.PRICE_NONE` / `PRICE_ELEMENT` |
|
||
| 1-Star Feuer + 2 Feuer = 2-Star | `Avatar.upgrade_cost()` |
|
||
| 2-Star Feuer/Wind + 3 Feuer + 3 Wind = 3-Star | `Avatar.upgrade_cost()` |
|
||
| Stats und Fähigkeiten pro Stufe | `Data.STAR_MULT`, `Avatar.ability_text()`, `Combat._cast()` |
|
||
| Elemente kombinierbar wie Traits | `Data.ELEMENT_TRAITS`, `Data.COMBO_TRAITS` |
|
||
| 5 Gold pro Runde, 1 für Sieg, mehr bei Serie | `PlayerState.round_income()` |
|
||
| 100 Leben | `Data.START_HP` |
|
||
| Begrenzte Elementmenge für alle Spieler | `Pool.element_left`, 100 pro Element |
|
||
| 7 Bots | `Game.BOT_NAMES`, `BotAI` |
|
||
| Keine Items | nicht vorhanden |
|
||
|
||
Nicht in den Anforderungen, aber „Basis wie TFT":
|
||
|
||
- **Level statt fester Kaderplätze.** Start Level 3, jede Runde 2 XP, 4 XP für 4 Gold.
|
||
Level = Kaderplätze, Maximum 9 (`Data.XP_TABLE`, `PlayerState.buy_xp()`).
|
||
- **Schaden steigt mit der Runde.** Grundschaden 2, alle vier Runden +1, dazu die
|
||
Sternsumme der Überlebenden (`Data.round_base_damage()`, `Combat.survivor_stars`).
|
||
- **Planungsuhr** 30 s (`Game.tick_planning()`), Tests schalten sie ab.
|
||
- **Gegner ansehen** über die Spielerliste (`Main._on_scout()`, `BoardView3D.scout_player`).
|
||
- **Heiler heilen nur im Umkreis 2** (`Data.HEAL_RANGE`), Aufstellung zählt also.
|
||
- **Lesbarer Kampf**: Schadens- und Heilzahlen, Krit mit „!", Sterbe-Animation,
|
||
Zustände als Tönung und Marke (B Brand, S langsam, Z betäubt), Schildstreifen.
|
||
- **Klänge** werden im Code erzeugt (`Sfx`), keine Audiodateien.
|
||
- **Sichtbare Angriffe**: Fernkampf schickt eine Kugel, Nahkampf einen Hieb (`BoardView3D.spawn_attack`).
|
||
- **Element-Profile**: das Hauptelement prägt die Werte, z. B. Feuer +18% Angriff, Erde +22% Leben
|
||
(`Data.ELEMENT_PROFILE`). Dazu ein Kennzeichen am Körper: Flamme, Tropfen, Finne, Zacke, Schultern, Kristall.
|
||
- **Gegner vorab**: Paarungen entstehen beim Rundenstart (`Game._make_pairings` in `_next_round`).
|
||
Bots stellen dagegen auf: Tanks vor gegnerische Schützen, Schützen weg von Tanks (`BotAI.arrange`).
|
||
- **Shop einfrieren** (`PlayerState.shop_locked`), Schnellkauf mit der letzten Wahl (`last_element`).
|
||
- **Rückblick** nach jedem Kampf: Ergebnis, Überlebende, stärkste Einheit, gefährlichster Gegner
|
||
(`Combat.recap`, `Game.human_recap`).
|
||
- **Start, Pause, Lautstärke** als Overlays in `Main`, Kamera mit Zoom und Drehung um die Brettmitte.
|
||
- **Element-Formen**: das Hauptelement gibt der Fähigkeit jeder Rolle ihre Form (`Data.ELEMENT_FORM`,
|
||
`Combat._apply_form`). Feuer trifft Nachbarn und entzündet, Wasser gibt Mana, Wind beschleunigt
|
||
Verbündete, Blitz springt weiter, Erde betäubt um das Ziel, Eis verlangsamt um das Ziel.
|
||
- **Fähigkeiten sichtbar**: Schildring, Fläche am Ziel, Heilring (`BoardView3D.spawn_effect`).
|
||
- **Gehen**: Einheiten drehen sich zur Laufrichtung oder zum Ziel und wippen beim Schritt.
|
||
- **Schwierigkeit** steuert Gier und Jagdverhalten der Bots (`Data.BOT_GREED`, `BotAI.setup`).
|
||
- **Bot-Heiler** stehen in Reihe 1–2 hinter den Tanks, sonst reicht ihre Heilung nicht (`BotAI.arrange`).
|
||
- **Rückgängig** (`PlayerState.undo_sell`) nimmt die Bausteine wieder aus dem Vorrat.
|
||
- **Wildnis-Runden** (`Data.PVE_ROUNDS`, `Game._make_creeps`): jeder Spieler bekommt eine eigene Kopie.
|
||
- **Speichern** als JSON in `user://spielstand.json` (`Game.to_dict/from_dict`), inklusive RNG-Zustand,
|
||
Bot-Vorlieben und geteiltem Vorrat. Trait-Zeilen nennen die nächste Stufe, neue Kombinationen und
|
||
Trait-Stufen werden gemeldet (`Main._announce_synergies`).
|
||
|
||
Der neutrale Avatar selbst ist unbegrenzt. Knapp ist das Element, das man ihm beim
|
||
Kauf gibt: das zieht einen Platz aus dem geteilten Vorrat. Die 100 pro Element sind
|
||
gemessen: mit den Bots, die jetzt gezielt Futter jagen und Level kaufen, läuft bei 80
|
||
in 2 von 10 Partien ein Element leer, bei 100 keines, Tiefstand rund ein Fünftel.
|
||
|
||
Die Neutral-Chance ist multiplikativ (0,75 hoch Anzahl Elemente) statt in
|
||
Prozentpunkten. So verschwindet Neutral nie ganz: ohne eigene Elemente 100%,
|
||
mit einem 75%, mit sechs noch 18%.
|
||
|
||
## Schere, Stein, Papier
|
||
|
||
Elemente im Ring, jedes schlägt genau eines: Wasser > Feuer > Eis > Wind > Erde > Blitz > Wasser.
|
||
Rollen als Dreieck: Tank > Schütze > Heiler > Tank. Konter ±20 % (Element) und ±15 % (Rolle),
|
||
nur das Hauptelement zählt (`Data.counter_multiplier`, angewandt in `Combat._apply_damage`).
|
||
Konter-Treffer tragen ein ▲, Tooltip und Info-Panel nennen „Stark gegen / Schwach gegen".
|
||
Bots wählen beim Kauf das Element, das das häufigste Element des nächsten Gegners schlägt;
|
||
schwere Bots auch außerhalb ihrer Vorlieben (`BotAI._pick_element`).
|
||
|
||
## Elemente und Traits
|
||
|
||
Feuer, Wasser, Wind, Blitz, Erde, Eis. Stufen bei 2 / 4 / 6 Einheiten.
|
||
Kombinationen (beide Elemente ab 2 Einheiten): Feuersturm, Frost, Magnetsturm,
|
||
Magma, Sturmfront, Dampf.
|
||
|
||
## Aufbau
|
||
|
||
scripts/core/ Data, Avatar, Pool, ShopEntry, PlayerState, Hex, Combat, BotAI, Game
|
||
scripts/ui/ Main (Eingabe, Aktionen, Orchestrierung), CameraRig, Overlays, TopBar,
|
||
InfoColumn, PlayersPanel, ShopPanel (melden per Signal), BoardView3D,
|
||
HudOverlay, TooltipView, Sfx, UiKit
|
||
scripts/core/ dazu Settings (user://settings.cfg)
|
||
scenes/Main.tscn
|
||
tests/ sim_test.gd (kopflos), Probe2, Probe3, CamCal
|
||
|
||
`Combat` ist reine Logik und wird in Schritten gerechnet. Bot-Kämpfe laufen sofort
|
||
durch (`run_to_end`), der eigene Kampf wird pro Frame gesteppt.
|
||
|
||
Die Darstellung hängt nicht an der Logik: `BoardView3D` liest nur `Game`.
|
||
Lebens- und Manabalken zeichnet `HudOverlay` in 2D über die projizierten
|
||
Weltpositionen, damit sie in jeder Entfernung lesbar bleiben.
|
||
|
||
`Main._fit_camera()` rechnet Abstand und Blickpunkt so aus, dass das Spielfeld
|
||
genau in die freie Bildmitte passt. Links stehen Elemente, Einheit und Rundenlog,
|
||
rechts die Spielerliste, unten der Shop. Das läuft bei jeder Fenstergröße neu.
|
||
|
||
## Tests
|
||
|
||
Kernlogik ohne Fenster:
|
||
|
||
godot --headless --path . --script tests/sim_test.gd
|
||
|
||
UI-Durchläufe mit Screenshots (Ordner als Argument):
|
||
|
||
godot --path . tests/probe2.tscn -- /pfad/zu/screenshots # 3-Star, Spielende, Neustart
|
||
godot --path . tests/probe3.tscn -- /pfad/zu/screenshots # echte Maus- und Tastatureingaben
|
||
godot --path . tests/camcal.tscn -- /pfad/zu/screenshots # Kamerabild und Trefferprüfung
|
||
|
||
`probe3` schickt Klicks und Drags über `Viewport.push_input` wie ein Nutzer und
|
||
prüft danach den Zustand. `camcal` prüft, dass alle 28 Felder und 9 Bankplätze
|
||
unter ihrer eigenen Bildschirmposition auch getroffen werden.
|