extends Node class_name GameManager @export_group("Prompts", "prompt_") @export var prompt_ask_play_or_spectate: Prompt @export var prompt_must_spectate: Prompt @export var prompt_ask_name: Prompt @onready var action_ask_name: Action = Action.new(prompt_ask_name, action_answer_name) @export var prompt_ask_pronouns: Prompt @onready var action_ask_pronouns: Action = Action.new(prompt_ask_pronouns, action_answer_pronouns) @export var prompt_vip_start_game: Prompt @onready var action_vip_start_game: Action = Action.new(prompt_vip_start_game, vip_start_game) @export var prompt_intoduce_spectator: Prompt @export var prompt_reveal_team: Prompt @export var prompt_reveal_role: Prompt @export var prompt_force_vote: Prompt @export var prompt_choose_night_action: Prompt @export var prompt_night_action_result: Prompt = Prompt.create("", Prompt.PromptType.CONFIRMATION) @export_group("Game Settings") @export var day_time: float = 5 * 60 @export var night_time: float = 3 * 60 @export var wolf_ratio: float = 4.0 @export_group("Teams", "team") @export var team_town: Team = preload("res://teams/town.tres") @export var team_anti_town: Array[Team] = [preload("res://teams/werewolves.tres")] # ask for new players # introduce rules # @onready var card_grid: CardGrid = $"../CardGrid" @onready var game_clock: GameClock = $"../GameClock" @onready var game_state: GameState = $"../GameState" @onready var players: Players = game_state.players @onready var animation: AnimationPlayer = $"../AnimationPlayer" @onready var announcer: Announcer = $"../Announcer" var player_actions: Dictionary = {} var timer_running: bool = false var vip_id: int = -1 func _ready() -> void: if players != null: players.connect("player_joined", on_player_joined) #if announcer != null: #announcer.announce_line_by_id("intro") if game_state != null: game_state.connect("phase_changed", game_clock.set_phase) game_state.connect("time_changed", game_clock.set_time) func _process(delta: float) -> void: if timer_running: game_state.time -= delta if game_state.time <= 0: phase_end() func on_player_joined(player: Player) -> void: await get_tree().create_timer(1.0).timeout check_player_setup(player) #player.connect("prompt_answered", on_player_answered_prompt.bind(player))a func check_player_setup(player: Player) -> void: if player.player_name.is_empty(): run_player_action(player, action_ask_name) elif player.player_pronouns.is_empty(): run_player_action(player, action_ask_pronouns) else: player.playing = true if vip_id == -1 and !player.player_name.contains("Bot"): vip_id = player.session_id run_player_action(player, action_vip_start_game) #var wolf_player: WolfPlayer = player as WolfPlayer #wolf_player.team = preload("res://teams/werewolves.tres") #wolf_player.role = preload("res://roles/investigator.tres") #wolf_player.role.night_action.setup(wolf_player) func run_player_prompt(player: Player, prompt: Prompt) -> void: player.send_action(Action.new(prompt, func(a,p): return true)) func run_player_action(player: Player, action: Action) -> void: player.send_action(action) #player_actions[player] = action #player.send_prompt(action.prompt) func action_answer_name(answer: String, player: Player) -> bool: player.player_name = answer.strip_edges().capitalize() check_player_setup(player) return true func action_answer_pronouns(answer: String, player: Player) -> bool: player.player_pronouns = answer check_player_setup(player) return true func vip_start_game(answer: String, player: Player) -> bool: start_game() return true func start_game() -> void: for player in game_state.get_wolf_players(false): player.game_reset() #for player in Players.instance.get_players(): #player.send_prompt(null) # interrupt distribute_teams() show_teams() distribute_roles() func distribute_teams() -> void: var players: Array[Player] = Players.instance.get_active_players() # randomly distribute teams var wolf_chance: float = 1.0/(1.0+wolf_ratio) var wolf_count: int = 0 for player in players: var wolf_player: WolfPlayer = player as WolfPlayer var rand: float = randf() if rand < wolf_chance: wolf_count += 1 wolf_player.team = team_anti_town.pick_random() else: wolf_player.team = team_town if wolf_count <= 0: # make a random player a wolf (players.pick_random() as WolfPlayer).team = team_anti_town.pick_random() while wolf_count >= ((players.size() / 2)-1): var wp: WolfPlayer = (players.pick_random() as WolfPlayer) if wp.team != team_town: wolf_count -= 1 wp.team = team_town func distribute_roles() -> void: var players: Array[Player] = Players.instance.get_active_players() var available_roles: Array = [] preload("res://roles/roles_group.tres").load_all_into(available_roles) #available_roles.assign(Role.roles.values()) for player in players: var wolf_player: WolfPlayer = player as WolfPlayer var valid_roles: Array = available_roles.filter(func(r: Role): return r.teams.has(wolf_player.team) ) if !valid_roles.is_empty(): wolf_player.role = valid_roles.pick_random() print("Choosing role for %s (team %s) from %d options (%d available): %s" % [wolf_player.player_name, wolf_player.team.name, valid_roles.size(), available_roles.size(), wolf_player.role.name if wolf_player.role != null else "null!"]) #run_player_prompt(player, prompt_reveal_role.with_context({"role":wolf_player.role.name})) func show_teams() -> void: # play announcer line var teams: Array[Team] = [] for player in game_state.get_wolf_players(): teams.append(player.team) teams.sort() if card_grid != null: card_grid.display_teams(teams) if announcer != null: announcer.announce_line_by_id("team_handout_pre") if card_grid != null: await card_grid.call_on_children("appear") await card_grid.call_on_children("flip") if announcer != null: if announcer.is_speaking(): await announcer.finished announcer.announce_line_by_id("team_handout_post") for player in game_state.get_wolf_players(): run_player_prompt(player, prompt_reveal_team.with_context({"team":player.team.name})) if card_grid != null: await card_grid.call_on_children("disappear") await wait_for_busy_players(30,15) func show_roles() -> void: var roles: Array[Role] = [] for player in game_state.get_wolf_players(): roles.append(player.role) roles.sort() if card_grid != null: card_grid.display_roles(roles) if announcer != null: announcer.announce_line_by_id("team_handout_pre") if card_grid != null: await card_grid.call_on_children("appear") await card_grid.call_on_children("flip") if announcer != null: if announcer.is_speaking(): await announcer.finished announcer.announce_line_by_id("team_handout_post") for player in game_state.get_wolf_players(): run_player_prompt(player, prompt_reveal_team.with_context({"team":player.team.name})) if card_grid != null: await card_grid.call_on_children("disappear") func phase_end() -> void: timer_running = false var winner: Team = null if game_state.phase == GameState.GameTime.DAY: resolve_vote() winner = check_game_won() if winner == null: start_night() elif game_state.phase == GameState.GameTime.NIGHT: resolve_night_actions() winner = check_game_won() if winner == null: start_day() #if winner != null: #announce_winner(team) func start_day() -> void: game_state.day_number += 1 game_state.phase = GameState.GameTime.DAY game_state.time = day_time players.call_on_active_players("day_reset") for player in game_state.get_wolf_players(): if player.role.passive_action != null: player.role.passive_action.setup_day(player) func start_night() -> void: game_state.phase = GameState.GameTime.NIGHT game_state.time = night_time players.call_on_active_players("night_reset") for player in game_state.get_wolf_players(): if player.role.passive_action != null: player.role.passive_action.setup_night(player) func resolve_vote(hammer_voter: WolfPlayer = null): var winner: WolfPlayer = game_state.find_vote_winner(GameState.VotingMethod.PLURALITY) print("Resolved day vote: %s is the 'winner'" % (winner.player_name if winner != null else "nobody")) if winner != null: winner.execute(hammer_voter) func force_vote() -> void: var prompt: Prompt = prompt_force_vote.duplicate() var players: Array[WolfPlayer] = game_state.get_wolf_players() for player in players: var wp: WolfPlayer = player as WolfPlayer if !wp.alive: continue prompt.options.append(wp.player_name) players.shuffle() for player in players: var wp: WolfPlayer = player as WolfPlayer if !wp.alive: continue player.send_action(Action.new(prompt, func(answer:String, player:Player): player.vote=answer; return true)) func resolve_night_actions(): var players_to_resolve: Array[WolfPlayer] = [] for player in game_state.get_wolf_players(): if player.alive and player.night_action_chosen != null and !player.night_action_answer.is_empty(): players_to_resolve.append(player) players_to_resolve.sort_custom(func(a:WolfPlayer,b:WolfPlayer): return a.night_action_chosen.night_action_priority < b.night_action_chosen.night_action_priority) for player in players_to_resolve: var success: bool = player.night_action_chosen.execute(player) if success: player.send_message(player.night_action_chosen.message_success) else: player.send_message(player.night_action_chosen.message_failure) wait_for_busy_players(10.0) func check_game_won() -> Team: var alive_players: int = game_state.count_alive_players() for team in Team.teams.values(): var members: int = game_state.count_team_members(team) var ratio: float = members / float(alive_players) if ratio >= team.win_percentage: return team return null func force_night_actions() -> void: var players: Array[WolfPlayer] = game_state.get_wolf_players() players.shuffle() for player in players: offer_night_action_choice(player) func offer_night_action_choice(player: WolfPlayer) -> void: var night_actions: Dictionary = player.get_night_actions() if night_actions.is_empty(): return elif night_actions.size() == 1: on_player_night_action_chosen(night_actions.keys()[0], player) #night_actions.values()[0].setup(player) else: var prompt: Prompt = prompt_choose_night_action.duplicate() prompt.options.assign(night_actions.keys()) run_player_action(player, Action.new(prompt, on_player_night_action_chosen)) func on_player_night_action_chosen(answer: String, player: Player) -> bool: var wp: WolfPlayer = player as WolfPlayer wp.use_night_action(answer) return true func wait_for_busy_players(timeout: float = 10, warning: float = 0) -> bool: var signals: Array[Signal] = [] var active_players: Array[Player] = Players.instance.get_active_players() for player in active_players: if player.is_busy(): signals.append(player.prompt_queue_finished) if signals.is_empty(): return true if warning > 0: var multi_await: MultiAwait = MultiAwait.new(signals, warning) var timed_out: bool = await multi_await.done if !timed_out: return true if announcer: announcer.announce_line_by_id("waiting_for_players", {"count":multi_await.waiting_count}) return !await MultiAwait.new(signals, timeout - warning).done #func on_player_answered_prompt(answer: String, player: Player) -> void: #var action: Action = player_actions.get(player) #if action == null: #return #var validated: bool = action.prompt_answered(player,answer) #if validated: #player_actions.erase(player) #check_player_setup(player) #else: ## send it back with some kind of error? #run_player_action(player, action)