2014-01-30 17:33:36 +00:00
|
|
|
/*
|
|
|
|
* =======================================================================
|
|
|
|
*
|
|
|
|
* Defender sphere.
|
|
|
|
*
|
|
|
|
* =======================================================================
|
|
|
|
*/
|
|
|
|
|
2011-10-11 11:40:43 +00:00
|
|
|
#include "header/local.h"
|
2009-03-12 20:03:41 +00:00
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
#define DEFENDER_LIFESPAN 30
|
|
|
|
#define HUNTER_LIFESPAN 30
|
|
|
|
#define VENGEANCE_LIFESPAN 30
|
|
|
|
#define MINIMUM_FLY_TIME 15
|
2009-03-12 20:03:41 +00:00
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
extern char *ED_NewString(char *string);
|
|
|
|
void LookAtKiller(edict_t *self, edict_t *inflictor, edict_t *attacker);
|
2009-03-12 20:03:41 +00:00
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
void defender_think(edict_t *self);
|
|
|
|
void hunter_think(edict_t *self);
|
|
|
|
void vengeance_think(edict_t *self);
|
|
|
|
void vengeance_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf);
|
|
|
|
void hunter_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf);
|
2009-03-12 20:03:41 +00:00
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
void
|
|
|
|
sphere_think_explode(edict_t *self)
|
|
|
|
{
|
|
|
|
if (!self)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (self->owner && self->owner->client &&
|
|
|
|
!(self->spawnflags & SPHERE_DOPPLEGANGER))
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
|
|
|
self->owner->client->owned_sphere = NULL;
|
|
|
|
}
|
2014-01-28 16:28:34 +00:00
|
|
|
|
|
|
|
BecomeExplosion1(self);
|
2009-03-12 20:03:41 +00:00
|
|
|
}
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
void
|
|
|
|
sphere_explode(edict_t *self, edict_t *inflictor /* unused */, edict_t *attacker /* unused */,
|
|
|
|
int damage /* unused */, vec3_t point /* unused */)
|
|
|
|
{
|
|
|
|
if (!self)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sphere_think_explode(self);
|
2009-03-12 20:03:41 +00:00
|
|
|
}
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
void
|
|
|
|
sphere_if_idle_die(edict_t *self, edict_t *inflictor /* unused */, edict_t *attacker /* unused */,
|
|
|
|
int damage /* unused */, vec3_t point /* unused */)
|
|
|
|
{
|
|
|
|
if (!self)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!self->enemy)
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
|
|
|
sphere_think_explode(self);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
void
|
|
|
|
sphere_fly(edict_t *self)
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
2014-01-28 16:28:34 +00:00
|
|
|
vec3_t dest;
|
|
|
|
vec3_t dir;
|
|
|
|
|
|
|
|
if (!self)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (level.time >= self->wait)
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
|
|
|
sphere_think_explode(self);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
VectorCopy(self->owner->s.origin, dest);
|
2009-03-12 20:03:41 +00:00
|
|
|
dest[2] = self->owner->absmax[2] + 4;
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
if (level.time == (float)(int)level.time)
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
2014-01-28 16:28:34 +00:00
|
|
|
if (!visible(self, self->owner))
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
|
|
|
VectorCopy(dest, self->s.origin);
|
|
|
|
gi.linkentity(self);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
VectorSubtract(dest, self->s.origin, dir);
|
|
|
|
VectorScale(dir, 5, self->velocity);
|
2009-03-12 20:03:41 +00:00
|
|
|
}
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
void
|
|
|
|
sphere_chase(edict_t *self, int stupidChase)
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
2014-01-28 16:28:34 +00:00
|
|
|
vec3_t dest;
|
|
|
|
vec3_t dir;
|
|
|
|
float dist;
|
|
|
|
|
|
|
|
if (!self)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((level.time >= self->wait) ||
|
|
|
|
(self->enemy && (self->enemy->health < 1)))
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
|
|
|
sphere_think_explode(self);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
VectorCopy(self->enemy->s.origin, dest);
|
|
|
|
|
|
|
|
if (self->enemy->client)
|
|
|
|
{
|
2009-03-12 20:03:41 +00:00
|
|
|
dest[2] += self->enemy->viewheight;
|
2014-01-28 16:28:34 +00:00
|
|
|
}
|
2009-03-12 20:03:41 +00:00
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
if (visible(self, self->enemy) || stupidChase)
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
2014-01-28 16:28:34 +00:00
|
|
|
/* if moving, hunter sphere uses active sound */
|
|
|
|
if (!stupidChase)
|
|
|
|
{
|
|
|
|
self->s.sound = gi.soundindex("spheres/h_active.wav");
|
|
|
|
}
|
2009-03-12 20:03:41 +00:00
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
VectorSubtract(dest, self->s.origin, dir);
|
|
|
|
VectorNormalize(dir);
|
2009-03-12 20:03:41 +00:00
|
|
|
vectoangles2(dir, self->s.angles);
|
2014-01-28 16:28:34 +00:00
|
|
|
VectorScale(dir, 500, self->velocity);
|
2009-03-12 20:03:41 +00:00
|
|
|
VectorCopy(dest, self->monsterinfo.saved_goal);
|
|
|
|
}
|
2014-01-28 16:28:34 +00:00
|
|
|
else if (VectorCompare(self->monsterinfo.saved_goal, vec3_origin))
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
|
|
|
VectorSubtract(self->enemy->s.origin, self->s.origin, dir);
|
|
|
|
vectoangles2(dir, self->s.angles);
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
/* if lurking, hunter sphere uses lurking sound */
|
|
|
|
self->s.sound = gi.soundindex("spheres/h_lurk.wav");
|
|
|
|
VectorClear(self->velocity);
|
2009-03-12 20:03:41 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
VectorSubtract(self->monsterinfo.saved_goal, self->s.origin, dir);
|
|
|
|
dist = VectorNormalize(dir);
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
if (dist > 1)
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
|
|
|
vectoangles2(dir, self->s.angles);
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
if (dist > 500)
|
|
|
|
{
|
2009-03-12 20:03:41 +00:00
|
|
|
VectorScale(dir, 500, self->velocity);
|
2014-01-28 16:28:34 +00:00
|
|
|
}
|
2009-03-12 20:03:41 +00:00
|
|
|
else if (dist < 20)
|
2014-01-28 16:28:34 +00:00
|
|
|
{
|
2009-03-12 20:03:41 +00:00
|
|
|
VectorScale(dir, (dist / FRAMETIME), self->velocity);
|
2014-01-28 16:28:34 +00:00
|
|
|
}
|
2009-03-12 20:03:41 +00:00
|
|
|
else
|
2014-01-28 16:28:34 +00:00
|
|
|
{
|
2009-03-12 20:03:41 +00:00
|
|
|
VectorScale(dir, dist, self->velocity);
|
2014-01-28 16:28:34 +00:00
|
|
|
}
|
2009-03-12 20:03:41 +00:00
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
/* if moving, hunter sphere uses active sound */
|
|
|
|
if (!stupidChase)
|
|
|
|
{
|
|
|
|
self->s.sound = gi.soundindex("spheres/h_active.wav");
|
|
|
|
}
|
2009-03-12 20:03:41 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
VectorSubtract(self->enemy->s.origin, self->s.origin, dir);
|
|
|
|
vectoangles2(dir, self->s.angles);
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
/* if not moving, hunter sphere uses lurk sound */
|
|
|
|
if (!stupidChase)
|
|
|
|
{
|
|
|
|
self->s.sound = gi.soundindex("spheres/h_lurk.wav");
|
|
|
|
}
|
2009-03-12 20:03:41 +00:00
|
|
|
|
|
|
|
VectorClear(self->velocity);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
void
|
|
|
|
sphere_fire(edict_t *self, edict_t *enemy)
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
2014-01-28 16:28:34 +00:00
|
|
|
vec3_t dest;
|
|
|
|
vec3_t dir;
|
|
|
|
|
|
|
|
if (!self || !enemy)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((level.time >= self->wait) || !enemy)
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
|
|
|
sphere_think_explode(self);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
VectorCopy(enemy->s.origin, dest);
|
2009-03-12 20:03:41 +00:00
|
|
|
self->s.effects |= EF_ROCKET;
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
VectorSubtract(dest, self->s.origin, dir);
|
|
|
|
VectorNormalize(dir);
|
|
|
|
vectoangles2(dir, self->s.angles);
|
|
|
|
VectorScale(dir, 1000, self->velocity);
|
2009-03-12 20:03:41 +00:00
|
|
|
|
|
|
|
self->touch = vengeance_touch;
|
|
|
|
self->think = sphere_think_explode;
|
|
|
|
self->nextthink = self->wait;
|
|
|
|
}
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
void
|
|
|
|
sphere_touch(edict_t *self, edict_t *other, cplane_t *plane,
|
|
|
|
csurface_t *surf, int mod)
|
|
|
|
{
|
|
|
|
if (!self || !other || !plane || !surf)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (self->spawnflags & SPHERE_DOPPLEGANGER)
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
|
|
|
if (other == self->teammaster)
|
2014-01-28 16:28:34 +00:00
|
|
|
{
|
2009-03-12 20:03:41 +00:00
|
|
|
return;
|
2014-01-28 16:28:34 +00:00
|
|
|
}
|
2009-03-12 20:03:41 +00:00
|
|
|
|
|
|
|
self->takedamage = DAMAGE_NO;
|
|
|
|
self->owner = self->teammaster;
|
|
|
|
self->teammaster = NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (other == self->owner)
|
2014-01-28 16:28:34 +00:00
|
|
|
{
|
2009-03-12 20:03:41 +00:00
|
|
|
return;
|
2014-01-28 16:28:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Don't blow up on bodies */
|
2009-03-12 20:03:41 +00:00
|
|
|
if (!strcmp(other->classname, "bodyque"))
|
2014-01-28 16:28:34 +00:00
|
|
|
{
|
2009-03-12 20:03:41 +00:00
|
|
|
return;
|
2014-01-28 16:28:34 +00:00
|
|
|
}
|
2009-03-12 20:03:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (surf && (surf->flags & SURF_SKY))
|
|
|
|
{
|
2014-01-28 16:28:34 +00:00
|
|
|
G_FreeEdict(self);
|
2009-03-12 20:03:41 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (other->takedamage)
|
|
|
|
{
|
2014-01-28 16:28:34 +00:00
|
|
|
T_Damage(other, self, self->owner, self->velocity, self->s.origin,
|
|
|
|
plane->normal, 10000, 1, DAMAGE_DESTROY_ARMOR, mod);
|
2009-03-12 20:03:41 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-01-28 16:28:34 +00:00
|
|
|
T_RadiusDamage(self, self->owner, 512, self->owner, 256, mod);
|
2009-03-12 20:03:41 +00:00
|
|
|
}
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
sphere_think_explode(self);
|
2009-03-12 20:03:41 +00:00
|
|
|
}
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
void
|
|
|
|
vengeance_touch(edict_t *self, edict_t *other, cplane_t *plane,
|
|
|
|
csurface_t *surf)
|
|
|
|
{
|
|
|
|
if (!self || !other || !plane)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (self->spawnflags & SPHERE_DOPPLEGANGER)
|
|
|
|
{
|
|
|
|
sphere_touch(self, other, plane, surf, MOD_DOPPLE_VENGEANCE);
|
|
|
|
}
|
2009-03-12 20:03:41 +00:00
|
|
|
else
|
2014-01-28 16:28:34 +00:00
|
|
|
{
|
|
|
|
sphere_touch(self, other, plane, surf, MOD_VENGEANCE_SPHERE);
|
|
|
|
}
|
2009-03-12 20:03:41 +00:00
|
|
|
}
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
void
|
|
|
|
hunter_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
2014-01-28 16:28:34 +00:00
|
|
|
edict_t *owner;
|
|
|
|
|
|
|
|
if (!self || !other || !plane || !surf)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* don't blow up if you hit the world.... sheesh. */
|
|
|
|
if (other == world)
|
|
|
|
{
|
2009-03-12 20:03:41 +00:00
|
|
|
return;
|
2014-01-28 16:28:34 +00:00
|
|
|
}
|
2009-03-12 20:03:41 +00:00
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
if (self->owner)
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
2014-01-28 16:28:34 +00:00
|
|
|
/* if owner is flying with us, make sure they stop too. */
|
|
|
|
owner = self->owner;
|
|
|
|
|
|
|
|
if (owner->flags & FL_SAM_RAIMI)
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
|
|
|
VectorClear(owner->velocity);
|
|
|
|
owner->movetype = MOVETYPE_NONE;
|
|
|
|
gi.linkentity(owner);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
if (self->spawnflags & SPHERE_DOPPLEGANGER)
|
|
|
|
{
|
|
|
|
sphere_touch(self, other, plane, surf, MOD_DOPPLE_HUNTER);
|
|
|
|
}
|
2009-03-12 20:03:41 +00:00
|
|
|
else
|
2014-01-28 16:28:34 +00:00
|
|
|
{
|
|
|
|
sphere_touch(self, other, plane, surf, MOD_HUNTER_SPHERE);
|
|
|
|
}
|
2009-03-12 20:03:41 +00:00
|
|
|
}
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
void
|
|
|
|
defender_shoot(edict_t *self, edict_t *enemy)
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
2014-01-28 16:28:34 +00:00
|
|
|
vec3_t dir;
|
|
|
|
vec3_t start;
|
|
|
|
|
|
|
|
if (!self || !enemy)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(enemy->inuse) || (enemy->health <= 0))
|
|
|
|
{
|
2009-03-12 20:03:41 +00:00
|
|
|
return;
|
2014-01-28 16:28:34 +00:00
|
|
|
}
|
2009-03-12 20:03:41 +00:00
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
if (enemy == self->owner)
|
|
|
|
{
|
2009-03-12 20:03:41 +00:00
|
|
|
return;
|
2014-01-28 16:28:34 +00:00
|
|
|
}
|
2009-03-12 20:03:41 +00:00
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
VectorSubtract(enemy->s.origin, self->s.origin, dir);
|
|
|
|
VectorNormalize(dir);
|
2009-03-12 20:03:41 +00:00
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
if (self->monsterinfo.attack_finished > level.time)
|
|
|
|
{
|
2009-03-12 20:03:41 +00:00
|
|
|
return;
|
2014-01-28 16:28:34 +00:00
|
|
|
}
|
2009-03-12 20:03:41 +00:00
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
if (!visible(self, self->enemy))
|
|
|
|
{
|
2009-03-12 20:03:41 +00:00
|
|
|
return;
|
2014-01-28 16:28:34 +00:00
|
|
|
}
|
2009-03-12 20:03:41 +00:00
|
|
|
|
|
|
|
VectorCopy(self->s.origin, start);
|
|
|
|
start[2] += 2;
|
2014-01-28 16:28:34 +00:00
|
|
|
fire_blaster2(self->owner, start, dir, 10, 1000, EF_BLASTER, 0);
|
2009-03-12 20:03:41 +00:00
|
|
|
|
|
|
|
self->monsterinfo.attack_finished = level.time + 0.4;
|
|
|
|
}
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
void
|
|
|
|
body_gib(edict_t *self)
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
2014-01-28 16:28:34 +00:00
|
|
|
int n;
|
|
|
|
|
|
|
|
if (!self)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
gi.sound(self, CHAN_BODY, gi.soundindex("misc/udeath.wav"), 1, ATTN_NORM, 0);
|
|
|
|
|
|
|
|
for (n = 0; n < 4; n++)
|
|
|
|
{
|
|
|
|
ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2", 50, GIB_ORGANIC);
|
|
|
|
}
|
2009-03-12 20:03:41 +00:00
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
ThrowGib(self, "models/objects/gibs/skull/tris.md2", 50, GIB_ORGANIC);
|
2009-03-12 20:03:41 +00:00
|
|
|
}
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
void
|
|
|
|
hunter_pain(edict_t *self, edict_t *other, float kick, int damage)
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
2014-01-28 16:28:34 +00:00
|
|
|
edict_t *owner;
|
|
|
|
float dist;
|
|
|
|
vec3_t dir;
|
|
|
|
|
|
|
|
if (!self || !other)
|
|
|
|
{
|
2009-03-12 20:03:41 +00:00
|
|
|
return;
|
2014-01-28 16:28:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (self->enemy)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2009-03-12 20:03:41 +00:00
|
|
|
|
|
|
|
owner = self->owner;
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
if (!(self->spawnflags & SPHERE_DOPPLEGANGER))
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
2014-01-28 16:28:34 +00:00
|
|
|
if (owner && (owner->health > 0))
|
|
|
|
{
|
2009-03-12 20:03:41 +00:00
|
|
|
return;
|
2014-01-28 16:28:34 +00:00
|
|
|
}
|
2009-03-12 20:03:41 +00:00
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
if (other == owner)
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-01-28 16:28:34 +00:00
|
|
|
/* if fired by a doppleganger, set it to 10 second timeout */
|
2009-03-12 20:03:41 +00:00
|
|
|
self->wait = level.time + MINIMUM_FLY_TIME;
|
|
|
|
}
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
if ((self->wait - level.time) < MINIMUM_FLY_TIME)
|
|
|
|
{
|
2009-03-12 20:03:41 +00:00
|
|
|
self->wait = level.time + MINIMUM_FLY_TIME;
|
2014-01-28 16:28:34 +00:00
|
|
|
}
|
|
|
|
|
2009-03-12 20:03:41 +00:00
|
|
|
self->s.effects |= EF_BLASTER | EF_TRACKER;
|
|
|
|
self->touch = hunter_touch;
|
|
|
|
self->enemy = other;
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
if ((self->spawnflags & SPHERE_DOPPLEGANGER) || !(owner && owner->client))
|
|
|
|
{
|
2009-03-12 20:03:41 +00:00
|
|
|
return;
|
2014-01-28 16:28:34 +00:00
|
|
|
}
|
2009-03-12 20:03:41 +00:00
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
if (!((int)dmflags->value & DF_FORCE_RESPAWN) &&
|
|
|
|
(huntercam && (huntercam->value)))
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
|
|
|
VectorSubtract(other->s.origin, self->s.origin, dir);
|
2014-01-28 16:28:34 +00:00
|
|
|
dist = VectorLength(dir);
|
2009-03-12 20:03:41 +00:00
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
if (owner && (dist >= 192))
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
2014-01-28 16:28:34 +00:00
|
|
|
/* detach owner from body and send him flying */
|
2009-03-12 20:03:41 +00:00
|
|
|
owner->movetype = MOVETYPE_FLYMISSILE;
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
/* gib like we just died, even though we didn't, really. */
|
2009-03-12 20:03:41 +00:00
|
|
|
body_gib(owner);
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
/* move the sphere to the owner's current viewpoint./
|
|
|
|
we know it's a valid spot (or will be momentarily) */
|
2009-03-12 20:03:41 +00:00
|
|
|
VectorCopy(owner->s.origin, self->s.origin);
|
|
|
|
self->s.origin[2] += owner->viewheight;
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
/* move the player's origin to the sphere's new origin */
|
2009-03-12 20:03:41 +00:00
|
|
|
VectorCopy(self->s.origin, owner->s.origin);
|
|
|
|
VectorCopy(self->s.angles, owner->s.angles);
|
|
|
|
VectorCopy(self->s.angles, owner->client->v_angle);
|
|
|
|
VectorClear(owner->mins);
|
|
|
|
VectorClear(owner->maxs);
|
|
|
|
VectorSet(owner->mins, -5, -5, -5);
|
|
|
|
VectorSet(owner->maxs, 5, 5, 5);
|
|
|
|
owner->client->ps.fov = 140;
|
|
|
|
owner->s.modelindex = 0;
|
|
|
|
owner->s.modelindex2 = 0;
|
|
|
|
owner->viewheight = 8;
|
|
|
|
owner->solid = SOLID_NOT;
|
|
|
|
owner->flags |= FL_SAM_RAIMI;
|
|
|
|
gi.linkentity(owner);
|
|
|
|
|
|
|
|
self->solid = SOLID_BBOX;
|
2014-01-28 16:28:34 +00:00
|
|
|
gi.linkentity(self);
|
2009-03-12 20:03:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
void
|
|
|
|
defender_pain(edict_t *self, edict_t *other, float kick, int damage)
|
|
|
|
{
|
|
|
|
if (!self || !other)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (other == self->owner)
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2014-01-28 16:28:34 +00:00
|
|
|
|
2009-03-12 20:03:41 +00:00
|
|
|
self->enemy = other;
|
|
|
|
}
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
void
|
|
|
|
vengeance_pain(edict_t *self, edict_t *other, float kick, int damage)
|
|
|
|
{
|
|
|
|
if (!self || !other)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (self->enemy)
|
|
|
|
{
|
2009-03-12 20:03:41 +00:00
|
|
|
return;
|
2014-01-28 16:28:34 +00:00
|
|
|
}
|
2009-03-12 20:03:41 +00:00
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
if (!(self->spawnflags & SPHERE_DOPPLEGANGER))
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
2014-01-28 16:28:34 +00:00
|
|
|
if (self->owner->health >= 25)
|
|
|
|
{
|
2009-03-12 20:03:41 +00:00
|
|
|
return;
|
2014-01-28 16:28:34 +00:00
|
|
|
}
|
2009-03-12 20:03:41 +00:00
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
if (other == self->owner)
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
self->wait = level.time + MINIMUM_FLY_TIME;
|
|
|
|
}
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
if ((self->wait - level.time) < MINIMUM_FLY_TIME)
|
|
|
|
{
|
2009-03-12 20:03:41 +00:00
|
|
|
self->wait = level.time + MINIMUM_FLY_TIME;
|
2014-01-28 16:28:34 +00:00
|
|
|
}
|
|
|
|
|
2009-03-12 20:03:41 +00:00
|
|
|
self->s.effects |= EF_ROCKET;
|
|
|
|
self->touch = vengeance_touch;
|
|
|
|
self->enemy = other;
|
|
|
|
}
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
void
|
|
|
|
defender_think(edict_t *self)
|
|
|
|
{
|
|
|
|
if (!self)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!self->owner)
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
|
|
|
G_FreeEdict(self);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
/* if we've exited the level, just remove ourselves. */
|
2009-03-12 20:03:41 +00:00
|
|
|
if (level.intermissiontime)
|
2014-01-28 16:28:34 +00:00
|
|
|
{
|
2009-03-12 20:03:41 +00:00
|
|
|
sphere_think_explode(self);
|
|
|
|
return;
|
2014-01-28 16:28:34 +00:00
|
|
|
}
|
2009-03-12 20:03:41 +00:00
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
if (self->owner->health <= 0)
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
|
|
|
sphere_think_explode(self);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
self->s.frame++;
|
2014-01-28 16:28:34 +00:00
|
|
|
|
|
|
|
if (self->s.frame > 19)
|
|
|
|
{
|
2009-03-12 20:03:41 +00:00
|
|
|
self->s.frame = 0;
|
2014-01-28 16:28:34 +00:00
|
|
|
}
|
2009-03-12 20:03:41 +00:00
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
if (self->enemy)
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
2014-01-28 16:28:34 +00:00
|
|
|
if (self->enemy->health > 0)
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
2014-01-28 16:28:34 +00:00
|
|
|
defender_shoot(self, self->enemy);
|
2009-03-12 20:03:41 +00:00
|
|
|
}
|
|
|
|
else
|
2014-01-28 16:28:34 +00:00
|
|
|
{
|
2009-03-12 20:03:41 +00:00
|
|
|
self->enemy = NULL;
|
2014-01-28 16:28:34 +00:00
|
|
|
}
|
2009-03-12 20:03:41 +00:00
|
|
|
}
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
sphere_fly(self);
|
2009-03-12 20:03:41 +00:00
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
if (self->inuse)
|
|
|
|
{
|
2009-03-12 20:03:41 +00:00
|
|
|
self->nextthink = level.time + 0.1;
|
2014-01-28 16:28:34 +00:00
|
|
|
}
|
2009-03-12 20:03:41 +00:00
|
|
|
}
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
void
|
|
|
|
hunter_think(edict_t *self)
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
|
|
|
edict_t *owner;
|
2014-01-28 16:28:34 +00:00
|
|
|
vec3_t dir, ang;
|
|
|
|
|
|
|
|
if (!self)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if we've exited the level, just remove ourselves. */
|
2009-03-12 20:03:41 +00:00
|
|
|
if (level.intermissiontime)
|
2014-01-28 16:28:34 +00:00
|
|
|
{
|
2009-03-12 20:03:41 +00:00
|
|
|
sphere_think_explode(self);
|
|
|
|
return;
|
2014-01-28 16:28:34 +00:00
|
|
|
}
|
2009-03-12 20:03:41 +00:00
|
|
|
|
|
|
|
owner = self->owner;
|
2014-01-28 16:28:34 +00:00
|
|
|
|
|
|
|
if (!owner && !(self->spawnflags & SPHERE_DOPPLEGANGER))
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
|
|
|
G_FreeEdict(self);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
if (owner)
|
|
|
|
{
|
2009-03-12 20:03:41 +00:00
|
|
|
self->ideal_yaw = owner->s.angles[YAW];
|
2014-01-28 16:28:34 +00:00
|
|
|
}
|
|
|
|
else if (self->enemy) /* fired by doppleganger */
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
|
|
|
VectorSubtract(self->enemy->s.origin, self->s.origin, dir);
|
|
|
|
vectoangles2(dir, ang);
|
|
|
|
self->ideal_yaw = ang[YAW];
|
|
|
|
}
|
|
|
|
|
|
|
|
M_ChangeYaw(self);
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
if (self->enemy)
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
2014-01-28 16:28:34 +00:00
|
|
|
sphere_chase(self, 0);
|
2009-03-12 20:03:41 +00:00
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
/* deal with sam raimi cam */
|
|
|
|
if (owner && (owner->flags & FL_SAM_RAIMI))
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
2014-01-28 16:28:34 +00:00
|
|
|
if (self->inuse)
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
|
|
|
owner->movetype = MOVETYPE_FLYMISSILE;
|
2014-01-28 16:28:34 +00:00
|
|
|
LookAtKiller(owner, self, self->enemy);
|
|
|
|
|
|
|
|
/* owner is flying with us, move him too */
|
2009-03-12 20:03:41 +00:00
|
|
|
owner->movetype = MOVETYPE_FLYMISSILE;
|
|
|
|
owner->viewheight = self->s.origin[2] - owner->s.origin[2];
|
|
|
|
VectorCopy(self->s.origin, owner->s.origin);
|
|
|
|
VectorCopy(self->velocity, owner->velocity);
|
|
|
|
VectorClear(owner->mins);
|
|
|
|
VectorClear(owner->maxs);
|
|
|
|
gi.linkentity(owner);
|
|
|
|
}
|
2014-01-28 16:28:34 +00:00
|
|
|
else /* sphere timed out */
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
|
|
|
VectorClear(owner->velocity);
|
|
|
|
owner->movetype = MOVETYPE_NONE;
|
|
|
|
gi.linkentity(owner);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-01-28 16:28:34 +00:00
|
|
|
else
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
2014-01-28 16:28:34 +00:00
|
|
|
sphere_fly(self);
|
2009-03-12 20:03:41 +00:00
|
|
|
}
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
if (self->inuse)
|
|
|
|
{
|
2009-03-12 20:03:41 +00:00
|
|
|
self->nextthink = level.time + 0.1;
|
2014-01-28 16:28:34 +00:00
|
|
|
}
|
2009-03-12 20:03:41 +00:00
|
|
|
}
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
void
|
|
|
|
vengeance_think(edict_t *self)
|
|
|
|
{
|
|
|
|
if (!self)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if we've exited the level, just remove ourselves. */
|
2009-03-12 20:03:41 +00:00
|
|
|
if (level.intermissiontime)
|
2014-01-28 16:28:34 +00:00
|
|
|
{
|
2009-03-12 20:03:41 +00:00
|
|
|
sphere_think_explode(self);
|
|
|
|
return;
|
2014-01-28 16:28:34 +00:00
|
|
|
}
|
2009-03-12 20:03:41 +00:00
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
if (!(self->owner) && !(self->spawnflags & SPHERE_DOPPLEGANGER))
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
|
|
|
G_FreeEdict(self);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
if (self->enemy)
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
2014-01-28 16:28:34 +00:00
|
|
|
sphere_chase(self, 1);
|
2009-03-12 20:03:41 +00:00
|
|
|
}
|
|
|
|
else
|
2014-01-28 16:28:34 +00:00
|
|
|
{
|
|
|
|
sphere_fly(self);
|
|
|
|
}
|
2009-03-12 20:03:41 +00:00
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
if (self->inuse)
|
|
|
|
{
|
2009-03-12 20:03:41 +00:00
|
|
|
self->nextthink = level.time + 0.1;
|
2014-01-28 16:28:34 +00:00
|
|
|
}
|
2009-03-12 20:03:41 +00:00
|
|
|
}
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
edict_t *
|
|
|
|
Sphere_Spawn(edict_t *owner, int spawnflags)
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
|
|
|
edict_t *sphere;
|
2014-01-28 16:28:34 +00:00
|
|
|
|
|
|
|
if (!owner)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2009-03-12 20:03:41 +00:00
|
|
|
sphere = G_Spawn();
|
|
|
|
VectorCopy(owner->s.origin, sphere->s.origin);
|
|
|
|
sphere->s.origin[2] = owner->absmax[2];
|
|
|
|
sphere->s.angles[YAW] = owner->s.angles[YAW];
|
|
|
|
sphere->solid = SOLID_BBOX;
|
|
|
|
sphere->clipmask = MASK_SHOT;
|
|
|
|
sphere->s.renderfx = RF_FULLBRIGHT | RF_IR_VISIBLE;
|
|
|
|
sphere->movetype = MOVETYPE_FLYMISSILE;
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
if (spawnflags & SPHERE_DOPPLEGANGER)
|
|
|
|
{
|
2009-03-12 20:03:41 +00:00
|
|
|
sphere->teammaster = owner->teammaster;
|
2014-01-28 16:28:34 +00:00
|
|
|
}
|
2009-03-12 20:03:41 +00:00
|
|
|
else
|
2014-01-28 16:28:34 +00:00
|
|
|
{
|
2009-03-12 20:03:41 +00:00
|
|
|
sphere->owner = owner;
|
2014-01-28 16:28:34 +00:00
|
|
|
}
|
|
|
|
|
2009-03-12 20:03:41 +00:00
|
|
|
sphere->classname = "sphere";
|
|
|
|
sphere->yaw_speed = 40;
|
|
|
|
sphere->monsterinfo.attack_finished = 0;
|
2014-01-28 16:28:34 +00:00
|
|
|
sphere->spawnflags = spawnflags; /* need this for the HUD to recognize sphere */
|
|
|
|
/* PMM */
|
2009-03-12 20:03:41 +00:00
|
|
|
sphere->takedamage = DAMAGE_NO;
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
switch (spawnflags & SPHERE_TYPE)
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
|
|
|
case SPHERE_DEFENDER:
|
|
|
|
sphere->s.modelindex = gi.modelindex("models/items/defender/tris.md2");
|
2014-01-28 16:28:34 +00:00
|
|
|
sphere->s.modelindex2 =
|
|
|
|
gi.modelindex("models/items/shell/tris.md2");
|
|
|
|
sphere->s.sound = gi.soundindex("spheres/d_idle.wav");
|
2009-03-12 20:03:41 +00:00
|
|
|
sphere->pain = defender_pain;
|
|
|
|
sphere->wait = level.time + DEFENDER_LIFESPAN;
|
|
|
|
sphere->die = sphere_explode;
|
|
|
|
sphere->think = defender_think;
|
|
|
|
break;
|
|
|
|
case SPHERE_HUNTER:
|
|
|
|
sphere->s.modelindex = gi.modelindex("models/items/hunter/tris.md2");
|
2014-01-28 16:28:34 +00:00
|
|
|
sphere->s.sound = gi.soundindex("spheres/h_idle.wav");
|
2009-03-12 20:03:41 +00:00
|
|
|
sphere->wait = level.time + HUNTER_LIFESPAN;
|
|
|
|
sphere->pain = hunter_pain;
|
|
|
|
sphere->die = sphere_if_idle_die;
|
|
|
|
sphere->think = hunter_think;
|
|
|
|
break;
|
|
|
|
case SPHERE_VENGEANCE:
|
|
|
|
sphere->s.modelindex = gi.modelindex("models/items/vengnce/tris.md2");
|
2014-01-28 16:28:34 +00:00
|
|
|
sphere->s.sound = gi.soundindex("spheres/v_idle.wav");
|
2009-03-12 20:03:41 +00:00
|
|
|
sphere->wait = level.time + VENGEANCE_LIFESPAN;
|
|
|
|
sphere->pain = vengeance_pain;
|
|
|
|
sphere->die = sphere_if_idle_die;
|
|
|
|
sphere->think = vengeance_think;
|
2014-01-28 16:28:34 +00:00
|
|
|
VectorSet(sphere->avelocity, 30, 30, 0);
|
2009-03-12 20:03:41 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
gi.dprintf("Tried to create an invalid sphere\n");
|
|
|
|
G_FreeEdict(sphere);
|
|
|
|
return NULL;
|
|
|
|
}
|
2014-01-28 16:28:34 +00:00
|
|
|
|
2009-03-12 20:03:41 +00:00
|
|
|
sphere->nextthink = level.time + 0.1;
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
gi.linkentity(sphere);
|
2009-03-12 20:03:41 +00:00
|
|
|
|
|
|
|
return sphere;
|
|
|
|
}
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
void
|
|
|
|
Own_Sphere(edict_t *self, edict_t *sphere)
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
2014-01-28 16:28:34 +00:00
|
|
|
if (!sphere || !self)
|
|
|
|
{
|
2009-03-12 20:03:41 +00:00
|
|
|
return;
|
2014-01-28 16:28:34 +00:00
|
|
|
}
|
2009-03-12 20:03:41 +00:00
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
/* ownership only for players */
|
|
|
|
if (self->client)
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
2014-01-28 16:28:34 +00:00
|
|
|
/* if they don't have one */
|
|
|
|
if (!(self->client->owned_sphere))
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
|
|
|
self->client->owned_sphere = sphere;
|
|
|
|
}
|
2014-01-28 16:28:34 +00:00
|
|
|
/* they already have one, take care of the old one */
|
2009-03-12 20:03:41 +00:00
|
|
|
else
|
|
|
|
{
|
2014-01-28 16:28:34 +00:00
|
|
|
if (self->client->owned_sphere->inuse)
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
|
|
|
G_FreeEdict(self->client->owned_sphere);
|
|
|
|
self->client->owned_sphere = sphere;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
self->client->owned_sphere = sphere;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
void
|
|
|
|
Defender_Launch(edict_t *self)
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
2014-01-28 16:28:34 +00:00
|
|
|
edict_t *sphere;
|
|
|
|
|
|
|
|
if (!self)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sphere = Sphere_Spawn(self, SPHERE_DEFENDER);
|
|
|
|
Own_Sphere(self, sphere);
|
2009-03-12 20:03:41 +00:00
|
|
|
}
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
void
|
|
|
|
Hunter_Launch(edict_t *self)
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
2014-01-28 16:28:34 +00:00
|
|
|
edict_t *sphere;
|
|
|
|
|
|
|
|
if (!self)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sphere = Sphere_Spawn(self, SPHERE_HUNTER);
|
|
|
|
Own_Sphere(self, sphere);
|
2009-03-12 20:03:41 +00:00
|
|
|
}
|
|
|
|
|
2014-01-28 16:28:34 +00:00
|
|
|
void
|
|
|
|
Vengeance_Launch(edict_t *self)
|
2009-03-12 20:03:41 +00:00
|
|
|
{
|
2014-01-28 16:28:34 +00:00
|
|
|
edict_t *sphere;
|
|
|
|
|
|
|
|
if (!self)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sphere = Sphere_Spawn(self, SPHERE_VENGEANCE);
|
|
|
|
Own_Sphere(self, sphere);
|
2009-03-12 20:03:41 +00:00
|
|
|
}
|