This commit is contained in:
gravestench 2021-03-01 02:03:03 -08:00 committed by Dylan Knuth
parent a33117fb56
commit 5dfca5e9f3
22 changed files with 1410 additions and 1597 deletions

View File

@ -1,20 +0,0 @@
package d2enum
// LayerStreamType represents a layer stream type
type LayerStreamType int
// Layer stream types
const (
LayerStreamWall1 LayerStreamType = iota
LayerStreamWall2
LayerStreamWall3
LayerStreamWall4
LayerStreamOrientation1
LayerStreamOrientation2
LayerStreamOrientation3
LayerStreamOrientation4
LayerStreamFloor1
LayerStreamFloor2
LayerStreamShadow
LayerStreamSubstitute
)

View File

@ -1,2 +1,2 @@
// Package d2ds1 provides functionality for loading/processing DS1 files
// Package d2ds1 provides functionality for loading/processing DS1 Files
package d2ds1

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,432 @@
package d2ds1
const (
maxWallLayers = 4
maxOrientationLayers = 4
maxFloorLayers = 2
maxShadowLayers = 1
maxSubstitutionLayers = 1
)
type layerGroupType int
const (
floorLayerGroup layerGroupType = iota
wallLayerGroup
orientationLayerGroup
shadowLayerGroup
substitutionLayerGroup
)
type layerGroup []*layer
type ds1Layers struct {
width, height int
Floors layerGroup
Walls layerGroup
Orientations layerGroup
Shadows layerGroup
Substitutions layerGroup
}
func (l *ds1Layers) ensureInit() {
if l.Floors == nil {
l.Floors = make(layerGroup, 0)
}
if l.Walls == nil {
l.Walls = make(layerGroup, 0)
}
if l.Orientations == nil {
l.Orientations = make(layerGroup, 0)
}
if l.Shadows == nil {
l.Shadows = make(layerGroup, 0)
}
if l.Substitutions == nil {
l.Substitutions = make(layerGroup, 0)
}
}
// removes nil layers from all layer groups
func (l *ds1Layers) cull() {
l.cullNilLayers(floorLayerGroup)
l.cullNilLayers(wallLayerGroup)
l.cullNilLayers(orientationLayerGroup)
l.cullNilLayers(shadowLayerGroup)
l.cullNilLayers(substitutionLayerGroup)
}
// removes nil layers of given layer group type
func (l *ds1Layers) cullNilLayers(t layerGroupType) {
var group *layerGroup
switch t {
case floorLayerGroup:
group = &l.Floors
case wallLayerGroup:
group = &l.Walls
case orientationLayerGroup:
group = &l.Orientations
case shadowLayerGroup:
group = &l.Shadows
case substitutionLayerGroup:
group = &l.Substitutions
default:
return
}
// from last to first layer, remove first encountered nil layer and restart the culling procedure.
// exit culling procedure when no nil layers are found in entire group.
culling:
for {
for idx := len(*group) - 1; idx > 0; idx-- {
if (*group)[idx] == nil {
*group = (*group)[:idx]
continue culling
}
}
break culling // encountered no new nil layers
}
}
func (l *ds1Layers) Size() (w, h int) {
l.ensureInit()
l.cull()
return l.width, l.height
}
func (l *ds1Layers) SetSize(w, h int) {
l.width, l.height = w, h
l.enforceSize(floorLayerGroup)
l.enforceSize(wallLayerGroup)
l.enforceSize(orientationLayerGroup)
l.enforceSize(shadowLayerGroup)
l.enforceSize(substitutionLayerGroup)
}
func (l *ds1Layers) enforceSize(t layerGroupType) {
l.ensureInit()
l.cull()
var group *layerGroup
switch t {
case floorLayerGroup:
group = &l.Floors
case wallLayerGroup:
group = &l.Walls
case orientationLayerGroup:
group = &l.Orientations
case shadowLayerGroup:
group = &l.Shadows
case substitutionLayerGroup:
group = &l.Substitutions
default:
return
}
for idx := range *group {
(*group)[idx].SetSize(l.width, l.height)
}
}
func (l *ds1Layers) Width() int {
w, _ := l.Size()
return w
}
func (l *ds1Layers) SetWidth(w int) {
l.SetSize(w, l.height)
}
func (l *ds1Layers) Height() int {
_, h := l.Size()
return h
}
func (l *ds1Layers) SetHeight(h int) {
l.SetSize(l.width, h)
}
// generic push func for all layer types
func (l *ds1Layers) push(t layerGroupType, layer *layer) {
l.ensureInit()
l.cull()
var group *layerGroup
var max int
switch t {
case floorLayerGroup:
group = &l.Floors
max = maxFloorLayers
case wallLayerGroup:
group = &l.Walls
max = maxWallLayers
case orientationLayerGroup:
group = &l.Orientations
max = maxOrientationLayers
case shadowLayerGroup:
group = &l.Shadows
max = maxShadowLayers
case substitutionLayerGroup:
group = &l.Substitutions
max = maxSubstitutionLayers
default:
return
}
if len(*group) < max {
*group = append(*group, layer)
}
}
// generic pop func for all layer types
func (l *ds1Layers) pop(t layerGroupType) *layer {
l.ensureInit()
l.cull()
var group *layerGroup
var theLayer *layer
switch t {
case floorLayerGroup:
group = &l.Floors
case wallLayerGroup:
group = &l.Walls
case orientationLayerGroup:
group = &l.Orientations
case shadowLayerGroup:
group = &l.Shadows
case substitutionLayerGroup:
group = &l.Substitutions
default:
return nil
}
// remove last layer of slice and return it
if len(*group) > 0 {
lastIdx := len(*group) - 1
theLayer = (*group)[lastIdx]
*group = (*group)[:lastIdx]
return theLayer
}
return nil
}
func (l *ds1Layers) get(t layerGroupType, idx int) *layer {
l.ensureInit()
l.cull()
var group *layerGroup
switch t {
case floorLayerGroup:
group = &l.Floors
case wallLayerGroup:
group = &l.Walls
case orientationLayerGroup:
group = &l.Orientations
case shadowLayerGroup:
group = &l.Shadows
case substitutionLayerGroup:
group = &l.Substitutions
default:
return nil
}
if idx >= len(*group) || idx < 0 {
return nil
}
return (*group)[idx]
}
func (l *ds1Layers) insert(t layerGroupType, idx int, newLayer *layer) {
l.ensureInit()
l.cull()
if newLayer == nil {
return
}
var group layerGroup
switch t {
case floorLayerGroup:
group = l.Floors
case wallLayerGroup:
group = l.Walls
case orientationLayerGroup:
group = l.Orientations
case shadowLayerGroup:
group = l.Shadows
case substitutionLayerGroup:
group = l.Substitutions
default:
return
}
if len(group) == 0 {
group = append(group, newLayer) // nolint:staticcheck // we possibly use group later
return
}
if idx > len(group)-1 {
idx = len(group) - 1
}
// example:
// suppose
// idx=1
// newLayer=c
// existing layerGroup is [a, b]
group = append(group, group[idx:]...) // [a, b] becomes [a, b, b]
group[idx] = newLayer // [a, b, b] becomes [a, c, b]
}
func (l *ds1Layers) delete(t layerGroupType, idx int) {
l.ensureInit()
l.cull()
var group layerGroup
switch t {
case floorLayerGroup:
group = l.Floors
case wallLayerGroup:
group = l.Walls
case orientationLayerGroup:
group = l.Orientations
case shadowLayerGroup:
group = l.Shadows
case substitutionLayerGroup:
group = l.Substitutions
default:
return
}
if idx >= len(group) || idx < 0 {
return
}
group[idx] = nil
l.cull()
}
func (l *ds1Layers) GetFloor(idx int) *layer {
return l.get(floorLayerGroup, idx)
}
func (l *ds1Layers) PushFloor(floor *layer) *ds1Layers {
l.push(floorLayerGroup, floor)
return l
}
func (l *ds1Layers) PopFloor() *layer {
return l.pop(floorLayerGroup)
}
func (l *ds1Layers) InsertFloor(idx int, newFloor *layer) {
l.insert(floorLayerGroup, idx, newFloor)
}
func (l *ds1Layers) DeleteFloor(idx int) {
l.delete(floorLayerGroup, idx)
}
func (l *ds1Layers) GetWall(idx int) *layer {
return l.get(wallLayerGroup, idx)
}
func (l *ds1Layers) PushWall(wall *layer) *ds1Layers {
l.push(wallLayerGroup, wall)
return l
}
func (l *ds1Layers) PopWall() *layer {
return l.pop(wallLayerGroup)
}
func (l *ds1Layers) InsertWall(idx int, newWall *layer) {
l.insert(wallLayerGroup, idx, newWall)
}
func (l *ds1Layers) DeleteWall(idx int) {
l.delete(wallLayerGroup, idx)
}
func (l *ds1Layers) GetOrientation(idx int) *layer {
return l.get(orientationLayerGroup, idx)
}
func (l *ds1Layers) PushOrientation(orientation *layer) *ds1Layers {
l.push(orientationLayerGroup, orientation)
return l
}
func (l *ds1Layers) PopOrientation() *layer {
return l.pop(orientationLayerGroup)
}
func (l *ds1Layers) InsertOrientation(idx int, newOrientation *layer) {
l.insert(orientationLayerGroup, idx, newOrientation)
}
func (l *ds1Layers) DeleteOrientation(idx int) {
l.delete(orientationLayerGroup, idx)
}
func (l *ds1Layers) GetShadow(idx int) *layer {
return l.get(shadowLayerGroup, idx)
}
func (l *ds1Layers) PushShadow(shadow *layer) *ds1Layers {
l.push(shadowLayerGroup, shadow)
return l
}
func (l *ds1Layers) PopShadow() *layer {
return l.pop(shadowLayerGroup)
}
func (l *ds1Layers) InsertShadow(idx int, newShadow *layer) {
l.insert(shadowLayerGroup, idx, newShadow)
}
func (l *ds1Layers) DeleteShadow(idx int) {
l.delete(shadowLayerGroup, idx)
}
func (l *ds1Layers) GetSubstitution(idx int) *layer {
return l.get(substitutionLayerGroup, idx)
}
func (l *ds1Layers) PushSubstitution(sub *layer) *ds1Layers {
l.push(substitutionLayerGroup, sub)
return l
}
func (l *ds1Layers) PopSubstitution() *layer {
return l.pop(substitutionLayerGroup)
}
func (l *ds1Layers) InsertSubstitution(idx int, newSubstitution *layer) {
l.insert(shadowLayerGroup, idx, newSubstitution)
}
func (l *ds1Layers) DeleteSubstitution(idx int) {
l.delete(shadowLayerGroup, idx)
}

