github.com/df-mc/dragonfly@v0.9.13/server/entity/firework_behaviour.go (about) 1 package entity 2 3 import ( 4 "github.com/df-mc/dragonfly/server/block/cube/trace" 5 "github.com/df-mc/dragonfly/server/item" 6 "github.com/df-mc/dragonfly/server/world" 7 "github.com/df-mc/dragonfly/server/world/sound" 8 "github.com/go-gl/mathgl/mgl64" 9 "math" 10 "time" 11 ) 12 13 // FireworkBehaviourConfig holds optional parameters for a FireworkBehaviour. 14 type FireworkBehaviourConfig struct { 15 // ExistenceDuration is the duration that an entity with this behaviour 16 // should last. Once this time expires, the entity is closed. If 17 // ExistenceDuration is 0, the entity will never expire automatically. 18 ExistenceDuration time.Duration 19 // SidewaysVelocityMultiplier is a value that the sideways velocity (X/Z) is 20 // multiplied with every tick. For normal fireworks this is 1.15. 21 SidewaysVelocityMultiplier float64 22 // UpwardsAcceleration is a value added to the firework's velocity every 23 // tick. For normal fireworks, this is 0.04. 24 UpwardsAcceleration float64 25 // Attached specifies if the firework is attached to its owner. If true, 26 // the firework will boost the speed of the owner while flying. 27 Attached bool 28 } 29 30 // New creates a FireworkBehaviour for an fw and owner using the optional 31 // parameters in conf. 32 func (conf FireworkBehaviourConfig) New(fw item.Firework, owner world.Entity) *FireworkBehaviour { 33 b := &FireworkBehaviour{conf: conf, firework: fw, owner: owner} 34 b.passive = PassiveBehaviourConfig{ 35 ExistenceDuration: conf.ExistenceDuration, 36 Expire: b.explode, 37 Tick: b.tick, 38 }.New() 39 return b 40 } 41 42 // FireworkBehaviour implements Behaviour for a firework entity. 43 type FireworkBehaviour struct { 44 conf FireworkBehaviourConfig 45 46 passive *PassiveBehaviour 47 48 firework item.Firework 49 owner world.Entity 50 } 51 52 // Firework returns the underlying item.Firework of the FireworkBehaviour. 53 func (f *FireworkBehaviour) Firework() item.Firework { 54 return f.firework 55 } 56 57 // Attached specifies if the firework is attached to its owner. 58 func (f *FireworkBehaviour) Attached() bool { 59 return f.conf.Attached 60 } 61 62 // Owner returns the world.Entity that launched the firework. 63 func (f *FireworkBehaviour) Owner() world.Entity { 64 return f.owner 65 } 66 67 // Tick moves the firework and makes it explode when it reaches its maximum 68 // duration. 69 func (f *FireworkBehaviour) Tick(e *Ent) *Movement { 70 return f.passive.Tick(e) 71 } 72 73 // tick ticks the entity, updating its velocity either with a constant factor 74 // or based on the owner's position and velocity if attached. 75 func (f *FireworkBehaviour) tick(e *Ent) { 76 var ownerVel mgl64.Vec3 77 if o, ok := f.owner.(interface { 78 Velocity() mgl64.Vec3 79 }); ok { 80 ownerVel = o.Velocity() 81 } 82 83 e.mu.Lock() 84 defer e.mu.Unlock() 85 if f.conf.Attached { 86 dV := f.owner.Rotation().Vec3() 87 88 // The client will propel itself to match the firework's velocity since 89 // we set the appropriate metadata. 90 e.pos = f.owner.Position() 91 e.vel = e.vel.Add(ownerVel.Add(dV.Mul(0.1).Add(dV.Mul(1.5).Sub(ownerVel).Mul(0.5)))) 92 } else { 93 e.vel[0] *= f.conf.SidewaysVelocityMultiplier 94 e.vel[1] += f.conf.UpwardsAcceleration 95 e.vel[2] *= f.conf.SidewaysVelocityMultiplier 96 } 97 } 98 99 // explode causes an explosion at the position of the firework, spawning 100 // particles and damaging nearby entities. 101 func (f *FireworkBehaviour) explode(e *Ent) { 102 w, pos, explosions := e.World(), e.Position(), f.firework.Explosions 103 104 for _, v := range w.Viewers(pos) { 105 v.ViewEntityAction(e, FireworkExplosionAction{}) 106 } 107 for _, explosion := range explosions { 108 if explosion.Shape == item.FireworkShapeHugeSphere() { 109 w.PlaySound(pos, sound.FireworkHugeBlast{}) 110 } else { 111 w.PlaySound(pos, sound.FireworkBlast{}) 112 } 113 if explosion.Twinkle { 114 w.PlaySound(pos, sound.FireworkTwinkle{}) 115 } 116 } 117 118 if len(explosions) == 0 { 119 return 120 } 121 122 force := float64(len(explosions)*2) + 5.0 123 targets := w.EntitiesWithin(e.Type().BBox(e).Translate(pos).Grow(5.25), func(e world.Entity) bool { 124 l, living := e.(Living) 125 return !living || l.AttackImmune() 126 }) 127 for _, e := range targets { 128 tpos := e.Position() 129 dist := pos.Sub(tpos).Len() 130 if dist > 5.0 { 131 // The maximum distance allowed is 5.0 blocks. 132 continue 133 } 134 if _, ok := trace.Perform(pos, tpos, w, e.Type().BBox(e).Grow(0.3), func(world.Entity) bool { 135 return true 136 }); ok { 137 dmg := force * math.Sqrt((5.0-dist)/5.0) 138 e.(Living).Hurt(dmg, ProjectileDamageSource{Owner: f.owner, Projectile: e}) 139 } 140 } 141 }