djcev.com

//

Git Repos / fte_dogmode / qc / triggers / teleport.qc

Last update to this file was on 2025-08-13 at 05:20.

Show teleport.qc

//==============================================================================
// TELEPORT TRIGGERS with added functions from Zerstrorer and Qmaster
// -- dumptruck_ds
//==============================================================================

//======================================================================
// constants
//======================================================================

#if defined(CSQC) || defined(SSQC)
//----------------------------------------------------------------------
// trigger_teleport spawnflags -- CEV
//----------------------------------------------------------------------
typedef enumflags
{
SPAWNFLAG_TRIGGER_TELEPORT_ONLYPLAYER = 1,
SPAWNFLAG_TRIGGER_TELEPORT_SILENT = 2,
SPAWNFLAG_TRIGGER_TELEPORT_RANDOM = 4,
SPAWNFLAG_TRIGGER_TELEPORT_STEALTH = 8,
SPAWNFLAG_TRIGGER_TELEPORT_ONLYMONSTER = 16,
SPAWNFLAG_TRIGGER_TELEPORT_DD = 32 // "different destination" -- 80s
// SPAWNFLAG_NOT_ON_EASY = 256, // see base_entities.qc -- CEV
// SPAWNFLAG_NOT_ON_NORMAL = 512,
// SPAWNFLAG_NOT_ON_HARD_OR_NIGHTMARE = 1024,
// SPAWNFLAG_NOT_IN_DEATHMATCH = 2048,
// SPAWNFLAG_NOT_IN_COOP = 4096,
// SPAWNFLAG_NOT_IN_SP = 8192,
// SPAWNFLAG_NOT_ON_SKILL2 = 32768, // see base_entities.qc -- CEV
// SPAWNFLAG_NOT_ON_SKILL3 = 65536, // see base_entities.qc -- CEV
// SPAWNFLAG_CENTERPRINTALL = 131072 // see base_entities.qc -- CEV
} trigger_teleport_spawnflags;
#endif

//======================================================================
// forward declarations
//======================================================================

#ifdef SSQC
// tfog & tdeath
void() spawn_tfog_think;
void(vector org) spawn_tfog;
void() spawn_tdeath_touch;
void(vector org, entity death_owner) spawn_tdeath;
#endif

// trigger_teleport
#ifdef CSQC
void(float isnew) trigger_teleport_netreceive;
void() trigger_teleport_think_particle;
#endif
#ifdef SSQC
float(entity to, float netflags) trigger_teleport_netsend;
entity() trigger_teleport_randomspot;
void() trigger_teleport_think_findtarget;
void() trigger_teleport_use;
#endif
#if defined(CSQC) || defined(SSQC)
void() trigger_teleport_touch;
void(entity e) trigger_teleport_init;
#endif
#ifdef SSQC
void() trigger_teleport;
#endif

//------------------------------------------------------------------------------

//======================================================================
// teleporter fog & teleporter death
//======================================================================

#ifdef SSQC
//----------------------------------------------------------------------
void() spawn_tfog_think =
{
local float f = random() * 5;
local sound_info_t snd = snd_empty;

if (f < 1)
snd = snd_misc_tele1;
else if (f < 2)
snd = snd_misc_tele2;
else if (f < 3)
snd = snd_misc_tele3;
else if (f < 4)
snd = snd_misc_tele4;
else
snd = snd_misc_tele5;

if (snd.wav != snd_empty.wav)
SOUND (self, snd)
remove (self);
};

//----------------------------------------------------------------------
void(vector org) spawn_tfog =
{
local entity s;

s = spawn ();
s.origin = org;
s.spawnflags = self.spawnflags; // dumptruck_ds
s.nextthink = time + 0.2;
s.think = spawn_tfog_think;

WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
WriteByte (MSG_BROADCAST, TE_TELEPORT);
WriteCoord (MSG_BROADCAST, org_x);
WriteCoord (MSG_BROADCAST, org_y);
WriteCoord (MSG_BROADCAST, org_z);
};

