github.com/df-mc/dragonfly@v0.9.13/server/entity/arrow.go (about)

     1  package entity
     2  
     3  import (
     4  	"github.com/df-mc/dragonfly/server/block/cube"
     5  	"github.com/df-mc/dragonfly/server/internal/nbtconv"
     6  	"github.com/df-mc/dragonfly/server/item"
     7  	"github.com/df-mc/dragonfly/server/item/enchantment"
     8  	"github.com/df-mc/dragonfly/server/item/potion"
     9  	"github.com/df-mc/dragonfly/server/world"
    10  	"github.com/df-mc/dragonfly/server/world/sound"
    11  	"github.com/go-gl/mathgl/mgl64"
    12  	"time"
    13  )
    14  
    15  // NewArrow creates a new Arrow and returns it. It is equivalent to calling NewTippedArrow with `potion.Potion{}` as
    16  // tip.
    17  func NewArrow(pos mgl64.Vec3, rot cube.Rotation, owner world.Entity) *Ent {
    18  	return NewTippedArrowWithDamage(pos, rot, 2.0, owner, potion.Potion{})
    19  }
    20  
    21  // NewArrowWithDamage creates a new Arrow with the given base damage, and returns it. It is equivalent to calling
    22  // NewTippedArrowWithDamage with `potion.Potion{}` as tip.
    23  func NewArrowWithDamage(pos mgl64.Vec3, rot cube.Rotation, damage float64, owner world.Entity) *Ent {
    24  	return NewTippedArrowWithDamage(pos, rot, damage, owner, potion.Potion{})
    25  }
    26  
    27  // NewTippedArrow creates a new Arrow with a potion effect added to an entity when hit.
    28  func NewTippedArrow(pos mgl64.Vec3, rot cube.Rotation, owner world.Entity, tip potion.Potion) *Ent {
    29  	return NewTippedArrowWithDamage(pos, rot, 2.0, owner, tip)
    30  }
    31  
    32  // NewTippedArrowWithDamage creates a new Arrow with a potion effect added to an entity when hit and, and returns it.
    33  // It uses the given damage as the base damage.
    34  func NewTippedArrowWithDamage(pos mgl64.Vec3, rot cube.Rotation, damage float64, owner world.Entity, tip potion.Potion) *Ent {
    35  	conf := arrowConf
    36  	conf.Damage = damage
    37  	conf.Potion = tip
    38  	a := Config{Behaviour: conf.New(owner)}.New(ArrowType{}, pos)
    39  	a.rot = rot
    40  	return a
    41  }
    42  
    43  var arrowConf = ProjectileBehaviourConfig{
    44  	Gravity:               0.05,
    45  	Drag:                  0.01,
    46  	Damage:                2.0,
    47  	Sound:                 sound.ArrowHit{},
    48  	SurviveBlockCollision: true,
    49  }
    50  
    51  // boolByte returns 1 if the bool passed is true, or 0 if it is false.
    52  func boolByte(b bool) uint8 {
    53  	if b {
    54  		return 1
    55  	}
    56  	return 0
    57  }
    58  
    59  // ArrowType is a world.EntityType implementation for Arrow.
    60  type ArrowType struct{}
    61  
    62  func (ArrowType) EncodeEntity() string { return "minecraft:arrow" }
    63  func (ArrowType) BBox(world.Entity) cube.BBox {
    64  	return cube.Box(-0.125, 0, -0.125, 0.125, 0.25, 0.125)
    65  }
    66  
    67  func (ArrowType) DecodeNBT(m map[string]any) world.Entity {
    68  	pot := potion.From(nbtconv.Int32(m, "auxValue") - 1)
    69  	arr := NewTippedArrowWithDamage(nbtconv.Vec3(m, "Pos"), nbtconv.Rotation(m), float64(nbtconv.Float32(m, "Damage")), nil, pot)
    70  	b := arr.conf.Behaviour.(*ProjectileBehaviour)
    71  	arr.vel = nbtconv.Vec3(m, "Motion")
    72  	b.conf.DisablePickup = !nbtconv.Bool(m, "player")
    73  	if !nbtconv.Bool(m, "isCreative") {
    74  		b.conf.PickupItem = item.NewStack(item.Arrow{Tip: pot}, 1)
    75  	}
    76  	arr.fireDuration = time.Duration(nbtconv.Int16(m, "Fire")) * time.Second / 20
    77  	b.conf.KnockBackForceAddend = (enchantment.Punch{}).KnockBackMultiplier() * float64(nbtconv.Uint8(m, "enchantPunch"))
    78  	if _, ok := m["StuckToBlockPos"]; ok {
    79  		b.collisionPos = nbtconv.Pos(m, "StuckToBlockPos")
    80  		b.collided = true
    81  	}
    82  	return arr
    83  }
    84  
    85  func (ArrowType) EncodeNBT(e world.Entity) map[string]any {
    86  	a := e.(*Ent)
    87  	b := a.conf.Behaviour.(*ProjectileBehaviour)
    88  	yaw, pitch := a.Rotation().Elem()
    89  	data := map[string]any{
    90  		"Pos":          nbtconv.Vec3ToFloat32Slice(a.Position()),
    91  		"Yaw":          float32(yaw),
    92  		"Pitch":        float32(pitch),
    93  		"Motion":       nbtconv.Vec3ToFloat32Slice(a.Velocity()),
    94  		"Damage":       float32(b.conf.Damage),
    95  		"Fire":         int16(a.OnFireDuration() * 20),
    96  		"enchantPunch": byte(b.conf.KnockBackForceAddend / (enchantment.Punch{}).KnockBackMultiplier()),
    97  		"auxValue":     int32(b.conf.Potion.Uint8() + 1),
    98  		"player":       boolByte(!b.conf.DisablePickup),
    99  		"isCreative":   boolByte(b.conf.PickupItem.Empty()),
   100  	}
   101  	// TODO: Save critical flag if Minecraft ever saves it?
   102  	if b.collided {
   103  		data["StuckToBlockPos"] = nbtconv.PosToInt32Slice(b.collisionPos)
   104  	}
   105  	return data
   106  }