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