Source: TreasureCollector.js

/**
 * @class
 */
function TreasureCollector() {}

TreasureCollector.prototype.Schema =
	"<a:help>Defines the treasure collecting abilities.</a:help>" +
	"<a:example>" +
		"<MaxDistance>2.0</MaxDistance>" +
	"</a:example>" +
	"<element name='MaxDistance' a:help='The maximum treasure taking distance in m.'>" +
		"<ref name='positiveDecimal'/>" +
	"</element>";

TreasureCollector.prototype.Init = function()
{
};

/**
 * @return {Object} - Min/Max range at which this entity can claim a treasure.
 */
TreasureCollector.prototype.GetRange = function()
{
	return { "min": 0, "max": +this.template.MaxDistance };
};

/**
 * @param {number} target - Entity ID of the target.
 * @return {boolean} - Whether we can collect from the target.
 */
TreasureCollector.prototype.CanCollect = function(target)
{
	let cmpTreasure = Engine.QueryInterface(target, IID_Treasure);
	return cmpTreasure && cmpTreasure.IsAvailable();
};

/**
 * @param {number} target - The target to collect.
 * @param {number} callerIID - The IID to notify on specific events.
 *
 * @return {boolean} - Whether we started collecting.
 */
TreasureCollector.prototype.StartCollecting = function(target, callerIID)
{
	if (this.target)
		this.StopCollecting();

	let cmpTreasure = Engine.QueryInterface(target, IID_Treasure);
	if (!cmpTreasure || !cmpTreasure.IsAvailable())
		return false;

	let cmpVisual = Engine.QueryInterface(this.entity, IID_Visual);
	if (cmpVisual)
		cmpVisual.SelectAnimation("collecting_treasure", false, 1.0);

	this.target = target;
	this.callerIID = callerIID;

	// ToDo: Implement rate modifiers.
	let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
	this.timer = cmpTimer.SetTimeout(this.entity, IID_TreasureCollector, "CollectTreasure", cmpTreasure.CollectionTime(), null);

	return true;
};

/**
 * @param {string} reason - The reason why we stopped collecting, used to notify the caller.
 */
TreasureCollector.prototype.StopCollecting = function(reason)
{
	if (!this.target)
		return;

	let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
	cmpTimer.CancelTimer(this.timer);
	delete this.timer;

	delete this.target;

	let cmpVisual = Engine.QueryInterface(this.entity, IID_Visual);
	if (cmpVisual)
		cmpVisual.SelectAnimation("idle", false, 1.0);

	// The callerIID component may start again,
	// replacing the callerIID, hence save that.
	let callerIID = this.callerIID;
	delete this.callerIID;

	if (reason && callerIID)
	{
		let component = Engine.QueryInterface(this.entity, callerIID);
		if (component)
			component.ProcessMessage(reason, null);
	}
};

/**
 * @params - Data and lateness are unused.
 */
TreasureCollector.prototype.CollectTreasure = function(data, lateness)
{
	let cmpTreasure = Engine.QueryInterface(this.target, IID_Treasure);
	if (!cmpTreasure || !cmpTreasure.IsAvailable())
	{
		this.StopCollecting("TargetInvalidated");
		return;
	}

	if (!this.IsTargetInRange(this.target))
	{
		this.StopCollecting("OutOfRange");
		return;
	}

	cmpTreasure.Reward(this.entity);
	this.StopCollecting("TargetInvalidated");
};

/**
 * @param {number} - The entity ID of the target to check.
 * @return {boolean} - Whether this entity is in range of its target.
 */
TreasureCollector.prototype.IsTargetInRange = function(target)
{
	let range = this.GetRange();
	let cmpObstructionManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ObstructionManager);
	return cmpObstructionManager.IsInTargetRange(this.entity, target, range.min, range.max, false);
};

Engine.RegisterComponentType(IID_TreasureCollector, "TreasureCollector", TreasureCollector);