This part of the guide utilizes Richter Belmont's code tutorial from DieHard Wolfers, which can be found here.
In WL_AGENT.C again, this time we want to go back up to the top of the file and add a definition:
// WL_AGENT.C
#include "wl_def.h"
#pragma hdrstop
extern statetype s_rocket;
/*
=============================================================================
LOCAL CONSTANTS
...
Then scroll down to the KnifeAttack function, where we will put a new function, called RocketLauncherAttack.
...
void RocketLauncherAttack (objtype *ob)
{
objtype *newobj = NULL;
newobj = GetNewActor();
if (gamestate.ammo < 1)
return;
SD_PlaySound (MISSILEFIRESND);
newobj->state = &s_rocket;
newobj->ticcount = 1;
newobj->x = ob->x ;
newobj->y = ob->y ;
newobj->tilex = newobj->x >> TILESHIFT;
newobj->tiley = newobj->y >> TILESHIFT;
newobj->obclass = rocketobj;
newobj->dir = nodir;
newobj->angle = ob->angle;
newobj->speed = 0x4200l;
newobj->flags = FL_NONMARK | FL_BONUS;
newobj->active = ac_yes;
}
/*
===============
=
= T_KnifeAttack
=
= Update player hands, and try to do damage when the proper frame is reached
=
===============
*/
void KnifeAttack (objtype *ob)
{
objtype *check,*closest;
...
This is the code that spawns a new rocket "object" in the game, and sets all its variables including speed and angle it flies. Now, we will go to the T_Attack function towards the bottom of the file, and look at the switch statement inside:
...
switch (cur->attack)
{
case -1:
ob->state = &s_player;
if (!gamestate.ammo)
{
gamestate.weapon = wp_knife;
DrawWeapon ();
}
else
{
if (gamestate.weapon != gamestate.chosenweapon)
{
gamestate.weapon = gamestate.chosenweapon;
DrawWeapon ();
}
}
gamestate.attackframe = gamestate.weaponframe = 0;
return;
case 4:
if (!gamestate.ammo)
break;
if (buttonstate[bt_attack])
gamestate.attackframe -= 2;
case 1:
if (!gamestate.ammo)
{ // can only happen with chain gun
gamestate.attackframe++;
break;
}
GunAttack (ob);
if (!ammocheat)
gamestate.ammo--;
DrawAmmo ();
break;
case 2:
KnifeAttack (ob);
break;
case 3:
if (gamestate.ammo && buttonstate[bt_attack])
gamestate.attackframe -= 2;
break;
}
...
This is where the "attack" information in the attackinfo array (from the previous page of this guide) is translated into actual actions by the game. As an example, "case 1" is the Gun Attack process, and when it is called by attackinfo, it will activate GunAttack, and remove ammo from the player.
We will want to add a new unique case for the rocket launcher:
...
case 2:
KnifeAttack (ob);
break;
case 5:
RocketLauncherAttack (ob);
if (!ammocheat)
gamestate.ammo--;
DrawAmmo ();
break;
case 3:
if (gamestate.ammo && buttonstate[bt_attack])
...
Now, we go back up to the attackinfo array at the top of the file:
...
} attackinfo[NUMWEAPONS][14] =
{
{ {6,0,1},{6,2,2},{6,0,3},{6,-1,4} },
{ {6,0,1},{6,1,2},{6,0,3},{6,-1,4} },
{ {6,0,1},{6,1,2},{6,3,3},{6,-1,4} },
{ {6,0,1},{6,1,2},{6,4,3},{6,-1,4} },
{ {6,0,1},{6,1,2},{6,0,3},{6,-1,4} }, // Rocket Launcher
};
...
Right now we still have the rocket launcher set to an attack value of "1", which is the Gun Attack. Now that we have a new attack type specifically for the rocket launcher, we'll want to change it to call that one instead.
...
} attackinfo[NUMWEAPONS][14] =
{
{ {6,0,1},{6,2,2},{6,0,3},{6,-1,4} },
{ {6,0,1},{6,1,2},{6,0,3},{6,-1,4} },
{ {6,0,1},{6,1,2},{6,3,3},{6,-1,4} },
{ {6,0,1},{6,1,2},{6,4,3},{6,-1,4} },
{ {6,0,1},{6,5,2},{6,0,3},{6,-1,4} }, // Rocket Launcher
};
...
The last changes we need to make are in a different file, WL_ACT2.C. This is the file that contains most of the information about non-player "actors" (enemies, projectiles).
At the top of the file, add the following line under LOCAL CONSTANTS:
...
LOCAL CONSTANTS
=============================================================================
*/
#define PROJECTILESIZE 0xc000l
#define BJRUNSPEED 2048
#define BJJUMPSPEED 680
#define BJROCKETSIZE 0x6000l
...
Then locate the T_Projectile function further down, replacing it in it's entirity with the following:
void T_Projectile (objtype *ob)
{
long deltax,deltay;
int damage;
long speed;
objtype *check, *extracheck;
speed = (long)ob->speed*tics;
deltax = FixedMul(speed,costable[ob->angle]);
deltay = -FixedMul(speed,sintable[ob->angle]);
if (deltax>0x10000l)
deltax = 0x10000l;
if (deltay>0x10000l)
deltay = 0x10000l;
ob->x += deltax;
ob->y += deltay;
if (!ProjectileTryMove (ob))
{
#ifndef APOGEE_1_0 // actually the whole method is never reached in shareware 1.0
if (ob->obclass == rocketobj)
{
PlaySoundLocActor(MISSILEHITSND,ob);
ob->state = &s_boom1;
}
#ifdef SPEAR
else if (ob->obclass == hrocketobj)
{
PlaySoundLocActor(MISSILEHITSND,ob);
ob->state = &s_hboom1;
}
#endif
else
#endif
ob->state = NULL; // mark for removal
return;
}
if (ob->obclass == rocketobj && ob->flags & FL_NONMARK && ob->flags & FL_BONUS)
{
check = objlist ;
while (check)
{
if (check->flags & FL_SHOOTABLE)
{
deltax = LABS(ob->x - check->x);
deltay = LABS(ob->y - check->y);
if (deltax < BJROCKETSIZE && deltay < BJROCKETSIZE)
{
if (check->obclass != angelobj)
// PlaySoundLocActor(MISSILEHITSND,ob);
ob->state = &s_boom1;
DamageActor (check, 150);
}
}
check = check->next ;
}
}
else
{
{
// Check if player hit by anything
deltax = LABS(ob->x - player->x);
deltay = LABS(ob->y - player->y);
if (deltax < PROJECTILESIZE && deltay < PROJECTILESIZE)
{ // hit the player
switch (ob->obclass)
{
case needleobj:
damage = (US_RndT() >>3) + 20;
break;
case rocketobj:
case hrocketobj:
// case sparkobj:
damage = (US_RndT() >>3) + 30;
break;
#ifdef SPEAR
case sparkobj:
damage = (US_RndT() >> 3) + 30;
break;
#endif
case fireobj:
damage = (US_RndT() >>3);
break;
}
TakeDamage (damage,ob);
ob->state = NULL; // mark for removal
return;
}
}
ob->tilex = ob->x >> TILESHIFT;
ob->tiley = ob->y >> TILESHIFT;
}
}
This new projectile code is mostly the same, but adds a new check for the player's rocket (BJROCKETSIZE), which is contained in this specific part of the new function:
if (ob->obclass == rocketobj && ob->flags & FL_NONMARK && ob->flags & FL_BONUS)
{
check = objlist ;
while (check)
{
if (check->flags & FL_SHOOTABLE)
{
deltax = LABS(ob->x - check->x);
deltay = LABS(ob->y - check->y);
if (deltax < BJROCKETSIZE && deltay < BJROCKETSIZE)
{
if (check->obclass != angelobj)
// PlaySoundLocActor(MISSILEHITSND,ob);
ob->state = &s_boom1;
DamageActor (check, 150);
}
}
check = check->next ;
}
}
This section of the code simplistically translates to:
- If the rocket belongs to the player, and
- hits an enemy, then
- explode and deal 150 damage to the enemy.
Provided you have followed this guide correctly thus far, you should be able to compile your project successfully, and have a new weapon for players to use in your game!