4
0
Fork 0
WolfBox/scripts/game_manager.gd

333 lines
11 KiB
GDScript3
Raw Normal View History

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)