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,299 @@
|
||||
package block
|
||||
|
||||
import (
|
||||
"github.com/df-mc/dragonfly/server/block/cube"
|
||||
"github.com/df-mc/dragonfly/server/block/model"
|
||||
"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"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Campfire is a block that can be used to cook food, pacify bees, act as a spread-proof light source, smoke signal or
|
||||
// damaging trap block.
|
||||
type Campfire struct {
|
||||
transparent
|
||||
bass
|
||||
sourceWaterDisplacer
|
||||
|
||||
// Items represents the items in the campfire that are being cooked.
|
||||
Items [4]CampfireItem
|
||||
// Facing represents the direction that the campfire is facing.
|
||||
Facing cube.Direction
|
||||
// Extinguished is true if the campfire was extinguished by a water source.
|
||||
Extinguished bool
|
||||
// Type represents the type of Campfire, currently there are Normal and Soul campfires.
|
||||
Type FireType
|
||||
}
|
||||
|
||||
// CampfireItem holds data about the items in the campfire.
|
||||
type CampfireItem struct {
|
||||
// Item is a specific item being cooked on top of the campfire.
|
||||
Item item.Stack
|
||||
// Time is the countdown of ticks until the food item is cooked (when 0).
|
||||
Time time.Duration
|
||||
}
|
||||
|
||||
// Model ...
|
||||
func (Campfire) Model() world.BlockModel {
|
||||
return model.Campfire{}
|
||||
}
|
||||
|
||||
// SideClosed ...
|
||||
func (Campfire) SideClosed(cube.Pos, cube.Pos, *world.Tx) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// BreakInfo ...
|
||||
func (c Campfire) BreakInfo() BreakInfo {
|
||||
return newBreakInfo(2, alwaysHarvestable, axeEffective, func(t item.Tool, enchantments []item.Enchantment) []item.Stack {
|
||||
if hasSilkTouch(enchantments) {
|
||||
return []item.Stack{item.NewStack(Campfire{Type: c.Type}, 1)}
|
||||
}
|
||||
switch c.Type {
|
||||
case NormalFire():
|
||||
return []item.Stack{item.NewStack(item.Charcoal{}, 2)}
|
||||
case SoulFire():
|
||||
return []item.Stack{item.NewStack(SoulSoil{}, 1)}
|
||||
}
|
||||
panic("should never happen")
|
||||
}).withBreakHandler(func(pos cube.Pos, tx *world.Tx, u item.User) {
|
||||
for _, v := range c.Items {
|
||||
if !v.Item.Empty() {
|
||||
dropItem(tx, v.Item, pos.Vec3Centre())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// LightEmissionLevel ...
|
||||
func (c Campfire) LightEmissionLevel() uint8 {
|
||||
if c.Extinguished {
|
||||
return 0
|
||||
}
|
||||
return c.Type.LightLevel()
|
||||
}
|
||||
|
||||
// Ignite ...
|
||||
func (c Campfire) Ignite(pos cube.Pos, tx *world.Tx, _ world.Entity) bool {
|
||||
tx.PlaySound(pos.Vec3(), sound.Ignite{})
|
||||
if !c.Extinguished {
|
||||
return false
|
||||
}
|
||||
if _, ok := tx.Liquid(pos); ok {
|
||||
return false
|
||||
}
|
||||
|
||||
c.Extinguished = false
|
||||
tx.SetBlock(pos, c, nil)
|
||||
return true
|
||||
}
|
||||
|
||||
// Splash ...
|
||||
func (c Campfire) Splash(tx *world.Tx, pos cube.Pos) {
|
||||
if c.Extinguished {
|
||||
return
|
||||
}
|
||||
|
||||
c.extinguish(pos, tx)
|
||||
}
|
||||
|
||||
// extinguish extinguishes the campfire.
|
||||
func (c Campfire) extinguish(pos cube.Pos, tx *world.Tx) {
|
||||
tx.PlaySound(pos.Vec3Centre(), sound.FireExtinguish{})
|
||||
c.Extinguished = true
|
||||
|
||||
for i := range c.Items {
|
||||
c.Items[i].Time = time.Second * 30
|
||||
}
|
||||
|
||||
tx.SetBlock(pos, c, nil)
|
||||
}
|
||||
|
||||
// Activate ...
|
||||
func (c Campfire) Activate(pos cube.Pos, _ cube.Face, tx *world.Tx, u item.User, ctx *item.UseContext) bool {
|
||||
held, _ := u.HeldItems()
|
||||
if held.Empty() {
|
||||
return false
|
||||
}
|
||||
|
||||
if _, ok := held.Item().(item.Shovel); ok && !c.Extinguished {
|
||||
c.extinguish(pos, tx)
|
||||
ctx.DamageItem(1)
|
||||
return true
|
||||
}
|
||||
|
||||
rawFood, ok := held.Item().(item.Smeltable)
|
||||
if !ok || !rawFood.SmeltInfo().Food {
|
||||
return false
|
||||
}
|
||||
|
||||
if _, ok = tx.Liquid(pos); ok {
|
||||
return false
|
||||
}
|
||||
|
||||
for i, it := range c.Items {
|
||||
if it.Item.Empty() {
|
||||
c.Items[i] = CampfireItem{
|
||||
Item: held.Grow(-held.Count() + 1),
|
||||
Time: time.Second * 30,
|
||||
}
|
||||
|
||||
ctx.SubtractFromCount(1)
|
||||
|
||||
tx.PlaySound(pos.Vec3Centre(), sound.ItemAdd{})
|
||||
tx.SetBlock(pos, c, nil)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// UseOnBlock ...
|
||||
func (c Campfire) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, tx *world.Tx, user item.User, ctx *item.UseContext) (used bool) {
|
||||
pos, _, used = firstReplaceable(tx, pos, face, c)
|
||||
if !used {
|
||||
return
|
||||
}
|
||||
if _, ok := tx.Block(pos.Side(cube.FaceDown)).(Campfire); ok {
|
||||
return false
|
||||
}
|
||||
c.Facing = user.Rotation().Direction().Opposite()
|
||||
place(tx, pos, c, user, ctx)
|
||||
return placed(ctx)
|
||||
}
|
||||
|
||||
// Tick is called to cook the items within the campfire.
|
||||
func (c Campfire) Tick(_ int64, pos cube.Pos, tx *world.Tx) {
|
||||
if c.Extinguished {
|
||||
// Extinguished, do nothing.
|
||||
return
|
||||
}
|
||||
if rand.Float64() <= 0.016 { // Every three or so seconds.
|
||||
tx.PlaySound(pos.Vec3Centre(), sound.CampfireCrackle{})
|
||||
}
|
||||
|
||||
updated := false
|
||||
for i, it := range c.Items {
|
||||
if it.Item.Empty() {
|
||||
continue
|
||||
}
|
||||
|
||||
updated = true
|
||||
if it.Time > 0 {
|
||||
c.Items[i].Time = it.Time - time.Millisecond*50
|
||||
continue
|
||||
}
|
||||
|
||||
if food, ok := it.Item.Item().(item.Smeltable); ok {
|
||||
dropItem(tx, food.SmeltInfo().Product, pos.Vec3Middle())
|
||||
}
|
||||
c.Items[i].Item = item.Stack{}
|
||||
}
|
||||
if updated {
|
||||
tx.SetBlock(pos, c, nil)
|
||||
}
|
||||
}
|
||||
|
||||
// NeighbourUpdateTick ...
|
||||
func (c Campfire) NeighbourUpdateTick(pos, _ cube.Pos, tx *world.Tx) {
|
||||
if _, ok := tx.Liquid(pos); ok {
|
||||
var updated bool
|
||||
for i, it := range c.Items {
|
||||
if !it.Item.Empty() {
|
||||
dropItem(tx, it.Item, pos.Vec3Centre())
|
||||
c.Items[i].Item, updated = item.Stack{}, true
|
||||
}
|
||||
}
|
||||
if !c.Extinguished {
|
||||
c.extinguish(pos, tx)
|
||||
} else if updated {
|
||||
tx.SetBlock(pos, c, nil)
|
||||
}
|
||||
return
|
||||
}
|
||||
if liquid, ok := tx.Liquid(pos.Side(cube.FaceUp)); ok && liquid.LiquidType() == "water" && !c.Extinguished {
|
||||
c.extinguish(pos, tx)
|
||||
}
|
||||
}
|
||||
|
||||
// EntityInside ...
|
||||
func (c Campfire) EntityInside(pos cube.Pos, tx *world.Tx, e world.Entity) {
|
||||
if flammable, ok := e.(flammableEntity); ok {
|
||||
if flammable.OnFireDuration() > 0 && c.Extinguished {
|
||||
c.Extinguished = false
|
||||
tx.PlaySound(pos.Vec3(), sound.Ignite{})
|
||||
tx.SetBlock(pos, c, nil)
|
||||
}
|
||||
if !c.Extinguished {
|
||||
if l, ok := e.(livingEntity); ok {
|
||||
l.Hurt(c.Type.Damage(), FireDamageSource{})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// EncodeNBT ...
|
||||
func (c Campfire) EncodeNBT() map[string]any {
|
||||
m := map[string]any{"id": "Campfire"}
|
||||
for i, v := range c.Items {
|
||||
id := strconv.Itoa(i + 1)
|
||||
if !v.Item.Empty() {
|
||||
m["Item"+id] = nbtconv.WriteItem(v.Item, true)
|
||||
m["ItemTime"+id] = uint8(v.Time.Milliseconds() / 50)
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// DecodeNBT ...
|
||||
func (c Campfire) DecodeNBT(data map[string]any) any {
|
||||
for i := 0; i < 4; i++ {
|
||||
id := strconv.Itoa(i + 1)
|
||||
c.Items[i] = CampfireItem{
|
||||
Item: nbtconv.MapItem(data, "Item"+id),
|
||||
Time: time.Duration(nbtconv.Int16(data, "ItemTime"+id)) * time.Millisecond * 50,
|
||||
}
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// EncodeItem ...
|
||||
func (c Campfire) EncodeItem() (name string, meta int16) {
|
||||
switch c.Type {
|
||||
case NormalFire():
|
||||
return "minecraft:campfire", 0
|
||||
case SoulFire():
|
||||
return "minecraft:soul_campfire", 0
|
||||
}
|
||||
panic("invalid fire type")
|
||||
}
|
||||
|
||||
// EncodeBlock ...
|
||||
func (c Campfire) EncodeBlock() (name string, properties map[string]any) {
|
||||
switch c.Type {
|
||||
case NormalFire():
|
||||
name = "minecraft:campfire"
|
||||
case SoulFire():
|
||||
name = "minecraft:soul_campfire"
|
||||
}
|
||||
return name, map[string]any{
|
||||
"minecraft:cardinal_direction": c.Facing.String(),
|
||||
"extinguished": c.Extinguished,
|
||||
}
|
||||
}
|
||||
|
||||
// allCampfires ...
|
||||
func allCampfires() (campfires []world.Block) {
|
||||
for _, d := range cube.Directions() {
|
||||
for _, f := range FireTypes() {
|
||||
campfires = append(campfires, Campfire{Facing: d, Type: f, Extinguished: true})
|
||||
campfires = append(campfires, Campfire{Facing: d, Type: f})
|
||||
}
|
||||
}
|
||||
return campfires
|
||||
}
|
||||
Reference in New Issue
Block a user