Users browsing this thread: 1 Guest(s)
Changing and Replacing Combat Functions via Special Effects

#1
Posts: 61
Threads: 18
Thanks Received: 1
Thanks Given: 47
Joined: May 2014
Status
None
Hi hackers.  This thread is going to be for mapping out the combat function and explaining how to change and replace combat formulas via special effects, making abilities and items much more dynamic with the game.

So far I've made a rocking formula successfully but I do have 3 questions for anyone who might be familiar with storing values, special effects, and the combat function.  I have searched about these, btw.

Q1: Storing Values.

I'm working on some new formulas and when I look at the original game formulas, it seems to store values in a low range of addresses.  I'm looking to use 2-3 byte calculations for changing and replacing damage functions.  Is there any range in particular I would need to store this extra data?  There are at least two spots where damage is stored, for example -- $F0 and $11B0.  I'm guessing the low address values are due to development planning rather than system mechanics---anyone know about that?

Q2: Special Effects

Special effects are called in a lot of different places--it looks like there's two tables and only one of them works for my spell sfx.  Has anyone found any surprising problems with editing special effects in general?  They're called in reflect and launcher too, IIRC.

Q3: Combat Functions

It seems like the combat --> damage --> result function never ends.  Any caveats and wisdom would be much appreciated before I wreck my hack for good.  I'm surprised at how little it seems the modify damage routine is referenced, but I'm worried there are so many references upstream I'm not seeing, that changing damage is too risky when added via special effects into $11B0 and $F0.

Thanks all.  Whether we have any wisdom on this or not, I'll be sure to make it enlightening for us.
RS
  Find
Quote  

#2
Posts: 61
Threads: 18
Thanks Received: 1
Thanks Given: 47
Joined: May 2014
Status
None
I've got two new special effects for us that work and one that doesn't work but seems very close to doing so.

FIRST REPLACEMENT - New Special Effect $14 - Mpwr^2/8 Added to damage

Code:
C2/3DF5: 8C 38 --> 90 67     Special ($14h) [20] now points to 6790
C2/4309: 8A 3E --> 90 67     Special ($14h) [20] now points to 6790

\\We changed both pointer tables because it looks like only the second one works for the spell during testing\\

C2/6790: E2 20        SEP #$20    (Set 8-bit Accumulator)
C2/6792: AD A0 11     LDA $11A0   (Mpwr)
C2/6795: EB           XBA
C2/6796: AD A0 11     LDA $11A0   (Mpwr)
C2/6799: 20 81 47     JSR $4781   (Multiply them together Mpwr * Mpwr)
C2/679C: C2 20           REP #$20    (Set 16-bit Accumulator)
C2/679E: 4A           LSR
C2/679F: 4A           LSR
C2/67A0: 4A           LSR      (Divide by 8)
C2/67A1: 18          CLC
C2/67A2: 6D B0 11     ADC $11B0   (Add to damage)
C2/67A5: 8D B0 11     STA $11B0   (Store in maximum damage)
C2/67A6: 60           RTS

Notice that I use XBA and JSR $4781 to multiply the $11A0 Mpwr together.  I then tried using $11A0 and $11A6....and it didn't work to create Mpwr*Vigor.  I have the same situation for the next formula below---I get a bit more daring by using the Y index to Lvl^2, but I have a suspicion that other data near those values (3B18) don't work when called by special effects.

The XBA and JSR technique was stolen from the Flare Star code, which I'll discuss in detail for my third, huge, formula:

SECOND REPLACEMENT - $03 - Lvl*Lvl Added to Damage


Code:
C2/3DD3: 43 3D ($03) --> B0 67   (jump to $67B0 as new special effect $03, 4th in weapon sfx menu in FF3usME,
                  replacing kill with X)
C2/42E7: 8A 3E ---->     B0 67

C2/67B0: E2 20        SEP #$20    (Set 8-bit Accumulator)
C2/67B2: B9 18 3B     LDA $3B18,Y (Level of attacker)
C2/67B5: EB           XBA
C2/67B6: B9 18 3B     LDA $3B18,Y (Level of attacker)
C2/67B9: 20 81 47     JSR $4781   (Multiply them together level * level)
C2/67BC: C2 20           REP #$20    (Set 16-bit Accumulator)
C2/67BE: 18          CLC
C2/67BF: 6D B0 11     ADC $11B0   (Add to damage)
C2/67C2: 8D B0 11     STA $11B0   (Store in maximum damage)
C2/67C5: 60           RTS

But first let's talk about how insanely great this is.  Would you rather have a sword that breaks, like ogre nix, or a sword that adds damage based on your Mpwr?  The limits of interesting combat are quickly destroyed with the prismatic expansion of damage formulas.  Do you really like having exploder in your Lore list?

But how much can we change?  I've recently read a lot of the Programming the 65816 document and have, of course, made some formulas that are much more sophisticated than a squared hero statistic.  This one isn't super sophisticated but it doesn't just do damage = your HP or dmg+HP, it does [(HP/64+1)+(MP/16+1)]^2 and replaces the damage from normal formulas:

Code:
HP+MP Damage Formula

OpCode        Registers->  X   B       A    Summary with comments    
C2 20        REP #20                16-bit mode    
B9 F4 3B    LDA, Y $3BF4            LDA, Y    HP
4A                        LSR    Divide by 2
4A                        LSR    4
4A                        LSR    8
4A                        LSR    16
4A                        LSR    32
4A                        LSR    64
1A                        INC    HP/64+1
48                        PHA    
B9 08 3C    LDA, Y $3C08            LDA, Y    MP
4A                        LSR    Divide by 2
4A                        LSR    4
4A                        LSR    8
4A                        LSR    16
1A                        INC    MP/16+1
8D 18 A7    STA $7A18            STA    Store at two bytes
68                        PLA    A=HP/64+1
18                        CLC    Clear carry before adding
6D 18 A7    ADC $7A18        HP+MP    ADC    Add stored MP/16+1
4A                        LSR    Divide by 2
E2 20        SEP #$20        HP+MP    8bit mode    
8D 18 A7    STA$7A18            STA    Store HP+MP at two bytes (A byte if 8-bit mode!)
EB        XBA        HP+MP        XBA    
18                        CLC    
6D 18 A7    ADC $7A18    HP+MP    HP+MP    ADC    Add stored HP+MP
C2 20        REP #20                16bit    
20 81 47    JSR $4781            JSR    Multiply them together
0A                        ASL    x2
8D 18 A7    STA $A718            STA    Store big value HP+MP at two bytes
B9 F4 3B    LDA, Y $3BF4            LDA, Y    HP
4A                        LSR    Divide by 2
4A                        LSR    4
18                        CLC    Clear carry before adding
6D 18 A7    ADC $A718            ADC    Add stored HP+MP
90 03        BCC +4                BCC    If carry wasn't set, overflow did not occur
A9 FF FF    ADC #$65535            LDA    Replace with 65535
8D B0 11    STA $11B0            STA    Store to replace dmg at 11B0
60                        RTS

And this time, for some reason, the formula doesn't work.  This code loads HP and MP data, reduces them to a # which will not exceed 1 byte, then goes into 8-bit mode to do the XBA JSR trick again.  The XBA trick swaps the high byte and low byte of the accumulator, with one of them (the high byte, I believe) referred to as "B" when in 8bit-mode, because there is no 2nd byte in 8bit-mod, but the space (and thus, the hex values there) is still there, it just isn't touched.  When you swap the low byte and high byte, you can multiply or divide the value by 256, or you can use the JSR $4781 multiplication subroutine to multiply the two bytes together, with the result in 16bit A.

The formula of this type is much more powerful because it multiplies 2 byte values like HP and MP together, although it's not perfectly ideal because I believe there is a lot of rounding down that occurs.

So why do the first two formulas work and this one doesn't? The same HP value is called by Valiant Knife, so that should be fine---but when I change the hero stats to be $11A0 (Mpwr) instead, the formula works brilliantly.

Does anyone know the values that can or can't be loaded during combat?
Below is the quick list of values I've found throughout C2:

