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 }