/*====================================================== JOBS.QC Custom TeamFortress v3.2SB1 (c) William Kerney 2/9/00 (c) SB-1 Tech 25/10/00 ======================================================== Functions for handling the custom class professions ======================================================*/ #include "defs.qh" #include "menu.qh" #include "jobs.qh" //WK - all of this void(float delay) Attack_Finished; void() DropToCustomClassGen; //Called when starting class generation void() DropFromCustomClassGen; //Called when finished class generation void() PrintMoney; void(float in) PrintRefund; void(float in) PrintNotEnoughMoney; void(float cost, float type) BuyItem; void(float cost, float type) BuyJob; /*void(float cost, float type) BuyGren1; void(float cost, float type) BuyGren2;*/ void() FragGrenadeTouch; void() FragGrenadeExplode; void() KracGrenadeTouch; void() KracGrenadeExplode; void(entity bastard,float threshold) createBastard; void() UseJobSkill; //Function for handling professions void (entity targ,float pain) RevealThief; //Extern void (vector org, entity death_owner) spawn_tdeath; void (string gib, float health) ThrowGib; float() W_BestWeapon; void() W_SetCurrentAmmo; void(entity p) TeamFortress_SetSpeed; void(entity p) TeamFortress_SetSkin; void () BecomeExplosion; void(entity bomb, entity attacker, float rad, entity ignore) T_RadiusDamage; void() SUB_regen; float modelindex_eyes, modelindex_player, modelindex_null; void(float inAuto) W_FireMedikit; void(float inAuto) TeamFortress_ID; void(float range,float inAuto) TeamFortress_Scan; void(float points) custom_demon_create; void() kill_my_demon; void() player_assaultcannondown1; void (entity rhook) Reset_Grapple; void (string temp) DebugSprint; void (float temp) DebugSprintFloat; void() TeamFortress_RegenerateCells; // for thief //- OfN Cyber Interface is now a job itself //---- OfN void(entity mine_owner) DetonateMines; float(entity thing) IsMonsterNonArmy; float(entity thing) IsMonster; // for mines void() JobArmy; void() JobHacker; // // Functions for handling our "professions" // which add some class-like behavior to this // unclassy version of TF // /* ** Thief Profession - ** Hides in shadows, can become fully hidden, leaves shadows if attacks or attacked */ void() JobThief = { local entity te; //local string st; if (self.job & JOB_ACTIVE) { //We are hiding //If we hit "skill" again and we are moving, we leave shadows //vel = vlen(self.velocity); /*if (vel > 100) { //Leave shadows // sprint(self,PRINT_HIGH,"Leaving shadows...\n"); //} else { //Become fully hidden //RJM - Can't hide while carrying flags. if (self.effects & EF_ANYGLOW) { sprint(self,PRINT_HIGH,"Not while glowing, idiot.\n"); return; } sprint(self,PRINT_HIGH,"You are fully hidden\n"); self.frame = 0; self.weaponframe = 0; self.modelindex = modelindex_null; self.job = self.job | JOB_FULL_HIDE; } */ if (self.invisible_time) sprint (self, PRINT_HIGH, "Leaving shadows... just as soon as this ring wears off...\n"); else { sprint (self, PRINT_HIGH, "Leaving shadows...\n"); self.items = self.items & ~IT_INVISIBILITY; self.modelindex = modelindex_player; } self.job = self.job - JOB_ACTIVE; self.job = self.job - (self.job & JOB_FULL_HIDE); self.job_finished = time + 2; TeamFortress_SetSpeed(self); } /*else if (self.job & JOB_FULL_HIDE) { RevealThief(self,FALSE); } */ else { //Start hiding //RJM - Can't hide while carrying flags. //WK - Allow them to go eyes invisible but not full hide /* if (self.effects & EF_ANYGLOW) { sprint(self, PRINT_HIGH, "Not while glowing, gimp.\n"); return; } */ if (self.invisible_time) sprint (self, PRINT_HIGH, "Entering shadows... well not really...\n"); else sprint (self, PRINT_HIGH, "Entering shadows...\n"); self.frame = 0; self.weaponframe = 0; self.modelindex = modelindex_eyes; self.job = self.job | JOB_ACTIVE; self.job_finished = time + 2; TeamFortress_SetSpeed(self); self.items = self.items | IT_INVISIBILITY; te = spawn(); te.nextthink = time + PC_SPY_CELL_REGEN_TIME; te.think = TeamFortress_RegenerateCells; te.owner = self; te.classname = "timer"; } }; /* ** Runner Profession - ** Sprints at +200 speed for a while, then has to rest (half speed) */ #define PHASE1 5 #define PHASE2 5 #define PHASE3 5 void() RunnerThink = { self.heat = self.heat + 1; if (self.heat == 1) { //Initial Phase sprint(self.owner,PRINT_HIGH,"Sprinting...\n"); TeamFortress_SetSpeed(self.owner); self.nextthink = time + PHASE1; } else if (self.heat == 2) { sprint(self.owner,PRINT_HIGH,"Recovering...\n"); self.owner.job = self.owner.job | JOB_TIRED; TeamFortress_SetSpeed(self.owner); self.nextthink = time + PHASE2; } else if (self.heat == 3) { self.owner.job = self.owner.job - (self.owner.job & JOB_ACTIVE); self.owner.job = self.owner.job - (self.owner.job & JOB_TIRED); TeamFortress_SetSpeed(self.owner); self.nextthink = time + PHASE3; } else { dremove(self); } }; void() JobRunner = { local entity RunnerTimer; self.job = self.job | JOB_ACTIVE; //Timer will remove this RunnerTimer = spawn (); RunnerTimer.classname = "timer"; RunnerTimer.owner = self; RunnerTimer.nextthink = time + 0.5; //Small delays are cool RunnerTimer.think = RunnerThink; RunnerTimer.heat = 0; self.job_finished = time + PHASE1 + PHASE2 + PHASE3 + 0.6; }; void() JobWarlock = { //local float r; //local entity SummonTimer; if (self.attack_finished > time) { sprint(self,PRINT_HIGH,"You can't shoot and summon at the same time\n"); self.job_finished = time + 2; return; } self.current_menu = MENU_DEMON; self.menu_count = MENU_REFRESH_RATE; }; /* ** Chaplan Profession - ** Dispels demons, inspires teammates to do x2 damage, but can't attack himself ** Timer triggers every so often, checking to see if you want to resume inspire */ #define GUIDE_TIME 1 //Period of how often lightning guides are shown. Must be less than... #define CHAPLAN_TIME 1 //Period of seconds how often it fires #define INSPIRE_TIME 6 //How long someone stays inspired #define CHAPLAN_RADIUS 320 //About the radius of brightglow #define CHAPLAN_HEAL 50 //If you have a medikit you'll heal friends this much #define CHAPLAN_HEAL_DELAY 3 //You can't have been shot in last three seconds to be healed //Hunt for all friendlies and power them up //Take special care to coexist with Quad damage void() ChaplanInspire = { local entity head; local float take; head = findradius(self.origin, CHAPLAN_RADIUS); while (head) { //Dispel enemy demons //- OfN - if (head.classname == "monster_demon1" || head.classname == "monster_shambler") if (IsMonsterNonArmy(head)) { //No short circuit evaluation, so... if (!Teammate(head.real_owner.team_no,self.team_no)) { //...to avoid a crash from deref if ((head.health <= 200 && head.classname == "monster_demon1") || head.health < 500 && head.classname == "monster_shambler") { sprint(self,PRINT_HIGH,"You dispel a demon\n"); self.real_frags = self.real_frags + 1; if (!(toggleflags & TFLAG_TEAMFRAGS)) self.frags = self.real_frags; } if (head.classname == "monster_shambler") TF_T_Damage(head, self, self, 500, 0, 0); else if (head.classname == "monster_demon1") TF_T_Damage(head, self, self, 200, 0, 0); } } else if (head.classname == "player" && //Person who is... Teammate(head.team_no,self.team_no) && //Same Team head != self && //Not me !(head.is_undercover) && //Not a spy !(head.job & JOB_CHAPLAN) && //Not a chaplan head.playerclass != PC_CIVILIAN && //KK //... and not Quadded !(head.items & IT_QUAD && !(head.tfstate & TFSTATE_INSPIRED)) ) { head.items = head.items | IT_QUAD; head.inspirator = self; //We are their designated preacherman head.super_time = 1; if (head.super_damage_finished < time + INSPIRE_TIME) head.super_damage_finished = time + INSPIRE_TIME; head.tfstate = head.tfstate | TFSTATE_INSPIRED; //Heal them if we have automedic too //SB amended to medikit since there is no automedic now if ((self.weapons_carried & WEAP_MEDIKIT) && (self.last_attacked_time < time + CHAPLAN_HEAL_DELAY)) { take = head.max_health - head.health; if (take > CHAPLAN_HEAL) take = CHAPLAN_HEAL; if (take < 0) take = 0; head.health = head.health + take; } } head = head.chain; } }; //Draws lightning bolts towards all the friendlies we're inspiring //entity(entity start, .string fld, string match) find = #18; void() ChaplanGuides = { local entity head; head = find(NIL,classname,"player"); while (head) { if (head.inspirator == self) { // Create the Lightning msg_entity = self; WriteByte (MSG_ONE, SVC_TEMPENTITY); WriteByte (MSG_ONE, TE_LIGHTNING1); WriteEntity (MSG_ONE, self); WriteCoord (MSG_ONE, self.origin_x); WriteCoord (MSG_ONE, self.origin_y); WriteCoord (MSG_ONE, self.origin_z + 8); WriteCoord (MSG_ONE, head.origin_x); WriteCoord (MSG_ONE, head.origin_y); WriteCoord (MSG_ONE, head.origin_z + 8); head = NIL; } else //We can only draw one lightning. :p head = find(head,classname,"player"); } }; void() ChaplanThink = { local entity oself; oself = self; self = self.owner; ChaplanGuides(); oself.nextthink = time + GUIDE_TIME; oself.frags = oself.frags + GUIDE_TIME; if (oself.frags >= CHAPLAN_TIME) { //Do the full thing every second oself.frags = 0; if (self.heat == TRUE) { //Inspire everyone again //sprint(self,PRINT_HIGH,"Chaplan: Still Preaching\n"); ChaplanInspire(); } else { //We stopped preaching sprint(self,PRINT_HIGH,"You finish your sermon\n"); //Sync CHAN_MUSIC with disconnect and sound below self.job = self.job - (self.job & JOB_ACTIVE); sound (self, CHAN_MUSIC, "items/r_item1.wav", 0.1, ATTN_NORM); self.effects &= ~EF_ANYGLOW; //self.tfstate = self.tfstate - (self.tfstate & TFSTATE_RELOADING); self.current_weapon = self.weapon; W_SetCurrentAmmo(); oself.nextthink = time + 0.1; oself.think = SUB_Remove; } } self = oself; }; void() JobChaplan = { local entity tWeapon; if (self.job & JOB_ACTIVE) { //self.job = self.job - JOB_ACTIVE; if (self.heat == TRUE) //Only print this once sprint(self,PRINT_HIGH,"You gradually stop preaching...\n"); self.heat = FALSE; //Bad to turn off active, since technically job is still on. self.job_finished = time + 0.7; //Don't allow them to trigger too often return; } if (self.tfstate & TFSTATE_RELOADING || self.is_feigning || self.heat) { sprint(self,PRINT_HIGH,"You can't preach while doing other stuff\n"); self.job_finished = time + 0.5; //Don't allow them to trigger too often return; } sprint(self,PRINT_HIGH,"You begin preaching. Hit skill again to stop.\n"); tWeapon = spawn(); tWeapon.frags = 0; //Clear guides counter tWeapon.owner = self; tWeapon.classname = "timer"; tWeapon.nextthink = time + GUIDE_TIME; tWeapon.think = ChaplanThink; self.job = self.job | JOB_ACTIVE; self.job_finished = time + 0.3; //Don't allow them to trigger too often //Hide our weapon. Can't shoot while preaching. self.weapon = self.current_weapon; self.current_weapon = 0; self.weaponmodel = ""; self.weaponframe = 0; self.heat = TRUE; //We're actively preaching. //self.tfstate = self.tfstate | TFSTATE_RELOADING; //Start playing preacher music, glow and inspire! //sound (self, CHAN_VOICE, "ambience/orff.wav", 0.75, ATTN_NORM); sound (self, CHAN_MUSIC, "ambience/orff.wav", 0.75, ATTN_NORM); self.effects |= EF_GlowColor(self); ChaplanInspire(); }; /* ** Martyr Proficiency - ** Becomes invincible, but dies after a few seconds */ #define MARTYR_TIME 3.5 void() MartyrThink = { //Self.owner is the guy who became a martyr //Clean these up so we can kill him self.job = self.job - (self.job & JOB_ACTIVE); //why not? self.owner.items = self.items & ~IT_INVULNERABILITY; self.owner.invincible_time = 0; self.owner.invincible_finished = 0; self.owner.effects = self.owner.effects - (self.owner.effects & EF_DIMLIGHT); //if (self.owner.martyr_enemy == self.owner) deathmsg = DMSG_MARTYR; //else // deathmsg = self.owner.stored_deathmsg; //- OfN was 20 TF_T_Damage(self.owner, self.owner.martyr_enemy, self.owner.martyr_enemy, self.owner.health + 20, TF_TD_IGNOREARMOUR, TF_TD_OTHER); self.think = SUB_Remove; self.nextthink = time + 0.1; }; // SB I'll leave this here since it's still close to a job void() JobMartyr = { local entity tWeapon; local float martyr_time; if (self.is_abouttodie) return; martyr_time = MARTYR_TIME; // don't need FORCED stuff since it's always automatic sprint(self,PRINT_HIGH,"Beginning your suicide run...\n"); self.items = self.items | IT_INVULNERABILITY; self.invincible_time = 1; self.invincible_finished = time + martyr_time + 1; //Overlap so we can't die till end self.job = self.job | JOB_ACTIVE; //why not? tWeapon = spawn(); tWeapon.owner = self; tWeapon.classname = "timer"; tWeapon.nextthink = time + martyr_time; tWeapon.think = MartyrThink; }; /* Berserker Profession - ** A simple soul that just likes killing things ** Takes 50 self inflicted damage and gets Quad for 5 seconds ** If he can't pay up with the 50 health he gets reduced to 1 and dies after his 5 seconds */ // now takes BERSERKER_HP_COST hp #define BERSERK_TIME 4 void() BerserkerKillTimer = { if (!(self.tfstate & TFSTATE_CONCUSSIONED)) stuffcmd(self.owner, "v_idlescale 0\n"); if (self.has_sensor) { deathmsg = DMSG_BERSERK; TF_T_Damage(self.owner, self.owner, self.owner, self.owner.health, TF_TD_IGNOREARMOUR, TF_TD_OTHER); } self.owner.job &= ~JOB_ACTIVE; //- OfN is a tard, should be self.owner idiot -Griev dremove(self); }; void() JobBerserker = { if (self.super_damage_finished > 0 || self.tfstate & TFSTATE_CONCUSSIONED) return; newmis = spawn(); newmis.classname = "berserker_timer"; newmis.nextthink = time + BERSERK_TIME + 1; newmis.think = BerserkerKillTimer; newmis.owner = self; if (self.health > BERSERKER_HP_COST) self.health -= BERSERKER_HP_COST; else { self.health = 1; newmis.has_sensor = 1; } self.super_time = 1; self.super_damage_finished = time + BERSERK_TIME + 1; self.items = self.items | IT_QUAD; self.job = self.job | JOB_ACTIVE; //- OfN self.job_finished = time + BERSERK_TIME * 2; // GR was * 3 stuffcmd(self, "v_idlescale 30\n"); }; /* ** Judoka Profession - ** Disarms opponents so they cannot attack */ //This is guaranteed to be removed if either target or owner dies or disconnects //So we don't have to do error checking on those situations //Four special cases, Rifle, Medikit, AC and Grapple, have side effects // when you remove them. Need special cases to handle their theft void() JudokaRearm = { //Self.owner is the guy who had his weapon taken away //Self.enemy is the guy who took it away local entity oself,te; self.enemy.job = self.enemy.job - (self.enemy.job & JOB_ACTIVE); if (self.heat == 1) { //We have their weapon ////Fix feign while stolen self.enemy.weapon = 0; ////Fix reloading te = find(NIL, netname, "reloadtimer"); while (te) { if (te.classname == "timer" && te.owner == self.enemy) { oself = self; self = te; self.think(); self = oself; te.think = SUB_Remove; te.nextthink = time + 0.1; } te = find(te, netname, "reloadtimer"); } ////Fix double weapons #ifndef NO_AUTORIFLE if (self.current_weapon == WEAP_SNIPER_RIFLE) { self.owner.weapons_carried = self.owner.weapons_carried | WEAP_AUTO_RIFLE; self.enemy.weapons_carried = self.enemy.weapons_carried - (self.enemy.weapons_carried & WEAP_AUTO_RIFLE); } #endif ////Fix weird weapons if (self.current_weapon == WEAP_ASSAULT_CANNON && self.enemy.current_weapon == WEAP_ASSAULT_CANNON) { oself = self; self = self.enemy; stuffcmd(self, "v_idlescale 0\n"); self.tfstate &= ~TFSTATE_ASSAULTCANNON; TeamFortress_SetSpeed(self); self.weaponframe = 0; self.count = 1; self.heat = 0; self.button0 = 0; self.fire_held_down = FALSE; player_assaultcannondown1(); self = oself; } if (self.current_weapon == WEAP_HOOK && self.enemy.hook_out) { oself = self; self = self.enemy; Reset_Grapple (self.hook); self.weaponframe = 0; self = oself; } sprint(self.owner,PRINT_HIGH,"You get your weapon back\n"); self.owner.weapons_carried = self.owner.weapons_carried | self.current_weapon; sprint(self.enemy,PRINT_HIGH,"You lose your stolen weapon\n"); self.enemy.weapons_carried = self.enemy.weapons_carried - (self.enemy.weapons_carried & self.current_weapon); //Fix for a bug that would let someone keep their weapon if they switched to autorifle before //the weapon returned. if (self.enemy.current_weapon == self.current_weapon || self.current_weapon == WEAP_SNIPER_RIFLE || self.current_weapon == WEAP_MEDIKIT) { oself = self; self = self.enemy; self.weaponframe = 0; self.current_weapon = W_BestWeapon (); W_SetCurrentAmmo(); self = oself; } //TODO: Is this really a Fix for diving? //Should we even do this? Might cause firing skip. Evaluate self.enemy.weaponframe = 0; } self.owner.tfstate &= ~TFSTATE_DISARMED; self.think = SUB_Remove; self.nextthink = time + 0.1; }; void() JobJudoka = { //Take the weapon of any person in front of you and force a reload self.job_finished = time + MISS_DELAY; //Delay if we don't hit local vector source; local vector dir; local float chance; local entity tWeapon,oself; local entity te; if (self.attack_finished > time) return; makevectors(self.v_angle); source = self.origin + '0 0 16'; if (self.cutf_items & CUTF_CLOSECOMBAT) traceline(source, source + v_forward*96, FALSE, self); else traceline(source, source + v_forward*64, FALSE, self); if (trace_fraction != 1.0 && trace_ent.classname == "player" && !Teammate(trace_ent.team_no, self.team_no) && trace_ent.playerclass != PC_UNDEFINED) { if (self.is_undercover) //Taking someone's weapon should give you away Spy_RemoveDisguise(self); // Let's not make it work all the time dir = normalize (trace_ent.origin - self.origin); makevectors(trace_ent.v_angle); chance = dir * normalize(v_forward); chance *= 0.35; if (chance > 0.25) chance = 0.25; if (trace_ent.job & JOB_JUDOKA) chance += 0.2; if (trace_ent.cutf_items & CUTF_CLOSECOMBAT) chance += 0.25; if (trace_ent.current_weapon == WEAP_AXE && trace_ent.cutf_items & CUTF_KNIFE) chance += 0.1; if (self.cutf_items & CUTF_CLOSECOMBAT) chance -= 0.25; if (random() > chance || trace_ent.invincible_finished > time) { sprint (self, PRINT_HIGH, "Your strike is parried!\n"); Attack_Finished(3); return; } sprint (trace_ent, PRINT_HIGH, "You have been disarmed by "); sprint (trace_ent, PRINT_HIGH, self.netname); sprint (trace_ent, PRINT_HIGH, "\n"); self.job = self.job | JOB_ACTIVE; //Simplify the dual-weapon problem #ifndef NO_AUTORIFLE if (trace_ent.current_weapon == WEAP_AUTO_RIFLE) trace_ent.current_weapon = WEAP_SNIPER_RIFLE; #endif trace_ent.tfstate &= ~(TFSTATE_AIMING | TFSTATE_RL_LASER); trace_ent.tfstate |= TFSTATE_DISARMED; //If already reloading, remove that timer te = find(NIL, netname, "reloadtimer"); while (te) { if (te.classname == "timer" && te.owner == trace_ent) { oself = self; self = te; self.think(); self = oself; te.think = SUB_Remove; te.nextthink = time + 0.1; } te = find(te, netname, "reloadtimer"); } tWeapon = spawn(); tWeapon.owner = trace_ent; tWeapon.enemy = self; tWeapon.current_weapon = trace_ent.current_weapon; tWeapon.classname = "timer"; tWeapon.netname = "judokatimer"; tWeapon.nextthink = time + DISARM_TIME; tWeapon.think = JudokaRearm; //Remove the weapon trace_ent.attack_finished = time + CANT_ATTACK_TIME; trace_ent.weaponmodel = ""; trace_ent.weaponframe = 0; trace_ent.currentammo = 0; if ((trace_ent.job & JOB_JUDOKA && trace_ent.job_finished > time) || trace_ent.current_weapon == 0) { //Hit fellow judoka or chaplan sprint (self, PRINT_HIGH, "You throw him with a mighty Seoi Otoshi\n"); deathmsg = DMSG_JUDOKA; TF_T_Damage (trace_ent, self, self, 150, TF_TD_NOTTEAM, TF_TD_OTHER); } else if (self.weapons_carried & trace_ent.current_weapon) { sprint (self, PRINT_HIGH, "You knock his weapon out of his hands\n"); tWeapon.heat = 0; //I.e., we didn't take a weapon trace_ent.attack_finished = time + DISARM_TIME; deathmsg = DMSG_JUDOKA; TF_T_Damage (trace_ent, self, self, 100, TF_TD_NOTTEAM, TF_TD_OTHER); } else if (trace_ent.current_weapon != 0 && trace_ent.current_weapon != WEAP_AXE){ //Steal their weapon if they have one sprint (self, PRINT_HIGH, "You rip his weapon from his hands!\n"); tWeapon.heat = 1; //Fix double weapons #ifndef NO_AUTORIFLE if (trace_ent.current_weapon == WEAP_SNIPER_RIFLE) { self.weapons_carried = self.weapons_carried | WEAP_AUTO_RIFLE; trace_ent.weapons_carried = trace_ent.weapons_carried - (trace_ent.weapons_carried & WEAP_AUTO_RIFLE); } #endif if (trace_ent.current_weapon == WEAP_MEDIKIT) { self.health = self.max_health; //You heal yourself. :) } ////Fix weird weapons if (trace_ent.current_weapon == WEAP_ASSAULT_CANNON) { oself = self; self = trace_ent; stuffcmd(self, "-attack;v_idlescale 0\n"); self.tfstate &= ~TFSTATE_ASSAULTCANNON; TeamFortress_SetSpeed(self); self.weaponframe = 0; self.count = 1; self.heat = 0; self.button0 = 0; self.fire_held_down = FALSE; player_assaultcannondown1(); self = oself; } if (trace_ent.current_weapon == WEAP_HOOK && trace_ent.hook_out) { oself = self; self = trace_ent; Reset_Grapple (self.hook); self = oself; } self.weapons_carried = self.weapons_carried | trace_ent.current_weapon; self.current_weapon = trace_ent.current_weapon; W_SetCurrentAmmo(); trace_ent.weapons_carried = trace_ent.weapons_carried - (trace_ent.weapons_carried & trace_ent.current_weapon); trace_ent.current_weapon = 0; deathmsg = DMSG_JUDOKA; TF_T_Damage (trace_ent, self, self, 65, TF_TD_NOTTEAM, TF_TD_OTHER); } self.job_finished = time + HIT_DELAY; Attack_Finished(0.5); } else { sprint (self, PRINT_HIGH, "You miss.\n"); Attack_Finished(1.5); } }; /* ** Guerilla Profession - ** Can set self-detonating land mines */ #define ACTIVATE_TIME 8 //Time until it turns on //- it was 3? prolly 10 #define BEEP_RATE 4 //Delay between beeps //- it was 3 #define MINE_DURATION 360 //Time it lasts after being activated //- it was 60 ofn #define JOB_DELAY 4 //Time between mine placements //- it was 10 #define GUERILLA_RADIUS 135 //-it was 150 without define #define MINE_COST 4 // OfN number of rockets a mine needs //#define MAX_MINES 4 // OfN Maximum number of mines for player NOW A LOCALINFO //Blow up a mine void() GuerillaExplode = { self.health = 0; //CH would cause tesla to act weird without deathmsg = DMSG_LAND_MINE; if (self.has_tesla == 2) // set by GuerillaSwep for trace weapons sprint(self.owner,PRINT_HIGH,"your mine is destroyed\n"); if (self.has_tesla == 0) // default sprint(self.owner,PRINT_HIGH,"your mine explodes\n"); // if has_tesla is 1 print nothing, as this is set by DetonateMines() and GuerillaThink if (time < self.heat + ACTIVATE_TIME) //If not charged, do less damage when blowing up T_RadiusDamage (self, self.owner, MINE_DMG * 0.5, NIL); //- damage was 80 else T_RadiusDamage (self, self.owner, MINE_DMG, NIL); //- damage was 160 // num_mines is the number of mines the player has self.owner.num_mines = self.owner.num_mines - 1; if (self.owner.num_mines < 0) self.owner.num_mines = 0; WriteByte (MSG_MULTICAST, SVC_TEMPENTITY); WriteByte (MSG_MULTICAST, TE_EXPLOSION); WriteCoord (MSG_MULTICAST, self.origin_x); WriteCoord (MSG_MULTICAST, self.origin_y); WriteCoord (MSG_MULTICAST, self.origin_z); multicast (self.origin, MULTICAST_PHS); dremove(self); }; //========================================================================= // Detonate all mines the mine_owner player entity has void(entity mine_owner) DetonateMines = { local entity e; // Find any mine e = find(NIL, netname, "land_mine"); while (e) { if(e.owner == mine_owner) { e.heat = time; e.has_tesla = 1; //- display no message on GuerillaExplode e.think = GuerillaExplode; e.nextthink = time; } e = find(e, netname, "land_mine"); } mine_owner.num_mines = 0; }; //==================================================== //Code to detonate the guerilla mines on traceattack weapons. //Has small radius, just large enough to hit what was aimed at void(vector startpos) GuerillaMineSweep = { local entity head; local entity inf = self; local float loopc = 0; head = findradius(startpos, 30); while (head) { if (head.classname == "grenade" && head.netname == "land_mine") { head.has_tesla = 2; // "mine is destroyed" message on guerillaexplode() head.think = GuerillaExplode; head.nextthink = time + 0.1; head.martyr_enemy = inf; } head = head.chain; } }; void() GuerillaThink = //Every second see if we have enemy nearby { local entity head; local float finished; //Only blow up once finished = 0; self.nextthink = time + MINE_SCANRATE; self.martyr_enemy = NIL; if (time < self.heat + ACTIVATE_TIME) return; if (time > self.last_attacked_time) { sound (self, CHAN_WEAPON, "weapons/guerilla_blip.wav", 1, ATTN_IDLE); self.last_attacked_time = time + BEEP_RATE; } self.health = self.health - 1; if (self.health <= 0) { //Detonate mine cause we ran out of time self.martyr_enemy = self.owner; self.heat = time; //Make it a smaller explosion sprint(self.owner,PRINT_HIGH,"Mine runs out of energy, "); if (self.owner.num_mines > 1) { local string st; st = ftos (self.owner.num_mines - 1); sprint(self.owner,PRINT_HIGH,"you still have "); sprint(self.owner,PRINT_HIGH,st); sprint(self.owner,PRINT_HIGH,"/"); st = ftos (max_mines); sprint(self.owner,PRINT_HIGH,st); sprint(self.owner,PRINT_HIGH," mines up\n"); } else sprint(self.owner,PRINT_HIGH,"you currently have no mines active\n"); self.has_tesla = 1; //- display no message on GuerillaExplode GuerillaExplode(); return; } head = findradius(self.origin,GUERILLA_RADIUS);//OfN it was 150 while (head && !finished) { if (head.classname == "player") { //Mines detonate on either enemies or yourself if (CanDamage(head,self)) { if (!Teammate(head.team_no,self.owner.team_no) && head.undercover_team == 0) { //Uncouvered ENEMY sprint(self.owner,PRINT_HIGH,"your mine explodes on "); sprint(self.owner,PRINT_HIGH,head.netname); sprint(self.owner,PRINT_HIGH,"'s face!\n"); self.has_tesla = 1; //- display no message on GuerillaExplode self.martyr_enemy = head; GuerillaExplode(); return; } else if (head == self.owner) { // ourselves self.martyr_enemy = self.owner; self.has_tesla = 1; //- display no message on GuerillaExplode GuerillaExplode(); return; } } } else if (IsMonster(head) && !Teammate(head.real_owner.team_no,self.owner.team_no)) { //- OfN - monsters and grunt are affected T_Damage(head,self,self.owner,MINE_DMG); //Demons are vulnerable self.has_tesla = 1; //- display no message on GuerillaExplode GuerillaExplode(); return; } head = head.chain; } }; void(entity inflictor, float amt) GuerillaPain = //What happens when someone hurts it { if (time < self.heat + ACTIVATE_TIME) return; self.martyr_enemy = inflictor; GuerillaExplode(); }; void() GuerillaTouch = // what happens when someone runs it over { GuerillaPain(other, 100); }; void() GuerillaTossTouch = { self.solid = SOLID_BBOX; setsize(self, self.mins, self.maxs); if (other && other != self.owner) { GuerillaPain(other, 100); return; } if (entpointcontents(self) == CONTENTS_SKY || entpointcontents(self) == CONTENTS_SOLID) { self.health = 0; //CH needed for tesla // OfN CAUSES ANY PROBLEM? Is this needed? self.owner.num_mines = self.owner.num_mines - 1; if (self.owner.num_mines < 0) self.owner.num_mines = 0; dremove(self); return; } self.angles = self.velocity = self.avelocity = '0 0 0'; self.movetype = MOVETYPE_NONE; sound (self, CHAN_WEAPON, "weapons/guerilla_set.wav", 1, ATTN_NORM); self.touch = GuerillaTouch; self.think = GuerillaThink; self.takedamage = DAMAGE_AIM; self.th_pain = GuerillaPain; self.nextthink = time + 1; }; void() JobGuerilla = { if (self.ammo_rockets < MINE_COST) { sprint(self, PRINT_HIGH, "every mine needs 4 rockets to work!\n"); self.job_finished = time + 1; return; } if (self.num_mines >= max_mines) { local string st; st = ftos(max_mines); sprint(self, PRINT_HIGH, "you can set upto "); sprint(self, PRINT_HIGH, st); sprint(self, PRINT_HIGH, " mines!\n"); self.job_finished = time + 1; return; } self.ammo_rockets = self.ammo_rockets - MINE_COST; self.job_finished = time + JOB_DELAY; // OfN - I created the field num_mines to store the number of mines the player has self.num_mines = self.num_mines + 1; sprint(self,PRINT_HIGH,"you place a mine...\n"); teamprefixsprint(self.team_no,self); teamsprint(self.team_no,self,self.netname); teamsprint(self.team_no,self," places a mine\n"); //Set all the critical crap on the mine newmis = spawn(); newmis.movetype = MOVETYPE_BOUNCE; newmis.solid = SOLID_TRIGGER; newmis.takedamage = DAMAGE_AIM; newmis.classname = "grenade"; newmis.netname = "land_mine"; setsize (newmis, '-0.5 -0.5 -0.5', '1 1 1'); newmis.owner = self; makevectors (self.v_angle); newmis.avelocity = '300 300 300'; newmis.velocity = v_forward*600 + v_up * 200 + v_right*10 + v_up*10; setorigin (newmis, self.origin + (normalize(newmis.velocity) * 5)); newmis.angles = vectoangles(newmis.velocity); // newmis.skin = self.team_no - 1; newmis.skin = 1; // setmodel (newmis, "progs/lndmine.mdl"); setmodel (newmis, "progs/biggren.mdl"); newmis.heat = time; //Controls when mine can first go off newmis.has_tesla = 0; // OfN flag to Control what to sprint to owner of the mine when it exlodes newmis.last_attacked_time = time; //Time that you were last shot at newmis.health = MINE_DURATION; //Max time for mine to live newmis.touch = GuerillaTossTouch; newmis.think = GuerillaThink; newmis.nextthink = time + ACTIVATE_TIME; }; void() JobCrusader; void() UseJobSkill = { local float myjob; //Make sure they can do it if (self.done_custom & CUSTOM_BUILDING) return; if (self.job_finished > time) return; myjob = self.job; if (myjob & JOB_THIEF) JobThief(); else if (myjob & JOB_RUNNER) JobRunner(); else if (myjob & JOB_WARLOCK) { if (HasFlag(self.team_no) == FALSE) { CenterPrint(self, "No demons until your team has the flag!\n"); return; } JobWarlock(); } else if (myjob & JOB_CHAPLAN) JobChaplan(); else if (myjob & JOB_BERSERKER) JobBerserker(); else if (myjob & JOB_GUERILLA) JobGuerilla(); else if (myjob & JOB_JUDOKA) JobJudoka(); else if (myjob & JOB_HACKER) JobHacker(); else if (myjob & JOB_MARTYR) JobMartyr(); else if (myjob & JOB_ARMY) JobArmy(); else if (myjob & JOB_CRUSADER) JobCrusader(); else { sprint(self,PRINT_HIGH,"You don't have a job. Go get employed.\n"); self.job_finished = time + 5; //Don't let them print this message that often } }; #define NOHIDE_TIME 8 #define EXPOSURE_NOHIDE_TIME 20 #define EXPOSURE_NORELOAD_TIME 7 #define FULLH_NORELOAD_TIME 1.5 void (entity targ, float pain) RevealThief = { if (targ.classname != "player") return; if (!targ.invisible_time) { targ.modelindex = modelindex_player; targ.items = targ.items & ~IT_INVISIBILITY; } if (pain) { if (targ.invisible_time) sprint (targ, PRINT_HIGH, "You have been uncovered, or would have if you didn't have that ring.\n"); else sprint(targ,PRINT_HIGH,"You have been uncovered!\n"); targ.attack_finished = time + EXPOSURE_NORELOAD_TIME; targ.job_finished = time + EXPOSURE_NOHIDE_TIME; } else { if (targ.invisible_time) sprint (targ, PRINT_HIGH, "Leaving shadows... just as soon as this ring wears off...\n"); else sprint (targ, PRINT_HIGH, "Leaving shadows...\n"); targ.job_finished = time + NOHIDE_TIME; if (targ.job & JOB_FULL_HIDE) targ.attack_finished = time + FULLH_NORELOAD_TIME; } targ.job = targ.job - (targ.job & JOB_FULL_HIDE); targ.job = targ.job - (targ.job & JOB_ACTIVE); TeamFortress_SetSpeed(targ); };