Files

138 lines
3.8 KiB
Go
Raw Permalink Normal View History

2026-07-09 08:33:57 +08:00
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/go-gl/mathgl/mgl64"
"time"
)
// Banner is a tall decorative block that can be customised.
type Banner struct {
empty
transparent
// Colour is the colour of the banner.
Colour item.Colour
// Attach is the attachment of the Banner. It is either of the type WallAttachment or StandingAttachment.
Attach Attachment
// Patterns represents the patterns the Banner should show when rendering.
Patterns []BannerPatternLayer
// Illager returns true if the banner is an illager banner.
Illager bool
}
// Pick ...
func (b Banner) Pick() item.Stack {
return item.NewStack(Banner{Colour: b.Colour, Patterns: b.Patterns, Illager: b.Illager}, 1)
}
// MaxCount ...
func (Banner) MaxCount() int {
return 16
}
// BreakInfo ...
func (b Banner) BreakInfo() BreakInfo {
return newBreakInfo(1, alwaysHarvestable, axeEffective, oneOf(b))
}
// FuelInfo ...
func (Banner) FuelInfo() item.FuelInfo {
return newFuelInfo(time.Second * 15)
}
// UseOnBlock ...
func (b Banner) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, tx *world.Tx, user item.User, ctx *item.UseContext) (used bool) {
pos, face, used = firstReplaceable(tx, pos, face, b)
if !used || face == cube.FaceDown {
return false
}
if face == cube.FaceUp {
b.Attach = StandingAttachment(user.Rotation().Orientation().Opposite())
place(tx, pos, b, user, ctx)
return
}
b.Attach = WallAttachment(face.Direction())
place(tx, pos, b, user, ctx)
return placed(ctx)
}
// NeighbourUpdateTick ...
func (b Banner) NeighbourUpdateTick(pos, _ cube.Pos, tx *world.Tx) {
if b.Attach.hanging {
if _, ok := tx.Block(pos.Side(b.Attach.facing.Opposite().Face())).(Air); ok {
breakBlock(b, pos, tx)
}
} else if _, ok := tx.Block(pos.Side(cube.FaceDown)).(Air); ok {
breakBlock(b, pos, tx)
}
}
// EncodeItem ...
func (b Banner) EncodeItem() (name string, meta int16) {
return "minecraft:banner", invertColour(b.Colour)
}
// EncodeBlock ...
func (b Banner) EncodeBlock() (name string, properties map[string]any) {
if b.Attach.hanging {
return "minecraft:wall_banner", map[string]any{"facing_direction": int32(b.Attach.facing + 2)}
}
return "minecraft:standing_banner", map[string]any{"ground_sign_direction": int32(b.Attach.o)}
}
// EncodeNBT ...
func (b Banner) EncodeNBT() map[string]any {
patterns := make([]any, 0, len(b.Patterns))
for _, p := range b.Patterns {
patterns = append(patterns, p.EncodeNBT())
}
return map[string]any{
"id": "Banner",
"Patterns": patterns,
"Type": int32(boolByte(b.Illager)),
"Base": int32(invertColour(b.Colour)),
}
}
// DecodeNBT ...
func (b Banner) DecodeNBT(m map[string]any) any {
if _, ok := m["Base"]; ok {
// Banner items do not have the Base NBT.
b.Colour = invertColourID(int16(nbtconv.Int32(m, "Base")))
}
b.Illager = nbtconv.Int32(m, "Type") == 1
if patterns := nbtconv.Slice(m, "Patterns"); patterns != nil {
b.Patterns = make([]BannerPatternLayer, len(patterns))
for i, p := range b.Patterns {
b.Patterns[i] = p.DecodeNBT(patterns[i].(map[string]any)).(BannerPatternLayer)
}
}
return b
}
// invertColour converts the item.Colour passed and returns the colour ID inverted.
func invertColour(c item.Colour) int16 {
return ^int16(c.Uint8()) & 0xf
}
// invertColourID converts the int16 passed the returns the item.Colour inverted.
func invertColourID(id int16) item.Colour {
return item.Colours()[uint8(^id&0xf)]
}
// allBanners returns all possible banners.
func allBanners() (banners []world.Block) {
for _, d := range cube.Directions() {
banners = append(banners, Banner{Attach: WallAttachment(d)})
}
for o := cube.Orientation(0); o <= 15; o++ {
banners = append(banners, Banner{Attach: StandingAttachment(o)})
}
return
}