(Old code tutorial) Adding a new Weapon- Spraying Shotgun from Wolf Planet forum

View previous topic View next topic Go down

(Old code tutorial) Adding a new Weapon- Spraying Shotgun from Wolf Planet forum

Post by Officer-M. John (Admin) on Tue Nov 29, 2016 9:59 am

Welcome to yet another of my tutorials. In this one, I present a new weapon- the shotgun. Along with being a usable weapon, it also possessess another special feature, but I'll explain it further down the tutorial. I will also detail adding a new ammotype for the weapon.

Credit for each part of the tutorial go to the following people: Deathshead, BrotherTank, Chris Chokan. This tutorial incorperates code from 'Working with Weapons', and is shown with the thought you are using a fresh copy of the source.

On with the Tutorial. Make backups of WL_DEF.H, WL_AGENT.C and WL_DRAW.C .

Open WL_DEF.H and add the following sprite constants:

Code:

//
// Shotgun Sprites
//
   SPR_SHOTGUN,SPR_SHELLS,
   SPR_SGUNREADY,SPR_SGUNATK1,SPR_SGUNATK2,SPR_SGUNATK3,
   SPR_SGUNATK4,

Now search for bo_chaingun:

Code:

bo_machinegun,
   bo_chaingun,
   bo_food,
   bo_fullheal,
   bo_25clip,

Add the following two lines underneath that:

Code:

   bo_shotgun,      //define shotgun pickup
   bo_shells,      //define shotgun ammo pickup

Code:

#define NUMWEAPONS   4
typedef enum   {
   wp_knife,
   wp_pistol,
   wp_machinegun,
   wp_chaingun
} weapontype;

Change that to read:

Code:

#define NUMWEAPONS   5   //there are five weapons
typedef enum   {
   wp_knife,
   wp_pistol,
   wp_machinegun,
   wp_chaingun,
   wp_shotgun      //added weapon define
} weapontype;

Go to the gamestate structure and delete the ammo variable (int ammo;). Now, underneath the structure, add:

Code:

//------------
// Ammo Structure
//------------
typedef struct
{
   char   ammo1;      //original ammo
   char    ammo2;      //new ammo for shotgun
   //char   ammo3;      //extra ammo in case you want it
   //char   ammo4;      //extra ammo in case you want it
} ammo_t;

Finally, go down to the WL_GAME DEFINITIONS, and locate this line:

Code:

extern   gametype   gamestate;

Add the following underneath it:

Code:

extern   ammo_t      ammotype;

What this ammotype code does, is make it easier to define ammo. Now, go through each WL_***.C file and change gamestate.ammo into ammotype.ammo1 .

With that done, Open up WL_DRAW.C and locate the following section of code:

Code:

int   weaponscale[NUMWEAPONS] = {SPR_KNIFEREADY,SPR_PISTOLREADY
   ,SPR_MACHINEGUNREADY,SPR_CHAINREADY};

Change it to read:

Code:

int   weaponscale[NUMWEAPONS] = {SPR_KNIFEREADY,SPR_PISTOLREADY
   ,SPR_MACHINEGUNREADY,SPR_CHAINREADY,SPR_SGUNREADY};

Here we see the addition of the SPR_SGUNREADY sprite. Now you can close that file. Open up WL_AGENT.C . This is where the main code will go. Search for this block:

Code:

struct atkinf
{
   char   tics,attack,frame;      // attack is 1 for gun, 2 for knife
} attackinfo[4][14] =

{
{ {6,0,1},{6,2,2},{6,0,3},{6,-1,4} },   //knife
{ {6,0,1},{6,1,2},{6,0,3},{6,-1,4} },   //pistol
{ {6,0,1},{6,1,2},{6,3,3},{6,-1,4} },   //machinegun
{ {6,0,1},{6,1,2},{6,4,3},{6,-1,4} },   //chaingun
};

Change it to read the following:

Code:

struct atkinf
{
   char   tics,attack,frame;      // attack is 1 for gun, 2 for knife
} attackinfo[NUMWEAPONS][14] =   //NUMWEAPONS is number of weapons, 14 is maximum frames for each weapon.

{
{ {6,0,1},{6,2,2},{6,0,3},{6,-1,4} },   //knife
{ {6,0,1},{6,1,2},{6,0,3},{6,-1,4} },   //pistol
{ {6,0,1},{6,1,2},{6,3,3},{6,-1,4} },   //machinegun
{ {6,0,1},{6,1,2},{6,4,3},{6,-1,4} },   //chaingun
{ {6,0,1},{6,1,2},{6,0,3},{6,-1,4} },   //shotgun
};

Here, we have added some of the most important weapon information. To explain what this above code does, here's an explanation by BrotherTank:


