diff --git a/Common/AnimatedEntity.go b/Common/AnimatedEntity.go new file mode 100644 index 00000000..31e51769 --- /dev/null +++ b/Common/AnimatedEntity.go @@ -0,0 +1,125 @@ +package Common + +import ( + "fmt" + "strings" + "time" + + "github.com/OpenDiablo2/OpenDiablo2/PaletteDefs" + + "github.com/hajimehoshi/ebiten" +) + +type AnimatedEntity struct { + dcc *DCC + cof *Cof + palette PaletteDefs.PaletteType + base string + token string + tr string + animationMode string + weaponClass string + lastFrameTime time.Time + framesToAnimate int + animationSpeed int + direction int + currentFrame int + LocationX float64 + LocationY float64 + frames []*ebiten.Image + frameLocations []Rectangle +} + +func CreateAnimatedEntity(base, token, tr string, palette PaletteDefs.PaletteType) *AnimatedEntity { + result := &AnimatedEntity{ + base: base, + token: token, + tr: tr, + palette: palette, + } + return result +} + +var DirectionLookup = []int{3, 15, 4, 8, 0, 9, 5, 10, 1, 11, 6, 12, 2, 13, 7, 14} + +func (v *AnimatedEntity) SetMode(animationMode, weaponClass string, direction int, provider FileProvider) { + dccPath := fmt.Sprintf("%s/%s/tr/%str%s%s%s.dcc", v.base, v.token, v.token, v.tr, animationMode, weaponClass) + v.dcc = LoadDCC(dccPath, provider) + cofPath := fmt.Sprintf("%s/%s/cof/%s%s%s.cof", v.base, v.token, v.token, animationMode, weaponClass) + v.cof = LoadCof(cofPath, provider) + v.animationMode = animationMode + v.weaponClass = weaponClass + v.direction = direction + v.cacheFrames() +} + +func (v *AnimatedEntity) Render(target *ebiten.Image, offsetX, offsetY int) { + for v.lastFrameTime.Add(time.Millisecond * time.Duration(v.animationSpeed)).Before(time.Now()) { + v.lastFrameTime = v.lastFrameTime.Add(time.Millisecond * time.Duration(v.animationSpeed)) + v.currentFrame++ + if v.currentFrame >= v.framesToAnimate { + v.currentFrame = 0 + } + } + + opts := &ebiten.DrawImageOptions{} + opts.GeoM.Translate(float64(v.frameLocations[v.currentFrame].Left+offsetX), float64(v.frameLocations[v.currentFrame].Top+offsetY+40)) + target.DrawImage(v.frames[v.currentFrame], opts) +} + +func (v *AnimatedEntity) cacheFrames() { + animationData := AnimationData[strings.ToLower(v.token+v.animationMode+v.weaponClass)][v.direction] + v.animationSpeed = int((float64(animationData.AnimationSpeed) / 255.0) * 0.04 * 1000.0) + v.framesToAnimate = animationData.FramesPerDirection + v.lastFrameTime = time.Now() + minX := int32(2147483647) + minY := int32(2147483647) + maxX := int32(-2147483648) + maxY := int32(-2147483648) + for _, layer := range v.dcc.Directions { + minX = MinInt32(minX, int32(layer.Box.Left)) + minY = MinInt32(minY, int32(layer.Box.Top)) + maxX = MaxInt32(maxX, int32(layer.Box.Right())) + maxY = MaxInt32(maxY, int32(layer.Box.Bottom())) + } + frameW := maxX - minX + frameH := maxY - minY + v.frames = make([]*ebiten.Image, v.framesToAnimate) + v.frameLocations = make([]Rectangle, v.framesToAnimate) + for frameIndex := range v.frames { + v.frames[frameIndex], _ = ebiten.NewImage(int(frameW), int(frameH), ebiten.FilterNearest) + priorityBase := (v.direction * animationData.FramesPerDirection * v.cof.NumberOfLayers) + (frameIndex * v.cof.NumberOfLayers) + for layerIdx := 0; layerIdx < v.cof.NumberOfLayers; layerIdx++ { + comp := v.cof.Priority[priorityBase+layerIdx] + if _, found := v.cof.CompositeLayers[comp]; !found { + continue + } + direction := v.dcc.Directions[v.direction] + frame := direction.Frames[frameIndex] + pixelData := make([]byte, 4*frameW*frameH) + for y := 0; y < direction.Box.Height; y++ { + for x := 0; x < direction.Box.Width; x++ { + paletteIndex := frame.PixelData[x+(y*direction.Box.Width)] + + if paletteIndex == 0 { + continue + } + color := Palettes[v.palette].Colors[paletteIndex] + actualX := x + direction.Box.Left - int(minX) + actualY := y + direction.Box.Top - int(minY) + pixelData[(actualX*4)+(actualY*int(frameW)*4)] = color.R + pixelData[(actualX*4)+(actualY*int(frameW)*4)+1] = color.G + pixelData[(actualX*4)+(actualY*int(frameW)*4)+2] = color.B + pixelData[(actualX*4)+(actualY*int(frameW)*4)+3] = 255 + } + } + v.frames[frameIndex].ReplacePixels(pixelData) + v.frameLocations[frameIndex] = Rectangle{ + Left: int(minX), + Top: int(minY), + Width: int(frameW), + Height: int(frameH), + } + } + } +} diff --git a/Common/Cof.go b/Common/Cof.go new file mode 100644 index 00000000..1c131782 --- /dev/null +++ b/Common/Cof.go @@ -0,0 +1,244 @@ +package Common + +import "strings" + +type AnimationMode int + +const ( + AnimationModePlayerDeath AnimationMode = 0 + AnimationModePlayerNeutral AnimationMode = 1 + AnimationModePlayerWalk AnimationMode = 2 + AnimationModePlayerRun AnimationMode = 3 + AnimationModePlayerGetHit AnimationMode = 4 + AnimationModePlayerTownNeutral AnimationMode = 5 + AnimationModePlayerTownWalk AnimationMode = 6 + AnimationModePlayerAttack1 AnimationMode = 7 + AnimationModePlayerAttack2 AnimationMode = 8 + AnimationModePlayerBlock AnimationMode = 9 + AnimationModePlayerCast AnimationMode = 10 + AnimationModePlayerThrow AnimationMode = 11 + AnimationModePlayerKick AnimationMode = 12 + AnimationModePlayerSkill1 AnimationMode = 13 + AnimationModePlayerSkill2 AnimationMode = 14 + AnimationModePlayerSkill3 AnimationMode = 15 + AnimationModePlayerSkill4 AnimationMode = 16 + AnimationModePlayerDead AnimationMode = 17 + AnimationModePlayerSequence AnimationMode = 18 + AnimationModePlayerKnockBack AnimationMode = 19 + AnimationModeMonsterDeath AnimationMode = 20 + AnimationModeMonsterNeutral AnimationMode = 21 + AnimationModeMonsterWalk AnimationMode = 22 + AnimationModeMonsterGetHit AnimationMode = 23 + AnimationModeMonsterAttack1 AnimationMode = 24 + AnimationModeMonsterAttack2 AnimationMode = 25 + AnimationModeMonsterBlock AnimationMode = 26 + AnimationModeMonsterCast AnimationMode = 27 + AnimationModeMonsterSkill1 AnimationMode = 28 + AnimationModeMonsterSkill2 AnimationMode = 29 + AnimationModeMonsterSkill3 AnimationMode = 30 + AnimationModeMonsterSkill4 AnimationMode = 31 + AnimationModeMonsterDead AnimationMode = 32 + AnimationModeMonsterKnockback AnimationMode = 33 + AnimationModeMonsterSequence AnimationMode = 34 + AnimationModeMonsterRun AnimationMode = 35 + AnimationModeObjectNeutral AnimationMode = 36 + AnimationModeObjectOperating AnimationMode = 37 + AnimationModeObjectOpened AnimationMode = 38 + AnimationModeObjectSpecial1 AnimationMode = 39 + AnimationModeObjectSpecial2 AnimationMode = 40 + AnimationModeObjectSpecial3 AnimationMode = 41 + AnimationModeObjectSpecial4 AnimationMode = 42 + AnimationModeObjectSpecial5 AnimationMode = 43 +) + +var AnimationModeStr = map[AnimationMode]string{ + AnimationModePlayerDeath: "DT", + AnimationModePlayerNeutral: "NU", + AnimationModePlayerWalk: "WL", + AnimationModePlayerRun: "RN", + AnimationModePlayerGetHit: "GH", + AnimationModePlayerTownNeutral: "TN", + AnimationModePlayerTownWalk: "TW", + AnimationModePlayerAttack1: "A1", + AnimationModePlayerAttack2: "A2", + AnimationModePlayerBlock: "BL", + AnimationModePlayerCast: "SC", + AnimationModePlayerThrow: "TH", + AnimationModePlayerKick: "KK", + AnimationModePlayerSkill1: "S1", + AnimationModePlayerSkill2: "S2", + AnimationModePlayerSkill3: "S3", + AnimationModePlayerSkill4: "S4", + AnimationModePlayerDead: "DD", + AnimationModePlayerSequence: "GH", + AnimationModePlayerKnockBack: "GH", + AnimationModeMonsterDeath: "DT", + AnimationModeMonsterNeutral: "NU", + AnimationModeMonsterWalk: "WL", + AnimationModeMonsterGetHit: "GH", + AnimationModeMonsterAttack1: "A1", + AnimationModeMonsterAttack2: "A2", + AnimationModeMonsterBlock: "BL", + AnimationModeMonsterCast: "SC", + AnimationModeMonsterSkill1: "S1", + AnimationModeMonsterSkill2: "S2", + AnimationModeMonsterSkill3: "S3", + AnimationModeMonsterSkill4: "S4", + AnimationModeMonsterDead: "DD", + AnimationModeMonsterKnockback: "GH", + AnimationModeMonsterSequence: "xx", + AnimationModeMonsterRun: "RN", + AnimationModeObjectNeutral: "NU", + AnimationModeObjectOperating: "OP", + AnimationModeObjectOpened: "ON", + AnimationModeObjectSpecial1: "S1", + AnimationModeObjectSpecial2: "S2", + AnimationModeObjectSpecial3: "S3", + AnimationModeObjectSpecial4: "S4", + AnimationModeObjectSpecial5: "S5", +} + +type CompositeType int + +const ( + CompositeTypeHead CompositeType = 0 + CompositeTypeTorso CompositeType = 1 + CompositeTypeLegs CompositeType = 2 + CompositeTypeRightArm CompositeType = 3 + CompositeTypeLeftArm CompositeType = 4 + CompositeTypeRightHand CompositeType = 5 + CompositeTypeLeftHand CompositeType = 6 + CompositeTypeShield CompositeType = 7 + CompositeTypeSpecial1 CompositeType = 8 + CompositeTypeSpecial2 CompositeType = 9 + CompositeTypeSpecial3 CompositeType = 10 + CompositeTypeSpecial4 CompositeType = 11 + CompositeTypeSpecial5 CompositeType = 12 + CompositeTypeSpecial6 CompositeType = 13 + CompositeTypeSpecial7 CompositeType = 14 + CompositeTypeSpecial8 CompositeType = 15 + CompositeTypeMax CompositeType = 16 +) + +type DrawEffect int + +const ( + DrawEffectPctTransparency75 = 0 //75 % transparency (colormaps 561-816 in a .pl2) + DrawEffectPctTransparency50 = 1 //50 % transparency (colormaps 305-560 in a .pl2) + DrawEffectPctTransparency25 = 2 //25 % transparency (colormaps 49-304 in a .pl2) + DrawEffectScreen = 3 //Screen (colormaps 817-1072 in a .pl2) + DrawEffectLuminance = 4 //luminance (colormaps 1073-1328 in a .pl2) + DrawEffectBringAlphaBlending = 5 //bright alpha blending (colormaps 1457-1712 in a .pl2) +) + +type WeaponClass int + +const ( + WeaponClassNone WeaponClass = 0 + WeaponClassHandToHand WeaponClass = 1 + WeaponClassBow WeaponClass = 2 + WeaponClassOneHandSwing WeaponClass = 3 + WeaponClassOneHandThrust WeaponClass = 4 + WeaponClassStaff WeaponClass = 5 + WeaponClassTwoHandSwing WeaponClass = 6 + WeaponClassTwoHandThrust WeaponClass = 7 + WeaponClassCrossbow WeaponClass = 8 + WeaponClassLeftJabRightSwing WeaponClass = 9 + WeaponClassLeftJabRightThrust WeaponClass = 10 + WeaponClassLeftSwingRightSwing WeaponClass = 11 + WeaponClassLeftSwingRightThrust WeaponClass = 12 + WeaponClassOneHandToHand WeaponClass = 13 + WeaponClassTwoHandToHand WeaponClass = 14 +) + +var WeaponClassStr = map[WeaponClass]string{ + WeaponClassNone: "", + WeaponClassHandToHand: "hth", + WeaponClassBow: "bow", + WeaponClassOneHandSwing: "1hs", + WeaponClassOneHandThrust: "1ht", + WeaponClassStaff: "stf", + WeaponClassTwoHandSwing: "2hs", + WeaponClassTwoHandThrust: "2ht", + WeaponClassCrossbow: "xbw", + WeaponClassLeftJabRightSwing: "1js", + WeaponClassLeftJabRightThrust: "1jt", + WeaponClassLeftSwingRightSwing: "1ss", + WeaponClassLeftSwingRightThrust: "1st", + WeaponClassOneHandToHand: "ht1", + WeaponClassTwoHandToHand: "ht2", +} + +func GetWeaponClass(val string) WeaponClass { + for weaponClass, weaponStr := range WeaponClassStr { + if val != weaponStr { + continue + } + return weaponClass + } + return WeaponClassNone +} + +type AnimationFrame int + +const ( + AnimationFrameNoEvent AnimationFrame = 0 + AnimationFrameAttack AnimationFrame = 1 + AnimationFrameMissile AnimationFrame = 2 + AnimationFrameSound AnimationFrame = 3 + AnimationFrameSkill AnimationFrame = 4 +) + +type CofLayer struct { + Type CompositeType + Shadow byte + Transparent bool + DrawEffect DrawEffect + WeaponClass WeaponClass +} + +type Cof struct { + NumberOfDirections int + FramesPerDirection int + NumberOfLayers int + CofLayers []*CofLayer + CompositeLayers map[CompositeType]int + AnimationFrames []AnimationFrame + Priority []CompositeType +} + +func LoadCof(fileName string, fileProvider FileProvider) *Cof { + result := &Cof{} + fileData := fileProvider.LoadFile(fileName) + streamReader := 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.CompositeLayers = make(map[CompositeType]int, 0) + for i := 0; i < result.NumberOfLayers; i++ { + layer := &CofLayer{} + layer.Type = CompositeType(streamReader.GetByte()) + layer.Shadow = streamReader.GetByte() + streamReader.SkipBytes(1) // Unknown + layer.Transparent = streamReader.GetByte() != 0 + layer.DrawEffect = DrawEffect(streamReader.GetByte()) + weaponClassStr, _ := streamReader.ReadBytes(4) + layer.WeaponClass = GetWeaponClass(strings.TrimSpace(strings.ReplaceAll(string(weaponClassStr), string(0), ""))) + result.CofLayers = append(result.CofLayers, layer) + result.CompositeLayers[layer.Type] = i + } + animationFrameBytes, _ := streamReader.ReadBytes(result.FramesPerDirection) + result.AnimationFrames = make([]AnimationFrame, result.FramesPerDirection) + for i := range animationFrameBytes { + result.AnimationFrames[i] = AnimationFrame(animationFrameBytes[i]) + } + priorityLen := result.FramesPerDirection * result.NumberOfDirections * result.NumberOfLayers + result.Priority = make([]CompositeType, priorityLen) + priorityBytes, _ := streamReader.ReadBytes(priorityLen) + for i := range priorityBytes { + result.Priority[i] = CompositeType(priorityBytes[i]) + } + return result +} diff --git a/Common/Dcc.go b/Common/Dcc.go index 1e322aa2..7cd001be 100644 --- a/Common/Dcc.go +++ b/Common/Dcc.go @@ -513,5 +513,6 @@ func LoadDCC(path string, fileProvider FileProvider) *DCC { for i := 0; i < result.NumberOfDirections; i++ { result.Directions[i] = CreateDCCDirection(CreateBitMuncher(fileData, directionOffsets[i]*8), result) } + return result } diff --git a/Common/Math.go b/Common/Math.go index 023acfed..052fb304 100644 --- a/Common/Math.go +++ b/Common/Math.go @@ -58,3 +58,9 @@ func IsoToScreen(isoX, isoY, modX, modY int) (int, int) { screenY := (isoX + isoY) * 40 return screenX + modX, screenY + modY } + +func ScreenToIso(sx, sy float64) (float64, float64) { + x := (sx/80 + sy/40) / 2 + y := (sy/40 - (sx / 80)) / 2 + return x, y +} diff --git a/Common/Palette.go b/Common/Palette.go index aea4ff65..c7eda1ae 100644 --- a/Common/Palette.go +++ b/Common/Palette.go @@ -2,7 +2,6 @@ package Common import ( "log" - "strings" "github.com/OpenDiablo2/OpenDiablo2/PaletteDefs" ) @@ -34,15 +33,15 @@ func CreatePalette(name PaletteDefs.PaletteType, data []byte) PaletteRec { return result } -func LoadPalettes(mpqFiles map[string]*MpqFileRecord, fileProvider FileProvider) { +func LoadPalettes(mpqFiles map[string]string, fileProvider FileProvider) { Palettes = make(map[PaletteDefs.PaletteType]PaletteRec) - for file := range mpqFiles { - if strings.Index(file, "/data/global/palette/") != 0 || strings.Index(file, ".dat") != len(file)-4 { - continue - } - nameParts := strings.Split(file, `/`) - paletteName := PaletteDefs.PaletteType(nameParts[len(nameParts)-2]) - palette := CreatePalette(paletteName, fileProvider.LoadFile(file)) + for _, pal := range []string{ + "act1", "act2", "act3", "act4", "act5", "endgame", "endgame2", "fechar", "loading", + "menu0", "menu1", "menu2", "menu3", "menu4", "sky", "static", "trademark", "units", + } { + filePath := `data\global\palette\` + pal + `\pal.dat` + paletteName := PaletteDefs.PaletteType(pal) + palette := CreatePalette(paletteName, fileProvider.LoadFile(filePath)) Palettes[paletteName] = palette } log.Printf("Loaded %d palettes", len(Palettes)) diff --git a/Common/Rectangle.go b/Common/Rectangle.go index 1fa33f48..54a4dcae 100644 --- a/Common/Rectangle.go +++ b/Common/Rectangle.go @@ -14,3 +14,7 @@ func (v *Rectangle) Bottom() int { func (v *Rectangle) Right() int { return v.Left + v.Width } + +func (v *Rectangle) IsInRect(x, y int) bool { + return x >= v.Left && x < v.Left+v.Width && y >= v.Top && y < v.Top+v.Height +} diff --git a/Core/Engine.go b/Core/Engine.go index b7c6f45b..e9d88118 100644 --- a/Core/Engine.go +++ b/Core/Engine.go @@ -2,7 +2,6 @@ package Core import ( "encoding/json" - "fmt" "io/ioutil" "log" "math" @@ -11,12 +10,13 @@ import ( "strings" "sync" + "github.com/OpenDiablo2/OpenDiablo2/MPQ" + "github.com/OpenDiablo2/OpenDiablo2/PaletteDefs" "github.com/OpenDiablo2/OpenDiablo2/Sound" "github.com/OpenDiablo2/OpenDiablo2/Common" - "github.com/OpenDiablo2/OpenDiablo2/MPQ" "github.com/OpenDiablo2/OpenDiablo2/ResourcePaths" "github.com/OpenDiablo2/OpenDiablo2/Scenes" "github.com/OpenDiablo2/OpenDiablo2/UI" @@ -42,17 +42,17 @@ type EngineConfig struct { // Engine is the core OpenDiablo2 engine type Engine struct { - Settings *EngineConfig // Engine configuration settings from json file - Files map[string]*Common.MpqFileRecord // 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 *Common.Sprite // The sprite shown when loading stuff - loadingProgress float64 // LoadingProcess is a range between 0.0 and 1.0. If set, loading screen displays. - stepLoadingSize float64 // The size for each loading step - CurrentScene Scenes.Scene // The current scene being rendered - UIManager *UI.Manager // The UI manager - SoundManager *Sound.Manager // The sound manager - nextScene Scenes.Scene // The next scene to be loaded at the end of the game loop - fullscreenKey bool // When true, the fullscreen toggle is still being pressed + Settings *EngineConfig // 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 *Common.Sprite // The sprite shown when loading stuff + loadingProgress float64 // LoadingProcess is a range between 0.0 and 1.0. If set, loading screen displays. + stepLoadingSize float64 // The size for each loading step + CurrentScene Scenes.Scene // The current scene being rendered + UIManager *UI.Manager // The UI manager + SoundManager *Sound.Manager // The sound manager + nextScene Scenes.Scene // The next scene to be loaded at the end of the game loop + fullscreenKey bool // When true, the fullscreen toggle is still being pressed } // CreateEngine creates and instance of the OpenDiablo2 engine @@ -115,6 +115,11 @@ func (v *Engine) loadConfigurationFile() { } } +func (v *Engine) mapMpqFiles() { + v.Files = make(map[string]string) +} + +/* func (v *Engine) mapMpqFiles() { log.Println("mapping mpq file structure") v.Files = make(map[string]*Common.MpqFileRecord) @@ -229,6 +234,37 @@ func (v *Engine) LoadFile(fileName string) []byte { mutex.Unlock() return result } +*/ +var mutex sync.Mutex + +func (v *Engine) LoadFile(fileName string) []byte { + fileName = strings.ReplaceAll(fileName, "{LANG}", ResourcePaths.LanguageCode) + fileName = strings.ToLower(fileName) + fileName = strings.ReplaceAll(fileName, `/`, "\\") + if fileName[0] == '\\' { + fileName = fileName[1:] + } + mutex.Lock() + defer mutex.Unlock() + // TODO: May want to cache some things if performance becomes an issue + cachedMpqFile, cacheExists := v.Files[fileName] + if cacheExists { + mpq, _ := MPQ.Load(cachedMpqFile) + result, _ := mpq.ReadFile(fileName) + return result + } + for _, mpqFile := range v.Settings.MpqLoadOrder { + mpq, _ := MPQ.Load(path.Join(v.Settings.MpqPath, mpqFile)) + if !mpq.FileExists(fileName) { + continue + } + v.Files[fileName] = path.Join(v.Settings.MpqPath, mpqFile) + result, _ := mpq.ReadFile(fileName) + return result + } + log.Fatalf("Could not load %s from MPQs", fileName) + return []byte{} +} // IsLoading returns true if the engine is currently in a loading state func (v *Engine) IsLoading() bool { diff --git a/MPQ/MPQ.go b/MPQ/MPQ.go index ae431c96..c3ef21dd 100644 --- a/MPQ/MPQ.go +++ b/MPQ/MPQ.go @@ -242,7 +242,6 @@ func (v *MPQ) Close() { } func (v MPQ) FileExists(fileName string) bool { - fileName = strings.ReplaceAll(fileName, "{LANG}", ResourcePaths.LanguageCode) _, err := v.getFileHashEntry(fileName) return err == nil } diff --git a/Map/Engine.go b/Map/Engine.go index 5a48abf0..5b930b68 100644 --- a/Map/Engine.go +++ b/Map/Engine.go @@ -1,7 +1,7 @@ package Map import ( - "image" + "math" "math/rand" "github.com/OpenDiablo2/OpenDiablo2/Common" @@ -10,7 +10,7 @@ import ( ) type EngineRegion struct { - Rect image.Rectangle + Rect Common.Rectangle Region *Region } @@ -37,76 +37,116 @@ func (v *Engine) GenerateMap(regionType RegionIdType, levelPreset int) { randomSource := rand.NewSource(v.gameState.Seed) region := LoadRegion(randomSource, regionType, levelPreset, v.fileProvider) v.regions = append(v.regions, EngineRegion{ - Rect: image.Rectangle{image.Point{0, 0}, image.Point{int(region.TileWidth), int(region.TileHeight)}}, + Rect: Common.Rectangle{0, 0, int(region.TileWidth), int(region.TileHeight)}, Region: region, }) - v.soundManager.PlayBGM("/data/global/music/Act1/tristram.wav") // TODO: Temp stuff here + v.soundManager.PlayBGM("/data/global/music/Act1/town1.wav") // TODO: Temp stuff here +} + +func (v *Engine) GetRegionAt(x, y int) *Region { + if v.regions == nil { + return nil + } + for _, region := range v.regions { + if !region.Rect.IsInRect(x, y) { + continue + } + return region.Region + } + return nil } func (v *Engine) Render(target *ebiten.Image) { - for y := 0; y < int(v.regions[0].Region.TileHeight); y++ { - offX := -(y * 80) - offY := y * 40 - for x := 0; x < int(v.regions[0].Region.TileWidth); x++ { - sx, sy := Common.IsoToScreen(x, y, int(v.OffsetX), int(v.OffsetY)) - if sx > -160 && sy > -160 && sx <= 800 && sy <= 1000 { - tile := v.regions[0].Region.DS1.Tiles[y][x] - for i := range tile.Floors { - if !tile.Floors[i].Hidden && tile.Floors[i].Prop1 != 0 { - v.regions[0].Region.RenderTile(offX+int(v.OffsetX), offY+int(v.OffsetY), x, y, RegionLayerTypeFloors, i, target) + for _, region := range v.regions { + for y := 0; y < int(region.Region.TileHeight); y++ { + offX := -(y * 80) + offY := y * 40 + for x := 0; x < int(region.Region.TileWidth); x++ { + sx, sy := Common.IsoToScreen(x, y, int(v.OffsetX), int(v.OffsetY)) + if sx > -160 && sy > -160 && sx <= 880 && sy <= 1000 { + tile := region.Region.DS1.Tiles[y][x] + for i := range tile.Floors { + if tile.Floors[i].Hidden || tile.Floors[i].Prop1 == 0 { + continue + } + region.Region.RenderTile(offX+int(v.OffsetX), offY+int(v.OffsetY), x, y, RegionLayerTypeFloors, i, target) } } + offX += 80 + offY += 40 } - offX += 80 - offY += 40 } } - for y := 0; y < int(v.regions[0].Region.TileHeight); y++ { - offX := -(y * 80) - offY := y * 40 - for x := 0; x < int(v.regions[0].Region.TileWidth); x++ { - sx, sy := Common.IsoToScreen(x, y, int(v.OffsetX), int(v.OffsetY)) - if sx > -160 && sy > -160 && sx <= 800 && sy <= 1000 { - tile := v.regions[0].Region.DS1.Tiles[y][x] - for i := range tile.Shadows { - if tile.Shadows[i].Hidden || tile.Shadows[i].Prop1 == 0 { + for _, region := range v.regions { + for y := 0; y < int(region.Region.TileHeight); y++ { + offX := -(y * 80) + offY := y * 40 + for x := 0; x < int(region.Region.TileWidth); x++ { + sx, sy := Common.IsoToScreen(x, y, int(v.OffsetX), int(v.OffsetY)) + if sx > -160 && sy > -160 && sx <= 880 && sy <= 1000 { + tile := region.Region.DS1.Tiles[y][x] + for i := range tile.Shadows { + if tile.Shadows[i].Hidden || tile.Shadows[i].Prop1 == 0 { + continue + } + region.Region.RenderTile(offX+int(v.OffsetX), offY+int(v.OffsetY), x, y, RegionLayerTypeShadows, i, target) + } + } + offX += 80 + offY += 40 + } + } + } + for _, region := range v.regions { + for y := 0; y < int(region.Region.TileHeight); y++ { + offX := -(y * 80) + offY := y * 40 + for x := 0; x < int(region.Region.TileWidth); x++ { + tile := region.Region.DS1.Tiles[y][x] + for i := range tile.Walls { + if tile.Walls[i].Hidden || tile.Walls[i].Orientation == 15 || tile.Walls[i].Orientation == 10 || tile.Walls[i].Orientation == 11 || tile.Walls[i].Orientation == 0 { continue } - v.regions[0].Region.RenderTile(offX+int(v.OffsetX), offY+int(v.OffsetY), x, y, RegionLayerTypeShadows, i, target) + region.Region.RenderTile(offX+int(v.OffsetX), offY+int(v.OffsetY), x, y, RegionLayerTypeWalls, i, target) } + offX += 80 + offY += 40 } - offX += 80 - offY += 40 } } - for y := 0; y < int(v.regions[0].Region.TileHeight); y++ { - offX := -(y * 80) - offY := y * 40 - for x := 0; x < int(v.regions[0].Region.TileWidth); x++ { - tile := v.regions[0].Region.DS1.Tiles[y][x] - for i := range tile.Walls { - if tile.Walls[i].Hidden || tile.Walls[i].Orientation == 15 || tile.Walls[i].Orientation == 10 || tile.Walls[i].Orientation == 11 || tile.Walls[i].Orientation == 0 { - continue + for _, region := range v.regions { + for y := 0; y < int(region.Region.TileHeight); y++ { + offX := -(y * 80) + offY := y * 40 + for x := 0; x < int(region.Region.TileWidth); x++ { + tile := region.Region.DS1.Tiles[y][x] + for i := range tile.Walls { + if tile.Walls[i].Hidden || tile.Walls[i].Orientation != 15 { + continue + } + region.Region.RenderTile(offX+int(v.OffsetX), offY+int(v.OffsetY), x, y, RegionLayerTypeWalls, i, target) } - v.regions[0].Region.RenderTile(offX+int(v.OffsetX), offY+int(v.OffsetY), x, y, RegionLayerTypeWalls, i, target) + offX += 80 + offY += 40 } - offX += 80 - offY += 40 } } - for y := 0; y < int(v.regions[0].Region.TileHeight); y++ { - offX := -(y * 80) - offY := y * 40 - for x := 0; x < int(v.regions[0].Region.TileWidth); x++ { - tile := v.regions[0].Region.DS1.Tiles[y][x] - for i := range tile.Walls { - if tile.Walls[i].Hidden || tile.Walls[i].Orientation != 15 { - continue + for _, region := range v.regions { + for y := 0; y < int(region.Region.TileHeight); y++ { + offX := -(y * 80) + offY := y * 40 + for x := 0; x < int(region.Region.TileWidth); x++ { + sx, sy := Common.IsoToScreen(x, y, int(v.OffsetX), int(v.OffsetY)) + if sx > -160 && sy > -160 && sx <= 880 && sy <= 1000 { + for _, obj := range region.Region.AnimationEntities { + if int(math.Floor(obj.LocationX)) == x && int(math.Floor(obj.LocationY)) == y { + obj.Render(target, offX+int(v.OffsetX), offY+int(v.OffsetY)) + } + } } - v.regions[0].Region.RenderTile(offX+int(v.OffsetX), offY+int(v.OffsetY), x, y, RegionLayerTypeWalls, i, target) + offX += 80 + offY += 40 } - offX += 80 - offY += 40 } } } diff --git a/Map/Region.go b/Map/Region.go index b08eb972..fe48f739 100644 --- a/Map/Region.go +++ b/Map/Region.go @@ -6,7 +6,7 @@ import ( "math" "math/rand" "strconv" - "strings" + "sync" "github.com/OpenDiablo2/OpenDiablo2/PaletteDefs" @@ -22,16 +22,17 @@ type TileCacheRecord struct { } type Region struct { - levelType Common.LevelTypeRecord - levelPreset *Common.LevelPresetRecord - TileWidth int32 - TileHeight int32 - Tiles []Tile - DS1 *DS1 - Palette Common.PaletteRec - FloorCache map[uint32]*TileCacheRecord - ShadowCache map[uint32]*TileCacheRecord - WallCache map[uint32]*TileCacheRecord + LevelType Common.LevelTypeRecord + levelPreset *Common.LevelPresetRecord + TileWidth int32 + TileHeight int32 + Tiles []Tile + DS1 *DS1 + Palette Common.PaletteRec + FloorCache map[uint32]*TileCacheRecord + ShadowCache map[uint32]*TileCacheRecord + WallCache map[uint32]*TileCacheRecord + AnimationEntities []*Common.AnimatedEntity } type RegionLayerType int @@ -84,16 +85,16 @@ const ( func LoadRegion(seed rand.Source, levelType RegionIdType, levelPreset int, fileProvider Common.FileProvider) *Region { result := &Region{ - levelType: Common.LevelTypes[levelType], + LevelType: Common.LevelTypes[levelType], levelPreset: Common.LevelPresets[levelPreset], Tiles: make([]Tile, 0), FloorCache: make(map[uint32]*TileCacheRecord), ShadowCache: make(map[uint32]*TileCacheRecord), WallCache: make(map[uint32]*TileCacheRecord), } - result.Palette = Common.Palettes[PaletteDefs.PaletteType("act"+strconv.Itoa(int(result.levelType.Act)))] + result.Palette = Common.Palettes[PaletteDefs.PaletteType("act"+strconv.Itoa(int(result.LevelType.Act)))] //\bm := result.levelPreset.Dt1Mask - for _, levelTypeDt1 := range result.levelType.Files { + for _, levelTypeDt1 := range result.LevelType.Files { /* if bm&1 == 0 { bm >>= 1 @@ -120,22 +121,32 @@ func LoadRegion(seed rand.Source, levelType RegionIdType, levelPreset int, fileP result.DS1 = LoadDS1("/data/global/tiles/"+levelFile, fileProvider) result.TileWidth = result.DS1.Width result.TileHeight = result.DS1.Height + var wg sync.WaitGroup + wg.Add(len(result.DS1.Objects)) + result.AnimationEntities = make([]*Common.AnimatedEntity, 0) for _, object := range result.DS1.Objects { - switch object.Lookup.Type { - case Common.ObjectTypeCharacter: - case Common.ObjectTypeItem: - if object.Lookup.Base == "" { - continue + go func(object Object) { + defer wg.Done() + switch object.Lookup.Type { + case Common.ObjectTypeCharacter: + case Common.ObjectTypeItem: + if object.Lookup.Base == "" || object.Lookup.Token == "" || object.Lookup.TR == "" { + return + } + animEntity := Common.CreateAnimatedEntity(object.Lookup.Base, object.Lookup.Token, object.Lookup.TR, PaletteDefs.Units) + animEntity.SetMode(object.Lookup.Mode, object.Lookup.Class, 0, fileProvider) + animEntity.LocationX = math.Floor(float64(object.X) / 5) + animEntity.LocationY = math.Floor(float64(object.Y) / 5) + result.AnimationEntities = append(result.AnimationEntities, animEntity) } - objPath := strings.ToLower(object.Lookup.Base + "/" + object.Lookup.Token + "/tr/" + object.Lookup.Token + "tr" + object.Lookup.TR + - object.Lookup.Mode + object.Lookup.Class + ".dcc") - _ = Common.LoadDCC(objPath, fileProvider) // This is where the magic will happen - } + }(object) } + wg.Wait() return result } func (v *Region) RenderTile(offsetX, offsetY, tileX, tileY int, layerType RegionLayerType, layerIndex int, target *ebiten.Image) { + offsetX -= 80 switch layerType { case RegionLayerTypeFloors: v.renderFloor(v.DS1.Tiles[tileY][tileX].Floors[layerIndex], offsetX, offsetY, target) diff --git a/Scenes/MapEngineTest.go b/Scenes/MapEngineTest.go index c55668db..28fa4106 100644 --- a/Scenes/MapEngineTest.go +++ b/Scenes/MapEngineTest.go @@ -1,11 +1,15 @@ package Scenes import ( + "fmt" + "math" + "github.com/OpenDiablo2/OpenDiablo2/Common" "github.com/OpenDiablo2/OpenDiablo2/Map" "github.com/OpenDiablo2/OpenDiablo2/Sound" "github.com/OpenDiablo2/OpenDiablo2/UI" "github.com/hajimehoshi/ebiten" + "github.com/hajimehoshi/ebiten/ebitenutil" ) type MapEngineTest struct { @@ -43,16 +47,16 @@ func (v *MapEngineTest) Load() []func() { v.mapEngine.GenerateMap(Map.RegionAct1Town, 1) //v.mapEngine.GenerateMap(Map.RegionAct1Tristram, 300) //v.mapEngine.GenerateMap(Map.RegionAct1Cathedral, 257) - //v.mapEngine.GenerateMap(Map.RegionAct2Town, 301) // Broken rendering - //v.mapEngine.GenerateMap(Map.RegionAct2Harem, 353) + //v.mapEngine.GenerateMap(Map.RegionAct2Town, 301) + //v.mapEngine.GenerateMap(Map.RegionAct2Harem, 353) // Crashes on dcc load //v.mapEngine.GenerateMap(Map.RegionAct3Town, 529) //v.mapEngine.GenerateMap(Map.RegionAct3Jungle, 574) - //v.mapEngine.GenerateMap(Map.RegionAct4Town, 797) - //v.mapEngine.GenerateMap(Map.RegonAct5Town, 863) - //v.mapEngine.GenerateMap(Map.RegionAct5IceCaves, 1038) - //v.mapEngine.GenerateMap(Map.RegionAct5Siege, 879) - //v.mapEngine.GenerateMap(Map.RegionAct5Lava, 1057) // PALETTE ISSUE - //v.mapEngine.GenerateMap(Map.RegionAct5Barricade, 880) + //v.mapEngine.GenerateMap(Map.RegionAct4Town, 797) // Broken height of large objects + //v.mapEngine.GenerateMap(Map.RegonAct5Town, 863) // Completely broken!! + //v.mapEngine.GenerateMap(Map.RegionAct5IceCaves, 1038) // Completely broken! + //v.mapEngine.GenerateMap(Map.RegionAct5Siege, 879) // Completely broken! + //v.mapEngine.GenerateMap(Map.RegionAct5Lava, 1057) // Broken + //v.mapEngine.GenerateMap(Map.RegionAct5Barricade, 880) // Broken }, } @@ -64,6 +68,18 @@ func (v *MapEngineTest) Unload() { func (v *MapEngineTest) Render(screen *ebiten.Image) { v.mapEngine.Render(screen) + actualX := float64(v.uiManager.CursorX) - v.mapEngine.OffsetX + actualY := float64(v.uiManager.CursorY) - v.mapEngine.OffsetY + tileX, tileY := Common.ScreenToIso(actualX, actualY) + subtileX := int(math.Ceil(math.Mod((tileX*10), 10))) / 2 + subtileY := int(math.Ceil(math.Mod((tileY*10), 10))) / 2 + line := fmt.Sprintf("%d, %d (Tile %d.%d, %d.%d)", int(math.Ceil(actualX)), int(math.Ceil(actualY)), int(math.Ceil(tileX)), subtileX, int(math.Ceil(tileY)), subtileY) + ebitenutil.DebugPrintAt(screen, line, 5, 5) + curRegion := v.mapEngine.GetRegionAt(int(tileX), int(tileY)) + if curRegion == nil { + return + } + ebitenutil.DebugPrintAt(screen, "Map: "+curRegion.LevelType.Name, 5, 17) } func (v *MapEngineTest) Update(tickTime float64) { diff --git a/go.mod b/go.mod index 7a530675..92a5cc70 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,11 @@ go 1.13 require ( github.com/JoshVarga/blast v0.0.0-20180421040937-681c804fb9f0 github.com/giorgisio/goav v0.1.0 - github.com/hajimehoshi/ebiten v1.9.3 + github.com/gopherjs/gopherjs v0.0.0-20191106031601-ce3c9ade29de // indirect + github.com/gopherjs/gopherwasm v1.1.0 // indirect + github.com/hajimehoshi/ebiten v1.10.0 github.com/mitchellh/go-homedir v1.1.0 + golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136 // indirect + golang.org/x/mobile v0.0.0-20191031020345-0945064e013a // indirect + golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd // indirect ) diff --git a/go.sum b/go.sum index d4eaacaa..c67f432a 100644 --- a/go.sum +++ b/go.sum @@ -1,14 +1,20 @@ +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +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/giorgisio/goav v0.1.0 h1:ZyfG3NfX7PMSimv4ulhmnQJf/XeHpMdGCn+afRmY5Oc= github.com/giorgisio/goav v0.1.0/go.mod h1:RtH8HyxLRLU1iY0pjfhWBKRhnbsnmfoI+FxMwb5bfEo= github.com/go-gl/glfw v0.0.0-20181213070059-819e8ce5125f h1:7MsFMbSn8Lcw0blK4+NEOf8DuHoOBDhJsHz04yh13pM= github.com/go-gl/glfw v0.0.0-20181213070059-819e8ce5125f/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/gofrs/flock v0.7.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +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/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c h1:16eHWuMGvCjSfgRJKqIzapE78onvvTbdi1rMkU00lZw= github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20191106031601-ce3c9ade29de/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherwasm v0.1.1/go.mod h1:kx4n9a+MzHH0BJJhvlsQ65hqLFXDO/m256AsaDPQ+/4= github.com/gopherjs/gopherwasm v1.0.0/go.mod h1:SkZ8z7CWBz5VXbhJel8TxCmAcsQqzgWGR/8nMhyhZSI= github.com/gopherjs/gopherwasm v1.1.0 h1:fA2uLoctU5+T3OhOn2vYP0DVT6pxc7xhTlBB1paATqQ= @@ -16,12 +22,19 @@ github.com/gopherjs/gopherwasm v1.1.0/go.mod h1:SkZ8z7CWBz5VXbhJel8TxCmAcsQqzgWG github.com/gosuri/uilive v0.0.0-20170323041506-ac356e6e42cd/go.mod h1:qkLSc0A5EXSP6B04TrN4oQoxqFI7A8XvoXSlJi8cwk8= github.com/gosuri/uiprogress v0.0.0-20170224063937-d0567a9d84a1/go.mod h1:C1RTYn4Sc7iEyf6j8ft5dyoZ4212h8G1ol9QQluh5+0= github.com/hajimehoshi/bitmapfont v1.1.1/go.mod h1:Hamfxgney7tDSmVOSDh2AWzoDH70OaC+P24zc02Gum4= +github.com/hajimehoshi/bitmapfont v1.2.0/go.mod h1:h9QrPk6Ktb2neObTlAbma6Ini1xgMjbJ3w7ysmD7IOU= github.com/hajimehoshi/ebiten v1.9.3 h1:YijWGMBwH2XA1ZytUQFy33UDHeCSS6d4JZKH1wq38O0= github.com/hajimehoshi/ebiten v1.9.3/go.mod h1:XxiJ4Eltvb1KmcD0i6F81eIB1asJhK47y5DC+FPkyso= +github.com/hajimehoshi/ebiten v1.10.0 h1:ADLOUI/7aaTOP7GRlQBHVI1Qtvfdt9M6XQqeULSK7Uo= +github.com/hajimehoshi/ebiten v1.10.0/go.mod h1:8BJhIws+Jkol+z7hSGP/WFsaDAPTtRQ+ELBPPQetq2w= github.com/hajimehoshi/go-mp3 v0.2.0/go.mod h1:4i+c5pDNKDrxl1iu9iG90/+fhP37lio6gNhjCx9WBJw= +github.com/hajimehoshi/go-mp3 v0.2.1/go.mod h1:Rr+2P46iH6PwTPVgSsEwBkon0CK5DxCAeX/Rp65DCTE= github.com/hajimehoshi/oto v0.1.1/go.mod h1:hUiLWeBQnbDu4pZsAhOnGqMI1ZGibS6e2qhQdfpwz04= github.com/hajimehoshi/oto v0.3.3 h1:Wi7VVtxe9sF2rbDBIJtVXnpFWhRfK57hw0JY7tR2qXM= github.com/hajimehoshi/oto v0.3.3/go.mod h1:e9eTLBB9iZto045HLbzfHJIc+jP3xaKrjZTghvb6fdM= +github.com/hajimehoshi/oto v0.3.4/go.mod h1:PgjqsBJff0efqL2nlMJidJgVJywLn6M4y8PI4TfeWfA= +github.com/hajimehoshi/oto v0.5.2 h1:5FEPlejAsR2PVRqiW7h2PIwp9UWR+8zxj2And102YU4= +github.com/hajimehoshi/oto v0.5.2/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI= 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= @@ -30,18 +43,55 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/exp v0.0.0-20180710024300-14dda7b62fcd h1:nLIcFw7GiqKXUS7HiChg6OAYWgASB2H97dZKd1GhDSs= golang.org/x/exp v0.0.0-20180710024300-14dda7b62fcd/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +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-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20180926015637-991ec62608f3/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190118043309-183bebdce1b2 h1:FNSSV4jv1PrPsiM2iKGpqLPPgYACqh9Muav7Pollk1k= golang.org/x/image v0.0.0-20190118043309-183bebdce1b2/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= +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-20180806140643-507816974b79/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190127143845-a42111704963 h1:2HSxAhImj2OpXsNjXSqfnv1xtqeCpDjwPB3o1DnQqKM= golang.org/x/mobile v0.0.0-20190127143845-a42111704963/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +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-20191031020345-0945064e013a h1:CrJ8+QyIm2tcw/zt9Rp/vGFsey+jndL1y5EnFwzgGOg= +golang.org/x/mobile v0.0.0-20191031020345-0945064e013a/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= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190203050204-7ae0202eb74c h1:YeMXU0KQqExdpG959DFhAhfpY8myIsnfqj8lhNFRzzE= golang.org/x/sys v0.0.0-20190203050204-7ae0202eb74c/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +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-20191105231009-c1f44814a5cd h1:3x5uuvBgE6oaXJjCOvpCC1IpgJogqQ+PqGGU3ZxAgII= +golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20190202235157-7414d4c1f71c/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190909214602-067311248421/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191026034945-b2104f82a97d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=