From 2f65cd312a46ae43d4bedab433bb96f86ac09816 Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Tue, 14 Apr 2020 15:11:25 +0100 Subject: [PATCH] Wire up inventory select to ship state Also mixed into this commit: * Use returning drivers where possible * Make the credits screen returnable via click --- internal/flow/bridge.go | 25 ++++--------- internal/flow/choices.go | 20 +++++++++++ internal/flow/credits.go | 8 +++++ internal/flow/flow.go | 50 ++++++++++++++++++-------- internal/flow/load_game.go | 2 +- internal/flow/new_game.go | 66 ++++++++++++++++++++++++++--------- internal/flow/options.go | 2 +- internal/flow/save_game.go | 10 ++++++ internal/ordoor/ordoor.go | 4 +-- internal/ship/ship.go | 23 ++++++++++++ internal/ui/noninteractive.go | 1 + 11 files changed, 158 insertions(+), 53 deletions(-) create mode 100644 internal/flow/choices.go create mode 100644 internal/flow/credits.go create mode 100644 internal/flow/save_game.go diff --git a/internal/flow/bridge.go b/internal/flow/bridge.go index 4a94ed4..1a62bb0 100644 --- a/internal/flow/bridge.go +++ b/internal/flow/bridge.go @@ -4,11 +4,13 @@ func (f *Flow) linkBridge() { // FIXME: sometimes these doors are frozen, depending on ship state, but we // don't implement that yet. - f.onClick(bridge, "2.1", f.setDriver(briefing)) // Mission briefing clickable - f.onClick(bridge, "2.2", f.setDriver(choices)) // Options door hotspot - f.onClick(bridge, "2.4", f.playNextScenario(bridge)) // Enter combat door hotspot - f.setFreeze(bridge, "2.6", true) // TODO: Vehicle configure door hotspot - f.onClick(bridge, "2.8", f.setDriver(arrange)) // Squads configure door hotspot + f.onClick(bridge, "2.1", f.setReturningDriver(bridge, briefing)) // Mission briefing clickable + f.onClick(bridge, "2.2", f.setReturningDriver(bridge, choices)) // Options door hotspot + f.onClick(bridge, "2.4", f.playNextScenario(bridge)) // Enter combat door hotspot + f.setFreeze(bridge, "2.6", true) // TODO: Vehicle configure door hotspot + + // FIXME: setReturningDriver would leave behind junk + f.onClick(bridge, "2.8", f.setDriver(arrange)) // Squads configure door hotspot. // link children f.linkBriefing() @@ -21,19 +23,6 @@ func (f *Flow) linkBriefing() { f.onClick(briefing, "3.1", f.setDriver(bridge)) } -func (f *Flow) linkChoices() { - f.onClick(choices, "2.1", f.setDriver(loadGame)) // Load another game button - f.onClick(choices, "2.2", f.setDriver(saveGame)) // Save this game button - f.onClick(choices, "2.3", f.setReturningDriver(choices, options)) // More options button - - // FIXME: wipe out game state when this goes through - f.onClick(choices, "2.4", f.setDriver(main)) // Restart button - - f.onClick(choices, "2.5", f.setDriver(credits)) // Credits button - f.onClick(choices, "2.6", f.setExit) // Quit button - f.onClick(choices, "2.7", f.setDriver(bridge)) // Back button -} - func (f *Flow) linkArrange() { // FIXME: we should be operating on game data in here f.onClick(arrange, "8.1", f.setDriver(bridge)) // Return to bridge ("cathedral") diff --git a/internal/flow/choices.go b/internal/flow/choices.go new file mode 100644 index 0000000..460e76b --- /dev/null +++ b/internal/flow/choices.go @@ -0,0 +1,20 @@ +package flow + +func (f *Flow) linkChoices() { + f.onClick(choices, "2.1", f.setReturningDriver(choices, loadGame)) // Load another game button + f.onClick(choices, "2.2", f.setReturningDriver(choices, saveGame)) // Save this game button + f.onClick(choices, "2.3", f.setReturningDriver(choices, options)) // More options button + f.onClick(choices, "2.4", func() { // New Game button. FIXME: should ask about the emperor + f.ship.Reset() // Throws away in-progress game + f.reset() + }) + + f.onClick(choices, "2.5", f.setReturningDriver(choices, credits)) // Credits button + f.onClick(choices, "2.6", f.setExit) // Quit button. FIXME: should ask about the emperor + f.onClick(choices, "2.7", f.returnToLastDriver(choices)) // Back button + + // loadGame is linked by main + f.linkSaveGame() + // options is linked by main + f.linkCredits() +} diff --git a/internal/flow/credits.go b/internal/flow/credits.go new file mode 100644 index 0000000..e49354d --- /dev/null +++ b/internal/flow/credits.go @@ -0,0 +1,8 @@ +package flow + +func (f *Flow) linkCredits() { + // Clicking anywhere in credits should return us + f.onClick(credits, "1", f.returnToLastDriver(credits)) + + // TODO: lots of text +} diff --git a/internal/flow/flow.go b/internal/flow/flow.go index e27eb24..e2ee8f8 100644 --- a/internal/flow/flow.go +++ b/internal/flow/flow.go @@ -86,15 +86,10 @@ func New(assets *assetstore.AssetStore, config *config.Config, ship *ship.Ship) 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) + out.reset() - return out, nil + return out, out.exit } func buildDriver(assets *assetstore.AssetStore, name driverName) (*ui.Driver, error) { @@ -164,11 +159,11 @@ func (f *Flow) Cursor() (*ebiten.Image, *ebiten.DrawImageOptions, error) { func (f *Flow) linkDrivers() { // linkMain - 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.setReturningDriver(main, options)) // Options - f.onClick(main, "2.5", f.setExit) // Quit + f.onClick(main, "2.1", f.setReturningDriver(main, newGame)) // New game + f.onClick(main, "2.2", f.setReturningDriver(main, loadGame)) // Load game + f.setFreeze(main, "2.3", true) // Multiplayer - disable for now + f.onClick(main, "2.4", f.setReturningDriver(main, options)) // Options + f.onClick(main, "2.5", f.setExit) // Quit // Now link immediate children. They will link their children, and so on f.linkNewGame() @@ -217,6 +212,18 @@ func (f *Flow) setValueBool(driver driverName, id string, value bool) { f.exit = f.drivers[driver].SetValueBool(id, value) } +func (f *Flow) valueBool(driver driverName, id string) bool { + if f.exit != nil { + return false + } + + var value bool + + f.exit = f.drivers[driver].ValueBool(id, &value) + + return value +} + func (f *Flow) playNextScenario(from driverName) func() { return func() { log.Printf("Loading scenario: %v", f.ship.NextScenario) @@ -244,11 +251,26 @@ func (f *Flow) hideDialogue(driver driverName) func() { return f.drivers[driver].HideDialogue } +func (f *Flow) reset() { + if f.exit != nil { + return + } + + f.setDriverNow(main) // Back to the main interface + + // Wipe out any returns that may exist + f.returns = make(map[driverName]driverName) + + // FIXME: these should really happen via data binding. + f.resetLevelPlyInventorySelect() + f.exit = f.configIntoOptions() +} + func (f *Flow) setExit() { f.exit = ErrExit } // TODO: convert all to locators -func locator(d driverName, id string) string { - return fmt.Sprintf("%v:%v", strings.ToLower(string(d)), id) +func locator(driver driverName, id string) string { + return fmt.Sprintf("%v:%v", strings.ToLower(string(driver)), id) } diff --git a/internal/flow/load_game.go b/internal/flow/load_game.go index 4cf3021..a443d03 100644 --- a/internal/flow/load_game.go +++ b/internal/flow/load_game.go @@ -2,5 +2,5 @@ package flow func (f *Flow) linkLoadGame() { // Load game - f.onClick(loadGame, "3.3", f.setDriver(main)) // Cancel button + f.onClick(loadGame, "3.3", f.returnToLastDriver(loadGame)) // Cancel button } diff --git a/internal/flow/new_game.go b/internal/flow/new_game.go index 5fae978..6ce9d90 100644 --- a/internal/flow/new_game.go +++ b/internal/flow/new_game.go @@ -1,36 +1,68 @@ package flow +import ( + "code.ur.gs/lupine/ordoor/internal/ship" +) + func (f *Flow) linkNewGame() { // 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 + f.onClick(newGame, "2.1", f.setReturningDriver(newGame, levelPly)) // New campaign button + f.onClick(newGame, "2.2", f.setReturningDriver(newGame, singles)) // Single scenario button + f.onClick(newGame, "2.3", f.setReturningDriver(newGame, randomMap)) // Random scenario button + f.onClick(newGame, "2.4", f.returnToLastDriver(newGame)) // Back button f.linkLevelPly() f.linkSingles() f.linkRandomMap() } -func (f *Flow) linkLevelPly() { - // We want the default difficulty level to be Veteran, not Hero. +func (f *Flow) resetLevelPlyInventorySelect() { // FIXME: Make the radio button respect changes via setValue - resetLevel := func() { - f.setValueBool(levelPly, "2.1", false) - f.setValueBool(levelPly, "2.2", true) + for _, v := range []string{"2.1", "2.2", "2.3", "2.4"} { + f.setValueBool(levelPly, v, false) } - resetLevel() + switch f.ship.Difficulty { + case ship.DifficultyLevelMarine: + f.setValueBool(levelPly, "2.1", true) + case ship.DifficultyLevelVeteran: + f.setValueBool(levelPly, "2.2", true) + case ship.DifficultyLevelHero: + f.setValueBool(levelPly, "2.3", true) + case ship.DifficultyLevelMighty: + f.setValueBool(levelPly, "2.4", true) + } +} + +func (f *Flow) linkLevelPly() { f.onClick(levelPly, "2.5", func() { // Back button - resetLevel() - f.setDriverNow(newGame) + f.resetLevelPlyInventorySelect() // FIXME: should use data binding + f.returnToLastDriverNow(levelPly) }) - // FIXME: we should select a savegame if Mighty Hero is selected here - // FIXME: we should show a movie here. Need an internal SMK player first - // FIXME: we should set up new game state here! + // FIXME: we should be able to read the difficulty level from the group f.onClick(levelPly, "2.7", func() { // Select button + if f.valueBool(levelPly, "2.1") { + f.ship.Difficulty = ship.DifficultyLevelMarine + } + + if f.valueBool(levelPly, "2.2") { + f.ship.Difficulty = ship.DifficultyLevelVeteran + } + + if f.valueBool(levelPly, "2.3") { + f.ship.Difficulty = ship.DifficultyLevelHero + } + + if f.valueBool(levelPly, "2.4") { + // FIXME: we should select a savegame. Mighty Hero disables manual saves. + f.ship.Difficulty = ship.DifficultyLevelMighty + } + f.ship.NextScenario = f.generic.CampaignMaps[0] + + // FIXME: we should show a movie here. Need an internal SMK player first + f.setDriverNow(bridge) }) @@ -39,9 +71,9 @@ func (f *Flow) linkLevelPly() { } func (f *Flow) linkSingles() { - f.onClick(singles, "4.11", f.setDriver(newGame)) // Back button + f.onClick(singles, "4.11", f.returnToLastDriver(singles)) // Back button } func (f *Flow) linkRandomMap() { - f.onClick(randomMap, "2.19", f.setDriver(newGame)) // Back button + f.onClick(randomMap, "2.19", f.returnToLastDriver(randomMap)) // Back button } diff --git a/internal/flow/options.go b/internal/flow/options.go index 61ffdb0..095dd40 100644 --- a/internal/flow/options.go +++ b/internal/flow/options.go @@ -26,7 +26,7 @@ func (f *Flow) acceptOptions() { log.Printf("Saving options to config failed: %v", err) f.exit = err } else { - f.setDriverNow(main) + f.returnToLastDriverNow(options) } } diff --git a/internal/flow/save_game.go b/internal/flow/save_game.go new file mode 100644 index 0000000..d3884cb --- /dev/null +++ b/internal/flow/save_game.go @@ -0,0 +1,10 @@ +package flow + +func (f *Flow) linkSaveGame() { + // Save game button is disabled unless a listbox item is selected + // 3.2 is a hypertext that should be displayed when 3.1 is disabled... but + // it has no DESC. + f.setFreeze(saveGame, "3.1", true) + f.onClick(saveGame, "3.1", func() {}) // TODO: Save Game button + f.onClick(saveGame, "3.3", f.returnToLastDriver(saveGame)) // Back button +} diff --git a/internal/ordoor/ordoor.go b/internal/ordoor/ordoor.go index cbb7020..2f5603d 100644 --- a/internal/ordoor/ordoor.go +++ b/internal/ordoor/ordoor.go @@ -63,7 +63,7 @@ func Run(configFile string, overrideX, overrideY int) error { ordoor := &Ordoor{ assets: assets, config: cfg, - ship: &ship.Ship{}, + ship: ship.New(), } x, y := cfg.Options.XRes, cfg.Options.YRes @@ -95,7 +95,7 @@ func Run(configFile string, overrideX, overrideY int) error { func (o *Ordoor) Run() error { // FIXME: we're missing a screen about SSI here if o.config.Options.PlayMovies { - o.PlayUnskippableVideo("LOGOS") + o.PlaySkippableVideo("LOGOS") o.PlaySkippableVideo("movie1") } diff --git a/internal/ship/ship.go b/internal/ship/ship.go index 3635662..0259f6f 100644 --- a/internal/ship/ship.go +++ b/internal/ship/ship.go @@ -1,8 +1,18 @@ package ship +type DifficultyLevel int + +const ( + DifficultyLevelMarine DifficultyLevel = 0 + DifficultyLevelVeteran DifficultyLevel = 1 + DifficultyLevelHero DifficultyLevel = 2 + DifficultyLevelMighty DifficultyLevel = 3 +) + // Ship encapsulates campaign state, including current location in the campaign, // marines and their stats, supplies, etc. type Ship struct { + Difficulty DifficultyLevel NextScenario string Squads []*Squad @@ -68,3 +78,16 @@ type Honours struct { PuritySeal bool ImperialLaurel bool } + +func New() *Ship { + s := &Ship{} + s.Reset() + + return s +} + +func (s *Ship) Reset() { + *s = Ship{ + Difficulty: DifficultyLevelVeteran, // Default difficulty level + } +} diff --git a/internal/ui/noninteractive.go b/internal/ui/noninteractive.go index 056f70f..9a06acf 100644 --- a/internal/ui/noninteractive.go +++ b/internal/ui/noninteractive.go @@ -83,6 +83,7 @@ func (d *Driver) buildStatic(p *menus.Properties) (*noninteractive, *Widget, err widget := &Widget{ Locator: ni.locator, + ownClickables: []clickable{ni}, // FIXME: credits background needs to be clickable ownHoverables: []hoverable{ni}, ownPaintables: []paintable{ni}, }