Files
elements-autochess/README.md
2026-09-16 13:21:45 +02:00

158 lines
9.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.