ew-progs/ew/monsters/ewai.qc
2011-09-06 00:00:00 +00:00

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