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 }