Quake DeveLS - Scanner Jammer

Author: Randy Maude, aka Sniper#1
Difficulty: Medium

Preamble
--------

My friends and I liked the scanner mod.  However, it became quickly apparent that it was being abused.  Game play would screech to a halt while players sat around watching their scanners, waiting for the other players to come around a corner.   There had to be something I could do to balance the normal game play with the abilities of the scanner.  Firstly, it had to cost players to use the scanner.   Secondly, what if each player had access to a device which jammed the scanners so they showed garbage.  I decided to charge each player 30 cells for the use of the jamming device.  The scanner would cost any player using it a cell per server frame.

Requirements
--------

You must have already installed the scanner mod.  You must also have added the qdevels.c module to the make file.

Changes
--------

Open and edit the file scanner.c.  Insert the following yellow lines into the file:

#include "g_local.h"
#include "scanner.h"

// Scanner Jammer Additions

#include "q_devels.h"

#define  JAMMER_CELLS         30       // cost in cells per jammer activation
#define  JAMMER_TIME_ON       100      // server frames the jammer is active
#define  JAMMER_TIME_OFF      50       // server frames required to recharge jammer
#define  SCANNER_CELLS        1        // cost in cells per server frame

int      ScannerJammerStartFrame;

void     InitializeJammer( void )
{
   ScannerJammerStartFrame = 0;
}

void     ActivateScanner( edict_t *ent )
{
   gitem_t *item;

   if ( !( ent->client->pers.scanner_active & 1 ) )
   {

      if ( SCANNER_CELLS == 0 )
      {
         Toggle_Scanner( ent );
      }
      else
      {
         item = FindItem( "cells" );

         if ( ent->client->pers.inventory[ ITEM_INDEX( item ) ] >= SCANNER_CELLS )
         {
            Toggle_Scanner( ent );
            ent->client->pers.inventory[ ITEM_INDEX( item ) ] -= SCANNER_CELLS;
         }

      }

   }
   else
   {
      Toggle_Scanner( ent );
   }

}

void     PowerScanner( edict_t *ent, char *string )
{
   int      i;

   edict_t *this_ent;

   gitem_t *item;

   if ( ent->client->pers.scanner_active & 1 )
   {

      if ( SCANNER_CELLS == 0 )
      {
         ShowScanner( ent, string );
      }
      else
      {
         item = FindItem( "cells" );

         if ( ent->client->pers.inventory[ ITEM_INDEX( item ) ] >= SCANNER_CELLS )
         {
            ShowScanner( ent, string );
            ent->client->pers.inventory[ ITEM_INDEX( item ) ] -= SCANNER_CELLS;
         }
         else
         {

            for ( i = 0; i < maxclients->value; i++ )
            {
               this_ent = &g_edicts[ i + 1 ];

               if ( this_ent == ent )
               {
                  ClearScanner( &game.clients[ i ] );
                  break;
               }

            }

         }

      }

   }

}

void     ActivateJammer( edict_t *ent )
{
   gitem_t *item;

   if ( ( level.framenum > ScannerJammerStartFrame + JAMMER_TIME_ON + JAMMER_TIME_OFF ) ||
        ( ScannerJammerStartFrame < 1 ) )
   {

   // see if player has enough cells

      item = FindItem( "cells" );

      if ( ent->client->pers.inventory[ ITEM_INDEX( item ) ] >= JAMMER_CELLS )
      {
         ent->client->pers.inventory[ ITEM_INDEX( item ) ] -= JAMMER_CELLS;
         ScannerJammerStartFrame = level.framenum;
      }

   }

}

// Scanner Jammer End Additions

void ClearScanner(gclient_t *client)
{
   client->pers.scanner_active = 0;
}
                 .
                 .
                 .

This creates a variable used to track when the jammer is "recharging" and when the jammer can be activated again.  There are also four functions added which are used to control the scanner and its use of cells, and the jammer, without requiring that other modules know about the variable ScannerJammerStartFrame.  The first one, InitializeScanner, sets up the starting frame so that the scanner can be activated immediately.  The second, ActivateScanner, conditionally activates the scanner, costing the player cells, or deactivates the scanner.  The third, PowerScanner, is called every server frame, and handles the power drain of the scanner on a player's cells. The fourth, ActivateJammer, conditionally activates the jamming device, checking first to make sure the player can spare enough cells.

 

                 .
                 .
                 .

