github.com/df-mc/dragonfly@v0.9.13/server/block/item_frame.go (about) 1 package block 2 3 import ( 4 "github.com/df-mc/dragonfly/server/block/cube" 5 "github.com/df-mc/dragonfly/server/block/model" 6 "github.com/df-mc/dragonfly/server/internal/nbtconv" 7 "github.com/df-mc/dragonfly/server/item" 8 "github.com/df-mc/dragonfly/server/world" 9 "github.com/df-mc/dragonfly/server/world/particle" 10 "github.com/df-mc/dragonfly/server/world/sound" 11 "github.com/go-gl/mathgl/mgl64" 12 "math/rand" 13 ) 14 15 // ItemFrame is a block entity that displays the item or block that is inside it. 16 type ItemFrame struct { 17 empty 18 transparent 19 sourceWaterDisplacer 20 21 // Facing is the direction from the frame to the block. 22 Facing cube.Face 23 // Item is the item that is displayed inside the frame. 24 Item item.Stack 25 // Rotations is the number of rotations for the item in the frame. Each rotation is 45 degrees, with the exception 26 // being maps having 90 degree rotations. 27 Rotations int 28 // DropChance is the chance of the item dropping when the frame is broken. In vanilla, this is always 1.0. 29 DropChance float64 30 // Glowing makes the frame the glowing variant. 31 Glowing bool 32 } 33 34 // Activate ... 35 func (i ItemFrame) Activate(pos cube.Pos, _ cube.Face, w *world.World, u item.User, ctx *item.UseContext) bool { 36 if !i.Item.Empty() { 37 // TODO: Item frames with maps can only be rotated four times. 38 i.Rotations = (i.Rotations + 1) % 8 39 w.PlaySound(pos.Vec3Centre(), sound.ItemFrameRotate{}) 40 } else if held, _ := u.HeldItems(); !held.Empty() { 41 i.Item = held.Grow(-held.Count() + 1) 42 // TODO: When maps are implemented, check the item is a map, and if so, display the large version of the frame. 43 ctx.SubtractFromCount(1) 44 w.PlaySound(pos.Vec3Centre(), sound.ItemFrameAdd{}) 45 } else { 46 return true 47 } 48 49 w.SetBlock(pos, i, nil) 50 return true 51 } 52 53 // Punch ... 54 func (i ItemFrame) Punch(pos cube.Pos, _ cube.Face, w *world.World, u item.User) { 55 if i.Item.Empty() { 56 return 57 } 58 59 if g, ok := u.(interface { 60 GameMode() world.GameMode 61 }); ok { 62 if rand.Float64() <= i.DropChance && !g.GameMode().CreativeInventory() { 63 dropItem(w, i.Item, pos.Vec3Centre()) 64 } 65 } 66 i.Item, i.Rotations = item.Stack{}, 0 67 w.PlaySound(pos.Vec3Centre(), sound.ItemFrameRemove{}) 68 w.SetBlock(pos, i, nil) 69 } 70 71 // UseOnBlock ... 72 func (i ItemFrame) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world.World, user item.User, ctx *item.UseContext) bool { 73 pos, face, used := firstReplaceable(w, pos, face, i) 74 if !used { 75 return false 76 } 77 if _, ok := w.Block(pos.Side(face.Opposite())).Model().(model.Empty); ok { 78 // TODO: Allow exceptions for pressure plates. 79 return false 80 } 81 i.Facing = face.Opposite() 82 i.DropChance = 1.0 83 84 place(w, pos, i, user, ctx) 85 return placed(ctx) 86 } 87 88 // BreakInfo ... 89 func (i ItemFrame) BreakInfo() BreakInfo { 90 return newBreakInfo(0.25, alwaysHarvestable, nothingEffective, oneOf(i)) 91 } 92 93 // EncodeItem ... 94 func (i ItemFrame) EncodeItem() (name string, meta int16) { 95 if i.Glowing { 96 return "minecraft:glow_frame", 0 97 } 98 return "minecraft:frame", 0 99 } 100 101 // EncodeBlock ... 102 func (i ItemFrame) EncodeBlock() (name string, properties map[string]any) { 103 name = "minecraft:frame" 104 if i.Glowing { 105 name = "minecraft:glow_frame" 106 } 107 return name, map[string]any{ 108 "facing_direction": int32(i.Facing.Opposite()), 109 "item_frame_map_bit": uint8(0), // TODO: When maps are added, set this to true if the item is a map. 110 "item_frame_photo_bit": uint8(0), // Only implemented in Education Edition. 111 } 112 } 113 114 // DecodeNBT ... 115 func (i ItemFrame) DecodeNBT(data map[string]any) any { 116 i.DropChance = float64(nbtconv.Float32(data, "ItemDropChance")) 117 i.Rotations = int(nbtconv.Uint8(data, "ItemRotation")) 118 i.Item = nbtconv.MapItem(data, "Item") 119 return i 120 } 121 122 // EncodeNBT ... 123 func (i ItemFrame) EncodeNBT() map[string]any { 124 m := map[string]any{ 125 "ItemDropChance": float32(i.DropChance), 126 "ItemRotation": uint8(i.Rotations), 127 "id": "ItemFrame", 128 } 129 if i.Glowing { 130 m["id"] = "GlowItemFrame" 131 } 132 if !i.Item.Empty() { 133 m["Item"] = nbtconv.WriteItem(i.Item, true) 134 } 135 return m 136 } 137 138 // Pick returns the item that is picked when the block is picked. 139 func (i ItemFrame) Pick() item.Stack { 140 if i.Item.Empty() { 141 return item.NewStack(ItemFrame{Glowing: i.Glowing}, 1) 142 } 143 return item.NewStack(i.Item.Item(), 1) 144 } 145 146 // SideClosed ... 147 func (ItemFrame) SideClosed(cube.Pos, cube.Pos, *world.World) bool { 148 return false 149 } 150 151 // NeighbourUpdateTick ... 152 func (i ItemFrame) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { 153 if _, ok := w.Block(pos.Side(i.Facing)).Model().(model.Empty); ok { 154 // TODO: Allow exceptions for pressure plates. 155 w.SetBlock(pos, nil, nil) 156 w.AddParticle(pos.Vec3Centre(), particle.BlockBreak{Block: i}) 157 dropItem(w, item.NewStack(i, 1), pos.Vec3Centre()) 158 if !i.Item.Empty() { 159 dropItem(w, i.Item, pos.Vec3Centre()) 160 } 161 } 162 } 163 164 // allItemFrames ... 165 func allItemFrames() (frames []world.Block) { 166 for _, f := range cube.Faces() { 167 frames = append(frames, ItemFrame{Facing: f, Glowing: true}) 168 frames = append(frames, ItemFrame{Facing: f, Glowing: false}) 169 } 170 return 171 }