Users browsing this thread: 1 Guest(s)
Make Morph work like Quick?

#1
Posts: 377
Threads: 34
Thanks Received: 10
Thanks Given: 7
Joined: Dec 2018
Reputation: 18
Status
Moog
I'm working on some changes to Terra.  Specifically, I'm replacing the Morph command with the Magitek command, which gives her access to her own personal list of 8 unique spells (provided they share targeting data with the rider spells).  To maintain her half-esper-ness, one of these spells is Trance which triggers the Morph status (with a different animation, because she doesn't play nice and move back into position with the Morph animation).

I'm running into the same problem everybody does with Morph, though, which is the wretched Revert timer.  So I want to remove it and make the status turn-based, applying its effect to the next 1-3 actions that Terra (or whichever character is morphed) takes.

I had two basic plans for how this might be achieved:

1) Turn off countdown of Morph Timer.  Check for Morph status on taking an action, and if present decrease Morph Timer by a fixed amount [say... 16].  Floor it at 0, triggering built-in revert.
  A) Possibly set the Morph Timer back to max at the end of Revert, so that Morph always has a fixed turn count.

2) Turn off countdown of Morph Timer.  Add Morph status to the Quick spell and its special effect, so that when Quick ends Morph ends too.  Not sure if this will work.


The first sounds more aesthetically pleasing because the morph meter will show roughly how many turns Terra has left with the morph bonus.  However... it involves interrupting actions which seems rather complicated.

This is some code I got from DataCrystal's rom map:
Code:
==============================================================
Pick Action for Berserk, Zombie, Muddle, Charm, Colliseum
(For Morph changes: add check, redirect, and timer deduction?)
==============================================================
C2/0420: 8A           TXA
C2/0421: EB           XBA
C2/0422: A9 06        LDA #$06
C2/0424: 20 81 47     JSR $4781    (X * 6)
C2/0427: A8           TAY
C2/0428: 64 FE        STZ $FE      (save Fight as Command 5)
C2/042A: 64 FF        STZ $FF      
C2/042C: B9 2E 20     LDA $202E,Y  (get Command 1)
C2/042F: 85 F6        STA $F6
C2/0431: B9 31 20     LDA $2031,Y  (get Command 2)
C2/0434: 85 F8        STA $F8
C2/0436: B9 34 20     LDA $2034,Y  (get Command 3)
C2/0439: 85 FA        STA $FA
C2/043B: B9 37 20     LDA $2037,Y  (get Command 4)
C2/043E: 85 FC        STA $FC
C2/0440: A9 05        LDA #$05     (indicate 5 valid commands to choose from)
C2/0442: 85 F5        STA $F5
C2/0444: BD E5 3E     LDA $3EE5,X  (Status byte 2)
C2/0447: 0A           ASL
C2/0448: 0A           ASL
C2/0449: 85 F4        STA $F4      (Bit 7 = Muddled, Bit 6 = Berserk, etc)
C2/044B: 0A           ASL
C2/044C: 10 04        BPL $0452    (If no Berserk status)
C2/044E: 64 F4        STZ $F4      (Clear $F4, then skip Charm and Colosseum checks if Berserked)
C2/0450: 80 0C        BRA $045E    
C2/0452: BD 95 33     LDA $3395,X  (Which target Charmed you)
C2/0455: 49 80        EOR #$80     (FFh indicates no Charmer, so Bit 7 will now be set if there IS a Charmer)
C2/0457: 04 F4        TSB $F4
C2/0459: AD 97 3A     LDA $3A97    (Top bit indicates Colosseum battle?)
C2/045C: 04 F4        TSB $F4
(Note that Berserk status will override Muddle/Charm/Colosseum for purposes of
determining whether we choose from the C2/04D0 or the C2/04D4 command list.
In contrast, Zombie will not; Charm/Colosseum/Muddle override it.)
C2/045E: 9B           TXY          (Y now points to the attacker who is taking this turn)
C2/045F: DA           PHX
C2/0460: A2 06        LDX #$06       (start checking 4th command slot)
C2/0462: DA           PHX            (save slot position)
C2/0463: B5 F6        LDA $F6,X
C2/0465: 48           PHA            (save command)
C2/0466: 30 1A        BMI $0482      (branch if slot empty)
C2/0468: 18           CLC            (clear Carry)
C2/0469: 20 17 52     JSR $5217      (X = A DIV 8, A = 2 ^ (A MOD 8) )
C2/046C: 3F D0 04 C2  AND $C204D0,X  (is command allowed when Muddled/Charmed/Colosseum?)
C2/0470: F0 10        BEQ $0482      (branch if not)
C2/0472: A5 F4        LDA $F4
C2/0474: 30 12        BMI $0488      (Branch if Muddled/Charmed/Colosseum but not Berserked)
C2/0476: A3 01        LDA $01,S      (get command)
C2/0478: 18           CLC
C2/0479: 20 17 52     JSR $5217      (X = A DIV 8, A = 2 ^ (A MOD 8) )
C2/047C: 3F D4 04 C2  AND $C204D4,X  (is command allowed when Berserked/Zombied?)
C2/0480: D0 06        BNE $0488      (branch if so)
C2/0482: A9 FF        LDA #$FF
C2/0484: 83 01        STA $01,S      (replace command on stack with Empty entry)
C2/0486: C6 F5        DEC $F5        (decrement number of valid commands)
C2/0488: 7B           TDC            (clear 16-bit A)
C2/0489: A3 01        LDA $01,S      (get current command)

(Loop below sees if our current command is one that requires special code to
work with Muddle/Charm/Colosseum/Berserk/Zombie.  If it is, a special function
is called for the command.)

C2/048B: A2 08        LDX #$08
C2/048D: DF D8 04 C2  CMP $C204D8,X  (does our current command match one from table?)
C2/0491: D0 06        BNE $0499      (if not, compare it to a second command in this loop iteration)
C2/0493: FC E2 04     JSR ($04E2,X)  (if it does, call the special code used by that command)
C2/0496: EB           XBA            (put attack/spell # in top of A)
C2/0497: 80 10        BRA $04A9      (exit loop, as we found our command)
C2/0499: DF D9 04 C2  CMP $C204D9,X  (does our current command match one from table?)
C2/049D: D0 06        BNE $04A5      (if not, our current command didn't match either
                                     compared one.  so move to new pair of commands
                                     in table, and repeat loop)
C2/049F: FC EC 04     JSR ($04EC,X)  (if it did match, call the special code used
                                     by that command)
C2/04A2: EB           XBA            (put attack/spell # in top of A)
C2/04A3: 80 04        BRA $04A9      (exit loop, as we found our command)
C2/04A5: CA           DEX
C2/04A6: CA           DEX
C2/04A7: 10 E4        BPL $048D     (Loop to compare current command to all 10 commands
                                    that utilize special code.
                                    If this loop doesn't exit before X becomes negative,
                                    that means our current command has no special function,
                                    and the Attack # in the top half of A will be zero.)
C2/04A9: 68           PLA           (get command # from stack)
C2/04AA: FA           PLX           (get command slot from stack)
C2/04AB: 95 F6        STA $F6,X     (save command # in current command slot)
C2/04AD: EB           XBA
C2/04AE: 95 F7        STA $F7,X     (save attack/spell # corresponding to that attack)
C2/04B0: CA           DEX
C2/04B1: CA           DEX
C2/04B2: 10 AE        BPL $0462     (loop for first 4 command slots)

(Fight has been put in the 5th command slot.  Any valid commands, with their
accompanying attack/spell numbers, have been established for slots 1 thru 4.
Now we shall randomly pick from those commands.  Each slot should have equal
probability of being chosen.)

C2/04B4: A5 F5        LDA $F5     (# of valid command slots)
C2/04B6: 20 65 4B     JSR $4B65   (RNG: 0 to A - 1 .  we're randomly picking a command)
C2/04B9: A8           TAY        
C2/04BA: A2 08        LDX #$08    (start pointing to Command slot 5)
C2/04BC: B5 F6        LDA $F6,X
C2/04BE: 30 03        BMI $04C3   (if that command slot is Empty, move to next one)
C2/04C0: 88           DEY         (only decrement when on a valid command slot)
C2/04C1: 30 07        BMI $04CA   (If Y is negative, we've found our Nth valid command
                                  [starting with the last valid command and counting
                                  backward], where N is the number returned from the
                                  RNG plus 1.
                                  This is what we wanted, so branch.)

C2/04C3: CA           DEX
C2/04C4: CA           DEX
C2/04C5: 10 F5        BPL $04BC   (loop for all 5 command slots)
C2/04C7: 7B           TDC         (clear 16-bit A)
C2/04C8: 80 04        BRA $04CE   (clean up stack and exit function.  A is zero,
                                  indicating Fight, which must be a fallback in case
                                  all 5 Command slots somehow came up useless.  not
                                  sure how that'd happen, as Slot #5 should always
                                  hold Fight anyway...)

C2/04CA: EB           XBA
C2/04CB: B5 F7        LDA $F7,X
C2/04CD: EB           XBA         (bottom of A = command # from $F6,X
                                  top of A = attack/spell # from $F7,X)
C2/04CE: FA           PLX
C2/04CF: 60           RTS
I think to do (1) I'd have to add another check and redirect.  I don't understand Assembly, though.  I can change hex values easy, but adding addition code creates space issues in a ROM, doesn't it?  I wouldn't even know what to add; all I've done thus-far is copy animations from one ability to another, and that with an editor.

I also have code from DataCrystal relevant to (2):

Code:
===============================
Morph Status Toggle
(For reference of status flag?)
===============================
C2/4678: A9 02 00     LDA #$0002
C2/467B: 80 CF        BRA $464C    


===========================================
Decrease Morph Timer
(Turn off... or attach to actions instead?)
===========================================
(Decrease Morph timer.  If it's run out, zero related Morph variables,
and queue the Revert command.)

C2/1211: C2 20        REP #$20    (Set 16-bit Accumulator)
C2/1213: 38           SEC
C2/1214: AD 30 3F     LDA $3F30   (Load the morph timer)
C2/1217: ED 32 3F     SBC $3F32   (Subtract morph decrement amount)  [Remove?  Or move to Quick?]
C2/121A: 8D 30 3F     STA $3F30   (Save the new morph timer)
C2/121D: E2 20        SEP #$20    (Set 8-bit Accumulator)
C2/121F: B0 13        BCS $1234   (Branch if it's greater than zero)
C2/1221: 9C 31 3F     STZ $3F31   (zero top byte of Morph timer.  i assume we're
                                  neglecting to zero $3F30 just to avoid adding a
                                  "REP" instruction.)
C2/1224: 20 36 0B     JSR $0B36   (adjust Morph supply [in this case, zero it]
                                  to match our new Morph timer)
C2/1227: A9 FF        LDA #$FF
C2/1229: 8D E2 3E     STA $3EE2   (Store #$FF to Morphed targets byte [no longer have a
                                  Morphed target])
C2/122C: A9 04        LDA #$04
C2/122E: 8D 7A 3A     STA $3A7A    (Store Revert as command)
C2/1231: 20 B2 4E     JSR $4EB2    (queue it?)
C2/1234: AD 31 3F     LDA $3F31    (Load the remaining amount of morph time DIV 256, if any)
C2/1237: 9D 04 3B     STA $3B04,X  (Store it to the character's Morph gauge?)
(Why do we bother zeroing all these timers and variables here when the forthcoming
Revert can handle it?  Presumably to avoid gauge screwiness and a bunch of pointless
calls to this function should Terra's Morph timer run down in the middle of an attack
animation..)
C2/123A: 60           RTS


==========================
Update Morph Supply
(Relevent to Morph Timer?)
==========================
(Establish new value for Morph supply based on its previous value and
the current Morph timer)
C2/0B36: AD E2 3E     LDA $3EE2   (Which target is Morphed)
C2/0B39: 30 0E        BMI $0B49   (Exit if no one is Morphed)
C2/0B3B: AD F6 1C     LDA $1CF6
C2/0B3E: EB           XBA
C2/0B3F: AD 31 3F     LDA $3F31
C2/0B42: 20 81 47     JSR $4781   (16-bit A = morph supply * (morph timer DIV 256) )
C2/0B45: EB           XBA
C2/0B46: 8D F6 1C     STA $1CF6   (morph supply =
                                  (morph supply * (morph timer DIV 256)) DIV 256  )
C2/0B49: 60           RTS
    
                                    
===========================================
Special Effect: Quick (Change status flag?)
===========================================
C2/42CA: AD 02 34     LDA $3402
C2/42CD: 10 09        BPL $42D8   (If already under influence of Quick)
C2/42CF: 8C 04 34     STY $3404
C2/42D2: A9 02        LDA #$02
C2/42D4: 8D 02 34     STA $3402
C2/42D7: 60           RTS

C2/42D8: C2 20        REP #$20    (Set 16-bit Accumulator)
C2/42DA: B9 18 30     LDA $3018,Y
C2/42DD: 0C 5A 3A     TSB $3A5A   (Set target as missed)
C2/42E0: 60           RTS
Here I'm thinking in terms of what I know about the game.  When Quick gives a player an extra turn, it removes itself as a status.  If I modify the Quick spell to add Morph as well, restrict it to Terra (with the Magitek menu), and then adjust the values in the Quick special effect, I should be able to make it turn off Morph as well, right?
This approach would make the green Morph bar mean absolutely nothing, though, which is a bit obtuse for me.  And while I know all I would need to do here is change two values, I can't figure out WHICH two values I need to change, or what they would need to be changed to.



I really don't know what I'm doing.  I'm spoiled by editors, and when it comes to direct hex or assembly I'm pretty much helpless.  I know many of you have extensive experience modifying these values, though; what would you do for this?


Would this change be something that would interest you in your own mods?  A single turn of Morph is mechanically identical to a patched Economizer, cutting the cost of a single spell in half (since casting it grants double power, saving a cast).  Each turn on top of that makes it an increasingly broken buff, with more than three turns being absolutely absurd.
If you don't like these ideas, what would you do instead?
  Find
Quote  

#2
Posts: 3,966
Threads: 279
Thanks Received: 233
Thanks Given: 56
Joined: Oct 2011
Reputation: 65
Status
Tissue-aware
You can check the C2 disassembly, probably an updated copy of what is on Datacrystal:

.zip  bank-$C2.zip (372.32 KB, 6 downloads)


Usually I would link assassin's website for it but it's down right now.

For ASM, either you ask someone to do it or get your hands dirty, learning how to do this would take more time but would allow you to do anything you want after. There are plenty of ASM resources to learn, I learned mostly by looking up opcodes online and reading comments in the disassembly (plus practice).

I can take a look at your thing maybe after Christmas but I don't have any answer for now, maybe someone else in the meanwhile could help you more..
  Find
Quote  
[-] The following 1 user says Thank You to madsiur for this post:
  • C-Dude (12-21-2018)

#3
Posts: 377
Threads: 34
Thanks Received: 10
Thanks Given: 7
Joined: Dec 2018
Reputation: 18
Status
Moog
(12-21-2018, 01:30 PM)madsiur Wrote: For ASM, either you ask someone to do it or get your hands dirty, learning how to do this would take more time but would allow you to do anything you want after. There are plenty of ASM resources to learn, I learned mostly by looking up opcodes online and reading comments in the disassembly (plus practice).

I can take a look at your thing maybe after Christmas but I don't have any answer for now, maybe someone else in the meanwhile could help you more..

I'm not in a hurry with this; I'm compartmentalizing changes into mini-patches as recommended and I can focus on other directions in the meantime.

I wanted to read the ASM tutorials at the top of the board, but the links are broken.  I'll try google searching for them, hopefully there's some info out there.  The thing that's tripping me up is the commands.  I understand (on a rudimentary level) that hex is just an addressed string of numbers and changing the number at a specific index changes what happens when the commands are executed.  But the commands aren't stored in the same fashion, are they?

Any which way I figured I'd ask, because I know several hackers here have made Morph patches before and are trying to make Morph less game-breaking.
  Find
Quote  

#4
Posts: 614
Threads: 49
Thanks Received: 0
Thanks Given: 4
Joined: Feb 2017
Reputation: 25
Status
None
Hmm... two things.

First off, I fixed the animation issue with Morph in my beta Morphenomenal patch. The code is as follows:
Code:
;Code Change at D0/69DC (Revert Animation):
DB 03        ;branch to $69E1 if character already stepped forward to attack
BF 19 70        ;call subroutine $7019

Second, I considered handling Morph the same way that you did, but there's one major, game-breaking problem with it. Morph doubles Terra's Magic Defense. Someone could choose to not take an action with her and boost her M.Def indefinitely. I don't think this could work unless she auto-reverted every time her command gauge filled up, which is even clunkier than the original mechanic.


Projects:
FFVI: Divergent Paths (Completed) - a complete storyline and gameplay hack of FF6 that adds Leo as a playable character
  Find
Quote  
[-] The following 2 users say Thank You to PowerPanda for this post:
  • C-Dude (12-22-2018), Robo Jesus (05-20-2019)

#5
Posts: 377
Threads: 34
Thanks Received: 10
Thanks Given: 7
Joined: Dec 2018
Reputation: 18
Status
Moog
(12-21-2018, 07:22 PM)PowerPanda Wrote: Hmm... two things.

First off, I fixed the animation issue with Morph in my beta Morphenomenal patch. The code is as follows:
Code:
;Code Change at D0/69DC (Revert Animation):
DB 03        ;branch to $69E1 if character already stepped forward to attack
BF 19 70        ;call subroutine $7019
I was running into issues because Magitek treats some of its functions as attacks, and others as spell casts.  I ended up finding an animation that looks really cool, though: my Trance spell replaces Confuser and uses the Drain spell graphic, with pallets 178, 182, and 155, and with the Trance sound effect (#64).  As an auto-confirm self-target, it makes orbs of shining energy crash into and envelop the caster (Terra), followed by toggling on the Morph status.  It's actually really cool.


(12-21-2018, 07:22 PM)PowerPanda Wrote: Second, I considered handling Morph the same way that you did, but there's one major, game-breaking problem with it. Morph doubles Terra's Magic Defense. Someone could choose to not take an action with her and boost her M.Def indefinitely. I don't think this could work unless she auto-reverted every time her command gauge filled up, which is even clunkier than the original mechanic.

I don't think that's a big problem, though.  Yeah, you can boost her MDef basically indefinitely, but that's at the cost of a team slot.  She'll be taking less damage, but she won't be contributing to the fight.  If the MorphBar deduction occurs within the same command that handles loss-of-control, (I'm guessing) you couldn't even get around it by berserking her, because she'd attack once and then que revert.
Doesn't seem all that different than having Gogo sit at full ATB while waiting for a good action to mimic.

Haven't had a chance to read any assembly guides yet, though, so I might be overlooking something obvious.
  Find
Quote  

#6
Posts: 3,966
Threads: 279
Thanks Received: 233
Thanks Given: 56
Joined: Oct 2011
Reputation: 65
Status
Tissue-aware
We have some ASM tutorials here: https://www.ff6hacking.com/wiki/doku.php...s:doc:snes

Some links are broken but you can find them by searching the website you are redirected to. I'll fix this later.
  Find
Quote  
[-] The following 1 user says Thank You to madsiur for this post:
  • C-Dude (12-22-2018)

#7
Posts: 377
Threads: 34
Thanks Received: 10
Thanks Given: 7
Joined: Dec 2018
Reputation: 18
Status
Moog
Okay.

Having read through some of the basic assembly tutorials linked, I've determined this kind of programming is beyond my capabilities.  If this is basic, I don't want to see what advanced looks like.

I know logically what would need to be done to do this:
* Set Morph Gauge to 2 when Quick is applied
* During the main battle loop's Quick resolution, deduct 1 from Morph Gauge each time the Quick turn count is reduced
* Deactivate the Magitek menu during Quick to prevent spamming the Morph ability (Optional, as instead I can use FF3USME to make the spell damage Terra by 1/4 each use)
* Remove post-battle Morph Gauge increases and the Morph Timer

If I could give instructions with logic gates, like with C or Java, then I could handle this.
But directly instructing a processor to do this stuff?  That's like learning to speak robot.  I can't wrap my head around it.

So... if someone else is interested in coding this as a standalone patch for me I would greatly appreciate it.  If not, I can live without it; the spell just won't do much before the player starts getting magic experience.
  Find
Quote  

#8
Posts: 377
Threads: 34
Thanks Received: 10
Thanks Given: 7
Joined: Dec 2018
Reputation: 18
Status
Moog
Having gained nine months of experience with direct hex editing, I'd just like to update this thread to inform that I've achieved what I was trying to accomplish (tentatively, there's always testing to do). I did this by adding to the Ready Subroutine before the main battle loop begins: it checks if the actor is morphed and triggers the morph decrement accordingly. A copy of the same function, but without the subtraction, remains in the original 'every frame' calculation. And the spell effect Spiraler has been changed to apply morph status to the corresponding spell IF the player has passed the second Zozo event (retrieval of Terra).

Basically, every time the morphed character readies an action, the morph bar depletes by half. After the second action it is empty, meaning the next action readied will be preceded by Revert.

I'll share the hex once I've finished testing the change. Not sure anybody else would want it, but hey, might as well right?
  Find
Quote  
[-] The following 2 users say Thank You to C-Dude for this post:
  • PowerPanda (09-17-2019), Robo Jesus (09-17-2019)



Forum Jump:

Users browsing this thread: 1 Guest(s)


Theme by Madsiur2017Custom Graphics by JamesWhite