View File

@ -0,0 +1,152 @@
package d2ds1
import (
"testing"
)
func Test_ds1Layers_DeleteFloor(t *testing.T) {}
func Test_ds1Layers_DeleteOrientation(t *testing.T) {}
func Test_ds1Layers_DeleteShadow(t *testing.T) {}
func Test_ds1Layers_DeleteSubstitution(t *testing.T) {}
func Test_ds1Layers_DeleteWall(t *testing.T) {}
func Test_ds1Layers_GetFloor(t *testing.T) {}
func Test_ds1Layers_GetOrientation(t *testing.T) {}
func Test_ds1Layers_GetShadow(t *testing.T) {}
func Test_ds1Layers_GetSubstitution(t *testing.T) {}
func Test_ds1Layers_GetWall(t *testing.T) {}
func Test_ds1Layers_InsertFloor(t *testing.T) {}
func Test_ds1Layers_InsertOrientation(t *testing.T) {}
func Test_ds1Layers_InsertShadow(t *testing.T) {}
func Test_ds1Layers_InsertSubstitution(t *testing.T) {}
func Test_ds1Layers_InsertWall(t *testing.T) {}
func Test_ds1Layers_PopFloor(t *testing.T) {}
func Test_ds1Layers_PopOrientation(t *testing.T) {}
func Test_ds1Layers_PopShadow(t *testing.T) {}
func Test_ds1Layers_PopSubstitution(t *testing.T) {}
func Test_ds1Layers_PopWall(t *testing.T) {}
func Test_ds1Layers_Push(t *testing.T) {
t.Run("Floor", func(t *testing.T) {
test_ds1Layers_PushLayer(floorLayerGroup, t)
})
t.Run("Wall", func(t *testing.T) {
test_ds1Layers_PushLayer(wallLayerGroup, t)
})
t.Run("Orientation", func(t *testing.T) {
test_ds1Layers_PushLayer(orientationLayerGroup, t)
})
t.Run("Shadow", func(t *testing.T) {
test_ds1Layers_PushLayer(shadowLayerGroup, t)
})
t.Run("Substitution", func(t *testing.T) {
test_ds1Layers_PushLayer(substitutionLayerGroup, t)
})
}
// for all layer types, the test is the same
// when we push a layer, we expect an increment, and when we push a bunch of times,
// we expect to never exceed the max. we also expect to be able to retrieve a non-nil
// layer after we push.
func test_ds1Layers_PushLayer(lt layerGroupType, t *testing.T) {
layers := &ds1Layers{}
// we need to set up some shit to handle the test in a generic way
var push func()
var get func(idx int) *layer
var max int
var group *layerGroup
check := func(expected int) {
actual := len(*group)
got := get(expected - 1)
if actual != expected {
t.Fatalf("unexpected number of layers: expected %d, got %d", expected, actual)
}
if got == nil {
t.Fatal("got nil layer")
}
}
switch lt {
case floorLayerGroup:
push = func() { layers.PushFloor(&layer{}) }
get = layers.GetFloor
max = maxFloorLayers
group = &layers.Floors
case wallLayerGroup:
push = func() { layers.PushWall(&layer{}) }
get = layers.GetWall
max = maxWallLayers
group = &layers.Walls
case orientationLayerGroup:
push = func() { layers.PushOrientation(&layer{}) }
get = layers.GetOrientation
max = maxOrientationLayers
group = &layers.Orientations
case shadowLayerGroup:
push = func() { layers.PushShadow(&layer{}) }
get = layers.GetShadow
max = maxShadowLayers
group = &layers.Shadows
case substitutionLayerGroup:
push = func() { layers.PushSubstitution(&layer{}) }
get = layers.GetSubstitution
max = maxSubstitutionLayers
group = &layers.Substitutions
default:
t.Fatal("unknown layer type given")
}
// push one time, we expect a single layer to exist
push()
check(1)
// if we push a bunch of times, we expect to not exceed the max
push()
push()
push()
push()
push()
push()
push()
push()
push()
push()
check(max)
}
func test_ds1Layers_PopLayer(lt layerGroupType, t *testing.T) {}
func test_ds1Layers_InsertLayer(lt layerGroupType, t *testing.T) {}
func test_ds1Layers_DeleteLayer(lt layerGroupType, t *testing.T) {}
func test_ds1Layers_GetLayer(lt layerGroupType, t *testing.T) {}

