moved titlescreen implementation to the new page system

added Screen middle-class to titlescreen/
added BootScreen implementation
added "missing" input enums to globals
PageView no longer requires a rootPage on construction
added a mouse handler to Page
ModeSelectPage no longer starts playing audio and animations on construction
This commit is contained in:
Hersi 2022-05-04 03:34:37 +02:00
parent ff8322ca28
commit be9a1f9408
11 changed files with 281 additions and 106 deletions

1
.gitignore vendored
View File

@ -1,5 +1,6 @@
# IDE files # IDE files
.vscode .vscode
docs/diagrams/export
# secret(?) assets # secret(?) assets
_asset _asset

View File

@ -0,0 +1,35 @@
@startuml titlescreen onScreenChange event
!theme materia-outline
skinparam DefaultFontName Courier
skinparam Shadowing false
participant usc
participant "titlescreen.lua" as main
collections screens
collections pages
activate main
activate screens
activate pages
hnote across
Screen loaded and page displayed
endhnote
pages -> screens : change screen event\n(eg. goes out of scope)
deactivate pages
screens --> main : onDeactivation(obj)
deactivate screens
main -> main : handle replacing screen\nby inspecting `obj`
main -> screens : call current screen's init()
activate screens
screens -> usc : set current screen as last screen value
screens -> pages : init()
activate pages
@enduml

View File

@ -0,0 +1,48 @@
@startuml titlescreen startup
!theme materia-outline
skinparam DefaultFontName Courier
skinparam Shadowing false
participant usc
participant "titlescreen.lua" as main
collections screens
collections pages
usc -> main : load titlescreen.lua
activate main
group construct screens
main -> screens : create
screens -> pages : create
screens -> pages : set callbacks
main -> screens : set callbacks
main -> usc : get persistent states
main <-- usc
main -> screens : load persistent previous state values
end
main -> usc : get last screen value
main <-- usc
main -> main : set last screen value as current screen
main -> screens : call current screen's init()
activate screens
screens -> usc : set current screen as last screen value
screens -> pages : init()
activate pages
loop main render loop
main -> screens : call current screen's render()
activate screens
screens -> pages : render()
activate pages
deactivate screens
deactivate pages
end
@enduml

View File

@ -9,19 +9,22 @@ local Page = {
} }
---Create a new Page instance ---Create a new Page instance
---@param o? table # initial parameters ---@param params? table # initial parameters
---@return Page ---@return Page
function Page.new(o) function Page.new(params)
o = o or {} params = params or {}
--set instance members --set default parameters
o.content = o.content or {} params.content = params.content or {}
o.viewHandler = o.viewHandler or nil params.viewHandler = params.viewHandler or nil
return CreateInstance(Page, o) return CreateInstance(Page, params)
end end
---Initialize Page
function Page:init() end
---Add field to page ---Add field to page
---@param field Field ---@param field Field
function Page:addField(field) function Page:addField(field)
@ -49,6 +52,11 @@ end
---@param delta number # in radians, `-2*pi` to `0` (turning CCW) and `0` to `2*pi` (turning CW) ---@param delta number # in radians, `-2*pi` to `0` (turning CCW) and `0` to `2*pi` (turning CW)
function Page:handleKnobInput(knob, delta) end function Page:handleKnobInput(knob, delta) end
---@param x number
---@param y number
---@param button integer
function Page:handleMouseInput(x, y, button) end
---@param deltaTime number # frametime in seconds ---@param deltaTime number # frametime in seconds
function Page:drawBackground(deltaTime) end function Page:drawBackground(deltaTime) end

View File

@ -15,21 +15,11 @@ local function popStack(t)
end end
---Create a new PageView instance ---Create a new PageView instance
---@param rootPage Page
---@return PageView ---@return PageView
function PageView.new(rootPage) function PageView.new()
local o = {} local self = CreateInstance(PageView, {})
self.pageStack = {}
--set viewHandler as this instance for rootPage return self
rootPage.viewHandler = o
--set instance members
o.pageStack = {}
pushStack(o.pageStack, rootPage)
return CreateInstance(PageView, o)
end end
---Get page from pageStack ---Get page from pageStack

View File

