diff options
Diffstat (limited to 'rust')
-rw-r--r-- | rust/src/basic_die.rs | 376 | ||||
-rw-r--r-- | rust/src/buff_ball.rs | 91 | ||||
-rw-r--r-- | rust/src/buff_bounce.rs | 52 | ||||
-rw-r--r-- | rust/src/buff_extra.rs | 4 | ||||
-rw-r--r-- | rust/src/buff_phase.rs | 27 | ||||
-rw-r--r-- | rust/src/game.rs | 3 | ||||
-rw-r--r-- | rust/src/lib.rs | 4 |
7 files changed, 451 insertions, 106 deletions
diff --git a/rust/src/basic_die.rs b/rust/src/basic_die.rs new file mode 100644 index 0000000..dbcccc8 --- /dev/null +++ b/rust/src/basic_die.rs @@ -0,0 +1,376 @@ +use std::borrow::Borrow; +use gdnative::api::*; +use gdnative::prelude::*; +use crate::buff_trait::Buff; +use crate::buff_ball::BuffBall; +use crate::buff_bounce::BuffBounce; +use crate::buff_phase::BuffPhase; +use crate::buff_extra::BuffExtra; + +/// the input state for the player +enum InputState { + Default, + Shooting, + Moving +} + + +type SpatialRef = Option<Ref<Spatial>>; + +/// the basic die used by the player +#[derive(NativeClass)] +#[inherit(Spatial)] +#[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="shooting/stopping_min_milliseconds")] + stopping_min_ms: i64, + #[property(path="input/camera_mouse_sensitivity")] + mouse_sensitivity: Vector2, + #[property(path="input/shoot_sensitivity")] + shoot_sensitivity: f32, + #[property(path="input/current_buff_index")] + current_buff_index: i32, + + all_buffs: [Option<Box<dyn Buff>>; 5], + + input_state: InputState, + current_force: f32, + last_shot_time: i64, + + action_shooting: String, + + node_die: SpatialRef, + + node_camera_root: SpatialRef, + node_camera_arm_horizontal: SpatialRef, + node_camera_arm_vertical: SpatialRef, + node_camera: SpatialRef, +} + +#[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: &Spatial) -> Self { + BasicDie { + camera_clamp: Vector2 { x: 0.0, y: 0.0 }, + max_force: 0.0, + up_angle: 5.0, + stopping_velocity: 0.0, + stopping_min_ms: 1000, + mouse_sensitivity: Vector2 { x: 1.0, y: 1.0 }, + shoot_sensitivity: 1.0, + current_buff_index: 0, + all_buffs: [None, None, None, None, None], + + input_state: InputState::Default, + current_force: 0.0, + last_shot_time: 0, + + action_shooting: String::from("mouse_btn_left"), + + node_die: None, + + node_camera_root: None, + node_camera_arm_horizontal: None, + node_camera_arm_vertical: None, + node_camera: None, + } + } + + #[export] + unsafe fn _ready(&mut self, owner: &Spatial) { + + Input::godot_singleton().set_mouse_mode(Input::MOUSE_MODE_CAPTURED); + owner.set_physics_process(true); + + self.last_shot_time = OS::godot_singleton().get_ticks_msec(); + + let search_node = | parent: &SpatialRef, name: String | { + let parent_node = match parent { + Some(node) => node.assume_safe().as_ref(), + None => owner + }; + match parent_node.get_node(NodePath::from_str(&name)) { + None => { godot_warn!("Could not find {}.", name); None }, + Some(node) => { + let save_node = node.assume_safe(); + match save_node.cast::<Spatial>() { + None => { godot_warn!("{} was not of type 'Spatial'.", name); None }, + Some(casted) => Some(casted.claim()) + } + } + } + }; + + // look for the camera and arm nodes + self.node_camera_root = search_node(&None, String::from("Camera")); + self.node_camera_arm_horizontal = search_node(&self.node_camera_root, String::from("CameraArmHorizontal")); + self.node_camera_arm_vertical = search_node(&self.node_camera_arm_horizontal, String::from("CameraArmVertical")); + self.node_camera = search_node(&self.node_camera_arm_vertical, String::from("Camera")); + + godot_print!("{:?}", self.node_camera_root); + godot_print!("{:?}", self.node_camera_arm_horizontal); + godot_print!("{:?}", self.node_camera_arm_vertical); + godot_print!("{:?}", self.node_camera); + + self.node_die = search_node(&None, String::from("W8")); + + let die = match self.node_die { + None => { godot_warn!("No W8 assigned."); return; }, + Some(node) => + match node.assume_safe().cast::<RigidBody>() { + None => { godot_warn!("W8 is not a RigidBody."); return; }, + Some(rb) => rb.claim() + } + }; + + self.all_buffs = [ + None, + Some(Box::new(BuffBall ::new(Box::new(die)))), + Some(Box::new(BuffBounce::new(Box::new(die)))), + Some(Box::new(BuffPhase ::new(Box::new(die)))), + Some(Box::new(BuffExtra ::new())), + ]; + + godot_print!("Current Buff: {}", self.current_buff_index); + godot_print!("Player is ready"); + } + + #[export] + unsafe fn _physics_process(&mut self, _owner: &Spatial, _delta: f64) { + + let die = match self.node_die { + None => { godot_warn!("No W8 assigned."); return; }, + Some(node) => match node.assume_safe().cast::<RigidBody>() { + None => { godot_warn!("W8 is not a RigidBody."); return; }, + Some(rb) => rb + } + }; + + // let the cam follow the die + match self.node_camera_root { + None => { godot_warn!("No W8 assigned."); }, + Some(cam) => { + cam.assume_safe().set_translation(die.translation()); + } + } + + let delta_ms = OS::godot_singleton().get_ticks_msec() - self.last_shot_time; + + // deactivate the Ball Buff after 5 seconds + if matches!(self.input_state, InputState::Moving) + && delta_ms > 5000 + && self.current_buff_index == 1 { + match self.all_buffs[1] { + Some(ref mut buff) => buff.revert_buff(), + None => {} + } + } + + // detect if the die stops moving + if matches!(self.input_state, InputState::Moving) + && (delta_ms > self.stopping_min_ms) { + + // check if the velocity is less than the threshold and change input state in that case + let current_vel = die.linear_velocity().length(); + if current_vel <= self.stopping_velocity { + self.input_state = InputState::Default; + godot_print!("Die stopped moving at velocity {} after {} ms", current_vel, delta_ms); + + //deactivate the old buff + godot_print!("Current Buff: {}", self.current_buff_index); + match self.all_buffs[self.current_buff_index as usize] { + Some(ref mut buff) => buff.revert_buff(), + None => {} + } + //TODO: find out which side is up and update the current buff index accordingly + } + }; + } + + #[export] + unsafe fn _input(&mut self, _owner: &Spatial, event: Ref<InputEvent>) { + + self.general_input(event.borrow()); + + match self.input_state { + InputState::Default => self.default_input(event), + InputState::Shooting => self.shooting_input(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<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); + }, + _ => {} + } + + let key_event = save_event.cast::<InputEventKey>(); + match key_event { + Some(key_event) => { + if key_event.scancode() == GlobalConstants::KEY_ESCAPE { + Input::godot_singleton().set_mouse_mode(Input::MOUSE_MODE_VISIBLE); + } + }, + _ => {} + } + } + + /// 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, 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(); + self.current_force = 0.0; + 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.shoot_sensitivity; + 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) { + + // make sure the camera actually exists + let impulse_dir = match self.node_camera { + None => { godot_warn!("No camera assigned!"); return; }, + Some(cam) => { + // get the forward vector of the camera setting the up angle to the defined value in the editor + let mut forward_vector = -cam.assume_safe().global_transform().basis.c().normalized(); + forward_vector.y = self.up_angle; + + // calculate the impulse force + forward_vector.normalized() * self.current_force + } + }; + + // get the die + let die = match self.node_die { + None => { godot_warn!("No W8 assigned."); return; }, + Some(node) => + match node.assume_safe().cast::<RigidBody>() { + None => { godot_warn!("W8 is not a RigidBody."); return; }, + Some(rb) => rb + } + }; + + // apply the current buff + godot_print!("Current Buff: {}", self.current_buff_index); + match self.all_buffs[self.current_buff_index as usize] { + Some(ref mut buff) => buff.execute_buff(), + None => {} + } + + // actually add the force + die.apply_impulse(die.transform().origin, impulse_dir); + + self.last_shot_time = OS::godot_singleton().get_ticks_msec(); + } +} diff --git a/rust/src/buff_ball.rs b/rust/src/buff_ball.rs index 4b89dbb..219e675 100644 --- a/rust/src/buff_ball.rs +++ b/rust/src/buff_ball.rs @@ -2,71 +2,66 @@ use gdnative::api::*; use gdnative::prelude::*; use crate::buff_trait::Buff; -struct BuffBall { +pub struct BuffBall { name: String, description: String, - - die_body: Option<Ref<RigidBody>>, - sphere_body: Option<Ref<RigidBody>> + collision_die: Ref<CollisionShape>, + collision_sphere: Ref<CollisionShape>, + mesh_die: Ref<MeshInstance>, + mesh_sphere: Ref<MeshInstance>, } impl BuffBall { - fn new(die_body: Option<Ref<RigidBody>>, sphere_body: Option<Ref<RigidBody>>) -> Self { + + pub fn new(die_body: Box<Ref<RigidBody>>) -> Self { + + // find a collision shape + fn search<T: SubClass<Node>> (die_body: &Box<Ref<RigidBody>>, name: String) -> Option<Ref<T>> { + unsafe{ + match die_body.assume_safe().get_node(&name) { + None => { + godot_warn!("Could not find {}", name); + None + } + Some(node) => { + match node.assume_safe().cast::<T>() { + None => { + godot_warn!("{} is not a {}", name, std::any::type_name::<T>()); + None + }, + Some(cs) => Some(cs.claim()) + } + } + } + } + } + BuffBall { name: String::from("Ball"), description: String::from("Roll the dice"), - - die_body, - sphere_body + collision_die: search::<CollisionShape>(&die_body, String::from("CollisionShapeDie" )).unwrap(), + collision_sphere: search::<CollisionShape>(&die_body, String::from("CollisionShapeSphere")).unwrap(), + mesh_die: search::<MeshInstance>(&die_body, String::from("MeshDie" )).unwrap(), + mesh_sphere: search::<MeshInstance>(&die_body, String::from("MeshSphere")).unwrap(), } } } impl Buff for BuffBall { unsafe fn execute_buff(&mut self) { - // make sure the sphere rigid body exists - match &self.sphere_body { - Some(sphere) => { - // make sure the dice rigid body exists - match &self.die_body { - Some(die) => { - // get the safe references from the ref<> - let save_sphere = sphere.assume_safe(); - let save_die = die.assume_safe(); - - // set the properties of the rigid bodies - save_die.set_process(false); - save_sphere.set_process(true); - save_sphere.show(); - }, - None => godot_warn!("Die body not assigned") - } - }, - None => godot_warn!("Sphere body not assigned") - } + self.collision_die.assume_safe().set_disabled(true); + self.collision_sphere.assume_safe().set_disabled(false); + self.mesh_die.assume_safe().set_visible(false); + self.mesh_sphere.assume_safe().set_visible(true); + godot_print!("Ball activated"); } unsafe fn revert_buff(&mut self) { - // make sure the sphere rigid body exists - match &self.sphere_body { - Some(sphere) => { - // make sure the dice rigid body exists - match &self.die_body { - Some(die) => { - // get the safe references from the ref<> - let save_sphere = sphere.assume_safe(); - let save_die = die.assume_safe(); - - // set the properties of the rigid bodies - save_die.set_process(true); - save_sphere.set_process(false); - save_sphere.hide(); - }, - None => godot_warn!("Die body not assigned") - } - }, - None => godot_warn!("Sphere body not assigned") - } + self.collision_die.assume_safe().set_disabled(false); + self.collision_sphere.assume_safe().set_disabled(true); + self.mesh_sphere.assume_safe().set_visible(false); + self.mesh_die.assume_safe().set_visible(true); + godot_print!("Ball deactivated"); } fn get_name(self) -> GodotString { diff --git a/rust/src/buff_bounce.rs b/rust/src/buff_bounce.rs index 179642d..cb9ded8 100644 --- a/rust/src/buff_bounce.rs +++ b/rust/src/buff_bounce.rs @@ -2,22 +2,21 @@ use gdnative::api::*; use gdnative::prelude::*; use crate::buff_trait::Buff; -struct BuffBounce { +pub struct BuffBounce { name: String, description: String, - rigid_body: Option<Ref<RigidBody>>, + rigid_body: Box<Ref<RigidBody>>, target_bounciness: f64, previous_bounciness: f64 } impl BuffBounce { - fn new(rigid_body: Option<Ref<RigidBody>>) -> Self { + pub fn new(rigid_body: Box<Ref<RigidBody>>) -> Self { BuffBounce { name: String::from("Bounce"), description: String::from("Let's the die bounce more than usual."), rigid_body, - target_bounciness: 1.0, previous_bounciness: 0.0 } @@ -25,43 +24,30 @@ impl BuffBounce { } impl Buff for BuffBounce { - unsafe fn execute_buff(&mut self) { - // make sure the rigid body exists - match &self.rigid_body { - Some(body) => { - let safe_body = body.assume_safe(); - // get the physics material - match safe_body.physics_material_override() { - Some(mat) => { - let save_mat = mat.assume_safe(); - self.previous_bounciness = save_mat.bounce(); - save_mat.set_bounce(self.target_bounciness); - }, - None => godot_warn!("Physics material was not found") - } + unsafe fn execute_buff(&mut self) { + // get the physics material + match self.rigid_body.assume_safe().physics_material_override() { + Some(mat) => { + let save_mat = mat.assume_safe(); + self.previous_bounciness = save_mat.bounce(); + save_mat.set_bounce(self.target_bounciness); }, - None => godot_warn!("No rigid body initialized to apply properties to") + None => godot_warn!("Physics material was not found") } + godot_print!("Bounce activated"); } unsafe fn revert_buff(&mut self) { - // make sure the rigid body exists - match &self.rigid_body { - Some(body) => { - let safe_body = body.assume_safe(); - - // get the physics material - match safe_body.physics_material_override() { - Some(mat) => { - let save_mat = mat.assume_safe(); - save_mat.set_bounce(self.previous_bounciness); - }, - None => godot_warn!("Physics material was not found") - } + // get the physics material + match self.rigid_body.assume_safe().physics_material_override() { + Some(mat) => { + let save_mat = mat.assume_safe(); + save_mat.set_bounce(self.previous_bounciness); }, - None => godot_warn!("No rigid body initialized to apply properties to") + None => godot_warn!("Physics material was not found") } + godot_print!("Bounce deactivated"); } fn get_name(self) -> GodotString { diff --git a/rust/src/buff_extra.rs b/rust/src/buff_extra.rs index 473cdde..a7b7902 100644 --- a/rust/src/buff_extra.rs +++ b/rust/src/buff_extra.rs @@ -2,13 +2,13 @@ use gdnative::api::*; use gdnative::prelude::*; use crate::buff_trait::Buff; -struct BuffExtra { +pub struct BuffExtra { name: String, description: String, } impl BuffExtra { - fn new() -> Self { + pub fn new() -> Self { BuffExtra { name: String::from("Extra Stroke"), description: String::from("One additional stroke that doesn't count"), diff --git a/rust/src/buff_phase.rs b/rust/src/buff_phase.rs index e64508e..f69fecc 100644 --- a/rust/src/buff_phase.rs +++ b/rust/src/buff_phase.rs @@ -2,16 +2,16 @@ use gdnative::api::*; use gdnative::prelude::*; use crate::buff_trait::Buff; -struct BuffPhase { +pub struct BuffPhase { name: String, description: String, - rigid_body: Option<Ref<RigidBody>>, + rigid_body: Box<Ref<RigidBody>>, wall_layer_bit: i64, } impl BuffPhase { - fn new(rigid_body: Option<Ref<RigidBody>>) -> Self { + pub fn new(rigid_body: Box<Ref<RigidBody>>) -> Self { // calculate the bit for the mask to enable/ disable collision detection let mask_layer = 2; let mask_bit = 2_i64.pow(mask_layer - 1); @@ -20,7 +20,6 @@ impl BuffPhase { name: String::from("Phase"), description: String::from("Phases through thin fences"), rigid_body, - wall_layer_bit: mask_bit, } } @@ -28,26 +27,12 @@ impl BuffPhase { impl Buff for BuffPhase { unsafe fn execute_buff(&mut self) { - match &self.rigid_body { - Some(body) => { - // actually disable the collision to the fences - let save_body = body.assume_safe(); - save_body.set_collision_mask_bit(self.wall_layer_bit, false); - }, - None => godot_warn!("Rigid body not found") - } + // actually disable the collision to the fences + self.rigid_body.assume_safe().set_collision_mask_bit(self.wall_layer_bit, false); } unsafe fn revert_buff(& mut self) { - // make sure the rigid body exists - match &self.rigid_body { - Some(body) => { - // actually enable the collision again - let save_body = body.assume_safe(); - save_body.set_collision_mask_bit(self.wall_layer_bit, false); - }, - None => godot_warn!("Rigid body not found") - } + self.rigid_body.assume_safe().set_collision_mask_bit(self.wall_layer_bit, false); } fn get_name(self) -> GodotString { diff --git a/rust/src/game.rs b/rust/src/game.rs index 7004f96..61b23ed 100644 --- a/rust/src/game.rs +++ b/rust/src/game.rs @@ -40,6 +40,5 @@ impl Game { // This function will be called in every frame #[export] - unsafe fn _process(&self, _owner: &Spatial, delta: f64) { - } + unsafe fn _process(&self, _owner: &Spatial, _delta: f64) { } } diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 401b668..1be319a 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -6,6 +6,8 @@ mod buff_trait; mod buff_ball; mod buff_extra; pub mod goal_trigger; +mod test; +mod basic_die; use gdnative::prelude::{godot_init, InitHandle}; @@ -14,6 +16,8 @@ fn init(handle: InitHandle) { handle.add_class::<game::Game>(); handle.add_class::<spinning_cube::SpinningCube>(); handle.add_class::<goal_trigger::GoalTriggerZone>(); + handle.add_class::<test::SpinningCubeReverse>(); + handle.add_class::<basic_die::BasicDie>(); } // macros that create the entry-points of the dynamic library. |