// Template file v5.202 (02/20/02)
////////////////////////////////////////////////////////////////////////
// File: war.wdl
//		WDL prefabs for hostile entities
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////


//DEFINE WAR_CHAT_ON;	// display 'thought' actions for the AIs
IFDEF WAR_CHAT_ON;
	STRING war_wait_str,"Waiting...";
	STRING war_attack_str,"Attacking!!!";
	STRING war_hunt_str,"Searching...";
	STRING war_escape_str,"Retreat!!!";
	STRING war_dead_str,"I have been undone!";
ENDIF;



//////////////////////////////////////////////////////////////////////
// WAR.WDL - fighting behaviour
///////////////////////////////////////////////////////////////////////
IFNDEF WAR_DEFS;
 DEFINE ACTOR_EXPLO,<explo+7.pcx>;
 DEFINE ACTOR_EXPLO_FRAMES,7;
ENDIF;

///////////////////////////////////////////////////////////////////////
var freeze_actors = 0;	// 1,2 for testing purposes


///////////////////////////////////////////////////////////////////////
DEFINE _STATE_WAIT,1;
DEFINE _STATE_ATTACK,2;
DEFINE _STATE_ESCAPE,3;
DEFINE _STATE_DIE,4;
DEFINE _STATE_FREEZE,5;       // 05/15/01 DCP: Changed from 4->5
DEFINE _STATE_HUNT,6;

DEFINE _MUZZLE_VERT,SKILL32;
DEFINE _I_ACCURACY,SKILL33;		// 07/19/01 MWS: Added
DEFINE _I_COWARDICE,SKILL34;		// 07/19/01 MWS: Added
DEFINE _I_ALERTNESS,SKILL35;		// 07/19/01 MWS: Added


// DEFINE USER INPUT SKILLs
// These skill will be inputted from WED and work with entities that use the actions:
//    "player_walk_fight" and "actor_walk_fight"
DEFINE	_WALKSWIM_DIST, SKILL1;   // Walk Distance . Swim Distance
DEFINE	_RUNCRAWL_DIST, SKILL2;   // Run Distance . Crawl Distance
DEFINE	_STANDJUMP_TIME, SKILL3;  // Stand Time . Jump Time
DEFINE	_ATTACKDUCK_TIME, SKILL4; // Attack Time . Duck Time
DEFINE	_RUNTHRESHOLD, SKILL14;	// Run Threshold
//DEFINE	_FORCE, SKILL5;			// Force
//DEFINE	_BANKING, SKILL6;			// Banking amount (player only)
//DEFINE	_HITMODE, SKILL6;			// Hitmode (actor only)
//DEFINE	_MOVEMODE, SKILL7;		// Starting movemode
//DEFINE	_FIREMODE, SKILL8;		// Firemode
//DEFINE	_HEALTH, SKILL9;			// Heath
//DEFINE	_ARMOR, SKILL10;			// Armor
DEFINE	_ALERTNESS, SKILL11;    // Alertness
DEFINE	_ACCURACY, SKILL12; 		// Accuracy
DEFINE	_COWARDICE, SKILL13;		// Cowardice
DEFINE	_MUZZLEATTACH, SKILL15;	// Muzzle vertex . Attach vertex




///////////////////////////////////////////////////////////////////////
entity* test_actor;	// test actor entity pointer. Used in robot_test

entity* ent_marker;	// marker entity, used in hunting


STRING	robot_attack_str, "attack";	// attack frame names used by robot

// Desc: test robot, removed if 'test' not defined
// 	No WED defined skills
ACTION robot_test
{
IFNDEF test;
	remove(ME);
	return;
ENDIF;

	test_actor = ME;
	MY._WALKFRAMES = 8.030;
	MY._RUNFRAMES = 4.050;
	MY._ATTACKFRAMES = 6;
	MY._DIEFRAMES = 1;
	MY._FORCE = 2;
	MY._FIREMODE = DAMAGE_SHOOT+FIRE_PARTICLE+HIT_FLASH+0.05;
	MY._HITMODE = HIT_GIB;//HIT_EXPLO;
	MY._WALKSOUND = _SOUND_ROBOT;
	drop_shadow();
	anim_init();
	actor_fight();
}

// Desc: Action for robot #1
//			Init values and call actor_fight
// 	No WED defined skills
ACTION robot1
{
	MY._FORCE = 0.7;
	MY._FIREMODE = DAMAGE_EXPLODE+FIRE_BALL+HIT_EXPLO+BULLET_SMOKETRAIL+0.20;
	MY._HITMODE = 0;
	MY._WALKSOUND = _SOUND_ROBOT;
 	anim_init();
	drop_shadow();	// attach shadow to robot
	actor_fight();
//	if(MY.FLAG4 == ON) { CALL patrol; }
}


// Desc: Action for robot #2
//			Init values and call actor_fight
// 	No WED defined skills
ACTION robot2
{
	MY._FORCE = 2;
	MY._FIREMODE = DAMAGE_SHOOT+FIRE_PARTICLE+HIT_FLASH+0.05;
	MY._HITMODE = HIT_GIB;//HIT_EXPLO;
	MY._WALKSOUND = _SOUND_ROBOT;
	anim_init();
	drop_shadow(); // attach shadow to robot
	actor_fight();
//	if(MY.FLAG4 == ON) { patrol(); }
//	create(<arrow.pcx>,MY.POS,_ROBOT_TEST_WATCHER);  // used to activate watcher drone
}


// Desc: Action for robot #3
//			Init values and call actor_fight
//
// NOTE: robot #3 removes itself from the game!
// 	No WED defined skills
ACTION robot3
{
	remove(ME);
	return;

	MY._FORCE = 0.7;
	MY._FIREMODE = DAMAGE_EXPLODE+FIRE_BALL+HIT_EXPLO+BULLET_SMOKETRAIL+0.20;
	anim_init();
	drop_shadow();
	actor_fight();
//	if(MY.FLAG4 == ON) { patrol(); }
}


