#include "defs.qh"
#include "ai.qh"
#include "monsters.qh"
/* ------------------------------------------------------------------------------------
** ------------------------------------GRUNTY(tm)--------------------------------------
** -- Your artificially intelligent mercenary from beyond the Void					 --
** -- Use with caution																  -
** -- Do not expose to excessive amounts of radiation								  -
** -- Share and Enjoy!																 --
** --- Copyright(c)2000 By Dwarven Star Interactive Industrial Industry Enterprises ---
** ----------------------------------------------------------------------------------*/
// OfN - Who the fuck coded this? it's cool!
// waypoint triggers were screwed in the version I started from though (chris6)
// lot of things changed, was VERY buggy. It made prozac server to crash like 5 times

/* ALREADY DEFINED IN MONSTERS.QC

#define WAYPOINT_TYPE_PRIMARY 0
#define WAYPOINT_TYPE_SECONDARY 1
#define WAYPOINT_TYPE_ENEMYLASTSEEN 2

*/

#define ARMY_RANGE_NONE_MIN 0
#define ARMY_RANGE_NONE_MAX 0
#define ARMY_RANGE_SMALL_MIN 75
#define ARMY_RANGE_SMALL_MAX 100
#define ARMY_RANGE_MEDIUM_MIN 175
#define ARMY_RANGE_MEDIUM_MAX 200
#define ARMY_RANGE_LONG_MIN 250
#define ARMY_RANGE_LONG_MAX 275

// Function Prototypes
// Whenever a function is added within the grunty, add it up here

void(entity wyp, entity soldier) RemoveWaypoint;

void () GruntyThink;
void () GRun;
void () GruntyScanTargets;
void () GruntyCheckFire;
void (entity targ) GetRank;
void () Grunty_Check_Frags;
void (float angle, float dist) botmovedist;
float () ReturnWeaponVelocity;
void () GruntyPayThink;
void () GruntyDrawPay;


// External Functions
// List all external functions used here
float () isMelee;
//entity (entity scanner) LookAround;

//=- OfN -=//
void(entity sld, entity player, string msg) PrintFromSoldier;
string(entity sld) GetOwnerMessage;
string(entity sld) GetFriendlyMessage;
entity (entity sold, entity viewpoint) ReturnEasyWaypoint;
string(entity thething) GetEnemyName;
void () CheckWaterJump;


// The Frames of Grunty

$cd /raid/quake/id1/models/player_4
$origin 0 -6 24
$base base		
$skin skin

// Frames, from player.qc

//
// running
//
$frame axrun1 axrun2 axrun3 axrun4 axrun5 axrun6

$frame rockrun1 rockrun2 rockrun3 rockrun4 rockrun5 rockrun6

//
// standing
//
$frame stand1 stand2 stand3 stand4 stand5

$frame axstnd1 axstnd2 axstnd3 axstnd4 axstnd5 axstnd6
$frame axstnd7 axstnd8 axstnd9 axstnd10 axstnd11 axstnd12


//
// pain
//
$frame axpain1 axpain2 axpain3 axpain4 axpain5 axpain6

$frame pain1 pain2 pain3 pain4 pain5 pain6


//
// death
//

$frame axdeth1 axdeth2 axdeth3 axdeth4 axdeth5 axdeth6
$frame axdeth7 axdeth8 axdeth9

$frame deatha1 deatha2 deatha3 deatha4 deatha5 deatha6 deatha7 deatha8
$frame deatha9 deatha10 deatha11

$frame deathb1 deathb2 deathb3 deathb4 deathb5 deathb6 deathb7 deathb8
$frame deathb9

$frame deathc1 deathc2 deathc3 deathc4 deathc5 deathc6 deathc7 deathc8
$frame deathc9 deathc10 deathc11 deathc12 deathc13 deathc14 deathc15

$frame deathd1 deathd2 deathd3 deathd4 deathd5 deathd6 deathd7
$frame deathd8 deathd9

$frame deathe1 deathe2 deathe3 deathe4 deathe5 deathe6 deathe7
$frame deathe8 deathe9

//
// attacks
//
$frame nailatt1 nailatt2

$frame light1 light2

$frame rockatt1 rockatt2 rockatt3 rockatt4 rockatt5 rockatt6

$frame shotatt1 shotatt2 shotatt3 shotatt4 shotatt5 shotatt6

$frame axatt1 axatt2 axatt3 axatt4 axatt5 axatt6

$frame axattb1 axattb2 axattb3 axattb4 axattb5 axattb6

$frame axattc1 axattc2 axattc3 axattc4 axattc5 axattc6

$frame axattd1 axattd2 axattd3 axattd4 axattd5 axattd6

/* ---------------------------------------------------------------------------------- */
// The Fun Bit

/* ---------------------------------------------------------------------------------- */
//						GRUNTY - PART ONE
//					Standing Around
//
//						Grunty is standing around having a good time
/* ---------------------------------------------------------------------------------- */

// Grunty is standing with his axe or other melee weapon
// Note that he must do a GruntyThink each frame
// (each frame is 0.05 seconds)
// However, he may only animate every 0.1 seconds, hence the flooring and odd incrementing
// of his frame variable. (0.05 animation is very smooth and twice as fast as normal)

void() grunty_run;

void() grunty_stand = // We need two stand sections for axe and weapon
{
	//self.nextthink = time + 0.05; needed?? nope
	if (self.health > 0) self.think = grunty_stand;

	self.weaponframe = 0;
	
	if (isMelee())
	{
		if (self.walkframe >= 12)
			self.walkframe = 0;
		self.frame = $axstnd1 + floor(self.walkframe);
	}
	else
	{
		if (self.walkframe >= 5)
			self.walkframe = 0;
		self.frame = $stand1 + floor(self.walkframe);
	}
	self.walkframe = self.walkframe + 0.5;
	
	#ifdef VERBOSE_GRUNTY
	if (self.super_time <= time)
	{
		sprint(self.real_owner, PRINT_HIGH, "Soldier in position and awaiting target.\n");
		self.super_time = time + 5;
	}
	#endif

	Grunty_Check_Frags();

    if (self.goalentity)
	{
		self.walkframe = 0;
		self.think = grunty_run;
		//return;
	}

	GruntyThink();
    //sprint(self.real_owner, PRINT_HIGH, "Done gruntythink in stand().\n");
};
/* ---------------------------------------------------------------------------------- */
//						GRUNTY - PART TWO
//					Running Around
//
//						Grunty is charging about the place
/* ---------------------------------------------------------------------------------- */

// Again we have a choice of which animation to use
void() grunty_run =
{
	if (self.health > 0) self.think = grunty_run;

	self.weaponframe = 0; //needed?
	
    if (isMelee())
	{
		if (self.walkframe >= 6)
			self.walkframe = 0;
		self.frame = $axrun1 + floor(self.walkframe);
	}
	else
	{
		if (self.walkframe >= 6)
			self.walkframe = 0;
		self.frame = $rockrun1 + floor(self.walkframe);
	}	
    
    self.walkframe = self.walkframe + 0.5;
	
	#ifdef VERBOSE_GRUNTY
    if (self.super_time <= time)
	{
		sprint(self.real_owner, PRINT_HIGH, "Hello, commander! I am currently running at speed ");
		sprint(self.real_owner, PRINT_HIGH, ftos(self.custom_speed));
		sprint(self.real_owner, PRINT_HIGH, " towards ");
		sprint(self.real_owner, PRINT_HIGH, self.goalentity.netname);
		sprint(self.real_owner, PRINT_HIGH, "\n");
		self.super_time = time + 5;
	}
	#endif

	if (!self.goalentity)
	{
		self.walkframe = 0;
		self.think = grunty_stand;
		//return;
	}
    
    GruntyThink();
	
    #ifdef VERBOSE_GRUNTY
    sprint(self.real_owner, PRINT_HIGH, "I have just finished GruntyThink (self.enemy = ");
	sprint(self.real_owner, PRINT_HIGH, self.enemy);
	sprint(self.real_owner, PRINT_HIGH, ")\n");
	#endif
    
	GRun();
	
    #ifdef VERBOSE_GRUNTY    
	sprint(self.real_owner, PRINT_HIGH, "I have just finished GRun (self.enemy = ");
	sprint(self.real_owner, PRINT_HIGH, self.enemy);
	sprint(self.real_owner, PRINT_HIGH, ")\n");
	#endif    
	
	Grunty_Check_Frags();
};

void() stand_frames = //- ofn - used when soldier is following us, and when hes stuck and set to not jump
{
    self.weaponframe=0;
    
    if (isMelee())
	{
		if (self.walkframe >= 12)
			self.walkframe = 0;
		self.frame = $axstnd1 + floor(self.walkframe);
	}
	else
	{
		if (self.walkframe >= 5)
			self.walkframe = 0;
		self.frame = $stand1 + floor(self.walkframe);
	}
	self.walkframe = self.walkframe + 0.1;
};

/* ---------------------------------------------------------------------------------- */
//						GRUNTY - PART THREE
//					Axing People
//
//						Grunty goes psycho
/* ---------------------------------------------------------------------------------- */

// Grunty's first axe attack
void() grunty_axeatta =
{
	self.frame = $axatt1 + floor(self.weaponframe);
	
	if (self.weaponframe == 1)
		if (self.current_weapon == WEAP_AXE)
			W_FireAxe();
		else
			W_FireSpanner();
	
	self.weaponframe = self.weaponframe + 0.5;
	GruntyThink();
	GRun();
	///self.nextthink = time + 0.05;
	if (self.weaponframe > 3)
		if (self.health > 0) self.think = grunty_run;
};

