//-----------------------------------------------------------------------------
//
//  $Logfile:: /EF2/Code/DLLs/game/scriptslave.cpp                           $
// $Revision:: 64                                                             $
//   $Author:: Singlis                                                        $
//     $Date:: 9/26/03 2:36p                                                  $
//
// Copyright (C) 1997 by Ritual Entertainment, Inc.
// All rights reserved.
//
// This source is may not be distributed and/or modified without
// expressly written permission by Ritual Entertainment, Inc.
//

//
// DESCRIPTION:
// Standard scripted objects.  Controlled by ScriptThread.  These objects
// are bmodel objects created in the editor and controlled by an external
// text based script.  Commands are interpretted on by one and executed
// upon a signal from the script master.  The base script object can
// perform several different relative and specific rotations and translations
// and can cause other parts of the script to be executed when touched, damaged,
// touched, or used.
//

#include "_pch_cpp.h"
#include "class.h"
#include "mover.h"
#include "scriptmaster.h"
#include "scriptslave.h"
#include "sentient.h"
#include "item.h"
#include "gibs.h"
#include "explosion.h"
#include "equipment.h"
#include <qcommon/gameplaymanager.h>

/*****************************************************************************/
/*QUAKED script_object (0 0.5 1) ? NOT_SOLID

******************************************************************************/

Event EV_ScriptSlave_DoMove
(
	"processCommands",
	EV_CODEONLY,
	NULL,
	NULL,
	"Move the script slave."
);
Event EV_ScriptSlave_NewOrders
(
	"newOrders",
	EV_CODEONLY,
	NULL,
	NULL,
	"Inform script that it is about to get new orders."
);
Event EV_ScriptSlave_Angles
(
	"angles",
	EV_SCRIPTONLY,
	"v",
	"angles",
	"Sets the angles."
);
Event EV_ScriptSlave_Trigger
( 
	"trigger",
	EV_SCRIPTONLY,
	"s",
	"entname",
	"Trigger entities target."
);
Event EV_ScriptSlave_Next
(
	"next",
	EV_DEFAULT,
	NULL,
	NULL,
	"Goto the next waypoint."
);
Event EV_ScriptSlave_JumpTo
(
	"jumpto",
	EV_SCRIPTONLY,
	"sFF",
	"vector_or_entity token token",
	"Jump to specified vector or entity."
);
Event EV_ScriptSlave_MoveTo
(
	"moveto",
	EV_SCRIPTONLY,
	"e",
	"entity_to_move_to",
	"Move to the specified entity."
);
Event EV_ScriptSlave_MoveToPosition
(
	"movetopos",
	EV_SCRIPTONLY,
	"v",
	"position",
	"Move to the specified position."
);
Event EV_ScriptSlave_Speed
(
	"speed",
	EV_DEFAULT,
	"f",
	"speed",
	"Sets the speed."
);
Event EV_ScriptSlave_Time
(
	"time",
	EV_SCRIPTONLY,
	"f",
	"travel_time",
	"Sets the travel time."
);
Event EV_ScriptSlave_MoveUp
(
	"moveUp",
	EV_SCRIPTONLY,
	"f",
	"dist",
	"Move the position up."
);
Event EV_ScriptSlave_MoveDown
(
	"moveDown",
	EV_SCRIPTONLY,
	"f",
	"dist",
	"Move the position down."
);
Event EV_ScriptSlave_MoveNorth
(
	"moveNorth",
	EV_SCRIPTONLY,
	"f",
	"dist",
	"Move the position north."
);
Event EV_ScriptSlave_MoveSouth
(
	"moveSouth",
	EV_SCRIPTONLY,
	"f",
	"dist",
	"Move the position south."
);
Event EV_ScriptSlave_MoveEast
(
	"moveEast",
	EV_SCRIPTONLY,
	"f",
	"dist",
	"Move the position east."
);
Event EV_ScriptSlave_MoveWest
(
	"moveWest",
	EV_SCRIPTONLY,
	"f",
	"dist",
	"Move the position west."
);
Event EV_ScriptSlave_MoveForward
(
	"moveForward",
	EV_SCRIPTONLY,
	"f",
	"dist",
	"Move the position forward."
);
Event EV_ScriptSlave_MoveBackward
(
	"moveBackward",
	EV_SCRIPTONLY,
	"f",
	"dist",
	"Move the position backward."
);
Event EV_ScriptSlave_MoveLeft
(
	"moveLeft",
	EV_SCRIPTONLY,
	"f",
	"dist",
	"Move the position left."
);
Event EV_ScriptSlave_MoveRight
(
	"moveRight",
	EV_SCRIPTONLY,
	"f",
	"dist",
	"Move the position right."
);
Event EV_ScriptSlave_RotateXDownTo
(
	"rotateXdownto",
	EV_SCRIPTONLY,
	"f",
	"angle",
	"Rotate the x down to angle."
);
Event EV_ScriptSlave_RotateYDownTo
(
	"rotateYdownto",
	EV_SCRIPTONLY,
	"f",
	"angle",
	"Rotate the y down to angle."
);
Event EV_ScriptSlave_RotateZDownTo
(
	"rotateZdownto",
	EV_SCRIPTONLY,
	"f",
	"angle",
	"Rotate the z down to angle."
);
Event EV_ScriptSlave_RotateAxisDownTo
(
	"rotateaxisdownto",
	EV_SCRIPTONLY,
	"if",
	"axis angle",
	"Rotate the specified axis down to angle."
);
Event EV_ScriptSlave_RotateXUpTo
(
	"rotateXupto",
	EV_SCRIPTONLY,
	"f",
	"angle",
	"Rotate the x up to angle."
);
Event EV_ScriptSlave_RotateYUpTo
(
	"rotateYupto",
	EV_SCRIPTONLY,
	"f",
	"angle",
	"Rotate the y up to angle."
);
Event EV_ScriptSlave_RotateZUpTo
(
	"rotateZupto",
	EV_SCRIPTONLY,
	"f",
	"angle",
	"Rotate the z up to angle."
);
Event EV_ScriptSlave_RotateAxisUpTo
(
	"rotateaxisupto",
	EV_SCRIPTONLY,
	"if",
	"axis angle",
	"Rotate the specified axis up to angle."
);
Event EV_ScriptSlave_RotateXDown
(
	"rotateXdown",
	EV_SCRIPTONLY,
	"f",
	"angle",
	"Rotate the x down by the specified amount."
);
Event EV_ScriptSlave_RotateYDown
(
	"rotateYdown",
	EV_SCRIPTONLY,
	"f",
	"angle",
	"Rotate the y down by the specified amount."
);
Event EV_ScriptSlave_RotateZDown
(
	"rotateZdown",
	EV_SCRIPTONLY,
	"f",
	"angle",
	"Rotate the z down by the specified amount."
);
Event EV_ScriptSlave_RotateAxisDown
(
	"rotateaxisdown",
	EV_SCRIPTONLY,
	"if",
	"axis angle",
	"Rotate the specified axis down by the specified amount."
);
Event EV_ScriptSlave_RotateXUp
(
	"rotateXup",
	EV_SCRIPTONLY,
	"f",
	"angle",
	"Rotate the x up by the specified amount."
);
Event EV_ScriptSlave_RotateYUp
(
	"rotateYup",
	EV_SCRIPTONLY,
	"f",
	"angle",
	"Rotate the y up by the specified amount."
);
Event EV_ScriptSlave_RotateZUp
(
	"rotateZup",
	EV_SCRIPTONLY,
	"f",
	"angle",
	"Rotate the z up by the specified amount."
);
Event EV_ScriptSlave_RotateAxisUp
(
	"rotateaxisup",
	EV_SCRIPTONLY,
	"if",
	"axis angle",
	"Rotate the specified axis up by the specified amount."
);
Event EV_ScriptSlave_RotateX
(
	"rotateX",
	EV_SCRIPTONLY,
	"f",
	"avelocity",
	"Rotate about the x axis at the specified angular velocity."
);
Event EV_ScriptSlave_RotateY
(
	"rotateY",
	EV_SCRIPTONLY,
	"f",
	"avelocity",
	"Rotate about the y axis at the specified angular velocity."
);
Event EV_ScriptSlave_RotateZ
(
	"rotateZ",
	EV_SCRIPTONLY,
	"f",
	"avelocity",
	"Rotate about the z axis at the specified angular velocity."
);
Event EV_ScriptSlave_RotateAxis
(
	"rotateaxis",
	EV_SCRIPTONLY,
	"if",
	"axis avelocity",
	"Rotate about the specified axis at the specified angular velocity."
);
Event EV_ScriptSlave_RotateDownTo
(
	"rotatedownto",
	EV_SCRIPTONLY,
	"v",
	"direction",
	"Rotate down to the specified direction."
);
Event EV_ScriptSlave_RotateUpTo
(
	"rotateupto",
	EV_SCRIPTONLY,
	"v",
	"direction",
	"Rotate up to the specified direction."
);
Event EV_ScriptSlave_RotateTo
(
	"rotateto",
	EV_SCRIPTONLY,
	"v",
	"direction",
	"Rotate to the specified direction."
);
Event EV_ScriptSlave_OnTouch
(
	"ontouch",
	EV_SCRIPTONLY,
	"s",
	"label",
	"Sets what label to jump to and process script at when touched."
);
Event EV_ScriptSlave_NoTouch
(
	"notouch",
	EV_SCRIPTONLY,
	NULL,
	NULL,
	"Removes the ontouch thread."
);
Event EV_ScriptSlave_OnUse
(
	"onuse",
	EV_SCRIPTONLY,
	"s",
	"label",
	"Sets what label to jump to and process script at when used."
);
Event EV_ScriptSlave_NoUse
(
	"nouse",
	EV_SCRIPTONLY,
	NULL,
	NULL,
	"Removes the onuse thread."
);
Event EV_ScriptSlave_OnBlock
(
	"onblock",
	EV_SCRIPTONLY,
	"s",
	"label",
	"Sets what label to jump to and process script at when blocked."
);
Event EV_ScriptSlave_NoBlock
(
	"noblock",
	EV_SCRIPTONLY,
	NULL,
	NULL,
	"Removes the onblock thread."
);
Event EV_ScriptSlave_OnTrigger
(
	"ontrigger",
	EV_SCRIPTONLY,
	"s",
	"label",
	"Sets what label to jump to and process script at when triggered."
);
Event EV_ScriptSlave_NoTrigger
(
	"notrigger",
	EV_SCRIPTONLY,
	NULL,
	NULL,
	"Removes the ontrigger thread."
);
Event EV_ScriptSlave_OnDamage
(
	"ondamage",
	EV_SCRIPTONLY,
	"s",
	"label",
	"Sets what label to jump to and process script at when damaged."
);
Event EV_ScriptSlave_NoDamage
(
	"nodamage",
	EV_DEFAULT,
	NULL,
	NULL,
	"Removes the ondamage thread."
);
Event EV_ScriptSlave_SetMeansOfDeath
(
	"setmeansofdeath",
	EV_SCRIPTONLY,
	"s",
	"means_of_death",
	"Set the damage means of death."
);
Event EV_ScriptSlave_SetDamageSpawn
(
	"dmg",
	EV_SCRIPTONLY,
	"f",
	"damage",
	"Set the damage."
);
Event EV_ScriptSlave_FollowPath
(
	"followpath",
	EV_SCRIPTONLY,
	"eSSSSSS",
	"path arg1 arg2 arg3 arg4 arg5 arg6",
	"Makes the script slave follow the specified path.  The allowable arguments are ignoreangles,\n"
	"normalangles, loop, and a number specifying the start time."
);
Event EV_ScriptSlave_EndPath
(
	"endpath",
	EV_SCRIPTONLY,
	NULL,
	NULL,
	"Stop following the path"
);
Event EV_ScriptSlave_MoveDone
(
	"scriptslave_movedone",
	EV_CODEONLY,
	NULL,
	NULL,
	"Called when the script slave is doen moving"
);
Event EV_ScriptSlave_FollowingPath
(
	"scriptslave_followingpath",
	EV_CODEONLY,
	NULL,
	NULL,
	"Called every frame to actually follow the path"
);
Event EV_ScriptSlave_Explode
(
	"scriptSlave_explode",
	EV_SCRIPTONLY,
	"f",
	"damage",
	"Creates an explosion at the script slave's position"
);
Event EV_ScriptSlave_NotShootable
(
	"notshootable",
	EV_SCRIPTONLY,
	NULL,
	NULL,
	"Makes the script slave not shootable"
);
Event EV_ScriptSlave_OpenAreaPortal
(
	"openportal",
	EV_SCRIPTONLY,
	NULL,
	NULL,
	"Open the area portal enclosed in this object"
);
Event EV_ScriptSlave_CloseAreaPortal
(
	"closeportal",
	EV_SCRIPTONLY,
	NULL,
	NULL,
	"Close the area portal enclosed in this object"
);
Event EV_ScriptSlave_PhysicsOn
(
	"physics_on",
	EV_SCRIPTONLY,
	NULL,
	NULL,
	"Turn physics on this script object on"
);
Event EV_ScriptSlave_PhysicsOff
(
	"physics_off",
	EV_SCRIPTONLY,
	NULL,
	NULL,
	"Turn physics off this script object on"
);
Event EV_ScriptSlave_PhysicsVelocity
(
	"physics_velocity",
	EV_SCRIPTONLY,
	"v",
	"impulseVector",
	"Add a physical impulse to an object when it is being physically simulated"
);
Event EV_ScriptSlave_StopEvent
(
	"stopspline",
	EV_SCRIPTONLY,
	NULL,
	NULL,
	"stops an scriptobject from moving on a spline"
);
Event EV_ScriptSlave_ContinueEvent
(
	"continuespline",
	EV_SCRIPTONLY,
	NULL,
	NULL,
	"makes a script object continue on a spline"
);