// Desc: Debugging utility, watches a ROBOT and sets flags using its values
//
entity* ent_test_watcher;		// marker entity, used in hunting
function _ROBOT_TEST_WATCHER()
{
	MY.PASSABLE = ON;
//	MY.INVISIBLE = ON;
	MY.ENTITY1 = YOU;

	WHILE(MY.ENTITY1 != 0)
	{
		ent_test_watcher = MY.ENTITY1;
 //		tst_value_1 =  ent_test_watcher._STATE;
		wait(1);

	}
 //	tst_value_1 =  -1;

	remove(ME);
}
///////////////////////////////////////////////////////////////////////


// Desc: the wait state
//
// Mod: 07/19/01 MWS
//			Use specified cowardice value instead of universal 30.
//			Use specified alertness instead of universal 1000.
//			Also now skips the player scan entirely if alertness is zero.
function state_wait()
{
IFDEF WAR_CHAT_ON;
msg.STRING = war_wait_str;
show_message();
ENDIF;

	// init values here for backwards compatibility
	if(MY._I_COWARDICE == 0)
	{ MY._I_COWARDICE = 30; }		// use default cowardice
	if(MY._I_ALERTNESS == 0)
	{ MY._I_ALERTNESS = 1000; }	// use default alertness


	MY._STATE = _STATE_WAIT;
	while(MY._STATE == _STATE_WAIT)
	{

		if(MY._HEALTH <= 0)      // no health? Die
		{
			EXCLUSIVE_ENTITY;
			wait(1);
			state_die();
			return;
		}

		// Note: changed, negative cowardice means never retreat
		if(MY._HEALTH <= MY._I_COWARDICE  &&  MY._I_COWARDICE >= 0)     // low health? Escape
		{
			EXCLUSIVE_ENTITY;
			wait(1);
			state_escape();
			return;
		}


		if (MY._I_ALERTNESS > 0)	// dont be alert if negative alertness
		{
			// scan for the player coming near
			temp.PAN = 180;
			temp.TILT = 180;
			temp.Z = MY._I_ALERTNESS;
			indicator = _WATCH;
			scan(MY.POS,MY.ANGLE,temp);

			// if the player has been detected...
			if(MY._SIGNAL == _DETECTED)
			{
				MY._SIGNAL = 0;
				state_attack();	// ATTACK!
				return;
			}
		}

		force = 0;     // no force from this actor
		actor_move();	// react to outside forces (gravity, etc) even while waiting
		waitt(1);
	}
}

// Desc: The attack state
//
function state_attack()
{
	MY._STATE = _STATE_ATTACK;
IFDEF WAR_CHAT_ON;
msg.STRING = war_attack_str;
show_message();
ENDIF;
	MY._MOVEMODE = _MODE_ATTACK;	// stop patrolling etc.
	attack_transitions();      // branch to other states depending on values


	// fire and close distance
	while(MY._STATE == _STATE_ATTACK)
	{

		// fire two or three times
		MY._COUNTER = 1.5 + RANDOM(1);

 		// check to see if player is shootable,
 		if((attack_fire()) == -1)
 		{
 			// cannot see player
 		  	EXCLUSIVE_ENTITY;
			wait(1);
			state_hunt();
			return;
 		}
		while(MY._COUNTER > 0) { waitt(1); }	// don't continue until attack_fire is finished

 		// walk towards player for one to three seconds
		MY._COUNTER = 16 + RANDOM(32);
		attack_approach();
		while(MY._COUNTER > 0) { waitt(1); } // don't continue until attack_approach is finished
	}


}

// Desc: Use internal and external values to branch to other states
//
// Mod Date: 7/26/01 MWS
//				Made "escape" branch test for MY._I_COWARDICE instead of default 30
function attack_transitions()
{
	while(MY._STATE == _STATE_ATTACK)
	{
		if(MY._HEALTH <= 0)    // goto die state
		{
			EXCLUSIVE_ENTITY;
			wait(1);
			state_die();
			return;
		}
		if(MY._HEALTH <= MY._I_COWARDICE  &&  MY._I_COWARDICE >= 0)     // low health? Escape
		{
			EXCLUSIVE_ENTITY;
			wait(1);
			state_escape();
			return;
		}
		if(freeze_actors > 0)   // goto freeze state
		{
			EXCLUSIVE_ENTITY;
			wait(1);
			state_freeze();
			return;
		}
		wait(1);
	}
}



