155 lines
4.1 KiB
Rust
155 lines
4.1 KiB
Rust
use std::f32::consts::PI;
|
|
|
|
use godot::{
|
|
classes::{
|
|
Camera3D, CharacterBody3D, ICharacterBody3D, Input, InputEvent, InputEventMouseMotion,
|
|
input::MouseMode,
|
|
},
|
|
obj::WithBaseField,
|
|
prelude::*,
|
|
};
|
|
|
|
use crate::ui::cli::CommandLinePanel;
|
|
|
|
pub trait Player: ICharacterBody3D + WithBaseField {
|
|
fn get_y_speed(&self) -> f32;
|
|
fn set_y_speed(&mut self, speed: f32);
|
|
fn get_movement_dir(&self) -> Vector3;
|
|
fn get_move_speed(&self) -> f32;
|
|
}
|
|
|
|
pub trait PlayerMovement {
|
|
fn player_update(&mut self, delta: f64);
|
|
fn jump(&mut self);
|
|
}
|
|
|
|
impl<T: Player + WithBaseField> PlayerMovement for T {
|
|
fn player_update(&mut self, delta: f64) {
|
|
self.set_y_speed(self.get_y_speed() - 9.8 * delta as f32);
|
|
if self.base().is_on_floor() && self.get_y_speed() < 0. {
|
|
self.set_y_speed(0.);
|
|
}
|
|
|
|
let speed = Vector3::UP * self.get_y_speed()
|
|
+ self
|
|
.get_movement_dir()
|
|
.try_normalized()
|
|
.unwrap_or_default()
|
|
.rotated(Vector3::UP, self.base().get_rotation().y)
|
|
* self.get_move_speed();
|
|
|
|
self.base_mut().set_velocity(speed);
|
|
self.base_mut().move_and_slide();
|
|
}
|
|
|
|
fn jump(&mut self) {
|
|
self.set_y_speed(5.);
|
|
}
|
|
}
|
|
|
|
#[derive(GodotClass)]
|
|
#[class(base=CharacterBody3D, init)]
|
|
pub struct LocalPlayer {
|
|
#[export]
|
|
look_sensitivity: f32,
|
|
#[export]
|
|
camera: Option<Gd<Camera3D>>,
|
|
#[export]
|
|
move_speed: f32,
|
|
|
|
movement_dir: Vector3,
|
|
y_speed: f32,
|
|
|
|
locked: bool,
|
|
|
|
base: Base<CharacterBody3D>,
|
|
}
|
|
|
|
impl Player for LocalPlayer {
|
|
fn get_y_speed(&self) -> f32 {
|
|
self.y_speed
|
|
}
|
|
|
|
fn set_y_speed(&mut self, speed: f32) {
|
|
self.y_speed = speed;
|
|
}
|
|
|
|
fn get_movement_dir(&self) -> Vector3 {
|
|
self.movement_dir
|
|
}
|
|
|
|
fn get_move_speed(&self) -> f32 {
|
|
self.move_speed
|
|
}
|
|
}
|
|
|
|
#[godot_api]
|
|
impl ICharacterBody3D for LocalPlayer {
|
|
fn ready(&mut self) {
|
|
if !CommandLinePanel::singleton().bind().opened {
|
|
self.set_lock(true);
|
|
}
|
|
|
|
CommandLinePanel::singleton()
|
|
.signals()
|
|
.on_open()
|
|
.connect_other(self, |s| s.set_lock(false));
|
|
CommandLinePanel::singleton()
|
|
.signals()
|
|
.on_close()
|
|
.connect_other(self, |s| {
|
|
s.set_lock(true);
|
|
});
|
|
}
|
|
|
|
fn process(&mut self, delta: f64) {
|
|
let input_direction = if self.locked {
|
|
let input = Input::singleton();
|
|
|
|
if self.base().is_on_floor() && input.is_action_just_pressed("jump") {
|
|
self.jump();
|
|
}
|
|
|
|
Vector3::RIGHT * input.is_action_pressed("right") as u32 as f32
|
|
- Vector3::RIGHT * input.is_action_pressed("left") as u32 as f32
|
|
+ Vector3::FORWARD * input.is_action_pressed("forward") as u32 as f32
|
|
- Vector3::FORWARD * input.is_action_pressed("backward") as u32 as f32
|
|
} else {
|
|
Vector3::ZERO
|
|
};
|
|
self.movement_dir = input_direction;
|
|
|
|
self.player_update(delta);
|
|
}
|
|
|
|
fn input(&mut self, event: Gd<InputEvent>) {
|
|
if self.locked {
|
|
if let Ok(event) = event.try_cast::<InputEventMouseMotion>() {
|
|
let x = event.get_relative().x * 0.001 * self.look_sensitivity;
|
|
let y = event.get_relative().y * 0.001 * self.look_sensitivity;
|
|
|
|
if let Some(camera) = self.camera.as_mut() {
|
|
let curr_rotation = camera.get_rotation();
|
|
let x_rot =
|
|
(curr_rotation.x - y * self.look_sensitivity).clamp(-PI / 2., PI / 2.);
|
|
camera.set_rotation(Vector3::RIGHT * x_rot);
|
|
}
|
|
|
|
let y_rot = -x * self.look_sensitivity;
|
|
self.base_mut().rotate_y(y_rot);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl LocalPlayer {
|
|
fn set_lock(&mut self, locked: bool) {
|
|
self.locked = locked;
|
|
if self.locked {
|
|
Input::singleton().set_mouse_mode(MouseMode::CAPTURED);
|
|
} else {
|
|
Input::singleton().set_mouse_mode(MouseMode::VISIBLE);
|
|
}
|
|
}
|
|
}
|