//----------------------------------------------------------------------
void() spawn_tdeath_touch =
{
if (other == self.owner)
return;

// frag anyone who teleports in on top of an invincible player
if (other.classtype == CT_PLAYER)
{
// 1998-07-26 Pentagram telefrag fix by Zoid/Maddes start
if (self.owner.classtype != CT_PLAYER)
{
// other monsters explode themselves
t_damage2 (self.owner, self, self, 50000);
return;
}
// 1998-07-26 Pentagram telefrag fix by Zoid/Maddes end

// 1998-07-26 Pentagram telefrag fix by Zoid/Maddes start
if (other.invincible_finished > time)
{
// player on spot has active pentagram
if (self.owner.invincible_finished > time)
{
// teleported player has active pentagram too
// can happen often in deathmatch 4
// and levels with more than one pentagram
self.classname = "teledeath3";
other.invincible_finished = 0;
// kill player on spot
t_damage2 (other, self, self, 50000);

/*
// 1998-07-26 only telefrag player on spot
// by Maddes
local entity other2;
other2 = self.owner;
self.owner = other;
other2.invincible_finished = 0;
// kill teleported player
T_Damage (other2, self, self, 50000);
*/
}
else
{
// 1998-07-26 Pentagram telefrag fix by
// Zoid/Maddes end
self.classname = "teledeath2";
// 1998-07-26 Pentagram telefrag fix by
// Zoid/Maddes start
t_damage2 (self.owner, self, self, 50000);
}
return;
}

/*
if (self.owner.classtype != CT_PLAYER)
{ // other monsters explode themselves
T_Damage (self.owner, self, self, 50000);
return;
}
*/
// 1998-07-26 Pentagram telefrag fix by Zoid/Maddes end
}

if (other.health)
t_damage2 (other, self, self, 50000);
};

//----------------------------------------------------------------------
void(vector org, entity death_owner) spawn_tdeath =
{
local entity death;

death = spawn ();
death.classname = "teledeath";
death.movetype = MOVETYPE_NONE;
death.solid = SOLID_TRIGGER;
death.angles = '0 0 0';
setsize (death, death_owner.mins - '1 1 1', death_owner.maxs + '1 1 1');
setorigin (death, org);
death.touch = spawn_tdeath_touch;
death.nextthink = time + 0.2;
death.think = sub_remove;
death.owner = death_owner;

// make sure even still objects get hit
force_retouch = 2;
};
#endif

//======================================================================
// trigger_teleport
//======================================================================

/*QUAKED trigger_teleport (.5 .5 .5) ? ONLYPLAYER SILENT RANDOM STEALTH ONLYMONSTER X X X NOT_ON_EASY NOT_ON_NORMAL NOT_ON_HARD_OR_NIGHTMARE NOT_IN_DEATHMATCH NOT_IN_COOP NOT_IN_SINGLEPLAYER X NOT_ON_HARD_ONLY NOT_ON_NIGHTMARE_ONLY

Any object touching this will be transported to the corresponding
info_teleport_destination entity. You must set the "target" field,
and create an object with a "targetname" field that matches.

If the trigger_teleport has a targetname, it will only teleport entities
when it has been fired.

SILENT(2) eliminates the teleporter ambient noise (good
for hidden monster teleporters.

RANDOM(4) causes the teleporter to send the player to a
random destination among the info_teleport_random markers in the level.
You MUST place a "count" field that is the number of info_teleport_random
entities you placed.

STEALTH(8) eliminates the particle flash and noise when
an entity is teleported.

ONLYMONSTER(16) will only teleport monsters
*/
//----------------------------------------------------------------------
// class trigger_teleport: base_trigger
// {
#ifdef CSQC
//--------------------------------------------------------------
void(float isnew) trigger_teleport_netreceive =
{
local float netflags = ReadByte ();

if (netflags & NETFLAG_BASE_TRIGGER_SPAWNFLAGS)
{
local float s0 = ReadByte ();

if (s0 & SPAWNFLAG_TRIGGER_TELEPORT_ONLYPLAYER)
self.spawnflags |=
SPAWNFLAG_TRIGGER_TELEPORT_ONLYPLAYER;
else
self.spawnflags &=
~SPAWNFLAG_TRIGGER_TELEPORT_ONLYPLAYER;

if (s0 & SPAWNFLAG_TRIGGER_TELEPORT_ONLYMONSTER)
self.spawnflags |=
SPAWNFLAG_TRIGGER_TELEPORT_ONLYMONSTER;
else
self.spawnflags &=
~SPAWNFLAG_TRIGGER_TELEPORT_ONLYMONSTER;
}

BASE_TRIGGER_READSTATEFLAGS (NETFLAG_BASE_TRIGGER_STATEFLAGS)
BASE_TRIGGER_READSIZE (NETFLAG_BASE_TRIGGER_SIZE)
BASE_TRIGGER_READMANGLE (NETFLAG_BASE_TRIGGER_MANGLE)
BASE_TRIGGER_READNEWORIGIN (NETFLAG_BASE_TRIGGER_NEWORIGIN)

if (isnew)
trigger_teleport_init (self);

// make sure size is set -- CEV
if (netflags & NETFLAG_BASE_TRIGGER_SIZE)
setsize (self, self.mins, self.maxs);
};

