mirror of
https://git.code.sf.net/p/quake/game-source
synced 2024-11-22 20:11:49 +00:00
458 lines
9 KiB
C++
458 lines
9 KiB
C++
/***********************************************
|
|
* *
|
|
* FrikBot Fight Code *
|
|
* "Because I ain't no Ghandi code" *
|
|
* *
|
|
***********************************************/
|
|
|
|
/*
|
|
|
|
This program is in the Public Domain. My crack legal
|
|
team would like to add:
|
|
|
|
RYAN "FRIKAC" SMITH IS PROVIDING THIS SOFTWARE "AS IS"
|
|
AND MAKES NO WARRANTY, EXPRESS OR IMPLIED, AS TO THE
|
|
ACCURACY, CAPABILITY, EFFICIENCY, MERCHANTABILITY, OR
|
|
FUNCTIONING OF THIS SOFTWARE AND/OR DOCUMENTATION. IN
|
|
NO EVENT WILL RYAN "FRIKAC" SMITH BE LIABLE FOR ANY
|
|
GENERAL, CONSEQUENTIAL, INDIRECT, INCIDENTAL,
|
|
EXEMPLARY, OR SPECIAL DAMAGES, EVEN IF RYAN "FRIKAC"
|
|
SMITH HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
|
DAMAGES, IRRESPECTIVE OF THE CAUSE OF SUCH DAMAGES.
|
|
|
|
You accept this software on the condition that you
|
|
indemnify and hold harmless Ryan "FrikaC" Smith from
|
|
any and all liability or damages to third parties,
|
|
including attorney fees, court costs, and other
|
|
related costs and expenses, arising out of your use
|
|
of this software irrespective of the cause of said
|
|
liability.
|
|
|
|
The export from the United States or the subsequent
|
|
reexport of this software is subject to compliance
|
|
with United States export control and munitions
|
|
control restrictions. You agree that in the event you
|
|
seek to export this software, you assume full
|
|
responsibility for obtaining all necessary export
|
|
licenses and approvals and for assuring compliance
|
|
with applicable reexport restrictions.
|
|
|
|
Any reproduction of this software must contain
|
|
this notice in its entirety.
|
|
|
|
*/
|
|
|
|
.entity avoid;
|
|
|
|
float(entity e) bot_size_player =
|
|
{
|
|
local float sz;
|
|
|
|
sz = e.health + e.armorvalue * e.armortype;
|
|
if (e.weapon == 32)
|
|
sz = sz + 60;
|
|
else if (e.weapon == 64)
|
|
sz = sz + 60;
|
|
else if (e.weapon == 16)
|
|
sz = sz + 50;
|
|
else if (e.weapon == 8)
|
|
sz = sz + 50;
|
|
else if (e.weapon == 4)
|
|
sz = sz + 40;
|
|
else if (e.weapon == 2)
|
|
sz = sz + 40;
|
|
else if (e.weapon == 1)
|
|
sz = sz + 10;
|
|
else if (e.weapon == 4096)
|
|
sz = sz - 50;
|
|
if (e.items & 4194304) // Quad
|
|
sz = sz + 200;
|
|
if (e.items & 1048576) // Invul
|
|
sz = sz + 300;
|
|
if (e.items & 524288) // Invis
|
|
sz = sz + 250;
|
|
return sz;
|
|
};
|
|
|
|
void() bot_dodge_stuff =
|
|
{
|
|
local entity foe;
|
|
local float foedist, avdist, scandist = 0, foesz, flen, tsz;
|
|
local vector v;
|
|
|
|
if (waypoint_mode > WM_LOADED)
|
|
return;
|
|
|
|
self.avoid = world;
|
|
|
|
|
|
if (self.enemy)
|
|
{
|
|
v = self.origin - realorigin(self.enemy);
|
|
foedist = vlen(v);
|
|
foesz = bot_size_player(self.enemy);
|
|
}
|
|
else
|
|
{
|
|
foedist = 3000;
|
|
foesz = 9999999;
|
|
}
|
|
avdist = 256;
|
|
|
|
foe = find(world, classname, "grenade");
|
|
while(foe)
|
|
{
|
|
flen = vlen(foe.origin - self.origin);
|
|
if (flen < avdist)
|
|
{
|
|
avdist = flen;
|
|
self.avoid = foe;
|
|
}
|
|
foe = find(foe, classname, "grenade");
|
|
}
|
|
if (!self.avoid)
|
|
{
|
|
foe = find(world, classname, "missile");
|
|
while(foe)
|
|
{
|
|
if (foe.owner != self)
|
|
{
|
|
flen = vlen(foe.origin - self.origin);
|
|
if (flen < avdist)
|
|
{
|
|
avdist = flen;
|
|
self.avoid = foe;
|
|
}
|
|
}
|
|
foe = find(foe, classname, "missile");
|
|
}
|
|
if (!self.avoid)
|
|
{
|
|
foe = find(world, classname, "spike");
|
|
while(foe)
|
|
{
|
|
if (foe.owner != self)
|
|
{
|
|
flen = vlen(foe.origin - self.origin);
|
|
if (flen < avdist)
|
|
{
|
|
avdist = flen;
|
|
self.avoid = foe;
|
|
}
|
|
}
|
|
foe = find(foe, classname, "spike");
|
|
}
|
|
}
|
|
}
|
|
if (coop)
|
|
{
|
|
if (!self.enemy)
|
|
{
|
|
foe = findradius(self.origin, foedist);
|
|
while(foe)
|
|
{
|
|
if(foe.flags & FL_MONSTER)
|
|
{
|
|
if(foe.health > 0)
|
|
{
|
|
flen = vlen(foe.origin - self.origin);
|
|
if (flen < foedist)
|
|
{
|
|
tsz = bot_size_player(foe);
|
|
if (tsz < foesz)
|
|
{
|
|
if (fisible(foe))
|
|
{
|
|
self.enemy = foe;
|
|
foedist = flen;
|
|
foesz = tsz;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
foe = foe.chain;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
foe = player_head;
|
|
while(foe)
|
|
{
|
|
if(foe != self)
|
|
{
|
|
if (foe.modelindex != 0)
|
|
{
|
|
if (foe.health > 0)
|
|
{
|
|
if (!(teamplay && self.team == foe.team))
|
|
{
|
|
flen = vlen(foe.origin - self.origin);
|
|
if (flen < foedist)
|
|
{
|
|
tsz = bot_size_player(foe);
|
|
if (tsz < foesz)
|
|
{
|
|
if (fov(foe) || foe.b_sound > time || self.b_skill == 3)
|
|
{
|
|
if (fisible(foe))
|
|
{
|
|
self.enemy = foe;
|
|
foedist = vlen(foe.origin - self.origin);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
foe = foe._next;
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
/*
|
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
|
|
|
weapon_range
|
|
|
|
_x "sweet spot range" - try to maintain this range if possible
|
|
_y minimum range bot can be to be effective (rl/gl) (move away)
|
|
_z maximum range bot can be to be effective (lg/axe) (move in)
|
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
|
*/
|
|
|
|
vector(float wep) weapon_range =
|
|
{
|
|
if (wep == 4096) // IT_AXE
|
|
return '48 0 64';
|
|
else if (wep == 1) // IT_SHOTGUN
|
|
return '128 0 99999';
|
|
else if (wep == 2) // IT_SUPER_SHOTGUN
|
|
return '128 0 99999';
|
|
else if (wep == 4) // IT_NAILGUN
|
|
return '180 0 3000';
|
|
else if (wep == 8) // IT_SUPER_NAILGUN
|
|
return '180 0 3000';
|
|
else if (wep == 16) // IT_GRENADE_LAUNCHER
|
|
return '180 48 3000';
|
|
else if (wep == 32) // IT_ROCKET_LAUNCHER
|
|
return '180 48 3000';
|
|
else if (wep == 64) // IT_LIGHTNING
|
|
return '350 0 512';
|
|
};
|
|
/*
|
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
|
|
|
bot_weapon_switch
|
|
|
|
Pick a weapon based on range / ammo
|
|
|
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
|
*/
|
|
|
|
void(float brange) bot_weapon_switch =
|
|
{
|
|
local float it, flag = 0, pulse = 0;
|
|
local vector v;
|
|
|
|
it = self.items & 127;
|
|
|
|
while(it)
|
|
{
|
|
if ((self.ammo_rockets >= 1) && (it & 32))
|
|
{
|
|
flag = 32;
|
|
pulse = 7;
|
|
}
|
|
else if (self.waterlevel <= 1 && self.ammo_cells >= 1 && (it & 64))
|
|
{
|
|
flag = 64;
|
|
pulse = 8;
|
|
}
|
|
else if(self.ammo_nails >= 2 && (it & 8))
|
|
{
|
|
flag = 8;
|
|
pulse = 5;
|
|
}
|
|
else if ((self.ammo_rockets >= 1) && (it & 16))
|
|
{
|
|
flag = 16;
|
|
pulse = 6;
|
|
}
|
|
else if(self.ammo_shells >= 2 && (it & 2))
|
|
{
|
|
flag = 2;
|
|
pulse = 3;
|
|
}
|
|
else if(self.ammo_nails >= 1 && (it & 4))
|
|
{
|
|
flag = 4;
|
|
pulse = 4;
|
|
}
|
|
else if(self.ammo_shells >= 1 && (it & 1))
|
|
{
|
|
flag = 1;
|
|
pulse = 2;
|
|
}
|
|
else
|
|
{
|
|
if (pulse)
|
|
self.impulse = pulse;
|
|
return;
|
|
}
|
|
|
|
if (brange == -1)
|
|
{
|
|
if (pulse)
|
|
self.impulse = pulse;
|
|
return;
|
|
}
|
|
|
|
v = weapon_range(flag);
|
|
if (brange < v_y || brange > v_z)
|
|
it = it - flag;
|
|
else
|
|
{
|
|
if (pulse)
|
|
self.impulse = pulse;
|
|
return;
|
|
}
|
|
}
|
|
};
|
|
|
|
void() bot_shoot =
|
|
{
|
|
// quick little function to stop making him shoot the wrong way ! Argh
|
|
local float g;
|
|
g = angcomp(self.v_angle_x, self.b_angle_x);
|
|
if (fabs(g) > 30)
|
|
return; // argh, too far away
|
|
g = angcomp(self.v_angle_y, self.b_angle_y);
|
|
if (fabs(g) > 30)
|
|
return; // not again!
|
|
self.button0 = TRUE;
|
|
};
|
|
|
|
/*
|
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
|
|
|
Bot_fight_style
|
|
|
|
This is the core of the bot's thinking when
|
|
attacking an enemy.
|
|
|
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
|
*/
|
|
|
|
void() bot_fight_style =
|
|
{
|
|
local vector v, v1 = '0 0 0', v2 = '0 0 0', org;
|
|
local float foedist, mysz, foesz;
|
|
|
|
|
|
if (self.enemy.health <= 0)
|
|
{
|
|
self.enemy = world;
|
|
return;
|
|
}
|
|
else if (!self.enemy.takedamage)
|
|
{
|
|
self.enemy = world;
|
|
return;
|
|
}
|
|
else if (!fisible(self.enemy))
|
|
{
|
|
self.enemy = world;
|
|
return;
|
|
}
|
|
|
|
org = realorigin(self.enemy);
|
|
makevectors(self.v_angle);
|
|
|
|
// decide if I should shoot
|
|
|
|
foedist = vlen(org - self.origin);
|
|
v = weapon_range(self.weapon);
|
|
if (foedist > v_y && foedist < v_z)
|
|
{
|
|
traceline(self.origin + self.view_ofs, self.origin + self.view_ofs + v_forward * v_z, FALSE, self);
|
|
if (vlen(trace_endpos - (self.origin + self.view_ofs)) >= v_y)
|
|
{
|
|
// try to avoid shooting teammates
|
|
if (trace_ent.classname == "player")
|
|
if ((trace_ent.team == self.team && teamplay) || (coop))
|
|
return;
|
|
bot_shoot();
|
|
}
|
|
}
|
|
else
|
|
bot_weapon_switch(foedist);
|
|
|
|
if (!(self.b_aiflags & (AI_PRECISION | AI_BLIND | AI_OBSTRUCTED)))
|
|
{
|
|
foesz = bot_size_player(self.enemy);
|
|
mysz = bot_size_player(self) + 5;
|
|
|
|
if (foesz > mysz)
|
|
{
|
|
if (teamplay)
|
|
{
|
|
if (random() < 0.02)
|
|
{
|
|
bot_start_topic(5);
|
|
self.b_chattime = 1;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
else if (mysz < 140)
|
|
return;
|
|
else if (self.avoid)
|
|
{
|
|
if (self.avoid.velocity)
|
|
v = self.avoid.velocity;
|
|
else
|
|
v = normalize(self.avoid.origin - self.origin);
|
|
v1_x = v_y;
|
|
v1_y = v_y * -1;
|
|
v2_x = v_y;
|
|
v2_y = v_y * -1;
|
|
foedist = vlen(self.avoid.origin - (self.origin + v1));
|
|
if (foedist < vlen(self.avoid.origin - (self.origin + v2)))
|
|
frik_walkmove(v2);
|
|
else
|
|
frik_walkmove(v1);
|
|
}
|
|
else if (!(self.enemy.flags & FL_MONSTER))
|
|
{
|
|
if (foedist + 32 < v_x)
|
|
frik_walkmove(self.origin - org);
|
|
else if (foedist - 32 > v_x)
|
|
frik_walkmove(org - self.origin);
|
|
else if (self.wallhug)
|
|
frik_walkmove(v_right);
|
|
else
|
|
frik_walkmove(v_right * -1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
foesz = bot_size_player(self.enemy);
|
|
mysz = bot_size_player(self) + 5;
|
|
|
|
if (foesz > mysz)
|
|
return;
|
|
else if (mysz < 140)
|
|
return;
|
|
self.keys = self.keys & 960;
|
|
}
|
|
};
|
|
|
|
|