home *** CD-ROM | disk | FTP | other *** search
/ Quake 'em / QUAKEEM.BIN / quake / programs / psychoc2 / client.qc < prev    next >
Encoding:
Text File  |  1996-08-14  |  44.5 KB  |  1,762 lines

  1. // prototypes
  2. void() W_WeaponFrame;
  3. void() W_SetCurrentAmmo;
  4. void() player_pain;
  5. void() player_stand1;
  6. void (vector org) spawn_tfog;
  7. void (vector org, entity death_owner) spawn_tdeath;
  8.  
  9. float modelindex_eyes, modelindex_player;
  10.  
  11. /*===========================================================================
  12.  
  13.                              Team Score Compilation
  14.  
  15. ===========================================================================*/
  16.  
  17. float() PlayerCount =
  18.    {
  19.    local float PlayerNum;
  20.  
  21.    local entity e;
  22.  
  23.    PlayerNum = 0;
  24.    e = find(world, classname, "player");
  25.    while(e)
  26.       {
  27.       PlayerNum = PlayerNum + 1;
  28.       e = find(e, classname, "player");
  29.       }
  30.  
  31.    return(PlayerNum);
  32.    };
  33.  
  34. float() AdminIn =
  35.    {
  36.    local float AdminFlag;
  37.  
  38.    local entity e;
  39.  
  40.    AdminFlag = 0;
  41.    e = find(world, classname, "player");
  42.    while(e)
  43.       {
  44.       if(e.items & IT_ADMIN) AdminFlag = AdminFlag + 1;
  45.       e = find(e, classname, "player");
  46.       }
  47.  
  48.    return(AdminFlag);
  49.    };
  50.  
  51. // preserve admin connection, and print entrace messages
  52. void() PlayerInitiate =
  53.    {
  54. // print bulitin
  55.    bprint(self.netname);
  56.    bprint(" entered the game\n");
  57. // server fine
  58.    if(PlayerCount() < PLAYERLIMIT - RCONNECT + AdminIn())
  59.       {
  60. // private message
  61.       sprint(self, "Hello ");
  62.       sprint(self, self.netname);
  63.       sprint(self, ", welcome to PsychoServer.\n");
  64.       sprint(self, "Visit this server's homepage at http://user.mc.net/psychop/quake.html.\n");
  65.       sprint(self, "This is a waiter-free server, you will be kicked if you camp.\n");
  66.       if(cvar("teamplay"))
  67.          {
  68.          sprint(self, "Teamplay is on, so please pick a team.\n");
  69.          sprint(self, "If you kill a teammate you will loose a frag.\n");
  70.          sprint(self, "Team scores are printed at the exit of the level.\n");
  71.          }
  72.       if(cvar("noexit")) sprint(self, "You will be fragged if you attempt to exit.\n");
  73.       }
  74. // server full
  75.    else
  76.       {
  77.       sprint(self, "The server is full, please enter your password.\n");
  78.       sound(self, CHAN_AUTO, "player/death1.wav", 1, ATTN_NORM);
  79.       self.Kicking = TRUE;
  80.       self.OpTime = time + KTIME;
  81.       }
  82.    };
  83.  
  84. // play exit sound and give invunerablity to every one
  85. void() TeamExit =
  86.    {
  87.    local entity e;
  88.  
  89.    e = find(world, classname, "player");
  90.    while(e)
  91.       {
  92. // sound
  93.       sound(e, CHAN_BODY, "player/tornoff2.wav", 1, ATTN_NONE);
  94. // flash
  95.       stuffcmd(e, "bf\n");
  96. // give invulnerability
  97.       e.items = e.items | IT_INVULNERABILITY;
  98.       e.invincible_time = 1;
  99.       e.invincible_finished = time + EXITWAIT + 3;
  100. // get next player
  101.       e = find(e, classname, "player");
  102.       }
  103.    };
  104.  
  105. // Return a name for the color of a team
  106. string(float Team) GetTeamColor =
  107.    {
  108.    if(Team == 0) return("blue");
  109.    if(Team == 1) return("steel blue");
  110.    if(Team == 2) return("brown");
  111.    if(Team == 3) return("baby blue");
  112.    if(Team == 4) return("green");
  113.    if(Team == 5) return("red");
  114.    if(Team == 6) return("olive");
  115.    if(Team == 7) return("orange");
  116.    if(Team == 8) return("peech");
  117.    if(Team == 9) return("purple");
  118.    if(Team == 10) return("majenta");
  119.    if(Team == 11) return("grey");
  120.    if(Team == 12) return("aqua");
  121.    if(Team == 13) return("yellow");
  122.    };
  123.  
  124. // Get the score for a given team
  125. float(float Team) GetTeamScore =
  126.    {
  127.    local entity e;
  128.    local float TeamFrags;
  129.  
  130. // build a sorted list
  131.    TeamFrags = 32767;
  132.    e = find(world, classname, "player");
  133.    while(e)
  134.       {
  135.       if(e.team == Team)
  136.          {
  137.          if(TeamFrags == 32767) TeamFrags = 0;
  138.          TeamFrags = TeamFrags + e.frags;
  139.          }
  140.       e = find(e, classname, "player");
  141.       }
  142.  
  143.    return(TeamFrags);
  144.    };
  145.  
  146. // Bulitin print team scores before intermision
  147. void() PrintTeamScores =
  148.    {
  149.    local float Team, WinFrags, TeamFrags, Winners;
  150.  
  151. // get highest frag count
  152.    Team = 0;
  153.    WinFrags = -100;
  154.    while(Team < 14)
  155.       {
  156.       TeamFrags = GetTeamScore(Team);
  157.       if(TeamFrags != 32767)
  158.          {
  159.          if(TeamFrags > WinFrags) WinFrags = TeamFrags;
  160.  
  161. // print scores
  162.          bprint("Team ");
  163.          bprint(GetTeamColor(Team));
  164.          bprint(" got ");
  165.          bprint(ftos(TeamFrags));
  166.          bprint(" frags.\n");
  167.          }
  168.       Team = Team + 1;
  169.       }
  170.  
  171. // get number of team with most frags
  172.    Team = 0;
  173.    Winners = 0;
  174.    while(Team < 14)
  175.       {
  176.       TeamFrags = GetTeamScore(Team);
  177.       if(TeamFrags != 32767)
  178.          {
  179.          if(TeamFrags == WinFrags) Winners = Winners + 1;
  180.          }
  181.       Team = Team + 1;
  182.       }
  183.  
  184. // list winners
  185.    Team = 0;
  186.    while(Team < 14)
  187.       {
  188.       TeamFrags = GetTeamScore(Team);
  189.       if(TeamFrags != 32767)
  190.          {
  191.          if(TeamFrags == WinFrags)
  192.             {
  193.             if(Winners == 1)
  194.                {
  195.                bprint("Team ");
  196.                bprint(GetTeamColor(Team));
  197.                bprint(" won with ");
  198.                bprint(ftos(TeamFrags));
  199.                bprint(" frags.\n");
  200.                }
  201.             else
  202.                {
  203.                bprint("Team ");
  204.                bprint(GetTeamColor(Team));
  205.                bprint(" tied for first with ");
  206.                bprint(ftos(TeamFrags));
  207.                bprint(" frags.\n");
  208.                }
  209.             }
  210.          }
  211.       Team = Team + 1;
  212.       }
  213.    };
  214.  
  215. /*
  216. =============================================================================
  217.  
  218.                 LEVEL CHANGING / INTERMISSION
  219.  
  220. =============================================================================
  221. */
  222.  
  223. float    intermission_running;
  224. float    intermission_exittime;
  225.  
  226. /*QUAKED info_intermission (1 0.5 0.5) (-16 -16 -16) (16 16 16)
  227. This is the camera point for the intermission.
  228. Use mangle instead of angle, so you can set pitch or roll as well as yaw.  'pitch roll yaw'
  229. */
  230. void() info_intermission =
  231. {
  232. };
  233.  
  234. void() SetChangeParms =
  235.    {
  236. // remove items
  237.    self.items = self.items - (self.items & (IT_KEY1 | IT_KEY2 | IT_INVISIBILITY | IT_INVULNERABILITY | IT_SUIT | IT_QUAD));
  238.  
  239. // cap super health
  240.    if(self.health > 100) self.health = 100;
  241.    if(self.health < 50) self.health = 50;
  242.    parm1 = self.items;
  243.    parm2 = self.health;
  244.    parm3 = self.armorvalue;
  245.    if(self.ammo_shells < 25) parm4 = 25;
  246.    else parm4 = self.ammo_shells;
  247.    parm5 = self.ammo_nails;
  248.    parm6 = self.ammo_rockets;
  249.    parm7 = self.ammo_cells;
  250.    parm8 = self.weapon;
  251.    parm9 = self.armortype * 100;
  252.    parm16 = self.NewPlayer;
  253.    };
  254.  
  255. void() SetNewParms =
  256.    {
  257.    if(self.items & IT_ADMIN) parm1 = IT_SHOTGUN | IT_AXE | IT_EXTRA_WEAPON | IT_ADMIN;
  258.    else parm1 = IT_SHOTGUN | IT_AXE;
  259.    parm2 = 100;
  260.    parm3 = 0;
  261.    parm4 = 25;
  262.    parm5 = 0;
  263.    parm6 = 0;
  264.    parm6 = 0;
  265.    parm8 = 1;
  266.    parm9 = 0;
  267.    parm16 = self.NewPlayer;
  268.    };
  269.  
  270. void() DecodeLevelParms =
  271.    {
  272. // take away all stuff on starting new episode
  273.    if(serverflags) if(world.model == "maps/start.bsp") SetNewParms();
  274.  
  275.    self.items = parm1;
  276.    self.health = parm2;
  277.    self.armorvalue = parm3;
  278.    self.ammo_shells = parm4;
  279.    self.ammo_nails = parm5;
  280.    self.ammo_rockets = parm6;
  281.    self.ammo_cells = parm7;
  282.    self.weapon = parm8;
  283.    self.armortype = parm9 * 0.01;
  284.    self.NewPlayer = parm16;
  285.    };
  286.  
  287. /*
  288. ============
  289. FindIntermission
  290.  
  291. Returns the entity to view from
  292. ============
  293. */
  294. entity() FindIntermission =
  295. {
  296.     local    entity spot;
  297.     local    float cyc;
  298.  
  299. // look for info_intermission first
  300.     spot = find (world, classname, "info_intermission");
  301.     if (spot)
  302.     {    // pick a random one
  303.         cyc = random() * 4;
  304.         while (cyc > 1)
  305.         {
  306.             spot = find (spot, classname, "info_intermission");
  307.             if (!spot)
  308.                 spot = find (spot, classname, "info_intermission");
  309.             cyc = cyc - 1;
  310.         }
  311.         return spot;
  312.     }
  313.  
  314. // then look for the start position
  315.     spot = find (world, classname, "info_player_start");
  316.     if (spot)
  317.         return spot;
  318.     
  319. // testinfo_player_start is only found in regioned levels
  320.     spot = find (world, classname, "testplayerstart");
  321.     if (spot)
  322.         return spot;
  323.     
  324.     objerror ("FindIntermission: no spot");
  325. };
  326.  
  327.  
  328. string nextmap;
  329. void() GotoNextMap =
  330.    {
  331. // if samelevel is set, stay on same level
  332.    if(cvar("samelevel")) changelevel(mapname);
  333.    else changelevel(nextmap);
  334.    };
  335.  
  336.  
  337. void() ExitIntermission =
  338.    {
  339. // skip any text in deathmatch
  340.    if(deathmatch)
  341.       {
  342.       GotoNextMap();
  343.       return;
  344.       }
  345.     
  346.    intermission_exittime = time + 1;
  347.    intermission_running = intermission_running + 1;
  348.  
  349. //
  350. // run some text if at the end of an episode
  351. //
  352.    if(intermission_running == 2)
  353.       {
  354.       if(world.model == "maps/e1m7.bsp")
  355.          {
  356.          WriteByte (MSG_ALL, SVC_CDTRACK);
  357.          WriteByte (MSG_ALL, 2);
  358.          WriteByte (MSG_ALL, 3);
  359.          if(!cvar("registered"))
  360.             {
  361.             WriteByte(MSG_ALL, SVC_FINALE);
  362.             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!");
  363.             }
  364.          else
  365.             {
  366.             WriteByte(MSG_ALL, SVC_FINALE);
  367.             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!");
  368.             }
  369.          return;
  370.          }
  371.       else if(world.model == "maps/e2m6.bsp")
  372.          {
  373.          WriteByte(MSG_ALL, SVC_CDTRACK);
  374.          WriteByte(MSG_ALL, 2);
  375.          WriteByte(MSG_ALL, 3);
  376.  
  377.          WriteByte(MSG_ALL, SVC_FINALE);
  378.          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.");
  379.          return;
  380.          }
  381.       else if(world.model == "maps/e3m6.bsp")
  382.          {
  383.          WriteByte(MSG_ALL, SVC_CDTRACK);
  384.          WriteByte(MSG_ALL, 2);
  385.          WriteByte(MSG_ALL, 3);
  386.  
  387.          WriteByte(MSG_ALL, SVC_FINALE);
  388.          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.");
  389.          return;
  390.          }
  391.       else if(world.model == "maps/e4m7.bsp")
  392.          {
  393.          WriteByte (MSG_ALL, SVC_CDTRACK);
  394.          WriteByte (MSG_ALL, 2);
  395.          WriteByte (MSG_ALL, 3);
  396.  
  397.          WriteByte (MSG_ALL, SVC_FINALE);
  398.          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.");
  399.          return;
  400.          }
  401.  
  402.       GotoNextMap();
  403.       }
  404.     
  405.    if(intermission_running == 3)
  406.       {
  407. // shareware episode has been completed, go to sell screen
  408.       if(!cvar("registered"))
  409.          {       
  410.          WriteByte(MSG_ALL, SVC_SELLSCREEN);
  411.          return;
  412.          }
  413.         
  414.       if((serverflags & 15) == 15)
  415.          {
  416.          WriteByte(MSG_ALL, SVC_FINALE);
  417.          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.");
  418.          return;
  419.          }
  420.       }
  421.    GotoNextMap();
  422.    };
  423.  
  424. /*
  425. ============
  426. IntermissionThink
  427.  
  428. When the player presses attack or jump, change to the next level
  429. ============
  430. */
  431. void() IntermissionThink =
  432.    {
  433.    if(time < intermission_exittime) return;
  434.  
  435.    if(!self.button0 && !self.button1 && !self.button2) return;
  436.     
  437.    ExitIntermission();
  438.    };
  439.  
  440. void() execute_changelevel =
  441.    {
  442.    local entity pos;
  443.  
  444.    intermission_running = 1;
  445.     
  446. // enforce a wait time before allowing changelevel
  447.    if(deathmatch) intermission_exittime = time + 5;
  448.    else intermission_exittime = time + 2;
  449.  
  450.    WriteByte (MSG_ALL, SVC_CDTRACK);
  451.    WriteByte (MSG_ALL, 3);
  452.    WriteByte (MSG_ALL, 3);
  453.     
  454.    pos = FindIntermission();
  455.  
  456.    other = find(world, classname, "player");
  457.    while(other != world)
  458.       {
  459.       other.view_ofs = '0 0 0';
  460.       other.angles = other.v_angle = pos.mangle;
  461.       other.fixangle = TRUE;          // turn this way immediately
  462.       other.nextthink = time + 0.5;
  463.       other.takedamage = DAMAGE_NO;
  464.       other.solid = SOLID_NOT;
  465.       other.movetype = MOVETYPE_NONE;
  466.       other.modelindex = 0;
  467.       setorigin(other, pos.origin);
  468.       other = find(other, classname, "player");
  469.       } 
  470.  
  471.    WriteByte(MSG_ALL, SVC_INTERMISSION);
  472.    };
  473.  
  474.  
  475. void() changelevel_touch =
  476.    {
  477.    local entity pos;
  478.  
  479.    if(other.classname != "player") return;
  480.  
  481.    if(cvar("noexit"))
  482.       {
  483.       T_Damage(other, self, self, 50000);
  484.       return;
  485.       }
  486.    bprint(other.netname);
  487.    bprint(" exited the level\n");
  488.  
  489.    nextmap = self.map;
  490.  
  491.    SUB_UseTargets();
  492.  
  493. // NO_INTERMISSION
  494.    if((self.spawnflags & 1) && (deathmatch == 0))
  495.       {       
  496.       GotoNextMap();
  497.       return;
  498.       }
  499.     
  500.    self.touch = SUB_Null;
  501.  
  502. // change level in 5 sec
  503.    if(cvar("teamplay"))
  504.       {
  505. // print scores
  506.       PrintTeamScores();
  507.       TeamExit();
  508. // change level in after delay
  509.       self.think = execute_changelevel;
  510.       self.nextthink = time + EXITWAIT;
  511.       }
  512.    else
  513.       {
  514.       self.think = execute_changelevel;
  515.       self.nextthink = time + 0.1;
  516.       }
  517.    };
  518.  
  519. /*
  520. QUAKED trigger_changelevel (0.5 0.5 0.5) ? NO_INTERMISSION
  521. 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.
  522. */
  523. void() trigger_changelevel =
  524.    {
  525.    if(!self.map) objerror("chagnelevel trigger doesn't have map");
  526.  
  527.    InitTrigger();
  528.    self.touch = changelevel_touch;
  529.    };
  530.  
  531.  
  532. /*
  533. =============================================================================
  534.  
  535.                 PLAYER GAME EDGE FUNCTIONS
  536.  
  537. =============================================================================
  538. */
  539.  
  540. void() set_suicide_frame;
  541.  
  542. // called by ClientKill and DeadThink
  543. void() respawn =
  544.    {
  545.    if(coop)
  546.       {
  547. // make a copy of the dead body for appearances sake
  548.       CopyToBodyQue (self);
  549. // get the spawn parms as they were at level start
  550.       setspawnparms (self);
  551. // respawn
  552.       PutClientInServer();
  553.       }
  554.    else if(deathmatch)
  555.       {
  556. // make a copy of the dead body for appearances sake
  557.       CopyToBodyQue(self);
  558. // set default spawn parms
  559.       SetNewParms();
  560. // respawn
  561.       PutClientInServer();
  562.       }
  563.    else
  564.       {
  565. // restart the entire server
  566.       localcmd("restart\n");
  567.       }
  568.    };
  569.  
  570.  
  571. /*
  572. ============
  573. ClientKill
  574.  
  575. Player entered the suicide command
  576. ============
  577. */
  578. void() ClientKill =
  579.    {
  580.    bprint(self.netname);
  581.    bprint (" suicides\n");
  582.    set_suicide_frame();
  583.    self.modelindex = modelindex_player;
  584.    self.frags = self.frags - 2;    // extra penalty
  585.    respawn();
  586.    };
  587.  
  588. float(vector v) CheckSpawnPoint =
  589.    {
  590.    return FALSE;
  591.    };
  592.  
  593. /*
  594. ============
  595. SelectSpawnPoint
  596.  
  597. Returns the entity to spawn at
  598. ============
  599. */
  600. entity() SelectSpawnPoint =
  601. {
  602.     local    entity spot;
  603.  
  604. // testinfo_player_start is only found in regioned levels
  605.     spot = find (world, classname, "testplayerstart");
  606.     if (spot)
  607.         return spot;
  608.  
  609. // choose a info_player_deathmatch point
  610.     if (coop)
  611.     {
  612.         lastspawn = find(lastspawn, classname, "info_player_coop");
  613.         if (lastspawn == world)
  614.             lastspawn = find (lastspawn, classname, "info_player_start");
  615.         if (lastspawn != world)
  616.             return lastspawn;
  617.     }
  618.     else if (deathmatch)
  619.     {
  620.         lastspawn = find(lastspawn, classname, "info_player_deathmatch");
  621.         if (lastspawn == world)
  622.             lastspawn = find (lastspawn, classname, "info_player_deathmatch");
  623.         if (lastspawn != world)
  624.             return lastspawn;
  625.     }
  626.  
  627.     if (serverflags)
  628.     {    // return with a rune to start
  629.         spot = find (world, classname, "info_player_start2");
  630.         if (spot)
  631.             return spot;
  632.     }
  633.  
  634.     spot = find (world, classname, "info_player_start");
  635.     if (!spot)
  636.         error ("PutClientInServer: no info_player_start on level");
  637.  
  638.     return spot;
  639. };
  640.  
  641. /*
  642. ===========
  643. PutClientInServer
  644.  
  645. called each time a player is spawned
  646. ============
  647. */
  648. void() DecodeLevelParms;
  649. void() PlayerDie;
  650.  
  651.  
  652. void() PutClientInServer =
  653. {
  654.     local    entity spot;
  655.  
  656.     self.classname = "player";
  657.     self.health = 100;
  658.     self.takedamage = DAMAGE_AIM;
  659.     self.solid = SOLID_SLIDEBOX;
  660.     self.movetype = MOVETYPE_WALK;
  661.     self.show_hostile = 0;
  662.     self.max_health = 100;
  663.     self.flags = FL_CLIENT;
  664.     self.air_finished = time + 12;
  665.     self.dmg = 2;           // initial water damage
  666.     self.super_damage_finished = 0;
  667.     self.radsuit_finished = 0;
  668.     self.invisible_finished = 0;
  669.     self.invincible_finished = 0;
  670.     self.effects = 0;
  671.     self.invincible_time = 0;
  672.  
  673.     DecodeLevelParms();
  674.  
  675.     W_SetCurrentAmmo();
  676.  
  677.     self.attack_finished = time;
  678.     self.th_pain = player_pain;
  679.     self.th_die = PlayerDie;
  680.  
  681.     self.deadflag = DEAD_NO;
  682. // paustime is set by teleporters to keep the player from moving a while
  683.     self.pausetime = 0;
  684.  
  685.     spot = SelectSpawnPoint ();
  686.  
  687.     self.origin = spot.origin + '0 0 1';
  688.     self.angles = spot.angles;
  689.     self.fixangle = TRUE;        // turn this way immediately
  690.  
  691. // oh, this is a hack!
  692.     setmodel (self, "progs/eyes.mdl");
  693.     modelindex_eyes = self.modelindex;
  694.  
  695.     setmodel (self, "progs/player.mdl");
  696.     modelindex_player = self.modelindex;
  697.  
  698.     setsize (self, VEC_HULL_MIN, VEC_HULL_MAX);
  699.     
  700.     self.view_ofs = '0 0 22';
  701.  
  702.     player_stand1 ();
  703.     
  704.     if (deathmatch || coop)
  705.     {
  706.         makevectors(self.angles);
  707.         spawn_tfog (self.origin + v_forward*20);
  708.     }
  709.  
  710.     spawn_tdeath (self.origin, self);
  711. };
  712.  
  713.  
  714. /*
  715. =============================================================================
  716.  
  717.                 QUAKED FUNCTIONS
  718.  
  719. =============================================================================
  720. */
  721.  
  722.  
  723. /*QUAKED info_player_start (1 0 0) (-16 -16 -24) (16 16 24)
  724. The normal starting point for a level.
  725. */
  726. void() info_player_start =
  727. {
  728. };
  729.  
  730.  
  731. /*QUAKED info_player_start2 (1 0 0) (-16 -16 -24) (16 16 24)
  732. Only used on start map for the return point from an episode.
  733. */
  734. void() info_player_start2 =
  735. {
  736. };
  737.  
  738.  
  739. /*
  740. saved out by quaked in region mode
  741. */
  742. void() testplayerstart =
  743. {
  744. };
  745.  
  746. /*QUAKED info_player_deathmatch (1 0 1) (-16 -16 -24) (16 16 24)
  747. potential spawning position for deathmatch games
  748. */
  749. void() info_player_deathmatch =
  750. {
  751. };
  752.  
  753. /*QUAKED info_player_coop (1 0 1) (-16 -16 -24) (16 16 24)
  754. potential spawning position for coop games
  755. */
  756. void() info_player_coop =
  757. {
  758. };
  759.  
  760. /*
  761. ===============================================================================
  762.  
  763. RULES
  764.  
  765. ===============================================================================
  766. */
  767.  
  768. void(entity c) PrintClientScore =
  769. {
  770.     if (c.frags > -10 && c.frags < 0)
  771.         bprint (" ");
  772.     else if (c.frags >= 0)
  773.     {
  774.         if (c.frags < 100)
  775.             bprint (" ");
  776.         if (c.frags < 10)
  777.             bprint (" ");
  778.     }
  779.     bprint (ftos(c.frags));
  780.     bprint (" ");
  781.     bprint (c.netname);
  782.     bprint ("\n");
  783. };
  784.  
  785. void() DumpScore =
  786. {
  787.     local entity    e, sort, walk;
  788.  
  789.     if (world.chain)
  790.         error ("DumpScore: world.chain is set");
  791.  
  792. // build a sorted lis
  793.     e = find(world, classname, "player");
  794.     sort = world;
  795.     while (e)
  796.     {
  797.         if (!sort)
  798.         {
  799.             sort = e;
  800.             e.chain = world;
  801.         }
  802.         else
  803.         {
  804.             if (e.frags > sort.frags)
  805.             {
  806.                 e.chain = sort;
  807.                 sort = e;
  808.             }
  809.             else
  810.             {
  811.                 walk = sort;
  812.                 do
  813.                 {
  814.                     if (!walk.chain)
  815.                     {
  816.                         e.chain = world;
  817.                         walk.chain = e;
  818.                     }
  819.                     else if (walk.chain.frags < e.frags)
  820.                     {
  821.                         e.chain = walk.chain;
  822.                         walk.chain = e;
  823.                     }
  824.                     else
  825.                         walk = walk.chain;
  826.                 } while (walk.chain != e);
  827.             }
  828.         }
  829.  
  830.         e = find(e, classname, "player");
  831.     }
  832.  
  833. // print the list
  834.  
  835.     bprint ("\n");
  836.     while (sort)
  837.     {
  838.                 PrintClientScore(sort);
  839.         sort = sort.chain;
  840.     }
  841.     bprint ("\n");
  842. };
  843.  
  844. /*--------------------------------------+
  845. |  go to the next level for deathmatch  |
  846. +--------------------------------------*/
  847. void() NextLevel =
  848.    {
  849.    local entity o, e;
  850.  
  851. // find a trigger changelevel
  852.    o = find(world, classname, "trigger_changelevel");
  853.    if(!o || mapname == "start")
  854.       {
  855. // go back to same map if no trigger_changelevel
  856.       o = spawn();
  857.       o.map = mapname;
  858.       }
  859.  
  860.    nextmap = o.map;
  861.  
  862.    if(o.nextthink < time)
  863.       {
  864. // print team scores
  865.       if(cvar("teamplay") != 0)
  866.          {
  867. // print scores
  868.          PrintTeamScores();
  869.          TeamExit();
  870. // change level in 5 sec
  871.          o.think = execute_changelevel;
  872.          o.nextthink = time + EXITWAIT;
  873.          }
  874.       else
  875.          {
  876.          o.think = execute_changelevel;
  877.          o.nextthink = time + 0.1;
  878.          }
  879.       }
  880.    };
  881.  
  882. /*
  883. ============
  884. CheckRules
  885.  
  886. Exit deathmatch games upon conditions
  887. ============
  888. */
  889. void() CheckRules =
  890.    {
  891.    local float timelimit;
  892.    local float fraglimit;
  893.     
  894. // someone else quit the game already
  895.    if(gameover) return;
  896.         
  897.    timelimit = cvar("timelimit") * 60;
  898.    fraglimit = cvar("fraglimit");
  899.     
  900.    if(timelimit && time >= timelimit)
  901.       {
  902.       NextLevel();
  903. /*
  904.       gameover = TRUE;
  905.       bprint("\n\n\n==============================\n");
  906.       bprint("game exited after ");
  907.       bprint(ftos(timelimit/60));
  908.       bprint(" minutes\n");
  909.       DumpScore();
  910.       localcmd("killserver\n");
  911. */
  912.       return;
  913.       }
  914.     
  915.    if(fraglimit && self.frags >= fraglimit)
  916.       {
  917.       NextLevel();
  918. /*
  919.       gameover = TRUE;
  920.       bprint("\n\n\n==============================\n");
  921.       bprint("game exited after ");
  922.       bprint(ftos(self.frags));
  923.       bprint(" frags\n");
  924.       DumpScore();
  925.       localcmd("killserver\n");
  926. */
  927.       return;
  928.       }
  929.    };
  930.  
  931. //============================================================================
  932.  
  933. void() PlayerDeathThink =
  934.    {
  935.    local entity old_self;
  936.    local float forward;
  937.  
  938.    if((self.flags & FL_ONGROUND))
  939.       {
  940.       forward = vlen(self.velocity);
  941.       forward = forward - 20;
  942.       if(forward <= 0) self.velocity = '0 0 0';
  943.       else self.velocity = forward * normalize(self.velocity);
  944.       }
  945.  
  946. // wait for all buttons released
  947.    if(self.deadflag == DEAD_DEAD)
  948.       {
  949.       if(self.button2 || self.button1 || self.button0) return;
  950.       self.deadflag = DEAD_RESPAWNABLE;
  951.       return;
  952.       }
  953.  
  954. // wait for any button down
  955.    if(!self.button2 && !self.button1 && !self.button0 && time < self.teleport_time) return;
  956.  
  957.    self.button0 = 0;
  958.    self.button1 = 0;
  959.    self.button2 = 0;
  960.    respawn();
  961.    };
  962.  
  963.  
  964. void() PlayerJump =
  965. {
  966.     local vector start, end;
  967.     
  968.     if (self.flags & FL_WATERJUMP)
  969.         return;
  970.     
  971.     if (self.waterlevel >= 2)
  972.     {
  973.         if (self.watertype == CONTENT_WATER)
  974.             self.velocity_z = 100;
  975.         else if (self.watertype == CONTENT_SLIME)
  976.             self.velocity_z = 80;
  977.         else
  978.             self.velocity_z = 50;
  979.  
  980. // play swiming sound
  981.         if (self.swim_flag < time)
  982.         {
  983.             self.swim_flag = time + 1;
  984.             if (random() < 0.5)
  985.                 sound(self, CHAN_BODY, "misc/water1.wav", 1, ATTN_NORM);
  986.             else
  987.                 sound(self, CHAN_BODY, "misc/water2.wav", 1, ATTN_NORM);
  988.         }
  989.  
  990.         return;
  991.     }
  992.  
  993.     if (!(self.flags & FL_ONGROUND))
  994.         return;
  995.  
  996.     if ( !(self.flags & FL_JUMPRELEASED) )
  997.         return;        // don't pogo stick
  998.  
  999.     self.flags = self.flags - (self.flags & FL_JUMPRELEASED);
  1000.  
  1001.     self.flags = self.flags - FL_ONGROUND;    // don't stairwalk
  1002.     
  1003.     self.button2 = 0;
  1004. // player jumping sound
  1005.     sound (self, CHAN_BODY, "player/plyrjmp8.wav", 1, ATTN_NORM);
  1006.     self.velocity_z = self.velocity_z + 270;
  1007. };
  1008.  
  1009.  
  1010. /*
  1011. ===========
  1012. WaterMove
  1013.  
  1014. ============
  1015. */
  1016. .float    dmgtime;
  1017.  
  1018. void() WaterMove =
  1019. {
  1020. //dprint (ftos(self.waterlevel));
  1021.     if (self.movetype == MOVETYPE_NOCLIP)
  1022.         return;
  1023.     if (self.health < 0)
  1024.         return;
  1025.  
  1026.     if (self.waterlevel != 3)
  1027.     {
  1028.         if (self.air_finished < time)
  1029.             sound (self, CHAN_VOICE, "player/gasp2.wav", 1, ATTN_NORM);
  1030.         else if (self.air_finished < time + 9)
  1031.             sound (self, CHAN_VOICE, "player/gasp1.wav", 1, ATTN_NORM);
  1032.         self.air_finished = time + 12;
  1033.         self.dmg = 2;
  1034.     }
  1035.     else if (self.air_finished < time)
  1036.     {    // drown!
  1037.         if (self.pain_finished < time)
  1038.         {
  1039.             self.dmg = self.dmg + 2;
  1040.             if (self.dmg > 15)
  1041.                 self.dmg = 10;
  1042.             T_Damage (self, world, world, self.dmg);
  1043.             self.pain_finished = time + 1;
  1044.         }
  1045.     }
  1046.  
  1047.     if (!self.waterlevel)
  1048.     {
  1049.         if (self.flags & FL_INWATER)
  1050.         {
  1051.             // play leave water sound
  1052.             sound (self, CHAN_BODY, "misc/outwater.wav", 1, ATTN_NORM);
  1053.             self.flags = self.flags - FL_INWATER;
  1054.         }
  1055.         return;
  1056.     }
  1057.  
  1058.     if (self.watertype == CONTENT_LAVA)
  1059.     {    // do damage
  1060.         if (self.dmgtime < time)
  1061.         {
  1062.             if (self.radsuit_finished > time)
  1063.                 self.dmgtime = time + 1;
  1064.             else
  1065.                 self.dmgtime = time + 0.2;
  1066.  
  1067.             T_Damage (self, world, world, 10*self.waterlevel);
  1068.         }
  1069.     }
  1070.     else if (self.watertype == CONTENT_SLIME)
  1071.     {    // do damage
  1072.         if (self.dmgtime < time && self.radsuit_finished < time)
  1073.         {
  1074.             self.dmgtime = time + 1;
  1075.             T_Damage (self, world, world, 4*self.waterlevel);
  1076.         }
  1077.     }
  1078.     
  1079.     if ( !(self.flags & FL_INWATER) )
  1080.     {    
  1081.  
  1082. // player enter water sound
  1083.  
  1084.         if (self.watertype == CONTENT_LAVA)
  1085.             sound (self, CHAN_BODY, "player/inlava.wav", 1, ATTN_NORM);
  1086.         if (self.watertype == CONTENT_WATER)
  1087.             sound (self, CHAN_BODY, "player/inh2o.wav", 1, ATTN_NORM);
  1088.         if (self.watertype == CONTENT_SLIME)
  1089.             sound (self, CHAN_BODY, "player/slimbrn2.wav", 1, ATTN_NORM);
  1090.  
  1091.         self.flags = self.flags + FL_INWATER;
  1092.         self.dmgtime = 0;
  1093.     }
  1094.     
  1095.     if (! (self.flags & FL_WATERJUMP) )
  1096.         self.velocity = self.velocity - 0.8*self.waterlevel*frametime*self.velocity;
  1097. };
  1098.  
  1099. void() CheckWaterJump =
  1100. {
  1101.     local vector start, end;
  1102.  
  1103. // check for a jump-out-of-water
  1104.     makevectors (self.angles);
  1105.     start = self.origin;
  1106.     start_z = start_z + 8; 
  1107.     v_forward_z = 0;
  1108.     normalize(v_forward);
  1109.     end = start + v_forward*24;
  1110.     traceline (start, end, TRUE, self);
  1111.     if (trace_fraction < 1)
  1112.     {    // solid at waist
  1113.         start_z = start_z + self.maxs_z - 8;
  1114.         end = start + v_forward*24;
  1115.         self.movedir = trace_plane_normal * -50;
  1116.         traceline (start, end, TRUE, self);
  1117.         if (trace_fraction == 1)
  1118.         {    // open at eye level
  1119.             self.flags = self.flags | FL_WATERJUMP;
  1120.             self.velocity_z = 225;
  1121.             self.flags = self.flags - (self.flags & FL_JUMPRELEASED);
  1122.             self.teleport_time = time + 2;    // safety net
  1123.             return;
  1124.         }
  1125.     }
  1126. };
  1127.  
  1128.  
  1129. /*
  1130. ================
  1131. PlayerPreThink
  1132.  
  1133. Called every frame before physics are run
  1134. ================
  1135. */
  1136. void() PlayerPreThink =
  1137.    {
  1138.    local float mspeed, aspeed;
  1139.    local float r;
  1140.  
  1141. // initiation
  1142.    if(self.NewPlayer < 10) self.NewPlayer = self.NewPlayer + 1;
  1143.    if(self.NewPlayer == 9) PlayerInitiate();
  1144.  
  1145. // check for time limit
  1146.    if(self.InPassword && time >= self.PassTime)
  1147.       {
  1148.       sprint(self, "Your time is up.\n");
  1149.       stuffcmd(self, "disconnect\n");
  1150.       return;
  1151.       }
  1152.  
  1153. // preservation of reserved conections
  1154.    if(self.Kicking && time >= self.OpTime)
  1155.       {
  1156.       sprint(self, "Sorry, ");
  1157.       sprint(self, self.netname);
  1158.       sprint(self, ", this connection is reserved for an administrator.\n");
  1159.  
  1160.       stuffcmd(self, "disconnect");
  1161.       }
  1162.  
  1163. // camper killer
  1164.    if(self.MoveBy != 0 && self.MoveBy <= time)
  1165.       {
  1166. // message
  1167.       bprint(self.netname);
  1168.       bprint(" has been kicked for camping.\n");
  1169. // gib
  1170.       T_Damage(world, self, self.owner, 1000);
  1171. // disconnect
  1172.       stuffcmd(self, "disconnect\n");
  1173.       }
  1174.  
  1175.    if(intermission_running)
  1176.       {
  1177.       IntermissionThink();   // otherwise a button could be missed between
  1178.       return;                                 // the think tics
  1179.       }
  1180.  
  1181.    if(self.view_ofs == '0 0 0') return;  // intermission or finale
  1182.  
  1183.    makevectors(self.v_angle);             // is this still used
  1184.  
  1185.    CheckRules();
  1186.    WaterMove();
  1187.  
  1188.    if(self.waterlevel == 2) CheckWaterJump ();
  1189.  
  1190.    if(self.deadflag >= DEAD_DEAD)
  1191.       {
  1192.       PlayerDeathThink();
  1193.       return;
  1194.       }
  1195.     
  1196.    if(self.deadflag == DEAD_DYING) return; // dying, so do nothing
  1197.  
  1198.    if(self.button2)
  1199.       {
  1200.       PlayerJump();
  1201.       }
  1202.    else self.flags = self.flags | FL_JUMPRELEASED;
  1203.  
  1204. // teleporters can force a non-moving pause time    
  1205.    if(time < self.pausetime) self.velocity = '0 0 0';
  1206.    };
  1207.     
  1208. /*
  1209. ================
  1210. CheckPowerups
  1211.  
  1212. Check for turning off powerups
  1213. ================
  1214. */
  1215. void() CheckPowerups =
  1216. {
  1217.     if (self.health <= 0)
  1218.         return;
  1219.  
  1220. // invisibility
  1221.     if (self.invisible_finished)
  1222.     {
  1223. // sound and screen flash when items starts to run out
  1224.         if (self.invisible_sound < time)
  1225.         {
  1226.             sound(self, CHAN_AUTO, "items/inv3.wav", 0.5, ATTN_IDLE);
  1227.             self.invisible_sound = time + ((random() * 3) + 1);
  1228.         }
  1229.  
  1230.  
  1231.         if (self.invisible_finished < time + 3)
  1232.         {
  1233.             if (self.invisible_time == 1)
  1234.             {
  1235.                 sprint (self, "Ring of Shadows magic is fading\n");
  1236.                 stuffcmd (self, "bf\n");
  1237.                 sound (self, CHAN_AUTO, "items/inv2.wav", 1, ATTN_NORM);
  1238.                 self.invisible_time = time + 1;
  1239.             }
  1240.  
  1241.             if (self.invisible_time < time)
  1242.             {
  1243.                 self.invisible_time = time + 1;
  1244.                 stuffcmd (self, "bf\n");
  1245.             }
  1246.         }
  1247.  
  1248.         if (self.invisible_finished < time)
  1249.         {    // just stopped
  1250.             self.items = self.items - IT_INVISIBILITY;
  1251.             self.invisible_finished = 0;
  1252.             self.invisible_time = 0;
  1253.         }
  1254.         
  1255.     // use the eyes
  1256.         self.frame = 0;
  1257.         self.modelindex = modelindex_eyes;
  1258.     }
  1259.     else
  1260.         self.modelindex = modelindex_player;    // don't use eyes
  1261.  
  1262. // invincibility
  1263.     if (self.invincible_finished)
  1264.     {
  1265. // sound and screen flash when items starts to run out
  1266.         if (self.invincible_finished < time + 3)
  1267.         {
  1268.             if (self.invincible_time == 1)
  1269.             {
  1270.                 sprint (self, "Protection is almost burned out\n");
  1271.                 stuffcmd (self, "bf\n");
  1272.                 sound (self, CHAN_AUTO, "items/protect2.wav", 1, ATTN_NORM);
  1273.                 self.invincible_time = time + 1;
  1274.             }
  1275.             
  1276.             if (self.invincible_time < time)
  1277.             {
  1278.                 self.invincible_time = time + 1;
  1279.                 stuffcmd (self, "bf\n");
  1280.             }
  1281.         }
  1282.         
  1283.         if (self.invincible_finished < time)
  1284.         {    // just stopped
  1285.             self.items = self.items - IT_INVULNERABILITY;
  1286.             self.invincible_time = 0;
  1287.             self.invincible_finished = 0;
  1288.         }
  1289.         if (self.invincible_finished > time)
  1290.             self.effects = self.effects | EF_DIMLIGHT;
  1291.         else
  1292.             self.effects = self.effects - (self.effects & EF_DIMLIGHT);
  1293.     }
  1294.  
  1295. // super damage
  1296.     if (self.super_damage_finished)
  1297.     {
  1298.  
  1299. // sound and screen flash when items starts to run out
  1300.  
  1301.         if (self.super_damage_finished < time + 3)
  1302.         {
  1303.             if (self.super_time == 1)
  1304.             {
  1305.                 sprint (self, "Quad Damage is wearing off\n");
  1306.                 stuffcmd (self, "bf\n");
  1307.                 sound (self, CHAN_AUTO, "items/damage2.wav", 1, ATTN_NORM);
  1308.                 self.super_time = time + 1;
  1309.             }      
  1310.             
  1311.             if (self.super_time < time)
  1312.             {
  1313.                 self.super_time = time + 1;
  1314.                 stuffcmd (self, "bf\n");
  1315.             }
  1316.         }
  1317.  
  1318.         if (self.super_damage_finished < time)
  1319.         {    // just stopped
  1320.             self.items = self.items - IT_QUAD;
  1321.             self.super_damage_finished = 0;
  1322.             self.super_time = 0;
  1323.         }
  1324.         if (self.super_damage_finished > time)
  1325.             self.effects = self.effects | EF_DIMLIGHT;
  1326.         else
  1327.             self.effects = self.effects - (self.effects & EF_DIMLIGHT);
  1328.     }
  1329.  
  1330. // suit
  1331.     if (self.radsuit_finished)
  1332.     {
  1333.         self.air_finished = time + 12;        // don't drown
  1334.  
  1335. // sound and screen flash when items starts to run out
  1336.         if (self.radsuit_finished < time + 3)
  1337.         {
  1338.             if (self.rad_time == 1)
  1339.             {
  1340.                 sprint (self, "Air supply in Biosuit expiring\n");
  1341.                 stuffcmd (self, "bf\n");
  1342.                 sound (self, CHAN_AUTO, "items/suit2.wav", 1, ATTN_NORM);
  1343.                 self.rad_time = time + 1;
  1344.             }
  1345.             
  1346.             if (self.rad_time < time)
  1347.             {
  1348.                 self.rad_time = time + 1;
  1349.                 stuffcmd (self, "bf\n");
  1350.             }
  1351.         }
  1352.  
  1353.         if (self.radsuit_finished < time)
  1354.         {    // just stopped
  1355.             self.items = self.items - IT_SUIT;
  1356.             self.rad_time = 0;
  1357.             self.radsuit_finished = 0;
  1358.         }
  1359.     }    
  1360.  
  1361. };
  1362.  
  1363.  
  1364. /*
  1365. ================
  1366. PlayerPostThink
  1367.  
  1368. Called every frame after physics are run
  1369. ================
  1370. */
  1371. void() PlayerPostThink =
  1372. {
  1373.     local    float    mspeed, aspeed;
  1374.     local    float    r;
  1375.  
  1376.     if (self.view_ofs == '0 0 0')
  1377.         return;        // intermission or finale
  1378.     if (self.deadflag)
  1379.         return;
  1380.         
  1381. // do weapon stuff
  1382.  
  1383.         W_WeaponFrame();
  1384.  
  1385. // check to see if player landed and play landing sound    
  1386.     if ((self.jump_flag < -300) && (self.flags & FL_ONGROUND) && (self.health > 0))
  1387.     {
  1388.         if (self.watertype == CONTENT_WATER)
  1389.             sound (self, CHAN_BODY, "player/h2ojump.wav", 1, ATTN_NORM);
  1390.         else if (self.jump_flag < -650)
  1391.         {
  1392.             T_Damage (self, world, world, 5); 
  1393.             sound (self, CHAN_VOICE, "player/land2.wav", 1, ATTN_NORM);
  1394.             self.deathtype = "falling";
  1395.         }
  1396.         else
  1397.             sound (self, CHAN_VOICE, "player/land.wav", 1, ATTN_NORM);
  1398.  
  1399.         self.jump_flag = 0;
  1400.     }
  1401.  
  1402.     if (!(self.flags & FL_ONGROUND))
  1403.         self.jump_flag = self.velocity_z;
  1404.  
  1405.     CheckPowerups ();
  1406. };
  1407.  
  1408.  
  1409. /*
  1410. ===========
  1411. ClientConnect
  1412.  
  1413. called when a player connects to a server
  1414. ============
  1415. */
  1416. void() ClientConnect =
  1417.    {
  1418. // a client connecting during an intermission can cause problems
  1419.    if(intermission_running) ExitIntermission();
  1420.    };
  1421.  
  1422.  
  1423. /*
  1424. ===========
  1425. ClientDisconnect
  1426.  
  1427. called when a player disconnects from a server
  1428. ============
  1429. */
  1430. void() ClientDisconnect =
  1431.    {
  1432.    local entity e, sort, walk;
  1433.    local float ppos, dflag, pnum;
  1434.  
  1435. // if the level end trigger has been activated, just return
  1436. // since they aren't *really* leaving
  1437.    if(gameover) return;
  1438.  
  1439.    if(world.chain) error("ClientDisconnect: world.chain is set");
  1440.  
  1441. // build a sorted list
  1442.    e = find(world, classname, "player");
  1443.    sort = world;
  1444.    while(e)
  1445.       {
  1446.       if(!sort)
  1447.          {
  1448.          sort = e;
  1449.          e.chain = world;
  1450.          }
  1451.       else
  1452.          {
  1453.          if(e.frags > sort.frags)
  1454.             {
  1455.             e.chain = sort;
  1456.             sort = e;
  1457.             }
  1458.          else
  1459.             {
  1460.             walk = sort;
  1461.             do
  1462.                {
  1463.                if(!walk.chain)
  1464.                   {
  1465.                   e.chain = world;
  1466.                   walk.chain = e;
  1467.                   }
  1468.                else if(walk.chain.frags < e.frags)
  1469.                   {
  1470.                   e.chain = walk.chain;
  1471.                   walk.chain = e;
  1472.                   }
  1473.                else walk = walk.chain;
  1474.                }
  1475.             while(walk.chain != e);
  1476.             }
  1477.          }
  1478.       e = find(e, classname, "player");
  1479.       }
  1480.  
  1481. // find out your position and number of players
  1482.    ppos = 0;
  1483.    dflag = 0;
  1484.    pnum = 0;
  1485.    while(sort)
  1486.       {
  1487.       if(sort == self) dflag = 1;
  1488.       sort = sort.chain;
  1489.       if(dflag == 0) ppos = ppos + 1;
  1490.       if(sort) pnum = pnum + 1;
  1491.       }
  1492. // invert
  1493.    ppos = ppos - pnum;
  1494. // scale
  1495.    ppos = ppos / pnum;
  1496.  
  1497. // print msg
  1498.    if(ppos == 1)
  1499.       {
  1500.       bprint(self.netname);
  1501.       bprint(" kicked ass with ");
  1502.       bprint(ftos(self.frags));
  1503.       bprint(" frags\n");
  1504.       }
  1505.    if(ppos < 1 && ppos >= 0.7)
  1506.       {
  1507.       bprint(self.netname);
  1508.       bprint(" left with his pride and ");
  1509.       bprint(ftos(self.frags));
  1510.       bprint(" frags\n");
  1511.       }
  1512.    if(ppos < 0.7 && ppos >= 0.3)
  1513.       {
  1514.       bprint(self.netname);
  1515.       bprint(" held his own with ");
  1516.       bprint(ftos(self.frags));
  1517.       bprint(" frags\n");
  1518.       }
  1519.    if(ppos < 0.3 && ppos > 0)
  1520.       {
  1521.       bprint(self.netname);
  1522.       bprint(" ran away with his tail between his legs and ");
  1523.       bprint(ftos(self.frags));
  1524.       bprint(" frags\n");
  1525.       }
  1526.    if(ppos == 0)
  1527.       {
  1528.       bprint(self.netname);
  1529.       bprint(" wussed out with ");
  1530.       bprint(ftos(self.frags));
  1531.       bprint(" frags\n");
  1532.       }
  1533.  
  1534. // cleanup
  1535.    sound(self, CHAN_BODY, "player/tornoff2.wav", 1, ATTN_NONE);
  1536.    set_suicide_frame();
  1537.    };
  1538.  
  1539. /*
  1540. ===========
  1541. ClientObituary
  1542.  
  1543. called when a player dies
  1544. ============
  1545. */
  1546. void(entity targ, entity attacker) ClientObituary =
  1547.    {
  1548.    local float rnum;
  1549.    local string deathstring, deathstring2;
  1550.    rnum = random();
  1551.  
  1552.    if(targ.classname == "player")
  1553.       {
  1554.       if(attacker.classname == "teledeath")
  1555.          {
  1556.          bprint(targ.netname);
  1557.          bprint(" was telefragged by ");
  1558.          bprint(attacker.owner.netname);
  1559.          bprint("\n");
  1560.  
  1561. // if teamplay is on, and someone telefraged a teammate, no frag
  1562.          if(teamplay == 1) if(attacker.team != targ.team) attacker.owner.frags = attacker.owner.frags + 1;
  1563.          else attacker.owner.frags = attacker.owner.frags + 1;
  1564.  
  1565.          return;
  1566.          }
  1567.  
  1568.       if(attacker.classname == "teledeath2")
  1569.          {
  1570.          bprint("Satan's power deflects ");
  1571.          bprint(targ.netname);
  1572.          bprint("'s telefrag\n");
  1573.  
  1574.          if(teamplay == 1) if(attacker.team != targ.team) targ.frags = targ.frags - 1;
  1575.          else targ.frags = targ.frags - 1;
  1576.          return;
  1577.          }
  1578.  
  1579.       if(attacker.classname == "player")
  1580.          {
  1581. // killed self
  1582.          if(targ == attacker)
  1583.             {
  1584.             attacker.frags = attacker.frags - 1;
  1585.             bprint(targ.netname);
  1586.                 
  1587.             if(targ.weapon == 64 && targ.waterlevel > 1)
  1588.                {
  1589.                bprint(" discharges into the water.\n");
  1590.                return;
  1591.                }
  1592.             if(targ.weapon == 16) bprint(" tries to put the pin back in\n");
  1593.             else if(rnum) bprint(" becomes bored with life\n");
  1594.             else bprint(" checks if his weapon is loaded\n");
  1595.             return;
  1596.             }
  1597. // killed other
  1598.          else
  1599.             {
  1600.             if(teamplay == 1)
  1601.                {
  1602.                if(attacker.team != targ.team) attacker.frags = attacker.frags + 1;
  1603.                else
  1604.                   {
  1605.                   attacker.frags = attacker.frags - 1;
  1606.                   bprint(attacker.netname);
  1607.                   bprint(" fraged his teammate ");
  1608.                   bprint(targ.netname);
  1609.                   bprint(".\n");
  1610.                   }
  1611.                }
  1612.             else attacker.frags = attacker.frags + 1;
  1613.             rnum = attacker.weapon;
  1614.             if(rnum == IT_AXE)
  1615.                {
  1616.                deathstring = " was ax-murdered by ";
  1617.                deathstring2 = "\n";
  1618.                }
  1619.             if(rnum == IT_SHOTGUN)
  1620.                {
  1621.                deathstring = " chewed on ";
  1622.                deathstring2 = "'s boomstick\n";
  1623.                }
  1624.             if(rnum == IT_SUPER_SHOTGUN)
  1625.                {
  1626.                deathstring = " ate 2 loads of ";
  1627.                deathstring2 = "'s buckshot\n";
  1628.                }
  1629.             if(rnum == IT_NAILGUN)
  1630.                {
  1631.                deathstring = " was nailed by ";
  1632.                deathstring2 = "\n";
  1633.                }
  1634.             if(rnum == IT_SUPER_NAILGUN)
  1635.                {
  1636.                deathstring = " was punctured by ";
  1637.                deathstring2 = "\n";
  1638.                }
  1639.             if(rnum == IT_GRENADE_LAUNCHER)
  1640.                {
  1641.                deathstring = " eats ";
  1642.                deathstring2 = "'s pineapple\n";
  1643.                if(targ.health < -40)
  1644.                   {
  1645.                   deathstring = " was gibbed by ";
  1646.                   deathstring2 = "'s grenade\n";
  1647.                   }
  1648.                }
  1649.             if(rnum == IT_ROCKET_LAUNCHER)
  1650.                {
  1651.                deathstring = " rides ";
  1652.                deathstring2 = "'s rocket\n";
  1653.                if(targ.health < -40)
  1654.                   {
  1655.                   deathstring = " was gibbed by ";
  1656.                   deathstring2 = "'s rocket\n" ;
  1657.                   }
  1658.                }
  1659.             if(rnum == IT_LIGHTNING)
  1660.                {
  1661.                deathstring = " accepts ";
  1662.                if(attacker.waterlevel > 1) deathstring2 = "'s discharge\n";
  1663.                else deathstring2 = "'s shaft\n";
  1664.                }
  1665.  
  1666.             bprint(targ.netname);
  1667.             bprint(deathstring);
  1668.             bprint(attacker.netname);
  1669.             bprint(deathstring2);
  1670.             }
  1671.          return;
  1672.          }
  1673.       else
  1674.          {
  1675. // killed self
  1676.          targ.frags = targ.frags - 1;
  1677.          rnum = targ.watertype;
  1678.  
  1679.          bprint(targ.netname);
  1680.          if(rnum == -3)
  1681.             {
  1682.             if(random() < 0.5) bprint(" sleeps with the fishes\n");
  1683.             else bprint (" sucks it down\n");
  1684.             return;
  1685.             }
  1686.          else if(rnum == -4)
  1687.             {
  1688.             if(random() < 0.5) bprint(" gulped a load of slime\n");
  1689.             else bprint(" can't exist on slime alone\n");
  1690.             return;
  1691.             }
  1692.          else if(rnum == -5)
  1693.             {
  1694.             if(targ.health < -15)
  1695.                {
  1696.                bprint(" burst into flames\n");
  1697.                return;
  1698.                }
  1699.             if(random() < 0.5) bprint(" turned into hot slag\n");
  1700.             else bprint(" visits the Volcano God\n");
  1701.             return;
  1702.             }
  1703.  
  1704.          if(attacker.flags & FL_MONSTER)
  1705.             {
  1706.             if(attacker.classname == "monster_army")        bprint(" was shot by a Grunt\n");
  1707.             if(attacker.classname == "monster_demon1")      bprint(" was eviscerated by a Fiend\n");
  1708.             if(attacker.classname == "monster_dog")         bprint(" was mauled by a Rottweiler\n");
  1709.             if(attacker.classname == "monster_dragon")      bprint(" was fried by a Dragon\n");
  1710.             if(attacker.classname == "monster_enforcer")    bprint(" was blasted by an Enforcer\n");
  1711.             if(attacker.classname == "monster_fish")        bprint(" was fed to the Rotfish\n");
  1712.             if(attacker.classname == "monster_hell_knight") bprint(" was slain by a Death Knight\n");
  1713.             if(attacker.classname == "monster_knight")      bprint(" was slashed by a Knight\n");
  1714.             if(attacker.classname == "monster_ogre")        bprint(" was destroyed by an Ogre\n");
  1715.             if(attacker.classname == "monster_oldone")      bprint(" became one with Shub-Niggurath\n");
  1716.             if(attacker.classname == "monster_shalrath")    bprint(" was exploded by a Vore\n");
  1717.             if(attacker.classname == "monster_shambler")    bprint(" was smashed by a Shambler\n");
  1718.             if(attacker.classname == "monster_tarbaby")     bprint(" was slimed by a Spawn\n");
  1719.             if(attacker.classname == "monster_vomit")       bprint(" was vomited on by a Vomitus\n");
  1720.             if(attacker.classname == "monster_wizard")      bprint(" was scragged by a Scrag\n");
  1721.             if(attacker.classname == "monster_zombie")      bprint(" joins the Zombies\n");
  1722.  
  1723.             return;
  1724.             }
  1725.          if(attacker.classname == "explo_box")
  1726.             {
  1727.             bprint(" blew up\n");
  1728.             return;
  1729.             }
  1730.          if(attacker.solid == SOLID_BSP && attacker != world)
  1731.             {
  1732.             bprint(" was squished\n");
  1733.             return;
  1734.             }
  1735.          if(targ.deathtype == "falling")
  1736.             {
  1737.             targ.deathtype = "";
  1738.             bprint(" fell to his death\n");
  1739.             return;
  1740.             }
  1741.          if(attacker.classname == "trap_shooter" || attacker.classname == "trap_spikeshooter")
  1742.             {
  1743.             bprint(" was spiked\n");
  1744.             return;
  1745.             }
  1746.          if(attacker.classname == "fireball")
  1747.             {
  1748.             bprint(" ate a lavaball\n");
  1749.             return;
  1750.             }
  1751.          if(attacker.classname == "trigger_changelevel")
  1752.             {
  1753.             bprint(" tried to leave\n");
  1754.             return;
  1755.             }
  1756.  
  1757.          bprint(" died\n");
  1758.          }
  1759.       }
  1760.    };
  1761.  
  1762.