Files
mc/server/entity/arrow.go
T

99 lines
3.5 KiB
Go
Raw Normal View History

2026-07-09 08:33:57 +08:00
package entity
import (
"github.com/df-mc/dragonfly/server/block/cube"
"github.com/df-mc/dragonfly/server/internal/nbtconv"
"github.com/df-mc/dragonfly/server/item"
"github.com/df-mc/dragonfly/server/item/enchantment"
"github.com/df-mc/dragonfly/server/item/potion"
"github.com/df-mc/dragonfly/server/world"
"github.com/df-mc/dragonfly/server/world/sound"
)
// NewArrow creates a new Arrow and returns it. It is equivalent to calling NewTippedArrow with `potion.Potion{}` as
// tip.
func NewArrow(opts world.EntitySpawnOpts, owner world.Entity) *world.EntityHandle {
return NewTippedArrowWithDamage(opts, 2.0, owner, potion.Potion{})
}
// NewArrowWithDamage creates a new Arrow with the given base damage, and returns it. It is equivalent to calling
// NewTippedArrowWithDamage with `potion.Potion{}` as tip.
func NewArrowWithDamage(opts world.EntitySpawnOpts, damage float64, owner world.Entity) *world.EntityHandle {
return NewTippedArrowWithDamage(opts, damage, owner, potion.Potion{})
}
// NewTippedArrow creates a new Arrow with a potion effect added to an entity when hit.
func NewTippedArrow(opts world.EntitySpawnOpts, owner world.Entity, tip potion.Potion) *world.EntityHandle {
return NewTippedArrowWithDamage(opts, 2.0, owner, tip)
}
// NewTippedArrowWithDamage creates a new Arrow with a potion effect added to an entity when hit and, and returns it.
// It uses the given damage as the base damage.
func NewTippedArrowWithDamage(opts world.EntitySpawnOpts, damage float64, owner world.Entity, tip potion.Potion) *world.EntityHandle {
conf := arrowConf
conf.Damage = damage
conf.Potion = tip
conf.Owner = owner.H()
return opts.New(ArrowType, conf)
}
var arrowConf = ProjectileBehaviourConfig{
Gravity: 0.05,
Drag: 0.01,
Damage: 2.0,
Sound: sound.ArrowHit{},
SurviveBlockCollision: true,
}
// 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
}
// ArrowType is a world.EntityType implementation for Arrow.
var ArrowType arrowType
type arrowType struct{}
func (t arrowType) Open(tx *world.Tx, handle *world.EntityHandle, data *world.EntityData) world.Entity {
return &Ent{tx: tx, handle: handle, data: data}
}
func (arrowType) EncodeEntity() string { return "minecraft:arrow" }
func (arrowType) BBox(world.Entity) cube.BBox {
return cube.Box(-0.125, 0, -0.125, 0.125, 0.25, 0.125)
}
func (arrowType) DecodeNBT(m map[string]any, data *world.EntityData) {
conf := arrowConf
conf.Damage = float64(nbtconv.Float32(m, "Damage"))
conf.Potion = potion.From(nbtconv.Int32(m, "auxValue") - 1)
conf.DisablePickup = !nbtconv.Bool(m, "player")
if !nbtconv.Bool(m, "isCreative") {
conf.PickupItem = item.NewStack(item.Arrow{Tip: conf.Potion}, 1)
}
conf.KnockBackForceAddend = enchantment.Punch.KnockBackMultiplier() * float64(nbtconv.Uint8(m, "enchantPunch"))
conf.CollisionPosition = nbtconv.Pos(m, "StuckToBlockPos")
data.Data = conf.New()
}
func (arrowType) EncodeNBT(data *world.EntityData) map[string]any {
b := data.Data.(*ProjectileBehaviour)
m := map[string]any{
"Damage": float32(b.conf.Damage),
"enchantPunch": byte(b.conf.KnockBackForceAddend / enchantment.Punch.KnockBackMultiplier()),
"auxValue": int32(b.conf.Potion.Uint8() + 1),
"player": boolByte(!b.conf.DisablePickup),
"isCreative": boolByte(b.conf.PickupItem.Empty()),
}
// TODO: Save critical flag if Minecraft ever saves it?
if b.collided {
m["StuckToBlockPos"] = nbtconv.PosToInt32Slice(b.collisionPos)
}
return m
}