From daf1fbc6eb8b30f87544652862ead1f51727e92f Mon Sep 17 00:00:00 2001 From: mand1nga Date: Fri, 7 Aug 2009 16:51:39 +0000 Subject: [PATCH] Added jetpack support for bots To use it add -DBOT_JETPACK_NAVIGATION to FTQECCFLAGS in data/Makefile (will be enabled by default after the next release) git-svn-id: svn://svn.icculus.org/nexuiz/trunk@7384 f962a42d-fe04-0410-a3ab-8c8b0445ebaa --- data/defaultNexuiz.cfg | 4 +- data/qcsrc/server/bots.qc | 115 +++++++++++++++++++++++++++++++++- data/qcsrc/server/havocbot.qc | 64 +++++++++++++++++++ 3 files changed, 181 insertions(+), 2 deletions(-) diff --git a/data/defaultNexuiz.cfg b/data/defaultNexuiz.cfg index e016ff22b..2adf0dea4 100644 --- a/data/defaultNexuiz.cfg +++ b/data/defaultNexuiz.cfg @@ -396,6 +396,9 @@ set bot_ai_bunnyhop_skilloffset 7 "Bots with skill equal or greater than this va set bot_ai_bunnyhop_startdistance 250 "Run to goals located further than this distance" set bot_ai_bunnyhop_stopdistance 220 "Stop jumping after reaching this distance to the goal" set bot_ai_bunnyhop_firstjumpdelay 0.5 "Start running to the goal only if it was seen for more than N seconds" +set bot_god 0 "god mode for bots" +set bot_ai_navigation_jetpack 1 "Enable bots to navigat maps using the jetpack" +set bot_ai_navigation_jetpack_mindistance 2500 "Bots will try fly to objects located farther than this distance" // Better don't touch these, there are hard to tweak! set bot_ai_aimskill_order_mix_1st 0.01 "Amount of the 1st filter output to apply to the aiming angle" set bot_ai_aimskill_order_mix_2nd 0.1 "Amount of the 1st filter output to apply to the aiming angle" @@ -407,7 +410,6 @@ set bot_ai_aimskill_order_filter_2nd 0.4 "Movement filter" set bot_ai_aimskill_order_filter_3th 0.2 "Acceleration filter" set bot_ai_aimskill_order_filter_4th 0.4 "Position prediction filter. Used rarely" set bot_ai_aimskill_order_filter_5th 0.5 "Movement prediction filter. Used rarely" -set bot_god 0 "god mode for bots" // waypoint editor enable set g_waypointeditor 0 diff --git a/data/qcsrc/server/bots.qc b/data/qcsrc/server/bots.qc index 17bc23f4a..4e0567910 100644 --- a/data/qcsrc/server/bots.qc +++ b/data/qcsrc/server/bots.qc @@ -8,6 +8,10 @@ float AI_STATUS_OUT_WATER = 32; // Trying to get out of water float AI_STATUS_WAYPOINT_PERSONAL_LINKING = 64; // Waiting for the personal waypoint to be linked float AI_STATUS_WAYPOINT_PERSONAL_GOING = 128; // Going to a personal waypoint float AI_STATUS_WAYPOINT_PERSONAL_REACHED = 256; // Personal waypoint reached +#ifdef BOT_JETPACK_NAVIGATION +float AI_STATUS_JETPACK_FLYING = 512; +float AI_STATUS_JETPACK_LANDING = 1024; +#endif // utilities for path debugging #ifdef DEBUG_TRACEWALK @@ -1745,6 +1749,12 @@ entity navigation_get_bestgoal() return ent; } +#ifdef BOT_JETPACK_NAVIGATION +// fields required for jetpack navigation +.entity navigation_jetpack_goal; +.vector navigation_jetpack_point; +#endif + // updates the best goal according to a weighted calculation of travel cost and item value of a new proposed item .void() havocbot_role; void() havocbot_role_ctf_offense; @@ -1752,6 +1762,98 @@ void navigation_routerating(entity e, float f, float rangebias) { if (!e) return; + +#ifdef BOT_JETPACK_NAVIGATION + // Evaluate path using jetpack + if(g_jetpack) + if(self.items & IT_JETPACK) + if(cvar("bot_ai_navigation_jetpack")) + if(vlen(self.origin - e.origin) > cvar("bot_ai_navigation_jetpack_mindistance")) + { + vector pointa, pointb; + + // dprint("jetpack ai: evaluating path for ", e.classname,"\n"); + + // Point A + traceline(self.origin, self.origin + '0 0 65535', MOVE_NORMAL, self); + pointa = trace_endpos - '0 0 1'; + + // Point B + traceline(e.origin, e.origin + '0 0 65535', MOVE_NORMAL, e); + pointb = trace_endpos - '0 0 1'; + + // Can I see these two points from the sky? + traceline(pointa, pointb, MOVE_NORMAL, self); + + if(trace_fraction==1) + { + // dprint("jetpack ai: can bridge these two points\n"); + + // Lower the altitude of these points as much as possible + local float zdistance, xydistance, cost, t, fuel; + local vector down, npa, npb; + + down = '0 0 -1' * (PL_MAX_z - PL_MIN_z) * 10; + + do{ + npa = pointa + down; + npb = pointb + down; + + if(npa_z<=self.absmax_z) + break; + + if(npb_z<=e.absmax_z) + break; + + traceline(npa, npb, MOVE_NORMAL, self); + if(trace_fraction==1) + { + pointa = npa; + pointb = npb; + } + } + while(trace_fraction == 1); + + + // Rough estimation of fuel consumption + // (ignores acceleration and current xyz velocity) + xydistance = vlen(pointa - pointb); + zdistance = fabs(pointa_z - self.origin_z); + + t = zdistance / cvar("g_jetpack_maxspeed_up"); + t += xydistance / cvar("g_jetpack_maxspeed_side"); + fuel = t * cvar("g_jetpack_fuel") * 0.8; + + // dprint("jetpack ai: required fuel ", ftos(fuel), " self.ammo_fuel ", ftos(self.ammo_fuel),"\n"); + + // enough fuel ? + if(self.ammo_fuel>fuel) + { + // Estimate cost + // (as onground costs calculation is mostly based on distances, here we do the same establishing some relationship + // - between air and ground speeds) + + cost = xydistance / (cvar("g_jetpack_maxspeed_side")/cvar("sv_maxspeed")); + cost += zdistance / (cvar("g_jetpack_maxspeed_up")/cvar("sv_maxspeed")); + cost *= 1.5; + + // Compare against other goals + f = f * rangebias / (rangebias + cost); + + if (navigation_bestrating < f) + { + // dprint("jetpack path: added goal", e.classname, " (with rating ", ftos(f), ")\n"); + navigation_bestrating = f; + navigation_add_bestgoal(e); + self.navigation_jetpack_goal = e; + self.navigation_jetpack_point = pointb; + } + return; + } + } + } + #endif + //te_wizspike(e.origin); //bprint(etos(e)); //bprint("\n"); @@ -1772,13 +1874,14 @@ void navigation_routerating(entity e, float f, float rangebias) if (e.nearestwaypoint.wpcost < 10000000) { //te_wizspike(e.nearestwaypoint.wpnearestpoint); - //dprint(e.classname, " ", ftos(f), "/(1+", ftos((e.nearestwaypoint.wpcost + vlen(e.origin - e.nearestwaypoint.wpnearestpoint))), "/", ftos(rangebias), ") = "); + // dprint(e.classname, " ", ftos(f), "/(1+", ftos((e.nearestwaypoint.wpcost + vlen(e.origin - e.nearestwaypoint.wpnearestpoint))), "/", ftos(rangebias), ") = "); f = f * rangebias / (rangebias + (e.nearestwaypoint.wpcost + vlen(e.origin - e.nearestwaypoint.wpnearestpoint))); //if (self.havocbot_role == havocbot_role_ctf_offense) //dprint("considering ", e.classname, " (with rating ", ftos(f), ")\n"); //dprint(ftos(f)); if (navigation_bestrating < f) { + // dprint("ground path: added goal ", e.classname, " (with rating ", ftos(f), ")\n"); navigation_bestrating = f; navigation_add_bestgoal(e); } @@ -1800,6 +1903,12 @@ float navigation_routetogoal(entity e, vector startposition) // put the entity on the goal stack navigation_pushroute(e); +#ifdef BOT_JETPACK_NAVIGATION + if(g_jetpack) + if(e==self.navigation_jetpack_goal) + return TRUE; +#endif + // if it can reach the goal there is nothing more to do if (tracewalk(self, startposition, PL_MIN, PL_MAX, e.origin, bot_navigation_movemode)) return TRUE; @@ -1913,6 +2022,9 @@ void navigation_poptouchedgoals() // begin a goal selection session (queries spawnfunc_waypoint network) void navigation_goalrating_start() { +#ifdef BOT_JETPACK_NAVIGATION + self.navigation_jetpack_goal = world; +#endif navigation_bestrating = -1; self.navigation_hasgoals = FALSE; navigation_bestgoals_reset(); @@ -1923,6 +2035,7 @@ void navigation_goalrating_start() void navigation_goalrating_end() { navigation_routetogoals(); +// dprint("best goal ", self.goalcurrent.classname , "\n"); // Hack: if it can't walk to any goal just move blindly to the first visible waypoint if not (self.navigation_hasgoals) diff --git a/data/qcsrc/server/havocbot.qc b/data/qcsrc/server/havocbot.qc index fe476ffab..4ac1a3fc3 100644 --- a/data/qcsrc/server/havocbot.qc +++ b/data/qcsrc/server/havocbot.qc @@ -308,6 +308,70 @@ void havocbot_movetogoal() self.movement = '0 0 0'; maxspeed = cvar("sv_maxspeed"); +#ifdef BOT_JETPACK_NAVIGATION + // Jetpack navigation + if(self.navigation_jetpack_goal) + if(self.goalcurrent==self.navigation_jetpack_goal) + if(self.ammo_fuel) + { + #ifdef DEBUG_BOT_GOALSTACK + debuggoalstack(); + te_wizspike(self.navigation_jetpack_point); + #endif + + // Take off + if not(self.aistatus & AI_STATUS_JETPACK_FLYING) + { + // Brake almost completely so it can get a good direction + if(vlen(self.velocity)>10) + return; + self.aistatus |= AI_STATUS_JETPACK_FLYING; + } + + makevectors(self.v_angle_y * '0 1 0'); + dir = normalize(self.navigation_jetpack_point - self.origin); + + // Landing + if(self.aistatus & AI_STATUS_JETPACK_LANDING) + { + // TODO: more accurate landing (with optimum fuel usage) + vector p = self.origin - self.goalcurrent.origin; p_z = 0; + if( vlen(p) < max(cvar("g_jetpack_maxspeed_side"), vlen(self.velocity))) + { + // Brake + if(fabs(self.velocity_x)>maxspeed) + { + self.BUTTON_HOOK = TRUE; + self.movement_x = dir * v_forward * -maxspeed; + return; + } + // Switch to normal mode + self.navigation_jetpack_goal = world; + self.aistatus &~= AI_STATUS_JETPACK_LANDING; + self.aistatus &~= AI_STATUS_JETPACK_FLYING; + return; + } + } + else if(checkpvs(self.origin,self.goalcurrent)) + { + // If I can see the goal switch to landing code + self.aistatus &~= AI_STATUS_JETPACK_FLYING; + self.aistatus |= AI_STATUS_JETPACK_LANDING; + return; + } + + // Flying + self.BUTTON_HOOK = TRUE; + if(self.navigation_jetpack_point_z - PL_MAX_z + PL_MIN_z < self.origin_z) + { + self.movement_x = dir * v_forward * maxspeed; + self.movement_y = dir * v_right * maxspeed; + } + return; + } +#endif + + // Handling of jump pads if(self.jumppadcount) { if(self.flags & FL_ONGROUND) -- 2.39.2