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

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 ();
}
}
};