Final Fantasy Hacktics

Modding => PSX FFT Hacking => Topic started by: Glain on March 27, 2011, 12:57:58 pm

Title: (ASM) Gear level - Now can use story progression instead of level/0x0169
Post by: Glain on March 27, 2011, 12:57:58 pm

EDIT: I'm putting the Gear Level ASM patches here for convenience. Also, I'm attaching them as an XML at the bottom of this post.


  <Patch name="Random unit gear based on story progression">
    <Description>
      Random unit gear based on story progression
    </Description>
    <Location file="BATTLE_BIN" offset="F59BC"> 
      FF00A530
      E4FFBD27
      1400BFAF
      0400ADAF
      0800A5AF
      0C00A6AF
      1000A7AF
      64ED040C
      6F000434
      0100452C
      21704500
      1400BF8F
      0400AD8F
      0800A58F
      0C00A68F
      1000A78F
      1C00BD27
      20000234
      0800E003
      00000000
    </Location>
    <Location file="SCUS_942_21" offset="4D49C">
      9400BFAF
      21688000
      6F72050C
      20000234
    </Location>
    <Location file="SCUS_942_21" offset="4D52C">
      00000000
    </Location>
    <Location file="SCUS_942_21" offset="4D5D8">
      0A00A390
    </Location>
  </Patch>



  <Patch name="Random unit equipment more selective">
    <Description>
   Random unit equipment will now be more selective
        of secondary item types. (You shouldn't see
        knights in Chapter 2 using linen robes)
    </Description>
    <Location file="SCUS_942_21" offset="4D53C">
      00000F34
    </Location>
    <Location file="SCUS_942_21" offset="4D5F0">
      03000214
      01000F34
      01002925
      00000000
      21606000
      21182001
    </Location>
    <Location file="SCUS_942_21" offset="4D628">
      01003025
      0C000F10
    </Location>
  </Patch>


-----

Recently I've been thinking about an idea for enemy unit levels scaling with the party and how that affects random equipment. By default, that means enemy gear will scale up with the player's level, meaning that after a certain point, the player is going to be outmatched by equipment he can't obtain except through stealing. On the other side of the coin, if the player manages to level very little and/or use degenerators, he can keep the shop equipment ahead of the enemy equipment and gain an advantage that way. My objective: fix the gear of enemies at a certain tier, depending on the battle... without fixing the equipment itself.

Using the pSX debugger, I was able to find where random equipment was being determined. It happens while you're placing your units before the battle, and there is a subroutine at 0x05cc98 in RAM (0x04d498 in SCUS_942.21) that chooses random equipment. It appears to loop through a list of items and check them against the current unit's (r4,r13) level based on the given equipment type (r5). The unit offset relating to the current unit is loaded into the register r13, and sure enough, we find this:


0x0005cd2c: lbu r14,0x0022(r13)


Loading the current unit's level into a register (r14)! r14 is then used for a check in the loop, so this is the number being used to determine the quality of enemy equipment.

My first thought was that this could be replaced with something relating to the shop availability for items, using some kind of multiplier. ([Shop availability variable] * 2? 3? 2.5?) Once again, I used the pSX debugger and went into a shop, to trace to where a check is made against this number. At first, I find this:


0x001251c0: lw r8,0x0028(r29)


I'm testing at the start of chapter 4, and r8 here is loaded with 0x0d (13). That would be... correct! That's the number it'd check against the item's shop availability, and I can see it doing this in the code:


0x001251bc: lbu r2,0x2ec2(r1)     Load in the item's shop availability number... (pSX debugger breakpoint)
0x001251c0: lw r8,0x0028(r29)               Load the player's shop availability (based on story progression) from the stack
0x001251c4: nop
0x001251c8: slt r2,r8,r2     If the player's number is less than the item's, the item is too high quality, so...
0x001251cc: bne r2,r0,0x00125238            ...skip adding it to the shop item list for purchase.


But, well, we loaded r8's value (the player's number) from the stack somewhere, it looks like. How did it get there?


0x00125128: jal 0x000ef1a8
(...)
0x00125134: sw r2,0x0028(r29)


r2 changed to the correct value (0x0d) after the call to subroutine 0x0ef1a8.

At least under some circumstances, this subroutine at 0x0ef1a8 seems to load r2 with the variable I wanted... but unfortunately, we're in a shop, so probably we're in WORLD.BIN. When I tried to call this subroutine in a battle, the memory at 0x0ef1a8 looked completely different... the subroutine wasn't even loaded into memory anymore! (Leading to fantastic results). Besides, I wasn't sure of the circumstances under which that subroutine would return the storyline variable (it uses a billion registers and is also recursive, so it's a barrel of fun to try to trace through).

So my next thought... forgetting subroutine 0x0ef1a8... was I overcomplicating things? We already have an offset to the current unit in subroutine 0x05cc98 (where the random equipment is loaded). So what other offsets can I use? Is there some other property of the current unit I can use to check for what the equipment quality should be? Looking at the formula hacking reference, there's basically nothing. I can't use HP, MP, Brave, Faith, etc. So what to do?

Venture into the unknown. In the ENTD in FFTPatcher, there are unknown fields for each unit, in that little box down in the bottom left. Some of them seem to be AI flags, but what of the one down in the very bottom left (last row, leftmost)? I couldn't find a single instance of it being anything other than zero (though I only checked the story battles). So if it's unused... why not use it? This number could function as a "give the enemy gear as if his level was X" variable, without actually changing the unit's level. I located it in RAM at unit offset 0x0169.

(http://img101.imageshack.us/img101/6051/fftpatcherunknown.jpg)

This is the number I'm talking about. In my patch, I've made Gafgarion here Party Level + 5, but his "gear level" is 0x12 = 18, which is his default level for this battle. (Gate of Lionel)

We could simply change the offset checked by subroutine 0x05cc98 to use 0x0169 instead of 0x0022 (level). While that's viable, we'd have to make sure we changed every single instance of this variable in the ENTD. What I really want is for it use 0x0169 unless it's zero, and otherwise use 0x0022.

Well...okay, I can write a block of ASM that does that. Here is the biggest oustanding problem with this whole thing though... where to put it? It just so happened that in my patch, I had ASM hacked formula 41 into something else, and I happened to have a bunch of nop's at the end of it. I used that and wrote my own subroutine at 0x0018a164. (I'll get to this shortly)

If we look at subroutine 0x05cc98, we have to rewrite a little bit to make our own subroutine call work. I want to make the call to my subroutine near the beginning of this one, but the return address wasn't saved yet. Originally, we have:


0x0005cc9c: 00806821 addu r13,r4,r0 r13 = r4 (reference to unit)
0x0005cca0: 30a500ff andi r5,r5,0xff r5 = (Lowest byte of r5)
0x0005cca4: 34020020 ori r2,r0,0x0020 r2 = 0x0020
0x0005cca8: afbf0094 sw r31,0x0094(r29)         (Save return address)


I changed mine to this:


0x0005cc9c: afbf0094 sw r31,0x0094(r29)         (Save return address)
0x0005cca0: 00806821 addu r13,r4,r0 r13 = r4 (reference to unit)
0x0005cca4: 0c062859 jal 0x0018a164 Call my subroutine!
0x0005cca8: 34020020 ori r2,r0,0x0020 r2 = 0x0020


I had to cut out the andi statement, but I execute that in my subroutine, as shown here:


0x0018a164: 91ae0169 lbu r14,0x0169(r13)      Load in unit offset 0x0169
0x0018a168: 140e0002 bne r14,r0,0x0018a174      If it's not zero, use it, so jump to the return statement
0x0018a16c: 30a500ff andi r5,r5,0xff      (Statement from calling subroutine that I cut and wanted to execute here)
0x0018a170: 91ae0022 lbu r14,0x0022(r13)      Otherwise, load in the unit's level, and
0x0018a174: 03e00008 jr r31      Return
0x0018a178: 00000000 nop


Now r14 (the number used to check quality of equipment) already has what I wanted in it. The only thing left to do is stop it from trying to load in the unit offset later in the subroutine. In other words, changing this:


0x0005cd2c: 91ae0022 lbu r14,0x0022(r13)


to this:


0x0005cd2c: 00000000 nop


And we've done it! Random enemy equipment would now load based on the "gear level" you give them at unit offset 0x0169, analogous to what their gear would be if they were at that level... without changing the actual level. Thus, the level can scale with the party, but the gear can remain fixed at a certain tier.

I implemented this in my own patch swimmingly enough (after lots of debugging, of course)! It's pretty cool, but making it into a general patch anyone can use presents some difficulties. The biggest problems at this point, as I see it, are:

(1) Where do I put the new subroutine? (I can't just assume everyone has a bunch of nop's at the end of their formula 41)
(2) What if unit offset 0x0169 is used by something and now it won't work the same way? (Doubtful, they're all zero, and my testing doesn't show any problems here)

Honestly, the simplest answer to (1) may be to just go with the first approach, just straight changing the instruction at 0x05cd2c to load offset 0x0169 instead of 0x0022, beause that would be a one-line ASM hack, and it would work... it's just that you'd have to change that 0x0169 number in the ENTD for every single unit that loads random equipment, or they'd have none at all (I presume). It's just that I'd prefer the (Use 0x0169 if it's there, otherwise the level) approach, but am not sure where I could put the subroutine that does that.

