use specs::{Component, VecStorage};
use crate::{RigidBody, Vec3};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub struct BrainState {
pub heading: f32,
pub running: bool,
pub jumping: bool,
pub sprinting: bool,
pub jump_count: u32,
pub is_jumping: bool,
pub current_jump_time: f32,
}
impl Default for BrainState {
fn default() -> Self {
Self {
heading: 0.0,
running: false,
jumping: false,
sprinting: false,
jump_count: 0,
is_jumping: false,
current_jump_time: 0.0,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BrainOptions {
pub max_speed: f32,
pub move_force: f32,
pub responsiveness: f32,
pub running_friction: f32,
pub standing_friction: f32,
pub air_move_mult: f32,
pub jump_impulse: f32,
pub jump_force: f32,
pub jump_time: f32, pub air_jumps: u32,
pub sprint_speed_mult: f32,
pub sprint_force_mult: f32,
}
impl Default for BrainOptions {
fn default() -> Self {
Self {
max_speed: 6.0,
move_force: 12.0,
responsiveness: 120.0,
running_friction: 0.4,
standing_friction: 2.0,
air_move_mult: 0.7,
jump_impulse: 8.0,
jump_force: 1.0,
jump_time: 50.0,
air_jumps: 0,
sprint_speed_mult: 1.2,
sprint_force_mult: 1.5,
}
}
}
#[derive(Default, Component, Serialize, Deserialize)]
#[storage(VecStorage)]
pub struct BrainComp {
pub state: BrainState,
pub options: BrainOptions,
zero_vec: Vec3<f32>,
temp_vec: Vec3<f32>,
temp_vec2: Vec3<f32>,
}
impl BrainComp {
pub fn new(options: BrainOptions) -> Self {
Self {
options,
state: BrainState::default(),
..Default::default()
}
}
pub fn stop(&mut self) {
self.state.running = false;
}
pub fn walk(&mut self) {
self.state.running = true;
}
pub fn jump(&mut self) {
self.state.jumping = true;
}
pub fn stop_jumping(&mut self) {
self.state.jumping = false;
}
pub fn sprint(&mut self) {
self.state.sprinting = true;
}
pub fn stop_sprinting(&mut self) {
self.state.sprinting = false;
}
pub fn operate(&mut self, target: &Vec3<f32>, body: &mut RigidBody, dt: f32) {
let origin = body.get_position();
let dx = target.0 - origin.0;
let dz = target.2 - origin.2;
let angle = dx.atan2(dz);
self.state.heading = angle;
let on_ground = body.at_rest_y() < 0;
let can_jump = on_ground || self.state.jump_count < self.options.air_jumps;
if on_ground {
self.state.is_jumping = false;
self.state.jump_count = 0;
}
if self.state.jumping {
if self.state.is_jumping {
if self.state.current_jump_time > 0.0 {
let mut jf = self.options.jump_force;
if self.state.current_jump_time < dt {
jf *= self.state.current_jump_time / dt;
}
body.apply_force(0.0, jf, 0.0);
self.state.current_jump_time -= dt;
}
} else if can_jump {
self.state.is_jumping = true;
if !on_ground {
self.state.jump_count += 1;
}
self.state.current_jump_time = self.options.jump_time;
body.apply_impulse(0.0, self.options.jump_impulse, 0.0);
if !on_ground && body.velocity[1] < 0.0 {
body.velocity[1] = 0.0;
}
}
} else {
self.state.is_jumping = false;
}
let m = &mut self.temp_vec;
let push = &mut self.temp_vec2;
if self.state.running {
let mut speed = self.options.max_speed;
if self.state.sprinting {
speed *= self.options.sprint_speed_mult;
}
m.set(0.0, 0.0, speed);
m.copy(&m.rotate_y(&self.zero_vec, self.state.heading));
push.copy(&m.sub(&body.velocity));
push[1] = 0.0;
let push_len = push.len();
push.copy(&push.normalize());
if push_len > 0.0 {
let mut can_push = self.options.move_force;
if self.state.sprinting {
can_push *= self.options.sprint_force_mult;
}
if !on_ground {
can_push *= self.options.air_move_mult;
}
let push_amt = self.options.responsiveness * push_len;
if can_push > push_amt {
can_push = push_amt;
}
push.copy(&push.scale(can_push));
body.apply_force(push.0, push.1, push.2);
}
body.friction = self.options.running_friction;
} else {
body.friction = self.options.standing_friction;
}
}
}