giants-godot/entities/character.gd

260 lines
6.4 KiB
GDScript

extends KinematicBody
class_name Character
enum States {
Alive,
Dead
}
enum GroundType {
DIRT,
WATER,
WOOD,
METAL,
SAND,
GRASS
}
signal health_changed
signal died
signal changed_weapon
signal fired(projectile)
signal pickup_weapon
signal jumped
var UI: Control
export var max_weapons: int = 5
var weapons: Array = []
var current_weapon: Weapon
var is_in_water: bool = false
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
export(float, 0.0, 1.0, 0.00001) var air_control := 0.001
export var can_jump = true
export var jump_height := 5
export var floor_max_angle := 90.0
export var gravity: float = 40.0
export var walk_speed: float = 2.0
export var ground_friction: float = 3.0
var ground_type = GroundType.DIRT
var footstep_sounds = {}
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
func can_pickup_weapon(weapon: Weapon) -> bool:
return has_weapon(weapon) or len(weapons) < max_weapons
func pickup_weapon(weapon: Weapon) -> Weapon:
var similar = get_similar_weapon(weapon)
if similar:
similar.current_ammo = clamp(similar.current_ammo + weapon.current_ammo, 0, weapon.max_ammo)
return similar
else:
# add new weapon
weapons.append(weapon)
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
func take_damage(hitpts: float):
health -= hitpts
emit_signal("health_changed")
if health <= 0:
die()
func get_current_weapon():
return current_weapon
func die():
state = States.Dead
emit_signal("died")
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()
func play_footstep():
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)
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
ground_type = GroundType.WATER
if abs(force) >= 50:
Globals.play_sound(water_splash_sound, Globals.Bus.Sounds, self)
func exit_water(force: float):
is_in_water = false
ground_type = GroundType.GRASS
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
if not Globals.process_3d_inputs:
return
# 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
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")
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
if can_jump and is_on_floor():
_snap = Vector3.DOWN
if Input.is_action_just_pressed("move_jump"):
_snap = Vector3.ZERO
velocity.y = jump_height
emit_signal("jumped")
# 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:
var _vel_clamp := 1
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
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