diff --git a/d2app/app.go b/d2app/app.go index 1a006efa..d5177355 100644 --- a/d2app/app.go +++ b/d2app/app.go @@ -60,6 +60,7 @@ type App struct { captureFrames []*image.RGBA gitBranch string gitCommit string + asset *d2asset.AssetManager inputManager d2interface.InputManager terminal d2interface.Terminal scriptEngine *d2script.ScriptEngine @@ -89,8 +90,9 @@ func Create(gitBranch, gitCommit string, scriptEngine *d2script.ScriptEngine, audio d2interface.AudioProvider, renderer d2interface.Renderer, + asset *d2asset.AssetManager, ) *App { - uiManager := d2ui.NewUIManager(renderer, inputManager, audio) + uiManager := d2ui.NewUIManager(asset, renderer, inputManager, audio) screenManager := d2screen.NewScreenManager(uiManager) result := &App{ @@ -103,6 +105,7 @@ func Create(gitBranch, gitCommit string, renderer: renderer, ui: uiManager, screen: screenManager, + asset: asset, tAllocSamples: createZeroedRing(nSamplesTAlloc), } @@ -174,11 +177,7 @@ func (a *App) initialize() error { } } - if err := d2asset.Initialize(a.renderer, a.terminal); err != nil { - return err - } - - if err := d2gui.Initialize(a.inputManager); err != nil { + if err := d2gui.Initialize(a.asset, a.inputManager); err != nil { return err } @@ -208,7 +207,7 @@ func (a *App) loadStrings() error { } for _, tablePath := range tablePaths { - data, err := d2asset.LoadFile(tablePath) + data, err := a.asset.LoadFile(tablePath) if err != nil { return err } @@ -298,7 +297,7 @@ func (a *App) loadDataDict() error { d2datadict.InitObjectRecords() for _, entry := range entries { - data, err := d2asset.LoadFile(entry.path) + data, err := a.asset.LoadFile(entry.path) if err != nil { return err } @@ -678,42 +677,51 @@ func updateInitError(target d2interface.Surface) error { // ToMainMenu forces the game to transition to the Main Menu func (a *App) ToMainMenu() { buildInfo := d2gamescreen.BuildInfo{Branch: a.gitBranch, Commit: a.gitCommit} - mainMenu := d2gamescreen.CreateMainMenu(a, a.renderer, a.inputManager, a.audio, a.ui, buildInfo) + + mainMenu := d2gamescreen.CreateMainMenu(a, a.asset, a.renderer, a.inputManager, a.audio, a.ui, + buildInfo) + a.screen.SetNextScreen(mainMenu) } // ToSelectHero forces the game to transition to the Select Hero (create character) screen func (a *App) ToSelectHero(connType d2clientconnectiontype.ClientConnectionType, host string) { - selectHero := d2gamescreen.CreateSelectHeroClass(a, a.renderer, a.audio, a.ui, connType, host) + selectHero := d2gamescreen.CreateSelectHeroClass(a, a.asset, a.renderer, a.audio, a.ui, + connType, host) a.screen.SetNextScreen(selectHero) } // ToCreateGame forces the game to transition to the Create Game screen func (a *App) ToCreateGame(filePath string, connType d2clientconnectiontype.ClientConnectionType, host string) { - gameClient, _ := d2client.Create(connType, a.scriptEngine) + gameClient, _ := d2client.Create(connType, a.asset, a.scriptEngine) if err := gameClient.Open(host, filePath); err != nil { // TODO an error screen should be shown in this case fmt.Printf("can not connect to the host: %s", host) } - a.screen.SetNextScreen(d2gamescreen.CreateGame(a, a.renderer, a.inputManager, a.audio, gameClient, a.terminal)) + a.screen.SetNextScreen(d2gamescreen.CreateGame(a, a.asset, a.ui, a.renderer, a.inputManager, + a.audio, gameClient, a.terminal)) } // ToCharacterSelect forces the game to transition to the Character Select (load character) screen func (a *App) ToCharacterSelect(connType d2clientconnectiontype.ClientConnectionType, connHost string) { - characterSelect := d2gamescreen.CreateCharacterSelect(a, a.renderer, a.inputManager, a.audio, a.ui, connType, connHost) + + characterSelect := d2gamescreen.CreateCharacterSelect(a, a.asset, a.renderer, a.inputManager, + a.audio, a.ui, connType, connHost) + a.screen.SetNextScreen(characterSelect) } // ToMapEngineTest forces the game to transition to the map engine test screen func (a *App) ToMapEngineTest(region, level int) { - met := d2gamescreen.CreateMapEngineTest(region, level, a.terminal, a.renderer, a.inputManager, + met := d2gamescreen.CreateMapEngineTest(region, level, a.asset, a.terminal, a.renderer, + a.inputManager, a.audio, a.screen) a.screen.SetNextScreen(met) } // ToCredits forces the game to transition to the credits screen func (a *App) ToCredits() { - a.screen.SetNextScreen(d2gamescreen.CreateCredits(a, a.renderer, a.ui)) + a.screen.SetNextScreen(d2gamescreen.CreateCredits(a, a.asset, a.renderer, a.ui)) } diff --git a/d2core/d2asset/animation_manager.go b/d2core/d2asset/animation_manager.go index 9828eed7..c7ba2513 100644 --- a/d2core/d2asset/animation_manager.go +++ b/d2core/d2asset/animation_manager.go @@ -5,8 +5,9 @@ import ( "path/filepath" "strings" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2cache" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dc6" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dcc" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" ) @@ -19,6 +20,7 @@ var _ d2interface.AnimationManager = &animationManager{} var _ d2interface.Cacher = &animationManager{} type animationManager struct { + *AssetManager cache d2interface.Cache renderer d2interface.Renderer } @@ -31,13 +33,6 @@ func (am *animationManager) GetCache() d2interface.Cache { return am.cache } -func createAnimationManager(renderer d2interface.Renderer) *animationManager { - return &animationManager{ - renderer: renderer, - cache: d2cache.CreateCache(animationBudget), - } -} - func (am *animationManager) LoadAnimation( animationPath, palettePath string, effect d2enum.DrawEffect) (d2interface.Animation, error) { @@ -51,22 +46,22 @@ func (am *animationManager) LoadAnimation( ext := strings.ToLower(filepath.Ext(animationPath)) switch ext { case ".dc6": - palette, err := LoadPalette(palettePath) + palette, err := am.LoadPalette(palettePath) if err != nil { return nil, err } - animation, err = CreateDC6Animation(am.renderer, animationPath, palette, d2enum.DrawEffectNone) + animation, err = am.CreateDC6Animation(animationPath, palette, d2enum.DrawEffectNone) if err != nil { return nil, err } case ".dcc": - palette, err := LoadPalette(palettePath) + palette, err := am.LoadPalette(palettePath) if err != nil { return nil, err } - animation, err = CreateDCCAnimation(am.renderer, animationPath, palette, effect) + animation, err = am.CreateDCCAnimation(animationPath, palette, effect) if err != nil { return nil, err } @@ -80,3 +75,85 @@ func (am *animationManager) LoadAnimation( return animation, nil } + +// CreateDC6Animation creates an Animation from d2dc6.DC6 and d2dat.DATPalette +func (am *animationManager) CreateDC6Animation(dc6Path string, + palette d2interface.Palette, effect d2enum.DrawEffect) (d2interface.Animation, error) { + dc6, err := am.loadDC6(dc6Path) + if err != nil { + return nil, err + } + + anim := DC6Animation{ + animation: animation{ + directions: make([]animationDirection, dc6.Directions), + playLength: defaultPlayLength, + playLoop: true, + originAtBottom: true, + effect: effect, + }, + dc6Path: dc6Path, + dc6: dc6, + palette: palette, + renderer: am.renderer, + } + + err = anim.SetDirection(0) + + return &anim, err +} + +// CreateDCCAnimation creates an animation from d2dcc.DCC and d2dat.DATPalette +func (am *animationManager) CreateDCCAnimation(dccPath string, + palette d2interface.Palette, + effect d2enum.DrawEffect) (d2interface.Animation, error) { + dcc, err := am.loadDCC(dccPath) + if err != nil { + return nil, err + } + + anim := animation{ + playLength: defaultPlayLength, + playLoop: true, + directions: make([]animationDirection, dcc.NumberOfDirections), + effect: effect, + } + + DCC := DCCAnimation{ + animation: anim, + animationManager: am, + dccPath: dccPath, + palette: palette, + renderer: am.renderer, + } + + err = DCC.SetDirection(0) + if err != nil { + return nil, err + } + + return &DCC, nil +} + +func (am *animationManager) loadDC6(path string) (*d2dc6.DC6, error) { + dc6Data, err := am.LoadFile(path) + if err != nil { + return nil, err + } + + dc6, err := d2dc6.Load(dc6Data) + if err != nil { + return nil, err + } + + return dc6, nil +} + +func (am *animationManager) loadDCC(path string) (*d2dcc.DCC, error) { + dccData, err := am.LoadFile(path) + if err != nil { + return nil, err + } + + return d2dcc.Load(dccData) +} diff --git a/d2core/d2asset/archive_manager.go b/d2core/d2asset/archive_manager.go index 7ca0bed1..210de0f9 100644 --- a/d2core/d2asset/archive_manager.go +++ b/d2core/d2asset/archive_manager.go @@ -5,7 +5,6 @@ import ( "path" "sync" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2cache" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2mpq" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2config" @@ -16,6 +15,7 @@ var _ d2interface.ArchiveManager = &archiveManager{} var _ d2interface.Cacher = &archiveManager{} type archiveManager struct { + *AssetManager cache d2interface.Cache config *d2config.Configuration archives []d2interface.Archive @@ -26,10 +26,6 @@ const ( archiveBudget = 1024 * 1024 * 512 ) -func createArchiveManager(config *d2config.Configuration) d2interface.ArchiveManager { - return &archiveManager{cache: d2cache.CreateCache(archiveBudget), config: config} -} - // LoadArchiveForFile loads the archive for the given (in-archive) file path func (am *archiveManager) LoadArchiveForFile(filePath string) (d2interface.Archive, error) { am.mutex.Lock() diff --git a/d2core/d2asset/archived_file_manager.go b/d2core/d2asset/archived_file_manager.go index 02d2fc9e..4826b3e4 100644 --- a/d2core/d2asset/archived_file_manager.go +++ b/d2core/d2asset/archived_file_manager.go @@ -3,7 +3,6 @@ package d2asset import ( "strings" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2cache" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2config" @@ -18,20 +17,12 @@ var _ d2interface.FileManager = &fileManager{} var _ d2interface.Cacher = &fileManager{} type fileManager struct { + *AssetManager cache d2interface.Cache archiveManager d2interface.ArchiveManager config *d2config.Configuration } -func createFileManager(config *d2config.Configuration, - archiveManager d2interface.ArchiveManager) d2interface.FileManager { - return &fileManager{ - d2cache.CreateCache(fileBudget), - archiveManager, - config, - } -} - // LoadFileStream loads a file as a stream automatically from an archive func (fm *fileManager) LoadFileStream(filePath string) (d2interface.ArchiveDataStream, error) { filePath = fm.fixupFilePath(filePath) diff --git a/d2core/d2asset/asset_manager.go b/d2core/d2asset/asset_manager.go index 3b35b2d4..c338adef 100644 --- a/d2core/d2asset/asset_manager.go +++ b/d2core/d2asset/asset_manager.go @@ -1,13 +1,14 @@ package d2asset import ( - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2cof" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dc6" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dcc" + "log" + + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" ) -type assetManager struct { +// AssetManager loads files and game objects +type AssetManager struct { archiveManager d2interface.ArchiveManager archivedFileManager d2interface.FileManager paletteManager d2interface.PaletteManager @@ -16,39 +17,66 @@ type assetManager struct { fontManager d2interface.FontManager } -func loadDC6(dc6Path string) (*d2dc6.DC6, error) { - dc6Data, err := LoadFile(dc6Path) +// LoadFileStream streams an MPQ file from a source file path +func (am *AssetManager) LoadFileStream(filePath string) (d2interface.ArchiveDataStream, error) { + data, err := am.archivedFileManager.LoadFileStream(filePath) if err != nil { - return nil, err + log.Printf("error loading file stream %s (%v)", filePath, err.Error()) } - dc6, err := d2dc6.Load(dc6Data) - if err != nil { - return nil, err - } - - return dc6, nil + return data, err } -func loadDCC(dccPath string) (*d2dcc.DCC, error) { - dccData, err := LoadFile(dccPath) +// LoadFile loads an entire file from a source file path as a []byte +func (am *AssetManager) LoadFile(filePath string) ([]byte, error) { + data, err := am.archivedFileManager.LoadFile(filePath) if err != nil { - return nil, err + log.Printf("error loading file %s (%v)", filePath, err.Error()) } - return d2dcc.Load(dccData) + return data, err } -func loadCOF(cofPath string) (*d2cof.COF, error) { - cofData, err := LoadFile(cofPath) - if err != nil { - return nil, err +// FileExists checks if a file exists on the underlying file system at the given file path. +func (am *AssetManager) FileExists(filePath string) (bool, error) { + return am.archivedFileManager.FileExists(filePath) +} + +// LoadAnimation loads an animation by its resource path and its palette path +func (am *AssetManager) LoadAnimation(animationPath, palettePath string) (d2interface.Animation, error) { + return am.LoadAnimationWithEffect(animationPath, palettePath, d2enum.DrawEffectNone) +} + +// LoadAnimationWithEffect loads an animation by its resource path and its palette path with a given transparency value +func (am *AssetManager) LoadAnimationWithEffect(animationPath, palettePath string, + drawEffect d2enum.DrawEffect) (d2interface.Animation, error) { + return am.animationManager.LoadAnimation(animationPath, palettePath, drawEffect) +} + +// LoadComposite creates a composite object from a ObjectLookupRecord and palettePath describing it +func (am *AssetManager) LoadComposite(baseType d2enum.ObjectType, token, palettePath string) (*Composite, error) { + c := &Composite{ + AssetManager: am, + baseType: baseType, + basePath: baseString(baseType), + token: token, + palettePath: palettePath, } - return d2cof.Load(cofData) + return c, nil } -func (am *assetManager) BindTerminalCommands(term d2interface.Terminal) error { +// LoadFont loads a font the resource files +func (am *AssetManager) LoadFont(tablePath, spritePath, palettePath string) (d2interface.Font, error) { + return am.fontManager.LoadFont(tablePath, spritePath, palettePath) +} + +// LoadPalette loads a palette from a given palette path +func (am *AssetManager) LoadPalette(palettePath string) (d2interface.Palette, error) { + return am.paletteManager.LoadPalette(palettePath) +} + +func (am *AssetManager) BindTerminalCommands(term d2interface.Terminal) error { if err := term.BindAction("assetspam", "display verbose asset manager logs", func(verbose bool) { if verbose { term.OutputInfof("asset manager verbose logging enabled") diff --git a/d2core/d2asset/composite.go b/d2core/d2asset/composite.go index 16252b05..33e665cf 100644 --- a/d2core/d2asset/composite.go +++ b/d2core/d2asset/composite.go @@ -13,6 +13,7 @@ import ( // Composite is a composite entity animation type Composite struct { + *AssetManager baseType d2enum.ObjectType basePath string token string @@ -28,12 +29,6 @@ type size struct { Height int } -// CreateComposite creates a Composite from a given ObjectLookupRecord and palettePath. -func CreateComposite(baseType d2enum.ObjectType, token, palettePath string) *Composite { - return &Composite{baseType: baseType, basePath: baseString(baseType), - token: token, palettePath: palettePath} -} - // Advance moves the composite animation forward for a given elapsed time in nanoseconds. func (c *Composite) Advance(elapsed float64) error { if c.mode == nil { @@ -233,11 +228,11 @@ type compositeMode struct { func (c *Composite) createMode(animationMode animationMode, weaponClass string) (*compositeMode, error) { cofPath := fmt.Sprintf("%s/%s/COF/%s%s%s.COF", c.basePath, c.token, c.token, animationMode, weaponClass) - if exists, _ := FileExists(cofPath); !exists { + if exists, _ := c.FileExists(cofPath); !exists { return nil, errors.New("composite not found") } - cof, err := loadCOF(cofPath) + cof, err := c.loadCOF(cofPath) if err != nil { return nil, err } @@ -296,8 +291,9 @@ func (c *Composite) loadCompositeLayer(layerKey, layerValue, animationMode, weap } for _, animationPath := range animationPaths { - if exists, _ := FileExists(animationPath); exists { - animation, err := LoadAnimationWithEffect(animationPath, palettePath, drawEffect) + if exists, _ := c.FileExists(animationPath); exists { + animation, err := c.LoadAnimationWithEffect(animationPath, palettePath, + drawEffect) if err == nil { return animation, nil } @@ -345,6 +341,15 @@ func (c *Composite) updateSize() { c.size.Height = biggestH } +func (c *Composite) loadCOF(cofPath string) (*d2cof.COF, error) { + cofData, err := c.LoadFile(cofPath) + if err != nil { + return nil, err + } + + return d2cof.Load(cofData) +} + func baseString(baseType d2enum.ObjectType) string { switch baseType { case d2enum.ObjectTypePlayer: diff --git a/d2core/d2asset/d2asset.go b/d2core/d2asset/d2asset.go index 9679f401..ec30a590 100644 --- a/d2core/d2asset/d2asset.go +++ b/d2core/d2asset/d2asset.go @@ -1,90 +1,51 @@ package d2asset import ( - "log" - - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2cache" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2config" ) -var singleton *assetManager //nolint:gochecknoglobals // Currently global by design +// NewAssetManager creates and assigns all necessary dependencies for the AssetManager top-level functions to work correctly +func NewAssetManager(renderer d2interface.Renderer, + term d2interface.Terminal) (*AssetManager, error) { -// Initialize creates and assigns all necessary dependencies for the assetManager top-level functions to work correctly -func Initialize(renderer d2interface.Renderer, - term d2interface.Terminal) error { - var ( - archiveManager = createArchiveManager(d2config.Config) - archivedFileManager = createFileManager(d2config.Config, archiveManager) - paletteManager = createPaletteManager() - paletteTransformManager = createPaletteTransformManager() - animationManager = createAnimationManager(renderer) - fontManager = createFontManager() - ) + manager := &AssetManager{} - singleton = &assetManager{ - archiveManager, - archivedFileManager, - paletteManager, - paletteTransformManager, - animationManager, - fontManager, + manager.archiveManager = &archiveManager{ + AssetManager: manager, + cache: d2cache.CreateCache(archiveBudget), + config: d2config.Config, } + manager.archivedFileManager = &fileManager{ + manager, + d2cache.CreateCache(fileBudget), + manager.archiveManager, + d2config.Config, + } + + manager.paletteManager = &paletteManager{ + manager, + d2cache.CreateCache(paletteBudget), + } + + manager.paletteTransformManager = &paletteTransformManager{ + manager, + d2cache.CreateCache(paletteTransformBudget), + } + + manager.animationManager = &animationManager{ + AssetManager: manager, + renderer: renderer, + cache: d2cache.CreateCache(animationBudget), + } + + manager.fontManager = &fontManager{manager, d2cache.CreateCache(fontBudget)} + if term != nil { - return singleton.BindTerminalCommands(term) + return manager, manager.BindTerminalCommands(term) } - return nil -} - -// LoadFileStream streams an MPQ file from a source file path -func LoadFileStream(filePath string) (d2interface.ArchiveDataStream, error) { - data, err := singleton.archivedFileManager.LoadFileStream(filePath) - if err != nil { - log.Printf("error loading file stream %s (%v)", filePath, err.Error()) - } - - return data, err -} - -// LoadFile loads an entire file from a source file path as a []byte -func LoadFile(filePath string) ([]byte, error) { - data, err := singleton.archivedFileManager.LoadFile(filePath) - if err != nil { - log.Printf("error loading file %s (%v)", filePath, err.Error()) - } - - return data, err -} - -// FileExists checks if a file exists on the underlying file system at the given file path. -func FileExists(filePath string) (bool, error) { - return singleton.archivedFileManager.FileExists(filePath) -} - -// LoadAnimation loads an animation by its resource path and its palette path -func LoadAnimation(animationPath, palettePath string) (d2interface.Animation, error) { - return LoadAnimationWithEffect(animationPath, palettePath, d2enum.DrawEffectNone) -} - -// LoadAnimationWithEffect loads an animation by its resource path and its palette path with a given transparency value -func LoadAnimationWithEffect(animationPath, palettePath string, - drawEffect d2enum.DrawEffect) (d2interface.Animation, error) { - return singleton.animationManager.LoadAnimation(animationPath, palettePath, drawEffect) -} - -// LoadComposite creates a composite object from a ObjectLookupRecord and palettePath describing it -func LoadComposite(baseType d2enum.ObjectType, token, palettePath string) (*Composite, error) { - return CreateComposite(baseType, token, palettePath), nil -} - -// LoadFont loads a font the resource files -func LoadFont(tablePath, spritePath, palettePath string) (d2interface.Font, error) { - return singleton.fontManager.LoadFont(tablePath, spritePath, palettePath) -} - -// LoadPalette loads a palette from a given palette path -func LoadPalette(palettePath string) (d2interface.Palette, error) { - return singleton.paletteManager.LoadPalette(palettePath) + return manager, nil } diff --git a/d2core/d2asset/dc6_animation.go b/d2core/d2asset/dc6_animation.go index f7fccbb8..69e233d4 100644 --- a/d2core/d2asset/dc6_animation.go +++ b/d2core/d2asset/dc6_animation.go @@ -3,6 +3,8 @@ package d2asset import ( "errors" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dc6" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dcc" d2iface "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" @@ -14,36 +16,11 @@ var _ d2iface.Animation = &DC6Animation{} // Static check to confirm struct conf type DC6Animation struct { animation dc6Path string + dc6 *d2dc6.DC6 palette d2iface.Palette renderer d2iface.Renderer } -// CreateDC6Animation creates an Animation from d2dc6.DC6 and d2dat.DATPalette -func CreateDC6Animation(renderer d2iface.Renderer, dc6Path string, - palette d2iface.Palette, effect d2enum.DrawEffect) (d2iface.Animation, error) { - dc6, err := loadDC6(dc6Path) - if err != nil { - return nil, err - } - - anim := DC6Animation{ - animation: animation{ - directions: make([]animationDirection, dc6.Directions), - playLength: defaultPlayLength, - playLoop: true, - originAtBottom: true, - effect: effect, - }, - dc6Path: dc6Path, - palette: palette, - renderer: renderer, - } - - err = anim.SetDirection(0) - - return &anim, err -} - // SetDirection decodes and sets the direction func (a *DC6Animation) SetDirection(directionIndex int) error { const smallestInvalidDirectionIndex = 64 @@ -66,11 +43,7 @@ func (a *DC6Animation) SetDirection(directionIndex int) error { } func (a *DC6Animation) decodeDirection(directionIndex int) error { - dc6, err := loadDC6(a.dc6Path) - if err != nil { - return err - } - + dc6 := a.dc6 startFrame := directionIndex * int(dc6.FramesPerDirection) for i := 0; i < int(dc6.FramesPerDirection); i++ { diff --git a/d2core/d2asset/dcc_animation.go b/d2core/d2asset/dcc_animation.go index 457b737f..02a7895e 100644 --- a/d2core/d2asset/dcc_animation.go +++ b/d2core/d2asset/dcc_animation.go @@ -16,41 +16,12 @@ var _ d2iface.Animation = &DCCAnimation{} // Static check to confirm struct conf // DCCAnimation represents an animation decoded from DCC type DCCAnimation struct { animation + *animationManager dccPath string palette d2iface.Palette renderer d2iface.Renderer } -// CreateDCCAnimation creates an animation from d2dcc.DCC and d2dat.DATPalette -func CreateDCCAnimation(renderer d2iface.Renderer, dccPath string, palette d2iface.Palette, - effect d2enum.DrawEffect) (d2iface.Animation, error) { - dcc, err := loadDCC(dccPath) - if err != nil { - return nil, err - } - - anim := animation{ - playLength: defaultPlayLength, - playLoop: true, - directions: make([]animationDirection, dcc.NumberOfDirections), - effect: effect, - } - - DCC := DCCAnimation{ - animation: anim, - dccPath: dccPath, - palette: palette, - renderer: renderer, - } - - err = DCC.SetDirection(0) - if err != nil { - return nil, err - } - - return &DCC, nil -} - // Clone creates a copy of the animation func (a *DCCAnimation) Clone() d2iface.Animation { animation := *a @@ -79,7 +50,7 @@ func (a *DCCAnimation) SetDirection(directionIndex int) error { } func (a *DCCAnimation) decodeDirection(directionIndex int) error { - dcc, err := loadDCC(a.dccPath) + dcc, err := a.loadDCC(a.dccPath) if err != nil { return err } diff --git a/d2core/d2asset/font.go b/d2core/d2asset/font.go index 7683828c..b45ace47 100644 --- a/d2core/d2asset/font.go +++ b/d2core/d2asset/font.go @@ -1,8 +1,6 @@ package d2asset import ( - "encoding/binary" - "errors" "image/color" "strings" @@ -25,45 +23,6 @@ type Font struct { color color.Color } -func loadFont(tablePath, spritePath, palettePath string) (d2interface.Font, error) { - sheet, err := LoadAnimation(spritePath, palettePath) - if err != nil { - return nil, err - } - - data, err := LoadFile(tablePath) - if err != nil { - return nil, err - } - - if string(data[:5]) != "Woo!\x01" { - return nil, errors.New("invalid font table format") - } - - _, maxCharHeight := sheet.GetFrameBounds() - - glyphs := make(map[rune]fontGlyph) - - for i := 12; i < len(data); i += 14 { - code := rune(binary.LittleEndian.Uint16(data[i : i+2])) - - var glyph fontGlyph - glyph.frame = int(binary.LittleEndian.Uint16(data[i+8 : i+10])) - glyph.width = int(data[i+3]) - glyph.height = maxCharHeight - - glyphs[code] = glyph - } - - font := &Font{ - sheet: sheet, - glyphs: glyphs, - color: color.White, - } - - return font, nil -} - // SetColor sets the fonts color func (f *Font) SetColor(c color.Color) { f.color = c diff --git a/d2core/d2asset/font_manager.go b/d2core/d2asset/font_manager.go index 9ac28b23..5145a0f8 100644 --- a/d2core/d2asset/font_manager.go +++ b/d2core/d2asset/font_manager.go @@ -1,9 +1,11 @@ package d2asset import ( + "encoding/binary" + "errors" "fmt" + "image/color" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2cache" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" ) @@ -16,13 +18,10 @@ var _ d2interface.FontManager = &fontManager{} var _ d2interface.Cacher = &fontManager{} type fontManager struct { + *AssetManager cache d2interface.Cache } -func createFontManager() d2interface.FontManager { - return &fontManager{d2cache.CreateCache(fontBudget)} -} - // LoadFont loads a font from the archives managed by the ArchiveManager func (fm *fontManager) LoadFont(tablePath, spritePath, palettePath string) (d2interface.Font, error) { @@ -31,7 +30,41 @@ func (fm *fontManager) LoadFont(tablePath, spritePath, palettePath string) (d2in return font.(d2interface.Font), nil } - font, err := loadFont(tablePath, spritePath, palettePath) + sheet, err := fm.LoadAnimation(spritePath, palettePath) + if err != nil { + return nil, err + } + + data, err := fm.LoadFile(tablePath) + if err != nil { + return nil, err + } + + if string(data[:5]) != "Woo!\x01" { + return nil, errors.New("invalid font table format") + } + + _, maxCharHeight := sheet.GetFrameBounds() + + glyphs := make(map[rune]fontGlyph) + + for i := 12; i < len(data); i += 14 { + code := rune(binary.LittleEndian.Uint16(data[i : i+2])) + + var glyph fontGlyph + glyph.frame = int(binary.LittleEndian.Uint16(data[i+8 : i+10])) + glyph.width = int(data[i+3]) + glyph.height = maxCharHeight + + glyphs[code] = glyph + } + + font := &Font{ + sheet: sheet, + glyphs: glyphs, + color: color.White, + } + if err != nil { return nil, err } diff --git a/d2core/d2asset/palette_manager.go b/d2core/d2asset/palette_manager.go index 0c295f20..59f374e0 100644 --- a/d2core/d2asset/palette_manager.go +++ b/d2core/d2asset/palette_manager.go @@ -1,7 +1,6 @@ package d2asset import ( - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2cache" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dat" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" ) @@ -11,6 +10,7 @@ var _ d2interface.PaletteManager = &paletteManager{} var _ d2interface.Cacher = &paletteManager{} type paletteManager struct { + *AssetManager cache d2interface.Cache } @@ -18,17 +18,13 @@ const ( paletteBudget = 64 ) -func createPaletteManager() d2interface.PaletteManager { - return &paletteManager{d2cache.CreateCache(paletteBudget)} -} - // LoadPalette loads a palette from archives managed by the ArchiveManager func (pm *paletteManager) LoadPalette(palettePath string) (d2interface.Palette, error) { if palette, found := pm.cache.Retrieve(palettePath); found { return palette.(d2interface.Palette), nil } - paletteData, err := LoadFile(palettePath) + paletteData, err := pm.LoadFile(palettePath) if err != nil { return nil, err } diff --git a/d2core/d2asset/palette_transform_manager.go b/d2core/d2asset/palette_transform_manager.go index 0cf2269f..82d6d989 100644 --- a/d2core/d2asset/palette_transform_manager.go +++ b/d2core/d2asset/palette_transform_manager.go @@ -1,12 +1,12 @@ package d2asset import ( - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2cache" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2pl2" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" ) type paletteTransformManager struct { + *AssetManager cache d2interface.Cache } @@ -14,16 +14,12 @@ const ( paletteTransformBudget = 64 ) -func createPaletteTransformManager() *paletteTransformManager { - return &paletteTransformManager{d2cache.CreateCache(paletteTransformBudget)} -} - func (pm *paletteTransformManager) loadPaletteTransform(path string) (*d2pl2.PL2, error) { if pl2, found := pm.cache.Retrieve(path); found { return pl2.(*d2pl2.PL2), nil } - data, err := LoadFile(path) + data, err := pm.LoadFile(path) if err != nil { return nil, err } diff --git a/d2core/d2audio/ebiten/ebiten_audio_provider.go b/d2core/d2audio/ebiten/ebiten_audio_provider.go index 214aef23..3e653fa3 100644 --- a/d2core/d2audio/ebiten/ebiten_audio_provider.go +++ b/d2core/d2audio/ebiten/ebiten_audio_provider.go @@ -4,6 +4,7 @@ package ebiten import ( "log" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" @@ -15,18 +16,11 @@ const sampleRate = 44100 var _ d2interface.AudioProvider = &AudioProvider{} // Static check to confirm struct conforms to interface -// AudioProvider represents a provider capable of playing audio -type AudioProvider struct { - audioContext *audio.Context // The Audio context - bgmAudio *audio.Player // The audio player - lastBgm string - sfxVolume float64 - bgmVolume float64 -} - // CreateAudio creates an instance of ebiten's audio provider -func CreateAudio() (*AudioProvider, error) { - result := &AudioProvider{} +func CreateAudio(am *d2asset.AssetManager) (*AudioProvider, error) { + result := &AudioProvider{ + assetManager: am, + } var err error result.audioContext, err = audio.NewContext(sampleRate) @@ -39,6 +33,16 @@ func CreateAudio() (*AudioProvider, error) { return result, nil } +// AudioProvider represents a provider capable of playing audio +type AudioProvider struct { + assetManager *d2asset.AssetManager + audioContext *audio.Context // The Audio context + bgmAudio *audio.Player // The audio player + lastBgm string + sfxVolume float64 + bgmVolume float64 +} + // PlayBGM loads an audio stream and plays it in the background func (eap *AudioProvider) PlayBGM(song string) { if eap.lastBgm == song { @@ -61,7 +65,7 @@ func (eap *AudioProvider) PlayBGM(song string) { } } - audioStream, err := d2asset.LoadFileStream(song) + audioStream, err := eap.assetManager.LoadFileStream(song) if err != nil { panic(err) @@ -103,7 +107,7 @@ func (eap *AudioProvider) LoadSound(sfx string, loop, bgm bool) (d2interface.Sou volume = eap.bgmVolume } - result := CreateSoundEffect(sfx, eap.audioContext, loop) + result := eap.createSoundEffect(sfx, eap.audioContext, loop) result.volumeScale = volume result.SetVolume(volume) @@ -116,3 +120,53 @@ func (eap *AudioProvider) SetVolumes(bgmVolume, sfxVolume float64) { eap.sfxVolume = sfxVolume eap.bgmVolume = bgmVolume } + +// createSoundEffect creates a new instance of ebiten's sound effect implementation. +func (eap *AudioProvider) createSoundEffect(sfx string, context *audio.Context, + loop bool) *SoundEffect { + result := &SoundEffect{} + + soundFile := "/data/global/sfx/" + + if _, exists := d2datadict.Sounds[sfx]; exists { + soundEntry := d2datadict.Sounds[sfx] + soundFile += soundEntry.FileName + } else { + soundFile += sfx + } + + audioData, err := eap.assetManager.LoadFileStream(soundFile) + + if err != nil { + audioData, err = eap.assetManager.LoadFileStream("/data/global/music/" + sfx) + } + + if err != nil { + panic(err) + } + + d, err := wav.Decode(context, audioData) + + if err != nil { + log.Fatal(err) + } + + var player *audio.Player + + if loop { + s := audio.NewInfiniteLoop(d, d.Length()) + result.panStream = newPanStreamFromReader(s) + player, err = audio.NewPlayer(context, result.panStream) + } else { + result.panStream = newPanStreamFromReader(d) + player, err = audio.NewPlayer(context, result.panStream) + } + + if err != nil { + log.Fatal(err) + } + + result.player = player + + return result +} diff --git a/d2core/d2audio/ebiten/ebiten_sound_effect.go b/d2core/d2audio/ebiten/ebiten_sound_effect.go index 837e02fd..4ade37a1 100644 --- a/d2core/d2audio/ebiten/ebiten_sound_effect.go +++ b/d2core/d2audio/ebiten/ebiten_sound_effect.go @@ -1,14 +1,9 @@ package ebiten import ( - "log" "math" "github.com/hajimehoshi/ebiten/audio" - "github.com/hajimehoshi/ebiten/audio/wav" - - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" ) type panStream struct { @@ -56,55 +51,6 @@ type SoundEffect struct { panStream *panStream } -// CreateSoundEffect creates a new instance of ebiten's sound effect implementation. -func CreateSoundEffect(sfx string, context *audio.Context, loop bool) *SoundEffect { - result := &SoundEffect{} - - soundFile := "/data/global/sfx/" - - if _, exists := d2datadict.Sounds[sfx]; exists { - soundEntry := d2datadict.Sounds[sfx] - soundFile += soundEntry.FileName - } else { - soundFile += sfx - } - - audioData, err := d2asset.LoadFileStream(soundFile) - - if err != nil { - audioData, err = d2asset.LoadFileStream("/data/global/music/" + sfx) - } - - if err != nil { - panic(err) - } - - d, err := wav.Decode(context, audioData) - - if err != nil { - log.Fatal(err) - } - - var player *audio.Player - - if loop { - s := audio.NewInfiniteLoop(d, d.Length()) - result.panStream = newPanStreamFromReader(s) - player, err = audio.NewPlayer(context, result.panStream) - } else { - result.panStream = newPanStreamFromReader(d) - player, err = audio.NewPlayer(context, result.panStream) - } - - if err != nil { - log.Fatal(err) - } - - result.player = player - - return result -} - // SetPan sets the audio pan, left is -1.0, center is 0.0, right is 1.0 func (v *SoundEffect) SetPan(pan float64) { v.panStream.pan = pan diff --git a/d2core/d2gui/button.go b/d2core/d2gui/button.go index 3d669a87..44534568 100644 --- a/d2core/d2gui/button.go +++ b/d2core/d2gui/button.go @@ -1,11 +1,7 @@ package d2gui import ( - "errors" - - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" ) type buttonState int @@ -30,90 +26,6 @@ type Button struct { surfaces []d2interface.Surface } -func createButton(renderer d2interface.Renderer, text string, buttonStyle ButtonStyle) (*Button, error) { - config := getButtonStyleConfig(buttonStyle) - if config == nil { - return nil, errors.New("invalid button style") - } - - animation, loadErr := d2asset.LoadAnimation(config.animationPath, config.palettePath) - if loadErr != nil { - return nil, loadErr - } - - var buttonWidth int - - for i := 0; i < config.segmentsX; i++ { - w, _, err := animation.GetFrameSize(i) - if err != nil { - return nil, err - } - - buttonWidth += w - } - - var buttonHeight int - - for i := 0; i < config.segmentsY; i++ { - _, h, err := animation.GetFrameSize(i * config.segmentsY) - if err != nil { - return nil, err - } - - buttonHeight += h - } - - font, loadErr := loadFont(config.fontStyle) - if loadErr != nil { - return nil, loadErr - } - - textColor := rgbaColor(grey) - textWidth, textHeight := font.GetTextMetrics(text) - textX := half(buttonWidth) - half(textWidth) - textY := half(buttonHeight) - half(textHeight) + config.textOffset - - surfaceCount := animation.GetFrameCount() / (config.segmentsX * config.segmentsY) - surfaces := make([]d2interface.Surface, surfaceCount) - - for i := 0; i < surfaceCount; i++ { - surface, surfaceErr := renderer.NewSurface(buttonWidth, buttonHeight, d2enum.FilterNearest) - if surfaceErr != nil { - return nil, surfaceErr - } - - segX, segY, frame := config.segmentsX, config.segmentsY, i - if segErr := renderSegmented(animation, segX, segY, frame, surface); segErr != nil { - return nil, segErr - } - - font.SetColor(textColor) - - var textOffsetX, textOffsetY int - - switch buttonState(i) { - case buttonStatePressed, buttonStatePressedToggled: - textOffsetX = -2 - textOffsetY = 2 - } - - surface.PushTranslation(textX+textOffsetX, textY+textOffsetY) - surfaceErr = font.RenderText(text, surface) - surface.Pop() - - if surfaceErr != nil { - return nil, surfaceErr - } - - surfaces[i] = surface - } - - button := &Button{width: buttonWidth, height: buttonHeight, surfaces: surfaces} - button.SetVisible(true) - - return button, nil -} - func (b *Button) onMouseButtonDown(_ d2interface.MouseEvent) bool { b.state = buttonStatePressed return false diff --git a/d2core/d2gui/common.go b/d2core/d2gui/common.go index 1d9b5743..61813357 100644 --- a/d2core/d2gui/common.go +++ b/d2core/d2gui/common.go @@ -6,7 +6,6 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2math" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" ) func loadFont(fontStyle FontStyle) (d2interface.Font, error) { @@ -15,7 +14,8 @@ func loadFont(fontStyle FontStyle) (d2interface.Font, error) { return nil, errors.New("invalid font style") } - return d2asset.LoadFont(config.fontBasePath+".tbl", config.fontBasePath+".dc6", config.palettePath) + return singleton.asset.LoadFont(config.fontBasePath+".tbl", config.fontBasePath+".dc6", + config.palettePath) } func renderSegmented(animation d2interface.Animation, segmentsX, segmentsY, frameOffset int, diff --git a/d2core/d2gui/d2gui.go b/d2core/d2gui/d2gui.go index 6f68967c..8bf2dd95 100644 --- a/d2core/d2gui/d2gui.go +++ b/d2core/d2gui/d2gui.go @@ -3,6 +3,8 @@ package d2gui import ( "errors" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" ) @@ -14,11 +16,11 @@ var ( var singleton *manager // nolint:gochecknoglobals // currently global by design // Initialize creates a singleton gui manager -func Initialize(inputManager d2interface.InputManager) error { +func Initialize(asset *d2asset.AssetManager, inputManager d2interface.InputManager) error { verifyNotInit() var err error - if singleton, err = createGuiManager(inputManager); err != nil { + if singleton, err = createGuiManager(asset, inputManager); err != nil { return err } diff --git a/d2core/d2gui/layout.go b/d2core/d2gui/layout.go index 5e7433b8..12f9de30 100644 --- a/d2core/d2gui/layout.go +++ b/d2core/d2gui/layout.go @@ -1,23 +1,13 @@ package d2gui import ( - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2geom" + "errors" + + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2math" ) -type layoutEntry struct { - widget widget - - x int - y int - width int - height int - - mouseOver bool - mouseDown [3]bool -} - const layoutDebug = false // turns on debug rendering stuff for layouts const ( @@ -168,7 +158,7 @@ func (l *Layout) AddLabel(text string, fontStyle FontStyle) (*Label, error) { // AddButton given a string and ButtonStyle, adds a button as a layout entry func (l *Layout) AddButton(text string, buttonStyle ButtonStyle) (*Button, error) { - button, err := createButton(l.renderer, text, buttonStyle) + button, err := l.createButton(l.renderer, text, buttonStyle) if err != nil { return nil, err } @@ -421,10 +411,88 @@ func (l *Layout) handleEntryVerticalAlign(width int, entry *layoutEntry) { } } -// IsIn layout entry, spc. of an event. -func (l *layoutEntry) IsIn(event d2interface.HandlerEvent) bool { - sx, sy := l.widget.ScreenPos() - rect := d2geom.Rectangle{Left: sx, Top: sy, Width: l.width, Height: l.height} +func (l *Layout) createButton(renderer d2interface.Renderer, text string, + buttonStyle ButtonStyle) (*Button, + error) { + config := getButtonStyleConfig(buttonStyle) + if config == nil { + return nil, errors.New("invalid button style") + } - return rect.IsInRect(event.X(), event.Y()) + animation, loadErr := singleton.asset.LoadAnimation(config.animationPath, config.palettePath) + if loadErr != nil { + return nil, loadErr + } + + var buttonWidth int + + for i := 0; i < config.segmentsX; i++ { + w, _, err := animation.GetFrameSize(i) + if err != nil { + return nil, err + } + + buttonWidth += w + } + + var buttonHeight int + + for i := 0; i < config.segmentsY; i++ { + _, h, err := animation.GetFrameSize(i * config.segmentsY) + if err != nil { + return nil, err + } + + buttonHeight += h + } + + font, loadErr := loadFont(config.fontStyle) + if loadErr != nil { + return nil, loadErr + } + + textColor := rgbaColor(grey) + textWidth, textHeight := font.GetTextMetrics(text) + textX := half(buttonWidth) - half(textWidth) + textY := half(buttonHeight) - half(textHeight) + config.textOffset + + surfaceCount := animation.GetFrameCount() / (config.segmentsX * config.segmentsY) + surfaces := make([]d2interface.Surface, surfaceCount) + + for i := 0; i < surfaceCount; i++ { + surface, surfaceErr := renderer.NewSurface(buttonWidth, buttonHeight, d2enum.FilterNearest) + if surfaceErr != nil { + return nil, surfaceErr + } + + segX, segY, frame := config.segmentsX, config.segmentsY, i + if segErr := renderSegmented(animation, segX, segY, frame, surface); segErr != nil { + return nil, segErr + } + + font.SetColor(textColor) + + var textOffsetX, textOffsetY int + + switch buttonState(i) { + case buttonStatePressed, buttonStatePressedToggled: + textOffsetX = -2 + textOffsetY = 2 + } + + surface.PushTranslation(textX+textOffsetX, textY+textOffsetY) + surfaceErr = font.RenderText(text, surface) + surface.Pop() + + if surfaceErr != nil { + return nil, surfaceErr + } + + surfaces[i] = surface + } + + button := &Button{width: buttonWidth, height: buttonHeight, surfaces: surfaces} + button.SetVisible(true) + + return button, nil } diff --git a/d2core/d2gui/layout_entry.go b/d2core/d2gui/layout_entry.go new file mode 100644 index 00000000..fc87d857 --- /dev/null +++ b/d2core/d2gui/layout_entry.go @@ -0,0 +1,26 @@ +package d2gui + +import ( + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2geom" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" +) + +type layoutEntry struct { + widget widget + + x int + y int + width int + height int + + mouseOver bool + mouseDown [3]bool +} + +// IsIn layout entry, spc. of an event. +func (l *layoutEntry) IsIn(event d2interface.HandlerEvent) bool { + sx, sy := l.widget.ScreenPos() + rect := d2geom.Rectangle{Left: sx, Top: sy, Width: l.width, Height: l.height} + + return rect.IsInRect(event.X(), event.Y()) +} diff --git a/d2core/d2gui/manager.go b/d2core/d2gui/manager.go index df5759f2..365c38cd 100644 --- a/d2core/d2gui/manager.go +++ b/d2core/d2gui/manager.go @@ -5,12 +5,12 @@ import ( "math" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" ) type manager struct { + asset *d2asset.AssetManager layout *Layout cursorAnim d2interface.Animation cursorX int @@ -20,18 +20,19 @@ type manager struct { loading bool } -func createGuiManager(inputManager d2interface.InputManager) (*manager, error) { - cursorAnim, err := d2asset.LoadAnimation(d2resource.CursorDefault, d2resource.PaletteUnits) +func createGuiManager(asset *d2asset.AssetManager, inputManager d2interface.InputManager) (*manager, error) { + cursorAnim, err := asset.LoadAnimation(d2resource.CursorDefault, d2resource.PaletteUnits) if err != nil { return nil, err } - loadingAnim, err := d2asset.LoadAnimation(d2resource.LoadingScreen, d2resource.PaletteLoading) + loadingAnim, err := asset.LoadAnimation(d2resource.LoadingScreen, d2resource.PaletteLoading) if err != nil { return nil, err } manager := &manager{ + asset: asset, cursorAnim: cursorAnim, loadingAnim: loadingAnim, cursorVisible: true, diff --git a/d2core/d2gui/sprite.go b/d2core/d2gui/sprite.go index 4668826a..1816c778 100644 --- a/d2core/d2gui/sprite.go +++ b/d2core/d2gui/sprite.go @@ -3,7 +3,6 @@ package d2gui import ( "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" ) // AnimationDirection is a the animation play direction @@ -32,7 +31,7 @@ type AnimatedSprite struct { } func createSprite(imagePath, palettePath string) (*Sprite, error) { - animation, err := d2asset.LoadAnimation(imagePath, palettePath) + animation, err := singleton.asset.LoadAnimation(imagePath, palettePath) if err != nil { return nil, err } @@ -45,7 +44,7 @@ func createSprite(imagePath, palettePath string) (*Sprite, error) { } func createAnimatedSprite(imagePath, palettePath string, direction AnimationDirection) (*AnimatedSprite, error) { - animation, err := d2asset.LoadAnimation(imagePath, palettePath) + animation, err := singleton.asset.LoadAnimation(imagePath, palettePath) if err != nil { return nil, err } diff --git a/d2core/d2map/d2mapengine/engine.go b/d2core/d2map/d2mapengine/engine.go index 2ea38805..799334ea 100644 --- a/d2core/d2map/d2mapengine/engine.go +++ b/d2core/d2map/d2mapengine/engine.go @@ -4,6 +4,8 @@ import ( "log" "strings" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapentity" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2ds1" @@ -16,6 +18,9 @@ import ( // MapEngine loads the tiles which make up the isometric map and the entities type MapEngine struct { + asset *d2asset.AssetManager + *d2mapstamp.StampFactory + *d2mapentity.MapEntityFactory seed int64 // The map seed entities map[string]d2interface.MapEntity // Entities on the map tiles []MapTile @@ -28,8 +33,16 @@ type MapEngine struct { } // CreateMapEngine creates a new instance of the map engine and returns a pointer to it. -func CreateMapEngine() *MapEngine { - engine := &MapEngine{} +func CreateMapEngine(asset *d2asset.AssetManager) *MapEngine { + entity := d2mapentity.NewMapEntityFactory(asset) + stamp := d2mapstamp.NewStampFactory(asset, entity) + + engine := &MapEngine{ + asset: asset, + MapEntityFactory: entity, + StampFactory: stamp, + } + return engine } @@ -64,7 +77,7 @@ func (m *MapEngine) addDT1(fileName string) { } } - fileData, err := d2asset.LoadFile("/data/global/tiles/" + fileName) + fileData, err := m.asset.LoadFile("/data/global/tiles/" + fileName) if err != nil { log.Printf("Could not load /data/global/tiles/%s", fileName) // panic(err) @@ -84,7 +97,7 @@ func (m *MapEngine) AddDS1(fileName string) { return } - fileData, err := d2asset.LoadFile("/data/global/tiles/" + fileName) + fileData, err := m.asset.LoadFile("/data/global/tiles/" + fileName) if err != nil { panic(err) } @@ -289,7 +302,7 @@ func (m *MapEngine) TileExists(tileX, tileY int) bool { // GenerateMap clears the map and places the specified stamp. func (m *MapEngine) GenerateMap(regionType d2enum.RegionIdType, levelPreset, fileIndex int) { - region := d2mapstamp.LoadStamp(regionType, levelPreset, fileIndex) + region := m.LoadStamp(regionType, levelPreset, fileIndex) regionSize := region.Size() m.ResetMap(regionType, regionSize.Width, regionSize.Height) m.PlaceStamp(region, 0, 0) diff --git a/d2core/d2map/d2mapentity/d2mapentity.go b/d2core/d2map/d2mapentity/factory.go similarity index 72% rename from d2core/d2map/d2mapentity/d2mapentity.go rename to d2core/d2map/d2mapentity/factory.go index 2979a91b..f223acb9 100644 --- a/d2core/d2map/d2mapentity/d2mapentity.go +++ b/d2core/d2map/d2mapentity/factory.go @@ -4,6 +4,9 @@ import ( "errors" "fmt" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2math/d2vector" + uuid "github.com/satori/go.uuid" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2tbl" @@ -15,6 +18,16 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2core/d2item/diablo2item" ) +// NewMapEntityFactory creates a MapEntityFactory instance with the given asset manager +func NewMapEntityFactory(asset *d2asset.AssetManager) *MapEntityFactory { + return &MapEntityFactory{asset} +} + +// MapEntityFactory creates map entities for the MapEngine +type MapEntityFactory struct { + asset *d2asset.AssetManager +} + // NewAnimatedEntity creates an instance of AnimatedEntity func NewAnimatedEntity(x, y int, animation d2interface.Animation) *AnimatedEntity { entity := &AnimatedEntity{ @@ -27,7 +40,7 @@ func NewAnimatedEntity(x, y int, animation d2interface.Animation) *AnimatedEntit } // NewPlayer creates a new player entity and returns a pointer to it. -func NewPlayer(id, name string, x, y, direction int, heroType d2enum.Hero, +func (f *MapEntityFactory) NewPlayer(id, name string, x, y, direction int, heroType d2enum.Hero, stats *d2hero.HeroStatsState, equipment *d2inventory.CharacterEquipment) *Player { layerEquipment := &[d2enum.CompositeTypeMax]string{ d2enum.CompositeTypeHead: equipment.Head.GetArmorClass(), @@ -40,7 +53,7 @@ func NewPlayer(id, name string, x, y, direction int, heroType d2enum.Hero, d2enum.CompositeTypeShield: equipment.Shield.GetItemCode(), } - composite, err := d2asset.LoadComposite(d2enum.ObjectTypePlayer, heroType.GetToken(), + composite, err := f.asset.LoadComposite(d2enum.ObjectTypePlayer, heroType.GetToken(), d2resource.PaletteUnits) if err != nil { panic(err) @@ -81,8 +94,8 @@ func NewPlayer(id, name string, x, y, direction int, heroType d2enum.Hero, } // NewMissile creates a new Missile and initializes it's animation. -func NewMissile(x, y int, record *d2datadict.MissileRecord) (*Missile, error) { - animation, err := d2asset.LoadAnimation( +func (f *MapEntityFactory) NewMissile(x, y int, record *d2datadict.MissileRecord) (*Missile, error) { + animation, err := f.asset.LoadAnimation( fmt.Sprintf("%s/%s.dcc", d2resource.MissileData, record.Animation.CelFileName), d2resource.PaletteUnits, ) @@ -109,7 +122,7 @@ func NewMissile(x, y int, record *d2datadict.MissileRecord) (*Missile, error) { } // NewItem creates an item map entity -func NewItem(x, y int, codes ...string) (*Item, error) { +func (f *MapEntityFactory) NewItem(x, y int, codes ...string) (*Item, error) { item := diablo2item.NewItem(codes...) if item == nil { @@ -118,7 +131,7 @@ func NewItem(x, y int, codes ...string) (*Item, error) { filename := item.CommonRecord().FlippyFile filepath := fmt.Sprintf("%s/%s.DC6", d2resource.ItemGraphics, filename) - animation, err := d2asset.LoadAnimation(filepath, d2resource.PaletteUnits) + animation, err := f.asset.LoadAnimation(filepath, d2resource.PaletteUnits) if err != nil { return nil, err @@ -137,7 +150,7 @@ func NewItem(x, y int, codes ...string) (*Item, error) { } // NewNPC creates a new NPC and returns a pointer to it. -func NewNPC(x, y int, monstat *d2datadict.MonStatsRecord, direction int) (*NPC, error) { +func (f *MapEntityFactory) NewNPC(x, y int, monstat *d2datadict.MonStatsRecord, direction int) (*NPC, error) { result := &NPC{ mapEntity: newMapEntity(x, y), HasPaths: false, @@ -151,7 +164,7 @@ func NewNPC(x, y int, monstat *d2datadict.MonStatsRecord, direction int) (*NPC, equipment[compType] = selectEquip(opts) } - composite, _ := d2asset.LoadComposite(d2enum.ObjectTypeCharacter, monstat.AnimationDirectoryToken, + composite, _ := f.asset.LoadComposite(d2enum.ObjectTypeCharacter, monstat.AnimationDirectoryToken, d2resource.PaletteUnits) result.composite = composite @@ -175,3 +188,30 @@ func NewNPC(x, y int, monstat *d2datadict.MonStatsRecord, direction int) (*NPC, return result, nil } + +// NewObject creates an instance of AnimatedComposite +func (f *MapEntityFactory) NewObject(x, y int, objectRec *d2datadict.ObjectRecord, + palettePath string) (*Object, error) { + locX, locY := float64(x), float64(y) + entity := &Object{ + uuid: uuid.NewV4().String(), + objectRecord: objectRec, + Position: d2vector.NewPosition(locX, locY), + name: d2tbl.TranslateString(objectRec.Name), + } + objectType := &d2datadict.ObjectTypes[objectRec.Index] + + composite, err := f.asset.LoadComposite(d2enum.ObjectTypeItem, objectType.Token, + palettePath) + if err != nil { + return nil, err + } + + entity.composite = composite + + entity.setMode(d2enum.ObjectAnimationModeNeutral, 0, false) + + initObject(entity) + + return entity, nil +} diff --git a/d2core/d2object/object.go b/d2core/d2map/d2mapentity/object.go similarity index 77% rename from d2core/d2object/object.go rename to d2core/d2map/d2mapentity/object.go index c9206e61..67b4ef21 100644 --- a/d2core/d2object/object.go +++ b/d2core/d2map/d2mapentity/object.go @@ -1,18 +1,14 @@ // Package d2object implements objects placed on the map and their functionality -package d2object +package d2mapentity import ( "fmt" "math/rand" - uuid "github.com/satori/go.uuid" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2tbl" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2math/d2vector" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" ) @@ -28,36 +24,6 @@ type Object struct { name string } -// CreateObject creates an instance of AnimatedComposite -func CreateObject(x, y int, objectRec *d2datadict.ObjectRecord, palettePath string) (*Object, error) { - locX, locY := float64(x), float64(y) - entity := &Object{ - uuid: uuid.NewV4().String(), - objectRecord: objectRec, - Position: d2vector.NewPosition(locX, locY), - name: d2tbl.TranslateString(objectRec.Name), - } - objectType := &d2datadict.ObjectTypes[objectRec.Index] - - composite, err := d2asset.LoadComposite(d2enum.ObjectTypeItem, objectType.Token, - d2resource.PaletteUnits) - if err != nil { - return nil, err - } - - entity.composite = composite - - if err := entity.setMode(d2enum.ObjectAnimationModeNeutral, 0, false); err != nil { - return nil, err - } - - if _, err := initObject(entity); err != nil { - return nil, err - } - - return entity, nil -} - // setMode changes the graphical mode of this animated entity func (ob *Object) setMode(animationMode d2enum.ObjectAnimationMode, direction int, randomFrame bool) error { err := ob.composite.SetMode(animationMode, "HTH") diff --git a/d2core/d2object/init_function.go b/d2core/d2map/d2mapentity/object_init.go similarity index 98% rename from d2core/d2object/init_function.go rename to d2core/d2map/d2mapentity/object_init.go index 66c5a5a2..21a4e46e 100644 --- a/d2core/d2object/init_function.go +++ b/d2core/d2map/d2mapentity/object_init.go @@ -1,4 +1,4 @@ -package d2object +package d2mapentity import ( "math/rand" diff --git a/d2core/d2map/d2mapgen/act1_overworld.go b/d2core/d2map/d2mapgen/act1_overworld.go index b54f6eb1..79d31970 100644 --- a/d2core/d2map/d2mapgen/act1_overworld.go +++ b/d2core/d2map/d2mapgen/act1_overworld.go @@ -1,4 +1,3 @@ -//nolint:gomnd package d2mapgen import ( @@ -20,7 +19,7 @@ func loadPreset(mapEngine *d2mapengine.MapEngine, id, index int) *d2mapstamp.Sta mapEngine.AddDS1(file) } - return d2mapstamp.LoadStamp(d2enum.RegionAct1Wilderness, id, index) + return mapEngine.LoadStamp(d2enum.RegionAct1Wilderness, id, index) } // GenerateAct1Overworld generates the map and entities for the first town and surrounding area. @@ -33,7 +32,7 @@ func GenerateAct1Overworld(mapEngine *d2mapengine.MapEngine) { mapWidth := mapEngine.Size().Width mapHeight := mapEngine.Size().Height - townStamp := d2mapstamp.LoadStamp(d2enum.RegionAct1Town, 1, -1) + townStamp := mapEngine.LoadStamp(d2enum.RegionAct1Town, 1, -1) townStamp.RegionPath() townSize := townStamp.Size() diff --git a/d2core/d2map/d2maprenderer/renderer.go b/d2core/d2map/d2maprenderer/renderer.go index 49e39d39..b613be05 100644 --- a/d2core/d2map/d2maprenderer/renderer.go +++ b/d2core/d2map/d2maprenderer/renderer.go @@ -48,6 +48,7 @@ const ( // MapRenderer manages the game viewport and Camera. It requests tile and entity data from MapEngine and renders it. type MapRenderer struct { + asset *d2asset.AssetManager renderer d2interface.Renderer // Used for drawing operations mapEngine *d2mapengine.MapEngine // The map engine that is being rendered palette d2interface.Palette // The palette used for this map @@ -61,9 +62,11 @@ type MapRenderer struct { } // CreateMapRenderer creates a new MapRenderer, sets the required fields and returns a pointer to it. -func CreateMapRenderer(renderer d2interface.Renderer, mapEngine *d2mapengine.MapEngine, +func CreateMapRenderer(asset *d2asset.AssetManager, renderer d2interface.Renderer, + mapEngine *d2mapengine.MapEngine, term d2interface.Terminal, startX, startY float64) *MapRenderer { result := &MapRenderer{ + asset: asset, renderer: renderer, mapEngine: mapEngine, viewport: NewViewport(0, 0, 800, 600), @@ -563,7 +566,8 @@ func (mr *MapRenderer) Advance(elapsed float64) { mr.Camera.Advance(elapsed) } -func loadPaletteForAct(levelType d2enum.RegionIdType) (d2interface.Palette, error) { +func (mr *MapRenderer) loadPaletteForAct(levelType d2enum.RegionIdType) (d2interface.Palette, + error) { var palettePath string switch levelType { @@ -586,7 +590,7 @@ func loadPaletteForAct(levelType d2enum.RegionIdType) (d2interface.Palette, erro return nil, errors.New("failed to find palette for region") } - return d2asset.LoadPalette(palettePath) + return mr.asset.LoadPalette(palettePath) } // ViewportToLeft moves the viewport to the left. diff --git a/d2core/d2map/d2maprenderer/tile_cache.go b/d2core/d2map/d2maprenderer/tile_cache.go index 4fdd7fc8..f5f1ef74 100644 --- a/d2core/d2map/d2maprenderer/tile_cache.go +++ b/d2core/d2map/d2maprenderer/tile_cache.go @@ -11,7 +11,7 @@ import ( ) func (mr *MapRenderer) generateTileCache() { - mr.palette, _ = loadPaletteForAct(d2enum.RegionIdType(mr.mapEngine.LevelType().ID)) + mr.palette, _ = mr.loadPaletteForAct(d2enum.RegionIdType(mr.mapEngine.LevelType().ID)) tiles := *mr.mapEngine.Tiles() for idx := range tiles { diff --git a/d2core/d2map/d2mapstamp/factory.go b/d2core/d2map/d2mapstamp/factory.go new file mode 100644 index 00000000..2a8ae56f --- /dev/null +++ b/d2core/d2map/d2mapstamp/factory.go @@ -0,0 +1,74 @@ +package d2mapstamp + +import ( + "math" + "math/rand" + + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapentity" + + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2ds1" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dt1" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" +) + +func NewStampFactory(asset *d2asset.AssetManager, entity *d2mapentity.MapEntityFactory) *StampFactory { + return &StampFactory{asset, entity} +} + +type StampFactory struct { + asset *d2asset.AssetManager + entity *d2mapentity.MapEntityFactory +} + +// LoadStamp loads the Stamp data from file. +func (f *StampFactory) LoadStamp(levelType d2enum.RegionIdType, levelPreset, fileIndex int) *Stamp { + stamp := &Stamp{ + entity: f.entity, + regionID: levelType, + levelType: d2datadict.LevelTypes[levelType], + levelPreset: d2datadict.LevelPresets[levelPreset], + } + + for _, levelTypeDt1 := range &stamp.levelType.Files { + if levelTypeDt1 != "" && levelTypeDt1 != "0" { + fileData, err := f.asset.LoadFile("/data/global/tiles/" + levelTypeDt1) + if err != nil { + panic(err) + } + + dt1, _ := d2dt1.LoadDT1(fileData) + + stamp.tiles = append(stamp.tiles, dt1.Tiles...) + } + } + + var levelFilesToPick []string + + for _, fileRecord := range stamp.levelPreset.Files { + if fileRecord != "" && fileRecord != "0" { + levelFilesToPick = append(levelFilesToPick, fileRecord) + } + } + + levelIndex := int(math.Round(float64(len(levelFilesToPick)-1) * rand.Float64())) + if fileIndex >= 0 && fileIndex < len(levelFilesToPick) { + levelIndex = fileIndex + } + + if levelFilesToPick == nil { + panic("no level files to pick from") + } + + stamp.regionPath = levelFilesToPick[levelIndex] + fileData, err := f.asset.LoadFile("/data/global/tiles/" + stamp.regionPath) + + if err != nil { + panic(err) + } + + stamp.ds1, _ = d2ds1.LoadDS1(fileData) + + return stamp +} diff --git a/d2core/d2map/d2mapstamp/stamp.go b/d2core/d2map/d2mapstamp/stamp.go index f8ddcf96..80ca6da5 100644 --- a/d2core/d2map/d2mapstamp/stamp.go +++ b/d2core/d2map/d2mapstamp/stamp.go @@ -1,9 +1,6 @@ package d2mapstamp import ( - "math" - "math/rand" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2ds1" @@ -13,13 +10,16 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2common/d2math/d2vector" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2path" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapentity" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2object" +) + +const ( + subtilesPerTile = 5 ) // Stamp represents a pre-fabricated map stamp that can be placed on a map. type Stamp struct { + entity *d2mapentity.MapEntityFactory regionPath string // The file path of the region regionID d2enum.RegionIdType levelType d2datadict.LevelTypeRecord // The level type id for this stamp @@ -28,56 +28,6 @@ type Stamp struct { ds1 *d2ds1.DS1 // The backing DS1 file for this stamp } -// LoadStamp loads the Stamp data from file. -func LoadStamp(levelType d2enum.RegionIdType, levelPreset, fileIndex int) *Stamp { - stamp := &Stamp{ - regionID: levelType, - levelType: d2datadict.LevelTypes[levelType], - levelPreset: d2datadict.LevelPresets[levelPreset], - } - - for _, levelTypeDt1 := range &stamp.levelType.Files { - if levelTypeDt1 != "" && levelTypeDt1 != "0" { - fileData, err := d2asset.LoadFile("/data/global/tiles/" + levelTypeDt1) - if err != nil { - panic(err) - } - - dt1, _ := d2dt1.LoadDT1(fileData) - - stamp.tiles = append(stamp.tiles, dt1.Tiles...) - } - } - - var levelFilesToPick []string - - for _, fileRecord := range stamp.levelPreset.Files { - if fileRecord != "" && fileRecord != "0" { - levelFilesToPick = append(levelFilesToPick, fileRecord) - } - } - - levelIndex := int(math.Round(float64(len(levelFilesToPick)-1) * rand.Float64())) - if fileIndex >= 0 && fileIndex < len(levelFilesToPick) { - levelIndex = fileIndex - } - - if levelFilesToPick == nil { - panic("no level files to pick from") - } - - stamp.regionPath = levelFilesToPick[levelIndex] - fileData, err := d2asset.LoadFile("/data/global/tiles/" + stamp.regionPath) - - if err != nil { - panic(err) - } - - stamp.ds1, _ = d2ds1.LoadDS1(fileData) - - return stamp -} - // Size returns the size of the stamp in tiles. func (mr *Stamp) Size() d2geom.Size { return d2geom.Size{Width: int(mr.ds1.Width), Height: int(mr.ds1.Height)} @@ -132,7 +82,7 @@ func (mr *Stamp) Entities(tileOffsetX, tileOffsetY int) []d2interface.MapEntity if monstat != nil { // Temorary use of Lookup. npcX, npcY := (tileOffsetX*5)+object.X, (tileOffsetY*5)+object.Y - npc, err := d2mapentity.NewNPC(npcX, npcY, monstat, 0) + npc, err := mr.entity.NewNPC(npcX, npcY, monstat, 0) if err == nil { npc.SetPaths(convertPaths(tileOffsetX, tileOffsetY, object.Paths)) @@ -153,7 +103,7 @@ func (mr *Stamp) Entities(tileOffsetX, tileOffsetY int) []d2interface.MapEntity objectRecord := d2datadict.Objects[lookup.ObjectsTxtId] if objectRecord != nil { - entity, err := d2object.CreateObject((tileOffsetX*5)+object.X, + entity, err := mr.entity.NewObject((tileOffsetX*5)+object.X, (tileOffsetY*5)+object.Y, objectRecord, d2resource.PaletteUnits) if err != nil { @@ -173,8 +123,8 @@ func convertPaths(tileOffsetX, tileOffsetY int, paths []d2path.Path) []d2path.Pa for i := 0; i < len(paths); i++ { result[i].Action = paths[i].Action result[i].Position = d2vector.NewPosition( - paths[i].Position.X()+float64(tileOffsetX*5), - paths[i].Position.Y()+float64(tileOffsetY*5)) + paths[i].Position.X()+float64(tileOffsetX*subtilesPerTile), + paths[i].Position.Y()+float64(tileOffsetY*subtilesPerTile)) } return result diff --git a/d2core/d2ui/button.go b/d2core/d2ui/button.go index 2b846aaf..80ffac42 100644 --- a/d2core/d2ui/button.go +++ b/d2core/d2ui/button.go @@ -8,7 +8,6 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2util" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui" ) @@ -192,7 +191,7 @@ func (ui *UIManager) NewButton(buttonType ButtonType, text string) *Button { lbl.Color[0] = d2util.Color(greyAlpha100) lbl.Alignment = d2gui.HorizontalAlignCenter - animation, _ := d2asset.LoadAnimation(buttonLayout.ResourceName, buttonLayout.PaletteName) + animation, _ := ui.asset.LoadAnimation(buttonLayout.ResourceName, buttonLayout.PaletteName) buttonSprite, _ := ui.NewSprite(animation) for i := 0; i < buttonLayout.XSegments; i++ { diff --git a/d2core/d2ui/checkbox.go b/d2core/d2ui/checkbox.go index 0c2d460a..390d504a 100644 --- a/d2core/d2ui/checkbox.go +++ b/d2core/d2ui/checkbox.go @@ -4,7 +4,6 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" ) // Checkbox represents a checkbox UI element @@ -32,7 +31,7 @@ func (ui *UIManager) NewCheckbox(checkState bool) *Checkbox { enabled: true, } - animation, _ := d2asset.LoadAnimation(d2resource.Checkbox, d2resource.PaletteFechar) + animation, _ := ui.asset.LoadAnimation(d2resource.Checkbox, d2resource.PaletteFechar) checkboxSprite, _ := ui.NewSprite(animation) result.width, result.height, _ = checkboxSprite.GetFrameSize(0) checkboxSprite.SetPosition(0, 0) diff --git a/d2core/d2ui/d2ui.go b/d2core/d2ui/d2ui.go index a42b75d4..4ae6f4b9 100644 --- a/d2core/d2ui/d2ui.go +++ b/d2core/d2ui/d2ui.go @@ -2,6 +2,7 @@ package d2ui import ( "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" ) // CursorButton represents a mouse button @@ -16,11 +17,13 @@ const ( // NewUIManager creates a UIManager instance with the given input and audio provider func NewUIManager( + asset *d2asset.AssetManager, renderer d2interface.Renderer, input d2interface.InputManager, audio d2interface.AudioProvider, ) *UIManager { ui := &UIManager{ + asset: asset, renderer: renderer, inputManager: input, audio: audio, diff --git a/d2core/d2ui/label.go b/d2core/d2ui/label.go index 58b13066..f3a79bcc 100644 --- a/d2core/d2ui/label.go +++ b/d2core/d2ui/label.go @@ -8,7 +8,6 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2util" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui" ) @@ -26,7 +25,7 @@ type Label struct { // NewLabel creates a new instance of a UI label func (ui *UIManager) NewLabel(fontPath, palettePath string) *Label { - font, _ := d2asset.LoadFont(fontPath+".tbl", fontPath+".dc6", palettePath) + font, _ := ui.asset.LoadFont(fontPath+".tbl", fontPath+".dc6", palettePath) result := &Label{ Alignment: d2gui.HorizontalAlignLeft, Color: map[int]color.Color{0: color.White}, diff --git a/d2core/d2ui/scrollbar.go b/d2core/d2ui/scrollbar.go index 7a99e5ad..77d63f51 100644 --- a/d2core/d2ui/scrollbar.go +++ b/d2core/d2ui/scrollbar.go @@ -3,7 +3,6 @@ package d2ui import ( "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" ) const ( @@ -29,7 +28,7 @@ type Scrollbar struct { // NewScrollbar creates a scrollbar instance func (ui *UIManager) NewScrollbar(x, y, height int) *Scrollbar { - animation, _ := d2asset.LoadAnimation(d2resource.Scrollbar, d2resource.PaletteSky) + animation, _ := ui.asset.LoadAnimation(d2resource.Scrollbar, d2resource.PaletteSky) scrollbarSprite, _ := ui.NewSprite(animation) result := &Scrollbar{ visible: true, diff --git a/d2core/d2ui/textbox.go b/d2core/d2ui/textbox.go index 68fc3aec..8b933ac3 100644 --- a/d2core/d2ui/textbox.go +++ b/d2core/d2ui/textbox.go @@ -7,7 +7,6 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" ) // TextBox represents a text input box @@ -27,7 +26,7 @@ type TextBox struct { // NewTextbox creates a new instance of a text box func (ui *UIManager) NewTextbox() *TextBox { - animation, _ := d2asset.LoadAnimation(d2resource.TextBox2, d2resource.PaletteUnits) + animation, _ := ui.asset.LoadAnimation(d2resource.TextBox2, d2resource.PaletteUnits) bgSprite, _ := ui.NewSprite(animation) tb := &TextBox{ filter: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", diff --git a/d2core/d2ui/ui_manager.go b/d2core/d2ui/ui_manager.go index 410d24a2..d5631ab0 100644 --- a/d2core/d2ui/ui_manager.go +++ b/d2core/d2ui/ui_manager.go @@ -3,6 +3,8 @@ package d2ui import ( "log" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" @@ -10,6 +12,7 @@ import ( // UIManager manages a collection of UI elements (buttons, textboxes, labels) type UIManager struct { + asset *d2asset.AssetManager renderer d2interface.Renderer inputManager d2interface.InputManager audio d2interface.AudioProvider diff --git a/d2game/d2gamescreen/blizzard_intro.go b/d2game/d2gamescreen/blizzard_intro.go index ebf44cb2..c97f8e56 100644 --- a/d2game/d2gamescreen/blizzard_intro.go +++ b/d2game/d2gamescreen/blizzard_intro.go @@ -8,17 +8,20 @@ import ( // BlizzardIntro represents the Blizzard Intro screen type BlizzardIntro struct { + asset *d2asset.AssetManager videoDecoder *d2video.BinkDecoder } // CreateBlizzardIntro creates a Blizzard Intro screen -func CreateBlizzardIntro() *BlizzardIntro { - return &BlizzardIntro{} +func CreateBlizzardIntro(asset *d2asset.AssetManager) *BlizzardIntro { + return &BlizzardIntro{ + asset: asset, + } } // OnLoad loads the resources for the Blizzard Intro screen func (v *BlizzardIntro) OnLoad(loading d2screen.LoadingState) { - videoBytes, err := d2asset.LoadFile("/data/local/video/BlizNorth640x480.bik") + videoBytes, err := v.asset.LoadFile("/data/local/video/BlizNorth640x480.bik") if err != nil { loading.Error(err) return diff --git a/d2game/d2gamescreen/character_select.go b/d2game/d2gamescreen/character_select.go index f51c6c0d..d280a8c4 100644 --- a/d2game/d2gamescreen/character_select.go +++ b/d2game/d2gamescreen/character_select.go @@ -21,6 +21,8 @@ import ( // CharacterSelect represents the character select screen type CharacterSelect struct { + asset *d2asset.AssetManager + *d2mapentity.MapEntityFactory background *d2ui.Sprite newCharButton *d2ui.Button convertCharButton *d2ui.Button @@ -54,6 +56,7 @@ type CharacterSelect struct { // CreateCharacterSelect creates the character select screen and returns a pointer to it func CreateCharacterSelect( navigator Navigator, + asset *d2asset.AssetManager, renderer d2interface.Renderer, inputManager d2interface.InputManager, audioProvider d2interface.AudioProvider, @@ -63,6 +66,8 @@ func CreateCharacterSelect( ) *CharacterSelect { return &CharacterSelect{ selectedCharacter: -1, + asset: asset, + MapEntityFactory: d2mapentity.NewMapEntityFactory(asset), renderer: renderer, connectionType: connectionType, connectionHost: connectionHost, @@ -133,7 +138,8 @@ func (v *CharacterSelect) OnLoad(loading d2screen.LoadingState) { loading.Progress(tenPercent) - animation, _ := d2asset.LoadAnimation(d2resource.CharacterSelectionBackground, d2resource.PaletteSky) + animation, _ := v.asset.LoadAnimation(d2resource.CharacterSelectionBackground, + d2resource.PaletteSky) bgX, bgY := 0, 0 v.background, _ = v.uiManager.NewSprite(animation) v.background.SetPosition(bgX, bgY) @@ -154,12 +160,13 @@ func (v *CharacterSelect) OnLoad(loading d2screen.LoadingState) { deleteConfirmX, deleteConfirmY := 400, 185 v.deleteCharConfirmLabel.SetPosition(deleteConfirmX, deleteConfirmY) - animation, _ = d2asset.LoadAnimation(d2resource.CharacterSelectionSelectBox, d2resource.PaletteSky) + animation, _ = v.asset.LoadAnimation(d2resource.CharacterSelectionSelectBox, + d2resource.PaletteSky) v.selectionBox, _ = v.uiManager.NewSprite(animation) selBoxX, selBoxY := 37, 86 v.selectionBox.SetPosition(selBoxX, selBoxY) - animation, _ = d2asset.LoadAnimation(d2resource.PopUpOkCancel, d2resource.PaletteFechar) + animation, _ = v.asset.LoadAnimation(d2resource.PopUpOkCancel, d2resource.PaletteFechar) v.okCancelBox, _ = v.uiManager.NewSprite(animation) okCancelX, okCancelY := 270, 175 v.okCancelBox.SetPosition(okCancelX, okCancelY) @@ -283,7 +290,7 @@ func (v *CharacterSelect) updateCharacterBoxes() { equipment := d2inventory.HeroObjects[heroType] // TODO: Generate or load the object from the actual player data... - v.characterImage[i] = d2mapentity.NewPlayer("", "", 0, 0, 0, + v.characterImage[i] = v.NewPlayer("", "", 0, 0, 0, v.gameStates[idx].HeroType, v.gameStates[idx].Stats, &equipment, diff --git a/d2game/d2gamescreen/credits.go b/d2game/d2gamescreen/credits.go index c4e07cb8..46eb36e9 100644 --- a/d2game/d2gamescreen/credits.go +++ b/d2game/d2gamescreen/credits.go @@ -39,14 +39,17 @@ type Credits struct { cyclesTillNextLine int doneWithCredits bool + asset *d2asset.AssetManager renderer d2interface.Renderer navigator Navigator uiManager *d2ui.UIManager } // CreateCredits creates an instance of the credits screen -func CreateCredits(navigator Navigator, renderer d2interface.Renderer, ui *d2ui.UIManager) *Credits { +func CreateCredits(navigator Navigator, asset *d2asset.AssetManager, renderer d2interface.Renderer, + ui *d2ui.UIManager) *Credits { result := &Credits{ + asset: asset, labels: make([]*labelItem, 0), cycleTime: 0, doneWithCredits: false, @@ -86,7 +89,7 @@ func (v *Credits) LoadContributors() []string { // OnLoad is called to load the resources for the credits screen func (v *Credits) OnLoad(loading d2screen.LoadingState) { - animation, _ := d2asset.LoadAnimation(d2resource.CreditsBackground, d2resource.PaletteSky) + animation, _ := v.asset.LoadAnimation(d2resource.CreditsBackground, d2resource.PaletteSky) v.creditsBackground, _ = v.uiManager.NewSprite(animation) v.creditsBackground.SetPosition(creditsX, creditsY) loading.Progress(twentyPercent) @@ -96,7 +99,7 @@ func (v *Credits) OnLoad(loading d2screen.LoadingState) { v.exitButton.OnActivated(func() { v.onExitButtonClicked() }) loading.Progress(fourtyPercent) - fileData, err := d2asset.LoadFile(d2resource.CreditsText) + fileData, err := v.asset.LoadFile(d2resource.CreditsText) if err != nil { loading.Error(err) return diff --git a/d2game/d2gamescreen/game.go b/d2game/d2gamescreen/game.go index 1fe0959f..fe8207ed 100644 --- a/d2game/d2gamescreen/game.go +++ b/d2game/d2gamescreen/game.go @@ -4,6 +4,8 @@ import ( "fmt" "image/color" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2math/d2vector" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui" @@ -30,6 +32,8 @@ const ( // Game represents the Gameplay screen type Game struct { + *d2mapentity.MapEntityFactory + asset *d2asset.AssetManager gameClient *d2client.GameClient mapRenderer *d2maprenderer.MapRenderer uiManager *d2ui.UIManager @@ -50,6 +54,8 @@ type Game struct { // CreateGame creates the Gameplay screen and returns a pointer to it func CreateGame( navigator Navigator, + asset *d2asset.AssetManager, + ui *d2ui.UIManager, renderer d2interface.Renderer, inputManager d2interface.InputManager, audioProvider d2interface.AudioProvider, @@ -71,19 +77,21 @@ func CreateGame( } result := &Game{ + asset: asset, gameClient: gameClient, gameControls: nil, localPlayer: nil, lastRegionType: d2enum.RegionNone, ticksSinceLevelCheck: 0, - mapRenderer: d2maprenderer.CreateMapRenderer(renderer, gameClient.MapEngine, term, startX, startY), - escapeMenu: NewEscapeMenu(navigator, renderer, audioProvider), - inputManager: inputManager, - audioProvider: audioProvider, - renderer: renderer, - terminal: term, - soundEngine: d2audio.NewSoundEngine(audioProvider, term), - uiManager: d2ui.NewUIManager(renderer, inputManager, audioProvider), + mapRenderer: d2maprenderer.CreateMapRenderer(asset, renderer, + gameClient.MapEngine, term, startX, startY), + escapeMenu: NewEscapeMenu(navigator, renderer, audioProvider), + inputManager: inputManager, + audioProvider: audioProvider, + renderer: renderer, + terminal: term, + soundEngine: d2audio.NewSoundEngine(audioProvider, term), + uiManager: ui, } result.soundEnv = d2audio.NewSoundEnvironment(result.soundEngine) @@ -135,12 +143,13 @@ func (v *Game) OnLoad(_ d2screen.LoadingState) { v.terminal.OutputErrorf("no monstat entry for \"%s\"", name) return } - var monster *d2mapentity.NPC - monster, err = d2mapentity.NewNPC(x, y, monstat, 0) + + monster, err := v.gameClient.MapEngine.NewNPC(x, y, monstat, 0) if err != nil { v.terminal.OutputErrorf("error generating monster \"%s\": %v", name, err) return } + v.gameClient.MapEngine.AddEntity(monster) }, ) @@ -266,7 +275,7 @@ func (v *Game) bindGameControls() error { v.localPlayer = player var err error - v.gameControls, err = d2player.NewGameControls(v.renderer, player, + v.gameControls, err = d2player.NewGameControls(v.asset, v.renderer, player, v.gameClient.MapEngine, v.mapRenderer, v, v.terminal, v.uiManager, v.gameClient.IsSinglePlayer()) if err != nil { diff --git a/d2game/d2gamescreen/main_menu.go b/d2game/d2gamescreen/main_menu.go index 323d1ae3..5823499d 100644 --- a/d2game/d2gamescreen/main_menu.go +++ b/d2game/d2gamescreen/main_menu.go @@ -111,6 +111,7 @@ type MainMenu struct { screenMode mainMenuScreenMode leftButtonHeld bool + asset *d2asset.AssetManager inputManager d2interface.InputManager renderer d2interface.Renderer audioProvider d2interface.AudioProvider @@ -124,6 +125,7 @@ type MainMenu struct { // CreateMainMenu creates an instance of MainMenu func CreateMainMenu( navigator Navigator, + asset *d2asset.AssetManager, renderer d2interface.Renderer, inputManager d2interface.InputManager, audioProvider d2interface.AudioProvider, @@ -131,6 +133,7 @@ func CreateMainMenu( buildInfo BuildInfo, ) *MainMenu { return &MainMenu{ + asset: asset, screenMode: ScreenModeUnknown, leftButtonHeld: true, renderer: renderer, @@ -169,19 +172,19 @@ func (v *MainMenu) OnLoad(loading d2screen.LoadingState) { } func (v *MainMenu) loadBackgroundSprites() { - animation, _ := d2asset.LoadAnimation(d2resource.GameSelectScreen, d2resource.PaletteSky) + animation, _ := v.asset.LoadAnimation(d2resource.GameSelectScreen, d2resource.PaletteSky) v.background, _ = v.uiManager.NewSprite(animation) v.background.SetPosition(backgroundX, backgroundY) - animation, _ = d2asset.LoadAnimation(d2resource.TrademarkScreen, d2resource.PaletteSky) + animation, _ = v.asset.LoadAnimation(d2resource.TrademarkScreen, d2resource.PaletteSky) v.trademarkBackground, _ = v.uiManager.NewSprite(animation) v.trademarkBackground.SetPosition(backgroundX, backgroundY) - animation, _ = d2asset.LoadAnimation(d2resource.TCPIPBackground, d2resource.PaletteSky) + animation, _ = v.asset.LoadAnimation(d2resource.TCPIPBackground, d2resource.PaletteSky) v.tcpIPBackground, _ = v.uiManager.NewSprite(animation) v.tcpIPBackground.SetPosition(backgroundX, backgroundY) - animation, _ = d2asset.LoadAnimation(d2resource.PopUpOkCancel, d2resource.PaletteFechar) + animation, _ = v.asset.LoadAnimation(d2resource.PopUpOkCancel, d2resource.PaletteFechar) v.serverIPBackground, _ = v.uiManager.NewSprite(animation) v.serverIPBackground.SetPosition(serverIPbackgroundX, serverIPbackgroundY) } @@ -233,24 +236,24 @@ func (v *MainMenu) createLabels(loading d2screen.LoadingState) { } func (v *MainMenu) createLogos(loading d2screen.LoadingState) { - animation, _ := d2asset.LoadAnimation(d2resource.Diablo2LogoFireLeft, d2resource.PaletteUnits) + animation, _ := v.asset.LoadAnimation(d2resource.Diablo2LogoFireLeft, d2resource.PaletteUnits) v.diabloLogoLeft, _ = v.uiManager.NewSprite(animation) v.diabloLogoLeft.SetEffect(d2enum.DrawEffectModulate) v.diabloLogoLeft.PlayForward() v.diabloLogoLeft.SetPosition(diabloLogoX, diabloLogoY) loading.Progress(sixtyPercent) - animation, _ = d2asset.LoadAnimation(d2resource.Diablo2LogoFireRight, d2resource.PaletteUnits) + animation, _ = v.asset.LoadAnimation(d2resource.Diablo2LogoFireRight, d2resource.PaletteUnits) v.diabloLogoRight, _ = v.uiManager.NewSprite(animation) v.diabloLogoRight.SetEffect(d2enum.DrawEffectModulate) v.diabloLogoRight.PlayForward() v.diabloLogoRight.SetPosition(diabloLogoX, diabloLogoY) - animation, _ = d2asset.LoadAnimation(d2resource.Diablo2LogoBlackLeft, d2resource.PaletteUnits) + animation, _ = v.asset.LoadAnimation(d2resource.Diablo2LogoBlackLeft, d2resource.PaletteUnits) v.diabloLogoLeftBack, _ = v.uiManager.NewSprite(animation) v.diabloLogoLeftBack.SetPosition(diabloLogoX, diabloLogoY) - animation, _ = d2asset.LoadAnimation(d2resource.Diablo2LogoBlackRight, d2resource.PaletteUnits) + animation, _ = v.asset.LoadAnimation(d2resource.Diablo2LogoBlackRight, d2resource.PaletteUnits) v.diabloLogoRightBack, _ = v.uiManager.NewSprite(animation) v.diabloLogoRightBack.SetPosition(diabloLogoX, diabloLogoY) } diff --git a/d2game/d2gamescreen/map_engine_testing.go b/d2game/d2gamescreen/map_engine_testing.go index f05f7c36..c61a9f1a 100644 --- a/d2game/d2gamescreen/map_engine_testing.go +++ b/d2game/d2gamescreen/map_engine_testing.go @@ -7,17 +7,14 @@ import ( "strings" "time" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" - - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2math/d2vector" - - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapgen" - - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapengine" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2maprenderer" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2math/d2vector" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapengine" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapgen" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2maprenderer" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2screen" "github.com/OpenDiablo2/OpenDiablo2/d2game/d2player" ) @@ -87,6 +84,7 @@ func getRegions() []regionSpec { // MapEngineTest represents the MapEngineTest screen type MapEngineTest struct { + asset *d2asset.AssetManager gameState *d2player.PlayerState mapEngine *d2mapengine.MapEngine mapRenderer *d2maprenderer.MapRenderer @@ -112,6 +110,7 @@ type MapEngineTest struct { // CreateMapEngineTest creates the Map Engine Test screen and returns a pointer to it func CreateMapEngineTest(currentRegion, levelPreset int, + asset *d2asset.AssetManager, term d2interface.Terminal, renderer d2interface.Renderer, inputManager d2interface.InputManager, @@ -124,6 +123,7 @@ func CreateMapEngineTest(currentRegion, fileIndex: 0, regionSpec: regionSpec{}, filesCount: 0, + asset: asset, terminal: term, renderer: renderer, inputManager: inputManager, @@ -171,7 +171,7 @@ func (met *MapEngineTest) loadRegionByIndex(n, levelPreset, fileIndex int) { met.mapEngine.SetSeed(time.Now().UnixNano()) d2mapgen.GenerateAct1Overworld(met.mapEngine) } else { - met.mapEngine = d2mapengine.CreateMapEngine() // necessary for map name update + met.mapEngine = d2mapengine.CreateMapEngine(met.asset) // necessary for map name update met.mapEngine.SetSeed(time.Now().UnixNano()) met.mapEngine.GenerateMap(d2enum.RegionIdType(n), levelPreset, fileIndex) } @@ -193,11 +193,12 @@ func (met *MapEngineTest) OnLoad(loading d2screen.LoadingState) { loading.Progress(twentyPercent) - met.mapEngine = d2mapengine.CreateMapEngine() + met.mapEngine = d2mapengine.CreateMapEngine(met.asset) loading.Progress(fiftyPercent) - met.mapRenderer = d2maprenderer.CreateMapRenderer(met.renderer, met.mapEngine, met.terminal, 0.0, 0.0) + met.mapRenderer = d2maprenderer.CreateMapRenderer(met.asset, met.renderer, met.mapEngine, + met.terminal, 0.0, 0.0) loading.Progress(seventyPercent) met.loadRegionByIndex(met.currentRegion, met.levelPreset, met.fileIndex) diff --git a/d2game/d2gamescreen/select_hero_class.go b/d2game/d2gamescreen/select_hero_class.go index 61a5096d..b0f4b894 100644 --- a/d2game/d2gamescreen/select_hero_class.go +++ b/d2game/d2gamescreen/select_hero_class.go @@ -4,13 +4,12 @@ import ( "fmt" "image" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2tbl" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2util" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2tbl" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2util" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2screen" @@ -269,6 +268,7 @@ func (hri *HeroRenderInfo) advance(elapsed float64) { // SelectHeroClass represents the Select Hero Class screen type SelectHeroClass struct { + asset *d2asset.AssetManager uiManager *d2ui.UIManager bgImage *d2ui.Sprite campfire *d2ui.Sprite @@ -298,6 +298,7 @@ type SelectHeroClass struct { // CreateSelectHeroClass creates an instance of a SelectHeroClass func CreateSelectHeroClass( navigator Navigator, + manager *d2asset.AssetManager, renderer d2interface.Renderer, audioProvider d2interface.AudioProvider, ui *d2ui.UIManager, @@ -732,7 +733,7 @@ func (v *SelectHeroClass) loadSprite(animationPath string, position image.Point, return nil } - animation, err := d2asset.LoadAnimation(animationPath, d2resource.PaletteFechar) + animation, err := v.asset.LoadAnimation(animationPath, d2resource.PaletteFechar) if err != nil { fmt.Printf("could not load animation: %s\n", animationPath) return nil diff --git a/d2game/d2player/game_controls.go b/d2game/d2player/game_controls.go index 1e8883b2..d4137e0b 100644 --- a/d2game/d2player/game_controls.go +++ b/d2game/d2player/game_controls.go @@ -47,6 +47,7 @@ const ( // GameControls represents the game's controls on the screen type GameControls struct { actionableRegions []ActionableRegion + asset *d2asset.AssetManager renderer d2interface.Renderer // TODO: This shouldn't be a dependency inputListener InputCallbackListener hero *d2mapentity.Player @@ -103,6 +104,7 @@ const ( // NewGameControls creates a GameControls instance and returns a pointer to it func NewGameControls( + asset *d2asset.AssetManager, renderer d2interface.Renderer, hero *d2mapentity.Player, mapEngine *d2mapengine.MapEngine, @@ -161,16 +163,17 @@ func NewGameControls( globeStatsLabel := hpManaStatsLabel gc := &GameControls{ + asset: asset, uiManager: ui, renderer: renderer, hero: hero, mapEngine: mapEngine, inputListener: inputListener, mapRenderer: mapRenderer, - inventory: NewInventory(ui, inventoryRecord), - heroStatsPanel: NewHeroStatsPanel(ui, hero.Name(), hero.Class, hero.Stats), - helpOverlay: help.NewHelpOverlay(renderer), - miniPanel: newMiniPanel(ui, isSinglePlayer), + inventory: NewInventory(asset, ui, inventoryRecord), + heroStatsPanel: NewHeroStatsPanel(asset, ui, hero.Name(), hero.Class, hero.Stats), + helpOverlay: help.NewHelpOverlay(asset, renderer, ui), + miniPanel: newMiniPanel(asset, ui, isSinglePlayer), missileID: missileID, nameLabel: hoverLabel, zoneChangeText: zoneLabel, @@ -378,20 +381,20 @@ func (g *GameControls) OnMouseButtonDown(event d2interface.MouseEvent) bool { // Load loads the resources required for the GameControls func (g *GameControls) Load() { - animation, _ := d2asset.LoadAnimation(d2resource.GameGlobeOverlap, d2resource.PaletteSky) + animation, _ := g.asset.LoadAnimation(d2resource.GameGlobeOverlap, d2resource.PaletteSky) g.globeSprite, _ = g.uiManager.NewSprite(animation) - animation, _ = d2asset.LoadAnimation(d2resource.HealthManaIndicator, d2resource.PaletteSky) + animation, _ = g.asset.LoadAnimation(d2resource.HealthManaIndicator, d2resource.PaletteSky) g.hpManaStatusSprite, _ = g.uiManager.NewSprite(animation) - animation, _ = d2asset.LoadAnimation(d2resource.GamePanels, d2resource.PaletteSky) + animation, _ = g.asset.LoadAnimation(d2resource.GamePanels, d2resource.PaletteSky) g.mainPanel, _ = g.uiManager.NewSprite(animation) - animation, _ = d2asset.LoadAnimation(d2resource.MenuButton, d2resource.PaletteSky) + animation, _ = g.asset.LoadAnimation(d2resource.MenuButton, d2resource.PaletteSky) _ = animation.SetCurrentFrame(2) g.menuButton, _ = g.uiManager.NewSprite(animation) - animation, _ = d2asset.LoadAnimation(d2resource.GenericSkills, d2resource.PaletteSky) + animation, _ = g.asset.LoadAnimation(d2resource.GenericSkills, d2resource.PaletteSky) g.skillIcon, _ = g.uiManager.NewSprite(animation) g.loadUIButtons() diff --git a/d2game/d2player/help/help.go b/d2game/d2player/help/help.go index 568310c7..5a204765 100644 --- a/d2game/d2player/help/help.go +++ b/d2game/d2player/help/help.go @@ -2,9 +2,10 @@ package help import ( "fmt" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2util" "image/color" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2util" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" @@ -25,6 +26,7 @@ const ( // HelpOverlay represents the in-game overlay that toggles visibility when the h key is pressed type Overlay struct { + asset *d2asset.AssetManager isOpen bool renderer d2interface.Renderer frames []*d2ui.Sprite @@ -36,9 +38,12 @@ type Overlay struct { layout *d2gui.Layout } -func NewHelpOverlay(renderer d2interface.Renderer) *Overlay { +func NewHelpOverlay(asset *d2asset.AssetManager, renderer d2interface.Renderer, + ui *d2ui.UIManager) *Overlay { h := &Overlay{ - renderer: renderer, + asset: asset, + renderer: renderer, + uiManager: ui, } return h @@ -89,7 +94,7 @@ func (h *Overlay) Load() { prevY = 0 ) for frameIndex := 0; frameIndex < 7; frameIndex++ { - animation, _ := d2asset.LoadAnimation(d2resource.HelpBorder, d2resource.PaletteSky) + animation, _ := h.asset.LoadAnimation(d2resource.HelpBorder, d2resource.PaletteSky) _ = animation.SetCurrentFrame(frameIndex) f, _ := h.uiManager.NewSprite(animation) @@ -139,7 +144,7 @@ func (h *Overlay) Load() { // Close - anim, _ := d2asset.LoadAnimation(d2resource.SquareButton, d2resource.PaletteSky) + anim, _ := h.asset.LoadAnimation(d2resource.SquareButton, d2resource.PaletteSky) close, _ := h.uiManager.NewSprite(anim) _ = close.SetCurrentFrame(0) close.SetPosition(685, 57) @@ -339,7 +344,7 @@ func (h *Overlay) createBullet(c callout) { newLabel.SetPosition(c.LabelX, c.LabelY) h.text = append(h.text, newLabel) - anim, _ := d2asset.LoadAnimation(d2resource.HelpYellowBullet, d2resource.PaletteSky) + anim, _ := h.asset.LoadAnimation(d2resource.HelpYellowBullet, d2resource.PaletteSky) newDot, _ := h.uiManager.NewSprite(anim) _ = newDot.SetCurrentFrame(0) newDot.SetPosition(c.DotX, c.DotY+14) @@ -365,7 +370,7 @@ func (h *Overlay) createCallout(c callout) { } h.lines = append(h.lines, l) - anim, _ := d2asset.LoadAnimation(d2resource.HelpWhiteBullet, d2resource.PaletteSky) + anim, _ := h.asset.LoadAnimation(d2resource.HelpWhiteBullet, d2resource.PaletteSky) newDot, _ := h.uiManager.NewSprite(anim) _ = newDot.SetCurrentFrame(0) newDot.SetPosition(c.DotX, c.DotY) diff --git a/d2game/d2player/hero_stats_panel.go b/d2game/d2player/hero_stats_panel.go index 10c16c2f..7dcbc0b8 100644 --- a/d2game/d2player/hero_stats_panel.go +++ b/d2game/d2player/hero_stats_panel.go @@ -41,6 +41,7 @@ type StatsPanelLabels struct { // HeroStatsPanel represents the hero status panel type HeroStatsPanel struct { + asset *d2asset.AssetManager uiManager *d2ui.UIManager frame *d2ui.Sprite panel *d2ui.Sprite @@ -57,12 +58,13 @@ type HeroStatsPanel struct { } // NewHeroStatsPanel creates a new hero status panel -func NewHeroStatsPanel(ui *d2ui.UIManager, heroName string, heroClass d2enum.Hero, +func NewHeroStatsPanel(asset *d2asset.AssetManager, ui *d2ui.UIManager, heroName string, heroClass d2enum.Hero, heroState *d2hero.HeroStatsState) *HeroStatsPanel { originX := 0 originY := 0 return &HeroStatsPanel{ + asset: asset, uiManager: ui, renderer: ui.Renderer(), originX: originX, @@ -76,9 +78,9 @@ func NewHeroStatsPanel(ui *d2ui.UIManager, heroName string, heroClass d2enum.Her // Load loads the data for the hero status panel func (s *HeroStatsPanel) Load() { - animation, _ := d2asset.LoadAnimation(d2resource.Frame, d2resource.PaletteSky) + animation, _ := s.asset.LoadAnimation(d2resource.Frame, d2resource.PaletteSky) s.frame, _ = s.uiManager.NewSprite(animation) - animation, _ = d2asset.LoadAnimation(d2resource.InventoryCharacterPanel, d2resource.PaletteSky) + animation, _ = s.asset.LoadAnimation(d2resource.InventoryCharacterPanel, d2resource.PaletteSky) s.panel, _ = s.uiManager.NewSprite(animation) s.initStatValueLabels() } diff --git a/d2game/d2player/inventory.go b/d2game/d2player/inventory.go index ddfacc8c..9a4e067a 100644 --- a/d2game/d2player/inventory.go +++ b/d2game/d2player/inventory.go @@ -16,6 +16,7 @@ import ( // Inventory represents the inventory type Inventory struct { + asset *d2asset.AssetManager uiManager *d2ui.UIManager frame *d2ui.Sprite panel *d2ui.Sprite @@ -32,13 +33,15 @@ type Inventory struct { } // NewInventory creates an inventory instance and returns a pointer to it -func NewInventory(ui *d2ui.UIManager, record *d2datadict.InventoryRecord) *Inventory { +func NewInventory(asset *d2asset.AssetManager, ui *d2ui.UIManager, + record *d2datadict.InventoryRecord) *Inventory { hoverLabel := ui.NewLabel(d2resource.FontFormal11, d2resource.PaletteStatic) hoverLabel.Alignment = d2gui.HorizontalAlignCenter return &Inventory{ + asset: asset, uiManager: ui, - grid: NewItemGrid(ui, record), + grid: NewItemGrid(asset, ui, record), originX: record.Panel.Left, hoverLabel: hoverLabel, // originY: record.Panel.Top, @@ -68,10 +71,10 @@ func (g *Inventory) Close() { // Load loads the resources required by the inventory func (g *Inventory) Load() { - animation, _ := d2asset.LoadAnimation(d2resource.Frame, d2resource.PaletteSky) + animation, _ := g.asset.LoadAnimation(d2resource.Frame, d2resource.PaletteSky) g.frame, _ = g.uiManager.NewSprite(animation) - animation, _ = d2asset.LoadAnimation(d2resource.InventoryCharacterPanel, d2resource.PaletteSky) + animation, _ = g.asset.LoadAnimation(d2resource.InventoryCharacterPanel, d2resource.PaletteSky) g.panel, _ = g.uiManager.NewSprite(animation) items := []InventoryItem{ diablo2item.NewItem("kit", "Crimson", "of the Bat", "of Frost").Identify(), diff --git a/d2game/d2player/inventory_grid.go b/d2game/d2player/inventory_grid.go index a993cf9b..85b06c4e 100644 --- a/d2game/d2player/inventory_grid.go +++ b/d2game/d2player/inventory_grid.go @@ -33,6 +33,7 @@ var errorInventoryFull = errors.New("inventory full") // ItemGrid is a reusable grid for use with player and merchant inventory. // Handles layout and rendering item icons based on code. type ItemGrid struct { + asset *d2asset.AssetManager uiManager *d2ui.UIManager items []InventoryItem equipmentSlots map[d2enum.EquippedSlot]EquipmentSlot @@ -44,10 +45,12 @@ type ItemGrid struct { slotSize int } -func NewItemGrid(ui *d2ui.UIManager, record *d2datadict.InventoryRecord) *ItemGrid { +func NewItemGrid(asset *d2asset.AssetManager, ui *d2ui.UIManager, + record *d2datadict.InventoryRecord) *ItemGrid { grid := record.Grid return &ItemGrid{ + asset: asset, uiManager: ui, width: grid.Box.Width, height: grid.Box.Height, @@ -118,7 +121,7 @@ func (g *ItemGrid) loadItem(item InventoryItem) { var itemSprite *d2ui.Sprite // TODO: Put the pattern into D2Shared - animation, err := d2asset.LoadAnimation( + animation, err := g.asset.LoadAnimation( fmt.Sprintf("/data/global/items/inv%s.dc6", item.GetItemCode()), d2resource.PaletteSky, ) diff --git a/d2game/d2player/mini_panel.go b/d2game/d2player/mini_panel.go index 56759138..3b6d84e4 100644 --- a/d2game/d2player/mini_panel.go +++ b/d2game/d2player/mini_panel.go @@ -9,6 +9,7 @@ import ( ) type miniPanel struct { + asset *d2asset.AssetManager container *d2ui.Sprite button *d2ui.Sprite isOpen bool @@ -16,16 +17,16 @@ type miniPanel struct { rectangle d2geom.Rectangle } -func newMiniPanel(uiManager *d2ui.UIManager, isSinglePlayer bool) *miniPanel { +func newMiniPanel(asset *d2asset.AssetManager, uiManager *d2ui.UIManager, isSinglePlayer bool) *miniPanel { miniPanelContainerPath := d2resource.Minipanel if isSinglePlayer { miniPanelContainerPath = d2resource.MinipanelSmall } - animation, _ := d2asset.LoadAnimation(miniPanelContainerPath, d2resource.PaletteSky) + animation, _ := asset.LoadAnimation(miniPanelContainerPath, d2resource.PaletteSky) containerSprite, _ := uiManager.NewSprite(animation) - animation, _ = d2asset.LoadAnimation(d2resource.MinipanelButton, d2resource.PaletteSky) + animation, _ = asset.LoadAnimation(d2resource.MinipanelButton, d2resource.PaletteSky) buttonSprite, _ := uiManager.NewSprite(animation) rectangle := d2geom.Rectangle{Left: 325, Top: 526, Width: 156, Height: 26} @@ -34,7 +35,14 @@ func newMiniPanel(uiManager *d2ui.UIManager, isSinglePlayer bool) *miniPanel { rectangle.Width = 182 } - return &miniPanel{container: containerSprite, button: buttonSprite, isOpen: true, isSinglePlayer: isSinglePlayer, rectangle: rectangle} + return &miniPanel{ + asset: asset, + container: containerSprite, + button: buttonSprite, + isOpen: true, + isSinglePlayer: isSinglePlayer, + rectangle: rectangle, + } } func (m *miniPanel) IsOpen() bool { diff --git a/d2networking/d2client/d2localclient/local_client_connection.go b/d2networking/d2client/d2localclient/local_client_connection.go index e5fd2a65..2f875d5d 100644 --- a/d2networking/d2client/d2localclient/local_client_connection.go +++ b/d2networking/d2client/d2localclient/local_client_connection.go @@ -1,6 +1,7 @@ package d2localclient import ( + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" uuid "github.com/satori/go.uuid" "github.com/OpenDiablo2/OpenDiablo2/d2game/d2player" @@ -13,6 +14,7 @@ import ( // LocalClientConnection is the implementation of ClientConnection // for a local client. type LocalClientConnection struct { + asset *d2asset.AssetManager clientListener d2networking.ClientListener // The game client uniqueID string // Unique ID generated on construction openNetworkServer bool // True if this is a server @@ -38,8 +40,9 @@ func (l *LocalClientConnection) SendPacketToClient(packet d2netpacket.NetPacket) // Create constructs a new LocalClientConnection and returns // a pointer to it. -func Create(openNetworkServer bool) *LocalClientConnection { +func Create(asset *d2asset.AssetManager, openNetworkServer bool) *LocalClientConnection { result := &LocalClientConnection{ + asset: asset, uniqueID: uuid.NewV4().String(), openNetworkServer: openNetworkServer, } @@ -53,7 +56,7 @@ func (l *LocalClientConnection) Open(_, saveFilePath string) error { l.SetPlayerState(d2player.LoadPlayerState(saveFilePath)) - l.gameServer, err = d2server.NewGameServer(l.openNetworkServer, 30) + l.gameServer, err = d2server.NewGameServer(l.asset, l.openNetworkServer, 30) if err != nil { return err } diff --git a/d2networking/d2client/game_client.go b/d2networking/d2client/game_client.go index f7eb5ee4..97dbc593 100644 --- a/d2networking/d2client/game_client.go +++ b/d2networking/d2client/game_client.go @@ -5,6 +5,8 @@ import ( "log" "os" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2math" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict" @@ -31,6 +33,7 @@ const ( type GameClient struct { clientConnection ServerConnection // Abstract local/remote connection connectionType d2clientconnectiontype.ClientConnectionType // Type of connection (local or remote) + asset *d2asset.AssetManager scriptEngine *d2script.ScriptEngine GameState *d2player.PlayerState // local player state MapEngine *d2mapengine.MapEngine // Map and entities @@ -41,9 +44,11 @@ type GameClient struct { } // Create constructs a new GameClient and returns a pointer to it. -func Create(connectionType d2clientconnectiontype.ClientConnectionType, scriptEngine *d2script.ScriptEngine) (*GameClient, error) { +func Create(connectionType d2clientconnectiontype.ClientConnectionType, + asset *d2asset.AssetManager, scriptEngine *d2script.ScriptEngine) (*GameClient, error) { result := &GameClient{ - MapEngine: d2mapengine.CreateMapEngine(), // TODO: Mapgen - Needs levels.txt stuff + asset: asset, + MapEngine: d2mapengine.CreateMapEngine(asset), // TODO: Mapgen - Needs levels.txt stuff Players: make(map[string]*d2mapentity.Player), connectionType: connectionType, scriptEngine: scriptEngine, @@ -53,9 +58,9 @@ func Create(connectionType d2clientconnectiontype.ClientConnectionType, scriptEn case d2clientconnectiontype.LANClient: result.clientConnection = d2remoteclient.Create() case d2clientconnectiontype.LANServer: - result.clientConnection = d2localclient.Create(true) + result.clientConnection = d2localclient.Create(asset, true) case d2clientconnectiontype.Local: - result.clientConnection = d2localclient.Create(false) + result.clientConnection = d2localclient.Create(asset, false) default: return nil, fmt.Errorf("unknown client connection type specified: %d", connectionType) } @@ -180,7 +185,7 @@ func (g *GameClient) handleAddPlayerPacket(packet d2netpacket.NetPacket) error { return err } - newPlayer := d2mapentity.NewPlayer(player.ID, player.Name, player.X, player.Y, 0, + newPlayer := g.MapEngine.NewPlayer(player.ID, player.Name, player.X, player.Y, 0, player.HeroType, player.Stats, &player.Equipment) g.Players[newPlayer.ID()] = newPlayer @@ -195,7 +200,7 @@ func (g *GameClient) handleSpawnItemPacket(packet d2netpacket.NetPacket) error { return err } - itemEntity, err := d2mapentity.NewItem(item.X, item.Y, item.Codes...) + itemEntity, err := g.MapEngine.NewItem(item.X, item.Y, item.Codes...) if err == nil { g.MapEngine.AddEntity(itemEntity) @@ -255,7 +260,7 @@ func (g *GameClient) handleCastSkillPacket(packet d2netpacket.NetPacket) error { player.ClearPath() // currently hardcoded to missile skill - missile, err := d2mapentity.NewMissile( + missile, err := g.MapEngine.NewMissile( int(player.Position.X()), int(player.Position.Y()), d2datadict.Missiles[playerCast.SkillID], diff --git a/d2networking/d2server/game_server.go b/d2networking/d2server/game_server.go index 0517fff8..cef44ea3 100644 --- a/d2networking/d2server/game_server.go +++ b/d2networking/d2server/game_server.go @@ -13,6 +13,7 @@ import ( "github.com/robertkrimen/otto" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapengine" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapgen" "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket" @@ -42,6 +43,7 @@ type GameServer struct { networkServer bool ctx context.Context cancel context.CancelFunc + asset *d2asset.AssetManager mapEngines []*d2mapengine.MapEngine scriptEngine *d2script.ScriptEngine seed int64 @@ -57,7 +59,9 @@ var singletonServer *GameServer // ctx: required context item // networkServer: true = 0.0.0.0 | false = 127.0.0.1 // maxConnections (default: 8): maximum number of TCP connections allowed open -func NewGameServer(networkServer bool, maxConnections ...int) (*GameServer, error) { +func NewGameServer(asset *d2asset.AssetManager, networkServer bool, + maxConnections ...int) (*GameServer, + error) { if len(maxConnections) == 0 { maxConnections = []int{8} } @@ -67,6 +71,7 @@ func NewGameServer(networkServer bool, maxConnections ...int) (*GameServer, erro gameServer := &GameServer{ ctx: ctx, cancel: cancel, + asset: asset, connections: make(map[string]ClientConnection), networkServer: networkServer, maxConnections: maxConnections[0], @@ -78,7 +83,7 @@ func NewGameServer(networkServer bool, maxConnections ...int) (*GameServer, erro // TODO: In order to support dedicated mode we need to load the levels txt and files. Revisit this once this we can // load files independent of the app. - mapEngine := d2mapengine.CreateMapEngine() + mapEngine := d2mapengine.CreateMapEngine(asset) mapEngine.SetSeed(gameServer.seed) mapEngine.ResetMap(d2enum.RegionAct1Town, 100, 100) // TODO: Mapgen - Needs levels.txt stuff d2mapgen.GenerateAct1Overworld(mapEngine) diff --git a/main.go b/main.go index dfce2c79..94f1c925 100644 --- a/main.go +++ b/main.go @@ -4,6 +4,7 @@ import ( "log" "github.com/OpenDiablo2/OpenDiablo2/d2app" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" ebiten2 "github.com/OpenDiablo2/OpenDiablo2/d2core/d2audio/ebiten" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2config" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2input" @@ -28,13 +29,18 @@ func main() { panic(err) } - // Initialize our providers + // NewAssetManager our providers renderer, err := ebiten.CreateRenderer() if err != nil { panic(err) } - audio, err := ebiten2.CreateAudio() + asset, err := d2asset.NewAssetManager(renderer, nil) + if err != nil { + panic(err) + } + + audio, err := ebiten2.CreateAudio(asset) if err != nil { panic(err) } @@ -46,9 +52,11 @@ func main() { log.Fatal(err) } + asset.BindTerminalCommands(term) + scriptEngine := d2script.CreateScriptEngine() - app := d2app.Create(GitBranch, GitCommit, inputManager, term, scriptEngine, audio, renderer) + app := d2app.Create(GitBranch, GitCommit, inputManager, term, scriptEngine, audio, renderer, asset) if err := app.Run(); err != nil { log.Fatal(err) }