Various map entity reworks (#439)

* Use integer directions for rotating map entities

* Manage composite directly in npc

* Player manages its own composite

* Split up animation mode types

Players, monsters, objects all have their own types

* Clean up AnimatedEntity

* Rename AnimatedEntity -> Object

* Keep the object txt record on hand in Object
This commit is contained in:
Ziemas 2020-06-24 19:49:13 +02:00 committed by GitHub
parent 6328cd50c4
commit 02605227c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 405 additions and 299 deletions

View File

@ -1,52 +1,60 @@
package d2enum package d2enum
type AnimationMode int type PlayerAnimationMode int
type MonsterAnimationMode int
type ObjectAnimationMode int
const ( const (
AnimationModePlayerDeath AnimationMode = 0 // DT AnimationModePlayerDeath PlayerAnimationMode = iota // DT
AnimationModePlayerNeutral AnimationMode = 1 // NU AnimationModePlayerNeutral // NU
AnimationModePlayerWalk AnimationMode = 2 // WL AnimationModePlayerWalk // WL
AnimationModePlayerRun AnimationMode = 3 // RN AnimationModePlayerRun // RN
AnimationModePlayerGetHit AnimationMode = 4 // GH AnimationModePlayerGetHit // GH
AnimationModePlayerTownNeutral AnimationMode = 5 // TN AnimationModePlayerTownNeutral // TN
AnimationModePlayerTownWalk AnimationMode = 6 // TW AnimationModePlayerTownWalk // TW
AnimationModePlayerAttack1 AnimationMode = 7 // A1 AnimationModePlayerAttack1 // A1
AnimationModePlayerAttack2 AnimationMode = 8 // A2 AnimationModePlayerAttack2 // A2
AnimationModePlayerBlock AnimationMode = 9 // BL AnimationModePlayerBlock // BL
AnimationModePlayerCast AnimationMode = 10 // SC AnimationModePlayerCast // SC
AnimationModePlayerThrow AnimationMode = 11 // TH AnimationModePlayerThrow // TH
AnimationModePlayerKick AnimationMode = 12 // KK AnimationModePlayerKick // KK
AnimationModePlayerSkill1 AnimationMode = 13 // S1 AnimationModePlayerSkill1 // S1
AnimationModePlayerSkill2 AnimationMode = 14 // S2 AnimationModePlayerSkill2 // S2
AnimationModePlayerSkill3 AnimationMode = 15 // S3 AnimationModePlayerSkill3 // S3
AnimationModePlayerSkill4 AnimationMode = 16 // S4 AnimationModePlayerSkill4 // S4
AnimationModePlayerDead AnimationMode = 17 // DD AnimationModePlayerDead // DD
AnimationModePlayerSequence AnimationMode = 18 // GH AnimationModePlayerSequence // GH
AnimationModePlayerKnockBack AnimationMode = 19 // GH AnimationModePlayerKnockBack // GH
AnimationModeMonsterDeath AnimationMode = 20 // DT )
AnimationModeMonsterNeutral AnimationMode = 21 // NU const (
AnimationModeMonsterWalk AnimationMode = 22 // WL AnimationModeMonsterDeath MonsterAnimationMode = iota // DT
AnimationModeMonsterGetHit AnimationMode = 23 // GH AnimationModeMonsterNeutral // NU
AnimationModeMonsterAttack1 AnimationMode = 24 // A1 AnimationModeMonsterWalk // WL
AnimationModeMonsterAttack2 AnimationMode = 25 // A2 AnimationModeMonsterGetHit // GH
AnimationModeMonsterBlock AnimationMode = 26 // BL AnimationModeMonsterAttack1 // A1
AnimationModeMonsterCast AnimationMode = 27 // SC AnimationModeMonsterAttack2 // A2
AnimationModeMonsterSkill1 AnimationMode = 28 // S1 AnimationModeMonsterBlock // BL
AnimationModeMonsterSkill2 AnimationMode = 29 // S2 AnimationModeMonsterCast // SC
AnimationModeMonsterSkill3 AnimationMode = 30 // S3 AnimationModeMonsterSkill1 // S1
AnimationModeMonsterSkill4 AnimationMode = 31 // S4 AnimationModeMonsterSkill2 // S2
AnimationModeMonsterDead AnimationMode = 32 // DD AnimationModeMonsterSkill3 // S3
AnimationModeMonsterKnockback AnimationMode = 33 // GH AnimationModeMonsterSkill4 // S4
AnimationModeMonsterSequence AnimationMode = 34 // xx AnimationModeMonsterDead // DD
AnimationModeMonsterRun AnimationMode = 35 // RN AnimationModeMonsterKnockback // GH
AnimationModeObjectNeutral AnimationMode = 36 // NU AnimationModeMonsterSequence // xx
AnimationModeObjectOperating AnimationMode = 37 // OP AnimationModeMonsterRun // RN
AnimationModeObjectOpened AnimationMode = 38 // ON )
AnimationModeObjectSpecial1 AnimationMode = 39 // S1 const (
AnimationModeObjectSpecial2 AnimationMode = 40 // S2 AnimationModeObjectNeutral ObjectAnimationMode = iota // NU
AnimationModeObjectSpecial3 AnimationMode = 41 // S3 AnimationModeObjectOperating // OP
AnimationModeObjectSpecial4 AnimationMode = 42 // S4 AnimationModeObjectOpened // ON
AnimationModeObjectSpecial5 AnimationMode = 43 // S5 AnimationModeObjectSpecial1 // S1
AnimationModeObjectSpecial2 // S2
AnimationModeObjectSpecial3 // S3
AnimationModeObjectSpecial4 // S4
AnimationModeObjectSpecial5 // S5
) )
//go:generate stringer -linecomment -type AnimationMode //go:generate stringer -linecomment -type PlayerAnimationMode
//go:generate stringer -linecomment -type MonsterAnimationMode
//go:generate stringer -linecomment -type ObjectAnimationMode

View File

@ -1,66 +0,0 @@
// Code generated by "stringer -linecomment -type AnimationMode"; 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[AnimationModePlayerDeath-0]
_ = x[AnimationModePlayerNeutral-1]
_ = x[AnimationModePlayerWalk-2]
_ = x[AnimationModePlayerRun-3]
_ = x[AnimationModePlayerGetHit-4]
_ = x[AnimationModePlayerTownNeutral-5]
_ = x[AnimationModePlayerTownWalk-6]
_ = x[AnimationModePlayerAttack1-7]
_ = x[AnimationModePlayerAttack2-8]
_ = x[AnimationModePlayerBlock-9]
_ = x[AnimationModePlayerCast-10]
_ = x[AnimationModePlayerThrow-11]
_ = x[AnimationModePlayerKick-12]
_ = x[AnimationModePlayerSkill1-13]
_ = x[AnimationModePlayerSkill2-14]
_ = x[AnimationModePlayerSkill3-15]
_ = x[AnimationModePlayerSkill4-16]
_ = x[AnimationModePlayerDead-17]
_ = x[AnimationModePlayerSequence-18]
_ = x[AnimationModePlayerKnockBack-19]
_ = x[AnimationModeMonsterDeath-20]
_ = x[AnimationModeMonsterNeutral-21]
_ = x[AnimationModeMonsterWalk-22]
_ = x[AnimationModeMonsterGetHit-23]
_ = x[AnimationModeMonsterAttack1-24]
_ = x[AnimationModeMonsterAttack2-25]
_ = x[AnimationModeMonsterBlock-26]
_ = x[AnimationModeMonsterCast-27]
_ = x[AnimationModeMonsterSkill1-28]
_ = x[AnimationModeMonsterSkill2-29]
_ = x[AnimationModeMonsterSkill3-30]
_ = x[AnimationModeMonsterSkill4-31]
_ = x[AnimationModeMonsterDead-32]
_ = x[AnimationModeMonsterKnockback-33]
_ = x[AnimationModeMonsterSequence-34]
_ = x[AnimationModeMonsterRun-35]
_ = x[AnimationModeObjectNeutral-36]
_ = x[AnimationModeObjectOperating-37]
_ = x[AnimationModeObjectOpened-38]
_ = x[AnimationModeObjectSpecial1-39]
_ = x[AnimationModeObjectSpecial2-40]
_ = x[AnimationModeObjectSpecial3-41]
_ = x[AnimationModeObjectSpecial4-42]
_ = x[AnimationModeObjectSpecial5-43]
}
const _AnimationMode_name = "DTNUWLRNGHTNTWA1A2BLSCTHKKS1S2S3S4DDGHGHDTNUWLGHA1A2BLSCS1S2S3S4DDGHxxRNNUOPONS1S2S3S4S5"
var _AnimationMode_index = [...]uint8{0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88}
func (i AnimationMode) String() string {
if i < 0 || i >= AnimationMode(len(_AnimationMode_index)-1) {
return "AnimationMode(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _AnimationMode_name[_AnimationMode_index[i]:_AnimationMode_index[i+1]]
}

View File

@ -0,0 +1,38 @@
// Code generated by "stringer -linecomment -type MonsterAnimationMode"; 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[AnimationModeMonsterDeath-0]
_ = x[AnimationModeMonsterNeutral-1]
_ = x[AnimationModeMonsterWalk-2]
_ = x[AnimationModeMonsterGetHit-3]
_ = x[AnimationModeMonsterAttack1-4]
_ = x[AnimationModeMonsterAttack2-5]
_ = x[AnimationModeMonsterBlock-6]
_ = x[AnimationModeMonsterCast-7]
_ = x[AnimationModeMonsterSkill1-8]
_ = x[AnimationModeMonsterSkill2-9]
_ = x[AnimationModeMonsterSkill3-10]
_ = x[AnimationModeMonsterSkill4-11]
_ = x[AnimationModeMonsterDead-12]
_ = x[AnimationModeMonsterKnockback-13]
_ = x[AnimationModeMonsterSequence-14]
_ = x[AnimationModeMonsterRun-15]
}
const _MonsterAnimationMode_name = "DTNUWLGHA1A2BLSCS1S2S3S4DDGHxxRN"
var _MonsterAnimationMode_index = [...]uint8{0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32}
func (i MonsterAnimationMode) String() string {
if i < 0 || i >= MonsterAnimationMode(len(_MonsterAnimationMode_index)-1) {
return "MonsterAnimationMode(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _MonsterAnimationMode_name[_MonsterAnimationMode_index[i]:_MonsterAnimationMode_index[i+1]]
}

View File

@ -0,0 +1,30 @@
// Code generated by "stringer -linecomment -type ObjectAnimationMode"; 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[AnimationModeObjectNeutral-0]
_ = x[AnimationModeObjectOperating-1]
_ = x[AnimationModeObjectOpened-2]
_ = x[AnimationModeObjectSpecial1-3]
_ = x[AnimationModeObjectSpecial2-4]
_ = x[AnimationModeObjectSpecial3-5]
_ = x[AnimationModeObjectSpecial4-6]
_ = x[AnimationModeObjectSpecial5-7]
}
const _ObjectAnimationMode_name = "NUOPONS1S2S3S4S5"
var _ObjectAnimationMode_index = [...]uint8{0, 2, 4, 6, 8, 10, 12, 14, 16}
func (i ObjectAnimationMode) String() string {
if i < 0 || i >= ObjectAnimationMode(len(_ObjectAnimationMode_index)-1) {
return "ObjectAnimationMode(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _ObjectAnimationMode_name[_ObjectAnimationMode_index[i]:_ObjectAnimationMode_index[i+1]]
}

View File

@ -0,0 +1,42 @@
// Code generated by "stringer -linecomment -type PlayerAnimationMode"; 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[AnimationModePlayerDeath-0]
_ = x[AnimationModePlayerNeutral-1]
_ = x[AnimationModePlayerWalk-2]
_ = x[AnimationModePlayerRun-3]
_ = x[AnimationModePlayerGetHit-4]
_ = x[AnimationModePlayerTownNeutral-5]
_ = x[AnimationModePlayerTownWalk-6]
_ = x[AnimationModePlayerAttack1-7]
_ = x[AnimationModePlayerAttack2-8]
_ = x[AnimationModePlayerBlock-9]
_ = x[AnimationModePlayerCast-10]
_ = x[AnimationModePlayerThrow-11]
_ = x[AnimationModePlayerKick-12]
_ = x[AnimationModePlayerSkill1-13]
_ = x[AnimationModePlayerSkill2-14]
_ = x[AnimationModePlayerSkill3-15]
_ = x[AnimationModePlayerSkill4-16]
_ = x[AnimationModePlayerDead-17]
_ = x[AnimationModePlayerSequence-18]
_ = x[AnimationModePlayerKnockBack-19]
}
const _PlayerAnimationMode_name = "DTNUWLRNGHTNTWA1A2BLSCTHKKS1S2S3S4DDGHGH"
var _PlayerAnimationMode_index = [...]uint8{0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40}
func (i PlayerAnimationMode) String() string {
if i < 0 || i >= PlayerAnimationMode(len(_PlayerAnimationMode_index)-1) {
return "PlayerAnimationMode(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _PlayerAnimationMode_name[_PlayerAnimationMode_index[i]:_PlayerAnimationMode_index[i+1]]
}

View File

@ -1,120 +0,0 @@
package d2mapentity
import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
)
// AnimatedComposite represents a composite of animations that can be projected onto the map.
type AnimatedComposite struct {
mapEntity
//animationMode string
composite *d2asset.Composite
direction int
player *Player
objectLookup *d2datadict.ObjectLookupRecord
}
// CreateAnimatedComposite creates an instance of AnimatedComposite
func CreateAnimatedComposite(x, y int, object *d2datadict.ObjectLookupRecord, palettePath string) (*AnimatedComposite, error) {
composite, err := d2asset.LoadComposite(object, palettePath)
if err != nil {
return nil, err
}
entity := &AnimatedComposite{
mapEntity: createMapEntity(x, y),
composite: composite,
objectLookup: object,
}
entity.mapEntity.directioner = entity.rotate
return entity, nil
}
func (ac *AnimatedComposite) SetPlayer(player *Player) {
ac.player = player
}
func (ac *AnimatedComposite) SetAnimationMode(animationMode string) error {
return ac.composite.SetMode(animationMode, ac.weaponClass, ac.direction)
}
// SetMode changes the graphical mode of this animated entity
func (ac *AnimatedComposite) SetMode(animationMode, weaponClass string, direction int) error {
ac.composite.SetMode(animationMode, weaponClass, direction)
ac.direction = direction
ac.weaponClass = weaponClass
err := ac.composite.SetMode(animationMode, weaponClass, direction)
if err != nil {
err = ac.composite.SetMode(animationMode, "HTH", direction)
ac.weaponClass = "HTH"
}
return err
}
// Render draws this animated entity onto the target
func (ac *AnimatedComposite) Render(target d2render.Surface) {
target.PushTranslation(
ac.offsetX+int((ac.subcellX-ac.subcellY)*16),
ac.offsetY+int(((ac.subcellX+ac.subcellY)*8)-5),
)
defer target.Pop()
ac.composite.Render(target)
}
// rotate sets direction and changes animation
func (ac *AnimatedComposite) rotate(angle float64) {
newAnimationMode := ac.GetAnimationMode().String()
newDirection := angleToDirection(angle)
if newAnimationMode != ac.composite.GetAnimationMode() || newDirection != ac.direction {
ac.SetMode(newAnimationMode, ac.weaponClass, newDirection)
}
}
func (ac *AnimatedComposite) GetAnimationMode() d2enum.AnimationMode {
var newAnimationMode d2enum.AnimationMode
if ac.player != nil {
newAnimationMode = ac.GetPlayerAnimationMode()
} else {
newAnimationMode = ac.GetMonsterAnimationMode()
}
return newAnimationMode
}
func (ac *AnimatedComposite) GetPlayerAnimationMode() d2enum.AnimationMode {
if ac.player.IsRunning() && !ac.IsAtTarget(){
return d2enum.AnimationModePlayerRun
}
if ac.player.IsInTown() {
if !ac.IsAtTarget() {
return d2enum.AnimationModePlayerTownWalk
}
return d2enum.AnimationModePlayerTownNeutral
}
if !ac.IsAtTarget() {
return d2enum.AnimationModePlayerWalk
}
return d2enum.AnimationModePlayerNeutral
}
func (ac *AnimatedComposite) GetMonsterAnimationMode() d2enum.AnimationMode {
if !ac.IsAtTarget() {
return d2enum.AnimationModeMonsterWalk
}
return d2enum.AnimationModeMonsterNeutral
}
func (ac *AnimatedComposite) Advance(elapsed float64) {
ac.composite.Advance(elapsed)
}

