Dropping acid. Dropping trou. Dropping a deuce. What do all these things have in common? They are all things that we won't learn in today's tutorial.
Dropping a weapon. That's what this lesson will cover. Specifically, we will add a feature into our mod which will enable the player or bot to drop his current weapon so that a teammate may pick it up.
We could show the individual model for each particular weapon when it is discarded, but for the sake of ease, we will use the backpack model for all of them. Technically, the dropped gun will become a backpack, adopting the backpack touch function and saving us code. Come to think of it, this is a bit more realistic, since you will also drop your current ammo with the weapon.
To know how to do this, I examined a few subroutines in the file items.qc, like weapon_touch() and BackpackTouch() (don't you just love how those filthy rich coders at id software used inconsistently named routines in QuakeC?). You can learn a lot about items and weapons in there.
Indeed, you should open it up right now, and scroll all the way to the bottom. This is where we will paste our new dropping function:
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ void(entity him) drop_current_weapon = // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ { local entity item, temp; if (him.weapon == IT_AXE || him.weapon == IT_SHOTGUN) { if (him.classname == "player") sprint(him, "you cannot drop this\n"); return; } item = spawn(); item.origin = him.origin - '0 0 24'; // naming the contents of the backpack item.items = him.weapon; if (item.items == IT_SUPER_SHOTGUN) item.netname = "Double-barrelled Shotgun"; else if (item.items == IT_NAILGUN) item.netname = "Nailgun"; else if (item.items == IT_SUPER_NAILGUN) item.netname = "Super Nailgun"; else if (item.items == IT_GRENADE_LAUNCHER) item.netname = "Grenade Launcher"; else if (item.items == IT_ROCKET_LAUNCHER) item.netname = "Rocket Launcher"; else if (item.items == IT_LIGHTNING) item.netname = "Thunderbolt"; else item.netname = ""; // giving the backpack the player's current ammo if (him.weapon == IT_SUPER_SHOTGUN) { item.ammo_shells = him.ammo_shells; him.ammo_shells = 0; } if (him.weapon == IT_SUPER_NAILGUN || him.weapon == IT_NAILGUN) { item.ammo_nails = him.ammo_nails; him.ammo_nails = 0; } if (him.weapon == IT_GRENADE_LAUNCHER || him.weapon == IT_ROCKET_LAUNCHER) { item.ammo_rockets = him.ammo_rockets; him.ammo_rockets = 0; } if (him.weapon == IT_LIGHTNING) { item.ammo_cells = him.ammo_cells; him.ammo_cells = 0; } him.currentammo = 0; him.items = him.items - him.weapon; // doing a switcheroo between "self" and "him" because the subroutines // W_SetCurrentAmmo() and bot_set_currentammo refer to a "self" entity temp = self; self = him; if (self.classname == "player") { self.weapon = W_BestWeapon (); W_SetCurrentAmmo(); } else { self.weapon = bot_bestweapon(); bot_set_currentammo(); } self = temp; // throwing the backpack and giving it a shape item.velocity_z = 300; item.velocity_x = -100 + (random() * 200); item.velocity_y = -100 + (random() * 200); item.flags = FL_ITEM; item.solid = SOLID_TRIGGER; item.movetype = MOVETYPE_TOSS; setmodel (item, "progs/backpack.mdl"); setsize (item, '-16 -16 0', '16 16 56'); item.touch = BackpackTouch; item.owner = him; item.attack_finished = time + 4; item.nextthink = time + 120; // remove after 2 minutes item.think = SUB_Remove; };If you are at least half awake, you will notice that this holds many similarities with id's DropBackPack() sub. Good, you're paying attention. You get a gold star. Hey, QuakeC is there for the taking; let's cut and paste as much of it as we can. It's free, so what are you worried about?
if (other == self.owner && time < self.attack_finished) return;See? The owner of the dropped backpack has to wait four seconds if he has changed his mind about what guns he wants to carry.
if (self.impulse == 150) drop_current_weapon(self);This should not make your brain hurt or anything. Type impulse 150 into the console to drop your weapon and ammo. The "self" entity here of course is the player, so everything is kosher.
/* ============= weapon_touch ============= */ float() W_BestWeapon; void() weapon_touch = { local float hadammo, best, new, old; local entity stemp; local float leave;After that crap, paste this:
if ( (self.weapon == IT_SUPER_SHOTGUN && (other.items & IT_NAILGUN) ) || (self.weapon == IT_NAILGUN && (other.items & IT_SUPER_SHOTGUN) ) || (self.weapon == IT_SUPER_NAILGUN && (other.items & IT_GRENADE_LAUNCHER) ) || (self.weapon == IT_GRENADE_LAUNCHER && (other.items & IT_SUPER_NAILGUN) ) || (self.weapon == IT_ROCKET_LAUNCHER && (other.items & IT_LIGHTNING) ) || (self.weapon == IT_LIGHTNING && (other.items & IT_ROCKET_LAUNCHER) ) ) { if (other.classname == "bot") { if (random() > 0.75) drop_current_weapon(other); other.goalentity = world; self.search_time = time + 30; } else sprint(other, "you cannot carry this weapon\n"); return; }Yes, that is one long and ugly if/then statement. Unfortunately, but needed. If a player of bot touches a certain weapon and he owns a certain other one, we exit the subroutine, prohibiting him from picking it up. If it is a bot, we will tell him to "decide" randomly to drop his current gun 25 percent of the time, then we will clear his goal entity. Recall that the bot touching the weapon is the "other" entity, so we pass "other" along to our drop_current_weapon() routine.
void() bot_set_currentammo; void(entity him) drop_current_weapon; float() bot_bestweapon;These are all subroutines that will be called befeore they appear in code, therefore we must declare them first. That's it.