1 /***************************************************************************
2     Ferrari Rendering & Handling Code.
3 
4     Much of the handling code is very messy. As such, the translated code
5     isn't great as I tried to focus on accuracy rather than refactoring.
6 
7     A good example of the randomness is a routine I've named
8       do_sound_score_slip()
9     which performs everything from updating the score, setting the audio
10     engine tone, triggering smoke effects etc. in an interwoven fashion.
11 
12     The Ferrari sprite has different properties to other game objects
13     As there's only one of them, I've rolled the additional variables into
14     this class.
15 
16     Copyright Chris White.
17     See license.txt for more details.
18 ***************************************************************************/
19 
20 #include "engine/oanimseq.hpp"
21 #include "engine/oattractai.hpp"
22 #include "engine/obonus.hpp"
23 #include "engine/ocrash.hpp"
24 #include "engine/ohud.hpp"
25 #include "engine/oinputs.hpp"
26 #include "engine/olevelobjs.hpp"
27 #include "engine/ooutputs.hpp"
28 #include "engine/ostats.hpp"
29 #include "engine/outils.hpp"
30 #include "engine/oferrari.hpp"
31 
32 OFerrari oferrari;
33 
34 static const uint16_t FERRARI_PALETTES[] =
35 {
36     OFerrari::PAL_RED,      // Red
37     OFerrari::PAL_BLUE,     // Blue
38     OFerrari::PAL_YELLOW,   // Yellow
39     OFerrari::PAL_GREEN,    // Green
40     OFerrari::PAL_CYAN,     // Cyan
41 };
42 
OFerrari(void)43 OFerrari::OFerrari(void)
44 {
45 
46 }
47 
~OFerrari(void)48 OFerrari::~OFerrari(void)
49 {
50 }
51 
init(oentry * f,oentry * p1,oentry * p2,oentry * s)52 void OFerrari::init(oentry *f, oentry *p1, oentry *p2, oentry *s)
53 {
54     state       = FERRARI_SEQ1;
55     spr_ferrari = f;
56     spr_pass1   = p1;
57     spr_pass2   = p2;
58     spr_shadow  = s;
59 
60     spr_ferrari->control |= OSprites::ENABLE;
61     spr_pass1->control   |= OSprites::ENABLE;
62     spr_pass2->control   |= OSprites::ENABLE;
63     spr_shadow->control  |= OSprites::ENABLE;
64 
65     state             = 0;
66     counter           = 0;
67     steering_old      = 0;
68     road_width_old    = 0;
69     car_state         = CAR_NORMAL;
70     auto_brake        = false;
71     torque_index      = 0;
72     torque            = 0;
73     revs              = 0;
74     rev_shift         = 0;
75     wheel_state       = WHEELS_ON;
76     wheel_traction    = TRACTION_ON;
77     is_slipping       = 0;
78     slip_sound        = 0;
79     car_inc_old       = 0;
80     car_x_diff        = 0;
81     rev_stop_flag     = 0;
82     revs_post_stop    = 0;
83     acc_post_stop     = 0;
84     rev_pitch1        = 0;
85     rev_pitch2        = 0;
86     sprite_ai_counter = 0;
87     sprite_ai_curve   = 0;
88     sprite_ai_x       = 0;
89     sprite_ai_steer   = 0;
90     sprite_car_x_bak  = 0;
91     sprite_wheel_state= 0;
92     sprite_slip_copy  = 0;
93     wheel_pal         = 0;
94     sprite_pass_y     = 0;
95     wheel_frame_reset = 0;
96     wheel_counter     = 0;
97 
98     road_width_old    = 0;
99     accel_value       = 0;
100     accel_value_bak   = 0;
101     brake_value       = 0;
102     gear_value        = false;
103     gear_bak          = false;
104     brake_subtract    = 0;
105     gear_counter      = 0;
106     rev_adjust        = 0;
107     gear_smoke        = 0;
108     gfx_smoke         = 0;
109     cornering         = 0;
110     cornering_old     = 0;
111     car_ctrl_active   = true;
112 
113     // Change high gear torque value to enable faster speeds
114     torque_lookup[0x1F] = config.engine.turbo ? 0x558 : 0x66C;
115 
116     int size = sizeof(FERRARI_PALETTES)/sizeof(FERRARI_PALETTES[0]);
117     if (config.engine.car_pal >= size)
118         config.engine.car_pal = 0;
119     ferrari_pal = FERRARI_PALETTES[config.engine.car_pal];
120 }
121 
122 // Reset all values relating to car speed, revs etc.
123 // Source: 0x61F2
reset_car()124 void OFerrari::reset_car()
125 {
126     rev_shift            = 1;  // Set normal rev shift value
127     ocrash.spin_control2 = 0;
128     revs                 = 0;
129     oinitengine.car_increment = 0;
130     gear_value           = 0;
131     gear_bak             = 0;
132     rev_adjust           = 0;
133     car_inc_old          = 0;
134     torque               = 0x1000;
135     torque_index         = 0x1F;
136     rev_stop_flag        = 0;
137     oinitengine.ingame_engine = false;
138     oinitengine.ingame_counter = 0x1E; // Set ingame counter (time until we hand control to user)
139     slip_sound           = sound::STOP_SLIP;
140     acc_adjust1          =
141     acc_adjust2          =
142     acc_adjust3          = 0;
143     brake_adjust1        =
144     brake_adjust2        =
145     brake_adjust3        = 0;
146     auto_brake           = false;
147     counter              = 0;
148     is_slipping          = 0;    // Denote not slipping/skidding
149 }
150 
tick()151 void OFerrari::tick()
152 {
153     switch (state)
154     {
155         case FERRARI_SEQ1:
156             oanimseq.ferrari_seq();
157             oanimseq.anim_seq_intro(&oanimseq.anim_pass1);
158             oanimseq.anim_seq_intro(&oanimseq.anim_pass2);
159             break;
160 
161         case FERRARI_SEQ2:
162             oanimseq.anim_seq_intro(&oanimseq.anim_ferrari);
163             oanimseq.anim_seq_intro(&oanimseq.anim_pass1);
164             oanimseq.anim_seq_intro(&oanimseq.anim_pass2);
165             break;
166 
167         case FERRARI_INIT:
168             if (spr_ferrari->control & OSprites::ENABLE)
169                 if (outrun.tick_frame)
170                     init_ingame();
171             break;
172 
173         case FERRARI_LOGIC:
174             if (spr_ferrari->control & OSprites::ENABLE)
175             {
176                 if (outrun.tick_frame)
177                     logic();
178                 else
179                     draw_sprite(spr_ferrari);
180             }
181             if (spr_pass1->control & OSprites::ENABLE)
182             {
183                 if (outrun.tick_frame)
184                     set_passenger_sprite(spr_pass1);
185                 else
186                     draw_sprite(spr_pass1);
187             }
188 
189             if (spr_pass2->control & OSprites::ENABLE)
190             {
191                 if (outrun.tick_frame)
192                     set_passenger_sprite(spr_pass2);
193                 else
194                     draw_sprite(spr_pass2);
195             }
196             break;
197 
198         // Ferrari End Sequence Logic
199         case FERRARI_END_SEQ:
200             oanimseq.tick_end_seq();
201             break;
202     }
203 }
204 
205 // Initalize Ferrari Start Sequence
206 //
207 // Source: 6036
208 //
209 // Note the remainder of this block is handled in oanimseq.ferrari_seq
init_ingame()210 void OFerrari::init_ingame()
211 {
212     // turn_off:
213     car_state = CAR_NORMAL;
214     state = FERRARI_LOGIC;
215     spr_ferrari->reload = 0;
216     spr_ferrari->counter = 0;
217     sprite_ai_counter = 0;
218     sprite_ai_curve = 0;
219     sprite_ai_x = 0;
220     sprite_ai_steer = 0;
221     sprite_car_x_bak = 0;
222     sprite_wheel_state = 0;
223 
224     // Passengers
225     // move.l  #SetPassengerSprite,$42(a5)
226     spr_pass1->reload = 0;
227     spr_pass1->counter = 0;
228     spr_pass1->xw1 = 0;
229     // move.l  #SetPassengerSprite,$82(a5)
230     spr_pass2->reload = 0;
231     spr_pass2->counter = 0;
232     spr_pass2->xw1 = 0;
233 }
234 
235 // Source: 9C84
logic()236 void OFerrari::logic()
237 {
238     switch (obonus.bonus_control)
239     {
240         // Not Bonus Mode
241         case OBonus::BONUS_DISABLE:
242             ferrari_normal();
243             break;
244 
245         case OBonus::BONUS_INIT:
246             rev_shift = 2;          // Set Double Rev Shift Value
247             obonus.bonus_control = OBonus::BONUS_TICK;
248 
249         case OBonus::BONUS_TICK:
250             oattractai.check_road_bonus();
251             oattractai.set_steering_bonus();
252 
253             // Accelerate Car
254             if (oinitengine.rd_split_state == 0 || (oroad.road_pos >> 16) <= 0x163)
255             {
256                 oinputs.acc_adjust = 0xFF;
257                 oinputs.brake_adjust = 0;
258                 setup_ferrari_bonus_sprite();
259                 return;
260             }
261             else // init_end_anim
262             {
263                 rev_shift = 1;
264                 obonus.bonus_control = OBonus::BONUS_SEQ0;
265                 // note fall through!
266             }
267 
268         case OBonus::BONUS_SEQ0:
269             if ((oroad.road_pos >> 16) < 0x18E)
270             {
271                 init_end_seq();
272                 return;
273             }
274             obonus.bonus_control = OBonus::BONUS_SEQ1;
275             // fall through
276 
277         case OBonus::BONUS_SEQ1:
278             if ((oroad.road_pos >> 16) < 0x18F)
279             {
280                 init_end_seq();
281                 return;
282             }
283             obonus.bonus_control = OBonus::BONUS_SEQ2;
284 
285         case OBonus::BONUS_SEQ2:
286             if ((oroad.road_pos >> 16) < 0x190)
287             {
288                 init_end_seq();
289                 return;
290             }
291             obonus.bonus_control = OBonus::BONUS_SEQ3;
292 
293         case OBonus::BONUS_SEQ3:
294             if ((oroad.road_pos >> 16) < 0x191)
295             {
296                 init_end_seq();
297                 return;
298             }
299             else
300             {
301                 oferrari.car_ctrl_active = false; // -1
302                 oinitengine.car_increment = 0;
303                 obonus.bonus_control = OBonus::BONUS_END;
304             }
305 
306         case OBonus::BONUS_END:
307             oinputs.acc_adjust = 0;
308             oinputs.brake_adjust = 0xFF;
309             do_end_seq();
310             break;
311     }
312 }
313 
314 // Ferrari - Normal Game Logic (Non-Bonus Mode Code)
315 //
316 // Source: 0x9CB4
ferrari_normal()317 void OFerrari::ferrari_normal()
318 {
319     if (FORCE_AI && outrun.game_state == GS_INGAME)
320     {
321         oattractai.tick_ai_enhanced();
322         setup_ferrari_sprite();
323         return;
324     }
325 
326     switch (outrun.game_state)
327     {
328         // Attract Mode: Process AI Code and fall through
329         case GS_INIT:
330         case GS_ATTRACT:
331             if (config.engine.new_attract)
332                 oattractai.tick_ai_enhanced();
333             else
334                 oattractai.tick_ai();
335             setup_ferrari_sprite();
336             break;
337 
338         case GS_INIT_BEST1:
339         case GS_BEST1:
340         case GS_INIT_LOGO:
341         case GS_LOGO:
342         case GS_INIT_GAME:
343         case GS_INIT_GAMEOVER:
344         case GS_GAMEOVER:
345             oinputs.brake_adjust = 0;
346         case GS_START1:
347         case GS_START2:
348         case GS_START3:
349             oinputs.steering_adjust = 0;
350         case GS_INGAME:
351         case GS_INIT_BONUS:
352         case GS_BONUS:
353         case GS_INIT_MAP:
354         case GS_MAP:
355             setup_ferrari_sprite();
356             break;
357 
358         case GS_INIT_MUSIC:
359         case GS_MUSIC:
360             return;
361     }
362 }
363 
364 // Setup Ferrari Sprite Object
365 //
366 // Source: 0x9D30
setup_ferrari_sprite()367 void OFerrari::setup_ferrari_sprite()
368 {
369     spr_ferrari->y = 221; // Set Default Ferrari Y
370 
371     // Test Collision With Other Sprite Object
372     if (olevelobjs.collision_sprite)
373     {
374         if (ocrash.coll_count1 == ocrash.coll_count2)
375         {
376             ocrash.coll_count1++;
377             olevelobjs.collision_sprite = 0;
378             ocrash.crash_state = 0;
379         }
380     }
381 
382     // Setup Default Ferrari Properties
383     spr_ferrari->x = 0;
384     spr_ferrari->zoom = 0x7F;
385     spr_ferrari->draw_props = oentry::BOTTOM    ; // Anchor Bottom
386     spr_ferrari->shadow = 3;
387     spr_ferrari->width = 0;
388     spr_ferrari->priority = spr_ferrari->road_priority = 0x1FD;
389 
390     // Set Ferrari H-Flip Based On Steering & Speed
391     int16_t d4 = oinputs.steering_adjust;
392 
393     // If steering close to centre clear d4 to ignore h-flip of Ferrari
394     if (d4 >= -8 && d4 <= 7)
395         d4 = 0;
396     // If speed too slow clear d4 to ignore h-flip of Ferrari
397     if (oinitengine.car_increment >> 16 < 0x14)
398         d4 = 0;
399 
400     // cont2:
401     d4 >>= 2; // increase change of being close to zero and no h-flip occurring
402 
403     int16_t x_off = 0;
404 
405     // ------------------------------------------------------------------------
406     // Not Skidding
407     // ------------------------------------------------------------------------
408     if (!ocrash.skid_counter)
409     {
410         if (d4 >= 0)
411             spr_ferrari->control &= ~OSprites::HFLIP;
412         else
413             spr_ferrari->control |= OSprites::HFLIP;
414 
415         // 0x9E4E not_skidding:
416 
417         // Calculate change in road y, so we can determine incline frame for ferrari
418         int16_t y = oroad.road_y[oroad.road_p0 + (0x3D0 / 2)] - oroad.road_y[oroad.road_p0 + (0x3E0 / 2)];
419 
420         // Converts y difference to a frame value (this is for when on an incline)
421         int16_t incline_frame_offset = 0;
422         if (y >= 0x12) incline_frame_offset += 8;
423         if (y >= 0x13) incline_frame_offset += 8;
424 
425         // Get abs version of ferrari turn
426         int16_t turn_frame_offset = 0;
427         int16_t d2 = d4;
428         if (d2 < 0) d2 = -d2;
429         if (d2 >= 0x12) turn_frame_offset += 0x18;
430         if (d2 >= 0x1E) turn_frame_offset += 0x18;
431 
432         // Set Ferrari Sprite Properties
433         uint32_t offset = outrun.adr.sprite_ferrari_frames + turn_frame_offset + incline_frame_offset;
434         spr_ferrari->addr = roms.rom0p->read32(offset);     // Set Ferrari Frame Address
435         sprite_pass_y = roms.rom0p->read16(offset + 4); // Set Passenger Y Offset
436         x_off = roms.rom0p->read16(offset + 6); // Set Ferrari Sprite X Offset
437 
438         if (d4 < 0) x_off = -x_off;
439     }
440     // ------------------------------------------------------------------------
441     // Skidding
442     // ------------------------------------------------------------------------
443     else
444     {
445         int16_t skid_counter = ocrash.skid_counter;
446 
447         if (skid_counter < 0)
448         {
449             spr_ferrari->control |= OSprites::HFLIP;
450             skid_counter = -skid_counter; // Needs to be positive
451         }
452         else
453             spr_ferrari->control &= ~OSprites::HFLIP;
454 
455         int16_t frame = 0;
456 
457         if (skid_counter >= 3)  frame += 8;
458         if (skid_counter >= 6)  frame += 8;
459         if (skid_counter >= 12) frame += 8;
460 
461         // Calculate incline
462         int16_t y = oroad.road_y[oroad.road_p0 + (0x3D0 / 2)] - oroad.road_y[oroad.road_p0 + (0x3E0 / 2)];
463 
464         int16_t incline_frame_offset = 0;
465         if (y >= 0x12) incline_frame_offset += 0x20;
466         if (y >= 0x13) incline_frame_offset += 0x20;
467 
468         uint32_t offset = outrun.adr.sprite_skid_frames + frame + incline_frame_offset;
469         spr_ferrari->addr = roms.rom0p->read32(offset); // Set Ferrari Frame Address
470         sprite_pass_y = roms.rom0p->read16(offset + 4); // Set Passenger Y Offset
471         x_off = roms.rom0p->read16(offset + 6);         // Set Ferrari Sprite X Offset
472         wheel_traction = TRACTION_OFF;                  // Both wheels have lost traction
473 
474         if (ocrash.skid_counter >= 0) x_off = -x_off;
475     }
476 
477     spr_ferrari->x += x_off;
478 
479     shake();
480     set_ferrari_palette();
481     draw_sprite(spr_ferrari);
482 }
483 
484 // Bonus Mode: Setup Ferrari Sprite Details
485 //
486 // Source: 0xA212
setup_ferrari_bonus_sprite()487 void OFerrari::setup_ferrari_bonus_sprite()
488 {
489     // Setup Default Ferrari Properties
490     spr_ferrari->y = 221;
491     //spr_ferrari->x = 0; not really needed as set below
492     spr_ferrari->priority = spr_ferrari->road_priority = 0x1FD;
493 
494     if (oinputs.steering_adjust > 0)
495         spr_ferrari->control &= ~OSprites::HFLIP;
496     else
497         spr_ferrari->control |= OSprites::HFLIP;
498 
499     // Get abs version of ferrari turn
500     int16_t turn_frame_offset = 0;
501     int16_t d2 = oinputs.steering_adjust >> 2;
502     if (d2 < 0) d2 = -d2;
503     if (d2 >= 0x4) turn_frame_offset += 0x18;
504     if (d2 >= 0x8) turn_frame_offset += 0x18;
505 
506     // Set Ferrari Sprite Properties
507     uint32_t offset   = outrun.adr.sprite_ferrari_frames + turn_frame_offset + 8; // 8 denotes the 'level' frames, no slope.
508     spr_ferrari->addr = roms.rom0p->read32(offset);     // Set Ferrari Frame Address
509     sprite_pass_y     = roms.rom0p->read16(offset + 4); // Set Passenger Y Offset
510     int16_t x_off     = roms.rom0p->read16(offset + 6); // Set Ferrari Sprite X Offset
511 
512     if (oinputs.steering_adjust < 0) x_off = -x_off;
513     spr_ferrari->x = x_off;
514 
515     set_ferrari_palette();
516     draw_sprite(spr_ferrari);
517 }
518 
519 // Source: 0xA1CE
init_end_seq()520 void OFerrari::init_end_seq()
521 {
522     // AI for remainder
523     oattractai.check_road_bonus();
524     oattractai.set_steering_bonus();
525     // Brake Car!
526     oinputs.acc_adjust = 0;
527     oinputs.brake_adjust = 0xFF;
528     do_end_seq();
529 }
530 
531 // Drive Ferrari to the side during end sequence
532 //
533 // Source: 0xA298
do_end_seq()534 void OFerrari::do_end_seq()
535 {
536     spr_ferrari->y = 221;
537     spr_ferrari->priority = spr_ferrari->road_priority = 0x1FD;
538 
539     // Set Ferrari Frame
540     // +0 [Long]: Address of frame
541     // +4 [Byte]: Passenger Offset (always 0!)
542     // +5 [Byte]: Ferrari X Change
543     // +6 [Byte]: Sprite Colour Palette
544     // +7 [Byte]: H-Flip
545 
546     uint32_t addr = outrun.adr.anim_ferrari_frames + ((obonus.bonus_control - 0xC) << 1);
547 
548     spr_ferrari->addr    = roms.rom0p->read32(addr);
549     sprite_pass_y        = roms.rom0p->read8(4 + addr);  // Set Passenger Y Offset
550     spr_ferrari->x       = roms.rom0p->read8(5 + addr);
551     spr_ferrari->pal_src = ferrari_pal;//  roms.rom0p->read8(6 + addr);
552     spr_ferrari->control = roms.rom0p->read8(7 + addr) | (spr_ferrari->control & 0xFE); // HFlip
553 
554     osprites.map_palette(spr_ferrari);
555     osprites.do_spr_order_shadows(spr_ferrari);
556 }
557 
558 // - Update Car Palette To Adjust Brake Lamp
559 // - Also Control Speed at which wheels spin through palette adjustment
560 //
561 // Source: 0x9F7C
set_ferrari_palette()562 void OFerrari::set_ferrari_palette()
563 {
564     uint8_t pal;
565 
566     // Denote palette for brake light
567     if (oinputs.brake_adjust >= OInputs::BRAKE_THRESHOLD1)
568     {
569         outrun.outputs->set_digital(OOutputs::D_BRAKE_LAMP);
570         pal = 2;
571     }
572     else
573     {
574         outrun.outputs->clear_digital(OOutputs::D_BRAKE_LAMP);
575         pal = 0;
576     }
577 
578     // Car Moving
579     if (oinitengine.car_increment >> 16 != 0)
580     {
581         int16_t car_increment = 5 - (oinitengine.car_increment >> 21);
582         if (car_increment < 0) car_increment = 0;
583         wheel_frame_reset = car_increment;
584 
585         // Increment wheel palette offset (to move wheels)
586         if (wheel_counter <= 0)
587         {
588             wheel_counter = wheel_frame_reset;
589             wheel_pal++;
590         }
591         else
592             wheel_counter--;
593     }
594     spr_ferrari->pal_src = pal + ferrari_pal + (wheel_pal & 1);
595 }
596 
597 // Set Ferrari X Position
598 //
599 // - Reads steering value
600 // - Converts to a practical change in x position
601 // - There are a number of special cases, as you will see by looking at the code
602 //
603 // In use:
604 //
605 // d0 = Amount to adjust car x position by
606 //
607 // Source: 0xC1B2
608 
set_ferrari_x()609 void OFerrari::set_ferrari_x()
610 {
611     int16_t steering = oinputs.steering_adjust;
612 
613     // Hack to reduce the amount you can steer left and right at the start of Stage 1
614     // The amount you can steer increases as you approach road position 0x7F
615     if (ostats.cur_stage == 0 && oinitengine.rd_split_state == 0 && (oroad.road_pos >> 16) <= 0x7F)
616     {
617         steering = (steering * (oroad.road_pos >> 16)) >> 7;
618     }
619 
620     steering -= steering_old;
621     if (steering > 0x40) steering = 0x40;
622     else if (steering < -0x40) steering = -0x40;
623     steering_old += steering;
624     steering = steering_old;
625 
626     // This block of code reduces the amount the player
627     // can steer left and right, when below a particular speed
628     if (wheel_state == OFerrari::WHEELS_ON && (oinitengine.car_increment >> 16) <= 0x7F)
629     {
630         steering = (steering * (oinitengine.car_increment >> 16)) >> 7;
631     }
632 
633     // Check Road Curve And Adjust X Value Accordingly
634     // This effectively makes it harder to steer into sharp corners
635     int16_t road_curve = oinitengine.road_curve;
636     if (road_curve)
637     {
638         road_curve -= 0x40;
639         if (road_curve < 0)
640         {
641             int16_t diff_from_max = (MAX_SPEED >> 17) - (oinitengine.car_increment >> 16);
642             if (diff_from_max < 0)
643             {
644                 int16_t curve = diff_from_max * road_curve;
645                 int32_t result = (int32_t) steering * (0x24C0 - curve);
646                 steering = result / 0x24C0;
647             }
648         }
649     }
650 
651     steering >>= 3;
652     int16_t steering2 = steering;
653     steering >>= 2;
654     steering += steering2;
655     steering = -steering;
656 
657     // Return if car is not moving
658     if (outrun.game_state == GS_INGAME && oinitengine.car_increment >> 16 == 0)
659     {
660         // Hack so car is centered if we want to bypass countdown sequence
661         int16_t road_width_change = (oroad.road_width >> 16) - road_width_old;
662         road_width_old = (oroad.road_width >> 16);
663         if (oinitengine.car_x_pos < 0)
664             road_width_change = -road_width_change;
665         oinitengine.car_x_pos += road_width_change;
666         // End of Hack
667         return;
668     }
669 
670     oinitengine.car_x_pos += steering;
671 
672     int16_t road_width_change = (oroad.road_width >> 16) - road_width_old;
673     road_width_old = (oroad.road_width >> 16);
674     if (oinitengine.car_x_pos < 0)
675         road_width_change = -road_width_change;
676     oinitengine.car_x_pos += road_width_change;
677 }
678 
679 // Ensure car does not stray too far from sides of road
680 // There are three parts to this function:
681 // a. Normal Road
682 // b. Road Split
683 // c. Dual Lanes
684 //
685 // Source: 0xC2B0
set_ferrari_bounds()686 void OFerrari::set_ferrari_bounds()
687 {
688     // d0 = road_width
689     // d2 = car_x_pos
690 
691     int16_t road_width16 = oroad.road_width >> 16;
692     int16_t d1 = 0;
693 
694     // Road Split Both Lanes
695     if (oinitengine.rd_split_state == 4)
696     {
697         if (oinitengine.car_x_pos < 0)
698             road_width16 = -road_width16;
699         d1 = road_width16 + 0x140;
700         road_width16 -= 0x140;
701     }
702     // One Lane
703     else if (road_width16 <= 0xFF)
704     {
705         road_width16 += OFFROAD_BOUNDS;
706         d1 = road_width16;
707         road_width16 = -road_width16;
708     }
709     // Two Lanes - road_two_lanes:
710     else
711     {
712         if (oinitengine.car_x_pos < 0)
713             road_width16 = -road_width16;
714         d1 = road_width16 + OFFROAD_BOUNDS;
715         road_width16 -= OFFROAD_BOUNDS;
716     }
717 
718     // Set Bounds
719     if (oinitengine.car_x_pos < road_width16)
720         oinitengine.car_x_pos = road_width16;
721     else if (oinitengine.car_x_pos > d1)
722         oinitengine.car_x_pos = d1;
723 
724     oroad.car_x_bak = oinitengine.car_x_pos;
725     oroad.road_width_bak = oroad.road_width >> 16;
726 }
727 
728 // Check Car Is Still On Road
729 //
730 // Source: 0xBFDC
check_wheels()731 void OFerrari::check_wheels()
732 {
733     wheel_state = WHEELS_ON;
734     wheel_traction = TRACTION_ON;
735     uint16_t road_width = oroad.road_width >> 16;
736 
737     switch (oroad.road_ctrl)
738     {
739         case ORoad::ROAD_OFF:         // Both Roads Off
740             return;
741 
742         // Single Road
743         case ORoad::ROAD_R0:          // Road 0
744         case ORoad::ROAD_R1:          // Road 1
745         case ORoad::ROAD_R0_SPLIT:    // Road 0 (Road Split.)
746         case ORoad::ROAD_R1_SPLIT:    // Road 1 (Road Split. Invert Road 1)
747         {
748             int16_t x = oinitengine.car_x_pos;
749 
750             if (oroad.road_ctrl == ORoad::ROAD_R0_SPLIT)
751                 x -= road_width;
752             else
753                 x += road_width;
754 
755             if (oroad.road_ctrl == ORoad::ROAD_R0_SPLIT || oroad.road_ctrl == ORoad::ROAD_R1_SPLIT)
756             {
757                 if (x > -0xD4 && x <= 0xD4) return;
758                 else if (x < -0x104 || x > 0x104) set_wheels(WHEELS_OFF);
759                 else if (x > 0xD4 && x <= 0x104) set_wheels(WHEELS_LEFT_OFF);
760                 else if (x <= 0xD4 && x >= -0x104) set_wheels(WHEELS_RIGHT_OFF);
761             }
762             else
763             {
764                 if (x > -0xD4 && x <= 0xD4) return;
765                 else if (x < -0x104 || x > 0x104) set_wheels(WHEELS_OFF);
766                 else if (x > 0xD4 && x <= 0x104) set_wheels(WHEELS_RIGHT_OFF);
767                 else if (x <= 0xD4 && x >= -0x104) set_wheels(WHEELS_LEFT_OFF);
768             }
769         }
770         break;
771 
772         // Both Roads
773         case ORoad::ROAD_BOTH_P0:     // Both Roads (Road 0 Priority) [DEFAULT]
774         case ORoad::ROAD_BOTH_P1:     // Both Roads (Road 1 Priority)
775         case ORoad::ROAD_BOTH_P0_INV: // Both Roads (Road 0 Priority) (Road Split. Invert Road 1)
776         case ORoad::ROAD_BOTH_P1_INV: // Both Roads (Road 1 Priority) (Road Split. Invert Road 1)
777         {
778             int16_t x = oinitengine.car_x_pos;
779 
780             if (road_width > 0xFF)
781             {
782                 if (x < 0)
783                     x += road_width;
784                 else
785                     x -= road_width;
786 
787                 if (x < -0x104 || x > 0x104) set_wheels(WHEELS_OFF);
788                 else if (x < -0xD4) set_wheels(WHEELS_RIGHT_OFF);
789                 else if (x > 0xD4) set_wheels(WHEELS_LEFT_OFF);
790             }
791             else
792             {
793                 road_width += 0x104;
794 
795                 if (x >= 0)
796                 {
797                     if (x > road_width) set_wheels(WHEELS_OFF);
798                     else if (x > road_width - 0x30) set_wheels(WHEELS_LEFT_OFF);
799                 }
800                 else
801                 {
802                     x = -x;
803                     if (x > road_width) set_wheels(WHEELS_OFF);
804                     else if (x > road_width - 0x30) set_wheels(WHEELS_RIGHT_OFF);
805                 }
806             }
807         }
808         break;
809     }
810 }
811 
set_wheels(uint8_t new_state)812 void OFerrari::set_wheels(uint8_t new_state)
813 {
814     wheel_state = new_state;
815     wheel_traction = (new_state == WHEELS_OFF) ? 2 : 1;
816 }
817 
818 // Adjusts the x-position of the car based on the curve.
819 // This effectively stops the user driving through the courses in a straight line.
820 // (sticks the car to the track).
821 //
822 // Source: 0xBF6E
set_curve_adjust()823 void OFerrari::set_curve_adjust()
824 {
825     int16_t x_diff = oroad.road_x[170] - oroad.road_x[511];
826 
827     // Invert x diff when taking roadsplit
828     if (oinitengine.rd_split_state && oinitengine.car_x_pos < 0)
829         x_diff = -x_diff;
830 
831     x_diff >>= 6;
832 
833     if (x_diff)
834     {
835         x_diff *= (oinitengine.car_increment >> 16);
836         x_diff /= config.engine.grippy_tyres ? 0xFF : 0xDC;
837         x_diff <<= 1;
838         oinitengine.car_x_pos += x_diff;
839     }
840 }
841 
842 // Draw Shadow below Ferrari
843 //
844 // Source: 0xA7BC
draw_shadow()845 void OFerrari::draw_shadow()
846 {
847     if (spr_shadow->control & OSprites::ENABLE)
848     {
849         if (outrun.game_state == GS_MUSIC) return;
850 
851         if (outrun.tick_frame)
852         {
853             spr_shadow->road_priority = spr_ferrari->road_priority - 1;
854             spr_shadow->x = spr_ferrari->x;
855             spr_shadow->y = 222;
856             spr_shadow->zoom = 0x99;
857             spr_shadow->draw_props = 8;
858             spr_shadow->addr = outrun.adr.shadow_data;
859         }
860 
861         if (oroad.get_view_mode() != ORoad::VIEW_INCAR)
862             osprites.do_spr_order_shadows(spr_shadow);
863     }
864 }
865 
866 // Set Passenger Sprite X/Y Position.
867 //
868 // Routine is used separately for both male and female passengers.
869 //
870 // In use:
871 // a1 = Passenger XY offset table
872 // a5 = Passenger 1 / Passenger 2 Sprite
873 // a6 = Car Sprite
874 //
875 // Memory locations:
876 // 62F00 = Car
877 // 62F40 = Passenger 1 (Man)
878 // 62F80 = Passenger 2 (Girl)
879 //
880 // Source: A568
set_passenger_sprite(oentry * sprite)881 void OFerrari::set_passenger_sprite(oentry* sprite)
882 {
883     sprite->road_priority = spr_ferrari->road_priority;
884     sprite->priority = spr_ferrari->priority + 1;
885     uint16_t frame = sprite_pass_y << 3;
886 
887     // Is this a bug in the original? Note that by negating HFLIP check the passengers
888     // shift right a few pixels on acceleration.
889     if ((oinitengine.car_increment >> 16 >= 0x14) && !(spr_ferrari->control & OSprites::HFLIP))
890         frame += 4;
891 
892     // --------------------------------------------------------------------------------------------
893     // Set Palette
894     // --------------------------------------------------------------------------------------------
895     uint8_t pal = 0;
896 
897     // Test for car collision frame
898     if (sprite_pass_y == 9)
899     {
900         // Set Brown/Blonde Palette depending on whether man or woman
901         if (sprite == spr_pass1) pal = 0xA;
902         else pal = 0x8;
903     }
904     else
905     {
906         if (sprite == spr_pass1) pal = 0x2D;
907         else pal = 0x2E;
908     }
909 
910     // --------------------------------------------------------------------------------------------
911     // Set X/Y Position
912     // --------------------------------------------------------------------------------------------
913 
914     sprite->pal_src = pal;
915     uint32_t offset_table = ((sprite == spr_pass1) ? PASS1_OFFSET : PASS2_OFFSET) + frame;
916     sprite->x = spr_ferrari->x + roms.rom0.read16(&offset_table);
917     sprite->y = spr_ferrari->y + roms.rom0.read16(offset_table);
918 
919     sprite->zoom = 0x7F;
920     sprite->draw_props = 8;
921     sprite->shadow = 3;
922     sprite->width = 0;
923 
924     set_passenger_frame(sprite);
925     draw_sprite(sprite);
926 }
927 
928 // Set Passenger Sprite Frame
929 //
930 // - Set the appropriate male/female frame
931 // - Uses the car's speed to set the hair frame
932 // - Also handles skid case
933 //
934 // Source: 0xA632
935 
set_passenger_frame(oentry * sprite)936 void OFerrari::set_passenger_frame(oentry* sprite)
937 {
938     uint32_t addr = outrun.adr.sprite_pass_frames;
939     if (sprite == spr_pass2) addr += 4; // Female frames
940     uint16_t inc = oinitengine.car_increment >> 16;
941 
942     // Car is moving
943     // Use adjusted increment/speed of car as reload value for sprite counter (to ultimately set hair frame)
944     if (inc != 0)
945     {
946         if (inc > 0xFF) inc = 0xFF;
947         inc >>= 5;
948         int16_t counter = 9 - inc;
949         if (counter < 0) counter = 0;
950         sprite->reload = counter;
951         if (sprite->counter <= 0)
952         {
953             sprite->counter = sprite->reload;
954             sprite->xw1++; // Reuse this as a personal tick counter to flick between hair frames
955         }
956         else
957             sprite->counter--;
958 
959         inc = (sprite->xw1 & 1) << 3;
960     }
961 
962     // Check Skid
963     if (sprite->pass_props >= 9)
964     {
965         // skid left
966         if (ocrash.skid_counter > 0)
967         {
968             sprite->addr = (sprite == spr_pass1) ?
969                 outrun.adr.sprite_pass1_skidl : outrun.adr.sprite_pass2_skidl;
970         }
971         // skid right
972         else
973         {
974             sprite->addr = (sprite == spr_pass1) ?
975                 outrun.adr.sprite_pass1_skidr : outrun.adr.sprite_pass2_skidr;
976         }
977     }
978     else
979         sprite->addr = roms.rom0p->read32(addr + inc);
980 }
981 
982 // ------------------------------------------------------------------------------------------------
983 //                                       CAR HANDLING ROUTINES
984 // ------------------------------------------------------------------------------------------------
985 
986 // Main routine handling car speed, revs, torque
987 //
988 // Source: 0x6288
move()989 void OFerrari::move()
990 {
991     if (car_ctrl_active)
992     {
993         // Auto braking if necessary
994         if (outrun.game_state != GS_ATTRACT && auto_brake)
995             oinputs.acc_adjust = 0;
996 
997         // Set Gear For Demo Mode
998         if (FORCE_AI ||
999             outrun.game_state == GS_ATTRACT || outrun.game_state == GS_BONUS ||
1000             config.controls.gear == config.controls.GEAR_AUTO)
1001         {
1002             // demo_mode_gear
1003             oinputs.gear = (oinitengine.car_increment >> 16 > 0xA0);
1004         }
1005 
1006         gfx_smoke = 0;
1007 
1008         // --------------------------------------------------------------------
1009         // CRASH CODE - Slow Car
1010         // --------------------------------------------------------------------
1011         if (ocrash.crash_counter && ocrash.spin_control1 <= 0)
1012         {
1013             oinitengine.car_increment = (oinitengine.car_increment & 0xFFFF) | ((((oinitengine.car_increment >> 16) * 31) >> 5) << 16);
1014             revs = 0;
1015             gear_value = 0;
1016             gear_bak = 0;
1017             gear_smoke = 0;
1018             torque = 0x1000;
1019         }
1020         // --------------------------------------------------------------------
1021         // NO CRASH
1022         // --------------------------------------------------------------------
1023         else
1024         {
1025             if (car_state >= 0) car_state = CAR_NORMAL; // No crash - Clear smoke from wheels
1026 
1027             // check_time_expired:
1028             // 631E: Clear acceleration value if time out
1029             if ((ostats.time_counter & 0xFF) == 0)
1030                 oinputs.acc_adjust = 0;
1031 
1032             // --------------------------------------------------------------------
1033             // Do Car Acceleration / Revs / Torque
1034             // Note: Torque gets set based on gear car is in
1035             // --------------------------------------------------------------------
1036 
1037             // do_acceleration:
1038             car_acc_brake();
1039 
1040             int32_t d2 = revs / torque;
1041 
1042             if (!oinitengine.ingame_engine)
1043             {
1044                 tick_engine_disabled(d2);
1045             }
1046             else
1047             {
1048                 int16_t d1 = torque_index;
1049 
1050                 if (gear_counter == 0)
1051                     do_gear_torque(d1);
1052             }
1053 
1054             // set_torque:
1055             int16_t new_torque = torque_lookup[torque_index];
1056             torque = new_torque;
1057             d2 = (int32_t) (d2 & 0xFFFF) * new_torque; // unsigned multiply
1058             int32_t accel_copy = accel_value << 16;
1059             int32_t rev_adjust_new = 0;
1060 
1061             if (gear_counter != 0)
1062                 rev_adjust_new = tick_gear_change(d2 >> 16);
1063 
1064             // Compare Accelerator To Proposed New Speed
1065             //
1066             // d0 = Desired Accelerator Value [accel_copy]
1067             // d1 = Torque Value [new_torque]
1068             // d2 = Proposed New Rev Value [d2]
1069             // d4 = Rev Adjustment (due to braking / off road etc / revs being higher than desired) [rev_adjust]
1070             else if (d2 != accel_copy)
1071             {
1072                 if (accel_copy >= d2)
1073                     rev_adjust_new = get_speed_inc_value(new_torque, d2);
1074                 else
1075                     rev_adjust_new = get_speed_dec_value(new_torque);
1076             }
1077 
1078             // test_smoke:
1079             if (gear_smoke)
1080                 rev_adjust_new = tick_smoke(); // Note this also changes the speed [stored in d4]
1081 
1082             // cont2:
1083             set_brake_subtract();               // Calculate brake value to subtract from revs
1084             finalise_revs(d2, rev_adjust_new);  // Subtract calculated value from revs
1085             convert_revs_speed(new_torque, d2); // d2 is converted from revs to speed
1086 
1087             // Ingame Control Not Active. Clear Car Speed
1088             if (!oinitengine.ingame_engine)
1089             {
1090                 oinitengine.car_increment = 0;
1091                 car_inc_old = 0;
1092             }
1093             // Set New Car Speed to car_increment
1094             else if (outrun.game_state != GS_BONUS)
1095             {
1096                 int16_t diff = car_inc_old - (d2 >> 16); // Old Speed - New Speed
1097 
1098                 // Car is moving
1099                 if (diff != 0)
1100                 {
1101                     // Car Speeding Up (New Speed is faster)
1102                     if (diff < 0)
1103                     {
1104                         diff = -diff;
1105                         uint8_t adjust = 2;
1106                         if (oinitengine.car_increment >> 16 <= 0x28)
1107                             adjust >>= 1;
1108                         if (diff > 2)
1109                             d2 = (car_inc_old + adjust) << 16;
1110                     }
1111                     // Car Slowing Down
1112                     else if (diff > 0)
1113                     {
1114                         uint8_t adjust = 2;
1115                         if (brake_subtract)
1116                             adjust <<= 2;
1117                         if (diff > adjust)
1118                             d2 = (car_inc_old - adjust) << 16;
1119                     }
1120                 }
1121                 oinitengine.car_increment = d2;
1122             }
1123             else
1124                 oinitengine.car_increment = d2;
1125         } // end crash if/else block
1126 
1127         // move_car_rev:
1128         update_road_pos();
1129         ohud.draw_rev_counter();
1130     } // end car_ctrl_active
1131 
1132     // Check whether we want to play slip sound
1133     // check_slip
1134     if (gfx_smoke)
1135     {
1136         car_state = CAR_SMOKE; // Set smoke from car wheels
1137         if (oinitengine.car_increment >> 16)
1138         {
1139             if (slip_sound == sound::STOP_SLIP)
1140                 osoundint.queue_sound(slip_sound = sound::INIT_SLIP);
1141         }
1142         else
1143             osoundint.queue_sound(slip_sound = sound::STOP_SLIP);
1144     }
1145     // no_smoke:
1146     else
1147     {
1148         if (slip_sound != sound::STOP_SLIP)
1149             osoundint.queue_sound(slip_sound = sound::STOP_SLIP);
1150     }
1151     // move_car
1152     car_inc_old = oinitengine.car_increment >> 16;
1153     counter++;
1154 
1155     // During Countdown: Clear Car Speed
1156     if (outrun.game_state == GS_START1 || outrun.game_state == GS_START2 || outrun.game_state == GS_START3)
1157     {
1158         oinitengine.car_increment = 0;
1159         car_inc_old = 0;
1160     }
1161 
1162 }
1163 
1164 // Handle revs/torque when in-game engine disabled
1165 //
1166 // Source: 0x6694
tick_engine_disabled(int32_t & d2)1167 void OFerrari::tick_engine_disabled(int32_t &d2)
1168 {
1169     torque_index = 0;
1170 
1171     // Crash taking place - do counter and set game engine when expired
1172     if (ocrash.coll_count1)
1173     {
1174         olevelobjs.spray_counter = 0;
1175         if (--oinitengine.ingame_counter != 0)
1176             return;
1177     }
1178     else if (outrun.game_state != GS_ATTRACT && outrun.game_state != GS_INGAME)
1179         return;
1180 
1181     // Switch back to in-game engine mode
1182     oinitengine.ingame_engine = true;
1183 
1184     torque = 0x1000;
1185 
1186     // Use top word of revs for lookup
1187     int16_t lookup = (revs >> 16);
1188     if (lookup > 0xFF) lookup = 0xFF;
1189 
1190     torque_index = (0x30 - rev_inc_lookup[lookup]) >> 2;
1191 
1192     int16_t acc = accel_value - 0x10;
1193     if (acc < 0) acc = 0;
1194     acc_post_stop = acc;
1195     revs_post_stop = lookup;
1196 
1197     // Clear bottom word of d2 and swap
1198     d2 = d2 << 16;
1199 
1200     //lookup -= 0x10;
1201     //if (lookup < 0) lookup = 0;
1202     rev_stop_flag = 14;
1203 }
1204 
1205 // - Convert the already processed accleration inputs into a meaningful value for both ACC and BRAKE
1206 // - Adjust acceleration based on number of wheels on road
1207 // - Adjust acceleration if car skidding or spinning
1208 //
1209 // Source: 0x6524
car_acc_brake()1210 void OFerrari::car_acc_brake()
1211 {
1212     // ------------------------------------------------------------------------
1213     // Acceleration
1214     // ------------------------------------------------------------------------
1215     int16_t acc1 = oinputs.acc_adjust;
1216     int16_t acc2 = oinputs.acc_adjust;
1217 
1218     acc1 += acc_adjust1 + acc_adjust2 + acc_adjust3;
1219     acc1 >>= 2;
1220     acc_adjust3 = acc_adjust2;
1221     acc_adjust2 = acc_adjust1;
1222     acc_adjust1 = acc2;
1223 
1224     if (!oinitengine.ingame_engine)
1225     {
1226         acc2 -= accel_value_bak;
1227         if (acc2 < 0) acc2 = -acc2;
1228         if (acc2 < 8)
1229             acc1 = accel_value_bak;
1230     }
1231     // test_skid_spin:
1232 
1233     // Clear acceleration on skid
1234     if (ocrash.spin_control1 > 0 || ocrash.skid_counter)
1235         acc1 = 0;
1236 
1237     // Adjust speed when offroad
1238     else if (wheel_state != WHEELS_ON)
1239     {
1240         if (!config.engine.offroad)
1241         {
1242             if (gear_value)
1243                 acc1 = (acc1 * 3) / 10;
1244             else
1245                 acc1 = (acc1 * 6) / 10;
1246 
1247             // If only one wheel off road, increase acceleration by a bit more than if both wheels off-road
1248             if (wheel_state != WHEELS_OFF)
1249                 acc1 = (acc1 << 1) + (acc1 >> 1);
1250         }
1251     }
1252 
1253     // finalise_acc_value:
1254     accel_value = acc1;
1255     accel_value_bak = acc1;
1256 
1257     // ------------------------------------------------------------------------
1258     // Brake
1259     // ------------------------------------------------------------------------
1260     int16_t brake1 = oinputs.brake_adjust;
1261     int16_t brake2 = oinputs.brake_adjust;
1262     brake1 += brake_adjust1 + brake_adjust2 + brake_adjust3;
1263     brake1 >>= 2;
1264     brake_adjust3 = brake_adjust2;
1265     brake_adjust2 = brake_adjust1;
1266     brake_adjust1 = brake2;
1267     brake_value = brake1;
1268 
1269     // ------------------------------------------------------------------------
1270     // Gears
1271     // ------------------------------------------------------------------------
1272     gear_bak = gear_value;
1273     gear_value = oinputs.gear;
1274 }
1275 
1276 // Do Gear Changes & Torque Index Settings
1277 //
1278 // Source: 0x6948
do_gear_torque(int16_t & d1)1279 void OFerrari::do_gear_torque(int16_t &d1)
1280 {
1281     if (oinitengine.ingame_engine)
1282     {
1283         d1 = torque_index;
1284         if (gear_value)
1285             do_gear_high(d1);
1286         else
1287             do_gear_low(d1);
1288     }
1289     torque_index = (uint8_t) d1;
1290     // Backup gear value for next iteration (so we can tell when gear has recently changed)
1291     gear_bak = gear_value;
1292 }
1293 
do_gear_low(int16_t & d1)1294 void OFerrari::do_gear_low(int16_t &d1)
1295 {
1296     // Recent Shift from high to low
1297     if (gear_bak)
1298     {
1299         gear_value = false;
1300         gear_counter = 4;
1301         return;
1302     }
1303 
1304     // Low Gear - Show Smoke When Accelerating From Standstill
1305     if (oinitengine.car_increment >> 16 < 0x50 && accel_value - 0xE0 >= 0)
1306         gfx_smoke++;
1307 
1308     // Adjust Torque Index
1309     if (d1 == 0x10) return;
1310     else if (d1 < 0x10)
1311     {
1312         d1++;
1313         return;
1314     }
1315     d1 -= 4;
1316     if (d1 < 0x10)
1317         d1 = 0x10;
1318 }
1319 
do_gear_high(int16_t & d1)1320 void OFerrari::do_gear_high(int16_t &d1)
1321 {
1322     // Change from Low Gear to High Gear
1323     if (!gear_bak)
1324     {
1325         gear_value = true;
1326         gear_counter = 4;
1327         return;
1328     }
1329 
1330     // Increment torque until it reaches 0x1F
1331     if (d1 == 0x1f) return;
1332     d1++;
1333 }
1334 
1335 // Source: 0x6752
tick_gear_change(int16_t rem)1336 int32_t OFerrari::tick_gear_change(int16_t rem)
1337 {
1338     gear_counter--;
1339     rev_adjust = rev_adjust - (rev_adjust >> 4);
1340 
1341     // Setup smoke when gear counter hits zero
1342     if (gear_counter == 0)
1343     {
1344         rem -= 0xE0;
1345         if (rem < 0)  return rev_adjust;
1346         int16_t acc = accel_value - 0xE0;
1347         if (acc < 0)  return rev_adjust;
1348         gear_smoke = acc;
1349     }
1350 
1351     return rev_adjust;
1352 }
1353 
1354 // Set value to increment speed by, when revs lower than acceleration
1355 //
1356 // Inputs:
1357 //
1358 // d1 = Torque Value [new_torque]
1359 // d2 = Proposed new increment value [new_rev]
1360 //
1361 // Outputs: d4 [Rev Adjustment]
1362 //
1363 // Source: 679C
1364 
get_speed_inc_value(uint16_t new_torque,uint32_t new_rev)1365 int32_t OFerrari::get_speed_inc_value(uint16_t new_torque, uint32_t new_rev)
1366 {
1367     // Use Top Word Of Revs For Table Lookup. Cap At 0xFF Max
1368     uint16_t lookup = (new_rev >> 16);
1369     if (lookup > 0xFF) lookup = 0xFF;
1370 
1371     uint32_t rev_adjust = rev_inc_lookup[lookup]; // d4
1372 
1373     // Double adjustment if car moving slowly
1374     if (oinitengine.car_increment >> 16 <= 0x14)
1375         rev_adjust <<= 1;
1376 
1377     rev_adjust = ((new_torque * new_torque) >> 12) * rev_adjust;
1378 
1379     if (!oinitengine.ingame_engine) return rev_adjust;
1380     return rev_adjust << rev_shift;
1381 }
1382 
1383 // Set value to decrement speed by, when revs higher than acceleration
1384 //
1385 // Inputs:
1386 //
1387 // d1 = Torque Value [new_torque]
1388 //
1389 // Outputs: d4 [Rev Adjustment]
1390 //
1391 // Source: 67E4
get_speed_dec_value(uint16_t new_torque)1392 int32_t OFerrari::get_speed_dec_value(uint16_t new_torque)
1393 {
1394     int32_t new_rev = -((0x440 * new_torque) >> 4);
1395     if (wheel_state == WHEELS_ON) return new_rev;
1396     return new_rev << 2;
1397 }
1398 
1399 // - Reads Brake Value
1400 // - Translates Into A Value To Subtract From Car Speed
1401 // - Also handles setting smoke on wheels during brake/skid
1402 // Source: 0x6A10
set_brake_subtract()1403 void OFerrari::set_brake_subtract()
1404 {
1405     int32_t d6 = 0;
1406     const int32_t DEC = -0x8800; // Base value to subtract from acceleration burst
1407 
1408     // Not skidding or spinning
1409     if (ocrash.skid_counter == 0 && ocrash.spin_control1 == 0)
1410     {
1411         if (brake_value < OInputs::BRAKE_THRESHOLD1)
1412         {
1413             brake_subtract = d6;
1414             return;
1415         }
1416         else if (brake_value < OInputs::BRAKE_THRESHOLD2)
1417         {
1418             brake_subtract = d6 + DEC;
1419             return;
1420         }
1421         else if (brake_value < OInputs::BRAKE_THRESHOLD3)
1422         {
1423             brake_subtract = d6 + (DEC * 3);
1424             return;
1425         }
1426         else if (brake_value < OInputs::BRAKE_THRESHOLD4)
1427         {
1428             brake_subtract = d6 + (DEC * 5);
1429             return;
1430         }
1431     }
1432     // check_smoke
1433     if (oinitengine.car_increment >> 16 > 0x28)
1434         gfx_smoke++;
1435 
1436     brake_subtract = d6 + (DEC * 9);
1437 }
1438 
1439 
1440 // - Finalise rev value, taking adjustments into account
1441 //
1442 // Inputs:
1443 //
1444 // d2 = Current Revs
1445 // d4 = Rev Adjustment
1446 //
1447 // Outputs:
1448 //
1449 // d2 = New rev value
1450 //
1451 // Source: 0x67FC
1452 
finalise_revs(int32_t & d2,int32_t rev_adjust_new)1453 void OFerrari::finalise_revs(int32_t &d2, int32_t rev_adjust_new)
1454 {
1455     rev_adjust_new += brake_subtract;
1456     if (rev_adjust_new < -0x44000) rev_adjust_new = -0x44000;
1457     d2 += rev_adjust_new;
1458     rev_adjust = rev_adjust_new;
1459 
1460     if (d2 > 0x13C0000) d2 = 0x13C0000;
1461     else if (d2 < 0) d2 = 0;
1462 }
1463 
1464 // Convert revs to final speed value
1465 // Set correct pitch for sound fx
1466 //
1467 // Note - main problem seems to be that the revs fed to this routine are wrong in the first place on restart
1468 // rev_stop_flag, revs_post_stop, accel_value and acc_post_stop seem ok?
1469 //
1470 // Source: 0x6838
convert_revs_speed(int32_t new_torque,int32_t & d2)1471 void OFerrari::convert_revs_speed(int32_t new_torque, int32_t &d2)
1472 {
1473     revs = d2;
1474     int32_t d3 = d2;
1475     if (d3 < 0x1F0000) d3 = 0x1F0000;
1476 
1477     int16_t revs_top = d3 >> 16;
1478 
1479     // Check whether we're switching back to ingame engine (after disabling user control of car)
1480     if (rev_stop_flag)
1481     {
1482         if (revs_top >= revs_post_stop)
1483         {
1484             rev_stop_flag = 0;
1485         }
1486         else
1487         {
1488             if (accel_value < acc_post_stop)
1489                 revs_post_stop -= rev_stop_flag;
1490 
1491             // cont1:
1492             int16_t d5 = revs_post_stop >> 1;
1493             int16_t d4 = rev_stop_flag;
1494             if (revs_top >= d5)
1495             {
1496                 d5 >>= 1;
1497                 d4 >>= 1;
1498                 if (revs_top >= d5)
1499                     d4 >>= 1;
1500             }
1501             // 689c
1502             revs_post_stop -= d4;
1503             if (revs_post_stop < 0x1F) revs_post_stop = 0x1F;
1504             revs_top = revs_post_stop;
1505         }
1506     }
1507 
1508     // setup_pitch_fx:
1509     rev_pitch1 = (revs_top * 0x1A90) >> 8;
1510 
1511     // Setup New Car Increment Speed
1512     d2 = ((d2 >> 16) * 0x1A90) >> 8;
1513     d2 = (d2 << 16) >> 4;
1514 
1515     /*if (!new_torque)
1516     {
1517         std::cout << "convert_revs_speed error!" << std::endl;
1518     }*/
1519 
1520     const int max_speed = !config.engine.turbo ? MAX_SPEED : (int) (MAX_SPEED * 1.2f);
1521     d2 = (d2 / new_torque) * 0x480;
1522     //std::cout << std::hex << "d2: " << (int)d2 << " new_torque: " << new_torque << " torque index: " << std::hex << (int) torque_index << std::endl;
1523     if (d2 < 0) d2 = 0;
1524     else if (d2 > max_speed) d2 = max_speed;
1525 }
1526 
1527 // Update road_pos, taking road_curve into account
1528 //
1529 // Source: 0x6ABA
update_road_pos()1530 void OFerrari::update_road_pos()
1531 {
1532     uint32_t car_inc = CAR_BASE_INC;
1533 
1534     // Bendy Roads
1535     if (oinitengine.road_type > OInitEngine::ROAD_STRAIGHT)
1536     {
1537         int32_t x = oinitengine.car_x_pos;
1538 
1539         if (oinitengine.road_type == OInitEngine::ROAD_RIGHT)
1540             x = -x;
1541 
1542         x = (x / 0x28) + oinitengine.road_curve;
1543 
1544         car_inc = (car_inc * x) / oinitengine.road_curve;
1545     }
1546 
1547     car_inc *= oinitengine.car_increment >> 16;
1548     oroad.road_pos_change = car_inc;
1549     oroad.road_pos += car_inc;
1550 }
1551 
1552 // Decrement Smoke Counter
1553 // Source: 0x666A
tick_smoke()1554 int32_t OFerrari::tick_smoke()
1555 {
1556     gear_smoke--;
1557     int32_t r = rev_adjust - (rev_adjust >> 4);
1558     if (gear_smoke >= 8) gfx_smoke++; // Trigger smoke
1559     return r;
1560 }
1561 
1562 // Calculate car score and sound. Strange combination, but there you go!
1563 //
1564 // Source: 0xBD78
do_sound_score_slip()1565 void OFerrari::do_sound_score_slip()
1566 {
1567     // ------------------------------------------------------------------------
1568     // ENGINE PITCH SOUND
1569     // ------------------------------------------------------------------------
1570     uint16_t engine_pitch = 0;
1571 
1572     // Do Engine Rev
1573     if (outrun.game_state >= GS_START1 && outrun.game_state <= GS_INGAME)
1574     {
1575         engine_pitch = rev_pitch2 + (rev_pitch2 >> 1);
1576     }
1577 
1578     osoundint.engine_data[sound::ENGINE_PITCH_H] = engine_pitch >> 8;
1579     osoundint.engine_data[sound::ENGINE_PITCH_L] = engine_pitch & 0xFF;
1580 
1581     // Curved Road
1582     if (oinitengine.road_type != OInitEngine::ROAD_STRAIGHT)
1583     {
1584         int16_t steering = oinputs.steering_adjust;
1585         if (steering < 0) steering = -steering;
1586 
1587         // Hard turn
1588         if (steering >= 0x70)
1589         {
1590             // Move Left
1591             if (oinitengine.car_x_pos > oinitengine.car_x_old)
1592                 cornering = (oinitengine.road_type == OInitEngine::ROAD_LEFT) ? 0 : -1;
1593             // Straight
1594             else if (oinitengine.car_x_pos == oinitengine.car_x_old)
1595                 cornering = 0;
1596             // Move Right
1597             else
1598                 cornering = (oinitengine.road_type == OInitEngine::ROAD_RIGHT) ? 0 : -1;
1599         }
1600         else
1601             cornering = 0;
1602     }
1603     else
1604         cornering = 0;
1605 
1606     // update_score:
1607     car_x_diff = oinitengine.car_x_pos - oinitengine.car_x_old;
1608     oinitengine.car_x_old = oinitengine.car_x_pos;
1609 
1610     if (outrun.game_state == GS_ATTRACT)
1611         return;
1612 
1613     // Wheels onroad - Convert Speed To Score
1614     if (wheel_state == WHEELS_ON)
1615     {
1616         ostats.convert_speed_score(oinitengine.car_increment >> 16);
1617     }
1618 
1619     // 0xBE6E
1620     if (!sprite_slip_copy)
1621     {
1622         if (ocrash.skid_counter)
1623         {
1624             is_slipping = -1;
1625             osoundint.queue_sound(sound::INIT_SLIP);
1626         }
1627     }
1628     // 0xBE94
1629     else
1630     {
1631         if (!ocrash.skid_counter)
1632         {
1633             is_slipping = 0;
1634             osoundint.queue_sound(sound::STOP_SLIP);
1635         }
1636     }
1637     // 0xBEAC
1638     sprite_slip_copy = ocrash.skid_counter;
1639 
1640     // ------------------------------------------------------------------------
1641     // Switch to cornering. Play Slip Sound. Init Smoke
1642     // ------------------------------------------------------------------------
1643     if (!ocrash.skid_counter)
1644     {
1645         // 0xBEBE: Initalize Slip
1646         if (!cornering_old)
1647         {
1648             if (cornering)
1649             {
1650                 is_slipping = -1;
1651                 osoundint.queue_sound(sound::INIT_SLIP);
1652             }
1653         }
1654         // 0xBEE4: Stop Cornering
1655         else
1656         {
1657             if (!cornering)
1658             {
1659                 is_slipping = 0;
1660                 osoundint.queue_sound(sound::STOP_SLIP);
1661             }
1662         }
1663     }
1664     // check_wheels:
1665     cornering_old = cornering;
1666 
1667     if (sprite_wheel_state)
1668     {
1669         // If previous wheels on-road & current wheels off-road - play safety zone sound
1670         if (!wheel_state)
1671             osoundint.queue_sound(sound::STOP_SAFETYZONE);
1672     }
1673     // Stop Safety Sound
1674     else
1675     {
1676         if (wheel_state)
1677             osoundint.queue_sound(sound::INIT_SAFETYZONE);
1678     }
1679     sprite_wheel_state = wheel_state;
1680 }
1681 
1682 // Shake Ferrari by altering XY Position when wheels are off-road
1683 //
1684 // Source: 0x9FEE
shake()1685 void OFerrari::shake()
1686 {
1687     if (outrun.game_state != GS_INGAME && outrun.game_state != GS_ATTRACT) return;
1688 
1689     if (wheel_traction == TRACTION_ON) return; // Return if both wheels have traction
1690 
1691     int8_t traction = wheel_traction - 1;
1692     int16_t rnd = outils::random();
1693     spr_ferrari->counter++;
1694 
1695     uint16_t car_inc = oinitengine.car_increment >> 16; // [d5]
1696 
1697     if (car_inc <= 0xA) return; // Do not shake car at low speeds
1698 
1699     if (car_inc <= (0x3C >> traction))
1700     {
1701         rnd &= 3;
1702         if (rnd != 0) return;
1703     }
1704     else if (car_inc <= (0x78 >> traction))
1705     {
1706         rnd &= 1;
1707         if (rnd != 0) return;
1708     }
1709 
1710     if (rnd < 0) spr_ferrari->y--;
1711     else spr_ferrari->y++;
1712 
1713     rnd &= 1;
1714 
1715     if (!(spr_ferrari->counter & BIT_1))
1716         rnd = -rnd;
1717 
1718     spr_ferrari->x += rnd; // Increment Ferrari X by 1 or -1
1719 }
1720 
1721 // Perform Skid (After Car Has Collided With Another Vehicle)
1722 //
1723 // Source: 0xBFB4
do_skid()1724 void OFerrari::do_skid()
1725 {
1726     if (!ocrash.skid_counter) return;
1727 
1728     if (ocrash.skid_counter > 0)
1729     {
1730         ocrash.skid_counter--;
1731         oinitengine.car_x_pos += OCrash::SKID_X_ADJ;
1732     }
1733     else
1734     {
1735         ocrash.skid_counter++;
1736         oinitengine.car_x_pos -= OCrash::SKID_X_ADJ;
1737     }
1738 }
1739 
draw_sprite(oentry * sprite)1740 void OFerrari::draw_sprite(oentry* sprite)
1741 {
1742     if (oroad.get_view_mode() != ORoad::VIEW_INCAR)
1743     {
1744         osprites.map_palette(sprite);
1745         osprites.do_spr_order_shadows(sprite);
1746     }
1747 }
1748 
1749 // Rev Lookup Table. 255 Values.
1750 // Used to provide rev adjustment. Note that values tail off at higher speeds.
1751 const uint8_t OFerrari::rev_inc_lookup[] =
1752 {
1753     0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17,
1754     0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
1755     0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
1756     0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
1757     0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
1758     0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
1759     0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B,
1760     0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D,
1761     0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1F, 0x1F, 0x1F, 0x1F, 0x20, 0x20, 0x20, 0x20,
1762     0x21, 0x21, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28,
1763     0x29, 0x29, 0x2A, 0x2A, 0x2B, 0x2B, 0x2B, 0x2C, 0x2C, 0x2C, 0x2D, 0x2D, 0x2D, 0x2E, 0x2E, 0x2E,
1764     0x2F, 0x2F, 0x2F, 0x2F, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x2F, 0x2F, 0x2F, 0x2F,
1765     0x2E, 0x2E, 0x2E, 0x2D, 0x2D, 0x2D, 0x2C, 0x2C, 0x2B, 0x2B, 0x2A, 0x2A, 0x29, 0x29, 0x28, 0x28,
1766     0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18,
1767     0x17, 0x15, 0x13, 0x11, 0x0F, 0x0D, 0x0B, 0x0A, 0x09, 0x09, 0x08, 0x08, 0x07, 0x07, 0x06, 0x06,
1768     0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01
1769 };
1770 
1771 
1772 uint16_t OFerrari::torque_lookup[] =
1773 {
1774     0x2600, // Offset 0x00 - Start line
1775     0x243C, // ..
1776     0x2278, // values only used when pulling away from start line
1777     0x20B4,
1778     0x1EF0,
1779     0x1D2C,
1780     0x1B68,
1781     0x19A4,
1782     0x17E0,
1783     0x161C,
1784     0x1458,
1785     0x1294,
1786     0x10D0,
1787     0xF0C,
1788     0xD48,
1789     0xB84,
1790     0x9BB, // Offset 0x10 - Low Gear
1791     0x983, // ..
1792     0x94B, // .. in between these values
1793     0x913, // .. is the lookup as we shift between
1794     0x8DB, // .. the two gears
1795     0x8A3, // ..
1796     0x86B,
1797     0x833,
1798     0x7FB,
1799     0x7C2,
1800     0x789,
1801     0x750,
1802     0x717,
1803     0x6DE,
1804     0x6A5,
1805     0x66C, // Offset 0x1F - High Gear
1806 };