Quote:    Speed (hero) - $11A4
    Stamina (hero) -  $11A2
    Magic Power of hero -  $11A0
    Vigor of hero - $11A6 -->Actually Vigor or BP or SP I think in some cases. Careful, this gets changed
    Vigor*2 or Mpwr - $11AE (this is weird)
    Level of attacker - $11AF (probably hero only)
    MDef - $3BB9,Y @ C/20CCC after    damage mod
    Def - $3BB8,Y
    HP of Hero - $3BF4, Y
    MP of Hero - $3C08,Y
    Max HP - $3C1C,Y (for hero only?)
    $3C1D,Y - High byte of Max HP (for hero only?)
    $3BF4,Y - HP (for hero only?)
    $3BF5,Y - High byte of HP (for hero only?)
    $3B2C,X - (attacker Vigor * 2)
    $11A8 - Hit rate (of spell checking for stamina blocking)
    $3C08,X - (MP)
    $3C30,X - (Max MP)


I should also mention that in this code, Y seems to be the attacker and X the target, but it seems extremely difficult to track the source of what's in the X or Y registers.
(Note: On the site the code text looks jilted but when editing it the code and comments are perfectly straight)
(To use these formulas by the way, FF3usME has the special effect box on the right when looking at a spell, and the special effect drop-down menu for weapons.  The first formula here is effect $14 in the spell menu, the 2nd is the "0x3" special effect for weapons, replacing the "Kill with X" effect.)
  Find
Quote  

#3
Posts: 290
Threads: 3
Thanks Received: 40
Thanks Given: 1
Joined: Apr 2012
Reputation: 9
Status
None
One thing you can do to troubleshoot your custom functions is to run them through the SNES9x debugger.

http://geigercount.net/crypt/

Just set a breakpoint at the address in question, then you can go through it line-by-line to check which values are getting loaded, and if they are what you expect.


GET A SILK BAG FROM THE GRAVEYARD DUCK TO LIVE LONGER.

Brave New World
  Find
Quote  
[-] The following 1 user says Thank You to Synchysi for this post:
  • ReturnerScum (02-27-2016)

#4
Posts: 61
Threads: 18
Thanks Received: 1
Thanks Given: 47
Joined: May 2014
Status
None
Thanks I'll try and figure out how to use that.  This thread isn't just about debugging my formulas by the way---it's to find ways that actually work, so if anyone else has successfully changed things up in a similar way, please feel free to join in.

Alright that debug tool worked like a charm once I figured out how to use it.  You have to add a breakpoint then check the "Execute" and "read"function and type in your 6 digit hex address.  When the address is called or used then it stops and you have to push the "step into" button to go step-by-step through the code that is executing and compare what should happen.  Then you can use the same program to change the hex code of the ROM on the fly and reload.

What the debugging tool also means, is that you can track the entire combat function using the same technique.  There's probably some button that can just write the code to a file but that wasn't explained by the self-titled "l337" pre-pubescent boy who was explaining this to me on Youtube.

Also I've found a nice explanation of the special effects combat function when foolishly trying to remove a patch by Assassin, the capture weapon fix "Recapture the Glory":

Quote:Also, the game only has a few basic damage formulae: one used by physical spells (and by
weapons, as Battle is physical), another used by magical spells, one for spells/items that
affect a fraction of the target's HP or MP, and one for items that just do a constant amount
of healing/damage.

What if you want to do a unconventional type of damage, like based on how many steps your
party has taken?

What if you want to use special criteria for how often an attack hits, like with Ragnarok's
Metamorph or Relm's Control?

What if you want to change statuses at random rather than a spell always trying to
inflict/remove/toggle the same ones?

What if you want to do greater damage to human targets?

For Square, the answer to all those above questions was the same: you make a special effect.

There are dozens of unique behaviors Square felt like throwing into the game, so it would
hardly be economical to designate a "Yes / No" bit for each one as done with more common
properties like elementals.  Instead, they set aside a single "special effect" byte in the
spell data and the item data (the latter also includes weapons, equipment, relics, tools,
etc).  When you perform an attack, this ROM byte is copied into the RAM data for the current
attack.  The byte's value determines which special effect function(s) is/are called.

Up to two functions can be assigned to a given effect #: one routine is called just once per
strike (where a "strike" can have single or multiple targets); the other is called once for
each separate target.  The former type of routine doesn't care about target properties,
while the latter does.  Spells/items without a special effect just put zero in the byte,
which results in harmless empty functions being called.

.....

As mentioned in the last three paragraphs, there is just one special effect byte.  That's
plenty sufficient in all but one known case: using the Capture command in conjunction
with a weapon that has a special effect of its own (see Section 0 for the many examples).