@ -1,2 +1,10 @@
---Drewol, what are you doing? Why is there no game.LOGGER_DEBUG? ---Drewol, what are you doing? Why is there no game.LOGGER_DEBUG?
game.LOGGER_DEBUG = 0 game.LOGGER_DEBUG = 0
-- add missing inputs
game.MOUSE_LEFT = 0
game.MOUSE_RIGHT = 1
game.MOUSE_MIDDLE = 2
game.KNOB_LEFT = 0
game.KNOB_RIGHT = 1

View File

@ -1,33 +1,43 @@
require("common.globals") require("common.globals")
local Common = require("common.util") local Common = require("common.util")
local Dim = require("common.dimensions")
local Wallpaper = require("components.wallpaper")
local PageView = require("api.page.pageview")
local BootPage = require("titlescreen.pages.boot.bootpage")
local ModeSelectPage = require("titlescreen.pages.modeselect.modeselectpage")
local ServiceMenuPage = require("titlescreen.pages.service.mainmenupage")
local BootScreen = require('titlescreen.boot')
local SplashScreen = require('titlescreen.splash')
local TitleScreen = require('titlescreen.title')
local ModeSelectScreen = require('titlescreen.modeselect')
local ServiceScreen = require('titlescreen.service')
local bootScreen = require('titlescreen.boot')
local splashScreen = require('titlescreen.splash')
local titleScreen = require('titlescreen.title')
local modeSelectScreen = require('titlescreen.modeselect')
local serviceScreen = require('titlescreen.service')
local screens = { local screens = {
boot = { [tostring(BootScreen)] = BootScreen.new(),
screen = bootScreen --splash = splashScreen,
}, --title = titleScreen,
splash = { --[tostring(ModeSelectPage)] = ModeSelectPage.new(),
screen = splashScreen --[tostring(ServiceMenuPage)] = ServiceMenuPage.new()
},
title = {
screen = titleScreen
},
mode_select = {
screen = modeSelectScreen
},
service = {
screen = serviceScreen
}
} }
local currentScreen = game.GetSkinSetting("animations_skipIntro") and screens.title or screens.boot -- show boot screen if skipIntro is not set --local currentScreen = game.GetSkinSetting("animations_skipIntro") and screens.title or screens.boot -- show boot screen if skipIntro is not set
local currentScreen = screens[tostring(BootScreen)]
---@param obj ScreenCallbackObject
local function onDeactivationCallback(obj)
currentScreen = screens[obj.hint]
end
for _, value in pairs(screens) do
value.onDeactivation = onDeactivationCallback
end
local function deltaKnob(delta) local function deltaKnob(delta)
-- what the hell does this do?
if math.abs(delta) > 1.5 * math.pi then if math.abs(delta) > 1.5 * math.pi then
return delta + 2 * math.pi * Common.sign(delta) * -1 return delta + 2 * math.pi * Common.sign(delta) * -1
end end
@ -35,44 +45,21 @@ local function deltaKnob(delta)
end end
local lastKnobs = nil local lastKnobs = nil
local knobProgress = 0 local knobThreshold = (2 * math.pi) / 360
local function handleKnobs() local function handleKnobs()
if not currentScreen.screen.onKnobsChange then
return
end
if lastKnobs == nil then if lastKnobs == nil then
lastKnobs = {game.GetKnob(0), game.GetKnob(1)} lastKnobs = {game.GetKnob(game.KNOB_LEFT), game.GetKnob(game.KNOB_RIGHT)}
else else
local newKnobs = {game.GetKnob(0), game.GetKnob(1)} local newKnobs = {game.GetKnob(game.KNOB_LEFT), game.GetKnob(game.KNOB_RIGHT)}
local knobProgress = {deltaKnob(lastKnobs[1] - newKnobs[1]), deltaKnob(lastKnobs[2] - newKnobs[2])}
knobProgress = knobProgress - deltaKnob(lastKnobs[1] - newKnobs[1]) * 1.2
knobProgress = knobProgress - deltaKnob(lastKnobs[2] - newKnobs[2]) * 1.2
lastKnobs = newKnobs lastKnobs = newKnobs
if math.abs(knobProgress) > 1 then if math.abs(knobProgress[1]) > knobThreshold then
if (knobProgress < 0) then currentScreen:handleKnobInput(game.KNOB_LEFT, knobProgress[1])
-- Negative
currentScreen.screen.onKnobsChange(-1)
else
-- Positive
currentScreen.screen.onKnobsChange(1)
end
knobProgress = knobProgress - Common.roundToZero(knobProgress)
end
end
end end
local function handleScreenResponse(res) if math.abs(knobProgress[2]) > knobThreshold then
if res and res.eventType == 'switch' then currentScreen:handleKnobInput(game.KNOB_RIGHT, knobProgress[2])
if not screens[res.toScreen] then
game.Log('Undefined screen ' .. res.toScreen, game.LOGGER_ERROR)
return
end
currentScreen = screens[res.toScreen]
if currentScreen.screen.reset then
currentScreen.screen.reset()
end end
end end
end end
@ -80,18 +67,21 @@ end
function render(deltaTime) function render(deltaTime)
handleKnobs() handleKnobs()
handleScreenResponse(currentScreen.screen.render(deltaTime)) Dim.updateResolution()
Wallpaper.render()
Dim.transformToScreenSpace()
currentScreen:render(deltaTime)
end end
function mouse_pressed(button) function mouse_pressed(button)
if (currentScreen.screen.onMousePressed) then local mouseX, mouseY = game.GetMousePos()
currentScreen.screen.onMousePressed(button) currentScreen:handleMouseInput(mouseX, mouseY, button)
end return 0 --THIS '0' IS VERY IMPORTANT, IT WILL CRASH VERY HARD WITHOUT THIS
return 0
end end
function button_pressed(button) function button_pressed(button)
if (currentScreen.screen.onButtonPressed) then currentScreen:handleButtonInput(button)
currentScreen.screen.onButtonPressed(button)
end
end end

