Continuing,
The soGenerateArticleManageModule contains an instance pool that holds reserved memory for all the article instances allowed for that character.
The regular soGenerateArticleManageModule with 3 aura spheres is on the left and our goal which has 8 aura spheres is to the right. To get from 3 to 8, we need to add space for 5 wnInstanceHolder<wnLucarioAuraBall> objects and 5 soInstancePoolSub<... wnLucarioAuraBall> objects.
Each soInstancePoolSub regardless of type will always be 0x4 bytes. The wnInstanceHolder objects vary in size, but you can determine their size by looking into the soGenerateArticleManageModule constructor:
Here, we can observe 3 wnInstanceHolder<wnLucarioAuraBall> constructors calls into 0x84DC. We can subtract two of the offsets passed into the constructors to get the wnInstanceHolder<wnLucarioAuraBall> size.
instanceHolderSize = 0x2060 - 0x0C = 0x2054
From here, we can calculate the size increase for adding 5 aura spheres:
sizeIncrease = 5 * (instanceHolderSize + instancePoolSubSize) = 0xA1B8
In order to continue, we also need to determine the original size of the soGenerateArticleManageModule. Because we are moving it to a completely new location, we can't use the old allocated memory for it.
Determining the size of the soGenerateArticleManageModule can easily be done by subtracting the location of the following module - soEffectModule - from the location of the soGenerateArticleManageModule. Looking a bit further down from where the soGenerateArticleManageModule constructor is called:
We find that the soEffectModule is constructed at [soInstanceManagerFixedSimple + 0xDC40]. This means the size of the soGenerateArticleManageModule is:
moduleSize = 0xDC40 - 0x5C24 = 0x801C
From here, we can calculate the new memory allocation size of our character.
newAllocSize = oldAllocSize + moduleSize + sizeIncrease = 0x2271C
We've increase the size of our character's memory space, and repositioned our soGenerateArticleManageModule into its new location, but we still need to add those 5 extra wnInstanceHolders and InstancePoolSubs. Let's look into the module soGenerateArticleManageModule constructor again.
As it turns out, there's enough repeated code here to merit a loop. And with a loop, we can control however many iterations we want - be it 3 or 8. Doing a little bit of refactoring gets us:
s[1] 0x844C:
li r27, 0x00000000
addi r3, r28, 0x1E5C
mulli r4, r27, 0x2054
add r3, r3, r4
mr r4, r31
bl s[1] 0x84DC
addi r27, r27, 1
cmpwi r27, 8
blt+ -0x1C
lis r3, 0x0000
addi r3, r3, 0x0000
stw r3, 0(r28)
If there weren't enough space to add duplicate instance holders - or if we were adding a completely new article, then we would need to use another hook to do the additional work required. Neither case is very much of an issue.
As was the case when we moved the soGenerateArticleManageModule, if we update the constructors, we also need to update the destructors. In this case we need to update the offsets inside each soInstancePoolSub<... wnLucarioAuraBall>Method[0][0] to reflect the new offsets. There's a bit more to it than that, but I'll save the details on it for later.
Now that we have 8 instance holders to store our 8 Aura Spheres, we need to change the character's ftArticleMediator to actually allow us to make those 8. Before continuing, let's take a look at the OpenSA documentation on ftArticleMediatorImpl
http://opensa.dantarion.com/wiki/SoArticleMediatorImplThere are 4 methods in this class that need to be changed: GetInstanceCount, GetInstanceCap, and the two versions of ClearInstances. These are just numeric caps on the number of instances for the aura ball type that we need to change from 3 to 8.
s[1] 0xAD24 cmpwi r31, 8 // ClearInstances
s[1] 0xAF84 cmpwi r31, 8 // GetInstanceCount
s[1] 0xB188 li r3, 8 // GetInstanceCap
s[1] 0xB534 cmpwi r31, 8 // ClearInstances (All)
Finally, we need to change the instantiation function itself that actually puts an instance of the article into the instance holder. This is the GenerateArticle method of the ftArticleMediator. Changing it it more or less the same process as how we changed the construction process for the instance holders - that is, we replace hard coded references to each instance holder with a loop that iterates over them.
And that's about it. I g
ed over a bit more than I would have liked, and I think I may have missed a few things all together, but I'll leave a file here with all the changes I made to the Lucario module.
ChangelistI've also made a near complete memory map of ftLucario in-game. It may come in handy for future references:
============== ftLucario ===================
POS: 0x0000
SIZE: 0x10548
0x00000 p_nodeName // "LUCARIO"
0x00004 Prev Node
0x00008 Next Node
0x0000C Prev Node
0x00010 Next Node
0x00014 Prev Node
0x00018 Next Node
0x0003C p_classMTable // ftLucario
0x00040 p_ancestorMTable
0x00048 p_ancestorMTable
0x00054 p_ancestorMTable
0x00060 p_soModuleAccessor
0x00064 p_ancestorMTable
0x00070 p_ancestorMTable
0x0007C p_ancestorMTable
0x00088 p_ancestorMTable
0x00094 p_ancestorMTable
0x000A0 p_ancestorMTable
0x000AC p_ancestorMTable
0x000B8 p_ancestorMTable
0x000C4 p_ancestorMTable
0x000D0 p_ancestorMTable
0x000DC p_ancestorMTable
0x000E8 p_ancestorMTable
0x000F4 p_ancestorMTable
0x00100 p_ancestorMTable
0x00110 Fighter ID
0x0013C ftOutsideEventPresenter
0x00194 INSTANCE MANAGER DATA
0x0FF98 ftCancelModuleImpl
0x0FFD4 ftVirtualNodeMatrixPoolImpl
0x1048C GIMMICK DATA
0x10524 ?
0x10528 0x00000001
0x1052C ?
0x10530 0x00000001
0x10534 soArrayContractibleTable<const soStatusData>
0x10544 ?
===== INSTANCE MANAGER DATA ======
POS: 0x0194
SIZE: 0xFE04
0x0000 soInstanceManagerFixedSimple
0x0014 soArrayVector<soInstanceUnit<soEventUnit *>, 19>
0x0020 soInstanceUnit<...> [00]
0x0028 soInstanceUnit<...> [01]
...
0x00A8 soInstanceUnit<...> [17]
0x00B0 soInstanceUnit<...> [18]
0x00B8 soEventManageModuleImpl
0x09D0 soModuleAccessor
0x0AB0 soHeapModuleImpl
0x0AC8 ftLucarioParamCustomizeModule
0x115C soResourceModuleImpl
0x1180 soModelModuleImpl
0x1440 soMotionModuleImpl
0x17D4 soPostureModuleImpl
0x1888 soGroundModuleImpl
0x1930 soSituationModuleImpl
0x196C soTeamModuleImpl
0x19E0 soCollisionAttackModuleImpl
0x1FFC ===== [BASE] soCollisionAttackModuleImpl
0x209C soCollisionHitModuleImpl
0x2990 ===== [BASE]soCollisionHitModuleImpl
0x29F8 soCollisionShieldModuleImpl
0x2D4C ===== [BASE]soCollisionShieldModuleImpl
0x2DA0 soCollisionShieldModuleImpl
0x37B4 ===== [BASE]soCollisionShieldModuleImpl
0x380C soCollisionCatchModuleImpl
0x3988 ===== [BASE]soCollisionCatchModuleImpl
0x3A70 soDamageModuleActor
0x3B1C ===== [BASE]soDamageModuleActor
0x3C20 soCatchModuleImpl
0x3C84 soCaptureModuleImpl
0x3CB8 ftStopModuleImpl
0x3CDC soTurnModuleImpl
0x3D14 soShakeModuleImpl
0x3D90 ===== [BASE]soShakeModuleImpl
0x3DC0 ===== [BASE]soSoundModuleImpl
0x3E1C soLinkModuleImpl
0x3F60 ===== [BASE]soLinkModuleImpl
0x3FB4 soVisibilityModuleImpl
0x3FE4 ftControllerModuleImpl
0x459C ===== [BASE]ftControllerModuleImpl
0x4708 soCameraModuleImpl
0x4758 ===== [BASE]soCameraModuleSimple
0x477C soWorkManageModuleImpl
0x47B0 soAnimCmdModuleImpl
0x48A4 soStatusModuleImpl
0x5688 ===== [BASE]soStatusModuleImpl
0x5738 soKineticModuleGenericImpl
0x5C00 ===== [BASE]soGeneralWorkSimple
0x5C24 soGenerateArticleManageModuleImpl
0xDC04 ===== [BASE]soGenerateArticleManageModuleImpl
0xDC40 soEffectModuleImpl
0xDCAC ===== [BASE]soEffectModuleImpl
0xDDE4 ftComboModuleImpl
0xDE14 ftAreaModuleImpl
0xDE24 ===== [BASE]ftAreaModuleImpl
0xE188 soPhysicsModuleImpl
0xE204 ===== [BASE]soPhysicsModuleImpl
0xE24C soSlopeModuleImpl
0xE2CC soShadowModuleImpl
0xE314 soItemManageModuleImpl
0xE3B8 ===== [BASE]nhsoItemManageModuleImpl
0xE424 soColorBlendModuleImpl
0xE578 soJostleModuleImpl
0xE5C4 ftAbnormalModuleImpl
0xE62C soSlowModuleImpl
0xE668 ftGlowModuleImpl
0xE7E8 soArrayContractibleTable<const soStatusData>
0xE7F8 ANIM CMD DATA
============== ANIM CMD DATA =============
POS: 0xE7F8
SIZE: 0x160C
0x0000 soArrayVector<const acAnimCmdConv *, 293>
0x000C acAnimCmdConv[000]
0x0010 acAnimCmdConv[001]
...
0x049C acAnimCmdConv[292]
0x04A0 soArrayVector<const acAnimCmdConv *, 293>
0x04AC acAnimCmdConv[000]
0x04B0 acAnimCmdConv[001]
...
0x093C acAnimCmdConv[292]
0x0944 soArrayVector<acCmdInterpreterStackData, 8>
0x0950 acCmdInterpreterStackData [0]
0x0964 acCmdInterpreterStackData [1]
...
0x09DC acCmdInterpreterStackData [7]
0x09F0 soAnimCmdAddressPackArraySeparate
0x0A0C soAnimCmdInterpreter
0x0A5C soArrayContractibleTable<const acAnimCmdConv *>
0x0B88 soArrayContractibleTable<const acAnimCmdConv *>
0x0CB4 soArrayContractibleTable<const acAnimCmdConv *>
0x0DE0 soArrayContractibleTable<const acAnimCmdConv *>
0x0F0C soArrayContractibleTable<const acAnimCmdConv *>
0x1038 soArrayContractibleTable<const acAnimCmdConv *>
0x1164 soArrayContractibleTable<const acAnimCmdConv *>
0x1290 soArrayContractibleTable<const acAnimCmdConv *>
0x13BC soAnimCmdInterpreter
0x14E0 soArrayContractibleTable<const acAnimCmdConv *>
========= GIMMICK DATA =========
POS: 0x1048C
SIZE: 0x00098
0x00 ftStatusGimmickUniqProcessPoolImpl
0x08 ftStatusUniqProcessGimmickBarrel
0x20 ftStatusUniqProcessGimmickDoor
0x34 ftStatusUniqProcessGimmickCatapult
0x48 ftStatusUniqProcessGimmickLadder
0x5C ftStatusUniqProcessGimmickSpring
0x70 ftStatusUniqProcessGimmickTruck
0x84 ftStatusUniqProcessGimmickEaten
========= ARTICLE MEDIATOR MODULE ========
POS: 0x5C24
SIZE: 0x801C
============
0x0000: soArrayVector<soArticle *, 5>
0x0020: soArrayVector<soArticleEventObserver *, 5>
0x007C: soArticleMediator<...> T1
0x0080: soArticleMediator<...> T2
0x0084: == INSTANCE POOL ===
0x0084: == LINE HIERARCHY ==
0x0084: Decl
0x0088: soInstancePoolSub<...>
0x008C: x
0x0090: == INSTANCE HOLDER DATA == [0x1E40]
0x0090: soInstanceHolder<...>.decl
0x0094: ? (0x8508)
0x1ED0: soInstancePoolSub<..., 3>
0x1ED4: soInstancePoolSub<..., 2>
0x1ED8: soInstancePoolSub<..., 1>
0x1EDC: x
0x1EE0: == INSTANCE HOLDER DATA == [0x2054]
0x1EE4: soInstanceHolder<...>.decl
0x3F34: == INSTANCE HOLDER DATA == [0x2054]
0x3F38: soInstanceHolder<...>.decl
0x5F88: == INSTANCE HOLDER DATA == [0x2054]
0x5F8C: soInstanceHolder<...>.decl
0x7FDC: byte Flag (END OF soArticleMediator)
0x7FE0: == ARTICLE MODULE BASE == [0x3C]
It also comes with a visual! Buy 1 for the low price of 0 dollars and get the second 1 50% off!
Buy Now!I hope this helps anyone looking to get serious with modules. Let me know if there's any questions.
So how are Article Floating Points handled?
And a bit unrelated, but which part of the rel tells the character to use an animation for their DamageFace Sub Action? I'd like to get a character of mine to use a tail animation (like Charizard's tail and wings) when being thrown, but I just couldn't figure it out. So I'm assuming it's module-related.
As Sammi-Husky mentioned, article floating point parameters are mostly to do with the ParamAccessor objects. That being said, I haven't done too much research into them either, so Sammi-Husky probably knows more than me at this point.
As for the damage face animations, I haven't touched on those at all, so I can't really say I can help you there. You would probably be correct in assuming that they are managed by the character module though.