// Grunty's second axe attack
void() grunty_axeattb =
{
	self.frame = $axattb1 + floor(self.weaponframe);
	
	if (self.weaponframe == 2)
		if (self.current_weapon == WEAP_AXE)
			W_FireAxe();
		else
			W_FireSpanner();
	
	self.weaponframe = self.weaponframe + 0.5;
	GruntyThink();
	GRun();
	///self.nextthink = time + 0.05;
	if (self.weaponframe > 3)
		if (self.health > 0) self.think = grunty_run;
};

// Grunty's third axe attack
void() grunty_axeattc =
{
	self.frame = $axattc1 + floor(self.weaponframe);
	
	if (self.weaponframe == 2)
		if (self.current_weapon == WEAP_AXE)
			W_FireAxe();
		else
			W_FireSpanner();
	
	self.weaponframe = self.weaponframe + 0.5;
	GruntyThink();
	GRun();
	///self.nextthink = time + 0.05;
	if (self.weaponframe > 3)
		if (self.health > 0) self.think = grunty_run;
};

// Grunty's fourth axe attack
void() grunty_axeattd =
{
	self.frame = $axattd1 + floor(self.weaponframe);
	
	if (self.weaponframe == 2)
		if (self.current_weapon == WEAP_AXE)
			W_FireAxe();
		else
			W_FireSpanner();
	
	self.weaponframe = self.weaponframe + 0.5;
	GruntyThink();
	GRun();
	///self.nextthink = time + 0.05;
	if (self.weaponframe > 3)
		if (self.health > 0) self.think = grunty_run;
};

// Axe attack chooser
void() grunty_axeatt =
{
	local float r;
	
	self.walkframe = 0; // Reset walkframe to avoid freaky animations?
	
	r = random();
	
	if (r < 0.25)
		grunty_axeatta();
	else if (r < 0.5)
		grunty_axeattb();
	else if (r < 0.75)
		grunty_axeattc();
	else
		grunty_axeattd();
};

/* ---------------------------------------------------------------------------------- */
//						GRUNTY - PART FOUR
//					Shooting Time
//
//						Grunty goes postal
/* ---------------------------------------------------------------------------------- */

// Grunty uses the shotgun
// The firing is done after he chooses the attack
void() grunty_shotgun =
{
	self.frame = $shotatt1 + floor(self.weaponframe);
	
	if (self.weaponframe == 0)
		self.effects = self.effects | EF_DIMLIGHT;
	else if (self.weaponframe == 0.5)
		self.effects = self.effects - (self.effects & EF_DIMLIGHT);
	
	self.weaponframe = self.weaponframe + 0.5;
	GruntyThink();
	GRun();
	///self.nextthink = time + 0.05;
	if (self.weaponframe > 5)
	{
		self.walkframe = 0;
        if (self.ammo_shells < 1) 
            PrintFromSoldier(self,self.real_owner,"i've run out of shells!\n");
		if (self.health > 0) self.think = grunty_run;
	}
};

// Grunty fires his rocket launcher! muahahahahaha
// The firing is done after he chooses the attack
void() grunty_rocket =
{
	self.frame = $rockatt1 + floor(self.weaponframe);
	
	if (self.weaponframe == 0)
		self.effects = self.effects | EF_DIMLIGHT;
	else if (self.weaponframe == 0.5)
		self.effects = self.effects - (self.effects & EF_DIMLIGHT);
	
	self.weaponframe = self.weaponframe + 0.5;
	GruntyThink();
	GRun();
	///self.nextthink = time + 0.05;
	if (self.weaponframe > 5)
	{
		self.walkframe = 0;
        if (self.ammo_rockets < 1) 
            PrintFromSoldier(self,self.real_owner,"i've run out of rockets!\n");
		if (self.health > 0) self.think = grunty_run;
	}
};

//- ofn - this to compensate no prediction
//- GRUNTY'S NAILS GO FASTER AS HIS RANK IS INCREASED (upto 2200)-//
void(vector org, vector dir) grunty_spike =
{
	newmis = spawn ();
	newmis.owner = self;
	newmis.movetype = MOVETYPE_FLYMISSILE;
	newmis.solid = SOLID_BBOX;

	newmis.angles = vectoangles(dir);

	newmis.touch = spike_touch;
	newmis.weapon = DMSG_NAILGUN;
	newmis.classname = "spike";
	newmis.think = SUB_Remove;
	newmis.nextthink = time + 6;
	setmodel (newmis, "progs/spike.mdl");
	setsize (newmis, VEC_ORIGIN, VEC_ORIGIN);		
	setorigin (newmis, org);

	newmis.velocity = dir*(1100+1100*(self.has_sensor/17));
};

// Grunty uses the nailgun
void() grunty_nail1 = [$nailatt1, grunty_nail2]
{
	GruntyThink(); GRun(); self.effects = self.effects | EF_DIMLIGHT;
	if (self.ammo_nails > 0)
    {   
        W_FireSpikes(4);
        if (self.ammo_nails < 1) PrintFromSoldier(self,self.real_owner,"i've run out of nails!\n");
    }
};
void() grunty_nail2 = [$nailatt1, grunty_nail3] 
{
    GruntyThink(); GRun(); self.effects = self.effects | EF_DIMLIGHT;
};
void() grunty_nail3 = [$nailatt2, grunty_nail4] 
{
    GruntyThink(); GRun(); self.effects = self.effects - (self.effects & EF_DIMLIGHT);
	if (self.ammo_nails > 0)
    {
        W_FireSpikes(-4);
        if (self.ammo_nails < 1) PrintFromSoldier(self,self.real_owner,"i've run out of nails!\n");
    }
};
void() grunty_nail4 = [$nailatt2, grunty_nail1] 
{
	GruntyThink(); GRun(); self.effects = self.effects - (self.effects & EF_DIMLIGHT);

    local float vis, dist;

    vis = visible(self.enemy);
    dist = vlen(self.origin - self.enemy.origin);

    if (!vis || self.ammo_nails < 1 || self.enemy.health <= 0 || dist < 200 || random() < 0.025)
	{
		self.walkframe = 0;
		grunty_run();
		return;
	}	
};

vector () Grunty_LeadShot =
{
	local vector pvel, dir, loc;
	local vector src, targ;
	
	src = self.origin + '0 0 16';
	targ = self.enemy.origin;
	
	
    pvel = '0 0 0'; // ofn - no prediction

    /*if (self.enemy.classname == "player")
		pvel='0 0 0';//pvel = self.enemy.velocity;
	else if (self.enemy.classname == "monster_army")
	{
		makevectors(self.enemy.angles);
		pvel = self.enemy.velocity + v_forward * self.enemy.custom_speed * 20;// * 10; //- ? was 20
	}
	else
		pvel = '0 0 0';*/
	
	//if (!self.enemy.flags & FL_ONGROUND)
	//	pvel_z = pvel_z / 20;
	
	//lead = pvel * (vlen(src - targ) / ReturnWeaponVelocity());
	loc = targ;// + lead;

    // do the following do anything?-----//
	//lead = pvel * (vlen(src - loc) / ReturnWeaponVelocity());
	//lead_x = lead_x + (10 - random() * 20);
	//lead_y = lead_y + (10 - random() * 20);
    //------------------------------------//    self.view_ofs
	
    dir = normalize (loc - src);

    traceline(src, targ, TRUE, self);
	if (trace_fraction != 1.0)
	{
		loc_z = loc_z + self.enemy.maxs_z; // already commented
		//if (self.current_weapon == WEAP_ROCKET_LAUNCHER)
		//	loc_z = loc_z + self.enemy.mins_z * -1;
	}
    else
    {
        if (self.current_weapon == WEAP_ROCKET_LAUNCHER && random() > 0.3)
    		loc_z = loc_z - self.enemy.maxs_z/2; 
            //- OFN - makes grunty aim to the ground with RL sometimes, so evil! ^_^
    }

    dir = normalize (loc - src);
	
	return dir;
};

/* ---------------------------------------------------------------------------------- */
//						GRUNTY - PART FIVE
//					Thinking Skills
//
//						Grunty reflects on what he has done
/* ---------------------------------------------------------------------------------- */

void() GruntyThink =
{
	AI_Check_Contents(self);

    if (self.health <= 0) return;
    
    self.nextthink = time + 0.05; //was 0.05
	
	// check for Follow Me Grunty's owner death
	if (self.goalentity == self.real_owner)
		if (self.goalentity.health <= 0)
			self.goalentity = NIL;
	
	
	// Check to buy
	if (self.suicide_time <= time)
		GruntyPayThink();
	// scan for targets
	
	GruntyScanTargets();
	
};

