diff --git a/doc/CsataFormatum.md b/doc/CsataFormatum.md index 92f2b12..511efb9 100644 --- a/doc/CsataFormatum.md +++ b/doc/CsataFormatum.md @@ -1,444 +1,402 @@ -# Csata objektum +# Csataadatok és csatafüggvények technikai leírása -## Szerkezet +Ez a dokumentum a csatamód (`battle` állapot) forráskódja alapján írja le, hogyan kell felépíteni a `battle_data.battles` bejegyzéseit, és milyen argumentumokkal hívódnak az **MI-függvények**, a **képességek/tárgyak függvényei** és az **akcióanimációk**. -```python -{ - "enemies": [...], - "party_members": [...], - "enemy_turn_func": ..., - "mus_id": "...", - "init_ai_data": {...}, - "menus": [...], - "spells": [...], - "inventory": [...] -} -``` +> **Jelölések:** Ami a csatamód forrásából közvetlenül kiolvasható, azt tényként írom le. Ami következtetés, vagy a nem látott fájlokban (`battle.py`, `battle_data.py`, `menu.py`, `text.py`) dől el, azt **[Feltételezés]** vagy **TODO** jelzi. --- -## enemies +## 1. Terminológia -Az ellenséges egységek listája. +A kód belső nevei eltérnek a játékbeli fogalmaktól, ezért érdemes tisztán tartani: -### Típus +| Kód | Játékbeli jelentés | Képernyőpozíció | +|---|---|---| +| `party_members` | A játékos által irányított karakter(ek), vagyis a megszállt főellenfél (és kísérői) | Bal oldal | +| `enemies` | A **hősök**, akik ellen a játékos harcol | Jobb oldal | +| `enemy_turn_func` | A hősök **MI-függvénye** | | +| `battle_id` | A csata azonosítója (a négy csataszoborhoz tartozik) | | -```python -list[Combatant] -``` - -### Példa - -```python -"enemies": [ - Combatant("Six-Seven kid", 120, 30, 80, 20, 0, 50, False, False), - Combatant("Ice Sprite", 60, 15, 150, 10, 0, 200, False, True), -] -``` - -Minden elem egy `Combatant` objektum. +> **TODO:** Melyik `battle_id` melyik szoborhoz tartozik, és ki állítja be az értékét a csata előtt (feltehetően az overworld). --- -## party_members +## 2. A csata betöltése -A játékos által irányított karakterek listája. +A `switch(game)` a `battle_data.battles[battle_id]` bejegyzésből tölti be a csatát. A következő mezők **mély másolatot** (`copy.deepcopy`) kapnak, tehát a csata közbeni módosításaik **nem maradnak meg** a következő csatára, és az újraindítás mindig tiszta állapotot ad: -### Típus +`enemies`, `party_members`, `enemy_turn_func`, `init_ai_data`, `menus`, `spells`, `inventory`. -```python -list[Combatant] -``` +A sprite-ok, a háttér és a zene **nem** másolódnak, csak hivatkozás történik rájuk. -### Példa - -```python -"party_members": [ - Combatant("Locke", 80, 45, 130, 15, 10, 10, False, False), - Combatant("Terra", 100, 35, 100, 30, 30, 30, False, False), -] -``` - -A lista sorrendje határozza meg: - -- a karakter pozícióját a csatában, -- a menük hozzárendelését, -- a varázslatok hozzárendelését. +> **Fontos:** Mivel a másolat készül, az MI-függvények és képességfüggvények mindig a nekik **átadott** objektumokkal dolgozzanak, ne a `battle_data` modulban lévő eredetikre hivatkozzanak (pl. egy zárványon (closure) keresztül). Különben az eredeti, nem a csatában résztvevő példány módosulna. A `damage_callback` is csak olyan célponttal ad helyes pozíciót, amely a **jelenlegi** `enemies` vagy `party_members` listában van, különben a szám a `(0, 0)` pozícióban jelenik meg. --- -## enemy_turn_func +## 3. A `battle_data.battles[battle_id]` szerkezete -Az ellenséges köröket kezelő függvény. +Egy bejegyzés egy szótár (dict) a következő kulcsokkal: -### Típus +| Kulcs | Típus | Leírás | +|---|---|---| +| `"enemies"` | `list[Combatant]` | A hősök listája | +| `"party_members"` | `list[Combatant]` | A játékos által irányított karakterek listája | +| `"enemy_turn_func"` | függvény | A hősök MI-függvénye (lásd 8. fejezet) | +| `"init_ai_data"` | másolható objektum (általában `dict`) | Az MI kezdeti állapota, a csata elején másolódik | +| `"menus"` | `list[list[tuple[str, str]]]` | Karakterenkénti parancsmenük (lásd 5. fejezet) | +| `"spells"` | `list[list[tuple]]` | Karakterenkénti képességlisták (lásd 6. fejezet) | +| `"inventory"` | `list[list]` | A csatában használható tárgyak, **közösek** (lásd 7. fejezet) | +| `"enemy_sprites"` | `list[list[azonosító]]` | Hősönként 3 sprite-azonosító (lásd 11. fejezet) | +| `"party_sprites"` | `list[list[azonosító]]` | Karakterenként 3 sprite-azonosító | +| `"background"` | azonosító | Kulcs az `assets.sprites` szótárba | +| `"mus_id"` | azonosító | Kulcs az `assets.sounds` szótárba, a zene ismétlődve (`loops=True`) szól | + +A `menus`, `spells`, `party_sprites` listák **indexe a `party_members` indexével egyezik**. Az `enemy_sprites` indexe az `enemies` indexével egyezik. + +A csatát a `battle_data.battles[battle_id]` alapján választja ki a rendszer. A `battles` tehát olyan tároló, amely a `battle_id` értékkel indexelhető. + +### Váz példa ```python -callable -``` - -### Példa - -```python -"enemy_turn_func": enemy_turn_test -``` - -A függvényt a rendszer az ellenfelek körének feldolgozására használja. - ---- - -## mus_id - -A csata alatt lejátszandó zene azonosítója. - -### Típus - -```python -str -``` - -### Példa - -```python -"mus_id": "mus_fnafsux" -``` - ---- - -## init_ai_data - -Az MI számára átadott inicializáló adatok. - -### Típus - -```python -dict -``` - ---- - -### moves - -Az MI által használható képességek gyűjteménye. - -#### Típus - -```python -dict[str, tuple] -``` - -#### Formátum - -```python -"azonosító": ( - név, - függvény, - célpont_szükséges -) -``` - -#### Paraméterek - -- `név` (`str`) - A képesség megjelenő neve. - -- `függvény` (`callable`) - A végrehajtandó függvény. - -- `célpont_szükséges` - - `True` → ellenséges célpont szükséges - - `False` → szövetséges célpont szükséges - - `None` → nincs célpont - -#### Példa - -```python -"attack": ( - "Physical attack", - move_attack, - True -) -``` - ---- - -### Teljes példa - -```python -"moves": { - "attack": ("Physical attack", move_attack, True), - "fire": ("Fira", move_fire, True), - "ice": ("Blizzara", move_ice, True), - "cure": ("Cure", move_cure, False), - "defend": ("Defend", move_defend, None) -} -``` - ---- - -## menus - -Karakterenként meghatározza a harci főmenüt. - -### Típus - -```python -list[list] -``` - -A külső lista a karaktereket reprezentálja. - -A sorrendnek meg kell egyeznie a `party_members` listával. - ---- - -### Egy karakter menüje - -```python -[ - ["Attack", "phys_att"], - ["Magic", "magic"], - ["Item", "items"] -] -``` - ---- - -### Menüpont formátuma - -```python -[ - megjelenő_szöveg, - parancs -] -``` - -#### Paraméterek - -- `megjelenő_szöveg` (`str`) -- `parancs` (`str`) - ---- - -### Speciális parancsok - -#### Fizikai támadás - -```python -["Attack", "phys_att"] -``` - -#### Mágia - -```python -["Magic", "magic"] -``` - -#### Tárgyak - -```python -["Item", "items"] -``` - -#### Nem választható hely - -```python -["", "unselectable"] -``` - -Ez egy üres, nem kiválasztható menüpont. - ---- - -## spells - -Karakterenként meghatározza az elérhető varázslatokat. - -### Típus - -```python -list[list] -``` - -A lista sorrendje megegyezik a `party_members` sorrendjével. - ---- - -### Varázslat formátuma - -```python -[ - név, - mp_költség, - ( - név, - függvény, - célpont_szükséges - ) -] -``` - -#### Paraméterek - -- `név` (`str`) - A varázslat megjelenő neve. - -- `mp_költség` (`int`) - A varázslat MP költsége. - -- `akció` (`tuple`) - A végrehajtandó harci művelet. - ---- - -### Példa - -```python -[ - "Fira", - 15, - ("Fira", move_fire, True) -] -``` - ---- - -### Teljes példa - -```python -"spells": [ - [], - [ - ["Fira", 0, ("Fira", move_fire, True)], - ["Blizzara", 0, ("Blizzara", move_ice, True)] - ], - [ - ["Fira", 0, ("Fira", move_fire, True)], - ["Blizzara", 0, ("Blizzara", move_ice, True)] - ], - [] -] -``` - -Ebben az esetben: - -- Locke nem rendelkezik varázslatokkal, -- Terra használhatja a **Fira** és **Blizzara** varázslatokat, -- Celes használhatja a **Fira** és **Blizzara** varázslatokat, -- Cyan nem rendelkezik varázslatokkal. - ---- - -## inventory - -A csatában használható tárgyak listája. - -### Típus - -```python -list -``` - ---- - -### Tárgy formátuma - -```python -[ - név, - mennyiség, - ( - név, - függvény, - célpont_szükséges - ) -] -``` - -#### Paraméterek - -- `név` (`str`) - A tárgy megjelenő neve. - -- `mennyiség` (`int`) - A rendelkezésre álló darabszám. - -- `akció` (`tuple`) - A tárgy használatakor meghívandó művelet. - ---- - -### Példa - -```python -[ - "Phoenix Down", - 67, - ( - "Phoenix Down", - move_phoenix, - False - ) -] -``` - ---- - -### Teljes példa - -```python -"inventory": [ - [ - "Phoenix Down", - 67, - ( - "Phoenix Down", - move_phoenix, - False - ) - ] -] -``` - ---- - -## Indexkapcsolatok - -A következő listák indexei összetartoznak: - -```python -party_members[i] -menus[i] -spells[i] -``` - -Példa: - -```python -party_members[1] # Terra -menus[1] # Terra menüje -spells[1] # Terra varázslatai -``` - -Ez biztosítja, hogy minden karakter saját menüvel és saját varázslistával rendelkezzen. - ---- - -## Összefoglaló - -```python -{ - "enemies": list[Combatant], - "party_members": list[Combatant], - "enemy_turn_func": callable, - "mus_id": str, - "init_ai_data": { - "moves": dict[str, tuple] +battles = [ + { + "enemies": [Combatant(...), Combatant(...)], + "party_members": [Combatant(...)], + "enemy_turn_func": hero_ai_1, + "init_ai_data": {"turn": 0}, + "menus": [ + [("Támadás", "phys_att"), ("Varázslat", "magic"), ("Tárgyak", "items")], + ], + "spells": [ + [("Tűzgolyó", 5, ("Tűzgolyó", fireball, "ene"))], + ], + "inventory": [ + ["Gyógyital", 3, ("Gyógyital", use_potion, "ally")], + ], + "enemy_sprites": [ + ["hero1_normal", "hero1_hurt", "hero1_ko"], + ["hero2_normal", "hero2_hurt", "hero2_ko"], + ], + "party_sprites": [ + ["boss1_normal", "boss1_hurt", "boss1_ko"], + ], + "background": "bg_arena_1", + "mus_id": "mus_battle_1", }, - "menus": list[list], - "spells": list[list], - "inventory": list -} + # ... további 3 csata +] ``` -Egy csata teljes egészében leírható egyetlen szótárral, amely tartalmazza az összes résztvevőt, a harci lehetőségeket, az MI által használható képességeket, valamint a játékos számára elérhető menüket, varázslatokat és tárgyakat. \ No newline at end of file +--- + +## 4. `Combatant` + +A `Combatant` osztály (`battle.py`) konstruktora **nem látható** a csatamód forrásában, ezért itt csak az onnan elérhető attribútumokat sorolom fel: + +| Attribútum | Használat a csatamódban | +|---|---| +| `name` | Megjelenített név (menü, célpontválasztó) | +| `hp` | Aktuális életerő (célpontválasztóban kijelzett; sprite-váltás) | +| `maxhp` | Maximális életerő. A 10%-os sprite-váltás küszöbe | +| `ko` | Legyőzött-e (a szereplő nem cselekszik, a célpontválasztóból kimarad) | +| `stone` | Kővé változott-e (nem cselekszik, szürke sprite) | +| `def_up` | Védekezik-e (a célpontválasztóban „Védés” felirat) | +| `atb` | Az akcióig hátralévő időzítő (lásd 9. és 12. fejezet) | +| `attack` | Támadóerő, a „Támadás” parancs ezt adja a `Damage`-nek | + +> **TODO:** A `Combatant` konstruktorának paraméterei és sorrendje, a kezdő `atb` értékek, további mezők (pl. MP, sebesség). + +--- + +## 5. Menük (`menus`) + +Karakterenként egy lista, amelynek elemei `(felirat, művelet)` párok: + +```python +("Támadás", "phys_att") +``` + +A `művelet` értéke a `menu_action` függvényben kerül feldolgozásra: + +| Művelet | Hatás | +|---|---| +| `"phys_att"` | Célpontválasztó (`"ene"`), majd egy `Action("Támadás", user, target, Damage(user.attack, 0, 0, 0), StatusChange(0, 0, 0), battle_data.anim_default)` kerül a sorba | +| `"magic"` | Megnyitja a képességmenüt (`spells[karakter_index]`) | +| `"items"` | Megnyitja a tárgymenüt (`inventory`) | +| `"unselectable"` | Nem csinál semmit, a kijelölés a menüben marad | +| *bármi más* | Kiírja az `Unknown action` hibát a konzolra, és **bezárja a menüt** (lásd 12. fejezet) | + +A menüpontok `x = 19`, `y = 64 - i * 13` pozícióban jelennek meg, ez a menüpanelen belül **legfeljebb 5 sort** jelent (lásd 12. fejezet). + +--- + +## 6. Képességek (`spells`) + +Karakterenként egy lista, amelynek elemei: + +```python +(név, mp_költség, (akció_név, függvény, célpont_típus)) +``` + +| Elem | Leírás | +|---|---| +| `név` | A képességmenüben megjelenő felirat | +| `mp_költség` | A kódban kicsomagolódik, de a csatamód **nem használja** (nem ellenőrzi, nem vonja le) | +| `akció_név` | A kódban kicsomagolódik, de a csatamód **nem használja** | +| `függvény` | `függvény(user, target) -> Action`. Lásd lent | +| `célpont_típus` | Lásd 8. fejezet | + +A kiválasztott célpont után a csatamód ezt hívja: + +```python +action_queue.append(függvény(user, targets[0])) +``` + +Vagyis a függvény két argumentumot kap: **a cselekvő karaktert** és **a kiválasztott egyetlen célpontot**, és egy `Action` objektumot kell visszaadnia. + +> **TODO:** Az MP-költség levonása és ellenőrzése. A csatamódban nincs ilyen, tehát vagy az `Action`/animáció végzi, vagy még nincs megvalósítva. + +--- + +## 7. Tárgyak (`inventory`) + +Egyetlen, a csata összes karaktere által **közösen** használt lista. Elemei **módosítható listák** (nem tuple-ök, mert a mennyiség csökken): + +```python +[név, mennyiség, (akció_név, függvény, célpont_típus)] +``` + +| Elem | Leírás | +|---|---| +| `név` | A menüben `név xMENNYISÉG` formában jelenik meg (pl. `Gyógyital x3`) | +| `mennyiség` | `int`. A 0 vagy kisebb értékű tárgyak nem jelennek meg | +| `függvény` | Ugyanaz, mint a képességeknél: `függvény(user, target) -> Action` | +| `célpont_típus` | Lásd 8. fejezet | + +A mennyiség **a kiválasztáskor** (az akció sorba állításakor) csökken eggyel, nem a végrehajtáskor. + +--- + +## 8. Célpont-típusok + +Az `open_target_menu(user, aparty, eparty, target_type, on_pick, back)` a `target_type` alapján állítja össze a választható célpontokat. + +> **Dokumentáció és kód eltérése:** A függvény docstringje az `"enemy"`, `"ally"`, `"self"`, `"all_enemies"`, `"all_allies"` értékeket említi, de a tényleges kód más szavakat vizsgál. **A kód a mérvadó.** + +| `target_type` | Célpont-lista | Legyőzöttek? | Megjegyzés | +|---|---|---|---| +| `"self"` | A cselekvő maga | Nem releváns | Nincs célpontválasztó, a menü azonnal bezárul | +| `"ene"` | `eparty` (a hősök) | Kimaradnak | A „Támadás” is ezt használja | +| `"ene_rev"` | `eparty` | **Benne vannak** | | +| `"all_rev"` | `aparty` (a saját oldal) | **Benne vannak** | Újraélesztő képességekhez | +| *bármilyen más szöveg* | `aparty` (a saját oldal) | Kimaradnak | Pl. `"ally"` ide esik | + +További tudnivalók: + +- Az `aparty` a játékos oldala (`party_members`), az `eparty` a hősöké (`enemies`). +- Ha nincs választható célpont, a függvény visszatér, és az előző menü marad nyitva. +- A célpont-visszahívás (`on_pick`) **mindig egyelemű listát** kap, így a csatamód többcélpontos kijelölést **nem támogat**. A célpontválasztó listája a célpont nevét és az aktuális életerejét mutatja, kiegészítve a `Kő` és `Védés` állapotokkal. +- Ha területi hatást akarsz, azt a függvénynek/animációnak kell kezelnie. **TODO:** A függvény csak a `user` és `target` argumentumot kapja, így a többi szereplő eléréséhez a modul szintű listákra (`battle.enemies`, `battle.party_members`) lenne szükség. Ennek a tervezett módja még tisztázandó. + +--- + +## 9. Az MI-függvény (`enemy_turn_func`) + +### Aláírás + +```python +def hero_ai(e, enemies, party_members, ai_data): + ... + return Action(...) # vagy None +``` + +| Argumentum | Tartalom | +|---|---| +| `e` | A cselekvésre kész hős (`Combatant`) | +| `enemies` | A hősök (saját oldal) listája | +| `party_members` | A játékos karaktereinek listája (az ellenfél, az MI szemszögéből) | +| `ai_data` | A `init_ai_data` csatánkénti másolata. Az MI ebben tárolhat állapotot (pl. számlálót) | + +**Visszatérési érték:** + +- **`Action`** (vagy bármi igaz értékű): az akció a sor végére kerül, és a hős `atb` értéke `math.inf` lesz. +- **`None` / hamis érték:** a hős nem csinál semmit **ebben a képkockában**, és az MI-függvény **a következő képkockában újra meghívódik**, amíg nem ad vissza akciót. Ez lehetővé teszi, hogy az MI egy feltételre várjon. + +### Mikor hívódik? + +A függvény minden képkockában (`update`) meghívódik azokra a hősökre, akik: + +1. nincsenek legyőzve és nincsenek kővé változva (`not ko and not stone`), és +2. az `atb` értékük `<= 0`. + +Minden képkockában minden hős `atb`-ja eggyel csökken (**képkockafüggő**, nem `dt`-alapú. Lásd 12. fejezet). + +### Példa + +```python +def hero_ai_1(e, enemies, party_members, ai_data): + targets = [p for p in party_members if not p.ko and not p.stone] + if not targets: + return None + ai_data["turn"] += 1 + target = random.choice(targets) + return Action( + "Támadás", e, target, + Damage(e.attack, 0, 0, 0), StatusChange(0, 0, 0), + battle_data.anim_default, + ) +``` + +--- + +## 10. Az `Action` és az akcióanimáció + +### Az `Action` felépítése + +A kódban látható hívás alapján: + +```python +Action(név, user, target, damage, status_change, animation) +``` + +| Argumentum | Leírás | +|---|---| +| `név` | Az akció neve (a képernyő alján megjelenő panelhez) | +| `user` | A cselekvő `Combatant` | +| `target` | A célpont `Combatant` | +| `damage` | `Damage(a, b, c, d)`. Az első érték a támadásnál a `user.attack` | +| `status_change` | `StatusChange(a, b, c)`. A „Támadás” csupa nullát ad | +| `animation` | Az animációs függvény (lásd lent) | + +A csatamód közvetlenül **csak az `animation` mezőt** olvassa. A többi attribútum nevét (pl. `action.user`, `action.target`) az animációknak kell ismerniük. + +> **TODO:** Az `Action`, `Damage`, `StatusChange` pontos attribútumnevei és mezőinek jelentése (a `Damage` és `StatusChange` további paraméterei). Ezek a `battle.py`-ban vannak. +> **TODO:** A `battle_data.anim_default` működése. + +### Az animációs függvény aláírása + +```python +def animation(action, action_text, timer, dt, damage_callback) -> bool: + ... +``` + +| Argumentum | Tartalom | +|---|---| +| `action` | Az éppen futó `Action` objektum | +| `action_text` | A `TextRenderer(122, 204, None, True)` példány, az akciópanel szövege | +| `timer` | Az animáció kezdete óta eltelt idő másodpercben. Az **első híváskor pontosan `0`** | +| `dt` | Az előző képkocka óta eltelt idő | +| `damage_callback` | `damage_callback(amount, target)`. Lebegő szám létrehozása (lásd lent) | + +**Visszatérési érték:** + +- **`True`**: az animáció véget ért. A csatamód törli az aktuális akciót, és elrejti az akciópanelt. +- **`False`**: az animáció még fut. A `timer` a `dt`-vel nő, és a következő képkockában újra hívódik. + +Az akciópanel (`action_panel`) a teljes animáció ideje alatt látható, de az `action_text` szövegét **az animációnak kell beállítania** (pl. `action_text.reset(...)`, `type_all()`), ahogy a menü szövegét is a csatamód teszi a menüben. Az `action_text.render()` minden képkockában lefut, még ha a panel rejtett is, ezért az animáció végén érdemes **kiüríteni a szöveget** (`action_text.reset("")`) **[Feltételezés]**. + +### `damage_callback(amount, target)` + +| Paraméter | Jelentés | +|---|---| +| `amount` | **Negatív** szám = sebzés (**fehér** számként, előjel nélkül jelenik meg). **Nem negatív** szám = gyógyulás (**zöld** szám) | +| `target` | A célpont `Combatant`. A szám a sprite pozíciója fölött jelenik meg (a `sprite_pos(target)` alapján) | + +A callback **csak a lebegő számot** hozza létre. A HP tényleges módosítása és a `ko` beállítása **nem itt** történik, ezeket az animációnak/akciónak kell elvégeznie **[Feltételezés]**. + +A lebegő szám 0,5 másodpercig él, és képkockánként `(0, 0.4)` értékkel mozog felfelé. + +### Példa (szemléltető jellegű) + +```python +def anim_example(action, action_text, timer, dt, damage_callback): + # Első képkocka: szöveg kiírása + if timer == 0: + action_text.reset(action.name) # [Feltételezés] az attribútum neve + action_text.type_all() + + # Egyszeri találat 0,6 mp-nél + if timer >= 0.6 and not getattr(action, "_hit", False): + action._hit = True + action.target.hp -= 5 # [Feltételezés] a HP-módosítás az animáció dolga + damage_callback(-5, action.target) + + # Vége 1,2 mp-nél + if timer >= 1.2: + action_text.reset("") + return True + return False +``` + +--- + +## 11. Egy akció életciklusa + +A `update(game, dt)` minden képkockában a következő sorrendben fut: + +1. **Hősök időzítője:** minden él(ő), nem kővé vált hősnél `atb -= 1`. Ha `atb <= 0`, meghívódik az `enemy_turn_func`. Ha akciót ad vissza, az a sorba kerül, és `atb = math.inf`. +2. **Játékos karaktereinek időzítője:** ugyanígy `atb -= 1`. Ha `atb <= 0`, a karakter a `menu_queue`-ba kerül, és `atb = math.inf`. +3. **Következő akció indítása:** ha az `action_queue` nem üres és nincs futó akció, az első elem lesz az aktuális (`cur_action`), az akciópanel megjelenik, `action_timer = 0`. +4. **Futó akció léptetése:** `cur_action.animation(...)` meghívása. `True` esetén vége az akciónak, különben `action_timer += dt`. +5. **Menü megnyitása:** ha van várakozó karakter és nincs nyitott menü, megnyílik a következő karakter menüje. A szöveg: „*név* menüje”. +6. **Menüpontok és lebegő számok frissítése.** +7. **Győzelem ellenőrzése:** ha az összes hős `ko` vagy `stone`, a zene megáll, és a játék átvált az `"overworld"` állapotra. +8. **Vereség ellenőrzése:** ha a játékos összes karaktere `ko` vagy `stone`, a zene megáll, és a játék átvált a `"gameover"` állapotra. + +A győzelem-ellenőrzés **a vereségé előtt** fut, ezért ha mindkét feltétel egyszerre teljesül, a játékos nyer. + +--- + +## 12. Elrendezés és sprite-ok + +### Pozíciók + +| Szereplő | Pozíció (képernyőpont) | +|---|---| +| Hős (`i`. index) | `(185, 175 - i * 20)` | +| Játékos karaktere (`i`. index) | `(40 + (i % 2) * 30, 165 - i * 25)` | + +A sprite-ok **középpontja** ezen a ponton van. A pozíciók a lista sorrendjét követik. Az alsó menüpanel `230 × 100` méretű, ezért a lista végén lévő szereplők a panel mögé/fölé kerülhetnek (hősöknél kb. a 4. index után, a játékos karaktereinél kb. a 3. index után). + +### Három sprite az állapotokhoz + +Az `enemy_sprites[i]` és `party_sprites[i]` **pontosan 3 azonosítót** tartalmaz, ebben a sorrendben: + +| Index | Mikor látszik | +|---|---| +| `[0]` | Normál állapot | +| `[1]` | Ha `hp <= maxhp * 0.1` (sebzett) | +| `[2]` | Ha `ko` igaz (a KO sprite elsőbbséget élvez) | + +Ha a szereplő `stone` állapotban van (és nem `ko`), akkor az éppen kiválasztott sprite **szürkeárnyalatos** változata látszik. A szürke változatot a csatamód létrehozza és gyorsítótárazza. + +Az azonosítók az `assets.sprites` szótár kulcsai. Az asset lehet közvetlenül textúra, vagy lehet `.texture` attribútuma. + +--- + +## 13. Ismert korlátok és buktatók + +Ezeket a csatamód forrásából olvastam ki. Érdemes őket a csatatervezés közben szem előtt tartani. + +1. **Üres képesség- vagy tárgymenü beragadást okoz.** Az `open_spell_menu` és az `open_item_menu` először kiüríti a `menu` listát, és csak utána ellenőrzi, hogy van-e elem. Ha nincs (pl. egy karakternek nincs képessége, vagy elfogytak a tárgyak), a menü üres marad, a `menu_open` pedig nem nullázódik, így a csata ettől a karaktertől kezdve **megakadhat**. Ezért: + - ne szerepeljen `"magic"` a menüben, ha a karakternek nincs képessége, + - ne szerepeljen `"items"`, ha nincs tárgy a csatában, vagy gondoskodni kell róla, hogy a tárgyak ne fogyjanak el. +2. **Ismeretlen menüművelet elveszíti a kört.** A `menu_action` az ismeretlen értéknél bezárja a menüt (`close_menu()`), ezért a karakter `atb` értéke `math.inf` marad, és **nem fog újra cselekedni**, hacsak más nem állítja vissza. +3. **Az `atb` visszaállítása nem a csatamódban történik.** A csatamód az akciók sorba állításakor `math.inf`-re állítja az `atb`-t, de újra nem tölti fel. Ezért az akcióknak/animációknak kell a `user.atb` értékét pozitív számra visszaállítani a végrehajtás után **[Feltételezés, ellenőrizendő a `battle.py`-ban]**. Ha ez elmarad, a karakter többé nem cselekszik. +4. **Az `atb` képkockafüggő.** Az `atb -= 1` minden `update` híváskor lefut, nem a `dt`-től függ. A csata sebessége így a frissítési gyakoriságtól függ. +5. **Legfeljebb 5 menüsor fér el.** A menüpontok `y = 64 - i * 13` pozícióban vannak. A 6. elem már a képernyőn kívülre esik. Ez a képesség-, tárgy- és célpontlistára egyaránt vonatkozik. +6. **Nincs ellenőrzés az akció végrehajtásakor.** A sorba állított akció akkor is lefut, ha közben a cselekvő vagy a célpont legyőzötté vált. Az animációnak/akciónak kell kezelnie ezt az esetet **[Feltételezés]**. +7. **Többcélpontos akció nincs.** A célpontválasztás mindig egyetlen célpontot ad át. +8. **Az MP nem érvényesül** a csatamódban (lásd 6. fejezet). + +--- + +## 14. Nyitott kérdések (TODO) + +- A `Combatant`, `Action`, `Damage`, `StatusChange` osztályok teljes definíciója (`battle.py`). +- A `battle_data.anim_default` és az egyéb beépített animációk működése. +- Az `atb` újratöltésének helye és értékei. +- A HP-módosítás, a `ko`, `stone`, `def_up` állapotok beállításának helye. +- Az MP-rendszer megléte és működése. +- A `TextRenderer` és `MenuItem` pontos API-ja (a csatamódban használt hívások: `reset`, `type_all`, `move`, `render`, `select`, `update`). +- A `battle_id` és a csataszobrok összerendelése.