252 lines
7.6 KiB
Go
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
|
||
|
|
}
|