AI generálta csaták

This commit is contained in:
markojozsef
2026-10-15 14:46:51 +02:00
parent 1383587e35
commit 158a375da2
5 changed files with 419 additions and 116 deletions

View File

@@ -13,7 +13,7 @@ Teljesen AI-generált szakaszokat egy `# !!! CHATGPT !!!` komment jelzi
* NineSlicesPanel
### battle_data.py
* csata függvények
* MINDEN
### gamestates/battle.py
* menük

View File

@@ -1,6 +1,10 @@
import random
from battle import Combatant, Action, Damage, StatusChange
# ---------------------------------------------------------------------------
# Animáció (ugyanaz, mint az eredetiben)
# ---------------------------------------------------------------------------
def anim_default(action, text, timer, dt):
if timer == 0:
text.reset(action.name)
@@ -13,142 +17,438 @@ def anim_default(action, text, timer, dt):
return False
# !!! CHATGPT !!!
# ---------------------------------------------------------------------------
# Hősök (ellenfelek) képességei
# StatusChange(halál, kő, védés): 1 = rátesz, 2 = levesz
# ---------------------------------------------------------------------------
def move_attack(user, target):
return Action("Támadás", 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)
return Action("Tűzlabda", user, target, Damage(0, 40, 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)
return Action("Jégszilánk", user, target, Damage(0, 0, 40, 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)
return Action("Gyógyítás", user, target, Damage(0, 0, 0, 45), StatusChange(0, 0, 0), anim_default)
def move_defend(user, target):
return Action("Védés", user, target, Damage(0, 0, 0, 0), StatusChange(0, 0, 1), anim_default)
return Action("Védekezés", 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)
# kővé változtatás
return Action("Kővé Változtatás", user, target, Damage(0, 0, 0, 0), StatusChange(0, 1, 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)
return Action("Főnixtoll", 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 move_esuna(user, target):
# kő állapot levétele
return Action("Megtisztítás", user, target, Damage(0, 0, 0, 0), StatusChange(0, 2, 0), anim_default)
def enemy_turn_test(e,ep, ap,d):
def move_potion(user, target):
return Action("Gyógyital", user, target, Damage(0, 0, 0, 60), StatusChange(0, 0, 0), anim_default)
# ---------------------------------------------------------------------------
# Főellenség (játékos) képességei
# ---------------------------------------------------------------------------
def move_crush(user, target):
return Action("Zúzó Csapás", user, target, Damage(user.attack * 2, 0, 0, 0), StatusChange(0, 0, 0), anim_default)
def move_hellfire(user, target):
return Action("Pokoltűz", user, target, Damage(0, 80, 0, 0), StatusChange(0, 0, 0), anim_default)
def move_frost(user, target):
return Action("Sarki Fagy", user, target, Damage(0, 0, 80, 0), StatusChange(0, 0, 0), anim_default)
def move_dark_heal(user, target):
return Action("Sötét Gyógyulás", user, target, Damage(0, 0, 0, 80), StatusChange(0, 0, 0), anim_default)
def move_dark_heal_minor(user, target):
return Action("Csontkovácsolás", user, target, Damage(0, 0, 0, 40), StatusChange(0, 0, 0), anim_default)
def move_curse(user, target):
return Action("Halálos Átok", user, target, Damage(0, 0, 0, 0), StatusChange(1, 0, 0), anim_default)
def move_petrify(user, target):
return Action("Kőátok", user, target, Damage(0, 0, 0, 0), StatusChange(0, 1, 0), anim_default)
def move_raise(user, target):
return Action("Felélesztés", user, target, Damage(0, 0, 0, 60), StatusChange(2, 0, 0), anim_default)
def move_bone_toss(user, target):
return Action("Csontdobás", user, target, Damage(user.attack + 15, 0, 0, 0), StatusChange(0, 0, 0), anim_default)
# ---------------------------------------------------------------------------
# Segédfüggvények a hősök MI-jéhez
# ---------------------------------------------------------------------------
def _choose(e, d, act, target):
_, builder, _ = d["moves"][act]
print(f" ({e.name} választása: {act} -> {target.name})")
return builder(e, target)
def _can_petrify(d):
# kővé változtatást csak akkor használnak a hősök, ha a csatában
# a főellenség oldalának van Megtisztítása (a data-ban benne van a "break")
return "break" in d["moves"]
def _best_element(target):
"""Eldönti, hogy tűz vagy jég jobb-e a célpont ellen (nyelő elemet elkerüli)."""
fire_ok = not getattr(target, "abs_fire", False)
ice_ok = not getattr(target, "abs_ice", False)
if fire_ok and ice_ok:
if getattr(target, "def_fire", 0) <= getattr(target, "def_ice", 0):
return "fire"
return "ice"
if fire_ok:
return "fire"
if ice_ok:
return "ice"
return "attack"
# ---------------------------------------------------------------------------
# 1. csata MI: kezdő hősök, egyszerű és kiszámítható
# ---------------------------------------------------------------------------
def hero_turn_rookies(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]
hurt_allies = [a for a in ep if not a.ko and a.hp < a.maxhp * 0.5]
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)
if hurt_allies and roll < 0.25:
return _choose(e, d, "cure", random.choice(hurt_allies))
if not e.def_up and e.hp < e.maxhp * 0.4 and roll < 0.6:
return _choose(e, d, "defend", e)
if _can_petrify(d) and roll < 0.15:
return _choose(e, d, "break", random.choice(living))
return _choose(e, d, random.choice(["attack", "attack", "fire", "ice"]), random.choice(living))
_, builder, _ = d["moves"][act]
print(f" ({e.name} chooses {act} -> {target.name})")
return builder(e, target)
# !!! CHATGPT VÉGE !!!
# ---------------------------------------------------------------------------
# 2. csata MI: elemi hősök, kerülik a nyelő elemet
# ---------------------------------------------------------------------------
def hero_turn_elemental(e, ep, ap, d):
living = [p for p in ap if not p.ko]
if not living:
return None
hurt_allies = [a for a in ep if not a.ko and a.hp < a.maxhp * 0.4]
roll = random.random()
if hurt_allies and roll < 0.25:
return _choose(e, d, "cure", random.choice(hurt_allies))
if _can_petrify(d) and roll < 0.35:
return _choose(e, d, "break", random.choice(living))
target = random.choice(living)
return _choose(e, d, _best_element(target), target)
# ---------------------------------------------------------------------------
# 3. csata MI: a főpap folyamatosan gyógyít és éleszt
# ---------------------------------------------------------------------------
HEALER_NAMES = {"Lilla, a Főpap"}
def hero_turn_healers(e, ep, ap, d):
living = [p for p in ap if not p.ko]
if not living:
return None
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 * 0.6]
is_healer = e.name in HEALER_NAMES
roll = random.random()
if is_healer:
if ko_allies and roll < 0.6:
return _choose(e, d, "phoenix", random.choice(ko_allies))
if hurt_allies and roll < 0.9:
return _choose(e, d, "cure", min(hurt_allies, key=lambda a: a.hp))
return _choose(e, d, "attack", random.choice(living))
if hurt_allies and roll < 0.1:
return _choose(e, d, "cure", random.choice(hurt_allies))
if roll < 0.2 and not e.def_up:
return _choose(e, d, "defend", e)
if _can_petrify(d) and roll < 0.4:
return _choose(e, d, "break", random.choice(living))
return _choose(e, d, "attack", random.choice(living))
# ---------------------------------------------------------------------------
# 4. csata MI: összehangolt hősök, a leggyengébb főellenségre fókuszálnak
# ---------------------------------------------------------------------------
def hero_turn_final(e, ep, ap, d):
living = [p for p in ap if not p.ko]
if not living:
return None
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 * 0.35]
weakest = min(living, key=lambda p: p.hp)
strongest = max(living, key=lambda p: p.attack)
roll = random.random()
if ko_allies and roll < 0.2:
return _choose(e, d, "phoenix", random.choice(ko_allies))
if hurt_allies and roll < 0.4:
return _choose(e, d, "cure", random.choice(hurt_allies))
if not e.def_up and e.hp < e.maxhp * 0.3:
return _choose(e, d, "defend", e)
if _can_petrify(d) and roll < 0.55:
return _choose(e, d, "break", strongest)
if roll < 0.8:
return _choose(e, d, _best_element(weakest), weakest)
return _choose(e, d, "attack", weakest)
# ---------------------------------------------------------------------------
# Közös hős-képességlista
# can_petrify=True csak akkor, ha a főellenség oldalán van Megtisztítás
# ---------------------------------------------------------------------------
def _hero_moves(can_petrify=False):
moves = {
"attack": ("Fizikai támadás", move_attack, True),
"fire": ("Tűzlabda", move_fire, True),
"ice": ("Jégszilánk", move_ice, True),
"cure": ("Gyógyítás", move_cure, False),
"defend": ("Védekezés", move_defend, None),
"phoenix": ("Főnixtoll", move_phoenix, False),
"unstone": ("Megtisztítás", move_esuna, False),
}
if can_petrify:
moves["break"] = ("Kővé Változtatás", move_break, True)
return {"moves": moves}
# ---------------------------------------------------------------------------
# CSATÁK: "te irányítod a főellenséget"
# "party_members" = a főellenség és csatlósai (játékos)
# "enemies" = a betolakodó hősök (MI)
# Ellenfél-célzás: "ene", szövetséges-célzás (spellek és tárgyak): "all_rev"
# ---------------------------------------------------------------------------
battles = [
# -----------------------------------------------------------------------
# 1. A Trónterem védelme (nincs Megtisztítás -> a hősök nem kövesítenek)
# -----------------------------------------------------------------------
{ "enemies": [
Combatant("Six-Seven gyerek", 120, 30, 80, 20, 0, 50, False, False),
Combatant("Jég Manó", 60, 15, 150, 10, 0, 200, False, True ),
Combatant("Láva Bestia", 200, 40, 40, 60, 999, -50, True, False),
Combatant("Aldor, az Ifjú Lovag", 130, 28, 90, 20, 0, 0, False, False),
Combatant("Mira, a Tanonc Varázsló", 90, 15, 110, 5, 10, 10, False, False),
Combatant("Bence, a Gyakornok Pap", 100, 12, 80, 10, 20, 20, False, 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),
Combatant("Mordrak, a Sötét Úr", 400, 50, 70, 30, 20, 20, False, False),
Combatant("Csontvázőr", 150, 35, 60, 40, -40, 0, False, False),
],
"enemy_turn_func": enemy_turn_test,
"enemy_turn_func": hero_turn_rookies,
"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),
}
},
"init_ai_data": _hero_moves(can_petrify=False),
"menus": [
[ # Locke
["Harc", "phys_att"],
["", "unselectable"],
["", "unselectable"],
["Tárgy", "items"]
],
[ # Terra
[ # Mordrak
["Harc", "phys_att"],
["Varázslat", "magic"],
["", "unselectable"],
["Tárgy", "items"]
],
[ # Celes
[ # Csontvázőr
["Harc", "phys_att"],
["Varázslat", "magic"],
["", "unselectable"],
["Tárgy", "items"]
],
[ # Cyan
["Harc", "phys_att"],
["", "unselectable"],
["", "unselectable"],
["Tárgy", "items"]
]
],
"spells": [
[], # Locke
[ # Terra
["Fira", 0, ("Fira", move_fire, "ene")],
["Blizzara", 0, ("Blizzara", move_ice, "ene")],
[ # Mordrak
["Pokoltűz", 0, ("Pokoltűz", move_hellfire, "ene")],
["Sarki Fagy", 0, ("Sarki Fagy", move_frost, "ene")],
["Zúzó Csapás", 0, ("Zúzó Csapás", move_crush, "ene")],
["Sötét Gyógyulás", 0, ("Sötét Gyógyulás", move_dark_heal, "all_rev")],
],
[ # Celes
["Fira", 0, ("Fira", move_fire, "ene")],
["Blizzara", 0, ("Blizzara", move_ice, "ene")],
],
[] # Cyan
[ # Csontvázőr
["Csontkovácsolás", 0, ("Csontkovácsolás", move_dark_heal_minor, "all_rev")],
]
],
"inventory": [
["Phoenix Down",67, ("Phoenix Down", move_phoenix, "all_rev")]
["Gyógyital", 3, ("Gyógyital", move_potion, "all_rev")],
["Főnixtoll", 2, ("Főnixtoll", move_phoenix, "all_rev")]
]
}
},
# -----------------------------------------------------------------------
# 2. A Tűz és Jég Kapuja (a sárkányúrnő nyeli a tüzet, de a jég fáj neki)
# Nincs kőátok, a hősök sem kövesítenek.
# -----------------------------------------------------------------------
{ "enemies": [
Combatant("Korom, a Tűzbűvölő", 110, 20, 95, 10, 60, 0, False, False),
Combatant("Fagyi, a Jégmágus", 110, 20, 100, 10, 0, 60, False, False),
Combatant("Szikla, a Pajzsos", 220, 30, 50, 60, 10, 10, False, False),
],
"party_members": [
Combatant("Ignisza, a Sárkányúrnő", 450, 45, 85, 25, 999, -60, True, False),
Combatant("Parázs Sárkánykölyök", 180, 40, 100, 30, 999, -50, True, False),
],
"enemy_turn_func": hero_turn_elemental,
"mus_id": "mus_fnafsux",
"init_ai_data": _hero_moves(can_petrify=False),
"menus": [
[ # Ignisza
["Harc", "phys_att"],
["Varázslat", "magic"],
["", "unselectable"],
["Tárgy", "items"]
],
[ # Parázs
["Harc", "phys_att"],
["Varázslat", "magic"],
["", "unselectable"],
["Tárgy", "items"]
]
],
"spells": [
[ # Ignisza
["Pokoltűz", 0, ("Pokoltűz", move_hellfire, "ene")],
["Zúzó Csapás", 0, ("Zúzó Csapás", move_crush, "ene")],
["Sötét Gyógyulás", 0, ("Sötét Gyógyulás", move_dark_heal, "all_rev")],
],
[ # Parázs
["Pokoltűz", 0, ("Pokoltűz", move_hellfire, "ene")],
["Csontkovácsolás", 0, ("Csontkovácsolás", move_dark_heal_minor, "all_rev")],
]
],
"inventory": [
["Gyógyital", 3, ("Gyógyital", move_potion, "all_rev")],
["Főnixtoll", 3, ("Főnixtoll", move_phoenix, "all_rev")]
]
},
# -----------------------------------------------------------------------
# 3. A Nekromanta Kriptája (előbb a főpapot kell elintézni!)
# A Csontmágus tud Megtisztítani, így a hősök kövesíthetnek.
# -----------------------------------------------------------------------
{ "enemies": [
Combatant("Lilla, a Főpap", 150, 10, 100, 10, 30, 30, False, False),
Combatant("Garat, a Harcos", 240, 45, 70, 40, 0, 0, False, False),
Combatant("Réka, a Fürge Tolvaj", 100, 40, 140, 10, 0, 0, False, False),
],
"party_members": [
Combatant("Vorik, a Nekromanta", 320, 35, 90, 15, 10, 10, False, False),
Combatant("Zombi Szolga", 140, 30, 40, 25, -30, 20, False, False),
Combatant("Csontmágus", 90, 20, 95, 5, 0, 0, False, False),
],
"enemy_turn_func": hero_turn_healers,
"mus_id": "mus_fnafsux",
"init_ai_data": _hero_moves(can_petrify=True),
"menus": [
[ # Vorik
["Harc", "phys_att"],
["Varázslat", "magic"],
["", "unselectable"],
["Tárgy", "items"]
],
[ # Zombi Szolga
["Harc", "phys_att"],
["", "unselectable"],
["", "unselectable"],
["Tárgy", "items"]
],
[ # Csontmágus
["Harc", "phys_att"],
["Varázslat", "magic"],
["", "unselectable"],
["Tárgy", "items"]
]
],
"spells": [
[ # Vorik
["Halálos Átok", 0, ("Halálos Átok", move_curse, "ene")],
["Kőátok", 0, ("Kőátok", move_petrify, "ene")],
["Felélesztés", 0, ("Felélesztés", move_raise, "all_rev")],
["Sötét Gyógyulás", 0, ("Sötét Gyógyulás", move_dark_heal, "all_rev")],
],
[], # Zombi Szolga
[ # Csontmágus
["Csontdobás", 0, ("Csontdobás", move_bone_toss, "ene")],
["Sarki Fagy", 0, ("Sarki Fagy", move_frost, "ene")],
["Megtisztítás", 0, ("Megtisztítás", move_esuna, "all_rev")],
["Csontkovácsolás", 0, ("Csontkovácsolás", move_dark_heal_minor, "all_rev")],
]
],
"inventory": [
["Gyógyital", 3, ("Gyógyital", move_potion, "all_rev")],
["Főnixtoll", 2, ("Főnixtoll", move_phoenix, "all_rev")]
]
},
# -----------------------------------------------------------------------
# 4. A Hősök Utolsó Bástyája (a végső küzdelem: három főellenség négy hős ellen)
# Nyx tud Megtisztítani, így a hősök kövesíthetnek.
# -----------------------------------------------------------------------
{ "enemies": [
Combatant("Szent Aurélia, a Vezér", 260, 42, 100, 35, 25, 25, False, False),
Combatant("Bálint, a Lovagkapitány", 220, 48, 80, 50, 10, 10, False, False),
Combatant("Eszter, a Nagymágus", 130, 22, 105, 10, 50, 50, False, False),
Combatant("Domonkos, a Gyógyító", 140, 14, 95, 15, 30, 30, False, False),
],
"party_members": [
Combatant("Sötét Úr Zagorath", 600, 55, 80, 35, 30, 30, False, False),
Combatant("Árnyboszorkány Nyx", 300, 30, 110, 10, 60, 60, False, False),
Combatant("Csontlovag Gorm", 380, 50, 60, 70, -20, 40, False, False),
],
"enemy_turn_func": hero_turn_final,
"mus_id": "mus_fnafsux",
"init_ai_data": _hero_moves(can_petrify=True),
"menus": [
[ # Zagorath
["Harc", "phys_att"],
["Varázslat", "magic"],
["", "unselectable"],
["Tárgy", "items"]
],
[ # Nyx
["Harc", "phys_att"],
["Varázslat", "magic"],
["", "unselectable"],
["Tárgy", "items"]
],
[ # Gorm
["Harc", "phys_att"],
["Varázslat", "magic"],
["", "unselectable"],
["Tárgy", "items"]
]
],
"spells": [
[ # Zagorath
["Pokoltűz", 0, ("Pokoltűz", move_hellfire, "ene")],
["Sarki Fagy", 0, ("Sarki Fagy", move_frost, "ene")],
["Zúzó Csapás", 0, ("Zúzó Csapás", move_crush, "ene")],
["Sötét Gyógyulás", 0, ("Sötét Gyógyulás", move_dark_heal, "all_rev")],
],
[ # Nyx
["Halálos Átok", 0, ("Halálos Átok", move_curse, "ene")],
["Kőátok", 0, ("Kőátok", move_petrify, "ene")],
["Sötét Gyógyulás", 0, ("Sötét Gyógyulás", move_dark_heal, "all_rev")],
["Felélesztés", 0, ("Felélesztés", move_raise, "all_rev")],
["Megtisztítás", 0, ("Megtisztítás", move_esuna, "all_rev")],
],
[ # Gorm
["Zúzó Csapás", 0, ("Zúzó Csapás", move_crush, "ene")],
["Csontdobás", 0, ("Csontdobás", move_bone_toss, "ene")],
["Csontkovácsolás", 0, ("Csontkovácsolás", move_dark_heal_minor, "all_rev")],
]
],
"inventory": [
["Gyógyital", 4, ("Gyógyital", move_potion, "all_rev")],
["Főnixtoll", 3, ("Főnixtoll", move_phoenix, "all_rev")]
]
},
]

View File

@@ -210,7 +210,7 @@ def menu_action(data):
def queue_attack(targets):
action_queue.append(Action(
"Támadás", user, targets[0],
Damage(166, 0, 0, 0), StatusChange(0, 0, 0),
Damage(user.attack, 0, 0, 0), StatusChange(0, 0, 0),
battle_data.anim_default))
open_target_menu(user, aparty, eparty, "ene", queue_attack,

View File

@@ -4,14 +4,17 @@ import controls
import gamestate
import math
from objects import BalthasarRenderer, OverworldMap, TestObject, DIR_UP, DIR_LEFT, DIR_DOWN, DIR_RIGHT, B_SPRITE_HEIGHT
from objects import BalthasarRenderer, OverworldMap, BattlePodium, DIR_UP, DIR_LEFT, DIR_DOWN, DIR_RIGHT, B_SPRITE_HEIGHT
WALK_SPEED = 48
balthasar = BalthasarRenderer()
overworld = OverworldMap()
objects = [
TestObject(0,16),
BattlePodium(0 , 0, 0),
BattlePodium(48, 0, 1),
BattlePodium(0 ,48, 2),
BattlePodium(48,48, 3),
balthasar
]

View File

@@ -152,14 +152,15 @@ class OverworldMap:
pixelated=True
)
# Teszt objektum
class TestObject:
def __init__(self, x, y):
# Csata Szobor
class BattlePodium:
def __init__(self, x, y, battle_id):
self.x = x
self.y = y
self.w = 16
self.h = 16
self.interactive = True
self.battle = battle_id
def get_aabb(self):
return self.x, self.y, self.x + self.w, self.y + self.h
@@ -171,5 +172,4 @@ class TestObject:
)
def on_interact(self, start_battle_func):
print("never gonna give you up")
start_battle_func(0)
start_battle_func(self.battle)