Files
quakemapping/mod_xj18/my_progs/triggers.qc
2020-01-07 11:54:38 +01:00

2030 lines
77 KiB
Plaintext

/*======================================================================
TRIGGER FUNCTIONS
======================================================================*/
float TRIG_SPAWNBUBBLES = 2; // Produce bubbles
float TRIG_SECRETNOMSG = 2; // No default message
float TRIG_TELEPLAYER = 1; // Player only
float TRIG_TELESILENT = 2; // No teleport sound
float TRIG_TELEALWAYSON = 4; // Trigger teleports starts on
float TRIG_HURTMONSTER = 4; // Only hurt monsters
float TRIG_HURTFALLING = 32; // Only hurt if player falling
float TRIG_PUSHONCE = 1; // Switch off after one use
float TRIG_PUSHSILENT = 2; // No wind sound for player
float TRIG_PUSHNOMONSTER = 4; // Monsters cannot use this push trigger
float TRIG_CONVMONSTER = 2; // Conveyor moving monsters
float TRIG_CONVITEM = 4; // Conveyor moving items
float TRIG_CONVPUSHABLE = 8; // Conveyor moving pushables
float TRIG_CMAPNOPAUSE = 1; // No info screen, next map
float TRIG_CMAPRESETINV = 2; // Reset inventory (shotgun+25shells)
float TRIG_CMAPSECSPAWN = 4; // Spawn at the second spawn point
float TRIG_MONJUMPFLY = 2; // Will affect flying units
float TRIG_MONJUMPSWIM = 4; // Will affect swimming units
float TRIG_TSOUNDWGEO = 4; // World Geometry interaction
float TRIG_TSOUNDDRAIN = 8; // Drain effect when disabled
float TRIG_VOIDNOCLIENT = 1; // Clients will suffer no damage
float TRIG_VOIDNOMONSTER = 2; // Monsters will be immune to the void
float TRIG_VOIDNOAMMO = 4; // Ammo projectiles will pass through
float TRIG_VOIDNOGG = 8; // Minion eggs will ignore the void
float TRIG_VOIDNOTEMP = 16; // Temporary entities will exist longer
float TRIG_VOIDNOITEM = 32; // All items will carry on as before
//======================================================================
// If a dead body has touched a trigger, setup removal
//======================================================================
float(entity body_ent, float body_death) trigger_check_body =
{
if (body_ent.flags & FL_MONSTER && body_ent.deadflag == DEAD_DEAD) {
body_ent.deadflag = body_death;
body_ent.touchedvoid = TRUE;
return TRUE;
}
return FALSE;
};
//======================================================================
// Setup/Spawn/Remove bubbles inside a trigger volume
//======================================================================
void() trigger_setup_bubbles =
{
// Quake compilers can add the style key to any entity connected
// to dynamic lights, this is usually 32-64 in value and should
// not interfere, but never use style > 32 for any values
if (self.style == 1) self.mdl = "progs/s_bubble_grey.spr";
else if (self.style == 2) self.mdl = "progs/s_bubble_brnd1.spr";
else if (self.style == 4) self.mdl = "progs/s_bubble_grn1.spr";
else if (self.style == 5) self.mdl = "progs/s_bubble_red1.spr";
else if (self.style == 6) self.mdl = "progs/s_bubble_brnd2.spr";
else if (self.style == 7) self.mdl = "progs/s_bubble_pinkyel.spr";
else if (self.style == 8) self.mdl = "progs/s_bubble_brnl1.spr";
else if (self.style == 9) self.mdl = "progs/s_bubble_purp1.spr";
else if (self.style == 10) self.mdl = "progs/s_bubble_purp2.spr";
else if (self.style == 11) self.mdl = "progs/s_bubble_brnl2.spr";
else if (self.style == 12) self.mdl = "progs/s_bubble_grn2.spr";
else if (self.style == 13) self.mdl = "progs/s_bubble_yellow.spr";
else if (self.style == 14) self.mdl = "progs/s_bubble_blue2.spr";
else if (self.style == 15) self.mdl = "progs/s_bubble_red2.spr";
else self.mdl = "progs/s_bubble_blue1.spr";
precache_model(self.mdl);
// Setup defaults for healing bubbles
self.waitmin = self.bubble_count = 0; // Reset counters
if (!self.count) self.count = 5; // max active bubbles
if (!self.height) self.height = self.size_z;
if (self.height < 64) self.height = 64; // min top of volume
self.oldorigin = bmodel_origin(self);
self.oldorigin_z = self.mins_z;
self.t_width = (self.size_x/2);
self.t_length = (self.size_y/2);
// Default spawn rate for bubbles
if (self.yaw_speed <= 0) self.yaw_speed = 0.5;
};
//----------------------------------------------------------------------
void() trigger_remove_bubble =
{
if (other == self.owner) return; // Touching self, do nothing
if (other.solid == SOLID_TRIGGER) return; // trigger field, do nothing
self.touch = SUB_Null;
if (self.owner.bubble_count > 0)
self.owner.bubble_count = self.owner.bubble_count - 1;
remove(self);
};
//----------------------------------------------------------------------
void() trigger_update_bubble =
{
// The size of the trigger is the default height for the bubbles
// to raise up, or The height can be specified as a value
self.lefty = fabs(self.origin_z - self.owner.oldorigin_z);
if (self.lefty > self.owner.height) {
trigger_remove_bubble();
}
else {
// Wobble on the X / Y axis as the bubble raises up
self.velocity_x = self.velocity_y = 0;
if (random() < 0.5) self.velocity_x = self.velocity_x + crandom()*4;
else self.velocity_y = self.velocity_y + crandom()*4;
// Make the bubble raise faster by random amounts
self.velocity_z = self.velocity_z + random();
// Keep updating bubble, 15 updates is the limit
self.nextthink = time + 0.5 + random()*0.5;
if (self.count < 15) self.count = self.count + 1;
else trigger_remove_bubble();
}
};
//----------------------------------------------------------------------
void() trigger_spawn_bubbles =
{
local entity bubble;
if (self.estate & ESTATE_BLOCK) return;
if ( !(self.spawnflags & TRIG_SPAWNBUBBLES) ) return;
if (self.bubble_count < self.count) {
self.bubble_count = self.bubble_count + 1;
bubble = spawn();
bubble.owner = self;
bubble.classname = "misc_bubble";
bubble.classtype = CT_BUBBLE;
bubble.classgroup = CG_TEMPENT;
bubble.movetype = MOVETYPE_NOCLIP;
bubble.solid = SOLID_TRIGGER;
setmodel(bubble, self.mdl);
bubble.frame = rint(random() * 3); // Light/dark colours
setsize (bubble, VEC_ORIGIN, VEC_ORIGIN);
bubble.origin_x = self.oldorigin_x + crandom()*self.t_width;
bubble.origin_y = self.oldorigin_y + crandom()*self.t_length;
bubble.origin_z = self.oldorigin_z;
setorigin(bubble, bubble.origin);
bubble.velocity = vecrand(0,5,TRUE);
bubble.velocity_z = 10 + random()*15;
bubble.count = rint(random()*4);
bubble.nextthink = time + 0.5 + random()*0.5;
bubble.think = trigger_update_bubble;
bubble.touch = trigger_remove_bubble;
}
// Keep spawning bubbles until told not too!
self.think = trigger_spawn_bubbles;
self.nextthink = time + self.yaw_speed + random()*self.yaw_speed;
};
/*======================================================================
/*QUAKED trigger_multiple (0.5 0 0.5) ? NOTOUCH x DEVMODE x MODCHECK MONSTERS STARTOFF x Not_Easy Not_Normal Not_Hard Not_DM
Variable sized bmodel that uses multiple times
------- KEYS --------
targetname : trigger entity (works with entity state system)
target : trigger all these targets
upgrade_ssg : = 1 will only trigger if shotgun upgrade active on server
upgrade_axe : = 1 will only trigger if axe upgrade active on server
upgrade_lg : = 1 will only trigger if lightning gun upgrade active on server
health : Can be damaged instead of touched
wait : time between re-triggering
delay : delay before firing (after being triggered)
angle : Facing Direction for trigger to work, use "360" for angle 0.
sounds : 1=Secret,2=talk(def),3=switch,4=silent,5=custom,6=secret2
noise : custom sound to play when triggered
message : message to display when triggered
-------- SPAWNFLAGS --------
NOTOUCH : can only be triggered via other entities
DEVMODE : Will only trigger if developer mode active
MODCHECK : Will remove this entity if THIS mod is active
MONSTER : can be touched/triggered by monsters
STARTOFF : Requires trigger to activate
------- NOTES --------
Variable sized bmodel that uses multiple times
======================================================================*/
void() trigger_multiple =
{
if (check_bmodel_keys()) return; // Check for bmodel errors
if (self.spawnflags & TRIG_MODCHECK) { remove(self); return; }
trigger_bmodel_sounds(); // Precache any sounds
self.classtype = CT_TRIGMULTI;
InitTrigger ();
if (!self.wait) self.wait = 0.2;
// Setup Entity State functionality
trigger_bmodel_setup();
};
/*======================================================================
/*QUAKED trigger_once (0.5 0 0.5) ? NOTOUCH x DEVMODE INVIEW MODCHECK MONSTER STARTOFF x Not_Easy Not_Normal Not_Hard Not_DM
Variable sized bmodel that uses once
-------- KEYS --------
targetname : trigger entity (works with entity state system)
target : trigger all these targets
upgrade_ssg : = 1 will only trigger if shotgun upgrade active on server
upgrade_axe : = 1 will only trigger if axe upgrade active on server
upgrade_lg : = 1 will only trigger if lightning gun upgrade active on server
health : Can be damaged instead of touched
wait : Always -1
delay : delay before firing (after being triggered)
angle : Facing Direction for trigger to work, use "360" for angle 0.
sounds : 1=Secret,2=talk(def),3=switch,4=silent,5=custom,6=secret2
noise : custom sound to play when triggered
message : message to display when triggered
t_length : Inview distance (less than) to activate trigger
-------- SPAWNFLAGS --------
NOTOUCH : can only be triggered via other entities
DEVMODE : Will only trigger if developer mode active
INVIEW : Player has to be infront and look at trigger (>30 & <60)
MODCHECK : Will remove this entity if THIS mod is active
MONSTER : can be touched/triggered by monsters
STARTOFF : Requires trigger to activate
-------- NOTES --------
Variable sized bmodel that uses once
======================================================================*/
void() trigger_once =
{
if (check_bmodel_keys()) return; // Check for bmodel errors
if (self.spawnflags & TRIG_MODCHECK) { remove(self); return; }
trigger_bmodel_sounds(); // Precache any sounds
self.classtype = CT_TRIGONCE;
InitTrigger ();
self.wait = -1;
// Inview triggers need to start delayed, high cpu functions
if (self.spawnflags & TRIG_INVIEW) {
self.spawnflags = self.spawnflags | ENT_STARTOFF;
// Cannot have inview triggers touched by monsters
self.spawnflags = self.spawnflags - (self.spawnflags & TRIG_MONSTERS);
}
// Setup Entity State functionality
trigger_bmodel_setup();
};
/*======================================================================
/*QUAKED trigger_secret (.5 0 .5) ? NOTOUCH NOMSG x x x x STARTOFF x Not_Easy Not_Normal Not_Hard Not_DM
Update map secret counter
------- KEYS --------
targetname : trigger entity (works with entity state system)
target : name of target(s) to trigger
upgrade_ssg : = 1 will only trigger if shotgun upgrade active on server
upgrade_axe : = 1 will only trigger if axe upgrade active on server
upgrade_lg : = 1 will only trigger if lightning gun upgrade active on server
health : Can be damaged instead of touched
wait : Always -1
angle : Facing Direction for trigger to work, use 360 for angle 0.
sounds : 1=Secret(def),2=talk,3=switch,4=silent,5=custom,6=secret2
noise : custom sound to play when triggered
message : message to display when triggered
-------- SPAWNFLAGS --------
NOTOUCH : can only be triggered via other entities
NOMSG : Remove/Block any trigger secret message
STARTOFF : Requires trigger to activate
------- NOTES --------
Update map secret counter
======================================================================*/
void() trigger_secret =
{
if (check_bmodel_keys()) return; // Check for bmodel errors
if (self.sounds == 0) self.sounds = 1;
trigger_bmodel_sounds(); // Precache any sounds
self.classtype = CT_TRIGSECRET;
InitTrigger ();
self.wait = -1; // Trigger ONCE
self.count = 1; // Add 1 secret
total_secrets = total_secrets + self.count;
if (!self.message) self.message = "You found a secret area!";
if (self.spawnflags & TRIG_SECRETNOMSG) self.message = "";
// Cannot have secret triggers touched by monsters
self.spawnflags = self.spawnflags - (self.spawnflags & TRIG_MONSTERS);
// Setup Entity State functionality
trigger_bmodel_setup();
};
/*======================================================================
/*QUAKED trigger_teleport (0.5 0 0.5) ? PLAYER_ONLY SILENT STARTON x x x STARTOFF x Not_Easy Not_Normal Not_Hard Not_DM
Teleport player/monsters to target location
-------- KEYS --------
targetname : trigger entity (works with entity state system)
target : Points to info_teleport_destination entity
wait : -1 = trigger once condition (def=0)
speed : forward momentum speed after teleporting (def=300)
volume : teleporter hum sound volume (def=0.5)
noise : custom sound to play when active (must be looped, def=hum1.wav)
waitmin : the length of the custom sound (def=3.622 for hum1.wav)
-------- SPAWNFLAGS --------
PLAYER_ONLY : Can only be used by players (nothing else)
SILENT : No teleporter hum sound regardless of state
STARTON : Will start active regardless of targetname setting
STARTOFF : Starts off and waits for trigger
-------- NOTES --------
Teleport player/monsters to target location
if targetname is setup, the teleporter requires a trigger to activate
This entity cannot be damaged and is always touchable once activated
======================================================================*/
void() trigger_teleport_sound =
{
// Has the original bmodel trigger been deleted!?!
if (!self.owner) {
sound (self, CHAN_VOICE, SOUND_EMPTY, 1, ATTN_NORM);
return;
}
// The Quake sound system is really basic and dumb! :)
// Originally the sound of the teleporter was setup as an ambientsound,
// which always play, loop and never can be turned off!
//
// Normal sounds are ONLY active when the player is in the same portal!
// This is probably an optimization (engine) thing to save time
// constantly checking attenuation levels
//
// The teleporter sound is manually looped because of portal problems
// and that the trigger can be turned on / off.
// The waitmin parameter has to match the length of the sound, otherwise
// the looping will literally sound odd
//
// Keep checking for any updates
self.nextthink = time + 0.1;
self.think = trigger_teleport_sound;
// Wait for trigger to start before doing anything
if (self.owner.spawnflags & ENT_STARTOFF) return;
// Has the trigger been switched off recently?
if (self.owner.estate != self.estate) {
// Check for OFF and disabled together
if (self.owner.estate & ESTATE_BLOCK) {
self.estate = self.owner.estate;
sound (self, CHAN_VOICE, SOUND_EMPTY, 1, ATTN_NORM);
self.fly_sound = LARGE_TIMER;
return;
}
else {
// trigger has been switched on, reset sound
self.estate = self.owner.estate;
self.fly_sound = 0;
}
}
// Play the sound if loop has finished or been reset
if (self.fly_sound < time ) {
self.fly_sound = time + self.owner.waitmin;
sound (self, CHAN_VOICE, self.owner.noise, self.owner.volume, ATTN_STATIC);
}
};
//----------------------------------------------------------------------
// This is used for the closet monster setup where the trigger is fired
// and then anything touching the trigger 0.2s later is teleported
// This forces all touch triggers in a map to check what they are touching!
//----------------------------------------------------------------------
void() trigger_teleport_use =
{
// Deal with START OFF functionality first
if (self.spawnflags & ENT_STARTOFF) {
// Remove start off flag and switch on entity
self.spawnflags = self.spawnflags - ENT_STARTOFF;
self.estate_on();
return;
}
// Block entity state DISABLE
if (self.estate & ESTATE_DISABLE) return;
if (self.estate & ESTATE_OFF) self.estate_on();
// Force retouch is really hard on the engine because EVERY trigger
// in the map is re-checked for any touching objects
force_retouch = 2;
self.nextthink = time + 0.2;
self.think = SUB_Null;
};
//----------------------------------------------------------------------
void() trigger_teleport_on =
{
self.estate = ESTATE_ON;
self.state = STATE_ON;
};
//----------------------------------------------------------------------
void() trigger_teleport_off =
{
self.estate = ESTATE_OFF;
self.state = STATE_OFF;
};
//----------------------------------------------------------------------
void() trigger_teleport_touch =
{
if (self.estate & ESTATE_BLOCK) return; // Entity off/disabled?
if (self.spawnflags & ENT_STARTOFF) return; // Starts off?
if (self.attack_finished > time) return; // Trigger once?
if (self.state == STATE_OFF) return; // Waiting for trigger?
// Is the teleporter designed for players only?
if (self.spawnflags & TRIG_TELEPLAYER && !(other.flags & FL_CLIENT)) return;
// only teleport living creatures and monsters (mostly)
if (other.health < 1 || other.solid != SOLID_SLIDEBOX) return;
// Find teleporter target
if (!self.goalentity) {
self.goalentity = find (world, targetname, self.target);
if (!self.goalentity) {
dprint("\b[TELEPORT_TOUCH]\b Cannot find target\n");
return;
}
// New feature, fire targets on teleporter destination
if (self.goalentity.classtype == CT_MISCTELEPORT && self.goalentity.target != "") {
trigger_strs(self.goalentity.target, other); // Fire all targets
self.goalentity.target = ""; // only work once
}
// This stuff never changes, might as well generate it now
makevectors (self.goalentity.mangle);
self.goalentity.movedir = v_forward;
self.goalentity.pos1 = self.goalentity.origin + 32 * self.goalentity.movedir;
}
// Check for a trigger_once condition
if (self.wait < 0) {
self.attack_finished = LARGE_TIMER;
self.estate_off();
}
// Fire all targets on trigger teleporter entity (ID code)
// not sure why? the target *should* be pointing at info_destination
// Could use killtarget or message strings
SUB_UseTargets ();
// put a tfog where the player was
spawn_tfog (other.origin);
// spawn a tfog flash in front of the destination
spawn_tfog (self.goalentity.pos1);
spawn_tdeath(self.goalentity.origin, other);
// Add a little forward momentum to teleporting entity
// This is usually used for gib/telefrag effects
if (!other.health) {
other.origin = self.goalentity.origin;
other.velocity = (self.goalentity.movedir * other.velocity_x) + (self.goalentity.movedir * other.velocity_y);
return;
}
// Move/rotate teleporting entity to new location
setorigin (other, self.goalentity.origin);
other.angles = self.goalentity.mangle;
// If player, rotate (immediately) and push forward
if (other.flags & FL_CLIENT) {
other.fixangle = 1; // turn this way immediately
other.teleport_time = time + 0.7; // Special state for engine
other.velocity = self.goalentity.movedir * self.speed;
}
// Telporting entities need to check for ground
other.flags = other.flags - (other.flags & FL_ONGROUND);
};
//----------------------------------------------------------------------
void() trigger_teleport =
{
if (check_bmodel_keys()) return; // Check for bmodel errors
// Always pre-cache telporter teleporter sound
if (self.noise == "") self.noise = "ambience/hum1.wav";
precache_sound (self.noise);
// The length of the custom sound cannot be < 0.1s
if (self.waitmin <= 0.1) self.waitmin = 3.622;
self.classtype = CT_TRIGTELEPORT;
InitTrigger ();
// A trigger teleporter going nowhere!?!
// Need to initialize trigger to get mins/maxs for origin
if (self.target == "") {
dprint("\b[TRIGGER_TELEPORT]\b Missing target\n");
self.oldorigin = bmodel_origin(self);
spawn_marker(self.oldorigin, SPNMARK_YELLOW);
remove(self);
return;
}
// Spawnflag 1 is used for something else (notouch duplicate)
// No trigger damage functionality and always touchable!
self.spawnflags = self.spawnflags | TRIG_ALWAYTOUCH | TRIG_NODAMAGE;
if (!self.volume) self.volume = 0.5;
if (!self.speed) self.speed = 300;
// Save this for later (sound emitter)
self.oldorigin = bmodel_origin(self);
// Trigger teleport is a special case in the way it is setup because
// is it abused for monster closets, where the trigger is always active
self.touch = trigger_teleport_touch;
self.solid = SOLID_TRIGGER;
setsize (self, self.bbmins, self.bbmaxs);
// Setup Entity State functionality
self.estate_on = trigger_teleport_on;
self.estate_off = trigger_teleport_off;
self.estate_use = trigger_teleport_use;
self.use = entity_state_use;
// Damn annoying that the targetname is being used like this because
// there could have been a better way to do this type of functionality
// == "" teleporter works fine (starts on)
// != "" teleporter requires trigger activation
if (self.spawnflags & TRIG_TELEALWAYSON) self.estate = ESTATE_ON;
else if (self.spawnflags & ENT_STARTOFF) self.estate = ESTATE_OFF;
else if (self.targetname != "") self.estate = ESTATE_OFF;
else self.estate = ESTATE_ON;
// Setup sound emitter for teleporter state
if ( !(self.spawnflags & TRIG_TELESILENT) ) {
self.attachment = spawn();
self.attachment.owner = self;
self.attachment.estate = -1;
self.attachment.movetype = MOVETYPE_NONE;
self.attachment.solid = SOLID_NOT;
setorigin(self.attachment, self.oldorigin);
//setmodel(self.attachment, MODEL_BROKEN);
self.attachment.nextthink = time + 1;
self.attachment.think = trigger_teleport_sound;
}
};
/*======================================================================
/*QUAKED trigger_changelevel (0.5 0 0.5) ? NO_INTERMIS RESETINV x x x x STARTOFF x Not_Easy Not_Normal Not_Hard Not_DM
Finish current map, show intermission screen and loads next
-------- KEYS --------
targetname : trigger entity (works with entity state system)
target : name of target(s) to trigger before intermission
map : The name of next map (e.g. e1m1) default=same map
startspawn2: Special unique number (1-7) which must match info_player_start2
-------- SPAWNFLAGS --------
NO_INTERMIS : No Intermission screen
RESETINV : Reset player inventory to default (Shotgun+Shells)
STARTOFF : Starts off and waits for trigger
-------- NOTES --------
Finish current map, show intermission screen and loads next
This entity cannot be damaged and is always touchable once activated
======================================================================*/
void() trigger_changelevel_finish =
{
intermission_running = 1;
// enforce a wait time before allowing changelevel
if (deathmatch) intermission_exittime = time + 5;
else intermission_exittime = time + 2;
WriteByte (MSG_ALL, SVC_CDTRACK); // Update CD track
WriteByte (MSG_ALL, SVC_UPDATESTAT); // Update stats, total kills etc
WriteByte (MSG_ALL, SVC_UPDATESTAT); // Update twice?
StartIntermissionCamera(); // Setup intermission camera(s)
WriteByte (MSG_ALL, SVC_INTERMISSION); // Start intermission (lock movement)
};
//----------------------------------------------------------------------
void() trigger_changelevel_fire =
{
if (self.attack_finished > time) return;
if ( !(self.bmodel_act.flags & FL_CLIENT) ) return;
if ( self.bmodel_act.health < 1 ) return;
self.attack_finished = LARGE_TIMER;
// Check for map variables on exit
if (!CheckZeroVector(self.mapvar_update))
mapvar_range(self.mapvar_update);
// Reset player inventory back to ID default (shotgun+25shells)
if (self.spawnflags & TRIG_CMAPRESETINV) {
dprint("\b[CHANGELVL]\b Resetting client inventory!\n");
self.bmodel_act.health = HEAL_PLAYMAX;
self.bmodel_act.armortype = self.bmodel_act.armorvalue = 0;
self.bmodel_act.ammo_shells = DEF_SHELLS;
self.bmodel_act.ammo_nails = self.bmodel_act.ammo_rockets = self.bmodel_act.ammo_cells = 0;
self.bmodel_act.items = IT_SHOTGUN | IT_AXE;
self.bmodel_act.moditems = 0;
self.bmodel_act.weapon = IT_SHOTGUN;
}
// Some crazy DM parameters to prevent teleport exit
if ((cvar("noexit") == 1) || ((cvar("noexit") == 2) && (mapname != "start"))) {
T_Damage (self.bmodel_act, self, self, MEGADEATH, NOARMOR); return;
}
// More crazy DM stuff
if (coop || deathmatch) {
bprint (self.bmodel_act.netname);
bprint (" exited the level\n");
}
// is there a special info_player_start2 location setup?
// This will also reset any values back to zero (default)
if (self.startspawn2 < 1 && self.startspawn2 > 7) self.startspawn2 = 0;
update_configflag(SVR_SPAWN_BIT1, floor( (self.startspawn2 & 1) / 1) );
update_configflag(SVR_SPAWN_BIT2, floor( (self.startspawn2 & 2) / 2) );
update_configflag(SVR_SPAWN_BIT3, floor( (self.startspawn2 & 4) / 4) );
// *change* if map key not defined, reload current map again
if (self.map) nextmap = self.map;
SUB_UseTargets ();
// If no intermission, go straight to next map
if ( (self.spawnflags & TRIG_CMAPNOPAUSE) && (deathmatch == 0) ) {
GotoNextMap();
return;
}
// we can't move people right now, because touch functions are called
// in the middle of C movement code, so set a think time to do it
self.think = trigger_changelevel_finish;
self.nextthink = time + 0.1;
};
//----------------------------------------------------------------------
void() trigger_changelevel_setup =
{
// If map not defined, use current mapname instead
if (!self.map) self.map = mapname;
};
//----------------------------------------------------------------------
// Re-direction for map hacks (not used normally)
//----------------------------------------------------------------------
void() changelevel_touch = { trigger_changelevel_fire();};
//----------------------------------------------------------------------
void() trigger_changelevel =
{
if (check_bmodel_keys()) return; // Check for bmodel errors
self.classtype = CT_TRIGLEVEL;
InitTrigger ();
// Spawnflag 1 is used for something else (notouch duplicate)
// No trigger damage functionality and always touchable!
self.spawnflags = self.spawnflags | TRIG_ALWAYTOUCH | TRIG_NODAMAGE;
// Cannot have change level triggers touched by monsters
self.spawnflags = self.spawnflags - (self.spawnflags & TRIG_MONSTERS);
// Setup Entity State functionality
if (self.targetname != "") self.use = entity_state_use;
self.estate_fire = trigger_changelevel_fire;
trigger_bmodel_setup();
// Wait for everything to spawn before checking map name
self.think = trigger_changelevel_setup;
self.nextthink = time + 1;
};
/*======================================================================
/*QUAKED trigger_setskill (0.5 0 0.5) ? NOTOUCH x x x x x STARTOFF x Not_Easy Not_Normal Not_Hard Not_DM
Sets player skill level (via console)
-------- KEYS --------
targetname : trigger entity (works with entity state system)
health : Can be damaged instead of touched
wait : time between re-triggering (def=0.2s, -1=once)
angle : Facing Direction for trigger to work, use "360" for angle 0.
message : Skill Level - 0 = easy, 1 = normal, 2 = hard, 3 = nightmare
-------- SPAWNFLAGS --------
NOTOUCH : can only be triggered via other entities
STARTOFF : Starts off and waits for trigger
-------- NOTES --------
Sets player skill level (via console)
======================================================================*/
void() trigger_setskill_fire =
{
if (self.attack_finished > time) return;
// There is no client check for the skill level change so that
// the use functionality can work from trigger chains
cvar_set ("skill", self.message);
// Is the trigger repeatable?
if (self.wait > 0) {
self.attack_finished = time + self.wait;
self.nextthink = self.attack_finished;
self.think = self.estate_on;
}
// block trigger and turn off (trigger_once)
else {
self.attack_finished = LARGE_TIMER;
self.estate_off();
}
};
//----------------------------------------------------------------------
void() trigger_setskill =
{
if (check_bmodel_keys()) return; // Check for bmodel errors
self.classtype = CT_TRIGSKILLS;
InitTrigger ();
if (!self.wait) self.wait = 1;
if (self.message == "") self.message = "0";
// Cannot have skill triggers touched by monsters
self.spawnflags = self.spawnflags - (self.spawnflags & TRIG_MONSTERS);
// Setup Entity State functionality
self.estate_fire = trigger_setskill_fire;
trigger_bmodel_setup();
};
/*======================================================================
/*QUAKED trigger_rune (0.5 0 0.5) ? E1 E2 E3 E4 x x STARTOFF x Not_Easy Not_Normal Not_Hard Not_DM
A Trigger that fires once if the player has certain runes
-------- KEYS --------
targetname : trigger entity (works with entity state system)
health : Can be damaged instead of touched
angle : Facing Direction for trigger to work, use "360" for angle 0.
target : trigger to fire if player has MIXTURE of runes
noise1 : trigger to fire if player has rune 1
noise2 : trigger to fire if player has rune 2
noise3 : trigger to fire if player has rune 3
noise4 : trigger to fire if player has rune 4
wait : = -1 Only trigger once if the player has runes
-------- SPAWNFLAGS --------
E1 : Episode 1
E2 : Episode 2
E3 : Episode 3
E4 : Episode 4
STARTOFF : Starts off and waits for trigger
-------- NOTES --------
There are two ways this trigger can be used, a single check for multiple
runes using target key OR individual triggers for runes using noise 1-4 keys
This trigger is designed to work once when rune conditions are met
======================================================================*/
void() trigger_rune_fire =
{
// This is after bmodel _use, _killed and _touch, so any reference
// to the trigger activator has to go through 'bmodel_act'
// Is the trigger blocked? (trigger_once)
if (self.attack_finished > time) return;
// Stop the trigger constantly firing
self.attack_finished = time + self.waitmin;
// Check for single rune trigger
if (self.target) {
if (query_configflag(SVR_RUNE_ALL) & self.customkey == self.customkey) {
trigger_strs(self.target, self.bmodel_act);
if (self.wait < 0) self.attack_finished = LARGE_TIMER;
}
}
else {
// Check for multiple rune triggers
if (query_configflag(self.customkey & SVR_RUNE_KEY1) == SVR_RUNE_KEY1 && self.noise1 != "") {
trigger_strs(self.noise1, self.bmodel_act);
if (self.wait < 0) self.attack_finished = LARGE_TIMER;
}
if (query_configflag(self.customkey & SVR_RUNE_KEY2) == SVR_RUNE_KEY2 && self.noise2 != "") {
trigger_strs(self.noise2, self.bmodel_act);
if (self.wait < 0) self.attack_finished = LARGE_TIMER;
}
if (query_configflag(self.customkey & SVR_RUNE_KEY3) == SVR_RUNE_KEY3 && self.noise3 != "") {
trigger_strs(self.noise3, self.bmodel_act);
if (self.wait < 0) self.attack_finished = LARGE_TIMER;
}
if (query_configflag(self.customkey & SVR_RUNE_KEY4) == SVR_RUNE_KEY4 && self.noise4 != "") {
trigger_strs(self.noise4, self.bmodel_act);
if (self.wait < 0) self.attack_finished = LARGE_TIMER;
}
}
// The attack_finished is set by the RUNE condition being TRUE
// The trigger is designed to be trigger_once (no need to check wait)
// The trigger needs to meet a rune condition before switching off
if (self.attack_finished == LARGE_TIMER) self.estate_off();
};
//----------------------------------------------------------------------
void() trigger_rune =
{
if (check_bmodel_keys()) return; // Check for bmodel errors
self.classtype = CT_TRIGRUNES;
// Spawnflag 1 is used for something else (notouch duplicate)
self.spawnflags = self.spawnflags | TRIG_ALWAYTOUCH;
InitTrigger ();
if (self.waitmin <=0) self.waitmin = 1;
// Cannot have rune triggers touched by monsters
self.spawnflags = self.spawnflags - (self.spawnflags & TRIG_MONSTERS);
// Calculate the rune key selection
self.customkey = self.spawnflags & SVR_RUNE_ALL;
// Cannot do anything with the trigger if no runes are selected
if (self.customkey == 0) {
dprint("\b[TRIG_RUNE]\b No runes setup!\n");
self.oldorigin = bmodel_origin(self);
spawn_marker(self.oldorigin, SPNMARK_YELLOW);
remove(self);
return;
}
// Setup Entity State functionality
self.estate_fire = trigger_rune_fire;
trigger_bmodel_setup();
};
/*======================================================================
/*QUAKED trigger_push (0.5 0 0.5) ? PUSH_ONCE SILENT NOMONSTER x x x STARTOFF x Not_Easy Not_Normal Not_Hard Not_DM
Push the Player and Grenades!
-------- KEYS --------
targetname : trigger entity (works with entity state system)
target : target entity for custom direction
angle : direction of push (-2 is down, -1 up)
angles : Pitch Yaw Roll (up/down, angle, tilt left/right)
speed : Speed of push direction (def=1000)
-------- SPAWNFLAGS --------
PUSH_ONCE : trigger_once functionality
SILENT : No wind sound for player
NOMONSTER : Monsters cannot be pushed by this trigger
STARTOFF : Starts off and waits for trigger
-------- NOTES --------
Push the Player, player/ogre grenades and minion eggs!
This entity cannot be damaged and is always touchable once activated
======================================================================*/
void() trigger_push_fire =
{
// This is after bmodel _use, _killed and _touch, so any reference
// to the trigger activator has to go through 'bmodel_act'
if (self.estate & ESTATE_BLOCK) return;
if (self.attack_finished > time) return;
// Check for any dead monster bodies (no exceptions)
if (trigger_check_body(self.bmodel_act,DEAD_EXPLODE)) return;
// Check for no monster exception
if (self.spawnflags & TRIG_PUSHNOMONSTER && self.bmodel_act.flags & FL_MONSTER) return;
// Cool feature is that it will push grenades!
// Added player, ogre and minion eggs to the catch
if (self.bmodel_act.classtype == CT_PROJ_GL ||
self.bmodel_act.classtype == CT_PROJ_GLMON ||
self.bmodel_act.classtype == CT_PROJ_MEGG)
self.bmodel_act.velocity = self.speed * self.movedir * 10;
// Standard push for all bmodel_act living entities
else if (self.bmodel_act.health > 0) {
self.bmodel_act.velocity = self.speed * self.movedir * 10;
// Play wind sound for the player
if (self.bmodel_act.classtype == CT_PLAYER) {
if (self.bmodel_act.fly_sound < time) {
self.bmodel_act.fly_sound = time + 1.5;
sound (self.bmodel_act, CHAN_AUTO, self.noise, 1, ATTN_NORM);
}
}
}
// Setup to trigger push once?
if (self.wait < 0) {
self.attack_finished = LARGE_TIMER;
self.estate_off();
}
};
//----------------------------------------------------------------------
// Map hack reference
void() trigger_push_touch = { self.bmodel_act = other; trigger_push_fire(); }
//----------------------------------------------------------------------
void() trigger_push =
{
if (check_bmodel_keys()) return; // Check for bmodel errors
self.classtype = CT_TRIGPUSH;
InitTrigger ();
// Spawnflag 1 is used for something else (notouch duplicate)
// No trigger damage functionality and always touchable!
self.spawnflags = self.spawnflags | TRIG_ALWAYTOUCH | TRIG_NODAMAGE;
if (!self.speed) self.speed = 1000;
// Setup default wind sound if no custom sound found
// else if silent trigger, use misc/empty sound
if ( !(self.spawnflags & TRIG_PUSHSILENT) && self.noise == "")
self.noise = "ambience/windfly.wav";
if (self.noise == "") self.noise = SOUND_EMPTY;
precache_sound (self.noise);
if (self.spawnflags & TRIG_PUSHONCE) self.wait = -1;
// Setup Entity State functionality
self.estate_fire = trigger_push_fire;
self.touch = trigger_bmodel_anytouch;
trigger_bmodel_setup();
// If target is setup, calculate new facing angle
if (self.target != "") {
self.nextthink = time + 2;
self.think = TargetMovedir;
}
};
//----------------------------------------------------------------------
// Removed - No map used the feature
//----------------------------------------------------------------------
void() trigger_conveyor = { remove(self); };
/*======================================================================
Player ladder (originally from Rubicon2 codebase by JohnFitz)
- This is a very simple system, jump to attach to the ladder brush
- move up down via jumpping (hook in preplayer code)
- Added multiple climbing sounds (works with player footsound state)
- Modified to have on/off/toggle state via triggers
- Downsides to system, there is no abilty to go down a ladder
/*======================================================================
/*QUAKED trigger_ladder (.5 .5 .5) ? x x x x x x STARTOFF x Not_Easy Not_Normal Not_Hard Not_DM
Invisible brush based ladder (jump key to climb)
-------- KEYS --------
targetname : trigger entity (works with entity state system)
angle : direction player must be facing to climb ladder (required)
waitmin : time between climb sound (def = depends on sound type)
speed : velocity speed to climb ladder (def=160)
sounds : 1=metal, 2=wood, 3=rope, 4=silent, 5=custom (def=wood)
noise1-4 : custom sounds to play when climbing ladder
-------- SPAWNFLAGS --------
STARTOFF : Starts off and waits for trigger
-------- NOTES --------
Invisible brush based ladder (jump key to climb)
This entity cannot be damaged and is always touchable once activated
======================================================================*/
void() trigger_ladder_fire =
{
// This is after bmodel _use, _killed and _touch, so any reference
// to the trigger activator has to go through 'bmodel_act'
// Ladders ONLY work with players because of client.qc (PlayerPreThink)
if (self.estate & ESTATE_BLOCK) return;
if ( !(self.bmodel_act.flags & FL_CLIENT) ) return;
if ( self.bmodel_act.health < 1 ) return;
// Don't stick underwater, or in the middle of a waterjump
if (self.bmodel_act.waterlevel > 1) return;
if (self.bmodel_act.flags & FL_WATERJUMP) return;
self.bmodel_act.onladder = 1; // Add everytime the player touches volume
self.bmodel_act.entladder = self; // Link back to play sounds
};
//----------------------------------------------------------------------
void() trigger_ladder =
{
if (check_bmodel_keys()) return; // Check for bmodel errors
// Default = wood
if (!self.sounds) self.sounds = 2;
if (self.sounds == 1) { // Metal
if(!self.waitmin) self.waitmin = 0.45;
self.noise1 = "player/ladmetal1.wav";
self.noise2 = "player/ladmetal2.wav";
self.noise3 = "player/ladmetal3.wav";
self.noise4 = "player/ladmetal4.wav";
}
else if (self.sounds == 2) { // Wood
if(!self.waitmin) self.waitmin = 0.4;
self.noise1 = "player/ladwood1.wav";
self.noise2 = "player/ladwood2.wav";
self.noise3 = "player/ladwood3.wav";
self.noise4 = "player/ladwood4.wav";
}
else if (self.sounds == 3) { // Old Rope
if(!self.waitmin) self.waitmin = 0.7;
self.noise1 = "player/ladrope1.wav";
self.noise2 = "player/ladrope2.wav";
self.noise3 = "player/ladrope3.wav";
self.noise4 = "player/ladrope4.wav";
}
else {
// Custom or empty
if (!self.waitmin) self.waitmin = 0.5;
if (self.noise1 == "") self.noise1 = SOUND_EMPTY;
if (self.noise2 == "") self.noise2 = SOUND_EMPTY;
if (self.noise3 == "") self.noise3 = SOUND_EMPTY;
if (self.noise4 == "") self.noise4 = SOUND_EMPTY;
}
precache_sound(self.noise1);
precache_sound(self.noise2);
precache_sound(self.noise3);
precache_sound(self.noise4);
self.classtype = CT_TRIGLADDER;
InitTrigger ();
if (!self.speed) self.speed = 160;
// No trigger damage functionality and always touchable!
self.spawnflags = self.spawnflags | TRIG_ALWAYTOUCH | TRIG_NODAMAGE;
// Cannot have ladder triggers touched by monsters
self.spawnflags = self.spawnflags - (self.spawnflags & TRIG_MONSTERS);
// Setup Entity State functionality
self.estate_fire = trigger_ladder_fire;
trigger_bmodel_setup();
};
/*======================================================================
/*QUAKED trigger_giveitems (0.5 0 0.5) ? NOTOUCH x x x x x STARTOFF x Not_Easy Not_Normal Not_Hard Not_DM
Variable sized bmodel used to give items to player
The target items only work if delay spawned spawnflag is set
The pickup sound can be turned off by adding sounds=4 to item
The target items will not respawn or work more than once
-------- KEYS --------
targetname : trigger entity (works with entity state system)
target : Points to all items to give to the player
wait : (def) -1 = will only fire targets once
angle : Facing Direction for trigger to work, use "360" for angle 0.
sounds : 1=Secret,2=talk(def),3=switch,4=silent,5=custom,6=secret2
noise : custom sound to play when triggered
message : message to display when triggered
-------- SPAWNFLAGS --------
NOTOUCH : can only be triggered via other entities
STARTOFF : Requires trigger to activate
-------- NOTES --------
Variable sized bmodel used to give items to activator
The target items only work if delay spawned spawnflag is set
The pickup sound can be turned off by adding sounds=4 to item
The target items will not respawn or work more than once
======================================================================*/
void() trigger_giveitem_fire =
{
local entity stemp;
// Is the trigger blocked? (trigger_once)
if (self.attack_finished > time) return;
// Activator can only be the player
if (!(self.bmodel_act.flags & FL_CLIENT)) return;
if (self.bmodel_act.health < 1) return;
if (self.target == "") return;
// Save for later and reset activator
stemp = self; activator = self.bmodel_act;
// Play the sound ON the activator and display message
if (self.noise != "") sound (activator, CHAN_VOICE, self.noise, 1, ATTN_NORM);
if (self.message != "") centerprint (activator, self.message);
// Search entity list for targets
self.enemy = find (world, targetname, self.target);
while(self.enemy) {
// This only works with items
if (self.enemy.flags & FL_ITEM) {
// Only works with items that start off
if (self.enemy.spawnflags & ENT_STARTOFF) {
if (self.enemy.touch2 != SUB_Null) {
// Switch to item for touch function
other = self.bmodel_act;
activator = self.bmodel_act;
self = self.enemy;
// make sure items never respawn and silent pickup
self.spawnflags = self.spawnflags - (self.spawnflags & ITEM_RESPAWN);
self.respawn_time = -1;
self.respawn_trig = FALSE;
// item should always be floating (no drop to floor functions)
self.spawnflags = self.spawnflags | ITEM_FLOATING;
// give trigger sound overrides all items
if (stemp.sounds != 4) self.noise = SOUND_EMPTY;
// Use original touch function
self.touch2 ();
self = stemp;
}
}
}
// Are there anymore targets left in the list?
self.enemy = find (self.enemy, targetname, self.target);
}
// always TRIGGER_ONCE functionality
self.attack_finished = LARGE_TIMER;
};
//----------------------------------------------------------------------
void() trigger_giveitems =
{
if (check_bmodel_keys()) return; // Check for bmodel errors
if (self.sounds == 0) self.sounds = 4; // Default = silence
trigger_bmodel_sounds(); // Precache any sounds
self.classtype = CT_TRIGGIVEITEM;
InitTrigger ();
// Always trigger once functionality
self.wait = -1;
// Cannot have trigger give items touched by monsters
self.spawnflags = self.spawnflags - (self.spawnflags & TRIG_MONSTERS);
// Setup Entity State functionality
self.estate_fire = trigger_giveitem_fire;
trigger_bmodel_setup();
};
/*======================================================================
/*QUAKED trigger_fog (.5 .5 .5) ? x x x x x x STARTOFF x Not_Easy Not_Normal Not_Hard Not_DM
Trigger change global fog to new value over time
-------- KEYS --------
targetname : trigger entity (works with entity state system)
target : name of target(s) to trigger
speed : time (secs) to fade from current to new (-1 = instant, 2s = default)
wait : time between re-triggering (def=2s, -1=once)
angle : Facing Direction for trigger to work, use "360" for angle 0.
fog_density : new fog density (def=0.5, -1=debug mode)
fog_colour : new fog colours (def=0.1 0.1 0.1)
-------- SPAWNFLAGS --------
STARTOFF : Starts off and waits for trigger
-------- NOTES --------
Trigger change global fog to new value over time
======================================================================*/
void() trigger_fog_fire =
{
// This is after bmodel _use, _killed and _touch, so any reference
// to the trigger activator has to go through 'bmodel_act'
if (self.estate & ESTATE_BLOCK) return;
if (self.attack_finished > time) return;
if (query_configflag(SVR_NOFOGCMDS)) {
dprint("\b[FOG]\b quake.rc disabled, removing!\n");
self.attack_finished = LARGE_TIMER;
entity_remove(self, 0.1);
return;
}
// Print dev message if worldspawn not setup correctly
// First fog blend will be weird otherwise
if (!fog_active) {
if (self.pain_finished < time) {
dprint("\b[FOG]\b Missing density + colour on worldspawn!\n");
dprint("\b[FOG]\b First fog blend requires worldspawn setup\n");
}
self.pain_finished = time + 1;
}
// Crazy test option, random fog!
if (self.lefty) {
self.fog_density = random();
self.fog_colour_x = random();
self.fog_colour_y = random();
self.fog_colour_z = random();
self.speed = 1 + random()*4;
}
// Update global fog controller
fade_fog(self.fog_density, self.fog_colour, self.speed);
SUB_UseTargets();
// Is the trigger repeatable?
if (self.wait > 0) {
if (self.wait < self.speed) self.wait = self.speed + 0.1;
self.attack_finished = time + self.wait;
self.nextthink = self.attack_finished;
self.think = self.estate_on;
}
// block trigger and turn off (trigger_once)
else {
self.attack_finished = LARGE_TIMER;
self.estate_off();
}
};
//----------------------------------------------------------------------
void() trigger_fog_waitforcontrol =
{
if (fog_control && fog_active) {
// Setup Entity State functionality
self.estate_fire = trigger_fog_fire;
trigger_bmodel_setup();
}
else {
self.think = trigger_fog_waitforcontrol;
self.nextthink = time + 0.1 + random();
}
};
//----------------------------------------------------------------------
void() trigger_fog =
{
if (check_bmodel_keys()) return; // Check for bmodel errors
self.classtype = CT_TRIGFOG;
InitTrigger ();
// A fade time < minimum fade time = instant change
if (self.speed == 0) self.speed = 2;
if (self.speed < FADEFOG_TIME) self.speed = FADEFOG_TIME;
if (self.wait == 0) self.wait = 2;
self.attack_finished = 0;
// Cannot have fog triggers touched by monsters
self.spawnflags = self.spawnflags - (self.spawnflags & TRIG_MONSTERS);
// Check for debug test mode (random density/colour)
if (self.fog_density < 0) self.lefty = TRUE;
// Default density/colour
if (!self.fog_density) self.fog_density = 0.1;
if (CheckZeroVector(self.fog_colour)) self.fog_colour = '0.1 0.1 0.1';
trigger_fog_waitforcontrol();
};
/*======================================================================
/*QUAKED trigger_monsternojump (.5 .5 .5) ? x x x x x x STARTOFF x Not_Easy Not_Normal Not_Hard Not_DM
Stop monsters from using jump attack
-------- KEYS --------
targetname : trigger entity (works with entity state system)
wait : -1 = trigger_once functionality
delay : time to delay jump attack by (def=0.5s)
waitmin: Re-trigger timer to stop touch flooding (def=0.1s)
noise1 : specify classname that CAN use this trigger (noise1=monster_dog)
-------- SPAWNFLAGS --------
STARTOFF : Starts off and waits for trigger
-------- NOTES --------
Stop monsters from using jump attack
======================================================================*/
void() trigger_monsternojump_fire =
{
// This is after bmodel _use, _killed and _touch, so any reference
// to the trigger activator has to go through 'bmodel_act'
if (self.estate & ESTATE_BLOCK) return;
if (self.attack_finished > time) return;
// Block non-monsters
if (!(self.bmodel_act.flags & FL_MONSTER)) return;
// Is there any classname exception setup?
if (self.noise1 != "") {
if (self.bmodel_act.classname != self.noise1) return;
}
// Check for any extra triggers, fire them once!
if (self.target != "") {
trigger_strs(self.target, activator);
self.target = "";
}
// Update jump flag to block jump attacks
self.bmodel_act.jump_flag = time + self.delay;
// Restrict the trigger to 0.1s re-triggering
self.attack_finished = time + self.waitmin;
// Setup to trigger push once?
if (self.wait < 0) {
self.attack_finished = LARGE_TIMER;
self.estate_off();
}
};
//----------------------------------------------------------------------
void() trigger_monsternojump =
{
if (check_bmodel_keys()) return; // Check for bmodel errors
self.classtype = CT_TRIGNOJUMP;
if (!self.delay) self.delay = 0.5;
if (!self.waitmin) self.waitmin = 0.1;
// Work out dimensions of trigger
InitTrigger ();
// No trigger damage functionality and always touchable!
self.spawnflags = self.spawnflags | TRIG_ALWAYTOUCH | TRIG_NODAMAGE;
// Setup Entity State functionality
self.estate_fire = trigger_monsternojump_fire;
self.touch = trigger_bmodel_anytouch;
trigger_bmodel_setup();
};
//----------------------------------------------------------------------
// Re-direct because it was renamed to be more consistent
void() trigger_nomonjump = { trigger_monsternojump(); };
/*======================================================================
/*QUAKED trigger_monsterdrop (0 .5 .5) (-8 -8 -8) (8 8 8) x x x x x x STARTOFF x Not_Easy Not_Normal Not_Hard Not_DM
Drop monster(s) to floor
-------- KEYS --------
targetname : trigger entity (works with entity state system)
wait : -1 = trigger_once functionality
height : the speed thrown upwards (def 50)
-------- SPAWNFLAGS --------
STARTOFF : Starts off and waits for trigger
-------- NOTES --------
Drop monster(s) to floor
======================================================================*/
void() trigger_monsterdrop_use =
{
if (self.estate & ESTATE_BLOCK) return;
if (self.attack_finished > time) return;
if (self.target == "") return;
// Build initial list from world
self.oldenemy = find(world, targetname, self.target);
while (self.oldenemy) {
// only works with monsters
if (self.oldenemy.flags & FL_MONSTER) {
// Double check for swim/fly types
if (!(self.oldenemy.flags & (FL_FLY || FL_SWIM))) {
// Got to be onground already
if (self.oldenemy.flags & FL_ONGROUND) {
// Set monster in motion
self.oldenemy.flags = self.oldenemy.flags - FL_ONGROUND;
self.oldenemy.velocity_z = self.height;
}
}
}
// Find next monster in list
self.oldenemy = find(self.oldenemy, targetname, self.target);
}
// Setup to trigger once functionality?
if (self.wait < 0) {
self.attack_finished = LARGE_TIMER;
self.estate_off();
}
};
//----------------------------------------------------------------------
void() trigger_monsterdrop =
{
self.classtype = CT_TRIGMONDROP;
if (!self.height) self.height = 50;
// Check for firing conditions (nightmare, coop)
if (check_nightmare() == TRUE) return;
if (check_coop() == TRUE) return;
// Setup Entity State functionality
if (self.targetname != "") self.use = entity_state_use;
self.estate_use = trigger_monsterdrop_use;
self.estate = ESTATE_ON;
};
/*======================================================================
/*QUAKED trigger_monsterjump (.5 .5 .5) ? x FLYING SWIMMING x x x STARTOFF x Not_Easy Not_Normal Not_Hard Not_DM
Push monsters in a certain direction
-------- KEYS --------
targetname : trigger entity (works with entity state system)
target : target entity for custom direction
wait : -1 = trigger_once functionality
angle : direction of push (-2 is down, -1 up)
speed : the speed thrown forward (def 200)
height : the speed thrown upwards (def 200)
noise1 : specify classname that CAN use this trigger (noise1=monster_dog)
-------- SPAWNFLAGS --------
FLYING : Will affect flying monsters
SWIMMING : Will affect swimming monsters
STARTOFF : Starts off and waits for trigger
-------- NOTES --------
Push monsters in a certain direction
This entity cannot be damaged and is always touchable once activated
======================================================================*/
void() trigger_monsterjump_fire =
{
// This is after bmodel _use, _killed and _touch, so any reference
// to the trigger activator has to go through 'bmodel_act'
if (self.estate & ESTATE_BLOCK) return;
if (self.attack_finished > time) return;
// Cannot move spawning statues
if (self.bmodel_act.spawnstatue) return;
// Check monster is not jumping already
if (self.bmodel_act.jump_flag > time) return;
// Block non-monsters and fly/swim if spawnflag not set
if (!(self.bmodel_act.flags & FL_MONSTER)) return;
if (!(self.spawnflags & TRIG_MONJUMPFLY) && self.bmodel_act.flags & FL_FLY) return;
if (!(self.spawnflags & TRIG_MONJUMPSWIM) && self.bmodel_act.flags & FL_SWIM) return;
// Is there any classname exception setup?
if (self.noise1 != "") {
if (self.bmodel_act.classname != self.noise1) return;
// extra special condition for enraged drole's
if (self.bmodel_act.classtype == CT_MONDROLE && !self.bmodel_act.attack_rage) return;
}
// Check for any triggers, fire them once!
if (self.target != "") {
trigger_strs(self.target, activator);
self.target = "";
}
// Flying/Swimming monsters only need a push in the right direction
if (self.bmodel_act.flags & FL_FLY || self.bmodel_act.flags & FL_SWIM) {
self.bmodel_act.velocity = self.movedir * self.speed;
}
else {
// set XY even if not on ground, so the jump will clear lips
self.bmodel_act.velocity_x = self.movedir_x * self.speed;
self.bmodel_act.velocity_y = self.movedir_y * self.speed;
// If monster on the ground, lift them up
if (self.bmodel_act.flags & FL_ONGROUND ) {
self.bmodel_act.flags = self.bmodel_act.flags - FL_ONGROUND;
self.bmodel_act.velocity_z = self.height;
}
}
// Setup to trigger push once?
if (self.wait < 0) {
self.attack_finished = LARGE_TIMER;
self.estate_off();
}
};
//----------------------------------------------------------------------
void() trigger_monsterjump =
{
if (check_bmodel_keys()) return; // Check for bmodel errors
self.classtype = CT_TRIGMONJUMP;
if (!self.speed) self.speed = 200;
if (!self.height) self.height = 200;
if (self.angles_y == 0) self.angles_y = 360;
// Work out facing angle
InitTrigger ();
// No trigger damage functionality and always touchable!
self.spawnflags = self.spawnflags | TRIG_ALWAYTOUCH | TRIG_NODAMAGE;
// Setup Entity State functionality
self.estate_fire = trigger_monsterjump_fire;
self.touch = trigger_bmodel_anytouch;
trigger_bmodel_setup();
// If target is setup, calculate new facing angle
if (self.target != "") {
self.nextthink = time + 2;
self.think = TargetMovedir;
}
};
//----------------------------------------------------------------------
void() trigger_drolejump =
{
self.noise1 = "monster_drole";
trigger_monsterjump();
};
/*======================================================================
/*QUAKED trigger_monsterturret (0.5 0.3 0) ? x x x x x x STARTOFF x Not_Easy Not_Normal Not_Hard Not_DM
Temporarily turn a monster into a turret
-------- KEYS --------
targetname : trigger entity (works with entity state system)
wait : -1 = Only trigger a monster turret function once
count : random chance to pause; constant = -1, def = 0.25, range = 0 - 1
noise1 : only works with this type of monster (monster_ogre)
-------- SPAWNFLAGS --------
STARTOFF : Starts off and waits for trigger
-------- NOTES --------
Temporarily turn a monster into a turret
This entity cannot be damaged and is always touchable once activated
======================================================================*/
void() trigger_monsterturret_fire =
{
// This is after bmodel _use, _killed and _touch, so any reference
// to the trigger activator has to go through 'bmodel_act'
local entity tself;
if (self.estate & ESTATE_BLOCK) return;
if (self.attack_finished > time) return; // trigger_once functionality
if (!(self.bmodel_act.flags & FL_MONSTER)) return; // ONLY Monsters!
if (self.bmodel_act.health < 1) return; // Dead things cannot be turrets!
if (self.bmodel_act.movespeed < 0) return; // Already a turret!?!
if (!self.bmodel_act.enemy) return; // This is a combat node only
if (self.bmodel_act.turretactive) return; // Already using a turret position
if (!self.bmodel_act.th_missile) return; // Only works if got range attack
if (self.noise1 != "") { if (self.noise1 != self.bmodel_act.classname) return; }
// Switch to monster
tself = self;
self = self.bmodel_act; other = tself;
// check enemy visibility and direction
if (visible(self.enemy) && infront(self.enemy)) {
// Link the monster to the turret (used by ai_run)
self.turretactive = other;
}
self = tself;
// Setup to trigger push once?
if (self.wait < 0) {
self.attack_finished = LARGE_TIMER;
self.estate_off();
}
};
//----------------------------------------------------------------------
void() trigger_monsterturret =
{
if (check_bmodel_keys()) return; // Check for bmodel errors
self.classtype = CT_TRIGMONTURRET;
if (self.count < 0) self.count = -1;
else if (self.count == 0 || self.count > 1) self.count = 0.25;
InitTrigger ();
// No trigger damage functionality and always touchable!
self.spawnflags = self.spawnflags | TRIG_ALWAYTOUCH | TRIG_NODAMAGE;
// Setup Entity State functionality
self.estate_fire = trigger_monsterturret_fire;
self.touch = trigger_bmodel_anytouch;
trigger_bmodel_setup();
};
/*======================================================================
/*QUAKED trigger_hurt (.5 .5 .5) ? x BUBBLES MONSTER_ONLY x MODCHECK FALLING STARTOFF x Not_Easy Not_Normal Not_Hard Not_DM
Will hurt any touching entity
-------- KEYS --------
targetname : trigger entity (works with entity state system)
dmg : damage from contact with trigger (def=5)
wait : time between pain contact (def=1s)
angle : Facing Direction for trigger to work, use "360" for angle 0.
speed : used by falling spawnflag for velocity check (def=300)
height : Maximum travel distance up for bubbles (default trigger size)
count : total amount of active bubbles at once (default 5)
style : 1-15 (grey,brown1,blue1,green1,red1,brown2,pinkyel,brown3,purp1,purp2,brown4,green2,yellow,blue2,red2)
-------- SPAWNFLAGS --------
BUBBLES : Spawn bubbles within trigger volume when active
MONSTER_ONLY : Will only affect monsters
MODCHECK : Will remove this entity if THIS mod is active
FALLING : Only hurts if the player is falling (speed=velocity)
STARTOFF : Starts off and waits for trigger
-------- NOTES --------
Will hurt any touching entity that can take damage
This entity cannot be damaged and is always touchable once activated
======================================================================*/
void() trigger_hurt_fire =
{
// This is after bmodel _use, _killed and _touch, so any reference
// to the trigger activator has to go through 'bmodel_act'
if (self.attack_finished > time) return;
if (self.bmodel_act.takedamage == DAMAGE_NO) return;
if (self.spawnflags & TRIG_HURTMONSTER && !(self.bmodel_act.flags & FL_MONSTER)) return;
// Check for falling damage conditions (player + flying + speeding)
if (self.spawnflags & TRIG_HURTFALLING) {
// Only affects players and monsters
if (!(self.bmodel_act.flags & FL_MONSTER) &&
!(self.bmodel_act.flags & FL_CLIENT)) return;
if (self.bmodel_act.flags & FL_ONGROUND) return;
// Can't kill something dead already!
if (self.bmodel_act.health < 1) return;
if (fabs(self.bmodel_act.velocity_z) < self.speed) return;
}
// Check for any dead monster bodies (no exceptions)
if (trigger_check_body(self.bmodel_act,DEAD_EXPLODE)) return;
// Check for godmode players taking screenshots!
if (self.bmodel_act.flags & FL_CLIENT && self.bmodel_act.flags & FL_GODMODE) return;
// Block touch function based on wait time
self.attack_finished = time + self.wait;
T_Damage (self.bmodel_act, self, self, self.dmg, DAMARMOR);
};
//----------------------------------------------------------------------
void() trigger_hurt_on =
{
self.estate = ESTATE_ON;
self.solid = SOLID_TRIGGER;
// Restore bounding box (dev testing visual thing)
setsize (self, self.bbmins, self.bbmaxs);
// Spawn bubbles inside volume brush
trigger_spawn_bubbles();
};
//----------------------------------------------------------------------
void() trigger_hurt =
{
if (check_bmodel_keys()) return; // Check for bmodel errors
if (self.spawnflags & TRIG_MODCHECK) { remove(self); return; }
self.classtype = CT_TRIGHURT;
InitTrigger ();
// No trigger damage functionality and always touchable!
self.spawnflags = self.spawnflags | TRIG_ALWAYTOUCH | TRIG_NODAMAGE;
if (self.dmg < 0) self.dmg = 0;
else if (self.dmg == 0) self.dmg = 5;
if (self.wait <= 0) self.wait = 1;
// Setup bubble model/counter/volume
if (self.spawnflags & TRIG_SPAWNBUBBLES) trigger_setup_bubbles();
// Setup default falling damage speed
if (self.spawnflags & TRIG_HURTFALLING && self.speed < 1) self.speed = 300;
// Setup Entity State functionality
if (self.targetname != "") self.use = entity_state_use;
self.estate_on = trigger_hurt_on;
self.estate_off = trigger_bmodel_off;
self.estate_disable = trigger_bmodel_disable;
if (self.dmg > 0) {
self.estate_fire = trigger_hurt_fire;
self.touch = trigger_bmodel_anytouch;
}
// Switch on OR off?
if (self.spawnflags & ENT_STARTOFF) self.estate_off();
else self.estate_on();
};
/*======================================================================
/*QUAKED trigger_heal (.5 .5 .5) ? x BUBBLES x x x x STARTOFF x Not_Easy Not_Normal Not_Hard Not_DM
Heals any player who touches this trigger
-------- KEYS --------
targetname : trigger entity (works with entity state system)
target : trigger once when players stands in trigger (start cycle)
target2 : trigger once when healing expires (finish cycle)
wait : time between healing (default 1s)
healamount : amount to heal each time touched (default 10)
max_health : total amount to heal (default 50, -1=no limit)
sounds : 1=heal_15, 2=heal_25, 3=heal_100, 4=respawn, 5=custom (default 1)
noise : Custom sound for healing
lip : All messages are silent to the player (heal/expire) 1=block heal 2=block all
message : centerprints when players stands in trigger
message2 : centerprints when healing function has expired
height : Maximum travel distance up for bubbles (default trigger size)
count : total amount of active bubbles at once (default 5)
style : 1-15 (grey,brown1,blue1,green1,red1,brown2,pinkyel,brown3,purp1,purp2,brown4,green2,yellow,blue2,red2)
yaw_speed : spawning rate (def=0.5) for bubbles (speed + random() x speed)
-------- SPAWNFLAGS --------
BUBBLES : Spawn bubbles within trigger volume when active
STARTOFF : Starts off and waits for trigger
-------- NOTES --------
Heals any player who touches this trigger, can be triggered on/off and produces
bubbles within the bounding box of the trigger when spawnflag enabled
======================================================================*/
void() trigger_heal_touch =
{
if (self.estate & ESTATE_BLOCK) return; // Function off/disabled
if (self.attack_finished > time) return; // Touch blocked (temporary)
if ( !(other.flags & FL_CLIENT) ) return; // Only works with clients
if (other.health < 1 ) return; // Cannot heal, target is dead
if (self.health < 1) return; // Run out of health
// Do not constantly check healing, use wait
self.attack_finished = time + self.wait;
// Can the pool heal the player?
if (!T_Heal(other, self.healamount, 0)) return;
// Healing sound
sound (other, CHAN_BODY, self.noise, 1, ATTN_NORM);
// Has the pool run out of health?
if (self.max_health > 0) self.health = self.health - self.healamount;
// Has the healing trigger expired?
if (self.health < 1) {
// Only block message lip=1 healing 2=all messages
if (self.lip < 2) centerprint (other, self.message2);
// Fire any targets once (finish of healing cycle)
if (self.target2 != "") {trigger_strs(self.target2, other);self.target2 = "";}
// Switch off healing trigger
entity_state_off();
}
else {
// Fire any targets once (start of healing cycle)
if (self.target != "") {trigger_strs(self.target, other);self.target = "";}
// Display healing message (check for lip block)
if (!self.lip) centerprint (other, self.message);
}
};
//----------------------------------------------------------------------
void() trigger_heal_on =
{
if (self.health > 0) {
self.estate = ESTATE_ON;
self.solid = SOLID_TRIGGER;
// Restore bounding box (dev testing visual thing)
setsize (self, self.bbmins, self.bbmaxs);
// Spawn bubbles inside volume brush
trigger_spawn_bubbles();
}
};
//----------------------------------------------------------------------
void() trigger_heal_reset =
{
if (self.max_health > 0) {
// Reset health, targets and switch on entity
self.health = self.max_health;
if (self.noise1 != "") self.target = self.noise1;
if (self.noise2 != "") self.target2 = self.noise2;
self.estate_on();
}
};
//----------------------------------------------------------------------
void() trigger_heal =
{
if (check_bmodel_keys()) return; // Check for bmodel errors
self.classtype = CT_TRIGHEAL;
InitTrigger ();
// Setup default healing sound
if (self.sounds == 2) self.noise = SOUND_HEAL25;
else if (self.sounds == 3) self.noise = SOUND_HEAL100;
else if (self.sounds == 4) self.noise = SOUND_RESPAWN;
else if (self.sounds == 5 && self.noise == "") self.noise = SOUND_HEAL15;
else self.noise = SOUND_HEAL15;
precache_sound(self.noise);
// Setup bubble model/counter/volume
if (self.spawnflags & TRIG_SPAWNBUBBLES) trigger_setup_bubbles();
if (!self.wait) self.wait = 1; // Default trigger time
if (self.healamount < 1) self.healamount = 10; // Quantity to heal each touch trigger
if (!self.max_health) self.max_health = 50; // Default max healing
// Cannot have healamount large than max, need to cap healamount
if (self.max_health > 0 && self.max_health < self.healamount)
self.healamount = self.max_health;
if (self.max_health < 0) self.health = 100; // max < 0 = Infinite healing
else self.health = self.max_health; // Reset total ready
if (!self.message) self.message = "You feel the effects of the healing pool";
if (!self.message2) self.message2 = "The Healing Pool has expired!";
// Save any trigger names for reset events
if (self.target != "" ) self.noise1 = self.target;
if (self.target2 != "" ) self.noise2 = self.target2;
// Setup Entity State functionality
if (self.targetname != "") self.use = entity_state_use;
self.estate_on = trigger_heal_on;
self.estate_off = trigger_bmodel_off;
self.estate_disable = trigger_bmodel_disable;
self.estate_reset = trigger_heal_reset;
self.touch = trigger_heal_touch;
// Switch on OR off?
if (self.spawnflags & ENT_STARTOFF) self.estate_off();
else self.estate_on();
};
/*======================================================================
/*QUAKED trigger_touchsound (.5 .5 .5) ? x x WORLDGEO DRAIN x x STARTOFF x Not_Easy Not_Normal Not_Hard Not_DM
Plays sounds when touched by the player
------- KEYS --------
targetname : trigger entity (works with entity state system)
sounds : 1=Water (def) 2=Slime 3=Lava 4=silent 5=custom
noise : Custom trigger touch sound
noise1 : Custom trigger exit sound
noise2 : Custom draining sound
speed : Time (def=1.5s) to drain liquid
yaw_speed : Vertical drain speed (def=0.05)
water_alpha: Alpha value for liquid (override worldspawn)
-------- SPAWNFLAGS --------
WORLDGEO : Will draw bmodel (not just a trigger)
DRAIN : Drain effect when trigger_disabled
STARTOFF : Requires trigger to activate
------- NOTES --------
Plays sounds when touched by the player
======================================================================*/
void() trigger_tsound_touch =
{
if (self.estate & ESTATE_BLOCK) return; // Function off/disabled
if (self.attack_finished > time) return; // Touch blocked (temporary)
if ( !(other.flags & FL_CLIENT) ) return; // Only works with clients
if (other.health < 1 ) return; // other is dead
if (other.touchedliquid < time)
sound (other, CHAN_BODY, self.noise, 1, ATTN_NORM);
other.touchedliquid = time + 0.1;
other.touchedsound = self.noise1;
self.attack_finished = time + 0.05;
};
//----------------------------------------------------------------------
void() trigger_tsound_on =
{
// No longer need this spawnflag, remove it
self.spawnflags = self.spawnflags - (self.spawnflags & ENT_STARTOFF);
self.estate = ESTATE_ON;
self.movetype = MOVETYPE_NONE;
self.solid = SOLID_TRIGGER;
self.origin = self.oldorigin;
self.alpha = self.water_alpha;
if (self.spawnflags & TRIG_TSOUNDWGEO) setmodel (self, self.mdl);
};
//----------------------------------------------------------------------
void() trigger_tsound_fade =
{
// Take draining time and divide by time passed
self.lip = self.speed - ((time - self.ltime) / self.speed);
// Start at current alpha value and move towards 0
self.alpha = self.lip * (self.water_alpha / self.speed);
// Slowly sink into the ground as alpha fading
self.origin_z = self.origin_z - (self.speed * self.yaw_speed);
// Exit condition
if (self.alpha <= 0) {
self.alpha = 0.01;
self.modelindex = 0; // Make sure no model
self.model = "";
self.estate = ESTATE_OFF;
return;
}
// Keep on loop (using minimum QS time segment)
self.nextthink = time + FADEMODEL_TIME;
};
//----------------------------------------------------------------------
void() trigger_tsound_drain =
{ sound(self, CHAN_AUTO, self.owner.noise2, 1, ATTN_NORM); };
//----------------------------------------------------------------------
void() trigger_tsound_disable =
{
if (self.estate & ESTATE_BLOCK) return;
self.solid = SOLID_NOT;
self.estate = ESTATE_DISABLE;
if (self.spawnflags & TRIG_TSOUNDDRAIN) {
// Allow for bmodel timer to alpha correctly
self.ltime = time;
// Gradually fade and lower bmodel
self.think = trigger_tsound_fade;
self.nextthink = time + FADEMODEL_TIME;
// Start playing draining sound
self.sound_emitter.think = trigger_tsound_drain;
self.sound_emitter.nextthink = time + self.super_time;
}
};
//----------------------------------------------------------------------
void() trigger_tsound_syncalpha =
{
// Allow for trigger to override global water alpha
if (self.water_alpha > 0) self.alpha = self.water_alpha;
else if (liquid_alpha > 0) self.alpha = liquid_alpha;
else self.alpha = 1;
// Save this value for later
self.water_alpha = self.alpha;
if (!(self.spawnflags & ENT_STARTOFF)) self.estate_on();
};
//----------------------------------------------------------------------
void() trigger_touchsound =
{
if (check_bmodel_keys()) return; // Check for bmodel errors
self.classtype = CT_TRIGTSOUND;
self.mdl = self.model;
InitTrigger ();
if (!self.speed) self.speed = 1.5;
if (!self.yaw_speed) self.yaw_speed = 0.05;
if (self.super_time < 0.1) self.super_time = 0.1;
self.attack_finished = 0;
self.oldorigin = self.origin;
// Default sounds (water)
if (self.sounds < 1) self.sounds = 1;
// Precache all sounds
if (self.sounds == 1) {
if (self.noise == "") self.noise = "player/inh2o.wav";
if (self.noise1 == "") self.noise1 = "misc/outwater.wav";
}
else if (self.sounds == 2) {
if (self.noise == "") self.noise = "player/slimbrn2.wav";
if (self.noise1 == "") self.noise1 = "misc/outwater.wav";
}
else if (self.sounds == 3) {
if (self.noise == "") self.noise = "player/inlava.wav";
if (self.noise1 == "") self.noise1 = "misc/outwater.wav";
}
// make sure there is always a sound (empty) defined
if (self.noise == "") self.noise = SOUND_EMPTY;
if (self.noise1 == "") self.noise1 = SOUND_EMPTY;
precache_sound(self.noise);
precache_sound(self.noise1);
if (self.spawnflags & TRIG_TSOUNDDRAIN) {
if (self.noise2 == "") self.noise2 = "ambience/liquid_drain.wav";
precache_sound(self.noise2);
// Create an entity to play drain sound
// bmodel origins are at 0,0,0 so never play properly
self.sound_emitter = spawn();
self.sound_emitter.owner = self;
self.sound_emitter.origin = bmodel_origin(self) + '0 0 32';
self.sound_emitter.solid = SOLID_NOT;
self.sound_emitter.movetype = MOVETYPE_NONE;
setmodel(self.sound_emitter, MODEL_EMPTY);
setorigin(self.sound_emitter, self.sound_emitter.origin);
}
// Setup Entity State functionality
if (self.targetname != "") self.use = entity_state_use;
self.estate_on = trigger_tsound_on;
self.estate_off = trigger_bmodel_off;
self.estate_disable = trigger_tsound_disable;
self.touch = trigger_tsound_touch;
// Switch off?
if (self.spawnflags & ENT_STARTOFF) self.estate_off();
// Sync the water alpha console variable
self.think = trigger_tsound_syncalpha;
self.nextthink = time + 0.1 + random();
};
/*======================================================================
/*QUAKED trigger_void (.5 .5 .5) ? NO_CLIENT NO_MONSTER NO_AMMO NO_EGG NO_TEMP NO_ITEM STARTOFF x Not_Easy Not_Normal Not_Hard Not_DM
Garbage collector for bottom of skyboxes
------- KEYS --------
targetname : trigger entity (works with entity state system)
-------- SPAWNFLAGS --------
NO_CLIENT : Ignore clients (anything flagged as a client)
NO_MONSTER : Ignore monsters (anything flagged as a monster)
NO_AMMO : Ignore ammo types (all ammo projectile types)
NO_EGG : Ignore minion eggs (affects shalrath, wraiths)
NO_TEMP : Ignore temporary ents (gibs, sparks, smoke)
NO_ITEM : Ignore items (weapons,armor,keys,runes,powerups)
STARTOFF : Requires trigger to activate
------- NOTES --------
Garbage collector for bottom of skyboxes
======================================================================*/
void() trigger_void_touch =
{
// Exception, entity state and particles!
if (self.estate & ESTATE_BLOCK) return;
if (other.classtype == CT_PARTICLE) return;
// Check for any dead monster bodies (no exceptions)
if (trigger_check_body(other,DEAD_REMOVE)) return;
// Process spawnflag exceptions
if (self.spawnflags & TRIG_VOIDNOCLIENT && other.flags & FL_CLIENT) return;
if (self.spawnflags & TRIG_VOIDNOMONSTER && other.flags & FL_MONSTER) return;
if (self.spawnflags & TRIG_VOIDNOAMMO) {
if (other.classgroup == CG_PROJALL) return;
else if (other.classgroup == CG_PROJSHELLS) return;
else if (other.classgroup == CG_PROJNAILS) return;
else if (other.classgroup == CG_PROJROCKETS) return;
else if (other.classgroup == CG_PROJCELLS) return;
}
if (self.spawnflags & TRIG_VOIDNOGG && other.classgroup == CG_MINIONEGG) return;
if (self.spawnflags & TRIG_VOIDNOTEMP && other.classgroup == CG_TEMPENT) return;
if (self.spawnflags & TRIG_VOIDNOITEM) {
if (other.classgroup == CG_WEAPON) return;
else if (other.classgroup == CG_AMMOITEM) return;
else if (other.classgroup == CG_ARMOR) return;
else if (other.classgroup == CG_KEY) return;
else if (other.classgroup == CG_RUNE) return;
else if (other.classgroup == CG_ARTIFACT) return;
}
// flag touching entity, so other functions will ignore it
other.touchedvoid = TRUE;
// Let monsters and clients die through their own functions
if (other.flags & FL_CLIENT || other.flags & FL_MONSTER)
T_Damage (other, self, self, other.health+8, NOARMOR);
else {
// Remove all ammo projectiles on contact
// Hide other items types (may have particles active)
if (other.classgroup == CG_PROJALL) entity_remove(other,0.1);
else if (other.classgroup == CG_PROJSHELLS) entity_remove(other,0.1);
else if (other.classgroup == CG_PROJNAILS) entity_remove(other,0.1);
else if (other.classgroup == CG_PROJROCKETS) entity_remove(other,0.1);
else if (other.classgroup == CG_PROJCELLS) entity_remove(other,0.1);
else if (other.classgroup == CG_MINIONEGG) entity_remove(other,0.1);
else if (other.classgroup == CG_TEMPENT) entity_remove(other,0.1);
else if (other.classgroup == CG_WEAPON) entity_hide(other);
else if (other.classgroup == CG_AMMOITEM) {
entity_hide(other);
// Hide any shell/nail lids
if (other.attachment) entity_hide(other.attachment);
}
else if (other.classgroup == CG_ARMOR) entity_hide(other);
else if (other.classgroup == CG_KEY) entity_hide(other);
else if (other.classgroup == CG_RUNE) entity_hide(other);
else if (other.classgroup == CG_ARTIFACT) entity_hide(other);
else if (other.classgroup == CG_MISCENT) entity_hide(other);
else if (other.classgroup == CG_BREAKABLE) entity_hide(other);
}
};
//----------------------------------------------------------------------
void() trigger_void =
{
if (check_bmodel_keys()) return; // Check for bmodel errors
self.classtype = CT_TRIGVOID;
InitTrigger ();
setsize (self, self.bbmins, self.bbmaxs);
// Setup Entity State functionality
// This does not work via typical trigger bmodel paths
// This trigger is special, needs to work straight away
// There is only a toggle/on/off state function
if (self.targetname != "") self.use = entity_state_use;
self.touch = trigger_void_touch;
if (self.spawnflags & ENT_STARTOFF) self.estate = ESTATE_OFF;
else self.estate = ESTATE_ON;
};