Compare commits
1 Commits
7081db42f4
...
6d58d12536
Author | SHA1 | Date | |
---|---|---|---|
6d58d12536 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -5,5 +5,4 @@
|
|||||||
/SL
|
/SL
|
||||||
/SaW
|
/SaW
|
||||||
/WoW
|
/WoW
|
||||||
/WoW-CD
|
|
||||||
/bin
|
/bin
|
||||||
|
67
README.md
67
README.md
@@ -72,10 +72,6 @@ The menu system seen in Chaos Gate is not present; instead, there is a `BUTTONS`
|
|||||||
directory and a lot of `pcx` files under `PIC` that, I suspect, do the job for
|
directory and a lot of `pcx` files under `PIC` that, I suspect, do the job for
|
||||||
this game.
|
this game.
|
||||||
|
|
||||||
Even with a full installation, Wages of War leaves a lot of data on the CD. It
|
|
||||||
may be best to run solely from the `WOW` directory on the CD, assuming it's a
|
|
||||||
strict superset of what gets installed, data-wise.
|
|
||||||
|
|
||||||
## Long-term goals
|
## Long-term goals
|
||||||
|
|
||||||
Once full playthrough of the official single-player campaign for all four games
|
Once full playthrough of the official single-player campaign for all four games
|
||||||
@@ -100,8 +96,7 @@ Completely new fantasy game using the same engine.
|
|||||||
## Building from source
|
## Building from source
|
||||||
|
|
||||||
I'm writing code in Go at the moment, so you'll need to have a Go runtime
|
I'm writing code in Go at the moment, so you'll need to have a Go runtime
|
||||||
installed on your system. Dependency management uses `go mod`, so ensure you
|
installed on your system:
|
||||||
have at least Go 1.11.
|
|
||||||
|
|
||||||
```
|
```
|
||||||
$ go version
|
$ go version
|
||||||
@@ -119,25 +114,28 @@ Debian:
|
|||||||
You can then run `make all` in the source tree to get the binaries that are
|
You can then run `make all` in the source tree to get the binaries that are
|
||||||
present at the moment.
|
present at the moment.
|
||||||
|
|
||||||
## Configuring
|
Place your WH40K: Chaos Gate installation in `./orig` to benefit from automatic
|
||||||
|
path defaults. Otherwise, point to it with `-game-path`
|
||||||
|
|
||||||
Since we support multiple games, a fair bit of configuration is required. Copy
|
The `view-map` binary attempts to render a map, and is the current focus of
|
||||||
`config.toml.example` to `config.toml` and edit it to your requirements. The
|
effort. Once I can render a whole map, including pre-placed characters (cultist
|
||||||
`data_dir` for the engine(s) you want to use is probably the most important bit,
|
scum), things can start to get more interesting.
|
||||||
along with the `default_engine`.
|
|
||||||
|
|
||||||
The various games all use snapshots of the original engine at different points
|
Current status: almost pixel-perfect map rendering. Static objects (four per map
|
||||||
in time, and specify a lot in code that we need to specify in data. That should
|
coordinate: floor, centre, left, and right) are rendered fine, and each Z level
|
||||||
all go into the config file, so new games will be able to adapt the engine to
|
looks good. There are a few minor artifacts here and there.
|
||||||
their needs.
|
|
||||||
|
|
||||||
## Running
|
Characters and animations aren't touched at all yet. Rendering performance is
|
||||||
|
poor. No gameplay, no campaign logic. Interaction with the play area is minimal
|
||||||
|
and limited to pan, zoom, and click for basic console output.
|
||||||
|
|
||||||
|
Still, I'm proud of myself.
|
||||||
|
|
||||||
To run:
|
To run:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ make view-map
|
$ make view-map
|
||||||
$ ./bin/view-map -map Chapter01
|
$ ./view-map -map Chapter01
|
||||||
```
|
```
|
||||||
|
|
||||||
Looks like this:
|
Looks like this:
|
||||||
@@ -147,29 +145,27 @@ Looks like this:
|
|||||||
Use the arrow keys to scroll around the map, the mouse wheel to zoom, and the
|
Use the arrow keys to scroll around the map, the mouse wheel to zoom, and the
|
||||||
`1` - `7` keys to change Z level.
|
`1` - `7` keys to change Z level.
|
||||||
|
|
||||||
Menus / UI widgets have fairly good support now; you can use the `view-menu`
|
Dependency management uses `go mod`, so ensure you have at least Go 1.11.
|
||||||
binary to inspect them:
|
|
||||||
|
There is the **start** of the menu / campaign flow in a `ordoor` binary:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ cp config.toml.example config.toml
|
||||||
|
$ make ordoor
|
||||||
|
$ ./ordoor
|
||||||
|
```
|
||||||
|
|
||||||
|
This plays the introductory videos so far, and nothing else.
|
||||||
|
|
||||||
|
Menus are in the process of being rendered; you can use the `view-menu` binary
|
||||||
|
to inspect them:
|
||||||
|
|
||||||
```
|
```
|
||||||
make view-menu
|
make view-menu
|
||||||
./bin/view-menu -menu Main
|
./view-menu -menu ./orig/Menu/Main.mnu
|
||||||
```
|
```
|
||||||
|
|
||||||
This renders the menus found in Chaos Gate and Soldiers At War. The Squad Leader
|
This menu *displays* OK, including
|
||||||
format seems basically the same, but has some extra files and aren't 8-bit
|
|
||||||
colour. They don't display at the moment. Wages of War uses a different format
|
|
||||||
altogether.
|
|
||||||
|
|
||||||
For Chaos Gate, there is the **start** of the game in an `ordoor` binary:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ make ordoor
|
|
||||||
$ ./bin/ordoor
|
|
||||||
```
|
|
||||||
|
|
||||||
The idea is to hook all the different parts together, and to an abstract game
|
|
||||||
state (which is called `ship` for ordoor), to make the whole thing playable. It
|
|
||||||
isn't playable *yet*, but it's heading in that direction.
|
|
||||||
|
|
||||||
## Sound
|
## Sound
|
||||||
|
|
||||||
@@ -184,3 +180,4 @@ $ ./scripts/convert-wav ./orig/Wav
|
|||||||
|
|
||||||
As with video playback, the ambition is to *eventually* remove this dependency
|
As with video playback, the ambition is to *eventually* remove this dependency
|
||||||
and operate on the unmodified files instead.
|
and operate on the unmodified files instead.
|
||||||
|
|
||||||
|
@@ -3,7 +3,7 @@ video_player = ["mpv", "--no-config", "--keep-open=no", "--force-window=no", "--
|
|||||||
default_engine = "ordoor"
|
default_engine = "ordoor"
|
||||||
|
|
||||||
[engines.geas] # Wages of War -> Gifts of Peace -> Geas
|
[engines.geas] # Wages of War -> Gifts of Peace -> Geas
|
||||||
data_dir = "./WoW-CD"
|
data_dir = "./WoW"
|
||||||
palette = "WagesOfWar"
|
palette = "WagesOfWar"
|
||||||
|
|
||||||
[engines.ordoor] # Chaos Gate -> Order Door -> Ordoor
|
[engines.ordoor] # Chaos Gate -> Order Door -> Ordoor
|
||||||
@@ -14,9 +14,9 @@ default_engine = "ordoor"
|
|||||||
data_dir = "./SaW"
|
data_dir = "./SaW"
|
||||||
palette = "SoldiersAtWar"
|
palette = "SoldiersAtWar"
|
||||||
|
|
||||||
[engines.sl] # Squad Leader -> ??? -> ???
|
# [engines.] Squad Leader -> ??? -> ???
|
||||||
data_dir = "./SL"
|
# data_dir = "./SL"
|
||||||
palette = "ChaosGate" # may not be relevant?
|
# palette = "SquadLeader" # may not be relevant?
|
||||||
|
|
||||||
[options]
|
[options]
|
||||||
play_movies = true
|
play_movies = true
|
||||||
|
1
go.mod
1
go.mod
@@ -9,7 +9,6 @@ require (
|
|||||||
github.com/jfreymuth/oggvorbis v1.0.1 // indirect
|
github.com/jfreymuth/oggvorbis v1.0.1 // indirect
|
||||||
github.com/kr/text v0.2.0 // indirect
|
github.com/kr/text v0.2.0 // indirect
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||||
github.com/samuel/go-pcx v0.0.0-20180426214139-d9c017170db4
|
|
||||||
github.com/stretchr/testify v1.5.1
|
github.com/stretchr/testify v1.5.1
|
||||||
golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5 // indirect
|
golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5 // indirect
|
||||||
golang.org/x/image v0.0.0-20200119044424-58c23975cae1
|
golang.org/x/image v0.0.0-20200119044424-58c23975cae1
|
||||||
|
2
go.sum
2
go.sum
@@ -45,8 +45,6 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA
|
|||||||
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
|
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/samuel/go-pcx v0.0.0-20180426214139-d9c017170db4 h1:Y/KOCu+ZLB730PudefxfsKVjtI0m0RhvFk9a0l4O1+c=
|
|
||||||
github.com/samuel/go-pcx v0.0.0-20180426214139-d9c017170db4/go.mod h1:qxuIawynlRhuaHowuXvd1xjyFWx87Ro4gkZlKRXtHnQ=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
|
@@ -8,8 +8,6 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten"
|
|
||||||
|
|
||||||
"code.ur.gs/lupine/ordoor/internal/config"
|
"code.ur.gs/lupine/ordoor/internal/config"
|
||||||
"code.ur.gs/lupine/ordoor/internal/data"
|
"code.ur.gs/lupine/ordoor/internal/data"
|
||||||
"code.ur.gs/lupine/ordoor/internal/idx"
|
"code.ur.gs/lupine/ordoor/internal/idx"
|
||||||
@@ -46,7 +44,6 @@ type AssetStore struct {
|
|||||||
fonts map[string]*Font
|
fonts map[string]*Font
|
||||||
generic *data.Generic
|
generic *data.Generic
|
||||||
idx *idx.Idx
|
idx *idx.Idx
|
||||||
images map[string]*ebiten.Image
|
|
||||||
maps map[string]*Map
|
maps map[string]*Map
|
||||||
menus map[string]*Menu
|
menus map[string]*Menu
|
||||||
objs map[string]*Object
|
objs map[string]*Object
|
||||||
@@ -112,7 +109,6 @@ func (a *AssetStore) Refresh() error {
|
|||||||
a.entries = newEntryMap
|
a.entries = newEntryMap
|
||||||
a.fonts = make(map[string]*Font)
|
a.fonts = make(map[string]*Font)
|
||||||
a.idx = nil
|
a.idx = nil
|
||||||
a.images = make(map[string]*ebiten.Image)
|
|
||||||
a.maps = make(map[string]*Map)
|
a.maps = make(map[string]*Map)
|
||||||
a.menus = make(map[string]*Menu)
|
a.menus = make(map[string]*Menu)
|
||||||
a.objs = make(map[string]*Object)
|
a.objs = make(map[string]*Object)
|
||||||
|
@@ -1,42 +0,0 @@
|
|||||||
package assetstore
|
|
||||||
|
|
||||||
import (
|
|
||||||
"image"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten"
|
|
||||||
_ "github.com/samuel/go-pcx/pcx" // PCX support
|
|
||||||
)
|
|
||||||
|
|
||||||
func (a *AssetStore) Image(name string) (*ebiten.Image, error) {
|
|
||||||
name = canonical(name)
|
|
||||||
if img, ok := a.images[name]; ok {
|
|
||||||
return img, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// baps, ordoor, geas store .pcx files in Pic
|
|
||||||
// TODO: SL stores .bmp files in Res
|
|
||||||
filename, err := a.lookup(name, "pcx", "Pic")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
f, err := os.Open(filename)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
rawImg, _, err := image.Decode(f)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
img, err := ebiten.NewImageFromImage(rawImg, ebiten.FilterDefault)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
a.images[name] = img
|
|
||||||
return img, nil
|
|
||||||
}
|
|
@@ -1,20 +0,0 @@
|
|||||||
package ordoor
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (o *Ordoor) DisplayImageFor(d time.Duration, name string) error {
|
|
||||||
img, err := o.assets.Image(name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
o.pic = img
|
|
||||||
go func() {
|
|
||||||
<-time.After(d)
|
|
||||||
o.pic = nil // FIXME: this is a race condition and a half
|
|
||||||
}()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
@@ -7,7 +7,6 @@ package ordoor
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten"
|
"github.com/hajimehoshi/ebiten"
|
||||||
"github.com/hajimehoshi/ebiten/audio"
|
"github.com/hajimehoshi/ebiten/audio"
|
||||||
@@ -30,10 +29,6 @@ type Ordoor struct {
|
|||||||
// Relevant to interface state
|
// Relevant to interface state
|
||||||
flow *flow.Flow
|
flow *flow.Flow
|
||||||
|
|
||||||
// FIXME: should be put inside flow
|
|
||||||
// If this is set, we display it instead of flow
|
|
||||||
pic *ebiten.Image
|
|
||||||
|
|
||||||
// Relevant to campaign state
|
// Relevant to campaign state
|
||||||
ship *ship.Ship
|
ship *ship.Ship
|
||||||
}
|
}
|
||||||
@@ -86,6 +81,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 finished with error: %v", err)
|
return fmt.Errorf("Run finished with error: %v", err)
|
||||||
}
|
}
|
||||||
@@ -94,16 +93,12 @@ func Run(configFile string, overrideX, overrideY int) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (o *Ordoor) Run() error {
|
func (o *Ordoor) Run() error {
|
||||||
// FIXME: these should be displayed *after*, not *before*, the copyright
|
// FIXME: we're missing a screen about SSI here
|
||||||
if o.config.Options.PlayMovies {
|
if o.config.Options.PlayMovies {
|
||||||
o.PlaySkippableVideo("LOGOS")
|
o.PlaySkippableVideo("LOGOS")
|
||||||
o.PlaySkippableVideo("movie1")
|
o.PlaySkippableVideo("movie1")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := o.DisplayImageFor(time.Second, "copyright"); err != nil {
|
|
||||||
log.Printf("Failed to display copyright image: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err := o.win.Run()
|
err := o.win.Run()
|
||||||
if err == flow.ErrExit {
|
if err == flow.ErrExit {
|
||||||
log.Printf("Exit requested")
|
log.Printf("Exit requested")
|
||||||
@@ -156,16 +151,6 @@ func (o *Ordoor) setupFlow() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (o *Ordoor) Update(screenX, screenY int) error {
|
func (o *Ordoor) Update(screenX, screenY int) error {
|
||||||
if pic := o.pic; pic != nil {
|
|
||||||
return nil // Ignore flow until we don't have a pic any more
|
|
||||||
}
|
|
||||||
|
|
||||||
if o.flow == nil {
|
|
||||||
if err := o.setupFlow(); err != nil {
|
|
||||||
return fmt.Errorf("failed to setup UI flow: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure music is doing the right thing
|
// Ensure music is doing the right thing
|
||||||
if o.music != nil && o.music.IsPlaying() != o.config.Options.PlayMusic {
|
if o.music != nil && o.music.IsPlaying() != o.config.Options.PlayMusic {
|
||||||
if o.config.Options.PlayMusic {
|
if o.config.Options.PlayMusic {
|
||||||
@@ -180,24 +165,9 @@ func (o *Ordoor) Update(screenX, screenY int) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (o *Ordoor) Draw(screen *ebiten.Image) error {
|
func (o *Ordoor) Draw(screen *ebiten.Image) error {
|
||||||
if pic := o.pic; pic != nil {
|
|
||||||
// Scale the picture to the screen and draw it
|
|
||||||
scaleX := float64(screen.Bounds().Dx()) / float64(pic.Bounds().Dx())
|
|
||||||
scaleY := float64(screen.Bounds().Dy()) / float64(pic.Bounds().Dy())
|
|
||||||
|
|
||||||
do := &ebiten.DrawImageOptions{}
|
|
||||||
do.GeoM.Scale(scaleX, scaleY)
|
|
||||||
|
|
||||||
return screen.DrawImage(pic, do)
|
|
||||||
}
|
|
||||||
|
|
||||||
return o.flow.Draw(screen)
|
return o.flow.Draw(screen)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Ordoor) Cursor() (*ebiten.Image, *ebiten.DrawImageOptions, error) {
|
func (o *Ordoor) Cursor() (*ebiten.Image, *ebiten.DrawImageOptions, error) {
|
||||||
if o.flow != nil {
|
return o.flow.Cursor()
|
||||||
return o.flow.Cursor()
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, nil, nil
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user