Files
ordoor/cmd/view-map/main.go

209 lines
4.9 KiB
Go

package main
import (
"flag"
"log"
"math"
"os"
"time"
"github.com/faiface/pixel"
"github.com/faiface/pixel/imdraw"
"github.com/faiface/pixel/pixelgl"
"golang.org/x/image/colornames"
"ur.gs/chaos-gate/internal/maps"
)
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")
)
func main() {
flag.Parse()
if *gamePath == "" || *mapFile == "" || *txtFile == "" {
flag.Usage()
os.Exit(1)
}
gameMap, err := maps.LoadGameMapByFiles(*mapFile, *txtFile)
if err != nil {
log.Fatalf("Couldn't load map file: %v", err)
}
// The main thread now belongs to pixelgl
pixelgl.Run(func() { run(gameMap) })
}
type runState struct {
redraw bool
started time.Time
autoUpdate bool
gameMap *maps.GameMap
zoom float64
zIdx int
cellIdx int
}
func run(gameMap *maps.GameMap) {
cfg := pixelgl.WindowConfig{
Title: "View Map " + *mapFile,
Bounds: pixel.R(0, 0, float64(*winX), float64(*winY)),
VSync: true,
}
win, err := pixelgl.NewWindow(cfg)
if err != nil {
log.Fatal("Couldn't create window: %v", err)
}
state := &runState{
redraw: true,
started: time.Now(),
autoUpdate: true,
gameMap: gameMap,
zoom: 1.0,
}
for !win.Closed() {
if state.redraw {
log.Printf("z=%d cellIdx=%d", state.zIdx, state.cellIdx)
presentFull(win, state)
}
state = runStep(win, state)
win.Update()
}
}
// TODO: cut this down to showing just the viewport?
// The naive approach using gameMap.Width() / Height() cuts half the map off :/
func presentFull(win *pixelgl.Window, state *runState) {
gameMap := state.gameMap
center := win.Bounds().Center()
sz := win.Bounds().Size()
imd := imdraw.New(nil)
// Rotate everything 45' anticlockwise to get an isometric view with the
// lowest coordinates at the (now) top corner
imd.SetMatrix(pixel.IM.Rotated(center, -math.Pi/4))
xPerCell := sz.X / float64(maps.MaxWidth)
yPerCell := sz.Y / float64(maps.MaxLength)
for y := 0; y < maps.MaxLength; y++ {
for x := 0; x < maps.MaxWidth; x++ {
min := pixel.Vec{X: float64(x) * xPerCell, Y: float64(y) * yPerCell}
max := pixel.Vec{X: min.X + xPerCell, Y: min.Y + yPerCell}
cell := gameMap.Cells.At(int(x), int(y), int(state.zIdx))
// TODO: represent the state of the cell *sensibly*, using colour.
// Need to understand the contents better first, so for now optimize
// for exploration
//
// CellIndex=3 shows walls & elevation changes
imd.Color = makeColour(cell, state.cellIdx)
imd.Push(min, max)
imd.Rectangle(0.0)
}
}
cam := pixel.IM.Scaled(center, state.zoom)
win.SetMatrix(cam)
win.Clear(colornames.Black)
imd.Draw(win)
}
func makeColour(cell maps.Cell, colIdx int) pixel.RGBA {
return pixel.RGB(
float64(cell[colIdx]),
float64(cell[colIdx]),
float64(cell[colIdx]),
)
}
func runStep(win *pixelgl.Window, state *runState) *runState {
nextState := *state
nextState.redraw = false
// Enable / disable auto-update with the enter key
if win.JustPressed(pixelgl.KeyEnter) {
nextState.autoUpdate = !state.autoUpdate
log.Printf("autoUpdate=%v", nextState.autoUpdate)
if nextState.autoUpdate {
nextState.started = time.Now()
}
}
// Automatically cycle every second when auto-update is on
if nextState.autoUpdate && time.Now().Sub(nextState.started) > time.Second {
nextState.redraw = true
nextState.cellIdx = nextState.cellIdx + 1
if nextState.cellIdx >= maps.CellSize {
nextState.cellIdx = 0
nextState.zIdx = nextState.zIdx + 1
}
if nextState.zIdx >= maps.MaxHeight {
nextState.zIdx = 0
}
nextState.started = time.Now()
}
if win.JustPressed(pixelgl.KeyDown) {
if nextState.zIdx <= 0 {
log.Printf("z index is already at minimum")
} else {
nextState.redraw = true
nextState.zIdx = nextState.zIdx - 1
}
}
if win.JustPressed(pixelgl.KeyUp) {
if nextState.zIdx >= maps.MaxHeight-1 {
log.Printf("z index is already at maximum")
} else {
nextState.redraw = true
nextState.zIdx = nextState.zIdx + 1
}
}
if win.JustPressed(pixelgl.KeyLeft) {
if nextState.cellIdx <= 0 {
log.Printf("cell index is already at minimum")
} else {
nextState.redraw = true
nextState.cellIdx = nextState.cellIdx - 1
}
}
if win.JustPressed(pixelgl.KeyRight) {
if nextState.cellIdx >= maps.CellSize-1 {
log.Printf("cell index is already at maximum")
} else {
nextState.redraw = true
nextState.cellIdx = nextState.cellIdx + 1
}
}
// Zoom in and out with the mouse wheel
nextState.zoom *= math.Pow(1.2, win.MouseScroll().Y)
if nextState.zoom != state.zoom {
log.Printf("zoom=%.2f", nextState.zoom)
nextState.redraw = true
}
return &nextState
}