Compare commits
1 Commits
2a39eee0a1
...
132670507c
Author | SHA1 | Date | |
---|---|---|---|
132670507c |
18
.gitignore
vendored
18
.gitignore
vendored
@@ -1,8 +1,14 @@
|
|||||||
/config.toml
|
/config.toml
|
||||||
/loader
|
/loader
|
||||||
/isos
|
/orig
|
||||||
/CG
|
/palette-idx
|
||||||
/SL
|
/view-ani
|
||||||
/SaW
|
/view-font
|
||||||
/WoW
|
/view-obj
|
||||||
/bin
|
/view-map
|
||||||
|
/view-minimap
|
||||||
|
/view-menu
|
||||||
|
/view-set
|
||||||
|
/ordoor
|
||||||
|
/investigation/Maps
|
||||||
|
/investigation/Obj
|
||||||
|
45
Makefile
45
Makefile
@@ -4,40 +4,37 @@ GOBUILD ?= go build -tags ebitengl
|
|||||||
|
|
||||||
all: loader ordoor palette-idx view-ani view-font view-obj view-map view-menu view-minimap view-set
|
all: loader ordoor palette-idx view-ani view-font view-obj view-map view-menu view-minimap view-set
|
||||||
|
|
||||||
bin:
|
loader: $(srcfiles)
|
||||||
mkdir bin
|
$(GOBUILD) -o loader ./cmd/loader
|
||||||
|
|
||||||
loader: bin $(srcfiles)
|
palette-idx: $(srcfiles)
|
||||||
$(GOBUILD) -o bin/loader ./cmd/loader
|
$(GOBUILD) -o palette-idx ./cmd/palette-idx
|
||||||
|
|
||||||
palette-idx: bin $(srcfiles)
|
view-ani: $(srcfiles)
|
||||||
$(GOBUILD) -o bin/palette-idx ./cmd/palette-idx
|
$(GOBUILD) -o view-ani ./cmd/view-ani
|
||||||
|
|
||||||
view-ani: bin $(srcfiles)
|
view-font: $(srcfiles)
|
||||||
$(GOBUILD) -o bin/view-ani ./cmd/view-ani
|
$(GOBUILD) -o view-font ./cmd/view-font
|
||||||
|
|
||||||
view-font: bin $(srcfiles)
|
view-obj: $(srcfiles)
|
||||||
$(GOBUILD) -o bin/view-font ./cmd/view-font
|
$(GOBUILD) -o view-obj ./cmd/view-obj
|
||||||
|
|
||||||
view-obj: bin $(srcfiles)
|
view-map: $(srcfiles)
|
||||||
$(GOBUILD) -o bin/view-obj ./cmd/view-obj
|
$(GOBUILD) -o view-map ./cmd/view-map
|
||||||
|
|
||||||
view-map: bin $(srcfiles)
|
view-menu: $(srcfiles)
|
||||||
$(GOBUILD) -o bin/view-map ./cmd/view-map
|
$(GOBUILD) -o view-menu ./cmd/view-menu
|
||||||
|
|
||||||
view-menu: bin $(srcfiles)
|
view-minimap: $(srcfiles)
|
||||||
$(GOBUILD) -o bin/view-menu ./cmd/view-menu
|
$(GOBUILD) -o view-minimap ./cmd/view-minimap
|
||||||
|
|
||||||
view-minimap: bin $(srcfiles)
|
view-set: $(srcfiles)
|
||||||
$(GOBUILD) -o bin/view-minimap ./cmd/view-minimap
|
$(GOBUILD) -o view-set ./cmd/view-set
|
||||||
|
|
||||||
view-set: bin $(srcfiles)
|
ordoor: $(srcfiles)
|
||||||
$(GOBUILD) -o bin/view-set ./cmd/view-set
|
$(GOBUILD) -o ordoor ./cmd/ordoor
|
||||||
|
|
||||||
ordoor: bin $(srcfiles)
|
|
||||||
$(GOBUILD) -o bin/ordoor ./cmd/ordoor
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf bin
|
rm -f loader ordoor view-ani view-obj view-map view-minimap view-set palette-idx view-font
|
||||||
|
|
||||||
.PHONY: all clean
|
.PHONY: all clean
|
||||||
|
82
README.md
82
README.md
@@ -1,34 +1,20 @@
|
|||||||
# Ordoor
|
# Ordoor
|
||||||
|
|
||||||
Ordoor is an **unofficial** [game engine recreation](https://en.wikipedia.org/wiki/Game_engine_recreation)
|
Ordoor is an **unofficial** [game engine recreation](https://en.wikipedia.org/wiki/Game_engine_recreation)
|
||||||
of the Random Games, Inc., [Strategy Engine](https://www.mobygames.com/game-group/game-engine-random-games-1996-2000-strategy-engine),
|
of the classic game from 1998, [Warhammer 40,000: Chaos Gate](https://en.wikipedia.org/wiki/Warhammer_40,000:_Chaos_Gate)
|
||||||
which was in use from 1996 - 2000.
|
|
||||||
|
|
||||||
Four games are known to have been published for this engine:
|
**You must have a copy of the original game data to use this project**. GOG is
|
||||||
|
the current publisher of this game; [you can purchase it here](https://www.gog.com/game/warhammer_40000_chaos_gate).
|
||||||
|
|
||||||
* [Wages of War: The Business of Battle](https://en.wikipedia.org/wiki/Wages_of_War) (1996)
|
"Warhammer 40,000" is a trademark of Games Workshop, and the game data used by
|
||||||
* [Soldiers At War](https://en.wikipedia.org/wiki/Soldiers_at_War) (1998)
|
Ordoor contains Games Workshop intellectual property. I am confident that this
|
||||||
* [Warhammer 40,000: Chaos Gate](https://en.wikipedia.org/wiki/Warhammer_40,000:_Chaos_Gate) (1998) [GOG](https://www.gog.com/game/warhammer_40000_chaos_gate)
|
project uses all those things in accordance with the
|
||||||
* [Avalon Hill's Squad Leader](https://en.wikipedia.org/wiki/Avalon_Hill%27s_Squad_Leader) (2000)
|
[Intellectual Property Policy](https://www.games-workshop.com/en-GB/Intellectual-Property-Policy)
|
||||||
|
and the license granted when purchasing a copy of the game in question. Do let
|
||||||
The aim of Ordoor is to be a complete reimplementation that allows all four
|
me know if you see or suspect any violation, and I'll address it immediately.
|
||||||
of these games to be played on modern hardware. It should also permit new games
|
|
||||||
of the same style to be built.
|
|
||||||
|
|
||||||
For each of the games above, **You must have a copy of the original game data to play**.
|
|
||||||
Links are provided above if we're aware of an active publisher; otherwise, check
|
|
||||||
your back catalogue, or perhaps a local charity shop.
|
|
||||||
|
|
||||||
Trademarks and intellectual property are the property of their respective
|
|
||||||
owners, and the games mentioned above (including the game data) are protected by
|
|
||||||
copyright. As a mere game engine recreation, we're confident that this project
|
|
||||||
operates legally, and that its goal is a noble one. Do get in touch if you
|
|
||||||
believe otherwise!
|
|
||||||
|
|
||||||
Ordoor is a portmanteau of Order Door, which is, of course, the opposite of a
|
Ordoor is a portmanteau of Order Door, which is, of course, the opposite of a
|
||||||
Chaos Gate. The project began with a Chaos Gate recreation, then more games were
|
Chaos Gate.
|
||||||
discovered, so scope expanded. A rename and/or rewrite may be on the cards as a
|
|
||||||
result.
|
|
||||||
|
|
||||||
## Current status
|
## Current status
|
||||||
|
|
||||||
@@ -45,27 +31,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.
|
||||||
|
|
||||||
## Long-term goals
|
|
||||||
|
|
||||||
Once full playthrough of the official single-player campaign for all four games
|
|
||||||
has been achieved, thoughts turn to other things we could do. Here are some
|
|
||||||
ideas, mostly at random.
|
|
||||||
|
|
||||||
Multi-player support.
|
|
||||||
|
|
||||||
Graphics enhancements - 3D models instead of sprites, high-resolution tile sets,
|
|
||||||
32-bit colour, etc. Hopefully we'd be able to drop these in one at a time.
|
|
||||||
|
|
||||||
Vastly improved AI.
|
|
||||||
|
|
||||||
Mash-ups? How do mercenaries fare against cultists fare against Nazis? Only one
|
|
||||||
way to find out!
|
|
||||||
|
|
||||||
New campaigns with existing assets. Tell new stories, or elaborate on / modify
|
|
||||||
existing ones.
|
|
||||||
|
|
||||||
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
|
||||||
@@ -154,3 +119,30 @@ $ ./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.
|
||||||
|
|
||||||
|
## Miscellany
|
||||||
|
|
||||||
|
"Mission Setup" includes information about available squad types
|
||||||
|
|
||||||
|
From EquipDef.cpp Dumo: CEquipment we learn the following object types:
|
||||||
|
|
||||||
|
0. DELETED
|
||||||
|
1. WEAPON
|
||||||
|
2. GRENADE
|
||||||
|
3. MEDIPACK
|
||||||
|
4. SCANNER
|
||||||
|
5. GENESEED
|
||||||
|
6. CLIP
|
||||||
|
7. DOOR KEY
|
||||||
|
8. DOOR KEY
|
||||||
|
9. DOOR KEY
|
||||||
|
10. DOOR KEY
|
||||||
|
|
||||||
|
And we learn they can be "on"....
|
||||||
|
|
||||||
|
0. CHARACTER
|
||||||
|
1. VEHICLE
|
||||||
|
2. CANISTER
|
||||||
|
|
||||||
|
I'm starting to see some parallels with [this](https://github.com/shlainn/game-file-formats/wiki/)
|
||||||
|
in the data formats, and the timeline (1997) seems about right. Worth keeping an
|
||||||
|
eye on!
|
||||||
|
@@ -3,58 +3,42 @@ package main
|
|||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"image/color"
|
|
||||||
"log"
|
"log"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"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/fonts"
|
"code.ur.gs/lupine/ordoor/internal/fonts"
|
||||||
"code.ur.gs/lupine/ordoor/internal/idx"
|
"code.ur.gs/lupine/ordoor/internal/idx"
|
||||||
"code.ur.gs/lupine/ordoor/internal/maps"
|
"code.ur.gs/lupine/ordoor/internal/maps"
|
||||||
"code.ur.gs/lupine/ordoor/internal/menus"
|
"code.ur.gs/lupine/ordoor/internal/menus"
|
||||||
"code.ur.gs/lupine/ordoor/internal/palettes"
|
|
||||||
"code.ur.gs/lupine/ordoor/internal/sets"
|
"code.ur.gs/lupine/ordoor/internal/sets"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
configFile = flag.String("config", "config.toml", "Config file")
|
gamePath = flag.String("game-path", "./orig", "Path to a WH40K: Chaos Gate installation")
|
||||||
engine = flag.String("engine", "", "Override engine to use")
|
skipObj = flag.Bool("skip-obj", true, "Skip loading .obj files")
|
||||||
|
|
||||||
skipObj = flag.Bool("skip-obj", true, "Skip loading .obj files")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// FIXME: all these paths are hardcoded with Chaos Gate in mind
|
|
||||||
func main() {
|
func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
cfg, err := config.Load(*configFile, *engine)
|
loadData()
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to load config: %v", err)
|
|
||||||
}
|
|
||||||
engine := cfg.DefaultEngine()
|
|
||||||
gamePath := engine.DataDir
|
|
||||||
palette, ok := palettes.Get(engine.Palette)
|
|
||||||
if !ok {
|
|
||||||
log.Fatalf("Unknown palette name: %v", engine.Palette)
|
|
||||||
}
|
|
||||||
|
|
||||||
loadData(filepath.Join(gamePath, "Data"))
|
|
||||||
|
|
||||||
if !*skipObj {
|
if !*skipObj {
|
||||||
loadObj(filepath.Join(gamePath, "Obj"))
|
loadObj()
|
||||||
}
|
}
|
||||||
|
|
||||||
loadMapsFrom(filepath.Join(gamePath, "Maps"))
|
loadMapsFrom("Maps")
|
||||||
loadMapsFrom(filepath.Join(gamePath, "MultiMaps"))
|
loadMapsFrom("MultiMaps")
|
||||||
loadSets(filepath.Join(gamePath, "Sets"))
|
loadSets()
|
||||||
loadMenus(filepath.Join(gamePath, "Menu"), palette)
|
loadMenus()
|
||||||
loadFonts(filepath.Join(gamePath, "Fonts"))
|
loadFonts()
|
||||||
loadIdx(filepath.Join(gamePath, "Idx", "WarHammer.idx"))
|
loadIdx()
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadData(dataPath string) {
|
func loadData() {
|
||||||
|
dataPath := filepath.Join(*gamePath, "Data")
|
||||||
accountingPath := filepath.Join(dataPath, "Accounting.dat")
|
accountingPath := filepath.Join(dataPath, "Accounting.dat")
|
||||||
aniObDefPath := filepath.Join(dataPath, "AniObDef.dat")
|
aniObDefPath := filepath.Join(dataPath, "AniObDef.dat")
|
||||||
genericDataPath := filepath.Join(dataPath, "GenericData.dat")
|
genericDataPath := filepath.Join(dataPath, "GenericData.dat")
|
||||||
@@ -100,7 +84,9 @@ func loadData(dataPath string) {
|
|||||||
ha.Print()
|
ha.Print()
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadObj(objDataPath string) {
|
func loadObj() {
|
||||||
|
objDataPath := filepath.Join(*gamePath, "Obj")
|
||||||
|
|
||||||
// TODO: Obj/cpiece.rec isn't loaded by this. Do we need it? How do we know?
|
// TODO: Obj/cpiece.rec isn't loaded by this. Do we need it? How do we know?
|
||||||
log.Printf("Loading %s...", objDataPath)
|
log.Printf("Loading %s...", objDataPath)
|
||||||
objects, err := data.LoadObjects(objDataPath)
|
objects, err := data.LoadObjects(objDataPath)
|
||||||
@@ -130,7 +116,8 @@ func loadObj(objDataPath string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadMapsFrom(mapsPath string) {
|
func loadMapsFrom(part string) {
|
||||||
|
mapsPath := filepath.Join(*gamePath, part)
|
||||||
log.Printf("Loading maps from %s", mapsPath)
|
log.Printf("Loading maps from %s", mapsPath)
|
||||||
|
|
||||||
gameMaps, err := maps.LoadGameMaps(mapsPath)
|
gameMaps, err := maps.LoadGameMaps(mapsPath)
|
||||||
@@ -153,7 +140,8 @@ func loadMapsFrom(mapsPath string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadSets(setsPath string) {
|
func loadSets() {
|
||||||
|
setsPath := filepath.Join(*gamePath, "Sets")
|
||||||
log.Printf("Loading sets from %s", setsPath)
|
log.Printf("Loading sets from %s", setsPath)
|
||||||
|
|
||||||
mapSets, err := sets.LoadSets(setsPath)
|
mapSets, err := sets.LoadSets(setsPath)
|
||||||
@@ -169,10 +157,10 @@ func loadSets(setsPath string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadMenus(menusPath string, palette color.Palette) {
|
func loadMenus() {
|
||||||
log.Printf("Loading menus from %s", menusPath)
|
menusPath := filepath.Join(*gamePath, "Menu")
|
||||||
|
|
||||||
menus, err := menus.LoadMenus(menusPath, palette)
|
menus, err := menus.LoadMenus(menusPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to parse %s/*.mnu as menus: %v", menusPath, err)
|
log.Fatalf("Failed to parse %s/*.mnu as menus: %v", menusPath, err)
|
||||||
}
|
}
|
||||||
@@ -199,8 +187,8 @@ func displayRecord(record *menus.Record, depth int) {
|
|||||||
fmt.Printf("%s* %s\n", strings.Repeat(" ", depth), content)
|
fmt.Printf("%s* %s\n", strings.Repeat(" ", depth), content)
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadFonts(fontsPath string) {
|
func loadFonts() {
|
||||||
log.Printf("Loading fonts from %s", fontsPath)
|
fontsPath := filepath.Join(*gamePath, "Fonts")
|
||||||
|
|
||||||
fonts, err := fonts.LoadFonts(fontsPath)
|
fonts, err := fonts.LoadFonts(fontsPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -212,9 +200,8 @@ func loadFonts(fontsPath string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadIdx(idxPath string) {
|
func loadIdx() {
|
||||||
log.Printf("Loading idx from %s", idxPath)
|
idxPath := filepath.Join(*gamePath, "Idx", "WarHammer.idx")
|
||||||
|
|
||||||
idx, err := idx.Load(idxPath)
|
idx, err := idx.Load(idxPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to parse %s as idx: %v", idxPath, err)
|
log.Fatalf("Failed to parse %s as idx: %v", idxPath, err)
|
||||||
|
@@ -11,14 +11,11 @@ import (
|
|||||||
"golang.org/x/image/colornames"
|
"golang.org/x/image/colornames"
|
||||||
|
|
||||||
"code.ur.gs/lupine/ordoor/internal/assetstore"
|
"code.ur.gs/lupine/ordoor/internal/assetstore"
|
||||||
"code.ur.gs/lupine/ordoor/internal/config"
|
|
||||||
"code.ur.gs/lupine/ordoor/internal/ui"
|
"code.ur.gs/lupine/ordoor/internal/ui"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
configFile = flag.String("config", "config.toml", "Config file")
|
gamePath = flag.String("game-path", "./orig", "Path to a WH40K: Chaos Gate installation")
|
||||||
engine = flag.String("engine", "", "Override engine to use")
|
|
||||||
|
|
||||||
groupIdx = flag.Int("group", 1, "Group index to start at")
|
groupIdx = flag.Int("group", 1, "Group index to start at")
|
||||||
recIdx = flag.Int("record", 0, "Record index to start at")
|
recIdx = flag.Int("record", 0, "Record index to start at")
|
||||||
|
|
||||||
@@ -46,17 +43,12 @@ type state struct {
|
|||||||
func main() {
|
func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
if *configFile == "" {
|
if *gamePath == "" {
|
||||||
flag.Usage()
|
flag.Usage()
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg, err := config.Load(*configFile, *engine)
|
assets, err := assetstore.New(*gamePath)
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to load config: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
assets, err := assetstore.New(cfg.DefaultEngine())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Failed to set up asset store: %v", err)
|
log.Fatal("Failed to set up asset store: %v", err)
|
||||||
}
|
}
|
||||||
|
@@ -10,14 +10,11 @@ import (
|
|||||||
"github.com/hajimehoshi/ebiten"
|
"github.com/hajimehoshi/ebiten"
|
||||||
|
|
||||||
"code.ur.gs/lupine/ordoor/internal/assetstore"
|
"code.ur.gs/lupine/ordoor/internal/assetstore"
|
||||||
"code.ur.gs/lupine/ordoor/internal/config"
|
|
||||||
"code.ur.gs/lupine/ordoor/internal/ui"
|
"code.ur.gs/lupine/ordoor/internal/ui"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
configFile = flag.String("config", "config.toml", "Config file")
|
gamePath = flag.String("game-path", "./orig", "Path to a WH40K: Chaos Gate installation")
|
||||||
engine = flag.String("engine", "", "Override engine to use")
|
|
||||||
|
|
||||||
fontName = flag.String("font", "", "Name of a font, e.g., basfont12")
|
fontName = flag.String("font", "", "Name of a font, e.g., basfont12")
|
||||||
txt = flag.String("text", "Test string", "Text to render")
|
txt = flag.String("text", "Test string", "Text to render")
|
||||||
|
|
||||||
@@ -40,17 +37,12 @@ type state struct {
|
|||||||
func main() {
|
func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
if *configFile == "" || *fontName == "" {
|
if *gamePath == "" || *fontName == "" {
|
||||||
flag.Usage()
|
flag.Usage()
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg, err := config.Load(*configFile, *engine)
|
assets, err := assetstore.New(*gamePath)
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to load config: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
assets, err := assetstore.New(cfg.DefaultEngine())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@@ -9,16 +9,13 @@ import (
|
|||||||
"github.com/hajimehoshi/ebiten"
|
"github.com/hajimehoshi/ebiten"
|
||||||
|
|
||||||
"code.ur.gs/lupine/ordoor/internal/assetstore"
|
"code.ur.gs/lupine/ordoor/internal/assetstore"
|
||||||
"code.ur.gs/lupine/ordoor/internal/config"
|
|
||||||
"code.ur.gs/lupine/ordoor/internal/scenario"
|
"code.ur.gs/lupine/ordoor/internal/scenario"
|
||||||
"code.ur.gs/lupine/ordoor/internal/ui"
|
"code.ur.gs/lupine/ordoor/internal/ui"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
configFile = flag.String("config", "config.toml", "Config file")
|
gamePath = flag.String("game-path", "./orig", "Path to a WH40K: Chaos Gate installation")
|
||||||
engine = flag.String("engine", "", "Override engine to use")
|
gameMap = flag.String("map", "", "Name of a map, e.g., Chapter01")
|
||||||
|
|
||||||
gameMap = flag.String("map", "", "Name of a map, e.g., Chapter01")
|
|
||||||
|
|
||||||
winX = flag.Int("win-x", 1280, "Pre-scaled window X dimension")
|
winX = flag.Int("win-x", 1280, "Pre-scaled window X dimension")
|
||||||
winY = flag.Int("win-y", 1024, "Pre-scaled window Y dimension")
|
winY = flag.Int("win-y", 1024, "Pre-scaled window Y dimension")
|
||||||
@@ -31,19 +28,14 @@ type env struct {
|
|||||||
func main() {
|
func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
if *configFile == "" || *gameMap == "" {
|
if *gamePath == "" || *gameMap == "" {
|
||||||
flag.Usage()
|
flag.Usage()
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg, err := config.Load(*configFile, *engine)
|
assets, err := assetstore.New(*gamePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to load config: %v", err)
|
log.Fatalf("Failed to scan root directory %v: %v", *gamePath, err)
|
||||||
}
|
|
||||||
|
|
||||||
assets, err := assetstore.New(cfg.DefaultEngine())
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to scan root directory: %v", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
scenario, err := scenario.NewScenario(assets, *gameMap)
|
scenario, err := scenario.NewScenario(assets, *gameMap)
|
||||||
|
@@ -8,14 +8,11 @@ import (
|
|||||||
"github.com/hajimehoshi/ebiten"
|
"github.com/hajimehoshi/ebiten"
|
||||||
|
|
||||||
"code.ur.gs/lupine/ordoor/internal/assetstore"
|
"code.ur.gs/lupine/ordoor/internal/assetstore"
|
||||||
"code.ur.gs/lupine/ordoor/internal/config"
|
|
||||||
"code.ur.gs/lupine/ordoor/internal/ui"
|
"code.ur.gs/lupine/ordoor/internal/ui"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
configFile = flag.String("config", "config.toml", "Config file")
|
gamePath = flag.String("game-path", "./orig", "Path to a WH40K: Chaos Gate installation")
|
||||||
engine = flag.String("engine", "", "Override engine to use")
|
|
||||||
|
|
||||||
menuName = flag.String("menu", "", "Name of a menu, e.g. Main")
|
menuName = flag.String("menu", "", "Name of a menu, e.g. Main")
|
||||||
|
|
||||||
winX = flag.Int("win-x", 1280, "Pre-scaled window X dimension")
|
winX = flag.Int("win-x", 1280, "Pre-scaled window X dimension")
|
||||||
@@ -31,17 +28,12 @@ type dlg struct {
|
|||||||
func main() {
|
func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
if *configFile == "" || *menuName == "" {
|
if *gamePath == "" || *menuName == "" {
|
||||||
flag.Usage()
|
flag.Usage()
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg, err := config.Load(*configFile, *engine)
|
assets, err := assetstore.New(*gamePath)
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to load config: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
assets, err := assetstore.New(cfg.DefaultEngine())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@@ -10,17 +10,14 @@ import (
|
|||||||
"github.com/hajimehoshi/ebiten"
|
"github.com/hajimehoshi/ebiten"
|
||||||
|
|
||||||
"code.ur.gs/lupine/ordoor/internal/assetstore"
|
"code.ur.gs/lupine/ordoor/internal/assetstore"
|
||||||
"code.ur.gs/lupine/ordoor/internal/config"
|
|
||||||
"code.ur.gs/lupine/ordoor/internal/ui"
|
"code.ur.gs/lupine/ordoor/internal/ui"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
configFile = flag.String("config", "config.toml", "Config file")
|
gamePath = flag.String("game-path", "./orig", "Path to a WH40K: Chaos Gate installation")
|
||||||
engine = flag.String("engine", "", "Override engine to use")
|
objFile = flag.String("obj-file", "", "Path of an .obj file, e.g. ./orig/Obj/TZEENTCH.OBJ")
|
||||||
|
objName = flag.String("obj-name", "", "Name of an .obj file, e.g. TZEENTCH")
|
||||||
objFile = flag.String("obj-file", "", "Path of an .obj file, e.g. ./orig/Obj/TZEENTCH.OBJ")
|
sprIdx = flag.Int("spr-idx", 0, "Sprite index to start at")
|
||||||
objName = flag.String("obj-name", "", "Name of an .obj file, e.g. TZEENTCH")
|
|
||||||
sprIdx = flag.Int("spr-idx", 0, "Sprite index to start at")
|
|
||||||
|
|
||||||
winX = flag.Int("win-x", 1280, "Pre-scaled window X dimension")
|
winX = flag.Int("win-x", 1280, "Pre-scaled window X dimension")
|
||||||
winY = flag.Int("win-y", 1024, "Pre-scaled window Y dimension")
|
winY = flag.Int("win-y", 1024, "Pre-scaled window Y dimension")
|
||||||
@@ -45,17 +42,12 @@ type state struct {
|
|||||||
func main() {
|
func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
if *configFile == "" || (*objName == "" && *objFile == "") {
|
if *gamePath == "" || (*objName == "" && *objFile == "") {
|
||||||
flag.Usage()
|
flag.Usage()
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg, err := config.Load(*configFile, *engine)
|
assets, err := assetstore.New(*gamePath)
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to load config: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
assets, err := assetstore.New(cfg.DefaultEngine())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Failed to set up asset store: %v", err)
|
log.Fatal("Failed to set up asset store: %v", err)
|
||||||
}
|
}
|
||||||
|
@@ -10,15 +10,12 @@ import (
|
|||||||
"github.com/hajimehoshi/ebiten"
|
"github.com/hajimehoshi/ebiten"
|
||||||
|
|
||||||
"code.ur.gs/lupine/ordoor/internal/assetstore"
|
"code.ur.gs/lupine/ordoor/internal/assetstore"
|
||||||
"code.ur.gs/lupine/ordoor/internal/config"
|
|
||||||
"code.ur.gs/lupine/ordoor/internal/ui"
|
"code.ur.gs/lupine/ordoor/internal/ui"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
configFile = flag.String("config", "config.toml", "Config file")
|
gamePath = flag.String("game-path", "./orig", "Path to a WH40K: Chaos Gate installation")
|
||||||
engine = flag.String("engine", "", "Override engine to use")
|
setName = flag.String("set", "", "Name of a set, e.g., map01")
|
||||||
|
|
||||||
setName = flag.String("set", "", "Name of a set, e.g., map01")
|
|
||||||
|
|
||||||
winX = flag.Int("win-x", 1280, "Pre-scaled window X dimension")
|
winX = flag.Int("win-x", 1280, "Pre-scaled window X dimension")
|
||||||
winY = flag.Int("win-y", 1024, "Pre-scaled window Y dimension")
|
winY = flag.Int("win-y", 1024, "Pre-scaled window Y dimension")
|
||||||
@@ -42,17 +39,12 @@ type state struct {
|
|||||||
func main() {
|
func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
if *configFile == "" || *setName == "" {
|
if *gamePath == "" || *setName == "" {
|
||||||
flag.Usage()
|
flag.Usage()
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg, err := config.Load(*configFile, *engine)
|
assets, err := assetstore.New(*gamePath)
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to load config: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
assets, err := assetstore.New(cfg.DefaultEngine())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@@ -1,22 +1,6 @@
|
|||||||
video_player = ["mpv", "--no-config", "--keep-open=no", "--force-window=no", "--no-border", "--no-osc", "--fullscreen", "--no-input-default-bindings"]
|
[ordoor]
|
||||||
|
data_dir = "./orig"
|
||||||
default_engine = "ordoor"
|
video_player = ["mpv", "--no-config", "--keep-open=no", "--force-window=no", "--no-border", "--no-osc", "--fullscreen", "--no-input-default-bindings"]
|
||||||
|
|
||||||
[engines.geas] # Wages of War -> Gifts of Peace -> Geas
|
|
||||||
data_dir = "./WoW"
|
|
||||||
palette = "WagesOfWar"
|
|
||||||
|
|
||||||
[engines.ordoor] # Chaos Gate -> Order Door -> Ordoor
|
|
||||||
data_dir = "./CG"
|
|
||||||
palette = "ChaosGate"
|
|
||||||
|
|
||||||
[engines.baps] # Soldiers At War -> Boys at Play -> Baps
|
|
||||||
data_dir = "./SaW"
|
|
||||||
palette = "SoldiersAtWar"
|
|
||||||
|
|
||||||
# [engines.] Squad Leader -> ??? -> ???
|
|
||||||
# data_dir = "./SL"
|
|
||||||
# palette = "SquadLeader" # may not be relevant?
|
|
||||||
|
|
||||||
[options]
|
[options]
|
||||||
play_movies = true
|
play_movies = true
|
||||||
|
@@ -2,23 +2,24 @@ package assetstore
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"image/color"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"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"
|
||||||
"code.ur.gs/lupine/ordoor/internal/palettes"
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
RootDir = "" // Used in the entryMap for entries pertaining to the root dir
|
||||||
)
|
)
|
||||||
|
|
||||||
type entryMap map[string]map[string]string
|
type entryMap map[string]map[string]string
|
||||||
|
|
||||||
// type AssetStore is responsible for lazily loading game data when it is
|
// type AssetStore is responsible for lazily loading game data when it is
|
||||||
// required. Applications shouldn't need to do anything except set one of these
|
// required. Applications shouldn't need to do anything except set one of these
|
||||||
// up, pointing at the game dir root, to access all assets for that game.
|
// up, pointing at the game dir root, to access all assets.
|
||||||
//
|
//
|
||||||
// Assets should be loaded on-demand to keep memory costs as low as possible.
|
// Assets should be loaded on-demand to keep memory costs as low as possible.
|
||||||
// Cross-platform differences such as filename case sensitivity are also dealt
|
// Cross-platform differences such as filename case sensitivity are also dealt
|
||||||
@@ -26,12 +27,8 @@ type entryMap map[string]map[string]string
|
|||||||
//
|
//
|
||||||
// We assume the directory is read-only. You can run Refresh() if you make a
|
// We assume the directory is read-only. You can run Refresh() if you make a
|
||||||
// change.
|
// change.
|
||||||
//
|
|
||||||
// To mix assets from different games, either construct a synthetic directory
|
|
||||||
// or instantiate two separate asset stores.
|
|
||||||
type AssetStore struct {
|
type AssetStore struct {
|
||||||
RootDir string
|
RootDir string
|
||||||
Palette color.Palette
|
|
||||||
|
|
||||||
// Case-insensitive file lookup.
|
// Case-insensitive file lookup.
|
||||||
// {"":{"anim":"Anim", "obj":"Obj", ...}, "anim":{ "warhammer.ani":"WarHammer.ani" }, ...}
|
// {"":{"anim":"Anim", "obj":"Obj", ...}, "anim":{ "warhammer.ani":"WarHammer.ani" }, ...}
|
||||||
@@ -53,19 +50,9 @@ type AssetStore struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new AssetStore
|
// New returns a new AssetStore
|
||||||
func New(engine *config.Engine) (*AssetStore, error) {
|
func New(dir string) (*AssetStore, error) {
|
||||||
if engine == nil {
|
|
||||||
return nil, fmt.Errorf("Unconfigured engine passed to assetstore")
|
|
||||||
}
|
|
||||||
|
|
||||||
palette, ok := palettes.Get(engine.Palette)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("Couldn't find palette %q for engine", engine.Palette)
|
|
||||||
}
|
|
||||||
|
|
||||||
store := &AssetStore{
|
store := &AssetStore{
|
||||||
RootDir: engine.DataDir,
|
RootDir: dir,
|
||||||
Palette: palette,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// fill entryMap
|
// fill entryMap
|
||||||
@@ -83,7 +70,7 @@ func (a *AssetStore) Refresh() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
newEntryMap := make(entryMap, len(rootEntries))
|
newEntryMap := make(entryMap, len(rootEntries))
|
||||||
newEntryMap[""] = rootEntries
|
newEntryMap[RootDir] = rootEntries
|
||||||
|
|
||||||
for lower, natural := range rootEntries {
|
for lower, natural := range rootEntries {
|
||||||
path := filepath.Join(a.RootDir, natural)
|
path := filepath.Join(a.RootDir, natural)
|
||||||
@@ -131,7 +118,7 @@ func (a *AssetStore) lookup(name, ext string, dirs ...string) (string, error) {
|
|||||||
dir = canonical(dir)
|
dir = canonical(dir)
|
||||||
if base, ok := a.entries[dir]; ok {
|
if base, ok := a.entries[dir]; ok {
|
||||||
if file, ok := base[filename]; ok {
|
if file, ok := base[filename]; ok {
|
||||||
actualDir := a.entries[""][dir]
|
actualDir := a.entries[RootDir][dir]
|
||||||
return filepath.Join(a.RootDir, actualDir, file), nil
|
return filepath.Join(a.RootDir, actualDir, file), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -62,7 +62,7 @@ func (a *AssetStore) Menu(name string) (*Menu, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
raw, err := menus.LoadMenu(filename, a.Palette)
|
raw, err := menus.LoadMenu(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@@ -117,7 +117,7 @@ func (o *Object) Sprite(idx int) (*Sprite, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
raw := o.raw.Sprites[idx]
|
raw := o.raw.Sprites[idx]
|
||||||
img, err := ebiten.NewImageFromImage(raw.ToImage(o.assets.Palette), ebiten.FilterDefault)
|
img, err := ebiten.NewImageFromImage(raw.ToImage(), ebiten.FilterDefault)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@@ -2,16 +2,15 @@ package config
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/BurntSushi/toml"
|
"github.com/BurntSushi/toml"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Engine struct {
|
type Ordoor struct {
|
||||||
DataDir string `toml:"data_dir"`
|
DataDir string `toml:"data_dir"`
|
||||||
Palette string `toml:"palette"`
|
VideoPlayer []string `toml:"video_player"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Things set in the options hash
|
// Things set in the options hash
|
||||||
@@ -39,40 +38,12 @@ type Options struct {
|
|||||||
type Config struct {
|
type Config struct {
|
||||||
filename string `toml:"-"`
|
filename string `toml:"-"`
|
||||||
|
|
||||||
VideoPlayer []string `toml:"video_player"`
|
|
||||||
Engines map[string]Engine `toml:"engines"`
|
|
||||||
|
|
||||||
DefaultEngineName string `toml:"default_engine"`
|
|
||||||
|
|
||||||
// FIXME: options may well end up being per-engine too
|
|
||||||
Defaults *Options `toml:"-"`
|
Defaults *Options `toml:"-"`
|
||||||
|
Ordoor `toml:"ordoor"`
|
||||||
Options `toml:"options"`
|
Options `toml:"options"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) Engine(name string) *Engine {
|
func Load(filename string) (*Config, error) {
|
||||||
engine, ok := c.Engines[name]
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &engine
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Config) DefaultEngine() *Engine {
|
|
||||||
return c.Engine(c.DefaultEngineName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: case-insensitive lookup
|
|
||||||
func (c *Config) DataFile(engine string, path string) string {
|
|
||||||
cfg, ok := c.Engines[engine]
|
|
||||||
if !ok {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
return filepath.Join(cfg.DataDir, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Load(filename string, overrideDefaultEngine string) (*Config, error) {
|
|
||||||
var out Config
|
var out Config
|
||||||
|
|
||||||
_, err := toml.DecodeFile(filename, &out)
|
_, err := toml.DecodeFile(filename, &out)
|
||||||
@@ -82,15 +53,7 @@ func Load(filename string, overrideDefaultEngine string) (*Config, error) {
|
|||||||
|
|
||||||
out.filename = filename
|
out.filename = filename
|
||||||
|
|
||||||
if overrideDefaultEngine != "" {
|
return &out, err
|
||||||
out.DefaultEngineName = overrideDefaultEngine
|
|
||||||
}
|
|
||||||
|
|
||||||
if out.DefaultEngine() == nil {
|
|
||||||
return nil, fmt.Errorf("Default engine %q not configured", out.DefaultEngineName)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &out, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) HasUnsetOptions() bool {
|
func (c *Config) HasUnsetOptions() bool {
|
||||||
@@ -109,6 +72,11 @@ func (c *Config) Save() error {
|
|||||||
return toml.NewEncoder(f).Encode(c)
|
return toml.NewEncoder(f).Encode(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: case-insensitive lookup
|
||||||
|
func (c *Config) DataFile(path string) string {
|
||||||
|
return filepath.Join(c.DataDir, path)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Config) ResetDefaults() error {
|
func (c *Config) ResetDefaults() error {
|
||||||
if c.Defaults == nil {
|
if c.Defaults == nil {
|
||||||
return errors.New("Defaults not available")
|
return errors.New("Defaults not available")
|
||||||
|
@@ -4,7 +4,6 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
"image/color"
|
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
@@ -13,6 +12,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.ur.gs/lupine/ordoor/internal/data/rle"
|
"code.ur.gs/lupine/ordoor/internal/data/rle"
|
||||||
|
"code.ur.gs/lupine/ordoor/internal/palettes"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SpriteHeader struct {
|
type SpriteHeader struct {
|
||||||
@@ -53,12 +53,12 @@ type Sprite struct {
|
|||||||
Data []byte
|
Data []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Sprite) ToImage(palette color.Palette) *image.Paletted {
|
func (s *Sprite) ToImage() *image.Paletted {
|
||||||
return &image.Paletted{
|
return &image.Paletted{
|
||||||
Pix: s.Data,
|
Pix: s.Data,
|
||||||
Stride: int(s.Width),
|
Stride: int(s.Width),
|
||||||
Rect: image.Rect(0, 0, int(s.Width), int(s.Height)),
|
Rect: image.Rect(0, 0, int(s.Width), int(s.Height)),
|
||||||
Palette: palette,
|
Palette: palettes.DefaultPalette(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -9,6 +9,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"code.ur.gs/lupine/ordoor/internal/palettes"
|
||||||
"code.ur.gs/lupine/ordoor/internal/util/asciiscan"
|
"code.ur.gs/lupine/ordoor/internal/util/asciiscan"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -142,7 +143,7 @@ func (p *Properties) Point() image.Point {
|
|||||||
return image.Point{}
|
return image.Point{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadMenu(filename string, palette color.Palette) (*Menu, error) {
|
func LoadMenu(filename string) (*Menu, error) {
|
||||||
name := filepath.Base(filename)
|
name := filepath.Base(filename)
|
||||||
name = strings.TrimSuffix(name, filepath.Ext(name))
|
name = strings.TrimSuffix(name, filepath.Ext(name))
|
||||||
name = strings.ToLower(name)
|
name = strings.ToLower(name)
|
||||||
@@ -162,7 +163,7 @@ func LoadMenu(filename string, palette color.Palette) (*Menu, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := loadProperties(out, scanner, palette); err != nil {
|
if err := loadProperties(out, scanner); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,7 +189,7 @@ func loadObjects(menu *Menu, scanner *asciiscan.Scanner) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadProperties(menu *Menu, scanner *asciiscan.Scanner, palette color.Palette) error {
|
func loadProperties(menu *Menu, scanner *asciiscan.Scanner) error {
|
||||||
for {
|
for {
|
||||||
ok, err := scanner.PeekProperty()
|
ok, err := scanner.PeekProperty()
|
||||||
|
|
||||||
@@ -217,9 +218,9 @@ func loadProperties(menu *Menu, scanner *asciiscan.Scanner, palette color.Palett
|
|||||||
|
|
||||||
switch strings.ToUpper(k) {
|
switch strings.ToUpper(k) {
|
||||||
case "BACKGROUND COLOR":
|
case "BACKGROUND COLOR":
|
||||||
menu.BackgroundColor = palette[vInt]
|
menu.BackgroundColor = palettes.DefaultPalette()[vInt]
|
||||||
case "HYPERTEXT COLOR":
|
case "HYPERTEXT COLOR":
|
||||||
menu.HypertextColor = palette[vInt]
|
menu.HypertextColor = palettes.DefaultPalette()[vInt]
|
||||||
case "FONT TYPE":
|
case "FONT TYPE":
|
||||||
menu.FontType = vInt
|
menu.FontType = vInt
|
||||||
default:
|
default:
|
||||||
@@ -318,7 +319,7 @@ func loadRecords(baseDir string, menu *Menu, scanner *asciiscan.Scanner) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadMenus(dir string, palette color.Palette) (map[string]*Menu, error) {
|
func LoadMenus(dir string) (map[string]*Menu, error) {
|
||||||
fis, err := ioutil.ReadDir(dir)
|
fis, err := ioutil.ReadDir(dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -336,7 +337,7 @@ func LoadMenus(dir string, palette color.Palette) (map[string]*Menu, error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
built, err := LoadMenu(filepath.Join(dir, relname), palette)
|
built, err := LoadMenu(filepath.Join(dir, relname))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%s: %v", filepath.Join(dir, relname), err)
|
return nil, fmt.Errorf("%s: %v", filepath.Join(dir, relname), err)
|
||||||
}
|
}
|
||||||
|
@@ -34,12 +34,12 @@ type Ordoor struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Run(configFile string, overrideX, overrideY int) error {
|
func Run(configFile string, overrideX, overrideY int) error {
|
||||||
cfg, err := config.Load(configFile, "ordoor")
|
cfg, err := config.Load(configFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Couldn't load config file: %v", err)
|
return fmt.Errorf("Couldn't load config file: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
assets, err := assetstore.New(cfg.DefaultEngine())
|
assets, err := assetstore.New(cfg.Ordoor.DataDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed to initialize asset store: %v", err)
|
return fmt.Errorf("Failed to initialize asset store: %v", err)
|
||||||
}
|
}
|
||||||
|
@@ -6,7 +6,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (o *Ordoor) PlayVideo(name string, skippable bool) {
|
func (o *Ordoor) PlayVideo(name string, skippable bool) {
|
||||||
filename := o.config.DataFile("ordoor", "SMK/"+name+".smk")
|
filename := o.config.DataFile("SMK/" + name + ".smk")
|
||||||
|
|
||||||
if len(o.config.VideoPlayer) == 0 {
|
if len(o.config.VideoPlayer) == 0 {
|
||||||
log.Printf("Video player not configured, skipping video %v", filename)
|
log.Printf("Video player not configured, skipping video %v", filename)
|
||||||
|
@@ -5,6 +5,9 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Override this to change the palette globally
|
||||||
|
const DefaultPaletteName = "SoldiersAtWar"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
Transparent = color.RGBA{R: 0, G: 0, B: 0, A: 0}
|
Transparent = color.RGBA{R: 0, G: 0, B: 0, A: 0}
|
||||||
|
|
||||||
@@ -13,8 +16,6 @@ var (
|
|||||||
initPalettes = sync.Once{}
|
initPalettes = sync.Once{}
|
||||||
)
|
)
|
||||||
|
|
||||||
func Get(name string) (color.Palette, bool) {
|
func DefaultPalette() color.Palette {
|
||||||
p, ok := Palettes[name]
|
return Palettes[DefaultPaletteName]
|
||||||
|
|
||||||
return p, ok
|
|
||||||
}
|
}
|
||||||
|
34
investigation/Palette/generate-palette
Executable file
34
investigation/Palette/generate-palette
Executable file
@@ -0,0 +1,34 @@
|
|||||||
|
#!/usr/bin/env ruby
|
||||||
|
|
||||||
|
lines = File.read("palette.ppm").split("\n")
|
||||||
|
lines = lines.select { |l| l[0] != "#" }
|
||||||
|
|
||||||
|
raise "Not an ASCII ppm" if lines.shift != "P3"
|
||||||
|
raise "Incorrect dimensions" unless lines.shift == "1 256"
|
||||||
|
raise "Incorrect maxval" unless lines.shift == "255"
|
||||||
|
raise "Too many lines left" unless lines.size == 768
|
||||||
|
|
||||||
|
puts <<EOF
|
||||||
|
package data
|
||||||
|
|
||||||
|
import "image/color"
|
||||||
|
|
||||||
|
var (
|
||||||
|
Transparent = color.RGBA{R: 0, G: 0, B: 0, A: 0}
|
||||||
|
|
||||||
|
ColorPalette = color.Palette{
|
||||||
|
Transparent,
|
||||||
|
EOF
|
||||||
|
|
||||||
|
lines.shift(3) # Ignore idx 0
|
||||||
|
|
||||||
|
255.times do
|
||||||
|
r, g, b = lines.shift(3).map(&:to_i)
|
||||||
|
|
||||||
|
puts "\t\tcolor.RGBA{R: #{r}, G: #{g}, B: #{b}, A: 255},"
|
||||||
|
end
|
||||||
|
|
||||||
|
puts <<EOF
|
||||||
|
}
|
||||||
|
)
|
||||||
|
EOF
|
12
investigation/Palette/palette.asn
Normal file
12
investigation/Palette/palette.asn
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# jungle floor
|
||||||
|
# jungtil.obj/.asn
|
||||||
|
# /--> d:\warflics\missions\jungtil.flc
|
||||||
|
#
|
||||||
|
|
||||||
|
0:DEF 2;
|
||||||
|
|
||||||
|
0:TYPE 2;
|
||||||
|
|
||||||
|
END OF FILE
|
||||||
|
|
||||||
|
|
BIN
investigation/Palette/palette.obj
Normal file
BIN
investigation/Palette/palette.obj
Normal file
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
772
investigation/Palette/palette.ppm
Normal file
772
investigation/Palette/palette.ppm
Normal file
@@ -0,0 +1,772 @@
|
|||||||
|
P3
|
||||||
|
# CREATOR: GIMP PNM Filter Version 1.1
|
||||||
|
1 256
|
||||||
|
255
|
||||||
|
255
|
||||||
|
255
|
||||||
|
255
|
||||||
|
128
|
||||||
|
0
|
||||||
|
0
|
||||||
|
0
|
||||||
|
128
|
||||||
|
0
|
||||||
|
128
|
||||||
|
128
|
||||||
|
0
|
||||||
|
0
|
||||||
|
0
|
||||||
|
128
|
||||||
|
128
|
||||||
|
0
|
||||||
|
128
|
||||||
|
0
|
||||||
|
128
|
||||||
|
128
|
||||||
|
192
|
||||||
|
192
|
||||||
|
192
|
||||||
|
192
|
||||||
|
220
|
||||||
|
192
|
||||||
|
166
|
||||||
|
202
|
||||||
|
240
|
||||||
|
255
|
||||||
|
255
|
||||||
|
255
|
||||||
|
240
|
||||||
|
240
|
||||||
|
240
|
||||||
|
221
|
||||||
|
221
|
||||||
|
221
|
||||||
|
203
|
||||||
|
203
|
||||||
|
203
|
||||||
|
187
|
||||||
|
187
|
||||||
|
187
|
||||||
|
178
|
||||||
|
178
|
||||||
|
178
|
||||||
|
168
|
||||||
|
168
|
||||||
|
168
|
||||||
|
157
|
||||||
|
157
|
||||||
|
157
|
||||||
|
147
|
||||||
|
147
|
||||||
|
147
|
||||||
|
137
|
||||||
|
137
|
||||||
|
137
|
||||||
|
127
|
||||||
|
127
|
||||||
|
127
|
||||||
|
117
|
||||||
|
117
|
||||||
|
117
|
||||||
|
106
|
||||||
|
106
|
||||||
|
106
|
||||||
|
96
|
||||||
|
96
|
||||||
|
96
|
||||||
|
86
|
||||||
|
86
|
||||||
|
86
|
||||||
|
76
|
||||||
|
76
|
||||||
|
76
|
||||||
|
61
|
||||||
|
61
|
||||||
|
61
|
||||||
|
49
|
||||||
|
49
|
||||||
|
49
|
||||||
|
36
|
||||||
|
36
|
||||||
|
36
|
||||||
|
24
|
||||||
|
24
|
||||||
|
24
|
||||||
|
12
|
||||||
|
12
|
||||||
|
12
|
||||||
|
0
|
||||||
|
0
|
||||||
|
0
|
||||||
|
134
|
||||||
|
134
|
||||||
|
255
|
||||||
|
113
|
||||||
|
113
|
||||||
|
241
|
||||||
|
93
|
||||||
|
93
|
||||||
|
228
|
||||||
|
72
|
||||||
|
72
|
||||||
|
214
|
||||||
|
63
|
||||||
|
63
|
||||||
|
200
|
||||||
|
55
|
||||||
|
55
|
||||||
|
186
|
||||||
|
46
|
||||||
|
46
|
||||||
|
172
|
||||||
|
38
|
||||||
|
38
|
||||||
|
158
|
||||||
|
29
|
||||||
|
29
|
||||||
|
144
|
||||||
|
20
|
||||||
|
20
|
||||||
|
131
|
||||||
|
12
|
||||||
|
12
|
||||||
|
117
|
||||||
|
3
|
||||||
|
3
|
||||||
|
103
|
||||||
|
3
|
||||||
|
3
|
||||||
|
91
|
||||||
|
3
|
||||||
|
3
|
||||||
|
79
|
||||||
|
3
|
||||||
|
3
|
||||||
|
68
|
||||||
|
3
|
||||||
|
3
|
||||||
|
56
|
||||||
|
255
|
||||||
|
145
|
||||||
|
145
|
||||||
|
242
|
||||||
|
123
|
||||||
|
123
|
||||||
|
230
|
||||||
|
101
|
||||||
|
101
|
||||||
|
217
|
||||||
|
79
|
||||||
|
79
|
||||||
|
205
|
||||||
|
70
|
||||||
|
70
|
||||||
|
193
|
||||||
|
61
|
||||||
|
61
|
||||||
|
181
|
||||||
|
53
|
||||||
|
53
|
||||||
|
169
|
||||||
|
44
|
||||||
|
44
|
||||||
|
157
|
||||||
|
35
|
||||||
|
35
|
||||||
|
144
|
||||||
|
26
|
||||||
|
26
|
||||||
|
132
|
||||||
|
18
|
||||||
|
18
|
||||||
|
120
|
||||||
|
9
|
||||||
|
9
|
||||||
|
108
|
||||||
|
8
|
||||||
|
8
|
||||||
|
94
|
||||||
|
7
|
||||||
|
7
|
||||||
|
79
|
||||||
|
7
|
||||||
|
7
|
||||||
|
65
|
||||||
|
6
|
||||||
|
6
|
||||||
|
147
|
||||||
|
142
|
||||||
|
185
|
||||||
|
132
|
||||||
|
126
|
||||||
|
172
|
||||||
|
117
|
||||||
|
109
|
||||||
|
159
|
||||||
|
102
|
||||||
|
93
|
||||||
|
146
|
||||||
|
95
|
||||||
|
86
|
||||||
|
133
|
||||||
|
88
|
||||||
|
78
|
||||||
|
123
|
||||||
|
82
|
||||||
|
73
|
||||||
|
115
|
||||||
|
77
|
||||||
|
67
|
||||||
|
107
|
||||||
|
72
|
||||||
|
61
|
||||||
|
100
|
||||||
|
67
|
||||||
|
55
|
||||||
|
92
|
||||||
|
61
|
||||||
|
50
|
||||||
|
84
|
||||||
|
56
|
||||||
|
44
|
||||||
|
76
|
||||||
|
51
|
||||||
|
38
|
||||||
|
68
|
||||||
|
46
|
||||||
|
32
|
||||||
|
60
|
||||||
|
40
|
||||||
|
27
|
||||||
|
52
|
||||||
|
35
|
||||||
|
21
|
||||||
|
44
|
||||||
|
200
|
||||||
|
150
|
||||||
|
137
|
||||||
|
187
|
||||||
|
130
|
||||||
|
115
|
||||||
|
175
|
||||||
|
110
|
||||||
|
94
|
||||||
|
164
|
||||||
|
95
|
||||||
|
77
|
||||||
|
154
|
||||||
|
79
|
||||||
|
61
|
||||||
|
143
|
||||||
|
64
|
||||||
|
44
|
||||||
|
137
|
||||||
|
60
|
||||||
|
42
|
||||||
|
132
|
||||||
|
55
|
||||||
|
38
|
||||||
|
125
|
||||||
|
50
|
||||||
|
33
|
||||||
|
120
|
||||||
|
48
|
||||||
|
29
|
||||||
|
111
|
||||||
|
44
|
||||||
|
26
|
||||||
|
103
|
||||||
|
39
|
||||||
|
24
|
||||||
|
94
|
||||||
|
35
|
||||||
|
21
|
||||||
|
83
|
||||||
|
30
|
||||||
|
18
|
||||||
|
72
|
||||||
|
25
|
||||||
|
14
|
||||||
|
61
|
||||||
|
20
|
||||||
|
10
|
||||||
|
121
|
||||||
|
107
|
||||||
|
34
|
||||||
|
109
|
||||||
|
94
|
||||||
|
29
|
||||||
|
96
|
||||||
|
82
|
||||||
|
25
|
||||||
|
84
|
||||||
|
69
|
||||||
|
20
|
||||||
|
77
|
||||||
|
62
|
||||||
|
17
|
||||||
|
70
|
||||||
|
55
|
||||||
|
14
|
||||||
|
63
|
||||||
|
47
|
||||||
|
12
|
||||||
|
56
|
||||||
|
40
|
||||||
|
9
|
||||||
|
93
|
||||||
|
120
|
||||||
|
53
|
||||||
|
80
|
||||||
|
103
|
||||||
|
42
|
||||||
|
66
|
||||||
|
86
|
||||||
|
31
|
||||||
|
53
|
||||||
|
69
|
||||||
|
20
|
||||||
|
49
|
||||||
|
60
|
||||||
|
16
|
||||||
|
45
|
||||||
|
52
|
||||||
|
12
|
||||||
|
43
|
||||||
|
44
|
||||||
|
10
|
||||||
|
43
|
||||||
|
38
|
||||||
|
8
|
||||||
|
136
|
||||||
|
145
|
||||||
|
44
|
||||||
|
118
|
||||||
|
128
|
||||||
|
37
|
||||||
|
101
|
||||||
|
111
|
||||||
|
30
|
||||||
|
83
|
||||||
|
94
|
||||||
|
23
|
||||||
|
70
|
||||||
|
79
|
||||||
|
17
|
||||||
|
56
|
||||||
|
65
|
||||||
|
11
|
||||||
|
42
|
||||||
|
50
|
||||||
|
6
|
||||||
|
28
|
||||||
|
36
|
||||||
|
0
|
||||||
|
57
|
||||||
|
134
|
||||||
|
64
|
||||||
|
48
|
||||||
|
118
|
||||||
|
54
|
||||||
|
38
|
||||||
|
101
|
||||||
|
43
|
||||||
|
29
|
||||||
|
85
|
||||||
|
33
|
||||||
|
22
|
||||||
|
71
|
||||||
|
25
|
||||||
|
15
|
||||||
|
58
|
||||||
|
17
|
||||||
|
7
|
||||||
|
45
|
||||||
|
8
|
||||||
|
0
|
||||||
|
32
|
||||||
|
0
|
||||||
|
143
|
||||||
|
87
|
||||||
|
56
|
||||||
|
126
|
||||||
|
75
|
||||||
|
45
|
||||||
|
110
|
||||||
|
64
|
||||||
|
35
|
||||||
|
93
|
||||||
|
52
|
||||||
|
24
|
||||||
|
85
|
||||||
|
44
|
||||||
|
16
|
||||||
|
72
|
||||||
|
36
|
||||||
|
12
|
||||||
|
64
|
||||||
|
32
|
||||||
|
8
|
||||||
|
56
|
||||||
|
24
|
||||||
|
4
|
||||||
|
127
|
||||||
|
96
|
||||||
|
54
|
||||||
|
115
|
||||||
|
85
|
||||||
|
46
|
||||||
|
102
|
||||||
|
75
|
||||||
|
39
|
||||||
|
90
|
||||||
|
64
|
||||||
|
31
|
||||||
|
82
|
||||||
|
57
|
||||||
|
25
|
||||||
|
75
|
||||||
|
51
|
||||||
|
20
|
||||||
|
68
|
||||||
|
44
|
||||||
|
15
|
||||||
|
61
|
||||||
|
38
|
||||||
|
10
|
||||||
|
141
|
||||||
|
86
|
||||||
|
56
|
||||||
|
126
|
||||||
|
75
|
||||||
|
46
|
||||||
|
110
|
||||||
|
65
|
||||||
|
36
|
||||||
|
95
|
||||||
|
54
|
||||||
|
26
|
||||||
|
88
|
||||||
|
51
|
||||||
|
25
|
||||||
|
73
|
||||||
|
40
|
||||||
|
18
|
||||||
|
57
|
||||||
|
29
|
||||||
|
10
|
||||||
|
42
|
||||||
|
18
|
||||||
|
3
|
||||||
|
172
|
||||||
|
199
|
||||||
|
199
|
||||||
|
138
|
||||||
|
173
|
||||||
|
173
|
||||||
|
104
|
||||||
|
148
|
||||||
|
148
|
||||||
|
71
|
||||||
|
122
|
||||||
|
122
|
||||||
|
37
|
||||||
|
97
|
||||||
|
97
|
||||||
|
3
|
||||||
|
71
|
||||||
|
71
|
||||||
|
4
|
||||||
|
56
|
||||||
|
56
|
||||||
|
4
|
||||||
|
41
|
||||||
|
41
|
||||||
|
217
|
||||||
|
209
|
||||||
|
200
|
||||||
|
202
|
||||||
|
194
|
||||||
|
184
|
||||||
|
188
|
||||||
|
178
|
||||||
|
167
|
||||||
|
173
|
||||||
|
163
|
||||||
|
151
|
||||||
|
158
|
||||||
|
147
|
||||||
|
134
|
||||||
|
148
|
||||||
|
136
|
||||||
|
123
|
||||||
|
137
|
||||||
|
125
|
||||||
|
112
|
||||||
|
126
|
||||||
|
114
|
||||||
|
101
|
||||||
|
116
|
||||||
|
104
|
||||||
|
91
|
||||||
|
105
|
||||||
|
93
|
||||||
|
80
|
||||||
|
94
|
||||||
|
82
|
||||||
|
69
|
||||||
|
84
|
||||||
|
71
|
||||||
|
58
|
||||||
|
73
|
||||||
|
60
|
||||||
|
47
|
||||||
|
62
|
||||||
|
50
|
||||||
|
36
|
||||||
|
52
|
||||||
|
39
|
||||||
|
25
|
||||||
|
41
|
||||||
|
28
|
||||||
|
14
|
||||||
|
231
|
||||||
|
232
|
||||||
|
207
|
||||||
|
219
|
||||||
|
217
|
||||||
|
180
|
||||||
|
208
|
||||||
|
201
|
||||||
|
152
|
||||||
|
196
|
||||||
|
186
|
||||||
|
125
|
||||||
|
184
|
||||||
|
171
|
||||||
|
98
|
||||||
|
173
|
||||||
|
155
|
||||||
|
70
|
||||||
|
161
|
||||||
|
140
|
||||||
|
43
|
||||||
|
150
|
||||||
|
129
|
||||||
|
39
|
||||||
|
139
|
||||||
|
119
|
||||||
|
37
|
||||||
|
127
|
||||||
|
109
|
||||||
|
33
|
||||||
|
117
|
||||||
|
99
|
||||||
|
29
|
||||||
|
105
|
||||||
|
89
|
||||||
|
25
|
||||||
|
90
|
||||||
|
76
|
||||||
|
21
|
||||||
|
75
|
||||||
|
62
|
||||||
|
18
|
||||||
|
60
|
||||||
|
49
|
||||||
|
14
|
||||||
|
45
|
||||||
|
35
|
||||||
|
10
|
||||||
|
128
|
||||||
|
99
|
||||||
|
127
|
||||||
|
113
|
||||||
|
73
|
||||||
|
112
|
||||||
|
97
|
||||||
|
47
|
||||||
|
97
|
||||||
|
82
|
||||||
|
21
|
||||||
|
82
|
||||||
|
75
|
||||||
|
2
|
||||||
|
74
|
||||||
|
68
|
||||||
|
2
|
||||||
|
67
|
||||||
|
52
|
||||||
|
3
|
||||||
|
50
|
||||||
|
35
|
||||||
|
4
|
||||||
|
33
|
||||||
|
247
|
||||||
|
178
|
||||||
|
102
|
||||||
|
229
|
||||||
|
152
|
||||||
|
75
|
||||||
|
212
|
||||||
|
127
|
||||||
|
48
|
||||||
|
194
|
||||||
|
101
|
||||||
|
21
|
||||||
|
179
|
||||||
|
87
|
||||||
|
16
|
||||||
|
161
|
||||||
|
73
|
||||||
|
12
|
||||||
|
142
|
||||||
|
59
|
||||||
|
9
|
||||||
|
124
|
||||||
|
45
|
||||||
|
5
|
||||||
|
255
|
||||||
|
0
|
||||||
|
0
|
||||||
|
194
|
||||||
|
3
|
||||||
|
3
|
||||||
|
161
|
||||||
|
2
|
||||||
|
2
|
||||||
|
255
|
||||||
|
227
|
||||||
|
11
|
||||||
|
209
|
||||||
|
185
|
||||||
|
8
|
||||||
|
169
|
||||||
|
150
|
||||||
|
6
|
||||||
|
103
|
||||||
|
190
|
||||||
|
255
|
||||||
|
2
|
||||||
|
130
|
||||||
|
232
|
||||||
|
4
|
||||||
|
4
|
||||||
|
209
|
||||||
|
0
|
||||||
|
255
|
||||||
|
0
|
||||||
|
7
|
||||||
|
180
|
||||||
|
7
|
||||||
|
3
|
||||||
|
132
|
||||||
|
3
|
||||||
|
255
|
||||||
|
114
|
||||||
|
230
|
||||||
|
255
|
||||||
|
17
|
||||||
|
205
|
||||||
|
203
|
||||||
|
6
|
||||||
|
156
|
||||||
|
246
|
||||||
|
164
|
||||||
|
73
|
||||||
|
221
|
||||||
|
123
|
||||||
|
16
|
||||||
|
177
|
||||||
|
95
|
||||||
|
4
|
||||||
|
0
|
||||||
|
0
|
||||||
|
0
|
||||||
|
0
|
||||||
|
0
|
||||||
|
0
|
||||||
|
0
|
||||||
|
0
|
||||||
|
0
|
||||||
|
0
|
||||||
|
0
|
||||||
|
0
|
||||||
|
0
|
||||||
|
0
|
||||||
|
0
|
||||||
|
132
|
||||||
|
18
|
||||||
|
18
|
||||||
|
113
|
||||||
|
13
|
||||||
|
13
|
||||||
|
94
|
||||||
|
7
|
||||||
|
7
|
||||||
|
107
|
||||||
|
8
|
||||||
|
8
|
||||||
|
120
|
||||||
|
9
|
||||||
|
9
|
||||||
|
193
|
||||||
|
62
|
||||||
|
62
|
||||||
|
181
|
||||||
|
54
|
||||||
|
54
|
||||||
|
168
|
||||||
|
45
|
||||||
|
45
|
||||||
|
157
|
||||||
|
36
|
||||||
|
36
|
||||||
|
144
|
||||||
|
27
|
||||||
|
27
|
||||||
|
62
|
||||||
|
50
|
||||||
|
36
|
||||||
|
57
|
||||||
|
45
|
||||||
|
31
|
||||||
|
52
|
||||||
|
39
|
||||||
|
25
|
||||||
|
46
|
||||||
|
34
|
||||||
|
20
|
||||||
|
41
|
||||||
|
28
|
||||||
|
14
|
||||||
|
255
|
||||||
|
251
|
||||||
|
240
|
||||||
|
160
|
||||||
|
160
|
||||||
|
164
|
||||||
|
128
|
||||||
|
128
|
||||||
|
128
|
||||||
|
255
|
||||||
|
0
|
||||||
|
0
|
||||||
|
0
|
||||||
|
255
|
||||||
|
0
|
||||||
|
255
|
||||||
|
255
|
||||||
|
0
|
||||||
|
0
|
||||||
|
0
|
||||||
|
255
|
||||||
|
255
|
||||||
|
0
|
||||||
|
255
|
||||||
|
0
|
||||||
|
255
|
||||||
|
255
|
||||||
|
255
|
||||||
|
255
|
||||||
|
255
|
BIN
investigation/Palette/palette.rgb.raw
Normal file
BIN
investigation/Palette/palette.rgb.raw
Normal file
Binary file not shown.
13
investigation/Palette/palette.txt
Normal file
13
investigation/Palette/palette.txt
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
To build this palette, I did the following:
|
||||||
|
|
||||||
|
* Created the .asn + .obj files by hand
|
||||||
|
* Referenced them in a .set file and asked WH40K_TD.exe to render the sprites
|
||||||
|
* Screenshotted the output and used GIMP to create a 1x255 image with them all
|
||||||
|
* Exported that image as an ASCII .ppm file
|
||||||
|
* Used the `generate_palette` script to output Go code \o/
|
||||||
|
|
||||||
|
Note that palette index 0 is ignored, and hardcoded to be transparent black.
|
||||||
|
This is because 0x00 seems to be used as a record separator in .obj files, so
|
||||||
|
can't be used as a palette index anyway.
|
||||||
|
|
||||||
|
|
BIN
investigation/Palette/palette.xcf
Normal file
BIN
investigation/Palette/palette.xcf
Normal file
Binary file not shown.
Reference in New Issue
Block a user