// LookAround for grunty
entity (entity scanner) LookAroundGrunty =
{
	if (infokey(NIL,"ceasefire")=="on") //OfN
	    return scanner;
    
	local entity		client;
	local float			gotatarget;

	client = findradius(scanner.origin, 2500);

	while (client)
	{
		gotatarget = 0;
		
        if (client != scanner && visible2(client, scanner))
		{
            
            #ifdef MAD_MONSTERS
            
            if (client.classname == "player" && IsMonsterNonArmy(scanner)) gotatarget = 1;
            
            #else
            
            if (client.classname == "player" && !Teammate(client.team_no, scanner.real_owner.team_no))
			{
                gotatarget = Pharse_Client(client, scanner, 1, 0, 0, 0);

                if (client.is_undercover)
                    if ((client.cutf_items & CUTF_JAMMER) || !(scanner.tf_items & NIT_SCANNER))
                        gotatarget=0;
                if (client.modelindex == modelindex_null)
                    if ((client.cutf_items & CUTF_JAMMER) || !(scanner.tf_items & NIT_SCANNER))
                        gotatarget=0;
                if (client.modelindex == modelindex_eyes)
                    if ((client.cutf_items & CUTF_JAMMER) || !(scanner.tf_items & NIT_SCANNER))
                        if (random() < 2 - scanner.has_tesla * random())
                            gotatarget=0;		
            }
            
            #endif

            else if (IsMonster(client))
			{
				if (!Teammate(client.real_owner.team_no,scanner.real_owner.team_no))
					gotatarget = 1;
			}
			else if (client.classname == "grenade" && client.netname == "land_mine")
			{
				if (!Teammate(client.owner.team_no,scanner.real_owner.team_no))
					gotatarget = 1;
			}
			else if (!Teammate(client.team_no, scanner.real_owner.team_no))
			{ //  && client.classname != "building_sentrygun_base"
				if (IsOffenseBuilding(client)) // for teslas isoffensiveb only returns true if not cloaked
					gotatarget = 1;                
			}	
            
		}
		if (gotatarget)
			return client;
		
		client = client.chain;
	}
	return scanner;
};


entity () GruntyPharse =
{
    local entity retarg = NIL;
    local float r;
	r = random();
    
    #ifdef MAD_GRUNTY

    if (r < 0.30 && (visible(self.real_owner)) && self.real_owner.health > 0)
        retarg = self.real_owner;
    
    #else

    //- ofn - lot of shit changed in lookaround to make them target well
    if (r < 0.30)
        retarg = LookAroundGrunty(self);
    
    #endif    

    return retarg;
};

void() GruntyScanTargets =
{
	local entity	targ; // our hapless foe
	targ = NIL;
	
	if (self.enemy)
		if (self.enemy.health <= 0 || self.enemy.has_disconnected)
		{

            self.enemy = NIL;
            self.goalentity = ReturnEasyWaypoint(self,self);

           //// self.enemy = NIL;
			//// self.goalentity = NIL;
			if (self.demon_one)
			{
                RemoveWaypoint(self.demon_one,self);
                self.demon_one = NIL;
            }

            self.has_teleporter = 0;
			self.effects = self.effects - (self.effects & EF_DIMLIGHT);
			return;
		}
	// If we have a target already
	if (self.enemy)
	{
		local float vis;
		vis = visible(self.enemy);
		if (vis)
		{
			if (self.demon_one)
			{
                RemoveWaypoint(self.demon_one,self);
                self.demon_one=NIL;

            } //often =world
			self.search_time = time + 3;
			if (self.attack_finished <= time)
				GruntyCheckFire();
		}
		else
		{
			if (self.is_malfunctioning==0 || self.is_malfunctioning==2)
            {
                self.enemy = NIL;
                self.goalentity = ReturnEasyWaypoint(self,self);

                self.has_teleporter = 0;
    			self.effects = self.effects - (self.effects & EF_DIMLIGHT);

                return;
            }
            // tactic
            
            if (!self.demon_one) // if we don't have a enemy last seen marker
            {
                if (self.demon_one)
                {
                    RemoveWaypoint(self.demon_one,self);
                }

                self.demon_one = CreateWaypoint(self.enemy.origin - self.enemy.velocity * 0.05, WAYPOINT_AI_LIFE, WAYPOINT_TYPE_ENEMYLASTSEEN);
                self.demon_one.goalentity = ReturnEasyWaypoint(self,self.demon_one);
                self.goalentity = self.demon_one;
                self.search_time = time + GRUNTY_SEEKTIME; // was 8
                //- OfN -
                self.has_teleporter = 0;
            }
            if (self.search_time <= time) // we seeked enemy for GRUNTY_SEEKTIME already so...
            {
                self.enemy = NIL;
                self.goalentity = NIL;
                
                if (self.demon_one) // d1 remove contingency check
                {
                    RemoveWaypoint(self.demon_one,self);
                    self.demon_one = NIL;
                    self.goalentity = ReturnEasyWaypoint(self,self);
                }
            }
            
		}
	}
	else // otherwise look for a target
	{
		targ=GruntyPharse();
		
        if (targ == self)
            return;
        if (!targ)
            return;

		if (!self.is_detpacking || infokey(NIL,"ceasefire")=="on")
        {
            if (!IsBuilding(targ) && self.super_time < time && targ.netname != "land_mine")
            {
                PrintFromSoldier(self,self.real_owner,"i can see ");
                sprint(self.real_owner, PRINT_HIGH, targ.netname);
                sprint(self.real_owner, PRINT_HIGH, " around there...\n");
                self.super_time = time + 4;
            }
            return;
        }
        
        if (self.super_time < time)
		{
			local string targetstr;
            targetstr=GetEnemyName(targ);//targ.netname;

            PrintFromSoldier(self,self.real_owner,"Target ");
           
            sprint(self.real_owner, PRINT_HIGH, targ.netname);
			sprint(self.real_owner, PRINT_HIGH, " spotted!\n Engaging target...\n");
			self.super_time = time + 2.5;
		}
		
		self.enemy = targ;
		self.goalentity = self.enemy;
		self.search_time = time + 3;
	}
};

void() GruntyCheckFire =
{
	local float dist, vis;

	if (self.attack_finished > time)
		return;
	
    if (!self.enemy)
		return;
    
    if (infokey(NIL,"ceasefire")=="on")
    {
        self.goalentity=ReturnEasyWaypoint(self,self);
        self.enemy=NIL;
        return;
    }

	vis = visible(self.enemy);
	if (!vis)
	{
		#ifdef VERBOSE_GRUNTY
		//if (self.super_time <= time)
		//{
			sprint(self.real_owner, PRINT_HIGH, "Target not visible! Cancelling firing...\n");
			self.super_time = time + 5;
		//}
		#endif
		
        //self.weaponframe = 0;
        //self.think = grunty_run;
		//self.nextthink = time + 0.05;
		return;
	}
	
    //-- OFN --// no need as yaw is set in GRun
    //enemy_yaw = vectoyaw(self.enemy.origin - self.origin);
	//self.ideal_yaw = enemy_yaw;
	//ChangeYaw();
    //---------//

    /*if (!FacingIdeal())
		return; */
	
    dist = vlen(self.origin - self.enemy.origin);
	self.weaponframe = 0;
	
    
	if (dist < 64 && self.weapons_carried & WEAP_AXE && self.cutf_items & CUTF_KNIFE)
	{
		self.current_weapon = WEAP_AXE;
		/*if (self.cutf_items & CUTF_KNIFE)
			self.attack_finished = time + 0.5;
		else
			self.attack_finished = time + 0.5;*/
        Attack_Finished(0.5);
		if (self.health > 0) self.think = grunty_axeatt;
		#ifdef VERBOSE_GRUNTY
		sprint(self.real_owner, PRINT_HIGH, "Attempting to knife...\n");
		#endif
		return;
	}
	if (dist < 200 && self.weapons_carried & WEAP_SUPER_SHOTGUN)
	{
		if (self.ammo_shells > 1)
		{
			self.current_weapon = WEAP_SUPER_SHOTGUN;
			//self.attack_finished = time + 0.7;
            Attack_Finished(0.7);
			W_FireSuperShotgun();
			if (self.health > 0) self.think = grunty_shotgun;
			#ifdef VERBOSE_GRUNTY
			sprint(self.real_owner, PRINT_HIGH, "Attempting to super shotgun...\n");
			#endif
			return;
		}
	}
	if (dist < 64 && self.weapons_carried & WEAP_AXE)
	{
		self.current_weapon = WEAP_AXE;
		/*if (self.cutf_items & CUTF_KNIFE)
			self.attack_finished = time + 0.5;
		else
			self.attack_finished = time + 0.5;*/
        Attack_Finished(0.5);
		if (self.health > 0) self.think = grunty_axeatt;
		#ifdef VERBOSE_GRUNTY
		sprint(self.real_owner, PRINT_HIGH, "Attempting to axe...\n");
		#endif
        return;
	}                       //was 800
	if (dist > 200 && dist < 1100 && self.weapons_carried & WEAP_ROCKET_LAUNCHER)
	{
        if (self.ammo_rockets > 1)
        {
            self.current_weapon = WEAP_ROCKET_LAUNCHER;
            //self.attack_finished = time + 0.8;
            Attack_Finished(0.8);
            W_FireRocket();
            if (self.health > 0) self.think = grunty_rocket;
            #ifdef VERBOSE_GRUNTY
            sprint(self.real_owner, PRINT_HIGH, "Attempting to rocket...\n");
            #endif
            return;
        }
	}
    if (dist < 1400 && self.weapons_carried & WEAP_ROCKET_LAUNCHER && random() > 0.75)
	{
        if (self.ammo_rockets > 1)
        {
            self.current_weapon = WEAP_ROCKET_LAUNCHER;
            //self.attack_finished = time + 0.8;
            Attack_Finished(0.8);
            W_FireRocket();
            if (self.health > 0) self.think = grunty_rocket;
            return;
        }
    }
	if (dist < 2000 && self.weapons_carried & WEAP_ROCKET_LAUNCHER && self.ammo_rockets > 1 && random() > 0.98)
	{
		self.current_weapon = WEAP_ROCKET_LAUNCHER;
		//self.attack_finished = time + 0.8;
        Attack_Finished(0.8);
		W_FireRocket();
		if (self.health > 0) self.think = grunty_rocket;
		return;
    }

    //- OfN 
	if (dist < 400 && self.weapons_carried & WEAP_SHOTGUN && self.ammo_shells > 0)
	{
		//if (self.ammo_shells > 0)
		//{
			self.current_weapon = WEAP_SHOTGUN;
			//self.attack_finished = time + 0.5; // TODO: MAKE ATTACK FINISHED
            Attack_Finished(0.5);
			W_FireShotgun();
			if (self.health > 0) self.think = grunty_shotgun;
			#ifdef VERBOSE_GRUNTY
			sprint(self.real_owner, PRINT_HIGH, "Attempting to shotgun...\n");
			#endif
//            RPrint("SHOTGUN\n");
            return;
		//}
	}
	if (dist < 2000 && self.weapons_carried & WEAP_NAILGUN && self.ammo_nails > 0)
	{
		//if (self.ammo_nails > 0)
		//{
			self.current_weapon = WEAP_NAILGUN;
			//self.attack_finished = time + 0.2;//+ 0.1;
            Attack_Finished(0.2);
			if (self.health > 0) self.think = grunty_nail1;
			#ifdef VERBOSE_GRUNTY
			sprint(self.real_owner, PRINT_HIGH, "Attempting to nail...\n");
			#endif
//            RPrint("NAIL\n");
			return;
		//}
	}
	else
	{
		// Do whatever you want
		self.effects = self.effects - (self.effects & EF_DIMLIGHT);
		//self.walkframe = 0; ofn
		if (self.health > 0) self.think = grunty_run;
		#ifdef VERBOSE_GRUNTY
		sprint(self.real_owner, PRINT_HIGH, "GruntyCheckFire defaulting to none...\n");
		#endif	
//        RPrint("NONE\n");
	}
};
float statehack;