View File

@ -1,665 +1 @@
package d2ds1
import (
"testing"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2path"
)
func exampleDS1() *DS1 {
return &DS1{
files: []string{"a.dt1", "b.dt1"},
objects: []Object{
{0, 0, 0, 0, 0, nil},
{0, 1, 0, 0, 0, []d2path.Path{{}}},
{0, 2, 0, 0, 0, nil},
{0, 3, 0, 0, 0, nil},
},
tiles: [][]Tile{ // 2x2
{
Tile{
[]Floor{{}},
[]Wall{{}},
[]Shadow{{}},
[]Substitution{{}},
},
Tile{
[]Floor{{}},
[]Wall{{}},
[]Shadow{{}},
[]Substitution{{}},
},
},
{
Tile{
[]Floor{{}},
[]Wall{{}},
[]Shadow{{}},
[]Substitution{{}},
},
Tile{
[]Floor{{}},
[]Wall{{}},
[]Shadow{{}},
[]Substitution{{}},
},
},
},
substitutionGroups: nil,
version: 17,
width: 2,
height: 2,
act: 1,
substitutionType: 0,
numberOfWallLayers: 1,
numberOfFloorLayers: 1,
numberOfShadowLayers: 1,
numberOfSubstitutionLayers: 1,
}
}
// checks, if DS1 structure could be marshaled and unmarshaled
func testIfRestorable(ds1 *DS1, test func(ds1 *DS1)) error {
test(ds1)
var err error
data := ds1.Marshal()
newDS1, err := LoadDS1(data)
if err != nil {
return err
}
test(newDS1)
return nil
}
func TestDS1_Marshal(t *testing.T) {
a := exampleDS1()
bytes := a.Marshal()
b, err := LoadDS1(bytes)
if err != nil {
t.Error("could not load new ds1 from marshaled ds1 data")
return
}
if b.width != a.width {
t.Error("new ds1 does not match original")
}
if len(b.tiles) != len(a.tiles) {
t.Error("new ds1 does not batch original")
}
}
func TestDS1_Files(t *testing.T) {
ds1 := exampleDS1()
files := ds1.Files()
for idx := range files {
if ds1.files[idx] != files[idx] {
t.Error("unexpected files from ds1")
}
}
}
func TestDS1_AddFile(t *testing.T) {
ds1 := exampleDS1()
numBefore := len(ds1.files)
ds1.AddFile("other.ds1")
numAfter := len(ds1.files)
test := func(ds1 *DS1) {
if (numBefore + 1) != numAfter {
t.Error("unexpected number of files in ds1")
}
}
if err := testIfRestorable(ds1, test); err != nil {
t.Errorf("unable to restore: %v", err)
}
}
func TestDS1_RemoveFile(t *testing.T) {
ds1 := exampleDS1()
test := func(ds1 *DS1) {
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, test); 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)
test := func(ds1 *DS1) {
if (numBefore + 1) != numAfter {
t.Error("unexpected number of objects in ds1")
}
}
if err := testIfRestorable(ds1, test); 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)
test := func(ds1 *DS1) {
if len(ds1.objects) == numBefore {
t.Error("did not remove object when expected")
}
}
if err := testIfRestorable(ds1, test); 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{
{8, 7, 2, 3, 4, 55, 33, true, 999},
},
Walls: []Wall{
{2, 3, 4, 5, 3, 2, 3, 0, 33, 99},
{2, 3, 4, 5, 3, 2, 3, 0, 33, 99},
},
Shadows: []floorShadow{
{2, 4, 5, 33, 6, 7, 0, false, 1024},
},
}
exampleTile2 := Tile{
Floors: []floorShadow{
{9, 9, 2, 3, 4, 55, 33, true, 999},
{9, 8, 2, 3, 102, 55, 33, true, 999},
},
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}, {exampleTile2, exampleTile1}}
ds1.SetTiles(tiles)
test := func(ds1 *DS1) {
if ds1.tiles[0][0].Floors[0].Prop1 != exampleTile1.Floors[0].Prop1 {
t.Fatal("1,unexpected tile was set")
}
if len(ds1.tiles[0][0].Walls) != len(exampleTile1.Walls) {
t.Fatal("2,unexpected tile was set")
}
if ds1.tiles[0][1].Walls[0].Style != exampleTile2.Walls[0].Style {
t.Fatal("3,unexpected tile was set")
}
if len(ds1.tiles[0][0].Walls) != len(exampleTile1.Walls) {
t.Fatal("4,unexpected tile was set")
}
}
if err := testIfRestorable(ds1, test); err != nil {
t.Errorf("unable to restore: %v", err)
}
}
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, 0, 0, true, 1024},
},
Walls: []Wall{
{2, 0, 4, 5, 3, 2, 3, 0, 33, 99},
{5, 8, 9, 4, 3, 0, 0, 124, 221, 12},
},
Shadows: []floorShadow{
{2, 44, 99, 2, 4, 3, 2, true, 933},
},
}
ds1.SetTile(0, 0, &exampleTile)
test := func(ds1 *DS1) {
if ds1.tiles[0][0].Floors[0].Prop1 != exampleTile.Floors[0].Prop1 {
t.Fatal("unexpected tile was set")
}
if ds1.tiles[0][0].Walls[0].Zero != exampleTile.Walls[0].Zero {
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, test); 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)
test := func(ds1 *DS1) {
if newVersion != int(ds1.version) {
t.Fatal("ds1.SetVersion set version incorrectly")
}
}
if err := testIfRestorable(ds1, test); 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))
test := func(ds1 *DS1) {
if newWidth != ds1.width {
t.Fatal("unexpected width after set")
}
}
if err := testIfRestorable(ds1, test); err != nil {
t.Errorf("unable to restore: %v", err)
}
}
func TestDS1_Height(t *testing.T) {
ds1 := exampleDS1()
if int(ds1.height) != ds1.Height() {
t.Error("unexpected height")
}
}
func TestDS1_SetHeight(t *testing.T) {
ds1 := exampleDS1()
var newHeight int32 = 5
ds1.SetHeight(int(newHeight))
test := func(ds1 *DS1) {
if newHeight != ds1.height {
t.Fatal("unexpected heigth after set")
}
}
if err := testIfRestorable(ds1, test); 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 := 5
ds1.SetAct(nice)
test := func(ds1 *DS1) {
if int(ds1.act) != nice {
t.Error("unexpected value for act")
}
}
if err := testIfRestorable(ds1, test); 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")
}
}
func TestDS1_SetNumberOfWalls(t *testing.T) {
var err error
ds1 := exampleDS1()
newNumber := int32(4)
err = ds1.SetNumberOfWallLayers(newNumber)
if err != nil {
t.Errorf("error setting number of walls: %v", err)
}
test := func(ds1 *DS1) {
if len(ds1.tiles[0][0].Walls) != int(newNumber) {
t.Fatal("unexpected walls length set")
}
if ds1.NumberOfWallLayers() != int(newNumber) {
t.Fatal("unexpected walls length set")
}
}
if err = testIfRestorable(ds1, test); err != nil {
t.Errorf("unable to restore: %v", err)
}
newNumber = 1
err = ds1.SetNumberOfWallLayers(newNumber)
if err != nil {
t.Errorf("error setting number of walls: %v", err)
}
test = func(ds1 *DS1) {
if len(ds1.tiles[0][0].Walls) != int(newNumber) {
t.Fatal("unexpected walls length set")
}
if ds1.NumberOfWallLayers() != int(newNumber) {
t.Fatal("unexpected walls length set")
}
}
if err = testIfRestorable(ds1, test); err != nil {
t.Errorf("unable to restore: %v", err)
}
}
func TestDS1_NumberOfFloors(t *testing.T) {
ds1 := exampleDS1()
if ds1.NumberOfFloorLayers() != int(ds1.numberOfFloorLayers) {
t.Error("unexpected number of floor layers")
}
}
func TestDS1_SetNumberOfFloors(t *testing.T) {
var err error
ds1 := exampleDS1()
newNumber := int32(2)
err = ds1.SetNumberOfFloorLayers(newNumber)
if err != nil {
t.Errorf("error setting number of floors: %v", err)
}
test := func(ds1 *DS1) {
if len(ds1.tiles[0][0].Floors) != int(newNumber) {
t.Fatal("unexpected floors length set")
}
if ds1.numberOfFloorLayers != newNumber {
t.Fatal("unexpected floors length set")
}
}
if err = testIfRestorable(ds1, test); err != nil {
t.Errorf("unable to restore: %v", err)
}
newNumber = 1
err = ds1.SetNumberOfFloorLayers(newNumber)
if err != nil {
t.Errorf("error setting number of floors: %v", err)
}
test = func(ds1 *DS1) {
if len(ds1.tiles[0][0].Floors) != int(newNumber) {
t.Fatal("unexpected floors length set")
}
if ds1.numberOfFloorLayers != newNumber {
t.Fatal("unexpected floors length set")
}
}
if err = testIfRestorable(ds1, test); err != nil {
t.Errorf("unable to restore: %v", err)
}
}
func TestDS1_NumberOfShadowLayers(t *testing.T) {
ds1 := exampleDS1()
if ds1.NumberOfShadowLayers() != int(ds1.numberOfShadowLayers) {
t.Error("unexpected number of shadow layers")
}
}
func TestDS1_NumberOfSubstitutionLayers(t *testing.T) {
ds1 := exampleDS1()
if ds1.NumberOfSubstitutionLayers() != int(ds1.numberOfSubstitutionLayers) {
t.Error("unexpected number of substitution layers")
}
}
func TestDS1_SubstitutionGroups(t *testing.T) {
ds1 := exampleDS1()
sg := ds1.SubstitutionGroups()
for i := 0; i < len(ds1.substitutionGroups); i++ {
if sg[i] != ds1.substitutionGroups[i] {
t.Fatal("unexpected substitution group returned")
}
}
}
func TestDS1_SetSubstitutionGroups(t *testing.T) {
ds1 := exampleDS1()
newGroup := []SubstitutionGroup{
{
TileX: 20,
TileY: 12,
WidthInTiles: 212,
HeightInTiles: 334,
Unknown: 1024,
},
}
ds1.SetSubstitutionGroups(newGroup)
if ds1.substitutionGroups[0] != newGroup[0] {
t.Fatal("unexpected substitution group added")
}
}
func TestDS1_GetStreamLayerTypes(t *testing.T) {
ds1 := exampleDS1()
lst := []d2enum.LayerStreamType{
d2enum.LayerStreamWall1,
d2enum.LayerStreamOrientation1,
d2enum.LayerStreamFloor1,
d2enum.LayerStreamShadow,
d2enum.LayerStreamSubstitute,
}
layerStreamTypes := ds1.GetStreamLayerTypes()
if len(lst) != len(layerStreamTypes) {
t.Fatal("unexpected length")
}
for i := range lst {
if lst[i] != layerStreamTypes[i] {
t.Fatal("Unexpected type was set")
}
}
}

