416 lines
11 KiB
GDScript
416 lines
11 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 WIN_MONEY: int = 3400
|
|
const LOSE_MONEY: int = 2000
|
|
|
|
const ATTACK_LAYER: int = 0b10000
|
|
const DEFENCE_LAYER: int = 0b100000
|
|
|
|
signal session_started
|
|
signal session_ended
|
|
signal round_started
|
|
signal round_state_changed(state: int)
|
|
signal player_stopped_interacting(id: int)
|
|
signal team_won(team: TEAMS)
|
|
|
|
var player_nodes: Dictionary[int,Player] = {}
|
|
var player_data: Dictionary[int,Dictionary] = {}
|
|
|
|
var dynamic_objects_parent: Node3D
|
|
var object_containers: Array[ObjectContainer]
|
|
var plants: Array[PlantSite]
|
|
var plant_deadzones: Dictionary[StringName, Area3D]
|
|
|
|
var session_started_flag: bool = false
|
|
var round_state: ROUND_STATES
|
|
|
|
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 reference_round_time: float
|
|
|
|
var bomb_timer: Timer
|
|
var round_timer: Timer
|
|
var buy_timer: Timer
|
|
|
|
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)
|
|
|
|
multiplayer.peer_connected.connect(send_session_data)
|
|
multiplayer.peer_disconnected.connect(delete_player)
|
|
|
|
func send_session_data(peer_id: int = -1):
|
|
if multiplayer.is_server() == false or session_started_flag == false:
|
|
return
|
|
|
|
var data: PackedByteArray
|
|
data.resize(5)
|
|
data.encode_u8(0,current_round)
|
|
data.encode_u8(1,attackers_alive)
|
|
data.encode_u8(2,defenders_alive)
|
|
data.encode_u8(3,round_state)
|
|
data.encode_u8(4,session_started_flag)
|
|
|
|
if peer_id == -1:
|
|
recieve_session_data.rpc(data,player_data)
|
|
else:
|
|
recieve_session_data.rpc_id(peer_id,data,player_data)
|
|
|
|
@rpc("authority","call_remote","unreliable")
|
|
func recieve_session_data(data: PackedByteArray,players_data_sent: Dictionary[int,Dictionary]):
|
|
if not is_server_request():
|
|
return
|
|
|
|
current_round = data.decode_u8(0)
|
|
attackers_alive = data.decode_u8(1)
|
|
defenders_alive = data.decode_u8(2)
|
|
round_state = data.decode_u8(3) as ROUND_STATES
|
|
if session_started_flag != bool(data.decode_u8(4)):
|
|
session_started_flag = bool(data.decode_u8(4))
|
|
if session_started_flag:
|
|
session_started.emit()
|
|
else:
|
|
session_ended.emit()
|
|
player_data = players_data_sent
|
|
|
|
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)
|
|
send_session_data()
|
|
|
|
@rpc("authority","call_remote","unreliable")
|
|
func update_clock(time: float):
|
|
reference_round_time = time
|
|
|
|
@rpc("authority","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_flag = true
|
|
session_started.emit()
|
|
bomb_timer.wait_time = LobbySettings.bomb_time
|
|
round_timer.wait_time = LobbySettings.round_time
|
|
buy_timer.wait_time = LobbySettings.buy_time
|
|
|
|
var all_players: PackedInt32Array = multiplayer.get_peers()
|
|
all_players.append(multiplayer.get_unique_id())
|
|
for player in all_players:
|
|
player_data[player] = {
|
|
"money" : 800,
|
|
"nickname" : "Seliboba"
|
|
}
|
|
|
|
start_round()
|
|
|
|
@rpc("authority","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_ended.emit()
|
|
|
|
|
|
session_started_flag = false
|
|
plants = []
|
|
plant_deadzones = {}
|
|
|
|
dynamic_objects_parent = null
|
|
|
|
func quit_session() -> void:
|
|
if multiplayer.is_server():
|
|
end_session()
|
|
await get_tree().create_timer(0.1).timeout
|
|
else:
|
|
session_ended.emit()
|
|
session_started_flag = false
|
|
Lobby.leave()
|
|
|
|
|
|
@rpc("authority","call_remote","reliable")
|
|
func start_round() -> void:
|
|
if not is_server_request():
|
|
return
|
|
if multiplayer.is_server():
|
|
buy_timer.start()
|
|
start_round.rpc()
|
|
round_timer.stop()
|
|
bomb_timer.stop()
|
|
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("authority","call_remote","reliable")
|
|
func end_round(win_team: int) -> void:
|
|
if not is_server_request():
|
|
return
|
|
|
|
if win_team == TEAMS.DEFENCE:
|
|
defender_score += 1
|
|
for defender in Lobby.defence_team:
|
|
player_data[defender].money += WIN_MONEY
|
|
for attacker in Lobby.attack_team:
|
|
player_data[attacker].money += LOSE_MONEY
|
|
if defender_score >= LobbySettings.win_score:
|
|
win_session(TEAMS.DEFENCE)
|
|
|
|
elif win_team == TEAMS.ATTACK:
|
|
attacker_score += 1
|
|
for defender in Lobby.defence_team:
|
|
player_data[defender].money += LOSE_MONEY
|
|
for attacker in Lobby.attack_team:
|
|
player_data[attacker].money += WIN_MONEY
|
|
|
|
if attacker_score >= LobbySettings.win_score:
|
|
win_session(TEAMS.ATTACK)
|
|
|
|
if multiplayer.is_server():
|
|
end_round.rpc(win_team)
|
|
if defender_score >= LobbySettings.win_score or attacker_score >= LobbySettings.win_score:
|
|
get_tree().create_timer(7.5).timeout.connect(end_session)
|
|
else:
|
|
get_tree().create_timer(5).timeout.connect(start_round)
|
|
if current_round == LobbySettings.half_rounds:
|
|
Lobby.swap_teams()
|
|
var temp_rounds = attacker_score
|
|
attacker_score = defender_score
|
|
defender_score = temp_rounds
|
|
sync_score.rpc(attacker_score,defender_score)
|
|
|
|
round_state = ROUND_STATES.AFTER_ROUND
|
|
round_state_changed.emit(round_state)
|
|
|
|
@rpc("authority","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("authority","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("reliable")
|
|
func win_session(team: int):
|
|
team_won.emit(team)
|
|
|
|
MouseConfiner.set_global_mode(Input.MOUSE_MODE_VISIBLE)
|
|
|
|
if is_multiplayer_authority():
|
|
win_session.rpc(team)
|
|
|
|
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 and round_state != ROUND_STATES.AFTER_PLANT:
|
|
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
|
|
|
|
func shoot(id:int , limb_damage: int, torso_damage: int,head_damage: int, distance: float,damage_reduction_curve: Curve = null) -> 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 * distance
|
|
|
|
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:
|
|
var hit_player: Player = collision["collider"]
|
|
var shape_object: CollisionShape3D = hit_player.shape_owner_get_owner(collision["shape"])
|
|
var reduction: float = 1
|
|
var damage: int = 0
|
|
|
|
match shape_object.get_groups()[0]:
|
|
"Head":
|
|
damage = head_damage
|
|
"Limb":
|
|
damage = limb_damage
|
|
_:
|
|
damage = torso_damage
|
|
|
|
if damage_reduction_curve != null:
|
|
var distance_to_hit = (player_camera.global_position - collision["position"]).length()/distance
|
|
reduction = damage_reduction_curve.sample(distance_to_hit)
|
|
|
|
hit_player.take_damage(int(float(damage) * reduction))
|
|
|
|
|
|
func interact(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(id: int) -> void:
|
|
if multiplayer.is_server() == false:
|
|
return
|
|
player_stopped_interacting.emit(id)
|
|
|
|
func is_on_site(id: int) -> bool:
|
|
for plant in plants:
|
|
if plant.is_player_on_site(id):
|
|
return true
|
|
return false
|
|
|
|
func get_site(id: int) -> PlantSite:
|
|
for plant in plants:
|
|
if plant.is_player_on_site(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.die()
|
|
|
|
@rpc("authority","call_remote","unreliable")
|
|
func sync_score(attack: int, defence: int):
|
|
attacker_score = attack
|
|
defender_score = defence
|
|
|
|
if defender_score >= LobbySettings.win_score:
|
|
win_session(TEAMS.DEFENCE)
|
|
elif attacker_score >= LobbySettings.win_score:
|
|
win_session(TEAMS.ATTACK)
|
|
|
|
@rpc("authority","call_remote","reliable")
|
|
func delete_player(id: int):
|
|
if not is_server_request():
|
|
return
|
|
if multiplayer.is_server():
|
|
delete_player.rpc_id(id)
|
|
|
|
player_data.erase(id)
|
|
|
|
func get_player_data() -> Dictionary:
|
|
var id: int = multiplayer.get_unique_id()
|
|
if player_data.has(id):
|
|
return player_data[id]
|
|
else:
|
|
return {}
|