mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-29 15:08:50 -04:00
2c356f28ea
The rest of the Gen 3 review fixes, each with a gated suite in tests/engine/.
ROM semantics were checked against pret/pokefirered.
Scripts
- givemon carried the wrong operand layout, found earlier; four more layout
desyncs came out of pret asm/macros/event.inc: comparestat is {byte,word},
and setptr / loadbytefromptr / setptrbyte each carry a leading byte plus a
word. A wrong size mis-decodes every instruction after the bad one, so
Versions.CACHE_VERSION moves to 113 and existing caches re-import.
- handlers for previously handler-less verbs: comparestat,
bufferitemnameplural, setmonmove, setmonmetlocation, the modern
fateful-encounter pair, the script-locals family (copylocal, setptr,
loadbytefromptr, setptrbyte, copybyte, compare_local_to_* and
compare_ptr_to_*), the RAM-script family (setvaddress, vgoto, vcall,
vgoto_if, vcall_if, vmessage, vbuffermessage, vbufferstring, endram,
returnram) and the same-map forms of the *at verbs.
- setdooropen / setdoorclosed read their coordinates through VarGet.
Battles
- Knock Off and Thief / Trick persist the item change instead of only
touching the in-battle copy.
- knocked-off party slots are tracked in a bitmask, so a slot reused later
does not inherit the flag.
Field and UI
- Player.reset restores facing and clears the surf flags.
- a definition-less Map.load no longer leaves collision unbound.
- an unresolved region-map section no longer reports PALLET TOWN.
- the naming screen splits input from the timer, so update(dt) stops
indexing a number.
- the hall of fame commits through the engine save path and serializes its
fields.
Persistence
- gameStats, the link-battle records and the trainer card are serialized.
- the PC deposit refuses at the 999 cap instead of destroying the overflow.
Robustness
- Data.load runs cached modules sandboxed.
- the file browser quotes shell arguments.
- .meta dimensions are bounds-checked and mids.idx validates its header.
100 lines
3.8 KiB
Lua
100 lines
3.8 KiB
Lua
-- A knocked-off item must stay marked for the rest of the battle.
|
|
--
|
|
-- Regression: KNOCK_OFF only set the per-battler volatile `expKnockedOff`, which
|
|
-- dies when that battler leaves the field. pret keeps
|
|
-- gWishFutureKnock.knockedOffMons -- one bit per party index per side -- so the
|
|
-- guard survives switch-out: a mon whose item was knocked off cannot have an
|
|
-- item stolen or swapped for the rest of the battle, even after it comes back
|
|
-- holding something else.
|
|
--
|
|
-- These drive the real effect functions; the battle-scoped state is the adapter's
|
|
-- `_st`, exactly as the engine carries it.
|
|
-- luajit tests/engine/game3_knocked_off_flag_test.lua
|
|
|
|
package.path = "./?.lua;./?/init.lua;" .. package.path
|
|
|
|
local T = require("tests.harness")
|
|
local check, eq = T.check, T.eq
|
|
love = love or require("tests.love_stub")
|
|
|
|
local Secondary = require("src.core.game3.battle.effects.secondary")
|
|
local Special = require("src.core.game3.battle.effects.special")
|
|
|
|
local failures = 0
|
|
|
|
local function adapter(st)
|
|
return {
|
|
_st = st,
|
|
abilityOf = function(_, b) return b.ability end,
|
|
hp = function(_, b) return b.hp or 100 end,
|
|
ownSide = function() return nil end,
|
|
foeSide = function() return nil end,
|
|
displayName = function(_, b) return b.name or "MON" end,
|
|
playAnim = function() end,
|
|
say = function() end,
|
|
sayFail = function() failures = failures + 1 end,
|
|
roll = function(_, lo) return lo end,
|
|
pushEvent = function() end,
|
|
}
|
|
end
|
|
|
|
local function battler(side, partyIndex, item, ability)
|
|
return {
|
|
side = side, partyIndex = partyIndex, id = 0,
|
|
name = side == "player" and "CHARMANDER" or "RATTATA",
|
|
hp = 100, item = item, ability = ability,
|
|
mon = item and { item = item, heldItem = item } or {},
|
|
}
|
|
end
|
|
|
|
local function knock_off(st, user, target)
|
|
return Secondary.set({ adapter = adapter(st), user = user, target = target,
|
|
move = { moveName = "KNOCK OFF" } },
|
|
"KNOCK_OFF", false, true, false)
|
|
end
|
|
|
|
local function steal(st, user, target)
|
|
return Secondary.set({ adapter = adapter(st), user = user, target = target,
|
|
move = { moveName = "THIEF" } },
|
|
"STEAL_ITEM", false, true, false)
|
|
end
|
|
|
|
local function trick(st, user, target)
|
|
failures = 0
|
|
Special.trick({ adapter = adapter(st), user = user, target = target })
|
|
return failures
|
|
end
|
|
|
|
-- 1. After a knock-off, Trick still refuses the victim once it has switched out
|
|
-- and come back holding something else.
|
|
local st = {}
|
|
knock_off(st, battler("player", 1, 0), battler("enemy", 1, 13))
|
|
local returning = battler("enemy", 1, 14)
|
|
eq(trick(st, battler("player", 1, 13), returning), 1,
|
|
"TRICK refuses a mon knocked off earlier this battle, after switch-out")
|
|
|
|
-- 2. A different party index on the same side is unaffected.
|
|
eq(trick(st, battler("player", 1, 13), battler("enemy", 2, 14)), 0,
|
|
"TRICK still works against a mon that was not knocked off")
|
|
|
|
-- 3. Thief also refuses when the *thief* had its own item knocked off earlier,
|
|
-- which only the battle-scoped mask can remember. (The thief's hands are
|
|
-- empty, as they must be to steal at all.)
|
|
local st2 = {}
|
|
knock_off(st2, battler("enemy", 1, 0), battler("player", 1, 13))
|
|
check(not steal(st2, battler("player", 1, 0), battler("enemy", 1, 13)),
|
|
"THIEF refuses after the user's own item was knocked off earlier this battle")
|
|
|
|
-- 4. Control: the same Thief works when nothing was knocked off.
|
|
local st3 = {}
|
|
check(steal(st3, battler("player", 1, 0), battler("enemy", 1, 13)),
|
|
"THIEF still steals when no knock-off happened")
|
|
|
|
-- 5. The mask is per side: an enemy knock-off does not block the player.
|
|
local st4 = {}
|
|
knock_off(st4, battler("player", 1, 0), battler("enemy", 1, 13))
|
|
eq(trick(st4, battler("player", 1, 14), battler("enemy", 2, 13)), 0,
|
|
"an enemy knock-off does not block the player's own party index")
|
|
|
|
T.finish("game3_knocked_off_flag_test")
|