//--------------------------------------------------------------
// clientside teleporter particles based on particle_tele from
// MachineGames misc_fx.qc -- CEV
//--------------------------------------------------------------
void() trigger_teleport_think_particle =
{
// where to spawn
local vector pos;
// scalar from org, used for speed too
local float dist = 64;
local vector rando;

rando_x = crandom() * 10;
rando_y = crandom() * 10;
rando_z = crandom() * 5;
rando = normalize (rando);

pos = ((self.absmin + self.absmax) * 0.5) + (rando * dist);

// spawn particle
particle (pos, rando * dist * -.125, 3, 3);
self.nextthink = time + 0.1 * random();
};
#endif

#ifdef SSQC
//--------------------------------------------------------------
float(entity to, float netflags) trigger_teleport_netsend =
{
BASE_ENTITY_WRITECLASSTYPE ()

if (netflags > NETFLAG_BASE_TRIGGER_FULLSEND)
netflags = NETFLAG_BASE_TRIGGER_FULLSEND;

WriteByte (MSG_ENTITY, netflags);

if (netflags & NETFLAG_BASE_TRIGGER_SPAWNFLAGS)
{
local float s0 = 0;

if (self.spawnflags &
SPAWNFLAG_TRIGGER_TELEPORT_ONLYPLAYER)
{
s0 |= SPAWNFLAG_TRIGGER_TELEPORT_ONLYPLAYER;
}

if (self.spawnflags &
SPAWNFLAG_TRIGGER_TELEPORT_ONLYMONSTER)
{
s0 |= SPAWNFLAG_TRIGGER_TELEPORT_ONLYMONSTER;
}

WriteByte (MSG_ENTITY, s0);
}

BASE_TRIGGER_WRITESTATEFLAGS (NETFLAG_BASE_TRIGGER_STATEFLAGS)
BASE_TRIGGER_WRITESIZE (NETFLAG_BASE_TRIGGER_SIZE)
BASE_TRIGGER_WRITEMANGLE (NETFLAG_BASE_TRIGGER_MANGLE)
BASE_TRIGGER_WRITENEWORIGIN (NETFLAG_BASE_TRIGGER_NEWORIGIN)

return TRUE;
};

//--------------------------------------------------------------
// trigger_teleport_randomspot -- more Zerstrorer -- dumptruck_ds
// teleport_randomspot - returns a random spot to teleport to
// among all of the "info_teleport_random" entities in the level.
// self.count is the number of spots
//--------------------------------------------------------------
entity() trigger_teleport_randomspot =
{
local float rndm;
// local float rndm, num1;
local entity spot, first;

rndm = rint (random() * (self.count - 1));
spot = findfloat (world, classtype, CT_INFO_TELEPORT_RANDOM);
if (!spot)
dprint ("trigger_teleport_randomspot: "
"no random teleport points found!\n");
first = spot;

while (rndm > 0)
{
rndm = rndm - 1;
spot = findfloat (spot, classtype,
CT_INFO_TELEPORT_RANDOM);
}

if (spot == world)
{
dprint ("trigger_teleport_randomspot: "
"random spot found world!!\n");
spot = first;
}

return spot;
};

