gl01/internal/entity/physics.go

271 lines
6.9 KiB
Go

package entity
import (
"math"
"time"
"edgaru089.ml/go/gl01/internal/util"
"edgaru089.ml/go/gl01/internal/util/itype"
"edgaru089.ml/go/gl01/internal/world"
)
func (e *Entity) worldHitbox(hitbox itype.Vec3d) itype.Boxd {
return itype.Boxd{
OffX: e.pos[0] - hitbox[0]/2,
OffY: e.pos[1],
OffZ: e.pos[2] - hitbox[2]/2,
SizeX: hitbox[0],
SizeY: hitbox[1],
SizeZ: hitbox[2],
}
}
// WorldHitbox returns the hitbox of the entity, in world coordinates.
func (e *Entity) WorldHitbox() itype.Boxd {
return e.worldHitbox(e.b.Hitbox(e.pos, e.ds))
}
// the distance between hitpoints, in blocks
const HitpointMeshLen = 0.4
func (e *Entity) boxHitpoints(points []itype.Vec3d, hitbox itype.Boxd) []itype.Vec3d {
box := hitbox
base := box.MinPoint()
points = points[0:0]
// add the bounding points first
points = append(points,
base,
base.Add(itype.Vec3d{box.SizeX, 0, 0}),
base.Add(itype.Vec3d{0, box.SizeY, 0}),
base.Add(itype.Vec3d{0, 0, box.SizeZ}),
base.Add(itype.Vec3d{box.SizeX, box.SizeY, 0}),
base.Add(itype.Vec3d{0, box.SizeY, box.SizeZ}),
base.Add(itype.Vec3d{box.SizeX, 0, box.SizeZ}),
base.Add(itype.Vec3d{box.SizeX, box.SizeY, box.SizeZ}),
)
return points
}
// MoveEps is a small value used to indicate a tiny amount more than zero.
const MoveEps = 1e-7
// tells if the point is already stuck inside a block hitbox
func pointStuck(point itype.Vec3d, w *world.World) bool {
blockid := point.Floor()
block := w.Block(blockid)
return block.Id != 0 &&
block.Behaviour.Appearance(
point.Floor(),
block.Aux,
block.Dataset,
w,
).Hitbox.Offset(blockid.ToFloat64()).Contains(point)
}
// the move series functions should be called with e.hp already filled
func (e *Entity) moveX(delta float64, hitbox itype.Boxd, w *world.World) {
if math.Abs(delta) < MoveEps*10 {
return
}
var hit bool = false
var deltaMin float64 = 10000
if delta > 1-MoveEps {
delta = 1 - MoveEps
}
if delta < -1+MoveEps {
delta = -1 + MoveEps
}
// X+ / X-
for _, p := range e.hp {
if pointStuck(p, w) {
continue
}
dest := p.Addv(delta, 0, 0)
blockid := dest.Floor()
block := w.Block(blockid)
var deltaDone float64
if block.Id == 0 {
deltaDone = delta
} else { // block.Id!=0
app := world.GetBlockAppearance(blockid, block.Id, block.Aux, block.Dataset, w)
blockBox := app.Hitbox.Offset(blockid.ToFloat64())
if !app.NotSolid && blockBox.Contains(dest) { // Hit!
hit = true
if delta > 0 { // moving X+, hit on the X- face
deltaDone = blockBox.OffX - (e.pos[0] + hitbox.SizeX/2) - MoveEps
//log.Print("Hit X+: On Block ", blockid, ", delta=", delta, ", coord=", e.pos, ", p=", p, ", dest=", dest, ", deltaDone=", deltaDone)
} else { // moving X-, hit on the X+ face
deltaDone = blockBox.OffX + blockBox.SizeX - (e.pos[0] - hitbox.SizeX/2) + MoveEps
//log.Print("Hit X-: On Block ", blockid, ", delta=", delta, ", coord=", e.pos, ", p=", p, ", dest=", dest, ", deltaDone=", deltaDone)
}
} else {
deltaDone = delta
}
}
deltaMin = util.AbsMind(deltaMin, deltaDone)
}
if hit {
e.speed[0] = 0
}
e.pos[0] += deltaMin
}
func (e *Entity) moveY(delta float64, hitbox itype.Boxd, w *world.World) {
if math.Abs(delta) < MoveEps*10 {
return
}
var hit bool = false
var deltaMin float64 = 10000
if delta > 1-MoveEps {
delta = 1 - MoveEps
}
if delta < -1+MoveEps {
delta = -1 + MoveEps
}
// Y+ / Y-
for _, p := range e.hp {
if pointStuck(p, w) {
continue
}
dest := p.Addv(0, delta, 0)
blockid := dest.Floor()
block := w.Block(blockid)
var deltaDone float64
if block.Id == 0 {
deltaDone = delta
} else { // block.Id!=0
app := world.GetBlockAppearance(blockid, block.Id, block.Aux, block.Dataset, w)
blockBox := app.Hitbox.Offset(blockid.ToFloat64())
if !app.NotSolid && blockBox.Contains(dest) { // Hit!
hit = true
if delta > 0 { // moving Y+, hit on the Y- face
deltaDone = blockBox.OffY - (e.pos[1] + hitbox.SizeY) - MoveEps
//log.Print("Hit Y+: On Block ", blockid, ", delta=", delta, ", coord=", e.pos, ", p=", p, ", dest=", dest,", deltaDone=",deltaDone)
} else { // moving Y-, hit on the Y+ face (on the ground)
deltaDone = blockBox.OffY + blockBox.SizeY - e.pos[1] + MoveEps
//log.Print("Hit Y-: On Block ", blockid, ", delta=", delta, ", coord=", e.pos, ", p=", p, ", dest=", dest,", deltaDone=",deltaDone)
/*if !e.onGround {
log.Print("onGround = true")
}*/
e.onGround = true
}
} else {
deltaDone = delta
}
}
deltaMin = util.AbsMind(deltaMin, deltaDone)
}
if hit {
e.speed[1] = 0
}
if math.Abs(deltaMin) > MoveEps*10 {
/*if e.onGround {
log.Print("onGround = false")
}*/
e.onGround = false
}
e.pos[1] += deltaMin
}
func (e *Entity) moveZ(delta float64, hitbox itype.Boxd, w *world.World) {
if math.Abs(delta) < MoveEps*10 {
return
}
var hit bool = false
var deltaMin float64 = 10000
if delta > 1-MoveEps {
delta = 1 - MoveEps
}
if delta < -1+MoveEps {
delta = -1 + MoveEps
}
// Z+ / Z-
for _, p := range e.hp {
if pointStuck(p, w) {
continue
}
dest := p.Addv(0, 0, delta)
blockid := dest.Floor()
block := w.Block(blockid)
var deltaDone float64
if block.Id == 0 {
deltaDone = delta
} else { // block.Id!=0
app := world.GetBlockAppearance(blockid, block.Id, block.Aux, block.Dataset, w)
blockBox := app.Hitbox.Offset(blockid.ToFloat64())
if !app.NotSolid && blockBox.Contains(dest) { // Hit!
hit = true
if delta > 0 { // moving Z+, hit on the Z- face
deltaDone = util.Maxd(blockBox.OffZ-(e.pos[2]+hitbox.SizeZ/2)-MoveEps, 0)
//log.Print("Hit Z+: On Block ", blockid, ", delta=", delta, ", coord=", e.pos, ", p=", p, ", dest=", dest, ", deltaDone=", deltaDone)
} else { // moving Z-, hit on the Z+ face
deltaDone = util.Mind(blockBox.OffZ+blockBox.SizeZ-(e.pos[2]-hitbox.SizeZ/2)+MoveEps, 0)
//log.Print("Hit Z-: On Block ", blockid, ", delta=", delta, ", coord=", e.pos, ", p=", p, ", dest=", dest, ", deltaDone=", deltaDone)
}
} else {
deltaDone = delta
}
}
deltaMin = util.AbsMind(deltaMin, deltaDone)
}
if hit {
e.speed[2] = 0
}
e.pos[2] += deltaMin
}
const gravity float64 = 26
const deaclc float64 = 28
func (e *Entity) Update(world *world.World, deltaTime time.Duration) {
hitbox := e.b.Hitbox(e.pos, e.ds)
box := e.worldHitbox(hitbox)
e.hp = e.hp[0:0]
e.hp = e.boxHitpoints(e.hp, box)
e.moveX(e.speed[0]*deltaTime.Seconds(), box, world)
e.moveY(e.speed[1]*deltaTime.Seconds(), box, world)
e.moveZ(e.speed[2]*deltaTime.Seconds(), box, world)
speed := math.Sqrt(e.speed[0]*e.speed[0] + e.speed[2]*e.speed[2])
if speed > MoveEps {
decrease := deaclc * deltaTime.Seconds()
if !e.onGround {
decrease /= 10
}
factor := util.Maxd(util.Mind(speed-decrease, 9), 0) / speed
e.speed[0] *= factor
e.speed[2] *= factor
}
e.speed[1] -= gravity * deltaTime.Seconds()
e.b.Update(e.pos, e.ds, world, deltaTime)
}