View File

@ -40,8 +40,8 @@ func (ae *AnimatedEntity) GetDirection() int {
} }
// rotate sets direction and changes animation // rotate sets direction and changes animation
func (ae *AnimatedEntity) rotate(angle float64) { func (ae *AnimatedEntity) rotate(direction int) {
ae.direction = angleToDirection(angle) ae.direction = direction
ae.animation.SetDirection(ae.direction) ae.animation.SetDirection(ae.direction)
} }

View File

@ -28,7 +28,7 @@ type mapEntity struct {
path []d2astar.Pather path []d2astar.Pather
done func() done func()
directioner func(angle float64) directioner func(direction int)
} }
// createMapEntity creates an instance of mapEntity // createMapEntity creates an instance of mapEntity
@ -147,7 +147,7 @@ func (m *mapEntity) SetTarget(tx, ty float64, done func()) {
tx, tx,
ty, ty,
) )
m.directioner(float64(angle)) m.directioner(angleToDirection(float64(angle)))
} }
} }

View File

@ -7,29 +7,50 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
) )
type NPC struct { type NPC struct {
*AnimatedComposite mapEntity
action int composite *d2asset.Composite
HasPaths bool action int
Paths []d2common.Path HasPaths bool
path int Paths []d2common.Path
isDone bool path int
repetitions int isDone bool
repetitions int
direction int
objectLookup *d2datadict.ObjectLookupRecord
} }
func CreateNPC(x, y int, object *d2datadict.ObjectLookupRecord, direction int) *NPC { func CreateNPC(x, y int, object *d2datadict.ObjectLookupRecord, direction int) *NPC {
entity, err := CreateAnimatedComposite(x, y, object, d2resource.PaletteUnits) composite, err := d2asset.LoadComposite(object, d2resource.PaletteUnits)
if err != nil { if err != nil {
panic(err) panic(err)
} }
result := &NPC{AnimatedComposite: entity, HasPaths: false} result := &NPC{
mapEntity: createMapEntity(x, y),
composite: composite,
objectLookup: object,
HasPaths: false,
}
result.SetMode(object.Mode, object.Class, direction) result.SetMode(object.Mode, object.Class, direction)
result.mapEntity.directioner = result.rotate
return result return result
} }
func (v *NPC) Render(target d2render.Surface) {
target.PushTranslation(
v.offsetX+int((v.subcellX-v.subcellY)*16),
v.offsetY+int(((v.subcellX+v.subcellY)*8)-5),
)
defer target.Pop()
v.composite.Render(target)
}
func (v *NPC) Path() d2common.Path { func (v *NPC) Path() d2common.Path {
return v.Paths[v.path] return v.Paths[v.path]
} }
@ -51,7 +72,7 @@ func (v *NPC) SetPaths(paths []d2common.Path) {
func (v *NPC) Advance(tickTime float64) { func (v *NPC) Advance(tickTime float64) {
v.Step(tickTime) v.Step(tickTime)
v.AnimatedComposite.Advance(tickTime) v.composite.Advance(tickTime)
if v.HasPaths && v.wait() { if v.HasPaths && v.wait() {
// If at the target, set target to the next path. // If at the target, set target to the next path.
@ -75,7 +96,7 @@ func (v *NPC) wait() bool {
func (v *NPC) next() { func (v *NPC) next() {
v.isDone = true v.isDone = true
v.repetitions = 3 + rand.Intn(5) v.repetitions = 3 + rand.Intn(5)
newAnimationMode := d2enum.AnimationModeObjectNeutral newAnimationMode := d2enum.AnimationModeMonsterNeutral
// TODO: Figure out what 1-3 are for, 4 is correct. // TODO: Figure out what 1-3 are for, 4 is correct.
switch v.action { switch v.action {
case 1: case 1:
@ -95,3 +116,30 @@ func (v *NPC) next() {
v.SetMode(newAnimationMode.String(), v.weaponClass, v.direction) v.SetMode(newAnimationMode.String(), v.weaponClass, v.direction)
} }
} }
// rotate sets direction and changes animation
func (v *NPC) rotate(direction int) {
var newMode d2enum.MonsterAnimationMode
if !v.IsAtTarget() {
newMode = d2enum.AnimationModeMonsterWalk
} else {
newMode = d2enum.AnimationModeMonsterNeutral
}
if newMode.String() != v.composite.GetAnimationMode() || direction != v.direction {
v.SetMode(newMode.String(), v.weaponClass, direction)
}
}
// SetMode changes the graphical mode of this animated entity
func (v *NPC) SetMode(animationMode, weaponClass string, direction int) error {
v.direction = direction
v.weaponClass = weaponClass
err := v.composite.SetMode(animationMode, weaponClass, direction)
if err != nil {
err = v.composite.SetMode(animationMode, "HTH", direction)
v.weaponClass = "HTH"
}
return err
}

View File

@ -0,0 +1,66 @@
package d2mapentity
import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
)
// Object represents a composite of animations that can be projected onto the map.
type Object struct {
mapEntity
composite *d2asset.Composite
direction int
objectRecord *d2datadict.ObjectRecord
objectLookup *d2datadict.ObjectLookupRecord
}
// CreateObject creates an instance of AnimatedComposite
func CreateObject(x, y int, object *d2datadict.ObjectLookupRecord, palettePath string) (*Object, error) {
composite, err := d2asset.LoadComposite(object, palettePath)
if err != nil {
return nil, err
}
entity := &Object{
mapEntity: createMapEntity(x, y),
composite: composite,
objectLookup: object,
}
entity.mapEntity.directioner = entity.rotate
entity.objectRecord = d2datadict.Objects[object.ObjectsTxtId]
return entity, nil
}
// SetMode changes the graphical mode of this animated entity
func (ob *Object) SetMode(animationMode, weaponClass string, direction int) error {
ob.composite.SetMode(animationMode, weaponClass, direction)
ob.direction = direction
ob.weaponClass = weaponClass
err := ob.composite.SetMode(animationMode, weaponClass, direction)
if err != nil {
err = ob.composite.SetMode(animationMode, "HTH", direction)
ob.weaponClass = "HTH"
}
return err
}
// Render draws this animated entity onto the target
func (ob *Object) Render(target d2render.Surface) {
target.PushTranslation(
ob.offsetX+int((ob.subcellX-ob.subcellY)*16),
ob.offsetY+int(((ob.subcellX+ob.subcellY)*8)-5),
)
defer target.Pop()
ob.composite.Render(target)
}
// rotate sets direction and changes animation
func (ob *Object) rotate(direction int) {
}
func (ob *Object) Advance(elapsed float64) {
ob.composite.Advance(elapsed)
}

View File

@ -6,13 +6,15 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2inventory" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2inventory"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui"
) )
type Player struct { type Player struct {
*AnimatedComposite mapEntity
composite *d2asset.Composite
Equipment d2inventory.CharacterEquipment Equipment d2inventory.CharacterEquipment
Id string Id string
direction int direction int
@ -46,27 +48,28 @@ func CreatePlayer(id, name string, x, y int, direction int, heroType d2enum.Hero
LH: equipment.LeftHand.GetItemCode(), LH: equipment.LeftHand.GetItemCode(),
} }
entity, err := CreateAnimatedComposite(x, y, object, d2resource.PaletteUnits) composite, err := d2asset.LoadComposite(object, d2resource.PaletteUnits)
if err != nil { if err != nil {
panic(err) panic(err)
} }
entity.SetSpeed(baseRunSpeed)
result := &Player{ result := &Player{
Id: id, Id: id,
AnimatedComposite: entity, mapEntity: createMapEntity(x, y),
Equipment: equipment, composite: composite,
direction: direction, Equipment: equipment,
Name: name, direction: direction,
nameLabel: d2ui.CreateLabel(d2resource.FontFormal11, d2resource.PaletteStatic), Name: name,
isRunToggled: true, nameLabel: d2ui.CreateLabel(d2resource.FontFormal11, d2resource.PaletteStatic),
isInTown: true, isRunToggled: true,
isRunning: true, isInTown: true,
isRunning: true,
} }
result.SetSpeed(baseRunSpeed)
result.mapEntity.directioner = result.rotate
result.nameLabel.Alignment = d2ui.LabelAlignCenter result.nameLabel.Alignment = d2ui.LabelAlignCenter
result.nameLabel.SetText(name) result.nameLabel.SetText(name)
result.nameLabel.Color = color.White result.nameLabel.Color = color.White
result.SetPlayer(result)
err = result.SetMode(d2enum.AnimationModePlayerTownNeutral.String(), equipment.RightHand.GetWeaponClass(), direction) err = result.SetMode(d2enum.AnimationModePlayerTownNeutral.String(), equipment.RightHand.GetWeaponClass(), direction)
if err != nil { if err != nil {
panic(err) panic(err)
@ -106,25 +109,71 @@ func (p Player) IsInTown() bool {
func (v *Player) Advance(tickTime float64) { func (v *Player) Advance(tickTime float64) {
v.Step(tickTime) v.Step(tickTime)
v.AnimatedComposite.Advance(tickTime) v.composite.Advance(tickTime)
if v.lastPathSize != len(v.path) { if v.lastPathSize != len(v.path) {
v.lastPathSize = len(v.path) v.lastPathSize = len(v.path)
} }
if v.AnimatedComposite.composite.GetAnimationMode() != v.animationMode { if v.composite.GetAnimationMode() != v.animationMode {
v.animationMode = v.AnimatedComposite.composite.GetAnimationMode() v.animationMode = v.composite.GetAnimationMode()
} }
} }
func (v *Player) Render(target d2render.Surface) { func (v *Player) Render(target d2render.Surface) {
v.AnimatedComposite.Render(target) target.PushTranslation(
offX := v.AnimatedComposite.offsetX + int((v.AnimatedComposite.subcellX-v.AnimatedComposite.subcellY)*16) v.offsetX+int((v.subcellX-v.subcellY)*16),
offY := v.AnimatedComposite.offsetY + int(((v.AnimatedComposite.subcellX+v.AnimatedComposite.subcellY)*8)-5) v.offsetY+int(((v.subcellX+v.subcellY)*8)-5),
v.nameLabel.X = offX )
v.nameLabel.Y = offY - 100 defer target.Pop()
v.composite.Render(target)
v.nameLabel.X = v.offsetX
v.nameLabel.Y = v.offsetY - 100
v.nameLabel.Render(target) v.nameLabel.Render(target)
} }
func (v *Player) GetPosition() (float64, float64) { func (v *Player) SetMode(animationMode, weaponClass string, direction int) error {
return v.AnimatedComposite.GetPosition() v.composite.SetMode(animationMode, weaponClass, direction)
v.direction = direction
v.weaponClass = weaponClass
err := v.composite.SetMode(animationMode, weaponClass, direction)
if err != nil {
err = v.composite.SetMode(animationMode, "HTH", direction)
v.weaponClass = "HTH"
}
return err
}
func (v *Player) GetAnimationMode() d2enum.PlayerAnimationMode {
if v.IsRunning() && !v.IsAtTarget() {
return d2enum.AnimationModePlayerRun
}
if v.IsInTown() {
if !v.IsAtTarget() {
return d2enum.AnimationModePlayerTownWalk
}
return d2enum.AnimationModePlayerTownNeutral
}
if !v.IsAtTarget() {
return d2enum.AnimationModePlayerWalk
}
return d2enum.AnimationModePlayerNeutral
}
func (v *Player) SetAnimationMode(animationMode string) error {
return v.composite.SetMode(animationMode, v.weaponClass, v.direction)
}
// rotate sets direction and changes animation
func (v *Player) rotate(direction int) {
newAnimationMode := v.GetAnimationMode().String()
if newAnimationMode != v.composite.GetAnimationMode() || direction != v.direction {
v.SetMode(newAnimationMode, v.weaponClass, direction)
}
} }

View File

@ -126,7 +126,7 @@ func (mr *Stamp) Entities(tileOffsetX, tileOffsetY int) []d2mapentity.MapEntity
} }
case d2datadict.ObjectTypeItem: case d2datadict.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.CreateAnimatedComposite((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 {
panic(err) panic(err)
} }

View File

@ -246,7 +246,7 @@ func (v *CharacterSelect) OnMouseButtonDown(event d2input.MouseEvent) bool {
func (v *CharacterSelect) Advance(tickTime float64) error { func (v *CharacterSelect) Advance(tickTime float64) error {
for _, hero := range v.characterImage { for _, hero := range v.characterImage {
if hero != nil { if hero != nil {
hero.AnimatedComposite.Advance(tickTime) hero.Advance(tickTime)
} }
} }

View File

@ -118,14 +118,14 @@ func (v *Game) Advance(tickTime float64) error {
// Update the camera to focus on the player // Update the camera to focus on the player
if v.localPlayer != nil && !v.gameControls.FreeCam { if v.localPlayer != nil && !v.gameControls.FreeCam {
rx, ry := v.mapRenderer.WorldToOrtho(v.localPlayer.AnimatedComposite.LocationX/5, v.localPlayer.AnimatedComposite.LocationY/5) rx, ry := v.mapRenderer.WorldToOrtho(v.localPlayer.LocationX/5, v.localPlayer.LocationY/5)
v.mapRenderer.MoveCameraTo(rx, ry) v.mapRenderer.MoveCameraTo(rx, ry)
} }
return nil return nil
} }
func (v *Game) OnPlayerMove(x, y float64) { func (v *Game) OnPlayerMove(x, y float64) {
heroPosX := v.localPlayer.AnimatedComposite.LocationX / 5.0 heroPosX := v.localPlayer.LocationX / 5.0
heroPosY := v.localPlayer.AnimatedComposite.LocationY / 5.0 heroPosY := v.localPlayer.LocationY / 5.0
v.gameClient.SendPacketToServer(d2netpacket.CreateMovePlayerPacket(v.gameClient.PlayerId, heroPosX, heroPosY, x, y)) v.gameClient.SendPacketToServer(d2netpacket.CreateMovePlayerPacket(v.gameClient.PlayerId, heroPosX, heroPosY, x, y))
} }

View File

@ -49,7 +49,10 @@ type GameControls struct {
} }
type ActionableType int type ActionableType int
type ActionableRegion struct { ActionableTypeId ActionableType; Rect d2common.Rectangle } type ActionableRegion struct {
ActionableTypeId ActionableType
Rect d2common.Rectangle
}
const ( const (
// Since they require special handling, not considering (1) globes, (2) content of the mini panel, (3) belt // Since they require special handling, not considering (1) globes, (2) content of the mini panel, (3) belt
@ -81,15 +84,15 @@ func NewGameControls(hero *d2mapentity.Player, mapEngine *d2mapengine.MapEngine,
heroStats: NewHeroStats(), heroStats: NewHeroStats(),
escapeMenu: NewEscapeMenu(), escapeMenu: NewEscapeMenu(),
zoneChangeText: &label, zoneChangeText: &label,
actionableRegions: []ActionableRegion { actionableRegions: []ActionableRegion{
{leftSkill, d2common.Rectangle{Left:115, Top:550, Width:50, Height:50 }}, {leftSkill, d2common.Rectangle{Left: 115, Top: 550, Width: 50, Height: 50}},
{leftSelec, d2common.Rectangle{Left:206, Top:563, Width:30, Height:30 }}, {leftSelec, d2common.Rectangle{Left: 206, Top: 563, Width: 30, Height: 30}},
{xp, d2common.Rectangle{Left:253, Top:560, Width:125, Height:5 }}, {xp, d2common.Rectangle{Left: 253, Top: 560, Width: 125, Height: 5}},
{walkRun, d2common.Rectangle{Left:255, Top:573, Width:17, Height:20 }}, {walkRun, d2common.Rectangle{Left: 255, Top: 573, Width: 17, Height: 20}},
{stamina, d2common.Rectangle{Left:273, Top:573, Width:105, Height:20 }}, {stamina, d2common.Rectangle{Left: 273, Top: 573, Width: 105, Height: 20}},
{miniPanel, d2common.Rectangle{Left:393, Top:563, Width:12, Height:23 }}, {miniPanel, d2common.Rectangle{Left: 393, Top: 563, Width: 12, Height: 23}},
{rightSelec,d2common.Rectangle{Left:562, Top:563, Width:30, Height:30 }}, {rightSelec, d2common.Rectangle{Left: 562, Top: 563, Width: 30, Height: 30}},
{rightSkill,d2common.Rectangle{Left:634, Top:550, Width:50, Height:50 }}, {rightSkill, d2common.Rectangle{Left: 634, Top: 550, Width: 50, Height: 50}},
}, },
} }
@ -239,8 +242,8 @@ func (g *GameControls) OnMouseButtonDown(event d2input.MouseEvent) bool {
func (g *GameControls) ShootMissile(px float64, py float64) bool { func (g *GameControls) ShootMissile(px float64, py float64) bool {
missile, err := d2mapentity.CreateMissile( missile, err := d2mapentity.CreateMissile(
int(g.hero.AnimatedComposite.LocationX), int(g.hero.LocationX),
int(g.hero.AnimatedComposite.LocationY), int(g.hero.LocationY),
d2datadict.Missiles[missileID], d2datadict.Missiles[missileID],
) )
if err != nil { if err != nil {
@ -248,8 +251,8 @@ func (g *GameControls) ShootMissile(px float64, py float64) bool {
} }
rads := d2common.GetRadiansBetween( rads := d2common.GetRadiansBetween(
g.hero.AnimatedComposite.LocationX, g.hero.LocationX,
g.hero.AnimatedComposite.LocationY, g.hero.LocationY,
px*5, px*5,
py*5, py*5,
) )
@ -434,14 +437,22 @@ func (g *GameControls) onHoverActionable(item ActionableType) {
// Handles what to do when an actionable is clicked // Handles what to do when an actionable is clicked
func (g *GameControls) onClickActionable(item ActionableType) { func (g *GameControls) onClickActionable(item ActionableType) {
switch item { switch item {
case leftSkill: log.Println("Left Skill Action Pressed") case leftSkill:
case leftSelec: log.Println("Left Skill Selector Action Pressed") log.Println("Left Skill Action Pressed")
case xp: log.Println("XP Action Pressed") case leftSelec:
case walkRun: log.Println("Walk/Run Action Pressed") log.Println("Left Skill Selector Action Pressed")
case stamina: log.Println("Stamina Action Pressed") case xp:
case miniPanel: log.Println("Mini Panel Action Pressed") log.Println("XP Action Pressed")
case rightSelec: log.Println("Right Skill Selector Action Pressed") case walkRun:
case rightSkill: log.Println("Right Skill Action Pressed") log.Println("Walk/Run Action Pressed")
case stamina:
log.Println("Stamina Action Pressed")
case miniPanel:
log.Println("Mini Panel Action Pressed")
case rightSelec:
log.Println("Right Skill Selector Action Pressed")
case rightSkill:
log.Println("Right Skill Action Pressed")
default: default:
log.Printf("Unrecognized ActionableType(%d) being clicked\n", item) log.Printf("Unrecognized ActionableType(%d) being clicked\n", item)
} }

View File

@ -88,7 +88,7 @@ func (g *GameClient) OnPacketReceived(packet d2netpacket.NetPacket) error {
player := g.Players[movePlayer.PlayerId] player := g.Players[movePlayer.PlayerId]
path, _, _ := g.MapEngine.PathFind(movePlayer.StartX, movePlayer.StartY, movePlayer.DestX, movePlayer.DestY) path, _, _ := g.MapEngine.PathFind(movePlayer.StartX, movePlayer.StartY, movePlayer.DestX, movePlayer.DestY)
if len(path) > 0 { if len(path) > 0 {
player.AnimatedComposite.SetPath(path, func() { player.SetPath(path, func() {
tile := g.MapEngine.TileAt(player.TileX, player.TileY) tile := g.MapEngine.TileAt(player.TileX, player.TileY)
if tile == nil { if tile == nil {
return return
@ -100,7 +100,7 @@ func (g *GameClient) OnPacketReceived(packet d2netpacket.NetPacket) error {
} else { } else {
player.SetIsInTown(false) player.SetIsInTown(false)
} }
player.AnimatedComposite.SetAnimationMode(player.GetAnimationMode().String()) player.SetAnimationMode(player.GetAnimationMode().String())
}) })
} }
case d2netpackettype.Ping: case d2netpackettype.Ping: