heretic2-sdk/Toolkit/Programming/GameCode/game/spl_BlueRing.c
1998-11-24 00:00:00 +00:00

264 lines
7.7 KiB
C

//
// spl_bluering.c
//
// Heretic II
// Copyright 1998 Raven Software
//
#include "g_local.h"
#include "fx.h"
#include "random.h"
#include "vector.h"
#include "g_playstats.h"
#include "p_actions2.h"
extern void AlertMonsters (edict_t *self, edict_t *enemy, float lifetime, qboolean ignore_shadows);
extern edict_t *PhoenixMissileReflect(edict_t *missile, edict_t *other, vec3_t vel);
extern edict_t *RedRainMissileReflect(edict_t *self, edict_t *other, vec3_t vel);
extern edict_t *MeteorBarrierReflect(edict_t *self, edict_t *other, vec3_t vel);
extern edict_t *SphereReflect(edict_t *self, edict_t *other, vec3_t vel);
extern edict_t *HellboltReflect(edict_t *self, edict_t *other, vec3_t vel);
extern edict_t *MorphReflect(edict_t *self, edict_t *other, vec3_t vel);
// Since findradius is not specific enough for our needs
// This, for one, will seek out player maceballs, arrows, and meteors.
#define NEW_RING (1)
#if !NEW_RING
edict_t *findringradius (edict_t *from, vec3_t org, float rad, edict_t *Caster)
{
vec3_t eorg;
int j;
if (!from)
from = g_edicts;
else
from++;
for ( ; from < &g_edicts[globals.num_edicts]; from++)
{
if (!from->inuse || (from->solid == SOLID_NOT) || (from->reflected_time == Caster->timestamp) || (from == Caster->owner))
continue;
// if we aren't in the same y band, don't affect anyone
if ((abs(from->s.origin[2] - Caster->s.origin[2])) > 35.0)
continue;
// if we've already reflected this, don't do it again.
from->reflected_time = Caster->timestamp;
for (j=0 ; j<3 ; j++)
eorg[j] = org[j] - (from->s.origin[j] + (from->mins[j] + from->maxs[j])*0.5);
if (VectorLength(eorg) > rad)
continue;
return from;
}
return NULL;
}
#else
edict_t *findringradius (edict_t *from, vec3_t org, float rad, edict_t *Caster)
{
static float max2;
static vec3_t min;
static vec3_t max;
vec3_t eorg;
int j;
float elen;
if (!from)
{
max2=rad*rad;
VectorCopy(org,min);
VectorCopy(org,max);
for (j=0 ; j<3 ; j++)
{
min[j]-=rad;
max[j]+=rad;
}
}
while (1)
{
from=findinbounds(from,min,max);
if (!from)
return 0;
if (!from->inuse|| (from->reflected_time == Caster->timestamp) || (from == Caster->owner))
continue;
if ((abs(from->s.origin[2] - Caster->s.origin[2])) > 35.0)
continue;
// if we've already reflected this, don't do it again.
from->reflected_time = Caster->timestamp;
for (j=0 ; j<3 ; j++)
eorg[j] = org[j] - (from->s.origin[j] + (from->mins[j] + from->maxs[j])*0.5);
elen = DotProduct(eorg,eorg);
if (elen > max2)
continue;
return from;
}
}
#endif
void RingThink(edict_t *self)
{
int hit;
edict_t *ent = NULL, *newent;
vec3_t vel, hitloc;
vec_t scale;
edict_t* (*reflect)(edict_t*, edict_t*, vec3_t);
// kill the ring eventually
self->nextthink = level.time + 0.1;
if (!(--self->count))
{
G_SetToFree(self);
return;
}
// Since find radius is not specific enough for our needs, here is
while(ent = findringradius(ent, self->s.origin, RING_EFFECT_RADIUS, self))
{
hit = false;
reflect = NULL;
if (ent->mass)
{
if(!((ent->client) && (ent->client->playerinfo.reflect_timer > level.time)))
{
VectorSubtract(ent->s.origin, self->s.origin, vel);
scale = (RING_EFFECT_RADIUS - VectorLength(vel))
* (RING_KNOCKBACK_SCALE/RING_EFFECT_RADIUS)
* sqrt(RING_MASS_FACTOR / ent->mass)
+ RING_KNOCKBACK_BASE;
VectorNormalize(vel);
if (ent->client)
{ // For players, force them up more and faster.
vel[2] = 0.5;
if (vel[2] < 0.5 && vel[2] > 0.0)
{
scale *= 2.0;
VectorNormalize(vel);
}
}
// Vel is just passing the direction of the knockback.
QPostMessage(ent, MSG_REPULSE, PRI_DIRECTIVE, "fff", vel[0], vel[1], vel[2] + 30.0);
if (ent->takedamage)
{
VectorMA(ent->s.origin, -ent->maxs[0], vel, hitloc);
if (ent->client && coop->value) // Don't damage players in coop.
T_Damage (ent, ent, self, vel, hitloc, vec3_origin, 0, (int)scale, DAMAGE_RADIUS | DAMAGE_SPELL,MOD_ROR);
if (ent->movetype != PHYSICSTYPE_NONE)
T_Damage (ent, ent, self, vel, hitloc, vec3_origin, 4, (int)scale, DAMAGE_RADIUS | DAMAGE_SPELL,MOD_ROR);
else
T_Damage (ent, ent, self, vel, hitloc, vec3_origin, 4, 0, DAMAGE_RADIUS | DAMAGE_SPELL,MOD_ROR);
// if we are hitting a player, knock the bastard down
// if (ent->client)
// KnockDownPlayer(&ent->client->playerinfo);
}
}
}
else if (strcmp(ent->classname, "Spell_RedRainArrow") == 0)
{
if (ent->owner != self && ent->reflect_debounce_time)
{
hit = true;
reflect = RedRainMissileReflect;
}
}
else if (strcmp(ent->classname, "Spell_PhoenixArrow") == 0)
{
if (ent->owner != self && ent->reflect_debounce_time)
{
hit = true;
reflect = PhoenixMissileReflect;
}
}
else if (strcmp(ent->classname, "Spell_MeteorBarrier") == 0)
{
if (ent->owner != self && ent->reflect_debounce_time)
{
hit = true;
reflect = MeteorBarrierReflect;
}
}
else if (strcmp(ent->classname, "Spell_SphereOfAnnihilation") == 0)
{
if (ent->owner != self && ent->reflect_debounce_time)
{
hit = true;
reflect = SphereReflect;
}
}
else if (strcmp(ent->classname, "Spell_Hellbolt") == 0)
{
if (ent->owner != self && ent->reflect_debounce_time)
{
hit = true;
reflect = HellboltReflect;
}
}
else if (strcmp(ent->classname, "Spell_MorphArrow") == 0)
{
if (ent->owner != self && ent->reflect_debounce_time)
{
hit = true;
reflect = MorphReflect;
}
}
else if (strcmp(ent->classname, "Spell_Maceball") == 0)
{
if (ent->owner != self)
{ // Don't screw up your own projectiles.
hit = true;
// Give the self credit for stuff killed with it, or worse yet, set the originator as the enemy.
ent->enemy = ent->owner;
ent->owner = self->owner;
// Do a nasty looking blast at the impact point
gi.CreateEffect(&ent->s, FX_LIGHTNING_HIT, CEF_OWNERS_ORIGIN, NULL, "t", ent->velocity);
}
}
if (hit)
{
VectorSubtract(self->s.origin, ent->s.origin, vel);
VectorNormalize(vel);
// The dot product is the velocity towards the self (normally negative), let's reverse it.
scale = DotProduct(vel, ent->velocity);
if (scale > 0) // If heading towards the self, reverse that portion of the velocity
VectorMA(ent->velocity, -2.0*scale, vel, vel);
else // Jes' double the speed away
VectorMA(ent->velocity, scale, vel, vel);
if(reflect)
{
if (Vec3IsZero(vel)) // Reflect needs a non-zero vel. If zeroed, throw it straight up.
VectorSet(vel, 0, 0, 200.0);
newent = reflect(ent, self->owner, vel);
vectoangles(newent->velocity, newent->s.angles);
}
}
}
}
#define RING_THINKS 4
// Formula for knockback: 1 to 0 (center to outside) * KNOCKBACK_SCALE + KNOCKBACK_BASE
// This total is multiplied by (MASS_FACTOR / mass). (If mass > 200, less, if < 200, more)
void SpellCastBlueRing(edict_t *Caster, vec3_t StartPos, vec3_t AimAngles, vec3_t AimDir, float value)
{
edict_t *newent;
// create the actual effect entity
newent = G_Spawn();
newent->owner = Caster;
newent->solid = SOLID_NOT;
newent->svflags |= SVF_NOCLIENT;
newent->movetype = PHYSICSTYPE_NONE;
newent->classname = "Spell_Ring";
newent->nextthink = level.time + 0.1;
newent->think = RingThink;
newent->count = RING_THINKS;
newent->timestamp = level.time;
VectorCopy(Caster->s.origin, newent->s.origin);
gi.linkentity(newent);
// fire off a special effect.
gi.CreateEffect(&Caster->s, FX_SPELL_BLUERING, CEF_OWNERS_ORIGIN, 0, "");
}
// end