Inside3D tutorials.
Created By: Pob the Impaler (Paul Johnstone)
eMail: itac025@lauder.ac.uk
Difficulty Scale: Medium




Yipee the RIPs are back...

Legion (Roscoe Sincero) has buggered off to work on his ElimaReaper patch, so I'll be filling his shoes for a while.
He is still active in a consultancy capacity, so I can still draw on his wealth of knowledge for support.

So, my name is Pob the Impaler (Paul Johnstone), you can send me mail at:
A.W.Johnstone@btinternet.com (for home)
OR
itac025@lauder.ac.uk (for College)
I don't mind which address you use, so when I inevitably make a boo-boo you'll know how to send me
hate-mail :)
My site is at: http://www.btinternet.com/~impaler please help my hit-counter and visit!

Roscoe Sincero's address is legion@keg.zymurgy.org

//------------//
//-Credits--//
//-----------//

Steve Polge - For the Reaper bots
Alan Kivlin - For the clever scoreboard stuff
Lee Smith - For ProQCC
Roscoe Sincero - For starting it all off and for giving me a shot of his ElimaReaper!


|=========================================================|

ALWAYS MAKE A BACKUP OF YOUR CODE BEFORE TRYING ONE OF MY LESSONS. IF THERE IS A MISTAKE IN IT WHICH RUINS YOUR CODE OR BLOWS UP YOUR HOUSE, TOUGH SHIT, RESTORE THE BACKUP AND TELL ME WHERE I FUCKED UP.

Note that this is like a "quickie" lesson until I get my stuff sorted out Today we are going to add a new deathmatch mode
- DM5 - "Last Man Standing" BTW before you start mailing me saying "RAzOR Entertainment have already done this", think again, 'cos I invented it, coded it and made up the name first, this has already been sorted out with them, so don't go bugging them either.
Their version is for QuakeWorld and this is for the bots ok? Good... And if you want to put this mode in any of your mods please put me in the credits or I'll stick you in my dangerous chamber and jab you with my pointy stick until you cry.

Right.. Here goes...


Basic Premise:
Every player and bot starts of with a number of frags equal to fraglimit, these frags can be thought of as "lives".

When you get killed, you lose a frag.
You do not gain a frag for killing someone.
You do not lose a frag for being telefragged
If you hit "0" frags you are "out" and made an observer
If a bot hits "0" frags it is out and does not respawn
The last person to have more than "0" frags wins!

easy peasy :)


Step 1

botspawn.qc

open botspawn.qc

under:
void () AddAnotherBot;

add:
//------Begin Code-------//
void () GetWinner;
//------End Code-------//


Step 2
At the bottom of botspawn.qc add:

//------Begin Code-------//
void () GetWinner =
{
// The 2 remmed out dprints are just debug checks, delete them if you want.
local entity p;
local entity b;

p = find (world,classname,"player");
if (p)
{
//dprint("I found a player.\n");
bprint("\n");
bprint(p.netname);
bprint(" is the Last Man Standing!");
bprint("\n");



nextmap = mapname;
execute_changelevel ();
return;
}

b = find (world,classname,"dmbot");
if (b)
{
//dprint("I found a bot");
bprint("\n");
bprint(b.netname);
bprint(" is the Last Man Standing!");
bprint("\n");


nextmap = mapname;
execute_changelevel ();
return;
}
};
//------End Code-------//


Step 3

