mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2024-09-27 05:35:57 -04: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
|
// 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
|
// LoadAnimationData loads the animation data table into the global AnimationData dictionary
|
||||||
func LoadAnimationData(rawData []byte) {
|
func LoadAnimationData(rawData []byte) {
|
||||||
AnimationData = make(map[string][]*AnimationDataRecord)
|
AnimationData = make(map[string][]*AnimationDataRecord)
|
||||||
streamReader := d2common.CreateStreamReader(rawData)
|
streamReader := d2common.CreateStreamReader(rawData)
|
||||||
|
|
||||||
for !streamReader.Eof() {
|
for !streamReader.Eof() {
|
||||||
dataCount := int(streamReader.GetInt32())
|
dataCount := int(streamReader.GetInt32())
|
||||||
for i := 0; i < dataCount; i++ {
|
for i := 0; i < dataCount; i++ {
|
||||||
@ -37,11 +38,14 @@ func LoadAnimationData(rawData []byte) {
|
|||||||
}
|
}
|
||||||
data.Flags = streamReader.ReadBytes(144)
|
data.Flags = streamReader.ReadBytes(144)
|
||||||
cofIndex := strings.ToLower(data.COFName)
|
cofIndex := strings.ToLower(data.COFName)
|
||||||
|
|
||||||
if _, found := AnimationData[cofIndex]; !found {
|
if _, found := AnimationData[cofIndex]; !found {
|
||||||
AnimationData[cofIndex] = make([]*AnimationDataRecord, 0)
|
AnimationData[cofIndex] = make([]*AnimationDataRecord, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimationData[cofIndex] = append(AnimationData[cofIndex], data)
|
AnimationData[cofIndex] = append(AnimationData[cofIndex], data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Loaded %d animation data records", len(AnimationData))
|
log.Printf("Loaded %d animation data records", len(AnimationData))
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
//
|
// Package d2compression is used for decompressing WAV files.
|
||||||
// MpqHuffman.go based on the origina CS file
|
package d2compression
|
||||||
|
|
||||||
|
// MpqHuffman.go based on the original CS file
|
||||||
//
|
//
|
||||||
// Authors:
|
// Authors:
|
||||||
// Foole (fooleau@gmail.com)
|
// Foole (fooleau@gmail.com)
|
||||||
@ -27,7 +29,6 @@
|
|||||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
//
|
//
|
||||||
package d2compression
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
@ -37,57 +38,60 @@ import (
|
|||||||
|
|
||||||
// linkedNode is a node which is both hierachcical (parent/child) and doubly linked (next/prev)
|
// linkedNode is a node which is both hierachcical (parent/child) and doubly linked (next/prev)
|
||||||
type linkedNode struct {
|
type linkedNode struct {
|
||||||
DecompressedValue int
|
decompressedValue int
|
||||||
Weight int
|
weight int
|
||||||
Parent *linkedNode
|
parent *linkedNode
|
||||||
Child0 *linkedNode
|
child0 *linkedNode
|
||||||
Prev *linkedNode
|
prev *linkedNode
|
||||||
Next *linkedNode
|
next *linkedNode
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateLinkedNode(decompVal, weight int) *linkedNode {
|
// createLinkedNode creates a linked node
|
||||||
|
func createLinkedNode(decompVal, weight int) *linkedNode {
|
||||||
result := &linkedNode{
|
result := &linkedNode{
|
||||||
DecompressedValue: decompVal,
|
decompressedValue: decompVal,
|
||||||
Weight: weight,
|
weight: weight,
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *linkedNode) GetChild1() *linkedNode {
|
func (v *linkedNode) getChild1() *linkedNode {
|
||||||
return v.Child0.Prev
|
return v.child0.prev
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *linkedNode) Insert(other *linkedNode) *linkedNode {
|
func (v *linkedNode) insert(other *linkedNode) *linkedNode {
|
||||||
// 'Next' should have a lower weight we should return the lower weight
|
// 'next' should have a lower weight we should return the lower weight
|
||||||
if other.Weight <= v.Weight {
|
if other.weight <= v.weight {
|
||||||
// insert before
|
// insert before
|
||||||
if v.Next != nil {
|
if v.next != nil {
|
||||||
v.Next.Prev = other
|
v.next.prev = other
|
||||||
other.Next = v.Next
|
other.next = v.next
|
||||||
}
|
}
|
||||||
|
|
||||||
v.Next = other
|
v.next = other
|
||||||
|
|
||||||
other.Prev = v
|
other.prev = v
|
||||||
|
|
||||||
return other
|
return other
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.Prev == nil {
|
if v.prev == nil {
|
||||||
// Insert after
|
// insert after
|
||||||
other.Prev = nil
|
other.prev = nil
|
||||||
v.Prev = other
|
v.prev = other
|
||||||
other.Next = v
|
other.next = v
|
||||||
} else {
|
} else {
|
||||||
v.Prev.Insert(other)
|
v.prev.insert(other)
|
||||||
}
|
}
|
||||||
|
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//nolint:funlen // Makes no sense to split
|
||||||
func getPrimes() [][]byte {
|
func getPrimes() [][]byte {
|
||||||
return [][]byte{
|
return [][]byte{
|
||||||
{
|
{ //nolint:dupl we're not interested in duplicates here
|
||||||
// Compression type 0
|
// Compression type 0
|
||||||
0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
@ -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, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
|
||||||
},
|
},
|
||||||
{
|
{ //nolint:dupl we're not interested in duplicates here
|
||||||
// Compression type 1
|
// Compression type 1
|
||||||
0x54, 0x16, 0x16, 0x0D, 0x0C, 0x08, 0x06, 0x05, 0x06, 0x05, 0x06, 0x03, 0x04, 0x04, 0x03, 0x05,
|
0x54, 0x16, 0x16, 0x0D, 0x0C, 0x08, 0x06, 0x05, 0x06, 0x05, 0x06, 0x03, 0x04, 0x04, 0x03, 0x05,
|
||||||
0x0E, 0x0B, 0x14, 0x13, 0x13, 0x09, 0x0B, 0x06, 0x05, 0x04, 0x03, 0x02, 0x03, 0x02, 0x02, 0x02,
|
0x0E, 0x0B, 0x14, 0x13, 0x13, 0x09, 0x0B, 0x06, 0x05, 0x04, 0x03, 0x02, 0x03, 0x02, 0x02, 0x02,
|
||||||
@ -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, 0x01, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||||
0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x06, 0x4B,
|
0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x06, 0x4B,
|
||||||
}, {
|
}, {
|
||||||
// Compression type 2
|
// Compression type 2 //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, 0x03, 0x27, 0x00, 0x00, 0x23, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0xFF, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x06, 0x0E, 0x10, 0x04,
|
0xFF, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x06, 0x0E, 0x10, 0x04,
|
||||||
@ -134,8 +138,8 @@ func getPrimes() [][]byte {
|
|||||||
0x03, 0x01, 0x03, 0x06, 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x02, 0x01, 0x01,
|
0x03, 0x01, 0x03, 0x06, 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x02, 0x01, 0x01,
|
||||||
0x01, 0x29, 0x07, 0x16, 0x12, 0x40, 0x0A, 0x0A, 0x11, 0x25, 0x01, 0x03, 0x17, 0x10, 0x26, 0x2A,
|
0x01, 0x29, 0x07, 0x16, 0x12, 0x40, 0x0A, 0x0A, 0x11, 0x25, 0x01, 0x03, 0x17, 0x10, 0x26, 0x2A,
|
||||||
0x10, 0x01, 0x23, 0x23, 0x2F, 0x10, 0x06, 0x07, 0x02, 0x09, 0x01, 0x01, 0x01, 0x01, 0x01,
|
0x10, 0x01, 0x23, 0x23, 0x2F, 0x10, 0x06, 0x07, 0x02, 0x09, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||||
}, {
|
}, { //nolint:dupl we're not interested in duplicates here
|
||||||
// Compression type 3
|
// Compression type 3 //nolint:dupl
|
||||||
0xFF, 0x0B, 0x07, 0x05, 0x0B, 0x02, 0x02, 0x02, 0x06, 0x02, 0x02, 0x01, 0x04, 0x02, 0x01, 0x03,
|
0xFF, 0x0B, 0x07, 0x05, 0x0B, 0x02, 0x02, 0x02, 0x06, 0x02, 0x02, 0x01, 0x04, 0x02, 0x01, 0x03,
|
||||||
0x09, 0x01, 0x01, 0x01, 0x03, 0x04, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01,
|
0x09, 0x01, 0x01, 0x01, 0x03, 0x04, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01,
|
||||||
0x05, 0x01, 0x01, 0x01, 0x0D, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
0x05, 0x01, 0x01, 0x01, 0x0D, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||||
@ -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, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x07, 0x01, 0x01, 0x02, 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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
||||||
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,
|
||||||
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,
|
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,
|
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,
|
||||||
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 {
|
func decode(input *d2common.BitStream, head *linkedNode) *linkedNode {
|
||||||
node := head
|
node := head
|
||||||
|
|
||||||
for node.Child0 != nil {
|
for node.child0 != nil {
|
||||||
bit := input.ReadBits(1)
|
bit := input.ReadBits(1)
|
||||||
if bit == -1 {
|
if bit == -1 {
|
||||||
log.Fatal("unexpected end of file")
|
log.Fatal("unexpected end of file")
|
||||||
}
|
}
|
||||||
|
|
||||||
if bit == 0 {
|
if bit == 0 {
|
||||||
node = node.Child0
|
node = node.child0
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
node = node.GetChild1()
|
node = node.getChild1()
|
||||||
}
|
}
|
||||||
|
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: these consts for buildList need better names
|
||||||
|
const (
|
||||||
|
decompVal1 = 256
|
||||||
|
decompVal2 = 257
|
||||||
|
)
|
||||||
|
|
||||||
func buildList(primeData []byte) *linkedNode {
|
func buildList(primeData []byte) *linkedNode {
|
||||||
root := CreateLinkedNode(256, 1)
|
root := createLinkedNode(decompVal1, 1)
|
||||||
root = root.Insert(CreateLinkedNode(257, 1))
|
root = root.insert(createLinkedNode(decompVal2, 1))
|
||||||
|
|
||||||
for i := 0; i < len(primeData); i++ {
|
for i := 0; i < len(primeData); i++ {
|
||||||
if primeData[i] != 0 {
|
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 {
|
func insertNode(tail *linkedNode, decomp int) *linkedNode {
|
||||||
parent := tail
|
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 := createLinkedNode(parent.decompressedValue, parent.weight)
|
||||||
temp.Parent = parent
|
temp.parent = parent
|
||||||
|
|
||||||
newnode := CreateLinkedNode(decomp, 0)
|
newnode := createLinkedNode(decomp, 0)
|
||||||
newnode.Parent = parent
|
newnode.parent = parent
|
||||||
|
|
||||||
parent.Child0 = newnode
|
parent.child0 = newnode
|
||||||
|
|
||||||
tail.Next = temp
|
tail.next = temp
|
||||||
temp.Prev = tail
|
temp.prev = tail
|
||||||
newnode.Prev = temp
|
newnode.prev = temp
|
||||||
temp.Next = newnode
|
temp.next = newnode
|
||||||
|
|
||||||
adjustTree(newnode)
|
adjustTree(newnode)
|
||||||
|
|
||||||
@ -257,7 +269,7 @@ func adjustTree(newNode *linkedNode) {
|
|||||||
current := newNode
|
current := newNode
|
||||||
|
|
||||||
for current != nil {
|
for current != nil {
|
||||||
current.Weight++
|
current.weight++
|
||||||
|
|
||||||
var insertpoint *linkedNode
|
var insertpoint *linkedNode
|
||||||
|
|
||||||
@ -267,12 +279,12 @@ func adjustTree(newNode *linkedNode) {
|
|||||||
insertpoint = current
|
insertpoint = current
|
||||||
|
|
||||||
for {
|
for {
|
||||||
prev = insertpoint.Prev
|
prev = insertpoint.prev
|
||||||
if prev == nil {
|
if prev == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if prev.Weight >= current.Weight {
|
if prev.weight >= current.weight {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,60 +293,60 @@ func adjustTree(newNode *linkedNode) {
|
|||||||
|
|
||||||
// No insertion point found
|
// No insertion point found
|
||||||
if insertpoint == current {
|
if insertpoint == current {
|
||||||
current = current.Parent
|
current = current.parent
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// The following code basically swaps insertpoint with current
|
// The following code basically swaps insertpoint with current
|
||||||
|
|
||||||
// remove insert point
|
// remove insert point
|
||||||
if insertpoint.Prev != nil {
|
if insertpoint.prev != nil {
|
||||||
insertpoint.Prev.Next = insertpoint.Next
|
insertpoint.prev.next = insertpoint.next
|
||||||
}
|
}
|
||||||
|
|
||||||
insertpoint.Next.Prev = insertpoint.Prev
|
insertpoint.next.prev = insertpoint.prev
|
||||||
|
|
||||||
// Insert insertpoint after current
|
// insert insertpoint after current
|
||||||
insertpoint.Next = current.Next
|
insertpoint.next = current.next
|
||||||
insertpoint.Prev = current
|
insertpoint.prev = current
|
||||||
|
|
||||||
if current.Next != nil {
|
if current.next != nil {
|
||||||
current.Next.Prev = insertpoint
|
current.next.prev = insertpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
current.Next = insertpoint
|
current.next = insertpoint
|
||||||
|
|
||||||
// remove current
|
// remove current
|
||||||
current.Prev.Next = current.Next
|
current.prev.next = current.next
|
||||||
current.Next.Prev = current.Prev
|
current.next.prev = current.prev
|
||||||
|
|
||||||
// insert current after prev
|
// insert current after prev
|
||||||
if prev == nil {
|
if prev == nil {
|
||||||
log.Fatal("previous frame not defined!")
|
log.Fatal("previous frame not defined!")
|
||||||
}
|
}
|
||||||
|
|
||||||
temp := prev.Next
|
temp := prev.next
|
||||||
current.Next = temp
|
current.next = temp
|
||||||
current.Prev = prev
|
current.prev = prev
|
||||||
temp.Prev = current
|
temp.prev = current
|
||||||
prev.Next = current
|
prev.next = current
|
||||||
|
|
||||||
// Set up parent/child links
|
// Set up parent/child links
|
||||||
currentparent := current.Parent
|
currentparent := current.parent
|
||||||
insertparent := insertpoint.Parent
|
insertparent := insertpoint.parent
|
||||||
|
|
||||||
if currentparent.Child0 == current {
|
if currentparent.child0 == current {
|
||||||
currentparent.Child0 = insertpoint
|
currentparent.child0 = insertpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
if currentparent != insertparent && insertparent.Child0 == insertpoint {
|
if currentparent != insertparent && insertparent.child0 == insertpoint {
|
||||||
insertparent.Child0 = current
|
insertparent.child0 = current
|
||||||
}
|
}
|
||||||
|
|
||||||
current.Parent = insertparent
|
current.parent = insertparent
|
||||||
insertpoint.Parent = currentparent
|
insertpoint.parent = currentparent
|
||||||
|
|
||||||
current = current.Parent
|
current = current.parent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -343,24 +355,25 @@ func buildTree(tail *linkedNode) *linkedNode {
|
|||||||
|
|
||||||
for current != nil {
|
for current != nil {
|
||||||
child0 := current
|
child0 := current
|
||||||
child1 := current.Prev
|
child1 := current.prev
|
||||||
|
|
||||||
if child1 == nil {
|
if child1 == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
parent := CreateLinkedNode(0, child0.Weight+child1.Weight)
|
parent := createLinkedNode(0, child0.weight+child1.weight)
|
||||||
parent.Child0 = child0
|
parent.child0 = child0
|
||||||
child0.Parent = parent
|
child0.parent = parent
|
||||||
child1.Parent = parent
|
child1.parent = parent
|
||||||
|
|
||||||
current.Insert(parent)
|
current.insert(parent)
|
||||||
current = current.Prev.Prev
|
current = current.prev.prev
|
||||||
}
|
}
|
||||||
|
|
||||||
return current
|
return current
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HuffmanDecompress decompresses huffman-compressed data
|
||||||
func HuffmanDecompress(data []byte) []byte {
|
func HuffmanDecompress(data []byte) []byte {
|
||||||
comptype := data[0]
|
comptype := data[0]
|
||||||
primes := getPrimes()
|
primes := getPrimes()
|
||||||
@ -380,7 +393,7 @@ func HuffmanDecompress(data []byte) []byte {
|
|||||||
Loop:
|
Loop:
|
||||||
for {
|
for {
|
||||||
node := decode(bitstream, head)
|
node := decode(bitstream, head)
|
||||||
decoded = node.DecompressedValue
|
decoded = node.decompressedValue
|
||||||
switch decoded {
|
switch decoded {
|
||||||
case 256:
|
case 256:
|
||||||
break Loop
|
break Loop
|
||||||
|
@ -4,7 +4,12 @@ import (
|
|||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||||
)
|
)
|
||||||
|
|
||||||
var sLookup = []int{
|
// 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,
|
0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E,
|
||||||
0x0010, 0x0011, 0x0013, 0x0015, 0x0017, 0x0019, 0x001C, 0x001F,
|
0x0010, 0x0011, 0x0013, 0x0015, 0x0017, 0x0019, 0x001C, 0x001F,
|
||||||
0x0022, 0x0025, 0x0029, 0x002D, 0x0032, 0x0037, 0x003C, 0x0042,
|
0x0022, 0x0025, 0x0029, 0x002D, 0x0032, 0x0037, 0x003C, 0x0042,
|
||||||
@ -17,21 +22,18 @@ var sLookup = []int{
|
|||||||
0x1BDC, 0x1EA5, 0x21B6, 0x2515, 0x28CA, 0x2CDF, 0x315B, 0x364B,
|
0x1BDC, 0x1EA5, 0x21B6, 0x2515, 0x28CA, 0x2CDF, 0x315B, 0x364B,
|
||||||
0x3BB9, 0x41B2, 0x4844, 0x4F7E, 0x5771, 0x602F, 0x69CE, 0x7462,
|
0x3BB9, 0x41B2, 0x4844, 0x4F7E, 0x5771, 0x602F, 0x69CE, 0x7462,
|
||||||
0x7FFF,
|
0x7FFF,
|
||||||
}
|
}
|
||||||
|
|
||||||
var sLookup2 = []int{
|
var sLookup2 = []int{
|
||||||
-1, 0, -1, 4, -1, 2, -1, 6,
|
-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, 1, -1, 5, -1, 3, -1, 7,
|
-1, 1, -1, 5, -1, 3, -1, 7,
|
||||||
-1, 2, -1, 4, -1, 6, -1, 8,
|
-1, 2, -1, 4, -1, 6, -1, 8,
|
||||||
}
|
}
|
||||||
|
|
||||||
func WavDecompress(data []byte, channelCount int) []byte {
|
|
||||||
Array1 := []int{0x2c, 0x2c}
|
|
||||||
Array2 := make([]int, channelCount)
|
|
||||||
|
|
||||||
input := d2common.CreateStreamReader(data)
|
input := d2common.CreateStreamReader(data)
|
||||||
output := d2common.CreateStreamWriter()
|
output := d2common.CreateStreamWriter()
|
||||||
|
|
||||||
input.GetByte()
|
input.GetByte()
|
||||||
|
|
||||||
shift := input.GetByte()
|
shift := input.GetByte()
|
||||||
@ -43,6 +45,7 @@ func WavDecompress(data []byte, channelCount int) []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
channel := channelCount - 1
|
channel := channelCount - 1
|
||||||
|
|
||||||
for input.GetPosition() < input.GetSize() {
|
for input.GetPosition() < input.GetSize() {
|
||||||
value := input.GetByte()
|
value := input.GetByte()
|
||||||
|
|
||||||
@ -56,12 +59,14 @@ func WavDecompress(data []byte, channelCount int) []byte {
|
|||||||
if Array1[channel] != 0 {
|
if Array1[channel] != 0 {
|
||||||
Array1[channel]--
|
Array1[channel]--
|
||||||
}
|
}
|
||||||
|
|
||||||
output.PushInt16(int16(Array2[channel]))
|
output.PushInt16(int16(Array2[channel]))
|
||||||
case 1:
|
case 1:
|
||||||
Array1[channel] += 8
|
Array1[channel] += 8
|
||||||
if Array1[channel] > 0x58 {
|
if Array1[channel] > 0x58 {
|
||||||
Array1[channel] = 0x58
|
Array1[channel] = 0x58
|
||||||
}
|
}
|
||||||
|
|
||||||
if channelCount == 2 {
|
if channelCount == 2 {
|
||||||
channel = 1 - channel
|
channel = 1 - channel
|
||||||
}
|
}
|
||||||
@ -71,6 +76,7 @@ func WavDecompress(data []byte, channelCount int) []byte {
|
|||||||
if Array1[channel] < 0 {
|
if Array1[channel] < 0 {
|
||||||
Array1[channel] = 0
|
Array1[channel] = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if channelCount == 2 {
|
if channelCount == 2 {
|
||||||
channel = 1 - channel
|
channel = 1 - channel
|
||||||
}
|
}
|
||||||
@ -116,12 +122,11 @@ func WavDecompress(data []byte, channelCount int) []byte {
|
|||||||
|
|
||||||
if Array1[channel] < 0 {
|
if Array1[channel] < 0 {
|
||||||
Array1[channel] = 0
|
Array1[channel] = 0
|
||||||
} else {
|
} else if Array1[channel] > 0x58 {
|
||||||
if Array1[channel] > 0x58 {
|
|
||||||
Array1[channel] = 0x58
|
Array1[channel] = 0x58
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return output.GetBytes()
|
return output.GetBytes()
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,11 @@ import (
|
|||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//nolint:gochecknoglobals // Currently global by design, only written once
|
||||||
var Armors map[string]*ItemCommonRecord
|
var Armors map[string]*ItemCommonRecord
|
||||||
|
|
||||||
|
// LoadArmors loads entries from armor.txt as ItemCommonRecords
|
||||||
func LoadArmors(file []byte) {
|
func LoadArmors(file []byte) {
|
||||||
Armors = *LoadCommonItems(file, d2enum.InventoryItemTypeArmor)
|
Armors = LoadCommonItems(file, d2enum.InventoryItemTypeArmor)
|
||||||
log.Printf("Loaded %d armors", len(Armors))
|
log.Printf("Loaded %d armors", len(Armors))
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,11 @@ import (
|
|||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
"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"}
|
var frameFields = []string{"Cel1", "Cel2", "Cel3", "Cel4"}
|
||||||
|
|
||||||
// AutoMapRecord represents one row from d2data.mpq/AutoMap.txt.
|
// AutoMapRecord represents one row from d2data.mpq/AutoMap.txt.
|
||||||
@ -27,7 +32,6 @@ type AutoMapRecord struct {
|
|||||||
// StartSequence and EndSequence are sub indices the
|
// StartSequence and EndSequence are sub indices the
|
||||||
// same 2D array as Style. They describe a range of
|
// same 2D array as Style. They describe a range of
|
||||||
// tiles for which covered by this AutoMapRecord.
|
// tiles for which covered by this AutoMapRecord.
|
||||||
//
|
|
||||||
// In some rows you can find a value of -1. This means
|
// In some rows you can find a value of -1. This means
|
||||||
// the game will only look at Style and TileName to
|
// the game will only look at Style and TileName to
|
||||||
// determine which tiles are addressed.
|
// determine which tiles are addressed.
|
||||||
@ -50,7 +54,6 @@ type AutoMapRecord struct {
|
|||||||
// re-extract the chart with Dc6Table, you can specify
|
// re-extract the chart with Dc6Table, you can specify
|
||||||
// how many graphics a line can hold), line 1 includes
|
// how many graphics a line can hold), line 1 includes
|
||||||
// icons 0-19, line 2 from 20 to 39 etc.
|
// icons 0-19, line 2 from 20 to 39 etc.
|
||||||
//
|
|
||||||
// Multiple values exist for Cel (and Type) to enable
|
// Multiple values exist for Cel (and Type) to enable
|
||||||
// variation. Presumably game chooses randomly between
|
// variation. Presumably game chooses randomly between
|
||||||
// any of the 4 values which are not set to -1.
|
// 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))
|
AutoMaps = make([]*AutoMapRecord, len(d.Data))
|
||||||
|
|
||||||
for idx := range d.Data {
|
for idx := range d.Data {
|
||||||
if d.GetString("LevelName", idx) == "Expansion" {
|
if d.GetString("LevelName", idx) == expansion {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,60 +7,65 @@ import (
|
|||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Charecter stats
|
// CharStatsRecord is a struct that represents a single row from charstats.txt
|
||||||
type CharStatsRecord struct {
|
type CharStatsRecord struct {
|
||||||
Class d2enum.Hero
|
Class d2enum.Hero
|
||||||
|
|
||||||
// the initial stats at character level 1
|
// the initial stats at character level 1
|
||||||
InitStr int
|
InitStr int // initial strength
|
||||||
InitDex int
|
InitDex int // initial dexterity
|
||||||
InitVit int
|
InitVit int // initial vitality
|
||||||
InitEne int
|
InitEne int // initial energy
|
||||||
InitStamina int
|
InitStamina int // initial stamina
|
||||||
|
|
||||||
ManaRegen int // number of seconds to regen mana completely
|
ManaRegen int // number of seconds to regen mana completely
|
||||||
ToHitFactor int // added to basic AR of character class
|
ToHitFactor int // added to basic AR of character class
|
||||||
|
|
||||||
VelocityWalk int
|
VelocityWalk int // velocity of the character while walking
|
||||||
VelocityRun int
|
VelocityRun int // velocity of the character while running
|
||||||
StaminaRunDrain int // rate of stamina loss, lower is longer drain time
|
StaminaRunDrain int // rate of stamina loss, lower is longer drain time
|
||||||
|
|
||||||
// NOTE: Each point of Life/Mana/Stamina is divided by 256 for precision.
|
// NOTE: Each point of Life/Mana/Stamina is divided by 256 for precision.
|
||||||
LifePerLevel int // value is in fourths, lowest possible is 64/256
|
// value is in fourths, lowest possible is 64/256
|
||||||
ManaPerLevel int
|
LifePerLevel int // amount of life per character level
|
||||||
StaminaPerLevel int
|
ManaPerLevel int // amount of mana per character level
|
||||||
|
StaminaPerLevel int // amount of stamina per character level
|
||||||
|
|
||||||
LifePerVit int // life per point of vitality
|
LifePerVit int // life per point of vitality
|
||||||
ManaPerEne int // mana per point of energy
|
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)
|
BlockFactor int // added to base shield block% in armor.txt (display & calc)
|
||||||
|
|
||||||
// appears on starting weapon
|
// appears on starting weapon
|
||||||
StartSkillBonus string
|
StartSkillBonus string // a key that points to a property
|
||||||
|
|
||||||
// The skills the character class starts with (always available)
|
// 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).
|
// 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
|
SkillStrTab [3]string // string for bonus per skill tabs
|
||||||
SkillStrClassOnly string // string for class-exclusive skills
|
SkillStrClassOnly string // string for class-exclusive skills
|
||||||
|
|
||||||
BaseWeaponClass d2enum.WeaponClass // controls animation when unarmed
|
BaseWeaponClass d2enum.WeaponClass // controls animation when unarmed
|
||||||
|
|
||||||
StartItem [10]string
|
StartItem [10]string // tokens for the starting items
|
||||||
StartItemLocation [10]string
|
StartItemLocation [10]string // locations of the starting items
|
||||||
StartItemCount [10]int
|
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 CharStats map[d2enum.Hero]*CharStatsRecord
|
||||||
var charStringMap map[string]d2enum.Hero
|
var charStringMap map[string]d2enum.Hero //nolint:gochecknoglobals // Currently global by design
|
||||||
var weaponTokenMap map[string]d2enum.WeaponClass
|
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
|
//nolint:funlen // Makes no sense to split
|
||||||
|
// LoadCharStats loads charstats.txt file contents into map[d2enum.Hero]*CharStatsRecord
|
||||||
func LoadCharStats(file []byte) {
|
func LoadCharStats(file []byte) {
|
||||||
charStringMap = map[string]d2enum.Hero{
|
charStringMap = map[string]d2enum.Hero{
|
||||||
"Amazon": d2enum.HeroAmazon,
|
"Amazon": d2enum.HeroAmazon,
|
||||||
|
@ -140,22 +140,26 @@ type CubeRecipeItemProperty struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CubeRecipes contains all rows in CubeMain.txt.
|
// CubeRecipes contains all rows in CubeMain.txt.
|
||||||
|
//nolint:gochecknoglobals // Currently global by design, only written once
|
||||||
var CubeRecipes []*CubeRecipeRecord
|
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
|
// LoadCubeRecipes populates CubeRecipes with
|
||||||
// the data from CubeMain.txt.
|
// the data from CubeMain.txt.
|
||||||
func LoadCubeRecipes(file []byte) {
|
func LoadCubeRecipes(file []byte) {
|
||||||
// Load data
|
// Load data
|
||||||
d := d2common.LoadDataDictionary(string(file))
|
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
|
// Create records
|
||||||
CubeRecipes = make([]*CubeRecipeRecord, len(d.Data))
|
CubeRecipes = make([]*CubeRecipeRecord, len(d.Data))
|
||||||
for idx := range d.Data {
|
for idx := range d.Data {
|
||||||
|
@ -58,7 +58,7 @@ type DifficultyLevelRecord struct {
|
|||||||
ManaStealDivisor int // ManaStealDivisor
|
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)
|
// my copy of the txt file (patch_d2/data/global/excel/difficultylevels.txt)
|
||||||
// so I am going to leave these comments
|
// so I am going to leave these comments
|
||||||
|
|
||||||
|
@ -31,49 +31,37 @@ import (
|
|||||||
10
|
10
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// ExperienceBreakpointsRecord describes the experience points required to
|
||||||
|
// gain a level for all character classes
|
||||||
type ExperienceBreakpointsRecord struct {
|
type ExperienceBreakpointsRecord struct {
|
||||||
Level int
|
Level int
|
||||||
HeroBreakpoints map[d2enum.Hero]int
|
HeroBreakpoints map[d2enum.Hero]int
|
||||||
Ratio int
|
Ratio int
|
||||||
}
|
}
|
||||||
|
|
||||||
var experienceStringMap map[string]d2enum.Hero
|
// ExperienceBreakpoints describes the required experience
|
||||||
var experienceHeroMap map[d2enum.Hero]string
|
// for each level for each character class
|
||||||
|
//nolint:gochecknoglobals // Currently global by design, only written once
|
||||||
var ExperienceBreakpoints []*ExperienceBreakpointsRecord
|
var ExperienceBreakpoints []*ExperienceBreakpointsRecord
|
||||||
|
|
||||||
|
//nolint:gochecknoglobals // Currently global by design
|
||||||
var maxLevels map[d2enum.Hero]int
|
var maxLevels map[d2enum.Hero]int
|
||||||
|
|
||||||
|
// GetMaxLevelByHero returns the highest level attainable for a hero type
|
||||||
func GetMaxLevelByHero(heroType d2enum.Hero) int {
|
func GetMaxLevelByHero(heroType d2enum.Hero) int {
|
||||||
return maxLevels[heroType]
|
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 {
|
func GetExperienceBreakpoint(heroType d2enum.Hero, level int) int {
|
||||||
return ExperienceBreakpoints[level].HeroBreakpoints[heroType]
|
return ExperienceBreakpoints[level].HeroBreakpoints[heroType]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadExperienceBreakpoints loads experience.txt into a map
|
||||||
|
// ExperienceBreakpoints []*ExperienceBreakpointsRecord
|
||||||
func LoadExperienceBreakpoints(file []byte) {
|
func LoadExperienceBreakpoints(file []byte) {
|
||||||
d := d2common.LoadDataDictionary(string(file))
|
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
|
// we skip the second row because that describes max level of char classes
|
||||||
ExperienceBreakpoints = make([]*ExperienceBreakpointsRecord, len(d.Data)-1)
|
ExperienceBreakpoints = make([]*ExperienceBreakpointsRecord, len(d.Data)-1)
|
||||||
|
|
||||||
|
@ -6,6 +6,8 @@ import (
|
|||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
"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 {
|
type GemsRecord struct {
|
||||||
Name string
|
Name string
|
||||||
Letter string
|
Letter string
|
||||||
@ -50,13 +52,17 @@ type GemsRecord struct {
|
|||||||
ShieldMod3Max int
|
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) {
|
func LoadGems(file []byte) {
|
||||||
d := d2common.LoadDataDictionary(string(file))
|
d := d2common.LoadDataDictionary(string(file))
|
||||||
|
|
||||||
var Gems []*GemsRecord
|
Gems = make(map[string]*GemsRecord, len(d.Data))
|
||||||
|
|
||||||
for idx := range 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
|
"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.
|
separate base-game gems and expansion runes.
|
||||||
@ -104,7 +110,7 @@ func LoadGems(file []byte) {
|
|||||||
ShieldMod3Min: d.GetNumber("shieldMod3Min", idx),
|
ShieldMod3Min: d.GetNumber("shieldMod3Min", idx),
|
||||||
ShieldMod3Max: d.GetNumber("shieldMod3Max", idx),
|
ShieldMod3Max: d.GetNumber("shieldMod3Max", idx),
|
||||||
}
|
}
|
||||||
Gems = append(Gems, gem)
|
Gems[gem.Name] = gem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,8 @@ import (
|
|||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// HirelingRecord is a representation of rows in hireling.txt
|
||||||
|
// these records describe mercenaries
|
||||||
type HirelingRecord struct {
|
type HirelingRecord struct {
|
||||||
Hireling string
|
Hireling string
|
||||||
SubType string
|
SubType string
|
||||||
@ -81,9 +83,15 @@ type HirelingRecord struct {
|
|||||||
Shield int
|
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) {
|
func LoadHireling(file []byte) {
|
||||||
d := d2common.LoadDataDictionary(string(file))
|
d := d2common.LoadDataDictionary(string(file))
|
||||||
var Hirelings []*HirelingRecord
|
|
||||||
|
Hirelings = make([]*HirelingRecord, len(d.Data))
|
||||||
|
|
||||||
for idx := range d.Data {
|
for idx := range d.Data {
|
||||||
hireling := &HirelingRecord{
|
hireling := &HirelingRecord{
|
||||||
|
@ -8,13 +8,9 @@ import (
|
|||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||||
)
|
)
|
||||||
|
|
||||||
var MagicPrefixDictionary *d2common.DataDictionary
|
// MagicPrefix + MagicSuffix store item affix records
|
||||||
var MagicSuffixDictionary *d2common.DataDictionary
|
var MagicPrefix []*ItemAffixCommonRecord //nolint:gochecknoglobals // Currently global by design
|
||||||
|
var MagicSuffix []*ItemAffixCommonRecord //nolint:gochecknoglobals // Currently global by design
|
||||||
var MagicPrefixRecords []*ItemAffixCommonRecord
|
|
||||||
var MagicSuffixRecords []*ItemAffixCommonRecord
|
|
||||||
|
|
||||||
var AffixMagicGroups []*ItemAffixCommonGroup
|
|
||||||
|
|
||||||
// LoadMagicPrefix loads MagicPrefix.txt
|
// LoadMagicPrefix loads MagicPrefix.txt
|
||||||
func LoadMagicPrefix(file []byte) {
|
func LoadMagicPrefix(file []byte) {
|
||||||
@ -22,7 +18,7 @@ func LoadMagicPrefix(file []byte) {
|
|||||||
|
|
||||||
subType := d2enum.ItemAffixMagic
|
subType := d2enum.ItemAffixMagic
|
||||||
|
|
||||||
MagicPrefixDictionary, MagicPrefixRecords = loadDictionary(file, superType, subType)
|
MagicPrefix = loadDictionary(file, superType, subType)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadMagicSuffix loads MagicSuffix.txt
|
// LoadMagicSuffix loads MagicSuffix.txt
|
||||||
@ -31,11 +27,11 @@ func LoadMagicSuffix(file []byte) {
|
|||||||
|
|
||||||
subType := d2enum.ItemAffixMagic
|
subType := d2enum.ItemAffixMagic
|
||||||
|
|
||||||
MagicSuffixDictionary, MagicSuffixRecords = loadDictionary(file, superType, subType)
|
MagicSuffix = loadDictionary(file, superType, subType)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAffixString(t1 d2enum.ItemAffixSuperType, t2 d2enum.ItemAffixSubType) string {
|
func getAffixString(t1 d2enum.ItemAffixSuperType, t2 d2enum.ItemAffixSubType) string {
|
||||||
var name string = ""
|
var name = ""
|
||||||
|
|
||||||
if t2 == d2enum.ItemAffixMagic {
|
if t2 == d2enum.ItemAffixMagic {
|
||||||
name = "Magic"
|
name = "Magic"
|
||||||
@ -55,58 +51,15 @@ func loadDictionary(
|
|||||||
file []byte,
|
file []byte,
|
||||||
superType d2enum.ItemAffixSuperType,
|
superType d2enum.ItemAffixSuperType,
|
||||||
subType d2enum.ItemAffixSubType,
|
subType d2enum.ItemAffixSubType,
|
||||||
) (*d2common.DataDictionary, []*ItemAffixCommonRecord) {
|
) []*ItemAffixCommonRecord {
|
||||||
dict := d2common.LoadDataDictionary(string(file))
|
dict := d2common.LoadDataDictionary(string(file))
|
||||||
records := createItemAffixRecords(dict, superType, subType)
|
records := createItemAffixRecords(dict, superType, subType)
|
||||||
name := getAffixString(superType, subType)
|
name := getAffixString(superType, subType)
|
||||||
log.Printf("Loaded %d %s records", len(dict.Data), name)
|
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(
|
func createItemAffixRecords(
|
||||||
d *d2common.DataDictionary,
|
d *d2common.DataDictionary,
|
||||||
superType d2enum.ItemAffixSuperType,
|
superType d2enum.ItemAffixSuperType,
|
||||||
@ -177,7 +130,7 @@ func createItemAffixRecords(
|
|||||||
}
|
}
|
||||||
|
|
||||||
group := ItemAffixGroups[affix.GroupID]
|
group := ItemAffixGroups[affix.GroupID]
|
||||||
group.AddMember(affix)
|
group.addMember(affix)
|
||||||
|
|
||||||
records = append(records, affix)
|
records = append(records, affix)
|
||||||
}
|
}
|
||||||
@ -185,14 +138,16 @@ func createItemAffixRecords(
|
|||||||
return records
|
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 {
|
type ItemAffixCommonGroup struct {
|
||||||
ID int
|
ID int
|
||||||
Members map[string]*ItemAffixCommonRecord
|
Members map[string]*ItemAffixCommonRecord
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *ItemAffixCommonGroup) AddMember(a *ItemAffixCommonRecord) {
|
func (g *ItemAffixCommonGroup) addMember(a *ItemAffixCommonRecord) {
|
||||||
if g.Members == nil {
|
if g.Members == nil {
|
||||||
g.Members = make(map[string]*ItemAffixCommonRecord)
|
g.Members = make(map[string]*ItemAffixCommonRecord)
|
||||||
}
|
}
|
||||||
@ -200,7 +155,7 @@ func (g *ItemAffixCommonGroup) AddMember(a *ItemAffixCommonRecord) {
|
|||||||
g.Members[a.Name] = a
|
g.Members[a.Name] = a
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *ItemAffixCommonGroup) GetTotalFrequency() int {
|
func (g *ItemAffixCommonGroup) getTotalFrequency() int {
|
||||||
total := 0
|
total := 0
|
||||||
|
|
||||||
for _, affix := range g.Members {
|
for _, affix := range g.Members {
|
||||||
@ -210,6 +165,9 @@ func (g *ItemAffixCommonGroup) GetTotalFrequency() int {
|
|||||||
return total
|
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 {
|
type ItemAffixCommonModifier struct {
|
||||||
Code string
|
Code string
|
||||||
Parameter int
|
Parameter int
|
||||||
@ -217,46 +175,49 @@ type ItemAffixCommonModifier struct {
|
|||||||
Max int
|
Max int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ItemAffixCommonRecord is a common definition that both prefix and suffix use
|
||||||
type ItemAffixCommonRecord struct {
|
type ItemAffixCommonRecord struct {
|
||||||
|
Group *ItemAffixCommonGroup
|
||||||
|
Modifiers []*ItemAffixCommonModifier
|
||||||
|
|
||||||
|
ItemInclude []string
|
||||||
|
ItemExclude []string
|
||||||
|
|
||||||
Name string
|
Name string
|
||||||
|
Class string
|
||||||
|
TransformColor string
|
||||||
|
|
||||||
Version int
|
Version int
|
||||||
Type d2enum.ItemAffixSubType
|
Type d2enum.ItemAffixSubType
|
||||||
|
|
||||||
|
Level int
|
||||||
|
MaxLevel int
|
||||||
|
|
||||||
|
LevelReq int
|
||||||
|
ClassLevelReq int
|
||||||
|
|
||||||
|
Frequency int
|
||||||
|
GroupID int
|
||||||
|
|
||||||
|
PriceAdd int
|
||||||
|
PriceScale int
|
||||||
|
|
||||||
IsPrefix bool
|
IsPrefix bool
|
||||||
IsSuffix bool
|
IsSuffix bool
|
||||||
|
|
||||||
Spawnable bool
|
Spawnable bool
|
||||||
Rare bool
|
Rare bool
|
||||||
|
|
||||||
Level int
|
|
||||||
MaxLevel int
|
|
||||||
|
|
||||||
LevelReq int
|
|
||||||
Class string
|
|
||||||
ClassLevelReq int
|
|
||||||
|
|
||||||
Frequency int
|
|
||||||
GroupID int
|
|
||||||
Group *ItemAffixCommonGroup
|
|
||||||
|
|
||||||
Modifiers []*ItemAffixCommonModifier
|
|
||||||
|
|
||||||
Transform bool
|
Transform bool
|
||||||
TransformColor string
|
|
||||||
|
|
||||||
ItemInclude []string
|
|
||||||
ItemExclude []string
|
|
||||||
|
|
||||||
PriceAdd int
|
|
||||||
PriceScale int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ProbabilityToSpawn returns the chance of the affix spawning on an
|
||||||
|
// item with a given quality level
|
||||||
func (a *ItemAffixCommonRecord) ProbabilityToSpawn(qlvl int) float64 {
|
func (a *ItemAffixCommonRecord) ProbabilityToSpawn(qlvl int) float64 {
|
||||||
if (qlvl > a.MaxLevel) || (qlvl < a.Level) {
|
if (qlvl > a.MaxLevel) || (qlvl < a.Level) {
|
||||||
return 0.0
|
return 0.0
|
||||||
}
|
}
|
||||||
|
|
||||||
p := float64(a.Frequency) / float64(a.Group.GetTotalFrequency())
|
p := float64(a.Frequency) / float64(a.Group.getTotalFrequency())
|
||||||
|
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
@ -9,16 +9,45 @@ import (
|
|||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ItemCommonRecord is a representation of entries from armor.txt, weapons.txt, and misc.txt
|
||||||
type ItemCommonRecord struct {
|
type ItemCommonRecord struct {
|
||||||
|
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
|
Source d2enum.InventoryItemType
|
||||||
|
|
||||||
Name string
|
|
||||||
|
|
||||||
Version int // 0 = classic, 100 = expansion
|
Version int // 0 = classic, 100 = expansion
|
||||||
CompactSave bool // if true, doesn't store any stats upon saving
|
|
||||||
Rarity int // higher, the rarer
|
Rarity int // higher, the rarer
|
||||||
Spawnable bool // if 0, cannot spawn in shops
|
|
||||||
|
|
||||||
MinAC int
|
MinAC int
|
||||||
MaxAC int
|
MaxAC int
|
||||||
Absorbs int // unused?
|
Absorbs int // unused?
|
||||||
@ -26,38 +55,20 @@ type ItemCommonRecord struct {
|
|||||||
RequiredStrength int
|
RequiredStrength int
|
||||||
Block int // chance to block, capped at 75%
|
Block int // chance to block, capped at 75%
|
||||||
Durability int // base durability 0-255
|
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)
|
Level int // base item level (controls monster drops, for instance a lv20 monster cannot drop a lv30 item)
|
||||||
RequiredLevel int // required level to wield
|
RequiredLevel int // required level to wield
|
||||||
Cost int // base cost
|
Cost int // base cost
|
||||||
GambleCost int // for reference only, not used
|
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?)
|
MagicLevel int // additional magic level (for gambling?)
|
||||||
AutoPrefix int // prefix automatically assigned to this item on spawn, maps to group column of Automagic.txt
|
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
|
SpellOffset int // unknown
|
||||||
|
|
||||||
Component int // corresponds to Composit.txt, player animation layer used by this
|
Component int // corresponds to Composit.txt, player animation layer used by this
|
||||||
InventoryWidth int
|
InventoryWidth int
|
||||||
InventoryHeight int
|
InventoryHeight int
|
||||||
HasInventory bool // if true, item can store gems or runes
|
|
||||||
GemSockets int // number of gems to store
|
GemSockets int // number of gems to store
|
||||||
GemApplyType int // what kind of gem effect is applied
|
GemApplyType int // what kind of gem effect is applied
|
||||||
// 0 = weapon, 1= armor or helmet, 2 = shield
|
// 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 represent how player animations and graphics change upon wearing this
|
||||||
// these come from ArmType.txt
|
// these come from ArmType.txt
|
||||||
AnimRightArm int
|
AnimRightArm int
|
||||||
@ -67,33 +78,15 @@ type ItemCommonRecord struct {
|
|||||||
AnimRightShoulderPad int
|
AnimRightShoulderPad int
|
||||||
AnimLeftShoulderPad 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
|
MinStack int // min size of stack when item is spawned, used if stackable
|
||||||
MaxStack int // max size of stack when item is spawned
|
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
|
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?
|
TransTable int // unknown, related to blending mode?
|
||||||
Quivered bool // if true, requires ammo to use
|
|
||||||
LightRadius int // apparently unused
|
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?
|
Quest int // indicates that this item belongs to a given quest?
|
||||||
|
|
||||||
MissileType int // missile gfx for throwing
|
MissileType int // missile gfx for throwing
|
||||||
DurabilityWarning int // controls what warning icon appears when durability is low
|
DurabilityWarning int // controls what warning icon appears when durability is low
|
||||||
QuantityWarning int // controls at what quantity the low quantity warning appears
|
QuantityWarning int // controls at what quantity the low quantity warning appears
|
||||||
|
|
||||||
MinDamage int
|
MinDamage int
|
||||||
MaxDamage int
|
MaxDamage int
|
||||||
StrengthBonus int
|
StrengthBonus int
|
||||||
@ -103,23 +96,8 @@ type ItemCommonRecord struct {
|
|||||||
|
|
||||||
GemOffset int // unknown
|
GemOffset int // unknown
|
||||||
BitField1 int // 1 = leather item, 3 = metal
|
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
|
ColorTransform int // colormap to use for player's gfx
|
||||||
InventoryColorTransform int // colormap to use for inventory'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
|
Min2HandDamage int
|
||||||
Max2HandDamage int
|
Max2HandDamage int
|
||||||
MinMissileDamage int // ranged damage stats
|
MinMissileDamage int // ranged damage stats
|
||||||
@ -129,50 +107,45 @@ type ItemCommonRecord struct {
|
|||||||
// final mindam = min * str / strbonus + min * dex / dexbonus
|
// final mindam = min * str / strbonus + min * dex / dexbonus
|
||||||
// same for maxdam
|
// same for maxdam
|
||||||
RequiredDexterity int
|
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?)
|
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
|
TransmogMin int // min amount of the transmog item to create
|
||||||
TransmogMax int // max ''
|
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?
|
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
|
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
|
EffectLength int // timer for timed usage effects
|
||||||
UsageStats [3]ItemUsageStat // stat boosts applied upon usage
|
|
||||||
|
|
||||||
SpellDescriptionType int // specifies how to format the usage description
|
SpellDescriptionType int // specifies how to format the usage description
|
||||||
// 0 = none, 1 = use desc string, 2 = use desc string + calc value
|
// 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)
|
|
||||||
|
|
||||||
|
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
|
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 {
|
type ItemUsageStat struct {
|
||||||
Stat string // name of the stat to add to
|
Stat string // name of the stat to add to
|
||||||
Calc d2common.CalcString // calc string representing the amount to add
|
Calc d2common.CalcString // calc string representing the amount to add
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ItemVendorParams are parameters that vendors use
|
||||||
type ItemVendorParams struct {
|
type ItemVendorParams struct {
|
||||||
Min int // minimum of this item they can stock
|
Min int // minimum of this item they can stock
|
||||||
Max int // max they can stock
|
Max int // max they can stock
|
||||||
@ -181,16 +154,18 @@ type ItemVendorParams struct {
|
|||||||
MagicLevel uint8
|
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 {
|
if CommonItems == nil {
|
||||||
CommonItems = make(map[string]*ItemCommonRecord)
|
CommonItems = make(map[string]*ItemCommonRecord)
|
||||||
}
|
}
|
||||||
|
|
||||||
items := make(map[string]*ItemCommonRecord)
|
items := make(map[string]*ItemCommonRecord)
|
||||||
data := strings.Split(string(file), "\r\n")
|
data := strings.Split(string(file), "\r\n")
|
||||||
mapping := MapHeaders(data[0])
|
mapping := mapHeaders(data[0])
|
||||||
|
|
||||||
for lineno, line := range data {
|
for lineno, line := range data {
|
||||||
if lineno == 0 {
|
if lineno == 0 {
|
||||||
@ -201,178 +176,178 @@ func LoadCommonItems(file []byte, source d2enum.InventoryItemType) *map[string]*
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
rec := createCommonItemRecord(line, &mapping, source)
|
rec := createCommonItemRecord(line, mapping, source)
|
||||||
items[rec.Code] = &rec
|
items[rec.Code] = &rec
|
||||||
CommonItems[rec.Code] = &rec
|
CommonItems[rec.Code] = &rec
|
||||||
}
|
}
|
||||||
|
|
||||||
return &items
|
return items
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint:funlen // Makes no sens to split
|
//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")
|
r := strings.Split(line, "\t")
|
||||||
result := ItemCommonRecord{
|
result := ItemCommonRecord{
|
||||||
Source: source,
|
Source: source,
|
||||||
|
|
||||||
Name: MapLoadString(&r, mapping, "name"),
|
Name: mapLoadString(&r, mapping, "name"),
|
||||||
|
|
||||||
Version: MapLoadInt(&r, mapping, "version"),
|
Version: mapLoadInt(&r, mapping, "version"),
|
||||||
CompactSave: MapLoadBool(&r, mapping, "compactsave"),
|
CompactSave: mapLoadBool(&r, mapping, "compactsave"),
|
||||||
Rarity: MapLoadInt(&r, mapping, "rarity"),
|
Rarity: mapLoadInt(&r, mapping, "rarity"),
|
||||||
Spawnable: MapLoadBool(&r, mapping, "spawnable"),
|
Spawnable: mapLoadBool(&r, mapping, "spawnable"),
|
||||||
|
|
||||||
MinAC: MapLoadInt(&r, mapping, "minac"),
|
MinAC: mapLoadInt(&r, mapping, "minac"),
|
||||||
MaxAC: MapLoadInt(&r, mapping, "maxac"),
|
MaxAC: mapLoadInt(&r, mapping, "maxac"),
|
||||||
Absorbs: MapLoadInt(&r, mapping, "absorbs"),
|
Absorbs: mapLoadInt(&r, mapping, "absorbs"),
|
||||||
Speed: MapLoadInt(&r, mapping, "speed"),
|
Speed: mapLoadInt(&r, mapping, "speed"),
|
||||||
RequiredStrength: MapLoadInt(&r, mapping, "reqstr"),
|
RequiredStrength: mapLoadInt(&r, mapping, "reqstr"),
|
||||||
Block: MapLoadInt(&r, mapping, "block"),
|
Block: mapLoadInt(&r, mapping, "block"),
|
||||||
Durability: MapLoadInt(&r, mapping, "durability"),
|
Durability: mapLoadInt(&r, mapping, "durability"),
|
||||||
NoDurability: MapLoadBool(&r, mapping, "nodurability"),
|
NoDurability: mapLoadBool(&r, mapping, "nodurability"),
|
||||||
|
|
||||||
Level: MapLoadInt(&r, mapping, "level"),
|
Level: mapLoadInt(&r, mapping, "level"),
|
||||||
RequiredLevel: MapLoadInt(&r, mapping, "levelreq"),
|
RequiredLevel: mapLoadInt(&r, mapping, "levelreq"),
|
||||||
Cost: MapLoadInt(&r, mapping, "cost"),
|
Cost: mapLoadInt(&r, mapping, "cost"),
|
||||||
GambleCost: MapLoadInt(&r, mapping, "gamble cost"),
|
GambleCost: mapLoadInt(&r, mapping, "gamble cost"),
|
||||||
Code: MapLoadString(&r, mapping, "code"),
|
Code: mapLoadString(&r, mapping, "code"),
|
||||||
NameString: MapLoadString(&r, mapping, "namestr"),
|
NameString: mapLoadString(&r, mapping, "namestr"),
|
||||||
MagicLevel: MapLoadInt(&r, mapping, "magic lvl"),
|
MagicLevel: mapLoadInt(&r, mapping, "magic lvl"),
|
||||||
AutoPrefix: MapLoadInt(&r, mapping, "auto prefix"),
|
AutoPrefix: mapLoadInt(&r, mapping, "auto prefix"),
|
||||||
|
|
||||||
AlternateGfx: MapLoadString(&r, mapping, "alternategfx"),
|
AlternateGfx: mapLoadString(&r, mapping, "alternategfx"),
|
||||||
OpenBetaGfx: MapLoadString(&r, mapping, "OpenBetaGfx"),
|
OpenBetaGfx: mapLoadString(&r, mapping, "OpenBetaGfx"),
|
||||||
NormalCode: MapLoadString(&r, mapping, "normcode"),
|
NormalCode: mapLoadString(&r, mapping, "normcode"),
|
||||||
UberCode: MapLoadString(&r, mapping, "ubercode"),
|
UberCode: mapLoadString(&r, mapping, "ubercode"),
|
||||||
UltraCode: MapLoadString(&r, mapping, "ultracode"),
|
UltraCode: mapLoadString(&r, mapping, "ultracode"),
|
||||||
|
|
||||||
SpellOffset: MapLoadInt(&r, mapping, "spelloffset"),
|
SpellOffset: mapLoadInt(&r, mapping, "spelloffset"),
|
||||||
|
|
||||||
Component: MapLoadInt(&r, mapping, "component"),
|
Component: mapLoadInt(&r, mapping, "component"),
|
||||||
InventoryWidth: MapLoadInt(&r, mapping, "invwidth"),
|
InventoryWidth: mapLoadInt(&r, mapping, "invwidth"),
|
||||||
InventoryHeight: MapLoadInt(&r, mapping, "invheight"),
|
InventoryHeight: mapLoadInt(&r, mapping, "invheight"),
|
||||||
HasInventory: MapLoadBool(&r, mapping, "hasinv"),
|
HasInventory: mapLoadBool(&r, mapping, "hasinv"),
|
||||||
GemSockets: MapLoadInt(&r, mapping, "gemsockets"),
|
GemSockets: mapLoadInt(&r, mapping, "gemsockets"),
|
||||||
GemApplyType: MapLoadInt(&r, mapping, "gemapplytype"),
|
GemApplyType: mapLoadInt(&r, mapping, "gemapplytype"),
|
||||||
|
|
||||||
FlippyFile: MapLoadString(&r, mapping, "flippyfile"),
|
FlippyFile: mapLoadString(&r, mapping, "flippyfile"),
|
||||||
InventoryFile: MapLoadString(&r, mapping, "invfile"),
|
InventoryFile: mapLoadString(&r, mapping, "invfile"),
|
||||||
UniqueInventoryFile: MapLoadString(&r, mapping, "uniqueinvfile"),
|
UniqueInventoryFile: mapLoadString(&r, mapping, "uniqueinvfile"),
|
||||||
SetInventoryFile: MapLoadString(&r, mapping, "setinvfile"),
|
SetInventoryFile: mapLoadString(&r, mapping, "setinvfile"),
|
||||||
|
|
||||||
AnimRightArm: MapLoadInt(&r, mapping, "rArm"),
|
AnimRightArm: mapLoadInt(&r, mapping, "rArm"),
|
||||||
AnimLeftArm: MapLoadInt(&r, mapping, "lArm"),
|
AnimLeftArm: mapLoadInt(&r, mapping, "lArm"),
|
||||||
AnimTorso: MapLoadInt(&r, mapping, "Torso"),
|
AnimTorso: mapLoadInt(&r, mapping, "Torso"),
|
||||||
AnimLegs: MapLoadInt(&r, mapping, "Legs"),
|
AnimLegs: mapLoadInt(&r, mapping, "Legs"),
|
||||||
AnimRightShoulderPad: MapLoadInt(&r, mapping, "rSPad"),
|
AnimRightShoulderPad: mapLoadInt(&r, mapping, "rSPad"),
|
||||||
AnimLeftShoulderPad: MapLoadInt(&r, mapping, "lSPad"),
|
AnimLeftShoulderPad: mapLoadInt(&r, mapping, "lSPad"),
|
||||||
|
|
||||||
Useable: MapLoadBool(&r, mapping, "useable"),
|
Useable: mapLoadBool(&r, mapping, "useable"),
|
||||||
|
|
||||||
Throwable: MapLoadBool(&r, mapping, "throwable"),
|
Throwable: mapLoadBool(&r, mapping, "throwable"),
|
||||||
Stackable: MapLoadBool(&r, mapping, "stackable"),
|
Stackable: mapLoadBool(&r, mapping, "stackable"),
|
||||||
MinStack: MapLoadInt(&r, mapping, "minstack"),
|
MinStack: mapLoadInt(&r, mapping, "minstack"),
|
||||||
MaxStack: MapLoadInt(&r, mapping, "maxstack"),
|
MaxStack: mapLoadInt(&r, mapping, "maxstack"),
|
||||||
|
|
||||||
Type: MapLoadString(&r, mapping, "type"),
|
Type: mapLoadString(&r, mapping, "type"),
|
||||||
Type2: MapLoadString(&r, mapping, "type2"),
|
Type2: mapLoadString(&r, mapping, "type2"),
|
||||||
|
|
||||||
DropSound: MapLoadString(&r, mapping, "dropsound"),
|
DropSound: mapLoadString(&r, mapping, "dropsound"),
|
||||||
DropSfxFrame: MapLoadInt(&r, mapping, "dropsfxframe"),
|
DropSfxFrame: mapLoadInt(&r, mapping, "dropsfxframe"),
|
||||||
UseSound: MapLoadString(&r, mapping, "usesound"),
|
UseSound: mapLoadString(&r, mapping, "usesound"),
|
||||||
|
|
||||||
Unique: MapLoadBool(&r, mapping, "unique"),
|
Unique: mapLoadBool(&r, mapping, "unique"),
|
||||||
Transparent: MapLoadBool(&r, mapping, "transparent"),
|
Transparent: mapLoadBool(&r, mapping, "transparent"),
|
||||||
TransTable: MapLoadInt(&r, mapping, "transtbl"),
|
TransTable: mapLoadInt(&r, mapping, "transtbl"),
|
||||||
Quivered: MapLoadBool(&r, mapping, "quivered"),
|
Quivered: mapLoadBool(&r, mapping, "quivered"),
|
||||||
LightRadius: MapLoadInt(&r, mapping, "lightradius"),
|
LightRadius: mapLoadInt(&r, mapping, "lightradius"),
|
||||||
Belt: MapLoadBool(&r, mapping, "belt"),
|
Belt: mapLoadBool(&r, mapping, "belt"),
|
||||||
|
|
||||||
Quest: MapLoadInt(&r, mapping, "quest"),
|
Quest: mapLoadInt(&r, mapping, "quest"),
|
||||||
|
|
||||||
MissileType: MapLoadInt(&r, mapping, "missiletype"),
|
MissileType: mapLoadInt(&r, mapping, "missiletype"),
|
||||||
DurabilityWarning: MapLoadInt(&r, mapping, "durwarning"),
|
DurabilityWarning: mapLoadInt(&r, mapping, "durwarning"),
|
||||||
QuantityWarning: MapLoadInt(&r, mapping, "qntwarning"),
|
QuantityWarning: mapLoadInt(&r, mapping, "qntwarning"),
|
||||||
|
|
||||||
MinDamage: MapLoadInt(&r, mapping, "mindam"),
|
MinDamage: mapLoadInt(&r, mapping, "mindam"),
|
||||||
MaxDamage: MapLoadInt(&r, mapping, "maxdam"),
|
MaxDamage: mapLoadInt(&r, mapping, "maxdam"),
|
||||||
StrengthBonus: MapLoadInt(&r, mapping, "StrBonus"),
|
StrengthBonus: mapLoadInt(&r, mapping, "StrBonus"),
|
||||||
DexterityBonus: MapLoadInt(&r, mapping, "DexBonus"),
|
DexterityBonus: mapLoadInt(&r, mapping, "DexBonus"),
|
||||||
|
|
||||||
GemOffset: MapLoadInt(&r, mapping, "gemoffset"),
|
GemOffset: mapLoadInt(&r, mapping, "gemoffset"),
|
||||||
BitField1: MapLoadInt(&r, mapping, "bitfield1"),
|
BitField1: mapLoadInt(&r, mapping, "bitfield1"),
|
||||||
|
|
||||||
Vendors: createItemVendorParams(&r, mapping),
|
Vendors: createItemVendorParams(&r, mapping),
|
||||||
|
|
||||||
SourceArt: MapLoadString(&r, mapping, "Source Art"),
|
SourceArt: mapLoadString(&r, mapping, "Source Art"),
|
||||||
GameArt: MapLoadString(&r, mapping, "Game Art"),
|
GameArt: mapLoadString(&r, mapping, "Game Art"),
|
||||||
ColorTransform: MapLoadInt(&r, mapping, "Transform"),
|
ColorTransform: mapLoadInt(&r, mapping, "Transform"),
|
||||||
InventoryColorTransform: MapLoadInt(&r, mapping, "InvTrans"),
|
InventoryColorTransform: mapLoadInt(&r, mapping, "InvTrans"),
|
||||||
|
|
||||||
SkipName: MapLoadBool(&r, mapping, "SkipName"),
|
SkipName: mapLoadBool(&r, mapping, "SkipName"),
|
||||||
NightmareUpgrade: MapLoadString(&r, mapping, "NightmareUpgrade"),
|
NightmareUpgrade: mapLoadString(&r, mapping, "NightmareUpgrade"),
|
||||||
HellUpgrade: MapLoadString(&r, mapping, "HellUpgrade"),
|
HellUpgrade: mapLoadString(&r, mapping, "HellUpgrade"),
|
||||||
|
|
||||||
Nameable: MapLoadBool(&r, mapping, "Nameable"),
|
Nameable: mapLoadBool(&r, mapping, "Nameable"),
|
||||||
|
|
||||||
// weapon params
|
// weapon params
|
||||||
BarbOneOrTwoHanded: MapLoadBool(&r, mapping, "1or2handed"),
|
BarbOneOrTwoHanded: mapLoadBool(&r, mapping, "1or2handed"),
|
||||||
UsesTwoHands: MapLoadBool(&r, mapping, "2handed"),
|
UsesTwoHands: mapLoadBool(&r, mapping, "2handed"),
|
||||||
Min2HandDamage: MapLoadInt(&r, mapping, "2handmindam"),
|
Min2HandDamage: mapLoadInt(&r, mapping, "2handmindam"),
|
||||||
Max2HandDamage: MapLoadInt(&r, mapping, "2handmaxdam"),
|
Max2HandDamage: mapLoadInt(&r, mapping, "2handmaxdam"),
|
||||||
MinMissileDamage: MapLoadInt(&r, mapping, "minmisdam"),
|
MinMissileDamage: mapLoadInt(&r, mapping, "minmisdam"),
|
||||||
MaxMissileDamage: MapLoadInt(&r, mapping, "maxmisdam"),
|
MaxMissileDamage: mapLoadInt(&r, mapping, "maxmisdam"),
|
||||||
MissileSpeed: MapLoadInt(&r, mapping, "misspeed"),
|
MissileSpeed: mapLoadInt(&r, mapping, "misspeed"),
|
||||||
ExtraRange: MapLoadInt(&r, mapping, "rangeadder"),
|
ExtraRange: mapLoadInt(&r, mapping, "rangeadder"),
|
||||||
|
|
||||||
RequiredDexterity: MapLoadInt(&r, mapping, "reqdex"),
|
RequiredDexterity: mapLoadInt(&r, mapping, "reqdex"),
|
||||||
|
|
||||||
WeaponClass: MapLoadString(&r, mapping, "wclass"),
|
WeaponClass: mapLoadString(&r, mapping, "wclass"),
|
||||||
WeaponClass2Hand: MapLoadString(&r, mapping, "2handedwclass"),
|
WeaponClass2Hand: mapLoadString(&r, mapping, "2handedwclass"),
|
||||||
|
|
||||||
HitClass: MapLoadString(&r, mapping, "hit class"),
|
HitClass: mapLoadString(&r, mapping, "hit class"),
|
||||||
SpawnStack: MapLoadInt(&r, mapping, "spawnstack"),
|
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
|
// misc params
|
||||||
FlavorText: MapLoadString(&r, mapping, "szFlavorText"),
|
FlavorText: mapLoadString(&r, mapping, "szFlavorText"),
|
||||||
|
|
||||||
Transmogrify: MapLoadBool(&r, mapping, "Transmogrify"),
|
Transmogrify: mapLoadBool(&r, mapping, "Transmogrify"),
|
||||||
TransmogCode: MapLoadString(&r, mapping, "TMogType"),
|
TransmogCode: mapLoadString(&r, mapping, "TMogType"),
|
||||||
TransmogMin: MapLoadInt(&r, mapping, "TMogMin"),
|
TransmogMin: mapLoadInt(&r, mapping, "TMogMin"),
|
||||||
TransmogMax: MapLoadInt(&r, mapping, "TMogMax"),
|
TransmogMax: mapLoadInt(&r, mapping, "TMogMax"),
|
||||||
|
|
||||||
AutoBelt: MapLoadBool(&r, mapping, "autobelt"),
|
AutoBelt: mapLoadBool(&r, mapping, "autobelt"),
|
||||||
|
|
||||||
SpellIcon: MapLoadInt(&r, mapping, "spellicon"),
|
SpellIcon: mapLoadInt(&r, mapping, "spellicon"),
|
||||||
SpellType: MapLoadInt(&r, mapping, "pSpell"),
|
SpellType: mapLoadInt(&r, mapping, "pSpell"),
|
||||||
OverlayState: MapLoadString(&r, mapping, "state"),
|
OverlayState: mapLoadString(&r, mapping, "state"),
|
||||||
CureOverlayStates: [2]string{
|
CureOverlayStates: [2]string{
|
||||||
MapLoadString(&r, mapping, "cstate1"),
|
mapLoadString(&r, mapping, "cstate1"),
|
||||||
MapLoadString(&r, mapping, "cstate2"),
|
mapLoadString(&r, mapping, "cstate2"),
|
||||||
},
|
},
|
||||||
EffectLength: MapLoadInt(&r, mapping, "len"),
|
EffectLength: mapLoadInt(&r, mapping, "len"),
|
||||||
UsageStats: createItemUsageStats(&r, mapping),
|
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
|
// 0 = none, 1 = use desc string, 2 = use desc string + calc value
|
||||||
SpellDescriptionString: MapLoadString(&r, mapping, "spelldescstr"),
|
SpellDescriptionString: mapLoadString(&r, mapping, "spelldescstr"),
|
||||||
SpellDescriptionCalc: d2common.CalcString(MapLoadString(&r, mapping, "spelldesccalc")),
|
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
|
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 := make([]string, 17)
|
||||||
vs[0] = "Charsi"
|
vs[0] = "Charsi"
|
||||||
vs[1] = "Gheed"
|
vs[1] = "Gheed"
|
||||||
@ -396,11 +371,11 @@ func createItemVendorParams(r *[]string, mapping *map[string]int) map[string]*It
|
|||||||
|
|
||||||
for _, name := range vs {
|
for _, name := range vs {
|
||||||
wvp := ItemVendorParams{
|
wvp := ItemVendorParams{
|
||||||
Min: MapLoadInt(r, mapping, name+"Min"),
|
Min: mapLoadInt(r, mapping, name+"Min"),
|
||||||
Max: MapLoadInt(r, mapping, name+"Max"),
|
Max: mapLoadInt(r, mapping, name+"Max"),
|
||||||
MagicMin: MapLoadInt(r, mapping, name+"MagicMin"),
|
MagicMin: mapLoadInt(r, mapping, name+"MagicMin"),
|
||||||
MagicMax: MapLoadInt(r, mapping, name+"MagicMax"),
|
MagicMax: mapLoadInt(r, mapping, name+"MagicMax"),
|
||||||
MagicLevel: MapLoadUint8(r, mapping, name+"MagicLvl"),
|
MagicLevel: mapLoadUint8(r, mapping, name+"MagicLvl"),
|
||||||
}
|
}
|
||||||
result[name] = &wvp
|
result[name] = &wvp
|
||||||
}
|
}
|
||||||
@ -408,11 +383,11 @@ func createItemVendorParams(r *[]string, mapping *map[string]int) map[string]*It
|
|||||||
return result
|
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{}
|
result := [3]ItemUsageStat{}
|
||||||
for i := 0; i < 3; i++ {
|
for i := 0; i < 3; i++ {
|
||||||
result[i].Stat = MapLoadString(r, mapping, "stat"+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)))
|
result[i].Calc = d2common.CalcString(mapLoadString(r, mapping, "calc"+strconv.Itoa(i)))
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
@ -7,35 +7,53 @@ import (
|
|||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
"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
|
// refer to https://d2mods.info/forum/kb/viewarticle?a=448
|
||||||
type ItemStatCostRecord struct {
|
type ItemStatCostRecord struct {
|
||||||
Name string
|
Name string
|
||||||
Index int
|
OpBase string
|
||||||
|
OpStat1 string
|
||||||
|
OpStat2 string
|
||||||
|
OpStat3 string
|
||||||
|
|
||||||
Signed bool // whether the stat is signed
|
MaxStat string // if Direct true, will not exceed val of MaxStat
|
||||||
KeepZero bool // prevent from going negative (assume only client side)
|
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
|
// path_d2.mpq version doesnt have Ranged columne, excluding for now
|
||||||
// Ranged bool // game attempts to keep stat in a range, like strength >-1
|
// Ranged bool // game attempts to keep stat in a range, like strength >-1
|
||||||
MinAccr int // minimum ranged value
|
MinAccr int // minimum ranged value
|
||||||
|
|
||||||
UpdateAnimRate bool // when altered, forces speed handler to adjust speed
|
|
||||||
|
|
||||||
SendOther bool // whether to send to other clients
|
|
||||||
SendBits int // #bits to send in stat update
|
SendBits int // #bits to send in stat update
|
||||||
SendParam 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
|
SaveBits int // #bits saved to .d2s files, max == 2^SaveBits-1
|
||||||
SaveAdd int // how large the negative range is (lowers max, as well)
|
SaveAdd int // how large the negative range is (lowers max, as well)
|
||||||
SaveParamBits int // #param bits are saved (safe value is 17)
|
SaveParamBits int // #param bits are saved (safe value is 17)
|
||||||
|
|
||||||
Encode EncodingType // how the stat is encoded in .d2s files
|
Encode d2enum.EncodingType // how the stat is encoded in .d2s files
|
||||||
|
|
||||||
CallbackEnabled bool // whether callback fn is called if value changes
|
|
||||||
|
|
||||||
// these two fields control additional cost on items
|
// these two fields control additional cost on items
|
||||||
// cost * (1 + value * multiply / 1024)) + add (...)
|
// cost * (1 + value * multiply / 1024)) + add (...)
|
||||||
@ -48,20 +66,8 @@ type ItemStatCostRecord struct {
|
|||||||
ValShift int // controls how stat is stored in .d2s
|
ValShift int // controls how stat is stored in .d2s
|
||||||
// so that you can save `+1` instead of `+256`
|
// so that you can save `+1` instead of `+256`
|
||||||
|
|
||||||
OperatorType OperatorType
|
OperatorType d2enum.OperatorType
|
||||||
OpParam int
|
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
|
EventID1 d2enum.ItemEventType
|
||||||
EventID2 d2enum.ItemEventType
|
EventID2 d2enum.ItemEventType
|
||||||
@ -70,175 +76,35 @@ type ItemStatCostRecord struct {
|
|||||||
|
|
||||||
DescPriority int // determines order when displayed
|
DescPriority int // determines order when displayed
|
||||||
DescFnID d2enum.DescFuncID
|
DescFnID d2enum.DescFuncID
|
||||||
DescFn interface{} // the sprintf func
|
|
||||||
|
|
||||||
// Controls whenever and if so in what way the stat value is shown
|
// Controls whenever and if so in what way the stat value is shown
|
||||||
// 0 = doesn't show the value of the stat
|
// 0 = doesn't show the value of the stat
|
||||||
// 1 = shows the value of the stat infront of the description
|
// 1 = shows the value of the stat infront of the description
|
||||||
// 2 = shows the value of the stat after the description.
|
// 2 = shows the value of the stat after the description.
|
||||||
DescVal int
|
DescVal int
|
||||||
DescStrPos string // string used when val is positive
|
|
||||||
DescStrNeg string
|
|
||||||
DescStr2 string // additional string used by some string funcs
|
|
||||||
|
|
||||||
// when stats in the same group have the same value they use the
|
// 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)
|
// group func for desc (they need to be in the same affix)
|
||||||
DescGroup int
|
DescGroup int
|
||||||
DescGroupFuncID d2enum.DescFuncID
|
|
||||||
DescGroupFn interface{} // group sprintf func
|
|
||||||
DescGroupVal int
|
DescGroupVal int
|
||||||
DescGroupStrPos string // string used when val is positive
|
DescGroupFuncID d2enum.DescFuncID
|
||||||
DescGroupStrNeg string
|
|
||||||
DescGroupStr2 string // additional string used by some string funcs
|
|
||||||
|
|
||||||
// Stay far away from this column unless you really know what you're
|
CallbackEnabled bool // whether callback fn is called if value changes
|
||||||
// doing and / or work for Blizzard, this column is used during bin-file
|
Signed bool // whether the stat is signed
|
||||||
// creation to generate a cache regulating the op-stat stuff and other
|
KeepZero bool // prevent from going negative (assume only client side)
|
||||||
// things, changing it can be futile, it works like the constants column
|
UpdateAnimRate bool // when altered, forces speed handler to adjust speed
|
||||||
// in MonUMod.txt and has no other relation to ItemStatCost.txt, the first
|
SendOther bool // whether to send to other clients
|
||||||
// stat in the file simply must have this set or else you may break the
|
Saved bool // whether this stat is saved in .d2s files
|
||||||
// entire op stuff.
|
SavedSigned bool // whether the stat is saved as signed/unsigned
|
||||||
Stuff string // ? TODO ?
|
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
|
// ItemStatCosts stores all of the ItemStatCostRecords
|
||||||
|
//nolint:gochecknoglobals // Currently global by design
|
||||||
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
|
|
||||||
*/
|
|
||||||
|
|
||||||
var ItemStatCosts map[string]*ItemStatCostRecord
|
var ItemStatCosts map[string]*ItemStatCostRecord
|
||||||
|
|
||||||
// LoadItemStatCosts loads ItemStatCostRecord's from text
|
// LoadItemStatCosts loads ItemStatCostRecord's from text
|
||||||
@ -271,7 +137,7 @@ func LoadItemStatCosts(file []byte) {
|
|||||||
SaveAdd: d.GetNumber("Save Add", idx),
|
SaveAdd: d.GetNumber("Save Add", idx),
|
||||||
SaveParamBits: d.GetNumber("Save Param Bits", 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,
|
CallbackEnabled: d.GetNumber("fCallback", idx) > 0,
|
||||||
|
|
||||||
@ -279,7 +145,7 @@ func LoadItemStatCosts(file []byte) {
|
|||||||
CostMultiply: d.GetNumber("Multiply", idx),
|
CostMultiply: d.GetNumber("Multiply", idx),
|
||||||
ValShift: d.GetNumber("ValShift", 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),
|
OpParam: d.GetNumber("op param", idx),
|
||||||
OpBase: d.GetString("op base", idx),
|
OpBase: d.GetString("op base", idx),
|
||||||
OpStat1: d.GetString("op stat1", idx),
|
OpStat1: d.GetString("op stat1", idx),
|
||||||
|
@ -6,6 +6,8 @@ import (
|
|||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
"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 {
|
type LevelMazeDetailsRecord struct {
|
||||||
// descriptive, not loaded in game. Corresponds with Name field in
|
// descriptive, not loaded in game. Corresponds with Name field in
|
||||||
// Levels.txt
|
// Levels.txt
|
||||||
@ -34,7 +36,8 @@ type LevelMazeDetailsRecord struct {
|
|||||||
// Beta
|
// 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
|
// LoadLevelMazeDetails loads LevelMazeDetailsRecords from text file
|
||||||
func LoadLevelMazeDetails(file []byte) {
|
func LoadLevelMazeDetails(file []byte) {
|
||||||
|
@ -7,25 +7,27 @@ import (
|
|||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
"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 {
|
type LevelPresetRecord struct {
|
||||||
|
Files [6]string
|
||||||
Name string
|
Name string
|
||||||
DefinitionID int
|
DefinitionID int
|
||||||
LevelID int
|
LevelID int
|
||||||
|
SizeX int
|
||||||
|
SizeY int
|
||||||
|
Pops int
|
||||||
|
PopPad int
|
||||||
|
FileCount int
|
||||||
|
Dt1Mask uint
|
||||||
Populate bool
|
Populate bool
|
||||||
Logicals bool
|
Logicals bool
|
||||||
Outdoors bool
|
Outdoors bool
|
||||||
Animate bool
|
Animate bool
|
||||||
KillEdge bool
|
KillEdge bool
|
||||||
FillBlanks bool
|
FillBlanks bool
|
||||||
SizeX int
|
|
||||||
SizeY int
|
|
||||||
AutoMap bool
|
AutoMap bool
|
||||||
Scan bool
|
Scan bool
|
||||||
Pops int
|
|
||||||
PopPad int
|
|
||||||
FileCount int
|
|
||||||
Files [6]string
|
|
||||||
Dt1Mask uint
|
|
||||||
Beta bool
|
Beta bool
|
||||||
Expansion bool
|
Expansion bool
|
||||||
}
|
}
|
||||||
@ -70,7 +72,8 @@ func createLevelPresetRecord(props []string) LevelPresetRecord {
|
|||||||
return result
|
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
|
// LoadLevelPresets loads level presets from text file
|
||||||
func LoadLevelPresets(file []byte) {
|
func LoadLevelPresets(file []byte) {
|
||||||
|
@ -6,9 +6,12 @@ import (
|
|||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
"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 {
|
type LevelSubstitutionRecord struct {
|
||||||
// Description, reference only.
|
// Description, reference only.
|
||||||
Name string // Name
|
Name string // Name
|
||||||
|
|
||||||
// This value is used in Levels.txt, in the column 'SubType'. You'll notice
|
// 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
|
// 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
|
// groups. If you count each row of a group starting from 0, then you'll
|
||||||
@ -61,8 +64,11 @@ type LevelSubstitutionRecord struct {
|
|||||||
// Beta
|
// Beta
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LevelSubstitutions stores all of the LevelSubstitutionRecords
|
||||||
|
//nolint:gochecknoglobals // Currently global by design
|
||||||
var LevelSubstitutions map[int]*LevelSubstitutionRecord
|
var LevelSubstitutions map[int]*LevelSubstitutionRecord
|
||||||
|
|
||||||
|
// LoadLevelSubstitutions loads lvlsub.txt and parses into records
|
||||||
func LoadLevelSubstitutions(file []byte) {
|
func LoadLevelSubstitutions(file []byte) {
|
||||||
dict := d2common.LoadDataDictionary(string(file))
|
dict := d2common.LoadDataDictionary(string(file))
|
||||||
numRecords := len(dict.Data)
|
numRecords := len(dict.Data)
|
||||||
|
@ -7,17 +7,21 @@ import (
|
|||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
"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 {
|
type LevelTypeRecord struct {
|
||||||
|
Files [32]string
|
||||||
Name string
|
Name string
|
||||||
ID int
|
ID int
|
||||||
Files [32]string
|
|
||||||
Beta bool
|
|
||||||
Act int
|
Act int
|
||||||
|
Beta bool
|
||||||
Expansion 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) {
|
func LoadLevelTypes(file []byte) {
|
||||||
data := strings.Split(string(file), "\r\n")[1:]
|
data := strings.Split(string(file), "\r\n")[1:]
|
||||||
LevelTypes = make([]LevelTypeRecord, len(data))
|
LevelTypes = make([]LevelTypeRecord, len(data))
|
||||||
|
@ -6,6 +6,8 @@ import (
|
|||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
"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 {
|
type LevelWarpRecord struct {
|
||||||
ID int32
|
ID int32
|
||||||
SelectX int32
|
SelectX int32
|
||||||
@ -21,8 +23,8 @@ type LevelWarpRecord struct {
|
|||||||
Direction string
|
Direction string
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint:gochecknoglobals // Currently global by design, only written once
|
|
||||||
// LevelWarps loaded from txt records
|
// LevelWarps loaded from txt records
|
||||||
|
//nolint:gochecknoglobals // Currently global by design, only written once
|
||||||
var LevelWarps map[int]*LevelWarpRecord
|
var LevelWarps map[int]*LevelWarpRecord
|
||||||
|
|
||||||
// LoadLevelWarps loads LevelWarpRecord's from text file data
|
// LoadLevelWarps loads LevelWarpRecord's from text file data
|
||||||
|
@ -7,21 +7,103 @@ import (
|
|||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
"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 {
|
type LevelDetailsRecord struct {
|
||||||
|
|
||||||
|
// Name
|
||||||
// This column has no function, it only serves as a comment field to make it
|
// This column has no function, it only serves as a comment field to make it
|
||||||
// easier to identify the Level name
|
// easier to identify the Level name
|
||||||
Name string // Name <-- the corresponding column name in the txt
|
Name string // Name <-- the corresponding column name in the txt
|
||||||
|
|
||||||
// Level ID (used in columns like VIS0-7)
|
// mon1-mon25 work in Normal difficulty, while nmon1-nmon25 in Nightmare and
|
||||||
Id int // Id
|
// 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
|
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
|
Act int // Act
|
||||||
|
|
||||||
|
// QuestFlag, QuestExpansionFlag
|
||||||
// Used the first one in Classic games and the latter in Expansion games ,
|
// 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
|
// 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
|
// completed the quest associated with the flag to take a town portal to
|
||||||
@ -37,15 +119,13 @@ type LevelDetailsRecord struct {
|
|||||||
// additional layers.
|
// additional layers.
|
||||||
AutomapIndex int // Layer
|
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
|
// 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.
|
// lvlprest.txt to set the size for the .ds1 file.
|
||||||
SizeXNormal int // SizeX
|
SizeXNormal int // SizeX
|
||||||
SizeYNormal int // SizeY
|
SizeYNormal int // SizeY
|
||||||
|
|
||||||
SizeXNightmare int // SizeX(N)
|
SizeXNightmare int // SizeX(N)
|
||||||
SizeYNightmare int // SizeY(N)
|
SizeYNightmare int // SizeY(N)
|
||||||
|
|
||||||
SizeXHell int // SizeX(H)
|
SizeXHell int // SizeX(H)
|
||||||
SizeYHell int // SizeY(H)
|
SizeYHell int // SizeY(H)
|
||||||
|
|
||||||
@ -58,6 +138,9 @@ type LevelDetailsRecord struct {
|
|||||||
// location.
|
// location.
|
||||||
DependantLevelID int // Depend
|
DependantLevelID int // Depend
|
||||||
|
|
||||||
|
// The type of the Level (Id from lvltypes.txt)
|
||||||
|
LevelType int // LevelType
|
||||||
|
|
||||||
// Controls if teleport is allowed in that level.
|
// Controls if teleport is allowed in that level.
|
||||||
// 0 = Teleport not allowed
|
// 0 = Teleport not allowed
|
||||||
// 1 = Teleport allowed
|
// 1 = Teleport allowed
|
||||||
@ -65,55 +148,12 @@ type LevelDetailsRecord struct {
|
|||||||
// (maybe for objects this is controlled by IsDoor column in objects.txt)
|
// (maybe for objects this is controlled by IsDoor column in objects.txt)
|
||||||
TeleportFlag d2enum.TeleportFlag // Teleport
|
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:
|
// Setting for Level Generation: You have 3 possibilities here:
|
||||||
// 1 Random Maze
|
// 1 Random Maze
|
||||||
// 2 Preset Area
|
// 2 Preset Area
|
||||||
// 3 Wilderness level
|
// 3 Wilderness level
|
||||||
LevelGenerationType d2enum.LevelGenerationType // DrlgType
|
LevelGenerationType d2enum.LevelGenerationType // DrlgType
|
||||||
|
|
||||||
// The type of the Level (Id from lvltypes.txt)
|
|
||||||
LevelType int // LevelType
|
|
||||||
|
|
||||||
// NOTE
|
// NOTE
|
||||||
// IDs from LvlSub.txt, which is used to randomize outdoor areas, such as
|
// 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.
|
// 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.
|
// Example: 6=wilderness, 9=desert etc, -1=no subtype.
|
||||||
SubType int // SubType
|
SubType int // SubType
|
||||||
|
|
||||||
// TODO this may need an enumeration.. ?
|
|
||||||
// Tells which subtheme a wilderness area should use.
|
// Tells which subtheme a wilderness area should use.
|
||||||
// Themes ranges from -1 (no subtheme) to 4.
|
// Themes ranges from -1 (no subtheme) to 4.
|
||||||
SubTheme int // SubTheme
|
SubTheme int // SubTheme
|
||||||
@ -170,29 +209,6 @@ type LevelDetailsRecord struct {
|
|||||||
Green int // Green
|
Green int // Green
|
||||||
Blue int // Blue
|
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
|
// 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).
|
// first quest Den of Evil are set to 1, since its the first quest).
|
||||||
QuestID int // Quest
|
QuestID int // Quest
|
||||||
@ -227,14 +243,6 @@ type LevelDetailsRecord struct {
|
|||||||
MonsterUniqueMaxNightmare int // MonUMax(N)
|
MonsterUniqueMaxNightmare int // MonUMax(N)
|
||||||
MonsterUniqueMaxHell int // MonUMax(H)
|
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
|
// 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
|
// 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
|
// Nightmare and Hell difficulties, selected randomly from nmon1-nmon25. In
|
||||||
@ -243,92 +251,12 @@ type LevelDetailsRecord struct {
|
|||||||
// types selected randomly from umon1-umon25.
|
// types selected randomly from umon1-umon25.
|
||||||
NumMonsterTypes int // NumMon
|
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.
|
// Controls the chance for a critter to spawn.
|
||||||
MonsterCritter1SpawnChance int // cpct1
|
MonsterCritter1SpawnChance int // cpct1
|
||||||
MonsterCritter2SpawnChance int // cpct2
|
MonsterCritter2SpawnChance int // cpct2
|
||||||
MonsterCritter3SpawnChance int // cpct3
|
MonsterCritter3SpawnChance int // cpct3
|
||||||
MonsterCritter4SpawnChance int // cpct4
|
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)
|
// Referes to a entry in SoundEnviron.txt (for the Levels Music)
|
||||||
SoundEnvironmentID int // SoundEnv
|
SoundEnvironmentID int // SoundEnv
|
||||||
|
|
||||||
@ -338,17 +266,6 @@ type LevelDetailsRecord struct {
|
|||||||
// between acts however so don't even bother to try.
|
// between acts however so don't even bother to try.
|
||||||
WaypointID int // Waypoint
|
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,
|
// this field uses the ID of the ObjectGroup you want to Spawn in this Area,
|
||||||
// taken from Objgroup.txt.
|
// taken from Objgroup.txt.
|
||||||
ObjectGroupID0 int // ObjGrp0
|
ObjectGroupID0 int // ObjGrp0
|
||||||
@ -371,12 +288,86 @@ type LevelDetailsRecord struct {
|
|||||||
ObjectGroupSpawnChance6 int // ObjPrb6
|
ObjectGroupSpawnChance6 int // ObjPrb6
|
||||||
ObjectGroupSpawnChance7 int // ObjPrb7
|
ObjectGroupSpawnChance7 int // ObjPrb7
|
||||||
|
|
||||||
// Reference Only (can be used for comments)
|
// It sets whether rain or snow (in act 5 only) can fall . Set it to 1 in
|
||||||
// Beta
|
// 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
|
var LevelDetails map[int]*LevelDetailsRecord
|
||||||
|
|
||||||
|
// GetLevelDetails gets a LevelDetailsRecord by the record Id
|
||||||
func GetLevelDetails(id int) *LevelDetailsRecord {
|
func GetLevelDetails(id int) *LevelDetailsRecord {
|
||||||
for i := 0; i < len(LevelDetails); i++ {
|
for i := 0; i < len(LevelDetails); i++ {
|
||||||
if LevelDetails[i].Id == id {
|
if LevelDetails[i].Id == id {
|
||||||
@ -387,6 +378,7 @@ func GetLevelDetails(id int) *LevelDetailsRecord {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadLevelDetails loads level details records from levels.txt
|
||||||
//nolint:funlen // Txt loader, makes no sense to split
|
//nolint:funlen // Txt loader, makes no sense to split
|
||||||
func LoadLevelDetails(file []byte) {
|
func LoadLevelDetails(file []byte) {
|
||||||
dict := d2common.LoadDataDictionary(string(file))
|
dict := d2common.LoadDataDictionary(string(file))
|
||||||
|
@ -6,7 +6,7 @@ import (
|
|||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||||
)
|
)
|
||||||
|
|
||||||
func MapHeaders(line string) map[string]int {
|
func mapHeaders(line string) map[string]int {
|
||||||
m := make(map[string]int)
|
m := make(map[string]int)
|
||||||
r := strings.Split(line, "\t")
|
r := strings.Split(line, "\t")
|
||||||
|
|
||||||
@ -17,8 +17,8 @@ func MapHeaders(line string) map[string]int {
|
|||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
func MapLoadInt(r *[]string, mapping *map[string]int, field string) int {
|
func mapLoadInt(r *[]string, mapping map[string]int, field string) int {
|
||||||
index, ok := (*mapping)[field]
|
index, ok := (mapping)[field]
|
||||||
if ok {
|
if ok {
|
||||||
return d2common.StringToInt(d2common.EmptyToZero(d2common.AsterToEmpty((*r)[index])))
|
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
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func MapLoadString(r *[]string, mapping *map[string]int, field string) string {
|
func mapLoadString(r *[]string, mapping map[string]int, field string) string {
|
||||||
index, ok := (*mapping)[field]
|
index, ok := (mapping)[field]
|
||||||
if ok {
|
if ok {
|
||||||
return d2common.AsterToEmpty((*r)[index])
|
return d2common.AsterToEmpty((*r)[index])
|
||||||
}
|
}
|
||||||
@ -35,12 +35,12 @@ func MapLoadString(r *[]string, mapping *map[string]int, field string) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func MapLoadBool(r *[]string, mapping *map[string]int, field string) bool {
|
func mapLoadBool(r *[]string, mapping map[string]int, field string) bool {
|
||||||
return MapLoadInt(r, mapping, field) == 1
|
return mapLoadInt(r, mapping, field) == 1
|
||||||
}
|
}
|
||||||
|
|
||||||
func MapLoadUint8(r *[]string, mapping *map[string]int, field string) uint8 {
|
func mapLoadUint8(r *[]string, mapping map[string]int, field string) uint8 {
|
||||||
index, ok := (*mapping)[field]
|
index, ok := (mapping)[field]
|
||||||
if ok {
|
if ok {
|
||||||
return d2common.StringToUint8(d2common.EmptyToZero(d2common.AsterToEmpty((*r)[index])))
|
return d2common.StringToUint8(d2common.EmptyToZero(d2common.AsterToEmpty((*r)[index])))
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,11 @@ import (
|
|||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
"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) {
|
func LoadMiscItems(file []byte) {
|
||||||
MiscItems = *LoadCommonItems(file, d2enum.InventoryItemTypeItem)
|
MiscItems = LoadCommonItems(file, d2enum.InventoryItemTypeItem)
|
||||||
log.Printf("Loaded %d misc items", len(MiscItems))
|
log.Printf("Loaded %d misc items", len(MiscItems))
|
||||||
}
|
}
|
||||||
|
@ -7,17 +7,20 @@ import (
|
|||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// MissileCalcParam is a calculation parameter for a missile
|
||||||
type MissileCalcParam struct {
|
type MissileCalcParam struct {
|
||||||
Param int
|
Param int
|
||||||
Desc string
|
Desc string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MissileCalc is a calculation for a missile
|
||||||
type MissileCalc struct {
|
type MissileCalc struct {
|
||||||
Calc d2common.CalcString
|
Calc d2common.CalcString
|
||||||
Desc string
|
Desc string
|
||||||
Params []MissileCalcParam
|
Params []MissileCalcParam
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MissileLight has the parameters for missile lighting
|
||||||
type MissileLight struct {
|
type MissileLight struct {
|
||||||
Diameter int
|
Diameter int
|
||||||
Flicker int
|
Flicker int
|
||||||
@ -26,24 +29,27 @@ type MissileLight struct {
|
|||||||
Blue uint8
|
Blue uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MissileAnimation stores parameters for a missile animation
|
||||||
type MissileAnimation struct {
|
type MissileAnimation struct {
|
||||||
|
CelFileName string
|
||||||
StepsBeforeVisible int
|
StepsBeforeVisible int
|
||||||
StepsBeforeActive int
|
StepsBeforeActive int
|
||||||
LoopAnimation bool
|
|
||||||
CelFileName string
|
|
||||||
AnimationRate int // seems to do nothing
|
AnimationRate int // seems to do nothing
|
||||||
AnimationLength int
|
AnimationLength int
|
||||||
AnimationSpeed int
|
AnimationSpeed int
|
||||||
StartingFrame int // called "RandFrame"
|
StartingFrame int // called "RandFrame"
|
||||||
HasSubLoop bool // runs after first animation ends
|
|
||||||
SubStartingFrame int
|
SubStartingFrame int
|
||||||
SubEndingFrame int
|
SubEndingFrame int
|
||||||
|
LoopAnimation bool
|
||||||
|
HasSubLoop bool // runs after first animation ends
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MissileCollision parameters for missile collision
|
||||||
type MissileCollision struct {
|
type MissileCollision struct {
|
||||||
CollisionType int // controls the kind of collision
|
CollisionType int // controls the kind of collision
|
||||||
// 0 = none, 1 = units only, 3 = normal (units, walls),
|
// 0 = none, 1 = units only, 3 = normal (units, walls),
|
||||||
// 6 = walls only, 8 = walls, units, and floors
|
// 6 = walls only, 8 = walls, units, and floors
|
||||||
|
TimerFrames int // how many frames to persist
|
||||||
DestroyedUponCollision bool
|
DestroyedUponCollision bool
|
||||||
FriendlyFire bool
|
FriendlyFire bool
|
||||||
LastCollide bool // unknown
|
LastCollide bool // unknown
|
||||||
@ -51,9 +57,9 @@ type MissileCollision struct {
|
|||||||
ClientCollision bool // unknown
|
ClientCollision bool // unknown
|
||||||
ClientSend bool // unclear
|
ClientSend bool // unclear
|
||||||
UseCollisionTimer bool // after hit, use timer before dying
|
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 {
|
type MissileDamage struct {
|
||||||
MinDamage int
|
MinDamage int
|
||||||
MaxDamage int
|
MaxDamage int
|
||||||
@ -63,6 +69,7 @@ type MissileDamage struct {
|
|||||||
DamageSynergyPerCalc d2common.CalcString // works like synergy in skills.txt, not clear
|
DamageSynergyPerCalc d2common.CalcString // works like synergy in skills.txt, not clear
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MissileElementalDamage parameters for calculating missile elemental damage
|
||||||
type MissileElementalDamage struct {
|
type MissileElementalDamage struct {
|
||||||
Damage MissileDamage
|
Damage MissileDamage
|
||||||
ElementType string
|
ElementType string
|
||||||
@ -70,20 +77,46 @@ type MissileElementalDamage struct {
|
|||||||
LevelDuration [3]int // 0,1,2, unknown level intervals, bonus duration per level
|
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 {
|
type MissileRecord struct {
|
||||||
|
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
|
Name string
|
||||||
Id int
|
|
||||||
|
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
|
ClientMovementFunc int
|
||||||
ClientCollisionFunc int
|
ClientCollisionFunc int
|
||||||
ServerMovementFunc int
|
ServerMovementFunc int
|
||||||
ServerCollisionFunc int
|
ServerCollisionFunc int
|
||||||
ServerDamageFunc int
|
ServerDamageFunc int
|
||||||
ServerMovementCalc MissileCalc
|
|
||||||
ClientMovementCalc MissileCalc
|
|
||||||
ServerCollisionCalc MissileCalc
|
|
||||||
ClientCollisionCalc MissileCalc
|
|
||||||
ServerDamageCalc MissileCalc
|
|
||||||
|
|
||||||
Velocity int
|
Velocity int
|
||||||
MaxVelocity int
|
MaxVelocity int
|
||||||
@ -92,19 +125,45 @@ type MissileRecord struct {
|
|||||||
Range int
|
Range int
|
||||||
LevelRangeBonus int
|
LevelRangeBonus int
|
||||||
|
|
||||||
Light MissileLight
|
|
||||||
|
|
||||||
Animation MissileAnimation
|
|
||||||
|
|
||||||
Collision MissileCollision
|
|
||||||
|
|
||||||
XOffset int
|
XOffset int
|
||||||
YOffset int
|
YOffset int
|
||||||
ZOffset int
|
ZOffset int
|
||||||
Size int // diameter
|
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
|
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
|
CanDestroy bool // unknown
|
||||||
|
|
||||||
UseAttackRating bool // if true, uses 'attack rating' to determine if it hits or misses
|
UseAttackRating bool // if true, uses 'attack rating' to determine if it hits or misses
|
||||||
@ -120,17 +179,11 @@ type MissileRecord struct {
|
|||||||
// if false, vanishes when spawned in town
|
// if false, vanishes when spawned in town
|
||||||
IgnoreBossModifiers bool // if true, doesn't get bonuses from boss mods
|
IgnoreBossModifiers bool // if true, doesn't get bonuses from boss mods
|
||||||
IgnoreMultishot bool // if true, can't gain the mulitshot modifier
|
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?)
|
// 0 = all units, 1 = undead only, 2 = demons only, 3 = all units (again?)
|
||||||
CanBeSlowed bool // if true, is affected by skill_handofathena
|
CanBeSlowed bool // if true, is affected by skill_handofathena
|
||||||
TriggersHitEvents bool // if true, triggers events that happen "upon getting hit" on targets
|
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
|
TriggersGetHit bool // if true, can cause target to enter hit recovery mode
|
||||||
SoftHit bool // unknown
|
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
|
UseQuantity bool // if true, uses quantity
|
||||||
// not clear what this means. Also apparently requires a special starting function in skills.txt
|
// not clear what this means. Also apparently requires a special starting function in skills.txt
|
||||||
@ -138,56 +191,13 @@ type MissileRecord struct {
|
|||||||
SpecialSetup bool // unknown, only true for potions
|
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
|
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"
|
|
||||||
|
|
||||||
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
|
ApplyMastery bool // unknown
|
||||||
SourceDamage int // 0-128, 128 is 100%
|
|
||||||
// percentage of source units attack properties to apply to the missile?
|
// 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)
|
// 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
|
// 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
|
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 {
|
func createMissileRecord(line string) MissileRecord {
|
||||||
@ -292,9 +302,11 @@ func createMissileRecord(line string) MissileRecord {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Missiles stores all of the MissileRecords
|
||||||
//nolint:gochecknoglobals // Currently global by design, only written once
|
//nolint:gochecknoglobals // Currently global by design, only written once
|
||||||
var Missiles map[int]*MissileRecord
|
var Missiles map[int]*MissileRecord
|
||||||
|
|
||||||
|
// LoadMissiles loads MissileRecords from missiles.txt
|
||||||
func LoadMissiles(file []byte) {
|
func LoadMissiles(file []byte) {
|
||||||
Missiles = make(map[int]*MissileRecord)
|
Missiles = make(map[int]*MissileRecord)
|
||||||
data := strings.Split(string(file), "\r\n")[1:]
|
data := strings.Split(string(file), "\r\n")[1:]
|
||||||
|
@ -6,9 +6,11 @@ import (
|
|||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// MonPresets stores monster presets
|
||||||
//nolint:gochecknoglobals // Currently global by design, only written once
|
//nolint:gochecknoglobals // Currently global by design, only written once
|
||||||
var MonPresets [][]string
|
var MonPresets [][]string
|
||||||
|
|
||||||
|
// LoadMonPresets loads monster presets from monpresets.txt
|
||||||
func LoadMonPresets(file []byte) {
|
func LoadMonPresets(file []byte) {
|
||||||
dict := d2common.LoadDataDictionary(string(file))
|
dict := d2common.LoadDataDictionary(string(file))
|
||||||
numRecords := len(dict.Data)
|
numRecords := len(dict.Data)
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
// d2datadict contains loaders for the txt file data
|
|
||||||
package d2datadict
|
package d2datadict
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -29,7 +28,7 @@ type (
|
|||||||
// column also links other hardcoded effects to the units, such as the
|
// column also links other hardcoded effects to the units, such as the
|
||||||
// transparency on necro summons and the name-color change on unique boss
|
// transparency on necro summons and the name-color change on unique boss
|
||||||
// units (thanks to Kingpin for the info)
|
// 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
|
// 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
|
// 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
|
SpawnAnimationKey string // called `spawnmode` in monstats.txt
|
||||||
|
|
||||||
// MinionId1 is an Id of a minion that spawns when this monster is created
|
// 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
|
MinionId1 string // called `minion1` in monstats.txt
|
||||||
|
|
||||||
// MinionId2 is an Id of a minion that spawns when this monster is created
|
// 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
|
MinionId2 string // called `minion2` in monstats.txt
|
||||||
|
|
||||||
// SoundKeyNormal, SoundKeySpecial
|
// SoundKeyNormal, SoundKeySpecial
|
||||||
@ -114,14 +115,14 @@ type (
|
|||||||
// the ID Pointer to the skill (from Skills.txt) the monster will cast when
|
// 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
|
// this specific slot is accessed by the AI. Which slots are used is
|
||||||
// determined by the units AI.
|
// determined by the units AI.
|
||||||
SkillId1 string // called `Skill1` in monstats.txt
|
SkillId1 string // called `Skill1` in monstats.txt //nolint:golint Id is the right key
|
||||||
SkillId2 string // called `Skill2` in monstats.txt
|
SkillId2 string // called `Skill2` in monstats.txt //nolint:golint Id is the right key
|
||||||
SkillId3 string // called `Skill3` in monstats.txt
|
SkillId3 string // called `Skill3` in monstats.txt //nolint:golint Id is the right key
|
||||||
SkillId4 string // called `Skill4` in monstats.txt
|
SkillId4 string // called `Skill4` in monstats.txt //nolint:golint Id is the right key
|
||||||
SkillId5 string // called `Skill5` in monstats.txt
|
SkillId5 string // called `Skill5` in monstats.txt //nolint:golint Id is the right key
|
||||||
SkillId6 string // called `Skill6` in monstats.txt
|
SkillId6 string // called `Skill6` in monstats.txt //nolint:golint Id is the right key
|
||||||
SkillId7 string // called `Skill7` in monstats.txt
|
SkillId7 string // called `Skill7` in monstats.txt //nolint:golint Id is the right key
|
||||||
SkillId8 string // called `Skill8` in monstats.txt
|
SkillId8 string // called `Skill8` in monstats.txt //nolint:golint Id is the right key
|
||||||
|
|
||||||
// SkillAnimation1 -- SkillAnimation8
|
// SkillAnimation1 -- SkillAnimation8
|
||||||
// the graphical MODE (or SEQUENCE) this unit uses when it uses this skill.
|
// 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
|
// 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
|
// the druids summons. IE their damage is specified solely by Skills.txt and
|
||||||
// not by MonStats.txt.
|
// not by MonStats.txt.
|
||||||
|
//nolint:golint Id is the right key
|
||||||
DamageSkillId string // called `SkillDamage` in monstats.txt
|
DamageSkillId string // called `SkillDamage` in monstats.txt
|
||||||
|
|
||||||
// ElementAttackMode1 -- ElementAttackMode3
|
// ElementAttackMode1 -- ElementAttackMode3
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// MonStats2Record is a representation of a row from monstats2.txt
|
||||||
type MonStats2Record struct {
|
type MonStats2Record struct {
|
||||||
// Key, the object ID MonStatEx feild from MonStat
|
// Key, the object ID MonStatEx feild from MonStat
|
||||||
Key string
|
Key string
|
||||||
@ -225,9 +226,11 @@ type MonStats2Record struct {
|
|||||||
ResurrectSkill string
|
ResurrectSkill string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MonStats2 stores all of the MonStats2Records
|
||||||
//nolint:gochecknoglobals // Current design issue
|
//nolint:gochecknoglobals // Current design issue
|
||||||
var MonStats2 map[string]*MonStats2Record
|
var MonStats2 map[string]*MonStats2Record
|
||||||
|
|
||||||
|
// LoadMonStats2 loads MonStats2Records from monstats2.txt
|
||||||
//nolint:funlen //just a big data loader
|
//nolint:funlen //just a big data loader
|
||||||
func LoadMonStats2(file []byte) {
|
func LoadMonStats2(file []byte) {
|
||||||
dict := d2common.LoadDataDictionary(string(file))
|
dict := d2common.LoadDataDictionary(string(file))
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -2,23 +2,19 @@ package d2datadict
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ObjectType int
|
// ObjectLookupRecord is a representation of a row from objects.txt
|
||||||
|
|
||||||
const (
|
|
||||||
ObjectTypeCharacter ObjectType = 1
|
|
||||||
ObjectTypeItem ObjectType = 2
|
|
||||||
)
|
|
||||||
|
|
||||||
type ObjectLookupRecord struct {
|
type ObjectLookupRecord struct {
|
||||||
Act int
|
Act int
|
||||||
Type ObjectType
|
Type d2enum.ObjectType
|
||||||
Id int
|
Id int //nolint:golint Id is the right key
|
||||||
Name string
|
Name string
|
||||||
Description string
|
Description string
|
||||||
ObjectsTxtId int
|
ObjectsTxtId int //nolint:golint Id is the right key
|
||||||
MonstatsTxtId int
|
MonstatsTxtId int //nolint:golint Id is the right key
|
||||||
Direction int
|
Direction int
|
||||||
Base string
|
Base string
|
||||||
Token string
|
Token string
|
||||||
@ -44,6 +40,7 @@ type ObjectLookupRecord struct {
|
|||||||
Index int
|
Index int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LookupObject looks up an object record
|
||||||
func LookupObject(act, typ, id int) *ObjectLookupRecord {
|
func LookupObject(act, typ, id int) *ObjectLookupRecord {
|
||||||
object := lookupObject(act, typ, id, indexedObjects)
|
object := lookupObject(act, typ, id, indexedObjects)
|
||||||
if object == nil {
|
if object == nil {
|
||||||
@ -54,14 +51,23 @@ func LookupObject(act, typ, id int) *ObjectLookupRecord {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func lookupObject(act, typ, id int, objects [][][]*ObjectLookupRecord) *ObjectLookupRecord {
|
func lookupObject(act, typ, id int, objects [][][]*ObjectLookupRecord) *ObjectLookupRecord {
|
||||||
if objects[act] != nil && objects[act][typ] != nil && objects[act][typ][id] != nil {
|
if len(objects) < act {
|
||||||
return objects[act][typ][id]
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(objects[act]) < typ {
|
||||||
return nil
|
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)
|
indexedObjects = indexObjects(objectLookups)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,7 +94,8 @@ func indexObjects(objects []ObjectLookupRecord) [][][]*ObjectLookupRecord {
|
|||||||
return indexedObjects
|
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.
|
// nil checks should be done for uninitialized values at each level.
|
||||||
// [Act 1-5][Type 1-2][Id 0-855]
|
// [Act 1-5][Type 1-2][Id 0-855]
|
||||||
|
//nolint:gochecknoglobals // Currently global by design
|
||||||
var indexedObjects [][][]*ObjectLookupRecord
|
var indexedObjects [][][]*ObjectLookupRecord
|
||||||
|
@ -3,6 +3,8 @@ package d2datadict
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||||
|
|
||||||
testify "github.com/stretchr/testify/assert"
|
testify "github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -11,28 +13,28 @@ func TestIndexObjects(t *testing.T) {
|
|||||||
assert := testify.New(t)
|
assert := testify.New(t)
|
||||||
|
|
||||||
testObjects := []ObjectLookupRecord{
|
testObjects := []ObjectLookupRecord{
|
||||||
{Act: 1, Type: ObjectTypeCharacter, Id: 0, Description: "Act1CharId0"},
|
{Act: 1, Type: d2enum.ObjectTypeCharacter, Id: 0, Description: "Act1CharId0"},
|
||||||
{Act: 1, Type: ObjectTypeCharacter, Id: 1, Description: "Act1CharId1"},
|
{Act: 1, Type: d2enum.ObjectTypeCharacter, Id: 1, Description: "Act1CharId1"},
|
||||||
{Act: 1, Type: ObjectTypeCharacter, Id: 2, Description: "Act1CharId2"},
|
{Act: 1, Type: d2enum.ObjectTypeCharacter, Id: 2, Description: "Act1CharId2"},
|
||||||
{Act: 1, Type: ObjectTypeCharacter, Id: 3, Description: "Act1CharId3"},
|
{Act: 1, Type: d2enum.ObjectTypeCharacter, Id: 3, Description: "Act1CharId3"},
|
||||||
{Act: 1, Type: ObjectTypeItem, Id: 0, Description: "Act1ItemId0"},
|
{Act: 1, Type: d2enum.ObjectTypeItem, Id: 0, Description: "Act1ItemId0"},
|
||||||
{Act: 1, Type: ObjectTypeItem, Id: 1, Description: "Act1ItemId1"},
|
{Act: 1, Type: d2enum.ObjectTypeItem, Id: 1, Description: "Act1ItemId1"},
|
||||||
{Act: 1, Type: ObjectTypeItem, Id: 2, Description: "Act1ItemId2"},
|
{Act: 1, Type: d2enum.ObjectTypeItem, Id: 2, Description: "Act1ItemId2"},
|
||||||
{Act: 1, Type: ObjectTypeItem, Id: 3, Description: "Act1ItemId3"},
|
{Act: 1, Type: d2enum.ObjectTypeItem, Id: 3, Description: "Act1ItemId3"},
|
||||||
{Act: 2, Type: ObjectTypeCharacter, Id: 0, Description: "Act2CharId0"},
|
{Act: 2, Type: d2enum.ObjectTypeCharacter, Id: 0, Description: "Act2CharId0"},
|
||||||
{Act: 2, Type: ObjectTypeCharacter, Id: 1, Description: "Act2CharId1"},
|
{Act: 2, Type: d2enum.ObjectTypeCharacter, Id: 1, Description: "Act2CharId1"},
|
||||||
{Act: 2, Type: ObjectTypeCharacter, Id: 2, Description: "Act2CharId2"},
|
{Act: 2, Type: d2enum.ObjectTypeCharacter, Id: 2, Description: "Act2CharId2"},
|
||||||
{Act: 2, Type: ObjectTypeCharacter, Id: 3, Description: "Act2CharId3"},
|
{Act: 2, Type: d2enum.ObjectTypeCharacter, Id: 3, Description: "Act2CharId3"},
|
||||||
{Act: 2, Type: ObjectTypeItem, Id: 0, Description: "Act2ItemId0"},
|
{Act: 2, Type: d2enum.ObjectTypeItem, Id: 0, Description: "Act2ItemId0"},
|
||||||
{Act: 2, Type: ObjectTypeItem, Id: 1, Description: "Act2ItemId1"},
|
{Act: 2, Type: d2enum.ObjectTypeItem, Id: 1, Description: "Act2ItemId1"},
|
||||||
{Act: 2, Type: ObjectTypeItem, Id: 2, Description: "Act2ItemId2"},
|
{Act: 2, Type: d2enum.ObjectTypeItem, Id: 2, Description: "Act2ItemId2"},
|
||||||
{Act: 2, Type: ObjectTypeItem, Id: 3, Description: "Act2ItemId3"},
|
{Act: 2, Type: d2enum.ObjectTypeItem, Id: 3, Description: "Act2ItemId3"},
|
||||||
}
|
}
|
||||||
|
|
||||||
indexedTestObjects := indexObjects(testObjects)
|
indexedTestObjects := indexObjects(testObjects)
|
||||||
|
|
||||||
typeCharacter := int(ObjectTypeCharacter)
|
typeCharacter := int(d2enum.ObjectTypeCharacter)
|
||||||
typeItem := int(ObjectTypeItem)
|
typeItem := int(d2enum.ObjectTypeItem)
|
||||||
|
|
||||||
assert.Equal("Act1CharId2", lookupObject(1, typeCharacter, 2, indexedTestObjects).Description)
|
assert.Equal("Act1CharId2", lookupObject(1, typeCharacter, 2, indexedTestObjects).Description)
|
||||||
assert.Equal("Act1ItemId0", lookupObject(1, typeItem, 0, indexedTestObjects).Description)
|
assert.Equal("Act1ItemId0", lookupObject(1, typeItem, 0, indexedTestObjects).Description)
|
||||||
|
@ -7,15 +7,17 @@ import (
|
|||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ObjectTypeRecord is a representation of a row from objtype.txt
|
||||||
type ObjectTypeRecord struct {
|
type ObjectTypeRecord struct {
|
||||||
Name string
|
Name string
|
||||||
Token string
|
Token string
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint:gochecknoglobals // Currently global by design, only written once
|
|
||||||
// ObjectTypes contains the name and token for objects
|
// ObjectTypes contains the name and token for objects
|
||||||
|
//nolint:gochecknoglobals // Currently global by design, only written once
|
||||||
var ObjectTypes []ObjectTypeRecord
|
var ObjectTypes []ObjectTypeRecord
|
||||||
|
|
||||||
|
// LoadObjectTypes loads ObjectTypeRecords from objtype.txt
|
||||||
func LoadObjectTypes(objectTypeData []byte) {
|
func LoadObjectTypes(objectTypeData []byte) {
|
||||||
streamReader := d2common.CreateStreamReader(objectTypeData)
|
streamReader := d2common.CreateStreamReader(objectTypeData)
|
||||||
count := streamReader.GetInt32()
|
count := streamReader.GetInt32()
|
||||||
|
@ -9,13 +9,20 @@ import (
|
|||||||
|
|
||||||
// An ObjectRecord represents the settings for one type of object from objects.txt
|
// An ObjectRecord represents the settings for one type of object from objects.txt
|
||||||
type ObjectRecord struct {
|
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
|
Name string
|
||||||
Description string
|
Description string
|
||||||
Id int
|
|
||||||
Token string // refers to what graphics this object uses
|
Token string // refers to what graphics this object uses
|
||||||
|
|
||||||
|
Id int //nolint:golint it's ok that it's called Id
|
||||||
SpawnMax int // unused?
|
SpawnMax int // unused?
|
||||||
Selectable [8]bool // is this mode selectable
|
|
||||||
TrapProbability int // unused
|
TrapProbability int // unused
|
||||||
|
|
||||||
SizeX int
|
SizeX int
|
||||||
@ -26,42 +33,11 @@ type ObjectRecord struct {
|
|||||||
NTgtBX int // unknown
|
NTgtBX int // unknown
|
||||||
NTgtBY 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)
|
Orientation int // unknown (1=sw, 2=nw, 3=se, 4=ne)
|
||||||
Trans int // controls palette mapping
|
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
|
|
||||||
|
|
||||||
XOffset int // in pixels offset
|
XOffset int // in pixels offset
|
||||||
YOffset int
|
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
|
TotalPieces int // selectable DCC components count
|
||||||
SubClass int // subclass of object:
|
SubClass int // subclass of object:
|
||||||
@ -79,21 +55,12 @@ type ObjectRecord struct {
|
|||||||
|
|
||||||
NameOffset int // pixels to offset the name from the animation pivot
|
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
|
OperateRange int // distance object can be used from, might be unused
|
||||||
ShrineFunction int // 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
|
|
||||||
|
|
||||||
Parm [8]int // unknown
|
|
||||||
Act int // what acts this object can appear in (15 = all three)
|
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)
|
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?
|
|
||||||
|
|
||||||
Left int // unknown, clickable bounding box?
|
Left int // unknown, clickable bounding box?
|
||||||
Top int
|
Top int
|
||||||
@ -110,13 +77,47 @@ type ObjectRecord struct {
|
|||||||
ClientFn int // controls special audio-visual functions
|
ClientFn int // controls special audio-visual functions
|
||||||
// (see above todo)
|
// (see above todo)
|
||||||
|
|
||||||
|
// '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)
|
RestoreVirgins bool // if true, only restores unused objects (see Restore)
|
||||||
BlockMissile bool // if true, missiles collide with this
|
BlockMissile bool // if true, missiles collide with this
|
||||||
DrawUnder bool // if true, drawn as a floor tile is
|
DrawUnder bool // if true, drawn as a floor tile is
|
||||||
OpenWarp bool // needs clarification, controls whether highlighting shows
|
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
|
LightRed byte // if lightdiameter is set, rgb of the light
|
||||||
// 0 = it doesn't, rest of modes need to be analyzed
|
LightGreen byte
|
||||||
|
LightBlue byte
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint:funlen // Makes no sense to split
|
//nolint:funlen // Makes no sense to split
|
||||||
@ -335,9 +336,11 @@ func createObjectRecord(props []string) ObjectRecord {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Objects stores all of the ObjectRecords
|
||||||
//nolint:gochecknoglobals // Currently global by design, only written once
|
//nolint:gochecknoglobals // Currently global by design, only written once
|
||||||
var Objects map[int]*ObjectRecord
|
var Objects map[int]*ObjectRecord
|
||||||
|
|
||||||
|
// LoadObjects loads all objects from objects.txt
|
||||||
func LoadObjects(file []byte) {
|
func LoadObjects(file []byte) {
|
||||||
Objects = make(map[int]*ObjectRecord)
|
Objects = make(map[int]*ObjectRecord)
|
||||||
data := strings.Split(string(file), "\r\n")[1:]
|
data := strings.Split(string(file), "\r\n")[1:]
|
||||||
|
@ -75,9 +75,11 @@ func createSoundEntry(soundLine string) SoundEntry {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sounds stores all of the SoundEntries
|
||||||
//nolint:gochecknoglobals // Currently global by design, only written once
|
//nolint:gochecknoglobals // Currently global by design, only written once
|
||||||
var Sounds map[string]SoundEntry
|
var Sounds map[string]SoundEntry
|
||||||
|
|
||||||
|
// LoadSounds loads SoundEntries from sounds.txt
|
||||||
func LoadSounds(file []byte) {
|
func LoadSounds(file []byte) {
|
||||||
Sounds = make(map[string]SoundEntry)
|
Sounds = make(map[string]SoundEntry)
|
||||||
soundData := strings.Split(string(file), "\r\n")[1:]
|
soundData := strings.Split(string(file), "\r\n")[1:]
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
package d2datadict
|
package d2datadict
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
|
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||||
)
|
)
|
||||||
|
|
||||||
// https://d2mods.info/forum/kb/viewarticle?a=162
|
// 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
|
// SuperUnique monsters are boss monsters which always appear at the same places
|
||||||
// and always have the same base special abilities
|
// 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).
|
// 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
|
UTransHell string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SuperUniques stores all of the SuperUniqueRecords
|
||||||
|
//nolint:gochecknoglobals // Currently global by design
|
||||||
var SuperUniques map[string]*SuperUniqueRecord
|
var SuperUniques map[string]*SuperUniqueRecord
|
||||||
|
|
||||||
|
// LoadSuperUniques loads SuperUniqueRecords from superuniques.txt
|
||||||
func LoadSuperUniques(file []byte) {
|
func LoadSuperUniques(file []byte) {
|
||||||
dictionary := d2common.LoadDataDictionary(string(file))
|
dictionary := d2common.LoadDataDictionary(string(file))
|
||||||
SuperUniques = make(map[string]*SuperUniqueRecord, len(dictionary.Data))
|
SuperUniques = make(map[string]*SuperUniqueRecord, len(dictionary.Data))
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// UniqueItemRecord is a representation of a row from uniqueitems.txt
|
||||||
type UniqueItemRecord struct {
|
type UniqueItemRecord struct {
|
||||||
Name string
|
Name string
|
||||||
Version int // 0 = classic pre 1.07, 1 = 1.07-1.11, 100 = expansion
|
Version int // 0 = classic pre 1.07, 1 = 1.07-1.11, 100 = expansion
|
||||||
@ -41,6 +42,7 @@ type UniqueItemRecord struct {
|
|||||||
Properties [12]UniqueItemProperty
|
Properties [12]UniqueItemProperty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UniqueItemProperty is describes a property of a unique item
|
||||||
type UniqueItemProperty struct {
|
type UniqueItemProperty struct {
|
||||||
Property string
|
Property string
|
||||||
Parameter d2common.CalcString // depending on the property, this may be an int (usually), or a 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
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UniqueItems stores all of the UniqueItemRecords
|
||||||
|
//nolint:gochecknoglobals // Currently global by design
|
||||||
var UniqueItems map[string]*UniqueItemRecord
|
var UniqueItems map[string]*UniqueItemRecord
|
||||||
|
|
||||||
|
// LoadUniqueItems loadsUniqueItemRecords fro uniqueitems.txt
|
||||||
func LoadUniqueItems(file []byte) {
|
func LoadUniqueItems(file []byte) {
|
||||||
UniqueItems = make(map[string]*UniqueItemRecord)
|
UniqueItems = make(map[string]*UniqueItemRecord)
|
||||||
data := strings.Split(string(file), "\r\n")[1:]
|
data := strings.Split(string(file), "\r\n")[1:]
|
||||||
|
@ -9,6 +9,6 @@ import (
|
|||||||
var Weapons map[string]*ItemCommonRecord
|
var Weapons map[string]*ItemCommonRecord
|
||||||
|
|
||||||
func LoadWeapons(file []byte) {
|
func LoadWeapons(file []byte) {
|
||||||
Weapons = *LoadCommonItems(file, d2enum.InventoryItemTypeWeapon)
|
Weapons = LoadCommonItems(file, d2enum.InventoryItemTypeWeapon)
|
||||||
log.Printf("Loaded %d weapons", len(Weapons))
|
log.Printf("Loaded %d weapons", len(Weapons))
|
||||||
}
|
}
|
||||||
|
@ -5,9 +5,10 @@ import (
|
|||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Object is a game world object
|
||||||
type Object struct {
|
type Object struct {
|
||||||
Type int
|
Type int
|
||||||
Id int
|
Id int //nolint:golint Id is the right key
|
||||||
X int
|
X int
|
||||||
Y int
|
Y int
|
||||||
Flags 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 {
|
for _, object := range mr.ds1.Objects {
|
||||||
|
|
||||||
switch object.Lookup.Type {
|
switch object.Lookup.Type {
|
||||||
case d2datadict.ObjectTypeCharacter:
|
case d2enum.ObjectTypeCharacter:
|
||||||
if object.Lookup.Base != "" && object.Lookup.Token != "" && object.Lookup.TR != "" {
|
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 := d2mapentity.CreateNPC((tileOffsetX*5)+object.X, (tileOffsetY*5)+object.Y, object.Lookup, 0)
|
||||||
npc.SetPaths(convertPaths(tileOffsetX, tileOffsetY, object.Paths))
|
npc.SetPaths(convertPaths(tileOffsetX, tileOffsetY, object.Paths))
|
||||||
entities = append(entities, npc)
|
entities = append(entities, npc)
|
||||||
}
|
}
|
||||||
case d2datadict.ObjectTypeItem:
|
case d2enum.ObjectTypeItem:
|
||||||
if object.ObjectInfo != nil && object.ObjectInfo.Draw && object.Lookup.Base != "" && object.Lookup.Token != "" {
|
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)
|
entity, err := d2mapentity.CreateObject((tileOffsetX*5)+object.X, (tileOffsetY*5)+object.Y, object.Lookup, d2resource.PaletteUnits)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
2
main.go
2
main.go
@ -449,6 +449,8 @@ func loadDataDict() error {
|
|||||||
{d2resource.SuperUniques, d2datadict.LoadSuperUniques},
|
{d2resource.SuperUniques, d2datadict.LoadSuperUniques},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
d2datadict.InitObjectRecords()
|
||||||
|
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
data, err := d2asset.LoadFile(entry.path)
|
data, err := d2asset.LoadFile(entry.path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user