mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-25 23:11:15 +02:00
570 lines
21 KiB
Lua
570 lines
21 KiB
Lua
-- The Game Corner's three prize counters, and the coin case they all read.
|
|
--
|
|
-- Unlike the slot machine and card flip, these are not engine screens on the
|
|
-- cart: each counter is a MAP SCRIPT that opens a static menu, and the whole
|
|
-- transaction is script commands the VM already has opcodes for. The three
|
|
-- kinds are:
|
|
--
|
|
-- coins GameCornerCoinVendorScript (engine/events/std_scripts.asm) -- the
|
|
-- attendant who sells 50 coins for ¥1000 and 500 for ¥10000
|
|
-- item the TM counters (CeladonGameCornerPrizeRoomTMVendor,
|
|
-- GoldenrodGameCornerTMVendorScript)
|
|
-- mon the Pokemon counters (CeladonGameCornerPokemonVendor,
|
|
-- GoldenrodGameCornerPrizeMonVendorScript)
|
|
--
|
|
-- Because of that this module is NOT a stack state the game ever pushes and
|
|
-- carries no src/ui/Screens.lua id: talking to a vendor runs the extracted
|
|
-- script through src/script/gen2/Vm.lua, which has every opcode the three
|
|
-- counters use. What is still read from here is the coin case at the top,
|
|
-- which the slot machine and card flip share.
|
|
--
|
|
-- The rest is kept as a transcription of the counters' own tables, because the
|
|
-- ORDER of the checks is what a player notices and what a test can pin down.
|
|
-- Those orders are NOT the same:
|
|
--
|
|
-- item counter coins are checked, then the player is asked, and only then
|
|
-- is the PACK asked for room, so a full bag is discovered
|
|
-- AFTER saying yes
|
|
-- mon counter coins are checked, then the PARTY is checked, and only then
|
|
-- is the player asked -- a full party never gets the question
|
|
-- coin counter the case is checked for room FIRST and the wallet second, so
|
|
-- a full case is reported even when you could not afford it
|
|
--
|
|
-- Coordinates are transcribed, never eyeballed. Every counter's menu header is
|
|
-- `menu_coords 0, 2, W, TEXTBOX_Y - 1` (the coin vendor's is `0, 4, 15, 11`)
|
|
-- with STATICMENU_CURSOR and no STATICMENU_NO_TOP_SPACING, so
|
|
-- GetMenuTextStartCoord (home/menu.asm) puts the first label at box + 1 for the
|
|
-- border, + 1 more row for the top spacing and + 1 more column for the cursor:
|
|
-- (2,4) for the prize counters, (2,6) for the coin vendor, cursor in column 1,
|
|
-- labels two rows apart (PlaceMenuStrings adds 2 * SCREEN_WIDTH per item).
|
|
--
|
|
-- DisplayCoinCaseBalance (engine/menus/menu_2.asm) is the little box every
|
|
-- counter's loop re-prints: Textbox at (11,0) with a 7x1 interior, "COIN" at
|
|
-- (12,0) -- yes, in the border row -- and the four-digit count at (13,1).
|
|
|
|
local Bag = require("src.inventory.Bag")
|
|
local Chrome = require("src.ui.gen2.Chrome")
|
|
local CoinCase = require("src.core.gen2.CoinCase")
|
|
local Save = require("src.core.gen2.Save")
|
|
local Sound = require("src.core.Sound")
|
|
|
|
local PrizeMenu = {}
|
|
PrizeMenu.__index = PrizeMenu
|
|
PrizeMenu.isOpaque = true
|
|
|
|
-- --------------------------------------------------------------- coin case
|
|
--
|
|
-- engine/events/money.asm GiveCoins / TakeCoins / CheckCoins now live in
|
|
-- src/core/gen2/CoinCase.lua: the slot machine and card flip read the same
|
|
-- case, and neither of those is a prize counter, so the case is model, not
|
|
-- menu. These names stay here as aliases because they are this module's
|
|
-- public API and the coin vendor counter is still the thing that calls them.
|
|
PrizeMenu.MAX_COINS = CoinCase.MAX_COINS
|
|
PrizeMenu.coins = CoinCase.coins
|
|
PrizeMenu.giveCoins = CoinCase.giveCoins
|
|
PrizeMenu.takeCoins = CoinCase.takeCoins
|
|
PrizeMenu.HAVE_MORE = CoinCase.HAVE_MORE
|
|
PrizeMenu.HAVE_AMOUNT = CoinCase.HAVE_AMOUNT
|
|
PrizeMenu.HAVE_LESS = CoinCase.HAVE_LESS
|
|
PrizeMenu.checkCoins = CoinCase.checkCoins
|
|
|
|
local function money(save)
|
|
local player = save and save.player
|
|
return (player and player.money) or 0
|
|
end
|
|
|
|
local function setMoney(save, amount)
|
|
local player = save and save.player
|
|
if not player then return end
|
|
player.money = math.max(0, math.min(math.floor(amount or 0), Save.MAX_MONEY))
|
|
end
|
|
|
|
-- ---------------------------------------------------------------- counters
|
|
--
|
|
-- Prices are the map scripts' own EQU block, kept as the constants they are
|
|
-- named after so a reader can find them in the .asm.
|
|
--
|
|
-- CeladonGameCornerPrizeRoom.asm:
|
|
-- TM32 1500 TM29 3500 TM15 7500
|
|
-- MR.MIME 3333 EEVEE 6666 PORYGON 9999
|
|
-- GoldenrodGameCorner.asm:
|
|
-- TM25 5500 TM14 5500 TM38 5500
|
|
-- ABRA 200 SANDSHREW 700 EKANS 700 DRATINI 2100
|
|
--
|
|
-- The Goldenrod Pokemon counter branches on `checkver`: Gold sells EKANS where
|
|
-- Silver sells SANDSHREW, which is the only version difference in the room.
|
|
-- The labels are the menu data's literal strings, spaces and all, because their
|
|
-- padding is what right-aligns the price column.
|
|
PrizeMenu.COUNTERS = {
|
|
CELADON_TM = {
|
|
kind = "item",
|
|
menu = { x = 0, y = 2, w = 16, h = 10 },
|
|
prizes = {
|
|
{ id = "TM_DOUBLE_TEAM", label = "TM32 1500", cost = 1500 },
|
|
{ id = "TM_PSYCHIC_M", label = "TM29 3500", cost = 3500 },
|
|
{ id = "TM_HYPER_BEAM", label = "TM15 7500", cost = 7500 },
|
|
},
|
|
},
|
|
CELADON_MON = {
|
|
kind = "mon",
|
|
menu = { x = 0, y = 2, w = 18, h = 10 },
|
|
prizes = {
|
|
{ id = "MR__MIME", label = "MR.MIME 3333", cost = 3333, level = 15 },
|
|
{ id = "EEVEE", label = "EEVEE 6666", cost = 6666, level = 15 },
|
|
{ id = "PORYGON", label = "PORYGON 9999", cost = 9999, level = 20 },
|
|
},
|
|
},
|
|
GOLDENROD_TM = {
|
|
kind = "item",
|
|
menu = { x = 0, y = 2, w = 16, h = 10 },
|
|
prizes = {
|
|
{ id = "TM_THUNDER", label = "TM25 5500", cost = 5500 },
|
|
{ id = "TM_BLIZZARD", label = "TM14 5500", cost = 5500 },
|
|
{ id = "TM_FIRE_BLAST", label = "TM38 5500", cost = 5500 },
|
|
},
|
|
},
|
|
GOLDENROD_MON = {
|
|
kind = "mon",
|
|
menu = { x = 0, y = 2, w = 18, h = 10 },
|
|
prizes = {
|
|
{ id = "ABRA", label = "ABRA 200", cost = 200, level = 10 },
|
|
{ id = "EKANS", label = "EKANS 700", cost = 700, level = 10,
|
|
silver = { id = "SANDSHREW", label = "SANDSHREW 700" } },
|
|
{ id = "DRATINI", label = "DRATINI 2100", cost = 2100, level = 10 },
|
|
},
|
|
},
|
|
-- GameCornerCoinVendorScript's own menu: `menu_coords 0, 4, 15, TEXTBOX_Y - 1`
|
|
-- and only two entries plus CANCEL. `checkcoins MAX_COINS - 50` with
|
|
-- HAVE_MORE meaning "full" is how the cart asks whether 50 more will fit.
|
|
COIN_VENDOR = {
|
|
kind = "coins",
|
|
menu = { x = 0, y = 4, w = 16, h = 8 },
|
|
prizes = {
|
|
{ amount = 50, cost = 1000, label = " 50 : \xc2\xa51000" },
|
|
{ amount = 500, cost = 10000, label = "500 : \xc2\xa510000" },
|
|
},
|
|
},
|
|
}
|
|
|
|
-- ------------------------------------------------------------------- text
|
|
--
|
|
-- The Celadon room and the Goldenrod room use DIFFERENT wording for the same
|
|
-- five beats, and the coin vendor a third set again. All three are transcribed
|
|
-- from the map scripts / data/text/std_text.asm; none are in the cache's
|
|
-- text.lua, because no script bytecode the extractor walks points at them.
|
|
local function page(...) return { ... } end
|
|
local function pages(...) return { ... } end
|
|
|
|
PrizeMenu.TEXTS = {
|
|
CELADON = {
|
|
intro = pages(page("Welcome!"),
|
|
page("We exchange your", "coins for fabulous"),
|
|
page("prizes!")),
|
|
which = page("Which prize would", "you like?"),
|
|
confirm = function(name)
|
|
return pages(page("OK, so you wanted", ("a %s?"):format(name)))
|
|
end,
|
|
hereYouGo = pages(page("Here you go!")),
|
|
notEnoughCoins = pages(page("You don't have", "enough coins.")),
|
|
noRoom = pages(page("You have no room", "for it.")),
|
|
comeAgain = pages(page("Oh. Please come", "back with coins!")),
|
|
noCoinCase = pages(page("Oh? You don't have", "a COIN CASE.")),
|
|
},
|
|
GOLDENROD = {
|
|
intro = pages(page("Welcome!"),
|
|
page("We exchange your", "game coins for"),
|
|
page("fabulous prizes!")),
|
|
which = page("Which prize would", "you like?"),
|
|
confirm = function(name)
|
|
return pages(page(("%s."):format(name), "Is that right?"))
|
|
end,
|
|
hereYouGo = pages(page("Here you go!")),
|
|
notEnoughCoins = pages(page("Sorry! You need", "more coins.")),
|
|
noRoom = pages(page("Sorry. You can't", "carry any more.")),
|
|
comeAgain = pages(page("OK. Please save", "your coins and"),
|
|
page("come again!")),
|
|
noCoinCase = pages(page("Oh? You don't have", "a COIN CASE.")),
|
|
},
|
|
COIN_VENDOR = {
|
|
intro = pages(page("Welcome to the", "GAME CORNER.")),
|
|
which = page("Do you need some", "game coins?"),
|
|
bought = {
|
|
[50] = pages(page("Thank you!", "Here are 50 coins.")),
|
|
[500] = pages(page("Thank you! Here", "are 500 coins.")),
|
|
},
|
|
notEnoughMoney = pages(page("You don't have", "enough money.")),
|
|
caseFull = pages(page("Whoops! Your COIN", "CASE is full.")),
|
|
comeAgain = pages(page("No coins for you?", "Come again!")),
|
|
noCoinCase = pages(page("Do you need game", "coins?"),
|
|
page("Oh, you don't have", "a COIN CASE for"),
|
|
page("your coins.")),
|
|
},
|
|
}
|
|
|
|
-- --------------------------------------------------------- pure purchase
|
|
--
|
|
-- Everything a counter refuses, decided without a screen so a test can drive
|
|
-- it. Returns "ok" or the reason, and never mutates.
|
|
--
|
|
-- PARTY_LENGTH is 6 (constants/pokemon_data_constants.asm); `readvar
|
|
-- VAR_PARTYCOUNT / ifequal PARTY_LENGTH` is the mon counter's room check.
|
|
PrizeMenu.PARTY_LENGTH = Save.PARTY_SIZE
|
|
|
|
PrizeMenu.canAfford = CoinCase.canAfford
|
|
|
|
-- The room check, split out because the two prize kinds ask different
|
|
-- questions and ask them at different points in the conversation.
|
|
function PrizeMenu.hasRoom(save, counter, prize, data)
|
|
if counter.kind == "mon" then
|
|
return #((save and save.party) or {}) < PrizeMenu.PARTY_LENGTH
|
|
end
|
|
if counter.kind == "coins" then
|
|
-- `checkcoins MAX_COINS - amount / ifequal HAVE_MORE` -- strictly more than
|
|
-- the headroom is a full case, exactly the headroom still fits.
|
|
return PrizeMenu.checkCoins(save, PrizeMenu.MAX_COINS - prize.amount)
|
|
~= PrizeMenu.HAVE_MORE
|
|
end
|
|
-- giveitem's own failure: AddItemToInventory refuses when the pocket is full
|
|
-- or the stack would pass 99. Bag.add is that routine, so ask it directly
|
|
-- against a scratch copy rather than reimplementing the rule.
|
|
local inv = (save and save.inventory) or {}
|
|
local scratch = { inventory = {} }
|
|
for id, count in pairs(inv) do scratch.inventory[id] = count end
|
|
return Bag.add(scratch, prize.id, 1, data or {}) == true
|
|
end
|
|
|
|
-- The whole refusal ladder for one counter, in the cart's order.
|
|
-- Returns "ok", "coins", "room", or "money" (the coin vendor only).
|
|
function PrizeMenu.check(save, counter, prize, data)
|
|
if counter.kind == "coins" then
|
|
if not PrizeMenu.hasRoom(save, counter, prize, data) then return "room" end
|
|
if money(save) < prize.cost then return "money" end
|
|
return "ok"
|
|
end
|
|
if not PrizeMenu.canAfford(save, prize.cost) then return "coins" end
|
|
if counter.kind == "mon"
|
|
and not PrizeMenu.hasRoom(save, counter, prize, data) then
|
|
return "room"
|
|
end
|
|
return "ok"
|
|
end
|
|
|
|
-- The transaction itself, once the player has said yes. Item counters do the
|
|
-- giveitem BEFORE the takecoins, so a bag that turns out to be full costs
|
|
-- nothing; that ordering is what makes "room" a possible answer here too.
|
|
function PrizeMenu.buy(save, counter, prize, data)
|
|
if counter.kind == "coins" then
|
|
local reason = PrizeMenu.check(save, counter, prize, data)
|
|
if reason ~= "ok" then return reason end
|
|
PrizeMenu.giveCoins(save, prize.amount)
|
|
setMoney(save, money(save) - prize.cost)
|
|
return "ok"
|
|
end
|
|
if not PrizeMenu.canAfford(save, prize.cost) then return "coins" end
|
|
if counter.kind == "mon" then
|
|
if not PrizeMenu.hasRoom(save, counter, prize, data) then return "room" end
|
|
local Mon = require("src.battle.gen2.Mon")
|
|
local mon = Mon.new(data, prize.id, prize.level)
|
|
if not mon then return "room" end
|
|
save.party = save.party or {}
|
|
-- The prize is a `givepoke`, so it takes AddPartyMon's stamp (move_mon.asm:143-149).
|
|
Mon.stampOT(save, mon)
|
|
save.party[#save.party + 1] = mon
|
|
-- `special GameCornerPrizeMonCheckDex` right before the givepoke: the prize
|
|
-- mon is registered as seen and caught even though it never appeared in a
|
|
-- battle.
|
|
save.pokedex = save.pokedex or { seen = {}, caught = {} }
|
|
save.pokedex.seen[prize.id] = true
|
|
save.pokedex.caught[prize.id] = true
|
|
PrizeMenu.takeCoins(save, prize.cost)
|
|
return "ok"
|
|
end
|
|
if not Bag.add(save, prize.id, 1, data or {}) then return "room" end
|
|
PrizeMenu.takeCoins(save, prize.cost)
|
|
return "ok"
|
|
end
|
|
|
|
-- ------------------------------------------------------------------ layout
|
|
local COIN_BOX_X, COIN_BOX_Y, COIN_BOX_W, COIN_BOX_H = 11, 0, 9, 3
|
|
local COIN_LABEL_X, COIN_LABEL_Y = 12, 0
|
|
local COIN_VALUE_X, COIN_VALUE_Y = 13, 1
|
|
|
|
local TEXT_BOX_X, TEXT_BOX_Y, TEXT_BOX_W, TEXT_BOX_H = 0, 12, 20, 6
|
|
local TEXT_X, TEXT_Y, TEXT_LINE = 1, 14, 2
|
|
|
|
-- YesNoBox: `lb bc, SCREEN_WIDTH - 6, 7` puts a 6x5 box at (14,7), and
|
|
-- YesNoMenuHeader carries STATICMENU_CURSOR | STATICMENU_NO_TOP_SPACING, so YES
|
|
-- lands at (16,8) and NO at (16,10) with the cursor in column 15.
|
|
local YESNO_X, YESNO_Y, YESNO_W, YESNO_H = 14, 7, 6, 5
|
|
|
|
local SFX_TRANSACTION = "Sfx_Transaction"
|
|
|
|
-- ------------------------------------------------------------------ screen
|
|
function PrizeMenu:wantsFillScale() return true end
|
|
function PrizeMenu:drawsWidescreen() return true end
|
|
|
|
-- opts: save, counter (a COUNTERS key or a table), texts (a TEXTS key),
|
|
-- version ("gold"/"silver"), data, hasCoinCase (defaults to the bag),
|
|
-- onClose()
|
|
function PrizeMenu.new(game, opts)
|
|
opts = opts or {}
|
|
local self = setmetatable({}, PrizeMenu)
|
|
self.game = game
|
|
self.save = opts.save or (game and game.save)
|
|
self.data = opts.data or (game and game.data)
|
|
self.onClose = opts.onClose
|
|
local counter = opts.counter or "CELADON_TM"
|
|
self.counter = type(counter) == "table" and counter
|
|
or PrizeMenu.COUNTERS[counter] or PrizeMenu.COUNTERS.CELADON_TM
|
|
local textKey = opts.texts
|
|
or (self.counter.kind == "coins" and "COIN_VENDOR" or "CELADON")
|
|
self.text = PrizeMenu.TEXTS[textKey] or PrizeMenu.TEXTS.CELADON
|
|
self.version = opts.version or (self.save and self.save.version) or "gold"
|
|
self:buildPrizes()
|
|
self.index = 1
|
|
-- `checkitem COIN_CASE / iffalse` is the very first thing after the intro.
|
|
if opts.hasCoinCase ~= nil then
|
|
self.hasCoinCase = opts.hasCoinCase
|
|
else
|
|
local inv = (self.save and self.save.inventory) or {}
|
|
self.hasCoinCase = (inv.COIN_CASE or 0) > 0
|
|
end
|
|
self:say(self.text.intro, function()
|
|
if not self.hasCoinCase then
|
|
self:say(self.text.noCoinCase, function() self:close() end)
|
|
else
|
|
self:enterMenu()
|
|
end
|
|
end)
|
|
return self
|
|
end
|
|
|
|
-- The Goldenrod mon counter's `checkver` swap, applied once at construction so
|
|
-- the menu rows and the purchase agree.
|
|
function PrizeMenu:buildPrizes()
|
|
local silver = self.version == "silver"
|
|
local out = {}
|
|
for i, prize in ipairs(self.counter.prizes) do
|
|
if silver and prize.silver then
|
|
local swapped = {}
|
|
for k, v in pairs(prize) do swapped[k] = v end
|
|
swapped.id = prize.silver.id
|
|
swapped.label = prize.silver.label
|
|
swapped.silver = nil
|
|
out[i] = swapped
|
|
else
|
|
out[i] = prize
|
|
end
|
|
end
|
|
-- Every counter's menu data ends with a literal CANCEL row.
|
|
out[#out + 1] = { cancel = true, label = "CANCEL" }
|
|
self.prizes = out
|
|
end
|
|
|
|
function PrizeMenu:say(list, onDone)
|
|
self.message = { pages = list or {}, page = 1, onDone = onDone }
|
|
end
|
|
|
|
function PrizeMenu:ask(list, onYes, onNo)
|
|
self.confirm = { pages = list or {}, page = 1, choice = 1,
|
|
onYes = onYes, onNo = onNo }
|
|
end
|
|
|
|
function PrizeMenu:enterMenu()
|
|
self.phase = "menu"
|
|
-- `db 1 ; default option`: the cursor is back on the first prize every pass
|
|
-- through the loop.
|
|
self.index = 1
|
|
self.message = nil
|
|
end
|
|
|
|
function PrizeMenu:close()
|
|
if self.onClose then self.onClose() end
|
|
end
|
|
|
|
function PrizeMenu:cancel()
|
|
self:say(self.text.comeAgain, function() self:close() end)
|
|
end
|
|
|
|
function PrizeMenu:sfx(name)
|
|
if self.data then Sound.play(self.data, name) end
|
|
end
|
|
|
|
function PrizeMenu:prizeName(prize)
|
|
if self.counter.kind == "coins" then
|
|
return ("%d coins"):format(prize.amount)
|
|
end
|
|
local items = self.data and self.data.items
|
|
local mons = self.data and self.data.pokemon
|
|
local def = (self.counter.kind == "mon" and mons or items)
|
|
local entry = def and def[prize.id]
|
|
return (entry and entry.name) or prize.id
|
|
end
|
|
|
|
function PrizeMenu:refuse(reason)
|
|
local text = self.text
|
|
if reason == "coins" then
|
|
self:say(text.notEnoughCoins, function() self:close() end)
|
|
elseif reason == "money" then
|
|
self:say(text.notEnoughMoney, function() self:close() end)
|
|
elseif reason == "room" then
|
|
self:say(text.noRoom or text.caseFull, function() self:close() end)
|
|
end
|
|
end
|
|
|
|
function PrizeMenu:choose()
|
|
local prize = self.prizes[self.index]
|
|
if not prize or prize.cancel then
|
|
self:cancel()
|
|
return
|
|
end
|
|
-- The pre-confirm ladder: coins first for a prize counter, room first for the
|
|
-- coin vendor. A refusal here ends the conversation, it does not loop.
|
|
local reason = PrizeMenu.check(self.save, self.counter, prize, self.data)
|
|
if reason ~= "ok" then
|
|
self:refuse(reason)
|
|
return
|
|
end
|
|
if self.counter.kind == "coins" then
|
|
-- The coin vendor never asks; it buys straight off the menu.
|
|
self:complete(prize)
|
|
return
|
|
end
|
|
self:ask(self.text.confirm(self:prizeName(prize)),
|
|
function() self:complete(prize) end,
|
|
function() self:cancel() end)
|
|
end
|
|
|
|
function PrizeMenu:complete(prize)
|
|
local reason = PrizeMenu.buy(self.save, self.counter, prize, self.data)
|
|
if reason ~= "ok" then
|
|
self:refuse(reason)
|
|
return
|
|
end
|
|
self:sfx(SFX_TRANSACTION)
|
|
local bought = self.text.bought and self.text.bought[prize.amount]
|
|
self:say(bought or self.text.hereYouGo, function() self:enterMenu() end)
|
|
end
|
|
|
|
function PrizeMenu:updateMessage(input)
|
|
if not (input:wasPressed("a") or input:wasPressed("b")) then return end
|
|
local message = self.message
|
|
if message.page < #message.pages then
|
|
message.page = message.page + 1
|
|
return
|
|
end
|
|
self.message = nil
|
|
if message.onDone then message.onDone() end
|
|
end
|
|
|
|
function PrizeMenu:updateConfirm(input)
|
|
local confirm = self.confirm
|
|
if confirm.page < #confirm.pages then
|
|
if input:wasPressed("a") or input:wasPressed("b") then
|
|
confirm.page = confirm.page + 1
|
|
end
|
|
return
|
|
end
|
|
if input:wasPressed("up") or input:wasPressed("down") then
|
|
confirm.choice = confirm.choice == 1 and 2 or 1
|
|
return
|
|
end
|
|
if input:wasPressed("b") then
|
|
self.confirm = nil
|
|
if confirm.onNo then confirm.onNo() end
|
|
return
|
|
end
|
|
if input:wasPressed("a") then
|
|
local yes = confirm.choice == 1
|
|
self.confirm = nil
|
|
if yes then
|
|
if confirm.onYes then confirm.onYes() end
|
|
elseif confirm.onNo then
|
|
confirm.onNo()
|
|
end
|
|
end
|
|
end
|
|
|
|
function PrizeMenu:update(_dt)
|
|
local input = self.game and self.game.input
|
|
if not input then return end
|
|
if self.confirm then
|
|
self:updateConfirm(input)
|
|
return
|
|
end
|
|
if self.message then
|
|
self:updateMessage(input)
|
|
return
|
|
end
|
|
if self.phase ~= "menu" then return end
|
|
if input:wasPressed("up") then
|
|
self.index = self.index > 1 and self.index - 1 or #self.prizes
|
|
elseif input:wasPressed("down") then
|
|
self.index = self.index < #self.prizes and self.index + 1 or 1
|
|
elseif input:wasPressed("b") then
|
|
self:cancel()
|
|
elseif input:wasPressed("a") then
|
|
self:choose()
|
|
end
|
|
end
|
|
|
|
-- ------------------------------------------------------------------- draw
|
|
function PrizeMenu:drawCoinBox()
|
|
Chrome.textbox(COIN_BOX_X, COIN_BOX_Y, COIN_BOX_W - 2, COIN_BOX_H - 2)
|
|
Chrome.print("COIN", COIN_LABEL_X, COIN_LABEL_Y)
|
|
Chrome.print(Chrome.number(PrizeMenu.coins(self.save), 4, true),
|
|
COIN_VALUE_X, COIN_VALUE_Y)
|
|
end
|
|
|
|
function PrizeMenu:drawPanel()
|
|
Chrome.clear()
|
|
self:drawCoinBox()
|
|
if self.phase == "menu" or self.confirm then
|
|
local menu = self.counter.menu
|
|
Chrome.textbox(menu.x, menu.y, menu.w - 2, menu.h - 2)
|
|
for i, prize in ipairs(self.prizes) do
|
|
local ty = menu.y + 2 + (i - 1) * 2
|
|
if i == self.index then Chrome.cursor(menu.x + 1, ty) end
|
|
Chrome.print(prize.label, menu.x + 2, ty)
|
|
end
|
|
end
|
|
local lines = nil
|
|
if self.confirm then
|
|
lines = self.confirm.pages[self.confirm.page]
|
|
elseif self.message then
|
|
lines = self.message.pages[self.message.page]
|
|
elseif self.phase == "menu" then
|
|
lines = self.text.which
|
|
end
|
|
if lines then
|
|
Chrome.textbox(TEXT_BOX_X, TEXT_BOX_Y, TEXT_BOX_W - 2, TEXT_BOX_H - 2)
|
|
for i, line in ipairs(lines) do
|
|
Chrome.print(line, TEXT_X, TEXT_Y + (i - 1) * TEXT_LINE)
|
|
end
|
|
end
|
|
if self.confirm and self.confirm.page >= #self.confirm.pages then
|
|
Chrome.textbox(YESNO_X, YESNO_Y, YESNO_W - 2, YESNO_H - 2)
|
|
Chrome.print("YES", YESNO_X + 2, YESNO_Y + 1)
|
|
Chrome.print("NO", YESNO_X + 2, YESNO_Y + 3)
|
|
Chrome.cursor(YESNO_X + 1, YESNO_Y + 1 + (self.confirm.choice - 1) * 2)
|
|
end
|
|
end
|
|
|
|
function PrizeMenu:draw()
|
|
self:drawPanel()
|
|
end
|
|
|
|
function PrizeMenu:drawWidescreen(winW, winH)
|
|
local G = love.graphics
|
|
G.setColor(1, 1, 1, 1)
|
|
G.rectangle("fill", 0, 0, winW, winH)
|
|
local scale = Chrome.fitScale(winW, winH)
|
|
G.push()
|
|
G.translate(Chrome.fitOrigin(winW, winH, scale))
|
|
G.scale(scale, scale)
|
|
self:drawPanel()
|
|
G.pop()
|
|
end
|
|
|
|
return PrizeMenu
|