//	Desc: attack the player:
//				-turn towards player
//				-check if entity can see the player
//				-play fire animation
//				-fire shot
//
// Mod Date: 05/18/01 DCP
//				Fixed "double shots" (the cause of robot1's self hits)
//
// Mod Date: 07/19/01 MWS
//				MY_ANGLE used in shooting at player now varies by a random
//				amount between -MY._I_ACCURACY to +MY._I_ACCURACY
//
// Mod Date: 08/22/01 DCP
//			Added "Advanced" Animation for firing
function attack_fire()
{
	while((MY._STATE == _STATE_ATTACK) && (MY._COUNTER > 0))
	{
		// turn towards player
		temp.X = player.X - MY.X;
		temp.Y = player.Y - MY.Y;
		temp.Z = player.Z - MY.Z;
		vec_to_angle(MY_ANGLE,temp);
		force = MY._FORCE * 2;
		actor_turn();

		// watch out if player visible. If yes, then shoot at him
		indicator = _WATCH;
		//SHOOT MY.POS,player.POS;
		trace_mode = IGNORE_ME + IGNORE_PASSABLE + IGNORE_PASSENTS;
		trace(MY.POS,player.POS);

		if((RESULT > 0) && (YOU == player))	// spotted him!
		{
			// fire at player
			// 1) PLAY ATTACK ANIMATION
			// check for *ADVANCED ANIMATION* (frame names and entity tick/dist values)
			if(int(MY._WALKFRAMES) < 0)
			{
				// reset entity's animation time to zero
				MY._ANIMDIST = 0;

				while(MY._ANIMDIST < 50)
				{
					// if you have more than one attacking animation, here's where you would test for it...
					// calculate a percentage out of the animation time
					MY._ANIMDIST +=  100*(TIME / (INT(((-MY._ADVANIM_TICK)&MASK_ANIM_ATTACK_TICKS)>>10)));
					// set the frame from the percentage
					ent_frame(robot_attack_str,MY._ANIMDIST);
					wait(1);
				}
			}
			else
			{
				// decide whether it's a frame number (old) or frame name (new) animation
				if(frc(MY._WALKFRAMES) > 0)// OLD STYLE ATTACK ANIMATION
				{
					// play attack animation
					MY.FRAME = 1 + MY._WALKFRAMES + MY._RUNFRAMES + 1;   // set frame to start
					MY.NEXT_FRAME = 0;	// inbetween to the real next frame
					while(MY.FRAME > 1 + MY._WALKFRAMES + MY._RUNFRAMES + MY._ATTACKFRAMES)
					{
						wait(1);
 						MY.FRAME += 0.4 * TIME;
					}
  					MY.FRAME = 1 + MY._WALKFRAMES + MY._RUNFRAMES + 1;  // end at start frame
				}// END OLD STYLE ATTACK ANIMATION
				else // new animation format    (frc(MY._WALKFRAMES) == 0)
				{
					// reset entity's animation time to zero
					MY._ANIMDIST = 0;

					while(MY._ANIMDIST < 50)
					{
						wait(1);
 						// calculate a percentage out of the animation time
						MY._ANIMDIST += 8.0 * TIME;   // attack in ~1 second
						// set the frame from the percentage
	  					// -old- SET_FRAME	ME,robot_attack_str,MY._ANIMDIST;
						ent_frame(robot_attack_str,MY._ANIMDIST);
					}
				}// END NEW STYLE ATTACK ANIMATION
			} // END NOT ADVANCED

			// 2) fire shot
			damage = frc(MY._FIREMODE) * 100;
			fire_mode = MY._FIREMODE;
			if((fire_mode & MODE_DAMAGE) == DAMAGE_SHOOT)
			{
				shot_speed.X = 500;
			}
			else
			{
				shot_speed.X = 100;
			}
			shot_speed.Y = 0;
			shot_speed.Z = 0;
			MY_ANGLE.PAN = MY.PAN;	// TILT is already set from TO_ANGLE

			// Add in a random amount according to MY._I_ACCURACY
			if (MY._I_ACCURACY != 0)
			{
		 		MY_ANGLE.PAN  += random(MY._I_ACCURACY*2) - MY._I_ACCURACY;
		  		MY_ANGLE.TILT += random(MY._I_ACCURACY*2) - MY._I_ACCURACY;
			}

			vec_rotate(shot_speed,MY_ANGLE);
			// now shot_speed points ahead

			// check to see if the model is using a muzzle vertice
			if(MY._MUZZLE_VERT == 0)
			{
				// default gun muzzle
				vec_set(gun_muzzle.X,MY.X);
			}
			else
			{
				ent_vertex(gun_muzzle.X,MY._MUZZLE_VERT);
			}

			play_entsound(ME,gun_wham,150);
			gun_shot();

			// check for *ADVANCED ANIMATION* (frame names and entity tick/dist values)
			if(int(MY._WALKFRAMES) < 0)
			{
				// set entity's animation to halfway
				MY._ANIMDIST = 50;

				while(MY._ANIMDIST < 100)
				{
					// if you have more than one attacking animation, here's where you would test for it...
					// calculate a percentage out of the animation time
					MY._ANIMDIST +=  100 * (TIME / (INT(((-MY._ADVANIM_TICK)&MASK_ANIM_ATTACK_TICKS)>>10)));
					// set the frame from the percentage
					ent_frame(robot_attack_str,MY._ANIMDIST);
					wait(1);
				}
			}
			else
			{
				if(frc(MY._WALKFRAMES) == 0)
				{
					// play the second half of the animation
 					while(MY._ANIMDIST < 100)
					{
						wait(1);
 						// calculate a percentage out of the animation time
						MY._ANIMDIST += 8.0 * TIME;   // attack in ~1 second
						// set the frame from the percentage
	  					// -old- SET_FRAME	ME,robot_attack_str,MY._ANIMDIST;
						ent_frame(robot_attack_str,MY._ANIMDIST);
					}
				}// END NEW STYLE ATTACK ANIMATION
			}
		}// END if((RESULT > 0) && (YOU == player))
		else
		{
			MY._COUNTER = 0;
			return(-1);	// can not see player
		}
		waitt(2);	// space shots appart (so they don't hit each other!
		MY._COUNTER -= 1;
	}// END  while((MY._STATE == _STATE_ATTACK) && (MY._COUNTER > 0))
}

// Desc: approach the player with random deviation
//			do not approach any closer then 50 units
//
//	Note: only rotates by PANning
//
//	Calls: actor_turn()
//			 actor_move()
//
// Mod Date: 06/05/01 DCP
//			Replaced TO_ANGLE with vec_to_angle()
function attack_approach()
{
	// calculate a direction to walk into
	temp.X = player.X - MY.X;
	temp.Y = player.Y - MY.Y;
	temp.Z = 0;
//---	TO_ANGLE MY_ANGLE,temp;
	vec_to_angle(MY_ANGLE,temp);
	// ADD random deviation angle
	MY._TARGET_PAN = MY_ANGLE.PAN - 15 + RANDOM(30);

	while((MY._STATE == _STATE_ATTACK) && (MY._COUNTER > 0))
	{
	// turn towards player
		MY_ANGLE.PAN = MY._TARGET_PAN;
		MY_ANGLE.TILT = 0;
		MY_ANGLE.ROLL = 0;
		force = MY._FORCE * 2;
		actor_turn();

		// walk towards him if not too close
		temp = (player.X - MY.X)*(player.X - MY.X)+(player.Y - MY.Y)*(player.Y - MY.Y);
		if(temp > 10000)  // 100^2
		{
			force = MY._FORCE;
			MY._MOVEMODE = _MODE_WALKING;
			actor_move();
		}

		wait(1);
		MY._COUNTER -= TIME;
	}
}