//--------------------------------------------------------------
void() trigger_teleport_think_findtarget =
{
local entity t;
local string tname;

if (self.spawnflags & SPAWNFLAG_TRIGGER_TELEPORT_DD)
tname = self.noise;
else
tname = self.target;

if (self.wait >= 2.0)
{
objerror (sprintf("trigger_teleport_think_findtarget: "
"giving up searching for target! tname %s\n",
tname));
self.think = sub_remove;
self.nextthink = time + 0.1;
return;
}

t = find (world, targetname, tname);

// if we didn't find a target then schedule a think for 0.5
// seconds from now and try again -- CEV
if (!t)
{
dprint (sprintf("trigger_teleport_think_findtarget: "
"couldn't find target; tname %s, wait %g\n",
tname, self.wait));
self.wait += 0.5;
self.nextthink = time + self.wait;
return;
}

// if we found a teleporttrain then *never* set a neworigin,
// instead look for the target on teleport_touch -- CEV
if (t.classtype == CT_MISC_TELEPORTTRAIN)
{
dprint (sprintf("trigger_teleport_think_findtarget: "
"returning early, found teleporttrain; tname "
"%s\n", tname));
self.nextthink = -1;
self.think = sub_null;
return;
}

if (self.wait)
self.wait = 0;

self.enemy = t;
self.mangle = t.mangle;
self.neworigin = t.origin;

self.SendEntity = trigger_teleport_netsend;
self.SendFlags = NETFLAG_BASE_TRIGGER_FULLSEND;

// don't (shouldn't) need to call think anymore -- CEV
self.nextthink = -1;
self.think = sub_null;
};

//--------------------------------------------------------------
void() trigger_teleport_use =
{
self.nextthink = time + 0.2;
self.stateflags &= ~STATE_INACTIVE;
self.SendFlags |= NETFLAG_BASE_TRIGGER_STATEFLAGS;
// make sure even still objects get hit
force_retouch = 2;
self.think = sub_null;
};
#endif

