Compare commits
22 Commits
7ea8df8002
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b77b110eb9 | ||
|
|
d8ed2afa60 | ||
|
|
68a8e44388 | ||
|
|
2a29f318b4 | ||
|
|
7ac1a8259f | ||
|
|
75fc03a88d | ||
|
|
4de57827b0 | ||
|
|
b30a7c291b | ||
|
|
eb57b16544 | ||
|
|
c9b7c0c001 | ||
|
|
6a91d3e6db | ||
|
|
c85c382f34 | ||
|
|
3558b7db7a | ||
|
|
2d1bae444c | ||
|
|
5c7db0e77e | ||
|
|
7ba9004a23 | ||
|
|
57cd65b6c5 | ||
|
|
372c55caac | ||
|
|
fcc37089f0 | ||
|
|
7a62dc6ae0 | ||
|
|
8e060ce43f | ||
|
|
d97b5b9fb7 |
22
doc/AIHasznalat.md
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# AI használat a kódban
|
||||||
|
|
||||||
|
## Jelző
|
||||||
|
Teljesen AI-generált szakaszokat egy `# !!! CHATGPT !!!` komment jelzi
|
||||||
|
|
||||||
|
## AI generálta komponensek:
|
||||||
|
|
||||||
|
### main.py
|
||||||
|
* Game
|
||||||
|
|
||||||
|
### renderers.py
|
||||||
|
* TextureRenderer
|
||||||
|
* NineSlicesPanel
|
||||||
|
|
||||||
|
### battle_data.py
|
||||||
|
* 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
@@ -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.
|
||||||
225
doc/MenuItem.md
Normal file
@@ -0,0 +1,225 @@
|
|||||||
|
# MenuItem
|
||||||
|
|
||||||
|
## Áttekintés
|
||||||
|
|
||||||
|
A `MenuItem` osztály egy játék menürendszerének egyetlen választható elemét reprezentálja. Az osztály kezeli:
|
||||||
|
|
||||||
|
- a menüpont szövegének megjelenítését,
|
||||||
|
- a kijelölést,
|
||||||
|
- a menüpontok közötti navigációt,
|
||||||
|
- a kiválasztáskor meghívandó függvényt (akciót),
|
||||||
|
- valamint a kurzor kirajzolását.
|
||||||
|
|
||||||
|
A menüpontok egy irányított hálózatot alkotnak, ahol minden elem ismeri a szomszédait (`up`, `down`, `left`, `right`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Konstruktor
|
||||||
|
|
||||||
|
```python
|
||||||
|
MenuItem(
|
||||||
|
label,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
up,
|
||||||
|
down,
|
||||||
|
action,
|
||||||
|
data=None,
|
||||||
|
back_func=None
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Paraméterek
|
||||||
|
|
||||||
|
- `label` (`str`)
|
||||||
|
A menüpont megjelenő szövege.
|
||||||
|
|
||||||
|
- `x` (`int`)
|
||||||
|
A menüpont vízszintes pozíciója.
|
||||||
|
|
||||||
|
- `y` (`int`)
|
||||||
|
A menüpont függőleges pozíciója.
|
||||||
|
|
||||||
|
- `left` (`MenuItem | None`)
|
||||||
|
A bal oldali szomszédos menüpont.
|
||||||
|
|
||||||
|
- `right` (`MenuItem | None`)
|
||||||
|
A jobb oldali szomszédos menüpont.
|
||||||
|
|
||||||
|
- `up` (`MenuItem | None`)
|
||||||
|
A felső szomszédos menüpont.
|
||||||
|
|
||||||
|
- `down` (`MenuItem | None`)
|
||||||
|
Az alsó szomszédos menüpont.
|
||||||
|
|
||||||
|
- `action` (`callable`)
|
||||||
|
A menüpont kiválasztásakor meghívandó függvény.
|
||||||
|
|
||||||
|
- `data` (`any`, opcionális)
|
||||||
|
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
|
||||||
|
|
||||||
|
### Navigáció
|
||||||
|
|
||||||
|
```python
|
||||||
|
self.left
|
||||||
|
self.right
|
||||||
|
self.up
|
||||||
|
self.down
|
||||||
|
```
|
||||||
|
|
||||||
|
A szomszédos menüpontokra mutató referenciák.
|
||||||
|
|
||||||
|
Ha egy adott irányban nincs további menüpont, akkor az attribútum értéke:
|
||||||
|
|
||||||
|
```python
|
||||||
|
None
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Akció
|
||||||
|
|
||||||
|
```python
|
||||||
|
self.action
|
||||||
|
```
|
||||||
|
|
||||||
|
A kiválasztás során meghívandó függvény.
|
||||||
|
|
||||||
|
Példa:
|
||||||
|
|
||||||
|
```python
|
||||||
|
def start_game(data):
|
||||||
|
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
|
||||||
|
|
||||||
|
```python
|
||||||
|
self.data
|
||||||
|
```
|
||||||
|
|
||||||
|
A menüelemhez kapcsolódó opcionális adat, amelyet az akció- és visszafüggvény paraméterként kap meg.
|
||||||
|
|
||||||
|
Példa:
|
||||||
|
|
||||||
|
```python
|
||||||
|
MenuItem(
|
||||||
|
"Nehéz",
|
||||||
|
100, 100,
|
||||||
|
None, None, None, None,
|
||||||
|
set_difficulty,
|
||||||
|
"hard"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
Kiválasztáskor:
|
||||||
|
|
||||||
|
```python
|
||||||
|
set_difficulty("hard")
|
||||||
|
```
|
||||||
|
|
||||||
|
fut le.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Pozíció
|
||||||
|
|
||||||
|
```python
|
||||||
|
self.x
|
||||||
|
self.y
|
||||||
|
```
|
||||||
|
|
||||||
|
A menüpont képernyőn elfoglalt pozíciója.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Szövegmegjelenítő
|
||||||
|
|
||||||
|
```python
|
||||||
|
self.text
|
||||||
|
```
|
||||||
|
|
||||||
|
Egy `TextRenderer` objektum, amely a felirat megjelenítéséért felel.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Kijelölt állapot
|
||||||
|
|
||||||
|
```python
|
||||||
|
self.selected
|
||||||
|
```
|
||||||
|
|
||||||
|
Logikai érték:
|
||||||
|
|
||||||
|
- `True`: a menüpont jelenleg ki van jelölve;
|
||||||
|
- `False`: a menüpont nincs kijelölve.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Metódusok
|
||||||
|
|
||||||
|
### `select()`
|
||||||
|
|
||||||
|
```python
|
||||||
|
select()
|
||||||
|
```
|
||||||
|
|
||||||
|
Kijelöli a menüpontot.
|
||||||
|
|
||||||
|
#### Működés
|
||||||
|
|
||||||
|
```python
|
||||||
|
self.selected = True
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Példa
|
||||||
|
|
||||||
|
```python
|
||||||
|
start_button.select()
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### `update()`
|
||||||
|
|
||||||
|
```python
|
||||||
|
update()
|
||||||
|
```
|
||||||
|
|
||||||
|
A menüpont állapotának frissítése.
|
||||||
|
|
||||||
|
Ha az elem nincs kijelölve, a függvény azonnal visszatér:
|
||||||
|
|
||||||
|
```python
|
||||||
|
if not self.selected:
|
||||||
|
return
|
||||||
|
```
|
||||||
218
doc/TextRenderer.md
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
# TextRenderer
|
||||||
|
|
||||||
|
A `TextRenderer` osztály egy egyszerű, karakterenként gépelődő (typewriter effektusú) szövegmegjelenítő. A szöveget sprite-okként tárolja és rendereli, támogatja a sortörést meghatározott szélesség esetén, valamint speciális karakterek kezelését egyedi `display()` függvényeken keresztül.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Konstruktor
|
||||||
|
|
||||||
|
```python
|
||||||
|
TextRenderer(x, y, width=None)
|
||||||
|
```
|
||||||
|
|
||||||
|
Új szövegmegjelenítőt hoz létre.
|
||||||
|
|
||||||
|
### Paraméterek
|
||||||
|
|
||||||
|
- `x` (`int`) - A szöveg kezdő X koordinátája.
|
||||||
|
- `y` (`int`) - A szöveg kezdő Y koordinátája.
|
||||||
|
- `width` (`int | None`) - Opcionális maximális szélesség pixelekben. Túllépés esetén automatikus sortörés történik.
|
||||||
|
|
||||||
|
### Attribútumok
|
||||||
|
|
||||||
|
- `sprites` - A renderelendő karaktersprite-ok listája.
|
||||||
|
- `base_x` - Kezdő X koordináta.
|
||||||
|
- `base_y` - Kezdő Y koordináta.
|
||||||
|
- `width` - Maximális szövegszélesség.
|
||||||
|
- `text` - A megjelenítendő szöveg.
|
||||||
|
- `textptr` - Az aktuálisan feldolgozott karakter indexe.
|
||||||
|
- `x` - Aktuális rajzolási pozíció X tengelyen.
|
||||||
|
- `y` - Aktuális rajzolási pozíció Y tengelyen.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## `reset(text)`
|
||||||
|
|
||||||
|
```python
|
||||||
|
reset(text)
|
||||||
|
```
|
||||||
|
|
||||||
|
Visszaállítja a renderelőt alapállapotba, és új szöveget tölt be.
|
||||||
|
|
||||||
|
### Paraméterek
|
||||||
|
|
||||||
|
- `text` (`str`) - A megjelenítendő szöveg.
|
||||||
|
|
||||||
|
### Működés
|
||||||
|
|
||||||
|
- Törli az összes korábbi sprite-ot.
|
||||||
|
- Visszaállítja a rajzolási pozíciót a kezdőkoordinátákra.
|
||||||
|
- Nullázza a karaktermutatót (`textptr`).
|
||||||
|
|
||||||
|
### Példa
|
||||||
|
|
||||||
|
```python
|
||||||
|
renderer.reset("Helló világ!")
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## `amend(text)`
|
||||||
|
|
||||||
|
```python
|
||||||
|
amend(text)
|
||||||
|
```
|
||||||
|
|
||||||
|
Hozzáfűz további szöveget a már meglévő szöveghez.
|
||||||
|
|
||||||
|
### Paraméterek
|
||||||
|
|
||||||
|
- `text` (`str`) - A hozzáfűzendő szöveg.
|
||||||
|
|
||||||
|
### Példa
|
||||||
|
|
||||||
|
```python
|
||||||
|
renderer.reset("Helló")
|
||||||
|
renderer.amend(" világ!")
|
||||||
|
```
|
||||||
|
|
||||||
|
Eredmény:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Helló világ!
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## `type_tick()`
|
||||||
|
|
||||||
|
```python
|
||||||
|
type_tick()
|
||||||
|
```
|
||||||
|
|
||||||
|
Egyetlen karakter feldolgozását végzi el. A függvény elsősorban gépelési animációk megvalósítására használható.
|
||||||
|
|
||||||
|
### Visszatérési érték
|
||||||
|
|
||||||
|
```python
|
||||||
|
bool
|
||||||
|
```
|
||||||
|
|
||||||
|
- `True` - a teljes szöveg feldolgozása befejeződött.
|
||||||
|
- `False` - még vannak feldolgozatlan karakterek.
|
||||||
|
|
||||||
|
### Működés
|
||||||
|
|
||||||
|
1. Lekéri az aktuális karakterhez tartozó definíciót a `texttable` szótárból.
|
||||||
|
2. Ismeretlen karakter esetén a `'?'` definíciót használja.
|
||||||
|
3. Látható karakter esetén:
|
||||||
|
- kivágja a megfelelő textúrát a font atlaszból,
|
||||||
|
- létrehozza a sprite adatait,
|
||||||
|
- hozzáadja azokat a `sprites` listához.
|
||||||
|
4. Ha a megadott szélesség elfogyott:
|
||||||
|
- új sorba lép,
|
||||||
|
- az Y koordinátát 13 pixellel csökkenti.
|
||||||
|
5. Nem látható karakter esetén meghívja a karakter:
|
||||||
|
|
||||||
|
```python
|
||||||
|
display(renderer)
|
||||||
|
```
|
||||||
|
|
||||||
|
metódusát.
|
||||||
|
|
||||||
|
### Példa
|
||||||
|
|
||||||
|
```python
|
||||||
|
while not renderer.type_tick():
|
||||||
|
pass
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## `type_all()`
|
||||||
|
|
||||||
|
```python
|
||||||
|
type_all()
|
||||||
|
```
|
||||||
|
|
||||||
|
A teljes szöveget azonnal feldolgozza.
|
||||||
|
|
||||||
|
### Működés
|
||||||
|
|
||||||
|
A `type_tick()` függvényt hívja ismételten addig, amíg minden karakter feldolgozásra nem kerül.
|
||||||
|
|
||||||
|
### Példa
|
||||||
|
|
||||||
|
```python
|
||||||
|
renderer.reset("Szia!")
|
||||||
|
renderer.type_all()
|
||||||
|
```
|
||||||
|
|
||||||
|
A teljes szöveg egyetlen frame alatt elkészül.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## `render()`
|
||||||
|
|
||||||
|
```python
|
||||||
|
render()
|
||||||
|
```
|
||||||
|
|
||||||
|
Kirajzolja az összes létrehozott karaktersprite-ot.
|
||||||
|
|
||||||
|
### Működés
|
||||||
|
|
||||||
|
A `sprites` listán végighaladva minden karakterhez meghívja:
|
||||||
|
|
||||||
|
```python
|
||||||
|
arcade.draw_texture_rect()
|
||||||
|
```
|
||||||
|
|
||||||
|
függvényt pixelpontos (`pixelated=True`) rendereléssel.
|
||||||
|
|
||||||
|
### Példa
|
||||||
|
|
||||||
|
```python
|
||||||
|
def on_draw(self):
|
||||||
|
arcade.start_render()
|
||||||
|
renderer.render()
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Tipikus használat
|
||||||
|
|
||||||
|
## Gépelési effekt
|
||||||
|
|
||||||
|
```python
|
||||||
|
renderer = TextRenderer(10, 100, width=200)
|
||||||
|
|
||||||
|
renderer.reset(
|
||||||
|
"Ez egy hosszabb szöveg, amely több sorba törhet."
|
||||||
|
)
|
||||||
|
|
||||||
|
renderer.type_tick()
|
||||||
|
|
||||||
|
renderer.render()
|
||||||
|
```
|
||||||
|
|
||||||
|
## Azonnali megjelenítés
|
||||||
|
|
||||||
|
```python
|
||||||
|
renderer = TextRenderer(10, 100)
|
||||||
|
|
||||||
|
renderer.reset("Helló világ!")
|
||||||
|
renderer.type_all()
|
||||||
|
|
||||||
|
renderer.render()
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Megjegyzések
|
||||||
|
|
||||||
|
- Az osztály nem hoz létre külön `Sprite` objektumot minden karakterhez, hanem csak a szükséges textúrát és pozíciót tárolja.
|
||||||
|
- Ismeretlen karakter esetén automatikusan a `'?'` karakter kerül megjelenítésre.
|
||||||
|
- A sortörés kizárólag a megadott `width` túllépésekor történik.
|
||||||
|
- Speciális vezérlőkarakterek létrehozhatók `visible=False` karakterdefiníciókkal.
|
||||||
|
- Ezek a karakterek tetszőleges működést valósíthatnak meg a `display(renderer)` metóduson keresztül.
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
#define RESOLUTION (1. / iResolution.xy)
|
|
||||||
#define MAX_MOSAIC 16.
|
|
||||||
#define SPEED 48.
|
|
||||||
#define FADEOUT_TIME 1.30
|
|
||||||
#define FADOUT_SPEED 3.
|
|
||||||
|
|
||||||
void mainImage( out vec4 fragColor, in vec2 fragCoord )
|
|
||||||
{
|
|
||||||
vec2 uv = fragCoord/iResolution.xy;
|
|
||||||
|
|
||||||
float mosaic = floor(((iTime * SPEED) < MAX_MOSAIC) ? iTime * SPEED : (iTime * SPEED) - MAX_MOSAIC) + 1.;
|
|
||||||
uv = floor(uv / RESOLUTION / mosaic) * RESOLUTION * mosaic;
|
|
||||||
|
|
||||||
// Output to screen
|
|
||||||
fragColor = texture(iChannel0,uv) * max(0., min(1., (FADEOUT_TIME * FADOUT_SPEED) - (iTime * FADOUT_SPEED)));
|
|
||||||
}
|
|
||||||
2
mkenv.sh
@@ -1,2 +1,2 @@
|
|||||||
python -m venv venv
|
python -m venv venv
|
||||||
./venv/bin/pip install arcade
|
./venv/bin/pip install arcade pygame
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
python -m venv venv
|
python -m venv venv
|
||||||
.\venv\Scripts\pip install arcade
|
.\venv\Scripts\pip install arcade pygame
|
||||||
|
|||||||
BIN
res/mus/fnafsux.wav
Normal file
BIN
res/sfx/encounter.wav
Normal file
BIN
res/sprite/balthasar.png
Normal file
|
After Width: | Height: | Size: 496 B |
|
Before Width: | Height: | Size: 344 B |
BIN
res/sprite/overworld_sheet.png
Normal file
|
After Width: | Height: | Size: 264 B |
BIN
screenshots/2026-09-30-11-58.png
Normal file
|
After Width: | Height: | Size: 72 KiB |
BIN
screenshots/2026-09-30-11-59.png
Normal file
|
After Width: | Height: | Size: 121 KiB |
BIN
screenshots/2026-09-30-13-12.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
screenshots/2026-10-07-13-06.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
@@ -1,7 +1,17 @@
|
|||||||
import arcade
|
import arcade
|
||||||
|
import pygame
|
||||||
|
|
||||||
|
pygame.mixer.init()
|
||||||
|
|
||||||
sprites = {
|
sprites = {
|
||||||
"cursor" : arcade.load_texture("res/sprite/handcursor.png"),
|
"cursor" : arcade.load_texture("res/sprite/handcursor.png"),
|
||||||
"font" : arcade.load_spritesheet("res/sprite/font.png"),
|
"font" : arcade.load_spritesheet("res/sprite/font.png"),
|
||||||
"panel" : arcade.load_spritesheet("res/sprite/panel.png"),
|
"panel" : arcade.load_spritesheet("res/sprite/panel.png"),
|
||||||
|
"balthasar": arcade.load_spritesheet("res/sprite/balthasar.png"),
|
||||||
|
"overworld": arcade.load_spritesheet("res/sprite/overworld_sheet.png"),
|
||||||
|
}
|
||||||
|
|
||||||
|
sounds = {
|
||||||
|
"mus_fnafsux": pygame.mixer.Sound("res/mus/fnafsux.wav"),
|
||||||
|
"sfx_encounter": pygame.mixer.Sound("res/sfx/encounter.wav")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,12 @@ import math
|
|||||||
K_NORMAL = 100
|
K_NORMAL = 100
|
||||||
K_DEFUP = 50
|
K_DEFUP = 50
|
||||||
MIN_DMG = 1
|
MIN_DMG = 1
|
||||||
|
BASE_DELAY = 30000
|
||||||
|
|
||||||
class Combatant:
|
class Combatant:
|
||||||
def __init__(self, name, maxhp, attack, speed, def_phys, def_fire, def_ice, abs_fire, abs_ice):
|
def __init__(self, name, maxhp, attack, speed, def_phys, def_fire, def_ice, abs_fire, abs_ice):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.max_atb = 1000/speed
|
self.max_atb = BASE_DELAY//speed
|
||||||
self.atb = self.max_atb
|
self.atb = self.max_atb
|
||||||
self.maxhp = maxhp
|
self.maxhp = maxhp
|
||||||
self.hp = maxhp
|
self.hp = maxhp
|
||||||
@@ -38,12 +39,13 @@ class StatusChange:
|
|||||||
|
|
||||||
# where = reference to the target, who = reference to the inflictor
|
# where = reference to the target, who = reference to the inflictor
|
||||||
class Action:
|
class Action:
|
||||||
def __init__(self, name, who, where, damage, status):
|
def __init__(self, name, who, where, damage, status, animation):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.who = who
|
self.who = who
|
||||||
self.where = where
|
self.where = where
|
||||||
self.damage = damage
|
self.damage = damage
|
||||||
self.status = status
|
self.status = status
|
||||||
|
self.animation = animation
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return (
|
return (
|
||||||
@@ -62,7 +64,7 @@ class Action:
|
|||||||
|
|
||||||
self.who.atb = self.who.max_atb
|
self.who.atb = self.who.max_atb
|
||||||
|
|
||||||
if target.ko:
|
if target.ko and self.status.ko != 2:
|
||||||
print(f" -> {target.name} is KO'd, move cancelled!")
|
print(f" -> {target.name} is KO'd, move cancelled!")
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -113,66 +115,3 @@ class Action:
|
|||||||
target.stone = False
|
target.stone = False
|
||||||
target.def_up = False
|
target.def_up = False
|
||||||
print(f" -> {target.name} got hurt and collapsed!")
|
print(f" -> {target.name} got hurt and collapsed!")
|
||||||
|
|
||||||
enemies = [
|
|
||||||
Combatant("Six-Seven kid", 100, 100, 100, 100, 100, 100, False, False)
|
|
||||||
]
|
|
||||||
|
|
||||||
party_members = [
|
|
||||||
Combatant("Terra", 100, 100, 100, 100, 100, 100, False, False),
|
|
||||||
Combatant("Locke", 100, 100, 100, 100, 100, 100, False, False),
|
|
||||||
Combatant("Celes", 100, 100, 100, 100, 100, 100, False, False),
|
|
||||||
Combatant("Cyan", 100, 100, 100, 100, 100, 100, False, False)
|
|
||||||
]
|
|
||||||
|
|
||||||
tick_counter = 0
|
|
||||||
action_queue = []
|
|
||||||
|
|
||||||
while True:
|
|
||||||
print(f"Tick #{tick_counter}!")
|
|
||||||
|
|
||||||
for e in enemies:
|
|
||||||
if e.ko or e.stone:
|
|
||||||
continue
|
|
||||||
|
|
||||||
e.atb -= 1
|
|
||||||
if e.atb == 0:
|
|
||||||
action_queue.append(Action("Physical Attack", e, party_members[0], Damage(25,0,0,0), StatusChange(0, 0, 0)))
|
|
||||||
|
|
||||||
for p in party_members:
|
|
||||||
if p.ko or p.stone:
|
|
||||||
continue
|
|
||||||
|
|
||||||
p.atb -= 1
|
|
||||||
if p.atb == 0:
|
|
||||||
print(f"{p.name} is ready to attack with {p.hp} health!")
|
|
||||||
should_loop = True
|
|
||||||
while should_loop:
|
|
||||||
print(f"Action(attack / heal / defend): ", end="")
|
|
||||||
act = input()
|
|
||||||
if act == "attack":
|
|
||||||
action_queue.append(Action("Physical Attack", p, enemies[0], Damage(25,0,0,0), StatusChange(0, 0, 0)))
|
|
||||||
should_loop = False
|
|
||||||
elif act == "heal":
|
|
||||||
action_queue.append(Action("Cure", p, p, Damage(0,0,0,42), StatusChange(0, 0, 0)))
|
|
||||||
should_loop = False
|
|
||||||
elif act == "defend":
|
|
||||||
action_queue.append(Action("Defend", p, p, Damage(0,0,0,0), StatusChange(0, 0, 1)))
|
|
||||||
should_loop = False
|
|
||||||
else:
|
|
||||||
print("Idk what you mean I told you attack and heal and defend are the only valid options bruh")
|
|
||||||
|
|
||||||
if len(action_queue) != 0:
|
|
||||||
action = action_queue.pop(0)
|
|
||||||
print(f"Executing: {action}")
|
|
||||||
action.execute()
|
|
||||||
|
|
||||||
if all(e.ko for e in enemies):
|
|
||||||
print("\nVICTORY! all enemies were defeated.")
|
|
||||||
break
|
|
||||||
|
|
||||||
if all(p.ko for p in party_members):
|
|
||||||
print("\nGAME OVER! the party was defeated.")
|
|
||||||
break
|
|
||||||
|
|
||||||
tick_counter += 1
|
|
||||||
154
src/battle_data.py
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
import random
|
||||||
|
from battle import Combatant, Action, Damage, StatusChange
|
||||||
|
|
||||||
|
def anim_default(action, text, timer, dt):
|
||||||
|
if timer == 0:
|
||||||
|
text.reset(action.name)
|
||||||
|
text.type_all()
|
||||||
|
|
||||||
|
if timer >= 1:
|
||||||
|
text.reset("")
|
||||||
|
action.execute()
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
# !!! CHATGPT !!!
|
||||||
|
def move_attack(user, target):
|
||||||
|
return Action("Attack", user, target, Damage(user.attack, 0, 0, 0), StatusChange(0, 0, 0), anim_default)
|
||||||
|
|
||||||
|
def move_fire(user, target):
|
||||||
|
return Action("Fira", user, target, Damage(0, 45, 0, 0), StatusChange(0, 0, 0), anim_default)
|
||||||
|
|
||||||
|
def move_ice(user, target):
|
||||||
|
return Action("Blizzara", user, target, Damage(0, 0, 45, 0), StatusChange(0, 0, 0), anim_default)
|
||||||
|
|
||||||
|
def move_cure(user, target):
|
||||||
|
return Action("Cure", user, target, Damage(0, 0, 0, 42), StatusChange(0, 0, 0), anim_default)
|
||||||
|
|
||||||
|
def move_defend(user, target):
|
||||||
|
return Action("Defend", user, target, Damage(0, 0, 0, 0), StatusChange(0, 0, 1), anim_default)
|
||||||
|
|
||||||
|
def move_break(user, target):
|
||||||
|
return Action("Break", user, target, Damage(0, 0, 0, 0), StatusChange(0, 1, 0), anim_default)
|
||||||
|
|
||||||
|
def move_death(user, target):
|
||||||
|
return Action("Death", user, target, Damage(0, 0, 0, 0), StatusChange(1, 0, 0), anim_default)
|
||||||
|
|
||||||
|
def move_phoenix(user, target):
|
||||||
|
return Action("Phoenix Down", user, target, Damage(0, 0, 0, 30), StatusChange(2, 0, 0), anim_default)
|
||||||
|
|
||||||
|
def move_softenup(user, target):
|
||||||
|
return Action("Esuna", user, target, Damage(0, 0, 0, 0), StatusChange(2, 0, 0), anim_default)
|
||||||
|
|
||||||
|
def enemy_turn_test(e,ep, ap,d):
|
||||||
|
living = [p for p in ap if not p.ko]
|
||||||
|
if not living:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# weighted move pool -- enemies get the same toolkit as the party
|
||||||
|
offensive = ["attack", "fire", "ice", "death"]
|
||||||
|
support = ["break"]
|
||||||
|
self_only = ["defend"]
|
||||||
|
|
||||||
|
# if an ally is KO'd, occasionally try to revive/cure instead of attacking
|
||||||
|
ko_allies = [a for a in ep if a.ko and a is not e]
|
||||||
|
hurt_allies = [a for a in ep if not a.ko and a.hp < a.maxhp]
|
||||||
|
|
||||||
|
roll = random.random()
|
||||||
|
|
||||||
|
if ko_allies and roll < 0.15:
|
||||||
|
act = "phoenix"
|
||||||
|
target = random.choice(ko_allies)
|
||||||
|
elif hurt_allies and roll < 0.30:
|
||||||
|
act = "cure"
|
||||||
|
target = random.choice(hurt_allies)
|
||||||
|
elif not e.def_up and roll < 0.40:
|
||||||
|
act = "defend"
|
||||||
|
target = e
|
||||||
|
elif roll < 0.55:
|
||||||
|
act = "break"
|
||||||
|
target = random.choice(living)
|
||||||
|
elif roll < 0.65:
|
||||||
|
act = "death"
|
||||||
|
target = random.choice(living)
|
||||||
|
else:
|
||||||
|
act = random.choice(["attack", "fire", "ice"])
|
||||||
|
target = random.choice(living)
|
||||||
|
|
||||||
|
_, builder, _ = d["moves"][act]
|
||||||
|
print(f" ({e.name} chooses {act} -> {target.name})")
|
||||||
|
return builder(e, target)
|
||||||
|
|
||||||
|
# !!! CHATGPT VÉGE !!!
|
||||||
|
|
||||||
|
battles = [
|
||||||
|
{ "enemies": [
|
||||||
|
Combatant("Six-Seven kid", 120, 30, 80, 20, 0, 50, False, False),
|
||||||
|
Combatant("Ice Sprite", 60, 15, 150, 10, 0, 200, False, True ),
|
||||||
|
Combatant("Magma Brute", 200, 40, 40, 60, 999, -50, True, False),
|
||||||
|
],
|
||||||
|
"party_members": [
|
||||||
|
Combatant("Locke", 80, 45, 130, 15, 10, 10, False, False),
|
||||||
|
Combatant("Terra", 100, 35, 100, 30, 30, 30, False, False),
|
||||||
|
Combatant("Celes", 110, 25, 90, 50, 40, 40, False, False),
|
||||||
|
Combatant("Cyan", 130, 38, 70, 40, 20, 20, False, False),
|
||||||
|
],
|
||||||
|
"enemy_turn_func": enemy_turn_test,
|
||||||
|
"mus_id": "mus_fnafsux",
|
||||||
|
"init_ai_data": {
|
||||||
|
"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),
|
||||||
|
"break": ("Break", move_break, True),
|
||||||
|
"death": ("Death", move_death, True),
|
||||||
|
"phoenix": ("Phoenix Down", move_phoenix, False),
|
||||||
|
"unstone": ("Esuna", move_softenup,False),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"menus": [
|
||||||
|
[ # Locke
|
||||||
|
["Attack", "phys_att"],
|
||||||
|
["", "unselectable"],
|
||||||
|
["", "unselectable"],
|
||||||
|
["Item", "items"]
|
||||||
|
],
|
||||||
|
[ # Terra
|
||||||
|
["Attack", "phys_att"],
|
||||||
|
["Magic", "magic"],
|
||||||
|
["", "unselectable"],
|
||||||
|
["Item", "items"]
|
||||||
|
],
|
||||||
|
[ # Celes
|
||||||
|
["Attack", "phys_att"],
|
||||||
|
["Magic", "magic"],
|
||||||
|
["", "unselectable"],
|
||||||
|
["Item", "items"]
|
||||||
|
],
|
||||||
|
[ # Cyan
|
||||||
|
["Attack", "phys_att"],
|
||||||
|
["", "unselectable"],
|
||||||
|
["", "unselectable"],
|
||||||
|
["Item", "items"]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"spells": [
|
||||||
|
[], # Locke
|
||||||
|
[ # Terra
|
||||||
|
["Fira", 0, ("Fira", move_fire, True)],
|
||||||
|
["Blizzara", 0, ("Blizzara", move_ice, True)],
|
||||||
|
],
|
||||||
|
[ # Celes
|
||||||
|
["Fira", 0, ("Fira", move_fire, True)],
|
||||||
|
["Blizzara", 0, ("Blizzara", move_ice, True)],
|
||||||
|
],
|
||||||
|
[] # Cyan
|
||||||
|
],
|
||||||
|
"inventory": [
|
||||||
|
["Phoenix Down",67, ("Phoenix Down", move_phoenix, False)]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -33,6 +33,11 @@ def update():
|
|||||||
c.pressed = False
|
c.pressed = False
|
||||||
c.held = True
|
c.held = True
|
||||||
|
|
||||||
|
def depress(cid):
|
||||||
|
if controls[cid].pressed:
|
||||||
|
controls[cid].pressed = False
|
||||||
|
controls[cid].held = True
|
||||||
|
|
||||||
def is_control_pressed(cid):
|
def is_control_pressed(cid):
|
||||||
return controls[cid].pressed
|
return controls[cid].pressed
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,25 @@
|
|||||||
import gamestates.none
|
import gamestates.none
|
||||||
import gamestates.title
|
import gamestates.title
|
||||||
|
import gamestates.overworld
|
||||||
|
import gamestates.battle
|
||||||
|
import gamestates.gameover
|
||||||
|
|
||||||
gamestates = {
|
gamestates = {
|
||||||
"none": gamestates.none,
|
"none": gamestates.none,
|
||||||
"title": gamestates.title
|
"title": gamestates.title,
|
||||||
|
"overworld": gamestates.overworld,
|
||||||
|
"battle": gamestates.battle,
|
||||||
|
"gameover": gamestates.gameover,
|
||||||
}
|
}
|
||||||
gamestate = "none"
|
gamestate = "none"
|
||||||
|
|
||||||
def switch_gamestate(state):
|
def switch_gamestate(game, state):
|
||||||
global gamestate
|
global gamestate
|
||||||
gamestate = state
|
gamestate = state
|
||||||
gamestates[gamestate].switch()
|
gamestates[gamestate].switch(game)
|
||||||
|
|
||||||
def update(dt):
|
def update(game, dt):
|
||||||
gamestates[gamestate].update(dt)
|
gamestates[gamestate].update(game, dt)
|
||||||
|
|
||||||
def draw(game):
|
def draw(game):
|
||||||
gamestates[gamestate].draw(game)
|
gamestates[gamestate].draw(game)
|
||||||
|
|||||||
275
src/gamestates/battle.py
Normal file
@@ -0,0 +1,275 @@
|
|||||||
|
import math
|
||||||
|
import copy
|
||||||
|
import random
|
||||||
|
import arcade
|
||||||
|
import gamestate
|
||||||
|
|
||||||
|
from battle import Combatant, Action, Damage, StatusChange
|
||||||
|
from menu import MenuItem
|
||||||
|
from text import TextRenderer
|
||||||
|
|
||||||
|
import assets
|
||||||
|
import battle_data
|
||||||
|
|
||||||
|
enemies = []
|
||||||
|
party_members = []
|
||||||
|
enemy_turn = None
|
||||||
|
battle_music = None
|
||||||
|
enemy_ai_data = {}
|
||||||
|
party_menus = []
|
||||||
|
spells = []
|
||||||
|
inventory = []
|
||||||
|
|
||||||
|
action_queue = []
|
||||||
|
cur_action = None
|
||||||
|
action_timer = 0
|
||||||
|
|
||||||
|
party_text = TextRenderer(3, 206, 210)
|
||||||
|
ene_text = TextRenderer(123, 206, 210)
|
||||||
|
menu_text = TextRenderer(3, 76, 210)
|
||||||
|
action_text = TextRenderer(110, 215, 210)
|
||||||
|
|
||||||
|
menu_open = 0
|
||||||
|
menu_queue = []
|
||||||
|
menu = []
|
||||||
|
|
||||||
|
battle_id = 0
|
||||||
|
|
||||||
|
def switch(game):
|
||||||
|
global enemies, party_members, battle_music, enemy_turn, enemy_ai_data, party_menus, spells, inventory
|
||||||
|
print("asdfasdfasdf")
|
||||||
|
enemies = copy.deepcopy(battle_data.battles[battle_id]["enemies"])
|
||||||
|
party_members = copy.deepcopy(battle_data.battles[battle_id]["party_members"])
|
||||||
|
enemy_turn = copy.deepcopy(battle_data.battles[battle_id]["enemy_turn_func"])
|
||||||
|
enemy_ai_data = copy.deepcopy(battle_data.battles[battle_id]["init_ai_data"])
|
||||||
|
party_menus = copy.deepcopy(battle_data.battles[battle_id]["menus"])
|
||||||
|
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)
|
||||||
|
|
||||||
|
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[0].select()
|
||||||
|
|
||||||
|
def close_menu():
|
||||||
|
global menu, menu_open
|
||||||
|
menu = []
|
||||||
|
menu_open = 0
|
||||||
|
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):
|
||||||
|
user, aparty, eparty, action = data
|
||||||
|
|
||||||
|
match action:
|
||||||
|
|
||||||
|
case "unselectable":
|
||||||
|
return
|
||||||
|
|
||||||
|
case "phys_att":
|
||||||
|
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()
|
||||||
|
|
||||||
|
case "magic":
|
||||||
|
open_spell_menu(user, aparty, eparty)
|
||||||
|
|
||||||
|
case "items":
|
||||||
|
open_item_menu(user, aparty, eparty)
|
||||||
|
|
||||||
|
case _:
|
||||||
|
print(f"Unknown action: {action}")
|
||||||
|
close_menu()
|
||||||
|
|
||||||
|
def update(game, dt):
|
||||||
|
global menu_open, cur_action, action_timer
|
||||||
|
|
||||||
|
for e in enemies:
|
||||||
|
if e.ko or e.stone:
|
||||||
|
continue
|
||||||
|
e.atb -= 1
|
||||||
|
if e.atb <= 0:
|
||||||
|
act = enemy_turn(e, enemies, party_members, enemy_ai_data)
|
||||||
|
if act:
|
||||||
|
action_queue.append(act)
|
||||||
|
e.atb = math.inf
|
||||||
|
|
||||||
|
for p in party_members:
|
||||||
|
if p.ko or p.stone:
|
||||||
|
continue
|
||||||
|
p.atb -= 1
|
||||||
|
if p.atb <= 0:
|
||||||
|
print(f"{p.name} is ready to act!")
|
||||||
|
menu_queue.append(p)
|
||||||
|
p.atb = math.inf
|
||||||
|
|
||||||
|
if len(action_queue) != 0 and cur_action == None:
|
||||||
|
action = action_queue.pop(0)
|
||||||
|
print(f"Executing: {action}")
|
||||||
|
cur_action = action
|
||||||
|
action_timer = 0
|
||||||
|
|
||||||
|
if cur_action != None:
|
||||||
|
if cur_action.animation(cur_action, action_text, action_timer, dt):
|
||||||
|
cur_action = None
|
||||||
|
else:
|
||||||
|
action_timer += dt
|
||||||
|
|
||||||
|
|
||||||
|
# Szövegek Frissítése
|
||||||
|
partytext = ""
|
||||||
|
enetext = ""
|
||||||
|
|
||||||
|
for m in party_members:
|
||||||
|
partytext += f"=={m.name}==\nHP: {m.hp}\n"
|
||||||
|
|
||||||
|
for e in enemies:
|
||||||
|
enetext += f"=={e.name}==\nHP: {e.hp}\n"
|
||||||
|
|
||||||
|
if len(menu_queue) != 0 and menu_open == 0:
|
||||||
|
whose = menu_queue.pop(0)
|
||||||
|
menu_open = whose
|
||||||
|
menu_text.reset(f"{whose.name}'s Menu")
|
||||||
|
menu_text.type_all()
|
||||||
|
open_menu(whose)
|
||||||
|
|
||||||
|
for m in menu:
|
||||||
|
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)
|
||||||
|
ene_text.reset(enetext)
|
||||||
|
party_text.type_all()
|
||||||
|
ene_text.type_all()
|
||||||
|
|
||||||
|
def draw(game):
|
||||||
|
for m in menu:
|
||||||
|
m.render()
|
||||||
|
|
||||||
|
party_text.render()
|
||||||
|
ene_text.render()
|
||||||
|
menu_text.render()
|
||||||
|
action_text.render()
|
||||||
|
|
||||||
38
src/gamestates/gameover.py
Normal 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()
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import gamestate
|
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(dt):
|
def update(game, dt):
|
||||||
gamestate.switch_gamestate("title")
|
gamestate.switch_gamestate(game, "overworld")
|
||||||
|
|||||||
80
src/gamestates/overworld.py
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
import assets
|
||||||
|
import arcade
|
||||||
|
import controls
|
||||||
|
import gamestate
|
||||||
|
import math
|
||||||
|
|
||||||
|
from objects import BalthasarRenderer, OverworldMap, DIR_UP, DIR_LEFT, DIR_DOWN, DIR_RIGHT
|
||||||
|
|
||||||
|
WALK_SPEED = 48
|
||||||
|
|
||||||
|
balthasar = BalthasarRenderer()
|
||||||
|
overworld = OverworldMap()
|
||||||
|
|
||||||
|
btrans_active = False
|
||||||
|
btrans_timer = 0
|
||||||
|
btrans_sound = None
|
||||||
|
BTRANS_PULSE_DUR = 0.25
|
||||||
|
BTRANS_DURATION = 0.75
|
||||||
|
|
||||||
|
def start_battle(battle_id):
|
||||||
|
global btrans_active, btrans_timer, btrans_sound
|
||||||
|
gamestate.gamestates["battle"].battle_id = battle_id
|
||||||
|
btrans_sound = assets.sounds["sfx_encounter"].play()
|
||||||
|
btrans_active = True
|
||||||
|
btrans_timer = 0
|
||||||
|
|
||||||
|
def switch(game):
|
||||||
|
in_battle_transition = False
|
||||||
|
|
||||||
|
def update(game, dt):
|
||||||
|
global btrans_active, btrans_timer
|
||||||
|
if btrans_active:
|
||||||
|
btrans_timer += dt
|
||||||
|
|
||||||
|
if btrans_timer >= BTRANS_DURATION:
|
||||||
|
game.mos_enabled = False
|
||||||
|
btrans_active = False
|
||||||
|
btrans_sound.stop()
|
||||||
|
gamestate.switch_gamestate(game,"battle")
|
||||||
|
|
||||||
|
game.mos_enabled = True
|
||||||
|
cycle_progress = (btrans_timer % BTRANS_PULSE_DUR) / BTRANS_PULSE_DUR
|
||||||
|
game.mosaic = max(1, math.floor(cycle_progress * 16))
|
||||||
|
return
|
||||||
|
|
||||||
|
balthasar.update()
|
||||||
|
|
||||||
|
if controls.is_control_pressed(4): # DEBUG: battle
|
||||||
|
start_battle(0)
|
||||||
|
|
||||||
|
if controls.is_control_pressed(0): # Fel
|
||||||
|
balthasar.set_direction(DIR_UP)
|
||||||
|
elif controls.is_control_pressed(1): # Le
|
||||||
|
balthasar.set_direction(DIR_DOWN)
|
||||||
|
elif controls.is_control_pressed(2): # Bal
|
||||||
|
balthasar.set_direction(DIR_LEFT)
|
||||||
|
elif controls.is_control_pressed(3): # Jobb
|
||||||
|
balthasar.set_direction(DIR_RIGHT)
|
||||||
|
|
||||||
|
walking = False
|
||||||
|
|
||||||
|
if controls.is_control_held(0): # Fel
|
||||||
|
overworld.scroll_y -= WALK_SPEED * dt
|
||||||
|
walking = True
|
||||||
|
elif controls.is_control_held(1): # Le
|
||||||
|
overworld.scroll_y += WALK_SPEED * dt
|
||||||
|
walking = True
|
||||||
|
|
||||||
|
if controls.is_control_held(2): # Bal
|
||||||
|
overworld.scroll_x += WALK_SPEED * dt
|
||||||
|
walking = True
|
||||||
|
elif controls.is_control_held(3): # Jobb
|
||||||
|
overworld.scroll_x -= WALK_SPEED * dt
|
||||||
|
walking = True
|
||||||
|
|
||||||
|
balthasar.set_walking(walking)
|
||||||
|
|
||||||
|
def draw(game):
|
||||||
|
overworld.render(game)
|
||||||
|
balthasar.render(game)
|
||||||
@@ -10,18 +10,26 @@ panel = renderers.NineSlicesPanel(0,100,230,100)
|
|||||||
|
|
||||||
m = []
|
m = []
|
||||||
|
|
||||||
def switch():
|
def mos_1x(g):
|
||||||
|
g.mosaic = 1
|
||||||
|
g.mos_enabled = False
|
||||||
|
|
||||||
|
def mos_2x(g):
|
||||||
|
g.mosaic = 8
|
||||||
|
g.mos_enabled = True
|
||||||
|
|
||||||
|
def switch(game):
|
||||||
global m
|
global m
|
||||||
m = [
|
m = [
|
||||||
menu.MenuItem("1", 100, 90, None, None, None, None, None, None),
|
menu.MenuItem("1", 100, 90, None, None, None, None, mos_1x, game),
|
||||||
menu.MenuItem("2", 100, 70 , None, None, None, None, None, None)
|
menu.MenuItem("2", 100, 70 , None, None, None, None, mos_2x, game)
|
||||||
]
|
]
|
||||||
|
|
||||||
m[0].down = m[1]
|
m[0].down = m[1]
|
||||||
m[1].up = m[0]
|
m[1].up = m[0]
|
||||||
m[0].select()
|
m[0].select()
|
||||||
|
|
||||||
def update(dt):
|
def update(game, dt):
|
||||||
controltext = ""
|
controltext = ""
|
||||||
|
|
||||||
for c in controls.controls:
|
for c in controls.controls:
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import text
|
|||||||
SCREEN_WIDTH = 256
|
SCREEN_WIDTH = 256
|
||||||
SCREEN_HEIGHT = 224
|
SCREEN_HEIGHT = 224
|
||||||
|
|
||||||
|
# !!! CHATGPT !!!
|
||||||
class Game(arcade.Window):
|
class Game(arcade.Window):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
@@ -19,6 +20,8 @@ class Game(arcade.Window):
|
|||||||
|
|
||||||
self.screen_width = SCREEN_WIDTH
|
self.screen_width = SCREEN_WIDTH
|
||||||
self.screen_height = SCREEN_HEIGHT
|
self.screen_height = SCREEN_HEIGHT
|
||||||
|
self.mosaic = 1
|
||||||
|
self.mos_enabled = False
|
||||||
|
|
||||||
self.game_texture = self.ctx.texture(
|
self.game_texture = self.ctx.texture(
|
||||||
(SCREEN_WIDTH, SCREEN_HEIGHT),
|
(SCREEN_WIDTH, SCREEN_HEIGHT),
|
||||||
@@ -33,7 +36,7 @@ class Game(arcade.Window):
|
|||||||
self.renderer = renderers.TextureRenderer(self.ctx)
|
self.renderer = renderers.TextureRenderer(self.ctx)
|
||||||
|
|
||||||
def on_update(self, dt):
|
def on_update(self, dt):
|
||||||
gamestate.update(dt)
|
gamestate.update(game, dt)
|
||||||
controls.update()
|
controls.update()
|
||||||
|
|
||||||
def on_key_press(self, symbol, modifiers):
|
def on_key_press(self, symbol, modifiers):
|
||||||
@@ -73,6 +76,8 @@ class Game(arcade.Window):
|
|||||||
y,
|
y,
|
||||||
scaled_width,
|
scaled_width,
|
||||||
scaled_height,
|
scaled_height,
|
||||||
|
self.mosaic,
|
||||||
|
self.mos_enabled
|
||||||
)
|
)
|
||||||
|
|
||||||
game = Game()
|
game = Game()
|
||||||
|
|||||||
21
src/menu.py
@@ -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,23 +31,27 @@ 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)
|
||||||
self.selected = False
|
self.selected = False
|
||||||
self.up.select()
|
self.up.select()
|
||||||
|
elif controls.is_control_pressed(1) and self.down: # Le
|
||||||
if controls.is_control_pressed(1) and self.down: # Le
|
controls.depress(1)
|
||||||
self.selected = False
|
self.selected = False
|
||||||
self.down.select()
|
self.down.select()
|
||||||
|
elif controls.is_control_pressed(2) and self.left: # Bal
|
||||||
if controls.is_control_pressed(2) and self.left: # Bal
|
controls.depress(2)
|
||||||
self.selected = False
|
self.selected = False
|
||||||
self.left.select()
|
self.left.select()
|
||||||
|
elif controls.is_control_pressed(3) and self.right: # Jobb
|
||||||
if controls.is_control_pressed(3) and self.right: # Jobb
|
controls.depress(3)
|
||||||
self.selected = False
|
self.selected = False
|
||||||
self.right.select()
|
self.right.select()
|
||||||
|
|
||||||
|
|
||||||
def render(self):
|
def render(self):
|
||||||
self.text.render()
|
self.text.render()
|
||||||
|
|
||||||
|
|||||||
100
src/objects.py
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
import assets
|
||||||
|
import arcade
|
||||||
|
|
||||||
|
# Main character(balthasar) renderer
|
||||||
|
DIR_DOWN = 0
|
||||||
|
DIR_UP = 1
|
||||||
|
DIR_RIGHT = 2
|
||||||
|
DIR_LEFT = 3
|
||||||
|
|
||||||
|
B_SPRITE_WIDHT = 16
|
||||||
|
B_SPRITE_HEIGHT = 24
|
||||||
|
B_SPRITE_WALKING = 3
|
||||||
|
|
||||||
|
|
||||||
|
class BalthasarRenderer:
|
||||||
|
def __init__(self):
|
||||||
|
self.direction = DIR_UP
|
||||||
|
self.walking = False
|
||||||
|
self.anim_timer = 0
|
||||||
|
|
||||||
|
def set_direction(self, direction):
|
||||||
|
self.direction = direction
|
||||||
|
|
||||||
|
def set_walking(self, walking):
|
||||||
|
self.walking = walking
|
||||||
|
if walking == False:
|
||||||
|
self.anim_timer = 0
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
self.anim_timer += 1
|
||||||
|
if self.anim_timer > 30:
|
||||||
|
self.anim_timer = 0
|
||||||
|
|
||||||
|
def render(self, game):
|
||||||
|
sprite_h = self.direction if self.direction < 3 else 2
|
||||||
|
sprite_v = 1 if (self.walking and self.anim_timer <= 15) else 0
|
||||||
|
flip_h = self.direction == DIR_LEFT
|
||||||
|
|
||||||
|
sprite = assets.sprites["balthasar"].get_texture(arcade.LBWH(B_SPRITE_WIDHT*sprite_h, B_SPRITE_HEIGHT*sprite_v, B_SPRITE_WIDHT, B_SPRITE_HEIGHT))
|
||||||
|
arcade.draw_texture_rect(
|
||||||
|
sprite.flip_horizontally() if flip_h else sprite,
|
||||||
|
arcade.LBWH((game.screen_width / 2) - (B_SPRITE_WIDHT / 2), (game.screen_height / 2) - (B_SPRITE_HEIGHT / 2), B_SPRITE_WIDHT, B_SPRITE_HEIGHT),
|
||||||
|
pixelated=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# Overworld Map
|
||||||
|
|
||||||
|
OVERWORLD_LB_X = 0
|
||||||
|
OVERWORLD_LB_Y = 0
|
||||||
|
OVERWORLD_TILE_WIDTH = 16
|
||||||
|
OVERWORLD_TILE_HEIGHT = 16
|
||||||
|
OVERWORLD_WIDTH = 4
|
||||||
|
OVERWORLD_HEIGHT = 4
|
||||||
|
OVERWORLD_MAP = [
|
||||||
|
[5,3,3,6],
|
||||||
|
[1,0,0,2],
|
||||||
|
[1,0,0,2],
|
||||||
|
[7,4,4,8],
|
||||||
|
]
|
||||||
|
|
||||||
|
class OverworldMap:
|
||||||
|
def __init__(self):
|
||||||
|
self.sprites = []
|
||||||
|
|
||||||
|
self.scroll_x = 0
|
||||||
|
self.scroll_y = 0
|
||||||
|
|
||||||
|
x = 0
|
||||||
|
y = len(OVERWORLD_MAP)
|
||||||
|
for row in OVERWORLD_MAP:
|
||||||
|
for tile in row:
|
||||||
|
self.sprites.append([
|
||||||
|
assets.sprites["overworld"].get_texture(
|
||||||
|
arcade.LBWH((tile % 4) * OVERWORLD_TILE_WIDTH, (tile // 4) * OVERWORLD_TILE_HEIGHT,
|
||||||
|
OVERWORLD_TILE_WIDTH, OVERWORLD_TILE_HEIGHT)
|
||||||
|
),
|
||||||
|
arcade.LBWH(
|
||||||
|
(x * OVERWORLD_TILE_WIDTH),
|
||||||
|
(y * OVERWORLD_TILE_HEIGHT),
|
||||||
|
OVERWORLD_TILE_WIDTH,
|
||||||
|
OVERWORLD_TILE_HEIGHT
|
||||||
|
)
|
||||||
|
])
|
||||||
|
x += 1
|
||||||
|
y -= 1
|
||||||
|
x = 0
|
||||||
|
|
||||||
|
|
||||||
|
def render(self, game):
|
||||||
|
for s in self.sprites:
|
||||||
|
arcade.draw_texture_rect(
|
||||||
|
s[0],
|
||||||
|
s[1].move(
|
||||||
|
self.scroll_x + (game.screen_width / 2) - ((OVERWORLD_WIDTH * OVERWORLD_TILE_WIDTH) / 2),
|
||||||
|
self.scroll_y + (game.screen_height / 2) - ((OVERWORLD_HEIGHT * OVERWORLD_TILE_HEIGHT) / 2)
|
||||||
|
),
|
||||||
|
pixelated=True
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -34,24 +34,36 @@ class TextureRenderer:
|
|||||||
""",
|
""",
|
||||||
fragment_shader="""
|
fragment_shader="""
|
||||||
#version 330
|
#version 330
|
||||||
|
#define INV_RESOLUTION_X (1. / bounds.z)
|
||||||
|
#define INV_RESOLUTION_Y (1. / bounds.w)
|
||||||
|
|
||||||
|
uniform vec4 bounds;
|
||||||
uniform sampler2D tex;
|
uniform sampler2D tex;
|
||||||
|
uniform float mosaic;
|
||||||
|
uniform bool mos_enabled;
|
||||||
|
|
||||||
in vec2 uv;
|
in vec2 uv;
|
||||||
out vec4 color;
|
out vec4 color;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
color = texture(tex, uv);
|
vec2 new_uv = vec2(
|
||||||
|
(floor((uv.x / INV_RESOLUTION_X) / mosaic) * INV_RESOLUTION_X) * mosaic,
|
||||||
|
(floor((uv.y / INV_RESOLUTION_Y) / mosaic) * INV_RESOLUTION_Y) * mosaic
|
||||||
|
);
|
||||||
|
|
||||||
|
color = texture(tex, mos_enabled ? new_uv : uv);
|
||||||
}
|
}
|
||||||
""",
|
""",
|
||||||
)
|
)
|
||||||
|
|
||||||
self.quad = arcade.gl.geometry.quad_2d_fs()
|
self.quad = arcade.gl.geometry.quad_2d_fs()
|
||||||
|
|
||||||
def render(self, texture, x, y, width, height):
|
def render(self, texture, x, y, width, height, mosaic, mos_enabled):
|
||||||
self.program["screen_size"] = (self.ctx.screen.width,
|
self.program["screen_size"] = (self.ctx.screen.width,
|
||||||
self.ctx.screen.height)
|
self.ctx.screen.height)
|
||||||
self.program["bounds"] = (x, y, width, height)
|
self.program["bounds"] = (x, y, width, height)
|
||||||
|
self.program["mosaic"] = mosaic
|
||||||
|
self.program["mos_enabled"] = mos_enabled
|
||||||
|
|
||||||
texture.use(0)
|
texture.use(0)
|
||||||
self.program["tex"] = 0
|
self.program["tex"] = 0
|
||||||
|
|||||||