Condensing Spell List in Battle
09-24-2017, 01:47 PM
Don't forget the "Not enough MP" messages also, I started to have them again in Phantom Forest and Phantom Train. I used to have them during Lete River and in Mt. Koltz too. There is some parts I did without your patch like Imperial camp so I have no data for that place.
My theory is that since monster abilities are Lores too and also because monsters use spells and lores used by characters, monsters get affected by your code and receive the MP cost of another spell by mistake (tho they still cast the intended spell like there was no problems). I could be totally wrong tho, I can't read your code.
And because I use my hack to test most of the time, my data may differ. For example, in Vanilla, all Monster Lores have a 20 MP cost; in my hack they all have an individual MP cost that goes beyond 20 most of the time. Also my monsters scripts are buffed up (there's more going on than in Vanilla) so I might have an easier time spotting issues as a result.
Too long didn't read: When no monsters are using spells/lores (only using Attack and Special): *possible* crash (might be predictable with RNG manipulation tho). When they cast spells/lores: *might* (again, random) show "Not enough MP" message.
My theory is that since monster abilities are Lores too and also because monsters use spells and lores used by characters, monsters get affected by your code and receive the MP cost of another spell by mistake (tho they still cast the intended spell like there was no problems). I could be totally wrong tho, I can't read your code.
And because I use my hack to test most of the time, my data may differ. For example, in Vanilla, all Monster Lores have a 20 MP cost; in my hack they all have an individual MP cost that goes beyond 20 most of the time. Also my monsters scripts are buffed up (there's more going on than in Vanilla) so I might have an easier time spotting issues as a result.
Too long didn't read: When no monsters are using spells/lores (only using Attack and Special): *possible* crash (might be predictable with RNG manipulation tho). When they cast spells/lores: *might* (again, random) show "Not enough MP" message.
10-04-2017, 05:15 PM
(This post was last modified: 10-04-2017, 05:18 PM by GrayShadows.)
I HAVE SOLVED IT! I think, anyway. The reason it didn't crash until you got Shadow was because the crash was caused by Interceptor counter-attacks. I didn't realise how the game handled them, and consequently missed a PLY because of it causing a stack mismatch. The new version of the code checks for Interceptor (attacks FDh and FEh) before it goes into the rest of the code and exits ahead of time, rather than searching for it in Shadow's spell list. (Or whoever has Interceptor status, really.)
I have also fixed, I believe, the 'Not enough MP' messages -- that was caused by checking for monster attackers out of order. I've adjusted the current code so that it does that check and THEN jumps away to the new code. It leaves more of the vanilla code intact, and we have to check again for Summon or Lore, but it also shortens the new code because we no longer need to check for Magic/X-Magic, as it's done before we jump away, and we no longer need to REP #$10 in the new code, as that's set before the jump as well.
I have also fixed, I believe, the 'Not enough MP' messages -- that was caused by checking for monster attackers out of order. I've adjusted the current code so that it does that check and THEN jumps away to the new code. It leaves more of the vanilla code intact, and we have to check again for Summon or Lore, but it also shortens the new code because we no longer need to check for Magic/X-Magic, as it's done before we jump away, and we no longer need to REP #$10 in the new code, as that's set before the jump as well.
Code:
hirom
;header
!freespace_C2_0 = $C2A65A ; 82 bytes needed
!freespace_C2_1 = $C2FAB0 ; 86 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
org !freespace_C2_0
print "New code CondenseSpellLists starts at: ",pc
reset bytes
condenseSpellLists:
PHX ; This is our character ID coming in
PHP
LDY #$04 ; this is the index of the first Spell slot in the character's spell list
.noMoreSpells ; we'll be branching back here to execute our Lore list
TYX ; X is going to be our 'write-space' indexREP
REP #$10
.checkLoreLoop
LDA ($F2),Y
INC
BNE .checkNextLore
.findNextLore
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 our lores
CPY #$013C ; this is after the last Lore slot, so if we've gone that far, there are no more spells to copy back
BEQ .noMoreLores
LDA ($F2),Y
INC
BEQ .findNextLore
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
.copyNextLore
LDA ($F2),Y ; Yes, we just did this, but we need to do it within this loop, too
PHY ; this stores our Y location, i.e. the next slot with a spell learned
TXY
STA ($F2),Y
PLY ; back to our 'write from' location
INY #2 ; and gets the next byte
INX #2
BCS .doneCopy
SEC
BPL .copyNextLore ; 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 .weCopiedALore
.checkNextLore
INX #4
.weCopiedALore
TXY ; and then copy it over to Y for our next loop through
CPY #$00D8 ; if this is the last spell slot
BEQ .checkNextLore ;loop back up and INX again so we skip over it and point at our first Lore slot
CPY #$0138
BNE .checkLoreLoop
.noMoreLores
PLP
PLX
JMP $532C
print "CondenseSpellLists ends at: ",pc," and used ",bytes," bytes of space."
org !freespace_C2_1
calculateMPDeduction:
print "New code 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 #$FD
BCS fka_4F53
STA $F0 ; save our spell ID in scratch memory
LDA #$04 ; four bytes per index
.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 ; rather than looking for the spell ID, I'm operating under
REP #$20 ; the assumption that if someone is using Summon, it's already
LDA $302C,X ; checked for the equipped Esper, so I'm just loading the MP cost
TAX ; from the first entry in the character's list
SEP #$20
LDA $0003,X
fka_4F45: JMP $4F54 ; (clean up stack and exit)
fka_4F53: JMP $4F53
print "CalculateMPDeduction ends at: ",pc," and used ",bytes," bytes of space."
The following 4 users say Thank You to GrayShadows for this post:
• Gi Nattak (10-04-2017), Robo Jesus (12-24-2017), SSJ Rick (10-05-2017), Warrax (10-04-2017)
• Gi Nattak (10-04-2017), Robo Jesus (12-24-2017), SSJ Rick (10-05-2017), Warrax (10-04-2017)
10-04-2017, 05:29 PM
Good, I'm about to leave work and will put your new code to test tonight, stay tuned.
10-04-2017, 06:43 PM
Code:
.calculateSummon ; rather than looking for the spell ID, I'm operating under
REP #$20 ; the assumption that if someone is using Summon, it's already
LDA $302C,X ; checked for the equipped Esper, so I'm just loading the MP cost
TAX ; from the first entry in the character's list
SEP #$20
LDA $0003,X
or replace all that with:
Code:
.calculateSummon
TDC ; rather than looking for the spell ID, I'm operating under
JMP $4F38 ; the assumption that if someone is using Summon, it's already
; checked for the equipped Esper, so I'm just loading the MP cost
; from the first entry in the character's list
8-)
----------
have you read my 9/20/17 post?
https://www.ff6hacking.com/forums/thread...l#pid35040
mainly interested in your reply to #1 at this point, as your recent changes and my above one are working towards #2.
10-04-2017, 07:28 PM
(This post was last modified: 10-04-2017, 07:55 PM by GrayShadows.)
Oh, hah, that is definitely easier. I'll implement that change for sure.
---
I did read that post, and then completely forgot about it while working on this other crashing issue. Oops? XD We're definitely working towards #2, for sure, and I'd love to move more of this back into the original space/less of it using free space.
As for #1: so I coded in the CPY #$00D8 thinking that I needed to jump over the last Spell slot or I risked copying back Lores, not thinking about the fact that the start of .findNextLore actually solves that by exiting if my 'copy from' index hits the first Lore slot. So I think you're right, that entire check isn't actually necessary and it should be save to remove it.
By that same virtue, I think it would even be safe to skip CPY #$0138 / BNE .checkLoreLoop and just BRA .checkLoreLoop instead; again, we're spending a few extra cycles before we exit (at Y = #$013C in .findNextLore), but it's another three bytes of space saved that can be used for other patches. Unless I'm missing something there?
---
OH AND. There maaaaay be an issue with the current code (similar to the Interceptor code) with on-low-HP relics, the Tapir wake-from-sleep, and Condemned's Doom. At this point I am reasonably certain that they bypass the section of code that was causing issues, but I'm reading through the code now to track where things happen.
ETA: Okay, no, it looks like all of the other counter-attacks (including Retort), the Near Fatal relics, etc., all come into C2/4F08 with different commands (if they enter it at all?), and so bypass the issue I was having with Interceptor, which enters C2/4F08 with the Magic command and therefore tried to check the character's spell list for the counterattack ID. Desperation Attacks MAY try to go through C2/4F08 with the Magic command, so I'm going to adjust the Interceptor check accordingly (it's an easy fix that doesn't cost anything), but at this point I don't see a need to check for any other non-Magic/Lore spell IDs/circumstances.
---
I did read that post, and then completely forgot about it while working on this other crashing issue. Oops? XD We're definitely working towards #2, for sure, and I'd love to move more of this back into the original space/less of it using free space.
As for #1: so I coded in the CPY #$00D8 thinking that I needed to jump over the last Spell slot or I risked copying back Lores, not thinking about the fact that the start of .findNextLore actually solves that by exiting if my 'copy from' index hits the first Lore slot. So I think you're right, that entire check isn't actually necessary and it should be save to remove it.
By that same virtue, I think it would even be safe to skip CPY #$0138 / BNE .checkLoreLoop and just BRA .checkLoreLoop instead; again, we're spending a few extra cycles before we exit (at Y = #$013C in .findNextLore), but it's another three bytes of space saved that can be used for other patches. Unless I'm missing something there?
---
OH AND. There maaaaay be an issue with the current code (similar to the Interceptor code) with on-low-HP relics, the Tapir wake-from-sleep, and Condemned's Doom. At this point I am reasonably certain that they bypass the section of code that was causing issues, but I'm reading through the code now to track where things happen.
ETA: Okay, no, it looks like all of the other counter-attacks (including Retort), the Near Fatal relics, etc., all come into C2/4F08 with different commands (if they enter it at all?), and so bypass the issue I was having with Interceptor, which enters C2/4F08 with the Magic command and therefore tried to check the character's spell list for the counterattack ID. Desperation Attacks MAY try to go through C2/4F08 with the Magic command, so I'm going to adjust the Interceptor check accordingly (it's an easy fix that doesn't cost anything), but at this point I don't see a need to check for any other non-Magic/Lore spell IDs/circumstances.
10-04-2017, 08:44 PM
GrayShadows Wrote:By that same virtue, I think it would even be safe to skip CPY #$0138 / BNE .checkLoreLoop and just BRA .checkLoreLoop instead; again, we're spending a few extra cycles before we exit (at Y = #$013C in .findNextLore), but it's another three bytes of space saved that can be used for other patches. Unless I'm missing something there?
i think ending the Lore list on a non-null entry would be problematic. we can get away with ending the Magic list this way, because:
me Wrote:- involve us finishing the original list on non-null entries, so the check at the top of the main loop (.checkLoreLoop) will fail, branching to .checkNextLore at the end of the loop -- bringing both X and Y to the start of the Lore list (or exiting, if we were processing lores).
iow, .findNextLore is a situational loop within the main loop, but we still need _something_ to tell that main loop to end.
10-04-2017, 10:01 PM
Looking good so far, no crash or anything.
Do you think it's feasible to sort both rows separately?
Do you think it's feasible to sort both rows separately?
10-04-2017, 10:34 PM
(10-04-2017, 08:44 PM)assassin Wrote: i think ending the Lore list on a non-null entry would be problematic.
Ah, yeah, nvm. It would only work if the final Lore were blank, but if they've got their full list then it would never exit. Brainfart on my part.
Warrax Wrote:Do you think it's feasible to sort both rows separately?
Like, the left and right columns? It's definitely feasible, but it would involve much more extensive coding and I don't know that it would be to any benefit. You could still run into issues where, say, you have a very long list on one side and only a couple of spells at the top of the other. It also wouldn't be compatible with sorting Lores, so you'd need to run the existing code separately for that.
-- That said, while it would still require separate code, it would be feasible to adjust the initial list build for proper two-per-line sorting and then run a sort to shuffle up a line only if an entire line is blank. You'd still end up with blank spaces if only one spell on a line was missing, and you'd run the same risk of having one very full column and one very empty one, but it would still be more user-friendly than the existing vanilla system.
10-04-2017, 11:11 PM
Yeah I'm talking about the left and right columns. It's no biggies. It's just that spells tend to swap columns with an auto sort but I understand the consequences of doing such a fix.
10-04-2017, 11:46 PM
for giggles, was wondering how to do that with minimal revamping. i wonder if tweaking my algorithm posted on 9/17 could do it (stuff between dotted lines is new):
or maybe the X and Y even/odd test should be before the X==Y test? and we'd probably want A in 16-bit mode here.
no, i'm not suggesting that untested code should replace proven code with dual list thresholds and various optimizations built into it.
just that the +1 advancement is more apparent within that algorithm to me. and indulging weeks-after-the-fact suggestions is alright when the method only takes 5 minutes to concoct. (yet somehow typing the rest of this post took me a good 15 minutes. wtf?)
Code:
stx $f0
cpy $f0 ; does full list index == output index? yes, i use
; this variable in a single place, because no other
; easy way to compare X and Y. :/
beq noCopy ; skip copying if so, but still advance
-----
tya
eor $f0
and #$04 ; does even/odd-ness of input index match that
; of output index?
beq noColumnChange ; branch if so
inx
inx
inx
inx ; move one output slot forward
noColumnChange:
-----
clc
rep #$20
copyLoop:
lda ($F2),Y ; read two spell menu bytes from original position
phy
txy
sta ($F2),Y ; save to new position
or maybe the X and Y even/odd test should be before the X==Y test? and we'd probably want A in 16-bit mode here.
no, i'm not suggesting that untested code should replace proven code with dual list thresholds and various optimizations built into it.
just that the +1 advancement is more apparent within that algorithm to me. and indulging weeks-after-the-fact suggestions is alright when the method only takes 5 minutes to concoct. (yet somehow typing the rest of this post took me a good 15 minutes. wtf?)
« Next Oldest | Next Newest »
Users browsing this thread: 2 Guest(s)