Users browsing this thread: 1 Guest(s)
Condensing Spell List in Battle

#51
Posts: 175
Threads: 11
Thanks Received: 10
Thanks Given: 8
Joined: May 2013
Reputation: 13
Status
Well-Fed
Hah, I like that. I may sit down with it and see if I can work out my own solution for it, too, just as an exercise.

Barring any further optimisations, this should be the definitive version of the patch; the first function is down to 77 bytes, and the second is down to 79.

Code:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Condensed Spell Lists v1.5
; Author: Gray Shadows (in.the.afterlight@gmail.com
; Applies to Final Fantasy 3/6us v1.0
;
; Urgency: Low - QoL enhancement
;
; Contributors: assassin (code support and optimisation)
;               seibaby (testing and problem solving)
;               Warrax (testing and bug identification)
;
; In vanilla FF6, because of the way the game builds spell lists in
; battle, if one character in battle knows a lot of spells and another
; character doesn't, that second character will have a lot of blank
; space in their Magic menu. Condensed Spell Lists adds some additional
; code to battle initialisation that takes a character's spell list
; and 'shuffles' it up, so that all of the blank spots are at the end.
; It also resorts the Lore list in the same manner.
;
; Version 1.5 update includes: bugfixes, including an error introduced
; wherein monster abilities were incorrectly costing MP, as well as a
; fatal crashing error caused by Interceptor counter-attacks. The hack
; should now properly account for all abilities that use the Magic
; command but do not cast from a character's spell list. (This should
; only be Interceptor counter-attacks, and possibly Desperation Attacks.)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

hirom
;header

!freespace_C2_0 = $C2A65A        ; 77 bytes needed
!freespace_C2_1 = $C2FAB0        ; 79 bytes needed

org $C2256D                
JSR condenseSpellLists
; This was originally a JSR to modify available commands; we'll be JMPing to
; that at the end of the modified code so that RTS comes back to the right spot.

org $C24F2E
JMP calculateMPDeduction
; This jumps in the middle of a function at C2/4F08 that grabs the MP cost for
; a spell. A character's individual spell list in battle stores a modified MP
; cost based on modifying relics, and as we're reshuffling the list, we need to
; add additional code to make sure that the game looks for MP costs in the right
; location.
;
;



                            
org !freespace_C2_0
print "   "
print "New function CondenseSpellLists starts at: ",pc
reset bytes

condenseSpellLists:
PHX                ;
PHP
LDY #$04        ; The 0 index in the list holds the equipped esper, which we're not touching.
                ; Each entry is four bytes long, so the spell list starts at Y = 4.
.noMoreSpells    ; We'll be branching back here to execute our Lore list
TYX                ; X is going to be our 'write-space' index, whereas Y is our 'read-space' index.
REP #$10

    .checkSpellLoop
    LDA ($F2),Y
    INC
    BNE .checkNextSpell

        .findNextSpell
        INY #4
        CPY #$00DC            ; If we've hit the first Lore slot, there are no more spells to copy back,
        BEQ .noMoreSpells    ; so jump out, reset X to start sorting the Lore list instead.
        CPY #$013C            ; This is after the last Lore slot, so if we've gone that far, there are
        BEQ .noMoreLores    ; no more spells to copy back and we can exit the function entirely.
        LDA ($F2),Y
        INC
        BEQ .findNextSpell

    PHY            ; we'll be pushing and pulling within the loop, but we need to know
                ; where it started so we can blank out the slot we copied from
    CLC
    REP #$20
    
        .copyNextSpell
        LDA ($F2),Y    
        PHY            ; This stores our Y location, i.e. the next slot with a spell learned
        TXY            ; and pulls our X, or the blank slot we're writing to.
        STA ($F2),Y
        PLY            ; back to our 'write from' location
        INY    #2        ; and gets the next bytes
        INX #2
        BCS .doneCopy
        SEC
        BPL .copyNextSpell    ; if we haven't done four bytes, loop back and grab the next
        .doneCopy
        
    SEP #$20    
    PLY            ; this is the first byte of the slot we copied from
    TDC
    STA ($F4),Y    ; this zeroes out the MP cost
    DEC
    STA ($F2),Y    ; and blanks out the spell we copied from
        
    BRA .weCopiedASpell
    .checkNextSpell
    INX #4
    .weCopiedASpell
    TXY                ; and then copy it over to Y for our next loop through
    CPY #$0138
    BNE .checkSpellLoop
    
.noMoreLores
PLP    
PLX
JMP $532C

print "CondenseSpellLists ends at: ",pc," and used ",bytes," bytes of space."
print "   "



org !freespace_C2_1
calculateMPDeduction:

