Files
gen1recomp/main.lua
T
bryanthaboi 15029d811f bug squashing
# Closed issues

CLOSES #17: Incorrect Character Visuals (Only with GBC filter)
CLOSES #23: Could you allow the player to change the order of the moves
CLOSES #24: Evolution music not playing during evolution
CLOSES #26: Standing on door glitch
CLOSES #27: Battle Intro text automatically continues
CLOSES #29: Visual bug when zoomed out
CLOSES #32: Team Rocket recruiter doesn't battle with you unless you speak with him first
CLOSES #33: Developer/Debug Console
CLOSES #35: Professor Oak's introduction Inaccuracies
CLOSES #36: Missing Pokemon Dex entries when picking starter + Rival Pathing issues
CLOSES #39: Guy who stops player from skipping brock doesn't bring you to brock's gym + doesn't leave once you've beaten brock
CLOSES #40: Bill cutscene is broken
CLOSES #41: Ticket guy failing to be a Ticket guy
CLOSES #42: S.S. anne odd behavior + Missing sailing away animation
CLOSES #43: Dig Attack animation appears to be glitched
CLOSES #44: Pokeball flashing doesn't appear to be accurate
CLOSES #45: Inaccurate Cut Animation
CLOSES #46: Dugtrio i caught in diglett cave has two of the same move
CLOSES #47: Rival ignores player in Lavender Tower
CLOSES #48: Healing pad in lavender tower does not function
CLOSES #49: Incorrect dialogue with parched security guard
CLOSES #50: (Game Breaking!) Rocket grunt guarding poster refuses to move
CLOSES #51: Badges showing up as items I can deposit in PC
CLOSES #52: Visual bug on Celadon Department Store roof (Red Filter)
CLOSES #54: Visual issue on route 15 + Fuchsia City
CLOSES #56: Running animation missing
CLOSES #57: Safari Zone does not display steps while you are inside of it
CLOSES #58: (Game Breaking!) Softlock at cycling road gate
CLOSES #59: Bike visual issues
CLOSES #60: Cycling road not forcing you to get on your bike
CLOSES #61: No keycard doors in Silph Co.
CLOSES #63: Missing teleporter animation
CLOSES #64: Inaccurate spinning
CLOSES #65: Reimplement unused Silph Co. Chief and Professor Oak trainer battles
2026-07-22 08:18:13 -04:00

239 lines
7.2 KiB
Lua

