mirror of
https://github.com/blendogames/thirtyflightsofloving.git
synced 2024-11-14 16:40:57 +00:00
cedf3628ac
Added close range safety check/trace to Tactician Gunner prox aiming. Added check for nightmare mode for Beta-Class Gladiator and dog.
1109 lines
28 KiB
C
1109 lines
28 KiB
C
// g_weapon_q1.c
|
|
|
|
#include "g_local.h"
|
|
|
|
|
|
void check_dodge (edict_t *self, vec3_t start, vec3_t dir, int speed);
|
|
|
|
|
|
/*
|
|
=================
|
|
q1_fire_nail
|
|
|
|
Fires a Nail, Used by Q1 NG and SNG
|
|
=================
|
|
*/
|
|
void q1_nail_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
|
|
{
|
|
int mod;
|
|
|
|
if (other == self->owner)
|
|
return;
|
|
|
|
if (surf && (surf->flags & SURF_SKY))
|
|
{
|
|
G_FreeEdict (self);
|
|
return;
|
|
}
|
|
|
|
if (self->owner->client)
|
|
PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
|
|
|
|
if (other->takedamage)
|
|
{
|
|
if (self->spawnflags & 1)
|
|
mod = MOD_Q1_SNG;
|
|
else
|
|
mod = MOD_Q1_NG;
|
|
T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, 0, DAMAGE_BULLET, mod);
|
|
}
|
|
else
|
|
{
|
|
gi.WriteByte (svc_temp_entity);
|
|
gi.WriteByte (TE_SHOTGUN);
|
|
gi.WritePosition (self->s.origin);
|
|
if (!plane)
|
|
gi.WriteDir (vec3_origin);
|
|
else
|
|
gi.WriteDir (plane->normal);
|
|
gi.multicast (self->s.origin, MULTICAST_PVS);
|
|
|
|
// not if online, too laggy
|
|
if (!deathmatch->value)
|
|
{
|
|
float sound = random();
|
|
if (sound < 0.3)
|
|
{
|
|
if (sound < 0.1)
|
|
gi.sound (self, CHAN_WEAPON, gi.soundindex ("q1weapons/ric3.wav"), 1, ATTN_STATIC, 0);
|
|
else if (sound < 0.2)
|
|
gi.sound (self, CHAN_WEAPON, gi.soundindex ("q1weapons/ric2.wav"), 1, ATTN_STATIC, 0);
|
|
else
|
|
gi.sound (self, CHAN_WEAPON, gi.soundindex ("q1weapons/ric1.wav"), 1, ATTN_STATIC, 0);
|
|
}
|
|
else if (sound < 0.5)
|
|
gi.sound (self, CHAN_WEAPON, gi.soundindex ("q1weapons/tink1.wav"), 1, ATTN_STATIC, 0);
|
|
}
|
|
|
|
}
|
|
G_FreeEdict (self);
|
|
}
|
|
|
|
|
|
void q1_fire_nail (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, qboolean sng)
|
|
{
|
|
edict_t *nail;
|
|
trace_t tr;
|
|
|
|
VectorNormalize (dir);
|
|
|
|
nail = G_Spawn();
|
|
VectorCopy (start, nail->s.origin);
|
|
VectorCopy (start, nail->s.old_origin);
|
|
vectoangles (dir, nail->s.angles);
|
|
VectorScale (dir, speed, nail->velocity);
|
|
nail->movetype = MOVETYPE_FLYMISSILE;
|
|
nail->clipmask = MASK_SHOT;
|
|
nail->solid = SOLID_BBOX;
|
|
nail->svflags = SVF_DEADMONSTER;
|
|
nail->s.renderfx = RF_FULLBRIGHT;
|
|
VectorClear (nail->mins);
|
|
VectorClear (nail->maxs);
|
|
nail->s.modelindex = gi.modelindex ("models/objects/q1nail/tris.md2");
|
|
nail->owner = self;
|
|
nail->touch = q1_nail_touch;
|
|
nail->nextthink = level.time + 8000/speed;
|
|
nail->think = G_FreeEdict;
|
|
nail->dmg = damage;
|
|
nail->classname = "nail";
|
|
if (sng)
|
|
nail->spawnflags = 1;
|
|
|
|
nail->common_name = "Nail";
|
|
nail->class_id = ENTITY_Q1_NAIL;
|
|
|
|
gi.linkentity (nail);
|
|
|
|
if (self->client)
|
|
check_dodge (self, nail->s.origin, dir, speed);
|
|
|
|
tr = gi.trace (self->s.origin, NULL, NULL, nail->s.origin, nail, MASK_SHOT);
|
|
if (tr.fraction < 1.0)
|
|
{
|
|
VectorMA (nail->s.origin, -10, dir, nail->s.origin);
|
|
nail->touch (nail, tr.ent, NULL, NULL);
|
|
}
|
|
}
|
|
|
|
|
|
void q1_nail_precache (void)
|
|
{
|
|
gi.modelindex ("models/objects/q1nail/tris.md2");
|
|
gi.soundindex ("q1weapons/tink1.wav");
|
|
gi.soundindex ("q1weapons/ric1.wav");
|
|
gi.soundindex ("q1weapons/ric2.wav");
|
|
gi.soundindex ("q1weapons/ric3.wav");
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
q1_fire_laser
|
|
|
|
Fires a laser bolt, used by Q1 enforcer and laser trap.
|
|
=================
|
|
*/
|
|
void q1_laser_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
|
|
{
|
|
if (other == self->owner)
|
|
return;
|
|
|
|
if (surf && (surf->flags & SURF_SKY))
|
|
{
|
|
G_FreeEdict (self);
|
|
return;
|
|
}
|
|
|
|
if (self->owner->client)
|
|
PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
|
|
|
|
if (other->takedamage) {
|
|
T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, 0, DAMAGE_ENERGY, MOD_Q1_LASER);
|
|
}
|
|
else
|
|
{
|
|
#if 1 // From Decino's Q2Infighter mod
|
|
gi.WriteByte (svc_temp_entity);
|
|
gi.WriteByte (TE_WELDING_SPARKS);
|
|
gi.WriteByte (15);
|
|
gi.WritePosition (self->s.origin);
|
|
gi.WriteDir ((!plane) ? vec3_origin : plane->normal);
|
|
gi.WriteByte (226);
|
|
gi.multicast (self->s.origin, MULTICAST_PVS);
|
|
#else
|
|
gi.WriteByte (svc_temp_entity);
|
|
gi.WriteByte (TE_SHOTGUN);
|
|
gi.WritePosition (self->s.origin);
|
|
if (!plane)
|
|
gi.WriteDir (vec3_origin);
|
|
else
|
|
gi.WriteDir (plane->normal);
|
|
gi.multicast (self->s.origin, MULTICAST_PVS);
|
|
#endif
|
|
|
|
// not if online, too laggy
|
|
if (!deathmatch->value)
|
|
{
|
|
gi.sound (self, CHAN_WEAPON, gi.soundindex ("q1enforcer/enfstop.wav"), 1, ATTN_STATIC, 0);
|
|
}
|
|
}
|
|
G_FreeEdict (self);
|
|
}
|
|
|
|
void q1_fire_laser (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed)
|
|
{
|
|
edict_t *laser;
|
|
trace_t tr;
|
|
|
|
VectorNormalize (dir);
|
|
|
|
laser = G_Spawn();
|
|
VectorCopy (start, laser->s.origin);
|
|
VectorCopy (start, laser->s.old_origin);
|
|
vectoangles (dir, laser->s.angles);
|
|
VectorScale (dir, speed, laser->velocity);
|
|
laser->movetype = MOVETYPE_FLYMISSILE;
|
|
laser->clipmask = MASK_SHOT;
|
|
laser->solid = SOLID_BBOX;
|
|
laser->svflags = SVF_DEADMONSTER;
|
|
laser->s.renderfx = RF_FULLBRIGHT;
|
|
VectorClear (laser->mins);
|
|
VectorClear (laser->maxs);
|
|
laser->s.modelindex = gi.modelindex ("models/monsters/q1enforcer/laser/tris.md2");
|
|
laser->owner = self;
|
|
laser->touch = q1_laser_touch;
|
|
laser->nextthink = level.time + 8000/speed;
|
|
laser->think = G_FreeEdict;
|
|
laser->dmg = damage;
|
|
laser->classname = "laser";
|
|
|
|
laser->common_name = "Laser";
|
|
laser->class_id = ENTITY_Q1_LASER;
|
|
|
|
gi.linkentity (laser);
|
|
|
|
if (self->client)
|
|
check_dodge (self, laser->s.origin, dir, speed);
|
|
|
|
tr = gi.trace (self->s.origin, NULL, NULL, laser->s.origin, laser, MASK_SHOT);
|
|
if (tr.fraction < 1.0)
|
|
{
|
|
VectorMA (laser->s.origin, -10, dir, laser->s.origin);
|
|
laser->touch (laser, tr.ent, NULL, NULL);
|
|
}
|
|
}
|
|
|
|
void q1_laser_precache (void)
|
|
{
|
|
gi.modelindex ("models/monsters/q1enforcer/laser/tris.md2");
|
|
gi.soundindex ("q1enforcer/enfstop.wav");
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
q1_fire_flame
|
|
|
|
Fires a flame bolt. Used by the Hell Knight.
|
|
=================
|
|
*/
|
|
|
|
void q1_flame_splash (int type, int count, int color, vec3_t start, vec3_t movdir, vec3_t origin)
|
|
{
|
|
gi.WriteByte (svc_temp_entity);
|
|
gi.WriteByte (type);
|
|
gi.WriteByte (count);
|
|
gi.WritePosition (start);
|
|
gi.WriteDir (movdir);
|
|
gi.WriteByte (color);
|
|
gi.multicast (origin, MULTICAST_PVS);
|
|
}
|
|
|
|
void q1_flame_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
|
|
{
|
|
if (other == self->owner)
|
|
return;
|
|
|
|
if (surf && (surf->flags & SURF_SKY))
|
|
{
|
|
G_FreeEdict (self);
|
|
return;
|
|
}
|
|
|
|
if (self->owner->client)
|
|
PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
|
|
|
|
if (other->takedamage)
|
|
{
|
|
T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, 1, DAMAGE_ENERGY, MOD_Q1_FLAMEBOLT);
|
|
}
|
|
else
|
|
{
|
|
#if 1 // From Decino's Q2Infighter mod
|
|
gi.WriteByte (svc_temp_entity);
|
|
gi.WriteByte (TE_WELDING_SPARKS);
|
|
gi.WriteByte (15);
|
|
gi.WritePosition (self->s.origin);
|
|
gi.WriteDir ((!plane) ? vec3_origin : plane->normal);
|
|
// gi.WriteByte (66);
|
|
gi.WriteByte (228);
|
|
gi.multicast (self->s.origin, MULTICAST_PVS);
|
|
#else
|
|
q1_flame_splash (TE_TUNNEL_SPARKS, 16, 71, self->s.origin, plane->normal, self->s.origin);
|
|
#endif
|
|
}
|
|
|
|
gi.sound (self, CHAN_WEAPON, gi.soundindex("q1hknight/hit.wav"), 1, ATTN_NORM, 0);
|
|
|
|
G_FreeEdict (self);
|
|
}
|
|
|
|
|
|
void q1_fire_flame (edict_t *self, vec3_t start, vec3_t dir, float leftrightoff)
|
|
{
|
|
edict_t *bolt;
|
|
trace_t tr;
|
|
int damage, speed;
|
|
// vec3_t up;
|
|
|
|
damage = 9;
|
|
speed = 300;
|
|
|
|
VectorNormalize (dir);
|
|
|
|
bolt = G_Spawn();
|
|
bolt->svflags |= SVF_DEADMONSTER;
|
|
// yes, I know it looks weird that projectiles are deadmonsters
|
|
// what this means is that when prediction is used against the object
|
|
// (blaster/hyperblaster shots), the player won't be solid clipped against
|
|
// the object. Right now trying to run into a firing hyperblaster
|
|
// is very jerky since you are predicted 'against' the shots.
|
|
|
|
VectorCopy (start, bolt->s.origin);
|
|
VectorCopy (start, bolt->s.old_origin);
|
|
vectoangles (dir, bolt->s.angles);
|
|
VectorScale (dir, speed, bolt->velocity);
|
|
bolt->movetype = MOVETYPE_FLYMISSILE;
|
|
bolt->clipmask = MASK_SHOT;
|
|
bolt->solid = SOLID_BBOX;
|
|
bolt->s.effects |= EF_IONRIPPER;
|
|
VectorClear (bolt->mins);
|
|
VectorClear (bolt->maxs);
|
|
bolt->s.modelindex = gi.modelindex ("models/monsters/q1hknight/k_spike/tris.md2");
|
|
bolt->owner = self;
|
|
bolt->touch = q1_flame_touch;
|
|
bolt->nextthink = level.time + 2;
|
|
bolt->think = G_FreeEdict;
|
|
bolt->dmg = damage;
|
|
bolt->classname = "bolt";
|
|
|
|
bolt->common_name = "Hell Knight Flame";
|
|
bolt->class_id = ENTITY_Q1_HKNIGHT_FLAME;
|
|
|
|
gi.linkentity (bolt);
|
|
|
|
tr = gi.trace (self->s.origin, NULL, NULL, bolt->s.origin, bolt, MASK_SHOT);
|
|
if (tr.fraction < 1.0)
|
|
{
|
|
VectorMA (bolt->s.origin, -10, dir, bolt->s.origin);
|
|
bolt->touch (bolt, tr.ent, NULL, NULL);
|
|
}
|
|
}
|
|
|
|
|
|
void q1_flame_precache (void)
|
|
{
|
|
gi.modelindex ("models/monsters/q1hknight/k_spike/tris.md2");
|
|
gi.soundindex("q1hknight/hit.wav");
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
q1_fire_grenade
|
|
|
|
Fires a grenade. Used by the Ogre.
|
|
=================
|
|
*/
|
|
void q1_explode (edict_t *self)
|
|
{
|
|
if (self->s.frame == 5)
|
|
{
|
|
G_FreeEdict (self);
|
|
return;
|
|
}
|
|
self->nextthink = level.time + FRAMETIME;
|
|
self->s.frame++;
|
|
}
|
|
|
|
void q1_grenade_explode (edict_t *ent)
|
|
{
|
|
vec3_t origin;
|
|
|
|
if (ent->owner->client)
|
|
PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT);
|
|
|
|
// FIXME: if we are onground then raise our Z just a bit since we are a point?
|
|
if (ent->enemy)
|
|
{
|
|
float points;
|
|
vec3_t v;
|
|
vec3_t dir;
|
|
|
|
VectorAdd (ent->enemy->mins, ent->enemy->maxs, v);
|
|
VectorMA (ent->enemy->s.origin, 0.5, v, v);
|
|
VectorSubtract (ent->s.origin, v, v);
|
|
points = ent->dmg - 0.5 * VectorLength (v);
|
|
VectorSubtract (ent->enemy->s.origin, ent->s.origin, dir);
|
|
T_Damage (ent->enemy, ent, ent->owner, dir, ent->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, MOD_Q1_GL);
|
|
}
|
|
|
|
T_RadiusDamage(ent, ent->owner, ent->dmg, ent->enemy, ent->dmg_radius, MOD_Q1_GL_SPLASH);
|
|
|
|
VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
|
|
gi.sound (ent, CHAN_AUTO, gi.soundindex ("q1weapons/r_exp3.wav"), 1.0, ATTN_NORM, 0);
|
|
|
|
gi.WriteByte (svc_temp_entity);
|
|
gi.WriteByte (TE_ROCKET_EXPLOSION);
|
|
gi.WritePosition (origin);
|
|
gi.multicast (ent->s.origin, MULTICAST_PVS);
|
|
|
|
// explosion sprite
|
|
gi.unlinkentity (ent);
|
|
ent->solid = SOLID_NOT;
|
|
ent->touch = NULL;
|
|
VectorCopy( origin, ent->s.origin);
|
|
VectorCopy( origin, ent->s.old_origin);
|
|
VectorClear (ent->velocity);
|
|
ent->s.modelindex = gi.modelindex ("sprites/s_explod.sp2");
|
|
ent->s.frame = 0;
|
|
ent->s.sound = 0;
|
|
ent->s.effects &= ~EF_ANIM_ALLFAST;
|
|
ent->think = q1_explode;
|
|
ent->nextthink = level.time + FRAMETIME;
|
|
gi.linkentity (ent);
|
|
}
|
|
|
|
|
|
void q1_grenade_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
|
|
{
|
|
if (other == ent->owner)
|
|
return;
|
|
|
|
if (surf && (surf->flags & SURF_SKY))
|
|
{
|
|
G_FreeEdict (ent);
|
|
return;
|
|
}
|
|
|
|
if (!other->takedamage ||
|
|
(other->solid == SOLID_BSP))
|
|
{
|
|
gi.sound (ent, CHAN_RELIABLE|CHAN_WEAPON, gi.soundindex ("q1weapons/bounce.wav"), 1, ATTN_NORM, 0);
|
|
return;
|
|
}
|
|
|
|
ent->enemy = other;
|
|
q1_grenade_explode (ent);
|
|
}
|
|
|
|
|
|
void q1_fire_grenade (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius)
|
|
{
|
|
edict_t *grenade;
|
|
vec3_t dir;
|
|
vec3_t forward, right, up;
|
|
|
|
vectoangles (aimdir, dir);
|
|
AngleVectors (dir, forward, right, up);
|
|
|
|
grenade = G_Spawn();
|
|
VectorCopy (start, grenade->s.origin);
|
|
VectorScale (aimdir, speed, grenade->velocity);
|
|
// Lazarus - keep same vertical boost for players, but monsters do a better job
|
|
// of calculating aim direction, so throw that out
|
|
// if (self->client)
|
|
VectorMA (grenade->velocity, 200 + (random() - 0.5) * 20.0, up, grenade->velocity);
|
|
// else
|
|
// VectorMA (grenade->velocity, (random() - 0.5) * 20.0, up, grenade->velocity);
|
|
|
|
VectorMA (grenade->velocity, (random() - 0.5) * 20.0, right, grenade->velocity);
|
|
VectorSet (grenade->avelocity, 300, 300, 300);
|
|
grenade->movetype = MOVETYPE_BOUNCE;
|
|
grenade->clipmask = MASK_SHOT;
|
|
grenade->solid = SOLID_BBOX;
|
|
grenade->s.effects |= EF_GRENADE;
|
|
grenade->s.renderfx |= RF_IR_VISIBLE;
|
|
VectorClear (grenade->mins);
|
|
VectorClear (grenade->maxs);
|
|
grenade->s.modelindex = gi.modelindex ("models/objects/grenade/tris.md2");
|
|
grenade->owner = self;
|
|
grenade->touch = q1_grenade_touch;
|
|
grenade->nextthink = level.time + timer;
|
|
grenade->think = q1_grenade_explode;
|
|
grenade->dmg = damage;
|
|
grenade->dmg_radius = damage_radius;
|
|
grenade->classname = "grenade";
|
|
|
|
grenade->common_name = "Quake Grenade";
|
|
grenade->class_id = ENTITY_Q1_GRENADE;
|
|
|
|
// Grenade_Add_To_Chain (grenade);
|
|
|
|
gi.linkentity (grenade);
|
|
}
|
|
|
|
void q1_grenade_precache (void)
|
|
{
|
|
gi.modelindex ("models/objects/grenade/tris.md2");
|
|
gi.modelindex ("sprites/s_explod.sp2");
|
|
gi.soundindex ("q1weapons/bounce.wav");
|
|
gi.soundindex ("q1weapons/r_exp3.wav");
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
q1_fire_rocket
|
|
|
|
Fires a q1 rocket. Used by ?
|
|
=================
|
|
*/
|
|
void q1_rocket_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
|
|
{
|
|
vec3_t origin;
|
|
|
|
if (other == ent->owner)
|
|
return;
|
|
|
|
if (surf && (surf->flags & SURF_SKY))
|
|
{
|
|
G_FreeEdict (ent);
|
|
return;
|
|
}
|
|
|
|
if (ent->owner->client)
|
|
PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT);
|
|
|
|
// calculate position for the explosion entity
|
|
VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
|
|
|
|
if (other->takedamage)
|
|
{
|
|
T_Damage (other, ent, ent->owner, ent->velocity, ent->s.origin, plane->normal, ent->dmg, 0, 0, MOD_Q1_RL);
|
|
}
|
|
T_RadiusDamage(ent, ent->owner, ent->radius_dmg, other, ent->dmg_radius, MOD_Q1_RL_SPLASH);
|
|
gi.sound (ent, CHAN_AUTO, gi.soundindex ("q1weapons/r_exp3.wav"), 1.0, ATTN_NORM, 0);
|
|
|
|
gi.WriteByte (svc_temp_entity);
|
|
gi.WriteByte (TE_ROCKET_EXPLOSION);
|
|
gi.WritePosition (origin);
|
|
gi.multicast (ent->s.origin, MULTICAST_PVS);
|
|
|
|
// if (ent->target_ent)
|
|
// G_FreeEdict(ent->target_ent);
|
|
|
|
// explosion sprite
|
|
gi.unlinkentity (ent);
|
|
ent->solid = SOLID_NOT;
|
|
ent->touch = NULL;
|
|
VectorCopy( origin, ent->s.origin);
|
|
VectorCopy( origin, ent->s.old_origin);
|
|
VectorClear (ent->velocity);
|
|
ent->s.modelindex = gi.modelindex ("sprites/s_explod.sp2");
|
|
ent->s.frame = 0;
|
|
ent->s.sound = 0;
|
|
ent->s.effects &= ~EF_ANIM_ALLFAST;
|
|
ent->think = q1_explode;
|
|
ent->nextthink = level.time + FRAMETIME;
|
|
gi.linkentity (ent);
|
|
}
|
|
|
|
|
|
void q1rocketTrail_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
|
|
{
|
|
G_FreeEdict (ent);
|
|
}
|
|
|
|
// Rocket Trails
|
|
// needed to give the yellow part of the Rocket trail
|
|
|
|
void q1rocket_trail (edict_t *self, vec3_t start, vec3_t dir)
|
|
{
|
|
edict_t *bolt;
|
|
VectorNormalize (dir);
|
|
|
|
bolt = G_Spawn();
|
|
VectorCopy (start, bolt->s.origin);
|
|
VectorCopy (start, bolt->s.old_origin);
|
|
vectoangles (dir, bolt->s.angles);
|
|
VectorScale (dir, 950, bolt->velocity);
|
|
bolt->movetype = MOVETYPE_FLYMISSILE;
|
|
bolt->clipmask = MASK_SHOT;
|
|
bolt->solid = SOLID_BBOX;
|
|
bolt->s.effects = EF_BLASTER;
|
|
bolt->svflags |= SVF_DEADMONSTER;
|
|
VectorClear (bolt->mins);
|
|
VectorClear (bolt->maxs);
|
|
bolt->s.modelindex = gi.modelindex ("models/objects/laser/tris.md2");
|
|
bolt->owner = self;
|
|
bolt->touch = q1rocketTrail_touch;
|
|
bolt->nextthink = level.time + 8000/950;
|
|
bolt->think = G_FreeEdict;
|
|
bolt->classname = "bolt";
|
|
gi.linkentity (bolt);
|
|
|
|
}
|
|
|
|
|
|
void q1_fire_rocket (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage)
|
|
{
|
|
edict_t *rocket;
|
|
|
|
rocket = G_Spawn();
|
|
VectorCopy (start, rocket->s.origin);
|
|
VectorCopy (dir, rocket->movedir);
|
|
vectoangles (dir, rocket->s.angles);
|
|
VectorScale (dir, speed, rocket->velocity);
|
|
rocket->movetype = MOVETYPE_FLYMISSILE;
|
|
rocket->clipmask = MASK_SHOT;
|
|
rocket->solid = SOLID_BBOX;
|
|
rocket->s.effects |= EF_GRENADE;
|
|
VectorClear (rocket->mins);
|
|
VectorClear (rocket->maxs);
|
|
rocket->s.modelindex = gi.modelindex ("models/objects/rocket/tris.md2");
|
|
rocket->owner = self;
|
|
rocket->touch = q1_rocket_touch;
|
|
rocket->nextthink = level.time + 8000/speed;
|
|
rocket->think = G_FreeEdict;
|
|
rocket->dmg = damage;
|
|
rocket->radius_dmg = radius_damage;
|
|
rocket->dmg_radius = damage_radius;
|
|
rocket->s.sound = gi.soundindex ("weapons/rockfly.wav");
|
|
rocket->classname = "rocket";
|
|
|
|
q1rocket_trail (rocket, start, dir);
|
|
|
|
rocket->common_name = "Quake Rocket";
|
|
rocket->class_id = ENTITY_Q1_ROCKET;
|
|
|
|
gi.linkentity (rocket);
|
|
}
|
|
|
|
|
|
void q1_rocket_precahce (void)
|
|
{
|
|
gi.modelindex ("models/objects/rocket/tris.md2");
|
|
gi.modelindex ("sprites/s_explod.sp2");
|
|
gi.soundindex ("weapons/rockfly.wav");
|
|
gi.soundindex ("q1weapons/r_exp3.wav");
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
q1_fire_lightning
|
|
|
|
Fires a lighting bolt. Used by ?
|
|
=================
|
|
*/
|
|
void q1_fire_lightning (edict_t *self, vec3_t start, vec3_t dir, int damage)
|
|
{
|
|
vec3_t end;
|
|
trace_t tr;
|
|
|
|
|
|
VectorNormalize(dir);
|
|
VectorMA(start,600,dir,end);
|
|
|
|
// Initial Trace - damage if close enough
|
|
tr = gi.trace (self->s.origin, NULL, NULL, start, self, MASK_SHOT);
|
|
if (tr.fraction < 1.0)
|
|
{
|
|
if (tr.ent && (tr.ent !=self) && (tr.ent->takedamage))
|
|
T_Damage (tr.ent, self, self, dir, tr.endpos, tr.plane.normal, damage, 0, DAMAGE_ENERGY, MOD_Q1_LG);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
// trace 2
|
|
tr = gi.trace (start, NULL, NULL, end, self, MASK_SHOT);
|
|
|
|
if (self->client->chasetoggle)
|
|
{
|
|
gi.WriteByte (svc_temp_entity);
|
|
gi.WriteByte (TE_MEDIC_CABLE_ATTACK);
|
|
// gi.WriteByte (TE_HEATBEAM);
|
|
gi.WriteShort (self->client->oldplayer - g_edicts);
|
|
gi.WritePosition (start);
|
|
gi.WritePosition (tr.endpos);
|
|
gi.multicast (self->client->oldplayer->s.origin, MULTICAST_PVS);
|
|
// gi.multicast (self->client->oldplayer->s.origin, MULTICAST_ALL);
|
|
}
|
|
else
|
|
{
|
|
gi.WriteByte (svc_temp_entity);
|
|
gi.WriteByte (TE_MEDIC_CABLE_ATTACK);
|
|
// gi.WriteByte (TE_HEATBEAM);
|
|
gi.WriteShort (self - g_edicts);
|
|
gi.WritePosition (start);
|
|
gi.WritePosition (tr.endpos);
|
|
gi.multicast (self->s.origin, MULTICAST_PVS);
|
|
// gi.multicast (self->s.origin, MULTICAST_ALL);
|
|
}
|
|
|
|
if ((tr.ent != self) && (tr.ent->takedamage))
|
|
T_Damage (tr.ent, self, self, dir, tr.endpos, tr.plane.normal, damage, 0, DAMAGE_ENERGY, MOD_Q1_LG);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
q1_fire_firepod
|
|
|
|
Fires a homing fire pod. Used by the Vore (shalrath).
|
|
=================
|
|
*/
|
|
void q1_firepod_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
|
|
{
|
|
// int mod;
|
|
vec3_t origin;
|
|
|
|
if (other == self->owner)
|
|
return;
|
|
|
|
if ( (Q_stricmp(other->classname, "q1_monster_zombie") == 0) || (Q_stricmp(other->classname, "monster_q1_zombie") == 0) )
|
|
T_Damage (other, self, self->owner, vec3_origin, other->s.origin, vec3_origin, 200, 0, 0, 0);
|
|
T_RadiusDamage (self, self->owner, 40, NULL, 40, MOD_Q1_FIREPOD);
|
|
|
|
gi.sound (self, CHAN_AUTO, gi.soundindex ("q1weapons/r_exp3.wav"), 1.0, ATTN_NORM, 0);
|
|
|
|
VectorMA (self->s.origin, -0.02, self->velocity, origin);
|
|
|
|
// gi.WriteByte (svc_temp_entity);
|
|
// gi.WriteByte (TE_Q1_EXPLOSION);
|
|
// FX Paril
|
|
// gi.WriteByte (TE_ROCKET_EXPLOSION);
|
|
// gi.WritePosition (origin);
|
|
// gi.multicast (self->s.origin, MULTICAST_PVS);
|
|
|
|
gi.unlinkentity (self);
|
|
self->solid = SOLID_NOT;
|
|
self->touch = NULL;
|
|
VectorCopy (origin, self->s.origin);
|
|
VectorCopy (origin, self->s.old_origin);
|
|
VectorClear (self->velocity);
|
|
self->s.modelindex = gi.modelindex ("sprites/s_explod.sp2");
|
|
self->s.frame = 0;
|
|
self->s.sound = 0;
|
|
self->s.effects &= ~EF_ANIM_ALLFAST;
|
|
self->think = q1_explode;
|
|
self->nextthink = level.time + FRAMETIME;
|
|
gi.linkentity (self);
|
|
|
|
// q1_explo_particles (self, origin);
|
|
// if (Q_stricmp(self->classname, "freed"))
|
|
// G_FreeEdict (self);
|
|
}
|
|
|
|
|
|
void q1_firepod_home (edict_t *self)
|
|
{
|
|
vec3_t dir, vtemp;
|
|
VectorClear (self->velocity);
|
|
VectorCopy (self->enemy->s.origin, vtemp);
|
|
VectorSet (dir, 0, 0, 10);
|
|
VectorAdd (vtemp, dir, vtemp);
|
|
VectorClear (dir);
|
|
if (self->enemy->health < 1)
|
|
{
|
|
G_FreeEdict(self);
|
|
return;
|
|
}
|
|
if (self->enemy->client && (self->enemy->client->ps.pmove.pm_flags & PMF_DUCKED))
|
|
vtemp[2] -= 10;
|
|
VectorSubtract (vtemp, self->s.origin, dir);
|
|
VectorNormalize (dir);
|
|
if (skill->value >= 2)
|
|
VectorMA (self->velocity, 350, dir, self->velocity);
|
|
else
|
|
VectorMA (self->velocity, 250, dir, self->velocity);
|
|
self->nextthink = level.time + 0.2;
|
|
}
|
|
|
|
|
|
void q1_fire_firepod (edict_t *self, vec3_t dir)
|
|
{
|
|
edict_t *pod;
|
|
trace_t tr;
|
|
vec3_t start, temp;
|
|
|
|
VectorCopy (self->s.origin, start);
|
|
VectorSet (temp, 0, 0, 10);
|
|
VectorAdd (start, temp, start);
|
|
|
|
VectorNormalize (dir);
|
|
|
|
pod = G_Spawn();
|
|
pod->svflags |= SVF_DEADMONSTER;
|
|
// yes, I know it looks weird that projectiles are deadmonsters
|
|
// what this means is that when prediction is used against the object
|
|
// (blaster/hyperblaster shots), the player won't be solid clipped against
|
|
// the object. Right now trying to run into a firing hyperblaster
|
|
// is very jerky since you are predicted 'against' the shots.
|
|
|
|
VectorCopy (start, pod->s.origin);
|
|
VectorCopy (start, pod->s.old_origin);
|
|
vectoangles (dir, pod->s.angles);
|
|
VectorScale (dir, 400, pod->velocity);
|
|
pod->movetype = MOVETYPE_FLYMISSILE;
|
|
pod->clipmask = MASK_SHOT;
|
|
pod->solid = SOLID_BBOX;
|
|
VectorSet (pod->avelocity, 300, 300, 300);
|
|
#ifdef KMQUAKE2_ENGINE_MOD
|
|
pod->s.effects |= EF_FLAG1|EF_BLUEHYPERBLASTER;
|
|
#else
|
|
pod->s.effects |= EF_FLAG1|EF_FLAG2;
|
|
#endif
|
|
VectorClear (pod->mins);
|
|
VectorClear (pod->maxs);
|
|
pod->s.modelindex = gi.modelindex ("models/monsters/q1shalrath/v_spike/tris.md2");
|
|
// pod->s.sound = gi.soundindex ("misc/lasfly.wav");
|
|
pod->touch = q1_firepod_touch;
|
|
pod->nextthink = level.time +.1;
|
|
pod->think = q1_firepod_home;
|
|
pod->classname = "firepod";
|
|
pod->enemy = self->enemy;
|
|
pod->owner = self;
|
|
|
|
pod->common_name = "Firepod";
|
|
pod->class_id = ENTITY_Q1_FIREPOD;
|
|
|
|
gi.linkentity (pod);
|
|
|
|
// if (self->client)
|
|
// check_dodge (self, pod->s.origin, dir, speed);
|
|
|
|
tr = gi.trace (self->s.origin, NULL, NULL, pod->s.origin, pod, MASK_SHOT);
|
|
if (tr.fraction < 1.0)
|
|
{
|
|
VectorMA (pod->s.origin, -10, dir, pod->s.origin);
|
|
pod->touch (pod, tr.ent, NULL, NULL);
|
|
}
|
|
}
|
|
|
|
|
|
void q1_firepod_precache (void)
|
|
{
|
|
gi.modelindex ("models/monsters/q1shalrath/v_spike/tris.md2");
|
|
gi.modelindex ("sprites/s_explod.sp2");
|
|
gi.soundindex ("q1weapons/r_exp3.wav");
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
q1_fire_lavaball
|
|
|
|
Fires a lavaball. Used by Chthon.
|
|
=================
|
|
*/
|
|
/*static*/ void q1_lavaball_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
|
|
{
|
|
vec3_t origin;
|
|
|
|
if (other == ent->owner)
|
|
return;
|
|
|
|
if (surf && (surf->flags & SURF_SKY))
|
|
{
|
|
G_FreeEdict (ent);
|
|
return;
|
|
}
|
|
|
|
// calculate position for the explosion entity
|
|
VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
|
|
|
|
if (other->takedamage)
|
|
{
|
|
T_Damage (other, ent, ent->owner, ent->velocity, ent->s.origin, plane->normal, ent->dmg,(ent->dmg*2), 0, MOD_Q1_RL);
|
|
}
|
|
|
|
T_RadiusDamage(ent, ent->owner, ent->radius_dmg, other, ent->dmg_radius, MOD_Q1_RL_SPLASH);
|
|
gi.sound (ent, CHAN_WEAPON, gi.soundindex ("q1weapons/r_exp3.wav"), 1.0, ATTN_NORM, 0);
|
|
|
|
//
|
|
gi.WriteByte (svc_temp_entity);
|
|
gi.WriteByte (TE_ROCKET_EXPLOSION);
|
|
gi.WritePosition (origin);
|
|
gi.multicast (ent->s.origin, MULTICAST_PVS);
|
|
//
|
|
// explosion sprite
|
|
gi.unlinkentity(ent);
|
|
ent->solid = SOLID_NOT;
|
|
ent->touch = NULL;
|
|
VectorCopy(ent->s.origin, origin);
|
|
VectorCopy(ent->s.origin, ent->s.old_origin);
|
|
VectorClear (ent->velocity);
|
|
ent->s.modelindex = gi.modelindex ("sprites/s_explod.sp2");
|
|
ent->s.frame = 0;
|
|
ent->s.sound = 0;
|
|
ent->s.effects &= ~EF_ANIM_ALLFAST;
|
|
ent->think = q1_explode;
|
|
ent->nextthink = level.time + FRAMETIME;
|
|
ent->enemy = other;
|
|
gi.linkentity (ent);
|
|
}
|
|
|
|
|
|
void q1_fire_lavaball (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage)
|
|
{
|
|
edict_t *lavaball;
|
|
|
|
lavaball = G_Spawn();
|
|
VectorCopy (start, lavaball->s.origin);
|
|
VectorCopy (dir, lavaball->movedir);
|
|
vectoangles (dir, lavaball->s.angles);
|
|
VectorScale (dir, speed, lavaball->velocity);
|
|
lavaball->movetype = MOVETYPE_FLYMISSILE;
|
|
lavaball->clipmask = MASK_SHOT;
|
|
lavaball->solid = SOLID_BBOX;
|
|
lavaball->s.effects |= EF_ROCKET; //EF_FLAG1
|
|
lavaball->s.renderfx |= RF_GLOW;
|
|
|
|
VectorClear (lavaball->mins);
|
|
VectorClear (lavaball->maxs);
|
|
lavaball->s.modelindex = gi.modelindex ("models/monsters/q1chthon/lavaball/tris.md2");
|
|
lavaball->owner = self;
|
|
lavaball->touch = q1_lavaball_touch;
|
|
lavaball->nextthink = level.time + 8000/speed;
|
|
lavaball->think = G_FreeEdict;
|
|
lavaball->dmg = damage;
|
|
lavaball->radius_dmg = radius_damage;
|
|
lavaball->dmg_radius = damage_radius;
|
|
lavaball->s.sound = gi.soundindex ("weapons/rockfly.wav");
|
|
lavaball->classname = "chthon_lavaball";
|
|
|
|
lavaball->common_name = "Chthon Lavaball";
|
|
lavaball->class_id = ENTITY_Q1_LAVABALL;
|
|
|
|
gi.linkentity (lavaball);
|
|
}
|
|
|
|
|
|
void q1_fire_lavaball_precache (void)
|
|
{
|
|
gi.modelindex ("models/monsters/q1chthon/lavaball/tris.md2");
|
|
gi.modelindex ("sprites/s_explod.sp2");
|
|
gi.soundindex ("q1weapons/r_exp3.wav");
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
q1_acidbolt_touch
|
|
|
|
Fires a bolt of acid spit. Used by the Scrag.
|
|
=================
|
|
*/
|
|
/*static*/ void q1_acidbolt_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
|
|
{
|
|
if (other == ent->owner)
|
|
return;
|
|
|
|
if (surf && (surf->flags & SURF_SKY))
|
|
{
|
|
G_FreeEdict (ent);
|
|
return;
|
|
}
|
|
|
|
if (other->takedamage)
|
|
{
|
|
// FIX MOD
|
|
T_Damage (other, ent, ent->owner, ent->velocity, ent->s.origin, plane->normal, ent->dmg, 0, 0, MOD_Q1_RL);
|
|
}
|
|
else
|
|
{
|
|
gi.WriteByte (svc_temp_entity);
|
|
gi.WriteByte (TE_WELDING_SPARKS);
|
|
gi.WriteByte (15);
|
|
gi.WritePosition (ent->s.origin);
|
|
gi.WriteDir ((!plane) ? vec3_origin : plane->normal);
|
|
// gi.WriteByte (209);
|
|
gi.WriteByte (222);
|
|
gi.multicast (ent->s.origin, MULTICAST_PVS);
|
|
|
|
gi.sound (ent, CHAN_VOICE, gi.soundindex ("q1scrag/hit.wav"), 1, ATTN_NORM, 0);
|
|
}
|
|
|
|
G_FreeEdict (ent);
|
|
}
|
|
|
|
/*
|
|
void G_Spawn_Trails(int type, vec3_t start, vec3_t endpos, vec3_t origin ) {
|
|
gi.WriteByte(svc_temp_entity);
|
|
gi.WriteByte(type);
|
|
gi.WritePosition(start);
|
|
gi.WritePosition(endpos);
|
|
gi.multicast(origin, MULTICAST_PVS);
|
|
}
|
|
*/
|
|
|
|
void q1_fire_acidspit (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed)
|
|
{
|
|
edict_t *acidbolt;
|
|
|
|
acidbolt = G_Spawn();
|
|
VectorCopy (start, acidbolt->s.origin);
|
|
VectorCopy (dir, acidbolt->movedir);
|
|
vectoangles (dir, acidbolt->s.angles);
|
|
VectorScale (dir, speed, acidbolt->velocity);
|
|
acidbolt->movetype = MOVETYPE_FLYMISSILE;
|
|
acidbolt->clipmask = MASK_SHOT;
|
|
acidbolt->solid = SOLID_BBOX;
|
|
acidbolt->s.effects |= EF_GREENGIB; // EF_HYPERBLASTER EF_BFG EF_GREENTRAIL
|
|
acidbolt->s.renderfx |= RF_TRANSLUCENT; // FULLBRIGHT
|
|
|
|
VectorClear (acidbolt->mins);
|
|
VectorClear (acidbolt->maxs);
|
|
acidbolt->s.modelindex = gi.modelindex ("models/monsters/q1scrag/w_spike/tris.md2");
|
|
acidbolt->owner = self;
|
|
acidbolt->touch = q1_acidbolt_touch;
|
|
acidbolt->nextthink = level.time + 8000/speed;
|
|
acidbolt->think = G_FreeEdict;
|
|
acidbolt->dmg = damage;
|
|
// acidbolt->s.sound = gi.soundindex ("weapons/rockfly.wav");
|
|
acidbolt->classname = "acidbolt";
|
|
|
|
acidbolt->common_name = "Acid Bolt";
|
|
acidbolt->class_id = ENTITY_Q1_ACIDBOLT;
|
|
|
|
gi.linkentity (acidbolt);
|
|
}
|
|
|
|
|
|
void q1_acidspit_precache (void)
|
|
{
|
|
gi.modelindex ("models/monsters/q1scrag/w_spike/tris.md2");
|
|
gi.soundindex ("q1scrag/hit.wav");
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
q1_fire_gib
|
|
|
|
Fires a gib projectile. Used by the Zombie.
|
|
=================
|
|
*/
|
|
/*static*/ void q1_zombiegib_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
|
|
{
|
|
if (other == ent->owner)
|
|
return;
|
|
|
|
if (surf && (surf->flags & SURF_SKY))
|
|
{
|
|
G_FreeEdict (ent);
|
|
return;
|
|
}
|
|
|
|
if (other->takedamage)
|
|
{
|
|
// FIX MOD
|
|
T_Damage (other, ent, ent->owner, ent->velocity, ent->s.origin, plane->normal, ent->dmg, 0, 0, MOD_Q1_RL);
|
|
gi.sound (ent, CHAN_RELIABLE|CHAN_WEAPON, gi.soundindex ("q1zombie/z_hit.wav"), 1.0, ATTN_NORM, 0);
|
|
}
|
|
else
|
|
{
|
|
gi.sound (ent, CHAN_RELIABLE|CHAN_WEAPON, gi.soundindex ("q1zombie/z_miss.wav"), 1.0, ATTN_NORM, 0);
|
|
gi.WriteByte (svc_temp_entity);
|
|
gi.WriteByte (TE_BLOOD);
|
|
gi.WritePosition (ent->s.origin);
|
|
gi.WriteDir (plane->normal);
|
|
gi.multicast (ent->s.origin, MULTICAST_PVS);
|
|
}
|
|
|
|
// no more touches
|
|
ent->touch = NULL;
|
|
|
|
ent->nextthink = level.time + 3;
|
|
ent->think = G_FreeEdict;
|
|
// G_FreeEdict (ent);
|
|
}
|
|
|
|
|
|
void q1_fire_gib (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed)
|
|
{
|
|
edict_t *gib;
|
|
vec3_t dir;
|
|
vec3_t up; //forward, right,
|
|
|
|
vectoangles (aimdir, dir);
|
|
AngleVectors (dir, NULL, NULL, up);
|
|
|
|
gib = G_Spawn();
|
|
VectorCopy (start, gib->s.origin);
|
|
VectorScale (aimdir, speed, gib->velocity);
|
|
VectorMA (gib->velocity, 200, up, gib->velocity);
|
|
// VectorMA (gib->velocity, 20, right, gib->velocity);
|
|
|
|
VectorSet (gib->avelocity, 3000, 1000, 2000);
|
|
gib->movetype = MOVETYPE_BOUNCE;
|
|
gib->clipmask = MASK_SHOT;
|
|
gib->solid = SOLID_BBOX;
|
|
gib->s.effects |= EF_GIB;
|
|
VectorClear (gib->mins);
|
|
VectorClear (gib->maxs);
|
|
gib->s.modelindex = gi.modelindex ("models/monsters/q1zombie/gib/tris.md2");
|
|
gib->owner = self;
|
|
gib->touch = q1_zombiegib_touch;
|
|
gib->nextthink = level.time + 2.5;
|
|
// gib->think = G_FreeEdict;
|
|
gib->think = gib_fade; // use gib_fade() instead of directly removing
|
|
gib->dmg = damage;
|
|
gib->classname = "gib";
|
|
|
|
gib->common_name = "Gib Projectile";
|
|
gib->class_id = ENTITY_Q1_ZOMBIE_GIB;
|
|
|
|
gi.linkentity (gib);
|
|
}
|
|
|
|
|
|
void q1_gib_precache (void)
|
|
{
|
|
gi.modelindex ("models/monsters/q1zombie/gib/tris.md2");
|
|
gi.soundindex ("q1zombie/z_hit.wav");
|
|
gi.soundindex ("q1zombie/z_miss.wav");
|
|
}
|