120 lines
4.8 KiB
Go
120 lines
4.8 KiB
Go
package level
|
|
|
|
// WallHeights describes the height delta, in HeightUnits, one would take to
|
|
// cross to the other tile. A value of 0 means equal floor, a negative value a fall down.
|
|
// A value of 0x20 (= maximum of height units) means a solid wall.
|
|
//
|
|
// For each cardinal direction, facing to the specified direction,
|
|
// the first entry is the left side, the second the center, and the third the right side.
|
|
type WallHeights struct {
|
|
North [3]float32
|
|
East [3]float32
|
|
South [3]float32
|
|
West [3]float32
|
|
}
|
|
|
|
// Reset sets all the heights to zero.
|
|
func (heights *WallHeights) Reset() {
|
|
*heights = WallHeights{}
|
|
}
|
|
|
|
// WallHeightsMap is rectangular table of wall heights, typically mirroring a tile map.
|
|
type WallHeightsMap [][]WallHeights
|
|
|
|
// NewWallHeightsMap returns a new, initialized map.
|
|
func NewWallHeightsMap(width, height int) WallHeightsMap {
|
|
m := make([][]WallHeights, height)
|
|
for y := 0; y < height; y++ {
|
|
m[y] = make([]WallHeights, width)
|
|
for x := 0; x < width; x++ {
|
|
m[y][x].Reset()
|
|
}
|
|
}
|
|
return m
|
|
}
|
|
|
|
// Tile returns a pointer to the tile within the map for given position.
|
|
func (m WallHeightsMap) Tile(pos TilePosition) *WallHeights {
|
|
return &m[pos.Y][pos.X]
|
|
}
|
|
|
|
// CalculateFrom updates all the wall heights according to the specified map.
|
|
func (m *WallHeightsMap) CalculateFrom(tileMap interface {
|
|
Tile(pos TilePosition) *TileMapEntry
|
|
}) {
|
|
posAt := func(x, y int) TilePosition {
|
|
return TilePosition{X: byte(x), Y: byte(y)}
|
|
}
|
|
for y, row := range *m {
|
|
for x := 0; x < len(row); x++ {
|
|
tile := tileMap.Tile(posAt(x, y))
|
|
if tile == nil {
|
|
continue
|
|
}
|
|
heights := &row[x]
|
|
heights.North = calculateWallHeight(tile, DirNorth, tileMap.Tile(posAt(x, y+1)), DirSouth)
|
|
heights.East = calculateWallHeight(tile, DirEast, tileMap.Tile(posAt(x+1, y)), DirWest)
|
|
heights.South = calculateWallHeight(tile, DirSouth, tileMap.Tile(posAt(x, y-1)), DirNorth)
|
|
heights.West = calculateWallHeight(tile, DirWest, tileMap.Tile(posAt(x-1, y)), DirEast)
|
|
}
|
|
}
|
|
}
|
|
|
|
func calculateWallHeight(entry *TileMapEntry, entrySide Direction, other *TileMapEntry, otherSide Direction) [3]float32 {
|
|
var result [3]float32
|
|
baseHeights := func(tile *TileMapEntry, side Direction) (floor TileHeightUnit, slope TileHeightUnit, ceiling TileHeightUnit) {
|
|
floor = TileHeightUnitMax
|
|
slope = TileHeightUnitMin
|
|
ceiling = TileHeightUnitMax
|
|
if (tile != nil) && ((tile.Type.Info().SolidSides & side.AsMask()) == 0) {
|
|
floor = tile.Floor.AbsoluteHeight()
|
|
slope = tile.SlopeHeight
|
|
ceiling = tile.Ceiling.AbsoluteHeight()
|
|
}
|
|
return
|
|
}
|
|
|
|
otherFloorHeight, otherSlopeHeight, otherCeilingHeight := baseHeights(other, otherSide)
|
|
var otherFloorFactors SlopeFactors
|
|
var otherCeilingFactors SlopeFactors
|
|
if other != nil {
|
|
otherSlopeControl := other.Flags.SlopeControl()
|
|
otherFloorFactors = otherSlopeControl.FloorSlopeFactors(other.Type)
|
|
otherCeilingFactors = otherSlopeControl.CeilingSlopeFactors(other.Type)
|
|
}
|
|
|
|
entryFloorHeight, entrySlopeHeight, entryCeilingHeight := baseHeights(entry, entrySide)
|
|
entrySlopeControl := entry.Flags.SlopeControl()
|
|
entryFloorFactors := entrySlopeControl.FloorSlopeFactors(entry.Type)
|
|
entryCeilingFactors := entrySlopeControl.CeilingSlopeFactors(entry.Type)
|
|
|
|
sideHeights := func(side Direction, sideOffset int, factors SlopeFactors, slopeHeight TileHeightUnit, absoluteHeight TileHeightUnit) [3]float32 {
|
|
return [3]float32{
|
|
factors[side.Offset(-sideOffset)]*float32(slopeHeight) + float32(absoluteHeight),
|
|
factors[side.Offset(0)]*float32(slopeHeight) + float32(absoluteHeight),
|
|
factors[side.Offset(+sideOffset)]*float32(slopeHeight) + float32(absoluteHeight),
|
|
}
|
|
}
|
|
entryFloorSideHeights := sideHeights(entrySide, 1, entryFloorFactors, entrySlopeHeight, entryFloorHeight)
|
|
entryCeilingSideHeights := sideHeights(entrySide, 1, entryCeilingFactors.Negated(), entrySlopeHeight, entryCeilingHeight)
|
|
otherFloorSideHeights := sideHeights(otherSide, -1, otherFloorFactors, otherSlopeHeight, otherFloorHeight)
|
|
otherCeilingSideHeights := sideHeights(otherSide, -1, otherCeilingFactors.Negated(), otherSlopeHeight, otherCeilingHeight)
|
|
|
|
for i := 0; i < 3; i++ {
|
|
switch {
|
|
case (otherFloorSideHeights[i] == entryFloorSideHeights[i]) && (entryFloorSideHeights[i] == float32(TileHeightUnitMax)):
|
|
// shortcut for solid-to-solid - no "wall" to see here
|
|
result[i] = 0
|
|
case (otherFloorSideHeights[i] >= otherCeilingSideHeights[i]) || // other ceiling side seals off at its floor
|
|
(otherCeilingSideHeights[i] <= entryFloorSideHeights[i]) || // other ceiling side seals off at this floor
|
|
(otherFloorSideHeights[i] >= entryCeilingSideHeights[i]) || // other floor side seals off at this ceiling
|
|
(entryFloorSideHeights[i] >= entryCeilingSideHeights[i]): // own ceiling side seals off at floor
|
|
result[i] = float32(TileHeightUnitMax)
|
|
default:
|
|
result[i] = otherFloorSideHeights[i] - entryFloorSideHeights[i]
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|