136 lines
4.8 KiB
Plaintext
136 lines
4.8 KiB
Plaintext
/*======================================================================
|
|
MONSTER TETHER SYSTEM
|
|
|
|
======================================================================*/
|
|
// new entity keys
|
|
|
|
.string tethertarget; // String name to link tethering too
|
|
.entity tethertarg; // Entity to track for tether system
|
|
.void() th_tether; // Function to call when at max range
|
|
.float tethered; // is the monster tethered during combat
|
|
.float tetherrange; // The circular range around tether point
|
|
.float tetherdist; // Distance from nonster to tether point
|
|
.float tetherenemy; // Distance from enemy to tether point
|
|
.float tetherwait; // Amount of time to wait before returning
|
|
.float tethertimer; // Time before returning to tether point
|
|
.float tetherturn; // Make sure monster turns towards enemy
|
|
.float tetherpause; // % to not pause attack at max range
|
|
.entity tetherfocus; // Enemy to turn towards while waiting
|
|
|
|
float TETHER_RADIUS = 300; // Default tether radius
|
|
float TETHER_WAIT = 30; // Default timer to wait before reset
|
|
|
|
// Defined under defs.qc
|
|
//.float tetherlock; // Is the monster locked from movement
|
|
|
|
//----------------------------------------------------------------------
|
|
// Check for tether point (find and setup entity)
|
|
//----------------------------------------------------------------------
|
|
void() setup_tethersystem =
|
|
{
|
|
// Check if the tether system be setup already
|
|
if (self.tethertarget != "") {
|
|
// Check for range attack, cannot tether if no range
|
|
if (!self.th_missile) {
|
|
dprint("\b[TETHER]\b No range attack for ");
|
|
dprint(self.tethertarget);
|
|
dprint("\n");
|
|
self.tethertarget = "";
|
|
return;
|
|
}
|
|
|
|
// Find the tether point (has to be path corner)
|
|
self.tethertarg = find(world, targetname, self.tethertarget);
|
|
if (self.tethertarg != world) {
|
|
// Tether system is active (reset lock)
|
|
self.tethered = TRUE;
|
|
self.tetherlock = FALSE;
|
|
// Setup default tether range
|
|
if (self.tetherrange < 32) self.tetherrange = TETHER_RADIUS;
|
|
if (self.tetherwait <= 0) self.tetherwait = TETHER_WAIT;
|
|
// Reset distances (updated in check function)
|
|
self.tetherdist = self.tetherenemy = 0;
|
|
// No stand/walk timer active yet
|
|
self.tethertimer = LARGE_TIMER;
|
|
// Check for tether function (def=missile)
|
|
if (!self.th_tether) self.th_tether = self.th_missile;
|
|
// Don't need tethertarget anymore
|
|
self.tethertarget = "";
|
|
}
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
// Check for tether range and return TRUE if at radius limit
|
|
//----------------------------------------------------------------------
|
|
float() check_tethersystem =
|
|
{
|
|
local vector org;
|
|
|
|
// Is the system active on the monster?
|
|
if (self.tethered == FALSE) return FALSE;
|
|
|
|
// Read tether target for BSP/Ent origin
|
|
if (self.tethertarg.bsporigin) org = bmodel_origin(self.tethertarg);
|
|
else org = self.tethertarg.origin;
|
|
|
|
// Setup tether distance for monster and enemy
|
|
self.tetherdist = vlen(self.origin - org);
|
|
// Check for enemy entity first before distance check
|
|
if (self.enemy) self.tetherenemy = vlen(self.enemy.origin - org);
|
|
else self.tetherenemy = 0;
|
|
|
|
// Reset lock
|
|
self.tetherlock = FALSE;
|
|
|
|
// If inside tether range, keep moving
|
|
if (self.tetherdist < self.tetherrange) return FALSE;
|
|
else {
|
|
// If enemy behind tether point, move closer
|
|
if (infront(self.tethertarg) && infront(self.enemy))
|
|
return FALSE;
|
|
// Reach tether limit
|
|
else {
|
|
// Can monster see the enemy?
|
|
if (enemy_vis) return TRUE;
|
|
else {
|
|
// Check for no pause override
|
|
if (self.tetherpause > 0) {
|
|
if (random() <= self.tetherpause) return TRUE;
|
|
}
|
|
// At max range + cannot see enemy, exit combat
|
|
self.tetherfocus = self.enemy; // Save for later
|
|
self.enemy = self.goalentity = self.movetarget = world;
|
|
self.attack_state = AS_STRAIGHT;
|
|
self.think = self.th_stand; // Wait for enemy
|
|
self.tethertimer = time + self.tetherwait;
|
|
}
|
|
}
|
|
}
|
|
return FALSE;
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
// Check tether timer for stand function
|
|
//----------------------------------------------------------------------
|
|
void() check_tethertimer =
|
|
{
|
|
if (self.tethered == FALSE) return; // System active?
|
|
if (!self.tethertarg) return; // No target active
|
|
if (self.enemy) return; // In combat?
|
|
|
|
// Has the timer been reached?
|
|
if (self.tethertimer < time) {
|
|
self.tetherlock = FALSE; // Make sure not set
|
|
self.goalentity = self.movetarget = self.tethertarg;
|
|
self.tethertimer = LARGE_TIMER; // Timer no longer needed
|
|
self.pausetime = 0; // Start walking
|
|
}
|
|
else {
|
|
// Check to see if facing enemy is active?
|
|
if (self.tetherturn && self.tetherfocus) {
|
|
self.ideal_yaw = vectoyaw(self.tetherfocus.origin - self.origin);
|
|
ChangeYaw ();
|
|
}
|
|
}
|
|
}; |