diff --git a/.gitignore b/.gitignore index 6b92e7e..a9901b8 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /loader /orig /view-map +/view-set diff --git a/Makefile b/Makefile index bf84a7c..43b7bad 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,10 @@ loader: $(srcfiles) view-map: $(srcfiles) go build -o view-map ur.gs/chaos-gate/cmd/view-map +view-set: $(srcfiles) + go build -o view-set ur.gs/chaos-gate/cmd/view-set + clean: - rm -f loader view-map + rm -f loader view-map view-set .PHONY: all clean diff --git a/cmd/view-map/main.go b/cmd/view-map/main.go index 0d3daf4..8cd09a4 100644 --- a/cmd/view-map/main.go +++ b/cmd/view-map/main.go @@ -15,15 +15,13 @@ import ( "ur.gs/chaos-gate/internal/maps" "ur.gs/chaos-gate/internal/sets" + "ur.gs/chaos-gate/internal/ui" ) var ( gamePath = flag.String("game-path", "./orig", "Path to a WH40K: Chaos Gate installation") mapFile = flag.String("map", "", "Prefix path to a .map file, e.g. ./orig/Maps/Chapter01.MAP") txtFile = flag.String("txt", "", "Prefix path to a .txt file, e.g. ./orig/Maps/Chapter01.txt") - - winX = flag.Int("win-x", 1280, "width of the view-map window") - winY = flag.Int("win-y", 1024, "height of the view-map window") ) type env struct { @@ -69,42 +67,32 @@ func main() { env := &env{gameMap: gameMap, set: mapSet} // The main thread now belongs to pixelgl - pixelgl.Run(func() { run(env) }) + pixelgl.Run(env.run) } -func run(env *env) { - // WARE! 0,0 is the *bottom left* of the window - cfg := pixelgl.WindowConfig{ - Title: "View Map " + *mapFile, - Bounds: pixel.R(0, 0, float64(*winX), float64(*winY)), - VSync: true, - } - - win, err := pixelgl.NewWindow(cfg) +func (e *env) run() { + win, err := ui.NewWindow("View Map " + *mapFile) if err != nil { log.Fatal("Couldn't create window: %v", err) } + pWin := win.PixelWindow state := &runState{ - env: env, + env: e, autoUpdate: true, - - camPos: pixel.V(0, float64(-*winY)), - - zoom: 8.0, + camPos: pixel.V(0, float64(-pWin.Bounds().Size().Y)), + zoom: 8.0, } - for !win.Closed() { + win.Run(func() { oldState := *state - state = runStep(win, state) + state = runStep(pWin, state) if oldState != *state { log.Printf("z=%d cellIdx=%d", state.zIdx, state.cellIdx) - present(win, state) + present(pWin, state) } - - win.Update() - } + }) } // Converts pixel coordinates to cell coordinates diff --git a/cmd/view-set/main.go b/cmd/view-set/main.go new file mode 100644 index 0000000..0ce7211 --- /dev/null +++ b/cmd/view-set/main.go @@ -0,0 +1,160 @@ +package main + +import ( + "flag" + "image/color" + "log" + "os" + "path/filepath" + + "github.com/faiface/pixel" + // "github.com/faiface/pixel/imdraw" + "github.com/faiface/pixel/pixelgl" + "golang.org/x/image/colornames" + + "ur.gs/chaos-gate/internal/data" + "ur.gs/chaos-gate/internal/sets" + "ur.gs/chaos-gate/internal/ui" +) + +var ( + gamePath = flag.String("game-path", "./orig", "Path to a WH40K: Chaos Gate installation") + setFile = flag.String("set", "", "Path to a .set file, e.g. ./orig/Sets/map01.set") +) + +type env struct { + set *sets.MapSet + objects map[string]*data.Object +} + +type state struct { + env *env + + objIdx int + spriteIdx int + + zoom float64 + + cam pixel.Matrix + camPos pixel.Vec +} + +func main() { + flag.Parse() + + if *gamePath == "" || *setFile == "" { + flag.Usage() + os.Exit(1) + } + + mapSet, err := sets.LoadSet(*setFile) + if err != nil { + log.Fatalf("Couldn't load set file %s: %v", setFile, err) + } + + objects := make(map[string]*data.Object) + + for _, name := range mapSet.Palette { + objFile := filepath.Join(*gamePath, "Obj", name+".obj") + obj, err := data.LoadObject(objFile) + if err != nil { + log.Fatalf("Failed to load %s: %v", name, err) + } + + objects[filepath.Base(objFile)] = obj + } + + env := &env{objects: objects, set: mapSet} + + // The main thread now belongs to pixelgl + pixelgl.Run(env.run) +} + +func (e *env) run() { + win, err := ui.NewWindow("View Set: " + *setFile) + if err != nil { + log.Fatal("Couldn't create window: %v", err) + } + + pWin := win.PixelWindow + state := &state{ + env: e, + camPos: pixel.V(0, float64(-pWin.Bounds().Size().Y)), + zoom: 8.0, + } + + // For now, just try to display the various objects + // left + right to change object, up + down to change frame + win.Run(func() { + oldState := *state + state = state.runStep(pWin) + + if oldState != *state { + log.Printf( + "new state: numObj=%d object=%d (%s) numFrames=%d frame=%d", // FIXME: rename to sprite throughout + state.env.set.Count(), state.objIdx, state.env.set.Palette[state.objIdx], state.curObject().NumFrames, state.spriteIdx) + + state.present(pWin) + } + }) +} + +func (s *state) runStep(pWin *pixelgl.Window) *state { + newState := *s + newState.handleKeys(pWin) + + return &newState +} + +func (s *state) present(pWin *pixelgl.Window) { + obj := s.curObject() + frame := obj.Frames[s.spriteIdx] // FIXME: Rename Frame to Sprite throughout + + log.Printf("%#v", frame) + + pic := pixel.MakePictureData(pixel.R(0, 0, float64(frame.Width), float64(frame.Height))) + + // FIXME: how do I convert? Do I even have the right data here? + for i, b := range frame.PixelData { + pic.Pix[i] = color.RGBA{b, b, b, 255} + } + + sprite := pixel.NewSprite(pic, pic.Bounds()) + + pWin.Clear(colornames.White) + sprite.Draw(pWin, pixel.IM.Moved(pWin.Bounds().Center())) +} + +func (s *state) handleKeys(pWin *pixelgl.Window) { + if pWin.JustPressed(pixelgl.KeyLeft) { + if s.objIdx > 0 { + s.objIdx -= 1 + s.spriteIdx = 0 + } + } + + if pWin.JustPressed(pixelgl.KeyRight) { + if s.objIdx < s.env.set.Count()-1 { + s.objIdx += 1 + s.spriteIdx = 0 + } + } + + if pWin.JustPressed(pixelgl.KeyDown) { + if s.spriteIdx > 0 { + s.spriteIdx -= 1 + } + } + + if pWin.JustPressed(pixelgl.KeyUp) { + if s.spriteIdx < int(s.curObject().NumFrames)-1 { + s.spriteIdx += 1 + } + } +} + +func (s *state) curObject() *data.Object { + name := s.env.set.Palette[s.objIdx] + ".obj" + + return s.env.objects[name] // FIXME: we should use consistent naming! +} diff --git a/internal/ui/window.go b/internal/ui/window.go new file mode 100644 index 0000000..baeb514 --- /dev/null +++ b/internal/ui/window.go @@ -0,0 +1,46 @@ +package ui + +import ( + "flag" + + "github.com/faiface/pixel" + "github.com/faiface/pixel/pixelgl" +) + +var ( + winX = flag.Int("win-x", 1280, "width of the view-map window") + winY = flag.Int("win-y", 1024, "height of the view-map window") +) + +type Window struct { + PixelWindow *pixelgl.Window + ButtonEventHandlers map[pixelgl.Button][]func() +} + +// WARE! 0,0 is the *bottom left* of the window +// +// To invert things so 0,0 is the *top left*, apply the InvertY` matrix +func NewWindow(title string) (*Window, error) { + cfg := pixelgl.WindowConfig{ + Title: title, + Bounds: pixel.R(0, 0, float64(*winX), float64(*winY)), + VSync: true, + } + + pixelWindow, err := pixelgl.NewWindow(cfg) + if err != nil { + return nil, err + } + + return &Window{ + PixelWindow: pixelWindow, + }, nil +} + +// TODO: a stop or other cancellation mechanism +func (w *Window) Run(f func()) { + for !w.PixelWindow.Closed() { + f() + w.PixelWindow.Update() + } +}