Match the original faint sound sequence per side (#709)

pokered plays no 'pitched-down faint cry': the player mon's faint is its
ordinary species cry (RemoveFaintedPlayerMon -> PlayCry) with no
Faint_Fall, and the enemy faint plays no species cry at all -- trainer
battles get SFX_FAINT_FALL then SFX_FAINT_THUD, wild battles go straight
to the victory music (FaintEnemyPokemon core.asm:732-796).

The port played the species cry AND Faint_Fall on every faint, so a
fainted enemy sounded its full battle cry and a fainted player mon got
the fall whistle the hardware never plays.

BattleState.onFaint now:
- player: Sound.playCry only
- enemy trainer: Faint_Fall then Faint_Thud (after the slide)
- enemy wild: no faint sfx (victory music already queued)

Adds tests/parity_faint_cry_bug709.lua asserting the per-side sequence.

Fixes #709
This commit is contained in:
Shane McGovern
2026-08-03 01:55:52 +01:00
parent 839b238e74
commit 175bff4b29
2 changed files with 137 additions and 2 deletions
+17 -2
View File
@@ -3540,8 +3540,16 @@ function BattleState:onFaint(battler)
self:actNext(function()
battler.fainted = true
local Sound = require("src.core.Sound")
Sound.playCry(self.data, battler.mon.species)
Sound.play(self.data, "Faint_Fall")
if battler.isPlayer then
-- RemoveFaintedPlayerMon (core.asm:1040-1042): the player mon's
-- faint plays its ordinary species cry -- no Faint_Fall
Sound.playCry(self.data, battler.mon.species)
elseif self.kind ~= "wild" then
-- FaintEnemyPokemon (core.asm:732-771): the enemy faint plays no
-- species cry; trainer battles get SFX_FAINT_FALL, then SFX_FAINT_THUD
-- once it finishes (wild battles skip straight to the victory music)
Sound.play(self.data, "Faint_Fall")
end
self.fx = self.fx or {}
-- SlideDownFaintedMonPic: PIC_HEIGHT (7) slide steps, each closing with
-- DelayFrames 2 (core.asm:1186-1222). The port held this one twice as
@@ -3550,6 +3558,13 @@ function BattleState:onFaint(battler)
end)
self.nextInsert = (self.nextInsert or 0) + 1
table.insert(self.queue, self.nextInsert, { wait = Timing.FAINT_SLIDE })
if not battler.isPlayer and self.kind ~= "wild" then
-- FaintEnemyPokemon's SFX_FAINT_THUD lands as the slide does (after
-- Faint_Fall, before EnemyMonFaintedText)
self:actNext(function()
require("src.core.Sound").play(self.data, "Faint_Thud")
end)
end
if not battler.isPlayer and self.kind == "wild" then
-- FaintEnemyPokemon .wild_win (core.asm:792-795): beating a wild
-- mon calls EndLowHealthAlarm and starts MUSIC_DEFEATED_WILD_MON