Normális csata formátum dokumentáció
Some checks failed
Build Windows EXE / build (push) Has been cancelled
Some checks failed
Build Windows EXE / build (push) Has been cancelled
This commit is contained in:
@@ -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.
|
||||
---
|
||||
|
||||
## 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.
|
||||
|
||||
Reference in New Issue
Block a user