demo
[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
856c3740
TW
14////////// TRIGGER /////////////////////////////////////////////////////////////
15
16trait Trigger {
17 fn update(&mut self, body: &Body, ctrl: &Controller, dt: Duration) -> Option<Box<dyn State>>;
18}
19
8bfd1477
TW
20////////// STATE ///////////////////////////////////////////////////////////////
21
22trait State {
856c3740 23 fn enter(&mut self, _body: &mut Body, _ctrl: &Controller, _objects: &mut Objects) {}
8bfd1477
TW
24 fn exit(&self) {}
25 fn update(&mut self, body: &mut Body, ctrl: &Controller, objects: &mut Objects, lvl: &Level, dt: Duration) -> Option<Box<dyn State>>;
26}
27
5d7eff9e
TW
28////////// CHARACTER ///////////////////////////////////////////////////////////
29
8bfd1477 30struct Body {
856c3740
TW
31 pos: Point<f64>,
32 vel: Point<f64>,
33 standing_on: Option<Wall>,
8bfd1477
TW
34}
35
5d7eff9e
TW
36pub struct Character {
37 ctrl: Rc<RefCell<Controller>>,
8bfd1477 38 body: Body,
856c3740
TW
39 triggers: Vec<Box<dyn Trigger>>,
40 state: Box<dyn State>,
5d7eff9e
TW
41}
42
43impl Character {
44 pub fn new(ctrl: Rc<RefCell<Controller>>) -> Self {
45 Character {
46 ctrl,
8bfd1477
TW
47 body: Body {
48 pos: point!(300.0, 300.0),
49 vel: point!(0.0, 0.0),
50 standing_on: None,
51 },
856c3740
TW
52 triggers: vec!(Box::new(JumpTrigger)),
53 state: Box::new(FallState),
5d7eff9e
TW
54 }
55 }
56}
57
58impl Object for Character {
59 fn update(&mut self, objects: &mut Objects, lvl: &Level, dt: Duration) -> ObjectState {
60 let ctrl = self.ctrl.borrow();
61
856c3740
TW
62 if let Some(state) = self.state.update(&mut self.body, &ctrl, objects, lvl, dt) {
63 self.state.exit();
64 self.state = state;
65 self.state.enter(&mut self.body, &ctrl, objects);
8bfd1477 66 }
5d7eff9e 67
8bfd1477
TW
68 match &self.body.standing_on {
69 Some(_wall) => {
70 },
71 None => { // in air
72 if let Intersection(wall, pos) = lvl.intersect_walls(self.body.pos - self.body.vel, self.body.pos) {
73 self.body.standing_on = Some(wall);
74 self.body.pos = pos;
856c3740
TW
75
76 self.state.exit();
15cda333 77 self.state = Box::new(StandState); // eller loopa igenom alla triggers eller states för att hitta rätt state?
856c3740 78 self.state.enter(&mut self.body, &ctrl, objects);
5d7eff9e
TW
79 }
80 }
81 }
82
856c3740
TW
83 for trigger in &mut self.triggers {
84 if let Some(state) = trigger.update(&self.body, &ctrl, dt) {
85 self.state.exit();
86 self.state = state;
87 self.state.enter(&mut self.body, &ctrl, objects);
88 }
89 }
90
5d7eff9e
TW
91 if ctrl.shoot.is_pressed {
92 use rand::distributions::{Distribution, Normal};
93 let normal = Normal::new(0.0, 0.1);
94 let direction = if ctrl.aim.to_point().length() > 0.1 { ctrl.aim.to_point() } else { ctrl.mov.to_point() };
95 for _i in 0..100 {
96 objects.push(Box::new(Boll::new(
8bfd1477
TW
97 self.body.pos + point!(0.0, -16.0), // half the height of mario
98 direction * (10.0 + rand::random::<f64>()) + point!(normal.sample(&mut rand::thread_rng()), normal.sample(&mut rand::thread_rng())) + self.body.vel,
5d7eff9e
TW
99 2,
100 )));
101 }
102 ctrl.rumble(1.0, dt);
8bfd1477 103 self.body.vel -= direction * 0.1;
5d7eff9e
TW
104 }
105
106 ObjectState::Alive
107 }
108
109 fn render(&self, renderer: &mut Renderer, sprites: &SpriteManager) {
110 let block = sprites.get("mario");
111 let size = 32;
8bfd1477 112 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
113
114 let ctrl = &self.ctrl.borrow();
115 let l = 300.0;
8bfd1477 116 let pos = (self.body.pos.x as i32, self.body.pos.y as i32);
5d7eff9e 117 // // axis values
8bfd1477 118 // let p = (self.body.pos + ctrl.aim.to_axis_point() * l).to_i32().into();
5d7eff9e
TW
119 // renderer.draw_line(pos, p, (0, 255, 0));
120 // draw_cross(renderer, p);
121 // values limited to unit vector
8bfd1477 122 let p = (self.body.pos + ctrl.aim.to_point() * l).to_i32().into();
5d7eff9e
TW
123 renderer.draw_line(pos, p, (255, 0, 0));
124 draw_cross(renderer, p);
8bfd1477 125 let p = (self.body.pos + ctrl.mov.to_point() * l).to_i32().into();
5d7eff9e
TW
126 renderer.draw_line(pos, p, (0, 255, 0));
127 draw_cross(renderer, p);
128 // // circle values
8bfd1477 129 // let p = (self.body.pos + Point::from(ctrl.aim.a) * l).to_i32().into();
5d7eff9e
TW
130 // renderer.draw_line(pos, p, (0, 0, 255));
131 // draw_cross(renderer, p);
132 }
133}
134
135fn draw_cross(renderer: &mut Renderer, p: (i32, i32)) {
136 renderer.canvas().draw_line((p.0 - 5, p.1), (p.0 + 5, p.1)).unwrap();
137 renderer.canvas().draw_line((p.0, p.1 - 5), (p.0, p.1 + 5)).unwrap();
138}
8bfd1477
TW
139
140////////// FALLING /////////////////////////////////////////////////////////////
141
142struct FallState;
143
144impl State for FallState {
145 fn update(&mut self, body: &mut Body, ctrl: &Controller, _objects: &mut Objects, lvl: &Level, _dt: Duration) -> Option<Box<dyn State>> {
146 body.vel += lvl.gravity;
147 body.pos += body.vel;
148
149 match ctrl.mov.x {
150 v if v < -0.9 && body.vel.x > -5.0 => { body.vel.x -= 0.5 }
151 v if v > 0.9 && body.vel.x < 5.0 => { body.vel.x += 0.5 }
152 _ => {}
153 }
154
155 None
156 }
157}
158
856c3740 159////////// STANDING ////////////////////////////////////////////////////////////
8bfd1477
TW
160
161struct StandState;
162
163impl State for StandState {
15cda333
TW
164 fn enter(&mut self, body: &mut Body, _ctrl: &Controller, _objects: &mut Objects) {
165 if let Some(wall) = &body.standing_on {
166 body.vel = body.vel.project_onto(wall.angle());
167 }
168 }
169
856c3740 170 fn update(&mut self, body: &mut Body, _ctrl: &Controller, _objects: &mut Objects, _lvl: &Level, _dt: Duration) -> Option<Box<dyn State>> {
15cda333 171 if let Some(wall) = &body.standing_on {
ebd7419c 172 body.vel += _ctrl.mov.to_point() * 0.2;
15cda333 173 let (mut pos, mut vel) = wall.from_2d(&body.pos, &body.vel);
ebd7419c 174 vel *= 0.99;
15cda333 175 pos += vel;
ebd7419c
TW
176 if pos < 0.0 {
177 let w = wall.previous();
178 pos += w.length();
179 let (p, v) = w.to_2d(pos, vel);
180 body.pos = p;
181 body.vel = v;
182 body.standing_on = Some(w);
183 } else if pos > wall.length() {
184 let w = wall.next();
185 pos -= wall.length();
186 let (p, v) = w.to_2d(pos, vel);
187 body.pos = p;
188 body.vel = v;
189 body.standing_on = Some(w);
190 } else {
191 let (p, v) = wall.to_2d(pos, vel);
192 body.pos = p;
193 body.vel = v;
194 }
15cda333
TW
195 }
196
197 None
198 }
199}
ebd7419c
TW
200
201////////// WALKING /////////////////////////////////////////////////////////////
202
203struct WalkTrigger;
204
205impl Trigger for WalkTrigger {
206 fn update(&mut self, body: &Body, ctrl: &Controller, _dt: Duration) -> Option<Box<dyn State>> {
207 if body.standing_on.is_some() && (ctrl.mov.x < -0.1 || ctrl.mov.x > 0.1) {
208 Some(Box::new(WalkState))
209 } else {
210 None
211 }
212 }
213}
214
215struct WalkState;
216
217impl State for WalkState {
218 fn enter(&mut self, body: &mut Body, ctrl: &Controller, _objects: &mut Objects) {
219 if let Some(wall) = &body.standing_on {
220 if ctrl.mov.to_point().length() < 0.1 {
221 body.vel = wall.normal().into();
222 } else {
223 body.vel = ctrl.mov.to_point();
224 }
225 body.vel *= 5.0;
226 body.pos += body.vel * 0.1;
227 body.standing_on = None;
228 }
229 }
230
231 fn update(&mut self, body: &mut Body, ctrl: &Controller, _objects: &mut Objects, lvl: &Level, _dt: Duration) -> Option<Box<dyn State>> {
232 body.vel += lvl.gravity;
233 body.pos += body.vel;
234
235 match ctrl.mov.x {
236 v if v < -0.9 && body.vel.x > -5.0 => { body.vel.x -= 0.5 }
237 v if v > 0.9 && body.vel.x < 5.0 => { body.vel.x += 0.5 }
238 _ => {}
856c3740
TW
239 }
240
241 None
242 }
243}
244
245////////// JUMPING /////////////////////////////////////////////////////////////
246
247struct JumpTrigger;
248
249impl Trigger for JumpTrigger {
250 fn update(&mut self, body: &Body, ctrl: &Controller, _dt: Duration) -> Option<Box<dyn State>> {
251 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
252 Some(Box::new(JumpState))
253 } else {
254 None
255 }
256 }
257}
258
259struct JumpState;
260
261impl State for JumpState {
262 fn enter(&mut self, body: &mut Body, ctrl: &Controller, _objects: &mut Objects) {
8bfd1477 263 if let Some(wall) = &body.standing_on {
ebd7419c
TW
264 let direction = if ctrl.mov.to_point().length() < 0.1 {
265 wall.normal().into()
8bfd1477 266 } else {
ebd7419c
TW
267 ctrl.mov.to_point()
268 };
269 body.vel += direction * 5.0;
856c3740
TW
270 body.pos += body.vel * 0.1;
271 body.standing_on = None;
272 }
273 }
274
275 fn update(&mut self, body: &mut Body, ctrl: &Controller, _objects: &mut Objects, lvl: &Level, _dt: Duration) -> Option<Box<dyn State>> {
276 body.vel += lvl.gravity;
277 body.pos += body.vel;
278
279 match ctrl.mov.x {
280 v if v < -0.9 && body.vel.x > -5.0 => { body.vel.x -= 0.5 }
281 v if v > 0.9 && body.vel.x < 5.0 => { body.vel.x += 0.5 }
282 _ => {}
8bfd1477
TW
283 }
284
285 None
286 }
287}