/***********************************************************************

tdm_response_effects.script

These are the functions that are referenced in tdm_response_effects.def
All the "atomic" effects are executed here.

***********************************************************************/

#ifndef __DARKMOD_RESPONSE_EFFECTS__
#define __DARKMOD_RESPONSE_EFFECTS__

#include "script/tdm_ai.script"
#include "script/tdm_lights.script"
#include "script/tdm_ai_base.script"
#include "script/tdm_light_holders.script"  // #4195


namespace StimResponse {

#define STR_ENTITY_SELF "_SELF"
#define STR_ENTITY_SOURCE "_SOURCE"
#define ECLASS_EMITTER "func_emitter"

/**
*	This is where the reponse effects are performed.
*
*	This is	a prototype of such a function, it has to take 
*	two entities, a string and two floats (threadnum and magnitude) as argument.
*
*	void sr_effect_BLA_action(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {
*		// your action code here
*   }
*/

/**
* Returns either the entity as specified by <targetEntityStr>
* or the <self> entity, if the STR_ENTITY_SELF is found.
* Note that <self> is referring to the "response entity", which can be
* a different one (imagine AI heads and AI body, the heads will return
* the body as "response entity".
*/
entity getTargetEntity(string targetEntityStr, entity self, entity source) {
    // Check, if the "owner" should be returned
    if (targetEntityStr == STR_ENTITY_SELF) {
        return self.GetResponseEntity();
    }
	// Check whether "stimEnt" should be returned
	else if (targetEntityStr == STR_ENTITY_SOURCE) {
        return source.GetResponseEntity();
    }
    else {
        return sys.getEntity(targetEntityStr);
    }
}

float isActive(entity owner, string effectPostfix) {
	if (owner.getKey("sr_effect_" + effectPostfix + "_state") == "0") {
		return 0;
	}
	else {
		// Always default to "1" even if the key is not set at all
		return 1;
	}
}

void effect_damage_action(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {
	if (!isActive(owner, effectPostfix)) return;
	
	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	// Get the damage value as first argument
	entity damageTarget = getTargetEntity(owner.getKey(argRoot + "1"), owner, stimEnt);
	string damageType = owner.getKey(argRoot + "2");

	// Deal the damage (the magnitude is passed as scaling factor)
	damageTarget.damage(stimEnt, stimEnt, stimEnt.getOrigin(), damageType, magnitude);
}

void effect_heal_action(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {
	if (!isActive(owner, effectPostfix)) return;
	
	if (magnitude <= 0) {
		return;
	}

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	// Get the damage value as first argument
	entity healTarget = getTargetEntity(owner.getKey(argRoot + "1"), owner, stimEnt);

	// Heal the entity using the second argument as healDefName
	float healed = owner.heal(owner.getKey(argRoot + "2"), magnitude);

	// If the entity could be healed (returnvalue != 0), play the sound, if there is one
	if (healed != 0) {
		// Do we have a non-empty soundshader string specified
		string soundShader = owner.getKey(argRoot + "3");
		if (soundShader != "") {
			sys.cacheSoundShader(soundShader);

			// If the soundchannel argument is empty
			owner.startSoundShader(soundShader, SND_CHANNEL_VOICE);
		}
	}
}

void effect_kill_action(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {
	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	entity damageTarget = getTargetEntity(owner.getKey(argRoot + "1"), owner, stimEnt);

	// Deal the damage
	damageTarget.damage(stimEnt, stimEnt, stimEnt.getOrigin(), "damage_suicide", 1.0);
}

void effect_knockout_action(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {

	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	// Check if we have a projectile owner here
	string projectileOwner = stimEnt.getKey("projectile_owner");
	entity originator = $null_entity;
	if (projectileOwner != "") {
		originator = sys.getEntity(projectileOwner);
	}

	entity koTarget = getTargetEntity(owner.getKey(argRoot + "1"), owner, stimEnt);

	// Try to knockout the entity

	koTarget.KO_Knockout(originator);
}

// grayman #2468 - gas knockout effect

void effect_gas_knockout_action(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {

	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	// Check if we have a projectile owner here
	string projectileOwner = stimEnt.getKey("projectile_owner");
	entity originator = $null_entity;
	if (projectileOwner != "") {
		originator = sys.getEntity(projectileOwner);
	}

	entity koTarget = getTargetEntity(owner.getKey(argRoot + "1"), owner, stimEnt);

	// Try to knockout the entity

	koTarget.Gas_Knockout(originator);
}

void effect_remove_action(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {
	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	entity removeEnt = getTargetEntity(owner.getKey(argRoot + "1"), owner, stimEnt);

	// Remove the target entity
	removeEnt.remove();
}

void effect_trigger_action(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {
	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	entity triggerTarget = getTargetEntity(owner.getKey(argRoot + "1"), owner, stimEnt);

	// Check for an activator string (optional argument)
	string activatorStr = owner.getKey(argRoot + "2");

	// The activator entity is set to the player entity, if the key is empty
	entity activatorEntity = $player1;
	if (activatorStr != "") {
		activatorEntity = getTargetEntity(activatorStr, owner, stimEnt);
	}

	// Trigger the target entity
	triggerTarget.activate(activatorEntity);
}

void effect_play_sound_shader_action(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {
	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	string soundShader = owner.getKey(argRoot + "1");
	sys.cacheSoundShader(soundShader);

	// If the soundchannel argument is empty, the float defaults to 0, which is SND_CHANNEL_ANY
	owner.startSoundShader(soundShader, owner.getFloatKey(argRoot + "2"));
}

// Plays the SOUND (as defined in a "snd_" spawnarg), not to be confused with play SOUND SHADER
void effect_play_sound_action(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {
	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	string soundName = owner.getKey(argRoot + "1");

	// If the soundchannel argument is empty, the float defaults to 0, which is SND_CHANNEL_ANY
	owner.startSound(soundName, owner.getFloatKey(argRoot + "2"), false);
}

void effect_set_model_action(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {
	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	// Get the target entity
	entity modelTarget = getTargetEntity(owner.getKey(argRoot + "1"), owner, stimEnt);
	
	// Set the model on the entity as specified in the second argument
	modelTarget.setModel(owner.getKey(argRoot + "2"));
}

void effect_set_skin_action(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {
	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	// Get the target entity
	entity skinTarget = getTargetEntity(owner.getKey(argRoot + "1"), owner, stimEnt);
	
	// Set the skin on the entity as specified in the second argument
	skinTarget.setSkin(owner.getKey(argRoot + "2"));
}

void effect_set_spawnarg_action(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {
	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	// Get the target entity
	entity spTarget = getTargetEntity(owner.getKey(argRoot + "1"), owner, stimEnt);
	
	// Set the spawnarg key to the value (argument 2 = key, argument 3 = value)
	spTarget.setKey(owner.getKey(argRoot + "2"), owner.getKey(argRoot + "3"));
}

/* 
*  A couple of functions to turn lights on and off, if they are not TDM script lights.
*  This code was repeated in the functions below -- SteveL working on #4195 (more 
*  consistent behaviour for TDM lights vs standard lights) 
*/
void internal_turn_off_light(entity lightTarget)
{
    lightTarget.Off();
    // Check for a "extinguished" model
    string extModel = lightTarget.getKey("model_extinguished");
    if (extModel != "") {
        lightTarget.setModel(extModel);
    }
}

void internal_turn_on_light(entity lightTarget)
{
    lightTarget.On();
    // Check for a "lit" model
    string litModel = lightTarget.getKey("model_lit");
    if (litModel != "") {
        lightTarget.setModel(litModel);
    }
}

void effect_toggle_light_action(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {
	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	// Retrive the light target entity
	entity lightTarget = getTargetEntity(owner.getKey(argRoot + "1"), owner, stimEnt);
    
    // If this is a TDM light, use the script object instead of manipulating it directly -- SteveL #4195
    if ( lightTarget.hasFunction("LightsToggle") )
    {
        lightTarget.callFunction("LightsToggle");
    } 
    else
    {
        if (lightTarget.getLightLevel() > 0) 
        {
            internal_turn_off_light(lightTarget);
        }
        else 
        {
            internal_turn_on_light(lightTarget);
        }
    }
}

void effect_light_ignite_action(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {
	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	// Retrieve the light target entity
	entity lightTarget = getTargetEntity(owner.getKey(argRoot + "1"), owner, stimEnt);

	light_moving_ext thelight = lightTarget;
	thelight.response_ignite(stimEnt, 0);
}

void effect_light_extinguish_action(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {
	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	// Retrieve the light target entity
	entity lightTarget = getTargetEntity(owner.getKey(argRoot + "1"), owner, stimEnt);

	light_moving_ext thelight = lightTarget;
	thelight.response_extinguish(stimEnt, 0);
}

void effect_light_on_action(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) 
{
	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";
	entity lightTarget = getTargetEntity(owner.getKey(argRoot + "1"), owner, stimEnt);

    // If this is a TDM light, use the script object instead of manipulating it directly -- SteveL #4195
    if ( lightTarget.hasFunction("LightsOn") )
    {
        lightTarget.callFunction("LightsOn");
    } 
    else 
    {
        internal_turn_on_light(lightTarget);
    }
}

void effect_light_off_action(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) 
{
	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";
    entity lightTarget = getTargetEntity(owner.getKey(argRoot + "1"), owner, stimEnt);
    // lightTarget.setRadius(0); // SteveL #4186
    
	// If this is a TDM light, use the script object instead of manipulating it directly -- SteveL #4195
    if ( lightTarget.hasFunction("LightsOff") )
    {
        lightTarget.callFunction("LightsOff");
    } 
    else 
    {
        internal_turn_off_light(lightTarget);
    }
}

void effect_set_light_color_action(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {
	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	entity lightTarget = getTargetEntity(owner.getKey(argRoot + "1"), owner, stimEnt);
	
	vector color = owner.getVectorKey(argRoot + "2");
	lightTarget.setLightParms( color_x, color_y, color_z, 1 );
}

void effect_fade_light_color_action(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {
	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	entity lightTarget = getTargetEntity(owner.getKey(argRoot + "1"), owner, stimEnt);
	
	vector targetColor = owner.getVectorKey(argRoot + "2");
	float fadeTime = owner.getFloatKey(argRoot + "3");

	vector currentColor;
	currentColor_x = lightTarget.getLightParm(0);
	currentColor_y = lightTarget.getLightParm(1);
	currentColor_z = lightTarget.getLightParm(2);

	vector diffColor = targetColor - currentColor;
	float numIterations = 20;

	float i;
	for (i = 0; i < numIterations; i++) {
		currentColor_x += diffColor_x / numIterations;
		currentColor_y += diffColor_y / numIterations;
		currentColor_z += diffColor_z / numIterations;
		lightTarget.setLightParms( currentColor_x, currentColor_y, currentColor_z, 1 );
		sys.wait(fadeTime/numIterations);
	}

	lightTarget.setLightParms( targetColor_x, targetColor_y, targetColor_z, 1 );
}

void effect_play_animation_action(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {
	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	// Get the target entity
	entity animTarget = getTargetEntity(owner.getKey(argRoot + "1"), owner, stimEnt);
	
	float channel = owner.getFloatKey(argRoot + "3");

	// Default to ANIMCHANNEL_TORSO, if the spawnarg is empty
	if (owner.getKey(argRoot + "3") == "") {
		channel = ANIMCHANNEL_TORSO;
	}

	// Try to play the animation (argument 2 = anim string, argument 3 = channel (defaults to ANIMCHANNEL_TORSO))
	animTarget.playAnim(channel, owner.getKey(argRoot + "2"));
	
	while (!animTarget.animDone(channel, 0)) {
		sys.waitFrame();
	}

	// Play the idle animation when done
	animTarget.playAnim(channel, "idle");
}

void effect_set_ai_team_action(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {
	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	// Get the target entity
	entity target = getTargetEntity(owner.getKey(argRoot + "1"), owner, stimEnt);

	float team = owner.getFloatKey(argRoot + "2");
	target.setTeam(team);
}

void effect_apply_stim_action(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {
	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	// Get the target entity
	entity responseTarget = getTargetEntity(owner.getKey(argRoot + "1"), owner, stimEnt);

	// Get the stim type (default would be 0)
	float stimType = owner.getFloatKey(argRoot + "2");

	// Get the source entity (defaults to owner if empty)
	entity stimSource = getTargetEntity(owner.getKey(argRoot + "3"), owner, stimEnt);
	if (owner.getKey(argRoot + "3") == "") {
		stimSource = owner;
	}

	// Trigger the response
	responseTarget.ResponseTrigger(stimSource, stimType);
}

void effect_teleport_action(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {
	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	// Get the target entity
	entity teleportTarget = getTargetEntity(owner.getKey(argRoot + "1"), owner, stimEnt);

	// Get the vector
	vector v = owner.getVectorKey(argRoot + "2");

	// Is this relative positioning?
	if (owner.getKey(argRoot + "3") == "1") {
		// Add the current origin
		v += teleportTarget.getOrigin();
	}

	// Move the origin
	teleportTarget.setOrigin(v);
}

void effect_move_to_position_action(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {
	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	// Get the target entity
	entity moveTarget = getTargetEntity(owner.getKey(argRoot + "1"), owner, stimEnt);

	// Get the vector
	vector v = owner.getVectorKey(argRoot + "2");

	// Is this relative positioning?
	if (owner.getKey(argRoot + "3") == "1") {
		// Add the current origin
		v += moveTarget.getOrigin();
	}

	// Tell the entity to move
	moveTarget.moveToPosition(v);

	while (1) {
		sys.waitFrame();
		// Have we reached the target?
		if (moveTarget.moveStatus() == MOVE_STATUS_DONE) {
			// Notify the entity
			moveTarget.ResponseTrigger(moveTarget, STIM_TARGET_REACHED);
			break;
		}
	}
}

void effect_move_to_random_position_action(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {
	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	// Get the target entity
	entity moveTarget = getTargetEntity(owner.getKey(argRoot + "1"), owner, stimEnt);

	// Get the vector
	float radius = owner.getFloatKey(argRoot + "2");

	float counter = 0;
	while (moveTarget.moveStatus() != MOVE_STATUS_MOVING && counter < 5000) {
		vector v;
		// Create a new position candidate, if this one is unreachable
		v_x = sys.random(radius) - radius/2;
		v_y = sys.random(radius) - radius/2;
		v_z = 0;
		v += moveTarget.getOrigin();
		counter++;

		// Tell the entity to move to that position
		moveTarget.moveToPosition(v);

		sys.waitFrame();
	}

	while (1) {
		sys.waitFrame();
		// Have we reached the target?
		if (moveTarget.moveStatus() == MOVE_STATUS_DONE) {
			// Notify the entity
			moveTarget.ResponseTrigger(moveTarget, STIM_TARGET_REACHED);
			break;
		}
	}
}

void effect_set_angles_action(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {
	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	// Get the target entity
	entity angleTarget = getTargetEntity(owner.getKey(argRoot + "1"), owner, stimEnt);

	// Get the vector
	vector angles = owner.getVectorKey(argRoot + "2");

	// Is this measured relatively to the current angles?
	if (owner.getKey(argRoot + "3") == "1") {
		// Add the current origin
		angles += angleTarget.getAngles();
	}

	// Trigger the response
	angleTarget.setAngles(angles);
}

void effect_spawn_entity_action(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {
	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	// Get the target entity
	entity newEntity = sys.spawn(owner.getKey(argRoot + "1"));

	// Place the new entity at the specified location
	newEntity.setOrigin(owner.getVectorKey(argRoot + "2"));

	// Show the entity, to make sure
	newEntity.show();
}

void effect_disable_effect_action(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {
	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	// Get the target entity
	entity targetEntity = getTargetEntity(owner.getKey(argRoot + "1"), owner, stimEnt);

	// Set the "effect_x_y_state" spawnarg to 0 to disable the effect
	string targetEffectPostfix = owner.getKey(argRoot + "2");
	targetEntity.setKey("sr_effect_" + targetEffectPostfix + "_state", "0");
}

void effect_enable_effect_action(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {
	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	// Get the target entity
	entity targetEntity = getTargetEntity(owner.getKey(argRoot + "1"), owner, stimEnt);

	// Set the "effect_x_y_state" spawnarg to 0 to disable the effect
	string targetEffectPostfix = owner.getKey(argRoot + "2");
	targetEntity.setKey("sr_effect_" + targetEffectPostfix + "_state", "1");
}

void effect_toggle_effect_action(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {
	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	// Get the target entity
	entity targetEntity = getTargetEntity(owner.getKey(argRoot + "1"), owner, stimEnt);

	// Set the "effect_x_y_state" spawnarg to 0 to disable the effect
	string targetEffectPostfix = owner.getKey(argRoot + "2");

	// Invert the current state
	string curState = targetEntity.getKey("sr_effect_" + targetEffectPostfix + "_state");
	string newState;
	if (curState == "0") {
		newState = "1";
	}
	else {
		newState = "0";
	}

	// Set the inverted state on the target entity
	sys.print("Setting effect state on " + targetEntity.getKey("name") + " to " + newState + " (effectPostfix: " + targetEffectPostfix + ")\n");
	targetEntity.setKey("sr_effect_" + targetEffectPostfix + "_state", newState);
}

void effect_activate_stim_action(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {
	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	// Get the target entity
	entity targetEntity = getTargetEntity(owner.getKey(argRoot + "1"), owner, stimEnt);

	// Set the "effect_x_y_state" spawnarg to 0 to disable the effect
	float stimTypeID = owner.getFloatKey(argRoot + "2");

	// Set the stim state on the target entity
	targetEntity.StimEnable(stimTypeID, 1);
}

void effect_deactivate_stim_action(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {
	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	// Get the target entity
	entity targetEntity = getTargetEntity(owner.getKey(argRoot + "1"), owner, stimEnt);

	// Set the "effect_x_y_state" spawnarg to 0 to disable the effect
	float stimTypeID = owner.getFloatKey(argRoot + "2");

	// Set the stim state on the target entity
	targetEntity.StimEnable(stimTypeID, 0);
}

void effect_activate_response_action(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {
	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	// Get the target entity
	entity targetEntity = getTargetEntity(owner.getKey(argRoot + "1"), owner, stimEnt);

	// Set the "effect_x_y_state" spawnarg to 0 to disable the effect
	float stimTypeID = owner.getFloatKey(argRoot + "2");

	// Set the response state on the target entity
	targetEntity.ResponseEnable(stimTypeID, 1);
}

void effect_deactivate_response_action(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {
	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	// Get the target entity
	entity targetEntity = getTargetEntity(owner.getKey(argRoot + "1"), owner, stimEnt);

	// Set the "effect_x_y_state" spawnarg to 0 to disable the effect
	float stimTypeID = owner.getFloatKey(argRoot + "2");

	// Set the response state on the target entity
	targetEntity.ResponseEnable(stimTypeID, 0);
}

void effect_start_stim_timer_action(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {
	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	// Get the target entity
	entity targetEntity = getTargetEntity(owner.getKey(argRoot + "1"), owner, stimEnt);

	// Set the "effect_x_y_state" spawnarg to 0 to disable the effect
	float stimTypeID = owner.getFloatKey(argRoot + "2");

	// Start the timer on the target entity
	targetEntity.StartTimer(stimTypeID);
}

void effect_stop_stim_timer_action(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {
	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	// Get the target entity
	entity targetEntity = getTargetEntity(owner.getKey(argRoot + "1"), owner, stimEnt);

	// Set the "effect_x_y_state" spawnarg to 0 to disable the effect
	float stimTypeID = owner.getFloatKey(argRoot + "2");

	// Stop the timer on the target entity
	targetEntity.StopTimer(stimTypeID);
}

void effect_spawn_particle_action(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {
	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	// Get the target entity
	entity newEntity = sys.spawn(ECLASS_EMITTER);
	
	// Place the new entity at the specified location
	newEntity.setOrigin(owner.getVectorKey(argRoot + "1"));
	newEntity.setModel(owner.getKey(argRoot + "2"));
	newEntity.setShaderParm(SHADERPARM_PARTICLE_STOPTIME, 0);
	newEntity.setShaderParm(SHADERPARM_TIMEOFFSET, -sys.getTime());

	// Show the entity, to make sure
	newEntity.show();

	float lifetime = owner.getFloatKey(argRoot + "3");
	if (lifetime > 0) {
		sys.wait(lifetime);
		newEntity.remove();
	}
}

void effect_blind_action(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {
	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	// Get the target entity
	entity targetEntity = getTargetEntity(owner.getKey(argRoot + "1"), owner, stimEnt);

	// Try to blind the target AI entity (use second argument as "skipVisibility" bool)
	targetEntity.processBlindStim(stimEnt, owner.getFloatKey(argRoot + "2"));
}

void effect_activate_shooter_action(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {
	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	// Get the target entity
	entity targetEntity = getTargetEntity(owner.getKey(argRoot + "1"), owner, stimEnt);

	// Try to activate the shooter
	targetEntity.shooterSetState(1);
}

void effect_deactivate_shooter_action(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {
	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	// Get the target entity
	entity targetEntity = getTargetEntity(owner.getKey(argRoot + "1"), owner, stimEnt);

	// Try to deactivate the shooter
	targetEntity.shooterSetState(0);
}

void effect_frob(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {
	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	// Get the target entity
	entity targetEntity = getTargetEntity(owner.getKey(argRoot + "1"), owner, stimEnt);

	// Frob the entity
	targetEntity.frob();
}

void effect_set_frobable(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {
	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	// Get the target entity
	entity targetEntity = getTargetEntity(owner.getKey(argRoot + "1"), owner, stimEnt);

	// Get the bool
	boolean frobable = owner.getIntKey(argRoot + "2");

	// Try to set the frobable status
	targetEntity.setFrobable(frobable);
}

void effect_clear_targets(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {
	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	// Get the target entity
	entity targetEntity = getTargetEntity(owner.getKey(argRoot + "1"), owner, stimEnt);

	float num = targetEntity.numTargets();
	float i;
	for( i = 0; i < num; i++ )
	{
		targetEntity.removeTarget( targetEntity.getTarget(i) );
	}
}

void effect_add_target(entity owner, entity stimEnt, string effectPostfix, float magnitude, float threadNum) {
	if (!isActive(owner, effectPostfix)) return;

	// Search for something like "sr_effect_1_1_argN"
	string argRoot = "sr_effect_" + effectPostfix + "_arg";

	// Get the target entity
	entity targetEntity = getTargetEntity(owner.getKey(argRoot + "1"), owner, stimEnt);

	// get the argument entity (the one we want to set to target)
	entity argEntity = getTargetEntity(owner.getKey(argRoot + "2"), owner, stimEnt);

	// avoid infinite loops by targeting one's self
	if( argEntity != $null_entity && targetEntity != $null_entity && targetEntity != argEntity )
		targetEntity.addTarget( argEntity );
}


} // end namespace StimResponse

#endif //__DARKMOD_RESPONSE_EFFECTS__

