Added a jumping trigger and state master
authorTomas Wenström <tomas.wenstrom@gmail.com>
Sun, 28 Feb 2021 19:58:13 +0000 (20:58 +0100)
committerTomas Wenström <tomas.wenstrom@gmail.com>
Sun, 28 Feb 2021 19:58:13 +0000 (20:58 +0100)
src/core/object/character.rs

index 18cb0aa..3d789f2 100644 (file)
@@ -11,10 +11,16 @@ use std::cell::RefCell;
 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>>;
 }
@@ -22,29 +28,16 @@ trait 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 {
@@ -56,7 +49,8 @@ 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),
        }
     }
 }
@@ -65,8 +59,10 @@ impl Object for Character {
     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 {
@@ -77,11 +73,22 @@ impl Object for Character {
                    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);
@@ -150,26 +157,58 @@ impl State for FallState {
     }
 }
 
-////////// 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