OpenDiablo2/Map/Region.go

304 lines
9.2 KiB
Go
Raw Normal View History

package Map
import (
"math"
"math/rand"
"strconv"
"github.com/essial/OpenDiablo2/PaletteDefs"
"github.com/hajimehoshi/ebiten"
"github.com/essial/OpenDiablo2/Common"
)
type TileCacheRecord struct {
Image *ebiten.Image
XOffset int
YOffset int
}
type Region struct {
levelType Common.LevelTypeRecord
2019-11-01 20:51:50 +00:00
levelPreset *Common.LevelPresetRecord
TileWidth int32
TileHeight int32
Tiles []Tile
DS1 *DS1
Palette Common.PaletteRec
TileCache map[uint32]*TileCacheRecord
}
type RegionLayerType int
const (
RegionLayerTypeFloors RegionLayerType = 0
RegionLayerTypeWalls RegionLayerType = 1
)
type RegionIdType int
const (
2019-11-01 20:51:50 +00:00
RegionAct1Town RegionIdType = 1
RegionAct1Wilderness RegionIdType = 2
RegionAct1Cave RegionIdType = 3
RegionAct1Crypt RegionIdType = 4
RegionAct1Monestary RegionIdType = 5
RegionAct1Courtyard RegionIdType = 6
RegionAct1Barracks RegionIdType = 7
RegionAct1Jail RegionIdType = 8
RegionAct1Cathedral RegionIdType = 9
RegionAct1Catacombs RegionIdType = 10
RegionAct1Tristram RegionIdType = 11
RegionAct2Town RegionIdType = 12
RegionAct2Sewer RegionIdType = 13
RegionAct2Harem RegionIdType = 14
RegionAct2Basement RegionIdType = 15
RegionAct2Desert RegionIdType = 16
RegionAct2Tomb RegionIdType = 17
RegionAct2Lair RegionIdType = 18
RegionAct2Arcane RegionIdType = 19
RegionAct3Town RegionIdType = 20
RegionAct3Jungle RegionIdType = 21
RegionAct3Kurast RegionIdType = 22
RegionAct3Spider RegionIdType = 23
RegionAct3Dungeon RegionIdType = 24
RegionAct3Sewer RegionIdType = 25
RegionAct4Town RegionIdType = 26
RegionAct4Mesa RegionIdType = 27
RegionAct4Lava RegionIdType = 28
RegonAct5Town RegionIdType = 29
RegionAct5Siege RegionIdType = 30
RegionAct5Barricade RegionIdType = 31
RegionAct5Temple RegionIdType = 32
RegionAct5IceCaves RegionIdType = 33
RegionAct5Baal RegionIdType = 34
RegionAct5Lava RegionIdType = 35
)
func LoadRegion(seed rand.Source, levelType RegionIdType, levelPreset int, fileProvider Common.FileProvider) *Region {
result := &Region{
levelType: Common.LevelTypes[levelType],
levelPreset: Common.LevelPresets[levelPreset],
Tiles: make([]Tile, 0),
TileCache: make(map[uint32]*TileCacheRecord),
}
result.Palette = Common.Palettes[PaletteDefs.PaletteType("act"+strconv.Itoa(int(result.levelType.Act)))]
2019-11-01 20:51:50 +00:00
//\bm := result.levelPreset.Dt1Mask
for _, levelTypeDt1 := range result.levelType.Files {
2019-11-01 20:51:50 +00:00
/*
if bm&1 == 0 {
bm >>= 1
continue
}
bm >>= 1
2019-11-01 20:51:50 +00:00
*/
if len(levelTypeDt1) == 0 || levelTypeDt1 == "" || levelTypeDt1 == "0" {
continue
}
dt1 := LoadDT1("/data/global/tiles/"+levelTypeDt1, fileProvider)
result.Tiles = append(result.Tiles, dt1.Tiles...)
}
levelFilesToPick := make([]string, 0)
for _, fileRecord := range result.levelPreset.Files {
if len(fileRecord) == 0 || fileRecord == "" || fileRecord == "0" {
continue
}
levelFilesToPick = append(levelFilesToPick, fileRecord)
}
random := rand.New(seed)
levelIndex := int(math.Round(float64(len(levelFilesToPick)-1) * random.Float64()))
levelFile := levelFilesToPick[levelIndex]
result.DS1 = LoadDS1("/data/global/tiles/"+levelFile, fileProvider)
result.TileWidth = result.DS1.Width
result.TileHeight = result.DS1.Height
return result
}
func (v *Region) RenderTile(offsetX, offsetY, tileX, tileY int, layerType RegionLayerType, layerIndex int, target *ebiten.Image) {
switch layerType {
case RegionLayerTypeFloors:
v.renderFloor(v.DS1.Tiles[tileY][tileX].Floors[layerIndex], offsetX, offsetY, target)
case RegionLayerTypeWalls:
v.renderWall(v.DS1.Tiles[tileY][tileX].Walls[layerIndex], offsetX, offsetY, target)
}
}
2019-11-01 20:51:50 +00:00
func (v *Region) getTile(mainIndex, subIndex, orientation int32) *Tile {
// TODO: Need to support randomly grabbing tile based on x/y as there can be multiple matches for same main/sub index
for _, tile := range v.Tiles {
if tile.MainIndex != mainIndex || tile.SubIndex != subIndex || tile.Orientation != orientation {
continue
}
2019-11-01 20:51:50 +00:00
return &tile
}
2019-11-01 20:51:50 +00:00
//log.Fatalf("Unknown tile ID [%d %d %d]", mainIndex, subIndex, orientation)
return nil
}
func (v *Region) renderFloor(tile FloorShadowRecord, offsetX, offsetY int, target *ebiten.Image) {
2019-11-01 20:51:50 +00:00
if tile.Hidden {
return
}
tileCacheIndex := (uint32(tile.MainIndex) << 16) + (uint32(tile.SubIndex) << 8)
tileCache := v.TileCache[tileCacheIndex]
if tileCache == nil {
v.TileCache[tileCacheIndex] = v.generateFloorCache(tile)
tileCache = v.TileCache[tileCacheIndex]
}
2019-11-01 20:51:50 +00:00
if tileCache == nil {
return
}
opts := &ebiten.DrawImageOptions{}
opts.GeoM.Translate(float64(offsetX+tileCache.XOffset), float64(offsetY+tileCache.YOffset))
target.DrawImage(tileCache.Image, opts)
}
2019-11-01 20:51:50 +00:00
func (v *Region) renderWall(tile WallRecord, offsetX, offsetY int, target *ebiten.Image) {
if tile.Hidden {
return
}
2019-11-01 20:51:50 +00:00
if tile.Prop1 == 0 {
return
}
tileCacheIndex := (uint32(tile.MainIndex) << 16) + (uint32(tile.SubIndex) << 8) + (uint32(tile.Orientation))
tileCache := v.TileCache[tileCacheIndex]
if tileCache == nil {
v.TileCache[tileCacheIndex] = v.generateWallCache(tile)
// TODO: Temporary hack
if v.TileCache[tileCacheIndex] == nil {
return
}
tileCache = v.TileCache[tileCacheIndex]
}
opts := &ebiten.DrawImageOptions{}
opts.GeoM.Translate(float64(offsetX+tileCache.XOffset), float64(offsetY+tileCache.YOffset))
target.DrawImage(tileCache.Image, opts)
}
2019-11-01 20:51:50 +00:00
func (v *Region) decodeFloorData(blocks []Block, pixels []byte, tileYOffset int32, tileWidth int32) {
for _, block := range blocks {
// TODO: Move this to a less stupid place
if block.Format == BlockFormatIsometric {
// 3D isometric decoding
xjump := []int32{14, 12, 10, 8, 6, 4, 2, 0, 2, 4, 6, 8, 10, 12, 14}
nbpix := []int32{4, 8, 12, 16, 20, 24, 28, 32, 28, 24, 20, 16, 12, 8, 4}
blockX := int32(block.X)
blockY := int32(block.Y)
length := int32(256)
x := int32(0)
y := int32(0)
idx := 0
for length > 0 {
x = xjump[y]
n := nbpix[y]
length -= n
for n > 0 {
colorIndex := block.EncodedData[idx]
if colorIndex != 0 {
pixelColor := v.Palette.Colors[colorIndex]
2019-11-01 20:51:50 +00:00
pixels[(4 * (((blockY + y) * tileWidth) + (blockX + x)))] = pixelColor.R
pixels[(4*(((blockY+y)*tileWidth)+(blockX+x)))+1] = pixelColor.G
pixels[(4*(((blockY+y)*tileWidth)+(blockX+x)))+2] = pixelColor.B
pixels[(4*(((blockY+y)*tileWidth)+(blockX+x)))+3] = 255
} else {
2019-11-01 20:51:50 +00:00
pixels[(4*(((blockY+y)*tileWidth)+(blockX+x)))+3] = 0
}
x++
n--
idx++
}
y++
}
} else {
// RLE Encoding
blockX := int32(block.X)
blockY := int32(block.Y)
x := int32(0)
y := int32(0)
idx := 0
length := block.Length
for length > 0 {
length -= 2
if (block.EncodedData[idx] + block.EncodedData[idx+1]) == 0 {
x = 0
y++
idx += 2
continue
}
length -= int32(block.EncodedData[idx+1])
x += int32(block.EncodedData[idx])
b2 := block.EncodedData[idx+1]
idx += 2
for b2 > 0 {
colorIndex := block.EncodedData[idx]
if colorIndex != 0 {
pixelColor := v.Palette.Colors[colorIndex]
2019-11-01 20:51:50 +00:00
pixels[(4 * (((blockY + y + tileYOffset) * tileWidth) + (blockX + x)))] = pixelColor.R
pixels[(4*(((blockY+y+tileYOffset)*tileWidth)+(blockX+x)))+1] = pixelColor.G
pixels[(4*(((blockY+y+tileYOffset)*tileWidth)+(blockX+x)))+2] = pixelColor.B
pixels[(4*(((blockY+y+tileYOffset)*tileWidth)+(blockX+x)))+3] = 255
} else {
2019-11-01 20:51:50 +00:00
pixels[(4*(((blockY+y+tileYOffset)*tileWidth)+(blockX+x)))+3] = 0
}
idx++
x++
b2--
}
}
}
}
2019-11-01 20:51:50 +00:00
}
func (v *Region) generateFloorCache(tile FloorShadowRecord) *TileCacheRecord {
tileData := v.getTile(int32(tile.MainIndex), int32(tile.SubIndex), 0)
if tileData == nil {
return nil
}
tileYMinimum := int32(0)
for _, block := range tileData.Blocks {
tileYMinimum = Common.MinInt32(tileYMinimum, int32(block.Y))
}
tileYOffset := Common.AbsInt32(tileYMinimum)
tileHeight := Common.AbsInt32(tileData.Height)
image, _ := ebiten.NewImage(int(tileData.Width), int(tileHeight), ebiten.FilterNearest)
pixels := make([]byte, 4*tileData.Width*tileHeight)
v.decodeFloorData(tileData.Blocks, pixels, tileYOffset, tileData.Width)
image.ReplacePixels(pixels)
return &TileCacheRecord{image, 0, 0}
}
func (v *Region) generateWallCache(tile WallRecord) *TileCacheRecord {
tileData := v.getTile(int32(tile.MainIndex), int32(tile.SubIndex), int32(tile.Orientation))
if tileData == nil {
return nil
}
tileYMinimum := int32(0)
for _, block := range tileData.Blocks {
tileYMinimum = Common.MinInt32(tileYMinimum, int32(block.Y))
}
tileYOffset := -tileYMinimum
tileHeight := Common.AbsInt32(tileData.Height)
image, _ := ebiten.NewImage(int(tileData.Width), int(tileHeight), ebiten.FilterNearest)
pixels := make([]byte, 4*tileData.Width*tileHeight)
v.decodeFloorData(tileData.Blocks, pixels, tileYOffset, tileData.Width)
image.ReplacePixels(pixels)
2019-11-01 20:51:50 +00:00
yAdjust := 0
if tile.Orientation == 15 {
// Roof
yAdjust = -int(tileData.RoofHeight)
} else if tile.Orientation > 15 {
// Lower walls
yAdjust = int(tileYMinimum) + 80
} else {
// Upper Walls
yAdjust = int(tileYMinimum) + 80
}
return &TileCacheRecord{
image,
2019-11-01 20:51:50 +00:00
0,
yAdjust,
}
}