View File

@ -1,28 +1,33 @@
local Dim = require("common.dimensions") require "common.globals"
local Wallpaper = require("components.wallpaper") require "common.class"
local BootPage = require("titlescreen.pages.boot.bootpage") local Screen = require "titlescreen.screen"
local PageView = require("api.page.pageview") local BootPage = require "titlescreen.pages.boot.bootpage"
local PageView = require "api.page.pageview"
local SplashScreen = require "titlescreen.splash"
local bootpage = BootPage.new() ---@class BootScreen : Screen
local pageview = PageView.new(bootpage) ---@field bootpage BootPage
local BootScreen = {
__tostring = function() return "BootScreen" end
}
local function render(deltaTime) ---Create a new BootScreen instance
Dim.updateResolution() ---@param o? BootScreen
---@return BootScreen
function BootScreen.new(o)
o = o or {}
Wallpaper.render() o.bootpage = o.bootpage or BootPage.new()
Dim.transformToScreenSpace() return CreateInstance(BootScreen, o, Screen)
pageview:render(deltaTime)
--pageview will be empty when you `back()` out of the root page
if not pageview:get() then
return {eventType = "switch", toScreen = "splash"}
end
end end
local function onButtonPressed(button) function BootScreen:init()
pageview:get():handleButtonInput(button) self.pageview:replace(self.bootpage)
end end
return {render = render, onButtonPressed = onButtonPressed} function BootScreen:deactivate()
self.onDeactivation({reason = "deactivation", hint = tostring(SplashScreen)})
end
return BootScreen

View File

