aboutsummaryrefslogblamecommitdiffstats
path: root/rust/src/BasicDie.rs
blob: 7bc6d656c0cb632e1d0f6e7cdbc37a91a265b760 (plain) (tree)














































































































































































































































                                                                                                                
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="base/camera_offset")]
    camera_offset: f32,
    #[property(path="base/camera_clamp")]
    camera_clamp: Vector2,
    #[property(path="shooting/max_force")]
    max_force: f32,
    #[property(path="shooting/up_angle")]
    up_angle: f32,
    #[property(path="input/mouse_sensitivity")]
    mouse_sensitivity: Vector2,

    input_state: InputState,
    current_force: f32,

    action_shooting: String,

    node_camera_arm_horizontal: Option<Ref<Spatial>>,
    node_camera_arm_vertical: 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_offset: 0.0,
            camera_clamp: Vector2 { x: 0.0, y: 0.0 },
            max_force: 0.0,
            up_angle: 5.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,
        }
    }

    #[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::<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 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::<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")
        }
    }

    #[export]
    unsafe fn _physics_process(&mut self, owner: &RigidBody, delta: f64) {

        if matches!(self.input_state, InputState::Moving) {
            // TODO check if velocity reached a certain threshold and set the mode to default again
        };
    }

    #[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(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();

        // get the input as mouse input
        let mouse_event = save_event.cast::<InputEventMouseMotion>();
        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();

        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::<InputEventMouseMotion>();
        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();

        godot_print!("shooting input");

        if save_event.is_action_released(GodotString::from_str(&self.action_shooting), false) {
            self.input_state = InputState::Moving;
            // TODO add force based on input and strength
            return;
        }

        // get the input as mouse input
        let mouse_event = save_event.cast::<InputEventMouseMotion>();
        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<InputEvent>) {
        let save_event = event.assume_safe();

        godot_print!("moving input");

        // get the input as mouse input
        let mouse_event = save_event.cast::<InputEventMouseMotion>();
        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();
                godot_print!("current rotation: {} | {} | {} ", current_rot.x, current_rot.y, current_rot.z);

                // 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")
        }
    }
}