diff --git a/d2common/d2fileformats/d2dcc/dcc.go b/d2common/d2fileformats/d2dcc/dcc.go index 7a1cdb2b..36b524d6 100644 --- a/d2common/d2fileformats/d2dcc/dcc.go +++ b/d2common/d2fileformats/d2dcc/dcc.go @@ -15,6 +15,7 @@ type DCC struct { Version int NumberOfDirections int FramesPerDirection int + Directions []*DCCDirection directionOffsets []int fileData []byte } @@ -37,6 +38,8 @@ func Load(fileData []byte) (*DCC, error) { result.NumberOfDirections = int(bm.GetByte()) result.FramesPerDirection = int(bm.GetInt32()) + result.Directions = make([]*DCCDirection, result.NumberOfDirections) + if bm.GetInt32() != 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++ { result.directionOffsets[i] = int(bm.GetInt32()) + result.Directions[i] = result.decodeDirection(i) } return result, nil } -// DecodeDirection decodes and returns the given direction -func (dcc *DCC) DecodeDirection(direction int) *DCCDirection { +// decodeDirection decodes and returns the given direction +func (dcc *DCC) decodeDirection(direction int) *DCCDirection { return CreateDCCDirection(d2datautils.CreateBitMuncher(dcc.fileData, dcc.directionOffsets[direction]*directionOffsetMultiplier), dcc) } diff --git a/d2common/d2interface/animation.go b/d2common/d2interface/animation.go index fb46844c..806f197a 100644 --- a/d2common/d2interface/animation.go +++ b/d2common/d2interface/animation.go @@ -9,6 +9,7 @@ import ( // Animation is an animation type Animation interface { + BindRenderer(Renderer) error Clone() Animation SetSubLoop(startFrame, EndFrame int) Advance(elapsed float64) error diff --git a/d2common/d2interface/font.go b/d2common/d2interface/font.go deleted file mode 100644 index c0255747..00000000 --- a/d2common/d2interface/font.go +++ /dev/null @@ -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 -} diff --git a/d2common/d2interface/surface.go b/d2common/d2interface/surface.go index ff8e3ad0..befdcf45 100644 --- a/d2common/d2interface/surface.go +++ b/d2common/d2interface/surface.go @@ -9,6 +9,7 @@ import ( // Surface represents a renderable surface. type Surface interface { + Renderer() Renderer Clear(color color.Color) error DrawRect(width, height int, color color.Color) DrawLine(x, y int, color color.Color) diff --git a/d2core/d2asset/animation.go b/d2core/d2asset/animation.go index fc031d59..5622964d 100644 --- a/d2core/d2asset/animation.go +++ b/d2core/d2asset/animation.go @@ -23,6 +23,8 @@ const ( const defaultPlayLength = 1.0 type animationFrame struct { + decoded bool + width int height int offsetX int @@ -33,11 +35,13 @@ type animationFrame struct { type animationDirection struct { decoded bool - frames []*animationFrame + frames []animationFrame } -// animation has directionality, play modes, and frame counting -type animation struct { +// Animation has directionality, play modes, and frame counting +type Animation struct { + renderer d2interface.Renderer + onBindRenderer func(renderer d2interface.Renderer) error directions []animationDirection effect d2enum.DrawEffect colorMod color.Color @@ -56,14 +60,14 @@ type animation struct { } // 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.subEndingFrame = endFrame a.hasSubLoop = true } // Advance advances the animation state -func (a *animation) Advance(elapsed float64) error { +func (a *Animation) Advance(elapsed float64) error { if a.playMode == playModePause { return nil } @@ -112,7 +116,7 @@ func (a *animation) Advance(elapsed float64) error { return nil } -func (a *animation) renderShadow(target d2interface.Surface) error { +func (a *Animation) renderShadow(target d2interface.Surface) error { direction := a.directions[a.directionIndex] 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 -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] frame := direction.frames[a.frameIndex] @@ -147,8 +155,22 @@ func (a *animation) Render(target d2interface.Surface) error { 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 -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 { direction := a.directions[a.directionIndex] 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 -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] 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. -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] if frameIndex >= len(direction.frames) { 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. -func (a *animation) GetCurrentFrameSize() (width, height int) { +func (a *Animation) GetCurrentFrameSize() (width, height int) { width, height, _ = a.GetFrameSize(a.frameIndex) return width, height } // 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 direction := a.directions[a.directionIndex] + for _, frame := range direction.frames { maxWidth = d2math.MaxInt(maxWidth, frame.width) 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 -func (a *animation) GetCurrentFrame() int { +func (a *Animation) GetCurrentFrame() int { return a.frameIndex } // GetFrameCount gets number of frames in animation -func (a *animation) GetFrameCount() int { +func (a *Animation) GetFrameCount() int { direction := a.directions[a.directionIndex] return len(direction.frames) } // IsOnFirstFrame gets if the animation on its first frame -func (a *animation) IsOnFirstFrame() bool { +func (a *Animation) IsOnFirstFrame() bool { return a.frameIndex == 0 } // 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 } // GetDirectionCount gets the number of animation direction -func (a *animation) GetDirectionCount() int { +func (a *Animation) GetDirectionCount() int { return len(a.directions) } // 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 if directionIndex >= smallestInvalidDirectionIndex { return errors.New("invalid direction index") @@ -255,12 +282,12 @@ func (a *animation) SetDirection(directionIndex int) error { } // GetDirection get the current animation direction -func (a *animation) GetDirection() int { +func (a *Animation) GetDirection() int { return a.directionIndex } // 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() { return errors.New("invalid frame index") } @@ -272,47 +299,47 @@ func (a *animation) SetCurrentFrame(frameIndex int) error { } // Rewind animation to beginning -func (a *animation) Rewind() { +func (a *Animation) Rewind() { _ = a.SetCurrentFrame(0) } // PlayForward plays animation forward -func (a *animation) PlayForward() { +func (a *Animation) PlayForward() { a.playMode = playModeForward a.lastFrameTime = 0 } // PlayBackward plays animation backward -func (a *animation) PlayBackward() { +func (a *Animation) PlayBackward() { a.playMode = playModeBackward a.lastFrameTime = 0 } // Pause animation -func (a *animation) Pause() { +func (a *Animation) Pause() { a.playMode = playModePause a.lastFrameTime = 0 } // SetPlayLoop sets whether to loop the animation -func (a *animation) SetPlayLoop(loop bool) { +func (a *Animation) SetPlayLoop(loop bool) { a.playLoop = loop } // 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())) } // 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 a.playLength = playLength a.lastFrameTime = 0 } // 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 const millisecondsPerSecond = 1000.0 @@ -320,24 +347,24 @@ func (a *animation) SetPlayLengthMs(playLengthMs int) { } // SetColorMod sets the Animation's color mod -func (a *animation) SetColorMod(colorMod color.Color) { +func (a *Animation) SetColorMod(colorMod color.Color) { a.colorMod = colorMod } // GetPlayedCount gets the number of times the application played -func (a *animation) GetPlayedCount() int { +func (a *Animation) GetPlayedCount() int { return a.playedCount } // ResetPlayedCount resets the play count -func (a *animation) ResetPlayedCount() { +func (a *Animation) ResetPlayedCount() { a.playedCount = 0 } -func (a *animation) SetEffect(e d2enum.DrawEffect) { +func (a *Animation) SetEffect(e d2enum.DrawEffect) { a.effect = e } -func (a *animation) SetShadow(shadow bool) { +func (a *Animation) SetShadow(shadow bool) { a.hasShadow = shadow } diff --git a/d2core/d2asset/asset_manager.go b/d2core/d2asset/asset_manager.go index c4e4a93a..45855d8a 100644 --- a/d2core/d2asset/asset_manager.go +++ b/d2core/d2asset/asset_manager.go @@ -1,7 +1,6 @@ package d2asset import ( - "encoding/binary" "fmt" "image/color" "log" @@ -32,7 +31,6 @@ const ( // AssetManager loads files and game objects type AssetManager struct { - renderer d2interface.Renderer loader *d2loader.Loader tables d2interface.Cache animations d2interface.Cache @@ -80,12 +78,12 @@ func (am *AssetManager) FileExists(filePath string) (bool, error) { 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) { 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, effect d2enum.DrawEffect) (d2interface.Animation, error) { cachePath := fmt.Sprintf("%s;%s;%d", animationPath, palettePath, effect) @@ -108,17 +106,17 @@ func (am *AssetManager) LoadAnimationWithEffect(animationPath, palettePath strin switch animAsset.Type() { case types.AssetTypeDC6: - animation, err = am.createDC6Animation(animationPath, palette, effect) + animation, err = am.loadDC6(animationPath, palette, effect) if err != nil { return nil, err } case types.AssetTypeDCC: - animation, err = am.createDCCAnimation(animationPath, palette, effect) + animation, err = am.loadDCC(animationPath, palette, effect) if err != nil { return nil, err } 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) @@ -140,11 +138,11 @@ func (am *AssetManager) LoadComposite(baseType d2enum.ObjectType, token, palette } // 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) if cached, found := am.fonts.Retrieve(cachePath); found { - return cached.(d2interface.Font), nil + return cached.(*Font), nil } 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) } - _, 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{ - sheet: sheet, - glyphs: glyphs, - color: color.White, + table: tableData, + sheet: sheet, + color: color.White, } err = am.fonts.Insert(cachePath, font, defaultCacheEntryWeight) @@ -229,7 +212,7 @@ func (am *AssetManager) LoadStringTable(tablePath string) (d2tbl.TextDictionary, } table := d2tbl.LoadTextDictionary(data) - if table != nil { + if table == nil { 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 } -// createDC6Animation creates an Animation from d2dc6.DC6 and d2dat.DATPalette -func (am *AssetManager) createDC6Animation(dc6Path string, +// loadDC6 creates an Animation from d2dc6.DC6 and d2dat.DATPalette +func (am *AssetManager) loadDC6(path string, 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) if err != nil { return nil, err @@ -331,16 +257,27 @@ func (am *AssetManager) loadDC6(path string) (*d2dc6.DC6, error) { 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) if err != nil { 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. @@ -368,7 +305,7 @@ func (am *AssetManager) BindTerminalCommands(term d2interface.Terminal) error { term.OutputInfof("palette cache: %f", cacheStatistics(am.palettes)) 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)) }); err != nil { return err diff --git a/d2core/d2asset/composite.go b/d2core/d2asset/composite.go index 33e665cf..30c915a4 100644 --- a/d2core/d2asset/composite.go +++ b/d2core/d2asset/composite.go @@ -63,6 +63,7 @@ func (c *Composite) Render(target d2interface.Surface) error { for _, layerIndex := range c.mode.cof.Priority[direction][c.mode.frameIndex] { layer := c.mode.layers[layerIndex] + if layer != nil { if err := layer.RenderFromOrigin(target, true); err != nil { return err @@ -145,6 +146,10 @@ func (c *Composite) SetAnimSpeed(speed int) { // SetDirection sets the direction of the composite and its layers func (c *Composite) SetDirection(direction int) { + if c.mode == nil { + return + } + c.direction = direction for layerIdx := range c.mode.layers { layer := c.mode.layers[layerIdx] @@ -241,7 +246,7 @@ func (c *Composite) createMode(animationMode animationMode, weaponClass string) animationData := d2data.AnimationData[animationKey] if len(animationData) == 0 { - return nil, errors.New("could not find animation data") + return nil, errors.New("could not find Animation data") } 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 diff --git a/d2core/d2asset/d2asset.go b/d2core/d2asset/d2asset.go index 46900780..a7538bde 100644 --- a/d2core/d2asset/d2asset.go +++ b/d2core/d2asset/d2asset.go @@ -2,16 +2,13 @@ package d2asset import ( "github.com/OpenDiablo2/OpenDiablo2/d2common/d2cache" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2loader" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2config" ) // NewAssetManager creates and assigns all necessary dependencies for the AssetManager top-level functions to work correctly -func NewAssetManager(renderer d2interface.Renderer, config *d2config.Configuration, - term d2interface.Terminal) (*AssetManager, error) { +func NewAssetManager(config *d2config.Configuration) (*AssetManager, error) { manager := &AssetManager{ - renderer, d2loader.NewLoader(config), d2cache.CreateCache(animationBudget), d2cache.CreateCache(tableBudget), @@ -20,10 +17,5 @@ func NewAssetManager(renderer d2interface.Renderer, config *d2config.Configurati d2cache.CreateCache(paletteTransformBudget), } - if term != nil { - err := manager.BindTerminalCommands(term) - return manager, err - } - return manager, nil } diff --git a/d2core/d2asset/dc6_animation.go b/d2core/d2asset/dc6_animation.go index 9a2f9c06..0d507224 100644 --- a/d2core/d2asset/dc6_animation.go +++ b/d2core/d2asset/dc6_animation.go @@ -3,9 +3,9 @@ package d2asset import ( "errors" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "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/d2dcc" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" @@ -14,13 +14,58 @@ import ( var _ d2interface.Animation = &DC6Animation{} // Static check to confirm struct conforms to // 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 type DC6Animation struct { - animation - dc6Path string - dc6 *d2dc6.DC6 - palette d2interface.Palette - renderer d2interface.Renderer + Animation + dc6 *d2dc6.DC6 + palette d2interface.Palette +} + +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 @@ -31,7 +76,8 @@ func (a *DC6Animation) SetDirection(directionIndex int) error { } direction := d2dcc.Dir64ToDcc(directionIndex, len(a.directions)) - if !a.directions[direction].decoded { + + if !a.directions[directionIndex].decoded { err := a.decodeDirection(direction) if err != nil { return err @@ -39,44 +85,115 @@ func (a *DC6Animation) SetDirection(directionIndex int) error { } 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 } func (a *DC6Animation) decodeDirection(directionIndex int) error { - dc6 := a.dc6 - startFrame := directionIndex * int(dc6.FramesPerDirection) - - 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) + for frameIndex := 0; frameIndex < int(a.dc6.FramesPerDirection); frameIndex++ { + frame, err := a.decodeFrame(directionIndex, frameIndex) if err != nil { return err } - indexData := dc6.DecodeFrame(startFrame + i) - colorData := d2util.ImgIndexToRGBA(indexData, a.palette) + a.directions[directionIndex].frames[frameIndex] = frame + } - 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 } - - 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 } +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 func (a *DC6Animation) Clone() d2interface.Animation { animation := *a diff --git a/d2core/d2asset/dcc_animation.go b/d2core/d2asset/dcc_animation.go index 5ad57604..41e0f314 100644 --- a/d2core/d2asset/dcc_animation.go +++ b/d2core/d2asset/dcc_animation.go @@ -4,10 +4,11 @@ import ( "errors" "math" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2math" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2util" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2math" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dcc" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" @@ -16,13 +17,57 @@ import ( var _ d2interface.Animation = &DCCAnimation{} // Static check to confirm struct conforms to // 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 type DCCAnimation struct { - animation - *AssetManager - dccPath string - palette d2interface.Palette - renderer d2interface.Renderer + Animation + dcc *d2dcc.DCC + palette d2interface.Palette +} + +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 @@ -52,18 +97,45 @@ func (a *DCCAnimation) SetDirection(directionIndex int) error { return nil } -func (a *DCCAnimation) decodeDirection(directionIndex int) error { - dcc, err := a.loadDCC(a.dccPath) - if err != nil { - return err +func (a *DCCAnimation) decode() error { + for directionIndex := 0; directionIndex < len(a.directions); directionIndex++ { + err := a.decodeDirection(directionIndex) + 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 maxX, maxY := math.MinInt32, math.MinInt32 - for _, dccFrame := range direction.Frames { + for _, dccFrame := range dccDirection.Frames { minX = d2math.MinInt(minX, dccFrame.Box.Left) minY = d2math.MinInt(minY, dccFrame.Box.Top) maxX = d2math.MaxInt(maxX, dccFrame.Box.Right()) @@ -73,27 +145,80 @@ func (a *DCCAnimation) decodeDirection(directionIndex int) error { frameWidth := maxX - minX frameHeight := maxY - minY - for _, dccFrame := range direction.Frames { - pixels := d2util.ImgIndexToRGBA(dccFrame.PixelData, a.palette) + frame := animationFrame{ + 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 { 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 } + +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 +} diff --git a/d2core/d2asset/font.go b/d2core/d2asset/font.go index b45ace47..76f5aaad 100644 --- a/d2core/d2asset/font.go +++ b/d2core/d2asset/font.go @@ -1,6 +1,7 @@ package d2asset import ( + "encoding/binary" "image/color" "strings" @@ -8,8 +9,6 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2common/d2math" ) -var _ d2interface.Font = &Font{} // Static check to confirm struct conforms to interface - type fontGlyph struct { frame int width int @@ -19,6 +18,7 @@ type fontGlyph struct { // Font represents a displayable font type Font struct { sheet d2interface.Animation + table []byte glyphs map[rune]fontGlyph color color.Color } @@ -30,17 +30,19 @@ func (f *Font) SetColor(c color.Color) { // GetTextMetrics returns the dimensions of the Font element in pixels func (f *Font) GetTextMetrics(text string) (width, height int) { + if f.glyphs == nil { + f.initGlyphs() + } + var ( - lineWidth int - lineHeight int - totalWidth int - totalHeight int + lineWidth int + lineHeight int ) for _, c := range text { if c == '\n' { - totalWidth = d2math.MaxInt(totalWidth, lineWidth) - totalHeight += lineHeight + width = d2math.MaxInt(width, lineWidth) + height += lineHeight lineWidth = 0 lineHeight = 0 } 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) - totalHeight += lineHeight + width = d2math.MaxInt(width, lineWidth) + 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) 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) lines := strings.Split(text, "\n") @@ -95,3 +106,22 @@ func (f *Font) RenderText(text string, target d2interface.Surface) error { 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 +} diff --git a/d2core/d2gui/common.go b/d2core/d2gui/common.go index 61813357..d769284c 100644 --- a/d2core/d2gui/common.go +++ b/d2core/d2gui/common.go @@ -4,11 +4,13 @@ import ( "errors" "image/color" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2math" ) -func loadFont(fontStyle FontStyle) (d2interface.Font, error) { +func loadFont(fontStyle FontStyle) (*d2asset.Font, error) { config := getFontStyleConfig(fontStyle) if config == nil { return nil, errors.New("invalid font style") diff --git a/d2core/d2gui/label.go b/d2core/d2gui/label.go index 3631aeda..444a87d9 100644 --- a/d2core/d2gui/label.go +++ b/d2core/d2gui/label.go @@ -3,6 +3,7 @@ package d2gui import ( "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" ) // Label is renderable text @@ -11,7 +12,7 @@ type Label struct { renderer d2interface.Renderer text string - font d2interface.Font + font *d2asset.Font surface d2interface.Surface } diff --git a/d2core/d2render/ebiten/ebiten_renderer.go b/d2core/d2render/ebiten/ebiten_renderer.go index 4b759b4e..ad109f45 100644 --- a/d2core/d2render/ebiten/ebiten_renderer.go +++ b/d2core/d2render/ebiten/ebiten_renderer.go @@ -29,7 +29,7 @@ type Renderer struct { // Update updates the screen with the given *ebiten.Image func (r *Renderer) Update(screen *ebiten.Image) error { - err := r.renderCallback(createEbitenSurface(screen)) + err := r.renderCallback(createEbitenSurface(r, screen)) if err != nil { 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 func (r *Renderer) CreateSurface(surface d2interface.Surface) (d2interface.Surface, error) { - result := createEbitenSurface( - surface.(*ebitenSurface).image, - surfaceState{ - filter: ebiten.FilterNearest, - effect: d2enum.DrawEffectNone, - saturation: defaultSaturation, - brightness: defaultBrightness, - skewX: defaultSkewX, - skewY: defaultSkewY, - scaleX: defaultScaleX, - scaleY: defaultScaleY, - }, - ) + img := surface.(*ebitenSurface).image + sfcState := surfaceState{ + filter: ebiten.FilterNearest, + effect: d2enum.DrawEffectNone, + saturation: defaultSaturation, + brightness: defaultBrightness, + skewX: defaultSkewX, + skewY: defaultSkewY, + scaleX: defaultScaleX, + scaleY: defaultScaleY, + } + result := createEbitenSurface(r, img, sfcState) return result, nil } @@ -114,7 +113,7 @@ func (r *Renderer) NewSurface(width, height int, filter d2enum.Filter) (d2interf 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 diff --git a/d2core/d2render/ebiten/ebiten_surface.go b/d2core/d2render/ebiten/ebiten_surface.go index 480e4ba3..3a93ceec 100644 --- a/d2core/d2render/ebiten/ebiten_surface.go +++ b/d2core/d2render/ebiten/ebiten_surface.go @@ -30,6 +30,7 @@ type colorMCacheEntry struct { } type ebitenSurface struct { + renderer *Renderer stateStack []surfaceState stateCurrent surfaceState image *ebiten.Image @@ -37,7 +38,7 @@ type ebitenSurface struct { monotonicClock int64 } -func createEbitenSurface(img *ebiten.Image, currentState ...surfaceState) *ebitenSurface { +func createEbitenSurface(r *Renderer, img *ebiten.Image, currentState ...surfaceState) *ebitenSurface { state := surfaceState{ effect: d2enum.DrawEffectNone, saturation: defaultSaturation, @@ -52,12 +53,18 @@ func createEbitenSurface(img *ebiten.Image, currentState ...surfaceState) *ebite } return &ebitenSurface{ + renderer: r, image: img, stateCurrent: state, 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 func (s *ebitenSurface) PushTranslation(x, y int) { s.stateStack = append(s.stateStack, s.stateCurrent) diff --git a/d2core/d2ui/button.go b/d2core/d2ui/button.go index 80ffac42..717abe75 100644 --- a/d2core/d2ui/button.go +++ b/d2core/d2ui/button.go @@ -191,8 +191,7 @@ func (ui *UIManager) NewButton(buttonType ButtonType, text string) *Button { lbl.Color[0] = d2util.Color(greyAlpha100) lbl.Alignment = d2gui.HorizontalAlignCenter - animation, _ := ui.asset.LoadAnimation(buttonLayout.ResourceName, buttonLayout.PaletteName) - buttonSprite, _ := ui.NewSprite(animation) + buttonSprite, _ := ui.NewSprite(buttonLayout.ResourceName, buttonLayout.PaletteName) for i := 0; i < buttonLayout.XSegments; i++ { w, _, _ := buttonSprite.GetFrameSize(i) diff --git a/d2core/d2ui/checkbox.go b/d2core/d2ui/checkbox.go index 390d504a..17f59240 100644 --- a/d2core/d2ui/checkbox.go +++ b/d2core/d2ui/checkbox.go @@ -31,8 +31,7 @@ func (ui *UIManager) NewCheckbox(checkState bool) *Checkbox { enabled: true, } - animation, _ := ui.asset.LoadAnimation(d2resource.Checkbox, d2resource.PaletteFechar) - checkboxSprite, _ := ui.NewSprite(animation) + checkboxSprite, _ := ui.NewSprite(d2resource.Checkbox, d2resource.PaletteFechar) result.width, result.height, _ = checkboxSprite.GetFrameSize(0) checkboxSprite.SetPosition(0, 0) diff --git a/d2core/d2ui/label.go b/d2core/d2ui/label.go index f3a79bcc..3982eef4 100644 --- a/d2core/d2ui/label.go +++ b/d2core/d2ui/label.go @@ -6,6 +6,8 @@ import ( "regexp" "strings" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2util" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui" @@ -18,7 +20,7 @@ type Label struct { X int Y int Alignment d2gui.HorizontalAlign - font d2interface.Font + font *d2asset.Font Color map[int]color.Color backgroundColor color.Color } @@ -26,6 +28,7 @@ type Label struct { // NewLabel creates a new instance of a UI label func (ui *UIManager) NewLabel(fontPath, palettePath string) *Label { font, _ := ui.asset.LoadFont(fontPath+".tbl", fontPath+".dc6", palettePath) + result := &Label{ Alignment: d2gui.HorizontalAlignLeft, Color: map[int]color.Color{0: color.White}, diff --git a/d2core/d2ui/scrollbar.go b/d2core/d2ui/scrollbar.go index 77d63f51..d6eab5c4 100644 --- a/d2core/d2ui/scrollbar.go +++ b/d2core/d2ui/scrollbar.go @@ -28,8 +28,7 @@ type Scrollbar struct { // NewScrollbar creates a scrollbar instance func (ui *UIManager) NewScrollbar(x, y, height int) *Scrollbar { - animation, _ := ui.asset.LoadAnimation(d2resource.Scrollbar, d2resource.PaletteSky) - scrollbarSprite, _ := ui.NewSprite(animation) + scrollbarSprite, _ := ui.NewSprite(d2resource.Scrollbar, d2resource.PaletteSky) result := &Scrollbar{ visible: true, enabled: true, diff --git a/d2core/d2ui/sprite.go b/d2core/d2ui/sprite.go index b6aab812..4787fb34 100644 --- a/d2core/d2ui/sprite.go +++ b/d2core/d2ui/sprite.go @@ -22,11 +22,17 @@ const ( ) // NewSprite creates a new Sprite -func (ui *UIManager) NewSprite(animation d2interface.Animation) (*Sprite, error) { - if animation == nil { +func (ui *UIManager) NewSprite(animationPath, palettePath string) (*Sprite, error) { + animation, err := ui.asset.LoadAnimation(animationPath, palettePath) + if animation == nil || err != nil { return nil, fmt.Errorf(errNoAnimation) } + err = animation.BindRenderer(ui.renderer) + if err != nil { + return nil, err + } + return &Sprite{animation: animation}, nil } @@ -56,7 +62,8 @@ func (s *Sprite) RenderSegmented(target d2interface.Surface, segmentsX, segments var currentX, maxFrameHeight int 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 } diff --git a/d2core/d2ui/textbox.go b/d2core/d2ui/textbox.go index 8b933ac3..dbce6b7b 100644 --- a/d2core/d2ui/textbox.go +++ b/d2core/d2ui/textbox.go @@ -26,8 +26,7 @@ type TextBox struct { // NewTextbox creates a new instance of a text box func (ui *UIManager) NewTextbox() *TextBox { - animation, _ := ui.asset.LoadAnimation(d2resource.TextBox2, d2resource.PaletteUnits) - bgSprite, _ := ui.NewSprite(animation) + bgSprite, _ := ui.NewSprite(d2resource.TextBox2, d2resource.PaletteUnits) tb := &TextBox{ filter: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", bgSprite: bgSprite, diff --git a/d2game/d2gamescreen/character_select.go b/d2game/d2gamescreen/character_select.go index d280a8c4..0b9ec80a 100644 --- a/d2game/d2gamescreen/character_select.go +++ b/d2game/d2gamescreen/character_select.go @@ -138,10 +138,8 @@ func (v *CharacterSelect) OnLoad(loading d2screen.LoadingState) { loading.Progress(tenPercent) - animation, _ := v.asset.LoadAnimation(d2resource.CharacterSelectionBackground, - d2resource.PaletteSky) 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.createButtons(loading) @@ -160,14 +158,11 @@ func (v *CharacterSelect) OnLoad(loading d2screen.LoadingState) { deleteConfirmX, deleteConfirmY := 400, 185 v.deleteCharConfirmLabel.SetPosition(deleteConfirmX, deleteConfirmY) - animation, _ = v.asset.LoadAnimation(d2resource.CharacterSelectionSelectBox, - d2resource.PaletteSky) - v.selectionBox, _ = v.uiManager.NewSprite(animation) + v.selectionBox, _ = v.uiManager.NewSprite(d2resource.CharacterSelectionSelectBox, d2resource.PaletteSky) selBoxX, selBoxY := 37, 86 v.selectionBox.SetPosition(selBoxX, selBoxY) - animation, _ = v.asset.LoadAnimation(d2resource.PopUpOkCancel, d2resource.PaletteFechar) - v.okCancelBox, _ = v.uiManager.NewSprite(animation) + v.okCancelBox, _ = v.uiManager.NewSprite(d2resource.PopUpOkCancel, d2resource.PaletteFechar) okCancelX, okCancelY := 270, 175 v.okCancelBox.SetPosition(okCancelX, okCancelY) diff --git a/d2game/d2gamescreen/credits.go b/d2game/d2gamescreen/credits.go index 46eb36e9..a049e156 100644 --- a/d2game/d2gamescreen/credits.go +++ b/d2game/d2gamescreen/credits.go @@ -89,8 +89,7 @@ func (v *Credits) LoadContributors() []string { // OnLoad is called to load the resources for the credits screen func (v *Credits) OnLoad(loading d2screen.LoadingState) { - animation, _ := v.asset.LoadAnimation(d2resource.CreditsBackground, d2resource.PaletteSky) - v.creditsBackground, _ = v.uiManager.NewSprite(animation) + v.creditsBackground, _ = v.uiManager.NewSprite(d2resource.CreditsBackground, d2resource.PaletteSky) v.creditsBackground.SetPosition(creditsX, creditsY) loading.Progress(twentyPercent) diff --git a/d2game/d2gamescreen/main_menu.go b/d2game/d2gamescreen/main_menu.go index 5823499d..6605a251 100644 --- a/d2game/d2gamescreen/main_menu.go +++ b/d2game/d2gamescreen/main_menu.go @@ -172,20 +172,16 @@ func (v *MainMenu) OnLoad(loading d2screen.LoadingState) { } func (v *MainMenu) loadBackgroundSprites() { - animation, _ := v.asset.LoadAnimation(d2resource.GameSelectScreen, d2resource.PaletteSky) - v.background, _ = v.uiManager.NewSprite(animation) + v.background, _ = v.uiManager.NewSprite(d2resource.GameSelectScreen, d2resource.PaletteSky) v.background.SetPosition(backgroundX, backgroundY) - animation, _ = v.asset.LoadAnimation(d2resource.TrademarkScreen, d2resource.PaletteSky) - v.trademarkBackground, _ = v.uiManager.NewSprite(animation) + v.trademarkBackground, _ = v.uiManager.NewSprite(d2resource.TrademarkScreen, d2resource.PaletteSky) v.trademarkBackground.SetPosition(backgroundX, backgroundY) - animation, _ = v.asset.LoadAnimation(d2resource.TCPIPBackground, d2resource.PaletteSky) - v.tcpIPBackground, _ = v.uiManager.NewSprite(animation) + v.tcpIPBackground, _ = v.uiManager.NewSprite(d2resource.TCPIPBackground, d2resource.PaletteSky) v.tcpIPBackground.SetPosition(backgroundX, backgroundY) - animation, _ = v.asset.LoadAnimation(d2resource.PopUpOkCancel, d2resource.PaletteFechar) - v.serverIPBackground, _ = v.uiManager.NewSprite(animation) + v.serverIPBackground, _ = v.uiManager.NewSprite(d2resource.PopUpOkCancel, d2resource.PaletteFechar) v.serverIPBackground.SetPosition(serverIPbackgroundX, serverIPbackgroundY) } @@ -236,25 +232,21 @@ func (v *MainMenu) createLabels(loading d2screen.LoadingState) { } func (v *MainMenu) createLogos(loading d2screen.LoadingState) { - animation, _ := v.asset.LoadAnimation(d2resource.Diablo2LogoFireLeft, d2resource.PaletteUnits) - v.diabloLogoLeft, _ = v.uiManager.NewSprite(animation) + v.diabloLogoLeft, _ = v.uiManager.NewSprite(d2resource.Diablo2LogoFireLeft, d2resource.PaletteUnits) v.diabloLogoLeft.SetEffect(d2enum.DrawEffectModulate) v.diabloLogoLeft.PlayForward() v.diabloLogoLeft.SetPosition(diabloLogoX, diabloLogoY) loading.Progress(sixtyPercent) - animation, _ = v.asset.LoadAnimation(d2resource.Diablo2LogoFireRight, d2resource.PaletteUnits) - v.diabloLogoRight, _ = v.uiManager.NewSprite(animation) + v.diabloLogoRight, _ = v.uiManager.NewSprite(d2resource.Diablo2LogoFireRight, d2resource.PaletteUnits) v.diabloLogoRight.SetEffect(d2enum.DrawEffectModulate) v.diabloLogoRight.PlayForward() v.diabloLogoRight.SetPosition(diabloLogoX, diabloLogoY) - animation, _ = v.asset.LoadAnimation(d2resource.Diablo2LogoBlackLeft, d2resource.PaletteUnits) - v.diabloLogoLeftBack, _ = v.uiManager.NewSprite(animation) + v.diabloLogoLeftBack, _ = v.uiManager.NewSprite(d2resource.Diablo2LogoBlackLeft, d2resource.PaletteUnits) v.diabloLogoLeftBack.SetPosition(diabloLogoX, diabloLogoY) - animation, _ = v.asset.LoadAnimation(d2resource.Diablo2LogoBlackRight, d2resource.PaletteUnits) - v.diabloLogoRightBack, _ = v.uiManager.NewSprite(animation) + v.diabloLogoRightBack, _ = v.uiManager.NewSprite(d2resource.Diablo2LogoBlackRight, d2resource.PaletteUnits) v.diabloLogoRightBack.SetPosition(diabloLogoX, diabloLogoY) } diff --git a/d2game/d2gamescreen/select_hero_class.go b/d2game/d2gamescreen/select_hero_class.go index 1c602a8c..cf9db311 100644 --- a/d2game/d2gamescreen/select_hero_class.go +++ b/d2game/d2gamescreen/select_hero_class.go @@ -734,29 +734,23 @@ func (v *SelectHeroClass) loadSprite(animationPath string, position image.Point, return nil } - animation, err := v.asset.LoadAnimation(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) + sprite, err := v.uiManager.NewSprite(animationPath, d2resource.PaletteFechar) if err != nil { fmt.Printf("could not load sprite for the animation: %s\n", animationPath) 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) return sprite diff --git a/d2game/d2player/game_controls.go b/d2game/d2player/game_controls.go index feb186fe..315018a0 100644 --- a/d2game/d2player/game_controls.go +++ b/d2game/d2player/game_controls.go @@ -381,21 +381,16 @@ func (g *GameControls) OnMouseButtonDown(event d2interface.MouseEvent) bool { // Load the resources required for the GameControls func (g *GameControls) Load() { - animation, _ := g.asset.LoadAnimation(d2resource.GameGlobeOverlap, d2resource.PaletteSky) - g.globeSprite, _ = g.uiManager.NewSprite(animation) + g.globeSprite, _ = g.uiManager.NewSprite(d2resource.GameGlobeOverlap, d2resource.PaletteSky) - animation, _ = g.asset.LoadAnimation(d2resource.HealthManaIndicator, d2resource.PaletteSky) - g.hpManaStatusSprite, _ = g.uiManager.NewSprite(animation) + g.hpManaStatusSprite, _ = g.uiManager.NewSprite(d2resource.HealthManaIndicator, d2resource.PaletteSky) - animation, _ = g.asset.LoadAnimation(d2resource.GamePanels, d2resource.PaletteSky) - g.mainPanel, _ = g.uiManager.NewSprite(animation) + g.mainPanel, _ = g.uiManager.NewSprite(d2resource.GamePanels, d2resource.PaletteSky) - animation, _ = g.asset.LoadAnimation(d2resource.MenuButton, d2resource.PaletteSky) - _ = animation.SetCurrentFrame(2) - g.menuButton, _ = g.uiManager.NewSprite(animation) + g.menuButton, _ = g.uiManager.NewSprite(d2resource.MenuButton, d2resource.PaletteSky) + _ = g.menuButton.SetCurrentFrame(2) - animation, _ = g.asset.LoadAnimation(d2resource.GenericSkills, d2resource.PaletteSky) - g.skillIcon, _ = g.uiManager.NewSprite(animation) + g.skillIcon, _ = g.uiManager.NewSprite(d2resource.GenericSkills, d2resource.PaletteSky) g.loadUIButtons() diff --git a/d2game/d2player/help/help.go b/d2game/d2player/help/help.go index 5a204765..0751e3c6 100644 --- a/d2game/d2player/help/help.go +++ b/d2game/d2player/help/help.go @@ -94,9 +94,8 @@ func (h *Overlay) Load() { prevY = 0 ) for frameIndex := 0; frameIndex < 7; frameIndex++ { - animation, _ := h.asset.LoadAnimation(d2resource.HelpBorder, d2resource.PaletteSky) - _ = animation.SetCurrentFrame(frameIndex) - f, _ := h.uiManager.NewSprite(animation) + f, _ := h.uiManager.NewSprite(d2resource.HelpBorder, d2resource.PaletteSky) + _ = f.SetCurrentFrame(frameIndex) ww, hh := f.GetCurrentFrameSize() //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) // Close - - anim, _ := h.asset.LoadAnimation(d2resource.SquareButton, d2resource.PaletteSky) - close, _ := h.uiManager.NewSprite(anim) + close, _ := h.uiManager.NewSprite(d2resource.SquareButton, d2resource.PaletteSky) _ = close.SetCurrentFrame(0) close.SetPosition(685, 57) h.frames = append(h.frames, close) @@ -344,8 +341,7 @@ func (h *Overlay) createBullet(c callout) { newLabel.SetPosition(c.LabelX, c.LabelY) h.text = append(h.text, newLabel) - anim, _ := h.asset.LoadAnimation(d2resource.HelpYellowBullet, d2resource.PaletteSky) - newDot, _ := h.uiManager.NewSprite(anim) + newDot, _ := h.uiManager.NewSprite(d2resource.HelpYellowBullet, d2resource.PaletteSky) _ = newDot.SetCurrentFrame(0) newDot.SetPosition(c.DotX, c.DotY+14) h.frames = append(h.frames, newDot) @@ -370,8 +366,7 @@ func (h *Overlay) createCallout(c callout) { } h.lines = append(h.lines, l) - anim, _ := h.asset.LoadAnimation(d2resource.HelpWhiteBullet, d2resource.PaletteSky) - newDot, _ := h.uiManager.NewSprite(anim) + newDot, _ := h.uiManager.NewSprite(d2resource.HelpWhiteBullet, d2resource.PaletteSky) _ = newDot.SetCurrentFrame(0) newDot.SetPosition(c.DotX, c.DotY) h.frames = append(h.frames, newDot) diff --git a/d2game/d2player/hero_stats_panel.go b/d2game/d2player/hero_stats_panel.go index e5752061..e51e38eb 100644 --- a/d2game/d2player/hero_stats_panel.go +++ b/d2game/d2player/hero_stats_panel.go @@ -78,10 +78,8 @@ func NewHeroStatsPanel(asset *d2asset.AssetManager, ui *d2ui.UIManager, heroName // Load the data for the hero status panel func (s *HeroStatsPanel) Load() { - animation, _ := s.asset.LoadAnimation(d2resource.Frame, d2resource.PaletteSky) - s.frame, _ = s.uiManager.NewSprite(animation) - animation, _ = s.asset.LoadAnimation(d2resource.InventoryCharacterPanel, d2resource.PaletteSky) - s.panel, _ = s.uiManager.NewSprite(animation) + s.frame, _ = s.uiManager.NewSprite(d2resource.Frame, d2resource.PaletteSky) + s.panel, _ = s.uiManager.NewSprite(d2resource.InventoryCharacterPanel, d2resource.PaletteSky) s.initStatValueLabels() } diff --git a/d2game/d2player/inventory.go b/d2game/d2player/inventory.go index 262d250e..a30e3ebf 100644 --- a/d2game/d2player/inventory.go +++ b/d2game/d2player/inventory.go @@ -71,11 +71,9 @@ func (g *Inventory) Close() { // Load the resources required by the inventory func (g *Inventory) Load() { - animation, _ := g.asset.LoadAnimation(d2resource.Frame, d2resource.PaletteSky) - g.frame, _ = g.uiManager.NewSprite(animation) + g.frame, _ = g.uiManager.NewSprite(d2resource.Frame, d2resource.PaletteSky) - animation, _ = g.asset.LoadAnimation(d2resource.InventoryCharacterPanel, d2resource.PaletteSky) - g.panel, _ = g.uiManager.NewSprite(animation) + g.panel, _ = g.uiManager.NewSprite(d2resource.InventoryCharacterPanel, d2resource.PaletteSky) items := []InventoryItem{ diablo2item.NewItem("kit", "Crimson", "of the Bat", "of Frost").Identify(), diablo2item.NewItem("rin", "Steel", "of Shock").Identify(), diff --git a/d2game/d2player/inventory_grid.go b/d2game/d2player/inventory_grid.go index 85b06c4e..2f564a03 100644 --- a/d2game/d2player/inventory_grid.go +++ b/d2game/d2player/inventory_grid.go @@ -121,17 +121,8 @@ func (g *ItemGrid) loadItem(item InventoryItem) { var itemSprite *d2ui.Sprite // TODO: Put the pattern into D2Shared - animation, err := g.asset.LoadAnimation( - fmt.Sprintf("/data/global/items/inv%s.dc6", item.GetItemCode()), - 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) + imgPath := fmt.Sprintf("/data/global/items/inv%s.dc6", item.GetItemCode()) + itemSprite, err := g.uiManager.NewSprite(imgPath, d2resource.PaletteSky) if err != nil { log.Printf("Failed to load sprite, error: " + err.Error()) } diff --git a/d2game/d2player/mini_panel.go b/d2game/d2player/mini_panel.go index 3b6d84e4..f2b45216 100644 --- a/d2game/d2player/mini_panel.go +++ b/d2game/d2player/mini_panel.go @@ -23,11 +23,9 @@ func newMiniPanel(asset *d2asset.AssetManager, uiManager *d2ui.UIManager, isSing miniPanelContainerPath = d2resource.MinipanelSmall } - animation, _ := asset.LoadAnimation(miniPanelContainerPath, d2resource.PaletteSky) - containerSprite, _ := uiManager.NewSprite(animation) + containerSprite, _ := uiManager.NewSprite(miniPanelContainerPath, d2resource.PaletteSky) - animation, _ = asset.LoadAnimation(d2resource.MinipanelButton, d2resource.PaletteSky) - buttonSprite, _ := uiManager.NewSprite(animation) + buttonSprite, _ := uiManager.NewSprite(d2resource.MinipanelButton, d2resource.PaletteSky) rectangle := d2geom.Rectangle{Left: 325, Top: 526, Width: 156, Height: 26} diff --git a/main.go b/main.go index 8be4d67b..f31896a5 100644 --- a/main.go +++ b/main.go @@ -35,7 +35,7 @@ func main() { panic(err) } - asset, err := d2asset.NewAssetManager(renderer, d2config.Config, nil) + asset, err := d2asset.NewAssetManager(d2config.Config) if err != nil { panic(err) }