mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2025-02-20 07:27:19 -05:00
Engine and region reorganization in preperation for spatial partitioning (#249)
* Engine and region reorg * Remove unnecessary file diff * Remove extra render of debug mesh
This commit is contained in:
parent
5b6da2434f
commit
a194913609
@ -8,7 +8,6 @@ import (
|
||||
"github.com/OpenDiablo2/D2Shared/d2common/d2resource"
|
||||
"github.com/OpenDiablo2/D2Shared/d2data/d2datadict"
|
||||
"github.com/OpenDiablo2/D2Shared/d2data/d2dc6"
|
||||
"github.com/OpenDiablo2/D2Shared/d2helper"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2audio"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2corecommon/d2coreinterface"
|
||||
@ -27,7 +26,8 @@ type Game struct {
|
||||
pentSpinLeft d2render.Sprite
|
||||
pentSpinRight d2render.Sprite
|
||||
testLabel d2ui.Label
|
||||
mapEngine *d2mapengine.Engine
|
||||
mapEngine *d2mapengine.MapEngine
|
||||
hero *d2core.Hero
|
||||
}
|
||||
|
||||
func CreateGame(
|
||||
@ -72,25 +72,23 @@ func (v *Game) Load() []func() {
|
||||
},
|
||||
func() {
|
||||
v.mapEngine = d2mapengine.CreateMapEngine(v.gameState, v.soundManager, v.fileProvider)
|
||||
// TODO: This needs to be different depending on the act of the player
|
||||
v.mapEngine.GenerateMap(d2enum.RegionAct1Town, 1, 0)
|
||||
v.mapEngine.SetRegion(0)
|
||||
region := v.mapEngine.Region()
|
||||
rx, ry := d2helper.IsoToScreen(region.Region.StartX, region.Region.StartY, 0, 0)
|
||||
v.mapEngine.CenterCameraOn(rx, ry)
|
||||
v.mapEngine.Hero = d2core.CreateHero(
|
||||
int32((region.Region.StartX*5)+3),
|
||||
int32((region.Region.StartY*5)+3),
|
||||
|
||||
startX, startY := v.mapEngine.GetStartPosition()
|
||||
v.hero = d2core.CreateHero(
|
||||
int32(startX*5)+3,
|
||||
int32(startY*5)+3,
|
||||
0,
|
||||
v.gameState.HeroType,
|
||||
v.gameState.Equipment,
|
||||
v.fileProvider)
|
||||
v.fileProvider,
|
||||
)
|
||||
v.mapEngine.AddEntity(v.hero)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (v *Game) Unload() {
|
||||
|
||||
}
|
||||
|
||||
func (v Game) Render(screen *ebiten.Image) {
|
||||
@ -99,40 +97,13 @@ func (v Game) Render(screen *ebiten.Image) {
|
||||
}
|
||||
|
||||
func (v *Game) Update(tickTime float64) {
|
||||
// TODO: Pathfinding
|
||||
v.mapEngine.Advance(tickTime)
|
||||
|
||||
if v.mapEngine.Hero.AnimatedEntity.LocationX != v.mapEngine.Hero.AnimatedEntity.TargetX ||
|
||||
v.mapEngine.Hero.AnimatedEntity.LocationY != v.mapEngine.Hero.AnimatedEntity.TargetY {
|
||||
v.mapEngine.Hero.AnimatedEntity.Step(tickTime)
|
||||
}
|
||||
|
||||
for _, npc := range v.mapEngine.Region().Region.NPCs {
|
||||
|
||||
if npc.HasPaths &&
|
||||
npc.AnimatedEntity.LocationX == npc.AnimatedEntity.TargetX &&
|
||||
npc.AnimatedEntity.LocationY == npc.AnimatedEntity.TargetY &&
|
||||
npc.AnimatedEntity.Wait() {
|
||||
// If at the target, set target to the next path.
|
||||
path := npc.NextPath()
|
||||
npc.AnimatedEntity.SetTarget(
|
||||
float64(path.X),
|
||||
float64(path.Y),
|
||||
path.Action,
|
||||
)
|
||||
}
|
||||
|
||||
if npc.AnimatedEntity.LocationX != npc.AnimatedEntity.TargetX ||
|
||||
npc.AnimatedEntity.LocationY != npc.AnimatedEntity.TargetY {
|
||||
npc.AnimatedEntity.Step(tickTime)
|
||||
}
|
||||
|
||||
}
|
||||
rx, ry := v.mapEngine.WorldToOrtho(v.hero.AnimatedEntity.LocationX/5, v.hero.AnimatedEntity.LocationY/5)
|
||||
v.mapEngine.MoveCameraTo(rx, ry)
|
||||
|
||||
if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) {
|
||||
px, py := v.mapEngine.ScreenToIso(ebiten.CursorPosition())
|
||||
v.mapEngine.Hero.AnimatedEntity.SetTarget(px*5, py*5, 1)
|
||||
px, py := v.mapEngine.ScreenToWorld(ebiten.CursorPosition())
|
||||
v.hero.AnimatedEntity.SetTarget(px*5, py*5, 1)
|
||||
}
|
||||
|
||||
rx, ry := d2helper.IsoToScreen(v.mapEngine.Hero.AnimatedEntity.LocationX/5, v.mapEngine.Hero.AnimatedEntity.LocationY/5, 0, 0)
|
||||
v.mapEngine.CenterCameraOn(rx, ry)
|
||||
}
|
||||
|
@ -5,8 +5,6 @@ import (
|
||||
"math"
|
||||
"os"
|
||||
|
||||
"github.com/OpenDiablo2/D2Shared/d2helper"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core"
|
||||
|
||||
"github.com/OpenDiablo2/D2Shared/d2common/d2interface"
|
||||
@ -88,7 +86,7 @@ type MapEngineTest struct {
|
||||
fileProvider d2interface.FileProvider
|
||||
sceneProvider d2coreinterface.SceneProvider
|
||||
gameState *d2core.GameState
|
||||
mapEngine *d2mapengine.Engine
|
||||
mapEngine *d2mapengine.MapEngine
|
||||
|
||||
//TODO: this is region specific properties, should be refactored for multi-region rendering
|
||||
currentRegion int
|
||||
@ -96,6 +94,7 @@ type MapEngineTest struct {
|
||||
fileIndex int
|
||||
regionSpec RegionSpec
|
||||
filesCount int
|
||||
debugVisLevel int
|
||||
}
|
||||
|
||||
func CreateMapEngineTest(
|
||||
@ -149,9 +148,8 @@ func (v *MapEngineTest) LoadRegionByIndex(n int, levelPreset, fileIndex int) {
|
||||
v.mapEngine = d2mapengine.CreateMapEngine(v.gameState, v.soundManager, v.fileProvider) // necessary for map name update
|
||||
v.mapEngine.GenerateMap(d2enum.RegionIdType(n), levelPreset, fileIndex)
|
||||
}
|
||||
isox, isoy := d2helper.IsoToScreen(float64(v.mapEngine.GetRegion(0).Rect.Width)/2,
|
||||
float64(v.mapEngine.GetRegion(0).Rect.Height)/2, 0, 0)
|
||||
v.mapEngine.CenterCameraOn(isox, isoy)
|
||||
|
||||
v.mapEngine.MoveCameraTo(v.mapEngine.WorldToOrtho(v.mapEngine.GetCenterPosition()))
|
||||
}
|
||||
|
||||
func (v *MapEngineTest) Load() []func() {
|
||||
@ -172,32 +170,36 @@ func (v *MapEngineTest) Unload() {
|
||||
|
||||
func (v *MapEngineTest) Render(screen *ebiten.Image) {
|
||||
v.mapEngine.Render(screen)
|
||||
actualX := v.uiManager.CursorX
|
||||
actualY := v.uiManager.CursorY
|
||||
tileX, tileY := v.mapEngine.ScreenToIso(actualX, actualY)
|
||||
subtileX := int(math.Ceil(math.Mod((tileX*10), 10))) / 2
|
||||
subtileY := int(math.Ceil(math.Mod((tileY*10), 10))) / 2
|
||||
curRegion := v.mapEngine.GetRegionAt(int(tileX), int(tileY))
|
||||
screenX := v.uiManager.CursorX
|
||||
screenY := v.uiManager.CursorY
|
||||
worldX, worldY := v.mapEngine.ScreenToWorld(screenX, screenY)
|
||||
subtileX := int(math.Ceil(math.Mod((worldX*10), 10))) / 2
|
||||
subtileY := int(math.Ceil(math.Mod((worldY*10), 10))) / 2
|
||||
curRegion := v.mapEngine.GetRegionAtTile(int(worldX), int(worldY))
|
||||
if curRegion == nil {
|
||||
return
|
||||
}
|
||||
|
||||
tileRect := curRegion.GetTileRect()
|
||||
line := fmt.Sprintf("%d, %d (Tile %d.%d, %d.%d)",
|
||||
actualX,
|
||||
actualY,
|
||||
int(math.Floor(tileX))-curRegion.Rect.Left,
|
||||
screenX,
|
||||
screenY,
|
||||
int(math.Floor(worldX))-tileRect.Left,
|
||||
subtileX,
|
||||
int(math.Floor(tileY))-curRegion.Rect.Top,
|
||||
int(math.Floor(worldY))-tileRect.Top,
|
||||
subtileY,
|
||||
)
|
||||
|
||||
levelFilesToPick := make([]string, 0)
|
||||
fileIndex := v.fileIndex
|
||||
for n, fileRecord := range curRegion.Region.LevelPreset.Files {
|
||||
levelPreset := curRegion.GetLevelPreset()
|
||||
regionPath := curRegion.GetPath()
|
||||
for n, fileRecord := range levelPreset.Files {
|
||||
if len(fileRecord) == 0 || fileRecord == "" || fileRecord == "0" {
|
||||
continue
|
||||
}
|
||||
levelFilesToPick = append(levelFilesToPick, fileRecord)
|
||||
if fileRecord == curRegion.Region.RegionPath {
|
||||
if fileRecord == regionPath {
|
||||
fileIndex = n
|
||||
}
|
||||
}
|
||||
@ -206,14 +208,16 @@ func (v *MapEngineTest) Render(screen *ebiten.Image) {
|
||||
}
|
||||
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/%v [%v, %v]", curRegion.Region.RegionPath, fileIndex+1, v.filesCount, v.currentRegion, v.levelPreset), 5, 29)
|
||||
ebitenutil.DebugPrintAt(screen, "Map: "+curRegion.GetLevelType().Name, 5, 17)
|
||||
ebitenutil.DebugPrintAt(screen, fmt.Sprintf("%v: %v/%v [%v, %v]", 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) {
|
||||
v.mapEngine.Advance(tickTime)
|
||||
|
||||
ctrlPressed := v.uiManager.KeyPressed(ebiten.KeyControl)
|
||||
shiftPressed := v.uiManager.KeyPressed(ebiten.KeyShift)
|
||||
|
||||
@ -236,11 +240,13 @@ func (v *MapEngineTest) Update(tickTime float64) {
|
||||
}
|
||||
|
||||
if inpututil.IsKeyJustPressed(ebiten.KeyF7) {
|
||||
if v.mapEngine.ShowTiles < 2 {
|
||||
v.mapEngine.ShowTiles++
|
||||
if v.debugVisLevel < 2 {
|
||||
v.debugVisLevel++
|
||||
} else {
|
||||
v.mapEngine.ShowTiles = 0
|
||||
v.debugVisLevel = 0
|
||||
}
|
||||
|
||||
v.mapEngine.SetDebugVisLevel(v.debugVisLevel)
|
||||
}
|
||||
|
||||
if v.uiManager.KeyPressed(ebiten.KeyEscape) {
|
||||
|
@ -43,6 +43,18 @@ func CreateHero(x, y int32, direction int, heroType d2enum.Hero, equipment Chara
|
||||
return result
|
||||
}
|
||||
|
||||
func (v *Hero) Advance(tickTime float64) {
|
||||
// TODO: Pathfinding
|
||||
if v.AnimatedEntity.LocationX != v.AnimatedEntity.TargetX ||
|
||||
v.AnimatedEntity.LocationY != v.AnimatedEntity.TargetY {
|
||||
v.AnimatedEntity.Step(tickTime)
|
||||
}
|
||||
}
|
||||
|
||||
func (v *Hero) Render(target *ebiten.Image, offsetX, offsetY int) {
|
||||
v.AnimatedEntity.Render(target, offsetX, offsetY)
|
||||
}
|
||||
|
||||
func (v *Hero) GetPosition() (float64, float64) {
|
||||
return v.AnimatedEntity.GetPosition()
|
||||
}
|
||||
|
@ -46,3 +46,27 @@ func (v *NPC) SetPaths(paths []d2common.Path) {
|
||||
func (v *NPC) Render(target *ebiten.Image, offsetX, offsetY int) {
|
||||
v.AnimatedEntity.Render(target, offsetX, offsetY)
|
||||
}
|
||||
|
||||
func (v *NPC) GetPosition() (float64, float64) {
|
||||
return v.AnimatedEntity.GetPosition()
|
||||
}
|
||||
|
||||
func (v *NPC) Advance(tickTime float64) {
|
||||
if v.HasPaths &&
|
||||
v.AnimatedEntity.LocationX == v.AnimatedEntity.TargetX &&
|
||||
v.AnimatedEntity.LocationY == v.AnimatedEntity.TargetY &&
|
||||
v.AnimatedEntity.Wait() {
|
||||
// If at the target, set target to the next path.
|
||||
path := v.NextPath()
|
||||
v.AnimatedEntity.SetTarget(
|
||||
float64(path.X),
|
||||
float64(path.Y),
|
||||
path.Action,
|
||||
)
|
||||
}
|
||||
|
||||
if v.AnimatedEntity.LocationX != v.AnimatedEntity.TargetX ||
|
||||
v.AnimatedEntity.LocationY != v.AnimatedEntity.TargetY {
|
||||
v.AnimatedEntity.Step(tickTime)
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,11 @@ package d2render
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"math"
|
||||
"math/rand"
|
||||
"strings"
|
||||
|
||||
"github.com/OpenDiablo2/D2Shared/d2common/d2enum"
|
||||
"github.com/OpenDiablo2/D2Shared/d2common/d2interface"
|
||||
"github.com/OpenDiablo2/D2Shared/d2data"
|
||||
@ -10,10 +15,6 @@ import (
|
||||
"github.com/OpenDiablo2/D2Shared/d2data/d2dcc"
|
||||
"github.com/OpenDiablo2/D2Shared/d2helper"
|
||||
"github.com/hajimehoshi/ebiten"
|
||||
"log"
|
||||
"math"
|
||||
"math/rand"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var DccLayerNames = []string{"HD", "TR", "LG", "RA", "LA", "RH", "LH", "SH", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8"}
|
||||
@ -416,3 +417,11 @@ func angleToDirection(angle float64, numberOfDirections int) int {
|
||||
|
||||
return newDirection
|
||||
}
|
||||
|
||||
func (v *AnimatedEntity) Advance(tickTime float64) {
|
||||
|
||||
}
|
||||
|
||||
func (v *AnimatedEntity) GetPosition() (float64, float64) {
|
||||
return float64(v.TileX), float64(v.TileY)
|
||||
}
|
||||
|
@ -1,52 +1,40 @@
|
||||
package d2mapengine
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image/color"
|
||||
"strings"
|
||||
|
||||
"github.com/OpenDiablo2/D2Shared/d2common/d2enum"
|
||||
|
||||
"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"
|
||||
|
||||
"github.com/OpenDiablo2/D2Shared/d2common/d2enum"
|
||||
"github.com/OpenDiablo2/D2Shared/d2common/d2interface"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2audio"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core"
|
||||
)
|
||||
|
||||
type RegionTile struct {
|
||||
tileX, tileY int // tile coordinates
|
||||
offX, offY int // world space coordinates of tile origin
|
||||
type MapEntity interface {
|
||||
Render(target *ebiten.Image, screenX, screenY int)
|
||||
GetPosition() (float64, float64)
|
||||
Advance(tickTime float64)
|
||||
}
|
||||
|
||||
type EngineRegion struct {
|
||||
Rect d2common.Rectangle
|
||||
Region *Region
|
||||
Tiles []RegionTile
|
||||
}
|
||||
|
||||
type Engine struct {
|
||||
type MapEngine struct {
|
||||
soundManager *d2audio.Manager
|
||||
gameState *d2core.GameState
|
||||
fileProvider d2interface.FileProvider
|
||||
region int
|
||||
regions []EngineRegion
|
||||
ShowTiles int
|
||||
Hero *d2core.Hero
|
||||
|
||||
debugVisLevel int
|
||||
|
||||
regions []*MapRegion
|
||||
entities []MapEntity
|
||||
viewport *Viewport
|
||||
camera Camera
|
||||
}
|
||||
|
||||
func CreateMapEngine(gameState *d2core.GameState, soundManager *d2audio.Manager, fileProvider d2interface.FileProvider) *Engine {
|
||||
engine := &Engine{
|
||||
func CreateMapEngine(gameState *d2core.GameState, soundManager *d2audio.Manager, fileProvider d2interface.FileProvider) *MapEngine {
|
||||
engine := &MapEngine{
|
||||
gameState: gameState,
|
||||
soundManager: soundManager,
|
||||
fileProvider: fileProvider,
|
||||
regions: make([]EngineRegion, 0),
|
||||
viewport: NewViewport(0, 0, 800, 600),
|
||||
}
|
||||
|
||||
@ -54,315 +42,108 @@ func CreateMapEngine(gameState *d2core.GameState, soundManager *d2audio.Manager,
|
||||
return engine
|
||||
}
|
||||
|
||||
func (v *Engine) Region() *EngineRegion {
|
||||
return &v.regions[v.region]
|
||||
func (me *MapEngine) GetStartPosition() (float64, float64) {
|
||||
var startX, startY float64
|
||||
if len(me.regions) > 0 {
|
||||
region := me.regions[0]
|
||||
startX, startY = region.getStartTilePosition()
|
||||
}
|
||||
|
||||
return startX, startY
|
||||
}
|
||||
|
||||
func (v *Engine) SetRegion(region int) {
|
||||
v.region = region
|
||||
func (me *MapEngine) GetCenterPosition() (float64, float64) {
|
||||
var centerX, centerY float64
|
||||
if len(me.regions) > 0 {
|
||||
region := me.regions[0]
|
||||
centerX = float64(region.tileRect.Left) + float64(region.tileRect.Width)/2
|
||||
centerY = float64(region.tileRect.Top) + float64(region.tileRect.Height)/2
|
||||
}
|
||||
|
||||
return centerX, centerY
|
||||
}
|
||||
|
||||
func (v *Engine) GetRegion(regionIndex int) *EngineRegion {
|
||||
return &v.regions[regionIndex]
|
||||
func (me *MapEngine) MoveCameraTo(x, y float64) {
|
||||
me.camera.MoveTo(x, y)
|
||||
}
|
||||
|
||||
func (v *Engine) CenterCameraOn(x, y float64) {
|
||||
v.camera.MoveTo(x, y)
|
||||
func (me *MapEngine) MoveCameraBy(x, y float64) {
|
||||
me.camera.MoveBy(x, y)
|
||||
}
|
||||
|
||||
func (v *Engine) MoveCameraBy(x, y float64) {
|
||||
v.camera.MoveBy(x, y)
|
||||
func (me *MapEngine) ScreenToWorld(x, y int) (float64, float64) {
|
||||
return me.viewport.ScreenToWorld(x, y)
|
||||
}
|
||||
|
||||
func (v *Engine) ScreenToIso(x, y int) (float64, float64) {
|
||||
return v.viewport.ScreenToIso(x, y)
|
||||
func (me *MapEngine) ScreenToOrtho(x, y int) (float64, float64) {
|
||||
return me.viewport.ScreenToOrtho(x, y)
|
||||
}
|
||||
|
||||
func (v *Engine) ScreenToWorld(x, y int) (float64, float64) {
|
||||
return v.viewport.ScreenToWorld(x, y)
|
||||
func (me *MapEngine) WorldToOrtho(x, y float64) (float64, float64) {
|
||||
return me.viewport.WorldToOrtho(x, y)
|
||||
}
|
||||
|
||||
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,
|
||||
})
|
||||
func (me *MapEngine) SetDebugVisLevel(debugVisLevel int) {
|
||||
me.debugVisLevel = debugVisLevel
|
||||
}
|
||||
|
||||
for i, _ := range v.regions {
|
||||
v.GenTiles(&v.regions[i])
|
||||
v.GenTilesCache(&v.regions[i])
|
||||
func (me *MapEngine) GenerateMap(regionType d2enum.RegionIdType, levelPreset int, fileIndex int) {
|
||||
region, entities := loadRegion(me.gameState.Seed, 0, 0, regionType, levelPreset, me.fileProvider, fileIndex)
|
||||
me.regions = append(me.regions, region)
|
||||
me.entities = append(me.entities, entities...)
|
||||
}
|
||||
|
||||
func (me *MapEngine) GenerateAct1Overworld() {
|
||||
me.soundManager.PlayBGM("/data/global/music/Act1/town1.wav") // TODO: Temp stuff here
|
||||
|
||||
region, entities := loadRegion(me.gameState.Seed, 0, 0, d2enum.RegionAct1Town, 1, me.fileProvider, -1)
|
||||
me.regions = append(me.regions, region)
|
||||
me.entities = append(me.entities, entities...)
|
||||
|
||||
if strings.Contains(region.regionPath, "E1") {
|
||||
region, entities := loadRegion(me.gameState.Seed, int(region.tileRect.Width-1), 0, d2enum.RegionAct1Town, 2, me.fileProvider, -1)
|
||||
me.regions = append(me.regions, region)
|
||||
me.entities = append(me.entities, entities...)
|
||||
} else if strings.Contains(region.regionPath, "S1") {
|
||||
region, entities := loadRegion(me.gameState.Seed, 0, int(region.tileRect.Height-1), d2enum.RegionAct1Town, 3, me.fileProvider, -1)
|
||||
me.regions = append(me.regions, region)
|
||||
me.entities = append(me.entities, entities...)
|
||||
}
|
||||
}
|
||||
|
||||
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])
|
||||
}
|
||||
|
||||
v.camera.MoveTo(v.viewport.IsoToWorld(region.StartX, region.StartY))
|
||||
}
|
||||
|
||||
func (v *Engine) GetRegionAt(x, y int) *EngineRegion {
|
||||
for _, region := range v.regions {
|
||||
if region.Rect.IsInRect(x, y) {
|
||||
return ®ion
|
||||
func (me *MapEngine) GetRegionAtTile(x, y int) *MapRegion {
|
||||
for _, region := range me.regions {
|
||||
if region.tileRect.IsInRect(x, y) {
|
||||
return region
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *Engine) Render(target *ebiten.Image) {
|
||||
for _, region := range v.regions {
|
||||
// X position of leftmost point of region
|
||||
left := float64((region.Rect.Left - region.Rect.Bottom()) * 80)
|
||||
// Y position of top of region
|
||||
top := float64((region.Rect.Left + region.Rect.Top) * 40)
|
||||
// X of right
|
||||
right := float64((region.Rect.Right() - region.Rect.Top) * 80)
|
||||
// Y of bottom
|
||||
bottom := float64((region.Rect.Right() + region.Rect.Bottom()) * 40)
|
||||
func (me *MapEngine) AddEntity(entity MapEntity) {
|
||||
me.entities = append(me.entities, entity)
|
||||
}
|
||||
|
||||
if v.viewport.IsWorldRectVisible(left, top, right, bottom) {
|
||||
v.RenderRegion(region, target)
|
||||
func (me *MapEngine) Advance(tickTime float64) {
|
||||
for _, region := range me.regions {
|
||||
if region.isVisbile(me.viewport) {
|
||||
region.advance(tickTime)
|
||||
}
|
||||
}
|
||||
|
||||
for _, entity := range me.entities {
|
||||
entity.Advance(tickTime)
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
if v.viewport.IsWorldTileVisbile(float64(region.Tiles[tileIdx].tileX+region.Rect.Left), float64(region.Tiles[tileIdx].tileY+region.Rect.Top)) {
|
||||
region.Region.UpdateAnimations()
|
||||
|
||||
v.viewport.PushTranslation(float64(region.Tiles[tileIdx].offX), float64(region.Tiles[tileIdx].offY))
|
||||
v.RenderPass1(region.Region, region.Tiles[tileIdx].tileX, region.Tiles[tileIdx].tileY, target)
|
||||
v.DrawTileLines(region.Region, region.Tiles[tileIdx].tileX, region.Tiles[tileIdx].tileY, target)
|
||||
v.viewport.PopTranslation()
|
||||
}
|
||||
}
|
||||
|
||||
for tileIdx := range region.Tiles {
|
||||
if v.viewport.IsWorldTileVisbile(float64(region.Tiles[tileIdx].tileX+region.Rect.Left), float64(region.Tiles[tileIdx].tileY+region.Rect.Top)) {
|
||||
v.viewport.PushTranslation(float64(region.Tiles[tileIdx].offX), float64(region.Tiles[tileIdx].offY))
|
||||
v.RenderPass2(region.Region, region.Tiles[tileIdx].tileX, region.Tiles[tileIdx].tileY, target)
|
||||
v.viewport.PopTranslation()
|
||||
}
|
||||
}
|
||||
|
||||
for tileIdx := range region.Tiles {
|
||||
if v.viewport.IsWorldTileVisbile(float64(region.Tiles[tileIdx].tileX+region.Rect.Left), float64(region.Tiles[tileIdx].tileY+region.Rect.Top)) {
|
||||
v.viewport.PushTranslation(float64(region.Tiles[tileIdx].offX), float64(region.Tiles[tileIdx].offY))
|
||||
v.RenderPass3(region.Region, region.Tiles[tileIdx].tileX, region.Tiles[tileIdx].tileY, target)
|
||||
v.viewport.PopTranslation()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (v *Engine) RenderPass1(region *Region, 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(v.viewport, x, y, d2enum.RegionLayerTypeWalls, i, target)
|
||||
}
|
||||
|
||||
for i := range tile.Floors {
|
||||
if tile.Floors[i].Hidden || tile.Floors[i].Prop1 == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
region.RenderTile(v.viewport, x, y, d2enum.RegionLayerTypeFloors, i, target)
|
||||
}
|
||||
for i := range tile.Shadows {
|
||||
if tile.Shadows[i].Hidden || tile.Shadows[i].Prop1 == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
region.RenderTile(v.viewport, x, y, d2enum.RegionLayerTypeShadows, i, target)
|
||||
}
|
||||
}
|
||||
|
||||
func (v *Engine) RenderPass2(region *Region, 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(v.viewport, x, y, d2enum.RegionLayerTypeWalls, i, target)
|
||||
}
|
||||
|
||||
screenX, screenY := v.viewport.WorldToScreen(v.viewport.GetTranslation())
|
||||
|
||||
for _, obj := range region.AnimationEntities {
|
||||
if obj.TileX == x && obj.TileY == y {
|
||||
obj.Render(target, screenX, screenY)
|
||||
}
|
||||
}
|
||||
|
||||
for _, npc := range region.NPCs {
|
||||
if npc.AnimatedEntity.TileX == x && npc.AnimatedEntity.TileY == y {
|
||||
npc.Render(target, screenX, screenY)
|
||||
}
|
||||
}
|
||||
|
||||
if v.Hero != nil && v.Hero.AnimatedEntity.TileX == x && v.Hero.AnimatedEntity.TileY == y {
|
||||
v.Hero.Render(target, screenX, screenY)
|
||||
}
|
||||
}
|
||||
|
||||
func (v *Engine) RenderPass3(region *Region, 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(v.viewport, x, y, d2enum.RegionLayerTypeWalls, i, target)
|
||||
}
|
||||
}
|
||||
|
||||
func (v *Engine) DrawTileLines(region *Region, x, y int, target *ebiten.Image) {
|
||||
if v.ShowTiles > 0 {
|
||||
subtileColor := color.RGBA{80, 80, 255, 100}
|
||||
tileColor := color.RGBA{255, 255, 255, 255}
|
||||
|
||||
screenX1, screenY1 := v.viewport.IsoToScreen(float64(x), float64(y))
|
||||
screenX2, screenY2 := v.viewport.IsoToScreen(float64(x+1), float64(y))
|
||||
screenX3, screenY3 := v.viewport.IsoToScreen(float64(x), float64(y+1))
|
||||
|
||||
ebitenutil.DrawLine(
|
||||
target,
|
||||
float64(screenX1),
|
||||
float64(screenY1),
|
||||
float64(screenX2),
|
||||
float64(screenY2),
|
||||
tileColor,
|
||||
)
|
||||
|
||||
ebitenutil.DrawLine(
|
||||
target,
|
||||
float64(screenX1),
|
||||
float64(screenY1),
|
||||
float64(screenX3),
|
||||
float64(screenY3),
|
||||
tileColor,
|
||||
)
|
||||
|
||||
ebitenutil.DebugPrintAt(
|
||||
target,
|
||||
fmt.Sprintf("%v,%v", x, y),
|
||||
screenX1-10,
|
||||
screenY1+10,
|
||||
)
|
||||
|
||||
if v.ShowTiles > 1 {
|
||||
for i := 1; i <= 4; i++ {
|
||||
x := i * 16
|
||||
y := i * 8
|
||||
|
||||
ebitenutil.DrawLine(
|
||||
target,
|
||||
float64(screenX1-x),
|
||||
float64(screenY1+y),
|
||||
float64(screenX1-x+80),
|
||||
float64(screenY1+y+40),
|
||||
subtileColor,
|
||||
)
|
||||
|
||||
ebitenutil.DrawLine(
|
||||
target,
|
||||
float64(screenX1+x),
|
||||
float64(screenY1+y),
|
||||
float64(screenX1+x-80),
|
||||
float64(screenY1+y+40),
|
||||
subtileColor,
|
||||
)
|
||||
}
|
||||
|
||||
tile := region.DS1.Tiles[y][x]
|
||||
for i := range tile.Floors {
|
||||
ebitenutil.DebugPrintAt(
|
||||
target,
|
||||
fmt.Sprintf("f: %v-%v", tile.Floors[i].Style, tile.Floors[i].Sequence),
|
||||
screenX1-20,
|
||||
screenY1+10+(i+1)*14,
|
||||
)
|
||||
}
|
||||
func (me *MapEngine) Render(target *ebiten.Image) {
|
||||
for _, region := range me.regions {
|
||||
if region.isVisbile(me.viewport) {
|
||||
region.renderPass1(me.viewport, target)
|
||||
region.renderDebug(me.debugVisLevel, me.viewport, target)
|
||||
region.renderPass2(me.entities, me.viewport, target)
|
||||
region.renderPass3(me.viewport, target)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,173 +1,171 @@
|
||||
package d2mapengine
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image/color"
|
||||
"log"
|
||||
"math"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
|
||||
"github.com/OpenDiablo2/D2Shared/d2data/d2dt1"
|
||||
"github.com/hajimehoshi/ebiten"
|
||||
"github.com/hajimehoshi/ebiten/ebitenutil"
|
||||
|
||||
"github.com/OpenDiablo2/D2Shared/d2common"
|
||||
"github.com/OpenDiablo2/D2Shared/d2common/d2enum"
|
||||
"github.com/OpenDiablo2/D2Shared/d2common/d2interface"
|
||||
"github.com/OpenDiablo2/D2Shared/d2data/d2datadict"
|
||||
"github.com/OpenDiablo2/D2Shared/d2data/d2ds1"
|
||||
|
||||
"github.com/OpenDiablo2/D2Shared/d2data/d2dt1"
|
||||
"github.com/OpenDiablo2/D2Shared/d2helper"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2corehelper"
|
||||
|
||||
"github.com/OpenDiablo2/D2Shared/d2helper"
|
||||
|
||||
"github.com/OpenDiablo2/D2Shared/d2common/d2interface"
|
||||
|
||||
"github.com/OpenDiablo2/D2Shared/d2common/d2enum"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2render"
|
||||
|
||||
"github.com/OpenDiablo2/D2Shared/d2data/d2datadict"
|
||||
|
||||
"github.com/hajimehoshi/ebiten"
|
||||
)
|
||||
|
||||
type Region struct {
|
||||
RegionPath string
|
||||
LevelType d2datadict.LevelTypeRecord
|
||||
LevelPreset d2datadict.LevelPresetRecord
|
||||
TileWidth int32
|
||||
TileHeight int32
|
||||
Tiles []d2dt1.Tile
|
||||
DS1 d2ds1.DS1
|
||||
Palette d2datadict.PaletteRec
|
||||
AnimationEntities []d2render.AnimatedEntity
|
||||
NPCs []*d2core.NPC
|
||||
StartX float64
|
||||
StartY float64
|
||||
type MapRegion struct {
|
||||
tileRect d2common.Rectangle
|
||||
regionPath string
|
||||
levelType d2datadict.LevelTypeRecord
|
||||
levelPreset d2datadict.LevelPresetRecord
|
||||
tiles []d2dt1.Tile
|
||||
ds1 d2ds1.DS1
|
||||
palette d2datadict.PaletteRec
|
||||
startX float64
|
||||
startY float64
|
||||
imageCacheRecords map[uint32]*ebiten.Image
|
||||
seed int64
|
||||
currentFrame byte
|
||||
lastFrameTime float64
|
||||
}
|
||||
|
||||
func LoadRegion(seed int64, levelType d2enum.RegionIdType, levelPreset int, fileProvider d2interface.FileProvider, fileIndex int) *Region {
|
||||
result := &Region{
|
||||
LevelType: d2datadict.LevelTypes[levelType],
|
||||
LevelPreset: d2datadict.LevelPresets[levelPreset],
|
||||
Tiles: make([]d2dt1.Tile, 0),
|
||||
func loadRegion(seed int64, tileOffsetX, tileOffsetY int, levelType d2enum.RegionIdType, levelPreset int, fileProvider d2interface.FileProvider, fileIndex int) (*MapRegion, []MapEntity) {
|
||||
region := &MapRegion{
|
||||
levelType: d2datadict.LevelTypes[levelType],
|
||||
levelPreset: d2datadict.LevelPresets[levelPreset],
|
||||
imageCacheRecords: map[uint32]*ebiten.Image{},
|
||||
seed: seed,
|
||||
}
|
||||
result.Palette = d2datadict.Palettes[d2enum.PaletteType("act"+strconv.Itoa(int(result.LevelType.Act)))]
|
||||
// Temp hack
|
||||
|
||||
region.palette = d2datadict.Palettes[d2enum.PaletteType("act"+strconv.Itoa(int(region.levelType.Act)))]
|
||||
if levelType == d2enum.RegionAct5Lava {
|
||||
result.Palette = d2datadict.Palettes[d2enum.PaletteType("act4")]
|
||||
region.palette = d2datadict.Palettes[d2enum.PaletteType("act4")]
|
||||
}
|
||||
//bm := result.levelPreset.Dt1Mask
|
||||
for _, levelTypeDt1 := range result.LevelType.Files {
|
||||
/*
|
||||
if bm&1 == 0 {
|
||||
bm >>= 1
|
||||
continue
|
||||
}
|
||||
bm >>= 1
|
||||
*/
|
||||
if len(levelTypeDt1) == 0 || levelTypeDt1 == "" || levelTypeDt1 == "0" {
|
||||
continue
|
||||
|
||||
for _, levelTypeDt1 := range region.levelType.Files {
|
||||
if len(levelTypeDt1) != 0 && levelTypeDt1 != "" && levelTypeDt1 != "0" {
|
||||
dt1 := d2dt1.LoadDT1("/data/global/tiles/"+levelTypeDt1, fileProvider)
|
||||
region.tiles = append(region.tiles, dt1.Tiles...)
|
||||
}
|
||||
dt1 := d2dt1.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
|
||||
|
||||
var levelFilesToPick []string
|
||||
for _, fileRecord := range region.levelPreset.Files {
|
||||
if len(fileRecord) != 0 && fileRecord != "" && fileRecord != "0" {
|
||||
levelFilesToPick = append(levelFilesToPick, fileRecord)
|
||||
}
|
||||
levelFilesToPick = append(levelFilesToPick, fileRecord)
|
||||
}
|
||||
|
||||
levelIndex := int(math.Round(float64(len(levelFilesToPick)-1) * rand.Float64()))
|
||||
if fileIndex >= 0 && fileIndex < len(levelFilesToPick) {
|
||||
levelIndex = fileIndex
|
||||
}
|
||||
levelFile := levelFilesToPick[levelIndex]
|
||||
|
||||
result.RegionPath = levelFile
|
||||
result.DS1 = d2ds1.LoadDS1("/data/global/tiles/"+levelFile, fileProvider)
|
||||
result.TileWidth = result.DS1.Width
|
||||
result.TileHeight = result.DS1.Height
|
||||
result.currentFrame = 0
|
||||
result.loadObjects(fileProvider)
|
||||
result.loadSpecials()
|
||||
return result
|
||||
region.regionPath = levelFilesToPick[levelIndex]
|
||||
region.ds1 = d2ds1.LoadDS1("/data/global/tiles/"+region.regionPath, fileProvider)
|
||||
region.tileRect = d2common.Rectangle{
|
||||
Left: tileOffsetX,
|
||||
Top: tileOffsetY,
|
||||
Width: int(region.ds1.Width),
|
||||
Height: int(region.ds1.Height),
|
||||
}
|
||||
|
||||
entities := region.loadEntities(fileProvider)
|
||||
region.loadSpecials()
|
||||
region.generateTileCache()
|
||||
|
||||
return region, entities
|
||||
}
|
||||
|
||||
func (v *Region) loadSpecials() {
|
||||
for y := range v.DS1.Tiles {
|
||||
for x := range v.DS1.Tiles[y] {
|
||||
for _, wall := range v.DS1.Tiles[y][x].Walls {
|
||||
if wall.Type != 10 {
|
||||
continue
|
||||
}
|
||||
if wall.Style == 30 && wall.Sequence == 0 {
|
||||
v.StartX = float64(x) + 0.5
|
||||
v.StartY = float64(y) + 0.5
|
||||
log.Printf("Starting location: %d, %d", x, y)
|
||||
func (mr *MapRegion) GetTileRect() d2common.Rectangle {
|
||||
return mr.tileRect
|
||||
}
|
||||
|
||||
func (mr *MapRegion) GetLevelPreset() d2datadict.LevelPresetRecord {
|
||||
return mr.levelPreset
|
||||
}
|
||||
|
||||
func (mr *MapRegion) GetLevelType() d2datadict.LevelTypeRecord {
|
||||
return mr.levelType
|
||||
}
|
||||
|
||||
func (mr *MapRegion) GetPath() string {
|
||||
return mr.regionPath
|
||||
}
|
||||
|
||||
func (mr *MapRegion) loadSpecials() {
|
||||
for tileY := range mr.ds1.Tiles {
|
||||
for tileX := range mr.ds1.Tiles[tileY] {
|
||||
for _, wall := range mr.ds1.Tiles[tileY][tileX].Walls {
|
||||
if wall.Type == 10 && wall.Style == 30 && wall.Sequence == 0 {
|
||||
mr.startX, mr.startY = mr.getTileWorldPosition(tileX, tileY)
|
||||
mr.startX += 0.5
|
||||
mr.startY += 0.5
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (v *Region) loadObjects(fileProvider d2interface.FileProvider) {
|
||||
v.AnimationEntities = make([]d2render.AnimatedEntity, 0)
|
||||
v.NPCs = make([]*d2core.NPC, 0)
|
||||
for _, object := range v.DS1.Objects {
|
||||
func (mr *MapRegion) loadEntities(fileProvider d2interface.FileProvider) []MapEntity {
|
||||
var entities []MapEntity
|
||||
|
||||
for _, object := range mr.ds1.Objects {
|
||||
worldX, worldY := mr.getTileWorldPosition(int(object.X), int(object.Y))
|
||||
|
||||
switch object.Lookup.Type {
|
||||
case d2datadict.ObjectTypeCharacter:
|
||||
// Temp code, maybe..
|
||||
if object.Lookup.Base == "" || object.Lookup.Token == "" || object.Lookup.TR == "" {
|
||||
continue
|
||||
if object.Lookup.Base != "" && object.Lookup.Token != "" && object.Lookup.TR != "" {
|
||||
npc := d2core.CreateNPC(int32(worldX), int32(worldY), object.Lookup, fileProvider, 0)
|
||||
npc.SetPaths(object.Paths)
|
||||
entities = append(entities, npc)
|
||||
}
|
||||
npc := d2core.CreateNPC(object.X, object.Y, object.Lookup, fileProvider, 1)
|
||||
npc.SetPaths(object.Paths)
|
||||
v.NPCs = append(v.NPCs, npc)
|
||||
case d2datadict.ObjectTypeItem:
|
||||
if object.ObjectInfo == nil || !object.ObjectInfo.Draw || object.Lookup.Base == "" || object.Lookup.Token == "" {
|
||||
continue
|
||||
if object.ObjectInfo != nil && object.ObjectInfo.Draw && object.Lookup.Base != "" && object.Lookup.Token != "" {
|
||||
entity := d2render.CreateAnimatedEntity(int32(worldX), int32(worldY), object.Lookup, fileProvider, d2enum.Units)
|
||||
entity.SetMode(object.Lookup.Mode, object.Lookup.Class, 0)
|
||||
entities = append(entities, &entity)
|
||||
}
|
||||
entity := d2render.CreateAnimatedEntity(object.X, object.Y, object.Lookup, fileProvider, d2enum.Units)
|
||||
entity.SetMode(object.Lookup.Mode, object.Lookup.Class, 0)
|
||||
v.AnimationEntities = append(v.AnimationEntities, entity)
|
||||
}
|
||||
}
|
||||
|
||||
return entities
|
||||
}
|
||||
|
||||
func (v *Region) UpdateAnimations() {
|
||||
func (mr *MapRegion) updateAnimations() {
|
||||
now := d2helper.Now()
|
||||
framesToAdd := math.Floor((now - v.lastFrameTime) / 0.1)
|
||||
framesToAdd := math.Floor((now - mr.lastFrameTime) / 0.1)
|
||||
if framesToAdd > 0 {
|
||||
v.lastFrameTime += 0.1 * framesToAdd
|
||||
v.currentFrame += byte(math.Floor(framesToAdd))
|
||||
if v.currentFrame > 9 {
|
||||
v.currentFrame = 0
|
||||
mr.lastFrameTime += 0.1 * framesToAdd
|
||||
mr.currentFrame += byte(math.Floor(framesToAdd))
|
||||
if mr.currentFrame > 9 {
|
||||
mr.currentFrame = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (v *Region) RenderTile(viewport *Viewport, tileX, tileY int, layerType d2enum.RegionLayerType, layerIndex int, target *ebiten.Image) {
|
||||
switch layerType {
|
||||
case d2enum.RegionLayerTypeFloors:
|
||||
v.renderFloor(v.DS1.Tiles[tileY][tileX].Floors[layerIndex], viewport, target, tileX, tileY)
|
||||
case d2enum.RegionLayerTypeWalls:
|
||||
v.renderWall(v.DS1.Tiles[tileY][tileX].Walls[layerIndex], viewport, target, tileX, tileY)
|
||||
case d2enum.RegionLayerTypeShadows:
|
||||
v.renderShadow(v.DS1.Tiles[tileY][tileX].Shadows[layerIndex], viewport, target, tileX, tileY)
|
||||
}
|
||||
func (mr *MapRegion) getStartTilePosition() (float64, float64) {
|
||||
return float64(mr.tileRect.Left) + mr.startX, float64(mr.tileRect.Top) + mr.startY
|
||||
}
|
||||
|
||||
func (v *Region) getRandomTile(tiles []d2dt1.Tile, x, y int, seed int64) byte {
|
||||
func (mr *MapRegion) getRandomTile(tiles []d2dt1.Tile, x, y int, seed int64) byte {
|
||||
/* Walker's Alias Method for weighted random selection
|
||||
* with xorshifting for random numbers */
|
||||
|
||||
var tileSeed uint64
|
||||
tileSeed = uint64(seed) + uint64(x)
|
||||
tileSeed *= uint64(y) + uint64(v.LevelType.Id)
|
||||
tileSeed *= uint64(y) + uint64(mr.levelType.Id)
|
||||
|
||||
tileSeed ^= tileSeed << 13
|
||||
tileSeed ^= tileSeed >> 17
|
||||
@ -196,9 +194,9 @@ func (v *Region) getRandomTile(tiles []d2dt1.Tile, x, y int, seed int64) byte {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (v *Region) getTiles(style, sequence, tileType int32, x, y int, seed int64) []d2dt1.Tile {
|
||||
func (mr *MapRegion) getTiles(style, sequence, tileType int32, x, y int, seed int64) []d2dt1.Tile {
|
||||
var tiles []d2dt1.Tile
|
||||
for _, tile := range v.Tiles {
|
||||
for _, tile := range mr.tiles {
|
||||
if tile.Style != style || tile.Sequence != sequence || tile.Type != tileType {
|
||||
continue
|
||||
}
|
||||
@ -211,50 +209,146 @@ func (v *Region) getTiles(style, sequence, tileType int32, x, y int, seed int64)
|
||||
return tiles
|
||||
}
|
||||
|
||||
func (v *Region) renderFloor(tile d2ds1.FloorShadowRecord, viewport *Viewport, target *ebiten.Image, tileX, tileY int) {
|
||||
func (mr *MapRegion) isVisbile(viewport *Viewport) bool {
|
||||
return viewport.IsTileRectVisible(mr.tileRect)
|
||||
}
|
||||
|
||||
func (mr *MapRegion) advance(tickTime float64) {
|
||||
mr.updateAnimations()
|
||||
}
|
||||
|
||||
func (mr *MapRegion) getTileWorldPosition(tileX, tileY int) (float64, float64) {
|
||||
return float64(tileX + mr.tileRect.Left), float64(tileY + mr.tileRect.Top)
|
||||
}
|
||||
|
||||
func (mr *MapRegion) renderPass1(viewport *Viewport, target *ebiten.Image) {
|
||||
for tileY := range mr.ds1.Tiles {
|
||||
for tileX, tile := range mr.ds1.Tiles[tileY] {
|
||||
worldX, worldY := mr.getTileWorldPosition(tileX, tileY)
|
||||
if viewport.IsTileVisible(worldX, worldY) {
|
||||
viewport.PushTranslationWorld(worldX, worldY)
|
||||
mr.renderTilePass1(tile, viewport, target)
|
||||
viewport.PopTranslation()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (mr *MapRegion) renderPass2(entities []MapEntity, viewport *Viewport, target *ebiten.Image) {
|
||||
for tileY := range mr.ds1.Tiles {
|
||||
for tileX, tile := range mr.ds1.Tiles[tileY] {
|
||||
worldX, worldY := mr.getTileWorldPosition(tileX, tileY)
|
||||
if viewport.IsTileVisible(worldX, worldY) {
|
||||
viewport.PushTranslationWorld(worldX, worldY)
|
||||
mr.renderTilePass2(tile, viewport, target)
|
||||
|
||||
for _, entity := range entities {
|
||||
entWorldX, entWorldY := entity.GetPosition()
|
||||
if entWorldX == worldX && entWorldY == worldY {
|
||||
screenX, screenY := viewport.GetTranslationScreen()
|
||||
entity.Render(target, screenX, screenY)
|
||||
}
|
||||
}
|
||||
|
||||
viewport.PopTranslation()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (mr *MapRegion) renderPass3(viewport *Viewport, target *ebiten.Image) {
|
||||
for tileY := range mr.ds1.Tiles {
|
||||
for tileX, tile := range mr.ds1.Tiles[tileY] {
|
||||
worldX, worldY := mr.getTileWorldPosition(tileX, tileY)
|
||||
if viewport.IsTileVisible(worldX, worldY) {
|
||||
viewport.PushTranslationWorld(worldX, worldY)
|
||||
mr.renderTilePass3(tile, viewport, target)
|
||||
viewport.PopTranslation()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (mr *MapRegion) renderTilePass1(tile d2ds1.TileRecord, viewport *Viewport, target *ebiten.Image) {
|
||||
for _, wall := range tile.Walls {
|
||||
if !wall.Hidden && wall.Prop1 != 0 && wall.Type.LowerWall() {
|
||||
mr.renderWall(wall, viewport, target)
|
||||
}
|
||||
}
|
||||
|
||||
for _, floor := range tile.Floors {
|
||||
if !floor.Hidden && floor.Prop1 != 0 {
|
||||
mr.renderFloor(floor, viewport, target)
|
||||
}
|
||||
}
|
||||
|
||||
for _, shadow := range tile.Shadows {
|
||||
if !shadow.Hidden && shadow.Prop1 != 0 {
|
||||
mr.renderShadow(shadow, viewport, target)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (mr *MapRegion) renderTilePass2(tile d2ds1.TileRecord, viewport *Viewport, target *ebiten.Image) {
|
||||
for _, wall := range tile.Walls {
|
||||
if !wall.Hidden && wall.Type.UpperWall() {
|
||||
mr.renderWall(wall, viewport, target)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (mr *MapRegion) renderTilePass3(tile d2ds1.TileRecord, viewport *Viewport, target *ebiten.Image) {
|
||||
for _, wall := range tile.Walls {
|
||||
if wall.Type == d2enum.Roof {
|
||||
mr.renderWall(wall, viewport, target)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (mr *MapRegion) renderFloor(tile d2ds1.FloorShadowRecord, viewport *Viewport, target *ebiten.Image) {
|
||||
var img *ebiten.Image
|
||||
if !tile.Animated {
|
||||
img = v.GetImageCacheRecord(tile.Style, tile.Sequence, 0, tile.RandomIndex)
|
||||
img = mr.getImageCacheRecord(tile.Style, tile.Sequence, 0, tile.RandomIndex)
|
||||
} else {
|
||||
img = v.GetImageCacheRecord(tile.Style, tile.Sequence, 0, v.currentFrame)
|
||||
img = mr.getImageCacheRecord(tile.Style, tile.Sequence, 0, mr.currentFrame)
|
||||
}
|
||||
if img == nil {
|
||||
log.Printf("Render called on uncached floor {%v,%v}", tile.Style, tile.Sequence)
|
||||
return
|
||||
}
|
||||
|
||||
viewport.PushTranslation(-80, float64(tile.YAdjust))
|
||||
screenX, screenY := viewport.WorldToScreen(viewport.GetTranslation())
|
||||
viewport.PushTranslationOrtho(-80, float64(tile.YAdjust))
|
||||
screenX, screenY := viewport.GetTranslationScreen()
|
||||
opts := &ebiten.DrawImageOptions{}
|
||||
opts.GeoM.Translate(float64(screenX), float64(screenY))
|
||||
target.DrawImage(img, opts)
|
||||
viewport.PopTranslation()
|
||||
}
|
||||
|
||||
func (v *Region) renderWall(tile d2ds1.WallRecord, viewport *Viewport, target *ebiten.Image, tileX, tileY int) {
|
||||
img := v.GetImageCacheRecord(tile.Style, tile.Sequence, tile.Type, tile.RandomIndex)
|
||||
func (mr *MapRegion) renderWall(tile d2ds1.WallRecord, viewport *Viewport, target *ebiten.Image) {
|
||||
img := mr.getImageCacheRecord(tile.Style, tile.Sequence, tile.Type, tile.RandomIndex)
|
||||
if img == nil {
|
||||
log.Printf("Render called on uncached wall {%v,%v,%v}", tile.Style, tile.Sequence, tile.Type)
|
||||
return
|
||||
}
|
||||
|
||||
viewport.PushTranslation(-80, float64(tile.YAdjust))
|
||||
screenX, screenY := viewport.WorldToScreen(viewport.GetTranslation())
|
||||
viewport.PushTranslationOrtho(-80, float64(tile.YAdjust))
|
||||
screenX, screenY := viewport.GetTranslationScreen()
|
||||
opts := &ebiten.DrawImageOptions{}
|
||||
opts.GeoM.Translate(float64(screenX), float64(screenY))
|
||||
target.DrawImage(img, opts)
|
||||
viewport.PopTranslation()
|
||||
}
|
||||
|
||||
func (v *Region) renderShadow(tile d2ds1.FloorShadowRecord, viewport *Viewport, target *ebiten.Image, tileX, tileY int) {
|
||||
img := v.GetImageCacheRecord(tile.Style, tile.Sequence, 13, tile.RandomIndex)
|
||||
func (mr *MapRegion) renderShadow(tile d2ds1.FloorShadowRecord, viewport *Viewport, target *ebiten.Image) {
|
||||
img := mr.getImageCacheRecord(tile.Style, tile.Sequence, 13, tile.RandomIndex)
|
||||
if img == nil {
|
||||
log.Printf("Render called on uncached shadow {%v,%v}", tile.Style, tile.Sequence)
|
||||
return
|
||||
}
|
||||
|
||||
viewport.PushTranslation(-80, float64(tile.YAdjust))
|
||||
screenX, screenY := viewport.WorldToScreen(viewport.GetTranslation())
|
||||
viewport.PushTranslationOrtho(-80, float64(tile.YAdjust))
|
||||
screenX, screenY := viewport.GetTranslationScreen()
|
||||
opts := &ebiten.DrawImageOptions{}
|
||||
opts.GeoM.Translate(float64(screenX), float64(screenY))
|
||||
opts.ColorM = d2corehelper.ColorToColorM(color.RGBA{255, 255, 255, 160})
|
||||
@ -262,7 +356,233 @@ func (v *Region) renderShadow(tile d2ds1.FloorShadowRecord, viewport *Viewport,
|
||||
viewport.PopTranslation()
|
||||
}
|
||||
|
||||
func (v *Region) decodeTileGfxData(blocks []d2dt1.Block, pixels *[]byte, tileYOffset int32, tileWidth int32) {
|
||||
func (mr *MapRegion) renderDebug(debugVisLevel int, viewport *Viewport, target *ebiten.Image) {
|
||||
for tileY := range mr.ds1.Tiles {
|
||||
for tileX := range mr.ds1.Tiles[tileY] {
|
||||
worldX, worldY := mr.getTileWorldPosition(tileX, tileY)
|
||||
if viewport.IsTileVisible(worldX, worldY) {
|
||||
mr.renderTileDebug(int(worldX), int(worldY), debugVisLevel, viewport, target)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (mr *MapRegion) renderTileDebug(x, y int, debugVisLevel int, viewport *Viewport, target *ebiten.Image) {
|
||||
if debugVisLevel > 0 {
|
||||
subtileColor := color.RGBA{80, 80, 255, 100}
|
||||
tileColor := color.RGBA{255, 255, 255, 255}
|
||||
|
||||
screenX1, screenY1 := viewport.WorldToScreen(float64(x), float64(y))
|
||||
screenX2, screenY2 := viewport.WorldToScreen(float64(x+1), float64(y))
|
||||
screenX3, screenY3 := viewport.WorldToScreen(float64(x), float64(y+1))
|
||||
|
||||
ebitenutil.DrawLine(target, float64(screenX1), float64(screenY1), float64(screenX2), float64(screenY2), tileColor)
|
||||
ebitenutil.DrawLine(target, float64(screenX1), float64(screenY1), float64(screenX3), float64(screenY3), tileColor)
|
||||
ebitenutil.DebugPrintAt(target, fmt.Sprintf("%v,%v", x, y), screenX1-10, screenY1+10)
|
||||
|
||||
if debugVisLevel > 1 {
|
||||
for i := 1; i <= 4; i++ {
|
||||
x := i * 16
|
||||
y := i * 8
|
||||
|
||||
ebitenutil.DrawLine(target, float64(screenX1-x), float64(screenY1+y), float64(screenX1-x+80), float64(screenY1+y+40), subtileColor)
|
||||
ebitenutil.DrawLine(target, float64(screenX1+x), float64(screenY1+y), float64(screenX1+x-80), float64(screenY1+y+40), subtileColor)
|
||||
}
|
||||
|
||||
tile := mr.ds1.Tiles[y][x]
|
||||
for i := range tile.Floors {
|
||||
ebitenutil.DebugPrintAt(target, fmt.Sprintf("f: %v-%v", tile.Floors[i].Style, tile.Floors[i].Sequence), screenX1-20, screenY1+10+(i+1)*14)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (mr *MapRegion) generateTileCache() {
|
||||
for tileY := range mr.ds1.Tiles {
|
||||
for tileX := range mr.ds1.Tiles[tileY] {
|
||||
tile := mr.ds1.Tiles[tileY][tileX]
|
||||
|
||||
for i := range tile.Floors {
|
||||
if !tile.Floors[i].Hidden && tile.Floors[i].Prop1 != 0 {
|
||||
mr.generateFloorCache(&tile.Floors[i], tileX, tileY)
|
||||
}
|
||||
}
|
||||
|
||||
for i := range tile.Shadows {
|
||||
if !tile.Shadows[i].Hidden && tile.Shadows[i].Prop1 != 0 {
|
||||
mr.generateShadowCache(&tile.Shadows[i], tileX, tileY)
|
||||
}
|
||||
}
|
||||
|
||||
for i := range tile.Walls {
|
||||
if !tile.Walls[i].Hidden && tile.Walls[i].Prop1 != 0 {
|
||||
mr.generateWallCache(&tile.Walls[i], tileX, tileY)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (mr *MapRegion) getImageCacheRecord(style, sequence byte, tileType d2enum.TileType, randomIndex byte) *ebiten.Image {
|
||||
lookupIndex := uint32(style)<<24 | uint32(sequence)<<16 | uint32(tileType)<<8 | uint32(randomIndex)
|
||||
return mr.imageCacheRecords[lookupIndex]
|
||||
}
|
||||
|
||||
func (mr *MapRegion) setImageCacheRecord(style, sequence byte, tileType d2enum.TileType, randomIndex byte, image *ebiten.Image) {
|
||||
lookupIndex := uint32(style)<<24 | uint32(sequence)<<16 | uint32(tileType)<<8 | uint32(randomIndex)
|
||||
mr.imageCacheRecords[lookupIndex] = image
|
||||
}
|
||||
|
||||
func (mr *MapRegion) generateFloorCache(tile *d2ds1.FloorShadowRecord, tileX, tileY int) {
|
||||
tileOptions := mr.getTiles(int32(tile.Style), int32(tile.Sequence), 0, tileX, tileY, mr.seed)
|
||||
var tileData []*d2dt1.Tile
|
||||
var tileIndex byte
|
||||
|
||||
if tileOptions == nil {
|
||||
log.Printf("Could not locate tile Style:%d, Seq: %d, Type: %d\n", tile.Style, tile.Sequence, 0)
|
||||
tileData = append(tileData, &d2dt1.Tile{})
|
||||
tileData[0].Width = 10
|
||||
tileData[0].Height = 10
|
||||
} else {
|
||||
if !tileOptions[0].MaterialFlags.Animated {
|
||||
tileIndex = mr.getRandomTile(tileOptions, tileX, tileY, mr.seed)
|
||||
tileData = append(tileData, &tileOptions[tileIndex])
|
||||
} else {
|
||||
tile.Animated = true
|
||||
for i := range tileOptions {
|
||||
tileData = append(tileData, &tileOptions[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for i := range tileData {
|
||||
if !tileData[i].MaterialFlags.Animated {
|
||||
tile.RandomIndex = tileIndex
|
||||
} else {
|
||||
tileIndex = byte(tileData[i].RarityFrameIndex)
|
||||
}
|
||||
cachedImage := mr.getImageCacheRecord(tile.Style, tile.Sequence, 0, tileIndex)
|
||||
if cachedImage != nil {
|
||||
return
|
||||
}
|
||||
tileYMinimum := int32(0)
|
||||
for _, block := range tileData[i].Blocks {
|
||||
tileYMinimum = d2helper.MinInt32(tileYMinimum, int32(block.Y))
|
||||
}
|
||||
tileYOffset := d2helper.AbsInt32(tileYMinimum)
|
||||
tileHeight := d2helper.AbsInt32(tileData[i].Height)
|
||||
image, _ := ebiten.NewImage(int(tileData[i].Width), int(tileHeight), ebiten.FilterNearest)
|
||||
pixels := make([]byte, 4*tileData[i].Width*tileHeight)
|
||||
mr.decodeTileGfxData(tileData[i].Blocks, &pixels, tileYOffset, tileData[i].Width)
|
||||
image.ReplacePixels(pixels)
|
||||
mr.setImageCacheRecord(tile.Style, tile.Sequence, 0, tileIndex, image)
|
||||
}
|
||||
}
|
||||
|
||||
func (mr *MapRegion) generateShadowCache(tile *d2ds1.FloorShadowRecord, tileX, tileY int) {
|
||||
tileOptions := mr.getTiles(int32(tile.Style), int32(tile.Sequence), 13, tileX, tileY, mr.seed)
|
||||
var tileIndex byte
|
||||
var tileData *d2dt1.Tile
|
||||
if tileOptions == nil {
|
||||
return
|
||||
} else {
|
||||
tileIndex = mr.getRandomTile(tileOptions, tileX, tileY, mr.seed)
|
||||
tileData = &tileOptions[tileIndex]
|
||||
}
|
||||
|
||||
tile.RandomIndex = tileIndex
|
||||
tileMinY := int32(0)
|
||||
tileMaxY := int32(0)
|
||||
for _, block := range tileData.Blocks {
|
||||
tileMinY = d2helper.MinInt32(tileMinY, int32(block.Y))
|
||||
tileMaxY = d2helper.MaxInt32(tileMaxY, int32(block.Y+32))
|
||||
}
|
||||
tileYOffset := -tileMinY
|
||||
tileHeight := int(tileMaxY - tileMinY)
|
||||
tile.YAdjust = int(tileMinY + 80)
|
||||
|
||||
cachedImage := mr.getImageCacheRecord(tile.Style, tile.Sequence, 13, tileIndex)
|
||||
if cachedImage != nil {
|
||||
return
|
||||
}
|
||||
|
||||
image, _ := ebiten.NewImage(int(tileData.Width), int(tileHeight), ebiten.FilterNearest)
|
||||
pixels := make([]byte, 4*tileData.Width*int32(tileHeight))
|
||||
mr.decodeTileGfxData(tileData.Blocks, &pixels, tileYOffset, tileData.Width)
|
||||
image.ReplacePixels(pixels)
|
||||
mr.setImageCacheRecord(tile.Style, tile.Sequence, 13, tileIndex, image)
|
||||
}
|
||||
|
||||
func (mr *MapRegion) generateWallCache(tile *d2ds1.WallRecord, tileX, tileY int) {
|
||||
tileOptions := mr.getTiles(int32(tile.Style), int32(tile.Sequence), int32(tile.Type), tileX, tileY, mr.seed)
|
||||
var tileIndex byte
|
||||
var tileData *d2dt1.Tile
|
||||
if tileOptions == nil {
|
||||
return
|
||||
} else {
|
||||
tileIndex = mr.getRandomTile(tileOptions, tileX, tileY, mr.seed)
|
||||
tileData = &tileOptions[tileIndex]
|
||||
}
|
||||
|
||||
tile.RandomIndex = tileIndex
|
||||
var newTileData *d2dt1.Tile = nil
|
||||
|
||||
if tile.Type == 3 {
|
||||
newTileOptions := mr.getTiles(int32(tile.Style), int32(tile.Sequence), int32(4), tileX, tileY, mr.seed)
|
||||
newTileIndex := mr.getRandomTile(newTileOptions, tileX, tileY, mr.seed)
|
||||
newTileData = &newTileOptions[newTileIndex]
|
||||
}
|
||||
|
||||
tileMinY := int32(0)
|
||||
tileMaxY := int32(0)
|
||||
|
||||
target := tileData
|
||||
|
||||
if newTileData != nil && newTileData.Height < tileData.Height {
|
||||
target = newTileData
|
||||
}
|
||||
|
||||
for _, block := range target.Blocks {
|
||||
tileMinY = d2helper.MinInt32(tileMinY, int32(block.Y))
|
||||
tileMaxY = d2helper.MaxInt32(tileMaxY, int32(block.Y+32))
|
||||
}
|
||||
|
||||
realHeight := d2helper.MaxInt32(d2helper.AbsInt32(tileData.Height), tileMaxY-tileMinY)
|
||||
tileYOffset := -tileMinY
|
||||
//tileHeight := int(tileMaxY - tileMinY)
|
||||
|
||||
if tile.Type == 15 {
|
||||
tile.YAdjust = -int(tileData.RoofHeight)
|
||||
} else {
|
||||
tile.YAdjust = int(tileMinY) + 80
|
||||
}
|
||||
|
||||
cachedImage := mr.getImageCacheRecord(tile.Style, tile.Sequence, tile.Type, tileIndex)
|
||||
if cachedImage != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if realHeight == 0 {
|
||||
log.Printf("Invalid 0 height for wall tile")
|
||||
return
|
||||
}
|
||||
|
||||
image, _ := ebiten.NewImage(160, int(realHeight), ebiten.FilterNearest)
|
||||
pixels := make([]byte, 4*160*realHeight)
|
||||
mr.decodeTileGfxData(tileData.Blocks, &pixels, tileYOffset, 160)
|
||||
|
||||
if newTileData != nil {
|
||||
mr.decodeTileGfxData(newTileData.Blocks, &pixels, tileYOffset, 160)
|
||||
}
|
||||
|
||||
if err := image.ReplacePixels(pixels); err != nil {
|
||||
log.Panicf(err.Error())
|
||||
}
|
||||
|
||||
mr.setImageCacheRecord(tile.Style, tile.Sequence, tile.Type, tileIndex, image)
|
||||
}
|
||||
|
||||
func (mr *MapRegion) decodeTileGfxData(blocks []d2dt1.Block, pixels *[]byte, tileYOffset int32, tileWidth int32) {
|
||||
for _, block := range blocks {
|
||||
if block.Format == d2dt1.BlockFormatIsometric {
|
||||
// 3D isometric decoding
|
||||
@ -281,7 +601,7 @@ func (v *Region) decodeTileGfxData(blocks []d2dt1.Block, pixels *[]byte, tileYOf
|
||||
for n > 0 {
|
||||
colorIndex := block.EncodedData[idx]
|
||||
if colorIndex != 0 {
|
||||
pixelColor := v.Palette.Colors[colorIndex]
|
||||
pixelColor := mr.palette.Colors[colorIndex]
|
||||
offset := 4 * (((blockY + y + tileYOffset) * tileWidth) + (blockX + x))
|
||||
(*pixels)[offset] = pixelColor.R
|
||||
(*pixels)[offset+1] = pixelColor.G
|
||||
@ -317,7 +637,7 @@ func (v *Region) decodeTileGfxData(blocks []d2dt1.Block, pixels *[]byte, tileYOf
|
||||
for b2 > 0 {
|
||||
colorIndex := block.EncodedData[idx]
|
||||
if colorIndex != 0 {
|
||||
pixelColor := v.Palette.Colors[colorIndex]
|
||||
pixelColor := mr.palette.Colors[colorIndex]
|
||||
offset := 4 * (((blockY + y + tileYOffset) * tileWidth) + (blockX + x))
|
||||
(*pixels)[offset] = pixelColor.R
|
||||
(*pixels)[offset+1] = pixelColor.G
|
||||
@ -333,162 +653,3 @@ func (v *Region) decodeTileGfxData(blocks []d2dt1.Block, pixels *[]byte, tileYOf
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (v *Region) generateFloorCache(tile *d2ds1.FloorShadowRecord, tileX, tileY int) {
|
||||
tileOptions := v.getTiles(int32(tile.Style), int32(tile.Sequence), 0, tileX, tileY, v.seed)
|
||||
var tileData []*d2dt1.Tile
|
||||
var tileIndex byte
|
||||
|
||||
if tileOptions == nil {
|
||||
log.Printf("Could not locate tile Style:%d, Seq: %d, Type: %d\n", tile.Style, tile.Sequence, 0)
|
||||
tileData = append(tileData, &d2dt1.Tile{})
|
||||
tileData[0].Width = 10
|
||||
tileData[0].Height = 10
|
||||
} else {
|
||||
if !tileOptions[0].MaterialFlags.Animated {
|
||||
tileIndex = v.getRandomTile(tileOptions, tileX, tileY, v.seed)
|
||||
tileData = append(tileData, &tileOptions[tileIndex])
|
||||
} else {
|
||||
tile.Animated = true
|
||||
for i := range tileOptions {
|
||||
tileData = append(tileData, &tileOptions[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for i := range tileData {
|
||||
if !tileData[i].MaterialFlags.Animated {
|
||||
tile.RandomIndex = tileIndex
|
||||
} else {
|
||||
tileIndex = byte(tileData[i].RarityFrameIndex)
|
||||
}
|
||||
cachedImage := v.GetImageCacheRecord(tile.Style, tile.Sequence, 0, tileIndex)
|
||||
if cachedImage != nil {
|
||||
return
|
||||
}
|
||||
tileYMinimum := int32(0)
|
||||
for _, block := range tileData[i].Blocks {
|
||||
tileYMinimum = d2helper.MinInt32(tileYMinimum, int32(block.Y))
|
||||
}
|
||||
tileYOffset := d2helper.AbsInt32(tileYMinimum)
|
||||
tileHeight := d2helper.AbsInt32(tileData[i].Height)
|
||||
image, _ := ebiten.NewImage(int(tileData[i].Width), int(tileHeight), ebiten.FilterNearest)
|
||||
pixels := make([]byte, 4*tileData[i].Width*tileHeight)
|
||||
v.decodeTileGfxData(tileData[i].Blocks, &pixels, tileYOffset, tileData[i].Width)
|
||||
image.ReplacePixels(pixels)
|
||||
v.SetImageCacheRecord(tile.Style, tile.Sequence, 0, tileIndex, image)
|
||||
}
|
||||
}
|
||||
|
||||
func (v *Region) generateShadowCache(tile *d2ds1.FloorShadowRecord, tileX, tileY int) {
|
||||
tileOptions := v.getTiles(int32(tile.Style), int32(tile.Sequence), 13, tileX, tileY, v.seed)
|
||||
var tileIndex byte
|
||||
var tileData *d2dt1.Tile
|
||||
if tileOptions == nil {
|
||||
return
|
||||
} else {
|
||||
tileIndex = v.getRandomTile(tileOptions, tileX, tileY, v.seed)
|
||||
tileData = &tileOptions[tileIndex]
|
||||
}
|
||||
|
||||
tile.RandomIndex = tileIndex
|
||||
tileMinY := int32(0)
|
||||
tileMaxY := int32(0)
|
||||
for _, block := range tileData.Blocks {
|
||||
tileMinY = d2helper.MinInt32(tileMinY, int32(block.Y))
|
||||
tileMaxY = d2helper.MaxInt32(tileMaxY, int32(block.Y+32))
|
||||
}
|
||||
tileYOffset := -tileMinY
|
||||
tileHeight := int(tileMaxY - tileMinY)
|
||||
tile.YAdjust = int(tileMinY + 80)
|
||||
|
||||
cachedImage := v.GetImageCacheRecord(tile.Style, tile.Sequence, 13, tileIndex)
|
||||
if cachedImage != nil {
|
||||
return
|
||||
}
|
||||
|
||||
image, _ := ebiten.NewImage(int(tileData.Width), int(tileHeight), ebiten.FilterNearest)
|
||||
pixels := make([]byte, 4*tileData.Width*int32(tileHeight))
|
||||
v.decodeTileGfxData(tileData.Blocks, &pixels, tileYOffset, tileData.Width)
|
||||
image.ReplacePixels(pixels)
|
||||
v.SetImageCacheRecord(tile.Style, tile.Sequence, 13, tileIndex, image)
|
||||
}
|
||||
|
||||
func (v *Region) generateWallCache(tile *d2ds1.WallRecord, tileX, tileY int) {
|
||||
tileOptions := v.getTiles(int32(tile.Style), int32(tile.Sequence), int32(tile.Type), tileX, tileY, v.seed)
|
||||
var tileIndex byte
|
||||
var tileData *d2dt1.Tile
|
||||
if tileOptions == nil {
|
||||
return
|
||||
} else {
|
||||
tileIndex = v.getRandomTile(tileOptions, tileX, tileY, v.seed)
|
||||
tileData = &tileOptions[tileIndex]
|
||||
}
|
||||
|
||||
tile.RandomIndex = tileIndex
|
||||
var newTileData *d2dt1.Tile = nil
|
||||
|
||||
if tile.Type == 3 {
|
||||
newTileOptions := v.getTiles(int32(tile.Style), int32(tile.Sequence), int32(4), tileX, tileY, v.seed)
|
||||
newTileIndex := v.getRandomTile(newTileOptions, tileX, tileY, v.seed)
|
||||
newTileData = &newTileOptions[newTileIndex]
|
||||
}
|
||||
|
||||
tileMinY := int32(0)
|
||||
tileMaxY := int32(0)
|
||||
|
||||
target := tileData
|
||||
|
||||
if newTileData != nil && newTileData.Height < tileData.Height {
|
||||
target = newTileData
|
||||
}
|
||||
|
||||
for _, block := range target.Blocks {
|
||||
tileMinY = d2helper.MinInt32(tileMinY, int32(block.Y))
|
||||
tileMaxY = d2helper.MaxInt32(tileMaxY, int32(block.Y+32))
|
||||
}
|
||||
|
||||
realHeight := d2helper.MaxInt32(d2helper.AbsInt32(tileData.Height), tileMaxY-tileMinY)
|
||||
tileYOffset := -tileMinY
|
||||
//tileHeight := int(tileMaxY - tileMinY)
|
||||
|
||||
if tile.Type == 15 {
|
||||
tile.YAdjust = -int(tileData.RoofHeight)
|
||||
} else {
|
||||
tile.YAdjust = int(tileMinY) + 80
|
||||
}
|
||||
|
||||
cachedImage := v.GetImageCacheRecord(tile.Style, tile.Sequence, tile.Type, tileIndex)
|
||||
if cachedImage != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if realHeight == 0 {
|
||||
log.Printf("Invalid 0 height for wall tile")
|
||||
return
|
||||
}
|
||||
|
||||
image, _ := ebiten.NewImage(160, int(realHeight), ebiten.FilterNearest)
|
||||
pixels := make([]byte, 4*160*realHeight)
|
||||
v.decodeTileGfxData(tileData.Blocks, &pixels, tileYOffset, 160)
|
||||
|
||||
if newTileData != nil {
|
||||
v.decodeTileGfxData(newTileData.Blocks, &pixels, tileYOffset, 160)
|
||||
}
|
||||
|
||||
if err := image.ReplacePixels(pixels); err != nil {
|
||||
log.Panicf(err.Error())
|
||||
}
|
||||
|
||||
v.SetImageCacheRecord(tile.Style, tile.Sequence, tile.Type, tileIndex, image)
|
||||
}
|
||||
|
||||
func (v *Region) GetImageCacheRecord(style, sequence byte, tileType d2enum.TileType, randomIndex byte) *ebiten.Image {
|
||||
lookupIndex := uint32(style)<<24 | uint32(sequence)<<16 | uint32(tileType)<<8 | uint32(randomIndex)
|
||||
return v.imageCacheRecords[lookupIndex]
|
||||
}
|
||||
|
||||
func (v *Region) SetImageCacheRecord(style, sequence byte, tileType d2enum.TileType, randomIndex byte, image *ebiten.Image) {
|
||||
lookupIndex := uint32(style)<<24 | uint32(sequence)<<16 | uint32(tileType)<<8 | uint32(randomIndex)
|
||||
v.imageCacheRecords[lookupIndex] = image
|
||||
}
|
||||
|
@ -33,67 +33,82 @@ func (v *Viewport) SetCamera(camera *Camera) {
|
||||
v.camera = camera
|
||||
}
|
||||
|
||||
func (v *Viewport) IsoToScreen(x, y float64) (int, int) {
|
||||
return v.WorldToScreen(v.IsoToWorld(x, y))
|
||||
}
|
||||
|
||||
func (v *Viewport) ScreenToIso(x, y int) (float64, float64) {
|
||||
return v.WorldToIso(v.ScreenToWorld(x, y))
|
||||
}
|
||||
|
||||
func (v *Viewport) WorldToIso(x, y float64) (float64, float64) {
|
||||
isoX := (x/80 + y/40) / 2
|
||||
isoY := (y/40 - x/80) / 2
|
||||
return isoX, isoY
|
||||
}
|
||||
|
||||
func (v *Viewport) IsoToWorld(x, y float64) (float64, float64) {
|
||||
worldX := (x - y) * 80
|
||||
worldY := (x + y) * 40
|
||||
return worldX, worldY
|
||||
func (v *Viewport) WorldToScreen(x, y float64) (int, int) {
|
||||
return v.OrthoToScreen(v.WorldToOrtho(x, y))
|
||||
}
|
||||
|
||||
func (v *Viewport) ScreenToWorld(x, y int) (float64, float64) {
|
||||
return v.OrthoToWorld(v.ScreenToOrtho(x, y))
|
||||
}
|
||||
|
||||
func (v *Viewport) OrthoToWorld(x, y float64) (float64, float64) {
|
||||
worldX := (x/80 + y/40) / 2
|
||||
worldY := (y/40 - x/80) / 2
|
||||
return worldX, worldY
|
||||
}
|
||||
|
||||
func (v *Viewport) WorldToOrtho(x, y float64) (float64, float64) {
|
||||
orthoX := (x - y) * 80
|
||||
orthoY := (x + y) * 40
|
||||
return orthoX, orthoY
|
||||
}
|
||||
|
||||
func (v *Viewport) ScreenToOrtho(x, y int) (float64, float64) {
|
||||
camX, camY := v.getCameraOffset()
|
||||
screenX := float64(x) + camX - float64(v.screenRect.Left)
|
||||
screenY := float64(y) + camY - float64(v.screenRect.Top)
|
||||
return screenX, screenY
|
||||
}
|
||||
|
||||
func (v *Viewport) WorldToScreen(x, y float64) (int, int) {
|
||||
camX, camY := v.getCameraOffset()
|
||||
worldX := int(math.Floor(x - camX + float64(v.screenRect.Left)))
|
||||
worldY := int(math.Floor(y - camY + float64(v.screenRect.Top)))
|
||||
return worldX, worldY
|
||||
func (v *Viewport) OrthoToScreen(x, y float64) (int, int) {
|
||||
camOrthoX, camOrthoY := v.getCameraOffset()
|
||||
orthoX := int(math.Floor(x - camOrthoX + float64(v.screenRect.Left)))
|
||||
orthoY := int(math.Floor(y - camOrthoY + float64(v.screenRect.Top)))
|
||||
return orthoX, orthoY
|
||||
}
|
||||
|
||||
func (v *Viewport) IsWorldTileVisbile(x, y float64) bool {
|
||||
worldX1, worldY1 := v.IsoToWorld(x-2, y)
|
||||
worldX2, worldY2 := v.IsoToWorld(x+2, y)
|
||||
return v.IsWorldRectVisible(worldX1, worldY1, worldX2, worldY2)
|
||||
func (v *Viewport) IsTileVisible(x, y float64) bool {
|
||||
orthoX1, orthoY1 := v.WorldToOrtho(x-3, y)
|
||||
orthoX2, orthoY2 := v.WorldToOrtho(x+3, y)
|
||||
return v.IsOrthoRectVisible(orthoX1, orthoY1, orthoX2, orthoY2)
|
||||
}
|
||||
|
||||
func (v *Viewport) IsWorldPointVisible(x, y float64) bool {
|
||||
screenX, screenY := v.WorldToScreen(x, y)
|
||||
return screenX >= 0 && screenX < v.screenRect.Width && screenY >= 0 && screenY < v.screenRect.Height
|
||||
func (v *Viewport) IsTileRectVisible(rect d2common.Rectangle) bool {
|
||||
left := float64((rect.Left - rect.Bottom()) * 80)
|
||||
top := float64((rect.Left + rect.Top) * 40)
|
||||
right := float64((rect.Right() - rect.Top) * 80)
|
||||
bottom := float64((rect.Right() + rect.Bottom()) * 40)
|
||||
return v.IsOrthoRectVisible(left, top, right, bottom)
|
||||
}
|
||||
|
||||
func (v *Viewport) IsWorldRectVisible(x1, y1, x2, y2 float64) bool {
|
||||
screenX1, screenY1 := v.WorldToScreen(x1, y1)
|
||||
screenX2, screenY2 := v.WorldToScreen(x2, y2)
|
||||
func (v *Viewport) IsOrthoRectVisible(x1, y1, x2, y2 float64) bool {
|
||||
screenX1, screenY1 := v.OrthoToScreen(x1, y1)
|
||||
screenX2, screenY2 := v.OrthoToScreen(x2, y2)
|
||||
return !(screenX1 >= v.screenRect.Width || screenX2 < 0 || screenY1 >= v.screenRect.Height || screenY2 < 0)
|
||||
}
|
||||
|
||||
func (v *Viewport) GetTranslation() (float64, float64) {
|
||||
func (v *Viewport) GetTranslationOrtho() (float64, float64) {
|
||||
return v.transCurrent.x, v.transCurrent.y
|
||||
}
|
||||
|
||||
func (v *Viewport) PushTranslation(x, y float64) {
|
||||
func (v *Viewport) GetTranslationScreen() (int, int) {
|
||||
return v.OrthoToScreen(v.transCurrent.x, v.transCurrent.y)
|
||||
}
|
||||
|
||||
func (v *Viewport) PushTranslationOrtho(x, y float64) {
|
||||
v.transStack = append(v.transStack, v.transCurrent)
|
||||
v.transCurrent.x += x
|
||||
v.transCurrent.y += y
|
||||
}
|
||||
|
||||
func (v *Viewport) PushTranslationWorld(x, y float64) {
|
||||
v.PushTranslationOrtho(v.WorldToOrtho(x, y))
|
||||
}
|
||||
|
||||
func (v *Viewport) PushTranslationScreen(x, y int) {
|
||||
v.PushTranslationOrtho(v.ScreenToOrtho(x, y))
|
||||
}
|
||||
|
||||
func (v *Viewport) PopTranslation() {
|
||||
count := len(v.transStack)
|
||||
if count == 0 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user