prozac-qfcc/grunty.qc
Finny Merrill db4e96e70a Engineer tweaks:
1) added checkmove forward so it's easier to build against walls
2) Can't build sentries on top of forcefields anymore (they get bounced off)
3) fieldgens are now one unit taller than their fields, so you CAN build on the gen
4) forcefields bounce everything (including buildings) away now.
5) added #ifdef DISALLOW_BLOCKED_TELE around tele block checks. didn't get the point

Debug tweaks:
1) added #ifdef DEBUG, which enables RPrint(), dremove(), and printtrace(), as well
   as the warlock cheat and origin reporting.
2) replaced EVERY dprint with RPrint.
3) changed makefile so that all = no DEBUG and no .sym
2003-11-27 07:07:26 +00:00

2134 lines
63 KiB
C++
Raw Blame History

#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 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> has ");
sprint(self.real_owner, PRINT_HIGH, talk);
talk = ftos(self.max_health);
sprint(self.real_owner, PRINT_HIGH, "<EFBFBD>");
sprint(self.real_owner, PRINT_HIGH, talk);
sprint(self.real_owner, PRINT_HIGH, " <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>");
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, "<EFBFBD><EFBFBD><EFBFBD><EFBFBD>: ");
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;
};