CLOSES #1396, CLOSES #1398, CLOSES #1400, CLOSES #1401, CLOSES #1406, CLOSES #1407, CLOSES #1411, CLOSES #1413, CLOSES #1415, CLOSES #1416, CLOSES #1417, CLOSES #1419, CLOSES #1421, CLOSES #1422, CLOSES #1423, CLOSES #1424, CLOSES #1425, CLOSES #1427, CLOSES #1428, CLOSES #1429, CLOSES #1431, CLOSES #1432, CLOSES #1433, CLOSES #1435, CLOSES #1437, CLOSES #1440, CLOSES #1441, CLOSES #1442, CLOSES #1443, CLOSES #1444, CLOSES #1447, CLOSES #1449, CLOSES #1456, CLOSES #1461, CLOSES #1464, CLOSES #1465, CLOSES #1466, CLOSES #1468, CLOSES #1469, CLOSES #1470

This commit is contained in:
bryanthaboi
2026-08-17 10:27:14 -04:00
parent 45519ad550
commit 8c9af95598
11 changed files with 186 additions and 10 deletions
@@ -46,4 +46,30 @@ do
T.eq(tradedWithId.otId, 777, "a recorded foreign OT id is never overwritten")
end
-- #1461: saves that passed through 0.1.82-0.1.9x already have the player's
-- own id written onto traded mons, so stamping correctly from now on does
-- not help them. The repair runs on every load, not behind a format gate.
do
local SaveData = require("src.core.SaveData")
local save = newSave()
local poisoned = { traded = true, otId = 12345 }
local foreign = { traded = true, otId = 777 }
local caught = { otId = 12345 }
save.party = { poisoned, foreign, caught }
save.boxes = { { { traded = true, otId = 12345 } } }
T.eq(SaveData.repairTradedOtIds(save), 2, "both poisoned mons are repaired")
T.eq(poisoned.otId, nil, "a traded mon carrying the player id is cleared")
T.eq(save.boxes[1][1].otId, nil, "boxed mons are repaired too")
T.eq(foreign.otId, 777, "a real foreign OT id is left alone")
T.eq(caught.otId, 12345, "a caught mon keeps the player id")
T.eq(caught.traded, nil, "and is never marked traded")
T.eq(SaveData.repairTradedOtIds(save), 0, "the repair is idempotent")
-- and after the repair the stamp loop must not re-adopt it
BattleState.stampOT(save, poisoned)
T.eq(poisoned.otId, nil, "the stamp loop does not undo the repair")
end
T.finish("exp traded ot survives reload bug 1265")
@@ -0,0 +1,62 @@
-- engine/battle/effect_commands.asm:5788, :3671, :3646, :4019
package.path = "./?.lua;./?/init.lua;" .. package.path
love = require("tests.love_stub")
local T = require("tests.harness")
local Battle = require("src.battle.gen2.Battle")
local MATCHUPS = {
{ attacker = "ELECTRIC", defender = "GROUND", multiplier = 0 },
{ attacker = "POISON", defender = "STEEL", multiplier = 0 },
{ attacker = "POISON", defender = "POISON", multiplier = 5 },
{ attacker = "NORMAL", defender = "GHOST", multiplier = 0 },
{ attacker = "PSYCHIC_TYPE", defender = "GROUND", multiplier = 10 },
}
local POKEMON = {
GEODUDE = { types = { "ROCK", "GROUND" } },
MAGNEMITE = { types = { "STEEL", "ELECTRIC" } },
ZUBAT = { types = { "POISON", "FLYING" } },
GASTLY = { types = { "GHOST", "POISON" } },
PIDGEY = { types = { "NORMAL", "FLYING" } },
}
local battle = setmetatable({
data = { type_chart = { matchups = MATCHUPS }, pokemon = POKEMON },
}, { __index = Battle })
local function mon(species) return { species = species, hp = 20 } end
local function refused(species, moveType, status)
return battle:statusRefusedByType(mon(species), moveType, status)
end
-- The two repros the reporter filed, neither of which the #1318 fix touched.
T.eq(refused("GEODUDE", "ELECTRIC", "paralyze"), true,
"Thunder Wave does not affect a GROUND type")
T.eq(refused("MAGNEMITE", "POISON", "poison"), true,
"Poison Gas does not affect a STEEL type")
-- Poison vs POISON is 0.5x, not an immunity, so only CheckIfTargetIsPoisonType
-- refuses it: this is the Poison Sting on Zubat case.
T.eq(refused("ZUBAT", "POISON", "poison"), true,
"a POISON type cannot be poisoned even on a resisted, landed hit")
T.eq(refused("GASTLY", "POISON", "toxic"), true,
"Toxic is refused by the same check")
T.eq(refused("GASTLY", "NORMAL", "paralyze"), true,
"Glare does not affect a GHOST type")
-- Everything the cart deliberately leaves ungated stays ungated.
T.eq(refused("GEODUDE", "PSYCHIC_TYPE", "sleep"), false,
"Hypnosis still lands on a GROUND type")
T.eq(refused("GEODUDE", "PSYCHIC_TYPE", "confuse"), false,
"confusion is never type-gated")
T.eq(refused("PIDGEY", "ELECTRIC", "paralyze"), false,
"Thunder Wave still paralyses a non-immune target")
T.eq(refused("PIDGEY", "POISON", "poison"), false,
"a non-POISON target is still poisonable")
T.eq(refused("GEODUDE", nil, "paralyze"), false,
"a status with no move type behind it is not refused")
T.finish("gen2 status type immunity bug 1444")
+27
View File
@@ -132,6 +132,33 @@ do
check(cat.species[1] < cat.species[2], "species sorted")
end
-- RomExtractorGen2 stamps generation/source beside the id-keyed records, and
-- they sort last because lowercase follows uppercase. #1466
do
local gold = {
pokemon = { generation = 2, source = "ROM", CHIKORITA = { name = "CHIKORITA" } },
items = { generation = 2, source = "ROM", POTION = { name = "POTION" } },
moves = {
generation = 2, source = "ROM:Moves + MoveNames",
TACKLE = { pp = 35 }, ZAP_CANNON = { pp = 5 },
},
}
local cat = Catalog.build(gold)
eq(#cat.moves, 2, "gold move catalog holds only real moves")
eq(cat.moves[#cat.moves], "ZAP_CANNON", "and the last entry is a move, not a scalar")
for _, list in pairs(cat) do
for _, id in ipairs(list) do
check(id ~= "generation" and id ~= "source",
"no provenance scalar reached a catalog: " .. tostring(id))
end
end
local S = { data = gold, cat = cat }
local mon = { moves = { { id = "ZAP_CANNON", pp = 5 } } }
check(require("Ops").cycleMove(S, mon, 1), "cycling off the last move succeeds")
eq(mon.moves[1].id, "TACKLE", "and wraps to the first move instead of a scalar")
end
do
local events = Catalog.scrapeEvents("data/scripts", "data/generated/trainer_headers.lua")
check(#events > 50, "scraped events")