View File

@ -0,0 +1,59 @@
package d2ds1
type ds1version int
const (
v3 ds1version = 3
v4 = 4
v7 = 7
v8 = 8
v9 = 9
v10 = 10
v12 = 12
v13 = 13
v14 = 14
v15 = 15
v16 = 16
v18 = 18
)
func (v ds1version) hasUnknown1Bytes() bool {
// just after the header will be some meaningless (?) bytes
return v >= v9 && v <= v13
}
func (v ds1version) hasUnknown2Bytes() bool {
return v >= v18
}
func (v ds1version) specifiesAct() bool {
// in the header
return v >= v8
}
func (v ds1version) specifiesSubstitutionType() bool {
// in the header
return v >= v10
}
func (v ds1version) specifiesWalls() bool {
// just after header, specifies number of Walls
return v >= v4
}
func (v ds1version) specifiesFloors() bool {
// just after header, specifies number of Floors
return v >= v16
}
func (v ds1version) hasFileList() bool {
return v >= v3
}
func (v ds1version) hasObjects() bool {
return v >= v3
}
func (v ds1version) hasSubstitutions() bool {
return v >= v12
}

View File

@ -1,74 +0,0 @@
package d2ds1
import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2datautils"
)
const (
prop1Bitmask = 0x000000FF
prop1Offset = 0
prop1Length = 8
sequenceBitmask = 0x00003F00
sequenceOffset = 8
sequenceLength = 6
unknown1Bitmask = 0x000FC000
unknown1Offset = 14
unknown1Length = 6
styleBitmask = 0x03F00000
styleOffset = 20
styleLength = 6
unknown2Bitmask = 0x7C000000
unknown2Offset = 26
unknown2Length = 5
hiddenBitmask = 0x80000000
hiddenOffset = 31
hiddenLength = 1
)
type floorShadow struct {
Prop1 byte
Sequence byte
Unknown1 byte
Style byte
Unknown2 byte
HiddenBytes byte
RandomIndex byte
Animated bool
YAdjust int
}
// Floor represents a floor record in a DS1 file. (it is just an alias of floorShadow).
type Floor = floorShadow
// Shadow represents a shadow record in a DS1 file. (it is just an alias of floorShadow).
type Shadow = floorShadow
// Hidden returns if floor/shadow is hidden
func (f *Floor) Hidden() bool {
return f.HiddenBytes > 0
}
// Decode decodes floor-shadow record
func (f *floorShadow) Decode(dw uint32) {
f.Prop1 = byte((dw & prop1Bitmask) >> prop1Offset)
f.Sequence = byte((dw & sequenceBitmask) >> sequenceOffset)
f.Unknown1 = byte((dw & unknown1Bitmask) >> unknown1Offset)
f.Style = byte((dw & styleBitmask) >> styleOffset)
f.Unknown2 = byte((dw & unknown2Bitmask) >> unknown2Offset)
f.HiddenBytes = byte((dw & hiddenBitmask) >> hiddenOffset)
}
// Encode adds Floor's bits to stream writter given
func (f *floorShadow) Encode(sw *d2datautils.StreamWriter) {
sw.PushBits32(uint32(f.Prop1), prop1Length)
sw.PushBits32(uint32(f.Sequence), sequenceLength)
sw.PushBits32(uint32(f.Unknown1), unknown1Length)
sw.PushBits32(uint32(f.Style), styleLength)
sw.PushBits32(uint32(f.Unknown2), unknown2Length)
sw.PushBits32(uint32(f.HiddenBytes), hiddenLength)
}

