feat: add player sword combat, dash, and hostile wolf/raider visuals
This commit is contained in:
@@ -67,6 +67,24 @@ roughness = 0.82
|
||||
material = SubResource("Material_accent")
|
||||
size = Vector3(0.48, 0.1, 0.38)
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="Material_sword"]
|
||||
albedo_color = Color(0.72, 0.74, 0.78, 1)
|
||||
roughness = 0.32
|
||||
|
||||
[sub_resource type="BoxMesh" id="Mesh_blade"]
|
||||
material = SubResource("Material_sword")
|
||||
size = Vector3(0.055, 0.78, 0.11)
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="Material_grip"]
|
||||
albedo_color = Color(0.3, 0.2, 0.12, 1)
|
||||
roughness = 0.6
|
||||
|
||||
[sub_resource type="CylinderMesh" id="Mesh_grip"]
|
||||
material = SubResource("Material_grip")
|
||||
top_radius = 0.045
|
||||
bottom_radius = 0.045
|
||||
height = 0.16
|
||||
|
||||
[sub_resource type="SphereMesh" id="Mesh_satchel"]
|
||||
material = SubResource("Material_accent")
|
||||
radius = 0.22
|
||||
@@ -128,3 +146,15 @@ mesh = SubResource("Mesh_foot")
|
||||
[node name="Satchel" type="MeshInstance3D" parent="."]
|
||||
position = Vector3(-0.43, 0.87, -0.04)
|
||||
mesh = SubResource("Mesh_satchel")
|
||||
|
||||
[node name="Sword" type="Node3D" parent="."]
|
||||
position = Vector3(0.52, 1.22, 0.12)
|
||||
rotation = Vector3(-0.35, 0, 0)
|
||||
|
||||
[node name="Blade" type="MeshInstance3D" parent="Sword"]
|
||||
position = Vector3(0, 0.42, 0)
|
||||
mesh = SubResource("Mesh_blade")
|
||||
|
||||
[node name="Grip" type="MeshInstance3D" parent="Sword"]
|
||||
position = Vector3(0, -0.05, 0)
|
||||
mesh = SubResource("Mesh_grip")
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
class_name PlayerCombatController
|
||||
extends Node3D
|
||||
|
||||
const SLASH_REST_ANGLE := Vector3(-0.35, 0.0, 0.0)
|
||||
|
||||
@export var player: CharacterBody3D
|
||||
@export var simulation_manager: Node
|
||||
@export var combat_presentation: Node
|
||||
@export var sword: Node3D
|
||||
|
||||
@export_range(0.5, 4.0, 0.1) var attack_range := 2.6
|
||||
@export_range(30.0, 160.0, 5.0) var attack_angle := 90.0
|
||||
@export var dash_speed := 22.0
|
||||
@export var dash_duration := 0.18
|
||||
@export var dash_cooldown_seconds := 1.1
|
||||
|
||||
var attack_cooldown_seconds := 0.55
|
||||
var attack_timer := 0.0
|
||||
var dash_timer := 0.0
|
||||
var dashing := false
|
||||
var dash_velocity := Vector3.ZERO
|
||||
var _swing_tween: Tween
|
||||
var _references_resolved := false
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
if sword == null:
|
||||
return
|
||||
sword.rotation = SLASH_REST_ANGLE
|
||||
var definition := SimulationItems.get_weapon(SimulationIds.ITEM_SWORD)
|
||||
if definition != null:
|
||||
attack_cooldown_seconds = definition.attack_cooldown
|
||||
attack_range = maxf(definition.reach + 0.2, attack_range)
|
||||
|
||||
|
||||
func _resolve_references() -> void:
|
||||
if simulation_manager == null:
|
||||
simulation_manager = get_tree().get_first_node_in_group("simulation_manager")
|
||||
if combat_presentation == null:
|
||||
combat_presentation = get_tree().get_first_node_in_group("combat_presentation")
|
||||
_references_resolved = true
|
||||
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if not _references_resolved:
|
||||
_resolve_references()
|
||||
attack_timer = maxf(attack_timer - delta, 0.0)
|
||||
dash_timer = maxf(dash_timer - delta, 0.0)
|
||||
if dashing:
|
||||
player.velocity = dash_velocity
|
||||
if dash_timer <= 0.0:
|
||||
dashing = false
|
||||
player.move_and_slide()
|
||||
return
|
||||
if Input.is_action_just_pressed("attack") and attack_timer <= 0.0:
|
||||
_perform_attack()
|
||||
if Input.is_action_just_pressed("dash") and dash_timer <= 0.0:
|
||||
_perform_dash()
|
||||
|
||||
|
||||
func _perform_attack() -> void:
|
||||
attack_timer = attack_cooldown_seconds
|
||||
_play_swing()
|
||||
var facing := -player.global_transform.basis.z
|
||||
var hostile: CombatantStateRecord = null
|
||||
if combat_presentation != null and combat_presentation.has_method("find_hostile_in_arc"):
|
||||
hostile = combat_presentation.find_hostile_in_arc(
|
||||
player.global_position, facing, attack_range, attack_angle
|
||||
)
|
||||
if hostile == null:
|
||||
return
|
||||
if simulation_manager != null and simulation_manager.has_method("player_attack"):
|
||||
var damage: float = simulation_manager.player_attack(hostile.get_combatant_id())
|
||||
if damage > 0.0:
|
||||
_emit_combat_feedback("Struck %s" % hostile.get_display_name(), damage)
|
||||
|
||||
|
||||
func _perform_dash() -> void:
|
||||
dash_timer = dash_duration
|
||||
dashing = true
|
||||
var facing := -player.global_transform.basis.z
|
||||
facing.y = 0.0
|
||||
facing = facing.normalized()
|
||||
dash_velocity = facing * dash_speed
|
||||
dash_velocity.y = 0.0
|
||||
|
||||
|
||||
func _play_swing() -> void:
|
||||
if sword == null:
|
||||
return
|
||||
if _swing_tween != null and _swing_tween.is_valid():
|
||||
_swing_tween.kill()
|
||||
sword.rotation = SLASH_REST_ANGLE
|
||||
_swing_tween = create_tween()
|
||||
_swing_tween.set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT)
|
||||
_swing_tween.tween_property(sword, "rotation", SLASH_REST_ANGLE + Vector3(-1.6, 0.6, 0.0), 0.12)
|
||||
_swing_tween.tween_property(sword, "rotation", SLASH_REST_ANGLE, 0.2)
|
||||
|
||||
|
||||
func _emit_combat_feedback(heading: String, damage: float) -> void:
|
||||
if player != null and player.has_signal("interaction_feedback"):
|
||||
player.interaction_feedback.emit(heading, "Dealt %.0f damage." % damage, true)
|
||||
|
||||
|
||||
func is_attacking() -> bool:
|
||||
return attack_timer > 0.0
|
||||
|
||||
|
||||
func is_dashing() -> bool:
|
||||
return dashing
|
||||
@@ -0,0 +1 @@
|
||||
uid://bfysko6pf0tnt
|
||||
Reference in New Issue
Block a user