extends "res://scripts/characters/NetworkedCharacter.gd" # Camera export var mouse_sensitivity:float = 12.0 export var FOV:float = 90.0 var mouse_axis: Vector2 = Vector2.ZERO onready var head: Spatial = $Neck/Head onready var neck: Spatial = $Neck onready var melee_ray: RayCast = $"%MeleeRay" onready var gun_ray: RayCast = $Neck/Head/GunRay onready var carry_point: Position3D = $Neck/Head/CarryPoint onready var viewmodel: Spatial = $Neck/Head/VIEWMODEL_ARMS # Move var velocity := Vector3() var direction := Vector3() var move_axis := Vector2() var floorspeed := Vector3() var jumping:bool = false var can_jump:bool = true onready var nav: NavigationAgent = $NavigationAgent # Walk const FLOOR_MAX_ANGLE: float = deg2rad(46.0) export var jump_height: float = 300.0 var in_water: bool = false var swim_speed: float = 450.0 var climb_speed: float = 5.0 #physics var player_state: PhysicsDirectBodyState = null var is_on_floor:bool = false var floor_normal: Vector3 = Vector3.UP var acceleration:float = 80.0 export var walk_speed:float = 5.0 var c_friction:float = 4.0 var air_control:float = 0.3 #ai var ai_state: String = "IDLE" var ai_should_pathfind: bool = false var ai_path_target_global: Vector3 = Vector3.ZERO var ai_look_target: Vector3 = Vector3.ZERO var ai_should_look: bool = false var ai_should_track: bool = false var ai_track_object: Spatial = null const AI_LOOK_SPEED: float = 4.0 # Called when the node enters the scene tree func _ready() -> void: if weapon_slot1 == null: weapon_slot1 = preload("res://scenes/weapons/hands.res") weapons = [weapon_slot1,weapon_slot2,weapon_slot3,weapon_slot4,weapon_slot5] for w in weapons: if w!= null: w.init(self) weapon = weapons[0] world = get_tree().get_root().get_node("GAMEWORLD") $"%UseRay".add_exception(self) $"%MeleeRay".add_exception(self) $"%UseRay".add_exception($AreaDetect) $"%MeleeRay".add_exception($AreaDetect) func add_weapon_vm(weapon_vm: Spatial, trfrm: Transform): viewmodel.get_node("Skeleton/GunBone").add_child(weapon_vm) weapon_vm.transform = trfrm # Called every frame. 'delta' is the elapsed time since the previous frame func _process(_delta: float) -> void: if is_player and !world.is_chatting: if Input.is_action_just_pressed("use"): initiate_use() if controlling_machine: if Input.is_action_just_pressed("fire"): machine.attack1() if Input.is_action_just_pressed("fire2"): machine.attack2() machine.direction_input(Input.get_action_strength("move_forward"),Input.get_action_strength("move_backward"), Input.get_action_strength("move_right"),Input.get_action_strength("move_left"), Input.get_action_strength("alt_right"),Input.get_action_strength("alt_left")) machine.misc_input(Input.get_action_strength("move_duck"),Input.get_action_strength("move_jump"),Input.get_action_strength("move_walk")) machine.mouse_input(Input.get_action_strength("fire"), Input.get_action_strength("fire3"),Input.get_action_strength("fire2")) else: weapon.mouse_input(Input.get_action_strength("fire"), Input.get_action_strength("fire3"),Input.get_action_strength("fire2")) jumping = Input.get_action_strength("move_jump") walk_speed = 5.0 if Input.get_action_strength("move_walk") else 7.5 if Input.is_action_just_pressed("fire"): weapon.attack1() if Input.is_action_just_pressed("reload") and weapon.has_method("reload"): weapon.reload() 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") if Input.is_action_just_pressed("slot1") and weapons[0] != null and weapon.deselect() : weapon = weapons[0] weapon.select() elif Input.is_action_just_pressed("slot2")and weapons[1] != null and weapon.deselect(): weapon = weapons[1] weapon.select() elif Input.is_action_just_pressed("slot3") and weapons[2] != null and weapon.deselect() : weapon = weapons[2] weapon.select() elif Input.is_action_just_pressed("slot4") and weapons[3] != null and weapon.deselect() : weapon = weapons[3] weapon.select() elif Input.is_action_just_pressed("slot5") and weapons[4] != null and weapon.deselect() : weapon = weapons[4] weapon.select() func initiate_use(): if controlling_machine: machine.relinquish_control() return if ladder_m != null: leave_ladder() return if carrying: carrying = false carrying_object.rpc("set_nm",1) carrying_object = null return if $"%UseRay".is_colliding(): var area_c = $"%UseRay".get_collider() match area_c.name: "SteerArea": #must be a networkedmachine if area_c.get_parent().controllable: world.rpc_id(1, "_call_on_server", "_client_request_control_vehicle", {"id" : world.client_id, "machine_path" : area_c.get_parent().get_path(), "char_name" : name}) "LadderArea": mount_ladder(area_c.get_parent()) "TugArea": pass "CarryArea": if weapon.name == "HANDS" and weapon.can_pickup(): carrying = true carrying_object = area_c.get_parent() carrying_object.rpc("set_nm",get_network_master()) carry_point.global_transform.origin = carrying_object.global_transform.origin _: pass func carry_process() -> void: if !is_instance_valid(carrying_object): carrying_object = null carrying = false return var vec = (carry_point.global_transform.origin - carrying_object.global_transform.origin) var length = vec.length() if length > 1: carrying = false carrying_object.rpc("set_nm",1) carrying_object = null return var dir = vec.normalized() carrying_object.add_central_force(carrying_object.mass*120*dir*length - 90*carrying_object.linear_velocity) # Called every physics tick. 'delta' is constant func _physics_process(delta: float) -> void: if is_network_master(): if !is_player: #ai behavior if ai_should_look: ai_look_at() if carrying: carry_process() if ladder_m != null: climb_ladder(delta) elif !on_floor_test() and in_water: swim(delta) else: walk(delta) is_on_floor = false #reset whether is on floor in between frames # called each physics frame func on_floor_test() -> bool: if $Feet.is_colliding(): is_on_floor = true floor_normal = Vector3.UP floorspeed = $Feet.get_collider().get_linear_velocity() if $Feet.get_collider().has_method("get_linear_velocity") else Vector3.ZERO return true if player_state: for i in range(player_state.get_contact_count()): var contact_angle_from_up : float = Vector3.UP.angle_to(player_state.get_contact_local_normal(i)) if contact_angle_from_up < FLOOR_MAX_ANGLE: floor_normal = player_state.get_contact_local_normal(i) is_on_floor = true return true return false #modify simulated physics results func _integrate_forces(state: PhysicsDirectBodyState) -> void: if !is_network_master(): return player_state = state velocity = state.get_linear_velocity() for i in range(player_state.get_contact_count()): var contact_angle_from_up : float = Vector3.UP.angle_to(player_state.get_contact_local_normal(i)) if contact_angle_from_up > FLOOR_MAX_ANGLE and !is_on_floor: friction = 0 break if i == player_state.get_contact_count() - 1: friction = 1 nav.set_velocity(velocity) rpc("set_phys_transform", transform, linear_velocity) if global_transform.origin.y < -30: rpc("damage", 500000, "drown", [1, "Davy Jones"], "") func walk(_delta: float) -> void: # Input direction = Vector3() var aim: Basis = head.get_global_transform().basis direction += -move_axis.x * aim.z + move_axis.y * aim.x direction.y = 0 direction = direction.normalized() if floor_normal != Vector3.UP: direction = direction.rotated(floor_normal.cross(Vector3.UP).normalized(), Vector3.UP.angle_to(floor_normal)) # Jump if is_player and jumping and is_on_floor and can_jump: jump() #max walk speed var _speed = walk_speed var _temp_accel: float = acceleration var _cspeed = sqrt(pow(velocity.x-floorspeed.x,2)+pow(velocity.z-floorspeed.z,2)) if not is_on_floor: _temp_accel *= air_control var projVel = Vector2(velocity.x-floorspeed.x,velocity.z-floorspeed.z).dot(Vector2(direction.x,direction.z)) if is_on_floor: add_central_force(-mass*_cspeed*linear_velocity.normalized()*c_friction)#friction if _speed - _cspeed > 0: add_central_force (mass*Vector3(direction.x*_temp_accel, 0, direction.z*_temp_accel))#velocity.x += direction.x*_temp_accel else: add_central_force(mass*Vector3(direction.x*(_speed-projVel), 0, direction.z*(_speed-projVel))) elif 1.0 - projVel > 0: add_central_force (mass*Vector3(direction.x*_temp_accel, 0, direction.z*_temp_accel)) func jump(): can_jump = false apply_central_impulse(Vector3.UP*jump_height) yield(get_tree().create_timer(0.05),"timeout") can_jump = true func swim(_delta: float): #drag and buoyancy add_central_force(Vector3.UP*weight*1.0) add_central_force(-1*linear_velocity*100) #controls var dir: Basis = head.get_global_transform().basis var m_dir: Vector3 = -move_axis.x * dir.z + move_axis.y * dir.x m_dir = m_dir.normalized() add_central_force(swim_speed*m_dir) if jumping: add_central_force(Vector3.UP*weight*0.5) func enter_water(): in_water = true func exit_water(): in_water = false func mount_ladder(target_ladder: Spatial): var old_neck_rot = neck.get_global_rotation() var ladder_tracker = Spatial.new() ladder_tracker.name = name target_ladder.add_child(ladder_tracker) ladder_tracker.transform = target_ladder.bottom.transform ladder_tracker.global_transform.origin = target_ladder.get_nearest_point_to_route(global_transform.origin) look_at(global_transform.origin + target_ladder.global_transform.basis.x, target_ladder.global_transform.basis.y) ladder_m = ladder_tracker global_transform.origin = ladder_m.global_transform.origin global_transform.basis = ladder_m.global_transform.basis.orthonormalized() neck.set_global_rotation(old_neck_rot) linear_velocity = Vector3.ZERO set_gravity_scale(0.0) #called each frame while climbing ladder func climb_ladder(delta: float): var new_ladder_pos = ladder_m.global_transform.origin + ladder_m.global_transform.basis.y.normalized() * move_axis.x * delta * climb_speed var prog = ladder_m.get_parent().get_climb_scalar(new_ladder_pos) if prog >= 0.0 and prog <= 1.0: ladder_m.global_transform.origin = new_ladder_pos global_transform.origin = ladder_m.global_transform.origin global_transform.basis = ladder_m.global_transform.basis.orthonormalized() func leave_ladder(): var old_neck_rot = neck.get_global_rotation() if (ladder_m.get_parent().top.global_transform.origin - global_transform.origin).length_squared() < 0.01: apply_central_impulse(-400*ladder_m.global_transform.basis.z) global_transform.basis = world.global_transform.basis neck.set_global_rotation(old_neck_rot) set_gravity_scale(1.0) ladder_m.queue_free() ladder_m = null remotesync func anim_event(event: Dictionary): $"%VIEWMODEL_ARMS".get_node("AnimationPlayer").play(event["VMANIMPLAY"]) remotesync func play_weapon_sound(filepath) -> void: $WeaponSound.stream = load(filepath) $WeaponSound.play() remotesync func add_rocket_to_scene(pos, dir, id): var rocket = preload("res://scenes/ballistics/Rocket.tscn").instance() world.get_node("BALLISTICS").add_child(rocket, true) rocket.shooter = name + " (" + world.players_info[get_network_master()][0] + ")" rocket.shooter_id = id rocket.global_transform.origin = pos rocket.global_transform.basis = Basis(-1*dir.z, dir.y, dir.x) rocket.add_collision_exception_with(self) func set_look_status(target, type="STATIC"): if type == "STATIC": ai_look_target = target ai_should_look = true elif type == "TRACK": ai_track_object = target ai_should_look = true ai_should_track = true else: ai_should_track = false func set_path_target(target): ai_path_target_global = target func ai_look_at(): if ai_should_track: if is_instance_valid(ai_track_object): ai_look_target = ai_track_object.global_transform.origin else: ai_track_object = null ai_should_track = false var p_neck: float = Vector3(ai_look_target.x - neck.global_transform.origin.x, 0, ai_look_target.z - neck.global_transform.origin.z).signed_angle_to(-neck.global_transform.basis.z, neck.global_transform.basis.y ) var p_head: float = Vector3(ai_look_target.x - neck.global_transform.origin.x, ai_look_target.y - head.global_transform.origin.y, ai_look_target.z - neck.global_transform.origin.z).signed_angle_to( -head.global_transform.basis.z, head.global_transform.basis.x) if abs(p_neck) > 0.01: neck.rotation_degrees.y = lerp(neck.rotation_degrees.y, neck.rotation_degrees.y-p_neck, AI_LOOK_SPEED+abs(p_neck/3.1415)) if abs(p_head) > 0.01: head.rotation_degrees.x = lerp(head.rotation_degrees.x, head.rotation_degrees.x-p_head, AI_LOOK_SPEED+abs(p_head/3.1415)) if abs(p_neck) <= 0.01 and abs(p_head) <= 0.01 and !ai_should_track: ai_should_look = false