// 1ST PLAYABLE HACK
Event EV_ScriptSlave_Hack_SetTriggerParms
(
	"setobjectparms",
	EV_DEFAULT,
	"ff",
	"force_field_number trigger_number",
	"HACK HACK HACK HACK HACK HACK HACK"
);
Event EV_ScriptSlave_Hack_GetForceFieldNumber
(
	"getforcefieldnumber",
	EV_DEFAULT,
	"@f",
	"number",
	"HACK HACK HACK HACK HACK HACK HACK"
);
Event EV_ScriptSlave_Hack_GetTriggerNumber
(
	"gettriggernumber",
	EV_DEFAULT,
	"@f",
	"number",
	"HACK HACK HACK HACK HACK HACK HACK"
);
Event EV_ScriptSlave_Hack_GetScanner
(
	"getscanner",
	EV_DEFAULT,
	"@e",
	"scanner",
	"HACK HACK HACK HACK HACK HACK HACK"
);

Event EV_ScriptSlave_HandlesDamage
(
	"handlesdamage",
	EV_DEFAULT,
	"b",
	"damage_flag",
	"sets the handlesdamage flag on the script slave"
);
Event EV_ScriptSlave_DamageEffect
(
	"damageEffect",
	EV_DEFAULT,
	"s",
	"damageEffectModel",
	"Sets the damage effect model name."
);

Event EV_ScriptSlave_BloodModel
(
	"setBloodModel",
	EV_DEFAULT,
	"s",
	"bloodmodel",
	"Sets the blood model"
);

Event EV_ScriptSlave_AddRequiredDamageMOD
(
	"addrequireddamagemod",
	EV_DEFAULT,
	"s",
	"MOD_String",
	"Adds the required MOD for damage to be applied"
);

Event EV_ScriptSlave_SetCanBeAttackedByOtherScriptObjects
(
	"allowAttackFromOtherScriptObjects",
	EV_DEFAULT,
	"b",
	"allow_flag",
	"Sets the _canBeAttackedByOtherScriptSlaves variable"
);