// low on health => try to run away
function state_escape()
{
IFDEF WAR_CHAT_ON;
msg.STRING = war_escape_str;
show_message();
ENDIF;
	MY._STATE = _STATE_ESCAPE;
	while(MY._STATE == _STATE_ESCAPE)
	{
		if(MY._HEALTH <= 0)
		{
			EXCLUSIVE_ENTITY;
			wait(1);
			state_die();
			return;
		}

		// turn away from player
		temp.X = MY.X - player.X;
		temp.Y = MY.Y - player.Y;
		temp.Z = MY.Z - player.Z;
		vec_to_angle(MY_ANGLE,temp);
		force = MY._FORCE * 6;
		actor_turn();

		force = MY._FORCE * 4;
		MY._MOVEMODE = _MODE_WALKING;
		actor_move();

		wait(1);
	}
}


// Desc: Die:
//				-stop moving
//				-stop scanning and shooting
//				-null out my.event
//				-play death animation
//				-become passable
//
//
//	Mod Date: 08/22/01 DCP
//		Use advanced animation for death (twice attack ticks).
//	Notes: Uses entity's _AMIMDIST value as a 'percent death' value (0 start, 100 end)
function state_die()
{
IFDEF WAR_CHAT_ON;
msg.STRING = war_dead_str;
show_message();
ENDIF;
	MY._MOVEMODE = 0;		// don't move anymore
	MY._STATE = _STATE_DIE;
	MY.ENABLE_SCAN = OFF;		// get deaf and blind
	MY.ENABLE_SHOOT = OFF;
	MY.EVENT = NULL;			// and don't react anymore

	// check if we have a marker...
	if(MY.ENTITY1 != 0)
	{
		// if so, remove it.
		remove(MY.ENTITY1);
	}

	// check for *ADVANCED ANIMATION* (frame names and entity tick/dist values)
	if(int(MY._WALKFRAMES) < 0)
	{
		// reset entity's animation time to zero
		MY._ANIMDIST = 0;

		while(MY._ANIMDIST < 100)
		{
			// if you have more than one attacking animation, here's where you would test for it...
			// calculate a percentage out of the animation time
			MY._ANIMDIST +=  100*(TIME / (INT(((-MY._ADVANIM_TICK)&MASK_ANIM_ATTACK_TICKS)>>9))); // (x>>10) * 2
			// set the frame from the percentage
			ent_frame(anim_death_str,MY._ANIMDIST);
			wait(1);
		}
	}
	else
	{
		// decide whether it's a frame number (old) or frame name (new) animation
		if(frc(MY._WALKFRAMES) > 0)// OLD STYLE ATTACK ANIMATION
		{
 			MY.FRAME = 1 + MY._WALKFRAMES + MY._RUNFRAMES + MY._ATTACKFRAMES + 1;
			MY.NEXT_FRAME = 0;	// inbetween to the real next frame
			while(MY.FRAME <= (1 + MY._WALKFRAMES + MY._RUNFRAMES + MY._ATTACKFRAMES + MY._DIEFRAMES))
			{
				wait(1);
				MY.FRAME += 0.7 * TIME;
			}
		}// END OLD STYLE Death ANIMATION
		else // new animation format    (frc(MY._WALKFRAMES) == 0)
		{
			// reset entity's animation time to zero
			MY._ANIMDIST = 0;

			while(MY._ANIMDIST < 100)
			{
				wait(1);
				// calculate a percentage out of the animation time
				MY._ANIMDIST += 5.0 * TIME;   // death in ~1.25 seconds
				// set the frame from the percentage
				// -old- SET_FRAME	ME,anim_death_str,MY._ANIMDIST;
				ent_frame(anim_death_str,MY._ANIMDIST);
			}
		}// END NEW STYLE ATTACK ANIMATION
	} // END NOT ADVANCED


	// If entity explodes after death
	if((ME != player) && ((MY._HITMODE & MODE_HIT) == HIT_EXPLO))//(MY._HITMODE & HIT_EXPLO))
	{

		morph(ACTOR_EXPLO,ME);
	  	MY._DIEFRAMES = ACTOR_EXPLO_FRAMES;
	  	actor_explode();       // ends with the removal of this entity
		return;
	}

  	// If entity 'gibs' after death
	if((ME != player) && ((MY._HITMODE & MODE_HIT) == HIT_GIB))//(MY._HITMODE & HIT_GIB))
	{
	  	_gib(25);             		// use new "gib" function
	  	actor_explode();     // ends with the removal of this entity
		return;
	}
	MY.PASSABLE = ON;   // body remains
}




// Desc: used to "tag" the last know target location
//       "CREATE <arrow.pcx>, MY.POS, _last_know_target_loc;"
function _last_know_target_loc()
{
	YOUR.ENTITY1 = ME;
	MY.INVISIBLE = ON;
	MY.PASSABLE = ON;
}

// Desc: move to the entity1 location
function hunt_approach()
{
	ent_marker = MY.ENTITY1;
	// calculate a direction to walk into
	temp.X = ent_marker.X - MY.X;
	temp.Y = ent_marker.Y - MY.Y;
	temp.Z = ent_marker.Z - MY.Z;
	vec_to_angle(MY_ANGLE,temp);
	MY._TARGET_PAN = MY_ANGLE.PAN;

   // turn towards target
	MY_ANGLE.PAN = MY._TARGET_PAN;
	MY_ANGLE.TILT = 0;
	MY_ANGLE.ROLL = 0;
	force = MY._FORCE * 2;
	actor_turn();
	if(avoid_walls() == 1)		// avoid hitting walls
	{
		force = MY._FORCE * 4;
		actor_turn();
	}


	temp = (ent_marker.X - MY.X)*(ent_marker.X - MY.X)+(ent_marker.Y - MY.Y)*(ent_marker.Y - MY.Y);
	// walk towards target only if not too close
	if(temp < 100) // 10^2
	{
		remove(MY.ENTITY1);	// remove marker
		EXCLUSIVE_ENTITY;
		wait(1);
		state_wait();	// branch to wait mode
		return;
	}

	// walk towards ent_marker
	force = MY._FORCE;
	MY._MOVEMODE = _MODE_WALKING;
	actor_move();

}


