1 /***************************************************************************
2 Core Game Engine Routines.
3
4 - The main loop which advances the level onto the next segment.
5 - Code to directly control the road hardware. For example, the road
6 split and bonus points routines.
7 - Code to determine whether to initialize certain game modes
8 (Crash state, Bonus points, road split state)
9
10 Copyright Chris White.
11 See license.txt for more details.
12 ***************************************************************************/
13
14 #include "trackloader.hpp"
15
16 #include "engine/oanimseq.hpp"
17 #include "engine/obonus.hpp"
18 #include "engine/ocrash.hpp"
19 #include "engine/oferrari.hpp"
20 #include "engine/ohud.hpp"
21 #include "engine/oinputs.hpp"
22 #include "engine/olevelobjs.hpp"
23 #include "engine/omusic.hpp"
24 #include "engine/ooutputs.hpp"
25 #include "engine/ostats.hpp"
26 #include "engine/outils.hpp"
27 #include "engine/opalette.hpp"
28 #include "engine/osmoke.hpp"
29 #include "engine/otiles.hpp"
30 #include "engine/otraffic.hpp"
31 #include "engine/oinitengine.hpp"
32
33 OInitEngine oinitengine;
34
35 // Continuous Mode Level Ordering
36 const static uint8_t CONTINUOUS_LEVELS[] = {0, 0x8, 0x9, 0x10, 0x11, 0x12, 0x18, 0x19, 0x1A, 0x1B, 0x20, 0x21, 0x22, 0x23, 0x24};
37
38 // Set to 0 to 4 to test bonus sequence, -1 disables
39 const static int DEBUG_BONUS = -1;
40
OInitEngine()41 OInitEngine::OInitEngine()
42 {
43 }
44
45
~OInitEngine()46 OInitEngine::~OInitEngine()
47 {
48 }
49
50 // Source: 0x8360
init(int8_t level)51 void OInitEngine::init(int8_t level)
52 {
53 ostats.game_completed = 0;
54
55 ingame_engine = false;
56 ingame_counter = 0;
57 ostats.cur_stage = 0;
58 oroad.stage_lookup_off = level ? level : 0;
59 rd_split_state = SPLIT_NONE;
60 road_type = ROAD_NOCHANGE;
61 road_type_next = ROAD_NOCHANGE;
62 end_stage_props = 0;
63 car_increment = 0;
64 car_x_pos = 0;
65 car_x_old = 0;
66 checkpoint_marker = 0;
67 road_curve = 0;
68 road_curve_next = 0;
69 road_remove_split = 0;
70 route_selected = 0;
71
72 road_width_next = 0;
73 road_width_adj = 0;
74 change_width = 0;
75 granular_rem = 0;
76 pos_fine_old = 0;
77 road_width_orig = 0;
78 road_width_merge = 0;
79 route_updated = 0;
80
81 init_road_seg_master();
82
83 // Road Renderer: Setup correct stage address
84 if (level)
85 trackloader.init_path(oroad.stage_lookup_off);
86
87 opalette.setup_sky_palette();
88 opalette.setup_ground_color();
89 opalette.setup_road_centre();
90 opalette.setup_road_stripes();
91 opalette.setup_road_side();
92 opalette.setup_road_colour();
93 otiles.setup_palette_hud(); // Init Default Palette for HUD
94 osprites.copy_palette_data(); // Copy Palette Data to RAM
95 otiles.setup_palette_tilemap(); // Setup Palette For Tilemap
96 setup_stage1(); // Setup Misc stuff relating to Stage 1
97 otiles.reset_tiles_pal(); // Reset Tiles, Palette And Road Split Data
98 ocrash.clear_crash_state();
99
100 // The following is set up specifically for time trial mode
101 if (level)
102 {
103 otiles.init_tilemap_palette(level);
104 oroad.road_ctrl = ORoad::ROAD_BOTH_P0;
105 oroad.road_width = RD_WIDTH_MERGE; // Setup a default road width
106 }
107
108 osoundint.reset();
109 }
110
111 // Source: 0x8402
setup_stage1()112 void OInitEngine::setup_stage1()
113 {
114 oroad.road_width = 0x1C2 << 16; // Force display of two roads at start
115 ostats.score = 0;
116 ostats.clear_stage_times();
117 oferrari.reset_car(); // Reset Car Speed/Rev Values
118 outrun.outputs->set_digital(OOutputs::D_EXT_MUTE);
119 outrun.outputs->set_digital(OOutputs::D_SOUND);
120 osoundint.engine_data[sound::ENGINE_VOL] = 0x3F;
121 ostats.extend_play_timer = 0;
122 checkpoint_marker = 0; // Denote not past checkpoint marker
123 otraffic.set_max_traffic(); // Set Number Of Enemy Cars Based On Dip Switches
124 ostats.clear_route_info();
125 osmoke.setup_smoke_sprite(true);
126 }
127
128 // Initialise Master Segment Address For Stage
129 //
130 // 1. Read Internal Stage Number from Stage Data Table (Using the lookup offset)
131 // 2. Load the master address, using the stage number as an index.
132 //
133 // Source: 0x8C80
134
init_road_seg_master()135 void OInitEngine::init_road_seg_master()
136 {
137 trackloader.init_track(oroad.stage_lookup_off);
138 }
139
140 //
141 // Check Road Width
142 // Source: B85A
143 //
144 // Potentially Update Width Of Road
145 //
146 // ADDRESS 2 - Road Segment Data [8 byte boundaries]:
147 //
148 // Word 1 [+0]: Segment Position
149 // Word 2 [+2]: Set = Denotes Road Height Info. Unset = Denotes Road Width
150 // Word 3 [+4]: Segment Road Width / Segment Road Height Index
151 // Word 4 [+6]: Segment Width Adjustment SIGNED (Speed at which width is adjusted)
152
update_road()153 void OInitEngine::update_road()
154 {
155 check_road_split(); // Check/Process road split if necessary
156 uint32_t addr = 0;
157 uint16_t d0 = trackloader.read_width_height(&addr);
158 // Update next road section
159 if (d0 <= oroad.road_pos >> 16)
160 {
161 // Skip road width adjustment if set and adjust height
162 if (trackloader.read_width_height(&addr) == 0)
163 {
164 // ROM:0000B8A6 skip_next_width
165 if (oroad.height_lookup == 0)
166 oroad.height_lookup = trackloader.read_width_height(&addr); // Set new height lookup section
167 }
168 else
169 {
170 // ROM:0000B87A
171 int16_t width = trackloader.read_width_height(&addr); // Segment road width
172 int16_t change = trackloader.read_width_height(&addr); // Segment adjustment speed
173
174 if (width != (int16_t) (oroad.road_width >> 16))
175 {
176 if (width <= (int16_t) (oroad.road_width >> 16))
177 change = -change;
178
179 road_width_next = width;
180 road_width_adj = change;
181 change_width = -1; // Denote road width is changing
182 }
183 }
184 trackloader.wh_offset += 8;
185 }
186
187 // ROM:0000B8BC set_road_width
188 // Width of road is changing & car is moving
189 if (change_width != 0 && car_increment >> 16 != 0)
190 {
191 int32_t d0 = ((car_increment >> 16) * road_width_adj) << 4;
192 oroad.road_width += d0; // add long here
193 if (d0 > 0)
194 {
195 if (road_width_next < (int16_t) (oroad.road_width >> 16))
196 {
197 change_width = 0;
198 oroad.road_width = road_width_next << 16;
199 }
200 }
201 else
202 {
203 if (road_width_next >= (int16_t) (oroad.road_width >> 16))
204 {
205 change_width = 0;
206 oroad.road_width = road_width_next << 16;
207 }
208 }
209 }
210 // ------------------------------------------------------------------------------------------------
211 // ROAD SEGMENT FORMAT
212 //
213 // Each segment of road is 6 bytes in memory, consisting of 3 words
214 // Each road segment is a significant length of road btw :)
215 //
216 // ADDRESS 3 - Road Segment Data [6 byte boundaries]
217 //
218 // Word 1 [+0]: Segment Position (used with 0x260006 car position)
219 // Word 2 [+2]: Segment Road Curve
220 // Word 3 [+4]: Segment Road type (1 = Straight, 2 = Right Bend, 3 = Left Bend)
221 //
222 // 60a08 = address of next road segment? (e.g. A0 = 0x0001DD86)
223 // ------------------------------------------------------------------------------------------------
224
225 // ROM:0000B91C set_road_type:
226
227 int16_t segment_pos = trackloader.read_curve(0);
228
229 if (segment_pos != -1)
230 {
231 int16_t d1 = segment_pos - 0x3C;
232
233 if (d1 <= (int16_t) (oroad.road_pos >> 16))
234 {
235 road_curve_next = trackloader.read_curve(2);
236 road_type_next = trackloader.read_curve(4);
237 }
238
239 if (segment_pos <= (int16_t) (oroad.road_pos >> 16))
240 {
241 road_curve = trackloader.read_curve(2);
242 road_type = trackloader.read_curve(4);
243 trackloader.curve_offset += 6;
244 road_type_next = 0;
245 road_curve_next = 0;
246 }
247 }
248 }
249
250 // Carries on from the above in the original code
update_engine()251 void OInitEngine::update_engine()
252 {
253 // ------------------------------------------------------------------------
254 // TILE MAP OFFSETS
255 // ROM:0000B986 setup_shadow_offset:
256 // Setup the shadow offset based on how much we've scrolled left/right. Lovely and subtle!
257 // ------------------------------------------------------------------------
258
259 update_shadow_offset();
260
261 // ------------------------------------------------------------------------
262 // Main Car Logic Block
263 // ------------------------------------------------------------------------
264
265 oferrari.move();
266
267 if (oferrari.car_ctrl_active)
268 {
269 oferrari.set_curve_adjust();
270 oferrari.set_ferrari_x();
271 oferrari.do_skid();
272 oferrari.check_wheels();
273 oferrari.set_ferrari_bounds();
274 }
275
276 oferrari.do_sound_score_slip();
277
278 // ------------------------------------------------------------------------
279 // Setup New Sprite Scroll Speed. Based On Granular Difference.
280 // ------------------------------------------------------------------------
281 set_granular_position();
282 set_fine_position();
283
284 // Draw Speed & Hud Stuff
285 if (outrun.game_state >= GS_START1 && outrun.game_state <= GS_BONUS)
286 {
287 // Convert & Blit Car Speed
288 ohud.blit_speed(0x110CB6, car_increment >> 16);
289 ohud.blit_text1(HUD_KPH1);
290 ohud.blit_text1(HUD_KPH2);
291
292 // Blit High/Low Gear
293 if ((config.controls.gear == config.controls.GEAR_BUTTON ||
294 config.controls.gear == config.controls.GEAR_SEPARATE)
295 && !config.smartypi.enabled)
296 {
297 if (oinputs.gear)
298 ohud.blit_text_new(9, 26, "H", OHud::GREEN);
299 else
300 ohud.blit_text_new(9, 26, "L", OHud::GREY);
301 }
302
303 if (config.engine.layout_debug)
304 ohud.draw_debug_info(oroad.road_pos, oroad.height_lookup_wrk, trackloader.read_sprite_pattern_index());
305 }
306
307 if (olevelobjs.spray_counter > 0)
308 olevelobjs.spray_counter--;
309
310 if (olevelobjs.sprite_collision_counter > 0)
311 olevelobjs.sprite_collision_counter--;
312
313 opalette.setup_sky_cycle();
314 }
315
update_shadow_offset()316 void OInitEngine::update_shadow_offset()
317 {
318 int16_t shadow_off = oroad.tilemap_h_target & 0x3FF;
319 if (shadow_off > 0x1FF)
320 shadow_off = -shadow_off + 0x3FF;
321 shadow_off >>= 2;
322 if (oroad.tilemap_h_target & BIT_A)
323 shadow_off = -shadow_off; // reverse direction of shadow
324 osprites.shadow_offset = shadow_off;
325 }
326
327 // Check for Road Split
328 //
329 // - Checks position in level and determine whether to init road split
330 // - Processes road split if initialized
331 //
332 // Source: 868E
check_road_split()333 void OInitEngine::check_road_split()
334 {
335 // Check whether to initialize the next level
336 ostats.init_next_level();
337
338 switch (rd_split_state)
339 {
340 // State 0: No road split. Check current road position with ROAD_END.
341 case SPLIT_NONE:
342 if (DEBUG_BONUS == -1)
343 {
344 if (oroad.road_pos >> 16 <= ROAD_END) return;
345 check_stage(); // Do Split - Split behaviour depends on stage
346 }
347 else
348 {
349 if (oroad.road_pos >> 16 <= 0x100) return;
350 init_bonus(DEBUG_BONUS);
351 }
352 break;
353
354 // State 1: (Does this ever need to be called directly?)
355 case SPLIT_INIT:
356 init_split1();
357 break;
358
359 // State 2: Road Split
360 case SPLIT_CHOICE1:
361 if (oroad.road_pos >> 16 >= 0x3F)
362 init_split2();
363 break;
364
365 // State 3: Beginning of split. User must choose.
366 case SPLIT_CHOICE2:
367 init_split2();
368 break;
369
370 // State 4: Road physically splits into two individual roads
371 case 4:
372 init_split3();
373 break;
374
375 // State 5: Road fully split. Init remove other road
376 case 5:
377 if (!road_curve)
378 rd_split_state = 6; // and fall through
379 else
380 break;
381
382 // State 6: Road split. Only one road drawn.
383 case 6:
384 init_split5();
385 break;
386
387 // Stage 7
388 case 7:
389 init_split6();
390 break;
391
392 // State 8: Init Road Merge before checkpoint sign
393 case 8:
394 otraffic.traffic_split = -1;
395 rd_split_state = 9;
396 break;
397
398 // State 9: Road Merge before checkpoint sign
399 case 9:
400 otraffic.traffic_split = 0;
401 case 0x0A:
402 init_split9();
403 break;
404
405 case 0x0B:
406 case 0x0C:
407 case 0x0D:
408 case 0x0E:
409 case 0x0F:
410 init_split10();
411 break;
412
413 // Init Bonus Sequence
414 case 0x10:
415 init_bonus(oroad.stage_lookup_off - 0x20);
416 break;
417
418 case 0x11:
419 bonus1();
420 break;
421
422 case 0x12:
423 bonus2();
424 break;
425
426 case 0x13:
427 bonus3();
428 break;
429
430 case 0x14:
431 bonus4();
432 break;
433
434 case 0x15:
435 bonus5();
436 break;
437
438 case 0x16:
439 case 0x17:
440 case 0x18:
441 bonus6();
442 break;
443 }
444 }
445
446 // ------------------------------------------------------------------------------------------------
447 // Check Stage Info To Determine What To Do With Road
448 //
449 // Stage 1-4: Init Road Split
450 // Stage 5: Init Bonus
451 // Stage 5 ATTRACT: Loop to Stage 1
452 // ------------------------------------------------------------------------------------------------
check_stage()453 void OInitEngine::check_stage()
454 {
455 // Time Trial Mode
456 if (outrun.cannonball_mode == Outrun::MODE_TTRIAL)
457 {
458 // Store laptime and reset
459 uint8_t* laptimes = outrun.ttrial.laptimes[outrun.ttrial.current_lap];
460
461 laptimes[0] = ostats.stage_times[0][0];
462 laptimes[1] = ostats.stage_times[0][1];
463 laptimes[2] = ostats.stage_times[0][2];
464 ostats.stage_times[0][0] =
465 ostats.stage_times[0][1] =
466 ostats.stage_times[0][2] = 0;
467
468 // Check for new best laptime
469 int16_t counter = ostats.stage_counters[outrun.ttrial.current_lap];
470 if (counter < outrun.ttrial.best_lap_counter)
471 {
472 outrun.ttrial.best_lap_counter = counter;
473 outrun.ttrial.best_lap[0] = laptimes[0];
474 outrun.ttrial.best_lap[1] = laptimes[1];
475 outrun.ttrial.best_lap[2] = ostats.lap_ms[laptimes[2]];
476
477 // Draw best laptime
478 ostats.extend_play_timer = 0x80;
479 ohud.blit_text1(TEXT1_LAPTIME1);
480 ohud.blit_text1(TEXT1_LAPTIME2);
481 ohud.draw_lap_timer(0x110554, laptimes, ostats.lap_ms[laptimes[2]]);
482
483 outrun.ttrial.new_high_score = true;
484 }
485
486 if (outrun.game_state == GS_INGAME)
487 {
488 // More laps to go, loop the course
489 if (++outrun.ttrial.current_lap < outrun.ttrial.laps)
490 {
491 // Update lap number
492 oroad.road_pos = 0;
493 oroad.tilemap_h_target = 0;
494 init_road_seg_master();
495 }
496 else
497 {
498 // Set correct finish segment for final 5 stages, otherwise just default to first one.
499 oroad.stage_lookup_off = oroad.stage_lookup_off < 0x20 ? 0x20 : oroad.stage_lookup_off;
500 ostats.time_counter = 1;
501 init_bonus(oroad.stage_lookup_off - 0x20);
502 }
503 }
504 }
505 else if (outrun.cannonball_mode == Outrun::MODE_CONT)
506 {
507 oroad.road_pos = 0;
508 oroad.tilemap_h_target = 0;
509
510 if ((ostats.cur_stage + 1) == 15)
511 {
512 if (outrun.game_state == GS_INGAME)
513 init_bonus(outils::random() % 5);
514 else
515 reload_stage1();
516 }
517 else
518 {
519 oroad.stage_lookup_off = CONTINUOUS_LEVELS[++ostats.cur_stage];
520 init_road_seg_master();
521 osprites.clear_palette_data();
522
523 // Init next tilemap
524 otiles.set_vertical_swap(); // Tell tilemap to v-scroll off/on
525
526 // Reload smoke data
527 osmoke.setup_smoke_sprite(true);
528
529 // Update palette
530 oinitengine.end_stage_props |= BIT_1; // Don't bump stage offset when fetching next palette
531 oinitengine.end_stage_props |= BIT_2;
532 opalette.pal_manip_ctrl = 1;
533 opalette.setup_sky_change();
534
535 // Denote Checkpoint Passed
536 checkpoint_marker = -1;
537
538 // Cycle Music every 5 stages
539 if (outrun.game_state == GS_INGAME)
540 {
541 if (ostats.cur_stage == 5 || ostats.cur_stage == 10)
542 omusic.cycle_music();
543 }
544 }
545 }
546
547 // Stages 0-4, do road split
548 else if (ostats.cur_stage <= 3)
549 {
550 rd_split_state = SPLIT_INIT;
551 init_split1();
552 }
553 // Stage 5: Init Bonus
554 else if (outrun.game_state == GS_INGAME)
555 {
556 init_bonus(oroad.stage_lookup_off - 0x20);
557 }
558 // Stage 5 Attract Mode: Reload Stage 1
559 else
560 {
561 reload_stage1();
562 }
563 }
564
reload_stage1()565 void OInitEngine::reload_stage1()
566 {
567 oroad.road_pos = 0;
568 oroad.tilemap_h_target = 0;
569 ostats.cur_stage = -1;
570 oroad.stage_lookup_off = -8;
571
572 ostats.clear_route_info();
573
574 end_stage_props |= BIT_1; // Loop back to stage 1 (Used by tilemap code)
575 end_stage_props |= BIT_2;
576 end_stage_props |= BIT_3;
577 osmoke.setup_smoke_sprite(true);
578 init_split_next_level();
579 }
580
581 // ------------------------------------------------------------------------------------------------
582 // Road Split 1
583 // Init Road Split & Begin Road Split
584 // Called When We're Not On The Final Stage
585 // ------------------------------------------------------------------------------------------------
init_split1()586 void OInitEngine::init_split1()
587 {
588 rd_split_state = SPLIT_CHOICE1;
589
590 oroad.road_load_split = -1;
591 oroad.road_ctrl = ORoad::ROAD_BOTH_P0_INV; // Both Roads (Road 0 Priority) (Road Split. Invert Road 0)
592 road_width_orig = oroad.road_width >> 16;
593 oroad.road_pos = 0;
594 oroad.tilemap_h_target = 0;
595 trackloader.init_track_split();
596 }
597
598 // ------------------------------------------------------------------------------------------------
599 // Road Split 2: Beginning of split. User must choose.
600 // ------------------------------------------------------------------------------------------------
init_split2()601 void OInitEngine::init_split2()
602 {
603 rd_split_state = SPLIT_CHOICE2;
604
605 // Manual adjustments to the road width, based on the current position
606 int16_t pos = (((oroad.road_pos >> 16) - 0x3F) << 3) + road_width_orig;
607 oroad.road_width = (pos << 16) | (oroad.road_width & 0xFFFF);
608 if (pos > 0xFF)
609 {
610 route_updated &= ~BIT_0;
611 init_split3();
612 }
613 }
614
615 // ------------------------------------------------------------------------------------------------
616 // Road Split 3: Road physically splits into two individual roads
617 // ------------------------------------------------------------------------------------------------
init_split3()618 void OInitEngine::init_split3()
619 {
620 rd_split_state = 4;
621
622 int16_t pos = (((oroad.road_pos >> 16) - 0x3F) << 3) + road_width_orig;
623 oroad.road_width = (pos << 16) | (oroad.road_width & 0xFFFF);
624
625 if (route_updated & BIT_0 || pos <= 0x168)
626 {
627 if (oroad.road_width >> 16 > 0x300)
628 init_split4();
629 return;
630 }
631
632 route_updated |= BIT_0; // Denote route info updated
633 route_selected = 0;
634
635 // Go Left
636 if (car_x_pos > 0)
637 {
638 route_selected = -1;
639 uint8_t inc = 1 << (3 - ostats.cur_stage);
640
641 // One of the following increment values
642
643 // Stage 1 = +8 (1 << 3 - 0)
644 // Stage 2 = +4 (1 << 3 - 1)
645 // Stage 3 = +2 (1 << 3 - 2)
646 // Stage 4 = +1 (1 << 3 - 3)
647 // Stage 5 = Road doesn't split on this stage
648
649 ostats.route_info += inc;
650 oroad.stage_lookup_off++;
651 }
652 // Go Right / Continue
653
654 end_stage_props |= BIT_0; // Set end of stage property (road splitting)
655 osmoke.load_smoke_data |= BIT_0; // Denote we should load smoke sprite data
656 ostats.routes[0]++; // Set upcoming stage number to store route info
657 ostats.routes[ostats.routes[0]] = ostats.route_info; // Store route info for course map screen
658
659 if (oroad.road_width >> 16 > 0x300)
660 init_split4();
661 }
662
663 // ------------------------------------------------------------------------------------------------
664 // Road Split 4: Road Fully Split, Remove Other Road
665 // ------------------------------------------------------------------------------------------------
666
init_split4()667 void OInitEngine::init_split4()
668 {
669 rd_split_state = 5; // init_split4
670
671 // Set Appropriate Road Control Value, Dependent On Route Chosen
672 if (route_selected != 0)
673 oroad.road_ctrl = ORoad::ROAD_R0_SPLIT;
674 else
675 oroad.road_ctrl = ORoad::ROAD_R1_SPLIT;
676
677 // Denote road split has been removed (for enemy traFfic logic)
678 road_remove_split |= BIT_0;
679
680 if (!road_curve)
681 init_split5();
682 }
683
684 // ------------------------------------------------------------------------------------------------
685 // Road Split 5: Only Draw One Section Of Road - Wait For Final Curve
686 // ------------------------------------------------------------------------------------------------
687
init_split5()688 void OInitEngine::init_split5()
689 {
690 rd_split_state = 6;
691 if (road_curve)
692 init_split6();
693 }
694
695 // ------------------------------------------------------------------------------------------------
696 // Road Split 6 - Car On Final Curve Of Split
697 // ------------------------------------------------------------------------------------------------
init_split6()698 void OInitEngine::init_split6()
699 {
700 rd_split_state = 7;
701 if (!road_curve)
702 init_split7();
703 }
704
705 // ------------------------------------------------------------------------------------------------
706 // Road Split 7: Init Road Merge Before Checkpoint (From Normal Section Of Road)
707 // ------------------------------------------------------------------------------------------------
708
init_split7()709 void OInitEngine::init_split7()
710 {
711 rd_split_state = 8;
712
713 oroad.road_ctrl = ORoad::ROAD_BOTH_P0;
714 route_selected = ~route_selected; // invert bits
715 int16_t width2 = (oroad.road_width >> 16) << 1;
716 if (route_selected == 0)
717 width2 = -width2;
718 car_x_pos += width2;
719 car_x_old += width2;
720 road_width_orig = oroad.road_pos >> 16;
721 road_width_merge = oroad.road_width >> 19; // (>> 16 and then >> 3)
722 road_remove_split &= ~BIT_0; // Denote we're back to normal road handling for enemy traffic logic
723 }
724
725 // ------------------------------------------------------------------------------------------------
726 // Road Split 9 - Do Road Merger. Road Gets Narrower Again.
727 // ------------------------------------------------------------------------------------------------
init_split9()728 void OInitEngine::init_split9()
729 {
730 rd_split_state = 10;
731
732 // Calculate narrower road width to merge roads
733 uint16_t d0 = (road_width_merge - ((oroad.road_pos >> 16) - road_width_orig)) << 3;
734
735 if (d0 <= RD_WIDTH_MERGE)
736 {
737 oroad.road_width = (RD_WIDTH_MERGE << 16) | (oroad.road_width & 0xFFFF);
738 init_split10();
739 }
740 else
741 oroad.road_width = (d0 << 16) | (oroad.road_width & 0xFFFF);
742 }
743
744
745 // ------------------------------------------------------------------------------------------------
746 // Road Split A: Checkpoint Sign
747 // ------------------------------------------------------------------------------------------------
init_split10()748 void OInitEngine::init_split10()
749 {
750 rd_split_state = 11;
751
752 if (oroad.road_pos >> 16 > 0x180)
753 {
754 rd_split_state = 0;
755 init_split_next_level();
756 }
757 }
758
759 // ------------------------------------------------------------------------------------------------
760 // Road Split B: Init Next Level
761 // ------------------------------------------------------------------------------------------------
init_split_next_level()762 void OInitEngine::init_split_next_level()
763 {
764 oroad.road_pos = 0;
765 oroad.tilemap_h_target = 0;
766 ostats.cur_stage++;
767 oroad.stage_lookup_off += 8; // Increment lookup to next block of stages
768 ostats.route_info += 0x10; // Route Info increments by 10 at each stage
769 ohud.do_mini_map();
770 init_road_seg_master();
771
772 // Clear sprite palette lookup
773 if (ostats.cur_stage)
774 osprites.clear_palette_data();
775 }
776
777 // ------------------------------------------------------------------------------------------------
778 // Bonus Road Mode Control
779 // ------------------------------------------------------------------------------------------------
780
781 // Initialize new segment of road data for bonus sequence
782 // Source: 0x8A04
init_bonus(int16_t seq)783 void OInitEngine::init_bonus(int16_t seq)
784 {
785 oroad.road_ctrl = ORoad::ROAD_BOTH_P0_INV;
786 oroad.road_pos = 0;
787 oroad.tilemap_h_target = 0;
788 oanimseq.end_seq = (uint8_t) seq; // Set End Sequence (0 - 4)
789 trackloader.init_track_bonus(oanimseq.end_seq);
790 outrun.game_state = GS_INIT_BONUS;
791 rd_split_state = 0x11;
792 bonus1();
793 }
794
bonus1()795 void OInitEngine::bonus1()
796 {
797 if (oroad.road_pos >> 16 >= 0x5B)
798 {
799 otraffic.bonus_lhs = 1; // Force traffic spawn on LHS during bonus mode
800 rd_split_state = 0x12;
801 bonus2();
802 }
803 }
804
bonus2()805 void OInitEngine::bonus2()
806 {
807 if (oroad.road_pos >> 16 >= 0xB6)
808 {
809 otraffic.bonus_lhs = 0; // Disable forced traffic spawn
810 road_width_orig = oroad.road_width >> 16;
811 rd_split_state = 0x13;
812 bonus3();
813 }
814 }
815
816 // Stretch the road to a wider width. It does this based on the car's current position.
bonus3()817 void OInitEngine::bonus3()
818 {
819 // Manual adjustments to the road width, based on the current position
820 int16_t pos = (((oroad.road_pos >> 16) - 0xB6) << 3) + road_width_orig;
821 oroad.road_width = (pos << 16) | (oroad.road_width & 0xFFFF);
822 if (pos > 0xFF)
823 {
824 route_selected = 0;
825 if (car_x_pos > 0)
826 route_selected = ~route_selected; // invert bits
827 rd_split_state = 0x14;
828 bonus4();
829 }
830 }
831
bonus4()832 void OInitEngine::bonus4()
833 {
834 // Manual adjustments to the road width, based on the current position
835 int16_t pos = (((oroad.road_pos >> 16) - 0xB6) << 3) + road_width_orig;
836 oroad.road_width = (pos << 16) | (oroad.road_width & 0xFFFF);
837 if (pos > 0x300)
838 {
839 // Set Appropriate Road Control Value, Dependent On Route Chosen
840 if (route_selected != 0)
841 oroad.road_ctrl = ORoad::ROAD_R0_SPLIT;
842 else
843 oroad.road_ctrl = ORoad::ROAD_R1_SPLIT;
844
845 // Denote road split has been removed (for enemy traFfic logic)
846 road_remove_split |= BIT_0;
847 rd_split_state = 0x15;
848 bonus5();
849 }
850 }
851
852 // Check for end of curve. Init next state when ended.
bonus5()853 void OInitEngine::bonus5()
854 {
855 if (!road_curve)
856 {
857 rd_split_state = 0x16;
858 bonus6();
859 }
860 }
861
862 // This state simply checks for the end of bonus mode
bonus6()863 void OInitEngine::bonus6()
864 {
865 if (obonus.bonus_control >= OBonus::BONUS_END)
866 rd_split_state = 0;
867 }
868
869 // SetGranularPosition
870 //
871 // Source: BD3E
872 //
873 // Uses the car increment value to set the granular position.
874 // The granular position is used to finely scroll the road by CPU 1 and smooth zooming of scenery.
875 //
876 // pos_fine is the (road_pos >> 16) * 10
877 //
878 // Notes:
879 // Disable with - bpset bd3e,1,{pc = bd76; g}
880
set_granular_position()881 void OInitEngine::set_granular_position()
882 {
883 uint16_t car_inc16 = car_increment >> 16;
884
885 uint16_t result = car_inc16 / 0x40;
886 uint16_t rem = car_inc16 % 0x40;
887
888 granular_rem += rem;
889 // When the overall counter overflows past 0x40, we must carry a 1 to the unsigned divide :)
890 if (granular_rem >= 0x40)
891 {
892 granular_rem -= 0x40;
893 result++;
894 }
895 oroad.pos_fine += result;
896 }
897
set_fine_position()898 void OInitEngine::set_fine_position()
899 {
900 uint16_t d0 = oroad.pos_fine - pos_fine_old;
901 if (d0 > 0xF)
902 d0 = 0xF;
903
904 d0 <<= 0xB;
905 osprites.sprite_scroll_speed = d0;
906
907 pos_fine_old = oroad.pos_fine;
908 }
909
910 // Check whether to initalize crash or bonus sequence code
911 //
912 // Source: 0x984E
init_crash_bonus()913 void OInitEngine::init_crash_bonus()
914 {
915 if (outrun.game_state == GS_MUSIC) return;
916
917 if (ocrash.skid_counter > 6 || ocrash.skid_counter < -6)
918 {
919 // do_skid:
920 if (otraffic.collision_traffic == 1)
921 {
922 otraffic.collision_traffic = 2;
923 uint8_t rnd = outils::random() & otraffic.collision_mask;
924 if (rnd == otraffic.collision_mask)
925 {
926 // Try to launch crash code and perform a spin
927 if (ocrash.coll_count1 == ocrash.coll_count2)
928 {
929 if (!ocrash.spin_control1)
930 {
931 ocrash.spin_control1 = 1;
932 ocrash.skid_counter_bak = ocrash.skid_counter;
933 test_bonus_mode(true); // 9924 fall through
934 return;
935 }
936 test_bonus_mode(false); // finalise skid
937 return;
938 }
939 else
940 {
941 test_bonus_mode(true); // test_bonus_mode
942 return;
943 }
944 }
945 }
946 }
947 else if (ocrash.spin_control2 == 1)
948 {
949 // 9894
950 ocrash.spin_control2 = 2;
951 if (ocrash.coll_count1 == ocrash.coll_count2)
952 {
953 ocrash.enable();
954 }
955 test_bonus_mode(false); // finalise skid
956 return;
957 }
958 else if (ocrash.spin_control1 == 1)
959 {
960 // 98c0
961 ocrash.spin_control1 = 2;
962 ocrash.enable();
963 test_bonus_mode(false); // finalise skid
964 return;
965 }
966
967 // 0x9924: Section Of Code
968 if (ocrash.coll_count1 == ocrash.coll_count2)
969 {
970 test_bonus_mode(true); // test_bonus_mode
971 }
972 else
973 {
974 ocrash.enable();
975 test_bonus_mode(false); // finalise skid
976 }
977 }
978
979 // Source: 0x993C
test_bonus_mode(bool do_bonus_check)980 void OInitEngine::test_bonus_mode(bool do_bonus_check)
981 {
982 // Bonus checking code
983 if (do_bonus_check && obonus.bonus_control)
984 {
985 // Do Bonus Text Display
986 if (outrun.cannonball_mode != Outrun::MODE_TTRIAL && obonus.bonus_state < 3)
987 obonus.do_bonus_text();
988
989 // End Seq Animation Stage #0
990 if (obonus.bonus_control == OBonus::BONUS_SEQ0)
991 {
992 if (outrun.cannonball_mode == Outrun::MODE_TTRIAL)
993 outrun.game_state = GS_INIT_GAMEOVER;
994 else
995 oanimseq.init_end_seq();
996 }
997 }
998
999 // finalise_skid:
1000 if (!ocrash.skid_counter)
1001 otraffic.collision_traffic = 0;
1002 }