1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-11-18 02:16:23 -05:00

High level resource caching; resource cleanup (#264)

* Work on resource loading

* Use new material flag name. (#261)

Update ebiten ref while at it

* Hopefully fix CI (#262)

* Don't try to copy config.json on travis (#263)

I doesn't exist anymore

* Update D2Shared references

* Fix character selection rect

Co-authored-by: Ziemas <ziemas@ziemas.se>
This commit is contained in:
Alex Yatskov 2019-12-21 17:53:18 -08:00 committed by Tim Sarbin
parent 29c5839876
commit 1b03e691b9
38 changed files with 1776 additions and 1200 deletions

255
d2asset/animation.go Normal file
View File

@ -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
}
}

View File

@ -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
}

View File

@ -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
}

149
d2asset/asset_manager.go Normal file
View File

@ -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)
}

View File

@ -1,4 +1,4 @@
package d2core
package d2asset
import (
"errors"

56
d2asset/file_manager.go Normal file
View File

@ -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
}

View File

@ -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
}

302
d2asset/paperdoll.go Normal file
View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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)

View File

@ -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
}

View File

@ -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)
},
}

View File

@ -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]))
}

View File

@ -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 {

View File

@ -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()
},
}

View File

@ -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)
}
}

View File

@ -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)
},
}

View File

@ -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)
}
}

View File

@ -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)

View File

@ -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,

View File

@ -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)

View File

@ -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)
}

View File

@ -1,20 +1,14 @@
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
@ -25,9 +19,8 @@ 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,
}
@ -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)
}

View File

@ -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),
}
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
}

View File

@ -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...)
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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"
)
@ -53,7 +46,7 @@ type ButtonLayout struct {
XSegments int //1
YSegments int // 1
ResourceName string // Font Name
PaletteName d2enum.PaletteType // PaletteType
PaletteName string // PaletteType
Toggleable bool // false
BaseFrame int // 0
DisabledFrame int // -1
@ -65,11 +58,11 @@ type ButtonLayout struct {
// 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,9 +103,8 @@ 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,
@ -122,48 +113,47 @@ func CreateButton(buttonType ButtonType, fileProvider d2interface.FileProvider,
}
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
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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

View File

@ -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()

View File

@ -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
}

View File

@ -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,
}
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)
func LoadSprite(animationPath, palettePath string) (*Sprite, error) {
animation, err := d2asset.LoadAnimation(animationPath, palettePath)
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)
return nil, 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))
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
}
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
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()
}
// 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
}
func (s *Sprite) GetCurrentFrame() int {
return s.animation.GetCurrentFrame()
}
// MoveTo moves the sprite to the specified coordinates
func (v *Sprite) MoveTo(x, y int) {
v.X = x
v.Y = y
func (s *Sprite) GetFrameCount() int {
return s.animation.GetFrameCount()
}
// GetLocation returns the location of the sprite
func (v Sprite) GetLocation() (int, int) {
return v.X, v.Y
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
}
s.lastFrameTime = lastFrameTime
return nil
}

2
go.mod
View File

@ -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

4
go.sum
View File

@ -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=

View File

@ -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)