• Welcome to Final Fantasy Hacktics. Please login or sign up.
 

[ASM] - Changing formula 2D (PA * (WP + Y)) to (XA * (YA +- Y)); etc

Started by nitwit, August 01, 2022, 08:03:54 pm

nitwit

Looking at formula 2D:
https://ffhacktics.com/wiki/2D_Dmg_(PA*(WP%2BY))_100%25_Status

Could I replace this line:
00189b38: jal 0x00185e5c               |-->Store PA and WP + Y XA is Attacker's PA and YA is WP + Y
... with a subroutine call (jal) to this Base XA Calculation (see below) to change the swordskill formula to take the current weapon's formula?
https://ffhacktics.com/wiki/Base_XA_Calculation



I know that alone isn't sufficient to make the change. I also want to do the things below, but I figure one question at a time is all that I can handle.
  • Maybe change require sword to require either the direct or lunging weapon flags.
  • Integrate formulas 2D, 2E, 2F, and 30, or as many of them as I can, so I can make them configurable and smoosh it all into the same space to avoid jumps to free space.
  • Logical OR the weapon and skill elements unless some flag is set to take one or the other.
  • Make Y signed.
  • Maybe make it default to the unarmed formula if a non-direct/lunging weapon is equipped.

I want to make these changes so swordskills can be used with more weapons & classes, and to enable varied swordskill builds (or a swordskill generic class if someone wants it) that work with mage weapons & classes.

Orkney

Hi,

