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 }