Aslickproductions.org/forum/index.php?PHPSESSID=5f0fck550j2m4m2fpbtkj2vkm1&action=profile;area=showposts;sa=topics;u=83e:/My Web Sites/Slick Productions - FFIV Message Board/slickproductions.org/forum/indexf56e-2.htmlslickproductions.org/forum/index.php?PHPSESSID=5f0fck550j2m4m2fpbtkj2vkm1&action=profile;u=83e:/My Web Sites/Slick Productions - FFIV Message Board/slickproductions.org/forum/indexf56e-2.html.zx0h^00WɇOKtext/htmlISO-8859-1gzip0|ɇWed, 11 Mar 2020 06:28:25 GMT0 0P0h^#ɇ Show Posts - magitek

Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Topics - magitek

Pages: 1
1
Hey folks, I'm trying to write a patch for Imzogelmo's color coded MP digits, to temporarily set the Reflect palette back to it's normal green-yellow for the duration the Reflect barrier is shown. I'm calling a routine that's basically a copy of Lenophis' Bad Decoration function to reset the palette at $7FF8-$7FFF, except I've inserted reflect's original four-color palette into free space and it's loading that instead.

It works, and much like Crusader, it overwrites previously-Reflect's-now-actually-MP-numerals palette for the rest of the battle. So naturally, I figured I'd combine this with Bad Decoration. However, from what I can tell, the Bad Decoration code is called multiple times every frame (during animations) and undoes any changes to Reflect's palette. I'm at a loss as to where to call my code from so that the palette changes last throughout Reflect's animation. I don't know exactly how Crusader does it.

I'm currently hooking C1/C9A5, as that is called by Reflect to play the "ping" sound. It's reached via C1/C884 (X = 8B) much in the same way Crusader is (X = 67).

Another approach may be to modify Bad Decoration so that it only calls the code to restore the palette once Crusader/Reflect has finished their animations, but I don't know where to hook Leno's code either.



Here's my code. Also, please find attached the code for Bad Decoration and Color-coded MP Digits (which I've taken the liberty to disassemble and comment for my own understanding).

Code: [Select]
hirom
;header
!freespacelong = $C0D613 ;new code and palette data (relocatable)

;Called by Reflect
org $C1C9A5
;INC $6281      ;from C1/C884, X = 8B (play "ping" sound?)
JSR c1changepalette     


org $C1FFE5
c1changepalette:
JSL changepalette
INC $6281      ;from C1/C884, X = 8B (play ching sound?)
RTS


macro changepalette()
      changepalette:
      PHA : PHX : PHY : PHP
      SEP #$20
      TDC
      TAX
      .loop
      LDA newpalette,X  ;load the palette
      STA $7FF8,X       ;store the palette
      INX
      CPX #$0008
      BNE .loop
      PLP : PLY : PLX : PLA
      RTL
      endmacro


;New code (relocatable anywhere)
org !freespacelong
%changepalette()
newpalette:
dw $43FD          ;B16:G31:R29 (Pastel Yellow)
dw $3FF1          ;B15:G31:R17 (Pastel Green)
dw $03E0          ;B00:G31:R00 (Light Green)
dw $02E0          ;B00:G23:R00 (Dark Green)

2
Game Modification Station / WIP Patch: Permanent Morph bugfix
« on: May 01, 2016, 08:02:21 AM »
This bug is referenced in Master ZEDs guide as "Permanent Morph status until the end of battle". This should fix the instances of the bug that aren't caused by Rippler.

It additionally causes the Morph gauge to pause if Terra is afflicted with Stop, or if someone else is under the influence of Quick.

I just want to note that Leet Sketcher is the one who originally did the research and came up with the actual bugfix for the bug (on my request).
He is the one who noted the weirdness of how Stop and Quick, being time freezes, still wouldn't impede the Morph timer from ticking down. I built off of his ideas to make this patch. Shoulder of giants, and all that. I'm posting this publicly because I want to get feedback and help with an issue, not to take credit for his work after I originally saddled him with this project. :)