// Desc: avoid hitting walls
//
// Return: 1 if to close to a wall, o otherwise
function avoid_walls()
{
	// shoot a line between the entity and the target point
	SHOOT MY.POS, ent_marker.POS;

	IF((YOU != ent_marker) && (RESULT < 50))	// too close to a wall...
	{
		// angle away from the wall.
		temp.X = (TARGET.X + (100 * NORMAL.X)) - MY.X;
		temp.Y = (TARGET.Y + (100 * NORMAL.Y)) - MY.Y;
		temp.Z = MY.Z;
		vec_to_angle(MY_ANGLE,temp);
		RETURN(1);
 	}
	RETURN(0);
}


// Desc: follow a wall until you reach a corner
//
// NOTE: to-do (pathfind.wdl?)
function follow_wall()
{
	RETURN(0);
}

// Desc: Use internal and external values to branch to other states
//
// Mod Date: 7/26/01 MWS
//				Escape branch tests for MY._I_COWARDICE instead of constant 30
function hunt_transitions()
{
	while(MY._STATE == _STATE_HUNT)
	{
		// Check heath
		IF(MY._HEALTH <= 0)     // goto die state
		{
			EXCLUSIVE_ENTITY;
			wait(1);
			state_die();
			return;
		}

		if(MY._HEALTH <= MY._I_COWARDICE  &&  MY._I_COWARDICE >= 0)     // low health? Escape
		{
			EXCLUSIVE_ENTITY;
			wait(1);
			state_escape();
			return;
		}

  		// scan for the player coming near
		temp.PAN = 180;
		temp.TILT = 180;
		temp.Z = MY._I_ALERTNESS;
		indicator = _WATCH;
		SCAN	MY.POS,MY.ANGLE,temp;

		// if the player has been detected...
		if(MY._SIGNAL == _DETECTED)
		{
			MY._SIGNAL = 0;
			remove(MY.ENTITY1);
			EXCLUSIVE_ENTITY;
			wait(1);
			state_attack();	// ATTACK!
			return;
		}

		// Check debug
		IF(freeze_actors > 0)  // goto freeze state
		{
			EXCLUSIVE_ENTITY;
			wait(1);
			state_freeze();
			return;
		}

		waitt(1);
	}
}

// Desc: if you lose your target, move towards its last know location while
//		  scanning.
function	state_hunt()
{
	MY._STATE = _STATE_HUNT;
IFDEF WAR_CHAT_ON;
msg.STRING = war_hunt_str;
show_message();
ENDIF;
	// create a "marker" at the last known player location
	create(<arrow.pcx>, PLAYER.POS, _last_know_target_loc);
	MY._MOVEMODE = _MODE_WALKING;	// stop patrolling etc.
	hunt_transitions();      // branch to other states depending on values

	while(MY._STATE == _STATE_HUNT)
	{
		// approach the marker
		hunt_approach();

		wait(1);
	}
}


// Desc: freeze in place
function state_freeze()
{
	MY._STATE = _STATE_FREEZE;
	MY._MOVEMODE = _MODE_STILL;	// stop patrolling etc.
	while(MY._STATE == _STATE_FREEZE)
	{
		if(freeze_actors > 1)
		{
			MY.PASSABLE = ON;
		}

		if(freeze_actors == 0)
		{
			state_wait();
			return;
		}

		wait(1);
	}
}

////////////////////////////////////////////////////////////////////////

// Desc: take damage and detect player
//
// Mod: 02/06/01 DCP
//		Added wait before shoot command to remove 'Dangerous instruction error'
//	   Replaced SHOOT with trace()
//
// Mod: 05/05/01 DCP
//		Modified damage from ranged explosion (replaced ABS with MAX)
function fight_event()
{
	if((EVENT_TYPE == EVENT_SCAN && indicator == _EXPLODE)
		|| (EVENT_TYPE == EVENT_SHOOT && indicator == _GUNFIRE))
	{
		MY._SIGNAL = _DETECTED;	// by shooting, player gives away his position

		if (indicator == _EXPLODE)
		{	// reduce damage according to distance

			damage *= max(0,(range - RESULT))/range;
		}

		if(MY._ARMOR <= 0)
		{
			MY._HEALTH -= damage;
		}
		else
		{
			MY._ARMOR -= damage;
		}
		return;
	}

	if(EVENT_TYPE == EVENT_DETECT && YOU == player)
	{
		wait(1);	//fix for dangerous instruction
		indicator = _WATCH;
//---		SHOOT MY.POS,YOUR.POS;	// can player be seen from here?
		trace_mode = IGNORE_ME + IGNORE_PASSABLE + ACTIVATE_SHOOT;
		RESULT = trace(MY.POS,YOUR.POS); // if entity YOU was visible from MY position, its SHOOT event was triggered
		if((RESULT > 0) && (YOU == player)) // yes
		{
			MY._SIGNAL = _DETECTED;
		}
		return;
	}
}

// Desc: init actor for fighting and branch to the wait state
function actor_fight()
{
//	if(MY._ARMOR == 0) { MY._ARMOR = 100; }
	if(MY._HEALTH == 0) { MY._HEALTH = 100; }	// default health
	if(MY._FORCE == 0) { MY._FORCE = 1; }       // default force

	// Allow player to pass thru actor if frozen
	if(freeze_actors > 1) { MY.PASSABLE = ON; }

	MY._SIGNAL = 0;
	MY.ENABLE_SCAN = ON;
	MY.ENABLE_SHOOT = ON;
	MY.ENABLE_DETECT = ON;
	MY.EVENT = fight_event;

	state_wait();
}


// PLAYER ACTIONS
///////////////////////////////////////////////////////////////////////

//		player_walk_fight - player walk and fight action (basic FPS)