-- Native LÖVE2D port of Pokemon Red. A packaged build creates its private
-- game-data cache from a user-provided ROM on first boot.
--
-- Set POKEPORT_EDITOR=1 or pass `--editor` to `love .` to boot the save
-- editor tool (tools/save-editor/) instead of the game.
local editorMode = os.getenv("POKEPORT_EDITOR") == "1" or POKEPORT_EDITOR_MODE == true
local Game, EditorApp, Importer
local autopilot -- optional scripted-input dev tool (tests/autopilot.lua)
local driverCo -- optional frame-driver (POKEPORT_DRIVER=file.lua): a
-- coroutine that receives `Game` and yields once per
-- frame; used headless (xvfb) for scripted screenshots
-- --speed N / POKEPORT_SPEED=N: run the logic clock N times faster without
-- touching audio (src/core/GameSpeed.lua). Overrides the saved option so a
-- bot or screenshot run is not at the mercy of the player's last choice.
local speedOverride = tonumber(os.getenv("POKEPORT_SPEED"))
-- How many times to run a scripted act+step loop per rendered frame. Only
-- scripted runs use this; interactive play fast-forwards through
-- Game.speedOverride / the GAME SPEED option instead.
local function scriptedIterations()
if not (autopilot or driverCo) then return 1 end
return math.max(1, math.floor(require("src.core.GameSpeed").clamp(speedOverride)))
end
local function bootGame()
Game = require("src.core.Game")
Game:load()
if os.getenv("POKEPORT_AUTOPILOT") then
autopilot = require("tests.autopilot")
end
local driverPath = os.getenv("POKEPORT_DRIVER")
if driverPath then
local fn = assert(loadfile(driverPath))()
driverCo = coroutine.create(fn)
end
-- After the two above are known: a scripted run drives the multiplier
-- from love.update's loop, so the in-engine one must stay at 1 or the
-- two would compound (10x10 = 100 steps per observation).
Game.speedOverride = (autopilot or driverCo) and 1 or speedOverride
end
function love.load(args)
local savePath
for i, a in ipairs(args or {}) do
if a == "--editor" then
editorMode = true
elseif a == "--developer" then
_G.POKEPORT_DEV_MODE = true
elseif a == "--save" and args[i + 1] and args[i + 1] ~= "" then
savePath = args[i + 1]
elseif a == "--speed" and tonumber(args[i + 1]) then
speedOverride = tonumber(args[i + 1])
end
end
love.graphics.setDefaultFilter("nearest", "nearest")
if editorMode then
package.path = love.filesystem.getSource() .. "/tools/save-editor/?.lua;"
.. love.filesystem.getSource() .. "/tools/save-editor/panels/?.lua;"
.. package.path
EditorApp = require("App")
EditorApp.load(savePath)
return
end
local RomImporter = require("src.import.RomImporter")
if os.getenv("POKEPORT_FORCE_IMPORT") == "1" or not RomImporter.isReady() then
Importer = RomImporter.new(function()
if os.getenv("POKEPORT_IMPORT_ONLY") == "1" then
love.event.quit()
return
end
Importer = nil
bootGame()
end)
local importPath = os.getenv("POKEPORT_IMPORT_ROM")
if importPath then Importer:startPath(importPath) end
return
end
bootGame()
end
function love.update(dt)
if editorMode then return EditorApp.update(dt) end
if Importer then return Importer:update(dt) end
-- Scripted runs (autopilot / POKEPORT_DRIVER) observe and act exactly
-- once per Game:update, so they must keep a 1:1 relationship with the
-- logic step. Fast-forwarding them by scaling the step inside
-- Game:update would run N steps per observation: a held direction walks
-- through all N, the player slides past the waypoint, and the script
-- re-plans from an overshot cell. So iterate the whole act+step loop
-- instead -- same script, just more of it per rendered frame.
local iterations = scriptedIterations()
if autopilot then
for _ = 1, iterations do
autopilot.update()
Game:update(1 / 60) -- deterministic stepping for the autopilot
end
return
end
if driverCo then
for _ = 1, iterations do
local ok, err = coroutine.resume(driverCo, Game)
if not ok then
print("driver error: " .. tostring(err))
love.event.quit(1)
return
end
if coroutine.status(driverCo) == "dead" then
love.event.quit()
return
end
Game:update(1 / 60)
end
return
end
Game:update(dt)
end
function love.draw()
if editorMode then return EditorApp.draw() end
if Importer then return Importer:draw() end
Game:draw()
-- frame capture requested by a driver
if Game.capturePath then
local path = Game.capturePath
Game.capturePath = nil
love.graphics.captureScreenshot(function(imagedata)
local fd = imagedata:encode("png")
local f = io.open(path, "wb")
if f then
f:write(fd:getString())
f:close()
end
end)
end
end
function love.keypressed(key, scancode, isrepeat)
if editorMode then return EditorApp.keypressed(key) end
if Importer then return Importer:keypressed(key) end
Game:keypressed(key)
end
function love.keyreleased(key)
if editorMode then return end
if Importer then return end
Game:keyreleased(key)
end
function love.gamepadpressed(joystick, button)
if editorMode then return end
if Importer then return end
Game:gamepadpressed(joystick, button)
end
function love.gamepadreleased(joystick, button)
if editorMode then return end
if Importer then return end
Game:gamepadreleased(joystick, button)
end
function love.gamepadaxis(joystick, axis, value)
if editorMode then return end
if Importer then return end
Game:gamepadaxis(joystick, axis, value)
end
function love.touchpressed(id, x, y, dx, dy, pressure)
if editorMode then return end
if Importer then return Importer:mousepressed(x, y, 1) end
Game:touchpressed(id, x, y)
end
function love.touchmoved(id, x, y, dx, dy, pressure)
if editorMode then return end
if Importer then return end
Game:touchmoved(id, x, y)
end
function love.touchreleased(id, x, y, dx, dy, pressure)
if editorMode then return end
if Importer then return end
Game:touchreleased(id, x, y)
end
function love.wheelmoved(x, y)
if editorMode then
if EditorApp.wheelmoved then return EditorApp.wheelmoved(x, y) end
return
end
if Importer then return end
Game:wheelmoved(x, y)
end
function love.mousepressed(x, y, button)
if Importer then return Importer:mousepressed(x, y, button) end
if editorMode and EditorApp.mousepressed then
return EditorApp.mousepressed(x, y, button)
end
end
function love.mousereleased(x, y, button)
if Importer then return end
if editorMode and EditorApp.mousereleased then
return EditorApp.mousereleased(x, y, button)
end
end
function love.textinput(text)
if Importer then return end
if editorMode and EditorApp.textinput then
return EditorApp.textinput(text)
end
end
function love.quit()
if editorMode and EditorApp.quit then
return EditorApp.quit() -- return true to abort quit
end
pcall(function()
require("src.core.DiscordPresence").shutdown()
end)
end
function love.filedropped(file)
if editorMode and EditorApp and EditorApp.filedropped then
return EditorApp.filedropped(file)
end
if Importer then Importer:filedropped(file) end
end