1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-06-12 02:30:43 +00:00
OpenDiablo2/d2core/d2asset/composite.go
Ziemas 11f743aa42
Get and use draw order and animation speed for objects (#473)
* 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
2020-06-27 14:30:23 -04:00

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")
}