// temp flags only used in player_walk_fight action.
DEFINE	__NO_FALLDAMAGE, FLAG1;
DEFINE	__NO_JUMP,FLAG4;
DEFINE	__NO_BOB,FLAG5;
DEFINE	__NO_STRAFE,FLAG6;
DEFINE	__NO_TRIGGER,FLAG7;
DEFINE	__NO_DUCK,FLAG8;

/////////////////////////////////////////////////////////////////////////
// Desc: player action. Walk and fight (take damage, use gun)
//
//	Used for basic FPS type games.  Set the following skills and flags in
//WED to define the player (note: all skills default to 0, all flags
//default to OFF)
//
//	Flags
//		Flag1 - ON = *NO damage from falls*
//				 OFF = Take damage from falls
//		Flag2 - ON = move as a car (i.e. no turning unless moving),
//				 OFF = move normally
//		Flag3 - ON = adapt tilt and roll angle to slops
//				 OFF = stay upright while walking up and down slops (normal)
//		Flag4 - ON = *NO JUMPING*
//				 OFF = Allow jumping
//		Flag5 - ON = *NO first person view "head-bob" while moving*
//				 OFF = Enable first person view "head-bob"
//		Flag6 - ON = *Disable strafe*
//				 OFF = Allow strafing
//		Flag7 - ON = *Disable trigger* (open doors automatically)
//				 OFF = Trigger on
//		Flag8 - ON = *NO DUCKING*
//				 OFF = Enable ducking
//	Skills
//	_WALKSWIM_DIST, SKILL1;   	// Walk Distance . Swim Distance
//	_RUNCRAWL_DIST, SKILL2;   	// Run Distance . Crawl Distance
//	_STANDJUMP_TIME, SKILL3;  	// Stand Time . Jump Time
//	_ATTACKDUCK_TIME, SKILL4; 	// Attack Time . Duck Time
//	_RUNTHRESHOLD, SKILL14;		// Run Threshold
//	_FORCE, SKILL5;				// Force
//	_BANKING, SKILL6;				// Hitmode (actor only)
//	_MOVEMODE, SKILL7;			// Starting movemode
//	_HEALTH, SKILL9;	 			// Heath
//	_ARMOR, SKILL10;				// Armor
//
// Mod: 6/18/01 DCP
//		Created
//
// Mod: 8/21/01 DCP
//		Updated to use new WED skills
//
// uses _WALKSWIM_DIST,_RUNCRAWL_DIST,_STANDJUMP_TIME,_ATTACKDUCK_TIME
//	uses _FORCE,_BANKING,_MOVEMODE,_FIREMODE,_HEALTH,_ARMOR,_RUNTHRESHOLD
// uses __NO_FALLDAMAGE, __WHEELS, __SLOPES, __NO_JUMP, __NO_BOB, __NO_STRAFE
// uses __NO_TRIGGER, __NO_DUCK
ACTION player_walk_fight
{
// player
	// Flags-------------------------------
	IF(MY.__NO_FALLDAMAGE == ON) { MY.__FALL = OFF; }
	ELSE { MY.__FALL = ON; }
	//MY.__WHEELS = MY.FLAG2; // note: redundant
	//MY.__SLOPES = MY.FLAG3; // note: redundant
	IF(MY.__NO_JUMP == ON) { MY.__JUMP = OFF; }
	ELSE { MY.__JUMP = ON; }
	IF(MY.__NO_BOB == ON) { MY.__BOB = OFF; }
	ELSE { MY.__BOB = ON; }
	IF(MY.__NO_STRAFE == ON) { MY.__STRAFE = OFF; }
	ELSE { MY.__STRAFE = ON; }
	IF(MY.__NO_TRIGGER == ON) { MY.__TRIGGER = OFF; }
	ELSE { MY.__TRIGGER = ON; }
	IF(MY.__NO_DUCK == ON) { MY.__DUCK = OFF; }
	ELSE { MY.__DUCK = ON; }

	// Skill1------------------------------
	if (MY._HEALTH == 0) {			// if user didn't enter a value
		MY._HEALTH = 100;				// use default
	}

	if (MY._ARMOR < 0) {				// if bad value specified somehow
		MY._ARMOR = 0;					// default to zero
	}

	if( MY._FORCE <= 0)
	{
		MY._FORCE = 0.75;				// use default
	}


	player_anim_pack();           // use SKILLs 1-4 to set up animation

 	// Other-------------------------------
	MY.NARROW = ON;	// use narrow hull!
	MY.FAT = OFF;
 	MY.TRIGGER_RANGE = 5;
	if( MY._MOVEMODE <= 0)
	{
		MY._MOVEMODE = _MODE_WALKING;
	}

	if( MY._BANKING == 0)
	{
		MY._BANKING = -0.1;
	}

	player_move();    // basic player move loop
	player_fight();   // basic player fight loop
	drop_shadow();		// add a drop shadow (if the player has no 'real' shadow)
	show_panels();    // show heath/armor/ammo panel
}



