mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-17 19:24:01 +02:00
CLOSES #1206, CLOSES #1189, CLOSES #1175, CLOSES #1129, CLOSES #1119, CLOSES #1089, CLOSES #1073, CLOSES #1069, CLOSES #1065, CLOSES #1010, CLOSES #990, CLOSES #989, CLOSES #988, CLOSES #978, CLOSES #944, CLOSES #936, CLOSES #1221, CLOSES #1220, CLOSES #1193, CLOSES #1101, CLOSES #1066, CLOSES #1056, CLOSES #1041, CLOSES #984, CLOSES #1225, CLOSES #1214, CLOSES #1011, CLOSES #1035, CLOSES #1219, CLOSES #1048, CLOSES #1047, CLOSES #964, CLOSES #949, CLOSES #1149, CLOSES #1007, CLOSES #914, CLOSES #1115, CLOSES #1146, CLOSES #999, CLOSES #1207, CLOSES #1029, CLOSES #1120, CLOSES #987, CLOSES #983
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
package.path = "./?.lua;./?/init.lua;" .. package.path
|
||||
|
||||
local T = require("tests.harness")
|
||||
local ItemEffects = require("src.core.gen2.ItemEffects")
|
||||
local Game2 = require("src.core.Game2")
|
||||
local Screens = require("src.ui.Screens")
|
||||
local EvolutionAnim = require("src.ui.gen2.EvolutionAnim")
|
||||
|
||||
local data = {
|
||||
pokemon = {
|
||||
SUNKERN = {
|
||||
evolutions = {
|
||||
{ method = "EVOLVE_ITEM", item = "SUN_STONE", into = "SUNFLORA" },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
local mon = { species = "SUNKERN", item = nil }
|
||||
|
||||
T.eq(ItemEffects.partyAction("SUN_STONE"), "stone",
|
||||
"SUN STONE opens the party target flow")
|
||||
local result = ItemEffects.useOnMon("SUN_STONE", mon, data)
|
||||
T.check(result.used, "SUN STONE succeeds on SUNKERN")
|
||||
T.eq(result.evolution and result.evolution.into, "SUNFLORA",
|
||||
"SUN STONE selects SUNFLORA")
|
||||
|
||||
mon.item = "EVERSTONE"
|
||||
result = ItemEffects.useOnMon("SUN_STONE", mon, data)
|
||||
T.check(not result.used, "held EVERSTONE refuses the evolution")
|
||||
|
||||
local function startStone()
|
||||
local partyOptions, evolutionOptions
|
||||
local game = setmetatable({
|
||||
data = {
|
||||
pokemon = data.pokemon,
|
||||
screens = {
|
||||
Gen2PartyMenu = function(_, options)
|
||||
partyOptions = options
|
||||
return {}
|
||||
end,
|
||||
Gen2EvolutionAnim = function(_, options)
|
||||
evolutionOptions = options
|
||||
return {}
|
||||
end,
|
||||
},
|
||||
},
|
||||
save = {
|
||||
party = { { species = "SUNKERN", item = nil } },
|
||||
inventory = { SUN_STONE = 1 },
|
||||
},
|
||||
stack = { pop = function() end, push = function() end },
|
||||
}, Game2)
|
||||
Screens.invalidate()
|
||||
game:usePartyItem("SUN_STONE")
|
||||
partyOptions.onChoose(nil, game.save.party[1])
|
||||
return game, evolutionOptions
|
||||
end
|
||||
|
||||
local game, evolution = startStone()
|
||||
T.check(evolution.force, "SUN STONE evolution sets wForceEvolution")
|
||||
T.eq(game.save.inventory.SUN_STONE, 1,
|
||||
"SUN STONE remains until evolution succeeds")
|
||||
|
||||
local animation = EvolutionAnim.new({ data = { pokemon = data.pokemon } }, {
|
||||
mon = game.save.party[1], entry = evolution and evolution.entry,
|
||||
force = evolution and evolution.force,
|
||||
})
|
||||
T.check(not animation:cancelPressed({ wasPressed = function(_, key)
|
||||
return key == "b"
|
||||
end }), "B cannot cancel a forced stone evolution")
|
||||
|
||||
evolution.onDone({ canceled = true })
|
||||
T.eq(game.save.inventory.SUN_STONE, 1,
|
||||
"a canceled evolution does not consume SUN STONE")
|
||||
|
||||
game, evolution = startStone()
|
||||
evolution.onDone({ evolved = { species = "SUNFLORA" } })
|
||||
T.eq(game.save.inventory.SUN_STONE, nil,
|
||||
"a completed evolution consumes SUN STONE")
|
||||
Screens.invalidate()
|
||||
|
||||
T.finish("gen2 sun stone bug 1219")
|
||||
@@ -65,7 +65,7 @@ T.check(pushed[1].text:find(BYE, 1, true) == nil,
|
||||
|
||||
pushed[1].onDone()
|
||||
T.eq(#pushed, 1, "the farewell waits for the bow")
|
||||
T.eq(nurse.facing, "up", "image index $14: the nurse bows")
|
||||
T.eq(nurse.frameOverride, 3, "image index $1: the nurse bows")
|
||||
T.check(fakeSelf.emote ~= nil, "the bow is a world hold, not a text pause")
|
||||
local hold = fakeSelf.emote or {}
|
||||
T.eq(hold.npc, nurse, "the hold is anchored on the nurse")
|
||||
@@ -79,11 +79,12 @@ if hold.onDone then hold.onDone() end
|
||||
T.eq(#pushed, 2, "the farewell follows the bow")
|
||||
local farewell = pushed[2] or {}
|
||||
T.eq(farewell.text, BYE, "second box is the farewell text")
|
||||
T.eq(nurse.facing, "up", "she is still bowed while the farewell prints")
|
||||
T.eq(nurse.frameOverride, nil, "the bow ends before the farewell prints")
|
||||
T.eq(nurse.facing, "down", "the nurse faces the player for the farewell")
|
||||
|
||||
if farewell.onDone then farewell.onDone() end
|
||||
T.eq(nurse.facing, "down", "the trailing UpdateSprites faces her back")
|
||||
T.eq(faced, 1, "she is turned back exactly once")
|
||||
T.eq(nurse.facing, "down", "the trailing UpdateSprites keeps her facing")
|
||||
T.eq(faced, 2, "she is turned back before and after the farewell")
|
||||
T.eq(finished, 1, "control returns to the player once, after the farewell")
|
||||
|
||||
-- === no nurse sprite (the Yellow/rest-stop callers): no bow, same text
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package.path = "./?.lua;./?/init.lua;" .. package.path
|
||||
|
||||
local T = require("tests.harness")
|
||||
local GenSave = require("src.save_convert.GenSave")
|
||||
local romText = require("src.core.RomText")
|
||||
|
||||
local localized = {
|
||||
text = {
|
||||
_MoveIsDisabledText = "The move {RAM:wNameBuffer} from {USER} is disabled!",
|
||||
},
|
||||
}
|
||||
T.eq(romText(localized, "_MoveIsDisabledText", "%s's %s is disabled!", {
|
||||
USER = "PIKACHU", ["RAM:wNameBuffer"] = "THUNDER",
|
||||
}), "The move THUNDER from PIKACHU is disabled!",
|
||||
"named ROM tokens survive translation reordering")
|
||||
|
||||
GenSave.setCharmap(loadfile("src/save_convert/data/charmap.lua")())
|
||||
local data = {
|
||||
pokemon = {}, moves = {}, items = {}, maps = {},
|
||||
hiddenItems = loadfile("src/save_convert/data/hidden_items.lua")(),
|
||||
}
|
||||
local save = {
|
||||
player = { name = "RED", id = 1, map = "PALLET_TOWN", x = 0, y = 0 },
|
||||
rival = { name = "BLUE" }, party = {}, boxes = {}, inventory = {},
|
||||
pcItems = {}, flags = {}, pokedex = { seen = {}, owned = {} },
|
||||
hiddenTaken = {
|
||||
VIRIDIAN_FOREST_1_18 = true,
|
||||
VIRIDIAN_CITY_14_4 = true,
|
||||
},
|
||||
}
|
||||
for i = 1, 12 do save.boxes[i] = {} end
|
||||
local bytes = GenSave.encode(save, data, nil)
|
||||
local decoded = GenSave.decode(bytes, data)
|
||||
T.check(decoded.hiddenTaken.VIRIDIAN_FOREST_1_18,
|
||||
"Viridian Forest hidden potion imports from wObtainedHiddenItemsFlags")
|
||||
T.check(decoded.hiddenTaken.VIRIDIAN_CITY_14_4,
|
||||
"Viridian City hidden potion imports from wObtainedHiddenItemsFlags")
|
||||
T.check(not decoded.hiddenTaken.ROUTE_9_14_7,
|
||||
"an uncollected hidden item remains available")
|
||||
|
||||
love = love or require("tests.love_stub")
|
||||
local start = require("src.ui.StartMenu").new({
|
||||
data = {}, save = {
|
||||
flags = {}, party = {}, inventory = {}, options = {},
|
||||
player = { name = "RED" }, pokedex = { owned = {} },
|
||||
},
|
||||
})
|
||||
local pokemonRow
|
||||
for _, row in ipairs(start.items) do
|
||||
if row.label == "POKéMON" then pokemonRow = row break end
|
||||
end
|
||||
T.check(pokemonRow and pokemonRow.keepOpen,
|
||||
"the empty-party POKéMON row leaves the start menu open")
|
||||
|
||||
T.finish("open menu bugs 949 and 1149")
|
||||
+3
-3
@@ -60,14 +60,14 @@ for _, r in ipairs(rows) do
|
||||
end
|
||||
check(not hasRecord, "CHAMPIONS_ROOM rival script no longer calls record_hall_of_fame")
|
||||
|
||||
-- The post-battle walk takes the right-hand detour before heading north, so
|
||||
-- The post-battle walk takes the left-hand detour before heading north, so
|
||||
-- the player does not visibly pass through the rival at (4,2).
|
||||
local route = {}
|
||||
for _, r in ipairs(rows) do
|
||||
if r[1] == "move_player" then route[#route + 1] = r end
|
||||
end
|
||||
eq(route[#route - 1] and route[#route - 1][2], "right",
|
||||
"walk-out route first moves right around the rival")
|
||||
eq(route[#route - 1] and route[#route - 1][2], "left",
|
||||
"walk-out route first moves left around the rival")
|
||||
eq(route[#route] and route[#route][2], "up",
|
||||
"walk-out route then heads north to Hall of Fame")
|
||||
|
||||
|
||||
@@ -203,13 +203,13 @@ do
|
||||
eq(tb.waitFrames, 60, "for the 60 frames AnimationBlinkEnemyMon takes")
|
||||
end
|
||||
|
||||
-- the OPTIONS animation toggle still gates the whole thing; the sound does not
|
||||
-- engine/battle/animations.asm PlayApplyingAttackAnimation
|
||||
do
|
||||
local tb = freshBattle()
|
||||
Game.save.options.animations = false
|
||||
tb:applyHitFx({ animType = 5, sfx = "Damage" })
|
||||
eq(tb.fx.shakeProg, nil, "animations off arms no shake")
|
||||
eq(tb.fx.blink, nil, "and no blink")
|
||||
check(tb.fx.shakeProg ~= nil, "animations off keeps the hit shake")
|
||||
eq(tb.fx.blink, nil, "type 5 remains a shake, not a blink")
|
||||
Game.save.options.animations = true
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
package.path = "./?.lua;./?/init.lua;" .. package.path
|
||||
if not _G.love then _G.love = require("tests.love_stub") end
|
||||
|
||||
local S = require("tests.harness").suite("parity Yellow disabled Pikachu")
|
||||
local check, eq = S.check, S.eq
|
||||
local Data = require("src.core.Data")
|
||||
Data:load()
|
||||
local GameVersion = require("src.core.GameVersion")
|
||||
local Follower = require("src.world.PikachuFollower")
|
||||
local ItemEffects = require("src.inventory.ItemEffects")
|
||||
local PartyMenu = require("src.ui.PartyMenu")
|
||||
local BoxMenu = require("src.ui.BoxMenu")
|
||||
local TextBox = require("src.render.TextBox")
|
||||
|
||||
local oldVersion = GameVersion.get()
|
||||
GameVersion.set("yellow")
|
||||
|
||||
local save = {
|
||||
player = { id = 7, name = "RED" },
|
||||
party = {
|
||||
{ species = "PIKACHU", otId = 7, ot = "RED", hp = 20 },
|
||||
{ species = "PIDGEY", otId = 7, ot = "RED", hp = 20 },
|
||||
},
|
||||
}
|
||||
|
||||
check(Follower.isStarterPikachu(save, save.party[1]), "the player Pikachu is identified")
|
||||
check(not Follower.isStarterPikachu(save, save.party[2]), "other party members are not starter Pikachu")
|
||||
|
||||
local pika = { pikachuFollower = true, cellX = 3, cellY = 4, facing = "down" }
|
||||
local seel = { def = { name = "POKEMONFANCLUB_SEEL" }, cellX = 1, cellY = 4 }
|
||||
local moves = {}
|
||||
local ow = {
|
||||
map = { id = "POKEMON_FAN_CLUB" },
|
||||
player = { cellX = 3, cellY = 5, facing = "up" },
|
||||
npcs = { pika, seel },
|
||||
scriptMove = function(_, npc, dir, tiles, done)
|
||||
moves[#moves + 1] = { dir, tiles }
|
||||
npc.facing = dir
|
||||
if done then done() end
|
||||
end,
|
||||
}
|
||||
|
||||
Follower.onFanClubEntered({ save = save, data = Data }, ow)
|
||||
check(ow.pikachuFanClubScene, "Fan Club disables normal Pikachu following")
|
||||
check(ow.pikachuMapScriptActive, "Fan Club sets the map-script flag")
|
||||
eq(ow.player.facing, "down", "Fan Club resets the player direction")
|
||||
eq(moves[1] and moves[1][1], "up", "Fan Club starts with slide-up displacement")
|
||||
eq(moves[1] and moves[1][2], 1, "Fan Club slide-up spans one tile")
|
||||
eq(moves[2] and moves[2][1], "right", "Fan Club then walks right")
|
||||
eq(moves[2] and moves[2][2], 3, "Fan Club walks right three tiles")
|
||||
eq(moves[3] and moves[3][1], "up", "Fan Club ends walking up")
|
||||
eq(moves[3] and moves[3][2], 1, "Fan Club final up spans one tile")
|
||||
eq(seel.movementStatus, 2, "Fan Club puts Seel into movement delay")
|
||||
eq(seel.facing, "down", "Fan Club turns Seel down")
|
||||
check(Follower.isFollowingDisabled(ow), "disabled Fan Club follower blocks starter selection")
|
||||
|
||||
local sleepOw = {
|
||||
map = { id = "PEWTER_POKECENTER" },
|
||||
player = { cellX = 3, cellY = 5 },
|
||||
npcs = { pika },
|
||||
pikachuPewterSleepScene = true,
|
||||
}
|
||||
local result = ItemEffects.use(Data, save, "POKE_FLUTE", nil, nil, nil, sleepOw)
|
||||
eq(result, "flute_wake_pikachu", "Poké Flute is allowed next to sleeping Pikachu")
|
||||
check(Follower.isFollowingDisabled(sleepOw), "sleeping Pikachu disables normal follower actions")
|
||||
|
||||
local pushed = {}
|
||||
local partyGame = {
|
||||
save = save,
|
||||
overworld = sleepOw,
|
||||
stack = { push = function(_, state) pushed[#pushed + 1] = state end },
|
||||
input = { wasPressed = function(_, key) return key == "a" end },
|
||||
}
|
||||
PartyMenu.new(partyGame):update()
|
||||
check(getmetatable(pushed[#pushed]) == TextBox,
|
||||
"sleeping Pikachu cannot be selected from the party menu")
|
||||
|
||||
local boxGame = {
|
||||
save = save,
|
||||
overworld = sleepOw,
|
||||
data = { pokemon = { PIKACHU = { name = "PIKACHU" }, PIDGEY = { name = "PIDGEY" } }, text = {} },
|
||||
stack = { push = function(_, state) pushed[#pushed + 1] = state end },
|
||||
}
|
||||
local pc = BoxMenu.new(boxGame)
|
||||
pc.items[2].onSelect()
|
||||
local depositList = pushed[#pushed]
|
||||
depositList.onChoose(depositList.items[1], depositList)
|
||||
check(getmetatable(pushed[#pushed]) == TextBox,
|
||||
"sleeping Pikachu cannot be deposited into Bill's PC")
|
||||
eq(#save.party, 2, "PC refusal leaves the party unchanged")
|
||||
|
||||
GameVersion.set(oldVersion)
|
||||
S.finish()
|
||||
+6
-7
@@ -891,8 +891,7 @@ do
|
||||
check(tb:lockedAction(tb.enemy) == nil, "victim is free after the release")
|
||||
end
|
||||
|
||||
-- #2: recoil and drain use the RAW computed damage, not the HP-capped
|
||||
-- amount dealt
|
||||
-- engine/battle/core.asm ApplyDamageToEnemyPokemon
|
||||
do
|
||||
Game.save.party = { Pokemon.new(Data, "BULBASAUR", 20) }
|
||||
local rb = BattleState.newWild(Game, "RATTATA", 3)
|
||||
@@ -903,8 +902,8 @@ do
|
||||
rb.rng = mkseq({ 0, 255, 255 })
|
||||
local hpBefore = rb.player.mon.hp
|
||||
rb:performMove(rb.player, rb.enemy, { id = "TAKE_DOWN", pp = 10 })
|
||||
eq(hpBefore - rb.player.mon.hp, math.floor(raw / 4),
|
||||
"recoil is raw damage / 4 even when only 1 HP was dealt")
|
||||
eq(hpBefore - rb.player.mon.hp, 1,
|
||||
"recoil is capped damage / 4 with a minimum of 1")
|
||||
|
||||
local db = BattleState.newWild(Game, "RATTATA", 3)
|
||||
db.enemy.mon.hp = 1
|
||||
@@ -914,9 +913,9 @@ do
|
||||
check(rawD >= 4, "raw MEGA DRAIN damage is meaningful (" .. rawD .. ")")
|
||||
db.rng = mkseq({ 0, 255, 255 })
|
||||
db:performMove(db.player, db.enemy, { id = "MEGA_DRAIN", pp = 10 })
|
||||
eq(db.player.mon.hp - 1, math.floor(rawD / 2),
|
||||
"drain heals raw damage / 2 even when only 1 HP was dealt")
|
||||
eq(db.lastDamage, math.floor(rawD / 2),
|
||||
eq(db.player.mon.hp - 1, 1,
|
||||
"drain heals capped damage / 2 with a minimum of 1")
|
||||
eq(db.lastDamage, 1,
|
||||
"drain halves wDamage in place (Counter would see the half)")
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user