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  }