/* * Copyright (C) 1999-2000 Id Software, Inc. */ #include "g_local.h" /*these look weired... I'd rather replace them with streight numbers. #define SF_SPECTATOR (1<<0) #define SF_RANDOM (1<<1)*/ /** * \brief Inits a trigger entity. * * Initializes trigger entities. * * @param self the trigger entity */ void InitTrigger( gentity_t *self ) { if (!VectorCompare (self->s.angles, vec3_origin)) G_SetMovedir (self->s.angles, self->movedir); if(!self->tmpEntity) /* for spawnTent command */ trap_SetBrushModel( self, self->model ); self->r.contents = CONTENTS_TRIGGER; /* replaces the -1 from trap_SetBrushModel */ self->r.svFlags = SVF_NOCLIENT; } // the wait time has passed, so set back up for another activation /** * \brief Reactivate a trigger_multiple after the wait time has passed. * * Reactivates the trigger_multiple after the wait time has passed. * * @param ent the trigger */ void multi_wait( gentity_t *ent ) { ent->nextthink = 0; } /** * \brief Function that gets called when a trigger_multiple has been ... umm well triggered ;) * * The trigger_multiple was just activated. ent->activator should be set to the activator * so it can be held through a delay. May wait for the delay time until firing. * * @param ent the trigger * @param activator the activator */ void multi_trigger( gentity_t *ent, gentity_t *activator ) { ent->activator = activator; if ( ent->nextthink ) { return; /* can't retrigger until the wait is over */ } if ( activator->client && ((ent->spawnflags&4)||(ent->spawnflags&2)||(ent->spawnflags&1)) ) {/* see if it's usable by this team */ switch( activator->client->sess.sessionTeam ) { case TEAM_RED: if ( !(ent->spawnflags&1) ) { return;/* red is not allowed */ } break; case TEAM_BLUE: if ( !(ent->spawnflags&2) ) { return;/* blue is not allowed */ } break; default: if ( (ent->spawnflags&4) ) { return;/* must be on a team */ } break; } } G_UseTargets (ent, ent->activator); #ifdef G_LUA if(ent->luaTrigger && !(ent->nextthink)) { if(activator) { LuaHook_G_EntityTrigger(ent->luaTrigger, ent->s.number, activator->s.number); } else { LuaHook_G_EntityTrigger(ent->luaTrigger, ent->s.number, ENTITYNUM_WORLD); } } #endif if ( ent->wait > 0 ) { ent->think = multi_wait; ent->nextthink = level.time + ( ent->wait + ent->random * crandom() ) * 1000; } else { /* * we can't just remove (self) here, because this is a touch function * called while looping through area links... */ ent->touch = 0; ent->nextthink = level.time + FRAMETIME; ent->think = G_FreeEntity; } } /** * \brief Use function for trigger_multiple. * * Use function for trigger_multiple. * * @param ent the trigger * @param other another entity * @param activator the activator */ void Use_Multi( gentity_t *ent, gentity_t *other, gentity_t *activator ) { multi_trigger( ent, activator ); } /** * \brief Touch function for trigger_multiple. * * Touch function for trigger_multiple. * * @param self the trigger * @param other touching entity * @param trace a trace */ void Touch_Multi( gentity_t *self, gentity_t *other, trace_t *trace ) { if( !other->client ) { return; } multi_trigger( self, other ); } /*QUAKED trigger_multiple (.5 .5 .5) ? RED_OK BLUE_OK TEAM_ONLY -----DESCRIPTION----- Variable sized repeatable trigger. Must be targeted at one or more entities. so, the basic time between firing is a random time between (wait - random) and (wait + random) -----SPAWNFLAGS----- 1: RED_OK - People on the red team can fire this trigger 2: BLUE_OK - People on the blue team can fire this trigger 4: TEAM_ONLY - Only people on red or blue can fire this trigger (not TEAM_FREE like in straight holomatch or spectators) -----KEYS----- "wait" - Seconds between triggerings, 0.5 default, -1 = one time only. "random" - wait variance, default is 0 */ /** * \brief Spawn function of trigger_multiple. * * Spawn function of trigger_multiple. * * @param ent the trigger */ void SP_trigger_multiple( gentity_t *ent ) { G_SpawnFloat( "wait", "0.5", &ent->wait ); G_SpawnFloat( "random", "0", &ent->random ); G_SetOrigin(ent, ent->s.origin); if ( ent->random >= ent->wait && ent->wait >= 0 ) { ent->random = ent->wait - FRAMETIME; DEVELOPER(G_Printf(S_COLOR_YELLOW "[Entity-Warning] trigger_multiple has random >= wait\n" );); } ent->touch = Touch_Multi; ent->use = Use_Multi; InitTrigger( ent ); VectorCopy(ent->r.maxs, ent->s.origin2); VectorCopy(ent->r.mins, ent->s.angles2); trap_LinkEntity (ent); level.numBrushEnts++; } /* ============================================================================== trigger_always ============================================================================== */ /** * \brief Think function of trigger_always. * * Think function of trigger_always. * * @param ent the trigger */ void trigger_always_think( gentity_t *ent ) { G_UseTargets(ent, ent); #ifdef G_LUA if(ent->luaTrigger) { LuaHook_G_EntityTrigger(ent->luaTrigger, ent->s.number, ent->s.number); } #endif G_FreeEntity( ent ); } /*QUAKED trigger_always (.5 .5 .5) (-8 -8 -8) (8 8 8) -----DESCRIPTION----- This trigger will always fire. It is activated by the world. Actually this is going to fire once 0.3 secs after spawn, so it's more a trigger_init. -----SPAWNFLAGS----- none -----KEYS----- "target" - targets to fire */ /** * \brief Spawn function of trigger_multiple. * * Spawn function of trigger_multiple. * * @param ent the trigger */ void SP_trigger_always (gentity_t *ent) { /* we must have some delay to make sure our use targets are present */ ent->nextthink = level.time + 300; ent->think = trigger_always_think; } /* ============================================================================== trigger_push ============================================================================== */ /** * \brief Touch function of trigger_push. * * Touch function of trigger_push. * * @param self the trigger * @param other the touching entity * @param trace a trace */ void trigger_push_touch (gentity_t *self, gentity_t *other, trace_t *trace ) { if ( !other->client ) { return; } /* if ( other->client->ps.pm_type != PM_NORMAL ) { return; } if ( other->client->ps.powerups[PW_FLIGHT] ) { return; } */ if ( other->client->ps.velocity[2] < 100 ) { /* don't play the event sound again if we are in a fat trigger */ G_AddPredictableEvent( other, EV_JUMP_PAD, 0 ); } VectorCopy (self->s.origin2, other->client->ps.velocity); } /** * \brief Calculate origin2 so the target apogee will be hit. * * Calculate origin2 so the target apogee will be hit. * * @param the trigger */ void AimAtTarget( gentity_t *self ) { gentity_t *ent; vec3_t origin; float height, gravity, time, forward; float dist; VectorAdd( self->r.absmin, self->r.absmax, origin ); VectorScale ( origin, 0.5, origin ); ent = G_PickTarget( self->target ); if ( !ent ) { G_FreeEntity( self ); return; } height = ent->s.origin[2] - origin[2]; gravity = g_gravity.value; time = sqrt( height / ( .5 * gravity ) ); if ( !time ) { G_FreeEntity( self ); return; } /* set s.origin2 to the push velocity */ VectorSubtract ( ent->s.origin, origin, self->s.origin2 ); self->s.origin2[2] = 0; dist = VectorNormalize( self->s.origin2); forward = dist / time; VectorScale( self->s.origin2, forward, self->s.origin2 ); self->s.origin2[2] = time * gravity; } /*QUAKED trigger_push (.5 .5 .5) ? -----DESCRIPTION----- Jumpfield/Booster Effect predicted on client side. This is activated by touch function. -----SPAWNFLAGS----- None -----KEYS----- "target" - apex of the leap. Must be a target_position or info_notnull. */ /** * \brief Spawn function of trigger_push. * * Spawn function of trigger_push. * * @param self the trigger */ void SP_trigger_push( gentity_t *self ) { InitTrigger (self); /* unlike other triggers, we need to send this one to the client */ self->r.svFlags &= ~SVF_NOCLIENT; self->s.eType = ET_PUSH_TRIGGER; self->touch = trigger_push_touch; self->think = AimAtTarget; self->nextthink = level.time + FRAMETIME; trap_LinkEntity (self); VectorCopy(self->r.maxs, self->s.apos.trBase); VectorCopy(self->r.mins, self->s.pos.trBase); level.numBrushEnts++; } /** * \brief Use function of target_push. * * Use function of target_push. * * @param self the entity * @param other another entity * @param activator the activator */ void Use_target_push( gentity_t *self, gentity_t *other, gentity_t *activator ) { if ( !activator->client ) { return; } /* RPG-X: J2J noclip use */ if ( activator->client->ps.pm_type != PM_NORMAL || activator->client->ps.pm_type != PM_NOCLIP) { return; } if ( activator->client->ps.powerups[PW_FLIGHT] ) { return; } VectorCopy (self->s.origin2, activator->client->ps.velocity); /* play fly sound every 1.5 seconds */ if ( activator->fly_sound_debounce_time < level.time ) { activator->fly_sound_debounce_time = level.time + 1500; G_Sound( activator, self->noise_index ); } } /*QUAKED target_push (.5 .5 .5) (-8 -8 -8) (8 8 8) ENERGYNOISE -----DESCRIPTION----- Pushes the activator in the direction of angle, or towards a target apex. This is predicted on the serverside and is triggered by use-function. -----SPAWNFLAGS----- 1: ENERGYNOISE - play energy noise instead of windfly -----KEYS----- "speed" - defaults to 1000 "target" - apex of the leap. Must be a target_position or info_notnull. */ /** * \brief Spawn function of target_push. * * Spawn function of target_push. * * @param self the entity */ void SP_target_push( gentity_t *self ) { if (!self->speed) { self->speed = 1000; } G_SetMovedir (self->s.angles, self->s.origin2); VectorScale (self->s.origin2, self->speed, self->s.origin2); if ( self->spawnflags & 1 ) { self->noise_index = G_SoundIndex("sound/ambience/forge/antigrav.wav"); } else { self->noise_index = G_SoundIndex("sound/misc/windfly.wav"); /* fixme need sound! */ } if ( self->target ) { VectorCopy( self->s.origin, self->r.absmin ); VectorCopy( self->s.origin, self->r.absmax ); self->think = AimAtTarget; self->nextthink = level.time + FRAMETIME; } self->use = Use_target_push; } /* ============================================================================== trigger_teleport ============================================================================== */ #define MAX_TRANSPORTER_POINTS 16 /** * \brief Select a random spawn point. * * Select a random spawn point. * * @return a random spawn point */ gentity_t *SelectRandomSpawnPoint( void ) { gentity_t *spot = NULL; int count = 0; int selection = 0; gentity_t *spots[MAX_TRANSPORTER_POINTS]; char *classname; classname = "info_player_deathmatch"; while ((spot = G_Find (spot, FOFS(classname), classname)) != NULL) { spots[ count++ ] = spot; if (count == MAX_TRANSPORTER_POINTS) { break; } } if ( !count ) { /* no spawn points !!??! */ return NULL; } selection = rand() % count; return spots[ selection ]; } /** * \brief Touch function of trigger_teleport. * * Touch function of trigger_teleport. * * @param self the trigger * @param other the touching entity * @param trace a trace */ void trigger_teleporter_touch (gentity_t *self, gentity_t *other, trace_t *trace ) { gentity_t *dest; vec3_t destPoint; trace_t tr; vec3_t tracePoint; int clientNum; if(self->flags & FL_LOCKED) return; if ( !other->client ) { return; } if ( other->client->ps.pm_type == PM_DEAD ) { return; } /* Spectators only? */ if ( ( self->spawnflags & 1 ) && other->client->sess.sessionTeam != TEAM_SPECTATOR ) { return; } clientNum = other->client->ps.clientNum; /* BOOKMARK J2J */ if (self->spawnflags & 2) { /* find a random spawn point */ dest = SelectRandomSpawnPoint(); } else { dest = G_PickTarget( self->target ); } if (!dest) { DEVELOPER(G_Printf (S_COLOR_YELLOW "[Entity-Error] Couldn't find teleporter destination\n");); return; } /* suspended */ if ( self->spawnflags & 8 ) { /* put the bottom of the player's bbox at the bottom of the target's bbox */ VectorCopy(dest->s.origin, destPoint); destPoint[2] += dest->r.mins[2]; destPoint[2] -= other->r.mins[2]; /* fudge it upwards just a bit */ destPoint[2] += 1; } else { VectorCopy( dest->s.origin, tracePoint ); tracePoint[2] -= 4096.0f; trap_Trace( &tr, dest->s.origin, dest->r.mins, dest->r.maxs, tracePoint, dest->s.number, MASK_PLAYERSOLID ); VectorCopy( tr.endpos, destPoint ); /* offset the player's bounding box. */ destPoint[2] += dest->r.mins[2]; destPoint[2] -= other->r.mins[2]; /* add 1 to ensure non-direct collision */ destPoint[2] += 1; } if (self->health) { /* * TiM - Transporter VFX * Only if no spitting though. Having spitting transporter with these VFX would look freaky weird */ if ( self->health == -1 && ( self->spawnflags & 4 ) ) { if ( TransDat[clientNum].beamTime == 0 ) { G_InitTransport( clientNum, destPoint, dest->s.angles ); } } else { TransportPlayer(other, destPoint, dest->s.angles, (self->health == -1)?0:self->health); } } else { /* Transporter VFX */ if ( self->spawnflags & 4 ) { if ( TransDat[clientNum].beamTime == 0 ) { G_InitTransport( clientNum, destPoint, dest->s.angles ); } } else { TeleportPlayer( other, destPoint, dest->s.angles, TP_NORMAL ); } } if(self->sound1to2 && self->wait && !(self->flags & FL_CLAMPED)) { G_AddEvent(self, EV_GENERAL_SOUND, self->sound1to2); self->flags ^= FL_CLAMPED; } } /** * \brief Think function of trigger_teleport. * * Think function of trigger_teleport. * * @param ent the trigger */ void trigger_teleport_think(gentity_t *ent) { ent->nextthink = -1; if(!(ent->flags & FL_LOCKED)) ent->flags ^= FL_LOCKED; if(ent->wait && (ent->flags & FL_CLAMPED)) ent->flags ^= FL_CLAMPED; } /** * \brief Use function of trigger_teleport. * * Use function of trigger_teleport. * * @param ent the trigger * @param other another entity * @param activator the activator */ void trigger_teleport_use(gentity_t *ent, gentity_t *other, gentity_t *activator) { if(!Q_stricmp(ent->swapname, activator->target)) if(ent->flags & FL_LOCKED) ent->nextthink = level.time + (ent->wait * 1000); ent->flags ^= FL_LOCKED; } /*QUAKED trigger_teleport (.5 .5 .5) ? SPECTATOR RANDOM VISUAL_FX SUSPENDED DEACTIVATED -----DESCRIPTION----- Allows client side prediction of teleportation events. Must point at a target_position or info_notnull, which will be the teleport destination. -----SPAWNFLAGS----- 1: SPECTATOR: If set, only spectators can use this teleport. Spectator teleporters are not normally placed in the editor, but are created automatically near doors to allow spectators to move through them. 2: RANDOM: send player to random info_player_deathmatch spawn point 4: VISUAL_FX: plays the Star Trek transporter FX and beams the player out slowly 8: SUSPENDED: player appears with the bounding box aligned to the bottom of the target If this isn't set, the player materializes at the first solid surface under it 16: DEACTIVATED: Spawns deactivated -----KEYS----- "swapname" - ACTIVATE/DEACTIVATE (Using entity needs SELF/NOACTIVATOR) "wait" - time before trigger deactivates itself automatically "soundstart" - sound to play if triggered "health" - default is original behavior (speed of 400), any other value will be the speed at which the player is spewed forth from the tranpsorter destination. -1 if you want no speed. The transporter VISUAL_FX flag will only work if the health is set to 0 or -1 as it cannot support 'spewing'. */ /** * \brief Spawn function of trigger_teleport. * * Spawn function of trigger_teleport. * * @param self the trigger */ void SP_trigger_teleport( gentity_t *self ) { char *temp; InitTrigger (self); /* * unlike other triggers, we need to send this one to the client * unless is a spectator trigger */ if ( self->spawnflags & 1) { self->r.svFlags |= SVF_NOCLIENT; } else { self->r.svFlags &= ~SVF_NOCLIENT; } self->s.eType = ET_TELEPORT_TRIGGER; self->touch = trigger_teleporter_touch; if(self->spawnflags & 16) self->flags ^= FL_LOCKED; self->use = trigger_teleport_use; if(self->wait) { self->nextthink = -1; self->think = trigger_teleport_think; } if(G_SpawnString("soundstart", "", &temp)) self->sound1to2 = G_SoundIndex(temp); VectorCopy(self->r.maxs, self->s.origin2); VectorCopy(self->r.mins, self->s.angles2); trap_LinkEntity (self); level.numBrushEnts++; } /* ============================================================================== trigger_hurt ============================================================================== */ /*QUAKED trigger_hurt (.5 .5 .5) ? START_OFF TOGGLE SILENT NO_PROTECTION SLOW EVO_PROTECT NO_ADMIN -----DESCRIPTION----- Any entity that touches this will be hurt. It does dmg points of damage each server frame Targeting the trigger will toggle its on / off state. -----SPAWNFLAGS----- 1: START_OFF - trigger will not be doing damage until toggled on 2: TOGGLE - can be toggled 4: SILENT - supresses playing the sound 8: NO_PROTECTION - *nothing* stops the damage 16: SLOW - changes the damage rate to once per second 32: EVO_PROTECT - Evosuit protects the client, even if NO_PROTECTION is set 64: NO_ADMIN - admins don't get hurt, even if NO_PROTECTION is set -----KEYS----- "dmg" - default 5 (whole numbers only) */ /** * \brief Use function of trigger_hurt. * * Use function of trigger hurt. * * @param self the trigger * @param other another entity * @param activator the activator */ void hurt_use( gentity_t *self, gentity_t *other, gentity_t *activator ) { self->count = !self->count; } /** * \brief Touch function of trigger_hurt. * * Touch function of trigger_hurt. * * @param self the trigger * @param other the touching entity * @param trace a trace */ void hurt_touch( gentity_t *self, gentity_t *other, trace_t *trace ) { int dflags; if(!self->count) return; /* * RPG-X | Phenix | 8/8/204 * (If the guy is wearning an evosuit) */ if ((self->spawnflags & 32) && (other->flags & FL_EVOSUIT)) { return; } if(self->spawnflags & 64) { if(IsAdmin(other)) return; } if ( !other->takedamage ) { return; } if ( self->timestamp > level.time ) { return; } /* RPG-X | Phenix | 9/8/2004 */ if (other->health <= 1) { return; } if ( self->spawnflags & 16 ) { self->timestamp = level.time + 1000; } else { self->timestamp = level.time + FRAMETIME; } /* play sound */ if ( !(self->spawnflags & 4) ) { G_Sound( other, self->noise_index ); } if (self->spawnflags & 8) dflags = DAMAGE_NO_PROTECTION; else dflags = 0; G_Damage (other, self, self, NULL, NULL, self->damage, dflags, MOD_TRIGGER_HURT); } /** * \brief Spawn function of trigger_hurt. * * Spawn function of trigger_hurt. * * @param self the trigger */ void SP_trigger_hurt( gentity_t *self ) { InitTrigger (self); /* TiM - gets very annoying after a while */ self->noise_index = G_SoundIndex( "sound/world/electro.wav" ); self->touch = hurt_touch; if ( !self->damage ) { self->damage = 5; } self->r.contents = CONTENTS_TRIGGER; if ( self->spawnflags & 2 ) { self->use = hurt_use; } self->count = !(self->spawnflags & 1); /* link in to the world if starting active | this won't work */ /*if ( ! (self->spawnflags & 1) ) { trap_LinkEntity (self); }*/ VectorCopy(self->r.maxs, self->s.origin2); VectorCopy(self->r.mins, self->s.angles2); trap_LinkEntity(self); level.numBrushEnts++; } /* ============================================================================== timer This should be renamed trigger_timer... ============================================================================== */ /*QUAKED func_timer (0.3 0.1 0.6) (-8 -8 -8) (8 8 8) START_ON -----DESCRIPTION----- Fires its targets every "wait" seconds. Can be turned on or off by using. -----SPAWNFLAGS----- 1: START_ON - will be on at spawn and setting up for it's first intervall -----KEYS----- "wait" - base time between triggering all targets, default is 1 "random" - wait variance, default is 0 so, the basic time between firing is a random time between (wait - random) and (wait + random) */ /** * \brief Think function of func_timer. * * Think function of func_timer. * * @param self the entity */ void func_timer_think( gentity_t *self ) { G_UseTargets (self, self->activator); /* set time before next firing */ self->nextthink = level.time + 1000 * ( self->wait + crandom() * self->random ); } /** * \brief Use function of func_timer. * * Use function of func_timer. * * @param self the entity * @param other another entity * @param activator the activator */ void func_timer_use( gentity_t *self, gentity_t *other, gentity_t *activator ) { self->activator = activator; /* if on, turn it off */ if ( self->nextthink ) { self->nextthink = 0; return; } /* turn it on */ func_timer_think (self); } /** * \brief Spawn function of func_timer. * * Spawn function of func_timer. * * @param self the entity */ void SP_func_timer( gentity_t *self ) { G_SpawnFloat( "random", "1", &self->random); G_SpawnFloat( "wait", "1", &self->wait ); self->use = func_timer_use; self->think = func_timer_think; if ( self->random >= self->wait ) { self->random = self->wait - FRAMETIME; DEVELOPER(G_Printf(S_COLOR_YELLOW "[Entity-Warning] func_timer at %s has random >= wait\n", vtos( self->s.origin ) );); } if ( self->spawnflags & 1 ) { self->nextthink = level.time + FRAMETIME; self->activator = self; } self->r.svFlags = SVF_NOCLIENT; } /*QUAKED trigger_transporter (0.5 0.5 0.5) ? -----DESCRIPTION----- This is used in combination with ui_transporter. Have this be targeted by ui_transporter. -----SPAWNFLAGS----- none -----KEYS----- "wait" time to wait before trigger gets deactivated again(in seconds, default 5) "soundstart" transport sound; */ /** * \brief Think function of trigger_transporter. * * Think function of trigger_transporter. * * @param ent the trigger */ void trigger_transporter_think(gentity_t *ent) { if(Q_stricmp(ent->classname, "tent")) { ent->nextthink = -1; ent->flags ^= FL_LOCKED; ent->flags ^= FL_CLAMPED; } else { ent->target_ent->flags ^= FL_LOCKED; ent->target_ent->flags ^= FL_CLAMPED; G_FreeEntity(ent); } } /** * \brief Think function of trigger_transporter for server change. * * Think function of trigger_transporter for server change. * * @param ent the trigger */ void trigger_transporter_serverchange(gentity_t *ent) { trap_SendServerCommand(ent->touched-g_entities, va("cg_connect \"%s\"\n", ent->targetname2)); G_FreeEntity(ent); } extern srvChangeData_t srvChangeData; /** * \brief Touch function of trigger_transporter. * * Touch function of trigger_transporter. * * @param ent the trigger * @param other the touching entity * @param trace a trace */ void trigger_transporter_touch(gentity_t *ent, gentity_t *other, trace_t *trace) { char *srv; vec3_t offset = { 0, 0, 12 }; vec3_t target; gentity_t *targetEnt; gentity_t *tent; int clientNum; if((ent->flags & FL_LOCKED) || (!other->client) || (other->flags & FL_CLAMPED)) return; clientNum = other->client->ps.clientNum; if(ent->sound1to2 && !(ent->flags & FL_CLAMPED)) { G_AddEvent(other, EV_GENERAL_SOUND, ent->sound1to2); ent->flags ^= FL_CLAMPED; } other->flags ^= FL_CLAMPED; if(!ent->count) if(ent->target_ent->target) { targetEnt = G_PickTarget(ent->target_ent->target); VectorAdd(targetEnt->s.origin, offset, target); G_InitTransport(clientNum, target, targetEnt->s.angles); } else { G_InitTransport(clientNum, ent->target_ent->s.origin, ent->target_ent->s.angles); } else { srv = srvChangeData.ip[ent->health]; tent = G_Spawn(); tent->think = trigger_transporter_serverchange; tent->nextthink = level.time + 3000; TransDat[clientNum].beamTime = level.time + 8000; other->client->ps.powerups[PW_BEAM_OUT] = level.time + 8000; tent->touched = other; tent->targetname2 = G_NewString(srv); tent = G_Spawn(); tent->classname = G_NewString("tent"); tent->target_ent = ent; tent->think = trigger_transporter_think; tent->nextthink = level.time + ent->wait; } } /** * \brief Delay think function of trigger_transporter. * * Delay think function of trigger_transporter. * * @param ent the trigger */ void trigger_transporter_delay(gentity_t *ent) { ent->think = trigger_teleport_think; ent->nextthink = level.time + ent->wait; ent->flags ^= FL_LOCKED; } /** * \brief Spawn function of trigger_transporter. * * Spawn function of trigger_transporter. * * @param ent the trigger */ void SP_trigger_transporter(gentity_t *ent) { char *temp; InitTrigger(ent); if(!ent->wait) { DEVELOPER(G_Printf(S_COLOR_YELLOW "[Entity-Error] trigger_transporter without wait at %s!\n", vtos(ent->s.origin));); G_FreeEntity(ent); return; } if(G_SpawnString("soundstart", "", &temp)) ent->sound1to2 = G_SoundIndex(temp); if(!ent->wait) ent->wait = 5; ent->wait *= 1000; ent->touch = trigger_transporter_touch; ent->flags ^= FL_LOCKED; VectorCopy(ent->r.maxs, ent->s.apos.trBase); VectorCopy(ent->r.mins, ent->s.pos.trBase); trap_LinkEntity(ent); level.numBrushEnts++; } /*QUAKED trigger_radiation (0.5 0.5 0.5) ? START_OFF MAP_WIDE -----DESCRIPTION----- This can be used in three ways: - as radiation volume trigger - as mapwide radiation -----SPAWNFLAGS----- 1: START_OFF - ent is off at spawn 2: MAP_WIDE - mapwide radiation -----KEYS----- The damage the radiation does is calculated from these two values: "dmg" damage(default 1) "wait" wait(seconds, default 10) Forumla is: dps = dmg / wait */ /** * \brief Touch function of trigger_radiation. * * Touch function of trigger_radiation. * * @param ent the trigger * @param other the touching entity * @param trace a trace */ void trigger_radiation_touch(gentity_t *ent, gentity_t *other, trace_t *trace) { if(!other || !other->client || !ent->count) return; if(!(ent->flags & FL_LOCKED)) { if(other->health - ent->damage < 0) other->health = 0; else other->health -= ent->damage; other->client->ps.stats[STAT_HEALTH] = other->health; ent->flags ^= FL_LOCKED; } /* TODO: display radiation symbol? */ } /** * \brief Think function of trigger_radiation. * * Think function of trigger_radiation. * * @param ent the trigger */ void trigger_radiation_think(gentity_t *ent) { if(ent->flags & FL_LOCKED) ent->flags ^= FL_LOCKED; } /** * \brief Use function of trigger_radiation. * * Use function of trigger_radiation. * * @param ent the trigger * @param other another entity * @param activator the activator */ void trigger_radiation_use(gentity_t *ent, gentity_t *other, gentity_t *activator) { ent->count = !ent->count; } /** * \brief Spawn function of trigger_radiation. * * Spawn function of trigger_radiation. * * @param ent the trigger */ void SP_trigger_radiation(gentity_t *ent) { if(!ent->damage) ent->damage = 1; if(!ent->wait) ent->wait = 10000; else ent->wait *= 1000; InitTrigger(ent); ent->count = !(ent->spawnflags & 1); if(!(ent->spawnflags & 2)) ent->touch = trigger_radiation_touch; ent->think = trigger_radiation_think; ent->nextthink = level.time + ent->wait; ent->flags ^= FL_LOCKED; ent->use = trigger_radiation_use; VectorCopy(ent->r.maxs, ent->s.apos.trBase); VectorCopy(ent->r.mins, ent->s.pos.trBase); trap_LinkEntity(ent); if(!ent->tmpEntity) level.numBrushEnts++; }