Compare commits

..

3 Commits

Author SHA1 Message Date
markojozsef
2a29f318b4 csata menük, csata nyerés, game over képernyő 2026-10-07 13:02:35 +02:00
markojozsef
7ac1a8259f vissza függvény menuItemben 2026-10-07 12:14:49 +02:00
markojozsef
75fc03a88d csata formátum dokumentáció 2026-10-07 12:13:48 +02:00
9 changed files with 675 additions and 25 deletions

View File

@@ -13,4 +13,10 @@ Teljesen AI-generált szakaszokat egy `# !!! CHATGPT !!!` komment jelzi
* NineSlicesPanel * NineSlicesPanel
### battle_data.py ### battle_data.py
* csata függvények * csata függvények
### gamestates/battle.py
* menük
## Dokumentáció
Ezen a fájlon kívül minden dokumentáció AI generálta.

444
doc/CsataFormatum.md Normal file
View File

@@ -0,0 +1,444 @@
# Csata objektum
## Szerkezet
```python
{
"enemies": [...],
"party_members": [...],
"enemy_turn_func": ...,
"mus_id": "...",
"init_ai_data": {...},
"menus": [...],
"spells": [...],
"inventory": [...]
}
```
---
## enemies
Az ellenséges egységek listája.
### Típus
```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.
---
## party_members
A játékos által irányított karakterek listája.
### Típus
```python
list[Combatant]
```
### 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.
---
## enemy_turn_func
Az ellenséges köröket kezelő függvény.
### Típus
```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]
},
"menus": list[list],
"spells": list[list],
"inventory": list
}
```
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.

View File

@@ -26,7 +26,8 @@ MenuItem(
up, up,
down, down,
action, action,
data=None data=None,
back_func=None
) )
``` ```
@@ -59,6 +60,9 @@ MenuItem(
- `data` (`any`, opcionális) - `data` (`any`, opcionális)
A kiválasztáskor az akciónak átadott adat. A kiválasztáskor az akciónak átadott adat.
- `back_func` (`callable`)
A menüpont kiválasztásakor meghívandó függvény.
--- ---
## Attribútumok ## Attribútumok
@@ -97,6 +101,24 @@ def start_game(data):
print("Játék indul!") print("Játék indul!")
``` ```
---
### Vissza
```python
self.back
```
A visszalépés során meghívandó függvény.
Példa:
```python
def vissza(data):
open_menu()
```
--- ---
### Adat ### Adat
@@ -105,7 +127,7 @@ def start_game(data):
self.data self.data
``` ```
A menüelemhez kapcsolódó opcionális adat, amelyet az akciófüggvény paraméterként kap meg. A menüelemhez kapcsolódó opcionális adat, amelyet az akció- és visszafüggvény paraméterként kap meg.
Példa: Példa:

View File

@@ -2,12 +2,14 @@ import gamestates.none
import gamestates.title import gamestates.title
import gamestates.overworld import gamestates.overworld
import gamestates.battle import gamestates.battle
import gamestates.gameover
gamestates = { gamestates = {
"none": gamestates.none, "none": gamestates.none,
"title": gamestates.title, "title": gamestates.title,
"overworld": gamestates.overworld, "overworld": gamestates.overworld,
"battle": gamestates.battle "battle": gamestates.battle,
"gameover": gamestates.gameover,
} }
gamestate = "none" gamestate = "none"

View File

@@ -1,6 +1,8 @@
import math import math
import copy
import random import random
import arcade import arcade
import gamestate
from battle import Combatant, Action, Damage, StatusChange from battle import Combatant, Action, Damage, StatusChange
from menu import MenuItem from menu import MenuItem
@@ -35,30 +37,157 @@ battle_id = 0
def switch(game): def switch(game):
global enemies, party_members, battle_music, enemy_turn, enemy_ai_data, party_menus, spells, inventory global enemies, party_members, battle_music, enemy_turn, enemy_ai_data, party_menus, spells, inventory
enemies = battle_data.battles[battle_id]["enemies"] print("asdfasdfasdf")
party_members = battle_data.battles[battle_id]["party_members"] enemies = copy.deepcopy(battle_data.battles[battle_id]["enemies"])
enemy_turn = battle_data.battles[battle_id]["enemy_turn_func"] party_members = copy.deepcopy(battle_data.battles[battle_id]["party_members"])
enemy_ai_data = battle_data.battles[battle_id]["init_ai_data"] enemy_turn = copy.deepcopy(battle_data.battles[battle_id]["enemy_turn_func"])
party_menus = battle_data.battles[battle_id]["menus"] enemy_ai_data = copy.deepcopy(battle_data.battles[battle_id]["init_ai_data"])
spells = battle_data.battles[battle_id]["spells"] party_menus = copy.deepcopy(battle_data.battles[battle_id]["menus"])
inventory = battle_data.battles[battle_id]["inventory"] spells = copy.deepcopy(battle_data.battles[battle_id]["spells"])
inventory = copy.deepcopy(battle_data.battles[battle_id]["inventory"])
battle_music = assets.sounds[battle_data.battles[battle_id]["mus_id"]].play(loops=True) battle_music = assets.sounds[battle_data.battles[battle_id]["mus_id"]].play(loops=True)
def open_menu(whose):
party_idx = party_members.index(whose)
for i, m in enumerate(party_menus[party_idx]):
menu.append(MenuItem(m[0], 19, 64-(i*13), None, None, None, None, menu_action, (whose, party_members, enemies, m[1])))
for i, m in enumerate(menu):
m.up = menu[(i - 1) % len(menu)]
m.down = menu[(i + 1) % len(menu)]
menu[1].select()
def close_menu(): def close_menu():
global menu, menu_open global menu, menu_open
menu = [] menu = []
menu_open = 0 menu_open = 0
menu_text.reset("") menu_text.reset("")
# !!! CHATGPT !!!
def open_spell_menu(user, aparty, eparty):
global menu
party_idx = party_members.index(user)
spell_list = spells[party_idx]
menu = []
for i, spell in enumerate(spell_list):
menu.append(
MenuItem(
spell[0],
19,
64 - (i * 13),
None,
None,
None,
None,
spell_action,
(user, spell, aparty, eparty)
)
)
if len(menu) == 0:
return
for i, m in enumerate(menu):
m.up = menu[(i - 1) % len(menu)]
m.down = menu[(i + 1) % len(menu)]
menu[0].select()
def spell_action(data):
user, spell, aparty, eparty = data
name, mp_cost, action = spell
action_name, func, target_type = action
action_queue.append(func(user, [p for p in eparty if not p.ko][0]))
close_menu()
def open_item_menu(user, aparty, eparty):
global menu
menu = []
for i, item in enumerate(inventory):
if item[1] <= 0:
continue
menu.append(
MenuItem(
f"{item[0]} x{item[1]}",
19,
64 - (i * 13),
None,
None,
None,
None,
item_action,
(user, item, aparty, eparty)
)
)
if len(menu) == 0:
return
for i, m in enumerate(menu):
m.up = menu[(i - 1) % len(menu)]
m.down = menu[(i + 1) % len(menu)]
menu[0].select()
def item_action(data):
user, item, aparty, eparty = data
name, amount, action = item
action_name, func, target_type = action
action_queue.append(
func(user, user)
)
item[1] -= 1
close_menu()
# !!! CHATGPT VÉGE !!!
def menu_action(data): def menu_action(data):
user, aparty, eparty, action = data user, aparty, eparty, action = data
match action: match action:
case "unselectable": case "unselectable":
return return
case "phys_att": case "phys_att":
action_queue.append(Action("Physical Attack", user, eparty[0], Damage(166, 0, 0, 0), StatusChange(0, 0, 0), battle_data.anim_default)) action_queue.append(
Action(
"Physical Attack",
user,
[p for p in eparty if not p.ko][0],
Damage(166, 0, 0, 0),
StatusChange(0, 0, 0),
battle_data.anim_default
)
)
close_menu() close_menu()
case "magic":
open_spell_menu(user)
case "items":
open_item_menu(user)
case _: case _:
print(f"Unknown action: {action}") print(f"Unknown action: {action}")
close_menu() close_menu()
@@ -113,20 +242,23 @@ def update(game, dt):
menu_open = whose menu_open = whose
menu_text.reset(f"{whose.name}'s Menu") menu_text.reset(f"{whose.name}'s Menu")
menu_text.type_all() menu_text.type_all()
open_menu(whose)
party_idx = party_members.index(whose)
for i, m in enumerate(party_menus[party_idx]):
menu.append(MenuItem(m[0], 19, 64-(i*13), None, None, None, None, menu_action, (whose, party_members, enemies, m[1])))
for i, m in enumerate(menu):
m.up = menu[(i - 1) % len(menu)]
m.down = menu[(i + 1) % len(menu)]
menu[1].select()
for m in menu: for m in menu:
m.update() m.update()
if len(enemies) != 0 and all(e.ko for e in enemies):
print("\nVICTORY! all enemies were defeated.")
battle_music.stop()
gamestate.switch_gamestate(game,"overworld")
return
if len(party_members) != 0 and all(a.ko for a in party_members):
print("\nGAME OVER! the party was defeated.")
battle_music.stop()
gamestate.switch_gamestate(game,"gameover")
return
party_text.reset(partytext) party_text.reset(partytext)
ene_text.reset(enetext) ene_text.reset(enetext)
party_text.type_all() party_text.type_all()

View File

@@ -0,0 +1,38 @@
import menu
import text
import renderers
import gamestate
text = text.TextRenderer(90, 180, 210)
panel = renderers.NineSlicesPanel(84,100,64,100)
m = []
def retry(g):
gamestate.switch_gamestate(g, "title")
def switch(game):
global m
m = [
menu.MenuItem("Ujrapróba", 98, 150, None, None, None, None, retry, game),
menu.MenuItem("Kilépés", 98, 130, None, None, None, None, quit)
]
m[0].down = m[1]
m[1].up = m[0]
m[0].select()
text.reset("Játék vége!")
text.type_all()
def update(game, dt):
for i in m:
i.update()
def draw(game):
panel.render()
text.render()
for i in m:
i.render()

View File

@@ -2,4 +2,4 @@ import gamestate
# Azért létezik ez a "none" gamestate hogy meghívja az elsö gamestate init() funkcióját # Azért létezik ez a "none" gamestate hogy meghívja az elsö gamestate init() funkcióját
def update(game, dt): def update(game, dt):
gamestate.switch_gamestate(game, "overworld") gamestate.switch_gamestate(game, "gameover")

View File

@@ -34,6 +34,7 @@ def update(game, dt):
if btrans_timer >= BTRANS_DURATION: if btrans_timer >= BTRANS_DURATION:
game.mos_enabled = False game.mos_enabled = False
btrans_active = False
btrans_sound.stop() btrans_sound.stop()
gamestate.switch_gamestate(game,"battle") gamestate.switch_gamestate(game,"battle")

View File

@@ -5,12 +5,13 @@ import controls
# Választható menüelem több menüelem közt, ha nincs valamelyik irányba másik menüelem, az irányra None értéket adunk # Választható menüelem több menüelem közt, ha nincs valamelyik irányba másik menüelem, az irányra None értéket adunk
class MenuItem: class MenuItem:
def __init__(self, label, x, y, left, right, up, down, action, data=None): def __init__(self, label, x, y, left, right, up, down, action, data=None, back_func=None):
self.left = left self.left = left
self.right = right self.right = right
self.up = up self.up = up
self.down = down self.down = down
self.action = action self.action = action
self.back = back_func
self.data = data self.data = data
self.x = x self.x = x
self.y = y self.y = y
@@ -30,6 +31,10 @@ class MenuItem:
self.action(self.data) self.action(self.data)
return return
if controls.is_control_pressed(5) and self.back: # Vissza
self.back(self.data)
return
if controls.is_control_pressed(0) and self.up: # Fel if controls.is_control_pressed(0) and self.up: # Fel
controls.depress(0) controls.depress(0)
self.selected = False self.selected = False