CLASS_DECLARATION( Trigger, ScriptSlave, "script_object" )
{
	{ &EV_Bind,									&ScriptSlave::BindEvent },
	{ &EV_Unbind,								&ScriptSlave::EventUnbind },
	{ &EV_ScriptSlave_DoMove,					&ScriptSlave::DoMove },
	{ &EV_ScriptSlave_NewOrders,				&ScriptSlave::NewOrders },
	{ &EV_ScriptSlave_Angles,					&ScriptSlave::SetAnglesEvent },
	{ &EV_SetAngle,								&ScriptSlave::SetAngleEvent },
	{ &EV_Model,								&ScriptSlave::SetModelEvent },
	{ &EV_ScriptSlave_Trigger,					&ScriptSlave::TriggerEvent },
	{ &EV_ScriptSlave_Next,						&ScriptSlave::GotoNextWaypoint },
	{ &EV_ScriptSlave_JumpTo,					&ScriptSlave::JumpTo },
	{ &EV_ScriptSlave_MoveTo,					&ScriptSlave::MoveToEvent },
	{ &EV_ScriptSlave_MoveToPosition,			&ScriptSlave::MoveToPositionEvent },
	{ &EV_ScriptSlave_Speed,					&ScriptSlave::SetSpeed },
	{ &EV_ScriptSlave_Time,						&ScriptSlave::SetTime },
	{ &EV_ScriptSlave_MoveUp,					&ScriptSlave::MoveUp },
	{ &EV_ScriptSlave_MoveDown,					&ScriptSlave::MoveDown },
	{ &EV_ScriptSlave_MoveNorth,				&ScriptSlave::MoveNorth },
	{ &EV_ScriptSlave_MoveSouth,				&ScriptSlave::MoveSouth },
	{ &EV_ScriptSlave_MoveEast,					&ScriptSlave::MoveEast },
	{ &EV_ScriptSlave_MoveWest,					&ScriptSlave::MoveWest },
	{ &EV_ScriptSlave_MoveForward,				&ScriptSlave::MoveForward },
	{ &EV_ScriptSlave_MoveBackward,				&ScriptSlave::MoveBackward },
	{ &EV_ScriptSlave_MoveLeft,					&ScriptSlave::MoveLeft },
	{ &EV_ScriptSlave_MoveRight,				&ScriptSlave::MoveRight },
	{ &EV_ScriptSlave_RotateXDownTo,			&ScriptSlave::RotateXdownto },
	{ &EV_ScriptSlave_RotateYDownTo,			&ScriptSlave::RotateYdownto },
	{ &EV_ScriptSlave_RotateZDownTo,			&ScriptSlave::RotateZdownto },
	{ &EV_ScriptSlave_RotateXUpTo,				&ScriptSlave::RotateXupto },
	{ &EV_ScriptSlave_RotateYUpTo,				&ScriptSlave::RotateYupto },
	{ &EV_ScriptSlave_RotateZUpTo,				&ScriptSlave::RotateZupto },
	{ &EV_ScriptSlave_RotateXDown,				&ScriptSlave::RotateXdown },
	{ &EV_ScriptSlave_RotateYDown,				&ScriptSlave::RotateYdown },
	{ &EV_ScriptSlave_RotateZDown,				&ScriptSlave::RotateZdown },
	{ &EV_ScriptSlave_RotateXUp,				&ScriptSlave::RotateXup },
	{ &EV_ScriptSlave_RotateYUp,				&ScriptSlave::RotateYup },
	{ &EV_ScriptSlave_RotateZUp,				&ScriptSlave::RotateZup },
	{ &EV_ScriptSlave_RotateX,					&ScriptSlave::RotateX },
	{ &EV_ScriptSlave_RotateY,					&ScriptSlave::RotateY },
	{ &EV_ScriptSlave_RotateZ,					&ScriptSlave::RotateZ },
	{ &EV_ScriptSlave_RotateAxisDownTo,			&ScriptSlave::RotateAxisdownto },
	{ &EV_ScriptSlave_RotateAxisUpTo,			&ScriptSlave::RotateAxisupto },
	{ &EV_ScriptSlave_RotateAxisDown,			&ScriptSlave::RotateAxisdown },
	{ &EV_ScriptSlave_RotateAxisUp,				&ScriptSlave::RotateAxisup },
	{ &EV_ScriptSlave_RotateAxis,				&ScriptSlave::RotateZ },
	{ &EV_ScriptSlave_OnTouch,					&ScriptSlave::OnTouch },
	{ &EV_ScriptSlave_NoTouch,					&ScriptSlave::NoTouch },
	{ &EV_ScriptSlave_OnUse,					&ScriptSlave::OnUse },
	{ &EV_ScriptSlave_NoUse,					&ScriptSlave::NoUse },
	{ &EV_ScriptSlave_OnBlock,					&ScriptSlave::OnBlock },
	{ &EV_ScriptSlave_NoBlock,					&ScriptSlave::NoBlock },
	{ &EV_ScriptSlave_OnTrigger,				&ScriptSlave::OnTrigger },
	{ &EV_ScriptSlave_NoTrigger,				&ScriptSlave::NoTrigger },
	{ &EV_ScriptSlave_OnDamage,					&ScriptSlave::OnDamage },
	{ &EV_ScriptSlave_NoDamage,					&ScriptSlave::NoDamage },
	{ &EV_ScriptSlave_SetMeansOfDeath,			&ScriptSlave::SetMeansOfDeath },
	{ &EV_ScriptSlave_SetDamageSpawn,			&ScriptSlave::SetDamage },
	{ &EV_ScriptSlave_FollowPath,				&ScriptSlave::FollowPath },
	{ &EV_ScriptSlave_EndPath,					&ScriptSlave::EndPath },
	{ &EV_ScriptSlave_FollowingPath,			&ScriptSlave::FollowingPath },
	{ &EV_Touch,								&ScriptSlave::TouchFunc },
	{ &EV_Blocked,								&ScriptSlave::BlockFunc },
	{ &EV_Activate,								&ScriptSlave::TriggerFunc },
	{ &EV_Use,									&ScriptSlave::UseFunc },
	{ &EV_ScriptSlave_MoveDone,					&ScriptSlave::MoveEnd },
	{ &EV_Damage,								&ScriptSlave::DamageFunc },
	{ &EV_ScriptSlave_RotateDownTo,				&ScriptSlave::Rotatedownto },
	{ &EV_ScriptSlave_RotateUpTo,				&ScriptSlave::Rotateupto },
	{ &EV_ScriptSlave_RotateTo,					&ScriptSlave::Rotateto },
	{ &EV_ScriptSlave_Explode,					&ScriptSlave::Explode },
	{ &EV_ScriptSlave_NotShootable,				&ScriptSlave::NotShootable },
	{ &EV_ScriptSlave_OpenAreaPortal,			&ScriptSlave::OpenPortal },
	{ &EV_ScriptSlave_CloseAreaPortal,			&ScriptSlave::ClosePortal },
	{ &EV_ScriptSlave_PhysicsOn,				&ScriptSlave::PhysicsOn },
	{ &EV_ScriptSlave_PhysicsOff,				&ScriptSlave::PhysicsOff },
	{ &EV_ScriptSlave_PhysicsVelocity,			&ScriptSlave::PhysicsVelocity },
	{ &EV_ScriptSlave_StopEvent,				&ScriptSlave::StopEvent },
	{ &EV_ScriptSlave_ContinueEvent,			&ScriptSlave::ContinueEvent },
	{ &EV_SetGameplayDamage,					&ScriptSlave::setDamage },
	{ &EV_ScriptSlave_HandlesDamage,			&ScriptSlave::HandlesDamage },
	{ &EV_ScriptSlave_DamageEffect,				&ScriptSlave::setDamageEffect },
	{ &EV_ScriptSlave_BloodModel,				&ScriptSlave::setBloodModel },
	{ &EV_ScriptSlave_AddRequiredDamageMOD,		&ScriptSlave::addRequiredDamageMOD },
	{ &EV_ScriptSlave_SetCanBeAttackedByOtherScriptObjects, &ScriptSlave::setCanBeAttackedByOtherSlaves },

	//1ST PLAYABLE HACK STUFF
	{ &EV_ScriptSlave_Hack_SetTriggerParms,     &ScriptSlave::Hack_AddParms             },
	{ &EV_ScriptSlave_Hack_GetForceFieldNumber, &ScriptSlave::Hack_GetForceFieldNumber  },
	{ &EV_ScriptSlave_Hack_GetTriggerNumber,    &ScriptSlave::Hack_GetTriggerNumber     },
	{ &EV_ScriptSlave_Hack_GetScanner,          &ScriptSlave::Hack_GetScanner           },

	{ NULL, NULL }
};

ScriptSlave::ScriptSlave()
{
	mover = new Mover( this );
	
	if ( LoadingSavegame )
	{
		// Archive function will setup all necessary data
		return;
	}
	// this is a normal entity
	edict->s.eType = ET_GENERAL;
	
	speed             = 100;
	takedamage			= DAMAGE_YES;
	waypoint				= NULL;
	SetNewAngles( localangles );
	SetNewPosition( GetLocalOrigin() );
	traveltime			= 0;
	commandswaiting	= false;
	movethread			= NULL;
	touchthread			= NULL;
	blockthread			= NULL;
	damagethread		= NULL;
	triggerthread		= NULL;
	usethread			= NULL;
	splinePath        = NULL;
	splineangles      = false;
	attack_finished   = 0;
	thinking          = true;
	
	dmg					 = 2;
	dmg_means_of_death = MOD_CRUSH;
	
	setMoveType( MOVETYPE_PUSH );
	setSolidType( SOLID_NOT );
	
	//1ST PLAYABLE HACK
	_forcefieldNumber  = -1.0f;
	_triggerNumber     = -1.0f;
	_scanner = NULL;
	
	_portalOpen = false;
	_handlesDamage = false;

	_nextNeedToUseTime = 0.0f;
	_canBeAttackedByOtherScriptSlaves = true;
	
	if ( spawnflags & 1 )
	{
		PostEvent( EV_BecomeNonSolid, EV_POSTSPAWN );
	}
}

ScriptSlave::~ScriptSlave()
{
	if ( splinePath )
	{
		delete splinePath;
		splinePath = NULL;
	}
}

//--------------------------------------------------------------
//
// Name:			setDamage
// Class:			ScriptSlave
//
// Description:		This function acts as a filter to the real function.
//					It gets data from the database, and then passes it
//					along to the original event.  This is here as an attempt
//					to sway people into using the database standard instead of
//					hardcoded numbers.
//
// Parameters:		Event *ev
//						str -- The value keyword from the database (low, medium, high, etc).
//
// Returns:			None
//
//--------------------------------------------------------------
void ScriptSlave::setDamage( Event *ev )
{
	if ( ev->NumArgs() < 1 )
		return;

	GameplayManager *gpm = GameplayManager::getTheGameplayManager();	
	if ( !gpm->hasFormula("OffensiveDamage") )
		return;

	str damagestr = ev->GetString( 1 );
	float damagemod = 1.0f;
	if ( gpm->getDefine(damagestr) != "" )
		damagemod = (float)atof(gpm->getDefine(damagestr));
	GameplayFormulaData fd(this, 0, 0, "");
	float finaldamage = gpm->calculate("OffensiveDamage", fd, damagemod);
	Event *newev = new Event(EV_ScriptSlave_SetDamageSpawn);
	newev->AddFloat(finaldamage);
	ProcessEvent(newev);
}

void ScriptSlave::setOrigin( const Vector &point )
{
	Trigger::setOrigin( point );
	SetNewPosition( GetLocalOrigin() );
}

void ScriptSlave::setOrigin( void )
{
	Trigger::setOrigin();
	SetNewPosition( GetLocalOrigin() );
}

void ScriptSlave::NewOrders( Event *ev )
{
	// make sure position and angles are current
	SetNewAngles( localangles );
	SetNewPosition( GetLocalOrigin() );
}

void ScriptSlave::BindEvent( Event *ev )
{
	Entity *ent;
	
	ent = ev->GetEntity( 1 );
	if ( ent )
	{
		bind( ent );
	}
	
	// make sure position and angles are current
	SetNewAngles( localangles );
	SetNewPosition( GetLocalOrigin() );
}

