142 lines
4.0 KiB
Go
142 lines
4.0 KiB
Go
|
|
package block
|
||
|
|
|
||
|
|
import (
|
||
|
|
"github.com/df-mc/dragonfly/server/block/cube"
|
||
|
|
"github.com/df-mc/dragonfly/server/internal/nbtconv"
|
||
|
|
"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"
|
||
|
|
"math/rand/v2"
|
||
|
|
"time"
|
||
|
|
)
|
||
|
|
|
||
|
|
// Furnace is a utility block used for the smelting of blocks and items.
|
||
|
|
// The empty value of Furnace is not valid. It must be created using block.NewFurnace(cube.Face).
|
||
|
|
type Furnace struct {
|
||
|
|
solid
|
||
|
|
bassDrum
|
||
|
|
*smelter
|
||
|
|
|
||
|
|
// Facing is the direction the furnace is facing.
|
||
|
|
Facing cube.Direction
|
||
|
|
// Lit is true if the furnace is lit.
|
||
|
|
Lit bool
|
||
|
|
}
|
||
|
|
|
||
|
|
// NewFurnace creates a new initialised furnace. The smelter is properly initialised.
|
||
|
|
func NewFurnace(face cube.Direction) Furnace {
|
||
|
|
return Furnace{
|
||
|
|
Facing: face,
|
||
|
|
smelter: newSmelter(),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Tick is called to check if the furnace should update and start or stop smelting.
|
||
|
|
func (f Furnace) Tick(_ int64, pos cube.Pos, tx *world.Tx) {
|
||
|
|
if f.Lit && rand.Float64() <= 0.016 { // Every three or so seconds.
|
||
|
|
tx.PlaySound(pos.Vec3Centre(), sound.FurnaceCrackle{})
|
||
|
|
}
|
||
|
|
if lit := f.tickSmelting(time.Second*10, time.Millisecond*100, f.Lit, func(item.SmeltInfo) bool {
|
||
|
|
return true
|
||
|
|
}); f.Lit != lit {
|
||
|
|
f.Lit = lit
|
||
|
|
tx.SetBlock(pos, f, nil)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// LightEmissionLevel ...
|
||
|
|
func (f Furnace) LightEmissionLevel() uint8 {
|
||
|
|
if f.Lit {
|
||
|
|
return 13
|
||
|
|
}
|
||
|
|
return 0
|
||
|
|
}
|
||
|
|
|
||
|
|
// EncodeItem ...
|
||
|
|
func (f Furnace) EncodeItem() (name string, meta int16) {
|
||
|
|
return "minecraft:furnace", 0
|
||
|
|
}
|
||
|
|
|
||
|
|
// EncodeBlock ...
|
||
|
|
func (f Furnace) EncodeBlock() (name string, properties map[string]interface{}) {
|
||
|
|
if f.Lit {
|
||
|
|
return "minecraft:lit_furnace", map[string]interface{}{"minecraft:cardinal_direction": f.Facing.String()}
|
||
|
|
}
|
||
|
|
return "minecraft:furnace", map[string]interface{}{"minecraft:cardinal_direction": f.Facing.String()}
|
||
|
|
}
|
||
|
|
|
||
|
|
// UseOnBlock ...
|
||
|
|
func (f Furnace) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, tx *world.Tx, user item.User, ctx *item.UseContext) bool {
|
||
|
|
pos, _, used := firstReplaceable(tx, pos, face, f)
|
||
|
|
if !used {
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
|
||
|
|
place(tx, pos, NewFurnace(user.Rotation().Direction().Opposite()), user, ctx)
|
||
|
|
return placed(ctx)
|
||
|
|
}
|
||
|
|
|
||
|
|
// BreakInfo ...
|
||
|
|
func (f Furnace) BreakInfo() BreakInfo {
|
||
|
|
xp := f.Experience()
|
||
|
|
return newBreakInfo(3.5, alwaysHarvestable, pickaxeEffective, oneOf(Furnace{})).withXPDropRange(xp, xp).withBreakHandler(func(pos cube.Pos, tx *world.Tx, u item.User) {
|
||
|
|
for _, i := range f.Inventory(tx, pos).Clear() {
|
||
|
|
dropItem(tx, i, pos.Vec3())
|
||
|
|
}
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
// Activate ...
|
||
|
|
func (f Furnace) Activate(pos cube.Pos, _ cube.Face, tx *world.Tx, u item.User, _ *item.UseContext) bool {
|
||
|
|
if opener, ok := u.(ContainerOpener); ok {
|
||
|
|
opener.OpenBlockContainer(pos, tx)
|
||
|
|
return true
|
||
|
|
}
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
|
||
|
|
// EncodeNBT ...
|
||
|
|
func (f Furnace) EncodeNBT() map[string]interface{} {
|
||
|
|
if f.smelter == nil {
|
||
|
|
//noinspection GoAssignmentToReceiver
|
||
|
|
f = NewFurnace(f.Facing)
|
||
|
|
}
|
||
|
|
remaining, maximum, cook := f.Durations()
|
||
|
|
return map[string]interface{}{
|
||
|
|
"BurnTime": int16(remaining.Milliseconds() / 50),
|
||
|
|
"CookTime": int16(cook.Milliseconds() / 50),
|
||
|
|
"BurnDuration": int16(maximum.Milliseconds() / 50),
|
||
|
|
"StoredXPInt": int16(f.Experience()),
|
||
|
|
"Items": nbtconv.InvToNBT(f.inventory),
|
||
|
|
"id": "Furnace",
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// DecodeNBT ...
|
||
|
|
func (f Furnace) DecodeNBT(data map[string]interface{}) interface{} {
|
||
|
|
remaining := nbtconv.TickDuration[int16](data, "BurnTime")
|
||
|
|
maximum := nbtconv.TickDuration[int16](data, "BurnDuration")
|
||
|
|
cook := nbtconv.TickDuration[int16](data, "CookTime")
|
||
|
|
|
||
|
|
xp := int(nbtconv.Int16(data, "StoredXPInt"))
|
||
|
|
lit := f.Lit
|
||
|
|
|
||
|
|
//noinspection GoAssignmentToReceiver
|
||
|
|
f = NewFurnace(f.Facing)
|
||
|
|
f.Lit = lit
|
||
|
|
f.setExperience(xp)
|
||
|
|
f.setDurations(remaining, maximum, cook)
|
||
|
|
nbtconv.InvFromNBT(f.inventory, nbtconv.Slice(data, "Items"))
|
||
|
|
return f
|
||
|
|
}
|
||
|
|
|
||
|
|
// allFurnaces ...
|
||
|
|
func allFurnaces() (furnaces []world.Block) {
|
||
|
|
for _, face := range cube.Directions() {
|
||
|
|
furnaces = append(furnaces, Furnace{Facing: face})
|
||
|
|
furnaces = append(furnaces, Furnace{Facing: face, Lit: true})
|
||
|
|
}
|
||
|
|
return
|
||
|
|
}
|