print "New function CalculateMPDeduction starts at: ",pc
reset bytes

LDA $3A7A                ; (get command #)
CMP #$19
BEQ .calculateSummon    ; (branch if it's Summon)
CMP #$0C
BEQ .calculateLore        ; (branch if it's Lore)

.calculateMagic
LDA $3A7B                ; (get attack #)
CMP #$F0                ; is it a Desperation Attack or an Interceptor counter?
BCS fka_4F53            ; if so, exit
STA $F0                    ; save our spell ID in scratch memory
LDA #$04                ; four bytes per index, and we're starting at the second index in
                        ; the list (i.e. the first Magic spell)

.loreEntersHere
REP #$20
CLC
ADC $302C,X                 ; get the start of our character's magic list (index #0 is esper)
STA $F2                        ; this points out our first Magic slot
INC #3
STA $F4                        ; and this points at our first MP cost slot
SEP #$20
PHY
LDY $00

    .findSpell
    LDA ($F2),Y
    CMP $F0
    BEQ .getMPCost
    INY #4
    BRA .findSpell
    
.getMPCost
LDA ($F4),Y
PLY
BRA fka_4F45

.calculateLore
LDA $3A7B                    ; (get attack #)
SEC
SBC #$8B                    ; turn our raw spell ID into a 0-23 Lore ID
STA $F0
LDA #$DC                    ; this is our first Lore slot in the character's spell list
BRA .loreEntersHere

.calculateSummon
TDC                    ; By zeroing A, we can just jump back into the vanilla code
JMP $4F38             ; and it will load the MP cost from the first index in the list,
                    ; i.e. the equipped esper, which SHOULD be the only ability being
                    ; cast with the Summon command.



fka_4F45: JMP $4F54            ; (clean up stack and exit)
fka_4F53: JMP $4F53

print "CalculateMPDeduction ends at: ",pc," and used ",bytes," bytes of space."
print "   "


Current Project: FF6: Tensei | Discord ID: TristanGrayse
  Find
Quote  

#52
Posts: 200
Threads: 1
Thanks Received: 10
Thanks Given: 0
Joined: Oct 2015
Reputation: 18
Status
None
between the original code and the calculateMPDeduction function, it's a bit of an "LDA $3A7N" fest: five of those 3-byte bastards.

so i'm thinking after C2/4F22, we add an "XBA".  and replace all "LDA $3A7N"s in your function with that same instruction.

we're mostly past the point where we need the 0 default MP Cost held in A.top.  i believe the only case that still needs it is the Desperation Attack or an Interceptor counter being detected.  and for that, we can achieve the 0 manually with a "TDC" or "LDA #$00".

aaanndd.. because there is only that one case of branching to "fka_4F53", we ought to be able to chuck that label (and its JMP), replacing it with a "return_zero:" label before the "fka_4F45" (which .calculateSummon no longer proceeds to).

if the fallback MP cost in A.top were something *variable*, this could get bigger and/or messier, but we know it's a zero.  we also know that entering C2/4F53, A.bottom holds meaningless crap (as it varies by the path taken through C2/4F08) that we shouldn't need to preserve/duplicate.
Quote  

#53
Posts: 2,548
Threads: 98
Thanks Received: 147
Thanks Given: 158
Joined: Aug 2009
Reputation: 52
Status
Nattak\'d
Great work, I am really fond of this patch and was able to find space for it after removing Umaro's throw character special attack lol.


We are born, live, die and then do the same thing over again.
Quote  

#54
Posts: 175
Threads: 11
Thanks Received: 10
Thanks Given: 8
Joined: May 2013
Reputation: 13
Status
Well-Fed
How much more space do you need before you can fit them both? If you want to keep the throw attack, that is. Laugh I'll be looking at assassin's further optimisation suggestions later, and you never know how small we're going to be able to get this... I'm definitely looking at how much I can keep in the vanilla codeblock, rather than JMPing out.

calculateMPDeduction is now down to 64 bytes of free space! Summons are now handled entirely in-line, so we're only JMPing away for Magic/X-Magic and Lores. 

I was getting garbage MP costs for a bit when casting magic, because I'm a moron who forgot to TDC after storing the attack ID in $F0. Otherwise, the command ID in the high byte of A was getting added to the starting address for a character's magic list, sending it 200h bytes too high. Oops? Everything looks to be working fine now, though -- no crashes, Interceptor is working fine still, and Summons, Lore, and Magic are all properly deducting MP when cast.

Code:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Condensed Spell Lists v1.5
; Author: Gray Shadows (in.the.afterlight@gmail.com
; Applies to Final Fantasy 3/6us v1.0
;
; Urgency: Low - QoL enhancement
;
; Contributors: assassin (code support and optimisation)
;               seibaby (testing and problem solving)
;               Warrax (testing and bug identification)
;
; In vanilla FF6, because of the way the game builds spell lists in
; battle, if one character in battle knows a lot of spells and another
; character doesn't, that second character will have a lot of blank
; space in their Magic menu. Condensed Spell Lists adds some additional
; code to battle initialisation that takes a character's spell list
; and 'shuffles' it up, so that all of the blank spots are at the end.
; It also resorts the Lore list in the same manner.
;
; Version 1.5 update includes: bugfixes, including an error introduced
; wherein monster abilities were incorrectly costing MP, as well as a
; fatal crashing error caused by Interceptor counter-attacks. The hack
; should now properly account for all abilities that use the Magic
; command but do not cast from a character's spell list. (This should
; only be Interceptor counter-attacks, and possibly Desperation Attacks.)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

hirom
;header

print " "

!freespace_C2_0 = $C2A65A        ; 77 bytes needed, used through $C2A6A6
!freespace_C2_1 = $C2FAB0        ; 64 bytes needed, used through $C2FAEF

org $C2256D                
JSR condenseSpellLists
; This was originally a JSR to modify available commands; we'll be JMPing to
; that at the end of the modified code so that RTS comes back to the right spot.

org $C24F24
print "Replacement Function updated_4F08 starts at: ",pc
reset bytes

updated_4F08:
XBA                ; high byte now holds the command ID
REP #$10
LDA $3A7B            ; and low byte now holds spell/attack ID
CPX #$0008
BCS fka_4F47
XBA                ; high is spell, low is command
CMP #$19
BEQ .summon
JMP calculateMPDeduction
NOP

.summon
CLC
TDC
REP #$20

print "Replacement Function updated_4F08 ends at: ",pc," and used ",bytes," bytes of space."
print " "

org $C24F47
fka_4F47:

; This jumps in the middle of a function at C2/4F08 that grabs the MP cost for
; a spell. A character's individual spell list in battle stores a modified MP
; cost based on modifying relics, and as we're reshuffling the list, we need to
; add additional code to make sure that the game looks for MP costs in the right
; location.



                            
org !freespace_C2_0
print "New function CondenseSpellLists starts at: ",pc
reset bytes

condenseSpellLists:
PHX                ;
PHP
LDY #$04        ; The 0 index in the list holds the equipped esper, which we're not touching.
                ; Each entry is four bytes long, so the spell list starts at Y = 4.
.noMoreSpells    ; We'll be branching back here to execute our Lore list
TYX                ; X is going to be our 'write-space' index, whereas Y is our 'read-space' index.
REP #$10

    .checkSpellLoop
    LDA ($F2),Y
    INC
    BNE .checkNextSpell

        .findNextSpell
        INY #4
        CPY #$00DC            ; If we've hit the first Lore slot, there are no more spells to copy back,
        BEQ .noMoreSpells    ; so jump out, reset X to start sorting the Lore list instead.
        CPY #$013C            ; This is after the last Lore slot, so if we've gone that far, there are
        BEQ .noMoreLores    ; no more spells to copy back and we can exit the function entirely.
        LDA ($F2),Y
        INC
        BEQ .findNextSpell

    PHY            ; we'll be pushing and pulling within the loop, but we need to know
                ; where it started so we can blank out the slot we copied from
    CLC
    REP #$20
    
        .copyNextSpell
        LDA ($F2),Y    
        PHY            ; This stores our Y location, i.e. the next slot with a spell learned
        TXY            ; and pulls our X, or the blank slot we're writing to.
        STA ($F2),Y
        PLY            ; back to our 'write from' location
        INY    #2        ; and gets the next bytes
        INX #2
        BCS .doneCopy
        SEC
        BPL .copyNextSpell    ; if we haven't done four bytes, loop back and grab the next
        .doneCopy
        
    SEP #$20    
    PLY            ; this is the first byte of the slot we copied from
    TDC
    STA ($F4),Y    ; this zeroes out the MP cost
    DEC
    STA ($F2),Y    ; and blanks out the spell we copied from
        
    BRA .weCopiedASpell
    .checkNextSpell
    INX #4
    .weCopiedASpell
    TXY                ; and then copy it over to Y for our next loop through
    CPY #$0138
    BNE .checkSpellLoop
    
.noMoreLores
PLP    
PLX
JMP $532C

print "CondenseSpellLists ends at: ",pc," and used ",bytes," bytes of space."
print " "



org !freespace_C2_1
calculateMPDeduction:

print "New function CalculateMPDeduction starts at: ",pc
reset bytes

CMP #$0C                ; coming in, low byte is command and high byte is spell ID
BEQ .calculateLore        ; (branch if it's Lore)

.calculateMagic
XBA                        ; (get attack #), high byte is now command and low byte is attack ID
CMP #$F0                ; is it a Desperation Attack or an Interceptor counter?
BCS .returnZero            ; if so, exit
STA $F0                    ; save our spell ID in scratch memory
TDC
LDA #$04                ; four bytes per index, and we're starting at the second index in
                        ; the list (i.e. the first Magic spell)

.loreEntersHere
REP #$20
CLC
ADC $302C,X                 ; get the start of our character's magic list (index #0 is esper)
STA $F2                        ; this points out our first Magic slot
INC #3
STA $F4                        ; and this points at our first MP cost slot
SEP #$20
PHY
LDY $00

    .findSpell
    LDA ($F2),Y
    CMP $F0
    BEQ .getMPCost
    INY #4
    BRA .findSpell
    
.getMPCost
LDA ($F4),Y
PLY
BRA .exitWithMP

.calculateLore
XBA                            ; (get attack #), high byte is command and low byte is attack ID
SEC
SBC #$8B                    ; turn our raw spell ID into a 0-23 Lore ID
STA $F0
TDC
LDA #$DC                    ; this is our first Lore slot in the character's spell list
BRA .loreEntersHere

.returnZero
TDC
.exitWithMP
JMP $4F54            ; (clean up stack and exit)


print "CalculateMPDeduction ends at: ",pc," and used ",bytes," bytes of space."
print " "


Current Project: FF6: Tensei | Discord ID: TristanGrayse
  Find
Quote  

#55
Posts: 2,548
Threads: 98
Thanks Received: 147
Thanks Given: 158
Joined: Aug 2009
Reputation: 52
Status
Nattak\'d
This latest version, at least for me, is acting strange, from testing the beginning trek through Narshe, using an item causes a 'not enough MP' to happen in battle, and selecting a Magitek attack freezes the game, while the previous code you posted before this was working fine. I'll double check I patched correctly here. There's still just the 2 free space parts needed in C2, right? Maybe something with this latest code conflicts with my ROM perhaps?

Oh and I'm okay with scrapping the Umaro throw in my hack, no problem there!


We are born, live, die and then do the same thing over again.
Quote  

#56
Posts: 200
Threads: 1
Thanks Received: 10
Thanks Given: 0
Joined: Oct 2015
Reputation: 18
Status
None
[deleted.  i can't read.]
Quote  

#57
Posts: 175
Threads: 11
Thanks Received: 10
Thanks Given: 8
Joined: May 2013
Reputation: 13
Status
Well-Fed
It must be conflicting with something else, both items and Magitek are working perfectly fine when I patch on vanilla. It is just the two free space bits in C2, and the second one is smaller here than it's ever been before.

The items thing is what's really confusing me, because I don't believe they should be going anywhere NEAR the code I've modified. Have you changed the command IDs for anything? Is Magitek running off of a different command ID than in vanilla?


Current Project: FF6: Tensei | Discord ID: TristanGrayse
  Find
Quote  

#58
Posts: 2,548
Threads: 98
Thanks Received: 147
Thanks Given: 158
Joined: Aug 2009
Reputation: 52
Status
Nattak\'d
I'm going to guess it's this:
org $C24F47
fka_4F47:

I see in my ROM that area of bytes is all different than vanilla. Some patch of some kind.

So just to check, the previously posted version of the code isn't safe to use then?


We are born, live, die and then do the same thing over again.
Quote  

#59
Posts: 175
Threads: 11
Thanks Received: 10
Thanks Given: 8
Joined: May 2013
Reputation: 13
Status
Well-Fed
Yeah, if that section is different, I'm surprised the patch was working for you before at all. Any idea which patch is modifying that section of code? The org $C24F47 is just there to place a label for the above code to branch to in the vanilla code, I'm not actually touching anything after C2/4F3B, so that wouldn't cause any problems unless C2/4F47 has been changed (which, as you say, it has).

@assassin: I mean, if it's any consolation, consider the number of times in this thread that I haven't been able to read. I wouldn't worry about it. Wink


Current Project: FF6: Tensei | Discord ID: TristanGrayse
  Find
Quote  

#60
Posts: 200
Threads: 1
Thanks Received: 10
Thanks Given: 0
Joined: Oct 2015
Reputation: 18
Status
None
as mentioned a few pages back, my "Control attacks ignore MP cost fix" edits this function.  no idea what "my ROM" consists of here.
Quote  



Forum Jump:

Users browsing this thread: 1 Guest(s)


Theme by Madsiur2017Custom Graphics by JamesWhite