@ -349,11 +349,17 @@ local function tickTransitions(deltaTime)
end end
end end
local PageView = require "api.page.pageview"
local ModeSelectPage = require "titlescreen.pages.modeselect.modeselectpage"
local PageViewInstance = PageView.new(ModeSelectPage.new())
local function render(deltaTime) local function render(deltaTime)
--[[
if not playedBgm then if not playedBgm then
game.PlaySample(resources.audiosamples.bgm, true) game.PlaySample(resources.audiosamples.bgm, true)
playedBgm = true playedBgm = true
end end
]]
game.SetSkinSetting("_currentScreen", "title") game.SetSkinSetting("_currentScreen", "title")
@ -365,7 +371,8 @@ local function render(deltaTime)
tickTransitions(deltaTime) tickTransitions(deltaTime)
draw_titlescreen(deltaTime) --draw_titlescreen(deltaTime)
PageViewInstance:render(deltaTime)
if (triggerServiceMenu) then if (triggerServiceMenu) then
triggerServiceMenu = false triggerServiceMenu = false
@ -373,6 +380,10 @@ local function render(deltaTime)
end end
end end
local function reset()
PageViewInstance:get():init()
end
local function callButtonAction() local function callButtonAction()
if buttons[cursorIndex].action == nil then setButtonActions() end if buttons[cursorIndex].action == nil then setButtonActions() end
buttons[cursorIndex].action() buttons[cursorIndex].action()
@ -459,6 +470,7 @@ end
return { return {
render = render, render = render,
reset = reset,
onKnobsChange = onKnobsChange, onKnobsChange = onKnobsChange,
onButtonPressed = onButtonPressed, onButtonPressed = onButtonPressed,
onMousePressed = onMousePressed, onMousePressed = onMousePressed,

View File

@ -5,6 +5,7 @@ local AudioSample = require "api.audiosample"
local Animation = require "api.animation" local Animation = require "api.animation"
local Page = require "api.page.page" local Page = require "api.page.page"
local Background = require "components.background"
local Footer = require "components.footer" local Footer = require "components.footer"
local Header = require "components.headers.modeSelectHeader" local Header = require "components.headers.modeSelectHeader"
@ -13,6 +14,7 @@ local crew = game.GetSkinSetting("single_idol")
---@class ModeSelectPage: Page ---@class ModeSelectPage: Page
---@field _idolAnimationState AnimationState ---@field _idolAnimationState AnimationState
local ModeSelectPage = { local ModeSelectPage = {
__tostring = function () return "ModeSelectPage" end,
images = { images = {
selectorBgImage = gfx.CreateSkinImage("titlescreen/selector_bg.png", 0), selectorBgImage = gfx.CreateSkinImage("titlescreen/selector_bg.png", 0),
selectorArrowsImage = gfx.CreateSkinImage("titlescreen/selector_arrows.png", 0), selectorArrowsImage = gfx.CreateSkinImage("titlescreen/selector_arrows.png", 0),
@ -40,15 +42,18 @@ local ModeSelectPage = {
} }
function ModeSelectPage.new(o) function ModeSelectPage.new(o)
local this = CreateInstance(ModeSelectPage, o, Page) local self = CreateInstance(ModeSelectPage, o, Page)
this._idolAnimationState = this.anims.idolAnimation:start() return self
this.audiosamples.bgm:play() end
return this function ModeSelectPage:init()
self._idolAnimationState = self.anims.idolAnimation:start()
self.audiosamples.bgm:play()
end end
function ModeSelectPage:drawBackground(deltaTime) function ModeSelectPage:drawBackground(deltaTime)
Background.draw(deltaTime)
self._idolAnimationState:render(deltaTime) self._idolAnimationState:render(deltaTime)
end end

View File

@ -0,0 +1,73 @@
require "common.globals"
require "common.class"
local PageView = require "api.page.pageview"
---@class Screen
---@field pageview PageView
local Screen = {
__tostring = function() return "Screen" end
}
---Create a new Screen instance
---@param o? Screen
---@return Screen
function Screen.new(o)
local self = CreateInstance(Screen, o)
self.pageview = PageView.new()
return self
end
---Initialize screen, override to push new page into pageview
function Screen:init()
end
---@param button integer # options are under the `game` table prefixed with `BUTTON`
function Screen:handleButtonInput(button)
if self.pageview:get() then
self.pageview:get():handleButtonInput(button)
end
end
---@param knob integer # `0` = Left, `1` = Right
---@param delta number # in radians, `-2*pi` to `0` (turning CCW) and `0` to `2*pi` (turning CW)
function Screen:handleKnobInput(knob, delta)
if self.pageview:get() then
self.pageview:get():handleKnobInput(knob, delta)
end
end
---@param x number
---@param y number
---@param button integer # `0` = Left, `1` = Right, `2` = Middle
function Screen:handleMouseInput(x, y, button)
if self.pageview:get() then
self.pageview:get():handleMouseInput(x, y, button)
end
end
---@class ScreenCallbackObject
---@field reason string # short string representation what the cb object is about
---@field hint any # hint object to help continue code flow in parent
---Event callback when screen gets deactivated (eg.: pageview is empty)
---@param obj ScreenCallbackObject
function Screen.onDeactivation(obj)
end
function Screen:deactivate()
self.onDeactivation({reason = "deactivation"})
end
---@param deltaTime number # frametime in seconds
function Screen:render(deltaTime)
if not self.pageview:get() then
self:deactivate()
end
self.pageview:render(deltaTime)
end
return Screen