void ScriptSlave::EventUnbind( Event *ev )
{
	unbind();
	
	// make sure position and angles are current
	SetNewAngles( localangles );
	SetNewPosition( GetLocalOrigin() );
}

void ScriptSlave::DoMove( Event *ev )
{
	float dist;
	CThread *thread;
	Event *event;
	
	thread = ev->GetThread();
	assert( thread );
	if ( thread && thread->WaitingFor( this ) )
	{
		if ( movethread && ( movethread != thread ) )
		{
			// warn the user
			ev->Error( "Overriding previous move commands for '%s'\n", TargetName() );
			
			// Yeah, we're not REALLY done, but we tell our old thread
			// that we are so that it doesn't wait forever
			event = new Event( EV_MoveDone );
			event->AddEntity( this );
			movethread->ProcessEvent( event );
		}
		
		movethread = thread;
	}
	if ( commandswaiting )
	{
		if ( splinePath )
		{
			moving = true;
			PostEvent( EV_ScriptSlave_FollowingPath, 0.0f );
		}
		else
		{
			float t = traveltime;
			if ( t == 0.0f )
			{
				dist = Vector( GetNewPosition() - GetLocalOrigin() ).length();
				t = dist / speed;
			}
			moving = true;
			mover->LinearInterpolate( GetNewPosition(), GetNewAngles(), t, EV_ScriptSlave_MoveDone );
		}
		commandswaiting = false;
	}
	else if ( movethread && ( movethread == thread ) && !moving )
	{
		// No commands, so tell the master that we're done
		PostEvent( EV_ScriptSlave_MoveDone, 0.0f );
	}
}



void ScriptSlave::MoveEnd( Event *ev )
{
	Event *event;
	
	moving = false;
	commandswaiting = false;
	SetNewAngles( localangles );
	SetNewPosition( GetLocalOrigin() );
	
	if ( movethread )
	{
		event = new Event( EV_MoveDone );
		event->AddEntity( this );
		movethread->ProcessEvent( event );
		movethread = NULL;
	}
}

void ScriptSlave::SetAnglesEvent( Event *ev )
{
	commandswaiting = true;
	setAngles( ev->GetVector( 1 ) );
	SetNewAngles( localangles );
}

void ScriptSlave::SetAngleEvent( Event *ev )
{
	float angle;
	
	angle = ev->GetFloat( 1 );
	if ( angle == -1.0f )
	{
		ForwardDir = Vector( 0.0f, 0.0f, 90.0f );
	}
	else if ( angle == -2.0f )
	{
		ForwardDir = Vector( 0.0f, 0.0f, -90.0f );
	}
	else
	{
		ForwardDir = Vector( 0.0f, angle, 0.0f );
	}
}

void ScriptSlave::SetModelEvent( Event *ev )
{
	const char *m;
	
	m = ev->GetString( 1 );
	
	setModel( m );
	showModel();
	
	if ( !edict->s.modelindex )
	{
		hideModel();
		setSolidType( SOLID_NOT );
	}
	else if ( !m || strstr( m, ".tik" ) )
	{
		setSolidType( SOLID_BBOX );
	}
	else if ( strstr( m, ".spr" ) )
	{
		setSolidType( SOLID_NOT );
	}
	else
	{
		setSolidType( SOLID_BSP );
	}
}

void ScriptSlave::TriggerEvent( Event *ev )
{
	Entity	*ent;
	Event		*e;
	str		name;
	
	name = ev->GetString(1);
	ent = G_FindTarget(NULL, name);
	
	if ( ent )
	{
		SetTarget( ent->TargetName() );
		
		e = new Event( EV_Trigger_ActivateTargets );
		//fixme
		//get "other"
		e->AddEntity( world );
		ProcessEvent( e );
	}
	else
	{
		gi.WDPrintf( "Invalid entity reference '%s'.\n", name.c_str() );
	}
}

void ScriptSlave::GotoNextWaypoint( Event *ev )
{
	commandswaiting = true;
	
	if ( !waypoint )
	{
		ev->Error( "%s is currently not at a waypoint", TargetName() );
		return;
	}
	
	waypoint = ( Waypoint * )G_FindTarget( NULL, waypoint->Target() );
	if ( !waypoint )
	{
		ev->Error( "%s could not find waypoint %s", TargetName(), waypoint->Target() );
		return;
	}
	else
	{
		SetNewPosition( waypoint->origin );
	}
}

void ScriptSlave::JumpTo( Event *ev )
{
	Entity *part;
	
	//
	// see if it is a vector
	//
	if ( ev->IsVectorAt( 1 ) )
	{
		SetNewPosition( ev->GetVector( 1 ) );
		if ( bind_info && bind_info->bindmaster )
		{
			SetLocalOrigin( bind_info->bindmaster->getLocalVector( GetNewPosition() - bind_info->bindmaster->origin ) );
		}
		else
		{
			SetLocalOrigin( GetNewPosition() );
		}
		
		part = this;
		
		while( part )
		{
			part->setOrigin();
			part->origin.copyTo( part->edict->s.origin2 );
			part->edict->s.renderfx |= RF_FRAMELERP;
			
			if ( part->bind_info )
				part = part->bind_info->teamchain;
			else
				part = NULL;
		}
	}
	else
	{
		waypoint = ( Waypoint * )ev->GetEntity( 1 );
		if ( waypoint )
		{
			SetNewPosition( waypoint->GetLocalOrigin() );
			if ( bind_info && bind_info->bindmaster )
			{
				SetLocalOrigin( bind_info->bindmaster->getLocalVector( GetNewPosition() - bind_info->bindmaster->origin ) );
			}
			else
			{
				SetLocalOrigin( GetNewPosition() );
			}
			
			part = this;
			
			while( part )
			{
				part->setOrigin();
				part->origin.copyTo( part->edict->s.origin2 );
				part->edict->s.renderfx |= RF_FRAMELERP;
				
				if ( part->bind_info )
					part = part->bind_info->teamchain;
				else
					part = NULL;
			}
		}
	}
}

void ScriptSlave::MoveToEvent( Event *ev )
{
	commandswaiting = true;
	
	waypoint = ( Waypoint * )ev->GetEntity( 1 );
	
	if ( waypoint )
	{
		SetNewPosition( waypoint->origin );
	}
}

void ScriptSlave::MoveToPositionEvent( Event *ev )
{
	commandswaiting = true;
	
	SetNewPosition( ev->GetVector( 1 ) );
}

void ScriptSlave::SetSpeed( Event *ev )
{
	speed = ev->GetFloat( 1 );
	traveltime = 0;
}

void ScriptSlave::SetTime( Event *ev )
{
	traveltime = ev->GetFloat( 1 );
}

// Relative move commands

void ScriptSlave::MoveUp( Event *ev )
{
	commandswaiting = true;
	SetNewPosition( Vector( GetNewPosition().x, GetNewPosition().y, GetNewPosition().z + ev->GetFloat( 1 ) ) );
}

void ScriptSlave::MoveDown( Event *ev )
{
	commandswaiting = true;
	SetNewPosition( Vector( GetNewPosition().x, GetNewPosition().y, GetNewPosition().z - ev->GetFloat( 1 ) ) );
}

void ScriptSlave::MoveNorth( Event *ev )
{
	commandswaiting = true;
	SetNewPosition( Vector( GetNewPosition().x, GetNewPosition().y + ev->GetFloat( 1 ), GetNewPosition().z ) );
}

void ScriptSlave::MoveSouth( Event *ev )
{
	commandswaiting = true;
	SetNewPosition( Vector( GetNewPosition().x, GetNewPosition().y - ev->GetFloat( 1 ), GetNewPosition().z ) );
}

void ScriptSlave::MoveEast( Event *ev )
{
	commandswaiting = true;
	SetNewPosition( Vector( GetNewPosition().x + ev->GetFloat( 1 ), GetNewPosition().y, GetNewPosition().z ) );
}

void ScriptSlave::MoveWest( Event *ev )
{
	commandswaiting = true;
	SetNewPosition( Vector( GetNewPosition().x - ev->GetFloat( 1 ), GetNewPosition().y, GetNewPosition().z ) );
}

void ScriptSlave::MoveForward( Event *ev )
{
	Vector v;
	Vector t;
	
	commandswaiting = true;
	
	t = GetNewAngles() + ForwardDir;
	t.AngleVectors( &v, NULL, NULL );
	
	SetNewPosition( GetNewPosition() + v * ev->GetFloat( 1 ) );
}

void ScriptSlave::MoveBackward( Event *ev )
{
	Vector v;
	Vector t;
	
	commandswaiting = true;
	
	t = GetNewAngles() + ForwardDir;
	t.AngleVectors( &v, NULL, NULL );
	
	SetNewPosition( GetNewPosition() - v * ev->GetFloat( 1 ) );
}

void ScriptSlave::MoveLeft( Event *ev )
{
	Vector v;
	Vector t;
	
	commandswaiting = true;
	
	t = GetNewAngles() + ForwardDir;
	t.AngleVectors( NULL, &v, NULL );
	
	SetNewPosition( GetNewPosition() + v * ev->GetFloat( 1 ) );
}

void ScriptSlave::MoveRight( Event *ev )
{
	Vector t;
	Vector v;
	
	commandswaiting = true;
	
	t = GetNewAngles() + ForwardDir;
	t.AngleVectors( NULL, &v, NULL );
	
	SetNewPosition( GetNewPosition() - v * ev->GetFloat( 1 ) );
}

// exact rotate commands

