Skip to content

Gen 7 critical hit chance mechanic

mirko93s edited this page Dec 3, 2024 · 3 revisions

This code backports the gen 7+ critical mechanic in gen 1, adding critical stages with their respective chances.

Almost every line is commented, I hope it is easier to understand. This is compatible with Yellow as well!

 ; determines if attack is a critical hit
-; Azure Heights claims "the fastest pokémon (who are, not coincidentally,
-; among the most popular) tend to CH about 20 to 25% of the time."
+; updated to use gen 7 critical mechanics with stages
+; stage 0 = 1/24 ~4.17%
+; stage 1 = 1/8 12.5%
+; stage 2 = 1/2 50%
+; stage 3+ = 100% (we don't have more than 3 stages anyway in gen1)
+; since high crit moves add +2 and focus energy adds +1
 CriticalHitTest:
-	xor a
+	xor a                               ; set critical flag to 0
+	ld b, a                             ; set critical stage to 0
	ld [wCriticalHitOrOHKO], a
-	ldh a, [hWhoseTurn]
+	ldh a, [hWhoseTurn]                 ; check whose turn is this, player or enemy
	and a
-	ld a, [wEnemyMonSpecies]
-	jr nz, .handleEnemy
	ld a, [wBattleMonSpecies]
-.handleEnemy
-	ld [wd0b5], a
-	call GetMonHeader
-	ld a, [wMonHBaseSpeed]
-	ld b, a
-	; srl b                        ; (effective (base speed/2))
-	ldh a, [hWhoseTurn]
-	and a
-	ld hl, wPlayerMovePower
+	ld hl, wPlayerMovePower
	ld de, wPlayerBattleStatus2
-	jr z, .calcCriticalHitProbability
-	ld hl, wEnemyMovePower
+	jr z, .checkIfDamageMove            ; if player's turn jump
+	ld a, [wEnemyMonSpecies]
+	ld hl, wEnemyMovePower
	ld de, wEnemyBattleStatus2
-.calcCriticalHitProbability
-	ld a, [hld]                  ; read base power from RAM
+.checkIfDamageMove
+	ld a, [hld]                         ; read base power from RAM
	and a
-	ret z                        ; do nothing if zero
+	ret z                               ; do nothing if zero (status moves)
	dec hl
-	ld c, [hl]                   ; read move id
-	ld hl, HighCriticalMoves     ; table of high critical hit moves
-.Loop
-	ld a, [hli]                  ; read move from move table
-	cp c                         ; does it match the move about to be used?
-	jr z, .HighCritical          ; if so, the move about to be used is a high critical hit ratio move
-	inc a                        ; move on to the next move, FF terminates loop
-	jr nz, .Loop                 ; check the next move in HighCriticalMoves
-	srl b                        ; /2 for regular move
-	jr .SkipHighCritical         ; continue as a normal move
-.HighCritical
-	sla b                        ; *2 for high critical hit moves
-	jr nc, .noCarry
-	ld b, $ff                    ; cap at 255/256
-.noCarry
-	sla b                        ; *4 for high critical move
-	jr nc, .SkipHighCritical
-	ld b, $ff
-.SkipHighCritical
+	ld c, [hl]                          ; read move id
+	ld hl, HighCriticalMoves            ; table of high critical hit moves
+.checkIfHighCritMoveLoop
+	ld a, [hli]                         ; read move from move table
+	cp c                                ; does it match the move about to be used?
+	jr z, .highCriticalMove             ; if so, the move about to be used is a high critical hit ratio move
+	inc a                               ; move on to the next move, FF terminates loop
+	jr nz, .checkIfHighCritMoveLoop     ; check the next move in HighCriticalMoves
+	jr .checkForFocusEnergy             ; continue as a normal move
+.highCriticalMove
+	inc b                               ; +2 stages for high crit moves
+	inc b
+.checkForFocusEnergy
	ld a, [de]
-	bit GETTING_PUMPED, a        ; test for focus energy
+	bit GETTING_PUMPED, a               ; test for focus energy
	jr z, .noFocusEnergyUsed
-	sla b                        ; (effective (base speed*2))
-	jr nc, .focusEnergyUsed
-	ld b, $ff                    ; cap at 255/256
-	jr .noFocusEnergyUsed
-.focusEnergyUsed
-	sla b                        ; (effective ((base speed*2)*2))
-	jr nc, .noFocusEnergyUsed
-	ld b, $ff                    ; cap at 255/256
+	inc b                               ; focus energy +1 stage
.noFocusEnergyUsed
	ld a, b
-	inc a ; optimization of "cp $ff"
-	jr z, .guaranteedCriticalHit
-	call BattleRandom            ; generates a random value, in "a"
-	rlc a
-	rlc a
-	rlc a
-	cp b                         ; check a against calculated crit rate
-	ret nc                       ; no critical hit if no borrow
-.guaranteedCriticalHit
+	cp 3                                ; stage 3+ 100% chance
+	jr z, .criticalHit
+	cp 2
+	ld b, 128                           ; stage 2 1/2 chance 50%
+	jr z, .rng
+	cp 1
+	ld b, 32                            ; stage 1 1/8 chance 12.5%
+	jr z, .rng
+	ld b, 11                            ; stage 0 1/24 chance ~4.17%
+.rng
+	call BattleRandom                   ; generates a random value, in "a"
+	cp b                                ; check a against calculated crit rate
+	ret nc                              ; no critical hit if no borrow
+; code below is a special case for stage 0 and a == 10
+; in this case only about 2/3 of the 10s are critical hits
+; doing this we get exactly 1/24 chance for stage 0
+; which is exactly how it works in gen7+
+	cp 11                               ; check if this is stage 0
+	jr nz, .criticalHit                 ; if not stage 0 skip and apply crit
+	cp 10                               ; check if rng is 10
+	jr nz, .criticalHit                 ; if rng is not 10 we skip all the code below and apply crit
+	call BattleRandom                   ; generates a random value, in "a"
+	cp 170                              ; check against 170 ~2/3 chance
+	ret nc                              ; no critical hit if borrow
+.criticalHit
	ld a, $1
-	ld [wCriticalHitOrOHKO], a   ; set critical hit flag
+	ld [wCriticalHitOrOHKO], a          ; set critical hit flag
	ret

Since it is almost all new code, if you prefer, you can just copy paste the entire label from the spoiler below without any diff

Click here for the code
; determines if attack is a critical hit
; updated to use gen 7 critical mechanics with stages
; stage 0 = 1/24 ~4.17%
; stage 1 = 1/8 12.5%
; stage 2 = 1/2 50%
; stage 3+ = 100% (we don't have more than 3 stages anyway in gen1)
; since high crit moves add +2 and focus energy adds +1
CriticalHitTest:
	xor a                               ; set critical flag to 0
	ld b, a                             ; set critical stage to 0
	ld [wCriticalHitOrOHKO], a
	ldh a, [hWhoseTurn]                 ; check whose turn is this, player or enemy
	and a
	ld a, [wBattleMonSpecies]
	ld hl, wPlayerMovePower
	ld de, wPlayerBattleStatus2
	jr z, .checkIfDamageMove            ; if player's turn jump
	ld a, [wEnemyMonSpecies]
	ld hl, wEnemyMovePower
	ld de, wEnemyBattleStatus2
.checkIfDamageMove
	ld a, [hld]                         ; read base power from RAM
	and a
	ret z                               ; do nothing if zero (status moves)
	dec hl
	ld c, [hl]                          ; read move id
	ld hl, HighCriticalMoves            ; table of high critical hit moves
.checkIfHighCritMoveLoop
	ld a, [hli]                         ; read move from move table
	cp c                                ; does it match the move about to be used?
	jr z, .highCriticalMove             ; if so, the move about to be used is a high critical hit ratio move
	inc a                               ; move on to the next move, FF terminates loop
	jr nz, .checkIfHighCritMoveLoop     ; check the next move in HighCriticalMoves
	jr .checkForFocusEnergy             ; continue as a normal move
.highCriticalMove
	inc b                               ; +2 stages for high crit moves
	inc b
.checkForFocusEnergy
	ld a, [de]
	bit GETTING_PUMPED, a               ; test for focus energy
	jr z, .noFocusEnergyUsed
	inc b                               ; focus energy +1 stage
.noFocusEnergyUsed
	ld a, b
	cp 3                                ; stage 3+ 100% chance
	jr z, .criticalHit
	cp 2 
	ld b, 128                           ; stage 2 1/2 chance 50%
	jr z, .rng
	cp 1
	ld b, 32                            ; stage 1 1/8 chance 12.5%
	jr z, .rng
	ld b, 11                            ; stage 0 1/24 chance ~4.17%
.rng
	call BattleRandom                   ; generates a random value, in "a"
	cp b                                ; check a against calculated crit rate
	ret nc                              ; no critical hit if no borrow
; code below is a special case for stage 0 and a == 10
; in this case only about 2/3 of the 10s are critical hits
; doing this we get exactly 1/24 chance for stage 0
; which is exactly how it works in gen7+
	cp 11                               ; check if this is stage 0
	jr nz, .criticalHit                 ; if not stage 0 skip and apply crit
	cp 10                               ; check if rng is 10
	jr nz, .criticalHit                 ; if rng is not 10 we skip all the code below and apply crit
	call BattleRandom                   ; generates a random value, in "a"
	cp 170                              ; check against 170 ~2/3 chance
	ret nc                              ; no critical hit if borrow
.criticalHit
	ld a, $1
	ld [wCriticalHitOrOHKO], a          ; set critical hit flag
	ret
Clone this wiki locally