Too lazy to comment
This commit is contained in:
693
mod_xj18/my_progs/traps.qc
Normal file
693
mod_xj18/my_progs/traps.qc
Normal file
@@ -0,0 +1,693 @@
|
||||
/*======================================================================
|
||||
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;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user