void ScriptSlave::RotateXdownto( Event *ev )
{
	commandswaiting = true;
	
	Vector newAngles( GetNewAngles() );
	newAngles[ 0 ] = ev->GetFloat( 1 );
	if ( newAngles[ 0 ] < localangles[ 0 ] )
	{
		newAngles[ 0 ] -= 360.0f;
	}
	SetNewAngles( newAngles );
}

void ScriptSlave::RotateYdownto( Event *ev )
{
	commandswaiting = true;
	
	Vector newAngles( GetNewAngles() );
	newAngles[ 1 ] = ev->GetFloat( 1 );
	if ( newAngles[ 1 ] < localangles[ 1 ] )
	{
		newAngles[ 1 ] -= 360.0f;
	}
	SetNewAngles( newAngles );
}

void ScriptSlave::RotateZdownto( Event *ev )
{
	commandswaiting = true;
	
	Vector newAngles( GetNewAngles() );
	newAngles[ 2 ] = ev->GetFloat( 1 );
	if ( newAngles[ 2 ] < localangles[ 2 ] )
	{
		newAngles[ 2 ] -= 360.0f;
	}
	SetNewAngles( newAngles );
}

void ScriptSlave::RotateAxisdownto( Event *ev )
{
	int axis;
	commandswaiting = true;
	
	axis = ev->GetInteger( 1 );
	
	Vector newAngles( GetNewAngles() );
	newAngles[ axis ] = ev->GetFloat( 1 );
	if ( newAngles[ axis ] < localangles[ axis ] )
	{
		newAngles[ axis ] -= 360.0f;
	}
	SetNewAngles( newAngles );
}

void ScriptSlave::RotateXupto( Event *ev )
{
	commandswaiting = true;
	Vector newAngles( GetNewAngles() );
	newAngles[ 0 ] = ev->GetFloat( 1 );
	if ( newAngles[ 0 ] < localangles[ 0 ] )
	{
		newAngles[ 0 ] += 360.0f;
	}
	SetNewAngles( newAngles );
}

void ScriptSlave::RotateYupto( Event *ev )
{
	commandswaiting = true;
	
	Vector newAngles( GetNewAngles() );
	newAngles[ 1 ] = ev->GetFloat( 1 );
	if ( newAngles[ 1 ] < localangles[ 1 ] )
	{
		newAngles[ 1 ] += 360.0f;
	}
	SetNewAngles( newAngles );
}

void ScriptSlave::RotateZupto( Event *ev )
{
	commandswaiting = true;
	
	Vector newAngles( GetNewAngles() );
	newAngles[ 2 ] = ev->GetFloat( 1 );
	if ( newAngles[ 2 ] < localangles[ 2 ] )
	{
		newAngles[ 2 ] += 360.0f;
	}
	SetNewAngles( newAngles );
}

void ScriptSlave::RotateAxisupto( Event *ev )
{
	int axis;
	commandswaiting = true;
	
	axis = ev->GetInteger( 1 );
	Vector newAngles( GetNewAngles() );
	newAngles[ axis ] = ev->GetFloat( 1 );
	if ( newAngles[ axis ] < localangles[ axis ] )
	{
		newAngles[ axis ] += 360.0f;
	}
	SetNewAngles( newAngles );
}

// full vector rotation

void ScriptSlave::Rotatedownto( Event *ev )
{
	Vector ang;
	commandswaiting = true;
	
	ang = ev->GetVector( 1 );
	
	Vector newAngles( GetNewAngles() );
	newAngles[ 0 ] = ang[ 0 ];
	if ( newAngles[ 0 ] > localangles[ 0 ] )
	{
		newAngles[ 0 ] -= 360.0f;
	}
	newAngles[ 1 ] = ang[ 1 ];
	if ( newAngles[ 1 ] > localangles[ 1 ] )
	{
		newAngles[ 1 ] -= 360.0f;
	}
	newAngles[ 2 ] = ang[ 2 ];
	if ( newAngles[ 2 ] > localangles[ 2 ] )
	{
		newAngles[ 2 ] -= 360.0f;
	}
	SetNewAngles( newAngles );
}

void ScriptSlave::Rotateupto( Event *ev )
{
	Vector ang;
	commandswaiting = true;
	
	ang = ev->GetVector( 1 );
	
	Vector newAngles( GetNewAngles() );
	newAngles[ 0 ] = ang[ 0 ];
	if ( newAngles[ 0 ] < localangles[ 0 ] )
	{
		newAngles[ 0 ] += 360.0f;
	}
	newAngles[ 1 ] = ang[ 1 ];
	if ( newAngles[ 1 ] < localangles[ 1 ] )
	{
		newAngles[ 1 ] += 360.0f;
	}
	newAngles[ 2 ] = ang[ 2 ];
	if ( newAngles[ 2 ] < localangles[ 2 ] )
	{
		newAngles[ 2 ] += 360.0f;
	}
	SetNewAngles( newAngles );
}

void ScriptSlave::Rotateto( Event *ev )
{
	commandswaiting = true;
	
	Vector ang = ev->GetVector( 1 );
	
	SetNewAngles( ang );
}

// Relative rotate commands

void ScriptSlave::RotateXdown( Event *ev )
{
	commandswaiting = true;
	Vector newAngles( GetNewAngles() );
	newAngles[ 0 ] = localangles[ 0 ] - ev->GetFloat( 1 );
	SetNewAngles( newAngles );
}

void ScriptSlave::RotateYdown( Event *ev )
{
	commandswaiting = true;
	Vector newAngles( GetNewAngles() );
	newAngles[ 1 ] = localangles[ 1 ] - ev->GetFloat( 1 );
	SetNewAngles( newAngles );
}

void ScriptSlave::RotateZdown( Event *ev )
{
	commandswaiting = true;
	Vector newAngles( GetNewAngles() );
	newAngles[ 2 ] = localangles[ 2 ] - ev->GetFloat( 1 );
	SetNewAngles( newAngles );
}

void ScriptSlave::RotateAxisdown( Event *ev )
{
	int axis;
	commandswaiting = true;
	
	axis = ev->GetInteger( 1 );
	Vector newAngles( GetNewAngles() );
	newAngles[ axis ] = localangles[ axis ] - ev->GetFloat( 1 );
	SetNewAngles( newAngles );
}

void ScriptSlave::RotateXup( Event *ev )
{
	commandswaiting = true;
	Vector newAngles( GetNewAngles() );
	newAngles[ 0 ] = localangles[ 0 ] + ev->GetFloat( 1 );
	SetNewAngles( newAngles );
}

void ScriptSlave::RotateYup( Event *ev )
{
	commandswaiting = true;
	Vector newAngles( GetNewAngles() );
	newAngles[ 1 ] = localangles[ 1 ] + ev->GetFloat( 1 );
	SetNewAngles( newAngles );
}

void ScriptSlave::RotateZup( Event *ev )
{
	commandswaiting = true;
	Vector newAngles( GetNewAngles() );
	newAngles[ 2 ] = localangles[ 2 ] + ev->GetFloat( 1 );
	SetNewAngles( newAngles );
}

void ScriptSlave::RotateAxisup( Event *ev )
{
	int axis;
	commandswaiting = true;
	
	axis = ev->GetInteger( 1 );
	Vector newAngles( GetNewAngles() );
	newAngles[ axis ] = localangles[ axis ] + ev->GetFloat( 1 );
	SetNewAngles( newAngles );
}

void ScriptSlave::RotateX( Event *ev )
{
	avelocity[ 0 ] = ev->GetFloat( 1 );
}

void ScriptSlave::RotateY( Event *ev )
{
	avelocity[ 1 ] = ev->GetFloat( 1 );
}

void ScriptSlave::RotateZ( Event *ev )
{
	avelocity[ 2 ] = ev->GetFloat( 1 );
}

void ScriptSlave::RotateAxis( Event *ev )
{
	int axis;
	
	axis = ev->GetInteger( 1 );
	avelocity[ axis ] = ev->GetFloat( 2 );
}


void ScriptSlave::OnTouch( Event *ev )
{
	const char *jumpto;
	
	touchlabel = "";
	
	jumpto = ev->GetString( 1 );
	touchthread = ev->GetThread();
	
	assert( jumpto && touchthread );
	if ( touchthread && !touchthread->labelExists( jumpto ) )
	{
		ev->Error( "Label '%s' not found", jumpto );
		return;
	}
	
	// if it isn't solid than we need to change it to a trigger
	if ( getSolidType() == SOLID_NOT )
	{
		setSolidType( SOLID_TRIGGER );
	}
	
	touchlabel = jumpto;
}

void ScriptSlave::NoTouch( Event *ev )
{
	touchlabel = "";
	// if it is a trigger than it wasn't solid, so restore that condition
	if ( getSolidType() == SOLID_TRIGGER )
	{
		setSolidType( SOLID_NOT );
	}
}

void ScriptSlave::TouchFunc( Event *ev )
{
	Event *e;
	Entity *other;
	
	if ( touchlabel.length() )
	{
		// since we use a SafePtr, the thread pointer will be NULL if the thread has ended
		// so we should just clear our label and continue
		if ( !touchthread )
		{
			touchlabel = "";
			return;
		}
		
		other = ev->GetEntity( 1 );
		
		setActivatingEntity( other );
		
		e = new Event( EV_ScriptThread_Callback );
		e->AddEntity( this );
		e->AddString( touchlabel );
		e->AddEntity( other );
		touchthread->ProcessEvent( e );
	}
}

