View Full Version: [Tutorial]Adding a new Weapon- Spraying Shotgun

wolfers >>Coding Alliance >>[Tutorial]Adding a new Weapon- Spraying Shotgun


<< Prev | Next >>

Deathshead- 03-06-2005
[Tutorial]Adding a new Weapon- Spraying Shotgun
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: // // Shotgun Sprites // SPR_SHOTGUN,SPR_SHELLS, SPR_SGUNREADY,SPR_SGUNATK1,SPR_SGUNATK2,SPR_SGUNATK3, SPR_SGUNATK4, Now search for bo_chaingun: bo_machinegun, bo_chaingun, bo_food, bo_fullheal, bo_25clip, Add the following two lines underneath that: bo_shotgun, //define shotgun pickup bo_shells, //define shotgun ammo pickup search for 'NUMBUTTONS'. You will find: #define NUMBUTTONS 8 enum { bt_nobutton=-1, bt_attack=0, bt_strafe, bt_run, bt_use, bt_readyknife, bt_readypistol, bt_readymachinegun, bt_readychaingun }; Change that to read: #define NUMBUTTONS 9 enum { bt_nobutton=-1, bt_attack=0, bt_strafe, bt_run, bt_use, bt_readyknife, bt_readypistol, bt_readymachinegun, bt_readychaingun, bt_readyshotgun }; This is added, so as to tell the engine what button is to be pressed (1-9) for each weapon. Swapping the weapons (bt_ready***) should change the order weapons are accessed. #define NUMWEAPONS 4 typedef enum { wp_knife, wp_pistol, wp_machinegun, wp_chaingun } weapontype; Change that to read: #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: //------------ // 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: extern gametype gamestate; Add the following underneath it: 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: int weaponscale[NUMWEAPONS] = {SPR_KNIFEREADY,SPR_PISTOLREADY ,SPR_MACHINEGUNREADY,SPR_CHAINREADY}; Change it to read: 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: 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: 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 . 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: /* ====================== = = 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: /*== ==== 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: /*== ==== 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: /*== ==== 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: /*== ==== 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: /*== ==== 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: case bo_clip: if (ammotype.ammo1 == 99) return; SD_PlaySound (GETAMMOSND); GiveAmmo (8); break; This is changed to: 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: 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; Now, go down to the GunAttack Function. Replace it with the following: /*== ==== 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: GunAttack (ob); ammotype.ammo1--; DrawAmmo (); break; Change that to: 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},': {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: 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. There you go -Deathshead

JackaL- 03-06-2005

I cant wait to try this feature!

Andy3012- 03-12-2005

i tried this tutorial but not too a fresh and one, and their was lots of errors, and warnings, in parts that i hadn't edited, but their was some to bits that i had edited, so yeh, just though i should point that out, when i compiled wl_main.c it said that ammotype.ammo1 = START AMMO was useless or unless i was supposed to get rid of start ammo

Deathshead- 03-15-2005

OK, the errors happen, I know. I did something in the Spear of Dreams source to kill this, but haven't done this in my source. Warnings don't matter, they don't wreck the game, or stop it from compiling. Show me your NewGame routine please, then maybe I could help with that.

Andy3012- 03-15-2005

i've already deleted it from my source, as their are other more important things i need to be working right now. If theirs time left over i will add a few fancy things like this gun.

lizardcommando- 06-04-2005

Um, I tried out this tutorial, fixing stuff that would fit to my needs (I didn't add in the ammo types), and I'm still having problems with the source. I can see one of the attack frames inside the game! IE, instead of the pickup sprite, it's the gun animation as the pickup object! But I can actually pick up and use the gun! Unfortunately, when I switch to another gun, I can't switch back to the rifle. Another problem I'm having is that I automatically start with all the guns minus the rifle. I only want to start off with the knife and pistol. Um, but I do have some other questions: For the first part where mess around with the SPR_'s in WL_DEF.H (Let's use SPR_RIFLE for an example) do they have to be in the exact order? As in, do I have to add in the pick up sprites first and then the firing animation? Because this other tutorial I followed said for me to add in the firing animation first and then the pickup sprite? I had posted the link to that tutorial in my Need Help thread. EDIT: Ok, I just changed a few things around in GFXE_WL6. I changed the LATCHPICS_NUM_END and the NUMPICS values. I was following that one tutorial on making new guns from DieHard Wolfers to see how you add a custom status bar picture for the gun. Ok, after messing around with the GFXE_WL6, I compiled it and tested it, but all it does now is freeze at that loading screen. Goddammit, I'm having s****y luck with this. EDIT2: Ok, I guess following that tutorial from DHW was a very bad idea. Changing the LATCHPICS_NUM_END was what was actually messing up the game! Now, All I have to do is fix the problem with the status bar rifle pic showing up as the pickup object rather than the sprite I made and being able to switch between the guns.

Deathshead- 06-07-2005

With the attack sprite problem, you obviously have them in the wrong order in your VSWAP.

lizardcommando- 06-07-2005

Thanks. I just fixed the problem.

WLHack- 08-05-2005
Re: Adding new weapon - spraying shotgun
Can anyone tell how can I get rid of "Extra parameter in call to GiveAmmo" error?

WLHack- 08-05-2005

Forget my last message. I found the source of the error and succesfully removed it.

Forumer™ is Voted #1 Free Forum Hosting provider
Build your own community today with the largest message board hosting company.