Character states - first draft
[kaka/rust-sdl-test.git] / src / core / object / character.rs
CommitLineData
5d7eff9e
TW
1use core::controller::Controller;
2use core::object::boll::Boll;
3use core::level::{Level, Wall, IntersectResult::Intersection};
4use core::object::{Object, Objects, ObjectState};
5use core::render::Renderer;
6use geometry::Point;
7use point;
8use sdl2::rect::Rect;
9use sprites::SpriteManager;
10use std::cell::RefCell;
11use std::rc::Rc;
12use time::Duration;
13
8bfd1477
TW
14////////// STATE ///////////////////////////////////////////////////////////////
15
16trait State {
17 fn enter(&self) {}
18 fn exit(&self) {}
19 fn update(&mut self, body: &mut Body, ctrl: &Controller, objects: &mut Objects, lvl: &Level, dt: Duration) -> Option<Box<dyn State>>;
20}
21
5d7eff9e
TW
22////////// CHARACTER ///////////////////////////////////////////////////////////
23
8bfd1477
TW
24struct Body {
25 pub pos: Point<f64>,
26 pub vel: Point<f64>,
27 pub standing_on: Option<Wall>,
28}
29
30struct StateHolder(Box<dyn State>);
31
32impl StateHolder {
33 pub fn get(&mut self) -> &mut Box<dyn State> {
34 &mut self.0
35 }
36
37 pub fn set(&mut self, state: Box<dyn State>) {
38 self.0.exit();
39 self.0 = state;
40 self.0.enter();
41 }
42}
43
5d7eff9e
TW
44pub struct Character {
45 ctrl: Rc<RefCell<Controller>>,
8bfd1477
TW
46 body: Body,
47 state: StateHolder, // Plays well with the borrow checker
5d7eff9e
TW
48}
49
50impl Character {
51 pub fn new(ctrl: Rc<RefCell<Controller>>) -> Self {
52 Character {
53 ctrl,
8bfd1477
TW
54 body: Body {
55 pos: point!(300.0, 300.0),
56 vel: point!(0.0, 0.0),
57 standing_on: None,
58 },
59 state: StateHolder(Box::new(FallState)),
5d7eff9e
TW
60 }
61 }
62}
63
64impl Object for Character {
65 fn update(&mut self, objects: &mut Objects, lvl: &Level, dt: Duration) -> ObjectState {
66 let ctrl = self.ctrl.borrow();
67
8bfd1477
TW
68 if let Some(state) = self.state.get().update(&mut self.body, &ctrl, objects, lvl, dt) {
69 self.state.set(state);
70 }
5d7eff9e 71
8bfd1477
TW
72 match &self.body.standing_on {
73 Some(_wall) => {
74 },
75 None => { // in air
76 if let Intersection(wall, pos) = lvl.intersect_walls(self.body.pos - self.body.vel, self.body.pos) {
77 self.body.standing_on = Some(wall);
78 self.body.pos = pos;
79 self.body.vel = point!(0.0, 0.0);
80 self.state.set(Box::new(StandState));
5d7eff9e
TW
81 }
82 }
83 }
84
85 if ctrl.shoot.is_pressed {
86 use rand::distributions::{Distribution, Normal};
87 let normal = Normal::new(0.0, 0.1);
88 let direction = if ctrl.aim.to_point().length() > 0.1 { ctrl.aim.to_point() } else { ctrl.mov.to_point() };
89 for _i in 0..100 {
90 objects.push(Box::new(Boll::new(
8bfd1477
TW
91 self.body.pos + point!(0.0, -16.0), // half the height of mario
92 direction * (10.0 + rand::random::<f64>()) + point!(normal.sample(&mut rand::thread_rng()), normal.sample(&mut rand::thread_rng())) + self.body.vel,
5d7eff9e
TW
93 2,
94 )));
95 }
96 ctrl.rumble(1.0, dt);
8bfd1477 97 self.body.vel -= direction * 0.1;
5d7eff9e
TW
98 }
99
100 ObjectState::Alive
101 }
102
103 fn render(&self, renderer: &mut Renderer, sprites: &SpriteManager) {
104 let block = sprites.get("mario");
105 let size = 32;
8bfd1477 106 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));
5d7eff9e
TW
107
108 let ctrl = &self.ctrl.borrow();
109 let l = 300.0;
8bfd1477 110 let pos = (self.body.pos.x as i32, self.body.pos.y as i32);
5d7eff9e 111 // // axis values
8bfd1477 112 // let p = (self.body.pos + ctrl.aim.to_axis_point() * l).to_i32().into();
5d7eff9e
TW
113 // renderer.draw_line(pos, p, (0, 255, 0));
114 // draw_cross(renderer, p);
115 // values limited to unit vector
8bfd1477 116 let p = (self.body.pos + ctrl.aim.to_point() * l).to_i32().into();
5d7eff9e
TW
117 renderer.draw_line(pos, p, (255, 0, 0));
118 draw_cross(renderer, p);
8bfd1477 119 let p = (self.body.pos + ctrl.mov.to_point() * l).to_i32().into();
5d7eff9e
TW
120 renderer.draw_line(pos, p, (0, 255, 0));
121 draw_cross(renderer, p);
122 // // circle values
8bfd1477 123 // let p = (self.body.pos + Point::from(ctrl.aim.a) * l).to_i32().into();
5d7eff9e
TW
124 // renderer.draw_line(pos, p, (0, 0, 255));
125 // draw_cross(renderer, p);
126 }
127}
128
129fn draw_cross(renderer: &mut Renderer, p: (i32, i32)) {
130 renderer.canvas().draw_line((p.0 - 5, p.1), (p.0 + 5, p.1)).unwrap();
131 renderer.canvas().draw_line((p.0, p.1 - 5), (p.0, p.1 + 5)).unwrap();
132}
8bfd1477
TW
133
134////////// FALLING /////////////////////////////////////////////////////////////
135
136struct FallState;
137
138impl State for FallState {
139 fn update(&mut self, body: &mut Body, ctrl: &Controller, _objects: &mut Objects, lvl: &Level, _dt: Duration) -> Option<Box<dyn State>> {
140 body.vel += lvl.gravity;
141 body.pos += body.vel;
142
143 match ctrl.mov.x {
144 v if v < -0.9 && body.vel.x > -5.0 => { body.vel.x -= 0.5 }
145 v if v > 0.9 && body.vel.x < 5.0 => { body.vel.x += 0.5 }
146 _ => {}
147 }
148
149 None
150 }
151}
152
153////////// STANDING /////////////////////////////////////////////////////////////
154
155struct StandState;
156
157impl State for StandState {
158 fn update(&mut self, body: &mut Body, ctrl: &Controller, _objects: &mut Objects, _lvl: &Level, _dt: Duration) -> Option<Box<dyn State>> {
159 if let Some(wall) = &body.standing_on {
160 if ctrl.jump.is_pressed && !ctrl.jump.was_pressed {
161 if ctrl.mov.to_point().length() < 0.1 {
162 body.vel = wall.normal().into();
163 } else {
164 body.vel = ctrl.mov.to_point();
165 }
166 body.vel *= 5.0;
167 body.pos += body.vel * 0.1;
168 body.standing_on = None;
169 return Some(Box::new(FallState))
170 } else {
171 body.vel *= 0.9;
172 }
173 }
174
175 None
176 }
177}