Compare commits
1 Commits
soldiers-a
...
2a39eee0a1
Author | SHA1 | Date | |
---|---|---|---|
2a39eee0a1 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -5,5 +5,4 @@
|
|||||||
/SL
|
/SL
|
||||||
/SaW
|
/SaW
|
||||||
/WoW
|
/WoW
|
||||||
/WoW-CD
|
|
||||||
/bin
|
/bin
|
||||||
|
94
README.md
94
README.md
@@ -32,8 +32,6 @@ result.
|
|||||||
|
|
||||||
## Current status
|
## Current status
|
||||||
|
|
||||||
### Chaos Gate
|
|
||||||
|
|
||||||
Some of the original file formats are either partially or fully decoded. Maps,
|
Some of the original file formats are either partially or fully decoded. Maps,
|
||||||
menus, and most visual data can be rendered pixel-perfect. Sound can be played
|
menus, and most visual data can be rendered pixel-perfect. Sound can be played
|
||||||
(with a preprocessing step). Some UI tookit work is done. No game mechanics are
|
(with a preprocessing step). Some UI tookit work is done. No game mechanics are
|
||||||
@@ -47,35 +45,6 @@ I've just been informed that another game from 1998, [Soldiers At War](https://e
|
|||||||
seems to use the same engine. Maybe at some point Ordoor will be able to play
|
seems to use the same engine. Maybe at some point Ordoor will be able to play
|
||||||
both. Will that need a rename? Hmm. Watch this space.
|
both. Will that need a rename? Hmm. Watch this space.
|
||||||
|
|
||||||
### Soldiers At War
|
|
||||||
|
|
||||||
(At least some) objects display. Map support is being worked on in the
|
|
||||||
`soldiers-at-war` branch, which can more-or-less display them, albeit with many
|
|
||||||
errors.
|
|
||||||
|
|
||||||
### Squad Leader
|
|
||||||
|
|
||||||
Squad Leader is the most recent of the games created with this engine. Nothing
|
|
||||||
has been done with it yet, but a preliminary look at the game data suggests many
|
|
||||||
changes are afoot. The object files are a different format, at the very least.
|
|
||||||
|
|
||||||
### Wages of War
|
|
||||||
|
|
||||||
This is the oldest of the four games. The object file format seems to be mostly
|
|
||||||
the same. the installer only copies some data to the game directory; we may want
|
|
||||||
to work directly from the CDROM instead, if we can.
|
|
||||||
|
|
||||||
Maps are uncompressed, around 243K, and no header is present. They look similar
|
|
||||||
in principle to the tile data of Soldiers At War or Chaos Gate maps, otherwise.
|
|
||||||
|
|
||||||
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
|
|
||||||
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 +69,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 +87,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 +118,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 +153,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.
|
||||||
|
|
||||||
|
@@ -58,7 +58,7 @@ func main() {
|
|||||||
|
|
||||||
assets, err := assetstore.New(cfg.DefaultEngine())
|
assets, err := assetstore.New(cfg.DefaultEngine())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to set up asset store: %v", err)
|
log.Fatal("Failed to set up asset store: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
state := state{
|
state := state{
|
||||||
|
@@ -57,7 +57,7 @@ func main() {
|
|||||||
|
|
||||||
assets, err := assetstore.New(cfg.DefaultEngine())
|
assets, err := assetstore.New(cfg.DefaultEngine())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to set up asset store: %v", err)
|
log.Fatal("Failed to set up asset store: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var obj *assetstore.Object
|
var obj *assetstore.Object
|
||||||
|
@@ -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
|
||||||
|
@@ -392,11 +392,8 @@ well-aligned amount.
|
|||||||
Investigation has so far suggested the following:
|
Investigation has so far suggested the following:
|
||||||
|
|
||||||
* `Cell[0]` seems related to doors and canisters. Observed:
|
* `Cell[0]` seems related to doors and canisters. Observed:
|
||||||
* Nothing special: 0x38
|
|
||||||
* ???: 0x39
|
|
||||||
* Imperial crate: 0x28
|
* Imperial crate: 0x28
|
||||||
* Door: 0xB8
|
* Door: 0xB8
|
||||||
|
|
||||||
* `Cell[1]` seems related to special placeables (but not triggers). Bitfield. Observed:
|
* `Cell[1]` seems related to special placeables (but not triggers). Bitfield. Observed:
|
||||||
* 0x01: Reactor
|
* 0x01: Reactor
|
||||||
* 0x20: Door or door lock?
|
* 0x20: Door or door lock?
|
||||||
@@ -411,12 +408,12 @@ Investigation has so far suggested the following:
|
|||||||
* `Cell[7]` Object 2 (Right) Area (Sets/*.set lookup)
|
* `Cell[7]` Object 2 (Right) Area (Sets/*.set lookup)
|
||||||
* `Cell[6]` Object 2 (Right) Sprite + active flag
|
* `Cell[6]` Object 2 (Right) Sprite + active flag
|
||||||
* `Cell[9]` Object 3 (Center) Area (Sets/*.set lookup)
|
* `Cell[9]` Object 3 (Center) Area (Sets/*.set lookup)
|
||||||
* `Cell[10]` Object 3 (Center) Sprite + active flag
|
* `Cell[10]` Object 3 (Right) Sprite + active flag
|
||||||
* `Cell[11]` all 255? Vehicle?
|
* `Cell[11]` all 255?
|
||||||
* `Cell[12]` all 0?
|
* `Cell[12]` all 0?
|
||||||
* `Cell[13]` all 0?
|
* `Cell[13]` all 0?
|
||||||
* `Cell[14]` all 0?
|
* `Cell[14]` all 0?
|
||||||
* `Cell[15]` shows squad positions, MP start positions, etc, as 0x04. Bitfield?
|
* `Cell[15]` shows squad positions, MP start positions, etc, as 0x04
|
||||||
|
|
||||||
Mapping the altar in Chapter01 to the map01 set suggests it's a palette entry
|
Mapping the altar in Chapter01 to the map01 set suggests it's a palette entry
|
||||||
lookup, 0-indexed. `U` debug in WH40K_TD.exe says the cell's `Object 3-Center`
|
lookup, 0-indexed. `U` debug in WH40K_TD.exe says the cell's `Object 3-Center`
|
||||||
@@ -518,120 +515,10 @@ Around 001841A0: mission objectives!
|
|||||||
00184240 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
00184240 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||||
```
|
```
|
||||||
|
|
||||||
Relative offsets from the start of the trailer, we have:
|
Since all the files are exactly the same length uncompressed, I'm going to
|
||||||
|
assume these are all a fixed number of fixed-size records when looking into it.
|
||||||
|
|
||||||
| Offset | Text |
|
|
||||||
| -------- | ---- |
|
|
||||||
| `0xEE` | Mania |
|
|
||||||
| `0x78A` | Dagon |
|
|
||||||
| `0xE26` | Nihasa |
|
|
||||||
| `0x14C2` | Samnu |
|
|
||||||
| `0x1b5e` | Bael |
|
|
||||||
| `0x2896` | Gigamen |
|
|
||||||
| `0x2f32` | Valefor |
|
|
||||||
| `0x35ce` | Baalberith |
|
|
||||||
| `0x3c6a` | Fenriz |
|
|
||||||
| `0x4306` | #Character |
|
|
||||||
| `0x49a2` | Apollyon |
|
|
||||||
|
|
||||||
So there are 1692 bytes between each name (the names probably don't come at the
|
|
||||||
start of each block, but it's still a useful stride). Presumably `#Character` is
|
|
||||||
a space for one of the player characters, while the others specify an NPC placed
|
|
||||||
on the map.
|
|
||||||
|
|
||||||
There's 56 of these records between the first and last name we see - `Ahpuch`.
|
|
||||||
|
|
||||||
Then there are a number of other strings that seem related to triggers / events,
|
|
||||||
including lots that say `NO FILE`. The first two are 96 bytes apart; from then
|
|
||||||
on they seem to be placed variably apart from each other; I've seen 96, 256, and
|
|
||||||
352 byte offsets.
|
|
||||||
|
|
||||||
At 0x20916 the mission objective is readable.
|
|
||||||
|
|
||||||
At 0x2092a the mission description is readable.
|
|
||||||
|
|
||||||
Generating another map with just 5 characters on it, things look different:
|
|
||||||
|
|
||||||
* Trailer size is 13543 bytes
|
|
||||||
* There are only 5 names
|
|
||||||
* There are none of the trigger/event strings
|
|
||||||
* Mission title is found at 0x2b93
|
|
||||||
* Mission briefing is found at 0x2c92
|
|
||||||
|
|
||||||
Since the trailer is a variable size, there must be a header that tells us how
|
|
||||||
many of each type of record to read. Peeking at the differences in `vbindiff`:
|
|
||||||
|
|
||||||
```
|
|
||||||
Chapter01.MAP.Trailer
|
|
||||||
0000 0000: 38 00 00 68 00 00 00 50 00 00 00 1A 00 00 00 14 8..h...P ........
|
|
||||||
0000 0010: 00 00 00 3A 00 00 00 00 38 25 00 04 00 00 00 00 ...:.... 8%......
|
|
||||||
0000 0020: 00 00 00 1A 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........
|
|
||||||
0000 0030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........
|
|
||||||
0000 0040: 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00 00 ........ ........
|
|
||||||
0000 0050: 00 00 00 32 00 00 00 00 00 00 00 00 00 00 00 00 ...2.... ........
|
|
||||||
|
|
||||||
TINYSQUAD.MAP.Trailer
|
|
||||||
0000 0000: 38 00 00 4B 00 00 00 3C 00 00 00 37 00 00 00 28 8..K...< ...7...(
|
|
||||||
0000 0010: 00 00 00 05 00 00 00 00 2B 3A 00 04 00 00 00 05 ........ +:......
|
|
||||||
0000 0020: 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........
|
|
||||||
0000 0030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........
|
|
||||||
0000 0040: 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00 00 ........ ........
|
|
||||||
0000 0050: 00 00 00 32 00 00 00 00 00 00 00 00 00 00 00 00 ...2.... ........
|
|
||||||
```
|
|
||||||
|
|
||||||
The size of the trailer for Chapter01 is 139,483 bytes, assuming it starts at
|
|
||||||
`0x163890`. However, things may be a lot more sensible if we drop 3 bytes off
|
|
||||||
the start of that to get the fields into little-endian alignment. Have I made a
|
|
||||||
maths error above somewhere? Is it some sort of alignment thing? Do those 3
|
|
||||||
bytes actually have meaning?
|
|
||||||
|
|
||||||
Ignoring them for now, here's a first guess at a header:
|
|
||||||
|
|
||||||
| Offset | Size | Meaning |
|
|
||||||
| ------ | ---- | ------- |
|
|
||||||
| 0 | 4 | Map maximum X + 1 |
|
|
||||||
| 4 | 4 | Map maximum Y + 1 |
|
|
||||||
| 8 | 4 | Map minimum X |
|
|
||||||
| 12 | 4 | Map minimum Y |
|
|
||||||
| 16 | 4 | Number of character records |
|
|
||||||
| 20 | 4 | Padding? - invariant `00 00 00 00` |
|
|
||||||
| 24 | 2 | ??? - varies. Seems related to character/squad position? |
|
|
||||||
| 26 | 2 | ??? - invariant `00 04` |
|
|
||||||
| 28 | 4 | ??? - varies (0 vs 5) |
|
|
||||||
| 32 | 4 | Number of thingies (26 vs 1) |
|
|
||||||
| 36 | 20 | Padding? |
|
|
||||||
|
|
||||||
56 bytes of data is interesting because the value of that first, ignored byte is
|
|
||||||
0x38 - perhaps it's a skip value + 2 bytes of padding? It's just weird. Keep
|
|
||||||
ignoring it for now.
|
|
||||||
|
|
||||||
0x4b contains the next non-null byte; is the gap between the the number of
|
|
||||||
thingies, and it, padding? Minus a bit? 0x50 is another non-null byte. Then
|
|
||||||
it's all zeroes until one byte before the first name at 0xee.
|
|
||||||
|
|
||||||
It's hard to say where the alignment should be at this point. We need to compare
|
|
||||||
more characters with each other. Other notes...
|
|
||||||
|
|
||||||
Characters are organised into Squads somehow.
|
|
||||||
|
|
||||||
Individual cells seem to have a flag to say "We have a character in us", but not
|
|
||||||
the number for the character themselves, so the coordinates must be in the
|
|
||||||
per-character records also. There are several candidates for this.
|
|
||||||
|
|
||||||
Placing a single character at (64,49) causes those bytes to show up at four
|
|
||||||
offsets - 0x18 (!), 0x1F4, 0x1F8, and 0x6C8.
|
|
||||||
|
|
||||||
Generating a map with no characters at all, the trailer is 2,447 bytes, and the
|
|
||||||
mission title starts at 0x3B (59). So we can say we have 20 bytes of padding as
|
|
||||||
a first approximation?
|
|
||||||
|
|
||||||
The "trailer trailer", for want of a better term, seems to be organised as:
|
|
||||||
|
|
||||||
| Offset | Size | Meaning |
|
|
||||||
| ----- | ---- | ------- |
|
|
||||||
| 0 | 255 | Title |
|
|
||||||
| 255 | 2048 | Briefing |
|
|
||||||
| 2304 | 85 | ??? - each byte is 1 or 0. Spaced so it may be partly uint32 |
|
|
||||||
|
|
||||||
|
|
||||||
## Soldiers At War
|
## Soldiers At War
|
||||||
@@ -713,15 +600,3 @@ xxd -s 0xc0 -c 13 -l 260 -g 13 BIGGESTMAP.MAP
|
|||||||
000000e7: 00 00 85 01 00 00 00 00 00 ff 00 00 1f .............
|
000000e7: 00 00 85 01 00 00 00 00 00 ff 00 00 1f .............
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
This can be interpreted more or less the same way as the Chaos Gate maps now,
|
|
||||||
and the `soldiers-at-war` branch contains a hacked-up implementation that kind
|
|
||||||
of works \o/.
|
|
||||||
|
|
||||||
Does the same trailer apply? Seemingly not. Looking at `PARIS.MAP`, there's no
|
|
||||||
similarity at first glance.
|
|
||||||
|
|
||||||
However, I did manage to track down 4 32-bit ints inside the trailer, starting
|
|
||||||
at `0x121ad1`, which specify dimensions of the map, at least. Perhaps the
|
|
||||||
position has moved, but some of the data is the same? It's 3320 bytes into the
|
|
||||||
trailer.
|
|
||||||
|
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
|
|
||||||
}
|
|
@@ -42,7 +42,7 @@ func (a *AssetStore) Object(name string) (*Object, error) {
|
|||||||
}
|
}
|
||||||
log.Printf("Loading object %v", name)
|
log.Printf("Loading object %v", name)
|
||||||
|
|
||||||
filename, err := a.lookup(name, "obj", "Obj", "spr")
|
filename, err := a.lookup(name, "obj", "Obj")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@@ -28,11 +28,7 @@ type SpriteHeader struct {
|
|||||||
|
|
||||||
func (s SpriteHeader) Check(expectedSize uint32) error {
|
func (s SpriteHeader) Check(expectedSize uint32) error {
|
||||||
if s.Padding1 != 0 || s.Padding2 != 0 {
|
if s.Padding1 != 0 || s.Padding2 != 0 {
|
||||||
if s.Padding1 == 271 && s.Padding2 == 0 {
|
return fmt.Errorf("Sprite header padding contains unknown values: %d %d", s.Padding1, s.Padding2)
|
||||||
log.Printf("Sprite header padding matches FIXME value")
|
|
||||||
} else {
|
|
||||||
return fmt.Errorf("Sprite header padding contains unknown values: %d %d", s.Padding1, s.Padding2)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: WarHammer.ani sets Unknown1 to this for all 188,286 sprites. I am
|
// TODO: WarHammer.ani sets Unknown1 to this for all 188,286 sprites. I am
|
||||||
|
@@ -38,40 +38,18 @@ type Header struct {
|
|||||||
IsCampaignMap byte
|
IsCampaignMap byte
|
||||||
}
|
}
|
||||||
|
|
||||||
type TrailerHeader struct {
|
func (h Header) Width() int {
|
||||||
Discard1 [3]byte // No idea what this lot is
|
return MaxWidth
|
||||||
MaxWidth uint32
|
|
||||||
MaxLength uint32
|
|
||||||
MinWidth uint32
|
|
||||||
MinLength uint32
|
|
||||||
|
|
||||||
NumCharacters uint32
|
|
||||||
|
|
||||||
Unknown1 uint32
|
|
||||||
Unknown2 uint16
|
|
||||||
Unknown3 uint16
|
|
||||||
Unknown4 uint32
|
|
||||||
NumThingies uint32
|
|
||||||
Padding1 [20]byte
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type TrailerTrailer struct {
|
func (h Header) Length() int {
|
||||||
Title [255]byte
|
return MaxLength
|
||||||
Briefing [2048]byte
|
|
||||||
Unknown1 [85]uint8 // Maybe? each contains either 0 or 1? Hard to say
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Character struct {
|
func (h Header) Height() int {
|
||||||
Unknown1 uint32
|
return MaxHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
type Characters []Character
|
|
||||||
|
|
||||||
// TODO. These are triggers/reactors/etc.
|
|
||||||
type Thingy struct {}
|
|
||||||
|
|
||||||
type Thingies []Thingy
|
|
||||||
|
|
||||||
func (h Header) MapSetName() string {
|
func (h Header) MapSetName() string {
|
||||||
idx := bytes.IndexByte(h.SetName[:], 0)
|
idx := bytes.IndexByte(h.SetName[:], 0)
|
||||||
if idx < 0 {
|
if idx < 0 {
|
||||||
@@ -208,23 +186,16 @@ func (h Header) Check() []error {
|
|||||||
type GameMap struct {
|
type GameMap struct {
|
||||||
Header
|
Header
|
||||||
Cells
|
Cells
|
||||||
TrailerHeader
|
// TODO: parse this into sections
|
||||||
Characters
|
|
||||||
Thingies
|
|
||||||
TrailerTrailer
|
|
||||||
|
|
||||||
// TODO: parse this into sections. This is the text that comes from the
|
|
||||||
// .TXT file. Maybe we don't need it at all since it should be duplicated in
|
|
||||||
// the trailer.
|
|
||||||
Text string
|
Text string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *GameMap) Rect() image.Rectangle {
|
func (m *GameMap) Rect() image.Rectangle {
|
||||||
return image.Rect(
|
return image.Rect(
|
||||||
int(m.TrailerHeader.MinWidth),
|
int(0),
|
||||||
int(m.TrailerHeader.MinLength),
|
int(0),
|
||||||
int(m.TrailerHeader.MaxWidth-1),
|
int(m.Width()-1),
|
||||||
int(m.TrailerHeader.MaxLength-1),
|
int(m.Length()-1),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -331,42 +302,5 @@ func loadMapFile(filename string) (*GameMap, error) {
|
|||||||
return nil, fmt.Errorf("Error parsing cells for %s: %v", filename, err)
|
return nil, fmt.Errorf("Error parsing cells for %s: %v", filename, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// no gzip.SeekReader, so discard unread trailer bytes for now
|
|
||||||
if _, err := io.CopyN(ioutil.Discard, zr, int64(3320-2)); err != nil { // observed
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := binary.Read(zr, binary.LittleEndian, &out.TrailerHeader); err != nil {
|
|
||||||
return nil, fmt.Errorf("Error parsing trailer header for %s: %v", filename, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("Trailer Header: %#+v", out.TrailerHeader)
|
|
||||||
/*
|
|
||||||
// TODO: until we know how large each character record should be, we can't read this lot
|
|
||||||
|
|
||||||
out.Characters = make(Characters, int(out.TrailerHeader.NumCharacters))
|
|
||||||
if err := binary.Read(zr, binary.LittleEndian, &out.Characters); err != nil {
|
|
||||||
return nil, fmt.Errorf("Error parsing characters for %s: %v", filename, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
out.Thingies = make(Thingies, int(out.TrailerHeader.NumThingies))
|
|
||||||
if err := binary.Read(zr, binary.LittleEndian, &out.Thingies); err != nil {
|
|
||||||
return nil, fmt.Errorf("Error parsing thingies for %s: %v", filename, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := binary.Read(zr, binary.LittleEndian, &out.TrailerTrailer); err != nil {
|
|
||||||
return nil, fmt.Errorf("Error parsing trailer trailer for %s: %v", filename, err)
|
|
||||||
}
|
|
||||||
log.Printf("Trailer Trailer: %s", out.TrailerTrailer.String())
|
|
||||||
*/
|
|
||||||
return &out, nil
|
return &out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TrailerTrailer) String() string {
|
|
||||||
return fmt.Sprintf(
|
|
||||||
"title=%q briefing=%q rest=%#+v",
|
|
||||||
strings.TrimRight(string(t.Title[:]), "\x00"),
|
|
||||||
strings.TrimRight(string(t.Briefing[:]), "\x00"),
|
|
||||||
t.Unknown1,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
@@ -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
|
|
||||||
}
|
}
|
||||||
|
@@ -1,269 +0,0 @@
|
|||||||
package palettes
|
|
||||||
|
|
||||||
import "image/color"
|
|
||||||
|
|
||||||
var (
|
|
||||||
WagesOfWarPalette = color.Palette{
|
|
||||||
Transparent,
|
|
||||||
|
|
||||||
color.RGBA{R: 1, G: 1, B: 1, A: 255},
|
|
||||||
color.RGBA{R: 254, G: 254, B: 254, A: 255},
|
|
||||||
color.RGBA{R: 187, G: 4, B: 4, A: 255},
|
|
||||||
color.RGBA{R: 181, G: 195, B: 2, A: 255},
|
|
||||||
color.RGBA{R: 94, G: 79, B: 148, A: 255},
|
|
||||||
color.RGBA{R: 233, G: 91, B: 4, A: 255},
|
|
||||||
color.RGBA{R: 60, G: 60, B: 60, A: 255},
|
|
||||||
color.RGBA{R: 102, G: 102, B: 102, A: 255},
|
|
||||||
color.RGBA{R: 170, G: 170, B: 170, A: 255},
|
|
||||||
color.RGBA{R: 197, G: 195, B: 193, A: 255},
|
|
||||||
color.RGBA{R: 234, G: 234, B: 235, A: 255},
|
|
||||||
color.RGBA{R: 218, G: 218, B: 240, A: 255},
|
|
||||||
color.RGBA{R: 202, G: 202, B: 245, A: 255},
|
|
||||||
color.RGBA{R: 186, G: 186, B: 250, A: 255},
|
|
||||||
color.RGBA{R: 170, G: 170, B: 255, A: 255},
|
|
||||||
color.RGBA{R: 238, G: 238, B: 238, A: 255},
|
|
||||||
color.RGBA{R: 222, G: 222, B: 222, A: 255},
|
|
||||||
color.RGBA{R: 206, G: 206, B: 206, A: 255},
|
|
||||||
color.RGBA{R: 190, G: 190, B: 190, A: 255},
|
|
||||||
color.RGBA{R: 174, G: 174, B: 174, A: 255},
|
|
||||||
color.RGBA{R: 157, G: 157, B: 157, A: 255},
|
|
||||||
color.RGBA{R: 141, G: 141, B: 141, A: 255},
|
|
||||||
color.RGBA{R: 129, G: 129, B: 129, A: 255},
|
|
||||||
color.RGBA{R: 113, G: 113, B: 113, A: 255},
|
|
||||||
color.RGBA{R: 97, G: 97, B: 97, A: 255},
|
|
||||||
color.RGBA{R: 80, G: 80, B: 80, A: 255},
|
|
||||||
color.RGBA{R: 64, G: 64, B: 64, A: 255},
|
|
||||||
color.RGBA{R: 48, G: 48, B: 48, A: 255},
|
|
||||||
color.RGBA{R: 32, G: 32, B: 32, A: 255},
|
|
||||||
color.RGBA{R: 16, G: 16, B: 16, A: 255},
|
|
||||||
color.RGBA{R: 4, G: 4, B: 4, A: 255},
|
|
||||||
color.RGBA{R: 255, G: 238, B: 238, A: 255},
|
|
||||||
color.RGBA{R: 246, G: 194, B: 194, A: 255},
|
|
||||||
color.RGBA{R: 238, G: 153, B: 153, A: 255},
|
|
||||||
color.RGBA{R: 230, G: 117, B: 117, A: 255},
|
|
||||||
color.RGBA{R: 222, G: 80, B: 80, A: 255},
|
|
||||||
color.RGBA{R: 214, G: 48, B: 48, A: 255},
|
|
||||||
color.RGBA{R: 210, G: 16, B: 16, A: 255},
|
|
||||||
color.RGBA{R: 190, G: 12, B: 12, A: 255},
|
|
||||||
color.RGBA{R: 170, G: 8, B: 8, A: 255},
|
|
||||||
color.RGBA{R: 153, G: 4, B: 4, A: 255},
|
|
||||||
color.RGBA{R: 133, G: 4, B: 4, A: 255},
|
|
||||||
color.RGBA{R: 113, G: 0, B: 0, A: 255},
|
|
||||||
color.RGBA{R: 97, G: 0, B: 0, A: 255},
|
|
||||||
color.RGBA{R: 76, G: 0, B: 0, A: 255},
|
|
||||||
color.RGBA{R: 56, G: 0, B: 0, A: 255},
|
|
||||||
color.RGBA{R: 40, G: 0, B: 0, A: 255},
|
|
||||||
color.RGBA{R: 255, G: 255, B: 250, A: 255},
|
|
||||||
color.RGBA{R: 255, G: 255, B: 165, A: 255},
|
|
||||||
color.RGBA{R: 255, G: 255, B: 80, A: 255},
|
|
||||||
color.RGBA{R: 255, G: 255, B: 0, A: 255},
|
|
||||||
color.RGBA{R: 234, G: 222, B: 12, A: 255},
|
|
||||||
color.RGBA{R: 214, G: 194, B: 28, A: 255},
|
|
||||||
color.RGBA{R: 194, G: 170, B: 40, A: 255},
|
|
||||||
color.RGBA{R: 174, G: 149, B: 52, A: 255},
|
|
||||||
color.RGBA{R: 255, G: 202, B: 153, A: 255},
|
|
||||||
color.RGBA{R: 255, G: 170, B: 101, A: 255},
|
|
||||||
color.RGBA{R: 255, G: 133, B: 48, A: 255},
|
|
||||||
color.RGBA{R: 255, G: 93, B: 0, A: 255},
|
|
||||||
color.RGBA{R: 222, G: 89, B: 12, A: 255},
|
|
||||||
color.RGBA{R: 190, G: 85, B: 28, A: 255},
|
|
||||||
color.RGBA{R: 157, G: 76, B: 36, A: 255},
|
|
||||||
color.RGBA{R: 129, G: 68, B: 40, A: 255},
|
|
||||||
color.RGBA{R: 198, G: 255, B: 206, A: 255},
|
|
||||||
color.RGBA{R: 170, G: 238, B: 182, A: 255},
|
|
||||||
color.RGBA{R: 149, G: 222, B: 157, A: 255},
|
|
||||||
color.RGBA{R: 125, G: 206, B: 137, A: 255},
|
|
||||||
color.RGBA{R: 105, G: 190, B: 113, A: 255},
|
|
||||||
color.RGBA{R: 89, G: 174, B: 97, A: 255},
|
|
||||||
color.RGBA{R: 72, G: 157, B: 76, A: 255},
|
|
||||||
color.RGBA{R: 56, G: 141, B: 64, A: 255},
|
|
||||||
color.RGBA{R: 40, G: 125, B: 48, A: 255},
|
|
||||||
color.RGBA{R: 32, G: 109, B: 36, A: 255},
|
|
||||||
color.RGBA{R: 20, G: 93, B: 24, A: 255},
|
|
||||||
color.RGBA{R: 12, G: 76, B: 16, A: 255},
|
|
||||||
color.RGBA{R: 4, G: 60, B: 8, A: 255},
|
|
||||||
color.RGBA{R: 0, G: 44, B: 4, A: 255},
|
|
||||||
color.RGBA{R: 0, G: 32, B: 0, A: 255},
|
|
||||||
color.RGBA{R: 0, G: 16, B: 0, A: 255},
|
|
||||||
color.RGBA{R: 210, G: 255, B: 255, A: 255},
|
|
||||||
color.RGBA{R: 182, G: 238, B: 234, A: 255},
|
|
||||||
color.RGBA{R: 157, G: 222, B: 218, A: 255},
|
|
||||||
color.RGBA{R: 137, G: 210, B: 202, A: 255},
|
|
||||||
color.RGBA{R: 113, G: 194, B: 186, A: 255},
|
|
||||||
color.RGBA{R: 97, G: 178, B: 165, A: 255},
|
|
||||||
color.RGBA{R: 76, G: 165, B: 149, A: 255},
|
|
||||||
color.RGBA{R: 64, G: 149, B: 129, A: 255},
|
|
||||||
color.RGBA{R: 48, G: 133, B: 113, A: 255},
|
|
||||||
color.RGBA{R: 36, G: 121, B: 97, A: 255},
|
|
||||||
color.RGBA{R: 24, G: 105, B: 80, A: 255},
|
|
||||||
color.RGBA{R: 16, G: 89, B: 64, A: 255},
|
|
||||||
color.RGBA{R: 8, G: 76, B: 52, A: 255},
|
|
||||||
color.RGBA{R: 4, G: 60, B: 36, A: 255},
|
|
||||||
color.RGBA{R: 0, G: 44, B: 24, A: 255},
|
|
||||||
color.RGBA{R: 0, G: 32, B: 16, A: 255},
|
|
||||||
color.RGBA{R: 255, G: 255, B: 170, A: 255},
|
|
||||||
color.RGBA{R: 238, G: 238, B: 149, A: 255},
|
|
||||||
color.RGBA{R: 222, G: 222, B: 129, A: 255},
|
|
||||||
color.RGBA{R: 206, G: 210, B: 109, A: 255},
|
|
||||||
color.RGBA{R: 190, G: 194, B: 93, A: 255},
|
|
||||||
color.RGBA{R: 174, G: 182, B: 76, A: 255},
|
|
||||||
color.RGBA{R: 157, G: 165, B: 64, A: 255},
|
|
||||||
color.RGBA{R: 141, G: 149, B: 52, A: 255},
|
|
||||||
color.RGBA{R: 125, G: 137, B: 40, A: 255},
|
|
||||||
color.RGBA{R: 109, G: 121, B: 28, A: 255},
|
|
||||||
color.RGBA{R: 97, G: 109, B: 20, A: 255},
|
|
||||||
color.RGBA{R: 80, G: 93, B: 12, A: 255},
|
|
||||||
color.RGBA{R: 64, G: 76, B: 4, A: 255},
|
|
||||||
color.RGBA{R: 52, G: 64, B: 4, A: 255},
|
|
||||||
color.RGBA{R: 40, G: 48, B: 0, A: 255},
|
|
||||||
color.RGBA{R: 28, G: 36, B: 0, A: 255},
|
|
||||||
color.RGBA{R: 214, G: 255, B: 214, A: 255},
|
|
||||||
color.RGBA{R: 157, G: 255, B: 157, A: 255},
|
|
||||||
color.RGBA{R: 105, G: 255, B: 105, A: 255},
|
|
||||||
color.RGBA{R: 48, G: 255, B: 48, A: 255},
|
|
||||||
color.RGBA{R: 0, G: 255, B: 0, A: 255},
|
|
||||||
color.RGBA{R: 24, G: 210, B: 28, A: 255},
|
|
||||||
color.RGBA{R: 44, G: 165, B: 48, A: 255},
|
|
||||||
color.RGBA{R: 52, G: 125, B: 56, A: 255},
|
|
||||||
color.RGBA{R: 226, G: 121, B: 0, A: 255},
|
|
||||||
color.RGBA{R: 198, G: 89, B: 0, A: 255},
|
|
||||||
color.RGBA{R: 174, G: 64, B: 0, A: 255},
|
|
||||||
color.RGBA{R: 145, G: 40, B: 0, A: 255},
|
|
||||||
color.RGBA{R: 121, G: 24, B: 0, A: 255},
|
|
||||||
color.RGBA{R: 93, G: 8, B: 0, A: 255},
|
|
||||||
color.RGBA{R: 64, G: 4, B: 0, A: 255},
|
|
||||||
color.RGBA{R: 40, G: 0, B: 0, A: 255},
|
|
||||||
color.RGBA{R: 194, G: 255, B: 255, A: 255},
|
|
||||||
color.RGBA{R: 165, G: 255, B: 255, A: 255},
|
|
||||||
color.RGBA{R: 109, G: 255, B: 255, A: 255},
|
|
||||||
color.RGBA{R: 0, G: 255, B: 255, A: 255},
|
|
||||||
color.RGBA{R: 4, G: 222, B: 222, A: 255},
|
|
||||||
color.RGBA{R: 16, G: 194, B: 194, A: 255},
|
|
||||||
color.RGBA{R: 20, G: 161, B: 161, A: 255},
|
|
||||||
color.RGBA{R: 24, G: 133, B: 133, A: 255},
|
|
||||||
color.RGBA{R: 137, G: 157, B: 255, A: 255},
|
|
||||||
color.RGBA{R: 101, G: 121, B: 255, A: 255},
|
|
||||||
color.RGBA{R: 64, G: 80, B: 255, A: 255},
|
|
||||||
color.RGBA{R: 28, G: 40, B: 255, A: 255},
|
|
||||||
color.RGBA{R: 0, G: 0, B: 255, A: 255},
|
|
||||||
color.RGBA{R: 12, G: 12, B: 206, A: 255},
|
|
||||||
color.RGBA{R: 20, G: 20, B: 157, A: 255},
|
|
||||||
color.RGBA{R: 24, G: 24, B: 109, A: 255},
|
|
||||||
color.RGBA{R: 234, G: 234, B: 255, A: 255},
|
|
||||||
color.RGBA{R: 202, G: 202, B: 238, A: 255},
|
|
||||||
color.RGBA{R: 178, G: 178, B: 222, A: 255},
|
|
||||||
color.RGBA{R: 153, G: 153, B: 206, A: 255},
|
|
||||||
color.RGBA{R: 129, G: 129, B: 194, A: 255},
|
|
||||||
color.RGBA{R: 105, G: 105, B: 178, A: 255},
|
|
||||||
color.RGBA{R: 89, G: 89, B: 161, A: 255},
|
|
||||||
color.RGBA{R: 68, G: 68, B: 145, A: 255},
|
|
||||||
color.RGBA{R: 52, G: 52, B: 133, A: 255},
|
|
||||||
color.RGBA{R: 40, G: 40, B: 117, A: 255},
|
|
||||||
color.RGBA{R: 28, G: 28, B: 101, A: 255},
|
|
||||||
color.RGBA{R: 16, G: 16, B: 89, A: 255},
|
|
||||||
color.RGBA{R: 8, G: 8, B: 72, A: 255},
|
|
||||||
color.RGBA{R: 4, G: 4, B: 56, A: 255},
|
|
||||||
color.RGBA{R: 0, G: 0, B: 40, A: 255},
|
|
||||||
color.RGBA{R: 0, G: 0, B: 28, A: 255},
|
|
||||||
color.RGBA{R: 250, G: 218, B: 170, A: 255},
|
|
||||||
color.RGBA{R: 234, G: 198, B: 145, A: 255},
|
|
||||||
color.RGBA{R: 218, G: 182, B: 129, A: 255},
|
|
||||||
color.RGBA{R: 202, G: 165, B: 109, A: 255},
|
|
||||||
color.RGBA{R: 190, G: 149, B: 93, A: 255},
|
|
||||||
color.RGBA{R: 174, G: 133, B: 76, A: 255},
|
|
||||||
color.RGBA{R: 157, G: 117, B: 60, A: 255},
|
|
||||||
color.RGBA{R: 141, G: 105, B: 48, A: 255},
|
|
||||||
color.RGBA{R: 129, G: 93, B: 36, A: 255},
|
|
||||||
color.RGBA{R: 113, G: 76, B: 28, A: 255},
|
|
||||||
color.RGBA{R: 97, G: 64, B: 16, A: 255},
|
|
||||||
color.RGBA{R: 85, G: 52, B: 12, A: 255},
|
|
||||||
color.RGBA{R: 68, G: 40, B: 4, A: 255},
|
|
||||||
color.RGBA{R: 52, G: 32, B: 0, A: 255},
|
|
||||||
color.RGBA{R: 36, G: 20, B: 0, A: 255},
|
|
||||||
color.RGBA{R: 24, G: 12, B: 0, A: 255},
|
|
||||||
color.RGBA{R: 255, G: 230, B: 186, A: 255},
|
|
||||||
color.RGBA{R: 238, G: 210, B: 161, A: 255},
|
|
||||||
color.RGBA{R: 226, G: 190, B: 141, A: 255},
|
|
||||||
color.RGBA{R: 214, G: 170, B: 121, A: 255},
|
|
||||||
color.RGBA{R: 202, G: 149, B: 105, A: 255},
|
|
||||||
color.RGBA{R: 186, G: 133, B: 89, A: 255},
|
|
||||||
color.RGBA{R: 174, G: 113, B: 72, A: 255},
|
|
||||||
color.RGBA{R: 161, G: 93, B: 60, A: 255},
|
|
||||||
color.RGBA{R: 145, G: 76, B: 48, A: 255},
|
|
||||||
color.RGBA{R: 133, G: 60, B: 36, A: 255},
|
|
||||||
color.RGBA{R: 121, G: 44, B: 24, A: 255},
|
|
||||||
color.RGBA{R: 109, G: 32, B: 16, A: 255},
|
|
||||||
color.RGBA{R: 93, G: 20, B: 8, A: 255},
|
|
||||||
color.RGBA{R: 80, G: 8, B: 4, A: 255},
|
|
||||||
color.RGBA{R: 68, G: 4, B: 0, A: 255},
|
|
||||||
color.RGBA{R: 56, G: 0, B: 0, A: 255},
|
|
||||||
color.RGBA{R: 218, G: 218, B: 198, A: 255},
|
|
||||||
color.RGBA{R: 194, G: 194, B: 170, A: 255},
|
|
||||||
color.RGBA{R: 170, G: 170, B: 145, A: 255},
|
|
||||||
color.RGBA{R: 145, G: 145, B: 121, A: 255},
|
|
||||||
color.RGBA{R: 125, G: 125, B: 101, A: 255},
|
|
||||||
color.RGBA{R: 101, G: 101, B: 76, A: 255},
|
|
||||||
color.RGBA{R: 76, G: 76, B: 56, A: 255},
|
|
||||||
color.RGBA{R: 56, G: 56, B: 40, A: 255},
|
|
||||||
color.RGBA{R: 246, G: 222, B: 206, A: 255},
|
|
||||||
color.RGBA{R: 234, G: 206, B: 190, A: 255},
|
|
||||||
color.RGBA{R: 226, G: 194, B: 170, A: 255},
|
|
||||||
color.RGBA{R: 214, G: 178, B: 153, A: 255},
|
|
||||||
color.RGBA{R: 206, G: 165, B: 141, A: 255},
|
|
||||||
color.RGBA{R: 194, G: 153, B: 125, A: 255},
|
|
||||||
color.RGBA{R: 186, G: 141, B: 113, A: 255},
|
|
||||||
color.RGBA{R: 174, G: 129, B: 101, A: 255},
|
|
||||||
color.RGBA{R: 165, G: 117, B: 89, A: 255},
|
|
||||||
color.RGBA{R: 153, G: 109, B: 76, A: 255},
|
|
||||||
color.RGBA{R: 145, G: 97, B: 64, A: 255},
|
|
||||||
color.RGBA{R: 133, G: 89, B: 56, A: 255},
|
|
||||||
color.RGBA{R: 125, G: 76, B: 48, A: 255},
|
|
||||||
color.RGBA{R: 113, G: 68, B: 36, A: 255},
|
|
||||||
color.RGBA{R: 105, G: 60, B: 32, A: 255},
|
|
||||||
color.RGBA{R: 93, G: 52, B: 24, A: 255},
|
|
||||||
color.RGBA{R: 85, G: 44, B: 16, A: 255},
|
|
||||||
color.RGBA{R: 72, G: 36, B: 12, A: 255},
|
|
||||||
color.RGBA{R: 64, G: 32, B: 8, A: 255},
|
|
||||||
color.RGBA{R: 56, G: 24, B: 4, A: 255},
|
|
||||||
color.RGBA{R: 44, G: 16, B: 0, A: 255},
|
|
||||||
color.RGBA{R: 36, G: 12, B: 0, A: 255},
|
|
||||||
color.RGBA{R: 24, G: 8, B: 0, A: 255},
|
|
||||||
color.RGBA{R: 16, G: 4, B: 0, A: 255},
|
|
||||||
color.RGBA{R: 255, G: 255, B: 234, A: 255},
|
|
||||||
color.RGBA{R: 255, G: 250, B: 198, A: 255},
|
|
||||||
color.RGBA{R: 255, G: 242, B: 165, A: 255},
|
|
||||||
color.RGBA{R: 255, G: 234, B: 129, A: 255},
|
|
||||||
color.RGBA{R: 255, G: 218, B: 97, A: 255},
|
|
||||||
color.RGBA{R: 255, G: 202, B: 60, A: 255},
|
|
||||||
color.RGBA{R: 255, G: 182, B: 28, A: 255},
|
|
||||||
color.RGBA{R: 255, G: 157, B: 0, A: 255},
|
|
||||||
color.RGBA{R: 222, G: 222, B: 234, A: 255},
|
|
||||||
color.RGBA{R: 202, G: 202, B: 218, A: 255},
|
|
||||||
color.RGBA{R: 182, G: 182, B: 202, A: 255},
|
|
||||||
color.RGBA{R: 165, G: 165, B: 186, A: 255},
|
|
||||||
color.RGBA{R: 149, G: 149, B: 170, A: 255},
|
|
||||||
color.RGBA{R: 133, G: 133, B: 157, A: 255},
|
|
||||||
color.RGBA{R: 117, G: 117, B: 141, A: 255},
|
|
||||||
color.RGBA{R: 101, G: 101, B: 125, A: 255},
|
|
||||||
color.RGBA{R: 85, G: 85, B: 109, A: 255},
|
|
||||||
color.RGBA{R: 72, G: 72, B: 97, A: 255},
|
|
||||||
color.RGBA{R: 60, G: 60, B: 80, A: 255},
|
|
||||||
color.RGBA{R: 44, G: 44, B: 64, A: 255},
|
|
||||||
color.RGBA{R: 32, G: 32, B: 48, A: 255},
|
|
||||||
color.RGBA{R: 20, G: 20, B: 32, A: 255},
|
|
||||||
color.RGBA{R: 12, G: 12, B: 24, A: 255},
|
|
||||||
color.RGBA{R: 0, G: 0, B: 0, A: 255},
|
|
||||||
color.RGBA{R: 0, G: 0, B: 0, A: 255},
|
|
||||||
color.RGBA{R: 0, G: 0, B: 0, A: 255},
|
|
||||||
color.RGBA{R: 0, G: 0, B: 0, A: 255},
|
|
||||||
color.RGBA{R: 0, G: 0, B: 0, A: 255},
|
|
||||||
color.RGBA{R: 0, G: 0, B: 0, A: 255},
|
|
||||||
color.RGBA{R: 0, G: 0, B: 0, A: 255},
|
|
||||||
color.RGBA{R: 0, G: 0, B: 0, A: 255},
|
|
||||||
color.RGBA{R: 0, G: 0, B: 0, A: 255},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
Palettes["WagesOfWar"] = WagesOfWarPalette
|
|
||||||
}
|
|
@@ -27,7 +27,7 @@ var (
|
|||||||
|
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
f.read(3) # Ignore idx 0 so we can make it transparent
|
bytes.read(3) # Ignore idx 0 so we can make it transparent
|
||||||
|
|
||||||
255.times do
|
255.times do
|
||||||
r, g, b = f.read(3).bytes
|
r, g, b = f.read(3).bytes
|
||||||
|
Reference in New Issue
Block a user