Global refactor

This commit is contained in:
Rendo 2025-12-09 11:53:52 +05:00
commit 0589ca4e23
180 changed files with 249 additions and 401 deletions

View file

@ -0,0 +1,11 @@
extends Label
@export var inventory: WeaponSystem
func _process(_delta: float) -> void:
var weapons = ""
for child in inventory.get_children():
if child is WeaponSubStateMachine:
weapons += child.name + ", "
text = weapons

View file

@ -0,0 +1 @@
uid://dmy6ahci16los

View file

@ -0,0 +1,21 @@
extends VBoxContainer
@export var property_array: Dictionary[NodePath,StringName]
func _ready() -> void:
for target in property_array.keys():
var hbox = HBoxContainer.new()
var name_label = Label.new()
var splitted = property_array[target].split(":")
name_label.text = splitted[len(splitted)-1]
name_label.name = "Name"
var value_label = Label.new()
value_label.name = "Value"
hbox.add_child(name_label,true)
hbox.add_child(value_label,true)
hbox.name = str(target).replace("/","_").replace(".","")
add_child(hbox,true)
func _process(_delta: float) -> void:
for target in property_array:
get_node(str(target).replace("/","_").replace(".","")+"/Value").text = str(get_node(target).get(property_array[target]))

View file

@ -0,0 +1 @@
uid://7gmgcaxfh8sb

View file

@ -0,0 +1,30 @@
extends CharacterBody3D
class_name Interactible
signal interacted(with: int)
signal stopped_interacting(with: int)
var interacted_id: int = -1
func _ready() -> void:
if not multiplayer.is_server():
return
Session.player_stopped_interacting.connect(player_stopped_interacting)
func interaction_start(player_id: int):
if not multiplayer.is_server():
return
interacted.emit(player_id)
interacted_id = player_id
func player_stopped_interacting(id: int):
if interacted_id == id:
interaction_end()
func interaction_end():
if not multiplayer.is_server():
return
stopped_interacting.emit(interacted_id)
interacted_id = -1

View file

@ -0,0 +1 @@
uid://d1c4u4aul8oc1

13
systems/item_spawner.gd Normal file
View file

@ -0,0 +1,13 @@
extends Node3D
@export var item: PackedScene
func _ready() -> void:
if multiplayer.is_server():
Session.round_started.connect(spawn)
func spawn():
if multiplayer.is_server():
var node = item.instantiate()
add_sibling(node,true)
node.global_position = global_position

View file

@ -0,0 +1 @@
uid://dncldab5y4yod

View file

@ -0,0 +1,16 @@
extends Node
class_name ObjectContainer
@export var exlusion_list: Array[Node]
func _ready() -> void:
if not multiplayer.is_server(): return
Session.object_containers.append(self)
func despawn():
if not multiplayer.is_server(): return
for child in get_children():
if exlusion_list.has(child):
continue
child.queue_free()

View file

@ -0,0 +1 @@
uid://3i00rp8urth7

View file

@ -0,0 +1,16 @@
extends Node
@export var player: Player
@export var layer: bool
@export var inverse: bool
const ATTACK_LAYER: int = 0b10000
const DEFENCE_LAYER: int = 0b100000
func _ready() -> void:
if is_multiplayer_authority() == false: return
var mask = (ATTACK_LAYER if (player.team == Session.TEAMS.DEFENCE != inverse) else DEFENCE_LAYER)
if layer:
get_parent().collision_layer |= mask
else:
get_parent().collision_mask |= mask

View file

@ -0,0 +1 @@
uid://5gwpjiswnegn

View file

@ -0,0 +1,35 @@
extends Camera3D
@export var SPEED = 10.0
var active: bool
func _enter_tree() -> void:
set_multiplayer_authority(int(get_parent().get_parent().name))
func set_active() -> void:
if not is_multiplayer_authority():
return
active = true
current = true
top_level = true
func _process(delta: float) -> void:
if active == false or not is_multiplayer_authority():
return
var xz_plane = Input.get_vector("plr_strafe_l","plr_strafe_r","plr_forward","plr_back")
var y = Input.get_axis("spc_down","spc_up")
var direction = Vector3(xz_plane.x,y,xz_plane.y)
global_position += global_basis * direction * SPEED * delta
func rotate_camera(x,y) -> void:
rotate_y(x)
rotation.x = clamp(rotation.x + y,-PI/2,PI/2)
orthonormalize()
func _input(event: InputEvent) -> void:
if active == false or not is_multiplayer_authority():
return
if event is InputEventMouseMotion:
rotate_camera(-event.relative.x * ClientSettings.SENSITIVITY,-event.relative.y * ClientSettings.SENSITIVITY)

