From 59bea426c5e40bc09e8633436cc96e827ced62ae Mon Sep 17 00:00:00 2001 From: mand1nga Date: Mon, 9 Feb 2009 14:58:59 +0000 Subject: [PATCH] Bot improvements: Use the 2nd best rated goal if something happened to the primary goal (ie: someone picked it up before). This is cheaper than forcing a whole strategy re-think. Anti lemming behavior workaround Force strategy re-think upon respawn Moved keyboard emulation out of movetogoal() Made them "try" to jump out of lava git-svn-id: svn://svn.icculus.org/nexuiz/trunk@5813 f962a42d-fe04-0410-a3ab-8c8b0445ebaa --- data/qcsrc/server/bots.qc | 13 +- data/qcsrc/server/havocbot.qc | 218 +++++++++++++++++----------- data/qcsrc/server/havocbot_roles.qc | 23 ++- 3 files changed, 161 insertions(+), 93 deletions(-) diff --git a/data/qcsrc/server/bots.qc b/data/qcsrc/server/bots.qc index c0dc659f2..0ace7d7f3 100644 --- a/data/qcsrc/server/bots.qc +++ b/data/qcsrc/server/bots.qc @@ -366,7 +366,8 @@ float lag_additem(float t, float f1, float f2, entity e1, vector v1, vector v2, // used during navigation_goalrating_begin/end sessions float navigation_bestrating; entity navigation_bestgoal; - +entity navigation_alternativegoal; +.entity alternativegoal; @@ -1252,6 +1253,7 @@ void navigation_routerating(entity e, float f, float rangebias) //dprint(ftos(f)); if (navigation_bestrating < f) { + navigation_alternativegoal = navigation_bestgoal; navigation_bestrating = f; navigation_bestgoal = e; } @@ -1325,6 +1327,7 @@ void navigation_goalrating_end() { if (self.havocbot_role == havocbot_role_ctf_offense) dprint(navigation_bestgoal.classname, " (with rating ", ftos(navigation_bestrating), ")\n"); + self.alternativegoal = navigation_alternativegoal; navigation_routetogoal(navigation_bestgoal); }; @@ -1367,6 +1370,7 @@ float sv_maxspeed; .float(entity player, entity item) bot_pickupevalfunc; .float bot_pickupbasevalue; .float bot_canfire; +.float bot_strategytime; // used for aiming currently // FIXME: make the weapon code use these and then replace the calculations here with a call to the weapon code @@ -1940,10 +1944,13 @@ void bot_think() if (self.deadflag) { if (self.deadflag == DEAD_DEAD) + { self.BUTTON_JUMP = 1; // press jump to respawn - return; + self.bot_strategytime = 0; + return; + } } - + // now call the current bot AI (havocbot for example) self.bot_ai(); }; diff --git a/data/qcsrc/server/havocbot.qc b/data/qcsrc/server/havocbot.qc index 9613457e3..133c27f96 100644 --- a/data/qcsrc/server/havocbot.qc +++ b/data/qcsrc/server/havocbot.qc @@ -55,10 +55,82 @@ vector havocbot_dodge() */ }; -//.float havocbotignoretime; .float havocbot_keyboardtime; .float havocbot_ducktime; .vector havocbot_keyboard; +void havocbot_keyboard_movement(vector destorg) +{ + local vector keyboard; + local float blend, maxspeed; + + maxspeed = cvar("sv_maxspeed"); + + if (time < self.havocbot_keyboardtime) + return; + + 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 = self.movement * (1.0 / maxspeed); + + local float trigger, trigger1; + blend = bound(0,skill*0.1,1); + trigger = cvar("bot_ai_keyboard_treshold"); + trigger1 = 0 - trigger; + + // categorize forward movement + // at skill < 1.5 only forward + // at skill < 2.5 only individual directions + // at skill < 4.5 only individual directions, and forward diagonals + // at skill >= 4.5, all cases allowed + if (keyboard_x > trigger) + { + keyboard_x = 1; + if (skill < 2.5) + keyboard_y = 0; + } + else if (keyboard_x < trigger1 && skill > 1.5) + { + keyboard_x = -1; + if (skill < 4.5) + keyboard_y = 0; + } + else + { + keyboard_x = 0; + if (skill < 1.5) + keyboard_y = 0; + } + if (skill < 4.5) + keyboard_z = 0; + + if (keyboard_y > trigger) + keyboard_y = 1; + else if (keyboard_y < trigger1) + keyboard_y = -1; + else + keyboard_y = 0; + + if (keyboard_z > trigger) + keyboard_z = 1; + else if (keyboard_z < trigger1) + keyboard_z = -1; + else + keyboard_z = 0; + + self.havocbot_keyboard = keyboard * maxspeed; + if (self.havocbot_ducktime>time) self.BUTTON_CROUCH=TRUE; + + keyboard = self.havocbot_keyboard; + blend = bound(0,vlen(destorg-self.origin)/cvar("bot_ai_keyboard_distance"),1); // When getting close move with 360 degree + //dprint("movement ", vtos(self.movement), " keyboard ", vtos(keyboard), " blend ", ftos(blend), "\n"); + self.movement = self.movement + (keyboard - self.movement) * blend; +}; + +//.float havocbotignoretime; //.vector bot_dodgevector; //.float bot_dodgevector_time; //.float bot_dodgevector_jumpbutton; @@ -91,13 +163,26 @@ void havocbot_movetogoal() if (self.goalcurrent == world) return; + navigation_poptouchedgoals(); if (self.goalcurrent == world) { - // ran out of goals, rethink strategy as soon as possible - self.bot_strategytime = 0; + if(self.alternativegoal==world) + { + // ran out of goals, rethink strategy as soon as possible + self.bot_strategytime = 0; + return; + } + // try to use the alternative goal + navigation_findnearestwaypoint(self.alternativegoal, TRUE); + navigation_routetogoal(self.alternativegoal); + self.alternativegoal = world; return; } + +// te_wizspike(self.goalcurrent.origin); +// te_lightning2(world, self.origin, self.goalcurrent.origin); + m1 = self.goalcurrent.origin + self.goalcurrent.mins; m2 = self.goalcurrent.origin + self.goalcurrent.maxs; destorg = self.origin; @@ -109,23 +194,21 @@ void havocbot_movetogoal() dir = normalize(diff); flatdir = diff;flatdir_z = 0; flatdir = normalize(flatdir); + //if (self.bot_dodgevector_time < time) { // self.bot_dodgevector_time = time + cvar("bot_ai_dodgeupdateinterval"); // self.bot_dodgevector_jumpbutton = 1; evadeobstacle = '0 0 0'; evadelava = '0 0 0'; - if (!self.waterlevel) + if (self.waterlevel) + { + makevectors(self.v_angle); + self.BUTTON_JUMP = 1; + // TODO: Make them swim up + } + else { - // 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 tracebox(self.origin, self.mins, self.maxs, self.origin + self.velocity * 0.2, FALSE, self); @@ -144,18 +227,42 @@ void havocbot_movetogoal() } } - traceline(self.origin + self.velocity * 0.3, self.origin + self.velocity * 0.3 + '0 0 -1000', TRUE, world); - s = pointcontents(trace_endpos + '0 0 1'); - if (s == CONTENT_LAVA || s == CONTENT_SLIME) - evadelava = normalize(self.velocity) * -1; + // avoiding dangers and obstacles + // TODO: don't make this check every frame + if(self.flags & FL_ONGROUND) + { + local vector dst_ahead, dst_down; + dst_ahead = self.origin + self.view_ofs + (self.velocity * 0.3); + dst_down = dst_ahead + '0 0 -1500'; + + traceline(self.origin + self.view_ofs , dst_ahead, TRUE, world); + + if(trace_fraction == 1){ + traceline(dst_ahead , dst_down, TRUE, world); + // te_lightning2(world, self.origin, dst_ahead); // Draw "ahead" look + if(trace_fraction == 1) + { + s = pointcontents(trace_endpos + '0 0 1'); + if (s == CONTENT_LAVA || s == CONTENT_SLIME) + evadelava = normalize(self.velocity) * -1; + else if (s == CONTENT_SKY) + evadeobstacle = normalize(self.velocity) * -1; + else if (tracebox_hits_trigger_hurt(dst_ahead, self.mins, self.maxs, trace_endpos)) + { + // te_lightning2(world, dst_ahead, dst_down); // Draw "downards" look + // TODO: this could be better, but actually it stops lemming behavior + evadeobstacle = flatdir * -1; + } + } + } + } dir = flatdir; evadeobstacle_z = 0; evadelava_z = 0; makevectors(self.v_angle_y * '0 1 0'); } - else - makevectors(self.v_angle); + dodge = havocbot_dodge(); 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 @@ -176,79 +283,13 @@ void havocbot_movetogoal() self.movement_y = dir * v_right * maxspeed; self.movement_z = dir * v_up * maxspeed; + // Emulate keyboard interface if (skill < 10) - { - // Emulate keyboard interface; - local vector keyboard; - local float blend; - if (time >= self.havocbot_keyboardtime) - { - 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 = self.movement * (1.0 / maxspeed); - - local float trigger, trigger1; - blend = bound(0,skill*0.1,1); - trigger = cvar("bot_ai_keyboard_treshold"); - trigger1 = 0 - trigger; - - // categorize forward movement - // at skill < 1.5 only forward - // at skill < 2.5 only individual directions - // at skill < 4.5 only individual directions, and forward diagonals - // at skill >= 4.5, all cases allowed - if (keyboard_x > trigger) - { - keyboard_x = 1; - if (skill < 2.5) - keyboard_y = 0; - } - else if (keyboard_x < trigger1 && skill > 1.5) - { - keyboard_x = -1; - if (skill < 4.5) - keyboard_y = 0; - } - else - { - keyboard_x = 0; - if (skill < 1.5) - keyboard_y = 0; - } - if (skill < 4.5) - keyboard_z = 0; - - if (keyboard_y > trigger) - keyboard_y = 1; - else if (keyboard_y < trigger1) - keyboard_y = -1; - else - keyboard_y = 0; - - if (keyboard_z > trigger) - keyboard_z = 1; - else if (keyboard_z < trigger1) - keyboard_z = -1; - else - keyboard_z = 0; - - self.havocbot_keyboard = keyboard * maxspeed; - if (self.havocbot_ducktime>time) self.BUTTON_CROUCH=TRUE; - } - keyboard = self.havocbot_keyboard; - blend = bound(0,vlen(destorg-self.origin)/cvar("bot_ai_keyboard_distance"),1); // When getting close move with 360 degree - //dprint("movement ", vtos(self.movement), " keyboard ", vtos(keyboard), " blend ", ftos(blend), "\n"); - self.movement = self.movement + (keyboard - self.movement) * blend; - } + havocbot_keyboard_movement(destorg); if ((dir * v_up) >= cvar("sv_jumpvelocity")*0.5 && (self.flags & FL_ONGROUND)) self.BUTTON_JUMP=1; if (((dodge * v_up) > 0) && random()*frametime >= 0.2*bound(0,(10-skill)*0.1,1)) self.BUTTON_JUMP=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; @@ -559,6 +600,9 @@ void havocbot_ai() // token has been used this frame bot_strategytoken_taken = TRUE; } + if(self.deadflag) + return; + havocbot_chooseenemy(); if (self.bot_chooseweapontime < time ) { diff --git a/data/qcsrc/server/havocbot_roles.qc b/data/qcsrc/server/havocbot_roles.qc index 2c4b3d97a..8896bd887 100644 --- a/data/qcsrc/server/havocbot_roles.qc +++ b/data/qcsrc/server/havocbot_roles.qc @@ -1,7 +1,6 @@ .float havocbot_role_timeout; .void() havocbot_previous_role; -.float bot_strategytime; .void() havocbot_role; float bot_ignore_bots; @@ -236,10 +235,28 @@ void havocbot_goalrating_enemyplayers(float ratingscale, vector org, float sradi if (self != head) if (head.health > 0) if ((noteam && (!bot_ignore_bots || clienttype(head) == CLIENTTYPE_REAL)) || head.team != self.team) - if (vlen(head.origin - org) < sradius) { + if (vlen(head.origin - org) > sradius) + continue; + + // rate only visible enemies + /* + traceline(self.origin + self.view_ofs, head.origin, MOVE_NOMONSTERS, self); + if (trace_fraction < 1 || trace_ent != head) + continue; + */ + + // not falling + if(head.flags & FL_ONGROUND == 0) + { + traceline(head.origin, head.origin + '0 0 -1500', TRUE, world); + t = pointcontents(trace_endpos + '0 0 1'); + if (t != CONTENT_SOLID) + continue; + // TODO: check for trigger_hurt here + } + t = (self.health + self.armorvalue ) / (head.health + head.armorvalue ); - // clientcommand(self, strcat("say rating ", ftos(t*ratingscale))); navigation_routerating(head, t * ratingscale, 2000); } } -- 2.39.2