We have 5 weapon frames sprites for each weapon. In wl_draw.c it assumes that if you aren't firing to use the first weapon sprite, so this array holds the information telling it which weapon firing sprite to use, how long to display it, and what to make the program do while displaying that frame. We have 4 groups (1 for each firing sprite for each weapon) of 3 numbers or group.

So breaking this down we see that the first number in each group of 3 variables ("6" as set in each) refers to the number of tics in 70th's of a second that the graphic should be drawn. Each tic in the game is 1/70th of a second. The second number in each group of 3 variables refers to the value sent to the "T_Attack" routine (discussed below). And the last of the numbers in each group of 3 variables tells the wl_draw.c file which weapon firing frame to draw during the shooting process.

Numbers passed to the "T_Attack" routine via the second number in each group of 3 variables are as follows:

0 = Do Nothing - Player is waiting
1 = Shoot Single Shot
2 = Attack with knife - Call "KnifeAttack" routine
3 = Shoot Single Shot then go back two frames
4 = Shoot Multiple Shots - Rapid Fire
-1 = Stop firing - return to basic resting/holding weapon position

Now, I have changed [4] to [NUMWEAPONS]. Why? Because 4 is the number of weapons in WL_DEF.H, determined by the line '#define NUMWEAPONS'. So, to save time when adding more weapons in future, we make it automatically keep track of the amount of weapons.

Now for the CheckWeaponChange Routine. Here, we tell the game to see what button is pressed, then react accordingly:

Code:

/*
======================
=
= CheckWeaponChange
=
= Keys 1-4 change weapons
=
======================
*/

void CheckWeaponChange (void)
{
   int   i,buttons;

   if (!gamestate.ammo)      // must use knife with no ammo
      return;

   for (i=wp_knife ; i<=gamestate.bestweapon ; i++)
      if (buttonstate[bt_readyknife+i-wp_knife])
      {
         gamestate.weapon = gamestate.chosenweapon = i;
         DrawWeapon ();
         return;
      }
}

This is the original CheckWeaponChange Routine. Now we replace it with BrotherTank's, slightly modified:

Code:

/*==
==== CheckWeaponChange
==== Editted by BrotherTank/Deathshead
====
==== Number Keys 1-0 select Weapon.
==*/

void CheckWeaponChange (void)
{
   int   i,kbuttons[10]={sc_1,sc_2,sc_3,sc_4,sc_5,sc_6,sc_7,sc_8,sc_9,sc_0}; //ten weapon buttons

   for (i=0;i<NUMWEAPONS;i++)
      if ((Keyboard[kbuttons[i]]) && (i != gamestate.weapon))
      {          
         if (buttonstate[bt_readyknife+i-wp_knife])
         {
             gamestate.weapon = gamestate.chosenweapon = i;
              DrawWeapon ();
              DrawAmmo ();
              return;
          }
      }
}

Now, change the DrawWeapon Routine to read:

Code:

/*==
==== DrawWeapon
==== By Deathshead/BrotherTank
==*/

void DrawWeapon (void)
{
   switch (gamestate.weapon)
   {
      case   wp_knife:
         StatusDrawPic (32,8,KNIFEPIC);         //statusbar pic for knife
         break;
      case   wp_pistol:
         StatusDrawPic (32,8,GUNPIC);         //statusbar pic for pistol
         break;
      case   wp_machinegun:
         StatusDrawPic (32,8,MACHINEGUNPIC);  //statusbar pic for machinegun
         break;
      case   wp_chaingun:
         StatusDrawPic (32,8,GATLINGGUNPIC);  //statusbar pic for chaingun
         break;
      case   wp_shotgun:
         StatusDrawPic (32,8,KNIFEPIC);   //change this to what you want...
         break;
   }
}

Now, for this, we need to change the GiveWeapon routine to:

Code:

/*==
==== GiveWeapon
==== Editted by BrotherTank/Deathshead
====
==== Gives Player a weapon, with 6 ammo for that weapon.
==*/

void GiveWeapon (int weapon)
{
   switch (weapon)
   {
   case wp_pistol:
   case wp_machinegun:
   case wp_chaingun:
      GiveAmmo (wp_pistol,6);      //give 6 pistol ammo
      break;
   case wp_shotgun:
      GiveAmmo (wp_shotgun,6);   //give 6 shotgun shells
   }    

   if (gamestate.bestweapon<weapon)
      gamestate.bestweapon = gamestate.weapon = gamestate.chosenweapon = weapon;
   DrawWeapon ();
   DrawAmmo ();
}

Now, you may notice the extra stuff inside GiveAmmo. Well, it will all be made clear in a few moments. Now, the DrawAmmo can be changed to read:

Code:

/*==
==== DrawAmmo
==== Editted by Deathshead
====
==== This is another variation of the original DrawAmmo
==== routine. For each weapon, the ammo variable becomes
==== the amount of ammo for the weapon. It then displays
==== the ammo onn te statusbar
==*/

