import math import copy import random import renderers import arcade import gamestate from PIL import Image 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 = [] enemy_sprites = [] party_sprites = [] gray_cache = {} action_queue = [] cur_action = None action_timer = 0 menu_text = TextRenderer(6, 76, 210) action_text = TextRenderer(110, 215, 210) menu_open = 0 menu_queue = [] menu = [] panel = renderers.NineSlicesPanel(0,0,230,100) battle_id = 0 def enemy_pos(i): return (185, 195 - i * 40) def party_pos(i): return (40 + (i % 2) * 30, 165 - i * 35) def switch(game): global enemies, party_members, battle_music, enemy_turn, enemy_ai_data, party_menus, spells, inventory, enemy_sprites, party_sprites 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"]) enemy_sprites = battle_data.battles[battle_id]["enemy_sprites"] party_sprites = battle_data.battles[battle_id]["party_sprites"] battle_music = assets.sounds[battle_data.battles[battle_id]["mus_id"]].play(loops=True) close_menu() 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), open_menu ) ) 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_name, func, target_type) = spell open_target_menu( user, aparty, eparty, target_type, lambda targets: action_queue.append(func(user, targets[0])), lambda *a: open_spell_menu(user, aparty, eparty)) 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), open_menu ) ) 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_name, func, target_type) = item def use(targets): action_queue.append(func(user, targets[0])) item[1] -= 1 open_target_menu(user, aparty, eparty, target_type, use, lambda *a: open_item_menu(user, aparty, eparty)) def open_target_menu(user, aparty, eparty, target_type, on_pick, back): """ target_type: "enemy", "ally", "self", "all_enemies", "all_allies" on_pick: called with a LIST of targets back: called when the player cancels (reopens the previous menu) """ global menu # No selection needed if target_type == "self": on_pick([user]); close_menu(); return pool = eparty if target_type in ["ene", "ene_rev"] else aparty candidates = [c for c in pool if not c.ko] if target_type not in ["ene_rev", "all_rev"] else pool if not candidates: return menu = [] for i, c in enumerate(candidates): name = c.name + "(" + str(c.hp) + ("," if c.def_up or c.stone else "") + ("Kő" if c.stone else "") + (", " if c.def_up and c.stone else "") + ("Védés" if c.def_up else "") + ")" menu.append(MenuItem( name, 19, 64 - (i * 13), None, None, None, None, target_picked, (c, on_pick), back )) for i, m in enumerate(menu): m.up = menu[(i - 1) % len(menu)] m.down = menu[(i + 1) % len(menu)] menu[0].select() def target_picked(data): target, on_pick = data on_pick([target]) close_menu() def open_menu(data): global menu whose, junk, aparty, eparty = data menu = [] 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, aparty, eparty, 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 menu_action(data): user, aparty, eparty, action = data match action: case "unselectable": return case "phys_att": def queue_attack(targets): action_queue.append(Action( "Támadás", user, targets[0], Damage(user.attack, 0, 0, 0), StatusChange(0, 0, 0), battle_data.anim_default)) open_target_menu(user, aparty, eparty, "ene", queue_attack, lambda *a: open_menu((user, 0, aparty, eparty))) case "magic": open_spell_menu(user, aparty, eparty) case "items": open_item_menu(user, aparty, eparty) case _: print(f"Unknown action: {action}") close_menu() # !!! CHATGPT VÉGE !!! 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 if len(menu_queue) != 0 and menu_open == 0: whose = menu_queue.pop(0) menu_open = whose menu_text.reset(f"{whose.name} menüje") menu_text.type_all() open_menu((whose, 67, party_members, enemies)) for m in menu: m.update() if len(enemies) != 0 and all(e.ko or e.stone 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 or a.stone for a in party_members): print("\nGAME OVER! the party was defeated.") battle_music.stop() gamestate.switch_gamestate(game,"gameover") return # !!! CHATGPT !!! def make_gray(tex, key): img = tex.image.convert("RGBA") a = img.getchannel("A") l = img.convert("L") out = Image.merge("RGBA", (l, l, l, a)) return arcade.Texture(out) def get_texture(sprite_id, gray=False): asset = assets.sprites[sprite_id] tex = getattr(asset, "texture", asset) if not gray: return tex if sprite_id not in gray_cache: gray_cache[sprite_id] = make_gray(tex, sprite_id) return gray_cache[sprite_id] def pick_sprite_id(c, ids): if c.ko: return ids[2] if c.hp <= c.maxhp * 0.1: return ids[1] return ids[0] def draw_tex(tex, cx, cy): arcade.draw_texture_rect(tex, arcade.LRBT( cx - tex.width / 2, cx + tex.width / 2, cy - tex.height / 2, cy + tex.height / 2)) def draw_battlers(combatants, sprite_table, pos_func): for i, c in enumerate(combatants): sid = pick_sprite_id(c, sprite_table[i]) tex = get_texture(sid, gray=bool(c.stone and not c.ko)) draw_tex(tex, *pos_func(i)) def draw(game): draw_battlers(enemies, enemy_sprites, enemy_pos) draw_battlers(party_members, party_sprites, party_pos) panel.render() for m in menu: m.render() menu_text.render() action_text.render()