mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2024-10-31 16:27:18 -04:00
b5db51800c
Change location to contain canonical location, add field to get rounded location for tile rendering. If NPC has path, loop through path.
311 lines
10 KiB
Go
311 lines
10 KiB
Go
package d2mapengine
|
|
|
|
import (
|
|
"fmt"
|
|
"image/color"
|
|
"strings"
|
|
|
|
"github.com/OpenDiablo2/D2Shared/d2common/d2enum"
|
|
|
|
"github.com/OpenDiablo2/D2Shared/d2helper"
|
|
|
|
"github.com/OpenDiablo2/D2Shared/d2common/d2interface"
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core"
|
|
|
|
"github.com/OpenDiablo2/D2Shared/d2common"
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2audio"
|
|
"github.com/hajimehoshi/ebiten"
|
|
"github.com/hajimehoshi/ebiten/ebitenutil"
|
|
)
|
|
|
|
type RegionTile struct {
|
|
tileX, tileY int // tile coordinates
|
|
offX, offY int // world space coordinates of tile origin
|
|
}
|
|
|
|
type EngineRegion struct {
|
|
Rect d2common.Rectangle
|
|
Region *Region
|
|
Tiles []RegionTile
|
|
}
|
|
|
|
type Engine struct {
|
|
soundManager *d2audio.Manager
|
|
gameState *d2core.GameState
|
|
fileProvider d2interface.FileProvider
|
|
region int
|
|
regions []EngineRegion
|
|
OffsetX float64
|
|
OffsetY float64
|
|
ShowTiles int
|
|
Hero *d2core.Hero
|
|
}
|
|
|
|
func CreateMapEngine(gameState *d2core.GameState, soundManager *d2audio.Manager, fileProvider d2interface.FileProvider) *Engine {
|
|
result := &Engine{
|
|
gameState: gameState,
|
|
soundManager: soundManager,
|
|
fileProvider: fileProvider,
|
|
regions: make([]EngineRegion, 0),
|
|
}
|
|
return result
|
|
}
|
|
|
|
func (v *Engine) Region() *EngineRegion {
|
|
return &v.regions[v.region]
|
|
}
|
|
|
|
func (v *Engine) SetRegion(region int) {
|
|
v.region = region
|
|
}
|
|
|
|
func (v *Engine) GetRegion(regionIndex int) *EngineRegion {
|
|
return &v.regions[regionIndex]
|
|
}
|
|
|
|
func (v *Engine) CenterCameraOn(x, y float64) {
|
|
v.OffsetX = -(x - 400)
|
|
v.OffsetY = -(y - 300)
|
|
}
|
|
|
|
func (v *Engine) GenerateMap(regionType d2enum.RegionIdType, levelPreset int, fileIndex int) {
|
|
region := LoadRegion(v.gameState.Seed, 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,
|
|
})
|
|
|
|
for i, _ := range v.regions {
|
|
v.GenTiles(&v.regions[i])
|
|
v.GenTilesCache(&v.regions[i])
|
|
}
|
|
}
|
|
|
|
func (v *Engine) GenerateAct1Overworld() {
|
|
v.soundManager.PlayBGM("/data/global/music/Act1/town1.wav") // TODO: Temp stuff here
|
|
region := LoadRegion(v.gameState.Seed, 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(v.gameState.Seed, 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(v.gameState.Seed, 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,
|
|
})
|
|
}
|
|
|
|
for i, _ := range v.regions {
|
|
v.GenTiles(&v.regions[i])
|
|
v.GenTilesCache(&v.regions[i])
|
|
}
|
|
|
|
sx, sy := d2helper.IsoToScreen(region.StartX, region.StartY, 0, 0)
|
|
v.OffsetX = sx - 400
|
|
v.OffsetY = sy - 300
|
|
}
|
|
|
|
func (v *Engine) GetRegionAt(x, y int) *EngineRegion {
|
|
if v.regions == nil {
|
|
return nil
|
|
}
|
|
for _, region := range v.regions {
|
|
if !region.Rect.IsInRect(x, y) {
|
|
continue
|
|
}
|
|
return ®ion
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (v *Engine) Render(target *ebiten.Image) {
|
|
for _, region := range v.regions {
|
|
// X position of leftmost point of region
|
|
left := (region.Rect.Left - region.Rect.Bottom()) * 80
|
|
// Y position of top of region
|
|
top := (region.Rect.Left + region.Rect.Top) * 40
|
|
// X of right
|
|
right := (region.Rect.Right() - region.Rect.Top) * 80
|
|
// Y of bottom
|
|
bottom := (region.Rect.Right() + region.Rect.Bottom()) * 40
|
|
|
|
if -v.OffsetX+800 > float64(left) && -v.OffsetX < float64(right) && -v.OffsetY+600 > float64(top) && -v.OffsetY < float64(bottom) {
|
|
v.RenderRegion(region, target)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (v *Engine) GenTiles(region *EngineRegion) {
|
|
for y := 0; y < int(region.Region.TileHeight); y++ {
|
|
offX := -((y + region.Rect.Top) * 80) + (region.Rect.Left * 80)
|
|
offY := ((y + region.Rect.Top) * 40) + (region.Rect.Left * 40)
|
|
for x := 0; x < int(region.Region.TileWidth); x++ {
|
|
region.Tiles = append(region.Tiles, RegionTile{
|
|
tileX: x,
|
|
tileY: y,
|
|
offX: offX,
|
|
offY: offY,
|
|
})
|
|
|
|
offX += 80
|
|
offY += 40
|
|
}
|
|
}
|
|
}
|
|
|
|
func (v *Engine) GenTilesCache(region *EngineRegion) {
|
|
for tileIdx := range region.Tiles {
|
|
t := ®ion.Tiles[tileIdx]
|
|
if t.tileY < len(region.Region.DS1.Tiles) && t.tileX < len(region.Region.DS1.Tiles[t.tileY]) {
|
|
tile := ®ion.Region.DS1.Tiles[t.tileY][t.tileX]
|
|
for i := range tile.Floors {
|
|
if tile.Floors[i].Hidden || tile.Floors[i].Prop1 == 0 {
|
|
continue
|
|
}
|
|
region.Region.generateFloorCache(&tile.Floors[i], t.tileX, t.tileY)
|
|
}
|
|
for i := range tile.Shadows {
|
|
if tile.Shadows[i].Hidden || tile.Shadows[i].Prop1 == 0 {
|
|
continue
|
|
}
|
|
region.Region.generateShadowCache(&tile.Shadows[i], t.tileX, t.tileY)
|
|
}
|
|
for i := range tile.Walls {
|
|
if tile.Walls[i].Hidden || tile.Walls[i].Prop1 == 0 {
|
|
continue
|
|
}
|
|
region.Region.generateWallCache(&tile.Walls[i], t.tileX, t.tileY)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (v *Engine) RenderRegion(region EngineRegion, target *ebiten.Image) {
|
|
for tileIdx := range region.Tiles {
|
|
sx, sy := d2helper.IsoToScreen(float64(region.Tiles[tileIdx].tileX+region.Rect.Left), float64(region.Tiles[tileIdx].tileY+region.Rect.Top), v.OffsetX, v.OffsetY)
|
|
if sx > -160 && sy > -380 && sx <= 880 && sy <= 1240 {
|
|
region.Region.UpdateAnimations()
|
|
v.RenderPass1(region.Region, region.Tiles[tileIdx].offX, region.Tiles[tileIdx].offY, region.Tiles[tileIdx].tileX, region.Tiles[tileIdx].tileY, target)
|
|
if v.ShowTiles > 0 {
|
|
v.DrawTileLines(region.Region, region.Tiles[tileIdx].offX, region.Tiles[tileIdx].offY, region.Tiles[tileIdx].tileX, region.Tiles[tileIdx].tileY, target)
|
|
}
|
|
|
|
}
|
|
}
|
|
for tileIdx := range region.Tiles {
|
|
sx, sy := d2helper.IsoToScreen(float64(region.Tiles[tileIdx].tileX+region.Rect.Left), float64(region.Tiles[tileIdx].tileY+region.Rect.Top), v.OffsetX, v.OffsetY)
|
|
if sx > -160 && sy > -380 && sx <= 880 && sy <= 1240 {
|
|
v.RenderPass2(region.Region, region.Tiles[tileIdx].offX, region.Tiles[tileIdx].offY, region.Tiles[tileIdx].tileX, region.Tiles[tileIdx].tileY, target)
|
|
}
|
|
}
|
|
for tileIdx := range region.Tiles {
|
|
sx, sy := d2helper.IsoToScreen(float64(region.Tiles[tileIdx].tileX+region.Rect.Left), float64(region.Tiles[tileIdx].tileY+region.Rect.Top), v.OffsetX, v.OffsetY)
|
|
if sx > -160 && sy > -380 && sx <= 880 && sy <= 1240 {
|
|
v.RenderPass3(region.Region, region.Tiles[tileIdx].offX, region.Tiles[tileIdx].offY, region.Tiles[tileIdx].tileX, region.Tiles[tileIdx].tileY, target)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (v *Engine) RenderPass1(region *Region, offX, offY, x, y int, target *ebiten.Image) {
|
|
tile := region.DS1.Tiles[y][x]
|
|
// Draw lower walls
|
|
for i := range tile.Walls {
|
|
if !tile.Walls[i].Type.LowerWall() || tile.Walls[i].Prop1 == 0 || tile.Walls[i].Hidden {
|
|
continue
|
|
}
|
|
region.RenderTile(offX+int(v.OffsetX), offY+int(v.OffsetY), x, y, d2enum.RegionLayerTypeWalls, i, target)
|
|
}
|
|
|
|
for i := range tile.Floors {
|
|
if tile.Floors[i].Hidden || tile.Floors[i].Prop1 == 0 {
|
|
continue
|
|
}
|
|
region.RenderTile(offX+int(v.OffsetX), offY+int(v.OffsetY), x, y, d2enum.RegionLayerTypeFloors, i, target)
|
|
}
|
|
for i := range tile.Shadows {
|
|
if tile.Shadows[i].Hidden || tile.Shadows[i].Prop1 == 0 {
|
|
continue
|
|
}
|
|
region.RenderTile(offX+int(v.OffsetX), offY+int(v.OffsetY), x, y, d2enum.RegionLayerTypeShadows, i, target)
|
|
}
|
|
|
|
}
|
|
|
|
func (v *Engine) RenderPass2(region *Region, offX, offY, x, y int, target *ebiten.Image) {
|
|
tile := region.DS1.Tiles[y][x]
|
|
|
|
// Draw upper walls
|
|
for i := range tile.Walls {
|
|
if !tile.Walls[i].Type.UpperWall() || tile.Walls[i].Hidden {
|
|
continue
|
|
}
|
|
region.RenderTile(offX+int(v.OffsetX), offY+int(v.OffsetY), x, y, d2enum.RegionLayerTypeWalls, i, target)
|
|
}
|
|
|
|
for _, obj := range region.AnimationEntities {
|
|
if obj.TileX == x && obj.TileY == y {
|
|
obj.Render(target, offX+int(v.OffsetX), offY+int(v.OffsetY))
|
|
}
|
|
}
|
|
for _, npc := range region.NPCs {
|
|
if npc.AnimatedEntity.TileX == x && npc.AnimatedEntity.TileY == y {
|
|
npc.Render(target, offX+int(v.OffsetX), offY+int(v.OffsetY))
|
|
}
|
|
}
|
|
if v.Hero != nil && v.Hero.AnimatedEntity.TileX == x && v.Hero.AnimatedEntity.TileY == y {
|
|
v.Hero.Render(target, offX+int(v.OffsetX), offY+int(v.OffsetY))
|
|
}
|
|
}
|
|
|
|
func (v *Engine) RenderPass3(region *Region, offX, offY, x, y int, target *ebiten.Image) {
|
|
tile := region.DS1.Tiles[y][x]
|
|
// Draw ceilings
|
|
for i := range tile.Walls {
|
|
if tile.Walls[i].Type != d2enum.Roof {
|
|
continue
|
|
}
|
|
region.RenderTile(offX+int(v.OffsetX), offY+int(v.OffsetY), x, y, d2enum.RegionLayerTypeWalls, i, target)
|
|
}
|
|
}
|
|
|
|
func (v *Engine) DrawTileLines(region *Region, offX, offY, x, y int, target *ebiten.Image) {
|
|
if v.ShowTiles > 0 {
|
|
subtileColor := color.RGBA{80, 80, 255, 100}
|
|
tileColor := color.RGBA{255, 255, 255, 255}
|
|
|
|
ebitenutil.DrawLine(target, float64(offX)+v.OffsetX, float64(offY)+v.OffsetY, float64(offX)+v.OffsetX+80, float64(offY)+v.OffsetY+40, tileColor)
|
|
ebitenutil.DrawLine(target, float64(offX)+v.OffsetX, float64(offY)+v.OffsetY, float64(offX)+v.OffsetX-80, float64(offY)+v.OffsetY+40, tileColor)
|
|
|
|
coords := fmt.Sprintf("%v,%v", x, y)
|
|
ebitenutil.DebugPrintAt(target, coords, offX+int(v.OffsetX)-10, offY+int(v.OffsetY)+10)
|
|
|
|
if v.ShowTiles > 1 {
|
|
for i := 1; i <= 4; i++ {
|
|
x := (16 * i)
|
|
y := (8 * i)
|
|
ebitenutil.DrawLine(target, float64(offX-x)+v.OffsetX, float64(offY+y)+v.OffsetY,
|
|
float64(offX-x)+v.OffsetX+80, float64(offY+y)+v.OffsetY+40, subtileColor)
|
|
ebitenutil.DrawLine(target, float64(offX+x)+v.OffsetX, float64(offY+y)+v.OffsetY,
|
|
float64(offX+x)+v.OffsetX-80, float64(offY+y)+v.OffsetY+40, subtileColor)
|
|
}
|
|
|
|
tile := region.DS1.Tiles[y][x]
|
|
for i := range tile.Floors {
|
|
floorSpec := fmt.Sprintf("f: %v-%v", tile.Floors[i].Style, tile.Floors[i].Sequence)
|
|
ebitenutil.DebugPrintAt(target, floorSpec, offX+int(v.OffsetX)-20, offY+int(v.OffsetY)+10+((i+1)*14))
|
|
}
|
|
// wallSpec := fmt.Sprintf("w: %v-%v", tile.Walls[0].Style, tile.Walls[0].Sequence)
|
|
// ebitenutil.DebugPrintAt(target, wallSpec, offX+int(v.OffsetX)-20, offY+int(v.OffsetY)+34)
|
|
}
|
|
}
|
|
}
|