diff --git a/d2asset/animation.go b/d2asset/animation.go new file mode 100644 index 00000000..0379135f --- /dev/null +++ b/d2asset/animation.go @@ -0,0 +1,255 @@ +package d2asset + +import ( + "errors" + "image/color" + + "github.com/OpenDiablo2/D2Shared/d2data/d2dc6" + "github.com/OpenDiablo2/D2Shared/d2helper" + "github.com/OpenDiablo2/OpenDiablo2/d2corehelper" + + "github.com/hajimehoshi/ebiten" +) + +type playMode int + +const ( + playModePause playMode = iota + playModeForward + playModeBackward +) + +type animationFrame struct { + width int + height int + offsetX int + offsetY int + + image *ebiten.Image +} + +type animationDirection struct { + frames []*animationFrame +} + +type Animation struct { + directions []*animationDirection + frameIndex int + directionIndex int + lastFrameTime float64 + + compositeMode ebiten.CompositeMode + colorMod color.Color + + playMode playMode + playLength float64 + playLoop bool +} + +func createAnimationFromDC6(dc6 *d2dc6.DC6File) (*Animation, error) { + animation := &Animation{ + playLength: 1.0, + playLoop: true, + } + + for frameIndex, frame := range dc6.Frames { + image, err := ebiten.NewImage(int(frame.Width), int(frame.Height), ebiten.FilterNearest) + if err != nil { + return nil, err + } + + if err := image.ReplacePixels(frame.ColorData()); err != nil { + return nil, err + } + + directionIndex := frameIndex / int(dc6.FramesPerDirection) + if directionIndex >= len(animation.directions) { + animation.directions = append(animation.directions, new(animationDirection)) + } + + direction := animation.directions[directionIndex] + direction.frames = append(direction.frames, &animationFrame{ + width: int(frame.Width), + height: int(frame.Height), + offsetX: int(frame.OffsetX), + offsetY: int(frame.OffsetY), + image: image, + }) + } + + return animation, nil +} + +func (a *Animation) clone() *Animation { + animation := *a + return &animation +} + +func (a *Animation) Advance(elapsed float64) error { + if a.playMode == playModePause { + return nil + } + + frameCount := a.GetFrameCount() + frameLength := a.playLength / float64(frameCount) + a.lastFrameTime += elapsed + framesAdvanced := int(a.lastFrameTime / frameLength) + a.lastFrameTime -= float64(framesAdvanced) * frameLength + + for i := 0; i < framesAdvanced; i++ { + switch a.playMode { + case playModeForward: + a.frameIndex++ + if a.frameIndex >= frameCount { + if a.playLoop { + a.frameIndex = 0 + } else { + a.frameIndex = frameCount - 1 + break + } + } + case playModeBackward: + a.frameIndex-- + if a.frameIndex < 0 { + if a.playLoop { + a.frameIndex = frameCount - 1 + } else { + a.frameIndex = 0 + break + } + } + } + } + + return nil +} + +func (a *Animation) Render(target *ebiten.Image, offsetX, offsetY int) error { + direction := a.directions[a.directionIndex] + frame := direction.frames[a.frameIndex] + + opts := &ebiten.DrawImageOptions{} + opts.GeoM.Translate(float64(frame.offsetX+offsetX), float64(frame.offsetY+offsetY)) + opts.CompositeMode = a.compositeMode + if a.colorMod != nil { + opts.ColorM = d2corehelper.ColorToColorM(a.colorMod) + } + + return target.DrawImage(frame.image, opts) +} + +func (a *Animation) GetFrameSize(frameIndex int) (int, int, error) { + direction := a.directions[a.directionIndex] + if frameIndex >= len(direction.frames) { + return 0, 0, errors.New("invalid frame index") + } + + frame := direction.frames[frameIndex] + return frame.width, frame.height, nil +} + +func (a *Animation) GetCurrentFrameSize() (int, int) { + width, height, _ := a.GetFrameSize(a.frameIndex) + return width, height +} + +func (a *Animation) GetFrameBounds() (int, int) { + maxWidth, maxHeight := 0, 0 + + direction := a.directions[a.directionIndex] + for _, frame := range direction.frames { + maxWidth = d2helper.MaxInt(maxWidth, frame.width) + maxHeight = d2helper.MaxInt(maxHeight, frame.height) + } + + return maxWidth, maxHeight +} + +func (a *Animation) GetCurrentFrame() int { + return a.frameIndex +} + +func (a *Animation) GetFrameCount() int { + direction := a.directions[a.directionIndex] + return len(direction.frames) +} + +func (a *Animation) IsOnFirstFrame() bool { + return a.frameIndex == 0 +} + +func (a *Animation) IsOnLastFrame() bool { + return a.frameIndex == a.GetFrameCount()-1 +} + +func (a *Animation) GetDirectionCount() int { + return len(a.directions) +} + +func (a *Animation) SetDirection(directionIndex int) error { + if directionIndex >= len(a.directions) { + return errors.New("invalid direction index") + } + + a.directionIndex = directionIndex + a.frameIndex = 0 + return nil +} + +func (a *Animation) GetDirection() int { + return a.directionIndex +} + +func (a *Animation) SetCurrentFrame(frameIndex int) error { + if frameIndex >= a.GetFrameCount() { + return errors.New("invalid frame index") + } + + a.frameIndex = frameIndex + a.lastFrameTime = 0 + return nil +} + +func (a *Animation) Rewind() { + a.SetCurrentFrame(0) +} + +func (a *Animation) PlayForward() { + a.playMode = playModeForward + a.lastFrameTime = 0 +} + +func (a *Animation) PlayBackward() { + a.playMode = playModeBackward + a.lastFrameTime = 0 +} + +func (a *Animation) Pause() { + a.playMode = playModePause + a.lastFrameTime = 0 +} + +func (a *Animation) SetPlayLoop(loop bool) { + a.playLoop = true +} + +func (a *Animation) SetPlayLength(playLength float64) { + a.playLength = playLength + a.lastFrameTime = 0 +} + +func (a *Animation) SetPlayLengthMs(playLengthMs int) { + a.SetPlayLength(float64(playLengthMs) / 1000.0) +} + +func (a *Animation) SetColorMod(color color.Color) { + a.colorMod = color +} + +func (a *Animation) SetBlend(blend bool) { + if blend { + a.compositeMode = ebiten.CompositeModeLighter + } else { + a.compositeMode = ebiten.CompositeModeSourceOver + } +} diff --git a/d2asset/animation_manager.go b/d2asset/animation_manager.go new file mode 100644 index 00000000..115e2914 --- /dev/null +++ b/d2asset/animation_manager.go @@ -0,0 +1,29 @@ +package d2asset + +type animationManager struct { + cache *cache +} + +func createAnimationManager() *animationManager { + return &animationManager{cache: createCache(AnimationBudget)} +} + +func (sm *animationManager) loadAnimation(animationPath, palettePath string) (*Animation, error) { + cachePath := animationPath + palettePath + if animation, found := sm.cache.retrieve(cachePath); found { + return animation.(*Animation).clone(), nil + } + + dc6, err := loadDC6(animationPath, palettePath) + if err != nil { + return nil, err + } + + animation, err := createAnimationFromDC6(dc6) + if err != nil { + return nil, err + } + + sm.cache.insert(cachePath, animation.clone(), 1) + return animation, err +} diff --git a/d2asset/archive_manager.go b/d2asset/archive_manager.go new file mode 100644 index 00000000..6146096f --- /dev/null +++ b/d2asset/archive_manager.go @@ -0,0 +1,83 @@ +package d2asset + +import ( + "fmt" + "path" + "sync" + + "github.com/OpenDiablo2/D2Shared/d2data/d2mpq" + "github.com/OpenDiablo2/OpenDiablo2/d2corecommon" +) + +type archiveEntry struct { + archivePath string + hashEntryMap d2mpq.HashEntryMap +} + +type archiveManager struct { + cache *cache + config *d2corecommon.Configuration + entries []archiveEntry + mutex sync.Mutex +} + +func createArchiveManager(config *d2corecommon.Configuration) *archiveManager { + return &archiveManager{cache: createCache(ArchiveBudget), config: config} +} + +func (am *archiveManager) loadArchiveForFilePath(filePath string) (*d2mpq.MPQ, error) { + am.mutex.Lock() + defer am.mutex.Unlock() + + if err := am.cacheArchiveEntries(); err != nil { + return nil, err + } + + for _, archiveEntry := range am.entries { + if archiveEntry.hashEntryMap.Contains(filePath) { + return am.loadArchive(archiveEntry.archivePath) + } + } + + return nil, fmt.Errorf("file not found: %s", filePath) +} + +func (am *archiveManager) loadArchive(archivePath string) (*d2mpq.MPQ, error) { + if archive, found := am.cache.retrieve(archivePath); found { + return archive.(*d2mpq.MPQ), nil + } + + archive, err := d2mpq.Load(archivePath) + if err != nil { + return nil, err + } + + if err := am.cache.insert(archivePath, archive, int(archive.Data.ArchiveSize)); err != nil { + return nil, err + } + + return archive, nil +} + +func (am *archiveManager) cacheArchiveEntries() error { + if len(am.entries) == len(am.config.MpqLoadOrder) { + return nil + } + + am.entries = nil + + for _, archiveName := range am.config.MpqLoadOrder { + archivePath := path.Join(am.config.MpqPath, archiveName) + archive, err := am.loadArchive(archivePath) + if err != nil { + return err + } + + am.entries = append( + am.entries, + archiveEntry{archivePath, archive.HashEntryMap}, + ) + } + + return nil +} diff --git a/d2asset/asset_manager.go b/d2asset/asset_manager.go new file mode 100644 index 00000000..bd93fe3f --- /dev/null +++ b/d2asset/asset_manager.go @@ -0,0 +1,149 @@ +package d2asset + +import ( + "errors" + + "github.com/OpenDiablo2/D2Shared/d2data/d2cof" + "github.com/OpenDiablo2/D2Shared/d2data/d2datadict" + "github.com/OpenDiablo2/D2Shared/d2data/d2dc6" + "github.com/OpenDiablo2/D2Shared/d2data/d2dcc" + "github.com/OpenDiablo2/D2Shared/d2data/d2mpq" + "github.com/OpenDiablo2/OpenDiablo2/d2corecommon" +) + +const ( + // In megabytes + ArchiveBudget = 1024 * 1024 * 512 + FileBudget = 1024 * 1024 * 32 + + // In counts + PaletteBudget = 64 + PaperdollBudget = 64 + AnimationBudget = 64 +) + +var ( + ErrHasInit error = errors.New("asset system is already initialized") + ErrNoInit error = errors.New("asset system is not initialized") +) + +type assetManager struct { + archiveManager *archiveManager + fileManager *fileManager + paletteManager *paletteManager + paperdollManager *paperdollManager + animationManager *animationManager +} + +var singleton *assetManager + +func Initialize(config *d2corecommon.Configuration) error { + if singleton != nil { + return ErrHasInit + } + + var ( + archiveManager = createArchiveManager(config) + fileManager = createFileManager(config, archiveManager) + paletteManager = createPaletteManager() + paperdollManager = createPaperdollManager() + animationManager = createAnimationManager() + ) + + singleton = &assetManager{ + archiveManager, + fileManager, + paletteManager, + paperdollManager, + animationManager, + } + + return nil +} + +func LoadArchive(archivePath string) (*d2mpq.MPQ, error) { + if singleton == nil { + return nil, ErrNoInit + } + + return singleton.archiveManager.loadArchive(archivePath) +} + +func LoadFile(filePath string) ([]byte, error) { + if singleton == nil { + return nil, ErrNoInit + } + + return singleton.fileManager.loadFile(filePath) +} + +func LoadAnimation(animationPath, palettePath string) (*Animation, error) { + if singleton == nil { + return nil, ErrNoInit + } + + return singleton.animationManager.loadAnimation(animationPath, palettePath) +} + +func LoadPaperdoll(object *d2datadict.ObjectLookupRecord, palettePath string) (*Paperdoll, error) { + if singleton == nil { + return nil, ErrNoInit + } + + return singleton.paperdollManager.loadPaperdoll(object, palettePath) +} + +// TODO: remove transitional usage pattern +func MustLoadFile(filePath string) []byte { + data, err := LoadFile(filePath) + if err != nil { + return []byte{} + } + + return data +} + +func loadPalette(palettePath string) (*d2datadict.PaletteRec, error) { + if singleton == nil { + return nil, ErrNoInit + } + + return singleton.paletteManager.loadPalette(palettePath) +} + +func loadDC6(dc6Path, palettePath string) (*d2dc6.DC6File, error) { + dc6Data, err := LoadFile(dc6Path) + if err != nil { + return nil, err + } + + paletteData, err := loadPalette(palettePath) + if err != nil { + return nil, err + } + + dc6, err := d2dc6.LoadDC6(dc6Data, *paletteData) + if err != nil { + return nil, err + } + + return &dc6, nil +} + +func LoadDCC(dccPath string) (*d2dcc.DCC, error) { + dccData, err := LoadFile(dccPath) + if err != nil { + return nil, err + } + + return d2dcc.LoadDCC(dccData) +} + +func LoadCOF(cofPath string) (*d2cof.COF, error) { + cofData, err := LoadFile(cofPath) + if err != nil { + return nil, err + } + + return d2cof.LoadCOF(cofData) +} diff --git a/d2core/cache.go b/d2asset/cache.go similarity index 98% rename from d2core/cache.go rename to d2asset/cache.go index a1406dd3..980edc9b 100644 --- a/d2core/cache.go +++ b/d2asset/cache.go @@ -1,4 +1,4 @@ -package d2core +package d2asset import ( "errors" diff --git a/d2asset/file_manager.go b/d2asset/file_manager.go new file mode 100644 index 00000000..517b621e --- /dev/null +++ b/d2asset/file_manager.go @@ -0,0 +1,56 @@ +package d2asset + +import ( + "strings" + + "github.com/OpenDiablo2/D2Shared/d2common/d2resource" + "github.com/OpenDiablo2/OpenDiablo2/d2corecommon" +) + +type fileManager struct { + cache *cache + archiveManager *archiveManager + config *d2corecommon.Configuration +} + +func createFileManager(config *d2corecommon.Configuration, archiveManager *archiveManager) *fileManager { + return &fileManager{createCache(FileBudget), archiveManager, config} +} + +func (fm *fileManager) loadFile(filePath string) ([]byte, error) { + filePath = fm.fixupFilePath(filePath) + if value, found := fm.cache.retrieve(filePath); found { + return value.([]byte), nil + } + + archive, err := fm.archiveManager.loadArchiveForFilePath(filePath) + if err != nil { + return nil, err + } + + data, err := archive.ReadFile(filePath) + if err != nil { + return nil, err + } + + if err := fm.cache.insert(filePath, data, len(data)); err != nil { + return nil, err + } + + return data, nil +} + +func (fm *fileManager) fixupFilePath(filePath string) string { + filePath = strings.ReplaceAll(filePath, "{LANG}", fm.config.Language) + if strings.ToUpper(d2resource.LanguageCode) == "CHI" { + filePath = strings.ReplaceAll(filePath, "{LANG_FONT}", fm.config.Language) + } else { + filePath = strings.ReplaceAll(filePath, "{LANG_FONT}", "latin") + } + + filePath = strings.ToLower(filePath) + filePath = strings.ReplaceAll(filePath, `/`, "\\") + filePath = strings.TrimPrefix(filePath, "\\") + + return filePath +} diff --git a/d2asset/palette_manager.go b/d2asset/palette_manager.go new file mode 100644 index 00000000..de9f3c02 --- /dev/null +++ b/d2asset/palette_manager.go @@ -0,0 +1,28 @@ +package d2asset + +import ( + "github.com/OpenDiablo2/D2Shared/d2data/d2datadict" +) + +type paletteManager struct { + cache *cache +} + +func createPaletteManager() *paletteManager { + return &paletteManager{createCache(PaletteBudget)} +} + +func (pm *paletteManager) loadPalette(palettePath string) (*d2datadict.PaletteRec, error) { + if palette, found := pm.cache.retrieve(palettePath); found { + return palette.(*d2datadict.PaletteRec), nil + } + + paletteData, err := LoadFile(palettePath) + if err != nil { + return nil, err + } + + palette := d2datadict.CreatePalette("", paletteData) + pm.cache.insert(palettePath, &palette, 1) + return &palette, nil +} diff --git a/d2asset/paperdoll.go b/d2asset/paperdoll.go new file mode 100644 index 00000000..b4a8c04f --- /dev/null +++ b/d2asset/paperdoll.go @@ -0,0 +1,302 @@ +package d2asset + +import ( + "errors" + "fmt" + "image" + "math" + "strings" + + "github.com/OpenDiablo2/D2Shared/d2common/d2enum" + "github.com/OpenDiablo2/D2Shared/d2data" + "github.com/OpenDiablo2/D2Shared/d2data/d2datadict" + "github.com/OpenDiablo2/D2Shared/d2data/d2dcc" + "github.com/OpenDiablo2/D2Shared/d2helper" + "github.com/hajimehoshi/ebiten" +) + +type paperdollCacheEntry struct { + sheetImage *ebiten.Image + compositeMode ebiten.CompositeMode + width int + height int + offsetX int + offsetY int +} + +type Paperdoll struct { + object *d2datadict.ObjectLookupRecord + palette *d2datadict.PaletteRec + + mode *paperdollMode +} + +func createPaperdoll(object *d2datadict.ObjectLookupRecord, palette *d2datadict.PaletteRec) *Paperdoll { + return &Paperdoll{object: object, palette: palette} +} + +func (p *Paperdoll) Render(target *ebiten.Image, offsetX, offsetY int) { + if p.mode == nil { + return + } + + if p.mode.animationSpeed > 0 { + frameTime := d2helper.Now() + framesToAdd := int(math.Floor((frameTime - p.mode.lastFrameTime) / p.mode.animationSpeed)) + if framesToAdd > 0 { + p.mode.lastFrameTime += p.mode.animationSpeed * float64(framesToAdd) + p.mode.currentFrame = (p.mode.currentFrame + framesToAdd) % p.mode.frameCount + } + } + + for _, layerIndex := range p.mode.drawOrder[p.mode.currentFrame] { + cacheEntry := p.mode.layerCache[layerIndex] + + x := float64(offsetX) + float64(p.mode.layerCache[layerIndex].offsetX) + y := float64(offsetY) + float64(p.mode.layerCache[layerIndex].offsetY) + + sheetOffset := cacheEntry.width * p.mode.currentFrame + sheetRect := image.Rect(sheetOffset, 0, sheetOffset+cacheEntry.width, cacheEntry.height) + + opts := &ebiten.DrawImageOptions{} + opts.GeoM.Translate(x, y) + opts.CompositeMode = cacheEntry.compositeMode + target.DrawImage(cacheEntry.sheetImage.SubImage(sheetRect).(*ebiten.Image), opts) + } +} + +func (p *Paperdoll) SetMode(animationMode, weaponClass string, direction int) error { + mode, err := p.createMode(animationMode, weaponClass, direction) + if err != nil { + return err + } + + p.mode = mode + return nil +} + +type paperdollMode struct { + animationMode string + weaponClass string + direction int + + layers []*d2dcc.DCC + layerCache []*paperdollCacheEntry + drawOrder [][]d2enum.CompositeType + + frameCount int + animationSpeed float64 + currentFrame int + lastFrameTime float64 +} + +func (p *Paperdoll) createMode(animationMode, weaponClass string, direction int) (*paperdollMode, error) { + mode := &paperdollMode{ + animationMode: animationMode, + weaponClass: weaponClass, + direction: direction, + } + + cofPath := fmt.Sprintf( + "%s/%s/COF/%s%s%s.COF", + p.object.Base, + p.object.Token, + p.object.Token, + mode.animationMode, + mode.weaponClass, + ) + + cof, err := LoadCOF(cofPath) + if err != nil { + return nil, err + } + + if mode.direction >= cof.NumberOfDirections { + return nil, errors.New("invalid direction") + } + + mode.layers = make([]*d2dcc.DCC, d2enum.CompositeTypeMax) + for _, cofLayer := range cof.CofLayers { + var layerKey, layerValue string + switch cofLayer.Type { + case d2enum.CompositeTypeHead: + layerKey = "HD" + layerValue = p.object.HD + case d2enum.CompositeTypeTorso: + layerKey = "TR" + layerValue = p.object.TR + case d2enum.CompositeTypeLegs: + layerKey = "LG" + layerValue = p.object.LG + case d2enum.CompositeTypeRightArm: + layerKey = "RA" + layerValue = p.object.RA + case d2enum.CompositeTypeLeftArm: + layerKey = "LA" + layerValue = p.object.LA + case d2enum.CompositeTypeRightHand: + layerKey = "RH" + layerValue = p.object.RH + case d2enum.CompositeTypeLeftHand: + layerKey = "LH" + layerValue = p.object.LH + case d2enum.CompositeTypeShield: + layerKey = "SH" + layerValue = p.object.SH + case d2enum.CompositeTypeSpecial1: + layerKey = "S1" + layerValue = p.object.S1 + case d2enum.CompositeTypeSpecial2: + layerKey = "S2" + layerValue = p.object.S2 + case d2enum.CompositeTypeSpecial3: + layerKey = "S3" + layerValue = p.object.S3 + case d2enum.CompositeTypeSpecial4: + layerKey = "S4" + layerValue = p.object.S4 + case d2enum.CompositeTypeSpecial5: + layerKey = "S5" + layerValue = p.object.S5 + case d2enum.CompositeTypeSpecial6: + layerKey = "S6" + layerValue = p.object.S6 + case d2enum.CompositeTypeSpecial7: + layerKey = "S7" + layerValue = p.object.S7 + case d2enum.CompositeTypeSpecial8: + layerKey = "S8" + layerValue = p.object.S8 + default: + return nil, errors.New("unknown layer type") + } + + layerPath := fmt.Sprintf( + "%s/%s/%s/%s%s%s%s%s.dcc", + p.object.Base, + p.object.Token, + layerKey, + p.object.Token, + layerKey, + layerValue, + mode.animationMode, + mode.weaponClass, + ) + + dcc, err := LoadDCC(layerPath) + if err != nil { + return nil, err + } + + mode.layers[cofLayer.Type] = dcc + } + + animationKey := strings.ToLower(p.object.Token + mode.animationMode + mode.weaponClass) + animationData := d2data.AnimationData[animationKey] + if len(animationData) == 0 { + return nil, errors.New("could not find animation data") + } + + mode.animationSpeed = 1.0 / ((float64(animationData[0].AnimationSpeed) * 25.0) / 256.0) + mode.lastFrameTime = d2helper.Now() + mode.frameCount = animationData[0].FramesPerDirection + + var dccDirection int + switch cof.NumberOfDirections { + case 4: + dccDirection = d2dcc.CofToDir4[mode.direction] + case 8: + dccDirection = d2dcc.CofToDir8[mode.direction] + case 16: + dccDirection = d2dcc.CofToDir16[mode.direction] + case 32: + dccDirection = d2dcc.CofToDir32[mode.direction] + } + + mode.drawOrder = make([][]d2enum.CompositeType, mode.frameCount) + for frame := 0; frame < mode.frameCount; frame++ { + mode.drawOrder[frame] = cof.Priority[direction][frame] + } + + mode.layerCache = make([]*paperdollCacheEntry, d2enum.CompositeTypeMax) + for _, cofLayer := range cof.CofLayers { + layer := mode.layers[cofLayer.Type] + + minX, minY := math.MaxInt32, math.MaxInt32 + maxX, maxY := math.MinInt32, math.MinInt32 + for _, frame := range layer.Directions[dccDirection].Frames { + minX = d2helper.MinInt(minX, frame.Box.Left) + minY = d2helper.MinInt(minY, frame.Box.Top) + maxX = d2helper.MaxInt(maxX, frame.Box.Right()) + maxY = d2helper.MaxInt(maxY, frame.Box.Bottom()) + } + + cacheEntry := &paperdollCacheEntry{ + offsetX: minX, + offsetY: minY, + width: maxX - minX, + height: maxY - minY, + } + + if cacheEntry.width <= 0 || cacheEntry.height <= 0 { + return nil, errors.New("invalid animation size") + } + + var transparency int + if cofLayer.Transparent { + switch cofLayer.DrawEffect { + case d2enum.DrawEffectPctTransparency25: + transparency = 64 + case d2enum.DrawEffectPctTransparency50: + transparency = 128 + case d2enum.DrawEffectPctTransparency75: + transparency = 192 + case d2enum.DrawEffectModulate: + cacheEntry.compositeMode = ebiten.CompositeModeLighter + default: + transparency = 255 + } + } + + pixels := make([]byte, mode.frameCount*cacheEntry.width*cacheEntry.height*4) + + for i := 0; i < mode.frameCount; i++ { + direction := layer.Directions[dccDirection] + if i >= len(direction.Frames) { + return nil, errors.New("invalid animation index") + } + + sheetOffset := cacheEntry.width * i + sheetWidth := cacheEntry.height * mode.frameCount + + frame := direction.Frames[i] + for y := 0; y < direction.Box.Height; y++ { + for x := 0; x < direction.Box.Width; x++ { + if paletteIndex := frame.PixelData[x+(y*direction.Box.Width)]; paletteIndex != 0 { + color := p.palette.Colors[paletteIndex] + frameX := (x + direction.Box.Left) - minX + frameY := (y + direction.Box.Top) - minY + offset := (sheetOffset + frameX + (frameY * sheetWidth)) * 4 + pixels[offset] = color.R + pixels[offset+1] = color.G + pixels[offset+2] = color.B + pixels[offset+3] = byte(transparency) + } + } + } + } + + cacheEntry.sheetImage, err = ebiten.NewImage(cacheEntry.width*mode.frameCount, cacheEntry.height, ebiten.FilterNearest) + if err != nil { + return nil, err + } + + if err := cacheEntry.sheetImage.ReplacePixels(pixels); err != nil { + return nil, err + } + + mode.layerCache[cofLayer.Type] = cacheEntry + } + + return mode, nil +} diff --git a/d2asset/paperdoll_manager.go b/d2asset/paperdoll_manager.go new file mode 100644 index 00000000..78f6a029 --- /dev/null +++ b/d2asset/paperdoll_manager.go @@ -0,0 +1,22 @@ +package d2asset + +import ( + "github.com/OpenDiablo2/D2Shared/d2data/d2datadict" +) + +type paperdollManager struct { + cache *cache +} + +func createPaperdollManager() *paperdollManager { + return &paperdollManager{cache: createCache(PaperdollBudget)} +} + +func (pm *paperdollManager) loadPaperdoll(object *d2datadict.ObjectLookupRecord, palettePath string) (*Paperdoll, error) { + palette, err := loadPalette(palettePath) + if err != nil { + return nil, err + } + + return createPaperdoll(object, palette), nil +} diff --git a/d2audio/audio_provider.go b/d2audio/audio_provider.go index 19216dbb..71be4a0f 100644 --- a/d2audio/audio_provider.go +++ b/d2audio/audio_provider.go @@ -3,15 +3,13 @@ package d2audio import ( "log" - "github.com/OpenDiablo2/D2Shared/d2common/d2interface" - + "github.com/OpenDiablo2/OpenDiablo2/d2asset" "github.com/hajimehoshi/ebiten/audio" "github.com/hajimehoshi/ebiten/audio/wav" ) // Manager provides sound type Manager struct { - fileProvider d2interface.FileProvider audioContext *audio.Context // The Audio context bgmAudio *audio.Player // The audio player lastBgm string @@ -20,10 +18,8 @@ type Manager struct { } // CreateManager creates a sound provider -func CreateManager(fileProvider d2interface.FileProvider) *Manager { - result := &Manager{ - fileProvider: fileProvider, - } +func CreateManager() *Manager { + result := &Manager{} audioContext, err := audio.NewContext(44100) if err != nil { log.Fatal(err) @@ -49,7 +45,7 @@ func (v *Manager) PlayBGM(song string) { log.Panic(err) } } - audioData := v.fileProvider.LoadFile(song) + audioData := d2asset.MustLoadFile(song) d, err := wav.Decode(v.audioContext, audio.BytesReadSeekCloser(audioData)) if err != nil { log.Fatal(err) @@ -73,7 +69,7 @@ func (v *Manager) PlayBGM(song string) { } func (v *Manager) LoadSoundEffect(sfx string) *SoundEffect { - result := CreateSoundEffect(sfx, v.fileProvider, v.audioContext, v.sfxVolume) + result := CreateSoundEffect(sfx, v.audioContext, v.sfxVolume) return result } diff --git a/d2audio/sound_effect.go b/d2audio/sound_effect.go index 5ccbba27..fd4d042e 100644 --- a/d2audio/sound_effect.go +++ b/d2audio/sound_effect.go @@ -3,9 +3,8 @@ package d2audio import ( "log" - "github.com/OpenDiablo2/D2Shared/d2common/d2interface" - "github.com/OpenDiablo2/D2Shared/d2data/d2datadict" + "github.com/OpenDiablo2/OpenDiablo2/d2asset" "github.com/hajimehoshi/ebiten/audio/wav" @@ -16,7 +15,7 @@ type SoundEffect struct { player *audio.Player } -func CreateSoundEffect(sfx string, fileProvider d2interface.FileProvider, context *audio.Context, volume float64) *SoundEffect { +func CreateSoundEffect(sfx string, context *audio.Context, volume float64) *SoundEffect { result := &SoundEffect{} var soundFile string if _, exists := d2datadict.Sounds[sfx]; exists { @@ -25,7 +24,7 @@ func CreateSoundEffect(sfx string, fileProvider d2interface.FileProvider, contex } else { soundFile = sfx } - audioData := fileProvider.LoadFile(soundFile) + audioData := d2asset.MustLoadFile(soundFile) d, err := wav.Decode(context, audio.BytesReadSeekCloser(audioData)) if err != nil { log.Fatal(err) diff --git a/d2core/asset_manager.go b/d2core/asset_manager.go deleted file mode 100644 index 9a3bc21a..00000000 --- a/d2core/asset_manager.go +++ /dev/null @@ -1,137 +0,0 @@ -package d2core - -import ( - "fmt" - "log" - "path" - "strings" - "sync" - - "github.com/OpenDiablo2/D2Shared/d2common/d2resource" - "github.com/OpenDiablo2/D2Shared/d2data/d2mpq" - "github.com/OpenDiablo2/OpenDiablo2/d2corecommon" -) - -type archiveEntry struct { - archivePath string - hashEntryMap d2mpq.HashEntryMap -} - -type assetManager struct { - fileCache *cache - archiveCache *cache - archiveEntries []archiveEntry - config *d2corecommon.Configuration - mutex sync.Mutex -} - -func createAssetManager(config *d2corecommon.Configuration) *assetManager { - return &assetManager{ - fileCache: createCache(1024 * 1024 * 32), - archiveCache: createCache(1024 * 1024 * 128), - config: config, - } -} - -func (am *assetManager) LoadFile(filePath string) []byte { - data, err := am.loadFile(am.fixupFilePath(filePath)) - if err != nil { - log.Println(err) - } - - return data -} - -func (am *assetManager) loadFile(filePath string) ([]byte, error) { - if value, found := am.fileCache.retrieve(filePath); found { - return value.([]byte), nil - } - - archive, err := am.loadArchiveForFilePath(filePath) - if err != nil { - return nil, err - } - - data, err := archive.ReadFile(filePath) - if err != nil { - return nil, err - } - - if err := am.fileCache.insert(filePath, data, len(data)); err != nil { - return nil, err - } - - return data, nil -} - -func (am *assetManager) loadArchiveForFilePath(filePath string) (*d2mpq.MPQ, error) { - am.mutex.Lock() - defer am.mutex.Unlock() - - if err := am.cacheArchiveEntries(); err != nil { - return nil, err - } - - for _, archiveEntry := range am.archiveEntries { - if archiveEntry.hashEntryMap.Contains(filePath) { - return am.loadArchive(archiveEntry.archivePath) - } - } - - return nil, fmt.Errorf("file not found: %s", filePath) -} - -func (am *assetManager) loadArchive(archivePath string) (*d2mpq.MPQ, error) { - if archive, found := am.archiveCache.retrieve(archivePath); found { - return archive.(*d2mpq.MPQ), nil - } - - archive, err := d2mpq.Load(archivePath) - if err != nil { - return nil, err - } - - if err := am.archiveCache.insert(archivePath, archive, int(archive.Data.ArchiveSize)); err != nil { - return nil, err - } - - return archive, nil -} - -func (am *assetManager) cacheArchiveEntries() error { - if len(am.archiveEntries) == len(am.config.MpqLoadOrder) { - return nil - } - - am.archiveEntries = nil - - for _, archiveName := range am.config.MpqLoadOrder { - archivePath := path.Join(am.config.MpqPath, archiveName) - archive, err := am.loadArchive(archivePath) - if err != nil { - return err - } - - am.archiveEntries = append( - am.archiveEntries, - archiveEntry{archivePath, archive.HashEntryMap}, - ) - } - - return nil -} - -func (am *assetManager) fixupFilePath(filePath string) string { - filePath = strings.ReplaceAll(filePath, "{LANG}", am.config.Language) - if strings.ToUpper(d2resource.LanguageCode) == "CHI" { - filePath = strings.ReplaceAll(filePath, "{LANG_FONT}", am.config.Language) - } else { - filePath = strings.ReplaceAll(filePath, "{LANG_FONT}", "latin") - } - - filePath = strings.ToLower(filePath) - filePath = strings.ReplaceAll(filePath, `/`, "\\") - filePath = strings.TrimPrefix(filePath, "\\") - - return filePath -} diff --git a/d2core/d2scene/blizzard_intro.go b/d2core/d2scene/blizzard_intro.go index 8e759828..fc885d4e 100644 --- a/d2core/d2scene/blizzard_intro.go +++ b/d2core/d2scene/blizzard_intro.go @@ -1,23 +1,19 @@ package d2scene import ( - "github.com/OpenDiablo2/D2Shared/d2common/d2interface" - "github.com/OpenDiablo2/OpenDiablo2/d2corecommon/d2coreinterface" "github.com/OpenDiablo2/D2Shared/d2data/d2video" + "github.com/OpenDiablo2/OpenDiablo2/d2asset" + "github.com/OpenDiablo2/OpenDiablo2/d2corecommon/d2coreinterface" "github.com/hajimehoshi/ebiten" ) type BlizzardIntro struct { - fileProvider d2interface.FileProvider sceneProvider d2coreinterface.SceneProvider videoDecoder *d2video.BinkDecoder } -func CreateBlizzardIntro(fileProvider d2interface.FileProvider, sceneProvider d2coreinterface.SceneProvider) *BlizzardIntro { - result := &BlizzardIntro{ - fileProvider: fileProvider, - sceneProvider: sceneProvider, - } +func CreateBlizzardIntro(sceneProvider d2coreinterface.SceneProvider) *BlizzardIntro { + result := &BlizzardIntro{sceneProvider: sceneProvider} return result } @@ -25,7 +21,7 @@ func CreateBlizzardIntro(fileProvider d2interface.FileProvider, sceneProvider d2 func (v *BlizzardIntro) Load() []func() { return []func(){ func() { - videoBytes := v.fileProvider.LoadFile("/data/local/video/BlizNorth640x480.bik") + videoBytes := d2asset.MustLoadFile("/data/local/video/BlizNorth640x480.bik") v.videoDecoder = d2video.CreateBinkDecoder(videoBytes) }, } diff --git a/d2core/d2scene/character_select.go b/d2core/d2scene/character_select.go index e51b9fbf..56ca0f46 100644 --- a/d2core/d2scene/character_select.go +++ b/d2core/d2scene/character_select.go @@ -6,15 +6,10 @@ import ( "os" "strings" - "github.com/OpenDiablo2/D2Shared/d2data/d2dc6" - "github.com/hajimehoshi/ebiten/ebitenutil" "github.com/OpenDiablo2/D2Shared/d2common" - "github.com/OpenDiablo2/D2Shared/d2common/d2enum" - "github.com/OpenDiablo2/D2Shared/d2common/d2interface" "github.com/OpenDiablo2/D2Shared/d2common/d2resource" - "github.com/OpenDiablo2/D2Shared/d2data/d2datadict" dh "github.com/OpenDiablo2/D2Shared/d2helper" "github.com/OpenDiablo2/OpenDiablo2/d2audio" "github.com/OpenDiablo2/OpenDiablo2/d2core" @@ -27,9 +22,8 @@ import ( type CharacterSelect struct { uiManager *d2ui.Manager soundManager *d2audio.Manager - fileProvider d2interface.FileProvider sceneProvider d2coreinterface.SceneProvider - background d2render.Sprite + background *d2render.Sprite newCharButton d2ui.Button convertCharButton d2ui.Button deleteCharButton d2ui.Button @@ -37,8 +31,8 @@ type CharacterSelect struct { okButton d2ui.Button deleteCharCancelButton d2ui.Button deleteCharOkButton d2ui.Button - selectionBox d2render.Sprite - okCancelBox d2render.Sprite + selectionBox *d2render.Sprite + okCancelBox *d2render.Sprite d2HeroTitle d2ui.Label deleteCharConfirmLabel d2ui.Label charScrollbar d2ui.Scrollbar @@ -52,17 +46,11 @@ type CharacterSelect struct { showDeleteConfirmation bool } -func CreateCharacterSelect( - fileProvider d2interface.FileProvider, - sceneProvider d2coreinterface.SceneProvider, - uiManager *d2ui.Manager, - soundManager *d2audio.Manager, -) *CharacterSelect { +func CreateCharacterSelect(sceneProvider d2coreinterface.SceneProvider, uiManager *d2ui.Manager, soundManager *d2audio.Manager) *CharacterSelect { result := &CharacterSelect{ selectedCharacter: -1, uiManager: uiManager, sceneProvider: sceneProvider, - fileProvider: fileProvider, soundManager: soundManager, } return result @@ -72,78 +60,75 @@ func (v *CharacterSelect) Load() []func() { v.soundManager.PlayBGM(d2resource.BGMTitle) return []func(){ func() { - dc6, _ := d2dc6.LoadDC6(v.fileProvider.LoadFile(d2resource.CharacterSelectionBackground), d2datadict.Palettes[d2enum.Sky]) - v.background = d2render.CreateSpriteFromDC6(dc6) - v.background.MoveTo(0, 0) + v.background, _ = d2render.LoadSprite(d2resource.CharacterSelectionBackground, d2resource.PaletteSky) + v.background.SetPosition(0, 0) }, func() { - v.newCharButton = d2ui.CreateButton(d2ui.ButtonTypeTall, v.fileProvider, dh.CombineStrings(dh.SplitIntoLinesWithMaxWidth(d2common.TranslateString("#831"), 15))) - v.newCharButton.MoveTo(33, 468) + v.newCharButton = d2ui.CreateButton(d2ui.ButtonTypeTall, dh.CombineStrings(dh.SplitIntoLinesWithMaxWidth(d2common.TranslateString("#831"), 15))) + v.newCharButton.SetPosition(33, 468) v.newCharButton.OnActivated(func() { v.onNewCharButtonClicked() }) v.uiManager.AddWidget(&v.newCharButton) }, func() { - v.convertCharButton = d2ui.CreateButton(d2ui.ButtonTypeTall, v.fileProvider, dh.CombineStrings(dh.SplitIntoLinesWithMaxWidth(d2common.TranslateString("#825"), 15))) - v.convertCharButton.MoveTo(233, 468) + v.convertCharButton = d2ui.CreateButton(d2ui.ButtonTypeTall, dh.CombineStrings(dh.SplitIntoLinesWithMaxWidth(d2common.TranslateString("#825"), 15))) + v.convertCharButton.SetPosition(233, 468) v.convertCharButton.SetEnabled(false) v.uiManager.AddWidget(&v.convertCharButton) }, func() { - v.deleteCharButton = d2ui.CreateButton(d2ui.ButtonTypeTall, v.fileProvider, dh.CombineStrings(dh.SplitIntoLinesWithMaxWidth(d2common.TranslateString("#832"), 15))) + v.deleteCharButton = d2ui.CreateButton(d2ui.ButtonTypeTall, dh.CombineStrings(dh.SplitIntoLinesWithMaxWidth(d2common.TranslateString("#832"), 15))) v.deleteCharButton.OnActivated(func() { v.onDeleteCharButtonClicked() }) - v.deleteCharButton.MoveTo(433, 468) + v.deleteCharButton.SetPosition(433, 468) v.uiManager.AddWidget(&v.deleteCharButton) }, func() { - v.exitButton = d2ui.CreateButton(d2ui.ButtonTypeMedium, v.fileProvider, d2common.TranslateString("#970")) - v.exitButton.MoveTo(33, 537) + v.exitButton = d2ui.CreateButton(d2ui.ButtonTypeMedium, d2common.TranslateString("#970")) + v.exitButton.SetPosition(33, 537) v.exitButton.OnActivated(func() { v.onExitButtonClicked() }) v.uiManager.AddWidget(&v.exitButton) }, func() { - v.deleteCharCancelButton = d2ui.CreateButton(d2ui.ButtonTypeOkCancel, v.fileProvider, d2common.TranslateString("#4231")) - v.deleteCharCancelButton.MoveTo(282, 308) + v.deleteCharCancelButton = d2ui.CreateButton(d2ui.ButtonTypeOkCancel, d2common.TranslateString("#4231")) + v.deleteCharCancelButton.SetPosition(282, 308) v.deleteCharCancelButton.SetVisible(false) v.deleteCharCancelButton.OnActivated(func() { v.onDeleteCharacterCancelClicked() }) v.uiManager.AddWidget(&v.deleteCharCancelButton) }, func() { - v.deleteCharOkButton = d2ui.CreateButton(d2ui.ButtonTypeOkCancel, v.fileProvider, d2common.TranslateString("#4227")) - v.deleteCharOkButton.MoveTo(422, 308) + v.deleteCharOkButton = d2ui.CreateButton(d2ui.ButtonTypeOkCancel, d2common.TranslateString("#4227")) + v.deleteCharOkButton.SetPosition(422, 308) v.deleteCharOkButton.SetVisible(false) v.deleteCharOkButton.OnActivated(func() { v.onDeleteCharacterConfirmClicked() }) v.uiManager.AddWidget(&v.deleteCharOkButton) }, func() { - v.okButton = d2ui.CreateButton(d2ui.ButtonTypeMedium, v.fileProvider, d2common.TranslateString("#971")) - v.okButton.MoveTo(625, 537) + v.okButton = d2ui.CreateButton(d2ui.ButtonTypeMedium, d2common.TranslateString("#971")) + v.okButton.SetPosition(625, 537) v.okButton.OnActivated(func() { v.onOkButtonClicked() }) v.uiManager.AddWidget(&v.okButton) }, func() { - v.d2HeroTitle = d2ui.CreateLabel(v.fileProvider, d2resource.Font42, d2enum.Units) - v.d2HeroTitle.MoveTo(320, 23) + v.d2HeroTitle = d2ui.CreateLabel(d2resource.Font42, d2resource.PaletteUnits) + v.d2HeroTitle.SetPosition(320, 23) v.d2HeroTitle.Alignment = d2ui.LabelAlignCenter }, func() { - v.deleteCharConfirmLabel = d2ui.CreateLabel(v.fileProvider, d2resource.Font16, d2enum.Units) + v.deleteCharConfirmLabel = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits) lines := dh.SplitIntoLinesWithMaxWidth(d2common.TranslateString("#1878"), 29) v.deleteCharConfirmLabel.SetText(strings.Join(lines, "\n")) v.deleteCharConfirmLabel.Alignment = d2ui.LabelAlignCenter - v.deleteCharConfirmLabel.MoveTo(400, 185) + v.deleteCharConfirmLabel.SetPosition(400, 185) }, func() { - dc6, _ := d2dc6.LoadDC6(v.fileProvider.LoadFile(d2resource.CharacterSelectionSelectBox), d2datadict.Palettes[d2enum.Sky]) - v.selectionBox = d2render.CreateSpriteFromDC6(dc6) - v.selectionBox.MoveTo(37, 86) + v.selectionBox, _ = d2render.LoadSprite(d2resource.CharacterSelectionSelectBox, d2resource.PaletteSky) + v.selectionBox.SetPosition(37, 86) }, func() { - dc6, _ := d2dc6.LoadDC6(v.fileProvider.LoadFile(d2resource.PopUpOkCancel), d2datadict.Palettes[d2enum.Fechar]) - v.okCancelBox = d2render.CreateSpriteFromDC6(dc6) - v.okCancelBox.MoveTo(270, 175) + v.okCancelBox, _ = d2render.LoadSprite(d2resource.PopUpOkCancel, d2resource.PaletteFechar) + v.okCancelBox.SetPosition(270, 175) }, func() { - v.charScrollbar = d2ui.CreateScrollbar(v.fileProvider, 586, 87, 369) + v.charScrollbar = d2ui.CreateScrollbar(586, 87, 369) v.charScrollbar.OnActivated(func() { v.onScrollUpdate() }) v.uiManager.AddWidget(&v.charScrollbar) }, @@ -153,14 +138,14 @@ func (v *CharacterSelect) Load() []func() { if i&1 > 0 { xOffset = 385 } - v.characterNameLabel[i] = d2ui.CreateLabel(v.fileProvider, d2resource.Font16, d2enum.Units) + v.characterNameLabel[i] = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits) v.characterNameLabel[i].Color = color.RGBA{188, 168, 140, 255} - v.characterNameLabel[i].MoveTo(xOffset, 100+((i/2)*95)) - v.characterStatsLabel[i] = d2ui.CreateLabel(v.fileProvider, d2resource.Font16, d2enum.Units) - v.characterStatsLabel[i].MoveTo(xOffset, 115+((i/2)*95)) - v.characterExpLabel[i] = d2ui.CreateLabel(v.fileProvider, d2resource.Font16, d2enum.Static) + v.characterNameLabel[i].SetPosition(xOffset, 100+((i/2)*95)) + v.characterStatsLabel[i] = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits) + v.characterStatsLabel[i].SetPosition(xOffset, 115+((i/2)*95)) + v.characterExpLabel[i] = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteStatic) v.characterExpLabel[i].Color = color.RGBA{24, 255, 0, 255} - v.characterExpLabel[i].MoveTo(xOffset, 130+((i/2)*95)) + v.characterExpLabel[i].SetPosition(xOffset, 130+((i/2)*95)) } v.refreshGameStates() }, @@ -193,17 +178,16 @@ func (v *CharacterSelect) updateCharacterBoxes() { 0, v.gameStates[idx].HeroType, d2core.HeroObjects[v.gameStates[idx].HeroType], - v.fileProvider, ) } } func (v *CharacterSelect) onNewCharButtonClicked() { - v.sceneProvider.SetNextScene(CreateSelectHeroClass(v.fileProvider, v.sceneProvider, v.uiManager, v.soundManager)) + v.sceneProvider.SetNextScene(CreateSelectHeroClass(v.sceneProvider, v.uiManager, v.soundManager)) } func (v *CharacterSelect) onExitButtonClicked() { - mainMenu := CreateMainMenu(v.fileProvider, v.sceneProvider, v.uiManager, v.soundManager) + mainMenu := CreateMainMenu(v.sceneProvider, v.uiManager, v.soundManager) mainMenu.ShowTrademarkScreen = false v.sceneProvider.SetNextScene(mainMenu) } @@ -212,26 +196,26 @@ func (v *CharacterSelect) Unload() { } func (v *CharacterSelect) Render(screen *ebiten.Image) { - v.background.DrawSegments(screen, 4, 3, 0) - v.d2HeroTitle.Draw(screen) + v.background.RenderSegmented(screen, 4, 3, 0) + v.d2HeroTitle.Render(screen) actualSelectionIndex := v.selectedCharacter - (v.charScrollbar.GetCurrentOffset() * 2) if v.selectedCharacter > -1 && actualSelectionIndex >= 0 && actualSelectionIndex < 8 { - v.selectionBox.DrawSegments(screen, 2, 1, 0) + v.selectionBox.RenderSegmented(screen, 2, 1, 0) } for i := 0; i < 8; i++ { idx := i + (v.charScrollbar.GetCurrentOffset() * 2) if idx >= len(v.gameStates) { continue } - v.characterNameLabel[i].Draw(screen) - v.characterStatsLabel[i].Draw(screen) - v.characterExpLabel[i].Draw(screen) + v.characterNameLabel[i].Render(screen) + v.characterStatsLabel[i].Render(screen) + v.characterExpLabel[i].Render(screen) v.characterImage[i].Render(screen, v.characterNameLabel[i].X-40, v.characterNameLabel[i].Y+50) } if v.showDeleteConfirmation { ebitenutil.DrawRect(screen, 0.0, 0.0, 800.0, 600.0, color.RGBA{0, 0, 0, 128}) - v.okCancelBox.DrawSegments(screen, 2, 1, 0) - v.deleteCharConfirmLabel.Draw(screen) + v.okCancelBox.RenderSegmented(screen, 2, 1, 0) + v.deleteCharConfirmLabel.Render(screen) } } @@ -243,7 +227,7 @@ func (v *CharacterSelect) moveSelectionBox() { bw := 272 bh := 92 selectedIndex := v.selectedCharacter - (v.charScrollbar.GetCurrentOffset() * 2) - v.selectionBox.MoveTo(37+((selectedIndex&1)*int(bw)), 86+(int(bh)*(selectedIndex/2))) + v.selectionBox.SetPosition(37+((selectedIndex&1)*int(bw)), 86+(int(bh)*(selectedIndex/2))) v.d2HeroTitle.SetText(v.gameStates[v.selectedCharacter].HeroName) } @@ -318,5 +302,5 @@ func (v *CharacterSelect) refreshGameStates() { } func (v *CharacterSelect) onOkButtonClicked() { - v.sceneProvider.SetNextScene(CreateGame(v.fileProvider, v.sceneProvider, v.uiManager, v.soundManager, v.gameStates[v.selectedCharacter])) + v.sceneProvider.SetNextScene(CreateGame(v.sceneProvider, v.uiManager, v.soundManager, v.gameStates[v.selectedCharacter])) } diff --git a/d2core/d2scene/credits.go b/d2core/d2scene/credits.go index a915ada2..d1186d7c 100644 --- a/d2core/d2scene/credits.go +++ b/d2core/d2scene/credits.go @@ -2,7 +2,6 @@ package d2scene import ( "bufio" - "github.com/OpenDiablo2/D2Shared/d2data/d2dc6" "image/color" "log" "os" @@ -11,18 +10,14 @@ import ( "github.com/OpenDiablo2/D2Shared/d2common/d2resource" - "github.com/OpenDiablo2/D2Shared/d2data/d2datadict" - - "github.com/OpenDiablo2/D2Shared/d2common/d2enum" - + "github.com/OpenDiablo2/OpenDiablo2/d2asset" "github.com/OpenDiablo2/OpenDiablo2/d2render" - "github.com/OpenDiablo2/D2Shared/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2corecommon/d2coreinterface" - "github.com/OpenDiablo2/OpenDiablo2/d2audio" "github.com/OpenDiablo2/D2Shared/d2common" dh "github.com/OpenDiablo2/D2Shared/d2helper" + "github.com/OpenDiablo2/OpenDiablo2/d2audio" "github.com/OpenDiablo2/OpenDiablo2/d2render/d2ui" "github.com/hajimehoshi/ebiten" ) @@ -37,9 +32,8 @@ type labelItem struct { type Credits struct { uiManager *d2ui.Manager soundManager *d2audio.Manager - fileProvider d2interface.FileProvider sceneProvider d2coreinterface.SceneProvider - creditsBackground d2render.Sprite + creditsBackground *d2render.Sprite exitButton d2ui.Button creditsText []string labels []*labelItem @@ -49,9 +43,8 @@ type Credits struct { } // CreateCredits creates an instance of the credits scene -func CreateCredits(fileProvider d2interface.FileProvider, sceneProvider d2coreinterface.SceneProvider, uiManager *d2ui.Manager, soundManager *d2audio.Manager) *Credits { +func CreateCredits(sceneProvider d2coreinterface.SceneProvider, uiManager *d2ui.Manager, soundManager *d2audio.Manager) *Credits { result := &Credits{ - fileProvider: fileProvider, uiManager: uiManager, soundManager: soundManager, sceneProvider: sceneProvider, @@ -84,18 +77,17 @@ func (v *Credits) LoadContributors() []string { func (v *Credits) Load() []func() { return []func(){ func() { - dc6, _ := d2dc6.LoadDC6(v.fileProvider.LoadFile(d2resource.CreditsBackground), d2datadict.Palettes[d2enum.Sky]) - v.creditsBackground = d2render.CreateSpriteFromDC6(dc6) - v.creditsBackground.MoveTo(0, 0) + v.creditsBackground, _ = d2render.LoadSprite(d2resource.CreditsBackground, d2resource.PaletteSky) + v.creditsBackground.SetPosition(0, 0) }, func() { - v.exitButton = d2ui.CreateButton(d2ui.ButtonTypeMedium, v.fileProvider, d2common.TranslateString("#970")) - v.exitButton.MoveTo(33, 543) + v.exitButton = d2ui.CreateButton(d2ui.ButtonTypeMedium, d2common.TranslateString("#970")) + v.exitButton.SetPosition(33, 543) v.exitButton.OnActivated(func() { v.onExitButtonClicked() }) v.uiManager.AddWidget(&v.exitButton) }, func() { - fileData, _ := dh.Utf16BytesToString(v.fileProvider.LoadFile(d2resource.CreditsText)[2:]) + fileData, _ := dh.Utf16BytesToString(d2asset.MustLoadFile(d2resource.CreditsText)[2:]) v.creditsText = strings.Split(fileData, "\r\n") for i := range v.creditsText { v.creditsText[i] = strings.Trim(v.creditsText[i], " ") @@ -112,12 +104,12 @@ func (v *Credits) Unload() { // Render renders the credits scene func (v *Credits) Render(screen *ebiten.Image) { - v.creditsBackground.DrawSegments(screen, 4, 3, 0) + v.creditsBackground.RenderSegmented(screen, 4, 3, 0) for _, label := range v.labels { if label.Available { continue } - label.Label.Draw(screen) + label.Label.Render(screen) } } @@ -147,7 +139,7 @@ func (v *Credits) Update(tickTime float64) { } func (v *Credits) onExitButtonClicked() { - mainMenu := CreateMainMenu(v.fileProvider, v.sceneProvider, v.uiManager, v.soundManager) + mainMenu := CreateMainMenu(v.sceneProvider, v.uiManager, v.soundManager) mainMenu.ShowTrademarkScreen = false v.sceneProvider.SetNextScene(mainMenu) } @@ -182,7 +174,7 @@ func (v *Credits) addNextItem() { isDoubled = true // Gotta go side by side - label.MoveTo(400-int(width), 605) + label.SetPosition(400-int(width), 605) text2 := v.creditsText[0] v.creditsText = v.creditsText[1:] @@ -191,9 +183,9 @@ func (v *Credits) addNextItem() { label2 := v.getNewFontLabel(isHeading) label2.SetText(text2) - label2.MoveTo(410, 605) + label2.SetPosition(410, 605) } else { - label.MoveTo(405-int(width/2), 605) + label.SetPosition(405-int(width/2), 605) } if isHeading && isNextHeading { @@ -227,7 +219,7 @@ func (v *Credits) getNewFontLabel(isHeading bool) *d2ui.Label { newLabelItem := &labelItem{ Available: false, IsHeading: isHeading, - Label: d2ui.CreateLabel(v.fileProvider, d2resource.FontFormal10, d2enum.Sky), + Label: d2ui.CreateLabel(d2resource.FontFormal10, d2resource.PaletteSky), } if isHeading { diff --git a/d2core/d2scene/game.go b/d2core/d2scene/game.go index 628d5899..772aa9db 100644 --- a/d2core/d2scene/game.go +++ b/d2core/d2scene/game.go @@ -1,11 +1,10 @@ package d2scene import ( + "image/color" + "github.com/OpenDiablo2/D2Shared/d2common/d2enum" - "github.com/OpenDiablo2/D2Shared/d2common/d2interface" "github.com/OpenDiablo2/D2Shared/d2common/d2resource" - "github.com/OpenDiablo2/D2Shared/d2data/d2datadict" - "github.com/OpenDiablo2/D2Shared/d2data/d2dc6" "github.com/OpenDiablo2/OpenDiablo2/d2audio" "github.com/OpenDiablo2/OpenDiablo2/d2core" "github.com/OpenDiablo2/OpenDiablo2/d2corecommon/d2coreinterface" @@ -14,17 +13,15 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2render/d2mapengine" "github.com/OpenDiablo2/OpenDiablo2/d2render/d2ui" "github.com/hajimehoshi/ebiten" - "image/color" ) type Game struct { gameState *d2core.GameState uiManager *d2ui.Manager soundManager *d2audio.Manager - fileProvider d2interface.FileProvider sceneProvider d2coreinterface.SceneProvider - pentSpinLeft d2render.Sprite - pentSpinRight d2render.Sprite + pentSpinLeft *d2render.Sprite + pentSpinRight *d2render.Sprite testLabel d2ui.Label mapEngine *d2mapengine.MapEngine hero *d2core.Hero @@ -32,7 +29,6 @@ type Game struct { } func CreateGame( - fileProvider d2interface.FileProvider, sceneProvider d2coreinterface.SceneProvider, uiManager *d2ui.Manager, soundManager *d2audio.Manager, @@ -40,7 +36,6 @@ func CreateGame( ) *Game { result := &Game{ gameState: gameState, - fileProvider: fileProvider, uiManager: uiManager, soundManager: soundManager, sceneProvider: sceneProvider, @@ -51,28 +46,25 @@ func CreateGame( func (v *Game) Load() []func() { return []func(){ func() { - dc6, _ := d2dc6.LoadDC6(v.fileProvider.LoadFile(d2resource.PentSpin), d2datadict.Palettes[d2enum.Sky]) - v.pentSpinLeft = d2render.CreateSpriteFromDC6(dc6) - v.pentSpinLeft.Animate = true - v.pentSpinLeft.AnimateBackwards = true - v.pentSpinLeft.SpecialFrameTime = 475 - v.pentSpinLeft.MoveTo(100, 300) + v.pentSpinLeft, _ = d2render.LoadSprite(d2resource.PentSpin, d2resource.PaletteSky) + v.pentSpinLeft.PlayBackward() + v.pentSpinLeft.SetPlayLengthMs(475) + v.pentSpinLeft.SetPosition(100, 300) }, func() { - dc6, _ := d2dc6.LoadDC6(v.fileProvider.LoadFile(d2resource.PentSpin), d2datadict.Palettes[d2enum.Sky]) - v.pentSpinRight = d2render.CreateSpriteFromDC6(dc6) - v.pentSpinRight.Animate = true - v.pentSpinRight.SpecialFrameTime = 475 - v.pentSpinRight.MoveTo(650, 300) + v.pentSpinRight, _ = d2render.LoadSprite(d2resource.PentSpin, d2resource.PaletteSky) + v.pentSpinRight.PlayForward() + v.pentSpinRight.SetPlayLengthMs(475) + v.pentSpinRight.SetPosition(650, 300) }, func() { - v.testLabel = d2ui.CreateLabel(v.fileProvider, d2resource.Font42, d2enum.Units) + v.testLabel = d2ui.CreateLabel(d2resource.Font42, d2resource.PaletteUnits) v.testLabel.Alignment = d2ui.LabelAlignCenter v.testLabel.SetText("Soon :tm:") - v.testLabel.MoveTo(400, 250) + v.testLabel.SetPosition(400, 250) }, func() { - v.mapEngine = d2mapengine.CreateMapEngine(v.gameState, v.soundManager, v.fileProvider) + v.mapEngine = d2mapengine.CreateMapEngine(v.gameState, v.soundManager) v.mapEngine.GenerateMap(d2enum.RegionAct1Town, 1, 0) startX, startY := v.mapEngine.GetStartPosition() @@ -82,12 +74,11 @@ func (v *Game) Load() []func() { 0, v.gameState.HeroType, v.gameState.Equipment, - v.fileProvider, ) v.mapEngine.AddEntity(v.hero) }, func() { - v.gameControls = d2player.NewGameControls(v.fileProvider, v.hero, v.mapEngine) + v.gameControls = d2player.NewGameControls(v.hero, v.mapEngine) v.gameControls.Load() }, } diff --git a/d2core/d2scene/main_menu.go b/d2core/d2scene/main_menu.go index 37bcee9f..d0106492 100644 --- a/d2core/d2scene/main_menu.go +++ b/d2core/d2scene/main_menu.go @@ -2,7 +2,6 @@ package d2scene import ( "fmt" - "github.com/OpenDiablo2/D2Shared/d2data/d2dc6" "image/color" "log" "os" @@ -13,17 +12,12 @@ import ( "github.com/OpenDiablo2/D2Shared/d2common/d2resource" - "github.com/OpenDiablo2/D2Shared/d2data/d2datadict" - - "github.com/OpenDiablo2/D2Shared/d2common/d2enum" - "github.com/OpenDiablo2/OpenDiablo2/d2render" - "github.com/OpenDiablo2/D2Shared/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2corecommon/d2coreinterface" - "github.com/OpenDiablo2/OpenDiablo2/d2audio" "github.com/OpenDiablo2/D2Shared/d2common" + "github.com/OpenDiablo2/OpenDiablo2/d2audio" "github.com/OpenDiablo2/OpenDiablo2/d2render/d2ui" "github.com/hajimehoshi/ebiten" @@ -33,14 +27,13 @@ import ( type MainMenu struct { uiManager *d2ui.Manager soundManager *d2audio.Manager - fileProvider d2interface.FileProvider sceneProvider d2coreinterface.SceneProvider - trademarkBackground d2render.Sprite - background d2render.Sprite - diabloLogoLeft d2render.Sprite - diabloLogoRight d2render.Sprite - diabloLogoLeftBack d2render.Sprite - diabloLogoRightBack d2render.Sprite + trademarkBackground *d2render.Sprite + background *d2render.Sprite + diabloLogoLeft *d2render.Sprite + diabloLogoRight *d2render.Sprite + diabloLogoLeftBack *d2render.Sprite + diabloLogoRightBack *d2render.Sprite singlePlayerButton d2ui.Button githubButton d2ui.Button exitDiabloButton d2ui.Button @@ -58,9 +51,8 @@ type MainMenu struct { } // CreateMainMenu creates an instance of MainMenu -func CreateMainMenu(fileProvider d2interface.FileProvider, sceneProvider d2coreinterface.SceneProvider, uiManager *d2ui.Manager, soundManager *d2audio.Manager) *MainMenu { +func CreateMainMenu(sceneProvider d2coreinterface.SceneProvider, uiManager *d2ui.Manager, soundManager *d2audio.Manager) *MainMenu { result := &MainMenu{ - fileProvider: fileProvider, uiManager: uiManager, soundManager: soundManager, sceneProvider: sceneProvider, @@ -75,111 +67,105 @@ func (v *MainMenu) Load() []func() { v.soundManager.PlayBGM(d2resource.BGMTitle) return []func(){ func() { - v.versionLabel = d2ui.CreateLabel(v.fileProvider, d2resource.FontFormal12, d2enum.Static) + v.versionLabel = d2ui.CreateLabel(d2resource.FontFormal12, d2resource.PaletteStatic) v.versionLabel.Alignment = d2ui.LabelAlignRight v.versionLabel.SetText("OpenDiablo2 - " + d2common.BuildInfo.Branch) v.versionLabel.Color = color.RGBA{255, 255, 255, 255} - v.versionLabel.MoveTo(795, -10) + v.versionLabel.SetPosition(795, -10) }, func() { - v.commitLabel = d2ui.CreateLabel(v.fileProvider, d2resource.FontFormal10, d2enum.Static) + v.commitLabel = d2ui.CreateLabel(d2resource.FontFormal10, d2resource.PaletteStatic) v.commitLabel.Alignment = d2ui.LabelAlignLeft v.commitLabel.SetText(d2common.BuildInfo.Commit) v.commitLabel.Color = color.RGBA{255, 255, 255, 255} - v.commitLabel.MoveTo(2, 2) + v.commitLabel.SetPosition(2, 2) }, func() { - v.copyrightLabel = d2ui.CreateLabel(v.fileProvider, d2resource.FontFormal12, d2enum.Static) + v.copyrightLabel = d2ui.CreateLabel(d2resource.FontFormal12, d2resource.PaletteStatic) v.copyrightLabel.Alignment = d2ui.LabelAlignCenter v.copyrightLabel.SetText("Diablo 2 is © Copyright 2000-2016 Blizzard Entertainment") v.copyrightLabel.Color = color.RGBA{188, 168, 140, 255} - v.copyrightLabel.MoveTo(400, 500) + v.copyrightLabel.SetPosition(400, 500) }, func() { - v.copyrightLabel2 = d2ui.CreateLabel(v.fileProvider, d2resource.FontFormal12, d2enum.Static) + v.copyrightLabel2 = d2ui.CreateLabel(d2resource.FontFormal12, d2resource.PaletteStatic) v.copyrightLabel2.Alignment = d2ui.LabelAlignCenter v.copyrightLabel2.SetText(d2common.TranslateString("#1614")) v.copyrightLabel2.Color = color.RGBA{188, 168, 140, 255} - v.copyrightLabel2.MoveTo(400, 525) + v.copyrightLabel2.SetPosition(400, 525) }, func() { - v.openDiabloLabel = d2ui.CreateLabel(v.fileProvider, d2resource.FontFormal10, d2enum.Static) + v.openDiabloLabel = d2ui.CreateLabel(d2resource.FontFormal10, d2resource.PaletteStatic) v.openDiabloLabel.Alignment = d2ui.LabelAlignCenter v.openDiabloLabel.SetText("OpenDiablo2 is neither developed by, nor endorsed by Blizzard or its parent company Activision") v.openDiabloLabel.Color = color.RGBA{255, 255, 140, 255} - v.openDiabloLabel.MoveTo(400, 580) + v.openDiabloLabel.SetPosition(400, 580) }, func() { - dc6, _ := d2dc6.LoadDC6(v.fileProvider.LoadFile(d2resource.GameSelectScreen), d2datadict.Palettes[d2enum.Sky]) - v.background = d2render.CreateSpriteFromDC6(dc6) - v.background.MoveTo(0, 0) + v.background, _ = d2render.LoadSprite(d2resource.GameSelectScreen, d2resource.PaletteSky) + v.background.SetPosition(0, 0) }, func() { - dc6, _ := d2dc6.LoadDC6(v.fileProvider.LoadFile(d2resource.TrademarkScreen), d2datadict.Palettes[d2enum.Sky]) - v.trademarkBackground = d2render.CreateSpriteFromDC6(dc6) - v.trademarkBackground.MoveTo(0, 0) + v.trademarkBackground, _ = d2render.LoadSprite(d2resource.TrademarkScreen, d2resource.PaletteSky) + v.trademarkBackground.SetPosition(0, 0) }, func() { - dc6, _ := d2dc6.LoadDC6(v.fileProvider.LoadFile(d2resource.Diablo2LogoFireLeft), d2datadict.Palettes[d2enum.Units]) - v.diabloLogoLeft = d2render.CreateSpriteFromDC6(dc6) - v.diabloLogoLeft.Blend = true - v.diabloLogoLeft.Animate = true - v.diabloLogoLeft.MoveTo(400, 120) + v.diabloLogoLeft, _ = d2render.LoadSprite(d2resource.Diablo2LogoFireLeft, d2resource.PaletteUnits) + v.diabloLogoLeft.SetBlend(true) + v.diabloLogoLeft.PlayForward() + v.diabloLogoLeft.SetPosition(400, 120) }, func() { - dc6, _ := d2dc6.LoadDC6(v.fileProvider.LoadFile(d2resource.Diablo2LogoFireRight), d2datadict.Palettes[d2enum.Units]) - v.diabloLogoRight = d2render.CreateSpriteFromDC6(dc6) - v.diabloLogoRight.Blend = true - v.diabloLogoRight.Animate = true - v.diabloLogoRight.MoveTo(400, 120) + v.diabloLogoRight, _ = d2render.LoadSprite(d2resource.Diablo2LogoFireRight, d2resource.PaletteUnits) + v.diabloLogoRight.SetBlend(true) + v.diabloLogoRight.PlayForward() + v.diabloLogoRight.SetPosition(400, 120) }, func() { - dc6, _ := d2dc6.LoadDC6(v.fileProvider.LoadFile(d2resource.Diablo2LogoBlackLeft), d2datadict.Palettes[d2enum.Units]) - v.diabloLogoLeftBack = d2render.CreateSpriteFromDC6(dc6) - v.diabloLogoLeftBack.MoveTo(400, 120) + v.diabloLogoLeftBack, _ = d2render.LoadSprite(d2resource.Diablo2LogoBlackLeft, d2resource.PaletteUnits) + v.diabloLogoLeftBack.SetPosition(400, 120) }, func() { - dc6, _ := d2dc6.LoadDC6(v.fileProvider.LoadFile(d2resource.Diablo2LogoBlackRight), d2datadict.Palettes[d2enum.Units]) - v.diabloLogoRightBack = d2render.CreateSpriteFromDC6(dc6) - v.diabloLogoRightBack.MoveTo(400, 120) + v.diabloLogoRightBack, _ = d2render.LoadSprite(d2resource.Diablo2LogoBlackRight, d2resource.PaletteUnits) + v.diabloLogoRightBack.SetPosition(400, 120) }, func() { - v.exitDiabloButton = d2ui.CreateButton(d2ui.ButtonTypeWide, v.fileProvider, d2common.TranslateString("#1625")) - v.exitDiabloButton.MoveTo(264, 535) + v.exitDiabloButton = d2ui.CreateButton(d2ui.ButtonTypeWide, d2common.TranslateString("#1625")) + v.exitDiabloButton.SetPosition(264, 535) v.exitDiabloButton.SetVisible(!v.ShowTrademarkScreen) v.exitDiabloButton.OnActivated(func() { v.onExitButtonClicked() }) v.uiManager.AddWidget(&v.exitDiabloButton) }, func() { - v.creditsButton = d2ui.CreateButton(d2ui.ButtonTypeShort, v.fileProvider, d2common.TranslateString("#1627")) - v.creditsButton.MoveTo(264, 505) + v.creditsButton = d2ui.CreateButton(d2ui.ButtonTypeShort, d2common.TranslateString("#1627")) + v.creditsButton.SetPosition(264, 505) v.creditsButton.SetVisible(!v.ShowTrademarkScreen) v.creditsButton.OnActivated(func() { v.onCreditsButtonClicked() }) v.uiManager.AddWidget(&v.creditsButton) }, func() { - v.cinematicsButton = d2ui.CreateButton(d2ui.ButtonTypeShort, v.fileProvider, d2common.TranslateString("#1639")) - v.cinematicsButton.MoveTo(401, 505) + v.cinematicsButton = d2ui.CreateButton(d2ui.ButtonTypeShort, d2common.TranslateString("#1639")) + v.cinematicsButton.SetPosition(401, 505) v.cinematicsButton.SetVisible(!v.ShowTrademarkScreen) v.uiManager.AddWidget(&v.cinematicsButton) }, func() { - v.singlePlayerButton = d2ui.CreateButton(d2ui.ButtonTypeWide, v.fileProvider, d2common.TranslateString("#1620")) - v.singlePlayerButton.MoveTo(264, 290) + v.singlePlayerButton = d2ui.CreateButton(d2ui.ButtonTypeWide, d2common.TranslateString("#1620")) + v.singlePlayerButton.SetPosition(264, 290) v.singlePlayerButton.SetVisible(!v.ShowTrademarkScreen) v.singlePlayerButton.OnActivated(func() { v.onSinglePlayerClicked() }) v.uiManager.AddWidget(&v.singlePlayerButton) }, func() { - v.githubButton = d2ui.CreateButton(d2ui.ButtonTypeWide, v.fileProvider, "PROJECT WEBSITE") - v.githubButton.MoveTo(264, 330) + v.githubButton = d2ui.CreateButton(d2ui.ButtonTypeWide, "PROJECT WEBSITE") + v.githubButton.SetPosition(264, 330) v.githubButton.SetVisible(!v.ShowTrademarkScreen) v.githubButton.OnActivated(func() { v.onGithubButtonClicked() }) v.uiManager.AddWidget(&v.githubButton) }, func() { - v.mapTestButton = d2ui.CreateButton(d2ui.ButtonTypeWide, v.fileProvider, "MAP ENGINE TEST") - v.mapTestButton.MoveTo(264, 450) + v.mapTestButton = d2ui.CreateButton(d2ui.ButtonTypeWide, "MAP ENGINE TEST") + v.mapTestButton.SetPosition(264, 450) v.mapTestButton.SetVisible(!v.ShowTrademarkScreen) v.mapTestButton.OnActivated(func() { v.onMapTestClicked() }) v.uiManager.AddWidget(&v.mapTestButton) @@ -188,7 +174,7 @@ func (v *MainMenu) Load() []func() { } func (v *MainMenu) onMapTestClicked() { - v.sceneProvider.SetNextScene(CreateMapEngineTest(v.fileProvider, v.sceneProvider, v.uiManager, v.soundManager, 0, 1)) + v.sceneProvider.SetNextScene(CreateMapEngineTest(v.sceneProvider, v.uiManager, v.soundManager, 0, 1)) } func openbrowser(url string) { @@ -213,10 +199,10 @@ func openbrowser(url string) { func (v *MainMenu) onSinglePlayerClicked() { // Go here only if existing characters are available to select if d2core.HasGameStates() { - v.sceneProvider.SetNextScene(CreateCharacterSelect(v.fileProvider, v.sceneProvider, v.uiManager, v.soundManager)) + v.sceneProvider.SetNextScene(CreateCharacterSelect(v.sceneProvider, v.uiManager, v.soundManager)) return } - v.sceneProvider.SetNextScene(CreateSelectHeroClass(v.fileProvider, v.sceneProvider, v.uiManager, v.soundManager)) + v.sceneProvider.SetNextScene(CreateSelectHeroClass(v.sceneProvider, v.uiManager, v.soundManager)) } func (v *MainMenu) onGithubButtonClicked() { @@ -228,7 +214,7 @@ func (v *MainMenu) onExitButtonClicked() { } func (v *MainMenu) onCreditsButtonClicked() { - v.sceneProvider.SetNextScene(CreateCredits(v.fileProvider, v.sceneProvider, v.uiManager, v.soundManager)) + v.sceneProvider.SetNextScene(CreateCredits(v.sceneProvider, v.uiManager, v.soundManager)) } // Unload unloads the data for the main menu @@ -239,22 +225,22 @@ func (v *MainMenu) Unload() { // Render renders the main menu func (v *MainMenu) Render(screen *ebiten.Image) { if v.ShowTrademarkScreen { - v.trademarkBackground.DrawSegments(screen, 4, 3, 0) + v.trademarkBackground.RenderSegmented(screen, 4, 3, 0) } else { - v.background.DrawSegments(screen, 4, 3, 0) + v.background.RenderSegmented(screen, 4, 3, 0) } - v.diabloLogoLeftBack.Draw(screen) - v.diabloLogoRightBack.Draw(screen) - v.diabloLogoLeft.Draw(screen) - v.diabloLogoRight.Draw(screen) + v.diabloLogoLeftBack.Render(screen) + v.diabloLogoRightBack.Render(screen) + v.diabloLogoLeft.Render(screen) + v.diabloLogoRight.Render(screen) if v.ShowTrademarkScreen { - v.copyrightLabel.Draw(screen) - v.copyrightLabel2.Draw(screen) + v.copyrightLabel.Render(screen) + v.copyrightLabel2.Render(screen) } else { - v.openDiabloLabel.Draw(screen) - v.versionLabel.Draw(screen) - v.commitLabel.Draw(screen) + v.openDiabloLabel.Render(screen) + v.versionLabel.Render(screen) + v.commitLabel.Render(screen) } } diff --git a/d2core/d2scene/map_engine_testing.go b/d2core/d2scene/map_engine_testing.go index 69ef52be..02086d1d 100644 --- a/d2core/d2scene/map_engine_testing.go +++ b/d2core/d2scene/map_engine_testing.go @@ -7,7 +7,6 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2core" - "github.com/OpenDiablo2/D2Shared/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2corecommon/d2coreinterface" "github.com/OpenDiablo2/D2Shared/d2common/d2enum" @@ -83,7 +82,6 @@ var regions []RegionSpec = []RegionSpec{ type MapEngineTest struct { uiManager *d2ui.Manager soundManager *d2audio.Manager - fileProvider d2interface.FileProvider sceneProvider d2coreinterface.SceneProvider gameState *d2core.GameState mapEngine *d2mapengine.MapEngine @@ -97,14 +95,8 @@ type MapEngineTest struct { debugVisLevel int } -func CreateMapEngineTest( - fileProvider d2interface.FileProvider, - sceneProvider d2coreinterface.SceneProvider, - uiManager *d2ui.Manager, - soundManager *d2audio.Manager, - currentRegion int, levelPreset int) *MapEngineTest { +func CreateMapEngineTest(sceneProvider d2coreinterface.SceneProvider, uiManager *d2ui.Manager, soundManager *d2audio.Manager, currentRegion int, levelPreset int) *MapEngineTest { result := &MapEngineTest{ - fileProvider: fileProvider, uiManager: uiManager, soundManager: soundManager, sceneProvider: sceneProvider, @@ -145,7 +137,7 @@ func (v *MapEngineTest) LoadRegionByIndex(n int, levelPreset, fileIndex int) { if n == 0 { v.mapEngine.GenerateAct1Overworld() } else { - v.mapEngine = d2mapengine.CreateMapEngine(v.gameState, v.soundManager, v.fileProvider) // necessary for map name update + v.mapEngine = d2mapengine.CreateMapEngine(v.gameState, v.soundManager) // necessary for map name update v.mapEngine.GenerateMap(d2enum.RegionIdType(n), levelPreset, fileIndex) } @@ -158,7 +150,7 @@ func (v *MapEngineTest) Load() []func() { v.soundManager.PlayBGM("") return []func(){ func() { - v.mapEngine = d2mapengine.CreateMapEngine(v.gameState, v.soundManager, v.fileProvider) + v.mapEngine = d2mapengine.CreateMapEngine(v.gameState, v.soundManager) v.LoadRegionByIndex(v.currentRegion, v.levelPreset, v.fileIndex) }, } diff --git a/d2core/d2scene/select_hero_class.go b/d2core/d2scene/select_hero_class.go index 2c8dc0de..86d771d3 100644 --- a/d2core/d2scene/select_hero_class.go +++ b/d2core/d2scene/select_hero_class.go @@ -1,7 +1,6 @@ package d2scene import ( - "github.com/OpenDiablo2/D2Shared/d2data/d2dc6" "image" "image/color" @@ -9,31 +8,28 @@ import ( "github.com/OpenDiablo2/D2Shared/d2common/d2resource" - "github.com/OpenDiablo2/D2Shared/d2data/d2datadict" - "github.com/OpenDiablo2/OpenDiablo2/d2render" "github.com/OpenDiablo2/D2Shared/d2common/d2enum" - "github.com/OpenDiablo2/D2Shared/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2corecommon/d2coreinterface" - "github.com/OpenDiablo2/OpenDiablo2/d2audio" "github.com/OpenDiablo2/D2Shared/d2common" dh "github.com/OpenDiablo2/D2Shared/d2helper" + "github.com/OpenDiablo2/OpenDiablo2/d2audio" "github.com/OpenDiablo2/OpenDiablo2/d2render/d2ui" "github.com/hajimehoshi/ebiten" ) type HeroRenderInfo struct { Stance d2enum.HeroStance - IdleSprite d2render.Sprite - IdleSelectedSprite d2render.Sprite - ForwardWalkSprite d2render.Sprite - ForwardWalkSpriteOverlay d2render.Sprite - SelectedSprite d2render.Sprite - SelectedSpriteOverlay d2render.Sprite - BackWalkSprite d2render.Sprite - BackWalkSpriteOverlay d2render.Sprite + IdleSprite *d2render.Sprite + IdleSelectedSprite *d2render.Sprite + ForwardWalkSprite *d2render.Sprite + ForwardWalkSpriteOverlay *d2render.Sprite + SelectedSprite *d2render.Sprite + SelectedSpriteOverlay *d2render.Sprite + BackWalkSprite *d2render.Sprite + BackWalkSpriteOverlay *d2render.Sprite SelectionBounds image.Rectangle SelectSfx *d2audio.SoundEffect DeselectSfx *d2audio.SoundEffect @@ -42,10 +38,9 @@ type HeroRenderInfo struct { type SelectHeroClass struct { uiManager *d2ui.Manager soundManager *d2audio.Manager - fileProvider d2interface.FileProvider sceneProvider d2coreinterface.SceneProvider - bgImage d2render.Sprite - campfire d2render.Sprite + bgImage *d2render.Sprite + campfire *d2render.Sprite headingLabel d2ui.Label heroClassLabel d2ui.Label heroDesc1Label d2ui.Label @@ -63,15 +58,10 @@ type SelectHeroClass struct { hardcoreCharLabel d2ui.Label } -func CreateSelectHeroClass( - fileProvider d2interface.FileProvider, - sceneProvider d2coreinterface.SceneProvider, - uiManager *d2ui.Manager, soundManager *d2audio.Manager, -) *SelectHeroClass { +func CreateSelectHeroClass(sceneProvider d2coreinterface.SceneProvider, uiManager *d2ui.Manager, soundManager *d2audio.Manager) *SelectHeroClass { result := &SelectHeroClass{ uiManager: uiManager, sceneProvider: sceneProvider, - fileProvider: fileProvider, soundManager: soundManager, heroRenderInfo: make(map[d2enum.Hero]*HeroRenderInfo), selectedHero: d2enum.HeroNone, @@ -79,365 +69,360 @@ func CreateSelectHeroClass( return result } -func (v *SelectHeroClass) loadSprite(path string, palette d2enum.PaletteType) d2render.Sprite { - dc6, _ := d2dc6.LoadDC6(v.fileProvider.LoadFile(path), d2datadict.Palettes[palette]) - return d2render.CreateSpriteFromDC6(dc6) -} - func (v *SelectHeroClass) Load() []func() { v.soundManager.PlayBGM(d2resource.BGMTitle) return []func(){ func() { - v.bgImage = v.loadSprite(d2resource.CharacterSelectBackground, d2enum.Fechar) - v.bgImage.MoveTo(0, 0) + v.bgImage, _ = d2render.LoadSprite(d2resource.CharacterSelectBackground, d2resource.PaletteFechar) + v.bgImage.SetPosition(0, 0) }, func() { - v.headingLabel = d2ui.CreateLabel(v.fileProvider, d2resource.Font30, d2enum.Units) + v.headingLabel = d2ui.CreateLabel(d2resource.Font30, d2resource.PaletteUnits) fontWidth, _ := v.headingLabel.GetSize() - v.headingLabel.MoveTo(400-int(fontWidth/2), 17) + v.headingLabel.SetPosition(400-int(fontWidth/2), 17) v.headingLabel.SetText("Select Hero Class") v.headingLabel.Alignment = d2ui.LabelAlignCenter }, func() { - v.heroClassLabel = d2ui.CreateLabel(v.fileProvider, d2resource.Font30, d2enum.Units) + v.heroClassLabel = d2ui.CreateLabel(d2resource.Font30, d2resource.PaletteUnits) v.heroClassLabel.Alignment = d2ui.LabelAlignCenter - v.heroClassLabel.MoveTo(400, 65) + v.heroClassLabel.SetPosition(400, 65) }, func() { - v.heroDesc1Label = d2ui.CreateLabel(v.fileProvider, d2resource.Font16, d2enum.Units) + v.heroDesc1Label = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits) v.heroDesc1Label.Alignment = d2ui.LabelAlignCenter - v.heroDesc1Label.MoveTo(400, 100) + v.heroDesc1Label.SetPosition(400, 100) }, func() { - v.heroDesc2Label = d2ui.CreateLabel(v.fileProvider, d2resource.Font16, d2enum.Units) + v.heroDesc2Label = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits) v.heroDesc2Label.Alignment = d2ui.LabelAlignCenter - v.heroDesc2Label.MoveTo(400, 115) + v.heroDesc2Label.SetPosition(400, 115) }, func() { - v.heroDesc3Label = d2ui.CreateLabel(v.fileProvider, d2resource.Font16, d2enum.Units) + v.heroDesc3Label = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits) v.heroDesc3Label.Alignment = d2ui.LabelAlignCenter - v.heroDesc3Label.MoveTo(400, 130) + v.heroDesc3Label.SetPosition(400, 130) }, func() { - v.campfire = v.loadSprite(d2resource.CharacterSelectCampfire, d2enum.Fechar) - v.campfire.MoveTo(380, 335) - v.campfire.Animate = true - v.campfire.Blend = true + v.campfire, _ = d2render.LoadSprite(d2resource.CharacterSelectCampfire, d2resource.PaletteFechar) + v.campfire.SetPosition(380, 335) + v.campfire.PlayForward() + v.campfire.SetBlend(true) }, func() { - v.exitButton = d2ui.CreateButton(d2ui.ButtonTypeMedium, v.fileProvider, d2common.TranslateString("#970")) - v.exitButton.MoveTo(33, 537) + v.exitButton = d2ui.CreateButton(d2ui.ButtonTypeMedium, d2common.TranslateString("#970")) + v.exitButton.SetPosition(33, 537) v.exitButton.OnActivated(func() { v.onExitButtonClicked() }) v.uiManager.AddWidget(&v.exitButton) }, func() { - v.okButton = d2ui.CreateButton(d2ui.ButtonTypeMedium, v.fileProvider, d2common.TranslateString("#971")) - v.okButton.MoveTo(630, 537) + v.okButton = d2ui.CreateButton(d2ui.ButtonTypeMedium, d2common.TranslateString("#971")) + v.okButton.SetPosition(630, 537) v.okButton.OnActivated(func() { v.onOkButtonClicked() }) v.okButton.SetVisible(false) v.okButton.SetEnabled(false) v.uiManager.AddWidget(&v.okButton) }, func() { - v.heroNameLabel = d2ui.CreateLabel(v.fileProvider, d2resource.Font16, d2enum.Units) + v.heroNameLabel = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits) v.heroNameLabel.Alignment = d2ui.LabelAlignLeft v.heroNameLabel.Color = color.RGBA{216, 196, 128, 255} v.heroNameLabel.SetText(d2common.TranslateString("#1694")) - v.heroNameLabel.MoveTo(321, 475) + v.heroNameLabel.SetPosition(321, 475) }, func() { - v.heroNameTextbox = d2ui.CreateTextbox(v.fileProvider) - v.heroNameTextbox.MoveTo(318, 493) + v.heroNameTextbox = d2ui.CreateTextbox() + v.heroNameTextbox.SetPosition(318, 493) v.heroNameTextbox.SetVisible(false) v.uiManager.AddWidget(&v.heroNameTextbox) }, func() { - v.expansionCheckbox = d2ui.CreateCheckbox(v.fileProvider, true) - v.expansionCheckbox.MoveTo(318, 526) + v.expansionCheckbox = d2ui.CreateCheckbox(true) + v.expansionCheckbox.SetPosition(318, 526) v.expansionCheckbox.SetVisible(false) v.uiManager.AddWidget(&v.expansionCheckbox) }, func() { - v.expansionCharLabel = d2ui.CreateLabel(v.fileProvider, d2resource.Font16, d2enum.Units) + v.expansionCharLabel = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits) v.expansionCharLabel.Alignment = d2ui.LabelAlignLeft v.expansionCharLabel.Color = color.RGBA{216, 196, 128, 255} v.expansionCharLabel.SetText(d2common.TranslateString("#803")) - v.expansionCharLabel.MoveTo(339, 526) + v.expansionCharLabel.SetPosition(339, 526) }, func() { - v.hardcoreCheckbox = d2ui.CreateCheckbox(v.fileProvider, false) - v.hardcoreCheckbox.MoveTo(318, 548) + v.hardcoreCheckbox = d2ui.CreateCheckbox(false) + v.hardcoreCheckbox.SetPosition(318, 548) v.hardcoreCheckbox.SetVisible(false) v.uiManager.AddWidget(&v.hardcoreCheckbox) }, func() { - v.hardcoreCharLabel = d2ui.CreateLabel(v.fileProvider, d2resource.Font16, d2enum.Units) + v.hardcoreCharLabel = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits) v.hardcoreCharLabel.Alignment = d2ui.LabelAlignLeft v.hardcoreCharLabel.Color = color.RGBA{216, 196, 128, 255} v.hardcoreCharLabel.SetText(d2common.TranslateString("#1696")) - v.hardcoreCharLabel.MoveTo(339, 548) + v.hardcoreCharLabel.SetPosition(339, 548) }, func() { v.heroRenderInfo[d2enum.HeroBarbarian] = &HeroRenderInfo{ d2enum.HeroStanceIdle, - v.loadSprite(d2resource.CharacterSelectBarbarianUnselected, d2enum.Fechar), - v.loadSprite(d2resource.CharacterSelectBarbarianUnselectedH, d2enum.Fechar), - v.loadSprite(d2resource.CharacterSelectBarbarianForwardWalk, d2enum.Fechar), - v.loadSprite(d2resource.CharacterSelectBarbarianForwardWalkOverlay, d2enum.Fechar), - v.loadSprite(d2resource.CharacterSelectBarbarianSelected, d2enum.Fechar), - d2render.Sprite{}, - v.loadSprite(d2resource.CharacterSelectBarbarianBackWalk, d2enum.Fechar), - d2render.Sprite{}, + d2render.MustLoadSprite(d2resource.CharacterSelectBarbarianUnselected, d2resource.PaletteFechar), + d2render.MustLoadSprite(d2resource.CharacterSelectBarbarianUnselectedH, d2resource.PaletteFechar), + d2render.MustLoadSprite(d2resource.CharacterSelectBarbarianForwardWalk, d2resource.PaletteFechar), + d2render.MustLoadSprite(d2resource.CharacterSelectBarbarianForwardWalkOverlay, d2resource.PaletteFechar), + d2render.MustLoadSprite(d2resource.CharacterSelectBarbarianSelected, d2resource.PaletteFechar), + nil, + d2render.MustLoadSprite(d2resource.CharacterSelectBarbarianBackWalk, d2resource.PaletteFechar), + nil, image.Rectangle{Min: image.Point{364, 201}, Max: image.Point{90, 170}}, v.soundManager.LoadSoundEffect(d2resource.SFXBarbarianSelect), v.soundManager.LoadSoundEffect(d2resource.SFXBarbarianDeselect), } - v.heroRenderInfo[d2enum.HeroBarbarian].IdleSprite.MoveTo(400, 330) - v.heroRenderInfo[d2enum.HeroBarbarian].IdleSprite.Animate = true - v.heroRenderInfo[d2enum.HeroBarbarian].IdleSelectedSprite.MoveTo(400, 330) - v.heroRenderInfo[d2enum.HeroBarbarian].IdleSelectedSprite.Animate = true - v.heroRenderInfo[d2enum.HeroBarbarian].ForwardWalkSprite.MoveTo(400, 330) - v.heroRenderInfo[d2enum.HeroBarbarian].ForwardWalkSprite.Animate = true - v.heroRenderInfo[d2enum.HeroBarbarian].ForwardWalkSprite.SpecialFrameTime = 2500 - v.heroRenderInfo[d2enum.HeroBarbarian].ForwardWalkSprite.StopOnLastFrame = true - v.heroRenderInfo[d2enum.HeroBarbarian].ForwardWalkSpriteOverlay.MoveTo(400, 330) - v.heroRenderInfo[d2enum.HeroBarbarian].ForwardWalkSpriteOverlay.Animate = true - v.heroRenderInfo[d2enum.HeroBarbarian].ForwardWalkSpriteOverlay.SpecialFrameTime = 2500 - v.heroRenderInfo[d2enum.HeroBarbarian].ForwardWalkSpriteOverlay.StopOnLastFrame = true - v.heroRenderInfo[d2enum.HeroBarbarian].SelectedSprite.MoveTo(400, 330) - v.heroRenderInfo[d2enum.HeroBarbarian].SelectedSprite.Animate = true - v.heroRenderInfo[d2enum.HeroBarbarian].BackWalkSprite.MoveTo(400, 330) - v.heroRenderInfo[d2enum.HeroBarbarian].BackWalkSprite.Animate = true - v.heroRenderInfo[d2enum.HeroBarbarian].BackWalkSprite.SpecialFrameTime = 1000 - v.heroRenderInfo[d2enum.HeroBarbarian].BackWalkSprite.StopOnLastFrame = true + v.heroRenderInfo[d2enum.HeroBarbarian].IdleSprite.SetPosition(400, 330) + v.heroRenderInfo[d2enum.HeroBarbarian].IdleSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroBarbarian].IdleSelectedSprite.SetPosition(400, 330) + v.heroRenderInfo[d2enum.HeroBarbarian].IdleSelectedSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroBarbarian].ForwardWalkSprite.SetPosition(400, 330) + v.heroRenderInfo[d2enum.HeroBarbarian].ForwardWalkSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroBarbarian].ForwardWalkSprite.SetPlayLengthMs(2500) + v.heroRenderInfo[d2enum.HeroBarbarian].ForwardWalkSprite.SetPlayLoop(false) + v.heroRenderInfo[d2enum.HeroBarbarian].ForwardWalkSpriteOverlay.SetPosition(400, 330) + v.heroRenderInfo[d2enum.HeroBarbarian].ForwardWalkSpriteOverlay.PlayForward() + v.heroRenderInfo[d2enum.HeroBarbarian].ForwardWalkSpriteOverlay.SetPlayLengthMs(2500) + v.heroRenderInfo[d2enum.HeroBarbarian].ForwardWalkSpriteOverlay.SetPlayLoop(false) + v.heroRenderInfo[d2enum.HeroBarbarian].SelectedSprite.SetPosition(400, 330) + v.heroRenderInfo[d2enum.HeroBarbarian].SelectedSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroBarbarian].BackWalkSprite.SetPosition(400, 330) + v.heroRenderInfo[d2enum.HeroBarbarian].BackWalkSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroBarbarian].BackWalkSprite.SetPlayLengthMs(1000) + v.heroRenderInfo[d2enum.HeroBarbarian].BackWalkSprite.SetPlayLoop(false) }, func() { v.heroRenderInfo[d2enum.HeroSorceress] = &HeroRenderInfo{ d2enum.HeroStanceIdle, - v.loadSprite(d2resource.CharacterSelecSorceressUnselected, d2enum.Fechar), - v.loadSprite(d2resource.CharacterSelecSorceressUnselectedH, d2enum.Fechar), - v.loadSprite(d2resource.CharacterSelecSorceressForwardWalk, d2enum.Fechar), - v.loadSprite(d2resource.CharacterSelecSorceressForwardWalkOverlay, d2enum.Fechar), - v.loadSprite(d2resource.CharacterSelecSorceressSelected, d2enum.Fechar), - v.loadSprite(d2resource.CharacterSelecSorceressSelectedOverlay, d2enum.Fechar), - v.loadSprite(d2resource.CharacterSelecSorceressBackWalk, d2enum.Fechar), - v.loadSprite(d2resource.CharacterSelecSorceressBackWalkOverlay, d2enum.Fechar), + d2render.MustLoadSprite(d2resource.CharacterSelecSorceressUnselected, d2resource.PaletteFechar), + d2render.MustLoadSprite(d2resource.CharacterSelecSorceressUnselectedH, d2resource.PaletteFechar), + d2render.MustLoadSprite(d2resource.CharacterSelecSorceressForwardWalk, d2resource.PaletteFechar), + d2render.MustLoadSprite(d2resource.CharacterSelecSorceressForwardWalkOverlay, d2resource.PaletteFechar), + d2render.MustLoadSprite(d2resource.CharacterSelecSorceressSelected, d2resource.PaletteFechar), + d2render.MustLoadSprite(d2resource.CharacterSelecSorceressSelectedOverlay, d2resource.PaletteFechar), + d2render.MustLoadSprite(d2resource.CharacterSelecSorceressBackWalk, d2resource.PaletteFechar), + d2render.MustLoadSprite(d2resource.CharacterSelecSorceressBackWalkOverlay, d2resource.PaletteFechar), image.Rectangle{Min: image.Point{580, 240}, Max: image.Point{65, 160}}, v.soundManager.LoadSoundEffect(d2resource.SFXSorceressSelect), v.soundManager.LoadSoundEffect(d2resource.SFXSorceressDeselect), } - v.heroRenderInfo[d2enum.HeroSorceress].IdleSprite.MoveTo(626, 352) - v.heroRenderInfo[d2enum.HeroSorceress].IdleSprite.Animate = true - v.heroRenderInfo[d2enum.HeroSorceress].IdleSprite.SpecialFrameTime = 2500 - v.heroRenderInfo[d2enum.HeroSorceress].IdleSelectedSprite.MoveTo(626, 352) - v.heroRenderInfo[d2enum.HeroSorceress].IdleSelectedSprite.Animate = true - v.heroRenderInfo[d2enum.HeroSorceress].IdleSelectedSprite.SpecialFrameTime = 2500 - v.heroRenderInfo[d2enum.HeroSorceress].ForwardWalkSprite.MoveTo(626, 352) - v.heroRenderInfo[d2enum.HeroSorceress].ForwardWalkSprite.Animate = true - v.heroRenderInfo[d2enum.HeroSorceress].ForwardWalkSprite.SpecialFrameTime = 2300 - v.heroRenderInfo[d2enum.HeroSorceress].ForwardWalkSprite.StopOnLastFrame = true - v.heroRenderInfo[d2enum.HeroSorceress].ForwardWalkSpriteOverlay.Blend = true - v.heroRenderInfo[d2enum.HeroSorceress].ForwardWalkSpriteOverlay.MoveTo(626, 352) - v.heroRenderInfo[d2enum.HeroSorceress].ForwardWalkSpriteOverlay.Animate = true - v.heroRenderInfo[d2enum.HeroSorceress].ForwardWalkSpriteOverlay.SpecialFrameTime = 2300 - v.heroRenderInfo[d2enum.HeroSorceress].ForwardWalkSpriteOverlay.StopOnLastFrame = true - v.heroRenderInfo[d2enum.HeroSorceress].SelectedSprite.MoveTo(626, 352) - v.heroRenderInfo[d2enum.HeroSorceress].SelectedSprite.Animate = true - v.heroRenderInfo[d2enum.HeroSorceress].SelectedSprite.SpecialFrameTime = 450 - v.heroRenderInfo[d2enum.HeroSorceress].SelectedSpriteOverlay.Blend = true - v.heroRenderInfo[d2enum.HeroSorceress].SelectedSpriteOverlay.MoveTo(626, 352) - v.heroRenderInfo[d2enum.HeroSorceress].SelectedSpriteOverlay.Animate = true - v.heroRenderInfo[d2enum.HeroSorceress].SelectedSpriteOverlay.SpecialFrameTime = 450 - v.heroRenderInfo[d2enum.HeroSorceress].BackWalkSprite.MoveTo(626, 352) - v.heroRenderInfo[d2enum.HeroSorceress].BackWalkSprite.Animate = true - v.heroRenderInfo[d2enum.HeroSorceress].BackWalkSprite.SpecialFrameTime = 1200 - v.heroRenderInfo[d2enum.HeroSorceress].BackWalkSprite.StopOnLastFrame = true - v.heroRenderInfo[d2enum.HeroSorceress].BackWalkSpriteOverlay.Blend = true - v.heroRenderInfo[d2enum.HeroSorceress].BackWalkSpriteOverlay.MoveTo(626, 352) - v.heroRenderInfo[d2enum.HeroSorceress].BackWalkSpriteOverlay.Animate = true - v.heroRenderInfo[d2enum.HeroSorceress].BackWalkSpriteOverlay.SpecialFrameTime = 1200 - v.heroRenderInfo[d2enum.HeroSorceress].BackWalkSpriteOverlay.StopOnLastFrame = true + v.heroRenderInfo[d2enum.HeroSorceress].IdleSprite.SetPosition(626, 352) + v.heroRenderInfo[d2enum.HeroSorceress].IdleSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroSorceress].IdleSprite.SetPlayLengthMs(2500) + v.heroRenderInfo[d2enum.HeroSorceress].IdleSelectedSprite.SetPosition(626, 352) + v.heroRenderInfo[d2enum.HeroSorceress].IdleSelectedSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroSorceress].IdleSelectedSprite.SetPlayLengthMs(2500) + v.heroRenderInfo[d2enum.HeroSorceress].ForwardWalkSprite.SetPosition(626, 352) + v.heroRenderInfo[d2enum.HeroSorceress].ForwardWalkSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroSorceress].ForwardWalkSprite.SetPlayLengthMs(2300) + v.heroRenderInfo[d2enum.HeroSorceress].ForwardWalkSprite.SetPlayLoop(false) + v.heroRenderInfo[d2enum.HeroSorceress].ForwardWalkSpriteOverlay.SetBlend(true) + v.heroRenderInfo[d2enum.HeroSorceress].ForwardWalkSpriteOverlay.SetPosition(626, 352) + v.heroRenderInfo[d2enum.HeroSorceress].ForwardWalkSpriteOverlay.PlayForward() + v.heroRenderInfo[d2enum.HeroSorceress].ForwardWalkSpriteOverlay.SetPlayLengthMs(2300) + v.heroRenderInfo[d2enum.HeroSorceress].ForwardWalkSpriteOverlay.SetPlayLoop(false) + v.heroRenderInfo[d2enum.HeroSorceress].SelectedSprite.SetPosition(626, 352) + v.heroRenderInfo[d2enum.HeroSorceress].SelectedSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroSorceress].SelectedSprite.SetPlayLengthMs(450) + v.heroRenderInfo[d2enum.HeroSorceress].SelectedSpriteOverlay.SetBlend(true) + v.heroRenderInfo[d2enum.HeroSorceress].SelectedSpriteOverlay.SetPosition(626, 352) + v.heroRenderInfo[d2enum.HeroSorceress].SelectedSpriteOverlay.PlayForward() + v.heroRenderInfo[d2enum.HeroSorceress].SelectedSpriteOverlay.SetPlayLengthMs(450) + v.heroRenderInfo[d2enum.HeroSorceress].BackWalkSprite.SetPosition(626, 352) + v.heroRenderInfo[d2enum.HeroSorceress].BackWalkSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroSorceress].BackWalkSprite.SetPlayLengthMs(1200) + v.heroRenderInfo[d2enum.HeroSorceress].BackWalkSprite.SetPlayLoop(false) + v.heroRenderInfo[d2enum.HeroSorceress].BackWalkSpriteOverlay.SetBlend(true) + v.heroRenderInfo[d2enum.HeroSorceress].BackWalkSpriteOverlay.SetPosition(626, 352) + v.heroRenderInfo[d2enum.HeroSorceress].BackWalkSpriteOverlay.PlayForward() + v.heroRenderInfo[d2enum.HeroSorceress].BackWalkSpriteOverlay.SetPlayLengthMs(1200) + v.heroRenderInfo[d2enum.HeroSorceress].BackWalkSpriteOverlay.SetPlayLoop(false) }, func() { v.heroRenderInfo[d2enum.HeroNecromancer] = &HeroRenderInfo{ d2enum.HeroStanceIdle, - v.loadSprite(d2resource.CharacterSelectNecromancerUnselected, d2enum.Fechar), - v.loadSprite(d2resource.CharacterSelectNecromancerUnselectedH, d2enum.Fechar), - v.loadSprite(d2resource.CharacterSelecNecromancerForwardWalk, d2enum.Fechar), - v.loadSprite(d2resource.CharacterSelecNecromancerForwardWalkOverlay, d2enum.Fechar), - v.loadSprite(d2resource.CharacterSelecNecromancerSelected, d2enum.Fechar), - v.loadSprite(d2resource.CharacterSelecNecromancerSelectedOverlay, d2enum.Fechar), - v.loadSprite(d2resource.CharacterSelecNecromancerBackWalk, d2enum.Fechar), - v.loadSprite(d2resource.CharacterSelecNecromancerBackWalkOverlay, d2enum.Fechar), + d2render.MustLoadSprite(d2resource.CharacterSelectNecromancerUnselected, d2resource.PaletteFechar), + d2render.MustLoadSprite(d2resource.CharacterSelectNecromancerUnselectedH, d2resource.PaletteFechar), + d2render.MustLoadSprite(d2resource.CharacterSelecNecromancerForwardWalk, d2resource.PaletteFechar), + d2render.MustLoadSprite(d2resource.CharacterSelecNecromancerForwardWalkOverlay, d2resource.PaletteFechar), + d2render.MustLoadSprite(d2resource.CharacterSelecNecromancerSelected, d2resource.PaletteFechar), + d2render.MustLoadSprite(d2resource.CharacterSelecNecromancerSelectedOverlay, d2resource.PaletteFechar), + d2render.MustLoadSprite(d2resource.CharacterSelecNecromancerBackWalk, d2resource.PaletteFechar), + d2render.MustLoadSprite(d2resource.CharacterSelecNecromancerBackWalkOverlay, d2resource.PaletteFechar), image.Rectangle{Min: image.Point{265, 220}, Max: image.Point{55, 175}}, v.soundManager.LoadSoundEffect(d2resource.SFXNecromancerSelect), v.soundManager.LoadSoundEffect(d2resource.SFXNecromancerDeselect), } - v.heroRenderInfo[d2enum.HeroNecromancer].IdleSprite.MoveTo(300, 335) - v.heroRenderInfo[d2enum.HeroNecromancer].IdleSprite.Animate = true - v.heroRenderInfo[d2enum.HeroNecromancer].IdleSprite.SpecialFrameTime = 1200 - v.heroRenderInfo[d2enum.HeroNecromancer].IdleSelectedSprite.MoveTo(300, 335) - v.heroRenderInfo[d2enum.HeroNecromancer].IdleSelectedSprite.Animate = true - v.heroRenderInfo[d2enum.HeroNecromancer].IdleSelectedSprite.SpecialFrameTime = 1200 - v.heroRenderInfo[d2enum.HeroNecromancer].ForwardWalkSprite.MoveTo(300, 335) - v.heroRenderInfo[d2enum.HeroNecromancer].ForwardWalkSprite.Animate = true - v.heroRenderInfo[d2enum.HeroNecromancer].ForwardWalkSprite.SpecialFrameTime = 2000 - v.heroRenderInfo[d2enum.HeroNecromancer].ForwardWalkSprite.StopOnLastFrame = true - v.heroRenderInfo[d2enum.HeroNecromancer].ForwardWalkSpriteOverlay.Blend = true - v.heroRenderInfo[d2enum.HeroNecromancer].ForwardWalkSpriteOverlay.MoveTo(300, 335) - v.heroRenderInfo[d2enum.HeroNecromancer].ForwardWalkSpriteOverlay.Animate = true - v.heroRenderInfo[d2enum.HeroNecromancer].ForwardWalkSpriteOverlay.SpecialFrameTime = 2000 - v.heroRenderInfo[d2enum.HeroNecromancer].ForwardWalkSpriteOverlay.StopOnLastFrame = true - v.heroRenderInfo[d2enum.HeroNecromancer].SelectedSprite.MoveTo(300, 335) - v.heroRenderInfo[d2enum.HeroNecromancer].SelectedSprite.Animate = true - v.heroRenderInfo[d2enum.HeroNecromancer].SelectedSpriteOverlay.Blend = true - v.heroRenderInfo[d2enum.HeroNecromancer].SelectedSpriteOverlay.MoveTo(300, 335) - v.heroRenderInfo[d2enum.HeroNecromancer].SelectedSpriteOverlay.Animate = true - v.heroRenderInfo[d2enum.HeroNecromancer].BackWalkSprite.MoveTo(300, 335) - v.heroRenderInfo[d2enum.HeroNecromancer].BackWalkSprite.Animate = true - v.heroRenderInfo[d2enum.HeroNecromancer].BackWalkSprite.SpecialFrameTime = 1500 - v.heroRenderInfo[d2enum.HeroNecromancer].BackWalkSprite.StopOnLastFrame = true - v.heroRenderInfo[d2enum.HeroNecromancer].BackWalkSpriteOverlay.Blend = true - v.heroRenderInfo[d2enum.HeroNecromancer].BackWalkSpriteOverlay.MoveTo(300, 335) - v.heroRenderInfo[d2enum.HeroNecromancer].BackWalkSpriteOverlay.Animate = true - v.heroRenderInfo[d2enum.HeroNecromancer].BackWalkSpriteOverlay.SpecialFrameTime = 1500 - v.heroRenderInfo[d2enum.HeroNecromancer].BackWalkSpriteOverlay.StopOnLastFrame = true + v.heroRenderInfo[d2enum.HeroNecromancer].IdleSprite.SetPosition(300, 335) + v.heroRenderInfo[d2enum.HeroNecromancer].IdleSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroNecromancer].IdleSprite.SetPlayLengthMs(1200) + v.heroRenderInfo[d2enum.HeroNecromancer].IdleSelectedSprite.SetPosition(300, 335) + v.heroRenderInfo[d2enum.HeroNecromancer].IdleSelectedSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroNecromancer].IdleSelectedSprite.SetPlayLengthMs(1200) + v.heroRenderInfo[d2enum.HeroNecromancer].ForwardWalkSprite.SetPosition(300, 335) + v.heroRenderInfo[d2enum.HeroNecromancer].ForwardWalkSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroNecromancer].ForwardWalkSprite.SetPlayLengthMs(2000) + v.heroRenderInfo[d2enum.HeroNecromancer].ForwardWalkSprite.SetPlayLoop(false) + v.heroRenderInfo[d2enum.HeroNecromancer].ForwardWalkSpriteOverlay.SetBlend(true) + v.heroRenderInfo[d2enum.HeroNecromancer].ForwardWalkSpriteOverlay.SetPosition(300, 335) + v.heroRenderInfo[d2enum.HeroNecromancer].ForwardWalkSpriteOverlay.PlayForward() + v.heroRenderInfo[d2enum.HeroNecromancer].ForwardWalkSpriteOverlay.SetPlayLengthMs(2000) + v.heroRenderInfo[d2enum.HeroNecromancer].ForwardWalkSpriteOverlay.SetPlayLoop(false) + v.heroRenderInfo[d2enum.HeroNecromancer].SelectedSprite.SetPosition(300, 335) + v.heroRenderInfo[d2enum.HeroNecromancer].SelectedSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroNecromancer].SelectedSpriteOverlay.SetBlend(true) + v.heroRenderInfo[d2enum.HeroNecromancer].SelectedSpriteOverlay.SetPosition(300, 335) + v.heroRenderInfo[d2enum.HeroNecromancer].SelectedSpriteOverlay.PlayForward() + v.heroRenderInfo[d2enum.HeroNecromancer].BackWalkSprite.SetPosition(300, 335) + v.heroRenderInfo[d2enum.HeroNecromancer].BackWalkSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroNecromancer].BackWalkSprite.SetPlayLengthMs(1500) + v.heroRenderInfo[d2enum.HeroNecromancer].BackWalkSprite.SetPlayLoop(false) + v.heroRenderInfo[d2enum.HeroNecromancer].BackWalkSpriteOverlay.SetBlend(true) + v.heroRenderInfo[d2enum.HeroNecromancer].BackWalkSpriteOverlay.SetPosition(300, 335) + v.heroRenderInfo[d2enum.HeroNecromancer].BackWalkSpriteOverlay.PlayForward() + v.heroRenderInfo[d2enum.HeroNecromancer].BackWalkSpriteOverlay.SetPlayLengthMs(1500) + v.heroRenderInfo[d2enum.HeroNecromancer].BackWalkSpriteOverlay.SetPlayLoop(false) }, func() { v.heroRenderInfo[d2enum.HeroPaladin] = &HeroRenderInfo{ d2enum.HeroStanceIdle, - v.loadSprite(d2resource.CharacterSelectPaladinUnselected, d2enum.Fechar), - v.loadSprite(d2resource.CharacterSelectPaladinUnselectedH, d2enum.Fechar), - v.loadSprite(d2resource.CharacterSelecPaladinForwardWalk, d2enum.Fechar), - v.loadSprite(d2resource.CharacterSelecPaladinForwardWalkOverlay, d2enum.Fechar), - v.loadSprite(d2resource.CharacterSelecPaladinSelected, d2enum.Fechar), - d2render.Sprite{}, - v.loadSprite(d2resource.CharacterSelecPaladinBackWalk, d2enum.Fechar), - d2render.Sprite{}, + d2render.MustLoadSprite(d2resource.CharacterSelectPaladinUnselected, d2resource.PaletteFechar), + d2render.MustLoadSprite(d2resource.CharacterSelectPaladinUnselectedH, d2resource.PaletteFechar), + d2render.MustLoadSprite(d2resource.CharacterSelecPaladinForwardWalk, d2resource.PaletteFechar), + d2render.MustLoadSprite(d2resource.CharacterSelecPaladinForwardWalkOverlay, d2resource.PaletteFechar), + d2render.MustLoadSprite(d2resource.CharacterSelecPaladinSelected, d2resource.PaletteFechar), + nil, + d2render.MustLoadSprite(d2resource.CharacterSelecPaladinBackWalk, d2resource.PaletteFechar), + nil, image.Rectangle{Min: image.Point{490, 210}, Max: image.Point{65, 180}}, v.soundManager.LoadSoundEffect(d2resource.SFXPaladinSelect), v.soundManager.LoadSoundEffect(d2resource.SFXPaladinDeselect), } - v.heroRenderInfo[d2enum.HeroPaladin].IdleSprite.MoveTo(521, 338) - v.heroRenderInfo[d2enum.HeroPaladin].IdleSprite.Animate = true - v.heroRenderInfo[d2enum.HeroPaladin].IdleSprite.SpecialFrameTime = 2500 - v.heroRenderInfo[d2enum.HeroPaladin].IdleSelectedSprite.MoveTo(521, 338) - v.heroRenderInfo[d2enum.HeroPaladin].IdleSelectedSprite.Animate = true - v.heroRenderInfo[d2enum.HeroPaladin].IdleSelectedSprite.SpecialFrameTime = 2500 - v.heroRenderInfo[d2enum.HeroPaladin].ForwardWalkSprite.MoveTo(521, 338) - v.heroRenderInfo[d2enum.HeroPaladin].ForwardWalkSprite.Animate = true - v.heroRenderInfo[d2enum.HeroPaladin].ForwardWalkSprite.SpecialFrameTime = 3400 - v.heroRenderInfo[d2enum.HeroPaladin].ForwardWalkSprite.StopOnLastFrame = true - v.heroRenderInfo[d2enum.HeroPaladin].ForwardWalkSpriteOverlay.MoveTo(521, 338) - v.heroRenderInfo[d2enum.HeroPaladin].ForwardWalkSpriteOverlay.Animate = true - v.heroRenderInfo[d2enum.HeroPaladin].ForwardWalkSpriteOverlay.SpecialFrameTime = 3400 - v.heroRenderInfo[d2enum.HeroPaladin].ForwardWalkSpriteOverlay.StopOnLastFrame = true - v.heroRenderInfo[d2enum.HeroPaladin].SelectedSprite.MoveTo(521, 338) - v.heroRenderInfo[d2enum.HeroPaladin].SelectedSprite.Animate = true - v.heroRenderInfo[d2enum.HeroPaladin].SelectedSprite.SpecialFrameTime = 650 - v.heroRenderInfo[d2enum.HeroPaladin].BackWalkSprite.MoveTo(521, 338) - v.heroRenderInfo[d2enum.HeroPaladin].BackWalkSprite.Animate = true - v.heroRenderInfo[d2enum.HeroPaladin].BackWalkSprite.SpecialFrameTime = 1300 - v.heroRenderInfo[d2enum.HeroPaladin].BackWalkSprite.StopOnLastFrame = true + v.heroRenderInfo[d2enum.HeroPaladin].IdleSprite.SetPosition(521, 338) + v.heroRenderInfo[d2enum.HeroPaladin].IdleSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroPaladin].IdleSprite.SetPlayLengthMs(2500) + v.heroRenderInfo[d2enum.HeroPaladin].IdleSelectedSprite.SetPosition(521, 338) + v.heroRenderInfo[d2enum.HeroPaladin].IdleSelectedSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroPaladin].IdleSelectedSprite.SetPlayLengthMs(2500) + v.heroRenderInfo[d2enum.HeroPaladin].ForwardWalkSprite.SetPosition(521, 338) + v.heroRenderInfo[d2enum.HeroPaladin].ForwardWalkSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroPaladin].ForwardWalkSprite.SetPlayLengthMs(3400) + v.heroRenderInfo[d2enum.HeroPaladin].ForwardWalkSprite.SetPlayLoop(false) + v.heroRenderInfo[d2enum.HeroPaladin].ForwardWalkSpriteOverlay.SetPosition(521, 338) + v.heroRenderInfo[d2enum.HeroPaladin].ForwardWalkSpriteOverlay.PlayForward() + v.heroRenderInfo[d2enum.HeroPaladin].ForwardWalkSpriteOverlay.SetPlayLengthMs(3400) + v.heroRenderInfo[d2enum.HeroPaladin].ForwardWalkSpriteOverlay.SetPlayLoop(false) + v.heroRenderInfo[d2enum.HeroPaladin].SelectedSprite.SetPosition(521, 338) + v.heroRenderInfo[d2enum.HeroPaladin].SelectedSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroPaladin].SelectedSprite.SetPlayLengthMs(650) + v.heroRenderInfo[d2enum.HeroPaladin].BackWalkSprite.SetPosition(521, 338) + v.heroRenderInfo[d2enum.HeroPaladin].BackWalkSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroPaladin].BackWalkSprite.SetPlayLengthMs(1300) + v.heroRenderInfo[d2enum.HeroPaladin].BackWalkSprite.SetPlayLoop(false) }, func() { v.heroRenderInfo[d2enum.HeroAmazon] = &HeroRenderInfo{ d2enum.HeroStanceIdle, - v.loadSprite(d2resource.CharacterSelectAmazonUnselected, d2enum.Fechar), - v.loadSprite(d2resource.CharacterSelectAmazonUnselectedH, d2enum.Fechar), - v.loadSprite(d2resource.CharacterSelecAmazonForwardWalk, d2enum.Fechar), - d2render.Sprite{}, - v.loadSprite(d2resource.CharacterSelecAmazonSelected, d2enum.Fechar), - d2render.Sprite{}, - v.loadSprite(d2resource.CharacterSelecAmazonBackWalk, d2enum.Fechar), - d2render.Sprite{}, + d2render.MustLoadSprite(d2resource.CharacterSelectAmazonUnselected, d2resource.PaletteFechar), + d2render.MustLoadSprite(d2resource.CharacterSelectAmazonUnselectedH, d2resource.PaletteFechar), + d2render.MustLoadSprite(d2resource.CharacterSelecAmazonForwardWalk, d2resource.PaletteFechar), + nil, + d2render.MustLoadSprite(d2resource.CharacterSelecAmazonSelected, d2resource.PaletteFechar), + nil, + d2render.MustLoadSprite(d2resource.CharacterSelecAmazonBackWalk, d2resource.PaletteFechar), + nil, image.Rectangle{Min: image.Point{70, 220}, Max: image.Point{55, 200}}, v.soundManager.LoadSoundEffect(d2resource.SFXAmazonSelect), v.soundManager.LoadSoundEffect(d2resource.SFXAmazonDeselect), } - v.heroRenderInfo[d2enum.HeroAmazon].IdleSprite.MoveTo(100, 339) - v.heroRenderInfo[d2enum.HeroAmazon].IdleSprite.Animate = true - v.heroRenderInfo[d2enum.HeroAmazon].IdleSprite.SpecialFrameTime = 2500 - v.heroRenderInfo[d2enum.HeroAmazon].IdleSelectedSprite.MoveTo(100, 339) - v.heroRenderInfo[d2enum.HeroAmazon].IdleSelectedSprite.Animate = true - v.heroRenderInfo[d2enum.HeroAmazon].IdleSelectedSprite.SpecialFrameTime = 2500 - v.heroRenderInfo[d2enum.HeroAmazon].ForwardWalkSprite.MoveTo(100, 339) - v.heroRenderInfo[d2enum.HeroAmazon].ForwardWalkSprite.Animate = true - v.heroRenderInfo[d2enum.HeroAmazon].ForwardWalkSprite.SpecialFrameTime = 2200 - v.heroRenderInfo[d2enum.HeroAmazon].ForwardWalkSprite.StopOnLastFrame = true - v.heroRenderInfo[d2enum.HeroAmazon].SelectedSprite.MoveTo(100, 339) - v.heroRenderInfo[d2enum.HeroAmazon].SelectedSprite.Animate = true - v.heroRenderInfo[d2enum.HeroAmazon].SelectedSprite.SpecialFrameTime = 1350 - v.heroRenderInfo[d2enum.HeroAmazon].BackWalkSprite.MoveTo(100, 339) - v.heroRenderInfo[d2enum.HeroAmazon].BackWalkSprite.Animate = true - v.heroRenderInfo[d2enum.HeroAmazon].BackWalkSprite.SpecialFrameTime = 1500 - v.heroRenderInfo[d2enum.HeroAmazon].BackWalkSprite.StopOnLastFrame = true + v.heroRenderInfo[d2enum.HeroAmazon].IdleSprite.SetPosition(100, 339) + v.heroRenderInfo[d2enum.HeroAmazon].IdleSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroAmazon].IdleSprite.SetPlayLengthMs(2500) + v.heroRenderInfo[d2enum.HeroAmazon].IdleSelectedSprite.SetPosition(100, 339) + v.heroRenderInfo[d2enum.HeroAmazon].IdleSelectedSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroAmazon].IdleSelectedSprite.SetPlayLengthMs(2500) + v.heroRenderInfo[d2enum.HeroAmazon].ForwardWalkSprite.SetPosition(100, 339) + v.heroRenderInfo[d2enum.HeroAmazon].ForwardWalkSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroAmazon].ForwardWalkSprite.SetPlayLengthMs(2200) + v.heroRenderInfo[d2enum.HeroAmazon].ForwardWalkSprite.SetPlayLoop(false) + v.heroRenderInfo[d2enum.HeroAmazon].SelectedSprite.SetPosition(100, 339) + v.heroRenderInfo[d2enum.HeroAmazon].SelectedSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroAmazon].SelectedSprite.SetPlayLengthMs(1350) + v.heroRenderInfo[d2enum.HeroAmazon].BackWalkSprite.SetPosition(100, 339) + v.heroRenderInfo[d2enum.HeroAmazon].BackWalkSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroAmazon].BackWalkSprite.SetPlayLengthMs(1500) + v.heroRenderInfo[d2enum.HeroAmazon].BackWalkSprite.SetPlayLoop(false) }, func() { v.heroRenderInfo[d2enum.HeroAssassin] = &HeroRenderInfo{ d2enum.HeroStanceIdle, - v.loadSprite(d2resource.CharacterSelectAssassinUnselected, d2enum.Fechar), - v.loadSprite(d2resource.CharacterSelectAssassinUnselectedH, d2enum.Fechar), - v.loadSprite(d2resource.CharacterSelectAssassinForwardWalk, d2enum.Fechar), - d2render.Sprite{}, - v.loadSprite(d2resource.CharacterSelectAssassinSelected, d2enum.Fechar), - d2render.Sprite{}, - v.loadSprite(d2resource.CharacterSelectAssassinBackWalk, d2enum.Fechar), - d2render.Sprite{}, + d2render.MustLoadSprite(d2resource.CharacterSelectAssassinUnselected, d2resource.PaletteFechar), + d2render.MustLoadSprite(d2resource.CharacterSelectAssassinUnselectedH, d2resource.PaletteFechar), + d2render.MustLoadSprite(d2resource.CharacterSelectAssassinForwardWalk, d2resource.PaletteFechar), + nil, + d2render.MustLoadSprite(d2resource.CharacterSelectAssassinSelected, d2resource.PaletteFechar), + nil, + d2render.MustLoadSprite(d2resource.CharacterSelectAssassinBackWalk, d2resource.PaletteFechar), + nil, image.Rectangle{Min: image.Point{175, 235}, Max: image.Point{50, 180}}, v.soundManager.LoadSoundEffect(d2resource.SFXAssassinSelect), v.soundManager.LoadSoundEffect(d2resource.SFXAssassinDeselect), } - v.heroRenderInfo[d2enum.HeroAssassin].IdleSprite.MoveTo(231, 365) - v.heroRenderInfo[d2enum.HeroAssassin].IdleSprite.Animate = true - v.heroRenderInfo[d2enum.HeroAssassin].IdleSprite.SpecialFrameTime = 2500 - v.heroRenderInfo[d2enum.HeroAssassin].IdleSelectedSprite.MoveTo(231, 365) - v.heroRenderInfo[d2enum.HeroAssassin].IdleSelectedSprite.Animate = true - v.heroRenderInfo[d2enum.HeroAssassin].IdleSelectedSprite.SpecialFrameTime = 2500 - v.heroRenderInfo[d2enum.HeroAssassin].ForwardWalkSprite.MoveTo(231, 365) - v.heroRenderInfo[d2enum.HeroAssassin].ForwardWalkSprite.Animate = true - v.heroRenderInfo[d2enum.HeroAssassin].ForwardWalkSprite.SpecialFrameTime = 3800 - v.heroRenderInfo[d2enum.HeroAssassin].ForwardWalkSprite.StopOnLastFrame = true - v.heroRenderInfo[d2enum.HeroAssassin].SelectedSprite.MoveTo(231, 365) - v.heroRenderInfo[d2enum.HeroAssassin].SelectedSprite.Animate = true - v.heroRenderInfo[d2enum.HeroAssassin].SelectedSprite.SpecialFrameTime = 2500 - v.heroRenderInfo[d2enum.HeroAssassin].BackWalkSprite.MoveTo(231, 365) - v.heroRenderInfo[d2enum.HeroAssassin].BackWalkSprite.Animate = true - v.heroRenderInfo[d2enum.HeroAssassin].BackWalkSprite.SpecialFrameTime = 1500 - v.heroRenderInfo[d2enum.HeroAssassin].BackWalkSprite.StopOnLastFrame = true + v.heroRenderInfo[d2enum.HeroAssassin].IdleSprite.SetPosition(231, 365) + v.heroRenderInfo[d2enum.HeroAssassin].IdleSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroAssassin].IdleSprite.SetPlayLengthMs(2500) + v.heroRenderInfo[d2enum.HeroAssassin].IdleSelectedSprite.SetPosition(231, 365) + v.heroRenderInfo[d2enum.HeroAssassin].IdleSelectedSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroAssassin].IdleSelectedSprite.SetPlayLengthMs(2500) + v.heroRenderInfo[d2enum.HeroAssassin].ForwardWalkSprite.SetPosition(231, 365) + v.heroRenderInfo[d2enum.HeroAssassin].ForwardWalkSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroAssassin].ForwardWalkSprite.SetPlayLengthMs(3800) + v.heroRenderInfo[d2enum.HeroAssassin].ForwardWalkSprite.SetPlayLoop(false) + v.heroRenderInfo[d2enum.HeroAssassin].SelectedSprite.SetPosition(231, 365) + v.heroRenderInfo[d2enum.HeroAssassin].SelectedSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroAssassin].SelectedSprite.SetPlayLengthMs(2500) + v.heroRenderInfo[d2enum.HeroAssassin].BackWalkSprite.SetPosition(231, 365) + v.heroRenderInfo[d2enum.HeroAssassin].BackWalkSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroAssassin].BackWalkSprite.SetPlayLengthMs(1500) + v.heroRenderInfo[d2enum.HeroAssassin].BackWalkSprite.SetPlayLoop(false) }, func() { v.heroRenderInfo[d2enum.HeroDruid] = &HeroRenderInfo{ d2enum.HeroStanceIdle, - v.loadSprite(d2resource.CharacterSelectDruidUnselected, d2enum.Fechar), - v.loadSprite(d2resource.CharacterSelectDruidUnselectedH, d2enum.Fechar), - v.loadSprite(d2resource.CharacterSelectDruidForwardWalk, d2enum.Fechar), - d2render.Sprite{}, - v.loadSprite(d2resource.CharacterSelectDruidSelected, d2enum.Fechar), - d2render.Sprite{}, - v.loadSprite(d2resource.CharacterSelectDruidBackWalk, d2enum.Fechar), - d2render.Sprite{}, + d2render.MustLoadSprite(d2resource.CharacterSelectDruidUnselected, d2resource.PaletteFechar), + d2render.MustLoadSprite(d2resource.CharacterSelectDruidUnselectedH, d2resource.PaletteFechar), + d2render.MustLoadSprite(d2resource.CharacterSelectDruidForwardWalk, d2resource.PaletteFechar), + nil, + d2render.MustLoadSprite(d2resource.CharacterSelectDruidSelected, d2resource.PaletteFechar), + nil, + d2render.MustLoadSprite(d2resource.CharacterSelectDruidBackWalk, d2resource.PaletteFechar), + nil, image.Rectangle{Min: image.Point{680, 220}, Max: image.Point{70, 195}}, v.soundManager.LoadSoundEffect(d2resource.SFXDruidSelect), v.soundManager.LoadSoundEffect(d2resource.SFXDruidDeselect), } - v.heroRenderInfo[d2enum.HeroDruid].IdleSprite.MoveTo(720, 370) - v.heroRenderInfo[d2enum.HeroDruid].IdleSprite.Animate = true - v.heroRenderInfo[d2enum.HeroDruid].IdleSprite.SpecialFrameTime = 1500 - v.heroRenderInfo[d2enum.HeroDruid].IdleSelectedSprite.MoveTo(720, 370) - v.heroRenderInfo[d2enum.HeroDruid].IdleSelectedSprite.Animate = true - v.heroRenderInfo[d2enum.HeroDruid].IdleSelectedSprite.SpecialFrameTime = 1500 - v.heroRenderInfo[d2enum.HeroDruid].ForwardWalkSprite.MoveTo(720, 370) - v.heroRenderInfo[d2enum.HeroDruid].ForwardWalkSprite.Animate = true - v.heroRenderInfo[d2enum.HeroDruid].ForwardWalkSprite.SpecialFrameTime = 4800 - v.heroRenderInfo[d2enum.HeroDruid].ForwardWalkSprite.StopOnLastFrame = true - v.heroRenderInfo[d2enum.HeroDruid].SelectedSprite.MoveTo(720, 370) - v.heroRenderInfo[d2enum.HeroDruid].SelectedSprite.Animate = true - v.heroRenderInfo[d2enum.HeroDruid].SelectedSprite.SpecialFrameTime = 1500 - v.heroRenderInfo[d2enum.HeroDruid].BackWalkSprite.MoveTo(720, 370) - v.heroRenderInfo[d2enum.HeroDruid].BackWalkSprite.Animate = true - v.heroRenderInfo[d2enum.HeroDruid].BackWalkSprite.SpecialFrameTime = 1500 - v.heroRenderInfo[d2enum.HeroDruid].BackWalkSprite.StopOnLastFrame = true + v.heroRenderInfo[d2enum.HeroDruid].IdleSprite.SetPosition(720, 370) + v.heroRenderInfo[d2enum.HeroDruid].IdleSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroDruid].IdleSprite.SetPlayLengthMs(1500) + v.heroRenderInfo[d2enum.HeroDruid].IdleSelectedSprite.SetPosition(720, 370) + v.heroRenderInfo[d2enum.HeroDruid].IdleSelectedSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroDruid].IdleSelectedSprite.SetPlayLengthMs(1500) + v.heroRenderInfo[d2enum.HeroDruid].ForwardWalkSprite.SetPosition(720, 370) + v.heroRenderInfo[d2enum.HeroDruid].ForwardWalkSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroDruid].ForwardWalkSprite.SetPlayLengthMs(4800) + v.heroRenderInfo[d2enum.HeroDruid].ForwardWalkSprite.SetPlayLoop(false) + v.heroRenderInfo[d2enum.HeroDruid].SelectedSprite.SetPosition(720, 370) + v.heroRenderInfo[d2enum.HeroDruid].SelectedSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroDruid].SelectedSprite.SetPlayLengthMs(1500) + v.heroRenderInfo[d2enum.HeroDruid].BackWalkSprite.SetPosition(720, 370) + v.heroRenderInfo[d2enum.HeroDruid].BackWalkSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroDruid].BackWalkSprite.SetPlayLengthMs(1500) + v.heroRenderInfo[d2enum.HeroDruid].BackWalkSprite.SetPlayLoop(false) }, } } @@ -451,22 +436,22 @@ func (v *SelectHeroClass) Unload() { } func (v SelectHeroClass) onExitButtonClicked() { - v.sceneProvider.SetNextScene(CreateCharacterSelect(v.fileProvider, v.sceneProvider, v.uiManager, v.soundManager)) + v.sceneProvider.SetNextScene(CreateCharacterSelect(v.sceneProvider, v.uiManager, v.soundManager)) } func (v SelectHeroClass) onOkButtonClicked() { gameState := d2core.CreateGameState(v.heroNameTextbox.GetText(), v.selectedHero, v.hardcoreCheckbox.GetCheckState()) - v.sceneProvider.SetNextScene(CreateGame(v.fileProvider, v.sceneProvider, v.uiManager, v.soundManager, gameState)) + v.sceneProvider.SetNextScene(CreateGame(v.sceneProvider, v.uiManager, v.soundManager, gameState)) } func (v *SelectHeroClass) Render(screen *ebiten.Image) { - v.bgImage.DrawSegments(screen, 4, 3, 0) - v.headingLabel.Draw(screen) + v.bgImage.RenderSegmented(screen, 4, 3, 0) + v.headingLabel.Render(screen) if v.selectedHero != d2enum.HeroNone { - v.heroClassLabel.Draw(screen) - v.heroDesc1Label.Draw(screen) - v.heroDesc2Label.Draw(screen) - v.heroDesc3Label.Draw(screen) + v.heroClassLabel.Render(screen) + v.heroDesc1Label.Render(screen) + v.heroDesc2Label.Render(screen) + v.heroDesc3Label.Render(screen) } for heroClass, heroInfo := range v.heroRenderInfo { if heroInfo.Stance == d2enum.HeroStanceIdle || heroInfo.Stance == d2enum.HeroStanceIdleSelected { @@ -478,11 +463,11 @@ func (v *SelectHeroClass) Render(screen *ebiten.Image) { v.renderHero(screen, heroClass) } } - v.campfire.Draw(screen) + v.campfire.Render(screen) if v.heroNameTextbox.GetVisible() { - v.heroNameLabel.Draw(screen) - v.expansionCharLabel.Draw(screen) - v.hardcoreCharLabel.Draw(screen) + v.heroNameLabel.Render(screen) + v.expansionCharLabel.Render(screen) + v.hardcoreCharLabel.Render(screen) } } @@ -512,18 +497,16 @@ func (v *SelectHeroClass) updateHeroSelectionHover(hero d2enum.Hero, canSelect b renderInfo := v.heroRenderInfo[hero] switch renderInfo.Stance { case d2enum.HeroStanceApproaching: - if renderInfo.ForwardWalkSprite.OnLastFrame() { + if renderInfo.ForwardWalkSprite.IsOnLastFrame() { renderInfo.Stance = d2enum.HeroStanceSelected - renderInfo.SelectedSprite.ResetAnimation() - if renderInfo.SelectedSpriteOverlay.IsValid() { - renderInfo.SelectedSpriteOverlay.ResetAnimation() - } + setSpriteToFirstFrame(renderInfo.SelectedSprite) + setSpriteToFirstFrame(renderInfo.SelectedSpriteOverlay) } return case d2enum.HeroStanceRetreating: - if renderInfo.BackWalkSprite.OnLastFrame() { + if renderInfo.BackWalkSprite.IsOnLastFrame() { renderInfo.Stance = d2enum.HeroStanceIdle - renderInfo.IdleSprite.ResetAnimation() + setSpriteToFirstFrame(renderInfo.IdleSprite) } return } @@ -543,10 +526,8 @@ func (v *SelectHeroClass) updateHeroSelectionHover(hero d2enum.Hero, canSelect b v.expansionCheckbox.SetVisible(true) v.hardcoreCheckbox.SetVisible(true) renderInfo.Stance = d2enum.HeroStanceApproaching - renderInfo.ForwardWalkSprite.ResetAnimation() - if renderInfo.ForwardWalkSpriteOverlay.IsValid() { - renderInfo.ForwardWalkSpriteOverlay.ResetAnimation() - } + setSpriteToFirstFrame(renderInfo.ForwardWalkSprite) + setSpriteToFirstFrame(renderInfo.ForwardWalkSpriteOverlay) for _, heroInfo := range v.heroRenderInfo { if heroInfo.Stance != d2enum.HeroStanceSelected { continue @@ -554,10 +535,8 @@ func (v *SelectHeroClass) updateHeroSelectionHover(hero d2enum.Hero, canSelect b heroInfo.SelectSfx.Stop() heroInfo.DeselectSfx.Play() heroInfo.Stance = d2enum.HeroStanceRetreating - heroInfo.BackWalkSprite.ResetAnimation() - if heroInfo.BackWalkSpriteOverlay.IsValid() { - heroInfo.BackWalkSpriteOverlay.ResetAnimation() - } + setSpriteToFirstFrame(heroInfo.BackWalkSprite) + setSpriteToFirstFrame(heroInfo.BackWalkSpriteOverlay) } v.selectedHero = hero v.updateHeroText() @@ -567,12 +546,10 @@ func (v *SelectHeroClass) updateHeroSelectionHover(hero d2enum.Hero, canSelect b } if mouseHover && renderInfo.Stance != d2enum.HeroStanceIdleSelected { - renderInfo.IdleSelectedSprite.LastFrameTime = renderInfo.IdleSprite.LastFrameTime - renderInfo.IdleSelectedSprite.Frame = renderInfo.IdleSprite.Frame + renderInfo.IdleSelectedSprite.SetCurrentFrame(renderInfo.IdleSprite.GetCurrentFrame()) renderInfo.Stance = d2enum.HeroStanceIdleSelected } else if !mouseHover && renderInfo.Stance != d2enum.HeroStanceIdle { - renderInfo.IdleSprite.LastFrameTime = renderInfo.IdleSelectedSprite.LastFrameTime - renderInfo.IdleSprite.Frame = renderInfo.IdleSelectedSprite.Frame + renderInfo.IdleSprite.SetCurrentFrame(renderInfo.IdleSelectedSprite.GetCurrentFrame()) renderInfo.Stance = d2enum.HeroStanceIdle } @@ -587,24 +564,18 @@ func (v *SelectHeroClass) renderHero(screen *ebiten.Image, hero d2enum.Hero) { renderInfo := v.heroRenderInfo[hero] switch renderInfo.Stance { case d2enum.HeroStanceIdle: - renderInfo.IdleSprite.Draw(screen) + drawSprite(renderInfo.IdleSprite, screen) case d2enum.HeroStanceIdleSelected: - renderInfo.IdleSelectedSprite.Draw(screen) + drawSprite(renderInfo.IdleSelectedSprite, screen) case d2enum.HeroStanceApproaching: - renderInfo.ForwardWalkSprite.Draw(screen) - if renderInfo.ForwardWalkSpriteOverlay.IsValid() { - renderInfo.ForwardWalkSpriteOverlay.Draw(screen) - } + drawSprite(renderInfo.ForwardWalkSprite, screen) + drawSprite(renderInfo.ForwardWalkSpriteOverlay, screen) case d2enum.HeroStanceSelected: - renderInfo.SelectedSprite.Draw(screen) - if renderInfo.SelectedSpriteOverlay.IsValid() { - renderInfo.SelectedSpriteOverlay.Draw(screen) - } + drawSprite(renderInfo.SelectedSprite, screen) + drawSprite(renderInfo.SelectedSpriteOverlay, screen) case d2enum.HeroStanceRetreating: - renderInfo.BackWalkSprite.Draw(screen) - if renderInfo.BackWalkSpriteOverlay.IsValid() { - renderInfo.BackWalkSpriteOverlay.Draw(screen) - } + drawSprite(renderInfo.BackWalkSprite, screen) + drawSprite(renderInfo.BackWalkSpriteOverlay, screen) } } @@ -669,3 +640,15 @@ func (v *SelectHeroClass) setDescLabels(descKey string) { v.heroDesc3Label.SetText("") } } + +func setSpriteToFirstFrame(sprite *d2render.Sprite) { + if sprite != nil { + sprite.Rewind() + } +} + +func drawSprite(sprite *d2render.Sprite, target *ebiten.Image) { + if sprite != nil { + sprite.Render(target) + } +} diff --git a/d2core/engine.go b/d2core/engine.go index 702a4ac7..1159c530 100644 --- a/d2core/engine.go +++ b/d2core/engine.go @@ -7,16 +7,12 @@ import ( "strconv" "time" - "github.com/OpenDiablo2/D2Shared/d2data/d2dc6" - "github.com/OpenDiablo2/D2Shared/d2common/d2resource" "github.com/OpenDiablo2/D2Shared/d2helper" "github.com/OpenDiablo2/OpenDiablo2/d2corecommon/d2coreinterface" - "github.com/OpenDiablo2/D2Shared/d2common/d2enum" - "github.com/OpenDiablo2/OpenDiablo2/d2render" "github.com/OpenDiablo2/D2Shared/d2data" @@ -26,6 +22,7 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2audio" "github.com/OpenDiablo2/D2Shared/d2common" + "github.com/OpenDiablo2/OpenDiablo2/d2asset" "github.com/OpenDiablo2/OpenDiablo2/d2corecommon" "github.com/OpenDiablo2/OpenDiablo2/d2render/d2ui" @@ -38,7 +35,7 @@ import ( type Engine struct { Settings *d2corecommon.Configuration // Engine configuration settings from json file CheckedPatch map[string]bool // First time we check a file, we'll check if it's in the patch. This notes that we've already checked that. - LoadingSprite d2render.Sprite // The sprite shown when loading stuff + LoadingSprite *d2render.Sprite // The sprite shown when loading stuff loadingProgress float64 // LoadingProcess is a range between 0.0 and 1.0. If set, loading screen displays. loadingIndex int // Determines which load function is currently being called thingsToLoad []func() // The load functions for the next scene @@ -50,7 +47,6 @@ type Engine struct { fullscreenKey bool // When true, the fullscreen toggle is still being pressed lastTime float64 // Last time we updated the scene showFPS bool - assetManager *assetManager } // CreateEngine creates and instance of the OpenDiablo2 engine @@ -62,7 +58,8 @@ func CreateEngine() Engine { log.Printf("could not load settings: %v", err) } - result.assetManager = createAssetManager(result.Settings) + d2asset.Initialize(result.Settings) + d2resource.LanguageCode = result.Settings.Language d2datadict.LoadPalettes(nil, &result) d2common.LoadTextDictionary(&result) @@ -80,17 +77,18 @@ func CreateEngine() Engine { d2data.LoadAnimationData(&result) d2datadict.LoadMonStats(&result) LoadHeroObjects() - result.SoundManager = d2audio.CreateManager(&result) + result.SoundManager = d2audio.CreateManager() result.SoundManager.SetVolumes(result.Settings.BgmVolume, result.Settings.SfxVolume) - result.UIManager = d2ui.CreateManager(&result, *result.SoundManager) - result.LoadingSprite = result.LoadSprite(d2resource.LoadingScreen, d2enum.Loading) - loadingSpriteSizeX, loadingSpriteSizeY := result.LoadingSprite.GetSize() - result.LoadingSprite.MoveTo(int(400-(loadingSpriteSizeX/2)), int(300+(loadingSpriteSizeY/2))) + result.UIManager = d2ui.CreateManager(*result.SoundManager) + result.LoadingSprite, _ = d2render.LoadSprite(d2resource.LoadingScreen, d2resource.PaletteLoading) + loadingSpriteSizeX, loadingSpriteSizeY := result.LoadingSprite.GetCurrentFrameSize() + result.LoadingSprite.SetPosition(int(400-(loadingSpriteSizeX/2)), int(300+(loadingSpriteSizeY/2))) return result } func (v *Engine) LoadFile(fileName string) []byte { - return v.assetManager.LoadFile(fileName) + data, _ := d2asset.LoadFile(fileName) + return data } // IsLoading returns true if the engine is currently in a loading state @@ -98,13 +96,6 @@ func (v Engine) IsLoading() bool { return v.loadingProgress < 1.0 } -// LoadSprite loads a sprite from the game's data files -func (v Engine) LoadSprite(fileName string, palette d2enum.PaletteType) d2render.Sprite { - dc6, _ := d2dc6.LoadDC6(v.LoadFile(fileName), d2datadict.Palettes[palette]) - sprite := d2render.CreateSpriteFromDC6(dc6) - return sprite -} - // updateScene handles the scene maintenance for the engine func (v *Engine) updateScene() { if v.nextScene == nil { @@ -176,14 +167,14 @@ func (v *Engine) Update() { // Draw draws the game func (v Engine) Draw(screen *ebiten.Image) { if v.loadingProgress < 1.0 { - v.LoadingSprite.Frame = int16(d2helper.Max(0, d2helper.Min(uint32(len(v.LoadingSprite.Frames)-1), uint32(float64(len(v.LoadingSprite.Frames)-1)*v.loadingProgress)))) - v.LoadingSprite.Draw(screen) + v.LoadingSprite.SetCurrentFrame(int(d2helper.Max(0, d2helper.Min(uint32(v.LoadingSprite.GetFrameCount()-1), uint32(float64(v.LoadingSprite.GetFrameCount()-1)*v.loadingProgress))))) + v.LoadingSprite.Render(screen) } else { if v.CurrentScene == nil { log.Fatal("no scene loaded") } v.CurrentScene.Render(screen) - v.UIManager.Draw(screen) + v.UIManager.Render(screen) } if v.showFPS { ebitenutil.DebugPrintAt(screen, "vsync:"+strconv.FormatBool(ebiten.IsVsyncEnabled())+"\nFPS:"+strconv.Itoa(int(ebiten.CurrentFPS())), 5, 565) diff --git a/d2core/hero.go b/d2core/hero.go index b6eedc01..b2159c97 100644 --- a/d2core/hero.go +++ b/d2core/hero.go @@ -2,7 +2,6 @@ package d2core import ( "github.com/OpenDiablo2/D2Shared/d2common/d2enum" - "github.com/OpenDiablo2/D2Shared/d2common/d2interface" "github.com/OpenDiablo2/D2Shared/d2data/d2datadict" "github.com/OpenDiablo2/OpenDiablo2/d2render" "github.com/hajimehoshi/ebiten" @@ -15,7 +14,7 @@ type Hero struct { direction int } -func CreateHero(x, y int32, direction int, heroType d2enum.Hero, equipment CharacterEquipment, fileProvider d2interface.FileProvider) *Hero { +func CreateHero(x, y int32, direction int, heroType d2enum.Hero, equipment CharacterEquipment) *Hero { result := &Hero{ AnimatedEntity: d2render.CreateAnimatedEntity(x, y, &d2datadict.ObjectLookupRecord{ Mode: d2enum.AnimationModePlayerNeutral.String(), @@ -32,7 +31,6 @@ func CreateHero(x, y int32, direction int, heroType d2enum.Hero, equipment Chara RH: equipment.RightHand.GetItemCode(), LH: equipment.LeftHand.GetItemCode(), }, - fileProvider, d2enum.Units, ), Equipment: equipment, diff --git a/d2core/npc.go b/d2core/npc.go index 2716bffa..2a1946a9 100644 --- a/d2core/npc.go +++ b/d2core/npc.go @@ -3,7 +3,6 @@ package d2core import ( "github.com/OpenDiablo2/D2Shared/d2common" "github.com/OpenDiablo2/D2Shared/d2common/d2enum" - "github.com/OpenDiablo2/D2Shared/d2common/d2interface" "github.com/OpenDiablo2/D2Shared/d2data/d2datadict" "github.com/OpenDiablo2/OpenDiablo2/d2render" "github.com/hajimehoshi/ebiten" @@ -16,9 +15,9 @@ type NPC struct { path int } -func CreateNPC(x, y int32, object *d2datadict.ObjectLookupRecord, fileProvider d2interface.FileProvider, direction int) *NPC { +func CreateNPC(x, y int32, object *d2datadict.ObjectLookupRecord, direction int) *NPC { result := &NPC{ - AnimatedEntity: d2render.CreateAnimatedEntity(x, y, object, fileProvider, d2enum.Units), + AnimatedEntity: d2render.CreateAnimatedEntity(x, y, object, d2enum.Units), HasPaths: false, } result.AnimatedEntity.SetMode(object.Mode, object.Class, direction) diff --git a/d2corecommon/d2coreinterface/drawable.go b/d2corecommon/d2coreinterface/drawable.go index 1a22433d..32e2acbb 100644 --- a/d2corecommon/d2coreinterface/drawable.go +++ b/d2corecommon/d2coreinterface/drawable.go @@ -4,10 +4,10 @@ import "github.com/hajimehoshi/ebiten" // Drawable represents an instance that can be drawn type Drawable interface { - Draw(target *ebiten.Image) - GetSize() (width, height uint32) - MoveTo(x, y int) - GetLocation() (x, y int) + Render(target *ebiten.Image) + GetSize() (width, height int) + SetPosition(x, y int) + GetPosition() (x, y int) GetVisible() bool SetVisible(visible bool) } diff --git a/d2player/game_controls.go b/d2player/game_controls.go index 2e190af4..a73d14c2 100644 --- a/d2player/game_controls.go +++ b/d2player/game_controls.go @@ -1,22 +1,16 @@ package d2player import ( - "github.com/OpenDiablo2/D2Shared/d2common/d2enum" - "github.com/OpenDiablo2/D2Shared/d2common/d2interface" "github.com/OpenDiablo2/D2Shared/d2common/d2resource" - "github.com/OpenDiablo2/D2Shared/d2data/d2datadict" - "github.com/OpenDiablo2/D2Shared/d2data/d2dc6" "github.com/OpenDiablo2/OpenDiablo2/d2core" "github.com/OpenDiablo2/OpenDiablo2/d2render" "github.com/OpenDiablo2/OpenDiablo2/d2render/d2mapengine" "github.com/hajimehoshi/ebiten" - "log" ) type GameControls struct { - fileProvider d2interface.FileProvider - hero *d2core.Hero - mapEngine *d2mapengine.MapEngine + hero *d2core.Hero + mapEngine *d2mapengine.MapEngine // UI globeSprite *d2render.Sprite @@ -25,11 +19,10 @@ type GameControls struct { skillIcon *d2render.Sprite } -func NewGameControls(fileProvider d2interface.FileProvider, hero *d2core.Hero, mapEngine *d2mapengine.MapEngine) *GameControls { +func NewGameControls(hero *d2core.Hero, mapEngine *d2mapengine.MapEngine) *GameControls { return &GameControls{ - fileProvider: fileProvider, - hero: hero, - mapEngine: mapEngine, + hero: hero, + mapEngine: mapEngine, } } @@ -67,111 +60,86 @@ func (g *GameControls) Move(tickTime float64) { } func (g *GameControls) Load() { - dc6, err := d2dc6.LoadDC6(g.fileProvider.LoadFile(d2resource.GameGlobeOverlap), d2datadict.Palettes[d2enum.Sky]) - if err != nil { - log.Panicf("failed to load %s: %v", d2resource.GameGlobeOverlap, err) - } - globeSprite := d2render.CreateSpriteFromDC6(dc6) - g.globeSprite = &globeSprite - - dc6, err = d2dc6.LoadDC6(g.fileProvider.LoadFile(d2resource.GamePanels), d2datadict.Palettes[d2enum.Sky]) - if err != nil { - log.Panicf("failed to load %s: %v", d2resource.GamePanels, err) - } - mainPanel := d2render.CreateSpriteFromDC6(dc6) - g.mainPanel = &mainPanel - - dc6, err = d2dc6.LoadDC6(g.fileProvider.LoadFile(d2resource.MenuButton), d2datadict.Palettes[d2enum.Sky]) - if err != nil { - log.Panicf("failed to load %s: %v", d2resource.MenuButton, err) - } - menuButton := d2render.CreateSpriteFromDC6(dc6) - g.menuButton = &menuButton - - dc6, err = d2dc6.LoadDC6(g.fileProvider.LoadFile(d2resource.GenericSkills), d2datadict.Palettes[d2enum.Sky]) - if err != nil { - log.Panicf("failed to load %s: %v", d2resource.GenericSkills, err) - } - skillIcon := d2render.CreateSpriteFromDC6(dc6) - g.skillIcon = &skillIcon - + g.globeSprite, _ = d2render.LoadSprite(d2resource.GameGlobeOverlap, d2resource.PaletteSky) + g.mainPanel, _ = d2render.LoadSprite(d2resource.GamePanels, d2resource.PaletteSky) + g.menuButton, _ = d2render.LoadSprite(d2resource.MenuButton, d2resource.PaletteSky) + g.skillIcon, _ = d2render.LoadSprite(d2resource.GenericSkills, d2resource.PaletteSky) } - // TODO: consider caching the panels to single image that is reused. func (g *GameControls) Render(target *ebiten.Image) { width, height := target.Size() - offset := uint32(0) + offset := int(0) // Left globe holder - g.mainPanel.Frame = 0 - w, _ := g.mainPanel.GetSize() - g.mainPanel.MoveTo(int(offset), height) - g.mainPanel.Draw(target) + g.mainPanel.SetCurrentFrame(0) + w, _ := g.mainPanel.GetCurrentFrameSize() + g.mainPanel.SetPosition(int(offset), height) + g.mainPanel.Render(target) // Left globe - g.globeSprite.Frame = 0 - g.globeSprite.MoveTo(int(offset+28), height - 5) - g.globeSprite.Draw(target) + g.globeSprite.SetCurrentFrame(0) + g.globeSprite.SetPosition(int(offset+28), height-5) + g.globeSprite.Render(target) offset += w // Left skill - g.skillIcon.Frame = 2 - w, _ = g.skillIcon.GetSize() - g.skillIcon.MoveTo(int(offset), height) - g.skillIcon.Draw(target) + g.skillIcon.SetCurrentFrame(2) + w, _ = g.skillIcon.GetCurrentFrameSize() + g.skillIcon.SetPosition(int(offset), height) + g.skillIcon.Render(target) offset += w // Left skill selector - g.mainPanel.Frame = 1 - w, _ = g.mainPanel.GetSize() - g.mainPanel.MoveTo(int(offset), height) - g.mainPanel.Draw(target) + g.mainPanel.SetCurrentFrame(1) + w, _ = g.mainPanel.GetCurrentFrameSize() + g.mainPanel.SetPosition(int(offset), height) + g.mainPanel.Render(target) offset += w // Stamina - g.mainPanel.Frame = 2 - w, _ = g.mainPanel.GetSize() - g.mainPanel.MoveTo(int(offset), height) - g.mainPanel.Draw(target) + g.mainPanel.SetCurrentFrame(2) + w, _ = g.mainPanel.GetCurrentFrameSize() + g.mainPanel.SetPosition(int(offset), height) + g.mainPanel.Render(target) offset += w // Center menu button - g.menuButton.Frame = 0 - w, _ = g.mainPanel.GetSize() - g.menuButton.MoveTo((width / 2) - 8 , height - 16) - g.menuButton.Draw(target) + g.menuButton.SetCurrentFrame(0) + w, _ = g.mainPanel.GetCurrentFrameSize() + g.menuButton.SetPosition((width/2)-8, height-16) + g.menuButton.Render(target) // Potions - g.mainPanel.Frame = 3 - w, _ = g.mainPanel.GetSize() - g.mainPanel.MoveTo(int(offset), height) - g.mainPanel.Draw(target) + g.mainPanel.SetCurrentFrame(3) + w, _ = g.mainPanel.GetCurrentFrameSize() + g.mainPanel.SetPosition(int(offset), height) + g.mainPanel.Render(target) offset += w // Right skill selector - g.mainPanel.Frame = 4 - w, _ = g.mainPanel.GetSize() - g.mainPanel.MoveTo(int(offset), height) - g.mainPanel.Draw(target) + g.mainPanel.SetCurrentFrame(4) + w, _ = g.mainPanel.GetCurrentFrameSize() + g.mainPanel.SetPosition(int(offset), height) + g.mainPanel.Render(target) offset += w // Right skill - g.skillIcon.Frame = 10 - w, _ = g.skillIcon.GetSize() - g.skillIcon.MoveTo(int(offset), height) - g.skillIcon.Draw(target) + g.skillIcon.SetCurrentFrame(10) + w, _ = g.skillIcon.GetCurrentFrameSize() + g.skillIcon.SetPosition(int(offset), height) + g.skillIcon.Render(target) offset += w // Right globe holder - g.mainPanel.Frame = 5 - w, _ = g.mainPanel.GetSize() - g.mainPanel.MoveTo(int(offset), height) - g.mainPanel.Draw(target) + g.mainPanel.SetCurrentFrame(5) + w, _ = g.mainPanel.GetCurrentFrameSize() + g.mainPanel.SetPosition(int(offset), height) + g.mainPanel.Render(target) // Right globe - g.globeSprite.Frame = 1 - g.globeSprite.MoveTo(int(offset) + 8, height - 8) - g.globeSprite.Draw(target) + g.globeSprite.SetCurrentFrame(1) + g.globeSprite.SetPosition(int(offset)+8, height-8) + g.globeSprite.Render(target) } diff --git a/d2render/animated_entity.go b/d2render/animated_entity.go index 013f4ad3..209f4462 100644 --- a/d2render/animated_entity.go +++ b/d2render/animated_entity.go @@ -1,6 +1,7 @@ package d2render import ( + "errors" "fmt" "image" "log" @@ -9,12 +10,12 @@ import ( "strings" "github.com/OpenDiablo2/D2Shared/d2common/d2enum" - "github.com/OpenDiablo2/D2Shared/d2common/d2interface" "github.com/OpenDiablo2/D2Shared/d2data" "github.com/OpenDiablo2/D2Shared/d2data/d2cof" "github.com/OpenDiablo2/D2Shared/d2data/d2datadict" "github.com/OpenDiablo2/D2Shared/d2data/d2dcc" "github.com/OpenDiablo2/D2Shared/d2helper" + "github.com/OpenDiablo2/OpenDiablo2/d2asset" "github.com/hajimehoshi/ebiten" ) @@ -30,12 +31,11 @@ type LayerCacheEntry struct { // AnimatedEntity represents an entity on the map that can be animated type AnimatedEntity struct { - fileProvider d2interface.FileProvider LocationX float64 LocationY float64 TileX, TileY int // Coordinates of the tile the unit is within subcellX, subcellY float64 // Subcell coordinates within the current tile - dccLayers map[string]d2dcc.DCC + dccLayers map[string]*d2dcc.DCC Cof *d2cof.COF palette d2enum.PaletteType base string @@ -58,16 +58,15 @@ type AnimatedEntity struct { } // CreateAnimatedEntity creates an instance of AnimatedEntity -func CreateAnimatedEntity(x, y int32, object *d2datadict.ObjectLookupRecord, fileProvider d2interface.FileProvider, palette d2enum.PaletteType) AnimatedEntity { +func CreateAnimatedEntity(x, y int32, object *d2datadict.ObjectLookupRecord, palette d2enum.PaletteType) AnimatedEntity { result := AnimatedEntity{ - fileProvider: fileProvider, - base: object.Base, - token: object.Token, - object: object, - palette: palette, - layerCache: make([]LayerCacheEntry, d2enum.CompositeTypeMax), + base: object.Base, + token: object.Token, + object: object, + palette: palette, + layerCache: make([]LayerCacheEntry, d2enum.CompositeTypeMax), } - result.dccLayers = make(map[string]d2dcc.DCC) + result.dccLayers = make(map[string]*d2dcc.DCC) result.LocationX = float64(x) result.LocationY = float64(y) result.TargetX = result.LocationX @@ -84,7 +83,10 @@ func CreateAnimatedEntity(x, y int32, object *d2datadict.ObjectLookupRecord, fil // SetMode changes the graphical mode of this animated entity func (v *AnimatedEntity) SetMode(animationMode, weaponClass string, direction int) { cofPath := fmt.Sprintf("%s/%s/COF/%s%s%s.COF", v.base, v.token, v.token, animationMode, weaponClass) - v.Cof = d2cof.LoadCOF(cofPath, v.fileProvider) + var err error + if v.Cof, err = d2asset.LoadCOF(cofPath); err != nil { + return + } if v.Cof.NumberOfDirections == 0 || v.Cof.NumberOfLayers == 0 || v.Cof.FramesPerDirection == 0 { return } @@ -95,11 +97,10 @@ func (v *AnimatedEntity) SetMode(animationMode, weaponClass string, direction in if v.direction >= v.Cof.NumberOfDirections { v.direction = v.Cof.NumberOfDirections - 1 } - v.dccLayers = make(map[string]d2dcc.DCC) + v.dccLayers = make(map[string]*d2dcc.DCC) for _, cofLayer := range v.Cof.CofLayers { layerName := DccLayerNames[cofLayer.Type] - v.dccLayers[layerName] = v.LoadLayer(layerName, v.fileProvider) - if !v.dccLayers[layerName].IsValid() { + if v.dccLayers[layerName], err = v.LoadLayer(layerName); err != nil { continue } } @@ -107,7 +108,7 @@ func (v *AnimatedEntity) SetMode(animationMode, weaponClass string, direction in v.updateFrameCache(resetAnimation) } -func (v *AnimatedEntity) LoadLayer(layer string, fileProvider d2interface.FileProvider) d2dcc.DCC { +func (v *AnimatedEntity) LoadLayer(layer string) (*d2dcc.DCC, error) { layerName := "TR" switch strings.ToUpper(layer) { case "HD": // Head @@ -144,15 +145,19 @@ func (v *AnimatedEntity) LoadLayer(layer string, fileProvider d2interface.FilePr layerName = v.object.S8 } if len(layerName) == 0 { - return d2dcc.DCC{} + return nil, errors.New("invalid layer") } dccPath := fmt.Sprintf("%s/%s/%s/%s%s%s%s%s.dcc", v.base, v.token, layer, v.token, layer, layerName, v.animationMode, v.weaponClass) - result := d2dcc.LoadDCC(dccPath, fileProvider) - if !result.IsValid() { + result, err := d2asset.LoadDCC(dccPath) + if err != nil { dccPath = fmt.Sprintf("%s/%s/%s/%s%s%s%s%s.dcc", v.base, v.token, layer, v.token, layer, layerName, v.animationMode, "HTH") - result = d2dcc.LoadDCC(dccPath, fileProvider) + result, err = d2asset.LoadDCC(dccPath) + if err != nil { + return nil, err + } } - return result + + return result, nil } // If an npc has a path to pause at each location. @@ -240,7 +245,7 @@ func (v *AnimatedEntity) updateFrameCache(resetAnimation bool) { layerType := v.Cof.CofLayers[cofLayerIdx].Type layerName := DccLayerNames[layerType] dccLayer := v.dccLayers[layerName] - if !dccLayer.IsValid() { + if dccLayer == nil { continue } diff --git a/d2render/d2mapengine/engine.go b/d2render/d2mapengine/engine.go index 3b74bf80..b646d30e 100644 --- a/d2render/d2mapengine/engine.go +++ b/d2render/d2mapengine/engine.go @@ -6,7 +6,6 @@ import ( "github.com/hajimehoshi/ebiten" "github.com/OpenDiablo2/D2Shared/d2common/d2enum" - "github.com/OpenDiablo2/D2Shared/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2audio" "github.com/OpenDiablo2/OpenDiablo2/d2core" ) @@ -20,7 +19,6 @@ type MapEntity interface { type MapEngine struct { soundManager *d2audio.Manager gameState *d2core.GameState - fileProvider d2interface.FileProvider debugVisLevel int @@ -30,11 +28,10 @@ type MapEngine struct { camera Camera } -func CreateMapEngine(gameState *d2core.GameState, soundManager *d2audio.Manager, fileProvider d2interface.FileProvider) *MapEngine { +func CreateMapEngine(gameState *d2core.GameState, soundManager *d2audio.Manager) *MapEngine { engine := &MapEngine{ gameState: gameState, soundManager: soundManager, - fileProvider: fileProvider, viewport: NewViewport(0, 0, 800, 600), } @@ -88,7 +85,7 @@ func (me *MapEngine) SetDebugVisLevel(debugVisLevel int) { } func (me *MapEngine) GenerateMap(regionType d2enum.RegionIdType, levelPreset int, fileIndex int) { - region, entities := loadRegion(me.gameState.Seed, 0, 0, regionType, levelPreset, me.fileProvider, fileIndex) + region, entities := loadRegion(me.gameState.Seed, 0, 0, regionType, levelPreset, fileIndex) me.regions = append(me.regions, region) me.entities = append(me.entities, entities...) } @@ -96,16 +93,16 @@ func (me *MapEngine) GenerateMap(regionType d2enum.RegionIdType, levelPreset int func (me *MapEngine) GenerateAct1Overworld() { me.soundManager.PlayBGM("/data/global/music/Act1/town1.wav") // TODO: Temp stuff here - region, entities := loadRegion(me.gameState.Seed, 0, 0, d2enum.RegionAct1Town, 1, me.fileProvider, -1) + region, entities := loadRegion(me.gameState.Seed, 0, 0, d2enum.RegionAct1Town, 1, -1) me.regions = append(me.regions, region) me.entities = append(me.entities, entities...) if strings.Contains(region.regionPath, "E1") { - region, entities := loadRegion(me.gameState.Seed, int(region.tileRect.Width-1), 0, d2enum.RegionAct1Town, 2, me.fileProvider, -1) + region, entities := loadRegion(me.gameState.Seed, int(region.tileRect.Width-1), 0, d2enum.RegionAct1Town, 2, -1) me.regions = append(me.regions, region) me.entities = append(me.entities, entities...) } else if strings.Contains(region.regionPath, "S1") { - region, entities := loadRegion(me.gameState.Seed, 0, int(region.tileRect.Height-1), d2enum.RegionAct1Town, 3, me.fileProvider, -1) + region, entities := loadRegion(me.gameState.Seed, 0, int(region.tileRect.Height-1), d2enum.RegionAct1Town, 3, -1) me.regions = append(me.regions, region) me.entities = append(me.entities, entities...) } diff --git a/d2render/d2mapengine/region.go b/d2render/d2mapengine/region.go index 80012f54..f096c484 100644 --- a/d2render/d2mapengine/region.go +++ b/d2render/d2mapengine/region.go @@ -13,11 +13,11 @@ import ( "github.com/OpenDiablo2/D2Shared/d2common" "github.com/OpenDiablo2/D2Shared/d2common/d2enum" - "github.com/OpenDiablo2/D2Shared/d2common/d2interface" "github.com/OpenDiablo2/D2Shared/d2data/d2datadict" "github.com/OpenDiablo2/D2Shared/d2data/d2ds1" "github.com/OpenDiablo2/D2Shared/d2data/d2dt1" "github.com/OpenDiablo2/D2Shared/d2helper" + "github.com/OpenDiablo2/OpenDiablo2/d2asset" "github.com/OpenDiablo2/OpenDiablo2/d2core" "github.com/OpenDiablo2/OpenDiablo2/d2corehelper" "github.com/OpenDiablo2/OpenDiablo2/d2render" @@ -39,7 +39,7 @@ type MapRegion struct { lastFrameTime float64 } -func loadRegion(seed int64, tileOffsetX, tileOffsetY int, levelType d2enum.RegionIdType, levelPreset int, fileProvider d2interface.FileProvider, fileIndex int) (*MapRegion, []MapEntity) { +func loadRegion(seed int64, tileOffsetX, tileOffsetY int, levelType d2enum.RegionIdType, levelPreset int, fileIndex int) (*MapRegion, []MapEntity) { region := &MapRegion{ levelType: d2datadict.LevelTypes[levelType], levelPreset: d2datadict.LevelPresets[levelPreset], @@ -54,7 +54,7 @@ func loadRegion(seed int64, tileOffsetX, tileOffsetY int, levelType d2enum.Regio for _, levelTypeDt1 := range region.levelType.Files { if len(levelTypeDt1) != 0 && levelTypeDt1 != "" && levelTypeDt1 != "0" { - dt1 := d2dt1.LoadDT1("/data/global/tiles/"+levelTypeDt1, fileProvider) + dt1 := d2dt1.LoadDT1(d2asset.MustLoadFile("/data/global/tiles/" + levelTypeDt1)) region.tiles = append(region.tiles, dt1.Tiles...) } } @@ -72,7 +72,7 @@ func loadRegion(seed int64, tileOffsetX, tileOffsetY int, levelType d2enum.Regio } region.regionPath = levelFilesToPick[levelIndex] - region.ds1 = d2ds1.LoadDS1("/data/global/tiles/"+region.regionPath, fileProvider) + region.ds1 = d2ds1.LoadDS1(d2asset.MustLoadFile("/data/global/tiles/" + region.regionPath)) region.tileRect = d2common.Rectangle{ Left: tileOffsetX, Top: tileOffsetY, @@ -80,7 +80,7 @@ func loadRegion(seed int64, tileOffsetX, tileOffsetY int, levelType d2enum.Regio Height: int(region.ds1.Height), } - entities := region.loadEntities(fileProvider) + entities := region.loadEntities() region.loadSpecials() region.generateTileCache() @@ -118,7 +118,7 @@ func (mr *MapRegion) loadSpecials() { } } -func (mr *MapRegion) loadEntities(fileProvider d2interface.FileProvider) []MapEntity { +func (mr *MapRegion) loadEntities() []MapEntity { var entities []MapEntity for _, object := range mr.ds1.Objects { @@ -127,13 +127,13 @@ func (mr *MapRegion) loadEntities(fileProvider d2interface.FileProvider) []MapEn switch object.Lookup.Type { case d2datadict.ObjectTypeCharacter: if object.Lookup.Base != "" && object.Lookup.Token != "" && object.Lookup.TR != "" { - npc := d2core.CreateNPC(int32(worldX), int32(worldY), object.Lookup, fileProvider, 0) + npc := d2core.CreateNPC(int32(worldX), int32(worldY), object.Lookup, 0) npc.SetPaths(object.Paths) entities = append(entities, npc) } case d2datadict.ObjectTypeItem: if object.ObjectInfo != nil && object.ObjectInfo.Draw && object.Lookup.Base != "" && object.Lookup.Token != "" { - entity := d2render.CreateAnimatedEntity(int32(worldX), int32(worldY), object.Lookup, fileProvider, d2enum.Units) + entity := d2render.CreateAnimatedEntity(int32(worldX), int32(worldY), object.Lookup, d2enum.Units) entity.SetMode(object.Lookup.Mode, object.Lookup.Class, 0) entities = append(entities, &entity) } diff --git a/d2render/d2ui/Scrollbar.go b/d2render/d2ui/Scrollbar.go index ccc509d5..484aa674 100644 --- a/d2render/d2ui/Scrollbar.go +++ b/d2render/d2ui/Scrollbar.go @@ -1,11 +1,7 @@ package d2ui import ( - "github.com/OpenDiablo2/D2Shared/d2common/d2enum" - "github.com/OpenDiablo2/D2Shared/d2common/d2interface" "github.com/OpenDiablo2/D2Shared/d2common/d2resource" - "github.com/OpenDiablo2/D2Shared/d2data/d2datadict" - "github.com/OpenDiablo2/D2Shared/d2data/d2dc6" "github.com/OpenDiablo2/OpenDiablo2/d2render" "github.com/hajimehoshi/ebiten" ) @@ -18,18 +14,18 @@ type Scrollbar struct { maxOffset int lastDirChange int onActivate func() - scrollbarSprite d2render.Sprite + scrollbarSprite *d2render.Sprite } -func CreateScrollbar(fileProvider d2interface.FileProvider, x, y, height int) Scrollbar { - dc6, _ := d2dc6.LoadDC6(fileProvider.LoadFile(d2resource.Scrollbar), d2datadict.Palettes[d2enum.Sky]) +func CreateScrollbar(x, y, height int) Scrollbar { + scrollbarSprite, _ := d2render.LoadSprite(d2resource.Scrollbar, d2resource.PaletteSky) result := Scrollbar{ visible: true, enabled: true, x: x, y: y, height: height, - scrollbarSprite: d2render.CreateSpriteFromDC6(dc6), + scrollbarSprite: scrollbarSprite, } return result } @@ -76,7 +72,7 @@ func (v Scrollbar) GetLastDirChange() int { return v.lastDirChange } -func (v *Scrollbar) Draw(target *ebiten.Image) { +func (v *Scrollbar) Render(target *ebiten.Image) { if !v.visible || v.maxOffset == 0 { return } @@ -84,31 +80,31 @@ func (v *Scrollbar) Draw(target *ebiten.Image) { if !v.enabled { offset = 2 } - v.scrollbarSprite.MoveTo(v.x, v.y) - v.scrollbarSprite.DrawSegments(target, 1, 1, 0+offset) - v.scrollbarSprite.MoveTo(v.x, v.y+v.height-10) - v.scrollbarSprite.DrawSegments(target, 1, 1, 1+offset) + v.scrollbarSprite.SetPosition(v.x, v.y) + v.scrollbarSprite.RenderSegmented(target, 1, 1, 0+offset) + v.scrollbarSprite.SetPosition(v.x, v.y+v.height-10) + v.scrollbarSprite.RenderSegmented(target, 1, 1, 1+offset) if v.maxOffset == 0 || v.currentOffset < 0 || v.currentOffset > v.maxOffset { return } - v.scrollbarSprite.MoveTo(v.x, v.y+10+v.getBarPosition()) + v.scrollbarSprite.SetPosition(v.x, v.y+10+v.getBarPosition()) offset = 0 if !v.enabled { offset = 1 } - v.scrollbarSprite.DrawSegments(target, 1, 1, 4+offset) + v.scrollbarSprite.RenderSegmented(target, 1, 1, 4+offset) } -func (v *Scrollbar) GetSize() (width, height uint32) { - return 10, uint32(v.height) +func (v *Scrollbar) GetSize() (width, height int) { + return 10, int(v.height) } -func (v *Scrollbar) MoveTo(x, y int) { +func (v *Scrollbar) SetPosition(x, y int) { v.x = x v.y = y } -func (v *Scrollbar) GetLocation() (x, y int) { +func (v *Scrollbar) GetPosition() (x, y int) { return v.x, v.y } diff --git a/d2render/d2ui/button.go b/d2render/d2ui/button.go index 1db92db1..8f5221bc 100644 --- a/d2render/d2ui/button.go +++ b/d2render/d2ui/button.go @@ -1,21 +1,14 @@ package d2ui import ( - "github.com/OpenDiablo2/D2Shared/d2data/d2dc6" "image" "image/color" "github.com/OpenDiablo2/D2Shared/d2common/d2resource" "github.com/OpenDiablo2/OpenDiablo2/d2corehelper" - - "github.com/OpenDiablo2/D2Shared/d2data/d2datadict" "github.com/OpenDiablo2/OpenDiablo2/d2render" - "github.com/OpenDiablo2/D2Shared/d2common/d2interface" - - "github.com/OpenDiablo2/D2Shared/d2common/d2enum" - "github.com/hajimehoshi/ebiten" ) @@ -50,26 +43,26 @@ const ( // ButtonLayout defines the type of buttons type ButtonLayout struct { - XSegments int //1 - YSegments int // 1 - ResourceName string // Font Name - PaletteName d2enum.PaletteType // PaletteType - Toggleable bool // false - BaseFrame int // 0 - DisabledFrame int // -1 - FontPath string // ResourcePaths.FontExocet10 - ClickableRect *image.Rectangle // nil - AllowFrameChange bool // true - TextOffset int // 0 + XSegments int //1 + YSegments int // 1 + ResourceName string // Font Name + PaletteName string // PaletteType + Toggleable bool // false + BaseFrame int // 0 + DisabledFrame int // -1 + FontPath string // ResourcePaths.FontExocet10 + ClickableRect *image.Rectangle // nil + AllowFrameChange bool // true + TextOffset int // 0 } // ButtonLayouts define the type of buttons you can have var ButtonLayouts = map[ButtonType]ButtonLayout{ - ButtonTypeWide: {2, 1, d2resource.WideButtonBlank, d2enum.Units, false, 0, -1, d2resource.FontExocet10, nil, true, 1}, - ButtonTypeShort: {1, 1, d2resource.ShortButtonBlank, d2enum.Units, false, 0, -1, d2resource.FontRediculous, nil, true, -1}, - ButtonTypeMedium: {1, 1, d2resource.MediumButtonBlank, d2enum.Units, false, 0, 0, d2resource.FontExocet10, nil, true, 0}, - ButtonTypeTall: {1, 1, d2resource.TallButtonBlank, d2enum.Units, false, 0, 0, d2resource.FontExocet10, nil, true, 5}, - ButtonTypeOkCancel: {1, 1, d2resource.CancelButton, d2enum.Units, false, 0, -1, d2resource.FontRediculous, nil, true, 0}, + ButtonTypeWide: {2, 1, d2resource.WideButtonBlank, d2resource.PaletteUnits, false, 0, -1, d2resource.FontExocet10, nil, true, 1}, + ButtonTypeShort: {1, 1, d2resource.ShortButtonBlank, d2resource.PaletteUnits, false, 0, -1, d2resource.FontRediculous, nil, true, -1}, + ButtonTypeMedium: {1, 1, d2resource.MediumButtonBlank, d2resource.PaletteUnits, false, 0, 0, d2resource.FontExocet10, nil, true, 0}, + ButtonTypeTall: {1, 1, d2resource.TallButtonBlank, d2resource.PaletteUnits, false, 0, 0, d2resource.FontExocet10, nil, true, 5}, + ButtonTypeOkCancel: {1, 1, d2resource.CancelButton, d2resource.PaletteUnits, false, 0, -1, d2resource.FontRediculous, nil, true, 0}, /* {eButtonType.Wide, new ButtonLayout { XSegments = 2, ResourceName = ResourcePaths.WideButtonBlank, PaletteName = PaletteDefs.Units } }, {eButtonType.Narrow, new ButtonLayout { ResourceName = ResourcePaths.NarrowButtonBlank, PaletteName = PaletteDefs.Units } }, @@ -96,11 +89,10 @@ var ButtonLayouts = map[ButtonType]ButtonLayout{ type Button struct { enabled bool x, y int - width, height uint32 + width, height int visible bool pressed bool toggled bool - fileProvider d2interface.FileProvider normalImage *ebiten.Image pressedImage *ebiten.Image toggledImage *ebiten.Image @@ -111,59 +103,57 @@ type Button struct { } // CreateButton creates an instance of Button -func CreateButton(buttonType ButtonType, fileProvider d2interface.FileProvider, text string) Button { +func CreateButton(buttonType ButtonType, text string) Button { result := Button{ - fileProvider: fileProvider, - width: 0, - height: 0, - visible: true, - enabled: true, - pressed: false, + width: 0, + height: 0, + visible: true, + enabled: true, + pressed: false, } buttonLayout := ButtonLayouts[buttonType] result.buttonLayout = buttonLayout - font := GetFont(buttonLayout.FontPath, d2enum.Units, fileProvider) + font := GetFont(buttonLayout.FontPath, d2resource.PaletteUnits) - dc6, _ := d2dc6.LoadDC6(fileProvider.LoadFile(buttonLayout.ResourceName), d2datadict.Palettes[buttonLayout.PaletteName]) - buttonSprite := d2render.CreateSpriteFromDC6(dc6) - totalButtonTypes := buttonSprite.GetTotalFrames() / (buttonLayout.XSegments * buttonLayout.YSegments) + buttonSprite, _ := d2render.LoadSprite(buttonLayout.ResourceName, buttonLayout.PaletteName) + totalButtonTypes := buttonSprite.GetFrameCount() / (buttonLayout.XSegments * buttonLayout.YSegments) for i := 0; i < buttonLayout.XSegments; i++ { - w, _ := buttonSprite.GetFrameSize(i) + w, _, _ := buttonSprite.GetFrameSize(i) result.width += w } for i := 0; i < buttonLayout.YSegments; i++ { - _, h := buttonSprite.GetFrameSize(i * buttonLayout.YSegments) + _, h, _ := buttonSprite.GetFrameSize(i * buttonLayout.YSegments) result.height += h } result.normalImage, _ = ebiten.NewImage(int(result.width), int(result.height), ebiten.FilterNearest) _, fontHeight := font.GetTextMetrics(text) - textY := int((result.height/2)-(fontHeight/2)) + buttonLayout.TextOffset + textY := int((result.height/2)-(int(fontHeight)/2)) + buttonLayout.TextOffset - buttonSprite.MoveTo(0, 0) - buttonSprite.Blend = true - buttonSprite.DrawSegments(result.normalImage, buttonLayout.XSegments, buttonLayout.YSegments, buttonLayout.BaseFrame) - font.Draw(0, textY, text, color.RGBA{100, 100, 100, 255}, result.normalImage) + buttonSprite.SetPosition(0, 0) + buttonSprite.SetBlend(true) + buttonSprite.RenderSegmented(result.normalImage, buttonLayout.XSegments, buttonLayout.YSegments, buttonLayout.BaseFrame) + font.Render(0, textY, text, color.RGBA{100, 100, 100, 255}, result.normalImage) if buttonLayout.AllowFrameChange { if totalButtonTypes > 1 { result.pressedImage, _ = ebiten.NewImage(int(result.width), int(result.height), ebiten.FilterNearest) - buttonSprite.DrawSegments(result.pressedImage, buttonLayout.XSegments, buttonLayout.YSegments, buttonLayout.BaseFrame+1) - font.Draw(-2, textY+2, text, color.RGBA{100, 100, 100, 255}, result.pressedImage) + buttonSprite.RenderSegmented(result.pressedImage, buttonLayout.XSegments, buttonLayout.YSegments, buttonLayout.BaseFrame+1) + font.Render(-2, textY+2, text, color.RGBA{100, 100, 100, 255}, result.pressedImage) } if totalButtonTypes > 2 { result.toggledImage, _ = ebiten.NewImage(int(result.width), int(result.height), ebiten.FilterNearest) - buttonSprite.DrawSegments(result.toggledImage, buttonLayout.XSegments, buttonLayout.YSegments, buttonLayout.BaseFrame+2) - font.Draw(0, textY, text, color.RGBA{100, 100, 100, 255}, result.toggledImage) + buttonSprite.RenderSegmented(result.toggledImage, buttonLayout.XSegments, buttonLayout.YSegments, buttonLayout.BaseFrame+2) + font.Render(0, textY, text, color.RGBA{100, 100, 100, 255}, result.toggledImage) } if totalButtonTypes > 3 { result.pressedToggledImage, _ = ebiten.NewImage(int(result.width), int(result.height), ebiten.FilterNearest) - buttonSprite.DrawSegments(result.pressedToggledImage, buttonLayout.XSegments, buttonLayout.YSegments, buttonLayout.BaseFrame+3) - font.Draw(0, textY, text, color.RGBA{100, 100, 100, 255}, result.pressedToggledImage) + buttonSprite.RenderSegmented(result.pressedToggledImage, buttonLayout.XSegments, buttonLayout.YSegments, buttonLayout.BaseFrame+3) + font.Render(0, textY, text, color.RGBA{100, 100, 100, 255}, result.pressedToggledImage) } if buttonLayout.DisabledFrame != -1 { result.disabledImage, _ = ebiten.NewImage(int(result.width), int(result.height), ebiten.FilterNearest) - buttonSprite.DrawSegments(result.disabledImage, buttonLayout.XSegments, buttonLayout.YSegments, buttonLayout.DisabledFrame) - font.Draw(0, textY, text, color.RGBA{100, 100, 100, 255}, result.disabledImage) + buttonSprite.RenderSegmented(result.disabledImage, buttonLayout.XSegments, buttonLayout.YSegments, buttonLayout.DisabledFrame) + font.Render(0, textY, text, color.RGBA{100, 100, 100, 255}, result.disabledImage) } } return result @@ -182,8 +172,8 @@ func (v *Button) Activate() { v.onClick() } -// Draw renders the button -func (v Button) Draw(target *ebiten.Image) { +// Render renders the button +func (v Button) Render(target *ebiten.Image) { opts := &ebiten.DrawImageOptions{ CompositeMode: ebiten.CompositeModeSourceAtop, Filter: ebiten.FilterNearest, @@ -216,18 +206,18 @@ func (v *Button) SetEnabled(enabled bool) { } // GetSize returns the size of the button -func (v Button) GetSize() (uint32, uint32) { +func (v Button) GetSize() (int, int) { return v.width, v.height } -// MoveTo moves the button -func (v *Button) MoveTo(x, y int) { +// SetPosition moves the button +func (v *Button) SetPosition(x, y int) { v.x = x v.y = y } -// GetLocation returns the location of the button -func (v Button) GetLocation() (x, y int) { +// GetPosition returns the location of the button +func (v Button) GetPosition() (x, y int) { return v.x, v.y } diff --git a/d2render/d2ui/checkbox.go b/d2render/d2ui/checkbox.go index 918c94fd..e17474a3 100644 --- a/d2render/d2ui/checkbox.go +++ b/d2render/d2ui/checkbox.go @@ -1,11 +1,7 @@ package d2ui import ( - "github.com/OpenDiablo2/D2Shared/d2common/d2enum" - "github.com/OpenDiablo2/D2Shared/d2common/d2interface" "github.com/OpenDiablo2/D2Shared/d2common/d2resource" - "github.com/OpenDiablo2/D2Shared/d2data/d2datadict" - "github.com/OpenDiablo2/D2Shared/d2data/d2dc6" "github.com/OpenDiablo2/OpenDiablo2/d2render" "github.com/hajimehoshi/ebiten" ) @@ -14,14 +10,14 @@ type Checkbox struct { x, y int checkState bool visible bool - width, height uint32 + width, height int Image *ebiten.Image checkedImage *ebiten.Image onClick func() enabled bool } -func CreateCheckbox(fileProvider d2interface.FileProvider, checkState bool) Checkbox { +func CreateCheckbox(checkState bool) Checkbox { result := Checkbox{ checkState: checkState, visible: true, @@ -29,20 +25,19 @@ func CreateCheckbox(fileProvider d2interface.FileProvider, checkState bool) Chec height: 0, enabled: true, } - dc6, _ := d2dc6.LoadDC6(fileProvider.LoadFile(d2resource.Checkbox), d2datadict.Palettes[d2enum.Fechar]) - checkboxSprite := d2render.CreateSpriteFromDC6(dc6) - result.width, result.height = checkboxSprite.GetFrameSize(0) - checkboxSprite.MoveTo(0, 0) + checkboxSprite, _ := d2render.LoadSprite(d2resource.Checkbox, d2resource.PaletteFechar) + result.width, result.height, _ = checkboxSprite.GetFrameSize(0) + checkboxSprite.SetPosition(0, 0) result.Image, _ = ebiten.NewImage(int(result.width), int(result.height), ebiten.FilterNearest) - checkboxSprite.DrawSegments(result.Image, 1, 1, 0) + checkboxSprite.RenderSegmented(result.Image, 1, 1, 0) result.checkedImage, _ = ebiten.NewImage(int(result.width), int(result.height), ebiten.FilterNearest) - checkboxSprite.DrawSegments(result.checkedImage, 1, 1, 1) + checkboxSprite.RenderSegmented(result.checkedImage, 1, 1, 1) return result } -func (v Checkbox) Draw(target *ebiten.Image) { +func (v Checkbox) Render(target *ebiten.Image) { opts := &ebiten.DrawImageOptions{ CompositeMode: ebiten.CompositeModeSourceAtop, Filter: ebiten.FilterNearest, @@ -89,11 +84,11 @@ func (v *Checkbox) Activate() { v.onClick() } -func (v Checkbox) GetLocation() (int, int) { +func (v Checkbox) GetPosition() (int, int) { return v.x, v.y } -func (v Checkbox) GetSize() (uint32, uint32) { +func (v Checkbox) GetSize() (int, int) { return v.width, v.height } @@ -101,7 +96,7 @@ func (v Checkbox) GetVisible() bool { return v.visible } -func (v *Checkbox) MoveTo(x int, y int) { +func (v *Checkbox) SetPosition(x int, y int) { v.x = x v.y = y } diff --git a/d2render/d2ui/font.go b/d2render/d2ui/font.go index bca19476..b3cc16ee 100644 --- a/d2render/d2ui/font.go +++ b/d2render/d2ui/font.go @@ -1,18 +1,12 @@ package d2ui import ( - "github.com/OpenDiablo2/D2Shared/d2data/d2dc6" "image/color" "strings" "github.com/OpenDiablo2/D2Shared/d2helper" - "github.com/OpenDiablo2/D2Shared/d2data/d2datadict" - - "github.com/OpenDiablo2/D2Shared/d2common/d2interface" - - "github.com/OpenDiablo2/D2Shared/d2common/d2enum" - + "github.com/OpenDiablo2/OpenDiablo2/d2asset" "github.com/OpenDiablo2/OpenDiablo2/d2render" "github.com/hajimehoshi/ebiten" @@ -32,33 +26,32 @@ type FontSize struct { // Font represents a font type Font struct { - fontSprite d2render.Sprite + fontSprite *d2render.Sprite fontTable map[uint16]uint16 metrics map[uint16]FontSize } // GetFont creates or loads an existing font -func GetFont(font string, palette d2enum.PaletteType, fileProvider d2interface.FileProvider) *Font { - cacheItem, exists := fontCache[font+"_"+string(palette)] +func GetFont(fontPath string, palettePath string) *Font { + cacheItem, exists := fontCache[fontPath+"_"+palettePath] if exists { return cacheItem } - newFont := CreateFont(font, palette, fileProvider) - fontCache[font+"_"+string(palette)] = newFont + newFont := CreateFont(fontPath, palettePath) + fontCache[fontPath+"_"+palettePath] = newFont return newFont } // CreateFont creates an instance of a MPQ Font -func CreateFont(font string, palette d2enum.PaletteType, fileProvider d2interface.FileProvider) *Font { +func CreateFont(font string, palettePath string) *Font { result := &Font{ fontTable: make(map[uint16]uint16), metrics: make(map[uint16]FontSize), } // bug: performance issue when using CJK fonts, because ten thousand frames will be rendered PER font - dc6, _ := d2dc6.LoadDC6(fileProvider.LoadFile(font+".dc6"), d2datadict.Palettes[palette]) - result.fontSprite = d2render.CreateSpriteFromDC6(dc6) + result.fontSprite, _ = d2render.LoadSprite(font+".dc6", palettePath) woo := "Woo!\x01" - fontData := fileProvider.LoadFile(font + ".tbl") + fontData := d2asset.MustLoadFile(font + ".tbl") if string(fontData[0:5]) != woo { panic("No woo :(") } @@ -89,18 +82,14 @@ func CreateFont(font string, palette d2enum.PaletteType, fileProvider d2interfac } // GetTextMetrics returns the size of the specified text -func (v *Font) GetTextMetrics(text string) (width, height uint32) { - width = uint32(0) - curWidth := uint32(0) - height = uint32(0) - maxCharHeight := uint32(0) - // todo: it can be saved as a struct member, since it only depends on `.Frames` - for _, m := range v.fontSprite.Frames { - maxCharHeight = d2helper.Max(maxCharHeight, uint32(m.Height)) - } +func (v *Font) GetTextMetrics(text string) (width, height int) { + width = int(0) + curWidth := int(0) + height = int(0) + _, maxCharHeight := v.fontSprite.GetFrameBounds() for _, ch := range text { if ch == '\n' { - width = d2helper.Max(width, curWidth) + width = d2helper.MaxInt(width, curWidth) curWidth = 0 height += maxCharHeight + 6 continue @@ -108,15 +97,15 @@ func (v *Font) GetTextMetrics(text string) (width, height uint32) { curWidth += v.getCharWidth(ch) } - width = d2helper.Max(width, curWidth) + width = d2helper.MaxInt(width, curWidth) height += maxCharHeight return } -// Draw draws the font on the target surface -func (v *Font) Draw(x, y int, text string, color color.Color, target *ebiten.Image) { - v.fontSprite.ColorMod = color - v.fontSprite.Blend = false +// Render draws the font on the target surface +func (v *Font) Render(x, y int, text string, color color.Color, target *ebiten.Image) { + v.fontSprite.SetColorMod(color) + v.fontSprite.SetBlend(false) maxCharHeight := uint32(0) for _, m := range v.metrics { @@ -132,9 +121,10 @@ func (v *Font) Draw(x, y int, text string, color color.Color, target *ebiten.Ima for _, ch := range line { width := v.getCharWidth(ch) index := v.fontTable[uint16(ch)] - v.fontSprite.Frame = int16(index) - v.fontSprite.MoveTo(xPos, y+int(v.fontSprite.Frames[index].Height)) - v.fontSprite.Draw(target) + v.fontSprite.SetCurrentFrame(int(index)) + _, height := v.fontSprite.GetCurrentFrameSize() + v.fontSprite.SetPosition(xPos, y+int(height)) + v.fontSprite.Render(target) xPos += int(width) } @@ -147,9 +137,9 @@ func (v *Font) Draw(x, y int, text string, color color.Color, target *ebiten.Ima } } -func (v *Font) getCharWidth(char rune) (width uint32) { +func (v *Font) getCharWidth(char rune) (width int) { if char < unicode.MaxLatin1 { - return uint32(v.metrics[uint16(char)].Width) + return int(v.metrics[uint16(char)].Width) } - return uint32(v.metrics[unicode.MaxLatin1].Width) + return int(v.metrics[unicode.MaxLatin1].Width) } diff --git a/d2render/d2ui/label.go b/d2render/d2ui/label.go index 839229b6..c1ecd73e 100644 --- a/d2render/d2ui/label.go +++ b/d2render/d2ui/label.go @@ -3,10 +3,6 @@ package d2ui import ( "image/color" - "github.com/OpenDiablo2/D2Shared/d2common/d2interface" - - "github.com/OpenDiablo2/D2Shared/d2common/d2enum" - "github.com/hajimehoshi/ebiten" ) @@ -27,8 +23,8 @@ type Label struct { text string X int Y int - Width uint32 - Height uint32 + Width int + Height int Alignment LabelAlignment font *Font imageData *ebiten.Image @@ -36,17 +32,17 @@ type Label struct { } // CreateLabel creates a new instance of a UI label -func CreateLabel(provider d2interface.FileProvider, font string, palette d2enum.PaletteType) Label { +func CreateLabel(fontPath, palettePath string) Label { result := Label{ Alignment: LabelAlignLeft, Color: color.White, - font: GetFont(font, palette, provider), + font: GetFont(fontPath, palettePath), } return result } -// Draw draws the label on the screen -func (v *Label) Draw(target *ebiten.Image) { +// Render draws the label on the screen +func (v *Label) Render(target *ebiten.Image) { if len(v.text) == 0 { return } @@ -65,13 +61,13 @@ func (v *Label) Draw(target *ebiten.Image) { target.DrawImage(v.imageData, opts) } -// MoveTo moves the label to the specified location -func (v *Label) MoveTo(x, y int) { +// SetPosition moves the label to the specified location +func (v *Label) SetPosition(x, y int) { v.X = x v.Y = y } -func (v *Label) GetTextMetrics(text string) (width, height uint32) { +func (v *Label) GetTextMetrics(text string) (width, height int) { return v.font.GetTextMetrics(text) } @@ -83,7 +79,7 @@ func (v *Label) cacheImage() { v.Width = width v.Height = height v.imageData, _ = ebiten.NewImage(int(width), int(height), ebiten.FilterNearest) - v.font.Draw(0, 0, v.text, v.Color, v.imageData) + v.font.Render(0, 0, v.text, v.Color, v.imageData) } // SetText sets the label's text @@ -96,7 +92,7 @@ func (v *Label) SetText(newText string) { } // GetSize returns the size of the label -func (v Label) GetSize() (width, height uint32) { +func (v Label) GetSize() (width, height int) { v.cacheImage() width = v.Width height = v.Height diff --git a/d2render/d2ui/manager.go b/d2render/d2ui/manager.go index a6f4d66e..0e0f31fe 100644 --- a/d2render/d2ui/manager.go +++ b/d2render/d2ui/manager.go @@ -1,12 +1,8 @@ package d2ui import ( - "github.com/OpenDiablo2/D2Shared/d2data/d2dc6" - "github.com/OpenDiablo2/OpenDiablo2/d2audio" - "github.com/OpenDiablo2/D2Shared/d2common/d2enum" - "github.com/OpenDiablo2/D2Shared/d2common/d2interface" "github.com/OpenDiablo2/D2Shared/d2common/d2resource" - "github.com/OpenDiablo2/D2Shared/d2data/d2datadict" + "github.com/OpenDiablo2/OpenDiablo2/d2audio" "github.com/OpenDiablo2/OpenDiablo2/d2render" "github.com/hajimehoshi/ebiten" ) @@ -24,7 +20,7 @@ const ( // Manager represents the UI manager type Manager struct { widgets []Widget - cursorSprite d2render.Sprite + cursorSprite *d2render.Sprite cursorButtons CursorButton pressedIndex int CursorX int @@ -34,12 +30,11 @@ type Manager struct { } // CreateManager creates a new instance of a UI manager -func CreateManager(fileProvider d2interface.FileProvider, soundManager d2audio.Manager) *Manager { - dc6, _ := d2dc6.LoadDC6(fileProvider.LoadFile(d2resource.CursorDefault), d2datadict.Palettes[d2enum.Units]) +func CreateManager(soundManager d2audio.Manager) *Manager { + cursorSprite, _ := d2render.LoadSprite(d2resource.CursorDefault, d2resource.PaletteUnits) result := &Manager{ pressedIndex: -1, - widgets: make([]Widget, 0), - cursorSprite: d2render.CreateSpriteFromDC6(dc6), + cursorSprite: cursorSprite, clickSfx: soundManager.LoadSoundEffect(d2resource.SFXButtonClick), waitForLeftMouseUp: false, } @@ -62,18 +57,18 @@ func (v *Manager) WaitForMouseRelease() { v.waitForLeftMouseUp = true } -// Draw renders all of the UI elements -func (v *Manager) Draw(screen *ebiten.Image) { +// Render renders all of the UI elements +func (v *Manager) Render(screen *ebiten.Image) { for _, widget := range v.widgets { if !widget.GetVisible() { continue } - widget.Draw(screen) + widget.Render(screen) } cx, cy := ebiten.CursorPosition() - v.cursorSprite.MoveTo(cx, cy) - v.cursorSprite.Draw(screen) + v.cursorSprite.SetPosition(cx, cy) + v.cursorSprite.Render(screen) } // Update updates all of the UI elements @@ -98,7 +93,7 @@ func (v *Manager) Update() { if !widget.GetVisible() || !widget.GetEnabled() { continue } - wx, wy := widget.GetLocation() + wx, wy := widget.GetPosition() ww, wh := widget.GetSize() if v.CursorX >= wx && v.CursorX <= wx+int(ww) && v.CursorY >= wy && v.CursorY <= wy+int(wh) { widget.SetPressed(true) @@ -125,7 +120,7 @@ func (v *Manager) Update() { } else { if v.pressedIndex > -1 { widget := v.widgets[v.pressedIndex] - wx, wy := widget.GetLocation() + wx, wy := widget.GetPosition() ww, wh := widget.GetSize() if v.CursorX >= wx && v.CursorX <= wx+int(ww) && v.CursorY >= wy && v.CursorY <= wy+int(wh) { widget.Activate() diff --git a/d2render/d2ui/textbox.go b/d2render/d2ui/textbox.go index 334e976d..14906cae 100644 --- a/d2render/d2ui/textbox.go +++ b/d2render/d2ui/textbox.go @@ -1,16 +1,12 @@ package d2ui import ( - "github.com/OpenDiablo2/D2Shared/d2data/d2dc6" "strings" "time" "github.com/hajimehoshi/ebiten/inpututil" - "github.com/OpenDiablo2/D2Shared/d2common/d2enum" - "github.com/OpenDiablo2/D2Shared/d2common/d2interface" "github.com/OpenDiablo2/D2Shared/d2common/d2resource" - "github.com/OpenDiablo2/D2Shared/d2data/d2datadict" "github.com/OpenDiablo2/OpenDiablo2/d2render" "github.com/hajimehoshi/ebiten" ) @@ -22,17 +18,17 @@ type TextBox struct { y int visible bool enabled bool - bgSprite d2render.Sprite + bgSprite *d2render.Sprite textLabel Label lineBar Label } -func CreateTextbox(fileProvider d2interface.FileProvider) TextBox { - dc6, _ := d2dc6.LoadDC6(fileProvider.LoadFile(d2resource.TextBox2), d2datadict.Palettes[d2enum.Units]) +func CreateTextbox() TextBox { + bgSprite, _ := d2render.LoadSprite(d2resource.TextBox2, d2resource.PaletteUnits) result := TextBox{ - bgSprite: d2render.CreateSpriteFromDC6(dc6), - textLabel: CreateLabel(fileProvider, d2resource.FontFormal11, d2enum.Units), - lineBar: CreateLabel(fileProvider, d2resource.FontFormal11, d2enum.Units), + bgSprite: bgSprite, + textLabel: CreateLabel(d2resource.FontFormal11, d2resource.PaletteUnits), + lineBar: CreateLabel(d2resource.FontFormal11, d2resource.PaletteUnits), enabled: true, visible: true, } @@ -55,14 +51,14 @@ func repeatingKeyPressed(key ebiten.Key) bool { return false } -func (v TextBox) Draw(target *ebiten.Image) { +func (v TextBox) Render(target *ebiten.Image) { if !v.visible { return } - v.bgSprite.Draw(target) - v.textLabel.Draw(target) + v.bgSprite.Render(target) + v.textLabel.Render(target) if (time.Now().UnixNano()/1e6)&(1<<8) > 0 { - v.lineBar.Draw(target) + v.lineBar.Render(target) } } @@ -105,25 +101,25 @@ func (v *TextBox) SetText(newText string) { result = result[1:] continue } - v.lineBar.MoveTo(v.x+6+int(tw), v.y+3) + v.lineBar.SetPosition(v.x+6+int(tw), v.y+3) v.textLabel.SetText(result) break } } -func (v TextBox) GetSize() (width, height uint32) { - return v.bgSprite.GetSize() +func (v TextBox) GetSize() (width, height int) { + return v.bgSprite.GetCurrentFrameSize() } -func (v *TextBox) MoveTo(x, y int) { +func (v *TextBox) SetPosition(x, y int) { v.x = x v.y = y - v.textLabel.MoveTo(v.x+6, v.y+3) - v.lineBar.MoveTo(v.x+6+int(v.textLabel.Width), v.y+3) - v.bgSprite.MoveTo(v.x, v.y+26) + v.textLabel.SetPosition(v.x+6, v.y+3) + v.lineBar.SetPosition(v.x+6+int(v.textLabel.Width), v.y+3) + v.bgSprite.SetPosition(v.x, v.y+26) } -func (v TextBox) GetLocation() (x, y int) { +func (v TextBox) GetPosition() (x, y int) { return v.x, v.y } diff --git a/d2render/sprite.go b/d2render/sprite.go index 9b14e553..169c6994 100644 --- a/d2render/sprite.go +++ b/d2render/sprite.go @@ -2,225 +2,179 @@ package d2render import ( "image/color" - "log" - "sync" - "github.com/OpenDiablo2/D2Shared/d2data/d2dc6" "github.com/OpenDiablo2/D2Shared/d2helper" - "github.com/OpenDiablo2/OpenDiablo2/d2corehelper" + "github.com/OpenDiablo2/OpenDiablo2/d2asset" "github.com/hajimehoshi/ebiten" ) -// Sprite represents a type of object in D2 that is comprised of one or more frames and directions type Sprite struct { - Directions uint32 - FramesPerDirection uint32 - Frames []SpriteFrame - SpecialFrameTime int - AnimateBackwards bool // Because why not - StopOnLastFrame bool - X, Y int - Frame, Direction int16 - Blend bool - LastFrameTime float64 - Animate bool - ColorMod color.Color - valid bool + x int + y int + lastFrameTime float64 + animation *d2asset.Animation } -// SpriteFrame represents a single frame of a sprite -type SpriteFrame struct { - Flip uint32 - Width uint32 - Height uint32 - OffsetX int32 - OffsetY int32 - Unknown uint32 - NextBlock uint32 - Length uint32 - FrameData []byte - Image *ebiten.Image -} - -func CreateSpriteFromDC6(dc6 d2dc6.DC6File) Sprite { - result := Sprite{ - X: 50, - Y: 50, - Frame: 0, - Direction: 0, - Blend: false, - ColorMod: nil, - Directions: dc6.Directions, - FramesPerDirection: dc6.FramesPerDirection, - Animate: false, - LastFrameTime: d2helper.Now(), - SpecialFrameTime: -1, - StopOnLastFrame: false, - valid: true, - AnimateBackwards: false, +func LoadSprite(animationPath, palettePath string) (*Sprite, error) { + animation, err := d2asset.LoadAnimation(animationPath, palettePath) + if err != nil { + return nil, err } - result.Frames = make([]SpriteFrame, len(dc6.Frames)) - wg := sync.WaitGroup{} - wg.Add(len(dc6.Frames)) - for i, f := range dc6.Frames { - go func(i int, frame *d2dc6.DC6Frame) { - defer wg.Done() - - image, err := ebiten.NewImage(int(frame.Width), int(frame.Height), ebiten.FilterNearest) - if err != nil { - log.Printf("failed to create new image: %v", err) - } - if err := image.ReplacePixels(frame.ColorData()); err != nil { - log.Printf("failed to replace pixels: %v", err) - } - - result.Frames[i] = SpriteFrame{ - Flip: frame.Flipped, - Width: frame.Width, - Height: frame.Height, - OffsetX: frame.OffsetX, - OffsetY: frame.OffsetY, - Unknown: frame.Unknown, - NextBlock: frame.NextBlock, - Length: frame.Length, - Image: image, - } - }(i, f) - } - wg.Wait() - return result + return &Sprite{lastFrameTime: d2helper.Now(), animation: animation}, nil } -func (v Sprite) IsValid() bool { - return v.valid +func MustLoadSprite(animationPath, palettePath string) *Sprite { + sprite, err := LoadSprite(animationPath, palettePath) + if err != nil { + panic(err) + } + + return sprite } -// GetSize returns the size of the sprite -func (v Sprite) GetSize() (uint32, uint32) { - frame := v.Frames[uint32(v.Frame)+(uint32(v.Direction)*v.FramesPerDirection)] - return frame.Width, frame.Height +func (s *Sprite) Render(target *ebiten.Image) error { + if err := s.advance(); err != nil { + return err + } + + _, frameHeight := s.animation.GetCurrentFrameSize() + + if err := s.animation.Render(target, s.x, s.y-frameHeight); err != nil { + return err + } + + return nil } -func (v *Sprite) updateAnimation() { - if !v.Animate { - return +func (s *Sprite) RenderSegmented(target *ebiten.Image, segmentsX, segmentsY, frameOffset int) error { + if err := s.advance(); err != nil { + return err } - var timePerFrame float64 - if v.SpecialFrameTime >= 0 { - timePerFrame = (float64(v.SpecialFrameTime) / float64(len(v.Frames))) / 1000.0 - } else { - timePerFrame = 1.0 / float64(len(v.Frames)) - } - now := d2helper.Now() - for v.LastFrameTime+timePerFrame < now { - v.LastFrameTime += timePerFrame - if !v.AnimateBackwards { - v.Frame++ - if v.Frame >= int16(v.FramesPerDirection) { - if v.StopOnLastFrame { - v.Frame = int16(v.FramesPerDirection) - 1 - } else { - v.Frame = 0 - } - } - continue - } - v.Frame-- - if v.Frame < 0 { - if v.StopOnLastFrame { - v.Frame = 0 - } else { - v.Frame = int16(v.FramesPerDirection) - 1 + var currentY int + for y := 0; y < segmentsY; y++ { + var currentX int + var maxFrameHeight int + for x := 0; x < segmentsX; x++ { + if err := s.animation.SetCurrentFrame(x + y*segmentsX + frameOffset*segmentsX*segmentsY); err != nil { + return err } + + if err := s.animation.Render(target, s.x+currentX, s.y+currentY); err != nil { + return err + } + + frameWidth, frameHeight := s.GetCurrentFrameSize() + maxFrameHeight = d2helper.MaxInt(maxFrameHeight, frameHeight) + currentX += frameWidth } + + currentY += maxFrameHeight } + + return nil } -func (v *Sprite) ResetAnimation() { - v.LastFrameTime = d2helper.Now() - v.Frame = 0 +func (s *Sprite) SetPosition(x, y int) { + s.x = x + s.y = y } -func (v Sprite) OnLastFrame() bool { - return v.Frame == int16(v.FramesPerDirection-1) +func (s *Sprite) GetPosition() (int, int) { + return s.x, s.y } -// GetFrameSize returns the size of the specific frame -func (v Sprite) GetFrameSize(frame int) (width, height uint32) { - width = v.Frames[frame].Width - height = v.Frames[frame].Height - return +func (s *Sprite) GetFrameSize(frameIndex int) (int, int, error) { + return s.animation.GetFrameSize(frameIndex) } -// GetTotalFrames returns the number of frames in this sprite (for all directions) -func (v Sprite) GetTotalFrames() int { - return len(v.Frames) +func (s *Sprite) GetCurrentFrameSize() (int, int) { + return s.animation.GetCurrentFrameSize() } -// Draw draws the sprite onto the target -func (v *Sprite) Draw(target *ebiten.Image) { - v.updateAnimation() - opts := &ebiten.DrawImageOptions{} - frame := v.Frames[uint32(v.Frame)+(uint32(v.Direction)*v.FramesPerDirection)] - opts.GeoM.Translate( - float64(int32(v.X)+frame.OffsetX), - float64(int32(v.Y)-int32(frame.Height)+frame.OffsetY), - ) - if v.Blend { - opts.CompositeMode = ebiten.CompositeModeLighter - } else { - opts.CompositeMode = ebiten.CompositeModeSourceOver - } - if v.ColorMod != nil { - opts.ColorM = d2corehelper.ColorToColorM(v.ColorMod) - } - if err := target.DrawImage(frame.Image, opts); err != nil { - log.Panic(err.Error()) +func (s *Sprite) GetFrameBounds() (int, int) { + return s.animation.GetFrameBounds() +} + +func (s *Sprite) GetCurrentFrame() int { + return s.animation.GetCurrentFrame() +} + +func (s *Sprite) GetFrameCount() int { + return s.animation.GetFrameCount() +} + +func (s *Sprite) IsOnFirstFrame() bool { + return s.animation.IsOnFirstFrame() +} + +func (s *Sprite) IsOnLastFrame() bool { + return s.animation.IsOnLastFrame() +} + +func (s *Sprite) GetDirectionCount() int { + return s.animation.GetDirectionCount() +} + +func (s *Sprite) SetDirection(directionIndex int) error { + return s.animation.SetDirection(directionIndex) +} + +func (s *Sprite) GetDirection() int { + return s.animation.GetDirection() +} + +func (s *Sprite) SetCurrentFrame(frameIndex int) error { + s.lastFrameTime = d2helper.Now() + return s.animation.SetCurrentFrame(frameIndex) +} + +func (s *Sprite) Rewind() { + s.lastFrameTime = d2helper.Now() + s.animation.SetCurrentFrame(0) +} + +func (s *Sprite) PlayForward() { + s.lastFrameTime = d2helper.Now() + s.animation.PlayForward() +} + +func (s *Sprite) PlayBackward() { + s.lastFrameTime = d2helper.Now() + s.animation.PlayBackward() +} + +func (s *Sprite) Pause() { + s.animation.Pause() +} + +func (s *Sprite) SetPlayLoop(loop bool) { + s.animation.SetPlayLoop(loop) +} + +func (s *Sprite) SetPlayLength(playLength float64) { + s.animation.SetPlayLength(playLength) +} + +func (s *Sprite) SetPlayLengthMs(playLengthMs int) { + s.animation.SetPlayLengthMs(playLengthMs) +} + +func (s *Sprite) SetColorMod(color color.Color) { + s.animation.SetColorMod(color) +} + +func (s *Sprite) SetBlend(blend bool) { + s.animation.SetBlend(blend) +} + +func (s *Sprite) advance() error { + lastFrameTime := d2helper.Now() + if err := s.animation.Advance(lastFrameTime - s.lastFrameTime); err != nil { + return err } -} - -// DrawSegments draws the sprite via a grid of segments -func (v *Sprite) DrawSegments(target *ebiten.Image, xSegments, ySegments, offset int) { - v.updateAnimation() - yOffset := int32(0) - for y := 0; y < ySegments; y++ { - xOffset := int32(0) - biggestYOffset := int32(0) - for x := 0; x < xSegments; x++ { - frame := v.Frames[uint32(x+(y*xSegments)+(offset*xSegments*ySegments))] - opts := &ebiten.DrawImageOptions{} - opts.GeoM.Translate( - float64(int32(v.X)+frame.OffsetX+xOffset), - float64(int32(v.Y)+frame.OffsetY+yOffset), - ) - if v.Blend { - opts.CompositeMode = ebiten.CompositeModeLighter - } else { - opts.CompositeMode = ebiten.CompositeModeSourceOver - } - if v.ColorMod != nil { - opts.ColorM = d2corehelper.ColorToColorM(v.ColorMod) - } - if err := target.DrawImage(frame.Image, opts); err != nil { - log.Panic(err.Error()) - } - xOffset += int32(frame.Width) - biggestYOffset = d2helper.MaxInt32(biggestYOffset, int32(frame.Height)) - } - yOffset += biggestYOffset - } -} - -// MoveTo moves the sprite to the specified coordinates -func (v *Sprite) MoveTo(x, y int) { - v.X = x - v.Y = y -} - -// GetLocation returns the location of the sprite -func (v Sprite) GetLocation() (int, int) { - return v.X, v.Y + s.lastFrameTime = lastFrameTime + return nil } diff --git a/go.mod b/go.mod index 86f97242..37af826a 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/OpenDiablo2/OpenDiablo2 go 1.12 require ( - github.com/OpenDiablo2/D2Shared v0.0.0-20191220005230-1cbc4e1fb658 + github.com/OpenDiablo2/D2Shared v0.0.0-20191222011122-c8cfab029ae6 github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d // indirect github.com/hajimehoshi/ebiten v1.11.0-alpha.1.0.20191219185932-f3712a7e620b diff --git a/go.sum b/go.sum index 2951215f..6036f857 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/JoshVarga/blast v0.0.0-20180421040937-681c804fb9f0 h1:tDnuU0igiBiQFjsvq1Bi7DpoUjqI76VVvW045vpeFeM= github.com/JoshVarga/blast v0.0.0-20180421040937-681c804fb9f0/go.mod h1:h/5OEGj4G+fpYxluLjSMZbFY011ZxAntO98nCl8mrCs= -github.com/OpenDiablo2/D2Shared v0.0.0-20191220005230-1cbc4e1fb658 h1:ZNi/kW/9Lt5vL/XLXeSlAGLcSwq+eqiWLowWfjPXfxw= -github.com/OpenDiablo2/D2Shared v0.0.0-20191220005230-1cbc4e1fb658/go.mod h1:mY8Ll5/iLRAQsaHvIdqSZiHX3aFCys/Q4Sot+xYpero= +github.com/OpenDiablo2/D2Shared v0.0.0-20191222011122-c8cfab029ae6 h1:9TXDUOedNtivxLPiuWMMPxH+aTbKZQv2QBb9lpiZb/8= +github.com/OpenDiablo2/D2Shared v0.0.0-20191222011122-c8cfab029ae6/go.mod h1:mY8Ll5/iLRAQsaHvIdqSZiHX3aFCys/Q4Sot+xYpero= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E= diff --git a/main.go b/main.go index 28dad284..bb86b582 100644 --- a/main.go +++ b/main.go @@ -47,9 +47,9 @@ func main() { d2Engine = d2core.CreateEngine() kingpin.Parse() if *region == 0 { - d2Engine.SetNextScene(d2scene.CreateMainMenu(&d2Engine, &d2Engine, d2Engine.UIManager, d2Engine.SoundManager)) + d2Engine.SetNextScene(d2scene.CreateMainMenu(&d2Engine, d2Engine.UIManager, d2Engine.SoundManager)) } else { - d2Engine.SetNextScene(d2scene.CreateMapEngineTest(&d2Engine, &d2Engine, d2Engine.UIManager, d2Engine.SoundManager, *region, *preset)) + d2Engine.SetNextScene(d2scene.CreateMapEngineTest(&d2Engine, d2Engine.UIManager, d2Engine.SoundManager, *region, *preset)) } ebiten.SetCursorVisible(false) ebiten.SetFullscreen(d2Engine.Settings.FullScreen)