diff --git a/d2core/d2scene/main_menu.go b/d2core/d2scene/main_menu.go index b5d0a033..64cf1fc7 100644 --- a/d2core/d2scene/main_menu.go +++ b/d2core/d2scene/main_menu.go @@ -181,7 +181,7 @@ func (v *MainMenu) Load() []func() { } func (v *MainMenu) onMapTestClicked() { - v.sceneProvider.SetNextScene(CreateMapEngineTest(v.fileProvider, v.sceneProvider, v.uiManager, v.soundManager, 0)) + v.sceneProvider.SetNextScene(CreateMapEngineTest(v.fileProvider, v.sceneProvider, v.uiManager, v.soundManager, 0, 1)) } func openbrowser(url string) { diff --git a/d2core/d2scene/map_engine_testing.go b/d2core/d2scene/map_engine_testing.go index 0761171c..01ce977c 100644 --- a/d2core/d2scene/map_engine_testing.go +++ b/d2core/d2scene/map_engine_testing.go @@ -21,6 +21,67 @@ import ( "github.com/hajimehoshi/ebiten/inpututil" ) +type RegionSpec struct { + regionType d2enum.RegionIdType + startPresetIndex int + endPresetIndex int + extra []int +} + +var regions []RegionSpec = []RegionSpec{ + //Act I + {d2enum.RegionAct1Town, 1, 3, []int{}}, + {d2enum.RegionAct1Wilderness, 4, 52, []int{ + 108, + 160, 161, 162, 163, 164, + }}, + {d2enum.RegionAct1Cave, 53, 107, []int{}}, + {d2enum.RegionAct1Crypt, 109, 159, []int{}}, + {d2enum.RegionAct1Monestary, 165, 165, []int{}}, + {d2enum.RegionAct1Courtyard, 166, 166, []int{256}}, + {d2enum.RegionAct1Barracks, 167, 205, []int{}}, + {d2enum.RegionAct1Jail, 206, 255, []int{}}, + {d2enum.RegionAct1Cathedral, 257, 257, []int{}}, + {d2enum.RegionAct1Catacombs, 258, 299, []int{}}, + {d2enum.RegionAct1Tristram, 300, 300, []int{}}, + + //Act II + {d2enum.RegionAct2Town, 301, 301, []int{}}, + {d2enum.RegionAct2Sewer, 302, 352, []int{}}, + {d2enum.RegionAct2Harem, 353, 357, []int{}}, + {d2enum.RegionAct2Basement, 358, 361, []int{}}, + {d2enum.RegionAct2Desert, 362, 413, []int{}}, + {d2enum.RegionAct2Tomb, 414, 481, []int{}}, + {d2enum.RegionAct2Lair, 482, 509, []int{}}, + {d2enum.RegionAct2Arcane, 510, 528, []int{}}, + + //Act III + {d2enum.RegionAct3Town, 529, 529, []int{}}, + {d2enum.RegionAct3Jungle, 530, 604, []int{}}, + {d2enum.RegionAct3Kurast, 605, 658, []int{ + 748, 749, 750, 751, 752, 753, 754, + 755, 756, 757, 758, 759, 760, 761, 762, 763, 764, 765, 766, 767, 768, 769, 770, 771, 772, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782, 783, 784, 785, 786, 787, 788, 789, 790, 791, 792, 793, 794, 795, 796, + //yeah, i know =( + }}, + {d2enum.RegionAct3Spider, 659, 664, []int{}}, + {d2enum.RegionAct3Dungeon, 665, 704, []int{}}, + {d2enum.RegionAct3Sewer, 705, 747, []int{}}, + + //Act IV + {d2enum.RegionAct4Town, 797, 798, []int{}}, + {d2enum.RegionAct4Mesa, 799, 835, []int{}}, + {d2enum.RegionAct4Lava, 836, 862, []int{}}, + + //Act V -- broken or wrong order + {d2enum.RegonAct5Town, 863, 864, []int{}}, + {d2enum.RegionAct5Siege, 865, 879, []int{}}, + {d2enum.RegionAct5Barricade, 880, 1002, []int{}}, + {d2enum.RegionAct5IceCaves, 1003, 1041, []int{}}, + {d2enum.RegionAct5Temple, 1042, 1052, []int{}}, + {d2enum.RegionAct5Baal, 1059, 1090, []int{}}, + {d2enum.RegionAct5Lava, 1053, 1058, []int{}}, +} + type MapEngineTest struct { uiManager *d2ui.Manager soundManager *d2audio.Manager @@ -28,8 +89,15 @@ type MapEngineTest struct { sceneProvider d2coreinterface.SceneProvider gameState *d2core.GameState mapEngine *_map.Engine + + //TODO: this is region specific properties, should be refactored for multi-region rendering currentRegion int - keyLocked bool + levelPreset int + fileIndex int + regionSpec RegionSpec + filesCount int + + keyLocked bool } func CreateMapEngineTest( @@ -37,50 +105,56 @@ func CreateMapEngineTest( sceneProvider d2coreinterface.SceneProvider, uiManager *d2ui.Manager, soundManager *d2audio.Manager, - currentRegion int) *MapEngineTest { + currentRegion int, levelPreset int) *MapEngineTest { result := &MapEngineTest{ fileProvider: fileProvider, uiManager: uiManager, soundManager: soundManager, sceneProvider: sceneProvider, currentRegion: currentRegion, + levelPreset: levelPreset, + fileIndex: -1, + regionSpec: RegionSpec{}, + filesCount: 0, keyLocked: false, } result.gameState = d2core.CreateTestGameState() return result } -type RegionSpec struct { - regionType d2enum.RegionIdType - levelPreset int -} +func (v *MapEngineTest) LoadRegionByIndex(n int, levelPreset, fileIndex int) { + for _, spec := range regions { + if spec.regionType == d2enum.RegionIdType(n) { + v.regionSpec = spec + inExtra := false + for _, e := range spec.extra { + if e == levelPreset { + inExtra = true + break + } + } + if !inExtra { + if levelPreset < spec.startPresetIndex { + levelPreset = spec.startPresetIndex + } -var regions []RegionSpec = []RegionSpec{ - {d2enum.RegionAct1Tristram, 300}, - {d2enum.RegionAct1Cathedral, 257}, - {d2enum.RegionAct2Town, 301}, - // {d2enum.RegionAct2Harem, 353}, - {d2enum.RegionAct3Town, 529}, - {d2enum.RegionAct3Jungle, 574}, - {d2enum.RegionAct4Town, 797}, - {d2enum.RegonAct5Town, 863}, - {d2enum.RegionAct5IceCaves, 1038}, - {d2enum.RegionAct5Siege, 879}, - {d2enum.RegionAct5Lava, 105}, - {d2enum.RegionAct5Barricade, 880}, -} + if levelPreset > spec.endPresetIndex { + levelPreset = spec.endPresetIndex + } + } + v.levelPreset = levelPreset + } + } -func (v *MapEngineTest) LoadRegionByIndex(n int) { if n == 0 { v.mapEngine.GenerateAct1Overworld() return } - region := regions[n-1] v.mapEngine = _map.CreateMapEngine(v.gameState, v.soundManager, v.fileProvider) // necessary for map name update v.mapEngine.OffsetY = 0 v.mapEngine.OffsetX = 0 - v.mapEngine.GenerateMap(region.regionType, region.levelPreset) + v.mapEngine.GenerateMap(d2enum.RegionIdType(n), levelPreset, fileIndex) } func (v *MapEngineTest) Load() []func() { @@ -91,21 +165,7 @@ func (v *MapEngineTest) Load() []func() { func() { v.mapEngine = _map.CreateMapEngine(v.gameState, v.soundManager, v.fileProvider) - v.LoadRegionByIndex(v.currentRegion) - // v.mapEngine.GenerateAct1Overworld() - // v.mapEngine.GenerateMap(d2enum.RegionAct1Tristram, 300) - // v.mapEngine.GenerateMap(d2enum.RegionAct1Cathedral, 257) - //v.mapEngine.GenerateMap(d2enum.RegionAct2Town, 301) - //v.mapEngine.GenerateMap(d2enum.RegionAct2Harem, 353) // Crashes on dcc load - //v.mapEngine.GenerateMap(d2enum.RegionAct3Town, 529) - //v.mapEngine.GenerateMap(d2enum.RegionAct3Jungle, 574) - //v.mapEngine.GenerateMap(d2enum.RegionAct4Town, 797) // Broken height of large objects - //v.mapEngine.GenerateMap(d2enum.RegonAct5Town, 863) // Completely broken!! - //v.mapEngine.GenerateMap(d2enum.RegionAct5IceCaves, 1038) // Completely broken! - //v.mapEngine.GenerateMap(d2enum.RegionAct5Siege, 879) // Completely broken! - //v.mapEngine.GenerateMap(d2enum.RegionAct5Lava, 1057) // Broken - //v.mapEngine.GenerateMap(d2enum.RegionAct5Barricade, 880) // Broken - + v.LoadRegionByIndex(v.currentRegion, v.levelPreset, v.fileIndex) }, } } @@ -133,13 +193,34 @@ func (v *MapEngineTest) Render(screen *ebiten.Image) { int(math.Ceil(tileY))-curRegion.Rect.Top, subtileY, ) + + levelFilesToPick := make([]string, 0) + fileIndex := v.fileIndex + for n, fileRecord := range curRegion.Region.LevelPreset.Files { + if len(fileRecord) == 0 || fileRecord == "" || fileRecord == "0" { + continue + } + levelFilesToPick = append(levelFilesToPick, fileRecord) + if fileRecord == curRegion.Region.RegionPath { + fileIndex = n + } + } + if v.fileIndex == -1 { + v.fileIndex = fileIndex + } + v.filesCount = len(levelFilesToPick) ebitenutil.DebugPrintAt(screen, line, 5, 5) ebitenutil.DebugPrintAt(screen, "Map: "+curRegion.Region.LevelType.Name, 5, 17) - ebitenutil.DebugPrintAt(screen, fmt.Sprintf("%v [%v]", curRegion.Region.RegionPath, v.currentRegion), 5, 29) - ebitenutil.DebugPrintAt(screen, "N - next map, P - previous map", 5, 41) + ebitenutil.DebugPrintAt(screen, fmt.Sprintf("%v: %v/%v [%v, %v]", curRegion.Region.RegionPath, fileIndex+1, v.filesCount, v.currentRegion, v.levelPreset), 5, 29) + ebitenutil.DebugPrintAt(screen, "N - next region, P - previous region", 5, 41) + ebitenutil.DebugPrintAt(screen, "Shift+N - next preset, Shift+P - previous preset", 5, 53) + ebitenutil.DebugPrintAt(screen, "Ctrl+N - next file, Ctrl+P - previous file", 5, 65) } func (v *MapEngineTest) Update(tickTime float64) { + ctrlPressed := v.uiManager.KeyPressed(ebiten.KeyControl) + shiftPressed := v.uiManager.KeyPressed(ebiten.KeyShift) + if v.uiManager.KeyPressed(ebiten.KeyDown) { v.mapEngine.OffsetY -= tickTime * 800 } @@ -164,26 +245,50 @@ func (v *MapEngineTest) Update(tickTime float64) { if v.uiManager.KeyPressed(ebiten.KeyEscape) { os.Exit(0) } - if v.uiManager.KeyPressed(ebiten.KeyN) && !v.keyLocked { - v.currentRegion++ - if v.currentRegion == len(regions) { - v.currentRegion = 0 - } - v.keyLocked = true - fmt.Println("---") - v.sceneProvider.SetNextScene(v) - return - } - if v.uiManager.KeyPressed(ebiten.KeyP) && !v.keyLocked { - v.currentRegion-- - if v.currentRegion == -1 { - v.currentRegion = len(regions) - 1 + if !v.keyLocked { + + if v.uiManager.KeyPressed(ebiten.KeyN) && ctrlPressed { + v.fileIndex = increment(v.fileIndex, 0, v.filesCount-1) + v.keyLocked = true + v.sceneProvider.SetNextScene(v) + return + } + + if v.uiManager.KeyPressed(ebiten.KeyP) && ctrlPressed { + v.fileIndex = decrement(v.fileIndex, 0, v.filesCount-1) + v.keyLocked = true + v.sceneProvider.SetNextScene(v) + return + } + + if v.uiManager.KeyPressed(ebiten.KeyN) && shiftPressed { + v.levelPreset = increment(v.levelPreset, v.regionSpec.startPresetIndex, v.regionSpec.endPresetIndex) + v.keyLocked = true + v.sceneProvider.SetNextScene(v) + return + } + + if v.uiManager.KeyPressed(ebiten.KeyP) && shiftPressed { + v.levelPreset = decrement(v.levelPreset, v.regionSpec.startPresetIndex, v.regionSpec.endPresetIndex) + v.keyLocked = true + v.sceneProvider.SetNextScene(v) + return + } + + if v.uiManager.KeyPressed(ebiten.KeyN) { + v.currentRegion = increment(v.currentRegion, 0, len(regions)) + v.keyLocked = true + v.sceneProvider.SetNextScene(v) + return + } + + if v.uiManager.KeyPressed(ebiten.KeyP) { + v.currentRegion = decrement(v.currentRegion, 0, len(regions)) + v.keyLocked = true + v.sceneProvider.SetNextScene(v) + return } - v.keyLocked = true - fmt.Println("---") - v.sceneProvider.SetNextScene(v) - return } //FIXME: do it better @@ -191,3 +296,19 @@ func (v *MapEngineTest) Update(tickTime float64) { v.keyLocked = false } } + +func increment(v, min, max int) int { + v++ + if v > max { + return min + } + return v +} + +func decrement(v, min, max int) int { + v-- + if v < min { + return max + } + return v +} diff --git a/d2render/animated_entity.go b/d2render/animated_entity.go index a0757977..3be2de9a 100644 --- a/d2render/animated_entity.go +++ b/d2render/animated_entity.go @@ -214,6 +214,9 @@ func (v *AnimatedEntity) cacheFrames(layerName string) { } direction := dcc.Directions[v.direction] + if frameIndex >= len(direction.Frames) { + continue + } frame := direction.Frames[frameIndex] img := image.NewRGBA(image.Rect(0, 0, int(frameW), int(frameH))) for y := 0; y < direction.Box.Height; y++ { diff --git a/d2render/d2mapengine/engine.go b/d2render/d2mapengine/engine.go index afe9c251..ad8610fc 100644 --- a/d2render/d2mapengine/engine.go +++ b/d2render/d2mapengine/engine.go @@ -52,9 +52,10 @@ func CreateMapEngine(gameState *d2core.GameState, soundManager *d2audio.Manager, return result } -func (v *Engine) GenerateMap(regionType d2enum.RegionIdType, levelPreset int) { +func (v *Engine) GenerateMap(regionType d2enum.RegionIdType, levelPreset int, fileIndex int) { randomSource := rand.NewSource(v.gameState.Seed) - region := LoadRegion(randomSource, regionType, levelPreset, v.fileProvider) + region := LoadRegion(randomSource, regionType, levelPreset, v.fileProvider, fileIndex) + fmt.Printf("Loading region: %v\n", region.RegionPath) v.regions = append(v.regions, EngineRegion{ Rect: d2common.Rectangle{0, 0, int(region.TileWidth), int(region.TileHeight)}, Region: region, @@ -68,19 +69,19 @@ func (v *Engine) GenerateMap(regionType d2enum.RegionIdType, levelPreset int) { func (v *Engine) GenerateAct1Overworld() { v.soundManager.PlayBGM("/data/global/music/Act1/town1.wav") // TODO: Temp stuff here randomSource := rand.NewSource(v.gameState.Seed) - region := LoadRegion(randomSource, d2enum.RegionAct1Town, 1, v.fileProvider) + region := LoadRegion(randomSource, d2enum.RegionAct1Town, 1, v.fileProvider, -1) v.regions = append(v.regions, EngineRegion{ Rect: d2common.Rectangle{0, 0, int(region.TileWidth), int(region.TileHeight)}, Region: region, }) if strings.Contains(region.RegionPath, "E1") { - region2 := LoadRegion(randomSource, d2enum.RegionAct1Town, 2, v.fileProvider) + region2 := LoadRegion(randomSource, d2enum.RegionAct1Town, 2, v.fileProvider, -1) v.regions = append(v.regions, EngineRegion{ Rect: d2common.Rectangle{int(region.TileWidth - 1), 0, int(region2.TileWidth), int(region2.TileHeight)}, Region: region2, }) } else if strings.Contains(region.RegionPath, "S1") { - region2 := LoadRegion(randomSource, d2enum.RegionAct1Town, 3, v.fileProvider) + region2 := LoadRegion(randomSource, d2enum.RegionAct1Town, 3, v.fileProvider, -1) v.regions = append(v.regions, EngineRegion{ Rect: d2common.Rectangle{0, int(region.TileHeight - 1), int(region2.TileWidth), int(region2.TileHeight)}, Region: region2, diff --git a/d2render/d2mapengine/region.go b/d2render/d2mapengine/region.go index c7e2d686..bc26e721 100644 --- a/d2render/d2mapengine/region.go +++ b/d2render/d2mapengine/region.go @@ -1,12 +1,15 @@ package d2mapengine import ( + "fmt" "image/color" "log" "math" "math/rand" + "sort" "strconv" "sync" + "time" "github.com/OpenDiablo2/D2Shared/d2data/d2dt1" @@ -30,32 +33,39 @@ import ( "github.com/hajimehoshi/ebiten" ) +//TODO: move to corresponding file +type ByRarity []d2dt1.Tile + +func (a ByRarity) Len() int { return len(a) } +func (a ByRarity) Less(i, j int) bool { return a[i].RarityFrameIndex < a[j].RarityFrameIndex } +func (a ByRarity) Swap(i, j int) { a[i], a[j] = a[j], a[i] } + type Region struct { RegionPath string LevelType d2datadict.LevelTypeRecord - levelPreset d2datadict.LevelPresetRecord + LevelPreset d2datadict.LevelPresetRecord TileWidth int32 TileHeight int32 Tiles []d2dt1.Tile DS1 d2ds1.DS1 Palette d2datadict.PaletteRec - FloorCache map[uint32]*TileCacheRecord - ShadowCache map[uint32]*TileCacheRecord - WallCache map[uint32]*TileCacheRecord + FloorCache map[string]*TileCacheRecord + ShadowCache map[string]*TileCacheRecord + WallCache map[string]*TileCacheRecord AnimationEntities []d2render.AnimatedEntity NPCs []*d2core.NPC StartX float64 StartY float64 } -func LoadRegion(seed rand.Source, levelType d2enum.RegionIdType, levelPreset int, fileProvider d2interface.FileProvider) *Region { +func LoadRegion(seed rand.Source, levelType d2enum.RegionIdType, levelPreset int, fileProvider d2interface.FileProvider, fileIndex int) *Region { result := &Region{ LevelType: d2datadict.LevelTypes[levelType], - levelPreset: d2datadict.LevelPresets[levelPreset], + LevelPreset: d2datadict.LevelPresets[levelPreset], Tiles: make([]d2dt1.Tile, 0), - FloorCache: make(map[uint32]*TileCacheRecord), - ShadowCache: make(map[uint32]*TileCacheRecord), - WallCache: make(map[uint32]*TileCacheRecord), + FloorCache: make(map[string]*TileCacheRecord), + ShadowCache: make(map[string]*TileCacheRecord), + WallCache: make(map[string]*TileCacheRecord), } result.Palette = d2datadict.Palettes[d2enum.PaletteType("act"+strconv.Itoa(int(result.LevelType.Act)))] //bm := result.levelPreset.Dt1Mask @@ -74,7 +84,7 @@ func LoadRegion(seed rand.Source, levelType d2enum.RegionIdType, levelPreset int result.Tiles = append(result.Tiles, dt1.Tiles...) } levelFilesToPick := make([]string, 0) - for _, fileRecord := range result.levelPreset.Files { + for _, fileRecord := range result.LevelPreset.Files { if len(fileRecord) == 0 || fileRecord == "" || fileRecord == "0" { continue } @@ -82,6 +92,9 @@ func LoadRegion(seed rand.Source, levelType d2enum.RegionIdType, levelPreset int } random := rand.New(seed) levelIndex := int(math.Round(float64(len(levelFilesToPick)-1) * random.Float64())) + if fileIndex >= 0 && fileIndex < len(levelFilesToPick) { + levelIndex = fileIndex + } levelFile := levelFilesToPick[levelIndex] result.RegionPath = levelFile result.DS1 = d2ds1.LoadDS1("/data/global/tiles/"+levelFile, fileProvider) @@ -125,59 +138,86 @@ func (v *Region) RenderTile(offsetX, offsetY, tileX, tileY int, layerType d2enum offsetX -= 80 switch layerType { case d2enum.RegionLayerTypeFloors: - v.renderFloor(v.DS1.Tiles[tileY][tileX].Floors[layerIndex], offsetX, offsetY, target) + v.renderFloor(v.DS1.Tiles[tileY][tileX].Floors[layerIndex], offsetX, offsetY, target, tileX, tileY) case d2enum.RegionLayerTypeWalls: - v.renderWall(v.DS1.Tiles[tileY][tileX].Walls[layerIndex], offsetX, offsetY, target) + v.renderWall(v.DS1.Tiles[tileY][tileX].Walls[layerIndex], offsetX, offsetY, target, tileX, tileY) case d2enum.RegionLayerTypeShadows: - v.renderShadow(v.DS1.Tiles[tileY][tileX].Shadows[layerIndex], offsetX, offsetY, target) + v.renderShadow(v.DS1.Tiles[tileY][tileX].Shadows[layerIndex], offsetX, offsetY, target, tileX, tileY) } } +func (v *Region) getRandomTile(tiles []d2dt1.Tile) *d2dt1.Tile { + if len(tiles) == 1 { + return &tiles[0] + } + sort.Sort(ByRarity(tiles)) + s := 0 + for _, t := range tiles { + s += int(t.RarityFrameIndex) + } + rand.Seed(time.Now().UnixNano()) + r := 0 + if s != 0 { + r = rand.Intn(s) + 1 + } + for _, t := range tiles { + r -= int(t.RarityFrameIndex) + if r <= 0 { + return &t + } + } + return &tiles[0] +} + 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 + tiles := []d2dt1.Tile{} for _, tile := range v.Tiles { if tile.MainIndex != mainIndex || tile.SubIndex != subIndex || tile.Orientation != orientation { continue } - return &tile + tiles = append(tiles, tile) } - //log.Fatalf("Unknown tile ID [%d %d %d]", mainIndex, subIndex, orientation) - return nil + if len(tiles) == 0 { + log.Printf("Unknown tile ID [%d %d %d]\n", mainIndex, subIndex, orientation) + return nil + } + return v.getRandomTile(tiles) } -func (v *Region) renderFloor(tile d2ds1.FloorShadowRecord, offsetX, offsetY int, target *ebiten.Image) { - tileCacheIndex := (uint32(tile.MainIndex) << 16) | (uint32(tile.SubIndex) << 8) +func (v *Region) renderFloor(tile d2ds1.FloorShadowRecord, offsetX, offsetY int, target *ebiten.Image, tileX, tileY int) { + tileCacheIndex := fmt.Sprintf("%v-%v-%v-%v", tileY, tileX, tile.MainIndex, tile.SubIndex) tileCache, exists := v.FloorCache[tileCacheIndex] if !exists { v.FloorCache[tileCacheIndex] = v.generateFloorCache(tile) tileCache = v.FloorCache[tileCacheIndex] if tileCache == nil { - log.Println("Could not load floor tile") + log.Println("Could not load floor tile: " + tileCacheIndex) return } } if tileCache == nil { - log.Println("Nil tile cache") + log.Println("Nil tile cache: " + tileCacheIndex) return } opts := &ebiten.DrawImageOptions{} opts.GeoM.Translate(float64(offsetX+tileCache.XOffset), float64(offsetY+tileCache.YOffset)) target.DrawImage(tileCache.Image, opts) + return } -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)) +func (v *Region) renderWall(tile d2ds1.WallRecord, offsetX, offsetY int, target *ebiten.Image, tileX, tileY int) { + tileCacheIndex := fmt.Sprintf("%v-%v-%v-%v-%v", tileY, tileX, tile.MainIndex, tile.SubIndex, tile.Orientation) tileCache, exists := v.WallCache[tileCacheIndex] if !exists { v.WallCache[tileCacheIndex] = v.generateWallCache(tile) if v.WallCache[tileCacheIndex] == nil { - log.Println("Could not generate wall") + log.Println("Could not generate wall: " + tileCacheIndex) return } tileCache = v.WallCache[tileCacheIndex] } if tileCache == nil { - log.Println("Nil tile cache") + log.Println("Nil tile cache: " + tileCacheIndex) return } opts := &ebiten.DrawImageOptions{} @@ -185,19 +225,19 @@ func (v *Region) renderWall(tile d2ds1.WallRecord, offsetX, offsetY int, target target.DrawImage(tileCache.Image, opts) } -func (v *Region) renderShadow(tile d2ds1.FloorShadowRecord, offsetX, offsetY int, target *ebiten.Image) { - tileCacheIndex := (uint32(tile.MainIndex) << 16) + (uint32(tile.SubIndex) << 8) + 0 +func (v *Region) renderShadow(tile d2ds1.FloorShadowRecord, offsetX, offsetY int, target *ebiten.Image, tileX, tileY int) { + tileCacheIndex := fmt.Sprintf("%v-%v-%v-%v", tileY, tileX, tile.MainIndex, tile.SubIndex) tileCache, exists := v.ShadowCache[tileCacheIndex] if !exists { v.ShadowCache[tileCacheIndex] = v.generateShadowCache(tile) tileCache = v.ShadowCache[tileCacheIndex] if tileCache == nil { - log.Println("Could not load shadow tile") + log.Println("Could not load shadow tile: " + tileCacheIndex) return } } if tileCache == nil { - log.Println("Nil tile cache") + log.Println("Nil tile cache: " + tileCacheIndex) return } opts := &ebiten.DrawImageOptions{} diff --git a/go.mod b/go.mod index 420bfa50..971e1d29 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,10 @@ module github.com/OpenDiablo2/OpenDiablo2 go 1.12 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/hajimehoshi/ebiten v1.11.0-alpha.0.20191115171053-e42cff071c36 github.com/OpenDiablo2/D2Shared v0.0.0-20191117044836-c56c888bec7d github.com/hajimehoshi/ebiten v1.11.0-alpha.0.20191116200143-acc933b7c399 github.com/hajimehoshi/oto v0.5.3 // indirect @@ -10,4 +14,5 @@ require ( golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136 // indirect golang.org/x/mobile v0.0.0-20191115022231-f0c40035f2ba // indirect golang.org/x/sys v0.0.0-20191115151921-52ab43148777 // indirect + gopkg.in/alecthomas/kingpin.v2 v2.2.6 ) diff --git a/main.go b/main.go index 3f2e74c7..da447a1f 100644 --- a/main.go +++ b/main.go @@ -13,6 +13,7 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2core" "github.com/OpenDiablo2/D2Shared/d2data/d2mpq" "github.com/hajimehoshi/ebiten" + "gopkg.in/alecthomas/kingpin.v2" ) // GitBranch is set by the CI build process to the name of the branch @@ -22,6 +23,9 @@ var GitBranch string var GitCommit string var d2Engine d2core.Engine +var region = kingpin.Arg("region", "Region type id").Int() +var preset = kingpin.Arg("preset", "Level preset").Int() + func main() { //defer profile.Start(profile.CPUProfile).Stop() //runtime.LockOSThread() @@ -39,7 +43,12 @@ func main() { } d2mpq.InitializeCryptoBuffer() d2Engine = d2core.CreateEngine() - d2Engine.SetNextScene(d2scene.CreateMainMenu(&d2Engine, &d2Engine, d2Engine.UIManager, d2Engine.SoundManager)) + kingpin.Parse() + if *region == 0 { + d2Engine.SetNextScene(d2scene.CreateMainMenu(&d2Engine, &d2Engine, d2Engine.UIManager, d2Engine.SoundManager)) + } else { + d2Engine.SetNextScene(d2scene.CreateMapEngineTest(&d2Engine, &d2Engine, d2Engine.UIManager, d2Engine.SoundManager, *region, *preset)) + } ebiten.SetCursorVisible(false) ebiten.SetFullscreen(d2Engine.Settings.FullScreen) ebiten.SetRunnableInBackground(d2Engine.Settings.RunInBackground)