Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Messages - RoSoDude

Pages: [1]
1
Nice work man!
Can this mod ever be made for italian version?

I'm not sure. Someone asked me about the Spanish ROM and not only did the US ROM locations for code not match up, but I could not even locate the relevant assembly code anywhere!

However, I've learned more since then about module compression in FF7, and I suspect that the international versions of FF9 have compressed archives for some reason. So I'll give that another look sometime.

2
I have become rather obsessed with the intricacies of the Active Time Battle system in Final Fantasy games. In particular, I find it very interesting how and when the games decide to pause ATB; FF4, FF5, and FF8 all pause during battle animations, while FF6 and FF9 let ATB run in the background (to rather deleterious effects). FF7 is in the middle; in Active mode it works like FF6/9, while in Recommended/Wait mode it works more like FF4/5/8... but with some notable quirks.

The FF7 manual states the following for ATB Recommended: "For moderately skilled players. Time stops while the screen effects are displayed when using Magic and Items."

Yeah, turns out it's much, much more complicated than this. In an earlier post on the issue, Sega Chief theorized that animation sequences had "hooks" to tell the battle engine to pause ATB, but these hooks were getting missed somehow due to menus. This seemed like a plausible theory since I myself had noticed that ATB pausing was inconsistent; sometimes ATB would pause after the startup phase of a spell command, while sometimes it would keep running. Limit Breaks usually fail to pause ATB, but if ATB was already paused going into a limit break, it would stay paused. I decided to go into the debugger to work it out myself, and see if I could fix any "bugs" in the code. It turns out this theory about animation sequences is totally wrong, and ATB pausing is broken by design rather than by any coding bugs.

The basic conditions for ATB pausing are:

  • ATB pauses during all summon animations (regardless of Active/Recommended/Wait)
  • ATB pauses when Wait mode is selected and the user is in a submenu
  • ATB pauses when Recommended/Wait is selected and the animation timer is 16,385 or greater

