mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-26 15:31:15 +02:00
Merge branch 'dev' of https://github.com/bryanthaboi/gen1recomp into dev
This commit is contained in:
@@ -422,7 +422,7 @@ local function discoverModSchemas(opts)
|
||||
-- except experimental mods, which stay off until opted in.
|
||||
local flag = require("src.core.SaveData").modEnabled(opts, m.id)
|
||||
local enabled = flag == true or (flag == nil and not m.experimental)
|
||||
if enabled then
|
||||
if enabled and not SaveData.isSafeMode(opts) then
|
||||
local chunk = fs.load(path .. "/" .. m.options_schema)
|
||||
if chunk then
|
||||
local okR, schema = pcall(chunk)
|
||||
@@ -521,9 +521,49 @@ local function modRows(opts, mod)
|
||||
return true
|
||||
end }
|
||||
end
|
||||
for _, row in ipairs(rows) do
|
||||
row.safeModeBlocked = true
|
||||
if row.step then
|
||||
local step = row.step
|
||||
row.step = function(dir)
|
||||
if SaveData.isSafeMode(opts) then return false end
|
||||
return step(dir)
|
||||
end
|
||||
end
|
||||
if row.setText then
|
||||
local setText = row.setText
|
||||
row.setText = function(text)
|
||||
if SaveData.isSafeMode(opts) then return false end
|
||||
return setText(text)
|
||||
end
|
||||
end
|
||||
end
|
||||
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
|
||||
@@ -704,6 +744,10 @@ 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,
|
||||
|
||||
+31
-11
@@ -682,6 +682,7 @@ end
|
||||
|
||||
local function modStatusColor(status)
|
||||
if status == "ok" then return Strings("Ready"), PAL.green end
|
||||
if status == "safe_mode" then return Strings("Safe mode"), PAL.yellow end
|
||||
if status == "needs_import" then return Strings("Import required"), PAL.yellow end
|
||||
if status == "conflict" then return Strings("Conflict"), PAL.red end
|
||||
-- not a fault: the mod is intact, this is simply not a game it is for
|
||||
@@ -1814,10 +1815,11 @@ end
|
||||
|
||||
-- One compact coloured checkbox for each game. The cartridge colour carries
|
||||
-- the game identity even when the row is narrow.
|
||||
local function modGameCheckbox(x, y, size, checked, game, id)
|
||||
local color = cartColor(game)
|
||||
local focused = Kit.focusable(id, x, y, size, size)
|
||||
local hot = focused or Kit.hover(x, y, size, size)
|
||||
local function modGameCheckbox(x, y, size, checked, game, id, enabled)
|
||||
enabled = enabled ~= false
|
||||
local color = enabled and cartColor(game) or PAL.steel
|
||||
local focused = enabled and Kit.focusable(id, x, y, size, size)
|
||||
local hot = enabled and (focused or Kit.hover(x, y, size, size))
|
||||
if love.graphics then
|
||||
Theme.fillRounded(x, y, size, size, PAL.bg, 1)
|
||||
if checked then
|
||||
@@ -1829,12 +1831,13 @@ local function modGameCheckbox(x, y, size, checked, game, id)
|
||||
hot and Theme.A.focus or Theme.A.hairline, 1)
|
||||
end
|
||||
end
|
||||
return Kit.press(x, y, size, size) or Kit._activateId == id
|
||||
return enabled and (Kit.press(x, y, size, size) or Kit._activateId == id)
|
||||
end
|
||||
|
||||
local function buildModsPanel(imp, x, y, w, availH, m)
|
||||
imp:_ensureMods()
|
||||
local ModUpdate = require("src.mods.ModUpdate")
|
||||
local safeMode = imp.safeMode == true
|
||||
local mods = imp.mods or {}
|
||||
local gap = m.gap
|
||||
local cy = y
|
||||
@@ -1866,9 +1869,11 @@ local function buildModsPanel(imp, x, y, w, availH, m)
|
||||
action = function() imp:chooseMod() end })
|
||||
btn(imp, place(disableW), cy, disableW, bh, "mods-disable-all", Strings("Disable all"), {
|
||||
kind = "warn", font = "small",
|
||||
enabled = not safeMode,
|
||||
action = function() imp:_setAllMods(false) end })
|
||||
btn(imp, place(enableW), cy, enableW, bh, "mods-enable-all", Strings("Enable all"), {
|
||||
kind = "good", font = "small",
|
||||
enabled = not safeMode,
|
||||
action = function() imp:_setAllMods(true) end })
|
||||
btn(imp, place(checkFullW), cy, checkFullW, bh, "mods-check-updates", Strings("Check for updates"), {
|
||||
font = "small",
|
||||
@@ -1917,7 +1922,9 @@ local function buildModsPanel(imp, x, y, w, availH, m)
|
||||
|
||||
-- notice line
|
||||
local noticeText, noticeCol
|
||||
if imp.modNotice then
|
||||
if safeMode then
|
||||
noticeText, noticeCol = "Safe mode is on. All mods are disabled. Turn it off in Settings to change mod toggles.", PAL.yellow
|
||||
elseif imp.modNotice then
|
||||
noticeText = imp.modNotice.text
|
||||
noticeCol = imp.modNotice.ok and PAL.green or PAL.red
|
||||
else
|
||||
@@ -2043,7 +2050,7 @@ local function buildModsPanel(imp, x, y, w, availH, m)
|
||||
local togKey = "mod-toggle-" .. mod.id .. "-" .. game
|
||||
if modGameCheckbox(tx, gamesY, togH,
|
||||
mod.enabledByVersion and mod.enabledByVersion[game] == true,
|
||||
game, togKey) then
|
||||
game, togKey, not safeMode) then
|
||||
local version = game
|
||||
queueAction(imp, togKey, function() imp:_toggleMod(mod.id, nil, version) end)
|
||||
flipped = true
|
||||
@@ -3079,6 +3086,7 @@ local function buildProfilesModal(imp, m)
|
||||
btn(imp, place(swBtnW), ry + math.floor(4 * m.s), swBtnW, rowH - math.floor(8 * m.s), "prof-sw-" .. i,
|
||||
Strings("Switch"), {
|
||||
kind = "good", font = "micro",
|
||||
enabled = not imp.safeMode,
|
||||
action = function()
|
||||
LauncherMods.applyProfile(p.name, options)
|
||||
if imp._refreshMods then imp:_refreshMods() end
|
||||
@@ -3105,8 +3113,10 @@ local function buildModHeaderActionsModal(imp, m)
|
||||
local btns = {
|
||||
{ label = Strings("Mod profiles..."), action = function() imp._profilesPopup = true end },
|
||||
{ label = Strings("Check for updates"), action = function() imp:_syncModUpdateInfo(true) end },
|
||||
{ label = Strings("Enable all mods"), kind = "good", action = function() imp:_setAllMods(true) end },
|
||||
{ label = Strings("Disable all mods"), kind = "warn", action = function() imp:_setAllMods(false) end },
|
||||
{ label = Strings("Enable all mods"), kind = "good", enabled = not imp.safeMode,
|
||||
action = function() imp:_setAllMods(true) end },
|
||||
{ label = Strings("Disable all mods"), kind = "warn", enabled = not imp.safeMode,
|
||||
action = function() imp:_setAllMods(false) end },
|
||||
{ label = Strings("Sort mods..."), action = function() imp._sortPopup = "mods" end },
|
||||
}
|
||||
local h = pad + Kit.textHeight("button") + math.floor(12 * m.s)
|
||||
@@ -3119,6 +3129,7 @@ local function buildModHeaderActionsModal(imp, m)
|
||||
for i, b in ipairs(btns) do
|
||||
btn(imp, px + pad, cy, pw - 2 * pad, m.btnH, "modheadact-" .. i, b.label, {
|
||||
kind = b.kind or "ghost", font = "small",
|
||||
enabled = b.enabled,
|
||||
action = function()
|
||||
imp._modHeaderActionsPopup = nil
|
||||
b.action()
|
||||
@@ -3737,6 +3748,7 @@ end
|
||||
|
||||
local function buildSettingsModal(imp, m)
|
||||
local model = imp._settings
|
||||
local SaveData = require("src.core.SaveData")
|
||||
local pad = math.floor(18 * m.s)
|
||||
local w = math.floor(640 * m.s)
|
||||
local h = math.floor(math.min(m.H - 2 * m.pad, m.H * 0.9))
|
||||
@@ -3826,6 +3838,8 @@ local function buildSettingsModal(imp, m)
|
||||
item.header)
|
||||
else
|
||||
local row = item.row
|
||||
local rowEnabled = not row.safeModeBlocked
|
||||
or not SaveData.isSafeMode(model.opts)
|
||||
local key = "set-" .. i
|
||||
Kit.card(px + pad, ry, pw - 2 * pad, rowH, "hairline")
|
||||
local ix = px + pad + math.floor(12 * m.s)
|
||||
@@ -3855,6 +3869,7 @@ local function buildSettingsModal(imp, m)
|
||||
ctlY + (m.btnH - Kit.textHeight("small")) / 2, PAL.detail)
|
||||
btn(imp, rx - ew, ctlY, ew, m.btnH,
|
||||
key .. "-edit", Strings("Edit"), { kind = "accent", font = "small",
|
||||
enabled = rowEnabled,
|
||||
action = function()
|
||||
imp._settingsText = { row = row, text = tostring(row.value() or ""),
|
||||
maxLen = row.editText.maxLen }
|
||||
@@ -3863,12 +3878,14 @@ local function buildSettingsModal(imp, m)
|
||||
elseif row.action then
|
||||
-- A plain action row (Reset rebinds, Touch controls): the whole right
|
||||
-- side is one button rather than a value ladder.
|
||||
local aw = Kit.textWidth("small", row.actionLabel or Strings("Run"))
|
||||
local actionLabel = type(row.actionLabel) == "function"
|
||||
and row.actionLabel() or row.actionLabel or Strings("Run")
|
||||
local aw = Kit.textWidth("small", actionLabel)
|
||||
+ math.floor(24 * m.s)
|
||||
Kit.text("small", Kit.ellipsize("small", row.label,
|
||||
labelW or (inner - aw - math.floor(12 * m.s))), ix, labelY, PAL.text)
|
||||
btn(imp, rx - aw, ctlY, aw, m.btnH,
|
||||
key .. "-act", row.actionLabel or Strings("Run"), {
|
||||
key .. "-act", actionLabel, {
|
||||
kind = row.danger and "danger" or "ghost", font = "small",
|
||||
action = function()
|
||||
if row.action() ~= false then model.save() end
|
||||
@@ -3884,12 +3901,14 @@ local function buildSettingsModal(imp, m)
|
||||
or valW
|
||||
btn(imp, rx - stepW, ctlY, stepW, m.btnH,
|
||||
key .. "-next", ">", { font = "small",
|
||||
enabled = rowEnabled,
|
||||
action = function() if row.step and row.step(1) then model.save() end end })
|
||||
Kit.textCenter("small", Kit.ellipsize("small", tostring(row.value()), vw),
|
||||
rx - stepW - vw, ctlY + (m.btnH - Kit.textHeight("small")) / 2, vw,
|
||||
PAL.heading)
|
||||
btn(imp, rx - stepW - vw - stepW, ctlY, stepW,
|
||||
m.btnH, key .. "-prev", "<", { font = "small",
|
||||
enabled = rowEnabled,
|
||||
action = function() if row.step and row.step(-1) then model.save() end end })
|
||||
end
|
||||
end
|
||||
@@ -4046,6 +4065,7 @@ local function buildDepResolverModal(imp, m)
|
||||
local bw = Kit.textWidth("small", btnLabel) + math.floor(20 * m.s)
|
||||
btn(imp, place(bw), ly, bw, chipH, "dep-dis-" .. i, btnLabel, {
|
||||
kind = "warn", font = "small",
|
||||
enabled = not imp.safeMode,
|
||||
action = function()
|
||||
local LauncherMods = require("src.mods.LauncherMods")
|
||||
LauncherMods.setEnabled(dep.id, false, imp.modScope)
|
||||
|
||||
@@ -136,6 +136,9 @@ local VERSION_REQUIRED_FILES_OVERRIDE = {
|
||||
-- costs nothing on a current cache and is the difference between every
|
||||
-- trainer battle opening with a picture and opening with none.
|
||||
"assets/generated/battle/trainers/falkner.png",
|
||||
-- BattleStart_TrainerHuds cannot draw its party rows from a cache made
|
||||
-- before the four ball tiles were extracted (#1502).
|
||||
"assets/generated/battle/hud/balls.png",
|
||||
"assets/generated/audio/programs.bin",
|
||||
},
|
||||
}
|
||||
@@ -326,6 +329,32 @@ function RomImporter.isReady(version)
|
||||
return marker == markerFor(version) and allRequiredFilesExist(version)
|
||||
end
|
||||
|
||||
function RomImporter.syncAndroidShortcuts(activeVersion)
|
||||
if not (love.system and love.system.getOS and love.system.getOS() == "Android"
|
||||
and love.system.updateShortcuts) then
|
||||
return false
|
||||
end
|
||||
|
||||
local allVersions = { "red", "blue", "yellow", "gold" }
|
||||
local ready = {}
|
||||
local seen = {}
|
||||
|
||||
if activeVersion and RomImporter.isReady(activeVersion) then
|
||||
table.insert(ready, activeVersion)
|
||||
seen[activeVersion] = true
|
||||
end
|
||||
|
||||
for _, v in ipairs(allVersions) do
|
||||
if not seen[v] and RomImporter.isReady(v) then
|
||||
table.insert(ready, v)
|
||||
seen[v] = true
|
||||
if #ready >= 4 then break end
|
||||
end
|
||||
end
|
||||
|
||||
return love.system.updateShortcuts(ready)
|
||||
end
|
||||
|
||||
-- Load the import manifest for a version and confirm it matches that ROM.
|
||||
local function sha1(data)
|
||||
local digest = love.data.hash("sha1", data)
|
||||
@@ -1393,6 +1422,7 @@ function RomImporter.new(onComplete, opts)
|
||||
self.romName[version] = "pokemon_" .. info.id
|
||||
.. ((info.id == "yellow" or info.id == "gold") and ".gbc" or ".gb")
|
||||
end
|
||||
RomImporter.syncAndroidShortcuts()
|
||||
self:_applyLastVersionTab()
|
||||
self:_queueBaseRomScan()
|
||||
|
||||
@@ -1787,6 +1817,7 @@ function RomImporter:_completeImport(version, prefix, displayName)
|
||||
self.workState = "complete"
|
||||
self.completeVersion = version
|
||||
self.status = "Ready"
|
||||
RomImporter.syncAndroidShortcuts(version)
|
||||
-- NX launcher stays put: keep the imports/ cleanup hint instead of
|
||||
-- overwriting it with a "Starting…" line that never boots from here.
|
||||
if self.launcher and self.isNX and type(displayName) == "string" then
|
||||
@@ -3598,6 +3629,10 @@ function RomImporter:_openSettings()
|
||||
-- The tab rides along: the editor persists the layout into that game's own
|
||||
-- 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()
|
||||
@@ -3615,11 +3650,13 @@ function RomImporter:_openSettings()
|
||||
-- The tab the gear was opened on decides the row set: Gold reads a
|
||||
-- different option block entirely, and offering it Gen 1's rows meant a
|
||||
-- dozen controls that changed nothing (see LauncherSettings.gen2Rows).
|
||||
local version = self.tab
|
||||
local ok, model = pcall(function()
|
||||
return require("src.import.LauncherSettings").open(hooks, version)
|
||||
end)
|
||||
if ok and model then self._settings = model end
|
||||
if ok and model then
|
||||
self._settings = model
|
||||
self._settingsSafeModeAtOpen = require("src.core.SaveData").isSafeMode(model.opts)
|
||||
end
|
||||
end
|
||||
|
||||
-- Quit from the launcher's own X. It goes through love.event.quit so main.lua's
|
||||
@@ -3630,8 +3667,38 @@ function RomImporter:_quitApp()
|
||||
end
|
||||
|
||||
function RomImporter:_closeSettings()
|
||||
if self._settings then self._settings.save() end
|
||||
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:_reportIssue(options, version)
|
||||
local ok, IssueReport = pcall(require, "src.core.IssueReport")
|
||||
if not ok then
|
||||
self.modNotice = { ok = false, text = "Could not prepare the issue report." }
|
||||
return false
|
||||
end
|
||||
local opened, url, reason = IssueReport.open(options, {
|
||||
version = version,
|
||||
mods = self.mods,
|
||||
})
|
||||
if not opened then
|
||||
self.modNotice = { 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
|
||||
return true
|
||||
end
|
||||
|
||||
function RomImporter:_commitSettingsText()
|
||||
@@ -3971,6 +4038,8 @@ end
|
||||
-- so a still list costs nothing after the first paint.
|
||||
function RomImporter:_refreshMods()
|
||||
local LauncherMods = require("src.mods.LauncherMods")
|
||||
local SaveData = require("src.core.SaveData")
|
||||
self.safeMode = SaveData.isSafeMode(SaveData.loadOptions())
|
||||
-- Once per session, ahead of the first listing: pull in any mod the player
|
||||
-- unzipped beside the executable, which an ordinary (non-portable) install
|
||||
-- has no way to read. It happens here rather than behind a button because
|
||||
@@ -4110,6 +4179,10 @@ end
|
||||
-- so that game's checkbox and status chips reflect the new resolution.
|
||||
-- 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." }
|
||||
return
|
||||
end
|
||||
local LauncherMods = require("src.mods.LauncherMods")
|
||||
local cur, experimental = false, false
|
||||
for _, m in ipairs(self.mods or {}) do
|
||||
@@ -4150,6 +4223,10 @@ end
|
||||
-- must not be the way around it. Disabling needs no confirm -- it is the
|
||||
-- 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." }
|
||||
return
|
||||
end
|
||||
local LauncherMods = require("src.mods.LauncherMods")
|
||||
local ids, experimental = {}, false
|
||||
for _, m in ipairs(self.mods or {}) do
|
||||
|
||||
Reference in New Issue
Block a user