First, the weapon's special effect byte is copied into the RAM attack data.  But before
its special effect function can be called, the Capture command overwrites that byte with
A4h.  Thus, you'll get the thievery aspect, but lose any snazzy properties the weapon may
have.  To handle this combination correctly, two bytes would have been needed.

____________________________________________________________________________________________

3. WHAT MY PATCH DOES
____________________________________________________________________________________________

As Kirstie Alley and Marv Albert will tell you, one byte isn't always enough.  This patch
introduces a second byte to allow for multiple special effects if -- and only if -- the
Capture command is involved.  Here's a very rough summary of the relevant happenings in a
given strike ("NC" means no change from original game):

- Zero the new special effect variable ($2F3D).

- The weapon data is loaded into the attack data, which includes copying the weapon special
  effect into the normal special effect variable ($11A9).  Weapons without a special effect
  just set the variable to zero.  NC

- If the command is Capture, A4h (Steal) is put into a new special effect byte ($2F3D.
  Previously, the game had put it into $11A9).

- [...]

- The once-per-strike special effect routine is called as normal (i.e. using $11A9's value.
  If it's zero, a harmless empty function is called).  NC

- For each target of a strike (there should only be one target with Fight and Capture, as
  multiple Offering or Genji Glove hits are considered separate strikes):

  - [...]

  - The once-per-target special effect routine is called as normal (i.e. using $11A9's
    value.  If it's zero, a harmless empty function is called).  NC

  - If Variable $11A9 =/= Variable $2F3D, call the once-per-target special effect function
    again, this time using $2F3D's value so as to attempt a steal.  (If $2F3D is zero, a
    harmless empty function is called.)

    We skip the call if $11A9 equals $2F3D, because that means either:

     - Both special effects are A4h (Steal).  We have the Capture command in conjunction
       with a capture attempted by Thief Knife; there's no reason to call the Steal function
       twice, and doing so would likely screw things up.

     OR

     - Both special effects are 0 (Nothing), in which case calling a second empty function
       is harmless, but a waste of CPU cycles.


As mentioned above, no modification was needed for the once-per-strike special effect call,
because Steal only has a once-per-target function defined.  Also note that the
once-per-target special effect call in the Super Ball/Launcher/Reflected spells routine
isn't changed, as there's no way to combine Capture with those without extensive hacking.

The problem with my formula was that my storage bytes were in the middle of the code and being interpreted as ASM instructions, derailing the train from there. Now the damage is off for a reason I haven't figured out yet. It should be doing around 4801 dmg with 2063 HP and 651 MP, but ends up around 1100.
  Find
Quote  

#5
Posts: 61
Threads: 18
Thanks Received: 1
Thanks Given: 47
Joined: May 2014
Status
None
Here is my mapping of the two SFX tables when cross-checking the C2Code and FF3usME:

Edit: Since all my text ends up jilted, a document is attached with this (even though it also doesn't look straight in the google drive preview).

Code:
C2/3DCD:    8C    38            Nothing        C2/42E1:    8A    3E
C2/3DCF:    8C    38            Steal        C2/42E3:    8B    3E
C2/3DD1:    8C    38            ($2)        C2/42E5:    44    40
C2/3DD3:    43    3D    ($3)                C2/42E7:    8A    3E
C2/3DD5:    F2    38    ($4)                C2/42E9:    8A    3E
C2/3DD7:    8C    38            ($5)        C2/42EB:    73    3F
C2/3DD9:    8C    38            ($6)        C2/42ED:    6E    3F
C2/3DDB:    8C    38            ($7)        C2/42EF:    22    3F
C2/3DDD:    FE    38    ($8)                C2/42F1:    8A    3E
C2/3DDF:    8C    38            ($9)        C2/42F3:    58    41
C2/3DE1:    8C    38            ($0A)        C2/42F5:    89    3F
C2/3DE3:    8C    38            ($0B)        C2/42F7:    9F    3F
C2/3DE5:    8C    38            ($0C)        C2/42F9:    7E    3F
C2/3DE7:    8D    38    ($0D)                C2/42FB:    8A    3E
C2/3DE9:    8C    38            ($0E)        C2/42FD:    CA    3E
C2/3DEB:    8C    38            ($0F)        C2/42FF:    50    3F
C2/3DED:    5B    3C    ($10)                C2/4301:    8A    3E
C2/3DED:    8C    38            ($11)        C2/4303:    65    3F
C2/3DF1:    3C    3A    ($12)                C2/4305:    8A    3E
C2/3DF3:    34    39    ($13)        ($13)        C2/4307:    F6    41
C2/3DF5:    8C    38            Empty!        C2/4309:    8A    3E
C2/3DF7:    8C    38            ($15)        C2/430B:    63    42
C2/3DF9:    8C    38            ($16)        C2/430D:    34    42
C2/3DFB:    A2    3C    ($17)                C2/430F:    8A    3E
C2/3DFD:    8C    38            ($18)        C2/4311:    4E    40
C2/3DFF:    4C    3C    ($19)        ($19)        C2/4313:    FC    3F
C2/3E01:    8C    38            ($1A)        C2/4315:    FE    40
C2/3E03:    8C    38            ($1B)        C2/4317:    54    3F
C2/3E05:    90    3C    ($1C)        ($1C)        C2/4319:    B7    42
C2/3E08:    8C    38            ($1D)        C2/431B:    A1    40
C2/3E09:    8C    38            ($1E)        C2/431D:    A0    3E
C2/3E0B:    78    39    ($1F)                C2/431F:    8A    3E
C2/3E0D:    B8    3C    ($20)                C2/4321:    8A    3E
C2/3E0F:    4    3C    ($21)                C2/4323:    8A    3E
C2/3E11:    22    39    ($22)                C2/4325:    8A    3E
C2/3E13:    8C    3C    ($23)                C2/4327:    8A    3E
C2/3E15:    8C    38            Crusader    C2/4329:    8A    3E
C2/3E17:    6B    3B    ($25)                C2/432B:    8A    3E
C2/3E19:    89    39    ($26)                C2/432D:    8A    3E
C2/3E1B:    6C    39    ($27)        ($27)        C2/432F:    BB    40
C2/3E1D:    B0    3B    ($28)        ($28)        C2/4331:    3C    41
C2/3E1F:    8C    38            ($29)        C2/4333:    4D    41
C2/3E21:    8C    38            ($2A)        C2/4335:    0F    41
C2/3E23:    80    39    ($2B)                C2/4337:    8A    3E
C2/3E25:    8C    38            ($2C)        C2/4339:    37    40
C2/3E27:    37    3D    ($2D)                C2/433B:    8A    3E
C2/3E29:    CE    3C    ($2E)                C2/433D:    8A    3E
C2/3E2B:    A8    3D    ($2F)                C2/433F:    8A    3E
C2/3E2D:    6E    3C    ($30)        ($30)        C2/4341:    8B    42
C2/3E2F:    8C    38            ($31)        C2/4343:    DA    40
C2/3E31:    8C    38            ($32)        C2/4345:    F1    40
C2/3E33:    66    39    ($33)        ($33)        C2/4347:    61    40
C2/3E35:    91    3C    ($34)                C2/4349:    8A    3E
C2/3E37:    49    3D    ($35)                C2/434B:    8A    3E
C2/3E39:    8C    38            ($36)        C2/434D:    1B    42
C2/3E3B:    1E    3D    ($37)                C2/434F:    8A    3E
C2/3E3D:    6C    39    ($38)                C2/4351:    8A    3E
C2/3E3F:    5E    39    ($39)                C2/4353:    8A    3E
C2/3E41:    27    3D    ($3A)                C2/4354:    8A    3E
C2/3E43:    CB    3B    ($3B)                C2/4357:    8A    3E
C2/3E45:    8C    38            Retort?        C2/4359:    8A    3E
C2/3E47:    8C    38            ($3D)        C2/435B:    E6    41
C2/3E49:    7C    3D    ($3E)                C2/435D:    8A    3E
C2/3E4B:    85    3D    ($3F)                C2/435F:    8A    3E
C2/3E4D:    B0    3D    ($40)                C2/4361:    8A    3E
C2/3E4F:    8C    38            ($41) Unused?    C2/4363:    85    42
C2/3E51:    8C    38            ($42) Unused?    C2/4365:    80    42
C2/3E53:    8C    38            ($43)        C2/4367:    CA    42
C2/3E55:    FD    3C    ($44)        ($44)        C2/4369:    4B    42
C2/3E57:    8C    38            Clear (spell?)    C2/436B:    8A    3E
C2/3E59:    8C    38            Empty!        C2/436D:    8A    3E
C2/3E5B:    8C    38            Empty!        C2/436F:    8A    3E
C2/3E5D:    8C    38            Empty!        C2/4371:    8A    3E
C2/3E5F:    8C    38            ($49)        C2/4373:    AD    3F
C2/3E61:    B8    3D    ($4A)        ($4A)        C2/4375:    2C    40
C2/3E63:    6C    39    ($4B)        ($4B)        C2/4377:    C8    40
C2/3E65:    17    3D    ($4C)        ($4C)        C2/4379:    36    41
C2/3E67:    8C    38            ($4D)        C2/437B:    4E    40
C2/3E69:    8C    38            Empty!        C2/437D:    8A    3E
C2/3E6B:    8C    38            Empty!        C2/437F:    8A    3E
C2/3E6D:    98    3B    ($50)        ($50)        C2/4381:    95    40
C2/3E6F:    8C    38            ($51)        C2/4383:    B7    3F
C2/3E71:    9E    39    ($52)                C2/4385:    8A    3E
C2/3E73:    C5    3A    ($53)                C2/4387:    8A    3E
C2/3E75:    71    3B    ($54)                C2/4389:    8A    3E
C2/3E77:    29    3B    ($55)                C2/438B:    8A    3E
C2/3E79:    8D    3A    ($56)                C2/438D:    8A    3E
C2/3E7B:    78    3C    ($57)                C2/438F:    8A    3E
  Find
Quote  

#6
Posts: 149
Threads: 21
Thanks Received: 40
Thanks Given: 3
Joined: Dec 2013
Reputation: 9
Status
Auto-life
Quote:I'm working on some new formulas and when I look at the original game formulas, it seems to store values in a low range of addresses.  I'm looking to use 2-3 byte calculations for changing and replacing damage functions.  Is there any range in particular I would need to store this extra data?  There are at least two spots where damage is stored, for example -- $F0 and $11B0.  I'm guessing the low address values are due to development planning rather than system mechanics---anyone know about that?

$11B0 (low) and $11B1 (high) are the two-byte damage value. $F0 generally is used as a temporary buffer or variable to calculate damage. For special effects, to setup $11B0 AND $11B1 are the correct way to setup a custom formula damage.

Quote:Special effects are called in a lot of different places--it looks like there's two tables and only one of them works for my spell sfx.  Has anyone found any surprising problems with editing special effects in general?  They're called in reflect and launcher too, IIRC.

Some special effects have hard coded features and are manually setup in code instead of a weapon or spell data. Examples are sketch, control, steal, etc. If they are modified, whatever code that setups them must be nullified as well to avoid undesired surprises.

Quote:It seems like the combat --> damage --> result function never ends.  Any caveats and wisdom would be much appreciated before I wreck my hack for good.  I'm surprised at how little it seems the modify damage routine is referenced, but I'm worried there are so many references upstream I'm not seeing, that changing damage is too risky when added via special effects into $11B0 and $F0.

The special effects that changes damage only overwrites the calculated default damage setup in $11B0 and $11B1. If the special effects don't change damage, the default damage is left as it was calculated.

Quote:I should also mention that in this code, Y seems to be the attacker and X the target, but it seems extremely difficult to track the source of what's in the X or Y registers.

I believe X or Y are the fighters index. 0, 2, 4 and 6 are the party side and above it are the opposition (monsters). As example, $3BF4,0 should be the actual hp of the first party member. $3BF4,2 should be the actual hp of the second party member, etc. The data stored in similar two bytes structures are always two bytes to accommodate all fighter indexes, party and opposition alike.

About the wrong text display in the posts, probably you used tabs instead of white space characters. The tab characters will try to setup the column text in a different position in the forum instead of the position of your text editor. You can try to check if your text editor has the option to automatically convert tab spaces in white spaces.
  Find
Quote  
[-] The following 2 users say Thank You to HatZen08 for this post:
  • madsiur (07-30-2018), ReturnerScum (07-30-2018)



Forum Jump:

Users browsing this thread: 1 Guest(s)


Theme by Madsiur2017Custom Graphics by JamesWhite