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  }