Thoughts? Concerns? Ideas on other ways to make this happen? Different offsets to use? Random comments?
Title: Re: (ASM) Random enemy gear not scaling with level + unit offset 0x0169
Post by: Darkholme on March 27, 2011, 02:26:42 pm
That's really cool.

So if you wanted to say: work this into FFT 1.3; you'd have to go through the battles, look at what level each NPC should be by default; and put that number in the box you noticed as unused?

I'm impressed; and interested to hear the results of anyone who tries this hack out.
Title: Re: (ASM) Random enemy gear not scaling with level + unit offset 0x0169
Post by: Glain on March 27, 2011, 04:09:49 pm
Quote from: Darkholme on March 27, 2011, 02:26:42 pm
That's really cool.

So if you wanted to say: work this into FFT 1.3; you'd have to go through the battles, look at what level each NPC should be by default; and put that number in the box you noticed as unused?

I'm impressed; and interested to hear the results of anyone who tries this hack out.


Yeah, that would be the idea. If we used the "writing a new subroutine" approach, you'd only have to change the unused box if you didn't want it to load the random gear based on level (so for the main story battles at least, maybe not the randoms, I dunno). If we used the "change one instruction and nothing else" approach, then yeah, it would have to be changed for every unit that has random equipment or they'd have no gear in any slots marked "<Random>".

Of course, you wouldn't have to use the default levels if you didn't want to, and you could change it for each unit separately. If all the enemies have similar default levels, it might be easier to just pick one number for the whole battle and give that to everyone. You'd just have to remember they're hex numbers, so if the default level is 11 and you wanted to use it for the gear level, you'd have to input B (for 11) instead of 11 (for 17).

Glad to hear the concept sounds useful!
Title: Re: (ASM) Random enemy gear not scaling with level + unit offset 0x0169
Post by: Pride on March 27, 2011, 04:23:21 pm
SCUS is infamous for having no room for codes within it. If you wanted to place your code in it, you'd have to rewrite large sections of squares awful coding.
Title: Re: (ASM) Random enemy gear not scaling with level + unit offset 0x0169
Post by: pokeytax on March 27, 2011, 04:51:25 pm
Impressive how far you got on your own! That's awfully ingenious. Please feel free to ask questions as I know from experience it's a pain to figure this stuff out.

There are two main places the FFH community puts hacks: WORLD.BIN, and BATTLE.BIN. SCUS_942.21 is just too cramped; adding a routine of any size requires jumping to WORLD.BIN or BATTLE.BIN, as you discovered. Conveniently, one of these is loaded pretty much all of the time.

The best place to put codes in each of these is the kanji table, since obviously it's unnecessary in the English release and takes up a lot of space. The place various hackers have called dibs on chunks of that table is here (http://www.ffhacktics.com/wiki/Allocated_space), so feel free to claim some space. There's no shortage but you don't want your code to conflict with someone else's.

I suggest you edit the wiki to claim BATTLE.BIN 0xF59BC - 0xF67F3 and start your code at BATTLE.BIN 0xF59BC, which works out to RAM 0x15C9BC. To get more detail on what RAM maps to which files, look at http://www.ffhacktics.com/wiki/Offset_Conversion (http://www.ffhacktics.com/wiki/Offset_Conversion).

As far as I know, 0x169 is unused; since you're only using it to load equipment, you should be covered even if it's temporary read/write during battle.
Title: Re: (ASM) Random enemy gear not scaling with level + unit offset 0x0169
Post by: Glain on March 28, 2011, 12:47:13 am
Awesome feedback, guys, thanks.

I went ahead and reserved the block from 0xF59BC - 0xF67F3 (BATTLE.BIN) in the wiki as pokeytax suggested. I'm also now putting the subroutine I created in BATTLE.BIN at 0xF59BC instead of formula 41. There didn't seem to be any issues with moving the subroutine to the new location.

So with that said, looks like I can make it into a general patch!

Patch file/location/hex format:

BATTLE.BIN

0xF59BC
6901AE91
00000000
03000E14
FF00A530
2200AE91
00000000
0800E003
00000000

SCUS_942.21

0x4D49C
9400BFAF
21688000
6F72050C
20000234

SCUS_942.21

0x4D52C
00000000


XML format (FFTorgASM)

 <Patch name="Random unit equipment based on unit flag 0x0169">
   <Description>
   Random gear in ENTD will now be based on unit flag 0x0169
       (in ENTD, unknown section, last row, leftmost box)
       instead of unit level. If unit flag 0x0169 is 0, unit level
       will be used as normal.
   </Description>
   <Location file="BATTLE_BIN" offset="F59BC">
      6901AE91
      00000000
      03000E14
      FF00A530
      2200AE91
      00000000
      0800E003
      00000000
   </Location>
   <Location file="SCUS_942_21" offset="4D49C">
     9400BFAF
     21688000
     6F72050C
     20000234
   </Location>
   <Location file="SCUS_942_21" offset="4D52C">
     00000000
   </Location>
 </Patch>


(Edit: Updated patch to play nice with the load delay)
Title: Re: (ASM) Random enemy gear not scaling with level + unit offset 0x0169
Post by: Darkholme on March 28, 2011, 03:24:10 am
Hey glain; I have a possible expansion idea for this.

Could you maybe set it up so that instead of defaulting to the level of the NPC, it defaults to some int that gets saved and updated as the game progresses? so for instance; You get to dorter. The equipment levels of the story NPCs have been manually set in the slot where you chose them. It cycles through the characters; and finds the highest Equipment Level value on the map, and saves it in memory (or the lowest non-0 value, if you want the gear to be on the low side).

