mirror of
https://github.com/DramaticShape/DramaticShapeVoxelMod.git
synced 2026-08-12 07:50:53 +02:00
shiny capture
This commit is contained in:
@@ -0,0 +1,178 @@
|
||||
-- SELECT on a row explains what it does.
|
||||
--
|
||||
-- ------- why this exists at all
|
||||
--
|
||||
-- Every setting in this mod has ALWAYS carried a paragraph of help. It goes
|
||||
-- into the schema handed to the mod manager (ModSetting:schema takes it), it
|
||||
-- has been written and kept up to date beside every row in main.lua's
|
||||
-- SETTINGS -- and nothing in the engine has ever drawn it. Not the OPTIONS
|
||||
-- menu, whose row is a label and a value and has no room for a third thing;
|
||||
-- not the mod manager's own page, which renders the same two lines. It was
|
||||
-- authored, structured, accurate prose sitting in a field with no reader.
|
||||
--
|
||||
-- So it gets one. A row on this mod's menus says what it IS on one line and
|
||||
-- what it is SET TO on the next, and SELECT says what that means -- which is
|
||||
-- the question a row like RENDER DIST or 2D-3D B cannot answer in eighteen
|
||||
-- characters however the label is worded.
|
||||
--
|
||||
-- SELECT rather than a button that already does something: A steps a setting,
|
||||
-- B leaves, and the d-pad moves. SELECT is free on a menu -- the mod's own
|
||||
-- SELECT hotkey is installed on OverworldController:handleInput, which only
|
||||
-- runs while the overworld is the top state, so a menu can have the button
|
||||
-- without taking anything from the map.
|
||||
--
|
||||
-- ------- the shape of it
|
||||
--
|
||||
-- The game's own dialogue box: drawn with Font.drawBox, so the border is the
|
||||
-- ROM's own glyphs and a mod-supplied font theme retextures this along with
|
||||
-- everything else (Font.BORDER) -- and anchored to the BOTTOM of the screen
|
||||
-- with the menu still visible above it, which is where this game has put
|
||||
-- every line of text anybody has ever read in it.
|
||||
--
|
||||
-- Sized to what it holds rather than to the screen. Each description is one
|
||||
-- sentence, so most of these are five or six tiles tall and the row being
|
||||
-- asked about is still on screen over the top of the box. A sentence long
|
||||
-- enough to overflow scrolls instead of growing past MAX_LINES, a line at a
|
||||
-- time on the d-pad -- which is a fallback, not the design: the answer to a
|
||||
-- description that needs scrolling is a shorter description.
|
||||
|
||||
-- the mod namespace (see main.lua)
|
||||
local V = ...
|
||||
|
||||
local Font = require("src.render.Font")
|
||||
local Theme = require("src.ui.Theme")
|
||||
local PaletteFX = require("src.render.PaletteFX")
|
||||
|
||||
local SettingsHelp = {}
|
||||
SettingsHelp.__index = SettingsHelp
|
||||
|
||||
-- NOT opaque: the menu stays drawn underneath, so the row being asked about
|
||||
-- is still on screen above the box. That is most of why the box is only as
|
||||
-- tall as it needs to be.
|
||||
SettingsHelp.isOpaque = false
|
||||
|
||||
-- The box spans the screen's twenty tiles and its border owns the outer ring,
|
||||
-- so text runs from tile 1. Seventeen columns rather than eighteen: tile 18
|
||||
-- is kept clear for the more-arrow, which would otherwise land on top of the
|
||||
-- last character of any line that filled the width.
|
||||
local COLS = 17
|
||||
local PEN_X = 8
|
||||
local SCREEN_ROWS = 18
|
||||
-- title, plus the body, plus the two border rows
|
||||
local CHROME_ROWS = 3
|
||||
-- A sentence needing more than this scrolls. Eight lines of seventeen is 136
|
||||
-- characters, which is a long sentence and a box two thirds up the screen.
|
||||
local MAX_LINES = 8
|
||||
|
||||
-- Break a string into lines that fit, on word boundaries. Unbounded, unlike
|
||||
-- StadiumScreen's -- that one is capping a save path to what a fixed plate can
|
||||
-- show, and this one is the whole point of the screen.
|
||||
local function wrapped(str, cols)
|
||||
cols = cols or COLS
|
||||
local lines, line = {}, nil
|
||||
for word in tostring(str or ""):gmatch("%S+") do
|
||||
local try = line and (line .. " " .. word) or word
|
||||
if #try <= cols then
|
||||
line = try
|
||||
else
|
||||
if line then lines[#lines + 1] = line end
|
||||
-- a word longer than the line is broken across lines rather than cut;
|
||||
-- nothing in the help text is that long today, but losing the end of a
|
||||
-- sentence silently is not a failure mode worth leaving open
|
||||
while #word > cols do
|
||||
lines[#lines + 1] = word:sub(1, cols)
|
||||
word = word:sub(cols + 1)
|
||||
end
|
||||
line = word
|
||||
end
|
||||
end
|
||||
if line then lines[#lines + 1] = line end
|
||||
return lines
|
||||
end
|
||||
|
||||
SettingsHelp.wrapped = wrapped
|
||||
|
||||
function SettingsHelp.new(game, title, body)
|
||||
return setmetatable({
|
||||
game = game,
|
||||
title = tostring(title or ""):gsub("%.%.$", ""),
|
||||
lines = wrapped(body),
|
||||
top = 0,
|
||||
}, SettingsHelp)
|
||||
end
|
||||
|
||||
-- How many body lines this box shows: all of them, unless there are more than
|
||||
-- a box is allowed to be tall.
|
||||
function SettingsHelp:bodyRows()
|
||||
return math.min(#self.lines, MAX_LINES)
|
||||
end
|
||||
|
||||
function SettingsHelp:maxTop()
|
||||
return math.max(0, #self.lines - self:bodyRows())
|
||||
end
|
||||
|
||||
-- Every button that could mean "done" closes it, including SELECT itself --
|
||||
-- the press that opened the box is the one a player is most likely to reach
|
||||
-- for to get rid of it. A is in there too: it steps a setting everywhere else
|
||||
-- on these menus, and stepping one you cannot see would be worse than an
|
||||
-- extra way out.
|
||||
local DISMISS = { "a", "b", "start", "select" }
|
||||
|
||||
function SettingsHelp:update()
|
||||
local input = self.game and self.game.input
|
||||
if not input then return end
|
||||
local maxTop = self:maxTop()
|
||||
-- the d-pad only does anything when there is something below the fold; a
|
||||
-- box showing its whole sentence has nowhere to go and says so by not
|
||||
-- moving
|
||||
if input:wasPressed("down") then
|
||||
self.top = math.min(maxTop, self.top + 1)
|
||||
return
|
||||
elseif input:wasPressed("up") then
|
||||
self.top = math.max(0, self.top - 1)
|
||||
return
|
||||
end
|
||||
for _, btn in ipairs(DISMISS) do
|
||||
if input:wasPressed(btn) then
|
||||
local stack = self.game.stack
|
||||
if self.game.data then
|
||||
require("src.core.Sound").play(self.game.data, "Press_AB")
|
||||
end
|
||||
if stack and stack:top() == self then stack:pop() end
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SettingsHelp:draw()
|
||||
local body = self:bodyRows()
|
||||
local th = body + CHROME_ROWS
|
||||
local ty = SCREEN_ROWS - th -- anchored to the bottom of the screen
|
||||
Font.drawBox(0, ty, 20, th)
|
||||
love.graphics.setColor(0, 0, 0, 1)
|
||||
-- the row's own name, so the box says what it is about even where it covers
|
||||
-- the row that was asked
|
||||
Font.draw(self.title, PEN_X, (ty + 1) * 8)
|
||||
for i = 1, body do
|
||||
local line = self.lines[self.top + i]
|
||||
if not line then break end
|
||||
Font.draw(line, PEN_X, (ty + 1 + i) * 8)
|
||||
end
|
||||
-- the same marker the options list uses for "there is more below this", so
|
||||
-- it means here what it means there
|
||||
if self.top < self:maxTop() then
|
||||
Font.drawCode(Theme.moreArrow, 144, (ty + th - 2) * 8)
|
||||
end
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
end
|
||||
|
||||
-- Game:draw stops at the first state that HAS this method, so without one the
|
||||
-- box would inherit whatever is underneath -- which is a menu of ours, whose
|
||||
-- answer happens to be right. Stated anyway: the reason that answer is right
|
||||
-- is not a property of this screen, and a future menu that paints something
|
||||
-- of its own would silently repaint this box with it.
|
||||
function SettingsHelp:sgbPalettes(game)
|
||||
return PaletteFX.wholeNamed(game.data, "MEWMON")
|
||||
end
|
||||
|
||||
return SettingsHelp
|
||||
Reference in New Issue
Block a user