up3
Build and deploy / Build (push) Has been cancelled
Build and deploy / Update Contributors (push) Has been cancelled
Build and deploy / Deploy (push) Has been cancelled
Build and deploy / Build (push) Has been cancelled
Build and deploy / Update Contributors (push) Has been cancelled
Build and deploy / Deploy (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,376 @@
|
||||
package block
|
||||
|
||||
import (
|
||||
"math/rand/v2"
|
||||
"time"
|
||||
|
||||
"github.com/df-mc/dragonfly/server/block/cube"
|
||||
"github.com/df-mc/dragonfly/server/block/customblock"
|
||||
"github.com/df-mc/dragonfly/server/item"
|
||||
"github.com/df-mc/dragonfly/server/world"
|
||||
"github.com/df-mc/dragonfly/server/world/sound"
|
||||
"github.com/go-gl/mathgl/mgl64"
|
||||
)
|
||||
|
||||
// Activatable represents a block that may be activated by a viewer of the world. When activated, the block
|
||||
// will execute some specific logic.
|
||||
type Activatable interface {
|
||||
// Activate activates the block at a specific block position. The face clicked is passed, as well as the
|
||||
// world in which the block was activated and the viewer that activated it.
|
||||
// Activate returns a bool indicating if activating the block was used successfully.
|
||||
Activate(pos cube.Pos, clickedFace cube.Face, tx *world.Tx, u item.User, ctx *item.UseContext) bool
|
||||
}
|
||||
|
||||
// Pickable represents a block that may give a different item then the block itself when picked.
|
||||
type Pickable interface {
|
||||
// Pick returns the item that is picked when the block is picked.
|
||||
Pick() item.Stack
|
||||
}
|
||||
|
||||
// Punchable represents a block that may be punched by a viewer of the world. When punched, the block
|
||||
// will execute some specific logic.
|
||||
type Punchable interface {
|
||||
// Punch punches the block at a specific block position. The face clicked is passed, as well as the
|
||||
// world in which the block was punched and the viewer that punched it.
|
||||
Punch(pos cube.Pos, clickedFace cube.Face, tx *world.Tx, u item.User)
|
||||
}
|
||||
|
||||
// LightEmitter represents a block that emits light when placed. Blocks such as torches or lanterns implement
|
||||
// this interface.
|
||||
type LightEmitter interface {
|
||||
// LightEmissionLevel returns the light emission level of the block, a number from 0-15 where 15 is the
|
||||
// brightest and 0 means it doesn't emit light at all.
|
||||
LightEmissionLevel() uint8
|
||||
}
|
||||
|
||||
// LightDiffuser represents a block that diffuses light. This means that a specific amount of light levels
|
||||
// will be subtracted when light passes through the block.
|
||||
// Blocks that do not implement LightDiffuser will be assumed to be solid: Light will not be able to pass
|
||||
// through these blocks.
|
||||
type LightDiffuser interface {
|
||||
// LightDiffusionLevel returns the amount of light levels that is subtracted when light passes through
|
||||
// this block. Some blocks, such as leaves, have this behaviour. A diffusion level of 15 means that all
|
||||
// light will be completely blocked when it passes through the block.
|
||||
LightDiffusionLevel() uint8
|
||||
}
|
||||
|
||||
// RedstoneWireStepDowner represents a block with custom behaviour for redstone wire providing power when travelling
|
||||
// down it.
|
||||
type RedstoneWireStepDowner interface {
|
||||
// CanRedstoneWireStepDown returns whether redstone wire may provide power while travelling down the block at
|
||||
// pos from the wire position passed.
|
||||
CanRedstoneWireStepDown(pos, from cube.Pos, tx *world.Tx) bool
|
||||
}
|
||||
|
||||
// Replaceable represents a block that may be replaced by another block automatically. An example is grass,
|
||||
// which may be replaced by clicking it with another block.
|
||||
type Replaceable interface {
|
||||
// ReplaceableBy returns a bool which indicates if the block is replaceableWith by another block.
|
||||
ReplaceableBy(b world.Block) bool
|
||||
}
|
||||
|
||||
// EntityLander represents a block that reacts to an entity landing on it after falling.
|
||||
type EntityLander interface {
|
||||
// EntityLand is called when an entity lands on the block.
|
||||
EntityLand(pos cube.Pos, tx *world.Tx, e world.Entity, distance *float64)
|
||||
}
|
||||
|
||||
// EntityInsider represents a block that reacts to an entity going inside its 1x1x1 axis
|
||||
// aligned bounding box.
|
||||
type EntityInsider interface {
|
||||
// EntityInside is called when an entity goes inside the block's 1x1x1 axis aligned bounding box.
|
||||
EntityInside(pos cube.Pos, tx *world.Tx, e world.Entity)
|
||||
}
|
||||
|
||||
// EntityStepper represents a block that reacts to an entity standing on top of it.
|
||||
type EntityStepper interface {
|
||||
// EntityStepOn is called every tick while an entity is standing on the top face of the block.
|
||||
EntityStepOn(pos cube.Pos, tx *world.Tx, e world.Entity)
|
||||
}
|
||||
|
||||
// ProjectileHitter represents a block that handles being hit by a projectile.
|
||||
type ProjectileHitter interface {
|
||||
// ProjectileHit is called when a projectile hits the block.
|
||||
ProjectileHit(pos cube.Pos, tx *world.Tx, e world.Entity, face cube.Face)
|
||||
}
|
||||
|
||||
// Frictional represents a block that may have a custom friction value. Friction is used for entity drag when the
|
||||
// entity is on ground. If a block does not implement this interface, it should be assumed that its friction is 0.6.
|
||||
type Frictional interface {
|
||||
// Friction returns the block's friction value.
|
||||
Friction() float64
|
||||
}
|
||||
|
||||
// Permutable represents a custom block that can have more permutations than its default state.
|
||||
type Permutable interface {
|
||||
// States returns a map of all the different properties for the block. The key is the property name, and the value
|
||||
// is a slice of all the possible values for that property. It is important that a block is registered in dragonfly
|
||||
// for each of the possible combinations of properties and values.
|
||||
States() map[string][]any
|
||||
// Permutations returns a slice of all the different permutations for the block. Multiple permutations can be
|
||||
// applied at once if their conditions are met.
|
||||
Permutations() []customblock.Permutation
|
||||
}
|
||||
|
||||
// unknownFace is a face that is used for certain block items. This should not be exposed in the API.
|
||||
var unknownFace = cube.Face(len(cube.Faces()))
|
||||
|
||||
// unknownDirection is a direction that is used for certain block items. This should not be exposed in the API.
|
||||
var unknownDirection = cube.Direction(len(cube.Directions()))
|
||||
|
||||
func calculateFace(user item.User, placePos cube.Pos) cube.Face {
|
||||
userPos := user.Position()
|
||||
pos := cube.PosFromVec3(userPos)
|
||||
if abs(pos[0]-placePos[0]) < 2 && abs(pos[2]-placePos[2]) < 2 {
|
||||
y := userPos[1]
|
||||
if eyed, ok := user.(interface{ EyeHeight() float64 }); ok {
|
||||
y += eyed.EyeHeight()
|
||||
}
|
||||
|
||||
if y-float64(placePos[1]) > 2.0 {
|
||||
return cube.FaceUp
|
||||
} else if float64(placePos[1])-y > 0.0 {
|
||||
return cube.FaceDown
|
||||
}
|
||||
}
|
||||
return user.Rotation().Direction().Opposite().Face()
|
||||
}
|
||||
|
||||
func abs(x int) int {
|
||||
if x > 0 {
|
||||
return x
|
||||
}
|
||||
return -x
|
||||
}
|
||||
|
||||
// replaceableWith checks if the block at the position passed is replaceable with the block passed.
|
||||
func replaceableWith(tx *world.Tx, pos cube.Pos, with world.Block) bool {
|
||||
if pos.OutOfBounds(tx.Range()) {
|
||||
return false
|
||||
}
|
||||
b := tx.Block(pos)
|
||||
if replaceable, ok := b.(Replaceable); ok {
|
||||
if !replaceable.ReplaceableBy(with) || b == with {
|
||||
return false
|
||||
}
|
||||
if liquid, ok := tx.Liquid(pos); ok {
|
||||
replaceable, ok := liquid.(Replaceable)
|
||||
return ok && replaceable.ReplaceableBy(with)
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// firstReplaceable finds the first replaceable block position eligible to have a block placed on it after
|
||||
// clicking on the position and face passed.
|
||||
// If none can be found, the bool returned is false.
|
||||
func firstReplaceable(tx *world.Tx, pos cube.Pos, face cube.Face, with world.Block) (cube.Pos, cube.Face, bool) {
|
||||
if replaceableWith(tx, pos, with) {
|
||||
// A replaceableWith block was clicked, so we can replace it. This will then be assumed to be placed on
|
||||
// the top face. (Torches, for example, will get attached to the floor when clicking tall grass.)
|
||||
return pos, cube.FaceUp, true
|
||||
}
|
||||
side := pos.Side(face)
|
||||
if replaceableWith(tx, side, with) {
|
||||
return side, face, true
|
||||
}
|
||||
return pos, face, false
|
||||
}
|
||||
|
||||
// place places the block passed at the position passed. If the user implements the block.Placer interface, it
|
||||
// will use its PlaceBlock method. If not, the block is placed without interaction from the user.
|
||||
func place(tx *world.Tx, pos cube.Pos, b world.Block, user item.User, ctx *item.UseContext) {
|
||||
if placer, ok := user.(Placer); ok {
|
||||
placer.PlaceBlock(pos, b, ctx)
|
||||
return
|
||||
}
|
||||
tx.SetBlock(pos, b, nil)
|
||||
tx.PlaySound(pos.Vec3(), sound.BlockPlace{Block: b})
|
||||
}
|
||||
|
||||
// horizontalDirection returns the horizontal direction of the given direction. This is a legacy type still used in
|
||||
// various blocks.
|
||||
func horizontalDirection(d cube.Direction) cube.Direction {
|
||||
switch d {
|
||||
case cube.South:
|
||||
return cube.North
|
||||
case cube.West:
|
||||
return cube.South
|
||||
case cube.North:
|
||||
return cube.West
|
||||
case cube.East:
|
||||
return cube.East
|
||||
}
|
||||
panic("invalid direction")
|
||||
}
|
||||
|
||||
// placed checks if an item was placed with the use context passed.
|
||||
func placed(ctx *item.UseContext) bool {
|
||||
return ctx.CountSub > 0
|
||||
}
|
||||
|
||||
// boolByte returns 1 if the bool passed is true, or 0 if it is false.
|
||||
func boolByte(b bool) uint8 {
|
||||
if b {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// replaceable is a struct that may be embedded to make a block replaceable by any other block.
|
||||
type replaceable struct{}
|
||||
|
||||
// ReplaceableBy ...
|
||||
func (replaceable) ReplaceableBy(world.Block) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// transparent is a struct that may be embedded to make a block transparent to light. Light will be able to
|
||||
// pass through this block freely.
|
||||
type transparent struct{}
|
||||
|
||||
// LightDiffusionLevel ...
|
||||
func (transparent) LightDiffusionLevel() uint8 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// gravityAffected is a struct that may be embedded for blocks affected by gravity.
|
||||
type gravityAffected struct{}
|
||||
|
||||
// Solidifies ...
|
||||
func (g gravityAffected) Solidifies(cube.Pos, *world.Tx) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// fall spawns a falling block entity at the given position.
|
||||
func (g gravityAffected) fall(b world.Block, pos cube.Pos, tx *world.Tx) {
|
||||
if replaceableWith(tx, pos.Side(cube.FaceDown), b) {
|
||||
tx.SetBlock(pos, nil, nil)
|
||||
opts := world.EntitySpawnOpts{Position: pos.Vec3Centre()}
|
||||
tx.AddEntity(tx.World().EntityRegistry().Config().FallingBlock(opts, b))
|
||||
}
|
||||
}
|
||||
|
||||
// Flammable is an interface for blocks that can catch on fire.
|
||||
type Flammable interface {
|
||||
// FlammabilityInfo returns information about a block's behaviour involving fire.
|
||||
FlammabilityInfo() FlammabilityInfo
|
||||
}
|
||||
|
||||
// FlammabilityInfo contains values related to block behaviours involving fire.
|
||||
type FlammabilityInfo struct {
|
||||
// Encouragement is the chance a block will catch on fire during attempted fire spread.
|
||||
Encouragement int
|
||||
// Flammability is the chance a block will burn away during a fire block tick.
|
||||
Flammability int
|
||||
// LavaFlammable returns whether it can catch on fire from lava.
|
||||
LavaFlammable bool
|
||||
}
|
||||
|
||||
// newFlammabilityInfo creates a FlammabilityInfo struct with the properties passed.
|
||||
func newFlammabilityInfo(encouragement, flammability int, lavaFlammable bool) FlammabilityInfo {
|
||||
return FlammabilityInfo{
|
||||
Encouragement: encouragement,
|
||||
Flammability: flammability,
|
||||
LavaFlammable: lavaFlammable,
|
||||
}
|
||||
}
|
||||
|
||||
// livingEntity ...
|
||||
type livingEntity interface {
|
||||
// Hurt hurts the entity for a given amount of damage. The source passed represents the cause of the
|
||||
// damage, for example damage.SourceEntityAttack if the entity is attacked by another entity.
|
||||
// If the final damage exceeds the health that the entity currently has, the entity is killed.
|
||||
// Hurt returns the final amount of damage dealt to the Living entity and returns whether the Living entity
|
||||
// was vulnerable to the damage at all.
|
||||
Hurt(damage float64, src world.DamageSource) (n float64, vulnerable bool)
|
||||
}
|
||||
|
||||
// flammableEntity ...
|
||||
type flammableEntity interface {
|
||||
// OnFireDuration returns duration of fire in ticks.
|
||||
OnFireDuration() time.Duration
|
||||
// SetOnFire sets the entity on fire for the specified duration.
|
||||
SetOnFire(duration time.Duration)
|
||||
// Extinguish extinguishes the entity.
|
||||
Extinguish()
|
||||
}
|
||||
|
||||
// dropItem ...
|
||||
func dropItem(tx *world.Tx, it item.Stack, pos mgl64.Vec3) {
|
||||
create := tx.World().EntityRegistry().Config().Item
|
||||
opts := world.EntitySpawnOpts{Position: pos, Velocity: mgl64.Vec3{rand.Float64()*0.2 - 0.1, 0.2, rand.Float64()*0.2 - 0.1}}
|
||||
tx.AddEntity(create(opts, it))
|
||||
}
|
||||
|
||||
// bass is a struct that may be embedded for blocks that create a bass sound.
|
||||
type bass struct{}
|
||||
|
||||
// Instrument ...
|
||||
func (bass) Instrument() sound.Instrument {
|
||||
return sound.Bass()
|
||||
}
|
||||
|
||||
// snare is a struct that may be embedded for blocks that create a snare drum sound.
|
||||
type snare struct{}
|
||||
|
||||
// Instrument ...
|
||||
func (snare) Instrument() sound.Instrument {
|
||||
return sound.Snare()
|
||||
}
|
||||
|
||||
// clicksAndSticks is a struct that may be embedded for blocks that create a clicks and sticks sound.
|
||||
type clicksAndSticks struct{}
|
||||
|
||||
// Instrument ...
|
||||
func (clicksAndSticks) Instrument() sound.Instrument {
|
||||
return sound.ClicksAndSticks()
|
||||
}
|
||||
|
||||
// bassDrum is a struct that may be embedded for blocks that create a bass drum sound.
|
||||
type bassDrum struct{}
|
||||
|
||||
// Instrument ...
|
||||
func (bassDrum) Instrument() sound.Instrument {
|
||||
return sound.BassDrum()
|
||||
}
|
||||
|
||||
// flute is a struct that may be embedded for blocks that create a flute sound.
|
||||
type flute struct{}
|
||||
|
||||
// Instrument ...
|
||||
func (flute) Instrument() sound.Instrument {
|
||||
return sound.Flute()
|
||||
}
|
||||
|
||||
// newSmeltInfo returns a new SmeltInfo with the given values.
|
||||
func newSmeltInfo(product item.Stack, experience float64) item.SmeltInfo {
|
||||
return item.SmeltInfo{
|
||||
Product: product,
|
||||
Experience: experience,
|
||||
}
|
||||
}
|
||||
|
||||
// newFoodSmeltInfo returns a new SmeltInfo with the given values that allows smelting in a smelter.
|
||||
func newFoodSmeltInfo(product item.Stack, experience float64) item.SmeltInfo {
|
||||
return item.SmeltInfo{
|
||||
Product: product,
|
||||
Experience: experience,
|
||||
Food: true,
|
||||
}
|
||||
}
|
||||
|
||||
// newOreSmeltInfo returns a new SmeltInfo with the given values that allows smelting in a blast furnace.
|
||||
func newOreSmeltInfo(product item.Stack, experience float64) item.SmeltInfo {
|
||||
return item.SmeltInfo{
|
||||
Product: product,
|
||||
Experience: experience,
|
||||
Ores: true,
|
||||
}
|
||||
}
|
||||
|
||||
// newFuelInfo returns a new FuelInfo with the given values.
|
||||
func newFuelInfo(duration time.Duration) item.FuelInfo {
|
||||
return item.FuelInfo{Duration: duration}
|
||||
}
|
||||
Reference in New Issue
Block a user