Files
card/card_game/player.py
2026-05-24 12:34:49 +08:00

200 lines
6.1 KiB
Python

"""Player class: manages hand, support line, frontline, HP, provisions."""
from card_game.config import (
STARTING_CAPITAL_HP, MAX_PROVISIONS, MAX_HAND_SIZE,
MAX_SUPPORT_SLOTS, MAX_FRONTLINE_SLOTS, DECK_SIZE,
FATIGUE_START_DAMAGE, FACTIONS, DECK_PRESETS,
FIRST_HAND_SIZE, SECOND_HAND_SIZE,
)
from card_game.deck import Deck
from card_game.card import Card
class Player:
def __init__(self, faction_id, is_ai=False):
faction = FACTIONS[faction_id]
self.faction_id = faction_id
self.faction = faction
self.is_ai = is_ai
self.capital_hp = faction["capital_hp"]
self.max_capital_hp = faction["capital_hp"]
self.provisions = 0
self.max_provisions = 0
self.hand = []
self.support_line = [None] * MAX_SUPPORT_SLOTS
self.frontline = [None] * MAX_FRONTLINE_SLOTS
self.deck = Deck()
self.fatigue_damage = 0
self.turn_number = 0
def build_deck(self):
preset = DECK_PRESETS[self.faction_id]
self.deck.build(preset["cards"])
def start_game(self, hand_size=None):
self.build_deck()
if hand_size is None:
hand_size = FIRST_HAND_SIZE
for _ in range(hand_size):
self.draw_card()
def start_turn(self, turn_number):
self.turn_number = turn_number
gained = min(turn_number, MAX_PROVISIONS)
self.provisions = gained
self.max_provisions = gained
# Faction passive: Qi extra provision
if self.faction_id == "qi":
self.provisions += 1
self.draw_card()
# Reset unit flags
for unit in self.get_all_units():
unit.reset_turn_flags()
if unit.zone == "frontline" and unit.turn_played < turn_number:
unit.can_attack = True
if unit.zone == "support" and unit.turn_played < turn_number:
unit.can_attack = True
# Chu healer
if self.faction_id == "chu":
self._apply_heal_ability()
def draw_card(self):
if len(self.hand) >= MAX_HAND_SIZE:
return
card = self.deck.draw()
if card:
card.zone = "hand"
self.hand.append(card)
else:
self.fatigue_damage += 1
self.capital_hp -= self.fatigue_damage
def can_play_card(self, card):
if card.cost > self.provisions:
return False
if card.card_type == "unit":
return any(s is None for s in self.support_line)
return True
def deploy_unit(self, card, slot=-1):
if slot < 0:
for i, s in enumerate(self.support_line):
if s is None:
slot = i
break
if slot < 0 or slot >= len(self.support_line):
return None
self.support_line[slot] = card
card.zone = "support"
card.slot = slot
card.turn_played = self.turn_number
self.provisions -= card.cost
self.hand.remove(card)
return slot
def play_order(self, card):
self.provisions -= card.cost
self.hand.remove(card)
def move_to_frontline(self, unit):
op_cost = self._get_op_cost(unit)
if self.provisions < op_cost:
return -1
slot = -1
for i, s in enumerate(self.frontline):
if s is None:
slot = i
break
if slot < 0:
return -1
self.support_line[unit.slot] = None
self.frontline[slot] = unit
unit.zone = "frontline"
unit.slot = slot
unit.has_moved = True
self.provisions -= op_cost
if unit.turn_played < self.turn_number:
if unit.can_move_and_attack():
unit.can_attack = True
return slot
def move_to_frontline_free(self, unit):
slot = -1
for i, s in enumerate(self.frontline):
if s is None:
slot = i
break
if slot < 0:
return -1
self.support_line[unit.slot] = None
self.frontline[slot] = unit
unit.zone = "frontline"
unit.slot = slot
unit.has_moved = True
if unit.turn_played < self.turn_number:
unit.can_attack = True
return slot
def remove_unit(self, unit):
if unit.zone == "support":
if 0 <= unit.slot < len(self.support_line):
self.support_line[unit.slot] = None
elif unit.zone == "frontline":
if 0 <= unit.slot < len(self.frontline):
self.frontline[unit.slot] = None
def get_all_units(self):
units = []
for u in self.support_line:
if u is not None:
units.append(u)
for u in self.frontline:
if u is not None:
units.append(u)
return units
def get_frontline_units(self):
return [u for u in self.frontline if u is not None]
def get_support_units(self):
return [u for u in self.support_line if u is not None]
def _get_op_cost(self, unit):
cost = unit.op_cost
if self.faction_id == "yan" and unit.unit_type == "cavalry":
cost = max(0, cost - 1)
return cost
def _apply_heal_ability(self):
healers = [u for u in self.get_all_units() if "heal_all:1" in u.abilities]
if healers:
for unit in self.get_all_units():
if unit.current_hp < unit.max_hp:
unit.current_hp = min(unit.max_hp, unit.current_hp + 1)
def get_attack_cost(self, unit):
cost = unit.op_cost
if self.faction_id == "yan" and unit.unit_type == "cavalry":
cost = max(0, cost - 1)
return cost
def can_afford_attack(self, unit):
return self.provisions >= self.get_attack_cost(unit)
def cleanup_dead(self):
for i in range(len(self.support_line)):
u = self.support_line[i]
if u is not None and not u.is_alive():
self.support_line[i] = None
for i in range(len(self.frontline)):
u = self.frontline[i]
if u is not None and not u.is_alive():
self.frontline[i] = None