264 lines
7.7 KiB
C
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
|