1998-11-24 00:00:00 +00:00
|
|
|
//
|
|
|
|
// spl_teleport.c
|
|
|
|
//
|
|
|
|
// Heretic II
|
|
|
|
// Copyright 1998 Raven Software
|
|
|
|
//
|
|
|
|
// Written by Jake Simpson
|
|
|
|
|
|
|
|
#include "g_local.h"
|
|
|
|
#include "fx.h"
|
|
|
|
#include "Angles.h"
|
|
|
|
#include "Utilities.h"
|
|
|
|
#include "random.h"
|
|
|
|
#include "vector.h"
|
|
|
|
#include "g_teleport.h"
|
1999-03-18 00:00:00 +00:00
|
|
|
#include "p_main.h"
|
1998-11-24 00:00:00 +00:00
|
|
|
#include "g_playstats.h"
|
|
|
|
|
|
|
|
extern void SelectSpawnPoint (edict_t *ent,vec3_t origin, vec3_t angles);
|
|
|
|
extern void AlertMonsters (edict_t *self, edict_t *enemy, float lifetime, qboolean ignore_shadows);
|
|
|
|
|
|
|
|
// PLAYER ONLY!!!
|
|
|
|
// make the guy actually teleport from one place to another
|
|
|
|
// called from ANIMACTOR
|
|
|
|
void Perform_Teleport(edict_t *self)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
//Get the player off the rope
|
|
|
|
self->client->playerinfo.flags |= PLAYER_FLAG_RELEASEROPE;
|
|
|
|
|
|
|
|
//physically move the player, bearing in mind thats what a teleport is
|
|
|
|
VectorCopy (self->client->tele_dest, self->client->playerinfo.origin);
|
|
|
|
VectorCopy (self->client->tele_dest, self->s.origin);
|
|
|
|
|
|
|
|
// set angles
|
|
|
|
for (i=0 ; i<3 ; i++)
|
|
|
|
self->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(self->client->tele_angles[i] - self->client->resp.cmd_angles[i]);
|
|
|
|
|
|
|
|
self->s.angles[PITCH] = 0;
|
|
|
|
self->s.angles[YAW] = self->client->tele_angles[YAW];
|
|
|
|
self->s.angles[ROLL] = 0;
|
|
|
|
VectorCopy (self->client->tele_angles, self->client->ps.viewangles);
|
|
|
|
VectorCopy (self->client->tele_angles, self->client->v_angle);
|
|
|
|
|
|
|
|
// reset the cvar Farclip dist, incase it was modified by a trigger - there should be no teleport
|
|
|
|
// destinations or spawn points anywhere where the far clip has been modified.
|
|
|
|
gi.cvar_set("r_farclipdist", FAR_CLIP_DIST);
|
|
|
|
|
|
|
|
// unlink to make sure it can't possibly interfere with KillBox - we don't want to collide with ourselves
|
|
|
|
gi.unlinkentity (self);
|
|
|
|
|
|
|
|
// kill anything at the destination
|
|
|
|
KillBox (self);
|
|
|
|
|
|
|
|
// re link us
|
|
|
|
gi.linkentity (self);
|
|
|
|
|
|
|
|
// draw the teleport splash at the destination
|
|
|
|
gi.CreateEffect(&self->s, FX_PLAYER_TELEPORT_IN, CEF_BROADCAST|CEF_OWNERS_ORIGIN | ((byte)self->client->tele_type << 5), NULL, "" );
|
|
|
|
|
|
|
|
// restart the loop and tell us next time we aren't de-materialising
|
|
|
|
self->client->tele_count = TELE_TIME;
|
|
|
|
self->client->tele_dest[0] = self->client->tele_dest[1] = self->client->tele_dest[2] = -1;
|
|
|
|
|
|
|
|
AlertMonsters (self, self, 2, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
// PLAYER ONLY!!!
|
|
|
|
// Done teleporting, clean up after ourselves
|
|
|
|
// called from ANIMACTOR
|
|
|
|
void CleanUpTeleport(edict_t *self)
|
|
|
|
{
|
|
|
|
self->client->tele_dest[0] = self->client->tele_dest[1] = self->client->tele_dest[2] = 0;
|
|
|
|
self->client->tele_count = 0;
|
|
|
|
self->flags &= ~FL_LOCKMOVE;
|
|
|
|
self->client->playerinfo.flags &=~PLAYER_FLAG_TELEPORT;
|
|
|
|
self->client->ps.pmove.pm_flags &= ~PMF_LOCKMOVE;
|
|
|
|
self->s.color.a = 255;
|
|
|
|
self->client->shrine_framenum = level.time - 1;;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Setup the teleporter - from the player hitting a teleport pad
|
|
|
|
// We could send the teleport type over the flags instead of as a parameter byte
|
|
|
|
void teleporter_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
|
|
|
|
{
|
|
|
|
vec3_t dest_v, dest_v_angles;
|
|
|
|
int rand_targ, i;
|
|
|
|
edict_t *dest = NULL;
|
|
|
|
|
|
|
|
// if we aren't a player, forget it
|
|
|
|
if (!other->client)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// if we are already teleporting, forget it
|
|
|
|
|
|
|
|
if ((other->client->playerinfo.flags & (PLAYER_FLAG_TELEPORT | PLAYER_FLAG_MORPHING)) || (other->deadflag & (DEAD_DYING|DEAD_DEAD)))
|
|
|
|
return;
|
|
|
|
|
|
|
|
// if we are in deathmatch, and this teleporter is so flagged, give us a random destination
|
|
|
|
if (deathmatch->value && (self->spawnflags & 2))
|
|
|
|
{
|
|
|
|
// figure out a destination point
|
|
|
|
SelectSpawnPoint(other,dest_v, dest_v_angles);
|
|
|
|
// dest coors to teleport to
|
|
|
|
VectorCopy(dest_v,other->client->tele_dest);
|
|
|
|
// angles we should start at
|
|
|
|
VectorCopy(dest_v_angles,other->client->tele_angles);
|
|
|
|
}
|
|
|
|
// we do have a specific destination in mind
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// setup in player info the dest entity of the teleport
|
|
|
|
|
|
|
|
// do we have multiple destinations ?
|
|
|
|
if (self->style)
|
|
|
|
{
|
|
|
|
rand_targ = irand(1,self->style);
|
|
|
|
for (i = 0; i<rand_targ;i++)
|
|
|
|
{
|
|
|
|
dest = G_Find (dest, FOFS(targetname), self->target);
|
|
|
|
if (!dest)
|
|
|
|
{
|
|
|
|
#ifdef _DEVEL
|
|
|
|
gi.dprintf ("Couldn't find multiple teleport destination %d\n",rand_targ);
|
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// no - just the one
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dest = G_Find (dest, FOFS(targetname), self->target);
|
|
|
|
if (!dest)
|
|
|
|
{
|
|
|
|
#ifdef _DEVEL
|
|
|
|
gi.dprintf ("Couldn't find teleport destination %s\n",self->target);
|
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// dest coors to teleport to
|
|
|
|
VectorCopy(dest->last_org,other->client->tele_dest);
|
|
|
|
// angles we should start at
|
|
|
|
VectorCopy(dest->s.angles,other->client->tele_angles);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// setup other teleporter information that the character will require
|
|
|
|
// when the teleport is actually performed in AnimUpdateFrame
|
|
|
|
|
|
|
|
// set the player as teleporting
|
|
|
|
|
|
|
|
other->client->playerinfo.flags |= PLAYER_FLAG_TELEPORT;
|
|
|
|
|
|
|
|
other->client->ps.pmove.pm_flags |= PMF_LOCKMOVE;
|
|
|
|
|
|
|
|
// time taken over dematerialisation
|
|
|
|
other->client->tele_count = TELE_TIME_OUT;
|
|
|
|
|
|
|
|
// save out what kind of solid ability we are
|
|
|
|
other->client->old_solid = other->solid;
|
|
|
|
|
|
|
|
// make us invunerable for a couple of seconds
|
|
|
|
other->client->shrine_framenum = level.time + 10;
|
|
|
|
|
|
|
|
// tell us how we triggered the teleport
|
|
|
|
other->client->tele_type = 0;
|
|
|
|
|
|
|
|
// clear the velocity and hold them in place briefly
|
|
|
|
VectorClear (other->velocity);
|
|
|
|
other->client->ps.pmove.pm_time = 50;
|
|
|
|
// make the player still
|
|
|
|
other->flags |= FL_LOCKMOVE;
|
|
|
|
// allow the player to fade out
|
|
|
|
other->s.color.a = 255;
|
|
|
|
other->s.color.r = 255;
|
|
|
|
other->s.color.g = 255;
|
|
|
|
other->s.color.b = 255;
|
|
|
|
|
|
|
|
// draw the teleport splash at the teleport source
|
|
|
|
gi.CreateEffect(&other->s, FX_PLAYER_TELEPORT_OUT, CEF_OWNERS_ORIGIN | ((byte)other->client->tele_type << 5), NULL, "" );
|
|
|
|
// do the teleport sound
|
1999-03-18 00:00:00 +00:00
|
|
|
gi.sound(other,CHAN_VOICE,gi.soundindex("weapons/teleport.wav"),1,ATTN_NORM,0);
|
1998-11-24 00:00:00 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Spawn the Spell teleport effect - from the player
|
|
|
|
// We could send the teleport type over the flags instead of as a parameter byte
|
|
|
|
void SpellCastTeleport(edict_t *caster,vec3_t StartPos,vec3_t AimAngles,vec3_t AimDir,float Value)
|
|
|
|
{
|
|
|
|
|
|
|
|
vec3_t dest, dest_angles;
|
|
|
|
|
|
|
|
// if we are already teleporting, forget it
|
|
|
|
if ((caster->client->playerinfo.flags & (PLAYER_FLAG_TELEPORT | PLAYER_FLAG_MORPHING)) || (caster->deadflag & (DEAD_DYING|DEAD_DEAD)))
|
|
|
|
return;
|
|
|
|
|
|
|
|
// setup other teleporter information that the character will require
|
|
|
|
// when the teleport is actually performed in AnimUpdateFrame
|
|
|
|
|
|
|
|
// set the player as teleporting
|
|
|
|
caster->client->playerinfo.flags |= PLAYER_FLAG_TELEPORT;
|
|
|
|
|
|
|
|
caster->client->ps.pmove.pm_flags |= PMF_LOCKMOVE;
|
|
|
|
|
|
|
|
// figure out a destination point
|
|
|
|
SelectSpawnPoint(caster,dest, dest_angles);
|
|
|
|
|
|
|
|
// dest coors to teleport to
|
|
|
|
VectorCopy(dest,caster->client->tele_dest);
|
|
|
|
// angles we should start at
|
|
|
|
VectorCopy(dest_angles,caster->client->tele_angles);
|
|
|
|
// time taken over dematerialisation
|
|
|
|
caster->client->tele_count = TELE_TIME_OUT;
|
|
|
|
|
|
|
|
// tell us how we triggered the teleport
|
|
|
|
caster->client->tele_type = 1;
|
|
|
|
|
|
|
|
// save out what kind of solid ability we are
|
|
|
|
caster->client->old_solid = caster->solid;
|
|
|
|
|
|
|
|
// make us invunerable for a couple of seconds
|
|
|
|
caster->client->shrine_framenum = level.time + 10;
|
|
|
|
|
|
|
|
// clear the velocity and hold them in place briefly
|
|
|
|
VectorClear (caster->velocity);
|
|
|
|
caster->client->ps.pmove.pm_time = 50;
|
|
|
|
// make the player still
|
|
|
|
caster->flags |= FL_LOCKMOVE;
|
|
|
|
// allow the player to fade out
|
|
|
|
caster->s.color.a = 255;
|
|
|
|
caster->s.color.r = 255;
|
|
|
|
caster->s.color.g = 255;
|
|
|
|
caster->s.color.b = 255;
|
|
|
|
|
|
|
|
// draw the teleport splash at the teleport source
|
|
|
|
gi.CreateEffect(&caster->s, FX_PLAYER_TELEPORT_OUT, CEF_OWNERS_ORIGIN | ((byte)caster->client->tele_type << 5), NULL, "" );
|
|
|
|
// do the teleport sound
|
1999-03-18 00:00:00 +00:00
|
|
|
gi.sound(caster,CHAN_VOICE,gi.soundindex("weapons/teleport.wav"),1,ATTN_NORM,0);
|
1998-11-24 00:00:00 +00:00
|
|
|
}
|