206 lines
5.6 KiB
Go
206 lines
5.6 KiB
Go
|
|
package block
|
||
|
|
|
||
|
|
import (
|
||
|
|
"fmt"
|
||
|
|
"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/particle"
|
||
|
|
"github.com/df-mc/dragonfly/server/world/sound"
|
||
|
|
"github.com/go-gl/mathgl/mgl64"
|
||
|
|
)
|
||
|
|
|
||
|
|
// PotDecoration represents an item that can be used as a decoration on a pot.
|
||
|
|
type PotDecoration interface {
|
||
|
|
world.Item
|
||
|
|
PotDecoration() bool
|
||
|
|
}
|
||
|
|
|
||
|
|
// DecoratedPot is a decoration block that can be crafted from up to four pottery sherds, and bricks on the sides where
|
||
|
|
// no pattern should be displayed.
|
||
|
|
type DecoratedPot struct {
|
||
|
|
transparent
|
||
|
|
sourceWaterDisplacer
|
||
|
|
|
||
|
|
// Item is the item being stored in the decorated pot.
|
||
|
|
Item item.Stack
|
||
|
|
// Facing is the direction the pot is facing. The first decoration will be facing opposite of this direction.
|
||
|
|
Facing cube.Direction
|
||
|
|
// Decorations are the four decorations displayed on the sides of the pot. If a decoration is a brick or nil,
|
||
|
|
// the side will appear to be empty.
|
||
|
|
Decorations [4]PotDecoration
|
||
|
|
}
|
||
|
|
|
||
|
|
// SideClosed ...
|
||
|
|
func (p DecoratedPot) SideClosed(cube.Pos, cube.Pos, *world.Tx) bool {
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
|
||
|
|
// ProjectileHit ...
|
||
|
|
func (p DecoratedPot) ProjectileHit(pos cube.Pos, tx *world.Tx, _ world.Entity, _ cube.Face) {
|
||
|
|
for _, d := range p.Decorations {
|
||
|
|
if d == nil {
|
||
|
|
dropItem(tx, item.NewStack(item.Brick{}, 1), pos.Vec3Centre())
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
dropItem(tx, item.NewStack(d, 1), pos.Vec3Centre())
|
||
|
|
}
|
||
|
|
breakBlockNoDrops(p, pos, tx)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Pick ...
|
||
|
|
func (p DecoratedPot) Pick() item.Stack {
|
||
|
|
return item.NewStack(DecoratedPot{Decorations: p.Decorations}, 1)
|
||
|
|
}
|
||
|
|
|
||
|
|
// ExtractItem ...
|
||
|
|
func (p DecoratedPot) ExtractItem(h Hopper, pos cube.Pos, tx *world.Tx) bool {
|
||
|
|
if p.Item.Empty() {
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
if _, err := h.inventory.AddItem(p.Item.Grow(-p.Item.Count() + 1)); err != nil {
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
p.Item = p.Item.Grow(-1)
|
||
|
|
tx.SetBlock(pos, p, nil)
|
||
|
|
return true
|
||
|
|
}
|
||
|
|
|
||
|
|
// InsertItem ...
|
||
|
|
func (p DecoratedPot) InsertItem(h Hopper, pos cube.Pos, tx *world.Tx) bool {
|
||
|
|
for sourceSlot, sourceStack := range h.inventory.Slots() {
|
||
|
|
if !sourceStack.Empty() && sourceStack.Comparable(p.Item) {
|
||
|
|
if p.Item.Empty() {
|
||
|
|
p.Item = sourceStack.Grow(-sourceStack.Count() + 1)
|
||
|
|
} else {
|
||
|
|
p.Item = p.Item.Grow(1)
|
||
|
|
}
|
||
|
|
_ = h.inventory.SetItem(sourceSlot, sourceStack.Grow(-1))
|
||
|
|
tx.SetBlock(pos, p, nil)
|
||
|
|
return true
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
|
||
|
|
// wobble ...
|
||
|
|
func (p DecoratedPot) wobble(pos cube.Pos, tx *world.Tx, success bool) {
|
||
|
|
for _, v := range tx.Viewers(pos.Vec3Centre()) {
|
||
|
|
v.ViewBlockAction(pos, DecoratedPotWobbleAction{DecoratedPot: p, Success: success})
|
||
|
|
}
|
||
|
|
|
||
|
|
if success {
|
||
|
|
tx.AddParticle(pos.Vec3Middle().Add(mgl64.Vec3{0, 1.2}), particle.DustPlume{})
|
||
|
|
tx.PlaySound(pos.Vec3Centre(), sound.DecoratedPotInserted{Progress: float64(p.Item.Count()) / float64(p.Item.MaxCount())})
|
||
|
|
} else {
|
||
|
|
tx.PlaySound(pos.Vec3Centre(), sound.DecoratedPotInsertFailed{})
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Activate ...
|
||
|
|
func (p DecoratedPot) Activate(pos cube.Pos, _ cube.Face, tx *world.Tx, u item.User, ctx *item.UseContext) bool {
|
||
|
|
held, _ := u.HeldItems()
|
||
|
|
if held.Empty() || !p.Item.Comparable(held) || p.Item.Count() == p.Item.MaxCount() {
|
||
|
|
p.wobble(pos, tx, false)
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
|
||
|
|
if p.Item.Empty() {
|
||
|
|
p.Item = held.Grow(-held.Count() + 1)
|
||
|
|
} else {
|
||
|
|
p.Item = p.Item.Grow(1)
|
||
|
|
}
|
||
|
|
tx.SetBlock(pos, p, nil)
|
||
|
|
p.wobble(pos, tx, true)
|
||
|
|
ctx.SubtractFromCount(1)
|
||
|
|
return true
|
||
|
|
}
|
||
|
|
|
||
|
|
// BreakInfo ...
|
||
|
|
func (p DecoratedPot) BreakInfo() BreakInfo {
|
||
|
|
return newBreakInfo(0, alwaysHarvestable, nothingEffective, oneOf(DecoratedPot{Decorations: p.Decorations})).withBreakHandler(func(pos cube.Pos, tx *world.Tx, u item.User) {
|
||
|
|
if !p.Item.Empty() {
|
||
|
|
dropItem(tx, p.Item, pos.Vec3Centre())
|
||
|
|
}
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
// EncodeItem ...
|
||
|
|
func (p DecoratedPot) EncodeItem() (name string, meta int16) {
|
||
|
|
return "minecraft:decorated_pot", 0
|
||
|
|
}
|
||
|
|
|
||
|
|
// EncodeBlock ...
|
||
|
|
func (p DecoratedPot) EncodeBlock() (name string, properties map[string]any) {
|
||
|
|
return "minecraft:decorated_pot", map[string]any{"direction": int32(horizontalDirection(p.Facing))}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Model ...
|
||
|
|
func (p DecoratedPot) Model() world.BlockModel {
|
||
|
|
return model.DecoratedPot{}
|
||
|
|
}
|
||
|
|
|
||
|
|
// UseOnBlock ...
|
||
|
|
func (p DecoratedPot) 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, p)
|
||
|
|
if !used {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
p.Facing = user.Rotation().Direction().Opposite()
|
||
|
|
|
||
|
|
place(tx, pos, p, user, ctx)
|
||
|
|
return placed(ctx)
|
||
|
|
}
|
||
|
|
|
||
|
|
// EncodeNBT ...
|
||
|
|
func (p DecoratedPot) EncodeNBT() map[string]any {
|
||
|
|
var sherds []any
|
||
|
|
for _, decoration := range p.Decorations {
|
||
|
|
if decoration == nil {
|
||
|
|
sherds = append(sherds, "minecraft:brick")
|
||
|
|
} else {
|
||
|
|
name, _ := decoration.EncodeItem()
|
||
|
|
sherds = append(sherds, name)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
m := map[string]any{
|
||
|
|
"id": "DecoratedPot",
|
||
|
|
"sherds": sherds,
|
||
|
|
}
|
||
|
|
if !p.Item.Empty() {
|
||
|
|
m["item"] = nbtconv.WriteItem(p.Item, true)
|
||
|
|
}
|
||
|
|
return m
|
||
|
|
}
|
||
|
|
|
||
|
|
// DecodeNBT ...
|
||
|
|
func (p DecoratedPot) DecodeNBT(data map[string]any) any {
|
||
|
|
p.Item = nbtconv.MapItem(data, "item")
|
||
|
|
p.Decorations = [4]PotDecoration{}
|
||
|
|
if sherds := nbtconv.Slice(data, "sherds"); sherds != nil {
|
||
|
|
for i, name := range sherds {
|
||
|
|
it, ok := world.ItemByName(name.(string), 0)
|
||
|
|
if !ok {
|
||
|
|
panic(fmt.Errorf("unknown item %s", name))
|
||
|
|
}
|
||
|
|
decoration, ok := it.(PotDecoration)
|
||
|
|
if !ok {
|
||
|
|
panic(fmt.Errorf("item %s is not a pot decoration", name))
|
||
|
|
}
|
||
|
|
p.Decorations[i] = decoration
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return p
|
||
|
|
}
|
||
|
|
|
||
|
|
// allDecoratedPots ...
|
||
|
|
func allDecoratedPots() (pots []world.Block) {
|
||
|
|
for _, f := range cube.Directions() {
|
||
|
|
pots = append(pots, DecoratedPot{Facing: f})
|
||
|
|
}
|
||
|
|
return
|
||
|
|
}
|