#if defined(CSQC) || defined(SSQC)
//--------------------------------------------------------------
void() trigger_teleport_touch =
{
#ifdef SSQC
local vector org;
#endif

// is this trigger inactive? -- CEV
if (self.stateflags & STATE_INACTIVE)
return;

// Supa, is this trigger waiting to be activated?
if (self.stateflags & STATE_WAITING)
return;

if (other.classtype == CT_PLAYER)
{
if (self.spawnflags &
SPAWNFLAG_TRIGGER_TELEPORT_ONLYMONSTER)
{
return;
}
}
else
{
if (self.spawnflags &
SPAWNFLAG_TRIGGER_TELEPORT_ONLYPLAYER)
{
return;
}
}

// from Copper -- dumptruck_ds
if (other.movetype == MOVETYPE_NOCLIP)
return;

// TODO CEV
/*
// Special case
if (self.is_waiting != -1)
if (self.targetname != "")
if (self.nextthink < time)
// not fired yet
return;
*/

// only teleport entities flagged as capable of it -- CEV
if (!(other.stateflags & STATE_TELEPORTABLE))
return;

#ifdef SSQC
sub_usetargets ();

// put a tfog where the player was
// ### dhm - if stealth, don't spawn a fog
if (!(self.spawnflags & SPAWNFLAG_TRIGGER_TELEPORT_STEALTH))
spawn_tfog (other.origin);

// dhm - if this is a random teleporter, pick a random spot!
if (self.spawnflags & SPAWNFLAG_TRIGGER_TELEPORT_RANDOM)
{
local entity t1 = trigger_teleport_randomspot ();
if (!t1)
objerror ("trigger_teleport_touch: couldn't "
"find target");

self.enemy = t1;
self.mangle = t1.mangle;
self.neworigin = t1.origin;
}

// this is probably a teleporter targetting a teleporttrain.
// look for target and set neworigin, mangle, and enemy. -- CEV
// TODO CEV this assumes we find the target
if (!(self.SendEntity))
{
// TODO CEV: self.noise or self.target ?
local entity t3 = find (world, targetname, self.target);
self.enemy = t3;
self.mangle = t3.mangle;
self.neworigin = t3.origin;
}

if ((self.spawnflags & SPAWNFLAG_TRIGGER_TELEPORT_DD) &&
other.classtype == CT_PLAYER)
{
local entity t2 = find (world, targetname, self.noise);
makevectors (t2.mangle);
org = t2.origin + 32 * v_forward;
spawn_tdeath (t2.origin, other);
}
else
{
makevectors (self.mangle);
org = self.neworigin + 32 * v_forward;
spawn_tdeath (self.neworigin, other);
}

// spawn a tfog flash in front of the destination
// ### dhm - if stealth, don't spawn a fog
if (!(self.spawnflags & SPAWNFLAG_TRIGGER_TELEPORT_STEALTH))
spawn_tfog (org);
#endif

#ifdef CSQC
// ensure makevectors is called on the client-side -- CEV
makevectors (self.mangle);
#endif

// move the player and lock him down for a little while
if (!other.health)
{
setorigin (other, self.neworigin);
other.angles = self.mangle;
other.velocity = v_forward * vlen (other.velocity);
return;
}

setorigin (other, self.neworigin);
other.angles = self.mangle;

if (other.classtype == CT_PLAYER)
{
#ifdef CSQC
// update view angles on the client side -- CEV
if (pmovecommandframe == clientcommandframe - 1)
{
view_angles = self.mangle;
setproperty (VF_CL_VIEWANGLES, self.mangle);
}
input_angles = self.mangle;
#endif

#ifdef SSQC
// fixangle on the server if the client is unaware
// of this teleporter (for random teles) -- CEV
if (!(self.SendEntity))
other.fixangle = 1;

// set fog values from teleport destination if needed
fog_set_from_ent (other, self.enemy);
#endif

// update teleport_time and velocity for both
// client and server -- CEV
other.teleport_time = time + 0.7;
other.velocity = v_forward * PM_MAXSPEED;
}
else
{
// turn this way immediately
other.fixangle = 1;
other.teleport_time = time + 0.7;
other.velocity = v_forward * PM_MAXSPEED;
if (other.flags & FL_ONGROUND)
other.flags &= ~FL_ONGROUND;
}
};

//--------------------------------------------------------------
void(entity e) trigger_teleport_init =
{
e.classname = "trigger_teleport";
e.classtype = CT_TRIGGER_TELEPORT;
base_trigger_init (e);

e.touch = trigger_teleport_touch;

#ifdef CSQC
e.think = trigger_teleport_think_particle;
e.nextthink = time + 0.1 * random();
#endif

#ifdef SSQC
e.use = trigger_teleport_use;

if (!e.target)
objerror ("trigger_teleport_init: no target");

// RANDOM teleporters will look for their destination
// every time trigger_teleport_touch is called -- CEV
if (!(e.spawnflags & SPAWNFLAG_TRIGGER_TELEPORT_RANDOM))
{
// schedule a think to find the destination -- CEV
if (self.targetname != "")
e.stateflags |= STATE_INACTIVE;
e.think = trigger_teleport_think_findtarget;
e.nextthink = time + 0.2 + (random() * 0.25);
}

if (!(e.spawnflags & SPAWNFLAG_TRIGGER_TELEPORT_SILENT))
{
precache_sound (snd_amb_telehum.wav);
AMBSOUND ((e.mins + e.maxs) * 0.5, snd_amb_telehum)
}

sub_checkwaiting (e);
#endif
};
#endif

#ifdef SSQC
//--------------------------------------------------------------
void() trigger_teleport =
{
BASE_TRIGGER_PREINIT (base_trigger_init_field)
trigger_teleport_init (self);
};
#endif
// };

Return to the top of this page or return to the overview of this repo.

Log teleport.qc

Return to the top of this page or return to the overview of this repo.