summaryrefslogtreecommitdiff
path: root/src/player_controller
diff options
context:
space:
mode:
Diffstat (limited to 'src/player_controller')
-rw-r--r--src/player_controller/.sconsign.dblitebin0 -> 27268 bytes
-rw-r--r--src/player_controller/PlayerController.gd297
-rw-r--r--src/player_controller/SConstruct2
-rw-r--r--src/player_controller/___player_controller.h (renamed from src/player_controller/player_controller.h)0
-rw-r--r--src/player_controller/gdlibrary.cpp17
-rw-r--r--src/player_controller/gdlibrary.osbin0 -> 406904 bytes
-rw-r--r--src/player_controller/playercam.cpp107
-rw-r--r--src/player_controller/playercam.h43
-rw-r--r--src/player_controller/playercam.osbin0 -> 413784 bytes
-rw-r--r--src/player_controller/playercontroller.cpp325
-rw-r--r--src/player_controller/playercontroller.h93
11 files changed, 883 insertions, 1 deletions
diff --git a/src/player_controller/.sconsign.dblite b/src/player_controller/.sconsign.dblite
new file mode 100644
index 0000000..577a41d
--- /dev/null
+++ b/src/player_controller/.sconsign.dblite
Binary files differ
diff --git a/src/player_controller/PlayerController.gd b/src/player_controller/PlayerController.gd
new file mode 100644
index 0000000..241cbe0
--- /dev/null
+++ b/src/player_controller/PlayerController.gd
@@ -0,0 +1,297 @@
+extends RigidBody
+
+# Game
+export var team: String = "RED"
+export var health: int = 100
+var weapon: Node = null
+var world: Spatial = null
+
+# 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
+
+# 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
+
+# Control
+var controlling_machine: bool = false #whether character is riding/controlling something
+var machine : RigidBody = null
+export var is_player: bool = false #whether character is currently controlled by a player
+var ladder_m: Spatial = null
+
+#physics
+var player_state: PhysicsDirectBodyState = null
+var is_on_floor:bool
+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
+
+# Called when the node enters the scene tree
+func _ready() -> void:
+ weapon = preload("res://scenes/weapons/w_Rockets.tscn").instance()
+ add_child(weapon)
+ 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 get_init_info() -> Dictionary:
+ return {"linear_velocity" : linear_velocity, "angular_velocity" : angular_velocity, "controlling_machine" : controlling_machine, "team" : team, "health" : health, "nametag" : $Nametag.text}
+
+func mp_init(init_info: Dictionary):
+ for variable in init_info.keys():
+ set(variable, init_info[variable])
+ $Nametag.text = init_info["nametag"]
+
+remote func set_phys_transform(trfrm: Transform, lvel: Vector3):
+ transform = trfrm
+ linear_velocity = lvel
+
+# 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:
+ jumping = Input.get_action_strength("move_jump")
+ if Input.is_action_just_pressed("fire"):
+ weapon.attack1()
+ 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 initiate_use():
+ if controlling_machine:
+ machine.relinquish_control()
+ return
+ if ladder_m != null:
+ leave_ladder()
+ return
+ if $"%UseRay".is_colliding():
+ var area_c = $"%UseRay".get_collider()
+ match area_c.name:
+ "SteerArea":
+ 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
+ "PickupArea":
+ pass
+ _:
+ pass
+
+remotesync func set_net_owner(owner_id: int):
+ $Nametag.text = ""
+ set_network_master(owner_id)
+ if owner_id != 1:
+ $Nametag.text = world.players_info[owner_id][0]
+ if get_tree().get_network_unique_id() != 1:
+ if owner_id == world.client_id:
+ $Nametag.visible = false
+ world.player_char = self
+ is_player = true
+ world.cam.attach(self, "FIRSTPERSON", "./Neck/Head")
+ else:
+ $Nametag.visible = true
+ is_player = false
+ world.get_node("HUD").update_characters()
+
+func deselect_character():
+ if is_network_master():
+ world.player_char = null
+ if world.client_id != 1: world.cam.attach(world, "STATIC", "./DEFAULTCAM")
+ rpc("set_net_owner", 1)
+
+func take_control_of_machine(slave_machine: RigidBody):
+ machine = slave_machine
+ controlling_machine = true
+
+func lose_machine():
+ if is_network_master(): world.cam.attach(self, "FIRSTPERSON", "./Neck/Head")
+ controlling_machine = false
+ machine = null
+
+# Called every physics tick. 'delta' is constant
+func _physics_process(delta: float) -> void:
+ if is_network_master():
+ 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()
+ $normal_vis.look_at($normal_vis.global_transform.origin + global_transform.basis.z, floor_normal)
+ 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", [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 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()
+ 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():
+ 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
+ set_gravity_scale(1.0)
+ ladder_m.queue_free()
+ ladder_m = null
+
+remotesync func damage(dmg_amt: int, _type: String, shooter: Array, extra: String):
+ health -= dmg_amt
+ if health <= 0 and is_network_master():
+ if shooter[0] != get_network_master() and shooter[0] != 1: world.rpc_id(shooter[0], "game_killsound")
+ if get_network_master() == 1:
+ world._call_on_server("_character_death", {"killer_id" : shooter[0], "killer" : shooter[1], "victim_mp_id" : get_network_master(), "victim" : name, "extra" : extra})
+ else:
+ world.rpc_id(1, "_call_on_server", "_character_death", {"killer_id" : shooter[0], "killer" : shooter[1], "victim_mp_id" : get_network_master(), "victim" : name, "extra" : extra})
+ elif is_network_master():
+ if shooter[0] != get_network_master() and shooter[0] != 1: world.rpc_id(shooter[0], "game_hitsound")
+
+remotesync func remove_dead_character():
+ if is_network_master() and machine != null:
+ machine.relinquish_control()
+ deselect_character()
+ queue_free()
+
+remotesync func net_apply_impulse(impulse_v: Vector3):
+ apply_central_impulse(impulse_v)
diff --git a/src/player_controller/SConstruct b/src/player_controller/SConstruct
index f4bb318..119ad0b 100644
--- a/src/player_controller/SConstruct
+++ b/src/player_controller/SConstruct
@@ -12,7 +12,7 @@ opts.Add(EnumVariable('platform', "Compilation platform", '', ['', 'windows', 'x
opts.Add(EnumVariable('p', "Compilation target, alias for 'platform'", '', ['', 'windows', 'x11', 'linux', 'osx']))
opts.Add(BoolVariable('use_llvm', "Use the LLVM / Clang compiler", 'no'))
opts.Add(PathVariable('target_path', 'The path where the lib is installed.', '../../godot/bin/'))
-opts.Add(PathVariable('target_name', 'The library name.', 'libgdexample', PathVariable.PathAccept))
+opts.Add(PathVariable('target_name', 'The library name.', 'libplayercontroller', PathVariable.PathAccept))
# Local dependency paths, adapt them to your setup
godot_headers_path = "../../godot-cpp/godot-headers/"
diff --git a/src/player_controller/player_controller.h b/src/player_controller/___player_controller.h
index 2514f79..2514f79 100644
--- a/src/player_controller/player_controller.h
+++ b/src/player_controller/___player_controller.h
diff --git a/src/player_controller/gdlibrary.cpp b/src/player_controller/gdlibrary.cpp
new file mode 100644
index 0000000..13bc8a1
--- /dev/null
+++ b/src/player_controller/gdlibrary.cpp
@@ -0,0 +1,17 @@
+#include "playercam.h"
+#include "playercontroller.h"
+
+extern "C" void GDN_EXPORT godot_gdnative_init(godot_gdnative_init_options *o) {
+ godot::Godot::gdnative_init(o);
+}
+
+extern "C" void GDN_EXPORT godot_gdnative_terminate(godot_gdnative_terminate_options *o) {
+ godot::Godot::gdnative_terminate(o);
+}
+
+extern "C" void GDN_EXPORT godot_nativescript_init(void *handle) {
+ godot::Godot::nativescript_init(handle);
+
+ godot::register_class<godot::PlayerCam>();
+ godot::register_class<godot::PlayerController>();
+}
diff --git a/src/player_controller/gdlibrary.os b/src/player_controller/gdlibrary.os
new file mode 100644
index 0000000..c984489
--- /dev/null
+++ b/src/player_controller/gdlibrary.os
Binary files differ
diff --git a/src/player_controller/playercam.cpp b/src/player_controller/playercam.cpp
new file mode 100644
index 0000000..f321c09
--- /dev/null
+++ b/src/player_controller/playercam.cpp
@@ -0,0 +1,107 @@
+#include "playercam.h"
+#include "InputEventMouseMotion.hpp"
+
+
+using namespace godot;
+
+PlayerCam::PlayerCam () { }
+PlayerCam::~PlayerCam () { }
+
+void PlayerCam::_register_methods() {
+ register_method("_ready", &PlayerCam::_ready);
+ register_method("_input", &PlayerCam::_input);
+ register_method("attach", &PlayerCam::attach);
+ register_method("mouse_firstperson", &PlayerCam::mouse_firstperson);
+ register_method("mouse_thirdperson", &PlayerCam::mouse_thirdperson);
+ register_method("mouse_arm", &PlayerCam::mouse_arm);
+ register_method("mouse_freecam", &PlayerCam::mouse_freecam);
+}
+
+void PlayerCam::_init() {
+ mode = String("STATIC");
+ head = nullptr;
+ neck = nullptr;
+ player = nullptr;
+ arm = nullptr;
+ mouse_axis = Vector2::ZERO;
+ mouse_sensitivity = 12.0;
+}
+
+void PlayerCam::_ready() {
+ Input *input_singleton = Input::get_singleton();
+ input_singleton->set_mouse_mode(Input::MOUSE_MODE_CAPTURED);
+ set_current(true);
+}
+
+void PlayerCam::_input(const Ref<InputEvent> event) {
+ Input const *input_singleton = Input::get_singleton();
+ Ref<InputEventMouseMotion> event_m(event);
+ if(event_m.is_valid() && input_singleton->get_mouse_mode() == Input::MOUSE_MODE_CAPTURED){
+ mouse_axis = event_m->get_relative();
+ if(mode == "FIRSTPERSON")mouse_firstperson();
+ else if(mode == "THIRDPERSON")mouse_thirdperson();
+ else if(mode == "STATIC");
+ else if(mode == "ARM")mouse_arm();
+ else if(mode == "FREECAM")mouse_freecam();
+ }
+}
+
+void PlayerCam::attach(Node* new_parent, String c_mode, String extra_path) {
+ if(get_parent() != nullptr)
+ get_parent()->remove_child(this);
+ if(c_mode == "FIRSTPERSON") {
+ head = (Spatial*)new_parent->find_node("Head", true, false); neck = (Spatial*)new_parent->find_node("Neck", true, false);
+ arm = nullptr; mode = c_mode;
+ }
+ else if (c_mode == "THIRDPERSON") {
+ head = (Spatial*)new_parent->find_node("Head", true, false); neck = (Spatial*)new_parent->find_node("Neck", true, false);
+ arm = (SpringArm*)new_parent->find_node("SpringArm", true, false); mode = c_mode;
+ }
+ else if(c_mode == "ARM") {
+ head = nullptr; neck = nullptr;
+ arm = (SpringArm*)new_parent->find_node("SpringArm", true, false); mode = c_mode;
+ }
+ else {
+ head = nullptr; neck = nullptr; arm = nullptr;
+ }
+ new_parent->get_node(NodePath(extra_path))->add_child(this);
+ set_transform(Transform::IDENTITY);
+}
+
+void PlayerCam::mouse_firstperson() {
+ if(mouse_axis.length_squared() > 0.0){
+ float horizontal = -mouse_axis.x * (mouse_sensitivity / 100.0);
+ float vertical = -mouse_axis.y * (mouse_sensitivity / 100.0);
+
+ neck->rotate_y(Math::deg2rad(horizontal));
+ head->rotate_x(Math::deg2rad(vertical));
+
+ //vertical clamp
+ Vector3 new_rot = head->get_rotation_degrees();
+ new_rot.x = Math::clamp((double)new_rot.x, -90.0, 90.0);
+ head->set_rotation_degrees(new_rot);
+ }
+}
+
+void PlayerCam::mouse_thirdperson() {
+ Vector3 new_arm_rot = arm->get_rotation_degrees();
+ new_arm_rot.x = Math::clamp((double)(get_rotation_degrees().x-mouse_axis.y*(mouse_sensitivity / 100)),-90.0,90.0);
+ new_arm_rot.y -= mouse_axis.x*(mouse_sensitivity / 100.0);
+ arm->set_rotation_degrees(new_arm_rot);
+ Vector3 new_head_rot = Vector3(arm->get_rotation_degrees().x, 0, 0);
+ Vector3 new_neck_rot = Vector3(0, arm->get_rotation_degrees().y, 0);
+
+ head->set_rotation_degrees(new_head_rot);
+ neck->set_rotation_degrees(new_neck_rot);
+}
+
+void PlayerCam::mouse_arm() {
+ //arm->rotation_degrees.x = Math::clamp(rotation_degrees.x-mouse_axis.y*(mouse_sensitivity / 100),-70,70);
+ //arm->rotation_degrees.y -= mouse_axis.x*(mouse_sensitivity / 100);
+}
+
+void PlayerCam::mouse_freecam() {
+// pass
+}
+
+
diff --git a/src/player_controller/playercam.h b/src/player_controller/playercam.h
new file mode 100644
index 0000000..dd089de
--- /dev/null
+++ b/src/player_controller/playercam.h
@@ -0,0 +1,43 @@
+#ifndef PLAYERCAMGDS_H
+#define PLAYERCAMGDS_H
+
+#include <Godot.hpp>
+#include <ClippedCamera.hpp>
+#include <RigidBody.hpp>
+#include <SpringArm.hpp>
+#include <Input.hpp>
+
+namespace godot {
+
+class PlayerCam : public ClippedCamera {
+ GODOT_CLASS(PlayerCam, ClippedCamera)
+
+private:
+ String mode;
+ Spatial* head;
+ Spatial* neck;
+ RigidBody* player;
+ SpringArm* arm;
+ Vector2 mouse_axis;
+ float mouse_sensitivity;
+
+public:
+ static void _register_methods();
+
+ PlayerCam();
+ ~PlayerCam();
+
+ void _init();
+
+ void _ready();
+ void _input(const Ref<InputEvent> event);
+ void attach(Node* new_parent, String c_mode, String extra_path = ".");
+ void mouse_firstperson();
+ void mouse_thirdperson();
+ void mouse_arm();
+ void mouse_freecam();
+
+};
+
+}
+#endif
diff --git a/src/player_controller/playercam.os b/src/player_controller/playercam.os
new file mode 100644
index 0000000..2b88b46
--- /dev/null
+++ b/src/player_controller/playercam.os
Binary files differ
diff --git a/src/player_controller/playercontroller.cpp b/src/player_controller/playercontroller.cpp
new file mode 100644
index 0000000..6ea5544
--- /dev/null
+++ b/src/player_controller/playercontroller.cpp
@@ -0,0 +1,325 @@
+#include "playercontroller.h"
+#include <ResourceLoader.hpp>
+#include <PackedScene.hpp>
+#include <Input.hpp>
+
+using namespace godot;
+
+PlayerController::PlayerController () { }
+PlayerController::~PlayerController () { }
+
+void PlayerController::_register_methods() {
+ register_method("_ready", &PlayerController::_ready);
+ register_method("get_init_info", &PlayerController::get_init_info);
+ register_method("mp_init", &PlayerController::mp_init);
+ register_method("set_phys_transform", &PlayerController::set_phys_transform, GODOT_METHOD_RPC_MODE_REMOTE);
+ register_method("_process", &PlayerController::_process);
+ register_method("initiate_use", &PlayerController::initiate_use);
+ register_method("set_net_owner", &PlayerController::set_net_owner, GODOT_METHOD_RPC_MODE_REMOTESYNC);
+ register_method("deselect_character", &PlayerController::deselect_character);
+ register_method("take_control_of_machine", &PlayerController::take_control_of_machine);
+ register_method("lose_machine", &PlayerController::lose_machine);
+ register_method("_physics_process", &PlayerController::_physics_process);
+ register_method("on_floor_test", &PlayerController::on_floor_test);
+ register_method("_integrate_forces", &PlayerController::_integrate_forces);
+ register_method("walk", &PlayerController::walk);
+ register_method("jump", &PlayerController::jump);
+ register_method("swim", &PlayerController::swim);
+ register_method("enter_water", &PlayerController::enter_water);
+ register_method("exit_water", &PlayerController::exit_water);
+ register_method("mount_ladder", &PlayerController::mount_ladder);
+ register_method("climb_ladder", &PlayerController::climb_ladder);
+ register_method("leave_ladder", &PlayerController::leave_ladder);
+ register_method("damage", &PlayerController::damage, GODOT_METHOD_RPC_MODE_REMOTESYNC);
+ register_method("remove_dead_character", &PlayerController::remove_dead_character, GODOT_METHOD_RPC_MODE_REMOTESYNC);
+ register_method("net_apply_impulse", &PlayerController::net_apply_impulse, GODOT_METHOD_RPC_MODE_REMOTESYNC);
+
+ register_property<PlayerController, String>("team", &PlayerController::team, String("RED"));
+ register_property<PlayerController, int>("health", &PlayerController::health, int(100));
+ register_property<PlayerController, float>("mouse_sensitivity", &PlayerController::mouse_sensitivity, float(12.0));
+ register_property<PlayerController, float>("FOV", &PlayerController::FOV, float(90.0));
+ register_property<PlayerController, float>("jump_height", &PlayerController::jump_height, float(300.0));
+ register_property<PlayerController, bool>("is_player", &PlayerController::is_player, bool(false));
+ register_property<PlayerController, float>("walk_speed", &PlayerController::walk_speed, float(5.0));
+}
+
+void PlayerController::_init() {
+ weapon = nullptr;
+ world = nullptr;
+ mouse_axis = Vector2::ZERO;
+ velocity = Vector3::ZERO;
+ direction = Vector3::ZERO;
+ move_axis = Vector2::ZERO;
+ floorspeed = Vector3::ZERO;
+ jumping = false;
+ can_jump = true;
+ FLOOR_MAX_ANGLE = Math::deg2rad(46.0);
+ in_water = false;
+ swim_speed = 450.0;
+ climb_speed = 5.0;
+ controlling_machine = false;
+ machine = nullptr;
+ ladder_m = nullptr;
+ player_state = nullptr;
+ is_on_floor = false;
+ floor_normal = Vector3::UP;
+ acceleration = 80.0;
+ c_friction = 4.0;
+ air_control = 0.3;
+}
+
+void PlayerController::_ready() {
+ head = get_node<Spatial>(NodePath("Neck/Head"));
+ neck = get_node<Spatial>(NodePath("Neck"));
+ useray = get_node<RayCast>(NodePath("Neck/Head/UseRay"));
+ nametag = get_node<Label3D>(NodePath("Nametag"));
+ nav = get_node<NavigationAgent>(NodePath("NavigationAgent"));
+ Ref<PackedScene> p = ResourceLoader::get_singleton()->load("res://scenes/weapons/w_Rockets.tscn");
+ weapon = (Node *)p->instance();
+ add_child(weapon);
+ world = get_tree()->get_root()->get_node<Spatial>("GAMEWORLD");
+ cam = (PlayerCam *)world->call("get_cam");
+}
+
+Dictionary PlayerController::get_init_info() {
+ return Dictionary::make("linear_velocity", get_linear_velocity(), "angular_velocity", get_angular_velocity(), "controlling_machine", controlling_machine, "team", team, "health", health, "nametag", get_node<Label3D>("Nametag")->get_text());
+}
+
+void PlayerController::mp_init(Dictionary init_info) {
+ for(int i = 0; i < init_info.keys().size(); i++)
+ set(init_info.keys()[i], init_info[init_info.keys()[i]]);
+ nametag->set_text(init_info["nametag"]);
+}
+
+void PlayerController::set_phys_transform(Transform trfrm, Vector3 lvel) {
+ set_transform(trfrm);
+ set_linear_velocity(lvel);
+}
+
+void PlayerController::_process(float _delta) {
+ const Input *input_singleton = Input::get_singleton();
+ if(is_player && !world->call("is_chatting_f")){
+ if(input_singleton->is_action_just_pressed("use"))
+ initiate_use();
+ if(controlling_machine) {
+ if(input_singleton->is_action_just_pressed("fire"))machine->call("attack1");
+ if(input_singleton->is_action_just_pressed("fire2"))machine->call("attack2");
+ }
+ }
+}
+
+void PlayerController::initiate_use() {
+ if(controlling_machine){machine->call("relinquish_control"); return; }
+ if(ladder_m != nullptr) { leave_ladder(); return; }
+ if(useray->is_colliding()){
+ const Node *area_c = (Node *) useray->get_collider();
+ if(area_c->get_name() == "SteerArea")
+ world->rpc_id(1, "_call_on_server", "_client_request_control_vehicle", Dictionary::make("id", world->call("get_client_id"), "machine_path", area_c->get_parent()->get_path(), "char_name", get_name()));
+ else if(area_c->get_name() == "LadderArea")
+ mount_ladder((Spatial *)area_c->get_parent());
+ else if(area_c->get_name() == "TugArea")return;
+ else if(area_c->get_name() == "PickupArea")return;
+ }
+}
+
+void PlayerController::set_net_owner(int owner_id) {
+ nametag->set_text("");
+ set_network_master(owner_id);
+ if(owner_id != 1)
+ nametag->set_text(String(world->call("get_players_info")[owner_id][0]));
+ if(get_tree()->get_network_unique_id() != 1){
+ if(owner_id == (int)world->call("get_client_id")){
+ nametag->set_visible(false);
+ world->call("set_player_char", this);
+ is_player = true;
+ cam->attach(this, "FIRSTPERSON", "./Neck/Head");
+ }else{
+ nametag->set_visible(true);
+ is_player = false;
+ }
+ world->get_node("HUD")->call("update_characters");
+ }
+}
+
+void PlayerController::deselect_character() {
+ if(is_network_master()){
+ world->call("set_player_char", nullptr);
+ if((int)world->call("get_client_id") != 1)cam->attach(world, "STATIC", "./DEFAULTCAM");
+ rpc("set_net_owner", 1);
+ }
+}
+
+void PlayerController::take_control_of_machine(RigidBody *slave_machine) {
+ machine = slave_machine;
+ controlling_machine = true;
+}
+
+void PlayerController::lose_machine() {
+ if(is_network_master())cam->attach(this, "FIRSTPERSON", "./Neck/Head");
+ controlling_machine = false;
+ machine = nullptr;
+}
+
+void PlayerController::_physics_process(float delta) {
+ if(is_network_master()) {
+ if (ladder_m != nullptr)
+ climb_ladder(delta);
+ else if( !on_floor_test() && in_water)
+ swim(delta);
+ else
+ walk(delta);
+ is_on_floor = false;// #reset whether is on floor in between frames
+ }
+}
+
+bool PlayerController::on_floor_test() {
+ const RayCast *feet = get_node<RayCast>("Feet");
+ if(feet->is_colliding()){
+ is_on_floor = true;
+ floor_normal = Vector3::UP;
+ floorspeed = feet->get_collider()->has_method("get_linear_velocity") ? feet->get_collider()->get_linear_velocity() : Vector3::ZERO;
+ return true;
+ }
+ if(player_state){
+ for(int i = 0;i< player_state->get_contact_count()) {
+ float contact_angle_from_up = 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;
+}
+
+void PlayerController::_integrate_forces(PhysicsDirectBodyState *state) {
+ if(!is_network_master()) return;
+ player_state = state;
+ velocity = state->get_linear_velocity();
+ for(int i=0; i < player_state->get_contact_count(); i++) {
+ float contact_angle_from_up = Vector3::UP.angle_to(player_state->get_contact_local_normal(i));
+ if(contact_angle_from_up > FLOOR_MAX_ANGLE && !is_on_floor){
+ set_friction(0);
+ break;
+ }
+ if(i == player_state.get_contact_count() - 1)
+ set_friction(1);
+ }
+ rpc("set_phys_transform", get_transform(), get_linear_velocity());
+ if(get_global_transform().origin.y < -30)
+ rpc("damage", 500000, "drown", Array::make(1, String("Davy Jones"));
+}
+
+void PlayerController::walk(float _delta) {
+// # 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
+}
+
+void PlayerController::jump() {
+ can_jump = false;
+ apply_central_impulse(Vector3.UP*jump_height);
+ yield(get_tree().create_timer(0.05),"timeout");
+ can_jump = true;
+}
+
+void PlayerController::swim(float _delta) {
+// #drag and buoyancy
+ add_central_force(Vector3::UP*weight);
+ add_central_force(-100*linear_velocity);
+// #controls
+ Basis dir = head->get_global_transform().basis;
+ Vector3 m_dir = -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*get_weight()*0.5);
+}
+
+void PlayerController::enter_water() {
+ in_water = true;
+}
+
+void PlayerController::exit_water() {
+ in_water = false;
+}
+
+void PlayerController::mount_ladder(Spatial *target_ladder) {
+ Spatial *ladder_tracker = Spatial::new();
+ ladder_tracker->name = get_name();
+ target_ladder->add_child(ladder_tracker);
+ ladder_tracker->set_transform(target_ladder->get_node<Spatial>("BOTTOM")->get_transform();
+
+ ladder_tracker->set_global_transform( Transform(target_ladder->call("get_nearest_point_to_route", get_global_transform().origin), ladder_tracker->get_global_transform().basis ) );
+ look_at(get_global_transform().origin + target_ladder->get_global_transform().basis.x, target_ladder->get_global_transform().basis.y)
+
+ ladder_m = ladder_tracker;
+ Transform t = ladder_m->get_global_transform();
+ set_global_transform( Transform(t.origin, t.basis.orthonormalized()));
+ set_linear_velocity(Vector3::ZERO);
+ set_gravity_scale(0.0);
+}
+
+void PlayerController::climb_ladder(float delta) {
+ Vector3 new_ladder_pos = ladder_m->get_global_transform().origin + ladder_m->get_global_transform().basis.y.normalized() * move_axis.x * delta * climb_speed;
+ float prog = ladder_m->get_parent()->call("get_climb_scalar", new_ladder_pos)
+ if(prog >= 0.0 and && <= 1.0)
+ ladder_m->set_global_transform(Transform(new_ladder_pos, ladder_m->get_global_transform().basis));
+ Transform t = ladder_m->get_global_transform();
+ set_global_transform( Transform(t.origin, t.basis.orthonormalized()));
+}
+
+void PlayerController::leave_ladder() {
+ if((ladder_m->get_parent()->get_node<Spatial>("TOP")->global_transform.origin - get_global_transform().origin).length_squared() < 0.01)
+ apply_central_impulse(-400*ladder_m->get_global_transform().basis.z);
+ Transform t = get_global_transform();
+ t.basis = world->get_global_transform().basis;
+ set_global_transform(t);
+ set_gravity_scale(1.0);
+ ladder_m->queue_free();
+ ladder_m = nullptr;
+}
+
+void PlayerController::damage(int dmg_amt, String _type, Array shooter, String extra = ".") {
+ health -= dmg_amt;
+ int shooter_id = shooter[0];
+ String shooter_text = shooter[1];
+ if(health <= 0 && is_network_master()){
+ if(shooter_id != get_network_master() && shooter_id != 1) world->rpc_id(shooter_id, "game_killsound");
+ if(get_network_master() == 1)
+ world->call("_call_on_server", "_character_death", Dictionary::make("killer_id", shooter_id, "killer", shooter_text, "victim_mp_id", get_network_master(), "victim", get_name(), "extra", extra));
+ else
+ world->rpc_id(1, "_call_on_server", "_character_death", Dictionary::make("killer_id", shooter_id, "killer", shooter_text, "victim_mp_id", get_network_master(), "victim", get_name(), "extra", extra));
+ } else if( is_network_master())
+ if(shooter_id != get_network_master() && shooter_id != 1) world->rpc_id(shooter_id, "game_hitsound");
+}
+
+void PlayerController::remove_dead_character() {
+ if(is_network_master() && machine != nullptr)
+ machine->call("relinquish_control");
+ deselect_character();
+ queue_free();
+}
+
+void PlayerController::net_apply_impulse(Vector3 impulse_v) {
+ apply_central_impulse(impulse_v);
+}
+
+
diff --git a/src/player_controller/playercontroller.h b/src/player_controller/playercontroller.h
new file mode 100644
index 0000000..402f7d3
--- /dev/null
+++ b/src/player_controller/playercontroller.h
@@ -0,0 +1,93 @@
+#ifndef PLAYERCONTROLLER_H
+#define PLAYERCONTROLLER_H
+
+#include "playercam.h"
+#include <Godot.hpp>
+#include <RigidBody.hpp>
+#include <RayCast.hpp>
+#include <NavigationAgent.hpp>
+#include <PhysicsDirectBodyState.hpp>
+#include <Label3D.hpp>
+#include <SceneTree.hpp>
+#include <Viewport.hpp>
+#include <ClippedCamera.hpp>
+
+namespace godot {
+
+class PlayerController : public RigidBody {
+ GODOT_CLASS(PlayerController, RigidBody)
+
+private:
+ String team;
+ int health;
+ Node *weapon;
+ Spatial *world;
+ PlayerCam *cam;
+ float mouse_sensitivity;
+ float FOV;
+ Vector2 mouse_axis;
+ Spatial *head;
+ Spatial *neck;
+ RayCast *useray;
+ Label3D *nametag;
+ Vector3 velocity;
+ Vector3 direction;
+ Vector2 move_axis;
+ Vector3 floorspeed;
+ bool jumping;
+ bool can_jump;
+ NavigationAgent *nav;
+ float FLOOR_MAX_ANGLE;
+ float jump_height;
+ bool in_water;
+ float swim_speed;
+ float climb_speed;
+ bool controlling_machine;
+ RigidBody *machine;
+ bool is_player;
+ Spatial *ladder_m;
+ PhysicsDirectBodyState *player_state;
+ bool is_on_floor;
+ Vector3 floor_normal;
+ float acceleration;
+ float walk_speed;
+ float c_friction;
+ float air_control;
+
+
+public:
+ static void _register_methods();
+
+ PlayerController();
+ ~PlayerController();
+
+ void _init();
+
+ void _ready();
+ Dictionary get_init_info();
+ void mp_init(Dictionary init_info);
+ void set_phys_transform(Transform trfrm, Vector3 lvel);
+ void _process(float _delta);
+ void initiate_use();
+ void set_net_owner(int owner_id);
+ void deselect_character();
+ void take_control_of_machine(RigidBody *slave_machine);
+ void lose_machine();
+ void _physics_process(float delta);
+ bool on_floor_test();
+ void _integrate_forces(PhysicsDirectBodyState *state);
+ void walk(float _delta);
+ void jump();
+ void swim(float _delta);
+ void enter_water();
+ void exit_water();
+ void mount_ladder(Spatial *target_ladder);
+ void climb_ladder(float delta);
+ void leave_ladder();
+ void damage(int dmg_amt, String _type, Array shooter, String extra);
+ void remove_dead_character();
+ void net_apply_impulse(Vector3 impulse_v);
+};
+
+}
+#endif