void ScriptSlave::OnBlock( Event *ev )
{
	const char *jumpto;
	
	blocklabel = "";
	
	jumpto = ev->GetString( 1 );
	blockthread = ev->GetThread();
	
	assert( jumpto && blockthread );
	if ( blockthread && !blockthread->labelExists( jumpto ) )
	{
		ev->Error( "Label '%s' not found", jumpto );
		return;
	}
	
	blocklabel = jumpto;
}

void ScriptSlave::NoBlock( Event *ev )
{
	blocklabel = "";
}

void ScriptSlave::BlockFunc( Event *ev )
{
	Event *e;
	Entity *other;
	
	other = ev->GetEntity( 1 );
	if ( level.time >= attack_finished )
	{
		attack_finished = level.time + ( float )0.5;
		if ( dmg != 0 )
		{
			other->Damage( this, this, dmg, origin, vec_zero, vec_zero, 0, 0, dmg_means_of_death );
		}
	}
	
	if ( blocklabel.length() )
	{
		// since we use a SafePtr, the thread pointer will be NULL if the thread has ended
		// so we should just clear our label and continue
		if ( !blockthread )
		{
			blocklabel = "";
			return;
		}
		
		setActivatingEntity( other );
		
		e = new Event( EV_ScriptThread_Callback );
		e->AddEntity( this );
		e->AddString( blocklabel );
		e->AddEntity( other );
		blockthread->ProcessEvent( e );
	}
}

void ScriptSlave::OnTrigger( Event *ev )
{
	const char *jumpto;
	
	triggerlabel = "";
	
	jumpto = ev->GetString( 1 );
	triggerthread = ev->GetThread();
	
	assert( jumpto && triggerthread );
	
	if ( triggerthread && !triggerthread->labelExists( jumpto ) )
	{
		ev->Error( "Label '%s' not found", jumpto );
		return;
	}
	
	triggerlabel = jumpto;
}

void ScriptSlave::NoTrigger( Event *ev )
{
	triggerlabel = "";
}

void ScriptSlave::TriggerFunc( Event *ev )
{
	Event *e;
	Entity *other;
	
	if ( triggerlabel.length() )
	{
		// since we use a SafePtr, the thread pointer will be NULL if the thread has ended
		// so we should just clear our label and continue
		if ( !triggerthread )
		{
			triggerlabel = "";
			return;
		}
		
		other = ev->GetEntity( 1 );
		
		setActivatingEntity( other );
		
		e = new Event( EV_ScriptThread_Callback );
		e->AddEntity( this );
		e->AddString( triggerlabel );
		e->AddEntity( other );
		
		triggerthread->ProcessEvent( e );
	}
}

void ScriptSlave::OnUse( Event *ev )
{
	const char *jumpto;
	
	uselabel = "";
	
	jumpto = ev->GetString( 1 );
	usethread = ev->GetThread();
	
	assert( jumpto && usethread );
	
	if ( usethread && !usethread->labelExists( jumpto ) )
	{
		ev->Error( "Label '%s' not found", jumpto );
		return;
	}
	
	uselabel = jumpto;
}

void ScriptSlave::NoUse( Event *ev )
{
	uselabel = "";
}

void ScriptSlave::UseFunc( Event *ev )
{
	Event *e;
	Entity *other;
	
	other = ev->GetEntity( 1 );
	
	// See if object == key
	
	if ( other->isSubclassOf( Equipment ) )
	{
		Equipment *equipment = (Equipment *)other;
		
		if ( equipment->getTypeName() != key )
			return;
	}
	else if ( key.length() )
	{
		if ( !other->isSubclassOf( Sentient ) || !( ( (Sentient *)other )->HasItem( key.c_str() ) ) )
		{
			Item        *item;
			ClassDef		*cls;
			
			cls = getClass( key.c_str() );
			if ( !cls )
			{
				gi.WDPrintf( "No item named '%s'\n", key.c_str() );
				return;
			}
			item = ( Item * )cls->newInstance();
			item->CancelEventsOfType( EV_Item_DropToFloor );
			item->CancelEventsOfType( EV_Remove );
			item->ProcessPendingEvents();
			gi.centerprintf ( other->edict, CENTERPRINT_IMPORTANCE_NORMAL, "$$ItemNeeded$$%s", item->getName().c_str() );
			delete item;
			return;
		}
		else if ( other->isSubclassOf( Sentient ) )
		{
			Item *item = ( (Sentient *)other )->FindItem( key.c_str() );
			
			if ( !item )
				return;

			if ( item->isSubclassOf( Equipment ) )
			{
				if ( _nextNeedToUseTime < level.time )
				{
					gi.centerprintf ( other->edict, CENTERPRINT_IMPORTANCE_NORMAL, "$$NeedToUse$$ %s", key.c_str() );

					if ( other->isSubclassOf( Player ) )
					{
						Player *player = (Player *)other;

						player->loadUseItem( key );
					}

					_nextNeedToUseTime = level.time + 1.0f;
				}

				return;
			}
		}
	}
	
	if ( uselabel.length() )
	{
		//ScriptVariableList *vars;
		
		// since we use a SafePtr, the thread pointer will be NULL if the thread has ended
		// so we should just clear our label and continue
		if ( !usethread )
		{
			uselabel = "";
			return;
		}
		
		setActivatingEntity( other );
		
		e = new Event( EV_ScriptThread_Callback );
		e->AddEntity( this );
		e->AddString( uselabel );
		e->AddEntity( other );
		
		/* vars = usethread->Vars();
		vars->SetVariable( "other", other );
		if ( key.length() )
		{
		vars->SetVariable( "key", key.c_str() );
	} */
		usethread->ProcessEvent( e );
	}
}

void ScriptSlave::OnDamage( Event *ev )
{
	const char *jumpto;
	
	damagelabel = "";
	
	jumpto = ev->GetString( 1 );
	damagethread = ev->GetThread();
	
	assert( jumpto && damagethread );
	
	if ( damagethread && !damagethread->labelExists( jumpto ) )
	{
		ev->Error( "Label '%s' not found", jumpto );
		return;
	}
	
	damagelabel = jumpto;
}

void ScriptSlave::NoDamage( Event *ev )
{
	damagelabel = "";
}

void ScriptSlave::DamageFunc( Event *ev )
{
	Event			*e;
	//Entity		*inflictor;
	Entity		*attacker;
	//int			damage;
	//Vector		position;
	//Vector		direction;
	//ScriptVariableList *vars;
	
	attacker	= ev->GetEntity( 3 );

	if ( !_canBeAttackedByOtherScriptSlaves )
		{
		if ( attacker->isSubclassOf(ScriptSlave) )
			return;
		}

	if ( _handlesDamage )
	{
		Event *newDamageEvent = new Event (ev);
		Entity::DamageEvent(newDamageEvent);
	}
	
	if ( damagelabel.length() )
	{
		// since we use a SafePtr, the thread pointer will be NULL if the thread has ended
		// so we should just clear our label and continue
		if ( !damagethread )
		{
			damagelabel = "";
			return;
		}
		
		if( _requiredMODlist.NumObjects() != 0 )
		{
			bool modMatches = false;
			for( int i = 1;	!modMatches && i <= _requiredMODlist.NumObjects(); i++ )
			{
				str& modname = _requiredMODlist.ObjectAt( i );
				int modID = MOD_NameToNum( modname );
				int damageMOD = ev->GetInteger( 9 );

				if ( modID == damageMOD )
					modMatches = true;
			}

			if( !modMatches )
				return;
		}
		
		
		setActivatingEntity( attacker );
		
		e = new Event( EV_ScriptThread_Callback );
		e->AddEntity( this );
		e->AddString( damagelabel );
		e->AddEntity( attacker );
		
		/* damage		= ev->GetInteger( 1 );
		inflictor	= ev->GetEntity( 2 );
		position		= ev->GetVector( 4 );
		direction	= ev->GetVector( 5 ); */
		
		/* vars = damagethread->Vars();
		vars->SetVariable( "damage", damage );
		vars->SetVariable( "inflictor", inflictor );
		vars->SetVariable( "attacker", attacker );
		vars->SetVariable( "position", position );
		vars->SetVariable( "direction", direction ); */
		damagethread->ProcessEvent( e );
	}
	
	if ( _bloodModel.length() )
	{
		SpawnEffect( _bloodModel , ev->GetVector( 4 ) , ev->GetVector( 6 ) , 1.0f );
	}

	if ( _damageEffect.length() )
	{
		//SpawnEffect( "models/fx/fx-impactburn-sniperrifle.tik", ev->GetVector( 4 ), ev->GetVector( 6 ), 1.0f );
		SpawnEffect( "models/fx/fx-electricitymesh-impactpoint.tik", ev->GetVector( 4 ), ev->GetVector( 6 ), 1.0f );
	}
}

void ScriptSlave::SetDamage( Event *ev )
{
	dmg = ev->GetFloat( 1 );
}

void ScriptSlave::SetMeansOfDeath( Event *ev )
{
	dmg_means_of_death = MOD_NameToNum( ev->GetString( 1 ) );
}

