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)
```
This commit is contained in:
lord 2020-09-14 14:31:45 -07:00 committed by GitHub
parent 8a670d7482
commit 7e3aff557b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 532 additions and 335 deletions

View File

@ -15,6 +15,7 @@ type DCC struct {
Version int Version int
NumberOfDirections int NumberOfDirections int
FramesPerDirection int FramesPerDirection int
Directions []*DCCDirection
directionOffsets []int directionOffsets []int
fileData []byte fileData []byte
} }
@ -37,6 +38,8 @@ func Load(fileData []byte) (*DCC, error) {
result.NumberOfDirections = int(bm.GetByte()) result.NumberOfDirections = int(bm.GetByte())
result.FramesPerDirection = int(bm.GetInt32()) result.FramesPerDirection = int(bm.GetInt32())
result.Directions = make([]*DCCDirection, result.NumberOfDirections)
if bm.GetInt32() != 1 { if bm.GetInt32() != 1 {
return nil, errors.New("this value isn't 1. It has to be 1") return nil, errors.New("this value isn't 1. It has to be 1")
} }
@ -47,13 +50,14 @@ func Load(fileData []byte) (*DCC, error) {
for i := 0; i < result.NumberOfDirections; i++ { for i := 0; i < result.NumberOfDirections; i++ {
result.directionOffsets[i] = int(bm.GetInt32()) result.directionOffsets[i] = int(bm.GetInt32())
result.Directions[i] = result.decodeDirection(i)
} }
return result, nil return result, nil
} }
// DecodeDirection decodes and returns the given direction // decodeDirection decodes and returns the given direction
func (dcc *DCC) DecodeDirection(direction int) *DCCDirection { func (dcc *DCC) decodeDirection(direction int) *DCCDirection {
return CreateDCCDirection(d2datautils.CreateBitMuncher(dcc.fileData, return CreateDCCDirection(d2datautils.CreateBitMuncher(dcc.fileData,
dcc.directionOffsets[direction]*directionOffsetMultiplier), dcc) dcc.directionOffsets[direction]*directionOffsetMultiplier), dcc)
} }

View File

@ -9,6 +9,7 @@ import (
// Animation is an animation // Animation is an animation
type Animation interface { type Animation interface {
BindRenderer(Renderer) error
Clone() Animation Clone() Animation
SetSubLoop(startFrame, EndFrame int) SetSubLoop(startFrame, EndFrame int)
Advance(elapsed float64) error Advance(elapsed float64) error

View File

@ -1,12 +0,0 @@
package d2interface
import (
"image/color"
)
// Font is a graphical representation associated with a set of glyphs.
type Font interface {
SetColor(c color.Color)
GetTextMetrics(text string) (width, height int)
RenderText(text string, target Surface) error
}

View File

@ -9,6 +9,7 @@ import (
// Surface represents a renderable surface. // Surface represents a renderable surface.
type Surface interface { type Surface interface {
Renderer() Renderer
Clear(color color.Color) error Clear(color color.Color) error
DrawRect(width, height int, color color.Color) DrawRect(width, height int, color color.Color)
DrawLine(x, y int, color color.Color) DrawLine(x, y int, color color.Color)

View File

@ -23,6 +23,8 @@ const (
const defaultPlayLength = 1.0 const defaultPlayLength = 1.0
type animationFrame struct { type animationFrame struct {
decoded bool
width int width int
height int height int
offsetX int offsetX int
@ -33,11 +35,13 @@ type animationFrame struct {
type animationDirection struct { type animationDirection struct {
decoded bool decoded bool
frames []*animationFrame frames []animationFrame
} }
// animation has directionality, play modes, and frame counting // Animation has directionality, play modes, and frame counting
type animation struct { type Animation struct {
renderer d2interface.Renderer
onBindRenderer func(renderer d2interface.Renderer) error
directions []animationDirection directions []animationDirection
effect d2enum.DrawEffect effect d2enum.DrawEffect
colorMod color.Color colorMod color.Color
@ -56,14 +60,14 @@ type animation struct {
} }
// SetSubLoop sets a sub loop for the animation // SetSubLoop sets a sub loop for the animation
func (a *animation) SetSubLoop(startFrame, endFrame int) { func (a *Animation) SetSubLoop(startFrame, endFrame int) {
a.subStartingFrame = startFrame a.subStartingFrame = startFrame
a.subEndingFrame = endFrame a.subEndingFrame = endFrame
a.hasSubLoop = true a.hasSubLoop = true
} }
// Advance advances the animation state // Advance advances the animation state
func (a *animation) Advance(elapsed float64) error { func (a *Animation) Advance(elapsed float64) error {
if a.playMode == playModePause { if a.playMode == playModePause {
return nil return nil
} }
@ -112,7 +116,7 @@ func (a *animation) Advance(elapsed float64) error {
return nil return nil
} }
func (a *animation) renderShadow(target d2interface.Surface) error { func (a *Animation) renderShadow(target d2interface.Surface) error {
direction := a.directions[a.directionIndex] direction := a.directions[a.directionIndex]
frame := direction.frames[a.frameIndex] frame := direction.frames[a.frameIndex]
@ -131,7 +135,11 @@ func (a *animation) renderShadow(target d2interface.Surface) error {
} }
// Render renders the animation to the given surface // Render renders the animation to the given surface
func (a *animation) Render(target d2interface.Surface) error { func (a *Animation) Render(target d2interface.Surface) error {
if a.renderer == nil {
a.BindRenderer(target.Renderer())
}
direction := a.directions[a.directionIndex] direction := a.directions[a.directionIndex]
frame := direction.frames[a.frameIndex] frame := direction.frames[a.frameIndex]
@ -147,8 +155,22 @@ func (a *animation) Render(target d2interface.Surface) error {
return target.Render(frame.image) return target.Render(frame.image)
} }
// BindRenderer binds the given renderer to the animation so that it can initialize
// the required surfaces
func (a *Animation) BindRenderer(r d2interface.Renderer) error {
if a.onBindRenderer == nil {
return errors.New("the Animation does not have a onBindRenderer handler")
}
return a.onBindRenderer(r)
}
// RenderFromOrigin renders the animation from the animation origin // RenderFromOrigin renders the animation from the animation origin
func (a *animation) RenderFromOrigin(target d2interface.Surface, shadow bool) error { func (a *Animation) RenderFromOrigin(target d2interface.Surface, shadow bool) error {
if a.renderer == nil {
a.BindRenderer(target.Renderer())
}
if a.originAtBottom { if a.originAtBottom {
direction := a.directions[a.directionIndex] direction := a.directions[a.directionIndex]
frame := direction.frames[a.frameIndex] frame := direction.frames[a.frameIndex]
@ -171,7 +193,11 @@ func (a *animation) RenderFromOrigin(target d2interface.Surface, shadow bool) er
} }
// RenderSection renders the section of the animation frame enclosed by bounds // RenderSection renders the section of the animation frame enclosed by bounds
func (a *animation) RenderSection(sfc d2interface.Surface, bound image.Rectangle) error { func (a *Animation) RenderSection(sfc d2interface.Surface, bound image.Rectangle) error {
if a.renderer == nil {
a.BindRenderer(sfc.Renderer())
}
direction := a.directions[a.directionIndex] direction := a.directions[a.directionIndex]
frame := direction.frames[a.frameIndex] frame := direction.frames[a.frameIndex]
@ -185,7 +211,7 @@ func (a *animation) RenderSection(sfc d2interface.Surface, bound image.Rectangle
} }
// GetFrameSize gets the Size(width, height) of a indexed frame. // GetFrameSize gets the Size(width, height) of a indexed frame.
func (a *animation) GetFrameSize(frameIndex int) (width, height int, err error) { func (a *Animation) GetFrameSize(frameIndex int) (width, height int, err error) {
direction := a.directions[a.directionIndex] direction := a.directions[a.directionIndex]
if frameIndex >= len(direction.frames) { if frameIndex >= len(direction.frames) {
return 0, 0, errors.New("invalid frame index") return 0, 0, errors.New("invalid frame index")
@ -197,16 +223,17 @@ func (a *animation) GetFrameSize(frameIndex int) (width, height int, err error)
} }
// GetCurrentFrameSize gets the Size(width, height) of the current frame. // GetCurrentFrameSize gets the Size(width, height) of the current frame.
func (a *animation) GetCurrentFrameSize() (width, height int) { func (a *Animation) GetCurrentFrameSize() (width, height int) {
width, height, _ = a.GetFrameSize(a.frameIndex) width, height, _ = a.GetFrameSize(a.frameIndex)
return width, height return width, height
} }
// GetFrameBounds gets maximum Size(width, height) of all frame. // GetFrameBounds gets maximum Size(width, height) of all frame.
func (a *animation) GetFrameBounds() (maxWidth, maxHeight int) { func (a *Animation) GetFrameBounds() (maxWidth, maxHeight int) {
maxWidth, maxHeight = 0, 0 maxWidth, maxHeight = 0, 0
direction := a.directions[a.directionIndex] direction := a.directions[a.directionIndex]
for _, frame := range direction.frames { for _, frame := range direction.frames {
maxWidth = d2math.MaxInt(maxWidth, frame.width) maxWidth = d2math.MaxInt(maxWidth, frame.width)
maxHeight = d2math.MaxInt(maxHeight, frame.height) maxHeight = d2math.MaxInt(maxHeight, frame.height)
@ -216,33 +243,33 @@ func (a *animation) GetFrameBounds() (maxWidth, maxHeight int) {
} }
// GetCurrentFrame gets index of current frame in animation // GetCurrentFrame gets index of current frame in animation
func (a *animation) GetCurrentFrame() int { func (a *Animation) GetCurrentFrame() int {
return a.frameIndex return a.frameIndex
} }
// GetFrameCount gets number of frames in animation // GetFrameCount gets number of frames in animation
func (a *animation) GetFrameCount() int { func (a *Animation) GetFrameCount() int {
direction := a.directions[a.directionIndex] direction := a.directions[a.directionIndex]
return len(direction.frames) return len(direction.frames)
} }
// IsOnFirstFrame gets if the animation on its first frame // IsOnFirstFrame gets if the animation on its first frame
func (a *animation) IsOnFirstFrame() bool { func (a *Animation) IsOnFirstFrame() bool {
return a.frameIndex == 0 return a.frameIndex == 0
} }
// IsOnLastFrame gets if the animation on its last frame // IsOnLastFrame gets if the animation on its last frame
func (a *animation) IsOnLastFrame() bool { func (a *Animation) IsOnLastFrame() bool {
return a.frameIndex == a.GetFrameCount()-1 return a.frameIndex == a.GetFrameCount()-1
} }
// GetDirectionCount gets the number of animation direction // GetDirectionCount gets the number of animation direction
func (a *animation) GetDirectionCount() int { func (a *Animation) GetDirectionCount() int {
return len(a.directions) return len(a.directions)
} }
// SetDirection places the animation in the direction of an animation // SetDirection places the animation in the direction of an animation
func (a *animation) SetDirection(directionIndex int) error { func (a *Animation) SetDirection(directionIndex int) error {
const smallestInvalidDirectionIndex = 64 const smallestInvalidDirectionIndex = 64
if directionIndex >= smallestInvalidDirectionIndex { if directionIndex >= smallestInvalidDirectionIndex {
return errors.New("invalid direction index") return errors.New("invalid direction index")
@ -255,12 +282,12 @@ func (a *animation) SetDirection(directionIndex int) error {
} }
// GetDirection get the current animation direction // GetDirection get the current animation direction
func (a *animation) GetDirection() int { func (a *Animation) GetDirection() int {
return a.directionIndex return a.directionIndex
} }
// SetCurrentFrame sets animation at a specific frame // SetCurrentFrame sets animation at a specific frame
func (a *animation) SetCurrentFrame(frameIndex int) error { func (a *Animation) SetCurrentFrame(frameIndex int) error {
if frameIndex >= a.GetFrameCount() { if frameIndex >= a.GetFrameCount() {
return errors.New("invalid frame index") return errors.New("invalid frame index")
} }
@ -272,47 +299,47 @@ func (a *animation) SetCurrentFrame(frameIndex int) error {
} }
// Rewind animation to beginning // Rewind animation to beginning
func (a *animation) Rewind() { func (a *Animation) Rewind() {
_ = a.SetCurrentFrame(0) _ = a.SetCurrentFrame(0)
} }
// PlayForward plays animation forward // PlayForward plays animation forward
func (a *animation) PlayForward() { func (a *Animation) PlayForward() {
a.playMode = playModeForward a.playMode = playModeForward
a.lastFrameTime = 0 a.lastFrameTime = 0
} }
// PlayBackward plays animation backward // PlayBackward plays animation backward
func (a *animation) PlayBackward() { func (a *Animation) PlayBackward() {
a.playMode = playModeBackward a.playMode = playModeBackward
a.lastFrameTime = 0 a.lastFrameTime = 0
} }
// Pause animation // Pause animation
func (a *animation) Pause() { func (a *Animation) Pause() {
a.playMode = playModePause a.playMode = playModePause
a.lastFrameTime = 0 a.lastFrameTime = 0
} }
// SetPlayLoop sets whether to loop the animation // SetPlayLoop sets whether to loop the animation
func (a *animation) SetPlayLoop(loop bool) { func (a *Animation) SetPlayLoop(loop bool) {
a.playLoop = loop a.playLoop = loop
} }
// SetPlaySpeed sets play speed of the animation // SetPlaySpeed sets play speed of the animation
func (a *animation) SetPlaySpeed(playSpeed float64) { func (a *Animation) SetPlaySpeed(playSpeed float64) {
a.SetPlayLength(playSpeed * float64(a.GetFrameCount())) a.SetPlayLength(playSpeed * float64(a.GetFrameCount()))
} }
// SetPlayLength sets the Animation's play length in seconds // SetPlayLength sets the Animation's play length in seconds
func (a *animation) SetPlayLength(playLength float64) { func (a *Animation) SetPlayLength(playLength float64) {
// TODO refactor to use time.Duration instead of float64 // TODO refactor to use time.Duration instead of float64
a.playLength = playLength a.playLength = playLength
a.lastFrameTime = 0 a.lastFrameTime = 0
} }
// SetPlayLengthMs sets the Animation's play length in milliseconds // SetPlayLengthMs sets the Animation's play length in milliseconds
func (a *animation) SetPlayLengthMs(playLengthMs int) { func (a *Animation) SetPlayLengthMs(playLengthMs int) {
// TODO remove this method // TODO remove this method
const millisecondsPerSecond = 1000.0 const millisecondsPerSecond = 1000.0
@ -320,24 +347,24 @@ func (a *animation) SetPlayLengthMs(playLengthMs int) {
} }
// SetColorMod sets the Animation's color mod // SetColorMod sets the Animation's color mod
func (a *animation) SetColorMod(colorMod color.Color) { func (a *Animation) SetColorMod(colorMod color.Color) {
a.colorMod = colorMod a.colorMod = colorMod
} }
// GetPlayedCount gets the number of times the application played // GetPlayedCount gets the number of times the application played
func (a *animation) GetPlayedCount() int { func (a *Animation) GetPlayedCount() int {
return a.playedCount return a.playedCount
} }
// ResetPlayedCount resets the play count // ResetPlayedCount resets the play count
func (a *animation) ResetPlayedCount() { func (a *Animation) ResetPlayedCount() {
a.playedCount = 0 a.playedCount = 0
} }
func (a *animation) SetEffect(e d2enum.DrawEffect) { func (a *Animation) SetEffect(e d2enum.DrawEffect) {
a.effect = e a.effect = e
} }
func (a *animation) SetShadow(shadow bool) { func (a *Animation) SetShadow(shadow bool) {
a.hasShadow = shadow a.hasShadow = shadow
} }

View File

@ -1,7 +1,6 @@
package d2asset package d2asset
import ( import (
"encoding/binary"
"fmt" "fmt"
"image/color" "image/color"
"log" "log"
@ -32,7 +31,6 @@ const (
// AssetManager loads files and game objects // AssetManager loads files and game objects
type AssetManager struct { type AssetManager struct {
renderer d2interface.Renderer
loader *d2loader.Loader loader *d2loader.Loader
tables d2interface.Cache tables d2interface.Cache
animations d2interface.Cache animations d2interface.Cache
@ -80,12 +78,12 @@ func (am *AssetManager) FileExists(filePath string) (bool, error) {
return true, nil return true, nil
} }
// LoadAnimation loads an animation by its resource path and its palette path // LoadAnimation loads an Animation by its resource path and its palette path
func (am *AssetManager) LoadAnimation(animationPath, palettePath string) (d2interface.Animation, error) { func (am *AssetManager) LoadAnimation(animationPath, palettePath string) (d2interface.Animation, error) {
return am.LoadAnimationWithEffect(animationPath, palettePath, d2enum.DrawEffectNone) return am.LoadAnimationWithEffect(animationPath, palettePath, d2enum.DrawEffectNone)
} }
// LoadAnimationWithEffect loads an animation by its resource path and its palette path with a given transparency value // LoadAnimationWithEffect loads an Animation by its resource path and its palette path with a given transparency value
func (am *AssetManager) LoadAnimationWithEffect(animationPath, palettePath string, func (am *AssetManager) LoadAnimationWithEffect(animationPath, palettePath string,
effect d2enum.DrawEffect) (d2interface.Animation, error) { effect d2enum.DrawEffect) (d2interface.Animation, error) {
cachePath := fmt.Sprintf("%s;%s;%d", animationPath, palettePath, effect) cachePath := fmt.Sprintf("%s;%s;%d", animationPath, palettePath, effect)
@ -108,17 +106,17 @@ func (am *AssetManager) LoadAnimationWithEffect(animationPath, palettePath strin
switch animAsset.Type() { switch animAsset.Type() {
case types.AssetTypeDC6: case types.AssetTypeDC6:
animation, err = am.createDC6Animation(animationPath, palette, effect) animation, err = am.loadDC6(animationPath, palette, effect)
if err != nil { if err != nil {
return nil, err return nil, err
} }
case types.AssetTypeDCC: case types.AssetTypeDCC:
animation, err = am.createDCCAnimation(animationPath, palette, effect) animation, err = am.loadDCC(animationPath, palette, effect)
if err != nil { if err != nil {
return nil, err return nil, err
} }
default: default:
return nil, fmt.Errorf("unknown animation format for file: %s", animAsset.Path()) return nil, fmt.Errorf("unknown Animation format for file: %s", animAsset.Path())
} }
err = am.animations.Insert(cachePath, animation, defaultCacheEntryWeight) err = am.animations.Insert(cachePath, animation, defaultCacheEntryWeight)
@ -140,11 +138,11 @@ func (am *AssetManager) LoadComposite(baseType d2enum.ObjectType, token, palette
} }
// LoadFont loads a font the resource files // LoadFont loads a font the resource files
func (am *AssetManager) LoadFont(tablePath, spritePath, palettePath string) (d2interface.Font, error) { func (am *AssetManager) LoadFont(tablePath, spritePath, palettePath string) (*Font, error) {
cachePath := fmt.Sprintf("%s;%s;%s", tablePath, spritePath, palettePath) cachePath := fmt.Sprintf("%s;%s;%s", tablePath, spritePath, palettePath)
if cached, found := am.fonts.Retrieve(cachePath); found { if cached, found := am.fonts.Retrieve(cachePath); found {
return cached.(d2interface.Font), nil return cached.(*Font), nil
} }
sheet, err := am.LoadAnimation(spritePath, palettePath) sheet, err := am.LoadAnimation(spritePath, palettePath)
@ -161,25 +159,10 @@ func (am *AssetManager) LoadFont(tablePath, spritePath, palettePath string) (d2i
return nil, fmt.Errorf("invalid font table format: %s", tablePath) return nil, fmt.Errorf("invalid font table format: %s", tablePath)
} }
_, maxCharHeight := sheet.GetFrameBounds()
glyphs := make(map[rune]fontGlyph)
for i := 12; i < len(tableData); i += 14 {
code := rune(binary.LittleEndian.Uint16(tableData[i : i+2]))
var glyph fontGlyph
glyph.frame = int(binary.LittleEndian.Uint16(tableData[i+8 : i+10]))
glyph.width = int(tableData[i+3])
glyph.height = maxCharHeight
glyphs[code] = glyph
}
font := &Font{ font := &Font{
sheet: sheet, table: tableData,
glyphs: glyphs, sheet: sheet,
color: color.White, color: color.White,
} }
err = am.fonts.Insert(cachePath, font, defaultCacheEntryWeight) err = am.fonts.Insert(cachePath, font, defaultCacheEntryWeight)
@ -229,7 +212,7 @@ func (am *AssetManager) LoadStringTable(tablePath string) (d2tbl.TextDictionary,
} }
table := d2tbl.LoadTextDictionary(data) table := d2tbl.LoadTextDictionary(data)
if table != nil { if table == nil {
return nil, fmt.Errorf("table not found: %s", tablePath) return nil, fmt.Errorf("table not found: %s", tablePath)
} }
@ -261,66 +244,9 @@ func (am *AssetManager) LoadPaletteTransform(path string) (*d2pl2.PL2, error) {
return pl2, nil return pl2, nil
} }
// createDC6Animation creates an Animation from d2dc6.DC6 and d2dat.DATPalette // loadDC6 creates an Animation from d2dc6.DC6 and d2dat.DATPalette
func (am *AssetManager) createDC6Animation(dc6Path string, func (am *AssetManager) loadDC6(path string,
palette d2interface.Palette, effect d2enum.DrawEffect) (d2interface.Animation, error) { palette d2interface.Palette, effect d2enum.DrawEffect) (d2interface.Animation, error) {
dc6, err := am.loadDC6(dc6Path)
if err != nil {
return nil, err
}
anim := DC6Animation{
animation: animation{
directions: make([]animationDirection, dc6.Directions),
playLength: defaultPlayLength,
playLoop: true,
originAtBottom: true,
effect: effect,
},
dc6Path: dc6Path,
dc6: dc6,
palette: palette,
renderer: am.renderer,
}
err = anim.SetDirection(0)
return &anim, err
}
// createDCCAnimation creates an animation from d2dcc.DCC and d2dat.DATPalette
func (am *AssetManager) createDCCAnimation(dccPath string,
palette d2interface.Palette,
effect d2enum.DrawEffect) (d2interface.Animation, error) {
dcc, err := am.loadDCC(dccPath)
if err != nil {
return nil, err
}
anim := animation{
playLength: defaultPlayLength,
playLoop: true,
directions: make([]animationDirection, dcc.NumberOfDirections),
effect: effect,
}
DCC := DCCAnimation{
animation: anim,
AssetManager: am,
dccPath: dccPath,
palette: palette,
renderer: am.renderer,
}
err = DCC.SetDirection(0)
if err != nil {
return nil, err
}
return &DCC, nil
}
func (am *AssetManager) loadDC6(path string) (*d2dc6.DC6, error) {
dc6Data, err := am.LoadFile(path) dc6Data, err := am.LoadFile(path)
if err != nil { if err != nil {
return nil, err return nil, err
@ -331,16 +257,27 @@ func (am *AssetManager) loadDC6(path string) (*d2dc6.DC6, error) {
return nil, err return nil, err
} }
return dc6, nil animation, err := newDC6Animation(dc6, palette, effect)
return animation, err
} }
func (am *AssetManager) loadDCC(path string) (*d2dcc.DCC, error) { // loadDCC creates an Animation from d2dcc.DCC and d2dat.DATPalette
func (am *AssetManager) loadDCC(path string,
palette d2interface.Palette, effect d2enum.DrawEffect) (d2interface.Animation, error) {
dccData, err := am.LoadFile(path) dccData, err := am.LoadFile(path)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return d2dcc.Load(dccData) dcc, err := d2dcc.Load(dccData)
if err != nil {
return nil, err
}
animation, err := newDCCAnimation(dcc, palette, effect)
return animation, nil
} }
// BindTerminalCommands binds the in-game terminal comands for the asset manager. // BindTerminalCommands binds the in-game terminal comands for the asset manager.
@ -368,7 +305,7 @@ func (am *AssetManager) BindTerminalCommands(term d2interface.Terminal) error {
term.OutputInfof("palette cache: %f", cacheStatistics(am.palettes)) term.OutputInfof("palette cache: %f", cacheStatistics(am.palettes))
term.OutputInfof("palette transform cache: %f", cacheStatistics(am.transforms)) term.OutputInfof("palette transform cache: %f", cacheStatistics(am.transforms))
term.OutputInfof("animation cache: %f", cacheStatistics(am.animations)) term.OutputInfof("Animation cache: %f", cacheStatistics(am.animations))
term.OutputInfof("font cache: %f", cacheStatistics(am.fonts)) term.OutputInfof("font cache: %f", cacheStatistics(am.fonts))
}); err != nil { }); err != nil {
return err return err

View File

@ -63,6 +63,7 @@ func (c *Composite) Render(target d2interface.Surface) error {
for _, layerIndex := range c.mode.cof.Priority[direction][c.mode.frameIndex] { for _, layerIndex := range c.mode.cof.Priority[direction][c.mode.frameIndex] {
layer := c.mode.layers[layerIndex] layer := c.mode.layers[layerIndex]
if layer != nil { if layer != nil {
if err := layer.RenderFromOrigin(target, true); err != nil { if err := layer.RenderFromOrigin(target, true); err != nil {
return err return err
@ -145,6 +146,10 @@ func (c *Composite) SetAnimSpeed(speed int) {
// SetDirection sets the direction of the composite and its layers // SetDirection sets the direction of the composite and its layers
func (c *Composite) SetDirection(direction int) { func (c *Composite) SetDirection(direction int) {
if c.mode == nil {
return
}
c.direction = direction c.direction = direction
for layerIdx := range c.mode.layers { for layerIdx := range c.mode.layers {
layer := c.mode.layers[layerIdx] layer := c.mode.layers[layerIdx]
@ -241,7 +246,7 @@ func (c *Composite) createMode(animationMode animationMode, weaponClass string)
animationData := d2data.AnimationData[animationKey] animationData := d2data.AnimationData[animationKey]
if len(animationData) == 0 { if len(animationData) == 0 {
return nil, errors.New("could not find animation data") return nil, errors.New("could not find Animation data")
} }
mode := &compositeMode{ mode := &compositeMode{
@ -300,7 +305,7 @@ func (c *Composite) loadCompositeLayer(layerKey, layerValue, animationMode, weap
} }
} }
return nil, errors.New("animation not found") return nil, errors.New("Animation not found")
} }
// GetSize returns the size of the composite // GetSize returns the size of the composite

View File

@ -2,16 +2,13 @@ package d2asset
import ( import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2cache" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2cache"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2loader" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2loader"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2config" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2config"
) )
// NewAssetManager creates and assigns all necessary dependencies for the AssetManager top-level functions to work correctly // NewAssetManager creates and assigns all necessary dependencies for the AssetManager top-level functions to work correctly
func NewAssetManager(renderer d2interface.Renderer, config *d2config.Configuration, func NewAssetManager(config *d2config.Configuration) (*AssetManager, error) {
term d2interface.Terminal) (*AssetManager, error) {
manager := &AssetManager{ manager := &AssetManager{
renderer,
d2loader.NewLoader(config), d2loader.NewLoader(config),
d2cache.CreateCache(animationBudget), d2cache.CreateCache(animationBudget),
d2cache.CreateCache(tableBudget), d2cache.CreateCache(tableBudget),
@ -20,10 +17,5 @@ func NewAssetManager(renderer d2interface.Renderer, config *d2config.Configurati
d2cache.CreateCache(paletteTransformBudget), d2cache.CreateCache(paletteTransformBudget),
} }
if term != nil {
err := manager.BindTerminalCommands(term)
return manager, err
}
return manager, nil return manager, nil
} }

View File

@ -3,9 +3,9 @@ package d2asset
import ( import (
"errors" "errors"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dc6" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dc6"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dcc" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dcc"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
@ -14,13 +14,58 @@ import (
var _ d2interface.Animation = &DC6Animation{} // Static check to confirm struct conforms to var _ d2interface.Animation = &DC6Animation{} // Static check to confirm struct conforms to
// interface // interface
func newDC6Animation(
dc6 *d2dc6.DC6,
pal d2interface.Palette,
effect d2enum.DrawEffect,
) (d2interface.Animation, error) {
DC6 := &DC6Animation{
dc6: dc6,
palette: pal,
}
anim := Animation{
playLength: defaultPlayLength,
playLoop: true,
originAtBottom: true,
effect: effect,
onBindRenderer: func(r d2interface.Renderer) error {
if DC6.renderer != r {
DC6.renderer = r
return DC6.createSurfaces()
}
return nil
},
}
DC6.Animation = anim
err := DC6.init()
if err != nil {
return nil, err
}
return DC6, nil
}
// DC6Animation is an animation made from a DC6 file // DC6Animation is an animation made from a DC6 file
type DC6Animation struct { type DC6Animation struct {
animation Animation
dc6Path string dc6 *d2dc6.DC6
dc6 *d2dc6.DC6 palette d2interface.Palette
palette d2interface.Palette }
renderer d2interface.Renderer
func (a *DC6Animation) init() error {
a.directions = make([]animationDirection, a.dc6.Directions)
for directionIndex := range a.directions {
a.directions[directionIndex].frames = make([]animationFrame, a.dc6.FramesPerDirection)
}
err := a.decode()
return err
} }
// SetDirection decodes and sets the direction // SetDirection decodes and sets the direction
@ -31,7 +76,8 @@ func (a *DC6Animation) SetDirection(directionIndex int) error {
} }
direction := d2dcc.Dir64ToDcc(directionIndex, len(a.directions)) direction := d2dcc.Dir64ToDcc(directionIndex, len(a.directions))
if !a.directions[direction].decoded {
if !a.directions[directionIndex].decoded {
err := a.decodeDirection(direction) err := a.decodeDirection(direction)
if err != nil { if err != nil {
return err return err
@ -39,44 +85,115 @@ func (a *DC6Animation) SetDirection(directionIndex int) error {
} }
a.directionIndex = direction a.directionIndex = direction
a.frameIndex = 0
return nil
}
func (a *DC6Animation) decode() error {
for directionIndex := 0; directionIndex < len(a.directions); directionIndex++ {
err := a.decodeDirection(directionIndex)
if err != nil {
return err
}
}
return nil return nil
} }
func (a *DC6Animation) decodeDirection(directionIndex int) error { func (a *DC6Animation) decodeDirection(directionIndex int) error {
dc6 := a.dc6 for frameIndex := 0; frameIndex < int(a.dc6.FramesPerDirection); frameIndex++ {
startFrame := directionIndex * int(dc6.FramesPerDirection) frame, err := a.decodeFrame(directionIndex, frameIndex)
for i := 0; i < int(dc6.FramesPerDirection); i++ {
dc6Frame := dc6.Frames[startFrame+i]
sfc, err := a.renderer.NewSurface(int(dc6Frame.Width), int(dc6Frame.Height),
d2enum.FilterNearest)
if err != nil { if err != nil {
return err return err
} }
indexData := dc6.DecodeFrame(startFrame + i) a.directions[directionIndex].frames[frameIndex] = frame
colorData := d2util.ImgIndexToRGBA(indexData, a.palette) }
if err := sfc.ReplacePixels(colorData); err != nil { a.directions[directionIndex].decoded = true
return nil
}
func (a *DC6Animation) decodeFrame(directionIndex, frameIndex int) (animationFrame, error) {
startFrame := directionIndex * int(a.dc6.FramesPerDirection)
dc6Frame := a.dc6.Frames[startFrame+frameIndex]
frame := animationFrame{
width: int(dc6Frame.Width),
height: int(dc6Frame.Height),
offsetX: int(dc6Frame.OffsetX),
offsetY: int(dc6Frame.OffsetY),
}
a.directions[directionIndex].frames[frameIndex].decoded = true
return frame, nil
}
func (a *DC6Animation) createSurfaces() error {
for directionIndex := 0; directionIndex < len(a.directions); directionIndex++ {
err := a.createDirectionSurfaces(directionIndex)
if err != nil {
return err return err
} }
a.directions[directionIndex].decoded = true
a.directions[directionIndex].frames = append(a.directions[directionIndex].frames, &animationFrame{
width: int(dc6Frame.Width),
height: int(dc6Frame.Height),
offsetX: int(dc6Frame.OffsetX),
offsetY: int(dc6Frame.OffsetY),
image: sfc,
})
} }
return nil return nil
} }
func (a *DC6Animation) createDirectionSurfaces(directionIndex int) error {
for frameIndex := 0; frameIndex < int(a.dc6.FramesPerDirection); frameIndex++ {
if !a.directions[directionIndex].decoded {
err := a.decodeDirection(directionIndex)
if err != nil {
return err
}
}
surface, err := a.createFrameSurface(directionIndex, frameIndex)
if err != nil {
return err
}
a.directions[directionIndex].frames[frameIndex].image = surface
}
return nil
}
func (a *DC6Animation) createFrameSurface(directionIndex, frameIndex int) (d2interface.Surface, error) {
if !a.directions[directionIndex].frames[frameIndex].decoded {
frame, err := a.decodeFrame(directionIndex, frameIndex)
if err != nil {
return nil, err
}
a.directions[directionIndex].frames[frameIndex] = frame
}
startFrame := directionIndex * int(a.dc6.FramesPerDirection)
dc6Frame := a.dc6.Frames[startFrame+frameIndex]
indexData := a.dc6.DecodeFrame(startFrame + frameIndex)
colorData := d2util.ImgIndexToRGBA(indexData, a.palette)
if a.renderer == nil {
return nil, errors.New("no renderer")
}
sfc, err := a.renderer.NewSurface(int(dc6Frame.Width), int(dc6Frame.Height), d2enum.FilterNearest)
if err != nil {
return nil, err
}
if err := sfc.ReplacePixels(colorData); err != nil {
return nil, err
}
return sfc, nil
}
// Clone creates a copy of the animation // Clone creates a copy of the animation
func (a *DC6Animation) Clone() d2interface.Animation { func (a *DC6Animation) Clone() d2interface.Animation {
animation := *a animation := *a

View File

@ -4,10 +4,11 @@ import (
"errors" "errors"
"math" "math"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2math"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2math"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dcc" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dcc"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
@ -16,13 +17,57 @@ import (
var _ d2interface.Animation = &DCCAnimation{} // Static check to confirm struct conforms to var _ d2interface.Animation = &DCCAnimation{} // Static check to confirm struct conforms to
// interface // interface
func newDCCAnimation(
dcc *d2dcc.DCC,
pal d2interface.Palette,
effect d2enum.DrawEffect,
) (d2interface.Animation, error) {
DCC := &DCCAnimation{
dcc: dcc,
palette: pal,
}
anim := Animation{
playLength: defaultPlayLength,
playLoop: true,
effect: effect,
onBindRenderer: func(r d2interface.Renderer) error {
if DCC.renderer != r {
DCC.renderer = r
return DCC.createSurfaces()
}
return nil
},
}
DCC.Animation = anim
err := DCC.init()
if err != nil {
return nil, err
}
return DCC, nil
}
// DCCAnimation represents an animation decoded from DCC // DCCAnimation represents an animation decoded from DCC
type DCCAnimation struct { type DCCAnimation struct {
animation Animation
*AssetManager dcc *d2dcc.DCC
dccPath string palette d2interface.Palette
palette d2interface.Palette }
renderer d2interface.Renderer
func (a *DCCAnimation) init() error {
a.directions = make([]animationDirection, a.dcc.NumberOfDirections)
for directionIndex := range a.directions {
a.directions[directionIndex].frames = make([]animationFrame, a.dcc.FramesPerDirection)
}
err := a.decode()
return err
} }
// Clone creates a copy of the animation // Clone creates a copy of the animation
@ -52,18 +97,45 @@ func (a *DCCAnimation) SetDirection(directionIndex int) error {
return nil return nil
} }
func (a *DCCAnimation) decodeDirection(directionIndex int) error { func (a *DCCAnimation) decode() error {
dcc, err := a.loadDCC(a.dccPath) for directionIndex := 0; directionIndex < len(a.directions); directionIndex++ {
if err != nil { err := a.decodeDirection(directionIndex)
return err if err != nil {
return err
}
} }
direction := dcc.DecodeDirection(directionIndex) return nil
}
func (a *DCCAnimation) decodeDirection(directionIndex int) error {
dccDirection := a.dcc.Directions[directionIndex]
for frameIndex := range dccDirection.Frames {
if a.directions[directionIndex].frames == nil {
a.directions[directionIndex].frames = make([]animationFrame, a.dcc.FramesPerDirection)
}
a.directions[directionIndex].decoded = true
frame, err := a.decodeFrame(directionIndex, frameIndex)
if err != nil {
return err
}
a.directions[directionIndex].frames[frameIndex] = frame
}
return nil
}
func (a *DCCAnimation) decodeFrame(directionIndex, frameIndex int) (animationFrame, error) {
dccDirection := a.dcc.Directions[directionIndex]
minX, minY := math.MaxInt32, math.MaxInt32 minX, minY := math.MaxInt32, math.MaxInt32
maxX, maxY := math.MinInt32, math.MinInt32 maxX, maxY := math.MinInt32, math.MinInt32
for _, dccFrame := range direction.Frames { for _, dccFrame := range dccDirection.Frames {
minX = d2math.MinInt(minX, dccFrame.Box.Left) minX = d2math.MinInt(minX, dccFrame.Box.Left)
minY = d2math.MinInt(minY, dccFrame.Box.Top) minY = d2math.MinInt(minY, dccFrame.Box.Top)
maxX = d2math.MaxInt(maxX, dccFrame.Box.Right()) maxX = d2math.MaxInt(maxX, dccFrame.Box.Right())
@ -73,27 +145,80 @@ func (a *DCCAnimation) decodeDirection(directionIndex int) error {
frameWidth := maxX - minX frameWidth := maxX - minX
frameHeight := maxY - minY frameHeight := maxY - minY
for _, dccFrame := range direction.Frames { frame := animationFrame{
pixels := d2util.ImgIndexToRGBA(dccFrame.PixelData, a.palette) width: frameWidth,
height: frameHeight,
offsetX: minX,
offsetY: minY,
decoded: true,
}
sfc, err := a.renderer.NewSurface(frameWidth, frameHeight, d2enum.FilterNearest) return frame, nil
}
func (a *DCCAnimation) createSurfaces() error {
for directionIndex := 0; directionIndex < len(a.directions); directionIndex++ {
err := a.createDirectionSurfaces(directionIndex)
if err != nil { if err != nil {
return err return err
} }
if err := sfc.ReplacePixels(pixels); err != nil {
return err
}
a.directions[directionIndex].decoded = true
a.directions[directionIndex].frames = append(a.directions[directionIndex].frames, &animationFrame{
width: frameWidth,
height: frameHeight,
offsetX: minX,
offsetY: minY,
image: sfc,
})
} }
return nil return nil
} }
func (a *DCCAnimation) createDirectionSurfaces(directionIndex int) error {
for frameIndex := 0; frameIndex < int(a.dcc.FramesPerDirection); frameIndex++ {
if !a.directions[directionIndex].decoded {
err := a.decodeDirection(directionIndex)
if err != nil {
return err
}
}
surface, err := a.createFrameSurface(directionIndex, frameIndex)
if err != nil {
return err
}
a.directions[directionIndex].frames[frameIndex].image = surface
}
return nil
}
func (a *DCCAnimation) createFrameSurface(directionIndex, frameIndex int) (d2interface.Surface, error) {
if !a.directions[directionIndex].frames[frameIndex].decoded {
frame, err := a.decodeFrame(directionIndex, frameIndex)
if err != nil {
return nil, err
}
a.directions[directionIndex].frames[frameIndex] = frame
}
dccFrame := a.dcc.Directions[directionIndex].Frames[frameIndex]
animFrame := a.directions[directionIndex].frames[frameIndex]
indexData := dccFrame.PixelData
if len(indexData) != (animFrame.width * animFrame.height) {
return nil, errors.New("pixel data incorrect")
}
colorData := d2util.ImgIndexToRGBA(indexData, a.palette)
if a.renderer == nil {
return nil, errors.New("no renderer")
}
sfc, err := a.renderer.NewSurface(animFrame.width, animFrame.height, d2enum.FilterNearest)
if err != nil {
return nil, err
}
if err := sfc.ReplacePixels(colorData); err != nil {
return nil, err
}
return sfc, nil
}

View File

@ -1,6 +1,7 @@
package d2asset package d2asset
import ( import (
"encoding/binary"
"image/color" "image/color"
"strings" "strings"
@ -8,8 +9,6 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2math" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2math"
) )
var _ d2interface.Font = &Font{} // Static check to confirm struct conforms to interface
type fontGlyph struct { type fontGlyph struct {
frame int frame int
width int width int
@ -19,6 +18,7 @@ type fontGlyph struct {
// Font represents a displayable font // Font represents a displayable font
type Font struct { type Font struct {
sheet d2interface.Animation sheet d2interface.Animation
table []byte
glyphs map[rune]fontGlyph glyphs map[rune]fontGlyph
color color.Color color color.Color
} }
@ -30,17 +30,19 @@ func (f *Font) SetColor(c color.Color) {
// GetTextMetrics returns the dimensions of the Font element in pixels // GetTextMetrics returns the dimensions of the Font element in pixels
func (f *Font) GetTextMetrics(text string) (width, height int) { func (f *Font) GetTextMetrics(text string) (width, height int) {
if f.glyphs == nil {
f.initGlyphs()
}
var ( var (
lineWidth int lineWidth int
lineHeight int lineHeight int
totalWidth int
totalHeight int
) )
for _, c := range text { for _, c := range text {
if c == '\n' { if c == '\n' {
totalWidth = d2math.MaxInt(totalWidth, lineWidth) width = d2math.MaxInt(width, lineWidth)
totalHeight += lineHeight height += lineHeight
lineWidth = 0 lineWidth = 0
lineHeight = 0 lineHeight = 0
} else if glyph, ok := f.glyphs[c]; ok { } else if glyph, ok := f.glyphs[c]; ok {
@ -49,14 +51,23 @@ func (f *Font) GetTextMetrics(text string) (width, height int) {
} }
} }
totalWidth = d2math.MaxInt(totalWidth, lineWidth) width = d2math.MaxInt(width, lineWidth)
totalHeight += lineHeight height += lineHeight
return totalWidth, totalHeight return width, height
} }
// RenderText prints a text using its configured style on a Surface (multi-lines are left-aligned, use label otherwise) // RenderText prints a text using its configured style on a Surface (multi-lines are left-aligned, use label otherwise)
func (f *Font) RenderText(text string, target d2interface.Surface) error { func (f *Font) RenderText(text string, target d2interface.Surface) error {
if f.glyphs == nil {
err := f.sheet.BindRenderer(target.Renderer())
if err != nil {
return err
}
f.initGlyphs()
}
f.sheet.SetColorMod(f.color) f.sheet.SetColorMod(f.color)
lines := strings.Split(text, "\n") lines := strings.Split(text, "\n")
@ -95,3 +106,22 @@ func (f *Font) RenderText(text string, target d2interface.Surface) error {
return nil return nil
} }
func (f *Font) initGlyphs() {
_, maxCharHeight := f.sheet.GetFrameBounds()
glyphs := make(map[rune]fontGlyph)
for i := 12; i < len(f.table); i += 14 {
code := rune(binary.LittleEndian.Uint16(f.table[i : i+2]))
var glyph fontGlyph
glyph.frame = int(binary.LittleEndian.Uint16(f.table[i+8 : i+10]))
glyph.width = int(f.table[i+3])
glyph.height = maxCharHeight
glyphs[code] = glyph
}
f.glyphs = glyphs
}

View File

@ -4,11 +4,13 @@ import (
"errors" "errors"
"image/color" "image/color"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2math" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2math"
) )
func loadFont(fontStyle FontStyle) (d2interface.Font, error) { func loadFont(fontStyle FontStyle) (*d2asset.Font, error) {
config := getFontStyleConfig(fontStyle) config := getFontStyleConfig(fontStyle)
if config == nil { if config == nil {
return nil, errors.New("invalid font style") return nil, errors.New("invalid font style")

View File

@ -3,6 +3,7 @@ package d2gui
import ( import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
) )
// Label is renderable text // Label is renderable text
@ -11,7 +12,7 @@ type Label struct {
renderer d2interface.Renderer renderer d2interface.Renderer
text string text string
font d2interface.Font font *d2asset.Font
surface d2interface.Surface surface d2interface.Surface
} }

View File

@ -29,7 +29,7 @@ type Renderer struct {
// Update updates the screen with the given *ebiten.Image // Update updates the screen with the given *ebiten.Image
func (r *Renderer) Update(screen *ebiten.Image) error { func (r *Renderer) Update(screen *ebiten.Image) error {
err := r.renderCallback(createEbitenSurface(screen)) err := r.renderCallback(createEbitenSurface(r, screen))
if err != nil { if err != nil {
return err return err
} }
@ -88,19 +88,18 @@ func (r *Renderer) Run(f func(surface d2interface.Surface) error, width, height
// CreateSurface creates a renderer surface from an existing surface // CreateSurface creates a renderer surface from an existing surface
func (r *Renderer) CreateSurface(surface d2interface.Surface) (d2interface.Surface, error) { func (r *Renderer) CreateSurface(surface d2interface.Surface) (d2interface.Surface, error) {
result := createEbitenSurface( img := surface.(*ebitenSurface).image
surface.(*ebitenSurface).image, sfcState := surfaceState{
surfaceState{ filter: ebiten.FilterNearest,
filter: ebiten.FilterNearest, effect: d2enum.DrawEffectNone,
effect: d2enum.DrawEffectNone, saturation: defaultSaturation,
saturation: defaultSaturation, brightness: defaultBrightness,
brightness: defaultBrightness, skewX: defaultSkewX,
skewX: defaultSkewX, skewY: defaultSkewY,
skewY: defaultSkewY, scaleX: defaultScaleX,
scaleX: defaultScaleX, scaleY: defaultScaleY,
scaleY: defaultScaleY, }
}, result := createEbitenSurface(r, img, sfcState)
)
return result, nil return result, nil
} }
@ -114,7 +113,7 @@ func (r *Renderer) NewSurface(width, height int, filter d2enum.Filter) (d2interf
return nil, err return nil, err
} }
return createEbitenSurface(img), nil return createEbitenSurface(r, img), nil
} }
// IsFullScreen returns a boolean for whether or not the renderer is currently set to fullscreen // IsFullScreen returns a boolean for whether or not the renderer is currently set to fullscreen

View File

@ -30,6 +30,7 @@ type colorMCacheEntry struct {
} }
type ebitenSurface struct { type ebitenSurface struct {
renderer *Renderer
stateStack []surfaceState stateStack []surfaceState
stateCurrent surfaceState stateCurrent surfaceState
image *ebiten.Image image *ebiten.Image
@ -37,7 +38,7 @@ type ebitenSurface struct {
monotonicClock int64 monotonicClock int64
} }
func createEbitenSurface(img *ebiten.Image, currentState ...surfaceState) *ebitenSurface { func createEbitenSurface(r *Renderer, img *ebiten.Image, currentState ...surfaceState) *ebitenSurface {
state := surfaceState{ state := surfaceState{
effect: d2enum.DrawEffectNone, effect: d2enum.DrawEffectNone,
saturation: defaultSaturation, saturation: defaultSaturation,
@ -52,12 +53,18 @@ func createEbitenSurface(img *ebiten.Image, currentState ...surfaceState) *ebite
} }
return &ebitenSurface{ return &ebitenSurface{
renderer: r,
image: img, image: img,
stateCurrent: state, stateCurrent: state,
colorMCache: make(map[colorMCacheKey]*colorMCacheEntry), colorMCache: make(map[colorMCacheKey]*colorMCacheEntry),
} }
} }
// Renderer returns the renderer
func (s *ebitenSurface) Renderer() d2interface.Renderer {
return s.renderer
}
// PushTranslation pushes an x,y translation to the state stack // PushTranslation pushes an x,y translation to the state stack
func (s *ebitenSurface) PushTranslation(x, y int) { func (s *ebitenSurface) PushTranslation(x, y int) {
s.stateStack = append(s.stateStack, s.stateCurrent) s.stateStack = append(s.stateStack, s.stateCurrent)

View File

@ -191,8 +191,7 @@ func (ui *UIManager) NewButton(buttonType ButtonType, text string) *Button {
lbl.Color[0] = d2util.Color(greyAlpha100) lbl.Color[0] = d2util.Color(greyAlpha100)
lbl.Alignment = d2gui.HorizontalAlignCenter lbl.Alignment = d2gui.HorizontalAlignCenter
animation, _ := ui.asset.LoadAnimation(buttonLayout.ResourceName, buttonLayout.PaletteName) buttonSprite, _ := ui.NewSprite(buttonLayout.ResourceName, buttonLayout.PaletteName)
buttonSprite, _ := ui.NewSprite(animation)
for i := 0; i < buttonLayout.XSegments; i++ { for i := 0; i < buttonLayout.XSegments; i++ {
w, _, _ := buttonSprite.GetFrameSize(i) w, _, _ := buttonSprite.GetFrameSize(i)

View File

@ -31,8 +31,7 @@ func (ui *UIManager) NewCheckbox(checkState bool) *Checkbox {
enabled: true, enabled: true,
} }
animation, _ := ui.asset.LoadAnimation(d2resource.Checkbox, d2resource.PaletteFechar) checkboxSprite, _ := ui.NewSprite(d2resource.Checkbox, d2resource.PaletteFechar)
checkboxSprite, _ := ui.NewSprite(animation)
result.width, result.height, _ = checkboxSprite.GetFrameSize(0) result.width, result.height, _ = checkboxSprite.GetFrameSize(0)
checkboxSprite.SetPosition(0, 0) checkboxSprite.SetPosition(0, 0)

View File

@ -6,6 +6,8 @@ import (
"regexp" "regexp"
"strings" "strings"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui"
@ -18,7 +20,7 @@ type Label struct {
X int X int
Y int Y int
Alignment d2gui.HorizontalAlign Alignment d2gui.HorizontalAlign
font d2interface.Font font *d2asset.Font
Color map[int]color.Color Color map[int]color.Color
backgroundColor color.Color backgroundColor color.Color
} }
@ -26,6 +28,7 @@ type Label struct {
// NewLabel creates a new instance of a UI label // NewLabel creates a new instance of a UI label
func (ui *UIManager) NewLabel(fontPath, palettePath string) *Label { func (ui *UIManager) NewLabel(fontPath, palettePath string) *Label {
font, _ := ui.asset.LoadFont(fontPath+".tbl", fontPath+".dc6", palettePath) font, _ := ui.asset.LoadFont(fontPath+".tbl", fontPath+".dc6", palettePath)
result := &Label{ result := &Label{
Alignment: d2gui.HorizontalAlignLeft, Alignment: d2gui.HorizontalAlignLeft,
Color: map[int]color.Color{0: color.White}, Color: map[int]color.Color{0: color.White},

View File

@ -28,8 +28,7 @@ type Scrollbar struct {
// NewScrollbar creates a scrollbar instance // NewScrollbar creates a scrollbar instance
func (ui *UIManager) NewScrollbar(x, y, height int) *Scrollbar { func (ui *UIManager) NewScrollbar(x, y, height int) *Scrollbar {
animation, _ := ui.asset.LoadAnimation(d2resource.Scrollbar, d2resource.PaletteSky) scrollbarSprite, _ := ui.NewSprite(d2resource.Scrollbar, d2resource.PaletteSky)
scrollbarSprite, _ := ui.NewSprite(animation)
result := &Scrollbar{ result := &Scrollbar{
visible: true, visible: true,
enabled: true, enabled: true,

View File

@ -22,11 +22,17 @@ const (
) )
// NewSprite creates a new Sprite // NewSprite creates a new Sprite
func (ui *UIManager) NewSprite(animation d2interface.Animation) (*Sprite, error) { func (ui *UIManager) NewSprite(animationPath, palettePath string) (*Sprite, error) {
if animation == nil { animation, err := ui.asset.LoadAnimation(animationPath, palettePath)
if animation == nil || err != nil {
return nil, fmt.Errorf(errNoAnimation) return nil, fmt.Errorf(errNoAnimation)
} }
err = animation.BindRenderer(ui.renderer)
if err != nil {
return nil, err
}
return &Sprite{animation: animation}, nil return &Sprite{animation: animation}, nil
} }
@ -56,7 +62,8 @@ func (s *Sprite) RenderSegmented(target d2interface.Surface, segmentsX, segments
var currentX, maxFrameHeight int var currentX, maxFrameHeight int
for x := 0; x < segmentsX; x++ { for x := 0; x < segmentsX; x++ {
if err := s.animation.SetCurrentFrame(x + y*segmentsX + frameOffset*segmentsX*segmentsY); err != nil { idx := x + y*segmentsX + frameOffset*segmentsX*segmentsY
if err := s.animation.SetCurrentFrame(idx); err != nil {
return err return err
} }

View File

@ -26,8 +26,7 @@ type TextBox struct {
// NewTextbox creates a new instance of a text box // NewTextbox creates a new instance of a text box
func (ui *UIManager) NewTextbox() *TextBox { func (ui *UIManager) NewTextbox() *TextBox {
animation, _ := ui.asset.LoadAnimation(d2resource.TextBox2, d2resource.PaletteUnits) bgSprite, _ := ui.NewSprite(d2resource.TextBox2, d2resource.PaletteUnits)
bgSprite, _ := ui.NewSprite(animation)
tb := &TextBox{ tb := &TextBox{
filter: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", filter: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
bgSprite: bgSprite, bgSprite: bgSprite,

View File

@ -138,10 +138,8 @@ func (v *CharacterSelect) OnLoad(loading d2screen.LoadingState) {
loading.Progress(tenPercent) loading.Progress(tenPercent)
animation, _ := v.asset.LoadAnimation(d2resource.CharacterSelectionBackground,
d2resource.PaletteSky)
bgX, bgY := 0, 0 bgX, bgY := 0, 0
v.background, _ = v.uiManager.NewSprite(animation) v.background, _ = v.uiManager.NewSprite(d2resource.CharacterSelectionBackground, d2resource.PaletteSky)
v.background.SetPosition(bgX, bgY) v.background.SetPosition(bgX, bgY)
v.createButtons(loading) v.createButtons(loading)
@ -160,14 +158,11 @@ func (v *CharacterSelect) OnLoad(loading d2screen.LoadingState) {
deleteConfirmX, deleteConfirmY := 400, 185 deleteConfirmX, deleteConfirmY := 400, 185
v.deleteCharConfirmLabel.SetPosition(deleteConfirmX, deleteConfirmY) v.deleteCharConfirmLabel.SetPosition(deleteConfirmX, deleteConfirmY)
animation, _ = v.asset.LoadAnimation(d2resource.CharacterSelectionSelectBox, v.selectionBox, _ = v.uiManager.NewSprite(d2resource.CharacterSelectionSelectBox, d2resource.PaletteSky)
d2resource.PaletteSky)
v.selectionBox, _ = v.uiManager.NewSprite(animation)
selBoxX, selBoxY := 37, 86 selBoxX, selBoxY := 37, 86
v.selectionBox.SetPosition(selBoxX, selBoxY) v.selectionBox.SetPosition(selBoxX, selBoxY)
animation, _ = v.asset.LoadAnimation(d2resource.PopUpOkCancel, d2resource.PaletteFechar) v.okCancelBox, _ = v.uiManager.NewSprite(d2resource.PopUpOkCancel, d2resource.PaletteFechar)
v.okCancelBox, _ = v.uiManager.NewSprite(animation)
okCancelX, okCancelY := 270, 175 okCancelX, okCancelY := 270, 175
v.okCancelBox.SetPosition(okCancelX, okCancelY) v.okCancelBox.SetPosition(okCancelX, okCancelY)

View File

@ -89,8 +89,7 @@ func (v *Credits) LoadContributors() []string {
// OnLoad is called to load the resources for the credits screen // OnLoad is called to load the resources for the credits screen
func (v *Credits) OnLoad(loading d2screen.LoadingState) { func (v *Credits) OnLoad(loading d2screen.LoadingState) {
animation, _ := v.asset.LoadAnimation(d2resource.CreditsBackground, d2resource.PaletteSky) v.creditsBackground, _ = v.uiManager.NewSprite(d2resource.CreditsBackground, d2resource.PaletteSky)
v.creditsBackground, _ = v.uiManager.NewSprite(animation)
v.creditsBackground.SetPosition(creditsX, creditsY) v.creditsBackground.SetPosition(creditsX, creditsY)
loading.Progress(twentyPercent) loading.Progress(twentyPercent)

View File

@ -172,20 +172,16 @@ func (v *MainMenu) OnLoad(loading d2screen.LoadingState) {
} }
func (v *MainMenu) loadBackgroundSprites() { func (v *MainMenu) loadBackgroundSprites() {
animation, _ := v.asset.LoadAnimation(d2resource.GameSelectScreen, d2resource.PaletteSky) v.background, _ = v.uiManager.NewSprite(d2resource.GameSelectScreen, d2resource.PaletteSky)
v.background, _ = v.uiManager.NewSprite(animation)
v.background.SetPosition(backgroundX, backgroundY) v.background.SetPosition(backgroundX, backgroundY)
animation, _ = v.asset.LoadAnimation(d2resource.TrademarkScreen, d2resource.PaletteSky) v.trademarkBackground, _ = v.uiManager.NewSprite(d2resource.TrademarkScreen, d2resource.PaletteSky)
v.trademarkBackground, _ = v.uiManager.NewSprite(animation)
v.trademarkBackground.SetPosition(backgroundX, backgroundY) v.trademarkBackground.SetPosition(backgroundX, backgroundY)
animation, _ = v.asset.LoadAnimation(d2resource.TCPIPBackground, d2resource.PaletteSky) v.tcpIPBackground, _ = v.uiManager.NewSprite(d2resource.TCPIPBackground, d2resource.PaletteSky)
v.tcpIPBackground, _ = v.uiManager.NewSprite(animation)
v.tcpIPBackground.SetPosition(backgroundX, backgroundY) v.tcpIPBackground.SetPosition(backgroundX, backgroundY)
animation, _ = v.asset.LoadAnimation(d2resource.PopUpOkCancel, d2resource.PaletteFechar) v.serverIPBackground, _ = v.uiManager.NewSprite(d2resource.PopUpOkCancel, d2resource.PaletteFechar)
v.serverIPBackground, _ = v.uiManager.NewSprite(animation)
v.serverIPBackground.SetPosition(serverIPbackgroundX, serverIPbackgroundY) v.serverIPBackground.SetPosition(serverIPbackgroundX, serverIPbackgroundY)
} }
@ -236,25 +232,21 @@ func (v *MainMenu) createLabels(loading d2screen.LoadingState) {
} }
func (v *MainMenu) createLogos(loading d2screen.LoadingState) { func (v *MainMenu) createLogos(loading d2screen.LoadingState) {
animation, _ := v.asset.LoadAnimation(d2resource.Diablo2LogoFireLeft, d2resource.PaletteUnits) v.diabloLogoLeft, _ = v.uiManager.NewSprite(d2resource.Diablo2LogoFireLeft, d2resource.PaletteUnits)
v.diabloLogoLeft, _ = v.uiManager.NewSprite(animation)
v.diabloLogoLeft.SetEffect(d2enum.DrawEffectModulate) v.diabloLogoLeft.SetEffect(d2enum.DrawEffectModulate)
v.diabloLogoLeft.PlayForward() v.diabloLogoLeft.PlayForward()
v.diabloLogoLeft.SetPosition(diabloLogoX, diabloLogoY) v.diabloLogoLeft.SetPosition(diabloLogoX, diabloLogoY)
loading.Progress(sixtyPercent) loading.Progress(sixtyPercent)
animation, _ = v.asset.LoadAnimation(d2resource.Diablo2LogoFireRight, d2resource.PaletteUnits) v.diabloLogoRight, _ = v.uiManager.NewSprite(d2resource.Diablo2LogoFireRight, d2resource.PaletteUnits)
v.diabloLogoRight, _ = v.uiManager.NewSprite(animation)
v.diabloLogoRight.SetEffect(d2enum.DrawEffectModulate) v.diabloLogoRight.SetEffect(d2enum.DrawEffectModulate)
v.diabloLogoRight.PlayForward() v.diabloLogoRight.PlayForward()
v.diabloLogoRight.SetPosition(diabloLogoX, diabloLogoY) v.diabloLogoRight.SetPosition(diabloLogoX, diabloLogoY)
animation, _ = v.asset.LoadAnimation(d2resource.Diablo2LogoBlackLeft, d2resource.PaletteUnits) v.diabloLogoLeftBack, _ = v.uiManager.NewSprite(d2resource.Diablo2LogoBlackLeft, d2resource.PaletteUnits)
v.diabloLogoLeftBack, _ = v.uiManager.NewSprite(animation)
v.diabloLogoLeftBack.SetPosition(diabloLogoX, diabloLogoY) v.diabloLogoLeftBack.SetPosition(diabloLogoX, diabloLogoY)
animation, _ = v.asset.LoadAnimation(d2resource.Diablo2LogoBlackRight, d2resource.PaletteUnits) v.diabloLogoRightBack, _ = v.uiManager.NewSprite(d2resource.Diablo2LogoBlackRight, d2resource.PaletteUnits)
v.diabloLogoRightBack, _ = v.uiManager.NewSprite(animation)
v.diabloLogoRightBack.SetPosition(diabloLogoX, diabloLogoY) v.diabloLogoRightBack.SetPosition(diabloLogoX, diabloLogoY)
} }

View File

@ -734,29 +734,23 @@ func (v *SelectHeroClass) loadSprite(animationPath string, position image.Point,
return nil return nil
} }
animation, err := v.asset.LoadAnimation(animationPath, d2resource.PaletteFechar) sprite, err := v.uiManager.NewSprite(animationPath, d2resource.PaletteFechar)
if err != nil {
fmt.Printf("could not load animation: %s\n", animationPath)
return nil
}
animation.PlayForward()
animation.SetPlayLoop(playLoop)
if blend {
animation.SetEffect(d2enum.DrawEffectModulate)
}
if playLength != 0 {
animation.SetPlayLengthMs(playLength)
}
sprite, err := v.uiManager.NewSprite(animation)
if err != nil { if err != nil {
fmt.Printf("could not load sprite for the animation: %s\n", animationPath) fmt.Printf("could not load sprite for the animation: %s\n", animationPath)
return nil return nil
} }
sprite.PlayForward()
sprite.SetPlayLoop(playLoop)
if blend {
sprite.SetEffect(d2enum.DrawEffectModulate)
}
if playLength != 0 {
sprite.SetPlayLengthMs(playLength)
}
sprite.SetPosition(position.X, position.Y) sprite.SetPosition(position.X, position.Y)
return sprite return sprite

View File

@ -381,21 +381,16 @@ func (g *GameControls) OnMouseButtonDown(event d2interface.MouseEvent) bool {
// Load the resources required for the GameControls // Load the resources required for the GameControls
func (g *GameControls) Load() { func (g *GameControls) Load() {
animation, _ := g.asset.LoadAnimation(d2resource.GameGlobeOverlap, d2resource.PaletteSky) g.globeSprite, _ = g.uiManager.NewSprite(d2resource.GameGlobeOverlap, d2resource.PaletteSky)
g.globeSprite, _ = g.uiManager.NewSprite(animation)
animation, _ = g.asset.LoadAnimation(d2resource.HealthManaIndicator, d2resource.PaletteSky) g.hpManaStatusSprite, _ = g.uiManager.NewSprite(d2resource.HealthManaIndicator, d2resource.PaletteSky)
g.hpManaStatusSprite, _ = g.uiManager.NewSprite(animation)
animation, _ = g.asset.LoadAnimation(d2resource.GamePanels, d2resource.PaletteSky) g.mainPanel, _ = g.uiManager.NewSprite(d2resource.GamePanels, d2resource.PaletteSky)
g.mainPanel, _ = g.uiManager.NewSprite(animation)
animation, _ = g.asset.LoadAnimation(d2resource.MenuButton, d2resource.PaletteSky) g.menuButton, _ = g.uiManager.NewSprite(d2resource.MenuButton, d2resource.PaletteSky)
_ = animation.SetCurrentFrame(2) _ = g.menuButton.SetCurrentFrame(2)
g.menuButton, _ = g.uiManager.NewSprite(animation)
animation, _ = g.asset.LoadAnimation(d2resource.GenericSkills, d2resource.PaletteSky) g.skillIcon, _ = g.uiManager.NewSprite(d2resource.GenericSkills, d2resource.PaletteSky)
g.skillIcon, _ = g.uiManager.NewSprite(animation)
g.loadUIButtons() g.loadUIButtons()

View File

@ -94,9 +94,8 @@ func (h *Overlay) Load() {
prevY = 0 prevY = 0
) )
for frameIndex := 0; frameIndex < 7; frameIndex++ { for frameIndex := 0; frameIndex < 7; frameIndex++ {
animation, _ := h.asset.LoadAnimation(d2resource.HelpBorder, d2resource.PaletteSky) f, _ := h.uiManager.NewSprite(d2resource.HelpBorder, d2resource.PaletteSky)
_ = animation.SetCurrentFrame(frameIndex) _ = f.SetCurrentFrame(frameIndex)
f, _ := h.uiManager.NewSprite(animation)
ww, hh := f.GetCurrentFrameSize() ww, hh := f.GetCurrentFrameSize()
//fmt.Printf("Help frame %d size: %d, %d\n", frameIndex, ww, hh) //fmt.Printf("Help frame %d size: %d, %d\n", frameIndex, ww, hh)
@ -143,9 +142,7 @@ func (h *Overlay) Load() {
h.text = append(h.text, newLabel) h.text = append(h.text, newLabel)
// Close // Close
close, _ := h.uiManager.NewSprite(d2resource.SquareButton, d2resource.PaletteSky)
anim, _ := h.asset.LoadAnimation(d2resource.SquareButton, d2resource.PaletteSky)
close, _ := h.uiManager.NewSprite(anim)
_ = close.SetCurrentFrame(0) _ = close.SetCurrentFrame(0)
close.SetPosition(685, 57) close.SetPosition(685, 57)
h.frames = append(h.frames, close) h.frames = append(h.frames, close)
@ -344,8 +341,7 @@ func (h *Overlay) createBullet(c callout) {
newLabel.SetPosition(c.LabelX, c.LabelY) newLabel.SetPosition(c.LabelX, c.LabelY)
h.text = append(h.text, newLabel) h.text = append(h.text, newLabel)
anim, _ := h.asset.LoadAnimation(d2resource.HelpYellowBullet, d2resource.PaletteSky) newDot, _ := h.uiManager.NewSprite(d2resource.HelpYellowBullet, d2resource.PaletteSky)
newDot, _ := h.uiManager.NewSprite(anim)
_ = newDot.SetCurrentFrame(0) _ = newDot.SetCurrentFrame(0)
newDot.SetPosition(c.DotX, c.DotY+14) newDot.SetPosition(c.DotX, c.DotY+14)
h.frames = append(h.frames, newDot) h.frames = append(h.frames, newDot)
@ -370,8 +366,7 @@ func (h *Overlay) createCallout(c callout) {
} }
h.lines = append(h.lines, l) h.lines = append(h.lines, l)
anim, _ := h.asset.LoadAnimation(d2resource.HelpWhiteBullet, d2resource.PaletteSky) newDot, _ := h.uiManager.NewSprite(d2resource.HelpWhiteBullet, d2resource.PaletteSky)
newDot, _ := h.uiManager.NewSprite(anim)
_ = newDot.SetCurrentFrame(0) _ = newDot.SetCurrentFrame(0)
newDot.SetPosition(c.DotX, c.DotY) newDot.SetPosition(c.DotX, c.DotY)
h.frames = append(h.frames, newDot) h.frames = append(h.frames, newDot)

View File

@ -78,10 +78,8 @@ func NewHeroStatsPanel(asset *d2asset.AssetManager, ui *d2ui.UIManager, heroName
// Load the data for the hero status panel // Load the data for the hero status panel
func (s *HeroStatsPanel) Load() { func (s *HeroStatsPanel) Load() {
animation, _ := s.asset.LoadAnimation(d2resource.Frame, d2resource.PaletteSky) s.frame, _ = s.uiManager.NewSprite(d2resource.Frame, d2resource.PaletteSky)
s.frame, _ = s.uiManager.NewSprite(animation) s.panel, _ = s.uiManager.NewSprite(d2resource.InventoryCharacterPanel, d2resource.PaletteSky)
animation, _ = s.asset.LoadAnimation(d2resource.InventoryCharacterPanel, d2resource.PaletteSky)
s.panel, _ = s.uiManager.NewSprite(animation)
s.initStatValueLabels() s.initStatValueLabels()
} }

View File

@ -71,11 +71,9 @@ func (g *Inventory) Close() {
// Load the resources required by the inventory // Load the resources required by the inventory
func (g *Inventory) Load() { func (g *Inventory) Load() {
animation, _ := g.asset.LoadAnimation(d2resource.Frame, d2resource.PaletteSky) g.frame, _ = g.uiManager.NewSprite(d2resource.Frame, d2resource.PaletteSky)
g.frame, _ = g.uiManager.NewSprite(animation)
animation, _ = g.asset.LoadAnimation(d2resource.InventoryCharacterPanel, d2resource.PaletteSky) g.panel, _ = g.uiManager.NewSprite(d2resource.InventoryCharacterPanel, d2resource.PaletteSky)
g.panel, _ = g.uiManager.NewSprite(animation)
items := []InventoryItem{ items := []InventoryItem{
diablo2item.NewItem("kit", "Crimson", "of the Bat", "of Frost").Identify(), diablo2item.NewItem("kit", "Crimson", "of the Bat", "of Frost").Identify(),
diablo2item.NewItem("rin", "Steel", "of Shock").Identify(), diablo2item.NewItem("rin", "Steel", "of Shock").Identify(),

View File

@ -121,17 +121,8 @@ func (g *ItemGrid) loadItem(item InventoryItem) {
var itemSprite *d2ui.Sprite var itemSprite *d2ui.Sprite
// TODO: Put the pattern into D2Shared // TODO: Put the pattern into D2Shared
animation, err := g.asset.LoadAnimation( imgPath := fmt.Sprintf("/data/global/items/inv%s.dc6", item.GetItemCode())
fmt.Sprintf("/data/global/items/inv%s.dc6", item.GetItemCode()), itemSprite, err := g.uiManager.NewSprite(imgPath, d2resource.PaletteSky)
d2resource.PaletteSky,
)
if err != nil {
log.Printf("failed to load sprite for item (%s): %v", item.GetItemCode(), err)
return
}
itemSprite, err = g.uiManager.NewSprite(animation)
if err != nil { if err != nil {
log.Printf("Failed to load sprite, error: " + err.Error()) log.Printf("Failed to load sprite, error: " + err.Error())
} }

View File

@ -23,11 +23,9 @@ func newMiniPanel(asset *d2asset.AssetManager, uiManager *d2ui.UIManager, isSing
miniPanelContainerPath = d2resource.MinipanelSmall miniPanelContainerPath = d2resource.MinipanelSmall
} }
animation, _ := asset.LoadAnimation(miniPanelContainerPath, d2resource.PaletteSky) containerSprite, _ := uiManager.NewSprite(miniPanelContainerPath, d2resource.PaletteSky)
containerSprite, _ := uiManager.NewSprite(animation)
animation, _ = asset.LoadAnimation(d2resource.MinipanelButton, d2resource.PaletteSky) buttonSprite, _ := uiManager.NewSprite(d2resource.MinipanelButton, d2resource.PaletteSky)
buttonSprite, _ := uiManager.NewSprite(animation)
rectangle := d2geom.Rectangle{Left: 325, Top: 526, Width: 156, Height: 26} rectangle := d2geom.Rectangle{Left: 325, Top: 526, Width: 156, Height: 26}

View File

@ -35,7 +35,7 @@ func main() {
panic(err) panic(err)
} }
asset, err := d2asset.NewAssetManager(renderer, d2config.Config, nil) asset, err := d2asset.NewAssetManager(d2config.Config)
if err != nil { if err != nil {
panic(err) panic(err)
} }