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 use_ray: RayCast = $"%UseRay" 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 use_held: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 var idle_t: bool = true #ai onready var ai_state_machine:StateMachine = get_node("AIStateMachine") enum AIStates { IDLE, MAN_CANNON } export(AIStates) var ai_state = AIStates.IDLE export var ai_target_machine_path := NodePath() var ai_target_machine: NetMachine = null var ai_should_pathfind: bool = false var ai_path_target_global: Vector3 = Vector3.ZERO var ai_path_array: Array = [] const AI_PATH_PROXIM_DIST_SQ: float = 1.0 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 const DROWN_DICT: Dictionary = {"type" : "DROWN", "attacker_net_id" : 1, "attacker_name" : "Davy Jones", "weapon_name" : "his locker"} # 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) if ai_target_machine_path: ai_target_machine = get_node(ai_target_machine_path) if get_tree().get_network_unique_id() != 1: #only server needs ai processing get_node("AIStateMachine").queue_free() #for playerbody in get_tree().get_nodes_in_group("player"): # if playerbody.team == team and playerbody != self: # add_collision_exception_with(playerbody) 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_network_master() and !is_player: pass elif is_player and !world.is_chatting: use_held = Input.is_action_pressed("use") 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 #interact with world if !use_ray.is_colliding(): return var area_c = use_ray.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}) elif !area_c.get_parent().loaded and inventory[area_c.get_parent().ammo_type] > 0: loading = true load_target = area_c.get_parent() load_ammo = area_c.get_parent().ammo_type "LadderArea": mount_ladder(area_c.get_parent()) "TugArea": pass "UseArea": #must have method use_generic area_c.get_parent().use_generic(self) "PickupArea": #must be a networkedprojectile. must have inventory_name property var type: String = area_c.get_parent().inventory_name if inventory[type] < inventory_caps[type]: area_c.get_parent().rpc("net_remove") inventory[type] += 1 rset("inventory", inventory) "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 load_process(delta) -> void: if use_ray.is_colliding() and use_ray.get_collider().get_parent() == load_target and use_held: var progress: float =load_target.increase_load(delta) if progress < 0: if get_network_master() != 1: world.hud.hide_progress() load_target.reset_load() loading = false load_target = null inventory[load_ammo] -= 1 else: if get_network_master() != 1: world.hud.set_progress(progress) else: if get_network_master() != 1: world.hud.hide_progress() load_target.reset_load() loading = false load_target = null func carry_process() -> void: if !is_instance_valid(carrying_object) or carrying_object.get_network_master() != get_network_master(): 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 loading: load_process(delta) move_dir_process() 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 func move_dir_process(): direction = Vector3() if is_player: 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() else: if ai_should_pathfind: if !ai_path_process(): return direction = ai_path_target_global - global_transform.origin direction.y = 0 direction = direction.normalized() # 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 rpc("set_phys_transform", transform, linear_velocity) if global_transform.origin.y < -30: rpc("damage", 500000, DROWN_DICT) nav.set_velocity(velocity) func walk(_delta: float) -> void: 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-floorspeed).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(Vector3(0,old_neck_rot.y,0)) 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 if get_network_master() != 1: rocket.shooter+= " (" + 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 ai_set_state(state: String): ai_state_machine.transition_to(state) func ai_set_look_status(target, type:String="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_look = false ai_should_track = false 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) neck.rotation_degrees.y = lerp(neck.rotation_degrees.y, neck.rotation_degrees.y-p_neck, AI_LOOK_SPEED+abs(p_neck/3.1415)) 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 func ai_set_path_target(target: Vector3): ai_should_pathfind = true nav.set_target_location(target) #CharacterAIManager.request_find_path(self, target, true) func ai_set_path_array(arr: PoolVector3Array): world.draw_path(arr) ai_should_pathfind = true ai_path_array = arr ai_path_target_global = ai_path_array[0] func ai_path_process() -> bool: # if (global_transform.origin - ai_path_array[0]).length_squared() < AI_PATH_PROXIM_DIST_SQ: # ai_path_array.remove(0) # if len(ai_path_array) == 0: #destination reached # ai_should_pathfind = false # return false # else: ai_path_target_global = ai_path_array[0] # return true if nav.is_navigation_finished(): ai_should_pathfind = false return false ai_path_target_global = nav.get_next_location() return true