github.com/df-mc/dragonfly@v0.9.13/server/internal/iteminternal/components.go (about)

     1  package iteminternal
     2  
     3  import (
     4  	"github.com/df-mc/dragonfly/server/item"
     5  	"github.com/df-mc/dragonfly/server/world"
     6  	"strings"
     7  )
     8  
     9  // Components returns all the components of the given custom item. If the item has no components, a nil map and false
    10  // are returned.
    11  func Components(it world.CustomItem) map[string]any {
    12  	category := it.Category()
    13  	identifier, _ := it.EncodeItem()
    14  	name := strings.Split(identifier, ":")[1]
    15  
    16  	builder := NewComponentBuilder(it.Name(), identifier, category)
    17  
    18  	if x, ok := it.(item.Armour); ok {
    19  		builder.AddComponent("minecraft:armor", map[string]any{
    20  			"protection": int32(x.DefencePoints()),
    21  		})
    22  
    23  		var slot string
    24  		switch it.(type) {
    25  		case item.HelmetType:
    26  			slot = "slot.armor.head"
    27  		case item.ChestplateType:
    28  			slot = "slot.armor.chest"
    29  		case item.LeggingsType:
    30  			slot = "slot.armor.legs"
    31  		case item.BootsType:
    32  			slot = "slot.armor.feet"
    33  		}
    34  		builder.AddComponent("minecraft:wearable", map[string]any{
    35  			"slot": slot,
    36  		})
    37  	}
    38  	if x, ok := it.(item.Consumable); ok {
    39  		builder.AddProperty("use_duration", int32(x.ConsumeDuration().Seconds()*20))
    40  		builder.AddComponent("minecraft:food", map[string]any{
    41  			"can_always_eat": x.AlwaysConsumable(),
    42  		})
    43  
    44  		if y, ok := it.(item.Drinkable); ok && y.Drinkable() {
    45  			builder.AddProperty("use_animation", int32(2))
    46  		} else {
    47  			builder.AddProperty("use_animation", int32(1))
    48  		}
    49  	}
    50  	if x, ok := it.(item.Cooldown); ok {
    51  		builder.AddComponent("minecraft:cooldown", map[string]any{
    52  			"category": name,
    53  			"duration": float32(x.Cooldown().Seconds()),
    54  		})
    55  	}
    56  	if x, ok := it.(item.Durable); ok {
    57  		builder.AddComponent("minecraft:durability", map[string]any{
    58  			"max_durability": int32(x.DurabilityInfo().MaxDurability),
    59  		})
    60  	}
    61  	if x, ok := it.(item.MaxCounter); ok {
    62  		builder.AddProperty("max_stack_size", int32(x.MaxCount()))
    63  	}
    64  	if x, ok := it.(item.OffHand); ok {
    65  		builder.AddProperty("allow_off_hand", x.OffHand())
    66  	}
    67  	if x, ok := it.(item.Throwable); ok {
    68  		// The data in minecraft:projectile is only used by vanilla server-side, but we must send at least an empty map
    69  		// so the client will play the throwing animation.
    70  		builder.AddComponent("minecraft:projectile", map[string]any{})
    71  		builder.AddComponent("minecraft:throwable", map[string]any{
    72  			"do_swing_animation": x.SwingAnimation(),
    73  		})
    74  	}
    75  	if x, ok := it.(item.Glinted); ok {
    76  		builder.AddProperty("foil", x.Glinted())
    77  	}
    78  	if x, ok := it.(item.HandEquipped); ok {
    79  		builder.AddProperty("hand_equipped", x.HandEquipped())
    80  	}
    81  	itemScale := calculateItemScale(it)
    82  	builder.AddComponent("minecraft:render_offsets", map[string]any{
    83  		"main_hand": map[string]any{
    84  			"first_person": map[string]any{
    85  				"scale": itemScale,
    86  			},
    87  			"third_person": map[string]any{
    88  				"scale": itemScale,
    89  			},
    90  		},
    91  		"off_hand": map[string]any{
    92  			"first_person": map[string]any{
    93  				"scale": itemScale,
    94  			},
    95  			"third_person": map[string]any{
    96  				"scale": itemScale,
    97  			},
    98  		},
    99  	})
   100  
   101  	return builder.Construct()
   102  }
   103  
   104  // calculateItemScale calculates the scale of the item to be rendered to the player according to the given size.
   105  func calculateItemScale(it world.CustomItem) []float32 {
   106  	width := float32(it.Texture().Bounds().Dx())
   107  	height := float32(it.Texture().Bounds().Dy())
   108  	var x, y, z float32 = 0.1, 0.1, 0.1
   109  	if _, ok := it.(item.HandEquipped); ok {
   110  		x, y, z = 0.075, 0.125, 0.075
   111  	}
   112  	newX := x / (width / 16)
   113  	newY := y / (height / 16)
   114  	newZ := z / (width / 16)
   115  	return []float32{newX, newY, newZ}
   116  }