Chargeable weapons
Chargeable weapons
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?
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...
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...
The problem I'm having is detecting the release of the attack button.
I've put an 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.
I've put an
Code: Select all
if (!self.button0)Hand grenades are working as a seperate weapon (as in Quake II) so doing it with impulse commands isn't an issue.
-
Dr. Shadowborg
- InsideQC Staff
- Posts: 1120
- Joined: Sat Oct 16, 2004 3:34 pm
Grenade Tutorial
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
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
In GrenadeTouch change: if (other.takedamage == DAMAGE_AIM)
to:
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 ="
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.
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".
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.
Basicaly a rehash of ThrowGrenade. Look at the way Quake handles nails and supernails to see how this could be done more efficiently.
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).
This goes in W_Attack anywhere that seems sensible.
This goes in W_ChangeWeapon before the grenade launcher.
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.
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.
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.
And finally put this after the Lightning Gun obit.
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;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"))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;
};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;
};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;
};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;
};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();
};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;
}Code: Select all
else if (self.weapon == HAND_GRENADES)
{
PrimeGrenade();
}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;
}
}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;
}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();
}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;
}Code: Select all
if (rnum == HAND_GRENADES)
{
deathstring = " was blown away by ";
deathstring2 = "'s hand grenade\n";
}