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  }