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

282 lines
9.6 KiB
Plaintext

/*======================================================================
TRAP Sawblade
* Originally based on code from Rogue Software
* Extra code support from lunaran
======================================================================*/
float SAW_STARTON = 1;
float SAW_XAXIS = 16;
float SAW_REVERSE = 32;
/*======================================================================
/*QUAKED trap_sawbladex (0 .5 .8) (-18 -18 -18) (18 18 18) STARTON x x x x REVERSE STARTOFF x
Rotating Saw Blade
-------- KEYS --------
targetname : toggle state (use trigger ent for exact state)
target : Name of first path_corner to start at (instant move)
speed : Speed to follow path, def=100
yaw_speed : Rotation speed for Saw Blade (def=180)
dmg : Damage to do for each contact, def=4
waitmin : Damage throttle for ON touch function, def=0.1s
height : Damage throttle for OFF touch function, def=1s
lip : Deceleration time; def=2s, =-1 instant stop
sounds : 0=Silent, 1=Woodmill, 5=Custom sounds
noise : custom sound - stopped
noise1 : custom sound - moving (looping)
-------- SPAWNFLAGS --------
STARTON : Start moving straight away if targetname is used
REVERSE : Start going backward through path_corner chain
STARTOFF : Starts off and waits for trigger
-------- NOTES --------
Rotating Saw Blade
======================================================================*/
void() trap_sawblade_touch =
{
// Blade can still hurt if touched, except when OFF = not visible
if (self.estate & ESTATE_OFF) return;
// Double check if dead monster body!
if (trigger_check_body(other,DEAD_EXPLODE)) return;
if (other.health < 1 || other.takedamage == DAMAGE_NO) return;
if (self.attack_finished > time) return;
// --- lunaran -------------------
// Different touch damage based on blade state
if (self.state == STATE_ON) {
if (self.waitmin > 0) self.attack_finished = time + self.waitmin;
// Hit sounded added by Lunaran, added sound string check
if (self.noise2 !="") sound (self, CHAN_AUTO, self.noise2, 1, 1);
}
else {
if (self.height > 0) self.attack_finished = time + self.height;
}
// Instant death for monsters
if (other.flags & FL_MONSTER)
T_Damage (other, self, self, other.max_health + 100, DAMARMOR);
else
// Small damage but can be lethal because of how often touch runs
T_Damage (other, self, self, self.dmg, DAMARMOR);
// Blood and gore at object location not sawblade
SpawnMeatSpray ( other, other, crandom() * 200);
};
//----------------------------------------------------------------------
// New slowdown/stop function by lunaran
//----------------------------------------------------------------------
void() trap_sawblade_stopthink =
{
float remaining;
// If slow down time expired, make sure blade has stopped
if (self.pain_finished <= time) {
self.velocity = self.avelocity = '0 0 0';
return;
}
remaining = (self.pain_finished - time) / self.lip;
//----------------------------------------------------------------------
// quit moving if we've hit the next pathcorner
// it'd be cooler to continue on the track as we decelerate but this way
// we don't have to muck about in the func_train code
//----------------------------------------------------------------------
if (self.rad_time && self.rad_time <= time) {
self.velocity = '0 0 0';
setorigin(self, self.finaldest);
self.rad_time = 0;
}
// ramp down from original speed and spinrate over self.lip seconds
if (CheckZeroVector(self.velocity) == FALSE)
self.velocity = normalize(self.velocity) * self.speed * remaining;
self.avelocity = normalize(self.avelocity) * self.yaw_speed * remaining;
// Double tick time to smooth out slowdown
self.nextthink = time + 0.05;
}
//----------------------------------------------------------------------
// New slowdown/stop function by lunaran
//----------------------------------------------------------------------
void() trap_sawblade_stop =
{
float d, timeToPathCorner;
// Play wind down stop sound
sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM);
// save the time we become completely still in pain_finished
self.pain_finished = time + self.lip;
// calculate when we'll hit the next pathcorner (if ever)
// so we can stop there
timeToPathCorner = vlen(self.origin-self.finaldest) / self.speed;
// linear deceleration = stopping distance of exactly 1/2 speed*stoptime
if (self.lip * 0.5 > timeToPathCorner ) {
// linear deceleration = exponential time taken relative to distance
d = mathlib_sqrt( 1 - ( 2 * timeToPathCorner / self.lip ) );
d = self.lip * ( 1 - d );
// save the time in rad_time
self.rad_time = time + d;
}
self.think = trap_sawblade_stopthink;
self.think();
};
//----------------------------------------------------------------------
void() trap_sawblade_use =
{
// Block entity state OFF + DISABLE
if (self.estate & ESTATE_BLOCK) return;
// Toggle function
if (self.state == STATE_OFF) {
self.state = STATE_ON;
// use special train resume function
func_train_resume();
}
else {
self.state = STATE_OFF;
trap_sawblade_stop();
}
};
//----------------------------------------------------------------------
void() trap_sawblade_setup =
{
// 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(); // Switch on entity
func_train_startcorner(); // Move to first corner
self.estate_use = trap_sawblade_use;// Switch ON sawblade
self.estate_fire = func_train_next; // Move to next path corner
self.state = STATE_OFF; // sawblade is OFF, waiting
// Sharp pointy edges!
self.touch = trap_sawblade_touch;
// Did the first corner (owner) exist?
if (self.owner && self.spawnflags & SAW_STARTON) {
self.nextthink = time + 0.1;
self.think = self.estate_use;
}
};
//----------------------------------------------------------------------
void() trap_sawblade_on =
{
// Stop re-triggering ON state
if (self.estate == ESTATE_ON) return;
// Turn on model/world interaction
self.estate = ESTATE_ON; // Switch on entity
self.solid = SOLID_TRIGGER; // Keep on cutting!
self.movetype = MOVETYPE_NOCLIP; // Free movement
setmodel (self, self.mdl); // Show bmodel/model
setsize (self, self.bbmins , self.bbmaxs); // Use pre-defined size
// Stop any current sounds, new sound will play in func_train function
sound (self, CHAN_VOICE, SOUND_EMPTY, 1, ATTN_NORM);
self.velocity = self.avelocity = '0 0 0'; // reset velocity/rotation
};
//----------------------------------------------------------------------
void() trap_sawbladey =
{
// 0 = No sound, 1 = buzzzzz, 5 = custom
if (self.sounds > 0) {
if (self.sounds == 1) {
self.noise = ("traps/sawblade_off.wav");
self.noise1 = ("traps/sawblade_on.wav");
self.noise2 = ("traps/sawstrike1.wav"); // lunaran
}
else if (self.sounds == 5) {
if (self.noise == "") self.noise = SOUND_EMPTY;
if (self.noise1 == "") self.noise1 = SOUND_EMPTY;
if (self.noise2 == "") self.noise2 = SOUND_EMPTY; // lunaran
}
precache_sound (self.noise);
precache_sound (self.noise1);
precache_sound (self.noise2); // lunaran
}
self.mdl = "progs/trap_sawblade.mdl";
precache_model (self.mdl);
self.classtype = CT_SAWBLADE;
self.state = STATE_OFF;
self.volume = 0.5;
// Error - Check for first path corner target
if (self.target == "") {
dprint("\b[SAWBLADE]\b missing path corner target!\n");
}
// Setup orientiation and bounding box parameters
// required for state on and touch function
if (self.spawnflags & SAW_XAXIS) {
self.angles = '0 0 0';
self.bbmins = '-20 -2 -20';
self.bbmaxs = '20 2 20';
}
else {
self.angles = '0 90 0';
self.bbmins = '-2 -20 -20';
self.bbmaxs = '2 20 20';
}
if (!self.dmg) self.dmg = 4; // Default touch damage
if (!self.waitmin) self.waitmin = 0.1; // Damage throttle ON state
if (self.speed < 1) self.speed = 100; // Default path speed
self.wait = self.delay = 0; // reset wait and delay
// --- lunaran ----------------------
if (!self.height) self.height = 1; // Damage throttle OFF state
if (!self.lip) self.lip = 2; // Deceleration time, -1 for none
if (self.lip < 0) self.lip = 0; // Convert -1 switch to 0 time
// Setup default rotation speed
if (!self.yaw_speed) self.yaw_speed = 180;
self.v_angle = '0 0 0';
self.v_angle_x = self.yaw_speed;
// Save spawn location
self.dest0 = self.finaldest = self.origin;
if (self.target == "") self.owner = self;
self.bsporigin = FALSE;
// Cannot have start ON and OFF together, remove OFF state!
if (self.spawnflags & SAW_STARTON && self.spawnflags & ENT_STARTOFF)
self.spawnflags = self.spawnflags - ENT_STARTOFF;
// Setup train direction (switchable via path corners)
if (self.spawnflags & SAW_REVERSE) self.direction = 1;
else self.direction = 0;
// Setup Entity State functionality
if (self.targetname != "") self.use = entity_state_use;
self.estate_on = trap_sawblade_on;
self.estate_off = func_train_off;
self.estate_use = trap_sawblade_setup;
self.estate_reset = func_train_reset;
self.estate_disable = func_train_disable;
if (self.spawnflags & ENT_STARTOFF) self.estate_off();
else {
self.nextthink = time + 0.1;
self.think = self.estate_use;
}
};
//----------------------------------------------------------------------
void() trap_sawbladex =
{
self.spawnflags = self.spawnflags | SAW_XAXIS;
trap_sawbladey();
};