mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2025-02-20 07:27:19 -05:00
Minor changes to project layout (#276)
* Minor changes to reduce interdependencies on modules.
This commit is contained in:
parent
6832a5a0db
commit
2461142fbd
@ -1,61 +0,0 @@
|
||||
package d2asset
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type animationManager struct {
|
||||
cache *cache
|
||||
}
|
||||
|
||||
func createAnimationManager() *animationManager {
|
||||
return &animationManager{cache: createCache(AnimationBudget)}
|
||||
}
|
||||
|
||||
func (sm *animationManager) loadAnimation(animationPath, palettePath string, transparency int) (*Animation, error) {
|
||||
cachePath := fmt.Sprintf("%s;%s;%d", animationPath, palettePath, transparency)
|
||||
if animation, found := sm.cache.retrieve(cachePath); found {
|
||||
return animation.(*Animation).clone(), nil
|
||||
}
|
||||
|
||||
var animation *Animation
|
||||
switch strings.ToLower(filepath.Ext(animationPath)) {
|
||||
case ".dc6":
|
||||
dc6, err := loadDC6(animationPath, palettePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
animation, err = createAnimationFromDC6(dc6)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case ".dcc":
|
||||
dcc, err := loadDCC(animationPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
palette, err := loadPalette(palettePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
animation, err = createAnimationFromDCC(dcc, palette, transparency)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, errors.New("unknown animation format")
|
||||
}
|
||||
|
||||
if err := sm.cache.insert(cachePath, animation.clone(), 1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return animation, nil
|
||||
}
|
@ -1,100 +0,0 @@
|
||||
package d2asset
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"path"
|
||||
"sync"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2corecommon"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2mpq"
|
||||
)
|
||||
|
||||
type archiveEntry struct {
|
||||
archivePath string
|
||||
hashEntryMap d2mpq.HashEntryMap
|
||||
}
|
||||
|
||||
type archiveManager struct {
|
||||
cache *cache
|
||||
config *d2corecommon.Configuration
|
||||
entries []archiveEntry
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
func createArchiveManager(config *d2corecommon.Configuration) *archiveManager {
|
||||
return &archiveManager{cache: createCache(ArchiveBudget), config: config}
|
||||
}
|
||||
|
||||
func (am *archiveManager) loadArchiveForFile(filePath string) (*d2mpq.MPQ, error) {
|
||||
am.mutex.Lock()
|
||||
defer am.mutex.Unlock()
|
||||
|
||||
if err := am.cacheArchiveEntries(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, archiveEntry := range am.entries {
|
||||
if archiveEntry.hashEntryMap.Contains(filePath) {
|
||||
return am.loadArchive(archiveEntry.archivePath)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.New("file not found")
|
||||
}
|
||||
|
||||
func (am *archiveManager) fileExistsInArchive(filePath string) (bool, error) {
|
||||
am.mutex.Lock()
|
||||
defer am.mutex.Unlock()
|
||||
|
||||
if err := am.cacheArchiveEntries(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, archiveEntry := range am.entries {
|
||||
if archiveEntry.hashEntryMap.Contains(filePath) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (am *archiveManager) loadArchive(archivePath string) (*d2mpq.MPQ, error) {
|
||||
if archive, found := am.cache.retrieve(archivePath); found {
|
||||
return archive.(*d2mpq.MPQ), nil
|
||||
}
|
||||
|
||||
archive, err := d2mpq.Load(archivePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := am.cache.insert(archivePath, archive, int(archive.Data.ArchiveSize)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return archive, nil
|
||||
}
|
||||
|
||||
func (am *archiveManager) cacheArchiveEntries() error {
|
||||
if len(am.entries) == len(am.config.MpqLoadOrder) {
|
||||
return nil
|
||||
}
|
||||
|
||||
am.entries = nil
|
||||
|
||||
for _, archiveName := range am.config.MpqLoadOrder {
|
||||
archivePath := path.Join(am.config.MpqPath, archiveName)
|
||||
archive, err := am.loadArchive(archivePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
am.entries = append(
|
||||
am.entries,
|
||||
archiveEntry{archivePath, archive.HashEntryMap},
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -1,181 +0,0 @@
|
||||
package d2asset
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2corecommon"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2cof"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2datadict"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2dc6"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2dcc"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2mpq"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2term"
|
||||
)
|
||||
|
||||
const (
|
||||
// In megabytes
|
||||
ArchiveBudget = 1024 * 1024 * 512
|
||||
FileBudget = 1024 * 1024 * 32
|
||||
|
||||
// In counts
|
||||
PaletteBudget = 64
|
||||
AnimationBudget = 64
|
||||
)
|
||||
|
||||
var (
|
||||
ErrHasInit error = errors.New("asset system is already initialized")
|
||||
ErrNoInit error = errors.New("asset system is not initialized")
|
||||
)
|
||||
|
||||
type assetManager struct {
|
||||
archiveManager *archiveManager
|
||||
fileManager *fileManager
|
||||
paletteManager *paletteManager
|
||||
animationManager *animationManager
|
||||
}
|
||||
|
||||
var singleton *assetManager
|
||||
|
||||
func Initialize(config *d2corecommon.Configuration) error {
|
||||
if singleton != nil {
|
||||
return ErrHasInit
|
||||
}
|
||||
|
||||
var (
|
||||
archiveManager = createArchiveManager(config)
|
||||
fileManager = createFileManager(config, archiveManager)
|
||||
paletteManager = createPaletteManager()
|
||||
animationManager = createAnimationManager()
|
||||
)
|
||||
|
||||
singleton = &assetManager{
|
||||
archiveManager,
|
||||
fileManager,
|
||||
paletteManager,
|
||||
animationManager,
|
||||
}
|
||||
|
||||
d2term.BindAction("assetspam", "display verbose asset manager logs", func(verbose bool) {
|
||||
if verbose {
|
||||
d2term.OutputInfo("asset manager verbose logging enabled")
|
||||
} else {
|
||||
d2term.OutputInfo("asset manager verbose logging disabled")
|
||||
}
|
||||
|
||||
archiveManager.cache.verbose = verbose
|
||||
fileManager.cache.verbose = verbose
|
||||
paletteManager.cache.verbose = verbose
|
||||
animationManager.cache.verbose = verbose
|
||||
})
|
||||
|
||||
d2term.BindAction("assetstat", "display asset manager cache statistics", func() {
|
||||
d2term.OutputInfo("archive cache: %f%%", float64(archiveManager.cache.weight)/float64(archiveManager.cache.budget)*100.0)
|
||||
d2term.OutputInfo("file cache: %f%%", float64(fileManager.cache.weight)/float64(fileManager.cache.budget)*100.0)
|
||||
d2term.OutputInfo("palette cache: %f%%", float64(paletteManager.cache.weight)/float64(paletteManager.cache.budget)*100.0)
|
||||
d2term.OutputInfo("animation cache: %f%%", float64(animationManager.cache.weight)/float64(animationManager.cache.budget)*100.0)
|
||||
})
|
||||
|
||||
d2term.BindAction("assetclear", "clear asset manager cache", func() {
|
||||
archiveManager.cache.clear()
|
||||
fileManager.cache.clear()
|
||||
paletteManager.cache.clear()
|
||||
animationManager.cache.clear()
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Shutdown() {
|
||||
singleton = nil
|
||||
}
|
||||
|
||||
func LoadArchive(archivePath string) (*d2mpq.MPQ, error) {
|
||||
if singleton == nil {
|
||||
return nil, ErrNoInit
|
||||
}
|
||||
|
||||
return singleton.archiveManager.loadArchive(archivePath)
|
||||
}
|
||||
|
||||
func LoadFile(filePath string) ([]byte, error) {
|
||||
if singleton == nil {
|
||||
return nil, ErrNoInit
|
||||
}
|
||||
|
||||
data, err := singleton.fileManager.loadFile(filePath)
|
||||
if err != nil {
|
||||
log.Printf("error loading file %s (%v)", filePath, err.Error())
|
||||
}
|
||||
|
||||
return data, err
|
||||
}
|
||||
|
||||
func FileExists(filePath string) (bool, error) {
|
||||
if singleton == nil {
|
||||
return false, ErrNoInit
|
||||
}
|
||||
|
||||
return singleton.fileManager.fileExists(filePath)
|
||||
}
|
||||
|
||||
func LoadAnimation(animationPath, palettePath string) (*Animation, error) {
|
||||
return LoadAnimationWithTransparency(animationPath, palettePath, 255)
|
||||
}
|
||||
|
||||
func LoadAnimationWithTransparency(animationPath, palettePath string, transparency int) (*Animation, error) {
|
||||
if singleton == nil {
|
||||
return nil, ErrNoInit
|
||||
}
|
||||
|
||||
return singleton.animationManager.loadAnimation(animationPath, palettePath, transparency)
|
||||
}
|
||||
|
||||
func LoadComposite(object *d2datadict.ObjectLookupRecord, palettePath string) (*Composite, error) {
|
||||
return createComposite(object, palettePath), nil
|
||||
}
|
||||
|
||||
func loadPalette(palettePath string) (*d2datadict.PaletteRec, error) {
|
||||
if singleton == nil {
|
||||
return nil, ErrNoInit
|
||||
}
|
||||
|
||||
return singleton.paletteManager.loadPalette(palettePath)
|
||||
}
|
||||
|
||||
func loadDC6(dc6Path, palettePath string) (*d2dc6.DC6File, error) {
|
||||
dc6Data, err := LoadFile(dc6Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
paletteData, err := loadPalette(palettePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dc6, err := d2dc6.LoadDC6(dc6Data, *paletteData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &dc6, nil
|
||||
}
|
||||
|
||||
func loadDCC(dccPath string) (*d2dcc.DCC, error) {
|
||||
dccData, err := LoadFile(dccPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return d2dcc.LoadDCC(dccData)
|
||||
}
|
||||
|
||||
func loadCOF(cofPath string) (*d2cof.COF, error) {
|
||||
cofData, err := LoadFile(cofPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return d2cof.LoadCOF(cofData)
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
package d2asset
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2corecommon"
|
||||
)
|
||||
|
||||
type fileManager struct {
|
||||
cache *cache
|
||||
archiveManager *archiveManager
|
||||
config *d2corecommon.Configuration
|
||||
}
|
||||
|
||||
func createFileManager(config *d2corecommon.Configuration, archiveManager *archiveManager) *fileManager {
|
||||
return &fileManager{createCache(FileBudget), archiveManager, config}
|
||||
}
|
||||
|
||||
func (fm *fileManager) loadFile(filePath string) ([]byte, error) {
|
||||
filePath = fm.fixupFilePath(filePath)
|
||||
if value, found := fm.cache.retrieve(filePath); found {
|
||||
return value.([]byte), nil
|
||||
}
|
||||
|
||||
archive, err := fm.archiveManager.loadArchiveForFile(filePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := archive.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := fm.cache.insert(filePath, data, len(data)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (fm *fileManager) fileExists(filePath string) (bool, error) {
|
||||
filePath = fm.fixupFilePath(filePath)
|
||||
return fm.archiveManager.fileExistsInArchive(filePath)
|
||||
}
|
||||
|
||||
func (fm *fileManager) fixupFilePath(filePath string) string {
|
||||
filePath = strings.ReplaceAll(filePath, "{LANG}", fm.config.Language)
|
||||
if strings.ToUpper(d2resource.LanguageCode) == "CHI" {
|
||||
filePath = strings.ReplaceAll(filePath, "{LANG_FONT}", fm.config.Language)
|
||||
} else {
|
||||
filePath = strings.ReplaceAll(filePath, "{LANG_FONT}", "latin")
|
||||
}
|
||||
|
||||
filePath = strings.ToLower(filePath)
|
||||
filePath = strings.ReplaceAll(filePath, `/`, "\\")
|
||||
filePath = strings.TrimPrefix(filePath, "\\")
|
||||
|
||||
return filePath
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
package d2asset
|
||||
|
||||
import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2datadict"
|
||||
)
|
||||
|
||||
type paletteManager struct {
|
||||
cache *cache
|
||||
}
|
||||
|
||||
func createPaletteManager() *paletteManager {
|
||||
return &paletteManager{createCache(PaletteBudget)}
|
||||
}
|
||||
|
||||
func (pm *paletteManager) loadPalette(palettePath string) (*d2datadict.PaletteRec, error) {
|
||||
if palette, found := pm.cache.retrieve(palettePath); found {
|
||||
return palette.(*d2datadict.PaletteRec), nil
|
||||
}
|
||||
|
||||
paletteData, err := LoadFile(palettePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
palette := d2datadict.CreatePalette("", paletteData)
|
||||
pm.cache.insert(palettePath, &palette, 1)
|
||||
return &palette, nil
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
package d2audio
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2asset"
|
||||
"github.com/hajimehoshi/ebiten/audio"
|
||||
"github.com/hajimehoshi/ebiten/audio/wav"
|
||||
)
|
||||
|
||||
// Manager provides sound
|
||||
type Manager struct {
|
||||
audioContext *audio.Context // The Audio context
|
||||
bgmAudio *audio.Player // The audio player
|
||||
lastBgm string
|
||||
sfxVolume float64
|
||||
bgmVolume float64
|
||||
}
|
||||
|
||||
// CreateManager creates a sound provider
|
||||
func CreateManager() *Manager {
|
||||
result := &Manager{}
|
||||
audioContext, err := audio.NewContext(44100)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
result.audioContext = audioContext
|
||||
return result
|
||||
}
|
||||
|
||||
// PlayBGM plays an infinitely looping background track
|
||||
func (v *Manager) PlayBGM(song string) {
|
||||
if v.lastBgm == song {
|
||||
return
|
||||
}
|
||||
v.lastBgm = song
|
||||
if song == "" && v.bgmAudio != nil && v.bgmAudio.IsPlaying() {
|
||||
_ = v.bgmAudio.Pause()
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
if v.bgmAudio != nil {
|
||||
err := v.bgmAudio.Close()
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
}
|
||||
audioData, err := d2asset.LoadFile(song)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
d, err := wav.Decode(v.audioContext, audio.BytesReadSeekCloser(audioData))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
s := audio.NewInfiniteLoop(d, d.Length())
|
||||
v.bgmAudio, err = audio.NewPlayer(v.audioContext, s)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
v.bgmAudio.SetVolume(v.bgmVolume)
|
||||
// Play the infinite-length stream. This never ends.
|
||||
err = v.bgmAudio.Rewind()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = v.bgmAudio.Play()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (v *Manager) LoadSoundEffect(sfx string) *SoundEffect {
|
||||
result := CreateSoundEffect(sfx, v.audioContext, v.sfxVolume)
|
||||
return result
|
||||
}
|
||||
|
||||
func (v *Manager) SetVolumes(bgmVolume, sfxVolume float64) {
|
||||
v.sfxVolume = sfxVolume
|
||||
v.bgmVolume = bgmVolume
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package d2asset
|
||||
package d2common
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@ -14,7 +14,7 @@ type cacheNode struct {
|
||||
weight int
|
||||
}
|
||||
|
||||
type cache struct {
|
||||
type Cache struct {
|
||||
head *cacheNode
|
||||
tail *cacheNode
|
||||
lookup map[string]*cacheNode
|
||||
@ -24,16 +24,27 @@ type cache struct {
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
func createCache(budget int) *cache {
|
||||
return &cache{lookup: make(map[string]*cacheNode), budget: budget}
|
||||
func CreateCache(budget int) *Cache {
|
||||
return &Cache{lookup: make(map[string]*cacheNode), budget: budget}
|
||||
}
|
||||
func (c *Cache) SetCacheVerbose(verbose bool) {
|
||||
c.verbose = verbose
|
||||
}
|
||||
|
||||
func (c *cache) insert(key string, value interface{}, weight int) error {
|
||||
func (c *Cache) GetWeight() int {
|
||||
return c.weight
|
||||
}
|
||||
|
||||
func (c *Cache) GetBudget() int {
|
||||
return c.budget
|
||||
}
|
||||
|
||||
func (c *Cache) Insert(key string, value interface{}, weight int) error {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
if _, found := c.lookup[key]; found {
|
||||
return errors.New("key already exists in cache")
|
||||
return errors.New("key already exists in Cache")
|
||||
}
|
||||
|
||||
node := &cacheNode{
|
||||
@ -61,7 +72,7 @@ func (c *cache) insert(key string, value interface{}, weight int) error {
|
||||
|
||||
if c.verbose {
|
||||
log.Printf(
|
||||
"warning -- cache is evicting %s (%d) for %s (%d); spare weight is now %d",
|
||||
"warning -- Cache is evicting %s (%d) for %s (%d); spare weight is now %d",
|
||||
c.tail.key,
|
||||
c.tail.weight,
|
||||
key,
|
||||
@ -76,7 +87,7 @@ func (c *cache) insert(key string, value interface{}, weight int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *cache) retrieve(key string) (interface{}, bool) {
|
||||
func (c *Cache) Retrieve(key string) (interface{}, bool) {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
@ -110,7 +121,7 @@ func (c *cache) retrieve(key string) (interface{}, bool) {
|
||||
return node.value, true
|
||||
}
|
||||
|
||||
func (c *cache) clear() {
|
||||
func (c *Cache) Clear() {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
46
d2common/composite_mode.go
Normal file
46
d2common/composite_mode.go
Normal file
@ -0,0 +1,46 @@
|
||||
package d2common
|
||||
|
||||
type CompositeMode int
|
||||
|
||||
const (
|
||||
// Regular alpha blending
|
||||
// c_out = c_src + c_dst × (1 - α_src)
|
||||
CompositeModeSourceOver CompositeMode = CompositeMode(1)
|
||||
|
||||
// c_out = 0
|
||||
CompositeModeClear CompositeMode = CompositeMode(2)
|
||||
|
||||
// c_out = c_src
|
||||
CompositeModeCopy CompositeMode = CompositeMode(3)
|
||||
|
||||
// c_out = c_dst
|
||||
CompositeModeDestination CompositeMode = CompositeMode(4)
|
||||
|
||||
// c_out = c_src × (1 - α_dst) + c_dst
|
||||
CompositeModeDestinationOver CompositeMode = CompositeMode(5)
|
||||
|
||||
// c_out = c_src × α_dst
|
||||
CompositeModeSourceIn CompositeMode = CompositeMode(6)
|
||||
|
||||
// c_out = c_dst × α_src
|
||||
CompositeModeDestinationIn CompositeMode = CompositeMode(7)
|
||||
|
||||
// c_out = c_src × (1 - α_dst)
|
||||
CompositeModeSourceOut CompositeMode = CompositeMode(8)
|
||||
|
||||
// c_out = c_dst × (1 - α_src)
|
||||
CompositeModeDestinationOut CompositeMode = CompositeMode(9)
|
||||
|
||||
// c_out = c_src × α_dst + c_dst × (1 - α_src)
|
||||
CompositeModeSourceAtop CompositeMode = CompositeMode(10)
|
||||
|
||||
// c_out = c_src × (1 - α_dst) + c_dst × α_src
|
||||
CompositeModeDestinationAtop CompositeMode = CompositeMode(11)
|
||||
|
||||
// c_out = c_src × (1 - α_dst) + c_dst × (1 - α_src)
|
||||
CompositeModeXor CompositeMode = CompositeMode(12)
|
||||
|
||||
// Sum of source and destination (a.k.a. 'plus' or 'additive')
|
||||
// c_out = c_src + c_dst
|
||||
CompositeModeLighter CompositeMode = CompositeMode(13)
|
||||
)
|
@ -1,9 +1,6 @@
|
||||
package d2corecommon
|
||||
package d2config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"os"
|
||||
"os/user"
|
||||
"path"
|
||||
"runtime"
|
||||
@ -24,56 +21,7 @@ type Configuration struct {
|
||||
BgmVolume float64
|
||||
}
|
||||
|
||||
func LoadConfiguration() *Configuration {
|
||||
configDir, err := os.UserConfigDir()
|
||||
if err != nil {
|
||||
return getDefaultConfiguration()
|
||||
}
|
||||
|
||||
configDir = path.Join(configDir, "OpenDiablo2")
|
||||
configPath := path.Join(configDir, "config.json")
|
||||
log.Printf("loading configuration file from %s...", configPath)
|
||||
configFile, err := os.Open(configPath)
|
||||
defer configFile.Close()
|
||||
|
||||
if err == nil {
|
||||
var config Configuration
|
||||
decoder := json.NewDecoder(configFile)
|
||||
if err := decoder.Decode(&config); err == nil {
|
||||
return &config
|
||||
}
|
||||
}
|
||||
|
||||
return getDefaultConfiguration()
|
||||
}
|
||||
|
||||
func (c *Configuration) Save() error {
|
||||
configDir, err := os.UserConfigDir()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
configDir = path.Join(configDir, "OpenDiablo2")
|
||||
if err := os.MkdirAll(configDir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
configPath := path.Join(configDir, "config.json")
|
||||
log.Printf("saving configuration file to %s...", configPath)
|
||||
configFile, err := os.Create(configPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
encoder := json.NewEncoder(configFile)
|
||||
encoder.SetIndent("", " ")
|
||||
if err := encoder.Encode(c); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
func getConfigurationPath() string {
|
||||
configDir, err := os.UserConfigDir()
|
||||
if err != nil {
|
||||
@ -82,11 +30,11 @@ func getConfigurationPath() string {
|
||||
|
||||
return path.Join(configDir, "OpenDiablo2/config.json")
|
||||
}
|
||||
|
||||
*/
|
||||
func getDefaultConfiguration() *Configuration {
|
||||
config := &Configuration{
|
||||
Language: "ENG",
|
||||
FullScreen: true,
|
||||
FullScreen: false,
|
||||
Scale: 1,
|
||||
TicksPerSecond: -1,
|
||||
RunInBackground: true,
|
||||
@ -100,7 +48,7 @@ func getDefaultConfiguration() *Configuration {
|
||||
"d2xmusic.mpq",
|
||||
"d2xtalk.mpq",
|
||||
"d2xvideo.mpq",
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2data.mpq",
|
||||
"d2datadict.mpq",
|
||||
"d2char.mpq",
|
||||
"d2music.mpq",
|
||||
"d2sfx.mpq",
|
82
d2common/d2config/d2config.go
Normal file
82
d2common/d2config/d2config.go
Normal file
@ -0,0 +1,82 @@
|
||||
package d2config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNotInit = errors.New("configuration is not initialized")
|
||||
ErrHasInit = errors.New("configuration has already been initialized")
|
||||
)
|
||||
|
||||
var singleton *Configuration
|
||||
|
||||
func Initialize() error {
|
||||
if singleton != nil {
|
||||
return ErrHasInit
|
||||
}
|
||||
configDir, err := os.UserConfigDir()
|
||||
if err != nil {
|
||||
singleton = getDefaultConfiguration()
|
||||
return nil
|
||||
}
|
||||
|
||||
configDir = path.Join(configDir, "OpenDiablo2")
|
||||
configPath := path.Join(configDir, "config.json")
|
||||
log.Printf("loading configuration file from %s...", configPath)
|
||||
configFile, err := os.Open(configPath)
|
||||
|
||||
if err == nil {
|
||||
var config Configuration
|
||||
decoder := json.NewDecoder(configFile)
|
||||
defer configFile.Close()
|
||||
if err := decoder.Decode(&config); err == nil {
|
||||
singleton = &config
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
singleton = getDefaultConfiguration()
|
||||
return nil
|
||||
}
|
||||
|
||||
func Save() error {
|
||||
if singleton == nil {
|
||||
return ErrNotInit
|
||||
}
|
||||
configDir, err := os.UserConfigDir()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
configDir = path.Join(configDir, "OpenDiablo2")
|
||||
if err := os.MkdirAll(configDir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
configPath := path.Join(configDir, "config.json")
|
||||
log.Printf("saving configuration file to %s...", configPath)
|
||||
configFile, err := os.Create(configPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
encoder := json.NewEncoder(configFile)
|
||||
encoder.SetIndent("", " ")
|
||||
if err := encoder.Encode(singleton); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Get() (*Configuration, error) {
|
||||
if singleton == nil {
|
||||
return nil, ErrNotInit
|
||||
}
|
||||
return singleton, nil
|
||||
}
|
@ -4,10 +4,6 @@ import (
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
)
|
||||
|
||||
@ -27,9 +23,8 @@ type AnimationDataRecord struct {
|
||||
var AnimationData map[string][]*AnimationDataRecord
|
||||
|
||||
// LoadAnimationData loads the animation data table into the global AnimationData dictionary
|
||||
func LoadAnimationData(fileProvider d2interface.FileProvider) {
|
||||
func LoadAnimationData(rawData []byte) {
|
||||
AnimationData = make(map[string][]*AnimationDataRecord)
|
||||
rawData := fileProvider.LoadFile(d2resource.AnimationData)
|
||||
streamReader := d2common.CreateStreamReader(rawData)
|
||||
for !streamReader.Eof() {
|
||||
dataCount := int(streamReader.GetInt32())
|
14
d2common/d2data/d2datadict/armor.go
Normal file
14
d2common/d2data/d2datadict/armor.go
Normal file
@ -0,0 +1,14 @@
|
||||
package d2datadict
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||
)
|
||||
|
||||
var Armors map[string]*ItemCommonRecord
|
||||
|
||||
func LoadArmors(file []byte) {
|
||||
Armors = *LoadCommonItems(file, d2enum.InventoryItemTypeArmor)
|
||||
log.Printf("Loaded %d armors", len(Armors))
|
||||
}
|
@ -7,8 +7,6 @@ import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||
)
|
||||
|
||||
type ItemCommonRecord struct {
|
||||
@ -119,7 +117,6 @@ type ItemCommonRecord struct {
|
||||
|
||||
Nameable bool // if true, item can be personalized
|
||||
|
||||
|
||||
// weapon params
|
||||
BarbOneOrTwoHanded bool // if true, barb can wield this in one or two hands
|
||||
UsesTwoHands bool // if true, it's a 2handed weapon
|
||||
@ -136,7 +133,7 @@ type ItemCommonRecord struct {
|
||||
WeaponClass string // what kind of attack does this weapon have (i.e. determines attack animations)
|
||||
WeaponClass2Hand string // what kind of attack when wielded with two hands
|
||||
HitClass string // determines sounds/graphic effects when attacking
|
||||
SpawnStack int // unknown, something to do with stack size when spawned (sold maybe?)
|
||||
SpawnStack int // unknown, something to do with stack size when spawned (sold maybe?)
|
||||
|
||||
SpecialFeature string // Just a comment
|
||||
|
||||
@ -147,24 +144,24 @@ type ItemCommonRecord struct {
|
||||
// misc params
|
||||
FlavorText string // unknown, probably just for reference
|
||||
|
||||
Transmogrify bool // if true, can be turned into another item via right click
|
||||
Transmogrify bool // if true, can be turned into another item via right click
|
||||
TransmogCode string // the 3 char code representing the item this becomes via transmog
|
||||
TransmogMin int // min amount of the transmog item to create
|
||||
TransmogMax int // max ''
|
||||
TransmogMin int // min amount of the transmog item to create
|
||||
TransmogMax int // max ''
|
||||
|
||||
AutoBelt bool // if true, item is put into your belt when picked up
|
||||
|
||||
SpellIcon int // which icon to display when used? Is this always -1?
|
||||
SpellType int // determines what kind of function is used when you use this item
|
||||
OverlayState string // name of the overlay state to be applied upon use of this item
|
||||
CureOverlayStates [2]string // name of the overlay states that are removed upon use of this item
|
||||
EffectLength int // timer for timed usage effects
|
||||
SpellIcon int // which icon to display when used? Is this always -1?
|
||||
SpellType int // determines what kind of function is used when you use this item
|
||||
OverlayState string // name of the overlay state to be applied upon use of this item
|
||||
CureOverlayStates [2]string // name of the overlay states that are removed upon use of this item
|
||||
EffectLength int // timer for timed usage effects
|
||||
UsageStats [3]ItemUsageStat // stat boosts applied upon usage
|
||||
|
||||
|
||||
SpellDescriptionType int // specifies how to format the usage description
|
||||
// 0 = none, 1 = use desc string, 2 = use desc string + calc value
|
||||
SpellDescriptionString string // points to a string containing the description
|
||||
SpellDescriptionCalc d2common.CalcString // a calc string what value to display
|
||||
SpellDescriptionCalc d2common.CalcString // a calc string what value to display
|
||||
|
||||
BetterGem string // 3 char code pointing to the gem this upgrades to (non if not applicable)
|
||||
|
||||
@ -184,17 +181,15 @@ type ItemVendorParams struct {
|
||||
MagicLevel uint8
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Loading Functions
|
||||
var CommonItems map[string]*ItemCommonRecord
|
||||
|
||||
func LoadCommonItems(fileProvider d2interface.FileProvider, filepath string, source d2enum.InventoryItemType) *map[string]*ItemCommonRecord {
|
||||
func LoadCommonItems(file []byte, source d2enum.InventoryItemType) *map[string]*ItemCommonRecord {
|
||||
if CommonItems == nil {
|
||||
CommonItems = make(map[string]*ItemCommonRecord)
|
||||
}
|
||||
items := make(map[string]*ItemCommonRecord)
|
||||
data := strings.Split(string(fileProvider.LoadFile(filepath)), "\r\n")
|
||||
data := strings.Split(string(file), "\r\n")
|
||||
mapping := MapHeaders(data[0])
|
||||
for lineno, line := range data {
|
||||
if lineno == 0 {
|
||||
@ -324,14 +319,14 @@ func createCommonItemRecord(line string, mapping *map[string]int, source d2enum.
|
||||
MaxMissileDamage: MapLoadInt(&r, mapping, "maxmisdam"),
|
||||
MissileSpeed: MapLoadInt(&r, mapping, "misspeed"),
|
||||
ExtraRange: MapLoadInt(&r, mapping, "rangeadder"),
|
||||
|
||||
|
||||
RequiredDexterity: MapLoadInt(&r, mapping, "reqdex"),
|
||||
|
||||
|
||||
WeaponClass: MapLoadString(&r, mapping, "wclass"),
|
||||
WeaponClass2Hand: MapLoadString(&r, mapping, "2handedwclass"),
|
||||
|
||||
HitClass: MapLoadString(&r, mapping, "hit class"),
|
||||
SpawnStack: MapLoadInt(&r, mapping, "spawnstack"),
|
||||
|
||||
HitClass: MapLoadString(&r, mapping, "hit class"),
|
||||
SpawnStack: MapLoadInt(&r, mapping, "spawnstack"),
|
||||
|
||||
SpecialFeature: MapLoadString(&r, mapping, "special"),
|
||||
|
||||
@ -346,30 +341,30 @@ func createCommonItemRecord(line string, mapping *map[string]int, source d2enum.
|
||||
TransmogCode: MapLoadString(&r, mapping, "TMogType"),
|
||||
TransmogMin: MapLoadInt(&r, mapping, "TMogMin"),
|
||||
TransmogMax: MapLoadInt(&r, mapping, "TMogMax"),
|
||||
|
||||
|
||||
AutoBelt: MapLoadBool(&r, mapping, "autobelt"),
|
||||
|
||||
SpellIcon: MapLoadInt(&r, mapping, "spellicon"),
|
||||
SpellType: MapLoadInt(&r, mapping, "pSpell"),
|
||||
OverlayState: MapLoadString(&r, mapping, "state"),
|
||||
|
||||
SpellIcon: MapLoadInt(&r, mapping, "spellicon"),
|
||||
SpellType: MapLoadInt(&r, mapping, "pSpell"),
|
||||
OverlayState: MapLoadString(&r, mapping, "state"),
|
||||
CureOverlayStates: [2]string{
|
||||
MapLoadString(&r, mapping, "cstate1"),
|
||||
MapLoadString(&r, mapping, "cstate2"),
|
||||
},
|
||||
EffectLength: MapLoadInt(&r, mapping, "len"),
|
||||
UsageStats: createItemUsageStats(&r, mapping),
|
||||
|
||||
EffectLength: MapLoadInt(&r, mapping, "len"),
|
||||
UsageStats: createItemUsageStats(&r, mapping),
|
||||
|
||||
SpellDescriptionType: MapLoadInt(&r, mapping, "spelldesc"),
|
||||
// 0 = none, 1 = use desc string, 2 = use desc string + calc value
|
||||
SpellDescriptionString: MapLoadString(&r, mapping, "spelldescstr"),
|
||||
SpellDescriptionCalc: d2common.CalcString(MapLoadString(&r, mapping, "spelldesccalc")),
|
||||
|
||||
|
||||
BetterGem: MapLoadString(&r, mapping, "BetterGem"),
|
||||
|
||||
Multibuy: MapLoadBool(&r, mapping, "multibuy"),
|
||||
|
||||
Multibuy: MapLoadBool(&r, mapping, "multibuy"),
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
func createItemVendorParams(r *[]string, mapping *map[string]int) map[string]*ItemVendorParams {
|
||||
vs := make([]string, 17)
|
||||
@ -395,11 +390,11 @@ func createItemVendorParams(r *[]string, mapping *map[string]int) map[string]*It
|
||||
|
||||
for _, name := range vs {
|
||||
wvp := ItemVendorParams{
|
||||
Min: MapLoadInt(r, mapping, name + "Min"),
|
||||
Max: MapLoadInt(r, mapping, name + "Max"),
|
||||
MagicMin: MapLoadInt(r, mapping, name + "MagicMin"),
|
||||
MagicMax: MapLoadInt(r, mapping, name + "MagicMax"),
|
||||
MagicLevel: MapLoadUint8(r, mapping, name + "MagicLvl"),
|
||||
Min: MapLoadInt(r, mapping, name+"Min"),
|
||||
Max: MapLoadInt(r, mapping, name+"Max"),
|
||||
MagicMin: MapLoadInt(r, mapping, name+"MagicMin"),
|
||||
MagicMax: MapLoadInt(r, mapping, name+"MagicMax"),
|
||||
MagicLevel: MapLoadUint8(r, mapping, name+"MagicLvl"),
|
||||
}
|
||||
result[name] = &wvp
|
||||
}
|
||||
@ -409,8 +404,8 @@ func createItemVendorParams(r *[]string, mapping *map[string]int) map[string]*It
|
||||
func createItemUsageStats(r *[]string, mapping *map[string]int) [3]ItemUsageStat {
|
||||
result := [3]ItemUsageStat{}
|
||||
for i := 0; i < 3; i++ {
|
||||
result[i].Stat = MapLoadString(r, mapping, "stat" + strconv.Itoa(i))
|
||||
result[i].Calc = d2common.CalcString(MapLoadString(r, mapping, "calc" + strconv.Itoa(i)))
|
||||
result[i].Stat = MapLoadString(r, mapping, "stat"+strconv.Itoa(i))
|
||||
result[i].Calc = d2common.CalcString(MapLoadString(r, mapping, "calc"+strconv.Itoa(i)))
|
||||
}
|
||||
return result
|
||||
}
|
@ -4,11 +4,7 @@ import (
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||
|
||||
dh "github.com/OpenDiablo2/OpenDiablo2/d2helper"
|
||||
dh "github.com/OpenDiablo2/OpenDiablo2/d2common/d2helper"
|
||||
)
|
||||
|
||||
type LevelPresetRecord struct {
|
||||
@ -75,9 +71,9 @@ func createLevelPresetRecord(props []string) LevelPresetRecord {
|
||||
|
||||
var LevelPresets map[int]LevelPresetRecord
|
||||
|
||||
func LoadLevelPresets(fileProvider d2interface.FileProvider) {
|
||||
func LoadLevelPresets(file []byte) {
|
||||
LevelPresets = make(map[int]LevelPresetRecord)
|
||||
data := strings.Split(string(fileProvider.LoadFile(d2resource.LevelPreset)), "\r\n")[1:]
|
||||
data := strings.Split(string(file), "\r\n")[1:]
|
||||
for _, line := range data {
|
||||
if len(line) == 0 {
|
||||
continue
|
@ -4,11 +4,7 @@ import (
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||
|
||||
dh "github.com/OpenDiablo2/OpenDiablo2/d2helper"
|
||||
dh "github.com/OpenDiablo2/OpenDiablo2/d2common/d2helper"
|
||||
)
|
||||
|
||||
type LevelTypeRecord struct {
|
||||
@ -22,8 +18,8 @@ type LevelTypeRecord struct {
|
||||
|
||||
var LevelTypes []LevelTypeRecord
|
||||
|
||||
func LoadLevelTypes(fileProvider d2interface.FileProvider) {
|
||||
data := strings.Split(string(fileProvider.LoadFile(d2resource.LevelType)), "\r\n")[1:]
|
||||
func LoadLevelTypes(file []byte) {
|
||||
data := strings.Split(string(file), "\r\n")[1:]
|
||||
LevelTypes = make([]LevelTypeRecord, len(data))
|
||||
for i, j := 0, 0; i < len(data); i, j = i+1, j+1 {
|
||||
idx := -1
|
@ -3,10 +3,6 @@ package d2datadict
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
)
|
||||
|
||||
@ -27,9 +23,8 @@ type LevelWarpRecord struct {
|
||||
|
||||
var LevelWarps map[int]*LevelWarpRecord
|
||||
|
||||
func LoadLevelWarps(fileProvider d2interface.FileProvider) {
|
||||
func LoadLevelWarps(levelWarpData []byte) {
|
||||
LevelWarps = make(map[int]*LevelWarpRecord)
|
||||
levelWarpData := fileProvider.LoadFile(d2resource.LevelWarp)
|
||||
streamReader := d2common.CreateStreamReader(levelWarpData)
|
||||
numRecords := int(streamReader.GetInt32())
|
||||
for i := 0; i < numRecords; i++ {
|
@ -1,8 +1,9 @@
|
||||
package d2datadict
|
||||
|
||||
import (
|
||||
dh "github.com/OpenDiablo2/OpenDiablo2/d2helper"
|
||||
"strings"
|
||||
|
||||
dh "github.com/OpenDiablo2/OpenDiablo2/d2common/d2helper"
|
||||
)
|
||||
|
||||
func MapHeaders(line string) map[string]int {
|
14
d2common/d2data/d2datadict/misc.go
Normal file
14
d2common/d2data/d2datadict/misc.go
Normal file
@ -0,0 +1,14 @@
|
||||
package d2datadict
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||
)
|
||||
|
||||
var MiscItems map[string]*ItemCommonRecord
|
||||
|
||||
func LoadMiscItems(file []byte) {
|
||||
MiscItems = *LoadCommonItems(file, d2enum.InventoryItemTypeItem)
|
||||
log.Printf("Loaded %d misc items", len(MiscItems))
|
||||
}
|
@ -6,11 +6,7 @@ import (
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||
|
||||
dh "github.com/OpenDiablo2/OpenDiablo2/d2helper"
|
||||
dh "github.com/OpenDiablo2/OpenDiablo2/d2common/d2helper"
|
||||
)
|
||||
|
||||
type MissileCalcParam struct {
|
||||
@ -297,9 +293,9 @@ func createMissileRecord(line string) MissileRecord {
|
||||
|
||||
var Missiles map[int]*MissileRecord
|
||||
|
||||
func LoadMissiles(fileProvider d2interface.FileProvider) {
|
||||
func LoadMissiles(file []byte) {
|
||||
Missiles = make(map[int]*MissileRecord)
|
||||
data := strings.Split(string(fileProvider.LoadFile(d2resource.Missiles)), "\r\n")[1:]
|
||||
data := strings.Split(string(file), "\r\n")[1:]
|
||||
for _, line := range data {
|
||||
if len(line) == 0 {
|
||||
continue
|
11
d2common/d2data/d2datadict/monstats.go
Normal file
11
d2common/d2data/d2datadict/monstats.go
Normal file
@ -0,0 +1,11 @@
|
||||
package d2datadict
|
||||
|
||||
import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
)
|
||||
|
||||
var MonStatsDictionary *d2common.DataDictionary
|
||||
|
||||
func LoadMonStats(file []byte) {
|
||||
MonStatsDictionary = d2common.LoadDataDictionary(string(file))
|
||||
}
|
@ -4,10 +4,6 @@ import (
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
)
|
||||
|
||||
@ -18,8 +14,7 @@ type ObjectTypeRecord struct {
|
||||
|
||||
var ObjectTypes []ObjectTypeRecord
|
||||
|
||||
func LoadObjectTypes(fileProvider d2interface.FileProvider) {
|
||||
objectTypeData := fileProvider.LoadFile(d2resource.ObjectType)
|
||||
func LoadObjectTypes(objectTypeData []byte) {
|
||||
streamReader := d2common.CreateStreamReader(objectTypeData)
|
||||
count := streamReader.GetInt32()
|
||||
ObjectTypes = make([]ObjectTypeRecord, count)
|
@ -4,11 +4,7 @@ import (
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||
|
||||
dh "github.com/OpenDiablo2/OpenDiablo2/d2helper"
|
||||
dh "github.com/OpenDiablo2/OpenDiablo2/d2common/d2helper"
|
||||
)
|
||||
|
||||
// An ObjectRecord represents the settings for one type of object from objects.txt
|
||||
@ -339,9 +335,9 @@ func createObjectRecord(props []string) ObjectRecord {
|
||||
|
||||
var Objects map[int]*ObjectRecord
|
||||
|
||||
func LoadObjects(fileProvider d2interface.FileProvider) {
|
||||
func LoadObjects(file []byte) {
|
||||
Objects = make(map[int]*ObjectRecord)
|
||||
data := strings.Split(string(fileProvider.LoadFile(d2resource.ObjectDetails)), "\r\n")[1:]
|
||||
data := strings.Split(string(file), "\r\n")[1:]
|
||||
for _, line := range data {
|
||||
if len(line) == 0 {
|
||||
continue
|
41
d2common/d2data/d2datadict/palette.go
Normal file
41
d2common/d2data/d2datadict/palette.go
Normal file
@ -0,0 +1,41 @@
|
||||
package d2datadict
|
||||
|
||||
import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||
)
|
||||
|
||||
// PaletteRGB represents a color in a palette
|
||||
type PaletteRGB struct {
|
||||
R, G, B uint8
|
||||
}
|
||||
|
||||
// PaletteType represents a palette
|
||||
type PaletteRec struct {
|
||||
Name d2enum.PaletteType
|
||||
Colors [256]PaletteRGB
|
||||
}
|
||||
|
||||
var Palettes map[d2enum.PaletteType]PaletteRec
|
||||
|
||||
// CreatePalette creates a palette
|
||||
func CreatePalette(name d2enum.PaletteType, data []byte) PaletteRec {
|
||||
result := PaletteRec{Name: name}
|
||||
|
||||
for i := 0; i <= 255; i++ {
|
||||
result.Colors[i] = PaletteRGB{
|
||||
B: data[i*3],
|
||||
G: data[(i*3)+1],
|
||||
R: data[(i*3)+2],
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func LoadPalette(paletteType d2enum.PaletteType, file []byte) {
|
||||
if Palettes == nil {
|
||||
Palettes = make(map[d2enum.PaletteType]PaletteRec)
|
||||
}
|
||||
palette := CreatePalette(paletteType, file)
|
||||
Palettes[paletteType] = palette
|
||||
|
||||
}
|
@ -4,11 +4,7 @@ import (
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||
|
||||
dh "github.com/OpenDiablo2/OpenDiablo2/d2helper"
|
||||
dh "github.com/OpenDiablo2/OpenDiablo2/d2common/d2helper"
|
||||
)
|
||||
|
||||
// SoundEntry represents a sound entry
|
||||
@ -80,9 +76,9 @@ func createSoundEntry(soundLine string) SoundEntry {
|
||||
|
||||
var Sounds map[string]SoundEntry
|
||||
|
||||
func LoadSounds(fileProvider d2interface.FileProvider) {
|
||||
func LoadSounds(file []byte) {
|
||||
Sounds = make(map[string]SoundEntry)
|
||||
soundData := strings.Split(string(fileProvider.LoadFile(d2resource.SoundSettings)), "\r\n")[1:]
|
||||
soundData := strings.Split(string(file), "\r\n")[1:]
|
||||
for _, line := range soundData {
|
||||
if len(line) == 0 {
|
||||
continue
|
@ -6,11 +6,7 @@ import (
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||
|
||||
dh "github.com/OpenDiablo2/OpenDiablo2/d2helper"
|
||||
dh "github.com/OpenDiablo2/OpenDiablo2/d2common/d2helper"
|
||||
)
|
||||
|
||||
type UniqueItemRecord struct {
|
||||
@ -120,9 +116,9 @@ func createUniqueItemProperty(r *[]string, inc func() int) UniqueItemProperty {
|
||||
|
||||
var UniqueItems map[string]*UniqueItemRecord
|
||||
|
||||
func LoadUniqueItems(fileProvider d2interface.FileProvider) {
|
||||
func LoadUniqueItems(file []byte) {
|
||||
UniqueItems = make(map[string]*UniqueItemRecord)
|
||||
data := strings.Split(string(fileProvider.LoadFile(d2resource.UniqueItems)), "\r\n")[1:]
|
||||
data := strings.Split(string(file), "\r\n")[1:]
|
||||
for _, line := range data {
|
||||
if len(line) == 0 {
|
||||
continue
|
14
d2common/d2data/d2datadict/weapons.go
Normal file
14
d2common/d2data/d2datadict/weapons.go
Normal file
@ -0,0 +1,14 @@
|
||||
package d2datadict
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||
)
|
||||
|
||||
var Weapons map[string]*ItemCommonRecord
|
||||
|
||||
func LoadWeapons(file []byte) {
|
||||
Weapons = *LoadCommonItems(file, d2enum.InventoryItemTypeWeapon)
|
||||
log.Printf("Loaded %d weapons", len(Weapons))
|
||||
}
|
@ -2,7 +2,7 @@ package d2data
|
||||
|
||||
import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2datadict"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
|
||||
)
|
||||
|
||||
type Object struct {
|
@ -3,9 +3,9 @@ package d2cof
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||
)
|
||||
|
||||
type COF struct {
|
@ -4,7 +4,7 @@ import (
|
||||
"encoding/binary"
|
||||
"log"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2datadict"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
|
||||
"github.com/go-restruct/restruct"
|
||||
)
|
||||
|
@ -4,7 +4,7 @@ import (
|
||||
"log"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2helper"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2helper"
|
||||
)
|
||||
|
||||
type DCCDirection struct {
|
@ -2,10 +2,10 @@ package d2ds1
|
||||
|
||||
import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2data"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2datadict"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2helper"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2helper"
|
||||
)
|
||||
|
||||
type DS1 struct {
|
@ -10,10 +10,10 @@ import (
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2helper"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2helper"
|
||||
|
||||
"github.com/JoshVarga/blast"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2compression"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2compression"
|
||||
)
|
||||
|
||||
// Stream represents a stream of data in an MPQ archive
|
7
d2common/d2interface/audio_provider.go
Normal file
7
d2common/d2interface/audio_provider.go
Normal file
@ -0,0 +1,7 @@
|
||||
package d2interface
|
||||
|
||||
type AudioProvider interface {
|
||||
PlayBGM(song string)
|
||||
LoadSoundEffect(sfx string) (SoundEffect, error)
|
||||
SetVolumes(bgmVolume, sfxVolume float64)
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package d2interface
|
||||
|
||||
// FileProvider is an instance that can provide different types of files
|
||||
type FileProvider interface {
|
||||
LoadFile(fileName string) []byte
|
||||
//LoadSprite(fileName string, palette enums.PaletteType) *d2render.Sprite
|
||||
}
|
20
d2common/d2interface/renderer.go
Normal file
20
d2common/d2interface/renderer.go
Normal file
@ -0,0 +1,20 @@
|
||||
package d2interface
|
||||
|
||||
import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
)
|
||||
|
||||
type Renderer interface {
|
||||
GetRendererName() string
|
||||
SetWindowIcon(fileName string)
|
||||
Run(f func(d2common.Surface) error, width, height int, title string) error
|
||||
IsDrawingSkipped() bool
|
||||
CreateSurface(surface d2common.Surface) (error, d2common.Surface)
|
||||
NewSurface(width, height int, filter d2common.Filter) (error, d2common.Surface)
|
||||
IsFullScreen() (bool, error)
|
||||
SetFullScreen(fullScreen bool) error
|
||||
SetVSyncEnabled(vsync bool) error
|
||||
GetVSyncEnabled() (bool, error)
|
||||
GetCursorPos() (int, int, error)
|
||||
CurrentFPS() float64
|
||||
}
|
6
d2common/d2interface/sound_effect.go
Normal file
6
d2common/d2interface/sound_effect.go
Normal file
@ -0,0 +1,6 @@
|
||||
package d2interface
|
||||
|
||||
type SoundEffect interface {
|
||||
Play()
|
||||
Stop()
|
||||
}
|
15
d2common/filter.go
Normal file
15
d2common/filter.go
Normal file
@ -0,0 +1,15 @@
|
||||
package d2common
|
||||
|
||||
// Filter represents the type of texture filter to be used when an image is maginified or minified.
|
||||
type Filter int
|
||||
|
||||
const (
|
||||
// FilterDefault represents the default filter.
|
||||
FilterDefault Filter = 0
|
||||
|
||||
// FilterNearest represents nearest (crisp-edged) filter
|
||||
FilterNearest Filter = Filter(1)
|
||||
|
||||
// FilterLinear represents linear filter
|
||||
FilterLinear Filter = Filter(2)
|
||||
)
|
22
d2common/surface.go
Normal file
22
d2common/surface.go
Normal file
@ -0,0 +1,22 @@
|
||||
package d2common
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
)
|
||||
|
||||
type Surface interface {
|
||||
Clear(color color.Color) error
|
||||
DrawRect(width, height int, color color.Color)
|
||||
DrawLine(x, y int, color color.Color)
|
||||
DrawText(format string, params ...interface{})
|
||||
GetSize() (width, height int)
|
||||
GetDepth() int
|
||||
Pop()
|
||||
PopN(n int)
|
||||
PushColor(color color.Color)
|
||||
PushCompositeMode(mode CompositeMode)
|
||||
PushFilter(filter Filter)
|
||||
PushTranslation(x, y int)
|
||||
Render(surface Surface) error
|
||||
ReplacePixels(pixels []byte) error
|
||||
}
|
@ -3,10 +3,6 @@ package d2common
|
||||
import (
|
||||
"log"
|
||||
"strconv"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||
)
|
||||
|
||||
type textDictionaryHashEntry struct {
|
||||
@ -28,16 +24,17 @@ func TranslateString(key string) string {
|
||||
return result
|
||||
}
|
||||
|
||||
func LoadTextDictionary(fileProvider d2interface.FileProvider) {
|
||||
lookupTable = make(map[string]string)
|
||||
loadDictionary(fileProvider, d2resource.PatchStringTable)
|
||||
loadDictionary(fileProvider, d2resource.ExpansionStringTable)
|
||||
loadDictionary(fileProvider, d2resource.StringTable)
|
||||
log.Printf("Loaded %d entries from the string table", len(lookupTable))
|
||||
func GetDictionaryEntryCount() int {
|
||||
if lookupTable == nil {
|
||||
return 0
|
||||
}
|
||||
return len(lookupTable)
|
||||
}
|
||||
|
||||
func loadDictionary(fileProvider d2interface.FileProvider, dictionaryName string) {
|
||||
dictionaryData := fileProvider.LoadFile(dictionaryName)
|
||||
func LoadDictionary(dictionaryData []byte) {
|
||||
if lookupTable == nil {
|
||||
lookupTable = make(map[string]string)
|
||||
}
|
||||
br := CreateStreamReader(dictionaryData)
|
||||
// CRC
|
||||
if _, err := br.ReadBytes(2); err != nil {
|
||||
|
123
d2core/d2archivemanager/archive_manager.go
Normal file
123
d2core/d2archivemanager/archive_manager.go
Normal file
@ -0,0 +1,123 @@
|
||||
package d2archivemanager
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"path"
|
||||
"sync"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2config"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2mpq"
|
||||
)
|
||||
|
||||
type archiveEntry struct {
|
||||
archivePath string
|
||||
hashEntryMap d2mpq.HashEntryMap
|
||||
}
|
||||
|
||||
type ArchiveManager struct {
|
||||
cache *d2common.Cache
|
||||
config *d2config.Configuration
|
||||
entries []archiveEntry
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
const (
|
||||
ArchiveBudget = 1024 * 1024 * 512
|
||||
)
|
||||
|
||||
func CreateArchiveManager(config *d2config.Configuration) *ArchiveManager {
|
||||
return &ArchiveManager{cache: d2common.CreateCache(ArchiveBudget), config: config}
|
||||
}
|
||||
|
||||
func (am *ArchiveManager) SetCacheVerbose(verbose bool) {
|
||||
am.cache.SetCacheVerbose(verbose)
|
||||
}
|
||||
|
||||
func (am *ArchiveManager) GetCacheWeight() int {
|
||||
return am.cache.GetWeight()
|
||||
}
|
||||
|
||||
func (am *ArchiveManager) GetCacheBudget() int {
|
||||
return am.cache.GetBudget()
|
||||
}
|
||||
|
||||
func (am *ArchiveManager) ClearCache() {
|
||||
am.cache.Clear()
|
||||
}
|
||||
|
||||
func (am *ArchiveManager) LoadArchiveForFile(filePath string) (*d2mpq.MPQ, error) {
|
||||
am.mutex.Lock()
|
||||
defer am.mutex.Unlock()
|
||||
|
||||
if err := am.CacheArchiveEntries(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, archiveEntry := range am.entries {
|
||||
if archiveEntry.hashEntryMap.Contains(filePath) {
|
||||
return am.LoadArchive(archiveEntry.archivePath)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.New("file not found")
|
||||
}
|
||||
|
||||
func (am *ArchiveManager) FileExistsInArchive(filePath string) (bool, error) {
|
||||
am.mutex.Lock()
|
||||
defer am.mutex.Unlock()
|
||||
|
||||
if err := am.CacheArchiveEntries(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, archiveEntry := range am.entries {
|
||||
if archiveEntry.hashEntryMap.Contains(filePath) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (am *ArchiveManager) LoadArchive(archivePath string) (*d2mpq.MPQ, error) {
|
||||
if archive, found := am.cache.Retrieve(archivePath); found {
|
||||
return archive.(*d2mpq.MPQ), nil
|
||||
}
|
||||
|
||||
archive, err := d2mpq.Load(archivePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := am.cache.Insert(archivePath, archive, int(archive.Data.ArchiveSize)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return archive, nil
|
||||
}
|
||||
|
||||
func (am *ArchiveManager) CacheArchiveEntries() error {
|
||||
if len(am.entries) == len(am.config.MpqLoadOrder) {
|
||||
return nil
|
||||
}
|
||||
|
||||
am.entries = nil
|
||||
|
||||
for _, archiveName := range am.config.MpqLoadOrder {
|
||||
archivePath := path.Join(am.config.MpqPath, archiveName)
|
||||
archive, err := am.LoadArchive(archivePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
am.entries = append(
|
||||
am.entries,
|
||||
archiveEntry{archivePath, archive.HashEntryMap},
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -1,15 +1,15 @@
|
||||
package d2render
|
||||
package d2assetmanager
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/rand"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2helper"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2asset"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2helper"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2datadict"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2render/d2surface"
|
||||
)
|
||||
|
||||
// AnimatedEntity represents an entity on the map that can be animated
|
||||
@ -27,12 +27,12 @@ type AnimatedEntity struct {
|
||||
action int32
|
||||
repetitions int
|
||||
|
||||
composite *d2asset.Composite
|
||||
composite *Composite
|
||||
}
|
||||
|
||||
// CreateAnimatedEntity creates an instance of AnimatedEntity
|
||||
func CreateAnimatedEntity(x, y int32, object *d2datadict.ObjectLookupRecord, palettePath string) (*AnimatedEntity, error) {
|
||||
composite, err := d2asset.LoadComposite(object, palettePath)
|
||||
composite, err := LoadComposite(object, palettePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -71,7 +71,7 @@ func (v AnimatedEntity) Wait() bool {
|
||||
}
|
||||
|
||||
// Render draws this animated entity onto the target
|
||||
func (v *AnimatedEntity) Render(target *d2surface.Surface) {
|
||||
func (v *AnimatedEntity) Render(target d2common.Surface) {
|
||||
target.PushTranslation(
|
||||
int(v.offsetX)+int((v.subcellX-v.subcellY)*16),
|
||||
int(v.offsetY)+int(((v.subcellX+v.subcellY)*8)-5),
|
246
d2core/d2assetmanager/asset_manager.go
Normal file
246
d2core/d2assetmanager/asset_manager.go
Normal file
@ -0,0 +1,246 @@
|
||||
package d2assetmanager
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2config"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2archivemanager"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2filemanager"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2cof"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dc6"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dcc"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2mpq"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2term"
|
||||
)
|
||||
|
||||
const (
|
||||
AnimationBudget = 64
|
||||
)
|
||||
|
||||
var (
|
||||
ErrHasInit error = errors.New("asset system is already initialized")
|
||||
ErrNoInit error = errors.New("asset system is not initialized")
|
||||
)
|
||||
|
||||
type AssetManager struct {
|
||||
archiveManager *d2archivemanager.ArchiveManager
|
||||
fileManager *d2filemanager.FileManager
|
||||
paletteManager *PaletteManager
|
||||
cache *d2common.Cache
|
||||
}
|
||||
|
||||
var singleton *AssetManager
|
||||
|
||||
func Initialize() error {
|
||||
if singleton != nil {
|
||||
return ErrHasInit
|
||||
}
|
||||
config, _ := d2config.Get()
|
||||
var (
|
||||
archiveManager = d2archivemanager.CreateArchiveManager(config)
|
||||
fileManager = d2filemanager.CreateFileManager(config, archiveManager)
|
||||
paletteManager = CreatePaletteManager()
|
||||
//animationManager = d2animationmanager.CreateAnimationManager()
|
||||
)
|
||||
|
||||
singleton = &AssetManager{
|
||||
archiveManager,
|
||||
fileManager,
|
||||
paletteManager,
|
||||
nil,
|
||||
}
|
||||
singleton.cache = d2common.CreateCache(AnimationBudget)
|
||||
|
||||
d2term.BindAction("assetspam", "display verbose asset manager logs", func(verbose bool) {
|
||||
if verbose {
|
||||
d2term.OutputInfo("asset manager verbose logging enabled")
|
||||
} else {
|
||||
d2term.OutputInfo("asset manager verbose logging disabled")
|
||||
}
|
||||
|
||||
archiveManager.SetCacheVerbose(verbose)
|
||||
fileManager.SetCacheVerbose(verbose)
|
||||
paletteManager.SetCacheVerbose(verbose)
|
||||
})
|
||||
|
||||
d2term.BindAction("assetstat", "display asset manager cache statistics", func() {
|
||||
d2term.OutputInfo("archive cache: %f%%", float64(archiveManager.GetCacheWeight())/float64(archiveManager.GetCacheBudget())*100.0)
|
||||
d2term.OutputInfo("file cache: %f%%", float64(fileManager.GetCacheWeight())/float64(fileManager.GetCacheBudget())*100.0)
|
||||
d2term.OutputInfo("palette cache: %f%%", float64(paletteManager.GetCacheWeight())/float64(paletteManager.GetCacheBudget())*100.0)
|
||||
//d2term.OutputInfo("animation cache: %f%%", float64(GetCacheWeight())/float64(GetCacheBudget())*100.0)
|
||||
})
|
||||
|
||||
d2term.BindAction("assetclear", "clear asset manager cache", func() {
|
||||
archiveManager.ClearCache()
|
||||
fileManager.ClearCache()
|
||||
paletteManager.ClearCache()
|
||||
//am.ClearCache()
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Shutdown() {
|
||||
singleton = nil
|
||||
}
|
||||
|
||||
func LoadArchive(archivePath string) (*d2mpq.MPQ, error) {
|
||||
if singleton == nil {
|
||||
return nil, ErrNoInit
|
||||
}
|
||||
|
||||
return singleton.archiveManager.LoadArchive(archivePath)
|
||||
}
|
||||
|
||||
func LoadFile(filePath string) ([]byte, error) {
|
||||
if singleton == nil {
|
||||
return nil, ErrNoInit
|
||||
}
|
||||
|
||||
data, err := singleton.fileManager.LoadFile(filePath)
|
||||
if err != nil {
|
||||
log.Printf("error loading file %s (%v)", filePath, err.Error())
|
||||
}
|
||||
|
||||
return data, err
|
||||
}
|
||||
|
||||
func FileExists(filePath string) (bool, error) {
|
||||
if singleton == nil {
|
||||
return false, ErrNoInit
|
||||
}
|
||||
|
||||
return singleton.fileManager.FileExists(filePath)
|
||||
}
|
||||
|
||||
func LoadAnimation(animationPath, palettePath string) (*d2render.Animation, error) {
|
||||
return LoadAnimationWithTransparency(animationPath, palettePath, 255)
|
||||
}
|
||||
|
||||
func LoadAnimationWithTransparency(animationPath, palettePath string, transparency int) (*d2render.Animation, error) {
|
||||
if singleton == nil {
|
||||
return nil, ErrNoInit
|
||||
}
|
||||
|
||||
return singleton.LoadAnimation(animationPath, palettePath, transparency)
|
||||
}
|
||||
|
||||
func LoadComposite(object *d2datadict.ObjectLookupRecord, palettePath string) (*Composite, error) {
|
||||
return CreateComposite(object, palettePath), nil
|
||||
}
|
||||
|
||||
func loadPalette(palettePath string) (*d2datadict.PaletteRec, error) {
|
||||
if singleton == nil {
|
||||
return nil, ErrNoInit
|
||||
}
|
||||
|
||||
return singleton.paletteManager.LoadPalette(palettePath)
|
||||
}
|
||||
|
||||
func loadDC6(dc6Path, palettePath string) (*d2dc6.DC6File, error) {
|
||||
dc6Data, err := LoadFile(dc6Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
paletteData, err := loadPalette(palettePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dc6, err := d2dc6.LoadDC6(dc6Data, *paletteData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &dc6, nil
|
||||
}
|
||||
|
||||
func loadDCC(dccPath string) (*d2dcc.DCC, error) {
|
||||
dccData, err := LoadFile(dccPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return d2dcc.LoadDCC(dccData)
|
||||
}
|
||||
|
||||
func loadCOF(cofPath string) (*d2cof.COF, error) {
|
||||
cofData, err := LoadFile(cofPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return d2cof.LoadCOF(cofData)
|
||||
}
|
||||
|
||||
func (am *AssetManager) SetCacheVerbose(verbose bool) {
|
||||
am.cache.SetCacheVerbose(verbose)
|
||||
}
|
||||
|
||||
func (am *AssetManager) ClearCache() {
|
||||
am.cache.Clear()
|
||||
}
|
||||
|
||||
func (am *AssetManager) GetCacheWeight() int {
|
||||
return am.cache.GetWeight()
|
||||
}
|
||||
|
||||
func (am *AssetManager) GetCacheBudget() int {
|
||||
return am.cache.GetBudget()
|
||||
}
|
||||
|
||||
func (am *AssetManager) LoadAnimation(animationPath, palettePath string, transparency int) (*d2render.Animation, error) {
|
||||
cachePath := fmt.Sprintf("%s;%s;%d", animationPath, palettePath, transparency)
|
||||
if animation, found := am.cache.Retrieve(cachePath); found {
|
||||
return animation.(*d2render.Animation).Clone(), nil
|
||||
}
|
||||
|
||||
var animation *d2render.Animation
|
||||
switch strings.ToLower(filepath.Ext(animationPath)) {
|
||||
case ".dc6":
|
||||
dc6, err := loadDC6(animationPath, palettePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
animation, err = d2render.CreateAnimationFromDC6(dc6)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case ".dcc":
|
||||
dcc, err := loadDCC(animationPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
palette, err := loadPalette(palettePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
animation, err = d2render.CreateAnimationFromDCC(dcc, palette, transparency)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, errors.New("unknown animation format")
|
||||
}
|
||||
|
||||
if err := am.cache.Insert(cachePath, animation.Clone(), 1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return animation, nil
|
||||
}
|
@ -1,15 +1,18 @@
|
||||
package d2asset
|
||||
package d2assetmanager
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2data"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2datadict"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2dcc"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2render/d2surface"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dcc"
|
||||
)
|
||||
|
||||
type Composite struct {
|
||||
@ -18,7 +21,7 @@ type Composite struct {
|
||||
mode *compositeMode
|
||||
}
|
||||
|
||||
func createComposite(object *d2datadict.ObjectLookupRecord, palettePath string) *Composite {
|
||||
func CreateComposite(object *d2datadict.ObjectLookupRecord, palettePath string) *Composite {
|
||||
return &Composite{object: object, palettePath: palettePath}
|
||||
}
|
||||
|
||||
@ -45,7 +48,7 @@ func (c *Composite) Advance(elapsed float64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Composite) Render(target *d2surface.Surface) error {
|
||||
func (c *Composite) Render(target d2common.Surface) error {
|
||||
if c.mode == nil {
|
||||
return nil
|
||||
}
|
||||
@ -105,7 +108,7 @@ type compositeMode struct {
|
||||
directionCount int
|
||||
playedCount int
|
||||
|
||||
layers []*Animation
|
||||
layers []*d2render.Animation
|
||||
drawOrder [][]d2enum.CompositeType
|
||||
|
||||
frameCount int
|
||||
@ -140,7 +143,7 @@ func (c *Composite) createMode(animationMode, weaponClass string, direction int)
|
||||
weaponClass: weaponClass,
|
||||
direction: direction,
|
||||
directionCount: cof.NumberOfDirections,
|
||||
layers: make([]*Animation, d2enum.CompositeTypeMax),
|
||||
layers: make([]*d2render.Animation, d2enum.CompositeTypeMax),
|
||||
frameCount: animationData[0].FramesPerDirection,
|
||||
animationSpeed: 1.0 / ((float64(animationData[0].AnimationSpeed) * 25.0) / 256.0),
|
||||
}
|
||||
@ -245,7 +248,7 @@ func (c *Composite) createMode(animationMode, weaponClass string, direction int)
|
||||
return mode, nil
|
||||
}
|
||||
|
||||
func loadCompositeLayer(object *d2datadict.ObjectLookupRecord, layerKey, layerValue, animationMode, weaponClass, palettePath string, transparency int) (*Animation, error) {
|
||||
func loadCompositeLayer(object *d2datadict.ObjectLookupRecord, layerKey, layerValue, animationMode, weaponClass, palettePath string, transparency int) (*d2render.Animation, error) {
|
||||
animationPaths := []string{
|
||||
fmt.Sprintf("%s/%s/%s/%s%s%s%s%s.dcc", object.Base, object.Token, layerKey, object.Token, layerKey, layerValue, animationMode, weaponClass),
|
||||
fmt.Sprintf("%s/%s/%s/%s%s%s%s%s.dcc", object.Base, object.Token, layerKey, object.Token, layerKey, layerValue, animationMode, "HTH"),
|
49
d2core/d2assetmanager/palette_manager.go
Normal file
49
d2core/d2assetmanager/palette_manager.go
Normal file
@ -0,0 +1,49 @@
|
||||
package d2assetmanager
|
||||
|
||||
import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
|
||||
)
|
||||
|
||||
type PaletteManager struct {
|
||||
cache *d2common.Cache
|
||||
}
|
||||
|
||||
const (
|
||||
PaletteBudget = 64
|
||||
)
|
||||
|
||||
func CreatePaletteManager() *PaletteManager {
|
||||
return &PaletteManager{d2common.CreateCache(PaletteBudget)}
|
||||
}
|
||||
|
||||
func (pm *PaletteManager) SetCacheVerbose(verbose bool) {
|
||||
pm.cache.SetCacheVerbose(verbose)
|
||||
}
|
||||
|
||||
func (pm *PaletteManager) ClearCache() {
|
||||
pm.cache.Clear()
|
||||
}
|
||||
|
||||
func (pm *PaletteManager) GetCacheWeight() int {
|
||||
return pm.cache.GetWeight()
|
||||
}
|
||||
|
||||
func (pm *PaletteManager) GetCacheBudget() int {
|
||||
return pm.cache.GetBudget()
|
||||
}
|
||||
|
||||
func (pm *PaletteManager) LoadPalette(palettePath string) (*d2datadict.PaletteRec, error) {
|
||||
if palette, found := pm.cache.Retrieve(palettePath); found {
|
||||
return palette.(*d2datadict.PaletteRec), nil
|
||||
}
|
||||
|
||||
paletteData, err := LoadFile(palettePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
palette := d2datadict.CreatePalette("", paletteData)
|
||||
pm.cache.Insert(palettePath, &palette, 1)
|
||||
return &palette, nil
|
||||
}
|
47
d2core/d2audio/d2audio.go
Normal file
47
d2core/d2audio/d2audio.go
Normal file
@ -0,0 +1,47 @@
|
||||
package d2audio
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||
)
|
||||
|
||||
var singleton d2interface.AudioProvider
|
||||
|
||||
var (
|
||||
ErrHasInit error = errors.New("audio system is already initialized")
|
||||
ErrNotInit error = errors.New("audio system has not been initialized")
|
||||
)
|
||||
|
||||
// CreateManager creates a sound provider
|
||||
func Initialize(audioProvider d2interface.AudioProvider) error {
|
||||
if singleton != nil {
|
||||
return ErrHasInit
|
||||
}
|
||||
singleton = audioProvider
|
||||
return nil
|
||||
}
|
||||
|
||||
// PlayBGM plays an infinitely looping background track
|
||||
func PlayBGM(song string) error {
|
||||
if singleton == nil {
|
||||
return ErrNotInit
|
||||
}
|
||||
singleton.PlayBGM(song)
|
||||
return nil
|
||||
}
|
||||
|
||||
func LoadSoundEffect(sfx string) (d2interface.SoundEffect, error) {
|
||||
if singleton == nil {
|
||||
return nil, ErrNotInit
|
||||
}
|
||||
return singleton.LoadSoundEffect(sfx)
|
||||
}
|
||||
|
||||
func SetVolumes(bgmVolume, sfxVolume float64) error {
|
||||
if singleton == nil {
|
||||
return ErrNotInit
|
||||
}
|
||||
singleton.SetVolumes(bgmVolume, sfxVolume)
|
||||
return nil
|
||||
}
|
83
d2core/d2audio/ebiten/ebiten_audio_provider.go
Normal file
83
d2core/d2audio/ebiten/ebiten_audio_provider.go
Normal file
@ -0,0 +1,83 @@
|
||||
package ebiten
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2assetmanager"
|
||||
"github.com/hajimehoshi/ebiten/audio/wav"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/audio"
|
||||
)
|
||||
|
||||
type EbitenAudioProvider struct {
|
||||
audioContext *audio.Context // The Audio context
|
||||
bgmAudio *audio.Player // The audio player
|
||||
lastBgm string
|
||||
sfxVolume float64
|
||||
bgmVolume float64
|
||||
}
|
||||
|
||||
func CreateAudio() (*EbitenAudioProvider, error) {
|
||||
result := &EbitenAudioProvider{}
|
||||
var err error
|
||||
result.audioContext, err = audio.NewContext(44100)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (eap *EbitenAudioProvider) PlayBGM(song string) {
|
||||
if eap.lastBgm == song {
|
||||
return
|
||||
}
|
||||
eap.lastBgm = song
|
||||
if song == "" && eap.bgmAudio != nil && eap.bgmAudio.IsPlaying() {
|
||||
_ = eap.bgmAudio.Pause()
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
if eap.bgmAudio != nil {
|
||||
err := eap.bgmAudio.Close()
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
}
|
||||
audioData, err := d2assetmanager.LoadFile(song)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
d, err := wav.Decode(eap.audioContext, audio.BytesReadSeekCloser(audioData))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
s := audio.NewInfiniteLoop(d, d.Length())
|
||||
eap.bgmAudio, err = audio.NewPlayer(eap.audioContext, s)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
eap.bgmAudio.SetVolume(eap.bgmVolume)
|
||||
// Play the infinite-length stream. This never ends.
|
||||
err = eap.bgmAudio.Rewind()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = eap.bgmAudio.Play()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (eap *EbitenAudioProvider) LoadSoundEffect(sfx string) (d2interface.SoundEffect, error) {
|
||||
result := CreateSoundEffect(sfx, eap.audioContext, eap.sfxVolume) // TODO: Split
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (eap *EbitenAudioProvider) SetVolumes(bgmVolume, sfxVolume float64) {
|
||||
eap.sfxVolume = sfxVolume
|
||||
eap.bgmVolume = bgmVolume
|
||||
}
|
@ -1,22 +1,20 @@
|
||||
package d2audio
|
||||
package ebiten
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2asset"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2datadict"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/audio/wav"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2assetmanager"
|
||||
"github.com/hajimehoshi/ebiten/audio"
|
||||
"github.com/hajimehoshi/ebiten/audio/wav"
|
||||
)
|
||||
|
||||
type SoundEffect struct {
|
||||
type EbitenSoundEffect struct {
|
||||
player *audio.Player
|
||||
}
|
||||
|
||||
func CreateSoundEffect(sfx string, context *audio.Context, volume float64) *SoundEffect {
|
||||
result := &SoundEffect{}
|
||||
func CreateSoundEffect(sfx string, context *audio.Context, volume float64) *EbitenSoundEffect {
|
||||
result := &EbitenSoundEffect{}
|
||||
var soundFile string
|
||||
if _, exists := d2datadict.Sounds[sfx]; exists {
|
||||
soundEntry := d2datadict.Sounds[sfx]
|
||||
@ -25,7 +23,7 @@ func CreateSoundEffect(sfx string, context *audio.Context, volume float64) *Soun
|
||||
soundFile = sfx
|
||||
}
|
||||
|
||||
audioData, err := d2asset.LoadFile(soundFile)
|
||||
audioData, err := d2assetmanager.LoadFile(soundFile)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -44,11 +42,11 @@ func CreateSoundEffect(sfx string, context *audio.Context, volume float64) *Soun
|
||||
return result
|
||||
}
|
||||
|
||||
func (v *SoundEffect) Play() {
|
||||
func (v *EbitenSoundEffect) Play() {
|
||||
v.player.Rewind()
|
||||
v.player.Play()
|
||||
}
|
||||
|
||||
func (v *SoundEffect) Stop() {
|
||||
func (v *EbitenSoundEffect) Stop() {
|
||||
v.player.Pause()
|
||||
}
|
84
d2core/d2filemanager/file_manager.go
Normal file
84
d2core/d2filemanager/file_manager.go
Normal file
@ -0,0 +1,84 @@
|
||||
package d2filemanager
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2archivemanager"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2config"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
|
||||
)
|
||||
|
||||
const (
|
||||
FileBudget = 1024 * 1024 * 32
|
||||
)
|
||||
|
||||
type FileManager struct {
|
||||
cache *d2common.Cache
|
||||
archiveManager *d2archivemanager.ArchiveManager
|
||||
config *d2config.Configuration
|
||||
}
|
||||
|
||||
func CreateFileManager(config *d2config.Configuration, archiveManager *d2archivemanager.ArchiveManager) *FileManager {
|
||||
return &FileManager{d2common.CreateCache(FileBudget), archiveManager, config}
|
||||
}
|
||||
|
||||
func (fm *FileManager) SetCacheVerbose(verbose bool) {
|
||||
fm.cache.SetCacheVerbose(verbose)
|
||||
}
|
||||
|
||||
func (fm *FileManager) ClearCache() {
|
||||
fm.cache.Clear()
|
||||
}
|
||||
|
||||
func (fm *FileManager) GetCacheWeight() int {
|
||||
return fm.cache.GetWeight()
|
||||
}
|
||||
|
||||
func (fm *FileManager) GetCacheBudget() int {
|
||||
return fm.cache.GetBudget()
|
||||
}
|
||||
func (fm *FileManager) LoadFile(filePath string) ([]byte, error) {
|
||||
filePath = fm.fixupFilePath(filePath)
|
||||
if value, found := fm.cache.Retrieve(filePath); found {
|
||||
return value.([]byte), nil
|
||||
}
|
||||
|
||||
archive, err := fm.archiveManager.LoadArchiveForFile(filePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := archive.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := fm.cache.Insert(filePath, data, len(data)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (fm *FileManager) FileExists(filePath string) (bool, error) {
|
||||
filePath = fm.fixupFilePath(filePath)
|
||||
return fm.archiveManager.FileExistsInArchive(filePath)
|
||||
}
|
||||
|
||||
func (fm *FileManager) fixupFilePath(filePath string) string {
|
||||
filePath = strings.ReplaceAll(filePath, "{LANG}", fm.config.Language)
|
||||
if strings.ToUpper(d2resource.LanguageCode) == "CHI" {
|
||||
filePath = strings.ReplaceAll(filePath, "{LANG_FONT}", fm.config.Language)
|
||||
} else {
|
||||
filePath = strings.ReplaceAll(filePath, "{LANG_FONT}", "latin")
|
||||
}
|
||||
|
||||
filePath = strings.ToLower(filePath)
|
||||
filePath = strings.ReplaceAll(filePath, `/`, "\\")
|
||||
filePath = strings.TrimPrefix(filePath, "\\")
|
||||
|
||||
return filePath
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package d2core
|
||||
package d2gamestate
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
@ -9,6 +9,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2hero"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||
@ -34,7 +36,7 @@ type GameState struct {
|
||||
HeroLevel int
|
||||
Act int
|
||||
FilePath string
|
||||
Equipment CharacterEquipment
|
||||
Equipment d2hero.CharacterEquipment
|
||||
}
|
||||
|
||||
const GameStateVersion = uint32(2) // Update this when you make breaking changes
|
@ -1,4 +1,4 @@
|
||||
package d2core
|
||||
package d2hero
|
||||
|
||||
type CharacterEquipment struct {
|
||||
Head *InventoryItemArmor // Head
|
@ -1,15 +1,15 @@
|
||||
package d2core
|
||||
package d2hero
|
||||
|
||||
import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2datadict"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2render"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2render/d2surface"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2assetmanager"
|
||||
)
|
||||
|
||||
type Hero struct {
|
||||
AnimatedEntity *d2render.AnimatedEntity
|
||||
AnimatedEntity *d2assetmanager.AnimatedEntity
|
||||
Equipment CharacterEquipment
|
||||
mode d2enum.AnimationMode
|
||||
direction int
|
||||
@ -32,7 +32,7 @@ func CreateHero(x, y int32, direction int, heroType d2enum.Hero, equipment Chara
|
||||
LH: equipment.LeftHand.ItemCode(),
|
||||
}
|
||||
|
||||
entity, err := d2render.CreateAnimatedEntity(x, y, object, d2resource.PaletteUnits)
|
||||
entity, err := d2assetmanager.CreateAnimatedEntity(x, y, object, d2resource.PaletteUnits)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -52,7 +52,7 @@ func (v *Hero) Advance(tickTime float64) {
|
||||
v.AnimatedEntity.Advance(tickTime)
|
||||
}
|
||||
|
||||
func (v *Hero) Render(target *d2surface.Surface) {
|
||||
func (v *Hero) Render(target d2common.Surface) {
|
||||
v.AnimatedEntity.Render(target)
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package d2core
|
||||
package d2hero
|
||||
|
||||
import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
@ -1,10 +1,10 @@
|
||||
package d2core
|
||||
package d2hero
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2datadict"
|
||||
)
|
||||
|
||||
type InventoryItemArmor struct {
|
@ -1,10 +1,10 @@
|
||||
package d2core
|
||||
package d2hero
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2datadict"
|
||||
)
|
||||
|
||||
type InventoryItemWeapon struct {
|
@ -1,23 +1,21 @@
|
||||
package d2core
|
||||
package d2npc
|
||||
|
||||
import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2datadict"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2render"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2render/d2surface"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2assetmanager"
|
||||
)
|
||||
|
||||
type NPC struct {
|
||||
AnimatedEntity *d2render.AnimatedEntity
|
||||
AnimatedEntity *d2assetmanager.AnimatedEntity
|
||||
HasPaths bool
|
||||
Paths []d2common.Path
|
||||
path int
|
||||
}
|
||||
|
||||
func CreateNPC(x, y int32, object *d2datadict.ObjectLookupRecord, direction int) *NPC {
|
||||
entity, err := d2render.CreateAnimatedEntity(x, y, object, d2resource.PaletteUnits)
|
||||
entity, err := d2assetmanager.CreateAnimatedEntity(x, y, object, d2resource.PaletteUnits)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -45,7 +43,7 @@ func (v *NPC) SetPaths(paths []d2common.Path) {
|
||||
v.HasPaths = len(paths) > 0
|
||||
}
|
||||
|
||||
func (v *NPC) Render(target *d2surface.Surface) {
|
||||
func (v *NPC) Render(target d2common.Surface) {
|
||||
v.AnimatedEntity.Render(target)
|
||||
}
|
||||
|
@ -1,18 +1,16 @@
|
||||
package d2asset
|
||||
package d2render
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"image/color"
|
||||
"math"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2helper"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2datadict"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2dc6"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2dcc"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2render/d2surface"
|
||||
|
||||
"github.com/hajimehoshi/ebiten"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dc6"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dcc"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2helper"
|
||||
)
|
||||
|
||||
type playMode int
|
||||
@ -29,7 +27,7 @@ type animationFrame struct {
|
||||
offsetX int
|
||||
offsetY int
|
||||
|
||||
image *ebiten.Image
|
||||
image d2common.Surface
|
||||
}
|
||||
|
||||
type animationDirection struct {
|
||||
@ -43,7 +41,7 @@ type Animation struct {
|
||||
lastFrameTime float64
|
||||
playedCount int
|
||||
|
||||
compositeMode ebiten.CompositeMode
|
||||
compositeMode d2common.CompositeMode
|
||||
colorMod color.Color
|
||||
|
||||
playMode playMode
|
||||
@ -51,7 +49,7 @@ type Animation struct {
|
||||
playLoop bool
|
||||
}
|
||||
|
||||
func createAnimationFromDCC(dcc *d2dcc.DCC, palette *d2datadict.PaletteRec, transparency int) (*Animation, error) {
|
||||
func CreateAnimationFromDCC(dcc *d2dcc.DCC, palette *d2datadict.PaletteRec, transparency int) (*Animation, error) {
|
||||
animation := &Animation{
|
||||
playLength: 1.0,
|
||||
playLoop: true,
|
||||
@ -85,7 +83,7 @@ func createAnimationFromDCC(dcc *d2dcc.DCC, palette *d2datadict.PaletteRec, tran
|
||||
}
|
||||
}
|
||||
|
||||
image, err := ebiten.NewImage(frameWidth, frameHeight, ebiten.FilterNearest)
|
||||
err, image := NewSurface(frameWidth, frameHeight, d2common.FilterNearest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -113,14 +111,14 @@ func createAnimationFromDCC(dcc *d2dcc.DCC, palette *d2datadict.PaletteRec, tran
|
||||
return animation, nil
|
||||
}
|
||||
|
||||
func createAnimationFromDC6(dc6 *d2dc6.DC6File) (*Animation, error) {
|
||||
func CreateAnimationFromDC6(dc6 *d2dc6.DC6File) (*Animation, error) {
|
||||
animation := &Animation{
|
||||
playLength: 1.0,
|
||||
playLoop: true,
|
||||
}
|
||||
|
||||
for frameIndex, dc6Frame := range dc6.Frames {
|
||||
image, err := ebiten.NewImage(int(dc6Frame.Width), int(dc6Frame.Height), ebiten.FilterNearest)
|
||||
err, image := NewSurface(int(dc6Frame.Width), int(dc6Frame.Height), d2common.FilterNearest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -147,7 +145,7 @@ func createAnimationFromDC6(dc6 *d2dc6.DC6File) (*Animation, error) {
|
||||
return animation, nil
|
||||
}
|
||||
|
||||
func (a *Animation) clone() *Animation {
|
||||
func (a *Animation) Clone() *Animation {
|
||||
animation := *a
|
||||
return &animation
|
||||
}
|
||||
@ -193,7 +191,7 @@ func (a *Animation) Advance(elapsed float64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Animation) Render(target *d2surface.Surface) error {
|
||||
func (a *Animation) Render(target d2common.Surface) error {
|
||||
direction := a.directions[a.directionIndex]
|
||||
frame := direction.frames[a.frameIndex]
|
||||
|
||||
@ -326,8 +324,8 @@ func (a *Animation) ResetPlayedCount() {
|
||||
|
||||
func (a *Animation) SetBlend(blend bool) {
|
||||
if blend {
|
||||
a.compositeMode = ebiten.CompositeModeLighter
|
||||
a.compositeMode = d2common.CompositeModeLighter
|
||||
} else {
|
||||
a.compositeMode = ebiten.CompositeModeSourceOver
|
||||
a.compositeMode = d2common.CompositeModeSourceOver
|
||||
}
|
||||
}
|
@ -3,21 +3,23 @@ package d2mapengine
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2gamestate"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2audio"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2audio"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2render/d2surface"
|
||||
)
|
||||
|
||||
type MapEntity interface {
|
||||
Render(target *d2surface.Surface)
|
||||
Render(target d2common.Surface)
|
||||
Advance(tickTime float64)
|
||||
GetPosition() (float64, float64)
|
||||
}
|
||||
|
||||
type MapEngine struct {
|
||||
soundManager *d2audio.Manager
|
||||
gameState *d2core.GameState
|
||||
gameState *d2gamestate.GameState
|
||||
|
||||
debugVisLevel int
|
||||
|
||||
@ -27,11 +29,10 @@ type MapEngine struct {
|
||||
camera Camera
|
||||
}
|
||||
|
||||
func CreateMapEngine(gameState *d2core.GameState, soundManager *d2audio.Manager) *MapEngine {
|
||||
func CreateMapEngine(gameState *d2gamestate.GameState) *MapEngine {
|
||||
engine := &MapEngine{
|
||||
gameState: gameState,
|
||||
soundManager: soundManager,
|
||||
viewport: NewViewport(0, 0, 800, 600),
|
||||
gameState: gameState,
|
||||
viewport: NewViewport(0, 0, 800, 600),
|
||||
}
|
||||
|
||||
engine.viewport.SetCamera(&engine.camera)
|
||||
@ -90,7 +91,7 @@ func (me *MapEngine) GenerateMap(regionType d2enum.RegionIdType, levelPreset int
|
||||
}
|
||||
|
||||
func (me *MapEngine) GenerateAct1Overworld() {
|
||||
me.soundManager.PlayBGM("/data/global/music/Act1/town1.wav") // TODO: Temp stuff here
|
||||
d2audio.PlayBGM("/data/global/music/Act1/town1.wav") // TODO: Temp stuff here
|
||||
|
||||
region, entities := loadRegion(me.gameState.Seed, 0, 0, d2enum.RegionAct1Town, 1, -1)
|
||||
me.regions = append(me.regions, region)
|
||||
@ -133,7 +134,7 @@ func (me *MapEngine) Advance(tickTime float64) {
|
||||
}
|
||||
}
|
||||
|
||||
func (me *MapEngine) Render(target *d2surface.Surface) {
|
||||
func (me *MapEngine) Render(target d2common.Surface) {
|
||||
for _, region := range me.regions {
|
||||
if region.isVisbile(me.viewport) {
|
||||
region.renderPass1(me.viewport, target)
|
@ -7,21 +7,20 @@ import (
|
||||
"math/rand"
|
||||
"strconv"
|
||||
|
||||
"github.com/hajimehoshi/ebiten"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2npc"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2assetmanager"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2helper"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2helper"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2asset"
|
||||
"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/d2common/d2resource"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2datadict"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2ds1"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2dt1"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2render"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2render/d2surface"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
|
||||
)
|
||||
|
||||
type MapRegion struct {
|
||||
@ -34,7 +33,7 @@ type MapRegion struct {
|
||||
palette d2datadict.PaletteRec
|
||||
startX float64
|
||||
startY float64
|
||||
imageCacheRecords map[uint32]*ebiten.Image
|
||||
imageCacheRecords map[uint32]d2common.Surface
|
||||
seed int64
|
||||
currentFrame int
|
||||
lastFrameTime float64
|
||||
@ -44,7 +43,7 @@ func loadRegion(seed int64, tileOffsetX, tileOffsetY int, levelType d2enum.Regio
|
||||
region := &MapRegion{
|
||||
levelType: d2datadict.LevelTypes[levelType],
|
||||
levelPreset: d2datadict.LevelPresets[levelPreset],
|
||||
imageCacheRecords: map[uint32]*ebiten.Image{},
|
||||
imageCacheRecords: map[uint32]d2common.Surface{},
|
||||
seed: seed,
|
||||
}
|
||||
|
||||
@ -55,7 +54,7 @@ func loadRegion(seed int64, tileOffsetX, tileOffsetY int, levelType d2enum.Regio
|
||||
|
||||
for _, levelTypeDt1 := range region.levelType.Files {
|
||||
if len(levelTypeDt1) != 0 && levelTypeDt1 != "" && levelTypeDt1 != "0" {
|
||||
fileData, err := d2asset.LoadFile("/data/global/tiles/" + levelTypeDt1)
|
||||
fileData, err := d2assetmanager.LoadFile("/data/global/tiles/" + levelTypeDt1)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -78,7 +77,7 @@ func loadRegion(seed int64, tileOffsetX, tileOffsetY int, levelType d2enum.Regio
|
||||
}
|
||||
|
||||
region.regionPath = levelFilesToPick[levelIndex]
|
||||
fileData, err := d2asset.LoadFile("/data/global/tiles/" + region.regionPath)
|
||||
fileData, err := d2assetmanager.LoadFile("/data/global/tiles/" + region.regionPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -137,13 +136,13 @@ func (mr *MapRegion) loadEntities() []MapEntity {
|
||||
switch object.Lookup.Type {
|
||||
case d2datadict.ObjectTypeCharacter:
|
||||
if object.Lookup.Base != "" && object.Lookup.Token != "" && object.Lookup.TR != "" {
|
||||
npc := d2core.CreateNPC(int32(worldX), int32(worldY), object.Lookup, 0)
|
||||
npc := d2npc.CreateNPC(int32(worldX), int32(worldY), object.Lookup, 0)
|
||||
npc.SetPaths(object.Paths)
|
||||
entities = append(entities, npc)
|
||||
}
|
||||
case d2datadict.ObjectTypeItem:
|
||||
if object.ObjectInfo != nil && object.ObjectInfo.Draw && object.Lookup.Base != "" && object.Lookup.Token != "" {
|
||||
entity, err := d2render.CreateAnimatedEntity(int32(worldX), int32(worldY), object.Lookup, d2resource.PaletteUnits)
|
||||
entity, err := d2assetmanager.CreateAnimatedEntity(int32(worldX), int32(worldY), object.Lookup, d2resource.PaletteUnits)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -231,7 +230,7 @@ func (mr *MapRegion) getTileWorldPosition(tileX, tileY int) (float64, float64) {
|
||||
return float64(tileX + mr.tileRect.Left), float64(tileY + mr.tileRect.Top)
|
||||
}
|
||||
|
||||
func (mr *MapRegion) renderPass1(viewport *Viewport, target *d2surface.Surface) {
|
||||
func (mr *MapRegion) renderPass1(viewport *Viewport, target d2common.Surface) {
|
||||
for tileY := range mr.ds1.Tiles {
|
||||
for tileX, tile := range mr.ds1.Tiles[tileY] {
|
||||
worldX, worldY := mr.getTileWorldPosition(tileX, tileY)
|
||||
@ -244,7 +243,7 @@ func (mr *MapRegion) renderPass1(viewport *Viewport, target *d2surface.Surface)
|
||||
}
|
||||
}
|
||||
|
||||
func (mr *MapRegion) renderPass2(entities []MapEntity, viewport *Viewport, target *d2surface.Surface) {
|
||||
func (mr *MapRegion) renderPass2(entities []MapEntity, viewport *Viewport, target d2common.Surface) {
|
||||
for tileY := range mr.ds1.Tiles {
|
||||
for tileX, tile := range mr.ds1.Tiles[tileY] {
|
||||
worldX, worldY := mr.getTileWorldPosition(tileX, tileY)
|
||||
@ -267,7 +266,7 @@ func (mr *MapRegion) renderPass2(entities []MapEntity, viewport *Viewport, targe
|
||||
}
|
||||
}
|
||||
|
||||
func (mr *MapRegion) renderPass3(viewport *Viewport, target *d2surface.Surface) {
|
||||
func (mr *MapRegion) renderPass3(viewport *Viewport, target d2common.Surface) {
|
||||
for tileY := range mr.ds1.Tiles {
|
||||
for tileX, tile := range mr.ds1.Tiles[tileY] {
|
||||
worldX, worldY := mr.getTileWorldPosition(tileX, tileY)
|
||||
@ -280,7 +279,7 @@ func (mr *MapRegion) renderPass3(viewport *Viewport, target *d2surface.Surface)
|
||||
}
|
||||
}
|
||||
|
||||
func (mr *MapRegion) renderTilePass1(tile d2ds1.TileRecord, viewport *Viewport, target *d2surface.Surface) {
|
||||
func (mr *MapRegion) renderTilePass1(tile d2ds1.TileRecord, viewport *Viewport, target d2common.Surface) {
|
||||
for _, wall := range tile.Walls {
|
||||
if !wall.Hidden && wall.Prop1 != 0 && wall.Type.LowerWall() {
|
||||
mr.renderWall(wall, viewport, target)
|
||||
@ -300,7 +299,7 @@ func (mr *MapRegion) renderTilePass1(tile d2ds1.TileRecord, viewport *Viewport,
|
||||
}
|
||||
}
|
||||
|
||||
func (mr *MapRegion) renderTilePass2(tile d2ds1.TileRecord, viewport *Viewport, target *d2surface.Surface) {
|
||||
func (mr *MapRegion) renderTilePass2(tile d2ds1.TileRecord, viewport *Viewport, target d2common.Surface) {
|
||||
for _, wall := range tile.Walls {
|
||||
if !wall.Hidden && wall.Type.UpperWall() {
|
||||
mr.renderWall(wall, viewport, target)
|
||||
@ -308,7 +307,7 @@ func (mr *MapRegion) renderTilePass2(tile d2ds1.TileRecord, viewport *Viewport,
|
||||
}
|
||||
}
|
||||
|
||||
func (mr *MapRegion) renderTilePass3(tile d2ds1.TileRecord, viewport *Viewport, target *d2surface.Surface) {
|
||||
func (mr *MapRegion) renderTilePass3(tile d2ds1.TileRecord, viewport *Viewport, target d2common.Surface) {
|
||||
for _, wall := range tile.Walls {
|
||||
if wall.Type == d2enum.Roof {
|
||||
mr.renderWall(wall, viewport, target)
|
||||
@ -316,8 +315,8 @@ func (mr *MapRegion) renderTilePass3(tile d2ds1.TileRecord, viewport *Viewport,
|
||||
}
|
||||
}
|
||||
|
||||
func (mr *MapRegion) renderFloor(tile d2ds1.FloorShadowRecord, viewport *Viewport, target *d2surface.Surface) {
|
||||
var img *ebiten.Image
|
||||
func (mr *MapRegion) renderFloor(tile d2ds1.FloorShadowRecord, viewport *Viewport, target d2common.Surface) {
|
||||
var img d2common.Surface
|
||||
if !tile.Animated {
|
||||
img = mr.getImageCacheRecord(tile.Style, tile.Sequence, 0, tile.RandomIndex)
|
||||
} else {
|
||||
@ -337,7 +336,7 @@ func (mr *MapRegion) renderFloor(tile d2ds1.FloorShadowRecord, viewport *Viewpor
|
||||
target.Render(img)
|
||||
}
|
||||
|
||||
func (mr *MapRegion) renderWall(tile d2ds1.WallRecord, viewport *Viewport, target *d2surface.Surface) {
|
||||
func (mr *MapRegion) renderWall(tile d2ds1.WallRecord, viewport *Viewport, target d2common.Surface) {
|
||||
img := mr.getImageCacheRecord(tile.Style, tile.Sequence, tile.Type, tile.RandomIndex)
|
||||
if img == nil {
|
||||
log.Printf("Render called on uncached wall {%v,%v,%v}", tile.Style, tile.Sequence, tile.Type)
|
||||
@ -353,7 +352,7 @@ func (mr *MapRegion) renderWall(tile d2ds1.WallRecord, viewport *Viewport, targe
|
||||
target.Render(img)
|
||||
}
|
||||
|
||||
func (mr *MapRegion) renderShadow(tile d2ds1.FloorShadowRecord, viewport *Viewport, target *d2surface.Surface) {
|
||||
func (mr *MapRegion) renderShadow(tile d2ds1.FloorShadowRecord, viewport *Viewport, target d2common.Surface) {
|
||||
img := mr.getImageCacheRecord(tile.Style, tile.Sequence, 13, tile.RandomIndex)
|
||||
if img == nil {
|
||||
log.Printf("Render called on uncached shadow {%v,%v}", tile.Style, tile.Sequence)
|
||||
@ -370,7 +369,7 @@ func (mr *MapRegion) renderShadow(tile d2ds1.FloorShadowRecord, viewport *Viewpo
|
||||
target.Render(img)
|
||||
}
|
||||
|
||||
func (mr *MapRegion) renderDebug(debugVisLevel int, viewport *Viewport, target *d2surface.Surface) {
|
||||
func (mr *MapRegion) renderDebug(debugVisLevel int, viewport *Viewport, target d2common.Surface) {
|
||||
for tileY := range mr.ds1.Tiles {
|
||||
for tileX := range mr.ds1.Tiles[tileY] {
|
||||
worldX, worldY := mr.getTileWorldPosition(tileX, tileY)
|
||||
@ -381,7 +380,7 @@ func (mr *MapRegion) renderDebug(debugVisLevel int, viewport *Viewport, target *
|
||||
}
|
||||
}
|
||||
|
||||
func (mr *MapRegion) renderTileDebug(x, y int, debugVisLevel int, viewport *Viewport, target *d2surface.Surface) {
|
||||
func (mr *MapRegion) renderTileDebug(x, y int, debugVisLevel int, viewport *Viewport, target d2common.Surface) {
|
||||
if debugVisLevel > 0 {
|
||||
subtileColor := color.RGBA{80, 80, 255, 100}
|
||||
tileColor := color.RGBA{255, 255, 255, 255}
|
||||
@ -449,12 +448,12 @@ func (mr *MapRegion) generateTileCache() {
|
||||
}
|
||||
}
|
||||
|
||||
func (mr *MapRegion) getImageCacheRecord(style, sequence byte, tileType d2enum.TileType, randomIndex byte) *ebiten.Image {
|
||||
func (mr *MapRegion) getImageCacheRecord(style, sequence byte, tileType d2enum.TileType, randomIndex byte) d2common.Surface {
|
||||
lookupIndex := uint32(style)<<24 | uint32(sequence)<<16 | uint32(tileType)<<8 | uint32(randomIndex)
|
||||
return mr.imageCacheRecords[lookupIndex]
|
||||
}
|
||||
|
||||
func (mr *MapRegion) setImageCacheRecord(style, sequence byte, tileType d2enum.TileType, randomIndex byte, image *ebiten.Image) {
|
||||
func (mr *MapRegion) setImageCacheRecord(style, sequence byte, tileType d2enum.TileType, randomIndex byte, image d2common.Surface) {
|
||||
lookupIndex := uint32(style)<<24 | uint32(sequence)<<16 | uint32(tileType)<<8 | uint32(randomIndex)
|
||||
mr.imageCacheRecords[lookupIndex] = image
|
||||
}
|
||||
@ -497,7 +496,7 @@ func (mr *MapRegion) generateFloorCache(tile *d2ds1.FloorShadowRecord, tileX, ti
|
||||
}
|
||||
tileYOffset := d2helper.AbsInt32(tileYMinimum)
|
||||
tileHeight := d2helper.AbsInt32(tileData[i].Height)
|
||||
image, _ := ebiten.NewImage(int(tileData[i].Width), int(tileHeight), ebiten.FilterNearest)
|
||||
_, image := d2render.NewSurface(int(tileData[i].Width), int(tileHeight), d2common.FilterNearest)
|
||||
pixels := make([]byte, 4*tileData[i].Width*tileHeight)
|
||||
mr.decodeTileGfxData(tileData[i].Blocks, &pixels, tileYOffset, tileData[i].Width)
|
||||
image.ReplacePixels(pixels)
|
||||
@ -532,7 +531,7 @@ func (mr *MapRegion) generateShadowCache(tile *d2ds1.FloorShadowRecord, tileX, t
|
||||
return
|
||||
}
|
||||
|
||||
image, _ := ebiten.NewImage(int(tileData.Width), int(tileHeight), ebiten.FilterNearest)
|
||||
_, image := d2render.NewSurface(int(tileData.Width), int(tileHeight), d2common.FilterNearest)
|
||||
pixels := make([]byte, 4*tileData.Width*int32(tileHeight))
|
||||
mr.decodeTileGfxData(tileData.Blocks, &pixels, tileYOffset, tileData.Width)
|
||||
image.ReplacePixels(pixels)
|
||||
@ -593,7 +592,7 @@ func (mr *MapRegion) generateWallCache(tile *d2ds1.WallRecord, tileX, tileY int)
|
||||
return
|
||||
}
|
||||
|
||||
image, _ := ebiten.NewImage(160, int(realHeight), ebiten.FilterNearest)
|
||||
_, image := d2render.NewSurface(160, int(realHeight), d2common.FilterNearest)
|
||||
pixels := make([]byte, 4*160*realHeight)
|
||||
mr.decodeTileGfxData(tileData.Blocks, &pixels, tileYOffset, 160)
|
||||
|
108
d2core/d2render/d2render.go
Normal file
108
d2core/d2render/d2render.go
Normal file
@ -0,0 +1,108 @@
|
||||
package d2render
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2input"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrHasInit error = errors.New("rendering system is already initialized")
|
||||
ErrNotInit error = errors.New("rendering system has not been initialized")
|
||||
ErrInvalidRenderer error = errors.New("invalid rendering system specified")
|
||||
)
|
||||
|
||||
var singleton d2interface.Renderer
|
||||
|
||||
func Initialize(rend d2interface.Renderer) error {
|
||||
if singleton != nil {
|
||||
return d2input.ErrHasInit
|
||||
}
|
||||
singleton = rend
|
||||
log.Printf("Initialized the %s renderer...", singleton.GetRendererName())
|
||||
return nil
|
||||
}
|
||||
|
||||
func SetWindowIcon(fileName string) error {
|
||||
if singleton == nil {
|
||||
return ErrNotInit
|
||||
}
|
||||
singleton.SetWindowIcon(fileName)
|
||||
return nil
|
||||
}
|
||||
|
||||
func Run(f func(d2common.Surface) error, width, height int, title string) error {
|
||||
if singleton == nil {
|
||||
return ErrNotInit
|
||||
}
|
||||
singleton.Run(f, width, height, title)
|
||||
return nil
|
||||
}
|
||||
|
||||
func IsDrawingSkipped() (error, bool) {
|
||||
if singleton == nil {
|
||||
return ErrNotInit, true
|
||||
}
|
||||
return nil, singleton.IsDrawingSkipped()
|
||||
}
|
||||
|
||||
func CreateSurface(surface d2common.Surface) (error, d2common.Surface) {
|
||||
if singleton == nil {
|
||||
return ErrNotInit, nil
|
||||
}
|
||||
return singleton.CreateSurface(surface)
|
||||
}
|
||||
|
||||
func NewSurface(width, height int, filter d2common.Filter) (error, d2common.Surface) {
|
||||
if singleton == nil {
|
||||
return ErrNotInit, nil
|
||||
}
|
||||
return singleton.NewSurface(width, height, filter)
|
||||
}
|
||||
|
||||
func IsFullScreen() (bool, error) {
|
||||
if singleton == nil {
|
||||
return false, ErrNotInit
|
||||
}
|
||||
return singleton.IsFullScreen()
|
||||
}
|
||||
|
||||
func SetFullScreen(fullScreen bool) error {
|
||||
if singleton == nil {
|
||||
return ErrNotInit
|
||||
}
|
||||
return singleton.SetFullScreen(fullScreen)
|
||||
}
|
||||
|
||||
func SetVSyncEnabled(vsync bool) error {
|
||||
if singleton == nil {
|
||||
return ErrNotInit
|
||||
}
|
||||
return singleton.SetVSyncEnabled(vsync)
|
||||
}
|
||||
|
||||
func GetVSyncEnabled() (bool, error) {
|
||||
if singleton == nil {
|
||||
return false, ErrNotInit
|
||||
}
|
||||
return singleton.GetVSyncEnabled()
|
||||
}
|
||||
|
||||
func GetCursorPos() (int, int, error) {
|
||||
if singleton == nil {
|
||||
return 0, 0, ErrNotInit
|
||||
}
|
||||
return singleton.GetCursorPos()
|
||||
}
|
||||
|
||||
func CurrentFPS() (float64, error) {
|
||||
if singleton == nil {
|
||||
return 0, ErrNotInit
|
||||
}
|
||||
return singleton.CurrentFPS(), nil
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package d2corehelper
|
||||
package ebiten
|
||||
|
||||
import (
|
||||
"image/color"
|
72
d2core/d2render/ebiten/composite_mode_helper.go
Normal file
72
d2core/d2render/ebiten/composite_mode_helper.go
Normal file
@ -0,0 +1,72 @@
|
||||
package ebiten
|
||||
|
||||
import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
"github.com/hajimehoshi/ebiten"
|
||||
)
|
||||
|
||||
func d2ToEbitenCompositeMode(comp d2common.CompositeMode) ebiten.CompositeMode {
|
||||
switch comp {
|
||||
case d2common.CompositeModeSourceOver:
|
||||
return ebiten.CompositeModeSourceOver
|
||||
case d2common.CompositeModeClear:
|
||||
return ebiten.CompositeModeClear
|
||||
case d2common.CompositeModeCopy:
|
||||
return ebiten.CompositeModeCopy
|
||||
case d2common.CompositeModeDestination:
|
||||
return ebiten.CompositeModeDestination
|
||||
case d2common.CompositeModeDestinationOver:
|
||||
return ebiten.CompositeModeDestinationOver
|
||||
case d2common.CompositeModeSourceIn:
|
||||
return ebiten.CompositeModeSourceIn
|
||||
case d2common.CompositeModeDestinationIn:
|
||||
return ebiten.CompositeModeDestinationIn
|
||||
case d2common.CompositeModeSourceOut:
|
||||
return ebiten.CompositeModeSourceOut
|
||||
case d2common.CompositeModeDestinationOut:
|
||||
return ebiten.CompositeModeDestinationOut
|
||||
case d2common.CompositeModeSourceAtop:
|
||||
return ebiten.CompositeModeSourceAtop
|
||||
case d2common.CompositeModeDestinationAtop:
|
||||
return ebiten.CompositeModeDestinationAtop
|
||||
case d2common.CompositeModeXor:
|
||||
return ebiten.CompositeModeXor
|
||||
case d2common.CompositeModeLighter:
|
||||
return ebiten.CompositeModeLighter
|
||||
}
|
||||
|
||||
return ebiten.CompositeModeSourceOver
|
||||
}
|
||||
|
||||
func ebitenToD2CompositeMode(comp ebiten.CompositeMode) d2common.CompositeMode {
|
||||
switch comp {
|
||||
case ebiten.CompositeModeSourceOver:
|
||||
return d2common.CompositeModeSourceOver
|
||||
case ebiten.CompositeModeClear:
|
||||
return d2common.CompositeModeClear
|
||||
case ebiten.CompositeModeCopy:
|
||||
return d2common.CompositeModeCopy
|
||||
case ebiten.CompositeModeDestination:
|
||||
return d2common.CompositeModeDestination
|
||||
case ebiten.CompositeModeDestinationOver:
|
||||
return d2common.CompositeModeDestinationOver
|
||||
case ebiten.CompositeModeSourceIn:
|
||||
return d2common.CompositeModeSourceIn
|
||||
case ebiten.CompositeModeDestinationIn:
|
||||
return d2common.CompositeModeDestinationIn
|
||||
case ebiten.CompositeModeSourceOut:
|
||||
return d2common.CompositeModeSourceOut
|
||||
case ebiten.CompositeModeDestinationOut:
|
||||
return d2common.CompositeModeDestinationOut
|
||||
case ebiten.CompositeModeSourceAtop:
|
||||
return d2common.CompositeModeSourceAtop
|
||||
case ebiten.CompositeModeDestinationAtop:
|
||||
return d2common.CompositeModeDestinationAtop
|
||||
case ebiten.CompositeModeXor:
|
||||
return d2common.CompositeModeXor
|
||||
case ebiten.CompositeModeLighter:
|
||||
return d2common.CompositeModeLighter
|
||||
}
|
||||
|
||||
return d2common.CompositeModeSourceOver
|
||||
}
|
116
d2core/d2render/ebiten/ebiten_renderer.go
Normal file
116
d2core/d2render/ebiten/ebiten_renderer.go
Normal file
@ -0,0 +1,116 @@
|
||||
package ebiten
|
||||
|
||||
import (
|
||||
"image"
|
||||
"log"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2config"
|
||||
|
||||
"github.com/hajimehoshi/ebiten"
|
||||
"github.com/hajimehoshi/ebiten/ebitenutil"
|
||||
)
|
||||
|
||||
type EbitenRenderer struct {
|
||||
}
|
||||
|
||||
func CreateRenderer() (*EbitenRenderer, error) {
|
||||
result := &EbitenRenderer{}
|
||||
|
||||
config, err := d2config.Get()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ebiten.SetCursorVisible(false)
|
||||
ebiten.SetFullscreen(config.FullScreen)
|
||||
ebiten.SetRunnableInBackground(config.RunInBackground)
|
||||
ebiten.SetVsyncEnabled(config.VsyncEnabled)
|
||||
ebiten.SetMaxTPS(config.TicksPerSecond)
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (*EbitenRenderer) GetRendererName() string {
|
||||
return "Ebiten"
|
||||
}
|
||||
|
||||
func (*EbitenRenderer) SetWindowIcon(fileName string) {
|
||||
_, iconImage, err := ebitenutil.NewImageFromFile(fileName, ebiten.FilterLinear)
|
||||
if err == nil {
|
||||
ebiten.SetWindowIcon([]image.Image{iconImage})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (r *EbitenRenderer) IsDrawingSkipped() bool {
|
||||
return ebiten.IsDrawingSkipped()
|
||||
}
|
||||
|
||||
func (r *EbitenRenderer) Run(f func(surface d2common.Surface) error, width, height int, title string) error {
|
||||
config, err := d2config.Get()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return err
|
||||
}
|
||||
|
||||
return ebiten.Run(func(img *ebiten.Image) error {
|
||||
err := f(&ebitenSurface{image: img})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}, width, height, config.Scale, title)
|
||||
}
|
||||
|
||||
func (r *EbitenRenderer) CreateSurface(surface d2common.Surface) (error, d2common.Surface) {
|
||||
result := &ebitenSurface{
|
||||
image: surface.(*ebitenSurface).image,
|
||||
stateCurrent: surfaceState{
|
||||
filter: ebiten.FilterNearest,
|
||||
mode: ebiten.CompositeModeSourceOver,
|
||||
},
|
||||
}
|
||||
return nil, result
|
||||
}
|
||||
|
||||
func (r *EbitenRenderer) NewSurface(width, height int, filter d2common.Filter) (error, d2common.Surface) {
|
||||
ebitenFilter := d2ToEbitenFilter(filter)
|
||||
img, err := ebiten.NewImage(width, height, ebitenFilter)
|
||||
if err != nil {
|
||||
return err, nil
|
||||
}
|
||||
result := &ebitenSurface{
|
||||
image: img,
|
||||
}
|
||||
return nil, result
|
||||
}
|
||||
|
||||
func (r *EbitenRenderer) IsFullScreen() (bool, error) {
|
||||
return ebiten.IsFullscreen(), nil
|
||||
}
|
||||
|
||||
func (r *EbitenRenderer) SetFullScreen(fullScreen bool) error {
|
||||
ebiten.SetFullscreen(fullScreen)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *EbitenRenderer) SetVSyncEnabled(vsync bool) error {
|
||||
ebiten.SetVsyncEnabled(vsync)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *EbitenRenderer) GetVSyncEnabled() (bool, error) {
|
||||
return ebiten.IsVsyncEnabled(), nil
|
||||
}
|
||||
|
||||
func (r *EbitenRenderer) GetCursorPos() (int, int, error) {
|
||||
cx, cy := ebiten.CursorPosition()
|
||||
return cx, cy, nil
|
||||
}
|
||||
|
||||
func (r *EbitenRenderer) CurrentFPS() float64 {
|
||||
return ebiten.CurrentFPS()
|
||||
}
|
@ -1,60 +1,43 @@
|
||||
package d2surface
|
||||
package ebiten
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image/color"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2corehelper"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
|
||||
"github.com/hajimehoshi/ebiten"
|
||||
"github.com/hajimehoshi/ebiten/ebitenutil"
|
||||
)
|
||||
|
||||
type surfaceState struct {
|
||||
x int
|
||||
y int
|
||||
mode ebiten.CompositeMode
|
||||
filter ebiten.Filter
|
||||
color color.Color
|
||||
}
|
||||
|
||||
type Surface struct {
|
||||
type ebitenSurface struct {
|
||||
stateStack []surfaceState
|
||||
stateCurrent surfaceState
|
||||
image *ebiten.Image
|
||||
}
|
||||
|
||||
func CreateSurface(image *ebiten.Image) *Surface {
|
||||
return &Surface{
|
||||
image: image,
|
||||
stateCurrent: surfaceState{
|
||||
filter: ebiten.FilterNearest,
|
||||
mode: ebiten.CompositeModeSourceOver,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Surface) PushTranslation(x, y int) {
|
||||
func (s *ebitenSurface) PushTranslation(x, y int) {
|
||||
s.stateStack = append(s.stateStack, s.stateCurrent)
|
||||
s.stateCurrent.x += x
|
||||
s.stateCurrent.y += y
|
||||
}
|
||||
|
||||
func (s *Surface) PushCompositeMode(mode ebiten.CompositeMode) {
|
||||
func (s *ebitenSurface) PushCompositeMode(mode d2common.CompositeMode) {
|
||||
s.stateStack = append(s.stateStack, s.stateCurrent)
|
||||
s.stateCurrent.mode = mode
|
||||
s.stateCurrent.mode = d2ToEbitenCompositeMode(mode)
|
||||
}
|
||||
|
||||
func (s *Surface) PushFilter(filter ebiten.Filter) {
|
||||
func (s *ebitenSurface) PushFilter(filter d2common.Filter) {
|
||||
s.stateStack = append(s.stateStack, s.stateCurrent)
|
||||
s.stateCurrent.filter = filter
|
||||
s.stateCurrent.filter = d2ToEbitenFilter(filter)
|
||||
}
|
||||
|
||||
func (s *Surface) PushColor(color color.Color) {
|
||||
func (s *ebitenSurface) PushColor(color color.Color) {
|
||||
s.stateStack = append(s.stateStack, s.stateCurrent)
|
||||
s.stateCurrent.color = color
|
||||
}
|
||||
|
||||
func (s *Surface) Pop() {
|
||||
func (s *ebitenSurface) Pop() {
|
||||
count := len(s.stateStack)
|
||||
if count == 0 {
|
||||
panic("empty stack")
|
||||
@ -64,28 +47,29 @@ func (s *Surface) Pop() {
|
||||
s.stateStack = s.stateStack[:count-1]
|
||||
}
|
||||
|
||||
func (s *Surface) PopN(n int) {
|
||||
func (s *ebitenSurface) PopN(n int) {
|
||||
for i := 0; i < n; i++ {
|
||||
s.Pop()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Surface) Render(image *ebiten.Image) error {
|
||||
func (s *ebitenSurface) Render(sfc d2common.Surface) error {
|
||||
opts := &ebiten.DrawImageOptions{CompositeMode: s.stateCurrent.mode}
|
||||
opts.GeoM.Translate(float64(s.stateCurrent.x), float64(s.stateCurrent.y))
|
||||
opts.Filter = s.stateCurrent.filter
|
||||
if s.stateCurrent.color != nil {
|
||||
opts.ColorM = d2corehelper.ColorToColorM(s.stateCurrent.color)
|
||||
opts.ColorM = ColorToColorM(s.stateCurrent.color)
|
||||
}
|
||||
|
||||
return s.image.DrawImage(image, opts)
|
||||
var img = sfc.(*ebitenSurface).image
|
||||
return s.image.DrawImage(img, opts)
|
||||
}
|
||||
|
||||
func (s *Surface) DrawText(format string, params ...interface{}) {
|
||||
func (s *ebitenSurface) DrawText(format string, params ...interface{}) {
|
||||
ebitenutil.DebugPrintAt(s.image, fmt.Sprintf(format, params...), s.stateCurrent.x, s.stateCurrent.y)
|
||||
}
|
||||
|
||||
func (s *Surface) DrawLine(x, y int, color color.Color) {
|
||||
func (s *ebitenSurface) DrawLine(x, y int, color color.Color) {
|
||||
ebitenutil.DrawLine(
|
||||
s.image,
|
||||
float64(s.stateCurrent.x),
|
||||
@ -96,7 +80,7 @@ func (s *Surface) DrawLine(x, y int, color color.Color) {
|
||||
)
|
||||
}
|
||||
|
||||
func (s *Surface) DrawRect(width, height int, color color.Color) {
|
||||
func (s *ebitenSurface) DrawRect(width, height int, color color.Color) {
|
||||
ebitenutil.DrawRect(
|
||||
s.image,
|
||||
float64(s.stateCurrent.x),
|
||||
@ -107,14 +91,18 @@ func (s *Surface) DrawRect(width, height int, color color.Color) {
|
||||
)
|
||||
}
|
||||
|
||||
func (s *Surface) Clear(color color.Color) error {
|
||||
func (s *ebitenSurface) Clear(color color.Color) error {
|
||||
return s.image.Fill(color)
|
||||
}
|
||||
|
||||
func (s *Surface) GetSize() (int, int) {
|
||||
func (s *ebitenSurface) GetSize() (int, int) {
|
||||
return s.image.Size()
|
||||
}
|
||||
|
||||
func (s *Surface) GetDepth() int {
|
||||
func (s *ebitenSurface) GetDepth() int {
|
||||
return len(s.stateStack)
|
||||
}
|
||||
|
||||
func (s *ebitenSurface) ReplacePixels(pixels []byte) error {
|
||||
return s.image.ReplacePixels(pixels)
|
||||
}
|
32
d2core/d2render/ebiten/filter_helper.go
Normal file
32
d2core/d2render/ebiten/filter_helper.go
Normal file
@ -0,0 +1,32 @@
|
||||
package ebiten
|
||||
|
||||
import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
"github.com/hajimehoshi/ebiten"
|
||||
)
|
||||
|
||||
func d2ToEbitenFilter(filter d2common.Filter) ebiten.Filter {
|
||||
switch filter {
|
||||
case d2common.FilterDefault:
|
||||
return ebiten.FilterDefault
|
||||
case d2common.FilterLinear:
|
||||
return ebiten.FilterLinear
|
||||
case d2common.FilterNearest:
|
||||
return ebiten.FilterNearest
|
||||
}
|
||||
|
||||
return ebiten.FilterDefault
|
||||
}
|
||||
|
||||
func ebitenToD2Filter(filter ebiten.Filter) d2common.Filter {
|
||||
switch filter {
|
||||
case ebiten.FilterDefault:
|
||||
return d2common.FilterDefault
|
||||
case ebiten.FilterLinear:
|
||||
return d2common.FilterLinear
|
||||
case ebiten.FilterNearest:
|
||||
return d2common.FilterNearest
|
||||
}
|
||||
|
||||
return d2common.FilterDefault
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user