Changing the Jal should work just fine (used weapon ID is ok with swordskills.

I'm not sure for the require sword things, because i haven't deal with this yet. But i don't see why you couldn't grab the weapon characteristics instead of ID or type. (you can use the ARH hack too)

Not sure to understand the integrate thing with swordskills formulas. You mean merge them into one ? It's doable of course but it's more complicated.

Logical or is possible (i've done it) but you'll have to write these lines. (I merge the ability and weapon element if weapon strike or weapon range is checked in patcher)

Y signed is possible, i have not messed with it, but i would start by changing the loading and maths instructions (suppressing the "u")

Switching to unarmed formula ? you mean just the XA and YA ? or a totally different formula ? If you want to alter XA and YA, i think you'll have to write a custom routine instead of using the base XA one. For a different formula, you can jump directly into it and then exit 0x2d directly as soon as you return from the jal.
  • Modding version: PSX

nitwit

I noticed that these 4 formulas are all very similar, and they're adjacent to one another.
https://ffhacktics.com/wiki/2D_Dmg_(PA*(WP%2BY))_100%25_Status
https://ffhacktics.com/wiki/2E_Equipped:Break_Dmg_(PA*WP)
https://ffhacktics.com/wiki/2F_AbsMP_(PA*WP)
https://ffhacktics.com/wiki/30_AbsHP_(PA*WP)

I want to consolidate them into this:
(Absorb OR Damage) (HP OR MP) as (Equipped Weapon's Formula XA * (Equipped Weapon's Formula YA +- Y))

Mostly Unrelated Desired XA & YA Changes
Warrior Weapons: PA * WP
Warrior Random Weapons: (PA + (1 ... PA)) / 2 * WP
Mage Weapons: MA * WP
Balanced Weapons: (PA + MA) / 2 * WP
Weakling Weapons: WP * WP
Unarmed without Martial Arts: base PA * (base PA ... current PA) * ((Brave / 2) + 50) / 100
Unarmed with Martial Arts: base PA * greatest(base PA, current PA) * ((Brave / 4) + 75) / 100



If the defaults are:
Damage
HP

Then I need a bit each to switch between:
Damage or Absorb
HP or MP

How does the AI know which Steal, Break, or Mighty Sword skill to use? I assume the AI is hardcoded to check certain skill slots against equipped weapon slots in their targets? If they do, then I need to know where that is so I can make changes in the AI routine as well as in the routines mentioned so far in this thread.

If I'm correct (and the AI gear modification formula hardcoding is both known and feasible to change to reflect the de-hardcoding here) then I could redo the Steal, Break, and Mighty Sword hardcoding subroutine so it also de-hardcodes Steal and Break. If that's the case, then I need to account for reasonable X values in formulas 25 and 26, which need something around 63 or so.

https://ffhacktics.com/wiki/25_Equipped:_Break_Hit_(PA%2BWP%2BX)%25
https://ffhacktics.com/wiki/26_Equipped:_Steal_Hit_(SP%2BX)%25
https://ffhacktics.com/wiki/Steal/Break/Might_Sword_Hard_Coding

In such a case, I should put the flags for "Absorb instead of damage" and "MP instead of HP" in the two most significant bits in the skill X value, which should leave me with a range of 0 to 63.

Skill X Value Bits
Bit 7: Absorb instead of damage
Bit 6: MP instead of HP
Bit 5: ?00000
Bit 4: 0?0000
Bit 3: 00?000
Bit 2: 000?00
Bit 1: 0000?0
Bit 0: 00000?

If I want to squeeze another 1 in there I could make it consider the range to be 1 to 64, by adding 1 to that value after extracting the flag information from it. Not sure how make the predicted hit rates or AI use them correctly though.

Minor Shitpost:
I could also go full hog and have it multiply that range bits in Skill X Value 2 or something, to free up maybe one or two more bits, but that's not enough to consolidate the other two formulas above: 2B (Knight's Stat Breaks) and 2C (Knight's MP Break).

If I did consolidate them, they need need 4 more bits for PA, MA, SP, and % damage flags. That leaves me with 2 bits and 3 values (0, 1, 2 - or maybe 1, 2, and 3 if I consider the floor to be 1).

I could multiply those by something and set a higher floor value, but that's probably not worth the effort when I could just consolidate formulas 2B and 2C into their own things and add CT to the stats they can break, then reference that in the Ark Knight's damaging stat breaks.

I also don't understand enough ASM to know how I would reflect the floor and multiplication shenanigans in predicted hit rates for the player or the AI, or how to unscrew the AI hardcoding that exists for formulas 2B and 2C.



That leaves the skill Y value. I need to fit the following flags for gear breaks in there, and have enough room left over for a signed value.

Skill Y Value Bits
Bit 7: Weapon
Bit 6: Shield
Bit 5: Head
Bit 4: Armor
Bit 3: Accessory
Bit 2: Sign flag
Bit 1: 2
Bit 0: 1

With 5 slots to break, that leaves me with bits 0, 1, and 2. With bit 2 as the sign flag, that leaves me with bits 1 and 2, which IIRC means I have a Y value range of -4 to +3. Homework below.

A: Sign Flag
B: Bit 1
C: Bit 0

ABC : Value
000 :  0
001 : +1
010 : +2
011 : +3
100 : -1
101 : -2
110 : -3
111 : -4

Am I correct in thinking that I can unpack the de-hardcoding flags with branches, then bit shift and bitwise logical the values into a proper signed value in a register somewhere?



That leaves me with correctly indexing each formula, if necessary.

https://ffhacktics.com/wiki/Routine_Locations

Looking at the routine locations, the formulas are not in order so I don't think the addresses in the Formula (Pointer) Table need to be increase in size.

I honestly don't know what I'm talking about here, I'm used to dealing with pointer tables of variable length data where the game apparently grabs pointer N+1 to delimit pointer N, and as a result every entry in such a pointer table must point to an address after it, leading to lots of wasted space for entries with only null data and duplicate entries.

Is that the case with the Formula Table? Or can I point to formulas all over the place, in any order?

https://ffhacktics.com/wiki/Formula_Table

If I can repoint formulas willy-nilly, or point multiple formulas to the same starting address, then I can save some space by doing the latter. If not, then I'll need to pad the start of the consolidated formulas with an nop for each consolidated formula, which wastes the least amount of space.


EDIT
After looking at it, they are not in order so they can probably point anywhere, in any order.

https://ffhacktics.com/wiki/Formula_Table
42: 0x0018F718 (0x00128718) -> 0x8018A17C
43: 0x0018F71C (0x0012871C) -> 0x80186E28 //lower than previous value
44: 0x0018F720 (0x00128720) -> 0x80186E54
45: 0x0018F724 (0x00128724) -> 0x80186E78
46: 0x0018F728 (0x00128728) -> 0x8018A218 //higher than previous value
47: 0x0018F72C (0x0012872C) -> 0x8018A220
/EDIT



Other stuff:
I saw a hack in an XML file that replaced the Holy Sword skill unsigned Y addition with a signed Y, creating the +-, so I know that works. Disregard that question, I guess (though obviously you're correct, that's how it was done in that hack).

Would I need to change some AI hardcoding if weapon and skill elements were logical OR'd? Or can I just do it and have the AI be smart and action preview be correct?

And on second thought I don't need to make it default to unarmed XA and YA if require sword is changed to require direct or lunging weapon. I want to avoid using ranged weapons with swordskills is the animation would look goofy - imagine Agrias doing the sword strike animation but holding her bow or harp aloft, and then an astral sword poking out of the ground at her target. I dunno, maybe it will use the correct animation with a bow where she fires an arrow and then the magic sword pokes the enemy.

Making and holding a weapon strike animation with any direct weapon - or her fist - looks fine.

What AI and other existing flags could I use instead of the skill X and Y values?

And, as always: What hardcoding does the AI have in regard to require sword? What did the existing hacks change, and do they have issues?



Conclusion:
My goal is to create a bunch of formulas in the laziest way possible that accomplish my mod goals, while avoiding a total refactor of the relevant parts of the codebase. As such, I want to do all the thinking beforehand to work out any kinks, and in public so other people can critique my ideas to improve them.

I want to avoid testing as much as I can because it's an endless black hole, even with a good test suite (every formula easily accessible on a few skillsets, and a RAM location reference for quick editing. I assume I must do my own testing, which is why I loathe it. Though if the general policy is to not test your hacks; well that works too.

So please tell me if I missed anything, what's unknown, and where to start searching.

Orkney

Ho man ! That's a ton of details...

I can't answer all of this point at once. And even if i would to, i don't have the required skills.

I can't answer you for the AI, i've no experience with it yet. (All i know is that when i write a hack that adds a support ability adding fire to weapon element, AI was smart enough to figure it).

If you want to spare some bit you can use the ability AI flags too.
Affect MP --> MP damage
Affect stat to point to the stat section (unequip for break/steal)
Undead reverse for absorption (but you'll have to implement real undead absorption for the AI sake)
Target enemy for damage, target ally for heal.

Your signed Y from -4 to 3 is more easy if you using a bitmask then using sll 0x05 then sra 0x05 (100 is - 4 and 111 is -1)

For the break/steal info, you can save some bit too, no need to use one bit per equipment part since you won't be able to break/steal more than one slot per action (not without writing a lot a stuff i guess). so bit 7 alone is weapon, bit 6 alone is armor, bit 7 and bit 6 is shield etc...
  • Modding version: PSX

nitwit

Quote from: Orkney on August 03, 2022, 05:10:10 pmI can't answer you for the AI, i've no experience with it yet. (All i know is that when i write a hack that adds a support ability adding fire to weapon element, AI was smart enough to figure it).
That's reassuring. If there's a reasonable chance it will Just Work, I'm less likely to be demoralized.

Quote from: Orkney on August 03, 2022, 05:10:10 pmIf you want to spare some bit you can use the ability AI flags too.
Affect MP --> MP damage
Affect stat to point to the stat section (unequip for break/steal)
Undead reverse for absorption (but you'll have to implement real undead absorption for the AI sake)
Target enemy for damage, target ally for heal.
That's what I was looking for, thank you.

Quote from: Orkney on August 03, 2022, 05:10:10 pmYour signed Y from -4 to 3 is more easy if you using a bitmask then using sll 0x05 then sra 0x05 (100 is - 4 and 111 is -1)
I remembered that on the toilet yesterday but forgot to edit it in. I always manage to screw up something.

Quote from: Orkney on August 03, 2022, 05:10:10 pmFor the break/steal info, you can save some bit too, no need to use one bit per equipment part since you won't be able to break/steal more than one slot per action (not without writing a lot a stuff i guess). so bit 7 alone is weapon, bit 6 alone is armor, bit 7 and bit 6 is shield etc...
Treat it like a range (0 to 4), basically? I could accomplish that by bit shifting it and masking out the parts I don't need, though I don't know if that would be simpler in ASM than dealing with bits and branches.

Orkney


Quote from: nitwit on August 03, 2022, 09:07:23 pmTreat it like a range (0 to 4), basically? I could accomplish that by bit shifting it and masking out the parts I don't need, though I don't know if that would be simpler in ASM than dealing with bits and branches.

You might achieve that, but still the weapon and the shield section are peculiar. Shield section check the 2 hands, weapon section too, but it compare the two weapons to choose the one to break... So in the end you'll have to deal with some branches i guess.
  • Modding version: PSX

nitwit

Quote from: Orkney on August 04, 2022, 09:25:56 amYou might achieve that, but still the weapon and the shield section are peculiar. Shield section check the 2 hands, weapon section too, but it compare the two weapons to choose the one to break... So in the end you'll have to deal with some branches i guess.
Thanks for the heads up. Is the juice worth the squeeze in your opinion? Should I go through the trouble of making weapon break smart, or should I make it simple?



Here's the new formula.
Hit Rate =  Add(PA &| MA &| SP &| WP &| X)%
Optional Effect = (Break | Steal) (Gear | Stat) //May disable Status Infliction
Optional Damage|Absorb HP|MP = (XA * (YA +- Y))
Status OK
Element OK

Formulas Consolidated:
25 Equipped: Break Hit_(PA+WP+X)%
26 Equipped: Steal Hit_(SP+X)%
27 StealGil_(CasLVL*SP) Hit_(SP+X)%
28 StealExp_(Lowest of TarCurExp & SP+Y) Hit_(SP+X)%
2B Hit_(PA+Y)% // -PA/MA/SP_(X)
2D Dmg_(PA*(WP+Y)) 100% Status
2E Equipped:Break Dmg_(PA*WP)
2F AbsMP_(PA*WP)
30 AbsHP_(PA*WP)
55 -PA_(Y) Hit_(MA+X)%
56 -MA_(Y) Hit_(MA+X)%
62 -Brave_(Y) Hit_(MA+X)%

Formulas Partially Consolidated:
2A Hit_(MA+X)% // AffectBraveOrFaith(Y)
3D Hit_(MA+X)%
3F Hit_(SP+X)%
50 Hit_(MA+X)%

I would need to split the flags for the stat that powers it from the Def/MDef and probably Shell that defends against it somehow to consolidate these while de-hardcoding them.

Here's the new bit allocation scheme. All values are "if set" unless said otherwise.

Unknown Flags Below...
- Force Self Target : Add SP to X value to determine hit rate. Adds to others affecting hit rate.
- Learn On Hit      : Replace Damage with Absorb for HP & MP. HP or MP damage must be enabled.
- Only Hits Enemies : Deal no damage if additional effect hits, and if it can be used.
- Use Weapon Range  : Replace Breaks with Steals.

AI Flags
- Physical AI Bit  : Add PA to X value to determine hit rate. Adds to others affecting hit rate.
- Magical AI Bit  : Add MA to X value to determine hit rate. Adds to others affecting hit rate.
- Weapon Range Bit : Add WP to X value to determine hit rate if weapon is equipped, otherwise add PA. Adds to others affecting hit rate.
- Affect HP AI Bit : Enable HP damage or absorb as (XA * (YA +- Y)). Doesn't stack with MP damage. Otherwise no HP damage.
- Affect MP AI Bit : Enable MP damage or absorb as (XA * (YA +- Y)). Doesn't stack with HP damage. Otherwise no MP damage.

Skill X Value
Range 0 - 255: Base Hit Rate before target evasion is considered (assuming Evadable flag is set)

Skill Y Value Bits
Upper Nybble - Break/Steal effects - damage unaffected by elements
F: 1111 : Break/Steal Brave(?)
E: 1110 : Break/Steal Faith(?)
D: 1101 : Break/Steal CT(?)    //Steal CT requires a charge time because post-turn CT settings are hardcoded IIRC ; values > 100 are wasted.
C: 1100 : Break/Steal Gil(?)

B: 1011 : Break/Steal EXP(?)
A: 1010 : Break/Steal SP(?)
9: 1001 : Break/Steal PA(?)
8: 1000 : Break/Steal MA(?)

7: 0111 : Break/Steal Level(?)
6: 0110 : Break/Steal Random Equipped Item //"Break: Random" is Minotaur, "Steal: Random" is Juravis monster families' skill.
5: 0101 : Break/Steal Weapon //Check left, then right if dual wielding; also check for double hand
4: 0100 : Break/Steal Shield //Check both hand slots

3: 0011 : Break/Steal Helm
2: 0010 : Break/Steal Armor
1: 0001 : Break/Steal Accessory
0: 0000 : No additional effect. Enables status infliction byte as vanilla. Any non-zero value here should assume it sets them.

Lower Nybble - Unsigned values are Y value hp/mp damage/absorb parameters, possibly also Break/Steal effects parameters somehow(?)
F: 1111 : - 1
E: 1110 : - 2
D: 1101 : - 3
C: 1100 : - 4
B: 1011 : - 5
A: 1010 : - 6
9: 1001 : - 7
8: 1000 : - 8
7: 0111 : + 7
6: 0110 : + 6
5: 0101 : + 5
4: 0100 : + 4
3: 0011 : + 3
2: 0010 : + 2
1: 0001 : + 1
0: 0000 : + 0



I'm not sure how I can set the Stat Break/Steal parameters and have them play nice with skill Y value parameters. Maybe I could turn another unused bit somewhere into a "Consider Skill Y Value Lower Nybble as Unsigned for Stat Breaks". I'll probably have to steal Skill X Value bit 7 for that. Or maybe I could make it implicit.

Maybe I could use the Status Effect byte to determine the amount of stat stolen/broken, since IIRC you can't inflict a traditional debuff like Silence and break/steal anything at the same time. Am I correct? This piece of insanity hinges on that. If I can't inflict both without significant modifications to how the game processes status changes then I am on the right track. If I can inflict both then I should reduce the scope of this hack and split it in two or more pieces to preserve that functionality.

As it stands I could get use from making the bit-flagged WP, SP, PA, and MA modifications to Skill X Value also affect stat breaks/steals, divided by something also reasonable. See below.

Brave, Faith, CT,
Caster (absoluteValue(Y lower nybble) +| WP +| SP +| PA +| MA; as per additions to X value hit rate flags above) / (number of X value additions)

SP, PA, MA:
Caster (absoluteValue(Y lower nybble) +| WP +| SP +| PA +| MA; as per additions to X value hit rate flags above) / (4 + number of X value additions)

Gil:
(Target Level * Caster (unsigned(Y lower nybble) +| WP +| SP +| PA +| MA; as per additions to X value hit rate flags above))

Exp:
(Lowest of TarCurExp & Caster (unsigned(Y lower nybble) +| WP +| SP +| PA +| MA; as per additions to X value hit rate flags above))



I want to limit SP, PA, and MA breaks to 7/10 of a character's base values, integer division. Same for SP, PA, and MA buffs (including those from steals) to 13/10 of a character's base values, also integer division. This is to prevent Yell/Accumulate spam, and stat break spam for JP grinding on hapless enemy chocobos.

Most of the weirder effects would go on monsters, zodiacs, demons, and unique enemy NPCs.

Edit

I forgot to add, I want to limit healing from absorbs to the amount of HP or MP lost by the target. If they have no HP or MP to lose - because you killed them and then some, or they ran out of MP - then you shouldn't get more than they have to give.

edit

I figured out how to use MassHexASM, still putting all the routines, their addresses, and my notes/plans in one place. Here it is in case my computer dies.