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
type AnimationMode int
type PlayerAnimationMode int
type MonsterAnimationMode int
type ObjectAnimationMode int
const (
AnimationModePlayerDeath AnimationMode = 0 // DT
AnimationModePlayerNeutral AnimationMode = 1 // NU
AnimationModePlayerWalk AnimationMode = 2 // WL
AnimationModePlayerRun AnimationMode = 3 // RN
AnimationModePlayerGetHit AnimationMode = 4 // GH
AnimationModePlayerTownNeutral AnimationMode = 5 // TN
AnimationModePlayerTownWalk AnimationMode = 6 // TW
AnimationModePlayerAttack1 AnimationMode = 7 // A1
AnimationModePlayerAttack2 AnimationMode = 8 // A2
AnimationModePlayerBlock AnimationMode = 9 // BL
AnimationModePlayerCast AnimationMode = 10 // SC
AnimationModePlayerThrow AnimationMode = 11 // TH
AnimationModePlayerKick AnimationMode = 12 // KK
AnimationModePlayerSkill1 AnimationMode = 13 // S1
AnimationModePlayerSkill2 AnimationMode = 14 // S2
AnimationModePlayerSkill3 AnimationMode = 15 // S3
AnimationModePlayerSkill4 AnimationMode = 16 // S4
AnimationModePlayerDead AnimationMode = 17 // DD
AnimationModePlayerSequence AnimationMode = 18 // GH
AnimationModePlayerKnockBack AnimationMode = 19 // GH
AnimationModeMonsterDeath AnimationMode = 20 // DT
AnimationModeMonsterNeutral AnimationMode = 21 // NU
AnimationModeMonsterWalk AnimationMode = 22 // WL
AnimationModeMonsterGetHit AnimationMode = 23 // GH
AnimationModeMonsterAttack1 AnimationMode = 24 // A1
AnimationModeMonsterAttack2 AnimationMode = 25 // A2
AnimationModeMonsterBlock AnimationMode = 26 // BL
AnimationModeMonsterCast AnimationMode = 27 // SC
AnimationModeMonsterSkill1 AnimationMode = 28 // S1
AnimationModeMonsterSkill2 AnimationMode = 29 // S2
AnimationModeMonsterSkill3 AnimationMode = 30 // S3
AnimationModeMonsterSkill4 AnimationMode = 31 // S4
AnimationModeMonsterDead AnimationMode = 32 // DD
AnimationModeMonsterKnockback AnimationMode = 33 // GH
AnimationModeMonsterSequence AnimationMode = 34 // xx
AnimationModeMonsterRun AnimationMode = 35 // RN
AnimationModeObjectNeutral AnimationMode = 36 // NU
AnimationModeObjectOperating AnimationMode = 37 // OP
AnimationModeObjectOpened AnimationMode = 38 // ON
AnimationModeObjectSpecial1 AnimationMode = 39 // S1
AnimationModeObjectSpecial2 AnimationMode = 40 // S2
AnimationModeObjectSpecial3 AnimationMode = 41 // S3
AnimationModeObjectSpecial4 AnimationMode = 42 // S4
AnimationModeObjectSpecial5 AnimationMode = 43 // S5
AnimationModePlayerDeath PlayerAnimationMode = iota // DT
AnimationModePlayerNeutral // NU
AnimationModePlayerWalk // WL
AnimationModePlayerRun // RN
AnimationModePlayerGetHit // GH
AnimationModePlayerTownNeutral // TN
AnimationModePlayerTownWalk // TW
AnimationModePlayerAttack1 // A1
AnimationModePlayerAttack2 // A2
AnimationModePlayerBlock // BL
AnimationModePlayerCast // SC
AnimationModePlayerThrow // TH
AnimationModePlayerKick // KK
AnimationModePlayerSkill1 // S1
AnimationModePlayerSkill2 // S2
AnimationModePlayerSkill3 // S3
AnimationModePlayerSkill4 // S4
AnimationModePlayerDead // DD
AnimationModePlayerSequence // GH
AnimationModePlayerKnockBack // GH
)
const (
AnimationModeMonsterDeath MonsterAnimationMode = iota // DT
AnimationModeMonsterNeutral // NU
AnimationModeMonsterWalk // WL
AnimationModeMonsterGetHit // GH
AnimationModeMonsterAttack1 // A1
AnimationModeMonsterAttack2 // A2
AnimationModeMonsterBlock // BL
AnimationModeMonsterCast // SC
AnimationModeMonsterSkill1 // S1
AnimationModeMonsterSkill2 // S2
AnimationModeMonsterSkill3 // S3
AnimationModeMonsterSkill4 // S4
AnimationModeMonsterDead // DD
AnimationModeMonsterKnockback // GH
AnimationModeMonsterSequence // xx
AnimationModeMonsterRun // RN
)
const (
AnimationModeObjectNeutral ObjectAnimationMode = iota // NU
AnimationModeObjectOperating // OP
AnimationModeObjectOpened // ON
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
func (ae *AnimatedEntity) rotate(angle float64) {
ae.direction = angleToDirection(angle)
func (ae *AnimatedEntity) rotate(direction int) {
ae.direction = direction
ae.animation.SetDirection(ae.direction)
}

View File

@ -28,7 +28,7 @@ type mapEntity struct {
path []d2astar.Pather
done func()
directioner func(angle float64)
directioner func(direction int)
}
// createMapEntity creates an instance of mapEntity
@ -147,7 +147,7 @@ func (m *mapEntity) SetTarget(tx, ty float64, done func()) {
tx,
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/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
)
type NPC struct {
*AnimatedComposite
action int
HasPaths bool
Paths []d2common.Path
path int
isDone bool
repetitions int
mapEntity
composite *d2asset.Composite
action int
HasPaths bool
Paths []d2common.Path
path int
isDone bool
repetitions int
direction int
objectLookup *d2datadict.ObjectLookupRecord
}
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 {
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.mapEntity.directioner = result.rotate
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 {
return v.Paths[v.path]
}
@ -51,7 +72,7 @@ func (v *NPC) SetPaths(paths []d2common.Path) {
func (v *NPC) Advance(tickTime float64) {
v.Step(tickTime)
v.AnimatedComposite.Advance(tickTime)
v.composite.Advance(tickTime)
if v.HasPaths && v.wait() {
// If at the target, set target to the next path.
@ -75,7 +96,7 @@ func (v *NPC) wait() bool {
func (v *NPC) next() {
v.isDone = true
v.repetitions = 3 + rand.Intn(5)
newAnimationMode := d2enum.AnimationModeObjectNeutral
newAnimationMode := d2enum.AnimationModeMonsterNeutral
// TODO: Figure out what 1-3 are for, 4 is correct.
switch v.action {
case 1:
@ -95,3 +116,30 @@ func (v *NPC) next() {
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/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2inventory"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui"
)
type Player struct {
*AnimatedComposite
mapEntity
composite *d2asset.Composite
Equipment d2inventory.CharacterEquipment
Id string
direction int
@ -46,27 +48,28 @@ func CreatePlayer(id, name string, x, y int, direction int, heroType d2enum.Hero
LH: equipment.LeftHand.GetItemCode(),
}
entity, err := CreateAnimatedComposite(x, y, object, d2resource.PaletteUnits)
composite, err := d2asset.LoadComposite(object, d2resource.PaletteUnits)
if err != nil {
panic(err)
}
entity.SetSpeed(baseRunSpeed)
result := &Player{
Id: id,
AnimatedComposite: entity,
Equipment: equipment,
direction: direction,
Name: name,
nameLabel: d2ui.CreateLabel(d2resource.FontFormal11, d2resource.PaletteStatic),
isRunToggled: true,
isInTown: true,
isRunning: true,
Id: id,
mapEntity: createMapEntity(x, y),
composite: composite,
Equipment: equipment,
direction: direction,
Name: name,
nameLabel: d2ui.CreateLabel(d2resource.FontFormal11, d2resource.PaletteStatic),
isRunToggled: true,
isInTown: true,
isRunning: true,
}
result.SetSpeed(baseRunSpeed)
result.mapEntity.directioner = result.rotate
result.nameLabel.Alignment = d2ui.LabelAlignCenter
result.nameLabel.SetText(name)
result.nameLabel.Color = color.White
result.SetPlayer(result)
err = result.SetMode(d2enum.AnimationModePlayerTownNeutral.String(), equipment.RightHand.GetWeaponClass(), direction)
if err != nil {
panic(err)
@ -106,25 +109,71 @@ func (p Player) IsInTown() bool {
func (v *Player) Advance(tickTime float64) {
v.Step(tickTime)
v.AnimatedComposite.Advance(tickTime)
v.composite.Advance(tickTime)
if v.lastPathSize != len(v.path) {
v.lastPathSize = len(v.path)
}
if v.AnimatedComposite.composite.GetAnimationMode() != v.animationMode {
v.animationMode = v.AnimatedComposite.composite.GetAnimationMode()
if v.composite.GetAnimationMode() != v.animationMode {
v.animationMode = v.composite.GetAnimationMode()
}
}
func (v *Player) Render(target d2render.Surface) {
v.AnimatedComposite.Render(target)
offX := v.AnimatedComposite.offsetX + int((v.AnimatedComposite.subcellX-v.AnimatedComposite.subcellY)*16)
offY := v.AnimatedComposite.offsetY + int(((v.AnimatedComposite.subcellX+v.AnimatedComposite.subcellY)*8)-5)
v.nameLabel.X = offX
v.nameLabel.Y = offY - 100
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)
v.nameLabel.X = v.offsetX
v.nameLabel.Y = v.offsetY - 100
v.nameLabel.Render(target)
}
func (v *Player) GetPosition() (float64, float64) {
return v.AnimatedComposite.GetPosition()
func (v *Player) SetMode(animationMode, weaponClass string, direction int) error {
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:
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 {
panic(err)
}

View File

@ -246,7 +246,7 @@ func (v *CharacterSelect) OnMouseButtonDown(event d2input.MouseEvent) bool {
func (v *CharacterSelect) Advance(tickTime float64) error {
for _, hero := range v.characterImage {
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
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)
}
return nil
}
func (v *Game) OnPlayerMove(x, y float64) {
heroPosX := v.localPlayer.AnimatedComposite.LocationX / 5.0
heroPosY := v.localPlayer.AnimatedComposite.LocationY / 5.0
heroPosX := v.localPlayer.LocationX / 5.0
heroPosY := v.localPlayer.LocationY / 5.0
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 ActionableRegion struct { ActionableTypeId ActionableType; Rect d2common.Rectangle }
type ActionableRegion struct {
ActionableTypeId ActionableType
Rect d2common.Rectangle
}
const (
// 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(),
escapeMenu: NewEscapeMenu(),
zoneChangeText: &label,
actionableRegions: []ActionableRegion {
{leftSkill, d2common.Rectangle{Left:115, Top:550, Width:50, Height:50 }},
{leftSelec, d2common.Rectangle{Left:206, Top:563, Width:30, Height:30 }},
{xp, d2common.Rectangle{Left:253, Top:560, Width:125, Height:5 }},
{walkRun, d2common.Rectangle{Left:255, Top:573, Width:17, Height:20 }},
{stamina, d2common.Rectangle{Left:273, Top:573, Width:105, Height:20 }},
{miniPanel, d2common.Rectangle{Left:393, Top:563, Width:12, Height:23 }},
{rightSelec,d2common.Rectangle{Left:562, Top:563, Width:30, Height:30 }},
{rightSkill,d2common.Rectangle{Left:634, Top:550, Width:50, Height:50 }},
actionableRegions: []ActionableRegion{
{leftSkill, d2common.Rectangle{Left: 115, Top: 550, Width: 50, Height: 50}},
{leftSelec, d2common.Rectangle{Left: 206, Top: 563, Width: 30, Height: 30}},
{xp, d2common.Rectangle{Left: 253, Top: 560, Width: 125, Height: 5}},
{walkRun, d2common.Rectangle{Left: 255, Top: 573, Width: 17, Height: 20}},
{stamina, d2common.Rectangle{Left: 273, Top: 573, Width: 105, Height: 20}},
{miniPanel, d2common.Rectangle{Left: 393, Top: 563, Width: 12, Height: 23}},
{rightSelec, d2common.Rectangle{Left: 562, Top: 563, Width: 30, Height: 30}},
{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 {
missile, err := d2mapentity.CreateMissile(
int(g.hero.AnimatedComposite.LocationX),
int(g.hero.AnimatedComposite.LocationY),
int(g.hero.LocationX),
int(g.hero.LocationY),
d2datadict.Missiles[missileID],
)
if err != nil {
@ -248,8 +251,8 @@ func (g *GameControls) ShootMissile(px float64, py float64) bool {
}
rads := d2common.GetRadiansBetween(
g.hero.AnimatedComposite.LocationX,
g.hero.AnimatedComposite.LocationY,
g.hero.LocationX,
g.hero.LocationY,
px*5,
py*5,
)
@ -434,14 +437,22 @@ func (g *GameControls) onHoverActionable(item ActionableType) {
// Handles what to do when an actionable is clicked
func (g *GameControls) onClickActionable(item ActionableType) {
switch item {
case leftSkill: log.Println("Left Skill Action Pressed")
case leftSelec: log.Println("Left Skill Selector Action Pressed")
case xp: log.Println("XP Action Pressed")
case walkRun: 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")
case leftSkill:
log.Println("Left Skill Action Pressed")
case leftSelec:
log.Println("Left Skill Selector Action Pressed")
case xp:
log.Println("XP Action Pressed")
case walkRun:
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:
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]
path, _, _ := g.MapEngine.PathFind(movePlayer.StartX, movePlayer.StartY, movePlayer.DestX, movePlayer.DestY)
if len(path) > 0 {
player.AnimatedComposite.SetPath(path, func() {
player.SetPath(path, func() {
tile := g.MapEngine.TileAt(player.TileX, player.TileY)
if tile == nil {
return
@ -100,7 +100,7 @@ func (g *GameClient) OnPacketReceived(packet d2netpacket.NetPacket) error {
} else {
player.SetIsInTown(false)
}
player.AnimatedComposite.SetAnimationMode(player.GetAnimationMode().String())
player.SetAnimationMode(player.GetAnimationMode().String())
})
}
case d2netpackettype.Ping: