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(&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 ///////////////////////////////////////////////////////////
-pub struct Character {
- ctrl: Rc<RefCell<Controller>>,
+struct Body {
pos: Point<f64>,
vel: Point<f64>,
standing_on: Option<Wall>,
}
+pub struct Character {
+ ctrl: Rc<RefCell<Controller>>,
+ body: Body,
+ triggers: Vec<Box<dyn Trigger>>,
+ state: Box<dyn State>,
+}
+
impl Character {
pub fn new(ctrl: Rc<RefCell<Controller>>) -> Self {
Character {
ctrl,
- pos: point!(300.0, 300.0),
- vel: point!(0.0, 0.0),
- standing_on: None,
+ body: Body {
+ pos: point!(300.0, 300.0),
+ vel: point!(0.0, 0.0),
+ standing_on: None,
+ },
+ 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();
- match &self.standing_on {
- Some(wall) => {
- if ctrl.jump.is_pressed && !ctrl.jump.was_pressed {
- if ctrl.mov.to_point().length() < 0.1 {
- self.vel = wall.normal().into();
- } else {
- self.vel = ctrl.mov.to_point();
- }
- self.vel *= 5.0;
- self.pos += self.vel * 0.1;
- self.standing_on = None;
- } else {
- self.vel *= 0.9;
- }
+ 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 {
+ Some(_wall) => {
},
- None => {
- self.vel += lvl.gravity;
- self.pos += self.vel;
-
- match ctrl.mov.x {
- v if v < -0.9 && self.vel.x > -5.0 => { self.vel.x -= 0.5 }
- v if v > 0.9 && self.vel.x < 5.0 => { self.vel.x += 0.5 }
- _ => {}
- }
+ None => { // in air
+ if let Intersection(wall, pos) = lvl.intersect_walls(self.body.pos - self.body.vel, self.body.pos) {
+ self.body.standing_on = Some(wall);
+ self.body.pos = pos;
+ self.body.vel = point!(0.0, 0.0);
- if let Intersection(wall, pos) = lvl.intersect_walls(self.pos - self.vel, self.pos) {
- self.standing_on = Some(wall);
- self.pos = pos;
- self.vel = point!(0.0, 0.0);
+ 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);
let direction = if ctrl.aim.to_point().length() > 0.1 { ctrl.aim.to_point() } else { ctrl.mov.to_point() };
for _i in 0..100 {
objects.push(Box::new(Boll::new(
- self.pos + point!(0.0, -16.0), // half the height of mario
- direction * (10.0 + rand::random::<f64>()) + point!(normal.sample(&mut rand::thread_rng()), normal.sample(&mut rand::thread_rng())) + self.vel,
+ self.body.pos + point!(0.0, -16.0), // half the height of mario
+ direction * (10.0 + rand::random::<f64>()) + point!(normal.sample(&mut rand::thread_rng()), normal.sample(&mut rand::thread_rng())) + self.body.vel,
2,
)));
}
ctrl.rumble(1.0, dt);
- self.vel -= direction * 0.1;
+ self.body.vel -= direction * 0.1;
}
ObjectState::Alive
fn render(&self, renderer: &mut Renderer, sprites: &SpriteManager) {
let block = sprites.get("mario");
let size = 32;
- renderer.blit(block, None, Rect::new(self.pos.x as i32 - size as i32 / 2, self.pos.y as i32 - size as i32, size, size));
+ renderer.blit(block, None, Rect::new(self.body.pos.x as i32 - size as i32 / 2, self.body.pos.y as i32 - size as i32, size, size));
let ctrl = &self.ctrl.borrow();
let l = 300.0;
- let pos = (self.pos.x as i32, self.pos.y as i32);
+ let pos = (self.body.pos.x as i32, self.body.pos.y as i32);
// // axis values
- // let p = (self.pos + ctrl.aim.to_axis_point() * l).to_i32().into();
+ // let p = (self.body.pos + ctrl.aim.to_axis_point() * l).to_i32().into();
// renderer.draw_line(pos, p, (0, 255, 0));
// draw_cross(renderer, p);
// values limited to unit vector
- let p = (self.pos + ctrl.aim.to_point() * l).to_i32().into();
+ let p = (self.body.pos + ctrl.aim.to_point() * l).to_i32().into();
renderer.draw_line(pos, p, (255, 0, 0));
draw_cross(renderer, p);
- let p = (self.pos + ctrl.mov.to_point() * l).to_i32().into();
+ let p = (self.body.pos + ctrl.mov.to_point() * l).to_i32().into();
renderer.draw_line(pos, p, (0, 255, 0));
draw_cross(renderer, p);
// // circle values
- // let p = (self.pos + Point::from(ctrl.aim.a) * l).to_i32().into();
+ // let p = (self.body.pos + Point::from(ctrl.aim.a) * l).to_i32().into();
// renderer.draw_line(pos, p, (0, 0, 255));
// draw_cross(renderer, p);
}
renderer.canvas().draw_line((p.0 - 5, p.1), (p.0 + 5, p.1)).unwrap();
renderer.canvas().draw_line((p.0, p.1 - 5), (p.0, p.1 + 5)).unwrap();
}
+
+////////// FALLING /////////////////////////////////////////////////////////////
+
+struct FallState;
+
+impl State for FallState {
+ 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
+ }
+}
+
+////////// 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>> {
+ 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.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;
+ }
+ }
+
+ 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
+ }
+}