+
+////////// 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 enter(&mut self, body: &mut Body, _ctrl: &Controller, _objects: &mut Objects) {
+ if let Some(wall) = &body.standing_on {
+ body.vel = body.vel.project_onto(wall.angle());
+ }
+ }
+
+ 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 += _ctrl.mov.to_point() * 0.2;
+ let (mut pos, mut vel) = wall.from_2d(&body.pos, &body.vel);
+ vel *= 0.99;
+ pos += vel;
+ if pos < 0.0 {
+ let w = wall.previous();
+ pos += w.length();
+ let (p, v) = w.to_2d(pos, vel);
+ body.pos = p;
+ body.vel = v;
+ body.standing_on = Some(w);
+ } else if pos > wall.length() {
+ let w = wall.next();
+ pos -= wall.length();
+ let (p, v) = w.to_2d(pos, vel);
+ body.pos = p;
+ body.vel = v;
+ body.standing_on = Some(w);
+ } else {
+ let (p, v) = wall.to_2d(pos, vel);
+ body.pos = p;
+ body.vel = v;
+ }
+ }
+
+ None
+ }
+}
+
+////////// WALKING /////////////////////////////////////////////////////////////
+
+struct WalkTrigger;
+
+impl Trigger for WalkTrigger {
+ fn update(&mut self, body: &Body, ctrl: &Controller, _dt: Duration) -> Option<Box<dyn State>> {
+ if body.standing_on.is_some() && (ctrl.mov.x < -0.1 || ctrl.mov.x > 0.1) {
+ Some(Box::new(WalkState))
+ } else {
+ None
+ }
+ }
+}
+
+struct WalkState;
+
+impl State for WalkState {
+ 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
+ }
+}
+
+////////// 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 {
+ let direction = if ctrl.mov.to_point().length() < 0.1 {
+ wall.normal().into()
+ } else {
+ ctrl.mov.to_point()
+ };
+ body.vel += direction * 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
+ }
+}