Then say you go to a random battle. The NPCs in the battle will never have better gear than the story battles.

Additionally; it means you don't need to set every NPC in the story battle's gear level. If you don't assign them a value; they'll get the same level gear as the last story battle you did.

Just an idea.
[When I say int; of course I mean a byte used as an unsigned int, and not an actual int; though I imagine you guessed that.]

Loving this function though.
Title: Re: (ASM) Random enemy gear not scaling with level + unit offset 0x0169
Post by: Glain on March 28, 2011, 06:12:52 pm
It's a cool idea, and it's similar to what I thought about doing when I set out to do this... I wanted to use the variable that tracked the story progression and compare that against the item levels... but unfortunately, didn't know where to find it. I knew it was being checked in WORLD.BIN, but the subroutine that retrieved it didn't exist during battles, and I couldn't tell where it was getting the variable from in the first place.

The idea of permanently storing a number in memory is interesting... I don't know enough about the memory blocks FFT uses to know if there are any addresses I could do that with. One of the big problems here is that the game likes to load things into memory, use them somehow, then discard them when done... For example, It might be difficult to find a memory location where a value could survive both in the world map and in battle, because often times I'll find that a memory location will have one value when WORLD.BIN is loaded (on the world map), and then be overwritten when BATTLE.BIN is loaded (in battle), or vice versa.

Apart from that, I'd think to store something permanently, I'd have to find a way to make FFT save it to the memory card when you save your game... Otherwise, when you saved and quit, it would be removed from memory. I'm also unfamiliar with the way that works.

A good expansion idea, though, to be sure. Glad to hear you like the function, Darkholme.

Title: Re: (ASM) Random enemy gear not scaling with level + unit offset 0x0169
Post by: Eternal on March 28, 2011, 06:17:33 pm
Very interesting ASM. I'd love to see this put in practice sometime.
Title: Re: (ASM) Random enemy gear not scaling with level + unit offset 0x0169
Post by: Darkholme on March 28, 2011, 09:48:22 pm
I added it to 1.3 easytype today and goofed around a little bit.

