/* Copyright (C) 1997-2001 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "g_local.h" void InitTrigger( edict_t *self ) { // Vic if ( !VectorCompare (self->pos2, vec3_origin) ) { VectorCopy ( self->pos2, self->movedir ); self->speed = 0.1f; } else if ( !VectorCompare (self->s.angles, vec3_origin) ) { G_SetMovedir ( self->s.angles, self->movedir ); } self->r.solid = SOLID_TRIGGER; self->movetype = MOVETYPE_NONE; trap_SetBrushModel (self, self->model); self->r.svflags = SVF_NOCLIENT; } // the wait time has passed, so set back up for another activation void multi_wait (edict_t *ent) { ent->nextthink = 0; } // the trigger was just activated // ent->activator should be set to the activator so it can be held through a delay // so wait for the delay time before firing void multi_trigger (edict_t *ent) { if (ent->nextthink) return; // already been triggered G_UseTargets (ent, ent->activator); if (ent->wait > 0) { ent->think = multi_wait; ent->nextthink = level.timemsec + 1000 * ent->wait; } else { // we can't just remove (self) here, because this is a touch function // called while looping through area links... ent->touch = NULL; ent->nextthink = level.timemsec + game.framemsec; ent->think = G_FreeEdict; } } void Use_Multi( edict_t *ent, edict_t *other, edict_t *activator ) { ent->activator = activator; multi_trigger (ent); } void Touch_Multi( edict_t *self, edict_t *other, cplane_t *plane, int surfFlags ) { if( other->r.client ) { if( self->spawnflags & 2 ) return; } else if( other->r.svflags & SVF_MONSTER ) { if( !(self->spawnflags & 1) ) return; } else return; if( self->s.team && self->s.team != other->s.team ) return; if( !VectorCompare(self->movedir, vec3_origin) ) { vec3_t forward; AngleVectors( other->s.angles, forward, NULL, NULL ); if( DotProduct(forward, self->movedir) < 0 ) return; } self->activator = other; multi_trigger( self ); } /*QUAKED trigger_multiple (.5 .5 .5) ? MONSTER NOT_PLAYER TRIGGERED Variable size repeatable trigger. It will fire the entities it targets when touched by player. Can be made to operate like a trigger_once entity by setting the "wait" key to -1. It can also be activated by another trigger that targets it. -------- KEYS -------- target : this points to the entity to activate. targetname : activating trigger points to this. noise : play this noise when triggered message : centerprint this text string when triggered wait : time in seconds until trigger becomes re-triggerable after it's been touched (default 0.2, -1 = trigger once). notsingle : when set to 1, entity will not spawn in Single Player mode notfree : when set to 1, entity will not spawn in "Free for all" and "Tournament" modes. notduel : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo) notteam : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. notctf : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo) -------- SPAWNFLAGS -------- MONSTER : &1 monsters won't activate this trigger unless this flag is set NOT_PLAYER : &2 players can't trigger this one (for those triggered by other triggers) TRIGGERED : &4 spawns as triggered and must wait for the "wait" key to pass to be re-triggered -------- NOTES -------- message is untested*/ void trigger_enable( edict_t *self, edict_t *other, edict_t *activator ) { self->r.solid = SOLID_TRIGGER; self->use = Use_Multi; trap_LinkEntity( self ); } void SP_trigger_multiple( edict_t *ent ) { /*if (ent->sounds && ent->sounds[0]) { if (ent->sounds[0] == '1') ent->noise_index = trap_SoundIndex (S_WORLD_SECRET); else if (ent->sounds[0] == '2') ent->noise_index = trap_SoundIndex (S_WORLD_MESSAGE); else if (ent->sounds[0] == '3') ent->noise_index = trap_SoundIndex ("sounds/misc/trigger1.wav"); }*/ if( st.noise ) ent->noise_index = trap_SoundIndex( st.noise ); // gameteam field from editor if( st.gameteam >= TEAM_SPECTATOR && st.gameteam < GS_MAX_TEAMS ) { ent->s.team = st.gameteam; } else { ent->s.team = TEAM_SPECTATOR; } if( !ent->wait ) ent->wait = 0.2f; ent->touch = Touch_Multi; ent->movetype = MOVETYPE_NONE; ent->r.svflags |= SVF_NOCLIENT; if( ent->spawnflags & 4 ) { ent->r.solid = SOLID_NOT; ent->use = trigger_enable; } else { ent->r.solid = SOLID_TRIGGER; ent->use = Use_Multi; } if ( !VectorCompare(ent->s.angles, vec3_origin) ) G_SetMovedir( ent->s.angles, ent->movedir ); trap_SetBrushModel( ent, ent->model ); trap_LinkEntity( ent ); } /*QUAKED trigger_once (.5 .5 .5) ? MONSTER NOT_PLAYER TRIGGERED Triggers once, then removes itself. You must set the key "target" to the name of another object in the level that has a matching "targetname". -------- KEYS -------- target : this points to the entity to activate. targetname : activating trigger points to this. noise : play this noise when triggered message : centerprint this text string when triggered notsingle : when set to 1, entity will not spawn in Single Player mode notfree : when set to 1, entity will not spawn in "Free for all" and "Tournament" modes. notduel : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo) notteam : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. notctf : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo) -------- SPAWNFLAGS -------- MONSTER : &1 monsters won't activate this trigger unless this flag is set NOT_PLAYER : &2 players can't trigger this one (for those triggered by other triggers) TRIGGERED : &4 spawns as triggered and must wait for the "wait" key to pass to be re-triggered -------- NOTES -------- Wait key will be ignored. message is untested*/ void SP_trigger_once(edict_t *ent) { /* // make old maps work because I messed up on flag assignments here // triggered was on bit 1 when it should have been on bit 4 if (ent->spawnflags & 1) { vec3_t v; VectorMA (ent->r.mins, 0.5, ent->r.size, v); ent->spawnflags &= ~1; ent->spawnflags |= 4; if (developer->integer) G_Printf ("fixed TRIGGERED flag on %s at %s\n", ent->classname, vtos(v)); }*/ ent->wait = -1; SP_trigger_multiple (ent); } /*QUAKED trigger_relay (.5 .5 .5) ? (-8 -8 -8) (8 8 8) This fixed size trigger cannot be touched, it can only be fired by other events. -------- KEYS -------- target : this points to the entity to activate. targetname : activating trigger points to this. notsingle : when set to 1, entity will not spawn in Single Player mode notfree : when set to 1, entity will not spawn in "Free for all" and "Tournament" modes. notduel : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo) notteam : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. notctf : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo) -------- NOTES -------- Trigger_relay is a tool for use in entities meccanos. It's of no use by itself, and can only be used as an intermediary between events. Wait key will be ignored.*/ void trigger_relay_use( edict_t *self, edict_t *other, edict_t *activator ) { G_UseTargets( self, activator ); } void SP_trigger_relay( edict_t *self ) { self->use = trigger_relay_use; } /* ============================================================================== trigger_counter ============================================================================== */ /*QUAKED trigger_counter (.5 .5 .5) ? NOMESSAGE NOSOUNDS Acts as an intermediary for an action that takes multiple inputs. Example: a sequence of several buttons to activate a event -------- KEYS -------- target : this points to the entity to activate. targetname : activating trigger points to this. count : number of actions to count (default 2) noise_start : sound to play each time a event happens noise_stop : sound to play at the last event in the count notsingle : when set to 1, entity will not spawn in Single Player mode notfree : when set to 1, entity will not spawn in "Free for all" and "Tournament" modes. notduel : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo) notteam : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. notctf : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo) -------- SPAWNFLAGS -------- NOMESSAGE : &1 if not set, it will print "1 more.. " etc when triggered and "sequence complete" when finished. NOSOUNDS : &2 if not set, it will try to play the noise_start and noise_stop sounds -------- NOTES -------- Sounds like this one should be a target and not a trigger, but well...*/ void trigger_counter_use( edict_t *self, edict_t *other, edict_t *activator ) { if( self->count == 0 ) return; self->count--; if( self->count ) { if( !(self->spawnflags & 1) ) G_CenterPrintMsg( activator, "%i more to go...", self->count ); if( !(self->spawnflags & 2)) G_Sound( activator, CHAN_AUTO, self->moveinfo.sound_start, 1, ATTN_NORM ); return; } if( !(self->spawnflags & 1) ) G_CenterPrintMsg( activator, "Sequence completed!" ); if( !(self->spawnflags & 2) ) G_Sound( activator, CHAN_AUTO, self->moveinfo.sound_end, 1, ATTN_NORM ); self->activator = activator; multi_trigger( self ); } void SP_trigger_counter( edict_t *self ) { self->wait = -1; if( !self->count ) self->count = 2; // gameteam field from editor if( st.gameteam >= TEAM_SPECTATOR && st.gameteam < GS_MAX_TEAMS ) { self->s.team = st.gameteam; } else { self->s.team = TEAM_SPECTATOR; } G_AssignMoverSounds( self, NULL, NULL, NULL ); self->use = trigger_counter_use; } /* ============================================================================== trigger_always ============================================================================== */ /*QUAKED trigger_always (.5 .5 .5) (-8 -8 -8) (8 8 8) Automatic trigger. It will fire the entities it targets as soon as it spawns in the game. -------- KEYS -------- target : fire entities with this targetname. notsingle : when set to 1, entity will not spawn in Single Player mode notfree : when set to 1, entity will not spawn in "Free for all" and "Tournament" modes. notduel : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo) notteam : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. notctf : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo) */ void SP_trigger_always (edict_t *ent) { // we must have some delay to make sure our use targets are present if (ent->delay < 0.2f) ent->delay = 0.2f; G_UseTargets(ent, ent); } /* ============================================================================== trigger_push ============================================================================== */ /*QUAKED trigger_push (.5 .5 .5) ? PUSH_ONCE This is used to create jump pads and launch ramps. It MUST point to a target_position or info_notnull entity to work. -------- KEYS -------- target : this points to the target_position to which the player will jump. noise : override default noise ("silent" doesn't make any noise) wait : time before it can be triggered again. notsingle : when set to 1, entity will not spawn in Single Player mode notfree : when set to 1, entity will not spawn in "Free for all" and "Tournament" modes. notduel : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo) notteam : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. notctf : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo) -------- SPAWNFLAGS -------- PUSH_ONCE : &1 only push when touched the first time -------- NOTES -------- To make a jump pad or launch ramp, place the target_position/info_notnull entity at the highest point of the jump and target it with this entity. */ void G_JumpPadSound( edict_t *ent ) { vec3_t org; if( !ent->s.modelindex ) return; if( !ent->moveinfo.sound_start ) return; org[0] = ent->s.origin[0] + 0.5 * (ent->r.mins[0] + ent->r.maxs[0]); org[1] = ent->s.origin[1] + 0.5 * (ent->r.mins[1] + ent->r.maxs[1]); org[2] = ent->s.origin[2] + 0.5 * (ent->r.mins[2] + ent->r.maxs[2]); G_PositionedSound( org, ent, CHAN_AUTO, ent->moveinfo.sound_start, 1.0f, ATTN_NORM ); } #define PUSH_ONCE 1 void trigger_push_touch( edict_t *self, edict_t *other, cplane_t *plane, int surfFlags ) { edict_t *event; float time, dist, f; vec3_t origin, velocity; if( self->timestamp >= level.time ) return; if( !other->r.client ) return; if( other->r.client->ps.pmove.pm_type != PM_NORMAL ) return; if( self->s.team && self->s.team != other->s.team ) return; VectorAdd( self->r.absmin, self->r.absmax, origin ); VectorScale( origin, 0.5, origin ); time = sqrt( (self->movetarget->s.origin[2] - origin[2]) / (0.5 * g_gravity->value) ); if (!time) goto remove; VectorSubtract( self->movetarget->s.origin, origin, velocity ); velocity[2] = 0; dist = VectorNormalize( velocity ); f = dist / time; VectorScale( velocity, f, velocity ); velocity[2] = time * g_gravity->value; other->r.client->jumppad_time = level.time; other->r.client->fall_value = 0.0f; // jal : remove latched fall damage VectorCopy( velocity, other->velocity ); // don't take falling damage immediately from this VectorCopy( other->velocity, other->r.client->oldvelocity ); G_JumpPadSound( self ); // play jump pad sound // add an event event = G_SpawnEvent( EV_JUMP_PAD, 0, other->s.origin ); event->r.svflags = SVF_NOOLDORIGIN; event->s.ownerNum = other - game.edicts; event->s.targetNum = self - game.edicts; // reset walljump counter other->r.client->ps.pmove.stats[PM_STAT_WJCOUNT] = 0; if( !(self->spawnflags & PUSH_ONCE) ) { self->timestamp = level.time + self->wait; return; } remove: // we can't just remove (self) here, because this is a touch function // called while looping through area links... self->touch = NULL; self->nextthink = level.timemsec + game.framemsec; self->think = G_FreeEdict; } void S_trigger_push_think( edict_t *ent ) { ent->movetarget = G_PickTarget( ent->target ); if( !ent->movetarget ) G_FreeEdict( ent ); } void SP_trigger_push( edict_t *self ) { InitTrigger( self ); if( st.noise && Q_stricmp( st.noise, "default") ) { if( Q_stricmp( st.noise, "silent") ) self->moveinfo.sound_start = trap_SoundIndex( st.noise ); } else self->moveinfo.sound_start = trap_SoundIndex( S_JUMPPAD ); self->touch = trigger_push_touch; self->think = S_trigger_push_think; self->nextthink = level.timemsec + game.framemsec; self->r.svflags &= ~SVF_NOCLIENT; self->s.type = ET_PUSH_TRIGGER; self->timestamp = level.time; if( !self->wait ) self->wait = 2 * FRAMETIME; // gameteam field from editor if( st.gameteam >= TEAM_SPECTATOR && st.gameteam < GS_MAX_TEAMS ) { self->s.team = st.gameteam; } else { self->s.team = TEAM_SPECTATOR; } trap_LinkEntity( self ); } /* ============================================================================== trigger_hurt ============================================================================== */ /*QUAKED trigger_hurt (.5 .5 .5) ? START_OFF TOGGLE SILENT NO_PROTECTION SLOW KILL FALL Any player that touches this will be hurt by "dmg" points of damage -------- KEYS -------- dmg : number of points of damage inflicted to player per "wait" time lapse (default 5 - integer values only). wait : wait time before hurting again (in seconds. Default 0.1) noise : sound to be played when inflicting damage notsingle : when set to 1, entity will not spawn in Single Player mode notfree : when set to 1, entity will not spawn in "Free for all" and "Tournament" modes. notduel : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo) notteam : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. notctf : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo) -------- SPAWNFLAGS -------- START_OFF : needs to be triggered (toggle) for damage TOGGLE : toogle SILENT : supresses the sizzling sound while player is being hurt. NO_PROTECTION : player will be hurt regardless of protection (see Notes). SLOW : changes the damage rate to once per second. KILL : player will die instantly. FALL : player will die the next time he touches the ground. -------- NOTES -------- The invulnerability power-up (item_enviro) does not protect the player from damage caused by this entity regardless of whether the NO_PROTECTION spawnflag is set or not. Triggering a trigger_hurt will have no effect if the START_OFF spawnflag is not set. A trigger_hurt always starts on in the game.*/ void hurt_use( edict_t *self, edict_t *other, edict_t *activator ) { if( self->r.solid == SOLID_NOT ) self->r.solid = SOLID_TRIGGER; else self->r.solid = SOLID_NOT; trap_LinkEntity( self ); if( !(self->spawnflags & 2) ) self->use = NULL; } void hurt_touch( edict_t *self, edict_t *other, cplane_t *plane, int surfFlags ) { int dflags; int damage; if( !other->takedamage ) return; if( other->deadflag ) return; if( self->timestamp > level.time ) return; if( self->spawnflags & 16 ) self->timestamp = level.time + 1; else self->timestamp = level.time + self->wait; if( self->s.team && self->s.team != other->s.team ) return; if( self->spawnflags & 64 ) // FALL { if( !other->r.client->fall_fatal ) { other->r.client->fall_fatal = qtrue; // Don't allow falling player to walljump and thus die from the collision to the wall other->r.client->ps.pmove.stats[PM_STAT_WJCOUNT] = GS_GameType_MaxWallJumps( game.gametype ); if( self->noise_index ) G_Sound( other, CHAN_AUTO, self->noise_index, 1, ATTN_NORM ); } return; } damage = self->dmg; if( self->spawnflags & 8) dflags = DAMAGE_NO_PROTECTION; else dflags = 0; if( self->spawnflags & 32 ) // KILL { dflags = DAMAGE_NO_PROTECTION; damage = ceil(other->health) - GIB_HEALTH + 1; if( self->noise_index ) G_Sound( other, CHAN_AUTO, self->noise_index, 1, ATTN_NORM ); } else if( !(self->spawnflags & 4) && self->noise_index ) { if( (level.framenum % 10) == 0) //jalfixme: we should not use framenum, but I don't know very well what sound is this one G_Sound (other, CHAN_AUTO, self->noise_index, 1, ATTN_NORM ); } T_Damage( other, self, world, vec3_origin, other->s.origin, vec3_origin, damage, self->dmg, dflags, MOD_TRIGGER_HURT ); } void SP_trigger_hurt( edict_t *self ) { InitTrigger( self ); if( self->spawnflags & 4 ) // SILENT self->noise_index = 0; else if( st.noise ) self->noise_index = trap_SoundIndex( st.noise ); else if( self->spawnflags & 32 || self->spawnflags & 64 ) // KILL or FALL self->noise_index = trap_SoundIndex( S_PLAYER_FALLDEATH ); else self->noise_index = 0; self->touch = hurt_touch; if( !self->dmg ) self->dmg = 5; if( !self->wait ) self->wait = 0.1f; if( self->spawnflags & 1 ) self->r.solid = SOLID_NOT; else self->r.solid = SOLID_TRIGGER; if( self->spawnflags & 2 ) self->use = hurt_use; // gameteam field from editor if( st.gameteam >= TEAM_SPECTATOR && st.gameteam < GS_MAX_TEAMS ) { self->s.team = st.gameteam; } else { self->s.team = TEAM_SPECTATOR; } trap_LinkEntity( self ); } /* ============================================================================== trigger_gravity ============================================================================== */ /*QUAKED trigger_gravity (.5 .5 .5) ? Any player that touches this will change his gravity fraction. 1.0 is standard gravity -------- KEYS -------- gravity : fraction of gravity to use. (Default 1.0) notsingle : when set to 1, entity will not spawn in Single Player mode notfree : when set to 1, entity will not spawn in "Free for all" and "Tournament" modes. notduel : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo) notteam : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. notctf : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo) -------- NOTES -------- Changes the touching entites gravity to the value of "gravity". 1.0 is standard gravity for the level.*/ void trigger_gravity_touch( edict_t *self, edict_t *other, cplane_t *plane, int surfFlags ) { if( self->s.team && self->s.team != other->s.team ) return; other->gravity = self->gravity; } void SP_trigger_gravity( edict_t *self ) { if( st.gravity == 0 ) { if( developer->integer ) G_Printf( "trigger_gravity without gravity set at %s\n", vtos(self->s.origin) ); G_FreeEdict( self ); return; } InitTrigger( self ); self->gravity = atof( st.gravity ); self->touch = trigger_gravity_touch; // gameteam field from editor if( st.gameteam >= TEAM_SPECTATOR && st.gameteam < GS_MAX_TEAMS ) { self->s.team = st.gameteam; } else { self->s.team = TEAM_SPECTATOR; } } /* ============================================================================== trigger_monsterjump ============================================================================== */ /*QUAKED trigger_monsterjump (.5 .5 .5) ? Walking monsters that touch this will jump in the direction of the trigger's angle "speed" default to 200, the speed thrown forward "height" default to 200, the speed thrown upwards */ void trigger_monsterjump_touch( edict_t *self, edict_t *other, cplane_t *plane, int surfFlags ) { if( other->flags & (FL_FLY | FL_SWIM) ) return; if( other->r.svflags & SVF_CORPSE ) return; if( !(other->r.svflags & SVF_MONSTER) ) return; if( self->s.team && self->s.team != other->s.team ) return; // set XY even if not on ground, so the jump will clear lips other->velocity[0] = self->movedir[0] * self->speed; other->velocity[1] = self->movedir[1] * self->speed; if( !other->groundentity ) return; other->groundentity = NULL; other->velocity[2] = self->movedir[2]; } void SP_trigger_monsterjump( edict_t *self ) { if( !self->speed ) self->speed = 200; if( !st.height ) st.height = 200; if( self->s.angles[YAW] == 0 ) self->s.angles[YAW] = 360; InitTrigger( self ); self->touch = trigger_monsterjump_touch; self->movedir[2] = st.height; } /*-------------------------------------------------------------------------- * just here to help old map conversions *--------------------------------------------------------------------------*/ /*QUAKED trigger_teleport (.5 .5 .5) ? SPECTATOR Players touching this will be teleported. Target it to a misc_teleporter_dest. -------- KEYS -------- target : this points to the entity to activate. targetname : activating trigger points to this. noise : play this noise when triggered wait : time in seconds until trigger becomes re-triggerable after it's been touched (default 0.2, -1 = trigger once). notsingle : when set to 1, entity will not spawn in Single Player mode notfree : when set to 1, entity will not spawn in "Free for all" and "Tournament" modes. notduel : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo) notteam : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. notctf : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo) -------- SPAWNFLAGS -------- SPECTATOR : &1 only teleport players moving in spectator mode -------- NOTES -------- Target it to a misc_teleporter_dest.*/ static void old_teleporter_touch( edict_t *self, edict_t *other, cplane_t *plane, int surfFlags ) { edict_t *dest; edict_t *event; int i; vec3_t forward; float speed; //jal: mantain speed when teleporting if( !other->r.client ) { return; } if( self->spawnflags & 1 ) { if( other->r.client->ps.pmove.pm_type != PM_SPECTATOR ) return; } else { if( other->r.client->ps.pmove.pm_type == PM_DEAD ) return; } if( self->s.team && self->s.team != other->s.team ) return; // wait delay if( self->timestamp > level.time ) return; self->timestamp = level.time + self->wait; dest = G_Find( NULL, FOFS(targetname), self->target ); if( !dest ) { if( developer->integer ) G_Printf( "Couldn't find destination.\n" ); return; } // unlink to make sure it can't possibly interfere with KillBox trap_UnlinkEntity( other ); // draw the teleport splash at source and on the player event = G_SpawnEvent( EV_PLAYER_TELEPORT_OUT, 0, other->s.origin ); event->r.svflags = SVF_NOOLDORIGIN; event->s.ownerNum = ENTNUM( other ); VectorCopy( dest->s.origin, other->s.origin ); VectorCopy( dest->s.origin, other->s.old_origin ); G_AddEvent( other, EV_TELEPORT, 0, qtrue ); //jal: Don't clear the velocity in teleporters, but let the moveflag in other->r.client->ps.pmove.pm_time = 1; other->velocity[2] = 0; //ignore vertical velocity speed = VectorLengthFast( other->velocity ); other->r.client->ps.pmove.pm_flags |= PMF_TIME_TELEPORT; event = G_SpawnEvent( EV_PLAYER_TELEPORT_IN, 0, other->s.origin ); event->r.svflags = SVF_NOOLDORIGIN; event->s.ownerNum = ENTNUM( other ); //play custom sound if any (played from the teleporter entrance) if( self->noise_index ) { vec3_t org; if( self->s.modelindex ) { org[0] = self->s.origin[0] + 0.5 * (self->r.mins[0] + self->r.maxs[0]); org[1] = self->s.origin[1] + 0.5 * (self->r.mins[1] + self->r.maxs[1]); org[2] = self->s.origin[2] + 0.5 * (self->r.mins[2] + self->r.maxs[2]); } else VectorCopy( self->s.origin, org ); G_PositionedSound( org, self, CHAN_AUTO, self->noise_index, 1.0f, ATTN_NORM ); } // set angles other->s.angles[PITCH] = 0; other->s.angles[YAW] = dest->s.angles[YAW]; other->s.angles[ROLL] = 0; VectorCopy( dest->s.angles, other->r.client->ps.viewangles ); VectorCopy( dest->s.angles, other->r.client->v_angle ); // set the delta angle for( i = 0 ; i < 3 ; i++ ) other->r.client->ps.pmove.delta_angles[i] = ANGLE2SHORT(other->s.angles[i]) - other->r.client->pers.cmd_angles[i]; // give a little forward velocity AngleVectors( other->r.client->v_angle, forward, NULL, NULL ); VectorScale( forward, speed, other->velocity );//jal: mantain player velocity // kill anything at the destination if( !KillBox (other) ) { } trap_LinkEntity( other ); } void SP_trigger_teleport( edict_t *ent ) { if( !ent->target ) { if( developer->integer ) G_Printf( "teleporter without a target.\n" ); G_FreeEdict( ent ); return; } if( st.noise ) ent->noise_index = trap_SoundIndex( st.noise ); InitTrigger ( ent ); ent->touch = old_teleporter_touch; // gameteam field from editor if( st.gameteam >= TEAM_SPECTATOR && st.gameteam < GS_MAX_TEAMS ) { ent->s.team = st.gameteam; } else { ent->s.team = TEAM_SPECTATOR; } } /*QUAKED info_teleport_destination (0.5 0.5 0.5) (-16 -16 -24) (16 16 32) You can point trigger_teleports at these. -------- KEYS -------- targetname : must match the target key of entity that uses this for pointing. notsingle : when set to 1, entity will not spawn in Single Player mode notfree : when set to 1, entity will not spawn in "Free for all" and "Tournament" modes. notduel : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo) notteam : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. notctf : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo) */ void SP_info_teleport_destination( edict_t *ent ) { ent->s.origin[2] += 16; }