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/mouse_sensitivity")] mouse_sensitivity: Vector2, input_state: InputState, current_force: f32, action_shooting: String, node_camera_arm_horizontal: Option>, node_camera_arm_vertical: Option>, node_camera: Option>, } #[methods] impl BasicDie { // Register the builder for methods, properties and/or signals. fn register_builder(_builder: &ClassBuilder) { 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 }, 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 horizontal camera arm match owner.get_node(NodePath::from_str("CameraArmHorizontal")) { Some(node) => { let save_node = node.assume_safe(); match save_node.cast::() { 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 child node called 'Camera 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::() { 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::() { 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) { 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) { 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), } } /// this input method will always be called, regardless of the input state unsafe fn general_input(&mut self, event: &Ref) { let save_event = event.assume_safe(); // get the input as mouse input let mouse_event = save_event.cast::(); 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) { let save_event = event.assume_safe(); godot_print!("default input"); // 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; } // get the input as mouse input let mouse_event = save_event.cast::(); 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) { let save_event = event.assume_safe(); godot_print!("shooting input"); if save_event.is_action_released(GodotString::from_str(&self.action_shooting), false) { self.input_state = InputState::Moving; self.shoot(owner); return; } // get the input as mouse input let mouse_event = save_event.cast::(); 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, _ => self.current_force + y_mov }; 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) { let save_event = event.assume_safe(); godot_print!("moving input"); // get the input as mouse input let mouse_event = save_event.cast::(); 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!"), } } }