Files
questshock/modding/hacked/ss1/content/archive/level/WallHeights.go
T

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
}