void() GRun =
{
	local float dist;
	local vector loc;

	// ok, we're trying to get somewhere
	// first look at where we are trying to go
	// Version 1.0 of GruntyLead(tm)
	if (self.enemy)
	{
		local vector pvel;
		
		if (self.enemy.classname == "player")
			pvel = self.enemy.velocity;
		else if (self.enemy.classname == "monster_army")
		{
			makevectors(self.enemy.angles);
			pvel = self.enemy.velocity + v_forward * self.enemy.custom_speed * 20;
		}
		else
			pvel = '0 0 0';
		if (ReturnWeaponVelocity() == 99999999)
			pvel = '0 0 0';
		loc = self.enemy.origin + pvel * (vlen(self.enemy.origin - self.origin) / ReturnWeaponVelocity());
		self.ideal_yaw = vectoyaw(loc - self.origin);
		self.ideal_yaw = anglemod(self.ideal_yaw);
	}
	else
	{
        if (self.goalentity) //- OfN - dont change yaw if we r not after a goal
        {
            self.ideal_yaw = vectoyaw(self.goalentity.origin - self.origin);
		    self.ideal_yaw = anglemod(self.ideal_yaw);
        }
	}
	
	ChangeYaw();

	// makevectors sets the global vars v_forward, v_right and v_up
	makevectors(self.angles);

	dist = self.custom_speed;
		
	if (self.tfstate & TFSTATE_TRANQUILISED)
		dist = dist * (AI_TRANQ_FACTOR_UP / AI_TRANQ_FACTOR_DN); //Tranq
	
	/* - OfN -if (self.enemy.classname == "monster_shambler" || self.enemy.weapons_carried & WEAP_ASSAULT_CANNON)
		if (self.has_teleporter == 0)
			self.has_teleporter = 3;*/

	// 0% > 75% health, 50% < 25% health, range in between
	local float chance = (BOUND (0.5, 1 - (self.health / self.max_health), 0.75) - 0.50) / 8;
//	dprint ("retreat chance: " + ftos (chance) + "\n");
	if (random () < chance)
		self.has_teleporter = 3; // we're getting wasted, better retreat
	
	if (random () < 0.02) // to prevent us from strafing or whatever forever.
		self.has_teleporter = 0;
	
	// Test to see if we want to do an evasive maneuver
	// If we're already doing a move, don't do another
	if (self.has_teleporter == -1) // left dodge move
		botmovedist(vectoyaw(v_right * -1), dist);// move left
	
	else if (self.has_teleporter == 1) // right dodge move
		botmovedist(vectoyaw(v_right), dist); // move right
	
	else if (self.has_teleporter == 2) // jump!
	{
		if (self.flags & FL_ONGROUND) // only jump if we're on the ground
		{
			self.velocity_z = self.velocity_z + 270; // boing
			sound (self, CHAN_BODY, "player/plyrjmp8.wav", 1, ATTN_NORM);
		}
		self.has_teleporter = 0; // don't jump
	}
	else if (self.has_teleporter == 3) //(backwards)// HE'S AFTER US, RUN!@#$ HE WILL KILL US ALL!!@
		botmovedist(vectoyaw(v_forward), dist * -1); // flee, I tell you!
	
	else if (self.has_teleporter == 0) // no evasive, standard move (forward)
	{
		//- OfN - force move to forward if we r going to a last seen enemy waypoint
        if (self.goalentity == self.demon_one && self.demon_one)
        {
            self.has_teleporter = 0;
            botmovedist(vectoyaw(v_forward), dist); // move
        }
		else
		{
			if (self.is_malfunctioning == 2 && self.enemy) {
				local float dist = vlen (self.origin - self.enemy.origin);
				local float desired_min, desired_max;

				if (self.army_ready == 0) {
					desired_min = ARMY_RANGE_NONE_MIN;
					desired_max = ARMY_RANGE_NONE_MAX;
				} else if (self.army_ready == 1) {
					desired_min = ARMY_RANGE_SMALL_MIN;
					desired_max = ARMY_RANGE_SMALL_MAX;
				} else if (self.army_ready == 2) {
					desired_min = ARMY_RANGE_MEDIUM_MIN;
					desired_max = ARMY_RANGE_MEDIUM_MAX;
				} else { // if (self.army_ready == 3)
					desired_min = ARMY_RANGE_LONG_MIN;
					desired_max = ARMY_RANGE_LONG_MAX;
				}

				local string diststring = "dist: " + ftos (dist)
										  + "  min: " + ftos (desired_min)
										  + "  max: " + ftos (desired_max)
										  + "  action: ";
				if (dist < desired_min) {
					if (statehack != -1) {
//						dprint ("me: " + vtos (self.origin) + "  goal: " + vtos (self.goalentity.origin) + "\n");
						RPrint (diststring + "back off\n");
						statehack = -1;
					}
					botmovedist (vectoyaw (v_forward), dist * -0.1); // back off
				} else if (dist > desired_max) {
                    if (statehack != 1) {
//                        dprint ("me: " + vtos (self.origin) + "  goal: " + vtos (self.goalentity.origin) + "\n");
                        RPrint (diststring + "close in\n");
                        statehack = 1;
                    }
					botmovedist (vectoyaw (v_forward), dist); // close in
				} else {
                    if (statehack != 0) {
//                        dprint ("me: " + vtos (self.origin) + "  goal: " + vtos (self.goalentity.origin) + "\n");
                        RPrint (diststring + "do nothing *cough*\n");
                        statehack = 0;
                    }
					if (random () < 0.1)
						botmovedist (vectoyaw (v_forward), dist);
				}
			} else
		        botmovedist(vectoyaw(v_forward), dist); // move

			if (random() < 0.05 && self.enemy)				// if we get a score
			{															// of one on a d20,
				local float r;											// special
				r = random();
				if (r < 0.25)
					self.has_teleporter = -1; // left
				else if (r < 0.5)
					self.has_teleporter = 1; // right
				else if (r < 0.75)
					self.has_teleporter = 2; // jump!
				else
					self.has_teleporter = 3; // italian style!
			}
		}
	}
};

void (float angle, float dist) botmovedist =
{ 
    
    local float success;
	
    //- OfN - Don't annoy owner if following him! heh
    #ifndef MAD_GRUNTY
    if (self.goalentity==self.real_owner) //if in "follow me!" state...
    {           
        local float dist2;
    
        dist2 = vlen(self.origin - self.real_owner.origin);
    
        if (dist2<64)
        {
            stand_frames();
            self.has_cheated = time + 0.5;
            walkmove(angle, 0);
            return;
        }
    }///////////////////////////////*/
    #endif

    /*if (pointcontents(self.origin) == CONTENT_EMPTY) // We're in open air
	{*/

        success = walkmove(angle, dist);
        
        if (success) // We walked sucessfully, woohoo
        {
            self.has_cheated = time + 0.5;
            return;
        }
        else // We just ran into something...
        {
            if (self.is_toffingadet==1) //if we r on stop on obstacles mode
            {
                self.has_cheated = time + 0.5;
                if (self.attack_finished < time) stand_frames();
                return;
            }
            
            if (self.has_cheated > time)
                return;
            self.has_teleporter = 0; // abort dodging
            makevectors(self.angles);
            // First, we'll see if jumping would be suitable
            if (self.flags & FL_ONGROUND) // don't bother if we aren't on the ground
            {
                traceline(self.origin + '0 0 20', self.origin + '0 0 20' + v_forward * 10, FALSE, self);
                if (trace_fraction == 1.0)
                {
                    self.velocity = self.velocity + v_forward * self.custom_speed * 20;
                    self.velocity_z = self.velocity_z + 270;
                    sound (self, CHAN_BODY, "player/plyrjmp8.wav", 1, ATTN_NORM);
                    self.has_cheated = time + 0.5;
                    return;
                }
            }
        }
    /*}
    else
	{
		self.velocity = normalize(self.goalentity.origin - self.origin) * self.custom_speed * 20;
		if (self.teleport_time < time)
			CheckWaterJump();
	}*/

};

