mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2025-01-13 12:56:35 -05:00
Spawn monster by monstat record (#508)
* DS1 reader no longer looks up objects * Start of enteties managing their own equipment * stringer and string2enum CompositeType String2enum * Use CompositeType stringer to simplify composite * Finally fix GetDelimitedList And lint issues * NPC selects random equipment
This commit is contained in:
parent
c27fb572bc
commit
fa20b9ee51
@ -2,7 +2,6 @@ package d2data
|
||||
|
||||
import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
|
||||
)
|
||||
|
||||
// Object is a game world object
|
||||
@ -13,6 +12,4 @@ type Object struct {
|
||||
Y int
|
||||
Flags int
|
||||
Paths []d2common.Path
|
||||
Lookup *d2datadict.ObjectLookupRecord
|
||||
ObjectInfo *d2datadict.ObjectRecord
|
||||
}
|
||||
|
@ -6,53 +6,55 @@ type CompositeType int
|
||||
const (
|
||||
|
||||
// CompositeTypeHead is a composite type for heads
|
||||
CompositeTypeHead CompositeType = iota
|
||||
CompositeTypeHead CompositeType = iota // HD
|
||||
|
||||
// CompositeTypeTorso is a composite type for torsos
|
||||
CompositeTypeTorso
|
||||
CompositeTypeTorso // TR
|
||||
|
||||
// CompositeTypeLegs is a composite type for legs
|
||||
CompositeTypeLegs
|
||||
CompositeTypeLegs // LG
|
||||
|
||||
// CompositeTypeRightArm is a composite type for right arms
|
||||
CompositeTypeRightArm
|
||||
CompositeTypeRightArm // RA
|
||||
|
||||
// CompositeTypeLeftArm is a composite type for left arms
|
||||
CompositeTypeLeftArm
|
||||
CompositeTypeLeftArm // LA
|
||||
|
||||
// CompositeTypeRightHand is a composite type for right hands
|
||||
CompositeTypeRightHand
|
||||
CompositeTypeRightHand // RH
|
||||
|
||||
// CompositeTypeLeftHand is a composite type for left hands
|
||||
CompositeTypeLeftHand
|
||||
CompositeTypeLeftHand // LH
|
||||
|
||||
// CompositeTypeShield is a composite type for shields
|
||||
CompositeTypeShield
|
||||
CompositeTypeShield // SH
|
||||
|
||||
// CompositeTypeSpecial1 is a composite type for special type 1s
|
||||
CompositeTypeSpecial1
|
||||
CompositeTypeSpecial1 // S1
|
||||
|
||||
// CompositeTypeSpecial2 is a composite type for special type 2s
|
||||
CompositeTypeSpecial2
|
||||
CompositeTypeSpecial2 // S2
|
||||
|
||||
// CompositeTypeSpecial3 is a composite type for special type 3s
|
||||
CompositeTypeSpecial3
|
||||
CompositeTypeSpecial3 // S3
|
||||
|
||||
// CompositeTypeSpecial4 is a composite type for special type 4s
|
||||
CompositeTypeSpecial4
|
||||
CompositeTypeSpecial4 // S4
|
||||
|
||||
// CompositeTypeSpecial5 is a composite type for special type 5s
|
||||
CompositeTypeSpecial5
|
||||
CompositeTypeSpecial5 // S5
|
||||
|
||||
// CompositeTypeSpecial6 is a composite type for special type 6s
|
||||
CompositeTypeSpecial6
|
||||
CompositeTypeSpecial6 // S6
|
||||
|
||||
// CompositeTypeSpecial7 is a composite type for special type 7s
|
||||
CompositeTypeSpecial7
|
||||
CompositeTypeSpecial7 // S7
|
||||
|
||||
// CompositeTypeSpecial8 is a composite type for special type 8s
|
||||
CompositeTypeSpecial8
|
||||
CompositeTypeSpecial8 // S8
|
||||
|
||||
// CompositeTypeMax is used to determine the max number of composite types
|
||||
CompositeTypeMax
|
||||
)
|
||||
|
||||
//go:generate stringer -linecomment -type CompositeType
|
||||
|
39
d2common/d2enum/compositetype_string.go
Normal file
39
d2common/d2enum/compositetype_string.go
Normal file
@ -0,0 +1,39 @@
|
||||
// Code generated by "stringer -linecomment -type CompositeType"; DO NOT EDIT.
|
||||
|
||||
package d2enum
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[CompositeTypeHead-0]
|
||||
_ = x[CompositeTypeTorso-1]
|
||||
_ = x[CompositeTypeLegs-2]
|
||||
_ = x[CompositeTypeRightArm-3]
|
||||
_ = x[CompositeTypeLeftArm-4]
|
||||
_ = x[CompositeTypeRightHand-5]
|
||||
_ = x[CompositeTypeLeftHand-6]
|
||||
_ = x[CompositeTypeShield-7]
|
||||
_ = x[CompositeTypeSpecial1-8]
|
||||
_ = x[CompositeTypeSpecial2-9]
|
||||
_ = x[CompositeTypeSpecial3-10]
|
||||
_ = x[CompositeTypeSpecial4-11]
|
||||
_ = x[CompositeTypeSpecial5-12]
|
||||
_ = x[CompositeTypeSpecial6-13]
|
||||
_ = x[CompositeTypeSpecial7-14]
|
||||
_ = x[CompositeTypeSpecial8-15]
|
||||
_ = x[CompositeTypeMax-16]
|
||||
}
|
||||
|
||||
const _CompositeType_name = "HDTRLGRALARHLHSHS1S2S3S4S5S6S7S8CompositeTypeMax"
|
||||
|
||||
var _CompositeType_index = [...]uint8{0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 48}
|
||||
|
||||
func (i CompositeType) String() string {
|
||||
if i < 0 || i >= CompositeType(len(_CompositeType_index)-1) {
|
||||
return "CompositeType(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _CompositeType_name[_CompositeType_index[i]:_CompositeType_index[i+1]]
|
||||
}
|
58
d2common/d2enum/compositetype_string2enum.go
Normal file
58
d2common/d2enum/compositetype_string2enum.go
Normal file
@ -0,0 +1,58 @@
|
||||
// Code generated by "string2enum -samepkg -linecomment -type CompositeType"; DO NOT EDIT.
|
||||
|
||||
package d2enum
|
||||
|
||||
import "fmt"
|
||||
|
||||
// CompositeTypeFromString returns the CompositeType enum corresponding to s.
|
||||
func CompositeTypeFromString(s string) CompositeType {
|
||||
if len(s) == 0 {
|
||||
return 0
|
||||
}
|
||||
for i := range _CompositeType_index[:len(_CompositeType_index)-1] {
|
||||
if s == _CompositeType_name[_CompositeType_index[i]:_CompositeType_index[i+1]] {
|
||||
return CompositeType(i)
|
||||
}
|
||||
}
|
||||
panic(fmt.Errorf("unable to locate CompositeType enum corresponding to %q", s))
|
||||
}
|
||||
|
||||
func _(s string) {
|
||||
// Check for duplicate string values in type "CompositeType".
|
||||
switch s {
|
||||
// 0
|
||||
case "HD":
|
||||
// 1
|
||||
case "TR":
|
||||
// 2
|
||||
case "LG":
|
||||
// 3
|
||||
case "RA":
|
||||
// 4
|
||||
case "LA":
|
||||
// 5
|
||||
case "RH":
|
||||
// 6
|
||||
case "LH":
|
||||
// 7
|
||||
case "SH":
|
||||
// 8
|
||||
case "S1":
|
||||
// 9
|
||||
case "S2":
|
||||
// 10
|
||||
case "S3":
|
||||
// 11
|
||||
case "S4":
|
||||
// 12
|
||||
case "S5":
|
||||
// 13
|
||||
case "S6":
|
||||
// 14
|
||||
case "S7":
|
||||
// 15
|
||||
case "S8":
|
||||
// 16
|
||||
case "CompositeTypeMax":
|
||||
}
|
||||
}
|
@ -4,7 +4,6 @@ package d2ds1
|
||||
import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||
)
|
||||
|
||||
@ -120,15 +119,6 @@ func (ds1 *DS1) loadObjects(br *d2common.StreamReader) {
|
||||
newObject.X = int(br.GetInt32())
|
||||
newObject.Y = int(br.GetInt32())
|
||||
newObject.Flags = int(br.GetInt32())
|
||||
newObject.Lookup = d2datadict.LookupObject(int(ds1.Act), newObject.Type, newObject.Id)
|
||||
|
||||
if newObject.Lookup != nil && newObject.Type == 1 {
|
||||
newObject.Lookup.Name = d2datadict.MonPresets[ds1.Act][newObject.Id]
|
||||
}
|
||||
|
||||
if newObject.Lookup != nil && newObject.Lookup.ObjectsTxtId != -1 {
|
||||
newObject.ObjectInfo = d2datadict.Objects[newObject.Lookup.ObjectsTxtId]
|
||||
}
|
||||
|
||||
ds1.Objects[objIdx] = newObject
|
||||
}
|
||||
|
@ -12,63 +12,70 @@ type DataDictionary struct {
|
||||
Data [][]string
|
||||
}
|
||||
|
||||
// LoadDataDictionary loads the contents of a spreadsheet style txt file
|
||||
func LoadDataDictionary(text string) *DataDictionary {
|
||||
result := &DataDictionary{}
|
||||
lines := strings.Split(text, "\r\n")
|
||||
fileNames := strings.Split(lines[0], "\t")
|
||||
result.FieldNameLookup = make(map[string]int)
|
||||
|
||||
for i, fieldName := range fileNames {
|
||||
result.FieldNameLookup[fieldName] = i
|
||||
}
|
||||
|
||||
result.Data = make([][]string, len(lines)-2)
|
||||
|
||||
for i, line := range lines[1:] {
|
||||
if len(strings.TrimSpace(line)) == 0 {
|
||||
if strings.TrimSpace(line) == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
values := strings.Split(line, "\t")
|
||||
if len(values) != len(result.FieldNameLookup) {
|
||||
continue
|
||||
}
|
||||
|
||||
result.Data[i] = values
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// GetString gets a string from the given column and row
|
||||
func (v *DataDictionary) GetString(fieldName string, index int) string {
|
||||
return v.Data[index][v.FieldNameLookup[fieldName]]
|
||||
}
|
||||
|
||||
// GetNumber gets a number for the given column and row
|
||||
func (v *DataDictionary) GetNumber(fieldName string, index int) int {
|
||||
str := v.GetString(fieldName, index)
|
||||
str = EmptyToZero(AsterToEmpty(str))
|
||||
|
||||
result, err := strconv.Atoi(str)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// GetDelimitedList splits a delimited list from the given column and row
|
||||
func (v *DataDictionary) GetDelimitedList(fieldName string, index int) []string {
|
||||
unsplit := v.GetString(fieldName, index)
|
||||
|
||||
// Commo delimited fields are quoted, not terribly pretty to do it here but...
|
||||
s := []byte(unsplit)
|
||||
j := 0
|
||||
// Comma delimited fields are quoted, not terribly pretty to fix that here but...
|
||||
unsplit = strings.TrimRight(unsplit, "\"")
|
||||
unsplit = strings.TrimLeft(unsplit, "\"")
|
||||
|
||||
for i := range s {
|
||||
if s[i] != '"' {
|
||||
s[j] = s[i]
|
||||
j++
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Split(string(s), ",")
|
||||
return strings.Split(unsplit, ",")
|
||||
}
|
||||
|
||||
// GetBool gets a bool value for the given column and row
|
||||
func (v *DataDictionary) GetBool(fieldName string, index int) bool {
|
||||
n := v.GetNumber(fieldName, index)
|
||||
if n > 1 {
|
||||
log.Panic("GetBool on non-bool field")
|
||||
}
|
||||
|
||||
return n == 1
|
||||
}
|
||||
|
@ -171,55 +171,40 @@ func (c *Composite) createMode(animationMode, weaponClass string, direction int)
|
||||
}
|
||||
|
||||
for _, cofLayer := range cof.CofLayers {
|
||||
var layerKey, layerValue string
|
||||
var layerValue string
|
||||
|
||||
switch cofLayer.Type {
|
||||
case d2enum.CompositeTypeHead:
|
||||
layerKey = "HD"
|
||||
layerValue = c.object.HD
|
||||
case d2enum.CompositeTypeTorso:
|
||||
layerKey = "TR"
|
||||
layerValue = c.object.TR
|
||||
case d2enum.CompositeTypeLegs:
|
||||
layerKey = "LG"
|
||||
layerValue = c.object.LG
|
||||
case d2enum.CompositeTypeRightArm:
|
||||
layerKey = "RA"
|
||||
layerValue = c.object.RA
|
||||
case d2enum.CompositeTypeLeftArm:
|
||||
layerKey = "LA"
|
||||
layerValue = c.object.LA
|
||||
case d2enum.CompositeTypeRightHand:
|
||||
layerKey = "RH"
|
||||
layerValue = c.object.RH
|
||||
case d2enum.CompositeTypeLeftHand:
|
||||
layerKey = "LH"
|
||||
layerValue = c.object.LH
|
||||
case d2enum.CompositeTypeShield:
|
||||
layerKey = "SH"
|
||||
layerValue = c.object.SH
|
||||
case d2enum.CompositeTypeSpecial1:
|
||||
layerKey = "S1"
|
||||
layerValue = c.object.S1
|
||||
case d2enum.CompositeTypeSpecial2:
|
||||
layerKey = "S2"
|
||||
layerValue = c.object.S2
|
||||
case d2enum.CompositeTypeSpecial3:
|
||||
layerKey = "S3"
|
||||
layerValue = c.object.S3
|
||||
case d2enum.CompositeTypeSpecial4:
|
||||
layerKey = "S4"
|
||||
layerValue = c.object.S4
|
||||
case d2enum.CompositeTypeSpecial5:
|
||||
layerKey = "S5"
|
||||
layerValue = c.object.S5
|
||||
case d2enum.CompositeTypeSpecial6:
|
||||
layerKey = "S6"
|
||||
layerValue = c.object.S6
|
||||
case d2enum.CompositeTypeSpecial7:
|
||||
layerKey = "S7"
|
||||
layerValue = c.object.S7
|
||||
case d2enum.CompositeTypeSpecial8:
|
||||
layerKey = "S8"
|
||||
layerValue = c.object.S8
|
||||
default:
|
||||
return nil, errors.New("unknown layer type")
|
||||
@ -240,7 +225,7 @@ func (c *Composite) createMode(animationMode, weaponClass string, direction int)
|
||||
}
|
||||
}
|
||||
|
||||
layer, err := loadCompositeLayer(c.object, layerKey, layerValue, animationMode, weaponClass, c.palettePath, transparency)
|
||||
layer, err := loadCompositeLayer(c.object, cofLayer.Type.String(), layerValue, animationMode, weaponClass, c.palettePath, transparency)
|
||||
if err == nil {
|
||||
layer.SetPlaySpeed(mode.animationSpeed)
|
||||
layer.PlayForward()
|
||||
|
@ -24,24 +24,42 @@ type NPC struct {
|
||||
direction int
|
||||
objectLookup *d2datadict.ObjectLookupRecord
|
||||
monstatRecord *d2datadict.MonStatsRecord
|
||||
monstatEx *d2datadict.MonStats2Record
|
||||
name string
|
||||
}
|
||||
|
||||
func CreateNPC(x, y int, object *d2datadict.ObjectLookupRecord, direction int) *NPC {
|
||||
func CreateNPC(x, y int, monstat *d2datadict.MonStatsRecord, direction int) *NPC {
|
||||
result := &NPC{
|
||||
mapEntity: createMapEntity(x, y),
|
||||
HasPaths: false,
|
||||
monstatRecord: monstat,
|
||||
monstatEx: d2datadict.MonStats2[monstat.ExtraDataKey],
|
||||
}
|
||||
|
||||
object := &d2datadict.ObjectLookupRecord{
|
||||
Base: "/Data/Global/Monsters",
|
||||
Token: monstat.AnimationDirectoryToken,
|
||||
Mode: result.monstatEx.ResurrectMode.String(),
|
||||
Class: result.monstatEx.BaseWeaponClass,
|
||||
TR: selectEquip(result.monstatEx.TRv),
|
||||
LG: selectEquip(result.monstatEx.LGv),
|
||||
RH: selectEquip(result.monstatEx.RHv),
|
||||
SH: selectEquip(result.monstatEx.SHv),
|
||||
RA: selectEquip(result.monstatEx.Rav),
|
||||
LA: selectEquip(result.monstatEx.Lav),
|
||||
LH: selectEquip(result.monstatEx.LHv),
|
||||
HD: selectEquip(result.monstatEx.HDv),
|
||||
}
|
||||
result.objectLookup = object
|
||||
composite, err := d2asset.LoadComposite(object, d2resource.PaletteUnits)
|
||||
result.composite = composite
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
result := &NPC{
|
||||
mapEntity: createMapEntity(x, y),
|
||||
composite: composite,
|
||||
objectLookup: object,
|
||||
HasPaths: false,
|
||||
}
|
||||
result.SetMode(object.Mode, object.Class, direction)
|
||||
result.mapEntity.directioner = result.rotate
|
||||
result.monstatRecord = d2datadict.MonStats[object.Name]
|
||||
|
||||
if result.monstatRecord != nil && result.monstatRecord.IsInteractable {
|
||||
result.name = d2common.TranslateString(result.monstatRecord.NameStringTableKey)
|
||||
@ -50,6 +68,15 @@ func CreateNPC(x, y int, object *d2datadict.ObjectLookupRecord, direction int) *
|
||||
return result
|
||||
}
|
||||
|
||||
func selectEquip(slice []string) string {
|
||||
if len(slice) != 0 {
|
||||
return slice[rand.Intn(len(slice))]
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
|
||||
func (v *NPC) Render(target d2interface.Surface) {
|
||||
target.PushTranslation(
|
||||
v.offsetX+int((v.subcellX-v.subcellY)*16),
|
||||
|
@ -37,6 +37,8 @@ func CreateObject(x, y int, object *d2datadict.ObjectLookupRecord, palettePath s
|
||||
entity.mapEntity.directioner = entity.rotate
|
||||
entity.objectRecord = d2datadict.Objects[object.ObjectsTxtId]
|
||||
entity.drawLayer = entity.objectRecord.OrderFlag[d2enum.AnimationModeObjectNeutral]
|
||||
entity.SetMode(object.Mode, object.Class, 0)
|
||||
|
||||
return entity, nil
|
||||
}
|
||||
|
||||
|
@ -6,13 +6,12 @@ import (
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapentity"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2ds1"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dt1"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
|
||||
)
|
||||
|
||||
@ -116,21 +115,42 @@ func (mr *Stamp) Entities(tileOffsetX, tileOffsetY int) []d2mapentity.MapEntity
|
||||
entities := make([]d2mapentity.MapEntity, 0)
|
||||
|
||||
for _, object := range mr.ds1.Objects {
|
||||
|
||||
switch object.Lookup.Type {
|
||||
case d2enum.ObjectTypeCharacter:
|
||||
if object.Lookup.Base != "" && object.Lookup.Token != "" && object.Lookup.TR != "" {
|
||||
npc := d2mapentity.CreateNPC((tileOffsetX*5)+object.X, (tileOffsetY*5)+object.Y, object.Lookup, 0)
|
||||
if object.Type == int(d2enum.ObjectTypeCharacter) {
|
||||
monstat := d2datadict.MonStats[d2datadict.MonPresets[mr.ds1.Act][object.Id]]
|
||||
// If monstat is nil here it is a place_ type object, idk how to handle those yet.
|
||||
// (See monpreset and monplace txts for reference)
|
||||
if monstat != nil {
|
||||
// Temorary use of Lookup.
|
||||
npc := d2mapentity.CreateNPC((tileOffsetX*5)+object.X, (tileOffsetY*5)+object.Y, monstat, 0)
|
||||
npc.SetPaths(convertPaths(tileOffsetX, tileOffsetY, object.Paths))
|
||||
entities = append(entities, npc)
|
||||
}
|
||||
case d2enum.ObjectTypeItem:
|
||||
if object.ObjectInfo != nil && object.ObjectInfo.Draw && object.Lookup.Base != "" && object.Lookup.Token != "" {
|
||||
entity, err := d2mapentity.CreateObject((tileOffsetX*5)+object.X, (tileOffsetY*5)+object.Y, object.Lookup, d2resource.PaletteUnits)
|
||||
}
|
||||
|
||||
if object.Type == int(d2enum.ObjectTypeItem) {
|
||||
// For objects the DS1 ID to objectID is hardcoded in the game
|
||||
// use the lookup table
|
||||
lookup := d2datadict.LookupObject(int(mr.ds1.Act), object.Type, object.Id)
|
||||
|
||||
if lookup == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
objectRecord := d2datadict.Objects[lookup.ObjectsTxtId]
|
||||
|
||||
if objectRecord != nil {
|
||||
// The lookup is used deeper in for crap without checking other sources :(
|
||||
// Bail out here for now
|
||||
if !objectRecord.Draw || lookup.Base == "" || objectRecord.Token == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
entity, err := d2mapentity.CreateObject((tileOffsetX*5)+object.X, (tileOffsetY*5)+object.Y, lookup, d2resource.PaletteUnits)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
entity.SetMode(object.Lookup.Mode, object.Lookup.Class, 0)
|
||||
|
||||
entities = append(entities, entity)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user