unit tests for ds1 (#4)

* ds1 refactor: added test fore some methods; put tests in right order

* ds1 refactor: unit tests for all methods

* ds1 refactor: fixed build errors

* ds1 refactor: lintfix

* ds1 refactor: fixed bug with SetWidth, SetHeight methods

* ds1 refactor: rename tile_record.go -> tile.go

* ds1 refactor: unit test for SetTiles

Co-authored-by: M. Sz <mszeptuch@protonmail.com>
This commit is contained in:
gucio321 2021-02-17 19:04:44 +01:00 committed by Dylan Knuth
parent 5e0e51d5e2
commit 194c1e467c
9 changed files with 404 additions and 196 deletions

View File

@ -1,6 +1,8 @@
package d2ds1
import (
"fmt"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2datautils"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2math"
@ -61,7 +63,8 @@ type DS1 struct {
npcIndexes []int
}
// Files returns a list of file path strings. These correspond to DT1 paths that this DS1 will use.
// Files returns a list of file path strings.
// These correspond to DT1 paths that this DS1 will use.
func (ds1 *DS1) Files() []string {
return ds1.files
}
@ -76,13 +79,16 @@ func (ds1 *DS1) AddFile(file string) {
}
// RemoveFile removes a file from the files slice
func (ds1 *DS1) RemoveFile(file string) {
func (ds1 *DS1) RemoveFile(file string) error {
for idx := range ds1.files {
if ds1.files[idx] == file {
ds1.files = append(ds1.files[:idx], ds1.files[idx+1:]...)
break
return nil
}
}
return fmt.Errorf("file %s not found", file)
}
// Objects returns the slice of objects found in this ds1
@ -209,6 +215,8 @@ func (ds1 *DS1) SetWidth(w int) {
ds1.tiles[rowIdx] = append(ds1.tiles[rowIdx], newTiles...)
}
}
ds1.width = int32(w)
}
// Height returns te ds1's height
@ -246,6 +254,8 @@ func (ds1 *DS1) SetHeight(h int) {
newRows[rowIdx][colIdx] = makeDefaultTile()
}
}
ds1.tiles = append(ds1.tiles, newRows...)
}
// if the ds1 has too many rows
@ -267,10 +277,11 @@ func (ds1 *DS1) Size() (w, h int) {
return int(ds1.width), int(ds1.height)
}
// SetSize sets the ds1's size (width,height)
func (ds1 *DS1) SetSize(w, h int) {
// setSize force sets the ds1's size (width,height)
func (ds1 *DS1) setSize(w, h int) {
ds1.SetWidth(w)
ds1.SetHeight(h)
ds1.width, ds1.height = int32(w), int32(h)
}
// Act returns the ds1's act
@ -348,7 +359,7 @@ func (ds1 *DS1) update() {
ds1.enforceAllTileLayersMatch()
ds1.updateLayerCounts()
ds1.SetSize(len(ds1.tiles[0]), len(ds1.tiles))
ds1.setSize(len(ds1.tiles[0]), len(ds1.tiles))
ds1.dirty = false
}

View File

@ -1,8 +1,11 @@
package d2ds1
import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"fmt"
"testing"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
)
func exampleDS1() *DS1 {
@ -34,49 +37,39 @@ func exampleDS1() *DS1 {
numberOfFloorLayers: 1,
numberOfShadowLayers: 1,
numberOfSubstitutionLayers: 1,
layerStreamTypes: []d2enum.LayerStreamType{
layerStreamTypes: []d2enum.LayerStreamType{
d2enum.LayerStreamWall1,
d2enum.LayerStreamOrientation1,
d2enum.LayerStreamFloor1,
d2enum.LayerStreamShadow,
},
npcIndexes: []int{},
npcIndexes: []int{},
}
}
func TestDS1_Act(t *testing.T) {
ds1 := exampleDS1()
// checks, if DS1 structure could be marshaled and unmarshaled
func testIfRestorable(ds1 *DS1) error {
var err error
if ds1.Act() != int(ds1.act) {
t.Error("unexpected value in example ds1")
}
data := ds1.Marshal()
_, err = LoadDS1(data)
return err
}
func TestDS1_AddFile(t *testing.T) {
ds1 := exampleDS1()
func TestDS1_Marshal(t *testing.T) {
a := exampleDS1()
numBefore := len(ds1.files)
bytes := a.Marshal()
ds1.AddFile("other.ds1")
numAfter := len(ds1.files)
if (numBefore+1) != numAfter {
t.Error("unexpected number of files in ds1")
b, err := LoadDS1(bytes)
if err != nil {
t.Error("could not load new ds1 from marshaled ds1 data")
return
}
}
func TestDS1_AddObject(t *testing.T) {
ds1 := exampleDS1()
numBefore := len(ds1.objects)
ds1.AddObject(Object{})
numAfter := len(ds1.objects)
if (numBefore+1) != numAfter {
t.Error("unexpected number of objects in ds1")
if b.width != a.width {
t.Error("new ds1 does not match original")
}
}
@ -92,27 +85,347 @@ func TestDS1_Files(t *testing.T) {
}
}
func TestDS1_AddFile(t *testing.T) {
ds1 := exampleDS1()
numBefore := len(ds1.files)
ds1.AddFile("other.ds1")
numAfter := len(ds1.files)
if (numBefore + 1) != numAfter {
t.Error("unexpected number of files in ds1")
}
if err := testIfRestorable(ds1); err != nil {
t.Errorf("unable to restore: %v", err)
}
}
func TestDS1_RemoveFile(t *testing.T) {
ds1 := exampleDS1()
numBefore := len(ds1.files)
err := ds1.RemoveFile("nonexistant file")
if err == nil {
t.Fatal("file 'nonexistant file' doesn't exist but ds1.RemoveFile doesn't return error")
}
if len(ds1.files) != numBefore {
t.Error("file removed when it should not have been")
}
filename := "c.ds1"
ds1.AddFile(filename)
if len(ds1.files) == numBefore {
t.Error("file not added when it should have been")
}
err = ds1.RemoveFile(filename)
if err != nil {
t.Error(err)
}
if len(ds1.files) != numBefore {
t.Error("file not removed when it should have been")
}
if err := testIfRestorable(ds1); err != nil {
t.Errorf("unable to restore: %v", err)
}
}
func TestDS1_Objects(t *testing.T) {
ds1 := exampleDS1()
objects := ds1.Objects()
for idx := range ds1.objects {
if !ds1.objects[idx].Equals(&objects[idx]) {
t.Error("unexpected object")
}
}
}
func TestDS1_AddObject(t *testing.T) {
ds1 := exampleDS1()
numBefore := len(ds1.objects)
ds1.AddObject(Object{})
numAfter := len(ds1.objects)
if (numBefore + 1) != numAfter {
t.Error("unexpected number of objects in ds1")
}
if err := testIfRestorable(ds1); err != nil {
t.Errorf("unable to restore: %v", err)
}
}
func TestDS1_RemoveObject(t *testing.T) {
ds1 := exampleDS1()
nice := 69420
obj := Object{
ID: nice,
}
ds1.AddObject(obj)
numBefore := len(ds1.objects)
ds1.RemoveObject(obj)
if len(ds1.objects) == numBefore {
t.Error("did not remove object when expected")
}
if err := testIfRestorable(ds1); err != nil {
t.Errorf("unable to restore: %v", err)
}
}
func TestDS1_Tiles(t *testing.T) {
ds1 := exampleDS1()
tiles := ds1.Tiles()
for y := range tiles {
for x := range tiles[y] {
if len(ds1.tiles[y][x].Floors) != len(tiles[y][x].Floors) {
t.Fatal("number of tile's floors returned by ds1.Tiles() isn't same as real number")
}
if ds1.tiles[y][x].Walls[0] != tiles[y][x].Walls[0] {
t.Fatal("wall record returned by ds1.Tiles isn't equal to real")
}
}
}
}
func TestDS1_SetTiles(t *testing.T) {
ds1 := exampleDS1()
exampleTile1 := Tile{
Floors: []FloorShadow{
{0, 0, 2, 3, 4, 55, 33, true, 999},
},
Shadows: []FloorShadow{
{2, 4, 5, 33, 6, 7, 0, false, 1024},
},
}
exampleTile2 := Tile{
Walls: []Wall{
{2, 3, 4, 5, 3, 2, 3, 0, 33, 99},
},
Shadows: []FloorShadow{
{2, 4, 5, 33, 6, 7, 0, false, 1024},
},
}
tiles := [][]Tile{{exampleTile1, exampleTile2}}
ds1.SetTiles(tiles)
if ds1.tiles[0][0].Floors[0] != exampleTile1.Floors[0] {
t.Fatal("unexpected tile was set")
}
if len(ds1.tiles[0][0].Walls) != len(exampleTile1.Walls) {
t.Fatal("unexpected tile was set")
}
if ds1.tiles[0][1].Walls[0] != exampleTile2.Walls[0] {
t.Fatal("unexpected tile was set")
}
if len(ds1.tiles[0][1].Walls) != len(exampleTile2.Walls) {
t.Fatal("unexpected tile was set")
}
}
func TestDS1_Tile(t *testing.T) {
ds1 := exampleDS1()
x, y := 1, 0
if ds1.tiles[y][x].Floors[0] != ds1.Tile(x, y).Floors[0] {
t.Fatal("ds1.Tile returned invalid value")
}
}
func TestDS1_SetTile(t *testing.T) {
ds1 := exampleDS1()
exampleTile := Tile{
Floors: []FloorShadow{
{5, 8, 9, 4, 3, 4, 2, true, 1024},
{8, 22, 7, 9, 6, 3, 0, false, 1024},
},
Walls: []Wall{
{2, 3, 4, 5, 3, 2, 3, 0, 33, 99},
},
Shadows: []FloorShadow{
{2, 44, 99, 2, 4, 3, 2, true, 933},
},
}
ds1.SetTile(0, 0, &exampleTile)
if ds1.tiles[0][0].Floors[0] != exampleTile.Floors[0] {
t.Fatal("unexpected tile was set")
}
if len(ds1.tiles[0][0].Walls) != len(exampleTile.Walls) {
t.Fatal("unexpected tile was set")
}
if err := testIfRestorable(ds1); err != nil {
t.Errorf("unable to restore: %v", err)
}
}
func TestDS1_Version(t *testing.T) {
ds1 := exampleDS1()
version := ds1.version
if version != int32(ds1.Version()) {
t.Fatal("version returned by ds1.Version() and real aren't equal")
}
}
func TestDS1_SetVersion(t *testing.T) {
ds1 := exampleDS1()
newVersion := 8
ds1.SetVersion(newVersion)
if newVersion != int(ds1.version) {
t.Fatal("ds1.SetVersion set version incorrectly")
}
if err := testIfRestorable(ds1); err != nil {
t.Errorf("unable to restore: %v", err)
}
}
func TestDS1_Width(t *testing.T) {
ds1 := exampleDS1()
if int(ds1.width) != ds1.Width() {
t.Error("unexpected width")
}
}
func TestDS1_SetWidth(t *testing.T) {
ds1 := exampleDS1()
var newWidth int32 = 4
ds1.SetWidth(int(newWidth))
if newWidth != ds1.width {
t.Fatal("unexpected width after set")
}
if err := testIfRestorable(ds1); err != nil {
t.Errorf("unable to restore: %v", err)
}
}
func TestDS1_Height(t *testing.T) {
ds1 := exampleDS1()
if int(ds1.height) != ds1.Height(){
if int(ds1.height) != ds1.Height() {
t.Error("unexpected height")
}
}
func TestDS1_Marshal(t *testing.T) {
a := exampleDS1()
func TestDS1_SetHeight(t *testing.T) {
ds1 := exampleDS1()
bytes := a.Marshal()
var newHeight int32 = 5
b, err := LoadDS1(bytes)
if err != nil {
t.Error("could not load new ds1 from marshalled ds1 data")
return
ds1.SetHeight(int(newHeight))
if newHeight != ds1.height {
fmt.Println(newHeight, ds1.height)
t.Fatal("unexpected heigth after set")
}
if b.width != a.width {
t.Error("new ds1 does not match original")
if err := testIfRestorable(ds1); err != nil {
t.Errorf("unable to restore: %v", err)
}
}
func TestDS1_Act(t *testing.T) {
ds1 := exampleDS1()
if ds1.Act() != int(ds1.act) {
t.Error("unexpected value in example ds1")
}
}
func TestDS1_SetAct(t *testing.T) {
ds1 := exampleDS1()
ds1.SetAct(-1)
if ds1.Act() < 0 {
t.Error("act cannot be less than 0")
}
nice := 69420
ds1.SetAct(nice)
if int(ds1.act) != nice {
t.Error("unexpected value for act")
}
if err := testIfRestorable(ds1); err != nil {
t.Errorf("unable to restore: %v", err)
}
}
func TestDS1_SubstitutionType(t *testing.T) {
ds1 := exampleDS1()
st := ds1.substitutionType
if int(st) != ds1.SubstitutionType() {
t.Fatal("unexpected substitution type returned")
}
}
func TestDS1_SetSubstitutionType(t *testing.T) {
ds1 := exampleDS1()
newST := 5
ds1.SetSubstitutionType(newST)
if ds1.substitutionType != int32(newST) {
t.Fatal("unexpected substitutionType was set")
}
}
func TestDS1_NumberOfWalls(t *testing.T) {
ds1 := exampleDS1()
if ds1.NumberOfWallLayers() != int(ds1.numberOfWallLayers) {
t.Error("unexpected number of wall layers")
}
}
@ -140,150 +453,34 @@ func TestDS1_NumberOfSubstitutionLayers(t *testing.T) {
}
}
func TestDS1_NumberOfWalls(t *testing.T) {
func TestDS1_SubstitutionGroups(t *testing.T) {
ds1 := exampleDS1()
if ds1.NumberOfWallLayers() != int(ds1.numberOfWallLayers) {
t.Error("unexpected number of wall layers")
}
}
sg := ds1.SubstitutionGroups()
func TestDS1_Objects(t *testing.T) {
ds1 := exampleDS1()
objects := ds1.Objects()
for idx := range ds1.objects {
if !ds1.objects[idx].Equals(&objects[idx]) {
t.Error("unexpected object")
for i := 0; i < len(ds1.substitutionGroups); i++ {
if sg[i] != ds1.substitutionGroups[i] {
t.Fatal("unexpected substitution group returned")
}
}
}
func TestDS1_RemoveFile(t *testing.T) {
ds1 := exampleDS1()
numBefore := len(ds1.files)
ds1.RemoveFile("nonexistant file")
if len(ds1.files) != numBefore {
t.Error("file removed when it should not have been")
}
filename := "c.ds1"
ds1.AddFile(filename)
if len(ds1.files) == numBefore {
t.Error("file not added when it should have been")
}
ds1.RemoveFile(filename)
if len(ds1.files) != numBefore {
t.Error("file not removed when it should have been")
}
}
func TestDS1_RemoveObject(t *testing.T) {
ds1 := exampleDS1()
nice := 69420
obj := Object{
ID: nice,
}
ds1.AddObject(obj)
numBefore := len(ds1.objects)
ds1.RemoveObject(obj)
if len(ds1.objects) == numBefore {
t.Error("did not remove object when expected")
}
}
func TestDS1_SetAct(t *testing.T) {
ds1 := exampleDS1()
ds1.SetAct(-1)
if ds1.Act() < 0 {
t.Error("act cannot be less than 0")
}
nice := 69420
ds1.SetAct(nice)
if int(ds1.act) != nice {
t.Error("unexpected value for act")
}
}
func TestDS1_SetHeight(t *testing.T) {
//ds1 := exampleDS1()
}
func TestDS1_SetSize(t *testing.T) {
//ds1 := exampleDS1()
}
func TestDS1_SetSubstitutionGroups(t *testing.T) {
//ds1 := exampleDS1()
}
ds1 := exampleDS1()
func TestDS1_SetSubstitutionType(t *testing.T) {
//ds1 := exampleDS1()
}
newGroup := []SubstitutionGroup{
{
TileX: 20,
TileY: 12,
WidthInTiles: 212,
HeightInTiles: 334,
Unknown: 1024,
},
}
func TestDS1_SetTile(t *testing.T) {
//ds1 := exampleDS1()
}
ds1.SetSubstitutionGroups(newGroup)
func TestDS1_SetTiles(t *testing.T) {
//ds1 := exampleDS1()
}
func TestDS1_SetVersion(t *testing.T) {
//ds1 := exampleDS1()
}
func TestDS1_SetWidth(t *testing.T) {
//ds1 := exampleDS1()
}
func TestDS1_Size(t *testing.T) {
//ds1 := exampleDS1()
}
func TestDS1_SubstitutionGroups(t *testing.T) {
//ds1 := exampleDS1()
}
func TestDS1_SubstitutionType(t *testing.T) {
//ds1 := exampleDS1()
}
func TestDS1_Tile(t *testing.T) {
//ds1 := exampleDS1()
}
func TestDS1_Tiles(t *testing.T) {
//ds1 := exampleDS1()
}
func TestDS1_Version(t *testing.T) {
//ds1 := exampleDS1()
}
func TestDS1_Width(t *testing.T) {
//ds1 := exampleDS1()
}
func TestLoadDS1(t *testing.T) {
//ds1 := exampleDS1()
if ds1.substitutionGroups[0] != newGroup[0] {
t.Fatal("unexpected substitution group added")
}
}

View File

@ -118,8 +118,8 @@ func (m *MapEngine) AddDS1(fileName string) {
m.Error(err.Error())
}
for idx := range ds1.files {
dt1File := ds1.files[idx]
for idx := range ds1.Files() {
dt1File := ds1.Files()[idx]
dt1File = strings.ToLower(dt1File)
dt1File = strings.ReplaceAll(dt1File, "c:", "") // Yes they did...
dt1File = strings.ReplaceAll(dt1File, ".tg1", ".dt1") // Yes they did...

View File

@ -8,7 +8,7 @@ import (
// MapTile is a tile placed on the map
type MapTile struct {
Components d2ds1.TileRecord
Components d2ds1.Tile
RegionType d2enum.RegionIdType
SubTiles [25]d2dt1.SubTileFlags
}

View File

@ -284,7 +284,7 @@ func (g *MapGenerator) generateWilderness1Contents(rect d2geom.Rectangle) {
for x := 0; x < rect.Width; x++ {
tile := g.engine.Tile(rect.Left+x, rect.Top+y)
tile.RegionType = d2enum.RegionIdType(levelDetails.LevelType)
tile.Components.Floors = []d2ds1.FloorShadowRecord{{Prop1: 1, Style: 0, Sequence: 0}} // wildernessGrass
tile.Components.Floors = []d2ds1.FloorShadow{{Prop1: 1, Style: 0, Sequence: 0}} // wildernessGrass
tile.PrepareTile(x, y, g.engine)
}
}

View File

@ -393,7 +393,7 @@ func (mr *MapRenderer) renderTilePass3(tile *d2mapengine.MapTile, target d2inter
}
}
func (mr *MapRenderer) renderFloor(tile d2ds1.FloorShadowRecord, target d2interface.Surface) {
func (mr *MapRenderer) renderFloor(tile d2ds1.FloorShadow, target d2interface.Surface) {
var img d2interface.Surface
if !tile.Animated {
img = mr.getImageCacheRecord(tile.Style, tile.Sequence, 0, tile.RandomIndex)
@ -415,7 +415,7 @@ func (mr *MapRenderer) renderFloor(tile d2ds1.FloorShadowRecord, target d2interf
target.Render(img)
}
func (mr *MapRenderer) renderWall(tile d2ds1.WallRecord, viewport *Viewport, target d2interface.Surface) {
func (mr *MapRenderer) renderWall(tile d2ds1.Wall, viewport *Viewport, target d2interface.Surface) {
img := mr.getImageCacheRecord(tile.Style, tile.Sequence, tile.Type, tile.RandomIndex)
if img == nil {
mr.Warningf("Render called on uncached wall {%v,%v,%v}", tile.Style, tile.Sequence, tile.Type)
@ -431,7 +431,7 @@ func (mr *MapRenderer) renderWall(tile d2ds1.WallRecord, viewport *Viewport, tar
target.Render(img)
}
func (mr *MapRenderer) renderShadow(tile d2ds1.FloorShadowRecord, target d2interface.Surface) {
func (mr *MapRenderer) renderShadow(tile d2ds1.FloorShadow, target d2interface.Surface) {
img := mr.getImageCacheRecord(tile.Style, tile.Sequence, 13, tile.RandomIndex)
if img == nil {
mr.Warningf("Render called on uncached shadow {%v,%v}", tile.Style, tile.Sequence)

View File

@ -53,7 +53,7 @@ func (mr *MapRenderer) generateTileCache() {
}
}
func (mr *MapRenderer) generateFloorCache(tile *d2ds1.FloorShadowRecord) {
func (mr *MapRenderer) generateFloorCache(tile *d2ds1.FloorShadow) {
tileOptions := mr.mapEngine.GetTiles(int(tile.Style), int(tile.Sequence), 0)
var tileData []*d2dt1.Tile
@ -110,7 +110,7 @@ func (mr *MapRenderer) generateFloorCache(tile *d2ds1.FloorShadowRecord) {
}
}
func (mr *MapRenderer) generateShadowCache(tile *d2ds1.FloorShadowRecord) {
func (mr *MapRenderer) generateShadowCache(tile *d2ds1.FloorShadow) {
tileOptions := mr.mapEngine.GetTiles(int(tile.Style), int(tile.Sequence), d2enum.TileShadow)
var tileData *d2dt1.Tile
@ -153,7 +153,7 @@ func (mr *MapRenderer) generateShadowCache(tile *d2ds1.FloorShadowRecord) {
mr.setImageCacheRecord(tile.Style, tile.Sequence, d2enum.TileShadow, tile.RandomIndex, image)
}
func (mr *MapRenderer) generateWallCache(tile *d2ds1.WallRecord) {
func (mr *MapRenderer) generateWallCache(tile *d2ds1.Wall) {
tileOptions := mr.mapEngine.GetTiles(int(tile.Style), int(tile.Sequence), tile.Type)
var tileData *d2dt1.Tile

View File

@ -31,7 +31,7 @@ type Stamp struct {
// Size returns the size of the stamp in tiles.
func (mr *Stamp) Size() d2geom.Size {
return d2geom.Size{Width: int(mr.ds1.Width), Height: int(mr.ds1.Height)}
return d2geom.Size{Width: mr.ds1.Width(), Height: mr.ds1.Height()}
}
// LevelPreset returns the level preset ID.
@ -55,8 +55,8 @@ func (mr *Stamp) RegionPath() string {
}
// Tile returns the tile at the given x and y tile coordinates.
func (mr *Stamp) Tile(x, y int) *d2ds1.TileRecord {
return &mr.ds1.Tiles[y][x]
func (mr *Stamp) Tile(x, y int) *d2ds1.Tile {
return mr.ds1.Tile(x, y)
}
// TileData returns the tile data for the tile with given style, sequence and type.
@ -75,9 +75,9 @@ func (mr *Stamp) TileData(style, sequence int32, tileType d2enum.TileType) *d2dt
func (mr *Stamp) Entities(tileOffsetX, tileOffsetY int) []d2interface.MapEntity {
entities := make([]d2interface.MapEntity, 0)
for _, object := range mr.ds1.Objects {
for _, object := range mr.ds1.Objects() {
if object.Type == int(d2enum.ObjectTypeCharacter) {
monPreset := mr.factory.asset.Records.Monster.Presets[mr.ds1.Act][object.ID]
monPreset := mr.factory.asset.Records.Monster.Presets[int32(mr.ds1.Act())][object.ID]
monstat := mr.factory.asset.Records.Monster.Stats[monPreset]
// If monstat is nil here it is a place_ type object, idk how to handle those yet.
// (See monpreset and monplace txts for reference)
@ -97,7 +97,7 @@ func (mr *Stamp) Entities(tileOffsetX, tileOffsetY int) []d2interface.MapEntity
if object.Type == int(d2enum.ObjectTypeItem) {
// For objects the DS1 ID to objectID is hardcoded in the game
// use the lookup table
lookup := mr.factory.asset.Records.LookupObject(int(mr.ds1.Act), object.Type, object.ID)
lookup := mr.factory.asset.Records.LookupObject(mr.ds1.Act(), object.Type, object.ID)
if lookup == nil {
continue