forked from Nekojimi/JackIt
333 lines
11 KiB
GDScript
333 lines
11 KiB
GDScript
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)
|