Forum

Chargeable weapons

Discuss programming in the QuakeC language.

Moderator: InsideQC Admins

Chargeable weapons

Postby Senban » Sat Jul 07, 2007 3:04 am

I am working on code for hand grenades for use in my mod (SuperCoop). I want to make holding down the attack button charge up the power of the throw and releasing the attack button launch the grenade (think Worms). Does anyone have any ideas how to do this, or can anyone think of a weapon mod that does something similar with .qc files that I could look at?
User avatar
Senban
 
Posts: 19
Joined: Sat Apr 14, 2007 8:04 pm

Postby Lardarse » Sat Jul 07, 2007 11:11 am

Lets try something like this:

Check for self.button0 being pressed. If it is, set a flag, and then set a variable to the current time.

While the flag is set, dheck for the button being released instead of being pressed. When it is relieased, time-that variable we created contains the length of time that the button was pressed for.

I don't know how to miodify this to throw the grenade using an impulse, though...
User avatar
Lardarse
 
Posts: 266
Joined: Sat Nov 05, 2005 1:58 pm
Location: Bristol, UK

Postby Senban » Sat Jul 07, 2007 2:56 pm

The problem I'm having is detecting the release of the attack button.
I've put an
Code: Select all
if (!self.button0)
case in the weapon frame function but it dosn't seem to be picking up the buttons release.
Hand grenades are working as a seperate weapon (as in Quake II) so doing it with impulse commands isn't an issue.
User avatar
Senban
 
Posts: 19
Joined: Sat Apr 14, 2007 8:04 pm

Postby Dr. Shadowborg » Sat Jul 07, 2007 4:05 pm

Have a look at player_nail1() in player.qc. (i.e. it goes to player_nail2() then loops back to player_nail1() again.)

That should give you a few ideas. Also, you may want to put a check in w_setcurrentammo() in weapons.qc so that it doesn't reset your weaponframe to 0 when you pick up an item.
User avatar
Dr. Shadowborg
InsideQC Staff
 
Posts: 1110
Joined: Sat Oct 16, 2004 3:34 pm

Postby Senban » Sun Jul 08, 2007 8:38 am

Thanks guys, I've got it working now. When I get the fine tuning done I'll post the code.
User avatar
Senban
 
Posts: 19
Joined: Sat Apr 14, 2007 8:04 pm

Grenade Tutorial

Postby Senban » Sat Jul 28, 2007 6:15 pm

