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

Author Topic: Final Fantasy 1 Magic Upgrade Idea  (Read 16831 times)

Jiggers

  • Sr. Member
  • ****
  • Posts: 422
    • View Profile
    • My Ko-Fi Page
Final Fantasy 1 Magic Upgrade Idea
« on: January 31, 2017, 11:45:05 pm »
Hello! I am new here, and very nervous about posting!

I've been working on an FF1 project, started out just with sprites, branched out into magic, some equipment, and so on, thanks to all the tools that have been made for the game. It was just a goofy little thing for my friends, but the more I work on it, the more I feel serious about really wringing something new out of it! I started poking deeper, reading documents, poking at hex code... The last few days I even started trying to figure out Assembly! It's... scary. And depressing.

I had this idea, though, that I am not sure about: A check that, when the Earth Orb is lit, adds 10-20 effectivity to certain magic spells. So Fire goes from 10 to 30, becoming as strong as Fire 2, and so on.

I'm still blundering through the Disassembly trying to make sense of it, but in the meantime, I thought it best to ask if its even remotely possible. It seems like it should be, if that one INT fix patch is able to make spells more effective based on an even more nebulous variable!

Ideally, every lit orb would give the spells a boost, upgrading the spells throughout the game, so you could free up magic slots for other classes, or more variety of spells that make use of other weaknesses. No more Fire 3, if Fire 2 can eventually get as strong as Nuke!

So far the closest thing I've seen in the Disassembly would be this:

DoClassChange:
    LDA ch_class        ; simply bump up every party member's class ID number
    CLC                 ; to up them to the promoted version of their class
    ADC #6
    STA ch_class

    LDA ch_class+(1<<6)
    CLC
    ADC #6
    STA ch_class+(1<<6)

My next goal is to find out where the magic information is called, and set up a check before spells are cast... then either JMP to a second list of spells (if there's room in the code for it, which I gather there should be? Can we delete the whole minimap thing to free up room?) or... something more complicated that I can't conceive of yet.

Blundering. It's both fun and demoralizing at the same time!
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: Final Fantasy 1 Magic Upgrade Idea
« Reply #1 on: February 01, 2017, 11:30:01 am »
Quote
Hello! I am new here, and very nervous about posting!

Welcome!  No need to be nervous.  We don't bite.   :thumbsup:

Quote
I thought it best to ask if its even remotely possible.

Anything is possible unless it exceeds the capabilities of the NES.   =P
So yes.  This is possible.

Quote
So far the closest thing I've seen in the Disassembly would be this:

In a game of "hot/cold", you are definitely cold.  ;P

The routine you'd probably want to hook into is 'Battle_PrepareMagic' in bank $0C.

This code is at the very end of that routine:
Code: [Select]
    LDY #MAGDATA_EFFECTIVITY    ; and effectivity
    LDA (btl_magdataptr), Y
    STA btlmag_effectivity
   
    RTS

That 'LDA' loads the spell's strength.  So that's where you'd want to check the inventory to see if orbs are lit, and then add some value to the effectivity, before writing the new value to btlmag_effectivity.  So yeah -- you'd want to squeeze your code directly between that LDA and STA


So... good news and bad news.

- Bad news is that bank C doesn't have a whole lot of openly free space.
- Good news is that it does have a lot of areas where the code uses way more space than it needs to, and can be rewritten/shortened to create free space.
- Bad news is that being able to shorten the code requires at least an intermediate understanding of 6502
- More bad news is that this entire bank has probably been ripped apart and heavily rewritten by other hacks, like the 'Int' fix patch.  So any changes you make here will probably not be compatible with the Int patch.  At least not unless you use that patch as a base --- but then you don't have the benefit of working with the disassembly.



Anyway -- yes this is definitely do-able.  It's a tad ambitious for an asm noobie, but not impossible, and I think it'd definitely worth pursuing. :)


Quote
Can we delete the whole minimap thing to free up room?

Not really.  The problem with finding free space is it has to be in an accessible bank.  Due to the way memory works, only 2 banks are visible at any given time.  One of them is always the 'fixed' bank (bank F), and the other is swappable.

So if you're running code in bank C, the only banks that are accessible are C and F, so you'd need free space in one of those banks.  It is possible to switch to another bank, then switch back to C, but that's more work than it's worth (and also requires you freeing up some space for the switch).

There IS some go-to free space at the very end of bank F:

Code: [Select]
; Unused space

  .BYTE $FF, $ED, $FF, $FF
  .BYTE $FF, $FF, $FF, $FF
  .BYTE $FF, $FF, $FF, $FF
  .BYTE $FF, $FF, $FF, $FF
  .BYTE $FF, $1E, $FF, $00
  .BYTE $00, $00
  .BYTE "FINAL FANTASY"     ; <- FFHackster will make sure that is a 'Y'
  .BYTE $43, $70, $00, $00  ;  if it isn't, it won't allow the user to open the ROM
  .BYTE $48, $04, $01, $0C
  .BYTE $C3, $FF

You can put code there if you need -- but it's very likely that space has been used by other hacks -- so if you use it your patch might conflict with other patches.

Jiggers

  • Sr. Member
  • ****
  • Posts: 422
    • View Profile
    • My Ko-Fi Page
Re: Final Fantasy 1 Magic Upgrade Idea
« Reply #2 on: February 07, 2017, 07:48:58 am »
Ooo... so very helpful, thank you! Given me a lot to think over, but I haven't had much time to resume my blunderings...

Quote
In a game of "hot/cold", you are definitely cold.  ;P

Heehee, yeah. I meant to say that was in terms of finding code that changed values in an super obvious way.

Quote
So yeah -- you'd want to squeeze your code directly between that LDA and STA

I'm a little nervous about pushing things around. Is there anything I should worry about, like code elsewhere specifically saying "goto line X" and then I shift all of X over two spaces, and need to go change the other thing to say "goto line Z"?

