first pass at resolving linting issues in d2asset (#518)

This commit is contained in:
David Carrell 2020-07-01 13:09:18 -05:00 committed by GitHub
parent 973e969002
commit c8ec4c018e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 134 additions and 102 deletions

View File

@ -26,6 +26,8 @@ const (
playModeBackward
)
const defaultPlayLength = 1.0
type animationFrame struct {
width int
height int
@ -59,9 +61,10 @@ type Animation struct {
subEndingFrame int
}
// CreateAnimationFromDCC creates an animation from d2dcc.DCC and d2dat.DATPalette
func CreateAnimationFromDCC(dcc *d2dcc.DCC, palette *d2dat.DATPalette, transparency int) (*Animation, error) {
animation := &Animation{
playLength: 1.0,
playLength: defaultPlayLength,
playLoop: true,
}
@ -80,14 +83,14 @@ func CreateAnimationFromDCC(dcc *d2dcc.DCC, palette *d2dat.DATPalette, transpare
frameWidth := maxX - minX
frameHeight := maxY - minY
pixels := make([]byte, frameWidth*frameHeight*4)
const bytesPerPixel = 4
pixels := make([]byte, frameWidth*frameHeight*bytesPerPixel)
for y := 0; y < frameHeight; y++ {
for x := 0; x < frameWidth; x++ {
if paletteIndex := dccFrame.PixelData[y*frameWidth+x]; paletteIndex != 0 {
palColor := palette.Colors[paletteIndex]
offset := (x + y*frameWidth) * 4
offset := (x + y*frameWidth) * bytesPerPixel
pixels[offset] = palColor.R
pixels[offset+1] = palColor.G
pixels[offset+2] = palColor.B
@ -123,9 +126,10 @@ func CreateAnimationFromDCC(dcc *d2dcc.DCC, palette *d2dat.DATPalette, transpare
return animation, nil
}
// CreateAnimationFromDC6 creates an Animation from d2dc6.DC6 and d2dat.DATPalette
func CreateAnimationFromDC6(dc6 *d2dc6.DC6, palette *d2dat.DATPalette) (*Animation, error) {
animation := &Animation{
playLength: 1.0,
playLength: defaultPlayLength,
playLoop: true,
originAtBottom: true,
}
@ -170,17 +174,18 @@ func CreateAnimationFromDC6(dc6 *d2dc6.DC6, palette *d2dat.DATPalette) (*Animati
}
}
colorData := make([]byte, dc6Frame.Width*dc6Frame.Height*4)
bytesPerPixel := 4
colorData := make([]byte, int(dc6Frame.Width)*int(dc6Frame.Height)*bytesPerPixel)
for i := 0; i < int(dc6Frame.Width*dc6Frame.Height); i++ {
if indexData[i] < 1 { // TODO: Is this == -1 or < 1?
continue
}
colorData[i*4] = palette.Colors[indexData[i]].R
colorData[i*4+1] = palette.Colors[indexData[i]].G
colorData[i*4+2] = palette.Colors[indexData[i]].B
colorData[i*4+3] = 0xff
colorData[i*bytesPerPixel] = palette.Colors[indexData[i]].R
colorData[i*bytesPerPixel+1] = palette.Colors[indexData[i]].G
colorData[i*bytesPerPixel+2] = palette.Colors[indexData[i]].B
colorData[i*bytesPerPixel+3] = 0xff
}
if err := sfc.ReplacePixels(colorData); err != nil {
@ -269,9 +274,14 @@ func (a *Animation) Render(target d2interface.Surface) error {
frame := direction.frames[a.frameIndex]
target.PushTranslation(frame.offsetX, frame.offsetY)
defer target.Pop()
target.PushCompositeMode(a.compositeMode)
defer target.Pop()
target.PushColor(a.colorMod)
defer target.PopN(3)
defer target.Pop()
return target.Render(frame.image)
}
@ -356,9 +366,11 @@ func (a *Animation) GetDirectionCount() int {
// SetDirection places the animation in the direction of an animation
func (a *Animation) SetDirection(directionIndex int) error {
if directionIndex >= 64 {
const smallestInvalidDirectionIndex = 64
if directionIndex >= smallestInvalidDirectionIndex {
return errors.New("invalid direction index")
}
a.directionIndex = d2dcc.Dir64ToDcc(directionIndex, len(a.directions))
a.frameIndex = 0
@ -378,6 +390,7 @@ func (a *Animation) SetCurrentFrame(frameIndex int) error {
a.frameIndex = frameIndex
a.lastFrameTime = 0
return nil
}
@ -414,17 +427,21 @@ func (a *Animation) SetPlaySpeed(playSpeed float64) {
a.SetPlayLength(playSpeed * float64(a.GetFrameCount()))
}
func (a *Animation) SetPlayLength(playLength float64) {
// SetPlayLength sets the Animation's play length in seconds
func (a *Animation) SetPlayLength(playLength float64) { // TODO refactor to use time.Duration instead of float64
a.playLength = playLength
a.lastFrameTime = 0
}
func (a *Animation) SetPlayLengthMs(playLengthMs int) {
a.SetPlayLength(float64(playLengthMs) / 1000.0)
// SetPlayLengthMs sets the Animation's play length in milliseconds
func (a *Animation) SetPlayLengthMs(playLengthMs int) { // TODO remove this method
const millisecondsPerSecond = 1000.0
a.SetPlayLength(float64(playLengthMs) / millisecondsPerSecond)
}
func (a *Animation) SetColorMod(color color.Color) {
a.colorMod = color
// SetColorMod sets the Animation's color mod
func (a *Animation) SetColorMod(colorMod color.Color) {
a.colorMod = colorMod
}
// GetPlayedCount gets the number of times the application played
@ -437,7 +454,7 @@ func (a *Animation) ResetPlayedCount() {
a.playedCount = 0
}
// SetBlend sets the animation alpha blending status
// SetBlend sets the Animation alpha blending status
func (a *Animation) SetBlend(blend bool) {
if blend {
a.compositeMode = d2enum.CompositeModeLighter

View File

@ -27,6 +27,7 @@ func (am *animationManager) loadAnimation(animationPath, palettePath string, tra
}
var animation *Animation
ext := strings.ToLower(filepath.Ext(animationPath))
switch ext {
case ".dc6":

View File

@ -17,7 +17,7 @@ type archiveEntry struct {
type archiveManager struct {
cache *d2common.Cache
config d2config.Configuration
config *d2config.Configuration
entries []archiveEntry
mutex sync.Mutex
}
@ -26,7 +26,7 @@ const (
archiveBudget = 1024 * 1024 * 512
)
func createArchiveManager(config d2config.Configuration) *archiveManager {
func createArchiveManager(config *d2config.Configuration) *archiveManager {
return &archiveManager{cache: d2common.CreateCache(archiveBudget), config: config}
}
@ -93,6 +93,7 @@ func (am *archiveManager) cacheArchiveEntries() error {
for _, archiveName := range am.config.MpqLoadOrder {
archivePath := path.Join(am.config.MpqPath, archiveName)
archive, err := am.loadArchive(archivePath)
if err != nil {
return err

View File

@ -1,18 +1,11 @@
package d2asset
import (
"errors"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2cof"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dc6"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dcc"
)
var (
ErrWasInit = errors.New("asset system is already initialized")
ErrNotInit = errors.New("asset system is not initialized")
)
type assetManager struct {
archiveManager *archiveManager
fileManager *fileManager

View File

@ -5,24 +5,26 @@ import (
"fmt"
"strings"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2cof"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2cof"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
)
// Composite is a composite entity animation
type Composite struct {
object *d2datadict.ObjectLookupRecord
palettePath string
mode *compositeMode
}
// CreateComposite creates a Composite from a given ObjectLookupRecord and palettePath.
func CreateComposite(object *d2datadict.ObjectLookupRecord, palettePath string) *Composite {
return &Composite{object: object, palettePath: palettePath}
}
// Advance moves the composite animation forward for a given elapsed time in nanoseconds.
func (c *Composite) Advance(elapsed float64) error {
if c.mode == nil {
return nil
@ -46,6 +48,7 @@ func (c *Composite) Advance(elapsed float64) error {
return nil
}
// Render performs drawing of the Composite on the rendered d2interface.Surface.
func (c *Composite) Render(target d2interface.Surface) error {
if c.mode == nil {
return nil
@ -63,10 +66,12 @@ func (c *Composite) Render(target d2interface.Surface) error {
return nil
}
// GetAnimationMode returns the animation mode the Composite should render with.
func (c Composite) GetAnimationMode() string {
return c.mode.animationMode
}
// SetMode sets the Composite's animation mode weapon class and direction
func (c *Composite) SetMode(animationMode, weaponClass string, direction int) error {
if c.mode != nil && c.mode.animationMode == animationMode && c.mode.weaponClass == weaponClass && c.mode.cofDirection == direction {
return nil
@ -77,11 +82,13 @@ func (c *Composite) SetMode(animationMode, weaponClass string, direction int) er
return err
}
c.ResetPlayedCount()
c.resetPlayedCount()
c.mode = mode
return nil
}
// SetSpeed sets the speed at which the Composite's animation should advance through its frames
func (c *Composite) SetSpeed(speed int) {
c.mode.animationSpeed = 1.0 / ((float64(speed) * 25.0) / 256.0)
for layerIdx := range c.mode.layers {
@ -92,6 +99,7 @@ func (c *Composite) SetSpeed(speed int) {
}
}
// GetDirectionCount returns the Composites number of available animated directions
func (c *Composite) GetDirectionCount() int {
if c.mode == nil {
return 0
@ -100,6 +108,7 @@ func (c *Composite) GetDirectionCount() int {
return c.mode.directionCount
}
// GetPlayedCount returns the number of times the current animation mode has completed all its distinct frames
func (c *Composite) GetPlayedCount() int {
if c.mode == nil {
return 0
@ -108,7 +117,7 @@ func (c *Composite) GetPlayedCount() int {
return c.mode.playedCount
}
func (c *Composite) ResetPlayedCount() {
func (c *Composite) resetPlayedCount() {
if c.mode != nil {
c.mode.playedCount = 0
}
@ -117,7 +126,7 @@ func (c *Composite) ResetPlayedCount() {
type compositeMode struct {
animationMode string
weaponClass string
cofDirection int
cofDirection int
directionCount int
playedCount int
@ -142,6 +151,7 @@ func (c *Composite) createMode(animationMode, weaponClass string, direction int)
}
animationKey := strings.ToLower(c.object.Token + animationMode + weaponClass)
animationData := d2data.AnimationData[animationKey]
if len(animationData) == 0 {
return nil, errors.New("could not find animation data")
@ -222,7 +232,9 @@ func (c *Composite) createMode(animationMode, weaponClass string, direction int)
layer.SetPlaySpeed(mode.animationSpeed)
layer.PlayForward()
layer.SetBlend(blend)
layer.SetDirection(direction)
if err := layer.SetDirection(direction); err != nil {
return nil, err
}
mode.layers[cofLayer.Type] = layer
}
}

View File

@ -1,3 +1,6 @@
/*
Package d2asset has behaviors to load and save assets from disk.
*/
package d2asset
import (
@ -8,19 +11,17 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2mpq"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2pl2"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2config"
)
var singleton *assetManager
// Initialize creates and assigns all necessary dependencies for the assetManager top-level functions to work correctly
func Initialize(term d2interface.Terminal) error {
verifyNotInit()
var (
config = d2config.Get()
archiveManager = createArchiveManager(config)
fileManager = createFileManager(config, archiveManager)
archiveManager = createArchiveManager(&config)
fileManager = createFileManager(&config, archiveManager)
paletteManager = createPaletteManager()
paletteTransformManager = createPaletteTransformManager()
animationManager = createAnimationManager()
@ -36,7 +37,7 @@ func Initialize(term d2interface.Terminal) error {
fontManager,
}
term.BindAction("assetspam", "display verbose asset manager logs", func(verbose bool) {
if err := term.BindAction("assetspam", "display verbose asset manager logs", func(verbose bool) {
if verbose {
term.OutputInfof("asset manager verbose logging enabled")
} else {
@ -48,41 +49,47 @@ func Initialize(term d2interface.Terminal) error {
paletteManager.cache.SetVerbose(verbose)
paletteTransformManager.cache.SetVerbose(verbose)
animationManager.cache.SetVerbose(verbose)
})
}); err != nil {
return err
}
term.BindAction("assetstat", "display asset manager cache statistics", func() {
term.OutputInfof("archive cache: %f", float64(archiveManager.cache.GetWeight())/float64(archiveManager.cache.GetBudget())*100.0)
term.OutputInfof("file cache: %f", float64(fileManager.cache.GetWeight())/float64(fileManager.cache.GetBudget())*100.0)
term.OutputInfof("palette cache: %f", float64(paletteManager.cache.GetWeight())/float64(paletteManager.cache.GetBudget())*100.0)
term.OutputInfof("palette transform cache: %f", float64(paletteTransformManager.cache.GetWeight())/float64(paletteTransformManager.cache.GetBudget())*100.0)
term.OutputInfof("animation cache: %f", float64(animationManager.cache.GetWeight())/float64(animationManager.cache.GetBudget())*100.0)
term.OutputInfof("font cache: %f", float64(fontManager.cache.GetWeight())/float64(fontManager.cache.GetBudget())*100.0)
})
if err := term.BindAction("assetstat", "display asset manager cache statistics", func() {
type cache interface {
GetWeight() int
GetBudget() int
}
term.BindAction("assetclear", "clear asset manager cache", func() {
var cacheStatistics = func(c cache) float64 {
const percent = 100.0
return float64(c.GetWeight()) / float64(c.GetBudget()) * percent
}
term.OutputInfof("archive cache: %f", cacheStatistics(archiveManager.cache))
term.OutputInfof("file cache: %f", cacheStatistics(fileManager.cache))
term.OutputInfof("palette cache: %f", cacheStatistics(paletteManager.cache))
term.OutputInfof("palette transform cache: %f", cacheStatistics(paletteTransformManager.cache))
term.OutputInfof("animation cache: %f", cacheStatistics(animationManager.cache))
term.OutputInfof("font cache: %f", cacheStatistics(fontManager.cache))
}); err != nil {
return err
}
if err := term.BindAction("assetclear", "clear asset manager cache", func() {
archiveManager.cache.Clear()
fileManager.cache.Clear()
paletteManager.cache.Clear()
paletteTransformManager.cache.Clear()
animationManager.cache.Clear()
fontManager.cache.Clear()
})
}); err != nil {
return err
}
return nil
}
func Shutdown() {
singleton = nil
}
func LoadArchive(archivePath string) (*d2mpq.MPQ, error) {
verifyWasInit()
return singleton.archiveManager.loadArchive(archivePath)
}
// LoadFileStream streams an MPQ file from a source file path
func LoadFileStream(filePath string) (*d2mpq.MpqDataStream, error) {
verifyWasInit()
data, err := singleton.fileManager.loadFileStream(filePath)
if err != nil {
log.Printf("error loading file stream %s (%v)", filePath, err.Error())
@ -91,9 +98,8 @@ func LoadFileStream(filePath string) (*d2mpq.MpqDataStream, error) {
return data, err
}
// LoadFile loads an entire file from a source file path as a []byte
func LoadFile(filePath string) ([]byte, error) {
verifyWasInit()
data, err := singleton.fileManager.loadFile(filePath)
if err != nil {
log.Printf("error loading file %s (%v)", filePath, err.Error())
@ -102,49 +108,32 @@ func LoadFile(filePath string) ([]byte, error) {
return data, err
}
// FileExists checks if a file exists on the underlying file system at the given file path.
func FileExists(filePath string) (bool, error) {
verifyWasInit()
return singleton.fileManager.fileExists(filePath)
}
// LoadAnimation loads an animation by its resource path and its palette path
func LoadAnimation(animationPath, palettePath string) (*Animation, error) {
verifyWasInit()
return LoadAnimationWithTransparency(animationPath, palettePath, 255)
}
func LoadPaletteTransform(pl2Path string) (*d2pl2.PL2, error) {
verifyWasInit()
return singleton.paletteTransformManager.loadPaletteTransform(pl2Path)
}
// LoadAnimationWithTransparency loads an animation by its resource path and its palette path with a given transparency value
func LoadAnimationWithTransparency(animationPath, palettePath string, transparency int) (*Animation, error) {
verifyWasInit()
return singleton.animationManager.loadAnimation(animationPath, palettePath, transparency)
}
// LoadComposite creates a composite object from a ObjectLookupRecord and palettePath describing it
func LoadComposite(object *d2datadict.ObjectLookupRecord, palettePath string) (*Composite, error) {
verifyWasInit()
return CreateComposite(object, palettePath), nil
}
// LoadFont loads a font the resource files
func LoadFont(tablePath, spritePath, palettePath string) (*Font, error) {
verifyWasInit()
return singleton.fontManager.loadFont(tablePath, spritePath, palettePath)
}
// LoadPalette loads a palette from a given palette path
func LoadPalette(palettePath string) (*d2dat.DATPalette, error) {
verifyWasInit()
return singleton.paletteManager.loadPalette(palettePath)
}
func verifyWasInit() {
if singleton == nil {
panic(ErrNotInit)
}
}
func verifyNotInit() {
if singleton != nil {
panic(ErrWasInit)
}
}

View File

@ -16,14 +16,13 @@ const (
type fileManager struct {
cache *d2common.Cache
archiveManager *archiveManager
config d2config.Configuration
config *d2config.Configuration
}
func createFileManager(config d2config.Configuration, archiveManager *archiveManager) *fileManager {
func createFileManager(config *d2config.Configuration, archiveManager *archiveManager) *fileManager {
return &fileManager{d2common.CreateCache(fileBudget), archiveManager, config}
}
func (fm *fileManager) loadFileStream(filePath string) (*d2mpq.MpqDataStream, error) {
filePath = fm.fixupFilePath(filePath)

View File

@ -17,6 +17,7 @@ type fontGlyph struct {
height int
}
// Font represents a displayable font
type Font struct {
sheet *Animation
glyphs map[rune]fontGlyph
@ -47,7 +48,7 @@ func loadFont(tablePath, spritePath, palettePath string) (*Font, error) {
var glyph fontGlyph
glyph.frame = int(binary.LittleEndian.Uint16(data[i+8 : i+10]))
glyph.width = int(data[i+3])
glyph.height = maxCharHeight // int(data[i+4])
glyph.height = maxCharHeight
glyphs[code] = glyph
}
@ -61,11 +62,12 @@ func loadFont(tablePath, spritePath, palettePath string) (*Font, error) {
return font, nil
}
func (f *Font) SetColor(color color.Color) {
f.color = color
func (f *Font) SetColor(c color.Color) {
f.color = c
}
func (f *Font) GetTextMetrics(text string) (int, int) {
// GetTextMetrics returns the dimensions of the Font element in pixels
func (f *Font) GetTextMetrics(text string) (width, height int) {
var (
lineWidth int
lineHeight int
@ -91,6 +93,7 @@ func (f *Font) GetTextMetrics(text string) (int, int) {
return totalWidth, totalHeight
}
// Clone creates a shallow copy of the Font
func (f *Font) Clone() *Font {
return &Font{
sheet: f.sheet,
@ -99,6 +102,7 @@ func (f *Font) Clone() *Font {
}
}
// RenderText draws a string of text in a style described by Font onto the d2interface.Surface
func (f *Font) RenderText(text string, target d2interface.Surface) error {
f.sheet.SetColorMod(f.color)
f.sheet.SetBlend(false)
@ -112,13 +116,23 @@ func (f *Font) RenderText(text string, target d2interface.Surface) error {
)
for _, c := range line {
if glyph, ok := f.glyphs[c]; ok {
f.sheet.SetCurrentFrame(glyph.frame)
f.sheet.Render(target)
lineHeight = d2common.MaxInt(lineHeight, glyph.height)
target.PushTranslation(glyph.width, 0)
lineLength++
glyph, ok := f.glyphs[c]
if !ok {
continue
}
if err := f.sheet.SetCurrentFrame(glyph.frame); err != nil {
return err
}
if err := f.sheet.Render(target); err != nil {
return err
}
lineHeight = d2common.MaxInt(lineHeight, glyph.height)
lineLength++
target.PushTranslation(glyph.width, 0)
}
target.PopN(lineLength)

View File

@ -32,6 +32,9 @@ func (pm *paletteManager) loadPalette(palettePath string) (*d2dat.DATPalette, er
return nil, err
}
pm.cache.Insert(palettePath, palette, 1)
if err := pm.cache.Insert(palettePath, palette, 1); err != nil {
return nil, err
}
return palette, nil
}

View File

@ -32,6 +32,9 @@ func (pm *paletteTransformManager) loadPaletteTransform(path string) (*d2pl2.PL2
return nil, err
}
pm.cache.Insert(path, pl2, 1)
if err := pm.cache.Insert(path, pl2, 1); err != nil {
return nil, err
}
return pl2, nil
}