mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2024-09-27 13:46:00 -04:00
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
This commit is contained in:
parent
4d655416c2
commit
51d78bfcbf
@ -1,23 +1,24 @@
|
|||||||
package d2scene
|
package d2scene
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/OpenDiablo2/D2Shared/d2data/d2dc6"
|
|
||||||
"image/color"
|
"image/color"
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/OpenDiablo2/D2Shared/d2data/d2dc6"
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten/ebitenutil"
|
"github.com/hajimehoshi/ebiten/ebitenutil"
|
||||||
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2audio"
|
|
||||||
"github.com/OpenDiablo2/D2Shared/d2common"
|
"github.com/OpenDiablo2/D2Shared/d2common"
|
||||||
"github.com/OpenDiablo2/D2Shared/d2common/d2enum"
|
"github.com/OpenDiablo2/D2Shared/d2common/d2enum"
|
||||||
"github.com/OpenDiablo2/D2Shared/d2common/d2interface"
|
"github.com/OpenDiablo2/D2Shared/d2common/d2interface"
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2corecommon/d2coreinterface"
|
|
||||||
"github.com/OpenDiablo2/D2Shared/d2common/d2resource"
|
"github.com/OpenDiablo2/D2Shared/d2common/d2resource"
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2core"
|
|
||||||
"github.com/OpenDiablo2/D2Shared/d2data/d2datadict"
|
"github.com/OpenDiablo2/D2Shared/d2data/d2datadict"
|
||||||
dh "github.com/OpenDiablo2/D2Shared/d2helper"
|
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"
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2render/d2ui"
|
"github.com/OpenDiablo2/OpenDiablo2/d2render/d2ui"
|
||||||
"github.com/hajimehoshi/ebiten"
|
"github.com/hajimehoshi/ebiten"
|
||||||
@ -189,7 +190,7 @@ func (v *CharacterSelect) updateCharacterBoxes() {
|
|||||||
v.characterImage[i] = d2core.CreateHero(
|
v.characterImage[i] = d2core.CreateHero(
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
5,
|
0,
|
||||||
v.gameStates[idx].HeroType,
|
v.gameStates[idx].HeroType,
|
||||||
d2core.HeroObjects[v.gameStates[idx].HeroType],
|
d2core.HeroObjects[v.gameStates[idx].HeroType],
|
||||||
v.fileProvider,
|
v.fileProvider,
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
package d2scene
|
package d2scene
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/OpenDiablo2/D2Shared/d2data/d2dc6"
|
|
||||||
"image/color"
|
"image/color"
|
||||||
|
|
||||||
|
"github.com/OpenDiablo2/D2Shared/d2data/d2dc6"
|
||||||
|
|
||||||
"github.com/OpenDiablo2/D2Shared/d2helper"
|
"github.com/OpenDiablo2/D2Shared/d2helper"
|
||||||
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2render/d2mapengine"
|
"github.com/OpenDiablo2/OpenDiablo2/d2render/d2mapengine"
|
||||||
@ -111,8 +112,7 @@ func (v *Game) Update(tickTime float64) {
|
|||||||
py,
|
py,
|
||||||
)
|
)
|
||||||
|
|
||||||
directionIndex := int((float64(angle) / 360.0) * 16.0)
|
newDirection := int((float64(angle) / 360.0) * 16.0)
|
||||||
newDirection := d2render.DirectionLookup[directionIndex]
|
|
||||||
if newDirection != v.mapEngine.Hero.AnimatedEntity.GetDirection() {
|
if newDirection != v.mapEngine.Hero.AnimatedEntity.GetDirection() {
|
||||||
v.mapEngine.Hero.AnimatedEntity.SetMode(d2enum.AnimationModePlayerTownNeutral.String(), v.mapEngine.Hero.Equipment.RightHand.GetWeaponClass(), newDirection)
|
v.mapEngine.Hero.AnimatedEntity.SetMode(d2enum.AnimationModePlayerTownNeutral.String(), v.mapEngine.Hero.Equipment.RightHand.GetWeaponClass(), newDirection)
|
||||||
}
|
}
|
||||||
|
@ -25,9 +25,6 @@ import (
|
|||||||
|
|
||||||
var DccLayerNames = []string{"HD", "TR", "LG", "RA", "LA", "RH", "LH", "SH", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8"}
|
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 {
|
type LayerCacheEntry struct {
|
||||||
frames []*ebiten.Image
|
frames []*ebiten.Image
|
||||||
compositeMode ebiten.CompositeMode
|
compositeMode ebiten.CompositeMode
|
||||||
@ -58,6 +55,7 @@ type AnimatedEntity struct {
|
|||||||
//frameLocations []d2common.Rectangle
|
//frameLocations []d2common.Rectangle
|
||||||
object *d2datadict.ObjectLookupRecord
|
object *d2datadict.ObjectLookupRecord
|
||||||
layerCache []LayerCacheEntry
|
layerCache []LayerCacheEntry
|
||||||
|
drawOrder [][]d2enum.CompositeType
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateAnimatedEntity creates an instance of AnimatedEntity
|
// CreateAnimatedEntity creates an instance of AnimatedEntity
|
||||||
@ -68,6 +66,7 @@ func CreateAnimatedEntity(x, y int32, object *d2datadict.ObjectLookupRecord, fil
|
|||||||
token: object.Token,
|
token: object.Token,
|
||||||
object: object,
|
object: object,
|
||||||
palette: palette,
|
palette: palette,
|
||||||
|
layerCache: make([]LayerCacheEntry, d2enum.CompositeTypeMax),
|
||||||
//frameLocations: []d2common.Rectangle{},
|
//frameLocations: []d2common.Rectangle{},
|
||||||
}
|
}
|
||||||
result.dccLayers = make(map[string]d2dcc.DCC)
|
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) {
|
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)
|
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)
|
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.animationMode = animationMode
|
||||||
v.weaponClass = weaponClass
|
v.weaponClass = weaponClass
|
||||||
v.direction = direction
|
v.direction = direction
|
||||||
@ -163,12 +165,16 @@ func (v *AnimatedEntity) Render(target *ebiten.Image, offsetX, offsetY int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
localX := (v.subcellX - v.subcellY) * 16
|
localX := (v.subcellX - v.subcellY) * 16
|
||||||
localY := ((v.subcellX + v.subcellY) * 8) - 5
|
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 {
|
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{}
|
opts := &ebiten.DrawImageOptions{}
|
||||||
x := float64(v.offsetX) + float64(offsetX) + localX + float64(v.layerCache[layerIdx].offsetX)
|
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.framesToAnimate = animationData.FramesPerDirection
|
||||||
v.lastFrameTime = d2helper.Now()
|
v.lastFrameTime = d2helper.Now()
|
||||||
|
|
||||||
v.layerCache = make([]LayerCacheEntry, len(v.Cof.CofLayers))
|
v.drawOrder = make([][]d2enum.CompositeType, v.framesToAnimate)
|
||||||
for layerIdx := range v.layerCache {
|
|
||||||
v.layerCache[layerIdx].frames = make([]*ebiten.Image, 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 {
|
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]
|
dccLayer := v.dccLayers[layerName]
|
||||||
if !dccLayer.IsValid() {
|
if !dccLayer.IsValid() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
v.layerCache[layerType].frames = make([]*ebiten.Image, v.framesToAnimate)
|
||||||
|
|
||||||
minX := int32(10000)
|
minX := int32(10000)
|
||||||
minY := int32(10000)
|
minY := int32(10000)
|
||||||
maxX := int32(-10000)
|
maxX := int32(-10000)
|
||||||
maxY := int32(-10000)
|
maxY := int32(-10000)
|
||||||
for frameIdx := range dccLayer.Directions[v.direction].Frames {
|
for frameIdx := range dccLayer.Directions[dccDirection].Frames {
|
||||||
minX = d2helper.MinInt32(minX, int32(dccLayer.Directions[v.direction].Frames[frameIdx].Box.Left))
|
minX = d2helper.MinInt32(minX, int32(dccLayer.Directions[dccDirection].Frames[frameIdx].Box.Left))
|
||||||
minY = d2helper.MinInt32(minY, int32(dccLayer.Directions[v.direction].Frames[frameIdx].Box.Top))
|
minY = d2helper.MinInt32(minY, int32(dccLayer.Directions[dccDirection].Frames[frameIdx].Box.Top))
|
||||||
maxX = d2helper.MaxInt32(maxX, int32(dccLayer.Directions[v.direction].Frames[frameIdx].Box.Right()))
|
maxX = d2helper.MaxInt32(maxX, int32(dccLayer.Directions[dccDirection].Frames[frameIdx].Box.Right()))
|
||||||
maxY = d2helper.MaxInt32(maxY, int32(dccLayer.Directions[v.direction].Frames[frameIdx].Box.Bottom()))
|
maxY = d2helper.MaxInt32(maxY, int32(dccLayer.Directions[dccDirection].Frames[frameIdx].Box.Bottom()))
|
||||||
}
|
}
|
||||||
|
|
||||||
v.layerCache[cofLayerIdx].offsetX = minX
|
v.layerCache[layerType].offsetX = minX
|
||||||
v.layerCache[cofLayerIdx].offsetY = minY
|
v.layerCache[layerType].offsetY = minY
|
||||||
actualWidth := maxX - minX
|
actualWidth := maxX - minX
|
||||||
actualHeight := maxY - minY
|
actualHeight := maxY - minY
|
||||||
|
|
||||||
@ -237,7 +260,7 @@ func (v *AnimatedEntity) updateFrameCache() {
|
|||||||
case d2enum.DrawEffectPctTransparency75:
|
case d2enum.DrawEffectPctTransparency75:
|
||||||
transparency = byte(192)
|
transparency = byte(192)
|
||||||
case d2enum.DrawEffectModulate:
|
case d2enum.DrawEffectModulate:
|
||||||
v.layerCache[cofLayerIdx].compositeMode = ebiten.CompositeModeLighter
|
v.layerCache[layerType].compositeMode = ebiten.CompositeModeLighter
|
||||||
case d2enum.DrawEffectBurn:
|
case d2enum.DrawEffectBurn:
|
||||||
// Flies in tal rasha's tomb use this
|
// Flies in tal rasha's tomb use this
|
||||||
case d2enum.DrawEffectNormal:
|
case d2enum.DrawEffectNormal:
|
||||||
@ -250,29 +273,29 @@ func (v *AnimatedEntity) updateFrameCache() {
|
|||||||
for i := 0; i < int(actualWidth*actualHeight); i++ {
|
for i := 0; i < int(actualWidth*actualHeight); i++ {
|
||||||
pixels[(i*4)+3] = 0
|
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)
|
log.Printf("Invalid animation index of %d for animated entity", animationIdx)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
frame := dccLayer.Directions[v.direction].Frames[animationIdx]
|
frame := dccLayer.Directions[dccDirection].Frames[animationIdx]
|
||||||
for y := 0; y < dccLayer.Directions[v.direction].Box.Height; y++ {
|
for y := 0; y < dccLayer.Directions[dccDirection].Box.Height; y++ {
|
||||||
for x := 0; x < dccLayer.Directions[v.direction].Box.Width; x++ {
|
for x := 0; x < dccLayer.Directions[dccDirection].Box.Width; x++ {
|
||||||
paletteIndex := frame.PixelData[x+(y*dccLayer.Directions[v.direction].Box.Width)]
|
paletteIndex := frame.PixelData[x+(y*dccLayer.Directions[dccDirection].Box.Width)]
|
||||||
if paletteIndex == 0 {
|
if paletteIndex == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
color := d2datadict.Palettes[v.palette].Colors[paletteIndex]
|
color := d2datadict.Palettes[v.palette].Colors[paletteIndex]
|
||||||
actualX := (x + dccLayer.Directions[v.direction].Box.Left) - int(minX)
|
actualX := (x + dccLayer.Directions[dccDirection].Box.Left) - int(minX)
|
||||||
actualY := (y + dccLayer.Directions[v.direction].Box.Top) - int(minY)
|
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)] = color.R
|
||||||
pixels[(actualX*4)+(actualY*int(actualWidth)*4)+1] = color.G
|
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)+2] = color.B
|
||||||
pixels[(actualX*4)+(actualY*int(actualWidth)*4)+3] = transparency
|
pixels[(actualX*4)+(actualY*int(actualWidth)*4)+3] = transparency
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
v.layerCache[cofLayerIdx].frames[animationIdx], _ = ebiten.NewImage(int(actualWidth), int(actualHeight), ebiten.FilterNearest)
|
v.layerCache[layerType].frames[animationIdx], _ = ebiten.NewImage(int(actualWidth), int(actualHeight), ebiten.FilterNearest)
|
||||||
_ = v.layerCache[cofLayerIdx].frames[animationIdx].ReplacePixels(pixels)
|
_ = v.layerCache[layerType].frames[animationIdx].ReplacePixels(pixels)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2
go.mod
2
go.mod
@ -3,7 +3,7 @@ module github.com/OpenDiablo2/OpenDiablo2
|
|||||||
go 1.12
|
go 1.12
|
||||||
|
|
||||||
require (
|
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/template v0.0.0-20190718012654-fb15b899a751 // indirect
|
||||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d // indirect
|
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d // indirect
|
||||||
github.com/hajimehoshi/ebiten v1.11.0-alpha.0.20191121152720-3df198f68eea
|
github.com/hajimehoshi/ebiten v1.11.0-alpha.0.20191121152720-3df198f68eea
|
||||||
|
8
go.sum
8
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/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 h1:tDnuU0igiBiQFjsvq1Bi7DpoUjqI76VVvW045vpeFeM=
|
||||||
github.com/JoshVarga/blast v0.0.0-20180421040937-681c804fb9f0/go.mod h1:h/5OEGj4G+fpYxluLjSMZbFY011ZxAntO98nCl8mrCs=
|
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-20191127010353-6abef1f4c7ae h1:yoY87JVtJIEtCRjQm8tnvL24n7aXbhz7seKkAJfhKU4=
|
||||||
github.com/OpenDiablo2/D2Shared v0.0.0-20191124053203-a445d5d8cbe0/go.mod h1:zRNOUiglwakbufN8EsNWqLLDHsZoQDA6/dI2GIu2nnU=
|
github.com/OpenDiablo2/D2Shared v0.0.0-20191127010353-6abef1f4c7ae/go.mod h1:fG8OQsAulB/t/RC9QeUjYAAEXZWY5kHvgWXmMgLhRwI=
|
||||||
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/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
|
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/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E=
|
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E=
|
||||||
|
Loading…
Reference in New Issue
Block a user