18cb0aa2f39ba2548ac540968197a38e064e862c
[kaka/rust-sdl-test.git] / src / core / object / character.rs
1 use core::controller::Controller;
2 use core::object::boll::Boll;
3 use core::level::{Level, Wall, IntersectResult::Intersection};
4 use core::object::{Object, Objects, ObjectState};
5 use core::render::Renderer;
6 use geometry::Point;
7 use point;
8 use sdl2::rect::Rect;
9 use sprites::SpriteManager;
10 use std::cell::RefCell;
11 use std::rc::Rc;
12 use time::Duration;
13
14 ////////// STATE ///////////////////////////////////////////////////////////////
15
16 trait 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
22 ////////// CHARACTER ///////////////////////////////////////////////////////////
23
24 struct Body {
25     pub pos: Point<f64>,
26     pub vel: Point<f64>,
27     pub standing_on: Option<Wall>,
28 }
29
30 struct StateHolder(Box<dyn State>);
31
32 impl 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
44 pub struct Character {
45     ctrl: Rc<RefCell<Controller>>,
46     body: Body,
47     state: StateHolder, // Plays well with the borrow checker
48 }
49
50 impl Character {
51     pub fn new(ctrl: Rc<RefCell<Controller>>) -> Self {
52         Character {
53             ctrl,
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)),
60         }
61     }
62 }
63
64 impl Object for Character {
65     fn update(&mut self, objects: &mut Objects, lvl: &Level, dt: Duration) -> ObjectState {
66         let ctrl = self.ctrl.borrow();
67
68         if let Some(state) = self.state.get().update(&mut self.body, &ctrl, objects, lvl, dt) {
69             self.state.set(state);
70         }
71
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));
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(
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,
93                     2,
94                 )));
95             }
96             ctrl.rumble(1.0, dt);
97             self.body.vel -= direction * 0.1;
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;
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));
107
108         let ctrl = &self.ctrl.borrow();
109         let l = 300.0;
110         let pos = (self.body.pos.x as i32, self.body.pos.y as i32);
111         // // axis values
112         // let p = (self.body.pos + ctrl.aim.to_axis_point() * l).to_i32().into();
113         // renderer.draw_line(pos, p, (0, 255, 0));
114         // draw_cross(renderer, p);
115         // values limited to unit vector
116         let p = (self.body.pos + ctrl.aim.to_point() * l).to_i32().into();
117         renderer.draw_line(pos, p, (255, 0, 0));
118         draw_cross(renderer, p);
119         let p = (self.body.pos + ctrl.mov.to_point() * l).to_i32().into();
120         renderer.draw_line(pos, p, (0, 255, 0));
121         draw_cross(renderer, p);
122         // // circle values
123         // let p = (self.body.pos + Point::from(ctrl.aim.a) * l).to_i32().into();
124         // renderer.draw_line(pos, p, (0, 0, 255));
125         // draw_cross(renderer, p);
126     }
127 }
128
129 fn 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 }
133
134 ////////// FALLING /////////////////////////////////////////////////////////////
135
136 struct FallState;
137
138 impl 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
155 struct StandState;
156
157 impl 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 }