View File

@ -0,0 +1,143 @@
package d2ds1
// layerStreamType represents a layer stream type
type layerStreamType int
// Layer stream types
const (
layerStreamWall1 layerStreamType = iota
layerStreamWall2
layerStreamWall3
layerStreamWall4
layerStreamOrientation1
layerStreamOrientation2
layerStreamOrientation3
layerStreamOrientation4
layerStreamFloor1
layerStreamFloor2
layerStreamShadow1
layerStreamSubstitute1
)
type tileRow []Tile // index is x coordinate
type tileGrid []tileRow // index is y coordinate
type grid interface {
Width() int
SetWidth(w int) grid
Height() int
SetHeight(h int) grid
Size() (w, h int)
SetSize(w, h int) grid
Tile(x, y int) *Tile
SetTile(x, y int, t *Tile)
}
type layer struct {
tiles tileGrid
}
func (l *layer) Tile(x, y int) *Tile {
if l.Width() < x || l.Height() < y {
return nil
}
return &l.tiles[y][x]
}
func (l *layer) SetTile(x, y int, t *Tile) {
if l.Width() > x || l.Height() > y {
return
}
l.tiles[y][x] = *t
}
func (l *layer) Width() int {
if len(l.tiles[0]) < 1 {
l.SetWidth(1)
}
return len(l.tiles[0])
}
func (l *layer) SetWidth(w int) grid {
if w < 1 {
w = 1
}
// ensure at least one row
if len(l.tiles) < 1 {
l.tiles = make(tileGrid, 1)
}
// create/copy tiles as required to satisfy width
for y := range l.tiles {
if (w - len(l.tiles[y])) == 0 { // if requested width same as row width
continue
}
tmpRow := make(tileRow, w)
for x := range tmpRow {
if x < len(l.tiles[y]) { // if tile exists
tmpRow[x] = l.tiles[y][x] // copy it
}
}
l.tiles[y] = tmpRow
}
return l
}
func (l *layer) Height() int {
if len(l.tiles) < 1 {
l.SetHeight(1)
}
return len(l.tiles)
}
func (l *layer) SetHeight(h int) grid {
if h < 1 {
h = 1
}
// make tmpGrid to move existing tiles into
tmpGrid := make(tileGrid, h)
for y := range tmpGrid {
tmpGrid[y] = make(tileRow, l.Width())
}
// move existing tiles over
for y := range l.tiles {
if y >= len(tmpGrid) {
continue
}
for x := range l.tiles[y] {
if x >= len(tmpGrid[y]) {
continue
}
tmpGrid[y][x] = l.tiles[y][x]
}
}
l.tiles = tmpGrid
return l
}
func (l *layer) Size() (w, h int) {
return l.Width(), l.Height()
}
func (l *layer) SetSize(w, h int) grid {
return l.SetWidth(w).SetHeight(h)
}

