2021-09-22 18:04:03 +02:00
|
|
|
extends KinematicBody
|
2021-09-08 18:36:41 +02:00
|
|
|
class_name Character
|
|
|
|
|
|
|
|
enum States {
|
|
|
|
Alive,
|
|
|
|
Dead
|
|
|
|
}
|
|
|
|
|
2021-09-22 18:04:03 +02:00
|
|
|
enum GroundType {
|
|
|
|
DIRT,
|
|
|
|
WATER,
|
2021-10-01 15:50:16 +02:00
|
|
|
WOOD,
|
|
|
|
METAL,
|
|
|
|
SAND,
|
|
|
|
GRASS
|
2021-09-22 18:04:03 +02:00
|
|
|
}
|
|
|
|
|
2021-10-01 15:50:16 +02:00
|
|
|
signal health_changed
|
|
|
|
signal died
|
|
|
|
signal changed_weapon
|
|
|
|
signal fired(projectile)
|
|
|
|
signal pickup_weapon
|
|
|
|
signal jumped
|
|
|
|
|
2021-09-22 18:04:03 +02:00
|
|
|
var UI: Control
|
|
|
|
export var max_weapons: int = 5
|
2021-09-23 21:19:11 +02:00
|
|
|
var weapons: Array = []
|
|
|
|
var current_weapon: Weapon
|
2021-09-22 18:04:03 +02:00
|
|
|
var is_in_water: bool = false
|
2021-09-23 21:19:11 +02:00
|
|
|
export var weapon_refpt_right: NodePath
|
|
|
|
export var weapon_refpt_left: NodePath
|
|
|
|
export var water_splash_sound: AudioStreamSample
|
|
|
|
var animation_blend_vector = Vector2.ZERO
|
|
|
|
export var animation_tree_path: NodePath
|
|
|
|
var animation_tree: AnimationTree
|
|
|
|
|
|
|
|
|
|
|
|
# Move
|
|
|
|
var velocity := Vector3()
|
|
|
|
var move_axis: Vector3 = Vector3.ZERO
|
|
|
|
var can_sprint := true
|
|
|
|
var sprinting := false
|
|
|
|
const FLOOR_NORMAL := Vector3.UP
|
|
|
|
export var sprint_speed := 16.0
|
|
|
|
export var acceleration := 8
|
|
|
|
export var deacceleration := 100
|
2021-10-01 15:50:16 +02:00
|
|
|
export(float, 0.0, 1.0, 0.00001) var air_control := 0.001
|
|
|
|
export var can_jump = true
|
2021-09-23 21:19:11 +02:00
|
|
|
export var jump_height := 5
|
2021-10-01 15:50:16 +02:00
|
|
|
export var floor_max_angle := 90.0
|
2021-09-23 21:19:11 +02:00
|
|
|
export var gravity: float = 40.0
|
|
|
|
export var walk_speed: float = 2.0
|
2021-10-01 15:50:16 +02:00
|
|
|
export var ground_friction: float = 3.0
|
2021-09-22 18:04:03 +02:00
|
|
|
|
|
|
|
var ground_type = GroundType.DIRT
|
|
|
|
|
2021-10-01 15:50:16 +02:00
|
|
|
var footstep_sounds = {}
|
2021-09-22 18:04:03 +02:00
|
|
|
|
|
|
|
export var health = 100
|
|
|
|
var state = States.Alive
|
|
|
|
|
|
|
|
func has_weapon(weapon: Weapon) -> bool:
|
|
|
|
if get_similar_weapon(weapon):
|
|
|
|
return true
|
|
|
|
else:
|
|
|
|
return false
|
|
|
|
|
|
|
|
func get_similar_weapon(weapon: Weapon) -> Weapon:
|
|
|
|
for weap in weapons:
|
|
|
|
if weap.name == weapon.name:
|
|
|
|
return weap
|
|
|
|
return null
|
2021-09-08 18:36:41 +02:00
|
|
|
|
2021-09-22 18:04:03 +02:00
|
|
|
func can_pickup_weapon(weapon: Weapon) -> bool:
|
|
|
|
return has_weapon(weapon) or len(weapons) < max_weapons
|
2021-09-08 18:36:41 +02:00
|
|
|
|
2021-09-23 21:19:11 +02:00
|
|
|
func pickup_weapon(weapon: Weapon) -> Weapon:
|
2021-09-22 18:04:03 +02:00
|
|
|
var similar = get_similar_weapon(weapon)
|
|
|
|
if similar:
|
|
|
|
similar.current_ammo = clamp(similar.current_ammo + weapon.current_ammo, 0, weapon.max_ammo)
|
2021-09-23 21:19:11 +02:00
|
|
|
return similar
|
2021-09-22 18:04:03 +02:00
|
|
|
else:
|
2021-09-23 21:19:11 +02:00
|
|
|
# add new weapon
|
2021-09-22 18:04:03 +02:00
|
|
|
weapons.append(weapon)
|
2021-09-23 21:19:11 +02:00
|
|
|
weapon.holder = self
|
|
|
|
weapon.visible = false
|
|
|
|
if weapon.held_hand == Weapon.WeaponHeld.Left:
|
|
|
|
get_node(weapon_refpt_left).add_child(weapon)
|
|
|
|
else:
|
|
|
|
get_node(weapon_refpt_right).add_child(weapon)
|
|
|
|
return weapon
|
2021-09-22 18:04:03 +02:00
|
|
|
|
|
|
|
func take_damage(hitpts: float):
|
|
|
|
health -= hitpts
|
2021-10-01 15:50:16 +02:00
|
|
|
emit_signal("health_changed")
|
2021-09-22 18:04:03 +02:00
|
|
|
if health <= 0:
|
|
|
|
die()
|
|
|
|
|
2021-09-23 21:19:11 +02:00
|
|
|
func get_current_weapon():
|
|
|
|
return current_weapon
|
|
|
|
|
2021-09-22 18:04:03 +02:00
|
|
|
func die():
|
|
|
|
state = States.Dead
|
2021-10-01 15:50:16 +02:00
|
|
|
emit_signal("died")
|
2021-09-23 21:19:11 +02:00
|
|
|
|
|
|
|
func set_weapon_slot(slot: int):
|
|
|
|
if current_weapon:
|
|
|
|
current_weapon.visible = false
|
|
|
|
var newweapon = weapons[slot]
|
|
|
|
current_weapon = newweapon
|
|
|
|
current_weapon.visible = true
|
|
|
|
current_weapon.play_switch_sound()
|
2021-09-08 18:36:41 +02:00
|
|
|
|
2021-09-22 18:04:03 +02:00
|
|
|
func play_footstep():
|
2021-10-01 15:50:16 +02:00
|
|
|
if ground_type in footstep_sounds:
|
|
|
|
var sound = footstep_sounds[ground_type][randi() % len(footstep_sounds[ground_type])]
|
|
|
|
Globals.play_sound(sound, Globals.Bus.Sounds, self)
|
2021-09-23 21:19:11 +02:00
|
|
|
|
|
|
|
func get_current_weapon_slot():
|
|
|
|
return get_weapon_slot(current_weapon)
|
|
|
|
|
|
|
|
func get_weapon_slot(weapon: Weapon):
|
|
|
|
var i = 0
|
|
|
|
for weap in weapons:
|
|
|
|
if weap == weapon:
|
|
|
|
return i
|
|
|
|
i+=1
|
|
|
|
return i
|
|
|
|
|
|
|
|
func create_weapon(weapon_scene: PackedScene):
|
|
|
|
var weapon = weapon_scene.instance()
|
|
|
|
if weapon is Weapon:
|
|
|
|
return weapon
|
|
|
|
else:
|
|
|
|
weapon.queue_free()
|
|
|
|
return null
|
|
|
|
|
|
|
|
func switch_next_weapon_slot():
|
|
|
|
var weapon_slot = 0
|
|
|
|
if get_current_weapon_slot() != len(weapons) - 1:
|
|
|
|
weapon_slot = get_current_weapon_slot() + 1
|
|
|
|
set_weapon_slot(weapon_slot)
|
|
|
|
|
|
|
|
func enter_water(force: float):
|
|
|
|
is_in_water = true
|
2021-10-01 15:50:16 +02:00
|
|
|
ground_type = GroundType.WATER
|
2021-09-23 21:19:11 +02:00
|
|
|
if abs(force) >= 50:
|
|
|
|
Globals.play_sound(water_splash_sound, Globals.Bus.Sounds, self)
|
|
|
|
|
|
|
|
func exit_water(force: float):
|
|
|
|
is_in_water = false
|
2021-10-01 15:50:16 +02:00
|
|
|
ground_type = GroundType.GRASS
|
2021-09-23 21:19:11 +02:00
|
|
|
|
|
|
|
func _ready():
|
|
|
|
if animation_tree_path:
|
|
|
|
animation_tree = get_node(animation_tree_path)
|
|
|
|
|
|
|
|
func _physics_process(delta):
|
|
|
|
if Engine.editor_hint:
|
|
|
|
return
|
|
|
|
|
|
|
|
walk(delta)
|
|
|
|
|
|
|
|
if not animation_tree:
|
|
|
|
return
|
2021-10-01 15:50:16 +02:00
|
|
|
if not Globals.process_3d_inputs:
|
|
|
|
return
|
2021-09-23 21:19:11 +02:00
|
|
|
# Blend nicely between animations
|
|
|
|
var movement_vector = Vector2.ZERO
|
|
|
|
if Input.is_action_pressed("move_forward"):
|
|
|
|
movement_vector.y = 1
|
|
|
|
if Input.is_action_pressed("move_backward"):
|
|
|
|
movement_vector.y = -1
|
|
|
|
if Input.is_action_pressed("move_right"):
|
|
|
|
movement_vector.x = 1
|
|
|
|
if Input.is_action_pressed("move_left"):
|
|
|
|
movement_vector.x = -1
|
|
|
|
var res = animation_blend_vector.linear_interpolate(movement_vector, delta*10)
|
|
|
|
animation_tree.set("parameters/movement/blend_position", res)
|
|
|
|
animation_blend_vector = res
|
|
|
|
|
|
|
|
func _process(delta):
|
|
|
|
if Engine.editor_hint:
|
|
|
|
return
|
2021-10-01 15:50:16 +02:00
|
|
|
if Globals.process_3d_inputs:
|
|
|
|
move_axis.x = Input.get_action_strength("move_forward") - Input.get_action_strength("move_backward")
|
|
|
|
move_axis.y = Input.get_action_strength("move_right") - Input.get_action_strength("move_left")
|
2021-09-23 21:19:11 +02:00
|
|
|
|
|
|
|
func fire_pressed():
|
|
|
|
current_weapon.activate(Globals.target_position["position"])
|
|
|
|
|
|
|
|
func fire_released():
|
|
|
|
current_weapon.deactivate(Globals.target_position["position"])
|
|
|
|
|
|
|
|
func walk(delta: float) -> void:
|
|
|
|
# Input
|
|
|
|
var direction = Vector3()
|
|
|
|
var aim: Basis = get_global_transform().basis
|
|
|
|
if move_axis.x >= 0.5:
|
|
|
|
direction -= aim.z
|
|
|
|
if move_axis.x <= -0.5:
|
|
|
|
direction += aim.z
|
|
|
|
if move_axis.y <= -0.5:
|
|
|
|
direction -= aim.x
|
|
|
|
if move_axis.y >= 0.5:
|
|
|
|
direction += aim.x
|
|
|
|
direction.y = 0
|
|
|
|
direction = direction.normalized()
|
|
|
|
|
|
|
|
# Jump
|
|
|
|
var _snap: Vector3
|
2021-10-01 15:50:16 +02:00
|
|
|
if can_jump and is_on_floor():
|
2021-09-23 21:19:11 +02:00
|
|
|
_snap = Vector3.DOWN
|
|
|
|
if Input.is_action_just_pressed("move_jump"):
|
|
|
|
_snap = Vector3.ZERO
|
|
|
|
velocity.y = jump_height
|
2021-10-01 15:50:16 +02:00
|
|
|
emit_signal("jumped")
|
2021-09-23 21:19:11 +02:00
|
|
|
|
|
|
|
# Apply Gravity
|
|
|
|
velocity.y -= gravity * delta
|
|
|
|
# if is_in_water:
|
|
|
|
# velocity.y = 0
|
|
|
|
|
|
|
|
# Sprint
|
|
|
|
var _speed: float
|
|
|
|
if Globals.process_3d_inputs and Input.is_action_pressed("move_sprint") and can_sprint and move_axis.x == 1:
|
|
|
|
_speed = sprint_speed
|
|
|
|
sprinting = true
|
|
|
|
else:
|
|
|
|
_speed = walk_speed
|
|
|
|
sprinting = false
|
|
|
|
|
|
|
|
# Acceleration and Deacceleration
|
|
|
|
# where would the player go
|
|
|
|
var _temp_vel: Vector3 = velocity
|
|
|
|
_temp_vel.y = 0
|
|
|
|
var _target: Vector3 = direction * _speed
|
|
|
|
var _temp_accel: float
|
|
|
|
if direction.dot(_temp_vel) > 0:
|
|
|
|
_temp_accel = acceleration
|
|
|
|
else:
|
|
|
|
_temp_accel = deacceleration
|
|
|
|
if not is_on_floor():
|
|
|
|
_temp_accel *= air_control
|
|
|
|
# interpolation
|
|
|
|
_temp_vel = _temp_vel.linear_interpolate(_target, _temp_accel * delta)
|
|
|
|
velocity.x = _temp_vel.x
|
|
|
|
velocity.z = _temp_vel.z
|
|
|
|
# clamping (to stop on slopes)
|
|
|
|
if direction.dot(velocity) == 0:
|
2021-10-01 15:50:16 +02:00
|
|
|
var _vel_clamp := 1
|
2021-09-23 21:19:11 +02:00
|
|
|
if velocity.x < _vel_clamp and velocity.x > -_vel_clamp:
|
|
|
|
velocity.x = 0
|
|
|
|
if velocity.z < _vel_clamp and velocity.z > -_vel_clamp:
|
|
|
|
velocity.z = 0
|
|
|
|
|
|
|
|
# Move
|
2021-10-01 15:50:16 +02:00
|
|
|
velocity = move_and_slide(velocity, FLOOR_NORMAL, true, 4, deg2rad(floor_max_angle))
|
|
|
|
# velocity.y = move_and_slide_with_snap(velocity, _snap, FLOOR_NORMAL, true, 4, deg2rad(floor_max_angle)).y
|