1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2025-02-13 03:56:31 -05:00
OpenDiablo2/d2common/d2math/d2vector/position.go
danhale-git 894c60f77a
Map entity rework - vectors in mapEntity (#579)
* Fixed NaN on normalize 0 vector.

* Tentative implementation in getStepLength.

* mapEntity.TileX/Y removed.

* Fixed Position and Target not being initialised in createMapEntity.

* mapEntity.Position is no longer embedded.

* mapEntity.LocationX/Y no longer used outside map_entity.go.

* mapEntity.subCellX/Y removed.

* mapEntity.LocationX/Y removed.

* mapEntity.OffsetX/Y and TargetX/Y removed.

* Direction method added to Vector, returns 0-64 direction.

* Moved Vector.Direction() to Position.DirectionTo and refactored.

* Renamed RenderOffset from SubCell and tested IsZero.

* d2math.WrapInt added for use with directions.

* Tidied up position and tests.

* Refactored d2common.AdjustWithRemainder into d2mapEntity.

* Tidying up d2mapEntity.

* Final cleanup.

* Lint warnings.

* Spelling correction.
2020-07-13 09:06:50 -04:00

89 lines
2.9 KiB
Go

package d2vector
import (
"fmt"
"math"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2math"
)
const (
subTilesPerTile float64 = 5 // The number of sub tiles that make up one map tile.
entityDirectionCount float64 = 64 // The Diablo equivalent of 360 degrees when dealing with entity rotation.
entityDirectionIncrement float64 = 8 // One 8th of 64. There 8 possible facing directions for entities.
// Note: there should be 16 facing directions for entities. See Position.DirectionTo()
)
// Position is a vector in world space. The stored value is the sub tile position.
type Position struct {
Vector
}
// NewPosition returns a Position struct with the given sub tile coordinates where 1 = 1 sub tile, with a fractional
// offset.
func NewPosition(x, y float64) Position {
p := Position{NewVector(x, y)}
p.checkValues()
return p
}
// Set sets this position to the given sub tile coordinates where 1 = 1 sub tile, with a fractional offset.
func (p *Position) Set(x, y float64) {
p.x, p.y = x, y
p.checkValues()
}
func (p *Position) checkValues() {
if math.IsNaN(p.x) || math.IsNaN(p.y) {
panic(fmt.Sprintf("float value is NaN: %s", p.Vector))
}
if math.IsInf(p.x, 0) || math.IsInf(p.y, 0) {
panic(fmt.Sprintf("float value is Inf: %s", p.Vector))
}
}
// World is the exact position where 1 = one map tile and 0.2 = one sub tile.
func (p *Position) World() *Vector {
c := p.Clone()
return c.DivideScalar(subTilesPerTile)
}
// Tile is the position of the current map tile. It is the floor of World(), always a whole number.
func (p *Position) Tile() *Vector {
return p.World().Floor()
}
// RenderOffset is the offset in sub tiles from the curren tile, + 1. This places the vector at the bottom vertex of an
// isometric diamond visually representing one sub tile. Sub tile indices increase to the lower right diagonal ('down')
// and to the lower left diagonal ('left') of the isometric grid. This renders the target one index above which visually
// is one tile below.
func (p *Position) RenderOffset() *Vector {
return p.subTileOffset().AddScalar(1)
}
// SubTileOffset is the offset from the current map tile in sub tiles.
func (p *Position) subTileOffset() *Vector {
t := p.Tile().Scale(subTilesPerTile)
c := p.Clone()
return c.Subtract(t)
}
// DirectionTo returns the entity direction from this vector to the given vector.
func (v *Vector) DirectionTo(target Vector) int {
direction := target.Clone()
direction.Subtract(v)
angle := direction.SignedAngle(VectorRight())
radiansPerDirection := d2math.RadFull / entityDirectionCount
// Note: The direction is always one increment out so we must subtract one increment.
// This might not work when we implement all 16 directions (as of this writing entities can only face one of 8
// directions). See entityDirectionIncrement.
newDirection := int((angle / radiansPerDirection) - entityDirectionIncrement)
return d2math.WrapInt(newDirection, int(entityDirectionCount))
}