mirror of
https://github.com/id-Software/quake-rerelease-qc.git
synced 2024-11-09 23:11:44 +00:00
570 lines
14 KiB
C++
570 lines
14 KiB
C++
/* Copyright (C) 1996-2022 id Software LLC
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
See file, 'COPYING', for details.
|
|
*/
|
|
|
|
// multi_weapons.qc
|
|
// pmack
|
|
// sept 96
|
|
|
|
//=============================================================================
|
|
// Multi Grenade Code
|
|
//=============================================================================
|
|
void() MultiGrenadeTouch;
|
|
|
|
//================================
|
|
//================================
|
|
void() MiniGrenadeExplode =
|
|
{
|
|
if ( self.owner.classname == "player")
|
|
T_RadiusDamage (self, self.owner, 90, world);
|
|
else
|
|
T_RadiusDamage (self, self.owner, 60, world);
|
|
|
|
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
|
|
WriteByte (MSG_BROADCAST, TE_EXPLOSION2);
|
|
WriteCoord (MSG_BROADCAST, self.origin_x);
|
|
WriteCoord (MSG_BROADCAST, self.origin_y);
|
|
WriteCoord (MSG_BROADCAST, self.origin_z);
|
|
WriteByte (MSG_BROADCAST, 230);
|
|
WriteByte (MSG_BROADCAST, 5);
|
|
|
|
BecomeExplosion ();
|
|
};
|
|
|
|
//================================
|
|
//================================
|
|
void(float offsetAngle) MiniGrenadeLaunch =
|
|
{
|
|
local entity missile, mpuff;
|
|
local float tempRand;
|
|
|
|
missile = spawn ();
|
|
missile.owner = self.owner;
|
|
missile.movetype = MOVETYPE_BOUNCE;
|
|
missile.solid = SOLID_BBOX;
|
|
missile.classname = "MiniGrenade";
|
|
|
|
// set missile speed
|
|
missile.v_angle = self.v_angle;
|
|
missile.v_angle_y = missile.v_angle_y + offsetAngle;
|
|
makevectors (missile.v_angle);
|
|
|
|
missile.velocity = v_forward*100 + v_up*400;
|
|
tempRand = (crandom()*60) - 30;
|
|
missile.velocity = missile.velocity + tempRand * v_forward;
|
|
tempRand = (crandom()*40) - 20;
|
|
missile.velocity = missile.velocity + tempRand * v_right;
|
|
tempRand = (crandom()*60) - 30;
|
|
missile.velocity = missile.velocity + tempRand * v_up;
|
|
|
|
missile.avelocity = '300 300 300';
|
|
missile.angles = vectoangles(missile.velocity);
|
|
missile.touch = MultiGrenadeTouch;
|
|
|
|
setmodel (missile, "progs/mervup.mdl");
|
|
setsize (missile, '0 0 0', '0 0 0');
|
|
setorigin (missile, self.origin);
|
|
|
|
// set missile duration
|
|
missile.nextthink = time + 1 + (crandom() * 0.5);
|
|
missile.think = MiniGrenadeExplode;
|
|
};
|
|
|
|
//================================
|
|
//================================
|
|
void() MultiGrenadeExplode =
|
|
{
|
|
MiniGrenadeLaunch(0);
|
|
MiniGrenadeLaunch(72);
|
|
MiniGrenadeLaunch(144);
|
|
MiniGrenadeLaunch(216);
|
|
MiniGrenadeLaunch(288);
|
|
|
|
remove (self);
|
|
};
|
|
|
|
//================================
|
|
//================================
|
|
void() MultiGrenadeTouch =
|
|
{
|
|
if (other == self.owner)
|
|
return; // don't explode on owner
|
|
if (other.takedamage == DAMAGE_AIM)
|
|
{
|
|
if (self.classname == "MiniGrenade")
|
|
MiniGrenadeExplode();
|
|
else
|
|
{
|
|
if (self.owner.classname == "player")
|
|
GrenadeExplode();
|
|
else
|
|
MiniGrenadeExplode();
|
|
}
|
|
return;
|
|
}
|
|
// bounce sound
|
|
sound (self, CHAN_WEAPON, "weapons/bounce.wav", 1, ATTN_NORM);
|
|
if (self.velocity == '0 0 0')
|
|
self.avelocity = '0 0 0';
|
|
};
|
|
|
|
//================================
|
|
//================================
|
|
void() W_FireMultiGrenade =
|
|
{
|
|
local entity missile, mpuff;
|
|
|
|
self.currentammo = self.ammo_multi_rockets = self.ammo_multi_rockets - 1;
|
|
UpdateAmmoCounts (self);
|
|
|
|
sound (self, CHAN_WEAPON, "weapons/grenade.wav", 1, ATTN_NORM);
|
|
|
|
self.punchangle_x = -2;
|
|
|
|
missile = spawn ();
|
|
missile.owner = self;
|
|
missile.movetype = MOVETYPE_BOUNCE;
|
|
missile.solid = SOLID_BBOX;
|
|
missile.classname = "MultiGrenade";
|
|
|
|
// set missile speed
|
|
makevectors (self.v_angle);
|
|
if (self.v_angle_x)
|
|
missile.velocity = v_forward*600 + v_up * 200 + crandom()*v_right*10 + crandom()*v_up*10;
|
|
else
|
|
{
|
|
missile.velocity = aim(self, 10000);
|
|
missile.velocity = missile.velocity * 600;
|
|
missile.velocity_z = 200;
|
|
}
|
|
|
|
missile.avelocity = '300 300 300';
|
|
missile.angles = vectoangles(missile.velocity);
|
|
missile.touch = MultiGrenadeTouch;
|
|
|
|
// set missile duration
|
|
missile.nextthink = time + 1;
|
|
missile.think = MultiGrenadeExplode;
|
|
|
|
setmodel (missile, "progs/mervup.mdl");
|
|
setsize (missile, '0 0 0', '0 0 0');
|
|
setorigin (missile, self.origin);
|
|
};
|
|
|
|
//=============================================================================
|
|
// Multi Rocket Code
|
|
//=============================================================================
|
|
|
|
//================================
|
|
//================================
|
|
void() MultiRocketExplode =
|
|
{
|
|
local float damg;
|
|
|
|
// Stock Single Rocket Damage...
|
|
// damg = 100 + random()*20;
|
|
|
|
damg = 60 + random()*15;
|
|
|
|
if (other.health)
|
|
{
|
|
if (other.classname == "monster_shambler")
|
|
damg = damg * 0.5; // mostly immune
|
|
if (other.classname == "monster_dragon")
|
|
damg = damg * 0.5; // mostly immune
|
|
T_Damage (other, self, self.owner, damg );
|
|
}
|
|
|
|
// don't do radius damage to the other, because all the damage
|
|
// was done in the impact
|
|
// Stock single rocket damage.
|
|
// T_RadiusDamage (self, self.owner, 120, other);
|
|
|
|
T_RadiusDamage (self, self.owner, 75, other);
|
|
|
|
// sound (self, CHAN_WEAPON, "weapons/r_exp3.wav", 1, ATTN_NORM);
|
|
self.origin = self.origin - 8*normalize(self.velocity);
|
|
|
|
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
|
|
WriteByte (MSG_BROADCAST, TE_EXPLOSION);
|
|
WriteCoord (MSG_BROADCAST, self.origin_x);
|
|
WriteCoord (MSG_BROADCAST, self.origin_y);
|
|
WriteCoord (MSG_BROADCAST, self.origin_z);
|
|
|
|
BecomeExplosion ();
|
|
};
|
|
|
|
//================================
|
|
//================================
|
|
void() MultiRocketTouch =
|
|
{
|
|
if (other == self.owner)
|
|
return; // don't explode on owner
|
|
|
|
if (pointcontents(self.origin) == CONTENT_SKY)
|
|
{
|
|
remove(self);
|
|
return;
|
|
}
|
|
|
|
MultiRocketExplode ();
|
|
};
|
|
|
|
|
|
//================================
|
|
//================================
|
|
void() HomingMissileThink =
|
|
{
|
|
local vector dir;
|
|
|
|
if (self.enemy.health < 1)
|
|
{
|
|
remove(self);
|
|
return;
|
|
}
|
|
|
|
dir = normalize(self.enemy.origin - self.origin);
|
|
self.velocity = dir * 1000;
|
|
self.nextthink = time + 0.1;
|
|
self.think = HomingMissileThink;
|
|
};
|
|
|
|
|
|
//================================
|
|
//================================
|
|
void() HomingMissileAcquire =
|
|
{
|
|
local vector oldVelocity;
|
|
local vector aimangle;
|
|
|
|
if ( self.delay < time )
|
|
{
|
|
MultiRocketExplode ();
|
|
return;
|
|
}
|
|
|
|
oldVelocity = self.velocity;
|
|
makevectors (self.v_angle);
|
|
self.velocity = aim (self, 1000);
|
|
self.velocity = self.velocity * 1000;
|
|
|
|
aimangle = self.origin + self.velocity;
|
|
traceline ( self.origin, aimangle, FALSE, self );
|
|
if (trace_fraction < 1)
|
|
{
|
|
if (trace_ent.flags & FL_MONSTER)
|
|
{
|
|
self.enemy = trace_ent;
|
|
HomingMissileThink();
|
|
return;
|
|
}
|
|
}
|
|
|
|
self.velocity = oldVelocity;
|
|
self.v_angle = vectoangles ( self.velocity );
|
|
self.angles = self.v_angle;
|
|
self.think = HomingMissileAcquire;
|
|
self.nextthink = time + 0.2;
|
|
};
|
|
|
|
//================================
|
|
//================================
|
|
void(float offset, float frameNum) MultiRocketLaunch =
|
|
{
|
|
local entity missile, mpuff;
|
|
local vector aimangle;
|
|
|
|
missile = spawn ();
|
|
missile.owner = self;
|
|
missile.movetype = MOVETYPE_FLYMISSILE;
|
|
missile.solid = SOLID_BBOX;
|
|
missile.classname = "MultiRocket";
|
|
missile.delay = time + 4;
|
|
missile.frame = frameNum;
|
|
missile.touch = MultiRocketTouch;
|
|
|
|
if (deathmatch || coop)
|
|
setmodel (missile, "progs/rockup_d.mdl");
|
|
else
|
|
setmodel (missile, "progs/rockup.mdl");
|
|
|
|
setsize (missile, '0 0 0', '0 0 0');
|
|
setorigin (missile, self.origin + v_forward*8 + '0 0 16');
|
|
|
|
if ( coop || deathmatch)
|
|
{
|
|
aimangle = self.v_angle;
|
|
aimangle_y = aimangle_y + (offset * 0.66);
|
|
makevectors (aimangle);
|
|
missile.velocity = aim(self, 1000);
|
|
missile.velocity = missile.velocity * 1000;
|
|
missile.angles = vectoangles(missile.velocity);
|
|
|
|
missile.think = MultiRocketExplode;
|
|
missile.nextthink = time + 4;
|
|
}
|
|
else
|
|
{
|
|
makevectors (self.v_angle);
|
|
missile.velocity = v_forward * 1000 - v_right*offset*8;
|
|
missile.angles = vectoangles(missile.velocity);
|
|
missile.v_angle = self.v_angle;
|
|
|
|
aimangle = missile.origin + missile.velocity;
|
|
traceline ( missile.origin, aimangle, FALSE, self );
|
|
if (trace_fraction < 1)
|
|
{
|
|
if (trace_ent.flags & FL_MONSTER)
|
|
{
|
|
missile.enemy = trace_ent;
|
|
missile.think = HomingMissileThink;
|
|
return;
|
|
}
|
|
}
|
|
|
|
missile.think = HomingMissileAcquire;
|
|
missile.nextthink = time + 0.1;
|
|
}
|
|
};
|
|
|
|
//================================
|
|
//================================
|
|
void() W_FireMultiRocket =
|
|
{
|
|
self.currentammo = self.ammo_multi_rockets = self.ammo_multi_rockets - 1;
|
|
UpdateAmmoCounts (self);
|
|
|
|
sound (self, CHAN_WEAPON, "weapons/sgun1.wav", 1, ATTN_NORM);
|
|
|
|
self.punchangle_x = -2;
|
|
|
|
MultiRocketLaunch ( -10, 2 );
|
|
MultiRocketLaunch ( -5, 3 );
|
|
MultiRocketLaunch ( 5, 0 );
|
|
MultiRocketLaunch ( 10, 1 );
|
|
};
|
|
|
|
//=============================================================================
|
|
// Plasma Gun Code
|
|
//=============================================================================
|
|
void(vector p1, vector p2, entity from, float damage) PlasmaDamage =
|
|
{
|
|
local entity e1, e2;
|
|
local vector f;
|
|
|
|
f = p2 - p1;
|
|
normalize (f);
|
|
f_x = 0 - f_y;
|
|
f_y = f_x;
|
|
f_z = 0;
|
|
f = f*16;
|
|
|
|
e1 = e2 = world;
|
|
|
|
traceline (p1, p2, FALSE, self);
|
|
if (trace_ent.takedamage)
|
|
{
|
|
particle (trace_endpos, '0 0 100', 225, damage*4);
|
|
T_Damage (trace_ent, from, from.owner, damage);
|
|
if (self.classname == "player")
|
|
{
|
|
if (other.classname == "player")
|
|
trace_ent.velocity_z = trace_ent.velocity_z + 400;
|
|
}
|
|
}
|
|
e1 = trace_ent;
|
|
|
|
traceline (p1 + f, p2 + f, FALSE, self);
|
|
if (trace_ent != e1 && trace_ent.takedamage)
|
|
{
|
|
particle (trace_endpos, '0 0 100', 225, damage*4);
|
|
T_Damage (trace_ent, from, from.owner, damage);
|
|
}
|
|
e2 = trace_ent;
|
|
|
|
traceline (p1 - f, p2 - f, FALSE, self);
|
|
if (trace_ent != e1 && trace_ent != e2 && trace_ent.takedamage)
|
|
{
|
|
particle (trace_endpos, '0 0 100', 225, damage*4);
|
|
T_Damage (trace_ent, from, from.owner, damage);
|
|
}
|
|
};
|
|
|
|
//================================
|
|
//================================
|
|
void(entity current, float doDamage) PlasmaDischarge =
|
|
{
|
|
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
|
|
WriteByte (MSG_BROADCAST, TE_LIGHTNING2);
|
|
WriteEntity (MSG_BROADCAST, current);
|
|
WriteCoord (MSG_BROADCAST, current.origin_x);
|
|
WriteCoord (MSG_BROADCAST, current.origin_y);
|
|
WriteCoord (MSG_BROADCAST, current.origin_z);
|
|
WriteCoord (MSG_BROADCAST, self.origin_x);
|
|
WriteCoord (MSG_BROADCAST, self.origin_y);
|
|
WriteCoord (MSG_BROADCAST, self.origin_z);
|
|
|
|
sound (self, CHAN_VOICE, "weapons/lhit.wav", 1, ATTN_NORM);
|
|
if (doDamage == 1)
|
|
PlasmaDamage (self.origin, current.origin, self, 50);
|
|
};
|
|
|
|
//================================
|
|
//================================
|
|
void() PlasmaGroundOut =
|
|
{
|
|
local entity current, start;
|
|
local float monstersHit;
|
|
|
|
monstersHit = 0;
|
|
current = findradius ( self.origin, 320 );
|
|
start = current;
|
|
while ( monstersHit < 5 )
|
|
{
|
|
if ( current.flags & FL_MONSTER || current.classname == "player")
|
|
{
|
|
if ( current != self.owner )
|
|
{
|
|
traceline ( self.origin, current.origin, TRUE, world );
|
|
if (trace_fraction == 1)
|
|
{
|
|
monstersHit = monstersHit + 1;
|
|
PlasmaDischarge ( current, 1 );
|
|
}
|
|
}
|
|
}
|
|
current = current.chain;
|
|
if (start == current || !current)
|
|
return;
|
|
}
|
|
};
|
|
|
|
//================================
|
|
//================================
|
|
void() PlasmaTouch =
|
|
{
|
|
local float damg;
|
|
|
|
if (other == self.owner)
|
|
return; // don't explode on owner
|
|
|
|
if (pointcontents(self.origin) == CONTENT_SKY)
|
|
{
|
|
remove(self);
|
|
return;
|
|
}
|
|
|
|
damg = 80 + random()*20;
|
|
sound (self, CHAN_WEAPON, "plasma/explode.wav", 1, ATTN_NORM);
|
|
|
|
if (other.health)
|
|
{
|
|
if (other.classname == "monster_shambler")
|
|
damg = damg * 0.5; // mostly immune
|
|
T_Damage (other, self, self.owner, damg );
|
|
}
|
|
|
|
// don't do radius damage to the other, because all the damage
|
|
// was done in the impact
|
|
T_RadiusDamage (self, self.owner, 70, other);
|
|
|
|
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
|
|
WriteByte (MSG_BROADCAST, TE_EXPLOSION2);
|
|
WriteCoord (MSG_BROADCAST, self.origin_x);
|
|
WriteCoord (MSG_BROADCAST, self.origin_y);
|
|
WriteCoord (MSG_BROADCAST, self.origin_z);
|
|
WriteByte (MSG_BROADCAST, 244);
|
|
WriteByte (MSG_BROADCAST, 3);
|
|
|
|
PlasmaGroundOut();
|
|
remove (self);
|
|
};
|
|
|
|
//================================
|
|
//================================
|
|
void() PlasmaLaunch =
|
|
{
|
|
self.velocity = normalize(self.velocity);
|
|
self.velocity = self.velocity * 1250;
|
|
|
|
self.nextthink = time + 5;
|
|
self.think = SUB_Remove;
|
|
};
|
|
|
|
void(vector org, vector dir) launch_plasma =
|
|
{
|
|
local entity missile, mpuff;
|
|
|
|
missile = spawn ();
|
|
missile.owner = self;
|
|
missile.movetype = MOVETYPE_FLYMISSILE;
|
|
missile.solid = SOLID_BBOX;
|
|
missile.classname = "plasma";
|
|
|
|
// set missile speed
|
|
missile.velocity = dir * 0.01;
|
|
missile.avelocity = '300 300 300';
|
|
missile.angles = vectoangles(missile.velocity);
|
|
missile.touch = PlasmaTouch;
|
|
|
|
setmodel (missile, "progs/plasma.mdl");
|
|
setsize (missile, '0 0 0', '0 0 0');
|
|
setorigin (missile, org);
|
|
|
|
sound (missile, CHAN_WEAPON, "plasma/flight.wav", 1, ATTN_NORM);
|
|
if (!deathmatch && !coop)
|
|
missile.effects = EF_BRIGHTLIGHT;
|
|
|
|
// set missile duration
|
|
missile.think = PlasmaLaunch;
|
|
missile.nextthink = time + 0.1;
|
|
};
|
|
|
|
//================================
|
|
//================================
|
|
void() W_FirePlasma =
|
|
{
|
|
local float cells;
|
|
local vector dir;
|
|
|
|
if (self.ammo_plasma < 1)
|
|
{
|
|
self.weapon = W_BestWeapon ();
|
|
W_SetCurrentAmmo ();
|
|
return;
|
|
}
|
|
|
|
// explode if under water
|
|
if (self.waterlevel > 1)
|
|
{
|
|
cells = self.ammo_plasma;
|
|
self.ammo_plasma = 0;
|
|
W_SetCurrentAmmo ();
|
|
T_RadiusDamage (self, self, 35*cells, world);
|
|
return;
|
|
}
|
|
self.currentammo = self.ammo_plasma = self.ammo_plasma - 1;
|
|
UpdateAmmoCounts (self);
|
|
sound (self, CHAN_WEAPON, "plasma/fire.wav", 0.5, ATTN_NORM);
|
|
self.punchangle_x = -2;
|
|
|
|
makevectors (self.v_angle);
|
|
dir = aim ( self, 1000 );
|
|
launch_plasma (self.origin + v_forward*24 + '0 0 16', dir);
|
|
};
|
|
|