#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 != world) { 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 == world) { 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 = world; // Check to buy if (self.suicide_time <= time) GruntyPayThink(); // scan for targets GruntyScanTargets(); }; // LookAround for grunty entity (entity scanner) LookAroundGrunty = { if (infokey(world,"ceasefire")=="on") //OfN return scanner; local entity 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 = world; 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 = world; if (self.enemy != world) if (self.enemy.health <= 0 || self.enemy.has_disconnected) { self.enemy = world; self.goalentity = ReturnEasyWaypoint(self,self); //// self.enemy = world; //// self.goalentity = world; if (self.demon_one != world) { RemoveWaypoint(self.demon_one,self); self.demon_one = world; } self.has_teleporter = 0; self.effects = self.effects - (self.effects & EF_DIMLIGHT); return; } // If we have a target already if (self.enemy != world) { local float vis; vis = visible(self.enemy); if (vis) { if (self.demon_one != world) { RemoveWaypoint(self.demon_one,self); self.demon_one=world; } //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 = world; self.goalentity = ReturnEasyWaypoint(self,self); self.has_teleporter = 0; self.effects = self.effects - (self.effects & EF_DIMLIGHT); return; } // tactic if (self.demon_one == world) // if we don't have a enemy last seen marker { if (self.demon_one!=world) { 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 = world; self.goalentity = world; if (self.demon_one != world) // d1 remove contingency check { RemoveWaypoint(self.demon_one,self); self.demon_one = world; self.goalentity = ReturnEasyWaypoint(self,self); } } } } else // otherwise look for a target { targ=GruntyPharse(); if (targ == self) return; if (targ == world) return; if (!self.is_detpacking || infokey(world,"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 == world) return; if (infokey(world,"ceasefire")=="on") { self.goalentity=ReturnEasyWaypoint(self,self); self.enemy=world; 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 != world) { 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!=world) //- 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!=world) { self.has_teleporter = 0; botmovedist(vectoyaw(v_forward), dist); // move } else { if (self.is_malfunctioning == 2 && self.enemy != world) { 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"); dprint (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"); dprint (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"); dprint (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 != world) // 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 = world; if (self.martyr_enemy != world) RemoveWaypoint(self.martyr_enemy, self); if (self.demon_one != world) RemoveWaypoint(self.demon_one, self); if (self.demon_two != world) 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 = world; 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 world 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 = world; if (self.has_sensor == WAYPOINT_TYPE_SECONDARY && self.owner.demon_one.demon_two == self) self.owner.demon_one.demon_two = world; if (self.has_sensor == WAYPOINT_TYPE_ENEMYLASTSEEN && self.owner.demon_one.demon_one == self) self.owner.demon_one.demon_one = world; 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 == world && 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==world) return; if (wyp.classname == "grunty_waypoint" && wyp.owner == soldier.real_owner) { if (wyp.owner.demon_one.martyr_enemy != world) { if (wyp.owner.demon_one.martyr_enemy.goalentity == wyp) wyp.owner.demon_one.martyr_enemy.goalentity = world; } if (wyp.owner.demon_one.demon_two != world) { if (wyp.owner.demon_one.demon_two.goalentity == wyp) wyp.owner.demon_one.demon_two.goalentity = world; } if (wyp.has_sensor == WAYPOINT_TYPE_PRIMARY && wyp.owner.demon_one.martyr_enemy == wyp) wyp.owner.demon_one.martyr_enemy = world; else if (wyp.has_sensor == WAYPOINT_TYPE_SECONDARY && wyp.owner.demon_one.demon_two == wyp) wyp.owner.demon_one.demon_two = world; else if (wyp.has_sensor == WAYPOINT_TYPE_ENEMYLASTSEEN && wyp.owner.demon_one.demon_one == wyp) wyp.owner.demon_one.demon_one = world; 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 world; if (sold.penance_time==0) return world; if (sold.penance_time==1) { if (sold.real_owner.health <= 0) { sold.penance_time=0; return world; } 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 world; } } if (sold.martyr_enemy!=world && sold.job==1 && visible2(viewpoint,sold.martyr_enemy)) return sold.martyr_enemy; if (sold.demon_two!=world && 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=world; if (sold.demon_two!=world && sold.martyr_enemy!=world) // 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!=world) // only secondary wayp is assigned? { if (visible2(viewpoint,sold.demon_two)) retENT=sold.demon_two; } else if (sold.martyr_enemy!=world) // only primary wayp is assigned? { if (visible2(viewpoint,sold.martyr_enemy)) retENT=sold.martyr_enemy; } if (sold.penance_time == 3 && retENT==world) { 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; };