home *** CD-ROM | disk | FTP | other *** search
/ PC Gamer 3.2 / 1997-05_Disc_3.2.iso / QUAKECTF / SRC / CTF / CLIENT.QC < prev    next >
Text File  |  1997-02-01  |  66KB  |  2,370 lines

  1.  
  2. // prototypes
  3. void () W_WeaponFrame;
  4. void() W_SetCurrentAmmo;
  5. void() player_pain;
  6. void() player_stand1;
  7. void (vector org) spawn_tfog;
  8. void (vector org, entity death_owner) spawn_tdeath;
  9. string(string old) RandomLevel;
  10.  
  11. float    modelindex_eyes, modelindex_player;
  12.  
  13. // CTFBOT [[[ various changes to the messages
  14. void() MOTD = 
  15. {
  16.     if (self.motd_count < 1) {
  17.         self.motd_time = time + 1;
  18.         self.motd_count = self.motd_count + 1;
  19.         if (teamplay & TEAM_CAPTURE_FLAG) 
  20.         {
  21.             if (teamplay & TEAM_INVASION)
  22.             {
  23.                 centerprint(self, "Welcome!\n\n├╘╞ ┬∩⌠ ▒«▓\n\nBased on ThreeWave CTF 3.5\n\nINVASION!");
  24.             }
  25.             // CTFBOT DTF [[[
  26.             else if (teamplay & TEAM_DAMAGE_THE_FLAG)
  27.             {
  28.                 if (self.team == TEAM_COLOR1 + 1)
  29.                     centerprint(self, "Welcome!\n\n─┴═┴╟┼ ╘╚┼ ╞╠┴╟ ░«▒\n\nBased on ThreeWave CTF 3.5\n\nYou are ╥┼─ team\n\nShoot the enemy flag to win\nTouch your flag to increase its health"); //red
  30.                 else
  31.                     centerprint(self, "Welcome!\n\n─┴═┴╟┼ ╘╚┼ ╞╠┴╟ ░«▒\n\nBased on Threewave CTF 3.5\n\nYou are ┬╠╒┼ team\n\nShoot the enemy flag to win\nTouch your flag to increase its health"); //blue
  32.             }
  33.             // CTFBOT DTF ]]]
  34.             else
  35.             {
  36.                 if (self.team == TEAM_COLOR1 + 1)
  37.                     centerprint(self, "Welcome!\n\n├╘╞ ┬∩⌠ ▒«▓\n\nBased on ThreeWave CTF 3.5\n\n├┴╨╘╒╥┼ ╘╚┼ ╞╠┴╟!\n\nYou are ╥┼─ team"); //red
  38.                 else
  39.                     centerprint(self, "Welcome!\n\n├╘╞ ┬∩⌠ ▒«▓\n\nBased on Threewave CTF 3.5\n\n├┴╨╘╒╥┼ ╘╚┼ ╞╠┴╟!\n\nYou are ┬╠╒┼ team"); //blue
  40.             }
  41.         }
  42.         else if (teamplay)
  43.             centerprint(self, "Welcome!\n\n├╘╞ ┬∩⌠ ▒«▓\n\nBased on Threewave CTF 3.5\n\nTEAMPLAY IS ACTIVE");
  44.         else
  45.             centerprint(self, "Welcome!\n\n├╘╞ ┬∩⌠ ▒«▓\n\nBased on ThreeWave Server Mods 3.5");
  46.         return;
  47.     } else {
  48.         sprint(self, "ThreeWave CTF info:\nwww.planetquake.com/quakex/threewave/\n");
  49.         sprint(self, "CTF Bot info:\nwww.interpath.net/~davidson/ctfbot.htm\n");
  50.         if (teamplay & TEAM_CAPTURE_FLAG) {
  51.             if (teamplay & TEAM_CAPTURE_CUSTOM)
  52.                 sprint(self, "You are playing\nCUSTOM ├┴╨╘╒╥┼ ╘╚┼ ╞╠┴╟ with ├╘╞ ┬∩⌠!\n"); // CAPTURE THE FLAG with CTF Bot
  53.             else
  54.                 sprint(self, "You are playing\n├┴╨╘╒╥┼ ╘╚┼ ╞╠┴╟ with ├╘╞ ┬∩⌠!\n"); // CAPTURE THE FLAG with CTF Bot
  55.             sprint(self, "  ThreeWave CTF │«╡ à CTF Bot ▒«▓\n  impulse 199 for help\n");
  56.         }
  57.         else
  58.  
  59.         {
  60.             sprint(self, "  CTF Bot ▒«▓\n  impulse 199 for help\n");
  61.  
  62.         }
  63.     }
  64.     self.motd_time = 0;
  65. };
  66. // CTFBOT ]]]
  67.  
  68. void() MOTD_ChooseTeam =
  69. {
  70.     if (!(teamplay & TEAM_CAPTURE_FLAG))
  71.         return;
  72.  
  73.     //self.motd_time = time + 1;        // CTFBOT removed
  74.     // CTFBOT DTF [[[
  75.     if (teamplay & TEAM_DAMAGE_THE_FLAG)    
  76.         centerprint(self, "Welcome!\n\n─┴═┴╟┼ ╘╚┼ ╞╠┴╟ ░«▒\n\nBased on ThreeWave CTF 3.5\n\nYou are an observer.\n\nPress ▒ to join ╥┼─ team\nPress ▓ to join ┬╠╒┼ team\nPress Jump for automatic team");
  77.     else
  78.     // CTFBOT DTF ]]]
  79.         centerprint(self, "Welcome!\n\n├╘╞ ┬∩⌠ ▒«▓\n\nBased on ThreeWave CTF 3.5\n\n├┴╨╘╒╥┼ ╘╚┼ ╞╠┴╟!\n\nYou are an observer.\n\nPress ▒ to join ╥┼─ team\nPress ▓ to join ┬╠╒┼ team\nPress Jump for automatic team");
  80. };
  81.  
  82. // ZOID: with several effects doing the dimlight thing, they just can't
  83. // turn it off.  Do not set self.effects with EF_DIMLIGHT directly.  This
  84. // will automatically do it when CheckPowerups is called
  85. // EF_DIMLIGHT is used;
  86. // 1. Invincible (Pentagram)
  87. // 2. Super Damage (Quad Power)
  88. // 3. Having Flag in Capture
  89. // self is player
  90. void () CheckDimLight = {
  91.     local float flag;
  92.  
  93.     flag = 0;
  94.     // invincable
  95.     if (self.invincible_finished > time)
  96.         flag = 1;
  97.     // quad
  98.     if (self.super_damage_finished > time)
  99.         flag = 1;
  100.     // flag
  101. //    if (self.player_flag & ITEM_ENEMY_FLAG)
  102. //        flag = 1;
  103.  
  104.     if (flag)
  105.         self.effects = self.effects | EF_DIMLIGHT;
  106.     else
  107.         self.effects = self.effects - (self.effects & EF_DIMLIGHT);
  108. };
  109.  
  110. /*
  111. =============================================================================
  112.  
  113.                 LEVEL CHANGING / INTERMISSION
  114.  
  115. =============================================================================
  116. */
  117.  
  118. float    intermission_running;
  119. float    intermission_exittime;
  120.  
  121. /*QUAKED info_intermission (1 0.5 0.5) (-16 -16 -16) (16 16 16)
  122. This is the camera point for the intermission.
  123. Use mangle instead of angle, so you can set pitch or roll as well as yaw.  'pitch roll yaw'
  124. */
  125. void() info_intermission =
  126. {
  127. };
  128.  
  129.  
  130.  
  131. void() SetChangeParms =
  132. {
  133.     if (self.health <= 0)
  134.     {
  135.         SetNewParms ();
  136. // *TEAMPLAY*
  137.         parm10 = self.lastteam;    // Save the current team of the player
  138.         parm15 = self.accesslvl; // remote admin state
  139.         parm16 = self.player_flag;
  140.         return;
  141.     }
  142.  
  143. // remove items
  144.     self.items = self.items - (self.items & 
  145.     (IT_KEY1 | IT_KEY2 | IT_INVISIBILITY | IT_INVULNERABILITY | IT_SUIT | IT_QUAD) );
  146.  
  147. // cap super health
  148.     if (self.health > 100)
  149.         self.health = 100;
  150.     if (self.health < 50)
  151.         self.health = 50;
  152.     if (teamplay & TEAM_CAPTURE_FLAG) // don't carry items between levels in CTF
  153.     {
  154.         // CTFBOT [[[
  155.         // save bots between levels
  156.         // since in capture the flag no weapons/items are taken between
  157.         // levels, we use those parms for bots.  Come up
  158.         // with another method of moving bots from level to level if
  159.         // you want to keep items between levels....
  160.         BotSaveParms();
  161.         // CTFBOT ]]]
  162.     }
  163.     else {
  164.         parm1 = self.items;
  165.         parm2 = self.health;
  166.         parm3 = self.armorvalue;
  167.         if (self.ammo_shells < 25)
  168.             parm4 = 25;
  169.         else
  170.             parm4 = self.ammo_shells;
  171.         parm5 = self.ammo_nails;
  172.         parm6 = self.ammo_rockets;
  173.         parm7 = self.ammo_cells;
  174.         parm8 = self.weapon;
  175.         parm9 = self.armortype * 100;
  176.     }
  177. // *TEAMPLAY*
  178.     parm10 = self.lastteam;    // Save the current team of the player
  179.     parm15 = self.accesslvl; // remote admin state
  180.     parm16 = self.player_flag;
  181. };
  182.  
  183. void() SetNewParms =
  184. {
  185.     parm1 = IT_SHOTGUN | IT_AXE | IT_HOOK;
  186.     parm2 = 100;
  187.     if (teamplay & TEAM_CAPTURE_FLAG) {
  188.         parm3 = 50;
  189.         parm9 = 30;
  190.         parm1 = parm1 + IT_ARMOR1;
  191.     } else {
  192.         parm3 = 0;
  193.         parm9 = 0;
  194.     }
  195.     parm4 = 40;
  196.     parm5 = 0;
  197.     parm6 = 0;
  198.     parm7 = 0;
  199.     parm8 = IT_SHOTGUN;
  200. // *TEAMPLAY*
  201.     parm10 = -1;    // Reset 
  202.     parm15 = 0; // remote admin
  203.     parm16 = 0;
  204. };
  205.  
  206. void() DecodeLevelParms =
  207. {
  208.     self.player_flag = self.player_flag | parm16;
  209.     self.player_flag = self.player_flag - (self.player_flag & ITEM_RUNE_MASK);
  210.     self.player_flag = self.player_flag - (self.player_flag & ITEM_ENEMY_FLAG);
  211.     self.skin = (self.player_flag & 65280)/256;
  212.     self.accesslvl = parm15;
  213.  
  214.     if (serverflags)
  215.     {
  216.         if (world.model == "maps/start.bsp")
  217.             SetNewParms ();        // take away all stuff on starting new episode
  218.     }
  219.     
  220.     // CTFBOT [[[
  221.     // decode bots between levels
  222.     BotDecodeParms();
  223.  
  224.     if (teamplay & TEAM_CAPTURE_FLAG) // don't carry items between levels in CTF
  225.         SetNewParms();
  226.     // CTFBOT ]]]
  227.     
  228.     self.items = parm1;
  229.     self.health = parm2;
  230.     self.armorvalue = parm3;
  231.     self.ammo_shells = parm4;
  232.     self.ammo_nails = parm5;
  233.     self.ammo_rockets = parm6;
  234.     self.ammo_cells = parm7;
  235.     self.weapon = parm8;
  236.     self.armortype = parm9 * 0.01;
  237.  
  238.     // *XXX* EXPERT CTF
  239.  
  240.     // Reset times for additional scoring system on level change and server join
  241.     // dprint("decode level parms\n");
  242.     self.last_returned_flag = -10;
  243.     self.last_fragged_carrier = -10;
  244.     self.flag_since = -10;
  245.     self.last_hurt_carrier = -10;
  246.  
  247. // *TEAMPLAY*
  248.     if(TeamColorIsLegal(parm10 - 1))
  249.         self.lastteam = parm10;
  250. };
  251.  
  252. /*
  253. ============
  254. FindIntermission
  255.  
  256. Returns the entity to view from
  257. ============
  258. */
  259. entity() FindIntermission =
  260. {
  261.     local    entity spot;
  262.     local    float cyc;
  263.  
  264. // look for info_intermission first
  265.     spot = find (world, classname, "info_intermission");
  266.     if (spot)
  267.     {    // pick a random one
  268.         cyc = random() * 4;
  269.         while (cyc > 1)
  270.         {
  271.             spot = find (spot, classname, "info_intermission");
  272.             if (!spot)
  273.                 spot = find (spot, classname, "info_intermission");
  274.             cyc = cyc - 1;
  275.         }
  276.         return spot;
  277.     }
  278.  
  279. // then look for the start position
  280.     spot = find (world, classname, "info_player_start");
  281.     if (spot)
  282.         return spot;
  283.     
  284. // testinfo_player_start is only found in regioned levels
  285.     spot = find (world, classname, "testplayerstart");
  286.     if (spot)
  287.         return spot;
  288.     
  289.     objerror ("FindIntermission: no spot");
  290. };
  291.  
  292.  
  293. string nextmap;
  294. void() GotoNextMap =
  295. {
  296.     if (cvar("samelevel"))    // if samelevel is set, stay on same level
  297.         changelevel (mapname);
  298.     else
  299.         changelevel (nextmap);
  300. };
  301.  
  302.  
  303. void() ExitIntermission =
  304. {
  305. // skip any text in deathmatch
  306.     if (deathmatch)
  307.     {
  308.         GotoNextMap ();
  309.         return;
  310.     }
  311.     
  312.     intermission_exittime = time + 1;
  313.     intermission_running = intermission_running + 1;
  314.  
  315. //
  316. // run some text if at the end of an episode
  317. //
  318.     if (intermission_running == 2)
  319.     {
  320.         if (world.model == "maps/e1m7.bsp")
  321.         {
  322.             WriteByte (MSG_ALL, SVC_CDTRACK);
  323.             WriteByte (MSG_ALL, 2);
  324.             WriteByte (MSG_ALL, 3);
  325.             if (!cvar("registered"))
  326.             {
  327.                 WriteByte (MSG_ALL, SVC_FINALE);
  328.                 WriteString (MSG_ALL, "As the corpse of the monstrous entity\nChthon sinks back into the lava whence\nit rose, you grip the Rune of Earth\nMagic tightly. Now that you have\nconquered the Dimension of the Doomed,\nrealm of Earth Magic, you are ready to\ncomplete your task in the other three\nhaunted lands of Quake. Or are you? If\nyou don't register Quake, you'll never\nknow what awaits you in the Realm of\nBlack Magic, the Netherworld, and the\nElder World!");
  329.             }
  330.             else
  331.             {
  332.                 WriteByte (MSG_ALL, SVC_FINALE);
  333.                 WriteString (MSG_ALL, "As the corpse of the monstrous entity\nChthon sinks back into the lava whence\nit rose, you grip the Rune of Earth\nMagic tightly. Now that you have\nconquered the Dimension of the Doomed,\nrealm of Earth Magic, you are ready to\ncomplete your task. A Rune of magic\npower lies at the end of each haunted\nland of Quake. Go forth, seek the\ntotality of the four Runes!");
  334.             }
  335.             return;
  336.         }
  337.         else if (world.model == "maps/e2m6.bsp")
  338.         {
  339.             WriteByte (MSG_ALL, SVC_CDTRACK);
  340.             WriteByte (MSG_ALL, 2);
  341.             WriteByte (MSG_ALL, 3);
  342.  
  343.             WriteByte (MSG_ALL, SVC_FINALE);
  344.             WriteString (MSG_ALL, "The Rune of Black Magic throbs evilly in\nyour hand and whispers dark thoughts\ninto your brain. You learn the inmost\nlore of the Hell-Mother; Shub-Niggurath!\nYou now know that she is behind all the\nterrible plotting which has led to so\nmuch death and horror. But she is not\ninviolate! Armed with this Rune, you\nrealize that once all four Runes are\ncombined, the gate to Shub-Niggurath's\nPit will open, and you can face the\nWitch-Goddess herself in her frightful\notherworld cathedral.");
  345.             return;
  346.         }
  347.         else if (world.model == "maps/e3m6.bsp")
  348.         {
  349.             WriteByte (MSG_ALL, SVC_CDTRACK);
  350.             WriteByte (MSG_ALL, 2);
  351.             WriteByte (MSG_ALL, 3);
  352.  
  353.             WriteByte (MSG_ALL, SVC_FINALE);
  354.             WriteString (MSG_ALL, "The charred viscera of diabolic horrors\nbubble viscously as you seize the Rune\nof Hell Magic. Its heat scorches your\nhand, and its terrible secrets blight\nyour mind. Gathering the shreds of your\ncourage, you shake the devil's shackles\nfrom your soul, and become ever more\nhard and determined to destroy the\nhideous creatures whose mere existence\nthreatens the souls and psyches of all\nthe population of Earth.");
  355.             return;
  356.         }
  357.         else if (world.model == "maps/e4m7.bsp")
  358.         {
  359.             WriteByte (MSG_ALL, SVC_CDTRACK);
  360.             WriteByte (MSG_ALL, 2);
  361.             WriteByte (MSG_ALL, 3);
  362.  
  363.             WriteByte (MSG_ALL, SVC_FINALE);
  364.             WriteString (MSG_ALL, "Despite the awful might of the Elder\nWorld, you have achieved the Rune of\nElder Magic, capstone of all types of\narcane wisdom. Beyond good and evil,\nbeyond life and death, the Rune\npulsates, heavy with import. Patient and\npotent, the Elder Being Shub-Niggurath\nweaves her dire plans to clear off all\nlife from the Earth, and bring her own\nfoul offspring to our world! For all the\ndwellers in these nightmare dimensions\nare her descendants! Once all Runes of\nmagic power are united, the energy\nbehind them will blast open the Gateway\nto Shub-Niggurath, and you can travel\nthere to foil the Hell-Mother's plots\nin person.");
  365.             return;
  366.         }
  367.  
  368.         GotoNextMap();
  369.     }
  370.     
  371.     if (intermission_running == 3)
  372.     {
  373.         if (!cvar("registered"))
  374.         {    // shareware episode has been completed, go to sell screen
  375.             WriteByte (MSG_ALL, SVC_SELLSCREEN);
  376.             return;
  377.         }
  378.         
  379.         if ( (serverflags&15) == 15)
  380.         {
  381.             WriteByte (MSG_ALL, SVC_FINALE);
  382.             WriteString (MSG_ALL, "Now, you have all four Runes. You sense\ntremendous invisible forces moving to\nunseal ancient barriers. Shub-Niggurath\nhad hoped to use the Runes Herself to\nclear off the Earth, but now instead,\nyou will use them to enter her home and\nconfront her as an avatar of avenging\nEarth-life. If you defeat her, you will\nbe remembered forever as the savior of\nthe planet. If she conquers, it will be\nas if you had never been born.");
  383.             return;
  384.         }
  385.         
  386.     }
  387.  
  388.     GotoNextMap();
  389. };
  390.  
  391. /*
  392. ============
  393. IntermissionThink
  394.  
  395. When the player presses attack or jump, change to the next level
  396. ============
  397. */
  398. void() IntermissionThink =
  399. {
  400.     if (time < intermission_exittime)
  401.         return;
  402.  
  403.     if (!self.button0 && !self.button1 && !self.button2)
  404.         return;
  405.     
  406.     ExitIntermission ();
  407. };
  408.  
  409. void() execute_changelevel =
  410. {
  411.     local entity    pos;
  412.  
  413.     intermission_running = 1;
  414.     
  415. // enforce a wait time before allowing changelevel
  416.     if (deathmatch)
  417.         intermission_exittime = time + 8;
  418.     else
  419.         intermission_exittime = time + 2;
  420.  
  421.     WriteByte (MSG_ALL, SVC_CDTRACK);
  422.     WriteByte (MSG_ALL, 3);
  423.     WriteByte (MSG_ALL, 3);
  424.     
  425.     pos = FindIntermission ();
  426.  
  427.     other = find (world, classname, "player");
  428.     while (other != world)
  429.     {
  430.         other.view_ofs = '0 0 0';
  431.         other.angles = other.v_angle = pos.mangle;
  432.         other.fixangle = TRUE;        // turn this way immediately
  433.         other.nextthink = time + 0.5;
  434.         other.takedamage = DAMAGE_NO;
  435.         other.solid = SOLID_NOT;
  436.         other.movetype = MOVETYPE_NONE;
  437.         other.modelindex = 0;
  438.         setorigin (other, pos.origin);
  439.         other = find (other, classname, "player");
  440.     }    
  441.  
  442.     WriteByte (MSG_ALL, SVC_INTERMISSION);
  443. };
  444.  
  445.  
  446. void() changelevel_touch =
  447. {
  448.     local entity    pos;
  449.  
  450.     if (other.classname != "player")    // CTFBOT bots can't change levels
  451.         return;
  452.  
  453. // *XXX* EXPERT CTF
  454. // Removed code that made exits do nothing in CTF games
  455.  
  456.     if (cvar("noexit") && (teamplay & TEAM_CAPTURE_FLAG))
  457.         return;
  458.  
  459.     if ((cvar("noexit") == 1) || ((cvar("noexit") == 2) && (mapname != "start")))
  460.     {
  461.         T_Damage (other, self, self, 50000);
  462.         return;
  463.     }
  464.  
  465.     if (coop || deathmatch)
  466.     {
  467.         bprint (other.netname);
  468.         bprint (" exited the level\n");
  469.     }
  470.     
  471. // ZOID - go to next level randomly
  472.     if (deathmatch)
  473.         nextmap = RandomLevel(mapname);
  474.     else
  475.         nextmap = self.map;
  476.  
  477.     SUB_UseTargets ();
  478.  
  479.     if ( (self.spawnflags & 1) && (deathmatch == 0) )
  480.     {    // NO_INTERMISSION
  481.         GotoNextMap();
  482.         return;
  483.     }
  484.     
  485.     self.touch = SUB_Null;
  486.  
  487. // we can't move people right now, because touch functions are called
  488. // in the middle of C movement code, so set a think time to do it
  489.     self.think = execute_changelevel;
  490.     self.nextthink = time + 0.1;
  491. };
  492.  
  493. /*QUAKED trigger_changelevel (0.5 0.5 0.5) ? NO_INTERMISSION
  494. When the player touches this, he gets sent to the map listed in the "map" variable.  Unless the NO_INTERMISSION flag is set, the view will go to the info_intermission spot and display stats.
  495. */
  496. void() trigger_changelevel =
  497. {
  498.     if (!self.map)
  499.         objerror ("chagnelevel trigger doesn't have map");
  500.     
  501.     InitTrigger ();
  502.     self.touch = changelevel_touch;
  503. };
  504.  
  505.  
  506. /*
  507. =============================================================================
  508.  
  509.                 PLAYER GAME EDGE FUNCTIONS
  510.  
  511. =============================================================================
  512. */
  513.  
  514. void() set_suicide_frame;
  515.  
  516. // called by ClientKill and DeadThink
  517. void() respawn =
  518. {
  519.     if (coop)
  520.     {
  521.         // make a copy of the dead body for appearances sake
  522.         CopyToBodyQue (self);
  523.         // get the spawn parms as they were at level start
  524.         setspawnparms (self);
  525.         // respawn        
  526.         PutClientInServer ();
  527.     }
  528.     else if (deathmatch)
  529.     {
  530.         // make a copy of the dead body for appearances sake
  531.         CopyToBodyQue (self);
  532.         // set default spawn parms
  533.         SetNewParms ();
  534.         // respawn        
  535.         PutClientInServer ();
  536.     }
  537.     else
  538.     {    // restart the entire server
  539.         localcmd ("restart\n");
  540.     }
  541. };
  542.  
  543.  
  544. /*
  545. ============
  546. ClientKill
  547.  
  548. Player entered the suicide command
  549. ============
  550. */
  551. void() ClientKill =
  552. {
  553.     if (self.observer)
  554.         return; // can't suicide when observer
  555.  
  556.     if (self.suicide_count > 3) {
  557.         sprint(self, "You have suicided too much already.\n");
  558.         return;
  559.     }
  560.     bprint (self.netname);
  561.     bprint (" suicides\n");
  562.     DropRune();
  563.     TeamCaptureDropFlagOfPlayer(self);
  564.     set_suicide_frame ();
  565.     self.modelindex = modelindex_player;
  566.     self.frags = self.frags - 2;    // extra penalty
  567.     self.suicide_count = self.suicide_count + 1;
  568.  
  569.     self.num_suicides = self.num_suicides + 1;    // CTFBOT
  570.     respawn ();
  571. };
  572.  
  573. void() SilentKill =
  574. {
  575.     set_suicide_frame ();
  576.     self.modelindex = modelindex_player;
  577.     respawn ();
  578. };
  579.  
  580. float(vector v) CheckSpawnPoint =
  581. {
  582.     return FALSE;
  583. };
  584.  
  585. /*
  586. void () Debugging =
  587. {
  588. local entity ent;
  589. local float num;
  590.  
  591.     dprint("lastspawn:\n");
  592.     eprint(lastspawn);
  593.     dprint("\n");
  594.     dprint("world:\n");
  595.     eprint(world);
  596.     dprint("\n");
  597.  
  598.     dprint("info_player_deathmatch 1:\n");
  599.  
  600.     num = 0;
  601.     ent = find(world, classname, "info_player_deathmatch");
  602.     if (ent == world)
  603.         dprint("find 1 didn't work!\n");
  604.     while (ent != world)
  605.     {
  606.         num = num + 1;
  607.         dprint(ftos(num));
  608.         dprint(" ");
  609.         dprint(vtos(ent.origin));
  610.         dprint("\n");
  611.         ent = find(ent, classname, "info_player_deathmatch");
  612.     }
  613.  
  614.     dprint("info_player_deathmatch 2:\n");
  615.  
  616.     num = 0;
  617.     ent = findradius('0 0 0', 99999999);
  618.     if (ent == world)
  619.         dprint("findradius didn't work!\n");
  620.     while (ent != world)
  621.     {
  622.         if (ent.classname == "info_player_deathmatch")
  623.         {
  624.             num = num + 1;
  625.             dprint(ftos(num));
  626.             dprint(" ");
  627.             dprint(vtos(ent.origin));
  628.             dprint("\n");
  629.         }
  630.         ent = ent.chain;
  631.     }
  632.     
  633.     ent = find(world, classname, "player");
  634.     stuffcmd(ent, "pause\n");
  635. };
  636. */
  637.  
  638. /*
  639. ============
  640. SelectSpawnPoint
  641.  
  642. Returns the entity to spawn at
  643. ============
  644. */
  645. entity() SelectSpawnPoint =
  646. {
  647.     local    entity spot;
  648.         
  649. // testinfo_player_start is only found in regioned levels
  650.     spot = find (world, classname, "testplayerstart");
  651.     if (spot)
  652.         return spot;
  653.         
  654. // choose a info_player_deathmatch point
  655.     if (coop)
  656.     {
  657.         lastspawn = find(lastspawn, classname, "info_player_coop");
  658.         if (lastspawn == world)
  659.             lastspawn = find (lastspawn, classname, "info_player_start");
  660.         if (lastspawn != world)
  661.             return lastspawn;
  662.     }
  663.     else if (deathmatch)
  664.     {
  665.         if (!self.killed) {
  666.             spot = TeamCaptureSpawn();
  667.             if (spot != world) 
  668.                 return spot;
  669.         }
  670.  
  671.         lastspawn = find(lastspawn, classname, "info_player_deathmatch");
  672.         if (lastspawn == world)
  673.             lastspawn = find (lastspawn, classname, "info_player_deathmatch");
  674.         
  675.         if (lastspawn != world)
  676.             return lastspawn;
  677.     }
  678.  
  679.     if (serverflags)
  680.     {    
  681.         // return with a rune to start
  682.         spot = find (world, classname, "info_player_start2");
  683.         if (spot)
  684.             return spot;
  685.     }
  686.     
  687.     spot = find (world, classname, "info_player_start");
  688.     if (!spot)
  689.         error ("PutClientInServer: no info_player_start on level");
  690.     
  691.     return spot;
  692. };
  693.  
  694. /*
  695. ===========
  696. PutClientInServer
  697.  
  698. called each time a player is spawned
  699. ============
  700. */
  701. void() DecodeLevelParms;
  702. void() PlayerDie;
  703.  
  704.  
  705. void() PutClientInServer =
  706. {
  707.     local    entity spot;
  708.  
  709.     spot = SelectSpawnPoint ();
  710. //ZOID: Minimize chance of telefragging someone, from Johannes Plass
  711. //(plass@dipmza.physik.uni-mainz.de) ServerModules package
  712.     spot = TelefragSelectSpawnPoint(spot);
  713.  
  714.     self.classname = "player";
  715.     self.health = 100;
  716.     self.takedamage = DAMAGE_AIM;
  717.     self.solid = SOLID_SLIDEBOX;
  718.     self.movetype = MOVETYPE_WALK;
  719.     self.show_hostile = 0;
  720.     self.max_health = 100;
  721.     self.flags = FL_CLIENT;
  722.     self.air_finished = time + 12;
  723.     self.dmg = 2;           // initial water damage
  724.     self.super_damage_finished = 0;
  725.     self.radsuit_finished = 0;
  726.     self.invisible_finished = 0;
  727.     self.invincible_finished = 0;
  728.     self.effects = 0;
  729.     self.invincible_time = 0;
  730.     self.staydeadtime = 0;
  731.     self.regen_time = 0;
  732.     self.rune_notice_time = 0;
  733.     
  734.     self.observer_flags = 0;            // CTFBOT
  735.     self.escort_time = time - 999;        // CTFBOT
  736.  
  737.     self.last_hurt_carrier = -10;
  738.  
  739.     DecodeLevelParms ();
  740.     
  741.     W_SetCurrentAmmo ();
  742.  
  743.     self.attack_finished = time;
  744.     self.th_pain = player_pain;
  745.     self.th_die = PlayerDie;
  746.     
  747.     self.deadflag = DEAD_NO;
  748. // paustime is set by teleporters to keep the player from moving a while
  749.     self.pausetime = 0;
  750.     
  751. //    spot = SelectSpawnPoint ();
  752.  
  753.     self.fixangle = TRUE;        // turn this way immediately
  754.  
  755. // oh, this is a hack!
  756.     setmodel (self, "progs/eyes.mdl");
  757.     modelindex_eyes = self.modelindex;
  758.  
  759.     setmodel (self, "progs/player.mdl");
  760.     modelindex_player = self.modelindex;
  761.  
  762.     setsize (self, VEC_HULL_MIN, VEC_HULL_MAX);
  763.     self.view_ofs = '0 0 22';
  764.  
  765.     self.origin = spot.origin + '0 0 1';
  766.     self.angles = spot.angles;
  767.     player_stand1 ();
  768.  
  769.     if (deathmatch || coop)
  770.     {
  771.         makevectors(self.angles);
  772.         spawn_tfog (self.origin + v_forward*20);
  773.     }
  774.  
  775.     spawn_tdeath (self.origin, self);
  776. };
  777.  
  778.  
  779. /*
  780. =============================================================================
  781.  
  782.                 QUAKED FUNCTIONS
  783.  
  784. =============================================================================
  785. */
  786.  
  787.  
  788. /*QUAKED info_player_start (1 0 0) (-16 -16 -24) (16 16 24)
  789. The normal starting point for a level.
  790. */
  791. void() info_player_start =
  792. {
  793. };
  794.  
  795.  
  796. /*QUAKED info_player_start2 (1 0 0) (-16 -16 -24) (16 16 24)
  797. Only used on start map for the return point from an episode.
  798. */
  799. void() info_player_start2 =
  800. {
  801. };
  802.  
  803.  
  804. void() SpawnRunes;
  805.  
  806. /*
  807. saved out by quaked in region mode
  808. */
  809. void() testplayerstart =
  810. {
  811. };
  812.  
  813. /*QUAKED info_player_deathmatch (1 0 1) (-16 -16 -24) (16 16 24)
  814. potential spawning position for deathmatch games
  815. */
  816. void() info_player_deathmatch =
  817. {
  818.     local entity rspawn;
  819.  
  820.     if (deathmatch) {
  821.         // spawn the runes
  822.         rspawn = spawn();
  823.         rspawn.nextthink = time + 0.1;
  824.         rspawn.think = SpawnRunes;
  825.     }
  826. };
  827.  
  828. void() info_player_team1 =
  829. {
  830. };
  831.  
  832. void() info_player_team2 =
  833. {
  834. };
  835.  
  836. /*QUAKED info_player_coop (1 0 1) (-16 -16 -24) (16 16 24)
  837. potential spawning position for coop games
  838. */
  839. void() info_player_coop =
  840. {
  841. };
  842.  
  843. /*
  844. ===============================================================================
  845.  
  846. RULES
  847.  
  848. ===============================================================================
  849. */
  850.  
  851. /*
  852. go to the next level for deathmatch
  853. only called if a time or frag limit has expired
  854. */
  855. void() NextLevel =
  856. {
  857.     local entity o;
  858.  
  859.     if (mapname == "start")
  860.     {
  861.         if (!cvar("registered"))
  862.         {
  863.             mapname = "e1m1";
  864.         }
  865.         else if (!(serverflags & 1))
  866.         {
  867.             mapname = "e1m1";
  868.             serverflags = serverflags | 1;
  869.         }
  870.         else if (!(serverflags & 2))
  871.         {
  872.             mapname = "e2m1";
  873.             serverflags = serverflags | 2;
  874.         }
  875.         else if (!(serverflags & 4))
  876.         {
  877.             mapname = "e3m1";
  878.             serverflags = serverflags | 4;
  879.         }
  880.         else if (!(serverflags & 8))
  881.         {
  882.             mapname = "e4m1";
  883.             serverflags = serverflags - 7;
  884.         }
  885.  
  886.         o = spawn();
  887.         o.map = mapname;
  888.     }
  889.     else
  890.     {
  891. //ZOID: RandomLevel is called.  Note that RandomLevel handles Capture
  892. //levels as well.
  893.         o = spawn();
  894.         o.map = RandomLevel(mapname);
  895.  
  896.         // find a trigger changelevel
  897. //        o = find(world, classname, "trigger_changelevel");
  898.  
  899.         // go back to start if no trigger_changelevel
  900. //        if (!o)
  901. //        {
  902. //            mapname = "start";
  903. //            o = spawn();
  904. //            o.map = mapname;
  905. //        }
  906.     }
  907.  
  908.     nextmap = o.map;
  909.     gameover = TRUE;
  910.     
  911.     if (o.nextthink < time)
  912.     {
  913.         o.think = execute_changelevel;
  914.         o.nextthink = time + 0.1;
  915.     }
  916.  
  917. };
  918.  
  919. // Return a new random level, that doesn't match old
  920.  
  921. // Rewriten for CTF
  922. // uses the server temp1 cvar for level tracking
  923. // temp1 is mapped out as:
  924. //  bit 16 to 12 for id level num 0xf800, 63488
  925. //  bit 11 to 7 for Custom level num 0x0f80, 3968
  926. //  bit 6 to 0 for percent of custom CTF levels 0x007f, 127
  927. // server ops should just set temp1 to the percent and the start id/ctf
  928. // levels will be zero (first level)
  929. // 1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 65536
  930. // ---------------- --------------------- ---------------------------
  931. // x x x x  x  x  x
  932. //                  x   x   x   x    x
  933. //                                        x    x    x     x     x
  934. //
  935. string(string old) RandomLevel =
  936. {
  937. local float r;
  938. local float idlvl, ctflvl;
  939. local float percent;
  940. local string smap;
  941. local string s;
  942.  
  943.     r = random();
  944.     if (teamplay & TEAM_CAPTURE_FLAG) {
  945.         // extract stuff from temp1
  946.         percent = cvar("temp1") & 127;
  947.         idlvl = (cvar("temp1") & 3968) / 128;
  948.         ctflvl = (cvar("temp1") & 126976) / 4096;
  949.  
  950.         if ((random() < percent / 100.0) && (teamplay & TEAM_CAPTURE_CUSTOM)) {
  951.             ctflvl = ctflvl + 1;
  952.             if (ctflvl == 1) smap = "ctf2";
  953.             else if (ctflvl == 2) smap = "ctf3";
  954.             else if (ctflvl == 3) smap = "ctf4";
  955.             else if (ctflvl == 4) smap = "ctf5";
  956.             else if (ctflvl == 5) smap = "ctf6";
  957.             else if (ctflvl == 6) smap = "ctf7";
  958.             else if (ctflvl == 7) smap = "ctf8";
  959.             else {
  960.                 smap = "ctf1";
  961.                 ctflvl = 0;
  962.             }
  963.         } else {
  964.             // capture levels
  965.             idlvl = idlvl + 1;
  966.             if (idlvl == 1) smap = "e1m2";
  967.             else if (idlvl == 2) smap = "e1m3";
  968.             else if (idlvl == 3) smap = "e1m4";
  969.             else if (idlvl == 4) smap = "e1m5";
  970.             else if (idlvl == 5) smap = "e1m6";
  971.             else if (idlvl == 6) smap = "e1m8";
  972.             else if (idlvl == 7) smap = "e2m1";
  973.             else if (idlvl == 8) smap = "e2m2";
  974.             else if (idlvl == 9) smap = "e2m3";
  975.             else if (idlvl == 10) smap = "e2m5";
  976.             else if (idlvl == 11) smap = "e4m3";
  977.             else if (idlvl == 12) smap = "e4m4";
  978.             else if (idlvl == 13) smap = "e4m5";
  979.             else if (idlvl == 14) smap = "e4m6";
  980.             else if (idlvl == 15) smap = "dm3";
  981.             else if (idlvl == 16) smap = "dm6";
  982.             else {
  983.                 smap = "e1m1";
  984.                 idlvl = 0;
  985.             }
  986.         }
  987.         // recombine the cvar
  988.         s = ftos(ctflvl * 4096 + idlvl * 128 + percent);
  989.         cvar_set("temp1", s);
  990.         return smap;
  991.     }
  992.     if (r > 0.8) {
  993.         r = random() * 7;
  994.         if ((r >= 6) && (old != "e1m1")) return ("e1m1"); // e1m1: Slipgate Complex -- by John Romero
  995.         if ((r >= 5) && (old != "e1m2")) return ("e1m2"); // e1m2: Castle of the Damned -- by Tim Willits
  996.         if ((r >= 4) && (old != "e1m3")) return ("e1m3"); // e1m3: The Necropolis -- by Tim Willits
  997.         if ((r >= 3) && (old != "e1m4")) return ("e1m4"); // e1m4: The Grisly Grotto -- by Tim Willits
  998.         if ((r >= 2) && (old != "e1m5")) return ("e1m5"); // e1m5: Gloom Keep -- by Tim Willits
  999.         if ((r >= 1) && (old != "e1m6")) return ("e1m6"); // e1m6: The Door To Chthon -- by American McGee
  1000. // e1m7: The House of Chthon -- removed because of lag problems
  1001.         return ("e1m8"); // e1m8: Ziggurat Vertigo --by American McGee
  1002.     } else if (r > 0.6) {
  1003.         r = random() * 6;
  1004.         if ((r >= 5) && (old != "e2m1")) return ("e2m1"); // e2m1: The Installation -- by John Romero
  1005.         if ((r >= 4) && (old != "e2m2")) return ("e2m2"); // e2m2: Ogre Citadel -- by John Romero
  1006.         if ((r >= 3) && (old != "e2m3")) return ("e2m3"); // e2m3: Crypt of Decay -- by John Romero
  1007.         if ((r >= 2) && (old != "e2m4")) return ("e2m4"); // e2m4: The Ebon Fortress  -- by John Romero
  1008.         if ((r >= 1) && (old != "e2m5")) return ("e2m5"); // e2m5: The Wizard's Manse -- by John Romero
  1009. // e2m6: The Dismal Oubliette -- removed because it sucks for dm
  1010.         return ("e2m7"); // e2m7: Underearth --by Tim Willits
  1011.     } else if (r > 0.4) {
  1012.         r = random() * 6;
  1013.         if ((r >= 5) && (old != "e3m1")) return ("e3m1"); // e3m1: Termination Central -- by John Romero
  1014.         if ((r >= 4) && (old != "e3m2")) return ("e3m2"); // e3m2: The Vaults of Zin -- by American McGee
  1015.         if ((r >= 3) && (old != "e3m3")) return ("e3m3"); // e3m3: The Tomb of Terror -- by American McGee
  1016. // e3m4 (Satan's Dark Delight) removed because it sucks for dm
  1017.         if ((r >= 2) && (old != "e3m5")) return ("e3m5"); // e3m5: Wind Tunnels --by Tim Willits
  1018.         if ((r >= 1) && (old != "e3m6")) return ("e3m6"); // e3m6: Chambers of Torment -- by American McGee & Tim Willits
  1019.         return ("e3m7"); // e3m7: The Haunted Halls -- by American McGee
  1020.     } else if (r > 0.2) {
  1021.         r = random() * 8;
  1022.         if ((r >= 7) && (old != "e4m1")) return ("e4m1"); // e4m1: The Sewage System -- by Tim Willits
  1023.         if ((r >= 6) && (old != "e4m2")) return ("e4m2"); // e4m2: The Tower of Despair --by Sandy Petersen
  1024.         if ((r >= 5) && (old != "e4m3")) return ("e4m3"); // e4m3: The Elder God Shrine --by Sandy Petersen
  1025.         if ((r >= 4) && (old != "e4m4")) return ("e4m4"); // e4m4: The Palace of Hate --by Sandy Petersen
  1026.         if ((r >= 3) && (old != "e4m5")) return ("e4m5"); // e4m5: Hell's Atrium --by Sandy Petersen
  1027.         if ((r >= 2) && (old != "e4m6")) return ("e4m6"); // e4m6: The Pain Maze --by Sandy Petersen
  1028.         if ((r >= 1) && (old != "e4m7")) return ("e4m7"); // e4m7: Azure Agony --by Sandy Petersen
  1029.         return ("e4m8"); // e4m8: The Nameless City -- by Sandy Petersen
  1030.     } else {
  1031.         r = random() * 6;
  1032.         if ((r >= 5) && (old != "dm1")) return ("dm1"); // dm1: Place of Two Deaths --by Tim Willits
  1033.         if ((r >= 4) && (old != "dm2")) return ("dm2"); // dm2: Claustrophobopolis --by American McGee
  1034.         if ((r >= 3) && (old != "dm3")) return ("dm3"); // dm3: The Abandoned Base --by John Romero
  1035.         if ((r >= 2) && (old != "dm4")) return ("dm4"); // dm4: The Bad Place --by American McGee
  1036.         if ((r >= 1) && (old != "dm5")) return ("dm5"); // dm5: The Cistern --by Tim Willits
  1037.         return ("dm6"); // dm6: The Dark Zone --by Tim Willits
  1038.     }
  1039.     return("start");
  1040. };
  1041.  
  1042. /*
  1043. ============
  1044. CheckRules
  1045.  
  1046. Exit deathmatch games upon conditions
  1047. ============
  1048. */
  1049. void() CheckRules =
  1050. {
  1051.     local    float        timelimit;
  1052.     local    float        fraglimit;
  1053.     local    entity        p;
  1054.     local    float        total1, total2;
  1055.  
  1056.     // *XXX* EXPERT CTF variables for team frag count
  1057.     local string    s;
  1058.  
  1059.     if (gameover)    // someone else quit the game already
  1060.         return;
  1061.         
  1062.     timelimit = cvar("timelimit") * 60;
  1063.     fraglimit = cvar("fraglimit");
  1064.     
  1065.     if ((timelimit && time >= timelimit) ||
  1066.         (fraglimit && (self.frags >= fraglimit)) ||
  1067.         (goal_winner != world) ||
  1068.         (dtf_level_over)
  1069.         ) {
  1070.         
  1071.         level_over_time = time;
  1072.         
  1073.         if (!(teamplay & TEAM_DAMAGE_THE_FLAG))
  1074.         {
  1075.             // CTFBOT EXTRAS print detailed scores first, so the total appears as a summary 
  1076.             DetailedScoresBroadcast();        // CTFBOT EXTRAS  
  1077.         }
  1078.         
  1079.         ToggleAllConsoles();        // CTFBOT EXTRAS
  1080.         
  1081.         /* CTFBOT this is superceded by the detailed scores
  1082.         
  1083.         // count up total
  1084.         total1 = total2 = 0;
  1085.         p = find(world, classname, "player");
  1086.         while (p != world) {
  1087.             if (p.lastteam == TEAM_COLOR1 + 1)
  1088.                 total1 = total1 + p.frags;
  1089.             else if (p.lastteam == TEAM_COLOR2 + 1)
  1090.                 total2 = total2 + p.frags;
  1091.  
  1092.             p = FindNextPlayerOrBot(p);    // CTFBOT find bots also 
  1093.         }
  1094.  
  1095.         if (total1 > total2) {
  1096.             bprint("╥┼─ team won with ");    // CTFBOT EXTRAS changed
  1097.             s = ftos(total1);
  1098.             bprint(s);
  1099.             bprint(" points!\n");
  1100.             bprint("┬╠╒┼ team lost with ");
  1101.             s = ftos(total2);
  1102.             bprint(s);
  1103.             bprint(" points.\n");
  1104.         } else if (total1 < total2) {
  1105.             bprint("┬╠╒┼ team won with ");    // CTFBOT EXTRAS changed
  1106.             s = ftos(total2);        // CTFBOT EXTRAS bug fix
  1107.             bprint(s);
  1108.             bprint(" points!\n");
  1109.             bprint("╥┼─ team lost with ");
  1110.             s = ftos(total1);        // CTFBOT EXTRAS bug fix
  1111.             bprint(s);
  1112.             bprint(" points.\n");
  1113.         } else {
  1114.             bprint("┬╠╒┼ and ╥┼─ team tied level with ");
  1115.             s = ftos(total1);
  1116.             bprint(s);
  1117.             bprint(" points!\n");
  1118.         }
  1119.         
  1120.         CTFBOT */
  1121.         
  1122.         NextLevel ();
  1123.         return;
  1124.     }
  1125.  
  1126.     // update team scores? 
  1127.     TeamCaptureCheckUpdate(FALSE);
  1128.  
  1129. };
  1130.  
  1131. //============================================================================
  1132.  
  1133. void() PlayerDeathThink =
  1134. {
  1135.     local entity    old_self;
  1136.     local float        forward;
  1137.  
  1138.     if ((self.flags & FL_ONGROUND))
  1139.     {
  1140.         forward = vlen (self.velocity);
  1141.         forward = forward - 20;
  1142.         if (forward <= 0)
  1143.             self.velocity = '0 0 0';
  1144.         else    
  1145.             self.velocity = forward * normalize(self.velocity);
  1146.     }
  1147.  
  1148. // wait for all buttons released
  1149.     if (self.deadflag == DEAD_DEAD)
  1150.     {
  1151.         if (self.button2 || self.button1 || self.button0)
  1152.             return;
  1153.         self.deadflag = DEAD_RESPAWNABLE;
  1154.         return;
  1155.     }
  1156.  
  1157. // wait for any button down
  1158.     if (!self.button2 && !self.button1 && !self.button0)
  1159.         return;
  1160.  
  1161.     self.button0 = 0;
  1162.     self.button1 = 0;
  1163.     self.button2 = 0;
  1164.     respawn();
  1165. };
  1166.  
  1167.  
  1168. void() PlayerJump =
  1169. {
  1170.     local vector start, end;
  1171.     
  1172.     if (self.flags & FL_WATERJUMP)
  1173.         return;
  1174.     
  1175.     if (self.waterlevel >= 2)
  1176.     {
  1177.         if (self.watertype == CONTENT_WATER)
  1178.             self.velocity_z = 100;
  1179.         else if (self.watertype == CONTENT_SLIME)
  1180.             self.velocity_z = 80;
  1181.         else
  1182.             self.velocity_z = 50;
  1183.  
  1184. // play swiming sound
  1185.         if (self.swim_flag < time)
  1186.         {
  1187.             self.swim_flag = time + 1;
  1188.             if (random() < 0.5)
  1189.                 sound (self, CHAN_BODY, "misc/water1.wav", 1, ATTN_NORM);
  1190.             else
  1191.                 sound (self, CHAN_BODY, "misc/water2.wav", 1, ATTN_NORM);
  1192.         }
  1193.  
  1194.         return;
  1195.     }
  1196.  
  1197.     if (!(self.flags & FL_ONGROUND))
  1198.         return;
  1199.  
  1200.     if ( !(self.flags & FL_JUMPRELEASED) )
  1201.         return;        // don't pogo stick
  1202.  
  1203.     self.flags = self.flags - (self.flags & FL_JUMPRELEASED);
  1204.  
  1205.     self.flags = self.flags - FL_ONGROUND;    // don't stairwalk
  1206.     
  1207.     self.button2 = 0;
  1208. // player jumping sound
  1209.     sound (self, CHAN_BODY, "player/plyrjmp8.wav", 1, ATTN_NORM);
  1210.     self.velocity_z = self.velocity_z + 270;
  1211. };
  1212.  
  1213.  
  1214. /*
  1215. ===========
  1216. WaterMove
  1217.  
  1218. ============
  1219. */
  1220. .float    dmgtime;
  1221.  
  1222. void() WaterMove =
  1223. {
  1224. //dprint (ftos(self.waterlevel));
  1225.     if (self.movetype == MOVETYPE_NOCLIP)
  1226.         return;
  1227.     if (self.health < 0)
  1228.         return;
  1229.  
  1230.     if (self.waterlevel != 3)
  1231.     {
  1232.         if (self.air_finished < time)
  1233.             sound (self, CHAN_VOICE, "player/gasp2.wav", 1, ATTN_NORM);
  1234.         else if (self.air_finished < time + 9)
  1235.             sound (self, CHAN_VOICE, "player/gasp1.wav", 1, ATTN_NORM);
  1236.         self.air_finished = time + 12;
  1237.         self.dmg = 2;
  1238.     }
  1239.     else if (self.air_finished < time)
  1240.     {    // drown!
  1241.         if (self.pain_finished < time)
  1242.         {
  1243.             self.dmg = self.dmg + 2;
  1244.             if (self.dmg > 15)
  1245.                 self.dmg = 10;
  1246.             T_Damage (self, world, world, self.dmg);
  1247.             self.pain_finished = time + 1;
  1248.         }
  1249.     }
  1250.  
  1251.     if (!self.waterlevel)
  1252.     {
  1253.         if (self.flags & FL_INWATER)
  1254.         {    
  1255.             // play leave water sound
  1256.             sound (self, CHAN_BODY, "misc/outwater.wav", 1, ATTN_NORM);
  1257.             self.flags = self.flags - FL_INWATER;
  1258.         }
  1259.         return;
  1260.     }
  1261.  
  1262.     if (self.watertype == CONTENT_LAVA)
  1263.     {    // do damage
  1264.         if (self.dmgtime < time)
  1265.         {
  1266.             if (self.radsuit_finished > time)
  1267.                 self.dmgtime = time + 1;
  1268.             else
  1269.                 self.dmgtime = time + 0.2;
  1270.  
  1271.             T_Damage (self, world, world, 10*self.waterlevel);
  1272.         }
  1273.     }
  1274.     else if (self.watertype == CONTENT_SLIME)
  1275.     {    // do damage
  1276.         if (self.dmgtime < time && self.radsuit_finished < time)
  1277.         {
  1278.             self.dmgtime = time + 1;
  1279.             T_Damage (self, world, world, 4*self.waterlevel);
  1280.         }
  1281.     }
  1282.     
  1283.     if ( !(self.flags & FL_INWATER) )
  1284.     {    
  1285.  
  1286. // player enter water sound
  1287.  
  1288.         if (self.watertype == CONTENT_LAVA)
  1289.             sound (self, CHAN_BODY, "player/inlava.wav", 1, ATTN_NORM);
  1290.         if (self.watertype == CONTENT_WATER)
  1291.             sound (self, CHAN_BODY, "player/inh2o.wav", 1, ATTN_NORM);
  1292.         if (self.watertype == CONTENT_SLIME)
  1293.             sound (self, CHAN_BODY, "player/slimbrn2.wav", 1, ATTN_NORM);
  1294.  
  1295.         self.flags = self.flags + FL_INWATER;
  1296.         self.dmgtime = 0;
  1297.     }
  1298.     
  1299.     if (! (self.flags & FL_WATERJUMP) )
  1300.         self.velocity = self.velocity - 0.8*self.waterlevel*frametime*self.velocity;
  1301. };
  1302.  
  1303. void() CheckWaterJump =
  1304. {
  1305.     local vector start, end;
  1306.  
  1307. // check for a jump-out-of-water
  1308.     makevectors (self.angles);
  1309.     start = self.origin;
  1310.     start_z = start_z + 8; 
  1311.     v_forward_z = 0;
  1312.     normalize(v_forward);
  1313.     end = start + v_forward*24;
  1314.     traceline (start, end, TRUE, self);
  1315.     if (trace_fraction < 1)
  1316.     {    // solid at waist
  1317.         start_z = start_z + self.maxs_z - 8;
  1318.         end = start + v_forward*24;
  1319.         self.movedir = trace_plane_normal * -50;
  1320.         traceline (start, end, TRUE, self);
  1321.         if (trace_fraction == 1)
  1322.         {    // open at eye level
  1323.             self.flags = self.flags | FL_WATERJUMP;
  1324.             self.velocity_z = 225;
  1325.             self.flags = self.flags - (self.flags & FL_JUMPRELEASED);
  1326.             self.teleport_time = time + 2;    // safety net
  1327.             return;
  1328.         }
  1329.     }
  1330.  
  1331. };
  1332.  
  1333.  
  1334. /*
  1335. ================
  1336. PlayerPreThink
  1337.  
  1338. Called every frame before physics are run
  1339. ================
  1340. */
  1341. void() PlayerPreThink =
  1342. {
  1343.     local    float    mspeed, aspeed;
  1344.     local    float    r;
  1345.  
  1346.     if (intermission_running)
  1347.     {
  1348.         IntermissionThink ();    // otherwise a button could be missed between
  1349.         return;                    // the think tics
  1350.     }
  1351.     if (self.staydeadtime && self.staydeadtime > time)
  1352.         return;// wait a bit before respawn
  1353.  
  1354. // *TEAMPLAY*
  1355.     if (coop && TEAM_STRICT_COOP)
  1356.         return;
  1357.  
  1358.     if (self.observer) {
  1359.         if (time < self.motd_time)        // CTFBOT
  1360.             MOTD_ChooseTeam();
  1361.         TeamCheckLock();
  1362.         return; // no more checks
  1363.     }
  1364.  
  1365.     if (self.view_ofs == '0 0 0')
  1366.         return;        // intermission or finale
  1367.  
  1368.     makevectors (self.v_angle);        // is this still used
  1369.  
  1370.     if (self.motd_time && self.motd_time < time)
  1371.         MOTD();
  1372.  
  1373.     CheckRules ();
  1374.     WaterMove ();
  1375.     
  1376.     /* BOTCAM
  1377.     if (self.observer_flags & PLAYER_USING_CAMERA)        // CTFBOT
  1378.         SetPlayerViewPoint(FALSE);                        // CTFBOT
  1379.     */
  1380.     
  1381.     /* CTFBOT
  1382.     local float tmp;
  1383.     tmp = StepUpZ();
  1384.     dprint("step up = ");
  1385.     dprint(ftos(tmp));
  1386.     dprint("\n");
  1387.     if (lastspawn == world)
  1388.         dprint("***** lastspawn == world!\n");
  1389.  
  1390.     traceline (self.origin, self.origin + '0 0 -500', TRUE, self);    // see through other monsters
  1391.     
  1392.     if (trace_inopen && trace_inwater)
  1393.         if (pointcontents(trace_endpos) == CONTENT_LAVA)
  1394.             dprint("over lava\n");
  1395.     */
  1396.     
  1397.     //SpawnFollowEntity();
  1398.     
  1399.     PrintCloseWaypoint();    // CTFBOT
  1400.  
  1401. // *TEAMPLAY*
  1402. // TeamCheckLock performs all necessary teamlock checking, and performs all
  1403. // actions needed.
  1404.     TeamCheckLock();
  1405.  
  1406.     if (self.waterlevel == 2)
  1407.         CheckWaterJump ();
  1408.  
  1409.     if (self.deadflag >= DEAD_DEAD)
  1410.     {
  1411.         PlayerDeathThink ();
  1412.         return;
  1413.     }
  1414.     
  1415.     if (self.deadflag == DEAD_DYING)
  1416.         return;    // dying, so do nothing
  1417.  
  1418.     if (self.button2)
  1419.     {
  1420.         PlayerJump ();
  1421.     }
  1422.     else
  1423.         self.flags = self.flags | FL_JUMPRELEASED;
  1424.  
  1425. // teleporters can force a non-moving pause time    
  1426.     if (time < self.pausetime)
  1427.         self.velocity = '0 0 0';
  1428.  
  1429.  
  1430. // RUNE: If player has rune of elder magic (4), regeneration
  1431.     if (self.player_flag & ITEM_RUNE4_FLAG) {
  1432.         if (self.health < 100 && self.regen_time < time) {
  1433.             self.health = self.health + 2;
  1434.             self.regen_time = time + 0.5;
  1435.             RegenerationSound();
  1436.         }
  1437.     }
  1438. // RUNE
  1439.  
  1440.     if(time > self.attack_finished && self.currentammo == 0 && 
  1441.         self.weapon != IT_AXE && self.weapon != IT_HOOK)
  1442.     {
  1443.         self.weapon = W_BestWeapon ();
  1444.         W_SetCurrentAmmo ();
  1445.     }
  1446. };
  1447.     
  1448. /*
  1449. ================
  1450. CheckPowerups
  1451.  
  1452. Check for turning off powerups
  1453. ================
  1454. */
  1455. void() CheckPowerups =
  1456. {
  1457.     if (self.health <= 0)
  1458.         return;
  1459.  
  1460. // invisibility
  1461.     if (self.invisible_finished)
  1462.     {
  1463. // sound and screen flash when items starts to run out
  1464.         if (self.invisible_sound < time)
  1465.         {
  1466.             sound (self, CHAN_AUTO, "items/inv3.wav", 0.5, ATTN_IDLE);
  1467.             self.invisible_sound = time + ((random() * 3) + 1);
  1468.         }
  1469.  
  1470.  
  1471.         if (self.invisible_finished < time + 3)
  1472.         {
  1473.             if (self.invisible_time == 1)
  1474.             {
  1475.                 if (self.classname == "player")        // CTFBOT
  1476.                     sprint (self, "Ring of Shadows magic is fading\n");
  1477.                 if (self.classname == "player")        // CTFBOT
  1478.                     stuffcmd (self, "bf\n");
  1479.                 sound (self, CHAN_AUTO, "items/inv2.wav", 1, ATTN_NORM);
  1480.                 self.invisible_time = time + 1;
  1481.             }
  1482.             
  1483.             if (self.invisible_time < time)
  1484.             {
  1485.                 self.invisible_time = time + 1;
  1486.                 if (self.classname == "player")        // CTFBOT
  1487.                     stuffcmd (self, "bf\n");
  1488.             }
  1489.         }
  1490.  
  1491.         if (self.invisible_finished < time)
  1492.         {    // just stopped
  1493.             self.items = self.items - IT_INVISIBILITY;
  1494.             self.invisible_finished = 0;
  1495.             self.invisible_time = 0;
  1496.         }
  1497.         
  1498.     // use the eyes
  1499.         self.frame = 0;
  1500.         self.modelindex = modelindex_eyes;
  1501.     }
  1502.     else
  1503.         self.modelindex = modelindex_player;    // don't use eyes
  1504.  
  1505. // invincibility
  1506.     if (self.invincible_finished)
  1507.     {
  1508. // sound and screen flash when items starts to run out
  1509.         if (self.invincible_finished < time + 3)
  1510.         {
  1511.             if (self.invincible_time == 1)
  1512.             {
  1513.                 if (self.classname == "player")        // CTFBOT
  1514.                     sprint (self, "Protection is almost burned out\n");
  1515.                 if (self.classname == "player")        // CTFBOT
  1516.                     stuffcmd (self, "bf\n");
  1517.                 sound (self, CHAN_AUTO, "items/protect2.wav", 1, ATTN_NORM);
  1518.                 self.invincible_time = time + 1;
  1519.             }
  1520.             
  1521.             if (self.invincible_time < time)
  1522.             {
  1523.                 self.invincible_time = time + 1;
  1524.                 if (self.classname == "player")        // CTFBOT
  1525.                     stuffcmd (self, "bf\n");
  1526.             }
  1527.         }
  1528.         
  1529.         if (self.invincible_finished < time)
  1530.         {    // just stopped
  1531.             self.items = self.items - IT_INVULNERABILITY;
  1532.             self.invincible_time = 0;
  1533.             self.invincible_finished = 0;
  1534.         }
  1535. // ZOID, next line isn't needed, EF_DIMLIGHT is handled by
  1536. // client.qc:CheckDimLight
  1537. //        if (self.invincible_finished > time)
  1538. //            self.effects = self.effects | EF_DIMLIGHT;
  1539. //        else
  1540. //            self.effects = self.effects - (self.effects & EF_DIMLIGHT);
  1541.     }
  1542.  
  1543. // super damage
  1544.     if (self.super_damage_finished)
  1545.     {
  1546.  
  1547. // sound and screen flash when items starts to run out
  1548.  
  1549.         if (self.super_damage_finished < time + 3)
  1550.         {
  1551.             if (self.super_time == 1)
  1552.             {
  1553.                 if (self.classname == "player")        // CTFBOT
  1554.                     sprint (self, "Quad Damage is wearing off\n");
  1555.                 if (self.classname == "player")        // CTFBOT
  1556.                     stuffcmd (self, "bf\n");
  1557.                 sound (self, CHAN_AUTO, "items/damage2.wav", 1, ATTN_NORM);
  1558.                 self.super_time = time + 1;
  1559.             }      
  1560.             
  1561.             if (self.super_time < time)
  1562.             {
  1563.                 self.super_time = time + 1;
  1564.                 if (self.classname == "player")        // CTFBOT
  1565.                     stuffcmd (self, "bf\n");
  1566.             }
  1567.         }
  1568.  
  1569.         if (self.super_damage_finished < time)
  1570.         {    // just stopped
  1571.             self.items = self.items - IT_QUAD;
  1572.             self.super_damage_finished = 0;
  1573.             self.super_time = 0;
  1574.         }
  1575. // ZOID, next line isn't needed, EF_DIMLIGHT is handled by
  1576. // client.qc:CheckDimLight
  1577. //        if (self.super_damage_finished > time)
  1578. //            self.effects = self.effects | EF_DIMLIGHT;
  1579. //        else
  1580. //            self.effects = self.effects - (self.effects & EF_DIMLIGHT);
  1581.     }    
  1582.  
  1583. // suit    
  1584.     if (self.radsuit_finished)
  1585.     {
  1586.         self.air_finished = time + 12;        // don't drown
  1587.  
  1588. // sound and screen flash when items starts to run out
  1589.         if (self.radsuit_finished < time + 3)
  1590.         {
  1591.             if (self.rad_time == 1)
  1592.             {
  1593.                 if (self.classname == "player")        // CTFBOT
  1594.                     sprint (self, "Air supply in Biosuit expiring\n");
  1595.                 if (self.classname == "player")        // CTFBOT
  1596.                     stuffcmd (self, "bf\n");
  1597.                 sound (self, CHAN_AUTO, "items/suit2.wav", 1, ATTN_NORM);
  1598.                 self.rad_time = time + 1;
  1599.             }
  1600.             
  1601.             if (self.rad_time < time)
  1602.             {
  1603.                 self.rad_time = time + 1;
  1604.                 if (self.classname == "player")        // CTFBOT
  1605.                     stuffcmd (self, "bf\n");
  1606.             }
  1607.         }
  1608.  
  1609.         if (self.radsuit_finished < time)
  1610.         {    // just stopped
  1611.             self.items = self.items - IT_SUIT;
  1612.             self.rad_time = 0;
  1613.             self.radsuit_finished = 0;
  1614.         }
  1615.     }    
  1616.  
  1617.     // Check to see about DIMLIGHT effects
  1618.     CheckDimLight();
  1619. };
  1620.  
  1621.  
  1622. /*
  1623. ================
  1624. PlayerPostThink
  1625.  
  1626. Called every frame after physics are run
  1627. ================
  1628. */
  1629. void() PlayerPostThink =
  1630. {
  1631.     local    float    mspeed, aspeed;
  1632.     local    float    r;
  1633.     local   string  num;
  1634.  
  1635.     if (self.do_observer) {
  1636.         BecomeObserver(self);
  1637.     } else if (self.observer) {
  1638.         ObserverThink();
  1639.         return;
  1640.     }
  1641.         
  1642.     if (self.view_ofs == '0 0 0')
  1643.         return;        // intermission or finale
  1644.  
  1645.     if (self.deadflag) {
  1646.         // check admin impulses if dead
  1647.         if (self.impulse) {
  1648.             CheckAdminCmd();
  1649.             self.impulse = 0;
  1650.         }
  1651.         return;
  1652.     }
  1653.  
  1654. // do weapon stuff
  1655.  
  1656.     W_WeaponFrame ();
  1657.  
  1658. // check to see if player landed and play landing sound    
  1659.     if ((self.jump_flag < -300) && (self.flags & FL_ONGROUND) && (self.health > 0))
  1660.     {
  1661.         if (self.watertype == CONTENT_WATER)
  1662.             sound (self, CHAN_BODY, "player/h2ojump.wav", 1, ATTN_NORM);
  1663.         else if (self.jump_flag < -650)
  1664.         {
  1665.             T_Damage (self, world, world, 5); 
  1666.             sound (self, CHAN_VOICE, "player/land2.wav", 1, ATTN_NORM);
  1667.             self.deathtype = "falling";
  1668.         }
  1669.         else
  1670.             sound (self, CHAN_VOICE, "player/land.wav", 1, ATTN_NORM);
  1671.  
  1672.         self.jump_flag = 0;
  1673.     }
  1674.  
  1675.     if (!(self.flags & FL_ONGROUND))
  1676.         self.jump_flag = self.velocity_z;
  1677.  
  1678.     CheckPowerups ();
  1679.  
  1680. };
  1681.  
  1682.  
  1683. /*
  1684. ===========
  1685. ClientConnect
  1686.  
  1687. called when a player connects to a server
  1688. ============
  1689. */
  1690. void() ClientConnect =
  1691. {
  1692.     bprint (self.netname);
  1693.     bprint (" entered the game\n");
  1694.     
  1695.     LogMsg(self, "CONNECT");
  1696.  
  1697.     self.motd_time = time + 3;
  1698.     self.motd_count = 0;
  1699.  
  1700.     self.suicide_count = 0;
  1701.     self.killed = 0;
  1702.     
  1703.     // CTFBOT [[[    
  1704.     self.num_kills = 0;            // CTFBOT EXTRAS number of kills
  1705.     self.num_deaths = 0;        // CTFBOT EXTRAS number of deaths
  1706.     self.num_suicides = 0;        // CTFBOT EXTRAS number of suicides
  1707.     self.num_captures = 0;        // CTFBOT EXTRAS number of personal flag captures 
  1708.     self.num_pickups = 0;        // CTFBOT EXTRAS number of personal enemy flag pickups
  1709.     self.num_recovery = 0;        // CTFBOT EXTRAS number of flag recoveries
  1710.     self.num_assists = 0;        // CTFBOT EXTRAS number of assists
  1711.     self.num_bonus = 0;            // CTFBOT EXTRAS number of general bonuses
  1712.     // CTFBOT ]]]
  1713.     
  1714. // *TEAMPLAY*
  1715.      // If this is our first connection, parm10 is < 0
  1716.      // Set lastteam negative.
  1717.      if(parm10 < 0 && teamplay > 0) {
  1718.          self.lastteam = -50;
  1719.         self.team = -1;
  1720.         if (teamplay & TEAM_CAPTURE_SELECT_TEAM)
  1721.             self.do_observer = 1;
  1722.         else {
  1723.             TeamCheckLock();
  1724.             if(teamplay & TEAM_LOCK_COLORS) // force a stuff cmd in think
  1725.                 self.player_flag = self.player_flag | TEAM_STUFF_COLOR;
  1726.             if ((teamplay & TEAM_CAPTURE_CUSTOM) && (teamplay & TEAM_CAPTURE_FLAG)) {
  1727.                 if (self.lastteam == TEAM_COLOR1 + 1)
  1728.                     self.skin = 1;
  1729.                 else
  1730.                     self.skin = 3;
  1731.                 if (random() < 0.5)
  1732.                     self.skin = self.skin + 1; // visor dude
  1733.                 self.player_flag = self.player_flag - (self.player_flag & 65280);
  1734.                 self.player_flag = self.player_flag | (self.skin * 256);  
  1735.             }
  1736.         }
  1737.     }
  1738.  
  1739.  
  1740. // a client connecting during an intermission can cause problems
  1741.     if (intermission_running)
  1742.         ExitIntermission ();
  1743. };
  1744.  
  1745.  
  1746. /*
  1747. ===========
  1748. ClientDisconnect
  1749.  
  1750. called when a player disconnects from a server
  1751. ============
  1752. */
  1753. void() ClientDisconnect =
  1754. {
  1755.     if (gameover)
  1756.         return;
  1757.     // if the level end trigger has been activated, just return
  1758.     // since they aren't *really* leaving
  1759.  
  1760.     // let everyone else know
  1761.     bprint (self.netname);
  1762.     bprint (" left the game with ");
  1763.     bprint (ftos(self.frags));
  1764.     bprint (" frags\n");
  1765.     sound (self, CHAN_BODY, "player/tornoff2.wav", 1, ATTN_NONE);
  1766.     DropRune();
  1767.     TeamCaptureDropFlagOfPlayer(self);
  1768.     set_suicide_frame ();
  1769.  
  1770.     self.lastteam = -50;
  1771.     self.team = -50;
  1772.  
  1773.     LogMsg(self, "DISCONNECT");
  1774. };
  1775.  
  1776. // *TEAMPLAY*
  1777. // Prototypes
  1778.  
  1779. float(entity targ, entity attacker) TeamFragPenalty;
  1780. void(entity targ, entity attacker) TeamDeathPenalty;
  1781.  
  1782. /*
  1783. ===========
  1784. ClientObituary
  1785.  
  1786. called when a player dies
  1787. ============
  1788. */
  1789. void(entity targ, entity attacker) ClientObituary =
  1790. {
  1791.  
  1792.     // *XXX* EXPERT CTF variable for 
  1793.     // flag/flag carrier defense bonus determination
  1794.     local    entity head;
  1795.     local float flag_radius;
  1796.     local float flag_carrier_radius;
  1797.  
  1798.  
  1799.  
  1800.     local string st;        // CTFBOT
  1801.  
  1802.     local    float rnum;
  1803.     local    string deathstring, deathstring2, what, s;
  1804.     rnum = random();
  1805.  
  1806.     if (targ.classname == "player" || targ.classname == "bot")    // CTFBOT
  1807.     {
  1808.  
  1809.         // *XXX* EXPERT CTF: 
  1810.         // When the flag carrier dies, reset the last_hurt_carrier field in
  1811.         // all players on the opposite team from the flag carrier.  The carrier
  1812.         // has been killed, so there is no longer a reason to award points for
  1813.         // killing off his assailants
  1814.         if (targ.player_flag & ITEM_ENEMY_FLAG) {
  1815.  
  1816.             head = find(world, classname, "player");
  1817.  
  1818.             while (head != world) {    
  1819.                 if (head.team != targ.team) {
  1820.                     head.last_hurt_carrier = -10;
  1821.                 }
  1822.                 head = FindNextPlayerOrBot(head);    // CTFBOT find bots also 
  1823.             }
  1824.         }
  1825.         // END EXPERT CTF
  1826.  
  1827.         if (attacker.classname == "teledeath")
  1828.         {
  1829.             bprint (targ.netname);
  1830.             bprint (" was telefragged by ");
  1831.             bprint (attacker.owner.netname);
  1832.             bprint ("\n");
  1833.  
  1834.             attacker.owner.frags = attacker.owner.frags + 1;
  1835.             LogPlayerDMDeath(targ, attacker.owner, "telefrag");
  1836.  
  1837.             targ.num_deaths = targ.num_deaths + 1;         // CTFBOT EXTRAS
  1838.             attacker.owner.num_kills = attacker.owner.num_kills + 1;     // CTFBOT EXTRAS
  1839.  
  1840.             return;
  1841.         }
  1842.  
  1843.         if (attacker.classname == "teledeath2")
  1844.         {
  1845.             bprint ("Satan's power deflects ");
  1846.             bprint (targ.netname);
  1847.             bprint ("'s telefrag\n");
  1848.  
  1849.             targ.frags = targ.frags - 1;
  1850.             LogPlayerDeath(targ, "telefrag");
  1851.  
  1852.             targ.num_deaths = targ.num_deaths + 1;         // CTFBOT EXTRAS
  1853.             attacker.owner.num_kills = attacker.owner.num_kills + 1;     // CTFBOT EXTRAS
  1854.  
  1855.             return;
  1856.         }
  1857.  
  1858.         if ((attacker.classname == "player") || (attacker.classname == "bot"))    // CTFBOT
  1859.         {
  1860.             if (targ == attacker)
  1861.             {
  1862.                 // killed self
  1863.                 attacker.frags = attacker.frags - 1;
  1864.                 bprint (targ.netname);
  1865.                 
  1866.                 if (targ.weapon == 64 && targ.waterlevel > 1)
  1867.                 {
  1868.                     bprint (" discharges into the water.\n");
  1869.                     LogPlayerDeath(targ, "discharge");
  1870.                     return;
  1871.                 }
  1872.                 if (targ.weapon == IT_GRENADE_LAUNCHER) {
  1873.                     bprint (" tries to put the pin back in\n");
  1874.                     LogPlayerDeath(targ, "grenade");
  1875.                 } else if (targ.team != targ.lastteam) {
  1876.                     //ZOID: try if player was gibbed for changing teams
  1877.                     if (teamplay & TEAM_STATIC_TEAMS)
  1878.                         bprint (" tried to change teams\n");
  1879.                     else
  1880.                         bprint (" changed teams\n");
  1881.                     LogPlayerDeath(targ, "teamchange");
  1882.                 } else {
  1883.                     bprint (" becomes bored with life\n");
  1884.                     LogPlayerDeath(targ, "rocket");
  1885.                 }
  1886.  
  1887.                 targ.num_deaths = targ.num_deaths + 1;         // CTFBOT EXTRAS
  1888.                 targ.num_suicides = targ.num_suicides + 1;     // CTFBOT EXTRAS
  1889.  
  1890.                 return;
  1891.             }
  1892.             else if ( (teamplay == 2) && (targ.team > 0)&&(targ.team == attacker.team) )
  1893.             {
  1894.                 if (rnum < 0.25)
  1895.                     deathstring = " mows down a teammate\n";
  1896.                 else if (rnum < 0.50)
  1897.                     deathstring = " checks his glasses\n";
  1898.                 else if (rnum < 0.75)
  1899.                     deathstring = " gets a frag for the other team\n";
  1900.                 else
  1901.                     deathstring = " loses another friend\n";
  1902.                 bprint (attacker.netname);
  1903.                 bprint (deathstring);
  1904.                 attacker.frags = attacker.frags - 1;
  1905.  
  1906.                 targ.num_deaths = targ.num_deaths + 1;             // CTFBOT EXTRAS
  1907.  
  1908.                 return;
  1909.             }
  1910.             else
  1911.             {
  1912.                  // *TEAMPLAY*
  1913.                  // TeamFragPenalty returns true if the attacker gets a frag penalty for
  1914.                  // killing this target.  It also deducts frags as needed.
  1915.                  if (!TeamFragPenalty(targ, attacker)) {
  1916.  
  1917.                      // the attacker is award the normal one frag.. now we 
  1918.                      // determine if he gets any bonuses
  1919.                      attacker.frags = attacker.frags + 1;
  1920.  
  1921.                      if ((targ.player_flag & ITEM_ENEMY_FLAG) &&
  1922.                          (targ.team != attacker.team)) {
  1923.                          //ZOID: one team fragged the other team's flag carrier
  1924.  
  1925.                          // *XXX* EXPERT CTF
  1926.                          // Mark the attacker with the time at which he killed the flag
  1927.                          // carrier, for awarding assist points
  1928.  
  1929.                          attacker.last_fragged_carrier = time;
  1930.  
  1931.                          // *XXX* EXPERT CTF: give player only the normal amount of frags
  1932.                          // if the carrier has only had the flag for a few seconds, to
  1933.                          // prevent ppl intentionally allowing enemies to grab the flag,
  1934.                          // then immediately fragging them
  1935.  
  1936.                          if (targ.flag_since + TEAM_CAPTURE_CARRIER_FLAG_SINCE_TIMEOUT > time) 
  1937.  
  1938.                         {
  1939.  
  1940.                             if (attacker.classname == "player")    // CTFBOT
  1941.                                  sprint(attacker, "Enemy flag carrier killed, no bonus\n");
  1942.                          } else {
  1943.                              attacker.frags = attacker.frags + TEAM_CAPTURE_FRAG_CARRIER_BONUS;
  1944.                                      // TeamScore (attacker, TEAM_CAPTURE_FRAG_CARRIER_BONUS);
  1945.                              if (attacker.classname == "player")    // CTFBOT
  1946.  
  1947.                                 sprint(attacker, "Enemy flag carrier killed: ");
  1948.                              s = ftos(TEAM_CAPTURE_FRAG_CARRIER_BONUS);
  1949.                             if (attacker.classname == "player")    // CTFBOT
  1950.  
  1951.                                  sprint(attacker, s);
  1952.                              if (attacker.classname == "player")    // CTFBOT
  1953.  
  1954.                                 sprint(attacker, " bonus frags\n");
  1955.                              
  1956.                              attacker.num_bonus = attacker.num_bonus + 1;    // CTFBOT EXTRAS
  1957.                          }
  1958.                          // END FLAG CARRIER FRAG CODE
  1959.                      }
  1960.                      
  1961.                      // *XXX* EXPERT CTF
  1962.                      // This code checks for all game-critical kills OTHER THAN fragging the enemy
  1963.                      // flag carrier, like killing players who are trying to kill your flag carrier
  1964.                      // or trying to grab your flag, and hands out bonus frags.
  1965.  
  1966.                      // The two variables below track whether special bonus frags have already
  1967.                      // been awarded for the attacker or target being near the flag or flag carrier.  
  1968.  
  1969.                      flag_radius = 0;
  1970.                      flag_carrier_radius = 0;
  1971.  
  1972.                      // get a string for the attacker's team now, for later announcements
  1973.                      s = GetCTFTeam(attacker.team);
  1974.  
  1975.                      if ((targ.last_hurt_carrier + TEAM_CAPTURE_CARRIER_DANGER_PROTECT_TIMEOUT > time) &&
  1976.                          !(attacker.player_flag & ITEM_ENEMY_FLAG) ) {
  1977.                          // a player on the same team as the flag carrier killed 
  1978.                          // someone who recently shot the flag carrier
  1979.                          attacker.frags = attacker.frags + 
  1980.                              TEAM_CAPTURE_CARRIER_DANGER_PROTECT_BONUS;
  1981.                          flag_carrier_radius = 1;
  1982.                          // NOTE: getting CARRIER_DANGER_PROTECT_BONUS precludes getting
  1983.                          // other kinds of bonuses for defending the flag carrier, since
  1984.                          // it's worth more points
  1985.                          bprint(attacker.netname);
  1986.                          bprint(" defends ");
  1987.                          bprint(s);
  1988.                          bprint("'s flag carrier against an agressive enemy\n");
  1989.                          
  1990.                          attacker.num_bonus = attacker.num_bonus + 1;    // CTFBOT EXTRAS
  1991.                      }
  1992.  
  1993.                      // *XXX* EXPERT CTF
  1994.                      // Bonusus for defending the flag carrier or the flag itself.
  1995.                      // Extra frags are awarded if either the attacker or the target are
  1996.                      // 1. within 40 feet of a flag carrier on the same team as the attacker
  1997.                      // 2. within 40 feet of the attacker's flag
  1998.                      // These bonuses are cumulative with respect to defending both the
  1999.                      // flag and the flag carrier at the same time, but not cumulative with
  2000.                      // respect to both the target and attacker being near the object being defended
  2001.  
  2002.                      // find flags or flag carriers within a radius of the attacker
  2003.                      head = findradius(attacker.origin, TEAM_CAPTURE_ATTACKER_PROTECT_RADIUS);
  2004.  
  2005.                      while (head) {
  2006.                          if (head.classname == "player" || head.classname == "bot") {    // CTFBOT
  2007.                              if ( (head.team == attacker.team) &&
  2008.                                   (head.player_flag & ITEM_ENEMY_FLAG) &&
  2009.                                   (head != attacker) && // self defense
  2010.                                   (!flag_carrier_radius) ) { 
  2011.                                 // attacker was near his own flag carrier
  2012.                                  attacker.frags = attacker.frags + 
  2013.                                      TEAM_CAPTURE_CARRIER_PROTECT_BONUS;
  2014.                                  flag_carrier_radius = 1;
  2015.                                  bprint(attacker.netname);
  2016.                                  bprint(" defends ");
  2017.                                  bprint(s);
  2018.                                  bprint("'s flag carrier\n");
  2019.                                   attacker.num_bonus = attacker.num_bonus + 1;    // CTFBOT EXTRAS
  2020.                             }
  2021.                          }
  2022.                          if ( (head.classname == "item_flag_team1") ||
  2023.                               (head.classname == "item_flag_team2")) 
  2024.                             if (head.solid != SOLID_NOT)        // CTFBOT EXTRAS bug fix: no defense bonus if flag already stolen
  2025.                             {
  2026.                              if (((attacker.team == (TEAM_COLOR1 + 1)) &&
  2027.                                 (head.classname == "item_flag_team1")) ||
  2028.                                  ((attacker.team == (TEAM_COLOR2 + 1)) &&
  2029.                                  (head.classname == "item_flag_team2"))) { 
  2030.                                 // attacker was near his own flag
  2031.                                  attacker.frags = attacker.frags + 
  2032.                                      TEAM_CAPTURE_FLAG_DEFENSE_BONUS;
  2033.                                  flag_radius = 1; 
  2034.                                  bprint(attacker.netname);
  2035.                                  bprint(" defends the ");
  2036.                                  bprint(s);
  2037.                                  bprint(" flag\n");
  2038.                                   attacker.num_bonus = attacker.num_bonus + 1;    // CTFBOT EXTRAS
  2039.                              }
  2040.                          }
  2041.                          head = head.chain;
  2042.                      }
  2043.  
  2044.                      // find flags or flag carriers within a radius from the target
  2045.                      head = findradius(targ.origin, TEAM_CAPTURE_TARGET_PROTECT_RADIUS);
  2046.                      while (head) {
  2047.                          if (head.classname == "player" || head.classname == "bot") {    // CTFBOT
  2048.                              if ( (head.team == attacker.team) &&
  2049.                                   (head.player_flag & ITEM_ENEMY_FLAG) &&
  2050.                                   (head != attacker) &&
  2051.                                   (!flag_carrier_radius)) { // prevents redundant points awarded
  2052.                                  // target was near attacker's flag carrier
  2053.                                  attacker.frags = attacker.frags + 
  2054.                                      TEAM_CAPTURE_CARRIER_PROTECT_BONUS;
  2055.                                  flag_carrier_radius = 1;
  2056.                                  bprint(attacker.netname);
  2057.                                  bprint(" defends ");
  2058.                                  bprint(s);
  2059.                                  bprint("'s flag carrier\n");
  2060.                                   attacker.num_bonus = attacker.num_bonus + 1;    // CTFBOT EXTRAS
  2061.                              }
  2062.                          }
  2063.                         if (((attacker.team == (TEAM_COLOR1 + 1)) &&
  2064.                             (head.classname == "item_flag_team1")) ||
  2065.                             ((attacker.team == (TEAM_COLOR2 + 1)) &&
  2066.                             (head.classname == "item_flag_team2"))
  2067.                             && (!flag_radius) // prevents redundant points awarded
  2068.                             && (head.solid != SOLID_NOT))        // CTFBOT EXTRAS bug fix: no defense bonus if flag already stolen
  2069.                              {
  2070.                             // target was near attacker's flag
  2071.                             attacker.frags = attacker.frags + 
  2072.                                 TEAM_CAPTURE_FLAG_DEFENSE_BONUS;
  2073.                             flag_radius = 1;
  2074.                             bprint(attacker.netname);
  2075.                             bprint(" defends the ");
  2076.                             bprint(s);
  2077.                             bprint(" flag\n");
  2078.                               attacker.num_bonus = attacker.num_bonus + 1;    // CTFBOT EXTRAS
  2079.                         }
  2080.                          head = head.chain;
  2081.                      }
  2082.                  }    
  2083.               
  2084.                  // *XXX* EXPERT CTF 
  2085.                  // End frag determination code.  Now determine death text for
  2086.                  // a member of one team killing a member of the other
  2087.  
  2088.                  // *TEAMPLAY*
  2089.                  // TeamDeathPenalty kills the attacker if necessary and adjusts frags to
  2090.                  // offset the one frag penalty for dying.
  2091.                 TeamDeathPenalty(targ, attacker);
  2092.  
  2093.                 rnum = attacker.weapon;        // CTFBOT this is a bug, should use the weapon used to kill, not current weapon
  2094.                 if (rnum == IT_AXE)
  2095.                 {
  2096.                     deathstring = " was ax-murdered by ";
  2097.                     deathstring2 = "\n";
  2098.                     what = "axe";
  2099.                 }
  2100.                 if (rnum == IT_HOOK) {
  2101.                     if (random() < 0.5)
  2102.                         deathstring = " was disembowled by ";
  2103.                     else
  2104.                         deathstring = " was hooked by ";
  2105.                     deathstring2 = "\n";
  2106.                     what = "hook";
  2107.                 }
  2108.                 if (rnum == IT_SHOTGUN)
  2109.                 {
  2110.                     deathstring = " chewed on ";
  2111.                     deathstring2 = "'s boomstick\n";
  2112.                     what = "shotgun";
  2113.                 }
  2114.                 if (rnum == IT_SUPER_SHOTGUN)
  2115.                 {
  2116.                     deathstring = " ate 2 loads of ";
  2117.                     if (targ.health < -40)
  2118.                         deathstring = " ate a box of ";
  2119.                     deathstring2 = "'s buckshot\n";
  2120.                     what = "supershotgun";
  2121.                 }
  2122.                 if (rnum == IT_NAILGUN)
  2123.                 {
  2124.                     deathstring = " was nailed by ";
  2125.                     deathstring2 = "\n";
  2126.                     what = "nailgun";
  2127.                 }
  2128.                 if (rnum == IT_SUPER_NAILGUN)
  2129.                 {
  2130.                     deathstring = " was punctured by ";
  2131.                     deathstring2 = "\n";
  2132.                     what = "supernailgun";
  2133.                 }
  2134.                 if (rnum == IT_GRENADE_LAUNCHER)
  2135.                 {
  2136.                     deathstring = " eats ";
  2137.                     deathstring2 = "'s pineapple\n";
  2138.                     if (targ.health < -40)
  2139.                     {
  2140.                         deathstring = " was gibbed by ";
  2141.                         deathstring2 = "'s grenade\n";
  2142.                     }
  2143.                     what = "grenade";
  2144.                 }
  2145.                 if (rnum == IT_ROCKET_LAUNCHER)
  2146.                 {
  2147.                     if (attacker.items & IT_QUAD) {
  2148.                         deathstring = " was destroyed by ";
  2149.                         deathstring2 = "'s Quad rocket\n";
  2150.                     } else {
  2151.                         deathstring = " rides ";
  2152.                         deathstring2 = "'s rocket\n";
  2153.                         if (targ.health < -40)
  2154.                         {
  2155.                             deathstring = " was gibbed by ";
  2156.                             deathstring2 = "'s rocket\n" ;
  2157.                         }
  2158.                     }
  2159.                     what = "rocket";
  2160.                 }
  2161.                 if (rnum == IT_LIGHTNING)
  2162.                 {
  2163.                     if (attacker.items & IT_QUAD) {
  2164.                         deathstring = " was touched by ";
  2165.                         deathstring2 = "'s Quad lightning\n";
  2166.                     } else {
  2167.                         deathstring = " accepts ";
  2168.                         if (attacker.waterlevel > 1)
  2169.                             deathstring2 = "'s discharge\n";
  2170.                         else
  2171.                             deathstring2 = "'s shaft\n";
  2172.                     }
  2173.                     what = "lightning";
  2174.                 }
  2175.                 bprint (targ.netname);
  2176.  
  2177.                 // CTFBOT [[[
  2178.                 if ((targ.classname == "bot") && (!bots_have_normal_names))
  2179.                 {
  2180.                     st = ftos(targ.bot_skill);
  2181.                     bprint(" [skill ");
  2182.                     bprint(st);
  2183.                     bprint("]");
  2184.                 }
  2185.                 // CTFBOT ]]]
  2186.  
  2187.  
  2188.                 bprint (deathstring);
  2189.                 bprint (attacker.netname);
  2190.  
  2191.                 // CTFBOT [[[
  2192.                 if ((attacker.classname == "bot") && (!bots_have_normal_names))
  2193.                 {
  2194.                     st = ftos(attacker.bot_skill);
  2195.                     bprint(" [skill ");
  2196.                     bprint(st);
  2197.                     bprint("]");
  2198.                 }
  2199.                 // CTFBOT ]]]
  2200.  
  2201.                 bprint (deathstring2);
  2202.                 LogPlayerDMDeath(targ, attacker, what);
  2203.                 
  2204.                 targ.num_deaths = targ.num_deaths + 1;             // CTFBOT EXTRAS
  2205.                 attacker.num_kills = attacker.num_kills + 1;     // CTFBOT EXTRAS
  2206.             }
  2207.             return;
  2208.         }
  2209.         else
  2210.         {
  2211.             targ.frags = targ.frags - 1;
  2212.  
  2213.             targ.num_deaths = targ.num_deaths + 1;         // CTFBOT EXTRAS
  2214.  
  2215.             bprint (targ.netname);
  2216.             
  2217.             /* CTFBOT remove to save code space
  2218.  
  2219.             // killed by a montser?
  2220.             if (attacker.flags & FL_MONSTER)
  2221.             {
  2222.                 if (attacker.classname == "monster_army") {
  2223.                     bprint (" was shot by a Grunt\n");
  2224.                     LogPlayerDeath(targ, "grunt");
  2225.                 } else if (attacker.classname == "monster_demon1") {
  2226.                     bprint (" was eviscerated by a Fiend\n");
  2227.                     LogPlayerDeath(targ, "fiend");
  2228.                 } else if (attacker.classname == "monster_dog") {
  2229.                     bprint (" was mauled by a Rottweiler\n");
  2230.                     LogPlayerDeath(targ, "dog");
  2231.                 } else if (attacker.classname == "monster_dragon") {
  2232.                     bprint (" was fried by a Dragon\n");
  2233.                     LogPlayerDeath(targ, "dragon");
  2234.                 } else if (attacker.classname == "monster_enforcer") {
  2235.                     bprint (" was blasted by an Enforcer\n");
  2236.                     LogPlayerDeath(targ, "enforcer");
  2237.                 } else if (attacker.classname == "monster_fish") {
  2238.                     bprint (" was fed to the Rotfish\n");
  2239.                     LogPlayerDeath(targ, "fish");
  2240.                 } else if (attacker.classname == "monster_hell_knight") {
  2241.                     bprint (" was slain by a Death Knight\n");
  2242.                     LogPlayerDeath(targ, "deathknight");
  2243.                 } else if (attacker.classname == "monster_knight") {
  2244.                     bprint (" was slashed by a Knight\n");
  2245.                     LogPlayerDeath(targ, "knight");
  2246.                 } else if (attacker.classname == "monster_ogre") {
  2247.                     bprint (" was destroyed by an Ogre\n");
  2248.                     LogPlayerDeath(targ, "ogre");
  2249.                 } else if (attacker.classname == "monster_oldone") {
  2250.                     bprint (" became one with Shub-Niggurath\n");
  2251.                     LogPlayerDeath(targ, "shub");
  2252.                 } else if (attacker.classname == "monster_shalrath") {
  2253.                     bprint (" was exploded by a Vore\n");
  2254.                     LogPlayerDeath(targ, "vore");
  2255.                 } else if (attacker.classname == "monster_shambler") {
  2256.                     bprint (" was smashed by a Shambler\n");
  2257.                     LogPlayerDeath(targ, "shambler");
  2258.                 } else if (attacker.classname == "monster_tarbaby") {
  2259.                     bprint (" was slimed by a Spawn\n");
  2260.                     LogPlayerDeath(targ, "spawn");
  2261.                 } else if (attacker.classname == "monster_vomit") {        // CTFBOT what the hell is this???
  2262.                     bprint (" was vomited on by a Vomitus\n");
  2263.                     LogPlayerDeath(targ, "vomitus");
  2264.                 } else if (attacker.classname == "monster_wizard") {
  2265.                     bprint (" was scragged by a Scrag\n");
  2266.                     LogPlayerDeath(targ, "scrag");
  2267.                 } else if (attacker.classname == "monster_zombie") {
  2268.                     bprint (" joins the Zombies\n");
  2269.                     LogPlayerDeath(targ, "zombie");
  2270.                 }
  2271.  
  2272.                 return;
  2273.             }
  2274.             
  2275.             */        // CTFBOT remove to save code space
  2276.  
  2277.             // tricks and traps
  2278.             if (attacker.classname == "explo_box")
  2279.             {
  2280.                 bprint (" blew up\n");
  2281.                 LogPlayerDeath(targ, "explosion");
  2282.                 return;
  2283.             }
  2284.             if (attacker.solid == SOLID_BSP && attacker != world)
  2285.             {    
  2286.                 bprint (" was squished\n");
  2287.                 LogPlayerDeath(targ, "squished");
  2288.                 return;
  2289.             }
  2290.             if (attacker.classname == "trap_shooter" || attacker.classname == "trap_spikeshooter")
  2291.             {
  2292.                 bprint (" was spiked\n");
  2293.                 LogPlayerDeath(targ, "spiked");
  2294.                 return;
  2295.             }
  2296.             if (attacker.classname == "fireball")
  2297.             {
  2298.                 bprint (" ate a lavaball\n");
  2299.                 LogPlayerDeath(targ, "fireball");
  2300.                 return;
  2301.             }
  2302.             if (attacker.classname == "trigger_changelevel")
  2303.             {
  2304.                 bprint (" tried to leave\n");
  2305.                 LogPlayerDeath(targ, "noexit");
  2306.                 return;
  2307.             }
  2308.  
  2309.             // CTFBOT DTF [[[
  2310.             if ((attacker.classname == "item_flag_team1")
  2311.             || (attacker.classname == "item_flag_team2"))
  2312.             {
  2313.                 bprint (" died for the flag\n");
  2314.                 LogPlayerDeath(targ, "noexit");
  2315.                 return;
  2316.             }
  2317.             // CTFBOT DTF ]]]
  2318.  
  2319.             // in-water deaths
  2320.             rnum = targ.watertype;
  2321.             if (rnum == -3)
  2322.             {
  2323.                 if (random() < 0.5)
  2324.                     bprint (" sleeps with the fishes\n");
  2325.                 else
  2326.                     bprint (" sucks it down\n");
  2327.                 LogPlayerDeath(targ, "drowned");
  2328.                 return;
  2329.             }
  2330.             else if (rnum == -4)
  2331.             {
  2332.                 if (random() < 0.5)
  2333.                     bprint (" gulped a load of slime\n");
  2334.                 else
  2335.                     bprint (" can't exist on slime alone\n");
  2336.                 LogPlayerDeath(targ, "slimed");
  2337.                 return;
  2338.             }
  2339.             else if (rnum == -5)
  2340.             {
  2341.                 if (targ.health < -15)
  2342.                 {
  2343.                     bprint (" burst into flames\n");
  2344.                     LogPlayerDeath(targ, "melted");
  2345.                     return;
  2346.                 }
  2347.                 if (random() < 0.5)
  2348.                     bprint (" turned into hot slag\n");
  2349.                 else
  2350.                     bprint (" visits the Volcano God\n");
  2351.                 LogPlayerDeath(targ, "melted");
  2352.                 return;
  2353.             }
  2354.  
  2355.             // fell to their death?
  2356.             if (targ.deathtype == "falling")
  2357.             {
  2358.                 targ.deathtype = "";
  2359.                 bprint (" fell to his death\n");
  2360.                 LogPlayerDeath(targ, "falling");
  2361.                 return;
  2362.             }
  2363.  
  2364.             // hell if I know; he's just dead!!!
  2365.             LogPlayerDeath(targ, "died");
  2366.             bprint (" died\n");
  2367.         }
  2368.     }
  2369. };
  2370.