This gives you a unique and entertaining secondary weapon. You select it by pressing button 6 twice (or once if you don't have the grenade launcher yet.)
Unlike quakes other weapons holding the fire button down charges the power of the shot and releasing it lobs (or simply drops, depending on how long you charged it up for) the grenade. To make things even more fun if you launch the grenade within arms reach of your target you will attach the grenade to them.

Step 1. Name rank and number.
DEFS.QC
Open defs.qc and make these the last two lines

Code: Select all
float HAND_GRENADES = 3;
.float charge;


Setting HAND_GRENADES to a non binary number means that it doesn't take up a precious item slot, which it doesn't need because you won't be doing any self.items checks with it.

Step 2. Meat and Veg.
WEAPONS.QC

Add this to the precached sounds list at the top of weapons.qc

Code: Select all
precache_sound ("buttons/switch04.wav");


In GrenadeTouch change: if (other.takedamage == DAMAGE_AIM)
to:
Code: Select all
if ((other.takedamage == DAMAGE_AIM) && (self.classname != "hand_grenade"))


This is just to disable the grenade launchers ability to detonate when it hits a monster.

The next set of functions goes in weapons.qc, after "void() superspike_touch ="

Code: Select all
//=============
//HAND GRENADES
//=============
void() PrimeGrenade =
{
   if (self.charge == 0)
      sound (self, CHAN_WEAPON, "player/axhit2.wav", 0.5, ATTN_NORM);
   
   if (self.charge >= 8)
   {
      self.charge = 8;
      self.punchangle_x = -2;
   }
   else
   {
      self.charge = self.charge + 1;
      sprint (self, "*");
   }
   self.attack_finished = time + 0.1;
};

This pulls the pin out with a nice "tink" and starts powering up for the throw.
The sprint gives the player a rudimentary charge up meter.
Code: Select all
void() ThrowGrenade =
{
   local   entity missile, mpuff;
   
   self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;
   
   sound (self, CHAN_BODY, "player/plyrjmp8.wav", 1, ATTN_NORM);

   missile = spawn ();
   missile.owner = self;
   missile.movetype = MOVETYPE_BOUNCE;
   missile.solid = SOLID_BBOX;   
      
// set missile speed   

   makevectors (self.v_angle);
      
        missile.velocity = v_forward*65 + v_up * 21.5 + crandom()*v_right*3 + crandom()*v_up*3;
   missile.velocity = missile.velocity * self.charge;
   
   missile.avelocity = '-50 -5 -5';
   missile.avelocity = missile.avelocity * self.charge;
   
   missile.angles = vectoangles(missile.velocity);
   
   missile.classname = "hand_grenade";
   missile.touch = GrenadeTouch;
   
// set missile duration
   missile.nextthink = time + 3;
   missile.think = GrenadeExplode;
   
   setmodel (missile, "progs/grenade.mdl");
   setsize (missile, '0 0 0', '0 0 0');      
   setorigin (missile, self.origin);

   self.charge = 0;
   sprint (self, "\n");
   self.attack_finished = time + 0.4;   
};

This is basically the grenade launcher code but with a different sound and modified velocity and fuse time. The velocity code is the interesting part here.
The sprint ends the charge up "meter".

Code: Select all
void() GrenadeStick =
{
   self.nextthink = time + 0.1;
   self.ammo_cells = self.ammo_cells - 1;
   
   if (self.ammo_cells < 1)
        GrenadeExplode();
   
   self.velocity = self.enemy.velocity;
      self.angles = self.enemy.angles;
      self.origin = self.enemy.origin;
};

This bit owes a certain ammount to Homing Sticky Missiles v0.9 by Jonathan Avraham. The grenade is given .ammo_cells to use as a fuse. When the battery runs out the grenade explodes.

Code: Select all
void(entity victim) PlaceGrenade =
{
   local   entity missile, mpuff;   

   self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;
   
   sound (victim, CHAN_BODY, "buttons/switch04.wav", 1, ATTN_NORM);
   
   missile = spawn ();
   missile.owner = self;
   missile.movetype = MOVETYPE_NOCLIP;
   missile.solid = SOLID_BBOX;
   
   missile.classname = "hand_grenade";
      
// set missile duration
   missile.nextthink = time + 0.1;
   missile.ammo_cells = 30;
   missile.think = GrenadeStick;
   missile.enemy = victim;   

   setmodel (missile, "progs/grenade.mdl");
   setsize (missile, '0 0 0', '0 0 0');      
   setorigin (missile, victim.origin);

   sprint (self, "\nIt's better to give than to recieve!\n");
   self.charge = 0;   
   self.attack_finished = time + 0.4;   
};

Basicaly a rehash of ThrowGrenade. Look at the way Quake handles nails and supernails to see how this could be done more efficiently.

Code: Select all
void() HandGrenade =
{
   local   vector   source;
   local   vector   org;

   makevectors (self.v_angle);
   source = self.origin + '0 0 16';
   traceline (source, source + v_forward*64, FALSE, self);
   if (trace_fraction == 1.0)
   {   
      ThrowGrenade();
      return;
   }
   org = trace_endpos - v_forward*4;   

   if ((trace_ent.flags == trace_ent.flags | FL_MONSTER) || (trace_ent.classname == "player"))   
      PlaceGrenade(trace_ent);
   else
      ThrowGrenade();
};

Simply determines whether grenade will be thrown or placed, based on the axe code.

Next add this to W_SetCurrentAmmo (I put it before the grenade launcher but you don't have to).
Code: Select all
   else if (self.weapon == HAND_GRENADES)
   {
      self.currentammo = self.ammo_rockets;
      self.weaponmodel = "";
      self.weaponframe = 0;
      self.items = self.items | IT_ROCKETS;
   }


This goes in W_Attack anywhere that seems sensible.
Code: Select all
   else if (self.weapon == HAND_GRENADES)
   {      
      PrimeGrenade();      
   }


This goes in W_ChangeWeapon before the grenade launcher.
Code: Select all
   else if ((self.impulse == 6) && (self.items != self.items | IT_GRENADE_LAUNCHER || self.weapon == IT_GRENADE_LAUNCHER))
   {
      if (self.ammo_rockets < 1)
         am = 1;
      else
      {
         self.weapon = HAND_GRENADES;
         self.charge = 0;      
         W_SetCurrentAmmo ();
         return;
      }
   }

This is one of the most important parts of the code as it allows you to select something without checking your items, it's also important to reset self.charge at this point or players would be able to carry charged grenades about with them which is silly.

These bits go between the grenade launcher and the super nailgun in CycleWeaponCommand and CycleWeaponReverseCommand respectively.
Code: Select all
      else if (self.weapon == HAND_GRENADES)
      {
         self.weapon = IT_GRENADE_LAUNCHER;
         if (self.ammo_rockets < 1)
            am = 1;
      }

Code: Select all
      else if (self.weapon == HAND_GRENADES)
      {
         self.weapon = IT_SUPER_NAILGUN;
         if (self.ammo_nails < 2)
            am = 1;
      }

This code does not allow you to select hand grenades by scrolling, but it means you can use weapon scrolling to switch to a different weapon from the grenades.

Step 3. Tidy up and got to bed.
CLIENT.QC

Put this in PlayerPreThink between the death code and the jump code.
Code: Select all
   if (self.weapon == HAND_GRENADES && self.charge > 0 && !self.button0)
   {
      player_rocket1();      
      HandGrenade();
   }

This is the most important piece of code as it let's the player perform an action by releasing the button rather than by pressing it.

Replace the grenade launcher in ClientObituary obit with this lot.
Code: Select all
            if (targ.weapon == IT_GRENADE_LAUNCHER)
            {
               bprint (" tries to put the pin back in\n");
               return;
            }
            if (targ.weapon == HAND_GRENADES)
            {
               bprint (" forgot where he put his hand grenade\n");
               return;
            }

And finally put this after the Lightning Gun obit.
Code: Select all
            if (rnum == HAND_GRENADES)
            {      
               deathstring = " was blown away by ";
               deathstring2 = "'s hand grenade\n";
            }
User avatar
Senban
 
Posts: 19
Joined: Sat Apr 14, 2007 8:04 pm


Return to QuakeC Programming

Who is online

Users browsing this forum: No registered users and 1 guest