View file

@ -0,0 +1 @@
uid://bl8gfrrc512q2

60
systems/player/player.gd Normal file
View file

@ -0,0 +1,60 @@
extends CharacterBody3D
class_name Player
@export var team: Session.TEAMS
@export var weapon_models: Dictionary[StringName,Node3D]
@export var player_id: int = 1:
set(id):
player_id = id
$PlayerInput.set_multiplayer_authority(id)
$Camera3D.set_multiplayer_authority(id)
var passived: bool = false
signal health_changed(to: int)
signal died
const MAX_HP = 100
@export var hp: int = 100:
set(value):
if value < 0:
hp = 0
else:
hp = value
health_changed.emit(hp)
if hp == 0:
die()
get:
return hp
func _physics_process(_delta: float) -> void:
if not multiplayer.is_server():
return
move_and_slide()
func die() -> void:
if (not multiplayer.is_server()):
return
Session.add_dead(team)
died.emit()
die_on_peers.rpc()
passived = true
@rpc("authority","call_remote","reliable")
func die_on_peers():
if multiplayer.get_remote_sender_id() != 1:
return
died.emit()
func passive() -> void:
passived = true
func depassive() -> void:
passived = false
func take_damage(damage: int):
hp -= damage

View file

@ -0,0 +1 @@
uid://3dphlay25fih

View file

@ -0,0 +1,59 @@
extends Camera3D
class_name PlayerCamera
var vertical_compensation : float
var compensation_tween: Tween
var compensate: bool = false
var disabled: bool = false
var compensation_speed: float
@export var compensation_time: float = 1.0
@export var compensation_delay: float = 0.5
func _ready() -> void:
if not is_multiplayer_authority():
return
# Move to level controller when possible
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
current = true
func _process(delta: float) -> void:
if disabled: return
if compensate:
if abs(vertical_compensation) <= 0.001:
vertical_compensation = 0
compensate = false
return
rotate_camera(0,compensation_speed * delta)
func _input(event: InputEvent) -> void:
if not is_multiplayer_authority() or disabled:
return
if Input.mouse_mode == Input.MouseMode.MOUSE_MODE_CAPTURED and event is InputEventMouseMotion:
rotate_camera(-event.relative.x * ClientSettings.SENSITIVITY,-event.relative.y * ClientSettings.SENSITIVITY)
func rotate_camera(x,y) -> void:
get_parent().rotate_y(x)
rotation.x = clamp(rotation.x + y,-PI/2,PI/2)
orthonormalize()
if sign(vertical_compensation) == sign(y):
if abs(vertical_compensation) - abs(y) < 0:
vertical_compensation = 0
else:
vertical_compensation -= y
func recoil(x,y) -> void:
rotate_camera(x,y)
vertical_compensation -= y
if compensation_tween:
compensation_tween.kill()
compensation_tween = create_tween()
compensation_tween.tween_interval(compensation_delay)
compensation_tween.tween_callback(start_compensating)
func start_compensating() -> void:
compensate = true
compensation_speed = vertical_compensation / compensation_time
func disable() -> void:
disabled = true

View file

@ -0,0 +1 @@
uid://bjhbdh6xsjgnn

View file

@ -0,0 +1,4 @@
extends Node
var player: CharacterBody3D

View file

@ -0,0 +1 @@
uid://dv3pm11t6p4si

View file

