[Patch] Granular Damage Modification - seibaby - 02-05-2017
Sup folks! @Imzogelmo recently quoted me (in agreement) as saying the equivalent of "this thing should exist". And I made it exist, actually, a while back, then promptly forgot all about it.
What is this, in a nutshell?
It allows a hacker to to make an attack apply, or not apply, each step in damage modification separately.
What are the steps of damage modification?
- Random variance
- Defense/Mdef
- Safe/Shell
- Defend
- Row
- Morph
- Self damage theory
- Damage multiplier
A bit of background info: the damage modification function performs modifications to the final damage done by an attack, most noticeably the reduction by Defense, but also a host of other minor functions. In fact, there are exactly eight such functions, as described above.
There is a spell bit for Ignore Defense, which actually causes the attack to ignore a lot more: Defense, Safe/Shell, Defend, Row, and Morph are all skipped if this is set. The whole thing really isn't very flexible. However, there is a single-byte battle variable ($3414) which, if cleared, causes the entirety of the damage modification function to be skipped. How many bits are in a byte? That's right, eight!
So I took it upon myself to modify the damage modification function to parse each bit of $3414 separately, to ensure that a hacker could separately set any combination of damage modifications for an attack. Now you can simply hook any place where $3414 is cleared (conveniently listed in the code) to insert a small function to set any desired combination of bits (example provided!).
Honestly, the fact that it took me this long to share this is reflective of how few actual use cases I saw for it. All I wanted was Ignore Target Row, and that's included, but it's really more of an add-on to the main functionality of this patch (I should split that off and make a separate patch for it...). Hopefully, someone with more imagination than me can make better use of this code.
It's been a while since I touched (or tested) this code, so I offer no guarantees. Also, I didn't bother to optimize free space usage - I simply relocated the entire function because the entire thing is full of branches and a pain to split up. This isn't a "plug and play" type of patch, anyway - to make use of it, you need at least basic assembly knowledge.
xkas 0.06 compatible code:
Code: ;Make damage modification more granular by checking each bit of $3414 separately
;Also make long range attacks (ignore attacker row) ignore defender row.
;58 bytes larger than original damage modification function
;If bit is set:
;bit 0 - Damage affected by random variance
;bit 1 - Damage affected by Defense/Mdef
;bit 2 - Damage affected by Safe/Shell
;bit 3 - Damage affected by Defend
;bit 4 - Damage affected by Row
;bit 5 - Damage affected by Morph
;bit 6 - Damage affected by Self damage theory
;bit 7 - Damage incremented by damage multiplier
;Note: different multipliers are applied at different times in damage resolution;
;ie. in separate code locations, to allow things like crits to stack multiplicatively
;with other damage multipliers.
;I haven't mapped out which multipliers are applied at what times
hirom
;header
!freespace = $C2A65A
;Replace old damage modification function call
org $C20B93
;JSR $0C9E ;(Damage modification)
JSR damage_mod
;Damage modification (Randomness, Row, Self Damage, and more)
org !freespace
damage_mod:
;Setup - store damage in 16-bit $F0
PHP
REP #$20
LDA $11B0 ;Maximum Damage
STA $F0
SEP #$20 ;Set 8-bit Accumulator
;Check if skips all
LDA $3414
BNE .randomness ;Exit if Skip damage modification
JMP .exit
.randomness
PHA ;Store $3414
;Check if Damage affected by random variance
BIT #$01
BEQ .mp_damage ;Branch if bit 0 of $3414 was clear
;Damage affected by random variance
JSR $4B5A ;Random number 0 to 255
ORA #$E0 ;Set bits 7,6,5; bits 0,1,2,3,4 are random
STA $E8 ;Random number [224..255]
JSR $0D3D ;Damage randomness
.mp_damage
;Check if Concern MP, or if Magical/physical damage type
CLC ;Carry (0 = Magical, 1 = Physical)
LDA $11A3
BMI .defense ;Branch if Concern MP (Concern MP = always magical)
LDA $11A2
LSR ;isolate magical vs. physical property
;Carry (0 = Magical, 1 = Physical)
.defense
PHP ;save Carry flag, which equals (physical attack) AND NOT(Concern MP)
;Check if attack Ignores Defense (and Safe/Shell, Morph, and Row/Defend)
LDA $11A2
BIT #$20
BNE .selfdamage ;Branch if ignores defense
;Check if Damage affected by Defense/Mdef
LDA $02,S
BIT #$02
BEQ .safe_shell ;Branch if bit 2 of $3414 was clear
;Damage affected by Defense/Mdef
LDA $3BB9,Y ;Magic Defense
BCC .mdef ;Branch if concern MP or Magical damage
LDA $3BB8,Y ;Defense
.mdef
INC
BEQ .defense_mod ;Branch if = 255
XBA
LDA $3A82
AND $3A83
BMI .no_golem ;If Blocked by Golem or Dog, defense = 192
LDA #$C1
XBA
.no_golem
XBA
DEC
EOR #$FF
.defense_mod
STA $E8 ;(= 255 - Defense)
JSR $0D3D ;Multiply damage by (255 - Defense) / 256 ,
; then add 1
.safe_shell
;Check if Damage affected by Safe/Shell
LDA $02,S
BIT #$04
BEQ .defend ;Branch if bit 3 was clear
;Damage affected by Safe/Shell
LDA $01,S ;Get physical/magical property
LSR ;Shift into carry
LDA $3EF8,Y ;Status byte 3
BCS .safe ;Branch if physical attack without Concerns MP
ASL ;Check Shell
.safe
ASL ;Check Safe
BPL .defend ;Branch if no Safe / Shell on target
LDA #$AA
STA $E8
JSR $0D3D ;Multiply damage by 170 / 256 , then add 1
.defend
LDA $01,S
LSR
BCC .morph ;Skip row/defend check if magical attack or Concern MP
;Check if Damage affected by Defend
LDA $02,S
BIT #$08
BEQ .row ;Branch if bit 3 of $3414 was clear
;Damage affected by Defend
LDA $3AA1,Y
BIT #$02 ;Check target Defending
BEQ .row ;Branch if target not defending
LSR $F1
ROR $F0 ;Cut damage in half
.row
;Check if damage affected by Row
LDA $02,S
BIT #$10
BEQ .selfdamage ;Branch if bit 4 of $3414 was clear
;Damage affected by Row
LDA $B3
BIT #$20 ;Check if attack ignores attacker Row
BNE .selfdamage ;Branch if ignore attacker Row
LDA $3AA1,Y
BIT #$20 ;Check target row
BEQ .selfdamage ;Branch if target in front row
LSR $F1
ROR $F0 ;Cut damage in half
BRA .selfdamage ;Skip morph if physical attack
;print ".row is ",bytes," bytes long"
.morph
;Check if Damage affected by Morph
LDA $02,S
BIT #$20
BEQ .selfdamage ;Branch if bit 5 of $3414 was clear
;Damage affected by Morph
LDA $3EF9,Y
BIT #$08
BEQ .selfdamage ;Branch if target not morphed
LSR $F1
ROR $F0 ;Cut damage in half)
;print ".morph is ",bytes," bytes long"
.selfdamage
;Check if Damage affected by Self damage theory
LDA $02,S
BIT #$40
REP #$20 ;Set 16-bit Accumulator)
BEQ .multiplier ;Branch if bit 6 of $3414 was clear
;Damage affected by Self damage theory
LDA $11A4
LSR
BCS .multiplier ;Branch if heal; heal skips self damage theory
CPY #$08
BCS .multiplier ;Branch if target is a monster
CPX #$08
BCS .multiplier ;Branch if attacker is monster
LSR $F0 ;Cut damage in half if party attacks party
;print ".selfdamage is ",bytes," bytes long"
.multiplier
;Check if Damage incremented by damage multiplier
PLA ;Clear stack (low byte = physical/magical property, high byte = $3414)
BIT #$8000
BEQ .exit ;Exit if bit 7 of $3414 was clear
;Damage incremented by damage multiplier
.increment
LDA $F0
JSR $370B ;Increment damage using $BC
STA $F0
;print ".multiplier is ",bytes," bytes long"
.exit
PLP
RTS
;Example use case
;Item
;item:
;LDA #$01
;STA $3414 ;Skip all dmg. mod. steps except random variance
;RTS
;Everything that sets Skip Damage Modification
;Item
org $C21897
STZ $3414 ;(Set ignore damage modification)
;JSR item
;GP Rain
org $C21913
STZ $3414 ;(Skip damage modification)
;Runic
org $C23598
STZ $3414 ;(Skip damage modification)
;Step Mine
org $C23EA0
STZ $3414 ;(Set to ignore damage modifiers)
;Pearl Wind
org $C23F59
STZ $3414 ;(Set to not modify damage)
;Exploder
org $C24003
STZ $3414 ;(Set to not modify damage)
;Blow Fish
org $C24103
STZ $3414 ;(Set to not modify damage)
;Flare Star
org $C2410F
STZ $3414 ;(Set to not modify damage)
;Dice
org $C24158
STZ $3414 ;(Set to not modify damage)
;Revenge
org $C241E6
STZ $3414 ;(Set to not modify damage)
;Mantra
org $C24268
STZ $3414 ;(Set to not modify damage)
RE: [Patch] Granular Damage Modification - Imzogelmo - 02-05-2017
Yep, as I said, I thought this is how the designers *should* have done it. I hate to see a whole byte wasted for a true/false flag... But yet, there it is. And to have that byte actually skip 8 individual steps-- it almost screams for a flexibility mod. So thanks, seibaby, and kudos for the code implementation.
As for what to do with it, I still have no idea really, but it would be a great thing to incorporate into a deep hack project. If you don't mind, I think I'll add the functionality into my as-yet-unnamed compilation patch, along with 24-bit HP for monsters, bugfix selection, and other such amenities.
RE: [Patch] Granular Damage Modification - Tenkarider - 02-05-2017
Sooner or later i'll need to check this stuff because, you know... i have an idea or two on how to use them
RE: [Patch] Granular Damage Modification - seibaby - 02-06-2017
(02-05-2017, 08:16 PM)Imzogelmo Wrote: As for what to do with it, I still have no idea really, but it would be a great thing to incorporate into a deep hack project. If you don't mind, I think I'll add the functionality into my as-yet-unnamed compilation patch, along with 24-bit HP for monsters, bugfix selection, and other such amenities.
You're absolutely welcome to use my code in your compilation. Your project sounds really neat and if you don't mind I'd like a copy of that reassemblable C2 bank to use as basis for my own work.
As for utility, most vanilla spells that make use of damage modification really work best with the binary option, so I can see why they simplified things. The best utility I can see is to allow things that ignore defense to not ignore Safe/Shell (and Drakkhen made a patch eons ago for just that). Adding random variance to stuff like potions or Runic is kinda neat, I guess, although mostly for flavour. Where this would shine is most likely in entirely new custom spell or Tools effects.
RE: [Patch] Granular Damage Modification - Imzogelmo - 02-07-2017
Oh, I have a disassembly that will produce the battle engine part of C2, equivalent to vanilla version 1.0. You can get that if you want. But the compilation thing is not quite ready yet.
|