1 /***************************************************************************
2     Animation Sequences.
3 
4     Used in three areas of the game:
5     - The sequence at the start with the Ferrari driving in from the side
6     - Flag Waving Man
7     - 5 x End Sequences
8 
9     See "oanimsprite.hpp" for the specific format used by animated sprites.
10     It is essentially a deviation from the normal sprites in the game.
11 
12     Copyright Chris White.
13     See license.txt for more details.
14 ***************************************************************************/
15 
16 #include "frontend/config.hpp"
17 
18 #include "engine/obonus.hpp"
19 #include "engine/oferrari.hpp"
20 #include "engine/oinputs.hpp"
21 #include "engine/oanimseq.hpp"
22 
23 // ----------------------------------------------------------------------------
24 // Animation Data Format.
25 // Animation blocks are stored in groups of 8 bytes, and formatted as follows:
26 //
27 // +00 [Byte] Sprite Colour Palette
28 // +01 [Byte] Bit 7: Make X Position Negative
29 //            Bits 4-6: Sprite To Sprite Priority
30 //            Bits 0-3: Top Bits Of Sprite Data Address
31 // +02 [Word] Sprite Data Address
32 // +04 [Byte] Sprite X Position
33 // +05 [Byte] Sprite Y Position
34 // +06 [Byte] Sprite To Road Priority
35 // +07 [Byte] Bit 7: Set To Load Next Block Of Sprite Animation Data To 0x1E
36 //            Bit 6: Set For H-Flip
37 //            Bit 4:
38 //            Bits 0-3: Animation Frame Delay
39 //                     (Before Incrementing To Next Block Of 8 Bytes)
40 // ----------------------------------------------------------------------------
41 
42 OAnimSeq oanimseq;
43 
OAnimSeq(void)44 OAnimSeq::OAnimSeq(void)
45 {
46 }
47 
48 
~OAnimSeq(void)49 OAnimSeq::~OAnimSeq(void)
50 {
51 }
52 
init(oentry * jump_table)53 void OAnimSeq::init(oentry* jump_table)
54 {
55     // --------------------------------------------------------------------------------------------
56     // Flag Animation Setup
57     // --------------------------------------------------------------------------------------------
58     oentry* sprite_flag = &jump_table[OSprites::SPRITE_FLAG];
59     anim_flag.sprite = sprite_flag;
60     anim_flag.anim_state = 0;
61 
62     // Jump table initalisations
63     sprite_flag->shadow = 7;
64     sprite_flag->draw_props = oentry::BOTTOM;
65 
66     // Routine initalisations
67     sprite_flag->control |= OSprites::ENABLE;
68     sprite_flag->z = 400 << 16;
69 
70     // --------------------------------------------------------------------------------------------
71     // Ferrari & Passenger Animation Setup For Intro
72     // --------------------------------------------------------------------------------------------
73     oentry* sprite_ferrari = &jump_table[OSprites::SPRITE_FERRARI];
74     anim_ferrari.init(sprite_ferrari);
75     anim_ferrari.anim_addr_curr = outrun.adr.anim_ferrari_curr;
76     anim_ferrari.anim_addr_next = outrun.adr.anim_ferrari_next;
77     sprite_ferrari->control |= OSprites::ENABLE;
78     sprite_ferrari->draw_props = oentry::BOTTOM;
79 
80     oentry* sprite_pass1 = &jump_table[OSprites::SPRITE_PASS1];
81     anim_pass1.init(sprite_pass1);
82     anim_pass1.anim_addr_curr = outrun.adr.anim_pass1_curr;
83     anim_pass1.anim_addr_next = outrun.adr.anim_pass1_next;
84     sprite_pass1->draw_props = oentry::BOTTOM;
85 
86     oentry* sprite_pass2 = &jump_table[OSprites::SPRITE_PASS2];
87     anim_pass2.init(sprite_pass2);
88     anim_pass2.anim_addr_curr = outrun.adr.anim_pass2_curr;
89     anim_pass2.anim_addr_next = outrun.adr.anim_pass2_next;
90     sprite_pass2->draw_props = oentry::BOTTOM;
91 
92     // --------------------------------------------------------------------------------------------
93     // End Sequence Animation
94     // --------------------------------------------------------------------------------------------
95     end_seq_state = 0; // init
96     seq_pos = 0;
97     ferrari_stopped = false;
98 
99     anim_obj1.init(&jump_table[OSprites::SPRITE_CRASH]);
100     anim_obj2.init(&jump_table[OSprites::SPRITE_CRASH_SHADOW]);
101     anim_obj3.init(&jump_table[OSprites::SPRITE_SHADOW]);
102     anim_obj4.init(&jump_table[OSprites::SPRITE_CRASH_PASS1]);
103     anim_obj5.init(&jump_table[OSprites::SPRITE_CRASH_PASS1_S]);
104     anim_obj6.init(&jump_table[OSprites::SPRITE_CRASH_PASS2]);
105     anim_obj7.init(&jump_table[OSprites::SPRITE_CRASH_PASS2_S]);
106     anim_obj8.init(&jump_table[OSprites::SPRITE_FLAG]);
107 }
108 
109 // ------------------------------------------------------------------------------------------------
110 // START ANIMATION SEQUENCES (FLAG, FERRARI DRIVING IN)
111 // ------------------------------------------------------------------------------------------------
112 
flag_seq()113 void OAnimSeq::flag_seq()
114 {
115     if (!(anim_flag.sprite->control & OSprites::ENABLE))
116         return;
117 
118     if (outrun.tick_frame)
119     {
120         if (outrun.game_state < GS_START1 || outrun.game_state > GS_GAMEOVER)
121         {
122             anim_flag.sprite->control &= ~OSprites::ENABLE;
123             return;
124         }
125 
126         // Init Flag Sequence
127         if (outrun.game_state < GS_INGAME && anim_flag.anim_state != outrun.game_state)
128         {
129             anim_flag.anim_state = outrun.game_state;
130 
131             // Index of animation sequences
132             uint32_t index = outrun.adr.anim_seq_flag + ((outrun.game_state - 9) << 3);
133 
134             anim_flag.anim_addr_curr = roms.rom0p->read32(&index);
135             anim_flag.anim_addr_next = roms.rom0p->read32(&index);
136 
137             anim_flag.frame_delay = roms.rom0p->read8(7 + anim_flag.anim_addr_curr) & 0x3F;
138             anim_flag.anim_frame  = 0;
139         }
140 
141         // Wave Flag
142         if (outrun.game_state <= GS_INGAME)
143         {
144             uint32_t index = anim_flag.anim_addr_curr + (anim_flag.anim_frame << 3);
145 
146             anim_flag.sprite->addr    = roms.rom0p->read32(index) & 0xFFFFF;
147             anim_flag.sprite->pal_src = roms.rom0p->read8(index);
148 
149 	        uint32_t addr = SPRITE_ZOOM_LOOKUP + (((anim_flag.sprite->z >> 16) << 2) | osprites.sprite_scroll_speed);
150 	        uint32_t value = roms.rom0p->read32(addr);
151 	        anim_flag.sprite->z += value;
152             uint16_t z16 = anim_flag.sprite->z >> 16;
153 
154             if (z16 >= 0x200)
155 	        {
156                 anim_flag.sprite->control &= ~OSprites::ENABLE;
157 		        return;
158 	        }
159 	        anim_flag.sprite->priority = z16;
160 	        anim_flag.sprite->zoom     = z16 >> 2;
161 
162             // Set X Position
163             int16_t sprite_x = (int8_t) roms.rom0p->read8(4 + index);
164             sprite_x -= oroad.road0_h[z16];
165             int32_t final_x = (sprite_x * z16) >> 9;
166 
167             if (roms.rom0p->read8(1 + index) & BIT_7)
168                 final_x = -final_x;
169 
170             anim_flag.sprite->x = final_x;
171 
172             // Set Y Position
173             int16_t sprite_y      = (int8_t) roms.rom0p->read8(5 + index);
174             int16_t final_y       = (sprite_y * z16) >> 9;
175             anim_flag.sprite->y   = oroad.get_road_y(z16) - final_y;
176 
177             // Set H-Flip
178             if (roms.rom0p->read8(7 + index) & BIT_6)
179                 anim_flag.sprite->control |= OSprites::HFLIP;
180             else
181                 anim_flag.sprite->control &= ~OSprites::HFLIP;
182 
183             // Ready for next frame
184             if (--anim_flag.frame_delay == 0)
185             {
186                 // Load Next Block Of Animation Data
187                 if (roms.rom0p->read8(7 + index) & BIT_7)
188                 {
189                     anim_flag.anim_addr_curr = anim_flag.anim_addr_next;
190                     anim_flag.frame_delay    = roms.rom0p->read8(7 + anim_flag.anim_addr_curr) & 0x3F;
191                     anim_flag.anim_frame     = 0;
192                 }
193                 // Last Block
194                 else
195                 {
196                     anim_flag.frame_delay = roms.rom0p->read8(0x0F + index) & 0x3F;
197                     anim_flag.anim_frame++;
198                 }
199             }
200         }
201     }
202 
203     // Order sprites
204     osprites.map_palette(anim_flag.sprite);
205     osprites.do_spr_order_shadows(anim_flag.sprite);
206 }
207 
208 // Setup Ferrari Animation Sequence
209 //
210 // Source: 0x6036
ferrari_seq()211 void OAnimSeq::ferrari_seq()
212 {
213     if (!(anim_ferrari.sprite->control & OSprites::ENABLE))
214         return;
215 
216     if (outrun.game_state == GS_MUSIC) return;
217 
218     anim_pass1.sprite->control |= OSprites::ENABLE;
219     anim_pass2.sprite->control |= OSprites::ENABLE;
220 
221     if (outrun.game_state <= GS_LOGO)
222     {
223         oferrari.init_ingame();
224         return;
225     }
226 
227     anim_ferrari.frame_delay = roms.rom0p->read8(7 + anim_ferrari.anim_addr_curr) & 0x3F;
228     anim_pass1.frame_delay   = roms.rom0p->read8(7 + anim_pass1.anim_addr_curr) & 0x3F;
229     anim_pass2.frame_delay   = roms.rom0p->read8(7 + anim_pass2.anim_addr_curr) & 0x3F;
230 
231     oferrari.car_state = OFerrari::CAR_NORMAL;
232     oferrari.state     = OFerrari::FERRARI_SEQ2;
233 
234     anim_seq_intro(&anim_ferrari);
235 }
236 
237 // Process Animations for Ferrari and Passengers on intro
238 //
239 // Source: 60AE
anim_seq_intro(oanimsprite * anim)240 void OAnimSeq::anim_seq_intro(oanimsprite* anim)
241 {
242     if (outrun.game_state <= GS_LOGO)
243     {
244         oferrari.init_ingame();
245         return;
246     }
247 
248     if (outrun.tick_frame)
249     {
250         if (anim->anim_frame >= 1)
251             oferrari.car_state = OFerrari::CAR_ANIM_SEQ;
252 
253         uint32_t index              = anim->anim_addr_curr + (anim->anim_frame << 3);
254 
255         anim->sprite->addr          = roms.rom0p->read32(index) & 0xFFFFF;
256         anim->sprite->pal_src       = anim == &anim_ferrari ? oferrari.ferrari_pal : roms.rom0p->read8(index);
257         anim->sprite->zoom          = 0x7F;
258         anim->sprite->road_priority = 0x1FE;
259         anim->sprite->priority      = 0x1FE - ((roms.rom0p->read16(index) & 0x70) >> 4);
260 
261         // Set X
262         int16_t sprite_x = (int8_t) roms.rom0p->read8(4 + index);
263         int32_t final_x = (sprite_x * anim->sprite->priority) >> 9;
264         if (roms.rom0p->read8(1 + index) & BIT_7)
265             final_x = -final_x;
266         anim->sprite->x = final_x;
267 
268         // Set Y
269         anim->sprite->y = 221 - ((int8_t) roms.rom0p->read8(5 + index));
270 
271         // Set H-Flip
272         if (roms.rom0p->read8(7 + index) & BIT_6)
273             anim->sprite->control |= OSprites::HFLIP;
274         else
275             anim->sprite->control &= ~OSprites::HFLIP;
276 
277         // Ready for next frame
278         if (--anim->frame_delay == 0)
279         {
280             // Load Next Block Of Animation Data
281             if (roms.rom0p->read8(7 + index) & BIT_7)
282             {
283                 // Yeah the usual OutRun code hacks to do really odd stuff!
284                 // In this case, to exit the routine and setup the Ferrari on the last entry for passenger 2
285                 if (anim == &anim_pass2)
286                 {
287                     if (oroad.get_view_mode() != ORoad::VIEW_INCAR)
288                     {
289                         osprites.map_palette(anim->sprite);
290                         osprites.do_spr_order_shadows(anim->sprite);
291                     }
292                     oferrari.init_ingame();
293                     return;
294                 }
295 
296                 anim->anim_addr_curr = anim->anim_addr_next;
297                 anim->frame_delay    = roms.rom0p->read8(7 + anim->anim_addr_curr) & 0x3F;
298                 anim->anim_frame     = 0;
299             }
300             // Last Block
301             else
302             {
303                 anim->frame_delay = roms.rom0p->read8(0x0F + index) & 0x3F;
304                 anim->anim_frame++;
305             }
306         }
307     }
308 
309     // Order sprites
310     if (oroad.get_view_mode() != ORoad::VIEW_INCAR)
311     {
312         osprites.map_palette(anim->sprite);
313         osprites.do_spr_order_shadows(anim->sprite);
314     }
315 }
316 
317 // ------------------------------------------------------------------------------------------------
318 // END ANIMATION SEQUENCES
319 // ------------------------------------------------------------------------------------------------
320 
321 // Initialize end sequence animations on game complete
322 // Source: 0x9978
init_end_seq()323 void OAnimSeq::init_end_seq()
324 {
325     // Process animation sprites instead of normal routine
326     oferrari.state = OFerrari::FERRARI_END_SEQ;
327 
328     // Setup Ferrari Sprite
329     anim_ferrari.sprite->control |= OSprites::ENABLE;
330     anim_ferrari.sprite->id = 0;
331     anim_ferrari.sprite->draw_props = oentry::BOTTOM;
332     anim_ferrari.anim_frame = 0;
333     anim_ferrari.frame_delay = 0;
334 
335     seq_pos = 0;
336 
337     // Disable Passenger Sprites. These are replaced with new versions by the animation sequence.
338     oferrari.spr_pass1->control &= ~OSprites::ENABLE;
339     oferrari.spr_pass2->control &= ~OSprites::ENABLE;
340 
341     obonus.bonus_control += 4;
342 }
343 
tick_end_seq()344 void OAnimSeq::tick_end_seq()
345 {
346     switch (end_seq_state)
347     {
348         case 0: // init
349             if (outrun.tick_frame) init_end_sprites();
350             else return;
351 
352         case 1: // tick & blit
353             anim_seq_outro_ferrari();                           // Ferrari Sprite
354             anim_seq_outro(&anim_obj1, oferrari.ferrari_pal);   // Car Door Opening Animation
355             anim_seq_outro(&anim_obj2);                         // Interior of Ferrari
356             anim_seq_shadow(&anim_ferrari, &anim_obj3);         // Car Shadow
357                                                                 // Man Sprite
358             // Fix Wrong Palette Bug: Only occurs on 3 of the 5 possible end sequences (0 and 3 are ok)
359             anim_seq_outro(&anim_pass1, config.engine.fix_bugs ? 10 : -1);
360             anim_seq_shadow(&anim_pass1, &anim_obj4);           // Man Shadow
361             anim_seq_outro(&anim_pass2);                        // Female Sprite
362             anim_seq_shadow(&anim_pass2, &anim_obj5);           // Female Shadow
363             anim_seq_outro(&anim_obj6);                         // Man Presenting Trophy
364             if (end_seq == 4)
365                 anim_seq_outro(&anim_obj7);                     // Varies
366             else
367                 anim_seq_shadow(&anim_obj6, &anim_obj7);
368             anim_seq_outro(&anim_obj8);                         // Effects
369             break;
370     }
371 }
372 
373 // Initalize Sprites For End Sequence.
374 // Source: 0x588A
init_end_sprites()375 void OAnimSeq::init_end_sprites()
376 {
377     // Ferrari Object [0x5B12 entry point]
378     uint32_t addr = outrun.adr.anim_endseq_obj1 + (end_seq << 3);
379     anim_ferrari.anim_addr_curr = roms.rom0p->read32(&addr);
380     anim_ferrari.anim_addr_next = roms.rom0p->read32(&addr);
381     ferrari_stopped = false;
382 
383     // 0x58A4: Car Door Opening Animation [seq_sprite_entry]
384     anim_obj1.sprite->control |= OSprites::ENABLE;
385     anim_obj1.sprite->id = 1;
386     anim_obj1.sprite->shadow = 3;
387     anim_obj1.sprite->draw_props = oentry::BOTTOM;
388     anim_obj1.anim_frame = 0;
389     anim_obj1.frame_delay = 0;
390     anim_obj1.anim_props = 0;
391     addr = outrun.adr.anim_endseq_obj2 + (end_seq << 3);
392     anim_obj1.anim_addr_curr = roms.rom0p->read32(&addr);
393     anim_obj1.anim_addr_next = roms.rom0p->read32(&addr);
394 
395     // 0x58EC: Interior of Ferrari (Note this wobbles a little when passengers exit) [seq_sprite_entry]
396     anim_obj2.sprite->control |= OSprites::ENABLE;
397     anim_obj2.sprite->id = 2;
398     anim_obj2.sprite->draw_props = oentry::BOTTOM;
399     anim_obj2.anim_frame = 0;
400     anim_obj2.frame_delay = 0;
401     anim_obj2.anim_props = 0;
402     addr = outrun.adr.anim_endseq_obj3 + (end_seq << 3);
403     anim_obj2.anim_addr_curr = roms.rom0p->read32(&addr);
404     anim_obj2.anim_addr_next = roms.rom0p->read32(&addr);
405 
406     // 0x592A: Car Shadow [SeqSpriteShadow]
407     anim_obj3.sprite->control |= OSprites::ENABLE;
408     anim_obj3.sprite->id = 3;
409     anim_obj3.sprite->draw_props = oentry::BOTTOM;
410     anim_obj3.anim_frame = 0;
411     anim_obj3.frame_delay = 0;
412     anim_obj3.anim_props = 0;
413     anim_obj3.sprite->addr = outrun.adr.shadow_data;
414 
415     // 0x5960: Man Sprite [seq_sprite_entry]
416     anim_pass1.sprite->control |= OSprites::ENABLE;
417     anim_pass1.sprite->id = 4;
418     anim_pass1.sprite->draw_props = oentry::BOTTOM;
419     anim_pass1.anim_frame = 0;
420     anim_pass1.frame_delay = 0;
421     anim_pass1.anim_props = 0;
422     addr = outrun.adr.anim_endseq_obj4 + (end_seq << 3);
423     anim_pass1.anim_addr_curr = roms.rom0p->read32(&addr);
424     anim_pass1.anim_addr_next = roms.rom0p->read32(&addr);
425 
426     // 0x5998: Man Shadow [SeqSpriteShadow]
427     anim_obj4.sprite->control = OSprites::ENABLE;
428     anim_obj4.sprite->id = 5;
429     anim_obj4.sprite->shadow = 7;
430     anim_obj4.sprite->draw_props = oentry::BOTTOM;
431     anim_obj4.anim_frame = 0;
432     anim_obj4.frame_delay = 0;
433     anim_obj4.anim_props = 0;
434     anim_obj4.sprite->addr = outrun.adr.shadow_data;
435 
436     // 0x59BE: Female Sprite [seq_sprite_entry]
437     anim_pass2.sprite->control |= OSprites::ENABLE;
438     anim_pass2.sprite->id = 6;
439     anim_pass2.sprite->draw_props = oentry::BOTTOM;
440     anim_pass2.anim_frame = 0;
441     anim_pass2.frame_delay = 0;
442     anim_pass2.anim_props = 0;
443     addr = outrun.adr.anim_endseq_obj5 + (end_seq << 3);
444     anim_pass2.anim_addr_curr = roms.rom0p->read32(&addr);
445     anim_pass2.anim_addr_next = roms.rom0p->read32(&addr);
446 
447     // 0x59F6: Female Shadow [SeqSpriteShadow]
448     anim_obj5.sprite->control = OSprites::ENABLE;
449     anim_obj5.sprite->id = 7;
450     anim_obj5.sprite->shadow = 7;
451     anim_obj5.sprite->draw_props = oentry::BOTTOM;
452     anim_obj5.anim_frame = 0;
453     anim_obj5.frame_delay = 0;
454     anim_obj5.anim_props = 0;
455     anim_obj5.sprite->addr = outrun.adr.shadow_data;
456 
457     // 0x5A2C: Person Presenting Trophy [seq_sprite_entry]
458     anim_obj6.sprite->control |= OSprites::ENABLE;
459     anim_obj6.sprite->id = 8;
460     anim_obj6.sprite->draw_props = oentry::BOTTOM;
461     anim_obj6.anim_frame = 0;
462     anim_obj6.frame_delay = 0;
463     anim_obj6.anim_props = 0;
464     addr = outrun.adr.anim_endseq_obj6 + (end_seq << 3);
465     anim_obj6.anim_addr_curr = roms.rom0p->read32(&addr);
466     anim_obj6.anim_addr_next = roms.rom0p->read32(&addr);
467 
468     // Alternate Use Based On End Sequence
469     anim_obj7.sprite->control |= OSprites::ENABLE;
470     anim_obj7.sprite->id = 9;
471     anim_obj7.sprite->draw_props = oentry::BOTTOM;
472     anim_obj7.anim_frame = 0;
473     anim_obj7.frame_delay = 0;
474     anim_obj7.anim_props = 0;
475 
476     // [seq_sprite_entry]
477     if (end_seq == 4)
478     {
479         addr = outrun.adr.anim_endseq_objB + (end_seq << 3);
480         anim_obj7.anim_addr_curr = roms.rom0p->read32(&addr);
481         anim_obj7.anim_addr_next = roms.rom0p->read32(&addr);
482     }
483     // Trophy Shadow
484     else
485     {
486         anim_obj7.sprite->shadow = 7;
487         anim_obj7.sprite->addr = outrun.adr.shadow_data;
488     }
489 
490     // 0x5AD0: Enable After Effects (e.g. cloud of smoke for genie) [seq_sprite_entry]
491     anim_obj8.sprite->control |= OSprites::ENABLE;
492     anim_obj8.sprite->id = 10;
493     anim_obj8.sprite->draw_props = oentry::BOTTOM;
494     anim_obj8.anim_frame = 0;
495     anim_obj8.frame_delay = 0;
496     anim_obj8.anim_props = 0xFF00;
497     addr = outrun.adr.anim_endseq_obj7 + (end_seq << 3);
498     anim_obj8.anim_addr_curr = roms.rom0p->read32(&addr);
499     anim_obj8.anim_addr_next = roms.rom0p->read32(&addr);
500 
501     end_seq_state = 1;
502 }
503 
504 // Ferrari Outro Animation Sequence
505 // Source: 0x5B12
anim_seq_outro_ferrari()506 void OAnimSeq::anim_seq_outro_ferrari()
507 {
508     if (!ferrari_stopped)
509     {
510         // Car is moving. Turn Brake On.
511         if (oinitengine.car_increment >> 16)
512         {
513             oferrari.auto_brake  = true;
514             oinputs.brake_adjust = 0xFF;
515         }
516         else
517         {
518             osoundint.queue_sound(sound::VOICE_CONGRATS);
519             ferrari_stopped = true;
520         }
521     }
522     anim_seq_outro(&anim_ferrari, oferrari.ferrari_pal);
523 }
524 
525 // End Sequence: Setup Animated Sprites
526 // Source: 0x5B42
anim_seq_outro(oanimsprite * anim,int pal_override)527 void OAnimSeq::anim_seq_outro(oanimsprite* anim, int pal_override)
528 {
529     oinputs.steering_adjust = 0;
530 
531     // Return if no animation data to process
532     if (!read_anim_data(anim))
533         return;
534 
535     // Process Animation Data
536     uint32_t index = anim->anim_addr_curr + (anim->anim_frame << 3);
537 
538     anim->sprite->addr          = roms.rom0p->read32(index) & 0xFFFFF;
539     // Override palette to overcome bugs / recolour Ferrari
540     anim->sprite->pal_src       = pal_override != -1 ? pal_override : roms.rom0p->read8(index);
541     anim->sprite->zoom          = roms.rom0p->read8(6 + index) >> 1;
542     anim->sprite->road_priority = roms.rom0p->read8(6 + index) << 1;
543     anim->sprite->priority      = anim->sprite->road_priority - ((roms.rom0p->read16(index) & 0x70) >> 4); // (bits 4-6)
544     anim->sprite->x             = (roms.rom0p->read8(4 + index) * anim->sprite->priority) >> 9;
545 
546     if (roms.rom0p->read8(1 + index) & BIT_7)
547         anim->sprite->x = -anim->sprite->x;
548 
549     // set_sprite_xy: (similar to flag code again)
550 
551     // Set Y Position
552     int16_t sprite_y = (int8_t) roms.rom0p->read8(5 + index);
553     int16_t final_y  = (sprite_y * anim->sprite->priority) >> 9;
554     anim->sprite->y  = oroad.get_road_y(anim->sprite->priority) - final_y;
555 
556     // Set H-Flip
557     if (roms.rom0p->read8(7 + index) & BIT_6)
558         anim->sprite->control |= OSprites::HFLIP;
559     else
560         anim->sprite->control &= ~OSprites::HFLIP;
561 
562     // Ready for next frame
563     if (outrun.tick_frame && --anim->frame_delay == 0)
564     {
565         // Load Next Block Of Animation Data
566         if (roms.rom0p->read8(7 + index) & BIT_7)
567         {
568             anim->anim_props    |= 0xFF;
569             anim->anim_addr_curr = anim->anim_addr_next;
570             anim->frame_delay    = roms.rom0p->read8(7 + anim->anim_addr_curr) & 0x3F;
571             anim->anim_frame     = 0;
572         }
573         // Last Block
574         else
575         {
576             anim->frame_delay = roms.rom0p->read8(0x0F + index) & 0x3F;
577             anim->anim_frame++;
578         }
579     }
580     osprites.map_palette(anim->sprite);
581     // Order sprites
582     osprites.do_spr_order_shadows(anim->sprite);
583 }
584 
585 // Render Sprite Shadow For End Sequence
586 // Use parent sprite as basis to set this up
587 // Source: 0x5C48
anim_seq_shadow(oanimsprite * parent,oanimsprite * anim)588 void OAnimSeq::anim_seq_shadow(oanimsprite* parent, oanimsprite* anim)
589 {
590     // Return if no animation data to process
591     if (!read_anim_data(anim))
592         return;
593 
594     if (outrun.tick_frame)
595     {
596         uint8_t zoom_shift = 3;
597 
598         // Car Shadow
599         if (anim->sprite->id == 3)
600         {
601             zoom_shift = 1;
602             if ((parent->anim_props & 0xFF) == 0 && oferrari.sprite_ai_x <= 5)
603                 zoom_shift++;
604         }
605         // 5C88 set_sprite_xy:
606         anim->sprite->x    = parent->sprite->x;
607         uint16_t priority  = parent->sprite->road_priority >> zoom_shift;
608         anim->sprite->zoom = priority - (priority >> 2);
609         anim->sprite->y    = oroad.get_road_y(parent->sprite->road_priority);
610 
611         // Chris - The following extra line seems necessary due to the way I set the sprites up.
612         // Actually, I think it's a bug in the original game, relying on this being setup by
613         // the crash code previously. But anyway!
614         anim->sprite->road_priority = parent->sprite->road_priority;
615     }
616 
617     osprites.do_spr_order_shadows(anim->sprite);
618 }
619 
620 // Read Animation Data for End Sequence
621 // Source: 0x5CC4
read_anim_data(oanimsprite * anim)622 bool OAnimSeq::read_anim_data(oanimsprite* anim)
623 {
624     uint32_t addr = outrun.adr.anim_end_table + (end_seq << 2) + (anim->sprite->id << 2) +  (anim->sprite->id << 4); // a0 + d1
625 
626     // Read start & end position in animation timeline for this object
627     int16_t start_pos = roms.rom0p->read16(addr);     // d0
628     int16_t end_pos =   roms.rom0p->read16(2 + addr); // d3
629 
630     int16_t pos = seq_pos; // d1
631 
632     // Global Sequence Position: Advance to next position
633     // Not particularly clean embedding this here obviously!
634     if (outrun.tick_frame && anim->anim_props & 0xFF00)
635         seq_pos++;
636 
637     // Test Whether Animation Sequence Is Over & Initalize Course Map
638     if (obonus.bonus_control != OBonus::BONUS_DISABLE)
639     {
640         const static uint16_t END_SEQ_LENGTHS[] = {0x244, 0x244, 0x244, 0x190, 0x258};
641 
642         if (seq_pos == END_SEQ_LENGTHS[end_seq])
643         {
644             obonus.bonus_control = OBonus::BONUS_DISABLE;
645             // we're missing all the code here to disable the animsprites, but probably not necessary?
646 
647             if (outrun.cannonball_mode == Outrun::MODE_ORIGINAL)
648                 outrun.game_state = GS_INIT_MAP;
649             else
650                 outrun.init_best_outrunners();
651         }
652     }
653 
654     // --------------------------------------------------------------------------------------------
655     // Process Animation Sequence
656     // --------------------------------------------------------------------------------------------
657 
658     const bool DO_NOTHING = false;
659     const bool PROCESS    = true;
660 
661     // check_seq_pos:
662     // Sequence: Start Position
663     // ret_set_frame_delay:
664     if (pos == start_pos)
665     {
666         // If current animation block is set, extract frame delay
667         if (anim->anim_addr_curr)
668             anim->frame_delay = roms.rom0p->read8(7 + anim->anim_addr_curr) & 0x3F;
669 
670         return PROCESS;
671     }
672     // Sequence: Invalid Position
673     else if (pos < start_pos || pos > end_pos) // d1 < d0 || d1 > d3
674         return DO_NOTHING;
675 
676     // Sequence: In Progress
677     else if (pos < end_pos)
678         return PROCESS;
679 
680     // Sequence: End Position
681     else
682     {
683         // End Of Animation Data
684         if (anim->sprite->id == 8) // Trophy person
685         {
686             anim->sprite->id = 11;
687             if (end_seq >= 2)
688                 anim->sprite->shadow = 7;
689 
690             anim->anim_addr_curr = roms.rom0p->read32(outrun.adr.anim_endseq_obj8 + (end_seq << 3));
691             anim->anim_addr_next = roms.rom0p->read32(outrun.adr.anim_endseq_obj8 + (end_seq << 3) + 4);
692             anim->anim_frame = 0;
693             return DO_NOTHING;
694         }
695         // 5e14
696         else if (anim->sprite->id == 10)
697         {
698             anim->sprite->id = 12;
699             if (end_seq >= 2)
700                 anim->sprite->shadow = 7;
701 
702             anim->anim_addr_curr = roms.rom0p->read32(outrun.adr.anim_endseq_objA + (end_seq << 3));
703             anim->anim_addr_next = roms.rom0p->read32(outrun.adr.anim_endseq_objA + (end_seq << 3) + 4);
704             anim->anim_frame = 0;
705             return DO_NOTHING;
706         }
707     }
708     return PROCESS;
709 }