I haven't actually been able to find the INT patch, except on a Gamefaqs thread, and haven't had the guts yet to go editing the hex directly to get that working (there was very little instructions for what to change, apart from some bolded text, but maybe it's clearer what to do once you're doing it.) Nor have I thought about making a patch compatible with other projects; I think that would definitely be beyond me. I'd be happy just to get it working in my own project first!

I'm actually wondering, another way I might like to try it, is to have spell strength relative to the character's level. Would it be worth getting the INT patch working to use as a base for that idea?

Either way, looks like I'm going to be spending the next few weeks getting intimate with bank C... Thanks again for explaining all that! I'm gonna try to do better research and have some work to show before I come limping back with more questions. :D
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: Final Fantasy 1 Magic Upgrade Idea
« Reply #3 on: February 08, 2017, 11:25:28 am »
I'm a little nervous about pushing things around.

If you're using the assembly, the risk is minimized, since the labels will adjust.  But it'll create a really big patch if code gets moved.  Biggest worry is running out of space.

TL;DR:  Yes that's something to worry about.  General practice is if you are inserting X bytes of new code, you need to delete X bytes.


What you could do in this case is replace the 'LDY/LDA' (which total 4 bytes of code) with a 'JSR/NOP' (also 4 bytes).  The JSR can jump to a block of free code where you can do all your computations, then RTS back with the desired magic effectivity in A, ready to be STA'd on the following line.

Quote
I haven't actually been able to find the INT patch,

In all honesty, I'd avoid it.  I'm sure it's a fine patch, but when making your own asm hacks, laying multiple patches on top of each other tends to cause all sorts of problems.

Quote
I'm actually wondering, another way I might like to try it, is to have spell strength relative to the character's level. Would it be worth getting the INT patch working to use as a base for that idea?

Up to you.  Though like I said, personally I'd try to avoid it.  Some might disagree with me, though.


Quote
Either way, looks like I'm going to be spending the next few weeks getting intimate with bank C... Thanks again for explaining all that! I'm gonna try to do better research and have some work to show before I come limping back with more questions. :D

That's cool.  But don't needlessly bang your head against the wall.  If you hit a stumbling block, it's better to ask than to spend hours being frustrated and not making any progress.  Plus, I love this crap and can talk about it for hours.

Don't be shy.  Asking questions are what these forums are for.  ;)

Jiggers

  • Sr. Member
  • ****
  • Posts: 422
    • View Profile
    • My Ko-Fi Page
Re: Final Fantasy 1 Magic Upgrade Idea
« Reply #4 on: March 19, 2017, 09:47:34 pm »
Uff. My post got deleted twice now... So I'll keep it brief.

What's the best way to JSR to $3FFDD, to use the space at the end of Bank F? I tried to make a label, but I either get "undefined" errors, or "already defined" errors. If I try adding the label to Constants or Variables, I get "illegal addressing mode"... And I just plain don't understand the high/low pointing thing in the ASM help files I've been reading. Feels like I need to learn another language to understand how to learn this stuff!

Part of what I'm not getting is that it seems to me going to $3FFDD or $03FFDD, that's at least 3 bytes? But all the addressing things I read only have examples with 2 bytes. Or else... each bank is set size, and the address doesn't necessarily point to an offset in the finished rom, but the point in the bank file, which I don't know how to figure out yet...?

Apologies for the frustrated tone... And thanks for your help!

Edit: Maybe it's not that at all! The "undefined label" error is referencing line 14281, which is "BEQ :+" ... this thing just hates my BEQ :+s! If I take them out and find some other way... Yay, progress! Memory overflow!

Edit 2: This is the fun part. It's like playing with Lego while blindfolded and wearing mittens.

OrbDamage:
   LDY #MAGDATA_EFFECTIVITY
   LDA item_lute
      LDA (btl_magdataptr), Y   
      CLC
      ADC $90
      STA btl_magdataptr
   LDA orb_fire
   LDA orb_water
   LDA orb_air
   STA btlmag_effectivity
   RTS

Without the Lute, Fire does 0 damage! With the Lute, Fire does 0 damage--to your party. XD I definitely don't have space to make each orb change the spell's properties with this setup, though. But I'll be happy to simply get the result I want...

Edit 3:
OrbDamage:
   LDY #MAGDATA_EFFECTIVITY
   LDA (btl_magdataptr), Y   
   LDA item_lute
      ADC #$90
   STA btlmag_effectivity
   RTS

