Files
TarnaWijaya 26ed99fda6
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
up3
2026-07-09 08:33:57 +08:00

252 lines
7.6 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"
"image/color"
"time"
)
// Sign is a non-solid block that can display text on the front and back of the block.
type Sign struct {
transparent
empty
bass
sourceWaterDisplacer
// Wood is the type of wood of the sign. This field must have one of the values found in the material
// package.
Wood WoodType
// Attach is the attachment of the Sign. It is either of the type WallAttachment or StandingAttachment.
Attach Attachment
// Waxed specifies if the Sign has been waxed by a player. If set to true, the Sign can no longer be edited by
// anyone and must be destroyed if the text needs to be changed.
Waxed bool
// Front is the text of the front side of the sign. Anyone can edit this unless the sign is Waxed.
Front SignText
// Back is the text of the back side of the sign. Anyone can edit this unless the sign is Waxed.
Back SignText
}
// SignText represents the data for a single side of a sign. The sign can be edited on the front and back side.
type SignText struct {
// Text is the text displayed on this side of the sign. The text is automatically wrapped if it does not fit on a line.
Text string
// BaseColour is the base colour of the text on this side of the sign, changed when using a dye on the sign. The default
// colour is black.
BaseColour color.RGBA
// Glowing specifies if the Sign has glowing text on the current side. If set to true, the text will be visible even
// in the dark, and it will have an outline to improve visibility.
Glowing bool
// Owner holds the XUID of the player that most recently edited this side of the sign.
Owner string
}
// SideClosed ...
func (s Sign) SideClosed(cube.Pos, cube.Pos, *world.Tx) bool {
return false
}
// MaxCount ...
func (s Sign) MaxCount() int {
return 16
}
// FlammabilityInfo ...
func (s Sign) FlammabilityInfo() FlammabilityInfo {
return newFlammabilityInfo(0, 0, true)
}
// FuelInfo ...
func (s Sign) FuelInfo() item.FuelInfo {
if !s.Wood.Flammable() {
return item.FuelInfo{}
}
return newFuelInfo(time.Second * 10)
}
// EncodeItem ...
func (s Sign) EncodeItem() (name string, meta int16) {
return "minecraft:" + s.Wood.String() + "_sign", 0
}
// BreakInfo ...
func (s Sign) BreakInfo() BreakInfo {
return newBreakInfo(1, alwaysHarvestable, axeEffective, oneOf(Sign{Wood: s.Wood}))
}
// Dye dyes the Sign, changing its base colour to that of the colour passed.
func (s Sign) Dye(pos cube.Pos, userPos mgl64.Vec3, c item.Colour) (world.Block, bool) {
if s.EditingFrontSide(pos, userPos) {
if s.Front.BaseColour == c.SignRGBA() {
return s, false
}
s.Front.BaseColour = c.SignRGBA()
} else {
if s.Back.BaseColour == c.SignRGBA() {
return s, false
}
s.Back.BaseColour = c.SignRGBA()
}
return s, true
}
// Ink inks the sign either glowing or non-glowing.
func (s Sign) Ink(pos cube.Pos, userPos mgl64.Vec3, glowing bool) (world.Block, bool) {
if s.EditingFrontSide(pos, userPos) {
if s.Front.Glowing == glowing {
return s, false
}
s.Front.Glowing = glowing
} else {
if s.Back.Glowing == glowing {
return s, false
}
s.Back.Glowing = glowing
}
return s, true
}
// Wax waxes a sign to prevent it from further editing.
func (s Sign) Wax(cube.Pos, mgl64.Vec3) (world.Block, bool) {
if s.Waxed {
return s, false
}
s.Waxed = true
return s, true
}
// Activate ...
func (s Sign) Activate(pos cube.Pos, _ cube.Face, tx *world.Tx, u item.User, _ *item.UseContext) bool {
if editor, ok := u.(SignEditor); ok && !s.Waxed {
editor.OpenSign(pos, s.EditingFrontSide(pos, u.Position()))
} else if s.Waxed {
tx.PlaySound(pos.Vec3(), sound.WaxedSignFailedInteraction{})
}
return true
}
// EditingFrontSide returns if the user is editing the front side of the sign based on their position relative to the
// position and direction of the sign.
func (s Sign) EditingFrontSide(pos cube.Pos, userPos mgl64.Vec3) bool {
return userPos.Sub(pos.Vec3Centre()).Dot(s.Attach.Rotation().Vec3()) > 0
}
// SignEditor represents something that can edit a sign, typically players.
type SignEditor interface {
OpenSign(pos cube.Pos, frontSide bool)
}
// UseOnBlock ...
func (s Sign) 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, s)
if !used || face == cube.FaceDown {
return false
}
if face == cube.FaceUp {
s.Attach = StandingAttachment(user.Rotation().Orientation().Opposite())
} else {
s.Attach = WallAttachment(face.Direction())
}
place(tx, pos, s, user, ctx)
if editor, ok := user.(SignEditor); ok {
editor.OpenSign(pos, true)
}
return placed(ctx)
}
// NeighbourUpdateTick ...
func (s Sign) NeighbourUpdateTick(pos, _ cube.Pos, tx *world.Tx) {
if s.Attach.hanging {
if _, ok := tx.Block(pos.Side(s.Attach.facing.Opposite().Face())).(Air); ok {
breakBlock(s, pos, tx)
}
} else if _, ok := tx.Block(pos.Side(cube.FaceDown)).(Air); ok {
breakBlock(s, pos, tx)
}
}
// EncodeBlock ...
func (s Sign) EncodeBlock() (name string, properties map[string]any) {
woodType := s.Wood.String() + "_"
switch s.Wood {
case OakWood():
woodType = ""
case DarkOakWood():
woodType = "darkoak_"
}
if s.Attach.hanging {
return "minecraft:" + woodType + "wall_sign", map[string]any{"facing_direction": int32(s.Attach.facing + 2)}
}
return "minecraft:" + woodType + "standing_sign", map[string]any{"ground_sign_direction": int32(s.Attach.o)}
}
// DecodeNBT ...
func (s Sign) DecodeNBT(data map[string]any) any {
if nbtconv.String(data, "Text") != "" {
// The NBT format changed in 1.19.80 to have separate data for each side of the sign. The old format must still
// be supported for backwards compatibility.
s.Front.Text = nbtconv.String(data, "Text")
s.Front.BaseColour = nbtconv.RGBAFromInt32(nbtconv.Int32(data, "SignTextColor"))
s.Front.Glowing = nbtconv.Bool(data, "IgnoreLighting") && nbtconv.Bool(data, "TextIgnoreLegacyBugResolved")
return s
}
front, ok := data["FrontText"].(map[string]any)
if ok {
s.Front.BaseColour = nbtconv.RGBAFromInt32(nbtconv.Int32(front, "Color"))
s.Front.Glowing = nbtconv.Bool(front, "GlowingText")
s.Front.Text = nbtconv.String(front, "Text")
s.Front.Owner = nbtconv.String(front, "Owner")
}
back, ok := data["BackText"].(map[string]any)
if ok {
s.Back.BaseColour = nbtconv.RGBAFromInt32(nbtconv.Int32(back, "Color"))
s.Back.Glowing = nbtconv.Bool(back, "GlowingText")
s.Back.Text = nbtconv.String(back, "Text")
s.Back.Owner = nbtconv.String(back, "Owner")
}
return s
}
// EncodeNBT ...
func (s Sign) EncodeNBT() map[string]any {
m := map[string]any{
"id": "Sign",
"IsWaxed": boolByte(s.Waxed),
"FrontText": map[string]any{
"SignTextColor": nbtconv.Int32FromRGBA(s.Front.BaseColour),
"IgnoreLighting": boolByte(s.Front.Glowing),
"Text": s.Front.Text,
"TextOwner": s.Front.Owner,
},
"BackText": map[string]any{
"SignTextColor": nbtconv.Int32FromRGBA(s.Back.BaseColour),
"IgnoreLighting": boolByte(s.Back.Glowing),
"Text": s.Back.Text,
"TextOwner": s.Back.Owner,
},
}
return m
}
// allSigns ...
func allSigns() (signs []world.Block) {
for _, w := range WoodTypes() {
for _, d := range cube.Directions() {
signs = append(signs, Sign{Wood: w, Attach: WallAttachment(d)})
}
for o := cube.Orientation(0); o <= 15; o++ {
signs = append(signs, Sign{Wood: w, Attach: StandingAttachment(o)})
}
}
return
}