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