THIS works!!! Fire does over 300 damage with that setup, haha. Now to test with a second item... (I hope these updates aren't annoying for the mods who have to check my post before it goes live! Sorry!)

Edit 4: Last edit!

Current code:
OrbDamage:
   LDY #MAGDATA_EFFECTIVITY
   LDA (btl_magdataptr), Y   
   LDA item_lute
      ADC #$45
   LDA item_tnt
      ADC #$95
   STA btlmag_effectivity
   RTS

Observations:
This doesn't actually check for if you have the items before boosting effectivity... I am also unsure if the second ADC is over-writing the first. With 90-95, a spell does over 300 damage. With 45, it does 150 damage. My brief test for both ADCs being 45 had the spells doing 200 damage, which may have been lucky/bad rolls.

Questions:
* Is ADC adding to the base effectivity, or over-writing it?

* Since BEQ :+ doesn't want to work, what's the best way to check you have an item before applying the boost?

* Since this JSR is set in the battle part, will it not work if you cast healing spells outside of battle?

* LDA (btl_magdataptr), Y ; What exactly is this doing? Is it doing math, adding (X) to Y? Or is it saying (X) = Y? Or something completely different?
« Last Edit: March 20, 2017, 03:26:00 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.

KingMike

  • Forum Moderator
  • Hero Member
  • *****
  • Posts: 7088
  • *sigh* A changed avatar. Big deal.
    • View Profile
Re: Final Fantasy 1 Magic Upgrade Idea
« Reply #5 on: March 20, 2017, 03:21:33 pm »
When you use LDA, you are loading the Accumulator ("A") register with a value. You have to store it somewhere (with a ST?, usually STA to some RAM address) or else you're not really doing anything.

Code: [Select]
  LDY #MAGDATA_EFFECTIVITY
   LDA (btl_magdataptr), Y   
   LDA item_lute
      ADC #$45
   LDA item_tnt
      ADC #$95
   STA btlmag_effectivity
   RTS

What you are doing here is doing "item_lute" + 0x45, and then trashing it by overwriting it with "item_int" + 0x95 and storing that to btlmag_effectivity, so btlmag_effectivity is always going to be item_int+0x95.
Are you trying to do a comparison or something?
"My watch says 30 chickens" Google, 2018

Disch

  • Hero Member
  • *****
  • Posts: 2814
  • NES Junkie
    • View Profile
Re: Final Fantasy 1 Magic Upgrade Idea
« Reply #6 on: March 21, 2017, 12:51:26 am »
:+ means "the next nameless label".  :- means "the previous nameless label"

Code: [Select]
:   ; <-  a nameless label
  JMP :-  ; <- jumps back to that label (infinite loop)


;;;; the above code is the same as this:
SomeLabel:
  JMP SomeLabel


Quote
Part of what I'm not getting is that it seems to me going to $3FFDD or $03FFDD, that's at least 3 bytes?

Don't confuse ROM offsets with CPU addresses.

All CPU addresses in Bank F fall between the range $C000-FFFF
All CPU addresses in all other banks fall between the range $8000-BFFF

The game will "swap" different banks in at different times, so the same address might refer to a different offset in the ROM at a different time depending on which bank is selected.

Though.. Bank F never moves.  So Anything you put in Bank F will always be accessible, and it's address will never change.

Quote
* Is ADC adding to the base effectivity, or over-writing it?

ADC performs addition.  It reads a byte from whatever value/address you give it, adds that value to the contents of the A register, and stores the resulting sum in the A register.

What you do with the sum after that depends entirely on what you do with the A register.

Quote
* Since BEQ :+ doesn't want to work, what's the best way to check you have an item before applying the boost?

BEQ will work... you just have to give it a label to jump to.  See notes above.

Quote
* Since this JSR is set in the battle part, will it not work if you cast healing spells outside of battle?

Unfortunately, In-battle and Out-of-battle magic has entirely different code.  So yeah you'll have to make changes elsewhere if you want to change out-of-battle magic behavior.

Quote
* LDA (btl_magdataptr), Y ; What exactly is this doing? Is it doing math, adding (X) to Y? Or is it saying (X) = Y? Or something completely different?

GENERAL EXPLANATION
LDA (some_address), Y performs an "indirect indexed" read.  This effectively let's you read from an address a pointer points to.

What this means is as follows:

1)  read 2 bytes from the given 'some_address'.  Let's call these 2 bytes 'ptr'
2)  Add the contents of the Y register to 'ptr'.  Let's call this sum 'final_addr'
3)  Read 1 byte from 'final_addr'.  Put the result in A

For example, say these locations in memory contain the given values:

Code: [Select]
memory | value
-------+------
 Y reg |  $0C
 $0010 |  $80
 $0011 |  $60
 $608C |  $AA

LDA ($10),Y will do the following:

- read the pointer '$6080' from the given address '$0010'
- Add Y to it, giving $608C'
- Read from that address (getting '$AA'), and putting that value in A.


SPECIFIC EXPLANATION
'btl_magdataptr' is a pointer which points to a block of magic data.
Y is [presumably] an index that indicates which data you want to read from that block.  In this case, since you are doing 'LDY #MAGDATA_EFFECTIVITY', Y is the index to the spell's effectivity.

So in this case.... LDY (btl_magdataptr),Y would read the effectivity for the current spell, and put that value in A.




Continuing with / elaboring on what KingMike is saying.... let's look at your code:

Code: [Select]
   LDY #MAGDATA_EFFECTIVITY     ; put effectivity index in Y
   LDA (btl_magdataptr), Y      ; put spell effectivity in A
   LDA item_lute                ; put quantity of lutes in A  (spell effectivity is no longer in A)
      ADC #$45                  ; Add $45 to A   (A is now quantity of lutes+$45)
   LDA item_tnt                 ; put quantity of TNTs in A (A no longer contains lutes+$45)
      ADC #$95                  ; Add $95 to A (A is now quantity of TNT+$95)
   STA btlmag_effectivity       ; Write A to btlmag_effectively, basicaly indicating you want to use that value as the spell's effectivity
   RTS

As you can see from the comments... most of this code is useless, as it's loading A with some value, only to immediately discard it and load A with something else.

The end result of this is that your magic will always have an effectivity of $95... or $96 if they currently have the TNT.

Jiggers

  • Sr. Member
  • ****
  • Posts: 422
    • View Profile
    • My Ko-Fi Page
Re: Final Fantasy 1 Magic Upgrade Idea
« Reply #7 on: March 26, 2017, 07:32:13 am »
Quote
Are you trying to do a comparison or something?

Whoops, no, I was just thoroughly confused and over-excited that I got it to compile at all! The plan was to use the Lute and TNT to test (plus 2 other items eventually), but when the code worked, swap them out for the 4 orbs. Then either have the act of having lit each orb, add 10 or 20 or 30 to the base effectivity of all magic--whatever balanced best. But I don't think I can make this fit at all without expanding the rom...

Quote
:+ means "the next nameless label".  :- means "the previous nameless label"

This is what I was missing. I was reading it as a kind of "if, then" thing... which I guess it still counts as? But not recognizing what the : below them was... Thank you so much, for all that explaining of everything! I think I'm actually starting to really grasp a few things now.

