2020-02-01 18:55:56 -05:00
|
|
|
package d2asset
|
2019-12-24 01:48:45 -05:00
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"strings"
|
2021-01-10 02:44:42 -05:00
|
|
|
"sync"
|
2019-12-24 01:48:45 -05:00
|
|
|
|
2020-01-26 00:39:13 -05:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
2020-07-01 14:09:18 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2cof"
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
2019-12-24 01:48:45 -05:00
|
|
|
)
|
|
|
|
|
2020-10-22 10:02:32 -04:00
|
|
|
const (
|
|
|
|
hardcodedFPS = 25.0
|
|
|
|
hardcodedDivisor = 1.0 / 256.0
|
|
|
|
speedUnit = hardcodedFPS * hardcodedDivisor
|
|
|
|
)
|
|
|
|
|
2020-07-01 14:09:18 -04:00
|
|
|
// Composite is a composite entity animation
|
2019-12-24 01:48:45 -05:00
|
|
|
type Composite struct {
|
2020-09-12 16:51:30 -04:00
|
|
|
*AssetManager
|
2020-07-03 22:52:50 -04:00
|
|
|
baseType d2enum.ObjectType
|
|
|
|
basePath string
|
|
|
|
token string
|
2019-12-24 01:48:45 -05:00
|
|
|
palettePath string
|
2020-07-03 22:52:50 -04:00
|
|
|
direction int
|
|
|
|
equipment [d2enum.CompositeTypeMax]string
|
2019-12-24 01:48:45 -05:00
|
|
|
mode *compositeMode
|
2020-08-01 19:03:09 -04:00
|
|
|
size *size
|
|
|
|
}
|
|
|
|
|
|
|
|
type size struct {
|
2020-08-11 18:01:33 -04:00
|
|
|
Width int
|
2020-08-01 19:03:09 -04:00
|
|
|
Height int
|
2019-12-24 01:48:45 -05:00
|
|
|
}
|
|
|
|
|
2020-07-01 14:09:18 -04:00
|
|
|
// Advance moves the composite animation forward for a given elapsed time in nanoseconds.
|
2019-12-24 01:48:45 -05:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2020-07-01 14:09:18 -04:00
|
|
|
// Render performs drawing of the Composite on the rendered d2interface.Surface.
|
2020-06-29 00:41:58 -04:00
|
|
|
func (c *Composite) Render(target d2interface.Surface) error {
|
2019-12-24 01:48:45 -05:00
|
|
|
if c.mode == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-07-03 22:52:50 -04:00
|
|
|
direction := d2cof.Dir64ToCof(c.direction, c.mode.cof.NumberOfDirections)
|
2020-07-26 19:29:37 -04:00
|
|
|
|
|
|
|
for _, layerIndex := range c.mode.cof.Priority[direction][c.mode.frameIndex] {
|
|
|
|
layer := c.mode.layers[layerIndex]
|
Decouple asset manager from renderer (#730)
* improve AssetManager implementation
Notable changes are:
* removed the individual managers inside of d2asset, only one asset manager
* AssetManager now has caches for the types of files it loads
* created a type for TextDictionary (the txt file structs)
* fixed a file path bug in d2loader Source
* fixed a asset stream bug in d2loader Asset
* d2loader.Loader now needs a d2config.Config on creation (for resolving locale files)
* updated the mpq file in d2asset test data, added test case for "sub-directory"
* added a Data method to d2asset.Asset. The data is cached on first full read.
* renamed ArchiveDataStream to DataStream in d2interface
* moved palette utility func out of d2asset and into d2util
* bugfix for MacOS mpq loader issue
* lint fixes, added data caching to filesystem asset
* adding comment for mpq asset close
* Decouple d2asset from d2render
Notable changes in d2common:
* d2dcc.Load now fully decodes the dcc and stores the directions/frames in the dcc struct
* un-exported dcc.decodeDirection, it is only used in d2dcc
* removed font interface from d2interface, we only have one font implementation
* added `Renderer` method to d2interface.Surface, animations use this to bind to a renderer and create surfaces as they need
* added `BindRenderer` method to animation interface
Notable changes in d2common/d2asset:
* **d2asset.NewAssetManager only needs to be passed a d2config.Config**, it is decoupled from d2render
* exported Animation
* Animation implementation binds to the renderer to create surfaces only on the first time it is rendered
* font, dcc, dc6 initialization logic moved out of asset_manager.go
* for dc6 and dcc animations, the process of decoding and creating render surfaces has been broken into different methods
* the d2asset.Font struct now stores font table data for initialization purposes
Notable changes in d2core/d2render:
* Surfaces store a renderer reference, this allows animations to bind to the renderer and create a surface just-in-time
**These last changes should have been a separate PR, sorry.**
Notable changes in d2core/d2ui:
* ui.NewSprite now handles creating an animation internally, only needs image and palette path as arguments
Notable Changes in d2game:
Because of the change in d2ui, all instances of this code pattern...
```golang
animation, err := screen.asset.LoadAnimation(imgPath, palettePath)
sprite, err := screen.ui.NewSprite(animation)
```
... becomes this ...
```golang
sprite, err := screen.ui.NewSprite(imgPath, palettePath)
```
2020-09-14 17:31:45 -04:00
|
|
|
|
2020-07-26 19:29:37 -04:00
|
|
|
if layer != nil {
|
2020-10-28 14:17:42 -04:00
|
|
|
layer.RenderFromOrigin(target, true)
|
2020-07-26 19:29:37 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-03 22:52:50 -04:00
|
|
|
for _, layerIndex := range c.mode.cof.Priority[direction][c.mode.frameIndex] {
|
2019-12-24 01:48:45 -05:00
|
|
|
layer := c.mode.layers[layerIndex]
|
|
|
|
if layer != nil {
|
2020-10-28 14:17:42 -04:00
|
|
|
layer.RenderFromOrigin(target, false)
|
2019-12-24 01:48:45 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-07-09 23:12:28 -04:00
|
|
|
// ObjectAnimationMode returns the object animation mode
|
|
|
|
func (c *Composite) ObjectAnimationMode() d2enum.ObjectAnimationMode {
|
|
|
|
return c.mode.animationMode.(d2enum.ObjectAnimationMode)
|
|
|
|
}
|
|
|
|
|
2020-07-01 14:09:18 -04:00
|
|
|
// GetAnimationMode returns the animation mode the Composite should render with.
|
2020-07-03 22:52:50 -04:00
|
|
|
func (c *Composite) GetAnimationMode() string {
|
2020-07-09 23:12:28 -04:00
|
|
|
return c.mode.animationMode.String()
|
2020-06-20 00:40:49 -04:00
|
|
|
}
|
|
|
|
|
2020-10-22 16:53:18 -04:00
|
|
|
// GetCurrentFrame returns the frame index in the current animation mode.
|
|
|
|
func (c *Composite) GetCurrentFrame() int {
|
|
|
|
return c.mode.frameIndex
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetFrameCount returns the number of frames in the current animation mode.
|
|
|
|
func (c *Composite) GetFrameCount() int {
|
|
|
|
return c.mode.frameCount
|
|
|
|
}
|
|
|
|
|
2020-07-03 22:52:50 -04:00
|
|
|
// GetWeaponClass returns the currently loaded weapon class
|
|
|
|
func (c *Composite) GetWeaponClass() string {
|
|
|
|
return c.mode.weaponClass
|
|
|
|
}
|
|
|
|
|
2020-07-01 14:09:18 -04:00
|
|
|
// SetMode sets the Composite's animation mode weapon class and direction
|
2020-07-09 23:12:28 -04:00
|
|
|
func (c *Composite) SetMode(animationMode animationMode, weaponClass string) error {
|
|
|
|
if c.mode != nil && c.mode.animationMode.String() == animationMode.String() && c.mode.weaponClass == weaponClass {
|
2019-12-24 01:48:45 -05:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-07-03 22:52:50 -04:00
|
|
|
mode, err := c.createMode(animationMode, weaponClass)
|
2019-12-24 01:48:45 -05:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-07-01 14:09:18 -04:00
|
|
|
c.resetPlayedCount()
|
2019-12-24 01:48:45 -05:00
|
|
|
c.mode = mode
|
2020-07-01 14:09:18 -04:00
|
|
|
|
2019-12-24 01:48:45 -05:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-07-03 22:52:50 -04:00
|
|
|
// Equip changes the current layer configuration
|
|
|
|
func (c *Composite) Equip(equipment *[d2enum.CompositeTypeMax]string) error {
|
|
|
|
c.equipment = *equipment
|
|
|
|
if c.mode == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
mode, err := c.createMode(c.mode.animationMode, c.mode.weaponClass)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
c.mode = mode
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetAnimSpeed sets the speed at which the Composite's animation should advance through its frames
|
|
|
|
func (c *Composite) SetAnimSpeed(speed int) {
|
2020-10-26 05:04:50 -04:00
|
|
|
c.mode.animationSpeed = 1.0 / (float64(speed) * speedUnit) //nolint:gomnd // taking inverse
|
2020-06-27 14:30:23 -04:00
|
|
|
for layerIdx := range c.mode.layers {
|
|
|
|
layer := c.mode.layers[layerIdx]
|
|
|
|
if layer != nil {
|
|
|
|
layer.SetPlaySpeed(c.mode.animationSpeed)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-03 22:52:50 -04:00
|
|
|
// SetDirection sets the direction of the composite and its layers
|
|
|
|
func (c *Composite) SetDirection(direction int) {
|
Decouple asset manager from renderer (#730)
* improve AssetManager implementation
Notable changes are:
* removed the individual managers inside of d2asset, only one asset manager
* AssetManager now has caches for the types of files it loads
* created a type for TextDictionary (the txt file structs)
* fixed a file path bug in d2loader Source
* fixed a asset stream bug in d2loader Asset
* d2loader.Loader now needs a d2config.Config on creation (for resolving locale files)
* updated the mpq file in d2asset test data, added test case for "sub-directory"
* added a Data method to d2asset.Asset. The data is cached on first full read.
* renamed ArchiveDataStream to DataStream in d2interface
* moved palette utility func out of d2asset and into d2util
* bugfix for MacOS mpq loader issue
* lint fixes, added data caching to filesystem asset
* adding comment for mpq asset close
* Decouple d2asset from d2render
Notable changes in d2common:
* d2dcc.Load now fully decodes the dcc and stores the directions/frames in the dcc struct
* un-exported dcc.decodeDirection, it is only used in d2dcc
* removed font interface from d2interface, we only have one font implementation
* added `Renderer` method to d2interface.Surface, animations use this to bind to a renderer and create surfaces as they need
* added `BindRenderer` method to animation interface
Notable changes in d2common/d2asset:
* **d2asset.NewAssetManager only needs to be passed a d2config.Config**, it is decoupled from d2render
* exported Animation
* Animation implementation binds to the renderer to create surfaces only on the first time it is rendered
* font, dcc, dc6 initialization logic moved out of asset_manager.go
* for dc6 and dcc animations, the process of decoding and creating render surfaces has been broken into different methods
* the d2asset.Font struct now stores font table data for initialization purposes
Notable changes in d2core/d2render:
* Surfaces store a renderer reference, this allows animations to bind to the renderer and create a surface just-in-time
**These last changes should have been a separate PR, sorry.**
Notable changes in d2core/d2ui:
* ui.NewSprite now handles creating an animation internally, only needs image and palette path as arguments
Notable Changes in d2game:
Because of the change in d2ui, all instances of this code pattern...
```golang
animation, err := screen.asset.LoadAnimation(imgPath, palettePath)
sprite, err := screen.ui.NewSprite(animation)
```
... becomes this ...
```golang
sprite, err := screen.ui.NewSprite(imgPath, palettePath)
```
2020-09-14 17:31:45 -04:00
|
|
|
if c.mode == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-01-10 02:44:42 -05:00
|
|
|
wg := sync.WaitGroup{}
|
|
|
|
|
2020-07-03 22:52:50 -04:00
|
|
|
c.direction = direction
|
2021-01-10 02:44:42 -05:00
|
|
|
wg.Add(len(c.mode.layers))
|
|
|
|
|
2020-07-03 22:52:50 -04:00
|
|
|
for layerIdx := range c.mode.layers {
|
2021-01-10 02:44:42 -05:00
|
|
|
go func(idx int) {
|
|
|
|
defer wg.Done()
|
|
|
|
|
|
|
|
layer := c.mode.layers[idx]
|
|
|
|
|
|
|
|
if layer != nil {
|
|
|
|
if err := layer.SetDirection(c.direction); err != nil {
|
|
|
|
fmt.Printf("failed to set direction of layer: %d, err: %v\n", idx, err)
|
|
|
|
}
|
2020-07-23 12:56:50 -04:00
|
|
|
}
|
2021-01-10 02:44:42 -05:00
|
|
|
}(layerIdx)
|
2019-12-24 01:48:45 -05:00
|
|
|
}
|
2021-01-10 02:44:42 -05:00
|
|
|
|
|
|
|
wg.Wait()
|
2020-07-03 22:52:50 -04:00
|
|
|
}
|
2019-12-24 01:48:45 -05:00
|
|
|
|
2020-07-04 00:48:31 -04:00
|
|
|
// GetDirection returns the current direction the composite is facing
|
2020-07-03 22:52:50 -04:00
|
|
|
func (c *Composite) GetDirection() int {
|
|
|
|
return c.direction
|
2019-12-24 01:48:45 -05:00
|
|
|
}
|
|
|
|
|
2020-07-01 14:09:18 -04:00
|
|
|
// GetPlayedCount returns the number of times the current animation mode has completed all its distinct frames
|
2019-12-24 01:48:45 -05:00
|
|
|
func (c *Composite) GetPlayedCount() int {
|
|
|
|
if c.mode == nil {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
return c.mode.playedCount
|
|
|
|
}
|
|
|
|
|
2020-07-06 20:12:53 -04:00
|
|
|
// SetPlayLoop turns on or off animation looping
|
|
|
|
func (c *Composite) SetPlayLoop(loop bool) {
|
|
|
|
for layerIdx := range c.mode.layers {
|
|
|
|
layer := c.mode.layers[layerIdx]
|
|
|
|
if layer != nil {
|
|
|
|
layer.SetPlayLoop(loop)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetSubLoop sets a loop to be between the specified frame indices
|
|
|
|
func (c *Composite) SetSubLoop(startFrame, endFrame int) {
|
|
|
|
for layerIdx := range c.mode.layers {
|
|
|
|
layer := c.mode.layers[layerIdx]
|
|
|
|
if layer != nil {
|
|
|
|
layer.SetSubLoop(startFrame, endFrame)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetCurrentFrame sets the current frame index of the animation
|
|
|
|
func (c *Composite) SetCurrentFrame(frame int) {
|
|
|
|
for layerIdx := range c.mode.layers {
|
|
|
|
layer := c.mode.layers[layerIdx]
|
|
|
|
if layer != nil {
|
2020-07-23 12:56:50 -04:00
|
|
|
if err := layer.SetCurrentFrame(frame); err != nil {
|
|
|
|
fmt.Printf("failed to set current frame of layer: %d, err: %v\n", layerIdx, err)
|
|
|
|
}
|
2020-07-06 20:12:53 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-01 14:09:18 -04:00
|
|
|
func (c *Composite) resetPlayedCount() {
|
2019-12-24 01:48:45 -05:00
|
|
|
if c.mode != nil {
|
|
|
|
c.mode.playedCount = 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-09 23:12:28 -04:00
|
|
|
type animationMode interface {
|
|
|
|
String() string
|
|
|
|
}
|
2020-07-06 20:12:53 -04:00
|
|
|
|
2019-12-24 01:48:45 -05:00
|
|
|
type compositeMode struct {
|
2020-07-03 22:52:50 -04:00
|
|
|
cof *d2cof.COF
|
2020-07-09 23:12:28 -04:00
|
|
|
animationMode animationMode
|
2020-07-03 22:52:50 -04:00
|
|
|
weaponClass string
|
|
|
|
playedCount int
|
2019-12-24 01:48:45 -05:00
|
|
|
|
2020-07-05 13:01:44 -04:00
|
|
|
layers []d2interface.Animation
|
2019-12-24 01:48:45 -05:00
|
|
|
|
|
|
|
frameCount int
|
|
|
|
frameIndex int
|
|
|
|
animationSpeed float64
|
|
|
|
lastFrameTime float64
|
|
|
|
}
|
|
|
|
|
2020-07-09 23:12:28 -04:00
|
|
|
func (c *Composite) createMode(animationMode animationMode, weaponClass string) (*compositeMode, error) {
|
2020-07-03 22:52:50 -04:00
|
|
|
cofPath := fmt.Sprintf("%s/%s/COF/%s%s%s.COF", c.basePath, c.token, c.token, animationMode, weaponClass)
|
2020-09-23 13:30:54 -04:00
|
|
|
if exists, err := c.FileExists(cofPath); !exists {
|
|
|
|
return nil, fmt.Errorf("composite not found at path '%s': %v", cofPath, err)
|
2019-12-28 23:32:24 -05:00
|
|
|
}
|
|
|
|
|
2021-01-10 02:44:42 -05:00
|
|
|
cof, err := c.LoadCOF(cofPath)
|
2019-12-24 01:48:45 -05:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-02-26 05:56:49 -05:00
|
|
|
animationKey := strings.ToUpper(c.token + animationMode.String() + weaponClass)
|
2020-07-01 14:09:18 -04:00
|
|
|
|
2021-02-26 05:56:49 -05:00
|
|
|
animationData := c.Records.Animation.Data.GetRecords(animationKey)
|
2019-12-24 01:48:45 -05:00
|
|
|
if len(animationData) == 0 {
|
Decouple asset manager from renderer (#730)
* improve AssetManager implementation
Notable changes are:
* removed the individual managers inside of d2asset, only one asset manager
* AssetManager now has caches for the types of files it loads
* created a type for TextDictionary (the txt file structs)
* fixed a file path bug in d2loader Source
* fixed a asset stream bug in d2loader Asset
* d2loader.Loader now needs a d2config.Config on creation (for resolving locale files)
* updated the mpq file in d2asset test data, added test case for "sub-directory"
* added a Data method to d2asset.Asset. The data is cached on first full read.
* renamed ArchiveDataStream to DataStream in d2interface
* moved palette utility func out of d2asset and into d2util
* bugfix for MacOS mpq loader issue
* lint fixes, added data caching to filesystem asset
* adding comment for mpq asset close
* Decouple d2asset from d2render
Notable changes in d2common:
* d2dcc.Load now fully decodes the dcc and stores the directions/frames in the dcc struct
* un-exported dcc.decodeDirection, it is only used in d2dcc
* removed font interface from d2interface, we only have one font implementation
* added `Renderer` method to d2interface.Surface, animations use this to bind to a renderer and create surfaces as they need
* added `BindRenderer` method to animation interface
Notable changes in d2common/d2asset:
* **d2asset.NewAssetManager only needs to be passed a d2config.Config**, it is decoupled from d2render
* exported Animation
* Animation implementation binds to the renderer to create surfaces only on the first time it is rendered
* font, dcc, dc6 initialization logic moved out of asset_manager.go
* for dc6 and dcc animations, the process of decoding and creating render surfaces has been broken into different methods
* the d2asset.Font struct now stores font table data for initialization purposes
Notable changes in d2core/d2render:
* Surfaces store a renderer reference, this allows animations to bind to the renderer and create a surface just-in-time
**These last changes should have been a separate PR, sorry.**
Notable changes in d2core/d2ui:
* ui.NewSprite now handles creating an animation internally, only needs image and palette path as arguments
Notable Changes in d2game:
Because of the change in d2ui, all instances of this code pattern...
```golang
animation, err := screen.asset.LoadAnimation(imgPath, palettePath)
sprite, err := screen.ui.NewSprite(animation)
```
... becomes this ...
```golang
sprite, err := screen.ui.NewSprite(imgPath, palettePath)
```
2020-09-14 17:31:45 -04:00
|
|
|
return nil, errors.New("could not find Animation data")
|
2019-12-24 01:48:45 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
mode := &compositeMode{
|
2020-07-03 22:52:50 -04:00
|
|
|
cof: cof,
|
2019-12-24 01:48:45 -05:00
|
|
|
animationMode: animationMode,
|
|
|
|
weaponClass: weaponClass,
|
2020-07-05 13:01:44 -04:00
|
|
|
layers: make([]d2interface.Animation, d2enum.CompositeTypeMax),
|
2021-02-26 05:56:49 -05:00
|
|
|
frameCount: animationData[0].FramesPerDirection(),
|
|
|
|
animationSpeed: 1.0 / (float64(animationData[0].Speed()) * speedUnit), //nolint:gomnd // taking inverse
|
2019-12-24 01:48:45 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, cofLayer := range cof.CofLayers {
|
2020-07-03 22:52:50 -04:00
|
|
|
layerValue := c.equipment[cofLayer.Type]
|
|
|
|
if layerValue == "" {
|
|
|
|
layerValue = "lit"
|
2019-12-24 01:48:45 -05:00
|
|
|
}
|
|
|
|
|
2020-07-08 17:46:45 -04:00
|
|
|
drawEffect := d2enum.DrawEffectNone
|
2020-07-03 22:52:50 -04:00
|
|
|
|
2019-12-24 01:48:45 -05:00
|
|
|
if cofLayer.Transparent {
|
2020-07-08 17:46:45 -04:00
|
|
|
drawEffect = cofLayer.DrawEffect
|
2019-12-24 01:48:45 -05:00
|
|
|
}
|
|
|
|
|
2020-07-09 23:12:28 -04:00
|
|
|
layer, err := c.loadCompositeLayer(cofLayer.Type.String(), layerValue, animationMode.String(),
|
2020-07-08 17:46:45 -04:00
|
|
|
cofLayer.WeaponClass.String(), c.palettePath, drawEffect)
|
2019-12-24 01:48:45 -05:00
|
|
|
if err == nil {
|
|
|
|
layer.SetPlaySpeed(mode.animationSpeed)
|
|
|
|
layer.PlayForward()
|
2020-08-16 14:59:34 -04:00
|
|
|
layer.SetShadow(cofLayer.Shadow != 0)
|
2020-07-03 22:52:50 -04:00
|
|
|
|
|
|
|
if err := layer.SetDirection(c.direction); err != nil {
|
2020-07-01 14:09:18 -04:00
|
|
|
return nil, err
|
|
|
|
}
|
2020-07-03 22:52:50 -04:00
|
|
|
|
2019-12-24 01:48:45 -05:00
|
|
|
mode.layers[cofLayer.Type] = layer
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return mode, nil
|
|
|
|
}
|
|
|
|
|
2020-07-03 22:52:50 -04:00
|
|
|
func (c *Composite) loadCompositeLayer(layerKey, layerValue, animationMode, weaponClass,
|
2020-07-08 17:46:45 -04:00
|
|
|
palettePath string, drawEffect d2enum.DrawEffect) (d2interface.Animation, error) {
|
2019-12-24 01:48:45 -05:00
|
|
|
animationPaths := []string{
|
2020-07-03 22:52:50 -04:00
|
|
|
fmt.Sprintf("%s/%s/%s/%s%s%s%s%s.dcc", c.basePath, c.token, layerKey, c.token, layerKey, layerValue, animationMode, weaponClass),
|
|
|
|
fmt.Sprintf("%s/%s/%s/%s%s%s%s%s.dc6", c.basePath, c.token, layerKey, c.token, layerKey, layerValue, animationMode, weaponClass),
|
2019-12-24 01:48:45 -05:00
|
|
|
}
|
|
|
|
|
2021-01-10 02:44:42 -05:00
|
|
|
for idx := range animationPaths {
|
|
|
|
exists, err := c.FileExists(animationPaths[idx])
|
2020-10-25 19:50:13 -04:00
|
|
|
if !exists || err != nil {
|
2021-01-10 02:44:42 -05:00
|
|
|
return nil, fmt.Errorf("animation path '%s' not found: %v", animationPaths[idx], err)
|
2019-12-24 01:48:45 -05:00
|
|
|
}
|
2020-10-25 19:50:13 -04:00
|
|
|
|
2021-01-10 02:44:42 -05:00
|
|
|
animation, err := c.LoadAnimationWithEffect(animationPaths[idx], palettePath, drawEffect)
|
2020-10-25 19:50:13 -04:00
|
|
|
if err == nil {
|
|
|
|
return animation, nil
|
|
|
|
}
|
2019-12-24 01:48:45 -05:00
|
|
|
}
|
|
|
|
|
2020-09-23 13:30:54 -04:00
|
|
|
return nil, errors.New("animation not found")
|
2019-12-24 01:48:45 -05:00
|
|
|
}
|
2020-07-03 22:52:50 -04:00
|
|
|
|
2020-08-11 18:01:33 -04:00
|
|
|
// GetSize returns the size of the composite
|
2020-08-01 19:03:09 -04:00
|
|
|
func (c *Composite) GetSize() (w, h int) {
|
|
|
|
c.updateSize()
|
|
|
|
return c.size.Width, c.size.Height
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Composite) updateSize() {
|
|
|
|
if c.mode == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
direction := d2cof.Dir64ToCof(c.direction, c.mode.cof.NumberOfDirections)
|
|
|
|
|
|
|
|
biggestW, biggestH := 0, 0
|
|
|
|
|
|
|
|
for _, layerIndex := range c.mode.cof.Priority[direction][c.mode.frameIndex] {
|
|
|
|
layer := c.mode.layers[layerIndex]
|
|
|
|
if layer != nil {
|
|
|
|
w, h := layer.GetCurrentFrameSize()
|
|
|
|
|
|
|
|
if biggestW < w {
|
|
|
|
biggestW = w
|
|
|
|
}
|
|
|
|
|
|
|
|
if biggestH < h {
|
|
|
|
biggestH = h
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if c.size == nil {
|
|
|
|
c.size = &size{}
|
|
|
|
}
|
|
|
|
|
|
|
|
c.size.Width = biggestW
|
|
|
|
c.size.Height = biggestH
|
|
|
|
}
|
|
|
|
|
2020-07-03 22:52:50 -04:00
|
|
|
func baseString(baseType d2enum.ObjectType) string {
|
|
|
|
switch baseType {
|
|
|
|
case d2enum.ObjectTypePlayer:
|
|
|
|
return "/data/global/chars"
|
|
|
|
case d2enum.ObjectTypeCharacter:
|
|
|
|
return "/data/global/monsters"
|
|
|
|
case d2enum.ObjectTypeItem:
|
|
|
|
return "/data/global/objects"
|
|
|
|
default:
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
}
|