github.com/df-mc/dragonfly@v0.9.13/server/entity/experience_orb_behaviour.go (about) 1 package entity 2 3 import ( 4 "github.com/df-mc/dragonfly/server/block/cube" 5 "github.com/df-mc/dragonfly/server/world" 6 "github.com/go-gl/mathgl/mgl64" 7 "math" 8 "time" 9 ) 10 11 // ExperienceOrbBehaviourConfig holds optional parameters for the creation of 12 // an ExperienceOrbBehaviour. 13 type ExperienceOrbBehaviourConfig struct { 14 // Gravity is the amount of Y velocity subtracted every tick. 15 Gravity float64 16 // Drag is used to reduce all axes of the velocity every tick. Velocity is 17 // multiplied with (1-Drag) every tick. 18 Drag float64 19 // ExistenceDuration specifies how long the experience orb should last. The 20 // default is time.Minute * 5. 21 ExistenceDuration time.Duration 22 // Experience is the amount of experience held by the orb. Default is 1. 23 Experience int 24 } 25 26 // New creates an ExperienceOrbBehaviour using the parameters in conf. 27 func (conf ExperienceOrbBehaviourConfig) New() *ExperienceOrbBehaviour { 28 if conf.Experience == 0 { 29 conf.Experience = 1 30 } 31 if conf.ExistenceDuration == 0 { 32 conf.ExistenceDuration = time.Minute * 5 33 } 34 b := &ExperienceOrbBehaviour{conf: conf, lastSearch: time.Now()} 35 b.passive = PassiveBehaviourConfig{ 36 Gravity: conf.Gravity, 37 Drag: conf.Drag, 38 ExistenceDuration: conf.ExistenceDuration, 39 Tick: b.tick, 40 }.New() 41 return b 42 } 43 44 // ExperienceOrbBehaviour implements Behaviour for an experience orb entity. 45 type ExperienceOrbBehaviour struct { 46 conf ExperienceOrbBehaviourConfig 47 48 passive *PassiveBehaviour 49 50 lastSearch time.Time 51 target experienceCollector 52 } 53 54 // Experience returns the amount of experience the orb carries. 55 func (exp *ExperienceOrbBehaviour) Experience() int { 56 return exp.conf.Experience 57 } 58 59 // Tick finds a target for the experience orb and moves the orb towards it. 60 func (exp *ExperienceOrbBehaviour) Tick(e *Ent) *Movement { 61 return exp.passive.Tick(e) 62 } 63 64 // followBox is the bounding box used to search for collectors to follow for experience orbs. 65 var followBox = cube.Box(-8, -8, -8, 8, 8, 8) 66 67 // tick finds a target for the experience orb and moves the orb towards it. 68 func (exp *ExperienceOrbBehaviour) tick(e *Ent) { 69 w, pos := e.World(), e.Position() 70 if exp.target != nil && (exp.target.Dead() || exp.target.World() != w || pos.Sub(exp.target.Position()).Len() > 8) { 71 exp.target = nil 72 } 73 74 if time.Since(exp.lastSearch) >= time.Second { 75 exp.findTarget(w, pos) 76 } 77 if exp.target != nil { 78 exp.moveToTarget(e) 79 } 80 } 81 82 // findTarget attempts to find a target for an experience orb in w around pos. 83 func (exp *ExperienceOrbBehaviour) findTarget(w *world.World, pos mgl64.Vec3) { 84 if exp.target == nil { 85 collectors := w.EntitiesWithin(followBox.Translate(pos), func(o world.Entity) bool { 86 _, ok := o.(experienceCollector) 87 return !ok 88 }) 89 if len(collectors) > 0 { 90 exp.target = collectors[0].(experienceCollector) 91 } 92 } 93 exp.lastSearch = time.Now() 94 } 95 96 // moveToTarget applies velocity to the experience orb so that it moves towards 97 // its current target. If it intersects with the target, the orb is collected. 98 func (exp *ExperienceOrbBehaviour) moveToTarget(e *Ent) { 99 pos, dst := e.Position(), exp.target.Position() 100 if o, ok := exp.target.(Eyed); ok { 101 dst[1] += o.EyeHeight() / 2 102 } 103 diff := dst.Sub(pos).Mul(0.125) 104 if dist := diff.LenSqr(); dist < 1 { 105 e.SetVelocity(e.Velocity().Add(diff.Normalize().Mul(0.2 * math.Pow(1-math.Sqrt(dist), 2)))) 106 } 107 108 if e.Type().BBox(e).Translate(pos).IntersectsWith(exp.target.Type().BBox(exp.target).Translate(exp.target.Position())) && exp.target.CollectExperience(exp.conf.Experience) { 109 _ = e.Close() 110 } 111 } 112 113 // experienceCollector represents an entity that can collect experience orbs. 114 type experienceCollector interface { 115 Living 116 // CollectExperience makes the player collect the experience points passed, 117 // adding it to the experience manager. A bool is returned indicating 118 // whether the player was able to collect the experience, or not, due to the 119 // 100ms delay between experience collection. 120 CollectExperience(value int) bool 121 }