mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-10-04 08:21:40 -04:00
@@ -1169,6 +1169,7 @@ local SOFT_RESET = {
|
||||
{ "src.ui.game3.daycare_menu", closeFlag("open") },
|
||||
{ "src.ui.game3.fame_checker", closeFlag("open") },
|
||||
{ "src.ui.game3.move_relearner", closeFlag("open") },
|
||||
{ "src.ui.game3.rse.move_relearner", closeFlag("open") },
|
||||
{ "src.ui.game3.egg_hatch", closeFlag("open") },
|
||||
{ "src.ui.game3.evolution_scene", closeFlag("open") },
|
||||
{ "src.ui.game3.diploma", closeFlag("open") },
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
local MoveSwap = {}
|
||||
|
||||
local function filled(mv)
|
||||
return mv ~= nil and mv ~= 0 and mv ~= ""
|
||||
end
|
||||
|
||||
function MoveSwap.moveCount(mon)
|
||||
local n = 0
|
||||
for i = 1, 4 do
|
||||
if not filled(mon and mon.moves and mon.moves[i]) then break end
|
||||
n = n + 1
|
||||
end
|
||||
return n
|
||||
end
|
||||
|
||||
-- pokeemerald/src/battle_controller_player.c:601
|
||||
function MoveSwap.canStart(st, battler)
|
||||
if not st or st.link or not battler or not battler.mon then return false end
|
||||
return MoveSwap.moveCount(battler.mon) > 1
|
||||
end
|
||||
|
||||
-- pokeemerald/src/battle_controller_player.c:604
|
||||
function MoveSwap.initialCursor(current)
|
||||
if current ~= 0 then return 0 end
|
||||
return current + 1
|
||||
end
|
||||
|
||||
-- pokeemerald/src/battle_controller_player.c:795
|
||||
function MoveSwap.step(cursor, dir, n)
|
||||
if dir == "left" then
|
||||
if cursor % 2 == 1 then return cursor - 1 end
|
||||
elseif dir == "right" then
|
||||
if cursor % 2 == 0 and cursor + 1 < n then return cursor + 1 end
|
||||
elseif dir == "up" then
|
||||
if cursor >= 2 then return cursor - 2 end
|
||||
elseif dir == "down" then
|
||||
if cursor < 2 and cursor + 2 < n then return cursor + 2 end
|
||||
end
|
||||
return cursor
|
||||
end
|
||||
|
||||
local function swap_field(t, a, b)
|
||||
if type(t) == "table" then t[a], t[b] = t[b], t[a] end
|
||||
end
|
||||
|
||||
-- pokeemerald/src/battle_controller_player.c:676
|
||||
function MoveSwap.apply(battler, slotA, slotB)
|
||||
if not battler or not battler.mon or slotA == slotB then return false end
|
||||
local Pokemon = require("src.core.game3.pokemon")
|
||||
local State = require("src.core.game3.battle.state")
|
||||
local party = State.partyMon(battler)
|
||||
if battler._partyMon then
|
||||
local proxy = battler.mon
|
||||
swap_field(rawget(proxy, "moves"), slotA, slotB)
|
||||
swap_field(rawget(proxy, "pp"), slotA, slotB)
|
||||
swap_field(battler.permanentSlots, slotA, slotB)
|
||||
swap_field(battler.sketched, slotA, slotB)
|
||||
if not battler.transformed and party then
|
||||
Pokemon.swapMoves(party, slotA, slotB)
|
||||
end
|
||||
else
|
||||
Pokemon.swapMoves(party or battler.mon, slotA, slotB)
|
||||
end
|
||||
for _, key in ipairs({ "expLockedSlot", "expEncoreSlot" }) do
|
||||
if battler[key] == slotA then
|
||||
battler[key] = slotB
|
||||
elseif battler[key] == slotB then
|
||||
battler[key] = slotA
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
return MoveSwap
|
||||
@@ -224,6 +224,7 @@ function Ui.reset(opts)
|
||||
Ui._actionCursor = {}
|
||||
Ui._moveCursor = {}
|
||||
Ui._moveCursorMon = {}
|
||||
Ui._swap = nil
|
||||
Ui._target = nil
|
||||
Ui._bounce = { hb = {}, mon = {} }
|
||||
Ui._preview = nil
|
||||
@@ -961,8 +962,46 @@ local function move_count(mon)
|
||||
return n
|
||||
end
|
||||
|
||||
-- pokeemerald/src/battle_controller_player.c:599
|
||||
local function move_swap_input(input, st, battler, id)
|
||||
local MoveSwap = require("src.core.game3.battle.move_swap")
|
||||
local sw = Ui._swap
|
||||
local cur = (Ui._moveIndex or 1) - 1
|
||||
if not sw then
|
||||
if input:wasPressed("select") and MoveSwap.canStart(st, battler) then
|
||||
Ui._swap = { cursor = MoveSwap.initialCursor(cur) }
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
if input:wasPressed("a") or input:wasPressed("select") then
|
||||
play_select()
|
||||
if sw.cursor ~= cur then MoveSwap.apply(battler, cur + 1, sw.cursor + 1) end
|
||||
Ui._moveIndex = sw.cursor + 1
|
||||
if id then Ui._moveCursor[id] = Ui._moveIndex end
|
||||
Ui._swap = nil
|
||||
elseif input:wasPressed("b") then
|
||||
play_select()
|
||||
Ui._swap = nil
|
||||
else
|
||||
local n = MoveSwap.moveCount(battler and battler.mon)
|
||||
for _, dir in ipairs({ "left", "right", "up", "down" }) do
|
||||
if input:wasPressed(dir) then
|
||||
local nc = MoveSwap.step(sw.cursor, dir, n)
|
||||
if nc ~= sw.cursor then
|
||||
sw.cursor = nc
|
||||
play_select()
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- pokefirered/src/battle_main.c:2380
|
||||
local function open_move_menu()
|
||||
Ui._swap = nil
|
||||
if is_double() then
|
||||
local id = Ui._active or 0
|
||||
local battler = active_battler()
|
||||
@@ -1519,6 +1558,7 @@ local function handle_double_input(input)
|
||||
return true
|
||||
elseif Ui._mode == "moves" then
|
||||
local b = active_battler(st)
|
||||
if move_swap_input(input, st, b, id) then return true end
|
||||
local n = move_count(b and b.mon)
|
||||
local c = (Ui._moveIndex or 1) - 1
|
||||
local nc = c
|
||||
@@ -1656,6 +1696,7 @@ function Ui.handleInput(input)
|
||||
-- Input owned by BagMenu / PartyMenu via Battle.update
|
||||
return true
|
||||
elseif Ui._mode == "moves" then
|
||||
if move_swap_input(input, Ui._st, Ui._st and Ui._st.player, nil) then return true end
|
||||
-- pokefirered/src/battle_controller_player.c:526
|
||||
local idx, moved = grid_nav(Ui._moveIndex, input, move_count(Ui._st and Ui._st.player and Ui._st.player.mon))
|
||||
if moved then
|
||||
@@ -2181,20 +2222,45 @@ local function draw_action_menu_rse(st)
|
||||
end
|
||||
end
|
||||
|
||||
local function swap_cursor_colors(variant, base)
|
||||
local clear = { 0, 0, 0, 0 }
|
||||
if variant == 27 then return { fg = base.fg, shadow = clear, bg = base.bg } end
|
||||
return { fg = base.shadow, shadow = clear, bg = base.bg }
|
||||
end
|
||||
|
||||
-- pokeemerald/src/battle_controller_player.c:608
|
||||
local function draw_move_cursors(pos_of, base)
|
||||
local cur = Ui._moveIndex - 1
|
||||
local sw = Ui._swap
|
||||
local p = pos_of(cur)
|
||||
if not sw or sw.cursor == cur then
|
||||
Window.cursorPx(p[1], p[2], { colors = base })
|
||||
return
|
||||
end
|
||||
Window.cursorPx(p[1], p[2], { colors = swap_cursor_colors(29, base) })
|
||||
local q = pos_of(sw.cursor)
|
||||
Window.cursorPx(q[1], q[2], { colors = swap_cursor_colors(27, base) })
|
||||
end
|
||||
|
||||
-- pokeemerald/src/battle_controller_player.c:1456
|
||||
local function draw_move_menu_rse(st)
|
||||
local W = BattleChrome.WIN
|
||||
local ab = st and (is_double(st) and active_battler(st) or st.player)
|
||||
local mon = ab and ab.mon
|
||||
local c = Ui._moveIndex - 1
|
||||
local _, cy = BattleChrome.textOrigin(W.MOVE_NAME_1)
|
||||
Window.cursorPx(8 * (9 * (c % 2) + 1), cy + 16 * math.floor(c / 2), { colors = rse_default_colors() })
|
||||
draw_move_cursors(function(c)
|
||||
return { 8 * (9 * (c % 2) + 1), cy + 16 * math.floor(c / 2) }
|
||||
end, rse_default_colors())
|
||||
for i = 1, 4 do
|
||||
local mv = mon and mon.moves and mon.moves[i]
|
||||
local label = "-"
|
||||
if mv and mv ~= 0 and mv ~= "" then label = Moves.displayName(mv) end
|
||||
rse_text(W.MOVE_NAME_1 + i - 1, label)
|
||||
end
|
||||
if Ui._swap then
|
||||
rse_text(W.SWITCH_PROMPT, RomText.plain("gText_BattleSwitchWhich"))
|
||||
return
|
||||
end
|
||||
local slot = Ui._moveIndex
|
||||
local mv = mon and mon.moves and mon.moves[slot]
|
||||
if mv and mv ~= 0 and mv ~= "" then
|
||||
@@ -2251,9 +2317,7 @@ local function draw_move_menu(st)
|
||||
{ 8, 122 }, { 80, 122 },
|
||||
{ 8, 138 }, { 80, 138 },
|
||||
}
|
||||
local c = Ui._moveIndex - 1
|
||||
local cp = cursorPos[c + 1] or cursorPos[1]
|
||||
Window.cursorPx(cp[1], cp[2], { colors = FrlgFont.COLOR.NORMAL })
|
||||
draw_move_cursors(function(c) return cursorPos[c + 1] or cursorPos[1] end, FrlgFont.COLOR.NORMAL)
|
||||
for i = 1, 4 do
|
||||
local mv = mon and mon.moves and mon.moves[i]
|
||||
-- pokefirered/src/data/text/move_names.h:2
|
||||
@@ -2263,6 +2327,11 @@ local function draw_move_menu(st)
|
||||
end
|
||||
draw_menu_text(label, positions[i][1], positions[i][2], { small = true, colors = FrlgFont.COLOR.NORMAL })
|
||||
end
|
||||
if Ui._swap then
|
||||
draw_menu_text(RomText.plain("gText_BattleSwitchWhich"), 168, 122,
|
||||
{ small = false, colors = FrlgFont.COLOR.NORMAL })
|
||||
return
|
||||
end
|
||||
local slot = Ui._moveIndex
|
||||
local mv = mon and mon.moves and mon.moves[slot]
|
||||
if mv and mv ~= 0 and mv ~= "" then
|
||||
|
||||
@@ -218,6 +218,7 @@ local function writeback(session, battleParty, remap, result, save, opts)
|
||||
exp = src.exp,
|
||||
pp = src.pp,
|
||||
maxPp = src.maxPp,
|
||||
ppBonusesPacked = src.ppBonusesPacked,
|
||||
moves = src.moves,
|
||||
species = src.species or src.speciesId,
|
||||
speciesId = src.speciesId or src.species,
|
||||
|
||||
@@ -304,6 +304,13 @@ function Evolution.apply(mon, newSpecies, session, bag, via)
|
||||
-- 2. Nickname update
|
||||
Evolution.renameMon(mon, preSpecies, newSpecies)
|
||||
|
||||
-- pokeemerald/src/pokemon.c:4556 GetMonAbility
|
||||
local pair = Pokemon.abilities(newSpecies)
|
||||
local slot = tonumber(mon.abilityNum)
|
||||
local ability = slot and pair[slot + 1]
|
||||
if not ability or ability == 0 then ability = Pokemon.abilityId(newSpecies, mon.personality) end
|
||||
mon.ability, mon.abilityId = ability, ability
|
||||
|
||||
-- 3. Recalculate stats & handle HP delta
|
||||
Pokemon.applyStats(mon)
|
||||
local newMax = tonumber(mon.maxHp) or oldMax
|
||||
|
||||
@@ -512,6 +512,11 @@ end
|
||||
|
||||
-- pokefirered/data/scripts/itemfinder.inc:1
|
||||
function Field.digUpUnderfootItem(game, hidden)
|
||||
if require("src.core.game3.profile").family(Field._session) == "rse" then
|
||||
-- pokeemerald/src/item_use.c:597
|
||||
require("src.ui.game3.message").close()
|
||||
return false
|
||||
end
|
||||
if not hidden then return false end
|
||||
local Flags = require("src.core.game3.scripting.flags")
|
||||
local flag = hidden_flag(hidden)
|
||||
@@ -552,14 +557,14 @@ function Field.useItemfinder(session, showOWMessage)
|
||||
})
|
||||
|
||||
if not result then
|
||||
local text = RomText.box("gText_NopeTheresNoResponse")
|
||||
local text = RomText.box(Itemfinder.textKey("nothing", session))
|
||||
if showOWMessage then
|
||||
-- pokefirered/src/itemfinder.c:150
|
||||
Message.show(text, { session = session, done = function() Message.close() end })
|
||||
end
|
||||
return false, "itemfinder", text, nil
|
||||
end
|
||||
local key = result.underfoot and "gText_ItemfinderShakingWildly" or "gText_ItemfinderResponding"
|
||||
local key = Itemfinder.textKey(result.underfoot and "onTop" or "nearby", session)
|
||||
local text = RomText.box(key)
|
||||
if showOWMessage then
|
||||
Field.lock()
|
||||
@@ -1849,8 +1854,7 @@ function Field.clearTempFieldEventData(game, mapId)
|
||||
local Flags = require("src.core.game3.scripting.flags")
|
||||
local Space = package.loaded["src.core.game3.scripting.space"]
|
||||
local session = Field._session
|
||||
local ids = {}
|
||||
for i = 1, #FieldMoves.TEMP_SYS_FLAGS do ids[i] = FieldMoves.TEMP_SYS_FLAGS[i] end
|
||||
local ids = FieldMoves.tempSysFlags()
|
||||
local data = game and game.data and game.data.maps
|
||||
local def = mapId and data and data[mapId]
|
||||
-- pokefirered/src/overworld.c:803
|
||||
|
||||
@@ -71,6 +71,8 @@ local FRLG_SYS_FLAGS = {
|
||||
|
||||
-- pokeemerald/include/constants/flags.h:1398
|
||||
local RSE_SYS_FLAGS = {
|
||||
WHITE_FLUTE_ACTIVE = "FLAG_SYS_ENC_UP_ITEM",
|
||||
BLACK_FLUTE_ACTIVE = "FLAG_SYS_ENC_DOWN_ITEM",
|
||||
USE_STRENGTH = "FLAG_SYS_USE_STRENGTH",
|
||||
FLASH_ACTIVE = "FLAG_SYS_USE_FLASH",
|
||||
}
|
||||
@@ -85,9 +87,23 @@ FieldMoves.SYS_FLAGS = setmetatable({}, {
|
||||
})
|
||||
|
||||
-- src/event_data.c:49
|
||||
FieldMoves.TEMP_SYS_FLAGS = {
|
||||
0x803, 0x804, 0x805,
|
||||
}
|
||||
local FRLG_TEMP_SYS_FLAGS = { "WHITE_FLUTE_ACTIVE", "BLACK_FLUTE_ACTIVE", "USE_STRENGTH" }
|
||||
|
||||
-- pokeemerald/src/event_data.c:39
|
||||
local RSE_TEMP_SYS_FLAGS = { "WHITE_FLUTE_ACTIVE", "BLACK_FLUTE_ACTIVE", "USE_STRENGTH", "FLAG_SYS_CTRL_OBJ_DELETE",
|
||||
"FLAG_NURSE_UNION_ROOM_REMINDER" }
|
||||
|
||||
function FieldMoves.tempSysFlags()
|
||||
local P = activeProfile()
|
||||
local rse = P.family == "rse"
|
||||
local IDS = rse and Flags.forVersion(P.id).IDS or nil
|
||||
local out = {}
|
||||
for _, key in ipairs(rse and RSE_TEMP_SYS_FLAGS or FRLG_TEMP_SYS_FLAGS) do
|
||||
local id = FieldMoves.SYS_FLAGS[key] or (IDS and IDS[key])
|
||||
if id then out[#out + 1] = id end
|
||||
end
|
||||
return out
|
||||
end
|
||||
|
||||
-- Graphics IDs for interactable field objects
|
||||
local FRLG_GFX_IDS = {
|
||||
|
||||
@@ -650,6 +650,12 @@ local VITAMIN_STAT_TEXT = {
|
||||
spe = "gText_ItemEffect_Speed", spa = "gText_ItemEffect_SpAtk", spd = "gText_ItemEffect_SpDef",
|
||||
}
|
||||
|
||||
-- pokeemerald/src/party_menu.c:4338
|
||||
local VITAMIN_STAT_TEXT_RSE = {
|
||||
hp = "gText_HP3", atk = "gText_Attack3", def = "gText_Defense3",
|
||||
spe = "gText_Speed2", spa = "gText_SpAtk3", spd = "gText_SpDef3",
|
||||
}
|
||||
|
||||
function ItemUse.useVitamin(session, mon, itemId)
|
||||
if not mon then return false, "none", no_pokemon_text() end
|
||||
local num = ItemsData.toNumericId(itemId) or tonumber(itemId)
|
||||
@@ -667,7 +673,8 @@ function ItemUse.useVitamin(session, mon, itemId)
|
||||
end
|
||||
Pokemon.itemFriendship(mon, Pokemon.VITAMIN_FRIENDSHIP_CHANGE,
|
||||
{ mapSec = Pokemon.currentMapSec(session) })
|
||||
local t = mon_text("gText_PkmnBaseVar2StatIncreased", mon, RomText.plain(VITAMIN_STAT_TEXT[key]))
|
||||
local statTextKey = require("src.core.game3.profile").family(session) == "rse" and VITAMIN_STAT_TEXT_RSE or VITAMIN_STAT_TEXT
|
||||
local t = mon_text("gText_PkmnBaseVar2StatIncreased", mon, RomText.plain(statTextKey[key]))
|
||||
return true, "vitamin", t
|
||||
end
|
||||
|
||||
@@ -874,9 +881,6 @@ local ITEM_TEACHY_TV = 366
|
||||
-- pokefirered/include/constants/items.h:435 ITEM_FAME_CHECKER
|
||||
local ITEM_FAME_CHECKER = 363
|
||||
local ITEM_AWAKENING = 17
|
||||
-- pokefirered/include/constants/flags.h:1330
|
||||
local FLAG_SYS_WHITE_FLUTE_ACTIVE = 0x803
|
||||
local FLAG_SYS_BLACK_FLUTE_ACTIVE = 0x804
|
||||
-- pokefirered/include/constants/songs.h:346 MUS_POKE_FLUTE
|
||||
local MUS_POKE_FLUTE = 338
|
||||
|
||||
@@ -1010,19 +1014,20 @@ end
|
||||
|
||||
-- pokefirered/src/item_use.c:582 FieldUseFunc_BlackWhiteFlute
|
||||
function ItemUse.useBlackWhiteFlute(session, num)
|
||||
local FieldMoves = require("src.core.game3.field_moves")
|
||||
local ctx = {
|
||||
playerName = tostring((session and (session.name or session.playerName)) or ""),
|
||||
stringVars = { [2] = ItemsData.displayName(num) },
|
||||
}
|
||||
local text
|
||||
if num == ITEM_WHITE_FLUTE then
|
||||
sys_flag(session, FLAG_SYS_WHITE_FLUTE_ACTIVE, true)
|
||||
sys_flag(session, FLAG_SYS_BLACK_FLUTE_ACTIVE, false)
|
||||
sys_flag(session, FieldMoves.SYS_FLAGS.WHITE_FLUTE_ACTIVE, true)
|
||||
sys_flag(session, FieldMoves.SYS_FLAGS.BLACK_FLUTE_ACTIVE, false)
|
||||
-- pokefirered/src/item_use.c:590
|
||||
text = RomText.box("gText_UsedVar2WildLured", ctx)
|
||||
else
|
||||
sys_flag(session, FLAG_SYS_BLACK_FLUTE_ACTIVE, true)
|
||||
sys_flag(session, FLAG_SYS_WHITE_FLUTE_ACTIVE, false)
|
||||
sys_flag(session, FieldMoves.SYS_FLAGS.BLACK_FLUTE_ACTIVE, true)
|
||||
sys_flag(session, FieldMoves.SYS_FLAGS.WHITE_FLUTE_ACTIVE, false)
|
||||
-- pokefirered/src/item_use.c:599
|
||||
text = RomText.box("gText_UsedVar2WildRepelled", ctx)
|
||||
end
|
||||
@@ -1044,6 +1049,14 @@ local function useField(session, bag, id, partySlot, moveSlot)
|
||||
if not info then return false, "unknown", Strings("Unknown item.") end
|
||||
local use = ItemsData.fieldUseKind(id)
|
||||
|
||||
if require("src.core.game3.profile").family(session) == "rse" then
|
||||
local n = ItemsData.toNumericId(id) or tonumber(id)
|
||||
if n == ITEM_POKE_FLUTE or n == ItemsData.ITEM_TM_CASE or n == ItemsData.ITEM_BERRY_POUCH then
|
||||
-- pokeemerald/src/item_use.c:150 ItemUseOutOfBattle_CannotUse
|
||||
return false, "none", not_the_time(session)
|
||||
end
|
||||
end
|
||||
|
||||
if info.fieldUseName == "ItemUseOutOfBattle_PokeblockCase" then
|
||||
-- pokeemerald/src/item_use.c:609
|
||||
require("src.core.game3.rse.pokeblock").openCase(session, {})
|
||||
|
||||
@@ -147,6 +147,17 @@ function Itemfinder.start(task)
|
||||
Itemfinder._task = task
|
||||
end
|
||||
|
||||
local TEXT_KEYS = {
|
||||
frlg = { nothing = "gText_NopeTheresNoResponse", nearby = "gText_ItemfinderResponding", onTop = "gText_ItemfinderShakingWildly" },
|
||||
rse = { nothing = "gText_ItemFinderNothing", nearby = "gText_ItemFinderNearby", onTop = "gText_ItemFinderOnTop" },
|
||||
}
|
||||
|
||||
-- pokeemerald/src/item_use.c:295
|
||||
function Itemfinder.textKey(kind, session)
|
||||
local family = require("src.core.game3.profile").family(session)
|
||||
return (TEXT_KEYS[family] or TEXT_KEYS.frlg)[kind]
|
||||
end
|
||||
|
||||
function Itemfinder.isActive()
|
||||
return Itemfinder._task ~= nil
|
||||
end
|
||||
@@ -167,7 +178,7 @@ function Itemfinder.runTask()
|
||||
if t.timer % 25 == 0 then
|
||||
if t.remaining == 0 then
|
||||
t.phase = "message"
|
||||
local key = t.result.underfoot and "gText_ItemfinderShakingWildly" or "gText_ItemfinderResponding"
|
||||
local key = Itemfinder.textKey(t.result.underfoot and "onTop" or "nearby")
|
||||
t.onMessage(key, function()
|
||||
Itemfinder._task = nil
|
||||
Itemfinder._sprites = {}
|
||||
|
||||
@@ -1442,9 +1442,17 @@ function LB.finish(result)
|
||||
bumpTrainerCard(s, recorded)
|
||||
LB.addOpponentRecord(s, LB.peer and LB.peer.name, LB.peer and LB.peer.trainerId, recorded)
|
||||
-- pokefirered/src/cable_club.c:782
|
||||
local okF, TFC = pcall(require, "src.core.game3.trainer_fan_club")
|
||||
if okF and TFC and TFC.updateTrainerFansAfterLinkBattle then
|
||||
TFC.updateTrainerFansAfterLinkBattle(s, ctx, recorded)
|
||||
if require("src.core.game3.profile").family(s) == "rse" then
|
||||
-- pokeemerald/src/field_specials.c:4244
|
||||
local okR, FieldRse = pcall(require, "src.core.game3.scripting.natives_field_rse")
|
||||
if okR and FieldRse.updateTrainerFansAfterLinkBattle then
|
||||
FieldRse.updateTrainerFansAfterLinkBattle(recorded == LB.B_OUTCOME.WON)
|
||||
end
|
||||
else
|
||||
local okF, TFC = pcall(require, "src.core.game3.trainer_fan_club")
|
||||
if okF and TFC and TFC.updateTrainerFansAfterLinkBattle then
|
||||
TFC.updateTrainerFansAfterLinkBattle(s, ctx, recorded)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -49,7 +49,7 @@ function Party.applyBattleFields(opaqueMon, fields)
|
||||
"happiness", "friendship", "evs", "pokerus", "item", "heldItem",
|
||||
"species", "speciesId", "name", "growthRate",
|
||||
"attack", "defense", "speed", "spAtk", "spDef",
|
||||
"atk", "def", "spe", "spa", "spd",
|
||||
"atk", "def", "spe", "spa", "spd", "ppBonusesPacked",
|
||||
}) do
|
||||
if fields[key] ~= nil then opaqueMon[key] = fields[key] end
|
||||
end
|
||||
|
||||
@@ -1311,6 +1311,12 @@ function Pokemon.swapMoves(mon, slotA, slotB)
|
||||
mon.pp[slotB] = ppA
|
||||
end
|
||||
|
||||
if type(mon.maxPp) == "table" then
|
||||
local maxA = mon.maxPp[slotA]
|
||||
mon.maxPp[slotA] = mon.maxPp[slotB]
|
||||
mon.maxPp[slotB] = maxA
|
||||
end
|
||||
|
||||
-- 4. If parallel array mon.ppBonuses / mon.ppBonus / mon.ppUp exists
|
||||
if type(mon.ppBonuses) == "table" then
|
||||
local bA = mon.ppBonuses[slotA]
|
||||
|
||||
@@ -82,6 +82,9 @@ return {
|
||||
gText_MonLearnedMove = "gText_MoveRelearnerPkmnLearnedMove",
|
||||
gText_1_2_and_Poof = "gText_MoveRelearnerAndPoof",
|
||||
gText_StopLearningMove = "gText_MoveRelearnerStopTryingToTeachMove",
|
||||
gText_MonIsTryingToLearnMove = "gText_MoveRelearnerPkmnTryingToLearnMove",
|
||||
gText_WhichMoveShouldBeForgotten = "gText_MoveRelearnerWhichMoveToForget",
|
||||
gText_MonForgotOldMoveAndMonLearnedNewMove = "gText_MoveRelearnerPkmnForgotMoveAndLearnedNew",
|
||||
},
|
||||
-- pokeemerald/src/berry_powder.c:223
|
||||
berryPowderBox = { left = 1, top = 1, width = 7, height = 4, title = "gText_Powder", titleX = 0, titleY = 1,
|
||||
|
||||
@@ -3,7 +3,8 @@ local Q=require('src.core.game3.quest_log')
|
||||
local R={}
|
||||
function R.location(game,session)
|
||||
local def=game and game.data and game.data.maps and game.data.maps[session.map]
|
||||
local info=require("src.import.gba.map_sections_extract").getInfo(def and def.regionMapSectionId,session.map,0)
|
||||
local ok,info=pcall(require("src.import.gba.map_sections_extract").getInfo,def and def.regionMapSectionId,session.map,0)
|
||||
if not ok then info=nil end
|
||||
if info and info.name and info.name~="???" then return info.name end
|
||||
return tostring(session.map or ''):gsub('^FR_',''):gsub('_',' ')
|
||||
end
|
||||
@@ -46,6 +47,7 @@ function R.tiles(game,session,frame)
|
||||
return R.fillTiles(game,session,frame,{})
|
||||
end
|
||||
function R.event(session,key,args)
|
||||
if not require('src.core.game3.field_modules').enabled('questLog',session) then return end
|
||||
local Runtime=package.loaded['src.core.game3.runtime']
|
||||
-- Ignore simulations/tests and sessions that aren't the active game.
|
||||
if not session or not Runtime or type(Runtime.isActive) ~= "function" or not Runtime.isActive() or (Runtime.getSession and Runtime.getSession() ~= session) then return end
|
||||
|
||||
@@ -284,6 +284,7 @@ end
|
||||
-- Do not force-hide lab Oak (43) — clearflag during the lead warp must stick.
|
||||
function Flags.ensurePalletOakHidden(store)
|
||||
if not store then return end
|
||||
if require("src.core.game3.profile").family() ~= "frlg" then return end
|
||||
local sceneVar = Flags.VAR_IDS.MAP_SCENE_PALLET_TOWN_OAK or 0x4050
|
||||
local scene = Flags.getVar(store, nil, sceneVar)
|
||||
if scene ~= 0 then return end
|
||||
|
||||
@@ -1278,6 +1278,20 @@ function FieldRse.tryGainNewFanFromCounter(incrementId)
|
||||
return bit.band(fanBits(), FANCLUB_COUNTER)
|
||||
end
|
||||
|
||||
-- pokeemerald/src/field_specials.c:4244
|
||||
function FieldRse.updateTrainerFansAfterLinkBattle(won)
|
||||
if (tonumber(Rse.var("VAR_LILYCOVE_FAN_CLUB_STATE")) or 0) ~= 2 then return end
|
||||
if fanFlag(FANCLUB_GOT_FIRST_FANS) then
|
||||
FieldRse.tryLoseFansFromPlayTime()
|
||||
Rse.setVar("VAR_FANCLUB_LOSE_FAN_TIMER", playTimeHours())
|
||||
end
|
||||
if won then
|
||||
FieldRse.playerGainRandomTrainerFan()
|
||||
else
|
||||
FieldRse.playerLoseRandomTrainerFan()
|
||||
end
|
||||
end
|
||||
|
||||
-- pokeemerald/src/field_specials.c:4170
|
||||
local FAN_NAME_SOURCES = {
|
||||
[9] = { 0, 3 }, [10] = { 0, 1 }, [11] = { 1, 0 }, [12] = { 0, 4 }, [13] = { 1, 5 },
|
||||
|
||||
@@ -140,7 +140,12 @@ MoveTeach.BY_NAME = {
|
||||
-- pokefirered/src/learn_move.c:367
|
||||
TeachMoveRelearnerMove = function(ctx, adapters)
|
||||
local mon, session = chosenMon(ctx)
|
||||
local okUi, MoveRelearner = pcall(require, "src.ui.game3.move_relearner")
|
||||
local screen = "src.ui.game3.move_relearner"
|
||||
if require("src.core.game3.profile").family(session) == "rse" then
|
||||
-- pokeemerald/src/move_relearner.c:373
|
||||
screen = "src.ui.game3.rse.move_relearner"
|
||||
end
|
||||
local okUi, MoveRelearner = pcall(require, screen)
|
||||
if not (mon and okUi and type(MoveRelearner) == "table" and MoveRelearner.show) then
|
||||
varSet(ctx, VAR_0x8004, 0)
|
||||
return false
|
||||
|
||||
@@ -3,7 +3,7 @@ local VersionsRse = require("src.import.gba.versions_rse")
|
||||
local V = VersionsRse.new("emerald")
|
||||
local sym, count = V.sym, V.count
|
||||
|
||||
V.CACHE_VERSION = 9
|
||||
V.CACHE_VERSION = 11
|
||||
V.NATIVE_VERSION = 2
|
||||
V.OW_VERSION = 1
|
||||
V.ANIM_VERSION = 1
|
||||
|
||||
@@ -19,6 +19,7 @@ return function(V)
|
||||
end
|
||||
|
||||
V.NAMED_TEXTS = offsets(Text.NAMED_TEXTS)
|
||||
V.NAMED_TEXTS.sText_CommunicationStandby = S.off("trade.o:sText_CommunicationStandby")
|
||||
V.NAMED_BATTLE_TEXTS = offsets(Text.NAMED_BATTLE_TEXTS)
|
||||
|
||||
local tables = {}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
local K = require("src.import.gba.rse.boot_gfx")
|
||||
|
||||
local M = {}
|
||||
|
||||
M.SUB = "rse/move_relearner"
|
||||
M.FILES = { "hearts.png" }
|
||||
M.REQUIRED = K.required(M.SUB, M.FILES)
|
||||
|
||||
-- pokeemerald/src/move_relearner.c:149
|
||||
M.HEARTS = { "appealEmpty", "appealFull", "jamEmpty", "jamFull" }
|
||||
-- pokeemerald/src/move_relearner.c:286
|
||||
local FIRST_HEART_TILE = 8
|
||||
|
||||
function M.run(rom, cache, opts)
|
||||
local c = K.context(rom, cache, opts, M.SUB)
|
||||
-- pokeemerald/src/move_relearner.c:188
|
||||
local tiles = c:raw("move_relearner.o:sUI_Tiles")
|
||||
local pal = c:pal("move_relearner.o:sUI_Pal", 16)
|
||||
local hearts = c:strip("hearts", tiles, 8, 8, #M.HEARTS, pal, 4, FIRST_HEART_TILE)
|
||||
local frames = {}
|
||||
for i, name in ipairs(M.HEARTS) do frames[name] = i - 1 end
|
||||
hearts.names = frames
|
||||
return true, c:finish({
|
||||
screen = "move_relearner",
|
||||
hearts = hearts,
|
||||
})
|
||||
end
|
||||
|
||||
function M.ready(cache, cacheRoot)
|
||||
return K.ready(M.SUB, cache, cacheRoot)
|
||||
end
|
||||
|
||||
return M
|
||||
@@ -18,7 +18,15 @@ return {
|
||||
{ name = "multichoice_extract", label = "Dialog Menus" },
|
||||
},
|
||||
},
|
||||
{
|
||||
id = "ui_relearner",
|
||||
run = "steps",
|
||||
weight = 0.01,
|
||||
steps = {
|
||||
{ name = "move_relearner_rse_extract", label = "Move Relearner" },
|
||||
},
|
||||
},
|
||||
},
|
||||
sequential = { "ui_wild", "ui_places" },
|
||||
dirs = { "/region_map", "/scripts", "/chrome/map_popup" },
|
||||
sequential = { "ui_wild", "ui_places", "ui_relearner" },
|
||||
dirs = { "/region_map", "/scripts", "/chrome/map_popup", "/rse/move_relearner" },
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ M.PAGES = { "info", "info_egg", "skills", "battle_moves", "contest_moves" }
|
||||
|
||||
M.FILES = {
|
||||
"info.png", "info_egg.png", "skills.png", "battle_moves.png", "contest_moves.png",
|
||||
"move_types.png", "move_select.png", "status.png", "tiles.png", "markings.png",
|
||||
"move_types.png", "move_select.png", "status.png", "status_plate.png", "tiles.png", "markings.png",
|
||||
}
|
||||
|
||||
-- pokeemerald/src/pokemon_summary_screen.c:2338
|
||||
@@ -54,6 +54,19 @@ local function bytes(c, name)
|
||||
return out
|
||||
end
|
||||
|
||||
-- pokeemerald/src/pokemon_summary_screen.c:2405
|
||||
local function clearStatusPlate(map)
|
||||
local out = {}
|
||||
for i = 1, #map do out[i] = map:sub(i, i) end
|
||||
for row = 18, 19 do
|
||||
for col = 0, 9 do
|
||||
local o = (row * 32 + col) * 2
|
||||
out[o + 1], out[o + 2] = "\1", "\0"
|
||||
end
|
||||
end
|
||||
return table.concat(out)
|
||||
end
|
||||
|
||||
function M.run(rom, cache, opts)
|
||||
local c = K.context(rom, cache, opts, M.SUB)
|
||||
local gfx = c:lz("gSummaryScreen_Gfx")
|
||||
@@ -62,7 +75,9 @@ function M.run(rom, cache, opts)
|
||||
c:pal("gPPTextPalette", 15, pal, 8 * 16 + 1)
|
||||
local layers = {}
|
||||
for _, key in ipairs(M.PAGES) do
|
||||
local idx, W, H = K.bakeText(gfx, c:lz(TILEMAPS[key]), 30, 20)
|
||||
local map = c:lz(TILEMAPS[key])
|
||||
if key == "info" or key == "info_egg" then map = clearStatusPlate(map) end
|
||||
local idx, W, H = K.bakeText(gfx, map, 30, 20)
|
||||
layers[key] = c:layer({ key = key }, idx, W, H, pal)
|
||||
end
|
||||
layers.info.backdrop = pal[0]
|
||||
@@ -115,6 +130,13 @@ function M.run(rom, cache, opts)
|
||||
local selSheet, selW, selH = K.stack(selFrames, 16, 16)
|
||||
c:png("move_select.png", selW, selH, selSheet, selPal, true)
|
||||
|
||||
-- pokeemerald/src/pokemon_summary_screen.c:369
|
||||
local plateMap = string.char(
|
||||
4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 0x17, 8, 0x17, 8, 0x17, 8, 0x0b, 0,
|
||||
4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 0x17, 0, 0x17, 0, 0x17, 0, 0x0b, 8)
|
||||
local plateIdx, PW, PH = K.bakeText(gfx, plateMap, 10, 2, { linear = true, mapWidth = 10 })
|
||||
c:png("status_plate.png", PW, PH, plateIdx, pal, true)
|
||||
|
||||
-- pokeemerald/src/pokemon_summary_screen.c:1367
|
||||
local stPal = c:pal("gStatusPal_Icons", 16, nil, 0, true)
|
||||
local stGfx = c:lz("gStatusGfx_Icons")
|
||||
@@ -140,6 +162,7 @@ function M.run(rom, cache, opts)
|
||||
tiles = { png = c:path("tiles.png"), index = tileIndex },
|
||||
moveTypes = { png = c:path("move_types.png"), w = 32, h = 16, count = typeCount },
|
||||
moveSelect = { png = c:path("move_select.png"), w = 16, h = 16, count = #selFrames },
|
||||
statusPlate = { png = c:path("status_plate.png"), x = 0, y = 144 },
|
||||
status = { png = c:path("status.png"), w = 32, h = 8, count = #stFrames },
|
||||
palette = K.palList(pal, 0, 144),
|
||||
windows = windows(c, "sSummaryTemplate"),
|
||||
|
||||
@@ -308,7 +308,10 @@ local function flagId(name)
|
||||
local n = tonumber(name)
|
||||
if n then return n end
|
||||
local Flags = g3("scripting.flags")
|
||||
return Flags and Flags.IDS and Flags.IDS[name] or nil
|
||||
if not Flags then return nil end
|
||||
local ok, t = pcall(Flags.active, session())
|
||||
local ids = ok and t and t.IDS or Flags.IDS
|
||||
return ids and ids[name] or nil
|
||||
end
|
||||
|
||||
local function varId(name)
|
||||
@@ -317,7 +320,10 @@ local function varId(name)
|
||||
local n = tonumber(name)
|
||||
if n then return n end
|
||||
local Flags = g3("scripting.flags")
|
||||
return Flags and Flags.VAR_IDS and Flags.VAR_IDS[name] or nil
|
||||
if not Flags then return nil end
|
||||
local ok, t = pcall(Flags.active, session())
|
||||
local ids = ok and t and t.VAR_IDS or Flags.VAR_IDS
|
||||
return ids and ids[name] or nil
|
||||
end
|
||||
|
||||
function Gen3Compat.getFlag(name)
|
||||
|
||||
@@ -583,7 +583,8 @@ local function begin_sell(row)
|
||||
clamp_cursor()
|
||||
reshow()
|
||||
end
|
||||
if num == ItemsData.ITEM_TM_CASE then
|
||||
local frlgCases = require("src.core.game3.profile").family(BagMenu._session) ~= "rse"
|
||||
if frlgCases and num == ItemsData.ITEM_TM_CASE then
|
||||
-- src/item_menu.c:1825 GoToTMCase_Sell
|
||||
open_submenu(function()
|
||||
require("src.ui.game3.tm_case").show(BagMenu._session, BagMenu._bag, {
|
||||
@@ -591,7 +592,7 @@ local function begin_sell(row)
|
||||
})
|
||||
end)
|
||||
return
|
||||
elseif num == ItemsData.ITEM_BERRY_POUCH then
|
||||
elseif frlgCases and num == ItemsData.ITEM_BERRY_POUCH then
|
||||
-- src/item_menu.c:1830 GoToBerryPouch_Sell
|
||||
open_submenu(function()
|
||||
require("src.ui.game3.berry_pouch").show(BagMenu._session, BagMenu._bag, {
|
||||
@@ -848,7 +849,8 @@ local function handle_menu_input(input)
|
||||
end
|
||||
else
|
||||
local numId = ItemsData.toNumericId(row.id)
|
||||
if numId == ItemsData.ITEM_TM_CASE or row.id == "TM_CASE" then
|
||||
local frlgCases = require("src.core.game3.profile").family(BagMenu._session) ~= "rse"
|
||||
if frlgCases and (numId == ItemsData.ITEM_TM_CASE or row.id == "TM_CASE") then
|
||||
local savedState = { pocketIdx = BagMenu.pocketIdx, cursor = BagMenu.cursor, scroll = BagMenu.scroll }
|
||||
local TmCase = require("src.ui.game3.tm_case")
|
||||
open_submenu(function()
|
||||
@@ -866,7 +868,7 @@ local function handle_menu_input(input)
|
||||
})
|
||||
end)
|
||||
return
|
||||
elseif numId == ItemsData.ITEM_BERRY_POUCH or row.id == "BERRY_POUCH" then
|
||||
elseif frlgCases and (numId == ItemsData.ITEM_BERRY_POUCH or row.id == "BERRY_POUCH") then
|
||||
local savedState = { pocketIdx = BagMenu.pocketIdx, cursor = BagMenu.cursor, scroll = BagMenu.scroll }
|
||||
local BerryPouch = require("src.ui.game3.berry_pouch")
|
||||
open_submenu(function()
|
||||
|
||||
@@ -70,6 +70,14 @@ end
|
||||
|
||||
function CoinsBox.draw()
|
||||
if not CoinsBox.visible then return end
|
||||
if require("src.core.game3.profile").family() == "rse" then
|
||||
-- pokeemerald/src/coins.c:14 PrintCoinsString, :24 ShowCoinsWindow
|
||||
local tpl = Window.template(CoinsBox.x, CoinsBox.y, 8, 2)
|
||||
Window.stdFrame(tpl)
|
||||
local text = RomText.plain("gText_Coins", { stringVars = { tostring(clamp(CoinsBox._amount)) } })
|
||||
Window.printPx(text, CoinsBox.x * 8 + 64 - FrlgFont.measure(text), CoinsBox.y * 8 + 1)
|
||||
return
|
||||
end
|
||||
-- pokefirered/src/coins.c:79
|
||||
local left = CoinsBox.x + 1
|
||||
local top = CoinsBox.y + 1
|
||||
|
||||
@@ -374,11 +374,13 @@ function EggHatch.update(dt)
|
||||
-- pokefirered/src/daycare.c:1935 IsFanfareTaskInactive
|
||||
local quiet = (not Audio.isFanfareFinished) or Audio.isFanfareFinished()
|
||||
if quiet and EggHatch._timer > 1 then
|
||||
-- pokefirered/src/daycare.c:1944 gText_NickHatchPrompt, pokeemerald/src/egg_hatch.c:673
|
||||
local key = require("src.core.game3.profile").family(EggHatch._session) == "rse"
|
||||
and "gText_NicknameHatchPrompt" or "gText_NickHatchPrompt"
|
||||
local text = RomText.box(key, { stringVars = { EggHatch._name } })
|
||||
EggHatch._state = "nickname_msg"
|
||||
EggHatch._timer = 0
|
||||
-- pokefirered/src/daycare.c:1944 gText_NickHatchPrompt
|
||||
Message.show(RomText.box("gText_NickHatchPrompt", { stringVars = { EggHatch._name } }),
|
||||
{ stay = true, frame = "battle" })
|
||||
Message.show(text, { stay = true, frame = "battle" })
|
||||
end
|
||||
|
||||
elseif st == "nickname_msg" then
|
||||
|
||||
@@ -402,6 +402,11 @@ local function right_align_3(n)
|
||||
n = math.floor(tonumber(n) or 0)
|
||||
if n < 0 then n = 0 end
|
||||
if n > 999 then n = 999 end
|
||||
if isRse() then
|
||||
-- pokeemerald/src/string_util.c:209 ConvertIntToDecimalStringN
|
||||
local digits = tostring(n)
|
||||
return string.rep("{UNK_SPACER}", 3 - #digits) .. digits
|
||||
end
|
||||
return string.format("%3d", n)
|
||||
end
|
||||
|
||||
@@ -1258,7 +1263,12 @@ function PartyMenu.enterChosenMon(slot)
|
||||
se("SE_FAILURE")
|
||||
-- pokefirered/src/party_menu.c:3769
|
||||
local key = (max == 2) and "gText_NoMoreThanTwoMayEnter" or "gText_NoMoreThanThreeMayEnter"
|
||||
show_rom_message(key, nil, function() PartyMenu.mode = "choose_multi" end)
|
||||
local vars = nil
|
||||
if isRse() then
|
||||
-- pokeemerald/src/party_menu.c:3544
|
||||
key, vars = "gText_NoMoreThanVar1Pkmn", { tostring(max) }
|
||||
end
|
||||
show_rom_message(key, vars, function() PartyMenu.mode = "choose_multi" end)
|
||||
return false
|
||||
end
|
||||
se("SE_SELECT")
|
||||
@@ -1798,13 +1808,19 @@ function PartyMenu.handleInput(input)
|
||||
if res.action == "dig" then
|
||||
-- pokefirered/src/party_menu.c:3946
|
||||
destMap = session.escapeWarp and session.escapeWarp.map
|
||||
key = "gText_EscapeFromHereAndReturnTo"
|
||||
key = isRse() and "gText_EscapeFromHere" or "gText_EscapeFromHereAndReturnTo"
|
||||
end
|
||||
local game = Field._game
|
||||
local def = game and game.data and game.data.maps and game.data.maps[destMap]
|
||||
local Sections = require("src.import.gba.map_sections_extract")
|
||||
-- pokefirered/src/region_map.c:3828 GetMapNameGeneric
|
||||
local placeName = Sections.getPlaceName(destMap, def and def.regionMapSectionId)
|
||||
local placeName
|
||||
if isRse() then
|
||||
-- pokeemerald/src/party_menu.c:3741
|
||||
placeName = require("src.ui.game3.rse.mapsec").name(def and def.regionMapSectionId)
|
||||
else
|
||||
local Sections = require("src.import.gba.map_sections_extract")
|
||||
-- pokefirered/src/region_map.c:3828 GetMapNameGeneric
|
||||
placeName = Sections.getPlaceName(destMap, def and def.regionMapSectionId)
|
||||
end
|
||||
-- pokefirered/src/party_menu.c:3984 DisplayFieldMoveExitAreaMessage
|
||||
PartyMenu.showYesNo(RomText.box(key, { stringVars = { placeName }, maxWidth = 216 }), function(yes)
|
||||
if yes then
|
||||
|
||||
@@ -505,7 +505,9 @@ function RseBag.draw(Bag)
|
||||
drawDescription(RomText.plain("gText_Var1IsSelected", { stringVars = { sel.name } }), pal)
|
||||
drawGrid(Bag, sel)
|
||||
elseif (Bag.mode == "toss" or Bag.mode == "deposit") and sel then
|
||||
drawDescription(RomText.plain("gText_TossHowManyVar1s", { stringVars = { sel.name } }), pal)
|
||||
-- pokeemerald/src/item_menu.c:2203
|
||||
local key = Bag.mode == "deposit" and "gText_DepositHowManyVar1" or "gText_TossHowManyVar1s"
|
||||
drawDescription(RomText.plain(key, { stringVars = { sel.name } }), pal)
|
||||
drawQuantity(pocket, Bag.tossQty or 1)
|
||||
elseif Bag.mode == "toss_confirm" and sel then
|
||||
drawDescription(RomText.plain("gText_ConfirmTossItems", { stringVars = { sel.name, tostring(Bag.tossQty) } }), pal)
|
||||
|
||||
@@ -0,0 +1,453 @@
|
||||
-- pokeemerald/src/move_relearner.c:475 DoMoveRelearnerMain
|
||||
|
||||
local Stack = require("src.ui.game3.stack")
|
||||
local Window = require("src.ui.game3.window")
|
||||
local FrlgFont = require("src.ui.game3.frlg_font")
|
||||
local Pokemon = require("src.core.game3.pokemon")
|
||||
local MoveLearn = require("src.core.game3.move_learn")
|
||||
local LearnMove = require("src.core.game3.battle.learn_move")
|
||||
local SummaryData = require("src.core.game3.summary_data")
|
||||
local RomText = require("src.core.game3.rom_text")
|
||||
local Kit = require("src.ui.game3.rse.scene_kit")
|
||||
|
||||
local RseRelearner = {}
|
||||
|
||||
RseRelearner.SUB = "rse/move_relearner"
|
||||
RseRelearner.open = false
|
||||
RseRelearner.state = "list"
|
||||
RseRelearner.cursor = 1
|
||||
RseRelearner.scroll = 0
|
||||
RseRelearner.yesNoCursor = 1
|
||||
RseRelearner.contest = false
|
||||
RseRelearner.t = 0
|
||||
|
||||
-- pokeemerald/src/menu_specialized.c:108 sMoveRelearnerWindowTemplates
|
||||
local WIN_DESC = Window.template(1, 1, 16, 12)
|
||||
local WIN_LIST = Window.template(19, 1, 10, 12)
|
||||
local WIN_MSG = Window.template(4, 15, 22, 4)
|
||||
-- pokeemerald/src/menu_specialized.c:159 sMoveRelearnerYesNoMenuTemplate
|
||||
local WIN_YESNO = Window.template(22, 8, 5, 4)
|
||||
RseRelearner.WIN = { desc = WIN_DESC, list = WIN_LIST, msg = WIN_MSG, yesno = WIN_YESNO }
|
||||
|
||||
-- pokeemerald/src/menu_specialized.c:747
|
||||
local MAX_SHOWN = 6
|
||||
local NARROW = { font = "narrow" }
|
||||
|
||||
-- pokeemerald/src/move_relearner.c:254
|
||||
local MODE_ARROWS = { { "left", 27, 16 }, { "right", 117, 16 } }
|
||||
-- pokeemerald/src/move_relearner.c:269
|
||||
local LIST_UP, LIST_DOWN = { 192, 8 }, { 192, 104 }
|
||||
|
||||
local function se(name)
|
||||
local SE = require("src.core.game3.se_ids")
|
||||
pcall(function() require("src.core.game3.audio").playSe(SE[name]) end)
|
||||
end
|
||||
|
||||
function RseRelearner.isOpen()
|
||||
return RseRelearner.open
|
||||
end
|
||||
|
||||
function RseRelearner.moves()
|
||||
return RseRelearner._moves or {}
|
||||
end
|
||||
|
||||
local function total_rows()
|
||||
return #RseRelearner.moves() + 1
|
||||
end
|
||||
|
||||
local function shown()
|
||||
return math.min(MAX_SHOWN, total_rows())
|
||||
end
|
||||
|
||||
local function clamp_cursor()
|
||||
local total = total_rows()
|
||||
if RseRelearner.cursor > total then RseRelearner.cursor = total end
|
||||
if RseRelearner.cursor < 1 then RseRelearner.cursor = 1 end
|
||||
local n = shown()
|
||||
if RseRelearner.cursor <= RseRelearner.scroll then RseRelearner.scroll = RseRelearner.cursor - 1 end
|
||||
if RseRelearner.cursor > RseRelearner.scroll + n then RseRelearner.scroll = RseRelearner.cursor - n end
|
||||
if RseRelearner.scroll > total - n then RseRelearner.scroll = total - n end
|
||||
if RseRelearner.scroll < 0 then RseRelearner.scroll = 0 end
|
||||
end
|
||||
|
||||
local function mon_name()
|
||||
return Pokemon.displayMonName(RseRelearner._mon)
|
||||
end
|
||||
|
||||
local function selected_move()
|
||||
return RseRelearner.moves()[RseRelearner.cursor]
|
||||
end
|
||||
|
||||
local function clean(text)
|
||||
text = tostring(text or ""):gsub("\\n", "\n")
|
||||
text = text:gsub("\\p$", ""):gsub("\\p", "\n")
|
||||
return text
|
||||
end
|
||||
|
||||
-- pokeemerald/src/move_relearner.c:836 ShowTeachMoveText
|
||||
local function to_list()
|
||||
RseRelearner.state = "list"
|
||||
RseRelearner.prompt = RomText.plain("gText_TeachWhichMoveToPkmn", { stringVars = { mon_name() } })
|
||||
end
|
||||
|
||||
local function push_message(text, cb)
|
||||
RseRelearner.state = "message"
|
||||
RseRelearner.prompt = clean(text)
|
||||
RseRelearner._messageCb = cb
|
||||
end
|
||||
|
||||
local function ask_yes_no(text, cb)
|
||||
RseRelearner.state = "yesno"
|
||||
RseRelearner.prompt = clean(text)
|
||||
RseRelearner.yesNoCursor = 1
|
||||
RseRelearner._yesNoCb = cb
|
||||
end
|
||||
|
||||
local function party_slot()
|
||||
local party = RseRelearner._session and RseRelearner._session.party
|
||||
if type(party) == "table" then
|
||||
for i = 1, #party do
|
||||
if party[i] == RseRelearner._mon then return party, i end
|
||||
end
|
||||
end
|
||||
return { RseRelearner._mon }, 1
|
||||
end
|
||||
|
||||
-- pokeemerald/src/move_relearner.c:661 ShowSelectMovePokemonSummaryScreen
|
||||
local function open_forget_screen(_labels, cb)
|
||||
local okS, SummaryMenu = pcall(require, "src.ui.game3.summary_menu")
|
||||
if not (okS and SummaryMenu and SummaryMenu.openMenu) then
|
||||
if cb then cb(nil) end
|
||||
return
|
||||
end
|
||||
local party, slot = party_slot()
|
||||
local okOpen = pcall(SummaryMenu.openMenu, party, slot, {
|
||||
session = RseRelearner._session,
|
||||
mode = "select_move",
|
||||
moveToLearn = RseRelearner._pendingMoveId,
|
||||
onSelectMove = function(slotIdx)
|
||||
if cb then cb(slotIdx) end
|
||||
end,
|
||||
})
|
||||
if not okOpen and cb then cb(nil) end
|
||||
end
|
||||
|
||||
function RseRelearner.finish(learned)
|
||||
if not RseRelearner.open then return end
|
||||
RseRelearner.open = false
|
||||
RseRelearner.state = "list"
|
||||
RseRelearner._messageCb = nil
|
||||
RseRelearner._yesNoCb = nil
|
||||
Stack.pop("move_relearner")
|
||||
local cb = RseRelearner._onDone
|
||||
RseRelearner._onDone = nil
|
||||
if cb then cb(learned == true) end
|
||||
end
|
||||
|
||||
-- pokeemerald/src/move_relearner.c:517
|
||||
local function start_learn(moveId)
|
||||
RseRelearner._pendingMoveId = moveId
|
||||
LearnMove.begin({
|
||||
mon = RseRelearner._mon,
|
||||
moveId = moveId,
|
||||
relearner = true,
|
||||
displayName = mon_name(),
|
||||
pushMsg = push_message,
|
||||
askYesNo = ask_yes_no,
|
||||
askForget = open_forget_screen,
|
||||
onDone = function(learned)
|
||||
if learned then
|
||||
RseRelearner.finish(true)
|
||||
else
|
||||
-- pokeemerald/src/move_relearner.c:637
|
||||
RseRelearner._moves = MoveLearn.relearnableMoves(RseRelearner._mon)
|
||||
clamp_cursor()
|
||||
to_list()
|
||||
end
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
function RseRelearner.show(mon, opts)
|
||||
opts = opts or {}
|
||||
RseRelearner.open = true
|
||||
RseRelearner._mon = mon
|
||||
RseRelearner._session = opts.session
|
||||
RseRelearner._onDone = opts.onDone
|
||||
RseRelearner._moves = MoveLearn.relearnableMoves(mon)
|
||||
RseRelearner._pendingMoveId = nil
|
||||
RseRelearner._messageCb = nil
|
||||
RseRelearner._yesNoCb = nil
|
||||
RseRelearner.cursor = 1
|
||||
RseRelearner.scroll = 0
|
||||
-- pokeemerald/src/move_relearner.c:406
|
||||
RseRelearner.contest = false
|
||||
RseRelearner.t = 0
|
||||
clamp_cursor()
|
||||
to_list()
|
||||
Stack.push("move_relearner", RseRelearner, { hideBelow = true, fullscreen = true })
|
||||
end
|
||||
|
||||
-- pokeemerald/src/move_relearner.c:811
|
||||
function RseRelearner.giveUpPrompt()
|
||||
ask_yes_no(RomText.plain("gText_MoveRelearnerGiveUp", { stringVars = { mon_name() } }), function(yes)
|
||||
if yes then
|
||||
RseRelearner.finish(false)
|
||||
else
|
||||
to_list()
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- pokeemerald/src/menu_helpers.c:252 GetLRKeysPressed
|
||||
local function lr_pressed(input)
|
||||
if not (input:wasPressed("l") or input:wasPressed("r")) then return false end
|
||||
local ok, lr = pcall(function() return require("src.core.game3.options").lrMode(RseRelearner._session) end)
|
||||
return ok and lr == true
|
||||
end
|
||||
|
||||
function RseRelearner.handleInput(input)
|
||||
if not RseRelearner.open then return end
|
||||
RseRelearner.t = RseRelearner.t + 1
|
||||
|
||||
if RseRelearner.state == "message" then
|
||||
if input:wasPressed("a") or input:wasPressed("b") then
|
||||
se("SE_SELECT")
|
||||
local cb = RseRelearner._messageCb
|
||||
RseRelearner._messageCb = nil
|
||||
to_list()
|
||||
if cb then cb() end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if RseRelearner.state == "yesno" then
|
||||
if input:wasPressed("up") and RseRelearner.yesNoCursor ~= 1 then
|
||||
RseRelearner.yesNoCursor = 1
|
||||
se("SE_SELECT")
|
||||
elseif input:wasPressed("down") and RseRelearner.yesNoCursor ~= 2 then
|
||||
RseRelearner.yesNoCursor = 2
|
||||
se("SE_SELECT")
|
||||
elseif input:wasPressed("a") or input:wasPressed("b") then
|
||||
se("SE_SELECT")
|
||||
local yes = input:wasPressed("a") and RseRelearner.yesNoCursor == 1
|
||||
local cb = RseRelearner._yesNoCb
|
||||
RseRelearner._yesNoCb = nil
|
||||
to_list()
|
||||
if cb then cb(yes) end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
local total = total_rows()
|
||||
if input:wasPressed("up") then
|
||||
if RseRelearner.cursor > 1 then
|
||||
RseRelearner.cursor = RseRelearner.cursor - 1
|
||||
clamp_cursor()
|
||||
se("SE_SELECT")
|
||||
end
|
||||
elseif input:wasPressed("down") then
|
||||
if RseRelearner.cursor < total then
|
||||
RseRelearner.cursor = RseRelearner.cursor + 1
|
||||
clamp_cursor()
|
||||
se("SE_SELECT")
|
||||
end
|
||||
elseif input:wasPressed("left") or input:wasPressed("right") or lr_pressed(input) then
|
||||
-- pokeemerald/src/move_relearner.c:786
|
||||
se("SE_SELECT")
|
||||
RseRelearner.contest = not RseRelearner.contest
|
||||
elseif input:wasPressed("a") then
|
||||
se("SE_SELECT")
|
||||
local moveId = selected_move()
|
||||
if moveId then
|
||||
-- pokeemerald/src/move_relearner.c:819
|
||||
ask_yes_no(RomText.plain("gText_MoveRelearnerTeachMoveConfirm",
|
||||
{ stringVars = { mon_name(), Pokemon.moveName(moveId) } }), function(yes)
|
||||
if yes then
|
||||
start_learn(moveId)
|
||||
else
|
||||
to_list()
|
||||
end
|
||||
end)
|
||||
else
|
||||
RseRelearner.giveUpPrompt()
|
||||
end
|
||||
elseif input:wasPressed("b") then
|
||||
se("SE_SELECT")
|
||||
RseRelearner.giveUpPrompt()
|
||||
end
|
||||
end
|
||||
|
||||
local function manifest()
|
||||
return Kit.manifest(RseRelearner.SUB)
|
||||
end
|
||||
|
||||
local function frame(tpl)
|
||||
Window.stdFrame(tpl)
|
||||
Window.fill(tpl, 1, 1, 1, 1)
|
||||
end
|
||||
|
||||
local function type_name(t)
|
||||
local Types = require("src.core.game3.battle.types")
|
||||
local id = tonumber(t)
|
||||
if not id and type(t) == "string" then id = Types.ID[t:upper()] end
|
||||
local ok, name = pcall(Types.name, id or 0)
|
||||
return ok and name or ""
|
||||
end
|
||||
|
||||
local function print_at(text, x, y, opts)
|
||||
FrlgFont.draw(tostring(text or ""), WIN_DESC.left * 8 + x, WIN_DESC.top * 8 + y, opts)
|
||||
end
|
||||
|
||||
local function right(text, edge, y)
|
||||
print_at(text, edge - FrlgFont.measure(text), y)
|
||||
end
|
||||
|
||||
local function center(text, width, y)
|
||||
print_at(text, math.floor((width - FrlgFont.measure(text)) / 2), y)
|
||||
end
|
||||
|
||||
local function print_lines(text, x, y, opts)
|
||||
local pitch = FrlgFont.linePitch(opts)
|
||||
for line in (tostring(text or "") .. "\n"):gmatch("(.-)\n") do
|
||||
print_at(line, x, y, opts)
|
||||
y = y + pitch
|
||||
end
|
||||
end
|
||||
|
||||
-- pokeemerald/src/menu_specialized.c:752 MoveRelearnerLoadBattleMoveDescription
|
||||
local function draw_battle(moveId)
|
||||
center(RomText.plain("gText_MoveRelearnerBattleMoves"), 128, 1)
|
||||
local ppLabel = RomText.plain("gText_MoveRelearnerPP")
|
||||
print_at(ppLabel, 4, 41)
|
||||
right(RomText.plain("gText_MoveRelearnerPower"), 106, 25)
|
||||
right(RomText.plain("gText_MoveRelearnerAccuracy"), 106, 41)
|
||||
if not moveId then return end
|
||||
local row = Pokemon.battleMove(moveId) or {}
|
||||
print_at(type_name(row.type), 4, 25)
|
||||
print_at(tostring(tonumber(row.pp) or 0), 4 + FrlgFont.measure(ppLabel), 41)
|
||||
local power, acc = tonumber(row.power) or 0, tonumber(row.accuracy) or 0
|
||||
local dashes = RomText.plain("gText_ThreeDashes")
|
||||
print_at(power < 2 and dashes or tostring(power), 106, 25)
|
||||
print_at(acc == 0 and dashes or tostring(acc), 106, 41)
|
||||
print_lines(SummaryData.moveDescription(moveId, Pokemon.moveName(moveId)), 0, 65, NARROW)
|
||||
end
|
||||
|
||||
local function contest_row(moveId)
|
||||
local c = Kit.loadLua("data/generated/gba/pokemon/contest_moves.lua")
|
||||
local cm = c and c.moves and c.moves[moveId]
|
||||
local eff = cm and c.effects and c.effects[cm.effect]
|
||||
return cm, eff, c
|
||||
end
|
||||
|
||||
-- pokeemerald/src/move_relearner.c:919 MoveRelearnerShowHideHearts
|
||||
local function heart_counts(eff)
|
||||
local appeal = eff and eff.appeal or 0
|
||||
local jam = eff and eff.jam or 0
|
||||
appeal = appeal == 0xFF and 0 or math.floor(appeal / 10)
|
||||
jam = jam == 0xFF and 0 or math.floor(jam / 10)
|
||||
return appeal, jam
|
||||
end
|
||||
RseRelearner.heartCounts = heart_counts
|
||||
|
||||
local quads = {}
|
||||
local function heart_quad(frame, h, sw, sh)
|
||||
local q = quads[frame]
|
||||
if not q then
|
||||
q = love.graphics.newQuad(0, frame * h.h, h.w, h.h, sw, sh)
|
||||
quads[frame] = q
|
||||
end
|
||||
return q
|
||||
end
|
||||
|
||||
local function draw_hearts(eff)
|
||||
local m = manifest()
|
||||
local h = m and m.hearts
|
||||
local img = h and Kit.image(h.png)
|
||||
if not img then return end
|
||||
local sw, sh = img:getDimensions()
|
||||
local appeal, jam = heart_counts(eff)
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
for i = 0, 7 do
|
||||
-- pokeemerald/src/move_relearner.c:856
|
||||
local x = (i % 4) * 8 + 104 - h.w / 2
|
||||
local ya = math.floor(i / 4) * 8 + 36 - h.h / 2
|
||||
local yj = math.floor(i / 4) * 8 + 52 - h.h / 2
|
||||
local fa = i < appeal and h.names.appealFull or h.names.appealEmpty
|
||||
local fj = i < jam and h.names.jamFull or h.names.jamEmpty
|
||||
love.graphics.draw(img, heart_quad(fa, h, sw, sh), x, ya)
|
||||
love.graphics.draw(img, heart_quad(fj, h, sw, sh), x, yj)
|
||||
end
|
||||
end
|
||||
|
||||
-- pokeemerald/src/menu_specialized.c:814 MoveRelearnerMenuLoadContestMoveDescription
|
||||
local function draw_contest(moveId)
|
||||
center(RomText.plain("gText_MoveRelearnerContestMovesTitle"), 128, 1)
|
||||
right(RomText.plain("gText_MoveRelearnerAppeal"), 92, 25)
|
||||
right(RomText.plain("gText_MoveRelearnerJam"), 92, 41)
|
||||
if not moveId then return end
|
||||
local cm, eff, c = contest_row(moveId)
|
||||
if cm then print_at(c.categories and c.categories[cm.category] or "", 4, 25) end
|
||||
if eff then print_lines(eff.description, 0, 65, NARROW) end
|
||||
draw_hearts(eff)
|
||||
end
|
||||
|
||||
local function draw_list()
|
||||
local moves = RseRelearner.moves()
|
||||
local ox, oy = WIN_LIST.left * 8, WIN_LIST.top * 8
|
||||
local pitch = Window.optionHeight()
|
||||
for i = 1, shown() do
|
||||
local idx = RseRelearner.scroll + i
|
||||
local y = oy + 1 + (i - 1) * pitch
|
||||
-- pokeemerald/src/move_relearner.c:913
|
||||
local label = moves[idx] and Pokemon.moveName(moves[idx]) or RomText.plain("gText_Cancel")
|
||||
Window.printPx(label, ox + 8, y)
|
||||
end
|
||||
Window.cursorPx(ox, oy + 1 + (RseRelearner.cursor - RseRelearner.scroll - 1) * pitch)
|
||||
end
|
||||
|
||||
local function draw_arrows()
|
||||
local BagChrome = require("src.ui.game3.rse.bag_chrome")
|
||||
local t = RseRelearner.t
|
||||
for _, a in ipairs(MODE_ARROWS) do BagChrome.drawArrow(a[1], a[2], a[3], t) end
|
||||
if RseRelearner.scroll > 0 then BagChrome.drawArrow("up", LIST_UP[1], LIST_UP[2], t) end
|
||||
if RseRelearner.scroll < total_rows() - shown() then BagChrome.drawArrow("down", LIST_DOWN[1], LIST_DOWN[2], t) end
|
||||
end
|
||||
|
||||
function RseRelearner.draw()
|
||||
if not RseRelearner.open then return end
|
||||
if not (love and love.graphics) then return end
|
||||
-- pokeemerald/src/move_relearner.c:415
|
||||
love.graphics.setColor(0, 0, 0, 1)
|
||||
love.graphics.rectangle("fill", 0, 0, 240, 160)
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
-- pokeemerald/src/menu_specialized.c:703 InitMoveRelearnerWindows
|
||||
frame(WIN_DESC)
|
||||
frame(WIN_LIST)
|
||||
frame(WIN_MSG)
|
||||
|
||||
local moveId = selected_move()
|
||||
if RseRelearner.contest then draw_contest(moveId) else draw_battle(moveId) end
|
||||
draw_list()
|
||||
if RseRelearner.state == "list" then draw_arrows() end
|
||||
|
||||
if RseRelearner.prompt then
|
||||
local y = WIN_MSG.top * 8 + 1
|
||||
for line in (RseRelearner.prompt .. "\n"):gmatch("(.-)\n") do
|
||||
Window.printPx(line, WIN_MSG.left * 8, y)
|
||||
y = y + FrlgFont.linePitch()
|
||||
end
|
||||
end
|
||||
|
||||
if RseRelearner.state == "yesno" then
|
||||
-- pokeemerald/src/menu_specialized.c:874 MoveRelearnerCreateYesNoMenu
|
||||
frame(WIN_YESNO)
|
||||
local ox, oy = WIN_YESNO.left * 8, WIN_YESNO.top * 8 + 1
|
||||
local pitch = Window.optionHeight()
|
||||
Window.printPx(RomText.plain("gText_Yes"), ox + 8, oy)
|
||||
Window.printPx(RomText.plain("gText_No"), ox + 8, oy + pitch)
|
||||
Window.cursorPx(ox, oy + (RseRelearner.yesNoCursor - 1) * pitch)
|
||||
end
|
||||
end
|
||||
|
||||
return RseRelearner
|
||||
@@ -467,6 +467,14 @@ local function draw_money_label()
|
||||
end
|
||||
end
|
||||
|
||||
RseShop.drawMoneyBox = function(amount)
|
||||
local m = WIN.money
|
||||
Window.stdFrame(m)
|
||||
Window.fill(m, 1, 1, 1, 1)
|
||||
draw_money_label()
|
||||
RseShop.drawMoneyAmount(nil, m, amount)
|
||||
end
|
||||
|
||||
local iconsImage
|
||||
local blankIcons = {}
|
||||
|
||||
|
||||
@@ -465,11 +465,22 @@ local function drawSkills(m, Sm, mon)
|
||||
drawTile(m, "exp", t, (21 + i) * 8, 18 * 8)
|
||||
ticks = math.max(0, ticks - 8)
|
||||
end
|
||||
-- pokeemerald/src/pokemon_summary_screen.c:1493
|
||||
local ailment = SummaryData.statusAilment(mon)
|
||||
if ailment and ailment > 0 then
|
||||
put(m, win(m, W.STATUS), "gText_Status", 2, 1, 1)
|
||||
drawFrame(m.status, ailment - 1, 64 - 16, 152 - 4)
|
||||
end
|
||||
|
||||
local function drawMoveSelector(m, row, endFrame, midFrame)
|
||||
local y = 40 + (row - 1) * 16 - 8
|
||||
for i = 0, 9 do
|
||||
local x = i * 16 + 89 - 8
|
||||
if i == 9 then
|
||||
local img = Kit.image(m.moveSelect.png)
|
||||
if img then
|
||||
local sw, sh = img:getDimensions()
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.draw(img, quad(m.moveSelect.png, 0, endFrame * 16, 16, 16, sw, sh), x + 16, y, 0, -1, 1)
|
||||
end
|
||||
else
|
||||
drawFrame(m.moveSelect, i == 0 and endFrame or midFrame, x, y)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -503,21 +514,19 @@ local function drawMoves(m, Sm, mon, contest, detail)
|
||||
local cur = Sm._moveCursor or 1
|
||||
local sel = moves[cur]
|
||||
-- pokeemerald/src/pokemon_summary_screen.c:4112
|
||||
local swapSlot = Sm._swapSlot
|
||||
if (swapSlot ~= nil) ~= st.swapping then
|
||||
st.swapping = swapSlot ~= nil
|
||||
st.blink = 0
|
||||
end
|
||||
st.blink = ((st.blink or 0) + 1) % 32
|
||||
if st.blink <= 24 then
|
||||
local y = 40 + (cur - 1) * 16 - 8
|
||||
for i = 0, 9 do
|
||||
local x = i * 16 + 89 - 8
|
||||
if i == 9 then
|
||||
local img = Kit.image(m.moveSelect.png)
|
||||
if img then
|
||||
local sw, sh = img:getDimensions()
|
||||
love.graphics.draw(img, quad(m.moveSelect.png, 0, 4 * 16, 16, 16, sw, sh), x + 16, y, 0, -1, 1)
|
||||
end
|
||||
else
|
||||
drawFrame(m.moveSelect, i == 0 and 4 or 5, x, y)
|
||||
end
|
||||
end
|
||||
local visible = st.blink <= 24
|
||||
if swapSlot then
|
||||
-- pokeemerald/src/pokemon_summary_screen.c:2043
|
||||
drawMoveSelector(m, swapSlot, 6, 7)
|
||||
if visible then drawMoveSelector(m, cur, 4, 5) end
|
||||
elseif visible then
|
||||
drawMoveSelector(m, cur, 4, 5)
|
||||
end
|
||||
if not sel then return end
|
||||
local dw = pageWin(m, "moves", 2)
|
||||
@@ -566,6 +575,10 @@ function RseSummary.draw(Sm)
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
local L = m.layers
|
||||
drawLayer(egg and L.info_egg or L.info)
|
||||
local ailment = not egg and SummaryData.statusAilment(mon) or 0
|
||||
if ailment > 0 and m.statusPlate then
|
||||
drawFrame({ png = m.statusPlate.png, w = 80, h = 16 }, 0, m.statusPlate.x, m.statusPlate.y)
|
||||
end
|
||||
if page == 1 then drawLayer(L.skills, 80, 240) end
|
||||
if page == 2 then drawLayer(L.battle_moves, detail and 0 or 80, 240) end
|
||||
if page == 3 then drawLayer(L.contest_moves, detail and 0 or 80, 240) end
|
||||
@@ -574,6 +587,11 @@ function RseSummary.draw(Sm)
|
||||
drawPagination(m, page, minPage, maxPage)
|
||||
drawTitles(m, page, detail, Sm)
|
||||
drawPortrait(m, Sm, mon, egg, detail, page)
|
||||
if ailment > 0 and not detail then
|
||||
-- pokeemerald/src/pokemon_summary_screen.c:1493
|
||||
put(m, win(m, W.STATUS), "gText_Status", 2, 1, 1)
|
||||
drawFrame(m.status, ailment - 1, 64 - 16, 152 - 4)
|
||||
end
|
||||
if page == 0 then drawInfo(m, Sm, mon, egg)
|
||||
elseif page == 1 then drawSkills(m, Sm, mon)
|
||||
else drawMoves(m, Sm, mon, page == 3, detail) end
|
||||
|
||||
@@ -17,6 +17,10 @@ local function se(id)
|
||||
pcall(function() require("src.core.game3.audio").playSe(id) end)
|
||||
end
|
||||
|
||||
local function is_rse(session)
|
||||
return require("src.core.game3.profile").family(session) == "rse"
|
||||
end
|
||||
|
||||
local function price_of(itemId)
|
||||
local info = ItemsData.info(itemId)
|
||||
return math.max(0, math.floor(tonumber(info and info.price) or 0))
|
||||
@@ -49,22 +53,36 @@ function SellFlow.start(opts)
|
||||
self.colors = dialog_colors()
|
||||
if price_of(opts.itemId) == 0 then
|
||||
self.state = "cant"
|
||||
self.text = RomText.box("gText_OhNoICantBuyThat", { stringVars = { self.name } })
|
||||
if is_rse(self.session) then
|
||||
self.text = RomText.box("gText_CantBuyKeyItem", { stringVars = { [2] = self.name } })
|
||||
else
|
||||
self.text = RomText.box("gText_OhNoICantBuyThat", { stringVars = { self.name } })
|
||||
end
|
||||
self.textColors = self.colors
|
||||
return self
|
||||
end
|
||||
local owned = math.max(1, tonumber(opts.owned) or 1)
|
||||
self.owned = math.min(99, owned)
|
||||
self.owned = math.min(self:rseBerry() and 999 or 99, owned)
|
||||
if owned == 1 then
|
||||
self:ask()
|
||||
else
|
||||
self.state = "qty"
|
||||
self.text = RomText.box("gText_HowManyWouldYouLikeToSell", { stringVars = { self.name } })
|
||||
if is_rse(self.session) then
|
||||
self.text = RomText.box("gText_HowManyToSell", { stringVars = { [2] = self.name } })
|
||||
else
|
||||
self.text = RomText.box("gText_HowManyWouldYouLikeToSell", { stringVars = { self.name } })
|
||||
end
|
||||
self.textColors = self.colors
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
function SellFlow:rseBerry()
|
||||
if not is_rse(self.session) then return false end
|
||||
local ok, BagMenu = pcall(require, "src.ui.game3.bag_menu")
|
||||
return ok and BagMenu.currentPocket and BagMenu.currentPocket() == "BERRY_POUCH" or false
|
||||
end
|
||||
|
||||
function SellFlow:total()
|
||||
return self.unit * self.qty
|
||||
end
|
||||
@@ -73,16 +91,24 @@ end
|
||||
function SellFlow:ask()
|
||||
self.state = "confirm"
|
||||
self.yesNo = 1
|
||||
self.text = RomText.box("gText_ICanPayThisMuch_WouldThatBeOkay",
|
||||
{ stringVars = { [3] = tostring(self:total()) } })
|
||||
if is_rse(self.session) then
|
||||
self.text = RomText.box("gText_ICanPayVar1", { stringVars = { tostring(self:total()) } })
|
||||
else
|
||||
self.text = RomText.box("gText_ICanPayThisMuch_WouldThatBeOkay",
|
||||
{ stringVars = { [3] = tostring(self:total()) } })
|
||||
end
|
||||
self.textColors = self.colors
|
||||
end
|
||||
|
||||
-- src/item_menu.c:1917 Task_SellItem_Yes, :1928 Task_FinalizeSaleToShop
|
||||
function SellFlow:commit()
|
||||
local earn = self:total()
|
||||
self.text = RomText.box("gText_TurnedOverItemsWorthYen",
|
||||
{ stringVars = { self.name, [3] = tostring(earn) } })
|
||||
if is_rse(self.session) then
|
||||
self.text = RomText.box("gText_TurnedOverVar1ForVar2", { stringVars = { tostring(earn), self.name } })
|
||||
else
|
||||
self.text = RomText.box("gText_TurnedOverItemsWorthYen",
|
||||
{ stringVars = { self.name, [3] = tostring(earn) } })
|
||||
end
|
||||
self.textColors = FrlgFont.COLOR.NORMAL
|
||||
self.state = "done"
|
||||
se(SE.SE_SHOP)
|
||||
@@ -184,9 +210,41 @@ local function draw_money_box(amount)
|
||||
Window.printPx(s, 8 + 64 - w, 8 + 12, { small = true })
|
||||
end
|
||||
|
||||
-- pokeemerald/src/item_menu.c:2120 InitSellHowManyInput, :1201 PrintItemSoldAmount
|
||||
function SellFlow:drawRse()
|
||||
local st = self.state
|
||||
local Shop = require("src.ui.game3.rse.shop_menu")
|
||||
local Chrome = require("src.ui.game3.chrome")
|
||||
if st ~= "cant" then
|
||||
Shop.drawMoneyBox(tonumber(self.session and self.session.money) or 0)
|
||||
end
|
||||
Window.dialogueFrame()
|
||||
local w = Chrome.DLG_W * 8
|
||||
FrlgFont.draw(FrlgFont.wrap(self.text, w), Chrome.DLG_LEFT * 8, Chrome.DLG_TOP * 8 + 1,
|
||||
{ maxWidth = w, colors = FrlgFont.COLOR.NORMAL })
|
||||
if st == "qty" then
|
||||
local q = Shop.WIN.qty
|
||||
Window.stdFrame(q)
|
||||
Window.fill(q, 1, 1, 1, 1)
|
||||
local digits = self:rseBerry() and 3 or 2
|
||||
Window.printPx(RomText.plain("gText_xVar1", { stringVars = { string.format("%0" .. digits .. "d", self.qty) } }),
|
||||
q.left * 8, q.top * 8 + 1)
|
||||
Shop.drawMoneyAmount(nil, q, self:total())
|
||||
elseif st == "confirm" then
|
||||
local yn = Shop.WIN.yesno
|
||||
Window.stdFrame(yn)
|
||||
Window.fill(yn, 1, 1, 1, 1)
|
||||
local pitch = Window.optionHeight()
|
||||
Window.printPx(RomText.plain("gText_Yes"), yn.left * 8 + 8, yn.top * 8 + 1)
|
||||
Window.printPx(RomText.plain("gText_No"), yn.left * 8 + 8, yn.top * 8 + 1 + pitch)
|
||||
Window.cursorPx(yn.left * 8, yn.top * 8 + 1 + (self.yesNo - 1) * pitch)
|
||||
end
|
||||
end
|
||||
|
||||
function SellFlow:draw()
|
||||
local st = self.state
|
||||
if not st then return end
|
||||
if is_rse(self.session) then return self:drawRse() end
|
||||
local Chrome = require("src.ui.game3.chrome")
|
||||
if st ~= "cant" then
|
||||
draw_money_box(tonumber(self.session and self.session.money) or 0)
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
local U = require("tests.drivers.util")
|
||||
|
||||
local M = {}
|
||||
|
||||
function M.run(game, opts)
|
||||
local DIR = os.getenv("POKEPORT_SHOT_DIR") or opts.dir
|
||||
local fails = 0
|
||||
local function result(ok, label)
|
||||
print((ok and "PASS " or "FAIL ") .. label)
|
||||
if not ok then fails = fails + 1 end
|
||||
end
|
||||
local function finish()
|
||||
love.event.quit(fails == 0 and 0 or 1)
|
||||
end
|
||||
|
||||
for _ = 1, 900 do
|
||||
if game.phase == "boot" and game.boot then break end
|
||||
U.wait(1)
|
||||
end
|
||||
game:_handleBootAction(opts.newGame)
|
||||
U.wait(240)
|
||||
|
||||
local Runtime = require("src.core.game3.runtime")
|
||||
local Party = require("src.core.game3.party")
|
||||
local BattleBridge = require("src.core.game3.battle_bridge")
|
||||
local Battle = require("src.core.game3.battle")
|
||||
local Ui = require("src.core.game3.battle.ui")
|
||||
local C = require("src.core.game3.constants").of(opts.version)
|
||||
local session = Runtime.getSession()
|
||||
result(session ~= nil, "field session exists")
|
||||
if not session then finish() return end
|
||||
|
||||
local function sp(name) return C.species.byName["SPECIES_" .. name] end
|
||||
local function mv(name) return C.moves.byName["MOVE_" .. name] end
|
||||
|
||||
local function set_party(moves, pp)
|
||||
session.party = {}
|
||||
Party.giveMon(session, sp("CHARIZARD"), 50, "CHARIZARD")
|
||||
local m = session.party[1]
|
||||
m.moves = moves
|
||||
m.pp = pp
|
||||
m.maxPp = { 35, 40, 25, 30 }
|
||||
m.ppBonusesPacked = 57
|
||||
m.hp = m.maxHp or m.hp
|
||||
return m
|
||||
end
|
||||
|
||||
local lastTap = 0
|
||||
local f = 0
|
||||
local function pump_text()
|
||||
f = f + 1
|
||||
if Ui.dialogPending and Ui.dialogPending() and f - lastTap >= 14 then
|
||||
lastTap = f
|
||||
U.tap(game, "a")
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
local function at_command()
|
||||
return Battle.isActive() and Battle._phase == "command" and Ui._mode == "menu"
|
||||
end
|
||||
|
||||
local function open_moves(tag)
|
||||
local ok, err = BattleBridge.startWild(Runtime._mod, game, { species = sp("ZIGZAGOON"), level = 3 }, {})
|
||||
result(ok == true, tag .. " battle started " .. tostring(err or ""))
|
||||
if not ok then return false end
|
||||
for _ = 1, 6000 do
|
||||
if at_command() then break end
|
||||
if not pump_text() then U.wait(1) end
|
||||
end
|
||||
result(at_command(), tag .. " reached command menu")
|
||||
if not at_command() then return false end
|
||||
local st = Battle.getState()
|
||||
st.enemy.mon.maxHp = 999
|
||||
st.enemy.mon.hp = 999
|
||||
U.wait(30)
|
||||
U.tap(game, "a")
|
||||
U.wait(30)
|
||||
result(Ui._mode == "moves", tag .. " move menu open")
|
||||
return Ui._mode == "moves"
|
||||
end
|
||||
|
||||
local function end_battle()
|
||||
Battle.abort("run")
|
||||
for _ = 1, 600 do
|
||||
if not Battle.isActive() then break end
|
||||
U.wait(1)
|
||||
end
|
||||
U.wait(60)
|
||||
end
|
||||
|
||||
local function same(a, b)
|
||||
for i = 1, 4 do if (a[i] or 0) ~= (b[i] or 0) then return false end end
|
||||
return true
|
||||
end
|
||||
|
||||
local base = { mv("FLAMETHROWER"), mv("GROWL"), mv("SLASH"), mv("EMBER") }
|
||||
local basePp = { 11, 22, 13, 24 }
|
||||
|
||||
local mon = set_party({ base[1], base[2], base[3], base[4] }, { basePp[1], basePp[2], basePp[3], basePp[4] })
|
||||
if not open_moves("four") then finish() return end
|
||||
local st = Battle.getState()
|
||||
|
||||
st.link = true
|
||||
U.tap(game, "select")
|
||||
U.wait(6)
|
||||
result(Ui._swap == nil, "SELECT ignored in link battle")
|
||||
st.link = nil
|
||||
|
||||
U.tap(game, "select")
|
||||
U.wait(6)
|
||||
result(Ui._swap ~= nil and Ui._swap.cursor == 1, "SELECT opens swap with second cursor on slot 2")
|
||||
U.wait(10)
|
||||
U.still(game, DIR .. "/" .. opts.tag .. "_01_swap_start.png")
|
||||
|
||||
U.tap(game, "down")
|
||||
U.wait(6)
|
||||
result(Ui._swap and Ui._swap.cursor == 3, "down moves second cursor to slot 4")
|
||||
U.tap(game, "left")
|
||||
U.wait(6)
|
||||
result(Ui._swap and Ui._swap.cursor == 2, "left moves second cursor to slot 3")
|
||||
U.wait(10)
|
||||
U.still(game, DIR .. "/" .. opts.tag .. "_02_swap_cursor_slot3.png")
|
||||
|
||||
U.tap(game, "b")
|
||||
U.wait(6)
|
||||
result(Ui._swap == nil, "B leaves swap mode")
|
||||
result(same(st.player.mon.moves, base), "cancel leaves battler order unchanged")
|
||||
result(same(st.player.mon.pp, basePp), "cancel leaves battler pp unchanged")
|
||||
result(Ui._mode == "moves" and Ui._moveIndex == 1, "cancel keeps move cursor")
|
||||
|
||||
U.tap(game, "select")
|
||||
U.wait(6)
|
||||
U.tap(game, "left")
|
||||
U.wait(6)
|
||||
U.tap(game, "select")
|
||||
U.wait(6)
|
||||
local bmon = st.player.mon
|
||||
result(Ui._swap == nil, "SELECT confirms; same slot is a no-op")
|
||||
result(same(bmon.moves, base), "same-slot confirm leaves order unchanged")
|
||||
|
||||
U.tap(game, "select")
|
||||
U.wait(6)
|
||||
U.tap(game, "down")
|
||||
U.wait(6)
|
||||
U.tap(game, "left")
|
||||
U.wait(6)
|
||||
U.tap(game, "a")
|
||||
U.wait(10)
|
||||
local want = { base[3], base[2], base[1], base[4] }
|
||||
local wantPp = { basePp[3], basePp[2], basePp[1], basePp[4] }
|
||||
local wantMax = { 25, 40, 35, 30 }
|
||||
result(Ui._swap == nil, "A confirms swap")
|
||||
result(same(bmon.moves, want), "battler moves swapped 1<->3")
|
||||
result(same(bmon.pp, wantPp), "battler pp swapped 1<->3")
|
||||
result(same(bmon.maxPp, wantMax), "battler maxPp swapped 1<->3")
|
||||
result(bmon.ppBonusesPacked == 27, "battler pp bonuses swapped 1<->3")
|
||||
result(Ui._moveIndex == 3, "move cursor lands on the swapped-to slot")
|
||||
U.wait(10)
|
||||
U.still(game, DIR .. "/" .. opts.tag .. "_03_after_swap.png")
|
||||
|
||||
U.tap(game, "b")
|
||||
U.wait(20)
|
||||
result(Ui._mode == "menu", "B from move menu returns to action menu")
|
||||
U.tap(game, "a")
|
||||
U.wait(20)
|
||||
result(Ui._mode == "moves" and same(st.player.mon.moves, want), "reopened move menu keeps order")
|
||||
end_battle()
|
||||
result(same(mon.moves, want) and same(mon.pp, wantPp), "session party order persists after the battle")
|
||||
result(same(mon.maxPp, wantMax) and mon.ppBonusesPacked == 27, "session party maxPp and bonuses persist after the battle")
|
||||
|
||||
local mon1 = set_party({ mv("TACKLE"), 0, 0, 0 }, { 35, 0, 0, 0 })
|
||||
if not open_moves("single") then finish() return end
|
||||
U.tap(game, "select")
|
||||
U.wait(6)
|
||||
result(Ui._swap == nil, "SELECT ignored with one move")
|
||||
result(mon1.moves[1] == mv("TACKLE"), "single move untouched")
|
||||
end_battle()
|
||||
|
||||
finish()
|
||||
end
|
||||
|
||||
return M
|
||||
@@ -0,0 +1,109 @@
|
||||
local U = require("tests.drivers.util")
|
||||
local DIR = os.getenv("POKEPORT_SHOT_DIR") or "/tmp/em_bag_items_flags"
|
||||
|
||||
local failures = 0
|
||||
local function check(ok, label)
|
||||
print((ok and "PASS " or "FAIL ") .. label)
|
||||
if not ok then failures = failures + 1 end
|
||||
return ok
|
||||
end
|
||||
|
||||
local function finish()
|
||||
print((failures == 0 and "PASS" or "FAIL") .. " em_bag_items_flags failures=" .. failures)
|
||||
love.event.quit(failures == 0 and 0 or 1)
|
||||
U.wait(10)
|
||||
end
|
||||
|
||||
local function try(label, fn)
|
||||
local ok, err = xpcall(fn, debug.traceback)
|
||||
if not ok then
|
||||
print("[driver] " .. label .. " error: " .. tostring(err))
|
||||
failures = failures + 1
|
||||
end
|
||||
return ok
|
||||
end
|
||||
|
||||
local function body(game)
|
||||
local Runtime = require("src.core.game3.runtime")
|
||||
local Bag = require("src.core.game3.bag")
|
||||
local ItemUse = require("src.core.game3.item_use")
|
||||
local FieldMoves = require("src.core.game3.field_moves")
|
||||
local Field = require("src.core.game3.field")
|
||||
local Flags = require("src.core.game3.scripting.flags")
|
||||
local Space = require("src.core.game3.scripting.space")
|
||||
local PartyMenu = require("src.ui.game3.party_menu")
|
||||
local RomText = require("src.core.game3.rom_text")
|
||||
local Party = require("src.core.game3.party")
|
||||
local C = require("src.core.game3.constants").of("emerald")
|
||||
local session = Runtime.getSession()
|
||||
if not check(session and session.version == "emerald", "emerald session") then return end
|
||||
session.bag = session.bag or Bag.new()
|
||||
session.party = {}
|
||||
Party.giveMonToPlayer(session, C:require("species", "SPECIES_TORCHIC"), 10)
|
||||
|
||||
for _, name in ipairs({ "ITEM_POKE_FLUTE", "ITEM_TM_CASE", "ITEM_BERRY_POUCH" }) do
|
||||
local ok, kind, text = ItemUse.useField(session, session.bag, C:require("items", name))
|
||||
check(ok == false and text and text:find("advice", 1, true) ~= nil,
|
||||
name .. " is not usable, Dad's advice: " .. tostring(text))
|
||||
end
|
||||
|
||||
local up = C:require("flags", "FLAG_SYS_ENC_UP_ITEM")
|
||||
local down = C:require("flags", "FLAG_SYS_ENC_DOWN_ITEM")
|
||||
check(FieldMoves.SYS_FLAGS.WHITE_FLUTE_ACTIVE == up and up == 2221, "white flute flag is FLAG_SYS_ENC_UP_ITEM")
|
||||
check(FieldMoves.SYS_FLAGS.BLACK_FLUTE_ACTIVE == down and down == 2222, "black flute flag is FLAG_SYS_ENC_DOWN_ITEM")
|
||||
local ok, kind, text = ItemUse.useBlackWhiteFlute(session, C:require("items", "ITEM_WHITE_FLUTE"))
|
||||
check(ok and kind == "black_white_flute", "white flute used")
|
||||
check(Flags.getFlag(Space.store, nil, up) == true and Flags.getFlag(Space.store, nil, down) ~= true, "ENC_UP set, ENC_DOWN clear")
|
||||
check(Flags.getFlag(Space.store, nil, 0x803) ~= true, "trainer flag 0x803 untouched by the flute")
|
||||
local t = 0x803
|
||||
Flags.setFlag(Space.store, nil, t, true)
|
||||
Field.clearTempFieldEventData(Field._game, session.map)
|
||||
check(Flags.getFlag(Space.store, nil, up) ~= true, "ENC_UP cleared on map change")
|
||||
check(Flags.getFlag(Space.store, nil, t) == true, "trainer flag survives map change")
|
||||
local list = FieldMoves.tempSysFlags()
|
||||
local names = {}
|
||||
for _, id in ipairs(list) do names[#names + 1] = Flags.nameFor(id) end
|
||||
print("[driver] temp flags: " .. table.concat(names, ","))
|
||||
check(#list == 5, "five Emerald temp flags")
|
||||
|
||||
local Itemfinder = require("src.core.game3.itemfinder")
|
||||
check(Itemfinder.textKey("nothing", session) == "gText_ItemFinderNothing", "itemfinder key")
|
||||
|
||||
PartyMenu._chooseOrder = { 1, 2 }
|
||||
PartyMenu._chooseMax = 2
|
||||
local shown
|
||||
local orig = PartyMenu.showMessage
|
||||
PartyMenu.showMessage = function(text) shown = text end
|
||||
PartyMenu.enterChosenMon(3)
|
||||
PartyMenu.showMessage = orig
|
||||
check(shown and shown:find("No more than 2", 1, true) ~= nil, "multi party entry text: " .. tostring(shown))
|
||||
|
||||
check(select(1, pcall(RomText.box, "gText_NicknameHatchPrompt", { stringVars = { "TORCHIC" } })), "hatch prompt key exists")
|
||||
local EggHatch = require("src.ui.game3.egg_hatch")
|
||||
check(EggHatch ~= nil, "egg hatch module loads")
|
||||
|
||||
local LB = require("src.core.game3.link.battle")
|
||||
local FieldRse = require("src.core.game3.scripting.natives_field_rse")
|
||||
local Rse = require("src.core.game3.rse.init")
|
||||
Rse.setVar("VAR_LILYCOVE_FAN_CLUB_STATE", 2)
|
||||
local before = FieldRse.numFans()
|
||||
check(pcall(FieldRse.updateTrainerFansAfterLinkBattle, true), "fan club update after a link win runs")
|
||||
check(FieldRse.numFans() >= before, "a link win does not lose fans")
|
||||
check(Flags.ensurePalletOakHidden(Space.store) == nil and Flags.getFlag(Space.store, nil, 0x2C) ~= true,
|
||||
"Pallet Oak hide flag not set on Emerald")
|
||||
local Compat = require("src.mods.Gen3Compat")
|
||||
Flags.setFlag(Space.store, nil, up, true)
|
||||
check(Compat.getFlag("FLAG_SYS_ENC_UP_ITEM") == true, "Gen3Compat resolves Emerald flag names")
|
||||
end
|
||||
|
||||
return function(game)
|
||||
for _ = 1, 900 do
|
||||
if game.phase == "boot" and game.boot then break end
|
||||
U.wait(1)
|
||||
end
|
||||
check(game.boot ~= nil, "boot reached")
|
||||
try("new_game", function() game:_handleBootAction({ action = "new_game", name = "BRENDAN", gender = 0 }) end)
|
||||
U.wait(30)
|
||||
try("body", function() body(game) end)
|
||||
return finish()
|
||||
end
|
||||
@@ -0,0 +1,10 @@
|
||||
local Common = require("tests.drivers.battle_move_swap_common")
|
||||
|
||||
return function(game)
|
||||
Common.run(game, {
|
||||
version = "emerald",
|
||||
tag = "em",
|
||||
dir = "/tmp/em_battle_move_swap",
|
||||
newGame = { action = "new_game", name = "NICK", gender = 0 },
|
||||
})
|
||||
end
|
||||
@@ -0,0 +1,39 @@
|
||||
local U = require("tests.drivers.util")
|
||||
|
||||
local failures = 0
|
||||
local function check(ok, label)
|
||||
print((ok and "PASS " or "FAIL ") .. label)
|
||||
if not ok then failures = failures + 1 end
|
||||
return ok
|
||||
end
|
||||
|
||||
local function finish()
|
||||
print((failures == 0 and "PASS" or "FAIL") .. " em_evolve_ability failures=" .. failures)
|
||||
love.event.quit(failures == 0 and 0 or 1)
|
||||
U.wait(10)
|
||||
end
|
||||
|
||||
return function(game)
|
||||
for _ = 1, 900 do
|
||||
if game.phase == "boot" and game.boot then break end
|
||||
U.wait(1)
|
||||
end
|
||||
game:_handleBootAction({ action = "new_game", name = "BRENDAN", gender = 0 })
|
||||
U.wait(30)
|
||||
local Runtime = require("src.core.game3.runtime")
|
||||
local Party = require("src.core.game3.party")
|
||||
local Evolution = require("src.core.game3.evolution")
|
||||
local Pokemon = require("src.core.game3.pokemon")
|
||||
local C = require("src.core.game3.constants").of("emerald")
|
||||
local session = Runtime.getSession()
|
||||
if not check(session and session.version == "emerald", "emerald session") then return finish() end
|
||||
local TRUANT, VITAL_SPIRIT = 54, 72
|
||||
Party.giveMonToPlayer(session, C:require("species", "SPECIES_SLAKOTH"), 18)
|
||||
local mon = session.party[1]
|
||||
check(Pokemon.abilityName(mon.ability) == "TRUANT", "Slakoth has Truant (" .. Pokemon.abilityName(mon.ability) .. ")")
|
||||
Evolution.apply(mon, C:require("species", "SPECIES_VIGOROTH"), session)
|
||||
check(mon.ability == VITAL_SPIRIT and mon.abilityId == VITAL_SPIRIT, "Vigoroth has Vital Spirit (" .. tostring(mon.ability) .. ")")
|
||||
Evolution.apply(mon, C:require("species", "SPECIES_SLAKING"), session)
|
||||
check(mon.ability == TRUANT, "Slaking has Truant (" .. tostring(mon.ability) .. ")")
|
||||
return finish()
|
||||
end
|
||||
@@ -0,0 +1,70 @@
|
||||
local U = require("tests.drivers.util")
|
||||
local DIR = os.getenv("POKEPORT_SHOT_DIR") or "/tmp/em_itemfinder"
|
||||
|
||||
local failures = 0
|
||||
local function check(ok, label)
|
||||
print((ok and "PASS " or "FAIL ") .. label)
|
||||
if not ok then failures = failures + 1 end
|
||||
return ok
|
||||
end
|
||||
|
||||
local function finish()
|
||||
print((failures == 0 and "PASS" or "FAIL") .. " em_itemfinder failures=" .. failures)
|
||||
love.event.quit(failures == 0 and 0 or 1)
|
||||
U.wait(10)
|
||||
end
|
||||
|
||||
local function try(label, fn)
|
||||
local ok, err = xpcall(fn, debug.traceback)
|
||||
if not ok then
|
||||
print("[driver] " .. label .. " error: " .. tostring(err))
|
||||
failures = failures + 1
|
||||
end
|
||||
return ok
|
||||
end
|
||||
|
||||
local function body(game)
|
||||
local Runtime = require("src.core.game3.runtime")
|
||||
local Field = require("src.core.game3.field")
|
||||
local Itemfinder = require("src.core.game3.itemfinder")
|
||||
local RomText = require("src.core.game3.rom_text")
|
||||
local Message = require("src.ui.game3.message")
|
||||
local session = Runtime.getSession()
|
||||
if not check(session and session.version == "emerald", "emerald session") then return end
|
||||
|
||||
check(Itemfinder.textKey("nothing", session) == "gText_ItemFinderNothing", "nothing key")
|
||||
check(Itemfinder.textKey("nearby", session) == "gText_ItemFinderNearby", "nearby key")
|
||||
check(Itemfinder.textKey("onTop", session) == "gText_ItemFinderOnTop", "on top key")
|
||||
for _, k in ipairs({ "gText_ItemFinderNothing", "gText_ItemFinderNearby", "gText_ItemFinderOnTop" }) do
|
||||
check(select(1, pcall(RomText.box, k)), "text exists " .. k)
|
||||
end
|
||||
|
||||
local ok, kind, text = Field.useItemfinder(session, true)
|
||||
check(ok == false and kind == "itemfinder", "itemfinder with nothing nearby returns no response")
|
||||
check(text and text:find("no response", 1, true) ~= nil, "nothing text: " .. tostring(text))
|
||||
U.wait(40)
|
||||
check(Message.isOpen and Message.isOpen(), "message shown")
|
||||
U.still(game, DIR .. "/nothing.png")
|
||||
Message.close()
|
||||
Field.unlock()
|
||||
|
||||
local sawUnderfoot = false
|
||||
local ok2 = pcall(function()
|
||||
local r = Field.digUpUnderfootItem(Field._game, { flag = 1 })
|
||||
sawUnderfoot = (r == false)
|
||||
end)
|
||||
check(ok2 and sawUnderfoot, "underfoot branch only closes the message on Emerald")
|
||||
Field.unlock()
|
||||
end
|
||||
|
||||
return function(game)
|
||||
for _ = 1, 900 do
|
||||
if game.phase == "boot" and game.boot then break end
|
||||
U.wait(1)
|
||||
end
|
||||
check(game.boot ~= nil, "boot reached")
|
||||
try("new_game", function() game:_handleBootAction({ action = "new_game", name = "BRENDAN", gender = 0 }) end)
|
||||
U.wait(30)
|
||||
try("body", function() body(game) end)
|
||||
return finish()
|
||||
end
|
||||
@@ -0,0 +1,57 @@
|
||||
local U = require("tests.drivers.util")
|
||||
local DIR = os.getenv("POKEPORT_SHOT_DIR") or "/tmp/em_link_trade_menu"
|
||||
|
||||
local failures = 0
|
||||
local function check(ok, label)
|
||||
print((ok and "PASS " or "FAIL ") .. label)
|
||||
if not ok then failures = failures + 1 end
|
||||
return ok
|
||||
end
|
||||
|
||||
local function finish()
|
||||
print((failures == 0 and "PASS" or "FAIL") .. " em_link_trade_menu failures=" .. failures)
|
||||
love.event.quit(failures == 0 and 0 or 1)
|
||||
U.wait(10)
|
||||
end
|
||||
|
||||
local function try(label, fn)
|
||||
local ok, err = xpcall(fn, debug.traceback)
|
||||
if not ok then
|
||||
print("[driver] " .. label .. " error: " .. tostring(err))
|
||||
failures = failures + 1
|
||||
end
|
||||
return ok
|
||||
end
|
||||
|
||||
local function body(game)
|
||||
local Runtime = require("src.core.game3.runtime")
|
||||
local Party = require("src.core.game3.party")
|
||||
local C = require("src.core.game3.constants").of("emerald")
|
||||
local LinkTradeMenu = require("src.ui.game3.link_trade_menu")
|
||||
local session = Runtime.getSession()
|
||||
if not check(session and session.version == "emerald", "emerald session") then return end
|
||||
session.party = {}
|
||||
Party.giveMonToPlayer(session, C:require("species", "SPECIES_TORCHIC"), 10)
|
||||
Party.giveMonToPlayer(session, C:require("species", "SPECIES_MUDKIP"), 10)
|
||||
|
||||
check(pcall(function() LinkTradeMenu.show() end), "link trade menu opens without throwing")
|
||||
check(LinkTradeMenu.open == true, "link trade menu is open")
|
||||
check(LinkTradeMenu.message and LinkTradeMenu.message:find("standby", 1, true) ~= nil, "standby message: " .. tostring(LinkTradeMenu.message))
|
||||
U.wait(20)
|
||||
check(pcall(function() LinkTradeMenu.draw() end), "link trade menu draws")
|
||||
U.still(game, DIR .. "/trade_menu.png")
|
||||
LinkTradeMenu.close()
|
||||
U.wait(10)
|
||||
end
|
||||
|
||||
return function(game)
|
||||
for _ = 1, 900 do
|
||||
if game.phase == "boot" and game.boot then break end
|
||||
U.wait(1)
|
||||
end
|
||||
check(game.boot ~= nil, "boot reached")
|
||||
try("new_game", function() game:_handleBootAction({ action = "new_game", name = "BRENDAN", gender = 0 }) end)
|
||||
U.wait(30)
|
||||
try("body", function() body(game) end)
|
||||
return finish()
|
||||
end
|
||||
@@ -0,0 +1,146 @@
|
||||
local U = require("tests.drivers.util")
|
||||
local DIR = os.getenv("POKEPORT_SHOT_DIR") or "/tmp/em_move_relearner"
|
||||
|
||||
local failures = 0
|
||||
local function check(ok, label)
|
||||
print((ok and "PASS " or "FAIL ") .. label)
|
||||
if not ok then failures = failures + 1 end
|
||||
return ok
|
||||
end
|
||||
|
||||
local function finish()
|
||||
print((failures == 0 and "PASS" or "FAIL") .. " em_move_relearner failures=" .. failures)
|
||||
love.event.quit(failures == 0 and 0 or 1)
|
||||
U.wait(10)
|
||||
end
|
||||
|
||||
local function try(label, fn)
|
||||
local ok, err = xpcall(fn, debug.traceback)
|
||||
if not ok then
|
||||
print("[driver] " .. label .. " error: " .. tostring(err))
|
||||
failures = failures + 1
|
||||
end
|
||||
return ok
|
||||
end
|
||||
|
||||
local function body(game)
|
||||
local Runtime = require("src.core.game3.runtime")
|
||||
local Party = require("src.core.game3.party")
|
||||
local Pokemon = require("src.core.game3.pokemon")
|
||||
local MoveLearn = require("src.core.game3.move_learn")
|
||||
local Stack = require("src.ui.game3.stack")
|
||||
local SummaryMenu = require("src.ui.game3.summary_menu")
|
||||
local MoveTeach = require("src.core.game3.scripting.natives_moveteach")
|
||||
local Rse = require("src.ui.game3.rse.move_relearner")
|
||||
local C = require("src.core.game3.constants").of("emerald")
|
||||
|
||||
local session = Runtime.getSession()
|
||||
if not check(session and session.version == "emerald", "emerald session") then return end
|
||||
|
||||
session.party = {}
|
||||
Party.giveMonToPlayer(session, C:require("species", "SPECIES_TORCHIC"), 40)
|
||||
local mon = session.party[1]
|
||||
local start = {}
|
||||
for i = 1, 4 do start[i] = Pokemon.moveIdAt(mon, i) end
|
||||
print("[driver] start moves: " .. table.concat({ Pokemon.moveName(start[1]), Pokemon.moveName(start[2]),
|
||||
Pokemon.moveName(start[3]), Pokemon.moveName(start[4]) }, ", "))
|
||||
local offers = MoveLearn.relearnableMoves(mon)
|
||||
check(#offers >= 6, "relearnable list scrolls, count=" .. #offers)
|
||||
|
||||
local ctx = { specialVars = { [0x8004] = 0 } }
|
||||
local yielded = MoveTeach.BY_NAME.TeachMoveRelearnerMove(ctx, {})
|
||||
check(yielded == true, "TeachMoveRelearnerMove yields to the host")
|
||||
U.wait(10)
|
||||
local top = Stack.top()
|
||||
check(top and top.id == "move_relearner" and top.mod == Rse, "native opened the Emerald relearner screen")
|
||||
check(Rse.isOpen() and not require("src.ui.game3.move_relearner").isOpen(), "FRLG screen stayed closed")
|
||||
check(Rse.prompt and Rse.prompt:find("Teach which move", 1, true) ~= nil, "prompt: " .. tostring(Rse.prompt))
|
||||
U.still(game, DIR .. "/01_list_battle.png")
|
||||
|
||||
for _ = 1, 6 do U.tap(game, "down"); U.wait(6) end
|
||||
check(Rse.cursor == 7 and Rse.scroll == 1, "cursor on CANCEL, list scrolled (cursor=" .. Rse.cursor .. " scroll=" .. Rse.scroll .. ")")
|
||||
U.still(game, DIR .. "/02_list_scrolled_cancel.png")
|
||||
|
||||
local want = C:require("moves", "MOVE_EMBER")
|
||||
local row
|
||||
for i, id in ipairs(Rse.moves()) do if id == want then row = i end end
|
||||
if not check(row ~= nil, "EMBER is relearnable") then return end
|
||||
for _ = 1, 10 do
|
||||
if Rse.cursor == row then break end
|
||||
U.tap(game, "up"); U.wait(6)
|
||||
end
|
||||
check(Rse.cursor == row, "cursor on EMBER")
|
||||
U.tap(game, "right"); U.wait(8)
|
||||
check(Rse.contest == true, "RIGHT switched to CONTEST MOVES")
|
||||
U.still(game, DIR .. "/03_contest_ember.png")
|
||||
U.tap(game, "left"); U.wait(8)
|
||||
check(Rse.contest == false, "LEFT switched back to BATTLE MOVES")
|
||||
U.still(game, DIR .. "/04_battle_ember.png")
|
||||
|
||||
U.tap(game, "a"); U.wait(10)
|
||||
check(Rse.state == "yesno" and Rse.prompt:find("Teach", 1, true) ~= nil, "teach confirm: " .. tostring(Rse.prompt))
|
||||
U.still(game, DIR .. "/05_teach_confirm.png")
|
||||
U.tap(game, "a"); U.wait(10)
|
||||
check(Rse.state == "message" and Rse.prompt:find("trying to learn", 1, true) ~= nil, "trying-to-learn page 1: " .. tostring(Rse.prompt))
|
||||
U.still(game, DIR .. "/06_trying_to_learn.png")
|
||||
U.tap(game, "a"); U.wait(10)
|
||||
check(Rse.state == "message" and Rse.prompt:find("four moves", 1, true) ~= nil, "page 2: " .. tostring(Rse.prompt))
|
||||
U.tap(game, "a"); U.wait(10)
|
||||
check(Rse.state == "yesno" and Rse.prompt:find("Delete an older move", 1, true) ~= nil, "delete prompt: " .. tostring(Rse.prompt))
|
||||
U.still(game, DIR .. "/07_delete_prompt.png")
|
||||
U.tap(game, "a"); U.wait(10)
|
||||
check(Rse.state == "message" and Rse.prompt:find("forgotten", 1, true) ~= nil and not Rse.prompt:find("\\", 1, true),
|
||||
"which-move prompt: " .. tostring(Rse.prompt))
|
||||
U.still(game, DIR .. "/08_which_move.png")
|
||||
U.tap(game, "a")
|
||||
for _ = 1, 120 do
|
||||
if SummaryMenu.isOpen() then break end
|
||||
U.wait(2)
|
||||
end
|
||||
if not check(SummaryMenu.isOpen() and SummaryMenu._mode == "select_move", "summary opened in select_move") then return end
|
||||
U.wait(60)
|
||||
U.still(game, DIR .. "/09_summary_select.png")
|
||||
U.tap(game, "a")
|
||||
for _ = 1, 120 do
|
||||
if not SummaryMenu.isOpen() then break end
|
||||
U.wait(2)
|
||||
end
|
||||
U.wait(10)
|
||||
check(Rse.state == "message" and Rse.prompt:find("Poof", 1, true) ~= nil, "poof: " .. tostring(Rse.prompt))
|
||||
U.still(game, DIR .. "/10_poof.png")
|
||||
local pages = {}
|
||||
for _ = 1, 8 do
|
||||
if not Rse.isOpen() then break end
|
||||
pages[#pages + 1] = Rse.prompt
|
||||
if #pages == 4 then U.still(game, DIR .. "/11_learned.png") end
|
||||
U.tap(game, "a"); U.wait(10)
|
||||
end
|
||||
print("[driver] pages: " .. table.concat(pages, " | "):gsub("\n", " "))
|
||||
check(pages[2] and pages[2]:find("forgot", 1, true) ~= nil, "forgot page")
|
||||
check(pages[4] and pages[4]:find("learned", 1, true) ~= nil, "learned page")
|
||||
check(not Rse.isOpen(), "relearner closed")
|
||||
check(ctx.specialVars[0x8004] == 1, "VAR_0x8004 = 1 after teach, got " .. tostring(ctx.specialVars[0x8004]))
|
||||
check(Pokemon.knowsMove(mon, want), "mon knows EMBER")
|
||||
check(not Pokemon.knowsMove(mon, start[1]), "mon forgot " .. tostring(Pokemon.moveName(start[1])))
|
||||
|
||||
ctx.specialVars[0x8004] = 0
|
||||
MoveTeach.BY_NAME.TeachMoveRelearnerMove(ctx, {})
|
||||
U.wait(10)
|
||||
U.tap(game, "b"); U.wait(10)
|
||||
check(Rse.state == "yesno" and Rse.prompt:find("Give up", 1, true) ~= nil, "give-up prompt: " .. tostring(Rse.prompt))
|
||||
U.still(game, DIR .. "/12_give_up.png")
|
||||
U.tap(game, "a"); U.wait(10)
|
||||
check(not Rse.isOpen() and ctx.specialVars[0x8004] == 0, "give up closes with VAR_0x8004 = 0")
|
||||
end
|
||||
|
||||
return function(game)
|
||||
for _ = 1, 900 do
|
||||
if game.phase == "boot" and game.boot then break end
|
||||
U.wait(1)
|
||||
end
|
||||
check(game.boot ~= nil, "boot reached")
|
||||
try("new_game", function() game:_handleBootAction({ action = "new_game", name = "BRENDAN", gender = 0 }) end)
|
||||
U.wait(30)
|
||||
try("body", function() body(game) end)
|
||||
return finish()
|
||||
end
|
||||
@@ -0,0 +1,113 @@
|
||||
local U = require("tests.drivers.util")
|
||||
local DIR = os.getenv("POKEPORT_SHOT_DIR") or "/tmp/em_shop_sell"
|
||||
|
||||
local failures = 0
|
||||
local function check(ok, label)
|
||||
print((ok and "PASS " or "FAIL ") .. label)
|
||||
if not ok then failures = failures + 1 end
|
||||
return ok
|
||||
end
|
||||
|
||||
local function finish()
|
||||
print((failures == 0 and "PASS" or "FAIL") .. " em_shop_sell failures=" .. failures)
|
||||
love.event.quit(failures == 0 and 0 or 1)
|
||||
U.wait(10)
|
||||
end
|
||||
|
||||
local function try(label, fn)
|
||||
local ok, err = xpcall(fn, debug.traceback)
|
||||
if not ok then
|
||||
print("[driver] " .. label .. " error: " .. tostring(err))
|
||||
failures = failures + 1
|
||||
end
|
||||
return ok
|
||||
end
|
||||
|
||||
local function body(game)
|
||||
local Runtime = require("src.core.game3.runtime")
|
||||
local Bag = require("src.core.game3.bag")
|
||||
local BagMenu = require("src.ui.game3.bag_menu")
|
||||
local ItemsData = require("src.core.game3.items_data")
|
||||
local C = require("src.core.game3.constants").of("emerald")
|
||||
local session = Runtime.getSession()
|
||||
if not check(session and session.version == "emerald", "emerald session") then return end
|
||||
|
||||
session.bag = session.bag or Bag.new()
|
||||
local POTION = C:require("items", "ITEM_POTION")
|
||||
local BAG_PACK = C:require("items", "ITEM_POKE_BALL")
|
||||
Bag.add(session.bag, POTION, 3)
|
||||
session.money = 1000
|
||||
local Q = require("src.core.game3.quest_log_recorder")
|
||||
check(select(1, pcall(Q.event, session, "SoldItemsIncludingItem", { D0 = "X", D1 = "Y", D2 = 1 })), "quest log event is a no-op on Emerald")
|
||||
|
||||
BagMenu.show(session.bag, { session = session, location = "shop" })
|
||||
U.wait(30)
|
||||
local row
|
||||
for i, r in ipairs(BagMenu.list()) do
|
||||
if tonumber(r.id) == POTION then row = i break end
|
||||
end
|
||||
check(row ~= nil, "potion in the bag")
|
||||
if not row then return end
|
||||
for _ = 1, 20 do
|
||||
if BagMenu.cursor == row then break end
|
||||
U.tap(game, "down")
|
||||
U.wait(6)
|
||||
end
|
||||
U.tap(game, "a")
|
||||
U.wait(20)
|
||||
check(BagMenu.mode == "sell" and BagMenu._sell ~= nil, "sell flow started")
|
||||
local flow = BagMenu._sell
|
||||
if not flow then return end
|
||||
check(flow.state == "qty", "quantity prompt for a stack of 3")
|
||||
check(flow.text:find("How many", 1, true) ~= nil, "how many text: " .. tostring(flow.text))
|
||||
U.wait(10)
|
||||
U.still(game, DIR .. "/qty.png")
|
||||
U.tap(game, "up")
|
||||
U.wait(6)
|
||||
check(flow.qty == 2, "quantity went to 2")
|
||||
U.tap(game, "a")
|
||||
U.wait(10)
|
||||
check(flow.state == "confirm", "confirm prompt")
|
||||
check(flow.text:find("I can pay", 1, true) ~= nil and flow.text:find(tostring(flow.unit * 2), 1, true) ~= nil,
|
||||
"price text: " .. tostring(flow.text))
|
||||
U.still(game, DIR .. "/confirm.png")
|
||||
local before = session.money
|
||||
U.tap(game, "a")
|
||||
U.wait(10)
|
||||
check(flow.state == "done" or flow.state == nil, "sale committed")
|
||||
check(session.money == before + flow.unit * 2, "money increased by " .. tostring(flow.unit * 2))
|
||||
check(Bag.get(session.bag, POTION) == 1, "two potions removed")
|
||||
check(flow.text:find(ItemsData.displayName(POTION), 1, true) ~= nil, "turned over text names the item: " .. tostring(flow.text))
|
||||
U.still(game, DIR .. "/done.png")
|
||||
U.tap(game, "a")
|
||||
U.wait(20)
|
||||
check(BagMenu.mode == "list", "back to the bag list")
|
||||
|
||||
local SellFlow = require("src.ui.game3.sell_flow")
|
||||
local cant = SellFlow.start({ itemId = C:require("items", "ITEM_POKE_FLUTE"), session = session, bag = session.bag, owned = 1 })
|
||||
check(cant.state == "cant" and cant.text:find("can't buy", 1, true) ~= nil, "key item refusal text: " .. tostring(cant.text))
|
||||
check(pcall(function() cant:draw() end), "cant state draws")
|
||||
|
||||
local CoinsBox = require("src.ui.game3.coins_box")
|
||||
CoinsBox.show(1, 1, 1234)
|
||||
check(pcall(function() CoinsBox.draw() end), "coins box draws on Emerald")
|
||||
U.wait(2)
|
||||
U.still(game, DIR .. "/coins.png")
|
||||
CoinsBox.hide()
|
||||
for _ = 1, 6 do
|
||||
U.tap(game, "b")
|
||||
U.wait(15)
|
||||
end
|
||||
end
|
||||
|
||||
return function(game)
|
||||
for _ = 1, 900 do
|
||||
if game.phase == "boot" and game.boot then break end
|
||||
U.wait(1)
|
||||
end
|
||||
check(game.boot ~= nil, "boot reached")
|
||||
try("new_game", function() game:_handleBootAction({ action = "new_game", name = "BRENDAN", gender = 0 }) end)
|
||||
U.wait(30)
|
||||
try("body", function() body(game) end)
|
||||
return finish()
|
||||
end
|
||||
@@ -0,0 +1,67 @@
|
||||
local U = require("tests.drivers.util")
|
||||
local DIR = os.getenv("POKEPORT_SHOT_DIR") or "/tmp/em_summary_info_page"
|
||||
|
||||
local failures = 0
|
||||
local function check(ok, label)
|
||||
print((ok and "PASS " or "FAIL ") .. label)
|
||||
if not ok then failures = failures + 1 end
|
||||
end
|
||||
|
||||
local function finish()
|
||||
print((failures == 0 and "PASS" or "FAIL") .. " em_summary_info_page failures=" .. failures)
|
||||
love.event.quit(failures == 0 and 0 or 1)
|
||||
U.wait(10)
|
||||
end
|
||||
|
||||
local function body(game)
|
||||
local Runtime = require("src.core.game3.runtime")
|
||||
local Party = require("src.core.game3.party")
|
||||
local SummaryMenu = require("src.ui.game3.summary_menu")
|
||||
local C = require("src.core.game3.constants").of("emerald")
|
||||
local session = Runtime.getSession()
|
||||
session.party = {}
|
||||
Party.giveMonToPlayer(session, C:require("species", "SPECIES_TORCHIC"), 9)
|
||||
Party.giveMonToPlayer(session, C:require("species", "SPECIES_TORCHIC"), 12)
|
||||
session.party[2].status = "PSN"
|
||||
U.wait(60)
|
||||
|
||||
local function settle()
|
||||
for _ = 1, 120 do
|
||||
if not (SummaryMenu._slide and SummaryMenu._slide.active) then break end
|
||||
U.wait(1)
|
||||
end
|
||||
U.wait(8)
|
||||
end
|
||||
|
||||
for mi = 1, 2 do
|
||||
SummaryMenu.openMenu(session.party, mi, {})
|
||||
settle()
|
||||
U.still(game, DIR .. "/m" .. mi .. "_p0_info.png")
|
||||
U.tap(game, "right") settle()
|
||||
U.still(game, DIR .. "/m" .. mi .. "_p1_skills.png")
|
||||
U.tap(game, "right") settle()
|
||||
U.still(game, DIR .. "/m" .. mi .. "_p2_moves.png")
|
||||
U.tap(game, "right") settle()
|
||||
U.still(game, DIR .. "/m" .. mi .. "_p3_contest.png")
|
||||
U.tap(game, "a") settle()
|
||||
U.still(game, DIR .. "/m" .. mi .. "_p3_detail.png")
|
||||
U.tap(game, "b") settle()
|
||||
U.tap(game, "left") settle()
|
||||
U.tap(game, "a") settle()
|
||||
U.still(game, DIR .. "/m" .. mi .. "_p2_detail.png")
|
||||
for _ = 1, 4 do U.tap(game, "b") settle() end
|
||||
check(true, "mon " .. mi .. " pages shot")
|
||||
end
|
||||
end
|
||||
|
||||
return function(game)
|
||||
for _ = 1, 900 do
|
||||
if game.phase == "boot" and game.boot then break end
|
||||
U.wait(1)
|
||||
end
|
||||
game:_handleBootAction({ action = "new_game", name = "BRENDAN", gender = 0 })
|
||||
U.wait(30)
|
||||
local ok, err = xpcall(function() body(game) end, debug.traceback)
|
||||
if not ok then print("[driver] error " .. tostring(err)) failures = failures + 1 end
|
||||
return finish()
|
||||
end
|
||||
@@ -0,0 +1,178 @@
|
||||
local U = require("tests.drivers.util")
|
||||
local DIR = os.getenv("POKEPORT_SHOT_DIR") or "/tmp/em_teleport_vitamin"
|
||||
|
||||
local failures = 0
|
||||
local function check(ok, label)
|
||||
print((ok and "PASS " or "FAIL ") .. label)
|
||||
if not ok then failures = failures + 1 end
|
||||
return ok
|
||||
end
|
||||
|
||||
local function finish()
|
||||
print((failures == 0 and "PASS" or "FAIL") .. " em_teleport_vitamin failures=" .. failures)
|
||||
love.event.quit(failures == 0 and 0 or 1)
|
||||
U.wait(10)
|
||||
end
|
||||
|
||||
local function try(label, fn)
|
||||
local ok, err = xpcall(fn, debug.traceback)
|
||||
if not ok then
|
||||
print("[driver] " .. label .. " error: " .. tostring(err))
|
||||
failures = failures + 1
|
||||
end
|
||||
return ok
|
||||
end
|
||||
|
||||
local function body(game)
|
||||
local Runtime = require("src.core.game3.runtime")
|
||||
local Map = require("src.core.game3.map")
|
||||
local Player = require("src.core.game3.player")
|
||||
local Party = require("src.core.game3.party")
|
||||
local Bag = require("src.core.game3.bag")
|
||||
local Pokemon = require("src.core.game3.pokemon")
|
||||
local PartyMenu = require("src.ui.game3.party_menu")
|
||||
local StartMenu = require("src.ui.game3.start_menu")
|
||||
local BagMenu = require("src.ui.game3.bag_menu")
|
||||
local Field = require("src.core.game3.field")
|
||||
local FieldEffects = require("src.core.game3.field_effects")
|
||||
local C = require("src.core.game3.constants").of("emerald")
|
||||
|
||||
local session = Runtime.getSession()
|
||||
if not check(session and session.version == "emerald", "emerald session") then return end
|
||||
|
||||
local Flags = require("src.core.game3.scripting.flags")
|
||||
local Space = require("src.core.game3.scripting.space")
|
||||
Flags.setFlag(Space.store, nil, C:require("flags", "FLAG_SYS_POKEMON_GET"), true)
|
||||
print("[driver] fresh healMap=" .. tostring(session.healMap) .. " map=" .. tostring(session.map))
|
||||
|
||||
session.party = {}
|
||||
Party.giveMonToPlayer(session, C:require("species", "SPECIES_TORCHIC"), 30)
|
||||
local mon = session.party[1]
|
||||
mon.moves[1] = C:require("moves", "MOVE_TELEPORT")
|
||||
session.bag = session.bag or Bag.new()
|
||||
local ITEM_HP_UP = C:require("items", "ITEM_HP_UP")
|
||||
Bag.add(session.bag, ITEM_HP_UP, 3)
|
||||
|
||||
Map.load(nil, game, "EM_ROUTE101", { x = 10, y = 12, facing = "down" })
|
||||
session.x, session.y, session.facing = 10, 12, "down"
|
||||
Player.cellX, Player.cellY = 10, 12
|
||||
Player.px, Player.py = 160, 192
|
||||
Player.targetX, Player.targetY = 10, 12
|
||||
Player.facing = "down"
|
||||
U.wait(90)
|
||||
local startMap = session.map
|
||||
|
||||
local function openStart(entryId)
|
||||
for _ = 1, 40 do
|
||||
if StartMenu.isOpen and StartMenu.isOpen() then break end
|
||||
U.tap(game, "start")
|
||||
U.wait(8)
|
||||
end
|
||||
for _ = 1, 20 do
|
||||
local e = StartMenu.ENTRIES and StartMenu.ENTRIES[StartMenu.cursor]
|
||||
if e and e.id == entryId then break end
|
||||
U.tap(game, "down")
|
||||
U.wait(6)
|
||||
end
|
||||
U.tap(game, "a")
|
||||
U.wait(40)
|
||||
end
|
||||
|
||||
local function teleportStage()
|
||||
U.tap(game, "start")
|
||||
U.wait(30)
|
||||
local ids = {}
|
||||
for _, e in ipairs(StartMenu.ENTRIES or {}) do ids[#ids + 1] = tostring(e.id) end
|
||||
print("[driver] start entries: " .. table.concat(ids, ","))
|
||||
U.tap(game, "b")
|
||||
U.wait(20)
|
||||
openStart("pokemon")
|
||||
check(PartyMenu.isOpen and PartyMenu.isOpen(), "party menu opens")
|
||||
U.tap(game, "a")
|
||||
U.wait(12)
|
||||
local target
|
||||
for i, name in ipairs(PartyMenu.ACTIONS or {}) do
|
||||
if name == "TELEPORT" then target = i end
|
||||
end
|
||||
check(target ~= nil, "TELEPORT is on the action list")
|
||||
if not target then return end
|
||||
for _ = 1, 12 do
|
||||
if PartyMenu.actionCursor == target then break end
|
||||
U.tap(game, "down")
|
||||
U.wait(6)
|
||||
end
|
||||
U.tap(game, "a")
|
||||
U.wait(20)
|
||||
U.tap(game, "a")
|
||||
U.wait(6)
|
||||
|
||||
local sawOut, sawIn = false, false
|
||||
for _ = 1, 1500 do
|
||||
for _, a in ipairs(FieldEffects._anims or {}) do
|
||||
if a.kind == "teleport_out" then sawOut = true end
|
||||
if a.kind == "teleport_in" then sawIn = true end
|
||||
end
|
||||
if sawIn and not Field.locked then break end
|
||||
U.wait(1)
|
||||
end
|
||||
check(sawOut, "teleport_out ran")
|
||||
check(sawIn, "teleport_in ran")
|
||||
check(Field.locked == false, "field unlocked after teleport")
|
||||
print("[driver] teleport " .. tostring(startMap) .. " -> " .. tostring(session.map))
|
||||
check(session.map ~= nil and session.map ~= startMap, "landed on a different map")
|
||||
U.wait(30)
|
||||
U.still(game, DIR .. "/after_teleport.png")
|
||||
end
|
||||
if not os.getenv("EM_TV_SKIP_TELEPORT") then teleportStage() end
|
||||
|
||||
local beforeHp = mon.maxHp
|
||||
local beforeEv = Pokemon.evCount(mon)
|
||||
openStart("bag")
|
||||
check(BagMenu.isOpen(), "bag opens")
|
||||
local row
|
||||
for i, r in ipairs(BagMenu.list()) do
|
||||
if tonumber(r.id) == ITEM_HP_UP then row = i break end
|
||||
end
|
||||
check(row ~= nil, "HP UP in the bag")
|
||||
if not row then return end
|
||||
for _ = 1, 30 do
|
||||
if BagMenu.cursor == row then break end
|
||||
U.tap(game, "down")
|
||||
U.wait(6)
|
||||
end
|
||||
U.tap(game, "a")
|
||||
U.wait(30)
|
||||
for _ = 1, 10 do
|
||||
if BagMenu.ACTIONS[BagMenu.actionCursor] == "USE" then break end
|
||||
U.tap(game, "down")
|
||||
U.wait(6)
|
||||
end
|
||||
U.tap(game, "a")
|
||||
U.wait(60)
|
||||
check(PartyMenu.open and PartyMenu.mode == "use", "party menu opened for HP UP")
|
||||
U.tap(game, "a")
|
||||
U.wait(60)
|
||||
U.tap(game, "a")
|
||||
U.wait(30)
|
||||
check(Pokemon.evCount(mon) == beforeEv + 10, "HP UP added 10 EVs (" .. tostring(Pokemon.evCount(mon)) .. ")")
|
||||
check(mon.maxHp >= beforeHp, "max HP did not drop")
|
||||
for _ = 1, 6 do
|
||||
U.tap(game, "b")
|
||||
U.wait(20)
|
||||
end
|
||||
U.wait(30)
|
||||
check(Field.locked ~= true, "field not locked after closing menus")
|
||||
U.still(game, DIR .. "/after_hpup.png")
|
||||
end
|
||||
|
||||
return function(game)
|
||||
for _ = 1, 900 do
|
||||
if game.phase == "boot" and game.boot then break end
|
||||
U.wait(1)
|
||||
end
|
||||
check(game.boot ~= nil, "boot reached")
|
||||
try("new_game", function() game:_handleBootAction({ action = "new_game", name = "BRENDAN", gender = 0 }) end)
|
||||
U.wait(30)
|
||||
try("body", function() body(game) end)
|
||||
return finish()
|
||||
end
|
||||
@@ -0,0 +1,10 @@
|
||||
local Common = require("tests.drivers.battle_move_swap_common")
|
||||
|
||||
return function(game)
|
||||
Common.run(game, {
|
||||
version = "firered",
|
||||
tag = "frlg",
|
||||
dir = "/tmp/game3_battle_move_swap",
|
||||
newGame = { action = "new_game", name = "RED" },
|
||||
})
|
||||
end
|
||||
@@ -0,0 +1,112 @@
|
||||
local U = require("tests.drivers.util")
|
||||
local DIR = os.getenv("POKEPORT_SHOT_DIR") or "/tmp/game3_sell_flow_frlg"
|
||||
|
||||
local failures = 0
|
||||
local function check(ok, label)
|
||||
print((ok and "PASS " or "FAIL ") .. label)
|
||||
if not ok then failures = failures + 1 end
|
||||
return ok
|
||||
end
|
||||
|
||||
local function finish()
|
||||
print((failures == 0 and "PASS" or "FAIL") .. " game3_sell_flow_frlg failures=" .. failures)
|
||||
love.event.quit(failures == 0 and 0 or 1)
|
||||
U.wait(10)
|
||||
end
|
||||
|
||||
local function try(label, fn)
|
||||
local ok, err = xpcall(fn, debug.traceback)
|
||||
if not ok then
|
||||
print("[driver] " .. label .. " error: " .. tostring(err))
|
||||
failures = failures + 1
|
||||
end
|
||||
return ok
|
||||
end
|
||||
|
||||
local function body(game)
|
||||
local Runtime = require("src.core.game3.runtime")
|
||||
local Bag = require("src.core.game3.bag")
|
||||
local BagMenu = require("src.ui.game3.bag_menu")
|
||||
local ItemsData = require("src.core.game3.items_data")
|
||||
local C = require("src.core.game3.constants").of("firered")
|
||||
local session = Runtime.getSession()
|
||||
if not check(session and session.version == "firered", "firered session") then return end
|
||||
|
||||
session.bag = session.bag or Bag.new()
|
||||
local POTION = C:require("items", "ITEM_POTION")
|
||||
local BAG_PACK = C:require("items", "ITEM_POKE_BALL")
|
||||
Bag.add(session.bag, POTION, 3)
|
||||
session.money = 1000
|
||||
local Q = require("src.core.game3.quest_log_recorder")
|
||||
|
||||
BagMenu.show(session.bag, { session = session, location = "shop" })
|
||||
U.wait(30)
|
||||
local row
|
||||
for i, r in ipairs(BagMenu.list()) do
|
||||
if tonumber(r.id) == POTION then row = i break end
|
||||
end
|
||||
check(row ~= nil, "potion in the bag")
|
||||
if not row then return end
|
||||
for _ = 1, 20 do
|
||||
if BagMenu.cursor == row then break end
|
||||
U.tap(game, "down")
|
||||
U.wait(6)
|
||||
end
|
||||
U.tap(game, "a")
|
||||
U.wait(20)
|
||||
check(BagMenu.mode == "sell" and BagMenu._sell ~= nil, "sell flow started")
|
||||
local flow = BagMenu._sell
|
||||
if not flow then return end
|
||||
check(flow.state == "qty", "quantity prompt for a stack of 3")
|
||||
check(flow.text:find("How many would you like to sell", 1, true) ~= nil, "how many text: " .. tostring(flow.text))
|
||||
U.wait(10)
|
||||
U.still(game, DIR .. "/qty.png")
|
||||
U.tap(game, "up")
|
||||
U.wait(6)
|
||||
check(flow.qty == 2, "quantity went to 2")
|
||||
U.tap(game, "a")
|
||||
U.wait(10)
|
||||
check(flow.state == "confirm", "confirm prompt")
|
||||
check(flow.text:find(tostring(flow.unit * 2), 1, true) ~= nil,
|
||||
"price text: " .. tostring(flow.text))
|
||||
U.still(game, DIR .. "/confirm.png")
|
||||
local before = session.money
|
||||
U.tap(game, "a")
|
||||
U.wait(10)
|
||||
check(flow.state == "done" or flow.state == nil, "sale committed")
|
||||
check(session.money == before + flow.unit * 2, "money increased by " .. tostring(flow.unit * 2))
|
||||
check(Bag.get(session.bag, POTION) == 1, "two potions removed")
|
||||
check(flow.text:find(ItemsData.displayName(POTION), 1, true) ~= nil, "turned over text names the item: " .. tostring(flow.text))
|
||||
U.still(game, DIR .. "/done.png")
|
||||
U.tap(game, "a")
|
||||
U.wait(20)
|
||||
check(BagMenu.mode == "list", "back to the bag list")
|
||||
|
||||
local SellFlow = require("src.ui.game3.sell_flow")
|
||||
local cant = SellFlow.start({ itemId = C:require("items", "ITEM_POKE_FLUTE"), session = session, bag = session.bag, owned = 1 })
|
||||
check(cant.state == "cant" and cant.state == "cant", "key item refusal text: " .. tostring(cant.text))
|
||||
check(pcall(function() cant:draw() end), "cant state draws")
|
||||
|
||||
local CoinsBox = require("src.ui.game3.coins_box")
|
||||
CoinsBox.show(1, 1, 1234)
|
||||
check(pcall(function() CoinsBox.draw() end), "coins box draws on FireRed")
|
||||
U.wait(2)
|
||||
U.still(game, DIR .. "/coins.png")
|
||||
CoinsBox.hide()
|
||||
for _ = 1, 6 do
|
||||
U.tap(game, "b")
|
||||
U.wait(15)
|
||||
end
|
||||
end
|
||||
|
||||
return function(game)
|
||||
for _ = 1, 900 do
|
||||
if game.phase == "boot" and game.boot then break end
|
||||
U.wait(1)
|
||||
end
|
||||
check(game.boot ~= nil, "boot reached")
|
||||
try("new_game", function() game:_handleBootAction({ action = "new_game", name = "RED" }) end)
|
||||
U.wait(30)
|
||||
try("body", function() body(game) end)
|
||||
return finish()
|
||||
end
|
||||
@@ -0,0 +1,65 @@
|
||||
package.path = "./?.lua;./?/init.lua;" .. package.path
|
||||
|
||||
local T = require("tests.harness")
|
||||
local check, eq = T.check, T.eq
|
||||
|
||||
local Plans = require("src.import.gba.plans.registry")
|
||||
local Extract = require("src.import.gba.move_relearner_rse_extract")
|
||||
local Rse = require("src.ui.game3.rse.move_relearner")
|
||||
local ui = require("src.core.game3.profiles.emerald.ui")
|
||||
|
||||
local required = {}
|
||||
for _, p in ipairs(Plans.required(Plans.of("emerald"), "data/generated/gba")) do required[p] = true end
|
||||
check(required["data/generated/gba/rse/move_relearner/hearts.png"], "emerald plan requires the relearner hearts")
|
||||
check(required["data/generated/gba/rse/move_relearner/manifest.lua"], "emerald plan requires the relearner manifest")
|
||||
local frRequired = {}
|
||||
for _, p in ipairs(Plans.required(Plans.of("firered"), "data/generated/gba")) do frRequired[p] = true end
|
||||
check(not frRequired["data/generated/gba/rse/move_relearner/hearts.png"], "firered plan does not pull the Emerald relearner")
|
||||
|
||||
local aliases = ui.textAliases
|
||||
eq(aliases.gText_MonIsTryingToLearnMove, "gText_MoveRelearnerPkmnTryingToLearnMove", "trying-to-learn alias")
|
||||
eq(aliases.gText_WhichMoveShouldBeForgotten, "gText_MoveRelearnerWhichMoveToForget", "which-move alias")
|
||||
eq(aliases.gText_MonForgotOldMoveAndMonLearnedNewMove, "gText_MoveRelearnerPkmnForgotMoveAndLearnedNew", "forgot alias")
|
||||
|
||||
local a, j = Rse.heartCounts({ appeal = 40, jam = 30 })
|
||||
eq(a, 4, "appeal 40 -> 4 hearts")
|
||||
eq(j, 3, "jam 30 -> 3 hearts")
|
||||
a, j = Rse.heartCounts({ appeal = 0xFF, jam = 0xFF })
|
||||
eq(a, 0, "appeal 0xFF -> 0 hearts")
|
||||
eq(j, 0, "jam 0xFF -> 0 hearts")
|
||||
eq(Rse.WIN.list.left, 19, "move list window left")
|
||||
eq(Rse.WIN.msg.width, 22, "message window width")
|
||||
|
||||
local ROM_PATH = os.getenv("POKEPORT_EMERALD_ROM") or "../pokeemerald/pokeemerald.gba"
|
||||
local f = io.open(ROM_PATH, "rb")
|
||||
local data = f and f:read("*a")
|
||||
if f then f:close() end
|
||||
if data and data:sub(0xAD, 0xB0) == "BPEE" then
|
||||
local GV = require("src.core.GameVersion")
|
||||
local Versions = require("src.import.gba.versions")
|
||||
local Rom = require("src.import.gba.rom")
|
||||
local K = require("src.import.gba.rse.boot_gfx")
|
||||
GV.set("emerald")
|
||||
Versions.select("emerald")
|
||||
local sha = GV.VERSIONS.emerald.sha1
|
||||
local rom = assert(Rom.open({
|
||||
info = function() return { size = #data, md5 = sha } end,
|
||||
read = function(_, _, off, len) return data:sub(off + 1, off + len) end,
|
||||
}, GV.forSha1(sha)))
|
||||
local files = {}
|
||||
local cache = {
|
||||
write = function(_, rel, bytes) files[rel] = bytes; return true end,
|
||||
read = function(_, rel) return files[rel] end,
|
||||
}
|
||||
local ok, m = Extract.run(rom, cache, { game = "emerald" })
|
||||
check(ok and m and m.hearts, "extractor ran")
|
||||
local w, h = K.pngSize(files["data/generated/gba/rse/move_relearner/hearts.png"])
|
||||
eq(w, 8, "hearts sheet width")
|
||||
eq(h, 32, "hearts sheet height")
|
||||
eq(m.hearts.names.jamFull, 3, "jam full frame")
|
||||
check(Extract.ready(cache), "extractor ready after run")
|
||||
else
|
||||
print("emerald_move_relearner_test: ROM extract checks skipped (no Emerald ROM at " .. ROM_PATH .. ")")
|
||||
end
|
||||
|
||||
T.finish()
|
||||
@@ -0,0 +1,73 @@
|
||||
package.path = "./?.lua;./?/init.lua;" .. package.path
|
||||
|
||||
local T = require("tests.harness")
|
||||
local eq = T.eq
|
||||
love = love or require("tests.love_stub")
|
||||
|
||||
local MoveSwap = require("src.core.game3.battle.move_swap")
|
||||
local State = require("src.core.game3.battle.state")
|
||||
|
||||
local function mon()
|
||||
return {
|
||||
moves = { 10, 20, 30, 40 }, pp = { 1, 2, 3, 4 }, maxPp = { 11, 12, 13, 14 },
|
||||
ppBonusesPacked = 1 + 2 * 4 + 3 * 16,
|
||||
}
|
||||
end
|
||||
|
||||
local function list(t) return table.concat(t, ",") end
|
||||
|
||||
eq(MoveSwap.canStart({}, { mon = mon() }), true, "four moves can start")
|
||||
eq(MoveSwap.canStart({ link = true }, { mon = mon() }), false, "link battle blocks swap")
|
||||
eq(MoveSwap.canStart({}, { mon = { moves = { 10, 0, 0, 0 } } }), false, "one move blocks swap")
|
||||
eq(MoveSwap.canStart({}, nil), false, "no battler blocks swap")
|
||||
|
||||
eq(MoveSwap.initialCursor(0), 1, "cursor 0 starts on 1")
|
||||
eq(MoveSwap.initialCursor(3), 0, "other cursor starts on 0")
|
||||
|
||||
eq(MoveSwap.step(1, "left", 4), 0, "left")
|
||||
eq(MoveSwap.step(0, "left", 4), 0, "left edge")
|
||||
eq(MoveSwap.step(0, "right", 1), 0, "right clamps to move count")
|
||||
eq(MoveSwap.step(0, "right", 4), 1, "right")
|
||||
eq(MoveSwap.step(2, "up", 4), 0, "up")
|
||||
eq(MoveSwap.step(0, "down", 2), 0, "down clamps to move count")
|
||||
eq(MoveSwap.step(1, "down", 4), 3, "down")
|
||||
|
||||
local plain = { mon = mon() }
|
||||
eq(MoveSwap.apply(plain, 1, 3), true, "apply returns true")
|
||||
eq(list(plain.mon.moves), "30,20,10,40", "moves swapped")
|
||||
eq(list(plain.mon.pp), "3,2,1,4", "pp swapped")
|
||||
eq(list(plain.mon.maxPp), "13,12,11,14", "maxPp swapped")
|
||||
eq(plain.mon.ppBonusesPacked, 3 + 2 * 4 + 1 * 16, "pp bonuses swapped")
|
||||
eq(MoveSwap.apply(plain, 2, 2), false, "same slot is a no-op")
|
||||
|
||||
local party = mon()
|
||||
local b = { mon = party, expLockedSlot = 1, expEncoreSlot = 3 }
|
||||
State.ensureBattleMoves(b)
|
||||
b.permanentSlots[1] = false
|
||||
b.sketched = { [3] = true }
|
||||
local proxy = b.mon
|
||||
proxy.moves[1] = 99
|
||||
proxy.pp[1] = 5
|
||||
MoveSwap.apply(b, 1, 3)
|
||||
eq(list(proxy.moves), "30,20,99,40", "proxy moves swapped")
|
||||
eq(list(proxy.pp), "3,2,5,4", "proxy pp swapped")
|
||||
eq(list(party.moves), "30,20,10,40", "party moves swapped with proxy")
|
||||
eq(list(party.pp), "3,2,1,4", "party pp swapped with proxy")
|
||||
eq(list(party.maxPp), "13,12,11,14", "party maxPp swapped once")
|
||||
eq(party.ppBonusesPacked, 3 + 2 * 4 + 1 * 16, "party bonuses swapped once")
|
||||
eq(b.permanentSlots[3], false, "mimicked flag follows the move")
|
||||
eq(b.permanentSlots[1], true, "swapped slot keeps permanence")
|
||||
eq(b.sketched[1], true, "sketched flag follows the move")
|
||||
eq(b.expLockedSlot, 3, "locked slot follows the move")
|
||||
eq(b.expEncoreSlot, 1, "encore slot follows the move")
|
||||
|
||||
local party2 = mon()
|
||||
local t = { mon = party2, transformed = true }
|
||||
State.ensureBattleMoves(t)
|
||||
t.mon.moves = { 1, 2, 3, 4 }
|
||||
t.mon.pp = { 5, 5, 5, 5 }
|
||||
MoveSwap.apply(t, 1, 2)
|
||||
eq(list(t.mon.moves), "2,1,3,4", "transformed battler moves swapped")
|
||||
eq(list(party2.moves), "10,20,30,40", "transformed swap leaves party alone")
|
||||
|
||||
T.finish("game3_battle_move_swap_test")
|
||||
@@ -0,0 +1,24 @@
|
||||
package.path = "./?.lua;./?/init.lua;" .. package.path
|
||||
|
||||
local T = require("tests.harness")
|
||||
local eq = T.eq
|
||||
love = love or require("tests.love_stub")
|
||||
|
||||
local Itemfinder = require("src.core.game3.itemfinder")
|
||||
local Flags = require("src.core.game3.scripting.flags")
|
||||
|
||||
local rse = { version = "emerald" }
|
||||
local frlg = { version = "firered" }
|
||||
|
||||
eq(Itemfinder.textKey("nothing", rse), "gText_ItemFinderNothing", "Emerald itemfinder nothing key")
|
||||
eq(Itemfinder.textKey("nearby", rse), "gText_ItemFinderNearby", "Emerald itemfinder nearby key")
|
||||
eq(Itemfinder.textKey("onTop", rse), "gText_ItemFinderOnTop", "Emerald itemfinder on top key")
|
||||
eq(Itemfinder.textKey("nothing", frlg), "gText_NopeTheresNoResponse", "FireRed itemfinder nothing key")
|
||||
eq(Itemfinder.textKey("nearby", frlg), "gText_ItemfinderResponding", "FireRed itemfinder nearby key")
|
||||
eq(Itemfinder.textKey("onTop", frlg), "gText_ItemfinderShakingWildly", "FireRed itemfinder on top key")
|
||||
|
||||
local em = Flags.forVersion("emerald").IDS
|
||||
eq(em.FLAG_SYS_ENC_UP_ITEM, 2221, "Emerald white flute flag")
|
||||
eq(em.FLAG_SYS_ENC_DOWN_ITEM, 2222, "Emerald black flute flag")
|
||||
|
||||
T.finish("game3_emerald_shared_keys_test")
|
||||
Reference in New Issue
Block a user