200 lines
6.1 KiB
Python
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
|