void () Grunty_StateInv =
{
	local string talk;
	
	talk = ftos(self.health);
	sprint(self.real_owner, PRINT_HIGH, "Your ������� has ");
	sprint(self.real_owner, PRINT_HIGH, talk);
	talk = ftos(self.max_health);
	sprint(self.real_owner, PRINT_HIGH, "�");
	sprint(self.real_owner, PRINT_HIGH, talk);
	sprint(self.real_owner, PRINT_HIGH, " ������");
	if (self.weapons_carried & WEAP_AXE)
		if (self.cutf_items & CUTF_KNIFE)
			sprint(self.real_owner, PRINT_HIGH, ", a combat knife");
		else
			sprint(self.real_owner, PRINT_HIGH, ", an axe");
	if (self.weapons_carried & WEAP_SHOTGUN)
		sprint(self.real_owner, PRINT_HIGH, ", a shotgun");
	if (self.weapons_carried & WEAP_NAILGUN)
		sprint(self.real_owner, PRINT_HIGH, ", a nailgun");
	if (self.weapons_carried & WEAP_SUPER_SHOTGUN)
		sprint(self.real_owner, PRINT_HIGH, ", a double barreled shotgun");
	if (self.weapons_carried & WEAP_ROCKET_LAUNCHER)
		sprint(self.real_owner, PRINT_HIGH, ", a rocket launcher");
	if (self.tf_items & NIT_SCANNER)
		sprint(self.real_owner, PRINT_HIGH, ", a scanner");
	talk = ftos(self.custom_speed * 20);
	sprint(self.real_owner, PRINT_HIGH, ", moves at speed ");
	sprint(self.real_owner, PRINT_HIGH, talk);
	talk = ftos(self.money);
	sprint(self.real_owner, PRINT_HIGH, " with $");
	sprint(self.real_owner, PRINT_HIGH, talk);
	sprint(self.real_owner, PRINT_HIGH, "\n");

    sprint(self.real_owner, PRINT_HIGH, "����: ");

    talk = ftos(self.ammo_shells);
	sprint(self.real_owner, PRINT_HIGH, talk);
    sprint(self.real_owner, PRINT_HIGH, " shells, ");

        talk = ftos(self.ammo_nails);
	sprint(self.real_owner, PRINT_HIGH, talk);
    sprint(self.real_owner, PRINT_HIGH, " nails and ");

    /*    talk = ftos(self.ammo_cells);
	sprint(self.real_owner, PRINT_HIGH, talk);
    sprint(self.real_owner, PRINT_HIGH, " cells\n");*/

    talk = ftos(self.ammo_rockets);
	sprint(self.real_owner, PRINT_HIGH, talk);
    sprint(self.real_owner, PRINT_HIGH, " rockets\n");
};

/* ---------------------------------------------------------------------------------- */
//						GRUNTY - PART SIX
//					Time to Die
//
//						Grunty sucks it down
/* ---------------------------------------------------------------------------------- */

void () gruntyDead =
{
	self.think = SUB_Remove;
    self.nextthink = time + 40 + 20*random();	
};


void()	grunty_diea1	=	[	$deatha1,	grunty_diea2	] {};
void()	grunty_diea2	=	[	$deatha2,	grunty_diea3	] {};
void()	grunty_diea3	=	[	$deatha3,	grunty_diea4	] {};
void()	grunty_diea4	=	[	$deatha4,	grunty_diea5	] {};
void()	grunty_diea5	=	[	$deatha5,	grunty_diea6	] {};
void()	grunty_diea6	=	[	$deatha6,	grunty_diea7	] {};
void()	grunty_diea7	=	[	$deatha7,	grunty_diea8	] {};
void()	grunty_diea8	=	[	$deatha8,	grunty_diea9	] {};
void()	grunty_diea9	=	[	$deatha9,	grunty_diea10	] {};
void()	grunty_diea10	=	[	$deatha10,	grunty_diea11	] {};
void()	grunty_diea11	=	[	$deatha11,	grunty_diea11 ] {gruntyDead();};

void()	grunty_dieb1	=	[	$deathb1,	grunty_dieb2	] {};
void()	grunty_dieb2	=	[	$deathb2,	grunty_dieb3	] {};
void()	grunty_dieb3	=	[	$deathb3,	grunty_dieb4	] {};
void()	grunty_dieb4	=	[	$deathb4,	grunty_dieb5	] {};
void()	grunty_dieb5	=	[	$deathb5,	grunty_dieb6	] {};
void()	grunty_dieb6	=	[	$deathb6,	grunty_dieb7	] {};
void()	grunty_dieb7	=	[	$deathb7,	grunty_dieb8	] {};
void()	grunty_dieb8	=	[	$deathb8,	grunty_dieb9	] {};
void()	grunty_dieb9	=	[	$deathb9,	grunty_dieb9	] {gruntyDead();};

void()	grunty_diec1	=	[	$deathc1,	grunty_diec2	] {};
void()	grunty_diec2	=	[	$deathc2,	grunty_diec3	] {};
void()	grunty_diec3	=	[	$deathc3,	grunty_diec4	] {};
void()	grunty_diec4	=	[	$deathc4,	grunty_diec5	] {};
void()	grunty_diec5	=	[	$deathc5,	grunty_diec6	] {};
void()	grunty_diec6	=	[	$deathc6,	grunty_diec7	] {};
void()	grunty_diec7	=	[	$deathc7,	grunty_diec8	] {};
void()	grunty_diec8	=	[	$deathc8,	grunty_diec9	] {};
void()	grunty_diec9	=	[	$deathc9,	grunty_diec10	] {};
void()	grunty_diec10	=	[	$deathc10,	grunty_diec11	] {};
void()	grunty_diec11	=	[	$deathc11,	grunty_diec12	] {};
void()	grunty_diec12	=	[	$deathc12,	grunty_diec13	] {};
void()	grunty_diec13	=	[	$deathc13,	grunty_diec14	] {};
void()	grunty_diec14	=	[	$deathc14,	grunty_diec15	] {};
void()	grunty_diec15	=	[	$deathc15,	grunty_diec15 ] {gruntyDead();};

void()	grunty_died1	=	[	$deathd1,	grunty_died2	] {};
void()	grunty_died2	=	[	$deathd2,	grunty_died3	] {};
void()	grunty_died3	=	[	$deathd3,	grunty_died4	] {};
void()	grunty_died4	=	[	$deathd4,	grunty_died5	] {};
void()	grunty_died5	=	[	$deathd5,	grunty_died6	] {};
void()	grunty_died6	=	[	$deathd6,	grunty_died7	] {};
void()	grunty_died7	=	[	$deathd7,	grunty_died8	] {};
void()	grunty_died8	=	[	$deathd8,	grunty_died9	] {};
void()	grunty_died9	=	[	$deathd9,	grunty_died9	] {gruntyDead();};

void()	grunty_diee1	=	[	$deathe1,	grunty_diee2	] {};
void()	grunty_diee2	=	[	$deathe2,	grunty_diee3	] {};
void()	grunty_diee3	=	[	$deathe3,	grunty_diee4	] {};
void()	grunty_diee4	=	[	$deathe4,	grunty_diee5	] {};
void()	grunty_diee5	=	[	$deathe5,	grunty_diee6	] {};
void()	grunty_diee6	=	[	$deathe6,	grunty_diee7	] {};
void()	grunty_diee7	=	[	$deathe7,	grunty_diee8	] {};
void()	grunty_diee8	=	[	$deathe8,	grunty_diee9	] {};
void()	grunty_diee9	=	[	$deathe9,	grunty_diee9	] {gruntyDead();};

void()	grunty_die_ax1	=	[	$axdeth1,	grunty_die_ax2	] {};
void()	grunty_die_ax2	=	[	$axdeth2,	grunty_die_ax3	] {};
void()	grunty_die_ax3	=	[	$axdeth3,	grunty_die_ax4	] {};
void()	grunty_die_ax4	=	[	$axdeth4,	grunty_die_ax5	] {};
void()	grunty_die_ax5	=	[	$axdeth5,	grunty_die_ax6	] {};
void()	grunty_die_ax6	=	[	$axdeth6,	grunty_die_ax7	] {};
void()	grunty_die_ax7	=	[	$axdeth7,	grunty_die_ax8	] {};
void()	grunty_die_ax8	=	[	$axdeth8,	grunty_die_ax9	] {};
void()	grunty_die_ax9	=	[	$axdeth9,	grunty_die_ax9	] {gruntyDead();};

// For now we'll just have this
// Later we'll have hard and soft deaths
void() ArmyDeathSound;

