remove d2asset singleton (#726)

* export d2asset singleton

* add *d2asset.AssetManager to d2app

- d2app now has a reference to an asset manager which it will use for loading
- added asset loader methods to the asset manager
- functions in d2asset are now wrappers for asset manager methods

* add asset manager reference to audio provider

- d2app asset manager reference is now passed to audio provider
- asset manager is created in main.go for now to pass into audio provider
- CreateSoundEffect is now a method, no longer exported, uses the asset manager reference

* d2app passes asset manager refence to map engine test

* in d2asset, all calls to LoadFile replaced with call to Singleton.Loadfile

* blizzard intro and credits screen

- d2app passes reference to the asset manager to these screens

* asset manager for d2map

- adding MapStampFactory, takes an asset manager reference
- embedded MapStampFactory into the MapEngine
- LoadStamp is now a method of the MapStampFactory

* d2asset: removed LoadFileStream, LoadFile, and FileExists

* d2gui changes

- singleton now has an asset manager reference
- calls to d2asset loader functions removed
- createButton is now a method of LayoutManager
- moved LayoutEntry to its own file

* map entity factory

- Map engine has an embedded map entity factory
- Map stamp factory gets a reference to the map engine's entity factory
- Stamps are given a reference to the map engine entity factory when created
- Character select gets a map entity factory
- Embedded the stamp factory into the MapEngine

* asset manager for d2ui

- d2ui is passed an asset manager reference when created
- all calls to d2asset loader functions in d2ui now refer to the asset manager
- d2gamescreen gets a ui manager when created
- help overlay is now passed a ui manager when created

* d2gamescreen + d2player: asset manager references

added an asset manager reference to
- inventory panel + inventory grid
- mini panel
- game controls
- help overlay
- character select
- main menu
- select hero class
- hero stats panel

* Removed d2asset.LoadAnimation

all references to this function have been replaced with calls to the asset manager method

* adding asset to help overlay, bugfix for 4d59c91

* Removed d2asset.LoadFont and d2asset.LoadAnimationWithEffect

all references to these have been replaced with calls to the asset manager methods

* MapRenderer now gets an asset manager reference

* removed d2asset.LoadPalette

all references have been replaced with calls to an asset manager instance

* merged d2object with d2mapentity

d2object was only being used to create objects in the map, so the provider
function is now a method of the map entity factory. calls to d2asset have
been removed.

* removed d2asset.LoadComposite

all calls are now made to the asset manager method

* removed d2asset singleton

all singleton references have been removed, a single instance of the
asset manager is passed around the entire app

* rename Initialize to NewAssetManager
This commit is contained in:
lord 2020-09-12 13:51:30 -07:00 committed by GitHub
parent f4a71c72e4
commit 854fce3b14
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
55 changed files with 790 additions and 669 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
package d2object
package d2mapentity
import (
"math/rand"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

14
main.go
View File

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