Commit | Line | Data |
---|---|---|
5d7eff9e TW |
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 | ||
856c3740 TW |
14 | ////////// TRIGGER ///////////////////////////////////////////////////////////// |
15 | ||
16 | trait Trigger { | |
17 | fn update(&mut self, body: &Body, ctrl: &Controller, dt: Duration) -> Option<Box<dyn State>>; | |
18 | } | |
19 | ||
8bfd1477 TW |
20 | ////////// STATE /////////////////////////////////////////////////////////////// |
21 | ||
22 | trait 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 | 30 | struct Body { |
856c3740 TW |
31 | pos: Point<f64>, |
32 | vel: Point<f64>, | |
33 | standing_on: Option<Wall>, | |
8bfd1477 TW |
34 | } |
35 | ||
5d7eff9e TW |
36 | pub 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 | ||
43 | impl 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 | ||
58 | impl 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; | |
75 | self.body.vel = point!(0.0, 0.0); | |
856c3740 TW |
76 | |
77 | self.state.exit(); | |
78 | self.state = Box::new(StandState); | |
79 | self.state.enter(&mut self.body, &ctrl, objects); | |
5d7eff9e TW |
80 | } |
81 | } | |
82 | } | |
83 | ||
856c3740 TW |
84 | for trigger in &mut self.triggers { |
85 | if let Some(state) = trigger.update(&self.body, &ctrl, dt) { | |
86 | self.state.exit(); | |
87 | self.state = state; | |
88 | self.state.enter(&mut self.body, &ctrl, objects); | |
89 | } | |
90 | } | |
91 | ||
5d7eff9e TW |
92 | if ctrl.shoot.is_pressed { |
93 | use rand::distributions::{Distribution, Normal}; | |
94 | let normal = Normal::new(0.0, 0.1); | |
95 | let direction = if ctrl.aim.to_point().length() > 0.1 { ctrl.aim.to_point() } else { ctrl.mov.to_point() }; | |
96 | for _i in 0..100 { | |
97 | objects.push(Box::new(Boll::new( | |
8bfd1477 TW |
98 | self.body.pos + point!(0.0, -16.0), // half the height of mario |
99 | direction * (10.0 + rand::random::<f64>()) + point!(normal.sample(&mut rand::thread_rng()), normal.sample(&mut rand::thread_rng())) + self.body.vel, | |
5d7eff9e TW |
100 | 2, |
101 | ))); | |
102 | } | |
103 | ctrl.rumble(1.0, dt); | |
8bfd1477 | 104 | self.body.vel -= direction * 0.1; |
5d7eff9e TW |
105 | } |
106 | ||
107 | ObjectState::Alive | |
108 | } | |
109 | ||
110 | fn render(&self, renderer: &mut Renderer, sprites: &SpriteManager) { | |
111 | let block = sprites.get("mario"); | |
112 | let size = 32; | |
8bfd1477 | 113 | 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 |
114 | |
115 | let ctrl = &self.ctrl.borrow(); | |
116 | let l = 300.0; | |
8bfd1477 | 117 | let pos = (self.body.pos.x as i32, self.body.pos.y as i32); |
5d7eff9e | 118 | // // axis values |
8bfd1477 | 119 | // let p = (self.body.pos + ctrl.aim.to_axis_point() * l).to_i32().into(); |
5d7eff9e TW |
120 | // renderer.draw_line(pos, p, (0, 255, 0)); |
121 | // draw_cross(renderer, p); | |
122 | // values limited to unit vector | |
8bfd1477 | 123 | let p = (self.body.pos + ctrl.aim.to_point() * l).to_i32().into(); |
5d7eff9e TW |
124 | renderer.draw_line(pos, p, (255, 0, 0)); |
125 | draw_cross(renderer, p); | |
8bfd1477 | 126 | let p = (self.body.pos + ctrl.mov.to_point() * l).to_i32().into(); |
5d7eff9e TW |
127 | renderer.draw_line(pos, p, (0, 255, 0)); |
128 | draw_cross(renderer, p); | |
129 | // // circle values | |
8bfd1477 | 130 | // let p = (self.body.pos + Point::from(ctrl.aim.a) * l).to_i32().into(); |
5d7eff9e TW |
131 | // renderer.draw_line(pos, p, (0, 0, 255)); |
132 | // draw_cross(renderer, p); | |
133 | } | |
134 | } | |
135 | ||
136 | fn draw_cross(renderer: &mut Renderer, p: (i32, i32)) { | |
137 | renderer.canvas().draw_line((p.0 - 5, p.1), (p.0 + 5, p.1)).unwrap(); | |
138 | renderer.canvas().draw_line((p.0, p.1 - 5), (p.0, p.1 + 5)).unwrap(); | |
139 | } | |
8bfd1477 TW |
140 | |
141 | ////////// FALLING ///////////////////////////////////////////////////////////// | |
142 | ||
143 | struct FallState; | |
144 | ||
145 | impl State for FallState { | |
146 | fn update(&mut self, body: &mut Body, ctrl: &Controller, _objects: &mut Objects, lvl: &Level, _dt: Duration) -> Option<Box<dyn State>> { | |
147 | body.vel += lvl.gravity; | |
148 | body.pos += body.vel; | |
149 | ||
150 | match ctrl.mov.x { | |
151 | v if v < -0.9 && body.vel.x > -5.0 => { body.vel.x -= 0.5 } | |
152 | v if v > 0.9 && body.vel.x < 5.0 => { body.vel.x += 0.5 } | |
153 | _ => {} | |
154 | } | |
155 | ||
156 | None | |
157 | } | |
158 | } | |
159 | ||
856c3740 | 160 | ////////// STANDING //////////////////////////////////////////////////////////// |
8bfd1477 TW |
161 | |
162 | struct StandState; | |
163 | ||
164 | impl State for StandState { | |
856c3740 TW |
165 | fn update(&mut self, body: &mut Body, _ctrl: &Controller, _objects: &mut Objects, _lvl: &Level, _dt: Duration) -> Option<Box<dyn State>> { |
166 | if let Some(_wall) = &body.standing_on { | |
167 | body.vel *= 0.9; | |
168 | } | |
169 | ||
170 | None | |
171 | } | |
172 | } | |
173 | ||
174 | ////////// JUMPING ///////////////////////////////////////////////////////////// | |
175 | ||
176 | struct JumpTrigger; | |
177 | ||
178 | impl Trigger for JumpTrigger { | |
179 | fn update(&mut self, body: &Body, ctrl: &Controller, _dt: Duration) -> Option<Box<dyn State>> { | |
180 | 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 | |
181 | Some(Box::new(JumpState)) | |
182 | } else { | |
183 | None | |
184 | } | |
185 | } | |
186 | } | |
187 | ||
188 | struct JumpState; | |
189 | ||
190 | impl State for JumpState { | |
191 | fn enter(&mut self, body: &mut Body, ctrl: &Controller, _objects: &mut Objects) { | |
8bfd1477 | 192 | if let Some(wall) = &body.standing_on { |
856c3740 TW |
193 | if ctrl.mov.to_point().length() < 0.1 { |
194 | body.vel = wall.normal().into(); | |
8bfd1477 | 195 | } else { |
856c3740 | 196 | body.vel = ctrl.mov.to_point(); |
8bfd1477 | 197 | } |
856c3740 TW |
198 | body.vel *= 5.0; |
199 | body.pos += body.vel * 0.1; | |
200 | body.standing_on = None; | |
201 | } | |
202 | } | |
203 | ||
204 | fn update(&mut self, body: &mut Body, ctrl: &Controller, _objects: &mut Objects, lvl: &Level, _dt: Duration) -> Option<Box<dyn State>> { | |
205 | body.vel += lvl.gravity; | |
206 | body.pos += body.vel; | |
207 | ||
208 | match ctrl.mov.x { | |
209 | v if v < -0.9 && body.vel.x > -5.0 => { body.vel.x -= 0.5 } | |
210 | v if v > 0.9 && body.vel.x < 5.0 => { body.vel.x += 0.5 } | |
211 | _ => {} | |
8bfd1477 TW |
212 | } |
213 | ||
214 | None | |
215 | } | |
216 | } |