void ShowScanner(edict_t *ent,char *layout)
{
   int      i;

   // Scanner Jammer End Additions
   int      sx;
   int      sy;
   int      TempNumber;
   // Scanner Jammer End Additions

   edict_t *player = g_edicts;

   char     stats[64],
            *tag;

   vec3_t v;

   // Main scanner graphic draw
   Com_sprintf (stats, sizeof(stats),"xv 80 yv 40 picn %s ", PIC_SCANNER_TAG);
   SAFE_STRCAT(layout,stats,LAYOUT_MAX_LENGTH);

   // Scanner Jammer Additions

   if ( ( level.framenum <= ScannerJammerStartFrame + JAMMER_TIME_ON ) &&
        ( ScannerJammerStartFrame > 0 ) )
   {

      for ( i = 0; i < game.maxclients; i++ )
      {
         sx = ( int ) ( 105 + rndnum( 0, SCANNER_RANGE - 1 ) );
         sy = ( int ) ( 67 + rndnum( 0, SCANNER_RANGE - 1 ) );

         // setup dot graphic

         TempNumber = rndnum( 0, 8 );

         switch( TempNumber )
         {

            case 4 :
               tag = PIC_QUADDOT_TAG;
               break;

            case 7 :
               tag = PIC_INVDOT_TAG;
               break;

            default :
               tag = PIC_DOT_TAG;

         }

         // Set output ...

         Com_sprintf( stats, sizeof( stats ), "xv %i yv %i picn %s ",
                      sx, sy, tag);

         SAFE_STRCAT( layout, stats, LAYOUT_MAX_LENGTH );

         *stats = 0;

         // set up/down arrow

         TempNumber = rndnum( 0, 8 );

         switch( TempNumber )
         {

            case 4 :

               Com_sprintf( stats, sizeof( stats ),"yv %i picn %s ",
                            sy - 5, PIC_UP_TAG);

               break;

            case 7 :

               Com_sprintf( stats, sizeof( stats ),"yv %i picn %s ",
                            sy + 5, PIC_DOWN_TAG);

               break;

         }

         if ( *stats )
         {
            SAFE_STRCAT( layout, stats, LAYOUT_MAX_LENGTH );
         }

      }

   }
   else
   {
   // Scanner Jammer End Additions
                 .
                 .
                 .

   }

The last brace must be placed at the end of the function ShowScanner, and can be either inserted before the ending brace or placed just after the closing brace.  This code outputs random noise to the scanner if the jammer is active.  The number of noise dots output equals the max number of clients set for the server.

Now open the file scanner.h and add the following lines to the end so other modules can access these functions:

// Scanner Jammer Additions
void     InitializeJammer( void );
void     ActivateScanner( edict_t *ent );
void     PowerScanner( edict_t *ent );
void     ActivateJammer( edict_t *ent );
// Scanner Jammer End Additions

Next, open the file g_cmds.c and find the following lines:

   else if ( Q_stricmp( cmd, "scanner" ) == 0 )
   {

      Toggle_Scanner(ent);
   }

and replace them with these lines:

   else if ( Q_stricmp( cmd, "scanner" ) == 0 )
   {

   // Scanner Jammer Additions
      ActivateScanner( ent );
   }
   else if ( Q_stricmp( cmd, "jammer" ) == 0 )
   {
      ActivateJammer( ent );
   }
   // Scanner Jammer End Additions

This will cause a new front-end function to be called when the scanner command is entered through the console.  It also adds recognition of the jammer command, entering "cmd jammer" from the console.

Next, open the file g_main.c and insert the following lines at the start of the function EndDMLevel:

void EndDMLevel (void)
{
   edict_t    *ent;

// Scanner Jammer Additions
   InitializeJammer();
// Scanner Jammer End Additions

   // stay on same level flag
   if ((int)dmflags->value & DF_SAME_LEVEL)
   {

This ensures that the jammer will be reset at the end of each level.  Position at the top of the file and add the following lines after the statement '#include "g_local.h"':

// Scanner Jammer Additions
#include "scanner.h"
// Scanner Jammer End Additions

Now open the file g_save.c and find the following line in the function InitGame:

      gi.dprintf ("==== InitGame ====\n");

Add the following lines immediately following the above line:

   // Scanner Jammer Additions
   InitializeJammer();
   // Scanner Jammer End Additions

This initializes the jammer when the game engine is started.  Position at the top of the file and add the following lines after the statement '#include "g_local.h"':

// Scanner Jammer Additions
#include "scanner.h"
// Scanner Jammer End Additions

Now, open the file p_hud.c and find the following lines:

   // Scanner active ?
   if (ent->client->pers.scanner_active & 1)
      ShowScanner(ent,string);

and replace them with these lines:

   // Scanner active ?
   // Scanner Jammer Additions
   PowerScanner( ent, string );
   // Scanner Jammer End Additions

This installs code which updates the scanner, and deducts the proper number of cells from the player.  If the player is out of cells, it also turns off the scanner.

That's all you need.  I hope you enjoy this mod as much as I have.

 

Tutorial by Randy Maude, aka Sniper#1.

 

This site, and all content and graphics displayed on it,
are Šopyrighted to the Quake DeveLS team. All rights received.
Got a suggestion? Comment? Question? Hate mail? Send it to us!
Oh yeah, this site is best viewed in 16 Bit or higher, with the resolution on 800*600.
Thanks to Planet Quake for their great help and support with hosting.
Best viewed with Netscape 4