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 };