r1821 - in trunk/data: . qcsrc/server
DONOTREPLY at icculus.org
DONOTREPLY at icculus.org
Thu Aug 17 08:31:40 EDT 2006
Author: div0
Date: 2006-08-17 08:31:39 -0400 (Thu, 17 Aug 2006)
New Revision: 1821
Modified:
trunk/data/default.cfg
trunk/data/qcsrc/server/bots.qc
trunk/data/qcsrc/server/cl_player.qc
trunk/data/qcsrc/server/havocbot.qc
trunk/data/qcsrc/server/w_crylink.qc
trunk/data/qcsrc/server/w_electro.qc
trunk/data/qcsrc/server/w_grenadelauncher.qc
trunk/data/qcsrc/server/w_hagar.qc
trunk/data/qcsrc/server/w_rocketlauncher.qc
trunk/data/qcsrc/server/w_shotgun.qc
trunk/data/qcsrc/server/w_uzi.qc
Log:
Qantourisc's improved havocbots AI - have fun!
Modified: trunk/data/default.cfg
===================================================================
--- trunk/data/default.cfg 2006-08-16 18:05:54 UTC (rev 1820)
+++ trunk/data/default.cfg 2006-08-17 12:31:39 UTC (rev 1821)
@@ -133,7 +133,7 @@
seta cl_playerdetailreduction 0 // the higher, the less detailed
set bot_number 0 // number of bots in server
-set bot_usemodelnames 0 // whether bots should be named after the models
+seta bot_usemodelnames 0 // whether bots should be named after the models
set bot_nofire 0 // makes bots not attack at all, mainly for testing in g_waypointeditor mode
seta bot_prefix "[BOT]"
seta bot_suffix ""
@@ -141,10 +141,27 @@
set bot_ai_strategyinterval 2
set bot_ai_enemydetectioninterval 0.5
set bot_ai_aimskill_blendrate 2
-set bot_ai_aimskill_fixedrate 30
+set bot_ai_aimskill_fixedrate 15
set bot_ai_aimskill_firetolerance_distdegrees 180
set bot_ai_aimskill_firetolerance_mindegrees 2
-set bot_ai_aimskill_firetolerance_maxdegrees 30
+set bot_ai_aimskill_firetolerance_maxdegrees 45
+set bot_ai_aimskill_mouse 1
+set bot_ai_keyboard_distance 250
+set bot_ai_keyboard_treshold 0.94
+set bot_ai_aimskill_offset 1
+set bot_ai_aimskill_think 1
+// Beter don't touch these, there are hard to tweak!
+set bot_ai_aimskill_order_mix_1st 0.01
+set bot_ai_aimskill_order_mix_2nd 0.1
+set bot_ai_aimskill_order_mix_3th 0.01
+set bot_ai_aimskill_order_mix_4th 0.05
+set bot_ai_aimskill_order_mix_5th 0.01
+set bot_ai_aimskill_order_filter_1st 0.2
+set bot_ai_aimskill_order_filter_2nd 0.2
+set bot_ai_aimskill_order_filter_3th 0.05
+set bot_ai_aimskill_order_filter_4th 0.25
+set bot_ai_aimskill_order_filter_5th 0.3
+
// waypoint editor enable
set g_waypointeditor 0
set bot_ignore_bots 0
Modified: trunk/data/qcsrc/server/bots.qc
===================================================================
--- trunk/data/qcsrc/server/bots.qc 2006-08-16 18:05:54 UTC (rev 1820)
+++ trunk/data/qcsrc/server/bots.qc 2006-08-17 12:31:39 UTC (rev 1821)
@@ -330,7 +330,13 @@
};
+// Random skill system
+.float bot_thinkskill;
+.float bot_mouseskill;
+.float bot_predictionskill;
+.float bot_offsetskill;
+
// waypoint navigation system
// itemscore = (howmuchmoreIwant / howmuchIcanwant) / itemdistance
@@ -1441,7 +1447,15 @@
};
.float bot_badaimtime;
+.float bot_aimthinktime;
+.vector bot_mouseaim;
.vector bot_badaimoffset;
+.vector bot_1st_order_aimfilter;
+.vector bot_2nd_order_aimfilter;
+.vector bot_3th_order_aimfilter;
+.vector bot_4th_order_aimfilter;
+.vector bot_5th_order_aimfilter;
+.vector bot_olddesiredang;
float(vector v, float maxfiredeviation) bot_aimdir =
{
/*
@@ -1496,9 +1510,9 @@
if (time >= self.bot_badaimtime)
{
self.bot_badaimtime = max(self.bot_badaimtime + 0.3, time);
- self.bot_badaimoffset = randomvec() * bound(0, 5 - 0.5 * skill, 5);
+ self.bot_badaimoffset = randomvec() * bound(0, 5 - 0.5 * (skill+self.bot_offsetskill), 5);
}
- desiredang = vectoangles(v) + self.bot_badaimoffset;
+ desiredang = vectoangles(v) + self.bot_badaimoffset*cvar("bot_ai_aimskill_offset");
//dprint(" desired:", vtos(desiredang));
if (desiredang_x >= 180)
desiredang_x = desiredang_x - 360;
@@ -1515,6 +1529,64 @@
//}
// calculate turn angles
+ diffang = (desiredang - self.bot_olddesiredang)*(1/frametime);
+ self.bot_olddesiredang = desiredang;
+ //dprint(" diff:", vtos(diffang));
+
+ // wrap yaw turn
+ while (diffang_y < -180)
+ diffang_y = diffang_y + 360;
+ while (diffang_y > 180)
+ diffang_y = diffang_y - 360;
+
+ // Here we will try to anticipate the comming aiming direction
+ self.bot_1st_order_aimfilter= self.bot_1st_order_aimfilter
+ + (diffang - self.bot_1st_order_aimfilter) * bound(0, cvar("bot_ai_aimskill_order_filter_1st"),1);
+ self.bot_2nd_order_aimfilter= self.bot_2nd_order_aimfilter
+ + (self.bot_1st_order_aimfilter - self.bot_2nd_order_aimfilter) * bound(0, cvar("bot_ai_aimskill_order_filter_2nd"),1);
+ self.bot_3th_order_aimfilter= self.bot_3th_order_aimfilter
+ + (self.bot_2nd_order_aimfilter - self.bot_3th_order_aimfilter) * bound(0, cvar("bot_ai_aimskill_order_filter_3th"),1);
+ self.bot_4th_order_aimfilter= self.bot_4th_order_aimfilter
+ + (self.bot_3th_order_aimfilter - self.bot_4th_order_aimfilter) * bound(0, cvar("bot_ai_aimskill_order_filter_4th"),1);
+ self.bot_5th_order_aimfilter= self.bot_5th_order_aimfilter
+ + (self.bot_4th_order_aimfilter - self.bot_5th_order_aimfilter) * bound(0, cvar("bot_ai_aimskill_order_filter_5th"),1);
+
+ local float blend;
+ //blend = (bound(0,skill,10)*0.1)*pow(1-bound(0,skill,10)*0.05,2.5)*5.656854249; //Plot formule before changing !
+ blend = bound(0,skill,10)*0.1;
+ desiredang = desiredang + blend *
+ (
+ self.bot_1st_order_aimfilter * cvar("bot_ai_aimskill_order_mix_1st")
+ + self.bot_2nd_order_aimfilter * cvar("bot_ai_aimskill_order_mix_2nd")
+ + self.bot_3th_order_aimfilter * cvar("bot_ai_aimskill_order_mix_3th")
+ + self.bot_4th_order_aimfilter * cvar("bot_ai_aimskill_order_mix_4th")
+ + self.bot_5th_order_aimfilter * cvar("bot_ai_aimskill_order_mix_5th")
+ );
+ while (desiredang_z < -180)
+ desiredang_z = desiredang_z + 360;
+ while (desiredang_z > 180)
+ desiredang_z = desiredang_z - 360;
+ // calculate turn angles
+ diffang = desiredang - self.bot_mouseaim;
+ //dprint(" diff:", vtos(diffang));
+
+ // wrap yaw turn
+ while (diffang_y < -180)
+ diffang_y = diffang_y + 360;
+ while (diffang_y > 180)
+ diffang_y = diffang_y - 360;
+
+ if (time >= self.bot_aimthinktime)
+ {
+ self.bot_aimthinktime = max(self.bot_aimthinktime + 0.5 - 0.05*(skill+self.bot_thinkskill), time);
+ self.bot_mouseaim = self.bot_mouseaim + diffang * (1-random()*0.1*bound(1,10-skill,10));
+ }
+
+ //self.v_angle = self.v_angle + diffang * bound(0, r * frametime * (skill * 0.5 + 2), 1);
+
+ desiredang = self.bot_mouseaim*bound(0,cvar("bot_ai_aimskill_think"),1) + desiredang * bound(0,(1-cvar("bot_ai_aimskill_think")),1);
+
+ // calculate turn angles
diffang = desiredang - self.v_angle;
//dprint(" diff:", vtos(diffang));
@@ -1523,17 +1595,30 @@
diffang_y = diffang_y + 360;
while (diffang_y > 180)
diffang_y = diffang_y - 360;
+ // calculate turn angles
+ diffang = desiredang - self.v_angle;
+ //dprint(" diff:", vtos(diffang));
+ // wrap yaw turn
+ while (diffang_y < -180)
+ diffang_y = diffang_y + 360;
+ while (diffang_y > 180)
+ diffang_y = diffang_y - 360;
+
// jitter tracking
dist = vlen(diffang);
//diffang = diffang + randomvec() * (dist * 0.05 * (3.5 - bound(0, skill, 3)));
// turn
local float r, fixedrate, blendrate;
- fixedrate = cvar("bot_ai_aimskill_fixedrate") / dist;
+ fixedrate = cvar("bot_ai_aimskill_fixedrate") / bound(1,dist,1000);
blendrate = cvar("bot_ai_aimskill_blendrate");
r = max(fixedrate, blendrate);
- self.v_angle = self.v_angle + diffang * bound(0, r * frametime * (skill * 0.5 + 2), 1);
+ //self.v_angle = self.v_angle + diffang * bound(frametime, r * frametime * (2+skill*skill*0.05-random()*0.05*(10-skill)), 1);
+ self.v_angle = self.v_angle + diffang * bound(frametime, r * frametime * (2+pow(skill+self.bot_mouseskill,3)*0.005-random()), 1);
+ self.v_angle = self.v_angle * bound(0,cvar("bot_ai_aimskill_mouse"),1) + desiredang * bound(0,(1-cvar("bot_ai_aimskill_mouse")),1);
+ //self.v_angle = self.v_angle + diffang * bound(0, r * frametime * (skill * 0.5 + 2), 1);
+ //self.v_angle = self.v_angle + diffang * (1/ blendrate);
self.v_angle_z = 0;
while (self.v_angle_y < -180)
self.v_angle_y = self.v_angle_y + 360;
@@ -1544,6 +1629,7 @@
makevectors(self.v_angle);
shotorg = self.origin + self.view_ofs;
shotdir = v_forward;
+
//dprint(" dir:", vtos(v_forward));
//te_lightning2(world, shotorg, shotorg + shotdir * 100);
@@ -1558,8 +1644,13 @@
// decide whether to fire this time
// note the maxfiredeviation is in degrees so this has to convert to radians first
+ //if ((normalize(v) * shotdir) >= cos(maxfiredeviation * (3.14159265358979323846 / 180)))
if ((normalize(v) * shotdir) >= cos(maxfiredeviation * (3.14159265358979323846 / 180)))
- self.bot_firetimer = time + 0.3;
+ self.bot_firetimer = time + 0.3/bound(1,skill*0.3,3);
+ traceline(shotorg,shotorg+shotdir*1000,FALSE,world);
+ if ( vlen(trace_endpos-shotorg)>bound(0,skill*20,20) && random()<bound(0,skill*0.5,10)*frametime*2 )
+ self.bot_canfire = 0;
+ //dprint(ftos(maxfiredeviation),"\n");
//dprint(" diff:", vtos(diffang), "\n");
return self.bot_canfire && (time < self.bot_firetimer);
@@ -1567,6 +1658,7 @@
vector(vector targorigin, vector targvelocity, float shotspeed, float shotdelay) bot_shotlead =
{
+ // Try to add code here that predicts gravity effect here, no clue HOW to though ... well not yet atleast...
return targorigin + targvelocity * (shotdelay + vlen(targorigin - shotorg) / shotspeed);
};
@@ -1596,7 +1688,11 @@
shotorg = self.origin + self.view_ofs;
shotdir = v_forward;
v = bot_shotlead(self.bot_aimtargorigin, self.bot_aimtargvelocity, shotspeed, self.bot_aimlatency);
- r = bound(cvar("bot_ai_aimskill_firetolerance_mindegrees"), cvar("bot_ai_aimskill_firetolerance_distdegrees") / ((vlen(v - shotorg) + 100) * (skill + 2)), cvar("bot_ai_aimskill_firetolerance_maxdegrees"));
+ local float distanceratio;
+ distanceratio =sqrt(bound(0,skill,10000))*0.3*(vlen(v-shotorg)-100)/cvar("bot_ai_aimskill_firetolerance_distdegrees");
+ distanceratio = bound(0,distanceratio,1);
+ r = (cvar("bot_ai_aimskill_firetolerance_maxdegrees")-cvar("bot_ai_aimskill_firetolerance_mindegrees"))
+ * (1-distanceratio) + cvar("bot_ai_aimskill_firetolerance_mindegrees");
if (applygravity && self.bot_aimtarg)
{
if (!findtrajectorywithleading(shotorg, '0 0 0', '0 0 0', self.bot_aimtarg, shotspeed, shotspeedupward, maxshottime, 0, self))
@@ -1651,7 +1747,9 @@
// calculate an aiming latency based on the skill setting
// (simulated network latency + naturally delayed reflexes)
- self.ping = 0.7 - bound(0, 0.05 * skill, 0.6);
+ //self.ping = 0.7 - bound(0, 0.05 * skill, 0.5); // moved the reflexes to bot_aimdir (under the name 'think')
+ // minimum ping 20+10 random
+ self.ping = bound(0,0.07 - bound(0, skill * 0.005,0.05)+random()*0.01,0.65); // Now holds real lag to server, and higer skill players take a less laggy server
// skill 10 = ping 0.2 (adrenaline)
// skill 0 = ping 0.7 (slightly drunk)
@@ -1713,6 +1811,7 @@
void() havocbot_setupbot;
float JoinBestTeam(entity pl, float only_return_best);
+
void() bot_clientconnect =
{
if (clienttype(self) != CLIENTTYPE_BOT)
@@ -1722,6 +1821,10 @@
self.createdtime = self.nextthink;
JoinBestTeam(self, FALSE);
havocbot_setupbot();
+ self.bot_mouseskill=random()-0.5;
+ self.bot_thinkskill=random()-0.5;
+ self.bot_predictionskill=random()-0.5;
+ self.bot_offsetskill=random()-0.5;
};
entity() bot_spawn =
Modified: trunk/data/qcsrc/server/cl_player.qc
===================================================================
--- trunk/data/qcsrc/server/cl_player.qc 2006-08-16 18:05:54 UTC (rev 1820)
+++ trunk/data/qcsrc/server/cl_player.qc 2006-08-17 12:31:39 UTC (rev 1821)
@@ -221,7 +221,7 @@
// throw off bot aim temporarily
local float shake;
- shake = damage * 5 / (skill + 1);
+ shake = damage * 5 / (bound(0,skill,100) + 1);
self.v_angle_x = self.v_angle_x + (random() * 2 - 1) * shake;
self.v_angle_y = self.v_angle_y + (random() * 2 - 1) * shake;
}
Modified: trunk/data/qcsrc/server/havocbot.qc
===================================================================
--- trunk/data/qcsrc/server/havocbot.qc 2006-08-16 18:05:54 UTC (rev 1820)
+++ trunk/data/qcsrc/server/havocbot.qc 2006-08-17 12:31:39 UTC (rev 1821)
@@ -1,6 +1,7 @@
.void() havocbot_role;
void() havocbot_chooserole;
+.float havocbot_keyboardskill;
vector() havocbot_dodge =
{
@@ -51,6 +52,9 @@
};
//.float havocbotignoretime;
+.float havocbot_keyboardtime;
+.float havocbot_ducktime;
+.vector havocbot_keyboard;
void() havocbot_movetogoal =
{
local vector destorg;
@@ -91,13 +95,14 @@
flatdir = normalize(flatdir);
if (!self.waterlevel)
{
- if (!(self.flags & FL_ONGROUND))
- {
- // prevent goal checks when we can't walk
- if (self.bot_strategytime < time + 0.1)
- self.bot_strategytime = time + 0.1;
- return;
- }
+ // Since new update in air contol, we can move in air
+ //if (!(self.flags & FL_ONGROUND))
+ //{
+ // // prevent goal checks when we can't walk
+ // if (self.bot_strategytime < time + 0.1)
+ // self.bot_strategytime = time + 0.1;
+ // return;
+ //}
// jump if going toward an obstacle that doesn't look like stairs we
// can walk up directly
@@ -125,14 +130,79 @@
dir = flatdir;
}
dodge = havocbot_dodge();
- dir = normalize(dir + dodge + evadeobstacle + evadelava) * 400;
- makevectors(self.v_angle);
+ dodge = dodge * bound(0,3+skill*0.1,1);
+ evadelava = evadelava * bound(1,3-skill,3); //Noobs fear lava a lot and take more distance from it
+ traceline(self.origin, self.enemy.origin, TRUE, world);
+ if(trace_ent.classname == "player")
+ dir = dir * bound(0,skill/7,1);
+
+ dir = normalize(dir + dodge + evadeobstacle + evadelava);
+
+
+
+ // Emulate keyboard interface;
+ local vector keyboard,flatangle;
+ local float blend;
+ keyboard = self.havocbot_keyboard;
+ if (time >= self.havocbot_keyboardtime)
+ {
+ flatdir=dir; flatdir_z = 0;
+ self.havocbot_keyboardtime =
+ max(
+ self.havocbot_keyboardtime
+ + bound(0,0.05/(skill+self.havocbot_keyboardskill),0.05)
+ +random()*bound(0,0.025/(skill+self.havocbot_keyboardskill),100)
+ , time);
+ keyboard = '0 0 0';
+
+ flatangle = self.v_angle; flatangle_z=0;
+ makevectors(flatangle);
+
+ local float trigger;
+ local vector v_forward_right;
+ local vector v_forward_left;
+ blend = bound(0,skill*0.1,1);
+ trigger = cvar("bot_ai_keyboard_treshold");
+ v_forward_right = normalize(v_forward + v_right);
+ v_forward_left = normalize(v_forward - v_right);
+ // Place in reverse order !! least important direction FIRST
+
+ if (skill > 4.5)
+ {
+ if (flatdir * v_forward_right * -1 > trigger) keyboard = v_forward_right * -1;
+ if (flatdir * v_forward_left * -1 > trigger) keyboard = v_forward_left * -1;
+ }
+ if (skill > 2.5)
+ {
+ if (flatdir * v_forward_right > trigger) keyboard = v_forward_right;
+ if (flatdir * v_forward_left > trigger) keyboard = v_forward_left;
+ if (flatdir * v_forward * 1 > trigger) keyboard = v_forward * -1;
+ }
+ if (skill > 1.5)
+ {
+ if (flatdir * v_right > trigger) keyboard = v_right;
+ if (flatdir * v_right * -1 > trigger) keyboard = v_right * -1;
+ }
+ if (flatdir * v_forward > trigger) keyboard = v_forward;
+ //dprint(ftos(flatdir * v_forward),"\n");
+ keyboard = normalize(keyboard);
+ self.havocbot_keyboard = keyboard;
+ if (self.havocbot_ducktime>time) self.button5=TRUE;
+ }
+ blend = bound(0,vlen(self.goalcurrent.origin-self.origin)/cvar("bot_ai_keyboard_distance"),1); // When getting close move with 360 degree
+ dir = (keyboard * blend + dir * (1-blend))*cvar("sv_maxspeed");
+
self.movement_x = dir * v_forward;
self.movement_y = dir * v_right;
- self.movement_z = dir * v_up;
+ if (self.flags & FL_INWATER) self.movement_z = dir * v_up; else self.movement_z = 0;
+ if ((dir * v_up) >= cvar("g_balance_jumpheight")*0.5 && (self.flags & FL_ONGROUND)) self.button2=1;
+ if (((dodge * v_up) > 0) && random()*frametime >= 0.2*bound(0,(10-skill)*0.1,1)) self.button2=TRUE;
+ if (((dodge * v_up) < 0) && random()*frametime >= 0.5*bound(0,(10-skill)*0.1,1)) self.havocbot_ducktime=time+0.3/bound(0.1,skill,10);
+
};
.float havocbot_chooseenemy_finished;
+.float havocbot_stickenemy;
void() havocbot_chooseenemy =
{
local entity head, best;
@@ -143,9 +213,30 @@
self.enemy = world;
return;
}
+ traceline(self.origin+self.view_ofs, self.enemy.origin+self.enemy.view_ofs*0.5,FALSE,world);
+ if (trace_ent.classname != "player")
+ self.havocbot_stickenemy =0;
+ else if ( (trace_ent != self.enemy) || (vlen(self.enemy.origin - self.origin) > 1000) )
+ {
+ self.havocbot_stickenemy =0;
+ if( (self.health < 30) || (self.enemy.health < 0))
+ self.havocbot_chooseenemy_finished = time;
+ }
+ //dprint(ftos(self.havocbot_stickenemy));dprint(etos(self.enemy),"\n");
+ //dprint(ftos(time),"-");dprint(ftos(self.havocbot_chooseenemy_finished),"\n");
+ if (self.havocbot_stickenemy == 1)
+ {
+ // remain tracking him for a shot while (case he went after a small corner or pilar
+ self.havocbot_chooseenemy_finished = time + bound(0,skill*0.1,1)*1.8;
+ return;
+ }
if (time < self.havocbot_chooseenemy_finished)
+ {
+ self.havocbot_stickenemy = 1;
return;
- self.havocbot_chooseenemy_finished = time + cvar("bot_ai_enemydetectioninterval");
+ }
+ self.havocbot_chooseenemy_finished = time + cvar("bot_ai_enemydetectioninterval")*bound(0,(11-skill)*0.1,1);
+ self.havocbot_chooseenemy_finished = time + 0.01;
eye = (self.origin + self.view_ofs);
best = world;
bestrating = 100000000;
@@ -167,12 +258,107 @@
head = head.chain;
}
self.enemy = best;
+ self.havocbot_stickenemy = 1;
};
float(entity e) w_getbestweapon;
+.float havocbot_chooseweapon_timer;
+.float havocbot_chooseweapon_lastbestscore;
void() havocbot_chooseweapon =
{
- self.switchweapon = w_getbestweapon(self);
+ if(self.enemy.classname!="player")
+ {
+ self.switchweapon = w_getbestweapon(self);
+ return;
+ }
+
+ local float rocket ; rocket =-1000;
+ local float nex ; nex =-1000;
+ local float hagar ; hagar =-1000;
+ local float grenade ; grenade =-1000;
+ local float electro ; electro =-1000;
+ local float crylink ; crylink =-1000;
+ local float uzi ; uzi =-1000;
+ local float shotgun ; shotgun =-1000;
+ local float laser ; laser =-1000;
+ local float bestscore; bestscore = 0;
+ local float bestweapon; bestweapon=self.switchweapon;
+ local float distance; distance=bound(10,vlen(self.origin-self.enemy.origin)-200,10000);
+ local float maxdelaytime=0.5;
+ local float spreadpenalty=10;
+ local float distancefromfloor;
+ traceline(self.enemy.origin,self.enemy.origin-'0 0 1000',TRUE,world);
+ distancefromfloor = self.enemy.origin_z - trace_endpos_z;
+ // Formula:
+ // (Damage/Sec * Weapon spefic change to get that damage)
+ // *(Time to get to target * weapon specfic hitchange bonus) / (in a time of maxdelaytime)
+ // *(Spread change of hit) // if it applies
+ // *(Penality for target beeing in air)
+ if (client_hasweapon(self, WEP_ROCKET_LAUNCHER, TRUE, FALSE))
+ rocket = (cvar("g_balance_rocketlauncher_damage")/cvar("g_balance_rocketlauncher_refire")*0.75)
+ * bound(0,(cvar("g_balance_rocketlauncher_speed")/distance*maxdelaytime),1)*1.5;
+ if (client_hasweapon(self, WEP_NEX, TRUE, FALSE))
+ nex = (cvar("g_balance_nex_damage")/cvar("g_balance_nex_refire")*1.0)
+ * (0.5);
+ if (client_hasweapon(self, WEP_HAGAR, TRUE, FALSE))
+ hagar = (cvar("g_balance_hagar_primary_damage")/cvar("g_balance_hagar_primary_refire")*1.0)
+ * bound(0,(cvar("g_balance_hagar_primary_speed")/distance*maxdelaytime),1)*0.2;
+ if (client_hasweapon(self, WEP_GRENADE_LAUNCHER, TRUE, FALSE))
+ grenade = (cvar("g_balance_grenadelauncher_primary_damage")/cvar("g_balance_grenadelauncher_primary_refire")*1.0)
+ * bound(0,(cvar("g_balance_grenadelauncher_primary_speed")/distance*maxdelaytime),1)*1.1;
+ if (client_hasweapon(self, WEP_ELECTRO, TRUE, FALSE))
+ electro = (cvar("g_balance_electro_primary_damage")/cvar("g_balance_electro_primary_refire")*0.75)
+ * bound(0,(cvar("g_balance_electro_primary_speed")/distance*maxdelaytime),1)*1.0;
+ if (client_hasweapon(self, WEP_CRYLINK, TRUE, FALSE))
+ crylink = (cvar("g_balance_crylink_primary_damage")/cvar("g_balance_crylink_primary_refire")*1.0)
+ * bound(0,(cvar("g_balance_crylink_primary_speed")/distance*maxdelaytime),1)*1.0
+ * bound(0,1/cvar("g_balance_crylink_primary_spread")/distance*spreadpenalty,1);
+ if (client_hasweapon(self, WEP_UZI, TRUE, FALSE))
+ uzi = (cvar("g_balance_uzi_sustained_damage")/cvar("g_balance_uzi_sustained_refire")*1.0)
+ * bound(0,1/cvar("g_balance_uzi_sustained_spread")/distance*spreadpenalty,1)*0.5;
+ if (client_hasweapon(self, WEP_SHOTGUN, TRUE, FALSE))
+ shotgun = (cvar("g_balance_shotgun_primary_damage")*cvar("g_balance_shotgun_primary_bullets")/cvar("g_balance_shotgun_primary_refire")*1.0)
+ * bound(0,1/cvar("g_balance_shotgun_primary_spread")/distance*spreadpenalty,1);
+ if (client_hasweapon(self, WEP_LASER, FALSE, FALSE))
+ laser = (cvar("g_balance_laser_damage")/cvar("g_balance_laser_refire")*1.0)
+ * bound(0,cvar("g_balance_laser_speed")/distance*0.2*maxdelaytime,1);
+ if((self.enemy.flags & FL_ONGROUND)==FALSE){
+ rocket = rocket * (1-bound(0, distancefromfloor/cvar("g_balance_rocketlauncher_radius" ),0.9)); //slight bigger change
+ grenade = grenade * (1-bound(0,distancefromfloor/cvar("g_balance_grenadelauncher_primary_radius"),0.95));
+ electro = electro * (1-bound(0,distancefromfloor/cvar("g_balance_electro_primary_radius" ),0.95));
+ laser = laser * (1-bound(0,distancefromfloor/cvar("g_balance_laser_radius" ),0.95));
+ }
+/*
+ dprint("Floor distance: ",ftos(distancefromfloor),"\n");
+ dprint("Rocket: " , ftos(rocket ), "\n");
+ dprint("Nex: " , ftos(nex ), "\n");
+ dprint("Hagar: " , ftos(hagar ), "\n");
+ dprint("Grenade: ", ftos(grenade ), "\n");
+ dprint("Electro: ", ftos(electro ), "\n");
+ dprint("Crylink: ", ftos(crylink ), "\n");
+ dprint("Uzi: " , ftos(uzi ), "\n");
+ dprint("Shotgun :", ftos(shotgun ), "\n");
+ dprint("Laser :", ftos(laser ), "\n\n");
+*/
+ if (rocket > bestscore){ bestscore = rocket ; bestweapon = WEP_ROCKET_LAUNCHER ;}
+ if (nex > bestscore){ bestscore = nex ; bestweapon = WEP_NEX ;}
+ if (hagar > bestscore){ bestscore = hagar ; bestweapon = WEP_HAGAR ;}
+ if (grenade > bestscore){ bestscore = grenade ; bestweapon = WEP_GRENADE_LAUNCHER ;}
+ if (electro > bestscore){ bestscore = electro ; bestweapon = WEP_ELECTRO ;}
+ if (crylink > bestscore){ bestscore = crylink ; bestweapon = WEP_CRYLINK ;}
+ if (uzi > bestscore){ bestscore = uzi ; bestweapon = WEP_UZI ;}
+ if (shotgun > bestscore){ bestscore = shotgun ; bestweapon = WEP_SHOTGUN ;}
+ if (laser > bestscore){ bestscore = laser ; bestweapon = WEP_LASER ;}
+
+ if(time>self.havocbot_chooseweapon_timer || self.havocbot_chooseweapon_lastbestscore<bestscore/10) //Or when the new damage is SOO much larger ! Or my gun runs out of ammo
+ {
+ self.havocbot_chooseweapon_timer=max(self.havocbot_chooseweapon_timer+cvar("g_balance_weaponswitchdelay")*120*(10-skill)*0.1,time);
+ if( self.havocbot_chooseweapon_lastbestscore*1.5<bestscore*bound(1,1+(skill*skill)*0.01,2))
+ {
+ self.switchweapon = bestweapon;
+ self.havocbot_chooseweapon_lastbestscore=bestscore;
+ }
+ }
};
.float nextaim;
@@ -208,9 +394,20 @@
weapon_action(self.weapon, WR_AIM);
else if (self.goalcurrent)
{
- local vector v;
- v = self.goalcurrent.origin - self.origin;
- //v = self.velocity;
+ local vector now,v,next;//,heading;
+ local float distance,skillblend,distanceblend;
+ now = self.goalcurrent.origin - self.origin;
+ distance = vlen(now);
+ //heading = self.velocity;
+ //dprint(self.goalstack01.classname,etos(self.goalstack01),"\n");
+ if(self.goalstack01 != self && self.goalstack01 != world)
+ next = self.goalstack01.origin - self.origin;
+ else
+ next = now;
+ skillblend=bound(0,(skill-2.5)*0.5,1); //lower skill player can't preturn
+ distanceblend=bound(0,distance/cvar("bot_ai_keyboard_distance"),1);
+ v = (now * (distanceblend) + next * (1-distanceblend)) * (skillblend) + now * (1-skillblend);
+ //v = now * (distanceblend) + next * (1-distanceblend);
if (self.waterlevel < 2)
v_z = 0;
//dprint("walk at:", vtos(v), "\n");
@@ -224,5 +421,7 @@
{
self.bot_ai = havocbot_ai;
// will be updated by think code
+ //Generate some random skill levels
+ self.havocbot_keyboardskill=random()-0.5;
havocbot_chooserole();
}
Modified: trunk/data/qcsrc/server/w_crylink.qc
===================================================================
--- trunk/data/qcsrc/server/w_crylink.qc 2006-08-16 18:05:54 UTC (rev 1820)
+++ trunk/data/qcsrc/server/w_crylink.qc 2006-08-17 12:31:39 UTC (rev 1821)
@@ -180,7 +180,12 @@
float(float req) w_crylink =
{
if (req == WR_AIM)
- self.button0 = bot_aim(cvar("g_balance_crylink_primary_speed"), 0, cvar("g_balance_crylink_primary_lifetime"), FALSE);
+ {
+ if (random() > 0.15)
+ self.button0 = bot_aim(cvar("g_balance_crylink_primary_speed"), 0, cvar("g_balance_crylink_primary_lifetime"), FALSE);
+ else
+ self.button3 = bot_aim(cvar("g_balance_crylink_secondary_speed"), 0, cvar("g_balance_crylink_secondary_lifetime"), FALSE);
+ }
else if (req == WR_THINK)
{
if (self.button0)
Modified: trunk/data/qcsrc/server/w_electro.qc
===================================================================
--- trunk/data/qcsrc/server/w_electro.qc 2006-08-16 18:05:54 UTC (rev 1820)
+++ trunk/data/qcsrc/server/w_electro.qc 2006-08-17 12:31:39 UTC (rev 1821)
@@ -149,10 +149,32 @@
//sound (proj, CHAN_BODY, "weapons/electro_fly.wav", 1, ATTN_NORM);
}
+.float bot_secondary_electromooth;
float(float req) w_electro =
{
if (req == WR_AIM)
- self.button0 = bot_aim(cvar("g_balance_electro_primary_speed"), 0, cvar("g_balance_electro_primary_lifetime"), FALSE);
+ {
+ self.button0=FALSE;
+ self.button3=FALSE;
+ if(vlen(self.origin-self.enemy.origin) > 1000)
+ self.bot_secondary_electromooth = 0;
+ if(self.bot_secondary_electromooth == 0)
+ {
+ if(bot_aim(cvar("g_balance_electro_primary_speed"), 0, cvar("g_balance_electro_primary_lifetime"), FALSE))
+ {
+ self.button0 = TRUE;
+ if(random() < 0.01) self.bot_secondary_electromooth = 1;
+ }
+ }
+ else
+ {
+ if(bot_aim(cvar("g_balance_electro_secondary_speed"), cvar("g_balance_grenadelauncher_secondary_speed_up"), cvar("g_balance_electro_secondary_lifetime"), TRUE))
+ {
+ self.button3 = TRUE;
+ if(random() < 0.03) self.bot_secondary_electromooth = 0;
+ }
+ }
+ }
else if (req == WR_THINK)
{
if (self.button0)
Modified: trunk/data/qcsrc/server/w_grenadelauncher.qc
===================================================================
--- trunk/data/qcsrc/server/w_grenadelauncher.qc 2006-08-16 18:05:54 UTC (rev 1820)
+++ trunk/data/qcsrc/server/w_grenadelauncher.qc 2006-08-17 12:31:39 UTC (rev 1821)
@@ -133,10 +133,30 @@
gren.flags = FL_PROJECTILE;
}
+.float bot_secondary_grenademooth;
float(float req) w_glauncher =
{
if (req == WR_AIM)
- self.button0 = bot_aim(cvar("g_balance_grenadelauncher_primary_speed"), cvar("g_balance_grenadelauncher_primary_speed_up"), cvar("g_balance_grenadelauncher_primary_lifetime"), TRUE);
+ {
+ self.button0 = FALSE;
+ self.button3 = FALSE;
+ if (self.bot_secondary_grenademooth == 0)
+ {
+ if(bot_aim(cvar("g_balance_grenadelauncher_primary_speed"), cvar("g_balance_grenadelauncher_primary_speed_up"), cvar("g_balance_grenadelauncher_primary_lifetime"), TRUE))
+ {
+ self.button0 = TRUE;
+ if(random() < 0.01) self.bot_secondary_grenademooth = 1;
+ }
+ }
+ else
+ {
+ if(bot_aim(cvar("g_balance_grenadelauncher_secondary_speed"), cvar("g_balance_grenadelauncher_secondary_speed_up"), cvar("g_balance_grenadelauncher_secondary_lifetime"), TRUE))
+ {
+ self.button3 = TRUE;
+ if(random() < 0.02) self.bot_secondary_grenademooth = 0;
+ }
+ }
+ }
else if (req == WR_THINK)
{
if (self.button0)
Modified: trunk/data/qcsrc/server/w_hagar.qc
===================================================================
--- trunk/data/qcsrc/server/w_hagar.qc 2006-08-16 18:05:54 UTC (rev 1820)
+++ trunk/data/qcsrc/server/w_hagar.qc 2006-08-17 12:31:39 UTC (rev 1821)
@@ -148,7 +148,13 @@
float(float req) w_hagar =
{
if (req == WR_AIM)
- self.button0 = bot_aim(cvar("g_balance_hagar_primary_speed"), 0, cvar("g_balance_hagar_primary_lifetime"), FALSE);
+ if (random()>0.15)
+ self.button0 = bot_aim(cvar("g_balance_hagar_primary_speed"), 0, cvar("g_balance_hagar_primary_lifetime"), FALSE);
+ else
+ {
+ // not using secondary_speed since these are only 15% and should cause some ricochets without re-aiming
+ self.button3 = bot_aim(cvar("g_balance_hagar_primary_speed"), 0, cvar("g_balance_hagar_primary_lifetime"), FALSE);
+ }
else if (req == WR_THINK)
{
if (self.button0)
Modified: trunk/data/qcsrc/server/w_rocketlauncher.qc
===================================================================
--- trunk/data/qcsrc/server/w_rocketlauncher.qc 2006-08-16 18:05:54 UTC (rev 1820)
+++ trunk/data/qcsrc/server/w_rocketlauncher.qc 2006-08-17 12:31:39 UTC (rev 1821)
@@ -251,11 +251,16 @@
missile = find(world, classname, "missile");
while (missile)
{
+ if (missile.owner != self)
+ {
+ missile = find(missile, classname, "missile");
+ continue;
+ }
targ = targetlist;
while (targ)
{
d = vlen(targ.origin + (targ.mins + targ.maxs) * 0.5 - missile.origin);
- d = edgedamage + (coredamage - edgedamage) * (1 - d * recipricoledgeradius);
+ d = bound(0, edgedamage + (coredamage - edgedamage) * sqrt(1 - d * recipricoledgeradius), 10000);
// count potential damage according to type of target
if (targ == self)
selfdamage = selfdamage + d;
@@ -273,13 +278,51 @@
desirabledamage = desirabledamage - selfdamage * cvar("g_balance_selfdamagepercent");
if (self.team && teamplay == 2)
desirabledamage = desirabledamage - teamdamage;
- // if we would be doing at least half of the core damage, detonate it
+
+ missile = find(world, classname, "missile");
+ while (missile)
+ {
+ if (missile.owner != self)
+ {
+ missile = find(missile, classname, "missile");
+ continue;
+ }
+ makevectors(missile.v_angle);
+ targ = targetlist;
+ if (skill > 9) // normal players only do this for the target they are tracking
+ {
+ targ = targetlist;
+ while (targ)
+ {
+ if (
+ (v_forward * normalize(missile.origin - targ.origin)< 0.1)
+ && desirabledamage > 0.1*coredamage
+ )self.button3 = TRUE;
+ targ = targ.chain;
+ }
+ }else{
+ local float distance; distance= bound(300,vlen(self.origin-self.enemy.origin),30000);
+ //As the distance gets larger, a correct detonation gets near imposible
+ //Bots are assumed to use the rocket light to see if the rocket gets near a player
+ if(v_forward * normalize(missile.origin - self.enemy.origin)< 0.1)
+ if(self.enemy.classname == "player")
+ if(desirabledamage >= 0.1*coredamage)
+ if(random()/distance*300 > frametime*bound(0,(10-skill)*0.2,1))
+ self.button3 = TRUE;
+ // dprint(ftos(random()/distance*300),">");dprint(ftos(frametime*bound(0,(10-skill)*0.2,1)),"\n");
+ }
+
+ missile = find(missile, classname, "missile");
+ }
+ // if we would be doing at X percent of the core damage, detonate it
// but don't fire a new shot at the same time!
- if (desirabledamage >= 0.5 * coredamage)
- {
+ if (desirabledamage >= 0.75 * coredamage) //this should do group damage in rare fortunate events
self.button3 = TRUE;
- self.button0 = FALSE;
- }
+ if ((skill > 6.5) && (selfdamage > self.health))
+ self.button3 = FALSE;
+ //if(self.button3 == TRUE)
+ // dprint(ftos(desirabledamage),"\n");
+ if (self.button3 == TRUE) self.button0 = FALSE;
}
}
else if (req == WR_THINK)
Modified: trunk/data/qcsrc/server/w_shotgun.qc
===================================================================
--- trunk/data/qcsrc/server/w_shotgun.qc 2006-08-16 18:05:54 UTC (rev 1820)
+++ trunk/data/qcsrc/server/w_shotgun.qc 2006-08-17 12:31:39 UTC (rev 1821)
@@ -64,7 +64,10 @@
float(float req) w_shotgun =
{
if (req == WR_AIM)
- self.button0 = bot_aim(1000000, 0, 0.001, FALSE);
+ if(vlen(self.origin-self.enemy.origin)>200)
+ self.button0 = bot_aim(1000000, 0, 0.001, FALSE);
+ else
+ self.button3 = bot_aim(1000000, 0, 0.001, FALSE);
else if (req == WR_THINK)
{
if (self.button0)
Modified: trunk/data/qcsrc/server/w_uzi.qc
===================================================================
--- trunk/data/qcsrc/server/w_uzi.qc 2006-08-16 18:05:54 UTC (rev 1820)
+++ trunk/data/qcsrc/server/w_uzi.qc 2006-08-17 12:31:39 UTC (rev 1821)
@@ -71,7 +71,12 @@
float(float req) w_uzi =
{
if (req == WR_AIM)
- self.button0 = bot_aim(1000000, 0, 0.001, FALSE);
+ if(vlen(self.origin-self.enemy.origin)<750)
+ self.button0 = bot_aim(1000000, 0, 0.001, FALSE);
+ else
+ {
+ self.button3 = bot_aim(1000000, 0, 0.001, FALSE);
+ }
else if (req == WR_THINK)
{
if (self.button0)
More information about the nexuiz-commits
mailing list