@ -0,0 +1,182 @@
extends MultiplayerSynchronizer
class_name PlayerInput
const SCOPE: int = 0b1
const CROUCH: int = 0b10
const WALK: int = 0b100
#region SYNC
@export var direction: Vector2
@export var compressed_states: int
#endregion
signal jumped
signal drop
signal switch_weapon(to_slot: StringName)
signal fire_begin
signal fire_end
signal alternate_state
signal switch_firemode
signal reload
signal scope_begin
signal scope_end
signal crouch_begin
signal crouch_end
signal walk_begin
signal walk_end
signal interact_begin
signal interact_end
func _process(_delta: float) -> void:
if not is_multiplayer_authority(): return
direction = Input.get_vector("plr_strafe_r","plr_strafe_l", "plr_back","plr_forward")
func _unhandled_input(event: InputEvent) -> void:
if not is_multiplayer_authority(): return
if event.is_action_pressed("plr_ult"):
switch_on_server.rpc_id(1,"ultimate")
elif event.is_action_pressed("plr_bomb"):
switch_on_server.rpc_id(1,"bomb")
elif event.is_action_pressed("plr_primary"):
switch_on_server.rpc_id(1,"primary")
elif event.is_action_pressed("plr_active_first"):
switch_on_server.rpc_id(1,"ability_first")
elif event.is_action_pressed("plr_active_second"):
switch_on_server.rpc_id(1,"ability_second")
elif event.is_action_pressed("plr_active_third"):
switch_on_server.rpc_id(1,"ability_third")
elif event.is_action_pressed("plr_secondary"):
switch_on_server.rpc_id(1,"secondary")
elif event.is_action_pressed("plr_knife"):
switch_on_server.rpc_id(1,"knife")
if event.is_action_pressed("plr_fire"):
fire_on_server.rpc_id(1,false)
if event.is_action_released("plr_fire"):
fire_on_server.rpc_id(1,true)
if event.is_action_pressed("plr_scope"):
alternate_state_on_server.rpc_id(1)
if event.is_action_pressed("plr_firemode"):
switch_firemode_on_server.rpc_id(1)
if event.is_action_pressed("plr_drop"):
drop_on_server.rpc_id(1)
var crouching: bool = compressed_states & CROUCH
var walking: bool = compressed_states & WALK
var scoping: bool = compressed_states & SCOPE
if event.is_action_pressed("plr_crouch"):
if ClientSettings.TOGGLE_CROUCH:
crouch_on_server.rpc_id(1,crouching)
compressed_states ^= CROUCH
elif not crouching:
compressed_states |= CROUCH
crouch_on_server.rpc_id(1,false)
if event.is_action_released("plr_crouch") and not ClientSettings.TOGGLE_CROUCH and crouching:
compressed_states &= ~CROUCH
crouch_on_server.rpc_id(1,true)
if event.is_action_pressed("plr_walk"):
if ClientSettings.TOGGLE_WALK:
walk_on_server.rpc_id(1,walking)
compressed_states ^= WALK
elif not walking:
compressed_states |= WALK
walk_on_server.rpc_id(1,false)
if event.is_action_released("plr_walk") and not ClientSettings.TOGGLE_WALK and walking:
compressed_states &= ~WALK
walk_on_server.rpc_id(1,true)
if event.is_action_pressed("plr_scope"):
if ClientSettings.TOGGLE_SCOPE:
scope_on_server.rpc_id(1,scoping)
compressed_states ^= SCOPE
elif not scoping:
compressed_states |= SCOPE
scope_on_server.rpc_id(1,false)
if event.is_action_released("plr_scope") and not ClientSettings.TOGGLE_SCOPE and scoping:
compressed_states &= ~SCOPE
scope_on_server.rpc_id(1,true)
if event.is_action_pressed("plr_reload"):
reload_on_server.rpc_id(1)
if event.is_action_pressed("plr_interact"):
interact_on_server.rpc_id(1,false)
if event.is_action_released("plr_interact"):
interact_on_server.rpc_id(1,true)
if event.is_action_pressed("plr_jump"):
jump_on_server.rpc_id(1)
@rpc("authority","call_local","reliable")
func switch_on_server(slot: StringName) -> void:
if not multiplayer.is_server(): return
switch_weapon.emit(slot)
@rpc("authority","call_local","reliable")
func drop_on_server() -> void:
if not multiplayer.is_server(): return
drop.emit()
@rpc("authority","call_local","reliable")
func fire_on_server(end: bool) -> void:
if not multiplayer.is_server(): return
if end:
fire_end.emit()
else:
fire_begin.emit()
@rpc("authority","call_local","reliable")
func alternate_state_on_server() -> void:
if not multiplayer.is_server(): return
alternate_state.emit()
@rpc("authority","call_local","reliable")
func switch_firemode_on_server() -> void:
if not multiplayer.is_server(): return
switch_firemode.emit()
@rpc("authority","call_local","reliable")
func crouch_on_server(end: bool) -> void:
if not multiplayer.is_server(): return
if end:
crouch_end.emit()
else:
crouch_begin.emit()
@rpc("authority","call_local","reliable")
func walk_on_server(end: bool) -> void:
if not multiplayer.is_server(): return
if end:
walk_end.emit()
else:
walk_begin.emit()
@rpc("authority","call_local","reliable")
func scope_on_server(end: bool) -> void:
if not multiplayer.is_server(): return
if end:
scope_end.emit()
else:
scope_begin.emit()
@rpc("authority","call_local","reliable")
func reload_on_server() -> void:
if not multiplayer.is_server(): return
reload.emit()
@rpc("authority","call_local","reliable")
func interact_on_server(end: bool) -> void:
if not multiplayer.is_server(): return
if end:
interact_end.emit()
else:
interact_begin.emit()
@rpc("authority","call_local","reliable")
func jump_on_server() -> void:
if not multiplayer.is_server(): return
jumped.emit()

