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 }