find the function respawnbot...  for void () respawnBot = {
Ok my original code for this is jumbled up so much that I can't remember what I had changed..
So.. either figure out the differences or cut & paste the whole function over your original.

//------Begin Code-------//
void () respawnBot = {

   local string m;
   local float n;
   local float o;
   local entity e;

   
   CopyToBodyQue (self);
   if ( !teamplay ) {

      if ( (serverflags & TEAMBOTS) ) {

         n = (NUMBOTS + numplayers ());
         o = ((serverflags & TEAMBOTS) / FL_ITEM);
         if ( ((o < n) || (self.team != 99.000)) ) {

            self.nextthink = (time + TRUE);
            if ( (self.team == 99.000) ) {

               NUMBOTS = (NUMBOTS - TRUE);

            }
            remove (self.movetarget);
            remove (self.goalentity);
            self.think = SUB_Remove;
            if ( self.pather ) {

               NUMPATHERS = (NUMPATHERS - TRUE);

            }
            if ( (self.team != 99.000) ) {

               m = ": I'm going to find a team server.\n";

            } else {

               if ( (totalscore (self) < FALSE) ) {

                  m = ": I'm tired of getting fragged.  I'm off\n";

               } else {

                  if ( (totalscore (self) > MUST_HAVE) ) {

                     m = ": I'm going to find some real competition\n";

                  } else {

                     m = ": This server's too crowded.  I'm off\n";

                  }

               }

            }
            bprint (self.teamname);
            bprint (self.netname);
            bprint (m);
            bprint (self.teamname);
            bprint (self.netname);
            bprint (" left the game with ");
            m = ftos (self.frags);
            bprint (m);
            bprint (" frags\n");
            return ;

         }

      }

   } else {

      if ( (self.team != 99.000) ) {

         e = find (world,classname,"player");
         while ( e ) {

            if ( (e.team == self.team) ) {
if ((deathmatch == 5) && (self.frags <= 0)) // if DM 5 rules, bot is "out"
{

bprint(self.netname);
bprint(" is out!");
bprint("\n");

               
                        standing = (standing + 1);

n = ( NUMBOTS + numplayers () );
if (standing == (n - 1))

{
GetWinner();
}  

            remove (self.movetarget);
            remove (self.goalentity);
            self.think = SUB_Remove;   
if ( self.pather ) {

      NUMPATHERS = (NUMPATHERS - TRUE);
      self.pather = FALSE;

   }


  msgUpdateNameToAll( self.fClientNo, self.netname );
  msgUpdateColorsToAll( self.fClientNo, self.fShirt, self.fPants ); // these 3 line, bot rankings
  msgUpdateFragsToAll( self.fClientNo, self.frags );

return;
}
// end check
               PutBotInServer ();
               return ;

            }
            e = find (e,classname,"player");

         }
         bprint (self.teamname);
         bprint (self.netname);
         bprint (": Going off to join my team\n");
         bprint (self.teamname);
         bprint (self.netname);
         bprint (" left the game with ");
         m = ftos (self.frags);
         bprint (m);
         bprint (" frags\n");
         return ;

      } else {

         if ( (SKINSMODE == FL_SWIM) ) {

            skinChange (self,15.000);

         }

      }

   }

if ((deathmatch == 5) && (self.frags <= 0)) // if DM 5 rules, bot is "out"
{

bprint(self.netname);
bprint(" is out!");
bprint("\n");

             
                        standing = (standing + 1);

  

n = ( NUMBOTS + numplayers () );
if (standing == (n - 1))
{
GetWinner();
}

            remove (self.movetarget);
            remove (self.goalentity);
            self.think = SUB_Remove;   
if ( self.pather ) {

      NUMPATHERS = (NUMPATHERS - TRUE);
      self.pather = FALSE;

   }


  msgUpdateNameToAll( self.fClientNo, self.netname );
  msgUpdateColorsToAll( self.fClientNo, self.fShirt, self.fPants ); // these 3 line, bot rankings
  msgUpdateFragsToAll( self.fClientNo, self.frags );

return;
}
   PutBotInServer ();

};
//------End Code-------//


Step 4

Find the function AddAnotherBot,  void () AddAnotherBot = {

At the VERY end of that function - after the last lone "}" but before the "};" add:
//------Begin Code-------//
//Initialise the bots ranking data and scores
if (deathmatch == 5)  //If using DM 5 rules, give 10 frags to start with
{
local float fragmax;
fragmax = cvar("fraglimit");

if (fragmax == 0)
        {
        newbot.frags = 10;
        }
        else
        {
        newbot.frags = (fragmax - 1);
        }

}
  msgUpdateNameToAll( newbot.fClientNo, newbot.netname );
  msgUpdateColorsToAll( newbot.fClientNo, newbot.fShirt, newbot.fPants ); // these 3 line, bot rankings
  msgUpdateFragsToAll( newbot.fClientNo, newbot.frags );
//------End Code-------//


Step 5

Find the function addTeamBots  for void (entity ply, float n) addTeamBots = {
find the last reference to newbot.teamname near the end,
now after that, and before:
i = (i - TRUE); 

add:
//------Begin Code-------//
 if (deathmatch == 5)  //If using DM 5 rules, give 10 frags to start with
{

local float fragmax;
fragmax = cvar("fraglimit");

if (fragmax == 0)
        {
        newbot.frags = 10;
        }
        else
        {
        newbot.frags = (fragmax - 1);
        }

}

  msgUpdateNameToAll( newbot.fClientNo, newbot.netname );
  msgUpdateColorsToAll( newbot.fClientNo, newbot.fShirt, newbot.fPants ); // these 3 line, bot rankings
  msgUpdateFragsToAll( newbot.fClientNo, newbot.frags );


//------End Code-------//

Save botspawn.qc


Step 6

Open defs.qc
At the very bottom add:

//------Begin Code-------//
float standing;
//------End Code-------//

Save defs.qc

That was a quick one!  :)


Step 7

Right then... open up client.qc
find the function PutClientInServer  void () PutClientInServer = {
After:
   self.view_ofs = '0.000 0.000 22.000';
and Before:
   if ( (self.movetarget == world) ) {
Add:

//------Begin Code-------//
 local float n;
if ((deathmatch == 5) && (self.frags == 0))
{

bprint(self.netname);
bprint(" is out!");
bprint("\n");
Peeper();
dprint(ftos(standing));
dprint(ftos(n));
                        standing = (standing + 1);
//if (standing == (fActiveClients - 2))
n = (NUMBOTS + numplayers() );
if (standing == (n - 1))
{
GetWinner();
}  

Peeper();
}
//------End Code-------//


Step 8

Don't worry nearly there...
find the function ClientConnect  void () ClientConnect = {
just before it says:
        initscore ();
add:
//------Begin Code-------//
if (deathmatch == 5) // if DM 5 rules are running give 10 frags
{
local float fragmax;
fragmax = cvar("fraglimit");
        if (fragmax == 0)
        {
        self.frags = 10;
        }
        else
        {
        self.frags = (fragmax - 1);
        }
}

//------End Code-------//


Step 9

This one is a wee bit fiddly and I may have made a couple of muck ups here.
Find ClientObituary  void (entity targ, entity attacker) ClientObituary = {

go down until you come to:

         if ( (attacker.owner.classname == "dmbot") ) {

            TELEFRAGFLAG = TRUE;

         }
beneath this add:

//------Begin Code-------//
if (deathmatch == 5)
{
attacker.owner.frags = (attacker.owner.frags + 0);
}
else
{
         attacker.owner.frags = (attacker.owner.frags + TRUE);
} 
        countkill (attacker.owner,targ);
if (attacker.owner.classname == "dmbot")      // Bot rankings
{
//msgUpdateFragsToPlayer( self, attacker.owner.fClientNo, attacker.owner.frags );
msgUpdateFragsToAll( attacker.owner.fClientNo, attacker.owner.frags );
}

//------End Code-------//

Now go down again until you find:
if ( rnum ) {

                  bprint (" becomes bored with life\n");

               } else {

                  bprint (" checks if his weapon is loaded\n");

               }

            }
            return ;

         } else {

There should be something like:
attacker.frags = (attacker.frags + TRUE);

Then:

countkill (attacker,targ);
            rnum = attacker.weapon;

You need to delete everything between the:
           } else {
and the:
countkill (attacker,targ);

Now in that space between the } else { and countkill (attacker,targ);
add:

//------Begin Code-------//

// Deathmatch 5 mod. If deathmatch 5 victim loses a frag
if (deathmatch == 5)
{
//               dprint("Deathmatch is 5");
targ.frags = (targ.frags - 1);
if (targ.classname == "dmbot")      // Bot rankings
{
//msgUpdateFragsToPlayer( self, targ.fClientNo, targ.frags );
 msgUpdateFragsToAll( targ.fClientNo, targ.frags );
}
}
// Deathmatch 5 mod end block


// ********* If not deathmatch 5 killer gets a frag
else if (deathmatch == 1)
{
//               dprint("Deathmatch is 1");
attacker.frags = (attacker.frags + TRUE);
if (attacker.classname == "dmbot")      // Bot rankings
{
//msgUpdateFragsToPlayer( self, attacker.fClientNo, attacker.frags );
 msgUpdateFragsToAll( attacker.fClientNo, attacker.frags );
}
}
// ********* end NOT DM 5 block

//------End Code-------//

I hope you managed to follow that!
This is an object lesson in remembering to comment your code just like I don't  :)
After writing this RIP I swear I'll start putting comments in.


UPDATE

client.qc

//==================================================//
I forgot to do this... thanX to everyone who e-mailed me to slag me off
about this!
At the top of client.qc you must add:

//====Begin code======//
void () Peeper;   // need this for DM 5 rules

void () GetWinner; // DM 5 rules

float () numplayers;
//====end code======//
Sorry!
//==================================================//

I think that's it! There is a new teamplay mode in the works called "Frag the Barrel", so watch this space! Also coming is ejecting ammo cases for the shotguns, nailguns and rocket launcher. Oh and a lesson on adding new weapons to the reaper bots! Stay tuned.