mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2024-11-10 06:16:27 -05:00
11f743aa42
* String2enum ObjectAnimationMode * Render objects at their assigned layer Gets the orderflag from the object record and assign it to the mapentity so the renderer can get at it. This adds another render pass that loops through the objects. * Get object animation speed from their txt entry
274 lines
7.3 KiB
Go
274 lines
7.3 KiB
Go
package d2asset
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"math"
|
|
"strings"
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data"
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
|
|
)
|
|
|
|
type Composite struct {
|
|
object *d2datadict.ObjectLookupRecord
|
|
palettePath string
|
|
mode *compositeMode
|
|
}
|
|
|
|
func CreateComposite(object *d2datadict.ObjectLookupRecord, palettePath string) *Composite {
|
|
return &Composite{object: object, palettePath: palettePath}
|
|
}
|
|
|
|
func (c *Composite) Advance(elapsed float64) error {
|
|
if c.mode == nil {
|
|
return nil
|
|
}
|
|
|
|
c.mode.lastFrameTime += elapsed
|
|
framesToAdd := int(c.mode.lastFrameTime / c.mode.animationSpeed)
|
|
c.mode.lastFrameTime -= float64(framesToAdd) * c.mode.animationSpeed
|
|
c.mode.frameIndex += framesToAdd
|
|
c.mode.playedCount += c.mode.frameIndex / c.mode.frameCount
|
|
c.mode.frameIndex %= c.mode.frameCount
|
|
|
|
for _, layer := range c.mode.layers {
|
|
if layer != nil {
|
|
if err := layer.Advance(elapsed); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *Composite) Render(target d2render.Surface) error {
|
|
if c.mode == nil {
|
|
return nil
|
|
}
|
|
|
|
for _, layerIndex := range c.mode.drawOrder[c.mode.frameIndex] {
|
|
layer := c.mode.layers[layerIndex]
|
|
if layer != nil {
|
|
if err := layer.RenderFromOrigin(target); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c Composite) GetAnimationMode() string {
|
|
return c.mode.animationMode
|
|
}
|
|
|
|
func (c *Composite) SetMode(animationMode, weaponClass string, direction int) error {
|
|
if c.mode != nil && c.mode.animationMode == animationMode && c.mode.weaponClass == weaponClass && c.mode.direction == direction {
|
|
return nil
|
|
}
|
|
|
|
mode, err := c.createMode(animationMode, weaponClass, direction)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
c.ResetPlayedCount()
|
|
c.mode = mode
|
|
return nil
|
|
}
|
|
|
|
func (c *Composite) SetSpeed(speed int) {
|
|
c.mode.animationSpeed = 1.0 / ((float64(speed) * 25.0) / 256.0)
|
|
for layerIdx := range c.mode.layers {
|
|
layer := c.mode.layers[layerIdx]
|
|
if layer != nil {
|
|
layer.SetPlaySpeed(c.mode.animationSpeed)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (c *Composite) GetDirectionCount() int {
|
|
if c.mode == nil {
|
|
return 0
|
|
}
|
|
|
|
return c.mode.directionCount
|
|
}
|
|
|
|
func (c *Composite) GetPlayedCount() int {
|
|
if c.mode == nil {
|
|
return 0
|
|
}
|
|
|
|
return c.mode.playedCount
|
|
}
|
|
|
|
func (c *Composite) ResetPlayedCount() {
|
|
if c.mode != nil {
|
|
c.mode.playedCount = 0
|
|
}
|
|
}
|
|
|
|
type compositeMode struct {
|
|
animationMode string
|
|
weaponClass string
|
|
direction int
|
|
directionCount int
|
|
playedCount int
|
|
|
|
layers []*Animation
|
|
drawOrder [][]d2enum.CompositeType
|
|
|
|
frameCount int
|
|
frameIndex int
|
|
animationSpeed float64
|
|
lastFrameTime float64
|
|
}
|
|
|
|
func (c *Composite) createMode(animationMode, weaponClass string, direction int) (*compositeMode, error) {
|
|
cofPath := fmt.Sprintf("%s/%s/COF/%s%s%s.COF", c.object.Base, c.object.Token, c.object.Token, animationMode, weaponClass)
|
|
if exists, _ := FileExists(cofPath); !exists {
|
|
return nil, errors.New("composite not found")
|
|
}
|
|
|
|
cof, err := loadCOF(cofPath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// oh god how do i math
|
|
offset := (64 / cof.NumberOfDirections) / 2
|
|
entityDirection := int(math.Trunc((float64(direction+offset)-64.0)*(-float64(cof.NumberOfDirections)/-64.0) + float64(cof.NumberOfDirections)))
|
|
|
|
if entityDirection >= cof.NumberOfDirections {
|
|
entityDirection = 0
|
|
}
|
|
|
|
animationKey := strings.ToLower(c.object.Token + animationMode + weaponClass)
|
|
animationData := d2data.AnimationData[animationKey]
|
|
if len(animationData) == 0 {
|
|
return nil, errors.New("could not find animation data")
|
|
}
|
|
|
|
mode := &compositeMode{
|
|
animationMode: animationMode,
|
|
weaponClass: weaponClass,
|
|
direction: entityDirection,
|
|
directionCount: cof.NumberOfDirections,
|
|
layers: make([]*Animation, d2enum.CompositeTypeMax),
|
|
frameCount: animationData[0].FramesPerDirection,
|
|
animationSpeed: 1.0 / ((float64(animationData[0].AnimationSpeed) * 25.0) / 256.0),
|
|
}
|
|
|
|
mode.drawOrder = make([][]d2enum.CompositeType, mode.frameCount)
|
|
for frame := 0; frame < mode.frameCount; frame++ {
|
|
mode.drawOrder[frame] = cof.Priority[mode.direction][frame]
|
|
}
|
|
|
|
for _, cofLayer := range cof.CofLayers {
|
|
var layerKey, 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")
|
|
}
|
|
|
|
blend := false
|
|
transparency := 255
|
|
if cofLayer.Transparent {
|
|
switch cofLayer.DrawEffect {
|
|
case d2enum.DrawEffectPctTransparency25:
|
|
transparency = 64
|
|
case d2enum.DrawEffectPctTransparency50:
|
|
transparency = 128
|
|
case d2enum.DrawEffectPctTransparency75:
|
|
transparency = 192
|
|
case d2enum.DrawEffectModulate:
|
|
blend = true
|
|
}
|
|
}
|
|
|
|
layer, err := loadCompositeLayer(c.object, layerKey, layerValue, animationMode, weaponClass, c.palettePath, transparency)
|
|
if err == nil {
|
|
layer.SetPlaySpeed(mode.animationSpeed)
|
|
layer.PlayForward()
|
|
layer.SetBlend(blend)
|
|
layer.SetDirection(direction)
|
|
mode.layers[cofLayer.Type] = layer
|
|
}
|
|
}
|
|
|
|
return mode, nil
|
|
}
|
|
|
|
func loadCompositeLayer(object *d2datadict.ObjectLookupRecord, layerKey, layerValue, animationMode, weaponClass, palettePath string, transparency int) (*Animation, error) {
|
|
animationPaths := []string{
|
|
fmt.Sprintf("%s/%s/%s/%s%s%s%s%s.dcc", object.Base, object.Token, layerKey, object.Token, layerKey, layerValue, animationMode, weaponClass),
|
|
fmt.Sprintf("%s/%s/%s/%s%s%s%s%s.dcc", object.Base, object.Token, layerKey, object.Token, layerKey, layerValue, animationMode, "HTH"),
|
|
fmt.Sprintf("%s/%s/%s/%s%s%s%s%s.dc6", object.Base, object.Token, layerKey, object.Token, layerKey, layerValue, animationMode, weaponClass),
|
|
fmt.Sprintf("%s/%s/%s/%s%s%s%s%s.dc6", object.Base, object.Token, layerKey, object.Token, layerKey, layerValue, animationMode, "HTH"),
|
|
}
|
|
|
|
for _, animationPath := range animationPaths {
|
|
if exists, _ := FileExists(animationPath); exists {
|
|
animation, err := LoadAnimationWithTransparency(animationPath, palettePath, transparency)
|
|
if err == nil {
|
|
return animation, nil
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil, errors.New("animation not found")
|
|
}
|