With that said, here is the code. If anyone can figure out a way to shrink this function by two bytes how to fix the brief pause when Reverting after the gauge runs out during an animation, that would be much appreciated.

EDIT: it seems that removing the JSR $0B36 (update_supply) isn't the cause of Terra's brief pause, as that happens even with that instruction still in place. Not sure if removing that instruction changes anything at all. The pause doesn't happen in vanilla, it's caused by not clearing $3EE2. Oh well. I updated the comments in the code.

Code: [Select]
;Fixes the Permanent Morph bug (both Quick and Stop/Sleep/Freeze)
;Also halts the Morph timer if Terra is afflicted with Stop, or if someone else
;is under the influence of Quick. Also introduces a minor bug, see below. :/

hirom                 ;do not change
;header               ;uncomment this if your ROM has a header

;Leet Sketcher's original fix
;org $C21227
;NOP #5               ;Don't null "who is Morphed" variable for now
                      ; Removing this seems to cause Terra to get stuck in the
                      ; stepped forward position briefly when she uses Revert
                      ; if the timer runs out during an attack animation. No
                      ; other screwiness immediately apparent...

;Full fix
org $C21211
;Start of new code (7 bytes used)
LDA $3AA0,X
BIT #$10              ;(is entity Wounded, Petrified, or Stopped, or is
                      ; somebody else under the influence of Quick?)
BNE exit              ;(Exit if any are true)
;End of new code
REP #$20              ;(Set 16-bit Accumulator)
SEC                                         
LDA $3F30             ;(Load the morph timer)
SBC $3F32             ;(Subtract morph decrement amount)
STA $3F30             ;(Save the new morph timer)
SEP #$20              ;(Set 8-bit Accumulator)
BCS update_gauge      ;(Branch if it's greater than zero)
STZ $3F31             ;(zero top byte of Morph timer.  i assume we're
                      ; neglecting to zero $3F30 just to avoid adding a
                      ; "REP" instruction.)
                      ;(The Morph supply will refill when the timer
                      ; runs out if we don't do this)
;Start of removed code (8 bytes freed)
;JSR update_supply    ;(removed, don't uncomment)
                      ;(adjust Morph supply [in this case, zero it] to match
                      ;our new Morph timer)
                      ;
                      ; Needs to be removed to free up space.
                      ;
;(This is what causes the original bug: we remove it and free up 5 bytes)
;LDA #$FF             ;(removed, don't uncomment)
;STA $3EE2            ;(removed, don't uncomment)
                      ;(Store #$FF to Morphed targets byte [no longer have a
                      ; Morphed target])
                      ; Removing this seems to cause Terra to get stuck in the
                      ; stepped forward position briefly when she uses Revert
                      ; if the timer runs out during an attack animation. No
                      ; other screwiness immediately apparent...
;end of removed code
NOP                   ;One byte left unused from the above; padding.
LDA #$04
STA $3A7A             ;(Store Revert as command)
JSR queue_revert      ;(queue it?)
update_gauge:
LDA $3F31             ;(Load the remaining amount of morph time DIV 256,
                      ; if any)
STA $3B04,X           ;(Store it to the character's Morph gauge?)

exit:
RTS

org $C24EB2
queue_revert:

org $C20B36
update_supply:

3
Hello all,

please let me make a quick introduction of myself, as is customary when making a first post anywhere. My name is Fredrik, I'm 26, from Sweden, and like all of you I'm hopelessly addicted to FFVI. :P

Since a few years back I started toying with the idea of modifying the game, randomly changing this and that with whatever editors I could find, in a scrambled attempt to understand how the game works. Recently I've been looking more and more into the inner workings of the game, thanks to the awesome commented disassemblies by Terii and assassin. At first it seemed intimidating, but with some tips gathered from lurking on these and other romhacking boards, I finally grabbed WindHex32 and tried some minor changes, like NOPing a few bytes here, or changing a value there.

So far I've managed to disable the inherent +20 speed in battle (thanks to Lenophis and Dragonsbrethren in a post somewhere describing exactly what to do). This was my first experience with hands on hex editing of the ROM's code.

Having cut my teeth on that one, I rummaged through the C2 disassembly and changed some bytes to make Haste double the rate at which the ATB bar fills, and make Safe/Shell halve damage (instead of cutting it by 1/3). Also successfully managed to prevent Dance giving you Dance status, as well as making the Rage command transform into Magitek while riding Magitek armor for every character (for a silly all-Gau proof-of-concept hack).

So far simple stuff. But last night I got the idea to make attacks that check for Stamina directly involve the hit rate of the spell as well. After a sleepless night I thought I had managed to make enough sense of the code to be able to copy-paste some code together in a manner which I had hoped would produce the wanted results.

I'll explain what I tried to do, but first let me paste the relevant code and section of Terii's algorithms doc, for reference.


Code: [Select]
Step 5. Check to hit for attacks that can be blocked by Stamina

    Most attacks use step 4 instead of this step. Only Break, Doom, Demi,
    Quartr, X-Zone, W Wind, Shoat, Odin, Raiden, Antlion, Snare, X-Fer, and
    Grav Bomb use this step.

    Step 5a. Chance to hit

      1. BlockValue = (255 - MBlock * 2) + 1
 
      2. If BlockValue > 255 then BlockValue = 255
         If BlockValue < 1 then BlockValue = 1

      3. If ((Hit Rate * BlockValue) / 256) > [0..99] then you hit, otherwise
      you miss.

    Step 5b. Check if Stamina blocks

      If target's stamina >= [0..127] then the attack misses (even if it hit in
      step 5a); otherwise, the attack hits as long as it hit in step 5a.

And the relevant code:

Code: [Select]
Check if hit if Stamina involved

C2/239C: B9 55 3B     LDA $3B55,Y (MBlock)
C2/239F: EB           XBA
C2/23A0: AD A8 11     LDA $11A8   (Hit Rate)
C2/23A3: 20 81 47     JSR $4781   (Multiplication Function)
C2/23A6: EB           XBA
C2/23A7: 85 EE        STA $EE     (High byte of Mblock * Hit Rate)
C2/23A9: A9 64        LDA #$64   
C2/23AB: 20 65 4B     JSR $4B65   (Random Number 0 to 99)
C2/23AE: C5 EE        CMP $EE
C2/23B0: B0 0C        BCS $23BE   (Attack misses, so exit)
C2/23B2: 20 5A 4B     JSR $4B5A   (Random Number 0 to 255)
C2/23B5: 29 7F        AND #$7F    (0 to 127)
C2/23B7: 85 EE        STA $EE
C2/23B9: B9 40 3B     LDA $3B40,Y (Stamina)
C2/23BC: C5 EE        CMP $EE
C2/23BE: 60           RTS


Right. So as far as my understanding goes, what this code does is:

(If attack checks for Stamina, jump here)
1. Get BlockValue for Mblock, previously determined to be (255 - Mblock * 2) + 1 in some other section of the code
2. Store it in the top byte of a two-byte something something? I don't really understand most of the comments. :P
3. Get Hit Rate
4. Multiply high byte by low byte (ie. BlockValue * Hit Rate)
5. Take the top byte again (effectively dividing by 256?)
6. Store as $EE?
7. Get 100
8. Get random number [0..(100-1)], ie. [0..99]
9. See if it's higher than $EE?
10. If it is, attack misses, so skip C bytes ahead, to exit
11. If it isn't, continue and get a random number [0..255]
12. modify it somehow to make it [0..127]? I don't know what this does.
13. Store it as $EE
14. Get Stamina
15. Is Stamina higher than $EE?
16. don't know what RTS is, but I'm guessing it's something like, "ok, got our value, now let's go back to where we were and continue on".

Short version:

1. Check for Mblock. Does attack miss?
2. If not, check for Stamina.

Okay, so what I tried to do was to modify the Stamina check to take the hit rate of the spell into account while also checking for Stamina and Mblock.

Basically this:
Code: [Select]
1. StaminaValue = (255 - Stamina * 2) + 1

2. BlockValue = (255 - MBlock * 2) + 1

3. BlockValue = StaminaValue * BlockValue / 256

   If BlockValue > 255 then BlockValue = 255
   If BlockValue < 1 then BlockValue = 1

4. If ((Hit Rate * BlockValue) / 256) > [0..99] then you hit, otherwise
      you miss.

And here is the block of code that I wrote/copypasted together:

Code: [Select]
C2/239C: B9 40 3B     LDA $3B40,Y (Stamina)
C2/239F: 20 61 28     JSR $2861   (255 - Stamina * 2) + 1 , capped at low of 1 and high of 255)
C2/23A2: EB           XBA
C2/23A3: B9 55 3B     LDA $3B55,Y (MBlock)
C2/23A6: 20 81 47     JSR $4781   (Multiplication Function)
C2/23A9: EB           XBA
C2/23AA: AD A8 11     LDA $11A8   (Hit Rate)
C2/23AD: 20 81 47     JSR $4781   (Multiplication Function)
C2/23B0: EB           XBA
C2/23B1: 85 EE        STA $EE     (High byte of Mblock * Hit Rate)
C2/23B3: A9 64        LDA #$64
C2/23B5: 20 65 4B     JSR $4B65   (Random Number 0 to 99)
C2/23B8: C5 EE        CMP $EE
C2/23BA: 60           RTS
C2/23BB: EA           NOP
C2/23BC: EA           NOP
C2/23BD: EA           NOP
C2/23BE: EA           NOP

In my understanding, what this code does is:

1. Get Stamina
2. Get StaminaValue = (255 - Stamina * 2) + 1, capped at low of 1 and high of 255
3. XBA. Stash value in high byte of whatwhat?
4. Get MblockValue
5. Multiply high byte with low byte (ie. StaminaValue * Mblock), a 16-bit value
6. XBA again! Take the high byte of previous result, effectively dividing by 256?
   (I know this doesn't work as intended, capping at low of 1 and high of 255. I don't know how to do it.)
7. Get Hit Rate
8. Multiply again
9. Again divide by 256
10. Store as $EE
11. Get 100
12. Random number between 0 to 99
13. Is it higher than $EE?
14. Exit, go do something else
15-19. Nothing! ;)


Tested against enemy at 40 Stamina and a spell with a Hit Rate of 147. According to my calculations, this should hit 100 % of the time. Didn't seem to be the case. Hm.
Tested against enemy at 1 Stamina, spell never hit. Wait what?
Tested against Terra at 128 Stamina, spell ALWAYS hit. At 74 hit rate (one half) it sometimes missed.
Tested against Vicks at 1 Stamina, both spells never hit.

Now, it doesn't crash the game or anything horrible like that, so I'm glad, but it seems to work backwards. I haven't done much testing, but it seems that this code makes it so that LOWER Stamina makes it harder to get hit, so obviously there's some parts of the code that I don't understand. That, or I made a design error, I was rather tired last night.


Sorry for the long post, but if you're still here reading this, I'd very much appreciate some help, comments or insights from more experienced hackers.

Specifically,

0. Is my code at all functional? ;)
1. WHY is it working backwards?
2. Also, if you spot any misunderstandings I've made in trying to understand HOW this works, please comment.
3. Oh, and also, if Stamina > 128, what happens when (StaminaValue * BlockValue) < 1?

Please bear in mind that I have absolutely NO assembly knowledge, apart from what I managed to grasp while looking through the disassembly last night and trying to make some sense of it. :)

Attached are my test calculations. If you spot any mistakes please comment.

Sincerely,
Fredrik

Pages: 1