use std::borrow::Borrow;
use gdnative::api::*;
use gdnative::prelude::*;
/// the input state for the player
enum InputState {
Default,
Shooting,
Moving
}
/// the basic die used by the player
#[derive(NativeClass)]
#[inherit(RigidBody)]
#[register_with(Self::register_builder)]
pub struct BasicDie {
#[property(path="camera/camera_clamp")]
camera_clamp: Vector2,
#[property(path="shooting/max_force")]
max_force: f32,
#[property(path="shooting/up_angle")]
up_angle: f32,
#[property(path="shooting/stopping_velocity")]
stopping_velocity: f32,
#[property(path="input/camera_mouse_sensitivity")]
mouse_sensitivity: Vector2,
#[property(path="input/shoot_sensitivity")]
shoot_sensitivity: f32,
input_state: InputState,
current_force: f32,
action_shooting: String,
node_camera_arm_horizontal: Option<Ref<Spatial>>,
node_camera_arm_vertical: Option<Ref<Spatial>>,
node_camera: Option<Ref<Spatial>>,
}
#[methods]
impl BasicDie {
// Register the builder for methods, properties and/or signals.
fn register_builder(_builder: &ClassBuilder<Self>) {
godot_print!("BasicDie builder is registered!");
}
fn new(_owner: &RigidBody) -> Self {
BasicDie {
camera_clamp: Vector2 { x: 0.0, y: 0.0 },
max_force: 0.0,
up_angle: 5.0,
stopping_velocity: 0.0,
mouse_sensitivity: Vector2 { x: 1.0, y: 1.0 },
shoot_sensitivity: 1.0,
input_state: InputState::Default,
current_force: 0.0,
action_shooting: String::from("mouse_btn_left"),
node_camera_arm_horizontal: None,
node_camera_arm_vertical: None,
node_camera: None,
}
}
#[export]
unsafe fn _ready(&mut self, owner: &RigidBody) {
owner.set_physics_process(true);
// look for the vertical camera arm
match self.node_camera_arm_horizontal {
Some(arm) => {
let save_arm = arm.assume_safe();
match save_arm.get_node(NodePath::from_str("CameraArmVertical")) {
Some(node) => {
let save_node = node.assume_safe();
match save_node.cast::<Spatial>() {
Some(casted) => {
let save_casted = casted.claim();
self.node_camera_arm_vertical = Some(save_casted)},
_ => godot_warn!("Camera Arm was not of type 'Spatial'"),
}
},
_ => godot_warn!("No vertical arm found.")
}
},
_ => godot_warn!("No horizontal arm to look for the vertical arm")
}
// look for the camera
match self.node_camera_arm_vertical {
Some(arm) => {
let save_arm = arm.assume_safe();
match save_arm.get_node(NodePath::from_str("Camera")) {
Some(node) => {
let save_node = node.assume_safe();
match save_node.cast::<Spatial>() {
Some(casted) => {
let save_casted = casted.claim();
self.node_camera = Some(save_casted)},
_ => godot_warn!("Camera was not of type 'Spatial'"),
}
},
_ => godot_warn!("No camera found.")
}
},
_ => godot_warn!("No vertical arm to look for the camera")
}
}
#[export]
unsafe fn _physics_process(&mut self, owner: &RigidBody, _delta: f64) {
// detect if the die stops moving
if matches!(self.input_state, InputState::Moving) {
// get the current velocity
let current_vel = owner.linear_velocity().length();
godot_print!("current velocity: {}", current_vel);
// check if the velocity is less than the threshold and change input state in that case
if current_vel <= self.stopping_velocity {
self.input_state = InputState::Default;
}
};
}
#[export]
unsafe fn _input(&mut self, owner: &RigidBody, event: Ref<InputEvent>) {
self.general_input(event.borrow());
match self.input_state {
InputState::Default => self.default_input(event),
InputState::Shooting => self.shooting_input(owner, event),
InputState::Moving => self.moving_input(event),
}
// look for the horizontal camera arm
match owner.get_node(NodePath::from_str("CameraArmHorizontal")) {
Some(node) => {
let save_node = node.assume_safe();
match save_node.cast::<Spatial>() {
Some(casted) => {
let save_casted = casted.claim();
self.node_camera_arm_horizontal = Some(save_casted)},
_ => godot_warn!("Camera Arm was not of type 'Spatial'"),
}
},
_ => godot_warn!("No horizontal arm found")
}
// look for the vertical camera arm
match self.node_camera_arm_horizontal {
Some(arm) => {
let save_arm = arm.assume_safe();
match save_arm.get_node(NodePath::from_str("CameraArmVertical")) {
Some(node) => {
let save_node = node.assume_safe();
match save_node.cast::<Spatial>() {
Some(casted) => {
let save_casted = casted.claim();
self.node_camera_arm_vertical = Some(save_casted)},
_ => godot_warn!("Camera Arm was not of type 'Spatial'"),
}
},
_ => godot_warn!("No vertical arm found.")
}
},
_ => godot_warn!("No horizontal arm to look for the vertical arm")
}
// look for the camera
match self.node_camera_arm_vertical {
Some(arm) => {
let save_arm = arm.assume_safe();
match save_arm.get_node(NodePath::from_str("Camera")) {
Some(node) => {
let save_node = node.assume_safe();
match save_node.cast::<Spatial>() {
Some(casted) => {
let save_casted = casted.claim();
self.node_camera = Some(save_casted)},
_ => godot_warn!("Camera was not of type 'Spatial'"),
}
},
_ => godot_warn!("No camera found.")
}
},
_ => godot_warn!("No vertical arm to look for the camera")
}
}
/// this input method will always be called, regardless of the input state
unsafe fn general_input(&mut self, event: &Ref<InputEvent>) {
let save_event = event.assume_safe();
// rotate camera horizontally
let mouse_event = save_event.cast::<InputEventMouseMotion>(); // get the input as mouse input
match mouse_event {
Some(motion_event) => {
let x_mov = motion_event.relative().x * self.mouse_sensitivity.x;
self.rotate_cam_horizontal(x_mov);
},
_ => {}
}
}
/// this input method will be called when looking around, before taking a shot
unsafe fn default_input(&mut self, event: Ref<InputEvent>) {
let save_event = event.assume_safe();
// left mouse button was pressed => switch to shooting mode
if save_event.is_action_pressed(GodotString::from_str(&self.action_shooting), false, false) {
godot_print!("mouse_button, switching to shooting mode");
self.input_state = InputState::Shooting;
return;
}
// rotate camera vertically
let mouse_event = save_event.cast::<InputEventMouseMotion>(); // get the input as mouse input
match mouse_event {
Some(motion_event) => {
let y_mov = -motion_event.relative().y * self.mouse_sensitivity.y;
self.rotate_cam_vertical(y_mov);
},
_ => {}
};
}
/// this input method will be called when player is currently taking a shot
unsafe fn shooting_input(&mut self, owner: &RigidBody, event: Ref<InputEvent>) {
let save_event = event.assume_safe();
// mouse released, shoot
if save_event.is_action_released(GodotString::from_str(&self.action_shooting), false) {
self.input_state = InputState::Moving;
self.shoot(owner);
return;
}
// charge shot with vertical mouse movement
let mouse_event = save_event.cast::<InputEventMouseMotion>(); // get the input as mouse input
match mouse_event {
Some(motion_event) => {
let y_mov = motion_event.relative().y * self.mouse_sensitivity.y;
self.current_force = match self.current_force + y_mov {
x if x < 0.0 => 0.0,
x if x > self.max_force => self.max_force,
x => x
};
godot_print!("current force: {}", self.current_force);
},
_ => {}
};
}
/// this input method will be called when player is moving
unsafe fn moving_input(&mut self, event: Ref<InputEvent>) {
let save_event = event.assume_safe();
// rotate camera vertically
let mouse_event = save_event.cast::<InputEventMouseMotion>(); // get the input as mouse input
match mouse_event {
Some(motion_event) => {
let y_mov = motion_event.relative().y * self.mouse_sensitivity.y;
self.rotate_cam_vertical(y_mov);
},
_ => {}
};
}
unsafe fn rotate_cam_horizontal(&mut self, input: f32) {
// make sure the arm exists
match self.node_camera_arm_horizontal {
Some(arm) => {
// rotate the horizontal camera arm
let save_arm = arm.assume_safe();
save_arm.rotate_object_local(Vector3 {x: 0.0, y: 1.0, z: 0.0}, input as f64)
},
_ => godot_warn!("No horizontal camera arm assigned.")
}
}
unsafe fn rotate_cam_vertical(&mut self, input: f32) {
// make sure the camera arm actually exists
match self.node_camera_arm_vertical {
Some(arm) => {
// check for the current rotation
let save_arm = arm.assume_safe();
let current_rot = save_arm.rotation();
// clamp the rotation
if current_rot.x + input > self.camera_clamp.x || current_rot.x + input <= self.camera_clamp.y {
return;
}
// actually rotate if possible
let save_arm = arm.assume_safe();
save_arm.rotate_object_local(Vector3 {x: 1.0, y: 0.0, z: 0.0}, input as f64)
},
_ => godot_warn!("No vertical camera arm assigned")
}
}
unsafe fn shoot(&mut self, owner: &RigidBody) {
// make sure the camera actually exists
match self.node_camera {
Some(cam) => {
// get the base position of the node
let base_pos = owner.transform().origin;
// get the save reference
let save_cam = cam.assume_safe();
// get the forward vector of the camera setting the up angle to the defined value in the editor
let mut forward_vector = save_cam.global_transform().basis.c();
forward_vector.y = self.up_angle;
// calculate the impulse force
let impulse = forward_vector.normalized() * self.current_force;
// actually add the force
owner.apply_impulse(base_pos, impulse);
},
None => godot_warn!("No camera assigned!"),
}
}
}