2024-05-25 17:15:59 -06:00
|
|
|
extends Node3D
|
2024-05-31 11:59:57 -06:00
|
|
|
class_name Level
|
|
|
|
|
|
|
|
enum GameState {
|
|
|
|
WAITING, # Stop state to be used by default, nothing should react to this
|
|
|
|
TAGGER_GROUNDED, # Tagger is on the ground
|
|
|
|
TAGGER_UNGROUNDED, # Tagger is off the ground
|
|
|
|
TAGGER_CHANGE, # Tagger has caught someone, and new person must go to ground
|
|
|
|
}
|
|
|
|
|
|
|
|
var game_state: GameState = GameState.WAITING
|
2024-06-03 11:05:52 -06:00
|
|
|
var safe_player: Player
|
2024-05-31 11:59:57 -06:00
|
|
|
var game_tagger: Player
|
|
|
|
|
|
|
|
signal state_change(old: GameState, new: GameState)
|
|
|
|
signal state_enter_tagger_grounded
|
|
|
|
signal state_leave_tagger_grounded
|
|
|
|
signal state_enter_tagger_ungrounded
|
|
|
|
signal state_leave_tagger_ungrounded
|
|
|
|
signal state_enter_tagger_change(new_tagger: Player)
|
|
|
|
signal state_leave_tagger_change
|
|
|
|
|
2024-06-03 11:05:52 -06:00
|
|
|
const MIN_PLAYERS := 3
|
2024-05-31 21:59:49 -06:00
|
|
|
const global_msg_1 := "Waiting for at least {min_players} players to join"
|
|
|
|
const global_msg_2 := "Game is starting soon, {tagger_username} is it!"
|
|
|
|
const global_msg_3 := "{tagger_username} is it! Run!"
|
|
|
|
const global_msg_4 := "{old_tagger_username} caught {tagger_username}!"
|
|
|
|
const global_msg_5 := "{old_tagger_username} left. {tagger_username} is now it!"
|
|
|
|
|
2024-06-03 11:05:52 -06:00
|
|
|
@rpc("any_peer", "reliable", "call_local")
|
|
|
|
func push_global_message(msg: String):
|
|
|
|
%GlobalMessage.text = msg
|
|
|
|
print(msg)
|
|
|
|
|
2024-05-31 11:59:57 -06:00
|
|
|
func set_tagger_grounded(v: bool):
|
|
|
|
if v and game_state == GameState.TAGGER_CHANGE:
|
|
|
|
_change_state(GameState.TAGGER_GROUNDED)
|
|
|
|
return
|
|
|
|
if not (game_state == GameState.TAGGER_GROUNDED or game_state == GameState.TAGGER_UNGROUNDED):
|
|
|
|
return
|
|
|
|
if (not v) and game_state == GameState.TAGGER_GROUNDED:
|
|
|
|
_change_state(GameState.TAGGER_UNGROUNDED)
|
|
|
|
if v and game_state == GameState.TAGGER_UNGROUNDED:
|
|
|
|
_change_state(GameState.TAGGER_GROUNDED)
|
2024-05-31 21:59:49 -06:00
|
|
|
|
|
|
|
|
|
|
|
func not_enough_players() -> bool:
|
|
|
|
if %Players.get_children().size() < MIN_PLAYERS:
|
2024-06-03 11:05:52 -06:00
|
|
|
game_tagger = null
|
2024-05-31 21:59:49 -06:00
|
|
|
_change_state(GameState.WAITING)
|
2024-06-03 11:05:52 -06:00
|
|
|
push_global_message.rpc(global_msg_1.format({"min_players": MIN_PLAYERS}))
|
2024-05-31 21:59:49 -06:00
|
|
|
return true
|
|
|
|
return false
|
|
|
|
|
|
|
|
func change_to_random_tagger():
|
2024-06-03 11:05:52 -06:00
|
|
|
safe_player = null
|
2024-05-31 21:59:49 -06:00
|
|
|
game_tagger = %Players.get_children().pick_random()
|
|
|
|
_change_state(GameState.TAGGER_CHANGE)
|
|
|
|
|
|
|
|
func tagger_left_game():
|
|
|
|
if not_enough_players():
|
|
|
|
return
|
|
|
|
var old_tagger_username := game_tagger.username
|
|
|
|
change_to_random_tagger()
|
|
|
|
var tagger_username := game_tagger.username
|
2024-06-03 11:05:52 -06:00
|
|
|
push_global_message.rpc(global_msg_5.format({
|
2024-05-31 21:59:49 -06:00
|
|
|
"old_tagger_username": old_tagger_username,
|
2024-06-03 11:05:52 -06:00
|
|
|
"tagger_username": tagger_username}))
|
2024-05-31 11:59:57 -06:00
|
|
|
|
|
|
|
func set_tagger(who: Player):
|
2024-06-03 11:05:52 -06:00
|
|
|
safe_player = game_tagger
|
2024-05-31 11:59:57 -06:00
|
|
|
game_tagger = who
|
|
|
|
_change_state(GameState.TAGGER_CHANGE)
|
|
|
|
|
|
|
|
func _change_state(new_state: GameState):
|
2024-06-03 11:05:52 -06:00
|
|
|
var id := -1
|
|
|
|
if game_tagger != null:
|
|
|
|
id = game_tagger.get_multiplayer_authority()
|
|
|
|
var safe_player_id := -1
|
|
|
|
if safe_player != null:
|
|
|
|
safe_player_id = safe_player.get_multiplayer_authority()
|
|
|
|
_change_state_full.rpc(new_state, id, safe_player_id)
|
|
|
|
|
|
|
|
@rpc("any_peer", "reliable", "call_local")
|
|
|
|
func _change_state_full(new_state: GameState, new_tagger_id: int, new_safe_player_id: int):
|
|
|
|
game_tagger = null
|
|
|
|
safe_player = null
|
|
|
|
if new_tagger_id != -1:
|
|
|
|
game_tagger = find_player_by_id(new_tagger_id)
|
|
|
|
if new_safe_player_id != -1:
|
|
|
|
safe_player = find_player_by_id(new_safe_player_id)
|
2024-05-31 11:59:57 -06:00
|
|
|
if game_state == new_state:
|
|
|
|
return
|
|
|
|
if new_state == GameState.WAITING:
|
|
|
|
game_tagger = null
|
|
|
|
state_change.emit(game_state, new_state)
|
|
|
|
if new_state == GameState.TAGGER_GROUNDED:
|
|
|
|
state_enter_tagger_grounded.emit()
|
|
|
|
if new_state == GameState.TAGGER_UNGROUNDED:
|
|
|
|
state_enter_tagger_ungrounded.emit()
|
|
|
|
if new_state == GameState.TAGGER_CHANGE:
|
|
|
|
state_enter_tagger_change.emit(game_tagger)
|
|
|
|
|
|
|
|
var old_state = game_state
|
|
|
|
game_state = new_state
|
|
|
|
|
|
|
|
if old_state == GameState.TAGGER_GROUNDED:
|
|
|
|
state_leave_tagger_grounded.emit()
|
|
|
|
if old_state == GameState.TAGGER_UNGROUNDED:
|
|
|
|
state_leave_tagger_ungrounded.emit()
|
|
|
|
if old_state == GameState.TAGGER_CHANGE:
|
|
|
|
state_leave_tagger_change.emit()
|
|
|
|
|
2024-06-03 11:05:52 -06:00
|
|
|
func tagging_possible(_tagger: Player, tagee: Player) -> bool:
|
|
|
|
if game_state != GameState.TAGGER_GROUNDED and game_state != GameState.TAGGER_UNGROUNDED:
|
|
|
|
return false
|
|
|
|
return safe_player != tagee
|
2024-05-25 17:15:59 -06:00
|
|
|
|
2024-05-25 18:44:52 -06:00
|
|
|
func _ready():
|
|
|
|
# Hacky way to start level without going though multiplayer screens
|
|
|
|
if get_parent() == get_tree().root:
|
2024-05-25 18:47:51 -06:00
|
|
|
%PlayerSpawner.spawn(multiplayer.get_unique_id())
|
2024-05-25 18:44:52 -06:00
|
|
|
|
2024-06-03 11:05:52 -06:00
|
|
|
|
|
|
|
func player_ground(v: bool, player: Player):
|
|
|
|
if game_tagger == player:
|
|
|
|
set_tagger_grounded(v)
|
|
|
|
|
|
|
|
func player_tag(who: Player, player: Player):
|
|
|
|
if not tagging_possible(player, who) or game_tagger != player:
|
|
|
|
return
|
|
|
|
var old_tagger_username := game_tagger.username
|
|
|
|
var tagger_username := player.username
|
|
|
|
push_global_message.rpc(global_msg_4.format({
|
|
|
|
"old_tagger_username": old_tagger_username,
|
|
|
|
"tagger_username": tagger_username}))
|
|
|
|
set_tagger(who)
|
|
|
|
|
|
|
|
func start_game():
|
|
|
|
change_to_random_tagger()
|
|
|
|
push_global_message.rpc(global_msg_3.format({"tagger_username" : game_tagger.username}))
|
|
|
|
|
2024-05-25 17:15:59 -06:00
|
|
|
# Called from the server
|
|
|
|
func server_add_player(id: int):
|
|
|
|
%PlayerSpawner.spawn(id)
|
2024-06-03 11:05:52 -06:00
|
|
|
if not not_enough_players() and game_state == GameState.WAITING:
|
|
|
|
start_game()
|
|
|
|
|
|
|
|
|
2024-05-31 21:59:49 -06:00
|
|
|
func server_remove_player(id: int):
|
2024-06-03 11:05:52 -06:00
|
|
|
var player := find_player_by_id(id)
|
|
|
|
var tagger_left := (game_tagger == player)
|
|
|
|
await %PlayerSpawner.despawn_player(id)
|
|
|
|
if not_enough_players():
|
|
|
|
return
|
|
|
|
if tagger_left:
|
|
|
|
tagger_left_game()
|
|
|
|
|
|
|
|
func client_add_player(player: Player):
|
|
|
|
if not player.is_multiplayer_authority():
|
|
|
|
return
|
|
|
|
player.ground.connect(player_ground.bind(player))
|
|
|
|
player.tag.connect(player_tag.bind(player))
|
|
|
|
|
|
|
|
|
|
|
|
func client_remove_player(_node):
|
|
|
|
pass
|
2024-05-31 11:59:57 -06:00
|
|
|
|
|
|
|
static func find_level(node: Node) -> Level:
|
|
|
|
while not (node is Level):
|
|
|
|
node = node.get_parent()
|
|
|
|
return node
|
2024-06-03 11:05:52 -06:00
|
|
|
|
|
|
|
func find_player_by_id(id: int) -> Player:
|
|
|
|
return %Players.get_node(str(id))
|