273 lines
11 KiB
Go
273 lines
11 KiB
Go
|
|
package item
|
||
|
|
|
||
|
|
import (
|
||
|
|
"encoding/binary"
|
||
|
|
"image/color"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"github.com/df-mc/dragonfly/server/block/cube"
|
||
|
|
"github.com/df-mc/dragonfly/server/entity/effect"
|
||
|
|
"github.com/df-mc/dragonfly/server/world"
|
||
|
|
"github.com/go-gl/mathgl/mgl64"
|
||
|
|
)
|
||
|
|
|
||
|
|
// MaxCounter represents an item that has a specific max count. By default, each item will be expected to have
|
||
|
|
// a maximum count of 64. MaxCounter may be implemented to change this behaviour.
|
||
|
|
type MaxCounter interface {
|
||
|
|
// MaxCount returns the maximum number of items that a stack may be composed of. The number returned must
|
||
|
|
// be positive.
|
||
|
|
MaxCount() int
|
||
|
|
}
|
||
|
|
|
||
|
|
// UsableOnBlock represents an item that may be used on a block. If an item implements this interface, the
|
||
|
|
// UseOnBlock method is called whenever the item is used on a block.
|
||
|
|
type UsableOnBlock interface {
|
||
|
|
// UseOnBlock is called when an item is used on a block. The world passed is the world that the item was
|
||
|
|
// used in. The user passed is the entity that used the item. Usually this entity is a player.
|
||
|
|
// The position of the block that was clicked, along with the clicked face and the position clicked
|
||
|
|
// relative to the corner of the block are passed.
|
||
|
|
// UseOnBlock returns a bool indicating if the item was used successfully.
|
||
|
|
UseOnBlock(pos cube.Pos, face cube.Face, clickPos mgl64.Vec3, tx *world.Tx, user User, ctx *UseContext) bool
|
||
|
|
}
|
||
|
|
|
||
|
|
// UsableOnEntity represents an item that may be used on an entity. If an item implements this interface, the
|
||
|
|
// UseOnEntity method is called whenever the item is used on an entity.
|
||
|
|
type UsableOnEntity interface {
|
||
|
|
// UseOnEntity is called when an item is used on an entity. The world passed is the world that the item is
|
||
|
|
// used in, and the entity clicked and the user of the item are also passed.
|
||
|
|
// UseOnEntity returns a bool indicating if the item was used successfully.
|
||
|
|
UseOnEntity(e world.Entity, tx *world.Tx, user User, ctx *UseContext) bool
|
||
|
|
}
|
||
|
|
|
||
|
|
// Usable represents an item that may be used 'in the air'. If an item implements this interface, the Use
|
||
|
|
// method is called whenever the item is used while pointing at the air. (For example, when throwing an egg.)
|
||
|
|
type Usable interface {
|
||
|
|
// Use is called when the item is used in the air. The user that used the item and the world that the item
|
||
|
|
// was used in are passed to the method.
|
||
|
|
// Use returns a bool indicating if the item was used successfully.
|
||
|
|
Use(tx *world.Tx, user User, ctx *UseContext) bool
|
||
|
|
}
|
||
|
|
|
||
|
|
// Throwable represents a custom item that can be thrown such as a projectile. This will only have an effect on
|
||
|
|
// non-vanilla items.
|
||
|
|
type Throwable interface {
|
||
|
|
// SwingAnimation returns true if the client should cause the player's arm to swing when the item is thrown.
|
||
|
|
SwingAnimation() bool
|
||
|
|
}
|
||
|
|
|
||
|
|
// OffHand represents an item that can be held in the off hand.
|
||
|
|
type OffHand interface {
|
||
|
|
// OffHand returns true if the item can be held in the off hand.
|
||
|
|
OffHand() bool
|
||
|
|
}
|
||
|
|
|
||
|
|
// Consumable represents an item that may be consumed by a player. If an item implements this interface, a player
|
||
|
|
// may use and hold the item to consume it.
|
||
|
|
type Consumable interface {
|
||
|
|
// AlwaysConsumable specifies if the item is always consumable. Normal food can generally only be consumed
|
||
|
|
// when the food bar is not full or when in creative mode. Returning true here means the item can always
|
||
|
|
// be consumed, like golden apples or potions.
|
||
|
|
AlwaysConsumable() bool
|
||
|
|
// ConsumeDuration is the duration consuming the item takes. If the player is using the item for at least
|
||
|
|
// this duration, the item will be consumed and have its Consume method called.
|
||
|
|
ConsumeDuration() time.Duration
|
||
|
|
// Consume consumes one item of the Stack that the Consumable is in. The Stack returned is added back to
|
||
|
|
// the inventory after consuming the item. For potions, for example, an empty bottle is returned.
|
||
|
|
Consume(tx *world.Tx, c Consumer) Stack
|
||
|
|
}
|
||
|
|
|
||
|
|
// Consumer represents a User that is able to consume Consumable items.
|
||
|
|
type Consumer interface {
|
||
|
|
User
|
||
|
|
// Saturate saturates the Consumer's food bar by the amount of food points passed and the saturation by
|
||
|
|
// up to as many saturation points as passed. The final saturation will never exceed the final food level.
|
||
|
|
Saturate(food int, saturation float64)
|
||
|
|
// AddEffect adds an effect.Effect to the Consumer. If the effect is instant, it is applied to the Consumer
|
||
|
|
// immediately. If not, the effect is applied to the consumer every time the Tick method is called.
|
||
|
|
// AddEffect will overwrite any effects present if the level of the effect is higher than the existing one, or
|
||
|
|
// if the effects' levels are equal and the new effect has a longer duration.
|
||
|
|
AddEffect(e effect.Effect)
|
||
|
|
// RemoveEffect removes any effect that might currently be active on the Consumer.
|
||
|
|
RemoveEffect(e effect.Type)
|
||
|
|
// Effects returns any effect currently applied to the Consumer. The returned effects are guaranteed not to have
|
||
|
|
// expired when returned.
|
||
|
|
Effects() []effect.Effect
|
||
|
|
// Absorption returns the absorption health of the Consumer.
|
||
|
|
Absorption() float64
|
||
|
|
// SetAbsorption sets the absorption health of the Consumer. Absorption health is shown as golden hearts and does
|
||
|
|
// not regenerate naturally.
|
||
|
|
SetAbsorption(health float64)
|
||
|
|
}
|
||
|
|
|
||
|
|
// DefaultConsumeDuration is the default duration that consuming an item takes. Dried kelp takes half this
|
||
|
|
// time to be consumed.
|
||
|
|
const DefaultConsumeDuration = (time.Second * 161) / 100
|
||
|
|
|
||
|
|
// Drinkable represents a custom item that can be drunk. It is used to make the client show the correct drinking
|
||
|
|
// animation when a player is using an item. This will only have an effect on non-vanilla items.
|
||
|
|
type Drinkable interface {
|
||
|
|
// Drinkable returns if the item can be drunk or not.
|
||
|
|
Drinkable() bool
|
||
|
|
}
|
||
|
|
|
||
|
|
// Glinted represents a custom item that can have a permanent enchantment glint, this glint is purely cosmetic and
|
||
|
|
// will show regardless of whether it is actually enchanted. An example of this is the enchanted golden apple.
|
||
|
|
type Glinted interface {
|
||
|
|
// Glinted returns whether the item has an enchantment glint.
|
||
|
|
Glinted() bool
|
||
|
|
}
|
||
|
|
|
||
|
|
// HandEquipped represents an item that can be 'hand equipped'. This means the item will show up in third person like
|
||
|
|
// a tool, sword or stick would, giving them a different orientation in the hand and making them slightly bigger.
|
||
|
|
type HandEquipped interface {
|
||
|
|
// HandEquipped returns whether the item is hand equipped.
|
||
|
|
HandEquipped() bool
|
||
|
|
}
|
||
|
|
|
||
|
|
// Weapon is an item that may be used as a weapon. It has an attack damage which may be different to the 2
|
||
|
|
// damage that attacking with an empty hand deals.
|
||
|
|
type Weapon interface {
|
||
|
|
// AttackDamage returns the custom attack damage to the weapon. The damage returned must not be negative.
|
||
|
|
AttackDamage() float64
|
||
|
|
}
|
||
|
|
|
||
|
|
// Cooldown represents an item that has a cooldown.
|
||
|
|
type Cooldown interface {
|
||
|
|
// Cooldown is the duration of the cooldown.
|
||
|
|
Cooldown() time.Duration
|
||
|
|
}
|
||
|
|
|
||
|
|
// nameable represents a block that may be named. These are often containers such as chests, which have a
|
||
|
|
// name displayed in their interface.
|
||
|
|
type nameable interface {
|
||
|
|
// WithName returns the block itself, except with a custom name applied to it.
|
||
|
|
WithName(a ...any) world.Item
|
||
|
|
}
|
||
|
|
|
||
|
|
// Releaser represents an entity that can release items, such as bows.
|
||
|
|
type Releaser interface {
|
||
|
|
User
|
||
|
|
// GameMode returns the gamemode of the releaser.
|
||
|
|
GameMode() world.GameMode
|
||
|
|
// PlaySound plays a world.Sound that only this Releaser can hear.
|
||
|
|
PlaySound(sound world.Sound)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Releasable represents an item that can be released.
|
||
|
|
type Releasable interface {
|
||
|
|
// Release is called when an item is released.
|
||
|
|
Release(releaser Releaser, tx *world.Tx, ctx *UseContext, duration time.Duration)
|
||
|
|
// Requirements returns the required items to release this item.
|
||
|
|
Requirements() []Stack
|
||
|
|
}
|
||
|
|
|
||
|
|
// Chargeable represents an item that can be charged.
|
||
|
|
type Chargeable interface {
|
||
|
|
// Charge is called when an item is being used.
|
||
|
|
Charge(releaser Releaser, tx *world.Tx, ctx *UseContext, duration time.Duration) bool
|
||
|
|
// ContinueCharge continues the charge.
|
||
|
|
ContinueCharge(releaser Releaser, tx *world.Tx, ctx *UseContext, duration time.Duration)
|
||
|
|
// ReleaseCharge is called when an item is being released.
|
||
|
|
ReleaseCharge(releaser Releaser, tx *world.Tx, ctx *UseContext) bool
|
||
|
|
// CanCharge returns whether the item can currently be charged.
|
||
|
|
CanCharge(releaser Releaser, tx *world.Tx, ctx *UseContext) bool
|
||
|
|
}
|
||
|
|
|
||
|
|
// User represents an entity that is able to use an item in the world, typically entities such as players,
|
||
|
|
// which interact with the world using an item.
|
||
|
|
type User interface {
|
||
|
|
Carrier
|
||
|
|
SetHeldItems(mainHand, offHand Stack)
|
||
|
|
|
||
|
|
UsingItem() bool
|
||
|
|
ReleaseItem()
|
||
|
|
UseItem()
|
||
|
|
}
|
||
|
|
|
||
|
|
// Carrier represents an entity that is able to carry an item.
|
||
|
|
type Carrier interface {
|
||
|
|
world.Entity
|
||
|
|
// HeldItems returns the items currently held by the entity. Viewers of the entity will be able to see
|
||
|
|
// these items.
|
||
|
|
HeldItems() (mainHand, offHand Stack)
|
||
|
|
}
|
||
|
|
|
||
|
|
// BeaconPayment represents an item that may be used as payment for a beacon to select effects to be broadcast
|
||
|
|
// to surrounding players.
|
||
|
|
type BeaconPayment interface {
|
||
|
|
PayableForBeacon() bool
|
||
|
|
}
|
||
|
|
|
||
|
|
// Compostable represents an item that may be used to fill up a composter.
|
||
|
|
type Compostable interface {
|
||
|
|
// CompostChance returns the chance the item will produce a layer of compost in the range of 0-1.
|
||
|
|
CompostChance() float64
|
||
|
|
}
|
||
|
|
|
||
|
|
// nopReleasable represents a releasable item that does nothing.
|
||
|
|
type nopReleasable struct{}
|
||
|
|
|
||
|
|
func (nopReleasable) Release(Releaser, *world.Tx, *UseContext, time.Duration) {}
|
||
|
|
func (nopReleasable) Requirements() []Stack {
|
||
|
|
return []Stack{}
|
||
|
|
}
|
||
|
|
|
||
|
|
// defaultFood represents a consumable item with a default consumption duration.
|
||
|
|
type defaultFood struct{}
|
||
|
|
|
||
|
|
// AlwaysConsumable ...
|
||
|
|
func (defaultFood) AlwaysConsumable() bool {
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
|
||
|
|
// ConsumeDuration ...
|
||
|
|
func (d defaultFood) ConsumeDuration() time.Duration {
|
||
|
|
return DefaultConsumeDuration
|
||
|
|
}
|
||
|
|
|
||
|
|
// eyePosition returns the position of the eyes of the entity if the entity implements entity.Eyed, or the
|
||
|
|
// actual position if it doesn't.
|
||
|
|
func eyePosition(e world.Entity) mgl64.Vec3 {
|
||
|
|
pos := e.Position()
|
||
|
|
if eyed, ok := e.(interface{ EyeHeight() float64 }); ok {
|
||
|
|
pos = pos.Add(mgl64.Vec3{0, eyed.EyeHeight()})
|
||
|
|
}
|
||
|
|
return pos
|
||
|
|
}
|
||
|
|
|
||
|
|
// torsoPosition returns the position of the torso of the entity if the entity implements entity.Torsoed, or the
|
||
|
|
// actual position if it doesn't.
|
||
|
|
func torsoPosition(e world.Entity) mgl64.Vec3 {
|
||
|
|
pos := e.Position()
|
||
|
|
if torso, ok := e.(interface{ TorsoHeight() float64 }); ok {
|
||
|
|
pos = pos.Add(mgl64.Vec3{0, torso.TorsoHeight()})
|
||
|
|
}
|
||
|
|
return pos
|
||
|
|
}
|
||
|
|
|
||
|
|
// Int32FromRGBA converts a color.RGBA into an int32. These int32s are present in things such as signs and dyed leather armour.
|
||
|
|
func int32FromRGBA(x color.RGBA) int32 {
|
||
|
|
if x.R == 0 && x.G == 0 && x.B == 0 {
|
||
|
|
// Default to black colour. The default (0x000000) is a transparent colour. Text with this colour will not show
|
||
|
|
// up on the sign.
|
||
|
|
return int32(-0x1000000)
|
||
|
|
}
|
||
|
|
return int32(binary.BigEndian.Uint32([]byte{x.A, x.R, x.G, x.B}))
|
||
|
|
}
|
||
|
|
|
||
|
|
// rgbaFromInt32 converts an int32 into a color.RGBA. These int32s are present in things such as signs and dyed leather armour.
|
||
|
|
func rgbaFromInt32(x int32) color.RGBA {
|
||
|
|
b := make([]byte, 4)
|
||
|
|
binary.BigEndian.PutUint32(b, uint32(x))
|
||
|
|
|
||
|
|
return color.RGBA{A: b[0], R: b[1], G: b[2], B: b[3]}
|
||
|
|
}
|
||
|
|
|
||
|
|
// boolByte returns 1 if the bool passed is true, or 0 if it is false.
|
||
|
|
func boolByte(b bool) uint8 {
|
||
|
|
if b {
|
||
|
|
return 1
|
||
|
|
}
|
||
|
|
return 0
|
||
|
|
}
|