tcg/tcg/match/match_manager.gd
2025-01-26 01:58:35 +07:00

186 lines
6.5 KiB
GDScript

extends Node
class_name MatchManager
signal match_begun
signal state_transitioned(transition: PhaseTransition)
const PLAYER_1_ID: int = 0
const PLAYER_2_ID: int = 1
var rps_wins = {
"rock": "scissors",
"scissors": "paper",
"paper": "rock"
}
var players: Dictionary
var player_1: MatchPlayer:
get:
return players[actual_p1_id] as MatchPlayer
var player_2: MatchPlayer:
get:
return players[actual_p2_id] as MatchPlayer
var phase: Match.Phase = Match.Phase.PREGAME
var support_cards_1: Dictionary = {}
var support_cards_2: Dictionary = {}
var actual_p1_id: int
var actual_p2_id: int
func _ready() -> void:
cleanup()
func init(decks: Dictionary, actual_p1_id: int, actual_p2_id: int):
self.actual_p1_id = actual_p1_id
self.actual_p2_id = actual_p2_id
cleanup()
for player_id in [PLAYER_1_ID, PLAYER_2_ID]:
var deck_shuffled = decks[player_id].duplicate() as Array[Card]
var first_monster = deck_shuffled.filter(func (card): return card is MonsterCard)[0]
var first_monster_idx = deck_shuffled.find(first_monster)
deck_shuffled.pop_at(first_monster_idx)
var zero_idx_card = deck_shuffled.pop_at(0)
deck_shuffled.insert(0, first_monster)
deck_shuffled.insert(first_monster_idx, zero_idx_card)
deck_shuffled.shuffle()
players[player_id] = MatchPlayer.new(player_id, deck_shuffled)
func cleanup():
for player_id in [PLAYER_1_ID, PLAYER_2_ID]:
var existing = players.get(player_id)
if existing:
existing.free()
phase = Match.Phase.PREGAME
func resolve(action_by_player_id: Dictionary) -> PhaseTransition:
var initial_phase = phase
var events = [] as Array[Event]
var player_1_action = action_by_player_id.get(actual_p1_id) as Action
var player_2_action = action_by_player_id.get(actual_p2_id) as Action
match phase:
Match.Phase.PREGAME:
var CARDS_TO_DRAW: int = 3
for player: MatchPlayer in [player_1, player_2]:
var n_cards_to_draw = min(player.deck.size(), CARDS_TO_DRAW)
var drawn_cards = range(n_cards_to_draw).map(func(_i): return player.deck.pop_back()) as Array[Card]
for card in drawn_cards:
player.hand.push_back(card)
var drawn_card_events = drawn_cards.map(func(card): return EventCardDrawn.new(player.id, card))
events.append_array(
drawn_card_events
)
phase = Match.Phase.SUMMON
Match.Phase.SUMMON:
support_cards_1.clear()
support_cards_2.clear()
for player_id in action_by_player_id.keys():
var action = action_by_player_id[player_id]
if action is ActionPlayCard:
var card = action.card
if card is MonsterCard:
players[player_id].monster = _monster_from_card(card)
players[player_id].hand = players[player_id].hand.filter(func(card_in_hand): return card.id != card_in_hand.id)
events.append(EventMonsterSummoned.new(card))
if players.values().all(func(player: MatchPlayer): return player.monster):
phase = Match.Phase.SUPPORT_1
Match.Phase.SUPPORT_1:
support_cards_1.clear()
support_cards_2.clear()
var support_cards = []
if player_1_action is ActionPlayCard:
support_cards.append({
"player": player_1,
"card": player_1_action.card
})
support_cards_1[player_1] = player_1_action.card
player_1.hand.erase(player_1_action.card)
if player_2_action is ActionPlayCard:
support_cards.append({
"player": player_2,
"card": player_2_action.card
})
support_cards_1[player_2] = player_2_action.card
player_2.hand.erase(player_2_action.card)
support_cards.sort_custom(func(a, b): return a.card.priority < b.card.priority)
for played_card in support_cards:
var player: MatchPlayer = played_card.player
var card: SupportCard = played_card.card
events.append_array(_resolve_support_card_effects(player, card))
phase = Match.Phase.RPS
Match.Phase.RPS:
var tie = player_1_action.move == player_2_action.move
var player_1_win = rps_wins[player_1_action.move] == player_2_action.move
var player_2_win = rps_wins[player_2_action.move] == player_1_action.move
if tie or player_1_win:
player_2.monster.health_delta -= player_1.monster.card.damage[player_1_action.move]
if tie or player_2_win:
player_1.monster.health_delta -= player_2.monster.card.damage[player_2_action.move]
phase = Match.Phase.SUPPORT_2
Match.Phase.SUPPORT_2:
var support_cards = []
if player_1_action is ActionPlayCard:
support_cards.append({
"player": player_1,
"card": player_1_action.card
})
support_cards_2[player_1] = player_1_action.card
player_1.hand.erase(player_1_action.card)
if player_2_action is ActionPlayCard:
support_cards.append({
"player": player_2,
"card": player_2_action.card
})
support_cards_2[player_2] = player_2_action.card
player_2.hand.erase(player_2_action.card)
support_cards.sort_custom(func(a, b): return a.card.priority < b.card.priority)
for played_card in support_cards:
var player: MatchPlayer = played_card.player
var card: SupportCard = played_card.card
events.append_array(_resolve_support_card_effects(player, card))
for player: MatchPlayer in players.values():
var monster_final_health = player.monster.health + player.monster.health_delta
if monster_final_health <= 0:
player.monster = null
events.append(EventMonsterDied.new(player.id))
else:
player.monster.health += player.monster.health_delta
player.monster.health_delta = 0
var drawn_card = player.deck.pop_back()
if drawn_card:
player.hand.append(drawn_card)
events.append(EventCardDrawn.new(player.id, drawn_card))
var players_without_monster = players.values().filter(func (player): return player.monster == null)
if players_without_monster.size() == 0:
phase = Match.Phase.SUPPORT_1
elif players_without_monster.all(
func (player): return player.hand.any(func (it): return it is MonsterCard and it.energy_cost <= player.energy)
):
phase = Match.Phase.SUMMON
else:
phase = Match.Phase.END
_:
pass
var transition = PhaseTransition.new(events, initial_phase, phase)
state_transitioned.emit(transition)
return transition
func _monster_from_card(card: MonsterCard) -> MatchMonster:
return MatchMonster.new(card, card.base_health, 0)
func _resolve_support_card_effects(player: MatchPlayer, card: SupportCard) -> Array[Event]:
var events: Array[Event]
for effect_instance in card.effects:
var effect = effect_instance.effect
var magnitude = effect_instance.magnitude
match effect.id:
"heal":
player.monster.health_delta += magnitude
events.append(EventSupportEffectApplied.new(player.id, effect_instance))
return events