X-Git-Url: https://de.git.xonotic.org/?p=xonotic%2Fxonotic-data.pk3dir.git;a=blobdiff_plain;f=qcsrc%2Fserver%2Fbot%2Fhavocbot%2Fhavocbot.qc;h=1d9054952d24bbdde4d63981bc5a86689f3a44f4;hp=7d1ef3a08f4bd82a7071045ddbf54c5c0f8f39f9;hb=9c40c25078426ed90e985c402c648fea3e3a8832;hpb=16f4c631d6478a969c9f8f021ed55597e3148061 diff --git a/qcsrc/server/bot/havocbot/havocbot.qc b/qcsrc/server/bot/havocbot/havocbot.qc index 7d1ef3a08..1d9054952 100644 --- a/qcsrc/server/bot/havocbot/havocbot.qc +++ b/qcsrc/server/bot/havocbot/havocbot.qc @@ -2,6 +2,8 @@ #include "role_ctf.qc" #include "role_onslaught.qc" #include "role_keyhunt.qc" +#include "role_freezetag.qc" +#include "role_keepaway.qc" #include "roles.qc" void havocbot_ai() @@ -118,7 +120,7 @@ void havocbot_ai() local vector now,v,next;//,heading; local float aimdistance,skillblend,distanceblend,blend; - next = now = self.goalcurrent.origin - (self.origin + self.view_ofs); + next = now = ( (self.goalcurrent.absmin + self.goalcurrent.absmax) * 0.5) - (self.origin + self.view_ofs); aimdistance = vlen(now); //heading = self.velocity; //dprint(self.goalstack01.classname,etos(self.goalstack01),"\n"); @@ -126,7 +128,7 @@ void havocbot_ai() self.goalstack01 != self && self.goalstack01 != world && self.aistatus & AI_STATUS_RUNNING == 0 && !(self.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT) ) - next = self.goalstack01.origin - (self.origin + self.view_ofs); + next = ((self.goalstack01.absmin + self.goalstack01.absmax) * 0.5) - (self.origin + self.view_ofs); skillblend=bound(0,(skill+self.bot_moveskill-2.5)*0.5,1); //lower skill player can't preturn distanceblend=bound(0,aimdistance/autocvar_bot_ai_keyboard_distance,1); @@ -146,26 +148,26 @@ void havocbot_ai() } havocbot_movetogoal(); - // if the bot is not attacking, go through all weapons that aren't fully loaded and reload them to keep them ready + // if the bot is not attacking, consider reloading weapons if not(self.aistatus & AI_STATUS_ATTACKING) { float i; entity e; // we are currently holding a weapon that's not fully loaded, reload it - if(self.clip_load < self.clip_size && self.weapon) - { + if(skill >= 2) // bots can only reload the held weapon on purpose past this skill + if(self.clip_load < self.clip_size) self.impulse = 20; // "press" the reload button, not sure if this is done right - self.clip_load = -1; - } // if we're not reloading a weapon, switch to any weapon in our invnetory that's not fully loaded to reload it next - if(self.clip_load >= 0) + // the code above executes next frame, starting the reloading then + if(skill >= 5) // bots can only look for unloaded weapons past this skill + if(self.clip_load >= 0) // only if we're not reloading a weapon already { - for(i = WEP_FIRST; i <= WEP_LAST ; ++i) + for(i = WEP_FIRST; i <= WEP_LAST; ++i) { e = get_weaponinfo(i); - if(self.weapon_load[i] < cvar(strcat("g_balance_", e.netname, "_reload_ammo"))) + if ((e.spawnflags & WEP_FLAG_RELOADABLE) && (self.weapon_load[i] < cvar(strcat("g_balance_", e.netname, "_reload_ammo")))) self.switchweapon = i; } } @@ -252,6 +254,7 @@ void havocbot_bunnyhop(vector dir) local float bunnyhopdistance; local vector deviation; local float maxspeed; + vector gco, gno; if(autocvar_g_midair) return; @@ -259,7 +262,7 @@ void havocbot_bunnyhop(vector dir) // Don't jump when using some weapons /* if(self.aistatus & AI_STATUS_ATTACKING) - if(self.weapon == WEP_SNIPERRIFLE) + if(self.weapon == WEP_RIFLE) return; if(self.goalcurrent.classname == "player") @@ -289,7 +292,8 @@ void havocbot_bunnyhop(vector dir) self.bot_timelastseengoal = 0; } - bunnyhopdistance = vlen(self.origin - self.goalcurrent.origin); + gco = (self.goalcurrent.absmin + self.goalcurrent.absmax) * 0.5; + bunnyhopdistance = vlen(self.origin - gco); // Run only to visible goals if(self.flags & FL_ONGROUND) @@ -322,18 +326,19 @@ void havocbot_bunnyhop(vector dir) if(self.aistatus & AI_STATUS_ROAMING) if(self.goalcurrent.classname=="waypoint") if not(self.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL) - if(fabs(self.goalcurrent.origin_z - self.origin_z) < self.maxs_z - self.mins_z) + if(fabs(gco_z - self.origin_z) < self.maxs_z - self.mins_z) if(self.goalstack01!=world) { - deviation = vectoangles(self.goalstack01.origin - self.origin) - vectoangles(self.goalcurrent.origin - self.origin); + gno = (self.goalstack01.absmin + self.goalstack01.absmax) * 0.5; + deviation = vectoangles(gno - self.origin) - vectoangles(gco - self.origin); while (deviation_y < -180) deviation_y = deviation_y + 360; while (deviation_y > 180) deviation_y = deviation_y - 360; if(fabs(deviation_y) < 20) - if(bunnyhopdistance < vlen(self.origin - self.goalstack01.origin)) - if(fabs(self.goalstack01.origin_z - self.goalcurrent.origin_z) < self.maxs_z - self.mins_z) + if(bunnyhopdistance < vlen(self.origin - gno)) + if(fabs(gno_z - gco_z) < self.maxs_z - self.mins_z) { - if(vlen(self.goalcurrent.origin - self.goalstack01.origin) > autocvar_bot_ai_bunnyhop_startdistance) + if(vlen(gco - gno) > autocvar_bot_ai_bunnyhop_startdistance) if(checkpvs(self.origin + self.view_ofs, self.goalstack01)) { checkdistance = FALSE; @@ -403,6 +408,7 @@ void havocbot_movetogoal() local vector evadelava; local float s; local float maxspeed; + local vector gco; //local float dist; local vector dodge; //if (self.goalentity) @@ -416,10 +422,11 @@ void havocbot_movetogoal() if(self.goalcurrent==self.navigation_jetpack_goal) if(self.ammo_fuel) { - #ifdef DEBUG_BOT_GOALSTACK + if(autocvar_bot_debug_goalstack) + { debuggoalstack(); te_wizspike(self.navigation_jetpack_point); - #endif + } // Take off if not(self.aistatus & AI_STATUS_JETPACK_FLYING) @@ -440,7 +447,7 @@ void havocbot_movetogoal() float db, v, d; vector dxy; - dxy = self.origin - self.goalcurrent.origin; dxy_z = 0; + dxy = self.origin - ( ( self.goalcurrent.absmin + self.goalcurrent.absmax ) * 0.5 ); dxy_z = 0; d = vlen(dxy); v = vlen(self.velocity - self.velocity_z * '0 0 1'); db = (pow(v,2) / (autocvar_g_jetpack_acceleration_side * 2)) + 100; @@ -498,7 +505,7 @@ void havocbot_movetogoal() if(distance>1000) continue; - traceline(self.origin + self.view_ofs , head.origin, TRUE, world); + traceline(self.origin + self.view_ofs , ( ( head.absmin + head.absmax ) * 0.5 ), TRUE, world); if(trace_fraction<1) continue; @@ -568,7 +575,7 @@ void havocbot_movetogoal() if(self.goalcurrent==world) dir = v_forward; else - dir = normalize(self.goalcurrent.origin - self.origin); + dir = normalize(( ( self.goalcurrent.absmin + self.goalcurrent.absmax ) * 0.5 ) - self.origin); local vector xyvelocity = self.velocity; xyvelocity_z = 0; local float xyspeed = xyvelocity * dir; @@ -653,9 +660,9 @@ void havocbot_movetogoal() return; } -#ifdef DEBUG_BOT_GOALSTACK - debuggoalstack(); -#endif + + if(autocvar_bot_debug_goalstack) + debuggoalstack(); m1 = self.goalcurrent.origin + self.goalcurrent.mins; m2 = self.goalcurrent.origin + self.goalcurrent.maxs; @@ -668,6 +675,7 @@ void havocbot_movetogoal() dir = normalize(diff); flatdir = diff;flatdir_z = 0; flatdir = normalize(flatdir); + gco = (self.goalcurrent.absmin + self.goalcurrent.absmax) * 0.5; //if (self.bot_dodgevector_time < time) { @@ -685,7 +693,7 @@ void havocbot_movetogoal() } else { - if(self.velocity_z >= 0 && !(self.watertype == CONTENT_WATER && self.goalcurrent.origin_z < self.origin_z) && + if(self.velocity_z >= 0 && !(self.watertype == CONTENT_WATER && gco_z < self.origin_z) && ( !(self.waterlevel == WATERLEVEL_WETFEET && self.watertype == CONTENT_WATER) || self.aistatus & AI_STATUS_OUT_WATER)) self.BUTTON_JUMP = TRUE; else @@ -706,12 +714,12 @@ void havocbot_movetogoal() if (trace_plane_normal_z < 0.7) { s = trace_fraction; - tracebox(self.origin + '0 0 16', self.mins, self.maxs, self.origin + self.velocity * 0.2 + '0 0 16', FALSE, self); + tracebox(self.origin + stepheightvec, self.mins, self.maxs, self.origin + self.velocity * 0.2 + stepheightvec, FALSE, self); if (trace_fraction < s + 0.01) if (trace_plane_normal_z < 0.7) { s = trace_fraction; - tracebox(self.origin + '0 0 48', self.mins, self.maxs, self.origin + self.velocity * 0.2 + '0 0 48', FALSE, self); + tracebox(self.origin + jumpstepheightvec, self.mins, self.maxs, self.origin + self.velocity * 0.2 + jumpstepheightvec, FALSE, self); if (trace_fraction > s) self.BUTTON_JUMP = 1; } @@ -781,7 +789,11 @@ void havocbot_movetogoal() { // Remove dangerous dynamic goals from stack if (self.goalcurrent.classname == "player" || self.goalcurrent.classname == "droppedweapon") + { navigation_poproute(); + if(self.goalcurrent) + gco = (self.goalcurrent.absmin + self.goalcurrent.absmax) * 0.5; + } // try to stop flatdir = '0 0 0'; evadeobstacle = normalize(self.velocity) * -1; @@ -802,7 +814,7 @@ void havocbot_movetogoal() dodge = havocbot_dodge(); dodge = dodge * bound(0,0.5+(skill+self.bot_dodgeskill)*0.1,1); evadelava = evadelava * bound(1,3-(skill+self.bot_dodgeskill),3); //Noobs fear lava a lot and take more distance from it - traceline(self.origin, self.enemy.origin, TRUE, world); + traceline(self.origin, ( ( self.enemy.absmin + self.enemy.absmax ) * 0.5 ), TRUE, world); if(trace_ent.classname == "player") dir = dir * bound(0,(skill+self.bot_dodgeskill)/7,1); @@ -872,9 +884,9 @@ void havocbot_chooseenemy() // and not really really far away // and we're not severely injured // then keep tracking for a half second into the future - traceline(self.origin+self.view_ofs, self.enemy.origin+self.enemy.view_ofs*0.5,FALSE,world); + traceline(self.origin+self.view_ofs, ( self.enemy.absmin + self.enemy.absmax ) * 0.5,FALSE,world); if (trace_ent == self.enemy || trace_fraction == 1) - if (vlen(self.enemy.origin - self.origin) < 1000) + if (vlen((( self.enemy.absmin + self.enemy.absmax ) * 0.5) - self.origin) < 1000) if (self.health > 30) { // remain tracking him for a shot while (case he went after a small corner or pilar @@ -918,7 +930,7 @@ void havocbot_chooseenemy() // I want to do a second scan if no enemy was found or I don't have weapons // TODO: Perform the scan when using the rifle (requires changes on the rifle code) - if(best || self.weapons) // || self.weapon == WEP_SNIPERRIFLE + if(best || self.weapons) // || self.weapon == WEP_RIFLE break; if(i) break; @@ -939,6 +951,31 @@ void havocbot_chooseenemy() self.havocbot_stickenemy = TRUE; }; +float havocbot_chooseweapon_checkreload(float new_weapon) +{ + // bots under this skill cannot find unloaded weapons to reload idly when not in combat, + // so skip this for them, or they'll never get to reload their weapons at all. + // this also allows bots under this skill to be more stupid, and reload more often during combat :) + if(skill < 5) + return FALSE; + + // if this weapon is scheduled for reloading, don't switch to it during combat + if (self.weapon_load[new_weapon] < 0) + { + local float i, other_weapon_available; + for(i = WEP_FIRST; i <= WEP_LAST; ++i) + { + // if we are out of ammo for all other weapons, it's an emergency to switch to anything else + if (weapon_action(i, WR_CHECKAMMO1) + weapon_action(i, WR_CHECKAMMO2)) + other_weapon_available = TRUE; + } + if(other_weapon_available) + return TRUE; + } + + return FALSE; +} + void havocbot_chooseweapon() { local float i; @@ -966,11 +1003,6 @@ void havocbot_chooseweapon() return; } - // Do not change weapon while reloading. If we do, the bot risks switching betwen empty weapons - // before getting to reload them, shooting none of them at all, giving lower overall performance - if(self.weapon && self.clip_load < 1) - return; - // Do not change weapon during the next second after a combo i = time - self.lastcombotime; if(i < 1) @@ -1008,8 +1040,9 @@ void havocbot_chooseweapon() if ( distance > bot_distance_far ) { for(i=0; i < WEP_COUNT && bot_weapons_far[i] != -1 ; ++i){ w = bot_weapons_far[i]; - if ( client_hasweapon(self, w, TRUE, FALSE) ){ - if ( self.weapon == w && combo) + if ( client_hasweapon(self, w, TRUE, FALSE) ) + { + if ((self.weapon == w && combo) || havocbot_chooseweapon_checkreload(w)) continue; self.switchweapon = w; return; @@ -1021,8 +1054,9 @@ void havocbot_chooseweapon() if ( distance > bot_distance_close) { for(i=0; i < WEP_COUNT && bot_weapons_mid[i] != -1 ; ++i){ w = bot_weapons_mid[i]; - if ( client_hasweapon(self, w, TRUE, FALSE) ){ - if ( self.weapon == w && combo) + if ( client_hasweapon(self, w, TRUE, FALSE) ) + { + if ((self.weapon == w && combo) || havocbot_chooseweapon_checkreload(w)) continue; self.switchweapon = w; return; @@ -1033,8 +1067,9 @@ void havocbot_chooseweapon() // Choose weapons for close distance for(i=0; i < WEP_COUNT && bot_weapons_close[i] != -1 ; ++i){ w = bot_weapons_close[i]; - if ( client_hasweapon(self, w, TRUE, FALSE) ){ - if ( self.weapon == w && combo) + if ( client_hasweapon(self, w, TRUE, FALSE) ) + { + if ((self.weapon == w && combo) || havocbot_chooseweapon_checkreload(w)) continue; self.switchweapon = w; return; @@ -1059,10 +1094,10 @@ void havocbot_aim() enemyvel = self.enemy.velocity; if (!self.enemy.waterlevel) enemyvel_z = 0; - lag_additem(time + self.ping, 0, 0, self.enemy, self.origin, selfvel, self.enemy.origin, enemyvel); + lag_additem(time + self.ping, 0, 0, self.enemy, self.origin, selfvel, (self.enemy.absmin + self.enemy.absmax) * 0.5, enemyvel); } else - lag_additem(time + self.ping, 0, 0, world, self.origin, selfvel, self.goalcurrent.origin, '0 0 0'); + lag_additem(time + self.ping, 0, 0, world, self.origin, selfvel, ( self.goalcurrent.absmin + self.goalcurrent.absmax ) * 0.5, '0 0 0'); }; float havocbot_moveto_refresh_route() @@ -1116,12 +1151,11 @@ float havocbot_moveto(vector pos) } } - #ifdef DEBUG_BOT_GOALSTACK + if(autocvar_bot_debug_goalstack) debuggoalstack(); - #endif // Heading - local vector dir = self.goalcurrent.origin - (self.origin + self.view_ofs); + local vector dir = ( ( self.goalcurrent.absmin + self.goalcurrent.absmax ) * 0.5 ) - (self.origin + self.view_ofs); dir_z = 0; bot_aimdir(dir, -1);