游戏可以运行
This commit is contained in:
337
card_game/battlefield.py
Normal file
337
card_game/battlefield.py
Normal file
@@ -0,0 +1,337 @@
|
||||
"""Battlefield: core game logic — combat, orders, turn management."""
|
||||
|
||||
from card_game.player import Player
|
||||
from card_game.factions import apply_faction_passive
|
||||
|
||||
|
||||
class Battlefield:
|
||||
def __init__(self, player_faction, ai_faction):
|
||||
self.player = Player(player_faction, is_ai=False)
|
||||
self.ai = Player(ai_faction, is_ai=True)
|
||||
self.turn_number = 0
|
||||
self.current_turn = "player"
|
||||
self.game_over = False
|
||||
self.winner = None
|
||||
self.log = []
|
||||
self.pending_order = None
|
||||
self.effects = []
|
||||
self.frontline_controller = None
|
||||
|
||||
def start_game(self):
|
||||
self.player.start_game()
|
||||
self.ai.start_game()
|
||||
self.turn_number = 1
|
||||
self.current_turn = "player"
|
||||
self.frontline_controller = None
|
||||
self.player.start_turn(self.turn_number)
|
||||
|
||||
def get_active_player(self):
|
||||
return self.player if self.current_turn == "player" else self.ai
|
||||
|
||||
def get_opponent(self, player):
|
||||
return self.ai if player == self.player else self.player
|
||||
|
||||
# --- Frontline Control ---
|
||||
|
||||
def can_move_to_frontline(self, player):
|
||||
if self.frontline_controller is None:
|
||||
return True
|
||||
if player == self.player and self.frontline_controller == "player":
|
||||
return True
|
||||
if player == self.ai and self.frontline_controller == "ai":
|
||||
return True
|
||||
return False
|
||||
|
||||
def claim_frontline(self, player):
|
||||
if player == self.player:
|
||||
self.frontline_controller = "player"
|
||||
else:
|
||||
self.frontline_controller = "ai"
|
||||
|
||||
def _update_frontline_control(self):
|
||||
if self.frontline_controller == "player":
|
||||
if not self.player.get_frontline_units():
|
||||
self.frontline_controller = None
|
||||
elif self.frontline_controller == "ai":
|
||||
if not self.ai.get_frontline_units():
|
||||
self.frontline_controller = None
|
||||
|
||||
# --- Turn Management ---
|
||||
|
||||
def end_player_turn(self):
|
||||
if self.current_turn != "player" or self.game_over:
|
||||
return
|
||||
self.current_turn = "ai"
|
||||
|
||||
def start_ai_turn(self):
|
||||
self.turn_number += 1
|
||||
self.ai.start_turn(self.turn_number)
|
||||
self.current_turn = "ai"
|
||||
|
||||
def end_ai_turn(self):
|
||||
if self.game_over:
|
||||
return
|
||||
self.current_turn = "player"
|
||||
self.turn_number += 1
|
||||
self.player.start_turn(self.turn_number)
|
||||
|
||||
# --- Combat ---
|
||||
|
||||
def resolve_attack(self, attacker, defender):
|
||||
dead = []
|
||||
atk = attacker.get_effective_attack()
|
||||
|
||||
if "siege" in attacker.abilities and defender == "capital":
|
||||
atk *= 2
|
||||
|
||||
owner = self._get_unit_owner(attacker)
|
||||
if (owner and owner.faction_id == "han"
|
||||
and attacker.unit_type == "archer" and defender == "capital"):
|
||||
atk += 1
|
||||
|
||||
if isinstance(defender, str) and defender == "capital":
|
||||
target_player = self.get_opponent(owner)
|
||||
target_player.capital_hp -= atk
|
||||
self._add_effect("damage", target_player, "capital", atk)
|
||||
self._check_game_over()
|
||||
attacker.has_attacked = True
|
||||
attacker.can_attack = False
|
||||
return dead
|
||||
|
||||
defender.take_damage(atk)
|
||||
self._add_effect("damage", self._get_unit_owner(defender), defender, atk)
|
||||
|
||||
if not defender.is_alive():
|
||||
dead.append(defender)
|
||||
if owner and owner.faction_id == "qin":
|
||||
owner.provisions += 1
|
||||
|
||||
if "no_retaliation" not in attacker.abilities:
|
||||
if not (attacker.is_ranged() and attacker.zone == "support"):
|
||||
retal = defender.get_effective_defense() if defender.is_alive() else 0
|
||||
if retal > 0:
|
||||
attacker.take_damage(retal)
|
||||
self._add_effect("damage", self._get_unit_owner(attacker), attacker, retal)
|
||||
if not attacker.is_alive():
|
||||
dead.append(attacker)
|
||||
|
||||
attacker.has_attacked = True
|
||||
attacker.can_attack = False
|
||||
self._cleanup_dead()
|
||||
self._check_game_over()
|
||||
return dead
|
||||
|
||||
def attack_capital(self, attacker):
|
||||
owner = self._get_unit_owner(attacker)
|
||||
if not owner:
|
||||
return
|
||||
self.resolve_attack(attacker, "capital")
|
||||
|
||||
# --- Order Effects ---
|
||||
|
||||
def apply_order_effect(self, card, caster, target=None):
|
||||
etype = card.effect_type
|
||||
params = card.effect_params
|
||||
opponent = self.get_opponent(caster)
|
||||
|
||||
if etype == "damage":
|
||||
dmg = params["damage"]
|
||||
if target and hasattr(target, 'take_damage'):
|
||||
target.take_damage(dmg)
|
||||
self._add_effect("damage", self._get_unit_owner(target), target, dmg)
|
||||
self._cleanup_dead()
|
||||
return True
|
||||
|
||||
elif etype == "damage_hq":
|
||||
dmg = params["damage"]
|
||||
opponent.capital_hp -= dmg
|
||||
self._add_effect("damage", opponent, "capital", dmg)
|
||||
self._check_game_over()
|
||||
return True
|
||||
|
||||
elif etype == "damage_all_front":
|
||||
dmg = params["damage"]
|
||||
tgt = params.get("target", "enemy")
|
||||
if tgt == "enemy":
|
||||
for u in opponent.get_frontline_units():
|
||||
u.take_damage(dmg)
|
||||
self._add_effect("damage", opponent, u, dmg)
|
||||
else:
|
||||
for u in caster.get_frontline_units():
|
||||
u.take_damage(dmg)
|
||||
self._add_effect("damage", caster, u, dmg)
|
||||
self._cleanup_dead()
|
||||
return True
|
||||
|
||||
elif etype == "draw":
|
||||
count = params["count"]
|
||||
for _ in range(count):
|
||||
caster.draw_card()
|
||||
return True
|
||||
|
||||
elif etype == "gain_provisions":
|
||||
caster.provisions += params["amount"]
|
||||
return True
|
||||
|
||||
elif etype == "buff_all":
|
||||
atk_b = params.get("attack_bonus", 0)
|
||||
def_b = params.get("defense_bonus", 0)
|
||||
dur = params.get("duration", 1)
|
||||
for u in caster.get_all_units():
|
||||
u.buffs.append((atk_b, def_b, dur))
|
||||
return True
|
||||
|
||||
elif etype == "buff_type":
|
||||
unit_type = params["unit_type"]
|
||||
atk_b = params.get("attack_bonus", 0)
|
||||
def_b = params.get("defense_bonus", 0)
|
||||
dur = params.get("duration", 1)
|
||||
for u in caster.get_all_units():
|
||||
if u.unit_type == unit_type:
|
||||
u.buffs.append((atk_b, def_b, dur))
|
||||
return True
|
||||
|
||||
elif etype == "buff_single":
|
||||
if target and hasattr(target, 'buffs'):
|
||||
atk_b = params.get("attack_bonus", 0)
|
||||
def_b = params.get("defense_bonus", 0)
|
||||
dur = params.get("duration", 1)
|
||||
target.buffs.append((atk_b, def_b, dur))
|
||||
return True
|
||||
|
||||
elif etype == "heal_hq":
|
||||
amount = params["amount"]
|
||||
caster.capital_hp = min(caster.max_capital_hp, caster.capital_hp + amount)
|
||||
return True
|
||||
|
||||
elif etype == "bounce":
|
||||
if target and hasattr(target, 'zone'):
|
||||
owner = self._get_unit_owner(target)
|
||||
if owner:
|
||||
owner.remove_unit(target)
|
||||
target.zone = "hand"
|
||||
if len(owner.hand) < 8:
|
||||
owner.hand.append(target)
|
||||
return True
|
||||
|
||||
elif etype == "destroy_damaged":
|
||||
if target and hasattr(target, 'is_alive'):
|
||||
if target.current_hp < target.max_hp:
|
||||
owner = self._get_unit_owner(target)
|
||||
if owner:
|
||||
owner.remove_unit(target)
|
||||
return True
|
||||
|
||||
elif etype == "move_to_front":
|
||||
if target and hasattr(target, 'zone') and target.zone == "support":
|
||||
caster.move_to_frontline_free(target)
|
||||
return True
|
||||
|
||||
elif etype == "summon":
|
||||
from card_game.card import Card
|
||||
count = params.get("count", 1)
|
||||
unit_id = params["unit_id"]
|
||||
for _ in range(count):
|
||||
new_card = Card(unit_id)
|
||||
new_card.turn_played = caster.turn_number
|
||||
placed = False
|
||||
for i, s in enumerate(caster.support_line):
|
||||
if s is None:
|
||||
caster.support_line[i] = new_card
|
||||
new_card.zone = "support"
|
||||
new_card.slot = i
|
||||
placed = True
|
||||
break
|
||||
if not placed:
|
||||
break
|
||||
return True
|
||||
|
||||
elif etype == "heal_all":
|
||||
amount = params.get("amount", 1)
|
||||
for u in caster.get_all_units():
|
||||
u.current_hp = min(u.max_hp, u.current_hp + amount)
|
||||
return True
|
||||
|
||||
elif etype == "draw_self_damage":
|
||||
count = params.get("count", 1)
|
||||
self_damage = params.get("self_damage", 0)
|
||||
for _ in range(count):
|
||||
caster.draw_card()
|
||||
if self_damage > 0:
|
||||
caster.capital_hp -= self_damage
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def needs_target(self, card):
|
||||
etype = card.effect_type
|
||||
if etype in ("damage", "bounce", "destroy_damaged", "move_to_front", "buff_single"):
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_valid_targets(self, card, caster):
|
||||
etype = card.effect_type
|
||||
opponent = self.get_opponent(caster)
|
||||
params = card.effect_params
|
||||
|
||||
if etype == "damage":
|
||||
tt = params.get("target_type", "any")
|
||||
targets = []
|
||||
if tt in ("any", "enemy_unit"):
|
||||
targets.extend(opponent.get_all_units())
|
||||
if tt == "any":
|
||||
targets.append(("capital", opponent))
|
||||
return targets
|
||||
|
||||
elif etype == "bounce":
|
||||
return opponent.get_frontline_units()
|
||||
|
||||
elif etype == "destroy_damaged":
|
||||
return [u for u in opponent.get_all_units() if u.current_hp < u.max_hp]
|
||||
|
||||
elif etype == "move_to_front":
|
||||
return [u for u in caster.get_support_units()]
|
||||
|
||||
elif etype == "buff_single":
|
||||
return caster.get_all_units()
|
||||
|
||||
return []
|
||||
|
||||
# --- Helpers ---
|
||||
|
||||
def _get_unit_owner(self, unit):
|
||||
for u in self.player.get_all_units():
|
||||
if u is unit:
|
||||
return self.player
|
||||
for u in self.ai.get_all_units():
|
||||
if u is unit:
|
||||
return self.ai
|
||||
return None
|
||||
|
||||
def _cleanup_dead(self):
|
||||
self.player.cleanup_dead()
|
||||
self.ai.cleanup_dead()
|
||||
self._update_frontline_control()
|
||||
|
||||
def _check_game_over(self):
|
||||
if self.player.capital_hp <= 0:
|
||||
self.game_over = True
|
||||
self.winner = "ai"
|
||||
elif self.ai.capital_hp <= 0:
|
||||
self.game_over = True
|
||||
self.winner = "player"
|
||||
|
||||
def _add_effect(self, etype, target_player, target, value):
|
||||
self.effects.append({
|
||||
"type": etype,
|
||||
"target_player": target_player,
|
||||
"target": target,
|
||||
"value": value,
|
||||
"timer": 60,
|
||||
})
|
||||
|
||||
def update_effects(self):
|
||||
for eff in self.effects[:]:
|
||||
eff["timer"] -= 1
|
||||
if eff["timer"] <= 0:
|
||||
self.effects.remove(eff)
|
||||
Reference in New Issue
Block a user