Chelimbalo/scripts/multiplayer/session.gd
2025-11-30 02:06:44 +05:00

315 lines
8.3 KiB
GDScript

extends Node
enum TEAMS {
DEFENCE,
ATTACK,
SPECTATE,
UNASSIGNED
}
enum ROUND_STATES {
NOT_SET = 0,
BUY = 1,
ROUND = 2,
AFTER_ROUND = 3,
AFTER_PLANT = 4,
AFTER_SESSION = 5,
}
const ATTACK_LAYER: int = 0b10000
const DEFENCE_LAYER: int = 0b100000
signal round_started
signal round_state_changed(state: int)
signal player_stopped_interacting(id: int)
var player_nodes: Dictionary[int,Player] = {}
var object_containers: Array[ObjectContainer]
var dynamic_objects_spawner: MultiplayerSpawner
var plants: Array[PlantSite]
var plant_deadzones: Dictionary[StringName, Area3D]
var current_round: int = 0
var attacker_score: int = 0
var defender_score: int = 0
var attackers_alive: int = 0
var defenders_alive: int = 0
var bomb_timer: Timer
var round_timer: Timer
var buy_timer: Timer
var round_state: ROUND_STATES
var session_started: bool = false
var reference_round_time: float
func _ready() -> void:
if multiplayer.is_server() == false:
return
bomb_timer = Timer.new()
bomb_timer.wait_time = 45.0
bomb_timer.one_shot = true
bomb_timer.timeout.connect(end_round.bind(TEAMS.ATTACK))
round_timer = Timer.new()
round_timer.wait_time = 150
round_timer.one_shot = true
round_timer.timeout.connect(end_round.bind(TEAMS.DEFENCE))
buy_timer = Timer.new()
buy_timer.wait_time = 1
buy_timer.one_shot = true
buy_timer.timeout.connect(begin_main_stage)
add_child(bomb_timer)
add_child(round_timer)
add_child(buy_timer)
func _process(_delta: float) -> void:
if multiplayer.is_server() == false or not session_started:
return
match round_state:
ROUND_STATES.BUY:
reference_round_time = buy_timer.time_left
ROUND_STATES.ROUND:
reference_round_time = round_timer.time_left
ROUND_STATES.AFTER_PLANT:
reference_round_time = bomb_timer.time_left
_:
reference_round_time = 0
update_clock.rpc(reference_round_time)
@rpc("authority","call_remote","unreliable")
func update_clock(time: float):
reference_round_time = time
@rpc("any_peer","call_remote","reliable")
func start_session() -> void:
if not is_server_request():
return
if multiplayer.is_server():
start_session.rpc()
current_round = 0
attacker_score = 0
defender_score = 0
session_started = true
start_round()
@rpc("any_peer","call_remote","reliable")
func end_session() -> void:
if not is_server_request():
return
if multiplayer.is_server():
end_session.rpc()
bomb_timer.stop()
round_timer.stop()
buy_timer.stop()
object_containers.clear()
session_started = false
@rpc("any_peer","call_remote","reliable")
func start_round() -> void:
if not is_server_request():
return
if multiplayer.is_server():
buy_timer.start()
start_round.rpc()
for container in object_containers:
container.despawn()
attackers_alive = 0
defenders_alive = 0
current_round += 1
round_started.emit.call_deferred()
round_state = ROUND_STATES.BUY
round_state_changed.emit.call_deferred(round_state)
@rpc("any_peer","call_remote","reliable")
func end_round(win_team: int) -> void:
if not is_server_request():
return
if multiplayer.is_server():
get_tree().create_timer(5).timeout.connect(start_round)
end_round.rpc(win_team)
if win_team == TEAMS.DEFENCE:
defender_score += 1
elif win_team == TEAMS.ATTACK:
attacker_score += 1
round_state = ROUND_STATES.AFTER_ROUND
round_state_changed.emit(round_state)
@rpc("any_peer","call_remote","reliable")
func begin_main_stage() -> void:
if not is_server_request():
return
if multiplayer.is_server():
round_timer.start()
begin_main_stage.rpc()
round_state = ROUND_STATES.ROUND
round_state_changed.emit(round_state)
@rpc("any_peer","call_remote","reliable")
func begin_bomb_stage() -> void:
if not is_server_request():
return
if multiplayer.is_server():
bomb_timer.start()
round_timer.stop()
begin_bomb_stage.rpc()
round_state = ROUND_STATES.AFTER_PLANT
round_state_changed.emit(round_state)
func defuse_win() -> void:
if not multiplayer.is_server():
return
bomb_timer.stop()
end_round(TEAMS.DEFENCE)
@rpc("any_peer","call_local","reliable")
func add_dead(team: int):
if multiplayer.is_server() == false:
return
if team == TEAMS.ATTACK:
attackers_alive -= 1
if attackers_alive == 0 and round_state != ROUND_STATES.AFTER_ROUND:
end_round(TEAMS.DEFENCE)
if team == TEAMS.DEFENCE:
defenders_alive -= 1
if defenders_alive == 0 and round_state != ROUND_STATES.AFTER_ROUND:
end_round(TEAMS.ATTACK)
func is_server_request() -> bool:
return multiplayer.is_server() or multiplayer.get_remote_sender_id() == 1
## Spawns dynamic object at game scene [br]
## Dictionary keys: [br]
## (Required) scene - path/uuid to scene [br]
## (Optional) position - position to spawn [br]
## (Optional) impulse - impulse to apply [br]
## (Optional but required for each other) [br]
## ammo, remaining_ammo, slot - data for dropped weapon [br]
## for more, see dyn_objects_spawner.gd
func spawn(data: Dictionary) -> void:
spawn_internal.rpc_id(1,data)
@rpc("any_peer","call_local","reliable")
func spawn_internal(data: Dictionary) -> void:
if multiplayer.is_server() == false:
printerr(str(multiplayer.get_remote_sender_id())+" tried to spawn internally on "+str(multiplayer.get_unique_id()))
return
var object = dynamic_objects_spawner.spawn(data)
if data.has("position"):
object.global_position = data.position
func despawn(path: NodePath):
despawn_internal.rpc_id(1,path)
@rpc("any_peer","call_local","reliable")
func despawn_internal(path: NodePath) -> void:
if multiplayer.is_server() == false:
printerr(str(multiplayer.get_remote_sender_id())+" tried to despawn internally on "+str(multiplayer.get_unique_id()))
return
get_node(path).queue_free()
func shoot(damage: int) -> void:
if multiplayer.get_unique_id() == 1:
shoot_internal(1,damage)
else:
shoot_internal.rpc_id(1,multiplayer.get_unique_id(),damage)
@rpc("any_peer","call_local","reliable")
func shoot_internal(id:int , damage: int) -> void:
if multiplayer.is_server() == false:
return
var player: Player = player_nodes[id]
var player_camera: Camera3D = player.get_node("Camera3D")
var space: PhysicsDirectSpaceState3D = player.get_world_3d().direct_space_state
var endpoint: Vector3 = player_camera.global_position - player_camera.global_basis.z * 100
var ray = PhysicsRayQueryParameters3D.create(player_camera.global_position,endpoint,1)
ray.exclude = [player.get_rid()]
ray.collide_with_areas = false
match player.team:
TEAMS.DEFENCE:
ray.collision_mask |= ATTACK_LAYER
TEAMS.ATTACK:
ray.collision_mask |= DEFENCE_LAYER
_:
ray.collision_mask |= ATTACK_LAYER | DEFENCE_LAYER
var collision = space.intersect_ray(ray)
if collision != {} and collision["collider"] is Player:
collision["collider"].take_damage.rpc_id(int(collision["collider"].name),damage)
func interact() -> void:
if multiplayer.get_unique_id() == 1:
interact_internal(1)
else:
interact_internal.rpc_id(1,multiplayer.get_unique_id())
@rpc("any_peer","call_local","reliable")
func interact_internal(id:int) -> void:
if multiplayer.is_server() == false:
return
var player: Player = player_nodes[id]
var player_camera: Camera3D = player.get_node("Camera3D")
var space: PhysicsDirectSpaceState3D = player.get_world_3d().direct_space_state
var endpoint: Vector3 = player_camera.global_position - player_camera.global_basis.z * 3
var ray = PhysicsRayQueryParameters3D.create(player_camera.global_position,endpoint,1)
ray.exclude = [player.get_rid()]
ray.collide_with_areas = false
ray.collision_mask |= 64
var collision = space.intersect_ray(ray)
if collision != {} and collision["collider"] is Interactible:
collision["collider"].interaction_start(id)
func stop_interact():
if multiplayer.get_unique_id() == 1:
stop_interact_internal(1)
else:
stop_interact_internal.rpc_id(1,multiplayer.get_unique_id())
@rpc("any_peer","call_local","reliable")
func stop_interact_internal(id: int) -> void:
if multiplayer.is_server() == false:
return
player_stopped_interacting.emit(id)
func is_on_site() -> bool:
for plant in plants:
if plant.is_player_on_site(multiplayer.get_unique_id()):
return true
return false
func get_site() -> PlantSite:
for plant in plants:
if plant.is_player_on_site(multiplayer.get_unique_id()):
return plant
return null
func kill_site(site: StringName) -> void:
if multiplayer.is_server() == false:
return
for body in plant_deadzones[site].get_overlapping_bodies():
if body is Player:
body.kill_request.rpc()