Merge pull request #537 from sirj0k3r/hook/exp

This commit is contained in:
bryanthaboi
2026-08-01 00:21:20 -04:00
committed by GitHub
2 changed files with 70 additions and 18 deletions
+30 -4
View File
@@ -3230,7 +3230,10 @@ function BattleState:onFaint(battler)
end end
end end
function BattleState:enemyMonFainted() -- Exp for the defeated enemy, shared by the faint path (enemyMonFainted)
-- and, when a mod's battle.catch_exp hook says so, the catch path
-- (storeCaughtMon).
function BattleState:awardExp()
-- exp is split among the mons that fought this enemy -- exp is split among the mons that fought this enemy
-- (engine/battle/experience.asm); traded mons earn x1.5; each -- (engine/battle/experience.asm); traded mons earn x1.5; each
-- participant gets the full stat exp -- participant gets the full stat exp
@@ -3308,11 +3311,16 @@ function BattleState:enemyMonFainted()
end end
end end
end end
-- battle.exp_award: the participant/EXP.ALL split, factored out so a
-- mod can replace it wholesale (e.g. a flat undivided share to every
-- non-fainted party mon) without re-deriving participants/alive.
-- ctx.applyShare(mon, split, announce) is the same helper vanilla uses.
local function vanillaExpAward(ctx)
-- with EXP.ALL, participants split half the exp and the other half -- with EXP.ALL, participants split half the exp and the other half
-- is divided among the whole party (engine/battle/experience.asm) -- is divided among the whole party (engine/battle/experience.asm)
local expAll = (self.game.save.inventory.EXP_ALL or 0) > 0 local expAll = (self.game.save.inventory.EXP_ALL or 0) > 0
for _, mon in ipairs(alive) do for _, mon in ipairs(ctx.alive) do
applyShare(mon, participants * (expAll and 2 or 1), true) ctx.applyShare(mon, ctx.participants * (expAll and 2 or 1), true)
end end
if expAll then if expAll then
-- the second GainExperience pass sets the gain flags for the WHOLE -- the second GainExperience pass sets the gain flags for the WHOLE
@@ -3324,11 +3332,23 @@ function BattleState:enemyMonFainted()
-- summary line -- summary line
for _, mon in ipairs(self.game.save.party) do for _, mon in ipairs(self.game.save.party) do
if mon.hp > 0 then if mon.hp > 0 then
applyShare(mon, math.max(1, participants) * #self.game.save.party * 2, "expAll") ctx.applyShare(mon, math.max(1, ctx.participants) * #self.game.save.party * 2, "expAll")
end end
end end
end end
end
local awardCtx = { battle = self, participants = participants, alive = alive,
applyShare = applyShare }
if Runtime.wantsHook("battle.exp_award") then
Runtime.call("battle.exp_award", vanillaExpAward, awardCtx)
else
vanillaExpAward(awardCtx)
end
self.participants = {} self.participants = {}
end
function BattleState:enemyMonFainted()
self:awardExp()
if self.kind == "trainer" then if self.kind == "trainer" then
-- EnemySendOutFirstMon / AnyEnemyPokemonAliveCheck (core.asm): scan -- EnemySendOutFirstMon / AnyEnemyPokemonAliveCheck (core.asm): scan
@@ -3889,6 +3909,12 @@ function BattleState:storeCaughtMon()
-- (item_effects.asm:472-501), regenerating its move list from the -- (item_effects.asm:472-501), regenerating its move list from the
-- base data -- a Mimic'd slot never leaves the battle with it -- base data -- a Mimic'd slot never leaves the battle with it
self:restoreMimicked(self.enemy) self:restoreMimicked(self.enemy)
-- battle.catch_exp: vanilla catches never grant exp; a mod can flip
-- this to true to pay out the same award a faint would have.
if Runtime.wantsHook("battle.catch_exp")
and Runtime.call("battle.catch_exp", function() return false end, { battle = self }) then
self:awardExp()
end
local game = self.game local game = self.game
local dex = game.save.pokedex local dex = game.save.pokedex
local species = self.enemy.mon.species local species = self.enemy.mon.species
+26
View File
@@ -716,6 +716,32 @@ do
check(actBattle:enemyAction().hooked == true, check(actBattle:enemyAction().hooked == true,
"battle.enemy_action hook rewrites the choice") "battle.enemy_action hook rewrites the choice")
unsub() unsub()
-- battle.catch_exp: vanilla catches never grant exp; a mod can flip that
unsub = hooks:wrap("battle.catch_exp", function() return true end)
local catchExpParty = { Pokemon.new(Data, "BULBASAUR", 10) }
local catchExpGame = makeGame(catchExpParty)
local catchExpBattle = BattleState.newWild(catchExpGame, "RATTATA", 3)
catchExpBattle.enemy.mon = Pokemon.new(Data, "RATTATA", 3)
local expBeforeCatch = catchExpParty[1].exp
catchExpBattle:storeCaughtMon()
check(catchExpParty[1].exp > expBeforeCatch,
"battle.catch_exp hook pays out exp on a catch")
unsub()
-- battle.exp_award: a mod can replace the participant/EXP.ALL split
-- wholesale via ctx.applyShare
unsub = hooks:wrap("battle.exp_award", function(nextFn, ctx)
ctx.applyShare(ctx.alive[1], 999, "flatShare")
end)
local awardParty = { Pokemon.new(Data, "BULBASAUR", 10) }
local awardGame = makeGame(awardParty)
local awardBattle = BattleState.newWild(awardGame, "RATTATA", 3)
local expBeforeAward = awardParty[1].exp
awardBattle:awardExp()
check(awardParty[1].exp > expBeforeAward,
"battle.exp_award hook replaces the award split")
unsub()
end end
-- ------- battle.low_health_alarm hook: mirrors the siren toggle -- ------- battle.low_health_alarm hook: mirrors the siren toggle