Added pathfinding and (buggy) collision data. (#285)

This commit is contained in:
Tim Sarbin 2020-02-02 12:46:19 -05:00 committed by GitHub
parent 935789dccf
commit 3412c4338c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 225 additions and 29 deletions

View File

@ -179,6 +179,7 @@ func LoadDS1(fileData []byte) DS1 {
newObject.X = br.GetInt32()
newObject.Y = br.GetInt32()
newObject.Flags = br.GetInt32()
//TODO: There's a crash here, we aren't loading this data right....
newObject.Lookup = d2datadict.LookupObject(int(ds1.Act), int(newObject.Type), int(newObject.Id))
if newObject.Lookup != nil && newObject.Lookup.ObjectsTxtId != -1 {
newObject.ObjectInfo = d2datadict.Objects[newObject.Lookup.ObjectsTxtId]

View File

@ -1,6 +1,7 @@
package d2map
import (
"github.com/beefsack/go-astar"
"math"
"math/rand"
@ -25,6 +26,7 @@ type AnimatedEntity struct {
TargetY float64
action int32
repetitions int
path []astar.Pather
composite *d2asset.Composite
}
@ -41,6 +43,7 @@ func CreateAnimatedEntity(x, y int32, object *d2datadict.ObjectLookupRecord, pal
entity.LocationY = float64(y)
entity.TargetX = entity.LocationX
entity.TargetY = entity.LocationY
entity.path = []astar.Pather{}
entity.TileX = int(entity.LocationX / 5)
entity.TileY = int(entity.LocationY / 5)
@ -69,6 +72,9 @@ func (v AnimatedEntity) Wait() bool {
return v.composite.GetPlayedCount() > v.repetitions
}
func (v *AnimatedEntity) SetPath(path []astar.Pather) {
v.path = path
}
// Render draws this animated entity onto the target
func (v *AnimatedEntity) Render(target d2render.Surface) {
target.PushTranslation(
@ -120,27 +126,44 @@ func (v *AnimatedEntity) Step(tickTime float64) {
v.TileX = int(v.LocationX / 5)
v.TileY = int(v.LocationY / 5)
if v.LocationX == v.TargetX && v.LocationY == v.TargetY {
v.repetitions = 3 + rand.Intn(5)
newAnimationMode := d2enum.AnimationModeObjectNeutral
// TODO: Figure out what 1-3 are for, 4 is correct.
switch v.action {
case 1:
newAnimationMode = d2enum.AnimationModeMonsterNeutral
case 2:
newAnimationMode = d2enum.AnimationModeMonsterNeutral
case 3:
newAnimationMode = d2enum.AnimationModeMonsterNeutral
case 4:
newAnimationMode = d2enum.AnimationModeMonsterSkill1
v.repetitions = 0
}
v.composite.ResetPlayedCount()
if v.animationMode != newAnimationMode.String() {
v.SetMode(newAnimationMode.String(), v.weaponClass, v.direction)
}
if (v.LocationX != v.TargetX) || (v.LocationY != v.TargetY) {
return
}
if len(v.path) > 0 {
v.SetTarget(v.path[0].(*PathTile).X * 5, v.path[0].(*PathTile).Y * 5, 1)
if len(v.path) > 1 {
v.path = v.path[1:]
} else {
v.path = []astar.Pather{}
}
return
}
v.repetitions = 3 + rand.Intn(5)
newAnimationMode := d2enum.AnimationModeObjectNeutral
// TODO: Figure out what 1-3 are for, 4 is correct.
switch v.action {
case 1:
newAnimationMode = d2enum.AnimationModeMonsterNeutral
case 2:
newAnimationMode = d2enum.AnimationModeMonsterNeutral
case 3:
newAnimationMode = d2enum.AnimationModeMonsterNeutral
case 4:
newAnimationMode = d2enum.AnimationModeMonsterSkill1
v.repetitions = 0
}
v.composite.ResetPlayedCount()
if v.animationMode != newAnimationMode.String() {
v.SetMode(newAnimationMode.String(), v.weaponClass, v.direction)
}
}
func (v *AnimatedEntity) HasPathFinding() bool {
return len(v.path) > 0
}
// SetTarget sets target coordinates and changes animation based on proximity and direction

View File

@ -1,6 +1,8 @@
package d2map
import (
"github.com/beefsack/go-astar"
"math"
"strings"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2gamestate"
@ -98,15 +100,20 @@ func (m *MapEngine) GenerateAct1Overworld() {
if strings.Contains(region.regionPath, "E1") {
region, entities := loadRegion(m.gameState.Seed, region.tileRect.Width-1, 0, d2enum.RegionAct1Town, 2, -1)
m.regions = append(m.regions, region)
m.AppendRegion(region)
m.entities = append(m.entities, entities...)
} else if strings.Contains(region.regionPath, "S1") {
region, entities := loadRegion(m.gameState.Seed, 0, region.tileRect.Height-1, d2enum.RegionAct1Town, 3, -1)
m.regions = append(m.regions, region)
m.AppendRegion(region)
m.entities = append(m.entities, entities...)
}
}
func (m *MapEngine) AppendRegion(region *MapRegion) {
// TODO: Stitch together region.walkableArea
m.regions = append(m.regions, region)
}
func (m *MapEngine) GetRegionAtTile(x, y int) *MapRegion {
for _, region := range m.regions {
if region.tileRect.IsInRect(x, y) {
@ -143,3 +150,25 @@ func (m *MapEngine) Render(target d2render.Surface) {
}
}
}
func (m *MapEngine) PathFind(startX, startY, endX, endY float64) (path []astar.Pather, distance float64, found bool){
startTileX := int(math.Floor(startX))
startTileY := int(math.Floor(startY))
startSubtileX := int((startX - float64(int(startX))) * 5)
startSubtileY := int((startY - float64(int(startY))) * 5)
startRegion := m.GetRegionAtTile(startTileX, startTileY)
startNode := &startRegion.walkableArea[startSubtileY + ((startTileY - startRegion.tileRect.Top) * 5)][startSubtileX + ((startTileX - startRegion.tileRect.Left) * 5)]
endTileX := int(math.Floor(endX))
endTileY := int(math.Floor(endY))
endSubtileX := int((endX - float64(int(endX))) * 5)
endSubtileY := int((endY - float64(int(endY))) * 5)
endRegion := m.GetRegionAtTile(endTileX, endTileY)
endNode := &endRegion.walkableArea[endSubtileY + ((endTileY - endRegion.tileRect.Top) * 5)][endSubtileX + ((endTileX - endRegion.tileRect.Left) * 5)]
path, distance, found = astar.Path(endNode, startNode)
if path != nil {
path = path[1:]
}
return
}

View File

@ -43,9 +43,9 @@ func CreateHero(x, y int32, direction int, heroType d2enum.Hero, equipment d2inv
}
func (v *Hero) Advance(tickTime float64) {
// TODO: Pathfinding
if v.AnimatedEntity.LocationX != v.AnimatedEntity.TargetX ||
v.AnimatedEntity.LocationY != v.AnimatedEntity.TargetY {
v.AnimatedEntity.LocationY != v.AnimatedEntity.TargetY ||
v.AnimatedEntity.HasPathFinding(){
v.AnimatedEntity.Step(tickTime)
}

View File

@ -16,8 +16,64 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
"github.com/beefsack/go-astar"
)
type PathTile struct {
Walkable bool
Up, Down, Left, Right, UpLeft, UpRight, DownLeft, DownRight *PathTile
X, Y float64
}
func (t *PathTile) PathNeighbors() []astar.Pather {
result := make([]astar.Pather, 0)
if t.Up != nil {
result = append(result, t.Up)
}
if t.Right != nil {
result = append(result, t.Right)
}
if t.Down != nil {
result = append(result, t.Down)
}
if t.Left != nil {
result = append(result, t.Left)
}
if t.UpLeft != nil {
result = append(result, t.UpLeft)
}
if t.UpRight != nil {
result = append(result, t.UpRight)
}
if t.DownLeft != nil {
result = append(result, t.DownLeft)
}
if t.DownRight != nil {
result = append(result, t.DownRight)
}
return result
}
func (t *PathTile) PathNeighborCost(to astar.Pather) float64 {
return 1 // No cost specifics currently...
}
func (t *PathTile) PathEstimatedCost(to astar.Pather) float64 {
toT := to.(*PathTile)
absX := toT.X - t.X
if absX < 0 {
absX = -absX
}
absY := toT.Y - t.Y
if absY < 0 {
absY = -absY
}
r := absX + absY
return r
}
type MapRegion struct {
tileRect d2common.Rectangle
regionPath string
@ -32,6 +88,7 @@ type MapRegion struct {
seed int64
currentFrame int
lastFrameTime float64
walkableArea [][]PathTile
}
func loadRegion(seed int64, tileOffsetX, tileOffsetY int, levelType d2enum.RegionIdType, levelPreset int, fileIndex int) (*MapRegion, []MapEntity) {
@ -91,10 +148,68 @@ func loadRegion(seed int64, tileOffsetX, tileOffsetY int, levelType d2enum.Regio
entities := region.loadEntities()
region.loadSpecials()
region.generateTileCache()
region.generateWalkableMatrix()
return region, entities
}
func (mr *MapRegion) generateWalkableMatrix() {
mr.walkableArea = make([][]PathTile, mr.tileRect.Height * 5)
for y := 0; y < mr.tileRect.Height * 5; y++ {
mr.walkableArea[y] = make([]PathTile, mr.tileRect.Width * 5)
ty := int(float64(y) / 5.0)
for x := 0; x < mr.tileRect.Width * 5; x++ {
tx := int(float64(x) / 5.0)
tile := mr.GetTile(tx, ty)
isBlocked := false
for _, floor := range tile.Floors {
tileData := mr.GetTileData(int32(floor.Style), int32(floor.Sequence), d2enum.Floor)
tileSubAttrs := &d2dt1.SubTileFlags{}
if tileData != nil {
tileSubAttrs = tileData.GetSubTileFlags(x % 5, 4 - (y % 5))
}
isBlocked = isBlocked || tileSubAttrs.BlockWalk
if isBlocked {
break
}
}
if !isBlocked {
for _, wall := range tile.Walls {
tileData := mr.GetTileData(int32(wall.Style), int32(wall.Sequence), d2enum.Floor)
tileSubAttrs := &d2dt1.SubTileFlags{}
if tileData != nil {
tileSubAttrs = tileData.GetSubTileFlags(x % 5, 4 - (y % 5))
}
isBlocked = isBlocked || tileSubAttrs.BlockPlayerWalk
if isBlocked {
break
}
}
}
mr.walkableArea[y][x] = PathTile{
Walkable: !isBlocked,
X: float64(x) / 5.0,
Y: float64(y) / 5.0,
}
if !isBlocked && y > 0 && mr.walkableArea[y-1][x].Walkable {
mr.walkableArea[y][x].Up = &mr.walkableArea[y-1][x]
mr.walkableArea[y-1][x].Down = &mr.walkableArea[y][x]
}
if !isBlocked && x > 0 && mr.walkableArea[y][x-1].Walkable {
mr.walkableArea[y][x].Left = &mr.walkableArea[y][x-1]
mr.walkableArea[y][x-1].Right = &mr.walkableArea[y][x]
}
if !isBlocked && x > 0 && y > 0 && mr.walkableArea[y-1][x-1].Walkable {
mr.walkableArea[y][x].UpLeft = &mr.walkableArea[y-1][x-1]
mr.walkableArea[y-1][x-1].DownRight = &mr.walkableArea[y][x]
}
if !isBlocked && y > 0 && x < (mr.tileRect.Width * 5) && mr.walkableArea[y-1][x+1].Walkable {
mr.walkableArea[y][x].UpRight = &mr.walkableArea[y-1][x+1]
mr.walkableArea[y-1][x+1].DownLeft = &mr.walkableArea[y][x]
}
}
}
}
func (mr *MapRegion) GetTileRect() d2common.Rectangle {
return mr.tileRect
}
@ -406,6 +521,7 @@ func (mr *MapRegion) renderTileDebug(x, y int, debugVisLevel int, viewport *View
}
subTileColor := color.RGBA{R: 80, G: 80, B: 255, A: 50}
tileColor := color.RGBA{R: 255, G: 255, B: 255, A: 100}
tileCollisionColor := color.RGBA{R: 128, G:0, B:0, A:100}
screenX1, screenY1 := viewport.WorldToScreen(float64(x), float64(y))
screenX2, screenY2 := viewport.WorldToScreen(float64(x+1), float64(y))
@ -440,6 +556,19 @@ func (mr *MapRegion) renderTileDebug(x, y int, debugVisLevel int, viewport *View
target.DrawText("f: %v-%v", floor.Style, floor.Sequence)
target.Pop()
}
for yy := 0; yy < 5; yy++ {
for xx := 0; xx < 5; xx++ {
isoX := (xx - yy) * 16
isoY := (xx + yy) * 8
target.PushTranslation(isoX-3, isoY+4)
var walkableArea = mr.walkableArea[yy + (ay * 5)][xx + (ax * 5)]
if !walkableArea.Walkable {
target.DrawRect(5, 5, tileCollisionColor)
}
target.Pop()
}
}
}
}
}

View File

@ -48,10 +48,16 @@ func (g *GameControls) OnKeyDown(event d2input.KeyEvent) bool {
func (g *GameControls) OnMouseButtonDown(event d2input.MouseEvent) bool {
if event.Button == d2input.MouseButtonLeft {
px, py := g.mapEngine.ScreenToWorld(event.X, event.Y)
g.hero.AnimatedEntity.SetTarget(px*5, py*5, 1)
px = float64(int(px * 10)) / 10.0
py = float64(int(py * 10)) / 10.0
heroPosX := g.hero.AnimatedEntity.LocationX / 5.0
heroPosY := g.hero.AnimatedEntity.LocationY / 5.0
path, _, found := g.mapEngine.PathFind(heroPosX, heroPosY, px, py)
if found {
g.hero.AnimatedEntity.SetPath(path)
}
return true
}
return false
}

1
go.mod
View File

@ -6,6 +6,7 @@ require (
github.com/JoshVarga/blast v0.0.0-20180421040937-681c804fb9f0
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d // indirect
github.com/beefsack/go-astar v0.0.0-20171024231011-f324bbb0d6f7
github.com/go-restruct/restruct v0.0.0-20191227155143-5734170a48a1
github.com/hajimehoshi/ebiten v1.11.0-alpha.2.0.20200102072751-e66f1fb71e2e
github.com/stretchr/testify v1.4.0

11
go.sum
View File

@ -6,6 +6,8 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafo
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/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/beefsack/go-astar v0.0.0-20171024231011-f324bbb0d6f7 h1:dX/NcR4V4sY+xio5sjMUUaBfmXz/7UH4R7S//oVPqhY=
github.com/beefsack/go-astar v0.0.0-20171024231011-f324bbb0d6f7/go.mod h1:Cu3t5VeqE8kXjUBeNXWQprfuaP5UCIc5ggGjgMx9KFc=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0=
@ -18,8 +20,6 @@ github.com/gofrs/flock v0.7.1 h1:DP+LD/t0njgoPBvT5MJLeliUIVQR03hiKR6vezdwHlc=
github.com/gofrs/flock v0.7.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/hajimehoshi/bitmapfont v1.2.0/go.mod h1:h9QrPk6Ktb2neObTlAbma6Ini1xgMjbJ3w7ysmD7IOU=
github.com/hajimehoshi/ebiten v1.10.3 h1:ywrEsXclsjrdKIhYYrmy/yxLFCd1y4efGVsnFRl8OLU=
github.com/hajimehoshi/ebiten v1.10.3/go.mod h1:i9dIEUf5/MuPtbK1/wHR0PB7ZtqhjOxxg+U1xfxapcY=
github.com/hajimehoshi/ebiten v1.11.0-alpha.2.0.20200102072751-e66f1fb71e2e h1:NRGAeXOSMrAo0f4GPaUoiF61eo0wWrfgONPSTZA6Zhg=
github.com/hajimehoshi/ebiten v1.11.0-alpha.2.0.20200102072751-e66f1fb71e2e/go.mod h1:0SLvfr8iI2NxzpNB/olBM+dLN9Ur5a9szG13wOgQ0nQ=
github.com/hajimehoshi/go-mp3 v0.2.1/go.mod h1:Rr+2P46iH6PwTPVgSsEwBkon0CK5DxCAeX/Rp65DCTE=
@ -29,8 +29,10 @@ github.com/hajimehoshi/oto v0.5.4/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2h
github.com/jakecoffman/cp v0.1.0/go.mod h1:a3xPx9N8RyFAACD644t2dj/nK4SuLg1v+jL61m2yVo4=
github.com/jfreymuth/oggvorbis v1.0.0/go.mod h1:abe6F9QRjuU9l+2jek3gj46lu40N4qlYxh2grqkLEDM=
github.com/jfreymuth/vorbis v1.0.0/go.mod h1:8zy3lUAm9K/rJJk223RKy6vjCZTWC61NA2QD06bfOE0=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
@ -44,15 +46,18 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136 h1:A1gGSx58LAGVHUUsOf7IiR0u8Xb6W51gRwfDBhkdcaw=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190703141733-d6a02ce849c9/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 h1:hVwzHzIUGRjiF7EcUjqNxk3NCfkPxbDKRdnNE1Rpg0U=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mobile v0.0.0-20191025110607-73ccc5ba0426/go.mod h1:p895TfNkDgPEmEQrNiOtIl3j98d/tGU95djDj7NfyjQ=
golang.org/x/mobile v0.0.0-20191115022231-f0c40035f2ba h1:NVszahdZPQTROdO0F5gnXdZhGl2lXFb9w7Ek1F2Pbmk=
golang.org/x/mobile v0.0.0-20191115022231-f0c40035f2ba/go.mod h1:p895TfNkDgPEmEQrNiOtIl3j98d/tGU95djDj7NfyjQ=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@ -64,6 +69,7 @@ golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191115151921-52ab43148777 h1:wejkGHRTr38uaKRqECZlsCsJ1/TGxIyFbH32x5zUdu4=
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
@ -74,6 +80,7 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=