For instance, I got my second idea working: Using character level to boost the effectivity.

Code: [Select]
LDY #MAGDATA_EFFECTIVITY     
LDA (btl_magdataptr), Y      
STA btlmag_effectivity       
LDY #ch_level - ch_stats ; Copied from Bank C, load character's level
    LDA (btl_entityptr_obrom), Y       ; This part is important, or else it just loads up as a $26 or something...
    ;STA btlmag_attacker_unk6883 ; this part doesn't seem necessary!
ASL A ; Multiply A by 2
CLC                                            ; Clear the carry, whatever that is! Seems a safe practice to do.
ADC btlmag_effectivity ; Add previous battle magic effectivity to A? Looks like it works!
STA btlmag_effectivity    ; Store A as battle magic effectivity
RTS

So with that code in place, a level 5 character can cast Fire -- base effectivity 10 -- with 20 effectivity instead. Although for some reason my testing with a level 50 character doesn't quite get up to 110. Their level is $31 instead of $32... but the level 5 characters are $05. I thought I read that $00 is level 1, so... might've been something weird with my cheating to test it.

With that huge mental block out of the way, I think I'll be able to start working on something that should do my original plan! And if it simply gets too hard, I'll have this to fall back on...
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: Final Fantasy 1 Magic Upgrade Idea
« Reply #8 on: March 26, 2017, 11:28:48 am »
This is what I was missing. I was reading it as a kind of "if, then" thing... which I guess it still counts as?

Yeah.  BEQ/BNE/etc are basically if statements... only they are structured more like gotos rather than rigid code blocks like in modern high level languages.

Quote
Thank you so much, for all that explaining of everything! I think I'm actually starting to really grasp a few things now.

No probalo!  Here's more info:

Loading from btl_entityptr_obrom like you are will work great for players, but for enemies casting a spell you're probably going to read some random garbage value (enemies don't have a 'level' like players do).  So this might lead to damage magic having strange results for enemies.

Though... interestingly, you seemed to stumble upon btlmag_attacker_unk6883, which might be what you want to use instead of your first two lines.

From variables.inc:
Code: [Select]
btlmag_attacker_unk6883     = $6883 ; Enemy:  0                     Player:  level



Quote
Clear the carry, whatever that is! Seems a safe practice to do.

Weee!  Assembly math lesson!

So think back to 1st grade arithmetic.  Imagine if you were asked to add 18+24.  How would you do it?

You'd probably do this:
Code: [Select]
                    1               1
  18                18              18
+ 24         ->   + 24            + 24
____              ____            ____
   2                 2              42
   
Add the 1's       Raise           Add the tens
column first     the carry       column (with carry)

The "carry" adds an additional 1 to your column, and exists only if there was overflow that needs to be "carried" over to the next digit.



So the same concept exists on the 6502.  Only instead of relating to 'digits', it relates to 'bytes'

Imagine you want to add $1234 and $01F0 together.  You'd want to use ADC, right?  But wait!  The A register is 8 bits wide... so how can you add 16-bit numbers?  The answer is, you add them one byte at a time... and include the 'carry' to handle overflow between bytes.

There's an internal 'C' flag that will get set by ADC when the addition exceeds $FF (too big to fit in a single byte).  Also, if C is set when you do an ADC, it will add a +1 to the sum to "include the carry" in the addition.

So the addition works out like this:

Code: [Select]
                        C                       C
  $1234               $1234                   $1234
+ $01F0         ->  + $01F0             ->  + $01F0
_______             _______                 _______
  $  24               $  24                   $1424
     
low byte            addition raised         high byte
addition            the carry flag          addition.
($34 + $F0                                  +1 because
 = $24)                                     carry is set

That's why you'll often see a CLC before an ADC.  It's to ensure that the C flag is clear (no additional +1), unless the game is doing multi-byte addition... in which case it will want to include the C.

So your usage of CLC here is apt, and is the correct/safe thing to do.  Although technically speaking it might not be necessary, since the previous ASL is going to clear the C flag (unless you have players that are over level 127).  Still, I'd do the CLC anyway.





All of that said... here's a slightly smaller version of your routine, just so you can see an example of how to shrink your code:

Code: [Select]
LDA btlmag_attacker_unk6883     ; get player's level (or 0, if enemy)
ASL A                           ; *2

LDY #MAGDATA_EFFECTIVITY
CLC
ADC (btl_magdataptr),Y          ; + base effectivity

STA btlmag_effectivity          ; store result
RTS

This is 13 bytes, vs. your 20 bytes.

The trick here (apart from using unk6883) is to keep the running total in A so that you don't have to keep doing LDA/STA.  Since btlmag_effectvity is a 16-bit address, that means each access to it is 3 bytes.



Isn't 6502 fun?!?!  =D =D =D

Jiggers

  • Sr. Member
  • ****
  • Posts: 422
    • View Profile
    • My Ko-Fi Page
Re: Final Fantasy 1 Magic Upgrade Idea
« Reply #9 on: March 26, 2017, 11:58:24 pm »
Haha, yeah, it is kinda fun! When you finally understand stuff! The carry explanation is super helpful to visualize what's going on with it. I only just figured out how to use the debugger in FCEUX yesterday, which also helped me finally understand what a lot of the numbers were doing.

I actually was using "btlmag_attacker_unk6883" on its own, but I was running into a weird problem. It was the caster after the level 50 character who was dealing the heavy damage. I found this part:

Code: [Select]
PreparePlayerMagAttack:
    LDA btl_attacker
    AND #$7F
   
    LDX btlmag_magicsource
    BNE :+                          ; if zero, it's magic... so...
      LDA #$00                      ; ...play magic sfx.  Although this is slightly BUGGED
      JSR PlayBattleSFX             ;  since the sound effect is audible before you walk forward.  It
      LDA btl_attacker              ;  would make more sense to do this in WalkForwardAndCastMagic
      AND #$7F
     
  : LDX btlmag_magicsource          ; X=0 for magic (indicating we should do magic casting animation)
    JSR WalkForwardAndCastMagic
   
