package entity import ( "math" "time" "edgaru089.ink/go/gl01/internal/util" "edgaru089.ink/go/gl01/internal/util/itype" "edgaru089.ink/go/gl01/internal/world" ) // WorldHitbox returns the hitboxes of the entity, in world coordinates. func (e *Entity) WorldHitbox() []itype.Boxd { boxes := e.Hitbox() for i := range boxes { boxes[i] = boxes[i].Offset(e.pos) } return boxes } // MoveEps is a small value used to indicate a tiny amount more than zero. const MoveEps = 1e-7 // move attempts to move the entity by delta in the world. // It does not change anything in e. func (e *Entity) move(delta itype.Vec3d, w *world.World) (finDelta itype.Vec3d, finVelocity itype.Vec3d, onGround bool) { // Truncate the delta to [0, 1] for i := 0; i < len(delta); i++ { if math.Abs(delta[i]) > 1 { delta[i] /= math.Abs(delta[i]) } } // Get the hitboxes and the blocks region covering it hb := e.WorldHitbox() hbmin, hbmax := itype.Vec3d{1000000000, 1000000000, 1000000000}, itype.Vec3d{-1000000000, -1000000000, -1000000000} for _, b := range hb { bmin, bmax := b.MinPoint(), b.MaxPoint() hbmin[0] = util.Mind(hbmin[0], bmin[0]) hbmin[1] = util.Mind(hbmin[1], bmin[1]) hbmin[2] = util.Mind(hbmin[2], bmin[2]) hbmax[0] = util.Maxd(hbmax[0], bmax[0]) hbmax[1] = util.Maxd(hbmax[1], bmax[1]) hbmax[2] = util.Maxd(hbmax[2], bmax[2]) } // So this is the region of blocks we should calculate imin, imax := hbmin.Floor().Addv(-1, -1, -1), hbmax.Ceiling().Addv(1, 1, 1) finDelta = delta finVelocity = e.speed for x := imin[0]; x <= imax[0]; x++ { for y := imin[1]; y <= imax[1]; y++ { for z := imin[2]; z <= imax[2]; z++ { blockDelta, blockVelocity, blockOnGround := e.moveBlock(hb, delta, itype.Vec3i{x, y, z}, w) finDelta[0] = util.AbsMind(finDelta[0], blockDelta[0]) finDelta[1] = util.AbsMind(finDelta[1], blockDelta[1]) finDelta[2] = util.AbsMind(finDelta[2], blockDelta[2]) finVelocity[0] = util.AbsMind(finVelocity[0], blockVelocity[0]) finVelocity[1] = util.AbsMind(finVelocity[1], blockVelocity[1]) finVelocity[2] = util.AbsMind(finVelocity[2], blockVelocity[2]) onGround = onGround || blockOnGround } } } // Y- if delta[1] < 0 { onGround = onGround || e.onGround } else { onGround = false } return } // moveBlock simulates moving the entity by delta, and tests collision with the block hibbox. func (e *Entity) moveBlock(worldHitbox []itype.Boxd, delta itype.Vec3d, blockCoord itype.Vec3i, w *world.World) (blockDelta itype.Vec3d, finVelocity itype.Vec3d, onGround bool) { blockDelta = delta finVelocity = e.speed onGround = false var blockHitbox []itype.Boxd if block := w.Block(blockCoord); block.Id != 0 { blocka := block.Appearance(blockCoord) if !blocka.NotSolid { blockHitbox = w.Block(blockCoord).Appearance(blockCoord).Hitbox for i := range blockHitbox { blockHitbox[i] = blockHitbox[i].Offset(blockCoord.ToFloat64()) } } } if len(blockHitbox) == 0 { return } for _, eb := range worldHitbox { for _, bb := range blockHitbox { // Already intersecting: return if ok, _ := eb.Intersect(bb); ok { continue } // X if ok, _ := eb.Offsetv(blockDelta[0], 0, 0).Intersect(bb); ok { if blockDelta[0] > 0 { blockDelta[0] = bb.OffX - (eb.OffX + eb.SizeX) - MoveEps //log.Printf("Hit(X+): On (X%d,Y%d,Z%d), delta=%v\n", blockCoord[0], blockCoord[1], blockCoord[2], blockDelta) } else { blockDelta[0] = (bb.OffX + bb.SizeX) - eb.OffX + MoveEps //log.Printf("Hit(X-): On (X%d,Y%d,Z%d), delta=%v\n", blockCoord[0], blockCoord[1], blockCoord[2], blockDelta) } finVelocity[0] = 0 } // Y if ok, _ := eb.Offsetv(0, blockDelta[1], 0).Intersect(bb); ok { if blockDelta[1] > 0 { blockDelta[1] = bb.OffY - (eb.OffY + eb.SizeY) - MoveEps //log.Printf("Hit(Y+): On (X%d,Y%d,Z%d), delta=%v\n", blockCoord[0], blockCoord[1], blockCoord[2], blockDelta) } else { onGround = true blockDelta[1] = (bb.OffY + bb.SizeY) - eb.OffY + MoveEps //log.Printf("Hit(Y-): On (X%d,Y%d,Z%d), delta=%v\n", blockCoord[0], blockCoord[1], blockCoord[2], blockDelta) } finVelocity[1] = 0 } // Z if ok, _ := eb.Offsetv(0, 0, blockDelta[2]).Intersect(bb); ok { if blockDelta[2] > 0 { blockDelta[2] = bb.OffZ - (eb.OffZ + eb.SizeZ) - MoveEps //log.Printf("Hit(Z+): On (X%d,Y%d,Z%d), delta=%v\n", blockCoord[0], blockCoord[1], blockCoord[2], blockDelta) } else { blockDelta[2] = (bb.OffZ + bb.SizeZ) - eb.OffZ + MoveEps //log.Printf("Hit(Z-): On (X%d,Y%d,Z%d), delta=%v\n", blockCoord[0], blockCoord[1], blockCoord[2], blockDelta) } finVelocity[2] = 0 } } } return } const gravity = 26 const deaclc = 28 const maxspeed = 8 func (e *Entity) Update(world *world.World, deltaTime time.Duration) { /* delta, vec, onGround := e.move(itype.Vec3d{e.speed[0] * deltaTime.Seconds()}, world) e.pos = e.pos.Add(delta) e.speed = vec delta, vec, onGround = e.move(e.speed.Multiply(deltaTime.Seconds()), world) e.pos = e.pos.Add(delta) e.speed = vec delta, vec, onGround = e.move(e.speed.Multiply(deltaTime.Seconds()), world) e.pos = e.pos.Add(delta) e.speed = vec e.onGround = onGround */ var deltaSpeed itype.Vec3d if e.onGround { speed := math.Sqrt(e.speed[0]*e.speed[0] + e.speed[2]*e.speed[2]) if speed > MoveEps { decrease := deaclc * deltaTime.Seconds() factor := util.Maxd(util.Mind(speed-decrease, 6), 0) / speed deltaSpeed[0] = -e.speed[0] * (1 - factor) deltaSpeed[2] = -e.speed[2] * (1 - factor) } else { e.speed[0] = 0 e.speed[2] = 0 } } deltaSpeed[1] = -gravity * deltaTime.Seconds() delta := e.speed.Multiply(deltaTime.Seconds()).Add(deltaSpeed.Multiply(deltaTime.Seconds() / 2)) e.speed = e.speed.Add(deltaSpeed) findelta, vec, onGround := e.move(delta, world) e.pos = e.pos.Add(findelta) e.speed = vec e.onGround = onGround if !e.onGround { vecXZ := itype.Vec2d{e.speed[0], e.speed[2]} if vecXZ.Length() > maxspeed { vecXZ = vecXZ.Normalize().Multiply(maxspeed) } e.speed[0] = vecXZ[0] e.speed[2] = vecXZ[1] } e.b.Update(e.pos, e.ds, world, deltaTime) }