github.com/df-mc/dragonfly@v0.9.13/server/block/sign.go (about) 1 package block 2 3 import ( 4 "github.com/df-mc/dragonfly/server/block/cube" 5 "github.com/df-mc/dragonfly/server/internal/nbtconv" 6 "github.com/df-mc/dragonfly/server/item" 7 "github.com/df-mc/dragonfly/server/world" 8 "github.com/df-mc/dragonfly/server/world/particle" 9 "github.com/go-gl/mathgl/mgl64" 10 "image/color" 11 "strings" 12 "time" 13 ) 14 15 // Sign is a non-solid block that can display text on the front and back of the block. 16 type Sign struct { 17 transparent 18 empty 19 bass 20 sourceWaterDisplacer 21 22 // Wood is the type of wood of the sign. This field must have one of the values found in the material 23 // package. 24 Wood WoodType 25 // Attach is the attachment of the Sign. It is either of the type WallAttachment or StandingAttachment. 26 Attach Attachment 27 // Waxed specifies if the Sign has been waxed by a player. If set to true, the Sign can no longer be edited by 28 // anyone and must be destroyed if the text needs to be changed. 29 Waxed bool 30 // Front is the text of the front side of the sign. Anyone can edit this unless the sign is Waxed. 31 Front SignText 32 // Back is the text of the back side of the sign. Anyone can edit this unless the sign is Waxed. 33 Back SignText 34 } 35 36 // SignText represents the data for a single side of a sign. The sign can be edited on the front and back side. 37 type SignText struct { 38 // Text is the text displayed on this side of the sign. The text is automatically wrapped if it does not fit on a line. 39 Text string 40 // BaseColour is the base colour of the text on this side of the sign, changed when using a dye on the sign. The default 41 // colour is black. 42 BaseColour color.RGBA 43 // Glowing specifies if the Sign has glowing text on the current side. If set to true, the text will be visible even 44 // in the dark, and it will have an outline to improve visibility. 45 Glowing bool 46 // Owner holds the XUID of the player that most recently edited this side of the sign. 47 Owner string 48 } 49 50 // SideClosed ... 51 func (s Sign) SideClosed(cube.Pos, cube.Pos, *world.World) bool { 52 return false 53 } 54 55 // MaxCount ... 56 func (s Sign) MaxCount() int { 57 return 16 58 } 59 60 // FlammabilityInfo ... 61 func (s Sign) FlammabilityInfo() FlammabilityInfo { 62 return newFlammabilityInfo(0, 0, true) 63 } 64 65 // FuelInfo ... 66 func (Sign) FuelInfo() item.FuelInfo { 67 return newFuelInfo(time.Second * 10) 68 } 69 70 // EncodeItem ... 71 func (s Sign) EncodeItem() (name string, meta int16) { 72 return "minecraft:" + s.Wood.String() + "_sign", 0 73 } 74 75 // BreakInfo ... 76 func (s Sign) BreakInfo() BreakInfo { 77 return newBreakInfo(1, alwaysHarvestable, axeEffective, oneOf(Sign{Wood: s.Wood})) 78 } 79 80 // Dye dyes the Sign, changing its base colour to that of the colour passed. 81 func (s Sign) Dye(pos cube.Pos, userPos mgl64.Vec3, c item.Colour) (world.Block, bool) { 82 if s.EditingFrontSide(pos, userPos) { 83 if s.Front.BaseColour == c.RGBA() { 84 return s, false 85 } 86 s.Front.BaseColour = c.RGBA() 87 } else { 88 if s.Back.BaseColour == c.RGBA() { 89 return s, false 90 } 91 s.Back.BaseColour = c.RGBA() 92 } 93 return s, true 94 } 95 96 // Ink inks the sign either glowing or non-glowing. 97 func (s Sign) Ink(pos cube.Pos, userPos mgl64.Vec3, glowing bool) (world.Block, bool) { 98 if s.EditingFrontSide(pos, userPos) { 99 if s.Front.Glowing == glowing { 100 return s, false 101 } 102 s.Front.Glowing = glowing 103 } else { 104 if s.Back.Glowing == glowing { 105 return s, false 106 } 107 s.Back.Glowing = glowing 108 } 109 return s, true 110 } 111 112 // Wax waxes a sign to prevent it from further editing. 113 func (s Sign) Wax(cube.Pos, mgl64.Vec3) (world.Block, bool) { 114 if s.Waxed { 115 return s, false 116 } 117 s.Waxed = true 118 return s, true 119 } 120 121 // Activate ... 122 func (s Sign) Activate(pos cube.Pos, _ cube.Face, _ *world.World, user item.User, _ *item.UseContext) bool { 123 if editor, ok := user.(SignEditor); ok && !s.Waxed { 124 editor.OpenSign(pos, s.EditingFrontSide(pos, user.Position())) 125 } 126 return true 127 } 128 129 // EditingFrontSide returns if the user is editing the front side of the sign based on their position relative to the 130 // position and direction of the sign. 131 func (s Sign) EditingFrontSide(pos cube.Pos, userPos mgl64.Vec3) bool { 132 return userPos.Sub(pos.Vec3Centre()).Dot(s.Attach.Rotation().Vec3()) > 0 133 } 134 135 // SignEditor represents something that can edit a sign, typically players. 136 type SignEditor interface { 137 OpenSign(pos cube.Pos, frontSide bool) 138 } 139 140 // UseOnBlock ... 141 func (s Sign) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world.World, user item.User, ctx *item.UseContext) (used bool) { 142 pos, face, used = firstReplaceable(w, pos, face, s) 143 if !used || face == cube.FaceDown { 144 return false 145 } 146 147 if face == cube.FaceUp { 148 s.Attach = StandingAttachment(user.Rotation().Orientation().Opposite()) 149 } else { 150 s.Attach = WallAttachment(face.Direction()) 151 } 152 place(w, pos, s, user, ctx) 153 if editor, ok := user.(SignEditor); ok { 154 editor.OpenSign(pos, true) 155 } 156 return placed(ctx) 157 } 158 159 // NeighbourUpdateTick ... 160 func (s Sign) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { 161 if s.Attach.hanging { 162 if _, ok := w.Block(pos.Side(s.Attach.facing.Opposite().Face())).(Air); ok { 163 w.SetBlock(pos, nil, nil) 164 w.AddParticle(pos.Vec3Centre(), particle.BlockBreak{Block: s}) 165 dropItem(w, item.NewStack(Sign{Wood: s.Wood}, 1), pos.Vec3Centre()) 166 } 167 return 168 } 169 if _, ok := w.Block(pos.Side(cube.FaceDown)).(Air); ok { 170 w.SetBlock(pos, nil, nil) 171 w.AddParticle(pos.Vec3Centre(), particle.BlockBreak{Block: s}) 172 dropItem(w, item.NewStack(Sign{Wood: s.Wood}, 1), pos.Vec3Centre()) 173 } 174 } 175 176 // EncodeBlock ... 177 func (s Sign) EncodeBlock() (name string, properties map[string]any) { 178 woodType := strings.Replace(s.Wood.String(), "_", "", 1) + "_" 179 if woodType == "oak_" { 180 woodType = "" 181 } 182 if s.Attach.hanging { 183 return "minecraft:" + woodType + "wall_sign", map[string]any{"facing_direction": int32(s.Attach.facing + 2)} 184 } 185 return "minecraft:" + woodType + "standing_sign", map[string]any{"ground_sign_direction": int32(s.Attach.o)} 186 } 187 188 // DecodeNBT ... 189 func (s Sign) DecodeNBT(data map[string]any) any { 190 if nbtconv.String(data, "Text") != "" { 191 // The NBT format changed in 1.19.80 to have separate data for each side of the sign. The old format must still 192 // be supported for backwards compatibility. 193 s.Front.Text = nbtconv.String(data, "Text") 194 s.Front.BaseColour = nbtconv.RGBAFromInt32(nbtconv.Int32(data, "SignTextColor")) 195 s.Front.Glowing = nbtconv.Bool(data, "IgnoreLighting") && nbtconv.Bool(data, "TextIgnoreLegacyBugResolved") 196 return s 197 } 198 199 front, ok := data["FrontText"].(map[string]any) 200 if ok { 201 s.Front.BaseColour = nbtconv.RGBAFromInt32(nbtconv.Int32(front, "Color")) 202 s.Front.Glowing = nbtconv.Bool(front, "GlowingText") 203 s.Front.Text = nbtconv.String(front, "Text") 204 s.Front.Owner = nbtconv.String(front, "Owner") 205 } 206 207 back, ok := data["BackText"].(map[string]any) 208 if ok { 209 s.Back.BaseColour = nbtconv.RGBAFromInt32(nbtconv.Int32(back, "Color")) 210 s.Back.Glowing = nbtconv.Bool(back, "GlowingText") 211 s.Back.Text = nbtconv.String(back, "Text") 212 s.Back.Owner = nbtconv.String(back, "Owner") 213 } 214 215 return s 216 } 217 218 // EncodeNBT ... 219 func (s Sign) EncodeNBT() map[string]any { 220 m := map[string]any{ 221 "id": "Sign", 222 "IsWaxed": boolByte(s.Waxed), 223 "FrontText": map[string]any{ 224 "SignTextColor": nbtconv.Int32FromRGBA(s.Front.BaseColour), 225 "IgnoreLighting": boolByte(s.Front.Glowing), 226 "Text": s.Front.Text, 227 "TextOwner": s.Front.Owner, 228 }, 229 "BackText": map[string]any{ 230 "SignTextColor": nbtconv.Int32FromRGBA(s.Back.BaseColour), 231 "IgnoreLighting": boolByte(s.Back.Glowing), 232 "Text": s.Back.Text, 233 "TextOwner": s.Back.Owner, 234 }, 235 } 236 return m 237 } 238 239 // allSigns ... 240 func allSigns() (signs []world.Block) { 241 for _, w := range WoodTypes() { 242 for _, d := range cube.Directions() { 243 signs = append(signs, Sign{Wood: w, Attach: WallAttachment(d)}) 244 } 245 for o := cube.Orientation(0); o <= 15; o++ { 246 signs = append(signs, Sign{Wood: w, Attach: StandingAttachment(o)}) 247 } 248 } 249 return 250 }