1use super::input_query::*;
2use super::level_query::*;
3use super::player_event::*;
4use super::points_counter::*;
5use super::slab_kind::*;
6use super::time_tracker::*;
7use euclid::default::Vector3D;
8use std::f64::consts::PI;
9
10const STEER_SLOW_DOWN: f64 = STEER_WIDTH_SCALE * 3.0 / 5.0;
11const STEER_MAX_SPEED: f64 = STEER_WIDTH_SCALE * 2.0 / 5.0;
12const STEER_WIDTH_SCALE: f64 = 500.0;
13
14const JUMP_VY_INIT: f64 = JUMP_HEIGHT_SCALE * 3.0;
15const JUMP_VY_BOOST_INIT: f64 = JUMP_HEIGHT_SCALE * 3.4;
16const JUMP_VY_OVERDRIVE_INIT: f64 = JUMP_HEIGHT_SCALE * 3.6;
17const JUMP_POSY_INIT: f64 = JUMP_HEIGHT_SCALE / 1000.0;
18const JUMP_GRAVITY: f64 = JUMP_HEIGHT_SCALE * 6.0;
19const JUMP_HEIGHT_SCALE: f64 = 100.0;
20
21const FORWARD_JUMP_BOOST: f64 = FORWARD_DEPTH_SCALE * 2.0;
22const FORWARD_STEER_BOOST_ON_GROUND: f64 = FORWARD_DEPTH_SCALE * 6.0;
23const FORWARD_STEER_BOOST_IN_AIR: f64 = FORWARD_DEPTH_SCALE * 2.0;
24const FORWARD_MAX_SPEED: f64 = FORWARD_DEPTH_SCALE * 15.0;
25const FORWARD_BOOST_SPEED: f64 = FORWARD_DEPTH_SCALE * 6.0;
26const FORWARD_OVERDRIVE_SPEED: f64 = FORWARD_DEPTH_SCALE * 12.0;
27const FORWARD_SLOW_DOWN_LINEAR_ON_GROUND: f64 = FORWARD_DEPTH_SCALE * 0.2;
28const FORWARD_SLOW_DOWN_SQUARE_ON_GROUND: f64 = 0.5;
29const FORWARD_SLOW_DOWN_LINEAR_IN_AIR: f64 = FORWARD_DEPTH_SCALE * 0.1;
30const FORWARD_SLOW_DOWN_SQUARE_IN_AIR: f64 = 0.4;
31const FORWARD_DEPTH_SCALE: f64 = 100.0;
32
33const BACKING_REPOS_DELAY: f64 = 0.05;
34const BACKING_FALL_DELAY: f64 = 0.55;
35const BACKING_REWIND_DELAY: f64 = 0.4;
36const BACKING_SIMULATED_DELAY: f64 = 5.0;
37
38const BACKING_FALL_DEPTH: f64 = 5.0;
39
40const EXTRA_TIME_ON_BOOST: f64 = 0.01;
41const EXTRA_TIME_ON_OVERDRIVE: f64 = 0.05;
42
43#[derive(Debug, Clone, Copy)]
44struct PointInTime {
45 position_vec: Vector3D<f64>,
46 time_sec: f64,
47}
48
49#[derive(Debug, Clone, Copy)]
50struct BackingData {
51 begin: PointInTime,
52 top_fall: PointInTime,
53 bottom_fall: PointInTime,
54 end: PointInTime,
55}
56
57#[derive(Debug)]
58pub struct Player {
59 initialized: bool,
60 steer_factor: f64,
61 position_vec: Vector3D<f64>,
62 speed_vec: Vector3D<f64>,
63 time_tracker: TimeTracker,
64 is_jumping: bool,
65 boost_state: bool,
66 cur_level_col: isize,
67 backing_data: Option<BackingData>,
68 points_counter: PointsCounter,
69 boost_count: isize,
70 overdrive_count: isize,
71}
72
73impl Player {
74 pub fn new() -> Player {
75 Player::default()
76 }
77
78 pub fn tick(
79 &mut self,
80 delta: f64,
81 level: &impl LevelQuery,
82 input: &mut impl InputQuery,
83 ) -> Vec<PlayerEvent> {
84 let mut events = Vec::new();
85 if !self.initialized {
86 self.replace(level);
87 self.initialized = true;
88 events.push(PlayerEvent::new_start());
89 }
90 self.time_tracker.add(delta);
91 events.append(&mut self.handle_steer(delta, input, level.width()));
92 events.append(&mut self.handle_jump(delta, input, level));
93 events.append(&mut self.handle_boost(level));
94 events.append(&mut self.handle_forward(delta, level));
95 events.append(&mut self.handle_fall(level));
96 events.append(&mut self.points_counter.handle_dist(
97 delta,
98 self.dist(),
99 self.time_to_complete(level),
100 ));
101 events
102 }
103
104 fn steer_max_speed(&self) -> f64 {
105 if self.steer_factor > 0.0 {
106 (STEER_MAX_SPEED * self.steer_factor).abs()
107 } else {
108 STEER_MAX_SPEED
109 }
110 }
111
112 fn steer_slow_down(&self) -> f64 {
113 if self.steer_factor > 0.0 {
114 (STEER_SLOW_DOWN * self.steer_factor).abs()
115 } else {
116 STEER_SLOW_DOWN
117 }
118 }
119
120 fn replace(&mut self, level: &impl LevelQuery) {
121 if let Some(start_spot) =
122 level.find_start_spot(self.level_col(level.width()), self.level_row())
123 {
124 self.position_vec = Self::from_level_coord(start_spot.0, start_spot.1, level.width())
125 };
126 }
127
128 fn handle_fall(&mut self, level: &impl LevelQuery) -> Vec<PlayerEvent> {
129 match self.backing_data {
130 Some(backing_data) => self.update_fall(backing_data),
131 None => {
132 if !self.check_valid_position(level) {
133 self.begin_fall(level)
134 } else {
135 Vec::new()
136 }
137 }
138 }
139 }
140
141 fn update_fall(&mut self, backing_data: BackingData) -> Vec<PlayerEvent> {
142 let now_sec = self.time_tracker.now_sec();
143
144 let end_sec = backing_data.end.time_sec;
145 if now_sec >= end_sec {
146 return self.end_fall(backing_data);
147 }
148
149 let bottom_fall_sec = backing_data.bottom_fall.time_sec;
150 if now_sec >= bottom_fall_sec {
151 let alpha = (now_sec - bottom_fall_sec) / (end_sec - bottom_fall_sec);
152 self.position_vec = backing_data
153 .top_fall
154 .position_vec
155 .lerp(backing_data.end.position_vec, alpha);
156 return Vec::new();
157 }
158
159 let top_fall_sec = backing_data.top_fall.time_sec;
160 if now_sec >= top_fall_sec {
161 let alpha = (now_sec - top_fall_sec) / (bottom_fall_sec - top_fall_sec);
162 self.position_vec = backing_data
163 .top_fall
164 .position_vec
165 .lerp(backing_data.bottom_fall.position_vec, alpha);
166 return Vec::new();
167 }
168
169 let begin_sec = backing_data.begin.time_sec;
170 if now_sec >= begin_sec {
171 let alpha = (now_sec - begin_sec) / (top_fall_sec - begin_sec);
172 self.position_vec = backing_data
173 .begin
174 .position_vec
175 .lerp(backing_data.begin.position_vec, alpha);
176 return Vec::new();
177 }
178
179 return Vec::new();
180 }
181
182 fn begin_fall(&mut self, level: &impl LevelQuery) -> Vec<PlayerEvent> {
183 let offset = BACKING_SIMULATED_DELAY * self.speed_vec.z / FORWARD_DEPTH_SCALE;
184 let backing_col = self.level_col(level.width());
185 let backing_src_row = self.level_row();
186 let backing_dst_row = backing_src_row - (offset as isize);
187 if let Some(start_spot) = level.find_start_spot(backing_col, backing_dst_row) {
188 let now_sec = self.time_tracker.now_sec();
189 let begin = PointInTime {
190 position_vec: self.position_vec,
191 time_sec: now_sec,
192 };
193 let top_fall = PointInTime {
194 position_vec: Self::from_level_coord(backing_col, backing_src_row, level.width()),
195 time_sec: now_sec + BACKING_REPOS_DELAY,
196 };
197 let bottom_fall = PointInTime {
198 position_vec: (Self::from_level_coord(backing_col, backing_src_row, level.width())
199 - Vector3D::new(0.0, BACKING_FALL_DEPTH * JUMP_HEIGHT_SCALE, 0.0)),
200 time_sec: now_sec + BACKING_REPOS_DELAY + BACKING_FALL_DELAY,
201 };
202 let end = PointInTime {
203 position_vec: Self::from_level_coord(start_spot.0, start_spot.1, level.width()),
204 time_sec: now_sec + BACKING_REPOS_DELAY + BACKING_FALL_DELAY + BACKING_REWIND_DELAY,
205 };
206 self.backing_data = Some(BackingData {
207 begin,
208 top_fall,
209 bottom_fall,
210 end,
211 });
212 };
213 self.speed_vec = Vector3D::new(0.0, 0.0, 0.0);
214 vec![PlayerEvent::new_fall()]
215 }
216
217 fn end_fall(&mut self, backing_data: BackingData) -> Vec<PlayerEvent> {
218 let mut vec = Vec::new();
219 self.position_vec = backing_data.end.position_vec;
220 if self.level_row() >= self.best_row() {
221 vec.push(PlayerEvent::new_catch_up());
222 }
223 self.speed_vec = Vector3D::new(0.0, 0.0, 0.0);
224 self.backing_data = None;
225 vec.push(PlayerEvent::new_start());
226 vec
227 }
228
229 fn export_position3(v: Vector3D<f64>) -> Vector3D<f32> {
230 Vector3D::<f32>::new(
231 (-v.x * 2.0 * PI / STEER_WIDTH_SCALE) as f32,
232 (v.y / JUMP_HEIGHT_SCALE) as f32,
233 (((v.z / FORWARD_DEPTH_SCALE) + 0.5).fract() - 0.5) as f32,
234 )
235 }
236
237 pub fn position(&self) -> Vector3D<f32> {
238 Self::export_position3(self.position_vec)
239 }
240
241 fn export_speed3(v: Vector3D<f64>) -> Vector3D<f32> {
242 Vector3D::<f32>::new(
243 (-v.x * 2.0 * PI / STEER_WIDTH_SCALE) as f32,
244 (v.y / JUMP_HEIGHT_SCALE) as f32,
245 (v.z / FORWARD_DEPTH_SCALE) as f32,
246 )
247 }
248
249 pub fn speed(&self) -> Vector3D<f32> {
250 Self::export_speed3(self.speed_vec)
251 }
252
253 pub fn level_col(&self, width: usize) -> isize {
254 ((self.position_vec.x * width as f64 / STEER_WIDTH_SCALE).round() as isize)
255 % (width as isize)
256 }
257
258 pub fn level_row(&self) -> isize {
259 (self.dist()).round() as isize
260 }
261
262 pub fn dist(&self) -> f64 {
263 self.position_vec.z / FORWARD_DEPTH_SCALE
264 }
265
266 pub fn from_level_coord(col: isize, row: isize, width: usize) -> Vector3D<f64> {
267 let mut real_col = col;
268 while real_col < 0 {
269 real_col += width as isize;
270 }
271 real_col = real_col % (width as isize);
272 let real_row = std::cmp::max(row, 0);
273 Vector3D::new(
274 real_col as f64 * STEER_WIDTH_SCALE / width as f64,
275 0.0,
276 real_row as f64 * FORWARD_DEPTH_SCALE,
277 )
278 }
279
280 pub fn set_steer_factor(&mut self, sensibility: f64) {
281 self.steer_factor = sensibility;
282 }
283
284 fn check_valid_position(&self, level: &impl LevelQuery) -> bool {
285 let kind = SlabKind::from_item(level.item(
286 self.level_col(level.width()),
287 self.level_row(),
288 self.boost_state,
289 ));
290 !(kind == SlabKind::Void && self.position_vec.y <= 0.0)
291 }
292
293 fn handle_steer(
294 &mut self,
295 delta: f64,
296 input: &mut impl InputQuery,
297 width: usize,
298 ) -> Vec<PlayerEvent> {
299 if let Some(_) = self.backing_data {
300 return Vec::new();
301 }
302
303 let steer = input.pop_steer();
304 self.speed_vec.x += steer;
305
306 let steer_max_speed = self.steer_max_speed();
307 if self.speed_vec.x > steer_max_speed {
308 self.speed_vec.x = steer_max_speed;
309 }
310 if self.speed_vec.x < -steer_max_speed {
311 self.speed_vec.x = -steer_max_speed;
312 }
313
314 let steer_slow_down = self.steer_slow_down();
315 if self.speed_vec.x > 0.0 {
316 self.speed_vec.x -= delta * steer_slow_down;
317 if self.speed_vec.x < 0.0 {
318 self.speed_vec.x = 0.0;
319 }
320 self.speed_vec.z +=
321 delta * (self.speed_vec.x / steer_max_speed) * self.forward_steer_boost();
322 }
323 if self.speed_vec.x < 0.0 {
324 self.speed_vec.x += delta * steer_slow_down;
325 if self.speed_vec.x > 0.0 {
326 self.speed_vec.x = 0.0;
327 }
328 self.speed_vec.z -=
329 delta * (self.speed_vec.x / steer_max_speed) * self.forward_steer_boost();
330 }
331
332 self.position_vec.x += self.speed_vec.x * (delta);
333
334 while self.position_vec.x >= STEER_WIDTH_SCALE {
335 self.position_vec.x -= STEER_WIDTH_SCALE;
336 }
337 while self.position_vec.x < 0.0 {
338 self.position_vec.x += STEER_WIDTH_SCALE;
339 }
340 if self.cur_level_col != self.level_col(width) {
341 self.cur_level_col = self.level_col(width);
342 return vec![PlayerEvent::new_change_column()];
343 }
344 Vec::new()
345 }
346
347 fn handle_jump(
348 &mut self,
349 delta: f64,
350 input: &mut impl InputQuery,
351 _level: &impl LevelQuery,
352 ) -> Vec<PlayerEvent> {
353 if let Some(_) = self.backing_data {
354 return Vec::new();
355 }
356
357 let jump = input.pop_jump();
358 if self.is_jumping {
359 self.speed_vec.y -= delta * JUMP_GRAVITY;
360 self.position_vec.y += delta * self.speed_vec.y;
361
362 if self.position_vec.y < 0.0 {
363 self.speed_vec.y = 0.0;
364 self.position_vec.y = 0.0;
365 self.is_jumping = false;
366 return vec![PlayerEvent::new_land()];
367 }
368 if self.speed_vec.y > 0.0 {
369 self.speed_vec.z += delta * FORWARD_JUMP_BOOST;
370 }
371 } else if jump {
372 self.is_jumping = true;
373 self.speed_vec.y = JUMP_VY_INIT;
374 self.position_vec.y = JUMP_POSY_INIT;
375 return vec![PlayerEvent::new_jump()];
376 }
377 Vec::new()
378 }
379
380 fn is_backing(&self) -> bool {
381 if let Some(_) = self.backing_data {
382 return true;
383 }
384 false
385 }
386
387 pub fn backing_camera_blend(&self) -> f64 {
388 match self.backing_data {
389 Some(backing_data) => {
390 let now_sec = self.time_tracker.now_sec();
391 let begin_sec = backing_data.begin.time_sec;
392 let end_sec = backing_data.end.time_sec;
393 if now_sec <= begin_sec || now_sec >= end_sec {
394 0.0
395 } else {
396 let middle_sec = (begin_sec + end_sec) / 2.0;
397 let half_range = middle_sec - begin_sec;
398 if half_range > 0.0 {
399 if now_sec <= middle_sec {
400 (now_sec - begin_sec) / half_range
401 } else {
402 (end_sec - now_sec) / half_range
403 }
404 } else {
405 0.0
406 }
407 }
408 }
409 None => 0.0,
410 }
411 }
412
413 fn is_on_ground(&self) -> bool {
414 self.position_vec.y == 0.0
415 }
416
417 fn handle_boost(&mut self, level: &impl LevelQuery) -> Vec<PlayerEvent> {
418 if self.is_on_ground() && !self.is_backing() {
419 let kind = SlabKind::from_item(level.item(
420 self.level_col(level.width()),
421 self.level_row(),
422 self.boost_state,
423 ));
424 if kind == SlabKind::Boost || kind == SlabKind::Overdrive {
425 let target_speed = if self.boost_state {
426 FORWARD_OVERDRIVE_SPEED
427 } else {
428 FORWARD_BOOST_SPEED
429 };
430 if self.speed_vec.z < target_speed {
431 self.speed_vec.z = target_speed;
432 }
433 let ret = if self.boost_state {
434 if self.level_row() >= self.best_row() {
435 self.overdrive_count += 1;
436 }
437 vec![PlayerEvent::new_overdrive()]
438 } else {
439 if self.level_row() >= self.best_row() {
440 self.boost_count += 1;
441 }
442 vec![PlayerEvent::new_boost()]
443 };
444 self.speed_vec.y = if self.boost_state {
445 JUMP_VY_OVERDRIVE_INIT
446 } else {
447 JUMP_VY_BOOST_INIT
448 };
449 self.position_vec.y = JUMP_POSY_INIT;
450 self.is_jumping = true;
451 self.boost_state = true;
452 return ret;
453 } else {
454 self.boost_state = false;
455 }
456 }
457 Vec::new()
458 }
459
460 fn forward_slow_down_linear(&self) -> f64 {
461 if self.is_on_ground() {
462 FORWARD_SLOW_DOWN_LINEAR_ON_GROUND
463 } else {
464 FORWARD_SLOW_DOWN_LINEAR_IN_AIR
465 }
466 }
467
468 fn forward_slow_down_square(&self) -> f64 {
469 if self.is_on_ground() {
470 FORWARD_SLOW_DOWN_SQUARE_ON_GROUND
471 } else {
472 FORWARD_SLOW_DOWN_SQUARE_IN_AIR
473 }
474 }
475
476 fn forward_steer_boost(&self) -> f64 {
477 if self.is_on_ground() {
478 FORWARD_STEER_BOOST_ON_GROUND
479 } else {
480 FORWARD_STEER_BOOST_IN_AIR
481 }
482 }
483
484 fn handle_forward(&mut self, delta: f64, level: &impl LevelQuery) -> Vec<PlayerEvent> {
485 if self.is_backing() {
486 return Vec::new();
487 }
488
489 self.speed_vec.z -= delta * self.forward_slow_down_linear();
490 self.speed_vec.z -= delta * self.forward_slow_down_square() * self.speed_vec.z;
491
492 if self.speed_vec.z > FORWARD_MAX_SPEED {
493 self.speed_vec.z = FORWARD_MAX_SPEED;
494 }
495
496 if self.speed_vec.z < 0.0 {
497 self.speed_vec.z = 0.0;
498 }
499
500 self.position_vec.z += delta * self.speed_vec.z * self.speed_factor(level);
501 if self.position_vec.z < 0.0 {
502 self.position_vec.z = 0.0;
503 }
504
505 Vec::new()
506 }
507
508 pub fn speed_factor(&self, level: &impl LevelQuery) -> f64 {
509 let time_pct = self.time_pct(level);
510 let speed_factor = ((100.0 - time_pct) * level.skill().begin_speed_factor()
511 + time_pct * level.skill().end_speed_factor())
512 / 100.0;
513 speed_factor
514 }
515
516 pub fn score(&self) -> i32 {
517 self.points_counter.score()
518 }
519
520 pub fn set_best_score(&mut self, best_score: i32) {
521 self.points_counter.set_best_score(best_score);
522 }
523
524 pub fn best_row(&self) -> isize {
525 self.points_counter.best_row()
526 }
527
528 pub fn best_dist(&self) -> f64 {
529 self.points_counter.best_dist()
530 }
531
532 pub fn is_boosting(&self) -> bool {
533 self.boost_state
534 }
535
536 fn time_pct(&self, level: &impl LevelQuery) -> f64 {
537 self.points_counter.time_pct(self.time_to_complete(level))
538 }
539
540 fn time_to_complete(&self, level: &impl LevelQuery) -> f64 {
541 let base = level.skill().time_to_complete();
542 base * self.time_progress_total() / 100.0
543 }
544
545 pub fn time_progress_done(&self, level: &impl LevelQuery) -> f64 {
546 let pct = self.time_pct(level);
547 if pct >= 100.0 {
548 self.time_progress_total()
549 } else {
550 pct * self.time_progress_total() / 100.0
551 }
552 }
553
554 pub fn time_progress_total(&self) -> f64 {
555 let extra_time_from_boost = EXTRA_TIME_ON_BOOST * (self.boost_count as f64);
556 let extra_time_from_overdrive = EXTRA_TIME_ON_OVERDRIVE * (self.overdrive_count as f64);
557 100.0 * (1.0 + extra_time_from_boost + extra_time_from_overdrive)
558 }
559}
560
561impl std::default::Default for Player {
562 fn default() -> Self {
563 Player {
564 initialized: false,
565 steer_factor: 1.0,
566 position_vec: Vector3D::default(),
567 speed_vec: Vector3D::default(),
568 time_tracker: TimeTracker::default(),
569 is_jumping: false,
570 boost_state: false,
571 cur_level_col: 0,
572 backing_data: None,
573 points_counter: PointsCounter::default(),
574 boost_count: 0,
575 overdrive_count: 0,
576 }
577 }
578}