diff --git a/README.md b/README.md index e8acee9..5103076 100644 --- a/README.md +++ b/README.md @@ -161,6 +161,22 @@ You can easily setup a local LAN dedicated server for you to join and play on. H - bots_main_debug - a boolean value (0 or 1), enables or disables the waypoint editor ## Changelog +- v2.0.1 + - Reduced bots crouching + - Increased bots sprinting + - Improved bots mantling, crouching and knifing glass when needed + - Fixed possible script runtime errors + - Improved domination + - Bots use explosives more if they have it + - Bots aim slower when ads'ing + - Fixed bots holding breath + - Fixed bots rubberbanding movement when their goal changes + - Added bots quickscoping with snipers + - Added bots reload canceling and fast swaps + - Bots use C4 + - Improved revenge + - Bots can swap weapons on spawn more likely + - v2.0.0 - Initial reboot release diff --git a/main/waypoints b/main/waypoints index bfdf1b9..55d419c 160000 --- a/main/waypoints +++ b/main/waypoints @@ -1 +1 @@ -Subproject commit bfdf1b98f54f71bdc6651834d7555ce539f7f40f +Subproject commit 55d419cdb20b2bf89a883b1b59d79df1ed8381ac diff --git a/main_shared/maps/mp/bots/_bot.gsc b/main_shared/maps/mp/bots/_bot.gsc index 64ebb18..1d49140 100644 --- a/main_shared/maps/mp/bots/_bot.gsc +++ b/main_shared/maps/mp/bots/_bot.gsc @@ -8,7 +8,7 @@ */ init() { - level.bw_VERSION = "2.0.0"; + level.bw_VERSION = "2.0.1"; if(getDvar("bots_main") == "") setDvar("bots_main", true); diff --git a/out/Add to root of CoD4x server/main_shared/maps/mp/bots/_bot.gsc b/out/Add to root of CoD4x server/main_shared/maps/mp/bots/_bot.gsc index 64ebb18..1d49140 100644 --- a/out/Add to root of CoD4x server/main_shared/maps/mp/bots/_bot.gsc +++ b/out/Add to root of CoD4x server/main_shared/maps/mp/bots/_bot.gsc @@ -8,7 +8,7 @@ */ init() { - level.bw_VERSION = "2.0.0"; + level.bw_VERSION = "2.0.1"; if(getDvar("bots_main") == "") setDvar("bots_main", true); diff --git a/out/Add to root of CoD4x server/main_shared/maps/mp/bots/_bot_http.gsc b/out/Add to root of CoD4x server/main_shared/maps/mp/bots/_bot_http.gsc index ebbe01b..1d9f1a6 100644 --- a/out/Add to root of CoD4x server/main_shared/maps/mp/bots/_bot_http.gsc +++ b/out/Add to root of CoD4x server/main_shared/maps/mp/bots/_bot_http.gsc @@ -12,34 +12,34 @@ */ getRemoteWaypoints(mapname) { - url = "https://raw.githubusercontent.com/ineedbots/cod4x_waypoints/master/" + mapname + "_wp.csv"; + url = "https://raw.githubusercontent.com/ineedbots/cod4x_waypoints/master/" + mapname + "_wp.csv"; filename = "waypoints/" + mapname + "_wp.csv"; - printToConsole("Attempting to get remote waypoints from " + url); - res = getLinesFromUrl(url, filename); + printToConsole("Attempting to get remote waypoints from " + url); + res = getLinesFromUrl(url, filename); - if (!res.lines.size) - return; + if (!res.lines.size) + return; - waypointCount = int(res.lines[0]); + waypointCount = int(res.lines[0]); waypoints = []; - printToConsole("Loading remote waypoints..."); + printToConsole("Loading remote waypoints..."); for (i = 1; i <= waypointCount; i++) - { - tokens = tokenizeLine(res.lines[i], ","); - - waypoint = parseTokensIntoWaypoint(tokens); + { + tokens = tokenizeLine(res.lines[i], ","); + + waypoint = parseTokensIntoWaypoint(tokens); - waypoints[i-1] = waypoint; - } + waypoints[i-1] = waypoint; + } - if (waypoints.size) - { - level.waypoints = waypoints; - printToConsole("Loaded " + waypoints.size + " waypoints from remote."); - } + if (waypoints.size) + { + level.waypoints = waypoints; + printToConsole("Loaded " + waypoints.size + " waypoints from remote."); + } } /* @@ -88,13 +88,13 @@ getRemoteVersion() */ getLinesFromUrl(url, filename) { - result = spawnStruct(); - result.lines = []; + result = spawnStruct(); + result.lines = []; #if isSyscallDefined HTTPS_GetString - data = HTTPS_GetString( url ); + data = HTTPS_GetString( url ); #else - data = undefined; + data = undefined; #endif if (!isDefined(data)) @@ -126,7 +126,7 @@ getLinesFromUrl(url, filename) line += c; } - result.lines[result.lines.size] = line; + result.lines[result.lines.size] = line; if (fd > 0) FS_FClose(fd); diff --git a/out/Add to root of CoD4x server/main_shared/maps/mp/bots/_bot_internal.gsc b/out/Add to root of CoD4x server/main_shared/maps/mp/bots/_bot_internal.gsc index dc9a0b5..803fd83 100644 --- a/out/Add to root of CoD4x server/main_shared/maps/mp/bots/_bot_internal.gsc +++ b/out/Add to root of CoD4x server/main_shared/maps/mp/bots/_bot_internal.gsc @@ -32,6 +32,8 @@ added() self.pers["bots"]["skill"]["aim_offset_amount"] = 1; // how far a bot's incorrect aim is self.pers["bots"]["skill"]["bone_update_interval"] = 0.05; // how often a bot changes their bone target self.pers["bots"]["skill"]["bones"] = "j_head"; // a list of comma seperated bones the bot will aim at + self.pers["bots"]["skill"]["ads_fov_multi"] = 0.5; // a factor of how much ads to reduce when adsing + self.pers["bots"]["skill"]["ads_aimspeed_multi"] = 0.5; // a factor of how much more aimspeed delay to add self.pers["bots"]["behavior"] = []; self.pers["bots"]["behavior"]["strafe"] = 50; // percentage of how often the bot strafes a target @@ -43,6 +45,9 @@ added() self.pers["bots"]["behavior"]["switch"] = 1; // percentage of how often the bot will switch weapons self.pers["bots"]["behavior"]["class"] = 1; // percentage of how often the bot will change classes self.pers["bots"]["behavior"]["jump"] = 100; // percentage of how often the bot will jumpshot and dropshot + + self.pers["bots"]["behavior"]["quickscope"] = false; // is a quickscoper + self.pers["bots"]["behavior"]["initswitch"] = 10; // percentage of how often the bot will switch weapons on spawn } /* @@ -131,6 +136,8 @@ resetBotVars() self.bot.stop_move = false; self.bot.greedy_path = false; self.bot.climbing = false; + self.bot.last_next_wp = -1; + self.bot.last_second_next_wp = -1; self.bot.isfrozen = false; self.bot.sprintendtime = -1; @@ -145,8 +152,11 @@ resetBotVars() self.bot.semi_time = false; self.bot.jump_time = undefined; + self.bot.last_fire_time = -1; self.bot.is_cur_full_auto = false; + self.bot.cur_weap_dist_multi = 1; + self.bot.is_cur_sniper = false; self.bot.rand = randomInt(100); @@ -213,10 +223,162 @@ spawned() self thread aim(); self thread watchHoldBreath(); self thread onNewEnemy(); + self thread doBotMovement(); + self thread watchGrenadeFire(); self notify("bot_spawned"); } +/* + Watches when the bot fires a grenade +*/ +watchGrenadeFire() +{ + self endon("disconnect"); + self endon("death"); + + for (;;) + { + self waittill( "grenade_fire", nade, weapname ); + + if ( weapname == "c4_mp" ) + self thread watchC4Thrown(nade); + } +} + +/* + Watches the c4 +*/ +watchC4Thrown(c4) +{ + self endon("disconnect"); + c4 endon("death"); + + wait 0.5; + + for (;;) + { + wait 1 + randomInt(50) * 0.05; + + shouldBreak = false; + for (i = 0; i < level.players.size; i++) + { + player = level.players[i]; + + if(player == self) + continue; + + if((level.teamBased && self.team == player.team) || player.sessionstate != "playing" || !isAlive(player)) + continue; + + if (distanceSquared(c4.origin, player.origin) > 200*200) + continue; + + if (!bulletTracePassed(c4.origin, player.origin + (0, 0, 25), false, c4)) + continue; + + shouldBreak = true; + } + + if (shouldBreak) + break; + } + + weap = self getCurrentWeapon(); + if ( weap != "c4_mp" ) + self notify( "alt_detonate" ); + else + self thread pressFire(); +} + +/* + Bot moves towards the point +*/ +doBotMovement() +{ + self endon("disconnect"); + self endon("death"); + + FORWARDAMOUNT = 25; + + for (i = 0;;i+=0.05) + { + wait 0.05; + + angles = self GetPlayerAngles(); + + // climb through windows + if (self isMantling()) + self crouch(); + + + startPos = self.origin + (0, 0, 50); + startPosForward = startPos + anglesToForward((0, angles[1], 0)) * FORWARDAMOUNT; + bt = bulletTrace(startPos, startPosForward, false, self); + if (bt["fraction"] >= 1) + { + // check if need to jump + bt = bulletTrace(startPosForward, startPosForward - (0, 0, 40), false, self); + + if (bt["fraction"] < 1 && bt["normal"][2] > 0.9 && i > 1.5 && !self isOnLadder()) + { + i = 0; + self thread jump(); + } + } + // check if need to knife glass + else if (bt["surfacetype"] == "glass") + { + if (i > 1.5) + { + i = 0; + self thread knife(); + } + } + else + { + // check if need to crouch + if (bulletTracePassed(startPos - (0, 0, 25), startPosForward - (0, 0, 25), false, self)) + self crouch(); + } + } +} + +/* + Sets the factor of distance for a weapon +*/ +SetWeaponDistMulti(weap) +{ + if (weap == "none") + return 1; + + switch(weaponClass(weap)) + { + case "rifle": + return 0.9; + case "smg": + return 0.7; + case "pistol": + return 0.5; + default: + return 1; + } +} + +/* + Is the weap a sniper +*/ +IsWeapSniper(weap) +{ + if (weap == "none") + return false; + + if (maps\mp\gametypes\_missions::getWeaponClass(weap) != "weapon_sniper") + return false; + + return true; +} + /* The hold breath thread. */ @@ -232,7 +394,7 @@ watchHoldBreath() if(self.bot.isfrozen) continue; - self holdbreath((self playerADS() && weaponClass(self getCurrentWEapon()) == "rifle")); + self holdbreath(self playerADS() > 0); } } @@ -276,22 +438,25 @@ onWeaponChange() { self endon("disconnect"); self endon("death"); - - weap = self GetCurrentWeapon(); - self.bot.is_cur_full_auto = WeaponIsFullAuto(weap); - if (weap != "none") - self changeToWeap(weap); + first = true; for(;;) { - self waittill( "weapon_change", newWeapon ); + newWeapon = undefined; + if (first) + { + first = false; + newWeapon = self getCurrentWeapon(); + } + else + self waittill( "weapon_change", newWeapon ); self.bot.is_cur_full_auto = WeaponIsFullAuto(newWeapon); + self.bot.cur_weap_dist_multi = SetWeaponDistMulti(newWeapon); + self.bot.is_cur_sniper = IsWeapSniper(newWeapon); if (newWeapon == "none") - { continue; - } self changeToWeap(newWeapon); } @@ -327,7 +492,18 @@ reload_watch() { self waittill("reload_start"); self.bot.isreloading = true; - self waittill_notify_or_timeout("reload", 7.5); + + while(true) + { + ret = self waittill_any_timeout(7.5, "reload"); + + if (ret == "timeout") + break; + + weap = self GetCurrentWeapon(); + if (self GetWeaponAmmoClip(weap) >= WeaponClipSize(weap)) + break; + } self.bot.isreloading = false; } } @@ -376,17 +552,25 @@ stance() self prone(); curweap = self getCurrentWeapon(); - + time = getTime(); + chance = self.pers["bots"]["behavior"]["sprint"]; + + if (time - self.lastSpawnTime < 5000) + chance *= 2; + + if(isDefined(self.bot.script_goal) && DistanceSquared(self.origin, self.bot.script_goal) > 256*256) + chance *= 2; + if(toStance != "stand" || self.bot.isreloading || self.bot.issprinting || self.bot.isfraggingafter || self.bot.issmokingafter) continue; - if(randomInt(100) > self.pers["bots"]["behavior"]["sprint"]) + if(randomInt(100) > chance) continue; if(isDefined(self.bot.target) && self canFire(curweap) && self isInRange(self.bot.target.dist, curweap)) continue; - if(self.bot.sprintendtime != -1 && getTime() - self.bot.sprintendtime < 2000) + if(self.bot.sprintendtime != -1 && time - self.bot.sprintendtime < 2000) continue; if(!isDefined(self.bot.towards_goal) || DistanceSquared(self.origin, self.bot.towards_goal) < level.bots_minSprintDistance || getConeDot(self.bot.towards_goal, self.origin, self GetPlayerAngles()) < 0.75) @@ -582,9 +766,11 @@ updateAimOffset(obj) targetObjUpdateTraced(obj, daDist, ent, theTime, isScriptObj) { distClose = self.pers["bots"]["skill"]["dist_start"]; + distClose *= self.bot.cur_weap_dist_multi; distClose *= distClose; distMax = self.pers["bots"]["skill"]["dist_max"]; + distMax *= self.bot.cur_weap_dist_multi; distMax *= distMax; timeMulti = 1; @@ -635,13 +821,18 @@ target() myAngles = self GetPlayerAngles(); myFov = self.pers["bots"]["skill"]["fov"]; bestTargets = []; - bestTime = 9999999999; + bestTime = 2147483647; rememberTime = self.pers["bots"]["skill"]["remember_time"]; initReactTime = self.pers["bots"]["skill"]["init_react_time"]; hasTarget = isDefined(self.bot.target); + adsAmount = self PlayerADS(); + adsFovFact = self.pers["bots"]["skill"]["ads_fov_multi"]; // reduce fov if ads'ing - myFov *= 1 - 0.5 * self PlayerADS(); + if (adsAmount > 0) + { + myFov *= 1 - adsFovFact * adsAmount; + } if(hasTarget && !isDefined(self.bot.target.entity)) { @@ -782,7 +973,7 @@ target() if(hasTarget && isDefined(bestTargets[self.bot.target.entity getEntityNumber()+""])) continue; - closest = 9999999999; + closest = 2147483647; toBeTarget = undefined; bestKeys = getArrayKeys(bestTargets); @@ -873,6 +1064,9 @@ watchToLook() if(!self isInRange(self.bot.target.dist, curweap)) continue; + + if (self.bot.is_cur_sniper) + continue; if(randomInt(100) > self.pers["bots"]["behavior"]["jump"]) continue; @@ -958,6 +1152,14 @@ aim() eyePos = self getEyePos(); curweap = self getCurrentWeapon(); angles = self GetPlayerAngles(); + adsAmount = self PlayerADS(); + adsAimSpeedFact = self.pers["bots"]["skill"]["ads_aimspeed_multi"]; + + // reduce aimspeed if ads'ing + if (adsAmount > 0) + { + aimspeed *= 1 + adsAimSpeedFact * adsAmount; + } if(isDefined(self.bot.target) && isDefined(self.bot.target.entity)) { @@ -1020,7 +1222,10 @@ aim() else { if (self canAds(dist, curweap)) - self thread pressADS(); + { + if (!self.bot.is_cur_sniper || !self.pers["bots"]["behavior"]["quickscope"]) + self thread pressAds(); + } } self botLookAt(last_pos + (0, 0, self getEyeHeight() + nadeAimOffset), aimspeed); @@ -1031,7 +1236,14 @@ aim() { if(isplay) { + if (!target IsPlayerModelOK()) + continue; + aimpos = target getTagOrigin( bone ); + + if (!isDefined(aimpos)) + continue; + aimpos += offset; aimpos += aimoffset; aimpos += (0, 0, nadeAimOffset); @@ -1065,15 +1277,25 @@ aim() if(!self canFire(curweap) || !self isInRange(dist, curweap)) continue; - //c4 logic here, but doesnt work anyway - - canADS = self canAds(dist, curweap); + canADS = (self canAds(dist, curweap) && conedot > 0.75); if (canADS) - self thread pressADS(); + { + stopAdsOverride = false; + if (self.bot.is_cur_sniper) + { + if (self.pers["bots"]["behavior"]["quickscope"] && self.bot.last_fire_time != -1 && getTime() - self.bot.last_fire_time < 1000) + stopAdsOverride = true; + else + self notify("kill_goal"); + } + + if (!stopAdsOverride) + self thread pressAds(); + } if (trace_time > reaction_time) { - if((!canADS || self playerads() == 1.0 || self InLastStand() || self GetStance() == "prone") && (conedot > 0.95 || dist < level.bots_maxKnifeDistance) && getDvarInt("bots_play_fire")) + if((!canADS || adsAmount >= 1.0 || self InLastStand() || self GetStance() == "prone") && (conedot > 0.99 || dist < level.bots_maxKnifeDistance) && getDvarInt("bots_play_fire")) self botFire(); if (isplay) @@ -1104,11 +1326,23 @@ aim() if(!self canFire(curweap) || !self isInRange(dist, curweap)) continue; - canADS = self canAds(dist, curweap); + canADS = (self canAds(dist, curweap) && conedot > 0.75); if (canADS) - self thread pressADS(); + { + stopAdsOverride = false; + if (self.bot.is_cur_sniper) + { + if (self.pers["bots"]["behavior"]["quickscope"] && self.bot.last_fire_time != -1 && getTime() - self.bot.last_fire_time < 1000) + stopAdsOverride = true; + else + self notify("kill_goal"); + } - if((!canADS || self playerads() == 1.0 || self InLastStand() || self GetStance() == "prone") && (conedot > 0.95 || dist < level.bots_maxKnifeDistance) && getDvarInt("bots_play_fire")) + if (!stopAdsOverride) + self thread pressAds(); + } + + if((!canADS || adsAmount >= 1.0 || self InLastStand() || self GetStance() == "prone") && (conedot > 0.95 || dist < level.bots_maxKnifeDistance) && getDvarInt("bots_play_fire")) self botFire(); continue; @@ -1144,6 +1378,8 @@ aim() */ botFire() { + self.bot.last_fire_time = getTime(); + if(self.bot.is_cur_full_auto) { self thread pressFire(); @@ -1258,6 +1494,8 @@ walk() if(self maps\mp\_flashgrenades::isFlashbanged()) { + self.bot.last_next_wp = -1; + self.bot.last_second_next_wp = -1; self botMoveTo(self.origin + self GetVelocity()*500); continue; } @@ -1274,7 +1512,7 @@ walk() if(self.bot.target.isplay && self.bot.target.trace_time && self canFire(curweap) && self isInRange(self.bot.target.dist, curweap)) { - if (self InLastStand() || self GetStance() == "prone") + if (self InLastStand() || self GetStance() == "prone" || (self.bot.is_cur_sniper && self PlayerADS() > 0)) continue; if(self.bot.target.rand <= self.pers["bots"]["behavior"]["strafe"]) @@ -1370,6 +1608,8 @@ strafe(target) if(traceRight["fraction"] > traceLeft["fraction"]) strafe = traceRight["position"]; + self.bot.last_next_wp = -1; + self.bot.last_second_next_wp = -1; self botMoveTo(strafe); wait 2; self notify("kill_goal"); @@ -1481,39 +1721,34 @@ doWalk(goal, dist, isScriptGoal) self thread watchOnGoal(goal, distsq); current = self initAStar(goal); - // if a waypoint is closer than the goal - //if (current >= 0 && DistanceSquared(self.origin, level.waypoints[self.bot.astar[current]].origin) < DistanceSquared(self.origin, goal)) - //{ - while(current >= 0) + // skip waypoints we already completed to prevent rubber banding + if (current > 0 && self.bot.astar[current] == self.bot.last_next_wp && self.bot.astar[current-1] == self.bot.last_second_next_wp) + current = self removeAStar(); + + if (current >= 0) + { + // check if a waypoint is closer than the goal + wpOrg = level.waypoints[self.bot.astar[current]].origin; + ppt = PlayerPhysicsTrace(self.origin + (0,0,32), wpOrg, false, self); + if (DistanceSquared(self.origin, wpOrg) < DistanceSquared(self.origin, goal) || DistanceSquared(wpOrg, ppt) > 1.0) { - // skip down the line of waypoints and go to the waypoint we have a direct path too - /*for (;;) + while(current >= 0) { - if (current <= 0) - break; - - ppt = PlayerPhysicsTrace(self.origin + (0,0,32), level.waypoints[self.bot.astar[current-1]].origin, false, self); - if (DistanceSquared(level.waypoints[self.bot.astar[current-1]].origin, ppt) > 1.0) - break; - - if (level.waypoints[self.bot.astar[current-1]].type == "climb" || level.waypoints[self.bot.astar[current]].type == "climb") - break; - + self.bot.next_wp = self.bot.astar[current]; + self.bot.second_next_wp = -1; + if(current > 0) + self.bot.second_next_wp = self.bot.astar[current-1]; + + self notify("new_static_waypoint"); + + self movetowards(level.waypoints[self.bot.next_wp].origin); + self.bot.last_next_wp = self.bot.next_wp; + self.bot.last_second_next_wp = self.bot.second_next_wp; + current = self removeAStar(); - }*/ - - self.bot.next_wp = self.bot.astar[current]; - self.bot.second_next_wp = -1; - if(current != 0) - self.bot.second_next_wp = self.bot.astar[current-1]; - - self notify("new_static_waypoint"); - - self movetowards(level.waypoints[self.bot.next_wp].origin); - - current = self removeAStar(); + } } - //} + } self.bot.next_wp = -1; self.bot.second_next_wp = -1; @@ -1521,6 +1756,8 @@ doWalk(goal, dist, isScriptGoal) if(DistanceSquared(self.origin, goal) > distsq) { + self.bot.last_next_wp = -1; + self.bot.last_second_next_wp = -1; self movetowards(goal); // any better way?? } @@ -1549,17 +1786,18 @@ movetowards(goal) { self botMoveTo(goal); - if(time > 3) + if(time > 3.5) { time = 0; if(distanceSquared(self.origin, lastOri) < 128) { + self thread knife(); + wait 0.5; + stucks++; randomDir = self getRandomLargestStafe(stucks); - self knife(); // knife glass - wait 0.25; self botMoveTo(randomDir); wait stucks; } @@ -1568,7 +1806,7 @@ movetowards(goal) } else if(timeslow > 1.5) { - self thread jump(); + self thread doMantle(); } else if(timeslow > 0.75) { @@ -1590,6 +1828,22 @@ movetowards(goal) self notify("completed_move_to"); } +/* + Bots do the mantle +*/ +doMantle() +{ + self endon("disconnect"); + self endon("death"); + self endon("kill_goal"); + + self jump(); + + wait 0.35; + + self jump(); +} + /* Will return the pos of the largest trace from the bot. */ diff --git a/out/Add to root of CoD4x server/main_shared/maps/mp/bots/_bot_script.gsc b/out/Add to root of CoD4x server/main_shared/maps/mp/bots/_bot_script.gsc index 0e8aeb9..27a1c0e 100644 --- a/out/Add to root of CoD4x server/main_shared/maps/mp/bots/_bot_script.gsc +++ b/out/Add to root of CoD4x server/main_shared/maps/mp/bots/_bot_script.gsc @@ -28,6 +28,7 @@ connected() self endon("disconnect"); self.killerLocation = undefined; + self.lastKiller = undefined; self thread difficulty(); self thread teamWatch(); @@ -44,6 +45,7 @@ connected() onKilled(eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration) { self.killerLocation = undefined; + self.lastKiller = undefined; if(!IsDefined( self ) || !isDefined(self.team)) return; @@ -70,6 +72,7 @@ onKilled(eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, return; self.killerLocation = eAttacker.origin; + self.lastKiller = eAttacker; } /* @@ -260,219 +263,233 @@ difficulty() for(;;) { - wait 1; - rankVar = GetDvarInt("bots_skill"); - if(rankVar == 9) - continue; - - switch(self.pers["bots"]["skill"]["base"]) + if(rankVar != 9) { - case 1: - self.pers["bots"]["skill"]["aim_time"] = 0.6; - self.pers["bots"]["skill"]["init_react_time"] = 1500; - self.pers["bots"]["skill"]["reaction_time"] = 1000; - self.pers["bots"]["skill"]["no_trace_ads_time"] = 500; - self.pers["bots"]["skill"]["no_trace_look_time"] = 600; - self.pers["bots"]["skill"]["remember_time"] = 750; - self.pers["bots"]["skill"]["fov"] = 0.7; - self.pers["bots"]["skill"]["dist_max"] = 2500; - self.pers["bots"]["skill"]["dist_start"] = 1000; - self.pers["bots"]["skill"]["spawn_time"] = 0.75; - self.pers["bots"]["skill"]["help_dist"] = 0; - self.pers["bots"]["skill"]["semi_time"] = 0.9; - self.pers["bots"]["skill"]["shoot_after_time"] = 1; - self.pers["bots"]["skill"]["aim_offset_time"] = 1.5; - self.pers["bots"]["skill"]["aim_offset_amount"] = 4; - self.pers["bots"]["skill"]["bone_update_interval"] = 2; - self.pers["bots"]["skill"]["bones"] = "j_spineupper,j_ankle_le,j_ankle_ri"; + switch(self.pers["bots"]["skill"]["base"]) + { + case 1: + self.pers["bots"]["skill"]["aim_time"] = 0.6; + self.pers["bots"]["skill"]["init_react_time"] = 1500; + self.pers["bots"]["skill"]["reaction_time"] = 1000; + self.pers["bots"]["skill"]["no_trace_ads_time"] = 500; + self.pers["bots"]["skill"]["no_trace_look_time"] = 600; + self.pers["bots"]["skill"]["remember_time"] = 750; + self.pers["bots"]["skill"]["fov"] = 0.7; + self.pers["bots"]["skill"]["dist_max"] = 2500; + self.pers["bots"]["skill"]["dist_start"] = 1000; + self.pers["bots"]["skill"]["spawn_time"] = 0.75; + self.pers["bots"]["skill"]["help_dist"] = 0; + self.pers["bots"]["skill"]["semi_time"] = 0.9; + self.pers["bots"]["skill"]["shoot_after_time"] = 1; + self.pers["bots"]["skill"]["aim_offset_time"] = 1.5; + self.pers["bots"]["skill"]["aim_offset_amount"] = 4; + self.pers["bots"]["skill"]["bone_update_interval"] = 2; + self.pers["bots"]["skill"]["bones"] = "j_spineupper,j_ankle_le,j_ankle_ri"; + self.pers["bots"]["skill"]["ads_fov_multi"] = 0.5; + self.pers["bots"]["skill"]["ads_aimspeed_multi"] = 0.5; - self.pers["bots"]["behavior"]["strafe"] = 0; - self.pers["bots"]["behavior"]["nade"] = 10; - self.pers["bots"]["behavior"]["sprint"] = 10; - self.pers["bots"]["behavior"]["camp"] = 5; - self.pers["bots"]["behavior"]["follow"] = 5; - self.pers["bots"]["behavior"]["crouch"] = 70; - self.pers["bots"]["behavior"]["switch"] = 2; - self.pers["bots"]["behavior"]["class"] = 2; - self.pers["bots"]["behavior"]["jump"] = 0; - break; - case 2: - self.pers["bots"]["skill"]["aim_time"] = 0.55; - self.pers["bots"]["skill"]["init_react_time"] = 1000; - self.pers["bots"]["skill"]["reaction_time"] = 800; - self.pers["bots"]["skill"]["no_trace_ads_time"] = 1000; - self.pers["bots"]["skill"]["no_trace_look_time"] = 1250; - self.pers["bots"]["skill"]["remember_time"] = 1500; - self.pers["bots"]["skill"]["fov"] = 0.65; - self.pers["bots"]["skill"]["dist_max"] = 3000; - self.pers["bots"]["skill"]["dist_start"] = 1500; - self.pers["bots"]["skill"]["spawn_time"] = 0.65; - self.pers["bots"]["skill"]["help_dist"] = 500; - self.pers["bots"]["skill"]["semi_time"] = 0.75; - self.pers["bots"]["skill"]["shoot_after_time"] = 0.75; - self.pers["bots"]["skill"]["aim_offset_time"] = 1; - self.pers["bots"]["skill"]["aim_offset_amount"] = 3; - self.pers["bots"]["skill"]["bone_update_interval"] = 1.5; - self.pers["bots"]["skill"]["bones"] = "j_spineupper,j_ankle_le,j_ankle_ri,j_head"; + self.pers["bots"]["behavior"]["strafe"] = 0; + self.pers["bots"]["behavior"]["nade"] = 10; + self.pers["bots"]["behavior"]["sprint"] = 30; + self.pers["bots"]["behavior"]["camp"] = 5; + self.pers["bots"]["behavior"]["follow"] = 5; + self.pers["bots"]["behavior"]["crouch"] = 20; + self.pers["bots"]["behavior"]["switch"] = 2; + self.pers["bots"]["behavior"]["class"] = 2; + self.pers["bots"]["behavior"]["jump"] = 0; + break; + case 2: + self.pers["bots"]["skill"]["aim_time"] = 0.55; + self.pers["bots"]["skill"]["init_react_time"] = 1000; + self.pers["bots"]["skill"]["reaction_time"] = 800; + self.pers["bots"]["skill"]["no_trace_ads_time"] = 1000; + self.pers["bots"]["skill"]["no_trace_look_time"] = 1250; + self.pers["bots"]["skill"]["remember_time"] = 1500; + self.pers["bots"]["skill"]["fov"] = 0.65; + self.pers["bots"]["skill"]["dist_max"] = 3000; + self.pers["bots"]["skill"]["dist_start"] = 1500; + self.pers["bots"]["skill"]["spawn_time"] = 0.65; + self.pers["bots"]["skill"]["help_dist"] = 500; + self.pers["bots"]["skill"]["semi_time"] = 0.75; + self.pers["bots"]["skill"]["shoot_after_time"] = 0.75; + self.pers["bots"]["skill"]["aim_offset_time"] = 1; + self.pers["bots"]["skill"]["aim_offset_amount"] = 3; + self.pers["bots"]["skill"]["bone_update_interval"] = 1.5; + self.pers["bots"]["skill"]["bones"] = "j_spineupper,j_ankle_le,j_ankle_ri,j_head"; + self.pers["bots"]["skill"]["ads_fov_multi"] = 0.5; + self.pers["bots"]["skill"]["ads_aimspeed_multi"] = 0.5; - self.pers["bots"]["behavior"]["strafe"] = 10; - self.pers["bots"]["behavior"]["nade"] = 15; - self.pers["bots"]["behavior"]["sprint"] = 15; - self.pers["bots"]["behavior"]["camp"] = 5; - self.pers["bots"]["behavior"]["follow"] = 5; - self.pers["bots"]["behavior"]["crouch"] = 60; - self.pers["bots"]["behavior"]["switch"] = 2; - self.pers["bots"]["behavior"]["class"] = 2; - self.pers["bots"]["behavior"]["jump"] = 10; - break; - case 3: - self.pers["bots"]["skill"]["aim_time"] = 0.4; - self.pers["bots"]["skill"]["init_react_time"] = 750; - self.pers["bots"]["skill"]["reaction_time"] = 500; - self.pers["bots"]["skill"]["no_trace_ads_time"] = 1000; - self.pers["bots"]["skill"]["no_trace_look_time"] = 1500; - self.pers["bots"]["skill"]["remember_time"] = 2000; - self.pers["bots"]["skill"]["fov"] = 0.6; - self.pers["bots"]["skill"]["dist_max"] = 4000; - self.pers["bots"]["skill"]["dist_start"] = 2250; - self.pers["bots"]["skill"]["spawn_time"] = 0.5; - self.pers["bots"]["skill"]["help_dist"] = 750; - self.pers["bots"]["skill"]["semi_time"] = 0.65; - self.pers["bots"]["skill"]["shoot_after_time"] = 0.65; - self.pers["bots"]["skill"]["aim_offset_time"] = 0.75; - self.pers["bots"]["skill"]["aim_offset_amount"] = 2.5; - self.pers["bots"]["skill"]["bone_update_interval"] = 1; - self.pers["bots"]["skill"]["bones"] = "j_spineupper,j_spineupper,j_ankle_le,j_ankle_ri,j_head"; + self.pers["bots"]["behavior"]["strafe"] = 10; + self.pers["bots"]["behavior"]["nade"] = 15; + self.pers["bots"]["behavior"]["sprint"] = 45; + self.pers["bots"]["behavior"]["camp"] = 5; + self.pers["bots"]["behavior"]["follow"] = 5; + self.pers["bots"]["behavior"]["crouch"] = 15; + self.pers["bots"]["behavior"]["switch"] = 2; + self.pers["bots"]["behavior"]["class"] = 2; + self.pers["bots"]["behavior"]["jump"] = 10; + break; + case 3: + self.pers["bots"]["skill"]["aim_time"] = 0.4; + self.pers["bots"]["skill"]["init_react_time"] = 750; + self.pers["bots"]["skill"]["reaction_time"] = 500; + self.pers["bots"]["skill"]["no_trace_ads_time"] = 1000; + self.pers["bots"]["skill"]["no_trace_look_time"] = 1500; + self.pers["bots"]["skill"]["remember_time"] = 2000; + self.pers["bots"]["skill"]["fov"] = 0.6; + self.pers["bots"]["skill"]["dist_max"] = 4000; + self.pers["bots"]["skill"]["dist_start"] = 2250; + self.pers["bots"]["skill"]["spawn_time"] = 0.5; + self.pers["bots"]["skill"]["help_dist"] = 750; + self.pers["bots"]["skill"]["semi_time"] = 0.65; + self.pers["bots"]["skill"]["shoot_after_time"] = 0.65; + self.pers["bots"]["skill"]["aim_offset_time"] = 0.75; + self.pers["bots"]["skill"]["aim_offset_amount"] = 2.5; + self.pers["bots"]["skill"]["bone_update_interval"] = 1; + self.pers["bots"]["skill"]["bones"] = "j_spineupper,j_spineupper,j_ankle_le,j_ankle_ri,j_head"; + self.pers["bots"]["skill"]["ads_fov_multi"] = 0.5; + self.pers["bots"]["skill"]["ads_aimspeed_multi"] = 0.5; - self.pers["bots"]["behavior"]["strafe"] = 20; - self.pers["bots"]["behavior"]["nade"] = 20; - self.pers["bots"]["behavior"]["sprint"] = 20; - self.pers["bots"]["behavior"]["camp"] = 5; - self.pers["bots"]["behavior"]["follow"] = 5; - self.pers["bots"]["behavior"]["crouch"] = 50; - self.pers["bots"]["behavior"]["switch"] = 2; - self.pers["bots"]["behavior"]["class"] = 2; - self.pers["bots"]["behavior"]["jump"] = 25; - break; - case 4: - self.pers["bots"]["skill"]["aim_time"] = 0.3; - self.pers["bots"]["skill"]["init_react_time"] = 600; - self.pers["bots"]["skill"]["reaction_time"] = 400; - self.pers["bots"]["skill"]["no_trace_ads_time"] = 1000; - self.pers["bots"]["skill"]["no_trace_look_time"] = 1500; - self.pers["bots"]["skill"]["remember_time"] = 3000; - self.pers["bots"]["skill"]["fov"] = 0.55; - self.pers["bots"]["skill"]["dist_max"] = 5000; - self.pers["bots"]["skill"]["dist_start"] = 3350; - self.pers["bots"]["skill"]["spawn_time"] = 0.35; - self.pers["bots"]["skill"]["help_dist"] = 1000; - self.pers["bots"]["skill"]["semi_time"] = 0.5; - self.pers["bots"]["skill"]["shoot_after_time"] = 0.5; - self.pers["bots"]["skill"]["aim_offset_time"] = 0.5; - self.pers["bots"]["skill"]["aim_offset_amount"] = 2; - self.pers["bots"]["skill"]["bone_update_interval"] = 0.75; - self.pers["bots"]["skill"]["bones"] = "j_spineupper,j_spineupper,j_ankle_le,j_ankle_ri,j_head,j_head"; + self.pers["bots"]["behavior"]["strafe"] = 20; + self.pers["bots"]["behavior"]["nade"] = 20; + self.pers["bots"]["behavior"]["sprint"] = 50; + self.pers["bots"]["behavior"]["camp"] = 5; + self.pers["bots"]["behavior"]["follow"] = 5; + self.pers["bots"]["behavior"]["crouch"] = 10; + self.pers["bots"]["behavior"]["switch"] = 2; + self.pers["bots"]["behavior"]["class"] = 2; + self.pers["bots"]["behavior"]["jump"] = 25; + break; + case 4: + self.pers["bots"]["skill"]["aim_time"] = 0.3; + self.pers["bots"]["skill"]["init_react_time"] = 600; + self.pers["bots"]["skill"]["reaction_time"] = 400; + self.pers["bots"]["skill"]["no_trace_ads_time"] = 1000; + self.pers["bots"]["skill"]["no_trace_look_time"] = 1500; + self.pers["bots"]["skill"]["remember_time"] = 3000; + self.pers["bots"]["skill"]["fov"] = 0.55; + self.pers["bots"]["skill"]["dist_max"] = 5000; + self.pers["bots"]["skill"]["dist_start"] = 3350; + self.pers["bots"]["skill"]["spawn_time"] = 0.35; + self.pers["bots"]["skill"]["help_dist"] = 1000; + self.pers["bots"]["skill"]["semi_time"] = 0.5; + self.pers["bots"]["skill"]["shoot_after_time"] = 0.5; + self.pers["bots"]["skill"]["aim_offset_time"] = 0.5; + self.pers["bots"]["skill"]["aim_offset_amount"] = 2; + self.pers["bots"]["skill"]["bone_update_interval"] = 0.75; + self.pers["bots"]["skill"]["bones"] = "j_spineupper,j_spineupper,j_ankle_le,j_ankle_ri,j_head,j_head"; + self.pers["bots"]["skill"]["ads_fov_multi"] = 0.5; + self.pers["bots"]["skill"]["ads_aimspeed_multi"] = 0.5; - self.pers["bots"]["behavior"]["strafe"] = 30; - self.pers["bots"]["behavior"]["nade"] = 25; - self.pers["bots"]["behavior"]["sprint"] = 30; - self.pers["bots"]["behavior"]["camp"] = 5; - self.pers["bots"]["behavior"]["follow"] = 5; - self.pers["bots"]["behavior"]["crouch"] = 40; - self.pers["bots"]["behavior"]["switch"] = 2; - self.pers["bots"]["behavior"]["class"] = 2; - self.pers["bots"]["behavior"]["jump"] = 35; - break; - case 5: - self.pers["bots"]["skill"]["aim_time"] = 0.25; - self.pers["bots"]["skill"]["init_react_time"] = 500; - self.pers["bots"]["skill"]["reaction_time"] = 300; - self.pers["bots"]["skill"]["no_trace_ads_time"] = 1500; - self.pers["bots"]["skill"]["no_trace_look_time"] = 2000; - self.pers["bots"]["skill"]["remember_time"] = 4000; - self.pers["bots"]["skill"]["fov"] = 0.5; - self.pers["bots"]["skill"]["dist_max"] = 7500; - self.pers["bots"]["skill"]["dist_start"] = 5000; - self.pers["bots"]["skill"]["spawn_time"] = 0.25; - self.pers["bots"]["skill"]["help_dist"] = 1500; - self.pers["bots"]["skill"]["semi_time"] = 0.4; - self.pers["bots"]["skill"]["shoot_after_time"] = 0.35; - self.pers["bots"]["skill"]["aim_offset_time"] = 0.35; - self.pers["bots"]["skill"]["aim_offset_amount"] = 1.5; - self.pers["bots"]["skill"]["bone_update_interval"] = 0.5; - self.pers["bots"]["skill"]["bones"] = "j_spineupper,j_head"; + self.pers["bots"]["behavior"]["strafe"] = 30; + self.pers["bots"]["behavior"]["nade"] = 25; + self.pers["bots"]["behavior"]["sprint"] = 55; + self.pers["bots"]["behavior"]["camp"] = 5; + self.pers["bots"]["behavior"]["follow"] = 5; + self.pers["bots"]["behavior"]["crouch"] = 10; + self.pers["bots"]["behavior"]["switch"] = 2; + self.pers["bots"]["behavior"]["class"] = 2; + self.pers["bots"]["behavior"]["jump"] = 35; + break; + case 5: + self.pers["bots"]["skill"]["aim_time"] = 0.25; + self.pers["bots"]["skill"]["init_react_time"] = 500; + self.pers["bots"]["skill"]["reaction_time"] = 300; + self.pers["bots"]["skill"]["no_trace_ads_time"] = 1500; + self.pers["bots"]["skill"]["no_trace_look_time"] = 2000; + self.pers["bots"]["skill"]["remember_time"] = 4000; + self.pers["bots"]["skill"]["fov"] = 0.5; + self.pers["bots"]["skill"]["dist_max"] = 7500; + self.pers["bots"]["skill"]["dist_start"] = 5000; + self.pers["bots"]["skill"]["spawn_time"] = 0.25; + self.pers["bots"]["skill"]["help_dist"] = 1500; + self.pers["bots"]["skill"]["semi_time"] = 0.4; + self.pers["bots"]["skill"]["shoot_after_time"] = 0.35; + self.pers["bots"]["skill"]["aim_offset_time"] = 0.35; + self.pers["bots"]["skill"]["aim_offset_amount"] = 1.5; + self.pers["bots"]["skill"]["bone_update_interval"] = 0.5; + self.pers["bots"]["skill"]["bones"] = "j_spineupper,j_head"; + self.pers["bots"]["skill"]["ads_fov_multi"] = 0.5; + self.pers["bots"]["skill"]["ads_aimspeed_multi"] = 0.5; - self.pers["bots"]["behavior"]["strafe"] = 40; - self.pers["bots"]["behavior"]["nade"] = 35; - self.pers["bots"]["behavior"]["sprint"] = 40; - self.pers["bots"]["behavior"]["camp"] = 5; - self.pers["bots"]["behavior"]["follow"] = 5; - self.pers["bots"]["behavior"]["crouch"] = 30; - self.pers["bots"]["behavior"]["switch"] = 2; - self.pers["bots"]["behavior"]["class"] = 2; - self.pers["bots"]["behavior"]["jump"] = 50; - break; - case 6: - self.pers["bots"]["skill"]["aim_time"] = 0.2; - self.pers["bots"]["skill"]["init_react_time"] = 250; - self.pers["bots"]["skill"]["reaction_time"] = 150; - self.pers["bots"]["skill"]["no_trace_ads_time"] = 2000; - self.pers["bots"]["skill"]["no_trace_look_time"] = 3000; - self.pers["bots"]["skill"]["remember_time"] = 5000; - self.pers["bots"]["skill"]["fov"] = 0.45; - self.pers["bots"]["skill"]["dist_max"] = 10000; - self.pers["bots"]["skill"]["dist_start"] = 7500; - self.pers["bots"]["skill"]["spawn_time"] = 0.2; - self.pers["bots"]["skill"]["help_dist"] = 2000; - self.pers["bots"]["skill"]["semi_time"] = 0.25; - self.pers["bots"]["skill"]["shoot_after_time"] = 0.25; - self.pers["bots"]["skill"]["aim_offset_time"] = 0.25; - self.pers["bots"]["skill"]["aim_offset_amount"] = 1; - self.pers["bots"]["skill"]["bone_update_interval"] = 0.25; - self.pers["bots"]["skill"]["bones"] = "j_spineupper,j_head,j_head"; + self.pers["bots"]["behavior"]["strafe"] = 40; + self.pers["bots"]["behavior"]["nade"] = 35; + self.pers["bots"]["behavior"]["sprint"] = 60; + self.pers["bots"]["behavior"]["camp"] = 5; + self.pers["bots"]["behavior"]["follow"] = 5; + self.pers["bots"]["behavior"]["crouch"] = 10; + self.pers["bots"]["behavior"]["switch"] = 2; + self.pers["bots"]["behavior"]["class"] = 2; + self.pers["bots"]["behavior"]["jump"] = 50; + break; + case 6: + self.pers["bots"]["skill"]["aim_time"] = 0.2; + self.pers["bots"]["skill"]["init_react_time"] = 250; + self.pers["bots"]["skill"]["reaction_time"] = 150; + self.pers["bots"]["skill"]["no_trace_ads_time"] = 2000; + self.pers["bots"]["skill"]["no_trace_look_time"] = 3000; + self.pers["bots"]["skill"]["remember_time"] = 5000; + self.pers["bots"]["skill"]["fov"] = 0.45; + self.pers["bots"]["skill"]["dist_max"] = 10000; + self.pers["bots"]["skill"]["dist_start"] = 7500; + self.pers["bots"]["skill"]["spawn_time"] = 0.2; + self.pers["bots"]["skill"]["help_dist"] = 2000; + self.pers["bots"]["skill"]["semi_time"] = 0.25; + self.pers["bots"]["skill"]["shoot_after_time"] = 0.25; + self.pers["bots"]["skill"]["aim_offset_time"] = 0.25; + self.pers["bots"]["skill"]["aim_offset_amount"] = 1; + self.pers["bots"]["skill"]["bone_update_interval"] = 0.25; + self.pers["bots"]["skill"]["bones"] = "j_spineupper,j_head,j_head"; + self.pers["bots"]["skill"]["ads_fov_multi"] = 0.5; + self.pers["bots"]["skill"]["ads_aimspeed_multi"] = 0.5; - self.pers["bots"]["behavior"]["strafe"] = 50; - self.pers["bots"]["behavior"]["nade"] = 45; - self.pers["bots"]["behavior"]["sprint"] = 50; - self.pers["bots"]["behavior"]["camp"] = 5; - self.pers["bots"]["behavior"]["follow"] = 5; - self.pers["bots"]["behavior"]["crouch"] = 20; - self.pers["bots"]["behavior"]["switch"] = 2; - self.pers["bots"]["behavior"]["class"] = 2; - self.pers["bots"]["behavior"]["jump"] = 75; - break; - case 7: - self.pers["bots"]["skill"]["aim_time"] = 0.1; - self.pers["bots"]["skill"]["init_react_time"] = 100; - self.pers["bots"]["skill"]["reaction_time"] = 50; - self.pers["bots"]["skill"]["no_trace_ads_time"] = 2500; - self.pers["bots"]["skill"]["no_trace_look_time"] = 4000; - self.pers["bots"]["skill"]["remember_time"] = 7500; - self.pers["bots"]["skill"]["fov"] = 0.4; - self.pers["bots"]["skill"]["dist_max"] = 15000; - self.pers["bots"]["skill"]["dist_start"] = 10000; - self.pers["bots"]["skill"]["spawn_time"] = 0.05; - self.pers["bots"]["skill"]["help_dist"] = 3000; - self.pers["bots"]["skill"]["semi_time"] = 0.1; - self.pers["bots"]["skill"]["shoot_after_time"] = 0; - self.pers["bots"]["skill"]["aim_offset_time"] = 0; - self.pers["bots"]["skill"]["aim_offset_amount"] = 0; - self.pers["bots"]["skill"]["bone_update_interval"] = 0.05; - self.pers["bots"]["skill"]["bones"] = "j_head"; + self.pers["bots"]["behavior"]["strafe"] = 50; + self.pers["bots"]["behavior"]["nade"] = 45; + self.pers["bots"]["behavior"]["sprint"] = 65; + self.pers["bots"]["behavior"]["camp"] = 5; + self.pers["bots"]["behavior"]["follow"] = 5; + self.pers["bots"]["behavior"]["crouch"] = 10; + self.pers["bots"]["behavior"]["switch"] = 2; + self.pers["bots"]["behavior"]["class"] = 2; + self.pers["bots"]["behavior"]["jump"] = 75; + break; + case 7: + self.pers["bots"]["skill"]["aim_time"] = 0.1; + self.pers["bots"]["skill"]["init_react_time"] = 100; + self.pers["bots"]["skill"]["reaction_time"] = 50; + self.pers["bots"]["skill"]["no_trace_ads_time"] = 2500; + self.pers["bots"]["skill"]["no_trace_look_time"] = 4000; + self.pers["bots"]["skill"]["remember_time"] = 7500; + self.pers["bots"]["skill"]["fov"] = 0.4; + self.pers["bots"]["skill"]["dist_max"] = 15000; + self.pers["bots"]["skill"]["dist_start"] = 10000; + self.pers["bots"]["skill"]["spawn_time"] = 0.05; + self.pers["bots"]["skill"]["help_dist"] = 3000; + self.pers["bots"]["skill"]["semi_time"] = 0.1; + self.pers["bots"]["skill"]["shoot_after_time"] = 0; + self.pers["bots"]["skill"]["aim_offset_time"] = 0; + self.pers["bots"]["skill"]["aim_offset_amount"] = 0; + self.pers["bots"]["skill"]["bone_update_interval"] = 0.05; + self.pers["bots"]["skill"]["bones"] = "j_head"; + self.pers["bots"]["skill"]["ads_fov_multi"] = 0.5; + self.pers["bots"]["skill"]["ads_aimspeed_multi"] = 0.5; - self.pers["bots"]["behavior"]["strafe"] = 65; - self.pers["bots"]["behavior"]["nade"] = 65; - self.pers["bots"]["behavior"]["sprint"] = 65; - self.pers["bots"]["behavior"]["camp"] = 5; - self.pers["bots"]["behavior"]["follow"] = 5; - self.pers["bots"]["behavior"]["crouch"] = 5; - self.pers["bots"]["behavior"]["switch"] = 2; - self.pers["bots"]["behavior"]["class"] = 2; - self.pers["bots"]["behavior"]["jump"] = 90; - break; + self.pers["bots"]["behavior"]["strafe"] = 65; + self.pers["bots"]["behavior"]["nade"] = 65; + self.pers["bots"]["behavior"]["sprint"] = 70; + self.pers["bots"]["behavior"]["camp"] = 5; + self.pers["bots"]["behavior"]["follow"] = 5; + self.pers["bots"]["behavior"]["crouch"] = 5; + self.pers["bots"]["behavior"]["switch"] = 2; + self.pers["bots"]["behavior"]["class"] = 2; + self.pers["bots"]["behavior"]["jump"] = 90; + break; + } } + + wait 5; } } @@ -545,6 +562,11 @@ set_class(rankxp) rank = self maps\mp\gametypes\_rank::getRankForXp( rankxp ) + 1; + if (RandomFloatRange(0, 1) < ((rank / level.maxRank) + 0.1)) + { + self.pers["bots"]["behavior"]["quickscope"] = true; + } + for(i=0; i < 5; i++) { primary = get_random_weapon(primaryGroups, rank); @@ -651,7 +673,6 @@ get_random_perk(perkslot, rank, att1, att2) { case "specialty_parabolic": case "specialty_holdbreath": - case "specialty_weapon_c4": case "specialty_explosivedamage": case "specialty_twoprimaries": continue; @@ -917,6 +938,7 @@ start_bot_threads() self thread bot_killstreak_think(); self thread bot_weapon_think(); + self thread doReloadCancel(); // script targeting if (getDvarInt("bots_play_target_other")) @@ -944,6 +966,7 @@ start_bot_threads() self thread bot_use_tube_think(); self thread bot_use_grenade_think(); self thread bot_use_equipment_think(); + self thread bot_watch_think_mw2(); } // obj @@ -969,6 +992,9 @@ bot_inc_bots(obj, unreach) { level endon("game_ended"); self endon("bot_inc_bots"); + + if (!isDefined(obj)) + return; if (!isDefined(obj.bots)) obj.bots = 0; @@ -1035,7 +1061,7 @@ nearAnyOfWaypoints(dist, waypoints) getNearestWaypointOfWaypoints(waypoints) { answer = undefined; - closestDist = 999999999999; + closestDist = 2147483647; for (i = 0; i < waypoints.size; i++) { waypoint = waypoints[i]; @@ -1311,6 +1337,23 @@ fire_current_weapon() } } +/* + Fires the bots c4 +*/ +fire_c4() +{ + self endon("death"); + self endon("disconnect"); + self endon("weapon_change"); + self endon("stop_firing_weapon"); + + for (;;) + { + self thread BotPressAds(0.05); + wait 0.1; + } +} + /* Changes to the weap */ @@ -1767,6 +1810,8 @@ bot_use_equipment_think() nade = undefined; if (self GetAmmoCount("claymore_mp")) nade = "claymore_mp"; + if (self GetAmmoCount("c4_mp")) + nade = "c4_mp"; if (!isDefined(nade)) continue; @@ -1844,7 +1889,10 @@ bot_use_equipment_think() if (self changeToWeapon(nade)) { - self thread fire_current_weapon(); + if (nade != "c4_mp") + self thread fire_current_weapon(); + else + self thread fire_c4(); self waittill_any_timeout(5, "grenade_fire", "weapon_change"); self notify("stop_firing_weapon"); } @@ -2086,9 +2134,19 @@ bot_revenge_think() if(self.pers["bots"]["skill"]["base"] <= 1) return; + + if (isDefined(self.lastKiller) && isAlive(self.lastKiller)) + { + if(bulletTracePassed(self getEyePos(), self.lastKiller getTagOrigin( "j_spineupper" ), false, self.lastKiller)) + { + self setAttacker(self.lastKiller); + } + } if(!isDefined(self.killerLocation)) return; + + loc = self.killerLocation; for(;;) { @@ -2100,13 +2158,80 @@ bot_revenge_think() if ( randomint( 100 ) < 75 ) return; - self SetScriptGoal( self.killerLocation, 64 ); + self SetScriptGoal( loc, 64 ); if (self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal") self ClearScriptGoal(); } } +/* + Reload cancels +*/ +doReloadCancel() +{ + self endon("disconnect"); + self endon("death"); + + for (;;) + { + ret = self waittill_any_return("reload", "weapon_change"); + + if(self BotIsFrozen()) + continue; + + if(self isDefusing() || self isPlanting()) + continue; + + if (self InLastStand()) + continue; + + curWeap = self GetCurrentWeapon(); + + if (!maps\mp\gametypes\_weapons::isSideArm( curWeap ) && !maps\mp\gametypes\_weapons::isPrimaryWeapon( curWeap )) + continue; + + if (ret == "reload") + { + // check single reloads + if (self GetWeaponAmmoClip(curWeap) < WeaponClipSize(curWeap)) + continue; + } + + // check difficulty + if (self.pers["bots"]["skill"]["base"] <= 3) + continue; + + // check if got another weapon + weaponslist = self GetWeaponsListPrimaries(); + weap = ""; + while(weaponslist.size) + { + weapon = weaponslist[randomInt(weaponslist.size)]; + weaponslist = array_remove(weaponslist, weapon); + + if (!maps\mp\gametypes\_weapons::isSideArm( weapon ) && !maps\mp\gametypes\_weapons::isPrimaryWeapon( weapon )) + continue; + + if(curWeap == weapon || weapon == "none" || weapon == "") + continue; + + weap = weapon; + break; + } + + if(weap == "") + continue; + + // do the cancel + wait 0.1; + self BotChangeToWeapon(weap); + wait 0.25; + self BotChangeToWeapon(curWeap); + wait 2; + } +} + /* Bot logic for switching weapons. */ @@ -2115,6 +2240,8 @@ bot_weapon_think() self endon("death"); self endon("disconnect"); level endon("game_ended"); + + first = true; for(;;) { @@ -2141,13 +2268,23 @@ bot_weapon_think() } } - if(curWeap != "none" && self getAmmoCount(curWeap) && curWeap != "c4_mp") + if (first) { - if(randomInt(100) > self.pers["bots"]["behavior"]["switch"]) + first = false; + + if(randomInt(100) > self.pers["bots"]["behavior"]["initswitch"]) continue; + } + else + { + if(curWeap != "none" && self getAmmoCount(curWeap)) + { + if(randomInt(100) > self.pers["bots"]["behavior"]["switch"]) + continue; - if(hasTarget) - continue; + if(hasTarget) + continue; + } } weaponslist = self getweaponslist(); @@ -2166,7 +2303,7 @@ bot_weapon_think() if (maps\mp\gametypes\_weapons::isGrenade( weapon )) continue; - if(curWeap == weapon || weapon == "c4_mp" || weapon == "none" || weapon == "claymore_mp" || weapon == "")//c4 no work + if(curWeap == weapon || weapon == "c4_mp" || weapon == "none" || weapon == "claymore_mp" || weapon == "") continue; weap = weapon; @@ -2180,6 +2317,52 @@ bot_weapon_think() } } +/* + Bots play mw2 +*/ +bot_watch_think_mw2() +{ + self endon("disconnect"); + self endon("death"); + level endon("game_ended"); + + for (;;) + { + wait randomIntRange(1, 4); + + if(self BotIsFrozen()) + continue; + + if(self isDefusing() || self isPlanting()) + continue; + + if (self InLastStand()) + continue; + + if (self HasThreat()) + continue; + + tube = self getValidTube(); + if (!isDefined(tube)) + { + if (self GetAmmoCount("rpg_mp")) + tube = "rpg_mp"; + else + continue; + } + + if (self GetCurrentWeapon() == tube) + continue; + + chance = self.pers["bots"]["behavior"]["nade"]; + + if (randomInt(100) > chance) + continue; + + self ChangeToWeapon(tube); + } +} + /* Bot logic for killstreaks. */ @@ -2285,8 +2468,12 @@ bot_killstreak_think() if (isAirstrikePos && !isDefined( level.airstrikeInProgress )) { + self BotFreezeControls(true); + self notify( "confirm_location", targetPos ); wait 1; + + self BotFreezeControls(false); } self thread changeToWeapon(curWeap); @@ -2466,6 +2653,9 @@ bot_equipment_kill_think() { item = grenades[i]; + if (!isDefined(item)) + continue; + if ( !IsDefined( item.name ) ) { continue; @@ -2706,20 +2896,23 @@ bot_dom_cap_think() otherFlagCount = maps\mp\gametypes\dom::getTeamFlagCount( otherTeam ); - if ( myFlagCount < otherFlagCount ) + if (game["teamScores"][myteam] >= game["teamScores"][otherTeam]) { - if ( randomint( 100 ) < 15 ) - continue; - } - else if ( myFlagCount == otherFlagCount ) - { - if ( randomint( 100 ) < 35 ) - continue; - } - else if ( myFlagCount > otherFlagCount ) - { - if ( randomint( 100 ) < 95 ) - continue; + if ( myFlagCount < otherFlagCount ) + { + if ( randomint( 100 ) < 15 ) + continue; + } + else if ( myFlagCount == otherFlagCount ) + { + if ( randomint( 100 ) < 35 ) + continue; + } + else if ( myFlagCount > otherFlagCount ) + { + if ( randomint( 100 ) < 95 ) + continue; + } } flag = undefined; diff --git a/out/Add to root of CoD4x server/main_shared/maps/mp/bots/_bot_utility.gsc b/out/Add to root of CoD4x server/main_shared/maps/mp/bots/_bot_utility.gsc index 9159a98..c16c195 100644 --- a/out/Add to root of CoD4x server/main_shared/maps/mp/bots/_bot_utility.gsc +++ b/out/Add to root of CoD4x server/main_shared/maps/mp/bots/_bot_utility.gsc @@ -799,9 +799,9 @@ SmokeTrace(start, end, rad) */ getConeDot(to, from, dir) { - dirToTarget = VectorNormalize(to-from); - forward = AnglesToForward(dir); - return vectordot(dirToTarget, forward); + dirToTarget = VectorNormalize(to-from); + forward = AnglesToForward(dir); + return vectordot(dirToTarget, forward); } /* @@ -974,25 +974,25 @@ cac_init_patch() */ tokenizeLine(line, tok) { - tokens = []; + tokens = []; - token = ""; - for (i = 0; i < line.size; i++) - { - c = line[i]; + token = ""; + for (i = 0; i < line.size; i++) + { + c = line[i]; - if (c == tok) - { - tokens[tokens.size] = token; - token = ""; - continue; - } + if (c == tok) + { + tokens[tokens.size] = token; + token = ""; + continue; + } - token += c; - } - tokens[tokens.size] = token; + token += c; + } + tokens[tokens.size] = token; - return tokens; + return tokens; } /* @@ -1525,7 +1525,7 @@ KDTree() */ KDTreeInsert(data)//as long as what you insert has a .origin attru, it will work. { - self.root = self _KDTreeInsert(self.root, data, 0, -9999999999, -9999999999, -9999999999, 9999999999, 9999999999, 9999999999); + self.root = self _KDTreeInsert(self.root, data, 0, -2147483647, -2147483647, -2147483647, 2147483647, 2147483647, 2147483647); } /* @@ -1804,7 +1804,7 @@ ReverseHeapAStar(item, item2) GetNearestWaypointWithSight(pos) { candidate = undefined; - dist = 9999999999; + dist = 2147483647; for(i = 0; i < level.waypointCount; i++) { diff --git a/out/Add to root of CoD4x server/main_shared/maps/mp/bots/_menu.gsc b/out/Add to root of CoD4x server/main_shared/maps/mp/bots/_menu.gsc index f1ec5c6..b92f8f9 100644 --- a/out/Add to root of CoD4x server/main_shared/maps/mp/bots/_menu.gsc +++ b/out/Add to root of CoD4x server/main_shared/maps/mp/bots/_menu.gsc @@ -27,6 +27,9 @@ watchPlayers() { wait 1; + if (!getDvarInt("bots_main_menu")) + return; + for (i = level.players.size - 1; i >= 0; i--) { player = level.players[i]; diff --git a/out/Add to root of CoD4x server/main_shared/maps/mp/bots/_wp_editor.gsc b/out/Add to root of CoD4x server/main_shared/maps/mp/bots/_wp_editor.gsc index be1a8b5..4b3bc9e 100644 --- a/out/Add to root of CoD4x server/main_shared/maps/mp/bots/_wp_editor.gsc +++ b/out/Add to root of CoD4x server/main_shared/maps/mp/bots/_wp_editor.gsc @@ -368,7 +368,7 @@ watchSaveWaypointsCommand() { self iPrintlnBold("Auto link enabled"); level.autoLink = true; - level.wpToLink = self.nearest; + level.wpToLink = self.closest; } } @@ -446,6 +446,12 @@ DeleteAllWaypoints() { level.waypoints = []; level.waypointCount = 0; + level.waypointsKDTree = WaypointsToKDTree(); + + level.waypointsCamp = []; + level.waypointsTube = []; + level.waypointsGren = []; + level.waypointsClay = []; self iprintln("DelAllWps"); } @@ -617,9 +623,7 @@ destroyOnDeath(hud) { hud endon("death"); self waittill_either("death","disconnect"); - hud notify("death"); hud destroy(); - hud = undefined; } textScroll(string) diff --git a/out/readme.txt b/out/readme.txt index 4c54fdd..6bde1ce 100644 --- a/out/readme.txt +++ b/out/readme.txt @@ -30,6 +30,22 @@ Make sure to disable this DVAR by adding 'set bots_main_firstIsHost 0' in your s - Pressing the menu button again closes menus. ## Changelog +- v2.0.1 + - Reduced bots crouching + - Increased bots sprinting + - Improved bots mantling, crouching and knifing glass when needed + - Fixed possible script runtime errors + - Improved domination + - Bots use explosives more if they have it + - Bots aim slower when ads'ing + - Fixed bots holding breath + - Fixed bots rubberbanding movement when their goal changes + - Added bots quickscoping with snipers + - Added bots reload canceling and fast swaps + - Bots use C4 + - Improved revenge + - Bots can swap weapons on spawn more likely + - v2.0.0 - Initial reboot release