mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-17 19:24:01 +02:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cecb24bb0e | |||
| b99b497893 | |||
| e16a45d6ba | |||
| 0e29046c31 | |||
| 5cbada493a | |||
| 4c79db3d5d | |||
| c7e46b6563 |
@@ -78,6 +78,14 @@ function love.conf(t)
|
|||||||
-- player to copy their ROM there instead of needing a native file
|
-- player to copy their ROM there instead of needing a native file
|
||||||
-- picker (LOVE 11.5 on Android has none -- see src/import/RomImporter.lua).
|
-- picker (LOVE 11.5 on Android has none -- see src/import/RomImporter.lua).
|
||||||
t.externalstorage = osName == "Android"
|
t.externalstorage = osName == "Android"
|
||||||
|
-- LOVE 11.x exposes the phone's accelerometer as a 3-axis joystick by
|
||||||
|
-- default. Gravity keeps one axis pinned near +/-1.0 whenever the phone
|
||||||
|
-- is held upright, so it streams past-deadzone joystickaxis events that
|
||||||
|
-- both hid the touch overlay every instant (it reads as "a controller is
|
||||||
|
-- in use") and, via the generic-joystick axis mapping (#459), walked the
|
||||||
|
-- player around by tilt. Nothing in the game reads the accelerometer,
|
||||||
|
-- so drop the device entirely (#468).
|
||||||
|
t.accelerometerjoystick = false
|
||||||
else
|
else
|
||||||
t.window.resizable = true
|
t.window.resizable = true
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -183,6 +183,7 @@ M.BILLS_HOUSE = {
|
|||||||
overworld = ow },
|
overworld = ow },
|
||||||
"BILLS_HOUSE", "BILLSHOUSE_BILL_POKEMON")
|
"BILLS_HOUSE", "BILLSHOUSE_BILL_POKEMON")
|
||||||
game.save.flags.EVENT_BILL_SAID_USE_CELL_SEPARATOR = true
|
game.save.flags.EVENT_BILL_SAID_USE_CELL_SEPARATOR = true
|
||||||
|
require("src.world.PikachuFollower").onBillEnteredMachine(game, ow)
|
||||||
done()
|
done()
|
||||||
end
|
end
|
||||||
if ow.player.facing == "down" then
|
if ow.player.facing == "down" then
|
||||||
@@ -273,6 +274,9 @@ M.BILLS_HOUSE = {
|
|||||||
Commands.hide_object(ctx, "BILLS_HOUSE", "BILLSHOUSE_BILL1")
|
Commands.hide_object(ctx, "BILLS_HOUSE", "BILLSHOUSE_BILL1")
|
||||||
Commands.show_object(ctx, "BILLS_HOUSE", "BILLSHOUSE_BILL2")
|
Commands.show_object(ctx, "BILLS_HOUSE", "BILLSHOUSE_BILL2")
|
||||||
end
|
end
|
||||||
|
if not flags.EVENT_MET_BILL_2 then
|
||||||
|
require("src.world.PikachuFollower").onBillsHouseEnter(game, ow)
|
||||||
|
end
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -491,21 +491,36 @@ function Game:gamepadaxis(joystick, axis, value)
|
|||||||
Input:gamepadaxis(joystick, axis, value)
|
Input:gamepadaxis(joystick, axis, value)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- conf.lua turns the mobile accelerometer-joystick off (#468), but guard the
|
||||||
|
-- generic joystick path anyway: any sensor-style device that still reaches us
|
||||||
|
-- has gravity pinning an axis past the deadzone, which would hide the touch
|
||||||
|
-- overlay every instant and steer the player by tilt through the axis-1/2
|
||||||
|
-- mapping (#459). Real controllers arrive as SDL gamepads or named sticks,
|
||||||
|
-- never as "* Accelerometer".
|
||||||
|
local function isAccelerometer(joystick)
|
||||||
|
local name = joystick and joystick.getName and joystick:getName()
|
||||||
|
return name ~= nil and name:lower():find("accelerometer", 1, true) ~= nil
|
||||||
|
end
|
||||||
|
|
||||||
function Game:joystickpressed(joystick, button)
|
function Game:joystickpressed(joystick, button)
|
||||||
|
if isAccelerometer(joystick) then return end
|
||||||
TouchControls:noteGamepad()
|
TouchControls:noteGamepad()
|
||||||
Input:joystickpressed(joystick, button)
|
Input:joystickpressed(joystick, button)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Game:joystickreleased(joystick, button)
|
function Game:joystickreleased(joystick, button)
|
||||||
|
if isAccelerometer(joystick) then return end
|
||||||
Input:joystickreleased(joystick, button)
|
Input:joystickreleased(joystick, button)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Game:joystickaxis(joystick, axis, value)
|
function Game:joystickaxis(joystick, axis, value)
|
||||||
|
if isAccelerometer(joystick) then return end
|
||||||
if math.abs(value) > 0.5 then TouchControls:noteGamepad() end
|
if math.abs(value) > 0.5 then TouchControls:noteGamepad() end
|
||||||
Input:joystickaxis(joystick, axis, value)
|
Input:joystickaxis(joystick, axis, value)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Game:joystickhat(joystick, hat, direction)
|
function Game:joystickhat(joystick, hat, direction)
|
||||||
|
if isAccelerometer(joystick) then return end
|
||||||
if direction ~= "c" then TouchControls:noteGamepad() end
|
if direction ~= "c" then TouchControls:noteGamepad() end
|
||||||
Input:joystickhat(joystick, hat, direction)
|
Input:joystickhat(joystick, hat, direction)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -2093,6 +2093,7 @@ function OverworldState:billsHouseBillExits()
|
|||||||
local Commands = require("src.script.Commands")
|
local Commands = require("src.script.Commands")
|
||||||
local ctx = { game = Game, save = Game.save, overworld = self }
|
local ctx = { game = Game, save = Game.save, overworld = self }
|
||||||
Commands.show_object(ctx, "BILLS_HOUSE", "BILLSHOUSE_BILL1")
|
Commands.show_object(ctx, "BILLS_HOUSE", "BILLSHOUSE_BILL1")
|
||||||
|
require("src.world.PikachuFollower").onBillExitedMachine(Game, self)
|
||||||
local function done()
|
local function done()
|
||||||
Game.save.flags.EVENT_MET_BILL = true
|
Game.save.flags.EVENT_MET_BILL = true
|
||||||
Game.save.flags.EVENT_MET_BILL_2 = true
|
Game.save.flags.EVENT_MET_BILL_2 = true
|
||||||
|
|||||||
@@ -189,6 +189,9 @@ function PikachuFollower.current(ow)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function PikachuFollower.onMapEntered(game, ow, opts)
|
function PikachuFollower.onMapEntered(game, ow, opts)
|
||||||
|
-- Bill's House owns a short scripted scene that deliberately keeps
|
||||||
|
-- Pikachu off the normal trailing loop. A new map instance ends it.
|
||||||
|
ow.pikachuBillsScene = nil
|
||||||
remove(ow)
|
remove(ow)
|
||||||
if not shouldSpawn(game, ow) then return end
|
if not shouldSpawn(game, ow) then return end
|
||||||
-- opts.keepPikachu is the follower a connection crossing kept alive:
|
-- opts.keepPikachu is the follower a connection crossing kept alive:
|
||||||
@@ -393,6 +396,7 @@ end
|
|||||||
-- (pikachu_follow.asm keeps it one walk step behind)
|
-- (pikachu_follow.asm keeps it one walk step behind)
|
||||||
function PikachuFollower.update(game, ow)
|
function PikachuFollower.update(game, ow)
|
||||||
if ow.pikaHop then return end -- the counter hop owns the follower (#417)
|
if ow.pikaHop then return end -- the counter hop owns the follower (#417)
|
||||||
|
if ow.pikachuBillsScene then return end
|
||||||
local npc = findFollower(ow)
|
local npc = findFollower(ow)
|
||||||
if not npc then
|
if not npc then
|
||||||
if shouldSpawn(game, ow) then PikachuFollower.onMapEntered(game, ow) end
|
if shouldSpawn(game, ow) then PikachuFollower.onMapEntered(game, ow) end
|
||||||
@@ -741,6 +745,68 @@ function PikachuFollower.picLift(emote)
|
|||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Bill's House has three map-scripted Yellow companion beats
|
||||||
|
-- (BillsHouseScript0/2/5): Pikachu walks over to investigate Bill, waits at
|
||||||
|
-- the cell separator, then reacts when Bill reappears. Keep it at the
|
||||||
|
-- machine until this map instance is discarded, just like the cartridge's
|
||||||
|
-- disabled following state.
|
||||||
|
local function billsHouseEmotion(game, ow, npc, bubble)
|
||||||
|
local Sprites = require("src.pokemon.Sprites")
|
||||||
|
ow.emote = {
|
||||||
|
npc = npc, frames = 50, bubble = bubbleIndex(game, bubble) or false,
|
||||||
|
pikaPic = Sprites.path(game.data, "PIKACHU", "front",
|
||||||
|
{ kind = "overworld" }),
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function movePikachu(ow, npc, steps, onDone)
|
||||||
|
npc.goalX, npc.goalY = nil, nil
|
||||||
|
idleReset(npc)
|
||||||
|
local function nextStep(i)
|
||||||
|
local step = steps[i]
|
||||||
|
if not step then
|
||||||
|
if onDone then onDone() end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
ow:scriptMove(npc, step[1], step[2], function() nextStep(i + 1) end)
|
||||||
|
end
|
||||||
|
nextStep(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
function PikachuFollower.onBillsHouseEnter(game, ow)
|
||||||
|
if not (GameVersion.isYellow() and ow.map and ow.map.id == "BILLS_HOUSE") then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if game.save.flags.EVENT_MET_BILL_2 then return end
|
||||||
|
local npc = findFollower(ow)
|
||||||
|
if not npc then return end
|
||||||
|
ow.pikachuBillsScene = true
|
||||||
|
movePikachu(ow, npc, { { "right", 3 }, { "up", 1 } }, function()
|
||||||
|
billsHouseEmotion(game, ow, npc, "QUESTION_BUBBLE")
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function PikachuFollower.onBillEnteredMachine(game, ow)
|
||||||
|
if not (GameVersion.isYellow() and ow.pikachuBillsScene) then return end
|
||||||
|
local npc = findFollower(ow)
|
||||||
|
if not npc then return end
|
||||||
|
local steps = ow.player.facing == "down"
|
||||||
|
and { { "up", 3 } }
|
||||||
|
or { { "up", 1 }, { "left", 1 }, { "up", 2 }, { "right", 1 } }
|
||||||
|
movePikachu(ow, npc, steps, function()
|
||||||
|
billsHouseEmotion(game, ow, npc, "QUESTION_BUBBLE")
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function PikachuFollower.onBillExitedMachine(game, ow)
|
||||||
|
if not (GameVersion.isYellow() and ow.pikachuBillsScene) then return end
|
||||||
|
local npc = findFollower(ow)
|
||||||
|
if not npc then return end
|
||||||
|
idleReset(npc)
|
||||||
|
npc.facing = "left"
|
||||||
|
billsHouseEmotion(game, ow, npc, "EXCLAMATION_BUBBLE")
|
||||||
|
end
|
||||||
|
|
||||||
-- ---------------------------------------------------------------------
|
-- ---------------------------------------------------------------------
|
||||||
-- PikachuWalksToNurseJoy (engine/pikachu/pikachu_emotions.asm, run by
|
-- PikachuWalksToNurseJoy (engine/pikachu/pikachu_emotions.asm, run by
|
||||||
-- engine/events/pokecenter.asm once the heal is accepted): the companion
|
-- engine/events/pokecenter.asm once the heal is accepted): the companion
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
-- Yellow starts Bill's House with Pikachu's confused reaction. The map
|
||||||
|
-- script owns that one-shot, while PikachuFollower owns the movement.
|
||||||
|
|
||||||
|
package.path = "./?.lua;./?/init.lua;" .. package.path
|
||||||
|
local S = require("tests.harness").suite("parity Yellow Bill's Pikachu")
|
||||||
|
local check = S.check
|
||||||
|
|
||||||
|
local entered = 0
|
||||||
|
local originalFollower = package.loaded["src.world.PikachuFollower"]
|
||||||
|
package.loaded["src.world.PikachuFollower"] = {
|
||||||
|
onBillsHouseEnter = function()
|
||||||
|
entered = entered + 1
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
|
||||||
|
local story = dofile("data/scripts/story.lua")
|
||||||
|
local game = { save = { flags = {} } }
|
||||||
|
story.BILLS_HOUSE.onEnter(game, {})
|
||||||
|
check(entered == 1,
|
||||||
|
"entering Bill's House before meeting Bill starts Pikachu's reaction")
|
||||||
|
|
||||||
|
entered = 0
|
||||||
|
game.save.flags.EVENT_MET_BILL_2 = true
|
||||||
|
story.BILLS_HOUSE.onEnter(game, {})
|
||||||
|
check(entered == 0,
|
||||||
|
"Pikachu's Bill reaction does not replay after Bill is met")
|
||||||
|
|
||||||
|
package.loaded["src.world.PikachuFollower"] = originalFollower
|
||||||
|
|
||||||
|
local GameVersion = require("src.core.GameVersion")
|
||||||
|
local PikachuFollower = require("src.world.PikachuFollower")
|
||||||
|
GameVersion.set("yellow")
|
||||||
|
|
||||||
|
local npc = {
|
||||||
|
pikachuFollower = true, cellX = 3, cellY = 8, px = 48, py = 128,
|
||||||
|
facing = "up",
|
||||||
|
}
|
||||||
|
local moves = {}
|
||||||
|
local yellowGame = {
|
||||||
|
save = { flags = {} },
|
||||||
|
data = {
|
||||||
|
pokemon = { PIKACHU = { spriteFront = "pikachu.png" } },
|
||||||
|
field = { emotionBubbles = {
|
||||||
|
bubbles = {
|
||||||
|
{ name = "QUESTION_BUBBLE" }, { name = "EXCLAMATION_BUBBLE" },
|
||||||
|
},
|
||||||
|
} },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
local ow = {
|
||||||
|
map = { id = "BILLS_HOUSE" }, npcs = { npc }, entities = { npc },
|
||||||
|
player = { cellX = 3, cellY = 7 },
|
||||||
|
scriptMove = function(_, entity, dir, tiles, onDone)
|
||||||
|
moves[#moves + 1] = { entity = entity, dir = dir, tiles = tiles,
|
||||||
|
onDone = onDone }
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
|
||||||
|
PikachuFollower.onBillsHouseEnter(yellowGame, ow)
|
||||||
|
check(ow.pikachuBillsScene and #moves == 1
|
||||||
|
and moves[1].entity == npc and moves[1].dir == "right"
|
||||||
|
and moves[1].tiles == 3,
|
||||||
|
"Bill's House entry parks Pikachu and walks it to Bill")
|
||||||
|
moves[1].onDone()
|
||||||
|
check(#moves == 2 and moves[2].dir == "up" and moves[2].tiles == 1,
|
||||||
|
"Pikachu finishes its cartridge entry route beside Bill")
|
||||||
|
moves[2].onDone()
|
||||||
|
check(ow.emote and ow.emote.bubble == 1,
|
||||||
|
"Pikachu shows its confused reaction after reaching Bill")
|
||||||
|
|
||||||
|
ow.player.facing = "down"
|
||||||
|
PikachuFollower.onBillEnteredMachine(yellowGame, ow)
|
||||||
|
check(#moves == 3 and moves[3].dir == "up" and moves[3].tiles == 3,
|
||||||
|
"Pikachu walks to the cell separator after Bill enters it")
|
||||||
|
moves[3].onDone()
|
||||||
|
check(ow.emote and ow.emote.bubble == 1,
|
||||||
|
"Pikachu wonders at the cell separator")
|
||||||
|
|
||||||
|
npc.goalX, npc.goalY = 9, 9
|
||||||
|
PikachuFollower.update(yellowGame, ow)
|
||||||
|
check(npc.goalX == 9 and npc.goalY == 9,
|
||||||
|
"Pikachu stays parked in Bill's House during the scene")
|
||||||
|
|
||||||
|
PikachuFollower.onBillExitedMachine(yellowGame, ow)
|
||||||
|
check(ow.emote and ow.emote.bubble == 2 and npc.facing == "left",
|
||||||
|
"Pikachu reacts when Bill comes back out")
|
||||||
|
|
||||||
|
GameVersion.set("red")
|
||||||
|
S.finish()
|
||||||
@@ -431,6 +431,34 @@ do
|
|||||||
for _, bak in ipairs(FsIo.globPrefix(tmpPath .. ".bak-")) do os.remove(bak) end
|
for _, bak in ipairs(FsIo.globPrefix(tmpPath .. ".bak-")) do os.remove(bak) end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
do
|
||||||
|
-- #476: on Android a high-DPI 1560x720 capture can leave the editor with
|
||||||
|
-- only a compact logical viewport. The Items picker must not hand a
|
||||||
|
-- negative list height to love.graphics.setScissor in that layout.
|
||||||
|
local tmpPath = os.tmpname() .. "-items-compact-save.lua"
|
||||||
|
local f = io.open(tmpPath, "wb")
|
||||||
|
f:write(SaveData.encode(SaveData.newGame()))
|
||||||
|
f:close()
|
||||||
|
|
||||||
|
local oldDimensions = love.graphics.getDimensions
|
||||||
|
local oldScissor = love.graphics.setScissor
|
||||||
|
love.graphics.getDimensions = function() return 520, 240 end
|
||||||
|
love.graphics.setScissor = function(_, _, width, height)
|
||||||
|
if width and (width < 0 or height < 0) then
|
||||||
|
error("Can't set scissor with negative width and/or height.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
App.load(tmpPath, { version = "red" })
|
||||||
|
App.getState().tab = "items"
|
||||||
|
local ok, err = pcall(App.draw)
|
||||||
|
check(ok, "the Items tab draws in a compact Android viewport: " .. tostring(err))
|
||||||
|
love.graphics.getDimensions = oldDimensions
|
||||||
|
love.graphics.setScissor = oldScissor
|
||||||
|
|
||||||
|
os.remove(tmpPath)
|
||||||
|
for _, bak in ipairs(FsIo.globPrefix(tmpPath .. ".bak-")) do os.remove(bak) end
|
||||||
|
end
|
||||||
|
|
||||||
do
|
do
|
||||||
-- Whole-editor smoke test: every tab has to survive a real headless draw,
|
-- Whole-editor smoke test: every tab has to survive a real headless draw,
|
||||||
-- which is what catches a layout that divides by a nil font metric or
|
-- which is what catches a layout that divides by a nil font metric or
|
||||||
|
|||||||
@@ -3278,6 +3278,7 @@ runSuites(orderedGlob("tests/parity_*.lua", {
|
|||||||
"tests/parity_static.lua", "tests/parity_trashcans.lua",
|
"tests/parity_static.lua", "tests/parity_trashcans.lua",
|
||||||
"tests/parity_hof.lua", "tests/parity_trade_gift.lua",
|
"tests/parity_hof.lua", "tests/parity_trade_gift.lua",
|
||||||
"tests/parity_yellow_trades.lua",
|
"tests/parity_yellow_trades.lua",
|
||||||
|
"tests/parity_yellow_bills_pikachu.lua",
|
||||||
"tests/parity_intro.lua", "tests/parity_tilt.lua",
|
"tests/parity_intro.lua", "tests/parity_tilt.lua",
|
||||||
"tests/parity_gbcfx.lua",
|
"tests/parity_gbcfx.lua",
|
||||||
}))
|
}))
|
||||||
|
|||||||
@@ -381,13 +381,22 @@ end
|
|||||||
|
|
||||||
-- Clip drawing to a rect (list bodies). No-ops under the headless stub.
|
-- Clip drawing to a rect (list bodies). No-ops under the headless stub.
|
||||||
function Kit.pushClip(x, y, w, h)
|
function Kit.pushClip(x, y, w, h)
|
||||||
if G and G.setScissor then
|
-- A compact mobile viewport can leave a panel with no room for a list.
|
||||||
G.setScissor(math.floor(x), math.floor(y), math.ceil(w), math.ceil(h))
|
-- LÖVE rejects negative scissor dimensions, so treat an exhausted clip
|
||||||
|
-- region as empty instead of passing invalid geometry through to it.
|
||||||
|
Kit._clipActive = G and G.setScissor ~= nil
|
||||||
|
if Kit._clipActive then
|
||||||
|
if w <= 0 or h <= 0 then
|
||||||
|
G.setScissor(0, 0, 0, 0)
|
||||||
|
else
|
||||||
|
G.setScissor(math.floor(x), math.floor(y), math.ceil(w), math.ceil(h))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Kit.popClip()
|
function Kit.popClip()
|
||||||
if G and G.setScissor then G.setScissor() end
|
if Kit._clipActive and G and G.setScissor then G.setScissor() end
|
||||||
|
Kit._clipActive = false
|
||||||
end
|
end
|
||||||
|
|
||||||
return Kit
|
return Kit
|
||||||
|
|||||||
Reference in New Issue
Block a user