2022-01-20 21:58:50 +08:00
|
|
|
package world
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
2024-08-02 18:38:33 +08:00
|
|
|
"edgaru089.ink/go/gl01/internal/util/itype"
|
2022-01-20 21:58:50 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
// BlockRenderType is an enum describing the rendering process of a block
|
|
|
|
type BlockRenderType int
|
|
|
|
|
|
|
|
const (
|
|
|
|
OneTexture BlockRenderType = iota // Render with one texture of the same on all faces, "Name.png"
|
|
|
|
ThreeTexture // Render with one texture on the top, one around the sides, and one on the bottom, "Name_top/side/bot.png,"
|
|
|
|
SixTexture // Render with six different textures on six faces, "Name_x+/x-/y+/y-/z+/z-.png"
|
|
|
|
CustomRendering // Rendering calls BlockAppearance.CustomRenderAppend()
|
|
|
|
)
|
|
|
|
|
|
|
|
// BlockAppearance describes basic appearance of a kind of block.
|
|
|
|
type BlockAppearance struct {
|
|
|
|
Name string // A short name, like "stone" or "dirt", used for texture lookups
|
2022-02-24 20:20:33 +08:00
|
|
|
Transparent bool // Is block transparent, i.e., does not block nearby blocks in rendering?
|
2022-02-01 23:48:39 +08:00
|
|
|
NotSolid bool // Is block not solid, i.e., has no hitbox at all? (this makes the zero value reasonable)
|
2022-01-20 21:58:50 +08:00
|
|
|
Light int // The light level it emits, 0 is none
|
|
|
|
|
2022-02-22 14:28:59 +08:00
|
|
|
Hitbox []itype.Boxd // Hitbox, in block-local coordinates; empty slice means a default hitbox of 1x1x1
|
|
|
|
Lookbox []itype.Boxd // Selection hitbox, hit only by the view ray; empty means Hitbox[]
|
2022-01-20 21:58:50 +08:00
|
|
|
|
|
|
|
RenderType BlockRenderType // Rendering type, defaults to OneTexture (zero value)
|
|
|
|
|
|
|
|
// Called on render if RenderType == CustomRendering.
|
|
|
|
//
|
|
|
|
// Be sure to return vertexArray at the end of the function (in case it got reallocated)!!!!
|
|
|
|
CustomRenderAppend func(
|
|
|
|
position itype.Vec3i,
|
|
|
|
aux int,
|
|
|
|
data itype.Dataset,
|
2022-01-28 15:24:03 +08:00
|
|
|
world *World,
|
2022-01-20 21:58:50 +08:00
|
|
|
vertexArray []Vertex,
|
2022-01-29 00:43:11 +08:00
|
|
|
vertexArrayWater []Vertex,
|
|
|
|
) (verts []Vertex, waters []Vertex)
|
2022-01-20 21:58:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// BlockBehaviour describes a kind of block of the same Major ID.
|
|
|
|
type BlockBehaviour interface {
|
|
|
|
|
|
|
|
// Static returns if the Behaviour is "static", i.e., the Appearance does not
|
|
|
|
// change with position, Minor ID or Dataset. Static Behaviours are cached
|
|
|
|
// by the renderer and only generated once.
|
|
|
|
//
|
|
|
|
// Static implies RequireDataset = false and RequireBlockUpdate = false.
|
|
|
|
Static() bool
|
|
|
|
|
|
|
|
// RequireDataset returns if the type of block requires a Dataset attached.
|
|
|
|
RequireDataset() bool
|
|
|
|
|
|
|
|
// RequireBlockUpdate return if BlockUpdate should be called if a neighboring
|
|
|
|
// block has changed. Blocks not requiring BlockUpdate does not change at all.
|
|
|
|
RequireBlockUpdate() bool
|
|
|
|
|
|
|
|
// Appearance returns the Appearance of the block at global position Position,
|
|
|
|
// with Minor ID aux, and Dataset data.
|
|
|
|
//
|
|
|
|
// If RequireDataset if false, data is nil.
|
2022-01-28 15:24:03 +08:00
|
|
|
Appearance(position itype.Vec3i, aux int, data itype.Dataset, world *World) BlockAppearance
|
2022-01-20 21:58:50 +08:00
|
|
|
|
|
|
|
// BlockUpdate is called when RequireBlockUpdate is true and the block at
|
|
|
|
// global position Position, with Minor ID aux, and Dataset data has a neighbor
|
|
|
|
// that changed state. A block will only be updated once in a tick.
|
|
|
|
//
|
|
|
|
// If RequireDataset if false, data is nil.
|
|
|
|
//
|
|
|
|
// Return true if this block also changed state, false otherwise.
|
2022-01-28 15:24:03 +08:00
|
|
|
BlockUpdate(position itype.Vec3i, aux int, data itype.Dataset, world *World) bool
|
2022-02-24 20:20:33 +08:00
|
|
|
|
|
|
|
// Break is called when the block is broken by natural means.
|
|
|
|
Break(position itype.Vec3i, aux int, data itype.Dataset, world *World)
|
2022-01-20 21:58:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
type blockBehaviourStatic struct {
|
|
|
|
app BlockAppearance
|
|
|
|
}
|
|
|
|
|
|
|
|
func (blockBehaviourStatic) Static() bool { return true }
|
|
|
|
func (blockBehaviourStatic) RequireDataset() bool { return false }
|
|
|
|
func (blockBehaviourStatic) RequireBlockUpdate() bool { return false }
|
2022-01-28 15:24:03 +08:00
|
|
|
func (b blockBehaviourStatic) Appearance(position itype.Vec3i, aux int, data itype.Dataset, world *World) BlockAppearance {
|
2022-01-20 21:58:50 +08:00
|
|
|
return b.app
|
|
|
|
}
|
2022-01-28 15:24:03 +08:00
|
|
|
func (blockBehaviourStatic) BlockUpdate(position itype.Vec3i, aux int, data itype.Dataset, world *World) bool {
|
2022-01-20 21:58:50 +08:00
|
|
|
return false
|
|
|
|
}
|
2022-02-24 20:20:33 +08:00
|
|
|
func (blockBehaviourStatic) Break(position itype.Vec3i, aux int, data itype.Dataset, world *World) {}
|
2022-01-20 21:58:50 +08:00
|
|
|
|
|
|
|
// BlockBehaviourStatic returns a Static BlockBehaviour that has the given BlockAppearance.
|
|
|
|
func BlockBehaviourStatic(app BlockAppearance) BlockBehaviour {
|
|
|
|
return blockBehaviourStatic{app: app}
|
|
|
|
}
|
|
|
|
|
|
|
|
var behaviour map[int]BlockBehaviour = make(map[int]BlockBehaviour)
|
|
|
|
var appearance map[int]BlockAppearance = make(map[int]BlockAppearance)
|
|
|
|
var behaviourDoneRegister bool
|
|
|
|
|
|
|
|
// RegisterBlockBehaviour registers behaviour with the given id.
|
|
|
|
//
|
|
|
|
// If the id is already taken, or id == 0, false is returned and nothing is done.
|
|
|
|
// Otherwise, true is returned and the block is registered.
|
|
|
|
func RegisterBlockBehaviour(id int, b BlockBehaviour) bool {
|
|
|
|
if _, ok := behaviour[id]; behaviourDoneRegister || id == 0 || ok {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
behaviour[id] = b
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// DoneRegisteringBlockBehaviour is to be called after Registering BlockBehaviour,
|
|
|
|
// i.e., in Post-Init() initializations.
|
|
|
|
func DoneRegisteringBlockBehaviour() {
|
|
|
|
for id, b := range behaviour {
|
|
|
|
if b.Static() {
|
2022-01-28 15:24:03 +08:00
|
|
|
appearance[id] = b.Appearance(itype.Vec3i{}, 0, nil, nil)
|
2022-01-20 21:58:50 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
behaviourDoneRegister = true
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetBlockAppearance gets the block appearance of the given block in the fastest way possible.
|
2022-01-28 15:24:03 +08:00
|
|
|
func GetBlockAppearance(position itype.Vec3i, id, aux int, data itype.Dataset, world *World) BlockAppearance {
|
2022-01-20 21:58:50 +08:00
|
|
|
if app, ok := appearance[id]; ok { // Cache
|
2022-02-01 23:48:39 +08:00
|
|
|
if len(app.Hitbox) == 0 {
|
|
|
|
app.Hitbox = []itype.Boxd{{
|
2022-01-20 21:58:50 +08:00
|
|
|
OffX: 0, OffY: 0, OffZ: 0,
|
|
|
|
SizeX: 1, SizeY: 1, SizeZ: 1,
|
2022-02-01 23:48:39 +08:00
|
|
|
}}
|
2022-01-20 21:58:50 +08:00
|
|
|
}
|
2022-02-22 14:28:59 +08:00
|
|
|
if len(app.Lookbox) == 0 {
|
|
|
|
app.Lookbox = app.Hitbox
|
|
|
|
}
|
2022-01-20 21:58:50 +08:00
|
|
|
return app
|
|
|
|
}
|
|
|
|
|
|
|
|
// Slow way
|
|
|
|
b, ok := behaviour[id]
|
|
|
|
if !ok {
|
|
|
|
panic(fmt.Sprint("invalid block type ", id))
|
|
|
|
}
|
|
|
|
|
2022-01-28 15:24:03 +08:00
|
|
|
app := b.Appearance(position, aux, data, world)
|
2022-02-01 23:48:39 +08:00
|
|
|
if len(app.Hitbox) == 0 {
|
|
|
|
app.Hitbox = []itype.Boxd{{
|
2022-01-20 21:58:50 +08:00
|
|
|
OffX: 0, OffY: 0, OffZ: 0,
|
|
|
|
SizeX: 1, SizeY: 1, SizeZ: 1,
|
2022-02-01 23:48:39 +08:00
|
|
|
}}
|
2022-01-20 21:58:50 +08:00
|
|
|
}
|
2022-02-22 14:28:59 +08:00
|
|
|
if len(app.Lookbox) == 0 {
|
|
|
|
app.Lookbox = app.Hitbox
|
|
|
|
}
|
2022-01-20 21:58:50 +08:00
|
|
|
return app
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetBlockBehaviour gets the block behaviour of the given id, or nil if not present.
|
|
|
|
func GetBlockBehaviour(id int) BlockBehaviour {
|
|
|
|
return behaviour[id]
|
|
|
|
}
|
|
|
|
|
|
|
|
// Block is a structure to store and pass Blocks around.
|
|
|
|
type Block struct {
|
|
|
|
Id, Aux int
|
|
|
|
Dataset itype.Dataset
|
|
|
|
Behaviour BlockBehaviour
|
2022-01-28 15:24:03 +08:00
|
|
|
World *World
|
2022-01-20 21:58:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Appearance is a shortcut for Behaviour.Appearance().
|
|
|
|
// It returns the Appearance of the block with the given parameters.
|
2022-01-28 15:24:03 +08:00
|
|
|
func (b Block) Appearance(position itype.Vec3i) BlockAppearance {
|
2022-02-24 20:20:33 +08:00
|
|
|
if b.Behaviour == nil {
|
|
|
|
return BlockAppearance{}
|
|
|
|
}
|
2022-02-01 23:48:39 +08:00
|
|
|
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,
|
|
|
|
}}
|
|
|
|
}
|
2022-02-22 14:28:59 +08:00
|
|
|
if len(app.Lookbox) == 0 {
|
|
|
|
if len(app.Hitbox) == 0 {
|
|
|
|
app.Lookbox = []itype.Boxd{{
|
|
|
|
OffX: 0, OffY: 0, OffZ: 0,
|
|
|
|
SizeX: 1, SizeY: 1, SizeZ: 1,
|
|
|
|
}}
|
|
|
|
} else {
|
|
|
|
app.Lookbox = app.Hitbox
|
|
|
|
}
|
|
|
|
}
|
2022-02-01 23:48:39 +08:00
|
|
|
return app
|
2022-01-20 21:58:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// BlockUpdate is a shortcut for Behaviour.BlockUpdate().
|
|
|
|
// It is called when RequireBlockUpdate is true and the block at
|
|
|
|
// global position Position, with Minor ID aux, and Dataset data has a neighbor
|
|
|
|
// that changed state. A block will only be updated once in a tick.
|
|
|
|
//
|
|
|
|
// If RequireDataset if false, data is nil.
|
|
|
|
//
|
|
|
|
// Return true if this block also changed state, false otherwise.
|
2022-01-28 15:24:03 +08:00
|
|
|
func (b Block) BlockUpdate(position itype.Vec3i) bool {
|
|
|
|
return b.Behaviour.BlockUpdate(position, b.Aux, b.Dataset, b.World)
|
2022-01-20 21:58:50 +08:00
|
|
|
}
|