void ScriptSlave::CreatePath( SplinePath *path, splinetype_t type )
{
	SplinePath* node;
	SplinePath* nextNode;
	
	if ( !splinePath )
	{
		splinePath = new BSpline;
	}
	
	splinePath->Clear();
	splinePath->SetType( type );
	
	node = path;
	while( node != NULL )
	{
		splinePath->AppendControlPoint( node->origin, node->angles, node->speed );
		
		// get the next node and check it before using it
		nextNode = node->GetNext();
		if( nextNode == node )
		{
			gi.Error( ERR_DROP, "info_splinepath '%s' targets itself\n", node->targetname.c_str() );
		}
		
		// don't loop
		if ( nextNode == path )
		{
			break;
		}

		// move to the next node
		node = nextNode;
	}
}

void ScriptSlave::FollowPath( Event *ev )
{
	int i, argnum;
	Entity * ent;
	const char * token;
	SplinePath *path;
	qboolean clamp;
	float starttime;
	
	
	ent = ev->GetEntity( 1 );
	argnum = 2;
	starttime = -2;
	clamp = true;
	ignoreangles = false;
	splineangles = true;
	for ( i = argnum; i <= ev->NumArgs() ; i++ )
	{
		token = ev->GetString( i );
		if (!strcmpi( token, "ignoreangles"))
		{
			ignoreangles = true;
		}
		else if (!strcmpi( token, "normalangles"))
		{
			splineangles = false;
		}
		else if (!strcmpi (token, "loop"))
		{
			clamp = false;
		}
		else if ( IsNumeric( token ) )
		{
			starttime = (float)atof( token );
		}
		else
		{
			ev->Error( "Unknown followpath command %s.", token );
		}
	}
	if ( ent && ent->isSubclassOf( SplinePath ) )
	{
		commandswaiting = true;
		path = ( SplinePath * )ent;
		if ( clamp )
			CreatePath( path, SPLINE_CLAMP );
		else
			CreatePath( path, SPLINE_LOOP );
		
		currentNode = path;
		
		if ( currentNode->thread != "" )
		{
			if ( !ExecuteThread( currentNode->thread , true , this) )
			{
				gi.Error( ERR_DROP, "Scriptslave could not start thread '%s' from info_splinepath '%s'\n", 
					currentNode->thread.c_str(), currentNode->targetname.c_str() );
			}
		}
		
		if ( currentNode->triggertarget != "" )
		{
			Entity	*ent;
			Event		*event;
			
			ent = NULL;
			do
			{
				ent = G_FindTarget( ent, currentNode->triggertarget.c_str() );
				if ( !ent )
				{
					break;
				}
				event = new Event( EV_Activate );
				event->AddEntity( this );
				ent->PostEvent( event, 0.0f );
			}
			while ( 1 );
		}
		
		splineTime = starttime;
		lastTime = (int)(splineTime + 2.0f);
		newTime = (int)(splineTime + 2.0f);
		CancelEventsOfType( EV_ScriptSlave_FollowingPath );
		if ( !ignoreangles )
		{
			avelocity = vec_zero;
		}
		velocity = vec_zero;
	}
}

void ScriptSlave::EndPath( Event *ev )
{
	if ( !splinePath )
		return;
	
	delete splinePath;
	splinePath = NULL;
	velocity = vec_zero;
	if ( !ignoreangles )
	{
		avelocity = vec_zero;
	}
}

void ScriptSlave::FollowingPath( Event *ev )
{
	Vector	pos;
	Vector	orient;
	float    speed_multiplier;
	
	if ( !splinePath )
		return;
	
	if ( !thinking )
	   {
		velocity = vec_zero;
		if ( !ignoreangles )
		{
			avelocity = vec_zero;
		}
		
		return;
		//PostEvent( EV_ScriptSlave_FollowingPath, level.frametime );
	}
	
	if ( ( splinePath->GetType() == SPLINE_CLAMP ) && ( splineTime > ( splinePath->EndPoint() - 2.0f ) ) )
	{
		delete splinePath;
		splinePath = NULL;
		velocity = vec_zero;
		if ( !ignoreangles )
		{
			avelocity = vec_zero;
		}
		moving = false;
		ProcessEvent( EV_ScriptSlave_MoveDone );
		return;
	}
	
	if ( ( lastTime != newTime ) && currentNode )
	{
		if ( newTime > 1 )
		{
			
			if ( currentNode->thread != "" )
			{
				if ( !ExecuteThread( currentNode->thread ,true , this) )
				{
					gi.Error( ERR_DROP, "Scriptslave could not start thread '%s' from info_splinepath '%s'\n", 
						currentNode->thread.c_str(), currentNode->targetname.c_str() );
				}
			}
			
			if ( currentNode->triggertarget != "" )
			{
				Entity	*ent;
				Event		*event;
				
				ent = NULL;
				do
				{
					ent = G_FindTarget( ent, currentNode->triggertarget.c_str() );
					if ( !ent )
					{
						break;
					}
					event = new Event( EV_Activate );
					event->AddEntity( this );
					ent->PostEvent( event, 0.0f );
				}
				while ( 1 );
			}
			
		}
		
		currentNode = currentNode->GetNext();
	}
	
	lastTime = newTime;
	
	speed_multiplier = splinePath->Eval( splineTime, pos, orient );
	
	splineTime += level.frametime * speed_multiplier;
	
	velocity = ( pos - origin ) * ( 1.0f / level.frametime );
	if ( !ignoreangles )
	{
		if ( splineangles )
		{
			avelocity = ( orient - angles ) * ( 1.0f / level.frametime );
		}
		else
		{
			float len;
			
			len = velocity.length();
			if ( len > 0.05f )
            {
				Vector ang;
				Vector dir;
				float  aroll;
				
				aroll = avelocity[ ROLL ];
				dir = velocity * ( 1.0f / len );
				ang = dir.toAngles();
				avelocity = ( ang - angles ) * ( 1.0f / level.frametime );
				avelocity[ ROLL ] = aroll;
            }
			else
				avelocity = vec_zero;
		}
	}
	
	newTime = splineTime + 2.0f;
	
	if ( newTime < 0 )
	{
		newTime = 0;
	}
	
	PostEvent( EV_ScriptSlave_FollowingPath, level.frametime );	
	
}

void ScriptSlave::Explode( Event *ev )
{
	float damage;
	
	if ( ev->NumArgs() )
	{
		damage = ev->GetFloat( 1 );
	}
	else
	{
		damage = 120.0f;
	}
	
	CreateExplosion( origin, damage, this, this, this );
}

void ScriptSlave::NotShootable( Event *ev )
{
	setContents( 0 );
}

void ScriptSlave::OpenPortal( Event *ev )
{
	if ( !_portalOpen )
	{
 		gi.AdjustAreaPortalState( this->edict, true );
		_portalOpen = true;
	}
}

void ScriptSlave::ClosePortal( Event *ev )
{
	if ( _portalOpen )
	{
		gi.AdjustAreaPortalState( this->edict, false );
		_portalOpen = false;
	}
}

void ScriptSlave::PhysicsOn( Event *ev )
{
	commandswaiting = false;
	setMoveType( MOVETYPE_BOUNCE );
	setSolidType( SOLID_BBOX );
	velocity = Vector(0, 0, 1);
	edict->clipmask = MASK_SOLID|CONTENTS_BODY;
}

void ScriptSlave::PhysicsOff( Event *ev )
{
	Event * event;
	
	commandswaiting = false;
	setMoveType( MOVETYPE_PUSH );
	edict->clipmask = 0;
	// become solid again
	event = new Event( EV_Model );
	event->AddString( model );
	PostEvent( event, 0.0f );
}

void ScriptSlave::PhysicsVelocity( Event *ev )
{
	velocity += ev->GetVector( 1 );
}

void ScriptSlave::StopEvent( Event *ev )
{
	Stop();
}

void ScriptSlave::Stop( void )
{
	thinking = false;
}

void ScriptSlave::ContinueEvent( Event *ev )
{
	Continue();
}

void ScriptSlave::Continue( void )
{
	thinking = true;
	PostEvent( EV_ScriptSlave_FollowingPath, level.frametime );
}

//-----------------------------------------------------
//
// Name:		Think
// Class:		ScriptSlave
//
// Description:	Processes the updates for the Script Slave.
//
// Parameters:	None
//
// Returns:		None
//-----------------------------------------------------
void ScriptSlave::Think()
{
	if(flags & FL_TOUCH_TRIGGERS)
	{
		G_TouchTriggers(this);
	}
}

/*****************************************************************************/
/*QUAKED script_model (0 0.5 1) (0 0 0) (0 0 0) NOT_SOLID LIT_STATIC

******************************************************************************/

Event EV_ScriptModel_SetAnim
(
	"anim",
	EV_DEFAULT,
	"s",
	"anim_name",
	"Sets the script model's animation"
);
Event EV_ScriptModel_SetAnimDriven
(
	"animdriven",
	EV_DEFAULT,
	"B",
	"boolean",
	"Sets the script model to be anim driven"
);
Event EV_ScriptModel_AnimOnce
(
	"animonce",
	EV_SCRIPTONLY,
	"s",
	"anim_name",
	"Sets the script model's animation but only plays it once"
);

CLASS_DECLARATION( ScriptSlave, ScriptModel, "script_model" )
{
	{ &EV_Gib,							&ScriptModel::GibEvent },
	{ &EV_SetAngle,						&ScriptModel::SetAngleEvent },
	{ &EV_ScriptModel_SetAnim,			&ScriptModel::SetAnimEvent },
	{ &EV_ScriptModel_SetAnimDriven,	&ScriptModel::SetAnimDrivenEvent },
	{ &EV_ScriptModel_AnimOnce,			&ScriptModel::AnimOnceEvent },
	{ &EV_Model,						&ScriptModel::SetModelEvent },

	{ NULL, NULL },
};

