Source: StatusEffectsReceiver.js

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

StatusEffectsReceiver.prototype.DefaultInterval = 1000;

/**
 * Initialises the status effects.
 */
StatusEffectsReceiver.prototype.Init = function()
{
	this.activeStatusEffects = {};
};

/**
 * Which status effects are active on this entity.
 *
 * @return {Object} - An object containing the status effects which currently affect the entity.
 */
StatusEffectsReceiver.prototype.GetActiveStatuses = function()
{
	return this.activeStatusEffects;
};

/**
 * Called by Attacking effects. Adds status effects for each entry in the effectData.
 *
 * @param {Object} effectData - An object containing the status effects to give to the entity.
 * @param {number} attacker - The entity ID of the attacker.
 * @param {number} attackerOwner - The player ID of the attacker.
 * @param {number} bonusMultiplier - A value to multiply the damage with (not implemented yet for SE).
 *
 * @return {Object} - The codes of the status effects which were processed.
 */
StatusEffectsReceiver.prototype.ApplyStatus = function(effectData, attacker, attackerOwner)
{
	for (let effect in effectData)
		this.AddStatus(effect, effectData[effect], attacker, attackerOwner);

	// TODO: implement loot?

	return { "inflictedStatuses": Object.keys(effectData) };
};

/**
 * Adds a status effect to the entity.
 *
 * @param {string} statusCode - The code of the status effect.
 * @param {Object} data - The various effects and timings.
 * @param {number} attacker - optional, the entity ID of the attacker.
 * @param {number} attackerOwner - optional, the player ID of the attacker.
 */
StatusEffectsReceiver.prototype.AddStatus = function(baseCode, data, attacker = INVALID_ENTITY, attackerOwner = INVALID_PLAYER)
{
	let statusCode = baseCode;
	if (this.activeStatusEffects[statusCode])
	{
		if (data.Stackability == "Ignore")
			return;
		if (data.Stackability == "Extend")
		{
			this.activeStatusEffects[statusCode].Duration += data.Duration;
			return;
		}
		if (data.Stackability == "Replace")
			this.RemoveStatus(statusCode);
		else if (data.Stackability == "Stack")
		{
			let i = 0;
			let temp;
			do
				temp = statusCode + "_" + i++;
			while (!!this.activeStatusEffects[temp]);
			statusCode = temp;
		}
	}

	this.activeStatusEffects[statusCode] = {
		"baseCode": baseCode
	};
	let status = this.activeStatusEffects[statusCode];
	Object.assign(status, data);

	if (status.Modifiers)
	{
		let modifications = DeriveModificationsFromXMLTemplate(status.Modifiers);
		let cmpModifiersManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ModifiersManager);
		cmpModifiersManager.AddModifiers(statusCode, modifications, this.entity);
	}

	// With neither an interval nor a duration, there is no point in starting a timer.
	if (!status.Duration && !status.Interval)
		return;

	// We need this to prevent Status Effects from giving XP
	// to the entity that applied them.
	status.StatusEffect = true;

	// We want an interval to update the GUI to show how much time of the status effect
	// is left even if the status effect itself has no interval.
	if (!status.Interval)
		status._interval = this.DefaultInterval;

	status._timeElapsed = 0;
	status._firstTime = true;
	status.source = { "entity": attacker, "owner": attackerOwner };

	let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
	status._timer = cmpTimer.SetInterval(this.entity, IID_StatusEffectsReceiver, "ExecuteEffect", 0, +(status.Interval || status._interval), statusCode);
};

/**
 * Removes a status effect from the entity.
 *
 * @param {string} statusCode - The status effect to be removed.
 */
StatusEffectsReceiver.prototype.RemoveStatus = function(statusCode)
{
	let statusEffect = this.activeStatusEffects[statusCode];
	if (!statusEffect)
		return;

	if (statusEffect.Modifiers)
	{
		let cmpModifiersManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ModifiersManager);
		cmpModifiersManager.RemoveAllModifiers(statusCode, this.entity);
	}

	if (statusEffect._timer)
	{
		let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
		cmpTimer.CancelTimer(statusEffect._timer);
	}
	delete this.activeStatusEffects[statusCode];
};

/**
 * Called by the timers. Executes a status effect.
 *
 * @param {string} statusCode - The status effect to be executed.
 * @param {number} lateness - The delay between the calling of the function and the actual execution (turn time?).
 */
StatusEffectsReceiver.prototype.ExecuteEffect = function(statusCode, lateness)
{
	let status = this.activeStatusEffects[statusCode];
	if (!status)
		return;

	if (status.Damage || status.Capture)
		AttackHelper.HandleAttackEffects(this.entity, {
			"type": statusCode,
			"attackData": status,
			"attacker": status.source.entity,
			"attackerOwner": status.source.owner
		});

	if (!status.Duration)
		return;

	if (status._firstTime)
	{
		status._firstTime = false;
		status._timeElapsed += lateness;
	}
	else
		status._timeElapsed += +(status.Interval || status._interval) + lateness;

	if (status._timeElapsed >= +status.Duration)
		this.RemoveStatus(statusCode);
};

Engine.RegisterComponentType(IID_StatusEffectsReceiver, "StatusEffectsReceiver", StatusEffectsReceiver);