I like the non-infinitely scaling gear. :)
Title: Re: (ASM) Random enemy gear not scaling with level + unit offset 0x0169
Post by: Celdia on March 29, 2011, 08:31:37 am
This is an amazing idea. I need to implement it into CCP somehow...
Title: Re: (ASM) Random enemy gear not scaling with level + unit offset 0x0169
Post by: Darkholme on March 29, 2011, 03:54:27 pm
From goofing around with it in my emulator (I'm running emulators again instead of playing it on my PSP! See what you people made me do! :P)
I like it.

All it's lacking is a way to scale gear in random battles the same way (while still having the option for "scale by level" if someone wants it to). - I proposed one method above; but the question is where we're going to store the "current gear level" so that it can access it when it needs to. Maybe leaving it at 0 could be "use current gear level" and setting it above 99 means "Use characer level." And instead of just checking for the lowest non-0 value, you'd need to check for the lowest non-0 value below 100.

Does that variable you're changing stay on party members? Could you say; save the value on Ramza and load it later?
Title: Re: (ASM) Random enemy gear not scaling with level + unit offset 0x0169
Post by: Glain on March 29, 2011, 10:38:13 pm
So I'm looking at the ASM I wrote a bit more, and from looking at a few resouces on MIPS, I'm a bit unclear on whether I needed to honor the load delay. If I did, I'm technically causing a potential problem by executing the lbu and bne instructions right next to each other in the third-to-last block in my original post, because I can't be sure r14's value will be ready yet for me to check on the bne. Hrm. I didn't notice any problems when I tested it though (Other than that it loves linen robes for some reason, but that might just be a peculiarity of loading in random gear in general). So maybe it's not coming into play? Not sure... is the PSX MIPS processor affected by the load delay? Some of the ASM seems to be coded as if it is, but I never noticed this happening when I was debugging with pSX.

In particular, this:

91ae0169 lbu r14,0x0169(r13)             Load in unit offset 0x0169
140e0002 bne r14,r0,0x0018a174             (Do I know r14 actually has the value I loaded in yet?)


Shoving a nop between the two statements would fix the potential problem, I believe. I should probably do it to be on the safe side...

Darkholme:

That's a clever idea, storing the variable on Ramza at the same offset... it might work! Still no clue if it would work between saving/loading the game, but it would make sense that Ramza's data would be saved with the save file, so that's got a chance of working.

Having the codes is an interesting idea too. Something like you said:
0x00 = (Load from Ramza's offset, the 'default')
0xFF = (Load from level)

I'm thinking as I type here, but maybe the process would be something like this for each unit:
(1) Load from unit's offset (0x0169)
(2) If result = 0, load from Ramza's 0x0169 (the default)
(3) If result still = 0, make result = 255
(4) If result = 255 (0xFF), load from level (0x0022) and jump to (6)
(5) Save to Ramza's 0x0169 offset
(6) Return

The reason for (3) being that we don't want to get caught in a loop where battles load Ramza's offset, get 0, then save back the 0, forever, resulting in enemies with no gear for the rest of the game...

The only thing I don't really like about all this is that if you were to just apply the ASM patch without changing anything in FFTPatcher, enemies would have level 1 gear for the entire game. That's why I went with the level as the default if you didn't enter anything, so nothing would be changed until you changed it in the ENTD. Not sure, maybe we should swap the 0x00 and 0xFF codes for Ramza's offset.

Still highly theoretical, of course. I'd have to play around with this for a while before doing it. Seems viable though.

Edit: Okay, thanks pokeytax, good to know. I'm modifying my post with the patch in it with the updated version that takes the load delay into account.

Edit: Changed the process to include Darkholme's idea of using the unit level if Ramza's offset was 0. We should probably also not save it back in that case.
Title: Re: (ASM) Random enemy gear not scaling with level + unit offset 0x0169
Post by: pokeytax on March 30, 2011, 07:47:11 pm
Quote from: Glain on March 29, 2011, 10:38:13 pm
So I'm looking at the ASM I wrote a bit more, and from looking at a few resouces on MIPS, I'm a bit unclear on whether I needed to honor the load delay.


I have definitely had code fail solely because I did not honor the load delay, as near as I could tell. It's not 100% predictable but it's not worth any amount of debugging headache when you can often rearrange code to make use of the delay slot anyway.

I have also had code fail because I put a load instruction in a branch delay slot, in pSX; writing for emulators can be inconsistent. ePSXe is more forgiving of erroneous code, I think. Sometimes code will function as intended on stepthrough, when running from a breakpoint, or even just when an untripped "execute" breakpoint is set, but crash when run without the debugger open.
Title: Re: (ASM) Random enemy gear not scaling with level + unit offset 0x0169
Post by: Darkholme on March 31, 2011, 04:12:43 am
For Number 3: I agree that you don't want to set it to 0; but you probably don't want to set it to 1 either (Too Low).
I'd suggest you have it default to the NPC's level as you mentioned in step 4.
Title: Re: (ASM) Random enemy gear not scaling with level + unit offset 0x0169
Post by: Glain on April 05, 2011, 01:21:40 am
I just discovered something very interesting about random equipment!

I made a passing reference to this, but I couldn't understand why the game was loading in certain items that were much lower quality than the level they were being checked against. For example, knights with Linen Robes at Lionel Castle.

The item selection algorithm loads in a beginning item and an end item to check for the current slot. For armor, it starts at 0xAC (Leather Armor) and goes until it reaches 0xD0 (one beyond Robe of Lords). These item codes are the same as the ones in the Gameshark Handbook.

There are several factors that disqualify an item from consideration (the unit can't equip it, the item has the rare flag checked, etc). Any item that doesn't pass the checks will be skipped, but the routine will go through the whole list of items for each slot. Ultimately, it will create a list of items in memory that are eligible, then choose a random index in the list and return the item at that index.

The algorithm also keeps track of what the previous item's level was. If it finds another eligible item that is a higher level, it will overwrite that slot in the list with the new one. It will go through the armor this way... for example:

0xAC: Leather Armor: Save to list in slot 0
0xAD: Linen Cuirass: Save to list in slot 0, because this item's level is higher than the previous
0xAE: Bronze Armor: Save to list in slot 0, because this item's level is higher than the previous

Et cetera. In my test at Lionel Castle, we'll get to 0xB2 (Gold Armor) before the level check will stop any higher quality armor from being considered. This is the same every time. The item list now looks like this:

List = [B2]

Fairly simple list at this point. It just contains Gold Armor.

The algorithm assumes that item levels are in order, so if it runs into an item that is a lower level than the previous one, it considers this a new item type and goes to the next index in the list. When we get to robes, the Linen Robe (0xC8) is of a lower level than Gold Armor, so it gets a new index in the list. Our list becomes:

List = [B2, C8]

So far, so good! We just need FFT to remember the previous item level for the robes and it'll just start overwriting them as it gets to higher quality ones! Next in the list are Silk Robe (0xC9) and Wizard Robe (0xCA), after which it will stop because the item's level will be too high.

Let's see what happens!

List = [B2, C8, C9]

Oh no FFT what are you doing? This isn't a new item type at all! I really don't think...

List = [B2, C8, C9, CA]

...

(http://img218.imageshack.us/img218/2871/fourchoicesbox.jpg)
Why would you do this, FFT?

The algorithm doesn't remember the previous item level here! When it adds a new element to the list, it still thinks the previous item was Gold Armor... and keeps adding new slots to the list for every robe!

This is the final list that will return here. As a result, we've got a 25% chance of any of [Gold Armor, Linen Robe, Silk Robe, Wizard Robe]!

Let's see what's going on:


0005cdf0: beq r2,r0,0x0005ce00 If (Item level < Previous item level), skip the next 3 lines
0005cdf4: nop

0005cdf8: addu r12,r3,r0 r12 = (Item level)
0005cdfc: addu r9,r0,r0 List index (r9) = 0

0005ce00: addu r3,r9,r0 r3 = r9
0005ce04: addiu r9,r9,0x0001 Increment list index (r9)


r12 is where it's storing the previous item level, and r9 is our list index. It's a little weird in that it always increments the list index, but sets it to 0 beforehand if it didn't really want to increment it. What we can see here, though, is that the update to the previous item level (at 0x0005cdf8) is skipped in this case! (r9 will actually be set to one beyond the list index at the end here, but we'll get to that in a minute...)

Going into ASM coding mode, I thought of a way to save the previous item level...


0005cdf0: 14020003 bne r2,r0,0x0005ce00 If (Item level >= Previous item level), skip the next 3 lines (This check is reversed)
0005cdf4: 00000000 nop

0005cdf8: 25290001 addiu r9,r9,0x0001 Increment list index (r9)
0005cdfc: 00000000 nop

0005ce00: 00606021 addu r12,r3,r0 r12 = (Item level)
0005ce04: 01201821 addu r3,r9,r0 r3 = r9


I'm not doing the funky incrementing-but-not-really logic here: The list index increments only when we're going to add another slot to the list.

The fact that the list index (r9) gets set to one beyond its value only comes into play once we've exited the loop, but it means I've got to remember to add one to the value of r9 when I put it into the next register.


0005ce28: andi r16,r9,0x00ff r16 = r9 (r9 always < 0xFF)


becomes


0005ce28: addiu r16,r9,0x0001 r16 = r9 + 1


...And now, trying things out, at the same battle. What do I see?

(http://img8.imageshack.us/img8/6086/twochoicesbox.jpg)
Success!

50% chance for any of: [Gold Armor, Wizard Robe]. It picked the highest quality armor and highest quality robe that still fit within the level check, then randomly picked one of the two! All the knights in the battle had one of these two items in the armor slot.

EDIT: Apparently there's something wrong with this patch. I was getting some pretty funky stuff in tests in the deep dungeon (items equipped in the wrong slots), so I'm redacting it for now.

EDIT 2: Back in business! It seemed monks were problem children, but what was really happening was that it couldn't distinguish between "I chose from one item type, without ever increasing my list index" and "I couldn't find any items at all to equip for this gear slot". In the latter case, it was trying to load whatever happened to be in that memory location on the last run (D:). It led to some interesting results. Wasn't aware you could equip katanas on your head... seems dangerous! Anyhow, here's the new patch with that fixed.

It was checking the list index to see if we had found an item, but that wasn't going to work anymore. I needed another register. r15 wasn't being used, and it's a temp register, so I used it.

Here I'm setting up r15 at the top of the loop. I'm able to replace this line because grabbing only the lowest byte of r7 was just extraneous... it was always <= 0xff anyway.


0005cd3c: andi r7,r7,0x00ff r7 = (lowest byte of r7) (redundant)


became


0005cd3c: ori r15,r0,0x0000                  r15 = 0 (Start out false... haven't found anything yet)


I fortuitously had a nop where I needed to set r15 = 1 (when we were going to choose an item), so I was able to replace it with this:


0005cdf4: ori r15,r0,0x0001                  r15 = 1 (We found an item)


And then the check at the end became this:


0005ce2c: beq r15,r0,0x0005ce60     Skip loading the item if we couldn't find one (Used to be r16 instead of r15)


Anyhow, here it is in XML format.


  <Patch name="Random unit equipment more selective">
    <Description>
   Random unit equipment will now be more selective
        of secondary item types. (You shouldn't see
        knights in Chapter 2 using linen robes)
    </Description>
    <Location file="SCUS_942_21" offset="4D53C">
      00000F34
    </Location>
    <Location file="SCUS_942_21" offset="4D5F0">
      03000214
      01000F34
      01002925
      00000000
      21606000
      21182001
    </Location>
    <Location file="SCUS_942_21" offset="4D628">
      01003025
      0C000F10
    </Location>
  </Patch>
Title: Re: (ASM) Random enemy gear not scaling with level + unit offset 0x0169
Post by: Darkholme on April 05, 2011, 07:16:19 am
*clapping*

Though I don't like that it only looks at the highest one for the level. :/

I was always under the impression that the game had a random chance of choosing a slightly lower level item, or a slightly higher level item. It's disappointing that it's actually supposed to be far less random than I'd like, but I like that you fixed the bug.

So.. Flipping through the items in FFTPatcher: Stone Gun is level 95, but the blaze gun one slot later is level 91. This means it will think they're in separate categories ans start counting again? So a level 95 enemy Mustadio would be choosing randomly between a lv 95 Stone Gun and a lv 93 Blast gun? that seems to not fit in well with the algorithm you just described...
Congrats on finding the bug though. :) there are some problems with even a working one, as I just pointed out, as not all the items are sorted from lowest to highest level within an item category. It would work if the items were better sorted though.

I could write a new algorithm to do what I think would make random gear a bit more suitable, but my ASM is fuzzy, and my hex editing is worse for being out of practice. Perhaps I'll do it in pseudocode, or fake c-like syntax, which would be readable enough to know how to implement it if you're familiar with the appropriate ASM, and then when I get the chance to brush up on my ASM I might implement it here. It would be nice if it doesn't always give them the optimum gear, but instead has a random chance of going slightly higher or lower as well.

Did the idea of storing the most recent NPC levels on Ramza pan out?
Title: Re: (ASM) Random enemy gear not scaling with level + unit offset 0x0169
Post by: pokeytax on April 05, 2011, 06:25:09 pm
Wow, that's a really awesome bugfix and should go in every patch. Nice job! (And nice explanation - gotta say I could learn something from the way you documented it.)

Both Blast Gun and Stone Gun are Rare and will be skipped by this algorithm regardless. There are a few quiddities with item levels not monotonically increasing in vanilla, but an individual patch can fix those.
Title: Re: (ASM) Random enemy gear not scaling with level + unit offset 0x0169
Post by: Pickle Girl Fanboy on April 05, 2011, 06:55:48 pm
So does this mean I'll never again fight level 60 Knights at Mandalia Plains who have Small Mantles equipped?
Title: Re: (ASM) Random enemy gear not scaling with level + unit offset 0x0169
Post by: Darkholme on April 05, 2011, 07:54:09 pm
I believe that's exactly what it means.
Title: Re: (ASM) Random enemy gear not scaling with level + unit offset 0x0169
Post by: Glain on April 05, 2011, 08:43:39 pm
Thanks guys. Yeah, as pokeytax said, Blaze Gun and Stone Gun would never be loaded randomly anyway unless you unchecked the rare flag, so you wouldn't run into that scenario.

Since mantles seem to be a progression (i.e. next to each other in the item list), their quality should indeed scale up with the enemies, so no late game Small Mantles! :cool: The mantles do appear to have rather high level requirements though (10 for small, 20 for leather, 30 for wizard, etc).

EDIT: Turns out I found a problem with that patch loading in gear in the wrong slots. I'll have to figure out what's wrong with it.
Solved this issue... Patch is back in business! Refer back to that patch post for more details.

Darkholme (and/or anyone interested):

I did just check out the idea of saving the gear level on Ramza as a default. It might indeed work (and I've got a patch for it :)), but I'm not totally sure if I can properly get away with what I'm doing...

Turns out Ramza's out-of-battle data is loaded in all the time... at least from everything I tested. World map, story battles, random battles, cutscenes, chapter changes, oh my! His data was always there. It even saved to the memory card! Perfect!

Most of Ramza's data is in use though. It starts at 0x00057f74 and goes for another 0x100 bytes, where it runs into the next unit's data. I found a location that was 16 or so bytes away from most of his data, in an area that always seemed to be filled with zeroes, and loaded/saved properly... 0x00058053. That's my magic memory location. It weathers everything, saves/loads, and hopefully isn't used for anything. Perfect, except I'm not sure about that last part! (This is why I'm not sure if I can truly get away with what I'm doing.)

The modification we were talking about just means I have to modify my BATTLE.BIN subroutine. I came up with this:

EDIT: I've got a new patch and a new routine!


(1) Load r14 = unit's offset (0x0169)
0015c9bc: 91ae0169 lbu r14,0x0169(r13)
0015c9c0: 30a500ff andi r5,r5,0x00ff

(2) Conditional branch based on value of r14 [0x00 -> continue; 0xFF -> Jump to (5); Neither -> Jump to (6)]
0015c9c4: 25c30001 addiu r3,r14,0x0001
0015c9c8: 306300ff andi r3,r3,0x00ff
0015c9cc: 28630002 slti r3,r3,0x0002
0015c9d0: 1003000a beq r3,r0,0x0015c9fc
0015c9d4: 00000000 nop
0015c9d8: 140e0006 bne r14,r0,0x0015c9f4
0015c9dc: 00000000 nop

(3) Load r14 = default (0x00058053)
0015c9e0: 3c0e0006 lui r14,0x0006
0015c9e4: 91ce8053 lbu r14,-0x7fad(r14)
0015c9e8: 00000000 nop

(4) If r14 > 0, jump to (7)
0015c9ec: 140e0005 bne r14,r0,0x0015ca04
0015c9f0: 00000000 nop

(5) Load r14 = unit's level (0x0022) and jump to (7)
0015c9f4: 91ae0022 lbu r14,0x0022(r13)
0015c9f8: 08057281 j 0x0015ca04

(6) Save r14 to default (0x00058053)
0015c9fc: 3c030006 lui r3,0x0006
0015ca00: a06e8053 sb r14,-0x7fad(r3)

(7) Return
0015ca04: 03e00008 jr r31
0015ca08: 00000000 nop



(1) Load from unit's offset (0x0169)
0015c9bc: 91ae0169 lbu r14,0x0169(r13)
0015c9c0: 30a500ff andi r5,r5,0xff
0015c9c4: 140e000e bne r14,r0,0x0015ca00
0015c9c8: 00000000 nop

(2) If result = 0, load from 0x00058053 (the default)
0015c9cc: 3c0e0006 lui r14,0x0006
0015c9d0: 91ce8053 lbu r14,-0x7fad(r14)
0015c9d4: 00000000 nop
0015c9d8: 140e000b bne r14,r0,0x0015ca08
0015c9dc: 00000000 nop

(3) If result >= 255 (0xFF), result = 0
0015c9e0: 29c300ff slti r3,r14,0x00ff
0015c9e4: 140e0008 bne r3,r0,0x0015ca08
0015c9e8: 00000000 nop
0015c9ec: 140e0006 bne r14,r0,0x0015ca08
0015c9f0: 00000000 nop

(4) If result = 0, load from level (0x0022) and jump to (6)
0015c9f4: 91ae0022 lbu r14,0x0022(r13)
0015c9f8: 00000000 nop
0015c9fc: 08057282 j 0x0015ca08

(5) Save to 0x00058053
0015ca00: 3c030006 lui r3,0x0006
0015ca04: a06e8053 sb r14,-0x7fad(r3)

(6) Return
0015ca08: 03e00008 jr r31
0015ca0c: 00000000 nop


I tested in a story battle to make sure nothing ridiculous happened. It all seemed fine, the same as before... so at the very least, it's not horribly breaking. I also checked to see if it saved to the right location... which it did. (After I had to modify the ASM a bit... didn't know about those negative lbu and sb offsets! Sneaky!)

I haven't actually tested this in a random battle yet however... I can't seem to remember where you run into humans in random battles, and thought you might have a good test scenario. Normally I'd do more testing before listing out a patch, but since I've already got the hex codes for the ASM, why not?

Use at your own risk... (Eh, just make a backup of your ISO :))


   <Patch name="Random unit gear priority: [Flag 0x0169, Default, Level]">
    <Description>
   Random gear in ENTD will now be based on unit flag 0x0169
        (in ENTD, unknown section, last row, leftmost box)
        instead of unit level. If unit flag 0x0169 is 0, it will
        try to load a 'default' level based on previous levels. If
        that also fails, unit level will be used as normal. Unit
        level will also be used if 0xFF (255) is specified for the
        gear level.
    </Description>
    <Location file="BATTLE_BIN" offset="F59BC">
      6901AE91
      FF00A530
      0100C325
      FF006330
      02006328
      0A000310
      00000000
      06000E14
      00000000
      06000E3C
      5380CE91
      00000000
      05000E14
      00000000
      2200AE91
      81720508
      0600033C
      53806EA0
      0800E003
      00000000
    </Location>
    <Location file="SCUS_942_21" offset="4D49C">
      9400BFAF
      21688000
      6F72050C
      20000234
    </Location>
    <Location file="SCUS_942_21" offset="4D52C">
      00000000
    </Location>
  </Patch>
Title: Re: (ASM) Random enemy gear not scaling with level + unit offset 0x0169
Post by: Darkholme on April 06, 2011, 01:53:40 am
I'll have to test thisafter my exam on next monday. I'll probably just make a random battle with a bunch of generics and see how it turns out.
Title: Re: (ASM) Random enemy gear not scaling with level + unit offset 0x0169
Post by: Darkholme on April 06, 2011, 02:16:02 am
Quote from: Glain on April 05, 2011, 08:43:39 pm
Thanks guys. Yeah, as pokeytax said, Blaze Gun and Stone Gun would never be loaded randomly anyway unless you unchecked the rare flag, so you wouldn't run into that scenario.

Since mantles seem to be a progression (i.e. next to each other in the item list), their quality should indeed scale up with the enemies, so no late game Small Mantles! :cool: The mantles do appear to have rather high level requirements though (10 for small, 20 for leather, 30 for wizard, etc).

EDIT: Turns out I found a problem with that patch loading in gear in the wrong slots. I'll have to figure out what's wrong with it.
Solved this issue... Patch is back in business! Refer back to that patch post for more details.

Darkholme (and/or anyone interested):

I did just check out the idea of saving the gear level on Ramza as a default. It might indeed work (and I've got a patch for it :)), but I'm not totally sure if I can properly get away with what I'm doing...

Turns out Ramza's out-of-battle data is loaded in all the time... at least from everything I tested. World map, story battles, random battles, cutscenes, chapter changes, oh my! His data was always there. It even saved to the memory card! Perfect!

Most of Ramza's data is in use though. It starts at 0x00057f74 and goes for another 0x100 bytes, where it runs into the next unit's data. I found a location that was 16 or so bytes away from most of his data, in an area that always seemed to be filled with zeroes, and loaded/saved properly... 0x00058053. That's my magic memory location. It weathers everything, saves/loads, and hopefully isn't used for anything. Perfect, except I'm not sure about that last part! (This is why I'm not sure if I can truly get away with what I'm doing.)

The modification we were talking about just means I have to modify my BATTLE.BIN subroutine. I came up with this:


(1) Load from unit's offset (0x0169)
0015c9bc: 91ae0169 lbu r14,0x0169(r13)
0015c9c0: 30a500ff andi r5,r5,0xff
0015c9c4: 140e000e bne r14,r0,0x0015ca00
0015c9c8: 00000000 nop

(2) If result = 0, load from 0x00058053 (the default)
0015c9cc: 3c0e0006 lui r14,0x0006
0015c9d0: 91ce8053 lbu r14,-0x7fad(r14)
0015c9d4: 00000000 nop
0015c9d8: 140e000b bne r14,r0,0x0015ca08
0015c9dc: 00000000 nop

(3) If result >= 255 (0xFF), result = 0
0015c9e0: 29c300ff slti r3,r14,0x00ff
0015c9e4: 140e0008 bne r3,r0,0x0015ca08
0015c9e8: 00000000 nop
0015c9ec: 140e0006 bne r14,r0,0x0015ca08
0015c9f0: 00000000 nop

(4) If result = 0, load from level (0x0022) and jump to (6)
0015c9f4: 91ae0022 lbu r14,0x0022(r13)
0015c9f8: 00000000 nop
0015c9fc: 08057282 j 0x0015ca08

(5) Save to 0x00058053
0015ca00: 3c030006 lui r3,0x0006
0015ca04: a06e8053 sb r14,-0x7fad(r3)

(6) Return
0015ca08: 03e00008 jr r31
0015ca0c: 00000000 nop



So.
1. it loads the gear level from the unit, and if !=0, it jumps to 5.
2. if the number is 0, load it from ramza. then skip to the end.
3. If the result is >=255 set "result"=0. (I dont understand the asm for this one)
4. if it's now equal to 0 load it from the unit's level; and then skip saving and go to 6.
5. Save the number you have on ramza.
6. End.

Here's how I see that it's supposed to work based on a readthrough of it (in theory).
- So: It assigns the numbers in whatever order they're loaded in battle, meaning the last one loaded is the number saved, as opposed to the highest in the battle or the lowest or anything like that. So if you put level 5, 10, and 15 gear into the fight, whichever one is read in last (likely the one with the highest index) is the level that gets saved.
- If you set it to 255, to use 'Unit Level' for gear, it doesn't save the number. This is useful for bosses and whatever else you might want to have a higher level of gear, without raising the level of gear in random battles.

So; I guess, the best way to do it, is Set the level of gear you want people to have in general at the beginning index (excluding anyone you set to 255), leave all the people in the middle at 0 (since they'll use the number of whoever came before them) and set the level of gear you want random battles to use at the end index, (you might not even need to include them in the fight, just set them to not show up).

Here's a bug I think I found.
- If you set the number to 255, #1 doesn't check for it, and it jumps to 5 and saves 255 in ramza's number.
Title: Re: (ASM) Random enemy gear not scaling with level + unit offset 0x0169
Post by: Glain on April 06, 2011, 07:10:48 pm
Yeah, I noticed numerous problems with that routine, and have now updated the previous post with a new one! I was trying to be a bit clever in section (2) in the new one, particularly:


0015c9c4: 25c30001 addiu r3,r14,0x0001         r3 = r14 + 1 (0x00 would become 0x01, 0xFF would become 0x100)
0015c9c8: 306300ff andi r3,r3,0x00ff               r3 = (Lowest byte of r3) (Affects nothing EXCEPT 0x100 becoming 0x00!)
0015c9cc: 28630002 slti r3,r3,0x0002              r3 = (r3 < 2) (Only true if r14 was 0x00 or 0xFF!)


I am still testing the new patch, but I entered a random battle and got a knight whose level was too low for diamond helmet, but was still equipped with one. The magic memory location had 22 in it, which would have been high enough... (and made sense as I was in chapter 3). So maybe it's working!
Title: Re: (ASM) Random enemy gear not scaling with level + unit offset 0x0169
Post by: Darkholme on April 06, 2011, 07:27:48 pm
I'd really like to see another #4 after #5, personally, as I liked the idea that if you load the unit's level it doesn't mess up the level of gear overall. I thought it would be useful because it allows you to use 255 on bosses. (and the current algorithm would raise the gear level to match the boss).

Other than that, great Job!
Title: Re: (ASM) Random enemy gear not scaling with level + unit offset 0x0169
Post by: Glain on April 06, 2011, 08:32:14 pm
It should already be working the way you describe. :) The default gear level (0x00058053) should never be written to unless we loaded a non-0, non-FF value from 0x0169 of some unit. This can only happen at the first branch in (2). As long as the boss isn't the last unit with something at 0x0169, his gear level won't be the one that ultimately gets saved.

Are you sure you're not misreading the routine? We don't save anything to the default memory location except at the end of (6). We can't get to (6) except from the jump at (2). (5) jumps to (7), skipping over (6). Adding anything between (5) and (6) would be unreachable code. (That j statement is an unconditional jump) Or am I missing something?

(Haha, I think that should be some kind of record for number of parens in a single paragraph... :P)
Title: Re: (ASM) Random enemy gear not scaling with level + unit offset 0x0169
Post by: Darkholme on April 06, 2011, 08:59:15 pm
ah. Yeah.
that's me, not used to the ASM for this processor. I've only really looked at ASM for a SPARC processor. it doesnt have a j operator; It has ba for branch always.

Totally missed the j. Okay. no worries; carry on then. Fantastic work.
I'd like to seen every patch implement your random gear algorithm fix, and all the scaling patches use the gear scaling algorithm you just updated.

You wanna head over here (http://ffhacktics.com/smf/index.php?topic=6826.msg142972#msg142972) next and see if you can help us figure out where they limit the FPS to get rid of the WotL slowdown?
Title: Re: (ASM) Gear level - Now can use story progression instead of level
Post by: Glain on May 26, 2011, 09:16:33 pm
So I found a way to use the story progression byte after all...

Way back in my first post in the thread (and the entire forum, as it happens), I made reference to attempting this, and finding the WORLD.BIN subroutine (RAM 0xef1a8) that seemed to get it. After a bunch of investigation, I seem to have found that same routine in BATTLE.BIN at RAM 0x13b590. It's the same code but with some different addresses, because we're elsewhere in memory. Makes you wonder why the routine isn't just in SCUS, but anyhow, since it's in BATTLE.BIN, I can use it in the gear picking code.

I believe this routine is actually what gets any script variable, i.e., those set/checked by TEST.EVT and ATTACK.OUT, with r4 = [the index of the variable]. Story progression is variable 0x6F (and this parameter is specified in WORLD.BIN as well when checking the shop items, but I didn't understand the significance of it before). That means, if I want the story progression byte, I go:

jal 0x0013b590
ori r4,r0,0x006f

Not so bad. Using story progression instead of unit offset 0x0169, we get this:


andi r5,r5,0x00ff          # Most likely worthless, but carried over from 0x5cc98
addiu r29,r29,-0x001c      # Make space on the stack
sw r31,0x0014(r29)         # Save return address to stack
sw r13,0x0004(r29)         # Save 0x5cc98's args to the stack (registers might be changed by 0x13b590)
sw r5,0x0008(r29)
sw r6,0x000c(r29)
sw r7,0x0010(r29)
jal 0x0013b590            # Find story progression variable! (code for this variable = 0x6f)
ori r4,r0,0x006f
ori r14,r2,0            # r14 = r2 (Story progression variable)
lw r31,0x0014(r29)         # Load return address from stack
lw r13,0x0004(r29)         # Load 0x5cc98's args from the stack
lw r5,0x0008(r29)
lw r6,0x000c(r29)
lw r7,0x0010(r29)
addiu r29,r29,0x001c      # Set stack back to original size
ori r2,r0,0x0020         # Set r2 back (0x5cc98)
jr r31                  # Return
nop


It's still nearly the same amount of space as the other one, even though there's far less relevant logic, because I had to save a bunch of registers on the stack that the gear-picking routine was using and I didn't know if 0x13b590 would overwrite them (it probably did). I also had to change the gear-picking routine to check the item's story progression instead of the item level (8 bytes to the right).

Anyhow! [Story progression] and [Unit offset 0x0169] patches are mutually exclusive, but the [more selective gear] patch/bugfix is totally independent of either of them.

Unlike the unit offset one, the story progression patch should be fire and forget -- no need to set anything in FFTPatcher, unless you wanted to edit item tiers. You may see a bit less gear variety though, since there are less item tiers than there are levels.


 <Patch name="Random unit gear based on story progression">
   <Description>
     Random unit gear based on story progression
   </Description>
   <Location file="BATTLE_BIN" offset="F59BC">  
     FF00A530
     E4FFBD27
     1400BFAF
     0400ADAF
     0800A5AF
     0C00A6AF
     1000A7AF
     64ED040C
     6F000434
     00004E34
     1400BF8F
     0400AD8F
     0800A58F
     0C00A68F
     1000A78F
     1C00BD27
     20000234
     0800E003
     00000000
   </Location>
   <Location file="SCUS_942_21" offset="4D49C">
     9400BFAF
     21688000
     6F72050C
     20000234
   </Location>
   <Location file="SCUS_942_21" offset="4D52C">
     00000000
   </Location>
   <Location file="SCUS_942_21" offset="4D5D8">
     0A00A390
   </Location>
 </Patch>

Title: Re: (ASM) Gear level - Now can use story progression instead of level/0x0169
Post by: pokeytax on June 01, 2011, 10:09:26 pm
This is another neat hack! And the stuff you uncovered is helpful in doing story progression job unlocks for the RAD hack. Also, actually seeing someone write out how they're using the stack is really useful.

Do you happen to know what variables the story progression/script variable subroutine overwrites?
Title: Re: (ASM) Gear level - Now can use story progression instead of level/0x0169
Post by: Glain on June 02, 2011, 10:31:42 am
I tried to trace that routine a few times... it's fairly insane and the recursion makes it hard to remember how far into it you are. I'm not entirely sure how many registers it changes, but it's a lot.

FFT's compiled code was compiled under these conventions (http://en.wikipedia.org/wiki/MIPS_architecture#Compiler_register_usage), which specifies that registers r16-r23 ($s0-$s7) must be preserved across routine calls (i.e., that these registers must be in the same state they started in when the routine returns). Standard practice is to assume that all other standard-use registers are overwritten (and that is probably not far off for this routine).

So basically, before calling a routine, save what you want saved either to:
1. The stack
2. r16-r23 (but you must save these registers to the stack before using them, and load them when done before returning)
3. Memory somewhere (if you want to save something outside the scope of this routine's stack frame, i.e. a global variable)

Since this is true for every routine, it saves us from having to figure out which routines change which registers, unless we're so cramped for space that we can't do things the normal way. But I'd assume this particular routine (0x13b590) changes everything but r16-r23, because it's an absolute beast.
Title: Re: (ASM) Gear level - Now can use story progression instead of level/0x0169
Post by: Eternal on June 30, 2011, 06:02:17 am
So, I'm slightly confused on how to use this. After using it in orgASM, do I just set each item to the chapter I want enemies to start using it and then I'm good? Or do I have to put the 12 in the ENTD box I saw earlier in the topic? I'd appreciate if you clarified this. Thanks a ton! :D
Title: Re: (ASM) Gear level - Now can use story progression instead of level/0x0169
Post by: Glain on June 30, 2011, 10:54:31 am
It depends on which XML you're using, but the patch names should be pretty specific. The patch descriptions cover most of it, but are pretty vague, so I can try to elaborate by patch name (I've got 3 different XML patches in this thread that do something like this).

Before I list them out, I should probably point out that I have a bugfix in this thread for a tendency in FFT's random gear picking routine to use a low quality item if it has to consider more than one item type (e.g. armor and robe). The "Random unit equipment more selective" XML should take care of that, and can be used in tandem with any of the patches.

Now, as far as the patch itself goes, you can choose one of three options (all found in this thread):

"Random unit gear based on story progression" (the latest XML): Enemy gear will just be based on where you are in the story (which is the current shop availability). No need for the ENTD box. You can manipulate which items are available in the shops at which points in the story in the items section in FFTPatcher.

Now, a note about the next two XML patches. They do something similar, but both use that ENTD box that you mentioned. The way FFT's random gear picking routine works by default is that it tries to pick the item with the highest item level that is still not beyond the unit's level. If you use this patch, the number in the ENTD box will be used as a replacement for the unit level in that routine. The thing to remember is that this box holds a hex value. So if you wanted an enemy to use gear that is no higher than item level 25, you'd set the box to "19", because hex 0x19 = 25. (You can see and change item level for each item in the items section of FFTPatcher).

"Random unit gear priority: [Flag 0x0169, Default, Level]": Checks the ENTD box, but if no number was specified in that box, it'll use a default number based on the last time it checked that box. If it can't find that either, it'll take the unit level as normal. You can see Darkholme and I discussing this process in the thread a bit.

"Random unit equipment based on unit flag 0x0169": Only looks at the ENTD box, but will use unit level it if can't find that.

I should probably just make a comprehensive XML with all this stuff in it...
Title: Re: (ASM) Gear level - Now can use story progression instead of level/0x0169
Post by: Eternal on June 30, 2011, 11:00:50 am
All right, this helps a ton. Thanks!

While I'm thinking about it, it'd be awesome if you could make that comprehensive XML and edited your first post with it. I think it'd help a ton of people out, but the way you have it now seems fine to me as well, IMO.
Title: Re: (ASM) Gear level - Now can use story progression instead of level/0x0169
Post by: Eternal on July 02, 2011, 05:53:58 pm
Okay, so I've implemented the ASM and Xifanie noticed something: enemies in Orbonne I/Gariland don't start off with any gear. Which leads me to ask, do you know exactly when the game officially starts Chapter I?
Title: Re: (ASM) Gear level - Now can use story progression instead of level/0x0169
Post by: Celdia on July 02, 2011, 06:51:50 pm
Et, do you really need story scaled gear randomly set on those battles? Just preset everyone. Its not like you don't know what level the player will be at.

*quietly slips off to edit the ENTDs of those battles in her own patch to have preset equipment*
Title: Re: (ASM) Gear level - Now can use story progression instead of level/0x0169
Post by: RavenOfRazgriz on July 02, 2011, 07:00:56 pm
Quote from: Eternal248 on July 02, 2011, 05:53:58 pm
Okay, so I've implemented the ASM and Xifanie noticed something: enemies in Orbonne I/Gariland don't start off with any gear. Which leads me to ask, do you know exactly when the game officially starts Chapter I?


Have you considered it might be directly after the battle in Gariland Magic City, where the Shop is available for the first time and carrying the Gear listed as being "Chapter 1 - Start"?
Title: Re: (ASM) Gear level - Now can use story progression instead of level/0x0169
Post by: Glain on July 02, 2011, 10:39:37 pm
All right, I've got what I think is a fix for this. Seems to work for me, at any rate.

I think the problem with those two battles, as you guys have basically deduced, is that the story variable hasn't been set to 1 yet, so it's 0 and no gear is allowed. When the game sets the story variable, it looks like this decompiled from TEST.EVT:

ZERO(x006F)
ADD(x006F,x0001)

This is where the story variable gets set to 1, and it looks like it's happening at the start of the Gariland battle. But the random gear is picked before the battle starts, i.e. before this event is run.

What I've done is change my routine to basically say:
1. [Allowed gear level] = [Story progression variable]
2. If [Allowed gear level] = 0, set [Allowed gear level] = 1
3. Return [Allowed gear level]

Here's an XML patch:

  <Patch name="Random unit gear based on story progression">
    <Description>
      Random unit gear based on story progression
    </Description>
    <Location file="BATTLE_BIN" offset="F59BC"> 
      FF00A530
      E4FFBD27
      1400BFAF
      0400ADAF
      0800A5AF
      0C00A6AF
      1000A7AF
      64ED040C
      6F000434
      0100452C
      21704500
      1400BF8F
      0400AD8F
      0800A58F
      0C00A68F
      1000A78F
      1C00BD27
      20000234
      0800E003
      00000000
    </Location>
    <Location file="SCUS_942_21" offset="4D49C">
      9400BFAF
      21688000
      6F72050C
      20000234
    </Location>
    <Location file="SCUS_942_21" offset="4D52C">
      00000000
    </Location>
    <Location file="SCUS_942_21" offset="4D5D8">
      0A00A390
    </Location>
  </Patch>


For those interested in what I've actually done on an ASM level, here's the relevant part:

jal 0x0013b590           # r2 = [Story progression variable] (code for this variable = 0x6f)
ori r4,r0,0x006f
sltiu r5,r2,1                 # r5 = (r2 == 0)  (If r2 == 0, r5 = 1, but r5 = 0 otherwise)
addu r14,r2,r5             # r14 = r2 (Story progression variable) + r5
Title: Re: (ASM) Gear level - Now can use story progression instead of level/0x0169
Post by: Eternal on July 03, 2011, 09:32:31 am
Quote from: RavenOfRazgriz on July 02, 2011, 07:00:56 pm
Have you considered it might be directly after the battle in Gariland Magic City, where the Shop is available for the first time and carrying the Gear listed as being "Chapter 1 - Start"?


Yeah, Raven, I had pretty much determined it was that. Just struck me as odd since I'd think it'd be right when the game starts. =\

@Glain- thanks, that'll help a ton. *goes of to reset gear*
Title: Re: (ASM) Gear level - Now can use story progression instead of level/0x0169
Post by: Dome on March 17, 2012, 06:40:29 am
Necrobumping like a boss!

Sorry if I might sound stupid, but I want to be sure
Step 1: I apply this patch to my hack (FFT: Plus)

  <Patch name="Random unit gear based on story progression">
    <Description>
      Random unit gear based on story progression
    </Description>
    <Location file="BATTLE_BIN" offset="F59BC"> 
      FF00A530
      E4FFBD27
      1400BFAF
      0400ADAF
      0800A5AF
      0C00A6AF
      1000A7AF
      64ED040C
      6F000434
      0100452C
      21704500
      1400BF8F
      0400AD8F
      0800A58F
      0C00A68F
      1000A78F
      1C00BD27
      20000234
      0800E003
      00000000
    </Location>
    <Location file="SCUS_942_21" offset="4D49C">
      9400BFAF
      21688000
      6F72050C
      20000234
    </Location>
    <Location file="SCUS_942_21" offset="4D52C">
      00000000
    </Location>
    <Location file="SCUS_942_21" offset="4D5D8">
      0A00A390
    </Location>
  </Patch>


Step 2: Flag all the units I want in the FFPatcher with "Random" equipment

From now on, every unit I flagged that way will always have the best gear you can buy in stores (And not more/less powerful stuff), regardless of levels?

P.s: Is there any bug/problem I should be aware of?
Title: Re: (ASM) Gear level - Now can use story progression instead of level/0x0169
Post by: Glain on March 17, 2012, 10:33:10 am
If you also use the "Random unit equipment more selective" one in addition to that one, then yes. Without that patch, it can select really bad gear (due to a bug in vanilla).

I don't think there should be any problems, but let me know if you find anything. Sterling LaVaughn made a post here (http://ffhacktics.com/smf/index.php?topic=7864.20#msg165713) about using it.
Title: Re: (ASM) Gear level - Now can use story progression instead of level/0x0169
Post by: Dome on March 18, 2012, 06:04:24 am
Thanks a lot for the quick reply, I'll let you know if something doesn't work