ScriptModel::ScriptModel()
{
	// this is a tiki model
	edict->s.eType = ET_MODELANIM;
	animationDriven = false;
	
	animate = new Animate( this );
}

void ScriptModel::Think( void )
{
	total_delta = vec_zero;
	ScriptSlave::Think();
	
	if(animationDriven)
	{
		localangles = InterceptTargetXY( GetNewPosition(), Vector::Identity(), velocity.lengthXY() );
		SetNewAngles( localangles );
		setAngles( localangles );
		
		// Set velocity based on current delta move
		Vector forward;
		Vector left;
		Vector up;
		localangles.AngleVectors( &forward, &left, &up );
		
		total_delta /= level.frametime;
		velocity = ( forward * total_delta[0] ) + ( left * total_delta[1] ) + ( up * total_delta[2] );
	}
}

void ScriptModel::SetModelEvent( Event *ev )
{
	char	 modelname[256] ;
	char	*tmpPtr ;
	
	strcpy(modelname, ev->GetString( 1 ) );
	tmpPtr = strstr(modelname, "*");
	
	if (tmpPtr)
	{
		ev->SetString( 1 , tmpPtr );
	}
	
	ScriptSlave::SetModelEvent( ev );
	setSolidType( SOLID_BBOX );
	
	if ( ( gi.IsModel( edict->s.modelindex ) ) && !mins.length() && !maxs.length() )
	{
		gi.CalculateBounds( edict->s.modelindex, edict->s.scale, mins, maxs );
	}
}

void ScriptModel::SetAnimEvent( Event *ev )
{
	const char * const animname = ev->GetString( 1 );
	if ( animname && strlen( animname ) && gi.IsModel( edict->s.modelindex ) )
	{
		int animnum = gi.Anim_NumForName( edict->s.modelindex, animname );
		
		if ( animnum >= 0 )
		{
			animate->NewAnim( animnum );
		}
	}
}

void ScriptModel::SetAnimDrivenEvent( Event *ev )
{
	if ( (ev->NumArgs() > 0) && ( ev->GetBoolean( 1 ) == false ) )
	{
		animationDriven = false;
	}
	else
	{
		animationDriven = true;
		turnThinkOn();
	}
}

void ScriptModel::AnimOnceEvent( Event *ev )
{
	const char * animname;
	
	animname = ev->GetString( 1 );
	if ( animname && strlen( animname ) && gi.IsModel( edict->s.modelindex ) )
	{
		animate->RandomAnimate( animname, EV_StopAnimating );
	}
}

void ScriptModel::SetAngleEvent( Event *ev )
{
	float angle;
	
	angle = ev->GetFloat( 1 );
	if ( angle == -1.0f )
	{
		ForwardDir = Vector( 0.0f, 0.0f, 90.0f );
		localangles = Vector( -90.0f, 0.0f, 0.0f );
	}
	else if ( angle == -2.0f )
	{
		ForwardDir = Vector( 0.0f, 0.0f, -90.0f );
		localangles = Vector( 90.0f, 0.0f, 0.0f );
	}
	else
	{
		ForwardDir = Vector( 0.0f, angle, 0.0f );
		localangles = Vector( 0.0f, angle, 0.0f );
	}
	
	setAngles( localangles );
}

void ScriptModel::GibEvent( Event *ev )
{
	int      num,power;
	float    scale;
	
	setSolidType( SOLID_NOT );
	hideModel();
	
	if ( !com_blood->integer )
	{
		PostEvent( EV_Remove, 0.0f );
		return;
	}
	
	num = ev->GetInteger( 1 );
	power = ev->GetInteger( 2 );
	scale = ev->GetFloat( 3 );
	
	power = -power;
	
	if ( ev->NumArgs() > 3 )
	{
		CreateGibs( this, power, scale, num, ev->GetString( 4 ) );
	}
	else
	{
		CreateGibs( this, power, scale, num );
	}
	
	PostEvent( EV_Remove, 0.0f );
}

/*****************************************************************************/
/*QUAKED script_origin (1.0 0 0) (-8 -8 -8) (8 8 8)

Used as an alternate origin for objects.  Bind the object to the script_origin
in order to simulate changing that object's origin.
******************************************************************************/

CLASS_DECLARATION( ScriptSlave, ScriptOrigin, "script_origin" )
{
	{ &EV_Model,				&ScriptOrigin::SetModelEvent },

	{ NULL, NULL }
};

ScriptOrigin::ScriptOrigin()
{
	setContents( 0 );
	setSolidType( SOLID_NOT );
}

/*****************************************************************************/
/*QUAKED script_skyorigin (1.0 0 0) ?

Used to specify the origin of a portal sky
******************************************************************************/

Event EV_ScriptSkyOrigin_SetBasePosition
(
	"baseposition",
	EV_SCRIPTONLY,
	"v",
	"base_position",
	"Sets the base position of the sky origin."
);
Event EV_ScriptSkyOrigin_SetPlayerBasePosition
(
	"playerbaseposition",
	EV_SCRIPTONLY,
	"v",
	"base_position",
	"Sets the base position for the player for the sky origin."
);
Event EV_ScriptSkyOrigin_SetTranslationMultiplier
(
	"translationmult",
	EV_SCRIPTONLY,
	"f",
	"translation_multiplier",
	"Sets the translation multiplier for the sky origin."
);
Event EV_ScriptSkyOrigin_SetMaxDistance
(
	"maxtranslationdist",
	EV_SCRIPTONLY,
	"f",
	"max_translation_distance",
	"Sets the maximum distance the sky origin will translate."
);

CLASS_DECLARATION( ScriptSlave, ScriptSkyOrigin, "script_skyorigin" )
{
	{ &EV_ScriptSkyOrigin_SetBasePosition,					&ScriptSkyOrigin::SetBasePosition },
	{ &EV_ScriptSkyOrigin_SetPlayerBasePosition,			&ScriptSkyOrigin::SetPlayerBasePosition },
	{ &EV_ScriptSkyOrigin_SetTranslationMultiplier,			&ScriptSkyOrigin::SetTranslationMultiplier },
	{ &EV_ScriptSkyOrigin_SetMaxDistance,					&ScriptSkyOrigin::SetMaxDistance },

	{ NULL, NULL }
};

ScriptSkyOrigin::ScriptSkyOrigin()
{
	edict->s.renderfx |= RF_SKYORIGIN;
	setContents( 0 );
	setSolidType( SOLID_NOT );
	turnThinkOn();
	
	use_base_position = false;
	use_player_base_position = false;
	translation_multiplier = 0;
	max_distance = 0;
}

void ScriptSkyOrigin::Think( void )
{
	Vector delta;
	Entity *player;
	Vector new_origin;
	
	new_origin = origin;
	
	if ( use_base_position )
	{
		if ( translation_multiplier  )
		{
			// Get player
			
			player = g_entities[ 0 ].entity;
			
			if ( !use_player_base_position )
			{
				// Get current player position
				
				player_base_position = player->origin;
				
				use_player_base_position = true;
			}
			
			// Calculate the new origin
			
			delta = player->origin - player_base_position;
			delta *= translation_multiplier;
			
			if ( max_distance && ( delta.length() > max_distance ) )
			{
				delta.normalize();
				delta *= max_distance;
			}
			
			new_origin = base_position + delta;
		}
		else
		{
			new_origin = base_position;
		}
	}
	
	setOrigin( new_origin );
}

void ScriptSkyOrigin::SetBasePosition( Event *ev )
{
	use_base_position = true;
	base_position = ev->GetVector( 1 );
	use_player_base_position = false;
}

void ScriptSkyOrigin::SetPlayerBasePosition( Event *ev )
{
	use_player_base_position = true;
	player_base_position = ev->GetVector( 1 );
}

void ScriptSkyOrigin::SetTranslationMultiplier( Event *ev )
{
	translation_multiplier = ev->GetFloat( 1 );
}

void ScriptSkyOrigin::SetMaxDistance( Event *ev )
{
	max_distance = ev->GetFloat( 1 );
}


//1ST PLAYABLE HACK STUFF
void ScriptSlave::Hack_AddParms( Event *ev )
{
	_forcefieldNumber = ev->GetFloat( 1 );
	_triggerNumber   = ev->GetFloat( 2 );   
}

void ScriptSlave::Hack_GetForceFieldNumber( Event *ev )
{
	ev->ReturnFloat( _forcefieldNumber );
}

void ScriptSlave::Hack_GetScanner( Event *ev )
{
	ev->ReturnEntity( _scanner );   
}

void ScriptSlave::Hack_GetTriggerNumber( Event *ev )
{
	ev->ReturnFloat( _triggerNumber );       
}

void ScriptSlave::HandlesDamage( Event *ev )
{
	_handlesDamage = ev->GetBoolean( 1 );
}

void ScriptSlave::setDamageEffect( Event *ev )
{
	_damageEffect = ev->GetString( 1 );
}

void ScriptSlave::setBloodModel( Event *ev )
{
	_bloodModel = ev->GetString( 1 );	
}

void ScriptSlave::addRequiredDamageMOD( Event *ev )
{
	str modname( ev->GetString( 1 ) );
	assert( modname.length() > 0 );
	_requiredMODlist.AddObject( modname );

}

void ScriptSlave::setCanBeAttackedByOtherSlaves(Event *ev)
{
	_canBeAttackedByOtherScriptSlaves = ev->GetBoolean( 1 );
}
