last init
This commit is contained in:
30
scripts/player/camera.gd
Normal file
30
scripts/player/camera.gd
Normal file
@@ -0,0 +1,30 @@
|
||||
extends Node3D
|
||||
|
||||
const SENSITIVITY := 0.003
|
||||
const PITCH_MIN := -80.0
|
||||
const PITCH_MAX := 80.0
|
||||
|
||||
var pitch := -0.3
|
||||
var camera_yaw := 0.0
|
||||
var player_yaw := -2.356
|
||||
|
||||
func _ready() -> void:
|
||||
get_parent().rotation.y = player_yaw
|
||||
rotation = Vector3(pitch, camera_yaw, 0)
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if event is InputEventMouseMotion:
|
||||
var lmb := Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT)
|
||||
var rmb := Input.is_mouse_button_pressed(MOUSE_BUTTON_RIGHT)
|
||||
|
||||
if lmb or rmb:
|
||||
pitch -= event.relative.y * SENSITIVITY
|
||||
pitch = clamp(pitch, deg_to_rad(PITCH_MIN), deg_to_rad(PITCH_MAX))
|
||||
|
||||
if rmb:
|
||||
player_yaw -= event.relative.x * SENSITIVITY
|
||||
get_parent().rotation.y = player_yaw
|
||||
else:
|
||||
camera_yaw -= event.relative.x * SENSITIVITY
|
||||
|
||||
rotation = Vector3(pitch, camera_yaw, 0)
|
||||
1
scripts/player/camera.gd.uid
Normal file
1
scripts/player/camera.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cohjyjge1kqxb
|
||||
35
scripts/player/combat.gd
Normal file
35
scripts/player/combat.gd
Normal file
@@ -0,0 +1,35 @@
|
||||
extends Node
|
||||
|
||||
@onready var player: CharacterBody3D = get_parent()
|
||||
@onready var targeting: Node = get_parent().get_node("Targeting")
|
||||
@onready var player_class: Node = get_parent().get_node("PlayerClass")
|
||||
|
||||
var abilities: Array = []
|
||||
|
||||
func _ready() -> void:
|
||||
_load_abilities()
|
||||
EventBus.class_changed.connect(_on_class_changed)
|
||||
|
||||
func _load_abilities() -> void:
|
||||
var ability_set: AbilitySet = player_class.get_ability_set()
|
||||
if ability_set:
|
||||
abilities = ability_set.abilities
|
||||
else:
|
||||
abilities = []
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
for i in range(min(abilities.size(), 5)):
|
||||
if event.is_action_pressed("ability_%s" % (i + 1)) and abilities[i]:
|
||||
if abilities[i].type == Ability.Type.PASSIVE:
|
||||
return
|
||||
abilities[i].execute(player, targeting)
|
||||
return
|
||||
|
||||
func apply_passive(base_damage: float) -> float:
|
||||
for ability in abilities:
|
||||
if ability and ability.type == Ability.Type.PASSIVE:
|
||||
return base_damage * (1.0 + ability.damage / 100.0)
|
||||
return base_damage
|
||||
|
||||
func _on_class_changed(_player: Node, _class_type: int) -> void:
|
||||
_load_abilities()
|
||||
1
scripts/player/combat.gd.uid
Normal file
1
scripts/player/combat.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://d15til6fsxw5b
|
||||
43
scripts/player/hud.gd
Normal file
43
scripts/player/hud.gd
Normal file
@@ -0,0 +1,43 @@
|
||||
extends CanvasLayer
|
||||
|
||||
@onready var health_bar: ProgressBar = $HealthBar
|
||||
@onready var shield_bar: ProgressBar = $ShieldBar
|
||||
@onready var respawn_label: Label = $RespawnTimer
|
||||
@onready var class_icon: Label = $AbilityBar/ClassIcon/Label
|
||||
|
||||
var player_node: Node = null
|
||||
|
||||
func _ready() -> void:
|
||||
respawn_label.visible = false
|
||||
EventBus.health_changed.connect(_on_health_changed)
|
||||
EventBus.shield_changed.connect(_on_shield_changed)
|
||||
EventBus.entity_died.connect(_on_entity_died)
|
||||
EventBus.player_respawned.connect(_on_player_respawned)
|
||||
EventBus.class_changed.connect(_on_class_changed)
|
||||
EventBus.respawn_tick.connect(_on_respawn_tick)
|
||||
|
||||
func _on_health_changed(entity: Node, current: float, max_val: float) -> void:
|
||||
if entity.name == "Player":
|
||||
health_bar.max_value = max_val
|
||||
health_bar.value = current
|
||||
|
||||
func _on_shield_changed(entity: Node, current: float, max_val: float) -> void:
|
||||
if entity.name == "Player":
|
||||
shield_bar.max_value = max_val
|
||||
shield_bar.value = current
|
||||
|
||||
func _on_entity_died(entity: Node) -> void:
|
||||
if entity.name == "Player":
|
||||
respawn_label.visible = true
|
||||
|
||||
func _on_player_respawned(_player: Node) -> void:
|
||||
respawn_label.visible = false
|
||||
|
||||
func _on_respawn_tick(timer: float) -> void:
|
||||
respawn_label.text = str(ceil(timer))
|
||||
|
||||
func _on_class_changed(_player: Node, class_type: int) -> void:
|
||||
match class_type:
|
||||
0: class_icon.text = "T"
|
||||
1: class_icon.text = "D"
|
||||
2: class_icon.text = "H"
|
||||
1
scripts/player/hud.gd.uid
Normal file
1
scripts/player/hud.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://c4jhr8k4uwoy7
|
||||
35
scripts/player/movement.gd
Normal file
35
scripts/player/movement.gd
Normal file
@@ -0,0 +1,35 @@
|
||||
extends Node
|
||||
|
||||
const SPEED := 5.0
|
||||
const JUMP_VELOCITY := 4.5
|
||||
|
||||
var gravity: float = ProjectSettings.get_setting("physics/3d/default_gravity")
|
||||
|
||||
@onready var player: CharacterBody3D = get_parent()
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if not player.is_on_floor():
|
||||
player.velocity.y -= gravity * delta
|
||||
|
||||
if Input.is_action_just_pressed("ui_accept") and player.is_on_floor():
|
||||
player.velocity.y = JUMP_VELOCITY
|
||||
|
||||
var input_dir := Input.get_vector("move_left", "move_right", "move_forward", "move_back")
|
||||
var camera_pivot := player.get_node("CameraPivot") as Node3D
|
||||
var forward := -camera_pivot.global_transform.basis.z
|
||||
forward.y = 0
|
||||
forward = forward.normalized()
|
||||
var right := camera_pivot.global_transform.basis.x
|
||||
right.y = 0
|
||||
right = right.normalized()
|
||||
|
||||
var direction := (forward * -input_dir.y + right * input_dir.x).normalized()
|
||||
|
||||
if direction:
|
||||
player.velocity.x = direction.x * SPEED
|
||||
player.velocity.z = direction.z * SPEED
|
||||
else:
|
||||
player.velocity.x = move_toward(player.velocity.x, 0, SPEED)
|
||||
player.velocity.z = move_toward(player.velocity.z, 0, SPEED)
|
||||
|
||||
player.move_and_slide()
|
||||
1
scripts/player/movement.gd.uid
Normal file
1
scripts/player/movement.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://fg87dh8fbc8
|
||||
1
scripts/player/player.gd
Normal file
1
scripts/player/player.gd
Normal file
@@ -0,0 +1 @@
|
||||
extends CharacterBody3D
|
||||
1
scripts/player/player.gd.uid
Normal file
1
scripts/player/player.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bfpt2p7uucfyb
|
||||
37
scripts/player/player_class.gd
Normal file
37
scripts/player/player_class.gd
Normal file
@@ -0,0 +1,37 @@
|
||||
extends Node
|
||||
|
||||
enum PlayerClass { TANK, DAMAGE, HEALER }
|
||||
|
||||
var current_class: int = PlayerClass.DAMAGE
|
||||
|
||||
@export var tank_set: AbilitySet
|
||||
@export var damage_set: AbilitySet
|
||||
@export var healer_set: AbilitySet
|
||||
|
||||
@onready var player: CharacterBody3D = get_parent()
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if event.is_action_pressed("class_tank"):
|
||||
set_class(PlayerClass.TANK)
|
||||
elif event.is_action_pressed("class_damage"):
|
||||
set_class(PlayerClass.DAMAGE)
|
||||
elif event.is_action_pressed("class_healer"):
|
||||
set_class(PlayerClass.HEALER)
|
||||
|
||||
func set_class(new_class: int) -> void:
|
||||
current_class = new_class
|
||||
EventBus.class_changed.emit(player, current_class)
|
||||
|
||||
func get_class_icon() -> String:
|
||||
match current_class:
|
||||
PlayerClass.TANK: return "T"
|
||||
PlayerClass.DAMAGE: return "D"
|
||||
PlayerClass.HEALER: return "H"
|
||||
return ""
|
||||
|
||||
func get_ability_set() -> AbilitySet:
|
||||
match current_class:
|
||||
PlayerClass.TANK: return tank_set
|
||||
PlayerClass.DAMAGE: return damage_set
|
||||
PlayerClass.HEALER: return healer_set
|
||||
return damage_set
|
||||
1
scripts/player/player_class.gd.uid
Normal file
1
scripts/player/player_class.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://rus4umqvvqq4
|
||||
46
scripts/player/respawn.gd
Normal file
46
scripts/player/respawn.gd
Normal file
@@ -0,0 +1,46 @@
|
||||
extends Node
|
||||
|
||||
const RESPAWN_TIME := 3.0
|
||||
var respawn_timer := 0.0
|
||||
var is_dead := false
|
||||
var spawn_position: Vector3
|
||||
|
||||
@onready var player: CharacterBody3D = get_parent()
|
||||
|
||||
func _ready() -> void:
|
||||
spawn_position = player.global_position
|
||||
EventBus.entity_died.connect(_on_entity_died)
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if is_dead:
|
||||
respawn_timer -= delta
|
||||
EventBus.respawn_tick.emit(respawn_timer)
|
||||
if respawn_timer <= 0:
|
||||
_respawn()
|
||||
|
||||
func _on_entity_died(entity: Node) -> void:
|
||||
if entity == player and not is_dead:
|
||||
is_dead = true
|
||||
respawn_timer = RESPAWN_TIME
|
||||
player.velocity = Vector3.ZERO
|
||||
player.get_node("Mesh").visible = false
|
||||
player.get_node("CollisionShape3D").disabled = true
|
||||
player.get_node("Movement").set_physics_process(false)
|
||||
player.get_node("Combat").set_process_unhandled_input(false)
|
||||
player.get_node("Targeting").set_process_unhandled_input(false)
|
||||
|
||||
func _respawn() -> void:
|
||||
is_dead = false
|
||||
player.global_position = spawn_position
|
||||
player.get_node("Mesh").visible = true
|
||||
player.get_node("CollisionShape3D").disabled = false
|
||||
player.get_node("Movement").set_physics_process(true)
|
||||
player.get_node("Combat").set_process_unhandled_input(true)
|
||||
player.get_node("Targeting").set_process_unhandled_input(true)
|
||||
var health_node: Node = player.get_node("Health")
|
||||
var shield_node: Node = player.get_node("Shield")
|
||||
health_node.current_health = health_node.max_health
|
||||
shield_node.current_shield = shield_node.max_shield
|
||||
EventBus.health_changed.emit(player, health_node.current_health, health_node.max_health)
|
||||
EventBus.shield_changed.emit(player, shield_node.current_shield, shield_node.max_shield)
|
||||
EventBus.player_respawned.emit(player)
|
||||
1
scripts/player/respawn.gd.uid
Normal file
1
scripts/player/respawn.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dw3dtax5bx0of
|
||||
113
scripts/player/targeting.gd
Normal file
113
scripts/player/targeting.gd
Normal file
@@ -0,0 +1,113 @@
|
||||
extends Node
|
||||
|
||||
const TARGET_RANGE := 20.0
|
||||
const COMBAT_TIMEOUT := 3.0
|
||||
|
||||
var current_target: Node3D = null
|
||||
var mouse_press_pos: Vector2 = Vector2.ZERO
|
||||
var in_combat := false
|
||||
var combat_timer := 0.0
|
||||
|
||||
@onready var player: CharacterBody3D = get_parent()
|
||||
@onready var camera: Camera3D = get_parent().get_node("CameraPivot/Camera3D")
|
||||
|
||||
func _ready() -> void:
|
||||
EventBus.damage_dealt.connect(_on_damage_dealt)
|
||||
EventBus.entity_died.connect(_on_entity_died)
|
||||
EventBus.enemy_engaged.connect(_on_enemy_engaged)
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if in_combat:
|
||||
combat_timer -= delta
|
||||
if combat_timer <= 0:
|
||||
in_combat = false
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT:
|
||||
if event.pressed:
|
||||
mouse_press_pos = event.position
|
||||
else:
|
||||
var drag: float = event.position.distance_to(mouse_press_pos)
|
||||
if drag < 5.0:
|
||||
_try_target_under_mouse(event.position)
|
||||
if event.is_action_pressed("target_next"):
|
||||
_cycle_target()
|
||||
|
||||
func _try_target_under_mouse(mouse_pos: Vector2) -> void:
|
||||
var from := camera.project_ray_origin(mouse_pos)
|
||||
var to := from + camera.project_ray_normal(mouse_pos) * TARGET_RANGE
|
||||
var space := player.get_world_3d().direct_space_state
|
||||
var query := PhysicsRayQueryParameters3D.create(from, to)
|
||||
query.collision_mask = 4
|
||||
query.collide_with_areas = true
|
||||
query.collide_with_bodies = false
|
||||
var result := space.intersect_ray(query)
|
||||
if result:
|
||||
var hit_target := result.collider.get_parent() as Node3D
|
||||
set_target(hit_target)
|
||||
else:
|
||||
set_target(null)
|
||||
|
||||
func _cycle_target() -> void:
|
||||
var enemies := get_tree().get_nodes_in_group("enemies")
|
||||
if enemies.is_empty():
|
||||
set_target(null)
|
||||
return
|
||||
if current_target == null or current_target not in enemies:
|
||||
set_target(enemies[0])
|
||||
return
|
||||
var idx := enemies.find(current_target)
|
||||
var next_idx := (idx + 1) % enemies.size()
|
||||
set_target(enemies[next_idx])
|
||||
|
||||
func set_target(target: Node3D) -> void:
|
||||
current_target = target
|
||||
EventBus.target_changed.emit(player, target)
|
||||
|
||||
func _on_enemy_engaged(_enemy: Node, target: Node) -> void:
|
||||
if target == player:
|
||||
combat_timer = COMBAT_TIMEOUT
|
||||
if not in_combat:
|
||||
in_combat = true
|
||||
if current_target == null:
|
||||
_target_nearest()
|
||||
|
||||
func _on_damage_dealt(_attacker: Node, target: Node, _amount: float) -> void:
|
||||
if target == player:
|
||||
combat_timer = COMBAT_TIMEOUT
|
||||
if not in_combat:
|
||||
in_combat = true
|
||||
if current_target == null:
|
||||
_target_nearest()
|
||||
|
||||
func _on_entity_died(entity: Node) -> void:
|
||||
if entity == current_target:
|
||||
set_target(null)
|
||||
if in_combat:
|
||||
_target_nearest_except(entity)
|
||||
|
||||
func _target_nearest_except(exclude: Node = null) -> void:
|
||||
var enemies := get_tree().get_nodes_in_group("enemies")
|
||||
var nearest: Node3D = null
|
||||
var nearest_dist: float = INF
|
||||
for enemy in enemies:
|
||||
if is_instance_valid(enemy) and enemy != exclude:
|
||||
var dist: float = player.global_position.distance_to(enemy.global_position)
|
||||
if dist < nearest_dist:
|
||||
nearest_dist = dist
|
||||
nearest = enemy
|
||||
if nearest:
|
||||
set_target(nearest)
|
||||
|
||||
func _target_nearest() -> void:
|
||||
var enemies := get_tree().get_nodes_in_group("enemies")
|
||||
var nearest: Node3D = null
|
||||
var nearest_dist: float = INF
|
||||
for enemy in enemies:
|
||||
if is_instance_valid(enemy):
|
||||
var dist: float = player.global_position.distance_to(enemy.global_position)
|
||||
if dist < nearest_dist:
|
||||
nearest_dist = dist
|
||||
nearest = enemy
|
||||
if nearest:
|
||||
set_target(nearest)
|
||||
1
scripts/player/targeting.gd.uid
Normal file
1
scripts/player/targeting.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://b05nkuryipwny
|
||||
Reference in New Issue
Block a user