refactor
This commit is contained in:
242
scenes/entities/player/player.gd
Normal file
242
scenes/entities/player/player.gd
Normal file
@@ -0,0 +1,242 @@
|
||||
extends CharacterBody3D
|
||||
|
||||
const GRAVITY: float = 18.0
|
||||
const MOUSE_SENS: float = 0.0035
|
||||
|
||||
@export var stats_resource: PlayerStats
|
||||
|
||||
@onready var pivot: Node3D = $Pivot
|
||||
@onready var pitch_pivot: Node3D = $Pivot/PitchPivot
|
||||
@onready var camera: Camera3D = $Pivot/PitchPivot/Camera
|
||||
@onready var mesh_holder: Node3D = $MeshHolder
|
||||
@onready var collision: CollisionShape3D = $Collision
|
||||
@onready var sync: MultiplayerSynchronizer = $Synchronizer
|
||||
@onready var name_label: Label3D = $NameLabel
|
||||
|
||||
var peer_id: int = 1
|
||||
var role: int = GameState.ROLE_DAMAGE
|
||||
var look_dragging: bool = false
|
||||
var current_target: Node = null
|
||||
var dead: bool = false
|
||||
var ui_capturing: bool = false
|
||||
var build_mode: bool = false
|
||||
|
||||
@export var sync_position: Vector3 = Vector3.ZERO
|
||||
@export var sync_velocity: Vector3 = Vector3.ZERO
|
||||
@export var sync_yaw: float = 0.0
|
||||
@export var sync_role: int = GameState.ROLE_DAMAGE
|
||||
|
||||
func _enter_tree() -> void:
|
||||
peer_id = name.to_int()
|
||||
set_multiplayer_authority(peer_id)
|
||||
|
||||
func _ready() -> void:
|
||||
add_to_group("player")
|
||||
if stats_resource == null:
|
||||
stats_resource = PlayerStats.new()
|
||||
Stats.register(self, stats_resource)
|
||||
Stats.set_stat(self, "role", role)
|
||||
name_label.text = Net.player_names.get(peer_id, "P%d" % peer_id)
|
||||
EventBus.entity_died.connect(_on_entity_died_clear_target)
|
||||
if is_multiplayer_authority():
|
||||
camera.current = true
|
||||
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
|
||||
else:
|
||||
camera.current = false
|
||||
_apply_role_visual(role)
|
||||
|
||||
func _on_entity_died_clear_target(entity: Node) -> void:
|
||||
if entity == current_target:
|
||||
current_target = null
|
||||
|
||||
func _set_target(t: Node) -> void:
|
||||
current_target = t
|
||||
EventBus.target_changed.emit(self, t)
|
||||
if multiplayer.multiplayer_peer != null and not (multiplayer.multiplayer_peer is OfflineMultiplayerPeer) and not multiplayer.is_server():
|
||||
_request_target.rpc_id(1, String(t.get_path()) if t else "")
|
||||
|
||||
@rpc("any_peer", "reliable")
|
||||
func _request_target(path_str: String) -> void:
|
||||
if not multiplayer.is_server():
|
||||
return
|
||||
var t: Node = get_node_or_null(NodePath(path_str)) if path_str != "" else null
|
||||
current_target = t
|
||||
|
||||
func _exit_tree() -> void:
|
||||
Stats.deregister(self)
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
if not is_multiplayer_authority():
|
||||
return
|
||||
if ui_capturing:
|
||||
return
|
||||
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_RIGHT:
|
||||
look_dragging = event.pressed
|
||||
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED if look_dragging else Input.MOUSE_MODE_VISIBLE
|
||||
elif event is InputEventMouseMotion and look_dragging:
|
||||
pivot.rotate_y(-event.relative.x * MOUSE_SENS)
|
||||
pitch_pivot.rotate_x(-event.relative.y * MOUSE_SENS)
|
||||
pitch_pivot.rotation.x = clamp(pitch_pivot.rotation.x, -1.2, 0.2)
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if not is_multiplayer_authority():
|
||||
return
|
||||
if dead:
|
||||
return
|
||||
if build_mode:
|
||||
return
|
||||
if event.is_action_pressed("class_tank"):
|
||||
_request_role(GameState.ROLE_TANK)
|
||||
elif event.is_action_pressed("class_damage"):
|
||||
_request_role(GameState.ROLE_DAMAGE)
|
||||
elif event.is_action_pressed("class_healer"):
|
||||
_request_role(GameState.ROLE_HEALER)
|
||||
elif event.is_action_pressed("ability_1"):
|
||||
EventBus.ability_use_requested.emit(self, 0)
|
||||
elif event.is_action_pressed("ability_2"):
|
||||
EventBus.ability_use_requested.emit(self, 1)
|
||||
elif event.is_action_pressed("ability_3"):
|
||||
EventBus.ability_use_requested.emit(self, 2)
|
||||
elif event.is_action_pressed("ability_4"):
|
||||
EventBus.ability_use_requested.emit(self, 3)
|
||||
elif event.is_action_pressed("target_next"):
|
||||
var nt := _cycle_target()
|
||||
_set_target(nt)
|
||||
elif event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.pressed and not look_dragging:
|
||||
var t := _pick_target_under_mouse()
|
||||
if t:
|
||||
_set_target(t)
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if is_multiplayer_authority():
|
||||
if not dead:
|
||||
_process_local(delta)
|
||||
sync_position = global_position
|
||||
sync_velocity = velocity
|
||||
sync_yaw = pivot.rotation.y
|
||||
sync_role = role
|
||||
else:
|
||||
global_position = global_position.lerp(sync_position, clamp(delta * 20.0, 0.0, 1.0))
|
||||
pivot.rotation.y = lerp_angle(pivot.rotation.y, sync_yaw, clamp(delta * 20.0, 0.0, 1.0))
|
||||
if sync_role != role:
|
||||
role = sync_role
|
||||
_apply_role_visual(role)
|
||||
|
||||
func _process_local(delta: float) -> void:
|
||||
if not is_on_floor():
|
||||
velocity.y -= GRAVITY * delta
|
||||
var move_dir: Vector2 = Input.get_vector("move_left", "move_right", "move_forward", "move_back") if not ui_capturing else Vector2.ZERO
|
||||
var speed: float = float(Stats.get_stat(self, "speed", 5.0))
|
||||
var basis_y := Basis(Vector3.UP, pivot.rotation.y)
|
||||
var direction := basis_y * Vector3(move_dir.x, 0.0, move_dir.y)
|
||||
if direction.length() > 0.01:
|
||||
velocity.x = direction.x * speed
|
||||
velocity.z = direction.z * speed
|
||||
var look_dir := Vector3(velocity.x, 0.0, velocity.z).normalized()
|
||||
var target_basis := Basis.looking_at(look_dir, Vector3.UP)
|
||||
mesh_holder.basis = mesh_holder.basis.slerp(target_basis, clamp(delta * 12.0, 0.0, 1.0))
|
||||
else:
|
||||
velocity.x = move_toward(velocity.x, 0.0, speed * 6.0 * delta)
|
||||
velocity.z = move_toward(velocity.z, 0.0, speed * 6.0 * delta)
|
||||
if Input.is_action_just_pressed("jump") and is_on_floor() and not ui_capturing:
|
||||
velocity.y = float(Stats.get_stat(self, "jump_velocity", 4.5))
|
||||
move_and_slide()
|
||||
|
||||
func _request_role(new_role: int) -> void:
|
||||
_set_role.rpc_id(1, new_role)
|
||||
|
||||
@rpc("any_peer", "reliable", "call_local")
|
||||
func _set_role(new_role: int) -> void:
|
||||
if not multiplayer.is_server():
|
||||
return
|
||||
if new_role == role:
|
||||
return
|
||||
role = new_role
|
||||
Stats.set_stat(self, "role", role)
|
||||
_apply_role.rpc(role)
|
||||
|
||||
@rpc("any_peer", "reliable", "call_local")
|
||||
func _apply_role(new_role: int) -> void:
|
||||
if multiplayer.multiplayer_peer != null and not (multiplayer.multiplayer_peer is OfflineMultiplayerPeer) and multiplayer.get_remote_sender_id() != 0 and multiplayer.get_remote_sender_id() != 1:
|
||||
return
|
||||
role = new_role
|
||||
if Stats.has(self):
|
||||
Stats.set_stat(self, "role", role)
|
||||
_apply_role_visual(role)
|
||||
EventBus.role_changed.emit(self, role)
|
||||
|
||||
func _apply_role_visual(r: int) -> void:
|
||||
var mesh: MeshInstance3D = mesh_holder.get_node("Mesh")
|
||||
var mat: StandardMaterial3D = mesh.get_active_material(0).duplicate() if mesh.get_active_material(0) else StandardMaterial3D.new()
|
||||
match r:
|
||||
GameState.ROLE_TANK:
|
||||
mat.albedo_color = Color(0.3, 0.5, 0.95)
|
||||
GameState.ROLE_DAMAGE:
|
||||
mat.albedo_color = Color(0.95, 0.3, 0.3)
|
||||
GameState.ROLE_HEALER:
|
||||
mat.albedo_color = Color(0.4, 0.85, 0.4)
|
||||
mesh.material_override = mat
|
||||
|
||||
@rpc("any_peer", "reliable", "call_local")
|
||||
func set_dead(value: bool) -> void:
|
||||
if multiplayer.multiplayer_peer != null and not (multiplayer.multiplayer_peer is OfflineMultiplayerPeer) and multiplayer.get_remote_sender_id() != 0 and multiplayer.get_remote_sender_id() != 1:
|
||||
return
|
||||
dead = value
|
||||
visible = not value
|
||||
collision.disabled = value
|
||||
if value:
|
||||
velocity = Vector3.ZERO
|
||||
|
||||
@rpc("any_peer", "reliable", "call_local")
|
||||
func teleport_to(pos: Vector3) -> void:
|
||||
if multiplayer.multiplayer_peer != null and not (multiplayer.multiplayer_peer is OfflineMultiplayerPeer) and multiplayer.get_remote_sender_id() != 0 and multiplayer.get_remote_sender_id() != 1:
|
||||
return
|
||||
global_position = pos
|
||||
sync_position = pos
|
||||
|
||||
func set_ui_capturing(v: bool) -> void:
|
||||
ui_capturing = v
|
||||
|
||||
func set_build_mode(v: bool) -> void:
|
||||
build_mode = v
|
||||
|
||||
func _cycle_target() -> Node:
|
||||
if current_target != null and not is_instance_valid(current_target):
|
||||
current_target = null
|
||||
var candidates: Array = []
|
||||
for n in get_tree().get_nodes_in_group("enemies"):
|
||||
if is_instance_valid(n):
|
||||
candidates.append(n)
|
||||
for n in get_tree().get_nodes_in_group("portals"):
|
||||
if is_instance_valid(n):
|
||||
candidates.append(n)
|
||||
for n in get_tree().get_nodes_in_group("gates"):
|
||||
if is_instance_valid(n):
|
||||
candidates.append(n)
|
||||
if candidates.is_empty():
|
||||
current_target = null
|
||||
return null
|
||||
candidates.sort_custom(func(a, b):
|
||||
return (a as Node3D).global_position.distance_to(global_position) < (b as Node3D).global_position.distance_to(global_position))
|
||||
if current_target == null or not current_target in candidates:
|
||||
current_target = candidates[0]
|
||||
else:
|
||||
var idx: int = candidates.find(current_target)
|
||||
current_target = candidates[(idx + 1) % candidates.size()]
|
||||
return current_target
|
||||
|
||||
func _pick_target_under_mouse() -> Node:
|
||||
var mouse := get_viewport().get_mouse_position()
|
||||
var from := camera.project_ray_origin(mouse)
|
||||
var to := from + camera.project_ray_normal(mouse) * 100.0
|
||||
var space := get_world_3d().direct_space_state
|
||||
var query := PhysicsRayQueryParameters3D.create(from, to)
|
||||
query.collision_mask = 0xFFFFFFFF
|
||||
query.exclude = [self]
|
||||
var hit := space.intersect_ray(query)
|
||||
if hit.is_empty():
|
||||
return null
|
||||
var n: Node = hit.collider
|
||||
while n != null and not (n.is_in_group("enemies") or n.is_in_group("portals") or n.is_in_group("gates") or n.is_in_group("npc")):
|
||||
n = n.get_parent()
|
||||
return n
|
||||
Reference in New Issue
Block a user