mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2024-09-25 04:36:12 -04:00
Fixed linting issues (#438)
This commit is contained in:
parent
46ccce56bd
commit
954670da5f
@ -65,10 +65,14 @@ func (v *linkedNode) Insert(other *linkedNode) *linkedNode {
|
|||||||
v.Next.Prev = other
|
v.Next.Prev = other
|
||||||
other.Next = v.Next
|
other.Next = v.Next
|
||||||
}
|
}
|
||||||
|
|
||||||
v.Next = other
|
v.Next = other
|
||||||
|
|
||||||
other.Prev = v
|
other.Prev = v
|
||||||
|
|
||||||
return other
|
return other
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.Prev == nil {
|
if v.Prev == nil {
|
||||||
// Insert after
|
// Insert after
|
||||||
other.Prev = nil
|
other.Prev = nil
|
||||||
@ -81,8 +85,10 @@ func (v *linkedNode) Insert(other *linkedNode) *linkedNode {
|
|||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
var sPrime = [][]byte{
|
func getPrimes() [][]byte {
|
||||||
{ // Compression type 0
|
return [][]byte{
|
||||||
|
{
|
||||||
|
// Compression type 0
|
||||||
0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
@ -99,7 +105,9 @@ var sPrime = [][]byte{
|
|||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
|
||||||
}, { // Compression type 1
|
},
|
||||||
|
{
|
||||||
|
// Compression type 1
|
||||||
0x54, 0x16, 0x16, 0x0D, 0x0C, 0x08, 0x06, 0x05, 0x06, 0x05, 0x06, 0x03, 0x04, 0x04, 0x03, 0x05,
|
0x54, 0x16, 0x16, 0x0D, 0x0C, 0x08, 0x06, 0x05, 0x06, 0x05, 0x06, 0x03, 0x04, 0x04, 0x03, 0x05,
|
||||||
0x0E, 0x0B, 0x14, 0x13, 0x13, 0x09, 0x0B, 0x06, 0x05, 0x04, 0x03, 0x02, 0x03, 0x02, 0x02, 0x02,
|
0x0E, 0x0B, 0x14, 0x13, 0x13, 0x09, 0x0B, 0x06, 0x05, 0x04, 0x03, 0x02, 0x03, 0x02, 0x02, 0x02,
|
||||||
0x0D, 0x07, 0x09, 0x06, 0x06, 0x04, 0x03, 0x02, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02,
|
0x0D, 0x07, 0x09, 0x06, 0x06, 0x04, 0x03, 0x02, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02,
|
||||||
@ -116,7 +124,8 @@ var sPrime = [][]byte{
|
|||||||
0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||||
0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||||
0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x06, 0x4B,
|
0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x06, 0x4B,
|
||||||
}, { // Compression type 2
|
}, {
|
||||||
|
// Compression type 2
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x27, 0x00, 0x00, 0x23, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x27, 0x00, 0x00, 0x23, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0xFF, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x06, 0x0E, 0x10, 0x04,
|
0xFF, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x06, 0x0E, 0x10, 0x04,
|
||||||
@ -125,7 +134,8 @@ var sPrime = [][]byte{
|
|||||||
0x03, 0x01, 0x03, 0x06, 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x02, 0x01, 0x01,
|
0x03, 0x01, 0x03, 0x06, 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x02, 0x01, 0x01,
|
||||||
0x01, 0x29, 0x07, 0x16, 0x12, 0x40, 0x0A, 0x0A, 0x11, 0x25, 0x01, 0x03, 0x17, 0x10, 0x26, 0x2A,
|
0x01, 0x29, 0x07, 0x16, 0x12, 0x40, 0x0A, 0x0A, 0x11, 0x25, 0x01, 0x03, 0x17, 0x10, 0x26, 0x2A,
|
||||||
0x10, 0x01, 0x23, 0x23, 0x2F, 0x10, 0x06, 0x07, 0x02, 0x09, 0x01, 0x01, 0x01, 0x01, 0x01,
|
0x10, 0x01, 0x23, 0x23, 0x2F, 0x10, 0x06, 0x07, 0x02, 0x09, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||||
}, { // Compression type 3
|
}, {
|
||||||
|
// Compression type 3
|
||||||
0xFF, 0x0B, 0x07, 0x05, 0x0B, 0x02, 0x02, 0x02, 0x06, 0x02, 0x02, 0x01, 0x04, 0x02, 0x01, 0x03,
|
0xFF, 0x0B, 0x07, 0x05, 0x0B, 0x02, 0x02, 0x02, 0x06, 0x02, 0x02, 0x01, 0x04, 0x02, 0x01, 0x03,
|
||||||
0x09, 0x01, 0x01, 0x01, 0x03, 0x04, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01,
|
0x09, 0x01, 0x01, 0x01, 0x03, 0x04, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01,
|
||||||
0x05, 0x01, 0x01, 0x01, 0x0D, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
0x05, 0x01, 0x01, 0x01, 0x0D, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||||
@ -180,6 +190,7 @@ var sPrime = [][]byte{
|
|||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x5F, 0x9E,
|
0x5F, 0x9E,
|
||||||
},
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func decode(input *d2common.BitStream, head *linkedNode) *linkedNode {
|
func decode(input *d2common.BitStream, head *linkedNode) *linkedNode {
|
||||||
@ -190,12 +201,15 @@ func decode(input *d2common.BitStream, head *linkedNode) *linkedNode {
|
|||||||
if bit == -1 {
|
if bit == -1 {
|
||||||
log.Fatal("unexpected end of file")
|
log.Fatal("unexpected end of file")
|
||||||
}
|
}
|
||||||
|
|
||||||
if bit == 0 {
|
if bit == 0 {
|
||||||
node = node.Child0
|
node = node.Child0
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
node = node.GetChild1()
|
node = node.GetChild1()
|
||||||
}
|
}
|
||||||
|
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,6 +222,7 @@ func buildList(primeData []byte) *linkedNode {
|
|||||||
root = root.Insert(CreateLinkedNode(i, int(primeData[i])))
|
root = root.Insert(CreateLinkedNode(i, int(primeData[i])))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return root
|
return root
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,9 +244,10 @@ func insertNode(tail *linkedNode, decomp int) *linkedNode {
|
|||||||
temp.Next = newnode
|
temp.Next = newnode
|
||||||
|
|
||||||
adjustTree(newnode)
|
adjustTree(newnode)
|
||||||
// TODO: For compression type 0, AdjustTree should be called
|
|
||||||
// once for every value written and only once here
|
// TODO: For compression type 0, AdjustTree should be called once for every value written and only once here
|
||||||
adjustTree(newnode)
|
adjustTree(newnode)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,18 +258,24 @@ func adjustTree(newNode *linkedNode) {
|
|||||||
|
|
||||||
for current != nil {
|
for current != nil {
|
||||||
current.Weight++
|
current.Weight++
|
||||||
|
|
||||||
var insertpoint *linkedNode
|
var insertpoint *linkedNode
|
||||||
|
|
||||||
var prev *linkedNode
|
var prev *linkedNode
|
||||||
|
|
||||||
// Go backwards thru the list looking for the insertion point
|
// Go backwards thru the list looking for the insertion point
|
||||||
insertpoint = current
|
insertpoint = current
|
||||||
|
|
||||||
for {
|
for {
|
||||||
prev = insertpoint.Prev
|
prev = insertpoint.Prev
|
||||||
if prev == nil {
|
if prev == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if prev.Weight >= current.Weight {
|
if prev.Weight >= current.Weight {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
insertpoint = prev
|
insertpoint = prev
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,14 +291,17 @@ func adjustTree(newNode *linkedNode) {
|
|||||||
if insertpoint.Prev != nil {
|
if insertpoint.Prev != nil {
|
||||||
insertpoint.Prev.Next = insertpoint.Next
|
insertpoint.Prev.Next = insertpoint.Next
|
||||||
}
|
}
|
||||||
|
|
||||||
insertpoint.Next.Prev = insertpoint.Prev
|
insertpoint.Next.Prev = insertpoint.Prev
|
||||||
|
|
||||||
// Insert insertpoint after current
|
// Insert insertpoint after current
|
||||||
insertpoint.Next = current.Next
|
insertpoint.Next = current.Next
|
||||||
insertpoint.Prev = current
|
insertpoint.Prev = current
|
||||||
|
|
||||||
if current.Next != nil {
|
if current.Next != nil {
|
||||||
current.Next.Prev = insertpoint
|
current.Next.Prev = insertpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
current.Next = insertpoint
|
current.Next = insertpoint
|
||||||
|
|
||||||
// remove current
|
// remove current
|
||||||
@ -319,6 +344,7 @@ func buildTree(tail *linkedNode) *linkedNode {
|
|||||||
for current != nil {
|
for current != nil {
|
||||||
child0 := current
|
child0 := current
|
||||||
child1 := current.Prev
|
child1 := current.Prev
|
||||||
|
|
||||||
if child1 == nil {
|
if child1 == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -331,22 +357,26 @@ func buildTree(tail *linkedNode) *linkedNode {
|
|||||||
current.Insert(parent)
|
current.Insert(parent)
|
||||||
current = current.Prev.Prev
|
current = current.Prev.Prev
|
||||||
}
|
}
|
||||||
|
|
||||||
return current
|
return current
|
||||||
}
|
}
|
||||||
|
|
||||||
func HuffmanDecompress(data []byte) []byte {
|
func HuffmanDecompress(data []byte) []byte {
|
||||||
comptype := data[0]
|
comptype := data[0]
|
||||||
|
primes := getPrimes()
|
||||||
|
|
||||||
if comptype == 0 {
|
if comptype == 0 {
|
||||||
log.Panic("compression type 0 is not currently supported")
|
log.Panic("compression type 0 is not currently supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
tail := buildList(sPrime[comptype])
|
tail := buildList(primes[comptype])
|
||||||
head := buildTree(tail)
|
head := buildTree(tail)
|
||||||
|
|
||||||
outputstream := d2common.CreateStreamWriter()
|
outputstream := d2common.CreateStreamWriter()
|
||||||
bitstream := d2common.CreateBitStream(data[1:])
|
bitstream := d2common.CreateBitStream(data[1:])
|
||||||
|
|
||||||
var decoded int
|
var decoded int
|
||||||
|
|
||||||
Loop:
|
Loop:
|
||||||
for {
|
for {
|
||||||
node := decode(bitstream, head)
|
node := decode(bitstream, head)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package d2datadict
|
package d2datadict
|
||||||
|
|
||||||
|
//nolint // This is a data dump file.
|
||||||
var objectLookups = []ObjectLookupRecord{
|
var objectLookups = []ObjectLookupRecord{
|
||||||
{Act: 1, Type: ObjectTypeCharacter, Id: 0, Description: "gheed-ACT 1 TABLE", ObjectsTxtId: -1, MonstatsTxtId: -1, Direction: -1, Base: "/Data/Global/Monsters", Token: "GH", Mode: "NU", Class: "HTH", TR: "LIT", Index: -1},
|
{Act: 1, Type: ObjectTypeCharacter, Id: 0, Description: "gheed-ACT 1 TABLE", ObjectsTxtId: -1, MonstatsTxtId: -1, Direction: -1, Base: "/Data/Global/Monsters", Token: "GH", Mode: "NU", Class: "HTH", TR: "LIT", Index: -1},
|
||||||
{Act: 1, Type: ObjectTypeCharacter, Id: 1, Description: "cain1-ACT 1 TABLE", ObjectsTxtId: -1, MonstatsTxtId: -1, Direction: -1, Base: "/Data/Global/Monsters", Token: "DC", Mode: "NU", Class: "HTH", TR: "LIT", Index: -1},
|
{Act: 1, Type: ObjectTypeCharacter, Id: 1, Description: "cain1-ACT 1 TABLE", ObjectsTxtId: -1, MonstatsTxtId: -1, Direction: -1, Base: "/Data/Global/Monsters", Token: "DC", Mode: "NU", Class: "HTH", TR: "LIT", Index: -1},
|
||||||
|
@ -11,7 +11,7 @@ type DCC struct {
|
|||||||
Version int
|
Version int
|
||||||
NumberOfDirections int
|
NumberOfDirections int
|
||||||
FramesPerDirection int
|
FramesPerDirection int
|
||||||
Directions []DCCDirection
|
Directions []*DCCDirection
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadDCC(fileData []byte) (*DCC, error) {
|
func LoadDCC(fileData []byte) (*DCC, error) {
|
||||||
@ -32,7 +32,7 @@ func LoadDCC(fileData []byte) (*DCC, error) {
|
|||||||
for i := 0; i < result.NumberOfDirections; i++ {
|
for i := 0; i < result.NumberOfDirections; i++ {
|
||||||
directionOffsets[i] = int(bm.GetInt32())
|
directionOffsets[i] = int(bm.GetInt32())
|
||||||
}
|
}
|
||||||
result.Directions = make([]DCCDirection, result.NumberOfDirections)
|
result.Directions = make([]*DCCDirection, result.NumberOfDirections)
|
||||||
for i := 0; i < result.NumberOfDirections; i++ {
|
for i := 0; i < result.NumberOfDirections; i++ {
|
||||||
result.Directions[i] = CreateDCCDirection(d2common.CreateBitMuncher(fileData, directionOffsets[i]*8), *result)
|
result.Directions[i] = CreateDCCDirection(d2common.CreateBitMuncher(fileData, directionOffsets[i]*8), *result)
|
||||||
}
|
}
|
||||||
|
@ -30,8 +30,8 @@ type DCCDirection struct {
|
|||||||
PixelBuffer []DCCPixelBufferEntry
|
PixelBuffer []DCCPixelBufferEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateDCCDirection(bm *d2common.BitMuncher, file DCC) DCCDirection {
|
func CreateDCCDirection(bm *d2common.BitMuncher, file DCC) *DCCDirection {
|
||||||
result := DCCDirection{}
|
result := &DCCDirection{}
|
||||||
result.OutSizeCoded = int(bm.GetUInt32())
|
result.OutSizeCoded = int(bm.GetUInt32())
|
||||||
result.CompressionFlags = int(bm.GetBits(2))
|
result.CompressionFlags = int(bm.GetBits(2))
|
||||||
result.Variable0Bits = int(crazyBitTable[bm.GetBits(4)])
|
result.Variable0Bits = int(crazyBitTable[bm.GetBits(4)])
|
||||||
|
@ -22,7 +22,7 @@ type DCCDirectionFrame struct {
|
|||||||
valid bool
|
valid bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateDCCDirectionFrame(bits *d2common.BitMuncher, direction DCCDirection) *DCCDirectionFrame {
|
func CreateDCCDirectionFrame(bits *d2common.BitMuncher, direction *DCCDirection) *DCCDirectionFrame {
|
||||||
result := &DCCDirectionFrame{}
|
result := &DCCDirectionFrame{}
|
||||||
bits.GetBits(direction.Variable0Bits) // Variable0
|
bits.GetBits(direction.Variable0Bits) // Variable0
|
||||||
result.Width = int(bits.GetBits(direction.WidthBits))
|
result.Width = int(bits.GetBits(direction.WidthBits))
|
||||||
@ -46,7 +46,7 @@ func CreateDCCDirectionFrame(bits *d2common.BitMuncher, direction DCCDirection)
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *DCCDirectionFrame) CalculateCells(direction DCCDirection) {
|
func (v *DCCDirectionFrame) CalculateCells(direction *DCCDirection) {
|
||||||
var w = 4 - ((v.Box.Left - direction.Box.Left) % 4) // Width of the first column (in pixels)
|
var w = 4 - ((v.Box.Left - direction.Box.Left) % 4) // Width of the first column (in pixels)
|
||||||
if (v.Width - w) <= 1 {
|
if (v.Width - w) <= 1 {
|
||||||
v.HorizontalCellCount = 1
|
v.HorizontalCellCount = 1
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
package d2ds1
|
|
||||||
|
|
||||||
var dirLookup = []int32{
|
|
||||||
0x00, 0x01, 0x02, 0x01, 0x02, 0x03, 0x03, 0x05, 0x05, 0x06,
|
|
||||||
0x06, 0x07, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
|
|
||||||
0x0F, 0x10, 0x11, 0x12, 0x14,
|
|
||||||
}
|
|
@ -1,3 +1,4 @@
|
|||||||
|
// Package d2ds1 provides functionality for loading/processing DS1 files
|
||||||
package d2ds1
|
package d2ds1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -7,23 +8,27 @@ import (
|
|||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const maxActNumber = 5
|
||||||
|
|
||||||
|
// DS1 represents the "stamp" data that is used to build up maps.
|
||||||
type DS1 struct {
|
type DS1 struct {
|
||||||
|
Files []string // FilePtr table of file string pointers
|
||||||
|
Objects []d2data.Object // Objects
|
||||||
|
Tiles [][]TileRecord // The tile data for the DS1
|
||||||
|
SubstitutionGroups []SubstitutionGroup // Substitution groups for the DS1
|
||||||
Version int32 // The version of the DS1
|
Version int32 // The version of the DS1
|
||||||
Width int32 // Width of map, in # of tiles
|
Width int32 // Width of map, in # of tiles
|
||||||
Height int32 // Height of map, in # of tiles
|
Height int32 // Height of map, in # of tiles
|
||||||
Act int32 // Act, from 1 to 5. This tells which act table to use for the Objects list
|
Act int32 // Act, from 1 to 5. This tells which act table to use for the Objects list
|
||||||
SubstitutionType int32 // SubstitutionType (layer type): 0 if no layer, else type 1 or type 2
|
SubstitutionType int32 // SubstitutionType (layer type): 0 if no layer, else type 1 or type 2
|
||||||
Files []string // FilePtr table of file string pointers
|
|
||||||
NumberOfWalls int32 // WallNum number of wall & orientation layers used
|
NumberOfWalls int32 // WallNum number of wall & orientation layers used
|
||||||
NumberOfFloors int32 // number of floor layers used
|
NumberOfFloors int32 // number of floor layers used
|
||||||
NumberOfShadowLayers int32 // ShadowNum number of shadow layer used
|
NumberOfShadowLayers int32 // ShadowNum number of shadow layer used
|
||||||
NumberOfSubstitutionLayers int32 // SubstitutionNum number of substitution layer used
|
NumberOfSubstitutionLayers int32 // SubstitutionNum number of substitution layer used
|
||||||
SubstitutionGroupsNum int32 // SubstitutionGroupsNum number of substitution groups, datas between objects & NPC paths
|
SubstitutionGroupsNum int32 // SubstitutionGroupsNum number of substitution groups, datas between objects & NPC paths
|
||||||
Objects []d2data.Object // Objects
|
|
||||||
Tiles [][]TileRecord
|
|
||||||
SubstitutionGroups []SubstitutionGroup
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadDS1 loads the specified DS1 file
|
||||||
func LoadDS1(fileData []byte) (*DS1, error) {
|
func LoadDS1(fileData []byte) (*DS1, error) {
|
||||||
ds1 := &DS1{
|
ds1 := &DS1{
|
||||||
Act: 1,
|
Act: 1,
|
||||||
@ -36,44 +41,126 @@ func LoadDS1(fileData []byte) (*DS1, error) {
|
|||||||
ds1.Version = br.GetInt32()
|
ds1.Version = br.GetInt32()
|
||||||
ds1.Width = br.GetInt32() + 1
|
ds1.Width = br.GetInt32() + 1
|
||||||
ds1.Height = br.GetInt32() + 1
|
ds1.Height = br.GetInt32() + 1
|
||||||
if ds1.Version >= 8 {
|
|
||||||
ds1.Act = d2common.MinInt32(5, br.GetInt32()+1)
|
if ds1.Version >= 8 { //nolint:gomnd // Version number
|
||||||
|
ds1.Act = d2common.MinInt32(maxActNumber, br.GetInt32()+1)
|
||||||
}
|
}
|
||||||
if ds1.Version >= 10 {
|
|
||||||
|
if ds1.Version >= 10 { //nolint:gomnd // Version number
|
||||||
ds1.SubstitutionType = br.GetInt32()
|
ds1.SubstitutionType = br.GetInt32()
|
||||||
if ds1.SubstitutionType == 1 || ds1.SubstitutionType == 2 {
|
if ds1.SubstitutionType == 1 || ds1.SubstitutionType == 2 {
|
||||||
ds1.NumberOfSubstitutionLayers = 1
|
ds1.NumberOfSubstitutionLayers = 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ds1.Version >= 3 {
|
|
||||||
|
if ds1.Version >= 3 { //nolint:gomnd // Version number
|
||||||
// These files reference things that don't exist anymore :-?
|
// These files reference things that don't exist anymore :-?
|
||||||
numberOfFiles := br.GetInt32()
|
numberOfFiles := br.GetInt32()
|
||||||
ds1.Files = make([]string, numberOfFiles)
|
ds1.Files = make([]string, numberOfFiles)
|
||||||
|
|
||||||
for i := 0; i < int(numberOfFiles); i++ {
|
for i := 0; i < int(numberOfFiles); i++ {
|
||||||
ds1.Files[i] = ""
|
ds1.Files[i] = ""
|
||||||
|
|
||||||
for {
|
for {
|
||||||
ch := br.GetByte()
|
ch := br.GetByte()
|
||||||
if ch == 0 {
|
if ch == 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
ds1.Files[i] += string(ch)
|
ds1.Files[i] += string(ch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ds1.Version >= 9 && ds1.Version <= 13 {
|
if ds1.Version >= 9 && ds1.Version <= 13 {
|
||||||
// Skipping two dwords because they are "meaningless"?
|
// Skipping two dwords because they are "meaningless"?
|
||||||
br.SkipBytes(8)
|
br.SkipBytes(8) //nolint:gomnd // We don't know what's here
|
||||||
}
|
}
|
||||||
if ds1.Version >= 4 {
|
|
||||||
|
if ds1.Version >= 4 { //nolint:gomnd // Version number
|
||||||
ds1.NumberOfWalls = br.GetInt32()
|
ds1.NumberOfWalls = br.GetInt32()
|
||||||
if ds1.Version >= 16 {
|
if ds1.Version >= 16 { //nolint:gomnd // Version number
|
||||||
ds1.NumberOfFloors = br.GetInt32()
|
ds1.NumberOfFloors = br.GetInt32()
|
||||||
} else {
|
} else {
|
||||||
ds1.NumberOfFloors = 1
|
ds1.NumberOfFloors = 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
layerStream := ds1.setupStreamLayerTypes()
|
||||||
|
|
||||||
|
ds1.Tiles = make([][]TileRecord, ds1.Height)
|
||||||
|
|
||||||
|
for y := range ds1.Tiles {
|
||||||
|
ds1.Tiles[y] = make([]TileRecord, ds1.Width)
|
||||||
|
for x := 0; x < int(ds1.Width); x++ {
|
||||||
|
ds1.Tiles[y][x].Walls = make([]WallRecord, ds1.NumberOfWalls)
|
||||||
|
ds1.Tiles[y][x].Floors = make([]FloorShadowRecord, ds1.NumberOfFloors)
|
||||||
|
ds1.Tiles[y][x].Shadows = make([]FloorShadowRecord, ds1.NumberOfShadowLayers)
|
||||||
|
ds1.Tiles[y][x].Substitutions = make([]SubstitutionRecord, ds1.NumberOfSubstitutionLayers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ds1.loadLayerStreams(br, layerStream)
|
||||||
|
ds1.loadObjects(br)
|
||||||
|
ds1.loadSubstitutions(br)
|
||||||
|
ds1.loadNPCs(br)
|
||||||
|
|
||||||
|
return ds1, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ds1 *DS1) loadObjects(br *d2common.StreamReader) {
|
||||||
|
if ds1.Version >= 2 { //nolint:gomnd // Version number
|
||||||
|
numberOfObjects := br.GetInt32()
|
||||||
|
ds1.Objects = make([]d2data.Object, numberOfObjects)
|
||||||
|
|
||||||
|
for objIdx := 0; objIdx < int(numberOfObjects); objIdx++ {
|
||||||
|
newObject := d2data.Object{}
|
||||||
|
newObject.Type = int(br.GetInt32())
|
||||||
|
newObject.Id = int(br.GetInt32())
|
||||||
|
newObject.X = int(br.GetInt32())
|
||||||
|
newObject.Y = int(br.GetInt32())
|
||||||
|
newObject.Flags = int(br.GetInt32())
|
||||||
|
newObject.Lookup = d2datadict.LookupObject(int(ds1.Act), newObject.Type, newObject.Id)
|
||||||
|
|
||||||
|
if newObject.Lookup != nil && newObject.Lookup.ObjectsTxtId != -1 {
|
||||||
|
newObject.ObjectInfo = d2datadict.Objects[newObject.Lookup.ObjectsTxtId]
|
||||||
|
}
|
||||||
|
|
||||||
|
ds1.Objects[objIdx] = newObject
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ds1.Objects = make([]d2data.Object, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ds1 *DS1) loadSubstitutions(br *d2common.StreamReader) {
|
||||||
|
if ds1.Version >= 12 && (ds1.SubstitutionType == 1 || ds1.SubstitutionType == 2) {
|
||||||
|
if ds1.Version >= 18 { //nolint:gomnd // Version number
|
||||||
|
br.GetUInt32()
|
||||||
|
}
|
||||||
|
|
||||||
|
numberOfSubGroups := br.GetInt32()
|
||||||
|
ds1.SubstitutionGroups = make([]SubstitutionGroup, numberOfSubGroups)
|
||||||
|
|
||||||
|
for subIdx := 0; subIdx < int(numberOfSubGroups); subIdx++ {
|
||||||
|
newSub := SubstitutionGroup{}
|
||||||
|
newSub.TileX = br.GetInt32()
|
||||||
|
newSub.TileY = br.GetInt32()
|
||||||
|
newSub.WidthInTiles = br.GetInt32()
|
||||||
|
newSub.HeightInTiles = br.GetInt32()
|
||||||
|
newSub.Unknown = br.GetInt32()
|
||||||
|
|
||||||
|
ds1.SubstitutionGroups[subIdx] = newSub
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ds1.SubstitutionGroups = make([]SubstitutionGroup, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ds1 *DS1) setupStreamLayerTypes() []d2enum.LayerStreamType {
|
||||||
var layerStream []d2enum.LayerStreamType
|
var layerStream []d2enum.LayerStreamType
|
||||||
if ds1.Version < 4 {
|
|
||||||
|
if ds1.Version < 4 { //nolint:gomnd // Version number
|
||||||
layerStream = []d2enum.LayerStreamType{
|
layerStream = []d2enum.LayerStreamType{
|
||||||
d2enum.LayerStreamWall1,
|
d2enum.LayerStreamWall1,
|
||||||
d2enum.LayerStreamFloor1,
|
d2enum.LayerStreamFloor1,
|
||||||
@ -82,7 +169,9 @@ func LoadDS1(fileData []byte) (*DS1, error) {
|
|||||||
d2enum.LayerStreamShadow,
|
d2enum.LayerStreamShadow,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
layerStream = make([]d2enum.LayerStreamType, (ds1.NumberOfWalls*2)+ds1.NumberOfFloors+ds1.NumberOfShadowLayers+ds1.NumberOfSubstitutionLayers)
|
layerStream = make([]d2enum.LayerStreamType,
|
||||||
|
(ds1.NumberOfWalls*2)+ds1.NumberOfFloors+ds1.NumberOfShadowLayers+ds1.NumberOfSubstitutionLayers)
|
||||||
|
|
||||||
layerIdx := 0
|
layerIdx := 0
|
||||||
for i := 0; i < int(ds1.NumberOfWalls); i++ {
|
for i := 0; i < int(ds1.NumberOfWalls); i++ {
|
||||||
layerStream[layerIdx] = d2enum.LayerStreamType(int(d2enum.LayerStreamWall1) + i)
|
layerStream[layerIdx] = d2enum.LayerStreamType(int(d2enum.LayerStreamWall1) + i)
|
||||||
@ -99,149 +188,114 @@ func LoadDS1(fileData []byte) (*DS1, error) {
|
|||||||
}
|
}
|
||||||
if ds1.NumberOfSubstitutionLayers > 0 {
|
if ds1.NumberOfSubstitutionLayers > 0 {
|
||||||
layerStream[layerIdx] = d2enum.LayerStreamSubstitute
|
layerStream[layerIdx] = d2enum.LayerStreamSubstitute
|
||||||
layerIdx++
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ds1.Tiles = make([][]TileRecord, ds1.Height)
|
|
||||||
for y := range ds1.Tiles {
|
|
||||||
ds1.Tiles[y] = make([]TileRecord, ds1.Width)
|
|
||||||
for x := 0; x < int(ds1.Width); x++ {
|
|
||||||
ds1.Tiles[y][x].Walls = make([]WallRecord, ds1.NumberOfWalls)
|
|
||||||
ds1.Tiles[y][x].Floors = make([]FloorShadowRecord, ds1.NumberOfFloors)
|
|
||||||
ds1.Tiles[y][x].Shadows = make([]FloorShadowRecord, ds1.NumberOfShadowLayers)
|
|
||||||
ds1.Tiles[y][x].Substitutions = make([]SubstitutionRecord, ds1.NumberOfSubstitutionLayers)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, layerStreamType := range layerStream {
|
|
||||||
for y := 0; y < int(ds1.Height); y++ {
|
|
||||||
for x := 0; x < int(ds1.Width); x++ {
|
|
||||||
dw := br.GetUInt32()
|
|
||||||
switch layerStreamType {
|
|
||||||
case d2enum.LayerStreamWall1:
|
|
||||||
fallthrough
|
|
||||||
case d2enum.LayerStreamWall2:
|
|
||||||
fallthrough
|
|
||||||
case d2enum.LayerStreamWall3:
|
|
||||||
fallthrough
|
|
||||||
case d2enum.LayerStreamWall4:
|
|
||||||
wallIndex := int(layerStreamType) - int(d2enum.LayerStreamWall1)
|
|
||||||
ds1.Tiles[y][x].Walls[wallIndex].Prop1 = byte(dw & 0x000000FF)
|
|
||||||
ds1.Tiles[y][x].Walls[wallIndex].Sequence = byte((dw & 0x00003F00) >> 8)
|
|
||||||
ds1.Tiles[y][x].Walls[wallIndex].Unknown1 = byte((dw & 0x000FC000) >> 14)
|
|
||||||
ds1.Tiles[y][x].Walls[wallIndex].Style = byte((dw & 0x03F00000) >> 20)
|
|
||||||
ds1.Tiles[y][x].Walls[wallIndex].Unknown2 = byte((dw & 0x7C000000) >> 26)
|
|
||||||
ds1.Tiles[y][x].Walls[wallIndex].Hidden = byte((dw&0x80000000)>>31) > 0
|
|
||||||
case d2enum.LayerStreamOrientation1:
|
|
||||||
fallthrough
|
|
||||||
case d2enum.LayerStreamOrientation2:
|
|
||||||
fallthrough
|
|
||||||
case d2enum.LayerStreamOrientation3:
|
|
||||||
fallthrough
|
|
||||||
case d2enum.LayerStreamOrientation4:
|
|
||||||
wallIndex := int(layerStreamType) - int(d2enum.LayerStreamOrientation1)
|
|
||||||
c := int32(dw & 0x000000FF)
|
|
||||||
if ds1.Version < 7 {
|
|
||||||
if c < 25 {
|
|
||||||
c = dirLookup[c]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ds1.Tiles[y][x].Walls[wallIndex].Type = d2enum.TileType(c)
|
|
||||||
ds1.Tiles[y][x].Walls[wallIndex].Zero = byte((dw & 0xFFFFFF00) >> 8)
|
|
||||||
case d2enum.LayerStreamFloor1:
|
|
||||||
fallthrough
|
|
||||||
case d2enum.LayerStreamFloor2:
|
|
||||||
floorIndex := int(layerStreamType) - int(d2enum.LayerStreamFloor1)
|
|
||||||
ds1.Tiles[y][x].Floors[floorIndex].Prop1 = byte(dw & 0x000000FF)
|
|
||||||
ds1.Tiles[y][x].Floors[floorIndex].Sequence = byte((dw & 0x00003F00) >> 8)
|
|
||||||
ds1.Tiles[y][x].Floors[floorIndex].Unknown1 = byte((dw & 0x000FC000) >> 14)
|
|
||||||
ds1.Tiles[y][x].Floors[floorIndex].Style = byte((dw & 0x03F00000) >> 20)
|
|
||||||
ds1.Tiles[y][x].Floors[floorIndex].Unknown2 = byte((dw & 0x7C000000) >> 26)
|
|
||||||
ds1.Tiles[y][x].Floors[floorIndex].Hidden = byte((dw&0x80000000)>>31) > 0
|
|
||||||
case d2enum.LayerStreamShadow:
|
|
||||||
ds1.Tiles[y][x].Shadows[0].Prop1 = byte(dw & 0x000000FF)
|
|
||||||
ds1.Tiles[y][x].Shadows[0].Sequence = byte((dw & 0x00003F00) >> 8)
|
|
||||||
ds1.Tiles[y][x].Shadows[0].Unknown1 = byte((dw & 0x000FC000) >> 14)
|
|
||||||
ds1.Tiles[y][x].Shadows[0].Style = byte((dw & 0x03F00000) >> 20)
|
|
||||||
ds1.Tiles[y][x].Shadows[0].Unknown2 = byte((dw & 0x7C000000) >> 26)
|
|
||||||
ds1.Tiles[y][x].Shadows[0].Hidden = byte((dw&0x80000000)>>31) > 0
|
|
||||||
case d2enum.LayerStreamSubstitute:
|
|
||||||
ds1.Tiles[y][x].Substitutions[0].Unknown = dw
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ds1.Version >= 2 {
|
|
||||||
numberOfObjects := br.GetInt32()
|
|
||||||
ds1.Objects = make([]d2data.Object, numberOfObjects)
|
|
||||||
for objIdx := 0; objIdx < int(numberOfObjects); objIdx++ {
|
|
||||||
newObject := d2data.Object{}
|
|
||||||
newObject.Type = int(br.GetInt32())
|
|
||||||
newObject.Id = int(br.GetInt32())
|
|
||||||
newObject.X = int(br.GetInt32())
|
|
||||||
newObject.Y = int(br.GetInt32())
|
|
||||||
newObject.Flags = int(br.GetInt32())
|
|
||||||
newObject.Lookup = d2datadict.LookupObject(int(ds1.Act), newObject.Type, newObject.Id)
|
|
||||||
if newObject.Lookup != nil && newObject.Lookup.ObjectsTxtId != -1 {
|
|
||||||
newObject.ObjectInfo = d2datadict.Objects[newObject.Lookup.ObjectsTxtId]
|
|
||||||
}
|
|
||||||
ds1.Objects[objIdx] = newObject
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ds1.Objects = make([]d2data.Object, 0)
|
|
||||||
}
|
|
||||||
if ds1.Version >= 12 && (ds1.SubstitutionType == 1 || ds1.SubstitutionType == 2) {
|
|
||||||
if ds1.Version >= 18 {
|
|
||||||
br.GetUInt32()
|
|
||||||
}
|
|
||||||
numberOfSubGroups := br.GetInt32()
|
|
||||||
ds1.SubstitutionGroups = make([]SubstitutionGroup, numberOfSubGroups)
|
|
||||||
for subIdx := 0; subIdx < int(numberOfSubGroups); subIdx++ {
|
|
||||||
newSub := SubstitutionGroup{}
|
|
||||||
newSub.TileX = br.GetInt32()
|
|
||||||
newSub.TileY = br.GetInt32()
|
|
||||||
newSub.WidthInTiles = br.GetInt32()
|
|
||||||
newSub.HeightInTiles = br.GetInt32()
|
|
||||||
newSub.Unknown = br.GetInt32()
|
|
||||||
|
|
||||||
ds1.SubstitutionGroups[subIdx] = newSub
|
return layerStream
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
ds1.SubstitutionGroups = make([]SubstitutionGroup, 0)
|
func (ds1 *DS1) loadNPCs(br *d2common.StreamReader) {
|
||||||
}
|
if ds1.Version >= 14 { //nolint:gomnd // Version number
|
||||||
if ds1.Version >= 14 {
|
|
||||||
numberOfNpcs := br.GetInt32()
|
numberOfNpcs := br.GetInt32()
|
||||||
for npcIdx := 0; npcIdx < int(numberOfNpcs); npcIdx++ {
|
for npcIdx := 0; npcIdx < int(numberOfNpcs); npcIdx++ {
|
||||||
numPaths := br.GetInt32()
|
numPaths := br.GetInt32()
|
||||||
npcX := int(br.GetInt32())
|
npcX := int(br.GetInt32())
|
||||||
npcY := int(br.GetInt32())
|
npcY := int(br.GetInt32())
|
||||||
objIdx := -1
|
objIdx := -1
|
||||||
|
|
||||||
for idx, ds1Obj := range ds1.Objects {
|
for idx, ds1Obj := range ds1.Objects {
|
||||||
if ds1Obj.X == npcX && ds1Obj.Y == npcY {
|
if ds1Obj.X == npcX && ds1Obj.Y == npcY {
|
||||||
objIdx = idx
|
objIdx = idx
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if objIdx > -1 {
|
if objIdx > -1 {
|
||||||
|
ds1.loadNpcPaths(br, objIdx, int(numPaths))
|
||||||
|
} else {
|
||||||
|
if ds1.Version >= 15 { //nolint:gomnd // Version number
|
||||||
|
br.SkipBytes(int(numPaths) * 3) //nolint:gomnd // Unknown data
|
||||||
|
} else {
|
||||||
|
br.SkipBytes(int(numPaths) * 2) //nolint:gomnd // Unknown data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ds1 *DS1) loadNpcPaths(br *d2common.StreamReader, objIdx, numPaths int) {
|
||||||
if ds1.Objects[objIdx].Paths == nil {
|
if ds1.Objects[objIdx].Paths == nil {
|
||||||
ds1.Objects[objIdx].Paths = make([]d2common.Path, numPaths)
|
ds1.Objects[objIdx].Paths = make([]d2common.Path, numPaths)
|
||||||
}
|
}
|
||||||
for pathIdx := 0; pathIdx < int(numPaths); pathIdx++ {
|
|
||||||
|
for pathIdx := 0; pathIdx < numPaths; pathIdx++ {
|
||||||
newPath := d2common.Path{}
|
newPath := d2common.Path{}
|
||||||
newPath.X = int(br.GetInt32())
|
newPath.X = int(br.GetInt32())
|
||||||
newPath.Y = int(br.GetInt32())
|
newPath.Y = int(br.GetInt32())
|
||||||
if ds1.Version >= 15 {
|
|
||||||
|
if ds1.Version >= 15 { //nolint:gomnd // Version number
|
||||||
newPath.Action = int(br.GetInt32())
|
newPath.Action = int(br.GetInt32())
|
||||||
}
|
}
|
||||||
|
|
||||||
ds1.Objects[objIdx].Paths[pathIdx] = newPath
|
ds1.Objects[objIdx].Paths[pathIdx] = newPath
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
if ds1.Version >= 15 {
|
|
||||||
br.SkipBytes(int(numPaths) * 3)
|
func (ds1 *DS1) loadLayerStreams(br *d2common.StreamReader, layerStream []d2enum.LayerStreamType) {
|
||||||
} else {
|
var dirLookup = []int32{
|
||||||
br.SkipBytes(int(numPaths) * 2)
|
0x00, 0x01, 0x02, 0x01, 0x02, 0x03, 0x03, 0x05, 0x05, 0x06,
|
||||||
}
|
0x06, 0x07, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
|
||||||
}
|
0x0F, 0x10, 0x11, 0x12, 0x14,
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return ds1, nil
|
for lIdx := range layerStream {
|
||||||
|
layerStreamType := layerStream[lIdx]
|
||||||
|
|
||||||
|
for y := 0; y < int(ds1.Height); y++ {
|
||||||
|
for x := 0; x < int(ds1.Width); x++ {
|
||||||
|
dw := br.GetUInt32()
|
||||||
|
|
||||||
|
switch layerStreamType {
|
||||||
|
case d2enum.LayerStreamWall1, d2enum.LayerStreamWall2, d2enum.LayerStreamWall3, d2enum.LayerStreamWall4:
|
||||||
|
wallIndex := int(layerStreamType) - int(d2enum.LayerStreamWall1)
|
||||||
|
ds1.Tiles[y][x].Walls[wallIndex].Prop1 = byte(dw & 0x000000FF) //nolint:gomnd // Bitmask
|
||||||
|
ds1.Tiles[y][x].Walls[wallIndex].Sequence = byte((dw & 0x00003F00) >> 8) //nolint:gomnd // Bitmask
|
||||||
|
ds1.Tiles[y][x].Walls[wallIndex].Unknown1 = byte((dw & 0x000FC000) >> 14) //nolint:gomnd // Bitmask
|
||||||
|
ds1.Tiles[y][x].Walls[wallIndex].Style = byte((dw & 0x03F00000) >> 20) //nolint:gomnd // Bitmask
|
||||||
|
ds1.Tiles[y][x].Walls[wallIndex].Unknown2 = byte((dw & 0x7C000000) >> 26) //nolint:gomnd // Bitmask
|
||||||
|
ds1.Tiles[y][x].Walls[wallIndex].Hidden = byte((dw&0x80000000)>>31) > 0 //nolint:gomnd // Bitmask
|
||||||
|
case d2enum.LayerStreamOrientation1, d2enum.LayerStreamOrientation2,
|
||||||
|
d2enum.LayerStreamOrientation3, d2enum.LayerStreamOrientation4:
|
||||||
|
wallIndex := int(layerStreamType) - int(d2enum.LayerStreamOrientation1)
|
||||||
|
c := int32(dw & 0x000000FF) //nolint:gomnd // Bitmask
|
||||||
|
|
||||||
|
if ds1.Version < 7 { //nolint:gomnd // Version number
|
||||||
|
if c < int32(len(dirLookup)) {
|
||||||
|
c = dirLookup[c]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ds1.Tiles[y][x].Walls[wallIndex].Type = d2enum.TileType(c)
|
||||||
|
ds1.Tiles[y][x].Walls[wallIndex].Zero = byte((dw & 0xFFFFFF00) >> 8) //nolint:gomnd // Bitmask
|
||||||
|
case d2enum.LayerStreamFloor1, d2enum.LayerStreamFloor2:
|
||||||
|
floorIndex := int(layerStreamType) - int(d2enum.LayerStreamFloor1)
|
||||||
|
ds1.Tiles[y][x].Floors[floorIndex].Prop1 = byte(dw & 0x000000FF) //nolint:gomnd // Bitmask
|
||||||
|
ds1.Tiles[y][x].Floors[floorIndex].Sequence = byte((dw & 0x00003F00) >> 8) //nolint:gomnd // Bitmask
|
||||||
|
ds1.Tiles[y][x].Floors[floorIndex].Unknown1 = byte((dw & 0x000FC000) >> 14) //nolint:gomnd // Bitmask
|
||||||
|
ds1.Tiles[y][x].Floors[floorIndex].Style = byte((dw & 0x03F00000) >> 20) //nolint:gomnd // Bitmask
|
||||||
|
ds1.Tiles[y][x].Floors[floorIndex].Unknown2 = byte((dw & 0x7C000000) >> 26) //nolint:gomnd // Bitmask
|
||||||
|
ds1.Tiles[y][x].Floors[floorIndex].Hidden = byte((dw&0x80000000)>>31) > 0 //nolint:gomnd // Bitmask
|
||||||
|
case d2enum.LayerStreamShadow:
|
||||||
|
ds1.Tiles[y][x].Shadows[0].Prop1 = byte(dw & 0x000000FF) //nolint:gomnd // Bitmask
|
||||||
|
ds1.Tiles[y][x].Shadows[0].Sequence = byte((dw & 0x00003F00) >> 8) //nolint:gomnd // Bitmask
|
||||||
|
ds1.Tiles[y][x].Shadows[0].Unknown1 = byte((dw & 0x000FC000) >> 14) //nolint:gomnd // Bitmask
|
||||||
|
ds1.Tiles[y][x].Shadows[0].Style = byte((dw & 0x03F00000) >> 20) //nolint:gomnd // Bitmask
|
||||||
|
ds1.Tiles[y][x].Shadows[0].Unknown2 = byte((dw & 0x7C000000) >> 26) //nolint:gomnd // Bitmask
|
||||||
|
ds1.Tiles[y][x].Shadows[0].Hidden = byte((dw&0x80000000)>>31) > 0 //nolint:gomnd // Bitmask
|
||||||
|
case d2enum.LayerStreamSubstitute:
|
||||||
|
ds1.Tiles[y][x].Substitutions[0].Unknown = dw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package d2ds1
|
package d2ds1
|
||||||
|
|
||||||
|
// FloorShadowRecord represents a floor or shadow record in a DS1 file.
|
||||||
type FloorShadowRecord struct {
|
type FloorShadowRecord struct {
|
||||||
Prop1 byte
|
Prop1 byte
|
||||||
Sequence byte
|
Sequence byte
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package d2ds1
|
package d2ds1
|
||||||
|
|
||||||
|
// SubstitutionGroup represents a substitution group in a DS1 file.
|
||||||
type SubstitutionGroup struct {
|
type SubstitutionGroup struct {
|
||||||
TileX int32
|
TileX int32
|
||||||
TileY int32
|
TileY int32
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package d2ds1
|
package d2ds1
|
||||||
|
|
||||||
|
// SubstitutionRecord represents a substitution record in a DS1 file.
|
||||||
type SubstitutionRecord struct {
|
type SubstitutionRecord struct {
|
||||||
Unknown uint32
|
Unknown uint32
|
||||||
}
|
}
|
||||||
|
16
d2common/d2fileformats/d2ds1/tile_record.go
Normal file
16
d2common/d2fileformats/d2ds1/tile_record.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package d2ds1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TileRecord represents a tile record in a DS1 file.
|
||||||
|
type TileRecord struct {
|
||||||
|
Floors []FloorShadowRecord // Collection of floor records
|
||||||
|
Walls []WallRecord // Collection of wall records
|
||||||
|
Shadows []FloorShadowRecord // Collection of shadow records
|
||||||
|
Substitutions []SubstitutionRecord // Collection of substitutions
|
||||||
|
|
||||||
|
// This is set and used internally by the engine to determine what region this map is from
|
||||||
|
RegionType d2enum.RegionIdType
|
||||||
|
}
|
@ -1,15 +0,0 @@
|
|||||||
package d2ds1
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
|
||||||
)
|
|
||||||
|
|
||||||
type TileRecord struct {
|
|
||||||
Floors []FloorShadowRecord
|
|
||||||
Walls []WallRecord
|
|
||||||
Shadows []FloorShadowRecord
|
|
||||||
Substitutions []SubstitutionRecord
|
|
||||||
|
|
||||||
// This is set and used internally by the engine to determine what region this map is from
|
|
||||||
RegionType d2enum.RegionIdType
|
|
||||||
}
|
|
@ -1,3 +1,5 @@
|
|||||||
|
// Package d2dt1 provides functionality for loading/processing DT1 files.
|
||||||
|
// https://d2mods.info/forum/viewtopic.php?t=65163
|
||||||
package d2dt1
|
package d2dt1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -6,31 +8,39 @@ import (
|
|||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||||
)
|
)
|
||||||
|
|
||||||
// https://d2mods.info/forum/viewtopic.php?t=65163
|
|
||||||
|
|
||||||
|
|
||||||
|
// DT1
|
||||||
type DT1 struct {
|
type DT1 struct {
|
||||||
Tiles []Tile
|
Tiles []Tile
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BlockDataFormat represents the format of the block data
|
||||||
type BlockDataFormat int16
|
type BlockDataFormat int16
|
||||||
|
|
||||||
const (
|
const (
|
||||||
BlockFormatRLE BlockDataFormat = 0 // Not 1
|
BlockFormatRLE BlockDataFormat = 0 // Specifies the block format is RLE encoded
|
||||||
BlockFormatIsometric BlockDataFormat = 1
|
BlockFormatIsometric BlockDataFormat = 1 // Specifies the block format isometrically encoded
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// LoadDT1 loads a DT1 record
|
||||||
func LoadDT1(fileData []byte) (*DT1, error) {
|
func LoadDT1(fileData []byte) (*DT1, error) {
|
||||||
result := &DT1{}
|
result := &DT1{}
|
||||||
br := d2common.CreateStreamReader(fileData)
|
br := d2common.CreateStreamReader(fileData)
|
||||||
ver1 := br.GetInt32()
|
ver1 := br.GetInt32()
|
||||||
ver2 := br.GetInt32()
|
ver2 := br.GetInt32()
|
||||||
|
|
||||||
if ver1 != 7 || ver2 != 6 {
|
if ver1 != 7 || ver2 != 6 {
|
||||||
return nil, fmt.Errorf("expected to have a version of 7.6, but got %d.%d instead", ver1, ver2)
|
return nil, fmt.Errorf("expected to have a version of 7.6, but got %d.%d instead", ver1, ver2)
|
||||||
}
|
}
|
||||||
br.SkipBytes(260)
|
|
||||||
|
br.SkipBytes(260) //nolint:gomnd // Unknown data
|
||||||
|
|
||||||
numberOfTiles := br.GetInt32()
|
numberOfTiles := br.GetInt32()
|
||||||
br.SetPosition(uint64(br.GetInt32()))
|
br.SetPosition(uint64(br.GetInt32()))
|
||||||
|
|
||||||
result.Tiles = make([]Tile, numberOfTiles)
|
result.Tiles = make([]Tile, numberOfTiles)
|
||||||
|
|
||||||
for tileIdx := range result.Tiles {
|
for tileIdx := range result.Tiles {
|
||||||
newTile := Tile{}
|
newTile := Tile{}
|
||||||
newTile.Direction = br.GetInt32()
|
newTile.Direction = br.GetInt32()
|
||||||
@ -38,46 +48,64 @@ func LoadDT1(fileData []byte) (*DT1, error) {
|
|||||||
newTile.MaterialFlags = NewMaterialFlags(br.GetUInt16())
|
newTile.MaterialFlags = NewMaterialFlags(br.GetUInt16())
|
||||||
newTile.Height = br.GetInt32()
|
newTile.Height = br.GetInt32()
|
||||||
newTile.Width = br.GetInt32()
|
newTile.Width = br.GetInt32()
|
||||||
br.SkipBytes(4)
|
|
||||||
|
br.SkipBytes(4) //nolint:gomnd // Unknown data
|
||||||
|
|
||||||
newTile.Type = br.GetInt32()
|
newTile.Type = br.GetInt32()
|
||||||
newTile.Style = br.GetInt32()
|
newTile.Style = br.GetInt32()
|
||||||
newTile.Sequence = br.GetInt32()
|
newTile.Sequence = br.GetInt32()
|
||||||
newTile.RarityFrameIndex = br.GetInt32()
|
newTile.RarityFrameIndex = br.GetInt32()
|
||||||
br.SkipBytes(4)
|
|
||||||
|
br.SkipBytes(4) //nolint:gomnd // Unknown data
|
||||||
|
|
||||||
for i := range newTile.SubTileFlags {
|
for i := range newTile.SubTileFlags {
|
||||||
newTile.SubTileFlags[i] = NewSubTileFlags(br.GetByte())
|
newTile.SubTileFlags[i] = NewSubTileFlags(br.GetByte())
|
||||||
}
|
}
|
||||||
br.SkipBytes(7)
|
|
||||||
|
br.SkipBytes(7) //nolint:gomnd // Unknown data
|
||||||
|
|
||||||
newTile.blockHeaderPointer = br.GetInt32()
|
newTile.blockHeaderPointer = br.GetInt32()
|
||||||
newTile.blockHeaderSize = br.GetInt32()
|
newTile.blockHeaderSize = br.GetInt32()
|
||||||
newTile.Blocks = make([]Block, br.GetInt32())
|
newTile.Blocks = make([]Block, br.GetInt32())
|
||||||
br.SkipBytes(12)
|
|
||||||
|
br.SkipBytes(12) //nolint:gomnd // Unknown data
|
||||||
|
|
||||||
result.Tiles[tileIdx] = newTile
|
result.Tiles[tileIdx] = newTile
|
||||||
}
|
}
|
||||||
for tileIdx, tile := range result.Tiles {
|
|
||||||
|
for tileIdx := range result.Tiles {
|
||||||
|
tile := &result.Tiles[tileIdx]
|
||||||
br.SetPosition(uint64(tile.blockHeaderPointer))
|
br.SetPosition(uint64(tile.blockHeaderPointer))
|
||||||
|
|
||||||
for blockIdx := range tile.Blocks {
|
for blockIdx := range tile.Blocks {
|
||||||
result.Tiles[tileIdx].Blocks[blockIdx].X = br.GetInt16()
|
result.Tiles[tileIdx].Blocks[blockIdx].X = br.GetInt16()
|
||||||
result.Tiles[tileIdx].Blocks[blockIdx].Y = br.GetInt16()
|
result.Tiles[tileIdx].Blocks[blockIdx].Y = br.GetInt16()
|
||||||
br.SkipBytes(2)
|
|
||||||
|
br.SkipBytes(2) //nolint:gomnd // Unknown data
|
||||||
|
|
||||||
result.Tiles[tileIdx].Blocks[blockIdx].GridX = br.GetByte()
|
result.Tiles[tileIdx].Blocks[blockIdx].GridX = br.GetByte()
|
||||||
result.Tiles[tileIdx].Blocks[blockIdx].GridY = br.GetByte()
|
result.Tiles[tileIdx].Blocks[blockIdx].GridY = br.GetByte()
|
||||||
formatValue := br.GetInt16()
|
formatValue := br.GetInt16()
|
||||||
|
|
||||||
if formatValue == 1 {
|
if formatValue == 1 {
|
||||||
result.Tiles[tileIdx].Blocks[blockIdx].Format = BlockFormatIsometric
|
result.Tiles[tileIdx].Blocks[blockIdx].Format = BlockFormatIsometric
|
||||||
} else {
|
} else {
|
||||||
result.Tiles[tileIdx].Blocks[blockIdx].Format = BlockFormatRLE
|
result.Tiles[tileIdx].Blocks[blockIdx].Format = BlockFormatRLE
|
||||||
}
|
}
|
||||||
|
|
||||||
result.Tiles[tileIdx].Blocks[blockIdx].Length = br.GetInt32()
|
result.Tiles[tileIdx].Blocks[blockIdx].Length = br.GetInt32()
|
||||||
br.SkipBytes(2)
|
|
||||||
|
br.SkipBytes(2) //nolint:gomnd // Unknown data
|
||||||
|
|
||||||
result.Tiles[tileIdx].Blocks[blockIdx].FileOffset = br.GetInt32()
|
result.Tiles[tileIdx].Blocks[blockIdx].FileOffset = br.GetInt32()
|
||||||
}
|
}
|
||||||
|
|
||||||
for blockIndex, block := range tile.Blocks {
|
for blockIndex, block := range tile.Blocks {
|
||||||
br.SetPosition(uint64(tile.blockHeaderPointer + block.FileOffset))
|
br.SetPosition(uint64(tile.blockHeaderPointer + block.FileOffset))
|
||||||
encodedData := br.ReadBytes(int(block.Length))
|
encodedData := br.ReadBytes(int(block.Length))
|
||||||
tile.Blocks[blockIndex].EncodedData = encodedData
|
tile.Blocks[blockIndex].EncodedData = encodedData
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
@ -247,7 +247,7 @@ func hashString(key string, hashType uint32) uint32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetFileBlockData gets a block table entry
|
// GetFileBlockData gets a block table entry
|
||||||
func (v MPQ) getFileBlockData(fileName string) (BlockTableEntry, error) {
|
func (v *MPQ) getFileBlockData(fileName string) (BlockTableEntry, error) {
|
||||||
fileEntry, found := v.HashEntryMap.Find(fileName)
|
fileEntry, found := v.HashEntryMap.Find(fileName)
|
||||||
if !found || fileEntry.BlockIndex >= uint32(len(v.BlockTableEntries)) {
|
if !found || fileEntry.BlockIndex >= uint32(len(v.BlockTableEntries)) {
|
||||||
return BlockTableEntry{}, errors.New("file not found")
|
return BlockTableEntry{}, errors.New("file not found")
|
||||||
@ -263,12 +263,12 @@ func (v *MPQ) Close() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v MPQ) FileExists(fileName string) bool {
|
func (v *MPQ) FileExists(fileName string) bool {
|
||||||
return v.HashEntryMap.Contains(fileName)
|
return v.HashEntryMap.Contains(fileName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadFile reads a file from the MPQ and returns a memory stream
|
// ReadFile reads a file from the MPQ and returns a memory stream
|
||||||
func (v MPQ) ReadFile(fileName string) ([]byte, error) {
|
func (v *MPQ) ReadFile(fileName string) ([]byte, error) {
|
||||||
fileBlockData, err := v.getFileBlockData(fileName)
|
fileBlockData, err := v.getFileBlockData(fileName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []byte{}, err
|
return []byte{}, err
|
||||||
@ -285,7 +285,7 @@ func (v MPQ) ReadFile(fileName string) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ReadTextFile reads a file and returns it as a string
|
// ReadTextFile reads a file and returns it as a string
|
||||||
func (v MPQ) ReadTextFile(fileName string) (string, error) {
|
func (v *MPQ) ReadTextFile(fileName string) (string, error) {
|
||||||
data, err := v.ReadFile(fileName)
|
data, err := v.ReadFile(fileName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@ -303,7 +303,7 @@ func (v *BlockTableEntry) calculateEncryptionSeed() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetFileList returns the list of files in this MPQ
|
// GetFileList returns the list of files in this MPQ
|
||||||
func (v MPQ) GetFileList() ([]string, error) {
|
func (v * MPQ) GetFileList() ([]string, error) {
|
||||||
data, err := v.ReadFile("(listfile)")
|
data, err := v.ReadFile("(listfile)")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -17,19 +17,19 @@ import (
|
|||||||
|
|
||||||
// Stream represents a stream of data in an MPQ archive
|
// Stream represents a stream of data in an MPQ archive
|
||||||
type Stream struct {
|
type Stream struct {
|
||||||
MPQData MPQ
|
|
||||||
BlockTableEntry BlockTableEntry
|
BlockTableEntry BlockTableEntry
|
||||||
FileName string
|
|
||||||
EncryptionSeed uint32
|
|
||||||
BlockPositions []uint32
|
BlockPositions []uint32
|
||||||
CurrentPosition uint32
|
|
||||||
CurrentData []byte
|
CurrentData []byte
|
||||||
|
FileName string
|
||||||
|
MPQData *MPQ
|
||||||
|
EncryptionSeed uint32
|
||||||
|
CurrentPosition uint32
|
||||||
CurrentBlockIndex uint32
|
CurrentBlockIndex uint32
|
||||||
BlockSize uint32
|
BlockSize uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateStream creates an MPQ stream
|
// CreateStream creates an MPQ stream
|
||||||
func CreateStream(mpq MPQ, blockTableEntry BlockTableEntry, fileName string) (*Stream, error) {
|
func CreateStream(mpq *MPQ, blockTableEntry BlockTableEntry, fileName string) (*Stream, error) {
|
||||||
result := &Stream{
|
result := &Stream{
|
||||||
MPQData: mpq,
|
MPQData: mpq,
|
||||||
BlockTableEntry: blockTableEntry,
|
BlockTableEntry: blockTableEntry,
|
||||||
|
@ -6,19 +6,19 @@ import (
|
|||||||
|
|
||||||
// Configuration defines the configuration for the engine, loaded from config.json
|
// Configuration defines the configuration for the engine, loaded from config.json
|
||||||
type Configuration struct {
|
type Configuration struct {
|
||||||
|
MpqLoadOrder []string
|
||||||
Language string
|
Language string
|
||||||
FullScreen bool
|
MpqPath string
|
||||||
RunInBackground bool
|
|
||||||
TicksPerSecond int
|
TicksPerSecond int
|
||||||
FpsCap int
|
FpsCap int
|
||||||
VsyncEnabled bool
|
|
||||||
MpqPath string
|
|
||||||
MpqLoadOrder []string
|
|
||||||
SfxVolume float64
|
SfxVolume float64
|
||||||
BgmVolume float64
|
BgmVolume float64
|
||||||
|
FullScreen bool
|
||||||
|
RunInBackground bool
|
||||||
|
VsyncEnabled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var singleton *Configuration = getDefaultConfig()
|
var singleton = getDefaultConfig()
|
||||||
|
|
||||||
func Load() error {
|
func Load() error {
|
||||||
configPaths := []string{
|
configPaths := []string{
|
||||||
@ -57,11 +57,6 @@ func Save() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func Set(config Configuration) {
|
|
||||||
temp := config
|
|
||||||
singleton = &temp
|
|
||||||
}
|
|
||||||
|
|
||||||
func Get() Configuration {
|
func Get() Configuration {
|
||||||
if singleton == nil {
|
if singleton == nil {
|
||||||
panic("configuration is not initialized")
|
panic("configuration is not initialized")
|
||||||
|
@ -12,13 +12,11 @@ import (
|
|||||||
|
|
||||||
type manager struct {
|
type manager struct {
|
||||||
layout *Layout
|
layout *Layout
|
||||||
|
|
||||||
cursorAnim *d2asset.Animation
|
cursorAnim *d2asset.Animation
|
||||||
cursorX int
|
cursorX int
|
||||||
cursorY int
|
cursorY int
|
||||||
cursorVisible bool
|
|
||||||
|
|
||||||
loadingAnim *d2asset.Animation
|
loadingAnim *d2asset.Animation
|
||||||
|
cursorVisible bool
|
||||||
loading bool
|
loading bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ func (ae *AnimatedEntity) Render(target d2render.Surface) {
|
|||||||
ae.animation.Render(target)
|
ae.animation.Render(target)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ae AnimatedEntity) GetDirection() int {
|
func (ae *AnimatedEntity) GetDirection() int {
|
||||||
return ae.direction
|
return ae.direction
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user