mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2024-11-06 18:27:20 -05:00
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:
parent
8a670d7482
commit
7e3aff557b
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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},
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
|
@ -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(),
|
||||
|
@ -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())
|
||||
}
|
||||
|
@ -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}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user