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/config"
|
||||
"code.ur.gs/lupine/ordoor/internal/ordoor/flow"
|
||||
"code.ur.gs/lupine/ordoor/internal/ui"
|
||||
)
|
||||
|
||||
type gameState int
|
||||
|
||||
const (
|
||||
StateInitial gameState = 0
|
||||
StateInterface gameState = 1
|
||||
StateExit gameState = 666
|
||||
)
|
||||
@@ -39,7 +39,7 @@ type Ordoor struct {
|
||||
nextState gameState
|
||||
|
||||
// Relevant to interface state
|
||||
driver *ui.Driver
|
||||
flow *flow.Flow
|
||||
}
|
||||
|
||||
func Run(configFile string, overrideX, overrideY int) error {
|
||||
@@ -60,7 +60,7 @@ func Run(configFile string, overrideX, overrideY int) error {
|
||||
ordoor := &Ordoor{
|
||||
assets: assets,
|
||||
config: cfg,
|
||||
state: StateInitial,
|
||||
state: StateInterface,
|
||||
nextState: StateInterface,
|
||||
}
|
||||
|
||||
@@ -79,6 +79,10 @@ func Run(configFile string, overrideX, overrideY int) error {
|
||||
|
||||
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 {
|
||||
return fmt.Errorf("Run returned %v", err)
|
||||
}
|
||||
@@ -93,7 +97,7 @@ func (o *Ordoor) Run() error {
|
||||
}
|
||||
|
||||
err := o.win.Run()
|
||||
if err == errExit {
|
||||
if err == flow.ErrExit {
|
||||
log.Printf("Exit requested")
|
||||
return nil
|
||||
}
|
||||
@@ -128,14 +132,15 @@ func (o *Ordoor) PlayMusic(name string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *Ordoor) setupInterface() error {
|
||||
func (o *Ordoor) setupFlow() error {
|
||||
o.PlayMusic("music_interface")
|
||||
main, err := o.mainDriver()
|
||||
|
||||
flow, err := flow.New(o.assets, o.config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
o.driver = main
|
||||
o.flow = flow
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -145,10 +150,6 @@ func (o *Ordoor) Update(screenX, screenY int) error {
|
||||
if o.state != o.nextState {
|
||||
log.Printf("State transition: %v -> %v", o.state, o.nextState)
|
||||
switch o.nextState {
|
||||
case StateInterface: // Setup, move state to interface
|
||||
if err := o.setupInterface(); err != nil {
|
||||
return err
|
||||
}
|
||||
case StateExit:
|
||||
{
|
||||
return errExit
|
||||
@@ -161,9 +162,19 @@ func (o *Ordoor) Update(screenX, screenY int) error {
|
||||
// State transition is finished, hooray
|
||||
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 {
|
||||
case StateInterface:
|
||||
return o.driver.Update(screenX, screenY)
|
||||
return o.flow.Update(screenX, screenY)
|
||||
default:
|
||||
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 {
|
||||
switch o.state {
|
||||
case StateInterface:
|
||||
return o.driver.Draw(screen)
|
||||
return o.flow.Draw(screen)
|
||||
default:
|
||||
return fmt.Errorf("Unknown state: %v", o.state)
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
"runtime/pprof"
|
||||
|
||||
"github.com/hajimehoshi/ebiten"
|
||||
@@ -67,7 +68,18 @@ func (w *Window) Layout(_, _ int) (int, int) {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
Reference in New Issue
Block a user