901 lines
22 KiB
C++
901 lines
22 KiB
C++
/*
|
|
=============================
|
|
ewai.qc
|
|
coded by
|
|
Michael Rogers a.k.a Xsniper
|
|
ssj_xsniper@yahoo.com
|
|
xsniper.virtualave.net
|
|
|
|
Description:
|
|
This file's holds important new artificial intelligence functions
|
|
for Eternal War. Things like better dodging code, better chase code,
|
|
and code to move monsters out of walls when they become stuck.
|
|
|
|
Also contains some other miscellaneous functions.
|
|
=============================
|
|
*/
|
|
|
|
//predefining functions
|
|
void() bossf1_flipb1;
|
|
//void() bossf1_block;
|
|
void() bossf1_atakd1; //summon shadow clones
|
|
void(float atype) Apprent_StartMissile; //apprentice missile attacks
|
|
void() CastLightning; //boss lightning attack
|
|
|
|
|
|
|
|
/*
|
|
=============================================================
|
|
Teleportation and Monster Unsticking Code Below
|
|
=============================================================
|
|
*/
|
|
|
|
/*
|
|
====================
|
|
monster_check_teleport
|
|
|
|
Check to see if we can teleport
|
|
Returns 0 if we can't teleport
|
|
Returns 1 if we can teleport
|
|
|
|
float ttype: variable controlling what type of teleport
|
|
0: teleport to his left
|
|
1: teleport to his right
|
|
2: teleport behind his self.enemy
|
|
3: teleport in slow increments infront of him with no sound or fog
|
|
4: teleport in slow increments behind him with no sound or fog
|
|
5: teleport in slow increments to his left with no sound or fog
|
|
6: teleport in slow increments to his right with no sound or fog
|
|
====================
|
|
*/
|
|
float(float ttype) monster_check_teleport =
|
|
{
|
|
local vector start, end;
|
|
|
|
//set up angles stuff
|
|
self.v_angle = self.angles;
|
|
makevectors (self.angles);
|
|
|
|
//we need to check for walls so that we don't teleport inside them
|
|
|
|
//check based on ttype
|
|
if (ttype == 0)
|
|
{
|
|
//check for teleport to his left
|
|
start = self.origin;
|
|
end = start - (v_right * 80);
|
|
}
|
|
else if (ttype == 1)
|
|
{
|
|
//check for teleport to his right
|
|
start = self.origin;
|
|
end = start + (v_right * 80);
|
|
}
|
|
else if (ttype == 2)
|
|
{
|
|
//check for teleport behind his self.enemy
|
|
start = self.enemy.origin;
|
|
end = start + (v_forward * 80);
|
|
}
|
|
|
|
// Do the check
|
|
traceline (start, end, TRUE, self);
|
|
|
|
// If there is a wall or obstacle then return NOTELEPORT
|
|
if (trace_fraction != 1.0)
|
|
return (0); //NOTELEPORT
|
|
|
|
return (1); //TELEPORT
|
|
};
|
|
|
|
/*
|
|
====================
|
|
monster_teleport
|
|
|
|
Teleports monster based on ttype
|
|
|
|
float ttype: variable controlling what type of teleport
|
|
0: teleport to his left
|
|
1: teleport to his right
|
|
2: teleport behind his self.enemy
|
|
3: teleport in slow increments infront of him with no sound or fog
|
|
4: teleport in slow increments behind him with no sound or fog
|
|
5: teleport in slow increments to his left with no sound or fog
|
|
6: teleport in slow increments to his right with no sound or fog
|
|
====================
|
|
*/
|
|
void(float ttype) monster_teleport =
|
|
{
|
|
//teleport based on ttype
|
|
if (ttype == 0)
|
|
{
|
|
//check to see if we can teleport to his left
|
|
if (monster_check_teleport(0) == 1)
|
|
{
|
|
//its time to teleport
|
|
spawn_tfog (self.origin); //spawn tfog at our old position
|
|
//change our origin to our new position
|
|
setorigin (self, self.origin - (v_right * 80));
|
|
spawn_tfog (self.origin); //spawn tfog at our new position
|
|
}
|
|
}
|
|
else if (ttype == 1)
|
|
{
|
|
//check to see if we can teleport to his right
|
|
if (monster_check_teleport(1) == 1)
|
|
{
|
|
//its time to teleport
|
|
spawn_tfog (self.origin); //spawn tfog at our old position
|
|
//change our origin to our new position
|
|
setorigin (self, self.origin + (v_right * 80));
|
|
spawn_tfog (self.origin); //spawn tfog at our new position
|
|
}
|
|
}
|
|
else if (ttype == 2)
|
|
{
|
|
//check to see if we can teleport behind his self.enemy
|
|
if (monster_check_teleport(2) == 1)
|
|
{
|
|
//its time to teleport
|
|
spawn_tfog (self.origin); //spawn tfog at our old position
|
|
//change our origin to our new position
|
|
setorigin (self, self.enemy.origin + (v_forward * 80));
|
|
spawn_tfog (self.origin); //spawn tfog at our new position
|
|
}
|
|
}
|
|
//for these last 4 types don't check if possible to teleport, just do it ;)
|
|
else if (ttype == 3)
|
|
setorigin (self, self.origin + (v_forward * 2)); //teleport 2 units forward
|
|
else if (ttype == 4)
|
|
setorigin (self, self.origin - (v_forward * 2)); //teleport 2 units backward
|
|
else if (ttype == 5)
|
|
setorigin (self, self.origin - (v_right * 2)); //teleport 2 units left
|
|
else if (ttype == 6)
|
|
setorigin (self, self.origin + (v_right * 2)); //teleport 2 units right
|
|
};
|
|
|
|
/*
|
|
====================
|
|
monster_check_stuck
|
|
|
|
Check in direction "d" a maximum of "max" units to see if we are stuck
|
|
Return 0 if not stuck
|
|
Return 1 if stuck
|
|
|
|
float d: determines which direction to check in
|
|
0: check infront
|
|
1: check behind
|
|
2: check left
|
|
3: check right
|
|
|
|
float max: the max distance to check in "d" direction
|
|
max also must be an even number divisible by 2.
|
|
====================
|
|
*/
|
|
float(float d, float max) monster_check_stuck =
|
|
{
|
|
local vector start, end;
|
|
local float x;
|
|
|
|
//initialize x
|
|
x = 2;
|
|
|
|
//set up angles stuff
|
|
self.v_angle = self.angles;
|
|
makevectors (self.angles);
|
|
|
|
//starting point
|
|
start = self.origin;
|
|
|
|
//check based on direction
|
|
if (d == 0) //check infront
|
|
{
|
|
end = start + (v_forward * x); //set initial end point
|
|
|
|
//keep checking until we hit something or reach max
|
|
while (x <= max)
|
|
{
|
|
traceline (start, end, TRUE, self);
|
|
// If there is a wall or obstacle then return STUCK
|
|
if (trace_fraction != 1.0)
|
|
return (1); //STUCK
|
|
//haven't found anything yet so increment x and reset end
|
|
x = x + 2;
|
|
end = start + (v_forward * x);
|
|
}
|
|
}
|
|
else if (d == 1) //check behind
|
|
{
|
|
end = start - (v_forward * x);
|
|
|
|
//keep checking until we hit something or reach max
|
|
while (x <= max)
|
|
{
|
|
traceline (start, end, TRUE, self);
|
|
// If there is a wall or obstacle then return STUCK
|
|
if (trace_fraction != 1.0)
|
|
return (1); //STUCK
|
|
//haven't found anything yet so increment x and reset end
|
|
x = x + 2;
|
|
end = start - (v_forward * x);
|
|
}
|
|
}
|
|
else if (d == 2) //check left
|
|
{
|
|
end = start - (v_right * x);
|
|
|
|
//keep checking until we hit something or reach max
|
|
while (x <= max)
|
|
{
|
|
traceline (start, end, TRUE, self);
|
|
// If there is a wall or obstacle then return STUCK
|
|
if (trace_fraction != 1.0)
|
|
return (1); //STUCK
|
|
//haven't found anything yet so increment x and reset end
|
|
x = x + 2;
|
|
end = start - (v_right * x);
|
|
}
|
|
}
|
|
else if (d == 3) //check right
|
|
{
|
|
end = start + (v_right * max);
|
|
|
|
//keep checking until we hit something or reach max
|
|
while (x <= max)
|
|
{
|
|
traceline (start, end, TRUE, self);
|
|
// If there is a wall or obstacle then return STUCK
|
|
if (trace_fraction != 1.0)
|
|
return (1); //STUCK
|
|
//haven't found anything yet so increment x and reset end
|
|
x = x + 2;
|
|
end = start + (v_right * max);
|
|
}
|
|
}
|
|
|
|
return (0); //NOTSTUCK
|
|
};
|
|
|
|
/*
|
|
====================
|
|
monster_stuck
|
|
|
|
Because of changes in size of monsters in ew compared to their quake counterparts
|
|
some of the monsters may be stuck in walls when playing on old quake levels.
|
|
This function is designed to check if the monster is stuck, and if he is then try to
|
|
unstick him.
|
|
|
|
float d: determines which direction to check in
|
|
0: check infront
|
|
1: check behind
|
|
2: check left
|
|
3: check right
|
|
|
|
float max: the maximum distance to check in "d" direction
|
|
max also must be an even number divisible by 2.
|
|
|
|
note: d and max are used for monster_check_stuck function above, and was put here for
|
|
reference purposes.
|
|
====================
|
|
*/
|
|
void() monster_stuck =
|
|
{
|
|
local float max;
|
|
|
|
max = 40;
|
|
|
|
//check in 4 different directions and if we are stuck in any one of those directions then
|
|
//teleport quietly in the opposite direction
|
|
while (monster_check_stuck(0,max) == 1) //check infront max units
|
|
monster_teleport(4); //teleport backwards quietly in slow increments
|
|
while (monster_check_stuck(1,max) == 1) //check behind max units
|
|
monster_teleport(3); //teleport forwards quietly in slow increments
|
|
while (monster_check_stuck(2,max) == 1) //check left max units
|
|
monster_teleport(6); //teleport right quietly in slow increments
|
|
while (monster_check_stuck(3,max) == 1) //check right max units
|
|
monster_teleport(5); //teleport left quietly in slow increments
|
|
};
|
|
|
|
void() th_stuck =
|
|
{
|
|
monster_stuck();
|
|
self.think = self.th_stand;
|
|
self.nextthink = time + 0.1 + random();
|
|
};
|
|
|
|
/*
|
|
=============================================================
|
|
Liquid Checking Code Below
|
|
=============================================================
|
|
*/
|
|
|
|
void() check_for_liquid =
|
|
{
|
|
local float p, dweller;
|
|
local entity player;
|
|
|
|
//initialize dweller
|
|
dweller = 0;
|
|
|
|
//find the player
|
|
if (self.monflag == "TRUE")
|
|
player = find(world, classname, "player");
|
|
|
|
//little hack for bots
|
|
if (self.classname == "xsniperbot")
|
|
player = find(world, monflag, "TRUE");
|
|
|
|
//check for water, slime, or lava
|
|
|
|
makevectors(self.angles);
|
|
p = pointcontents(self.origin + v_forward*16);
|
|
if (p != CONTENT_WATER && p != CONTENT_SLIME && p != CONTENT_LAVA)
|
|
{
|
|
self.air_finished = time + 5;
|
|
return;
|
|
}
|
|
|
|
if (p == CONTENT_WATER && time > self.air_finished)
|
|
{
|
|
//check for dwellers
|
|
if (self.classname == "monster_dweller")
|
|
dweller = 1;
|
|
//make the damage come from the player
|
|
T_Damage (self, player, player, 5);
|
|
self.pain_finished = time + 2;
|
|
}
|
|
|
|
if (p == CONTENT_SLIME)
|
|
{
|
|
//check for dwellers
|
|
if (self.classname == "monster_dweller")
|
|
dweller = 1;
|
|
T_Damage (self, player, player, 10);
|
|
//self.pain_finished = time + 1;
|
|
}
|
|
if (p == CONTENT_LAVA)
|
|
{
|
|
//check for dwellers
|
|
if (self.classname == "monster_dweller")
|
|
dweller = 1;
|
|
T_Damage (self, player, player, 20);
|
|
//self.pain_finished = time + 0.5;
|
|
}
|
|
|
|
//kill dwellers instantly
|
|
if (dweller == 1)
|
|
T_Damage (self, player, player, 50000);
|
|
};
|
|
|
|
/*
|
|
=============================================================
|
|
Dodging Code Below
|
|
=============================================================
|
|
*/
|
|
|
|
/*
|
|
=================
|
|
ai_strafe
|
|
|
|
monster sidesteps to avoid enemy fire
|
|
=================
|
|
*/
|
|
void(float dist) ai_strafe =
|
|
{
|
|
local float ofs;
|
|
|
|
// this is a cool strafing routine
|
|
if (self.lefty)
|
|
ofs = 90;
|
|
else
|
|
ofs = -90;
|
|
|
|
if (walkmove (self.angles_y + ofs, dist))
|
|
{
|
|
if (ofs == -90)
|
|
{
|
|
makevectors(self.angles);
|
|
self.flags = self.flags - (self.flags & FL_ONGROUND);
|
|
self.velocity = v_right * self.speed;
|
|
}
|
|
else
|
|
{
|
|
makevectors(self.angles);
|
|
self.flags = self.flags - (self.flags & FL_ONGROUND);
|
|
self.velocity = v_right * self.speed * -1;
|
|
}
|
|
return;
|
|
}
|
|
|
|
self.lefty = 1 - self.lefty;
|
|
walkmove (self.angles_y - ofs, dist);
|
|
|
|
if (ofs == -90)
|
|
{
|
|
makevectors(self.angles);
|
|
self.flags = self.flags - (self.flags & FL_ONGROUND);
|
|
self.velocity = v_right * self.speed;
|
|
}
|
|
else
|
|
{
|
|
makevectors(self.angles);
|
|
self.flags = self.flags - (self.flags & FL_ONGROUND);
|
|
self.velocity = v_right * self.speed * -1;
|
|
}
|
|
};
|
|
|
|
// ----------------------
|
|
void(float dist) ai_left =
|
|
// ----------------------
|
|
{
|
|
walkmove(self.angles_y + 90, dist);
|
|
makevectors(self.angles);
|
|
self.flags = self.flags - (self.flags & FL_ONGROUND);
|
|
self.velocity = v_right * self.speed * -1;
|
|
};
|
|
|
|
// ----------------------
|
|
void(float dist) ai_right =
|
|
// ----------------------
|
|
{
|
|
walkmove(self.angles_y - 90, dist);
|
|
makevectors(self.angles);
|
|
self.flags = self.flags - (self.flags & FL_ONGROUND);
|
|
self.velocity = v_right * self.speed;
|
|
};
|
|
|
|
void(float dist) ai_back_strafe =
|
|
{
|
|
//back up a little
|
|
ai_back(dist);
|
|
|
|
//go either left or right
|
|
if (random() <= 0.5)
|
|
ai_left(dist); //go left
|
|
else
|
|
ai_right(dist); //go right
|
|
|
|
//back up a little more
|
|
ai_back(dist);
|
|
};
|
|
|
|
void(float dist) ai_forward_strafe =
|
|
{
|
|
//move forward a little
|
|
ai_forward(dist);
|
|
|
|
//go either left or right
|
|
if (random() <= 0.5)
|
|
ai_left(dist); //go left
|
|
else
|
|
ai_right(dist); //go right
|
|
|
|
//move forward a little more
|
|
ai_forward(dist);
|
|
};
|
|
|
|
void() ai_teleport_strafe =
|
|
{
|
|
//pick random way to teleport
|
|
if (random() <= 0.5)
|
|
monster_teleport(0); //teleport left
|
|
else
|
|
monster_teleport(1); //teleport right
|
|
|
|
monster_stuck(); //see if I'm stuck
|
|
};
|
|
|
|
/*
|
|
====================
|
|
ai_dodge
|
|
|
|
Dodge attacks if being shot at.
|
|
|
|
float dist: variable controlling distance to move
|
|
|
|
.float dtype: variable controlling how to dodge
|
|
each monster has his own .dtype
|
|
0: sidestep left and right
|
|
1: back up and to the left or right
|
|
2: move forward and to the left or right
|
|
3: teleport left and right
|
|
4: do nothing
|
|
====================
|
|
*/
|
|
void(float dist) ai_dodge =
|
|
{
|
|
local float r;
|
|
local float d;
|
|
|
|
//calculate d
|
|
d = dist + 10 + (random() * 10);
|
|
|
|
//calculate r based on skill level
|
|
if (cvar("skill") == 0)
|
|
r = 0.2;
|
|
else if (cvar("skill") == 1)
|
|
r = 0.4;
|
|
else if (cvar("skill") == 2)
|
|
r = 0.6;
|
|
else if (cvar("skill") == 3)
|
|
r = 0.9;
|
|
|
|
//Chances to dodge an attack are based off of the skill level
|
|
if (time < self.enemy.attack_finished && random() <= r && visible(self.enemy))
|
|
{
|
|
//now to dodge based on dtype
|
|
if (self.dtype == 0) //sidestep left and right
|
|
ai_strafe(d);
|
|
else if (self.dtype == 1) //back up and then sidestep
|
|
ai_back_strafe(d);
|
|
else if (self.dtype == 2) //move forward and then sidestep
|
|
ai_forward_strafe(d);
|
|
else if (self.dtype == 3) //teleport to the left or right
|
|
ai_teleport_strafe();
|
|
else if (self.dtype == 4) //don't dodge at all
|
|
return;
|
|
}
|
|
|
|
//If we are the boss monster then our dodge type will be 5
|
|
//So lets flip backwards, alter time, and teleport behind the player
|
|
if (time < self.enemy.attack_finished && random() <= 0.7 && infront(self.enemy) && self.dtype == 5)
|
|
bossf1_flipb1();
|
|
};
|
|
|
|
/*
|
|
=============================================================
|
|
Summoning Code Below
|
|
=============================================================
|
|
*/
|
|
void() ai_summon =
|
|
{
|
|
if (self.classname == "monster_bossf1" || self.classname == "monster_shadowclone")
|
|
monster_stuck();
|
|
|
|
//if i'm not supposed to be here then kick me out
|
|
if (self.classname != "monster_bossf1")
|
|
return;
|
|
|
|
//random chance of summoning
|
|
if (random() <= 0.4)
|
|
{
|
|
//random chance of attacking while summoning
|
|
if (random() <= 0.2)
|
|
{
|
|
//attack while summoning
|
|
|
|
//apprentice skull blast attack
|
|
ai_face();
|
|
Apprent_StartMissile(1);
|
|
|
|
//lightning attack
|
|
sound (self, CHAN_WEAPON, "weapons/holy/lightarc.wav", 1, ATTN_NORM);
|
|
CastLightning();
|
|
|
|
//summon shadow clones
|
|
bossf1_atakd1();
|
|
}
|
|
else
|
|
bossf1_atakd1(); //summon shadow clones
|
|
}
|
|
};
|
|
|
|
/*
|
|
=============================================================
|
|
Boss Level Teleport Code Below
|
|
=============================================================
|
|
*/
|
|
|
|
void() reset_telespot =
|
|
{
|
|
local entity telespot;
|
|
|
|
//search for info_teleport_destination 's
|
|
telespot = find(world, classname, "info_teleport_destination");
|
|
|
|
while(telespot)
|
|
{
|
|
if (telespot.active == 1)
|
|
telespot.active = 0; //deactivate the teleporter
|
|
|
|
telespot = telespot.chain;
|
|
}
|
|
};
|
|
|
|
void(float spot) boss_teleport =
|
|
{
|
|
local entity telespot, player;
|
|
local float r;
|
|
local float failed;
|
|
|
|
if (spot == 0) //teleport to random spot in arena
|
|
{
|
|
|
|
//set up random variable
|
|
r = random();
|
|
|
|
//pick spot to teleport to at random
|
|
if (r <= 0.2) //teleport northarena
|
|
{
|
|
telespot = find(world, targetname, "northarena");
|
|
if (telespot.active == 0)
|
|
{
|
|
//activate this teleporter
|
|
telespot.active = 1;
|
|
|
|
//its time to teleport
|
|
spawn_tfog (self.origin); //spawn tfog at our old position
|
|
//change our origin to our new position
|
|
setorigin (self, telespot.origin);
|
|
spawn_tfog (self.origin); //spawn tfog at our new position
|
|
spawn_tdeath (self.origin, self); //spawn tdeath at our new position
|
|
}
|
|
else
|
|
failed = 1;
|
|
}
|
|
else if (r <= 0.4)
|
|
{
|
|
telespot = find(world, targetname, "southarena");
|
|
if (telespot.active == 0)
|
|
{
|
|
//activate this teleporter
|
|
telespot.active = 1;
|
|
|
|
//its time to teleport
|
|
spawn_tfog (self.origin); //spawn tfog at our old position
|
|
//change our origin to our new position
|
|
setorigin (self, telespot.origin);
|
|
spawn_tfog (self.origin); //spawn tfog at our new position
|
|
spawn_tdeath (self.origin, self); //spawn tdeath at our new position
|
|
}
|
|
else
|
|
failed = 1;
|
|
}
|
|
else if (r <= 0.6)
|
|
{
|
|
telespot = find(world, targetname, "eastarena");
|
|
if (telespot.active == 0)
|
|
{
|
|
//activate this teleporter
|
|
telespot.active = 1;
|
|
|
|
//its time to teleport
|
|
spawn_tfog (self.origin); //spawn tfog at our old position
|
|
//change our origin to our new position
|
|
setorigin (self, telespot.origin);
|
|
spawn_tfog (self.origin); //spawn tfog at our new position
|
|
spawn_tdeath (self.origin, self); //spawn tdeath at our new position
|
|
}
|
|
else
|
|
failed = 1;
|
|
}
|
|
else if (r <= 0.8)
|
|
{
|
|
telespot = find(world, targetname, "westarena");
|
|
if (telespot.active == 0)
|
|
{
|
|
//activate this teleporter
|
|
telespot.active = 1;
|
|
|
|
//its time to teleport
|
|
spawn_tfog (self.origin); //spawn tfog at our old position
|
|
//change our origin to our new position
|
|
setorigin (self, telespot.origin);
|
|
spawn_tfog (self.origin); //spawn tfog at our new position
|
|
spawn_tdeath (self.origin, self); //spawn tdeath at our new position
|
|
}
|
|
else
|
|
failed = 1;
|
|
}
|
|
else
|
|
{
|
|
telespot = find(world, targetname, "centerarena");
|
|
if (telespot.active == 0)
|
|
{
|
|
//activate this teleporter
|
|
telespot.active = 1;
|
|
|
|
//its time to teleport
|
|
spawn_tfog (self.origin); //spawn tfog at our old position
|
|
//change our origin to our new position
|
|
setorigin (self, telespot.origin);
|
|
spawn_tfog (self.origin); //spawn tfog at our new position
|
|
spawn_tdeath (self.origin, self); //spawn tdeath at our new position
|
|
}
|
|
else
|
|
failed = 1;
|
|
}
|
|
}
|
|
else if (spot == 1) //teleport to center arena
|
|
{
|
|
telespot = find(world, targetname, "centerarena");
|
|
|
|
//its time to teleport
|
|
spawn_tfog (self.origin); //spawn tfog at our old position
|
|
//change our origin to our new position
|
|
setorigin (self, telespot.origin);
|
|
spawn_tfog (self.origin); //spawn tfog at our new position
|
|
spawn_tdeath (self.origin, self); //spawn tdeath at our new position
|
|
}
|
|
else if (spot == 2) //teleport to bossdeathview
|
|
{
|
|
//Find the player
|
|
player = find(world, classname, "player");
|
|
|
|
//Find the spot
|
|
telespot = find(world, targetname, "bossdeathview");
|
|
|
|
//its time to teleport
|
|
spawn_tfog (player.origin); //spawn tfog at our old position
|
|
//change our origin to our new position
|
|
setorigin (player, telespot.origin);
|
|
spawn_tfog (player.origin); //spawn tfog at our new position
|
|
spawn_tdeath (player.origin, player); //spawn tdeath at our new position
|
|
player.angles = telespot.mangle;
|
|
//player.angles = '0 180 0';
|
|
bprint(vtos(player.angles));
|
|
bprint("\n");
|
|
player.fixangle = 1;
|
|
}
|
|
|
|
//if we get here then that means all the teleporters were active
|
|
//so lets teleport behind the player and then reset all the teleporters
|
|
if (failed == 1)
|
|
{
|
|
//teleport behind the player
|
|
monster_teleport(2);
|
|
|
|
//reset teleporters to inactive state
|
|
reset_telespot();
|
|
}
|
|
};
|
|
|
|
/*
|
|
=============
|
|
ai_face_player
|
|
|
|
Find the player and turn towards him
|
|
=============
|
|
*/
|
|
void() ai_face_player =
|
|
{
|
|
local entity player;
|
|
|
|
//search for the player
|
|
player = find(world, classname, "player");
|
|
|
|
//turn towards the player
|
|
self.ideal_yaw = vectoyaw(player.origin - self.origin);
|
|
ChangeYaw ();
|
|
};
|
|
|
|
/*
|
|
=============================================================
|
|
Roaming Code Below
|
|
|
|
This code is only used in Flood Co-op, at all other times
|
|
monsters use default movement code.
|
|
=============================================================
|
|
*/
|
|
|
|
void() monster_regen_item =
|
|
{
|
|
//unflag our owner
|
|
self.owner.count = 0;
|
|
|
|
//remove ourself
|
|
remove(self);
|
|
};
|
|
|
|
/*
|
|
================================
|
|
search_for_bots
|
|
|
|
Monsters will search for bots to attack.
|
|
================================
|
|
*/
|
|
void() search_for_bots =
|
|
{
|
|
local entity found, foe;
|
|
|
|
// bots aren't clients, so we have to check fo them manually
|
|
// we just see if any of the bots in the entity list are visible
|
|
|
|
if (self.enemy)
|
|
return;
|
|
|
|
found = world;
|
|
foe = find(world, classname, "xsniperbot");
|
|
|
|
while(foe)
|
|
{
|
|
if (visible(foe) && foe.health > 0)
|
|
found = foe;
|
|
foe = find(foe, classname, "xsniperbot");
|
|
}
|
|
|
|
if (found != world)
|
|
{
|
|
self.enemy = found;
|
|
self.goalentity = found;
|
|
self.think = self.th_run;
|
|
self.nextthink = time + 0.1;
|
|
}
|
|
};
|
|
|
|
/*
|
|
=================================
|
|
monster_search_for_items
|
|
|
|
Have the monster look around for items.
|
|
This is one way to get him moving around a level.
|
|
=================================
|
|
*/
|
|
void() monster_search_for_items =
|
|
{
|
|
local entity item;
|
|
|
|
if (time > self.search_time)
|
|
self.goalentity = world;
|
|
|
|
if (self.goalentity)
|
|
return;
|
|
|
|
item = findradius(self.origin, 1500);
|
|
|
|
while(item)
|
|
{
|
|
if ( (item.flags & FL_ITEM) && visible(item) && item.count != 1)
|
|
{
|
|
self.goalentity = item;
|
|
self.search_time = time + 30;
|
|
}
|
|
item = item.chain;
|
|
}
|
|
};
|
|
|
|
/*
|
|
===========================
|
|
monster_grab_items
|
|
|
|
When monsters reach an item
|
|
they will grab it, or atleast
|
|
act like they are grabbing it.
|
|
===========================
|
|
*/
|
|
void() monster_grab_items =
|
|
{
|
|
local entity monster_regen;
|
|
|
|
if (self.goalentity == world)
|
|
return;
|
|
|
|
if (vlen(self.origin - self.goalentity.origin) <= 70)
|
|
{
|
|
//bring regeneration entity into world
|
|
monster_regen = spawn();
|
|
|
|
//put it where self.goalentity is
|
|
setorigin(monster_regen, self.goalentity.origin);
|
|
|
|
//make our owner be the goalentity
|
|
monster_regen.owner = self.goalentity;
|
|
|
|
//other stuff
|
|
monster_regen.solid = SOLID_NOT;
|
|
setmodel(monster_regen,"");
|
|
|
|
//flag the goalentity so we don't try to pick it up anymore
|
|
self.goalentity.count = 1;
|
|
|
|
//set up our thoughts
|
|
monster_regen.nextthink = time + 20;
|
|
monster_regen.think = monster_regen_item;
|
|
|
|
self.goalentity = world;
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|