; Everything below this is loading the useless attacker stats.
    ;   As with PrepareEnemyMagAttack, this is all entirely pointless.
    LDA btl_attacker
    JSR PrepEntityPtr_Player
   
    LDY #ch_ailments - ch_stats
    LDA (btl_entityptr_obrom), Y
    STA btlmag_attacker_unk686C
   
    LDY #btlch_dmg
    LDA (btl_entityptr_ibram), Y
    STA btlmag_attacker_unk6875
   
    LDY #ch_level - ch_stats
    LDA (btl_entityptr_obrom), Y
    STA btlmag_attacker_unk6883
   
    LDY #btlch_hitrate
    LDA (btl_entityptr_ibram), Y
    STA btlmag_attacker_unk6884
   
    LDY #ch_class - ch_stats
    LDA (btl_entityptr_obrom), Y
    STA btlmag_attacker_unk6879
   
    RTS

And swapped "JSR WalkForwardAndCastMagic" to just before the RTS at the bottom. Which ended up having the fourth character doing animations instead of who should have been... Perhaps it would be better to just move the Level code to before the magic cast, and leave the rest? Or if this is all useless, can I delete it to make room so I don't have to use bank F for space? XD

Edit: So I gave swapping just the Level code up to before WalkForwardAndCastMagic and... same deal, spells were using the previous caster's level, or just plain 00 for the first cast. Plus weird graphical glitches like turning the Black mage's robes grey... Useless or not, maybe I shouldn't touch that stuff, heh.
« Last Edit: March 27, 2017, 12:46:07 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.

Disch

  • Hero Member
  • *****
  • Posts: 2814
  • NES Junkie
    • View Profile
Re: Final Fantasy 1 Magic Upgrade Idea
« Reply #10 on: March 27, 2017, 12:34:25 am »
AHA

Okay so Battle_PrepareMagic is the code that loads the effectivity.  And Battle_PrepareMagic is the first routine called when doing a magic attack.  And the code that's setting unk isn't run until Battle_PlayerMagic_CastOnTarget (which calls PreparePlayerMagAttack).

So btlmag_effectivity is being loaded before unk... which is why you're having this problem.


Also, I just realized that even if this works, it will change the effectivity for ALL spells, not just damage spells (is this your intent?).  And lastly ... this effectivity var is limited to a 1-byte value... so if your effectivity boost pushes it over 255 it'll wrap and make your magic extremely weak.



With all of that in mind -- I probably gave you bad advice originally when I told you to hook into Battle_PrepareMagic.  =x   Sorry!  A better time to inject this code would be when btlmag_effectivity is copied from that byte to the math buffer.  And there's actually a routine named "PutEffectivityInDamageMathBuf".  That would be a much more reasonable place to inject your code.  Plus it would allow you to have a 16-bit value instead of just an 8-bit value.  BUT!  It would only work for damage (and damage undead) spells, not for other kinds of spells.


As for deleting the unk stuff to free up space -- yes you can totally do that.  The unk stuff isn't used -- if it were I'd have figured out what it was and it would have changed the name so it wasn't 'unknown' anymore.  Though you might want to keep unk6883 so you can use it for this.

Jiggers

  • Sr. Member
  • ****
  • Posts: 422
    • View Profile
    • My Ko-Fi Page
Re: Final Fantasy 1 Magic Upgrade Idea
« Reply #11 on: March 27, 2017, 01:55:29 am »
Quote
Also, I just realized that even if this works, it will change the effectivity for ALL spells, not just damage spells (is this your intent?).

Kind of, but I haven't thought it through entirely. The whole purpose of this idea was to take out "duplicate" spells like Fire 2 and Cure 2 and Lock 2 and so on. Then sort of have most magic available in the first few towns, fewer spells for each class, then create new spells, so the Fighter and Thief and maybe even Black Belt classes can have their own tricks. If I got with Level as a magic booster, I'd need to make levelling up a bit faster so its feasible to reach level 50 (I never hit level 40 when playing the game normally as a kid, when I actually had TIME to grind) before the end. So, lots and lots of tweaking to come! On the other hand, if lighting each orb powered up magic spells instead, that'd be a lot easier to balance in the long run...

If I free up enough spell space, I could even make some more enemy-only spells, which would have a higher effectivity to compensate for their lack of level*2 boosting. If it comes to that.

I'll look into moving it to that spot, if I figure I need to, thanks! If I do it like that, there'll be a way to make a similar thing for healing spells, maybe?

For now, I've removed all that useless code, which freed up room to just do this:

Code: [Select]
    ;LDY #MAGDATA_EFFECTIVITY    ; and effectivity (for reference to where its placed)

LDY #ch_level - ch_stats
        LDA (btl_entityptr_obrom), Y
ASL A
LDY #MAGDATA_EFFECTIVITY
CLC
ADC (btl_magdataptr),Y     
STA btlmag_effectivity       
    RTS

My only worry at this point is that FFHackster is going to mess up. But I don't think it edits anything this far down in Bank C, right? And Bank C will fill its empty space at the end when it compiles the rom, so stuff after it will be fine too? So I should be able to just copy the pure hex code from this little hack into the file I've been working on, make sure its all the same size...
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: Final Fantasy 1 Magic Upgrade Idea
« Reply #12 on: March 27, 2017, 02:29:38 am »
I'll look into moving it to that spot, if I figure I need to, thanks! If I do it like that, there'll be a way to make a similar thing for healing spells, maybe?

Healing spells go through BtlMag_Effect_RecoverHP, which currently has a hard max recovery of 255 HP.  So you'd probably want to rewrite a chunk of that routine anyway if you're going to expand healing magic.

