News: 11 March 2016 - Forum Rules
Current Moderators - DarkSol, KingMike, MathOnNapkins, Azkadellia, Danke

Author Topic: FF1 MMC5 Disassembly Updates  (Read 212935 times)

Jiggers

  • Sr. Member
  • ****
  • Posts: 405
    • View Profile
    • My Ko-Fi Page
Re: FF1 MMC5 Disassembly Updates
« Reply #520 on: May 04, 2020, 10:38:23 pm »
And we come back to the first routine I ever edited...

I still think the idea of adding damage to spells based on how many orbs are lit is a pretty neat way to increase power as the game goes on.

How's this look for Intelligence use? Edit: Updated for Confusion. Confusion now subtracts from effectivity, since their brain is addlepated. Or I could just leave it alone, but I like little changes like this...

Code: [Select]
PutEffectivityInDamageMathBuf:
    LDX #0
    LDA btl_attacker
    BPL @Enemy                      ; high bit set if its a player

    LDA btlmag_magicsource
    BNE @Enemy                      ; 0 if magic... if not 0, then its an item's spell

    LDY #ch_intelligence - ch_stats
    LDA (CharStatsPointer), Y
   
    LDY ConfusedMagic
    BEQ @NotConfused
   
       LSR A
       LSR A                        ; divide by 4
       STA tmp
       SEC
       LDA btlmag_effectivity     
       SBC tmp                      ; subtract intelligence/4 from spell effectivity
       BCC @CapAt5                  ; Carry set if subtraction result remains 0 or over
       BEQ @CapAt5
       BCS @Save
       
   @CapAt5:                         ; choosing 5, since the base damage for FIRE 1 is #10
    LDA #5
    BNE @Save
   
   @NotConfused:
    LSR A                           ; divide by 2
    LSR A                           ; divide by 4
    LSR A                           ; divide by 8
    CLC
    ADC btlmag_effectivity          ; add the spell's initial damage
    BCC @Save                       ; if carry was set by the addition
        INX                         ; just increment X to 1
        BNE @Save
         
   @Enemy:
    LDA btlmag_effectivity          ; move effectivity into the math buffer!
   
   @Save:
    STA math_basedamage
    STX math_basedamage+1
    RTS

Are you sure intelligence shouldn't also maybe make ailment applying more effective? That's something I always thought needed a boost.

I got items working again, just need to fine-tune some of the effects (Soft won't flash a sprite since there's no sprite to flash until its cured; so my idea is to apply/delete the stone ailment for the target for a couple frames and have the sprite updating routine handle the transition from grey to colour again.) (And the Alarmclock doesn't flash anyone, since there's no code in place for flashing the whole party at once... yet.)

All this time Ethers were broken apparently. I need to stop testing things on the first slot only. They now work properly for all spell levels.

At the moment poison doesn't seem to trigger damage at the end of turns, but while looking at it I re-wrote a bunch of it, now I have a better understanding of things. So, saved a bunch of space there, and it should handle flashing hidden characters too? Haven't updated that yet though. Going to test it first.

I need some help with the concept of division again. Check out the RegenDivision, YXDivideA, and DoDivision routines? The way poison works is dividing max HP and putting that into the damage buffer... but I based this off something Anomie did in hex, since I was helping do some assembly for someone else's FF1 hack a while ago. I THINK what Anomie did was swap the variables btltmp_divLo and btltmp_divHi... or divV... or SOMETHING... so that it could be used with the MathBuf routines.

However, looking at it now, YXDivideA puts "btltmp_divLo" into A, then RTS. And immediately after that I have it ORA with "btltmpdivLo" again. So uh. That should be something else maybe? Maybe I'll figure it out in the disassembler and seeing what is actually happening...

Going to see if I can get healing spells to be more effective than just #$FF.



Had to do some thinking. Since the addition of intelligence to healing spell effectivity could potentially make it a 16-bit number, how to handle this formula? Healing potency = Base potency + a random number between base potency and 0. If adding intelligence to the potency made the low byte 1, this would suuuuck. Therefore, if a high byte exists, make the random number be between $0 and $FF. I really have no idea how you would get a random number between 0 and a 16-bit value...

