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; | |
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 | ||
135 | fn 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 | ||
142 | struct FallState; | |
143 | ||
144 | impl 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 | |
161 | struct StandState; | |
162 | ||
163 | impl 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 TW |
171 | if let Some(wall) = &body.standing_on { |
172 | let (mut pos, mut vel) = wall.from_2d(&body.pos, &body.vel); | |
173 | vel *= 0.9; | |
174 | pos += vel; | |
175 | let (p, v) = wall.to_2d(pos, vel); | |
176 | body.pos = p; | |
177 | body.vel = v; | |
178 | } | |
179 | ||
180 | None | |
181 | } | |
182 | } | |
856c3740 TW |
183 | } |
184 | ||
185 | None | |
186 | } | |
187 | } | |
188 | ||
189 | ////////// JUMPING ///////////////////////////////////////////////////////////// | |
190 | ||
191 | struct JumpTrigger; | |
192 | ||
193 | impl Trigger for JumpTrigger { | |
194 | fn update(&mut self, body: &Body, ctrl: &Controller, _dt: Duration) -> Option<Box<dyn State>> { | |
195 | 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 | |
196 | Some(Box::new(JumpState)) | |
197 | } else { | |
198 | None | |
199 | } | |
200 | } | |
201 | } | |
202 | ||
203 | struct JumpState; | |
204 | ||
205 | impl State for JumpState { | |
206 | fn enter(&mut self, body: &mut Body, ctrl: &Controller, _objects: &mut Objects) { | |
8bfd1477 | 207 | if let Some(wall) = &body.standing_on { |
856c3740 TW |
208 | if ctrl.mov.to_point().length() < 0.1 { |
209 | body.vel = wall.normal().into(); | |
8bfd1477 | 210 | } else { |
856c3740 | 211 | body.vel = ctrl.mov.to_point(); |
8bfd1477 | 212 | } |
856c3740 TW |
213 | body.vel *= 5.0; |
214 | body.pos += body.vel * 0.1; | |
215 | body.standing_on = None; | |
216 | } | |
217 | } | |
218 | ||
219 | fn update(&mut self, body: &mut Body, ctrl: &Controller, _objects: &mut Objects, lvl: &Level, _dt: Duration) -> Option<Box<dyn State>> { | |
220 | body.vel += lvl.gravity; | |
221 | body.pos += body.vel; | |
222 | ||
223 | match ctrl.mov.x { | |
224 | v if v < -0.9 && body.vel.x > -5.0 => { body.vel.x -= 0.5 } | |
225 | v if v > 0.9 && body.vel.x < 5.0 => { body.vel.x += 0.5 } | |
226 | _ => {} | |
8bfd1477 TW |
227 | } |
228 | ||
229 | None | |
230 | } | |
231 | } |