// Desc - An entity action that will make a generic AI player with skill
//			 settings that can be set as above in WED
////////////////////////////////////////////////////////////
// These skills can be set in WED for the intended effect //
////////////////////////////////////////////////////////////
//	_WALKSWIM_DIST, SKILL1;   	// Walk Distance . Swim Distance
//	_RUNCRAWL_DIST, SKILL2;   	// Run Distance . Crawl Distance
//	_STANDJUMP_TIME, SKILL3;  	// Stand Time . Jump Time
//	_ATTACKDUCK_TIME, SKILL4; 	// Attack Time . Duck Time
//	_RUNTHRESHOLD, SKILL14;		// Run Threshold
//	_FORCE, SKILL5;				// Force
//	_HITMODE, SKILL6;				// Hitmode (actor only)
//	_MOVEMODE, SKILL7;			// Starting movemode
//	_FIREMODE, SKILL8; 			// Firemode
//	_HEALTH, SKILL9;	 			// Heath
//	_ARMOR, SKILL10;				// Armor
//	_ALERTNESS, SKILL11; 	   // Alertness
//	_ACCURACY, SKILL12; 			// Accuracy
//	_COWARDICE, SKILL13;	 		// Cowardice
//	_MUZZLEATTACH, SKILL15;		// Muzzle vertex . Attach vertex
////////////////////////////////////////////////////////////
// 07/16/01 - MWS - Created
// 07/24/01 - MWS - Overhauled (16 WED skills now!)
//
// Mod: 8/21/01 DCP
//		Updated to use new WED skills
//
// uses _WALKSWIM_DIST,_RUNCRAWL_DIST,_STANDJUMP_TIME,_ATTACKDUCK_TIME
// uses _FORCE,_HITMODE,_MOVEMODE,_FIREMODE,_HEALTH,_ARMOR,_ALERTNESS
// uses _ACCURACY,_COWARDICE,_RUNTHRESHOLD,_MUZZLEATTACH
action actor_walk_fight
{
	// Health-
	if (MY._HEALTH <= 0) { MY._HEALTH = 50; }	// default 50 health

	// Armor-
	if (MY._ARMOR <= 0) { MY._ARMOR = 0; }	// default 0 armor

 	// Force-
	if (MY._FORCE == 0) { MY._FORCE = 1.5; } 	// default force

 	// Alertness-
	MY._I_ALERTNESS = MY._ALERTNESS;

	// Cowardice-
	MY._I_COWARDICE = MY._COWARDICE;

	// Firemode-
	if (MY._FIREMODE == 0)
	{ MY._FIREMODE = DAMAGE_SHOOT+FIRE_PARTICLE+HIT_FLASH+0.10; }	// use default

	// Accuracy-
	if (MY._ACCURACY > 0)
	{
		MY._I_ACCURACY = MY._ACCURACY; // positive value given, use specified accuracy
	}
	else
	{
		if (MY._ACCURACY < 0)
		{ MY._I_ACCURACY = 0; }	// negative value given, use perfect accuracy
		else // MY._ACCURACY == 0
		{ MY._I_ACCURACY = 5; }	// no value given, use default accuracy
	}



	// Hitmode-
	//--MY._HITMODE = MY._HITMODE;	// zero is the default, no extra checking needed


	// Muzzle Vertex-
	MY._MUZZLE_VERT = int(MY._MUZZLEATTACH);



	// uses same advanced animation packing as player (dont let the function name fool you)
	player_anim_pack();

	// Other setup
	MY._WALKSOUND = _SOUND_ROBOT;
	anim_init();	// setup animation a bit
	drop_shadow(); // attach shadow to robot
	actor_fight();	// start simple AI state machine
}



/*

These skills are set in WED as below - 7/12/01 - MWS

_WALKSWIM_DIST	 Walk Distance . Swim Distance
_RUNCRAWL_DIST	 Run Distance . Crawl Distance
_STANDJUMP_TIME	 Stand Time . Jump Time
_ATTACKDUCK_TIME	 Attack Time . Duck Time
_RUNTHRESHOLD		 Run Threshold

Note: Times and distances after decimal use only TWO decimal places

-----------
Default animation values, taken from animate.wdl
-----------
(divide these by 4 for quarter-seconds, as they're 16ths now)
var anim_stand_ticks = 16;	// time for one standing anim cycle
var anim_jump_ticks = 6; 	// time for one jump animation cycle
var anim_attack_ticks = 16;// time for one attack animation cycle
var anim_duck_ticks = 8; 	// time for one duck animation cycle

(multiply these by 4 for quarter-widths)
var anim_walk_dist = 1; 	// dist per model width per walk cycle
var anim_swim_dist = 1; 	// dist per model width per swim cycle
var anim_run_dist = 1.5;	// dist per model width per run cycle
var anim_crawl_dist = 0.8; // dist per model width per crawl cycle
//var anim_wade_dist = 0.8;// dist per model width per crawl cycle (unused)

var walk_or_run = 12; 	// max quants per tick to switch from walk to run animation
*/
var	pap_pack1; // used to pack SKILL1
var	pap_pack2; // used to pack SKILL2

