Files
gen1recomp/src/world/gen2/UnownWords.lua
T
2026-08-26 12:04:06 -04:00

227 lines
7.6 KiB
Lua

-- ../pokecrystal/engine/events/unown_walls.asm:102 DisplayUnownWords, and the
-- Unown alphabet it writes out of ../pokecrystal/constants/charmap.asm:424;
-- :1 HoOhChamber, :13 OmanyteChamber, :54 SpecialAerodactylChamber and :81
-- SpecialKabutoChamber, the four routines that open the chambers' walls.
local Assets = require("src.render.Assets")
local Chrome = require("src.ui.gen2.Chrome")
local Font = require("src.render.Font")
local GbcPalette = require("src.render.GbcPalette")
local Palettes = require("src.world.gen2.Palettes")
local Sound = require("src.core.Sound")
local UnownWords = {}
UnownWords.__index = UnownWords
UnownWords.isOpaque = false
-- engine/tilesets/map_palettes.asm:40, ../pokecrystal/constants/tileset_constants.asm:55
UnownWords.BANK1 = 0x80
UnownWords.BROWN = 6
-- ../pokecrystal/engine/events/unown_walls.asm:225 .YChar, :237 .ZChar, :249 .DashChar
local FIXED = {
[0x60] = { 0x5b, 0x5c, 0x4d, 0x5d },
[0x62] = { 0x4e, 0x4f, 0x5e, 0x5f },
[0x64] = { 0x02, 0x03, 0x03, 0x02 },
}
function UnownWords.square(char)
local fixed = FIXED[char]
if fixed then return fixed[1], fixed[2], fixed[3], fixed[4] end
local base = UnownWords.BANK1 + char
return base, base + 1, base + 0x10, base + 0x11
end
-- ../pokecrystal/engine/events/unown_walls.asm:122-127
function UnownWords.origin(wall)
return (wall.x1 or 0) + 1, (wall.y1 or 0) + 2
end
-- ../pokecrystal/engine/events/unown_walls.asm:119 MenuBox, home/menu.asm:131 GetMenuBoxDims
function UnownWords.boxRect(wall)
local x1, y1 = wall.x1 or 0, wall.y1 or 0
return x1, y1, (wall.x2 or x1) - x1 + 1, (wall.y2 or y1) - y1 + 1
end
-- ../pokecrystal/engine/events/unown_walls.asm:182 _DisplayUnownWords_CopyWord
function UnownWords.layout(wall)
local tx, ty = UnownWords.origin(wall)
local out = {}
for index, char in ipairs(wall.chars or {}) do
local tl, tr, bl, br = UnownWords.square(char)
out[index] = {
tx = tx + (index - 1) * 2, ty = ty,
tl = tl, tr = tr, bl = bl, br = br,
}
end
return out
end
-- ../pokecrystal/constants/event_flags.asm:486-489, the four Crystal-only
-- EVENT_WALL_OPENED_IN_*_CHAMBER bits.
UnownWords.WALL_OPENED = {
HO_OH = 806,
KABUTO = 807,
OMANYTE = 808,
AERODACTYL = 809,
}
-- ../pokecrystal/engine/events/unown_walls.asm:60, :87 -- the
-- GetMapAttributesPointer compare the two non-special routines are gated on.
UnownWords.CHAMBER_MAPS = {
HO_OH = "RUINS_OF_ALPH_HO_OH_CHAMBER",
KABUTO = "RUINS_OF_ALPH_KABUTO_CHAMBER",
OMANYTE = "RUINS_OF_ALPH_OMANYTE_CHAMBER",
AERODACTYL = "RUINS_OF_ALPH_AERODACTYL_CHAMBER",
}
-- ../pokecrystal/engine/events/unown_walls.asm:4, :22
UnownWords.HO_OH = "HO_OH"
UnownWords.WATER_STONE = "WATER_STONE"
-- ../pokecrystal/engine/events/unown_walls.asm:16 EventFlagAction CHECK_FLAG
function UnownWords.wallOpened(events, chamber)
local flag = UnownWords.WALL_OPENED[chamber]
if not (events and flag) then return false end
return events:get(flag) and true or false
end
-- ../pokecrystal/engine/events/unown_walls.asm:8 EventFlagAction SET_FLAG
function UnownWords.openWall(events, chamber)
local flag = UnownWords.WALL_OPENED[chamber]
if not (events and flag) then return false end
if events:get(flag) then return false end
events:set(flag, true)
return true
end
-- ../pokecrystal/engine/events/unown_walls.asm:2-5: wPartySpecies[0], which
-- holds EGG rather than the hatchling's species while a slot is an egg.
function UnownWords.leadIsHoOh(party)
local lead = party and party[1]
if not lead or lead.isEgg then return false end
return lead.species == UnownWords.HO_OH
end
-- ../pokecrystal/engine/events/unown_walls.asm:28-43: wPartyCount down to 1,
-- MON_ITEM on each, so the LAST slot holding one is the one it stops at.
function UnownWords.waterStoneSlot(party)
party = party or {}
for slot = #party, 1, -1 do
local mon = party[slot]
if mon and mon.item == UnownWords.WATER_STONE then return slot end
end
return nil
end
-- ../pokecrystal/engine/events/unown_walls.asm:54, whose carry FlashFunction
-- jumps on at ../pokecrystal/engine/events/overworld.asm:285.
function UnownWords.aerodactylChamber(events, mapId)
if mapId ~= UnownWords.CHAMBER_MAPS.AERODACTYL then return false end
UnownWords.openWall(events, "AERODACTYL")
return true
end
-- ../pokecrystal/engine/events/unown_walls.asm:81, off the escape rope arm of
-- EscapeRopeOrDig (../pokecrystal/engine/events/overworld.asm:809).
function UnownWords.kabutoChamber(events, mapId)
if mapId ~= UnownWords.CHAMBER_MAPS.KABUTO then return false end
UnownWords.openWall(events, "KABUTO")
return true
end
-- ../pokecrystal/constants/script_constants.asm:319 UNOWNWORDS_*
function UnownWords.wallFor(data, scriptVar)
local walls = data and data.gen2EventTables and data.gen2EventTables.unownWalls
if not walls then return nil end
return walls[(scriptVar or 0) + 1]
end
-- opts: wall (an events.unownWalls row), world, onClose()
function UnownWords.new(game, opts)
opts = opts or {}
local self = setmetatable({}, UnownWords)
self.game = game
self.wall = opts.wall
self.world = opts.world
self.onClose = opts.onClose
self.done = false
self.squares = self.wall and UnownWords.layout(self.wall) or {}
return self
end
-- ../pokecrystal/engine/events/unown_walls.asm:155 _DisplayUnownWords_FillAttr
function UnownWords:tileset()
local world = self.world
local map = world and world.map
local def = map and map.def
local tileset = def and world.tilesets and world.tilesets[def.tileset]
if not tileset or not tileset.image then return nil end
if self.atlas == nil then
local ok, image = pcall(Assets.image, tileset.image)
self.atlas = ok and image or false
if self.atlas then self.atlas:setFilter("nearest", "nearest") end
local set = world.palettes
and Palettes.bgSet(world.palettes, def, world.daytime or "DAY")
self.colors = set and set[UnownWords.BROWN] or nil
end
if not self.atlas then return nil end
return tileset, self.atlas
end
-- ../pokecrystal/engine/events/unown_walls.asm:147 PlayClickSFX, :148 CloseWindow
function UnownWords:finish()
if self.done then return end
self.done = true
local game = self.game
if game and game.data then Sound.play(game.data, "SFX_READ_TEXT_2") end
if game and game.stack then game.stack:pop() end
if self.onClose then self.onClose() end
end
function UnownWords:update(_dt)
if self.done then return end
local input = self.game and self.game.input
if not input then return end
if input:wasPressed("a") or input:wasPressed("b") then self:finish() end
end
function UnownWords:draw()
local wall = self.wall
if not wall then return end
local bx, by, bw, bh = UnownWords.boxRect(wall)
Chrome.paletteBox(bx, by, bw, bh)
local tileset, atlas = self:tileset()
if not tileset then return end
local perRow = tileset.tilesPerRow or 16
local aw, ah = atlas:getDimensions()
local quads = {}
local function quadFor(tile)
local q = quads[tile]
if not q then
q = love.graphics.newQuad((tile % perRow) * 8,
math.floor(tile / perRow) * 8, 8, 8, aw, ah)
quads[tile] = q
end
return q
end
local function body()
love.graphics.setColor(1, 1, 1, 1)
for _, square in ipairs(self.squares) do
local x, y = square.tx * 8, square.ty * 8
love.graphics.draw(atlas, quadFor(square.tl), x, y)
love.graphics.draw(atlas, quadFor(square.tr), x + 8, y)
love.graphics.draw(atlas, quadFor(square.bl), x, y + 8)
love.graphics.draw(atlas, quadFor(square.br), x + 8, y + 8)
end
end
if self.colors and GbcPalette.available() then
GbcPalette.with(self.colors, body)
else
body()
end
love.graphics.setColor(0, 0, 0, 1)
end
return UnownWords