View File

@ -0,0 +1,29 @@
package d2ds1
import "testing"
func Test_layers(t *testing.T) {
const (
fmtWidthHeightError = "unexpected wall layer width/height: %dx%d"
)
l := &layer{}
l.SetSize(0, 0)
if l.Width() != 1 || l.Height() != 1 {
t.Fatalf(fmtWidthHeightError, l.Width(), l.Height())
}
l.SetSize(4, 5)
if l.Width() != 4 || l.Height() != 5 {
t.Fatalf(fmtWidthHeightError, l.Width(), l.Height())
}
l.SetSize(4, 3)
if l.Width() != 4 || l.Height() != 3 {
t.Fatalf(fmtWidthHeightError, l.Width(), l.Height())
}
}

View File

@ -1,6 +0,0 @@
package d2ds1
// Substitution represents a substitution record in a DS1 file.
type Substitution struct {
Unknown uint32
}

View File

@ -1,18 +1,123 @@
package d2ds1
import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2datautils"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
)
const (
prop1Bitmask = 0x000000FF
prop1Offset = 0
prop1Length = 8
sequenceBitmask = 0x00003F00
sequenceOffset = 8
sequenceLength = 6
unknown1Bitmask = 0x000FC000
unknown1Offset = 14
unknown1Length = 6
styleBitmask = 0x03F00000
styleOffset = 20
styleLength = 6
unknown2Bitmask = 0x7C000000
unknown2Offset = 26
unknown2Length = 5
hiddenBitmask = 0x80000000
hiddenOffset = 31
hiddenLength = 1
)
type tileCommonFields struct {
Type d2enum.TileType
Zero byte
Prop1 byte
Sequence byte
Unknown1 byte
Style byte
Unknown2 byte
HiddenBytes byte
RandomIndex byte
YAdjust int
}
type tileFloorShadowFields struct {
Animated bool
}
type tileSubstitutionFields struct {
Substitution uint32 // unknown
}
// Tile represents a tile record in a DS1 file.
type Tile struct {
Floors []Floor // Collection of floor records
Walls []Wall // Collection of wall records
Shadows []Shadow // Collection of shadow records
Substitutions []Substitution // Collection of substitutions
tileCommonFields
tileFloorShadowFields
tileSubstitutionFields
}
func makeDefaultTile() Tile {
return Tile{
Floors: []Floor{{}},
Walls: []Wall{{}},
Shadows: []Shadow{{}},
Substitutions: []Substitution{{}},
}
// Hidden returns if wall is hidden
func (t *Tile) Hidden() bool {
return t.HiddenBytes > 0
}
// DecodeWall decodes as a wall record
func (t *Tile) DecodeWall(dw uint32) {
t.Prop1 = byte((dw & prop1Bitmask) >> prop1Offset)
t.Sequence = byte((dw & sequenceBitmask) >> sequenceOffset)
t.Unknown1 = byte((dw & unknown1Bitmask) >> unknown1Offset)
t.Style = byte((dw & styleBitmask) >> styleOffset)
t.Unknown2 = byte((dw & unknown2Bitmask) >> unknown2Offset)
t.HiddenBytes = byte((dw & hiddenBitmask) >> hiddenOffset)
}
// EncodeWall adds wall's record's bytes into stream writer given
func (t *Tile) EncodeWall(sw *d2datautils.StreamWriter) {
sw.PushBits32(uint32(t.Prop1), prop1Length)
sw.PushBits32(uint32(t.Sequence), sequenceLength)
sw.PushBits32(uint32(t.Unknown1), unknown1Length)
sw.PushBits32(uint32(t.Style), styleLength)
sw.PushBits32(uint32(t.Unknown2), unknown2Length)
sw.PushBits32(uint32(t.HiddenBytes), hiddenLength)
}
func (t *Tile) decodeFloorShadow(dw uint32) {
t.Prop1 = byte((dw & prop1Bitmask) >> prop1Offset)
t.Sequence = byte((dw & sequenceBitmask) >> sequenceOffset)
t.Unknown1 = byte((dw & unknown1Bitmask) >> unknown1Offset)
t.Style = byte((dw & styleBitmask) >> styleOffset)
t.Unknown2 = byte((dw & unknown2Bitmask) >> unknown2Offset)
t.HiddenBytes = byte((dw & hiddenBitmask) >> hiddenOffset)
}
func (t *Tile) encodeFloorShadow(sw *d2datautils.StreamWriter) {
sw.PushBits32(uint32(t.Prop1), prop1Length)
sw.PushBits32(uint32(t.Sequence), sequenceLength)
sw.PushBits32(uint32(t.Unknown1), unknown1Length)
sw.PushBits32(uint32(t.Style), styleLength)
sw.PushBits32(uint32(t.Unknown2), unknown2Length)
sw.PushBits32(uint32(t.HiddenBytes), hiddenLength)
}
// DecodeFloor decodes as a floor record
func (t *Tile) DecodeFloor(dw uint32) {
t.decodeFloorShadow(dw)
}
// EncodeFloor adds Floor's bits to stream writer given
func (t *Tile) EncodeFloor(sw *d2datautils.StreamWriter) {
t.encodeFloorShadow(sw)
}
// DecodeShadow decodes as a shadow record
func (t *Tile) DecodeShadow(dw uint32) {
t.decodeFloorShadow(dw)
}
// EncodeShadow adds shadow's bits to stream writer given
func (t *Tile) EncodeShadow(sw *d2datautils.StreamWriter) {
t.encodeFloorShadow(sw)
}

View File

@ -1,45 +0,0 @@
package d2ds1
import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2datautils"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
)
// Wall represents a wall record.
type Wall struct {
Type d2enum.TileType
Zero byte
Prop1 byte
Sequence byte
Unknown1 byte
Style byte
Unknown2 byte
HiddenBytes byte
RandomIndex byte
YAdjust int
}
// Hidden returns if wall is hidden
func (w *Wall) Hidden() bool {
return w.HiddenBytes > 0
}
// Decode decodes wall record
func (w *Wall) Decode(dw uint32) {
w.Prop1 = byte((dw & prop1Bitmask) >> prop1Offset)
w.Sequence = byte((dw & sequenceBitmask) >> sequenceOffset)
w.Unknown1 = byte((dw & unknown1Bitmask) >> unknown1Offset)
w.Style = byte((dw & styleBitmask) >> styleOffset)
w.Unknown2 = byte((dw & unknown2Bitmask) >> unknown2Offset)
w.HiddenBytes = byte((dw & hiddenBitmask) >> hiddenOffset)
}
// Encode adds wall's record's bytes into stream writer given
func (w *Wall) Encode(sw *d2datautils.StreamWriter) {
sw.PushBits32(uint32(w.Prop1), prop1Length)
sw.PushBits32(uint32(w.Sequence), sequenceLength)
sw.PushBits32(uint32(w.Unknown1), unknown1Length)
sw.PushBits32(uint32(w.Style), styleLength)
sw.PushBits32(uint32(w.Unknown2), unknown2Length)
sw.PushBits32(uint32(w.HiddenBytes), hiddenLength)
}

