1181 lines
40 KiB
Plaintext
1181 lines
40 KiB
Plaintext
/*======================================================================
|
|
PLAYER ONLY WEAPON FUNCTIONS
|
|
|
|
void() W_Precache; - moved to world.qc
|
|
float() crandom; - moved to subs.qc
|
|
vector() wall_velocity - merged into spawn_touchblood (only used once)
|
|
void(vector org, vector vel) SpawnChunk - Not used anywhere, removed
|
|
|
|
W_SetCurrentAmmo - Update HUD icons based on self.weapon value
|
|
W_BestWeapon - returns best weapon LG > SNG > SSG > NG > SG > Axe
|
|
interesting that the function does not return GL/RL
|
|
W_CheckNoAmmo - Ammo check for attack function
|
|
W_Attack - Main function that fire weapons (player animations)
|
|
Will automatically wakeup any nearby monsters
|
|
W_ChangeWeapon - Change weapon based on availabilty and ammo
|
|
CycleWeaponCommand - Move forward or backward through active weapons
|
|
CycleWeaponReverseCommand
|
|
W_WeaponFrame - Called every frame, catches impulse event
|
|
SuperDamageSound - Plays quad damage sound
|
|
|
|
======================================================================*/
|
|
void() player_run; // Start of run cycle
|
|
void(entity targ) remoteplayer_run;
|
|
void() player_axe1; // Different Axe animations
|
|
void() player_axeb1;
|
|
void() player_axec1;
|
|
void() player_axed1;
|
|
void() player_axee1;
|
|
void() player_sg1; // Shotgun
|
|
void() player_supersg1; // Super Shotgun
|
|
void() player_nail1; // Nailgun
|
|
void() player_snail1; // Super Nailgun
|
|
void() player_rocket1; // Rocket Launcher
|
|
void() player_grenade1; // Grenade Launcher
|
|
void() player_light1; // Thunderbolt
|
|
void() player_plasma1; // Plasma Gun
|
|
|
|
void() player_ssgreset; // Reset weaponframe
|
|
void() player_sgreset;
|
|
void() player_grenreset;
|
|
void() player_rockreset;
|
|
|
|
/*======================================================================
|
|
Weapon ammo run out and switching to next best weapon
|
|
======================================================================*/
|
|
void(float wait_time) forceweaponswitch =
|
|
{
|
|
local float nextweap;
|
|
|
|
// has the player run out of ammo? work out next best weapon
|
|
nextweap = W_BestWeapon (self);
|
|
// Does the player need to switch weapons?
|
|
if (self.weapon != nextweap) {
|
|
self.weapon = nextweap;
|
|
W_SetCurrentAmmo (self);
|
|
}
|
|
};
|
|
|
|
/*======================================================================
|
|
W_FireAxe
|
|
======================================================================*/
|
|
void() W_FireAxe =
|
|
{
|
|
local vector source, org, vec;
|
|
local float src_dist, dotprod, axedmg;
|
|
local entity onflr;
|
|
|
|
makevectors (self.v_angle); // Player forward angle
|
|
source = self.origin + '0 0 16'; // Move source point up body
|
|
|
|
if (self.flags & FL_GODMODE && self.flags & FL_NOTARGET) {
|
|
traceline (source, source + (v_forward* RANGE_PLAYAXE), FALSE, self);
|
|
if (trace_fraction < 1.0) {
|
|
if (trace_ent.flags & FL_MONSTER && trace_ent.health > 0) {
|
|
dprint("\n\b[DEBUG]\b system active, changing monster\n");
|
|
trace_ent.debuglvl = 1 - trace_ent.debuglvl;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (self.moditems & IT_UPGRADE_AXE) {
|
|
// See if there are any bodies lying around
|
|
onflr = find(world, bodyonflr, MON_ONFLR);
|
|
while (onflr) {
|
|
src_dist = vlen(source - onflr.origin);
|
|
if (src_dist < RANGE_CHOPAXE) { // Is the body close to the player?
|
|
org = onflr.origin - '0 0 16'; // move origin close to floor
|
|
makevectors (self.v_angle); // Calculate viewing angle
|
|
vec = normalize (org - self.origin);
|
|
dotprod = vec * v_forward;
|
|
if (dotprod > 0.6) { // Is the body infront of the player
|
|
onflr.origin = org; // Move gib closer to floor
|
|
// Now a function in ai_gibs, used by boil explosion as well
|
|
monster_flrbody_gib(onflr, axedmg*4);
|
|
return;
|
|
}
|
|
}
|
|
// See if there are anymore bodies close by
|
|
onflr = find(onflr,bodyonflr, MON_ONFLR);
|
|
}
|
|
}
|
|
|
|
// Trace forward and see if the axe has hit anything?
|
|
traceline (source, source + (v_forward* RANGE_PLAYAXE), FALSE, self);
|
|
if (trace_fraction == 1.0) return; // No contact, no hit
|
|
|
|
org = trace_endpos - v_forward*4; // Back 4 units to spawn blood
|
|
|
|
// Only stuff that can be damaged has unique impact sounds
|
|
if (trace_ent.takedamage) {
|
|
// Mark the monster with type of axe used and change damage
|
|
if (self.moditems & IT_UPGRADE_AXE) {
|
|
axedmg = DAMAGE_PLAYAXE2; trace_ent.axhitme = 2; }
|
|
else {
|
|
axedmg = DAMAGE_PLAYAXE1; trace_ent.axhitme = 1; }
|
|
|
|
// Spamn blood always up so player can see it
|
|
SpawnBlood (trace_ent, org, '0 0 1', axedmg);
|
|
T_Damage (trace_ent, self, self, axedmg, DAMARMOR);
|
|
|
|
// Monster impact sounds (fleshy stuff)
|
|
if (trace_ent.flags & FL_MONSTER) {
|
|
if (random() < 0.5) sound (self, CHAN_WEAPON, GIB_SOUND_HEAVY, 1, ATTN_NORM);
|
|
else sound (self, CHAN_WEAPON, GIB_SOUND_HEAVY2, 1, ATTN_NORM);
|
|
}
|
|
// Breakable impact sounds (def=stone/brick)
|
|
else if (trace_ent.classtype == CT_FUNCBREAK) {
|
|
if (trace_ent.style == BTYPE_WOOD)
|
|
sound (self, CHAN_WEAPON, SOUND_AXE_WOOD, 1, ATTN_NORM);
|
|
else if (trace_ent.style == BTYPE_GLASS)
|
|
sound (self, CHAN_WEAPON, SOUND_AXE_GLASS, 1, ATTN_NORM);
|
|
else if (trace_ent.style == BTYPE_METAL)
|
|
sound (self, CHAN_WEAPON, SOUND_AXE_METAL, 1, ATTN_NORM);
|
|
else
|
|
sound (self, CHAN_WEAPON, SOUND_AXE_STONE, 1, ATTN_NORM);
|
|
}
|
|
// Pushable impact sounds (def=stone/brick)
|
|
else if (trace_ent.classtype == CT_FUNCPUSHABLE) {
|
|
if (trace_ent.style == PTYPE_WOOD)
|
|
sound (self, CHAN_WEAPON, SOUND_AXE_WOOD, 1, ATTN_NORM);
|
|
else if (trace_ent.style == PTYPE_GLASS)
|
|
sound (self, CHAN_WEAPON, SOUND_AXE_GLASS, 1, ATTN_NORM);
|
|
else if (trace_ent.style == PTYPE_METAL)
|
|
sound (self, CHAN_WEAPON, SOUND_AXE_METAL, 1, ATTN_NORM);
|
|
else
|
|
sound (self, CHAN_WEAPON, SOUND_AXE_STONE, 1, ATTN_NORM);
|
|
}
|
|
}
|
|
//----------------------------------------------------------------------
|
|
// WORLD OBJECT : Target does not bleed, play stone hitting sound
|
|
//----------------------------------------------------------------------
|
|
else {
|
|
sound (self, CHAN_WEAPON, SOUND_AXE_STONE, 1, ATTN_NORM);
|
|
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
|
|
WriteByte (MSG_BROADCAST, TE_GUNSHOT);
|
|
WriteCoord (MSG_BROADCAST, org_x);
|
|
WriteCoord (MSG_BROADCAST, org_y);
|
|
WriteCoord (MSG_BROADCAST, org_z);
|
|
}
|
|
};
|
|
|
|
/*======================================================================
|
|
OLD STYLE BULLET SYSTEM (HITSCAN)
|
|
======================================================================*/
|
|
void() ClearMultiDamage =
|
|
{
|
|
multi_ent = world;
|
|
multi_damage = 0;
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() ApplyMultiDamage =
|
|
{
|
|
local float temp_classgroup;
|
|
|
|
// Return if no target entity
|
|
if (!multi_ent) return;
|
|
|
|
// Check for bullet/shell resistance
|
|
if (multi_ent.resist_shells > 0)
|
|
multi_damage = Resist_Damage(multi_ent, IT_SHELLS, multi_damage);
|
|
|
|
// Need to fool T_Damage that damage is coming from a shell
|
|
temp_classgroup = self.classgroup;
|
|
self.classgroup = CG_PROJSHELLS;
|
|
T_Damage (multi_ent, self, self, multi_damage, DAMARMOR);
|
|
self.classgroup = temp_classgroup;
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void(entity hit, float damage) AddMultiDamage =
|
|
{
|
|
if (!hit) return;
|
|
|
|
if (hit != multi_ent) {
|
|
ApplyMultiDamage ();
|
|
multi_damage = damage;
|
|
multi_ent = hit;
|
|
}
|
|
else multi_damage = multi_damage + damage;
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void(vector org, float marker_time) MarkAttack =
|
|
{
|
|
newmis = spawn();
|
|
newmis.classtype = CT_DEVMARKER;
|
|
newmis.movetype = MOVETYPE_NONE;
|
|
newmis.solid = SOLID_NOT;
|
|
setmodel(newmis, MODEL_IMPACT);
|
|
newmis.skin = rint(random()*7);
|
|
setorigin(newmis, org);
|
|
setsize (newmis, VEC_ORIGIN, VEC_ORIGIN);
|
|
newmis.think = SUB_Remove;
|
|
newmis.nextthink = time + marker_time;
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void(float damage, vector dir) TraceAttack =
|
|
{
|
|
local vector vel, org;
|
|
|
|
vel = normalize(dir + v_up*crandom() + v_right*crandom());
|
|
vel = vel + 2*trace_plane_normal;
|
|
vel = vel * 200;
|
|
|
|
org = trace_endpos - dir*4;
|
|
|
|
// Check for sky content? skies don't impact or bleed
|
|
if (!check_skycontent(org)) {
|
|
if (trace_ent.takedamage) {
|
|
// Show bullet resistance as small blood+gunshot+smoke
|
|
if (trace_ent.resist_shells > 0)
|
|
Resist_Shells(trace_ent, org, vel, damage);
|
|
else {
|
|
// Hitting monsters does twice the amount of blood effects
|
|
if (trace_ent.flags & FL_MONSTER) SpawnBlood (trace_ent, org, vel*0.2, damage*2);
|
|
else SpawnBlood (trace_ent, org, vel*0.2, damage);
|
|
}
|
|
// Keep adding up the damage
|
|
AddMultiDamage (trace_ent, damage);
|
|
|
|
// Check for target dummy (manually create marker)
|
|
if (trace_ent.classtype == CT_TARGETDUMMY)
|
|
MarkAttack(org, trace_ent.wait);
|
|
}
|
|
else {
|
|
// Hit something that does not bleed (often world)
|
|
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
|
|
WriteByte (MSG_BROADCAST, TE_GUNSHOT);
|
|
WriteCoord (MSG_BROADCAST, org_x);
|
|
WriteCoord (MSG_BROADCAST, org_y);
|
|
WriteCoord (MSG_BROADCAST, org_z);
|
|
}
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void(float shotcount, vector spread) FireBullets =
|
|
{
|
|
local vector direction, src, dir;
|
|
|
|
if (self.flags & FL_CLIENT) {
|
|
makevectors(self.v_angle);
|
|
src = self.origin + v_forward*10;
|
|
src_z = self.absmin_z + self.size_z * 0.7;
|
|
// Auto aim assist (builtin function 44)
|
|
if (autoaim_cvar < 1) dir = aim(self, SPEED_PLAYAIM);
|
|
// Straight line forward where crosshair is pointing
|
|
else dir = normalize(v_forward * SPEED_PLAYAIM);
|
|
}
|
|
else {
|
|
makevectors(self.angles);
|
|
src = self.origin + attack_vector(self.attack_offset);
|
|
// A monster attacking a monster will not dodge
|
|
if (self.enemy.flags & FL_MONSTER)
|
|
dir = normalize(self.enemy.origin - src);
|
|
else {
|
|
// fire somewhat behind the player
|
|
// so a dodging player is harder to hit
|
|
dir = self.enemy.origin - self.enemy.velocity*0.2;
|
|
dir = normalize (dir - src);
|
|
}
|
|
}
|
|
|
|
ClearMultiDamage ();
|
|
while (shotcount > 0) {
|
|
direction = dir + crandom()*spread_x*v_right + crandom()*spread_y*v_up;
|
|
traceline (src, src + direction*2048, FALSE, self);
|
|
if (trace_fraction != 1.0) TraceAttack (DAMAGE_SHELL, direction);
|
|
shotcount = shotcount - 1;
|
|
}
|
|
ApplyMultiDamage ();
|
|
};
|
|
|
|
/*======================================================================
|
|
W_FireShotgun
|
|
======================================================================*/
|
|
void() W_FireShotgun =
|
|
{
|
|
local vector spread_pat;
|
|
|
|
// Ran out of ammo, switch to next best weapon
|
|
if (self.attack_finished > time) return;
|
|
if (self.ammo_shells < 1) { forceweaponswitch(0.2); return;}
|
|
|
|
// Change weapon sound if using Projectile Pellets
|
|
// IMPORTANT - default for Projectile Pellets is ON = 0
|
|
if (!query_configflag(SVR_SHOTGPROJ))
|
|
sound (self ,CHAN_WEAPON, "weapons/sg2.wav", 1, ATTN_NORM);
|
|
else
|
|
sound (self ,CHAN_WEAPON, "weapons/sg1.wav", 1, ATTN_NORM);
|
|
|
|
self.effects = self.effects | EF_MUZZLEFLASH;
|
|
self.attack_finished = time + 0.5;
|
|
self.punchangle_x = -2;
|
|
player_sgreset(); // reset weaponframe
|
|
|
|
self.currentammo = self.ammo_shells = self.ammo_shells - 1;
|
|
|
|
if (self.moditems & IT_ARTSHARP) spread_pat = SPREAD_SG2;
|
|
else spread_pat = SPREAD_SG;
|
|
|
|
// Choose between a projectile or hitscan system
|
|
if (query_configflag(SVR_SHOTGPROJ)) {
|
|
FireBullets (QUANTITY_SG, spread_pat);
|
|
Launch_ShellCasing(1); // Shell casings
|
|
}
|
|
else {
|
|
Launch_Shells(QUANTITY_SG, spread_pat, CT_PROJ_SG);
|
|
Launch_ShellCasing(1); // Shell casings
|
|
}
|
|
};
|
|
|
|
/*======================================================================
|
|
W_FireSuperShotgun
|
|
======================================================================*/
|
|
void() W_FireSuperShotgun =
|
|
{
|
|
local float ssg_qty, shell_qty;
|
|
local vector spread_pat;
|
|
|
|
// Ran out of ammo, switch to next best weapon
|
|
if (self.attack_finished > time) return;
|
|
if (self.ammo_shells < 2) { forceweaponswitch(0.2); return;}
|
|
|
|
makevectors (self.v_angle); // Special view angle for the player
|
|
|
|
// if shot in the air - do Newton's 3rd law. (from zerstorer)
|
|
if (!(self.flags & FL_ONGROUND))
|
|
self.velocity = self.velocity - (v_forward * 35);
|
|
|
|
// Change weapon sound if using Shotgun Upgrade or Projectile Pellets
|
|
// IMPORTANT - default for Projectile Pellets is ON = 0
|
|
if (self.moditems & IT_UPGRADE_SSG || !query_configflag(SVR_SHOTGPROJ))
|
|
sound (self ,CHAN_WEAPON, "weapons/ssg2.wav", 1, ATTN_NORM);
|
|
else
|
|
sound (self ,CHAN_WEAPON, "weapons/ssg1.wav", 1, ATTN_NORM);
|
|
|
|
self.effects = self.effects | EF_MUZZLEFLASH;
|
|
self.attack_finished = time + 0.7;
|
|
self.punchangle_x = -4;
|
|
player_ssgreset(); // reset weaponframe
|
|
shell_qty = 2; // Standard shell casings quantity
|
|
|
|
// If the player has the Widowmaker, setup triple shot
|
|
if (self.moditems & IT_UPGRADE_SSG) {
|
|
// Only got 2 shells in the gun (reduced damage)
|
|
if (self.ammo_shells < 3) {
|
|
self.ammo_shells = 0;
|
|
ssg_qty = QUANTITY_SSG;
|
|
}
|
|
else {
|
|
// 150% damage
|
|
shell_qty = 3;
|
|
self.ammo_shells = self.ammo_shells - 3;
|
|
ssg_qty = QUANTITY_WM;
|
|
}
|
|
}
|
|
else {
|
|
// Default ID SSG damage
|
|
self.ammo_shells = self.ammo_shells - 2;
|
|
ssg_qty = QUANTITY_SSG;
|
|
}
|
|
|
|
self.currentammo = self.ammo_shells;
|
|
if (self.moditems & IT_ARTSHARP) spread_pat = SPREAD_SSG2;
|
|
else spread_pat = SPREAD_SSG;
|
|
|
|
// Choose between a projectile or hit-scan system
|
|
if (query_configflag(SVR_SHOTGPROJ)) {
|
|
FireBullets (ssg_qty, spread_pat); // Hit-scan
|
|
Launch_ShellCasing(shell_qty);
|
|
}
|
|
else {
|
|
Launch_Shells(ssg_qty, spread_pat, CT_PROJ_SSG);
|
|
Launch_ShellCasing(shell_qty);
|
|
}
|
|
};
|
|
|
|
/*======================================================================
|
|
W_FireSpikes
|
|
======================================================================*/
|
|
void(float oz, float ox) W_FireSpikes =
|
|
{
|
|
local vector org, dir;
|
|
|
|
// If run out of ammo, switch weapons to next best
|
|
if (self.ammo_nails < 1) { forceweaponswitch(0.2); return; }
|
|
|
|
self.effects = self.effects | EF_MUZZLEFLASH;
|
|
self.attack_finished = time + 0.2;
|
|
self.punchangle_x = -2;
|
|
makevectors (self.v_angle);
|
|
|
|
// Check for auto aim state
|
|
// Auto aim assist (builtin function 44)
|
|
if (autoaim_cvar < 1) dir = aim(self, SPEED_PLAYSPIKE);
|
|
// Straight line forward where crosshair is pointing
|
|
else dir = normalize(v_forward * SPEED_PLAYSPIKE);
|
|
|
|
// SNG setup, sound and ammo change
|
|
if (self.ammo_nails > 1 && self.weapon == IT_SUPER_NAILGUN) {
|
|
sound (self, CHAN_WEAPON, "weapons/spike2.wav", 1, ATTN_NORM);
|
|
self.currentammo = self.ammo_nails = self.ammo_nails - 2;
|
|
|
|
//-------------------------------------------------------------
|
|
// Original setup - org = self.origin + '0 0 16';
|
|
// - SNG projectile offset idea by Kinn
|
|
// org = self.origin + dir*14 + v_right*ox;
|
|
// org_z = org_z + oz;
|
|
// The SNG offset idea was hiting far below the crosshair
|
|
// which was causing impact problems.
|
|
// Kept the barrel offset, removed the Z adjustment instead!
|
|
//-------------------------------------------------------------
|
|
org = self.origin + '0 0 16' + v_right*ox;
|
|
launch_projectile (org, dir, CT_PROJ_SNG, SPEED_PLAYSPIKE);
|
|
}
|
|
// NG setup, sound and ammo change
|
|
else {
|
|
sound (self, CHAN_WEAPON, "weapons/rocket1i.wav", 1, ATTN_NORM);
|
|
self.currentammo = self.ammo_nails = self.ammo_nails - 1;
|
|
|
|
// Original setup - org = self.origin + '0 0 16' + v_right*ox;
|
|
// - NG projectile offset idea by Kinn
|
|
// org = self.origin + '0 0 8' + dir*14 + v_right*ox;
|
|
org = self.origin + '0 0 16' + v_right*ox;
|
|
launch_projectile (org, dir, CT_PROJ_NG, SPEED_PLAYSPIKE);
|
|
}
|
|
};
|
|
|
|
/*======================================================================
|
|
GRENADES
|
|
======================================================================*/
|
|
void() W_FireGrenade =
|
|
{
|
|
local vector dir, avel;
|
|
|
|
// Ran out of ammo, switch to next best weapon
|
|
if (self.attack_finished > time) return;
|
|
if (self.ammo_rockets < 1) { forceweaponswitch(0.2); return;}
|
|
|
|
self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;
|
|
sound (self, CHAN_WEAPON, "weapons/grenade.wav", 1, ATTN_NORM);
|
|
self.effects = self.effects | EF_MUZZLEFLASH;
|
|
self.attack_finished = time + 0.6;
|
|
self.punchangle_x = -2;
|
|
player_grenreset(); // reset weaponframe
|
|
|
|
makevectors (self.v_angle);
|
|
// Has the player aimed left/right? then no auto aim assist
|
|
if (self.v_angle_x) {
|
|
dir = v_forward*SPEED_PLAYGRENADE + v_up * ELEV_ZAXIS + crandom()*v_right*10 + crandom()*v_up*10;
|
|
}
|
|
else {
|
|
// Check for auto aim state
|
|
if (autoaim_cvar < 1) dir = aim(self, SPEED_PLAYAIM);
|
|
else dir = normalize(v_forward * SPEED_PLAYGRENADE);
|
|
// Work out default speed and elevation
|
|
dir = dir * SPEED_PLAYGRENADE;
|
|
dir_z = ELEV_ZAXIS;
|
|
}
|
|
avel = vecrand(100,200,FALSE);
|
|
Launch_Grenade(self.origin, dir, avel, CT_PROJ_GL);
|
|
};
|
|
|
|
/*======================================================================
|
|
ROCKETS
|
|
======================================================================*/
|
|
void() W_FireRocket =
|
|
{
|
|
local vector org, dir;
|
|
|
|
// Ran out of ammo, switch to next best weapon
|
|
if (self.attack_finished > time) return;
|
|
if (self.ammo_rockets < 1) { forceweaponswitch(0.2); return;}
|
|
|
|
self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;
|
|
sound (self, CHAN_WEAPON, "weapons/sgun1.wav", 1, ATTN_NORM);
|
|
self.effects = self.effects | EF_MUZZLEFLASH;
|
|
self.attack_finished = time + 0.8;
|
|
self.punchangle_x = -2;
|
|
player_rockreset(); // reset weaponframe
|
|
|
|
makevectors (self.v_angle);
|
|
org = self.origin + v_forward * 8 + '0 0 16';
|
|
|
|
// Auto aim assist (builtin function 44)
|
|
if (autoaim_cvar < 1) dir = aim(self, SPEED_RLPLAYER);
|
|
// Straight line forward where crosshair is pointing
|
|
else dir = normalize(v_forward * SPEED_RLPLAYER);
|
|
|
|
Launch_Missile (org, dir, '0 0 0', CT_PROJ_ROCKET, SPEED_RLPLAYER);
|
|
};
|
|
|
|
/*======================================================================
|
|
PLASMA
|
|
======================================================================*/
|
|
void() W_FirePlasma =
|
|
{
|
|
local vector org, dir;
|
|
|
|
// If run out of ammo, switch weapons to next best
|
|
if (self.attack_finished > time) return;
|
|
if (self.ammo_cells < 1) { forceweaponswitch(0.2); return; }
|
|
|
|
// Do nothing if weapon is under water
|
|
if (self.waterlevel > 1) {
|
|
self.attack_finished = time + 0.2;
|
|
sound (self, CHAN_WEAPON, "weapons/nofire.wav", 1, ATTN_NORM);
|
|
return;
|
|
}
|
|
|
|
self.effects = self.effects | EF_MUZZLEFLASH;
|
|
self.attack_finished = time + 0.2;
|
|
self.punchangle_x = -2;
|
|
|
|
makevectors (self.v_angle);
|
|
|
|
// Check for auto aim state
|
|
// Auto aim assist (builtin function 44)
|
|
if (autoaim_cvar < 1) dir = aim(self, SPEED_PLAYPLASMA);
|
|
// Straight line forward where crosshair is pointing
|
|
else dir = normalize(v_forward * SPEED_PLAYPLASMA);
|
|
|
|
sound (self, CHAN_WEAPON, "weapons/plasma_fire.wav", 1, ATTN_NORM);
|
|
self.currentammo = self.ammo_cells = self.ammo_cells - 1;
|
|
|
|
org = self.origin + v_forward*8 + '0 0 16';
|
|
launch_plasma (org, dir, CT_PLAYER, SPEED_PLAYPLASMA);
|
|
};
|
|
|
|
/*======================================================================
|
|
LIGHTNING
|
|
======================================================================*/
|
|
void(vector lstart, entity lsource, float ldamage) LightningReflection;
|
|
//----------------------------------------------------------------------
|
|
// This is a general purpose lighting damage function
|
|
// Used by monsters and traps
|
|
//----------------------------------------------------------------------
|
|
void(vector lstart, vector lfinish, entity lsource, float ldamage) LightningDamage =
|
|
{
|
|
local vector spot1, spot2, lightdir;
|
|
local float lighthit, loffset;
|
|
|
|
lighthit = FALSE;
|
|
loffset = 8;
|
|
|
|
// Traceline 1 - Direct single line from source to target
|
|
traceline (lstart, lfinish, FALSE, lsource);
|
|
if (trace_ent.takedamage > DAMAGE_NO) lighthit = TRUE;
|
|
else {
|
|
// Where out vector direction from source to target
|
|
lightdir = lfinish - lstart;
|
|
makevectors(lightdir);
|
|
|
|
// Traceline 2 - Left and right of center
|
|
spot1 = lstart + v_right * loffset;
|
|
spot2 = lfinish + v_right * loffset;
|
|
traceline (spot1, spot2, FALSE, lsource);
|
|
if (trace_ent.takedamage > DAMAGE_NO) lighthit = TRUE;
|
|
else {
|
|
spot1 = lstart - v_right * loffset;
|
|
spot2 = lfinish - v_right * loffset;
|
|
traceline (spot1, spot2, FALSE, lsource);
|
|
if (trace_ent.takedamage > DAMAGE_NO) lighthit = TRUE;
|
|
else {
|
|
// Traceline 3 - Up and down of center
|
|
spot1 = lstart + v_up * loffset;
|
|
spot2 = lfinish + v_up * loffset;
|
|
traceline (spot1, spot2, FALSE, lsource);
|
|
if (trace_ent.takedamage > DAMAGE_NO) lighthit = TRUE;
|
|
else {
|
|
spot1 = lstart - v_up * loffset;
|
|
spot2 = lfinish - v_up * loffset;
|
|
traceline (spot1, spot2, FALSE, lsource);
|
|
if (trace_ent.takedamage > DAMAGE_NO) lighthit = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Found a target to hit?
|
|
if (lighthit) {
|
|
//----------------------------------------------------------------------
|
|
// Check for breakable/pushable monster immunity
|
|
if (ai_immunedamage(self, trace_ent)) {
|
|
// This is resist lightning function without pain sound
|
|
// Don't spawn smoke constantly (let the sprite finish)
|
|
if (self.lightning_timer < time) {
|
|
self.lightning_timer = time + 0.3;
|
|
SpawnExplosion(EXPLODE_BURST_SMOKE, trace_endpos, "");
|
|
}
|
|
SpawnProjectileSmoke(trace_endpos, 200, 50, 150);
|
|
}
|
|
//----------------------------------------------------------------------
|
|
// Check for any cell/lightning resistance
|
|
else if (trace_ent.resist_cells > 0) {
|
|
ldamage = Resist_Damage(trace_ent, IT_CELLS, ldamage);
|
|
Resist_Lightning(trace_ent, trace_endpos);
|
|
if (ldamage > 0)
|
|
// classic damage from source with armour saves
|
|
T_Damage (trace_ent, lsource, lsource, ldamage, DAMARMOR);
|
|
}
|
|
else {
|
|
// Originally used 225 blood colour
|
|
SpawnBlood(trace_ent, trace_endpos, '0 0 100', ldamage*4);
|
|
T_Damage (trace_ent, lsource, lsource, ldamage, DAMARMOR);
|
|
}
|
|
// Check for any lightning reflection abilitites
|
|
if (trace_ent.reflectlightning) {
|
|
// Source = Lightning originally came from player
|
|
LightningReflection(trace_endpos, trace_ent, ldamage*0.5);
|
|
}
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
// New ability to spawn lighting strikes in random directions
|
|
//----------------------------------------------------------------------
|
|
void(vector lstart, entity lsource, float ldamage) LightningReflection =
|
|
{
|
|
local vector lfinish, dir;
|
|
|
|
// Setup a random XYZ direction (+/-)
|
|
lfinish = lstart + vecrand(0,1000,1);
|
|
|
|
// Trace line in random direction
|
|
traceline(lstart, lfinish, FALSE, lsource);
|
|
|
|
// Random chance of a plasma/lightning bolt!
|
|
if (random() < 0.5) {
|
|
dir = normalize(trace_endpos - lstart);
|
|
launch_plasma(lstart, dir, CT_REFLECTLIGHT, SPEED_REFLECTION);
|
|
}
|
|
else {
|
|
// Draw lighting beam (32 model unit chunks)
|
|
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
|
|
WriteByte (MSG_BROADCAST, TE_LIGHTNING2);
|
|
WriteEntity (MSG_BROADCAST, lsource);
|
|
WriteCoord (MSG_BROADCAST, lstart_x);
|
|
WriteCoord (MSG_BROADCAST, lstart_y);
|
|
WriteCoord (MSG_BROADCAST, lstart_z);
|
|
WriteCoord (MSG_BROADCAST, trace_endpos_x);
|
|
WriteCoord (MSG_BROADCAST, trace_endpos_y);
|
|
WriteCoord (MSG_BROADCAST, trace_endpos_z);
|
|
// Check for damage with new lightning beam
|
|
LightningDamage(lstart, trace_endpos, world, ldamage);
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
// This lightning damage check is designed for the lightning gun
|
|
// with many modifiers designed for the player and MP combat
|
|
// This function should not be used for general lightning damage
|
|
//----------------------------------------------------------------------
|
|
void(vector p1, vector p2, entity from, float damage) PlayerLightningDamage =
|
|
{
|
|
local entity e1, e2;
|
|
local vector f;
|
|
local float lighthit, temp_classgroup;
|
|
|
|
f = p2 - p1;
|
|
normalize (f);
|
|
f_x = 0 - f_y;
|
|
f_y = f_x;
|
|
f_z = 0;
|
|
f = f*16;
|
|
|
|
e1 = e2 = world;
|
|
lighthit = FALSE;
|
|
// LightningDamage (org, trace_endpos, self, 10);
|
|
// dprint("Light ("); dprint(trace_ent.classname); dprint(")\n");
|
|
traceline (p1, p2, FALSE, self);
|
|
if (trace_ent.takedamage) {
|
|
lighthit = TRUE;
|
|
// Some weird MP velocity hack!
|
|
// The story about this code (from quakeworld.nu)
|
|
//
|
|
// This code appears to have been there for a long, long time.
|
|
// It was there in Quake 1.06 Shareware release, and it may have
|
|
// been there before that. Someone must have been experimenting
|
|
// with something and forgot it there, but no one noticed because
|
|
// in that function, the 'other' entity is not usually set to
|
|
// anything in particular, and the code never worked.
|
|
//
|
|
// But then QW came along, and in the QW engine it turns out
|
|
// that 'other' will be set to 'self' if the player is touching
|
|
// a platform. And so the dormant code stared working! It was
|
|
// discovered by players, and players started using it to their
|
|
// advantage. Now it's an integral part of dm6 gameplay.
|
|
//
|
|
// The code was apparently discovered and removed when Quake QC
|
|
// code was cleaned up before being released to public, so you
|
|
// won't normally see it in NQ mods. But it is there in the
|
|
// progs.dat in pak0.pak; and it will work in QuakeWorld engines
|
|
// supporting progs.dat (currently FTE and ZQuake).
|
|
//
|
|
if (self.classtype == CT_PLAYER) {
|
|
if (other.classtype == CT_PLAYER)
|
|
trace_ent.velocity_z = trace_ent.velocity_z + 400;
|
|
}
|
|
}
|
|
else {
|
|
e1 = trace_ent;
|
|
|
|
traceline (p1 + f, p2 + f, FALSE, self);
|
|
if (trace_ent != e1 && trace_ent.takedamage) lighthit = TRUE;
|
|
else {
|
|
e2 = trace_ent;
|
|
|
|
traceline (p1 - f, p2 - f, FALSE, self);
|
|
if (trace_ent != e1 && trace_ent != e2 && trace_ent.takedamage)
|
|
lighthit = TRUE;
|
|
}
|
|
}
|
|
|
|
// Found a target to hit?
|
|
if (lighthit) {
|
|
// Check for any cell/lightning resistance
|
|
if (trace_ent.resist_cells > 0) {
|
|
damage = Resist_Damage(trace_ent, IT_CELLS, damage);
|
|
Resist_Lightning(trace_ent, trace_endpos);
|
|
if (damage > 0) {
|
|
// Need to fool T_Damage that damage is coming from LG
|
|
temp_classgroup = from.classgroup;
|
|
from.classgroup = CG_PROJCELLS;
|
|
T_Damage (trace_ent, from, from, damage, DAMARMOR);
|
|
from.classgroup = temp_classgroup;
|
|
}
|
|
}
|
|
else {
|
|
// Originally used 225 blood colour
|
|
SpawnBlood(trace_ent, trace_endpos, '0 0 100', damage*4);
|
|
T_Damage (trace_ent, from, from, damage, DAMARMOR);
|
|
}
|
|
// Check for any lightning reflection abilitites
|
|
if (trace_ent.reflectlightning) {
|
|
// Source = Lightning originally came from player
|
|
LightningReflection(trace_endpos, trace_ent, damage*0.5);
|
|
}
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() W_FireLightning =
|
|
{
|
|
local vector dir;
|
|
local float cells;
|
|
|
|
// Ran out of ammo, switch to next best weapon
|
|
if (self.ammo_cells < 1) { forceweaponswitch(0.2); return; }
|
|
|
|
// explode if under water
|
|
if (self.waterlevel > 1) {
|
|
cells = self.ammo_cells;
|
|
self.ammo_cells = 0;
|
|
W_SetCurrentAmmo (self);
|
|
T_RadiusDamage (self, self, 35*cells, world, DAMAGEALL);
|
|
return;
|
|
}
|
|
|
|
// Time for a new LG hit sound?
|
|
if (self.t_width < time) {
|
|
sound (self, CHAN_WEAPON, "weapons/lhit.wav", 1, ATTN_NORM);
|
|
self.t_width = time + 0.6;
|
|
}
|
|
|
|
self.currentammo = self.ammo_cells = self.ammo_cells - 1;
|
|
self.effects = self.effects | EF_MUZZLEFLASH;
|
|
self.attack_finished = time + 0.2;
|
|
self.punchangle_x = -2;
|
|
|
|
dir = self.origin + '0 0 16';
|
|
traceline (dir, dir + v_forward*600, TRUE, self);
|
|
|
|
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
|
|
WriteByte (MSG_BROADCAST, TE_LIGHTNING2);
|
|
WriteEntity (MSG_BROADCAST, self);
|
|
WriteCoord (MSG_BROADCAST, dir_x);
|
|
WriteCoord (MSG_BROADCAST, dir_y);
|
|
WriteCoord (MSG_BROADCAST, dir_z);
|
|
WriteCoord (MSG_BROADCAST, trace_endpos_x);
|
|
WriteCoord (MSG_BROADCAST, trace_endpos_y);
|
|
WriteCoord (MSG_BROADCAST, trace_endpos_z);
|
|
|
|
PlayerLightningDamage (self.origin, trace_endpos + v_forward*4, self, DAMAGE_LGPLAYER);
|
|
};
|
|
|
|
/*======================================================================
|
|
PLAYER WEAPON UPDATES AND AMMO CHECKS
|
|
|
|
W_SetCurrentAmmo - Update HUD icons based on self.weapon value
|
|
W_BestWeapon - returns best weapon LG > SNG > SSG > NG > SG > Axe
|
|
interesting that the function does not return GL/RL
|
|
W_CheckNoAmmo - Ammo check for attack function
|
|
W_Attack - Main function that fire weapons (player animations)
|
|
Will automatically wakeup any nearby monsters
|
|
W_ChangeWeapon - Change weapon based on availabilty and ammo
|
|
CycleWeaponCommand - Move forward or backward through active weapons
|
|
CycleWeaponReverseCommand
|
|
W_WeaponFrame - Called every frame, catches impulse event
|
|
SuperDamageSound - Plays quad damage sound
|
|
|
|
======================================================================*/
|
|
void(entity targ) W_SetCurrentAmmo =
|
|
{
|
|
if (intermission_running > 0) return; // intermission or finale
|
|
remoteplayer_run (targ); // get out of any weapon firing states
|
|
|
|
// Reset all Ammo types for gfx on sbar (engine code hack - sbar.c)
|
|
targ.items = targ.items - ( targ.items & (IT_SHELLS | IT_NAILS | IT_ROCKETS | IT_CELLS) );
|
|
|
|
if (targ.weapon == IT_AXE) {
|
|
targ.currentammo = 0;
|
|
targ.weaponframe = 0; // reset before setting model
|
|
if (targ.moditems & IT_UPGRADE_AXE)
|
|
targ.weaponmodel = MODEL_VWEAP_UPAXE;
|
|
else targ.weaponmodel = MODEL_VWEAP_AXE;
|
|
}
|
|
else if (targ.weapon == IT_SHOTGUN) {
|
|
targ.currentammo = targ.ammo_shells;
|
|
targ.weaponframe = 0; // reset before setting model
|
|
targ.weaponmodel = MODEL_VWEAP_SG;
|
|
targ.items = targ.items | IT_SHELLS;
|
|
}
|
|
else if (targ.weapon == IT_SUPER_SHOTGUN) {
|
|
targ.currentammo = targ.ammo_shells;
|
|
targ.weaponframe = 0; // reset before setting model
|
|
if (targ.moditems & IT_UPGRADE_SSG)
|
|
targ.weaponmodel = MODEL_VWEAP_UPSSG;
|
|
else targ.weaponmodel = MODEL_VWEAP_SSG;
|
|
targ.items = targ.items | IT_SHELLS;
|
|
}
|
|
else if (targ.weapon == IT_NAILGUN) {
|
|
targ.currentammo = targ.ammo_nails;
|
|
targ.weaponframe = 0; // reset before setting model
|
|
targ.weaponmodel = MODEL_VWEAP_NG;
|
|
targ.items = targ.items | IT_NAILS;
|
|
}
|
|
else if (targ.weapon == IT_SUPER_NAILGUN) {
|
|
targ.currentammo = targ.ammo_nails;
|
|
targ.weaponframe = 0; // reset before setting model
|
|
targ.weaponmodel = MODEL_VWEAP_SNG;
|
|
targ.items = targ.items | IT_NAILS;
|
|
}
|
|
else if (targ.weapon == IT_GRENADE_LAUNCHER) {
|
|
targ.currentammo = targ.ammo_rockets;
|
|
targ.weaponframe = 0; // reset before setting model
|
|
targ.weaponmodel = MODEL_VWEAP_GL;
|
|
targ.items = targ.items | IT_ROCKETS;
|
|
}
|
|
else if (targ.weapon == IT_ROCKET_LAUNCHER) {
|
|
targ.currentammo = targ.ammo_rockets;
|
|
targ.weaponframe = 0; // reset before setting model
|
|
targ.weaponmodel = MODEL_VWEAP_RL;
|
|
targ.items = targ.items | IT_ROCKETS;
|
|
}
|
|
else if (targ.weapon == IT_LIGHTNING) {
|
|
targ.currentammo = targ.ammo_cells;
|
|
targ.weaponframe = 0; // reset before setting model
|
|
if (targ.moditems & IT_UPGRADE_LG)
|
|
targ.weaponmodel = MODEL_VWEAP_UPLG;
|
|
else targ.weaponmodel = MODEL_VWEAP_LG;
|
|
targ.items = targ.items | IT_CELLS;
|
|
}
|
|
else {
|
|
// Bad situation no viewmodel
|
|
targ.currentammo = 0;
|
|
targ.weaponframe = 0; // reset before setting model
|
|
targ.weaponmodel = "";
|
|
}
|
|
};
|
|
|
|
/*======================================================================
|
|
Return best weapon based on current ammo quantities
|
|
======================================================================*/
|
|
float(entity targ) W_BestWeapon =
|
|
{
|
|
if (targ.waterlevel < 2 && targ.ammo_cells > 0 && (targ.items & IT_LIGHTNING) )
|
|
return IT_LIGHTNING;
|
|
if(targ.ammo_nails > 1 && (targ.items & IT_SUPER_NAILGUN) )
|
|
return IT_SUPER_NAILGUN;
|
|
if(targ.ammo_shells > 1 && (targ.items & IT_SUPER_SHOTGUN) )
|
|
return IT_SUPER_SHOTGUN;
|
|
if(targ.ammo_nails > 0 && (targ.items & IT_NAILGUN) )
|
|
return IT_NAILGUN;
|
|
if(targ.ammo_shells > 0 && (targ.items & IT_SHOTGUN) )
|
|
return IT_SHOTGUN;
|
|
return IT_AXE;
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
float(entity targ) W_CheckNoAmmo =
|
|
{
|
|
if (targ.currentammo > 0) return TRUE;
|
|
if (targ.weapon == IT_AXE) return TRUE;
|
|
|
|
targ.weapon = W_BestWeapon (targ);
|
|
W_SetCurrentAmmo (targ);
|
|
|
|
// drop the weapon down
|
|
return FALSE;
|
|
};
|
|
|
|
/*======================================================================
|
|
W_Attack
|
|
======================================================================*/
|
|
void() W_Attack =
|
|
{
|
|
if (intermission_running > 0) return; // intermission or finale
|
|
if ( !W_CheckNoAmmo(self) ) return; // Out of ammo?
|
|
|
|
makevectors (self.v_angle); // calculate forward angle for velocity
|
|
self.show_hostile = time + 1; // wake monsters up
|
|
|
|
//----------------------------------------------------------------------
|
|
// Axe - Mighty chopper
|
|
//----------------------------------------------------------------------
|
|
if (self.weapon == IT_AXE) {
|
|
if (random() < 0.5) sound (self, CHAN_WEAPON, SOUND_AXE_SWIPE1, 1, ATTN_NORM);
|
|
else sound (self, CHAN_WEAPON, SOUND_AXE_SWIPE2, 1, ATTN_NORM);
|
|
// Work out which axe swing to play (never play swing twice in a row)
|
|
self.lip = self.meleeattack;
|
|
while (self.meleeattack == self.lip) {
|
|
self.lip = rint(random()*4.4);
|
|
}
|
|
self.meleeattack = self.lip;
|
|
if (self.meleeattack == 0) player_axe1();
|
|
else if (self.meleeattack == 1) player_axeb1();
|
|
else if (self.meleeattack == 2) player_axec1();
|
|
else if (self.meleeattack == 3) player_axed1();
|
|
else player_axee1();
|
|
self.attack_finished = time + 0.5;
|
|
}
|
|
//----------------------------------------------------------------------
|
|
else if (self.weapon == IT_SHOTGUN) player_sg1();
|
|
else if (self.weapon == IT_SUPER_SHOTGUN) player_supersg1();
|
|
else if (self.weapon == IT_NAILGUN) player_nail1();
|
|
else if (self.weapon == IT_SUPER_NAILGUN) player_snail1();
|
|
else if (self.weapon == IT_GRENADE_LAUNCHER) player_grenade1();
|
|
else if (self.weapon == IT_ROCKET_LAUNCHER) player_rocket1();
|
|
else if (self.weapon == IT_LIGHTNING) {
|
|
if (self.moditems & IT_UPGRADE_LG) player_plasma1();
|
|
else {
|
|
player_light1();
|
|
sound (self, CHAN_AUTO, "weapons/lstart.wav", 1, ATTN_NORM);
|
|
}
|
|
}
|
|
};
|
|
|
|
/*======================================================================
|
|
W_ChangeWeapon
|
|
Check if got weapon and ammo and switch to relevant weapon
|
|
======================================================================*/
|
|
void(entity targ) W_ChangeWeapon =
|
|
{
|
|
local float it, am, fl;
|
|
|
|
if (intermission_running > 0) return; // intermission or finale
|
|
it = targ.items;
|
|
am = FALSE;
|
|
|
|
if (targ.impulse == 1) fl = IT_AXE;
|
|
else if (targ.impulse == 2) {
|
|
fl = IT_SHOTGUN;
|
|
if (targ.ammo_shells < 1) am = TRUE;
|
|
}
|
|
else if (targ.impulse == 3) {
|
|
fl = IT_SUPER_SHOTGUN;
|
|
if (targ.ammo_shells < 2) am = TRUE;
|
|
}
|
|
else if (targ.impulse == 4) {
|
|
fl = IT_NAILGUN;
|
|
if (targ.ammo_nails < 1) am = TRUE;
|
|
}
|
|
else if (targ.impulse == 5) {
|
|
fl = IT_SUPER_NAILGUN;
|
|
if (targ.ammo_nails < 2) am = TRUE;
|
|
}
|
|
else if (targ.impulse == 6) {
|
|
fl = IT_GRENADE_LAUNCHER;
|
|
if (targ.ammo_rockets < 1) am = TRUE;
|
|
}
|
|
else if (targ.impulse == 7) {
|
|
fl = IT_ROCKET_LAUNCHER;
|
|
if (targ.ammo_rockets < 1) am = TRUE;
|
|
}
|
|
else if (targ.impulse == 8) {
|
|
fl = IT_LIGHTNING;
|
|
if (targ.ammo_cells < 1) am = TRUE;
|
|
}
|
|
|
|
targ.impulse = 0; // Clear impulse
|
|
|
|
//----------------------------------------------------------------------
|
|
// FL = don't have the weapon
|
|
// AM = out of ammo
|
|
//----------------------------------------------------------------------
|
|
if (!(targ.items & fl)) sprint (targ, "no weapon.\n");
|
|
else if (am) sprint (targ, "not enough ammo.\n");
|
|
else {
|
|
targ.weapon = fl; // Switch to weapon (internal number)
|
|
W_SetCurrentAmmo (targ);
|
|
}
|
|
};
|
|
|
|
/*======================================================================
|
|
CycleWeaponCommand
|
|
======================================================================*/
|
|
void() CycleWeaponCommand =
|
|
{
|
|
local float am;
|
|
|
|
if (intermission_running > 0) return;// intermission or finale
|
|
self.impulse = 0; // reset impulse
|
|
|
|
// Keep cycling around weapon list until found a weapon with ammo
|
|
while (1) {
|
|
am = 0;
|
|
if (self.weapon == IT_LIGHTNING) {
|
|
self.weapon = IT_AXE;
|
|
}
|
|
else if (self.weapon == IT_AXE) {
|
|
self.weapon = IT_SHOTGUN;
|
|
if (self.ammo_shells < 1) am = 1;
|
|
}
|
|
else if (self.weapon == IT_SHOTGUN) {
|
|
self.weapon = IT_SUPER_SHOTGUN;
|
|
if (self.ammo_shells < 2) am = 1;
|
|
}
|
|
else if (self.weapon == IT_SUPER_SHOTGUN) {
|
|
self.weapon = IT_NAILGUN;
|
|
if (self.ammo_nails < 1) am = 1;
|
|
}
|
|
else if (self.weapon == IT_NAILGUN) {
|
|
self.weapon = IT_SUPER_NAILGUN;
|
|
if (self.ammo_nails < 2) am = 1;
|
|
}
|
|
else if (self.weapon == IT_SUPER_NAILGUN) {
|
|
self.weapon = IT_GRENADE_LAUNCHER;
|
|
if (self.ammo_rockets < 1) am = 1;
|
|
}
|
|
else if (self.weapon == IT_GRENADE_LAUNCHER) {
|
|
self.weapon = IT_ROCKET_LAUNCHER;
|
|
if (self.ammo_rockets < 1) am = 1;
|
|
}
|
|
else if (self.weapon == IT_ROCKET_LAUNCHER) {
|
|
self.weapon = IT_LIGHTNING;
|
|
if (self.ammo_cells < 1) am = 1;
|
|
}
|
|
|
|
// Has the player got the weapons and ammo to switch?
|
|
if ( (self.items & self.weapon) && !am) {
|
|
W_SetCurrentAmmo (self);
|
|
return;
|
|
}
|
|
}
|
|
};
|
|
|
|
/*======================================================================
|
|
CycleWeaponReverseCommand
|
|
======================================================================*/
|
|
void() CycleWeaponReverseCommand =
|
|
{
|
|
local float am;
|
|
|
|
if (intermission_running > 0) return; // intermission or finale
|
|
self.impulse = 0; // reset impulse
|
|
|
|
// Keep cycling around weapon list until found a weapon with ammo
|
|
while (1) {
|
|
am = 0;
|
|
if (self.weapon == IT_LIGHTNING) {
|
|
self.weapon = IT_ROCKET_LAUNCHER;
|
|
if (self.ammo_rockets < 1) am = 1;
|
|
}
|
|
else if (self.weapon == IT_ROCKET_LAUNCHER) {
|
|
self.weapon = IT_GRENADE_LAUNCHER;
|
|
if (self.ammo_rockets < 1) am = 1;
|
|
}
|
|
else if (self.weapon == IT_GRENADE_LAUNCHER) {
|
|
self.weapon = IT_SUPER_NAILGUN;
|
|
if (self.ammo_nails < 2) am = 1;
|
|
}
|
|
else if (self.weapon == IT_SUPER_NAILGUN) {
|
|
self.weapon = IT_NAILGUN;
|
|
if (self.ammo_nails < 1) am = 1;
|
|
}
|
|
else if (self.weapon == IT_NAILGUN) {
|
|
self.weapon = IT_SUPER_SHOTGUN;
|
|
if (self.ammo_shells < 2) am = 1;
|
|
}
|
|
else if (self.weapon == IT_SUPER_SHOTGUN) {
|
|
self.weapon = IT_SHOTGUN;
|
|
if (self.ammo_shells < 1) am = 1;
|
|
}
|
|
else if (self.weapon == IT_SHOTGUN) {
|
|
self.weapon = IT_AXE;
|
|
}
|
|
else if (self.weapon == IT_AXE) {
|
|
self.weapon = IT_LIGHTNING;
|
|
if (self.ammo_cells < 1) am = 1;
|
|
}
|
|
|
|
if ( (self.items & self.weapon) && !am) {
|
|
W_SetCurrentAmmo (self);
|
|
return;
|
|
}
|
|
}
|
|
};
|
|
|
|
/*======================================================================
|
|
W_WeaponFrame
|
|
======================================================================*/
|
|
void() W_WeaponFrame =
|
|
{
|
|
ImpulseCommands ();
|
|
// Allow for impulse commands before weapon lockout
|
|
if (time < self.attack_finished) return;
|
|
|
|
// check for attack
|
|
if (self.button0) {
|
|
// originally - SuperDamageSound
|
|
// Only play one powerup sound at once
|
|
if (self.super_damage_finished > 0 && self.powerup_sound < time) {
|
|
if (self.super_sound < time) {
|
|
self.super_sound = time + 1;
|
|
self.powerup_sound = time + 1;
|
|
sound (self, CHAN_BODY, SOUND_ARTQUAD3, 1, ATTN_NORM);
|
|
}
|
|
}
|
|
// Only play one powerup sound at once
|
|
if (self.sharpshoot_finished > 0 && self.powerup_sound < time) {
|
|
// Only works with the shotguns
|
|
if (self.weapon == IT_SHOTGUN || self.weapon == IT_SUPER_SHOTGUN) {
|
|
if (self.sharpshooter_sound < time) {
|
|
self.sharpshooter_sound = time + 0.5;
|
|
// Only play the sound every other shot
|
|
self.powerup_sound = time + 1;
|
|
sound (self, CHAN_BODY, SOUND_ARTSHARP3, 1, ATTN_NORM);
|
|
}
|
|
}
|
|
}
|
|
// Only play one powerup sound at once
|
|
if (self.nailpiercer_finished > 0 && self.powerup_sound < time) {
|
|
// Only works with nailgun + super nailgun
|
|
if (self.weapon == IT_NAILGUN || self.weapon == IT_SUPER_NAILGUN) {
|
|
if (self.nailpiercer_sound < time) {
|
|
self.nailpiercer_sound = time + 0.5;
|
|
self.powerup_sound = time + 1;
|
|
sound (self, CHAN_BODY, SOUND_ARTNAILP3, 1, ATTN_NORM);
|
|
}
|
|
}
|
|
}
|
|
// Check for weapon updates
|
|
W_Attack ();
|
|
}
|
|
};
|