Code: [Select]
BtlMag_Effect_RecoverHP:
    INC btlmag_spellconnected       ; HP recovery always connects (doesn't it miss if dead?)
    JSR PutEffectivityInDamageMathBuf
    ;; add intelligence to effectivity
   
    LDA btlmag_defender_ailments    ; Check defender ailment
    AND #AIL_DEAD
    BNE BtlMag_Effect_Slow_RTS      ; If they're dead, do nothing
   
;    LDX btlmag_effectivity          ; This block just does:  X = rand[ effectivity, effectivity*2 ], capping at 255
;    LDA #0
;    JSR RandAX                      ; random between [0,effectivity]
;    CLC
;    ADC btlmag_effectivity          ; add effectivtiy
;    BCC :+
;       LDA #$FF                     ;   (cap at 255)
;  : TAX
;    LDA #MATHBUF_MAGDEFENDERHP      ; Add X to defender's HP
;    JSR MathBuf_Add

    LDX math_basedamage             ; basedamage is now amount of HP to heal
    LDA math_basedamage+1
    BEQ :+
    LDX #$FF                        ; effectivity is over $FF, so random between $0-$FF
  : LDA #0
    JSR RandAX
    CLC
    ADC math_basedamage
    BCC @DoHealing                  ; no carry, so just do healing
   
    ; add carry to any high bits already set by PutEffectivityInDamageMathBuf
    INC math_basedamage+1
   
   @DoHealing:
    LDX #MATHBUF_DEFENDERHP
    LDY #MATHBUF_BASEDAMAGE         
    JSR MathBuf_Add16
   
    LDX #MATHBUF_MAGDEFENDERMAXHP
    LDY #MATHBUF_MAGDEFENDERHP
    JSR MathBuf_Compare             ; Compare current HP with max HP
    BCC BtlMag_Effect_CureAil_RTS   ; if hp < maxhp, just exit



Pushed all these things to GitHub. Poison seems to work properly again! My signature has never been more accurate--despite having 0 compile errors and going through the debugger while testing to catch any dumb mistakes... My fighter turns green if a hidden character gets poison damage. None of the smileys on this forum represent my level of vexedness. Update: Turns out successfully hiding after saving defender magic stats was clobbering X, and so a write to "$6940, X" -- where X is $F0 somehow -- was writing to the attribute byte for the first character...
« Last Edit: May 05, 2020, 04:09:22 pm by Jiggers »
I know exactly what I'm doing. I just don't know what effect it's going to have.

I wrote some NES music! Its a legal ROM file. - I got a Ko-Fi page too.

Vanya

  • Hero Member
  • *****
  • Posts: 1750
    • View Profile
Re: FF1 MMC5 Disassembly Updates
« Reply #521 on: May 05, 2020, 04:59:07 pm »
I still think the idea of adding damage to spells based on how many orbs are lit is a pretty neat way to increase power as the game goes on.

That is a good idea. You may have seen that FF2 actually does something similar.
So there is precedent for it in the series.
You just have to be careful not to unbalance the characters.

It might be better to take this idea in a different direction.
Maybe have each orb cumulatively increase all damage by say 5%.
That way you only have to worry about making sure the monsters can keep up with the new damage output.
« Last Edit: May 05, 2020, 05:52:50 pm by Vanya »

Jiggers

  • Sr. Member
  • ****
  • Posts: 405
    • View Profile
    • My Ko-Fi Page
Re: FF1 MMC5 Disassembly Updates
« Reply #522 on: May 05, 2020, 05:10:15 pm »
That was for when I was going to make an entirely different hack. Where I thought I would be limited to a certain amount of spells and wanted to make sure that all (most?) of the classes had 1-2 spells of each level, and maybe do away with MP unlocking higher levels as you go up--so you start out with MP for level 4 spells, then at level 2, you get MP for the next 4... Then if you had a fire-elemental damage spell that hit all enemies at level 1, it would still be viable at level 50. Intelligence and orbs would increase power over time for magic, rather than having 4 versions of Cure.

But now? Still a neat idea, but I know I can also make 128 different spells if I wanted to. Give every class 3 spells per level that are unique to them. And likely the biggest hurdle for that is where to get them--if I wanted to keep it to shops. There just might not be enough shop doors in the Town map, and last time I tried to increase the amount of tilesets, to make a second town... it broke things horribly! And I don't know if I want to tinker with making shops have scrolling inventories for buying, the way selling does...
I know exactly what I'm doing. I just don't know what effect it's going to have.

I wrote some NES music! Its a legal ROM file. - I got a Ko-Fi page too.

Vanya

  • Hero Member
  • *****
  • Posts: 1750
    • View Profile
Re: FF1 MMC5 Disassembly Updates
« Reply #523 on: May 05, 2020, 05:58:29 pm »
Definitely still a neat idea!
You can give some spells as treasure maybe?
If you want to get crazy, you can try expanding the spell shops to hold more spells.
Maybe have some NPC shops?



How's this look for Intelligence use? Edit: Updated for Confusion. Confusion now subtracts from effectivity, since their brain is addlepated. Or I could just leave it alone, but I like little changes like this...

The ASM looks fine, though I can't say I fully understand everything that is happening in it without taking the time to reference the opcodes.

An INT penalty during the confused status? I can see that making sense. I dig it.



Are you sure intelligence shouldn't also maybe make ailment applying more effective? That's something I always thought needed a boost.

I believe it should, yes.



I got items working again, just need to fine-tune some of the effects (Soft won't flash a sprite since there's no sprite to flash until its cured; so my idea is to apply/delete the stone ailment for the target for a couple frames and have the sprite updating routine handle the transition from grey to colour again.) (And the Alarmclock doesn't flash anyone, since there's no code in place for flashing the whole party at once... yet.)

All this time Ethers were broken apparently. I need to stop testing things on the first slot only. They now work properly for all spell levels.

At the moment poison doesn't seem to trigger damage at the end of turns, but while looking at it I re-wrote a bunch of it, now I have a better understanding of things. So, saved a bunch of space there, and it should handle flashing hidden characters too? Haven't updated that yet though. Going to test it first.

Groovy!



I need some help with the concept of division again. Check out the RegenDivision, YXDivideA, and DoDivision routines? The way poison works is dividing max HP and putting that into the damage buffer... but I based this off something Anomie did in hex, since I was helping do some assembly for someone else's FF1 hack a while ago. I THINK what Anomie did was swap the variables btltmp_divLo and btltmp_divHi... or divV... or SOMETHING... so that it could be used with the MathBuf routines.

However, looking at it now, YXDivideA puts "btltmp_divLo" into A, then RTS. And immediately after that I have it ORA with "btltmpdivLo" again. So uh. That should be something else maybe? Maybe I'll figure it out in the disassembler and seeing what is actually happening...

Yeah. Definitely step through it in the debugger. It sounds like that situation is ripe for a logic error or two.



Going to see if I can get healing spells to be more effective than just #$FF.

That would be neat.



Had to do some thinking. Since the addition of intelligence to healing spell effectivity could potentially make it a 16-bit number, how to handle this formula? Healing potency = Base potency + a random number between base potency and 0. If adding intelligence to the potency made the low byte 1, this would suuuuck. Therefore, if a high byte exists, make the random number be between $0 and $FF. I really have no idea how you would get a random number between 0 and a 16-bit value...

Off the top of my head:
If the high byte is 0, you get a random number as normal for an 8-bit value.
If the high bytes is not 0, get a random number as normal where the high byte is used by itself as an 8-but value. Use this as the "random high byte".
Then get a second random number that is always from 0 to FF.
Use this as the "random low byte".

Hope that helps! :3

Jiggers

  • Sr. Member
  • ****
  • Posts: 405
    • View Profile
    • My Ko-Fi Page
Re: FF1 MMC5 Disassembly Updates
« Reply #524 on: May 05, 2020, 08:06:30 pm »
Off the top of my head:
If the high byte is 0, you get a random number as normal for an 8-bit value.
If the high bytes is not 0, get a random number as normal where the high byte is used by itself as an 8-but value. Use this as the "random high byte".
Then get a second random number that is always from 0 to FF.
Use this as the "random low byte".

Hmm... Not sure I understand step #2. >.< I might be able to grasp it better if you put it in ASM?

While trying to picture it, I remembered bit shifting. So for example, Effectivity is:

$01, $12 | #274
Shift that right...
$89 | #137
Then get a random number between $0 and $89
$61 | #97
Then shift it back left?
$C2 | #197

Then its $C2 + $12 for the low byte, $01 + #0 (to get the carry) for the high byte = $01, $D4 | #468

That seems to make sense, but I'm not sure if that's overpowered or Just Right or what!



Since I did bug fixes last, it was time to treat myself! Behold, randomized backdrop patterns!







Due to the nature of RNG, I capped it at 3 possible repeats in row. First attempt was uncapped and resulted in 5:



I imagine without a cap it would be possible to get a screen of only short or only tall trees, depending on how the RNG works (still using the 256 byte table.) So take picture #3 and the RNG basically popped out: 3, 1, 1, 1, [1 or 3], 3, 1, 1, 1, [1 or 3], 3, 1, 1, 1, [1 or 3], 3. Very silly.



Another Update: Did some fancy sprite work. Using a Soft, the character will now flash grey (in their normal pose) before turning the right colour. I could get them to flash in the Cheer pose, but its easier not to, and gives the little hint that they're softening up. This only happens with Soft items so far, not the spell; will update that next time.

Alarm Clock now calls a little routine that flashes the whole party. I will make Smokebombs use it too!! Easier than doing enemy explosion sprites over everyone. Segue!

I moved all character sprites in OAM to the bottom and took out the spaces between them again. I just don't see animated poison bubbles and stuff working well with the palette situation as it is now. Not to mention having 9 cursors pointing at enemies for variable magic will not leave enough room for that. There will be 4 tiles of sprites left! I moved all this so that the multiple cursor sprite can be loaded up contiguously. Groundwork is laid out for them to point to all -- HECK* -- enemy formations except fiends and Chaos. Should also work on the party. Just needs to be loaded up by the magic targeting routine, then called in a loop to keep them doing the ghost-y effect.

* I forgot to add something in so that cursors aren't pointing at empty enemy slots. Gonna do that before I go to bed.
« Last Edit: May 06, 2020, 03:57:35 am by Jiggers »
I know exactly what I'm doing. I just don't know what effect it's going to have.

I wrote some NES music! Its a legal ROM file. - I got a Ko-Fi page too.

Vanya

  • Hero Member
  • *****
  • Posts: 1750
    • View Profile
Re: FF1 MMC5 Disassembly Updates
« Reply #525 on: May 06, 2020, 03:53:51 am »
I'm not sure about the ASM for my example.
I'm more used to thinking in terms of Pascal/C++.
The best I can do is pseudocode.

... fuck it! I did a bit of snooping around the net and found something far more useful than anything I could have come up with.

Here: https://wiki.nesdev.com/w/index.php/Random_number_generator

It even gives a 16-bit value in the first example.
The only hangup I can see is that this never gives a value of 0.
So maybe run the low byte through the normal RNG, and use the example only for the high byte?
I dunno. :\



Hot damn those randomized backgrounds are freakin' great!! :3

Disch

  • Hero Member
  • *****
  • Posts: 2814
  • NES Junkie
    • View Profile
Re: FF1 MMC5 Disassembly Updates
« Reply #526 on: May 07, 2020, 08:32:53 pm »
A big LSFR like that is probably the best way to get a "good" 16-bit number, but that is going to be quite time consuming.  The fast version of the routine in the given link is 69 cycles (nearly 2/3 of a scanline) and that's BEFORE having to scale it to a desired range which itself is likely to be a time consuming process.

IIRC, FF1 uses a multiplier to scale the RNG output to a desired range (value = rng_out * range / 256) -- but it would only work with an 8 bit result.  If you want to scale the output to a 16-bit number and scale it by the same means you'll have to do a 16-bit * 16-bit = 32-bit multiplication (value = rng_out * range / 65536) which would be just atrocious and might consume 2 or 3 scanlines to generate a single random number.

For this, I'd probably go with some hackish "fast but not perfect" way to scale up the number.  Particularly if you really only need a 9 or 10 bit number (and not a full 16-bit number necessarily)... you can probably cheese that quite a bit.


One of my first thoughts was to just make an 8-bit number and shift it up as needed.

Example:
Say you need a number between 0-500  (9 bit number)

- Shift the upper-bound right until it can fit in 8-bit number  (right shift once:  0-250 .. 250 fits in 8-bits)
- Generate a random number in that range (example, say you get 136, which is between 0-250)
- Shift that number left the same number of times we shifted the range down (left shift once:  136 becomes 278)

This basically keeps an 8-bit RNG viable, but now instead of just creating 8-bit numbers, you're using it to find the 8 most significant bits of ANY size number.


This certainly would give you somewhat flawed results (ex:  you'll never get odd numbers), but it would certainly be much faster than a massive multiplication... and the results would probably be "good enough" for practical purposes.

Jiggers

  • Sr. Member
  • ****
  • Posts: 405
    • View Profile
    • My Ko-Fi Page
Re: FF1 MMC5 Disassembly Updates
« Reply #527 on: May 07, 2020, 10:44:37 pm »
Hey, that example looks like my idea! *feels smart*

I'll probably do the shifting; its easier to do than replace the whole RNG, and more space-efficient than having two kinds. And if its only ever used in battle healing... Could also do a second random roll to tack on the last 1 bit.

I DO want to replace the 8-bit RNG thing sometime. It only needs to hit all 256 numbers when doing Chaos, but I can do that with some RAM and Bank B code. (Clear out $100 bytes, fetch RNG number, write $FF or something to that number's spot in RAM, then if its fetched again and its spot is $FF, toss it to fetch another one. Then hope it doesn't get stuck on the last few numbers before the next frame.)

I was doing some calculations with regards to the Focus skill, and made some changes.

Focus is now level / 2, + morale / 4. The amount of morale used is then subtracted from the morale stat. Any additional Focus is kept--so if you had 22 last turn, then 11 this turn, that 11 is added in. And its all capped at 127. Each turn, instead of dividing Focus by 3, its just straight shifted to divide by 2, to drain slower.

THEN, during the damage boost with Intelligence, Focus is added to Intelligence before its divided and added to the damage. Now here I was looking at the max boost this would give, which... seemed pretty pitifiul. Max focus (max level + max morale = 127) + max intelligence (99) would give only +28 to damage... The difference between Nuke (100) and Ice 3 (70) is 30. So you're sacrificing a turn to do less damage than casting one spell each turn?

So then I changed the Focus+Intelligence/8 thing to ... Focus+Intelligence/4, back that up, divide it again (8 as before), then add in the backup. So that's uh... divided by 6 maybe? I don't know what that would be called! But the max would then be +84 damage. Which seems pretty good.

I wasn't able to really get a good description of what Focus does in other games, so if there's a better calculation I could do, I'm up for it.

(I also added in item sprites, and battle sprites are loaded with the stack now so there's less display errors... which means I'll need to figure out another teleport solution. Can the stack be backed up to the other RAM page...? Hm...)
I know exactly what I'm doing. I just don't know what effect it's going to have.

I wrote some NES music! Its a legal ROM file. - I got a Ko-Fi page too.

Disch

  • Hero Member
  • *****
  • Posts: 2814
  • NES Junkie
    • View Profile
Re: FF1 MMC5 Disassembly Updates
« Reply #528 on: May 07, 2020, 11:43:41 pm »
Whoops!  Yeah looking up a bit it turns out you said the exact same thing.  That's what I get for only reading the most recent post.  Sorry Jiggers.  ^_^    :laugh: :thumbsup:

Vanya

  • Hero Member
  • *****
  • Posts: 1750
    • View Profile
Re: FF1 MMC5 Disassembly Updates
« Reply #529 on: May 08, 2020, 05:46:08 am »
Still worth it when you drop the knowledge, Disch.

For what it's worth my flailing idea was to treat each byte of the 16-bit value as if it were an 8-bit value and the sort of "glue" them together.

Let me see if I can express it better now...

Let's say the 16-bit number is 05CA.
High byte is 05.
Low byte is CA.
Feed each into the RNG.
Random high byte result is 03, set that aside in the stack I guess.
Random  low byte result is 15, set it aside in the stack too or whatever.
Now pull the low and high bytes off the stack and add them to your damage as 0315.

I dunno if this makes more sense or if it isn't just cockamamie pseudo code that misses important factors I'm not aware of.
Also, I have absolutely no idea about how processing cycles work in relation to scanlines, so there's that working against me too.

I really need to take some time and study up some more.



About the Focus ability.
I'll see if I can dig up any info about the other games.

Generally speaking I think the main thing to consider is that the effects of Focus should last until the end of the battle.
If it only affects your next spell then the boost needs to be worth more than casting the same spell twice in a row.
If it lasts longer then you don't need to have it add a ton of damage since the gains will be cumulative for every spell you cast.

Disch

  • Hero Member
  • *****
  • Posts: 2814
  • NES Junkie
    • View Profile
Re: FF1 MMC5 Disassembly Updates
« Reply #530 on: May 08, 2020, 11:33:09 am »
For what it's worth my flailing idea was to treat each byte of the 16-bit value as if it were an 8-bit value and the sort of "glue" them together.

That would work if you had a actual random numbers, but with a pRNG it ends up not being great.

You can sort of think of the pRNG as a fixed sequence of numbers that are spit out.  IE the sequence might look like this:

F3 A7 03 15 57 21 ...

The problem with taking two numbers from the sequence and gluing them together is you don't really get a wide range.  Since the sequence is only 256 values long, there is only 256 possible values for your 16-bit number.

IE... since 03 is followed by 15 in the sequence, if 03 is your high byte, 15 will ALWAYS be your low byte (you'll never get 0314 or 0318 or anything else).

And then you still have the problem of scaling that 16-bit number down to a desired range.

EDIT:
I might have misunderstood your idea at first!  Looks like you're taking numbers and using them as a seed to run them through the RNG again to sort of shuffle them around a bit more.

A neat idea -- but again the pRNG isn't actually random.  So if you use a specific number as a seed, you'll get the same "random" number output every time.  IE:  an input of 05 will always get you an output of 03.  So running random numbers through the pRNG again doesn't really do anything for you.  Each input has a direct 1:1 relationship with its output.



pRNGs are pretty fascinating and a pretty cool area of computer science.  There's like an entire field dedicated to studying them, so my knowledge on them is limited.  But the important thing to keep in mind is that the numbers aren't actually random .. and therefore a lot of things you might expect to be able to do you can't really do.


Quote
Also, I have absolutely no idea about how processing cycles work in relation to scanlines, so there's that working against me too.

There's 113.6667 cycles per scanline, and 262 scanlines per frame (20 scanlines of VBlank).  Most/all 6502 reference sheets tell you how many cycles each instruction consumes.

That's kind of esoteric knowledge but once you know it, you know it.  ;)

Quote
I really need to take some time and study up some more.

Ahh, don't be too hard on yourself!  You've had a lot of great contributions to this thread.  Honestly a lot more than me, since I just pop in for like one or two posts then disappear for months.   :laugh:
« Last Edit: May 08, 2020, 11:53:59 am by Disch »

Jiggers

  • Sr. Member
  • ****
  • Posts: 405
    • View Profile
    • My Ko-Fi Page
Re: FF1 MMC5 Disassembly Updates
« Reply #531 on: May 08, 2020, 04:12:31 pm »
Cycle/Scanline stuff isn't SUPER important in an RPG like FF1. Until you start doing screen effects, then... its everything. When I was first reading up on it I was like, boy, sure glad I don't have to deal with THAT! And then, well. Then I suddenly had to deal with it, because I wanted to change a color mid-frame, and then I had to deal with it again to load sprites between frames... and re-write the entire box-drawing routine so every box would be loaded up before the scanlines hit the color change spot so it could reach the routine that does the change instead of missing it... The important thing would be that the random number generator doesn't have to keep spitting out dozens or hundreds of new numbers to get the one you want--like with my background randomization. No doubt its called a few times there, only to throw away a number because it hasn't got a 1 bit at the end. (But even then, that's done with the screen off, so it doesn't matter if it takes 800 scanlines... that's like 3/60th of a second.)

Whoops!  Yeah looking up a bit it turns out you said the exact same thing.  That's what I get for only reading the most recent post.  Sorry Jiggers.  ^_^    :laugh: :thumbsup:

No worries! I'm just proud of myself for coming up with the same solution that you would have. I've come a long way from getting confused about the difference between BEQ/BNE!

Quote
I dunno if this makes more sense or if it isn't just cockamamie pseudo code that misses important factors I'm not aware of.

It makes sense to me, but it might be missing a factor... Since intelligence+base spell potency likely won't ever cause the high byte to be more than $01*, that means the low byte is REALLY not likely to be higher than, say, $10. So the random chance of getting between 0-1 and 0-10 is wildly in favor of closer to 0. 50/50 chance of 0 on the high byte, then the low byte is pretty much useless.

Disch is right though, your ideas have been keeping this going. :D Focus! Why the heck am I even making it fade out as the turn goes on? I guess I was thinking in terms of Hide but for magic--Hiding gives an accuracy and critical hit boost (+ damage for thieves) only so long as you STAY hidden during making the attack, then its gone. So maybe I was thinking Focus would be a "only for the next turn" thing? So yeah... maybe I'll make it drop off 1-2 points every turn, just because I like those weird details... but it shouldn't be just "sacrifice this turn to do double damage next turn". Making it stick around for the whole battle so that every spell afterward is boosted is much better!

For random NES knowledge, I'd recommend joining the NESDev Discord and just lurking and soaking up conversations. Seeing people talk back and forth about concepts and hardware makes it easier to understand than just reading a wiki page that was written for scientists and engineers to read. Seeing the terms like PPU and cycles and VBlank in context builds up the language background I need to understand anything going on. I still find pieces of FF1 code that I never touched because they were too confusing, and then suddenly I know exactly what its doing, why, and if I'm really lucky, how to improve it. Though no doubt lots of people will say my battle box could is garbage because my improvement was "what if I just used like $2000 bytes of RAM to store everything"...


* Unless someone starts making HP cap at 9999 and inflating stats and numbers in general...
I know exactly what I'm doing. I just don't know what effect it's going to have.

I wrote some NES music! Its a legal ROM file. - I got a Ko-Fi page too.

Vanya

  • Hero Member
  • *****
  • Posts: 1750
    • View Profile
Re: FF1 MMC5 Disassembly Updates
« Reply #532 on: May 08, 2020, 07:02:21 pm »
Thanks, guys! I'm glad I can help somehow. :3

So... How does the game currently calculate spell damage?
From what I have read the formula is like this:

Quote
E = Effectivity. Determined by Spell. (Effectively capped at 255)

Resisted Attack Spell
Damage = E...2E

Unresisted Attack Spell
Damage = 2(E...2E)

--If the target is resistant to spell element, divide effectivity by 2
--If the target is weak to spell element, multiply effectivity by 1.5

So how do you actually get those damage ranges? (E...2E)
I always assumed that the RNG is used with the result being limited by E.

Focus is a bit of a mess to research, but I can say that 4 Heroes of Light has 2 versions of the focus ability called Magic Mojo and Spell Focus.
I have no idea about how much of a boost they provide, but they are an example of one-and-done abilities.
They only state that one does more damage and the other does a lot more damage.
In contrast in X-2 it increases the Magic stat by 3 points per use and stacks up a maximum of 10 times for a total boost of 30.

Jiggers

  • Sr. Member
  • ****
  • Posts: 405
    • View Profile
    • My Ko-Fi Page
Re: FF1 MMC5 Disassembly Updates
« Reply #533 on: May 08, 2020, 09:40:14 pm »
Damage spell routine:

Code: [Select]
BtlMag_Effect_Damage:
    INC btlmag_spellconnected               ; damage spells always connect
    JSR BtlMag_LoadBaseHitChance            ; load base hit chance (since damage always hits, this becomes more of a "critical" chance)
    JSR PutEffectivityInDamageMathBuf       ; Load spell effectivity into 'damage' math buffer
   
    LDA btlmag_element
    AND btlmag_defender_elementresist       ; see if the defender resists this element
    BEQ :+                                  ; if they do...
      JSR BtlMag_ZeroHitChance              ; ... zero the hit/crit chance
      LSR math_basedamage+1                 ; and halve the damage
      ROR math_basedamage
     
  : LDA btlmag_element
    AND btlmag_defender_elementweakness     ; see if they are weak to the element
    BEQ :+                                  ; if yes...
      LDA #MATHBUF_HITCHANCE
      LDX #40
      JSR MathBuf_Add                       ; crit bonus of +40
     
                                            ; damage *= 1.5
      LDX #MATHBUF_BASEDAMAGE               ;   copy 1x damage into temp buffer
      LDY #$02                              ;  (use math buf 2 as a temp buffer)
      JSR MathBuf_CopyXToY
     
      LSR math_basedamage+1                 ;   damage *= 0.5
      ROR math_basedamage
     
      LDA #MATHBUF_BASEDAMAGE               ;   then add the backup back into the damage
      LDX #$02                              ;   resulting in 1.5 damage
      LDY #MATHBUF_BASEDAMAGE
      JSR MathBuf_Add16
     
    ; Once damage is set up...
  : JSR BtlMag_PrepHitAndDamage             ; Get hit/damage stuff into math buffers
  ; JMP BtlMag_ApplyDamage                  ; <- Flow into

Then BtlMag_PrepHitAndDamage:
   
Code: [Select]
LDA #MATHBUF_HITCHANCE
    LDX btlmag_hitrate
    JSR MathBuf_Add             ; math_hitchance += spell's base hit rate
   
    LDA #MATHBUF_HITCHANCE
    LDX btlmag_defender_magicdefense
    JSR MathBuf_Sub             ; math_hitchance -= defender's magdef
   
    LDA #0
    LDX math_basedamage
    JSR RandAX
    TAX                         ; X = rand[ 0, spelldamage ]
   
    LDA #MATHBUF_BASEDAMAGE
    JSR MathBuf_Add             ; math_basedamage = rand[ spelldamage, spelldamage*2 ]
   
    JSR Random_0_200
    JMP WriteAToMagRandHit      ; math_magrandhit = rand[ 0, 200 ]

I haven't edited these yet. I'm a little confused again here actually. It looks like math_basedamage is a 16 bit number, but then the second part is only doing the low byte of extra damage...? And only using the 8-bit Math buffer addition routine? So I should maybe use my new healing code here as well???

Code: [Select]
    LDA math_basedamage             ; low byte of healing
    STA tmp                         ; save in tmp
    LDA math_basedamage+1           ; high byte of healing
    LSR A                           ; shift far right bit out
    ROR tmp                         ; and into low byte of healing (dropping out far right bit)
    PHP                             ; push carry to the stack
    LDX tmp                         ; load shifted high/low into X
    LDA #0                          ; and clear A and tmp+1
    STA tmp+1                       ;
    JSR RandAX                      ; get a random number between high/low and 0
    ASL A                           ; shift the high bit out
    ROL tmp+1                       ; and into the empty tmp+1
    PLP                             ; pull the precious carry, if set
    ADC math_basedamage             ; add it in with the low byte of healing
    STA bath_basedamage             ; and save it
    LDA tmp+1                       ; then load up the high byte of the random number
    ADC math_basedamage+1           ; add in any carry from the low byte addition
    STA math_basedamage+1           ; and save THAT!
    ;; Example:
    ; %0000,0001 %0101,1011   ; $01, $5B (347)
    ; %0000,0000>%1010,1101>1 ; $00, $AD +1
    ;  push that lone 1 for | later
    ;  then random between  | $0 and $AD... lets say $54
    ;            %0101,0100 | ; $54      (84)
    ; %0000,0000<%1010,1000 v ; $00, $A8 (168)
    ;   |       $A8 + $5B + 1 = $01, $04 (260) (high byte is carry)
    ;   \-----> $00 + $01 + 1 = $02, $04 (516)
    ; that turns 357 healing into 516! An additional 159! (that's weird, shouldn't it be 168?)

Which confounds me at the end, since I have a missing 9 somewhere.
I know exactly what I'm doing. I just don't know what effect it's going to have.

I wrote some NES music! Its a legal ROM file. - I got a Ko-Fi page too.

Disch

  • Hero Member
  • *****
  • Posts: 2814
  • NES Junkie
    • View Profile
Re: FF1 MMC5 Disassembly Updates
« Reply #534 on: May 08, 2020, 11:37:28 pm »
Quote
I haven't edited these yet. I'm a little confused again here actually. It looks like math_basedamage

I haven't looked in a while so I might be wrong... BUT

From what I remember, I'm pretty sure all the damage stats for spells are a single byte wide.  IE:  there's only one "effectivity" byte.  Any damage done beyond that is done by adding onto that through the math buffer.

So when these routines START to calculate damage, just the effectivity byte is loaded into math_basedamage, meaning math_basedamage+1 would be zero'd out.

However, once it starts adding bonuses for crit damage, and extra damage from RNG calculations, then it grows into 2-byte territory.


Quote
that turns 357 healing into 516! An additional 159! (that's weird, shouldn't it be 168?)

The output here is exactly what I would expect.

It looks like your goal is to pick a random number between 1x and 2x the base healing (ie:  base healing stat of 300 will do between 300-600 healing).  Is that correct?

Your code looks to me like it's doing that just fine.

Even your example makes sense.  If your base rate is 347, and your randomly generated number is 84

347 + (84*2) = 515 ... plus an extra 1 for the carry (because 347 was odd) and you get 516.  Which is exactly what I would expect.  Not sure why you'd think it should be 9 higher than that.



EDIT:

But yeah to answer Vanya's question

Quote
So how do you actually get those damage ranges? (E...2E)
I always assumed that the RNG is used with the result being limited by E.

You're right.

The RNG produces 0..E

However if you add E to whatever the result is, that effectively changes the range to E..2E

This is a common programming thing... as virtually all integer RNGs will generate a number starting at zero.  So if you want a range of, say, 5-9 ... you'd actually generate 0-4 with the RNG and then just +5 to the result.
« Last Edit: May 08, 2020, 11:43:39 pm by Disch »

Jiggers

  • Sr. Member
  • ****
  • Posts: 405
    • View Profile
    • My Ko-Fi Page
Re: FF1 MMC5 Disassembly Updates
« Reply #535 on: May 09, 2020, 12:16:31 am »
Quote
It looks like your goal is to pick a random number between 1x and 2x the base healing (ie:  base healing stat of 300 will do between 300-600 healing).  Is that correct?

Yep!

But if I subtract 347 from 516 ... I get 169. ... but ... uh... I'm really confused. I swear I was doing the math each step of the way and I got 159 as the amount added with my example numbers... then it was 10 off, but I guess I went with 9 to ignore the added 1... Ughhh... WELL IT WORKS NOW in Windows Calculator.

And the damage stuff is odd, because when its doing the calculations for 1.5* damage, it does it with the full 16 bit base damage. But then AFTER that, it goes into BtlMag_PrepHitAndDamage, which does the E..2E thing, using only the low byte.

So if Intelligence+Focus+Elemental Weakness boosts the base damage into having a high byte, the player gets ripped off here?
I know exactly what I'm doing. I just don't know what effect it's going to have.

I wrote some NES music! Its a legal ROM file. - I got a Ko-Fi page too.

Disch

  • Hero Member
  • *****
  • Posts: 2814
  • NES Junkie
    • View Profile
Re: FF1 MMC5 Disassembly Updates
« Reply #536 on: May 09, 2020, 12:22:33 am »
So if Intelligence+Focus+Elemental Weakness boosts the base damage into having a high byte, the player gets ripped off here?

Yeah it sounds like it.

The original game code just isn't built to work with 2-byte effectivity, so if you are adding bonuses to push that into the 2 byte range BEFORE you jump to the original game code, then yeah it'll probably screw up.

But if you do the original game code first and add your bonuses on top afterwards, it could be okay.  Either that or you'll have to tweak the original code to work with 2 byte input.

Vanya

  • Hero Member
  • *****
  • Posts: 1750
    • View Profile
Re: FF1 MMC5 Disassembly Updates
« Reply #537 on: May 09, 2020, 08:08:54 pm »
I think I get most of it except how the RNG is able to give useful numbers if it's just a sequence of numbers.

Disch

  • Hero Member
  • *****
  • Posts: 2814
  • NES Junkie
    • View Profile
Re: FF1 MMC5 Disassembly Updates
« Reply #538 on: May 09, 2020, 10:44:11 pm »
Because unless you are looking EXTREMELY CLOSELY there's not a noticable pattern to the sequence.

Here is a LUT from FF1 which is basically one of the RNG sequences:



Yes, it's just a sequence of numbers, but if you were to get 1F, A6, DE, BA, CC, 12, etc... would you be able to guess that it's a sequence?  At a glance it's convincingly random, and so it works fine for most things.

Vanya

  • Hero Member
  • *****
  • Posts: 1750
    • View Profile
Re: FF1 MMC5 Disassembly Updates
« Reply #539 on: May 10, 2020, 05:09:28 am »
I get that, but if I'm trying to get a random number between 0 and 15 will it always return 15 when it grabs a number that is over 15? Or is there more to it?