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 }