1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-06-04 15:00:42 +00:00

More optimizations and cleanup. (#131)

This commit is contained in:
Tim Sarbin 2019-11-10 10:44:13 -05:00 committed by GitHub
parent f2b1bdfba4
commit e8292e9c42
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 515 additions and 451 deletions

View File

@ -20,8 +20,8 @@ func LoadDataDictionary(text string) *DataDictionary {
for i, fieldName := range fileNames {
result.FieldNameLookup[fieldName] = i
}
result.Data = make([][]string, 0)
for _, line := range lines[1:] {
result.Data = make([][]string, len(lines)-1)
for i, line := range lines[1:] {
if len(strings.TrimSpace(line)) == 0 {
continue
}
@ -29,7 +29,7 @@ func LoadDataDictionary(text string) *DataDictionary {
if len(values) != len(result.FieldNameLookup) {
continue
}
result.Data = append(result.Data, values)
result.Data[i] = values
}
return result
}

View File

@ -0,0 +1,18 @@
package d2enum
type LayerStreamType int
const (
LayerStreamWall1 LayerStreamType = 0
LayerStreamWall2 LayerStreamType = 1
LayerStreamWall3 LayerStreamType = 2
LayerStreamWall4 LayerStreamType = 3
LayerStreamOrientation1 LayerStreamType = 4
LayerStreamOrientation2 LayerStreamType = 5
LayerStreamOrientation3 LayerStreamType = 6
LayerStreamOrientation4 LayerStreamType = 7
LayerStreamFloor1 LayerStreamType = 8
LayerStreamFloor2 LayerStreamType = 9
LayerStreamShadow LayerStreamType = 10
LayerStreamSubstitute LayerStreamType = 11
)

View File

@ -41,7 +41,7 @@ type Engine struct {
Settings *d2common.Configuration // Engine configuration settings from json file
Files map[string]string // Map that defines which files are in which MPQs
CheckedPatch map[string]bool // First time we check a file, we'll check if it's in the patch. This notes that we've already checked that.
LoadingSprite *d2render.Sprite // The sprite shown when loading stuff
LoadingSprite d2render.Sprite // The sprite shown when loading stuff
loadingProgress float64 // LoadingProcess is a range between 0.0 and 1.0. If set, loading screen displays.
loadingIndex int // Determines which load function is currently being called
thingsToLoad []func() // The load functions for the next scene
@ -56,31 +56,31 @@ type Engine struct {
}
// CreateEngine creates and instance of the OpenDiablo2 engine
func CreateEngine() *Engine {
result := &Engine{
func CreateEngine() Engine {
result := Engine{
CurrentScene: nil,
nextScene: nil,
}
result.loadConfigurationFile()
d2resource.LanguageCode = result.Settings.Language
result.mapMpqFiles()
d2datadict.LoadPalettes(result.Files, result)
d2common.LoadTextDictionary(result)
d2datadict.LoadLevelTypes(result)
d2datadict.LoadLevelPresets(result)
d2datadict.LoadLevelWarps(result)
d2datadict.LoadObjectTypes(result)
d2datadict.LoadObjects(result)
d2datadict.LoadWeapons(result)
d2datadict.LoadArmors(result)
d2datadict.LoadUniqueItems(result)
d2datadict.LoadMissiles(result)
d2datadict.LoadSounds(result)
d2data.LoadAnimationData(result)
d2datadict.LoadMonStats(result)
result.SoundManager = d2audio.CreateManager(result)
d2datadict.LoadPalettes(result.Files, &result)
d2common.LoadTextDictionary(&result)
d2datadict.LoadLevelTypes(&result)
d2datadict.LoadLevelPresets(&result)
d2datadict.LoadLevelWarps(&result)
d2datadict.LoadObjectTypes(&result)
d2datadict.LoadObjects(&result)
d2datadict.LoadWeapons(&result)
d2datadict.LoadArmors(&result)
d2datadict.LoadUniqueItems(&result)
d2datadict.LoadMissiles(&result)
d2datadict.LoadSounds(&result)
d2data.LoadAnimationData(&result)
d2datadict.LoadMonStats(&result)
result.SoundManager = d2audio.CreateManager(&result)
result.SoundManager.SetVolumes(result.Settings.BgmVolume, result.Settings.SfxVolume)
result.UIManager = d2ui.CreateManager(result, *result.SoundManager)
result.UIManager = d2ui.CreateManager(&result, *result.SoundManager)
result.LoadingSprite = result.LoadSprite(d2resource.LoadingScreen, d2enum.Loading)
loadingSpriteSizeX, loadingSpriteSizeY := result.LoadingSprite.GetSize()
result.LoadingSprite.MoveTo(int(400-(loadingSpriteSizeX/2)), int(300+(loadingSpriteSizeY/2)))
@ -135,12 +135,12 @@ func (v *Engine) LoadFile(fileName string) []byte {
}
// IsLoading returns true if the engine is currently in a loading state
func (v *Engine) IsLoading() bool {
func (v Engine) IsLoading() bool {
return v.loadingProgress < 1.0
}
// LoadSprite loads a sprite from the game's data files
func (v *Engine) LoadSprite(fileName string, palette d2enum.PaletteType) *d2render.Sprite {
func (v Engine) LoadSprite(fileName string, palette d2enum.PaletteType) d2render.Sprite {
data := v.LoadFile(fileName)
sprite := d2render.CreateSprite(data, d2datadict.Palettes[palette])
return sprite
@ -215,7 +215,7 @@ func (v *Engine) Update() {
}
// Draw draws the game
func (v *Engine) Draw(screen *ebiten.Image) {
func (v Engine) Draw(screen *ebiten.Image) {
if v.loadingProgress < 1.0 {
v.LoadingSprite.Frame = uint8(d2helper.Max(0, d2helper.Min(uint32(len(v.LoadingSprite.Frames)-1), uint32(float64(len(v.LoadingSprite.Frames)-1)*v.loadingProgress))))
v.LoadingSprite.Draw(screen)

View File

@ -10,7 +10,7 @@ import (
)
type NPC struct {
AnimatedEntity *d2render.AnimatedEntity
AnimatedEntity d2render.AnimatedEntity
Paths []d2common.Path
}

View File

@ -18,7 +18,7 @@ type CharacterSelect struct {
soundManager *d2audio.Manager
fileProvider d2interface.FileProvider
sceneProvider d2interface.SceneProvider
background *d2render.Sprite
background d2render.Sprite
newCharButton *d2ui.Button
convertCharButton *d2ui.Button
deleteCharButton *d2ui.Button

View File

@ -33,7 +33,7 @@ type Credits struct {
soundManager *d2audio.Manager
fileProvider d2interface.FileProvider
sceneProvider d2interface.SceneProvider
creditsBackground *d2render.Sprite
creditsBackground d2render.Sprite
exitButton *d2ui.Button
creditsText []string
labels []*labelItem

View File

@ -31,12 +31,12 @@ type MainMenu struct {
soundManager *d2audio.Manager
fileProvider d2interface.FileProvider
sceneProvider d2interface.SceneProvider
trademarkBackground *d2render.Sprite
background *d2render.Sprite
diabloLogoLeft *d2render.Sprite
diabloLogoRight *d2render.Sprite
diabloLogoLeftBack *d2render.Sprite
diabloLogoRightBack *d2render.Sprite
trademarkBackground d2render.Sprite
background d2render.Sprite
diabloLogoLeft d2render.Sprite
diabloLogoRight d2render.Sprite
diabloLogoLeftBack d2render.Sprite
diabloLogoRightBack d2render.Sprite
singlePlayerButton *d2ui.Button
githubButton *d2ui.Button
exitDiabloButton *d2ui.Button

View File

@ -21,14 +21,14 @@ import (
type HeroRenderInfo struct {
Stance d2enum.HeroStance
IdleSprite *d2render.Sprite
IdleSelectedSprite *d2render.Sprite
ForwardWalkSprite *d2render.Sprite
ForwardWalkSpriteOverlay *d2render.Sprite
SelectedSprite *d2render.Sprite
SelectedSpriteOverlay *d2render.Sprite
BackWalkSprite *d2render.Sprite
BackWalkSpriteOverlay *d2render.Sprite
IdleSprite d2render.Sprite
IdleSelectedSprite d2render.Sprite
ForwardWalkSprite d2render.Sprite
ForwardWalkSpriteOverlay d2render.Sprite
SelectedSprite d2render.Sprite
SelectedSpriteOverlay d2render.Sprite
BackWalkSprite d2render.Sprite
BackWalkSpriteOverlay d2render.Sprite
SelectionBounds image.Rectangle
SelectSfx *d2audio.SoundEffect
DeselectSfx *d2audio.SoundEffect
@ -39,8 +39,8 @@ type SelectHeroClass struct {
soundManager *d2audio.Manager
fileProvider d2interface.FileProvider
sceneProvider d2interface.SceneProvider
bgImage *d2render.Sprite
campfire *d2render.Sprite
bgImage d2render.Sprite
campfire d2render.Sprite
headingLabel *d2ui.Label
heroClassLabel *d2ui.Label
heroDesc1Label *d2ui.Label
@ -67,7 +67,7 @@ func CreateSelectHeroClass(
return result
}
func (v *SelectHeroClass) loadSprite(path string, palette d2enum.PaletteType) *d2render.Sprite {
func (v *SelectHeroClass) loadSprite(path string, palette d2enum.PaletteType) d2render.Sprite {
return d2render.CreateSprite(v.fileProvider.LoadFile(path), d2datadict.Palettes[palette])
}
@ -125,9 +125,9 @@ func (v *SelectHeroClass) Load() []func() {
v.loadSprite(d2resource.CharacterSelectBarbarianForwardWalk, d2enum.Fechar),
v.loadSprite(d2resource.CharacterSelectBarbarianForwardWalkOverlay, d2enum.Fechar),
v.loadSprite(d2resource.CharacterSelectBarbarianSelected, d2enum.Fechar),
nil,
d2render.Sprite{},
v.loadSprite(d2resource.CharacterSelectBarbarianBackWalk, d2enum.Fechar),
nil,
d2render.Sprite{},
image.Rectangle{Min: image.Point{364, 201}, Max: image.Point{90, 170}},
v.soundManager.LoadSoundEffect(d2resource.SFXBarbarianSelect),
v.soundManager.LoadSoundEffect(d2resource.SFXBarbarianDeselect),
@ -245,9 +245,9 @@ func (v *SelectHeroClass) Load() []func() {
v.loadSprite(d2resource.CharacterSelecPaladinForwardWalk, d2enum.Fechar),
v.loadSprite(d2resource.CharacterSelecPaladinForwardWalkOverlay, d2enum.Fechar),
v.loadSprite(d2resource.CharacterSelecPaladinSelected, d2enum.Fechar),
nil,
d2render.Sprite{},
v.loadSprite(d2resource.CharacterSelecPaladinBackWalk, d2enum.Fechar),
nil,
d2render.Sprite{},
image.Rectangle{Min: image.Point{490, 210}, Max: image.Point{65, 180}},
v.soundManager.LoadSoundEffect(d2resource.SFXPaladinSelect),
v.soundManager.LoadSoundEffect(d2resource.SFXPaladinDeselect),
@ -277,11 +277,11 @@ func (v *SelectHeroClass) Load() []func() {
v.loadSprite(d2resource.CharacterSelectAmazonUnselected, d2enum.Fechar),
v.loadSprite(d2resource.CharacterSelectAmazonUnselectedH, d2enum.Fechar),
v.loadSprite(d2resource.CharacterSelecAmazonForwardWalk, d2enum.Fechar),
nil,
d2render.Sprite{},
v.loadSprite(d2resource.CharacterSelecAmazonSelected, d2enum.Fechar),
nil,
d2render.Sprite{},
v.loadSprite(d2resource.CharacterSelecAmazonBackWalk, d2enum.Fechar),
nil,
d2render.Sprite{},
image.Rectangle{Min: image.Point{70, 220}, Max: image.Point{55, 200}},
v.soundManager.LoadSoundEffect(d2resource.SFXAmazonSelect),
v.soundManager.LoadSoundEffect(d2resource.SFXAmazonDeselect),
@ -307,11 +307,11 @@ func (v *SelectHeroClass) Load() []func() {
v.loadSprite(d2resource.CharacterSelectAssassinUnselected, d2enum.Fechar),
v.loadSprite(d2resource.CharacterSelectAssassinUnselectedH, d2enum.Fechar),
v.loadSprite(d2resource.CharacterSelectAssassinForwardWalk, d2enum.Fechar),
nil,
d2render.Sprite{},
v.loadSprite(d2resource.CharacterSelectAssassinSelected, d2enum.Fechar),
nil,
d2render.Sprite{},
v.loadSprite(d2resource.CharacterSelectAssassinBackWalk, d2enum.Fechar),
nil,
d2render.Sprite{},
image.Rectangle{Min: image.Point{175, 235}, Max: image.Point{50, 180}},
v.soundManager.LoadSoundEffect(d2resource.SFXAssassinSelect),
v.soundManager.LoadSoundEffect(d2resource.SFXAssassinDeselect),
@ -337,11 +337,11 @@ func (v *SelectHeroClass) Load() []func() {
v.loadSprite(d2resource.CharacterSelectDruidUnselected, d2enum.Fechar),
v.loadSprite(d2resource.CharacterSelectDruidUnselectedH, d2enum.Fechar),
v.loadSprite(d2resource.CharacterSelectDruidForwardWalk, d2enum.Fechar),
nil,
d2render.Sprite{},
v.loadSprite(d2resource.CharacterSelectDruidSelected, d2enum.Fechar),
nil,
d2render.Sprite{},
v.loadSprite(d2resource.CharacterSelectDruidBackWalk, d2enum.Fechar),
nil,
d2render.Sprite{},
image.Rectangle{Min: image.Point{680, 220}, Max: image.Point{70, 195}},
v.soundManager.LoadSoundEffect(d2resource.SFXDruidSelect),
v.soundManager.LoadSoundEffect(d2resource.SFXDruidDeselect),
@ -421,7 +421,7 @@ func (v *SelectHeroClass) updateHeroSelectionHover(hero d2enum.Hero, canSelect b
if renderInfo.ForwardWalkSprite.OnLastFrame() {
renderInfo.Stance = d2enum.HeroStanceSelected
renderInfo.SelectedSprite.ResetAnimation()
if renderInfo.SelectedSpriteOverlay != nil {
if renderInfo.SelectedSpriteOverlay.IsValid() {
renderInfo.SelectedSpriteOverlay.ResetAnimation()
}
}
@ -447,7 +447,7 @@ func (v *SelectHeroClass) updateHeroSelectionHover(hero d2enum.Hero, canSelect b
// showEntryUi = true;
renderInfo.Stance = d2enum.HeroStanceApproaching
renderInfo.ForwardWalkSprite.ResetAnimation()
if renderInfo.ForwardWalkSpriteOverlay != nil {
if renderInfo.ForwardWalkSpriteOverlay.IsValid() {
renderInfo.ForwardWalkSpriteOverlay.ResetAnimation()
}
for _, heroInfo := range v.heroRenderInfo {
@ -458,7 +458,7 @@ func (v *SelectHeroClass) updateHeroSelectionHover(hero d2enum.Hero, canSelect b
heroInfo.DeselectSfx.Play()
heroInfo.Stance = d2enum.HeroStanceRetreating
heroInfo.BackWalkSprite.ResetAnimation()
if heroInfo.BackWalkSpriteOverlay != nil {
if heroInfo.BackWalkSpriteOverlay.IsValid() {
heroInfo.BackWalkSpriteOverlay.ResetAnimation()
}
}
@ -491,17 +491,17 @@ func (v *SelectHeroClass) renderHero(screen *ebiten.Image, hero d2enum.Hero) {
renderInfo.IdleSelectedSprite.Draw(screen)
case d2enum.HeroStanceApproaching:
renderInfo.ForwardWalkSprite.Draw(screen)
if renderInfo.ForwardWalkSpriteOverlay != nil {
if renderInfo.ForwardWalkSpriteOverlay.IsValid() {
renderInfo.ForwardWalkSpriteOverlay.Draw(screen)
}
case d2enum.HeroStanceSelected:
renderInfo.SelectedSprite.Draw(screen)
if renderInfo.SelectedSpriteOverlay != nil {
if renderInfo.SelectedSpriteOverlay.IsValid() {
renderInfo.SelectedSpriteOverlay.Draw(screen)
}
case d2enum.HeroStanceRetreating:
renderInfo.BackWalkSprite.Draw(screen)
if renderInfo.BackWalkSpriteOverlay != nil {
if renderInfo.BackWalkSpriteOverlay.IsValid() {
renderInfo.BackWalkSpriteOverlay.Draw(screen)
}
}

View File

@ -1,4 +1,4 @@
package d2data
package d2cof
import (
"strings"
@ -10,36 +10,28 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common"
)
type CofLayer struct {
Type d2enum.CompositeType
Shadow byte
Transparent bool
DrawEffect d2enum.DrawEffect
WeaponClass d2enum.WeaponClass
}
type Cof struct {
type COF struct {
NumberOfDirections int
FramesPerDirection int
NumberOfLayers int
CofLayers []*CofLayer
CofLayers []CofLayer
CompositeLayers map[d2enum.CompositeType]int
AnimationFrames []d2enum.AnimationFrame
Priority [][][]d2enum.CompositeType
}
func LoadCof(fileName string, fileProvider d2interface.FileProvider) *Cof {
result := &Cof{}
func LoadCOF(fileName string, fileProvider d2interface.FileProvider) *COF {
result := &COF{}
fileData := fileProvider.LoadFile(fileName)
streamReader := d2common.CreateStreamReader(fileData)
result.NumberOfLayers = int(streamReader.GetByte())
result.FramesPerDirection = int(streamReader.GetByte())
result.NumberOfDirections = int(streamReader.GetByte())
streamReader.SkipBytes(25) // Skip 25 unknown bytes...
result.CofLayers = make([]*CofLayer, 0)
result.CofLayers = make([]CofLayer, result.NumberOfLayers)
result.CompositeLayers = make(map[d2enum.CompositeType]int, 0)
for i := 0; i < result.NumberOfLayers; i++ {
layer := &CofLayer{}
layer := CofLayer{}
layer.Type = d2enum.CompositeType(streamReader.GetByte())
layer.Shadow = streamReader.GetByte()
streamReader.SkipBytes(1) // Unknown
@ -47,7 +39,7 @@ func LoadCof(fileName string, fileProvider d2interface.FileProvider) *Cof {
layer.DrawEffect = d2enum.DrawEffect(streamReader.GetByte())
weaponClassStr, _ := streamReader.ReadBytes(4)
layer.WeaponClass = d2enum.WeaponClassFromString(strings.TrimSpace(strings.ReplaceAll(string(weaponClassStr), string(0), "")))
result.CofLayers = append(result.CofLayers, layer)
result.CofLayers[i] = layer
result.CompositeLayers[layer.Type] = i
}
animationFrameBytes, _ := streamReader.ReadBytes(result.FramesPerDirection)

11
d2data/d2cof/CofLayer.go Normal file
View File

@ -0,0 +1,11 @@
package d2cof
import "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
type CofLayer struct {
Type d2enum.CompositeType
Shadow byte
Transparent bool
DrawEffect d2enum.DrawEffect
WeaponClass d2enum.WeaponClass
}

View File

@ -73,10 +73,10 @@ func createLevelPresetRecord(props []string) LevelPresetRecord {
return result
}
var LevelPresets map[int]*LevelPresetRecord
var LevelPresets map[int]LevelPresetRecord
func LoadLevelPresets(fileProvider d2interface.FileProvider) {
LevelPresets = make(map[int]*LevelPresetRecord)
LevelPresets = make(map[int]LevelPresetRecord)
data := strings.Split(string(fileProvider.LoadFile(d2resource.LevelPreset)), "\r\n")[1:]
for _, line := range data {
if len(line) == 0 {
@ -87,7 +87,7 @@ func LoadLevelPresets(fileProvider d2interface.FileProvider) {
continue // any line without a definition id is skipped (e.g. the "Expansion" line)
}
rec := createLevelPresetRecord(props)
LevelPresets[rec.DefinitionId] = &rec
LevelPresets[rec.DefinitionId] = rec
}
log.Printf("Loaded %d level presets", len(LevelPresets))
}

View File

@ -0,0 +1,11 @@
package d2dcc
var crazyBitTable = []byte{0, 1, 2, 4, 6, 8, 10, 12, 14, 16, 20, 24, 26, 28, 30, 32}
var pixelMaskLookup = []int{0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4}
var dccDir4 = []byte{0, 1, 2, 3}
var dccDir8 = []byte{4, 0, 5, 1, 6, 2, 7, 3}
var dccDir16 = []byte{4, 8, 0, 9, 5, 10, 1, 11, 6, 12, 2, 13, 7, 14, 3, 15}
var dccDir32 = []byte{
4, 16, 8, 17, 0, 18, 9, 19, 5, 20, 10, 21, 1, 22, 11, 23,
6, 24, 12, 25, 2, 26, 13, 27, 7, 28, 14, 29, 3, 30, 15, 31,
}

62
d2data/d2dcc/DCC.go Normal file
View File

@ -0,0 +1,62 @@
package d2dcc
import (
"log"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2common"
)
type DCC struct {
Signature int
Version int
NumberOfDirections int
FramesPerDirection int
Directions []DCCDirection
valid bool
}
func (v DCC) IsValid() bool {
return v.valid
}
func LoadDCC(path string, fileProvider d2interface.FileProvider) DCC {
result := DCC{}
fileData := fileProvider.LoadFile(path)
var bm = d2common.CreateBitMuncher(fileData, 0)
result.Signature = int(bm.GetByte())
if result.Signature != 0x74 {
log.Fatal("Signature expected to be 0x74 but it is not.")
}
result.Version = int(bm.GetByte())
result.NumberOfDirections = int(bm.GetByte())
result.FramesPerDirection = int(bm.GetInt32())
if bm.GetInt32() != 1 {
log.Fatal("This value isn't 1. It has to be 1.")
}
bm.GetInt32() // TotalSizeCoded
directionOffsets := make([]int, result.NumberOfDirections)
for i := 0; i < result.NumberOfDirections; i++ {
directionOffsets[i] = int(bm.GetInt32())
}
result.Directions = make([]DCCDirection, result.NumberOfDirections)
for i := 0; i < result.NumberOfDirections; i++ {
dir := byte(0)
switch result.NumberOfDirections {
case 1:
dir = 0
case 4:
dir = dccDir4[i]
case 8:
dir = dccDir8[i]
case 16:
dir = dccDir16[i]
case 32:
dir = dccDir32[i]
}
result.Directions[dir] = CreateDCCDirection(d2common.CreateBitMuncher(fileData, directionOffsets[i]*8), result)
}
result.valid = true
return result
}

12
d2data/d2dcc/DCCCell.go Normal file
View File

@ -0,0 +1,12 @@
package d2dcc
type DCCCell struct {
Width int
Height int
XOffset int
YOffset int
LastWidth int
LastHeight int
LastXOffset int
LastYOffset int
}

View File

@ -1,47 +1,12 @@
package d2data
package d2dcc
import (
"log"
"github.com/OpenDiablo2/OpenDiablo2/d2helper"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2common"
"github.com/OpenDiablo2/OpenDiablo2/d2helper"
)
type DCCPixelBufferEntry struct {
Value []byte
Frame int
FrameCellIndex int
}
type DCCCell struct {
Width int
Height int
XOffset int
YOffset int
LastWidth int
LastHeight int
LastXOffset int
LastYOffset int
}
type DCCDirectionFrame struct {
Width int
Height int
XOffset int
YOffset int
NumberOfOptionalBytes int
NumberOfCodedBytes int
FrameIsBottomUp bool
Box d2common.Rectangle
Cells []DCCCell
PixelData []byte
HorizontalCellCount int
VerticalCellCount int
}
type DCCDirection struct {
OutSizeCoded int
CompressionFlags int
@ -66,108 +31,8 @@ type DCCDirection struct {
PixelBuffer []*DCCPixelBufferEntry
}
type DCC struct {
Signature int
Version int
NumberOfDirections int
FramesPerDirection int
Directions []*DCCDirection
}
var crazyBitTable = []byte{0, 1, 2, 4, 6, 8, 10, 12, 14, 16, 20, 24, 26, 28, 30, 32}
var pixelMaskLookup = []int{0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4}
var dccDir4 = []byte{0, 1, 2, 3}
var dccDir8 = []byte{4, 0, 5, 1, 6, 2, 7, 3}
var dccDir16 = []byte{4, 8, 0, 9, 5, 10, 1, 11, 6, 12, 2, 13, 7, 14, 3, 15}
var dccDir32 = []byte{4, 16, 8, 17, 0, 18, 9, 19, 5, 20, 10, 21, 1, 22, 11, 23,
6, 24, 12, 25, 2, 26, 13, 27, 7, 28, 14, 29, 3, 30, 15, 31}
func CreateDCCDirectionFrame(bits *d2common.BitMuncher, direction *DCCDirection) *DCCDirectionFrame {
result := &DCCDirectionFrame{}
bits.GetBits(direction.Variable0Bits) // Variable0
result.Width = int(bits.GetBits(direction.WidthBits))
result.Height = int(bits.GetBits(direction.HeightBits))
result.XOffset = bits.GetSignedBits(direction.XOffsetBits)
result.YOffset = bits.GetSignedBits(direction.YOffsetBits)
result.NumberOfOptionalBytes = int(bits.GetBits(direction.OptionalDataBits))
result.NumberOfCodedBytes = int(bits.GetBits(direction.CodedBytesBits))
result.FrameIsBottomUp = bits.GetBit() == 1
if result.FrameIsBottomUp {
log.Panic("Bottom up frames are not implemented.")
} else {
result.Box = d2common.Rectangle{
result.XOffset,
result.YOffset - result.Height + 1,
result.Width,
result.Height,
}
}
return result
}
func (v *DCCDirectionFrame) CalculateCells(direction *DCCDirection) {
var w = 4 - ((v.Box.Left - direction.Box.Left) % 4) // Width of the first column (in pixels)
if (v.Width - w) <= 1 {
v.HorizontalCellCount = 1
} else {
tmp := v.Width - w - 1
v.HorizontalCellCount = 2 + (tmp / 4)
if (tmp % 4) == 0 {
v.HorizontalCellCount--
}
}
h := 4 - ((v.Box.Top - direction.Box.Top) % 4) // Height of the first column (in pixels)
if (v.Height - h) <= 1 {
v.VerticalCellCount = 1
} else {
tmp := v.Height - h - 1
v.VerticalCellCount = 2 + (tmp / 4)
if (tmp % 4) == 0 {
v.VerticalCellCount--
}
}
// Calculate the cell widths and heights
cellWidths := make([]int, v.HorizontalCellCount)
if v.HorizontalCellCount == 1 {
cellWidths[0] = v.Width
} else {
cellWidths[0] = w
for i := 1; i < (v.HorizontalCellCount - 1); i++ {
cellWidths[i] = 4
}
cellWidths[v.HorizontalCellCount-1] = v.Width - w - (4 * (v.HorizontalCellCount - 2))
}
cellHeights := make([]int, v.VerticalCellCount)
if v.VerticalCellCount == 1 {
cellHeights[0] = v.Height
} else {
cellHeights[0] = h
for i := 1; i < (v.VerticalCellCount - 1); i++ {
cellHeights[i] = 4
}
cellHeights[v.VerticalCellCount-1] = v.Height - h - (4 * (v.VerticalCellCount - 2))
}
v.Cells = make([]DCCCell, v.HorizontalCellCount*v.VerticalCellCount)
offsetY := v.Box.Top - direction.Box.Top
for y := 0; y < v.VerticalCellCount; y++ {
offsetX := v.Box.Left - direction.Box.Left
for x := 0; x < v.HorizontalCellCount; x++ {
v.Cells[x+(y*v.HorizontalCellCount)] = DCCCell{
XOffset: offsetX,
YOffset: offsetY,
Width: cellWidths[x],
Height: cellHeights[y],
}
offsetX += cellWidths[x]
}
offsetY += cellHeights[y]
}
}
func CreateDCCDirection(bm *d2common.BitMuncher, file *DCC) *DCCDirection {
result := &DCCDirection{}
func CreateDCCDirection(bm *d2common.BitMuncher, file DCC) DCCDirection {
result := DCCDirection{}
result.OutSizeCoded = int(bm.GetUInt32())
result.CompressionFlags = int(bm.GetBits(2))
result.Variable0Bits = int(crazyBitTable[bm.GetBits(4)])
@ -498,43 +363,3 @@ func (v *DCCDirection) CalculateCells() {
yOffset += 4
}
}
func LoadDCC(path string, fileProvider d2interface.FileProvider) *DCC {
result := &DCC{}
fileData := fileProvider.LoadFile(path)
var bm = d2common.CreateBitMuncher(fileData, 0)
result.Signature = int(bm.GetByte())
if result.Signature != 0x74 {
log.Fatal("Signature expected to be 0x74 but it is not.")
}
result.Version = int(bm.GetByte())
result.NumberOfDirections = int(bm.GetByte())
result.FramesPerDirection = int(bm.GetInt32())
if bm.GetInt32() != 1 {
log.Fatal("This value isn't 1. It has to be 1.")
}
bm.GetInt32() // TotalSizeCoded
directionOffsets := make([]int, result.NumberOfDirections)
for i := 0; i < result.NumberOfDirections; i++ {
directionOffsets[i] = int(bm.GetInt32())
}
result.Directions = make([]*DCCDirection, result.NumberOfDirections)
for i := 0; i < result.NumberOfDirections; i++ {
dir := byte(0)
switch result.NumberOfDirections {
case 1:
dir = 0
case 4:
dir = dccDir4[i]
case 8:
dir = dccDir8[i]
case 16:
dir = dccDir16[i]
case 32:
dir = dccDir32[i]
}
result.Directions[dir] = CreateDCCDirection(d2common.CreateBitMuncher(fileData, directionOffsets[i]*8), result)
}
return result
}

View File

@ -0,0 +1,108 @@
package d2dcc
import (
"log"
"github.com/OpenDiablo2/OpenDiablo2/d2common"
)
type DCCDirectionFrame struct {
Width int
Height int
XOffset int
YOffset int
NumberOfOptionalBytes int
NumberOfCodedBytes int
FrameIsBottomUp bool
Box d2common.Rectangle
Cells []DCCCell
PixelData []byte
HorizontalCellCount int
VerticalCellCount int
valid bool
}
func CreateDCCDirectionFrame(bits *d2common.BitMuncher, direction DCCDirection) *DCCDirectionFrame {
result := &DCCDirectionFrame{}
bits.GetBits(direction.Variable0Bits) // Variable0
result.Width = int(bits.GetBits(direction.WidthBits))
result.Height = int(bits.GetBits(direction.HeightBits))
result.XOffset = bits.GetSignedBits(direction.XOffsetBits)
result.YOffset = bits.GetSignedBits(direction.YOffsetBits)
result.NumberOfOptionalBytes = int(bits.GetBits(direction.OptionalDataBits))
result.NumberOfCodedBytes = int(bits.GetBits(direction.CodedBytesBits))
result.FrameIsBottomUp = bits.GetBit() == 1
if result.FrameIsBottomUp {
log.Panic("Bottom up frames are not implemented.")
} else {
result.Box = d2common.Rectangle{
result.XOffset,
result.YOffset - result.Height + 1,
result.Width,
result.Height,
}
}
result.valid = true
return result
}
func (v *DCCDirectionFrame) CalculateCells(direction DCCDirection) {
var w = 4 - ((v.Box.Left - direction.Box.Left) % 4) // Width of the first column (in pixels)
if (v.Width - w) <= 1 {
v.HorizontalCellCount = 1
} else {
tmp := v.Width - w - 1
v.HorizontalCellCount = 2 + (tmp / 4)
if (tmp % 4) == 0 {
v.HorizontalCellCount--
}
}
h := 4 - ((v.Box.Top - direction.Box.Top) % 4) // Height of the first column (in pixels)
if (v.Height - h) <= 1 {
v.VerticalCellCount = 1
} else {
tmp := v.Height - h - 1
v.VerticalCellCount = 2 + (tmp / 4)
if (tmp % 4) == 0 {
v.VerticalCellCount--
}
}
// Calculate the cell widths and heights
cellWidths := make([]int, v.HorizontalCellCount)
if v.HorizontalCellCount == 1 {
cellWidths[0] = v.Width
} else {
cellWidths[0] = w
for i := 1; i < (v.HorizontalCellCount - 1); i++ {
cellWidths[i] = 4
}
cellWidths[v.HorizontalCellCount-1] = v.Width - w - (4 * (v.HorizontalCellCount - 2))
}
cellHeights := make([]int, v.VerticalCellCount)
if v.VerticalCellCount == 1 {
cellHeights[0] = v.Height
} else {
cellHeights[0] = h
for i := 1; i < (v.VerticalCellCount - 1); i++ {
cellHeights[i] = 4
}
cellHeights[v.VerticalCellCount-1] = v.Height - h - (4 * (v.VerticalCellCount - 2))
}
v.Cells = make([]DCCCell, v.HorizontalCellCount*v.VerticalCellCount)
offsetY := v.Box.Top - direction.Box.Top
for y := 0; y < v.VerticalCellCount; y++ {
offsetX := v.Box.Left - direction.Box.Left
for x := 0; x < v.HorizontalCellCount; x++ {
v.Cells[x+(y*v.HorizontalCellCount)] = DCCCell{
XOffset: offsetX,
YOffset: offsetY,
Width: cellWidths[x],
Height: cellHeights[y],
}
offsetX += cellWidths[x]
}
offsetY += cellHeights[y]
}
}

View File

@ -0,0 +1,7 @@
package d2dcc
type DCCPixelBufferEntry struct {
Value []byte
Frame int
FrameCellIndex int
}

View File

@ -0,0 +1,7 @@
package d2ds1
var dirLookup = []int32{
0x00, 0x01, 0x02, 0x01, 0x02, 0x03, 0x03, 0x05, 0x05, 0x06,
0x06, 0x07, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
0x0F, 0x10, 0x11, 0x12, 0x14,
}

View File

@ -1,93 +1,33 @@
package d2data
package d2ds1
import (
"github.com/OpenDiablo2/OpenDiablo2/d2common"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2data"
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2datadict"
"github.com/OpenDiablo2/OpenDiablo2/d2helper"
)
var dirLookup = []int32{
0x00, 0x01, 0x02, 0x01, 0x02, 0x03, 0x03, 0x05, 0x05, 0x06,
0x06, 0x07, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
0x0F, 0x10, 0x11, 0x12, 0x14,
}
type LayerStreamType int
const (
LayerStreamWall1 LayerStreamType = 0
LayerStreamWall2 LayerStreamType = 1
LayerStreamWall3 LayerStreamType = 2
LayerStreamWall4 LayerStreamType = 3
LayerStreamOrientation1 LayerStreamType = 4
LayerStreamOrientation2 LayerStreamType = 5
LayerStreamOrientation3 LayerStreamType = 6
LayerStreamOrientation4 LayerStreamType = 7
LayerStreamFloor1 LayerStreamType = 8
LayerStreamFloor2 LayerStreamType = 9
LayerStreamShadow LayerStreamType = 10
LayerStreamSubstitute LayerStreamType = 11
)
type FloorShadowRecord struct {
Prop1 byte
SubIndex byte
Unknown1 byte
MainIndex byte
Unknown2 byte
Hidden bool
}
type WallRecord struct {
Orientation byte
Zero byte
Prop1 byte
SubIndex byte
Unknown1 byte
MainIndex byte
Unknown2 byte
Hidden bool
}
type SubstitutionRecord struct {
Unknown uint32
}
type TileRecord struct {
Floors []FloorShadowRecord
Walls []WallRecord
Shadows []FloorShadowRecord
Substitutions []SubstitutionRecord
}
type SubstitutionGroup struct {
TileX int32
TileY int32
WidthInTiles int32
HeightInTiles int32
Unknown int32
}
type DS1 struct {
Version int32 // The version of the DS1
Width int32 // Width of map, in # of tiles
Height int32 // Height of map, in # of tiles
Act int32 // Act, from 1 to 5. This tells which act table to use for the Objects list
SubstitutionType int32 // SubstitutionType (layer type): 0 if no layer, else type 1 or type 2
Files []string // FilePtr table of file string pointers
NumberOfWalls int32 // WallNum number of wall & orientation layers used
NumberOfFloors int32 // number of floor layers used
NumberOfShadowLayers int32 // ShadowNum number of shadow layer used
NumberOfSubstitutionLayers int32 // SubstitutionNum number of substitution layer used
SubstitutionGroupsNum int32 // SubstitutionGroupsNum number of substitution groups, datas between objects & NPC paths
Objects []Object // Objects
Version int32 // The version of the DS1
Width int32 // Width of map, in # of tiles
Height int32 // Height of map, in # of tiles
Act int32 // Act, from 1 to 5. This tells which act table to use for the Objects list
SubstitutionType int32 // SubstitutionType (layer type): 0 if no layer, else type 1 or type 2
Files []string // FilePtr table of file string pointers
NumberOfWalls int32 // WallNum number of wall & orientation layers used
NumberOfFloors int32 // number of floor layers used
NumberOfShadowLayers int32 // ShadowNum number of shadow layer used
NumberOfSubstitutionLayers int32 // SubstitutionNum number of substitution layer used
SubstitutionGroupsNum int32 // SubstitutionGroupsNum number of substitution groups, datas between objects & NPC paths
Objects []d2data.Object // Objects
Tiles [][]TileRecord
SubstitutionGroups []SubstitutionGroup
}
func LoadDS1(path string, fileProvider d2interface.FileProvider) *DS1 {
ds1 := &DS1{
func LoadDS1(path string, fileProvider d2interface.FileProvider) DS1 {
ds1 := DS1{
NumberOfFloors: 1,
NumberOfWalls: 1,
NumberOfShadowLayers: 1,
@ -134,29 +74,34 @@ func LoadDS1(path string, fileProvider d2interface.FileProvider) *DS1 {
ds1.NumberOfFloors = 1
}
}
var layerStream []LayerStreamType
var layerStream []d2enum.LayerStreamType
if ds1.Version < 4 {
layerStream = []LayerStreamType{
LayerStreamWall1,
LayerStreamFloor1,
LayerStreamOrientation1,
LayerStreamSubstitute,
LayerStreamShadow,
layerStream = []d2enum.LayerStreamType{
d2enum.LayerStreamWall1,
d2enum.LayerStreamFloor1,
d2enum.LayerStreamOrientation1,
d2enum.LayerStreamSubstitute,
d2enum.LayerStreamShadow,
}
} else {
layerStream = make([]LayerStreamType, 0)
layerStream = make([]d2enum.LayerStreamType, (ds1.NumberOfWalls*2)+ds1.NumberOfFloors+ds1.NumberOfShadowLayers+ds1.NumberOfSubstitutionLayers)
layerIdx := 0
for i := 0; i < int(ds1.NumberOfWalls); i++ {
layerStream = append(layerStream, LayerStreamType(int(LayerStreamWall1)+i))
layerStream = append(layerStream, LayerStreamType(int(LayerStreamOrientation1)+i))
layerStream[layerIdx] = d2enum.LayerStreamType(int(d2enum.LayerStreamWall1) + i)
layerStream[layerIdx+1] = d2enum.LayerStreamType(int(d2enum.LayerStreamOrientation1) + i)
layerIdx += 2
}
for i := 0; i < int(ds1.NumberOfFloors); i++ {
layerStream = append(layerStream, LayerStreamType(int(LayerStreamFloor1)+i))
layerStream[layerIdx] = d2enum.LayerStreamType(int(d2enum.LayerStreamFloor1) + i)
layerIdx++
}
if ds1.NumberOfShadowLayers > 0 {
layerStream = append(layerStream, LayerStreamShadow)
layerStream[layerIdx] = d2enum.LayerStreamShadow
layerIdx++
}
if ds1.NumberOfSubstitutionLayers > 0 {
layerStream = append(layerStream, LayerStreamSubstitute)
layerStream[layerIdx] = d2enum.LayerStreamSubstitute
layerIdx++
}
}
ds1.Tiles = make([][]TileRecord, ds1.Height)
@ -174,28 +119,28 @@ func LoadDS1(path string, fileProvider d2interface.FileProvider) *DS1 {
for x := 0; x < int(ds1.Width); x++ {
dw := br.GetUInt32()
switch layerStreamType {
case LayerStreamWall1:
case d2enum.LayerStreamWall1:
fallthrough
case LayerStreamWall2:
case d2enum.LayerStreamWall2:
fallthrough
case LayerStreamWall3:
case d2enum.LayerStreamWall3:
fallthrough
case LayerStreamWall4:
wallIndex := int(layerStreamType) - int(LayerStreamWall1)
case d2enum.LayerStreamWall4:
wallIndex := int(layerStreamType) - int(d2enum.LayerStreamWall1)
ds1.Tiles[y][x].Walls[wallIndex].Prop1 = byte(dw & 0x000000FF)
ds1.Tiles[y][x].Walls[wallIndex].SubIndex = byte((dw & 0x00003F00) >> 8)
ds1.Tiles[y][x].Walls[wallIndex].Unknown1 = byte((dw & 0x000FC000) >> 14)
ds1.Tiles[y][x].Walls[wallIndex].MainIndex = byte((dw & 0x03F00000) >> 20)
ds1.Tiles[y][x].Walls[wallIndex].Unknown2 = byte((dw & 0x7C000000) >> 26)
ds1.Tiles[y][x].Walls[wallIndex].Hidden = byte((dw&0x80000000)>>31) > 0
case LayerStreamOrientation1:
case d2enum.LayerStreamOrientation1:
fallthrough
case LayerStreamOrientation2:
case d2enum.LayerStreamOrientation2:
fallthrough
case LayerStreamOrientation3:
case d2enum.LayerStreamOrientation3:
fallthrough
case LayerStreamOrientation4:
wallIndex := int(layerStreamType) - int(LayerStreamOrientation1)
case d2enum.LayerStreamOrientation4:
wallIndex := int(layerStreamType) - int(d2enum.LayerStreamOrientation1)
c := int32(dw & 0x000000FF)
if ds1.Version < 7 {
if c < 25 {
@ -204,34 +149,34 @@ func LoadDS1(path string, fileProvider d2interface.FileProvider) *DS1 {
}
ds1.Tiles[y][x].Walls[wallIndex].Orientation = byte(c)
ds1.Tiles[y][x].Walls[wallIndex].Zero = byte((dw & 0xFFFFFF00) >> 8)
case LayerStreamFloor1:
case d2enum.LayerStreamFloor1:
fallthrough
case LayerStreamFloor2:
floorIndex := int(layerStreamType) - int(LayerStreamFloor1)
case d2enum.LayerStreamFloor2:
floorIndex := int(layerStreamType) - int(d2enum.LayerStreamFloor1)
ds1.Tiles[y][x].Floors[floorIndex].Prop1 = byte(dw & 0x000000FF)
ds1.Tiles[y][x].Floors[floorIndex].SubIndex = byte((dw & 0x00003F00) >> 8)
ds1.Tiles[y][x].Floors[floorIndex].Unknown1 = byte((dw & 0x000FC000) >> 14)
ds1.Tiles[y][x].Floors[floorIndex].MainIndex = byte((dw & 0x03F00000) >> 20)
ds1.Tiles[y][x].Floors[floorIndex].Unknown2 = byte((dw & 0x7C000000) >> 26)
ds1.Tiles[y][x].Floors[floorIndex].Hidden = byte((dw&0x80000000)>>31) > 0
case LayerStreamShadow:
case d2enum.LayerStreamShadow:
ds1.Tiles[y][x].Shadows[0].Prop1 = byte(dw & 0x000000FF)
ds1.Tiles[y][x].Shadows[0].SubIndex = byte((dw & 0x00003F00) >> 8)
ds1.Tiles[y][x].Shadows[0].Unknown1 = byte((dw & 0x000FC000) >> 14)
ds1.Tiles[y][x].Shadows[0].MainIndex = byte((dw & 0x03F00000) >> 20)
ds1.Tiles[y][x].Shadows[0].Unknown2 = byte((dw & 0x7C000000) >> 26)
ds1.Tiles[y][x].Shadows[0].Hidden = byte((dw&0x80000000)>>31) > 0
case LayerStreamSubstitute:
case d2enum.LayerStreamSubstitute:
ds1.Tiles[y][x].Substitutions[0].Unknown = dw
}
}
}
}
ds1.Objects = make([]Object, 0)
if ds1.Version >= 2 {
numberOfObjects := br.GetInt32()
ds1.Objects = make([]d2data.Object, numberOfObjects)
for objIdx := 0; objIdx < int(numberOfObjects); objIdx++ {
newObject := Object{}
newObject := d2data.Object{}
newObject.Type = br.GetInt32()
newObject.Id = br.GetInt32()
newObject.X = br.GetInt32()
@ -241,15 +186,17 @@ func LoadDS1(path string, fileProvider d2interface.FileProvider) *DS1 {
if newObject.Lookup != nil && newObject.Lookup.ObjectsTxtId != -1 {
newObject.ObjectInfo = d2datadict.Objects[newObject.Lookup.ObjectsTxtId]
}
ds1.Objects = append(ds1.Objects, newObject)
ds1.Objects[objIdx] = newObject
}
} else {
ds1.Objects = make([]d2data.Object, 0)
}
ds1.SubstitutionGroups = make([]SubstitutionGroup, 0)
if ds1.Version >= 12 && (ds1.SubstitutionType == 1 || ds1.SubstitutionType == 2) {
if ds1.Version >= 18 {
br.GetUInt32()
}
numberOfSubGroups := br.GetInt32()
ds1.SubstitutionGroups = make([]SubstitutionGroup, numberOfSubGroups)
for subIdx := 0; subIdx < int(numberOfSubGroups); subIdx++ {
newSub := SubstitutionGroup{}
newSub.TileX = br.GetInt32()
@ -258,8 +205,10 @@ func LoadDS1(path string, fileProvider d2interface.FileProvider) *DS1 {
newSub.HeightInTiles = br.GetInt32()
newSub.Unknown = br.GetInt32()
ds1.SubstitutionGroups = append(ds1.SubstitutionGroups, newSub)
ds1.SubstitutionGroups[subIdx] = newSub
}
} else {
ds1.SubstitutionGroups = make([]SubstitutionGroup, 0)
}
if ds1.Version >= 14 {
numberOfNpcs := br.GetInt32()

View File

@ -0,0 +1,10 @@
package d2ds1
type FloorShadowRecord struct {
Prop1 byte
SubIndex byte
Unknown1 byte
MainIndex byte
Unknown2 byte
Hidden bool
}

View File

@ -0,0 +1,9 @@
package d2ds1
type SubstitutionGroup struct {
TileX int32
TileY int32
WidthInTiles int32
HeightInTiles int32
Unknown int32
}

View File

@ -0,0 +1,5 @@
package d2ds1
type SubstitutionRecord struct {
Unknown uint32
}

View File

@ -0,0 +1,8 @@
package d2ds1
type TileRecord struct {
Floors []FloorShadowRecord
Walls []WallRecord
Shadows []FloorShadowRecord
Substitutions []SubstitutionRecord
}

View File

@ -0,0 +1,12 @@
package d2ds1
type WallRecord struct {
Orientation byte
Zero byte
Prop1 byte
SubIndex byte
Unknown1 byte
MainIndex byte
Unknown2 byte
Hidden bool
}

12
d2data/d2dt1/Block.go Normal file
View File

@ -0,0 +1,12 @@
package d2dt1
type Block struct {
X int16
Y int16
GridX byte
GridY byte
Format BlockDataFormat
EncodedData []byte
Length int32
FileOffset int32
}

View File

@ -1,4 +1,4 @@
package d2data
package d2dt1
import (
"log"
@ -10,34 +10,6 @@ import (
// https://d2mods.info/forum/viewtopic.php?t=65163
type Block struct {
X int16
Y int16
GridX byte
GridY byte
Format BlockDataFormat
EncodedData []byte
Length int32
FileOffset int32
}
type Tile struct {
Direction int32
RoofHeight int16
SoundIndex byte
Animated bool
Height int32
Width int32
Orientation int32
MainIndex int32
SubIndex int32
RarityFrameIndex int32
SubTileFlags [25]byte
blockHeaderPointer int32
blockHeaderSize int32
Blocks []Block
}
type DT1 struct {
Tiles []Tile
}
@ -49,8 +21,8 @@ const (
BlockFormatIsometric BlockDataFormat = 1
)
func LoadDT1(path string, fileProvider d2interface.FileProvider) *DT1 {
result := &DT1{}
func LoadDT1(path string, fileProvider d2interface.FileProvider) DT1 {
result := DT1{}
fileData := fileProvider.LoadFile(path)
br := d2common.CreateStreamReader(fileData)
ver1 := br.GetInt32()

18
d2data/d2dt1/Tile.go Normal file
View File

@ -0,0 +1,18 @@
package d2dt1
type Tile struct {
Direction int32
RoofHeight int16
SoundIndex byte
Animated bool
Height int32
Width int32
Orientation int32
MainIndex int32
SubIndex int32
RarityFrameIndex int32
SubTileFlags [25]byte
blockHeaderPointer int32
blockHeaderSize int32
Blocks []Block
}

View File

@ -6,6 +6,10 @@ import (
"strings"
"time"
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2cof"
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2dcc"
"github.com/OpenDiablo2/OpenDiablo2/d2helper"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
@ -29,8 +33,8 @@ type AnimatedEntity struct {
LocationX float64
// LocationY represents the tile Y position of the entity
LocationY float64
dccLayers map[string]*d2data.DCC
Cof *d2data.Cof
dccLayers map[string]d2dcc.DCC
Cof *d2cof.COF
palette d2enum.PaletteType
base string
token string
@ -47,14 +51,14 @@ type AnimatedEntity struct {
}
// CreateAnimatedEntity creates an instance of AnimatedEntity
func CreateAnimatedEntity(object d2data.Object, fileProvider d2interface.FileProvider, palette d2enum.PaletteType) *AnimatedEntity {
result := &AnimatedEntity{
func CreateAnimatedEntity(object d2data.Object, fileProvider d2interface.FileProvider, palette d2enum.PaletteType) AnimatedEntity {
result := AnimatedEntity{
base: object.Lookup.Base,
token: object.Lookup.Token,
object: object,
palette: palette,
}
result.dccLayers = make(map[string]*d2data.DCC)
result.dccLayers = make(map[string]d2dcc.DCC)
result.LocationX = float64(object.X) / 5
result.LocationY = float64(object.Y) / 5
return result
@ -65,8 +69,8 @@ var DirectionLookup = []int{3, 15, 4, 8, 0, 9, 5, 10, 1, 11, 6, 12, 2, 13, 7, 14
// SetMode changes the graphical mode of this animated entity
func (v *AnimatedEntity) SetMode(animationMode, weaponClass string, direction int, provider d2interface.FileProvider) {
cofPath := fmt.Sprintf("%s/%s/Cof/%s%s%s.Cof", v.base, v.token, v.token, animationMode, weaponClass)
v.Cof = d2data.LoadCof(cofPath, provider)
cofPath := fmt.Sprintf("%s/%s/COF/%s%s%s.COF", v.base, v.token, v.token, animationMode, weaponClass)
v.Cof = d2cof.LoadCOF(cofPath, provider)
v.animationMode = animationMode
v.weaponClass = weaponClass
v.direction = direction
@ -75,11 +79,11 @@ func (v *AnimatedEntity) SetMode(animationMode, weaponClass string, direction in
}
v.frames = make(map[string][]*ebiten.Image)
v.frameLocations = make(map[string][]d2common.Rectangle)
v.dccLayers = make(map[string]*d2data.DCC)
v.dccLayers = make(map[string]d2dcc.DCC)
for _, cofLayer := range v.Cof.CofLayers {
layerName := DccLayerNames[cofLayer.Type]
v.dccLayers[layerName] = v.LoadLayer(layerName, provider)
if v.dccLayers[layerName] == nil {
if !v.dccLayers[layerName].IsValid() {
continue
}
v.cacheFrames(layerName)
@ -87,7 +91,7 @@ func (v *AnimatedEntity) SetMode(animationMode, weaponClass string, direction in
}
func (v *AnimatedEntity) LoadLayer(layer string, fileProvider d2interface.FileProvider) *d2data.DCC {
func (v *AnimatedEntity) LoadLayer(layer string, fileProvider d2interface.FileProvider) d2dcc.DCC {
layerName := "tr"
switch strings.ToUpper(layer) {
case "HD": // Head
@ -124,10 +128,10 @@ func (v *AnimatedEntity) LoadLayer(layer string, fileProvider d2interface.FilePr
layerName = v.object.Lookup.S8
}
if len(layerName) == 0 {
return nil
return d2dcc.DCC{}
}
dccPath := fmt.Sprintf("%s/%s/%s/%s%s%s%s%s.dcc", v.base, v.token, layer, v.token, layer, layerName, v.animationMode, v.weaponClass)
return d2data.LoadDCC(dccPath, fileProvider)
return d2dcc.LoadDCC(dccPath, fileProvider)
}
// Render draws this animated entity onto the target

View File

@ -20,7 +20,7 @@ type Sprite struct {
FramesPerDirection uint32
atlas *ebiten.Image
atlasBytes []byte
Frames []*SpriteFrame
Frames []SpriteFrame
SpecialFrameTime int
StopOnLastFrame bool
X, Y int
@ -30,6 +30,7 @@ type Sprite struct {
Animate bool
ColorMod color.Color
visible bool
valid bool
}
// SpriteFrame represents a single frame of a sprite
@ -49,8 +50,8 @@ type SpriteFrame struct {
}
// CreateSprite creates an instance of a sprite
func CreateSprite(data []byte, palette d2datadict.PaletteRec) *Sprite {
result := &Sprite{
func CreateSprite(data []byte, palette d2datadict.PaletteRec) Sprite {
result := Sprite{
X: 50,
Y: 50,
Frame: 0,
@ -63,6 +64,7 @@ func CreateSprite(data []byte, palette d2datadict.PaletteRec) *Sprite {
LastFrameTime: time.Now(),
SpecialFrameTime: -1,
StopOnLastFrame: false,
valid: false,
}
dataPointer := uint32(24)
totalFrames := result.Directions * result.FramesPerDirection
@ -71,14 +73,14 @@ func CreateSprite(data []byte, palette d2datadict.PaletteRec) *Sprite {
framePointers[i] = binary.LittleEndian.Uint32(data[dataPointer : dataPointer+4])
dataPointer += 4
}
result.Frames = make([]*SpriteFrame, totalFrames)
result.Frames = make([]SpriteFrame, totalFrames)
wg := sync.WaitGroup{}
wg.Add(int(totalFrames))
for i := uint32(0); i < totalFrames; i++ {
go func(i uint32) {
defer wg.Done()
dataPointer := framePointers[i]
result.Frames[i] = &SpriteFrame{}
result.Frames[i] = SpriteFrame{}
result.Frames[i].Flip = binary.LittleEndian.Uint32(data[dataPointer : dataPointer+4])
dataPointer += 4
result.Frames[i].Width = binary.LittleEndian.Uint32(data[dataPointer : dataPointer+4])
@ -169,14 +171,18 @@ func CreateSprite(data []byte, palette d2datadict.PaletteRec) *Sprite {
curX += curMaxWidth
curY = 0
}
result.valid = true
return result
}
func (v Sprite) IsValid() bool {
return v.valid
}
func (v *Sprite) cacheFrame(frame int) {
if v.Frames[frame].cached {
return
}
r := v.Frames[frame].Image.Bounds().Min
curX := r.X
curY := r.Y
@ -196,7 +202,7 @@ func (v *Sprite) cacheFrame(frame int) {
}
// GetSize returns the size of the sprite
func (v *Sprite) GetSize() (uint32, uint32) {
func (v Sprite) GetSize() (uint32, uint32) {
frame := v.Frames[uint32(v.Frame)+(uint32(v.Direction)*v.FramesPerDirection)]
return frame.Width, frame.Height
}
@ -230,19 +236,19 @@ func (v *Sprite) ResetAnimation() {
v.Frame = 0
}
func (v *Sprite) OnLastFrame() bool {
func (v Sprite) OnLastFrame() bool {
return v.Frame == uint8(v.FramesPerDirection-1)
}
// GetFrameSize returns the size of the specific frame
func (v *Sprite) GetFrameSize(frame int) (width, height uint32) {
func (v Sprite) GetFrameSize(frame int) (width, height uint32) {
width = v.Frames[frame].Width
height = v.Frames[frame].Height
return
}
// GetTotalFrames returns the number of frames in this sprite (for all directions)
func (v *Sprite) GetTotalFrames() int {
func (v Sprite) GetTotalFrames() int {
return len(v.Frames)
}
@ -309,6 +315,6 @@ func (v *Sprite) MoveTo(x, y int) {
}
// GetLocation returns the location of the sprite
func (v *Sprite) GetLocation() (int, int) {
func (v Sprite) GetLocation() (int, int) {
return v.X, v.Y
}

View File

@ -8,6 +8,10 @@ import (
"strconv"
"sync"
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2dt1"
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2ds1"
"github.com/OpenDiablo2/OpenDiablo2/d2core"
"github.com/OpenDiablo2/OpenDiablo2/d2helper"
@ -34,16 +38,16 @@ type TileCacheRecord struct {
type Region struct {
RegionPath string
LevelType d2datadict.LevelTypeRecord
levelPreset *d2datadict.LevelPresetRecord
levelPreset d2datadict.LevelPresetRecord
TileWidth int32
TileHeight int32
Tiles []d2data.Tile
DS1 *d2data.DS1
Tiles []d2dt1.Tile
DS1 d2ds1.DS1
Palette d2datadict.PaletteRec
FloorCache map[uint32]*TileCacheRecord
ShadowCache map[uint32]*TileCacheRecord
WallCache map[uint32]*TileCacheRecord
AnimationEntities []*d2render.AnimatedEntity
AnimationEntities []d2render.AnimatedEntity
NPCs []*d2core.NPC
StartX float64
StartY float64
@ -101,7 +105,7 @@ func LoadRegion(seed rand.Source, levelType RegionIdType, levelPreset int, fileP
result := &Region{
LevelType: d2datadict.LevelTypes[levelType],
levelPreset: d2datadict.LevelPresets[levelPreset],
Tiles: make([]d2data.Tile, 0),
Tiles: make([]d2dt1.Tile, 0),
FloorCache: make(map[uint32]*TileCacheRecord),
ShadowCache: make(map[uint32]*TileCacheRecord),
WallCache: make(map[uint32]*TileCacheRecord),
@ -119,7 +123,7 @@ func LoadRegion(seed rand.Source, levelType RegionIdType, levelPreset int, fileP
if len(levelTypeDt1) == 0 || levelTypeDt1 == "" || levelTypeDt1 == "0" {
continue
}
dt1 := d2data.LoadDT1("/data/global/tiles/"+levelTypeDt1, fileProvider)
dt1 := d2dt1.LoadDT1("/data/global/tiles/"+levelTypeDt1, fileProvider)
result.Tiles = append(result.Tiles, dt1.Tiles...)
}
levelFilesToPick := make([]string, 0)
@ -133,7 +137,7 @@ func LoadRegion(seed rand.Source, levelType RegionIdType, levelPreset int, fileP
levelIndex := int(math.Round(float64(len(levelFilesToPick)-1) * random.Float64()))
levelFile := levelFilesToPick[levelIndex]
result.RegionPath = levelFile
result.DS1 = d2data.LoadDS1("/data/global/tiles/"+levelFile, fileProvider)
result.DS1 = d2ds1.LoadDS1("/data/global/tiles/"+levelFile, fileProvider)
result.TileWidth = result.DS1.Width
result.TileHeight = result.DS1.Height
result.loadObjects(fileProvider)
@ -143,7 +147,7 @@ func LoadRegion(seed rand.Source, levelType RegionIdType, levelPreset int, fileP
func (v *Region) loadObjects(fileProvider d2interface.FileProvider) {
var wg sync.WaitGroup
wg.Add(len(v.DS1.Objects))
v.AnimationEntities = make([]*d2render.AnimatedEntity, 0)
v.AnimationEntities = make([]d2render.AnimatedEntity, 0)
v.NPCs = make([]*d2core.NPC, 0)
for _, object := range v.DS1.Objects {
go func(object d2data.Object) {
@ -181,7 +185,7 @@ func (v *Region) RenderTile(offsetX, offsetY, tileX, tileY int, layerType Region
}
}
func (v *Region) getTile(mainIndex, subIndex, orientation int32) *d2data.Tile {
func (v *Region) getTile(mainIndex, subIndex, orientation int32) *d2dt1.Tile {
// TODO: Need to support randomly grabbing tile based on x/y as there can be multiple matches for same main/sub index
for _, tile := range v.Tiles {
if tile.MainIndex != mainIndex || tile.SubIndex != subIndex || tile.Orientation != orientation {
@ -193,7 +197,7 @@ func (v *Region) getTile(mainIndex, subIndex, orientation int32) *d2data.Tile {
return nil
}
func (v *Region) renderFloor(tile d2data.FloorShadowRecord, offsetX, offsetY int, target *ebiten.Image) {
func (v *Region) renderFloor(tile d2ds1.FloorShadowRecord, offsetX, offsetY int, target *ebiten.Image) {
tileCacheIndex := (uint32(tile.MainIndex) << 16) | (uint32(tile.SubIndex) << 8)
tileCache, exists := v.FloorCache[tileCacheIndex]
if !exists {
@ -208,7 +212,7 @@ func (v *Region) renderFloor(tile d2data.FloorShadowRecord, offsetX, offsetY int
target.DrawImage(tileCache.Image, opts)
}
func (v *Region) renderWall(tile d2data.WallRecord, offsetX, offsetY int, target *ebiten.Image) {
func (v *Region) renderWall(tile d2ds1.WallRecord, offsetX, offsetY int, target *ebiten.Image) {
tileCacheIndex := (uint32(tile.MainIndex) << 16) | (uint32(tile.SubIndex) << 8) | (uint32(tile.Orientation))
tileCache, exists := v.WallCache[tileCacheIndex]
if !exists {
@ -223,7 +227,7 @@ func (v *Region) renderWall(tile d2data.WallRecord, offsetX, offsetY int, target
target.DrawImage(tileCache.Image, opts)
}
func (v *Region) renderShadow(tile d2data.FloorShadowRecord, offsetX, offsetY int, target *ebiten.Image) {
func (v *Region) renderShadow(tile d2ds1.FloorShadowRecord, offsetX, offsetY int, target *ebiten.Image) {
tileCacheIndex := (uint32(tile.MainIndex) << 16) + (uint32(tile.SubIndex) << 8) + 0
tileCache, exists := v.ShadowCache[tileCacheIndex]
if !exists {
@ -239,9 +243,9 @@ func (v *Region) renderShadow(tile d2data.FloorShadowRecord, offsetX, offsetY in
target.DrawImage(tileCache.Image, opts)
}
func (v *Region) decodeTileGfxData(blocks []d2data.Block, pixels []byte, tileYOffset int32, tileWidth int32) {
func (v *Region) decodeTileGfxData(blocks []d2dt1.Block, pixels []byte, tileYOffset int32, tileWidth int32) {
for _, block := range blocks {
if block.Format == d2data.BlockFormatIsometric {
if block.Format == d2dt1.BlockFormatIsometric {
// 3D isometric decoding
xjump := []int32{14, 12, 10, 8, 6, 4, 2, 0, 2, 4, 6, 8, 10, 12, 14}
nbpix := []int32{4, 8, 12, 16, 20, 24, 28, 32, 28, 24, 20, 16, 12, 8, 4}
@ -311,7 +315,7 @@ func (v *Region) decodeTileGfxData(blocks []d2data.Block, pixels []byte, tileYOf
}
}
func (v *Region) generateFloorCache(tile d2data.FloorShadowRecord) *TileCacheRecord {
func (v *Region) generateFloorCache(tile d2ds1.FloorShadowRecord) *TileCacheRecord {
tileData := v.getTile(int32(tile.MainIndex), int32(tile.SubIndex), 0)
if tileData == nil {
log.Fatalf("Could not locate tile Idx:%d, Sub: %d, Ori: %d", tile.MainIndex, tile.SubIndex, 0)
@ -329,7 +333,7 @@ func (v *Region) generateFloorCache(tile d2data.FloorShadowRecord) *TileCacheRec
return &TileCacheRecord{image, 0, 0}
}
func (v *Region) generateShadowCache(tile d2data.FloorShadowRecord) *TileCacheRecord {
func (v *Region) generateShadowCache(tile d2ds1.FloorShadowRecord) *TileCacheRecord {
tileData := v.getTile(int32(tile.MainIndex), int32(tile.SubIndex), 13)
if tileData == nil {
return nil
@ -349,12 +353,12 @@ func (v *Region) generateShadowCache(tile d2data.FloorShadowRecord) *TileCacheRe
return &TileCacheRecord{image, 0, int(tileMinY) + 80}
}
func (v *Region) generateWallCache(tile d2data.WallRecord) *TileCacheRecord {
func (v *Region) generateWallCache(tile d2ds1.WallRecord) *TileCacheRecord {
tileData := v.getTile(int32(tile.MainIndex), int32(tile.SubIndex), int32(tile.Orientation))
if tileData == nil {
return nil
}
var newTileData *d2data.Tile = nil
var newTileData *d2dt1.Tile = nil
if tile.Orientation == 3 {
newTileData = v.getTile(int32(tile.MainIndex), int32(tile.SubIndex), int32(4))
}

View File

@ -27,7 +27,7 @@ type FontSize struct {
// Font represents a font
type Font struct {
fontSprite *d2render.Sprite
fontSprite d2render.Sprite
metrics map[uint8]FontSize
}

View File

@ -23,7 +23,7 @@ const (
// Manager represents the UI manager
type Manager struct {
widgets []Widget
cursorSprite *d2render.Sprite
cursorSprite d2render.Sprite
cursorButtons CursorButton
pressedIndex int
CursorX int

View File

@ -20,7 +20,7 @@ var GitBranch string
// GitCommit is set by the CI build process to the commit hash
var GitCommit string
var d2Engine *d2core.Engine
var d2Engine d2core.Engine
func main() {
//defer profile.Start(profile.CPUProfile).Stop()
@ -39,7 +39,7 @@ func main() {
}
d2mpq.InitializeCryptoBuffer()
d2Engine = d2core.CreateEngine()
d2Engine.SetNextScene(d2scene.CreateMainMenu(d2Engine, d2Engine, d2Engine.UIManager, d2Engine.SoundManager))
d2Engine.SetNextScene(d2scene.CreateMainMenu(&d2Engine, &d2Engine, d2Engine.UIManager, d2Engine.SoundManager))
ebiten.SetCursorVisible(false)
ebiten.SetFullscreen(d2Engine.Settings.FullScreen)
ebiten.SetRunnableInBackground(d2Engine.Settings.RunInBackground)

View File

@ -6,7 +6,9 @@ import (
"strings"
"testing"
"github.com/OpenDiablo2/OpenDiablo2/d2data"
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2cof"
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2dcc"
"github.com/OpenDiablo2/OpenDiablo2/d2core"
@ -36,12 +38,12 @@ func TestMPQScanPerformance(t *testing.T) {
parts := strings.Split(archiveFile, ".")
switch strings.ToLower(parts[len(parts)-1]) {
case "coff":
_ = d2data.LoadCof(archiveFile, engine)
_ = d2cof.LoadCOF(archiveFile, engine)
case "dcc":
if strings.ContainsAny(archiveFile, "common") {
continue
}
_ = d2data.LoadDCC(archiveFile, engine)
_ = d2dcc.LoadDCC(archiveFile, engine)
}
_, _ = archive.ReadFile(archiveFile)