void   DrawAmmo (void)
{
   char   ammo;

   switch (gamestate.weapon)
   {
      case wp_knife:
         break;         //display no ammo for knife
      case wp_pistol:
      case wp_machinegun:
      case wp_chaingun:
         ammo = ammotype.ammo1;   //ammo becomes pistol ammo
         break;
      case wp_shotgun:
         ammo = ammotype.ammo2;   //ammo becomes shells
         break;
   }
   LatchNumber (27,16,3,ammo);      //display whatever ammo equals
}

Now, the GiveAmmo Routine. This is a modified version of the Working with Weapons routine, which allows all ammotypes to work inside it, so you don't need to create a new function for each ammo. Replace the current GiveAmmo with:

Code:

/*==
==== GiveAmmo
==== Editted by BrotherTank/Deathshead
====
==== This variation of GiveAmmo, will give
==== the player ammo for a weapon
==*/

void   GiveAmmo (int weapon,char ammo)
{
     if (!ammotype.ammo1 && !ammotype.ammo2 && !ammotype.ammo3)   // knife was out
     {
    if (!gamestate.attackframe)
    {
      gamestate.weapon = gamestate.chosenweapon;
      DrawWeapon ();
    }
     }
     switch (weapon)
     {
         case wp_pistol:
    case wp_machinegun:
    case wp_chaingun:
      ammotype.ammo1 += ammo;
      if (ammotype.ammo1 > 99) ammotype.ammo1 = 99;
      break;
    case wp_shotgun:
      ammotype.ammo2 += ammo;
      if (ammotype.ammo2 > 50) ammotype.ammo2 = 50;
      break;
     }
     DrawAmmo ();
}

Now, when calling GiveAmmo, you have to call GiveAmmo(weapon,ammo); . With this code, to give ammo to the pistol, machinegun and chaingun, you only need to write GiveAmmo (wp_pistol,ammo); . To exercise this, scroll down the GetBonus function till you find:

Code:

 case   bo_clip:
      if (ammotype.ammo1 == 99)
         return;

      SD_PlaySound (GETAMMOSND);
      GiveAmmo (8);
      break;

This is changed to:

Code:

 case   bo_clip:
      if (ammotype.ammo1 == 99)
         return;

      SD_PlaySound (GETAMMOSND);
      GiveAmmo (wp_pistol,8);
      break;

This is also to be done to bo_clip2, and bo_25clip if your spear user. Now, underneath the case for bo_clip2, add:

Code:

 case   bo_shells:
      if (ammotype.ammo2 == 50)
         return;
      SD_PlaySound (GETAMMOSND);
      GiveAmmo (wp_shotgun,5);
      break;
   case   bo_shotgun:
      GiveWeapon (wp_shotgun);
      SD_PlaySound (GETMACHINESND);
      break;

Code:

/*==
==== GunAttack
==== By Chris Chokan/Deathshead
==*/

void   GunAttack (objtype *ob)
{
   objtype *check,*closest,*oldclosest;
   int      damage;
   int      dx,dy,dist;
   int   count=0, ratio=1;
   long   viewdist;

   switch (gamestate.weapon)
   {
   case wp_pistol:
      SD_PlaySound (ATKPISTOLSND);
      break;
   case wp_machinegun:
      SD_PlaySound (ATKMACHINEGUNSND);
      break;
   case wp_chaingun:
      SD_PlaySound (ATKGATLINGSND);
      break;
   case wp_voodoogun:
      SD_PlaySound (ATKVOODOOSND);
      ratio=10;
      break;
   case wp_shotgun:
      SD_PlaySound (ATKSKULLSND);
      ratio=4;
      break;
   }

   madenoise = true;

//
// find potential targets
//
   TOP:
   viewdist = 0x7fffffffl;
   closest = NULL;

   DrawScore();

   while (1)
   {
      oldclosest = closest;

      for (check=ob->next ; check ; check=check->next)
         if ( (check->flags & FL_SHOOTABLE)
         && (check->flags & FL_VISABLE)
         && abs (check->viewx-centerx) < shootdelta*ratio
         )
         {
            if (check->transx < viewdist)
            {
               viewdist = check->transx;
               closest = check;
            }
         }

      if (closest == oldclosest)
         return; // no more targets, all missed

   //
   // trace a line from player to enemey
   //
      if (CheckLine(closest))
         break;

   }

//
// hit something
//
   dx = abs(closest->tilex - player->tilex);
   dy = abs(closest->tiley - player->tiley);
   dist = dx>dy ? dx:dy;


   if (dist<2)
      damage = US_RndT() / 4;
   else if (dist<4)
      damage = US_RndT() / 6;
   else
   {
      if ( (US_RndT() / 12) < dist)      // missed
         return;
      damage = US_RndT() / 6;
   }

   if ((gamestate.weapon == wp_shotgun) && count < 3)
   {
      DamageActor (closest,damage);
      count++;
      goto TOP;
   }
   else
      DamageActor (closest, damage);  // Original code

}