void() custom_grunt_die = 
{
    self.effects=0;
    
    if (self.real_owner.classname == "player")
	{
		sprint(self.real_owner,PRINT_HIGH,"Your soldier is dead.\n");
		self.real_owner.job = self.real_owner.job - (self.real_owner.job & JOB_DEMON_OUT);
		self.real_owner.job_finished = time + 2; //Can't summon streams of demons SB can so
        self.real_owner.demon_one = NIL;
    	
        if (self.martyr_enemy)
        RemoveWaypoint(self.martyr_enemy, self);
        
        if (self.demon_one)
        RemoveWaypoint(self.demon_one, self);
        
        if (self.demon_two)
        RemoveWaypoint(self.demon_two, self);

        local entity oself;
        oself=self;
        self=self.real_owner;
        SetArmyTimer();
        self=oself;
	}
    
    if (self.health < -30)
    {        
        sound (self, CHAN_VOICE, "player/udeath.wav", 1, ATTN_MONSTERDIE);

        ThrowGib ("progs/h_guard.mdl", self.health);
	    ThrowGib ("progs/gib1.mdl", self.health);
	    ThrowGib ("progs/gib2.mdl", self.health);
        ThrowGib ("progs/gib3.mdl", self.health);
	    //ThrowGib ("progs/gib3.mdl", self.health);
	    dremove(self);
        return;
    }

    self.solid = SOLID_NOT;

    ArmyDeathSound();
    self.classname = "monster_corpse";
    
    self.think=NIL;
    self.touch=NIL;
    
    if (isMelee())
    {
        grunty_die_ax1();
    }
    else
    {
        local float r;
        r=random();
        if (r<0.2)
          grunty_diea1();
        else if (r<0.4)
          grunty_dieb1();
        else if (r<0.6)
          grunty_diec1();
        else if (r<0.8)
          grunty_died1();
        else grunty_diee1();
    }

};

void() ArmyDeathSound =
{
	local float		rs;

	// water death sounds
	if (self.waterlevel == 3)
	{
		DeathBubbles(10);
        sound (self, CHAN_VOICE, "player/h2odeath.wav", 1, ATTN_MONSTERDIE);
		return;
	}
	
	rs = rint ((random() * 4) + 1);
	if (rs == 1)
		self.noise = "player/death1.wav";
	if (rs == 2)
		self.noise = "player/death2.wav";
	if (rs == 3)
		self.noise = "player/death3.wav";
	if (rs == 4)
		self.noise = "player/death4.wav";
	if (rs == 5)
		self.noise = "player/death5.wav";

	sound (self, CHAN_VOICE, self.noise, 1, ATTN_MONSTERDIE);
};

// We'll include pain here

void() grunty_pain1 = [$pain1, grunty_pain2] {PainSound();self.weaponframe=0;self.effects=0;};
void() grunty_pain2 = [$pain2, grunty_pain3] {};
void() grunty_pain3 = [$pain3, grunty_pain4] {};
void() grunty_pain4 = [$pain4, grunty_pain5] {};
void() grunty_pain5 = [$pain5, grunty_pain6] {};
void() grunty_pain6 = [$pain6, grunty_run] {self.walkframe = 0;};

void() grunty_axepain1 =	[	$axpain1,	grunty_axepain2	] {PainSound();self.weaponframe=0;self.effects=0;};
void() grunty_axepain2 =	[	$axpain2,	grunty_axepain3	] {};
void() grunty_axepain3 =	[	$axpain3,	grunty_axepain4	] {};
void() grunty_axepain4 =	[	$axpain4,	grunty_axepain5	] {};
void() grunty_axepain5 =	[	$axpain5,	grunty_axepain6	] {};
void() grunty_axepain6 =	[	$axpain6,	grunty_run		] 
{
    self.walkframe = 0;                                     // - 1)/16);
    self.pain_finished = time + random()*1.5*(self.has_sensor/17);    
};

void(entity attacker, float damage) grunty_pain =
{
    //self.real_owner.StatusRefreshTime = time + 0.2;
	//self.real_owner.StatusBarScreen = 3; 

#ifdef VERBOSE_GRUNTY
//    RPrint(self.real_owner, PRINT_HIGH, "Grunty PAIN!\n");
#endif

    if (self.pain_finished > time)
		return;
    
    //if (random()*40 > damage) // random()*40+40*((self.has_sensor-1)/16)
    if (random()*40 + 40*((self.has_sensor - 1)/16) > damage)
		return;	

    if (isMelee())
		grunty_axepain1();
	else
		grunty_pain1();
};

/* ---------------------------------------------------------------------------------- */
//						GRUNTY - PART SEVEN
//					Moving with style
//
//						Grunty needs waypoints
/* ---------------------------------------------------------------------------------- */

/*void() Grunty_PhysUpdate =
{
	setorigin(self, self.origin + self.velocity * 0.05);
};*/

void() Waypoint_Touch =
{
	// If the toucher isn't a grunt, return.
	if (other.classname != "monster_army")
		return;

	// If the toucher isn't trying to reach us, return.
	// We could do this with self.owner but it might accidentally touch a home waypoint
	// making this a bad option.
	if (other.goalentity != self)
		return;

    if (self.has_sensor == WAYPOINT_TYPE_ENEMYLASTSEEN)
    {        
        if (self.owner.demon_one.demon_one == self)
           self.owner.demon_one.demon_one = NIL;   
       
        dremove(self);
        return;
    }

    if (self.has_sensor == WAYPOINT_TYPE_PRIMARY)
    {        
        self.owner.demon_one.job=2;
    }
    else if (self.has_sensor == WAYPOINT_TYPE_SECONDARY)
    {        
        self.owner.demon_one.job=1;
    }

    self.num_mines = time + self.has_holo; // reset entity live time
    //has_holo is the asigned life time in createwaypoint 


    other.goalentity = self.goalentity; // Set grunty's movement target
	//if (!self.has_sensor) // If we're non-permanent (ie. a temporary goal)
	//	dremove(self); // remove ourself
};

void() Waypoint_DoNothing = // A waypoint checks for redundancy
{
	self.nextthink = time + 5; // next check in 5 seconds

    //if (self.owner.classname!="player") return; //- OfN - NO!! remove it..

    //- OfN - remove waypoint entity?
    if (self.owner.has_disconnected 
        || !(self.owner.job & JOB_ARMY)
        || !(self.owner.job & JOB_DEMON_OUT)
        || self.owner.classname!="player")
    {
       dremove(self); // no pointer set to NIL needed cause soldier shouldnt be present on game
       return;
    }

    if (self.num_mines < time) // if it wasnt touched for specified life-time remove it
    {
       if (self.has_sensor == WAYPOINT_TYPE_PRIMARY
        && self.owner.demon_one.martyr_enemy == self)
           self.owner.demon_one.martyr_enemy = NIL;
       if (self.has_sensor == WAYPOINT_TYPE_SECONDARY
        && self.owner.demon_one.demon_two == self)
           self.owner.demon_one.demon_two = NIL;
       if (self.has_sensor == WAYPOINT_TYPE_ENEMYLASTSEEN
        && self.owner.demon_one.demon_one == self)
           self.owner.demon_one.demon_one = NIL;

       dremove(self);
       return;
    }
};

/* CreateWaypoint
 * 
 * Creates a waypoint for use by grunty
 * waypoint.goalentity = next target
 * waypoint.owner = owner grunt
 */

entity (vector location, float life, float type) CreateWaypoint =
{
	newmis = spawn(); // Spawn a new entity (the waypoint)
	
    //newmis.has_sensor = 1; // has_sensor means we're a permanent waypoint, which always are
	// - OfN now has_sensor holds the type of waypoint, primary, second etc..

 // set the origin.
    setsize (newmis, '-20 -20 -20', '20 20 20'); // set the size of the waypoint
    
    newmis.solid = SOLID_TRIGGER; // We're a solid trigger type - touch, but don't block

	//newmis.netname = "grunty_waypoint"; // set the name so grunty can talk about us
    newmis.classname = "grunty_waypoint";
	newmis.touch = Waypoint_Touch; // Set the waypoint's touch function
	newmis.think = Waypoint_DoNothing; // Set the waypoint's redundancy check function
	newmis.nextthink = time + 2; // Think in 2 seconds

    if (self.classname=="player")
        newmis.owner=self;
    else if (self.classname=="monster_army")
        newmis.owner=self.real_owner;

    newmis.num_mines = time + life; //WAYPOINT_LIFE;
    newmis.has_holo = life;
    newmis.has_sensor = type; // sets the type of waypoint to has_sensor
    
    setorigin(newmis, location);
    
	return newmis; // return the entity
};

/* ---------------------------------------------------------------------------------- */
//						GRUNTY - PART EIGHT
//					Life and Happiness
//
//						Grunty earns hard cash
/* ---------------------------------------------------------------------------------- */

void (entity targ) GetRank =
{
	if (targ.has_sensor == 1)
		targ.undercover_name = "Private Class III ";
	else if (targ.has_sensor == 2)
		targ.undercover_name = "Private Class II ";
	else if (targ.has_sensor == 3)
		targ.undercover_name = "Private Class I ";
	else if (targ.has_sensor == 4)
		targ.undercover_name = "Lance Corporal ";
	else if (targ.has_sensor == 5)
		targ.undercover_name = "Corporal ";
	else if (targ.has_sensor == 6)
		targ.undercover_name = "Sergeant ";
	else if (targ.has_sensor == 7)
		targ.undercover_name = "Staff Sergeant ";
	else if (targ.has_sensor == 8)
		targ.undercover_name = "Warrant Officer II ";
	else if (targ.has_sensor == 9)
		targ.undercover_name = "Warrant Officer I ";
	else if (targ.has_sensor == 10)
		targ.undercover_name = "Lieutenant Class II ";
	else if (targ.has_sensor == 11)
		targ.undercover_name = "Lieutenant Class I ";
	else if (targ.has_sensor == 12)
		targ.undercover_name = "Captain ";
	else if (targ.has_sensor == 13)
		targ.undercover_name = "Major ";
	else if (targ.has_sensor == 14)
		targ.undercover_name = "Colonel ";
	else if (targ.has_sensor == 15)
		targ.undercover_name = "Brigadier ";
	else if (targ.has_sensor == 16)
		targ.undercover_name = "General ";
	else if (targ.has_sensor == 17)
		targ.undercover_name = "Field Marshal ";
	else
		targ.undercover_name = "Error producer ";
};