View file

@ -0,0 +1 @@
uid://dfvnx8f1v6m5g

View file

@ -0,0 +1,8 @@
extends Node
func on_player_start_interacting():
Session.interact(get_parent().player_id)
func on_player_end_interacting():
Session.stop_interact(get_parent().player_id)

View file

@ -0,0 +1 @@
uid://dsp1sq46c5i3y

View file

@ -0,0 +1,31 @@
extends Node
class_name PlayerMovement
@export var player: Player
@export var player_input: PlayerInput
@export var jump_velocity: float
var disabled: bool
func disable() -> void:
disabled = true
func process_movement(max_speed: float,acceleration: float,deceleration: float,delta: float) -> void:
if is_multiplayer_authority() == false:
return
if Session.round_state == Session.ROUND_STATES.BUY or disabled or player.passived:
player.velocity.x = 0
player.velocity.z = 0
return
var input_dir := player_input.direction
var direction := (player.transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
if direction:
player.velocity.x = clamp(player.velocity.x + direction.x * acceleration * delta,-max_speed*abs(direction.x),max_speed*abs(direction.x))
player.velocity.z = clamp(player.velocity.z + direction.z * acceleration * delta,-max_speed*abs(direction.z),max_speed*abs(direction.z))
else:
player.velocity.x = move_toward(player.velocity.x, 0, deceleration*delta)
player.velocity.z = move_toward(player.velocity.z, 0, deceleration*delta)
func jump() -> void:
player.velocity.y = jump_velocity

View file

@ -0,0 +1 @@
uid://bs4y647h5rdfr

View file

@ -0,0 +1,43 @@
extends Area3D
@export var weapon_spawner: MultiplayerSpawner
@export var weapon_system: WeaponSystem
var disabled: bool
func _ready() -> void:
if is_multiplayer_authority() == false: return
body_entered.connect(on_body_entered)
func disable() -> void:
disabled = true
func on_body_entered(body: Node3D):
if disabled:
return
if body is DroppableWeapon:
if weapon_system.can_add(body.slot) == false or (body.team != Session.TEAMS.UNASSIGNED and get_parent().team != body.team):
return
weapon_spawner.spawn({
"ammo": body.weapon.ammo,
"remaining_ammo": body.weapon.remaining_ammo,
"scene_file_path": body.weapon.scene_file_path,
"slot": body.slot
})
body.queue_free()
func start_temp_ignore():
if disabled:
return
if is_multiplayer_authority() == false:
return
monitoring = false
get_tree().create_timer(0.5).timeout.connect(stop_temp_ignore)
func stop_temp_ignore():
if disabled:
return
if is_multiplayer_authority() == false:
return
monitoring = true

View file

@ -0,0 +1 @@
uid://xsgfbuyje35p

View file

@ -0,0 +1,12 @@
extends RayCast3D
func _ready() -> void:
add_exception($"../..")
func try_deal_damage(damage) -> void:
if is_colliding() == false:
return
var collider = get_collider()
if collider is Player:
collider.hp -= damage

View file

@ -0,0 +1 @@
uid://6c14qse4vnra

View file

@ -0,0 +1,37 @@
extends Camera3D
@export var SPEED = 10.0
func _enter_tree() -> void:
set_multiplayer_authority(int(name))
func _ready() -> void:
if not is_multiplayer_authority():
return
# Move to level controller when possible
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
current = true
func _process(delta: float) -> void:
if not is_multiplayer_authority():
return
var xz_plane = Input.get_vector("plr_strafe_l","plr_strafe_r","plr_forward","plr_back")
var y = Input.get_axis("spc_down","spc_up")
var direction = Vector3(xz_plane.x,y,xz_plane.y)
global_position += global_basis * direction * SPEED * delta
func rotate_camera(x,y) -> void:
rotate_y(x)
rotation.x = clamp(rotation.x + y,-PI/2,PI/2)
orthonormalize()
func _input(event: InputEvent) -> void:
if not is_multiplayer_authority():
return
if event is InputEventMouseMotion:
rotate_camera(-event.relative.x * ClientSettings.SENSITIVITY,-event.relative.y * ClientSettings.SENSITIVITY)
@rpc("any_peer","call_local","reliable")
func set_after_spawn(start_position: Vector3, _team: int):
global_position = start_position

View file

@ -0,0 +1 @@
uid://cikw7fcykkpd5

View file

@ -0,0 +1,36 @@
extends State
@export var max_speed: float = 2.5
@export var acceleration: float = 1.0
@export var deceleration: float = 100.0
@export var toggle: bool = false
@export var stand_up_area: Area3D
@export var player: Player
@export var player_movement: PlayerMovement
@export var player_input: PlayerInput
@export var animation_player: AnimationPlayer
@export var crouch_time: float = 0.1
@export var weapon_system: WeaponSystem
func enter() -> void:
animation_player.play("crouch",-1,1/crouch_time)
player_input.crouch_end.connect(try_end_crouch)
func exit() -> void:
animation_player.play("crouch",-1,-1/crouch_time,true)
player_input.crouch_end.disconnect(try_end_crouch)
func physics_update(delta: float) -> void:
if not is_multiplayer_authority():
return
if not player.is_on_floor():
transition.emit("Fall")
return
player_movement.process_movement(max_speed * weapon_system.get_speed_modifier(),acceleration,deceleration,delta)
func try_end_crouch() -> void:
if player.is_on_floor() and stand_up_area.has_overlapping_bodies() == false:
transition.emit("Stand")

View file

@ -0,0 +1 @@
uid://bv8sgx78s8hwn

View file

@ -0,0 +1,12 @@
extends State
@export var animation_player: AnimationPlayer
func on_death() -> void:
transition.emit("Death")
func enter() -> void:
animation_player.play("die")
func exit() -> void:
pass

View file

@ -0,0 +1 @@
uid://tb140f8fweug

View file

@ -0,0 +1,32 @@
extends State
@export var player: Player
@export var player_movement: PlayerMovement
@export var player_input: PlayerInput
@export var max_speed: float = 5.0
@export var acceleration: float
@export var weapon_system: WeaponSystem
@export var land_sound: MultiplayerAudio3D
func enter() -> void:
pass
func exit() -> void:
pass
func physics_update(delta: float) -> void:
if not is_multiplayer_authority():
return
if player.is_on_floor():
if player_input.compressed_states & PlayerInput.CROUCH:
transition.emit("Crouch")
elif player_input.compressed_states & PlayerInput.WALK:
transition.emit("Walk")
else:
transition.emit("Stand")
land_sound.multiplayer_play()
player.velocity += player.get_gravity() * delta
player_movement.process_movement(max_speed * weapon_system.get_speed_modifier(),acceleration,0,delta)

View file

@ -0,0 +1 @@
uid://cq4i0afwesdm3

View file

@ -0,0 +1,51 @@
extends State
@export var max_speed: float = 5.0
@export var acceleration: float = 2.0
@export var deceleration: float = 200.0
@export var player: Player
@export var player_movement: PlayerMovement
@export var player_input: PlayerInput
@export var weapon_system: WeaponSystem
@export var audio: MultiplayerAudio3D
@export var step_delay: float
@export var step_speed_curve: Curve
var step_time: float
func enter() -> void:
player_input.jumped.connect(on_jumped)
player_input.crouch_begin.connect(begin_crouch)
player_input.walk_begin.connect(begin_walk)
func exit() -> void:
player_input.jumped.disconnect(on_jumped)
player_input.crouch_begin.disconnect(begin_crouch)
player_input.walk_begin.disconnect(begin_walk)
step_time = 0
func physics_update(delta: float) -> void:
if not is_multiplayer_authority():
return
if not player.is_on_floor():
transition.emit("Fall")
return
step_time += delta * step_speed_curve.sample((player.velocity * Vector3(1,0,1)).length_squared()/(max_speed*max_speed))
if step_time >= step_delay:
step_time = 0
audio.multiplayer_play()
player_movement.process_movement(max_speed * weapon_system.get_speed_modifier(),acceleration,deceleration,delta)
func on_jumped() -> void:
if player.is_on_floor():
player_movement.jump()
transition.emit("Fall")
func begin_walk() -> void:
transition.emit("Walk")
func begin_crouch() -> void:
transition.emit("Crouch")

View file

@ -0,0 +1 @@
uid://u0e2b2mvij1k

View file

@ -0,0 +1,41 @@
extends State
@export var max_speed: float = 2.5
@export var acceleration: float = 1.0
@export var deceleration: float = 100.0
@export var JUMP_VELOCITY: float = 4.5
@export var player: Player
@export var player_movement: PlayerMovement
@export var player_input: PlayerInput
@export var weapon_system: WeaponSystem
func enter() -> void:
player_input.crouch_begin.connect(begin_crouch)
player_input.walk_end.connect(end_walk)
player_input.jumped.connect(on_jumped)
func exit() -> void:
player_input.crouch_begin.disconnect(begin_crouch)
player_input.walk_end.disconnect(end_walk)
player_input.jumped.disconnect(on_jumped)
func physics_update(delta: float) -> void:
if not is_multiplayer_authority():
return
if not player.is_on_floor():
transition.emit("Fall")
return
player_movement.process_movement(max_speed * weapon_system.get_speed_modifier(),acceleration,deceleration,delta)
func on_jumped() -> void:
if player.is_on_floor():
player_movement.jump()
transition.emit("Fall")
func end_walk() -> void:
transition.emit("Stand")
func begin_crouch() -> void:
transition.emit("Crouch")

View file

@ -0,0 +1 @@
uid://cwasvwhm5yg0o

View file

@ -0,0 +1,9 @@
extends Node
@export var material: StandardMaterial3D
@export var player: Player
@export var blue_team_texture: Texture2D
func _ready() -> void:
if player.team == Session.TEAMS.DEFENCE:
material.albedo_texture = blue_team_texture

View file

@ -0,0 +1 @@
uid://b2djbdh05pbcn

8
systems/registry.gd Normal file
View file

@ -0,0 +1,8 @@
extends Node
var weapons: Dictionary[StringName,WeaponResource]
func _ready() -> void:
for file in ResourceLoader.list_directory("res://weapons/"):
if file.ends_with(".tres"):
weapons[file.trim_suffix(".tres")] = load("res://weapons/"+ file)

1
systems/registry.gd.uid Normal file
View file

@ -0,0 +1 @@
uid://csoswr6rx8fx6

View file

@ -0,0 +1,60 @@
extends Node
class_name StateMachine
@export var current_state: State
var states: Dictionary[StringName,State] = {}
func _ready() -> void:
for child in get_children():
if child is State:
states[child.name] = child
child.transition.connect(on_transition_required)
else:
push_warning("Child of state machine is not state")
current_state.enter()
func on_transition_required(to: StringName):
if is_multiplayer_authority() == false:
return
if states.has(to) == false:
push_warning("Incorrect state request: " + to)
return
change_state(states[to])
change_state_to_name.rpc(to)
func change_state(to_state: State) -> void:
if current_state != null:
current_state.exit()
current_state = to_state
current_state.enter()
@rpc("authority","call_local","reliable")
func change_state_to_name(to_name: StringName):
if current_state != null:
current_state.exit()
current_state = states[to_name]
current_state.enter()
@rpc("authority","call_local","unreliable")
func clear_state():
if current_state == null:
return
current_state.exit()
current_state = null
func _process(delta: float) -> void:
if current_state == null:
return
current_state.update(delta)
func _input(event: InputEvent) -> void:
if current_state != null:
current_state.state_input(event)
func _physics_process(delta: float) -> void:
if current_state == null:
return
current_state.physics_update(delta)

View file

@ -0,0 +1 @@
uid://3777rkbebgjm

View file

@ -0,0 +1,17 @@
@abstract
extends Node
class_name State
@warning_ignore_start("unused_signal","unused_parameter")
signal transition(to: StringName)
@abstract func enter() -> void
@abstract func exit() -> void
func update(delta: float) -> void:
pass
func physics_update(delta: float) -> void:
pass
func state_input(event: InputEvent) -> void:
pass

View file

@ -0,0 +1 @@
uid://cmsphnymgquwq

View file

@ -0,0 +1,35 @@
extends StateMachine
class_name SubStateMachine
@export var enter_state: State
func enter() -> void:
change_state(enter_state)
func exit() -> void:
if is_multiplayer_authority():
clear_state.rpc()
func update(delta: float) -> void:
if current_state == null:
return
current_state.update(delta)
func physics_update(delta: float) -> void:
if current_state == null:
return
current_state.physics_update(delta)
func _process(_delta: float) -> void:
pass
func _physics_process(_delta: float) -> void:
pass
func _input(_event: InputEvent) -> void:
pass
func state_input(event: InputEvent) -> void:
if current_state != null:
current_state.state_input(event)

View file

@ -0,0 +1 @@
uid://btc5vyvccdqfp

View file

@ -0,0 +1,9 @@
extends RigidBody3D
class_name DroppableWeapon
const IMPULSE = 10
@export var slot: StringName
@export var weapon: WeaponSubStateMachine
@export var team: Session.TEAMS

View file

@ -0,0 +1 @@
uid://cskgqgkr7pmb0

View file

@ -0,0 +1,14 @@
extends Node
@export var starting_pistol: StringName
@export var starting_knife: StringName
@export var weapon_spawner: MultiplayerSpawner
func _ready() -> void:
deferred_ready.call_deferred()
func deferred_ready() -> void:
if is_multiplayer_authority():
weapon_spawner.spawn({"scene_file_path": starting_pistol})
weapon_spawner.spawn({"scene_file_path": starting_knife})
queue_free()

View file

@ -0,0 +1 @@
uid://doevvmbvhlig8

View file

@ -0,0 +1,9 @@
extends Resource
class_name WeaponResource
@export var cost: int
@export var preview: Texture2D
@export var dropped_scene: PackedScene
@export var weapon_system_scene: PackedScene
@export var slot: StringName

View file

@ -0,0 +1 @@
uid://bvnn2eiwqbu7t

View file

@ -0,0 +1,20 @@
@abstract
extends State
class_name WeaponState
@warning_ignore("unused_signal")
signal return_to_previous
var machine: WeaponSubStateMachine
func use_begin() -> void:
pass
func use_end() -> void:
pass
func alternate_state() -> void:
pass
# Need to clarify naming; Switch mode like firemode. For different states use
# alternate_state
func switch_mode() -> void:
pass

View file

@ -0,0 +1 @@
uid://fd4tm5wexdk

View file

@ -0,0 +1,80 @@
extends SubStateMachine
class_name WeaponSubStateMachine
@export var animation_prefix: StringName
@export var registry_entry: StringName
@export var visibility_target: StringName
@export var max_ammo: int
@export var ammo: int = -1:
set(value):
if value < 0:
ammo = 0
else:
ammo = value
ammo_updated.emit()
if ammo <= 0 and remaining_ammo <= 0:
ammo_depleted.emit()
get:
return ammo
@export var ammo_mags: int = 3
@export var remaining_ammo: int = -1:
set(value):
if value < 0:
remaining_ammo = 0
else:
remaining_ammo = value
@export var speed_modifier: float = 1.0
@export var can_be_previous: bool = true
@export var destroy_when_empty: bool = false
@export var slot: StringName
signal request_return
signal ammo_updated
signal ammo_depleted
var system: WeaponSystem
var animation_player: AnimationPlayer
var player_camera: PlayerCamera
var player: Player
func _ready() -> void:
if remaining_ammo == -1:
remaining_ammo = max_ammo * ammo_mags
if ammo == -1:
ammo = max_ammo
for child in get_children():
if child is WeaponState:
states[child.name] = child
child.machine = self
child.transition.connect(on_transition_required)
child.return_to_previous.connect(request_return.emit)
func enter() -> void:
super()
player.weapon_models[visibility_target].show()
func exit() -> void:
super()
player.weapon_models[visibility_target].hide()
@rpc("authority","call_local","reliable")
func use_begin() -> void:
if current_state != null:
current_state.use_begin()
@rpc("authority","call_local","reliable")
func use_end() -> void:
if current_state != null:
current_state.use_end()
func alternate_state() -> void:
if current_state != null:
current_state.alternate_state()
# Need to clarify naming; Switch mode like firemode. For different states use
# alternate_state
func switch_mode() -> void:
if current_state != null:
current_state.switch_mode()

View file

@ -0,0 +1 @@
uid://e6lqknfl4ngt

View file

@ -0,0 +1,186 @@
extends Node
class_name WeaponSystem
@export var animation_player: AnimationPlayer
@export var camera: PlayerCamera
@export var player: Player
@export var player_input: PlayerInput
var current_state: WeaponSubStateMachine
var last_slot: StringName
var disabled: bool
var slots: Dictionary[StringName,WeaponSubStateMachine] = {
"primary": null,
"secondary": null,
"knife": null,
"bomb": null,
"ability_first": null,
"ability_second": null,
"ability_third": null,
"ultimate": null
}
signal switched_to(state: WeaponSubStateMachine)
signal slots_updated(slots: Dictionary[StringName,WeaponSubStateMachine])
signal ammo_updated(ammo: int, remaining_ammo: int)
func _ready() -> void:
$WeaponSpawner.spawn_function = pick_up_weapon
player_input.drop.connect(drop_current)
player_input.fire_begin.connect(use_begin)
player_input.fire_end.connect(use_end)
player_input.switch_weapon.connect(switch)
player_input.alternate_state.connect(alternate_state)
player_input.switch_firemode.connect(switch_mode)
func get_speed_modifier() -> float:
if current_state == null:
return 1
return current_state.speed_modifier
func can_add(slot: StringName) -> bool:
return slots.has(slot) and slots[slot] == null
@rpc("call_local","reliable")
func add(state: WeaponSubStateMachine, slot: StringName,ignore_parent: bool = false) -> void:
if can_add(slot) == false:
return
if ignore_parent == false:
if state.get_parent() == null:
add_child(state, true)
if state.get_parent() != self:
state.get_parent().remove_child(state)
add_child(state,true)
state.ready.emit()
slots[slot] = state
state.system = self
state.animation_player = animation_player
state.player_camera = camera
state.player = player
state.request_return.connect(return_to_previous)
state.ammo_depleted.connect(check_for_empty)
state.ammo_updated.connect(on_ammo_updated)
slots_updated.emit(slots)
if current_state == null:
current_state = state
ammo_updated.emit(current_state.ammo,current_state.remaining_ammo)
state.enter.call_deferred()
@rpc("authority","call_remote","reliable")
func switch(to: StringName, exit: bool = true):
if slots.has(to) == false or slots[to] == null or slots[to] == current_state or (multiplayer.get_remote_sender_id() != 1 and is_multiplayer_authority() == false):
return
if current_state != null and exit:
current_state.exit()
if current_state.can_be_previous:
last_slot = slots.find_key(current_state)
else:
last_slot = ""
current_state = slots[to]
current_state.enter()
ammo_updated.emit(current_state.ammo,current_state.remaining_ammo)
switched_to.emit(current_state)
if is_multiplayer_authority():
switch.rpc(to,exit)
func return_to_previous(exit: bool = true):
if last_slot != "":
switch(last_slot, exit)
else:
switch("knife", exit)
func drop_current():
drop(current_state)
func drop(weapon: WeaponSubStateMachine) -> void:
if not is_multiplayer_authority():
return
if slots.find_key(weapon) == "knife":
return
var dropped_weapon: DroppableWeapon = Registry.weapons[weapon.registry_entry].dropped_scene.instantiate()
dropped_weapon.weapon.ammo = weapon.ammo
dropped_weapon.weapon.remaining_ammo = weapon.remaining_ammo
dropped_weapon.weapon.slot = weapon.slot
Session.dynamic_objects_parent.add_child(dropped_weapon)
dropped_weapon.global_position = camera.global_position
dropped_weapon.apply_central_impulse(-camera.global_basis.z * 10 + player.velocity)
$"../PickupRange".start_temp_ignore()
slots[slots.find_key(weapon)] = null
slots_updated.emit(slots)
weapon.queue_free()
return_to_previous(false)
func drop_slot(slot: StringName):
if slots.has(slot) == false or slots[slot] == null:
return
drop(slots[slot])
# Spawn function
# Data should be a dictionary with these keys:
# ammo
# remaining_ammo
# scene_file_path
func pick_up_weapon(data: Variant) -> Node:
if data.has("scene_file_path") == false:
return Node.new()
if data.has_all(["ammo","remaining_ammo"]):
var scene: WeaponSubStateMachine = load(data["scene_file_path"]).instantiate()
scene.ammo = data["ammo"]
scene.remaining_ammo = data["remaining_ammo"]
scene.slot = data["slot"]
scene.set_multiplayer_authority(get_multiplayer_authority())
add(scene,scene.slot,true)
return scene
else:
var scene: WeaponSubStateMachine = load(data["scene_file_path"]).instantiate()
scene.set_multiplayer_authority(get_multiplayer_authority())
add(scene,scene.slot,true)
return scene
func check_for_empty() -> void:
if is_multiplayer_authority() == false:
return
for child in get_children():
if child is WeaponSubStateMachine and child.ammo == 0 and child.remaining_ammo == 0 and child.destroy_when_empty:
child.queue_free()
func on_ammo_updated() -> void:
ammo_updated.emit(current_state.ammo,current_state.remaining_ammo)
func disable() -> void:
disabled = true
func _process(delta: float) -> void:
if current_state == null or disabled:
return
current_state.update(delta)
func _physics_process(delta: float) -> void:
if current_state == null or disabled:
return
current_state.physics_update(delta)
func use_begin() -> void:
current_state.use_begin.rpc()
func use_end() -> void:
current_state.use_end.rpc()
func alternate_state() -> void:
current_state.alternate_state()
func switch_mode() -> void:
current_state.switch_mode()

View file

@ -0,0 +1 @@
uid://bmecgup3kcua7