Quote
My only worry at this point is that FFHackster is going to mess up.

I wouldn't trust Hackster at all unless it's the only thing touching your ROM.  That was the very first "serious" program I ever wrote ---- it is extremely naive and will have no reservations about clobbering any custom changes you make.  I've been wanting to write a replacement program for years but I just can't find the time to dedicate to it.

Here's what I'd recommend:

1)  Keep a copy of the original ROM
2)  Make a copy that has only your asm changes
3)  Make a copy that has only your hackster changes
4)  Write a script (maybe in python -- or hell even just a .bat file) that automatically does the following:
- Creates an IPS of your asm ROM (using original as base)
- Creates an IPS of your hackster ROM (using original as base)
- Copies the original ROM
- Applies the hackster patch
- Applies the asm patch
5)  Run that script every time you make an asm or hackster change to produce your final ROM.


That way if anything gets clobbered, you have both your Hackster stuff and your Asm stuff separate.  No information is lost, and you can run the separated ROMs (and/or compare them) to help find where the conflict is.

Jiggers

  • Sr. Member
  • ****
  • Posts: 422
    • View Profile
    • My Ko-Fi Page
Re: Final Fantasy 1 Magic Upgrade Idea
« Reply #13 on: March 28, 2017, 09:56:50 am »
Aww... I saw some progress on Hackster 2 somewhere, I was so excited for it...

I got my original idea working! It's just a mess, though.

Code: [Select]
CLC
LDA orb_earth
BEQ :+ ; if Earth orb is not lit, check next orb?
ADC #$14    ; if it is, add 20
TAX ; store 20 in X
: LDA orb_fire
BEQ :+
TXA ; load any previous 20 from X
ADC #$14    ; add another 20
TAX ; now save as 40, if both orbs lit
:   LDA orb_water ; continue doing this...
BEQ :+
TXA
ADC #$14   
TAX
:   LDA orb_air
BEQ :+
TXA ; load previous additions from X
ADC #$14    ; Don't need to save back to X at this point, I think...
: TXA ; redundant if all 4 orbs lit, but otherwise stuff's stuck in X - X is still 0 if no orbs are lit
LDY #MAGDATA_EFFECTIVITY
ADC (btl_magdataptr),Y   ; + base effectivity
STA btlmag_effectivity   
RTS

It fits, but... wondering if there's a better way. I was trying to LDX the orbs to keep Accumulator from being wiped every check, but I couldn't find the address in the debugger at the time, and it was doing 800 damage with 2 orbs lit, obviously not right. So I don't know what the heck that was doing... lemme check...

Whaddya know, it does work, just the A was filled up with junk before the first addition. So let's fix that all up.

Code: [Select]
CLC ; Safety first!
LDA orb_earth
BEQ :+ ; if Earth orb is not lit, check next orb
ADC #$13    ; if it is, add 19 to the orb's 1, making 20
: LDX orb_fire ; Next three orbs can go into X! Nothing happens in X when casting magic!? (at leat offensive spells)
BEQ :+
ADC #$14    ; add another 20, or the first 20 to the 0 that is the non-lit earth orb
:   LDX orb_water ; continue doing this...
BEQ :+
ADC #$14   
:   LDX orb_air
BEQ :+
ADC #$14   
: LDY #MAGDATA_EFFECTIVITY
ADC (btl_magdataptr),Y   ; + base effectivity
STA btlmag_effectivity  ; ta-da!
RTS

I'm so proud! I even saved a byte by not using TXA to clear the accumulator first! Well, I guess I could have moved the LDY back to the top and re-used LDA instead of ADC to start with the base effectivity, but... then I wouldn't feel as clever.
« Last Edit: March 28, 2017, 10:23:20 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.

Disch

  • Hero Member
  • *****
  • Posts: 2814
  • NES Junkie
    • View Profile
Re: Final Fantasy 1 Magic Upgrade Idea
« Reply #14 on: March 28, 2017, 10:50:05 am »
Awesome!  Congrats!   :beer:  A real, honest-to-goodness asm hack of your very own!

My general stance is "if it works, it's good enough" -- but since you explicitly asked if there was a better way.... you might consider a "lookup table" (aka LUT):

Code: [Select]
CLC
LDA orb_earth
ADC orb_fire
ADC orb_water
ADC orb_air                 ; A is now 0-4, indicating the number of orbs lit

TAX                         ; throw that in X
LDA @orbMagicBoost_lut, X   ; use it to index our lut (effectively multiplying by 20)

LDY #MAGDATA_EFFECTIVITY
ADC (btl_magdataptr),Y      ; add to effectivity
STA btlmag_effectivity      ; save it
RTS

@orbMagicBoost_lut:              ; our lookup table
    .byte  0, 20, 40, 60, 80

Though this isn't really much "better", as it's a comparable size.  I think it's only like ~5 bytes smaller than your code.


EDIT:

I also want to say it's really awesome to see you stick with this, learn how to do what you want, and actually get it working.  A lot of people would have given up, but you kept at it!  Now you're on you way to being an asm pro!   :thumbsup:  Kudos!
« Last Edit: March 28, 2017, 10:56:44 am by Disch »

Jiggers

  • Sr. Member
  • ****
  • Posts: 422
    • View Profile
    • My Ko-Fi Page
Re: Final Fantasy 1 Magic Upgrade Idea
« Reply #15 on: March 28, 2017, 11:33:26 am »
Hee, thanks! I thought I did give up a few times. Spent more than a few hours staring blankly at things... Kept asking myself what the heck I'm doing, going through all this trouble for a silly game maybe 3 people will play... Guess I just had nothing better to do! And now that I do have this, I feel a lot more inspired to keep chugging away at my other ideas, maybe actually turn the game from a graphics/magic edit into a new story, new maps, get a friend to draw up some new monster sprites...

