Physical Special Split - pret/pokered GitHub Wiki

In generations I, II, and III, the move types determined whether the damage type was physical or special. After generation IV, any move type could be physical or special. In order to implement this, we make a list of the physical moves that should become special and a list of all the special moves that should be physical. Then during damage calculation, we check if the move used is in one of the lists.

List of Moves which Switch Damage Type

Create the file physical_special_split.asm in data/battle/, and add the following to it,

PhysicalToSpecialMoves:
; Flying
        db GUST
; Poison
        db ACID
        db SLUDGE
; Normal
        db HYPER_BEAM
        db RAZOR_WIND
        db SWIFT
        db TRI_ATTACK
        db -1 ; end

SpecialToPhysicalMoves:
; Fire
        db FIRE_PUNCH
; Water
        db CLAMP
        db CRABHAMMER
        db WATERFALL
; Grass
        db RAZOR_LEAF
        db VINE_WHIP
; Electric
        db THUNDERPUNCH
; Ice
        db ICE_PUNCH
        db -1 ; end

These should be all the moves in generation I that flipped damage type. If you add in a new move, and the damage type is different from the move type, then add it to the appropriate list above.

Damage Calculation Changes

Now open up engine/battle/core.asm We will be modifying the GetDamageVarsForPlayerAttack subroutine.

...
GetDamageVarsForPlayerAttack:
	xor a
	ld hl, wDamage ; damage to eventually inflict, initialise to zero
	ldi [hl], a
	ld [hl], a
	ld hl, wPlayerMovePower
	ld a, [hli]
	and a
	ld d, a ; d = move power
	ret z ; return if move power is zero
	ld a, [hl] ; a = [wPlayerMoveType]
	cp SPECIAL ; types >= SPECIAL are all special
-	jr nc, .specialAttack
+        ld a, [wPlayerMoveNum]
+        ld b, a
+        jr nc, .isSpecialActuallyPhysical
+        jr .isPhysicalActuallySpecial
+.isSpecialActuallyPhysical
+        ld hl, SpecialToPhysicalMoves
+.specialPhysicalLoop
+        ld a, [hli]
+        cp b
+        jr z, .physicalAttack
+        cp $ff ; end of list
+        jr nz, .specialPhysicalLoop ; keep checking list
+        jr .specialAttack ; Not actually a physical move
+.isPhysicalActuallySpecial
+        ld hl, PhysicalToSpecialMoves
+.physicalSpecialLoop
+        ld a, [hli]
+        cp b
+        jr z, .specialAttack ; the physical move is actually special
+        cp $ff ; end of list
+        jr nz, .physicalSpecialLoop ; keep checking list
+        ; fallthrough
.physicalAttack
	ld hl, wEnemyMonDefense
	ld a, [hli]
	ld b, a
	ld c, [hl] ; bc = enemy defense	
...

We will now make a similar modification for GetDamageVarsForEnemyAttack.

...
GetDamageVarsForEnemyAttack:
	ld hl, wDamage ; damage to eventually inflict, initialise to zero
	xor a
	ld [hli], a
	ld [hl], a
	ld hl, wEnemyMovePower
	ld a, [hli]
	ld d, a ; d = move power
	and a
	ret z ; return if move power is zero
	ld a, [hl] ; a = [wEnemyMoveType]
	cp SPECIAL ; types >= SPECIAL are all special
-	jr nc, .specialAttack
+        ld a, [wEnemyMoveNum]
+        ld b, a
+        jr nc, .isSpecialActuallyPhysical
+        jr .isPhysicalActuallySpecial
+.isSpecialActuallyPhysical
+        ld hl, SpecialToPhysicalMoves
+.specialPhysicalLoop
+        ld a, [hli]
+        cp b
+        jr z, .physicalAttack
+        cp $ff ; end of list
+        jr nz, .specialPhysicalLoop ; keep checking list
+        jr .specialAttack ; Not actually a physical move
+.isPhysicalActuallySpecial
+        ld hl, PhysicalToSpecialMoves
+.physicalSpecialLoop
+        ld a, [hli]
+        cp b
+        jr z, .specialAttack ; the physical move is actually special
+        cp $ff ; end of list
+        jr nz, .physicalSpecialLoop ; keep checking list
+        ; fallthrough
.physicalAttack
	ld hl, wBattleMonDefense
	ld a, [hli]
	ld b, a
	ld c, [hl] ; bc = player defense
...

And lastly, at the end of this subroutine, add in the INCLUDE statement for new file.

...
.done
        ld a, $1
        and a
        and a
        ret

+INCLUDE "data/battle/physical_special_split.asm"

; get stat c of enemy mon
; c: stat to get (HP=1,Attack=2,Defense=3,Speed=4,Special=5)
GetEnemyMonStat:
...

That's it. I should mention that the changes to engine/battle/core.asm are not the best. There should really be a separate subroutine that GetDamageVarsForPlayerAttack and GetDamageVarsForEnemyAttack both use instead of essentially copy pasting the same code. I haven't had a chance to get around to it yet, but I (or anyone else) will/can update the tutorial once its changed.