void() Grunty_Check_Frags =
{
	if (self.frags >= self.has_sentry && self.has_sensor < 17)
	{
		self.has_tesla = self.has_tesla + 0.1;
		self.has_sensor = self.has_sensor + 1;
		self.has_sentry = self.has_sentry + 2;
		//self.health = self.health + self.has_sensor * 25; //-this before
        self.health = self.health + 20 + self.has_sensor * 12; //-this now
		self.max_health = self.max_health + 20 + self.has_sensor * 16;//-was 25

        //ofn
	    if (self.max_health > GRUNT_MAX_HP)
   		 	self.max_health = GRUNT_MAX_HP;

		if (self.health > self.max_health)
			self.health = self.max_health;

        custom_demon_name(self);
        
        GetRank(self);
		
        sprint(self.real_owner, PRINT_HIGH, "Your soldier, ");
		sprint(self.real_owner, PRINT_HIGH, self.netname);
		sprint(self.real_owner, PRINT_HIGH, ", has reached the rank of ");
		sprint(self.real_owner, PRINT_HIGH, self.undercover_name);
		sprint(self.real_owner, PRINT_HIGH, "\n");
	}
};

float () ReturnWeaponVelocity =
{
	if (self.current_weapon == WEAP_NAILGUN)
//		return 1250;
        return 99999999;
	else if (self.current_weapon == WEAP_ROCKET_LAUNCHER)
		//return 1100;
        return 99999999;
	else
		return 99999999;
};

#define GRUNTY_COST_HEALTHPACK		35 // was 20
#define GRUNTY_COST_SHOTGUN			100 // was 50
#define GRUNTY_COST_NAILGUN			300 // was 300
#define GRUNTY_COST_SUPER_SHOTGUN	500 // was 500
#define GRUNTY_COST_KNIFE			500 // was 700 // then 400
#define GRUNTY_COST_ROCKET_LAUNCHER 3000 // was 3000 // then 2000
#define GRUNTY_COST_SCANNER			1200 // was 1000 // then 800
#define GRUNTY_COST_SHELL			4 // was 2
#define GRUNTY_COST_NAIL			2 // was 1
#define GRUNTY_COST_ROCKET			12 // was 3
#define GRUNTY_COST_CELL			2
#define GRUNTY_COST_MAXHEALTH		120 // was 100
#define GRUNTY_COST_SPEED			800 // was 400 // then 400
#define GRUNTY_MAX_SPEED			13 //- was 30

void () GruntyPayThink =
{
	local float chance;
	local float r;
	local float spendalloc;
	// If it's salary time, draw pay
	if (self.respawn_time < time)
	{
		GruntyDrawPay();
		self.respawn_time = time + 20;
	}
	// Grunty is on a SHOPPING SPREE!
	if (!(self.weapons_carried & WEAP_ROCKET_LAUNCHER))
		spendalloc = self.money * 0.2; // amount to spend on health/speed upgrades
	else
		spendalloc = self.money;
	// Consider buying more max health      )//- added
	if (spendalloc >= GRUNTY_COST_MAXHEALTH && self.max_health < GRUNT_MAX_HP)
	{
		if (self.weapons_carried & WEAP_ROCKET_LAUNCHER)
			chance = 0.75; //- was 0.9
		else
			chance = 0.25;

		r = random();
		if (r < chance)
		{
			self.max_health = self.max_health + 25;
			self.money = self.money - GRUNTY_COST_MAXHEALTH;
			spendalloc = spendalloc - GRUNTY_COST_MAXHEALTH;
            //  	#ifdef VERBOSE_GRUNTY
            PrintFromSoldier(self,self.real_owner,"my health maximum has been increased!\n");				
//		    sprint(self.real_owner, PRINT_HIGH, "BUYS more MAX HP\n");
            //    #endif
		}
	}
	// Consider buying a health pack
	if (self.health < self.max_health && self.money >= GRUNTY_COST_HEALTHPACK)
	{
		local float loops = 0, bought;
		bought=0;

		while (loops < (0.75+(self.has_sensor/4)) && self.money >= GRUNTY_COST_HEALTHPACK && self.max_health > self.health)
		{
			if (self.health < self.max_health - 25)
				chance = 1;
			else if (self.health < self.max_health - 15)
				chance = 0.25;
			else
				chance = 0.1;
				
			r = random();
			if (r < chance)
			{
				self.health = self.health + 25;
				if (self.health > self.max_health)
					self.health = self.max_health;
				self.money = self.money - GRUNTY_COST_HEALTHPACK;
              //                 	#ifdef VERBOSE_GRUNTY
                				
//                sprint(self.real_owner, PRINT_HIGH, "BUYS A HEALTHPACK\n");
                //PrintFromSoldier(self,self.real_owner,"i've bought a healthpack.\n");
                bought=bought+1;
                //                  #endif
			}
			loops = loops + 1;
		}

        if (bought>0)
        {
            if (bought==1) 
                PrintFromSoldier(self,self.real_owner,"i've bought a healthpack.\n");
            else
            {
                local string tempst;
                tempst=ftos(bought);                
                PrintFromSoldier(self,self.real_owner,"i've bought ");
                sprint(self.real_owner,PRINT_HIGH,tempst);
                sprint(self.real_owner,PRINT_HIGH," healthpacks.\n");

            }
        }
       
	}
	
	// Consider buying a shotgun
	if (!(self.weapons_carried & WEAP_SHOTGUN))
		if (self.money >= GRUNTY_COST_SHOTGUN)
		{
			r = random();
			if (r < 0.5)
			{
				self.weapons_carried = self.weapons_carried | WEAP_SHOTGUN;
				self.money = self.money - GRUNTY_COST_SHOTGUN;
                PrintFromSoldier(self,self.real_owner,"i have bought a shotgun.\n");				
				//sprint(self.real_owner, PRINT_HIGH, "Your soldier has just purchased a shotgun.\n");
			}
		}
	// Consider buying a knife
	if (!(self.cutf_items & CUTF_KNIFE))
		if (self.money >= GRUNTY_COST_KNIFE)
		{
			r = random();
			if (r < 0.5)
			{
				self.cutf_items = self.cutf_items | CUTF_KNIFE;
				self.money = self.money - GRUNTY_COST_KNIFE;
                PrintFromSoldier(self,self.real_owner,"i have bought a knife.\n");				
				//sprint(self.real_owner, PRINT_HIGH, "Your soldier has just purchased a knife.\n");
			}
		}
	// Consider buying a nailgun
	if (!(self.weapons_carried & WEAP_NAILGUN))
		if (self.money >= GRUNTY_COST_NAILGUN)
		{
			r = random();
			if (r < 0.25)
			{
				self.weapons_carried = self.weapons_carried | WEAP_NAILGUN;
				self.money = self.money - GRUNTY_COST_NAILGUN;
                PrintFromSoldier(self,self.real_owner,"i now own a nailgun.\n");				
				//sprint(self.real_owner, PRINT_HIGH, "Your soldier now owns a nailgun.\n");
			}
		}
	// Consider buying a double barrel
	if (!(self.weapons_carried & WEAP_SUPER_SHOTGUN))
		if (self.money >= GRUNTY_COST_SUPER_SHOTGUN)
		{
			r = random();
			if (r < 0.25)
			{
				self.weapons_carried = self.weapons_carried | WEAP_SUPER_SHOTGUN;
				self.money = self.money - GRUNTY_COST_SUPER_SHOTGUN;
                PrintFromSoldier(self,self.real_owner,"i've just bought a super shotgun.\n");				
				//sprint(self.real_owner, PRINT_HIGH, "Your soldier just bought a super shotgun.\n");
				
			}
		}
	// Consider buying a scanner
	if (!(self.tf_items & NIT_SCANNER))
		if (self.money >= GRUNTY_COST_SCANNER)
		{
			r = random();
			if (r < 0.15)
			{
				self.tf_items = self.tf_items | NIT_SCANNER;
				self.money = self.money - GRUNTY_COST_SCANNER;
                PrintFromSoldier(self,self.real_owner,"i now own a scanner.\n");				
                //sprint(self.real_owner, PRINT_HIGH, "Your soldier now owns a scanner.\n");
			}	
		}
	// Consider buying...a rocket launcher.
	if (!(self.weapons_carried & WEAP_ROCKET_LAUNCHER))
		if (self.money >= GRUNTY_COST_ROCKET_LAUNCHER)
		{
			r = random();
			if (r < 0.3) //- was 0.2
			{
				self.weapons_carried = self.weapons_carried | WEAP_ROCKET_LAUNCHER;
				self.money = self.money - GRUNTY_COST_ROCKET_LAUNCHER;
                PrintFromSoldier(self,self.real_owner,"i have bought a ROCKET LAUNCHER!! hehe\n");				
                //sprint(self.real_owner, PRINT_HIGH, "Your soldier is now the proud owner of a brand new rocket launcher.\n");
			}
		}
		
	// Buy ammo
	if ((self.weapons_carried & WEAP_ROCKET_LAUNCHER) && self.ammo_rockets < self.maxammo_rockets)
	{
		local float rocketstobuy;
		
		rocketstobuy = self.maxammo_rockets - self.ammo_rockets;
		if (rocketstobuy > 4)
			rocketstobuy = 4;
		if (self.money >= rocketstobuy * GRUNTY_COST_ROCKET)
		{
			self.ammo_rockets = self.ammo_rockets + rocketstobuy;
			self.money = self.money - rocketstobuy * GRUNTY_COST_ROCKET;
            
            //    #ifdef VERBOSE_GRUNTY
            PrintFromSoldier(self,self.real_owner,"i've bought some rockets.\n");
            //sprint(self.real_owner, PRINT_HIGH, "BUYS SOME ROCKETS\n");
            //    #endif
		}
	}
	
    if ((self.weapons_carried & WEAP_NAILGUN) && self.ammo_nails < self.maxammo_nails)
	{
		local float nailstobuy;
		
		nailstobuy = self.maxammo_nails - self.ammo_nails;
		if (nailstobuy > 50) // was 50
			nailstobuy = 50;
		if (self.money >= nailstobuy * GRUNTY_COST_NAIL)
		{
			self.ammo_nails = self.ammo_nails + nailstobuy;
			self.money = self.money - nailstobuy * GRUNTY_COST_NAIL;
            //   	#ifdef VERBOSE_GRUNTY
            PrintFromSoldier(self,self.real_owner,"i've bought some nails.\n");
            //sprint(self.real_owner, PRINT_HIGH, "BUYS SOME NAILS\n");
            //    #endif

		}
	}
    
    if ((self.weapons_carried & WEAP_SHOTGUN ||
		self.weapons_carried & WEAP_SUPER_SHOTGUN)
		&& self.ammo_shells < self.maxammo_shells)
	{
		local float shellstobuy;
		
		shellstobuy = self.maxammo_shells - self.ammo_shells;
		if (shellstobuy > 15)
			shellstobuy = 15;
		if (self.money >= shellstobuy * GRUNTY_COST_SHELL)
		{
			self.ammo_shells = self.ammo_shells + shellstobuy;
			self.money = self.money - shellstobuy * GRUNTY_COST_SHELL;
            //   	#ifdef VERBOSE_GRUNTY
            PrintFromSoldier(self,self.real_owner,"i've bought some shells.\n");
		    //sprint(self.real_owner, PRINT_HIGH, "BUYS SOME SHELLS\n");
            //    #endif
		}
	}
	
	// Consider buying more speed
	if (spendalloc >= GRUNTY_COST_SPEED)
	{
		if (self.custom_speed < GRUNTY_MAX_SPEED)
		{
			r = random();
			if (r < 0.3)
			{
				self.custom_speed = self.custom_speed + 1;
				self.money = self.money - GRUNTY_COST_SPEED;
				spendalloc = spendalloc - GRUNTY_COST_SPEED;

                PrintFromSoldier(self,self.real_owner,"i'm faster now.\n");
				//sprint(self.real_owner, PRINT_HIGH, "Your soldier is now faster.\n");
			}
		}
	}
	
    //- OfteN: don't let grunt have more HP than GRUNT_MAX_HP -//
    if (self.health > GRUNT_MAX_HP)
    	self.health = GRUNT_MAX_HP;
    
    if (self.max_health > GRUNT_MAX_HP)
    	self.max_health = GRUNT_MAX_HP;

    self.suicide_time = time + 10;
};

