264 lines
6.3 KiB
GDScript
264 lines
6.3 KiB
GDScript
class_name Player
|
|
extends RefCounted
|
|
|
|
## Player - Represents a player's game state
|
|
|
|
var player_index: int = 0
|
|
var player_name: String = "Player"
|
|
|
|
# Zones
|
|
var deck: Zone
|
|
var hand: Zone
|
|
var field_forwards: Zone
|
|
var field_backups: Zone
|
|
var damage_zone: Zone
|
|
var break_zone: Zone
|
|
|
|
# Resources
|
|
var cp_pool: CPPool
|
|
|
|
# Game state
|
|
var is_first_player: bool = false
|
|
var has_mulliganed: bool = false
|
|
|
|
# Constants
|
|
const MAX_HAND_SIZE: int = 5
|
|
const MAX_BACKUPS: int = 5
|
|
const DAMAGE_TO_LOSE: int = 7
|
|
|
|
func _init(index: int, name: String = "") -> void:
|
|
player_index = index
|
|
player_name = name if name != "" else "Player %d" % (index + 1)
|
|
|
|
# Initialize zones
|
|
deck = Zone.new(Enums.ZoneType.DECK, index)
|
|
hand = Zone.new(Enums.ZoneType.HAND, index)
|
|
field_forwards = Zone.new(Enums.ZoneType.FIELD_FORWARDS, index)
|
|
field_backups = Zone.new(Enums.ZoneType.FIELD_BACKUPS, index)
|
|
damage_zone = Zone.new(Enums.ZoneType.DAMAGE, index)
|
|
break_zone = Zone.new(Enums.ZoneType.BREAK, index)
|
|
|
|
# Initialize CP pool
|
|
cp_pool = CPPool.new()
|
|
|
|
## Setup the player's deck from a list of card IDs
|
|
func setup_deck(card_ids: Array[String]) -> void:
|
|
for card_id in card_ids:
|
|
var card_data = CardDatabase.get_card(card_id)
|
|
if card_data:
|
|
var card = CardInstance.new(card_data, player_index)
|
|
deck.add_card(card)
|
|
|
|
# Shuffle the deck
|
|
deck.shuffle()
|
|
|
|
## Draw cards from deck to hand
|
|
func draw_cards(count: int) -> Array[CardInstance]:
|
|
var drawn: Array[CardInstance] = []
|
|
|
|
for i in range(count):
|
|
if deck.is_empty():
|
|
break # Can't draw from empty deck
|
|
var card = deck.pop_top_card()
|
|
if card:
|
|
hand.add_card(card)
|
|
drawn.append(card)
|
|
|
|
return drawn
|
|
|
|
## Check if player can draw (deck not empty)
|
|
func can_draw() -> bool:
|
|
return not deck.is_empty()
|
|
|
|
## Get current damage count
|
|
func get_damage_count() -> int:
|
|
return damage_zone.get_count()
|
|
|
|
## Check if player has lost (7+ damage)
|
|
func has_lost() -> bool:
|
|
return get_damage_count() >= DAMAGE_TO_LOSE
|
|
|
|
## Take damage (move cards from deck to damage zone)
|
|
func take_damage(amount: int) -> Array[CardInstance]:
|
|
var damage_cards: Array[CardInstance] = []
|
|
|
|
for i in range(amount):
|
|
if deck.is_empty():
|
|
break
|
|
var card = deck.pop_top_card()
|
|
if card:
|
|
damage_zone.add_card(card)
|
|
damage_cards.append(card)
|
|
|
|
return damage_cards
|
|
|
|
## Discard a card from hand to generate CP
|
|
func discard_for_cp(card: CardInstance) -> bool:
|
|
if not hand.has_card(card):
|
|
return false
|
|
|
|
# Light/Dark cards cannot be discarded for CP
|
|
if card.is_light_or_dark():
|
|
return false
|
|
|
|
hand.remove_card(card)
|
|
break_zone.add_card(card)
|
|
|
|
# Generate 2 CP of the card's element
|
|
var element = card.get_element()
|
|
cp_pool.add_cp(element, 2)
|
|
|
|
return true
|
|
|
|
## Dull a backup to generate CP
|
|
func dull_backup_for_cp(card: CardInstance) -> bool:
|
|
if not field_backups.has_card(card):
|
|
return false
|
|
|
|
if not card.is_backup():
|
|
return false
|
|
|
|
if card.is_dull():
|
|
return false
|
|
|
|
card.dull()
|
|
|
|
# Generate 1 CP of the backup's element
|
|
var element = card.get_element()
|
|
cp_pool.add_cp(element, 1)
|
|
|
|
return true
|
|
|
|
## Play a card from hand to field
|
|
func play_card(card: CardInstance) -> bool:
|
|
if not hand.has_card(card):
|
|
return false
|
|
|
|
# Check if we can afford it
|
|
if not cp_pool.can_afford_card(card.card_data):
|
|
return false
|
|
|
|
# Check field restrictions
|
|
if card.is_backup():
|
|
if field_backups.get_count() >= MAX_BACKUPS:
|
|
return false
|
|
|
|
# Check unique name restriction
|
|
if not card.card_data.is_generic:
|
|
if card.is_forward() and field_forwards.has_card_with_name(card.card_data.name):
|
|
return false
|
|
if card.is_backup() and field_backups.has_card_with_name(card.card_data.name):
|
|
return false
|
|
|
|
# Check Light/Dark restriction
|
|
if card.is_light_or_dark():
|
|
if field_forwards.has_light_or_dark() or field_backups.has_light_or_dark():
|
|
return false
|
|
|
|
# Pay the cost
|
|
if not cp_pool.spend_for_card(card.card_data):
|
|
return false
|
|
|
|
# Move to field
|
|
hand.remove_card(card)
|
|
|
|
if card.is_forward():
|
|
field_forwards.add_card(card)
|
|
card.state = Enums.CardState.ACTIVE
|
|
elif card.is_backup():
|
|
field_backups.add_card(card)
|
|
card.state = Enums.CardState.DULL # Backups enter dull
|
|
|
|
card.entered_field()
|
|
|
|
return true
|
|
|
|
## Break a card (move from field to break zone)
|
|
func break_card(card: CardInstance) -> bool:
|
|
var removed = false
|
|
|
|
if field_forwards.has_card(card):
|
|
field_forwards.remove_card(card)
|
|
removed = true
|
|
elif field_backups.has_card(card):
|
|
field_backups.remove_card(card)
|
|
removed = true
|
|
|
|
if removed:
|
|
break_zone.add_card(card)
|
|
return true
|
|
|
|
return false
|
|
|
|
## Activate all dull cards (Active Phase)
|
|
func activate_all() -> void:
|
|
for card in field_forwards.get_dull_cards():
|
|
card.activate()
|
|
for card in field_backups.get_dull_cards():
|
|
card.activate()
|
|
|
|
## Discard down to hand size (End Phase)
|
|
func discard_to_hand_limit() -> Array[CardInstance]:
|
|
var discarded: Array[CardInstance] = []
|
|
|
|
while hand.get_count() > MAX_HAND_SIZE:
|
|
# For now, just discard the last card
|
|
# In actual game, player would choose
|
|
var card = hand.pop_top_card()
|
|
if card:
|
|
break_zone.add_card(card)
|
|
discarded.append(card)
|
|
|
|
return discarded
|
|
|
|
## End of turn cleanup
|
|
func end_turn_cleanup() -> void:
|
|
# Clear CP pool
|
|
cp_pool.clear()
|
|
|
|
# Reset temporary effects on field cards
|
|
for card in field_forwards.get_cards():
|
|
card.end_turn_cleanup()
|
|
for card in field_backups.get_cards():
|
|
card.end_turn_cleanup()
|
|
|
|
## Start of turn setup
|
|
func start_turn() -> void:
|
|
# Increment turn counter for field cards
|
|
for card in field_forwards.get_cards():
|
|
card.start_turn()
|
|
for card in field_backups.get_cards():
|
|
card.start_turn()
|
|
|
|
## Get all cards on field
|
|
func get_all_field_cards() -> Array[CardInstance]:
|
|
var cards: Array[CardInstance] = []
|
|
cards.append_array(field_forwards.get_cards())
|
|
cards.append_array(field_backups.get_cards())
|
|
return cards
|
|
|
|
## Get forwards that can attack
|
|
func get_attackable_forwards() -> Array[CardInstance]:
|
|
var attackers: Array[CardInstance] = []
|
|
for card in field_forwards.get_cards():
|
|
if card.can_attack():
|
|
attackers.append(card)
|
|
return attackers
|
|
|
|
## Get forwards that can block
|
|
func get_blockable_forwards() -> Array[CardInstance]:
|
|
var blockers: Array[CardInstance] = []
|
|
for card in field_forwards.get_cards():
|
|
if card.can_block():
|
|
blockers.append(card)
|
|
return blockers
|
|
|
|
func _to_string() -> String:
|
|
return "[Player: %s, Hand: %d, Forwards: %d, Backups: %d, Damage: %d]" % [
|
|
player_name,
|
|
hand.get_count(),
|
|
field_forwards.get_count(),
|
|
field_backups.get_count(),
|
|
damage_zone.get_count()
|
|
]
|