1+ from memory .space import Bank , Reserve , Read , Write
2+ import data .battle_animation_scripts as battle_animation_scripts
3+ import instruction .asm as asm
4+ import args
5+
6+ class Animations :
7+ def __init__ (self ):
8+ self .health_animation_reflect_mod ()
9+
10+ if args .flashes_remove_most :
11+ flash_address_arrays = battle_animation_scripts .BATTLE_ANIMATION_FLASHES .values ()
12+ self .remove_battle_flashes_mod (flash_address_arrays )
13+ self .remove_critical_flash ()
14+
15+ if args .flashes_remove_worst :
16+ flash_address_arrays = []
17+ animation_names = ["Boss Death" , "Ice 3" , "Fire 3" , "Bolt 3" , "Schiller" , "R.Polarity" , "X-Zone" ,
18+ "Muddle" , "Dispel" , "Shock" , "Bum Rush" , "Quadra Slam" , "Slash" , "Flash" ,
19+ "Step Mine" , "Rippler" , "WallChange" , "Ultima" , "ForceField" ]
20+ for name in animation_names :
21+ flash_address_arrays .append (battle_animation_scripts .BATTLE_ANIMATION_FLASHES [name ])
22+ self .remove_battle_flashes_mod (flash_address_arrays )
23+
24+ def remove_critical_flash (self ):
25+ space = Reserve (0x23410 , 0x23413 , "Critical hit screen flash" , asm .NOP ())
26+
27+ def remove_battle_flashes_mod (self , flash_address_arrays ):
28+ ABSOLUTE_CHANGES = [0xb0 , 0xaf ]
29+ RELATIVE_CHANGES = [0xb5 , 0xb6 ]
30+ # For each battle animation command
31+ for flash_addresses in flash_address_arrays :
32+ # For each address in its array
33+ for flash_address in flash_addresses :
34+ # Read the current animation command at the address
35+ animation_cmd = Read (flash_address , flash_address + 1 )
36+ if (animation_cmd [0 ] in ABSOLUTE_CHANGES ):
37+ # This is an absolute color change. To remove flashing effects, set the value to E0 to cause no background change
38+ Write (flash_address + 1 , 0xE0 , "Background color change (absolute)" )
39+ elif (animation_cmd [0 ] in RELATIVE_CHANGES ):
40+ # This is a relative color change. To remove flash effects, set the value to F0 to cause no background change
41+ Write (flash_address + 1 , 0xF0 , "Background color change (relative)" )
42+ else :
43+ # This is an error, reflecting a difference between the disassembly used to generate BATTLE_ANIMATION_FLASHES and the ROM
44+ raise ValueError (f"Battle Animation Script Command at 0x{ flash_address :x} (0x{ animation_cmd [0 ]:x} ) did not match an expected value." )
45+
46+ def health_animation_reflect_mod (self ):
47+ # Ref: https://www.ff6hacking.com/forums/thread-4145.html
48+ # Banon's Health command casts Cure 2 on the party with a unique animation.
49+ # Because the animation is unique, it has the step-forward component built into it.
50+ # And because Cure 2 can be reflected, if the command hits a mirrored target it will bounce and make Banon step forward again.
51+ # Note: this only occurs if the whole party doesn't have reflect, only a subset.
52+ # Used over and over, Banon can be made to walk completely off-screen.
53+ #
54+ # Fix:
55+ # We tell the HEALTH animation to ignore block graphics, which prevents the reflect animation from playing.
56+ # When encountering a reflection, the regular green Cure 2 animation will follow on the reflect recipient.
57+ src = [
58+ asm .INC (0x62C0 , asm .ABS ), #Makes the animation ignore blocking graphics
59+ asm .JSR (0xBC35 , asm .ABS ), #Call the subroutine that got displaced to inject the block override
60+ asm .RTS ()
61+ ]
62+ space = Write (Bank .C1 , src , "Health animation fix" )
63+ jsrAddr = space .start_address
64+
65+ # Replace the existing jump with one to our new service routine
66+ space = Reserve (0x1BB67 , 0x1BB69 , "Health animation JSR" )
67+ space .write (asm .JSR (jsrAddr , asm .ABS ))
0 commit comments