void () GruntyDrawPay =
{
	self.money = self.money + 70 + floor((self.has_sensor/2)+1) * 25;//-was 30 and has_sensor wasnt/2 // then was 30 again
	self.respawn_time = time + 20;
	return;
};

//-----------------------------------------------------//
void() grunty_touch =
{
    /*if (other.classname=="ammobox")
    {
        PrintFromSoldier(self,other,"AMMO?\n");
        return;
    }*/
    
    if (other.classname=="player" && self.has_holo < time && !self.enemy && self.health >= 0)
    {
        local string st;    

        if (self.real_owner==other)
        {        
            st=GetOwnerMessage(self);
            PrintFromSoldier(self,other,st);

            self.has_holo=time+5;
        }
        else if (Teammate(self.real_owner.team_no, other.team_no))
        {
            local float rnum;
            rnum=random();

            if (rnum < 0.4)
            {
                PrintFromSoldier(self,other,"Hi ");
                sprint(other,PRINT_HIGH,other.netname);
                sprint(other,PRINT_HIGH,", I'm ");
                sprint(other,PRINT_HIGH,self.netname);
                sprint(other,PRINT_HIGH,", ");
                sprint(other,PRINT_HIGH,self.real_owner.netname);
                sprint(other,PRINT_HIGH,"'s soldier!\n");

            }
            else
            {            
                st=GetFriendlyMessage(self);
                PrintFromSoldier(self,other,st);
            }

            self.has_holo=time+5;
        }
       
    }

    //self=other; // why this?
};

//========================================================================
// lets make the server to not crash when removing waypoints ok? :)

void(entity wyp, entity soldier) RemoveWaypoint =
{
    if (!wyp) return;

    if (wyp.classname == "grunty_waypoint" && wyp.owner == soldier.real_owner)
    {    
        if (wyp.owner.demon_one.martyr_enemy)
        {
            if (wyp.owner.demon_one.martyr_enemy.goalentity == wyp)
                wyp.owner.demon_one.martyr_enemy.goalentity = NIL;
        }

        if (wyp.owner.demon_one.demon_two)
        {
            if (wyp.owner.demon_one.demon_two.goalentity == wyp)
                wyp.owner.demon_one.demon_two.goalentity = NIL;
        }
        
        if (wyp.has_sensor == WAYPOINT_TYPE_PRIMARY
         && wyp.owner.demon_one.martyr_enemy == wyp)
            wyp.owner.demon_one.martyr_enemy = NIL;
        else if (wyp.has_sensor == WAYPOINT_TYPE_SECONDARY
         && wyp.owner.demon_one.demon_two == wyp)
            wyp.owner.demon_one.demon_two = NIL;
        else if (wyp.has_sensor == WAYPOINT_TYPE_ENEMYLASTSEEN
         && wyp.owner.demon_one.demon_one == wyp)
            wyp.owner.demon_one.demon_one = NIL;
        
        dremove(wyp);
    }   
};

//=======================================================================//
// OfN returns the goalentity the grunt should be going to if its able  

entity(entity sold, entity viewpoint) ReturnEasyWaypoint =
{    
    if (sold.penance_time==2) return NIL;
    if (sold.penance_time==0) return NIL;

    if (sold.penance_time==1)
    {
        if (sold.real_owner.health <= 0)
        {
            sold.penance_time=0;
            return NIL;
        }
        
        if (visible2(sold,sold.real_owner))
            return sold.real_owner;
        else
        {
            //sold.penance_time=0; // don't reset sold last "intention"
            PrintFromSoldier(sold,sold.real_owner,"i was following you but i can't see you now!\n");
            return NIL;
        }

    }
    
    if (sold.martyr_enemy && sold.job==1 && visible2(viewpoint,sold.martyr_enemy))
        return sold.martyr_enemy;
    
    if (sold.demon_two && sold.job==2 && visible2(viewpoint,sold.demon_two))
        return sold.demon_two;

    // no last waypoint walk so lets see whats easier to reach...    

    local entity retENT;
    retENT=NIL;
    
    if (sold.demon_two && sold.martyr_enemy) // both waypoints assigned? ok...
    {
        if (visible2(viewpoint,sold.martyr_enemy) && visible2(viewpoint,sold.demon_two)) // both waypoints visible? lets see which one is closest...
        {
            local float dist1,dist2;
            dist1 = vlen(viewpoint.origin - sold.martyr_enemy.origin);
            dist2 = vlen(viewpoint.origin - sold.demon_two.origin);

            if (dist1 > dist2) // which is the closest one?
            {
                retENT=sold.demon_two;
            }
            else
            {
                retENT=sold.martyr_enemy;
            }
        }
        else if (visible2(viewpoint,sold.martyr_enemy)) // only primary wayp is visible?
            retENT=sold.martyr_enemy;
        else if (visible2(viewpoint,sold.demon_two)) // only secondary wayp is visible?
            retENT=sold.demon_two;
    } 
    else if (sold.demon_two) // only secondary wayp is assigned?
    {
        if (visible2(viewpoint,sold.demon_two))
            retENT=sold.demon_two;
    }
    else if (sold.martyr_enemy) // only primary wayp is assigned?
    {
        if (visible2(viewpoint,sold.martyr_enemy))
            retENT=sold.martyr_enemy;
    }    

    if (sold.penance_time == 3 && !retENT)
    {
        PrintFromSoldier(sold,sold.real_owner,"can't see my waypoints around here!\n");
        //sold.penance_time = 0; // don't reset sold last "intention"
    }

    return retENT;
};

//== OfN, used when picking up ammo ==//
void(entity sld) grunty_boundammo =
{
    if (sld.classname!="monster_army") return;

    if (sld.ammo_nails > sld.maxammo_nails) sld.ammo_nails = sld.maxammo_nails;
    if (sld.ammo_cells > sld.maxammo_cells) sld.ammo_cells = sld.maxammo_cells;
    if (sld.ammo_shells > sld.maxammo_shells) sld.ammo_shells = sld.maxammo_shells;
    if (sld.ammo_rockets > sld.maxammo_rockets) sld.ammo_rockets = sld.maxammo_rockets;
};