diff --git a/cmd/view-map/main.go b/cmd/view-map/main.go index 934ba89..d510778 100644 --- a/cmd/view-map/main.go +++ b/cmd/view-map/main.go @@ -2,6 +2,7 @@ package main import ( "flag" + "fmt" "log" "math" "os" @@ -112,26 +113,28 @@ func (e *env) run() { }) } -func (e *env) getSprite(palette []string, ref maps.ObjRef) *conv.Sprite { +func (e *env) getSprite(palette []string, ref maps.ObjRef) (*conv.Sprite, error) { + // There seems to be an active bit that hides many sins + if !ref.IsActive() { + return nil, nil + } + if ref.Index() >= len(palette) { - log.Printf("Palette too small: %v requested", ref.Index()) - return nil + return nil, fmt.Errorf("Palette too small: %v requested", ref.Index()) } name := palette[ref.Index()] obj := e.objects[name] if obj == nil { - log.Printf("Failed to find surface sprite %#v -> %q", ref, name) - return nil + return nil, fmt.Errorf("Failed to find surface sprite %#v -> %q", ref, name) } - if ref.Frame() >= len(obj.Sprites) { - log.Printf("Out-of-index sprite %v requested for %v", ref.Frame(), name) - return nil + if ref.Sprite() >= len(obj.Sprites) { + return nil, fmt.Errorf("Out-of-index sprite %v requested for %v", ref.Sprite(), name) } - return &obj.Sprites[ref.Frame()] + return &obj.Sprites[ref.Sprite()], nil } // TODO: build all the sprites in the set into a single spritesheet so we can @@ -171,14 +174,29 @@ func (s *state) renderCell(x, y, z int, pWin *pixelgl.Window) { cell := s.env.gameMap.Cells.At(x, y, z) - sprites = append(sprites, s.env.getSprite(s.env.set.Palette, cell.Surface)) + if spr, err := s.env.getSprite(s.env.set.Palette, cell.Surface); err != nil { + log.Printf("%v %v %v surface: %v", x, y, z, err) + } else { + sprites = append(sprites, spr) + } - sprites = append( - sprites, - s.env.getSprite(s.env.set.Palette, cell.Center), - s.env.getSprite(s.env.set.Palette, cell.Left), - s.env.getSprite(s.env.set.Palette, cell.Right), - ) + if spr, err := s.env.getSprite(s.env.set.Palette, cell.Center); err != nil { + log.Printf("%v %v %v center: %v", x, y, z, err) + } else { + sprites = append(sprites, spr) + } + + if spr, err := s.env.getSprite(s.env.set.Palette, cell.Left); err != nil { + log.Printf("%v %v %v left: %v", x, y, z, err) + } else { + sprites = append(sprites, spr) + } + + if spr, err := s.env.getSprite(s.env.set.Palette, cell.Right); err != nil { + log.Printf("%v %v %v right: %v", x, y, z, err) + } else { + sprites = append(sprites, spr) + } // Taking the Z index away *seems* to draw the object in the correct place. // FIXME: There are some artifacts, investigate more @@ -227,11 +245,14 @@ func (s *state) handleKeys(pWin *pixelgl.Window) { // FIXME: this suggests we should pass the next state into here and // modify it instead if pWin.JustPressed(pixelgl.MouseButton1) { - cell := s.pixToCell(s.cam.Unproject(pWin.MousePosition())) if s.zIdx != 0 { log.Printf("WARNING: z-index not yet taken into account") } - log.Printf("X=%v Y=%v, zIdx=%v", cell.X, cell.Y, s.zIdx) + + pos := s.pixToCell(s.cam.Unproject(pWin.MousePosition())) + cell := s.env.gameMap.Cells.At(int(pos.X), int(pos.Y), s.zIdx) + log.Printf("X=%v Y=%v, zIdx=%v", pos.X, pos.Y, s.zIdx) + log.Printf("Cell=%#v", cell) } if pWin.Pressed(pixelgl.KeyLeft) { diff --git a/doc/formats/maps.md b/doc/formats/maps.md index 7a9610e..0535248 100644 --- a/doc/formats/maps.md +++ b/doc/formats/maps.md @@ -400,15 +400,15 @@ Investigation has so far suggested the following: * 0x40: Animated object * `Cell[2]` hasn't been seen with a value > 0 yet * `Cell[3]` Object 0 (Surface) Area (Sets/*.set lookup) -* `Cell[4]` Object 0 (Surface) Sprite + ??? +* `Cell[4]` Object 0 (Surface) Sprite + active flag * Bottom bits encode the sprite (frame number in the .obj file) * 0x80 is set too. A flag? * `Cell[5]` Object 1 (Left) Area (Sets/*.set lookup) -* `Cell[6]` Object 1 (Surface) Sprite + ??? +* `Cell[6]` Object 1 (Surface) Sprite + active flag * `Cell[7]` Object 2 (Right) Area (Sets/*.set lookup) -* `Cell[6]` Object 2 (Right) Sprite + ??? +* `Cell[6]` Object 2 (Right) Sprite + active flag * `Cell[9]` Object 3 (Center) Area (Sets/*.set lookup) -* `Cell[10]` Object 3 (Right) Sprite + ??? +* `Cell[10]` Object 3 (Right) Sprite + active flag * `Cell[11]` all 255? * `Cell[12]` all 0? * `Cell[13]` all 0? @@ -436,6 +436,12 @@ So `CellIdx == 9` points to the center object's Area, looked up in the set file! It seems the area numbers are absolute indexes into the set, rather than having a new set of indices for each type. +We have to remove 0x80 from the sprite byte to get a valid reference. This seems +to act as an "active" flag - without it, Chapter01.MAP has a "ghost" of the +template close to the 0,0 boundary. Theory: the devs originally started at 0,0 +but then they decided to center smaller maps rather than being there, so the +layout got moved! + With this information, we can render a given Z index for a map quite easily, using the new `view-map` binary. It draws the four objects for every cell, and gives results like this: diff --git a/internal/conv/object.go b/internal/conv/object.go index 07c9380..65f13d0 100644 --- a/internal/conv/object.go +++ b/internal/conv/object.go @@ -52,7 +52,7 @@ var transparent = color.RGBA{0, 0, 0, 0} func spriteToPic(name string, idx int, sprite *data.Sprite) *pixel.PictureData { pic := pixel.MakePictureData(pixel.R(float64(0), float64(0), float64(sprite.Width), float64(sprite.Height))) - log.Printf("%v %v: width=%v height=%v", name, idx, sprite.Width, sprite.Height) + //log.Printf("%v %v: width=%v height=%v", name, idx, sprite.Width, sprite.Height) for y := 0; y < int(sprite.Height); y++ { // Start with all bytes transparent @@ -61,7 +61,7 @@ func spriteToPic(name string, idx int, sprite *data.Sprite) *pixel.PictureData { } row := sprite.Rows[y] - log.Printf("%#v", row) + //log.Printf("%#v", row) pixels := row[0 : len(row)-1] // Strip off the record separator (0x00) // Not really clear on what this does yet. Aligned with sprite width in @@ -79,7 +79,7 @@ func spriteToPic(name string, idx int, sprite *data.Sprite) *pixel.PictureData { // Do nothing if we're out of pixels if u0 == 0x80 { - log.Printf("Handling 0x80: %#v", pixels) + //log.Printf("Handling 0x80: %#v", pixels) xOffset = int(pixels[0]) pixels = pixels[1:len(pixels)] @@ -98,10 +98,10 @@ func spriteToPic(name string, idx int, sprite *data.Sprite) *pixel.PictureData { } } - log.Printf( - "%v %d: len(row)=%v, len(pixels)=%v sprWidth=%v u0=%v xOffset=%v", - name, idx, len(row), len(pixels), sprite.Width, u0, xOffset, - ) + //log.Printf( + // "%v %d: len(row)=%v, len(pixels)=%v sprWidth=%v u0=%v xOffset=%v", + // name, idx, len(row), len(pixels), sprite.Width, u0, xOffset, + //) for x, b := range pixels { vec := pixel.V(float64(xOffset+x), float64(y)) diff --git a/internal/maps/maps.go b/internal/maps/maps.go index d788f53..f28bfc5 100644 --- a/internal/maps/maps.go +++ b/internal/maps/maps.go @@ -69,8 +69,8 @@ func (h Header) MapSetFilename() string { } type ObjRef struct { - AreaByte byte - FrameAndUnknownByte byte + AreaByte byte + SpriteAndFlagByte byte } // The index into a set palette to retrieve the object @@ -78,8 +78,14 @@ func (o ObjRef) Index() int { return int(o.AreaByte) } -func (o ObjRef) Frame() int { - return int(o.FrameAndUnknownByte - 0x80) +func (o ObjRef) Sprite() int { + // The top bit seems to be a flag of some kind + return int(o.SpriteAndFlagByte & 0x7f) +} + +// The top bit seems to say whether we should draw or not. +func (o ObjRef) IsActive() bool { + return (o.SpriteAndFlagByte & 0x80) == 0x80 } type Cell struct { @@ -108,19 +114,19 @@ func (c *Cell) At(n int) byte { case 3: return c.Surface.AreaByte case 4: - return c.Surface.FrameAndUnknownByte + return c.Surface.SpriteAndFlagByte case 5: return c.Left.AreaByte case 6: - return c.Left.FrameAndUnknownByte + return c.Left.SpriteAndFlagByte case 7: return c.Right.AreaByte case 8: - return c.Right.FrameAndUnknownByte + return c.Right.SpriteAndFlagByte case 9: return c.Center.AreaByte case 10: - return c.Center.FrameAndUnknownByte + return c.Center.SpriteAndFlagByte case 11: return c.Unknown11 case 12: @@ -139,7 +145,6 @@ func (c *Cell) At(n int) byte { // Cells is always a fixed size; use At to get a cell according to x,y,z type Cells []Cell -// FIXME: Ordering may be incorrect? I assume z,y,x for now... func (c Cells) At(x, y, z int) Cell { return c[(z*MaxLength*MaxWidth)+(y*MaxWidth)+x] }