From 51d78bfcbf509f0fd51425214f7b7cfb383dc1a1 Mon Sep 17 00:00:00 2001 From: Ziemas Date: Wed, 27 Nov 2019 03:04:36 +0100 Subject: [PATCH] Draw object layers in order and generally untangle the direction mess. (#239) * Implement drawing COF Layers by priority Hack it up by using always draw order for direction 0 until we get a better handle on directions in the engine. * Take directions in "cof directions" Transform into dcc directions for loading frames. * Set characters to dir 0 in char select. Direction 0 is now facing down universally --- d2core/d2scene/character_select.go | 11 +++-- d2core/d2scene/game.go | 6 +-- d2render/animated_entity.go | 75 +++++++++++++++++++----------- go.mod | 2 +- go.sum | 8 +--- 5 files changed, 61 insertions(+), 41 deletions(-) diff --git a/d2core/d2scene/character_select.go b/d2core/d2scene/character_select.go index 707ea575..e51b9fbf 100644 --- a/d2core/d2scene/character_select.go +++ b/d2core/d2scene/character_select.go @@ -1,23 +1,24 @@ package d2scene import ( - "github.com/OpenDiablo2/D2Shared/d2data/d2dc6" "image/color" "math" "os" "strings" + "github.com/OpenDiablo2/D2Shared/d2data/d2dc6" + "github.com/hajimehoshi/ebiten/ebitenutil" - "github.com/OpenDiablo2/OpenDiablo2/d2audio" "github.com/OpenDiablo2/D2Shared/d2common" "github.com/OpenDiablo2/D2Shared/d2common/d2enum" "github.com/OpenDiablo2/D2Shared/d2common/d2interface" - "github.com/OpenDiablo2/OpenDiablo2/d2corecommon/d2coreinterface" "github.com/OpenDiablo2/D2Shared/d2common/d2resource" - "github.com/OpenDiablo2/OpenDiablo2/d2core" "github.com/OpenDiablo2/D2Shared/d2data/d2datadict" dh "github.com/OpenDiablo2/D2Shared/d2helper" + "github.com/OpenDiablo2/OpenDiablo2/d2audio" + "github.com/OpenDiablo2/OpenDiablo2/d2core" + "github.com/OpenDiablo2/OpenDiablo2/d2corecommon/d2coreinterface" "github.com/OpenDiablo2/OpenDiablo2/d2render" "github.com/OpenDiablo2/OpenDiablo2/d2render/d2ui" "github.com/hajimehoshi/ebiten" @@ -189,7 +190,7 @@ func (v *CharacterSelect) updateCharacterBoxes() { v.characterImage[i] = d2core.CreateHero( 0, 0, - 5, + 0, v.gameStates[idx].HeroType, d2core.HeroObjects[v.gameStates[idx].HeroType], v.fileProvider, diff --git a/d2core/d2scene/game.go b/d2core/d2scene/game.go index 6afe7930..4c53fb59 100644 --- a/d2core/d2scene/game.go +++ b/d2core/d2scene/game.go @@ -1,9 +1,10 @@ package d2scene import ( - "github.com/OpenDiablo2/D2Shared/d2data/d2dc6" "image/color" + "github.com/OpenDiablo2/D2Shared/d2data/d2dc6" + "github.com/OpenDiablo2/D2Shared/d2helper" "github.com/OpenDiablo2/OpenDiablo2/d2render/d2mapengine" @@ -111,8 +112,7 @@ func (v *Game) Update(tickTime float64) { py, ) - directionIndex := int((float64(angle) / 360.0) * 16.0) - newDirection := d2render.DirectionLookup[directionIndex] + newDirection := int((float64(angle) / 360.0) * 16.0) if newDirection != v.mapEngine.Hero.AnimatedEntity.GetDirection() { v.mapEngine.Hero.AnimatedEntity.SetMode(d2enum.AnimationModePlayerTownNeutral.String(), v.mapEngine.Hero.Equipment.RightHand.GetWeaponClass(), newDirection) } diff --git a/d2render/animated_entity.go b/d2render/animated_entity.go index 7f636f7b..a97bf3f3 100644 --- a/d2render/animated_entity.go +++ b/d2render/animated_entity.go @@ -25,9 +25,6 @@ import ( var DccLayerNames = []string{"HD", "TR", "LG", "RA", "LA", "RH", "LH", "SH", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8"} -// DirectionLookup is used to decode the direction offset indexes -var DirectionLookup = []int{9, 15, 5, 6, 4, 12, 10, 2, 8, 13, 1, 7, 0, 14, 11, 3} - type LayerCacheEntry struct { frames []*ebiten.Image compositeMode ebiten.CompositeMode @@ -58,6 +55,7 @@ type AnimatedEntity struct { //frameLocations []d2common.Rectangle object *d2datadict.ObjectLookupRecord layerCache []LayerCacheEntry + drawOrder [][]d2enum.CompositeType } // CreateAnimatedEntity creates an instance of AnimatedEntity @@ -68,6 +66,7 @@ func CreateAnimatedEntity(x, y int32, object *d2datadict.ObjectLookupRecord, fil token: object.Token, object: object, palette: palette, + layerCache: make([]LayerCacheEntry, d2enum.CompositeTypeMax), //frameLocations: []d2common.Rectangle{}, } result.dccLayers = make(map[string]d2dcc.DCC) @@ -84,6 +83,9 @@ func CreateAnimatedEntity(x, y int32, object *d2datadict.ObjectLookupRecord, fil func (v *AnimatedEntity) SetMode(animationMode, weaponClass string, direction int) { cofPath := fmt.Sprintf("%s/%s/COF/%s%s%s.COF", v.base, v.token, v.token, animationMode, weaponClass) v.Cof = d2cof.LoadCOF(cofPath, v.fileProvider) + if v.Cof.NumberOfDirections == 0 || v.Cof.NumberOfLayers == 0 || v.Cof.FramesPerDirection == 0 { + return + } v.animationMode = animationMode v.weaponClass = weaponClass v.direction = direction @@ -163,12 +165,16 @@ func (v *AnimatedEntity) Render(target *ebiten.Image, offsetX, offsetY int) { } } } + localX := (v.subcellX - v.subcellY) * 16 localY := ((v.subcellX + v.subcellY) * 8) - 5 - for layerIdx := range v.layerCache { + if v.drawOrder == nil { + return + } + for _, layerIdx := range v.drawOrder[v.currentFrame] { if v.currentFrame < 0 || v.layerCache[layerIdx].frames == nil || v.currentFrame >= len(v.layerCache[layerIdx].frames) || v.layerCache[layerIdx].frames[v.currentFrame] == nil { - return + continue } opts := &ebiten.DrawImageOptions{} x := float64(v.offsetX) + float64(offsetX) + localX + float64(v.layerCache[layerIdx].offsetX) @@ -193,31 +199,48 @@ func (v *AnimatedEntity) updateFrameCache() { v.framesToAnimate = animationData.FramesPerDirection v.lastFrameTime = d2helper.Now() - v.layerCache = make([]LayerCacheEntry, len(v.Cof.CofLayers)) - for layerIdx := range v.layerCache { - v.layerCache[layerIdx].frames = make([]*ebiten.Image, v.framesToAnimate) + v.drawOrder = make([][]d2enum.CompositeType, v.framesToAnimate) + + var dccDirection int + switch v.Cof.NumberOfDirections { + case 4: + dccDirection = d2dcc.CofToDir4[v.direction] + case 8: + dccDirection = d2dcc.CofToDir8[v.direction] + case 16: + dccDirection = d2dcc.CofToDir16[v.direction] + case 32: + dccDirection = d2dcc.CofToDir32[v.direction] + default: + dccDirection = 0 + } + + for frame := 0; frame < v.framesToAnimate; frame++ { + v.drawOrder[frame] = v.Cof.Priority[v.direction][frame] } for cofLayerIdx := range v.Cof.CofLayers { - layerName := DccLayerNames[v.Cof.CofLayers[cofLayerIdx].Type] + layerType := v.Cof.CofLayers[cofLayerIdx].Type + layerName := DccLayerNames[layerType] dccLayer := v.dccLayers[layerName] if !dccLayer.IsValid() { continue } + v.layerCache[layerType].frames = make([]*ebiten.Image, v.framesToAnimate) minX := int32(10000) minY := int32(10000) maxX := int32(-10000) maxY := int32(-10000) - for frameIdx := range dccLayer.Directions[v.direction].Frames { - minX = d2helper.MinInt32(minX, int32(dccLayer.Directions[v.direction].Frames[frameIdx].Box.Left)) - minY = d2helper.MinInt32(minY, int32(dccLayer.Directions[v.direction].Frames[frameIdx].Box.Top)) - maxX = d2helper.MaxInt32(maxX, int32(dccLayer.Directions[v.direction].Frames[frameIdx].Box.Right())) - maxY = d2helper.MaxInt32(maxY, int32(dccLayer.Directions[v.direction].Frames[frameIdx].Box.Bottom())) + for frameIdx := range dccLayer.Directions[dccDirection].Frames { + minX = d2helper.MinInt32(minX, int32(dccLayer.Directions[dccDirection].Frames[frameIdx].Box.Left)) + minY = d2helper.MinInt32(minY, int32(dccLayer.Directions[dccDirection].Frames[frameIdx].Box.Top)) + maxX = d2helper.MaxInt32(maxX, int32(dccLayer.Directions[dccDirection].Frames[frameIdx].Box.Right())) + maxY = d2helper.MaxInt32(maxY, int32(dccLayer.Directions[dccDirection].Frames[frameIdx].Box.Bottom())) } - v.layerCache[cofLayerIdx].offsetX = minX - v.layerCache[cofLayerIdx].offsetY = minY + v.layerCache[layerType].offsetX = minX + v.layerCache[layerType].offsetY = minY actualWidth := maxX - minX actualHeight := maxY - minY @@ -237,7 +260,7 @@ func (v *AnimatedEntity) updateFrameCache() { case d2enum.DrawEffectPctTransparency75: transparency = byte(192) case d2enum.DrawEffectModulate: - v.layerCache[cofLayerIdx].compositeMode = ebiten.CompositeModeLighter + v.layerCache[layerType].compositeMode = ebiten.CompositeModeLighter case d2enum.DrawEffectBurn: // Flies in tal rasha's tomb use this case d2enum.DrawEffectNormal: @@ -250,29 +273,29 @@ func (v *AnimatedEntity) updateFrameCache() { for i := 0; i < int(actualWidth*actualHeight); i++ { pixels[(i*4)+3] = 0 } - if animationIdx >= len(dccLayer.Directions[v.direction].Frames) { + if animationIdx >= len(dccLayer.Directions[dccDirection].Frames) { log.Printf("Invalid animation index of %d for animated entity", animationIdx) continue } - frame := dccLayer.Directions[v.direction].Frames[animationIdx] - for y := 0; y < dccLayer.Directions[v.direction].Box.Height; y++ { - for x := 0; x < dccLayer.Directions[v.direction].Box.Width; x++ { - paletteIndex := frame.PixelData[x+(y*dccLayer.Directions[v.direction].Box.Width)] + frame := dccLayer.Directions[dccDirection].Frames[animationIdx] + for y := 0; y < dccLayer.Directions[dccDirection].Box.Height; y++ { + for x := 0; x < dccLayer.Directions[dccDirection].Box.Width; x++ { + paletteIndex := frame.PixelData[x+(y*dccLayer.Directions[dccDirection].Box.Width)] if paletteIndex == 0 { continue } color := d2datadict.Palettes[v.palette].Colors[paletteIndex] - actualX := (x + dccLayer.Directions[v.direction].Box.Left) - int(minX) - actualY := (y + dccLayer.Directions[v.direction].Box.Top) - int(minY) + actualX := (x + dccLayer.Directions[dccDirection].Box.Left) - int(minX) + actualY := (y + dccLayer.Directions[dccDirection].Box.Top) - int(minY) pixels[(actualX*4)+(actualY*int(actualWidth)*4)] = color.R pixels[(actualX*4)+(actualY*int(actualWidth)*4)+1] = color.G pixels[(actualX*4)+(actualY*int(actualWidth)*4)+2] = color.B pixels[(actualX*4)+(actualY*int(actualWidth)*4)+3] = transparency } } - v.layerCache[cofLayerIdx].frames[animationIdx], _ = ebiten.NewImage(int(actualWidth), int(actualHeight), ebiten.FilterNearest) - _ = v.layerCache[cofLayerIdx].frames[animationIdx].ReplacePixels(pixels) + v.layerCache[layerType].frames[animationIdx], _ = ebiten.NewImage(int(actualWidth), int(actualHeight), ebiten.FilterNearest) + _ = v.layerCache[layerType].frames[animationIdx].ReplacePixels(pixels) } } } diff --git a/go.mod b/go.mod index 267be90c..492a5357 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/OpenDiablo2/OpenDiablo2 go 1.12 require ( - github.com/OpenDiablo2/D2Shared v0.0.0-20191124224548-cf6a3b59eadb + github.com/OpenDiablo2/D2Shared v0.0.0-20191127010353-6abef1f4c7ae github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d // indirect github.com/hajimehoshi/ebiten v1.11.0-alpha.0.20191121152720-3df198f68eea diff --git a/go.sum b/go.sum index 69c64f5a..567afc5b 100644 --- a/go.sum +++ b/go.sum @@ -2,12 +2,8 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/JoshVarga/blast v0.0.0-20180421040937-681c804fb9f0 h1:tDnuU0igiBiQFjsvq1Bi7DpoUjqI76VVvW045vpeFeM= github.com/JoshVarga/blast v0.0.0-20180421040937-681c804fb9f0/go.mod h1:h/5OEGj4G+fpYxluLjSMZbFY011ZxAntO98nCl8mrCs= -github.com/OpenDiablo2/D2Shared v0.0.0-20191124053203-a445d5d8cbe0 h1:AvMIpYAhSC3xpDwAHHXUt8LN75oeH3sHrzXys/DgJU4= -github.com/OpenDiablo2/D2Shared v0.0.0-20191124053203-a445d5d8cbe0/go.mod h1:zRNOUiglwakbufN8EsNWqLLDHsZoQDA6/dI2GIu2nnU= -github.com/OpenDiablo2/D2Shared v0.0.0-20191124133124-e4652c013e40 h1:NjjCVr+4GCH9NWyfCcwYI5QrYiW3DiOhPxlvEqY2h/g= -github.com/OpenDiablo2/D2Shared v0.0.0-20191124133124-e4652c013e40/go.mod h1:zRNOUiglwakbufN8EsNWqLLDHsZoQDA6/dI2GIu2nnU= -github.com/OpenDiablo2/D2Shared v0.0.0-20191124224548-cf6a3b59eadb h1:4W+jWE86ZuDgHTg/RhWEcUoK7UpRJC/hEnlQ9/SEep4= -github.com/OpenDiablo2/D2Shared v0.0.0-20191124224548-cf6a3b59eadb/go.mod h1:fG8OQsAulB/t/RC9QeUjYAAEXZWY5kHvgWXmMgLhRwI= +github.com/OpenDiablo2/D2Shared v0.0.0-20191127010353-6abef1f4c7ae h1:yoY87JVtJIEtCRjQm8tnvL24n7aXbhz7seKkAJfhKU4= +github.com/OpenDiablo2/D2Shared v0.0.0-20191127010353-6abef1f4c7ae/go.mod h1:fG8OQsAulB/t/RC9QeUjYAAEXZWY5kHvgWXmMgLhRwI= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E=