2020-03-22 17:19:26 +00:00
|
|
|
// package ordoor implements the full WH40K.EXE functionality, and is used from
|
|
|
|
// cmd/ordoor/main.go
|
2018-10-13 03:24:10 +01:00
|
|
|
//
|
|
|
|
// Entrypoint is Run()
|
2020-03-22 17:19:26 +00:00
|
|
|
package ordoor
|
2018-10-13 03:24:10 +01:00
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2020-03-22 19:12:44 +00:00
|
|
|
"log"
|
2018-10-13 03:24:10 +01:00
|
|
|
|
2020-03-22 19:12:44 +00:00
|
|
|
"github.com/hajimehoshi/ebiten"
|
|
|
|
"github.com/hajimehoshi/ebiten/audio"
|
|
|
|
|
|
|
|
"code.ur.gs/lupine/ordoor/internal/assetstore"
|
2019-12-31 01:55:58 +00:00
|
|
|
"code.ur.gs/lupine/ordoor/internal/config"
|
2020-04-10 22:49:57 +01:00
|
|
|
"code.ur.gs/lupine/ordoor/internal/flow"
|
2020-04-11 00:13:28 +01:00
|
|
|
"code.ur.gs/lupine/ordoor/internal/ship"
|
2020-03-22 19:12:44 +00:00
|
|
|
"code.ur.gs/lupine/ordoor/internal/ui"
|
|
|
|
)
|
|
|
|
|
|
|
|
type gameState int
|
|
|
|
|
2020-03-22 17:19:26 +00:00
|
|
|
type Ordoor struct {
|
2020-03-22 19:12:44 +00:00
|
|
|
assets *assetstore.AssetStore
|
|
|
|
config *config.Config
|
|
|
|
music *audio.Player
|
|
|
|
win *ui.Window
|
|
|
|
|
|
|
|
// Relevant to interface state
|
2020-03-25 02:12:17 +00:00
|
|
|
flow *flow.Flow
|
2020-04-10 19:24:03 +01:00
|
|
|
|
|
|
|
// Relevant to campaign state
|
2020-04-11 00:13:28 +01:00
|
|
|
ship *ship.Ship
|
2018-10-13 03:24:10 +01:00
|
|
|
}
|
|
|
|
|
2020-03-23 00:33:29 +00:00
|
|
|
func Run(configFile string, overrideX, overrideY int) error {
|
2018-10-13 03:24:10 +01:00
|
|
|
cfg, err := config.Load(configFile)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Couldn't load config file: %v", err)
|
|
|
|
}
|
|
|
|
|
2020-03-22 19:12:44 +00:00
|
|
|
assets, err := assetstore.New(cfg.Ordoor.DataDir)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Failed to initialize asset store: %v", err)
|
|
|
|
}
|
|
|
|
|
2020-04-02 01:21:32 +01:00
|
|
|
defaults, err := assets.DefaultOptions()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Failed to read option defaults: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
cfg.Defaults = defaults
|
|
|
|
if cfg.HasUnsetOptions() {
|
|
|
|
if err := cfg.ResetDefaults(); err != nil {
|
|
|
|
return fmt.Errorf("Failed to set options on first-start: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-22 19:12:44 +00:00
|
|
|
if _, err := audio.NewContext(48000); err != nil {
|
|
|
|
return fmt.Errorf("Failed to set up audio context: %v", err)
|
|
|
|
}
|
|
|
|
|
2020-03-22 17:19:26 +00:00
|
|
|
ordoor := &Ordoor{
|
2020-04-11 00:13:28 +01:00
|
|
|
assets: assets,
|
|
|
|
config: cfg,
|
|
|
|
ship: &ship.Ship{},
|
2018-10-13 03:24:10 +01:00
|
|
|
}
|
|
|
|
|
2020-03-23 00:33:29 +00:00
|
|
|
x, y := cfg.Options.XRes, cfg.Options.YRes
|
|
|
|
if overrideX > 0 {
|
|
|
|
x = overrideX
|
|
|
|
}
|
|
|
|
if overrideY > 0 {
|
|
|
|
y = overrideY
|
|
|
|
}
|
|
|
|
|
|
|
|
win, err := ui.NewWindow(ordoor, "Ordoor", x, y)
|
2020-03-22 19:12:44 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Failed to create window: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
ordoor.win = win
|
2018-10-13 03:24:10 +01:00
|
|
|
|
2020-03-25 02:12:17 +00:00
|
|
|
if err := ordoor.setupFlow(); err != nil {
|
|
|
|
return fmt.Errorf("failed to setup UI flow: %v", err)
|
|
|
|
}
|
|
|
|
|
2020-03-22 19:12:44 +00:00
|
|
|
if err := ordoor.Run(); err != nil {
|
2020-04-11 00:13:28 +01:00
|
|
|
return fmt.Errorf("Run finished with error: %v", err)
|
2020-03-22 19:12:44 +00:00
|
|
|
}
|
2020-03-22 02:58:52 +00:00
|
|
|
|
2018-10-13 03:24:10 +01:00
|
|
|
return nil
|
|
|
|
}
|
2020-03-22 19:12:44 +00:00
|
|
|
|
|
|
|
func (o *Ordoor) Run() error {
|
2020-03-26 23:35:34 +00:00
|
|
|
// FIXME: we're missing a screen about SSI here
|
2020-03-22 22:12:59 +00:00
|
|
|
if o.config.Options.PlayMovies {
|
|
|
|
o.PlayUnskippableVideo("LOGOS")
|
|
|
|
o.PlaySkippableVideo("movie1")
|
|
|
|
}
|
2020-03-22 19:12:44 +00:00
|
|
|
|
|
|
|
err := o.win.Run()
|
2020-03-25 02:12:17 +00:00
|
|
|
if err == flow.ErrExit {
|
2020-03-22 22:12:59 +00:00
|
|
|
log.Printf("Exit requested")
|
2020-03-22 19:12:44 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Only one music track can play at a time. This is handled at the toplevel.
|
|
|
|
// FIXME: should take references from Sounds.dat
|
2020-04-11 00:13:28 +01:00
|
|
|
// FIXME: music probably properly belongs to flow. This package can just do
|
|
|
|
// initialization and wire the flow to the ship?
|
2020-03-22 19:12:44 +00:00
|
|
|
func (o *Ordoor) PlayMusic(name string) error {
|
|
|
|
if o.music != nil {
|
|
|
|
if err := o.music.Close(); err != nil {
|
|
|
|
return fmt.Errorf("Failed to close old music: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sound, err := o.assets.Sound(name)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Failed to find sound %v: %v", name, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
player, err := sound.InfinitePlayer()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Failed to generate music player for %v: %v", name, err)
|
|
|
|
}
|
|
|
|
o.music = player
|
2020-03-23 00:33:29 +00:00
|
|
|
|
|
|
|
if o.config.Options.PlayMusic {
|
|
|
|
player.Play()
|
|
|
|
}
|
2020-03-22 19:12:44 +00:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-03-25 02:12:17 +00:00
|
|
|
func (o *Ordoor) setupFlow() error {
|
2020-03-22 19:12:44 +00:00
|
|
|
o.PlayMusic("music_interface")
|
2020-03-25 02:12:17 +00:00
|
|
|
|
2020-04-11 00:13:28 +01:00
|
|
|
flow, err := flow.New(o.assets, o.config, o.ship)
|
2020-03-22 19:12:44 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-03-25 02:12:17 +00:00
|
|
|
o.flow = flow
|
2020-03-22 19:12:44 +00:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (o *Ordoor) Update(screenX, screenY int) error {
|
2020-03-25 02:12:17 +00:00
|
|
|
// 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()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-11 00:13:28 +01:00
|
|
|
return o.flow.Update(screenX, screenY)
|
2020-03-22 19:12:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (o *Ordoor) Draw(screen *ebiten.Image) error {
|
2020-04-11 00:13:28 +01:00
|
|
|
return o.flow.Draw(screen)
|
2020-03-22 19:12:44 +00:00
|
|
|
}
|
2020-04-10 20:54:58 +01:00
|
|
|
|
|
|
|
func (o *Ordoor) Cursor() (*ebiten.Image, *ebiten.DrawImageOptions, error) {
|
2020-04-11 00:13:28 +01:00
|
|
|
return o.flow.Cursor()
|
2020-04-10 20:54:58 +01:00
|
|
|
}
|