View File

@ -517,7 +517,7 @@ func (am *AssetManager) LoadDS1(ds1Path string) (*d2ds1.DS1, error) {
return nil, err
}
ds1, err := d2ds1.LoadDS1(fileData)
ds1, err := d2ds1.Unmarshal(fileData)
if err != nil {
return nil, err
}

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

@ -2,13 +2,14 @@ package d2mapengine
import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2ds1"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapstamp"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dt1"
)
// MapTile is a tile placed on the map
type MapTile struct {
Components d2ds1.Tile
Components d2mapstamp.Tile
RegionType d2enum.RegionIdType
SubTiles [25]d2dt1.SubTileFlags
}

View File

@ -284,7 +284,9 @@ 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.Floor{{Prop1: 1, Style: 0, Sequence: 0}} // wildernessGrass
floorTile := d2ds1.Tile{}
floorTile.Prop1 = 1
tile.Components.Floors = []d2ds1.Tile{floorTile} // 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.Floor, target d2interface.Surface) {
func (mr *MapRenderer) renderFloor(tile d2ds1.Tile, 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.Floor, target d2interface.Surface)
target.Render(img)
}
func (mr *MapRenderer) renderWall(tile d2ds1.Wall, viewport *Viewport, target d2interface.Surface) {
func (mr *MapRenderer) renderWall(tile d2ds1.Tile, 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.Wall, viewport *Viewport, target d2
target.Render(img)
}
func (mr *MapRenderer) renderShadow(tile d2ds1.Shadow, target d2interface.Surface) {
func (mr *MapRenderer) renderShadow(tile d2ds1.Tile, 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.Floor) {
func (mr *MapRenderer) generateFloorCache(tile *d2ds1.Tile) {
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.Floor) {
}
}
func (mr *MapRenderer) generateShadowCache(tile *d2ds1.Shadow) {
func (mr *MapRenderer) generateShadowCache(tile *d2ds1.Tile) {
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.Shadow) {
mr.setImageCacheRecord(tile.Style, tile.Sequence, d2enum.TileShadow, tile.RandomIndex, image)
}
func (mr *MapRenderer) generateWallCache(tile *d2ds1.Wall) {
func (mr *MapRenderer) generateWallCache(tile *d2ds1.Tile) {
tileOptions := mr.mapEngine.GetTiles(int(tile.Style), int(tile.Sequence), tile.Type)
var tileData *d2dt1.Tile

View File

@ -87,7 +87,7 @@ func (f *StampFactory) LoadStamp(levelType d2enum.RegionIdType, levelPreset, fil
panic(err)
}
stamp.ds1, err = d2ds1.LoadDS1(fileData)
stamp.ds1, err = d2ds1.Unmarshal(fileData)
if err != nil {
f.Error(err.Error())
return nil

View File

@ -54,9 +54,45 @@ func (mr *Stamp) RegionPath() string {
return mr.regionPath
}
type Tile struct {
Walls []d2ds1.Tile
Orientations []d2ds1.Tile
Floors []d2ds1.Tile
Shadows []d2ds1.Tile
Substitutions []d2ds1.Tile
}
// Tile returns the tile at the given x and y tile coordinates.
func (mr *Stamp) Tile(x, y int) *d2ds1.Tile {
return mr.ds1.Tile(x, y)
func (mr *Stamp) Tile(x, y int) *Tile {
t := &Tile{
Walls: make([]d2ds1.Tile, len(mr.ds1.Walls)),
Orientations: make([]d2ds1.Tile, len(mr.ds1.Orientations)),
Floors: make([]d2ds1.Tile, len(mr.ds1.Floors)),
Shadows: make([]d2ds1.Tile, len(mr.ds1.Shadows)),
Substitutions: make([]d2ds1.Tile, len(mr.ds1.Substitutions)),
}
for idx := range mr.ds1.Walls {
t.Walls[idx] = *mr.ds1.Walls[idx].Tile(x, y)
}
for idx := range mr.ds1.Orientations {
t.Orientations[idx] = *mr.ds1.Orientations[idx].Tile(x, y)
}
for idx := range mr.ds1.Floors {
t.Floors[idx] = *mr.ds1.Floors[idx].Tile(x, y)
}
for idx := range mr.ds1.Shadows {
t.Shadows[idx] = *mr.ds1.Shadows[idx].Tile(x, y)
}
for idx := range mr.ds1.Substitutions {
t.Substitutions[idx] = *mr.ds1.Substitutions[idx].Tile(x, y)
}
return t
}
// TileData returns the tile data for the tile with given style, sequence and type.
@ -75,9 +111,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[int32(mr.ds1.Act())][object.ID]
monPreset := mr.factory.asset.Records.Monster.Presets[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 +133,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(mr.ds1.Act(), object.Type, object.ID)
lookup := mr.factory.asset.Records.LookupObject(int(mr.ds1.Act), object.Type, object.ID)
if lookup == nil {
continue

73
et Normal file
View File

@ -0,0 +1,73 @@
diff --git a/d2common/d2fileformats/d2ds1/ds1_test.go b/d2common/d2fileformats/d2ds1/ds1_test.go
index 23035a50..d17527f0 100644
--- a/d2common/d2fileformats/d2ds1/ds1_test.go
+++ b/d2common/d2fileformats/d2ds1/ds1_test.go
@@ -575,6 +575,24 @@ func TestDS1_SetNumberOfFloors(t *testing.T) {
if err := testIfRestorable(ds1, test); err != nil {
t.Errorf("unable to restore: %v", err)
}
+
+ newNumber = 10
+
+ ds1.SetNumberOfFloorLayers(newNumber)
+
+ test = func(ds1 *DS1) {
+ if len(ds1.tiles[0][0].Floors) != int(newNumber) {
+ t.Fatal("unexpected floors length set")
+ }
+
+ if ds1.numberOfFloorLayers != newNumber {
+ t.Fatal("unexpected floors length set")
+ }
+ }
+
+ if err := testIfRestorable(ds1, test); err != nil {
+ t.Errorf("unable to restore: %v", err)
+ }
}

func TestDS1_NumberOfShadowLayers(t *testing.T) {
diff --git a/d2common/d2fileformats/d2ds1/layers.go b/d2common/d2fileformats/d2ds1/layers.go
index f69ef7f1..b0488a34 100644
--- a/d2common/d2fileformats/d2ds1/layers.go
+++ b/d2common/d2fileformats/d2ds1/layers.go
@@ -1 +1,39 @@
package d2ds1
+
+const (
+ maxWalls = 4
+)
+
+type WallLayer [][]*Wall
+type FloorLayer [][]*Floor
+type ShadowLayer [][]*Shadow
+
+type layers struct {
+ Walls []WallLayer
+ Floors []FloorLayer
+ Shadows []ShadowLayer
+}
+
+func (l *layers) PushWallLayer() {
+
+}
+
+func (l *layers) PopWallLayer() WallLayer {
+
+}
+
+func (l *layers) PushFloorLayer() {
+
+}
+
+func (l *layers) PopFloorLayer() FloorLayer {
+
+}
+
+func (l *layers) PushShadowLayer() {
+
+}
+
+func (l *layers) PopShadowLayer() ShadowLayer {
+
+}