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 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 export var back_camera_path: NodePath onready var back_camera = get_node(back_camera_path) export var head_path: NodePath onready var head = get_node(head_path) # 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 := 6 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 export var animation_blend_speed: float = 10 var ground_snap = Vector3.DOWN var ground_type = GroundType.DIRT var footstep_sounds = {} export var max_health = 100 export var health = 100 var state = States.Alive var shoot_delay: float = 0 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 can_shoot_delay() -> bool: # returns true if the character has not shot a bullet recently return shoot_delay <= 0 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): if state == States.Alive: 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") print("Me ded") func set_weapon_slot(slot: int): if current_weapon: current_weapon.visible = false current_weapon.disconnect("fired", self, "weapon_fired") var newweapon = weapons[slot] current_weapon = newweapon current_weapon.visible = true current_weapon.play_switch_sound() current_weapon.connect("fired", self, "weapon_fired") 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 get_next_weapon_slot(): var current = get_current_weapon_slot() if current + 1 < len(weapons): return current + 1 else: return 0 func switch_next_weapon_slot(): var next_slot = get_next_weapon_slot() if next_slot == get_current_weapon_slot(): return set_weapon_slot(next_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 _process(delta): if shoot_delay > 0: shoot_delay -= delta func _physics_process(delta): if Engine.editor_hint: return walk(delta) if not animation_tree: return # Blend nicely between animations var anim_vector = Vector2(move_axis.y, move_axis.x) var res = animation_blend_vector.linear_interpolate(anim_vector, delta*animation_blend_speed) animation_tree.set("parameters/movement/blend_position", res) animation_blend_vector = res func fire_pressed(): if current_weapon and can_shoot_delay(): current_weapon.activate(Globals.target_position["position"]) func fire_released(): if current_weapon: current_weapon.deactivate(Globals.target_position["position"]) func jump(): if can_jump and is_on_floor(): velocity.y = jump_height emit_signal("jumped") func set_sprint(sprint_state: bool): if can_sprint: sprinting = sprint_state func weapon_fired(): shoot_delay = current_weapon.repeat_time 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() # Apply Gravity velocity.y -= gravity * delta # if is_in_water: # velocity.y = 0 # Sprint var _speed: float if sprinting: # if can_sprint and move_axis.x == 1: _speed = sprint_speed else: _speed = walk_speed # 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