use std::rc::Rc;
use time::Duration;
+////////// TRIGGER /////////////////////////////////////////////////////////////
+
+trait Trigger {
+ fn update(&mut self, body: &Body, ctrl: &Controller, dt: Duration) -> Option<Box<dyn State>>;
+}
+
////////// STATE ///////////////////////////////////////////////////////////////
trait State {
- fn enter(&self) {}
+ fn enter(&mut self, _body: &mut Body, _ctrl: &Controller, _objects: &mut Objects) {}
fn exit(&self) {}
fn update(&mut self, body: &mut Body, ctrl: &Controller, objects: &mut Objects, lvl: &Level, dt: Duration) -> Option<Box<dyn State>>;
}
////////// CHARACTER ///////////////////////////////////////////////////////////
struct Body {
- pub pos: Point<f64>,
- pub vel: Point<f64>,
- pub standing_on: Option<Wall>,
-}
-
-struct StateHolder(Box<dyn State>);
-
-impl StateHolder {
- pub fn get(&mut self) -> &mut Box<dyn State> {
- &mut self.0
- }
-
- pub fn set(&mut self, state: Box<dyn State>) {
- self.0.exit();
- self.0 = state;
- self.0.enter();
- }
+ pos: Point<f64>,
+ vel: Point<f64>,
+ standing_on: Option<Wall>,
}
pub struct Character {
ctrl: Rc<RefCell<Controller>>,
body: Body,
- state: StateHolder, // Plays well with the borrow checker
+ triggers: Vec<Box<dyn Trigger>>,
+ state: Box<dyn State>,
}
impl Character {
vel: point!(0.0, 0.0),
standing_on: None,
},
- state: StateHolder(Box::new(FallState)),
+ triggers: vec!(Box::new(JumpTrigger)),
+ state: Box::new(FallState),
}
}
}
fn update(&mut self, objects: &mut Objects, lvl: &Level, dt: Duration) -> ObjectState {
let ctrl = self.ctrl.borrow();
- if let Some(state) = self.state.get().update(&mut self.body, &ctrl, objects, lvl, dt) {
- self.state.set(state);
+ if let Some(state) = self.state.update(&mut self.body, &ctrl, objects, lvl, dt) {
+ self.state.exit();
+ self.state = state;
+ self.state.enter(&mut self.body, &ctrl, objects);
}
match &self.body.standing_on {
self.body.standing_on = Some(wall);
self.body.pos = pos;
self.body.vel = point!(0.0, 0.0);
- self.state.set(Box::new(StandState));
+
+ self.state.exit();
+ self.state = Box::new(StandState);
+ self.state.enter(&mut self.body, &ctrl, objects);
}
}
}
+ for trigger in &mut self.triggers {
+ if let Some(state) = trigger.update(&self.body, &ctrl, dt) {
+ self.state.exit();
+ self.state = state;
+ self.state.enter(&mut self.body, &ctrl, objects);
+ }
+ }
+
if ctrl.shoot.is_pressed {
use rand::distributions::{Distribution, Normal};
let normal = Normal::new(0.0, 0.1);
}
}
-////////// STANDING /////////////////////////////////////////////////////////////
+////////// STANDING ////////////////////////////////////////////////////////////
struct StandState;
impl State for StandState {
- fn update(&mut self, body: &mut Body, ctrl: &Controller, _objects: &mut Objects, _lvl: &Level, _dt: Duration) -> Option<Box<dyn State>> {
+ fn update(&mut self, body: &mut Body, _ctrl: &Controller, _objects: &mut Objects, _lvl: &Level, _dt: Duration) -> Option<Box<dyn State>> {
+ if let Some(_wall) = &body.standing_on {
+ body.vel *= 0.9;
+ }
+
+ None
+ }
+}
+
+////////// JUMPING /////////////////////////////////////////////////////////////
+
+struct JumpTrigger;
+
+impl Trigger for JumpTrigger {
+ fn update(&mut self, body: &Body, ctrl: &Controller, _dt: Duration) -> Option<Box<dyn State>> {
+ if body.standing_on.is_some() && ctrl.jump.is_pressed && !ctrl.jump.was_pressed { // this is redundant now because JumpState needs a wall to get starting velocity, but that will probably change
+ Some(Box::new(JumpState))
+ } else {
+ None
+ }
+ }
+}
+
+struct JumpState;
+
+impl State for JumpState {
+ fn enter(&mut self, body: &mut Body, ctrl: &Controller, _objects: &mut Objects) {
if let Some(wall) = &body.standing_on {
- if ctrl.jump.is_pressed && !ctrl.jump.was_pressed {
- if ctrl.mov.to_point().length() < 0.1 {
- body.vel = wall.normal().into();
- } else {
- body.vel = ctrl.mov.to_point();
- }
- body.vel *= 5.0;
- body.pos += body.vel * 0.1;
- body.standing_on = None;
- return Some(Box::new(FallState))
+ if ctrl.mov.to_point().length() < 0.1 {
+ body.vel = wall.normal().into();
} else {
- body.vel *= 0.9;
+ body.vel = ctrl.mov.to_point();
}
+ body.vel *= 5.0;
+ body.pos += body.vel * 0.1;
+ body.standing_on = None;
+ }
+ }
+
+ fn update(&mut self, body: &mut Body, ctrl: &Controller, _objects: &mut Objects, lvl: &Level, _dt: Duration) -> Option<Box<dyn State>> {
+ body.vel += lvl.gravity;
+ body.pos += body.vel;
+
+ match ctrl.mov.x {
+ v if v < -0.9 && body.vel.x > -5.0 => { body.vel.x -= 0.5 }
+ v if v > 0.9 && body.vel.x < 5.0 => { body.vel.x += 0.5 }
+ _ => {}
}
None