The animation timer is the tricky part here. First of all, the increment is determined by the Battle Speed, in particular Speed_mod = (65536/(2*(120+(480*Bat_Spd)/256)). Once each frame (at 60fps), this Speed_mod is added to the timer. For example, if the Battle Speed is 0 (fastest), the increment is 273 and so the timer takes 1 second to reach 16385; if Battle Speed is 128 (default), the increment is 91 and the timer takes 3 seconds; if Battle Speed is 255 (slowest), the increment is 54 and the timer takes 5 seconds. This is why you can sometimes see basic attacks pause ATB on the fastest Battle Speed, but you will never see this on slower settings.

More importantly, the timer only increments under specific conditions, and frequently resets before it can reach [1-5] seconds. Let's break it down further. The animation timer only ticks up if the variable I'm calling the "pause flag" is equal to 0x0003, and the timer is usually reset when the pause flag is changed. Here's how it works (the following applies only to Recommended/Wait mode):

  • When no turns are being executed, the pause flag is set to 0x0000
  • At the start of a command animation (excepting commands that jump the queue like counters and limit breaks), the pause flag is set as follows:
    • If the players and enemies have had an equal number of turns, the pause flag is set to 0x0001 and the timer is reset
    • If the players and enemies have had an unequal number of turns, the pause flag is set to 0x0003 and the timer is reset
  • If any character has received full ATB, the pause flag is set to pause_flag = pause_flag OR 0x0002, and the timer is not reset. This bitwise OR has the following effects on existing values of the pause flag:
    • pause_flag == 0x0000 becomes pause_flag == 0x0002
    • pause_flag == 0x0001 becomes pause_flag == 0x0003
    • pause_flag == 0x0002 becomes pause_flag == 0x0002
    • pause_flag == 0x0003 becomes pause_flag == 0x0003

Taking it all together, we can explain some common scenarios with ATB (assuming default Battle Speed):
  • Cloud casts Bolt when all characters already have full ATB. The pause flag is set to 0x0001 and never gets bumped up to 0x0003, so ATB never pauses during the attack
  • Cloud casts Bolt right before Barret gets full ATB. The pause flag is then set to 0x0003 and ATB pauses 3 seconds into the animation
  • Guard Scorpion queues Tail Laser during Cloud's attack, and then ATB is paused. ATB stays paused during Tail Laser
  • Tail Laser gives Cloud and Barret full limit gauges. Cloud activates Braver, but since limit breaks do not update the pause flag it is stuck at 0x0000. The Guard Scorpion gets full ATB during Braver which updates the pause flag to 0x0002. Barrett activates Big Shot (since limit breaks jump the queue), but the pause flag is now stuck at 0x0002 and ATB never pauses
  • Tail Laser gives Barret a full limit gauge. First, Cloud uses a potion, which sets the pause flag to 0x001. During the potion animation, the Guard Scorpion gets full ATB, updating the pause flag to 0x003. Barret activates Big Shot (since limit breaks jump the queue), and ATB pauses within 3 seconds of the animation
  • Cloud and Barret have gotten fewer turns than the Guard Scorpion. Cloud does a basic attack which sets the pause flag to 0x0003, but the animation is done before 3 seconds elapse so ATB never pauses, and the timer resets
  • Cloud and Barret have gotten the same number of turns as the Guard Scorpion. Cloud casts Bolt which sets the pause flag to 0x0001, and Barrett gets full ATB 3 seconds into the animation which sets the pause flag to 0x0003. However, the animation finishes before 3 more seconds can elapse, so ATB never pauses and the timer resets

We can glean several goals of this ATB design from the implementation. In addition to keeping the command queue from getting backlogged, a primary goal is to maintain relatively equal turn counts between players and enemies. Active mode's turn dynamic already does this, but characters spend a lot of time waiting with queued moves while status effects tick away. A secondary goal is to maintain the flow of battle in contrast to the strict animation pausing of FF4/5/8, such that turns come up during animations like in FF6/9/Active but without the balance drawbacks that these bring. A final goal is to make this all scale with the Battle Speed setting, such that the ATB pauses more often at high Battle Speed but less often at low Battle Speed. However, despite these reasonable goals, the implementation is a bit of a failure in my view, since there are so many conditions that reset (or fail to set) the timer that the ATB pause ends up feeling random and unpredictable, which is not ideal for strategic gameplay.

I will be updating this thread later with commented MIPS disassembly that details the implementation. I already programmed a patch which simplifies the system; I removed the check for player/enemy turn counts and set the pause flag on all attacks (whether player, enemy, or limit break) to 0x0001 always, and pause ATB as soon as the pause flag is updated to 0x0003 by a character getting full ATB. This puts a greater emphasis on the effect of Dexterity on character speed (rather than equalizing turn counts), and lets ATB flow until the next move is ready to go onto the queue.

3
Hi there. I don't have particular information about FF7, but I think it is likely that it works just like in FF9, for which I recently made a ROMhack that increases the 3D scene framerate in battle from 15 fps to 20 fps. So I should be able to help, and this may serve as usual documentation for others.

In FF9, the game runs at 60fps (really 59.94 Hz), but the 3D scene only updates every 4th frame. This is accomplished using a frame counter at RAM address 0x80104E84 that ticks up on every menu frame (there are many many frame counters all over RAM; this is the relevant one). Here's the code to increment that frame counter:

Code: [Select]
000e9b70: 3c058010 lui r5,0x8010 ; r5 = 0x80100000
000e9b74: 24a54e68 addiu r5,r5,0x4e68 ; r5 = r5 + 0x4E68 = 0x80104E68
000e9b78: 3c038007 lui r3,0x8007
000e9b7c: 8ca2001c lw r2,0x001c(r5)
000e9b80: 24637148 addiu r3,r3,0x7148
000e9b84: 24420001 addiu r2,r2,0x0001
000e9b88: aca2001c sw r2,0x001c(r5)
000e9b8c: 8c620008 lw r2,0x0008(r3)
000e9b90: 8ca40018 lw r4,0x0018(r5) ; r4 = $(r5 +0x0018) = $0x80104E68 [load frame counter]
000e9b94: 24420001 addiu r2,r2,0x0001
000e9b98: 24840001 addiu r4,r4,0x0001 ; r4 = r4 + 1 [increment frame counter]
000e9b9c: ac620008 sw r2,0x0008(r3)
000e9ba0: aca40018 sw r4,0x0018(r5) ; store r4 at $(r5 +0x0018) = $0x80104E68 [store frame counter]

This frame counter is then compared with a buffer frame limit that's stored at RAM address 0x8008B009. If the frame count is equal to the buffer frame limit minus 1, the game code tells the processor to update the 3D scene and resets the frame counter to 0x00. So we need to modify the buffer frame limit to get faster battle animations.

Code: [Select]
000e9ee0: 3c038010 lui r3,0x8010 ; r3 = 0x80100000
000e9ee4: 3c028006 lui r2,0x8006 ; r2 = 0x80060000
000e9ee8: 8c42794c lw r2,0x794c(r2) ; r2 = $(r2+0x794c) = 0x8008B000
000e9eec: 24704e68 addiu r16,r3,0x4e68 ; r16 = r3 + 0x4E68 = 0x80104E68
000e9ef0: 90430009 lbu r3,0x0009(r2) ; r3 = $(r2 + 0x0009) = $0x8008B009 [load buffer frame limit]
000e9ef4: 8e02001c lw r2,0x001c(r16) ; r2 = $(r3 + 0x001C) = $80104E84 [load frame counter]
000e9ef8: 2464ffff addiu r4,r3,0xffff ; r4 = r3 - 1 [buffer frame limit - 1]
000e9efc: 0044102b sltu r2,r2,r4 ; r2 = (r2 < r4)
000e9f00: 10400003 beq r2,r0,0x000e9f10 ; branch if r2 == 0 [frame counter was less than buffer frame limit, skip updating 3D scene]
000e9f04: 00000000 nop
000e9f08: 0c005716 jal 0x00015c58 ; jump to subroutine to update 3D scene
000e9f0c: 00000000 nop
000e9f10: 0c03d0e1 jal 0x000f4384
000e9f14: 00000000 nop

The buffer frame limit is set in two different places. At the start of battle, it's set to 0x04 by this code (60 fps / 4 = 15 fps).

Code: [Select]
000a8d08: 240400da addiu r4,r0,0x00da
000a8d0c: 8e23794c lw r3,0x794c(r17)
000a8d10: 24020004 addiu r2,r0,0x0004 ; wait 4 frames for update = 15fps
000a8d14: 0c007341 jal 0x0001cd04
000a8d18: a0620009 sb r2,0x0009(r3) ; store buffer frame limit at $0x8008B009

I was able to set the 3D scene framerate by changing "addiu r2,r0,0x0004" to "addiu r2,r0,0x0003", i.e. setting the buffer frame limit to 3 (60 fps / 3 = 20 fps)

The buffer frame limit is also set during some spell animation sequences. Some animations will temporarily change the framerate e.g. to 20 fps and then back to 15 fps later in the animation sequence. The animation sequence uses the opcode 0x2C for this, for example a target framerate of 20 = 0x14 would be set using the opcode "2C 14 00". This target framerate is read by a subroutine that divides 60 by the target framerate to calculate the buffer frame limit as an integer value (so only 60 / x framerates are possible).

Code: [Select]
000e45d0: 27bdffe8 addiu r29,r29,0xffe8 ; SUBROUTINE: set 3D battle framerate
000e45d4: 24020001 addiu r2,r0,0x0001
000e45d8: 14820012 bne r4,r2,0x000e4624
000e45dc: afbf0010 sw r31,0x0010(r29)
000e45e0: 8fa20028 lw r2,0x0028(r29)
000e45e4: 00000000 nop
000e45e8: 8c420000 lw r2,0x0000(r2) ; load target framerate
000e45ec: 2404003c addiu r4,r0,0x003c ; r4 = 60 (global framerate)
000e45f0: 0082001a div r4,r2 ; divide r4/r2 (buffer frame count)
000e45f4: 14400002 bne r2,r0,0x000e4600 ; make sure we didn't divide by 0
000e45f8: 00000000 nop
000e45fc: 0007000d break 0x00001c00
000e4600: 2401ffff addiu r1,r0,0xffff
000e4604: 14410004 bne r2,r1,0x000e4618 ; make sure we didn't divide by 0xFFFF
000e4608: 3c018000 lui r1,0x8000
000e460c: 14810002 bne r4,r1,0x000e4618 ; make sure we didn't divide from 0x80000000 (?)
000e4610: 00000000 nop
000e4614: 0006000d break 0x00001800
000e4618: 00002012 mflo r4 ; get result of divide
000e461c: 0c03a3d0 jal 0x000e8f40 ; store buffer frame limit in r4
000e4620: 00000000 nop
000e4624: 8fbf0010 lw r31,0x0010(r29)
000e4628: 24020001 addiu r2,r0,0x0001
000e462c: 03e00008 jr r31
000e4630: 27bd0018 addiu r29,r29,0x0018

I modified this code to subtract 1 from the buffer frame limit if it was 0x04 or greater (15 fps -> 20 fps, 7.5 fps -> 8.58 fps, etc.) so that animations would return to the correct default of 20 fps from the "2C 0F 00" opcode.

I would imagine there is similar code in FF7, but it would take a lot of searching through RAM in the debugger to chase it down. I was actually considering doing this work for FF7 and FF8 myself to make patches to speed up their battles, though it isn't as pressing an issue as with FF9 and its sluggish battle animations.

4
Final Fantasy 9 — Comprehensive ATB Enhancement v1.00
by RoSoDude https://rosodudemods.wordpress.com/

Final Fantasy 9’s ATB system has some notable flaws compared to other entries in the series. ATB is always running in the background regardless of battle animations, so commands can quickly pile up on the queue. This functionally caps the effectiveness of the Agility stat, lets buffs wear off too quickly, and makes auto-regen overpowered. Furthermore, it enables “Wait tricking” abuse with the Wait mode config setting, where the player enters a command submenu to pause ATB during player animations, but lets ATB resume during enemy animations to gain a turn order advantage. Finally, there is general agreement that the battle animations in FF9 are too slow, leading to very sluggish battles compared to prior entries.

Whereas a previous hack sought to speed up battles by increasing the speed of ATB alone, this only exacerbated the problem with slow animations and the command queue, and furthermore lead to status effects ticking much too quickly in the background. This hack makes ATB gauges and other battle timers pause during battle animations, identical to my Comprehensive ATB Enhancement hack for FF6. ATB speed and rates for all status effect timers are more than doubled to offset the added downtime. The 3D scene framerate in battles is also increased by 33%, resulting in faster battle animations and a shorter camera swirl at the start of fights. Monster ATB has a small delay to account for higher Speed stats and instant command queueing. Instant fill ATB gimmicks are removed from several enemies (Ozma, Hades, Kraken, Tonberries) to make these fights possible in the new ATB system. The end result should be a more tactical and robust ATB system which can be tweaked to the user’s preference using the in-game config settings.

Included alongside the main hack is an alternate “CTB Wait Addon” patch which removes all active time elements from battle when “Wait” mode is selected, similar to FF10’s CTB battle system (or the PC Memoria Engine’s turn-based mode). See the separate readme for details.

The included patches are compatible with 1.0 and 1.1 (Rev 1) US ROMs. The hack should be compatible with pretty much every other hack for FF9, except for “Final Fantasy IX ATB Gauge SpeedUp” which is redundant. A special compatibility patch is required for use with Alternate Fantasy v3.2; see the separate readme for details.
NOTE: If the game gets stuck on a black screen while loading (title screen or battles) when used alongside another hack, you need to use error_recalc or EDCRE to fix the EDC/ECC data on your ROM.

Source files for assembly and hex codes are included as a reference for other modders.

Acknowledgements to FFHacktics community members Tallcall, Sardek, Xifanie, and RetroTypes for essential tips on MIPS assembly/PSX debugging
Acknowledgements to Tirlititi for Hades Workshop editor and technical information

Download here


5
Hi there, I'm trying to investigate whether it is feasible to make a hack for the PSX version of FF9 which pauses ATB during animations (I wrote a similar hack to do this for FF6 on the SNES, as it had the same issue). There's already a hack to speed up ATB, but this just leads to a stuffed command queue while you wait for the animations to play out. It would be nice to have faster ATB gauges that also pause during animations so the turn order is more meaningful, and commands aren't so delayed.

Also, I wonder if anything can be done about the long pauses before animations start?

I know assembly (most familiar with SNES 65816), so I am willing to dig through MIPS code if I know where to start looking.

Pages: [1]