mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-28 18:06:09 +02:00
+126
-53
@@ -1008,9 +1008,23 @@ end
|
||||
-- flickers the OBJ palette, DoBallTossSpecialEffects)
|
||||
function BattleState:animNext(name, isPlayer, shakes, ball)
|
||||
self.nextInsert = (self.nextInsert or 0) + 1
|
||||
table.insert(self.queue, self.nextInsert,
|
||||
{ anim = name, attackerIsPlayer = isPlayer, shakes = shakes,
|
||||
ball = ball })
|
||||
local row = { anim = name, attackerIsPlayer = isPlayer, shakes = shakes,
|
||||
ball = ball }
|
||||
table.insert(self.queue, self.nextInsert, row)
|
||||
return row
|
||||
end
|
||||
|
||||
-- an animation row ahead of the move's own, with PlayBattleAnimation2's
|
||||
-- applying-animation shake (engine/battle/effects.asm:1461-1471)
|
||||
function BattleState:animBeforeMove(name, isPlayer)
|
||||
local at
|
||||
for i, item in ipairs(self.queue) do
|
||||
if item == self.moveAnimRow then at = i break end
|
||||
end
|
||||
self.nextInsert = (self.nextInsert or 0) + 1
|
||||
table.insert(self.queue, at or self.nextInsert,
|
||||
{ anim = name, attackerIsPlayer = isPlayer, animDelayed = true,
|
||||
hit = { animType = isPlayer and 6 or 3 } })
|
||||
end
|
||||
|
||||
-- insert an act right after the current queue item
|
||||
@@ -1554,8 +1568,13 @@ end
|
||||
function BattleState:sendOutText(name)
|
||||
local e = self.enemy and self.enemy.mon
|
||||
local pct = 100
|
||||
if e and e.hp > 0 and math.floor(e.stats.hp / 4) > 0 then
|
||||
pct = math.floor(e.hp * 25 / math.floor(e.stats.hp / 4))
|
||||
if e and e.hp > 0 then
|
||||
-- the same routine stamps wLastSwitchInEnemyMonHP
|
||||
-- (engine/battle/common_text.asm:105-110)
|
||||
self.lastSwitchInEnemyHP = e.hp
|
||||
if math.floor(e.stats.hp / 4) > 0 then
|
||||
pct = math.floor(e.hp * 25 / math.floor(e.stats.hp / 4))
|
||||
end
|
||||
end
|
||||
if pct >= 70 then return Strings("Go! %s!", name) end
|
||||
if pct >= 40 then return Strings("Do it! %s!", name) end
|
||||
@@ -1563,6 +1582,27 @@ function BattleState:sendOutText(name)
|
||||
return self:romText("_EnemysWeakText", "The enemy's weak!\nGet'm! %s!", name)
|
||||
end
|
||||
|
||||
-- RetreatMon / PlayerMon2Text (engine/battle/common_text.asm:167-243): the
|
||||
-- adjective reads the enemy HP lost since this mon switched in
|
||||
function BattleState:withdrawText(name)
|
||||
local e = self.enemy and self.enemy.mon
|
||||
local drop = 0
|
||||
if e and self.lastSwitchInEnemyHP and math.floor(e.stats.hp / 4) > 0 then
|
||||
drop = math.floor((self.lastSwitchInEnemyHP - e.hp) * 25
|
||||
/ math.floor(e.stats.hp / 4))
|
||||
end
|
||||
local word = ""
|
||||
if drop <= 0 then
|
||||
word = self:romText("_EnoughText", "enough!")
|
||||
elseif drop >= 70 then
|
||||
word = self:romText("_GoodText", "good!")
|
||||
elseif drop >= 30 then
|
||||
word = self:romText("_OKExclamationText", "OK!")
|
||||
end
|
||||
return self:romText("_PlayerMon2Text", "%s ", name) .. word
|
||||
.. self:romText("_ComeBackText", "\nCome back!")
|
||||
end
|
||||
|
||||
-- The cry a mon makes as it takes the field. Yellow does not run its
|
||||
-- starter Pikachu through PlayCry at all: SendOutMon branches to
|
||||
-- .starterPikachu (engine/battle/core.asm:1807-1817) and voices PCM
|
||||
@@ -1816,7 +1856,8 @@ function BattleState:enter()
|
||||
self.enemySendingOut = true
|
||||
self:slidePic("foe")
|
||||
end)
|
||||
self:say(Strings("%s sent\nout %s!", foeName, self.enemy.name))
|
||||
-- _TrainerSentOutText ends `done`, not `prompt` (data/text/text_2.asm:923)
|
||||
self:sayAuto(Strings("%s sent\nout %s!", foeName, self.enemy.name))
|
||||
self:act(function()
|
||||
-- EnemySendOutFirstMon (core.asm:1421-1434): after the text the
|
||||
-- pic grows out of the ball (AnimateSendingOutMon), then the cry
|
||||
@@ -1845,7 +1886,8 @@ function BattleState:enter()
|
||||
self.sendingOut = true
|
||||
self:slidePic("back")
|
||||
end)
|
||||
self:say(self:sendOutText(self.player.name))
|
||||
-- _GoText.._PlayerMon1Text carry no prompt (data/text/text_2.asm:1274-1294)
|
||||
self:sayAuto(self:sendOutText(self.player.name))
|
||||
-- then the POOF plays and the mon appears with its cry
|
||||
-- (SendOutMon: message -> AnimateSendingOutMon -> PlayCry)
|
||||
self:queueSendOutAnim(true)
|
||||
@@ -2659,22 +2701,28 @@ function BattleState:resolveSwitch(newMon)
|
||||
self.phase = "messages"
|
||||
self.afterQueue = "menu"
|
||||
self:act(function()
|
||||
self:restoreMimicked(self.player) -- the battle copy leaves with it
|
||||
local previous = self.player
|
||||
self.player = makeBattler(self.data, newMon, true, self.game.save)
|
||||
-- SendOutMon (core.asm:1761-1762): player's send-out clears the
|
||||
-- foe's USING_TRAPPING_MOVE -- Wrap/Bind/etc. ends on any switch
|
||||
clearTrapping(self.enemy)
|
||||
self:syncSides()
|
||||
Runtime.emit("battle.battler_switched", {
|
||||
battle = self, side = self.sides[1], battler = self.player,
|
||||
previous = previous,
|
||||
})
|
||||
self:markParticipant()
|
||||
sendOutMonCursors(self)
|
||||
self.sendingOut = true
|
||||
self:sayNext(self:sendOutText(self.player.name))
|
||||
self:queueSendOutAnim(false)
|
||||
-- SwitchPlayerMon (core.asm:2419-2423): RetreatMon prints over the
|
||||
-- outgoing pic and holds 50 frames before the mon is recalled
|
||||
self:sayNextAuto(self:withdrawText(self.player.name),
|
||||
Timing.SWITCH_PLAYER_MON)
|
||||
self:actNext(function()
|
||||
self:restoreMimicked(self.player) -- the battle copy leaves with it
|
||||
local previous = self.player
|
||||
self.player = makeBattler(self.data, newMon, true, self.game.save)
|
||||
-- SendOutMon (core.asm:1761-1762): player's send-out clears the
|
||||
-- foe's USING_TRAPPING_MOVE -- Wrap/Bind/etc. ends on any switch
|
||||
clearTrapping(self.enemy)
|
||||
self:syncSides()
|
||||
Runtime.emit("battle.battler_switched", {
|
||||
battle = self, side = self.sides[1], battler = self.player,
|
||||
previous = previous,
|
||||
})
|
||||
self:markParticipant()
|
||||
sendOutMonCursors(self)
|
||||
self.sendingOut = true
|
||||
self:sayNextAuto(self:sendOutText(self.player.name))
|
||||
self:queueSendOutAnim(false)
|
||||
end)
|
||||
end)
|
||||
self:act(function()
|
||||
self:executeAction(self.enemy, self.player, self:enemyAction())
|
||||
@@ -2703,7 +2751,12 @@ function BattleState:residualFor(b, opp)
|
||||
if b.residualDone then return end
|
||||
b.residualDone = true
|
||||
local msgs = Status.residual(b, opp, self)
|
||||
local rec = Status.recordFor(self.data and self.data.statuses, b.mon.status)
|
||||
for _, m in ipairs(msgs) do self:sayNext(prefixEnemy(m, b)) end
|
||||
-- engine/battle/core.asm:490-493
|
||||
if rec and rec.residual then
|
||||
self:animNext("BURN_PSN_ANIM", b.isPlayer)
|
||||
end
|
||||
if b.leechSeeded and b.mon.hp > 0 then
|
||||
-- the drain plays the ABSORB animation from the healing side
|
||||
-- (core.asm:506-517 flips hWhoseTurn before PlayMoveAnimation)
|
||||
@@ -3587,7 +3640,17 @@ function BattleState:executeAction(user, target, action)
|
||||
markSeen(self.game, self.enemy.mon.species)
|
||||
-- _AIBattleWithdrawText: "X with-/drew Y!"
|
||||
self:sayNext(Strings("%s with-\ndrew %s!", self.trainer.name, oldName))
|
||||
self:sayNext(Strings("%s sent\nout %s!", self.trainer.name, self.enemy.name))
|
||||
-- EnemySendOut falls into EnemySendOutFirstMon: TrainerSentOutText,
|
||||
-- then AnimateSendingOutMon and PlayCry (core.asm:1276-1434)
|
||||
self.enemySendingOut = true
|
||||
self:sayNextAuto(Strings("%s sent\nout %s!", self.trainer.name, self.enemy.name))
|
||||
self:actNext(function()
|
||||
self.enemySendingOut = false
|
||||
self:startGrowIn(self.enemy)
|
||||
self:actNext(function()
|
||||
self:waitSfxNext(self:playEntranceCry(self.enemy))
|
||||
end)
|
||||
end)
|
||||
return
|
||||
end
|
||||
|
||||
@@ -4277,6 +4340,9 @@ function BattleState:enemyMonFainted()
|
||||
self:act(function()
|
||||
local previous = self.enemy
|
||||
self.enemy = makeBattler(self.data, self.enemyParty[self.enemyIndex], false)
|
||||
-- EnemySendOutFirstMon (core.asm:1359-1363): the fresh foe's HP is the
|
||||
-- new wLastSwitchInEnemyMonHP baseline RetreatMon measures from
|
||||
self.lastSwitchInEnemyHP = self.enemy.mon.hp
|
||||
-- EnemySendOutFirstMon (core.asm:1314-1315): clears player's trap
|
||||
clearTrapping(self.player)
|
||||
self:syncSides()
|
||||
@@ -4296,7 +4362,7 @@ function BattleState:enemyMonFainted()
|
||||
-- (AnimateSendingOutMon) with the cry; no POOF -- that animation
|
||||
-- belongs to the player-side SendOutMon (core.asm:1757-1762)
|
||||
self.enemySendingOut = true
|
||||
self:sayNext(Strings("%s sent\nout %s!", self.trainer.name, self.enemy.name))
|
||||
self:sayNextAuto(Strings("%s sent\nout %s!", self.trainer.name, self.enemy.name))
|
||||
self:actNext(function()
|
||||
self.enemySendingOut = false
|
||||
self:startGrowIn(self.enemy)
|
||||
@@ -4309,34 +4375,41 @@ function BattleState:enemyMonFainted()
|
||||
self:act(function()
|
||||
local mon = shiftSwitchMon
|
||||
if not mon then return end
|
||||
local previous = self.player
|
||||
self.player = makeBattler(self.data, mon, true, self.game.save)
|
||||
clearTrapping(self.enemy)
|
||||
self:syncSides()
|
||||
Runtime.emit("battle.battler_switched", {
|
||||
battle = self, side = self.sides[1],
|
||||
battler = self.player, previous = previous,
|
||||
})
|
||||
-- Taking the SHIFT offer ZEROES wPartyGainExpFlags and
|
||||
-- wPartyFoughtCurrentEnemyFlags before jumping to SwitchPlayerMon
|
||||
-- (EnemySendOutFirstMon tail, core.asm:1436-1443), and SwitchPlayerMon
|
||||
-- then FLAG_SETs only the mon coming in (core.asm:2424-2433). Without
|
||||
-- the reset the mon that was out when the enemy fainted -- marked by
|
||||
-- the send-out act above, which mirrors EnemySendOut's own re-flag
|
||||
-- (core.asm:1276-1289) -- stayed a participant, so the exp divisor in
|
||||
-- enemyMonFainted counted two mons and the switch-in earned half the
|
||||
-- next KO (#275). Voluntary switches (resolveSwitch) and post-faint
|
||||
-- replacements (openReplacementMenu) must NOT do this: pokered's
|
||||
-- party-menu SwitchPlayerMon keeps the outgoing mon flagged, which is
|
||||
-- the deliberate exp-share, and a fainted mon is already dropped by
|
||||
-- onFaint mirroring RemoveFaintedPlayerMon (core.asm:1002-1007).
|
||||
self.participants = {}
|
||||
self:markParticipant()
|
||||
-- SwitchPlayerMon (core.asm:2419-2423): RetreatMon, the 50-frame
|
||||
-- hold, then the recall and the send-out
|
||||
self.nextInsert = 0
|
||||
sendOutMonCursors(self)
|
||||
self.sendingOut = true
|
||||
self:sayNext(self:sendOutText(self.player.name))
|
||||
self:queueSendOutAnim(false)
|
||||
self:sayNextAuto(self:withdrawText(self.player.name),
|
||||
Timing.SWITCH_PLAYER_MON)
|
||||
self:actNext(function()
|
||||
local previous = self.player
|
||||
self.player = makeBattler(self.data, mon, true, self.game.save)
|
||||
clearTrapping(self.enemy)
|
||||
self:syncSides()
|
||||
Runtime.emit("battle.battler_switched", {
|
||||
battle = self, side = self.sides[1],
|
||||
battler = self.player, previous = previous,
|
||||
})
|
||||
-- Taking the SHIFT offer ZEROES wPartyGainExpFlags and
|
||||
-- wPartyFoughtCurrentEnemyFlags before jumping to SwitchPlayerMon
|
||||
-- (EnemySendOutFirstMon tail, core.asm:1436-1443), and SwitchPlayerMon
|
||||
-- then FLAG_SETs only the mon coming in (core.asm:2424-2433). Without
|
||||
-- the reset the mon that was out when the enemy fainted -- marked by
|
||||
-- the send-out act above, which mirrors EnemySendOut's own re-flag
|
||||
-- (core.asm:1276-1289) -- stayed a participant, so the exp divisor in
|
||||
-- enemyMonFainted counted two mons and the switch-in earned half the
|
||||
-- next KO (#275). Voluntary switches (resolveSwitch) and post-faint
|
||||
-- replacements (openReplacementMenu) must NOT do this: pokered's
|
||||
-- party-menu SwitchPlayerMon keeps the outgoing mon flagged, which is
|
||||
-- the deliberate exp-share, and a fainted mon is already dropped by
|
||||
-- onFaint mirroring RemoveFaintedPlayerMon (core.asm:1002-1007).
|
||||
self.participants = {}
|
||||
self:markParticipant()
|
||||
self.nextInsert = 0
|
||||
sendOutMonCursors(self)
|
||||
self.sendingOut = true
|
||||
self:sayNextAuto(self:sendOutText(self.player.name))
|
||||
self:queueSendOutAnim(false)
|
||||
end)
|
||||
end)
|
||||
return
|
||||
end
|
||||
@@ -4526,7 +4599,7 @@ function BattleState:openReplacementMenu()
|
||||
self.nextInsert = 0
|
||||
sendOutMonCursors(self)
|
||||
self.sendingOut = true
|
||||
self:sayNext(self:sendOutText(self.player.name))
|
||||
self:sayNextAuto(self:sendOutText(self.player.name))
|
||||
self:queueSendOutAnim(false)
|
||||
end,
|
||||
})
|
||||
|
||||
@@ -92,6 +92,20 @@ local function hitCount(ctx, record)
|
||||
return dist[r + 1]
|
||||
end
|
||||
|
||||
-- engine/battle/effects.asm:119-151 (poison), :194-255 (burn/freeze/paralyze)
|
||||
local FBP_SIDE_STATUS = { BRN = true, FRZ = true, PAR = true }
|
||||
|
||||
local function secondaryStatusFx(battle, user, status)
|
||||
if status == "PSN" then
|
||||
local row = battle:animNext(user.isPlayer and "ENEMY_HUD_SHAKE_ANIM"
|
||||
or "SHAKE_SCREEN_ANIM", user.isPlayer)
|
||||
row.animDelayed = true
|
||||
row.hit = { animType = user.isPlayer and 6 or 3 }
|
||||
elseif FBP_SIDE_STATUS[status] and user.isPlayer then
|
||||
battle:animNext("ENEMY_HUD_SHAKE_ANIM", true).animDelayed = true
|
||||
end
|
||||
end
|
||||
|
||||
-- The damaging pipeline, extracted from the performMove monolith: every
|
||||
-- stage keeps the original's exact check order and rng consumption
|
||||
-- (invulnerability -> gate -> hit count -> pre-accuracy -> accuracy ->
|
||||
@@ -318,7 +332,12 @@ function EffectRegistry.runDamaging(battle, ctx, record)
|
||||
-- secondary side effects (blocked by fainting)
|
||||
if record and record.run and record.kind ~= "primary"
|
||||
and target.mon.hp > 0 and totalDealt > 0 then
|
||||
for _, m in ipairs(record.run(ctx)) do
|
||||
local hadStatus = target.mon.status
|
||||
local msgs = record.run(ctx)
|
||||
if target.mon.status and target.mon.status ~= hadStatus then
|
||||
secondaryStatusFx(battle, user, target.mon.status)
|
||||
end
|
||||
for _, m in ipairs(msgs) do
|
||||
battle:sayNext(m)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -581,6 +581,15 @@ MoveEffects.full = {
|
||||
end,
|
||||
},
|
||||
THRASH_PETAL_DANCE_EFFECT = {
|
||||
-- ThrashPetalDanceEffect (effects.asm:791-808) runs before damage
|
||||
-- (data/battle/special_effects.asm:22) and animates the setup turn
|
||||
beforeAccuracy = function(ctx)
|
||||
local user = ctx.user
|
||||
if not user.thrashTurns then
|
||||
ctx.battle:animBeforeMove(
|
||||
user.isPlayer and "SHRINKING_SQUARE_ANIM" or "ANIM_B1", user.isPlayer)
|
||||
end
|
||||
end,
|
||||
afterDamage = function(ctx)
|
||||
local user = ctx.user
|
||||
if not user.thrashTurns then
|
||||
|
||||
+86
-13
@@ -6,6 +6,49 @@ local IssueReport = {}
|
||||
local FORM_URL = "https://github.com/bryanthaboi/gen1recomp/issues/new"
|
||||
local TEMPLATE = "bug_report.yml"
|
||||
|
||||
local APPLE_MODELS = {
|
||||
["iPhone14,2"] = "iPhone 13 Pro",
|
||||
["iPhone14,3"] = "iPhone 13 Pro Max",
|
||||
["iPhone14,4"] = "iPhone 13 mini",
|
||||
["iPhone14,5"] = "iPhone 13",
|
||||
["iPhone14,7"] = "iPhone 14",
|
||||
["iPhone14,8"] = "iPhone 14 Plus",
|
||||
["iPhone15,2"] = "iPhone 14 Pro",
|
||||
["iPhone15,3"] = "iPhone 14 Pro Max",
|
||||
["iPhone15,4"] = "iPhone 15",
|
||||
["iPhone15,5"] = "iPhone 15 Plus",
|
||||
["iPhone16,1"] = "iPhone 15 Pro",
|
||||
["iPhone16,2"] = "iPhone 15 Pro Max",
|
||||
["iPhone17,1"] = "iPhone 16 Pro",
|
||||
["iPhone17,2"] = "iPhone 16 Pro Max",
|
||||
["iPhone17,3"] = "iPhone 16",
|
||||
["iPhone17,4"] = "iPhone 16 Plus",
|
||||
["iPhone17,5"] = "iPhone 16e",
|
||||
["Mac14,2"] = "MacBook Air (13-inch, M2)",
|
||||
["Mac14,3"] = "Mac mini (M2)",
|
||||
["Mac14,5"] = "MacBook Pro (14-inch, M2 Max)",
|
||||
["Mac14,6"] = "MacBook Pro (16-inch, M2 Max)",
|
||||
["Mac14,7"] = "MacBook Pro (13-inch, M2)",
|
||||
["Mac14,9"] = "MacBook Pro (14-inch, M2 Pro)",
|
||||
["Mac14,10"] = "MacBook Pro (16-inch, M2 Pro)",
|
||||
["Mac14,12"] = "Mac mini (M2 Pro)",
|
||||
["Mac14,13"] = "Mac Studio (M2 Max)",
|
||||
["Mac14,14"] = "Mac Studio (M2 Ultra)",
|
||||
["Mac14,15"] = "MacBook Air (15-inch, M2)",
|
||||
["Mac15,3"] = "MacBook Pro (14-inch, M3)",
|
||||
["Mac15,6"] = "MacBook Pro (14-inch, M3 Pro)",
|
||||
["Mac15,7"] = "MacBook Pro (16-inch, M3 Pro)",
|
||||
["Mac15,12"] = "MacBook Air (13-inch, M3)",
|
||||
["Mac15,13"] = "MacBook Air (15-inch, M3)",
|
||||
["Mac16,1"] = "MacBook Pro (14-inch, M4)",
|
||||
["Mac16,5"] = "MacBook Pro (16-inch, M4 Max)",
|
||||
["Mac16,6"] = "MacBook Pro (14-inch, M4 Max)",
|
||||
["Mac16,7"] = "MacBook Pro (16-inch, M4 Pro)",
|
||||
["Mac16,8"] = "MacBook Pro (14-inch, M4 Pro)",
|
||||
["Mac16,10"] = "Mac mini (M4)",
|
||||
["Mac16,11"] = "Mac mini (M4 Pro)",
|
||||
}
|
||||
|
||||
local function clean(value)
|
||||
if value == nil then return nil end
|
||||
local text = tostring(value):gsub("^%s+", ""):gsub("%s+$", "")
|
||||
@@ -36,6 +79,16 @@ local function commandValue(command)
|
||||
return clean(value)
|
||||
end
|
||||
|
||||
local function commandText(command)
|
||||
if not io or type(io.popen) ~= "function" then return nil end
|
||||
local ok, pipe = pcall(io.popen, command, "r")
|
||||
if not ok or not pipe then return nil end
|
||||
local readOK, value = pcall(pipe.read, pipe, "*a")
|
||||
pcall(pipe.close, pipe)
|
||||
if not readOK then return nil end
|
||||
return clean(value)
|
||||
end
|
||||
|
||||
local function percentEncode(value)
|
||||
local text = tostring(value or "")
|
||||
return (text:gsub("([^%w%-_%.~])", function(char)
|
||||
@@ -66,6 +119,25 @@ local function loveVersion()
|
||||
return result
|
||||
end
|
||||
|
||||
local function friendlyModel(identifier)
|
||||
identifier = clean(identifier)
|
||||
if not identifier then return nil end
|
||||
return APPLE_MODELS[identifier] or identifier
|
||||
end
|
||||
|
||||
local function macModel()
|
||||
local details = commandText("system_profiler SPHardwareDataType 2>/dev/null")
|
||||
if details then
|
||||
local name = clean(details:match("Model Name:%s*([^\r\n]+)"))
|
||||
local chip = clean(details:match("Chip:%s*([^\r\n]+)"))
|
||||
if name and chip and not name:find(chip, 1, true) then
|
||||
return name .. " (" .. chip .. ")"
|
||||
end
|
||||
if name then return name end
|
||||
end
|
||||
return friendlyModel(commandValue("sysctl -n hw.model 2>/dev/null"))
|
||||
end
|
||||
|
||||
local function appVersion()
|
||||
local version = clean(Version.engine)
|
||||
if not version or version == "0.0.0" or version == "0.0.0-dev" then return "" end
|
||||
@@ -73,20 +145,25 @@ local function appVersion()
|
||||
end
|
||||
|
||||
local function deviceModel(rawOS, system)
|
||||
local model = clean(call(system.getModel))
|
||||
if model then return model end
|
||||
local nativeModel = clean(call(system.getDeviceModel))
|
||||
if nativeModel then return friendlyModel(nativeModel) end
|
||||
if rawOS == "OS X" or rawOS == "macOS" then
|
||||
return commandValue("sysctl -n hw.model 2>/dev/null")
|
||||
return macModel()
|
||||
end
|
||||
local model = clean(call(system.getModel))
|
||||
if model and not model:lower():find("gpu", 1, true)
|
||||
and not model:lower():find("renderer", 1, true) then
|
||||
return friendlyModel(model)
|
||||
end
|
||||
if rawOS == "Windows" then
|
||||
return commandValue("powershell.exe -NoProfile -NonInteractive -Command \"(Get-CimInstance Win32_ComputerSystem).Model\" 2>NUL")
|
||||
return friendlyModel(commandValue("powershell.exe -NoProfile -NonInteractive -Command \"(Get-CimInstance Win32_ComputerSystem).Model\" 2>NUL"))
|
||||
end
|
||||
if rawOS == "Linux" then
|
||||
return commandValue("cat /sys/devices/virtual/dmi/id/product_name 2>/dev/null")
|
||||
or commandValue("cat /sys/devices/virtual/dmi/id/model 2>/dev/null")
|
||||
return friendlyModel(commandValue("cat /sys/devices/virtual/dmi/id/product_name 2>/dev/null")
|
||||
or commandValue("cat /sys/devices/virtual/dmi/id/model 2>/dev/null"))
|
||||
end
|
||||
if rawOS == "Android" then
|
||||
return commandValue("getprop ro.product.model 2>/dev/null")
|
||||
return friendlyModel(commandValue("getprop ro.product.model 2>/dev/null"))
|
||||
end
|
||||
return nil
|
||||
end
|
||||
@@ -121,7 +198,7 @@ local function metadata(options, context)
|
||||
local window = love and love.window or {}
|
||||
local rawOS = clean(call(system.getOS))
|
||||
local model = deviceModel(rawOS, system)
|
||||
local renderer, rendererVersion, _, rendererDevice = call(graphics.getRendererInfo)
|
||||
local renderer, rendererVersion = call(graphics.getRendererInfo)
|
||||
local width, height = call(graphics.getDimensions)
|
||||
local pixelWidth, pixelHeight = call(graphics.getPixelDimensions)
|
||||
local modeWidth, modeHeight, flags = call(window.getMode)
|
||||
@@ -134,11 +211,7 @@ local function metadata(options, context)
|
||||
if value then lines[#lines + 1] = "- " .. label .. ": " .. value end
|
||||
end
|
||||
add("Platform", formOS(rawOS))
|
||||
local hardware = model
|
||||
if rendererDevice and rendererDevice ~= model then
|
||||
hardware = hardware and (hardware .. " (" .. rendererDevice .. ")") or rendererDevice
|
||||
end
|
||||
add("Device", hardware)
|
||||
add("Device", model)
|
||||
local rendererDetails = clean(renderer)
|
||||
if rendererDetails and clean(rendererVersion) then
|
||||
rendererDetails = rendererDetails .. " " .. clean(rendererVersion)
|
||||
|
||||
@@ -541,29 +541,6 @@ local function modRows(opts, mod)
|
||||
return rows
|
||||
end
|
||||
|
||||
local function troubleshootingRows(opts, hooks)
|
||||
return {
|
||||
{
|
||||
label = Strings("SAFE MODE"),
|
||||
actionLabel = function()
|
||||
return SaveData.isSafeMode(opts) and Strings("Turn off") or Strings("Turn on")
|
||||
end,
|
||||
action = function()
|
||||
SaveData.setSafeMode(opts, not SaveData.isSafeMode(opts))
|
||||
return true
|
||||
end,
|
||||
},
|
||||
{
|
||||
label = Strings("REPORT ISSUE"),
|
||||
actionLabel = Strings("Report bug"),
|
||||
action = function()
|
||||
if hooks and hooks.reportIssue then hooks.reportIssue(opts) end
|
||||
return false
|
||||
end,
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
-- ------- Gen 2 (Gold)
|
||||
--
|
||||
-- Gold reads NONE of the rows above. Its OPTION screen writes a different
|
||||
@@ -744,10 +721,6 @@ function LauncherSettings.open(hooks, version)
|
||||
sections[#sections + 1] = { title = mod.name, rows = rows }
|
||||
end
|
||||
end
|
||||
sections[#sections + 1] = {
|
||||
title = Strings("TROUBLESHOOTING"),
|
||||
rows = troubleshootingRows(opts, hooks),
|
||||
}
|
||||
return {
|
||||
opts = opts,
|
||||
version = version,
|
||||
|
||||
+131
-38
@@ -406,6 +406,15 @@ local function cartQuad(project, x, y, w, h, z)
|
||||
}
|
||||
end
|
||||
|
||||
local function cartFacing(points)
|
||||
local area = 0
|
||||
for i = 1, #points do
|
||||
local a, b = points[i], points[i % #points + 1]
|
||||
area = area + a[1] * b[2] - b[1] * a[2]
|
||||
end
|
||||
return area > 0
|
||||
end
|
||||
|
||||
local function cartPill(project, x, y, w, h, z, color, alpha)
|
||||
local points, radius = {}, h / 2
|
||||
for i = 0, 10 do
|
||||
@@ -628,51 +637,64 @@ local function cartridgeButton(imp, x, y, w, h, key, version, gameName, action)
|
||||
local side = { math.floor(shell[1] * 0.54), math.floor(shell[2] * 0.54),
|
||||
math.floor(shell[3] * 0.54) }
|
||||
|
||||
cartPolygon(mainBack, side, 1)
|
||||
cartPolygon(capBack, side, 1)
|
||||
local frontFacing = cartFacing(mainFront)
|
||||
if frontFacing then
|
||||
cartPolygon(mainBack, side, 1)
|
||||
cartPolygon(capBack, side, 1)
|
||||
else
|
||||
cartPolygon(mainFront, shell, 1)
|
||||
cartPolygon(capFront, shell, 1)
|
||||
end
|
||||
cartPolygon({ mainFront[2], mainFront[3], mainBack[3], mainBack[2] }, side, 1)
|
||||
cartPolygon({ mainFront[3], mainFront[4], mainBack[4], mainBack[3] }, side, 1)
|
||||
cartPolygon({ mainFront[1], mainFront[2], mainBack[2], mainBack[1] }, side, 1)
|
||||
cartPolygon({ capFront[2], capFront[3], capBack[3], capBack[2] }, side, 1)
|
||||
cartPolygon({ capFront[1], capFront[2], capBack[2], capBack[1] }, side, 1)
|
||||
cartPolygon({ capFront[4], capFront[1], capBack[1], capBack[4] }, side, 1)
|
||||
cartPolygon(mainFront, shell, 1)
|
||||
cartPolygon(capFront, shell, 1)
|
||||
|
||||
local faceZ = depth + 0.8
|
||||
for i = 0, 4 do
|
||||
local ry = mainTop + 7 + i * h * 0.025
|
||||
cartPolygon(cartQuad(project, -halfW + 2, ry, w * 0.13, 2, faceZ), side, 0.7)
|
||||
cartPolygon(cartQuad(project, halfW - w * 0.13 - 2, ry, w * 0.13, 2, faceZ), side, 0.7)
|
||||
if frontFacing then
|
||||
cartPolygon(mainFront, shell, 1)
|
||||
cartPolygon(capFront, shell, 1)
|
||||
else
|
||||
cartPolygon(mainBack, side, 1)
|
||||
cartPolygon(capBack, side, 1)
|
||||
end
|
||||
local recessX, recessY = -w * 0.32, mainTop + h * 0.023
|
||||
local recessW, recessH = w * 0.64, h * 0.24
|
||||
cartPolygon(cartQuad(project, recessX, recessY, recessW, recessH, faceZ), shell, 0.88)
|
||||
cartPill(project, recessX + w * 0.025, recessY + h * 0.025,
|
||||
recessW - w * 0.05, h * 0.12, faceZ + 0.5, shell, 0.7)
|
||||
cartPill(project, recessX + w * 0.045, recessY + h * 0.043,
|
||||
recessW - w * 0.09, h * 0.083, faceZ + 0.8, side, 0.42)
|
||||
|
||||
local labelX, labelY = -w * 0.33, -h * 0.20
|
||||
local labelW, labelH = w * 0.66, h * 0.55
|
||||
local plate = cartQuad(project, labelX - 2, labelY - 2, labelW + 4, labelH + 4, faceZ + 0.8)
|
||||
cartPolygon(plate, side, 0.95)
|
||||
local labelPoints = cartQuad(project, labelX, labelY, labelW, labelH, faceZ + 1.2)
|
||||
local label = cartridgeLabel(imp, version)
|
||||
local mesh = label and cartLabelMesh(imp, version, label, labelPoints)
|
||||
if mesh then
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.draw(mesh)
|
||||
elseif label then
|
||||
local artScale = math.min(labelW / label.width, labelH / label.height)
|
||||
love.graphics.draw(label.image, labelPoints[1][1], labelPoints[1][2],
|
||||
0, artScale, artScale)
|
||||
if frontFacing then
|
||||
local faceZ = depth + 0.8
|
||||
for i = 0, 4 do
|
||||
local ry = mainTop + 7 + i * h * 0.025
|
||||
cartPolygon(cartQuad(project, -halfW + 2, ry, w * 0.13, 2, faceZ), side, 0.7)
|
||||
cartPolygon(cartQuad(project, halfW - w * 0.13 - 2, ry, w * 0.13, 2, faceZ), side, 0.7)
|
||||
end
|
||||
local recessX, recessY = -w * 0.32, mainTop + h * 0.023
|
||||
local recessW, recessH = w * 0.64, h * 0.24
|
||||
cartPolygon(cartQuad(project, recessX, recessY, recessW, recessH, faceZ), shell, 0.88)
|
||||
cartPill(project, recessX + w * 0.025, recessY + h * 0.025,
|
||||
recessW - w * 0.05, h * 0.12, faceZ + 0.5, shell, 0.7)
|
||||
cartPill(project, recessX + w * 0.045, recessY + h * 0.043,
|
||||
recessW - w * 0.09, h * 0.083, faceZ + 0.8, side, 0.42)
|
||||
|
||||
local labelX, labelY = -w * 0.33, -h * 0.20
|
||||
local labelW, labelH = w * 0.66, h * 0.55
|
||||
local plate = cartQuad(project, labelX - 2, labelY - 2, labelW + 4, labelH + 4, faceZ + 0.8)
|
||||
cartPolygon(plate, side, 0.95)
|
||||
local labelPoints = cartQuad(project, labelX, labelY, labelW, labelH, faceZ + 1.2)
|
||||
local label = cartridgeLabel(imp, version)
|
||||
local mesh = label and cartLabelMesh(imp, version, label, labelPoints)
|
||||
if mesh then
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.draw(mesh)
|
||||
elseif label then
|
||||
local artScale = math.min(labelW / label.width, labelH / label.height)
|
||||
love.graphics.draw(label.image, labelPoints[1][1], labelPoints[1][2],
|
||||
0, artScale, artScale)
|
||||
end
|
||||
cartPolygon({
|
||||
{ project(-w * 0.07, h * 0.37, faceZ + 1) },
|
||||
{ project(w * 0.07, h * 0.37, faceZ + 1) },
|
||||
{ project(0, h * 0.43, faceZ + 1) },
|
||||
}, side, 0.70)
|
||||
end
|
||||
cartPolygon({
|
||||
{ project(-w * 0.07, h * 0.37, faceZ + 1) },
|
||||
{ project(w * 0.07, h * 0.37, faceZ + 1) },
|
||||
{ project(0, h * 0.43, faceZ + 1) },
|
||||
}, side, 0.70)
|
||||
love.graphics.pop()
|
||||
|
||||
if not state.active and (Kit._activateId == key) then
|
||||
@@ -926,10 +948,13 @@ local HEADER_TABS = {
|
||||
{ id = "mods", key = "tab-mods" },
|
||||
{ id = "find", key = "tab-find" },
|
||||
{ id = "skins", key = "tab-skins", glyph = true },
|
||||
{ id = "bug", key = "tab-bug" },
|
||||
}
|
||||
for _, t in ipairs(HEADER_TABS) do
|
||||
t.opts = { face = "tab", font = "tab", color = t.color, letter = t.letter }
|
||||
if t.glyph then t.opts.drawFn = drawSkinGlyph end
|
||||
if t.glyph then
|
||||
t.opts.drawFn = drawSkinGlyph
|
||||
end
|
||||
end
|
||||
|
||||
-- Which cartridge the dropdown is showing: the open game tab, else the last
|
||||
@@ -1067,6 +1092,8 @@ local function buildHeader(imp, m)
|
||||
or love.graphics.newImage("assets/launcher/mods.png")
|
||||
imp._findIcon = imp._findIcon
|
||||
or love.graphics.newImage("assets/launcher/find.png")
|
||||
imp._bugIcon = imp._bugIcon
|
||||
or love.graphics.newImage("assets/launcher/bug.png")
|
||||
-- Game tabs keep their cartridge colours -- that is the one piece of brand
|
||||
-- identity in the launcher, and "the red one" is how people actually refer
|
||||
-- to these. The colour rides the outline and the glyph at rest and becomes
|
||||
@@ -1077,6 +1104,7 @@ local function buildHeader(imp, m)
|
||||
for _, t in ipairs(tabs) do
|
||||
if t.id == "mods" then t.icon = imp._modsIcon end
|
||||
if t.id == "find" then t.icon = imp._findIcon end
|
||||
if t.id == "bug" then t.icon = imp._bugIcon end
|
||||
end
|
||||
local tabH = m.chip
|
||||
local tx = m.x + m.pad
|
||||
@@ -1923,7 +1951,7 @@ local function buildModsPanel(imp, x, y, w, availH, m)
|
||||
-- notice line
|
||||
local noticeText, noticeCol
|
||||
if safeMode then
|
||||
noticeText, noticeCol = "Safe mode is on. All mods are disabled. Turn it off in Settings to change mod toggles.", PAL.yellow
|
||||
noticeText, noticeCol = "Safe mode is on. All mods are disabled. Turn it off in the Bug tab to change mod toggles.", PAL.yellow
|
||||
elseif imp.modNotice then
|
||||
noticeText = imp.modNotice.text
|
||||
noticeCol = imp.modNotice.ok and PAL.green or PAL.red
|
||||
@@ -2322,6 +2350,69 @@ local function buildSkinsPanel(imp, x, y, w, availH, m)
|
||||
return cy + hintH - y
|
||||
end
|
||||
|
||||
local function buildBugPanel(imp, x, y, w, availH, m)
|
||||
local SaveData = require("src.core.SaveData")
|
||||
local gap = m.gap
|
||||
local pad = math.floor(16 * m.s)
|
||||
local cy = y
|
||||
local safeMode = imp:_safeModeEnabled()
|
||||
|
||||
Kit.text("button", Strings("Troubleshooting"), x, cy, PAL.heading)
|
||||
cy = cy + Kit.textHeight("button") + gap
|
||||
|
||||
if imp.issueNotice then
|
||||
cy = cy + Kit.textWrapped("small", imp.issueNotice.text, x, cy, w,
|
||||
imp.issueNotice.ok and PAL.green or PAL.red, 2) + gap
|
||||
end
|
||||
|
||||
local switchW = math.floor(92 * m.s)
|
||||
local switchH = math.max(m.btnH, Kit.tapMin())
|
||||
local detail = safeMode
|
||||
and Strings("All mods are disabled and their toggles are locked until safe mode is turned off.")
|
||||
or Strings("Temporarily disable every mod while you reproduce a bug.")
|
||||
local textW = math.max(0, w - 2 * pad - switchW - gap)
|
||||
local detailH = Kit.wrapHeight("small", detail, textW, 3)
|
||||
local safeH = math.max(switchH, Kit.textHeight("small") + math.floor(4 * m.s) + detailH)
|
||||
+ 2 * pad
|
||||
Kit.card(x, cy, w, safeH)
|
||||
local textX = x + pad
|
||||
local textY = cy + pad
|
||||
Kit.text("small", Strings("Safe mode"), textX, textY, PAL.heading)
|
||||
Kit.textWrapped("small", detail, textX,
|
||||
textY + Kit.textHeight("small") + math.floor(4 * m.s), textW,
|
||||
PAL.muted, 3)
|
||||
local toggleX = x + w - pad - switchW
|
||||
local toggleY = cy + math.floor((safeH - switchH) / 2)
|
||||
local _, changed = Kit.toggle(toggleX, toggleY, switchW, switchH, safeMode,
|
||||
"bug-safe-mode")
|
||||
if changed then
|
||||
queueAction(imp, "bug-safe-mode", function() imp:_toggleSafeMode() end)
|
||||
end
|
||||
cy = cy + safeH + gap
|
||||
|
||||
local reportLabel = Strings("Report a bug")
|
||||
local reportW = math.min(w - 2 * pad,
|
||||
Kit.textWidth("small", reportLabel) + math.floor(32 * m.s))
|
||||
local reportDetail = Strings("Fill out the GitHub form with the available system information.")
|
||||
local reportTextW = math.max(0, w - 2 * pad - reportW - gap)
|
||||
local reportDetailH = Kit.wrapHeight("small", reportDetail, reportTextW, 3)
|
||||
local reportH = math.max(m.btnH, Kit.textHeight("small") + math.floor(4 * m.s) + reportDetailH)
|
||||
+ 2 * pad
|
||||
Kit.card(x, cy, w, reportH)
|
||||
Kit.text("small", Strings("Something not working?"), textX, cy + pad, PAL.heading)
|
||||
Kit.textWrapped("small", reportDetail, textX,
|
||||
cy + pad + Kit.textHeight("small") + math.floor(4 * m.s), reportTextW,
|
||||
PAL.muted, 3)
|
||||
btn(imp, x + w - pad - reportW,
|
||||
cy + math.floor((reportH - m.btnH) / 2), reportW, m.btnH,
|
||||
"bug-report", reportLabel, {
|
||||
kind = "accent", font = "small",
|
||||
action = function()
|
||||
imp:_ensureMods()
|
||||
imp:_reportIssue(SaveData.loadOptions(), nil)
|
||||
end })
|
||||
end
|
||||
|
||||
local function buildFindPanel(imp, x, y, w, availH, m)
|
||||
imp:_ensureFind()
|
||||
imp:_ensureMods()
|
||||
@@ -4822,6 +4913,8 @@ function LauncherView.draw(imp)
|
||||
contentH = buildFindPanel(imp, x, py, panelW, budgetH, m)
|
||||
elseif imp.tab == "skins" then
|
||||
contentH = buildSkinsPanel(imp, x, py, panelW, budgetH, m)
|
||||
elseif imp.tab == "bug" then
|
||||
contentH = buildBugPanel(imp, x, py, panelW, budgetH, m)
|
||||
else
|
||||
contentH = buildGamePanel(imp, x, py, panelW, availH, m, imp.tab, budgetH)
|
||||
end
|
||||
|
||||
+32
-20
@@ -1266,6 +1266,7 @@ end
|
||||
function RomImporter:_applyLastVersionTab()
|
||||
local okLO, LO = pcall(require, "src.core.LaunchOptions")
|
||||
if okLO and LO.pendingTab then return end
|
||||
if os.getenv("POKEPORT_LAUNCHER_TAB") then return end
|
||||
local okOpt, opts = pcall(function()
|
||||
return require("src.core.SaveData").loadOptions()
|
||||
end)
|
||||
@@ -1337,7 +1338,7 @@ function RomImporter.new(onComplete, opts)
|
||||
-- player at least arrives on the tab they asked for (src/core/LaunchOptions).
|
||||
tab = (function()
|
||||
local okLO, LO = pcall(require, "src.core.LaunchOptions")
|
||||
return (okLO and LO.pendingTab) or "red"
|
||||
return (okLO and LO.pendingTab) or os.getenv("POKEPORT_LAUNCHER_TAB") or "red"
|
||||
end)(),
|
||||
logo = love.graphics.newImage("assets/logo/logo.png"),
|
||||
bcg = love.graphics.newImage("assets/logo/bcg.png"),
|
||||
@@ -1364,7 +1365,8 @@ function RomImporter.new(onComplete, opts)
|
||||
-- in draw); modNotice is the last install/delete result { ok, text }.
|
||||
-- requiredImportNotice stays inside the imported-files modal so validation
|
||||
-- failures are visible beside the file picker that caused them.
|
||||
mods = nil, modScroll = 0, modNotice = nil, requiredImportNotice = nil,
|
||||
mods = nil, modScroll = 0, modNotice = nil, issueNotice = nil,
|
||||
requiredImportNotice = nil,
|
||||
-- Which game the MODS panel is answering for (a GameVersion id, nil =
|
||||
-- every game). Rows resolve their enable-state and their "runs here"
|
||||
-- verdict against it (src/mods/ModTargets.lua).
|
||||
@@ -2755,7 +2757,7 @@ function RomImporter:resumeAfterOverlay()
|
||||
end
|
||||
|
||||
function RomImporter:_cycleTab(delta)
|
||||
local order = { "red", "blue", "yellow", "gold", "mods", "find", "skins" }
|
||||
local order = { "red", "blue", "yellow", "gold", "mods", "find", "skins", "bug" }
|
||||
local idx = 1
|
||||
for i, id in ipairs(order) do
|
||||
if id == self.tab then idx = i; break end
|
||||
@@ -3630,9 +3632,6 @@ function RomImporter:_openSettings()
|
||||
-- option block, and Gold's is not the flat Gen 1 one (#1100).
|
||||
local hooks = {}
|
||||
local version = self.tab
|
||||
hooks.reportIssue = function(opts)
|
||||
return self:_reportIssue(opts, version)
|
||||
end
|
||||
if self.onEditTouchControls then
|
||||
local version = self.tab
|
||||
hooks.editTouchControls = function()
|
||||
@@ -3655,7 +3654,6 @@ function RomImporter:_openSettings()
|
||||
end)
|
||||
if ok and model then
|
||||
self._settings = model
|
||||
self._settingsSafeModeAtOpen = require("src.core.SaveData").isSafeMode(model.opts)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3670,22 +3668,36 @@ function RomImporter:_closeSettings()
|
||||
local model = self._settings
|
||||
if model then
|
||||
model.save()
|
||||
local safeMode = require("src.core.SaveData").isSafeMode(model.opts)
|
||||
if safeMode ~= self._settingsSafeModeAtOpen then
|
||||
self.mods = nil
|
||||
self.safeMode = safeMode
|
||||
self._modSortCache = nil
|
||||
self._modInfoFetch = nil
|
||||
end
|
||||
end
|
||||
self._settings = nil
|
||||
self._settingsSafeModeAtOpen = nil
|
||||
end
|
||||
|
||||
function RomImporter:_safeModeEnabled()
|
||||
if self.safeMode == nil then
|
||||
local SaveData = require("src.core.SaveData")
|
||||
self.safeMode = SaveData.isSafeMode(SaveData.loadOptions())
|
||||
end
|
||||
return self.safeMode == true
|
||||
end
|
||||
|
||||
function RomImporter:_toggleSafeMode()
|
||||
local SaveData = require("src.core.SaveData")
|
||||
local options = SaveData.loadOptions()
|
||||
local enabled = not SaveData.isSafeMode(options)
|
||||
SaveData.setSafeMode(options, enabled)
|
||||
SaveData.saveOptions(options)
|
||||
self.safeMode = enabled
|
||||
self.mods = nil
|
||||
self._modSortCache = nil
|
||||
self._modInfoFetch = nil
|
||||
self.modNotice = nil
|
||||
end
|
||||
|
||||
function RomImporter:_reportIssue(options, version)
|
||||
self.issueNotice = nil
|
||||
local ok, IssueReport = pcall(require, "src.core.IssueReport")
|
||||
if not ok then
|
||||
self.modNotice = { ok = false, text = "Could not prepare the issue report." }
|
||||
self.issueNotice = { ok = false, text = "Could not prepare the issue report." }
|
||||
return false
|
||||
end
|
||||
local opened, url, reason = IssueReport.open(options, {
|
||||
@@ -3693,11 +3705,11 @@ function RomImporter:_reportIssue(options, version)
|
||||
mods = self.mods,
|
||||
})
|
||||
if not opened then
|
||||
self.modNotice = { ok = false, text = reason or "Could not open the issue report." }
|
||||
self.issueNotice = { ok = false, text = reason or "Could not open the issue report." }
|
||||
return false
|
||||
end
|
||||
self._lastIssueReportURL = url
|
||||
if reason then self.modNotice = { ok = true, text = reason } end
|
||||
if reason then self.issueNotice = { ok = true, text = reason } end
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -4180,7 +4192,7 @@ end
|
||||
-- Enabling an experimental mod arms a confirmation for that same game.
|
||||
function RomImporter:_toggleMod(id, confirmed, version)
|
||||
if self.safeMode then
|
||||
self.modNotice = { ok = false, text = "Safe mode is active. Turn it off in Settings to change mods." }
|
||||
self.modNotice = { ok = false, text = "Safe mode is active. Turn it off in the Bug tab to change mods." }
|
||||
return
|
||||
end
|
||||
local LauncherMods = require("src.mods.LauncherMods")
|
||||
@@ -4224,7 +4236,7 @@ end
|
||||
-- recovery action, and Delete is the only destructive one on this panel.
|
||||
function RomImporter:_setAllMods(want, confirmed)
|
||||
if self.safeMode then
|
||||
self.modNotice = { ok = false, text = "Safe mode is active. Turn it off in Settings to change mods." }
|
||||
self.modNotice = { ok = false, text = "Safe mode is active. Turn it off in the Bug tab to change mods." }
|
||||
return
|
||||
end
|
||||
local LauncherMods = require("src.mods.LauncherMods")
|
||||
|
||||
@@ -57,6 +57,8 @@ function TextBox.new(game, text, onDone, opts)
|
||||
self.money = opts and opts.money
|
||||
self.auto = opts and opts.auto
|
||||
self.stay = opts and opts.stay
|
||||
-- engine/events/hidden_events/cinnabar_gym_quiz.asm:119
|
||||
self.preSound = opts and opts.preSound
|
||||
-- opts.instant: put the LAST page up already typed, with no typewriter and
|
||||
-- no page waits. A `yesorno` follows a `writetext` that has already been
|
||||
-- read, so re-typing the line under the YES/NO box would be wrong -- the
|
||||
@@ -270,6 +272,17 @@ end
|
||||
function TextBox:update(dt)
|
||||
local input = self.game.input
|
||||
self.blink = (self.blink + 1) % 60
|
||||
-- home/text.asm:506
|
||||
if self.preSound then
|
||||
if not self.preStarted then
|
||||
self.preStarted = true
|
||||
self.preSrc = self.preSound()
|
||||
end
|
||||
if self.preSrc and self.preSrc.isPlaying and self.preSrc:isPlaying() then
|
||||
return
|
||||
end
|
||||
self.preSound, self.preSrc = nil, nil
|
||||
end
|
||||
-- A page or CONT advance blocks the whole box while the original's scroll
|
||||
-- and clear run (src/core/Timing.lua TEXT_SCROLL_PAIR / TEXT_PAGE_CLEAR).
|
||||
-- Nothing types and no input is read until it drains.
|
||||
|
||||
@@ -254,7 +254,8 @@ function Commands.give_item(ctx, itemId, count, gotText)
|
||||
Commands.show_text(ctx, gotText
|
||||
or Strings("{PLAYER} got\n%s!", ctx.game.stringBuffer))
|
||||
else
|
||||
Sound.play(ctx.game.data, jingle)
|
||||
-- scripts/OaksLab.asm:1058
|
||||
Commands.text_sound(ctx, jingle)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -537,6 +537,15 @@ function OverworldState:setMap(mapId, x, y, facing, opts)
|
||||
-- Route22Gate_Script rewrites wLastMap from the player's Y on entry
|
||||
-- too (not only on step), so a save/load mid-gate keeps exits correct
|
||||
if not (opts and opts.checkpoint) then self:syncLastMapRewrite() end
|
||||
-- home/overworld.asm:1821 (JoypadOverworld runs RunMapScript every frame);
|
||||
-- scripts/Route16Gate1F.asm:16, Route5Gate.asm:19, Route22Gate.asm:21
|
||||
if opts and opts.freshBoot and not opts.checkpoint then
|
||||
local standing = mapScripts and mapScripts.get(mapId)
|
||||
if not (standing and standing.onStep
|
||||
and standing.onStep(Game, self, self.player.cellX, self.player.cellY)) then
|
||||
self:checkBadgeGate()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Neighbor maps drawn at the composed connection offsets: at least the
|
||||
@@ -2910,7 +2919,15 @@ function OverworldState:openPC(onDone)
|
||||
keepOpen = true,
|
||||
onSelect = function()
|
||||
require("src.core.Sound").play(Game.data, "Enter_PC")
|
||||
Screens.push(Game, "BoxMenu")
|
||||
-- engine/menus/pc.asm:73 BillsPC prints the access text before the farcall
|
||||
local accessed = metBill
|
||||
and romText(Game.data, "_AccessedBillsPCText",
|
||||
"Accessed BILL's\nPC.\fAccessed POKéMON\nStorage System.")
|
||||
or romText(Game.data, "_AccessedSomeonesPCText",
|
||||
"Accessed someone's\nPC.\fAccessed POKéMON\nStorage System.")
|
||||
Game.stack:push(TextBox.new(Game, accessed, function()
|
||||
Screens.push(Game, "BoxMenu")
|
||||
end))
|
||||
done()
|
||||
end,
|
||||
})
|
||||
@@ -2920,9 +2937,13 @@ function OverworldState:openPC(onDone)
|
||||
label = (Game.save.player.name or "RED") .. "'s PC",
|
||||
keepOpen = true,
|
||||
onSelect = function()
|
||||
-- pc.asm .playersPC plays SFX_ENTER_PC before the farcall (#960)
|
||||
-- pc.asm .playersPC plays SFX_ENTER_PC then prints AccessedMyPCText
|
||||
-- before the farcall (engine/menus/pc.asm:54, #960)
|
||||
require("src.core.Sound").play(Game.data, "Enter_PC")
|
||||
Screens.push(Game, "PlayerPC")
|
||||
Game.stack:push(TextBox.new(Game,
|
||||
romText(Game.data, "_AccessedMyPCText",
|
||||
"Accessed my PC.\fAccessed Item\nStorage System."),
|
||||
function() Screens.push(Game, "PlayerPC") end))
|
||||
done()
|
||||
end,
|
||||
})
|
||||
|
||||
@@ -126,6 +126,10 @@ local PLAYER_STATE_BY_ID = {
|
||||
[8] = FieldMoves.PLAYER_SURF_PIKA,
|
||||
}
|
||||
|
||||
-- engine/overworld/variables.asm:49 VAR_MOVEMENT reads wPlayerState back
|
||||
local PLAYER_STATE_ID = {}
|
||||
for id, state in pairs(PLAYER_STATE_BY_ID) do PLAYER_STATE_ID[state] = id end
|
||||
|
||||
local BATTLETYPE = {
|
||||
CANLOSE = 1,
|
||||
FORCESHINY = 7,
|
||||
|
||||
Reference in New Issue
Block a user