I get why the site says to do the work and not simply ask--I got real close a few times. "Why'm I spending hours doing this! I bet he could do it in seconds!" But... yeah! I feel great now! I am the smartest person alive!!

And hey, 5 bytes is 5 bytes! I may need that space. :D I think I understand what a LUT is now, too, which is great, because at one point I was naming my label "lut_OrbDamage" as if that might define it...

".byte  0, 20, 40, 60, 80"

If I'm getting it right, 0 orbs, 0 boost. 1 orb, 20 boost, and so on... What if I changed these numbers?

".byte  0, 20, 30, 50, 60"?

So the second orb you light only gives a 10 boost instead of 20, then third is 20 again, then fourth is 10. Is that what would happen with those bytes?
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: Final Fantasy 1 Magic Upgrade Idea
« Reply #16 on: March 28, 2017, 12:24:36 pm »
If I'm getting it right, 0 orbs, 0 boost. 1 orb, 20 boost, and so on...

Yep.  Your understanding of that is 100% correct.   :D

Jiggers

  • Sr. Member
  • ****
  • Posts: 422
    • View Profile
    • My Ko-Fi Page
Re: Final Fantasy 1 Magic Upgrade Idea
« Reply #17 on: March 29, 2017, 10:21:31 am »
*strut*

Round 2! Healing from the menu!

I'm going to transfer @orbMagicBoost_lut to Bank F, because... I'll need it in Bank E as well. Since I'm doing away with HEL2, HEL3, CUR2, CUR3, and possibly even CUR4, I have a WHOLE lotta space to work with, but still.

Code: [Select]
UseMagic_HEAL:
LDA orb_earth
ADC orb_fire
ADC orb_water
ADC orb_air               
TAX                     
LDA framecounter        ; use the framecounter as a makeshift pRNG
AND #$07                ; get low bits, or max range (+7)
ADC @orbMagicBoost_lut, X ; increase max range? or do AND again instead?
CLC
ADC #$10                ; Minimum of 16 (roll between 16-23)
ADC @orbMagicBoost_lut, X ; increase base minimum, I hope!
TAX ; store back in X for a moment...
LDY #ch_level - ch_stats ; Bringing this back for a nice 1-50 boost. No x2 this time.
LDA (btl_entityptr_obrom), Y ; Will this work outside of battle? Have a bad feeling it won't...
;BNE UseMagic_HealFamily ; Don't need this, it'll flow.

UseMagic_HealFamily:
    STA hp_recovery, X   ; store HP recovery for future use, adding the RNG roll with X with character's (hopefully the caster's) level

I haven't tested this yet... Gonna go do it... once I figure out how .import and .export works. Hm... It doesn't seem to like the @ in @orbMagicBoost_lut; is it necessary? I tried taking it out. It compiled!

Does the .import and .export stuff add bytes to the final product, or is it all invisible stuff that somehow gets cleaned up?

Update: So... that was a wild ride of nonsense. Couldn't make sense of what the debugger was telling me, so I thought I'd go back and re-add the @, and also just keep the whole table nearby in Bank C, remove all the import/export stuff. Now the build.bat thing is telling me "@orbMagicBoost_lut" is undefined... I'm using it exactly the way it was! I put the BNE back and put the LUT right there above UseMagic_HealFamily, it doesn't exist anywhere else but there now...
« Last Edit: March 29, 2017, 11:03:06 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.

Disch

  • Hero Member
  • *****
  • Posts: 2814
  • NES Junkie
    • View Profile
Re: Final Fantasy 1 Magic Upgrade Idea
« Reply #18 on: March 29, 2017, 12:34:03 pm »
ca65 stuff.

The '@' prefix means it's a local symbol.  Local symbols are only visible between global labels:

Code: [Select]
SomeRoutine:  ; <- a global label
   LDX somecode
   LDA @local, X
   RTS

@local:
   .byte 1, 2, 3

AnotherRoutine:  ; <- another global label -- '@local' is no longer visible beyond this point
   LDA @local   ;<- will generate an error

This simulates smaller scope, which is useful in larger projects, so you don't have to worry about name conflicts.  IE, you can use a generic label like '@loop:' at many, many different places in your code because they'd all be local to whatever routine they're in... but you can only have one 'loop:' because it's visible everywhere in the file.



As for export/import ... this also has to do with visibility.  Labels are only visible in whatever .asm file they exist in.  Unless you export them -- in which case they also become visible in whatever file imports them.

So it sounds like you want to do this:
1)  make orbMagicBoost_lut global by removing the '@'
2)  move it to bank F
3)  export it from bank F
4)  import it from bank C (and E)



As for your code:

Code: [Select]
UseMagic_HEAL:
LDA orb_earth
ADC orb_fire            ; Probably should CLC before these ADCs
ADC orb_water
ADC orb_air               
TAX                     ; You're making X =  [0..4] for the # of orbs.  Though... at this point X is the index to
                        ;  the caster's stats!  So you absolutely don't want to clobber it.  You'll need
                        ;  it to subtract MP, and also you can use it to get the caster's level later.
                        ;  Maybe use Y instead?

LDA framecounter            ; the "framecounter & 7" is the range only because it's the only variable
AND #$07                    ;  or randomized part of this forumla.  As of now, A is in the range [0 .. 7]

