2020-06-21 18:40:37 -04:00
|
|
|
package d2mapengine
|
|
|
|
|
|
|
|
import (
|
|
|
|
"log"
|
2020-06-23 01:05:01 -04:00
|
|
|
"strings"
|
2020-06-21 18:40:37 -04:00
|
|
|
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
|
|
|
|
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dt1"
|
|
|
|
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
|
|
|
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapentity"
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapstamp"
|
|
|
|
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2ds1"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Represents the map data for a specific location
|
|
|
|
type MapEngine struct {
|
2020-06-26 17:12:19 -04:00
|
|
|
seed int64 // The map seed
|
|
|
|
entities []d2mapentity.MapEntity // Entities on the map
|
|
|
|
tiles []d2ds1.TileRecord // The map tiles
|
|
|
|
size d2common.Size // The size of the map, in tiles
|
|
|
|
levelType d2datadict.LevelTypeRecord // The level type of this map
|
|
|
|
dt1TileData []d2dt1.Tile // The DT1 tile data
|
|
|
|
walkMesh []d2common.PathTile // The walk mesh
|
|
|
|
startSubTileX int // The starting X position
|
|
|
|
startSubTileY int // The starting Y position
|
|
|
|
dt1Files []string // The list of DS1 strings
|
2020-06-21 18:40:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Creates a new instance of the map engine
|
|
|
|
func CreateMapEngine() *MapEngine {
|
|
|
|
engine := &MapEngine{}
|
|
|
|
return engine
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *MapEngine) WalkMesh() *[]d2common.PathTile {
|
|
|
|
return &m.walkMesh
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the starting position on the map in sub-tiles
|
|
|
|
func (m *MapEngine) GetStartingPosition() (int, int) {
|
|
|
|
return m.startSubTileX, m.startSubTileY
|
|
|
|
}
|
|
|
|
|
2020-06-21 23:23:00 -04:00
|
|
|
func (m *MapEngine) ResetMap(levelType d2enum.RegionIdType, width, height int) {
|
2020-06-21 18:40:37 -04:00
|
|
|
m.entities = make([]d2mapentity.MapEntity, 0)
|
|
|
|
m.levelType = d2datadict.LevelTypes[levelType]
|
|
|
|
m.size = d2common.Size{Width: width, Height: height}
|
|
|
|
m.tiles = make([]d2ds1.TileRecord, width*height)
|
|
|
|
m.dt1TileData = make([]d2dt1.Tile, 0)
|
|
|
|
m.walkMesh = make([]d2common.PathTile, width*height*25)
|
2020-06-23 01:05:01 -04:00
|
|
|
m.dt1Files = make([]string, 0)
|
2020-06-21 18:40:37 -04:00
|
|
|
|
2020-06-24 14:35:49 -04:00
|
|
|
for idx := range m.levelType.Files {
|
|
|
|
m.addDT1(m.levelType.Files[idx])
|
2020-06-23 01:05:01 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *MapEngine) addDT1(fileName string) {
|
|
|
|
if len(fileName) == 0 || fileName == "0" {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
fileName = strings.ToLower(fileName)
|
|
|
|
for i := 0; i < len(m.dt1Files); i++ {
|
|
|
|
if m.dt1Files[i] == fileName {
|
|
|
|
return
|
2020-06-21 18:40:37 -04:00
|
|
|
}
|
|
|
|
}
|
2020-06-22 19:33:12 -04:00
|
|
|
|
2020-06-23 01:05:01 -04:00
|
|
|
fileData, err := d2asset.LoadFile("/data/global/tiles/" + fileName)
|
|
|
|
if err != nil {
|
2020-06-24 00:04:27 -04:00
|
|
|
log.Printf("Could not load /data/global/tiles/%s", fileName)
|
|
|
|
return
|
|
|
|
//panic(err)
|
2020-06-23 01:05:01 -04:00
|
|
|
}
|
|
|
|
dt1, _ := d2dt1.LoadDT1(fileData)
|
|
|
|
m.dt1TileData = append(m.dt1TileData, dt1.Tiles...)
|
|
|
|
m.dt1Files = append(m.dt1Files, fileName)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *MapEngine) AddDS1(fileName string) {
|
|
|
|
if len(fileName) == 0 || fileName == "0" {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
fileData, err := d2asset.LoadFile("/data/global/tiles/" + fileName)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
ds1, _ := d2ds1.LoadDS1(fileData)
|
2020-06-24 14:35:49 -04:00
|
|
|
for idx := range ds1.Files {
|
|
|
|
dt1File := ds1.Files[idx]
|
|
|
|
dt1File = strings.ToLower(dt1File)
|
2020-06-24 00:04:27 -04:00
|
|
|
dt1File = strings.Replace(dt1File, "c:", "", -1) // Yes they did...
|
|
|
|
dt1File = strings.Replace(dt1File, ".tg1", ".dt1", -1) // Yes they did...
|
2020-06-23 01:05:01 -04:00
|
|
|
dt1File = strings.Replace(dt1File, "\\d2\\data\\global\\tiles\\", "", -1)
|
|
|
|
m.addDT1(strings.Replace(dt1File, "\\", "/", -1))
|
|
|
|
}
|
2020-06-21 18:40:37 -04:00
|
|
|
}
|
|
|
|
|
2020-06-21 23:23:00 -04:00
|
|
|
func (m *MapEngine) FindTile(style, sequence, tileType int32) d2dt1.Tile {
|
2020-06-24 14:35:49 -04:00
|
|
|
for idx := range m.dt1TileData {
|
|
|
|
if m.dt1TileData[idx].Style == style && m.dt1TileData[idx].Sequence == sequence && m.dt1TileData[idx].Type == tileType {
|
|
|
|
return m.dt1TileData[idx]
|
2020-06-21 23:23:00 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
panic("Could not find the requested tile!")
|
|
|
|
}
|
|
|
|
|
2020-06-21 18:40:37 -04:00
|
|
|
// Returns the level type of this map
|
2020-06-26 17:12:19 -04:00
|
|
|
func (m *MapEngine) LevelType() d2datadict.LevelTypeRecord {
|
2020-06-21 18:40:37 -04:00
|
|
|
return m.levelType
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sets the seed of the map for generation
|
|
|
|
func (m *MapEngine) SetSeed(seed int64) {
|
|
|
|
log.Printf("Setting map engine seed to %d", seed)
|
|
|
|
m.seed = seed
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the size of the map (in sub-tiles)
|
|
|
|
func (m *MapEngine) Size() d2common.Size {
|
|
|
|
return m.size
|
|
|
|
}
|
|
|
|
|
2020-06-24 00:04:27 -04:00
|
|
|
func (m *MapEngine) Tile(x, y int) *d2ds1.TileRecord {
|
|
|
|
return &m.tiles[x+(y*m.size.Width)]
|
|
|
|
}
|
|
|
|
|
2020-06-21 18:40:37 -04:00
|
|
|
// Returns the map's tiles
|
|
|
|
func (m *MapEngine) Tiles() *[]d2ds1.TileRecord {
|
|
|
|
return &m.tiles
|
|
|
|
}
|
|
|
|
|
2020-06-24 08:10:48 -04:00
|
|
|
// Places a stamp at the specified location.
|
|
|
|
// Also adds any entities from the stamp to the map engine
|
2020-06-21 18:40:37 -04:00
|
|
|
func (m *MapEngine) PlaceStamp(stamp *d2mapstamp.Stamp, tileOffsetX, tileOffsetY int) {
|
|
|
|
stampSize := stamp.Size()
|
2020-06-24 08:10:48 -04:00
|
|
|
stampW := stampSize.Width
|
|
|
|
stampH := stampSize.Height
|
|
|
|
|
|
|
|
mapW := m.size.Width
|
|
|
|
mapH := m.size.Height
|
|
|
|
|
|
|
|
xMin := tileOffsetX
|
|
|
|
yMin := tileOffsetY
|
|
|
|
xMax := xMin + stampSize.Width
|
|
|
|
yMax := yMin + stampSize.Height
|
|
|
|
|
|
|
|
if (xMin < 0) || (yMin < 0) || (xMax > mapW) || (yMax > mapH) {
|
2020-06-21 18:40:37 -04:00
|
|
|
panic("Tried placing a stamp outside the bounds of the map")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy over the map tile data
|
2020-06-24 08:10:48 -04:00
|
|
|
for y := 0; y < stampH; y++ {
|
|
|
|
for x := 0; x < stampW; x++ {
|
|
|
|
targetTileIndex := m.tileCoordinateToIndex((x + xMin), (y + yMin))
|
|
|
|
stampTile := *stamp.Tile(x, y)
|
|
|
|
m.tiles[targetTileIndex] = stampTile
|
2020-06-21 18:40:37 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy over the entities
|
2020-06-22 19:33:12 -04:00
|
|
|
m.entities = append(m.entities, stamp.Entities(tileOffsetX, tileOffsetY)...)
|
2020-06-21 18:40:37 -04:00
|
|
|
}
|
|
|
|
|
2020-06-24 08:10:48 -04:00
|
|
|
// converts x,y tile coordinate into index in MapEngine.tiles
|
|
|
|
func (m *MapEngine) tileCoordinateToIndex(x, y int) int {
|
2020-06-24 14:35:49 -04:00
|
|
|
return x + (y * m.size.Width)
|
2020-06-24 08:10:48 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// converts tile index from MapEngine.tiles to x,y coordinate
|
|
|
|
func (m *MapEngine) tileIndexToCoordinate(index int) (int, int) {
|
|
|
|
return (index % m.size.Width), (index / m.size.Width)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns a reference to a map tile based on the tile X,Y coordinate
|
2020-06-21 18:40:37 -04:00
|
|
|
func (m *MapEngine) TileAt(tileX, tileY int) *d2ds1.TileRecord {
|
2020-06-24 08:10:48 -04:00
|
|
|
idx := m.tileCoordinateToIndex(tileX, tileY)
|
2020-06-21 18:40:37 -04:00
|
|
|
if idx < 0 || idx >= len(m.tiles) {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return &m.tiles[idx]
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns a reference to the map entities
|
|
|
|
func (m *MapEngine) Entities() *[]d2mapentity.MapEntity {
|
|
|
|
return &m.entities
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the map engine's seed
|
|
|
|
func (m *MapEngine) Seed() int64 {
|
|
|
|
return m.seed
|
|
|
|
}
|
|
|
|
|
|
|
|
// Adds an entity to the map engine
|
|
|
|
func (m *MapEngine) AddEntity(entity d2mapentity.MapEntity) {
|
|
|
|
m.entities = append(m.entities, entity)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Removes an entity from the map engine
|
|
|
|
func (m *MapEngine) RemoveEntity(entity d2mapentity.MapEntity) {
|
|
|
|
if entity == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
panic("Removing entities is not currently implemented")
|
|
|
|
//m.entities.Remove(entity)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *MapEngine) GetTiles(style, sequence, tileType int32) []d2dt1.Tile {
|
|
|
|
var tiles []d2dt1.Tile
|
2020-06-24 14:35:49 -04:00
|
|
|
for idx := range m.dt1TileData {
|
|
|
|
if m.dt1TileData[idx].Style != style || m.dt1TileData[idx].Sequence != sequence || m.dt1TileData[idx].Type != tileType {
|
2020-06-21 18:40:37 -04:00
|
|
|
continue
|
|
|
|
}
|
2020-06-24 14:35:49 -04:00
|
|
|
tiles = append(tiles, m.dt1TileData[idx])
|
2020-06-21 18:40:37 -04:00
|
|
|
}
|
|
|
|
if len(tiles) == 0 {
|
|
|
|
log.Printf("Unknown tile ID [%d %d %d]\n", style, sequence, tileType)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return tiles
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *MapEngine) GetStartPosition() (float64, float64) {
|
|
|
|
for tileY := 0; tileY < m.size.Height; tileY++ {
|
|
|
|
for tileX := 0; tileX < m.size.Width; tileX++ {
|
|
|
|
tile := m.tiles[tileX+(tileY*m.size.Width)]
|
2020-06-24 14:35:49 -04:00
|
|
|
for idx := range tile.Walls {
|
|
|
|
if tile.Walls[idx].Type.Special() && tile.Walls[idx].Style == 30 {
|
2020-06-21 18:40:37 -04:00
|
|
|
return float64(tileX) + 0.5, float64(tileY) + 0.5
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return m.GetCenterPosition()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the center of the map
|
|
|
|
func (m *MapEngine) GetCenterPosition() (float64, float64) {
|
|
|
|
return float64(m.size.Width) / 2.0, float64(m.size.Height) / 2.0
|
|
|
|
}
|
|
|
|
|
|
|
|
// Advances time on the map engine
|
|
|
|
func (m *MapEngine) Advance(tickTime float64) {
|
2020-06-24 14:35:49 -04:00
|
|
|
for idx := range m.entities {
|
|
|
|
m.entities[idx].Advance(tickTime)
|
2020-06-21 18:40:37 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-24 08:10:48 -04:00
|
|
|
// Checks if a tile exists
|
2020-06-21 18:40:37 -04:00
|
|
|
func (m *MapEngine) TileExists(tileX, tileY int) bool {
|
2020-06-24 08:10:48 -04:00
|
|
|
tileIndex := m.tileCoordinateToIndex(tileX, tileY)
|
|
|
|
if valid := (tileIndex >= 0) && (tileIndex <= len(m.tiles)); valid {
|
|
|
|
tile := m.tiles[tileIndex]
|
|
|
|
numFeatures := len(tile.Floors)
|
|
|
|
numFeatures += len(tile.Shadows)
|
|
|
|
numFeatures += len(tile.Walls)
|
|
|
|
numFeatures += len(tile.Substitutions)
|
|
|
|
return numFeatures > 0
|
2020-06-21 18:40:37 -04:00
|
|
|
}
|
2020-06-24 08:10:48 -04:00
|
|
|
return false
|
2020-06-21 18:40:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m *MapEngine) GenerateMap(regionType d2enum.RegionIdType, levelPreset int, fileIndex int, cacheTiles bool) {
|
2020-06-22 19:33:12 -04:00
|
|
|
region := d2mapstamp.LoadStamp(regionType, levelPreset, fileIndex)
|
2020-06-21 18:40:37 -04:00
|
|
|
regionSize := region.Size()
|
2020-06-21 23:23:00 -04:00
|
|
|
m.ResetMap(regionType, regionSize.Width, regionSize.Height)
|
2020-06-21 18:40:37 -04:00
|
|
|
m.PlaceStamp(region, 0, 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *MapEngine) GetTileData(style int32, sequence int32, tileType d2enum.TileType) *d2dt1.Tile {
|
2020-06-24 14:35:49 -04:00
|
|
|
for idx := range m.dt1TileData {
|
|
|
|
if m.dt1TileData[idx].Style == style && m.dt1TileData[idx].Sequence == sequence && m.dt1TileData[idx].Type == int32(tileType) {
|
|
|
|
return &m.dt1TileData[idx]
|
2020-06-21 18:40:37 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|