2025-05-31 22:21:22 +01:00
extends Node
class_name GameManager
2025-06-21 14:36:09 +01:00
@ 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 "
2025-05-31 22:21:22 +01:00
var player_actions : Dictionary = { }
2025-06-21 14:36:09 +01:00
var timer_running : bool = false
var vip_id : int = - 1
2025-05-31 22:21:22 +01:00
func _ready ( ) - > void :
2025-06-21 14:36:09 +01:00
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 ( )
2025-05-31 22:21:22 +01:00
func on_player_joined ( player : Player ) - > void :
2025-06-21 14:36:09 +01:00
await get_tree ( ) . create_timer ( 1.0 ) . timeout
2025-05-31 22:21:22 +01:00
check_player_setup ( player )
2025-06-21 14:36:09 +01:00
#player.connect("prompt_answered", on_player_answered_prompt.bind(player))a
2025-05-31 22:21:22 +01:00
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 )
2025-06-21 14:36:09 +01:00
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 ) )
2025-05-31 22:21:22 +01:00
func run_player_action ( player : Player , action : Action ) - > void :
2025-06-21 14:36:09 +01:00
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 ( )
2025-05-31 22:21:22 +01:00
2025-06-21 14:36:09 +01:00
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 ( ) :
2025-05-31 22:21:22 +01:00
return
2025-06-21 14:36:09 +01:00
elif night_actions . size ( ) == 1 :
on_player_night_action_chosen ( night_actions . keys ( ) [ 0 ] , player )
#night_actions.values()[0].setup(player)
2025-05-31 22:21:22 +01:00
else :
2025-06-21 14:36:09 +01:00
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)