ADC @orbMagicBoost_lut, X   ; by adding orb boost, A is now ranged [orbBoost .. orbBoost+7]
CLC                         ; (probably don't need this CLC here -- move it up to top of routine)
ADC #$10                    ; adding $10... new range:  [orbBoost+$10 .. orbBoost+$17]
ADC @orbMagicBoost_lut, X   ; new range:  [2*orbBoost + $10 ... 2*orbBoost + $17]
TAX

LDY #ch_level - ch_stats
LDA (btl_entityptr_obrom), Y    ; This definitely will not work out of battle.  BUT... if you don't clobber
                            ;  X, it's the index to caster's stats.  So you can just do:
                            ;  "LDA ch_level, X" to get the caster's level.
                           
                            ; regardless, at this point you have tossed aside your calculated HP range and
                            ;   now A = player_level. Meaning you will only recover HP equal to the target's
                            ;   current level.
                           
;BNE UseMagic_HealFamily

UseMagic_HealFamily:
    STA hp_recovery, X  ; There's no RNG beyond this point.  The only randomness is the framecounter.  Everything
                        ;   else is fixed.  Also, X has been clobbered by your TAX's, so this is not STAing to the proper
                        ;   memory location.  This will just end up overwriting random memory -- which
                        ;   will create very, very strange behavior.

If you want a broader range, there's a routine in ?bank C? called RandAX that you might want to export.  The idea is you put
the max value in X, the min value in A, and it gives you a random value between [A..X].

But again, note that doing that will clobber X.  So maybe back X up beforehand:

Code: [Select]
STX tmp         ; backup X because we need to clobber it

LDA whatever
ADC whatever    ; make A your desired base

PHA             ; back it up

ADC whatever
ADC whatever    ; add a bunch more crap to get the max
TAX             ; X is the max
PLA             ; restore A to the base

JSR RandAX      ; A is now a randomized value between your desired range
LDX tmp         ; un-clobber X

; .. proceed with original game code


Jiggers

  • Sr. Member
  • ****
  • Posts: 422
    • View Profile
    • My Ko-Fi Page
Re: Final Fantasy 1 Magic Upgrade Idea
« Reply #19 on: March 31, 2017, 12:07:15 pm »
Quote
So it sounds like you want to do this:
1)  make orbMagicBoost_lut global by removing the '@'
2)  move it to bank F
3)  export it from bank F
4)  import it from bank C (and E)


Hah, that's exactly what I did! I just didn't think it was working because the other dumb stuff I was trying to do at the same time... That's a very good and useful lesson though! So @labels are like footnotes in books, almost... Every page can have its own¹ or², but that's wiped out on the next page...

Gotta admit, I'm not a big fan of the random aspect with healing. I noticed that it chooses the effectivity before even asking if you want to use it, so maybe I can slip in the "hp_recovery" byte into the text, so you know how much it'll heal before you commit... That might be too much effort, if I can simply take it out entirely. And I THINK I worked out the formula here... How's this look?

Code: [Select]
UseMagic_HEAL:
CLC
LDA orb_earth
ADC orb_fire
ADC orb_water
ADC orb_air               
TAY         
           
LDA framecounter         ; use the framecounter as a makeshift pRNG
AND #$14                 ; get low bits, or max range of +20 (0 .. 20)
ADC #$14                 ; add 20 to both min and max (roll between 20 .. 40)
ADC orbMagicBoost_lut, Y ; add orb boost (Orb+20 .. Orb+40)
ADC ch_level, X ; Add caster's level (Level+Orb+20 .. Level+Orb+20)

UseMagic_HealFamily:
    STA hp_recovery 

So... Using the orb boost as 0, 20, 35, 55, 65:
0 orb= 20 .. 40 ; at level 20= 40 .. 60
1 orb= 40 .. 60
2 orb= 55 .. 75
3 orb= 75 .. 95
4 orb= 85 .. 105 ; at level 50=135 .. 155

I hope I got this right. Math and allergies don't mix, I can barely keep my eyes open. But I do like how when I take out all the dumb stuff I tried to do, it just gets smaller and smaller and more neat looking! Thank you so much for the note about X being used. I need to learn to trace that before I go poking into things.

Ooh, alternate code:

Code: [Select]
UseMagic_HEAL:
CLC
LDA orb_earth
ADC orb_fire
ADC orb_water
ADC orb_air               
TAY         

LDA framecounter         ; use the framecounter as a makeshift pRNG
AND @orbHealBoost_lut, Y ; make the higher part of the thing be a different LUT!
ADC #$14                 ; Minimum of 20 (roll between 20 .. 40)
ADC orbMagicBoost_lut, Y ; increase base minimum
ADC ch_level, X ; Add caster's level to both...

;using the orb boost as 0..20, 20..30, 35..40, 55..50, 65..60:
;0 orb= 20 .. 40 ; at level 20= 40 .. 60
;1 orb= 40 .. 70
;2 orb= 55 .. 95
;3 orb= 75 .. 125
;4 orb= 85 .. 145 ; at level 50=135 .. 195
; I have no idea if any of this math is accurate at this point, its nearly 3 AM

BNE UseMagic_HealFamily ; SUDDENLY I NEED THIS

@orbHealBoost_lut:
.BYTE 20, 30, 40, 50, 60

UseMagic_HealFamily:
    STA hp_recovery 

Hm, I don't think its getting the character level, though. The debugger says: ADC $6126, X @ $61AE = #$00
Character's in the third slot, X is 88.


Fixed it by doing "ADC ch_level-8, X" I guess X is off by 8 at some point? Maybe from selecting the spell... So far Heal has been in the 1st slot for level 3, so I'm gonna check... Yep, it works for Cure in slot 1, level 1, too. And all the different character positions, yay!

So what exactly is the framecounter? I see it popping up all over but haven't found the description of how it works just yet.

And, does spell accuracy have any effect? Lots of 18 for attack spells, 18 or 40 for negative effects, then things go weird with the Level 8 spells (and some others, like QAKE and BANE)... If I wanted negative effects to have a higher chance of working, would increasing this byte work, or is it only about enemy's HP or something?

Whup, another question--as I was reading up on spell accuracy, it sounded like it was saying that for things like stun/sleep/dark, the effectivity byte tells it what status effects to do. So boosting that byte is gonna play havoc with status spells? If so, then... I should take your advice and edit the magic damage part, and healing power somewhere else...
« Last Edit: April 01, 2017, 03:56:22 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.