Break flow out of ordoor
This commit is contained in:
187
internal/ordoor/flow/flow.go
Normal file
187
internal/ordoor/flow/flow.go
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
package flow
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/hajimehoshi/ebiten"
|
||||||
|
|
||||||
|
"code.ur.gs/lupine/ordoor/internal/assetstore"
|
||||||
|
"code.ur.gs/lupine/ordoor/internal/config"
|
||||||
|
"code.ur.gs/lupine/ordoor/internal/ui"
|
||||||
|
)
|
||||||
|
|
||||||
|
// type Flow is responsible for wiring up UI elements to each other and ensuring
|
||||||
|
// they behave as expected. This includes forward / back buttons to switch
|
||||||
|
// between screens, loading and saving options, launching a scenario, etc
|
||||||
|
type Flow struct {
|
||||||
|
assets *assetstore.AssetStore
|
||||||
|
config *config.Config
|
||||||
|
current *ui.Driver
|
||||||
|
drivers map[driverName]*ui.Driver
|
||||||
|
|
||||||
|
exit error
|
||||||
|
}
|
||||||
|
|
||||||
|
type driverName string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Names of all the drivers
|
||||||
|
main driverName = "main"
|
||||||
|
levelPly driverName = "levelPly"
|
||||||
|
singles driverName = "singles"
|
||||||
|
randomMap driverName = "randomMap"
|
||||||
|
newGame driverName = "newGame"
|
||||||
|
loadGame driverName = "loadGame"
|
||||||
|
options driverName = "options"
|
||||||
|
kbd driverName = "keyboard"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrExit = errors.New("exiting gracefully")
|
||||||
|
|
||||||
|
driverNames = []driverName{
|
||||||
|
main, levelPly, singles, randomMap, newGame, loadGame, options, kbd,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constants used for sliders
|
||||||
|
|
||||||
|
h3Slider = map[int]int{1: 8, 2: 56, 3: 110, 4: 120}
|
||||||
|
|
||||||
|
v10Slider = map[int]int{
|
||||||
|
0: 0,
|
||||||
|
10: 9, 20: 18, 30: 27, 40: 36, 50: 45,
|
||||||
|
60: 54, 70: 63, 80: 72, 90: 81, 100: 90,
|
||||||
|
}
|
||||||
|
|
||||||
|
h9Slider = map[int]int{
|
||||||
|
0: 0,
|
||||||
|
10: 10, 20: 20, 30: 30, 40: 40,
|
||||||
|
50: 50, 60: 60, 70: 70, 80: 80,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func New(assets *assetstore.AssetStore, config *config.Config) (*Flow, error) {
|
||||||
|
out := &Flow{
|
||||||
|
assets: assets,
|
||||||
|
config: config,
|
||||||
|
drivers: make(map[driverName]*ui.Driver, len(driverNames)),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load all the drivers upfront
|
||||||
|
for _, name := range driverNames {
|
||||||
|
driver, err := buildDriver(assets, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
out.drivers[name] = driver
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initial load of the config into the options UI
|
||||||
|
if err := out.configIntoOptions(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
out.linkDrivers()
|
||||||
|
out.setDriverNow(main)
|
||||||
|
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Flow) Update(screenX, screenY int) error {
|
||||||
|
if f.exit != nil {
|
||||||
|
return f.exit
|
||||||
|
}
|
||||||
|
|
||||||
|
return f.current.Update(screenX, screenY)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Flow) Draw(screen *ebiten.Image) error {
|
||||||
|
if f.exit != nil {
|
||||||
|
return f.exit
|
||||||
|
}
|
||||||
|
|
||||||
|
return f.current.Draw(screen)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Flow) setDriver(name driverName) func() {
|
||||||
|
return func() {
|
||||||
|
f.setDriverNow(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Flow) setDriverNow(name driverName) {
|
||||||
|
f.current = f.drivers[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Flow) setExit() {
|
||||||
|
f.exit = ErrExit
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Flow) linkDrivers() {
|
||||||
|
// Main interface
|
||||||
|
f.onClick(main, "2.1", f.setDriver(newGame)) // New game
|
||||||
|
f.onClick(main, "2.2", f.setDriver(loadGame)) // Load game
|
||||||
|
f.setFreeze(main, "2.3", true) // Multiplayer - disable for now
|
||||||
|
f.onClick(main, "2.4", f.setDriver(options)) // Options
|
||||||
|
f.onClick(main, "2.5", f.setExit) // Quit
|
||||||
|
|
||||||
|
// New game
|
||||||
|
f.onClick(newGame, "2.1", f.setDriver(levelPly)) // New campaign button
|
||||||
|
f.onClick(newGame, "2.2", f.setDriver(singles)) // Single scenario button
|
||||||
|
f.onClick(newGame, "2.3", f.setDriver(randomMap)) // Random scenario button
|
||||||
|
f.onClick(newGame, "2.4", f.setDriver(main)) // Back button
|
||||||
|
|
||||||
|
// Load game
|
||||||
|
f.onClick(loadGame, "3.3", f.setDriver(main)) // Cancel button
|
||||||
|
|
||||||
|
// Options
|
||||||
|
f.linkOptions()
|
||||||
|
|
||||||
|
// Level of play select
|
||||||
|
f.onClick(levelPly, "2.5", f.setDriver(newGame)) // Back button
|
||||||
|
|
||||||
|
// Single scenario setup
|
||||||
|
f.onClick(singles, "4.11", f.setDriver(newGame)) // Back button
|
||||||
|
|
||||||
|
// Random map setup
|
||||||
|
f.onClick(randomMap, "2.19", f.setDriver(newGame)) // Back button
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Flow) configureSlider(driver driverName, id string, steps map[int]int) {
|
||||||
|
if f.exit != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
f.exit = f.drivers[driver].ConfigureSlider(id, steps)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Flow) onClick(driver driverName, id string, fn func()) {
|
||||||
|
if f.exit != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
f.exit = f.drivers[driver].OnClick(id, fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Flow) setFreeze(driver driverName, id string, value bool) {
|
||||||
|
if f.exit != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
f.exit = f.drivers[driver].SetFreeze(id, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildDriver(assets *assetstore.AssetStore, name driverName) (*ui.Driver, error) {
|
||||||
|
menu, err := assets.Menu(string(name))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
driver, err := ui.NewDriver(menu)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return driver, nil
|
||||||
|
}
|
114
internal/ordoor/flow/options.go
Normal file
114
internal/ordoor/flow/options.go
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
package flow
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (f *Flow) linkOptions() {
|
||||||
|
// Main options
|
||||||
|
f.onClick(options, "2.8", f.setDriver(kbd)) // Keyboard settings button
|
||||||
|
|
||||||
|
f.configureSlider(options, "2.9", h3Slider) // Resolution slider
|
||||||
|
f.configureSlider(options, "2.10", v10Slider) // Music volume slider
|
||||||
|
f.configureSlider(options, "2.11", v10Slider) // SFX volume slider
|
||||||
|
|
||||||
|
f.onClick(options, "2.12", f.acceptOptions()) // OK button
|
||||||
|
f.onClick(options, "2.24", f.cancelOptions()) // Cancel button
|
||||||
|
|
||||||
|
f.configureSlider(options, "2.26", h9Slider) // Unit speed slider
|
||||||
|
f.configureSlider(options, "2.27", h9Slider) // Animation speed slider
|
||||||
|
|
||||||
|
// Keyboard settings
|
||||||
|
// TODO: implement keybindings save/load behaviour
|
||||||
|
f.onClick(kbd, "3.1", f.setDriver(options)) // Done button
|
||||||
|
f.onClick(kbd, "3.2", f.setDriver(options)) // Cancel button
|
||||||
|
f.onClick(kbd, "3.4", func() {}) // Reset to defaults button
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: exiting is a bit OTT. Perhaps display "save failed"?
|
||||||
|
func (f *Flow) acceptOptions() func() {
|
||||||
|
return func() {
|
||||||
|
if err := f.optionsIntoConfig(); err != nil {
|
||||||
|
log.Printf("Saving options to config failed: %v", err)
|
||||||
|
f.exit = err
|
||||||
|
} else {
|
||||||
|
f.setDriverNow(main)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: again, exiting is OTT. We're just resetting the state of
|
||||||
|
// the interface to the values in config.
|
||||||
|
func (f *Flow) cancelOptions() func() {
|
||||||
|
return func() {
|
||||||
|
if err := f.configIntoOptions(); err != nil {
|
||||||
|
log.Printf("Saving options to config failed: %v", err)
|
||||||
|
f.exit = err
|
||||||
|
} else {
|
||||||
|
f.setDriverNow(main)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Flow) configIntoOptions() error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
cfg := &f.config.Options
|
||||||
|
optionsUI := f.drivers[options]
|
||||||
|
|
||||||
|
try(optionsUI.SetValueBool("2.1", cfg.PlayMovies), &err)
|
||||||
|
try(optionsUI.SetValueBool("2.1", cfg.Animations), &err)
|
||||||
|
try(optionsUI.SetValueBool("2.3", cfg.PlayMusic), &err)
|
||||||
|
try(optionsUI.SetValueBool("2.4", cfg.CombatVoices), &err)
|
||||||
|
try(optionsUI.SetValueBool("2.5", cfg.ShowGrid), &err)
|
||||||
|
try(optionsUI.SetValueBool("2.6", cfg.ShowPaths), &err)
|
||||||
|
try(optionsUI.SetValueBool("2.7", cfg.PointSaving), &err)
|
||||||
|
try(optionsUI.SetValueInt("2.9", cfg.ResolutionIndex()), &err)
|
||||||
|
try(optionsUI.SetValueInt("2.10", cfg.MusicVolume), &err)
|
||||||
|
try(optionsUI.SetValueInt("2.11", cfg.SFXVolume), &err)
|
||||||
|
try(optionsUI.SetValueBool("2.25", cfg.AutoCutLevel), &err)
|
||||||
|
try(optionsUI.SetValueInt("2.26", cfg.UnitSpeed), &err)
|
||||||
|
try(optionsUI.SetValueInt("2.27", cfg.AnimSpeed), &err)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Flow) optionsIntoConfig() error {
|
||||||
|
var resIdx int // needs handling manually
|
||||||
|
var err error
|
||||||
|
|
||||||
|
cfg := &f.config.Options
|
||||||
|
optionsUI := f.drivers[options]
|
||||||
|
|
||||||
|
try(optionsUI.ValueBool("2.1", &cfg.PlayMovies), &err)
|
||||||
|
try(optionsUI.ValueBool("2.2", &cfg.Animations), &err)
|
||||||
|
try(optionsUI.ValueBool("2.3", &cfg.PlayMusic), &err)
|
||||||
|
try(optionsUI.ValueBool("2.4", &cfg.CombatVoices), &err)
|
||||||
|
try(optionsUI.ValueBool("2.5", &cfg.ShowGrid), &err)
|
||||||
|
try(optionsUI.ValueBool("2.6", &cfg.ShowPaths), &err)
|
||||||
|
try(optionsUI.ValueBool("2.7", &cfg.PointSaving), &err)
|
||||||
|
try(optionsUI.ValueInt("2.9", &resIdx), &err)
|
||||||
|
try(optionsUI.ValueInt("2.10", &cfg.MusicVolume), &err)
|
||||||
|
try(optionsUI.ValueInt("2.11", &cfg.SFXVolume), &err)
|
||||||
|
try(optionsUI.ValueBool("2.25", &cfg.AutoCutLevel), &err)
|
||||||
|
try(optionsUI.ValueInt("2.26", &cfg.UnitSpeed), &err)
|
||||||
|
try(optionsUI.ValueInt("2.27", &cfg.AnimSpeed), &err)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.SetResolutionIndex(resIdx)
|
||||||
|
|
||||||
|
if err := f.config.Save(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func try(result error, into *error) {
|
||||||
|
if *into == nil {
|
||||||
|
*into = result
|
||||||
|
}
|
||||||
|
}
|
@@ -1,307 +0,0 @@
|
|||||||
package ordoor
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"code.ur.gs/lupine/ordoor/internal/ui"
|
|
||||||
)
|
|
||||||
|
|
||||||
func try(result error, into *error) {
|
|
||||||
if *into == nil {
|
|
||||||
*into = result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// These are UI interfaces covering the game entrypoint
|
|
||||||
|
|
||||||
func (o *Ordoor) mainDriver() (*ui.Driver, error) {
|
|
||||||
// Start in the "main" menu
|
|
||||||
main, err := o.buildDriver("main")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
newGame, err := o.newGameDriver(main)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
loadGame, err := o.loadGameDriver(main)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
options, err := o.optionsDriver(main)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: clicking these buttons should load other interfaces
|
|
||||||
try(main.OnClick("2.1", func() { o.driver = newGame }), &err) // New game
|
|
||||||
try(main.OnClick("2.2", func() { o.driver = loadGame }), &err) // Load game
|
|
||||||
try(main.SetFreeze("2.3", true), &err) // Multiplayer - disable for now
|
|
||||||
try(main.OnClick("2.4", func() { o.driver = options }), &err) // Options
|
|
||||||
try(main.OnClick("2.5", func() { o.nextState = StateExit }), &err) // Quit
|
|
||||||
|
|
||||||
return main, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *Ordoor) newGameDriver(main *ui.Driver) (*ui.Driver, error) {
|
|
||||||
newGame, err := o.buildDriver("newgame")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
levelPly, err := o.levelPlyDriver(newGame)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
singles, err := o.singlesDriver(newGame)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
randomMap, err := o.randomMapDriver(newGame)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
try(newGame.OnClick("2.1", func() { o.driver = levelPly }), &err) // New campaign button
|
|
||||||
try(newGame.OnClick("2.2", func() { o.driver = singles }), &err) // Single scenario button
|
|
||||||
try(newGame.OnClick("2.3", func() { o.driver = randomMap }), &err) // Random scenario button
|
|
||||||
try(newGame.OnClick("2.4", func() { o.driver = main }), &err) // Back button
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return newGame, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *Ordoor) loadGameDriver(main *ui.Driver) (*ui.Driver, error) {
|
|
||||||
loadGame, err := o.buildDriver("loadgame")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
try(loadGame.OnClick("3.3", func() { o.driver = main }), &err) // Cancel button
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return loadGame, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Options needs to know how to go back to main
|
|
||||||
func (o *Ordoor) optionsDriver(main *ui.Driver) (*ui.Driver, error) {
|
|
||||||
options, err := o.buildDriver("options")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
kbd, err := o.keyboardDriver(options)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := o.configIntoOptions(options); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
h3Slider := map[int]int{1: 8, 2: 56, 3: 110, 4: 120}
|
|
||||||
v10Slider := map[int]int{
|
|
||||||
0: 0,
|
|
||||||
10: 9, 20: 18, 30: 27, 40: 36, 50: 45,
|
|
||||||
60: 54, 70: 63, 80: 72, 90: 81, 100: 90,
|
|
||||||
}
|
|
||||||
h9Slider := map[int]int{
|
|
||||||
0: 0,
|
|
||||||
10: 10, 20: 20, 30: 30, 40: 40,
|
|
||||||
50: 50, 60: 60, 70: 70, 80: 80,
|
|
||||||
}
|
|
||||||
|
|
||||||
try(options.OnClick("2.8", func() { o.driver = kbd }), &err) // Keyboard settings button
|
|
||||||
|
|
||||||
try(options.ConfigureSlider("2.9", h3Slider), &err) // Resolution slider
|
|
||||||
try(options.ConfigureSlider("2.10", v10Slider), &err) // Music volume slider
|
|
||||||
try(options.ConfigureSlider("2.11", v10Slider), &err) // SFX volume slider
|
|
||||||
try(options.OnClick("2.12", acceptOptionsFn(o, main, options)), &err)
|
|
||||||
// 13...23 are "hypertext"
|
|
||||||
try(options.OnClick("2.24", cancelOptionsFn(o, main, options)), &err)
|
|
||||||
try(options.ConfigureSlider("2.26", h9Slider), &err) // Unit speed slider
|
|
||||||
try(options.ConfigureSlider("2.27", h9Slider), &err) // Animation speed slider
|
|
||||||
// Sample of unit speed animation is 2,28
|
|
||||||
// Sample of effect speed animation is 2,29
|
|
||||||
|
|
||||||
// 30...35 are "hypertext"
|
|
||||||
|
|
||||||
return options, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// "Level of play menu when starting a new campaign
|
|
||||||
func (o *Ordoor) levelPlyDriver(newGame *ui.Driver) (*ui.Driver, error) {
|
|
||||||
levelPly, err := o.buildDriver("levelply")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
try(levelPly.OnClick("2.5", func() { o.driver = newGame }), &err) // Back button
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return levelPly, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *Ordoor) singlesDriver(newGame *ui.Driver) (*ui.Driver, error) {
|
|
||||||
singles, err := o.buildDriver("singles")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
try(singles.OnClick("4.11", func() { o.driver = newGame }), &err) // Back button
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return singles, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *Ordoor) randomMapDriver(newGame *ui.Driver) (*ui.Driver, error) {
|
|
||||||
randomMap, err := o.buildDriver("randommap")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
try(randomMap.OnClick("2.19", func() { o.driver = newGame }), &err) // Back button
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return randomMap, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *Ordoor) keyboardDriver(options *ui.Driver) (*ui.Driver, error) {
|
|
||||||
kbd, err := o.buildDriver("keyboard")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: implement keybindings save/load behaviour
|
|
||||||
try(kbd.OnClick("3.1", func() { o.driver = options }), &err) // Done button
|
|
||||||
try(kbd.OnClick("3.2", func() { o.driver = options }), &err) // Cancel button
|
|
||||||
try(kbd.OnClick("3.4", func() {}), &err) // Reset to defaults button
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return kbd, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: exiting is a bit OTT. Perhaps display "save failed"?
|
|
||||||
func acceptOptionsFn(o *Ordoor, main, options *ui.Driver) func() {
|
|
||||||
return func() {
|
|
||||||
if err := o.optionsIntoConfig(options); err != nil {
|
|
||||||
log.Printf("Saving options to config failed: %v", err)
|
|
||||||
o.nextState = StateExit
|
|
||||||
} else {
|
|
||||||
o.driver = main
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: again, exiting is OTT. We're just resetting the state of
|
|
||||||
// the interface to the values in config.
|
|
||||||
func cancelOptionsFn(o *Ordoor, main, options *ui.Driver) func() {
|
|
||||||
return func() {
|
|
||||||
if err := o.configIntoOptions(options); err != nil {
|
|
||||||
log.Printf("Saving options to config failed: %v", err)
|
|
||||||
o.nextState = StateExit
|
|
||||||
} else {
|
|
||||||
o.driver = main
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *Ordoor) configIntoOptions(options *ui.Driver) error {
|
|
||||||
cfg := &o.config.Options
|
|
||||||
var err error
|
|
||||||
|
|
||||||
try(options.SetValueBool("2.1", cfg.PlayMovies), &err)
|
|
||||||
try(options.SetValueBool("2.1", cfg.Animations), &err)
|
|
||||||
try(options.SetValueBool("2.3", cfg.PlayMusic), &err)
|
|
||||||
try(options.SetValueBool("2.4", cfg.CombatVoices), &err)
|
|
||||||
try(options.SetValueBool("2.5", cfg.ShowGrid), &err)
|
|
||||||
try(options.SetValueBool("2.6", cfg.ShowPaths), &err)
|
|
||||||
try(options.SetValueBool("2.7", cfg.PointSaving), &err)
|
|
||||||
try(options.SetValueInt("2.9", cfg.ResolutionIndex()), &err)
|
|
||||||
try(options.SetValueInt("2.10", cfg.MusicVolume), &err)
|
|
||||||
try(options.SetValueInt("2.11", cfg.SFXVolume), &err)
|
|
||||||
try(options.SetValueBool("2.25", cfg.AutoCutLevel), &err)
|
|
||||||
try(options.SetValueInt("2.26", cfg.UnitSpeed), &err)
|
|
||||||
try(options.SetValueInt("2.27", cfg.AnimSpeed), &err)
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *Ordoor) optionsIntoConfig(options *ui.Driver) error {
|
|
||||||
cfg := &o.config.Options
|
|
||||||
var resIdx int // needs handling manually
|
|
||||||
var err error
|
|
||||||
|
|
||||||
try(options.ValueBool("2.1", &cfg.PlayMovies), &err)
|
|
||||||
try(options.ValueBool("2.2", &cfg.Animations), &err)
|
|
||||||
try(options.ValueBool("2.3", &cfg.PlayMusic), &err)
|
|
||||||
try(options.ValueBool("2.4", &cfg.CombatVoices), &err)
|
|
||||||
try(options.ValueBool("2.5", &cfg.ShowGrid), &err)
|
|
||||||
try(options.ValueBool("2.6", &cfg.ShowPaths), &err)
|
|
||||||
try(options.ValueBool("2.7", &cfg.PointSaving), &err)
|
|
||||||
try(options.ValueInt("2.9", &resIdx), &err)
|
|
||||||
try(options.ValueInt("2.10", &cfg.MusicVolume), &err)
|
|
||||||
try(options.ValueInt("2.11", &cfg.SFXVolume), &err)
|
|
||||||
try(options.ValueBool("2.25", &cfg.AutoCutLevel), &err)
|
|
||||||
try(options.ValueInt("2.26", &cfg.UnitSpeed), &err)
|
|
||||||
try(options.ValueInt("2.27", &cfg.AnimSpeed), &err)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg.SetResolutionIndex(resIdx)
|
|
||||||
|
|
||||||
if err := o.config.Save(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: emit events, rather than just modifying state here
|
|
||||||
if o.music != nil && o.music.IsPlaying() != cfg.PlayMusic {
|
|
||||||
if cfg.PlayMusic {
|
|
||||||
o.music.Rewind()
|
|
||||||
o.music.Play()
|
|
||||||
} else {
|
|
||||||
o.music.Pause()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *Ordoor) buildDriver(name string) (*ui.Driver, error) {
|
|
||||||
menu, err := o.assets.Menu(name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
driver, err := ui.NewDriver(menu)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return driver, nil
|
|
||||||
}
|
|
@@ -14,13 +14,13 @@ import (
|
|||||||
|
|
||||||
"code.ur.gs/lupine/ordoor/internal/assetstore"
|
"code.ur.gs/lupine/ordoor/internal/assetstore"
|
||||||
"code.ur.gs/lupine/ordoor/internal/config"
|
"code.ur.gs/lupine/ordoor/internal/config"
|
||||||
|
"code.ur.gs/lupine/ordoor/internal/ordoor/flow"
|
||||||
"code.ur.gs/lupine/ordoor/internal/ui"
|
"code.ur.gs/lupine/ordoor/internal/ui"
|
||||||
)
|
)
|
||||||
|
|
||||||
type gameState int
|
type gameState int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
StateInitial gameState = 0
|
|
||||||
StateInterface gameState = 1
|
StateInterface gameState = 1
|
||||||
StateExit gameState = 666
|
StateExit gameState = 666
|
||||||
)
|
)
|
||||||
@@ -39,7 +39,7 @@ type Ordoor struct {
|
|||||||
nextState gameState
|
nextState gameState
|
||||||
|
|
||||||
// Relevant to interface state
|
// Relevant to interface state
|
||||||
driver *ui.Driver
|
flow *flow.Flow
|
||||||
}
|
}
|
||||||
|
|
||||||
func Run(configFile string, overrideX, overrideY int) error {
|
func Run(configFile string, overrideX, overrideY int) error {
|
||||||
@@ -60,7 +60,7 @@ func Run(configFile string, overrideX, overrideY int) error {
|
|||||||
ordoor := &Ordoor{
|
ordoor := &Ordoor{
|
||||||
assets: assets,
|
assets: assets,
|
||||||
config: cfg,
|
config: cfg,
|
||||||
state: StateInitial,
|
state: StateInterface,
|
||||||
nextState: StateInterface,
|
nextState: StateInterface,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,6 +79,10 @@ func Run(configFile string, overrideX, overrideY int) error {
|
|||||||
|
|
||||||
ordoor.win = win
|
ordoor.win = win
|
||||||
|
|
||||||
|
if err := ordoor.setupFlow(); err != nil {
|
||||||
|
return fmt.Errorf("failed to setup UI flow: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
if err := ordoor.Run(); err != nil {
|
if err := ordoor.Run(); err != nil {
|
||||||
return fmt.Errorf("Run returned %v", err)
|
return fmt.Errorf("Run returned %v", err)
|
||||||
}
|
}
|
||||||
@@ -93,7 +97,7 @@ func (o *Ordoor) Run() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := o.win.Run()
|
err := o.win.Run()
|
||||||
if err == errExit {
|
if err == flow.ErrExit {
|
||||||
log.Printf("Exit requested")
|
log.Printf("Exit requested")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -128,14 +132,15 @@ func (o *Ordoor) PlayMusic(name string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Ordoor) setupInterface() error {
|
func (o *Ordoor) setupFlow() error {
|
||||||
o.PlayMusic("music_interface")
|
o.PlayMusic("music_interface")
|
||||||
main, err := o.mainDriver()
|
|
||||||
|
flow, err := flow.New(o.assets, o.config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
o.driver = main
|
o.flow = flow
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -145,10 +150,6 @@ func (o *Ordoor) Update(screenX, screenY int) error {
|
|||||||
if o.state != o.nextState {
|
if o.state != o.nextState {
|
||||||
log.Printf("State transition: %v -> %v", o.state, o.nextState)
|
log.Printf("State transition: %v -> %v", o.state, o.nextState)
|
||||||
switch o.nextState {
|
switch o.nextState {
|
||||||
case StateInterface: // Setup, move state to interface
|
|
||||||
if err := o.setupInterface(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
case StateExit:
|
case StateExit:
|
||||||
{
|
{
|
||||||
return errExit
|
return errExit
|
||||||
@@ -161,9 +162,19 @@ func (o *Ordoor) Update(screenX, screenY int) error {
|
|||||||
// State transition is finished, hooray
|
// State transition is finished, hooray
|
||||||
o.state = o.nextState
|
o.state = o.nextState
|
||||||
|
|
||||||
|
// Ensure music is doing the right thing
|
||||||
|
if o.music != nil && o.music.IsPlaying() != o.config.Options.PlayMusic {
|
||||||
|
if o.config.Options.PlayMusic {
|
||||||
|
o.music.Rewind()
|
||||||
|
o.music.Play()
|
||||||
|
} else {
|
||||||
|
o.music.Pause()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch o.state {
|
switch o.state {
|
||||||
case StateInterface:
|
case StateInterface:
|
||||||
return o.driver.Update(screenX, screenY)
|
return o.flow.Update(screenX, screenY)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("Unknown state: %v", o.state)
|
return fmt.Errorf("Unknown state: %v", o.state)
|
||||||
}
|
}
|
||||||
@@ -172,7 +183,7 @@ func (o *Ordoor) Update(screenX, screenY int) error {
|
|||||||
func (o *Ordoor) Draw(screen *ebiten.Image) error {
|
func (o *Ordoor) Draw(screen *ebiten.Image) error {
|
||||||
switch o.state {
|
switch o.state {
|
||||||
case StateInterface:
|
case StateInterface:
|
||||||
return o.driver.Draw(screen)
|
return o.flow.Draw(screen)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("Unknown state: %v", o.state)
|
return fmt.Errorf("Unknown state: %v", o.state)
|
||||||
}
|
}
|
||||||
|
@@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"runtime/debug"
|
||||||
"runtime/pprof"
|
"runtime/pprof"
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten"
|
"github.com/hajimehoshi/ebiten"
|
||||||
@@ -67,7 +68,18 @@ func (w *Window) Layout(_, _ int) (int, int) {
|
|||||||
return w.xRes, w.yRes
|
return w.xRes, w.yRes
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Window) Update(screen *ebiten.Image) error {
|
func (w *Window) Update(screen *ebiten.Image) (outErr error) {
|
||||||
|
// Ebiten does not like it if we panic inside its main loop
|
||||||
|
defer func() {
|
||||||
|
if panicErr := recover(); panicErr != nil {
|
||||||
|
if w.debug {
|
||||||
|
debug.PrintStack()
|
||||||
|
}
|
||||||
|
|
||||||
|
outErr = fmt.Errorf("Panic: %v", panicErr)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
if err := w.game.Update(screen.Size()); err != nil {
|
if err := w.game.Update(screen.Size()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user