mirror of
https://github.com/blendogames/thirtyflightsofloving.git
synced 2024-11-15 00:41:21 +00:00
653 lines
18 KiB
C
653 lines
18 KiB
C
|
#include "g_local.h"
|
||
|
|
||
|
/*
|
||
|
==============================================================================
|
||
|
|
||
|
SHOCKWAVE
|
||
|
|
||
|
==============================================================================
|
||
|
*/
|
||
|
|
||
|
void ShockQuake (edict_t *self)
|
||
|
{
|
||
|
int i;
|
||
|
edict_t *ent;
|
||
|
|
||
|
for (i = 1, ent = g_edicts+1; i < globals.num_edicts; i++, ent++)
|
||
|
{
|
||
|
if ( !ent->inuse || !ent->client || !ent->groundentity )
|
||
|
continue;
|
||
|
|
||
|
ent->groundentity = NULL;
|
||
|
ent->velocity[0] += crandom() * 125.0f;
|
||
|
ent->velocity[1] += crandom() * 125.0f;
|
||
|
ent->velocity[2] += self->speed * (150.0f / ent->mass);
|
||
|
}
|
||
|
|
||
|
/* if (!strcmp(self->classname, "shocksphere")) // remove shock sphere after x bounces
|
||
|
{
|
||
|
if (self->count > sk_shockwave_bounces->value)
|
||
|
G_FreeEdict (self);
|
||
|
return; //don't loop
|
||
|
}*/
|
||
|
|
||
|
if (level.time < self->timestamp)
|
||
|
self->nextthink = level.time + FRAMETIME;
|
||
|
else
|
||
|
G_FreeEdict (self);
|
||
|
}
|
||
|
|
||
|
void shock_effect_think (edict_t *self)
|
||
|
{
|
||
|
if (++self->s.frame < 19)
|
||
|
self->nextthink = level.time + FRAMETIME;
|
||
|
else
|
||
|
{
|
||
|
self->s.frame = 0;
|
||
|
self->nextthink = level.time + FRAMETIME;
|
||
|
}
|
||
|
self->count--;
|
||
|
|
||
|
// fade out
|
||
|
#ifdef KMQUAKE2_ENGINE_MOD
|
||
|
if (self->count <= 6)
|
||
|
self->s.alpha -= 0.10;
|
||
|
if (self->s.alpha < 0.10)
|
||
|
self->s.alpha = 0.10;
|
||
|
#else
|
||
|
if (self->count == 5)
|
||
|
{
|
||
|
self->s.effects |= EF_SPHERETRANS;
|
||
|
self->s.renderfx &= ~RF_TRANSLUCENT;
|
||
|
}
|
||
|
#endif
|
||
|
// remove after 6 secs
|
||
|
if (self->count == 0)
|
||
|
G_FreeEdict (self);
|
||
|
// inflict field damage on surroundings
|
||
|
T_RadiusDamage(self, self->owner, self->radius_dmg, NULL, self->dmg_radius, MOD_SHOCK_SPLASH);
|
||
|
}
|
||
|
|
||
|
void shock_effect_center_think (edict_t *self)
|
||
|
{
|
||
|
self->nextthink = level.time + FRAMETIME;
|
||
|
if ((self->count % 5) == 0)
|
||
|
{
|
||
|
if (self->count > 10) // double effect for first 40 seconds
|
||
|
{
|
||
|
gi.WriteByte (svc_temp_entity);
|
||
|
gi.WriteByte (TE_NUKEBLAST);
|
||
|
gi.WritePosition (self->s.origin);
|
||
|
gi.multicast (self->s.origin, MULTICAST_ALL);
|
||
|
}
|
||
|
gi.WriteByte (svc_temp_entity);
|
||
|
gi.WriteByte (TE_NUKEBLAST);
|
||
|
gi.WritePosition (self->s.origin);
|
||
|
gi.multicast (self->s.origin, MULTICAST_ALL);
|
||
|
}
|
||
|
// fade out
|
||
|
#ifdef KMQUAKE2_ENGINE_MOD
|
||
|
if (self->count <= 10)
|
||
|
self->s.alpha -= 0.10;
|
||
|
self->s.alpha = max(self->s.alpha, 1/255);
|
||
|
// remove after 5 secs
|
||
|
self->count--;
|
||
|
if (self->count == 0 || self->s.alpha <= 1/255)
|
||
|
G_FreeEdict (self);
|
||
|
#else
|
||
|
if (self->count == 10)
|
||
|
self->s.renderfx |= RF_TRANSLUCENT;
|
||
|
if (self->count == 5)
|
||
|
{
|
||
|
self->s.effects |= EF_SPHERETRANS;
|
||
|
self->s.renderfx &= ~RF_TRANSLUCENT;
|
||
|
}
|
||
|
// remove after 5 secs
|
||
|
self->count--;
|
||
|
if (self->count == 0)
|
||
|
G_FreeEdict (self);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void ShockEffect (edict_t *source, edict_t *attacker, float damage, float radius, cplane_t *plane)
|
||
|
{
|
||
|
edict_t *ent;
|
||
|
edict_t *center;
|
||
|
vec3_t hit_point;
|
||
|
|
||
|
if (plane->normal)
|
||
|
{ // put origin of effect 32 units away from last hit surface
|
||
|
VectorMA (source->s.origin, 32.0, plane->normal, hit_point);
|
||
|
}
|
||
|
|
||
|
ent = G_Spawn();
|
||
|
// same origin as exploding shock sphere
|
||
|
if (plane->normal)
|
||
|
VectorCopy (hit_point, ent->s.origin);
|
||
|
else
|
||
|
VectorCopy (source->s.origin, ent->s.origin);
|
||
|
ent->radius_dmg = sk_shockwave_effect_damage->value;
|
||
|
ent->dmg_radius = sk_shockwave_effect_radius->value;
|
||
|
ent->owner = attacker;
|
||
|
ent->movetype = MOVETYPE_NONE;
|
||
|
ent->solid = SOLID_NOT;
|
||
|
VectorSet (ent->mins, -8, -8, 8);
|
||
|
VectorSet (ent->maxs, 8, 8, 8);
|
||
|
ent->s.modelindex = gi.modelindex ("models/objects/shockfield/tris.md2");
|
||
|
#ifdef KMQUAKE2_ENGINE_MOD
|
||
|
ent->s.alpha = 0.70;
|
||
|
#else
|
||
|
ent->s.renderfx |= RF_TRANSLUCENT;
|
||
|
#endif
|
||
|
ent->s.renderfx |= RF_NOSHADOW|RF_FULLBRIGHT;
|
||
|
ent->s.effects = EF_FLAG2;
|
||
|
ent->count = 60; // lasts 6 seconds
|
||
|
ent->think = shock_effect_think;
|
||
|
ent->nextthink = level.time + 2 * FRAMETIME;
|
||
|
gi.linkentity (ent);
|
||
|
|
||
|
// center light burst effect
|
||
|
center = G_Spawn();
|
||
|
// same origin as exploding shock sphere
|
||
|
if (plane->normal)
|
||
|
VectorCopy (hit_point, center->s.origin);
|
||
|
else
|
||
|
VectorCopy (source->s.origin, center->s.origin);
|
||
|
center->movetype = MOVETYPE_NONE;
|
||
|
center->solid = SOLID_NOT;
|
||
|
VectorSet (center->mins, -8, -8, 8);
|
||
|
VectorSet (center->maxs, 8, 8, 8);
|
||
|
center->s.modelindex = gi.modelindex ("sprites/s_trap.sp2");
|
||
|
center->s.effects |= EF_FLAG2 | EF_ANIM_ALLFAST;
|
||
|
#ifdef KMQUAKE2_ENGINE_MOD
|
||
|
center->s.alpha = 0.90;
|
||
|
#endif
|
||
|
center->count = 50; // lasts 5 seconds
|
||
|
center->think = shock_effect_center_think;
|
||
|
center->nextthink = level.time + 2 * FRAMETIME;
|
||
|
ent->teamchain = center;
|
||
|
gi.linkentity (center);
|
||
|
}
|
||
|
|
||
|
#ifndef KMQUAKE2_ENGINE_MOD
|
||
|
void ShockSplashThink (edict_t *self)
|
||
|
{
|
||
|
self->s.frame++;
|
||
|
if (self->s.frame > 8)
|
||
|
{
|
||
|
G_FreeEdict(self);
|
||
|
return;
|
||
|
}
|
||
|
self->nextthink = level.time + FRAMETIME;
|
||
|
}
|
||
|
#else
|
||
|
// Gotta have this for extractfuncs...
|
||
|
void ShockSplashThink (edict_t *self)
|
||
|
{
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
void ShockSplash(edict_t *self, cplane_t *plane)
|
||
|
{
|
||
|
#ifdef KMQUAKE2_ENGINE_MOD // use new client-side effect
|
||
|
vec3_t shockdir;
|
||
|
|
||
|
if (!plane->normal)
|
||
|
AngleVectors(self->s.angles, shockdir, NULL, NULL);
|
||
|
else
|
||
|
VectorCopy (plane->normal, shockdir);
|
||
|
|
||
|
gi.WriteByte (svc_temp_entity);
|
||
|
gi.WriteByte (TE_SHOCKSPLASH);
|
||
|
gi.WritePosition (self->s.origin);
|
||
|
gi.WriteDir (shockdir);
|
||
|
gi.multicast (self->s.origin, MULTICAST_ALL);
|
||
|
|
||
|
// Lazarus reflections
|
||
|
if (level.num_reflectors)
|
||
|
ReflectSparks (TE_SHOCKSPLASH, self->s.origin, shockdir);
|
||
|
|
||
|
#else
|
||
|
edict_t *shockring;
|
||
|
int i;
|
||
|
|
||
|
gi.sound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, gi.soundindex ("weapons/shockhit.wav"), 1, ATTN_NONE, 0);
|
||
|
|
||
|
for (i = 0; i < 5; i++)
|
||
|
{
|
||
|
shockring = G_Spawn();
|
||
|
shockring->classname = "shock_ring";
|
||
|
if (plane->normal)
|
||
|
{ // put origin of impact effect 16*i+1 units away from last hit surface
|
||
|
VectorMA (self->s.origin, 16.0*(i+1), plane->normal, shockring->s.origin);
|
||
|
vectoangles(plane->normal, shockring->s.angles);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
VectorCopy (self->s.origin, shockring->s.origin);
|
||
|
VectorCopy (self->s.angles, shockring->s.angles);
|
||
|
}
|
||
|
shockring->solid = SOLID_NOT;
|
||
|
shockring->movetype = MOVETYPE_NONE;
|
||
|
shockring->owner = self;
|
||
|
shockring->s.modelindex = gi.modelindex("models/objects/shocksplash/tris.md2");
|
||
|
shockring->s.frame = (4-i);
|
||
|
shockring->s.effects |= EF_SPHERETRANS;
|
||
|
shockring->s.renderfx |= (RF_FULLBRIGHT | RF_NOSHADOW);
|
||
|
shockring->nextthink = level.time + FRAMETIME;
|
||
|
shockring->think = ShockSplashThink;
|
||
|
gi.linkentity (shockring);
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void shock_sphere_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
|
||
|
{
|
||
|
vec3_t origin;
|
||
|
edict_t *impact;
|
||
|
|
||
|
// ignore other projectiles
|
||
|
if ((other->movetype == MOVETYPE_FLYMISSILE) || (other->movetype == MOVETYPE_BOUNCE)
|
||
|
|| (other->movetype == MOVETYPE_WALLBOUNCE))
|
||
|
return;
|
||
|
|
||
|
ent->count++; // add to count
|
||
|
ent->movetype = MOVETYPE_BOUNCE;
|
||
|
|
||
|
if (other == ent->owner)
|
||
|
return;
|
||
|
|
||
|
// detonate if hit a monster
|
||
|
if (other->takedamage)
|
||
|
ent->count = sk_shockwave_bounces->value + 1;
|
||
|
|
||
|
if ( (ent->velocity[0] < 20) && (ent->velocity[0] > -20)
|
||
|
&& (ent->velocity[1] < 20) && (ent->velocity[1] > -20)
|
||
|
&& (ent->velocity[2] < 20) && (ent->velocity[2] > -20) )
|
||
|
ent->count = sk_shockwave_bounces->value + 1;
|
||
|
|
||
|
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_SHOCK_SPHERE);
|
||
|
T_RadiusDamage(ent, ent->owner, ent->radius_dmg, other, ent->dmg_radius, MOD_SHOCK_SPLASH);
|
||
|
|
||
|
// spawn impact
|
||
|
impact = G_Spawn();
|
||
|
impact->classname = "shock_impact";
|
||
|
VectorCopy (ent->s.origin, impact->s.origin);
|
||
|
gi.linkentity (impact);
|
||
|
impact->think = ShockQuake;
|
||
|
impact->speed = 250;
|
||
|
impact->nextthink = level.time + FRAMETIME;
|
||
|
|
||
|
if ((ent->count <= sk_shockwave_bounces->value) && !other->takedamage) //no shock rings if hit a monster
|
||
|
{
|
||
|
ShockSplash (ent, plane); // Spawn shock rings
|
||
|
|
||
|
if (impact)
|
||
|
impact->timestamp = level.time + 3;
|
||
|
}
|
||
|
else // don't exploode until final hit or hit a monster
|
||
|
{
|
||
|
gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, gi.soundindex ("weapons/shockexp.wav"), 1, ATTN_NONE, 0);
|
||
|
|
||
|
ShockEffect (ent, ent->owner, ent->radius_dmg, ent->dmg_radius, plane);
|
||
|
|
||
|
if (impact)
|
||
|
impact->timestamp = level.time + 6;
|
||
|
|
||
|
// remove after x bounces
|
||
|
G_FreeEdict (ent);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void shock_sphere_think (edict_t *sphere)
|
||
|
{
|
||
|
sphere->avelocity[PITCH] = 80;
|
||
|
sphere->avelocity[ROLL] = 80;
|
||
|
sphere->nextthink = level.time + FRAMETIME;
|
||
|
}
|
||
|
|
||
|
void fire_shock_sphere (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage)
|
||
|
{
|
||
|
edict_t *sphere;
|
||
|
|
||
|
sphere = G_Spawn();
|
||
|
sphere->classname = "shocksphere";
|
||
|
sphere->class_id = ENTITY_SHOCK_SPHERE;
|
||
|
VectorCopy (start, sphere->s.origin);
|
||
|
VectorCopy (dir, sphere->movedir);
|
||
|
vectoangles (dir, sphere->s.angles);
|
||
|
VectorScale (dir, speed, sphere->velocity);
|
||
|
sphere->avelocity[PITCH] = 80;
|
||
|
sphere->avelocity[ROLL] = 80;
|
||
|
sphere->movetype = MOVETYPE_WALLBOUNCE;
|
||
|
sphere->count = 0;
|
||
|
sphere->clipmask = MASK_SHOT;
|
||
|
sphere->solid = SOLID_BBOX;
|
||
|
VectorClear (sphere->mins);
|
||
|
VectorClear (sphere->maxs);
|
||
|
VectorSet (sphere->mins, -14, -14, -14);
|
||
|
VectorSet (sphere->maxs, 14, 14, 14);
|
||
|
sphere->s.modelindex = gi.modelindex ("models/objects/shocksphere/tris.md2");
|
||
|
sphere->s.renderfx |= RF_IR_VISIBLE;
|
||
|
sphere->owner = self;
|
||
|
sphere->touch = shock_sphere_touch;
|
||
|
sphere->timestamp = level.time;
|
||
|
sphere->nextthink = level.time + 0.1;
|
||
|
sphere->think = shock_sphere_think;
|
||
|
sphere->dmg = damage;
|
||
|
sphere->radius_dmg = radius_damage;
|
||
|
sphere->dmg_radius = damage_radius;
|
||
|
|
||
|
if (self->client)
|
||
|
check_dodge (self, sphere->s.origin, dir, speed);
|
||
|
gi.linkentity (sphere);
|
||
|
}
|
||
|
|
||
|
// NOTE: SP_shocksphere should ONLY be used to spawn shockspheres that change maps
|
||
|
// via a trigger_transition. It should NOT be used for map entities.
|
||
|
|
||
|
void shocksphere_delayed_start (edict_t *shocksphere)
|
||
|
{
|
||
|
if (g_edicts[1].linkcount)
|
||
|
{
|
||
|
VectorScale(shocksphere->movedir,shocksphere->moveinfo.speed,shocksphere->velocity);
|
||
|
if (shocksphere->count > 0)
|
||
|
shocksphere->movetype = MOVETYPE_BOUNCE;
|
||
|
else
|
||
|
shocksphere->movetype = MOVETYPE_WALLBOUNCE;
|
||
|
shocksphere->nextthink = level.time + FRAMETIME;
|
||
|
shocksphere->think = shock_sphere_think;
|
||
|
gi.linkentity(shocksphere);
|
||
|
}
|
||
|
else
|
||
|
shocksphere->nextthink = level.time + FRAMETIME;
|
||
|
}
|
||
|
|
||
|
void SP_shocksphere (edict_t *shocksphere)
|
||
|
{
|
||
|
shocksphere->s.modelindex = gi.modelindex ("models/objects/shocksphere/tris.md2");
|
||
|
shocksphere->touch = shock_sphere_touch;
|
||
|
|
||
|
// For SP, freeze shocksphere until player spawns in
|
||
|
if (game.maxclients == 1)
|
||
|
{
|
||
|
shocksphere->movetype = MOVETYPE_NONE;
|
||
|
VectorCopy(shocksphere->velocity,shocksphere->movedir);
|
||
|
VectorNormalize(shocksphere->movedir);
|
||
|
shocksphere->moveinfo.speed = VectorLength(shocksphere->velocity);
|
||
|
VectorClear(shocksphere->velocity);
|
||
|
shocksphere->think = shocksphere_delayed_start;
|
||
|
shocksphere->nextthink = level.time + FRAMETIME;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (shocksphere->count > 0)
|
||
|
shocksphere->movetype = MOVETYPE_BOUNCE;
|
||
|
else
|
||
|
shocksphere->movetype = MOVETYPE_WALLBOUNCE;
|
||
|
shocksphere->nextthink = level.time + FRAMETIME;
|
||
|
shocksphere->think = shock_sphere_think;
|
||
|
}
|
||
|
gi.linkentity (shocksphere);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
==============================================================================
|
||
|
|
||
|
BLU-86 (aka NEUTRON BOMB)
|
||
|
|
||
|
==============================================================================
|
||
|
*/
|
||
|
#define NBOMB_DELAY 4
|
||
|
#define NBOMB_TIME_TO_LIVE 6
|
||
|
//#define NBOMB_TIME_TO_LIVE 40
|
||
|
#define NBOMB_RADIUS 256
|
||
|
#define NBOMB_DAMAGE 5000
|
||
|
#define NBOMB_QUAKE_TIME 3
|
||
|
#define NBOMB_QUAKE_STRENGTH 100
|
||
|
|
||
|
void Nbomb_Quake (edict_t *self)
|
||
|
{
|
||
|
int i;
|
||
|
edict_t *e;
|
||
|
|
||
|
if (self->last_move_time < level.time)
|
||
|
{
|
||
|
gi.positioned_sound (self->s.origin, self, CHAN_AUTO, self->noise_index, 0.75, ATTN_NONE, 0);
|
||
|
self->last_move_time = level.time + 0.5;
|
||
|
}
|
||
|
|
||
|
for (i=1, e=g_edicts+i; i < globals.num_edicts; i++,e++)
|
||
|
{
|
||
|
if (!e->inuse)
|
||
|
continue;
|
||
|
if (!e->client)
|
||
|
continue;
|
||
|
if (!e->groundentity)
|
||
|
continue;
|
||
|
|
||
|
e->groundentity = NULL;
|
||
|
e->velocity[0] += crandom()* 150.0f;
|
||
|
e->velocity[1] += crandom()* 150.0f;
|
||
|
e->velocity[2] = self->speed * (100.0f / e->mass);
|
||
|
}
|
||
|
|
||
|
if (level.time < self->timestamp)
|
||
|
self->nextthink = level.time + FRAMETIME;
|
||
|
else
|
||
|
G_FreeEdict (self);
|
||
|
}
|
||
|
|
||
|
|
||
|
void Nbomb_Explode (edict_t *ent)
|
||
|
{
|
||
|
if (ent->teammaster->client)
|
||
|
PlayerNoise(ent->teammaster, ent->s.origin, PNOISE_IMPACT);
|
||
|
|
||
|
T_RadiusNukeDamage(ent, ent->teammaster, ent->dmg, ent, ent->dmg_radius, MOD_NBOMB);
|
||
|
|
||
|
if (ent->dmg > sk_nbomb_damage->value)
|
||
|
{
|
||
|
if (ent->dmg < (4 * sk_nbomb_damage->value)) // double sound
|
||
|
gi.sound(ent, CHAN_ITEM, gi.soundindex("misc/ddamage3.wav"), 1, ATTN_NORM, 0);
|
||
|
else // quad sound
|
||
|
gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage3.wav"), 1, ATTN_NORM, 0);
|
||
|
}
|
||
|
|
||
|
gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, gi.soundindex ("weapons/grenlx1a.wav"), 1, ATTN_NONE, 0);
|
||
|
|
||
|
|
||
|
gi.WriteByte (svc_temp_entity);
|
||
|
gi.WriteByte (TE_EXPLOSION1_BIG);
|
||
|
gi.WritePosition (ent->s.origin);
|
||
|
gi.multicast (ent->s.origin, MULTICAST_PVS);
|
||
|
|
||
|
gi.WriteByte (svc_temp_entity);
|
||
|
gi.WriteByte (TE_NUKEBLAST);
|
||
|
gi.WritePosition (ent->s.origin);
|
||
|
gi.multicast (ent->s.origin, MULTICAST_ALL);
|
||
|
|
||
|
// Lazarus reflections
|
||
|
if (level.num_reflectors)
|
||
|
{
|
||
|
ReflectExplosion (TE_EXPLOSION1_BIG, ent->s.origin);
|
||
|
// ReflectExplosion (TE_NUKEBLAST, ent->s.origin);
|
||
|
}
|
||
|
|
||
|
// become a quake
|
||
|
ent->svflags |= SVF_NOCLIENT;
|
||
|
ent->noise_index = gi.soundindex ("world/rumble.wav");
|
||
|
ent->think = Nbomb_Quake;
|
||
|
ent->speed = NBOMB_QUAKE_STRENGTH;
|
||
|
ent->timestamp = level.time + NBOMB_QUAKE_TIME;
|
||
|
ent->nextthink = level.time + FRAMETIME;
|
||
|
ent->last_move_time = 0;
|
||
|
}
|
||
|
|
||
|
void nbomb_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
|
||
|
{
|
||
|
self->takedamage = DAMAGE_NO;
|
||
|
if ( (attacker) && !(strcmp(attacker->classname, "nbomb")) )
|
||
|
{
|
||
|
// if ((g_showlogic) && (g_showlogic->value))
|
||
|
// gi.dprintf ("nbomb nuked by a nbomb, not nuking\n");
|
||
|
G_FreeEdict (self);
|
||
|
return;
|
||
|
}
|
||
|
Nbomb_Explode(self);
|
||
|
}
|
||
|
|
||
|
void Nbomb_Think (edict_t *ent)
|
||
|
{
|
||
|
float attenuation, default_atten = 1.8;
|
||
|
int muzzleflash;
|
||
|
|
||
|
attenuation = default_atten/3.0;
|
||
|
muzzleflash = MZ_NUKE4;
|
||
|
|
||
|
if (ent->wait < level.time)
|
||
|
Nbomb_Explode(ent);
|
||
|
else if (level.time >= (ent->wait - sk_nbomb_life->value))
|
||
|
{
|
||
|
if (gi.pointcontents (ent->s.origin) & (CONTENTS_SLIME|CONTENTS_LAVA))
|
||
|
{
|
||
|
Nbomb_Explode (ent);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Knightmare- remove smoke trail if we've stopped moving
|
||
|
if ( ent->groundentity && !VectorLength(ent->velocity) )
|
||
|
ent->s.effects &= ~EF_GRENADE;
|
||
|
// but restore it if we go flying
|
||
|
else if ( !ent->groundentity )
|
||
|
ent->s.effects |= EF_GRENADE;
|
||
|
|
||
|
ent->think = Nbomb_Think;
|
||
|
ent->nextthink = level.time + 0.1;
|
||
|
ent->health = 1;
|
||
|
ent->owner = NULL;
|
||
|
|
||
|
gi.WriteByte (svc_muzzleflash);
|
||
|
gi.WriteShort (ent-g_edicts);
|
||
|
gi.WriteByte (muzzleflash);
|
||
|
gi.multicast (ent->s.origin, MULTICAST_PVS);
|
||
|
|
||
|
if (ent->timestamp <= level.time)
|
||
|
{
|
||
|
/* gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/nukewarn.wav"), 1, ATTN_NORM, 0);
|
||
|
ent->timestamp += 10.0;
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
if ((ent->wait - level.time) <= (sk_nbomb_life->value / 2.0))
|
||
|
{
|
||
|
// gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, ATTN_NORM, 0);
|
||
|
gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, attenuation, 0);
|
||
|
// gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, ATTN_NORM, 0);
|
||
|
ent->timestamp = level.time + 0.3;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, attenuation, 0);
|
||
|
// gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, ATTN_NORM, 0);
|
||
|
ent->timestamp = level.time + 0.5;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (ent->timestamp <= level.time)
|
||
|
{
|
||
|
gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, attenuation, 0);
|
||
|
// gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, ATTN_NORM, 0);
|
||
|
// gi.dprintf ("time %2.2f\n", ent->wait-level.time);
|
||
|
ent->timestamp = level.time + 1.0;
|
||
|
}
|
||
|
ent->nextthink = level.time + FRAMETIME;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void nbomb_bounce (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
|
||
|
{
|
||
|
if (random() > 0.5)
|
||
|
gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb1a.wav"), 1, ATTN_NORM, 0);
|
||
|
else
|
||
|
gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb2a.wav"), 1, ATTN_NORM, 0);
|
||
|
}
|
||
|
|
||
|
|
||
|
extern byte P_DamageModifier(edict_t *ent);
|
||
|
|
||
|
void fire_nbomb (edict_t *self, vec3_t start, vec3_t aimdir, int speed)
|
||
|
{
|
||
|
edict_t *nbomb;
|
||
|
vec3_t dir;
|
||
|
vec3_t forward, right, up;
|
||
|
int damage_modifier;
|
||
|
|
||
|
damage_modifier = (int)P_DamageModifier (self);
|
||
|
|
||
|
vectoangles2 (aimdir, dir);
|
||
|
AngleVectors (dir, forward, right, up);
|
||
|
|
||
|
nbomb = G_Spawn();
|
||
|
VectorCopy (start, nbomb->s.origin);
|
||
|
VectorScale (aimdir, speed, nbomb->velocity);
|
||
|
|
||
|
VectorMA (nbomb->velocity, 200 + crandom() * 10.0, up, nbomb->velocity);
|
||
|
VectorMA (nbomb->velocity, crandom() * 10.0, right, nbomb->velocity);
|
||
|
// Knightmare- add player's base velocity to nbomb
|
||
|
if (self->groundentity)
|
||
|
VectorAdd (nbomb->velocity, self->groundentity->velocity, nbomb->velocity);
|
||
|
|
||
|
VectorClear (nbomb->avelocity);
|
||
|
VectorClear (nbomb->s.angles);
|
||
|
nbomb->movetype = MOVETYPE_BOUNCE;
|
||
|
nbomb->clipmask = MASK_SHOT;
|
||
|
nbomb->solid = SOLID_BBOX;
|
||
|
nbomb->s.effects |= EF_GRENADE;
|
||
|
nbomb->s.renderfx |= RF_IR_VISIBLE;
|
||
|
VectorSet (nbomb->mins, -8, -8, -16);
|
||
|
VectorSet (nbomb->maxs, 8, 8, 14);
|
||
|
nbomb->s.modelindex = gi.modelindex ("models/items/ammo/nbomb/tris.md2");
|
||
|
nbomb->owner = self;
|
||
|
nbomb->teammaster = self;
|
||
|
nbomb->wait = level.time + sk_nbomb_delay->value + sk_nbomb_life->value;
|
||
|
nbomb->nextthink = level.time + FRAMETIME;
|
||
|
nbomb->think = Nbomb_Think;
|
||
|
nbomb->touch = nbomb_bounce;
|
||
|
|
||
|
nbomb->health = 10000;
|
||
|
nbomb->takedamage = DAMAGE_YES;
|
||
|
nbomb->svflags |= SVF_DAMAGEABLE;
|
||
|
nbomb->dmg = sk_nbomb_damage->value * damage_modifier;
|
||
|
if (damage_modifier == 1)
|
||
|
nbomb->dmg_radius = sk_nbomb_radius->value;
|
||
|
else
|
||
|
nbomb->dmg_radius = sk_nbomb_radius->value + sk_nbomb_radius->value * (0.25*(float)damage_modifier);
|
||
|
// this yields 1.0, 1.5, 2.0, 3.0 times radius
|
||
|
|
||
|
// if ((g_showlogic) && (g_showlogic->value))
|
||
|
// gi.dprintf ("nbomb modifier = %d, damage = %d, radius = %f\n", damage_modifier, nbomb->dmg, nbomb->dmg_radius);
|
||
|
|
||
|
nbomb->classname = "nbomb";
|
||
|
nbomb->class_id = ENTITY_NBOMB;
|
||
|
nbomb->die = nbomb_die;
|
||
|
|
||
|
gi.linkentity (nbomb);
|
||
|
}
|