mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2025-02-20 23:47:16 -05:00
Resolved most lint errors in d2data and d2datadict (#499)
* adding comments to d2interface for linter * moved d2render renderer interfaces and types into d2interface * fixed most lint errors for monstats loader * de-lint d2data wip * d2data: resolve linting errors
This commit is contained in:
parent
691368cba7
commit
4938ec1f44
@ -20,12 +20,13 @@ type AnimationDataRecord struct {
|
||||
}
|
||||
|
||||
// AnimationData represents all of the animation data records, mapped by the COF index
|
||||
var AnimationData map[string][]*AnimationDataRecord
|
||||
var AnimationData map[string][]*AnimationDataRecord //nolint:gochecknoglobals // Currently global by design
|
||||
|
||||
// LoadAnimationData loads the animation data table into the global AnimationData dictionary
|
||||
func LoadAnimationData(rawData []byte) {
|
||||
AnimationData = make(map[string][]*AnimationDataRecord)
|
||||
streamReader := d2common.CreateStreamReader(rawData)
|
||||
|
||||
for !streamReader.Eof() {
|
||||
dataCount := int(streamReader.GetInt32())
|
||||
for i := 0; i < dataCount; i++ {
|
||||
@ -37,11 +38,14 @@ func LoadAnimationData(rawData []byte) {
|
||||
}
|
||||
data.Flags = streamReader.ReadBytes(144)
|
||||
cofIndex := strings.ToLower(data.COFName)
|
||||
|
||||
if _, found := AnimationData[cofIndex]; !found {
|
||||
AnimationData[cofIndex] = make([]*AnimationDataRecord, 0)
|
||||
}
|
||||
|
||||
AnimationData[cofIndex] = append(AnimationData[cofIndex], data)
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("Loaded %d animation data records", len(AnimationData))
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
//
|
||||
// MpqHuffman.go based on the origina CS file
|
||||
// Package d2compression is used for decompressing WAV files.
|
||||
package d2compression
|
||||
|
||||
// MpqHuffman.go based on the original CS file
|
||||
//
|
||||
// Authors:
|
||||
// Foole (fooleau@gmail.com)
|
||||
@ -27,7 +29,6 @@
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
package d2compression
|
||||
|
||||
import (
|
||||
"log"
|
||||
@ -37,57 +38,60 @@ import (
|
||||
|
||||
// linkedNode is a node which is both hierachcical (parent/child) and doubly linked (next/prev)
|
||||
type linkedNode struct {
|
||||
DecompressedValue int
|
||||
Weight int
|
||||
Parent *linkedNode
|
||||
Child0 *linkedNode
|
||||
Prev *linkedNode
|
||||
Next *linkedNode
|
||||
decompressedValue int
|
||||
weight int
|
||||
parent *linkedNode
|
||||
child0 *linkedNode
|
||||
prev *linkedNode
|
||||
next *linkedNode
|
||||
}
|
||||
|
||||
func CreateLinkedNode(decompVal, weight int) *linkedNode {
|
||||
// createLinkedNode creates a linked node
|
||||
func createLinkedNode(decompVal, weight int) *linkedNode {
|
||||
result := &linkedNode{
|
||||
DecompressedValue: decompVal,
|
||||
Weight: weight,
|
||||
decompressedValue: decompVal,
|
||||
weight: weight,
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (v *linkedNode) GetChild1() *linkedNode {
|
||||
return v.Child0.Prev
|
||||
func (v *linkedNode) getChild1() *linkedNode {
|
||||
return v.child0.prev
|
||||
}
|
||||
|
||||
func (v *linkedNode) Insert(other *linkedNode) *linkedNode {
|
||||
// 'Next' should have a lower weight we should return the lower weight
|
||||
if other.Weight <= v.Weight {
|
||||
func (v *linkedNode) insert(other *linkedNode) *linkedNode {
|
||||
// 'next' should have a lower weight we should return the lower weight
|
||||
if other.weight <= v.weight {
|
||||
// insert before
|
||||
if v.Next != nil {
|
||||
v.Next.Prev = other
|
||||
other.Next = v.Next
|
||||
if v.next != nil {
|
||||
v.next.prev = other
|
||||
other.next = v.next
|
||||
}
|
||||
|
||||
v.Next = other
|
||||
v.next = other
|
||||
|
||||
other.Prev = v
|
||||
other.prev = v
|
||||
|
||||
return other
|
||||
}
|
||||
|
||||
if v.Prev == nil {
|
||||
// Insert after
|
||||
other.Prev = nil
|
||||
v.Prev = other
|
||||
other.Next = v
|
||||
if v.prev == nil {
|
||||
// insert after
|
||||
other.prev = nil
|
||||
v.prev = other
|
||||
other.next = v
|
||||
} else {
|
||||
v.Prev.Insert(other)
|
||||
v.prev.insert(other)
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
//nolint:funlen // Makes no sense to split
|
||||
func getPrimes() [][]byte {
|
||||
return [][]byte{
|
||||
{
|
||||
{ //nolint:dupl we're not interested in duplicates here
|
||||
// Compression type 0
|
||||
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,
|
||||
@ -106,7 +110,7 @@ func getPrimes() [][]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, 0x02,
|
||||
},
|
||||
{
|
||||
{ //nolint:dupl we're not interested in duplicates here
|
||||
// Compression type 1
|
||||
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,
|
||||
@ -125,7 +129,7 @@ func getPrimes() [][]byte {
|
||||
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,
|
||||
}, {
|
||||
// Compression type 2
|
||||
// Compression type 2 //nolint:dupl
|
||||
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,
|
||||
0xFF, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x06, 0x0E, 0x10, 0x04,
|
||||
@ -134,8 +138,8 @@ func getPrimes() [][]byte {
|
||||
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,
|
||||
0x10, 0x01, 0x23, 0x23, 0x2F, 0x10, 0x06, 0x07, 0x02, 0x09, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
}, {
|
||||
// Compression type 3
|
||||
}, { //nolint:dupl we're not interested in duplicates here
|
||||
// Compression type 3 //nolint:dupl
|
||||
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,
|
||||
0x05, 0x01, 0x01, 0x01, 0x0D, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
@ -152,14 +156,15 @@ func getPrimes() [][]byte {
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x07, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01,
|
||||
0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x11,
|
||||
}, { // Compression type 4
|
||||
}, { // Compression type 4 //nolint:dupl
|
||||
0xFF, 0xFB, 0x98, 0x9A, 0x84, 0x85, 0x63, 0x64, 0x3E, 0x3E, 0x22, 0x22, 0x13, 0x13, 0x18, 0x17,
|
||||
}, { // Compression type 5
|
||||
}, { // Compression type 5 //nolint:dupl
|
||||
0xFF, 0xF1, 0x9D, 0x9E, 0x9A, 0x9B, 0x9A, 0x97, 0x93, 0x93, 0x8C, 0x8E, 0x86, 0x88, 0x80, 0x82,
|
||||
0x7C, 0x7C, 0x72, 0x73, 0x69, 0x6B, 0x5F, 0x60, 0x55, 0x56, 0x4A, 0x4B, 0x40, 0x41, 0x37, 0x37,
|
||||
0x2F, 0x2F, 0x27, 0x27, 0x21, 0x21, 0x1B, 0x1C, 0x17, 0x17, 0x13, 0x13, 0x10, 0x10, 0x0D, 0x0D,
|
||||
0x0B, 0x0B, 0x09, 0x09, 0x08, 0x08, 0x07, 0x07, 0x06, 0x05, 0x05, 0x04, 0x04, 0x04, 0x19, 0x18,
|
||||
}, { // Compression type 6
|
||||
}, { //nolint:dupl we're not interested in duplicates here
|
||||
// Compression type 6
|
||||
0xC3, 0xCB, 0xF5, 0x41, 0xFF, 0x7B, 0xF7, 0x21, 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,
|
||||
@ -169,7 +174,8 @@ func getPrimes() [][]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,
|
||||
0x7A, 0x46,
|
||||
}, { // Compression type 7
|
||||
}, { //nolint:dupl we're not interested in duplicates here
|
||||
// Compression type 7
|
||||
0xC3, 0xD9, 0xEF, 0x3D, 0xF9, 0x7C, 0xE9, 0x1E, 0xFD, 0xAB, 0xF1, 0x2C, 0xFC, 0x5B, 0xFE, 0x17,
|
||||
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,
|
||||
@ -196,30 +202,36 @@ func getPrimes() [][]byte {
|
||||
func decode(input *d2common.BitStream, head *linkedNode) *linkedNode {
|
||||
node := head
|
||||
|
||||
for node.Child0 != nil {
|
||||
for node.child0 != nil {
|
||||
bit := input.ReadBits(1)
|
||||
if bit == -1 {
|
||||
log.Fatal("unexpected end of file")
|
||||
}
|
||||
|
||||
if bit == 0 {
|
||||
node = node.Child0
|
||||
node = node.child0
|
||||
continue
|
||||
}
|
||||
|
||||
node = node.GetChild1()
|
||||
node = node.getChild1()
|
||||
}
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
// TODO: these consts for buildList need better names
|
||||
const (
|
||||
decompVal1 = 256
|
||||
decompVal2 = 257
|
||||
)
|
||||
|
||||
func buildList(primeData []byte) *linkedNode {
|
||||
root := CreateLinkedNode(256, 1)
|
||||
root = root.Insert(CreateLinkedNode(257, 1))
|
||||
root := createLinkedNode(decompVal1, 1)
|
||||
root = root.insert(createLinkedNode(decompVal2, 1))
|
||||
|
||||
for i := 0; i < len(primeData); i++ {
|
||||
if primeData[i] != 0 {
|
||||
root = root.Insert(CreateLinkedNode(i, int(primeData[i])))
|
||||
root = root.insert(createLinkedNode(i, int(primeData[i])))
|
||||
}
|
||||
}
|
||||
|
||||
@ -228,20 +240,20 @@ func buildList(primeData []byte) *linkedNode {
|
||||
|
||||
func insertNode(tail *linkedNode, decomp int) *linkedNode {
|
||||
parent := tail
|
||||
result := tail.Prev // This will be the new tail after the tree is updated
|
||||
result := tail.prev // This will be the new tail after the tree is updated
|
||||
|
||||
temp := CreateLinkedNode(parent.DecompressedValue, parent.Weight)
|
||||
temp.Parent = parent
|
||||
temp := createLinkedNode(parent.decompressedValue, parent.weight)
|
||||
temp.parent = parent
|
||||
|
||||
newnode := CreateLinkedNode(decomp, 0)
|
||||
newnode.Parent = parent
|
||||
newnode := createLinkedNode(decomp, 0)
|
||||
newnode.parent = parent
|
||||
|
||||
parent.Child0 = newnode
|
||||
parent.child0 = newnode
|
||||
|
||||
tail.Next = temp
|
||||
temp.Prev = tail
|
||||
newnode.Prev = temp
|
||||
temp.Next = newnode
|
||||
tail.next = temp
|
||||
temp.prev = tail
|
||||
newnode.prev = temp
|
||||
temp.next = newnode
|
||||
|
||||
adjustTree(newnode)
|
||||
|
||||
@ -257,7 +269,7 @@ func adjustTree(newNode *linkedNode) {
|
||||
current := newNode
|
||||
|
||||
for current != nil {
|
||||
current.Weight++
|
||||
current.weight++
|
||||
|
||||
var insertpoint *linkedNode
|
||||
|
||||
@ -267,12 +279,12 @@ func adjustTree(newNode *linkedNode) {
|
||||
insertpoint = current
|
||||
|
||||
for {
|
||||
prev = insertpoint.Prev
|
||||
prev = insertpoint.prev
|
||||
if prev == nil {
|
||||
break
|
||||
}
|
||||
|
||||
if prev.Weight >= current.Weight {
|
||||
if prev.weight >= current.weight {
|
||||
break
|
||||
}
|
||||
|
||||
@ -281,60 +293,60 @@ func adjustTree(newNode *linkedNode) {
|
||||
|
||||
// No insertion point found
|
||||
if insertpoint == current {
|
||||
current = current.Parent
|
||||
current = current.parent
|
||||
continue
|
||||
}
|
||||
|
||||
// The following code basically swaps insertpoint with current
|
||||
|
||||
// remove insert point
|
||||
if insertpoint.Prev != nil {
|
||||
insertpoint.Prev.Next = insertpoint.Next
|
||||
if insertpoint.prev != nil {
|
||||
insertpoint.prev.next = insertpoint.next
|
||||
}
|
||||
|
||||
insertpoint.Next.Prev = insertpoint.Prev
|
||||
insertpoint.next.prev = insertpoint.prev
|
||||
|
||||
// Insert insertpoint after current
|
||||
insertpoint.Next = current.Next
|
||||
insertpoint.Prev = current
|
||||
// insert insertpoint after current
|
||||
insertpoint.next = current.next
|
||||
insertpoint.prev = current
|
||||
|
||||
if current.Next != nil {
|
||||
current.Next.Prev = insertpoint
|
||||
if current.next != nil {
|
||||
current.next.prev = insertpoint
|
||||
}
|
||||
|
||||
current.Next = insertpoint
|
||||
current.next = insertpoint
|
||||
|
||||
// remove current
|
||||
current.Prev.Next = current.Next
|
||||
current.Next.Prev = current.Prev
|
||||
current.prev.next = current.next
|
||||
current.next.prev = current.prev
|
||||
|
||||
// insert current after prev
|
||||
if prev == nil {
|
||||
log.Fatal("previous frame not defined!")
|
||||
}
|
||||
|
||||
temp := prev.Next
|
||||
current.Next = temp
|
||||
current.Prev = prev
|
||||
temp.Prev = current
|
||||
prev.Next = current
|
||||
temp := prev.next
|
||||
current.next = temp
|
||||
current.prev = prev
|
||||
temp.prev = current
|
||||
prev.next = current
|
||||
|
||||
// Set up parent/child links
|
||||
currentparent := current.Parent
|
||||
insertparent := insertpoint.Parent
|
||||
currentparent := current.parent
|
||||
insertparent := insertpoint.parent
|
||||
|
||||
if currentparent.Child0 == current {
|
||||
currentparent.Child0 = insertpoint
|
||||
if currentparent.child0 == current {
|
||||
currentparent.child0 = insertpoint
|
||||
}
|
||||
|
||||
if currentparent != insertparent && insertparent.Child0 == insertpoint {
|
||||
insertparent.Child0 = current
|
||||
if currentparent != insertparent && insertparent.child0 == insertpoint {
|
||||
insertparent.child0 = current
|
||||
}
|
||||
|
||||
current.Parent = insertparent
|
||||
insertpoint.Parent = currentparent
|
||||
current.parent = insertparent
|
||||
insertpoint.parent = currentparent
|
||||
|
||||
current = current.Parent
|
||||
current = current.parent
|
||||
}
|
||||
}
|
||||
|
||||
@ -343,24 +355,25 @@ func buildTree(tail *linkedNode) *linkedNode {
|
||||
|
||||
for current != nil {
|
||||
child0 := current
|
||||
child1 := current.Prev
|
||||
child1 := current.prev
|
||||
|
||||
if child1 == nil {
|
||||
break
|
||||
}
|
||||
|
||||
parent := CreateLinkedNode(0, child0.Weight+child1.Weight)
|
||||
parent.Child0 = child0
|
||||
child0.Parent = parent
|
||||
child1.Parent = parent
|
||||
parent := createLinkedNode(0, child0.weight+child1.weight)
|
||||
parent.child0 = child0
|
||||
child0.parent = parent
|
||||
child1.parent = parent
|
||||
|
||||
current.Insert(parent)
|
||||
current = current.Prev.Prev
|
||||
current.insert(parent)
|
||||
current = current.prev.prev
|
||||
}
|
||||
|
||||
return current
|
||||
}
|
||||
|
||||
// HuffmanDecompress decompresses huffman-compressed data
|
||||
func HuffmanDecompress(data []byte) []byte {
|
||||
comptype := data[0]
|
||||
primes := getPrimes()
|
||||
@ -380,7 +393,7 @@ func HuffmanDecompress(data []byte) []byte {
|
||||
Loop:
|
||||
for {
|
||||
node := decode(bitstream, head)
|
||||
decoded = node.DecompressedValue
|
||||
decoded = node.decompressedValue
|
||||
switch decoded {
|
||||
case 256:
|
||||
break Loop
|
||||
|
@ -4,34 +4,36 @@ import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
)
|
||||
|
||||
var sLookup = []int{
|
||||
0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E,
|
||||
0x0010, 0x0011, 0x0013, 0x0015, 0x0017, 0x0019, 0x001C, 0x001F,
|
||||
0x0022, 0x0025, 0x0029, 0x002D, 0x0032, 0x0037, 0x003C, 0x0042,
|
||||
0x0049, 0x0050, 0x0058, 0x0061, 0x006B, 0x0076, 0x0082, 0x008F,
|
||||
0x009D, 0x00AD, 0x00BE, 0x00D1, 0x00E6, 0x00FD, 0x0117, 0x0133,
|
||||
0x0151, 0x0173, 0x0198, 0x01C1, 0x01EE, 0x0220, 0x0256, 0x0292,
|
||||
0x02D4, 0x031C, 0x036C, 0x03C3, 0x0424, 0x048E, 0x0502, 0x0583,
|
||||
0x0610, 0x06AB, 0x0756, 0x0812, 0x08E0, 0x09C3, 0x0ABD, 0x0BD0,
|
||||
0x0CFF, 0x0E4C, 0x0FBA, 0x114C, 0x1307, 0x14EE, 0x1706, 0x1954,
|
||||
0x1BDC, 0x1EA5, 0x21B6, 0x2515, 0x28CA, 0x2CDF, 0x315B, 0x364B,
|
||||
0x3BB9, 0x41B2, 0x4844, 0x4F7E, 0x5771, 0x602F, 0x69CE, 0x7462,
|
||||
0x7FFF,
|
||||
}
|
||||
|
||||
var sLookup2 = []int{
|
||||
-1, 0, -1, 4, -1, 2, -1, 6,
|
||||
-1, 1, -1, 5, -1, 3, -1, 7,
|
||||
-1, 1, -1, 5, -1, 3, -1, 7,
|
||||
-1, 2, -1, 4, -1, 6, -1, 8,
|
||||
}
|
||||
|
||||
func WavDecompress(data []byte, channelCount int) []byte {
|
||||
// WavDecompress decompresses wav files
|
||||
func WavDecompress(data []byte, channelCount int) []byte { //nolint:funlen doesn't make sense to split
|
||||
Array1 := []int{0x2c, 0x2c}
|
||||
Array2 := make([]int, channelCount)
|
||||
|
||||
var sLookup = []int{
|
||||
0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E,
|
||||
0x0010, 0x0011, 0x0013, 0x0015, 0x0017, 0x0019, 0x001C, 0x001F,
|
||||
0x0022, 0x0025, 0x0029, 0x002D, 0x0032, 0x0037, 0x003C, 0x0042,
|
||||
0x0049, 0x0050, 0x0058, 0x0061, 0x006B, 0x0076, 0x0082, 0x008F,
|
||||
0x009D, 0x00AD, 0x00BE, 0x00D1, 0x00E6, 0x00FD, 0x0117, 0x0133,
|
||||
0x0151, 0x0173, 0x0198, 0x01C1, 0x01EE, 0x0220, 0x0256, 0x0292,
|
||||
0x02D4, 0x031C, 0x036C, 0x03C3, 0x0424, 0x048E, 0x0502, 0x0583,
|
||||
0x0610, 0x06AB, 0x0756, 0x0812, 0x08E0, 0x09C3, 0x0ABD, 0x0BD0,
|
||||
0x0CFF, 0x0E4C, 0x0FBA, 0x114C, 0x1307, 0x14EE, 0x1706, 0x1954,
|
||||
0x1BDC, 0x1EA5, 0x21B6, 0x2515, 0x28CA, 0x2CDF, 0x315B, 0x364B,
|
||||
0x3BB9, 0x41B2, 0x4844, 0x4F7E, 0x5771, 0x602F, 0x69CE, 0x7462,
|
||||
0x7FFF,
|
||||
}
|
||||
|
||||
var sLookup2 = []int{
|
||||
-1, 0, -1, 4, -1, 2, -1, 6,
|
||||
-1, 1, -1, 5, -1, 3, -1, 7,
|
||||
-1, 1, -1, 5, -1, 3, -1, 7,
|
||||
-1, 2, -1, 4, -1, 6, -1, 8,
|
||||
}
|
||||
|
||||
input := d2common.CreateStreamReader(data)
|
||||
output := d2common.CreateStreamWriter()
|
||||
|
||||
input.GetByte()
|
||||
|
||||
shift := input.GetByte()
|
||||
@ -43,6 +45,7 @@ func WavDecompress(data []byte, channelCount int) []byte {
|
||||
}
|
||||
|
||||
channel := channelCount - 1
|
||||
|
||||
for input.GetPosition() < input.GetSize() {
|
||||
value := input.GetByte()
|
||||
|
||||
@ -56,12 +59,14 @@ func WavDecompress(data []byte, channelCount int) []byte {
|
||||
if Array1[channel] != 0 {
|
||||
Array1[channel]--
|
||||
}
|
||||
|
||||
output.PushInt16(int16(Array2[channel]))
|
||||
case 1:
|
||||
Array1[channel] += 8
|
||||
if Array1[channel] > 0x58 {
|
||||
Array1[channel] = 0x58
|
||||
}
|
||||
|
||||
if channelCount == 2 {
|
||||
channel = 1 - channel
|
||||
}
|
||||
@ -71,6 +76,7 @@ func WavDecompress(data []byte, channelCount int) []byte {
|
||||
if Array1[channel] < 0 {
|
||||
Array1[channel] = 0
|
||||
}
|
||||
|
||||
if channelCount == 2 {
|
||||
channel = 1 - channel
|
||||
}
|
||||
@ -116,12 +122,11 @@ func WavDecompress(data []byte, channelCount int) []byte {
|
||||
|
||||
if Array1[channel] < 0 {
|
||||
Array1[channel] = 0
|
||||
} else {
|
||||
if Array1[channel] > 0x58 {
|
||||
Array1[channel] = 0x58
|
||||
}
|
||||
} else if Array1[channel] > 0x58 {
|
||||
Array1[channel] = 0x58
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return output.GetBytes()
|
||||
}
|
||||
|
@ -6,9 +6,11 @@ import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||
)
|
||||
|
||||
//nolint:gochecknoglobals // Currently global by design, only written once
|
||||
var Armors map[string]*ItemCommonRecord
|
||||
|
||||
// LoadArmors loads entries from armor.txt as ItemCommonRecords
|
||||
func LoadArmors(file []byte) {
|
||||
Armors = *LoadCommonItems(file, d2enum.InventoryItemTypeArmor)
|
||||
Armors = LoadCommonItems(file, d2enum.InventoryItemTypeArmor)
|
||||
log.Printf("Loaded %d armors", len(Armors))
|
||||
}
|
||||
|
@ -7,6 +7,11 @@ import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
)
|
||||
|
||||
const (
|
||||
expansion = "Expansion" // blizzard put this in the txt where expansion data starts
|
||||
)
|
||||
|
||||
//nolint:gochecknoglobals // Currently global by design, only written once
|
||||
var frameFields = []string{"Cel1", "Cel2", "Cel3", "Cel4"}
|
||||
|
||||
// AutoMapRecord represents one row from d2data.mpq/AutoMap.txt.
|
||||
@ -27,7 +32,6 @@ type AutoMapRecord struct {
|
||||
// StartSequence and EndSequence are sub indices the
|
||||
// same 2D array as Style. They describe a range of
|
||||
// tiles for which covered by this AutoMapRecord.
|
||||
//
|
||||
// In some rows you can find a value of -1. This means
|
||||
// the game will only look at Style and TileName to
|
||||
// determine which tiles are addressed.
|
||||
@ -50,7 +54,6 @@ type AutoMapRecord struct {
|
||||
// re-extract the chart with Dc6Table, you can specify
|
||||
// how many graphics a line can hold), line 1 includes
|
||||
// icons 0-19, line 2 from 20 to 39 etc.
|
||||
//
|
||||
// Multiple values exist for Cel (and Type) to enable
|
||||
// variation. Presumably game chooses randomly between
|
||||
// any of the 4 values which are not set to -1.
|
||||
@ -74,7 +77,7 @@ func LoadAutoMaps(file []byte) {
|
||||
AutoMaps = make([]*AutoMapRecord, len(d.Data))
|
||||
|
||||
for idx := range d.Data {
|
||||
if d.GetString("LevelName", idx) == "Expansion" {
|
||||
if d.GetString("LevelName", idx) == expansion {
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -7,60 +7,65 @@ import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||
)
|
||||
|
||||
// Charecter stats
|
||||
// CharStatsRecord is a struct that represents a single row from charstats.txt
|
||||
type CharStatsRecord struct {
|
||||
Class d2enum.Hero
|
||||
|
||||
// the initial stats at character level 1
|
||||
InitStr int
|
||||
InitDex int
|
||||
InitVit int
|
||||
InitEne int
|
||||
InitStamina int
|
||||
InitStr int // initial strength
|
||||
InitDex int // initial dexterity
|
||||
InitVit int // initial vitality
|
||||
InitEne int // initial energy
|
||||
InitStamina int // initial stamina
|
||||
|
||||
ManaRegen int // number of seconds to regen mana completely
|
||||
ToHitFactor int // added to basic AR of character class
|
||||
|
||||
VelocityWalk int
|
||||
VelocityRun int
|
||||
VelocityWalk int // velocity of the character while walking
|
||||
VelocityRun int // velocity of the character while running
|
||||
StaminaRunDrain int // rate of stamina loss, lower is longer drain time
|
||||
|
||||
// NOTE: Each point of Life/Mana/Stamina is divided by 256 for precision.
|
||||
LifePerLevel int // value is in fourths, lowest possible is 64/256
|
||||
ManaPerLevel int
|
||||
StaminaPerLevel int
|
||||
// value is in fourths, lowest possible is 64/256
|
||||
LifePerLevel int // amount of life per character level
|
||||
ManaPerLevel int // amount of mana per character level
|
||||
StaminaPerLevel int // amount of stamina per character level
|
||||
|
||||
LifePerVit int // life per point of vitality
|
||||
ManaPerEne int // mana per point of energy
|
||||
StaminaPerVit int
|
||||
StaminaPerVit int // stamina per point of vitality
|
||||
|
||||
StatPerLevel int // stat points per level
|
||||
StatPerLevel int // amount of stat points per level
|
||||
|
||||
BlockFactor int // added to base shield block% in armor.txt (display & calc)
|
||||
|
||||
// appears on starting weapon
|
||||
StartSkillBonus string
|
||||
StartSkillBonus string // a key that points to a property
|
||||
|
||||
// The skills the character class starts with (always available)
|
||||
BaseSkill [10]string
|
||||
BaseSkill [10]string // the base skill keys of the character, always available
|
||||
|
||||
// string for bonus to class skills (ex: +1 to all Amazon skills).
|
||||
SkillStrAll string
|
||||
SkillStrAll string // string for bonus to all skills
|
||||
SkillStrTab [3]string // string for bonus per skill tabs
|
||||
SkillStrClassOnly string // string for class-exclusive skills
|
||||
|
||||
BaseWeaponClass d2enum.WeaponClass // controls animation when unarmed
|
||||
|
||||
StartItem [10]string
|
||||
StartItemLocation [10]string
|
||||
StartItemCount [10]int
|
||||
StartItem [10]string // tokens for the starting items
|
||||
StartItemLocation [10]string // locations of the starting items
|
||||
StartItemCount [10]int // amount of the starting items
|
||||
}
|
||||
|
||||
// CharStats holds all of the CharStatsRecords
|
||||
//nolint:gochecknoglobals // Currently global by design, only written once
|
||||
var CharStats map[d2enum.Hero]*CharStatsRecord
|
||||
var charStringMap map[string]d2enum.Hero
|
||||
var weaponTokenMap map[string]d2enum.WeaponClass
|
||||
var charStringMap map[string]d2enum.Hero //nolint:gochecknoglobals // Currently global by design
|
||||
var weaponTokenMap map[string]d2enum.WeaponClass //nolint:gochecknoglobals // Currently global by design
|
||||
|
||||
// LoadCharStats loads charstats.txt file contents into map[d2enum.Hero]*CharStatsRecord
|
||||
//nolint:funlen // Makes no sense to split
|
||||
// LoadCharStats loads charstats.txt file contents into map[d2enum.Hero]*CharStatsRecord
|
||||
func LoadCharStats(file []byte) {
|
||||
charStringMap = map[string]d2enum.Hero{
|
||||
"Amazon": d2enum.HeroAmazon,
|
||||
|
@ -140,22 +140,26 @@ type CubeRecipeItemProperty struct {
|
||||
}
|
||||
|
||||
// CubeRecipes contains all rows in CubeMain.txt.
|
||||
//nolint:gochecknoglobals // Currently global by design, only written once
|
||||
var CubeRecipes []*CubeRecipeRecord
|
||||
|
||||
// There are repeated fields and sections in this file, some
|
||||
// of which have inconsistent naming conventions. These slices
|
||||
// are a simple way to handle them.
|
||||
var outputFields = []string{"output", "output b", "output c"}
|
||||
var outputLabels = []string{"", "b ", "c "}
|
||||
var propLabels = []string{"mod 1", "mod 2", "mod 3", "mod 4", "mod 5"}
|
||||
var inputFields = []string{"input 1", "input 2", "input 3", "input 4", "input 5", "input 6", "input 7"}
|
||||
|
||||
// LoadCubeRecipes populates CubeRecipes with
|
||||
// the data from CubeMain.txt.
|
||||
func LoadCubeRecipes(file []byte) {
|
||||
// Load data
|
||||
d := d2common.LoadDataDictionary(string(file))
|
||||
|
||||
// There are repeated fields and sections in this file, some
|
||||
// of which have inconsistent naming conventions. These slices
|
||||
// are a simple way to handle them.
|
||||
var outputFields = []string{"output", "output b", "output c"}
|
||||
|
||||
var outputLabels = []string{"", "b ", "c "}
|
||||
|
||||
var propLabels = []string{"mod 1", "mod 2", "mod 3", "mod 4", "mod 5"}
|
||||
|
||||
var inputFields = []string{"input 1", "input 2", "input 3", "input 4", "input 5", "input 6", "input 7"}
|
||||
|
||||
// Create records
|
||||
CubeRecipes = make([]*CubeRecipeRecord, len(d.Data))
|
||||
for idx := range d.Data {
|
||||
|
@ -58,7 +58,7 @@ type DifficultyLevelRecord struct {
|
||||
ManaStealDivisor int // ManaStealDivisor
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Gravestench: The rest of these are listed on PK page, but not present in
|
||||
// The rest of these are listed on PK page, but not present in
|
||||
// my copy of the txt file (patch_d2/data/global/excel/difficultylevels.txt)
|
||||
// so I am going to leave these comments
|
||||
|
||||
|
@ -31,49 +31,37 @@ import (
|
||||
10
|
||||
*/
|
||||
|
||||
// ExperienceBreakpointsRecord describes the experience points required to
|
||||
// gain a level for all character classes
|
||||
type ExperienceBreakpointsRecord struct {
|
||||
Level int
|
||||
HeroBreakpoints map[d2enum.Hero]int
|
||||
Ratio int
|
||||
}
|
||||
|
||||
var experienceStringMap map[string]d2enum.Hero
|
||||
var experienceHeroMap map[d2enum.Hero]string
|
||||
|
||||
// ExperienceBreakpoints describes the required experience
|
||||
// for each level for each character class
|
||||
//nolint:gochecknoglobals // Currently global by design, only written once
|
||||
var ExperienceBreakpoints []*ExperienceBreakpointsRecord
|
||||
|
||||
//nolint:gochecknoglobals // Currently global by design
|
||||
var maxLevels map[d2enum.Hero]int
|
||||
|
||||
// GetMaxLevelByHero returns the highest level attainable for a hero type
|
||||
func GetMaxLevelByHero(heroType d2enum.Hero) int {
|
||||
return maxLevels[heroType]
|
||||
}
|
||||
|
||||
// GetExperienceBreakpoint given a hero type and a level, returns the experience required for the level
|
||||
func GetExperienceBreakpoint(heroType d2enum.Hero, level int) int {
|
||||
return ExperienceBreakpoints[level].HeroBreakpoints[heroType]
|
||||
}
|
||||
|
||||
// LoadExperienceBreakpoints loads experience.txt into a map
|
||||
// ExperienceBreakpoints []*ExperienceBreakpointsRecord
|
||||
func LoadExperienceBreakpoints(file []byte) {
|
||||
d := d2common.LoadDataDictionary(string(file))
|
||||
|
||||
experienceStringMap = map[string]d2enum.Hero{
|
||||
"Amazon": d2enum.HeroAmazon,
|
||||
"Barbarian": d2enum.HeroBarbarian,
|
||||
"Druid": d2enum.HeroDruid,
|
||||
"Assassin": d2enum.HeroAssassin,
|
||||
"Necromancer": d2enum.HeroNecromancer,
|
||||
"Paladin": d2enum.HeroPaladin,
|
||||
"Sorceress": d2enum.HeroSorceress,
|
||||
}
|
||||
|
||||
experienceHeroMap = map[d2enum.Hero]string{
|
||||
d2enum.HeroAmazon: "Amazon",
|
||||
d2enum.HeroBarbarian: "Barbarian",
|
||||
d2enum.HeroDruid: "Druid",
|
||||
d2enum.HeroAssassin: "Assassin",
|
||||
d2enum.HeroNecromancer: "Necromancer",
|
||||
d2enum.HeroPaladin: "Paladin",
|
||||
d2enum.HeroSorceress: "Sorceress",
|
||||
}
|
||||
|
||||
// we skip the second row because that describes max level of char classes
|
||||
ExperienceBreakpoints = make([]*ExperienceBreakpointsRecord, len(d.Data)-1)
|
||||
|
||||
|
@ -6,6 +6,8 @@ import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
)
|
||||
|
||||
// GemsRecord is a representation of a single row of gems.txt
|
||||
// it describes the properties of socketable items
|
||||
type GemsRecord struct {
|
||||
Name string
|
||||
Letter string
|
||||
@ -50,13 +52,17 @@ type GemsRecord struct {
|
||||
ShieldMod3Max int
|
||||
}
|
||||
|
||||
// Gems stores all of the GemsRecords
|
||||
var Gems map[string]*GemsRecord //nolint:gochecknoglobals // Currently global by design, only written once
|
||||
|
||||
// LoadGems loads gem records into a map[string]*GemsRecord
|
||||
func LoadGems(file []byte) {
|
||||
d := d2common.LoadDataDictionary(string(file))
|
||||
|
||||
var Gems []*GemsRecord
|
||||
Gems = make(map[string]*GemsRecord, len(d.Data))
|
||||
|
||||
for idx := range d.Data {
|
||||
if d.GetString("name", idx) != "Expansion" {
|
||||
if d.GetString("name", idx) != expansion {
|
||||
/*
|
||||
"Expansion" is the only field in line 36 of /data/global/excel/gems.txt and is only used to visually
|
||||
separate base-game gems and expansion runes.
|
||||
@ -104,7 +110,7 @@ func LoadGems(file []byte) {
|
||||
ShieldMod3Min: d.GetNumber("shieldMod3Min", idx),
|
||||
ShieldMod3Max: d.GetNumber("shieldMod3Max", idx),
|
||||
}
|
||||
Gems = append(Gems, gem)
|
||||
Gems[gem.Name] = gem
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,8 @@ import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
)
|
||||
|
||||
// HirelingRecord is a representation of rows in hireling.txt
|
||||
// these records describe mercenaries
|
||||
type HirelingRecord struct {
|
||||
Hireling string
|
||||
SubType string
|
||||
@ -81,9 +83,15 @@ type HirelingRecord struct {
|
||||
Shield int
|
||||
}
|
||||
|
||||
// Hirelings stores hireling (mercenary) records
|
||||
//nolint:gochecknoglobals // Currently global by design, only written once
|
||||
var Hirelings []*HirelingRecord
|
||||
|
||||
// LoadHireling loads hireling data into []*HirelingRecord
|
||||
func LoadHireling(file []byte) {
|
||||
d := d2common.LoadDataDictionary(string(file))
|
||||
var Hirelings []*HirelingRecord
|
||||
|
||||
Hirelings = make([]*HirelingRecord, len(d.Data))
|
||||
|
||||
for idx := range d.Data {
|
||||
hireling := &HirelingRecord{
|
||||
|
@ -8,13 +8,9 @@ import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||
)
|
||||
|
||||
var MagicPrefixDictionary *d2common.DataDictionary
|
||||
var MagicSuffixDictionary *d2common.DataDictionary
|
||||
|
||||
var MagicPrefixRecords []*ItemAffixCommonRecord
|
||||
var MagicSuffixRecords []*ItemAffixCommonRecord
|
||||
|
||||
var AffixMagicGroups []*ItemAffixCommonGroup
|
||||
// MagicPrefix + MagicSuffix store item affix records
|
||||
var MagicPrefix []*ItemAffixCommonRecord //nolint:gochecknoglobals // Currently global by design
|
||||
var MagicSuffix []*ItemAffixCommonRecord //nolint:gochecknoglobals // Currently global by design
|
||||
|
||||
// LoadMagicPrefix loads MagicPrefix.txt
|
||||
func LoadMagicPrefix(file []byte) {
|
||||
@ -22,7 +18,7 @@ func LoadMagicPrefix(file []byte) {
|
||||
|
||||
subType := d2enum.ItemAffixMagic
|
||||
|
||||
MagicPrefixDictionary, MagicPrefixRecords = loadDictionary(file, superType, subType)
|
||||
MagicPrefix = loadDictionary(file, superType, subType)
|
||||
}
|
||||
|
||||
// LoadMagicSuffix loads MagicSuffix.txt
|
||||
@ -31,11 +27,11 @@ func LoadMagicSuffix(file []byte) {
|
||||
|
||||
subType := d2enum.ItemAffixMagic
|
||||
|
||||
MagicSuffixDictionary, MagicSuffixRecords = loadDictionary(file, superType, subType)
|
||||
MagicSuffix = loadDictionary(file, superType, subType)
|
||||
}
|
||||
|
||||
func getAffixString(t1 d2enum.ItemAffixSuperType, t2 d2enum.ItemAffixSubType) string {
|
||||
var name string = ""
|
||||
var name = ""
|
||||
|
||||
if t2 == d2enum.ItemAffixMagic {
|
||||
name = "Magic"
|
||||
@ -55,58 +51,15 @@ func loadDictionary(
|
||||
file []byte,
|
||||
superType d2enum.ItemAffixSuperType,
|
||||
subType d2enum.ItemAffixSubType,
|
||||
) (*d2common.DataDictionary, []*ItemAffixCommonRecord) {
|
||||
) []*ItemAffixCommonRecord {
|
||||
dict := d2common.LoadDataDictionary(string(file))
|
||||
records := createItemAffixRecords(dict, superType, subType)
|
||||
name := getAffixString(superType, subType)
|
||||
log.Printf("Loaded %d %s records", len(dict.Data), name)
|
||||
|
||||
return dict, records
|
||||
return records
|
||||
}
|
||||
|
||||
// --- column names from d2exp.mpq:/data/globa/excel/MagicPrefix.txt
|
||||
// Name
|
||||
// version
|
||||
// spawnable
|
||||
// rare
|
||||
// level
|
||||
// maxlevel
|
||||
// levelreq
|
||||
// classspecific
|
||||
// class
|
||||
// classlevelreq
|
||||
// frequency
|
||||
// group
|
||||
// mod1code
|
||||
// mod1param
|
||||
// mod1min
|
||||
// mod1max
|
||||
// mod2code
|
||||
// mod2param
|
||||
// mod2min
|
||||
// mod2max
|
||||
// mod3code
|
||||
// mod3param
|
||||
// mod3min
|
||||
// mod3max
|
||||
// transform
|
||||
// transformcolor
|
||||
// itype1
|
||||
// itype2
|
||||
// itype3
|
||||
// itype4
|
||||
// itype5
|
||||
// itype6
|
||||
// itype7
|
||||
// etype1
|
||||
// etype2
|
||||
// etype3
|
||||
// etype4
|
||||
// etype5
|
||||
// divide
|
||||
// multiply
|
||||
// add
|
||||
|
||||
func createItemAffixRecords(
|
||||
d *d2common.DataDictionary,
|
||||
superType d2enum.ItemAffixSuperType,
|
||||
@ -177,7 +130,7 @@ func createItemAffixRecords(
|
||||
}
|
||||
|
||||
group := ItemAffixGroups[affix.GroupID]
|
||||
group.AddMember(affix)
|
||||
group.addMember(affix)
|
||||
|
||||
records = append(records, affix)
|
||||
}
|
||||
@ -185,14 +138,16 @@ func createItemAffixRecords(
|
||||
return records
|
||||
}
|
||||
|
||||
var ItemAffixGroups map[int]*ItemAffixCommonGroup
|
||||
// ItemAffixGroups are groups of MagicPrefix/Suffixes
|
||||
var ItemAffixGroups map[int]*ItemAffixCommonGroup //nolint:gochecknoglobals // Currently global by design
|
||||
|
||||
// ItemAffixCommonGroup is a grouping that is common between prefix/suffix
|
||||
type ItemAffixCommonGroup struct {
|
||||
ID int
|
||||
Members map[string]*ItemAffixCommonRecord
|
||||
}
|
||||
|
||||
func (g *ItemAffixCommonGroup) AddMember(a *ItemAffixCommonRecord) {
|
||||
func (g *ItemAffixCommonGroup) addMember(a *ItemAffixCommonRecord) {
|
||||
if g.Members == nil {
|
||||
g.Members = make(map[string]*ItemAffixCommonRecord)
|
||||
}
|
||||
@ -200,7 +155,7 @@ func (g *ItemAffixCommonGroup) AddMember(a *ItemAffixCommonRecord) {
|
||||
g.Members[a.Name] = a
|
||||
}
|
||||
|
||||
func (g *ItemAffixCommonGroup) GetTotalFrequency() int {
|
||||
func (g *ItemAffixCommonGroup) getTotalFrequency() int {
|
||||
total := 0
|
||||
|
||||
for _, affix := range g.Members {
|
||||
@ -210,6 +165,9 @@ func (g *ItemAffixCommonGroup) GetTotalFrequency() int {
|
||||
return total
|
||||
}
|
||||
|
||||
// ItemAffixCommonModifier is the generic modifier form that prefix/suffix shares
|
||||
// modifiers are like dynamic properties, they have a key that points to a property
|
||||
// a parameter for the property, and a min/max value
|
||||
type ItemAffixCommonModifier struct {
|
||||
Code string
|
||||
Parameter int
|
||||
@ -217,46 +175,49 @@ type ItemAffixCommonModifier struct {
|
||||
Max int
|
||||
}
|
||||
|
||||
// ItemAffixCommonRecord is a common definition that both prefix and suffix use
|
||||
type ItemAffixCommonRecord struct {
|
||||
Name string
|
||||
Group *ItemAffixCommonGroup
|
||||
Modifiers []*ItemAffixCommonModifier
|
||||
|
||||
ItemInclude []string
|
||||
ItemExclude []string
|
||||
|
||||
Name string
|
||||
Class string
|
||||
TransformColor string
|
||||
|
||||
Version int
|
||||
Type d2enum.ItemAffixSubType
|
||||
|
||||
Level int
|
||||
MaxLevel int
|
||||
|
||||
LevelReq int
|
||||
ClassLevelReq int
|
||||
|
||||
Frequency int
|
||||
GroupID int
|
||||
|
||||
PriceAdd int
|
||||
PriceScale int
|
||||
|
||||
IsPrefix bool
|
||||
IsSuffix bool
|
||||
|
||||
Spawnable bool
|
||||
Rare bool
|
||||
|
||||
Level int
|
||||
MaxLevel int
|
||||
|
||||
LevelReq int
|
||||
Class string
|
||||
ClassLevelReq int
|
||||
|
||||
Frequency int
|
||||
GroupID int
|
||||
Group *ItemAffixCommonGroup
|
||||
|
||||
Modifiers []*ItemAffixCommonModifier
|
||||
|
||||
Transform bool
|
||||
TransformColor string
|
||||
|
||||
ItemInclude []string
|
||||
ItemExclude []string
|
||||
|
||||
PriceAdd int
|
||||
PriceScale int
|
||||
Transform bool
|
||||
}
|
||||
|
||||
// ProbabilityToSpawn returns the chance of the affix spawning on an
|
||||
// item with a given quality level
|
||||
func (a *ItemAffixCommonRecord) ProbabilityToSpawn(qlvl int) float64 {
|
||||
if (qlvl > a.MaxLevel) || (qlvl < a.Level) {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
p := float64(a.Frequency) / float64(a.Group.GetTotalFrequency())
|
||||
p := float64(a.Frequency) / float64(a.Group.getTotalFrequency())
|
||||
|
||||
return p
|
||||
}
|
||||
|
@ -9,55 +9,66 @@ import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||
)
|
||||
|
||||
// ItemCommonRecord is a representation of entries from armor.txt, weapons.txt, and misc.txt
|
||||
type ItemCommonRecord struct {
|
||||
Source d2enum.InventoryItemType
|
||||
|
||||
Name string
|
||||
|
||||
Version int // 0 = classic, 100 = expansion
|
||||
CompactSave bool // if true, doesn't store any stats upon saving
|
||||
Rarity int // higher, the rarer
|
||||
Spawnable bool // if 0, cannot spawn in shops
|
||||
UsageStats [3]ItemUsageStat // stat boosts applied upon usage
|
||||
CureOverlayStates [2]string // name of the overlay states that are removed upon use of this item
|
||||
OverlayState string // name of the overlay state to be applied upon use of this item
|
||||
SpellDescriptionString string // points to a string containing the description
|
||||
BetterGem string // 3 char code pointing to the gem this upgrades to (non if not applicable)
|
||||
SpellDescriptionCalc d2common.CalcString // a calc string what value to display
|
||||
WeaponClass string // what kind of attack does this weapon have (i.e. determines attack animations)
|
||||
WeaponClass2Hand string // what kind of attack when wielded with two hands
|
||||
HitClass string // determines sounds/graphic effects when attacking
|
||||
SpecialFeature string // Just a comment
|
||||
FlavorText string // unknown, probably just for reference
|
||||
TransmogCode string // the 3 char code representing the item this becomes via transmog
|
||||
NightmareUpgrade string // upgraded in higher difficulties
|
||||
HellUpgrade string
|
||||
SourceArt string // unused?
|
||||
GameArt string // unused?
|
||||
Vendors map[string]*ItemVendorParams // controls vendor settings
|
||||
Type string // base type in ItemTypes.txt
|
||||
Type2 string
|
||||
DropSound string // sfx for dropping
|
||||
UseSound string // sfx for using
|
||||
FlippyFile string // DC6 file animation to play when item drops on the ground
|
||||
InventoryFile string // DC6 file used in your inventory
|
||||
UniqueInventoryFile string // DC6 file used by the unique version of this item
|
||||
SetInventoryFile string // DC6 file used by the set version of this item
|
||||
Code string // identifies the item
|
||||
NameString string // seems to be identical to code?
|
||||
AlternateGfx string // code of the DCC used when equipped
|
||||
OpenBetaGfx string // unknown
|
||||
NormalCode string
|
||||
UberCode string
|
||||
UltraCode string
|
||||
Name string
|
||||
Source d2enum.InventoryItemType
|
||||
|
||||
Version int // 0 = classic, 100 = expansion
|
||||
Rarity int // higher, the rarer
|
||||
MinAC int
|
||||
MaxAC int
|
||||
Absorbs int // unused?
|
||||
Speed int // affects movement speed of wielder, >0 = you move slower, <0 = you move faster
|
||||
RequiredStrength int
|
||||
Block int // chance to block, capped at 75%
|
||||
Durability int // base durability 0-255
|
||||
NoDurability bool // if true, item has no durability
|
||||
|
||||
Level int // base item level (controls monster drops, for instance a lv20 monster cannot drop a lv30 item)
|
||||
RequiredLevel int // required level to wield
|
||||
Cost int // base cost
|
||||
GambleCost int // for reference only, not used
|
||||
Code string // identifies the item
|
||||
NameString string // seems to be identical to code?
|
||||
MagicLevel int // additional magic level (for gambling?)
|
||||
AutoPrefix int // prefix automatically assigned to this item on spawn, maps to group column of Automagic.txt
|
||||
|
||||
AlternateGfx string // code of the DCC used when equipped
|
||||
OpenBetaGfx string // unknown
|
||||
NormalCode string
|
||||
UberCode string
|
||||
UltraCode string
|
||||
|
||||
SpellOffset int // unknown
|
||||
|
||||
Component int // corresponds to Composit.txt, player animation layer used by this
|
||||
InventoryWidth int
|
||||
InventoryHeight int
|
||||
HasInventory bool // if true, item can store gems or runes
|
||||
GemSockets int // number of gems to store
|
||||
GemApplyType int // what kind of gem effect is applied
|
||||
Block int // chance to block, capped at 75%
|
||||
Durability int // base durability 0-255
|
||||
Level int // base item level (controls monster drops, for instance a lv20 monster cannot drop a lv30 item)
|
||||
RequiredLevel int // required level to wield
|
||||
Cost int // base cost
|
||||
GambleCost int // for reference only, not used
|
||||
MagicLevel int // additional magic level (for gambling?)
|
||||
AutoPrefix int // prefix automatically assigned to this item on spawn, maps to group column of Automagic.txt
|
||||
SpellOffset int // unknown
|
||||
Component int // corresponds to Composit.txt, player animation layer used by this
|
||||
InventoryWidth int
|
||||
InventoryHeight int
|
||||
GemSockets int // number of gems to store
|
||||
GemApplyType int // what kind of gem effect is applied
|
||||
// 0 = weapon, 1= armor or helmet, 2 = shield
|
||||
|
||||
FlippyFile string // DC6 file animation to play when item drops on the ground
|
||||
InventoryFile string // DC6 file used in your inventory
|
||||
UniqueInventoryFile string // DC6 file used by the unique version of this item
|
||||
SetInventoryFile string // DC6 file used by the set version of this item
|
||||
|
||||
// these represent how player animations and graphics change upon wearing this
|
||||
// these come from ArmType.txt
|
||||
AnimRightArm int
|
||||
@ -67,112 +78,74 @@ type ItemCommonRecord struct {
|
||||
AnimRightShoulderPad int
|
||||
AnimLeftShoulderPad int
|
||||
|
||||
Useable bool // can be used via right click if true
|
||||
// game knows what to do if used by item code
|
||||
Throwable bool
|
||||
Stackable bool // can be stacked in inventory
|
||||
MinStack int // min size of stack when item is spawned, used if stackable
|
||||
MaxStack int // max size of stack when item is spawned
|
||||
|
||||
Type string // base type in ItemTypes.txt
|
||||
Type2 string
|
||||
|
||||
DropSound string // sfx for dropping
|
||||
DropSfxFrame int // what frame of drop animation the sfx triggers on
|
||||
UseSound string // sfx for using
|
||||
|
||||
Unique bool // if true, only spawns as unique
|
||||
Transparent bool // unused
|
||||
TransTable int // unknown, related to blending mode?
|
||||
Quivered bool // if true, requires ammo to use
|
||||
LightRadius int // apparently unused
|
||||
Belt bool // tells what kind of belt this item is
|
||||
|
||||
Quest int // indicates that this item belongs to a given quest?
|
||||
|
||||
MinStack int // min size of stack when item is spawned, used if stackable
|
||||
MaxStack int // max size of stack when item is spawned
|
||||
DropSfxFrame int // what frame of drop animation the sfx triggers on
|
||||
TransTable int // unknown, related to blending mode?
|
||||
LightRadius int // apparently unused
|
||||
Quest int // indicates that this item belongs to a given quest?
|
||||
MissileType int // missile gfx for throwing
|
||||
DurabilityWarning int // controls what warning icon appears when durability is low
|
||||
QuantityWarning int // controls at what quantity the low quantity warning appears
|
||||
|
||||
MinDamage int
|
||||
MaxDamage int
|
||||
StrengthBonus int
|
||||
DexterityBonus int
|
||||
MinDamage int
|
||||
MaxDamage int
|
||||
StrengthBonus int
|
||||
DexterityBonus int
|
||||
// final mindam = min * str / strbonus + min * dex / dexbonus
|
||||
// same for maxdam
|
||||
|
||||
GemOffset int // unknown
|
||||
BitField1 int // 1 = leather item, 3 = metal
|
||||
|
||||
Vendors map[string]*ItemVendorParams // controls vendor settings
|
||||
|
||||
SourceArt string // unused?
|
||||
GameArt string // unused?
|
||||
ColorTransform int // colormap to use for player's gfx
|
||||
InventoryColorTransform int // colormap to use for inventory's gfx
|
||||
|
||||
SkipName bool // if true, don't include the base name in the item description
|
||||
NightmareUpgrade string // upgraded in higher difficulties
|
||||
HellUpgrade string
|
||||
|
||||
Nameable bool // if true, item can be personalized
|
||||
|
||||
// weapon params
|
||||
BarbOneOrTwoHanded bool // if true, barb can wield this in one or two hands
|
||||
UsesTwoHands bool // if true, it's a 2handed weapon
|
||||
Min2HandDamage int
|
||||
Max2HandDamage int
|
||||
MinMissileDamage int // ranged damage stats
|
||||
MaxMissileDamage int
|
||||
MissileSpeed int // unknown, affects movement speed of wielder during ranged attacks?
|
||||
ExtraRange int // base range = 1, if this is non-zero add this to the range
|
||||
GemOffset int // unknown
|
||||
BitField1 int // 1 = leather item, 3 = metal
|
||||
ColorTransform int // colormap to use for player's gfx
|
||||
InventoryColorTransform int // colormap to use for inventory's gfx
|
||||
Min2HandDamage int
|
||||
Max2HandDamage int
|
||||
MinMissileDamage int // ranged damage stats
|
||||
MaxMissileDamage int
|
||||
MissileSpeed int // unknown, affects movement speed of wielder during ranged attacks?
|
||||
ExtraRange int // base range = 1, if this is non-zero add this to the range
|
||||
// final mindam = min * str / strbonus + min * dex / dexbonus
|
||||
// same for maxdam
|
||||
RequiredDexterity int
|
||||
|
||||
WeaponClass string // what kind of attack does this weapon have (i.e. determines attack animations)
|
||||
WeaponClass2Hand string // what kind of attack when wielded with two hands
|
||||
HitClass string // determines sounds/graphic effects when attacking
|
||||
SpawnStack int // unknown, something to do with stack size when spawned (sold maybe?)
|
||||
|
||||
SpecialFeature string // Just a comment
|
||||
|
||||
QuestDifficultyCheck bool // if true, item only works in the difficulty it was found in
|
||||
|
||||
PermStoreItem bool // if true, vendor will always sell this
|
||||
|
||||
// misc params
|
||||
FlavorText string // unknown, probably just for reference
|
||||
|
||||
Transmogrify bool // if true, can be turned into another item via right click
|
||||
TransmogCode string // the 3 char code representing the item this becomes via transmog
|
||||
TransmogMin int // min amount of the transmog item to create
|
||||
TransmogMax int // max ''
|
||||
|
||||
AutoBelt bool // if true, item is put into your belt when picked up
|
||||
|
||||
SpellIcon int // which icon to display when used? Is this always -1?
|
||||
SpellType int // determines what kind of function is used when you use this item
|
||||
OverlayState string // name of the overlay state to be applied upon use of this item
|
||||
CureOverlayStates [2]string // name of the overlay states that are removed upon use of this item
|
||||
EffectLength int // timer for timed usage effects
|
||||
UsageStats [3]ItemUsageStat // stat boosts applied upon usage
|
||||
|
||||
RequiredDexterity int
|
||||
SpawnStack int // unknown, something to do with stack size when spawned (sold maybe?)
|
||||
TransmogMin int // min amount of the transmog item to create
|
||||
TransmogMax int // max ''
|
||||
SpellIcon int // which icon to display when used? Is this always -1?
|
||||
SpellType int // determines what kind of function is used when you use this item
|
||||
EffectLength int // timer for timed usage effects
|
||||
SpellDescriptionType int // specifies how to format the usage description
|
||||
// 0 = none, 1 = use desc string, 2 = use desc string + calc value
|
||||
SpellDescriptionString string // points to a string containing the description
|
||||
SpellDescriptionCalc d2common.CalcString // a calc string what value to display
|
||||
|
||||
BetterGem string // 3 char code pointing to the gem this upgrades to (non if not applicable)
|
||||
|
||||
Multibuy bool // if true, when you buy via right click + shift it will fill your belt automatically
|
||||
AutoBelt bool // if true, item is put into your belt when picked up
|
||||
HasInventory bool // if true, item can store gems or runes
|
||||
CompactSave bool // if true, doesn't store any stats upon saving
|
||||
Spawnable bool // if 0, cannot spawn in shops
|
||||
NoDurability bool // if true, item has no durability
|
||||
Useable bool // can be used via right click if true
|
||||
// game knows what to do if used by item code
|
||||
Throwable bool
|
||||
Stackable bool // can be stacked in inventory
|
||||
Unique bool // if true, only spawns as unique
|
||||
Transparent bool // unused
|
||||
Quivered bool // if true, requires ammo to use
|
||||
Belt bool // tells what kind of belt this item is
|
||||
SkipName bool // if true, don't include the base name in the item description
|
||||
Nameable bool // if true, item can be personalized
|
||||
BarbOneOrTwoHanded bool // if true, barb can wield this in one or two hands
|
||||
UsesTwoHands bool // if true, it's a 2handed weapon
|
||||
QuestDifficultyCheck bool // if true, item only works in the difficulty it was found in
|
||||
PermStoreItem bool // if true, vendor will always sell this
|
||||
Transmogrify bool // if true, can be turned into another item via right click
|
||||
Multibuy bool // if true, when you buy via right click + shift it will fill your belt automatically
|
||||
}
|
||||
|
||||
// ItemUsageStat the stat that gets applied when the item is used
|
||||
type ItemUsageStat struct {
|
||||
Stat string // name of the stat to add to
|
||||
Calc d2common.CalcString // calc string representing the amount to add
|
||||
}
|
||||
|
||||
// ItemVendorParams are parameters that vendors use
|
||||
type ItemVendorParams struct {
|
||||
Min int // minimum of this item they can stock
|
||||
Max int // max they can stock
|
||||
@ -181,16 +154,18 @@ type ItemVendorParams struct {
|
||||
MagicLevel uint8
|
||||
}
|
||||
|
||||
var CommonItems map[string]*ItemCommonRecord
|
||||
// CommonItems stores all ItemCommonRecords
|
||||
var CommonItems map[string]*ItemCommonRecord //nolint:gochecknoglobals // Currently global by design
|
||||
|
||||
func LoadCommonItems(file []byte, source d2enum.InventoryItemType) *map[string]*ItemCommonRecord {
|
||||
// LoadCommonItems loads armor/weapons/misc.txt ItemCommonRecords
|
||||
func LoadCommonItems(file []byte, source d2enum.InventoryItemType) map[string]*ItemCommonRecord {
|
||||
if CommonItems == nil {
|
||||
CommonItems = make(map[string]*ItemCommonRecord)
|
||||
}
|
||||
|
||||
items := make(map[string]*ItemCommonRecord)
|
||||
data := strings.Split(string(file), "\r\n")
|
||||
mapping := MapHeaders(data[0])
|
||||
mapping := mapHeaders(data[0])
|
||||
|
||||
for lineno, line := range data {
|
||||
if lineno == 0 {
|
||||
@ -201,178 +176,178 @@ func LoadCommonItems(file []byte, source d2enum.InventoryItemType) *map[string]*
|
||||
continue
|
||||
}
|
||||
|
||||
rec := createCommonItemRecord(line, &mapping, source)
|
||||
rec := createCommonItemRecord(line, mapping, source)
|
||||
items[rec.Code] = &rec
|
||||
CommonItems[rec.Code] = &rec
|
||||
}
|
||||
|
||||
return &items
|
||||
return items
|
||||
}
|
||||
|
||||
//nolint:funlen // Makes no sens to split
|
||||
func createCommonItemRecord(line string, mapping *map[string]int, source d2enum.InventoryItemType) ItemCommonRecord {
|
||||
func createCommonItemRecord(line string, mapping map[string]int, source d2enum.InventoryItemType) ItemCommonRecord {
|
||||
r := strings.Split(line, "\t")
|
||||
result := ItemCommonRecord{
|
||||
Source: source,
|
||||
|
||||
Name: MapLoadString(&r, mapping, "name"),
|
||||
Name: mapLoadString(&r, mapping, "name"),
|
||||
|
||||
Version: MapLoadInt(&r, mapping, "version"),
|
||||
CompactSave: MapLoadBool(&r, mapping, "compactsave"),
|
||||
Rarity: MapLoadInt(&r, mapping, "rarity"),
|
||||
Spawnable: MapLoadBool(&r, mapping, "spawnable"),
|
||||
Version: mapLoadInt(&r, mapping, "version"),
|
||||
CompactSave: mapLoadBool(&r, mapping, "compactsave"),
|
||||
Rarity: mapLoadInt(&r, mapping, "rarity"),
|
||||
Spawnable: mapLoadBool(&r, mapping, "spawnable"),
|
||||
|
||||
MinAC: MapLoadInt(&r, mapping, "minac"),
|
||||
MaxAC: MapLoadInt(&r, mapping, "maxac"),
|
||||
Absorbs: MapLoadInt(&r, mapping, "absorbs"),
|
||||
Speed: MapLoadInt(&r, mapping, "speed"),
|
||||
RequiredStrength: MapLoadInt(&r, mapping, "reqstr"),
|
||||
Block: MapLoadInt(&r, mapping, "block"),
|
||||
Durability: MapLoadInt(&r, mapping, "durability"),
|
||||
NoDurability: MapLoadBool(&r, mapping, "nodurability"),
|
||||
MinAC: mapLoadInt(&r, mapping, "minac"),
|
||||
MaxAC: mapLoadInt(&r, mapping, "maxac"),
|
||||
Absorbs: mapLoadInt(&r, mapping, "absorbs"),
|
||||
Speed: mapLoadInt(&r, mapping, "speed"),
|
||||
RequiredStrength: mapLoadInt(&r, mapping, "reqstr"),
|
||||
Block: mapLoadInt(&r, mapping, "block"),
|
||||
Durability: mapLoadInt(&r, mapping, "durability"),
|
||||
NoDurability: mapLoadBool(&r, mapping, "nodurability"),
|
||||
|
||||
Level: MapLoadInt(&r, mapping, "level"),
|
||||
RequiredLevel: MapLoadInt(&r, mapping, "levelreq"),
|
||||
Cost: MapLoadInt(&r, mapping, "cost"),
|
||||
GambleCost: MapLoadInt(&r, mapping, "gamble cost"),
|
||||
Code: MapLoadString(&r, mapping, "code"),
|
||||
NameString: MapLoadString(&r, mapping, "namestr"),
|
||||
MagicLevel: MapLoadInt(&r, mapping, "magic lvl"),
|
||||
AutoPrefix: MapLoadInt(&r, mapping, "auto prefix"),
|
||||
Level: mapLoadInt(&r, mapping, "level"),
|
||||
RequiredLevel: mapLoadInt(&r, mapping, "levelreq"),
|
||||
Cost: mapLoadInt(&r, mapping, "cost"),
|
||||
GambleCost: mapLoadInt(&r, mapping, "gamble cost"),
|
||||
Code: mapLoadString(&r, mapping, "code"),
|
||||
NameString: mapLoadString(&r, mapping, "namestr"),
|
||||
MagicLevel: mapLoadInt(&r, mapping, "magic lvl"),
|
||||
AutoPrefix: mapLoadInt(&r, mapping, "auto prefix"),
|
||||
|
||||
AlternateGfx: MapLoadString(&r, mapping, "alternategfx"),
|
||||
OpenBetaGfx: MapLoadString(&r, mapping, "OpenBetaGfx"),
|
||||
NormalCode: MapLoadString(&r, mapping, "normcode"),
|
||||
UberCode: MapLoadString(&r, mapping, "ubercode"),
|
||||
UltraCode: MapLoadString(&r, mapping, "ultracode"),
|
||||
AlternateGfx: mapLoadString(&r, mapping, "alternategfx"),
|
||||
OpenBetaGfx: mapLoadString(&r, mapping, "OpenBetaGfx"),
|
||||
NormalCode: mapLoadString(&r, mapping, "normcode"),
|
||||
UberCode: mapLoadString(&r, mapping, "ubercode"),
|
||||
UltraCode: mapLoadString(&r, mapping, "ultracode"),
|
||||
|
||||
SpellOffset: MapLoadInt(&r, mapping, "spelloffset"),
|
||||
SpellOffset: mapLoadInt(&r, mapping, "spelloffset"),
|
||||
|
||||
Component: MapLoadInt(&r, mapping, "component"),
|
||||
InventoryWidth: MapLoadInt(&r, mapping, "invwidth"),
|
||||
InventoryHeight: MapLoadInt(&r, mapping, "invheight"),
|
||||
HasInventory: MapLoadBool(&r, mapping, "hasinv"),
|
||||
GemSockets: MapLoadInt(&r, mapping, "gemsockets"),
|
||||
GemApplyType: MapLoadInt(&r, mapping, "gemapplytype"),
|
||||
Component: mapLoadInt(&r, mapping, "component"),
|
||||
InventoryWidth: mapLoadInt(&r, mapping, "invwidth"),
|
||||
InventoryHeight: mapLoadInt(&r, mapping, "invheight"),
|
||||
HasInventory: mapLoadBool(&r, mapping, "hasinv"),
|
||||
GemSockets: mapLoadInt(&r, mapping, "gemsockets"),
|
||||
GemApplyType: mapLoadInt(&r, mapping, "gemapplytype"),
|
||||
|
||||
FlippyFile: MapLoadString(&r, mapping, "flippyfile"),
|
||||
InventoryFile: MapLoadString(&r, mapping, "invfile"),
|
||||
UniqueInventoryFile: MapLoadString(&r, mapping, "uniqueinvfile"),
|
||||
SetInventoryFile: MapLoadString(&r, mapping, "setinvfile"),
|
||||
FlippyFile: mapLoadString(&r, mapping, "flippyfile"),
|
||||
InventoryFile: mapLoadString(&r, mapping, "invfile"),
|
||||
UniqueInventoryFile: mapLoadString(&r, mapping, "uniqueinvfile"),
|
||||
SetInventoryFile: mapLoadString(&r, mapping, "setinvfile"),
|
||||
|
||||
AnimRightArm: MapLoadInt(&r, mapping, "rArm"),
|
||||
AnimLeftArm: MapLoadInt(&r, mapping, "lArm"),
|
||||
AnimTorso: MapLoadInt(&r, mapping, "Torso"),
|
||||
AnimLegs: MapLoadInt(&r, mapping, "Legs"),
|
||||
AnimRightShoulderPad: MapLoadInt(&r, mapping, "rSPad"),
|
||||
AnimLeftShoulderPad: MapLoadInt(&r, mapping, "lSPad"),
|
||||
AnimRightArm: mapLoadInt(&r, mapping, "rArm"),
|
||||
AnimLeftArm: mapLoadInt(&r, mapping, "lArm"),
|
||||
AnimTorso: mapLoadInt(&r, mapping, "Torso"),
|
||||
AnimLegs: mapLoadInt(&r, mapping, "Legs"),
|
||||
AnimRightShoulderPad: mapLoadInt(&r, mapping, "rSPad"),
|
||||
AnimLeftShoulderPad: mapLoadInt(&r, mapping, "lSPad"),
|
||||
|
||||
Useable: MapLoadBool(&r, mapping, "useable"),
|
||||
Useable: mapLoadBool(&r, mapping, "useable"),
|
||||
|
||||
Throwable: MapLoadBool(&r, mapping, "throwable"),
|
||||
Stackable: MapLoadBool(&r, mapping, "stackable"),
|
||||
MinStack: MapLoadInt(&r, mapping, "minstack"),
|
||||
MaxStack: MapLoadInt(&r, mapping, "maxstack"),
|
||||
Throwable: mapLoadBool(&r, mapping, "throwable"),
|
||||
Stackable: mapLoadBool(&r, mapping, "stackable"),
|
||||
MinStack: mapLoadInt(&r, mapping, "minstack"),
|
||||
MaxStack: mapLoadInt(&r, mapping, "maxstack"),
|
||||
|
||||
Type: MapLoadString(&r, mapping, "type"),
|
||||
Type2: MapLoadString(&r, mapping, "type2"),
|
||||
Type: mapLoadString(&r, mapping, "type"),
|
||||
Type2: mapLoadString(&r, mapping, "type2"),
|
||||
|
||||
DropSound: MapLoadString(&r, mapping, "dropsound"),
|
||||
DropSfxFrame: MapLoadInt(&r, mapping, "dropsfxframe"),
|
||||
UseSound: MapLoadString(&r, mapping, "usesound"),
|
||||
DropSound: mapLoadString(&r, mapping, "dropsound"),
|
||||
DropSfxFrame: mapLoadInt(&r, mapping, "dropsfxframe"),
|
||||
UseSound: mapLoadString(&r, mapping, "usesound"),
|
||||
|
||||
Unique: MapLoadBool(&r, mapping, "unique"),
|
||||
Transparent: MapLoadBool(&r, mapping, "transparent"),
|
||||
TransTable: MapLoadInt(&r, mapping, "transtbl"),
|
||||
Quivered: MapLoadBool(&r, mapping, "quivered"),
|
||||
LightRadius: MapLoadInt(&r, mapping, "lightradius"),
|
||||
Belt: MapLoadBool(&r, mapping, "belt"),
|
||||
Unique: mapLoadBool(&r, mapping, "unique"),
|
||||
Transparent: mapLoadBool(&r, mapping, "transparent"),
|
||||
TransTable: mapLoadInt(&r, mapping, "transtbl"),
|
||||
Quivered: mapLoadBool(&r, mapping, "quivered"),
|
||||
LightRadius: mapLoadInt(&r, mapping, "lightradius"),
|
||||
Belt: mapLoadBool(&r, mapping, "belt"),
|
||||
|
||||
Quest: MapLoadInt(&r, mapping, "quest"),
|
||||
Quest: mapLoadInt(&r, mapping, "quest"),
|
||||
|
||||
MissileType: MapLoadInt(&r, mapping, "missiletype"),
|
||||
DurabilityWarning: MapLoadInt(&r, mapping, "durwarning"),
|
||||
QuantityWarning: MapLoadInt(&r, mapping, "qntwarning"),
|
||||
MissileType: mapLoadInt(&r, mapping, "missiletype"),
|
||||
DurabilityWarning: mapLoadInt(&r, mapping, "durwarning"),
|
||||
QuantityWarning: mapLoadInt(&r, mapping, "qntwarning"),
|
||||
|
||||
MinDamage: MapLoadInt(&r, mapping, "mindam"),
|
||||
MaxDamage: MapLoadInt(&r, mapping, "maxdam"),
|
||||
StrengthBonus: MapLoadInt(&r, mapping, "StrBonus"),
|
||||
DexterityBonus: MapLoadInt(&r, mapping, "DexBonus"),
|
||||
MinDamage: mapLoadInt(&r, mapping, "mindam"),
|
||||
MaxDamage: mapLoadInt(&r, mapping, "maxdam"),
|
||||
StrengthBonus: mapLoadInt(&r, mapping, "StrBonus"),
|
||||
DexterityBonus: mapLoadInt(&r, mapping, "DexBonus"),
|
||||
|
||||
GemOffset: MapLoadInt(&r, mapping, "gemoffset"),
|
||||
BitField1: MapLoadInt(&r, mapping, "bitfield1"),
|
||||
GemOffset: mapLoadInt(&r, mapping, "gemoffset"),
|
||||
BitField1: mapLoadInt(&r, mapping, "bitfield1"),
|
||||
|
||||
Vendors: createItemVendorParams(&r, mapping),
|
||||
|
||||
SourceArt: MapLoadString(&r, mapping, "Source Art"),
|
||||
GameArt: MapLoadString(&r, mapping, "Game Art"),
|
||||
ColorTransform: MapLoadInt(&r, mapping, "Transform"),
|
||||
InventoryColorTransform: MapLoadInt(&r, mapping, "InvTrans"),
|
||||
SourceArt: mapLoadString(&r, mapping, "Source Art"),
|
||||
GameArt: mapLoadString(&r, mapping, "Game Art"),
|
||||
ColorTransform: mapLoadInt(&r, mapping, "Transform"),
|
||||
InventoryColorTransform: mapLoadInt(&r, mapping, "InvTrans"),
|
||||
|
||||
SkipName: MapLoadBool(&r, mapping, "SkipName"),
|
||||
NightmareUpgrade: MapLoadString(&r, mapping, "NightmareUpgrade"),
|
||||
HellUpgrade: MapLoadString(&r, mapping, "HellUpgrade"),
|
||||
SkipName: mapLoadBool(&r, mapping, "SkipName"),
|
||||
NightmareUpgrade: mapLoadString(&r, mapping, "NightmareUpgrade"),
|
||||
HellUpgrade: mapLoadString(&r, mapping, "HellUpgrade"),
|
||||
|
||||
Nameable: MapLoadBool(&r, mapping, "Nameable"),
|
||||
Nameable: mapLoadBool(&r, mapping, "Nameable"),
|
||||
|
||||
// weapon params
|
||||
BarbOneOrTwoHanded: MapLoadBool(&r, mapping, "1or2handed"),
|
||||
UsesTwoHands: MapLoadBool(&r, mapping, "2handed"),
|
||||
Min2HandDamage: MapLoadInt(&r, mapping, "2handmindam"),
|
||||
Max2HandDamage: MapLoadInt(&r, mapping, "2handmaxdam"),
|
||||
MinMissileDamage: MapLoadInt(&r, mapping, "minmisdam"),
|
||||
MaxMissileDamage: MapLoadInt(&r, mapping, "maxmisdam"),
|
||||
MissileSpeed: MapLoadInt(&r, mapping, "misspeed"),
|
||||
ExtraRange: MapLoadInt(&r, mapping, "rangeadder"),
|
||||
BarbOneOrTwoHanded: mapLoadBool(&r, mapping, "1or2handed"),
|
||||
UsesTwoHands: mapLoadBool(&r, mapping, "2handed"),
|
||||
Min2HandDamage: mapLoadInt(&r, mapping, "2handmindam"),
|
||||
Max2HandDamage: mapLoadInt(&r, mapping, "2handmaxdam"),
|
||||
MinMissileDamage: mapLoadInt(&r, mapping, "minmisdam"),
|
||||
MaxMissileDamage: mapLoadInt(&r, mapping, "maxmisdam"),
|
||||
MissileSpeed: mapLoadInt(&r, mapping, "misspeed"),
|
||||
ExtraRange: mapLoadInt(&r, mapping, "rangeadder"),
|
||||
|
||||
RequiredDexterity: MapLoadInt(&r, mapping, "reqdex"),
|
||||
RequiredDexterity: mapLoadInt(&r, mapping, "reqdex"),
|
||||
|
||||
WeaponClass: MapLoadString(&r, mapping, "wclass"),
|
||||
WeaponClass2Hand: MapLoadString(&r, mapping, "2handedwclass"),
|
||||
WeaponClass: mapLoadString(&r, mapping, "wclass"),
|
||||
WeaponClass2Hand: mapLoadString(&r, mapping, "2handedwclass"),
|
||||
|
||||
HitClass: MapLoadString(&r, mapping, "hit class"),
|
||||
SpawnStack: MapLoadInt(&r, mapping, "spawnstack"),
|
||||
HitClass: mapLoadString(&r, mapping, "hit class"),
|
||||
SpawnStack: mapLoadInt(&r, mapping, "spawnstack"),
|
||||
|
||||
SpecialFeature: MapLoadString(&r, mapping, "special"),
|
||||
SpecialFeature: mapLoadString(&r, mapping, "special"),
|
||||
|
||||
QuestDifficultyCheck: MapLoadBool(&r, mapping, "questdiffcheck"),
|
||||
QuestDifficultyCheck: mapLoadBool(&r, mapping, "questdiffcheck"),
|
||||
|
||||
PermStoreItem: MapLoadBool(&r, mapping, "PermStoreItem"),
|
||||
PermStoreItem: mapLoadBool(&r, mapping, "PermStoreItem"),
|
||||
|
||||
// misc params
|
||||
FlavorText: MapLoadString(&r, mapping, "szFlavorText"),
|
||||
FlavorText: mapLoadString(&r, mapping, "szFlavorText"),
|
||||
|
||||
Transmogrify: MapLoadBool(&r, mapping, "Transmogrify"),
|
||||
TransmogCode: MapLoadString(&r, mapping, "TMogType"),
|
||||
TransmogMin: MapLoadInt(&r, mapping, "TMogMin"),
|
||||
TransmogMax: MapLoadInt(&r, mapping, "TMogMax"),
|
||||
Transmogrify: mapLoadBool(&r, mapping, "Transmogrify"),
|
||||
TransmogCode: mapLoadString(&r, mapping, "TMogType"),
|
||||
TransmogMin: mapLoadInt(&r, mapping, "TMogMin"),
|
||||
TransmogMax: mapLoadInt(&r, mapping, "TMogMax"),
|
||||
|
||||
AutoBelt: MapLoadBool(&r, mapping, "autobelt"),
|
||||
AutoBelt: mapLoadBool(&r, mapping, "autobelt"),
|
||||
|
||||
SpellIcon: MapLoadInt(&r, mapping, "spellicon"),
|
||||
SpellType: MapLoadInt(&r, mapping, "pSpell"),
|
||||
OverlayState: MapLoadString(&r, mapping, "state"),
|
||||
SpellIcon: mapLoadInt(&r, mapping, "spellicon"),
|
||||
SpellType: mapLoadInt(&r, mapping, "pSpell"),
|
||||
OverlayState: mapLoadString(&r, mapping, "state"),
|
||||
CureOverlayStates: [2]string{
|
||||
MapLoadString(&r, mapping, "cstate1"),
|
||||
MapLoadString(&r, mapping, "cstate2"),
|
||||
mapLoadString(&r, mapping, "cstate1"),
|
||||
mapLoadString(&r, mapping, "cstate2"),
|
||||
},
|
||||
EffectLength: MapLoadInt(&r, mapping, "len"),
|
||||
EffectLength: mapLoadInt(&r, mapping, "len"),
|
||||
UsageStats: createItemUsageStats(&r, mapping),
|
||||
|
||||
SpellDescriptionType: MapLoadInt(&r, mapping, "spelldesc"),
|
||||
SpellDescriptionType: mapLoadInt(&r, mapping, "spelldesc"),
|
||||
// 0 = none, 1 = use desc string, 2 = use desc string + calc value
|
||||
SpellDescriptionString: MapLoadString(&r, mapping, "spelldescstr"),
|
||||
SpellDescriptionCalc: d2common.CalcString(MapLoadString(&r, mapping, "spelldesccalc")),
|
||||
SpellDescriptionString: mapLoadString(&r, mapping, "spelldescstr"),
|
||||
SpellDescriptionCalc: d2common.CalcString(mapLoadString(&r, mapping, "spelldesccalc")),
|
||||
|
||||
BetterGem: MapLoadString(&r, mapping, "BetterGem"),
|
||||
BetterGem: mapLoadString(&r, mapping, "BetterGem"),
|
||||
|
||||
Multibuy: MapLoadBool(&r, mapping, "multibuy"),
|
||||
Multibuy: mapLoadBool(&r, mapping, "multibuy"),
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func createItemVendorParams(r *[]string, mapping *map[string]int) map[string]*ItemVendorParams {
|
||||
func createItemVendorParams(r *[]string, mapping map[string]int) map[string]*ItemVendorParams {
|
||||
vs := make([]string, 17)
|
||||
vs[0] = "Charsi"
|
||||
vs[1] = "Gheed"
|
||||
@ -396,11 +371,11 @@ func createItemVendorParams(r *[]string, mapping *map[string]int) map[string]*It
|
||||
|
||||
for _, name := range vs {
|
||||
wvp := ItemVendorParams{
|
||||
Min: MapLoadInt(r, mapping, name+"Min"),
|
||||
Max: MapLoadInt(r, mapping, name+"Max"),
|
||||
MagicMin: MapLoadInt(r, mapping, name+"MagicMin"),
|
||||
MagicMax: MapLoadInt(r, mapping, name+"MagicMax"),
|
||||
MagicLevel: MapLoadUint8(r, mapping, name+"MagicLvl"),
|
||||
Min: mapLoadInt(r, mapping, name+"Min"),
|
||||
Max: mapLoadInt(r, mapping, name+"Max"),
|
||||
MagicMin: mapLoadInt(r, mapping, name+"MagicMin"),
|
||||
MagicMax: mapLoadInt(r, mapping, name+"MagicMax"),
|
||||
MagicLevel: mapLoadUint8(r, mapping, name+"MagicLvl"),
|
||||
}
|
||||
result[name] = &wvp
|
||||
}
|
||||
@ -408,11 +383,11 @@ func createItemVendorParams(r *[]string, mapping *map[string]int) map[string]*It
|
||||
return result
|
||||
}
|
||||
|
||||
func createItemUsageStats(r *[]string, mapping *map[string]int) [3]ItemUsageStat {
|
||||
func createItemUsageStats(r *[]string, mapping map[string]int) [3]ItemUsageStat {
|
||||
result := [3]ItemUsageStat{}
|
||||
for i := 0; i < 3; i++ {
|
||||
result[i].Stat = MapLoadString(r, mapping, "stat"+strconv.Itoa(i))
|
||||
result[i].Calc = d2common.CalcString(MapLoadString(r, mapping, "calc"+strconv.Itoa(i)))
|
||||
result[i].Stat = mapLoadString(r, mapping, "stat"+strconv.Itoa(i))
|
||||
result[i].Calc = d2common.CalcString(mapLoadString(r, mapping, "calc"+strconv.Itoa(i)))
|
||||
}
|
||||
|
||||
return result
|
||||
|
@ -7,35 +7,53 @@ import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||
)
|
||||
|
||||
// ItemStatCostRecord represents a row from itemstatcost.txt
|
||||
// these records describe the stat values and costs (in shops) of items
|
||||
// refer to https://d2mods.info/forum/kb/viewarticle?a=448
|
||||
type ItemStatCostRecord struct {
|
||||
Name string
|
||||
Index int
|
||||
Name string
|
||||
OpBase string
|
||||
OpStat1 string
|
||||
OpStat2 string
|
||||
OpStat3 string
|
||||
|
||||
Signed bool // whether the stat is signed
|
||||
KeepZero bool // prevent from going negative (assume only client side)
|
||||
MaxStat string // if Direct true, will not exceed val of MaxStat
|
||||
DescStrPos string // string used when val is positive
|
||||
DescStrNeg string
|
||||
DescStr2 string // additional string used by some string funcs
|
||||
DescGroupStrPos string // string used when val is positive
|
||||
DescGroupStrNeg string
|
||||
DescGroupStr2 string // additional string used by some string funcs
|
||||
|
||||
// Stuff
|
||||
// Stay far away from this column unless you really know what you're
|
||||
// doing and / or work for Blizzard, this column is used during bin-file
|
||||
// creation to generate a cache regulating the op-stat stuff and other
|
||||
// things, changing it can be futile, it works like the constants column
|
||||
// in MonUMod.txt and has no other relation to ItemStatCost.txt, the first
|
||||
// stat in the file simply must have this set or else you may break the
|
||||
// entire op stuff.
|
||||
Stuff string
|
||||
|
||||
DescFn interface{} // the sprintf func
|
||||
DescGroupFn interface{} // group sprintf func
|
||||
|
||||
Index int
|
||||
|
||||
// path_d2.mpq version doesnt have Ranged columne, excluding for now
|
||||
// Ranged bool // game attempts to keep stat in a range, like strength >-1
|
||||
MinAccr int // minimum ranged value
|
||||
|
||||
UpdateAnimRate bool // when altered, forces speed handler to adjust speed
|
||||
SendBits int // #bits to send in stat update
|
||||
SendParam int // #bits to send in stat update
|
||||
|
||||
SendOther bool // whether to send to other clients
|
||||
SendBits int // #bits to send in stat update
|
||||
SendParam int // #bits to send in stat update
|
||||
|
||||
Saved bool // whether this stat is saved in .d2s files
|
||||
SavedSigned bool // whether the stat is saved as signed/unsigned
|
||||
SavedBits int // #bits allocated to the value in .d2s file
|
||||
SavedBits int // #bits allocated to the value in .d2s file
|
||||
|
||||
SaveBits int // #bits saved to .d2s files, max == 2^SaveBits-1
|
||||
SaveAdd int // how large the negative range is (lowers max, as well)
|
||||
SaveParamBits int // #param bits are saved (safe value is 17)
|
||||
|
||||
Encode EncodingType // how the stat is encoded in .d2s files
|
||||
|
||||
CallbackEnabled bool // whether callback fn is called if value changes
|
||||
Encode d2enum.EncodingType // how the stat is encoded in .d2s files
|
||||
|
||||
// these two fields control additional cost on items
|
||||
// cost * (1 + value * multiply / 1024)) + add (...)
|
||||
@ -48,20 +66,8 @@ type ItemStatCostRecord struct {
|
||||
ValShift int // controls how stat is stored in .d2s
|
||||
// so that you can save `+1` instead of `+256`
|
||||
|
||||
OperatorType OperatorType
|
||||
OperatorType d2enum.OperatorType
|
||||
OpParam int
|
||||
OpBase string
|
||||
OpStat1 string
|
||||
OpStat2 string
|
||||
OpStat3 string
|
||||
|
||||
Direct bool // whether is temporary or permanent
|
||||
MaxStat string // if Direct true, will not exceed val of MaxStat
|
||||
|
||||
ItemSpecific bool // prevents stacking with an existing stat on item
|
||||
// like when socketing a jewel
|
||||
|
||||
DamageRelated bool // prevents stacking of stats while dual wielding
|
||||
|
||||
EventID1 d2enum.ItemEventType
|
||||
EventID2 d2enum.ItemEventType
|
||||
@ -70,175 +76,35 @@ type ItemStatCostRecord struct {
|
||||
|
||||
DescPriority int // determines order when displayed
|
||||
DescFnID d2enum.DescFuncID
|
||||
DescFn interface{} // the sprintf func
|
||||
|
||||
// Controls whenever and if so in what way the stat value is shown
|
||||
// 0 = doesn't show the value of the stat
|
||||
// 1 = shows the value of the stat infront of the description
|
||||
// 2 = shows the value of the stat after the description.
|
||||
DescVal int
|
||||
DescStrPos string // string used when val is positive
|
||||
DescStrNeg string
|
||||
DescStr2 string // additional string used by some string funcs
|
||||
DescVal int
|
||||
|
||||
// when stats in the same group have the same value they use the
|
||||
// group func for desc (they need to be in the same affix)
|
||||
DescGroup int
|
||||
DescGroupFuncID d2enum.DescFuncID
|
||||
DescGroupFn interface{} // group sprintf func
|
||||
DescGroupVal int
|
||||
DescGroupStrPos string // string used when val is positive
|
||||
DescGroupStrNeg string
|
||||
DescGroupStr2 string // additional string used by some string funcs
|
||||
DescGroupFuncID d2enum.DescFuncID
|
||||
|
||||
// Stay far away from this column unless you really know what you're
|
||||
// doing and / or work for Blizzard, this column is used during bin-file
|
||||
// creation to generate a cache regulating the op-stat stuff and other
|
||||
// things, changing it can be futile, it works like the constants column
|
||||
// in MonUMod.txt and has no other relation to ItemStatCost.txt, the first
|
||||
// stat in the file simply must have this set or else you may break the
|
||||
// entire op stuff.
|
||||
Stuff string // ? TODO ?
|
||||
CallbackEnabled bool // whether callback fn is called if value changes
|
||||
Signed bool // whether the stat is signed
|
||||
KeepZero bool // prevent from going negative (assume only client side)
|
||||
UpdateAnimRate bool // when altered, forces speed handler to adjust speed
|
||||
SendOther bool // whether to send to other clients
|
||||
Saved bool // whether this stat is saved in .d2s files
|
||||
SavedSigned bool // whether the stat is saved as signed/unsigned
|
||||
Direct bool // whether is temporary or permanent
|
||||
ItemSpecific bool // prevents stacking with an existing stat on item
|
||||
// like when socketing a jewel
|
||||
|
||||
DamageRelated bool // prevents stacking of stats while dual wielding
|
||||
}
|
||||
|
||||
type EncodingType int
|
||||
|
||||
const (
|
||||
// TODO: determine other encoding types.
|
||||
// didn't see anything about how this stuff is encoded, or the types...
|
||||
EncodeDefault = EncodingType(iota)
|
||||
)
|
||||
|
||||
type OperatorType int // for dynamic properties
|
||||
|
||||
const (
|
||||
// just adds the stat to the unit directly
|
||||
OpDefault = OperatorType(iota)
|
||||
|
||||
// Op1 adds opstat.base * statvalue / 100 to the opstat.
|
||||
Op1
|
||||
|
||||
// Op2 adds (statvalue * basevalue) / (2 ^ param) to the opstat
|
||||
// this does not work properly with any stat other then level because of the
|
||||
// way this is updated, it is only refreshed when you re-equip the item,
|
||||
// your character is saved or you level up, similar to passive skills, just
|
||||
// because it looks like it works in the item description
|
||||
// does not mean it does, the game just recalculates the information in the
|
||||
// description every frame, while the values remain unchanged serverside.
|
||||
Op2
|
||||
|
||||
// Op3 is a percentage based version of op #2
|
||||
// look at op #2 for information about the formula behind it, just
|
||||
// remember the stat is increased by a percentage rather then by adding
|
||||
// an integer.
|
||||
Op3
|
||||
|
||||
// Op4 works the same way op #2 works, however the stat bonus is
|
||||
// added to the item and not to the player (so that +defense per level
|
||||
// properly adds the defense to the armor and not to the character
|
||||
// directly!)
|
||||
Op4
|
||||
|
||||
// Op5 works like op #4 but is percentage based, it is used for percentage
|
||||
// based increase of stats that are found on the item itself, and not stats
|
||||
// that are found on the character.
|
||||
Op5
|
||||
|
||||
// Op6 works like for op #7, however this adds a plain bonus to the stat, and just
|
||||
// like #7 it also doesn't work so I won't bother to explain the arithmetic
|
||||
// behind it either.
|
||||
Op6
|
||||
|
||||
// Op7 is used to increase a stat based on the current daytime of the game
|
||||
// world by a percentage, there is no need to explain the arithmetics
|
||||
// behind it because frankly enough it just doesn't work serverside, it
|
||||
// only updates clientside so this op is essentially useless.
|
||||
Op7
|
||||
|
||||
// Op8 hardcoded to work only with maxmana, this will apply the proper amount
|
||||
// of mana to your character based on CharStats.txt for the amount of energy
|
||||
// the stat added (doesn't work for non characters)
|
||||
Op8
|
||||
|
||||
// Op9 hardcoded to work only with maxhp and maxstamina, this will apply the
|
||||
// proper amount of maxhp and maxstamina to your character based on
|
||||
// CharStats.txt for the amount of vitality the stat added (doesn't work
|
||||
// for non characters)
|
||||
Op9
|
||||
|
||||
// Op10 doesn't do anything, this has no switch case in the op function.
|
||||
Op10
|
||||
|
||||
// Op11 adds opstat.base * statvalue / 100 similar to 1 and 13, the code just
|
||||
// does a few more checks
|
||||
Op11
|
||||
|
||||
// Op12 doesn't do anything, this has no switch case in the op function.
|
||||
Op12
|
||||
|
||||
// Op13 adds opstat.base * statvalue / 100 to the value of opstat, this is
|
||||
// useable only on items it will not apply the bonus to other unit types
|
||||
// (this is why it is used for +% durability, +% level requirement,
|
||||
// +% damage, +% defense ).
|
||||
Op13
|
||||
)
|
||||
|
||||
/* column names from path_d2.mpq/data/global/excel/ItemStatCost.txt
|
||||
Stat
|
||||
ID
|
||||
Send Other
|
||||
Signed
|
||||
Send Bits
|
||||
Send Param Bits
|
||||
UpdateAnimRate
|
||||
Saved
|
||||
CSvSigned
|
||||
CSvBits
|
||||
CSvParam
|
||||
fCallback
|
||||
fMin
|
||||
MinAccr
|
||||
Encode
|
||||
Add
|
||||
Multiply
|
||||
Divide
|
||||
ValShift
|
||||
1.09-Save Bits
|
||||
1.09-Save Add
|
||||
Save Bits
|
||||
Save Add
|
||||
Save Param Bits
|
||||
keepzero
|
||||
op
|
||||
op param
|
||||
op base
|
||||
op stat1
|
||||
op stat2
|
||||
op stat3
|
||||
direct
|
||||
maxstat
|
||||
itemspecific
|
||||
damagerelated
|
||||
itemevent1
|
||||
itemeventfunc1
|
||||
itemevent2
|
||||
itemeventfunc2
|
||||
descpriority
|
||||
descfunc
|
||||
descval
|
||||
descstrpos
|
||||
descstrneg
|
||||
descstr2
|
||||
dgrp
|
||||
dgrpfunc
|
||||
dgrpval
|
||||
dgrpstrpos
|
||||
dgrpstrneg
|
||||
dgrpstr2
|
||||
stuff
|
||||
*eol
|
||||
*/
|
||||
|
||||
// ItemStatCosts stores all of the ItemStatCostRecords
|
||||
//nolint:gochecknoglobals // Currently global by design
|
||||
var ItemStatCosts map[string]*ItemStatCostRecord
|
||||
|
||||
// LoadItemStatCosts loads ItemStatCostRecord's from text
|
||||
@ -271,7 +137,7 @@ func LoadItemStatCosts(file []byte) {
|
||||
SaveAdd: d.GetNumber("Save Add", idx),
|
||||
SaveParamBits: d.GetNumber("Save Param Bits", idx),
|
||||
|
||||
Encode: EncodingType(d.GetNumber("Encode", idx)),
|
||||
Encode: d2enum.EncodingType(d.GetNumber("Encode", idx)),
|
||||
|
||||
CallbackEnabled: d.GetNumber("fCallback", idx) > 0,
|
||||
|
||||
@ -279,7 +145,7 @@ func LoadItemStatCosts(file []byte) {
|
||||
CostMultiply: d.GetNumber("Multiply", idx),
|
||||
ValShift: d.GetNumber("ValShift", idx),
|
||||
|
||||
OperatorType: OperatorType(d.GetNumber("op", idx)),
|
||||
OperatorType: d2enum.OperatorType(d.GetNumber("op", idx)),
|
||||
OpParam: d.GetNumber("op param", idx),
|
||||
OpBase: d.GetString("op base", idx),
|
||||
OpStat1: d.GetString("op stat1", idx),
|
||||
|
@ -6,6 +6,8 @@ import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
)
|
||||
|
||||
// LevelMazeDetailsRecord is a representation of a row from lvlmaze.txt
|
||||
// these records define the parameters passed to the maze level generator
|
||||
type LevelMazeDetailsRecord struct {
|
||||
// descriptive, not loaded in game. Corresponds with Name field in
|
||||
// Levels.txt
|
||||
@ -34,7 +36,8 @@ type LevelMazeDetailsRecord struct {
|
||||
// Beta
|
||||
}
|
||||
|
||||
var LevelMazeDetails map[int]*LevelMazeDetailsRecord
|
||||
// LevelMazeDetails stores all of the LevelMazeDetailsRecords
|
||||
var LevelMazeDetails map[int]*LevelMazeDetailsRecord //nolint:gochecknoglobals // Currently global by design
|
||||
|
||||
// LoadLevelMazeDetails loads LevelMazeDetailsRecords from text file
|
||||
func LoadLevelMazeDetails(file []byte) {
|
||||
|
@ -7,25 +7,27 @@ import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
)
|
||||
|
||||
// LevelPresetRecord is a representation of a row from lvlprest.txt
|
||||
// these records define parameters for the preset level map generator
|
||||
type LevelPresetRecord struct {
|
||||
Files [6]string
|
||||
Name string
|
||||
DefinitionID int
|
||||
LevelID int
|
||||
SizeX int
|
||||
SizeY int
|
||||
Pops int
|
||||
PopPad int
|
||||
FileCount int
|
||||
Dt1Mask uint
|
||||
Populate bool
|
||||
Logicals bool
|
||||
Outdoors bool
|
||||
Animate bool
|
||||
KillEdge bool
|
||||
FillBlanks bool
|
||||
SizeX int
|
||||
SizeY int
|
||||
AutoMap bool
|
||||
Scan bool
|
||||
Pops int
|
||||
PopPad int
|
||||
FileCount int
|
||||
Files [6]string
|
||||
Dt1Mask uint
|
||||
Beta bool
|
||||
Expansion bool
|
||||
}
|
||||
@ -70,7 +72,8 @@ func createLevelPresetRecord(props []string) LevelPresetRecord {
|
||||
return result
|
||||
}
|
||||
|
||||
var LevelPresets map[int]LevelPresetRecord
|
||||
// LevelPresets stores all of the LevelPresetRecords
|
||||
var LevelPresets map[int]LevelPresetRecord //nolint:gochecknoglobals // Currently global by design
|
||||
|
||||
// LoadLevelPresets loads level presets from text file
|
||||
func LoadLevelPresets(file []byte) {
|
||||
|
@ -6,9 +6,12 @@ import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
)
|
||||
|
||||
// LevelSubstitutionRecord is a representation of a row from lvlsub.txt
|
||||
// these records are parameters for levels and describe substitution rules
|
||||
type LevelSubstitutionRecord struct {
|
||||
// Description, reference only.
|
||||
Name string // Name
|
||||
|
||||
// This value is used in Levels.txt, in the column 'SubType'. You'll notice
|
||||
// that in LvlSub.txt some rows use the same value, we can say they forms
|
||||
// groups. If you count each row of a group starting from 0, then you'll
|
||||
@ -61,8 +64,11 @@ type LevelSubstitutionRecord struct {
|
||||
// Beta
|
||||
}
|
||||
|
||||
// LevelSubstitutions stores all of the LevelSubstitutionRecords
|
||||
//nolint:gochecknoglobals // Currently global by design
|
||||
var LevelSubstitutions map[int]*LevelSubstitutionRecord
|
||||
|
||||
// LoadLevelSubstitutions loads lvlsub.txt and parses into records
|
||||
func LoadLevelSubstitutions(file []byte) {
|
||||
dict := d2common.LoadDataDictionary(string(file))
|
||||
numRecords := len(dict.Data)
|
||||
|
@ -7,17 +7,21 @@ import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
)
|
||||
|
||||
// LevelTypeRecord is a representation of a row from lvltype.txt
|
||||
// the fields describe what ds1 files a level uses
|
||||
type LevelTypeRecord struct {
|
||||
Files [32]string
|
||||
Name string
|
||||
ID int
|
||||
Files [32]string
|
||||
Beta bool
|
||||
Act int
|
||||
Beta bool
|
||||
Expansion bool
|
||||
}
|
||||
|
||||
var LevelTypes []LevelTypeRecord
|
||||
// LevelTypes stores all of the LevelTypeRecords
|
||||
var LevelTypes []LevelTypeRecord //nolint:gochecknoglobals // Currently global by design,
|
||||
|
||||
// LoadLevelTypes loads the LevelTypeRecords
|
||||
func LoadLevelTypes(file []byte) {
|
||||
data := strings.Split(string(file), "\r\n")[1:]
|
||||
LevelTypes = make([]LevelTypeRecord, len(data))
|
||||
|
@ -6,6 +6,8 @@ import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
)
|
||||
|
||||
// LevelWarpRecord is a representation of a row from lvlwarp.txt
|
||||
// it describes the warp graphics offsets and dimensions for levels
|
||||
type LevelWarpRecord struct {
|
||||
ID int32
|
||||
SelectX int32
|
||||
@ -21,8 +23,8 @@ type LevelWarpRecord struct {
|
||||
Direction string
|
||||
}
|
||||
|
||||
//nolint:gochecknoglobals // Currently global by design, only written once
|
||||
// LevelWarps loaded from txt records
|
||||
//nolint:gochecknoglobals // Currently global by design, only written once
|
||||
var LevelWarps map[int]*LevelWarpRecord
|
||||
|
||||
// LoadLevelWarps loads LevelWarpRecord's from text file data
|
||||
|
@ -7,21 +7,103 @@ import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||
)
|
||||
|
||||
// LevelDetailsRecord is a representation of a row from levels.txt
|
||||
// it describes lots of things about the levels, like where they are connected,
|
||||
// what kinds of monsters spawn, the level generator type, and lots of other stuff.
|
||||
type LevelDetailsRecord struct {
|
||||
|
||||
// Name
|
||||
// This column has no function, it only serves as a comment field to make it
|
||||
// easier to identify the Level name
|
||||
Name string // Name <-- the corresponding column name in the txt
|
||||
|
||||
// Level ID (used in columns like VIS0-7)
|
||||
Id int // Id
|
||||
// mon1-mon25 work in Normal difficulty, while nmon1-nmon25 in Nightmare and
|
||||
// Hell. They tell the game which monster ID taken from MonStats.txt.
|
||||
// NOTE: you need to manually add from mon11 to mon25 and from nmon11 to
|
||||
// nmon25 !
|
||||
MonsterID1Normal string // mon1
|
||||
MonsterID2Normal string // mon2
|
||||
MonsterID3Normal string // mon3
|
||||
MonsterID4Normal string // mon4
|
||||
MonsterID5Normal string // mon5
|
||||
MonsterID6Normal string // mon6
|
||||
MonsterID7Normal string // mon7
|
||||
MonsterID8Normal string // mon8
|
||||
MonsterID9Normal string // mon9
|
||||
MonsterID10Normal string // mon10
|
||||
|
||||
// Act Palette . Reference only
|
||||
MonsterID1Nightmare string // nmon1
|
||||
MonsterID2Nightmare string // nmon2
|
||||
MonsterID3Nightmare string // nmon3
|
||||
MonsterID4Nightmare string // nmon4
|
||||
MonsterID5Nightmare string // nmon5
|
||||
MonsterID6Nightmare string // nmon6
|
||||
MonsterID7Nightmare string // nmon7
|
||||
MonsterID8Nightmare string // nmon8
|
||||
MonsterID9Nightmare string // nmon9
|
||||
MonsterID10Nightmare string // nmon10
|
||||
|
||||
// Gravestench - adding additional fields for Hell, original txt combined
|
||||
// the nighmare and hell ID's stringo the same field
|
||||
MonsterID1Hell string // nmon1
|
||||
MonsterID2Hell string // nmon2
|
||||
MonsterID3Hell string // nmon3
|
||||
MonsterID4Hell string // nmon4
|
||||
MonsterID5Hell string // nmon5
|
||||
MonsterID6Hell string // nmon6
|
||||
MonsterID7Hell string // nmon7
|
||||
MonsterID8Hell string // nmon8
|
||||
MonsterID9Hell string // nmon9
|
||||
MonsterID10Hell string // nmon10
|
||||
|
||||
// Works only in normal and it tells which ID will be used for Champion and
|
||||
// Random Uniques. The ID is taken from MonStats.txtOnly the first ten
|
||||
// columns appear in the unmodded file. In 1.10 final, beta 1.10s and
|
||||
// v1.11+ you can add the missing umon11-umon25 columns.
|
||||
// NOTE: you can allow umon1-25 to also work in Nightmare and Hell by
|
||||
// following this simple ASM edit
|
||||
// (https://d2mods.info/forum/viewtopic.php?f=8&t=53969&p=425179&hilit=umon#p425179)
|
||||
MonsterUniqueID1 string // umon1
|
||||
MonsterUniqueID2 string // umon2
|
||||
MonsterUniqueID3 string // umon3
|
||||
MonsterUniqueID4 string // umon4
|
||||
MonsterUniqueID5 string // umon5
|
||||
MonsterUniqueID6 string // umon6
|
||||
MonsterUniqueID7 string // umon7
|
||||
MonsterUniqueID8 string // umon8
|
||||
MonsterUniqueID9 string // umon9
|
||||
MonsterUniqueID10 string // umon10
|
||||
|
||||
// Critter Species 1-4. Uses the Id from monstats2.txt and only monsters
|
||||
// with critter column set to 1 can spawn here. critter column is also found
|
||||
// in monstats2.txt. Critters are in reality only present clientside.
|
||||
MonsterCritterID1 string // cmon1
|
||||
MonsterCritterID2 string // cmon2
|
||||
MonsterCritterID3 string // cmon3
|
||||
MonsterCritterID4 string // cmon4
|
||||
|
||||
// String Code for the Display name of the Level
|
||||
LevelDisplayName string // LevelName
|
||||
|
||||
LevelWarpName string // LevelWarp
|
||||
|
||||
// Which *.DC6 Title Image is loaded when you enter this area. this file
|
||||
// MUST exist, otherwise you will crash with an exception when you enter the
|
||||
// level (for all levels below the expansion row, the files must be
|
||||
// present in the expension folders)
|
||||
TitleImageName string // EntryFile
|
||||
|
||||
// Id
|
||||
// Level ID (used in columns like VIS0-7)
|
||||
Id int //nolint:golint Id is the right key
|
||||
|
||||
// Palette is the Act Palette . Reference only
|
||||
Palette int // Pal
|
||||
|
||||
// The Act the Level is located in (internal enumeration ranges from 0 to 4)
|
||||
// Act that the Level is located in (internal enumeration ranges from 0 to 4)
|
||||
Act int // Act
|
||||
|
||||
// QuestFlag, QuestExpansionFlag
|
||||
// Used the first one in Classic games and the latter in Expansion games ,
|
||||
// they set a questflag. If this flag is set, a character must have
|
||||
// completed the quest associated with the flag to take a town portal to
|
||||
@ -37,17 +119,15 @@ type LevelDetailsRecord struct {
|
||||
// additional layers.
|
||||
AutomapIndex int // Layer
|
||||
|
||||
// sizeX - SizeY in each difficulty. If this is a preset area this sets the
|
||||
// SizeXNormal -- SizeYHell If this is a preset area this sets the
|
||||
// X size for the area. Othervise use the same value here that are used in
|
||||
// lvlprest.txt to set the size for the .ds1 file.
|
||||
SizeXNormal int // SizeX
|
||||
SizeYNormal int // SizeY
|
||||
|
||||
SizeXNormal int // SizeX
|
||||
SizeYNormal int // SizeY
|
||||
SizeXNightmare int // SizeX(N)
|
||||
SizeYNightmare int // SizeY(N)
|
||||
|
||||
SizeXHell int // SizeX(H)
|
||||
SizeYHell int // SizeY(H)
|
||||
SizeXHell int // SizeX(H)
|
||||
SizeYHell int // SizeY(H)
|
||||
|
||||
// They set the X\Y position in the world space
|
||||
WorldOffsetX int // OffsetX
|
||||
@ -58,6 +138,9 @@ type LevelDetailsRecord struct {
|
||||
// location.
|
||||
DependantLevelID int // Depend
|
||||
|
||||
// The type of the Level (Id from lvltypes.txt)
|
||||
LevelType int // LevelType
|
||||
|
||||
// Controls if teleport is allowed in that level.
|
||||
// 0 = Teleport not allowed
|
||||
// 1 = Teleport allowed
|
||||
@ -65,55 +148,12 @@ type LevelDetailsRecord struct {
|
||||
// (maybe for objects this is controlled by IsDoor column in objects.txt)
|
||||
TeleportFlag d2enum.TeleportFlag // Teleport
|
||||
|
||||
// It sets whether rain or snow (in act 5 only) can fall . Set it to 1 in
|
||||
// order to enable it, 0 to disable it.
|
||||
EnableRain bool // Rain
|
||||
|
||||
// Unused setting (In pre beta D2 Blizzard planned Rain to generate Mud
|
||||
// which would have slowed your character's speed down, but this never made
|
||||
// it into the final game). the field is read by the code but the return
|
||||
// value is never utilized.
|
||||
EnableMud bool // Mud
|
||||
|
||||
// Setting for 3D Enhanced D2 that disables Perspective Mode for a specific
|
||||
// level. A value of 1 enables the users to choose between normal and
|
||||
// Perspective view, while 0 disables that choice.
|
||||
EnablePerspective bool // NoPer
|
||||
|
||||
// Allows you to look through objects and walls even if they are not in a
|
||||
// wilderness level. 1 enables it, 0 disables it.
|
||||
EnableLineOfSightDraw bool // LOSDraw
|
||||
|
||||
// Unknown. Probably has to do with Tiles and their Placement.
|
||||
// 1 enables it, 0 disables it.
|
||||
EnableFloorFliter bool // FloorFilter
|
||||
|
||||
// Unknown. Probably has to do with tiles and their placement.
|
||||
// 1 enables it, 0 disables it.
|
||||
// TODO: needs a better name
|
||||
EnableBlankScreen bool // BlankScreen
|
||||
|
||||
// for levels bordered with mountains or walls, like the act 1 wildernesses.
|
||||
// 1 enables it, 0 disables it.
|
||||
EnableDrawEdges bool // DrawEdges
|
||||
|
||||
// Setting it to 1 makes the level to be treated as an indoor area, while
|
||||
// 0 makes this level an outdoor. Indoor areas are not affected by day-night
|
||||
// cycles, because they always use the light values specified in Intensity,
|
||||
// Red, Green, Blue. this field also controls whenever sounds will echo if
|
||||
// you're running the game with a sound card capable of it and have
|
||||
// environment sound effects set to true.
|
||||
IsInside bool // IsInside
|
||||
|
||||
// Setting for Level Generation: You have 3 possibilities here:
|
||||
// 1 Random Maze
|
||||
// 2 Preset Area
|
||||
// 3 Wilderness level
|
||||
LevelGenerationType d2enum.LevelGenerationType // DrlgType
|
||||
|
||||
// The type of the Level (Id from lvltypes.txt)
|
||||
LevelType int // LevelType
|
||||
|
||||
// NOTE
|
||||
// IDs from LvlSub.txt, which is used to randomize outdoor areas, such as
|
||||
// spawning ponds in the blood moor and more stones in the Stoney Field.
|
||||
@ -123,7 +163,6 @@ type LevelDetailsRecord struct {
|
||||
// Example: 6=wilderness, 9=desert etc, -1=no subtype.
|
||||
SubType int // SubType
|
||||
|
||||
// TODO this may need an enumeration.. ?
|
||||
// Tells which subtheme a wilderness area should use.
|
||||
// Themes ranges from -1 (no subtheme) to 4.
|
||||
SubTheme int // SubTheme
|
||||
@ -170,29 +209,6 @@ type LevelDetailsRecord struct {
|
||||
Green int // Green
|
||||
Blue int // Blue
|
||||
|
||||
// This field is required for some levels, entering those levels when portal
|
||||
// field isn't set will often crash the game. This also applies to
|
||||
// duplicates of those levels created with both of the extended level
|
||||
// plugins.
|
||||
PortalEnable bool // Portal
|
||||
// TODO: this field needs a better name
|
||||
|
||||
// This controls if you can re-position a portal in a level or not. If it's
|
||||
// set to 1 you will be able to reposition the portal by using either map
|
||||
// entry#76 Tp Location #79. If both tiles are in the level it will use Tp
|
||||
// Location #79. If set to 0 the map won't allow repositioning.
|
||||
PortalRepositionEnable bool // Position
|
||||
|
||||
// Setting this field to 1 will make the monsters status saved in the map.
|
||||
// Setting it to 0 will allow some useful things like NPC refreshing their
|
||||
// stores.
|
||||
// WARNING: Do not set this to 1 for non-town areas, or the monsters you'll
|
||||
// flee from will simply vanish and never reappear. They won't even be
|
||||
// replaced by new ones
|
||||
// Gravestench - this funcionality should not be in one field
|
||||
SaveMonsterStates bool // SaveMonsters
|
||||
SaveMerchantStates bool // SaveMonsters
|
||||
|
||||
// What quest is this level related to. This is the quest id (as example the
|
||||
// first quest Den of Evil are set to 1, since its the first quest).
|
||||
QuestID int // Quest
|
||||
@ -227,14 +243,6 @@ type LevelDetailsRecord struct {
|
||||
MonsterUniqueMaxNightmare int // MonUMax(N)
|
||||
MonsterUniqueMaxHell int // MonUMax(H)
|
||||
|
||||
// No info on the PK page, but I'm guessing it's for monster wandering
|
||||
MonsterWanderEnable bool // MonWndr
|
||||
|
||||
// This setting is hardcoded to certain level Ids, like the River Of Flame,
|
||||
// enabling it in other places can glitch up the game, so leave it alone.
|
||||
// It is not known what exactly it does however.
|
||||
MonsterSpecialWalk bool // MonSpcWalk
|
||||
|
||||
// Number of different Monster Types that will be present in this area, the
|
||||
// maximum is 13. You can have up to 13 different monster types at a time in
|
||||
// Nightmare and Hell difficulties, selected randomly from nmon1-nmon25. In
|
||||
@ -243,92 +251,12 @@ type LevelDetailsRecord struct {
|
||||
// types selected randomly from umon1-umon25.
|
||||
NumMonsterTypes int // NumMon
|
||||
|
||||
// mon1-mon25 work in Normal difficulty, while nmon1-nmon25 in Nightmare and
|
||||
// Hell. They tell the game which monster ID taken from MonStats.txt.
|
||||
// NOTE: you need to manually add from mon11 to mon25 and from nmon11 to
|
||||
// nmon25 !
|
||||
MonsterID1Normal string // mon1
|
||||
MonsterID2Normal string // mon2
|
||||
MonsterID3Normal string // mon3
|
||||
MonsterID4Normal string // mon4
|
||||
MonsterID5Normal string // mon5
|
||||
MonsterID6Normal string // mon6
|
||||
MonsterID7Normal string // mon7
|
||||
MonsterID8Normal string // mon8
|
||||
MonsterID9Normal string // mon9
|
||||
MonsterID10Normal string // mon10
|
||||
|
||||
MonsterID1Nightmare string // nmon1
|
||||
MonsterID2Nightmare string // nmon2
|
||||
MonsterID3Nightmare string // nmon3
|
||||
MonsterID4Nightmare string // nmon4
|
||||
MonsterID5Nightmare string // nmon5
|
||||
MonsterID6Nightmare string // nmon6
|
||||
MonsterID7Nightmare string // nmon7
|
||||
MonsterID8Nightmare string // nmon8
|
||||
MonsterID9Nightmare string // nmon9
|
||||
MonsterID10Nightmare string // nmon10
|
||||
|
||||
// Gravestench - adding additional fields for Hell, original txt combined
|
||||
// the nighmare and hell ID's stringo the same field
|
||||
MonsterID1Hell string // nmon1
|
||||
MonsterID2Hell string // nmon2
|
||||
MonsterID3Hell string // nmon3
|
||||
MonsterID4Hell string // nmon4
|
||||
MonsterID5Hell string // nmon5
|
||||
MonsterID6Hell string // nmon6
|
||||
MonsterID7Hell string // nmon7
|
||||
MonsterID8Hell string // nmon8
|
||||
MonsterID9Hell string // nmon9
|
||||
MonsterID10Hell string // nmon10
|
||||
|
||||
// Give preference to monsters set to ranged=1 in MonStats.txt on Nightmare
|
||||
// and Hell difficulties when picking something to spawn.
|
||||
MonsterPreferRanged bool // rangedspawn
|
||||
|
||||
// Works only in normal and it tells which ID will be used for Champion and
|
||||
// Random Uniques. The ID is taken from MonStats.txtOnly the first ten
|
||||
// columns appear in the unmodded file. In 1.10 final, beta 1.10s and
|
||||
// v1.11+ you can add the missing umon11-umon25 columns.
|
||||
// NOTE: you can allow umon1-25 to also work in Nightmare and Hell by
|
||||
// following this simple ASM edit
|
||||
// (https://d2mods.info/forum/viewtopic.php?f=8&t=53969&p=425179&hilit=umon#p425179)
|
||||
MonsterUniqueID1 string // umon1
|
||||
MonsterUniqueID2 string // umon2
|
||||
MonsterUniqueID3 string // umon3
|
||||
MonsterUniqueID4 string // umon4
|
||||
MonsterUniqueID5 string // umon5
|
||||
MonsterUniqueID6 string // umon6
|
||||
MonsterUniqueID7 string // umon7
|
||||
MonsterUniqueID8 string // umon8
|
||||
MonsterUniqueID9 string // umon9
|
||||
MonsterUniqueID10 string // umon10
|
||||
|
||||
// Critter Species 1-4. Uses the Id from monstats2.txt and only monsters
|
||||
// with critter column set to 1 can spawn here. critter column is also found
|
||||
// in monstats2.txt. Critters are in reality only present clientside.
|
||||
MonsterCritterID1 string // cmon1
|
||||
MonsterCritterID2 string // cmon2
|
||||
MonsterCritterID3 string // cmon3
|
||||
MonsterCritterID4 string // cmon4
|
||||
|
||||
// Controls the chance for a critter to spawn.
|
||||
MonsterCritter1SpawnChance int // cpct1
|
||||
MonsterCritter2SpawnChance int // cpct2
|
||||
MonsterCritter3SpawnChance int // cpct3
|
||||
MonsterCritter4SpawnChance int // cpct4
|
||||
|
||||
// Unknown. These columns are bugged, as the game overrides the contents of
|
||||
// columns 3-4 with the value from column 1 when it compiles the bin files.
|
||||
// camt1
|
||||
// camt2
|
||||
// camt3
|
||||
// camt4
|
||||
|
||||
// Unknown. It states which theme is used by the area and this field is
|
||||
// accessed by the code but it is not exactly known what it does.
|
||||
// Themes
|
||||
|
||||
// Referes to a entry in SoundEnviron.txt (for the Levels Music)
|
||||
SoundEnvironmentID int // SoundEnv
|
||||
|
||||
@ -338,17 +266,6 @@ type LevelDetailsRecord struct {
|
||||
// between acts however so don't even bother to try.
|
||||
WaypointID int // Waypoint
|
||||
|
||||
// String Code for the Display name of the Level
|
||||
LevelDisplayName string // LevelName
|
||||
|
||||
LevelWarpName string // LevelWarp
|
||||
|
||||
// Which *.DC6 Title Image is loaded when you enter this area. this file
|
||||
// MUST exist, otherwise you will crash with an exception when you enter the
|
||||
// level (for all levels below the expansion row, the files must be
|
||||
// present in the expension folders)
|
||||
TitleImageName string // EntryFile
|
||||
|
||||
// this field uses the ID of the ObjectGroup you want to Spawn in this Area,
|
||||
// taken from Objgroup.txt.
|
||||
ObjectGroupID0 int // ObjGrp0
|
||||
@ -371,12 +288,86 @@ type LevelDetailsRecord struct {
|
||||
ObjectGroupSpawnChance6 int // ObjPrb6
|
||||
ObjectGroupSpawnChance7 int // ObjPrb7
|
||||
|
||||
// Reference Only (can be used for comments)
|
||||
// Beta
|
||||
// It sets whether rain or snow (in act 5 only) can fall . Set it to 1 in
|
||||
// order to enable it, 0 to disable it.
|
||||
EnableRain bool // Rain
|
||||
|
||||
// Unused setting (In pre beta D2 Blizzard planned Rain to generate Mud
|
||||
// which would have slowed your character's speed down, but this never made
|
||||
// it into the final game). the field is read by the code but the return
|
||||
// value is never utilized.
|
||||
EnableMud bool // Mud
|
||||
|
||||
// Setting for 3D Enhanced D2 that disables Perspective Mode for a specific
|
||||
// level. A value of 1 enables the users to choose between normal and
|
||||
// Perspective view, while 0 disables that choice.
|
||||
EnablePerspective bool // NoPer
|
||||
|
||||
// Allows you to look through objects and walls even if they are not in a
|
||||
// wilderness level. 1 enables it, 0 disables it.
|
||||
EnableLineOfSightDraw bool // LOSDraw
|
||||
|
||||
// Unknown. Probably has to do with Tiles and their Placement.
|
||||
// 1 enables it, 0 disables it.
|
||||
EnableFloorFliter bool // FloorFilter
|
||||
|
||||
// Unknown. Probably has to do with tiles and their placement.
|
||||
// 1 enables it, 0 disables it.
|
||||
EnableBlankScreen bool // BlankScreen
|
||||
|
||||
// for levels bordered with mountains or walls, like the act 1 wildernesses.
|
||||
// 1 enables it, 0 disables it.
|
||||
EnableDrawEdges bool // DrawEdges
|
||||
|
||||
// Setting it to 1 makes the level to be treated as an indoor area, while
|
||||
// 0 makes this level an outdoor. Indoor areas are not affected by day-night
|
||||
// cycles, because they always use the light values specified in Intensity,
|
||||
// Red, Green, Blue. this field also controls whenever sounds will echo if
|
||||
// you're running the game with a sound card capable of it and have
|
||||
// environment sound effects set to true.
|
||||
IsInside bool // IsInside
|
||||
|
||||
// This field is required for some levels, entering those levels when portal
|
||||
// field isn't set will often crash the game. This also applies to
|
||||
// duplicates of those levels created with both of the extended level
|
||||
// plugins.
|
||||
PortalEnable bool // Portal
|
||||
|
||||
// This controls if you can re-position a portal in a level or not. If it's
|
||||
// set to 1 you will be able to reposition the portal by using either map
|
||||
// entry#76 Tp Location #79. If both tiles are in the level it will use Tp
|
||||
// Location #79. If set to 0 the map won't allow repositioning.
|
||||
PortalRepositionEnable bool // Position
|
||||
|
||||
// Setting this field to 1 will make the monsters status saved in the map.
|
||||
// Setting it to 0 will allow some useful things like NPC refreshing their
|
||||
// stores.
|
||||
// WARNING: Do not set this to 1 for non-town areas, or the monsters you'll
|
||||
// flee from will simply vanish and never reappear. They won't even be
|
||||
// replaced by new ones
|
||||
// Gravestench - this funcionality should not be in one field
|
||||
SaveMonsterStates bool // SaveMonsters
|
||||
SaveMerchantStates bool // SaveMonsters
|
||||
|
||||
// No info on the PK page, but I'm guessing it's for monster wandering
|
||||
MonsterWanderEnable bool // MonWndr
|
||||
|
||||
// This setting is hardcoded to certain level Ids, like the River Of Flame,
|
||||
// enabling it in other places can glitch up the game, so leave it alone.
|
||||
// It is not known what exactly it does however.
|
||||
MonsterSpecialWalk bool // MonSpcWalk
|
||||
|
||||
// Give preference to monsters set to ranged=1 in MonStats.txt on Nightmare
|
||||
// and Hell difficulties when picking something to spawn.
|
||||
MonsterPreferRanged bool // rangedspawn
|
||||
|
||||
}
|
||||
|
||||
// LevelDetails has all of the LevelDetailsRecords
|
||||
//nolint:gochecknoglobals // Currently global by design, only written once
|
||||
var LevelDetails map[int]*LevelDetailsRecord
|
||||
|
||||
// GetLevelDetails gets a LevelDetailsRecord by the record Id
|
||||
func GetLevelDetails(id int) *LevelDetailsRecord {
|
||||
for i := 0; i < len(LevelDetails); i++ {
|
||||
if LevelDetails[i].Id == id {
|
||||
@ -387,6 +378,7 @@ func GetLevelDetails(id int) *LevelDetailsRecord {
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadLevelDetails loads level details records from levels.txt
|
||||
//nolint:funlen // Txt loader, makes no sense to split
|
||||
func LoadLevelDetails(file []byte) {
|
||||
dict := d2common.LoadDataDictionary(string(file))
|
||||
|
@ -6,7 +6,7 @@ import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
)
|
||||
|
||||
func MapHeaders(line string) map[string]int {
|
||||
func mapHeaders(line string) map[string]int {
|
||||
m := make(map[string]int)
|
||||
r := strings.Split(line, "\t")
|
||||
|
||||
@ -17,8 +17,8 @@ func MapHeaders(line string) map[string]int {
|
||||
return m
|
||||
}
|
||||
|
||||
func MapLoadInt(r *[]string, mapping *map[string]int, field string) int {
|
||||
index, ok := (*mapping)[field]
|
||||
func mapLoadInt(r *[]string, mapping map[string]int, field string) int {
|
||||
index, ok := (mapping)[field]
|
||||
if ok {
|
||||
return d2common.StringToInt(d2common.EmptyToZero(d2common.AsterToEmpty((*r)[index])))
|
||||
}
|
||||
@ -26,8 +26,8 @@ func MapLoadInt(r *[]string, mapping *map[string]int, field string) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func MapLoadString(r *[]string, mapping *map[string]int, field string) string {
|
||||
index, ok := (*mapping)[field]
|
||||
func mapLoadString(r *[]string, mapping map[string]int, field string) string {
|
||||
index, ok := (mapping)[field]
|
||||
if ok {
|
||||
return d2common.AsterToEmpty((*r)[index])
|
||||
}
|
||||
@ -35,12 +35,12 @@ func MapLoadString(r *[]string, mapping *map[string]int, field string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func MapLoadBool(r *[]string, mapping *map[string]int, field string) bool {
|
||||
return MapLoadInt(r, mapping, field) == 1
|
||||
func mapLoadBool(r *[]string, mapping map[string]int, field string) bool {
|
||||
return mapLoadInt(r, mapping, field) == 1
|
||||
}
|
||||
|
||||
func MapLoadUint8(r *[]string, mapping *map[string]int, field string) uint8 {
|
||||
index, ok := (*mapping)[field]
|
||||
func mapLoadUint8(r *[]string, mapping map[string]int, field string) uint8 {
|
||||
index, ok := (mapping)[field]
|
||||
if ok {
|
||||
return d2common.StringToUint8(d2common.EmptyToZero(d2common.AsterToEmpty((*r)[index])))
|
||||
}
|
||||
|
@ -6,9 +6,11 @@ import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||
)
|
||||
|
||||
var MiscItems map[string]*ItemCommonRecord
|
||||
// MiscItems stores all of the ItemCommonRecords for misc.txt
|
||||
var MiscItems map[string]*ItemCommonRecord //nolint:gochecknoglobals // Currently global by design
|
||||
|
||||
// LoadMiscItems loads ItemCommonRecords from misc.txt
|
||||
func LoadMiscItems(file []byte) {
|
||||
MiscItems = *LoadCommonItems(file, d2enum.InventoryItemTypeItem)
|
||||
MiscItems = LoadCommonItems(file, d2enum.InventoryItemTypeItem)
|
||||
log.Printf("Loaded %d misc items", len(MiscItems))
|
||||
}
|
||||
|
@ -7,17 +7,20 @@ import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
)
|
||||
|
||||
// MissileCalcParam is a calculation parameter for a missile
|
||||
type MissileCalcParam struct {
|
||||
Param int
|
||||
Desc string
|
||||
}
|
||||
|
||||
// MissileCalc is a calculation for a missile
|
||||
type MissileCalc struct {
|
||||
Calc d2common.CalcString
|
||||
Desc string
|
||||
Params []MissileCalcParam
|
||||
}
|
||||
|
||||
// MissileLight has the parameters for missile lighting
|
||||
type MissileLight struct {
|
||||
Diameter int
|
||||
Flicker int
|
||||
@ -26,24 +29,27 @@ type MissileLight struct {
|
||||
Blue uint8
|
||||
}
|
||||
|
||||
// MissileAnimation stores parameters for a missile animation
|
||||
type MissileAnimation struct {
|
||||
CelFileName string
|
||||
StepsBeforeVisible int
|
||||
StepsBeforeActive int
|
||||
LoopAnimation bool
|
||||
CelFileName string
|
||||
AnimationRate int // seems to do nothing
|
||||
AnimationLength int
|
||||
AnimationSpeed int
|
||||
StartingFrame int // called "RandFrame"
|
||||
HasSubLoop bool // runs after first animation ends
|
||||
StartingFrame int // called "RandFrame"
|
||||
SubStartingFrame int
|
||||
SubEndingFrame int
|
||||
LoopAnimation bool
|
||||
HasSubLoop bool // runs after first animation ends
|
||||
}
|
||||
|
||||
// MissileCollision parameters for missile collision
|
||||
type MissileCollision struct {
|
||||
CollisionType int // controls the kind of collision
|
||||
// 0 = none, 1 = units only, 3 = normal (units, walls),
|
||||
// 6 = walls only, 8 = walls, units, and floors
|
||||
TimerFrames int // how many frames to persist
|
||||
DestroyedUponCollision bool
|
||||
FriendlyFire bool
|
||||
LastCollide bool // unknown
|
||||
@ -51,9 +57,9 @@ type MissileCollision struct {
|
||||
ClientCollision bool // unknown
|
||||
ClientSend bool // unclear
|
||||
UseCollisionTimer bool // after hit, use timer before dying
|
||||
TimerFrames int // how many frames to persist
|
||||
}
|
||||
|
||||
// MissileDamage parameters for calculating missile physical damage
|
||||
type MissileDamage struct {
|
||||
MinDamage int
|
||||
MaxDamage int
|
||||
@ -63,6 +69,7 @@ type MissileDamage struct {
|
||||
DamageSynergyPerCalc d2common.CalcString // works like synergy in skills.txt, not clear
|
||||
}
|
||||
|
||||
// MissileElementalDamage parameters for calculating missile elemental damage
|
||||
type MissileElementalDamage struct {
|
||||
Damage MissileDamage
|
||||
ElementType string
|
||||
@ -70,20 +77,46 @@ type MissileElementalDamage struct {
|
||||
LevelDuration [3]int // 0,1,2, unknown level intervals, bonus duration per level
|
||||
}
|
||||
|
||||
// MissileRecord is a representation of a row from missiles.txt
|
||||
type MissileRecord struct {
|
||||
Name string
|
||||
Id int
|
||||
ServerMovementCalc MissileCalc
|
||||
ClientMovementCalc MissileCalc
|
||||
ServerCollisionCalc MissileCalc
|
||||
ClientCollisionCalc MissileCalc
|
||||
ServerDamageCalc MissileCalc
|
||||
Light MissileLight
|
||||
Animation MissileAnimation
|
||||
Collision MissileCollision
|
||||
Damage MissileDamage
|
||||
ElementalDamage MissileElementalDamage
|
||||
SubMissile [3]string // 0,1,2 name of missiles spawned by movement function
|
||||
HitSubMissile [4]string // 0,1,2 name of missiles spawned by collision function
|
||||
ClientSubMissile [3]string // see above, but for client only
|
||||
ClientHitSubMissile [4]string // see above, but for client only
|
||||
Name string
|
||||
|
||||
SkillName string // if not empty, the missile will refer to this skill instead of its own data for the following:
|
||||
// "ResultFlags, HitFlags, HitShift, HitClass, SrcDamage (SrcDam in skills.txt!),
|
||||
// MinDam, MinLevDam1-5, MaxDam, MaxLevDam1-5, DmgSymPerCalc, EType, EMin, EMinLev1-5,
|
||||
// EMax, EMaxLev1-5, EDmgSymPerCalc, ELen, ELenLev1-3, ELenSymPerCalc"
|
||||
|
||||
TravelSound string // name of sound to play during lifetime
|
||||
// whether or not it loops depends on the specific sound's settings?
|
||||
// if it doesn't loop, it's just a on-spawn sound effect
|
||||
HitSound string // sound plays upon collision
|
||||
ProgSound string // plays at "special events", like a mariachi band
|
||||
|
||||
ProgOverlay string // name of an overlay from overlays.txt to use at special events
|
||||
ExplosionMissile string // name of a missile from missiles.txt that is created upon collision
|
||||
// or anytime it is destroyed if AlwaysExplode is true
|
||||
|
||||
Id int //nolint:golint Id is the correct key
|
||||
|
||||
ClientMovementFunc int
|
||||
ClientCollisionFunc int
|
||||
ServerMovementFunc int
|
||||
ServerCollisionFunc int
|
||||
ServerDamageFunc int
|
||||
ServerMovementCalc MissileCalc
|
||||
ClientMovementCalc MissileCalc
|
||||
ServerCollisionCalc MissileCalc
|
||||
ClientCollisionCalc MissileCalc
|
||||
ServerDamageCalc MissileCalc
|
||||
|
||||
Velocity int
|
||||
MaxVelocity int
|
||||
@ -92,20 +125,46 @@ type MissileRecord struct {
|
||||
Range int
|
||||
LevelRangeBonus int
|
||||
|
||||
Light MissileLight
|
||||
|
||||
Animation MissileAnimation
|
||||
|
||||
Collision MissileCollision
|
||||
|
||||
XOffset int
|
||||
YOffset int
|
||||
ZOffset int
|
||||
Size int // diameter
|
||||
|
||||
DestroyedByTP bool // if true, destroyed when source player teleports to town
|
||||
DestroyedByTPFrame int // see above, for client side, (this is a guess) which frame it vanishes on
|
||||
CanDestroy bool // unknown
|
||||
DestroyedByTPFrame int // see above, for client side, (this is a guess) which frame it vanishes on
|
||||
|
||||
HolyFilterType int // specifies what this missile can hit
|
||||
KnockbackPercent int // chance of knocking the target back, 0-100
|
||||
|
||||
TransparencyMode int // controls rendering
|
||||
// 0 = normal, 1 = alpha blending (darker = more transparent)
|
||||
// 2 = special (black and white?)
|
||||
|
||||
ResultFlags int // unknown
|
||||
// 4 = normal missiles, 5 = explosions, 8 = non-damaging missiles
|
||||
HitFlags int // unknown
|
||||
// 2 = explosions, 5 = freezing arrow
|
||||
|
||||
HitShift int // damage is measured in 256s
|
||||
// the actual damage is [damage] * 2 ^ [hitshift]
|
||||
// e.g. 100 damage, 8 hitshift = 100 * 2 ^ 8 = 100 * 256 = 25600
|
||||
// (visually, the damage is this result / 256)
|
||||
|
||||
SourceDamage int // 0-128, 128 is 100%
|
||||
SourceMissDamage int // 0-128, 128 is 100%
|
||||
// unknown, only used for poison clouds.
|
||||
|
||||
HitClass int // controls clientside aesthetic effects for collisions
|
||||
// particularly sound effects that are played on a hit
|
||||
NumDirections int // count of dirs in the DCC loaded by CelFile
|
||||
// apparently this value is no longer needed in D2
|
||||
LocalBlood int // blood effects?
|
||||
// 0 = no blood, 1 = blood, 2 = blood and affected by open wounds
|
||||
DamageReductionRate int // how many frames between applications of the
|
||||
// magic_damage_reduced stat, so for instance on a 0 this stat applies every frame
|
||||
// on a 3, only every 4th frame has damage reduced
|
||||
|
||||
DestroyedByTP bool // if true, destroyed when source player teleports to town
|
||||
CanDestroy bool // unknown
|
||||
|
||||
UseAttackRating bool // if true, uses 'attack rating' to determine if it hits or misses
|
||||
// if false, has a 95% chance to hit.
|
||||
@ -120,74 +179,25 @@ type MissileRecord struct {
|
||||
// if false, vanishes when spawned in town
|
||||
IgnoreBossModifiers bool // if true, doesn't get bonuses from boss mods
|
||||
IgnoreMultishot bool // if true, can't gain the mulitshot modifier
|
||||
HolyFilterType int // specifies what this missile can hit
|
||||
// 0 = all units, 1 = undead only, 2 = demons only, 3 = all units (again?)
|
||||
CanBeSlowed bool // if true, is affected by skill_handofathena
|
||||
TriggersHitEvents bool // if true, triggers events that happen "upon getting hit" on targets
|
||||
TriggersGetHit bool // if true, can cause target to enter hit recovery mode
|
||||
SoftHit bool // unknown
|
||||
KnockbackPercent int // chance of knocking the target back, 0-100
|
||||
|
||||
TransparencyMode int // controls rendering
|
||||
// 0 = normal, 1 = alpha blending (darker = more transparent)
|
||||
// 2 = special (black and white?)
|
||||
|
||||
UseQuantity bool // if true, uses quantity
|
||||
// not clear what this means. Also apparently requires a special starting function in skills.txt
|
||||
AffectedByPierce bool // if true, affected by the pierce modifier and the Pierce skill
|
||||
SpecialSetup bool // unknown, only true for potions
|
||||
|
||||
MissileSkill bool // if true, applies elemental damage from items to the splash radius instead of normal damage modifiers
|
||||
SkillName string // if not empty, the missile will refer to this skill instead of its own data for the following:
|
||||
// "ResultFlags, HitFlags, HitShift, HitClass, SrcDamage (SrcDam in skills.txt!),
|
||||
// MinDam, MinLevDam1-5, MaxDam, MaxLevDam1-5, DmgSymPerCalc, EType, EMin, EMinLev1-5,
|
||||
// EMax, EMaxLev1-5, EDmgSymPerCalc, ELen, ELenLev1-3, ELenSymPerCalc"
|
||||
MissileSkill bool // if true, applies elemental damage from items to the splash radius instead of normal damage modifiers
|
||||
|
||||
ResultFlags int // unknown
|
||||
// 4 = normal missiles, 5 = explosions, 8 = non-damaging missiles
|
||||
HitFlags int // unknown
|
||||
// 2 = explosions, 5 = freezing arrow
|
||||
|
||||
HitShift int // damage is measured in 256s
|
||||
// the actual damage is [damage] * 2 ^ [hitshift]
|
||||
// e.g. 100 damage, 8 hitshift = 100 * 2 ^ 8 = 100 * 256 = 25600
|
||||
// (visually, the damage is this result / 256)
|
||||
ApplyMastery bool // unknown
|
||||
SourceDamage int // 0-128, 128 is 100%
|
||||
// percentage of source units attack properties to apply to the missile?
|
||||
// not only affects damage but also other modifiers like lifesteal and manasteal (need a complete list)
|
||||
// setting this to -1 "gets rid of SrcDmg from skills.txt", not clear what that means
|
||||
HalfDamageForTwoHander bool // if true, damage is halved when a two-handed weapon is used
|
||||
SourceMissDamage int // 0-128, 128 is 100%
|
||||
// unknown, only used for poison clouds.
|
||||
|
||||
Damage MissileDamage
|
||||
ElementalDamage MissileElementalDamage
|
||||
|
||||
HitClass int // controls clientside aesthetic effects for collisions
|
||||
// particularly sound effects that are played on a hit
|
||||
NumDirections int // count of dirs in the DCC loaded by CelFile
|
||||
// apparently this value is no longer needed in D2
|
||||
LocalBlood int // blood effects?
|
||||
// 0 = no blood, 1 = blood, 2 = blood and affected by open wounds
|
||||
DamageReductionRate int // how many frames between applications of the
|
||||
// magic_damage_reduced stat, so for instance on a 0 this stat applies every frame
|
||||
// on a 3, only every 4th frame has damage reduced
|
||||
|
||||
TravelSound string // name of sound to play during lifetime
|
||||
// whether or not it loops depends on the specific sound's settings?
|
||||
// if it doesn't loop, it's just a on-spawn sound effect
|
||||
HitSound string // sound plays upon collision
|
||||
ProgSound string // plays at "special events", like a mariachi band
|
||||
|
||||
ProgOverlay string // name of an overlay from overlays.txt to use at special events
|
||||
ExplosionMissile string // name of a missile from missiles.txt that is created upon collision
|
||||
// or anytime it is destroyed if AlwaysExplode is true
|
||||
|
||||
SubMissile [3]string // 0,1,2 name of missiles spawned by movement function
|
||||
HitSubMissile [4]string // 0,1,2 name of missiles spawned by collision function
|
||||
ClientSubMissile [3]string // see above, but for client only
|
||||
ClientHitSubMissile [4]string // see above, but for client only
|
||||
}
|
||||
|
||||
func createMissileRecord(line string) MissileRecord {
|
||||
@ -292,9 +302,11 @@ func createMissileRecord(line string) MissileRecord {
|
||||
return result
|
||||
}
|
||||
|
||||
// Missiles stores all of the MissileRecords
|
||||
//nolint:gochecknoglobals // Currently global by design, only written once
|
||||
var Missiles map[int]*MissileRecord
|
||||
|
||||
// LoadMissiles loads MissileRecords from missiles.txt
|
||||
func LoadMissiles(file []byte) {
|
||||
Missiles = make(map[int]*MissileRecord)
|
||||
data := strings.Split(string(file), "\r\n")[1:]
|
||||
|
@ -6,9 +6,11 @@ import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
)
|
||||
|
||||
// MonPresets stores monster presets
|
||||
//nolint:gochecknoglobals // Currently global by design, only written once
|
||||
var MonPresets [][]string
|
||||
|
||||
// LoadMonPresets loads monster presets from monpresets.txt
|
||||
func LoadMonPresets(file []byte) {
|
||||
dict := d2common.LoadDataDictionary(string(file))
|
||||
numRecords := len(dict.Data)
|
||||
|
@ -1,4 +1,3 @@
|
||||
// d2datadict contains loaders for the txt file data
|
||||
package d2datadict
|
||||
|
||||
import (
|
||||
@ -29,7 +28,7 @@ type (
|
||||
// column also links other hardcoded effects to the units, such as the
|
||||
// transparency on necro summons and the name-color change on unique boss
|
||||
// units (thanks to Kingpin for the info)
|
||||
Id string // called `hcIdx` in monstats.txt
|
||||
Id string // called `hcIdx` in monstats.txt //nolint:golint Id is the right key
|
||||
|
||||
// BaseKey is an ID pointer of the “base” unit for this specific
|
||||
// monster type (ex. There are five types of “Fallen”; all of them have
|
||||
@ -82,9 +81,11 @@ type (
|
||||
SpawnAnimationKey string // called `spawnmode` in monstats.txt
|
||||
|
||||
// MinionId1 is an Id of a minion that spawns when this monster is created
|
||||
//nolint:golint Id is the right key
|
||||
MinionId1 string // called `minion1` in monstats.txt
|
||||
|
||||
// MinionId2 is an Id of a minion that spawns when this monster is created
|
||||
//nolint:golint Id is the right key
|
||||
MinionId2 string // called `minion2` in monstats.txt
|
||||
|
||||
// SoundKeyNormal, SoundKeySpecial
|
||||
@ -114,14 +115,14 @@ type (
|
||||
// the ID Pointer to the skill (from Skills.txt) the monster will cast when
|
||||
// this specific slot is accessed by the AI. Which slots are used is
|
||||
// determined by the units AI.
|
||||
SkillId1 string // called `Skill1` in monstats.txt
|
||||
SkillId2 string // called `Skill2` in monstats.txt
|
||||
SkillId3 string // called `Skill3` in monstats.txt
|
||||
SkillId4 string // called `Skill4` in monstats.txt
|
||||
SkillId5 string // called `Skill5` in monstats.txt
|
||||
SkillId6 string // called `Skill6` in monstats.txt
|
||||
SkillId7 string // called `Skill7` in monstats.txt
|
||||
SkillId8 string // called `Skill8` in monstats.txt
|
||||
SkillId1 string // called `Skill1` in monstats.txt //nolint:golint Id is the right key
|
||||
SkillId2 string // called `Skill2` in monstats.txt //nolint:golint Id is the right key
|
||||
SkillId3 string // called `Skill3` in monstats.txt //nolint:golint Id is the right key
|
||||
SkillId4 string // called `Skill4` in monstats.txt //nolint:golint Id is the right key
|
||||
SkillId5 string // called `Skill5` in monstats.txt //nolint:golint Id is the right key
|
||||
SkillId6 string // called `Skill6` in monstats.txt //nolint:golint Id is the right key
|
||||
SkillId7 string // called `Skill7` in monstats.txt //nolint:golint Id is the right key
|
||||
SkillId8 string // called `Skill8` in monstats.txt //nolint:golint Id is the right key
|
||||
|
||||
// SkillAnimation1 -- SkillAnimation8
|
||||
// the graphical MODE (or SEQUENCE) this unit uses when it uses this skill.
|
||||
@ -138,6 +139,7 @@ type (
|
||||
// ID Pointer to the skill that controls this units damage. This is used for
|
||||
// the druids summons. IE their damage is specified solely by Skills.txt and
|
||||
// not by MonStats.txt.
|
||||
//nolint:golint Id is the right key
|
||||
DamageSkillId string // called `SkillDamage` in monstats.txt
|
||||
|
||||
// ElementAttackMode1 -- ElementAttackMode3
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||
)
|
||||
|
||||
// MonStats2Record is a representation of a row from monstats2.txt
|
||||
type MonStats2Record struct {
|
||||
// Key, the object ID MonStatEx feild from MonStat
|
||||
Key string
|
||||
@ -225,9 +226,11 @@ type MonStats2Record struct {
|
||||
ResurrectSkill string
|
||||
}
|
||||
|
||||
// MonStats2 stores all of the MonStats2Records
|
||||
//nolint:gochecknoglobals // Current design issue
|
||||
var MonStats2 map[string]*MonStats2Record
|
||||
|
||||
// LoadMonStats2 loads MonStats2Records from monstats2.txt
|
||||
//nolint:funlen //just a big data loader
|
||||
func LoadMonStats2(file []byte) {
|
||||
dict := d2common.LoadDataDictionary(string(file))
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -2,23 +2,19 @@ package d2datadict
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||
)
|
||||
|
||||
type ObjectType int
|
||||
|
||||
const (
|
||||
ObjectTypeCharacter ObjectType = 1
|
||||
ObjectTypeItem ObjectType = 2
|
||||
)
|
||||
|
||||
// ObjectLookupRecord is a representation of a row from objects.txt
|
||||
type ObjectLookupRecord struct {
|
||||
Act int
|
||||
Type ObjectType
|
||||
Id int
|
||||
Type d2enum.ObjectType
|
||||
Id int //nolint:golint Id is the right key
|
||||
Name string
|
||||
Description string
|
||||
ObjectsTxtId int
|
||||
MonstatsTxtId int
|
||||
ObjectsTxtId int //nolint:golint Id is the right key
|
||||
MonstatsTxtId int //nolint:golint Id is the right key
|
||||
Direction int
|
||||
Base string
|
||||
Token string
|
||||
@ -44,6 +40,7 @@ type ObjectLookupRecord struct {
|
||||
Index int
|
||||
}
|
||||
|
||||
// LookupObject looks up an object record
|
||||
func LookupObject(act, typ, id int) *ObjectLookupRecord {
|
||||
object := lookupObject(act, typ, id, indexedObjects)
|
||||
if object == nil {
|
||||
@ -54,14 +51,23 @@ func LookupObject(act, typ, id int) *ObjectLookupRecord {
|
||||
}
|
||||
|
||||
func lookupObject(act, typ, id int, objects [][][]*ObjectLookupRecord) *ObjectLookupRecord {
|
||||
if objects[act] != nil && objects[act][typ] != nil && objects[act][typ][id] != nil {
|
||||
return objects[act][typ][id]
|
||||
if len(objects) < act {
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
if len(objects[act]) < typ {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(objects[act][typ]) < id {
|
||||
return nil
|
||||
}
|
||||
|
||||
return objects[act][typ][id]
|
||||
}
|
||||
|
||||
func init() {
|
||||
// InitObjectRecords loads ObjectRecords
|
||||
func InitObjectRecords() {
|
||||
indexedObjects = indexObjects(objectLookups)
|
||||
}
|
||||
|
||||
@ -88,7 +94,8 @@ func indexObjects(objects []ObjectLookupRecord) [][][]*ObjectLookupRecord {
|
||||
return indexedObjects
|
||||
}
|
||||
|
||||
// Indexed slice of object records for quick lookups.
|
||||
// IndexedObjects is a slice of object records for quick lookups.
|
||||
// nil checks should be done for uninitialized values at each level.
|
||||
// [Act 1-5][Type 1-2][Id 0-855]
|
||||
//nolint:gochecknoglobals // Currently global by design
|
||||
var indexedObjects [][][]*ObjectLookupRecord
|
||||
|
@ -3,6 +3,8 @@ package d2datadict
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||
|
||||
testify "github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@ -11,28 +13,28 @@ func TestIndexObjects(t *testing.T) {
|
||||
assert := testify.New(t)
|
||||
|
||||
testObjects := []ObjectLookupRecord{
|
||||
{Act: 1, Type: ObjectTypeCharacter, Id: 0, Description: "Act1CharId0"},
|
||||
{Act: 1, Type: ObjectTypeCharacter, Id: 1, Description: "Act1CharId1"},
|
||||
{Act: 1, Type: ObjectTypeCharacter, Id: 2, Description: "Act1CharId2"},
|
||||
{Act: 1, Type: ObjectTypeCharacter, Id: 3, Description: "Act1CharId3"},
|
||||
{Act: 1, Type: ObjectTypeItem, Id: 0, Description: "Act1ItemId0"},
|
||||
{Act: 1, Type: ObjectTypeItem, Id: 1, Description: "Act1ItemId1"},
|
||||
{Act: 1, Type: ObjectTypeItem, Id: 2, Description: "Act1ItemId2"},
|
||||
{Act: 1, Type: ObjectTypeItem, Id: 3, Description: "Act1ItemId3"},
|
||||
{Act: 2, Type: ObjectTypeCharacter, Id: 0, Description: "Act2CharId0"},
|
||||
{Act: 2, Type: ObjectTypeCharacter, Id: 1, Description: "Act2CharId1"},
|
||||
{Act: 2, Type: ObjectTypeCharacter, Id: 2, Description: "Act2CharId2"},
|
||||
{Act: 2, Type: ObjectTypeCharacter, Id: 3, Description: "Act2CharId3"},
|
||||
{Act: 2, Type: ObjectTypeItem, Id: 0, Description: "Act2ItemId0"},
|
||||
{Act: 2, Type: ObjectTypeItem, Id: 1, Description: "Act2ItemId1"},
|
||||
{Act: 2, Type: ObjectTypeItem, Id: 2, Description: "Act2ItemId2"},
|
||||
{Act: 2, Type: ObjectTypeItem, Id: 3, Description: "Act2ItemId3"},
|
||||
{Act: 1, Type: d2enum.ObjectTypeCharacter, Id: 0, Description: "Act1CharId0"},
|
||||
{Act: 1, Type: d2enum.ObjectTypeCharacter, Id: 1, Description: "Act1CharId1"},
|
||||
{Act: 1, Type: d2enum.ObjectTypeCharacter, Id: 2, Description: "Act1CharId2"},
|
||||
{Act: 1, Type: d2enum.ObjectTypeCharacter, Id: 3, Description: "Act1CharId3"},
|
||||
{Act: 1, Type: d2enum.ObjectTypeItem, Id: 0, Description: "Act1ItemId0"},
|
||||
{Act: 1, Type: d2enum.ObjectTypeItem, Id: 1, Description: "Act1ItemId1"},
|
||||
{Act: 1, Type: d2enum.ObjectTypeItem, Id: 2, Description: "Act1ItemId2"},
|
||||
{Act: 1, Type: d2enum.ObjectTypeItem, Id: 3, Description: "Act1ItemId3"},
|
||||
{Act: 2, Type: d2enum.ObjectTypeCharacter, Id: 0, Description: "Act2CharId0"},
|
||||
{Act: 2, Type: d2enum.ObjectTypeCharacter, Id: 1, Description: "Act2CharId1"},
|
||||
{Act: 2, Type: d2enum.ObjectTypeCharacter, Id: 2, Description: "Act2CharId2"},
|
||||
{Act: 2, Type: d2enum.ObjectTypeCharacter, Id: 3, Description: "Act2CharId3"},
|
||||
{Act: 2, Type: d2enum.ObjectTypeItem, Id: 0, Description: "Act2ItemId0"},
|
||||
{Act: 2, Type: d2enum.ObjectTypeItem, Id: 1, Description: "Act2ItemId1"},
|
||||
{Act: 2, Type: d2enum.ObjectTypeItem, Id: 2, Description: "Act2ItemId2"},
|
||||
{Act: 2, Type: d2enum.ObjectTypeItem, Id: 3, Description: "Act2ItemId3"},
|
||||
}
|
||||
|
||||
indexedTestObjects := indexObjects(testObjects)
|
||||
|
||||
typeCharacter := int(ObjectTypeCharacter)
|
||||
typeItem := int(ObjectTypeItem)
|
||||
typeCharacter := int(d2enum.ObjectTypeCharacter)
|
||||
typeItem := int(d2enum.ObjectTypeItem)
|
||||
|
||||
assert.Equal("Act1CharId2", lookupObject(1, typeCharacter, 2, indexedTestObjects).Description)
|
||||
assert.Equal("Act1ItemId0", lookupObject(1, typeItem, 0, indexedTestObjects).Description)
|
||||
|
@ -7,15 +7,17 @@ import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
)
|
||||
|
||||
// ObjectTypeRecord is a representation of a row from objtype.txt
|
||||
type ObjectTypeRecord struct {
|
||||
Name string
|
||||
Token string
|
||||
}
|
||||
|
||||
//nolint:gochecknoglobals // Currently global by design, only written once
|
||||
// ObjectTypes contains the name and token for objects
|
||||
//nolint:gochecknoglobals // Currently global by design, only written once
|
||||
var ObjectTypes []ObjectTypeRecord
|
||||
|
||||
// LoadObjectTypes loads ObjectTypeRecords from objtype.txt
|
||||
func LoadObjectTypes(objectTypeData []byte) {
|
||||
streamReader := d2common.CreateStreamReader(objectTypeData)
|
||||
count := streamReader.GetInt32()
|
||||
|
@ -9,14 +9,21 @@ import (
|
||||
|
||||
// An ObjectRecord represents the settings for one type of object from objects.txt
|
||||
type ObjectRecord struct {
|
||||
FrameCount [8]int // how many frames does this mode have, 0 = skip
|
||||
FrameDelta [8]int // what rate is the animation played at (256 = 100% speed)
|
||||
LightDiameter [8]int
|
||||
|
||||
StartFrame [8]int
|
||||
|
||||
OrderFlag [8]int // 0 = object, 1 = floor, 2 = wall
|
||||
Parm [8]int // unknown
|
||||
Name string
|
||||
Description string
|
||||
Id int
|
||||
Token string // refers to what graphics this object uses
|
||||
|
||||
SpawnMax int // unused?
|
||||
Selectable [8]bool // is this mode selectable
|
||||
TrapProbability int // unused
|
||||
Id int //nolint:golint it's ok that it's called Id
|
||||
SpawnMax int // unused?
|
||||
TrapProbability int // unused
|
||||
|
||||
SizeX int
|
||||
SizeY int
|
||||
@ -26,42 +33,11 @@ type ObjectRecord struct {
|
||||
NTgtBX int // unknown
|
||||
NTgtBY int // unknown
|
||||
|
||||
FrameCount [8]int // how many frames does this mode have, 0 = skip
|
||||
FrameDelta [8]int // what rate is the animation played at (256 = 100% speed)
|
||||
CycleAnimation [8]bool // probably whether animation loops
|
||||
LightDiameter [8]int
|
||||
BlocksLight [8]bool
|
||||
HasCollision [8]bool
|
||||
IsAttackable bool // do we kick it when interacting
|
||||
StartFrame [8]int
|
||||
|
||||
EnvEffect bool // unknown
|
||||
IsDoor bool
|
||||
BlockVisibility bool // only works with IsDoor
|
||||
Orientation int // unknown (1=sw, 2=nw, 3=se, 4=ne)
|
||||
Trans int // controls palette mapping
|
||||
|
||||
OrderFlag [8]int // 0 = object, 1 = floor, 2 = wall
|
||||
PreOperate bool // unknown
|
||||
HasAnimationMode [8]bool // 'Mode' in source, true if this mode is used
|
||||
Orientation int // unknown (1=sw, 2=nw, 3=se, 4=ne)
|
||||
Trans int // controls palette mapping
|
||||
|
||||
XOffset int // in pixels offset
|
||||
YOffset int
|
||||
Draw bool // if false, object isn't drawn (shadow is still drawn and player can still select though)
|
||||
|
||||
LightRed byte // if lightdiameter is set, rgb of the light
|
||||
LightGreen byte
|
||||
LightBlue byte
|
||||
|
||||
SelHD bool // whether these DCC components are selectable
|
||||
SelTR bool
|
||||
SelLG bool
|
||||
SelRA bool
|
||||
SelLA bool
|
||||
SelRH bool
|
||||
SelLH bool
|
||||
SelSH bool
|
||||
SelS [8]bool
|
||||
|
||||
TotalPieces int // selectable DCC components count
|
||||
SubClass int // subclass of object:
|
||||
@ -79,21 +55,12 @@ type ObjectRecord struct {
|
||||
|
||||
NameOffset int // pixels to offset the name from the animation pivot
|
||||
|
||||
MonsterOk bool // unknown
|
||||
OperateRange int // distance object can be used from, might be unused
|
||||
ShrineFunction int // unused
|
||||
Restore bool // if true, object is stored in memory and will be retained if you leave and re-enter the area
|
||||
OperateRange int // distance object can be used from, might be unused
|
||||
ShrineFunction int // unused
|
||||
|
||||
Parm [8]int // unknown
|
||||
Act int // what acts this object can appear in (15 = all three)
|
||||
Lockable bool
|
||||
Gore bool // unknown, something with corpses
|
||||
Sync bool // unknown
|
||||
Flicker bool // light flickers if true
|
||||
Damage int // amount of damage done by this (used depending on operatefn)
|
||||
Beta bool // if true, appeared in the beta?
|
||||
Overlay bool // unknown
|
||||
CollisionSubst bool // unknown, controls some kind of special collision checking?
|
||||
Act int // what acts this object can appear in (15 = all three)
|
||||
|
||||
Damage int // amount of damage done by this (used depending on operatefn)
|
||||
|
||||
Left int // unknown, clickable bounding box?
|
||||
Top int
|
||||
@ -110,13 +77,47 @@ type ObjectRecord struct {
|
||||
ClientFn int // controls special audio-visual functions
|
||||
// (see above todo)
|
||||
|
||||
RestoreVirgins bool // if true, only restores unused objects (see Restore)
|
||||
BlockMissile bool // if true, missiles collide with this
|
||||
DrawUnder bool // if true, drawn as a floor tile is
|
||||
OpenWarp bool // needs clarification, controls whether highlighting shows
|
||||
// 'To ...' or 'trap door' when highlighting, not sure which is T/F
|
||||
AutoMap int // controls how this object appears on the map
|
||||
// 0 = it doesn't, rest of modes need to be analyzed
|
||||
|
||||
CycleAnimation [8]bool // probably whether animation loops
|
||||
Selectable [8]bool // is this mode selectable
|
||||
BlocksLight [8]bool
|
||||
HasCollision [8]bool
|
||||
HasAnimationMode [8]bool // 'Mode' in source, true if this mode is used
|
||||
SelS [8]bool
|
||||
IsAttackable bool // do we kick it when interacting
|
||||
EnvEffect bool // unknown
|
||||
IsDoor bool
|
||||
BlockVisibility bool // only works with IsDoor
|
||||
PreOperate bool // unknown
|
||||
Draw bool // if false, object isn't drawn (shadow is still drawn and player can still select though)
|
||||
SelHD bool // whether these DCC components are selectable
|
||||
SelTR bool
|
||||
SelLG bool
|
||||
SelRA bool
|
||||
SelLA bool
|
||||
SelRH bool
|
||||
SelLH bool
|
||||
SelSH bool
|
||||
MonsterOk bool // unknown
|
||||
Restore bool // if true, object is stored in memory and will be retained if you leave and re-enter the area
|
||||
Lockable bool
|
||||
Gore bool // unknown, something with corpses
|
||||
Sync bool // unknown
|
||||
Flicker bool // light flickers if true
|
||||
Beta bool // if true, appeared in the beta?
|
||||
Overlay bool // unknown
|
||||
CollisionSubst bool // unknown, controls some kind of special collision checking?
|
||||
RestoreVirgins bool // if true, only restores unused objects (see Restore)
|
||||
BlockMissile bool // if true, missiles collide with this
|
||||
DrawUnder bool // if true, drawn as a floor tile is
|
||||
OpenWarp bool // needs clarification, controls whether highlighting shows
|
||||
|
||||
LightRed byte // if lightdiameter is set, rgb of the light
|
||||
LightGreen byte
|
||||
LightBlue byte
|
||||
}
|
||||
|
||||
//nolint:funlen // Makes no sense to split
|
||||
@ -335,9 +336,11 @@ func createObjectRecord(props []string) ObjectRecord {
|
||||
return result
|
||||
}
|
||||
|
||||
// Objects stores all of the ObjectRecords
|
||||
//nolint:gochecknoglobals // Currently global by design, only written once
|
||||
var Objects map[int]*ObjectRecord
|
||||
|
||||
// LoadObjects loads all objects from objects.txt
|
||||
func LoadObjects(file []byte) {
|
||||
Objects = make(map[int]*ObjectRecord)
|
||||
data := strings.Split(string(file), "\r\n")[1:]
|
||||
|
@ -75,9 +75,11 @@ func createSoundEntry(soundLine string) SoundEntry {
|
||||
return result
|
||||
}
|
||||
|
||||
// Sounds stores all of the SoundEntries
|
||||
//nolint:gochecknoglobals // Currently global by design, only written once
|
||||
var Sounds map[string]SoundEntry
|
||||
|
||||
// LoadSounds loads SoundEntries from sounds.txt
|
||||
func LoadSounds(file []byte) {
|
||||
Sounds = make(map[string]SoundEntry)
|
||||
soundData := strings.Split(string(file), "\r\n")[1:]
|
||||
|
@ -1,13 +1,14 @@
|
||||
package d2datadict
|
||||
|
||||
import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
"log"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
)
|
||||
|
||||
// https://d2mods.info/forum/kb/viewarticle?a=162
|
||||
|
||||
// Defines the SuperUnique monsters and their properties.
|
||||
// SuperUniqueRecord Defines the unique monsters and their properties.
|
||||
// SuperUnique monsters are boss monsters which always appear at the same places
|
||||
// and always have the same base special abilities
|
||||
// with the addition of one or two extra ones per difficulty (Nightmare provides one extra ability, Hell provides two).
|
||||
@ -118,8 +119,11 @@ type SuperUniqueRecord struct {
|
||||
UTransHell string
|
||||
}
|
||||
|
||||
// SuperUniques stores all of the SuperUniqueRecords
|
||||
//nolint:gochecknoglobals // Currently global by design
|
||||
var SuperUniques map[string]*SuperUniqueRecord
|
||||
|
||||
// LoadSuperUniques loads SuperUniqueRecords from superuniques.txt
|
||||
func LoadSuperUniques(file []byte) {
|
||||
dictionary := d2common.LoadDataDictionary(string(file))
|
||||
SuperUniques = make(map[string]*SuperUniqueRecord, len(dictionary.Data))
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
)
|
||||
|
||||
// UniqueItemRecord is a representation of a row from uniqueitems.txt
|
||||
type UniqueItemRecord struct {
|
||||
Name string
|
||||
Version int // 0 = classic pre 1.07, 1 = 1.07-1.11, 100 = expansion
|
||||
@ -41,6 +42,7 @@ type UniqueItemRecord struct {
|
||||
Properties [12]UniqueItemProperty
|
||||
}
|
||||
|
||||
// UniqueItemProperty is describes a property of a unique item
|
||||
type UniqueItemProperty struct {
|
||||
Property string
|
||||
Parameter d2common.CalcString // depending on the property, this may be an int (usually), or a string
|
||||
@ -114,8 +116,11 @@ func createUniqueItemProperty(r *[]string, inc func() int) UniqueItemProperty {
|
||||
return result
|
||||
}
|
||||
|
||||
// UniqueItems stores all of the UniqueItemRecords
|
||||
//nolint:gochecknoglobals // Currently global by design
|
||||
var UniqueItems map[string]*UniqueItemRecord
|
||||
|
||||
// LoadUniqueItems loadsUniqueItemRecords fro uniqueitems.txt
|
||||
func LoadUniqueItems(file []byte) {
|
||||
UniqueItems = make(map[string]*UniqueItemRecord)
|
||||
data := strings.Split(string(file), "\r\n")[1:]
|
||||
|
@ -9,6 +9,6 @@ import (
|
||||
var Weapons map[string]*ItemCommonRecord
|
||||
|
||||
func LoadWeapons(file []byte) {
|
||||
Weapons = *LoadCommonItems(file, d2enum.InventoryItemTypeWeapon)
|
||||
Weapons = LoadCommonItems(file, d2enum.InventoryItemTypeWeapon)
|
||||
log.Printf("Loaded %d weapons", len(Weapons))
|
||||
}
|
||||
|
@ -5,9 +5,10 @@ import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
|
||||
)
|
||||
|
||||
// Object is a game world object
|
||||
type Object struct {
|
||||
Type int
|
||||
Id int
|
||||
Id int //nolint:golint Id is the right key
|
||||
X int
|
||||
Y int
|
||||
Flags int
|
||||
|
7
d2common/d2enum/encoding_type.go
Normal file
7
d2common/d2enum/encoding_type.go
Normal file
@ -0,0 +1,7 @@
|
||||
package d2enum
|
||||
|
||||
type EncodingType int
|
||||
|
||||
const (
|
||||
EncodeDefault EncodingType = iota
|
||||
)
|
9
d2common/d2enum/object_type.go
Normal file
9
d2common/d2enum/object_type.go
Normal file
@ -0,0 +1,9 @@
|
||||
package d2enum
|
||||
|
||||
// ObjectType is the type of an object
|
||||
type ObjectType int
|
||||
|
||||
const (
|
||||
ObjectTypeCharacter ObjectType = iota + 1
|
||||
ObjectTypeItem
|
||||
)
|
76
d2common/d2enum/operator_type.go
Normal file
76
d2common/d2enum/operator_type.go
Normal file
@ -0,0 +1,76 @@
|
||||
package d2enum
|
||||
|
||||
// OperatorType is used for calculating dynamic property values
|
||||
type OperatorType int // for dynamic properties
|
||||
|
||||
const (
|
||||
// OpDefault just adds the stat to the unit directly
|
||||
OpDefault = OperatorType(iota)
|
||||
|
||||
// Op1 adds opstat.base * statvalue / 100 to the opstat.
|
||||
Op1
|
||||
|
||||
// Op2 adds (statvalue * basevalue) / (2 ^ param) to the opstat
|
||||
// this does not work properly with any stat other then level because of the
|
||||
// way this is updated, it is only refreshed when you re-equip the item,
|
||||
// your character is saved or you level up, similar to passive skills, just
|
||||
// because it looks like it works in the item description
|
||||
// does not mean it does, the game just recalculates the information in the
|
||||
// description every frame, while the values remain unchanged serverside.
|
||||
Op2
|
||||
|
||||
// Op3 is a percentage based version of op #2
|
||||
// look at op #2 for information about the formula behind it, just
|
||||
// remember the stat is increased by a percentage rather then by adding
|
||||
// an integer.
|
||||
Op3
|
||||
|
||||
// Op4 works the same way op #2 works, however the stat bonus is
|
||||
// added to the item and not to the player (so that +defense per level
|
||||
// properly adds the defense to the armor and not to the character
|
||||
// directly!)
|
||||
Op4
|
||||
|
||||
// Op5 works like op #4 but is percentage based, it is used for percentage
|
||||
// based increase of stats that are found on the item itself, and not stats
|
||||
// that are found on the character.
|
||||
Op5
|
||||
|
||||
// Op6 works like for op #7, however this adds a plain bonus to the stat, and just
|
||||
// like #7 it also doesn't work so I won't bother to explain the arithmetic
|
||||
// behind it either.
|
||||
Op6
|
||||
|
||||
// Op7 is used to increase a stat based on the current daytime of the game
|
||||
// world by a percentage, there is no need to explain the arithmetics
|
||||
// behind it because frankly enough it just doesn't work serverside, it
|
||||
// only updates clientside so this op is essentially useless.
|
||||
Op7
|
||||
|
||||
// Op8 hardcoded to work only with maxmana, this will apply the proper amount
|
||||
// of mana to your character based on CharStats.txt for the amount of energy
|
||||
// the stat added (doesn't work for non characters)
|
||||
Op8
|
||||
|
||||
// Op9 hardcoded to work only with maxhp and maxstamina, this will apply the
|
||||
// proper amount of maxhp and maxstamina to your character based on
|
||||
// CharStats.txt for the amount of vitality the stat added (doesn't work
|
||||
// for non characters)
|
||||
Op9
|
||||
|
||||
// Op10 doesn't do anything, this has no switch case in the op function.
|
||||
Op10
|
||||
|
||||
// Op11 adds opstat.base * statvalue / 100 similar to 1 and 13, the code just
|
||||
// does a few more checks
|
||||
Op11
|
||||
|
||||
// Op12 doesn't do anything, this has no switch case in the op function.
|
||||
Op12
|
||||
|
||||
// Op13 adds opstat.base * statvalue / 100 to the value of opstat, this is
|
||||
// useable only on items it will not apply the bonus to other unit types
|
||||
// (this is why it is used for +% durability, +% level requirement,
|
||||
// +% damage, +% defense ).
|
||||
Op13
|
||||
)
|
@ -118,13 +118,13 @@ func (mr *Stamp) Entities(tileOffsetX, tileOffsetY int) []d2mapentity.MapEntity
|
||||
for _, object := range mr.ds1.Objects {
|
||||
|
||||
switch object.Lookup.Type {
|
||||
case d2datadict.ObjectTypeCharacter:
|
||||
case d2enum.ObjectTypeCharacter:
|
||||
if object.Lookup.Base != "" && object.Lookup.Token != "" && object.Lookup.TR != "" {
|
||||
npc := d2mapentity.CreateNPC((tileOffsetX*5)+object.X, (tileOffsetY*5)+object.Y, object.Lookup, 0)
|
||||
npc.SetPaths(convertPaths(tileOffsetX, tileOffsetY, object.Paths))
|
||||
entities = append(entities, npc)
|
||||
}
|
||||
case d2datadict.ObjectTypeItem:
|
||||
case d2enum.ObjectTypeItem:
|
||||
if object.ObjectInfo != nil && object.ObjectInfo.Draw && object.Lookup.Base != "" && object.Lookup.Token != "" {
|
||||
entity, err := d2mapentity.CreateObject((tileOffsetX*5)+object.X, (tileOffsetY*5)+object.Y, object.Lookup, d2resource.PaletteUnits)
|
||||
if err != nil {
|
||||
|
Loading…
x
Reference in New Issue
Block a user