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

     1  package effect
     2  
     3  import (
     4  	"github.com/df-mc/dragonfly/server/world"
     5  	"image/color"
     6  	"time"
     7  )
     8  
     9  // LastingType represents an effect type that can have a duration. An effect can be made using it by calling effect.New
    10  // with the LastingType.
    11  type LastingType interface {
    12  	Type
    13  	// Start is called for lasting effects when they are initially added to an entity.
    14  	Start(e world.Entity, lvl int)
    15  	// End is called for lasting effects when they are removed from an entity.
    16  	End(e world.Entity, lvl int)
    17  }
    18  
    19  // PotentType represents an effect type which can have its potency changed.
    20  type PotentType interface {
    21  	Type
    22  	// WithPotency updates the potency of the type with the one given and returns it.
    23  	WithPotency(potency float64) Type
    24  }
    25  
    26  // Type is an effect implementation that can be applied to an entity.
    27  type Type interface {
    28  	// RGBA returns the colour of the effect. If multiple effects are present, the colours will be mixed
    29  	// together to form a new colour.
    30  	RGBA() color.RGBA
    31  	// Apply applies the effect to an entity. This method applies the effect to an entity once for instant effects, such
    32  	// as healing the world.Entity for instant health.
    33  	// Apply always has a duration of 0 passed to it for instant effect implementations. For lasting effects that
    34  	// implement LastingType, the appropriate leftover duration is passed.
    35  	Apply(e world.Entity, lvl int, d time.Duration)
    36  }
    37  
    38  // Effect is an effect that can be added to an entity. Effects are either instant (applying the effect only once) or
    39  // lasting (applying the effect every tick).
    40  type Effect struct {
    41  	t                        Type
    42  	d                        time.Duration
    43  	lvl                      int
    44  	ambient, particlesHidden bool
    45  }
    46  
    47  // NewInstant returns a new instant Effect using the Type passed. The effect will be applied to an entity once
    48  // and will expire immediately after.
    49  func NewInstant(t Type, lvl int) Effect {
    50  	return Effect{t: t, lvl: lvl}
    51  }
    52  
    53  // New creates a new Effect using a LastingType passed. Once added to an entity, the time.Duration passed will be ticked down
    54  // by the entity until it reaches a duration of 0.
    55  func New(t LastingType, lvl int, d time.Duration) Effect {
    56  	return Effect{t: t, lvl: lvl, d: d}
    57  }
    58  
    59  // NewAmbient creates a new ambient (reduced particles, as when using a beacon) Effect using a LastingType passed. Once added
    60  // to an entity, the time.Duration passed will be ticked down by the entity until it reaches a duration of 0.
    61  func NewAmbient(t LastingType, lvl int, d time.Duration) Effect {
    62  	return Effect{t: t, lvl: lvl, d: d, ambient: true}
    63  }
    64  
    65  // WithoutParticles returns the same Effect with particles disabled. Adding the effect to players will not display the
    66  // particles around the player.
    67  func (e Effect) WithoutParticles() Effect {
    68  	e.particlesHidden = true
    69  	return e
    70  }
    71  
    72  // ParticlesHidden returns true if the Effect had its particles hidden by calling WithoutParticles.
    73  func (e Effect) ParticlesHidden() bool {
    74  	return e.particlesHidden
    75  }
    76  
    77  // Level returns the level of the Effect.
    78  func (e Effect) Level() int {
    79  	return e.lvl
    80  }
    81  
    82  // Duration returns the leftover duration of the Effect. The duration returned is always 0 if NewInstant was used to
    83  // create the effect.
    84  func (e Effect) Duration() time.Duration {
    85  	return e.d
    86  }
    87  
    88  // Ambient returns whether the Effect is an ambient effect, leading to reduced particles shown to the client. False is
    89  // always returned if the Effect was created using New or NewInstant.
    90  func (e Effect) Ambient() bool {
    91  	return e.ambient
    92  }
    93  
    94  // Type returns the underlying type of the Effect. It is either of the type Type or LastingType, depending on whether it
    95  // was created using New or NewAmbient, or NewInstant.
    96  func (e Effect) Type() Type {
    97  	return e.t
    98  }
    99  
   100  // TickDuration ticks the effect duration, subtracting time.Second/20 from the leftover time and returning the resulting
   101  // Effect.
   102  func (e Effect) TickDuration() Effect {
   103  	if _, ok := e.t.(LastingType); ok {
   104  		e.d -= time.Second / 20
   105  	}
   106  	return e
   107  }
   108  
   109  // nopLasting is a lasting effect with no (server-side) behaviour. It does not implement the RGBA method.
   110  type nopLasting struct{}
   111  
   112  func (nopLasting) Apply(world.Entity, int, time.Duration) {}
   113  func (nopLasting) End(world.Entity, int)                  {}
   114  func (nopLasting) Start(world.Entity, int)                {}
   115  
   116  // tickDuration returns the duration as in-game ticks.
   117  func tickDuration(d time.Duration) int {
   118  	return int(d / (time.Second / 20))
   119  }
   120  
   121  // ResultingColour calculates the resulting colour of the effects passed and returns a bool specifying if the
   122  // effects were ambient effects, which will cause their particles to display less frequently.
   123  func ResultingColour(effects []Effect) (color.RGBA, bool) {
   124  	r, g, b, a, n := 0, 0, 0, 0, 0
   125  	ambient := true
   126  	for _, e := range effects {
   127  		if e.particlesHidden {
   128  			// Don't take effects with hidden particles into account for colour calculation: Their particles are hidden
   129  			// after all.
   130  			continue
   131  		}
   132  		c := e.Type().RGBA()
   133  		r += int(c.R)
   134  		g += int(c.G)
   135  		b += int(c.B)
   136  		a += int(c.A)
   137  		n++
   138  		if !e.Ambient() {
   139  			ambient = false
   140  		}
   141  	}
   142  	if n == 0 {
   143  		return color.RGBA{R: 0x38, G: 0x5d, B: 0xc6, A: 0xff}, false
   144  	}
   145  	return color.RGBA{R: uint8(r / n), G: uint8(g / n), B: uint8(b / n), A: uint8(a / n)}, ambient
   146  }
   147  
   148  // living represents a living entity that has health and the ability to move around.
   149  type living interface {
   150  	world.Entity
   151  	// Health returns the health of the entity.
   152  	Health() float64
   153  	// MaxHealth returns the maximum health of the entity.
   154  	MaxHealth() float64
   155  	// SetMaxHealth changes the maximum health of the entity to the value passed.
   156  	SetMaxHealth(v float64)
   157  	// Hurt hurts the entity for a given amount of damage. The source passed represents the cause of the
   158  	// damage, for example entity.AttackDamageSource if the entity is attacked by another entity.
   159  	// If the final damage exceeds the health that the player currently has, the entity is killed.
   160  	Hurt(damage float64, source world.DamageSource) (n float64, vulnerable bool)
   161  	// Heal heals the entity for a given amount of health. The source passed represents the cause of the
   162  	// healing, for example entity.FoodHealingSource if the entity healed by having a full food bar. If the health
   163  	// added to the original health exceeds the entity's max health, Heal may not add the full amount.
   164  	Heal(health float64, source world.HealingSource)
   165  	// Speed returns the current speed of the living entity. The default value is different for each entity.
   166  	Speed() float64
   167  	// SetSpeed sets the speed of an entity to a new value.
   168  	SetSpeed(float64)
   169  }