Now go down to T_Attack function and look for:

Code:

   GunAttack (ob);
   ammotype.ammo1--;
   DrawAmmo ();
   break;

Change that to:

Code:

   GunAttack (ob);
   switch (gamestate.weapon)
   {
      case wp_pistol:
      case wp_machinegun:
      case wp_chaingun:
         ammotype.ammo1--;  //if weapon=pistol,machinegun,or chaingun, take ammo1 when shooting
         break;
      case wp_shotgun:
         ammotype.ammo2--;  //if weapon=shotgun, take ammo2 when shooting
         break;
   }
   DrawAmmo ();
   break;

Now, last of all (phew!), Go to WL_ACT1.C and add the following lines underneath '{SPR_STAT_26,bo_clip2},':

Code:

{SPR_SHOTGUN,bo_shotgun},
{SPR_SHELLS,bo_shells},

That concludes the code. Open up an editor, and add the sprites in the following order (after the chaingun sprites):

Shotgun Pickup
Shells
Shotgun Weapon Sprites (for holding and attacking)

Then, in the OBJDATA.WL6 of MapEdit, add these two lines:

Code:

0048 36aa Shotgun
0049 86ab Shells

This concludes my very long tutorial on adding the Spraying Shotgun. Please post your opinions on this.

Edit: Adding more comments, changed a few mistakes. If using this, please start again.

Edit: Removed NUMBUTTONS info, as it was made apparent that it is unnecessary.

There you go
-Deathshead

Writte by Deathshead on Sun Mar 06, 2005 11:50 pm


Last edited by Officer-M. John (Admin) on Thu Dec 01, 2016 11:29 am; edited 1 time in total
avatar
Officer-M. John (Admin)
Admin

Posts : 328
Join date : 2015-10-02
Age : 19

View user profile http://wolf3dfanboys.usersboard.com

Back to top Go down

Re: (Old code tutorial) Adding a new Weapon- Spraying Shotgun from Wolf Planet forum

Post by Officer-M. John (Admin) on Tue Nov 29, 2016 10:00 am

I knew that one time arose.


Last edited by Officer-M. John (Admin) on Thu Dec 01, 2016 11:47 am; edited 1 time in total
avatar
Officer-M. John (Admin)
Admin

Posts : 328
Join date : 2015-10-02
Age : 19

View user profile http://wolf3dfanboys.usersboard.com

Back to top Go down

Re: (Old code tutorial) Adding a new Weapon- Spraying Shotgun from Wolf Planet forum

Post by Officer-M. John (Admin) on Thu Dec 01, 2016 11:12 am

If you compiled and you get error:Linker Error: Undefined Symbol _ammotype in WL_MAIN.C

You need adding in WL_GAME.C:

GLOBAL VARIABLES

This insert gametype gamestate; below

Code:

ammo_t          ammotype;

Changing:

Died

Code:

ammotype.ammo1 = STARTAMMO;
ammotype.ammo2 = 0;

NewGame

You need changing in WL_MAIN.C from gamestate.ammo to:

Code:

ammotype.ammo1 = STARTAMMO;
ammotype.ammo2 = 0;

LoadTheGame

Code:

ammotype.ammo1 = 8;
ammotype.ammo2 = 0;

If you found a bug,that on knife there is 117 and there is not as much as the ammo. Then you add this better DrawAmmo routine (I tested on SDL):

WL_AGENT.C:

DrawAmmo

Code:

void DrawAmmo (void)
{
 char  ammo;
 
    switch (gamestate.chosenweapon)
    {
      case wp_knife:
 LatchNumber (27,16,3,0);
        break;        //display no ammo for knife
      case wp_pistol:
      case wp_machinegun:
      case wp_chaingun:
        ammo = ammotype.ammo1;  //ammo becomes pistol ammo
        LatchNumber (27,16,3,ammo);
        break;
      case wp_shotgun:
        ammo = ammotype.ammo2;  //ammo becomes shells
        LatchNumber (27,16,3,ammo);
        break;
       }      
}

If you found more problem then you write for Me.
avatar
Officer-M. John (Admin)
Admin

Posts : 328
Join date : 2015-10-02
Age : 19

View user profile http://wolf3dfanboys.usersboard.com

Back to top Go down

Re: (Old code tutorial) Adding a new Weapon- Spraying Shotgun from Wolf Planet forum

Post by Sponsored content


Sponsored content


Back to top Go down

View previous topic View next topic Back to top

- Similar topics

 
Permissions in this forum:
You cannot reply to topics in this forum