1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-06-07 00:10:43 +00:00

Minor changes to project layout (#276)

* Minor changes to reduce interdependencies on modules.
This commit is contained in:
Tim Sarbin 2020-01-31 23:18:11 -05:00 committed by GitHub
parent 6832a5a0db
commit 2461142fbd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
139 changed files with 2614 additions and 1935 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
package d2asset package d2common
import ( import (
"errors" "errors"
@ -14,7 +14,7 @@ type cacheNode struct {
weight int weight int
} }
type cache struct { type Cache struct {
head *cacheNode head *cacheNode
tail *cacheNode tail *cacheNode
lookup map[string]*cacheNode lookup map[string]*cacheNode
@ -24,16 +24,27 @@ type cache struct {
mutex sync.Mutex mutex sync.Mutex
} }
func createCache(budget int) *cache { func CreateCache(budget int) *Cache {
return &cache{lookup: make(map[string]*cacheNode), budget: budget} 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() c.mutex.Lock()
defer c.mutex.Unlock() defer c.mutex.Unlock()
if _, found := c.lookup[key]; found { if _, found := c.lookup[key]; found {
return errors.New("key already exists in cache") return errors.New("key already exists in Cache")
} }
node := &cacheNode{ node := &cacheNode{
@ -61,7 +72,7 @@ func (c *cache) insert(key string, value interface{}, weight int) error {
if c.verbose { if c.verbose {
log.Printf( 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.key,
c.tail.weight, c.tail.weight,
key, key,
@ -76,7 +87,7 @@ func (c *cache) insert(key string, value interface{}, weight int) error {
return nil return nil
} }
func (c *cache) retrieve(key string) (interface{}, bool) { func (c *Cache) Retrieve(key string) (interface{}, bool) {
c.mutex.Lock() c.mutex.Lock()
defer c.mutex.Unlock() defer c.mutex.Unlock()
@ -110,7 +121,7 @@ func (c *cache) retrieve(key string) (interface{}, bool) {
return node.value, true return node.value, true
} }
func (c *cache) clear() { func (c *Cache) Clear() {
c.mutex.Lock() c.mutex.Lock()
defer c.mutex.Unlock() defer c.mutex.Unlock()

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

View File

@ -1,9 +1,6 @@
package d2corecommon package d2config
import ( import (
"encoding/json"
"log"
"os"
"os/user" "os/user"
"path" "path"
"runtime" "runtime"
@ -24,56 +21,7 @@ type Configuration struct {
BgmVolume float64 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 { func getConfigurationPath() string {
configDir, err := os.UserConfigDir() configDir, err := os.UserConfigDir()
if err != nil { if err != nil {
@ -82,11 +30,11 @@ func getConfigurationPath() string {
return path.Join(configDir, "OpenDiablo2/config.json") return path.Join(configDir, "OpenDiablo2/config.json")
} }
*/
func getDefaultConfiguration() *Configuration { func getDefaultConfiguration() *Configuration {
config := &Configuration{ config := &Configuration{
Language: "ENG", Language: "ENG",
FullScreen: true, FullScreen: false,
Scale: 1, Scale: 1,
TicksPerSecond: -1, TicksPerSecond: -1,
RunInBackground: true, RunInBackground: true,
@ -100,7 +48,7 @@ func getDefaultConfiguration() *Configuration {
"d2xmusic.mpq", "d2xmusic.mpq",
"d2xtalk.mpq", "d2xtalk.mpq",
"d2xvideo.mpq", "d2xvideo.mpq",
"github.com/OpenDiablo2/OpenDiablo2/d2data.mpq", "d2datadict.mpq",
"d2char.mpq", "d2char.mpq",
"d2music.mpq", "d2music.mpq",
"d2sfx.mpq", "d2sfx.mpq",

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

View File

@ -4,10 +4,6 @@ import (
"log" "log"
"strings" "strings"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2common" "github.com/OpenDiablo2/OpenDiablo2/d2common"
) )
@ -27,9 +23,8 @@ type AnimationDataRecord struct {
var AnimationData map[string][]*AnimationDataRecord var AnimationData map[string][]*AnimationDataRecord
// LoadAnimationData loads the animation data table into the global AnimationData dictionary // 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) AnimationData = make(map[string][]*AnimationDataRecord)
rawData := fileProvider.LoadFile(d2resource.AnimationData)
streamReader := d2common.CreateStreamReader(rawData) streamReader := d2common.CreateStreamReader(rawData)
for !streamReader.Eof() { for !streamReader.Eof() {
dataCount := int(streamReader.GetInt32()) dataCount := int(streamReader.GetInt32())

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

View File

@ -7,8 +7,6 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common" "github.com/OpenDiablo2/OpenDiablo2/d2common"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
) )
type ItemCommonRecord struct { type ItemCommonRecord struct {
@ -119,7 +117,6 @@ type ItemCommonRecord struct {
Nameable bool // if true, item can be personalized Nameable bool // if true, item can be personalized
// weapon params // weapon params
BarbOneOrTwoHanded bool // if true, barb can wield this in one or two hands BarbOneOrTwoHanded bool // if true, barb can wield this in one or two hands
UsesTwoHands bool // if true, it's a 2handed weapon 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) 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 WeaponClass2Hand string // what kind of attack when wielded with two hands
HitClass string // determines sounds/graphic effects when attacking 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 SpecialFeature string // Just a comment
@ -147,24 +144,24 @@ type ItemCommonRecord struct {
// misc params // misc params
FlavorText string // unknown, probably just for reference 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 TransmogCode string // the 3 char code representing the item this becomes via transmog
TransmogMin int // min amount of the transmog item to create TransmogMin int // min amount of the transmog item to create
TransmogMax int // max '' TransmogMax int // max ''
AutoBelt bool // if true, item is put into your belt when picked up 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? 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 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 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 CureOverlayStates [2]string // name of the overlay states that are removed upon use of this item
EffectLength int // timer for timed usage effects EffectLength int // timer for timed usage effects
UsageStats [3]ItemUsageStat // stat boosts applied upon usage UsageStats [3]ItemUsageStat // stat boosts applied upon usage
SpellDescriptionType int // specifies how to format the usage description SpellDescriptionType int // specifies how to format the usage description
// 0 = none, 1 = use desc string, 2 = use desc string + calc value // 0 = none, 1 = use desc string, 2 = use desc string + calc value
SpellDescriptionString string // points to a string containing the description 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) 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 MagicLevel uint8
} }
// Loading Functions // Loading Functions
var CommonItems map[string]*ItemCommonRecord 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 { if CommonItems == nil {
CommonItems = make(map[string]*ItemCommonRecord) CommonItems = make(map[string]*ItemCommonRecord)
} }
items := 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]) mapping := MapHeaders(data[0])
for lineno, line := range data { for lineno, line := range data {
if lineno == 0 { if lineno == 0 {
@ -324,14 +319,14 @@ func createCommonItemRecord(line string, mapping *map[string]int, source d2enum.
MaxMissileDamage: MapLoadInt(&r, mapping, "maxmisdam"), MaxMissileDamage: MapLoadInt(&r, mapping, "maxmisdam"),
MissileSpeed: MapLoadInt(&r, mapping, "misspeed"), MissileSpeed: MapLoadInt(&r, mapping, "misspeed"),
ExtraRange: MapLoadInt(&r, mapping, "rangeadder"), ExtraRange: MapLoadInt(&r, mapping, "rangeadder"),
RequiredDexterity: MapLoadInt(&r, mapping, "reqdex"), RequiredDexterity: MapLoadInt(&r, mapping, "reqdex"),
WeaponClass: MapLoadString(&r, mapping, "wclass"), WeaponClass: MapLoadString(&r, mapping, "wclass"),
WeaponClass2Hand: MapLoadString(&r, mapping, "2handedwclass"), WeaponClass2Hand: MapLoadString(&r, mapping, "2handedwclass"),
HitClass: MapLoadString(&r, mapping, "hit class"), HitClass: MapLoadString(&r, mapping, "hit class"),
SpawnStack: MapLoadInt(&r, mapping, "spawnstack"), SpawnStack: MapLoadInt(&r, mapping, "spawnstack"),
SpecialFeature: MapLoadString(&r, mapping, "special"), SpecialFeature: MapLoadString(&r, mapping, "special"),
@ -346,30 +341,30 @@ func createCommonItemRecord(line string, mapping *map[string]int, source d2enum.
TransmogCode: MapLoadString(&r, mapping, "TMogType"), TransmogCode: MapLoadString(&r, mapping, "TMogType"),
TransmogMin: MapLoadInt(&r, mapping, "TMogMin"), TransmogMin: MapLoadInt(&r, mapping, "TMogMin"),
TransmogMax: MapLoadInt(&r, mapping, "TMogMax"), TransmogMax: MapLoadInt(&r, mapping, "TMogMax"),
AutoBelt: MapLoadBool(&r, mapping, "autobelt"), AutoBelt: MapLoadBool(&r, mapping, "autobelt"),
SpellIcon: MapLoadInt(&r, mapping, "spellicon"), SpellIcon: MapLoadInt(&r, mapping, "spellicon"),
SpellType: MapLoadInt(&r, mapping, "pSpell"), SpellType: MapLoadInt(&r, mapping, "pSpell"),
OverlayState: MapLoadString(&r, mapping, "state"), OverlayState: MapLoadString(&r, mapping, "state"),
CureOverlayStates: [2]string{ CureOverlayStates: [2]string{
MapLoadString(&r, mapping, "cstate1"), MapLoadString(&r, mapping, "cstate1"),
MapLoadString(&r, mapping, "cstate2"), MapLoadString(&r, mapping, "cstate2"),
}, },
EffectLength: MapLoadInt(&r, mapping, "len"), EffectLength: MapLoadInt(&r, mapping, "len"),
UsageStats: createItemUsageStats(&r, mapping), UsageStats: createItemUsageStats(&r, mapping),
SpellDescriptionType: MapLoadInt(&r, mapping, "spelldesc"), SpellDescriptionType: MapLoadInt(&r, mapping, "spelldesc"),
// 0 = none, 1 = use desc string, 2 = use desc string + calc value // 0 = none, 1 = use desc string, 2 = use desc string + calc value
SpellDescriptionString: MapLoadString(&r, mapping, "spelldescstr"), SpellDescriptionString: MapLoadString(&r, mapping, "spelldescstr"),
SpellDescriptionCalc: d2common.CalcString(MapLoadString(&r, mapping, "spelldesccalc")), SpellDescriptionCalc: d2common.CalcString(MapLoadString(&r, mapping, "spelldesccalc")),
BetterGem: MapLoadString(&r, mapping, "BetterGem"), BetterGem: MapLoadString(&r, mapping, "BetterGem"),
Multibuy: MapLoadBool(&r, mapping, "multibuy"), Multibuy: MapLoadBool(&r, mapping, "multibuy"),
} }
return result return result
} }
func createItemVendorParams(r *[]string, mapping *map[string]int) map[string]*ItemVendorParams { func createItemVendorParams(r *[]string, mapping *map[string]int) map[string]*ItemVendorParams {
vs := make([]string, 17) vs := make([]string, 17)
@ -395,11 +390,11 @@ func createItemVendorParams(r *[]string, mapping *map[string]int) map[string]*It
for _, name := range vs { for _, name := range vs {
wvp := ItemVendorParams{ wvp := ItemVendorParams{
Min: MapLoadInt(r, mapping, name + "Min"), Min: MapLoadInt(r, mapping, name+"Min"),
Max: MapLoadInt(r, mapping, name + "Max"), Max: MapLoadInt(r, mapping, name+"Max"),
MagicMin: MapLoadInt(r, mapping, name + "MagicMin"), MagicMin: MapLoadInt(r, mapping, name+"MagicMin"),
MagicMax: MapLoadInt(r, mapping, name + "MagicMax"), MagicMax: MapLoadInt(r, mapping, name+"MagicMax"),
MagicLevel: MapLoadUint8(r, mapping, name + "MagicLvl"), MagicLevel: MapLoadUint8(r, mapping, name+"MagicLvl"),
} }
result[name] = &wvp 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 { func createItemUsageStats(r *[]string, mapping *map[string]int) [3]ItemUsageStat {
result := [3]ItemUsageStat{} result := [3]ItemUsageStat{}
for i := 0; i < 3; i++ { for i := 0; i < 3; i++ {
result[i].Stat = MapLoadString(r, mapping, "stat" + 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))) result[i].Calc = d2common.CalcString(MapLoadString(r, mapping, "calc"+strconv.Itoa(i)))
} }
return result return result
} }

View File

@ -4,11 +4,7 @@ import (
"log" "log"
"strings" "strings"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" dh "github.com/OpenDiablo2/OpenDiablo2/d2common/d2helper"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
dh "github.com/OpenDiablo2/OpenDiablo2/d2helper"
) )
type LevelPresetRecord struct { type LevelPresetRecord struct {
@ -75,9 +71,9 @@ func createLevelPresetRecord(props []string) LevelPresetRecord {
var LevelPresets map[int]LevelPresetRecord var LevelPresets map[int]LevelPresetRecord
func LoadLevelPresets(fileProvider d2interface.FileProvider) { func LoadLevelPresets(file []byte) {
LevelPresets = make(map[int]LevelPresetRecord) 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 { for _, line := range data {
if len(line) == 0 { if len(line) == 0 {
continue continue

View File

@ -4,11 +4,7 @@ import (
"log" "log"
"strings" "strings"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" dh "github.com/OpenDiablo2/OpenDiablo2/d2common/d2helper"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
dh "github.com/OpenDiablo2/OpenDiablo2/d2helper"
) )
type LevelTypeRecord struct { type LevelTypeRecord struct {
@ -22,8 +18,8 @@ type LevelTypeRecord struct {
var LevelTypes []LevelTypeRecord var LevelTypes []LevelTypeRecord
func LoadLevelTypes(fileProvider d2interface.FileProvider) { func LoadLevelTypes(file []byte) {
data := strings.Split(string(fileProvider.LoadFile(d2resource.LevelType)), "\r\n")[1:] data := strings.Split(string(file), "\r\n")[1:]
LevelTypes = make([]LevelTypeRecord, len(data)) LevelTypes = make([]LevelTypeRecord, len(data))
for i, j := 0, 0; i < len(data); i, j = i+1, j+1 { for i, j := 0, 0; i < len(data); i, j = i+1, j+1 {
idx := -1 idx := -1

View File

@ -3,10 +3,6 @@ package d2datadict
import ( import (
"log" "log"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2common" "github.com/OpenDiablo2/OpenDiablo2/d2common"
) )
@ -27,9 +23,8 @@ type LevelWarpRecord struct {
var LevelWarps map[int]*LevelWarpRecord var LevelWarps map[int]*LevelWarpRecord
func LoadLevelWarps(fileProvider d2interface.FileProvider) { func LoadLevelWarps(levelWarpData []byte) {
LevelWarps = make(map[int]*LevelWarpRecord) LevelWarps = make(map[int]*LevelWarpRecord)
levelWarpData := fileProvider.LoadFile(d2resource.LevelWarp)
streamReader := d2common.CreateStreamReader(levelWarpData) streamReader := d2common.CreateStreamReader(levelWarpData)
numRecords := int(streamReader.GetInt32()) numRecords := int(streamReader.GetInt32())
for i := 0; i < numRecords; i++ { for i := 0; i < numRecords; i++ {

View File

@ -1,8 +1,9 @@
package d2datadict package d2datadict
import ( import (
dh "github.com/OpenDiablo2/OpenDiablo2/d2helper"
"strings" "strings"
dh "github.com/OpenDiablo2/OpenDiablo2/d2common/d2helper"
) )
func MapHeaders(line string) map[string]int { func MapHeaders(line string) map[string]int {

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

View File

@ -6,11 +6,7 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common" "github.com/OpenDiablo2/OpenDiablo2/d2common"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" dh "github.com/OpenDiablo2/OpenDiablo2/d2common/d2helper"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
dh "github.com/OpenDiablo2/OpenDiablo2/d2helper"
) )
type MissileCalcParam struct { type MissileCalcParam struct {
@ -297,9 +293,9 @@ func createMissileRecord(line string) MissileRecord {
var Missiles map[int]*MissileRecord var Missiles map[int]*MissileRecord
func LoadMissiles(fileProvider d2interface.FileProvider) { func LoadMissiles(file []byte) {
Missiles = make(map[int]*MissileRecord) 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 { for _, line := range data {
if len(line) == 0 { if len(line) == 0 {
continue continue

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

View File

@ -4,10 +4,6 @@ import (
"log" "log"
"strings" "strings"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2common" "github.com/OpenDiablo2/OpenDiablo2/d2common"
) )
@ -18,8 +14,7 @@ type ObjectTypeRecord struct {
var ObjectTypes []ObjectTypeRecord var ObjectTypes []ObjectTypeRecord
func LoadObjectTypes(fileProvider d2interface.FileProvider) { func LoadObjectTypes(objectTypeData []byte) {
objectTypeData := fileProvider.LoadFile(d2resource.ObjectType)
streamReader := d2common.CreateStreamReader(objectTypeData) streamReader := d2common.CreateStreamReader(objectTypeData)
count := streamReader.GetInt32() count := streamReader.GetInt32()
ObjectTypes = make([]ObjectTypeRecord, count) ObjectTypes = make([]ObjectTypeRecord, count)

View File

@ -4,11 +4,7 @@ import (
"log" "log"
"strings" "strings"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" dh "github.com/OpenDiablo2/OpenDiablo2/d2common/d2helper"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
dh "github.com/OpenDiablo2/OpenDiablo2/d2helper"
) )
// An ObjectRecord represents the settings for one type of object from objects.txt // 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 var Objects map[int]*ObjectRecord
func LoadObjects(fileProvider d2interface.FileProvider) { func LoadObjects(file []byte) {
Objects = make(map[int]*ObjectRecord) 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 { for _, line := range data {
if len(line) == 0 { if len(line) == 0 {
continue continue

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

View File

@ -4,11 +4,7 @@ import (
"log" "log"
"strings" "strings"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" dh "github.com/OpenDiablo2/OpenDiablo2/d2common/d2helper"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
dh "github.com/OpenDiablo2/OpenDiablo2/d2helper"
) )
// SoundEntry represents a sound entry // SoundEntry represents a sound entry
@ -80,9 +76,9 @@ func createSoundEntry(soundLine string) SoundEntry {
var Sounds map[string]SoundEntry var Sounds map[string]SoundEntry
func LoadSounds(fileProvider d2interface.FileProvider) { func LoadSounds(file []byte) {
Sounds = make(map[string]SoundEntry) 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 { for _, line := range soundData {
if len(line) == 0 { if len(line) == 0 {
continue continue

View File

@ -6,11 +6,7 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common" "github.com/OpenDiablo2/OpenDiablo2/d2common"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" dh "github.com/OpenDiablo2/OpenDiablo2/d2common/d2helper"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
dh "github.com/OpenDiablo2/OpenDiablo2/d2helper"
) )
type UniqueItemRecord struct { type UniqueItemRecord struct {
@ -120,9 +116,9 @@ func createUniqueItemProperty(r *[]string, inc func() int) UniqueItemProperty {
var UniqueItems map[string]*UniqueItemRecord var UniqueItems map[string]*UniqueItemRecord
func LoadUniqueItems(fileProvider d2interface.FileProvider) { func LoadUniqueItems(file []byte) {
UniqueItems = make(map[string]*UniqueItemRecord) 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 { for _, line := range data {
if len(line) == 0 { if len(line) == 0 {
continue continue

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

View File

@ -2,7 +2,7 @@ package d2data
import ( import (
"github.com/OpenDiablo2/OpenDiablo2/d2common" "github.com/OpenDiablo2/OpenDiablo2/d2common"
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2datadict" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
) )
type Object struct { type Object struct {

View File

@ -3,9 +3,9 @@ package d2cof
import ( import (
"strings" "strings"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common" "github.com/OpenDiablo2/OpenDiablo2/d2common"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
) )
type COF struct { type COF struct {

View File

@ -4,7 +4,7 @@ import (
"encoding/binary" "encoding/binary"
"log" "log"
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2datadict" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
"github.com/go-restruct/restruct" "github.com/go-restruct/restruct"
) )

View File

@ -4,7 +4,7 @@ import (
"log" "log"
"github.com/OpenDiablo2/OpenDiablo2/d2common" "github.com/OpenDiablo2/OpenDiablo2/d2common"
"github.com/OpenDiablo2/OpenDiablo2/d2helper" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2helper"
) )
type DCCDirection struct { type DCCDirection struct {

View File

@ -2,10 +2,10 @@ package d2ds1
import ( import (
"github.com/OpenDiablo2/OpenDiablo2/d2common" "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/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2data" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2helper"
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2datadict"
"github.com/OpenDiablo2/OpenDiablo2/d2helper"
) )
type DS1 struct { type DS1 struct {

View File

@ -10,10 +10,10 @@ import (
"log" "log"
"strings" "strings"
"github.com/OpenDiablo2/OpenDiablo2/d2helper" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2helper"
"github.com/JoshVarga/blast" "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 // Stream represents a stream of data in an MPQ archive

View File

@ -0,0 +1,7 @@
package d2interface
type AudioProvider interface {
PlayBGM(song string)
LoadSoundEffect(sfx string) (SoundEffect, error)
SetVolumes(bgmVolume, sfxVolume float64)
}

View File

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

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

View File

@ -0,0 +1,6 @@
package d2interface
type SoundEffect interface {
Play()
Stop()
}

15
d2common/filter.go Normal file
View 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
View 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
}

View File

@ -3,10 +3,6 @@ package d2common
import ( import (
"log" "log"
"strconv" "strconv"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
) )
type textDictionaryHashEntry struct { type textDictionaryHashEntry struct {
@ -28,16 +24,17 @@ func TranslateString(key string) string {
return result return result
} }
func LoadTextDictionary(fileProvider d2interface.FileProvider) { func GetDictionaryEntryCount() int {
lookupTable = make(map[string]string) if lookupTable == nil {
loadDictionary(fileProvider, d2resource.PatchStringTable) return 0
loadDictionary(fileProvider, d2resource.ExpansionStringTable) }
loadDictionary(fileProvider, d2resource.StringTable) return len(lookupTable)
log.Printf("Loaded %d entries from the string table", len(lookupTable))
} }
func loadDictionary(fileProvider d2interface.FileProvider, dictionaryName string) { func LoadDictionary(dictionaryData []byte) {
dictionaryData := fileProvider.LoadFile(dictionaryName) if lookupTable == nil {
lookupTable = make(map[string]string)
}
br := CreateStreamReader(dictionaryData) br := CreateStreamReader(dictionaryData)
// CRC // CRC
if _, err := br.ReadBytes(2); err != nil { if _, err := br.ReadBytes(2); err != nil {

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

View File

@ -1,15 +1,15 @@
package d2render package d2assetmanager
import ( import (
"math" "math"
"math/rand" "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/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 // AnimatedEntity represents an entity on the map that can be animated
@ -27,12 +27,12 @@ type AnimatedEntity struct {
action int32 action int32
repetitions int repetitions int
composite *d2asset.Composite composite *Composite
} }
// CreateAnimatedEntity creates an instance of AnimatedEntity // CreateAnimatedEntity creates an instance of AnimatedEntity
func CreateAnimatedEntity(x, y int32, object *d2datadict.ObjectLookupRecord, palettePath string) (*AnimatedEntity, error) { 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 { if err != nil {
return nil, err return nil, err
} }
@ -71,7 +71,7 @@ func (v AnimatedEntity) Wait() bool {
} }
// Render draws this animated entity onto the target // Render draws this animated entity onto the target
func (v *AnimatedEntity) Render(target *d2surface.Surface) { func (v *AnimatedEntity) Render(target d2common.Surface) {
target.PushTranslation( target.PushTranslation(
int(v.offsetX)+int((v.subcellX-v.subcellY)*16), int(v.offsetX)+int((v.subcellX-v.subcellY)*16),
int(v.offsetY)+int(((v.subcellX+v.subcellY)*8)-5), int(v.offsetY)+int(((v.subcellX+v.subcellY)*8)-5),

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

View File

@ -1,15 +1,18 @@
package d2asset package d2assetmanager
import ( import (
"errors" "errors"
"fmt" "fmt"
"strings" "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/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2data" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dcc"
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2datadict"
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2dcc"
"github.com/OpenDiablo2/OpenDiablo2/d2render/d2surface"
) )
type Composite struct { type Composite struct {
@ -18,7 +21,7 @@ type Composite struct {
mode *compositeMode mode *compositeMode
} }
func createComposite(object *d2datadict.ObjectLookupRecord, palettePath string) *Composite { func CreateComposite(object *d2datadict.ObjectLookupRecord, palettePath string) *Composite {
return &Composite{object: object, palettePath: palettePath} return &Composite{object: object, palettePath: palettePath}
} }
@ -45,7 +48,7 @@ func (c *Composite) Advance(elapsed float64) error {
return nil return nil
} }
func (c *Composite) Render(target *d2surface.Surface) error { func (c *Composite) Render(target d2common.Surface) error {
if c.mode == nil { if c.mode == nil {
return nil return nil
} }
@ -105,7 +108,7 @@ type compositeMode struct {
directionCount int directionCount int
playedCount int playedCount int
layers []*Animation layers []*d2render.Animation
drawOrder [][]d2enum.CompositeType drawOrder [][]d2enum.CompositeType
frameCount int frameCount int
@ -140,7 +143,7 @@ func (c *Composite) createMode(animationMode, weaponClass string, direction int)
weaponClass: weaponClass, weaponClass: weaponClass,
direction: direction, direction: direction,
directionCount: cof.NumberOfDirections, directionCount: cof.NumberOfDirections,
layers: make([]*Animation, d2enum.CompositeTypeMax), layers: make([]*d2render.Animation, d2enum.CompositeTypeMax),
frameCount: animationData[0].FramesPerDirection, frameCount: animationData[0].FramesPerDirection,
animationSpeed: 1.0 / ((float64(animationData[0].AnimationSpeed) * 25.0) / 256.0), 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 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{ 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, weaponClass),
fmt.Sprintf("%s/%s/%s/%s%s%s%s%s.dcc", object.Base, object.Token, layerKey, object.Token, layerKey, layerValue, animationMode, "HTH"), fmt.Sprintf("%s/%s/%s/%s%s%s%s%s.dcc", object.Base, object.Token, layerKey, object.Token, layerKey, layerValue, animationMode, "HTH"),

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

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

View File

@ -1,22 +1,20 @@
package d2audio package ebiten
import ( import (
"log" "log"
"github.com/OpenDiablo2/OpenDiablo2/d2asset" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2datadict" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2assetmanager"
"github.com/hajimehoshi/ebiten/audio/wav"
"github.com/hajimehoshi/ebiten/audio" "github.com/hajimehoshi/ebiten/audio"
"github.com/hajimehoshi/ebiten/audio/wav"
) )
type SoundEffect struct { type EbitenSoundEffect struct {
player *audio.Player player *audio.Player
} }
func CreateSoundEffect(sfx string, context *audio.Context, volume float64) *SoundEffect { func CreateSoundEffect(sfx string, context *audio.Context, volume float64) *EbitenSoundEffect {
result := &SoundEffect{} result := &EbitenSoundEffect{}
var soundFile string var soundFile string
if _, exists := d2datadict.Sounds[sfx]; exists { if _, exists := d2datadict.Sounds[sfx]; exists {
soundEntry := d2datadict.Sounds[sfx] soundEntry := d2datadict.Sounds[sfx]
@ -25,7 +23,7 @@ func CreateSoundEffect(sfx string, context *audio.Context, volume float64) *Soun
soundFile = sfx soundFile = sfx
} }
audioData, err := d2asset.LoadFile(soundFile) audioData, err := d2assetmanager.LoadFile(soundFile)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -44,11 +42,11 @@ func CreateSoundEffect(sfx string, context *audio.Context, volume float64) *Soun
return result return result
} }
func (v *SoundEffect) Play() { func (v *EbitenSoundEffect) Play() {
v.player.Rewind() v.player.Rewind()
v.player.Play() v.player.Play()
} }
func (v *SoundEffect) Stop() { func (v *EbitenSoundEffect) Stop() {
v.player.Pause() v.player.Pause()
} }

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

View File

@ -1,4 +1,4 @@
package d2core package d2gamestate
import ( import (
"io/ioutil" "io/ioutil"
@ -9,6 +9,8 @@ import (
"strings" "strings"
"time" "time"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2hero"
"github.com/OpenDiablo2/OpenDiablo2/d2common" "github.com/OpenDiablo2/OpenDiablo2/d2common"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
@ -34,7 +36,7 @@ type GameState struct {
HeroLevel int HeroLevel int
Act int Act int
FilePath string FilePath string
Equipment CharacterEquipment Equipment d2hero.CharacterEquipment
} }
const GameStateVersion = uint32(2) // Update this when you make breaking changes const GameStateVersion = uint32(2) // Update this when you make breaking changes

View File

@ -1,4 +1,4 @@
package d2core package d2hero
type CharacterEquipment struct { type CharacterEquipment struct {
Head *InventoryItemArmor // Head Head *InventoryItemArmor // Head

View File

@ -1,15 +1,15 @@
package d2core package d2hero
import ( import (
"github.com/OpenDiablo2/OpenDiablo2/d2common"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2datadict" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2assetmanager"
"github.com/OpenDiablo2/OpenDiablo2/d2render"
"github.com/OpenDiablo2/OpenDiablo2/d2render/d2surface"
) )
type Hero struct { type Hero struct {
AnimatedEntity *d2render.AnimatedEntity AnimatedEntity *d2assetmanager.AnimatedEntity
Equipment CharacterEquipment Equipment CharacterEquipment
mode d2enum.AnimationMode mode d2enum.AnimationMode
direction int direction int
@ -32,7 +32,7 @@ func CreateHero(x, y int32, direction int, heroType d2enum.Hero, equipment Chara
LH: equipment.LeftHand.ItemCode(), 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 { if err != nil {
panic(err) panic(err)
} }
@ -52,7 +52,7 @@ func (v *Hero) Advance(tickTime float64) {
v.AnimatedEntity.Advance(tickTime) v.AnimatedEntity.Advance(tickTime)
} }
func (v *Hero) Render(target *d2surface.Surface) { func (v *Hero) Render(target d2common.Surface) {
v.AnimatedEntity.Render(target) v.AnimatedEntity.Render(target)
} }

View File

@ -1,4 +1,4 @@
package d2core package d2hero
import ( import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"

View File

@ -1,10 +1,10 @@
package d2core package d2hero
import ( import (
"log" "log"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2datadict"
) )
type InventoryItemArmor struct { type InventoryItemArmor struct {

View File

@ -1,10 +1,10 @@
package d2core package d2hero
import ( import (
"log" "log"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2datadict"
) )
type InventoryItemWeapon struct { type InventoryItemWeapon struct {

View File

@ -1,23 +1,21 @@
package d2core package d2npc
import ( import (
"github.com/OpenDiablo2/OpenDiablo2/d2common" "github.com/OpenDiablo2/OpenDiablo2/d2common"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2datadict" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2assetmanager"
"github.com/OpenDiablo2/OpenDiablo2/d2render"
"github.com/OpenDiablo2/OpenDiablo2/d2render/d2surface"
) )
type NPC struct { type NPC struct {
AnimatedEntity *d2render.AnimatedEntity AnimatedEntity *d2assetmanager.AnimatedEntity
HasPaths bool HasPaths bool
Paths []d2common.Path Paths []d2common.Path
path int path int
} }
func CreateNPC(x, y int32, object *d2datadict.ObjectLookupRecord, direction int) *NPC { 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 { if err != nil {
panic(err) panic(err)
} }
@ -45,7 +43,7 @@ func (v *NPC) SetPaths(paths []d2common.Path) {
v.HasPaths = len(paths) > 0 v.HasPaths = len(paths) > 0
} }
func (v *NPC) Render(target *d2surface.Surface) { func (v *NPC) Render(target d2common.Surface) {
v.AnimatedEntity.Render(target) v.AnimatedEntity.Render(target)
} }

View File

@ -1,18 +1,16 @@
package d2asset package d2render
import ( import (
"errors" "errors"
"image/color" "image/color"
"math" "math"
"github.com/OpenDiablo2/OpenDiablo2/d2helper" "github.com/OpenDiablo2/OpenDiablo2/d2common"
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2datadict" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2dc6" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dc6"
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2dcc" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dcc"
"github.com/OpenDiablo2/OpenDiablo2/d2render/d2surface" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2helper"
"github.com/hajimehoshi/ebiten"
) )
type playMode int type playMode int
@ -29,7 +27,7 @@ type animationFrame struct {
offsetX int offsetX int
offsetY int offsetY int
image *ebiten.Image image d2common.Surface
} }
type animationDirection struct { type animationDirection struct {
@ -43,7 +41,7 @@ type Animation struct {
lastFrameTime float64 lastFrameTime float64
playedCount int playedCount int
compositeMode ebiten.CompositeMode compositeMode d2common.CompositeMode
colorMod color.Color colorMod color.Color
playMode playMode playMode playMode
@ -51,7 +49,7 @@ type Animation struct {
playLoop bool 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{ animation := &Animation{
playLength: 1.0, playLength: 1.0,
playLoop: true, 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 { if err != nil {
return nil, err return nil, err
} }
@ -113,14 +111,14 @@ func createAnimationFromDCC(dcc *d2dcc.DCC, palette *d2datadict.PaletteRec, tran
return animation, nil return animation, nil
} }
func createAnimationFromDC6(dc6 *d2dc6.DC6File) (*Animation, error) { func CreateAnimationFromDC6(dc6 *d2dc6.DC6File) (*Animation, error) {
animation := &Animation{ animation := &Animation{
playLength: 1.0, playLength: 1.0,
playLoop: true, playLoop: true,
} }
for frameIndex, dc6Frame := range dc6.Frames { 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 { if err != nil {
return nil, err return nil, err
} }
@ -147,7 +145,7 @@ func createAnimationFromDC6(dc6 *d2dc6.DC6File) (*Animation, error) {
return animation, nil return animation, nil
} }
func (a *Animation) clone() *Animation { func (a *Animation) Clone() *Animation {
animation := *a animation := *a
return &animation return &animation
} }
@ -193,7 +191,7 @@ func (a *Animation) Advance(elapsed float64) error {
return nil return nil
} }
func (a *Animation) Render(target *d2surface.Surface) error { func (a *Animation) Render(target d2common.Surface) error {
direction := a.directions[a.directionIndex] direction := a.directions[a.directionIndex]
frame := direction.frames[a.frameIndex] frame := direction.frames[a.frameIndex]
@ -326,8 +324,8 @@ func (a *Animation) ResetPlayedCount() {
func (a *Animation) SetBlend(blend bool) { func (a *Animation) SetBlend(blend bool) {
if blend { if blend {
a.compositeMode = ebiten.CompositeModeLighter a.compositeMode = d2common.CompositeModeLighter
} else { } else {
a.compositeMode = ebiten.CompositeModeSourceOver a.compositeMode = d2common.CompositeModeSourceOver
} }
} }

View File

@ -3,21 +3,23 @@ package d2mapengine
import ( import (
"strings" "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/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2audio"
"github.com/OpenDiablo2/OpenDiablo2/d2core"
"github.com/OpenDiablo2/OpenDiablo2/d2render/d2surface"
) )
type MapEntity interface { type MapEntity interface {
Render(target *d2surface.Surface) Render(target d2common.Surface)
Advance(tickTime float64) Advance(tickTime float64)
GetPosition() (float64, float64) GetPosition() (float64, float64)
} }
type MapEngine struct { type MapEngine struct {
soundManager *d2audio.Manager gameState *d2gamestate.GameState
gameState *d2core.GameState
debugVisLevel int debugVisLevel int
@ -27,11 +29,10 @@ type MapEngine struct {
camera Camera camera Camera
} }
func CreateMapEngine(gameState *d2core.GameState, soundManager *d2audio.Manager) *MapEngine { func CreateMapEngine(gameState *d2gamestate.GameState) *MapEngine {
engine := &MapEngine{ engine := &MapEngine{
gameState: gameState, gameState: gameState,
soundManager: soundManager, viewport: NewViewport(0, 0, 800, 600),
viewport: NewViewport(0, 0, 800, 600),
} }
engine.viewport.SetCamera(&engine.camera) engine.viewport.SetCamera(&engine.camera)
@ -90,7 +91,7 @@ func (me *MapEngine) GenerateMap(regionType d2enum.RegionIdType, levelPreset int
} }
func (me *MapEngine) GenerateAct1Overworld() { 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) region, entities := loadRegion(me.gameState.Seed, 0, 0, d2enum.RegionAct1Town, 1, -1)
me.regions = append(me.regions, region) 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 { for _, region := range me.regions {
if region.isVisbile(me.viewport) { if region.isVisbile(me.viewport) {
region.renderPass1(me.viewport, target) region.renderPass1(me.viewport, target)

View File

@ -7,21 +7,20 @@ import (
"math/rand" "math/rand"
"strconv" "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/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/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/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2core" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
"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"
) )
type MapRegion struct { type MapRegion struct {
@ -34,7 +33,7 @@ type MapRegion struct {
palette d2datadict.PaletteRec palette d2datadict.PaletteRec
startX float64 startX float64
startY float64 startY float64
imageCacheRecords map[uint32]*ebiten.Image imageCacheRecords map[uint32]d2common.Surface
seed int64 seed int64
currentFrame int currentFrame int
lastFrameTime float64 lastFrameTime float64
@ -44,7 +43,7 @@ func loadRegion(seed int64, tileOffsetX, tileOffsetY int, levelType d2enum.Regio
region := &MapRegion{ region := &MapRegion{
levelType: d2datadict.LevelTypes[levelType], levelType: d2datadict.LevelTypes[levelType],
levelPreset: d2datadict.LevelPresets[levelPreset], levelPreset: d2datadict.LevelPresets[levelPreset],
imageCacheRecords: map[uint32]*ebiten.Image{}, imageCacheRecords: map[uint32]d2common.Surface{},
seed: seed, seed: seed,
} }
@ -55,7 +54,7 @@ func loadRegion(seed int64, tileOffsetX, tileOffsetY int, levelType d2enum.Regio
for _, levelTypeDt1 := range region.levelType.Files { for _, levelTypeDt1 := range region.levelType.Files {
if len(levelTypeDt1) != 0 && levelTypeDt1 != "" && levelTypeDt1 != "0" { 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 { if err != nil {
panic(err) panic(err)
} }
@ -78,7 +77,7 @@ func loadRegion(seed int64, tileOffsetX, tileOffsetY int, levelType d2enum.Regio
} }
region.regionPath = levelFilesToPick[levelIndex] 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 { if err != nil {
panic(err) panic(err)
} }
@ -137,13 +136,13 @@ func (mr *MapRegion) loadEntities() []MapEntity {
switch object.Lookup.Type { switch object.Lookup.Type {
case d2datadict.ObjectTypeCharacter: case d2datadict.ObjectTypeCharacter:
if object.Lookup.Base != "" && object.Lookup.Token != "" && object.Lookup.TR != "" { 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) npc.SetPaths(object.Paths)
entities = append(entities, npc) entities = append(entities, npc)
} }
case d2datadict.ObjectTypeItem: case d2datadict.ObjectTypeItem:
if object.ObjectInfo != nil && object.ObjectInfo.Draw && object.Lookup.Base != "" && object.Lookup.Token != "" { 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 { if err != nil {
panic(err) 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) 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 tileY := range mr.ds1.Tiles {
for tileX, tile := range mr.ds1.Tiles[tileY] { for tileX, tile := range mr.ds1.Tiles[tileY] {
worldX, worldY := mr.getTileWorldPosition(tileX, 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 tileY := range mr.ds1.Tiles {
for tileX, tile := range mr.ds1.Tiles[tileY] { for tileX, tile := range mr.ds1.Tiles[tileY] {
worldX, worldY := mr.getTileWorldPosition(tileX, 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 tileY := range mr.ds1.Tiles {
for tileX, tile := range mr.ds1.Tiles[tileY] { for tileX, tile := range mr.ds1.Tiles[tileY] {
worldX, worldY := mr.getTileWorldPosition(tileX, 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 { for _, wall := range tile.Walls {
if !wall.Hidden && wall.Prop1 != 0 && wall.Type.LowerWall() { if !wall.Hidden && wall.Prop1 != 0 && wall.Type.LowerWall() {
mr.renderWall(wall, viewport, target) 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 { for _, wall := range tile.Walls {
if !wall.Hidden && wall.Type.UpperWall() { if !wall.Hidden && wall.Type.UpperWall() {
mr.renderWall(wall, viewport, target) 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 { for _, wall := range tile.Walls {
if wall.Type == d2enum.Roof { if wall.Type == d2enum.Roof {
mr.renderWall(wall, viewport, target) 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) { func (mr *MapRegion) renderFloor(tile d2ds1.FloorShadowRecord, viewport *Viewport, target d2common.Surface) {
var img *ebiten.Image var img d2common.Surface
if !tile.Animated { if !tile.Animated {
img = mr.getImageCacheRecord(tile.Style, tile.Sequence, 0, tile.RandomIndex) img = mr.getImageCacheRecord(tile.Style, tile.Sequence, 0, tile.RandomIndex)
} else { } else {
@ -337,7 +336,7 @@ func (mr *MapRegion) renderFloor(tile d2ds1.FloorShadowRecord, viewport *Viewpor
target.Render(img) 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) img := mr.getImageCacheRecord(tile.Style, tile.Sequence, tile.Type, tile.RandomIndex)
if img == nil { if img == nil {
log.Printf("Render called on uncached wall {%v,%v,%v}", tile.Style, tile.Sequence, tile.Type) 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) 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) img := mr.getImageCacheRecord(tile.Style, tile.Sequence, 13, tile.RandomIndex)
if img == nil { if img == nil {
log.Printf("Render called on uncached shadow {%v,%v}", tile.Style, tile.Sequence) 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) 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 tileY := range mr.ds1.Tiles {
for tileX := range mr.ds1.Tiles[tileY] { for tileX := range mr.ds1.Tiles[tileY] {
worldX, worldY := mr.getTileWorldPosition(tileX, 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 { if debugVisLevel > 0 {
subtileColor := color.RGBA{80, 80, 255, 100} subtileColor := color.RGBA{80, 80, 255, 100}
tileColor := color.RGBA{255, 255, 255, 255} 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) lookupIndex := uint32(style)<<24 | uint32(sequence)<<16 | uint32(tileType)<<8 | uint32(randomIndex)
return mr.imageCacheRecords[lookupIndex] 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) lookupIndex := uint32(style)<<24 | uint32(sequence)<<16 | uint32(tileType)<<8 | uint32(randomIndex)
mr.imageCacheRecords[lookupIndex] = image mr.imageCacheRecords[lookupIndex] = image
} }
@ -497,7 +496,7 @@ func (mr *MapRegion) generateFloorCache(tile *d2ds1.FloorShadowRecord, tileX, ti
} }
tileYOffset := d2helper.AbsInt32(tileYMinimum) tileYOffset := d2helper.AbsInt32(tileYMinimum)
tileHeight := d2helper.AbsInt32(tileData[i].Height) 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) pixels := make([]byte, 4*tileData[i].Width*tileHeight)
mr.decodeTileGfxData(tileData[i].Blocks, &pixels, tileYOffset, tileData[i].Width) mr.decodeTileGfxData(tileData[i].Blocks, &pixels, tileYOffset, tileData[i].Width)
image.ReplacePixels(pixels) image.ReplacePixels(pixels)
@ -532,7 +531,7 @@ func (mr *MapRegion) generateShadowCache(tile *d2ds1.FloorShadowRecord, tileX, t
return 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)) pixels := make([]byte, 4*tileData.Width*int32(tileHeight))
mr.decodeTileGfxData(tileData.Blocks, &pixels, tileYOffset, tileData.Width) mr.decodeTileGfxData(tileData.Blocks, &pixels, tileYOffset, tileData.Width)
image.ReplacePixels(pixels) image.ReplacePixels(pixels)
@ -593,7 +592,7 @@ func (mr *MapRegion) generateWallCache(tile *d2ds1.WallRecord, tileX, tileY int)
return return
} }
image, _ := ebiten.NewImage(160, int(realHeight), ebiten.FilterNearest) _, image := d2render.NewSurface(160, int(realHeight), d2common.FilterNearest)
pixels := make([]byte, 4*160*realHeight) pixels := make([]byte, 4*160*realHeight)
mr.decodeTileGfxData(tileData.Blocks, &pixels, tileYOffset, 160) mr.decodeTileGfxData(tileData.Blocks, &pixels, tileYOffset, 160)

108
d2core/d2render/d2render.go Normal file
View 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
}

View File

@ -1,4 +1,4 @@
package d2corehelper package ebiten
import ( import (
"image/color" "image/color"

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

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

View File

@ -1,60 +1,43 @@
package d2surface package ebiten
import ( import (
"fmt" "fmt"
"image/color" "image/color"
"github.com/OpenDiablo2/OpenDiablo2/d2corehelper" "github.com/OpenDiablo2/OpenDiablo2/d2common"
"github.com/hajimehoshi/ebiten" "github.com/hajimehoshi/ebiten"
"github.com/hajimehoshi/ebiten/ebitenutil" "github.com/hajimehoshi/ebiten/ebitenutil"
) )
type surfaceState struct { type ebitenSurface struct {
x int
y int
mode ebiten.CompositeMode
filter ebiten.Filter
color color.Color
}
type Surface struct {
stateStack []surfaceState stateStack []surfaceState
stateCurrent surfaceState stateCurrent surfaceState
image *ebiten.Image image *ebiten.Image
} }
func CreateSurface(image *ebiten.Image) *Surface { func (s *ebitenSurface) PushTranslation(x, y int) {
return &Surface{
image: image,
stateCurrent: surfaceState{
filter: ebiten.FilterNearest,
mode: ebiten.CompositeModeSourceOver,
},
}
}
func (s *Surface) PushTranslation(x, y int) {
s.stateStack = append(s.stateStack, s.stateCurrent) s.stateStack = append(s.stateStack, s.stateCurrent)
s.stateCurrent.x += x s.stateCurrent.x += x
s.stateCurrent.y += y 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.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.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.stateStack = append(s.stateStack, s.stateCurrent)
s.stateCurrent.color = color s.stateCurrent.color = color
} }
func (s *Surface) Pop() { func (s *ebitenSurface) Pop() {
count := len(s.stateStack) count := len(s.stateStack)
if count == 0 { if count == 0 {
panic("empty stack") panic("empty stack")
@ -64,28 +47,29 @@ func (s *Surface) Pop() {
s.stateStack = s.stateStack[:count-1] s.stateStack = s.stateStack[:count-1]
} }
func (s *Surface) PopN(n int) { func (s *ebitenSurface) PopN(n int) {
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
s.Pop() 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 := &ebiten.DrawImageOptions{CompositeMode: s.stateCurrent.mode}
opts.GeoM.Translate(float64(s.stateCurrent.x), float64(s.stateCurrent.y)) opts.GeoM.Translate(float64(s.stateCurrent.x), float64(s.stateCurrent.y))
opts.Filter = s.stateCurrent.filter opts.Filter = s.stateCurrent.filter
if s.stateCurrent.color != nil { 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) 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( ebitenutil.DrawLine(
s.image, s.image,
float64(s.stateCurrent.x), 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( ebitenutil.DrawRect(
s.image, s.image,
float64(s.stateCurrent.x), 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) return s.image.Fill(color)
} }
func (s *Surface) GetSize() (int, int) { func (s *ebitenSurface) GetSize() (int, int) {
return s.image.Size() return s.image.Size()
} }
func (s *Surface) GetDepth() int { func (s *ebitenSurface) GetDepth() int {
return len(s.stateStack) return len(s.stateStack)
} }
func (s *ebitenSurface) ReplacePixels(pixels []byte) error {
return s.image.ReplacePixels(pixels)
}

View 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