function player_anim_pack()
{
	pap_pack1 = 0;	//reset
	pap_pack2 = 0;	//reset

	// Stand Time
	temp = INT(MY._STANDJUMP_TIME);				  		// quarter seconds
	if (temp == 0) {								// no value entered in WED
		temp = 4;									// default anim_stand_ticks = 16
	}
 	pap_pack1 += temp >> 10;		  			// fractional 10 bits (2^10 = 1024)
	//pap_pack1 += temp / 1024;		  	  	// fractional 10 bits (2^10 = 1024)


	// Jump Time
	temp = INT(FRC(MY._STANDJUMP_TIME) * 100); 		// quarter seconds
	if (temp == 0) {								// no value entered in WED
		temp = 2;									// default anim_jump_ticks = 6
	}
	pap_pack1 += temp << 6;			 			// middle 6 bits (2^6 = 64)
	//pap_pack1 += temp * 64;				 	// middle 6 bits (2^6 = 64)


	// Attack Time
	temp = INT(MY._ATTACKDUCK_TIME);   					// quarter seconds
	if (temp == 0) {								// no value entered in WED
		temp = 4;									// default anim_attack_ticks = 16
	}
	pap_pack1 += temp << 12;			  		// upper 9 bits, shift 12 (2^12 = 4096)
	//pap_pack1 += temp * 4096;				// upper 9 bits, shift 12 (2^12 = 4096)


	// Duck Time
	temp = INT(FRC(MY._ATTACKDUCK_TIME) * 100);	 	// quarter seconds * 4 = game ticks
	if (temp == 0) {								// no value entered in WED
		temp = 2;									// default anim_duck_ticks = 8
	}
	pap_pack1 += temp; 							// lowest 6 bits


	// Walk Distance
	temp = INT(MY._WALKSWIM_DIST); 		// quarter widths
	if (temp == 0) {								// no value entered in WED
		temp = 4;									// default anim_walk_dist = 1
	}
	pap_pack2 += temp << 10;					// next upper 5 bits (2^10 = 1024)
	//pap_pack2 += temp * 1024;			  	// next upper 5 bits (2^10 = 1024)


	// Swim Distance
	temp = INT(FRC(MY._WALKSWIM_DIST) * 100); 		// quarter widths
	if (temp == 0) {								// no value entered in WED
		temp = 4;									// default anim_swim_dist = 1
	}
	pap_pack2 += temp;	  						// bottom 5 bits


	// Skill6------------------------------
	// Run Distance
	temp = INT(MY._RUNCRAWL_DIST);   					// quarter widths
	if (temp == 0) {								// no value entered in WED
		temp = 6;									// default anim_run_dist = 1.5
	}
	pap_pack2 += temp << 15;	  	 			// upper 6 bits (2^15 = 2^10 * 2^5 = 1024 * 32)
	//pap_pack2 += temp * 1024 * 32;	  		// upper 6 bits (2^15 = 2^10 * 2^5 = 1024 * 32)


	// Crawl Distance
	temp = INT(FRC(MY._RUNCRAWL_DIST) * 100);	  	// quarter widths
	if (temp == 0) {								// no value entered in WED
		temp = 3;									// default anim_crawl_dist = 0.8
	}
	pap_pack2 += temp << 5;			  			// upper 5 bits (2^5 = 32)
	//pap_pack2 += temp * 32;					// upper 5 bits (2^5 = 32)


	// Skill7------------------------------
	// Walk Or Run
	temp = INT(MY._RUNTHRESHOLD);   		 			// whole quants
	if (temp == 0) {								// no value entered in WED
		temp = 12;									// default walk_or_run = 12
	}
	pap_pack2 += temp >> 10;			  		// fractional 10 bits (2^10 = 1024)
	//pap_pack2 += temp / 1024;		  		// fractional 10 bits (2^10 = 1024)


	MY._ADVANIM_TICK = -1 * pap_pack1;		// activate advanced animation
	MY._ADVANIM_DIST  = -1 * pap_pack2;		// activate advanced animation
}






function player_fight()
{
	if(MY._HEALTH == 0) { MY._HEALTH = 100; }

	MY.ENABLE_SCAN = ON;
	MY.ENABLE_SHOOT = ON;
	MY.EVENT = fight_event;

	while(MY._HEALTH > 0)
	{
		if(MY._SIGNAL == _DETECTED) {	// Hit received?
			MY._SIGNAL = 0;	// reset the _signal skill
			if(person_3rd == 0) {
				player_shake(); }
		}
		wait(1);
	}

// Dead
	MY._HEALTH = 0;
	if(person_3rd == 0)
	{	// 1st person die action
		MY._MOVEMODE = 0;		// don't move anymore
		MY.EVENT = NULL;		// prevent health counting down
		player_tip();
		waitt(8);
		weapon_remove(); 	// let weapon disappear
	}
	else
	{
		weapon_remove();	// prevent dead player firing
		state_die();
		return;
	}
}





// Desc: explode into x number of random models
//
// Mod Date: 8/22/00 DCP
//				Uses function parameter (numberOfParts) to determine
//			how many 'gib' parts to create
function	_gib(numberOfParts)
{
	temp = 0;
	while(temp < numberOfParts)
	{
		create(<gibbit.mdl>, MY.POS, _gib_action);
		temp += 1;
	}

}

// Desc: Init and animate a gib-bit
//
// Mod Date: 6/29/00 Doug Poston
//				Added rotation and alpha-fading
// Mod Date: 8/31/00 DCP
//				Scale absdist vector by movement_scale
//				Scale the gibbits down by the actor_scale
function _gib_action()
{
	// scall the bits down by the actor_scale amount
	vec_scale(MY.SCALE_X,actor_scale);

	// Init gib bit
	MY._SPEED_X = 25 * (RANDOM(10) - 5);    // -125 -> +125
	MY._SPEED_Y = 25 * (RANDOM(10) - 5);    // -125 -> +125
	MY._SPEED_Z = RANDOM(35) + 15;          // 15 -> 50

	MY._ASPEED_PAN = RANDOM(35) + 35;       // 35 -> 70
	MY._ASPEED_TILT = RANDOM(35) + 35;      // 35 -> 70
	MY._ASPEED_ROLL = RANDOM(35) + 35;      // 35 -> 70

	MY.ROLL = RANDOM(180);	// start with a random orientation
	MY.PAN = RANDOM(180);

	MY.PUSH = -1;	// allow user/enemys to push thru



	// Animate gib-bit
	MY.SKILL9 = RANDOM(50);
	while(MY.SKILL9 > -75)
	{
		abspeed[0] = MY._SPEED_X * TIME;
		abspeed[1] = MY._SPEED_Y * TIME;
		abspeed[2] = MY._SPEED_Z * TIME;

		MY.PAN += MY._ASPEED_PAN * TIME;
		MY.TILT += MY._ASPEED_TILT * TIME;
		MY.ROLL += MY._ASPEED_ROLL * TIME;

		vec_scale(absdist,movement_scale);	// scale absolute distance by movement_scale
		MOVE  ME,NULLSKILL,abspeed;

		IF(BOUNCE.Z)
		{
			MY._SPEED_Z = -(MY._SPEED_Z/2);
			if(MY._SPEED_Z < 0.25)
			{
				MY._SPEED_X = 0;
				MY._SPEED_Y = 0;
				MY._SPEED_Z = 0;
				MY._ASPEED_PAN = 0;
				MY._ASPEED_TILT = 0;
				MY._ASPEED_ROLL = 0;
			}
		}

		MY._SPEED_Z -= 2;
		MY.SKILL9 -= 1;

		wait(1);
	}


	// Fade out
	MY.transparent = ON;
	MY.alpha = 100;
	while(1)
	{
   	MY.alpha -= 5*time;
		if(MY.alpha <=0)
		{
			// remove
			remove(ME);
			return;
		}

   	wait(1);
	}
}
