694 lines
24 KiB
Plaintext
694 lines
24 KiB
Plaintext
/*======================================================================
|
|
TRAP SHOOTERS
|
|
======================================================================*/
|
|
float TRAP_SNGSPIKE = 1; // Large SNG spikes (high damage)
|
|
float TRAP_LASER = 2; // Enforcer style lasers
|
|
float TRAP_WIZSPIKE = 4; // Wizard Acid spit spike
|
|
float TRAP_HELLSPIKE = 8; // Hell Knight Fire spikes
|
|
float TRAP_LARGEGRENADE = 1; // Player grenades (high damage)
|
|
float TRAP_LAVAROCKET = 1; // Slow moving Cthton lava ball
|
|
float TRAP_FIREROCKET = 2; // Fast moving Gargoyle fireball
|
|
float TRAP_JIMROCKET = 4; // Low damage player rocket
|
|
float TRAP_LIGHTLARGE = 1; // Cthton Lightning effect
|
|
float TRAP_LIGHTDUST = 8; // Dust/Smoke effects at impact
|
|
float TRAP_LIGHTPART = 16; // Particle effects at impact
|
|
|
|
float TRAP_GASSTEAM = 1; // Gas particle types (default)
|
|
float TRAP_GASFIRE = 2;
|
|
float TRAP_GASPOISON = 4;
|
|
float TRAP_GASSILENT = 16; // No particle sound effects
|
|
|
|
float TRAP_TOGGLE = 32; // Toggle function with triggered
|
|
float TRAP_TRACKING = 128; // Keep updating the target position
|
|
|
|
/*======================================================================
|
|
/*QUAKED trap_spikeshooter (0 0.5 0.8) (-8 -8 -8) (8 8 8) SNG LASER WIZARD HELLK x TOGGLE x TRACK
|
|
When triggered, fires a SPIKE in the direction determined by angle
|
|
-------- KEYS --------
|
|
targetname : toggle state (use trigger ent for exact state)
|
|
target : targeting entity used for custom direction
|
|
angle : direction for projectile to follow, use "360" for angle 0
|
|
wait : time between projectiles (def=0s)
|
|
delay : random time between projectile (def=0s)
|
|
count : = 1 Continuous mode (toggle/trigger to switch off)
|
|
speed : Change projectile speed (def/sng=500, laser=600, wiz=500, hell=300)
|
|
-------- SPAWNFLAGS --------
|
|
SNG : shoots large spike (SNG damage)
|
|
LASER : shoots laser (Enforcer damage)
|
|
WIZARD : shoots acid spike (Wizard damage)
|
|
HELLK : shoots fire spike (Hell Knight damage)
|
|
TOGGLE : Trigger will toggle the shooter on/off instead
|
|
TRACK : Will update target entity origin before firing
|
|
-------- NOTES --------
|
|
When triggered, fires a SPIKE in the direction determined by angle
|
|
Use TOGGLE spawnflag and trigger to enable continuous mode
|
|
|
|
======================================================================*/
|
|
void() trap_tracking =
|
|
{
|
|
if (self.target == "") return;
|
|
if (!self.movetarget)
|
|
self.movetarget = find(world, targetname, self.target);
|
|
|
|
if (self.movetarget) {
|
|
// Check for a Bmodel object (special origin)
|
|
if (self.movetarget.bsporigin) self.dest1 = bmodel_origin(self.movetarget);
|
|
else self.dest1 = self.movetarget.origin;
|
|
// Calculate facing angle towards target
|
|
self.movedir = normalize(self.dest1 - self.origin);
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() trap_shooter_fire =
|
|
{
|
|
local vector lightn_start, lightn_finish;
|
|
|
|
// Check if disabled/off first
|
|
if (self.estate & ESTATE_BLOCK) self.state = STATE_OFF;
|
|
if (self.state == STATE_OFF) return;
|
|
|
|
// Check for any target entity tracking changes
|
|
if (self.spawnflags & TRAP_TRACKING) trap_tracking();
|
|
|
|
// Fire projectile sound
|
|
if (self.classtype == CT_LIGHTSHOOTER) {
|
|
// Time for a new LG hit sound?
|
|
if (self.t_width < time) {
|
|
// Lower volume and attenuation just in case of several together
|
|
sound (self, CHAN_VOICE, self.noise, self.volume, ATTN_NORM);
|
|
self.t_width = time + 0.6;
|
|
}
|
|
}
|
|
else if (self.noise != "")
|
|
sound (self, CHAN_VOICE, self.noise, self.volume, ATTN_NORM);
|
|
|
|
// Determine type of projectile shooter
|
|
if (self.classtype == CT_SPIKESHOOTER) {
|
|
launch_projectile (self.origin, self.movedir, self.classproj ,self.speed);
|
|
}
|
|
else if (self.classtype == CT_GRENADESHOOTER) {
|
|
self.finaldest = self.movedir * self.speed;
|
|
self.finaldest_z = ELEV_ZAXIS;
|
|
self.finalangle = vecrand(100,200,FALSE);
|
|
Launch_Grenade(self.origin, self.finaldest, self.finalangle, self.classproj);
|
|
}
|
|
else if (self.classtype == CT_ROCKETSHOOTER) {
|
|
if (self.spawnflags == TRAP_LAVAROCKET) self.finalangle = vecrand(100,200,FALSE);
|
|
else self.finalangle = '0 0 0';
|
|
Launch_Missile (self.origin, self.movedir, self.finalangle, self.classproj, self.speed);
|
|
}
|
|
else if (self.classtype == CT_LIGHTSHOOTER) {
|
|
// Double check a destination origin exists
|
|
if (CheckZeroVector(self.dest1)) return;
|
|
|
|
self.effects = self.effects | EF_MUZZLEFLASH;
|
|
|
|
// setup any random X/Y/Z start/end point wobble
|
|
lightn_start = lightn_finish = '0 0 0';
|
|
if (CheckZeroVector(self.pos1) == FALSE) {
|
|
lightn_start_x = self.pos1_x * crandom();
|
|
lightn_start_y = self.pos1_y * crandom();
|
|
lightn_start_z = self.pos1_z * crandom();
|
|
}
|
|
lightn_start = lightn_start + self.origin;
|
|
if (CheckZeroVector(self.pos2) == FALSE) {
|
|
lightn_finish_x = self.pos2_x * crandom();
|
|
lightn_finish_y = self.pos2_y * crandom();
|
|
lightn_finish_z = self.pos2_z * crandom();
|
|
}
|
|
lightn_finish = lightn_finish + self.dest1;
|
|
|
|
// trace a line from trap in direction or an exact end point
|
|
traceline(lightn_start, lightn_finish, TRUE, self);
|
|
|
|
// Check for particle effects at impact
|
|
// Designed to spray dust back towards source origin
|
|
if (self.spawnflags & TRAP_LIGHTDUST && random() < 0.7) {
|
|
// Classic temporary entity
|
|
newmis = spawn();
|
|
newmis.classgroup = CG_TEMPENT;
|
|
newmis.movetype = MOVETYPE_TOSS;
|
|
newmis.solid = SOLID_NOT;
|
|
setmodel(newmis, MODEL_PROJ_SMOKE);
|
|
setorigin(newmis, trace_endpos);
|
|
setsize (newmis, VEC_ORIGIN, VEC_ORIGIN);
|
|
|
|
// Work out vector between source and target
|
|
self.pos3 = normalize(lightn_finish - lightn_start);
|
|
// Convert vector to angle so v_forward/v_right correct
|
|
self.pos3 = vectoangles(self.pos3);
|
|
makevectors(self.pos3);
|
|
|
|
// Randomize the X/Y and push the vector back to source
|
|
newmis.velocity = (crandom()*v_right)*150 + v_forward*(150+random()*300);
|
|
|
|
// Temporary ents, quickly remove afterward
|
|
newmis.nextthink = time + 1 + random()*3;
|
|
newmis.think = SUB_Remove;
|
|
}
|
|
if (self.spawnflags & TRAP_LIGHTPART)
|
|
particle_explode(trace_endpos, 5+random()*5, 1, PARTICLE_BURST_BLUE, PARTICLE_BURST_LOSTUP);
|
|
|
|
// Generate the lightning effect
|
|
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
|
|
WriteByte (MSG_BROADCAST, self.lip);
|
|
WriteEntity (MSG_BROADCAST, self);
|
|
WriteCoord (MSG_BROADCAST, lightn_start_x);
|
|
WriteCoord (MSG_BROADCAST, lightn_start_y);
|
|
WriteCoord (MSG_BROADCAST, lightn_start_z);
|
|
WriteCoord (MSG_BROADCAST, trace_endpos_x);
|
|
WriteCoord (MSG_BROADCAST, trace_endpos_y);
|
|
WriteCoord (MSG_BROADCAST, trace_endpos_z);
|
|
// Check for things to damage in between the two points
|
|
LightningDamage (lightn_start, trace_endpos, self, self.dmg);
|
|
}
|
|
|
|
// Check for temporary continuous mode
|
|
if (self.waitmin > 0 && time < self.pausetime) {
|
|
self.think = trap_shooter_fire;
|
|
self.nextthink = time + self.waitmin2;
|
|
}
|
|
else {
|
|
// Continuous mode?
|
|
if (self.spawnflags & TRAP_TOGGLE) {
|
|
self.think = trap_shooter_fire;
|
|
self.nextthink = time + self.wait + random()*self.delay;
|
|
}
|
|
// Fire once and switch off
|
|
else self.state = STATE_OFF;
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() trap_shooter_use =
|
|
{
|
|
// Check if disabled/off first
|
|
if (self.estate & ESTATE_BLOCK) return;
|
|
|
|
// Check for firing conditions (nightmare, coop)
|
|
if (check_nightmare() == TRUE) return;
|
|
if (check_coop() == TRUE) return;
|
|
|
|
// Toggle shooter on/off
|
|
if (self.state == STATE_OFF) {
|
|
self.state = STATE_ON;
|
|
if (self.waitmin > 0) self.pausetime = time + self.waitmin;
|
|
if (!self.waitmin2) self.waitmin2 = 0.1;
|
|
}
|
|
else self.state = STATE_OFF;
|
|
|
|
trap_shooter_fire();
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
// Vanilla ID QC Hack
|
|
void() spikeshooter_use = {};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() trap_shooter_reset =
|
|
{
|
|
self.estate = ESTATE_ON;
|
|
self.state = STATE_OFF;
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() trap_spikeshooter =
|
|
{
|
|
if (self.spawnflags & TRAP_SNGSPIKE) {
|
|
self.mdl = MODEL_PROJ_SNG;
|
|
self.noise = "weapons/spike2.wav";
|
|
self.classproj = CT_PROJ_SNG;
|
|
if (!self.speed) self.speed = SPEED_TRAPSPIKE;
|
|
}
|
|
else if (self.spawnflags & TRAP_LASER) {
|
|
self.mdl = MODEL_PROJ_LASER;
|
|
self.noise = "enforcer/enfire.wav";
|
|
self.classproj = CT_PROJ_LASER;
|
|
if (!self.speed) self.speed = SPEED_LASER;
|
|
// Used for impact sound of laser
|
|
precache_sound ("enforcer/enfstop.wav");
|
|
}
|
|
else if (self.spawnflags & TRAP_WIZSPIKE) {
|
|
self.mdl = MODEL_PROJ_WIZ;
|
|
self.noise = "weapons/spike2.wav";
|
|
self.volume = 0.5;
|
|
self.classproj = CT_PROJ_WIZ;
|
|
if (!self.speed) self.speed = SPEED_WIZSPIKE;
|
|
}
|
|
else if (self.spawnflags & TRAP_HELLSPIKE) {
|
|
self.mdl = MODEL_PROJ_HKN;
|
|
self.noise = "weapons/spike2.wav";
|
|
self.volume = 0.5;
|
|
self.classproj = CT_PROJ_HKN;
|
|
if (!self.speed) self.speed = SPEED_HKSPIKE;
|
|
}
|
|
else {
|
|
self.mdl = MODEL_PROJ_NG;
|
|
self.noise = "weapons/spike2.wav";
|
|
self.classproj = CT_PROJ_NG;
|
|
if (!self.speed) self.speed = SPEED_TRAPSPIKE;
|
|
}
|
|
precache_model (self.mdl);
|
|
precache_sound (self.noise);
|
|
|
|
self.classtype = CT_SPIKESHOOTER;
|
|
if (self.wait <= 0) self.wait = 1;
|
|
if (self.delay < 0) self.delay = 0;
|
|
if (!self.volume) self.volume = 1;
|
|
self.mangle = self.angles;
|
|
SetMovedir ();
|
|
|
|
// Setup Entity State functionality
|
|
if (self.targetname != "") self.use = entity_state_use;
|
|
self.estate_use = trap_shooter_use;
|
|
self.estate_reset = trap_shooter_reset;
|
|
self.estate = ESTATE_ON;
|
|
self.state = STATE_OFF;
|
|
|
|
// If target is setup, calculate new facing angle
|
|
if (self.target != "") {
|
|
self.nextthink = time + 1 + random();
|
|
self.think = trap_tracking;
|
|
}
|
|
};
|
|
|
|
/*======================================================================
|
|
/*QUAKED trap_grenadeshooter (0 0.5 0.8) (-8 -8 -8) (8 8 8) LARGE x x x x TOGGLE x TRACK Not_Easy Not_Normal Not_Hard Not_DM
|
|
When triggered, fires a GRENADE in the direction determined by angle
|
|
-------- KEYS --------
|
|
targetname : toggle state (use trigger ent for exact state)
|
|
target : targeting entity used for custom direction
|
|
angle : direction for projectile to follow, use "360" for angle 0
|
|
wait : time between projectiles (def=0s)
|
|
delay : random time between projectile (def=0s)
|
|
speed : Change projectile speed (def=500, large=600)
|
|
-------- SPAWNFLAGS --------
|
|
LARGE : shoots high damage grenade (Player damage, def=ogre)
|
|
TOGGLE : Trigger will toggle the shooter on/off instead
|
|
TRACK : Will update target entity origin before firing
|
|
-------- NOTES --------
|
|
When triggered, fires a GRENADE in the direction determined by angle
|
|
Use TOGGLE spawnflag and trigger to enable continuous mode
|
|
|
|
======================================================================*/
|
|
void() trap_grenadeshooter =
|
|
{
|
|
self.mdl = MODEL_PROJ_GRENADE;
|
|
precache_model (self.mdl);
|
|
self.noise = "weapons/grenade.wav";
|
|
precache_sound (self.noise);
|
|
|
|
if (self.spawnflags & TRAP_LARGEGRENADE) {
|
|
self.classproj = CT_PROJ_GL;
|
|
if(!self.speed) self.speed = SPEED_PLAYGRENADE;
|
|
}
|
|
else {
|
|
self.classproj = CT_PROJ_GLMON;
|
|
if(!self.speed) self.speed = SPEED_PLAYGRENADE;
|
|
}
|
|
|
|
self.classtype = CT_GRENADESHOOTER;
|
|
if (self.wait <= 0) self.wait = 1;
|
|
if (self.delay < 0) self.delay = 0;
|
|
if (!self.volume) self.volume = 1;
|
|
self.mangle = self.angles;
|
|
SetMovedir ();
|
|
|
|
// Setup Entity State functionality
|
|
if (self.targetname != "") self.use = entity_state_use;
|
|
self.estate_use = trap_shooter_use;
|
|
self.estate_reset = trap_shooter_reset;
|
|
self.estate = ESTATE_ON;
|
|
self.state = STATE_OFF;
|
|
|
|
// If target is setup, calculate new facing angle
|
|
if (self.target != "") {
|
|
self.nextthink = time + 1 + random();
|
|
self.think = trap_tracking;
|
|
}
|
|
};
|
|
|
|
/*======================================================================
|
|
/*QUAKED trap_rocketshooter (0 0.5 0.8) (-8 -8 -8) (8 8 8) LAVA FIRE JIM x x TOGGLE x TRACK Not_Easy Not_Normal Not_Hard Not_DM
|
|
When triggered, fires a ROCKET in the direction determined by angle
|
|
-------- KEYS --------
|
|
targetname : toggle state (use trigger ent for exact state)
|
|
target : targeting entity used for custom direction
|
|
angle : direction for projectile to follow, use "360" for angle 0
|
|
wait : time between projectiles (def=0s)
|
|
delay : random time between projectile (def=0s)
|
|
speed : Change projectile speed (def=1000, lava=300, fire=500)
|
|
-------- SPAWNFLAGS --------
|
|
LAVABALL : shoots Chthon lava ball (Player damage)
|
|
FIREBALL : shoots Gargoyle fire ball (low damage)
|
|
JIM FLYER: shoots Jim rocket (low damage)
|
|
TOGGLE : Trigger will toggle the shooter on/off instead
|
|
TRACK : Will update target entity origin before firing
|
|
-------- NOTES --------
|
|
When triggered, fires a ROCKET in the direction determined by angle
|
|
Use TOGGLE spawnflag and trigger to enable continuous mode
|
|
|
|
======================================================================*/
|
|
void() trap_rocketshooter =
|
|
{
|
|
if (self.spawnflags & TRAP_LAVAROCKET) {
|
|
self.mdl = MODEL_PROJ_LAVA;
|
|
precache_model (self.mdl);
|
|
self.noise = "boss1/throw.wav";
|
|
precache_sound (self.noise);
|
|
|
|
self.classproj = CT_PROJ_LAVA;
|
|
if (!self.speed) self.speed = SPEED_LAVABALL;
|
|
}
|
|
else if (self.spawnflags & TRAP_FIREROCKET) {
|
|
self.mdl = MODEL_PROJ_GARGOYLE;
|
|
precache_model (self.mdl);
|
|
self.noise = "gargoyle/attack1.wav";
|
|
precache_sound (self.noise);
|
|
|
|
self.classproj = CT_PROJ_GARG;
|
|
if (!self.speed) self.speed = SPEED_GARGMISSILE;
|
|
}
|
|
else if (self.spawnflags & TRAP_JIMROCKET) {
|
|
self.mdl = MODEL_PROJ_ROCKET;
|
|
precache_model (self.mdl);
|
|
self.noise = "weapons/sgun1.wav";
|
|
precache_sound (self.noise);
|
|
precache_sound ("jim/rocket_hit.wav");
|
|
|
|
self.classproj = CT_PROJ_JIM2;
|
|
if (!self.speed) self.speed = SPEED_RLPLAYER;
|
|
}
|
|
else {
|
|
self.mdl = MODEL_PROJ_ROCKET;
|
|
precache_model (self.mdl);
|
|
self.noise = "weapons/sgun1.wav";
|
|
precache_sound (self.noise);
|
|
|
|
self.classproj = CT_PROJ_ROCKET;
|
|
if (!self.speed) self.speed = SPEED_RLPLAYER;
|
|
}
|
|
|
|
self.classtype = CT_ROCKETSHOOTER;
|
|
if (self.wait <= 0) self.wait = 1;
|
|
if (self.delay < 0) self.delay = 0;
|
|
if (!self.volume) self.volume = 1;
|
|
self.mangle = self.angles;
|
|
SetMovedir ();
|
|
|
|
// Setup Entity State functionality
|
|
if (self.targetname != "") self.use = entity_state_use;
|
|
self.estate_use = trap_shooter_use;
|
|
self.estate_reset = trap_shooter_reset;
|
|
self.estate = ESTATE_ON;
|
|
self.state = STATE_OFF;
|
|
|
|
// If target is setup, calculate new facing angle
|
|
if (self.target != "") {
|
|
self.nextthink = time + 1 + random();
|
|
self.think = trap_tracking;
|
|
}
|
|
};
|
|
|
|
/*======================================================================
|
|
/*QUAKED trap_lightningshooter (0 0.5 0.8) (-8 -8 -8) (8 8 8) LARGE x x DUST PART TOGGLE x TRACK
|
|
When triggered, fires a LIGHTNING at the target entity (can be blocked)
|
|
-------- KEYS --------
|
|
targetname : toggle state (use trigger ent for exact state)
|
|
target : targeting entity used for destination (required)
|
|
wait : time between projectiles (def=1s)
|
|
delay : random time between projectile (def=0s)
|
|
volume : Lightning hit sound volume (def=0.75)
|
|
dmg : Damage from lightning strike (def=15, large=30)
|
|
pos1 : Random XYZ wobble to source position
|
|
pos2 : Random XYZ wobble to target position
|
|
-------- SPAWNFLAGS --------
|
|
LARGE : Cthton Boss Lightning
|
|
DUST : Produce dust/smoke at point of impact
|
|
PARTICLE: Produce particles at point of impact
|
|
TOGGLE : Trigger will toggle the shooter on/off instead
|
|
TRACK : Will update target entity origin before firing
|
|
-------- NOTES --------
|
|
When triggered, fires a LIGHTNING at the target entity (can be blocked)
|
|
Use TOGGLE spawnflag and trigger to enable continuous mode
|
|
|
|
======================================================================*/
|
|
void() trap_lightningshooter =
|
|
{
|
|
self.noise = "weapons/lhit.wav";
|
|
precache_sound (self.noise);
|
|
|
|
self.classtype = CT_LIGHTSHOOTER;
|
|
if (self.wait <= 0) self.wait = 1;
|
|
if (self.delay < 0) self.delay = 0;
|
|
if (!self.volume) self.volume = 0.75;
|
|
self.t_width = 0; // Light sound timer
|
|
|
|
// Sort out lightning damage
|
|
if (self.dmg < 1) {
|
|
self.dmg = 15;
|
|
// Double for large lightning
|
|
if (self.spawnflags & TRAP_LIGHTLARGE) self.dmg = 30;
|
|
}
|
|
|
|
// Work out which lightning bolt to use
|
|
if (self.spawnflags & TRAP_LIGHTLARGE) self.lip = TE_LIGHTNING3;
|
|
else self.lip = TE_LIGHTNING2;
|
|
|
|
// Must have target for destination of lightning
|
|
if (self.target == "") {
|
|
dprint("\b[TRAP]\b Lightning trap missing target!\n");
|
|
spawn_marker(self.origin, SPNMARK_YELLOW);
|
|
remove(self);
|
|
return;
|
|
}
|
|
|
|
// Setup Entity State functionality
|
|
if (self.targetname != "") self.use = entity_state_use;
|
|
self.estate_use = trap_shooter_use;
|
|
self.estate_reset = trap_shooter_reset;
|
|
self.estate = ESTATE_ON;
|
|
self.state = STATE_OFF;
|
|
|
|
// Find target once everything has spawned
|
|
self.nextthink = time + 1 + random();
|
|
self.think = trap_tracking;
|
|
};
|
|
|
|
//======================================================================
|
|
/*QUAKED trap_gasshooter (0 .5 .8) (-8 -8 -8) (8 8 8) STEAM FIRE POISON x SILENT TOGGLE x TRACK
|
|
When triggered, fires a gas particle in the direction determined by angle
|
|
-------- KEYS --------
|
|
targetname : toggle state (use trigger ent for exact state)
|
|
target : targeting entity used for custom direction
|
|
angle : direction for stream to follow, use "360" for angle 0
|
|
wait : time between particles (def=0.05s)
|
|
speed : velocity speed (def=200)
|
|
waitmin: auto switch off timer (def=0.5s)
|
|
dmg : damage from contact with particles (def=1)
|
|
-------- SPAWNFLAGS --------
|
|
STEAM : White hot clouds of steam (default)
|
|
FIRE : Will add burning debuff to player
|
|
POISON : Will add poison debuff to player
|
|
SILENT : No on/off sound, its silent!
|
|
TOGGLE : Trigger will toggle the shooter on/off instead
|
|
TRACK : Will update target entity origin before firing
|
|
-------- NOTES --------
|
|
When triggered, fires a gas particle in the direction determined by angle
|
|
Use TOGGLE spawnflag and trigger to enable continuous mode
|
|
|
|
======================================================================*/
|
|
void() trap_gasshooter_touch =
|
|
{
|
|
if (other == self.owner) return;
|
|
if (other.solid == SOLID_TRIGGER) return;
|
|
if (other.health < 1) return;
|
|
if (other.takedamage == DAMAGE_NO) return;
|
|
if (self.attack_finished > time) return;
|
|
if (other.classtype == self.owner.classtype) return;
|
|
|
|
self.attack_finished = time + 1;
|
|
|
|
// Check contact is a player?
|
|
if (other.flags & FL_CLIENT) {
|
|
// Check for debuff particle type first
|
|
if (self.owner.spawnflags & TRAP_GASFIRE)
|
|
ApplyFireDmg(other, self.owner.dmg, self.owner.dmg);
|
|
else if (self.owner.spawnflags & TRAP_GASPOISON)
|
|
PoisonDeBuff(other);
|
|
else T_Damage (other, self, self, self.owner.dmg, NOARMOR);
|
|
}
|
|
// Monsters have special damage to kill them quicker
|
|
else if (other.flags & FL_MONSTER) T_Damage (other, self, self, DAMAGE_MONFLAME, NOARMOR);
|
|
else T_Damage (other, self, self, self.owner.dmg, NOARMOR);
|
|
self.velocity = '0 0 0';
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() trap_gasshooter_think =
|
|
{
|
|
self.cnt = self.cnt + 1;
|
|
if (self.cnt > 6) remove(self);
|
|
else {
|
|
self.frame = self.cnt;
|
|
self.nextthink = time + 0.1 + random() * 0.05;
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() trap_gasshooter_spawn =
|
|
{
|
|
local entity partemit;
|
|
|
|
// Check if disabled/off first
|
|
if (self.estate & ESTATE_BLOCK) self.state = STATE_OFF;
|
|
if (self.state == STATE_OFF) return;
|
|
|
|
// Check for any target entity tracking changes
|
|
if (self.spawnflags & TRAP_TRACKING) trap_tracking();
|
|
|
|
partemit = spawn();
|
|
partemit.classtype = CT_TEMPSTREAM;
|
|
partemit.owner = self;
|
|
partemit.frame = partemit.cnt = 0;
|
|
|
|
partemit.movetype = MOVETYPE_FLY; // Fly, no gravity, but contact
|
|
partemit.solid = SOLID_TRIGGER; // collision, touch required
|
|
setmodel (partemit, self.mdl);
|
|
setorigin (partemit, self.origin);
|
|
setsize (partemit, VEC_ORIGIN, VEC_ORIGIN);
|
|
|
|
partemit.oldorigin = vecrand(0,20,TRUE);
|
|
partemit.velocity = (self.movedir * self.speed) + partemit.oldorigin;
|
|
partemit.angles_z = random() * 360;
|
|
|
|
// If DP engine active remove particle shadow
|
|
if (engine == ENG_DPEXT) partemit.effects = partemit.effects + EF_NOSHADOW;
|
|
|
|
partemit.nextthink = time + 0.1 + random() * 0.05;
|
|
partemit.think = trap_gasshooter_think;
|
|
partemit.touch = trap_gasshooter_touch;
|
|
|
|
// Continuous mode or auto switch off mode still on?
|
|
if (self.spawnflags & TRAP_TOGGLE || self.waitmin2 > time) {
|
|
self.think = trap_gasshooter_spawn;
|
|
self.nextthink = time + self.wait;
|
|
}
|
|
// Time to switch off
|
|
else {
|
|
self.state = STATE_OFF;
|
|
sound (self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() trap_gasshooter_off =
|
|
{
|
|
self.estate = ESTATE_OFF;
|
|
// If gas shooter currently on, play switch off sound
|
|
if (self.state == STATE_ON)
|
|
sound (self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);
|
|
else sound (self, CHAN_VOICE, SOUND_EMPTY, 1, ATTN_NORM);
|
|
self.state = STATE_OFF;
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() trap_gasshooter_reset =
|
|
{
|
|
self.estate = ESTATE_ON;
|
|
// clear any sounds playing
|
|
sound (self, CHAN_VOICE, SOUND_EMPTY, 1, ATTN_NORM);
|
|
self.state = STATE_OFF;
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() trap_gasshooter_on =
|
|
{
|
|
self.estate = ESTATE_ON;
|
|
self.state = STATE_ON;
|
|
sound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM);
|
|
self.waitmin2 = time + self.waitmin;
|
|
trap_gasshooter_spawn();
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() trap_gasshooter_use =
|
|
{
|
|
// Check if disabled/off first
|
|
if (self.estate & ESTATE_BLOCK) return;
|
|
|
|
// Check for firing conditions (nightmare, coop)
|
|
if (check_nightmare() == TRUE) return;
|
|
if (check_coop() == TRUE) return;
|
|
|
|
// Toggle shooter on/off
|
|
if (self.state == STATE_ON) {
|
|
self.state = STATE_OFF;
|
|
sound (self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);
|
|
}
|
|
else trap_gasshooter_on();
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() trap_gasshooter =
|
|
{
|
|
if (self.spawnflags & TRAP_GASFIRE) {
|
|
self.mdl = SBURST_FLAME;
|
|
self.noise1 = "traps/flame_loop.wav";
|
|
self.noise2 = "traps/flame_off.wav";
|
|
}
|
|
else if (self.spawnflags & TRAP_GASPOISON) {
|
|
self.mdl = SBURST_POISON;
|
|
self.noise1 = "traps/steam_loop.wav";
|
|
self.noise2 = "traps/steam_off.wav";
|
|
}
|
|
else {
|
|
// Steam particles is the default
|
|
self.spawnflags = self.spawnflags | TRAP_GASSTEAM;
|
|
self.mdl = SBURST_STEAM;
|
|
self.noise1 = "traps/steam_loop.wav";
|
|
self.noise2 = "traps/steam_off.wav";
|
|
}
|
|
|
|
if (self.spawnflags & TRAP_GASSILENT) {
|
|
self.noise1 = SOUND_EMPTY;
|
|
self.noise2 = SOUND_EMPTY;
|
|
}
|
|
precache_model (self.mdl);
|
|
precache_sound (self.noise1);
|
|
precache_sound (self.noise2);
|
|
|
|
self.classtype = CT_GASSHOOTER;
|
|
self.solid = SOLID_NOT; // No world interaction
|
|
self.movetype = MOVETYPE_NONE; // Static item, no movement
|
|
|
|
if (!self.dmg) self.dmg = 1;
|
|
if (self.speed < 1) self.speed = 200;
|
|
if (self.wait <0.05) self.wait = 0.05;
|
|
if (self.waitmin <=0) self.waitmin = 0.5;
|
|
|
|
// Does not auto switch off anymore
|
|
if (self.spawnflags & TRAP_TOGGLE) self.waitmin = LARGE_TIMER;
|
|
|
|
// setup any angle direction
|
|
if (self.angles_y == 0) self.angles_y = 360;
|
|
self.mangle = self.angles;
|
|
SetMovedir();
|
|
|
|
// Setup Entity State functionality
|
|
if (self.targetname != "") self.use = entity_state_use;
|
|
self.estate_use = trap_gasshooter_use;
|
|
self.estate_reset = trap_gasshooter_reset;
|
|
self.estate_off = trap_gasshooter_off;
|
|
self.estate_on = trap_gasshooter_on;
|
|
self.estate = ESTATE_ON;
|
|
self.state = STATE_OFF;
|
|
|
|
// If target is setup, calculate new facing angle
|
|
if (self.target != "") {
|
|
self.nextthink = time + 1 + random();
|
|
self.think = trap_tracking;
|
|
}
|
|
};
|