874 lines
31 KiB
Plaintext
874 lines
31 KiB
Plaintext
/*======================================================================
|
|
TARGET FUNCTIONS
|
|
|
|
Trigger_relay and trigger_count are not triggers, they don't have
|
|
any touch functionality and don't even set/use any of the activator
|
|
system used by most trigger/func entities.
|
|
|
|
These entities are essentially targets that reply on input from other
|
|
targets (use) and then use their targets (conditionally). They should
|
|
be re-classifed as target_relay and target_count, but it is too late now
|
|
for such a change, the opportunity has long gone!
|
|
|
|
======================================================================*/
|
|
float TRIG_COUNTNOMESSAGE = 1; // Prevents all count messages
|
|
float TRIG_COUNTEXACTNO = 16; // Display exact count number
|
|
float TRIG_COUNTSTARTDIS = 32; // Count starts in disabled state
|
|
|
|
float TRIG_RANDOMTARGET = 2; // Randomly trigger target/target2
|
|
float TRIG_RELAYSTARTDIS = 32; // trigger_delay starts disabled
|
|
float TRIG_EXPLODENOEFF = 2; // No old particle effect
|
|
float TRIG_EXPLODEDUST = 4; // Exploding projectile dust
|
|
float TRIG_MONKILLDFUNC = 16; // kill monsters via death function
|
|
|
|
float TRIG_ENGFITZ = 1; // Fitz engine
|
|
float TRIG_ENGDP = 2; // DP engine
|
|
float TRIG_ENGFTE = 4; // FTE/QSS engine
|
|
float TRIG_ENGRAIN = 16; // Check for rain effects
|
|
float TRIG_ENGSNOW = 32; // Check for snow effects
|
|
|
|
/*======================================================================
|
|
/*QUAKED trigger_relay (0.5 0 0.5) (-8 -8 -8) (8 8 8) x RANDOM x x x STARTDIS x x Not_Easy Not_Normal Not_Hard
|
|
Triggers target(s) with custom sounds and messages
|
|
-------- KEYS --------
|
|
targetname : trigger entity
|
|
target : targets to trigger when relay is activated
|
|
target2 : secondary targets to trigger when activated
|
|
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
|
|
wait : -1 = will only fire targets once
|
|
delay : delay before firing (after being triggered)
|
|
cnt : random amount of time to add to delay
|
|
waitmin : % random chance between target/target2
|
|
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 --------
|
|
RANDOM : Will randomly select between target/target2
|
|
STARTDIS : Will start disabled, will req trigger_entitystate_on to enable
|
|
-------- NOTES --------
|
|
This fixed size trigger cannot be touched, it can only be fired by other events.
|
|
Can contain killtargets, targets, delays, and messages.
|
|
|
|
======================================================================*/
|
|
void() trigger_relay_use =
|
|
{
|
|
if (self.estate & ESTATE_BLOCK) return;
|
|
if (self.attack_finished > time) return;
|
|
|
|
//----------------------------------------------------------------------
|
|
// The Shotgun and Axe upgrades can make maps too easy, allow for
|
|
// triggers to not fire if the key is TRUE and inventory empty
|
|
//----------------------------------------------------------------------
|
|
if ( other.flags & FL_CLIENT ) {
|
|
if (self.upgrade_axe && !(other.moditems & IT_UPGRADE_AXE)) return;
|
|
if (self.upgrade_ssg && !(other.moditems & IT_UPGRADE_SSG) ) return;
|
|
if (self.upgrade_lg && !(other.moditems & IT_UPGRADE_LG) ) return;
|
|
}
|
|
|
|
// Setup to trigger once?
|
|
if (self.wait < 0) self.attack_finished = LARGE_TIMER;
|
|
if (self.noise != "") sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM);
|
|
|
|
// Randomly pick between target/target2
|
|
// SUB_UseTargets will fire both target strings if found
|
|
// the random choices need to be stored elsewhere (noise3/4)
|
|
if (self.spawnflags & TRIG_RANDOMTARGET) {
|
|
if (random() < self.waitmin) self.target = self.noise3;
|
|
else self.target = self.noise4;
|
|
}
|
|
|
|
// One thing to note about trigger_relay is that it does not change the
|
|
// activator global variable to the entity that used this trigger last
|
|
// This is handy for client test triggers (like trigger_secret)
|
|
SUB_UseTargets();
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() trigger_relay =
|
|
{
|
|
trigger_bmodel_sounds(); // Precache any sounds
|
|
self.classtype = CT_TRIGRELAY;
|
|
if (self.delay <= 0) self.delay = 0;
|
|
if (self.cnt > 0) self.delay = self.delay + random()*self.cnt;
|
|
|
|
// Setup random trigger selection strings and random %
|
|
// No checks are done on the target/target so that they can
|
|
// be blank and the mapper can randomly select an empty trigger
|
|
// only check = The random % has got to exist between 0-1
|
|
if (self.spawnflags & TRIG_RANDOMTARGET) {
|
|
if (self.waitmin <=0 || self.waitmin >= 1) self.waitmin = 0.5;
|
|
self.noise3 = self.noise4 = ""; // Initialize strings
|
|
self.noise3 = self.target; // Copy over strings
|
|
self.noise4 = self.target2;
|
|
self.target = self.target2 = ""; // Remove originals
|
|
}
|
|
|
|
// 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_relay_use;
|
|
// Setup intial entity state (can start disabled)
|
|
if (self.spawnflags & TRIG_RELAYSTARTDIS) self.estate = ESTATE_DISABLE;
|
|
else self.estate = ESTATE_ON;
|
|
};
|
|
|
|
/*======================================================================
|
|
/*QUAKED trigger_counter (0.5 0 0.1) (-8 -8 -8) (8 8 8) NOMESSAGE x x x EXACTNO STARTDIS STARTOFF x Not_Easy Not_Normal Not_Hard Not_DM
|
|
A counter which triggers target(s) when complete
|
|
-------- KEYS --------
|
|
targetname : trigger entity (works with entity state system)
|
|
target : trigger target(s) when complete
|
|
|
|
message : message to display when complete (ignores NOMESSAGE spawnflag)
|
|
startmsg : message to display before counting begins (use wait for pause time)
|
|
count : number of triggers needed to fire own target, (def=2)
|
|
delay : time delay to fire final trigger event
|
|
sounds : 0=silent,1=Secret,2=talk,3=switch,5=custom,6=secret2
|
|
noise : custom sound to play when complete
|
|
wait : time to pause before starting to count (def=2s)
|
|
-------- SPAWNFLAGS --------
|
|
NOMESSAGE : disables count display
|
|
EXACTNO : display exact number when counting down
|
|
STARTDIS : Starts disabled and waits for trigger
|
|
STARTOFF : Requires trigger to activate
|
|
-------- NOTES --------
|
|
A counter which triggers target(s) when complete
|
|
|
|
======================================================================*/
|
|
void() trigger_counter_reset =
|
|
{
|
|
// Reset counter to initial (spawning) value
|
|
self.count = self.height;
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() trigger_counter_use =
|
|
{
|
|
if (self.estate & ESTATE_BLOCK) return;
|
|
|
|
// Check for any pre-count messages
|
|
if (self.startmsg != "") {
|
|
centerprint (activator, self.startmsg);
|
|
self.startmsg = "";
|
|
self.nextthink = time + self.wait;
|
|
self.think = self.estate_use;
|
|
return;
|
|
}
|
|
|
|
self.count = self.count - 1;
|
|
if (self.count < 0) return;
|
|
|
|
// Count down messages for trigger
|
|
if (self.count > 0) {
|
|
if (activator.flags & FL_CLIENT && !(self.spawnflags & TRIG_COUNTNOMESSAGE) ) {
|
|
if (self.count >= 4) {
|
|
if (self.spawnflags & TRIG_COUNTEXACTNO)
|
|
centerprint3 (activator, "Only ", ftos(self.count), " more to go...");
|
|
else centerprint (activator, "There are more to go...");
|
|
}
|
|
else if (self.count == 3) centerprint (activator, "Only 3 more to go...");
|
|
else if (self.count == 2) centerprint (activator, "Only 2 more to go...");
|
|
else centerprint (activator, "Only 1 more to go...");
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Reach zero on counter, time to trigger counter target
|
|
if (activator.flags & FL_CLIENT) {
|
|
if (self.message2 != "") centerprint(activator, self.message2);
|
|
else if ( !(self.spawnflags & TRIG_COUNTNOMESSAGE) )
|
|
centerprint(activator, "Sequence completed!");
|
|
}
|
|
|
|
// If sound defined, play sound
|
|
if (self.noise != "") sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM);
|
|
|
|
// This is a big problem with the trigger_counter entity, it does not update
|
|
// the activator global variable and does not wake up enemies correctly.
|
|
// If a trigger_count is targetting monsters then they will not get angry
|
|
// at the player, the count has to target a _once or _multi trigger instead.
|
|
self.enemy = activator;
|
|
SUB_UseTargets();
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() trigger_counter_delay =
|
|
{
|
|
if (self.estate & ESTATE_BLOCK) return;
|
|
|
|
// Remove the trigger delay function
|
|
if (self.spawnflags & ENT_STARTOFF)
|
|
self.spawnflags = self.spawnflags - ENT_STARTOFF;
|
|
|
|
// Re-route use function to actual counter
|
|
self.estate_use = trigger_counter_use;
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() trigger_counter =
|
|
{
|
|
trigger_bmodel_sounds(); // Precache any sounds
|
|
if (!self.count) self.count = 2; // default count
|
|
self.height = self.count; // Save for later, reset
|
|
if (self.wait <= 0) self.wait = 2; // Default pre-message time
|
|
self.classtype = CT_TRIGCOUNT;
|
|
|
|
// 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_reset = trigger_counter_reset;
|
|
if (!self.estate) self.estate = ESTATE_ON;
|
|
// Check for disable state (need trigger_entityon to work again)
|
|
if (self.spawnflags & TRIG_COUNTSTARTDIS) self.estate = ESTATE_DISABLE;
|
|
|
|
// The delay function is not switched off, its activate to use
|
|
if (self.spawnflags & ENT_STARTOFF) self.estate_use = trigger_counter_delay;
|
|
else self.estate_use = trigger_counter_use;
|
|
};
|
|
|
|
/*======================================================================
|
|
/*QUAKED trigger_engine (0 0 1) (-8 -8 -16) (8 8 16) FITZ DP FTE x RAIN SNOW x x
|
|
Triggers target(s) when certain engine active
|
|
-------- KEYS --------
|
|
targetname : trigger entity
|
|
target : targets to trigger when relay is activated
|
|
wait : -1 = will only fire targets once
|
|
delay : delay before firing (after being triggered)
|
|
cnt : random amount of time to add to delay
|
|
-------- SPAWNFLAGS --------
|
|
FITZ : Only trigger for Fitz engines (default type)
|
|
DP : Only trigger for DP engine
|
|
FTE : Only trigger for FTE/QSS engines
|
|
RAIN : Check for rain effect being active
|
|
SNOW : Check for snow effect being active
|
|
-------- NOTES --------
|
|
Triggers target(s) when certain engine active
|
|
|
|
======================================================================*/
|
|
void() trigger_engine_fire =
|
|
{
|
|
// Parameter to check at of tests
|
|
self.aflag = FALSE;
|
|
|
|
// Check each engine type for conditions
|
|
// This trigger will not reset the activator
|
|
// Should be done with one of the targets instead
|
|
if (self.spawnflags & TRIG_ENGFITZ && engine == ENG_FITZ) self.aflag = TRUE;
|
|
else if (self.spawnflags & TRIG_ENGDP && engine == ENG_DPEXT) self.aflag = TRUE;
|
|
// Extra check for FTE/QSS (they support DP particles)
|
|
else if (self.spawnflags & TRIG_ENGFTE && engine == ENG_DPEXT) {
|
|
if (checkextension("FTE_SV_POINTPARTICLES")) self.aflag = TRUE;
|
|
}
|
|
|
|
if (self.aflag == TRUE) {
|
|
// Check for engine weather effects
|
|
if (self.spawnflags & TRIG_ENGRAIN && !ext_dprain) self.aflag = FALSE;
|
|
if (self.spawnflags & TRIG_ENGSNOW && !ext_dpsnow) self.aflag = FALSE;
|
|
// Check for weather being disabled
|
|
if (self.spawnflags & (TRIG_ENGRAIN | TRIG_ENGSNOW)) {
|
|
if (query_configflag(SVR_WEATHER)) self.aflag = FALSE;
|
|
}
|
|
|
|
// If engine and/or weather active, fire targets!
|
|
if (self.aflag == TRUE) SUB_UseTargets();
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() trigger_engine_use =
|
|
{
|
|
if (self.estate & ESTATE_BLOCK) return;
|
|
if (self.attack_finished > time) return;
|
|
|
|
// Setup to trigger once?
|
|
if (self.wait < 0) self.attack_finished = LARGE_TIMER;
|
|
|
|
// Check for any trigger delay?
|
|
if (self.delay > 0) {
|
|
self.think = trigger_engine_fire;
|
|
self.nextthink = time + self.delay;
|
|
}
|
|
else trigger_engine_fire();
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() trigger_engine =
|
|
{
|
|
self.classtype = CR_TRIGENG;
|
|
|
|
// 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_engine_use;
|
|
self.estate = ESTATE_ON;
|
|
};
|
|
|
|
/*======================================================================
|
|
/*QUAKED trigger_clientmsg (0.5 0.5 0) (-8 -8 -16) (8 8 16) x
|
|
Centerprints a message to all clients
|
|
-------- KEYS --------
|
|
targetname : trigger entity
|
|
message : Text to center print
|
|
sounds : 1=Secret,2=talk,3=switch,4=silent(def),5=custom,6=secret2
|
|
noise : custom sound to play when triggered
|
|
-------- SPAWNFLAGS --------
|
|
-------- NOTES --------
|
|
Centerprints a message to all clients
|
|
|
|
======================================================================*/
|
|
void() trigger_clientmsg_use =
|
|
{
|
|
if (self.estate & ESTATE_BLOCK) return;
|
|
|
|
// Write message to all clients
|
|
msg_entity = self;
|
|
WriteByte (MSG_ALL, SVC_CENTERPRINT);
|
|
WriteString (MSG_ALL, self.message);
|
|
|
|
// If any sounds defined play them at entity source
|
|
if (self.noise != "") sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM);
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() trigger_clientmsg =
|
|
{
|
|
// Default = the sound of silence!
|
|
if (self.sounds == 0) self.sounds = 4;
|
|
trigger_bmodel_sounds();
|
|
|
|
// Setup default message
|
|
if (self.message == "") self.message = "Default Trigger Message!\n";
|
|
self.classtype = CT_TRIGCLMSG;
|
|
|
|
// 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_clientmsg_use;
|
|
self.estate = ESTATE_ON;
|
|
};
|
|
|
|
/*======================================================================
|
|
/*QUAKED trigger_cdtrack (0.8 0.5 0) (-8 -8 -16) (8 8 16) x
|
|
Change CD track for all clients
|
|
-------- KEYS --------
|
|
targetname : trigger entity
|
|
count : CD track number (eg. 0-x)
|
|
-------- SPAWNFLAGS --------
|
|
-------- NOTES --------
|
|
Change CD track for all clients
|
|
|
|
======================================================================*/
|
|
void() trigger_cdtrack_change =
|
|
{
|
|
// Write CD track to all clients
|
|
msg_entity = self;
|
|
WriteByte (MSG_ALL, SVC_CDTRACK);
|
|
WriteByte (MSG_ALL, trig_cdtrack);
|
|
WriteByte (MSG_ALL, trig_cdtrack);
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() trigger_cdtrack_use =
|
|
{
|
|
// Check for entity state system block
|
|
if (self.estate & ESTATE_BLOCK) return;
|
|
|
|
// Make sure CD track change in savefile
|
|
trig_cdtrack = self.count;
|
|
|
|
// Write message to all clients
|
|
trigger_cdtrack_change();
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() trigger_cdtrack =
|
|
{
|
|
self.classtype = CT_TRIGCDTRACK;
|
|
// Make sure specified cd track is integer number
|
|
self.count = fabs(rint(self.count));
|
|
|
|
// 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_cdtrack_use;
|
|
self.estate = ESTATE_ON;
|
|
};
|
|
|
|
/*======================================================================
|
|
/*QUAKED trigger_skybox (0.9 0.9 0.9) (-8 -8 -16) (8 8 16) x
|
|
Load/Change Skybox for all clients
|
|
-------- KEYS --------
|
|
targetname : trigger entity
|
|
message : Skybox name (eg moonrise_)
|
|
-------- SPAWNFLAGS --------
|
|
-------- NOTES --------
|
|
Load/Change Skybox for all clients
|
|
|
|
======================================================================*/
|
|
void(entity targ) trigger_skybox_change =
|
|
{
|
|
// Write skybox change to all clients
|
|
stuffcmd(targ, "SKY ");
|
|
stuffcmd(targ, trig_skybox);
|
|
stuffcmd(targ, "\n");
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() trigger_skybox_use =
|
|
{
|
|
// Check for entity state system block
|
|
if (self.estate & ESTATE_BLOCK) return;
|
|
|
|
// Check for client trigger first
|
|
if (activator.classtype == CT_PLAYER) self.enemy = activator;
|
|
|
|
// Find a player for the stuff command
|
|
if (!self.enemy) {
|
|
self.enemy = find(world,targetname,"player");
|
|
if (self.enemy.classtype != CT_PLAYER) return;
|
|
}
|
|
|
|
// Write skybox name to all clients
|
|
trig_skybox = self.message;
|
|
trigger_skybox_change(self.enemy);
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() trigger_skybox =
|
|
{
|
|
self.classtype = CT_TRIGSKYBOX;
|
|
if (self.message == "") self.message = "Sky";
|
|
|
|
// 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_skybox_use;
|
|
self.estate = ESTATE_ON;
|
|
};
|
|
/*======================================================================
|
|
/*QUAKED trigger_itemrespawnupd (0.5 0 0.5) (-8 -8 -16) (8 8 16) x x x x RESPAWN x x x
|
|
Change the state of respawn flag on items
|
|
-------- KEYS --------
|
|
targetname : trigger entity
|
|
target : this points to the item to affect
|
|
-------- SPAWNFLAGS --------
|
|
RESPAWN : Value of respawn flag to copy to item
|
|
-------- NOTES --------
|
|
Change the state of respawn flag on items
|
|
Useful for switching off respawning items after arena fight is over
|
|
|
|
======================================================================*/
|
|
void() trigger_itemrespawnupd_use =
|
|
{
|
|
if (self.estate & ESTATE_BLOCK) return;
|
|
|
|
// Loop around target(s) and update respawn flag
|
|
self.enemy = find (world, targetname, self.target);
|
|
while(self.enemy) {
|
|
// only work with active items
|
|
if (self.enemy.flags & FL_ITEM) {
|
|
if (self.spawnflags & ITEM_RESPAWN) {
|
|
self.enemy.spawnflags = self.enemy.spawnflags | ITEM_RESPAWN;
|
|
}
|
|
else {
|
|
// Remove respawn flag (even if missing)
|
|
self.enemy.spawnflags = self.enemy.spawnflags - (self.enemy.spawnflags & ITEM_RESPAWN);
|
|
}
|
|
}
|
|
// Are there anymore targets left in the list?
|
|
self.enemy = find (self.enemy, targetname, self.target);
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() trigger_itemrespawnupd =
|
|
{
|
|
self.classtype = CT_TRIGITEMFLAG;
|
|
|
|
// 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_itemrespawnupd_use;
|
|
self.estate = ESTATE_ON;
|
|
};
|
|
|
|
/*======================================================================
|
|
QUAKED trigger_monstermovespeed (0.5 0 0.5) (-8 -8 -16) (8 8 16) x
|
|
Toggle the monster move speed state
|
|
-------- KEYS --------
|
|
targetname : trigger entity (works with entity state system)
|
|
target : this points to the monster(s) to affect
|
|
state : -1 = No movement, 0 = Toggle, 1 = Free movement
|
|
wait : -1 = trigger once only
|
|
-------- SPAWNFLAGS --------
|
|
-------- NOTES --------
|
|
Toggle the monster move speed state
|
|
|
|
======================================================================*/
|
|
void() trigger_monstermovespeed_use =
|
|
{
|
|
if (self.estate & ESTATE_BLOCK) return;
|
|
|
|
// Loop around target(s) and update move speed
|
|
self.enemy = find (world, targetname, self.target);
|
|
while(self.enemy) {
|
|
// only work with living monsters (ignore rest)
|
|
if (self.enemy.flags & FL_MONSTER && self.enemy.health > 0) {
|
|
// Is the state a toggle or exact update?
|
|
if (self.state == 0) {
|
|
if (self.enemy.movespeed < 1) self.enemy.movespeed = 1;
|
|
else self.enemy.movespeed = -1;
|
|
}
|
|
// Exact value update
|
|
else self.enemy.movespeed = self.state;
|
|
}
|
|
// Are there anymore targets left in the list?
|
|
self.enemy = find (self.enemy, targetname, self.target);
|
|
}
|
|
|
|
// Trigger once functionality?
|
|
if (self.wait < 0) self.estate = ESTATE_OFF;
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() trigger_monstermovespeed =
|
|
{
|
|
self.classtype = CT_TRIGMONMOVE;
|
|
if (self.target == "") {
|
|
dprint("\b[MON_MOVESPEED]\b No target set, removing\n");
|
|
remove(self);
|
|
}
|
|
|
|
// make sure state has the correct values
|
|
if (self.state < -1 || self.state > 1) self.state = 0;
|
|
|
|
// 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_monstermovespeed_use;
|
|
self.estate = ESTATE_ON;
|
|
};
|
|
|
|
/*======================================================================
|
|
/*QUAKED trigger_monsterkill (0.5 0 0.5) (-8 -8 -16) (8 8 16) x x x x DEATH x x x
|
|
Remove monster(s) from the map
|
|
-------- KEYS --------
|
|
targetname : Name of this trigger entity
|
|
target : Name of monster(s) to remove/stop
|
|
-------- SPAWNFLAGS --------
|
|
DEATH : kill monsters via death function
|
|
-------- NOTES --------
|
|
Remove monster(s) from the map
|
|
|
|
======================================================================*/
|
|
void() trigger_monsterkill_use =
|
|
{
|
|
if (self.estate & ESTATE_BLOCK) return;
|
|
// This only works once!
|
|
self.use = SUB_Null;
|
|
|
|
// Loop around target(s) and remove the game
|
|
self.enemy = find (world, targetname, self.target);
|
|
while(self.enemy) {
|
|
// There are always exceptions to the use of the monster flag
|
|
// Point Hell knights don't use monster flag to prevent damage!
|
|
if (self.enemy.spawnflags & MON_POINT_KNIGHT && self.enemy.health > 0)
|
|
self.enemy.flags = self.enemy.flags | FL_MONSTER;
|
|
|
|
// only work with monsters, can't be dead, dying or negative health!
|
|
if (self.enemy.flags & FL_MONSTER && self.enemy.health > 0 && !self.enemy.deadflag) {
|
|
// Check if death function is required for monster
|
|
// Also double check they have a death function setup!
|
|
if (self.spawnflags & TRIG_MONKILLDFUNC && self.enemy.th_die) {
|
|
self.enemy.takedamage = DAMAGE_YES;
|
|
// Always do enough damage to kill the monster
|
|
// Can be a problem for ammo resistant
|
|
T_Damage (self.enemy, self, self, self.enemy.health+1, NOARMOR);
|
|
}
|
|
else {
|
|
self.enemy.deadflag = DEAD_DEAD;
|
|
self.enemy.enemy = world;
|
|
self.enemy.health = self.enemy.gibhealth;
|
|
|
|
// If a monster marked with no monster count dies
|
|
// update HUD totals to reflect death properly
|
|
if (self.enemy.nomonstercount) {
|
|
total_monsters = total_monsters + 1;
|
|
update_hud_totals(HUD_MONSTERS);
|
|
}
|
|
// Update global monster death totals
|
|
killed_monsters = killed_monsters + 1;
|
|
WriteByte (MSG_ALL, SVC_KILLEDMONSTER);
|
|
|
|
// Stop all animation think functions
|
|
self.enemy.think = SUB_Null;
|
|
self.enemy.nextthink = time + LARGE_TIMER;
|
|
// Finally stop and hide all models
|
|
// Should not remove monster, it might have dependancies
|
|
entity_hide(self.enemy);
|
|
entity_remove(self.enemy.attachment, 0.1);
|
|
entity_remove(self.enemy.attachment2, 0.1);
|
|
entity_remove(self.enemy.attachment3, 0.1);
|
|
}
|
|
}
|
|
// Are there anymore targets left in the list?
|
|
self.enemy = find (self.enemy, targetname, self.target);
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() trigger_monsterkill =
|
|
{
|
|
self.classtype = CT_TRIGMONKILL;
|
|
|
|
// 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_monsterkill_use;
|
|
self.estate = ESTATE_ON;
|
|
};
|
|
|
|
/*======================================================================
|
|
/*QUAKED trigger_monsterattack (0.5 0 0.5) (-8 -8 -16) (8 8 16) x
|
|
Force a monster to attack a certain target
|
|
-------- KEYS --------
|
|
targetname : Name of this trigger entity
|
|
target : Name of monster to affect
|
|
target2 : Name of entity to attack
|
|
-------- SPAWNFLAGS --------
|
|
-------- NOTES --------
|
|
Force a monster to attack a certain target
|
|
|
|
======================================================================*/
|
|
void() trigger_monsterattack_use =
|
|
{
|
|
if (self.estate & ESTATE_BLOCK) return;
|
|
|
|
// Find target monster first and see if alive?
|
|
self.enemy = find (world, targetname, self.target);
|
|
// only work with monsters, can't be dead, dying or negative health!
|
|
if (self.enemy.flags & FL_MONSTER && self.enemy.health > 0) {
|
|
self.oldenemy = find(world, targetname, self.target2);
|
|
// Check if attacking target is alive and can be damaged?
|
|
if (self.oldenemy.health > 0 && self.oldenemy.takedamage != DAMAGE_NO) {
|
|
// Setup monster to attack new target (use damage function)
|
|
T_Damage (self.enemy, self.oldenemy, self.oldenemy, 1, NOARMOR);
|
|
}
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() trigger_monsterattack =
|
|
{
|
|
self.classtype = CT_TRIGMONATT;
|
|
|
|
// 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_monsterattack_use;
|
|
self.estate = ESTATE_ON;
|
|
};
|
|
|
|
//======================================================================
|
|
// General purpose sprite+particle explosion
|
|
// Entity is fired once (default)
|
|
//======================================================================
|
|
/*QUAKED trigger_explode (.8 .5 .5) (-4 -4 -4) (4 4 4) x EXPLODENOEFF DUST
|
|
Triggered entity producing damage + sprite explosion
|
|
-------- KEYS --------
|
|
targetname : trigger entity (works with entity state system)
|
|
dmg : explosive radius damage (def=40, -1=no damage)
|
|
delay : time delay to explosion (def=0s)
|
|
noise : string name for explosion sound (def=weapons/r_exp3b.wav)
|
|
wait : re-trigger explosions (def=-1 trigger once)
|
|
style : 0=Explosion, 1=Plasma, 2=Poison, 3=Electric, 4=Burst (Smoke/Flame/Poison)
|
|
height : 0=Small, 1=Medium, 2=Large, -1=Random
|
|
count : Random amount of dust particles to spawn (1-x)
|
|
pos1 : Base dust velocity (X=Forward, Y=Right, Z=Up)
|
|
pos2 : Random dust velocity (X=Forward, Y=Right, Z=Up)
|
|
-------- SPAWNFLAGS --------
|
|
EXPLODENOEFF : no old school particle explosion
|
|
DUST : Dust particle explosion (use angle for direction)
|
|
-------- NOTES --------
|
|
Triggered entity producing damage + sprite explosion
|
|
|
|
=============================================================================*/
|
|
void() trig_explode_fire =
|
|
{
|
|
// Dust particles are empty models with rocket smoke trails
|
|
if (self.spawnflags & TRIG_EXPLODEDUST) {
|
|
// Setup direction of projectile
|
|
makevectors(self.angles);
|
|
// Always spawn at least 1 dust particle
|
|
self.lip = 1 + rint(random() * self.count);
|
|
while (self.lip > 0) {
|
|
// Keep spawning temporary entities
|
|
newmis = spawn();
|
|
newmis.classgroup = CG_TEMPENT;
|
|
newmis.movetype = MOVETYPE_TOSS;
|
|
newmis.solid = SOLID_NOT;
|
|
setmodel(newmis, MODEL_PROJ_SMOKE);
|
|
|
|
// Randomize the origin of the particles
|
|
self.oldorigin = self.origin + (v_right*(crandom()*self.pos1_y));
|
|
setorigin(newmis, self.oldorigin);
|
|
setsize (newmis, VEC_ORIGIN, VEC_ORIGIN);
|
|
|
|
// Use pos1+pos2 vector (calculated from angles)
|
|
self.pos3_x = self.pos1_x + ( random() * self.pos2_x );
|
|
self.pos3_y = crandom() * (self.pos1_y + ( random() * self.pos2_y ));
|
|
self.pos3_z = self.pos1_z + ( random() * self.pos2_z );
|
|
newmis.velocity = (v_forward*self.pos3_x) + (v_right*self.pos3_y) + (v_up*self.pos3_z);
|
|
|
|
// Setup removal and keep counting
|
|
newmis.nextthink = time + 1 + random()*3;
|
|
newmis.think = SUB_Remove;
|
|
self.lip = self.lip - 1;
|
|
}
|
|
// No damage or sprites needed
|
|
return;
|
|
}
|
|
|
|
// create any explosive radius damage
|
|
if (self.dmg > 0) T_RadiusDamage (self, self, self.dmg, self, DAMAGEALL);
|
|
|
|
// Check for old particle effect
|
|
if ( !(self.spawnflags & TRIG_EXPLODENOEFF) ) {
|
|
// classic ID explosion
|
|
if (self.style == 0) {
|
|
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
|
|
WriteByte (MSG_BROADCAST, TE_EXPLOSION);
|
|
WriteCoord (MSG_BROADCAST, self.origin_x);
|
|
WriteCoord (MSG_BROADCAST, self.origin_y);
|
|
WriteCoord (MSG_BROADCAST, self.origin_z);
|
|
}
|
|
// New Rogue extension - Blue
|
|
else if (self.style == 1) {
|
|
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
|
|
WriteByte (MSG_BROADCAST, TE_EXPLOSION2);
|
|
WriteCoord (MSG_BROADCAST, self.origin_x);
|
|
WriteCoord (MSG_BROADCAST, self.origin_y);
|
|
WriteCoord (MSG_BROADCAST, self.origin_z);
|
|
WriteByte (MSG_BROADCAST, 39); // Blue 32 + 7
|
|
WriteByte (MSG_BROADCAST, 8); // Colour range
|
|
}
|
|
// New Rogue extension - Green
|
|
else if (self.style == 2) {
|
|
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
|
|
WriteByte (MSG_BROADCAST, TE_EXPLOSION2);
|
|
WriteCoord (MSG_BROADCAST, self.origin_x);
|
|
WriteCoord (MSG_BROADCAST, self.origin_y);
|
|
WriteCoord (MSG_BROADCAST, self.origin_z);
|
|
WriteByte (MSG_BROADCAST, 55); // Green 48 + 7
|
|
WriteByte (MSG_BROADCAST, 8); // Colour range
|
|
}
|
|
}
|
|
|
|
// Check for randomly sized explosion type
|
|
if (self.height == -1) self.lip = rint(0.5+ random() * 2.5);
|
|
else self.lip = rint(self.height);
|
|
// Work out explosion type offset
|
|
self.lip = self.lip + rint(self.style) * 10;
|
|
|
|
// Use global function (Fitz/DP aware)
|
|
SpawnExplosion(self.lip, self.origin, self.noise);
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() trig_explode_use =
|
|
{
|
|
if (self.estate & ESTATE_BLOCK) return;
|
|
if (self.attack_finished > time) return;
|
|
|
|
// Check for Trigger once setting
|
|
if (self.wait < 0) self.attack_finished = LARGE_TIMER;
|
|
|
|
// Check for explosion delay or straight away blow up
|
|
if (self.delay > 0) {
|
|
self.nextthink = time + self.delay;
|
|
self.think = trig_explode_fire;
|
|
}
|
|
else trig_explode_fire();
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() trigger_explode =
|
|
{
|
|
// Default settings
|
|
self.classtype = CT_TRIGEXPLODE;
|
|
if (self.dmg >= 0) self.dmg = DAMAGE_MONROCKET;
|
|
if (self.wait == 0) self.wait = -1;
|
|
if (self.height == 0 || self.height > 3) self.height = 1;
|
|
if (self.style < 0 || self.style > 4) self.style = 0;
|
|
self.attack_finished = 0;
|
|
|
|
if (self.noise == "") {
|
|
if (self.style == 3) self.noise = SOUND_PLASMA_HIT;
|
|
else if (self.style == 4) self.noise = SOUND_RESIST_ROCKET;
|
|
else self.noise = SOUND_REXP3; // Default
|
|
}
|
|
precache_sound (self.noise);
|
|
|
|
// Precache smoke particles and set default movement
|
|
if (self.spawnflags & TRIG_EXPLODEDUST) {
|
|
precache_model(MODEL_PROJ_SMOKE);
|
|
if (CheckZeroVector(self.angles)) self.angles_y = 360;
|
|
if (CheckZeroVector(self.pos1)) self.pos1 = '100 25 100';
|
|
if (CheckZeroVector(self.pos2)) self.pos2 = '100 25 100';
|
|
}
|
|
|
|
// Burst Sprites are NOT precached in world.qc
|
|
if (self.style == 4) {
|
|
// Check for random selection
|
|
if (self.height == -1) {
|
|
precache_model(SBURST_SMOKE);
|
|
precache_model(SBURST_FLAME);
|
|
precache_model(SBURST_POISON);
|
|
}
|
|
else if (self.height == 1) precache_model(SBURST_SMOKE);
|
|
else if (self.height == 2) precache_model(SBURST_FLAME);
|
|
else precache_model(SBURST_POISON);
|
|
}
|
|
|
|
// 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 = trig_explode_use;
|
|
self.estate = ESTATE_ON;
|
|
};
|