diff --git a/internal/entity/.physics.go.swp b/internal/entity/.physics.go.swp deleted file mode 100644 index 594ecab..0000000 Binary files a/internal/entity/.physics.go.swp and /dev/null differ diff --git a/internal/entity/entities/player.go b/internal/entity/entities/player.go index 03b4a19..8624796 100644 --- a/internal/entity/entities/player.go +++ b/internal/entity/entities/player.go @@ -16,8 +16,11 @@ func init() { func (PlayerBehaviour) Name() string { return "player" } -func (PlayerBehaviour) Hitbox(pos itype.Vec3d, dataset itype.Dataset) itype.Vec3d { - return itype.Vec3d{0.6, 1.8, 0.6} +func (PlayerBehaviour) Hitbox(pos itype.Vec3d, dataset itype.Dataset) []itype.Boxd { + return []itype.Boxd{{ + OffX: -0.3, OffY: 0, OffZ: -0.3, + SizeX: 0.6, SizeY: 1.8, SizeZ: 0.6, + }} } func (PlayerBehaviour) EyeHeight(pos itype.Vec3d, dataset itype.Dataset) float64 { diff --git a/internal/entity/entity.go b/internal/entity/entity.go index c60bdb4..81723c7 100644 --- a/internal/entity/entity.go +++ b/internal/entity/entity.go @@ -3,27 +3,26 @@ package entity import ( "time" - "edgaru089.ml/go/gl01/internal/util" "edgaru089.ml/go/gl01/internal/util/itype" "edgaru089.ml/go/gl01/internal/world" ) // EntityBehaviour describes the behaviour of a type of entity with the same Name. +// It should hold no data of its own. type EntityBehaviour interface { // Name returns the type Name of the behaviour. Name() string - // Hitbox gets the size of the hitbox of the entity. + // Hitbox gets the hitbox(s) of the entity. // - // The hitbox is positioned with the position coordinates pointing to - // the center of the bottom of the box. - Hitbox(pos itype.Vec3d, dataset itype.Dataset) itype.Vec3d + // The hitbox is in entity-local coordinates, originating from the position. + Hitbox(pos itype.Vec3d, dataset itype.Dataset) []itype.Boxd // EyeHeight gets the height of the eye of the entity. EyeHeight(pos itype.Vec3d, dataset itype.Dataset) float64 // Update is called on every frame. - // It should be used to update physics. + // It should be used to update the behaviour of the entity. Update(pos itype.Vec3d, dataset itype.Dataset, world *world.World, deltaTime time.Duration) } @@ -45,14 +44,14 @@ func RegisterEntityBehaviour(b EntityBehaviour) bool { // Entity is a struct holding a Behaviour, a Position and a Speed. type Entity struct { b EntityBehaviour - pos, speed itype.Vec3d // pos has a origin of the **center of the bottom** of the hitbox + pos, speed itype.Vec3d // pos should have a origin of the **center of the bottom** of the hitbox ds itype.Dataset name string // a shortcut to b.Name(), the typename + // physics stuff onGround bool worldbox []itype.Boxd - hp []itype.Vec3d } func NewEntity(typename string, pos itype.Vec3d) *Entity { @@ -84,16 +83,16 @@ func (e *Entity) SetSpeed(speed itype.Vec3d) { e.speed = speed } -func (e *Entity) Hitbox() itype.Vec3d { +func (e *Entity) Hitbox() []itype.Boxd { return e.b.Hitbox(e.pos, e.ds) } func (e *Entity) Accelerate(x, y, z float64) { - /*e.speed[0] += x + e.speed[0] += x e.speed[1] += y - e.speed[2] += z*/ - vec := itype.Vec3d{x, y, z} - e.speed = util.BunnyhopAccelerate(vec, e.speed, vec.Length(), 8) + e.speed[2] += z + /*vec := itype.Vec3d{x, y, z} + e.speed = util.BunnyhopAccelerate(vec, e.speed, vec.Length(), 8)*/ } func (e *Entity) OnGround() bool { diff --git a/internal/entity/physics.go b/internal/entity/physics.go index 794001d..e2726df 100644 --- a/internal/entity/physics.go +++ b/internal/entity/physics.go @@ -9,262 +9,187 @@ import ( "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 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) } -} - -// 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 + return boxes } // 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) +// 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) { + + // 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 } -// the move series functions should be called with e.hp already filled +// 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 -func (e *Entity) moveX(delta float64, hitbox itype.Boxd, w *world.World) { - if math.Abs(delta) < MoveEps*10 { + 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 } - var hit bool = false - var deltaMin float64 = 10000 + for _, eb := range worldHitbox { + for _, bb := range blockHitbox { + // Already intersecting: return + if ok, _ := eb.Intersect(bb); ok { + continue + } - 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) + // 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) } - } else { - deltaDone = delta + 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 } } - - 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 + return } -const gravity float64 = 26 -const deaclc float64 = 28 +const gravity = 26 +const deaclc = 28 +const maxspeed = 8 func (e *Entity) Update(world *world.World, deltaTime time.Duration) { - hitbox := e.b.Hitbox(e.pos, e.ds) - box := e.worldHitbox(hitbox) + /* + 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.hp = e.hp[0:0] - e.hp = e.boxHitpoints(e.hp, box) + e.onGround = onGround + */ - 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) + 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 - 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 + deltaSpeed[0] = -e.speed[0] * (1 - factor) + deltaSpeed[2] = -e.speed[2] * (1 - factor) + } else { + e.speed[0] = 0 + e.speed[2] = 0 } - factor := util.Maxd(util.Mind(speed-decrease, 9), 0) / speed - - e.speed[0] *= factor - e.speed[2] *= factor } + deltaSpeed[1] = -gravity * deltaTime.Seconds() - e.speed[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) } diff --git a/internal/game/imgui.go b/internal/game/imgui.go index e7d3603..1bcae21 100644 --- a/internal/game/imgui.go +++ b/internal/game/imgui.go @@ -3,6 +3,7 @@ package game import ( "fmt" "log" + "math" "os" "runtime" @@ -52,7 +53,8 @@ func (g *Game) imgui() { if imgui.BeginV("Player", nil, imgui.WindowFlagsAlwaysAutoResize) { pos := g.player.Position() vel := g.player.Speed() - imgui.Text(fmt.Sprintf("Pos: (%.5f, %.5f, %.5f), Vel: (%.5f, %.5f, %.5f)", pos[0], pos[1], pos[2], vel[0], vel[1], vel[2])) + igwrap.Text("Pos: (%.5f, %.5f, %.5f), Vel: (%.5f, %.5f, %.5f)", pos[0], pos[1], pos[2], vel[0], vel[1], vel[2]) + igwrap.Text("VelXZ=%.5f, VelXYZ=%.5f", math.Sqrt(vel[0]*vel[0]+vel[2]*vel[2]), vel.Length()) } imgui.End() diff --git a/internal/game/logic.go b/internal/game/logic.go index 2f3c349..fe600d0 100644 --- a/internal/game/logic.go +++ b/internal/game/logic.go @@ -166,6 +166,7 @@ func (g *Game) Init(win *glfw.Window) { igwrap.MouseScrollCallback(xpos, ypos) } }) + } const airAccel = 0.1 @@ -234,7 +235,7 @@ func (g *Game) Update(win *glfw.Window, delta time.Duration) { io.ViewPos = g.player.EyePosition() io.ViewDir = itype.Vec3d(mgl64.Rotate3DY(float64(g.rotY.Radians())).Mul3(mgl64.Rotate3DZ(float64(itype.Degrees(g.rotZ)))).Mul3x1(mgl64.Vec3{1, 0, 0})) - render.Framewire.PushBox(g.player.WorldHitbox().ToFloat32(), color.White) + render.Framewire.PushBox(g.player.WorldHitbox()[0].ToFloat32(), color.White) if g.player.Position()[1] < -100 { g.player.SetPosition(itype.Vec3d{18, 80, 18}) diff --git a/internal/util/itype/rect.go b/internal/util/itype/rect.go index bb40b55..194bbad 100644 --- a/internal/util/itype/rect.go +++ b/internal/util/itype/rect.go @@ -137,15 +137,15 @@ func pointIntersect(n, m, p, q float64) (min, len float64) { return arr[1], arr[2] - arr[1] } -func (box1 Boxd) Intersects(box2 Boxd) (result Boxd) { +func (box1 Boxd) Intersect(box2 Boxd) (ok bool, intersect Boxd) { a, b := pointIntersect(box1.OffX, box1.OffX+box1.SizeX, box2.OffX, box2.OffX+box2.SizeX) c, d := pointIntersect(box1.OffY, box1.OffY+box1.SizeY, box2.OffY, box2.OffY+box2.SizeY) e, f := pointIntersect(box1.OffZ, box1.OffZ+box1.SizeZ, box2.OffZ, box2.OffZ+box2.SizeZ) if b == 0 || d == 0 || f == 0 { - return Boxd{} + return false, Boxd{} } else { - return Boxd{ + return true, Boxd{ OffX: a, SizeX: b, OffY: c, diff --git a/internal/util/itype/vec.go b/internal/util/itype/vec.go index fcb1237..d108b4f 100644 --- a/internal/util/itype/vec.go +++ b/internal/util/itype/vec.go @@ -205,6 +205,14 @@ func (v Vec3d) Floor() Vec3i { } } +func (v Vec3d) Ceiling() Vec3i { + return Vec3i{ + int(math.Ceil(v[0])), + int(math.Ceil(v[1])), + int(math.Ceil(v[2])), + } +} + func (v Vec3d) ToFloat32() Vec3f { return Vec3f{float32(v[0]), float32(v[1]), float32(v[2])} } diff --git a/internal/world/block.go b/internal/world/block.go index af536e7..6e24b79 100644 --- a/internal/world/block.go +++ b/internal/world/block.go @@ -20,10 +20,10 @@ const ( type BlockAppearance struct { Name string // A short name, like "stone" or "dirt", used for texture lookups Transparent bool // Is block transparent? - NotSolid bool // Is block not solid, i.e., has no solid hitbox? (this makes the zero value reasonable) + NotSolid bool // Is block not solid, i.e., has no hitbox at all? (this makes the zero value reasonable) Light int // The light level it emits, 0 is none - Hitbox itype.Boxd // Hitbox, in block-local coordinates; empty slice means a default hitbox of 1x1x1 + Hitbox []itype.Boxd // Hitbox, in block-local coordinates; empty slice means a default hitbox of 1x1x1 RenderType BlockRenderType // Rendering type, defaults to OneTexture (zero value) @@ -124,11 +124,11 @@ func DoneRegisteringBlockBehaviour() { // GetBlockAppearance gets the block appearance of the given block in the fastest way possible. func GetBlockAppearance(position itype.Vec3i, id, aux int, data itype.Dataset, world *World) BlockAppearance { if app, ok := appearance[id]; ok { // Cache - if app.Hitbox == (itype.Boxd{}) { - app.Hitbox = itype.Boxd{ + if len(app.Hitbox) == 0 { + app.Hitbox = []itype.Boxd{{ OffX: 0, OffY: 0, OffZ: 0, SizeX: 1, SizeY: 1, SizeZ: 1, - } + }} } return app } @@ -140,11 +140,11 @@ func GetBlockAppearance(position itype.Vec3i, id, aux int, data itype.Dataset, w } app := b.Appearance(position, aux, data, world) - if app.Hitbox == (itype.Boxd{}) { - app.Hitbox = itype.Boxd{ + if len(app.Hitbox) == 0 { + app.Hitbox = []itype.Boxd{{ OffX: 0, OffY: 0, OffZ: 0, SizeX: 1, SizeY: 1, SizeZ: 1, - } + }} } return app } @@ -165,7 +165,14 @@ type Block struct { // Appearance is a shortcut for Behaviour.Appearance(). // It returns the Appearance of the block with the given parameters. func (b Block) Appearance(position itype.Vec3i) BlockAppearance { - return b.Behaviour.Appearance(position, b.Aux, b.Dataset, b.World) + app := b.Behaviour.Appearance(position, b.Aux, b.Dataset, b.World) + if !app.NotSolid && len(app.Hitbox) == 0 { + app.Hitbox = []itype.Boxd{{ + OffX: 0, OffY: 0, OffZ: 0, + SizeX: 1, SizeY: 1, SizeZ: 1, + }} + } + return app } // BlockUpdate is a shortcut for Behaviour.BlockUpdate().