iw4_bot_warfare/userraw/maps/mp/bots/_bot_utility.gsc
Your Name 0952e8af0a host
2020-09-12 22:25:37 -06:00

2251 lines
56 KiB
Plaintext

#include common_scripts\utility;
#include maps\mp\_utility;
#include maps\mp\gametypes\_hud_util;
is_host()
{
return (isDefined(self.pers["bot_host"]) && self.pers["bot_host"]);
}
doHostCheck()
{
self.pers["bot_host"] = false;
if (self is_bot())
return;
DvarGUID = getDvar("bots_main_GUIDs");
result = false;
if (DvarGUID != "")
{
guids = strtok(DvarGUID, ",");
for (i = 0; i < guids.size; i++)
{
if(self.guid == guids[i])
result = true;
}
}
if (!self isHost() && !result)
return;
self.pers["bot_host"] = true;
}
/*
Returns if the player is a bot.
*/
is_bot()
{
assert(isDefined(self));
assert(isPlayer(self));
return ((isDefined(self.pers["isBot"]) && self.pers["isBot"]) || (isDefined(self.pers["isBotWarfare"]) && self.pers["isBotWarfare"]) || isSubStr( self.guid, "bot" ));
}
/*
Returns how much the bot is ads'ing all the way.
*/
botAdsAmount()
{
return (1 / (self.bot.ads_highest - self.bot.ads_lowest)) * self.bot.ads_tightness + (1 + (self.bot.ads_highest / (self.bot.ads_lowest - self.bot.ads_highest)));
}
BotPressADS(time)
{
self maps\mp\bots\_bot_internal::pressAds(time);
}
BotPressAttack(time)
{
self maps\mp\bots\_bot_internal::pressFire(time);
}
BotStopMoving(what)
{
self.bot.stop_move = what;
if(what)
self notify("kill_goal");
}
getValidTube()
{
weaps = self getweaponslistall();
for (i = 0; i < weaps.size; i++)
{
weap = weaps[i];
if(!self getAmmoCount(weap))
continue;
if ((isSubStr(weap, "gl_") && !isSubStr(weap, "_gl_")) || weap == "m79_mp")
return weap;
}
return undefined;
}
/*
Returns a random grenade in the bot's inventory.
*/
getValidGrenade()
{
grenadeTypes = [];
grenadeTypes[grenadeTypes.size] = "frag_grenade_mp";
grenadeTypes[grenadeTypes.size] = "smoke_grenade_mp";
grenadeTypes[grenadeTypes.size] = "flash_grenade_mp";
grenadeTypes[grenadeTypes.size] = "concussion_grenade_mp";
grenadeTypes[grenadeTypes.size] = "semtex_mp";
grenadeTypes[grenadeTypes.size] = "throwingknife_mp";
possibles = [];
for(i = 0; i < grenadeTypes.size; i++)
{
if ( !self hasWeapon( grenadeTypes[i] ) )
continue;
if ( !self getAmmoCount( grenadeTypes[i] ) )
continue;
possibles[possibles.size] = grenadeTypes[i];
}
return random(possibles);
}
throwBotGrenade(gname)
{
return self maps\mp\bots\_bot_internal::botThrowGrenade(gname);
}
botIsClimbing()
{
return self.bot.climbing;
}
BotGetTargetRandom()
{
if (!isDefined(self.bot.target))
return undefined;
return self.bot.target.rand;
}
/*
Returns the bot's random assigned number.
*/
BotGetRandom()
{
return self.bot.rand;
}
/*
Returns if the bot is pressing frag button.
*/
IsBotFragging()
{
return self.bot.isfragging;
}
/*
Returns if the bot is sprinting.
*/
IsBotSprinting()
{
return self.bot.running;
}
/*
Returns if the bot is reloading.
*/
IsBotReloading()
{
return self.bot.isreloading;
}
/*
Freezes the bot's controls.
*/
BotFreezeControls(what)
{
self.bot.isfrozen = what;
if(what)
self notify("kill_goal");
}
/*
Returns if the bot is script frozen.
*/
BotIsFrozen()
{
return self.bot.isfrozen;
}
/*
Sets the bot's target to be this ent.
*/
SetAttacker(att)
{
self.bot.target_this_frame = att;
}
/*
Returns if the bot has a script goal.
(like t5 gsc bot)
*/
HasScriptGoal()
{
return (isDefined(self GetScriptGoal()));
}
/*
Sets the bot's goal, will acheive it when dist away from it.
*/
SetScriptGoal(goal, dist)
{
if (!isDefined(dist))
dist = 16;
self.bot.script_goal = goal;
self.bot.script_goal_dist = dist;
waittillframeend;
self notify("new_goal_internal");
self notify("new_goal");
}
GetScriptGoal()
{
return self.bot.script_goal;
}
/*
Clears the bot's goal.
*/
ClearScriptGoal()
{
self SetScriptGoal(undefined, 0);
}
HasBotJavelinLocation()
{
return isDefined(self.bot.jav_loc);
}
SetScriptAimPos(pos)
{
self.bot.script_aimpos = pos;
}
ClearScriptAimPos()
{
self SetScriptAimPos(undefined);
}
GetScriptAimPos()
{
return self.bot.script_aimpos;
}
HasScriptAimPos()
{
return isDefined(self GetScriptAimPos());
}
SetBotJavelinLocation(loc)
{
self.bot.jav_loc = loc;
self notify("new_enemy");
}
ClearBotJavelinLocation()
{
self SetBotJavelinLocation(undefined);
}
/*
Sets the script enemy for a bot.
*/
SetScriptEnemy(enemy, offset)
{
self.bot.script_target = enemy;
self.bot.script_target_offset = offset;
}
/*
Removes the script enemy of the bot.
*/
ClearScriptEnemy()
{
self SetScriptEnemy(undefined, undefined);
}
/*
Returns the entity of the bot's target.
*/
GetThreat()
{
if(!isdefined(self.bot.target))
return undefined;
return self.bot.target.entity;
}
/*
Returns if the bot has a script enemy.
*/
HasScriptEnemy()
{
return (isDefined(self.bot.script_target));
}
/*
Returns if the bot has a threat.
*/
HasThreat()
{
return (isDefined(self GetThreat()));
}
IsBotKnifing()
{
return self.bot.knifing;
}
getBotVelocity()
{
return self.bot.velocity;
}
isWeaponPrimary(weap)
{
return (maps\mp\gametypes\_weapons::isPrimaryWeapon(weap) || maps\mp\gametypes\_weapons::isAltModeWeapon(weap));
}
entIsVehicle(ent)
{
return (ent.classname == "script_vehicle" || ent.model == "vehicle_uav_static_mp" || ent.model == "vehicle_ac130_coop");
}
/*
Returns if the given weapon is full auto.
*/
WeaponIsFullAuto(weap)
{
weaptoks = strtok(weap, "_");
assert(isDefined(weaptoks[0]));
assert(isString(weaptoks[0]));
return !isDefined(level.bots_nonfullautoguns[weaptoks[0]]);
}
IsDefusing()
{
return (isDefined(self.isDefusing) && self.isDefusing);
}
isPlanting()
{
return (isDefined(self.isPlanting) && self.isPlanting);
}
inLastStand()
{
return (isDefined(self.lastStand) && self.lastStand);
}
inFinalStand()
{
return (isDefined(self.inFinalStand) && self.inFinalStand);
}
isFlagCarrier()
{
return (isDefined(self.carryFlag) && self.carryFlag);
}
isWeaponDroppable(weap)
{
return (maps\mp\gametypes\_weapons::mayDropWeapon(weap));
}
/*
Returns if we are stunned.
*/
IsStunned()
{
return (isdefined(self.concussionEndTime) && self.concussionEndTime > gettime());
}
/*
Returns if we are beingArtilleryShellshocked
*/
isArtShocked()
{
return (isDefined(self.beingArtilleryShellshocked) && self.beingArtilleryShellshocked);
}
getEyeHeight()
{
myEye = self getEye();
return myEye[2] - self.origin[2];
}
notifyAfterDelay(delay, not)
{
wait delay;
self notify(not);
}
/*
Pezbot's line sphere intersection.
*/
RaySphereIntersect(start, end, spherePos, radius)
{
dp = end - start;
a = dp[0] * dp[0] + dp[1] * dp[1] + dp[2] * dp[2];
b = 2 * (dp[0] * (start[0] - spherePos[0]) + dp[1] * (start[1] - spherePos[1]) + dp[2] * (start[2] - spherePos[2]));
c = spherePos[0] * spherePos[0] + spherePos[1] * spherePos[1] + spherePos[2] * spherePos[2];
c += start[0] * start[0] + start[1] * start[1] + start[2] * start[2];
c -= 2.0 * (spherePos[0] * start[0] + spherePos[1] * start[1] + spherePos[2] * start[2]);
c -= radius * radius;
bb4ac = b * b - 4.0 * a * c;
return (bb4ac >= 0);
}
/*
Returns if a smoke grenade would intersect start to end line.
*/
SmokeTrace(start, end, rad)
{
for(i = level.bots_smokeList.count - 1; i >= 0; i--)
{
nade = level.bots_smokeList.data[i];
if(nade.state != "smoking")
continue;
if(!RaySphereIntersect(start, end, nade.origin, rad))
continue;
return false;
}
return true;
}
/*
Returns the cone dot (like fov, or distance from the center of our screen).
*/
getConeDot(to, from, dir)
{
dirToTarget = VectorNormalize(to-from);
forward = AnglesToForward(dir);
return vectordot(dirToTarget, forward);
}
DistanceSquared2D(to, from)
{
to = (to[0], to[1], 0);
from = (from[0], from[1], 0);
return DistanceSquared(to, from);
}
/*
Rounds to the nearest whole number.
*/
Round(x)
{
y = int(x);
if(abs(x) - abs(y) > 0.5)
{
if(x < 0)
return y - 1;
else
return y + 1;
}
else
return y;
}
/*
Rounds up the given value.
*/
RoundUp( floatVal )
{
i = int( floatVal );
if ( i != floatVal )
return i + 1;
else
return i;
}
float(num)
{
setdvar("temp_dvar_bot_util", num);
return GetDvarFloat("temp_dvar_bot_util");
}
tokenizeLine(line, tok)
{
tokens = [];
token = "";
for (i = 0; i < line.size; i++)
{
c = line[i];
if (c == tok)
{
tokens[tokens.size] = token;
token = "";
continue;
}
token += c;
}
tokens[tokens.size] = token;
return tokens;
}
parseTokensIntoWaypoint(tokens)
{
waypoint = spawnStruct();
orgStr = tokens[0];
orgToks = strtok(orgStr, " ");
waypoint.origin = (float(orgToks[0]), float(orgToks[1]), float(orgToks[2]));
childStr = tokens[1];
childToks = strtok(childStr, " ");
waypoint.childCount = childToks.size;
waypoint.children = [];
for( j=0; j<childToks.size; j++ )
waypoint.children[j] = int(childToks[j]);
type = tokens[2];
waypoint.type = type;
anglesStr = tokens[3];
if (isDefined(anglesStr) && anglesStr != "")
{
anglesToks = strtok(anglesStr, " ");
waypoint.angles = (float(anglesToks[0]), float(anglesToks[1]), float(anglesToks[2]));
}
javStr = tokens[4];
if (isDefined(javStr) && javStr != "")
{
javToks = strtok(javStr, " ");
waypoint.jav_point = (float(javToks[0]), float(javToks[1]), float(javToks[2]));
}
return waypoint;
}
// https://github.com/leiizko/cod4x_lua_plugin/blob/master/LuaScripts/Rotu-R/waypoints.gsc
wpsFromCSV(mapname)
{
fileName = "waypoints/"+ toLower(mapname) + "_wp.csv";
waypoints = [];
waypointCount = int(tableLookupByRow(fileName, 0, 0));
if (waypointCount <= 0)
return waypoints;
printLn( "Getting waypoints from csv: "+fileName );
for (i = 1; i <= waypointCount; i++)
{
tokens = [];
tokens[tokens.size] = tableLookupByRow(fileName, i, 0);
tokens[tokens.size] = tableLookupByRow(fileName, i, 1);
tokens[tokens.size] = tableLookupByRow(fileName, i, 2);
tokens[tokens.size] = tableLookupByRow(fileName, i, 3);
tokens[tokens.size] = tableLookupByRow(fileName, i, 4);
waypoint = parseTokensIntoWaypoint(tokens);
waypoints[i-1] = waypoint;
}
return waypoints;
}
/*
Loads the waypoints. Populating everything needed for the waypoints.
*/
load_waypoints()
{
level.waypointCount = 0;
level.waypoints = [];
mapname = getDvar("mapname");
wps = wpsFromCSV(mapname);
if (wps.size)
{
level.waypoints = wps;
println("Loaded " + wps.size + " waypoints from csv.");
}
else
{
switch(mapname)
{
case "mp_afghan":
level.waypoints = maps\mp\bots\waypoints\afghan::Afghan();
break;
case "mp_derail":
level.waypoints = maps\mp\bots\waypoints\derail::Derail();
break;
case "mp_estate":
case "mp_estate_trop":
case "mp_estate_tropical":
level.waypoints = maps\mp\bots\waypoints\estate::Estate();
break;
case "mp_favela":
case "mp_fav_tropical":
level.waypoints = maps\mp\bots\waypoints\favela::Favela();
break;
case "mp_highrise":
level.waypoints = maps\mp\bots\waypoints\highrise::Highrise();
break;
case "mp_invasion":
level.waypoints = maps\mp\bots\waypoints\invasion::Invasion();
break;
case "mp_checkpoint":
level.waypoints = maps\mp\bots\waypoints\karachi::Karachi();
break;
case "mp_quarry":
level.waypoints = maps\mp\bots\waypoints\quarry::Quarry();
break;
case "mp_rundown":
level.waypoints = maps\mp\bots\waypoints\rundown::Rundown();
break;
case "mp_rust":
level.waypoints = maps\mp\bots\waypoints\rust::Rust();
break;
case "mp_boneyard":
level.waypoints = maps\mp\bots\waypoints\scrapyard::Scrapyard();
break;
case "mp_nightshift":
level.waypoints = maps\mp\bots\waypoints\skidrow::Skidrow();
break;
case "mp_subbase":
level.waypoints = maps\mp\bots\waypoints\subbase::Subbase();
break;
case "mp_terminal":
level.waypoints = maps\mp\bots\waypoints\terminal::Terminal();
break;
case "mp_underpass":
level.waypoints = maps\mp\bots\waypoints\underpass::Underpass();
break;
case "mp_brecourt":
level.waypoints = maps\mp\bots\waypoints\wasteland::Wasteland();
break;
case "mp_complex":
level.waypoints = maps\mp\bots\waypoints\bailout::Bailout();
break;
case "mp_crash":
case "mp_crash_trop":
case "mp_crash_tropical":
level.waypoints = maps\mp\bots\waypoints\crash::Crash();
break;
case "mp_overgrown":
level.waypoints = maps\mp\bots\waypoints\overgrown::Overgrown();
break;
case "mp_compact":
level.waypoints = maps\mp\bots\waypoints\salvage::Salvage();
break;
case "mp_storm":
case "mp_storm_spring":
level.waypoints = maps\mp\bots\waypoints\storm::Storm();
break;
case "mp_abandon":
level.waypoints = maps\mp\bots\waypoints\carnival::Carnival();
break;
case "mp_fuel2":
level.waypoints = maps\mp\bots\waypoints\fuel::Fuel();
break;
case "mp_strike":
level.waypoints = maps\mp\bots\waypoints\strike::Strike();
break;
case "mp_trailerpark":
level.waypoints = maps\mp\bots\waypoints\trailerPark::TrailerPark();
break;
case "mp_vacant":
level.waypoints = maps\mp\bots\waypoints\vacant::Vacant();
break;
case "mp_nuked":
level.waypoints = maps\mp\bots\waypoints\nuketown::Nuketown();
break;
case "mp_cross_fire":
level.waypoints = maps\mp\bots\waypoints\crossfire::Crossfire();
break;
case "mp_bloc":
case "mp_bloc_sh":
level.waypoints = maps\mp\bots\waypoints\bloc::Bloc();
break;
case "mp_cargoship":
case "mp_cargoship_sh":
level.waypoints = maps\mp\bots\waypoints\wetwork::Wetwork();
break;
case "mp_killhouse":
level.waypoints = maps\mp\bots\waypoints\killhouse::Killhouse();
break;
case "mp_bog_sh":
level.waypoints = maps\mp\bots\waypoints\bog::Bog();
break;
case "mp_firingrange":
level.waypoints = maps\mp\bots\waypoints\firingrange::Firingrange();
break;
case "mp_shipment":
level.waypoints = maps\mp\bots\waypoints\shipment::Shipment();
break;
case "mp_shipment_long":
level.waypoints = maps\mp\bots\waypoints\shipmentlong::ShipmentLong();
break;
case "mp_rust_long":
level.waypoints = maps\mp\bots\waypoints\rustlong::Rustlong();
break;
case "invasion":
level.waypoints = maps\mp\bots\waypoints\burgertown::Burgertown();
break;
case "iw4_credits":
level.waypoints = maps\mp\bots\waypoints\testmap::Testmap();
break;
case "oilrig":
level.waypoints = maps\mp\bots\waypoints\oilrig::Oilrig();
break;
case "co_hunted":
level.waypoints = maps\mp\bots\waypoints\hunted::Hunted();
break;
case "contingency":
level.waypoints = maps\mp\bots\waypoints\contingency::Contingency();
break;
case "gulag":
level.waypoints = maps\mp\bots\waypoints\gulag::Gulag();
break;
case "so_ghillies":
level.waypoints = maps\mp\bots\waypoints\pripyat::Pripyat();
break;
case "airport":
level.waypoints = maps\mp\bots\waypoints\airport::Airport();
break;
case "ending":
level.waypoints = maps\mp\bots\waypoints\museum::Museum();
break;
case "af_chase":
level.waypoints = maps\mp\bots\waypoints\afghanchase::AfghanChase();
break;
case "trainer":
level.waypoints = maps\mp\bots\waypoints\trainer::Trainer();
break;
case "roadkill":
level.waypoints = maps\mp\bots\waypoints\roadkill::Roadkill();
break;
case "dcemp":
level.waypoints = maps\mp\bots\waypoints\dcemp::DCEMP();
break;
case "dcburning":
level.waypoints = maps\mp\bots\waypoints\dcburning::DCBurning();
break;
case "af_caves":
level.waypoints = maps\mp\bots\waypoints\afghancaves::AfghanCaves();
break;
case "arcadia":
level.waypoints = maps\mp\bots\waypoints\arcadia::Arcadia();
break;
case "boneyard":
level.waypoints = maps\mp\bots\waypoints\boneyard::Boneyard();
break;
case "cliffhanger":
level.waypoints = maps\mp\bots\waypoints\cliffhanger::Cliffhanger();
break;
case "downtown":
level.waypoints = maps\mp\bots\waypoints\downtown::Downtown();
break;
case "estate":
level.waypoints = maps\mp\bots\waypoints\estatesp::EstateSP();
break;
case "favela":
level.waypoints = maps\mp\bots\waypoints\favelasp::FavelaSP();
break;
case "favela_escape":
level.waypoints = maps\mp\bots\waypoints\favelaescape::FavelaEscape();
break;
case "so_bridge":
level.waypoints = maps\mp\bots\waypoints\bridge::Bridge();
break;
case "dc_whitehouse":
level.waypoints = maps\mp\bots\waypoints\whitehouse::Whitehouse();
break;
default:
maps\mp\bots\waypoints\_custom_map::main(mapname);
break;
}
if (level.waypoints.size)
println("Loaded " + level.waypoints.size + " waypoints from script.");
}
if (!level.waypoints.size)
{
maps\mp\bots\_bot_http::getRemoteWaypoints(mapname);
}
level.waypointCount = level.waypoints.size;
for(i = 0; i < level.waypointCount; i++)
{
level.waypoints[i].index = i;
level.waypoints[i].bots = [];
level.waypoints[i].bots["allies"] = 1;
level.waypoints[i].bots["axis"] = 1;
level.waypoints[i].childCount = level.waypoints[i].children.size;
}
level.waypointsKDTree = WaypointsToKDTree();
level.waypointsCamp = [];
level.waypointsTube = [];
level.waypointsGren = [];
level.waypointsClay = [];
level.waypointsJav = [];
for(i = 0; i < level.waypointCount; i++)
if(level.waypoints[i].type == "crouch" && level.waypoints[i].childCount == 1)
level.waypointsCamp[level.waypointsCamp.size] = level.waypoints[i];
else if(level.waypoints[i].type == "tube")
level.waypointsTube[level.waypointsTube.size] = level.waypoints[i];
else if(level.waypoints[i].type == "grenade")
level.waypointsGren[level.waypointsGren.size] = level.waypoints[i];
else if(level.waypoints[i].type == "claymore")
level.waypointsClay[level.waypointsClay.size] = level.waypoints[i];
else if(level.waypoints[i].type == "javelin")
level.waypointsJav[level.waypointsJav.size] = level.waypoints[i];
}
getMapName(mapname)
{
switch(mapname)
{
case "mp_abandon":
return "Carnival";
case "mp_rundown":
return "Rundown";
case "mp_afghan":
return "Afghan";
case "mp_boneyard":
return "Scrapyard";
case "mp_brecourt":
return "Wasteland";
case "mp_cargoship":
return "Wetwork";
case "mp_checkpoint":
return "Karachi";
case "mp_compact":
return "Salvage";
case "mp_complex":
return "Bailout";
case "mp_crash":
return "Crash";
case "mp_cross_fire":
return "Crossfire";
case "mp_derail":
return "Derail";
case "mp_estate":
return "Estate";
case "mp_favela":
return "Favela";
case "mp_fuel2":
return "Fuel";
case "mp_highrise":
return "Highrise";
case "mp_invasion":
return "Invasion";
case "mp_killhouse":
return "Killhouse";
case "mp_nightshift":
return "Skidrow";
case "mp_nuked":
return "Nuketown";
case "oilrig":
return "Oilrig";
case "mp_quarry":
return "Quarry";
case "mp_rust":
return "Rust";
case "mp_storm":
return "Storm";
case "mp_strike":
return "Strike";
case "mp_subbase":
return "Subbase";
case "mp_terminal":
return "Terminal";
case "mp_trailerpark":
return "Trailer Park";
case "mp_overgrown":
return "Overgrown";
case "mp_underpass":
return "Underpass";
case "mp_vacant":
return "Vacant";
case "iw4_credits":
return "IW4 Test Map";
case "airport":
return "Airport";
case "co_hunted":
return "Hunted";
case "invasion":
return "Burgertown";
case "mp_bloc":
return "Bloc";
case "mp_bog_sh":
return "Bog";
case "contingency":
return "Contingency";
case "gulag":
return "Gulag";
case "so_ghillies":
return "Pripyat";
case "ending":
return "Museum";
case "af_chase":
return "Afghan Chase";
case "af_caves":
return "Afghan Caves";
case "arcadia":
return "Arcadia";
case "boneyard":
return "Boneyard";
case "cliffhanger":
return "Cliffhanger";
case "dcburning":
return "DCBurning";
case "dcemp":
return "DCEMP";
case "downtown":
return "Downtown";
case "estate":
return "EstateSP";
case "favela":
return "FavelaSP";
case "favela_escape":
return "Favela Escape";
case "roadkill":
return "Roadkill";
case "trainer":
return "TH3 PIT";
case "so_bridge":
return "Bridge";
case "dc_whitehouse":
return "Whitehouse";
case "mp_shipment_long":
return "ShipmentLong";
case "mp_shipment":
return "Shipment";
case "mp_firingrange":
return "Firing Range";
case "mp_rust_long":
return "RustLong";
case "mp_cargoship_sh":
return "Freighter";
case "mp_storm_spring":
return "Chemical Plant";
case "mp_crash_trop":
case "mp_crash_tropical":
return "Crash Tropical";
case "mp_fav_tropical":
return "Favela Tropical";
case "mp_estate_trop":
case "mp_estate_tropical":
return "Estate Tropical";
case "mp_bloc_sh":
return "Forgotten City";
default:
return mapname;
}
}
/*
Returns a good amount of players.
*/
getGoodMapAmount()
{
switch(getdvar("mapname"))
{
case "mp_rust":
case "iw4_credits":
case "mp_nuked":
case "oilrig":
case "mp_killhouse":
case "invasion":
case "mp_bog_sh":
case "co_hunted":
case "contingency":
case "gulag":
case "so_ghillies":
case "ending":
case "af_chase":
case "af_caves":
case "arcadia":
case "boneyard":
case "cliffhanger":
case "dcburning":
case "dcemp":
case "downtown":
case "estate":
case "favela":
case "favela_escape":
case "roadkill":
case "so_bridge":
case "trainer":
case "dc_whitehouse":
case "mp_shipment":
if(level.teambased)
return 8;
else
return 4;
case "mp_vacant":
case "mp_terminal":
case "mp_nightshift":
case "mp_favela":
case "mp_highrise":
case "mp_boneyard":
case "mp_subbase":
case "mp_firingrange":
case "mp_fav_tropical":
case "mp_shipment_long":
case "mp_rust_long":
if(level.teambased)
return 12;
else
return 8;
case "mp_afghan":
case "mp_crash":
case "mp_brecourt":
case "mp_cross_fire":
case "mp_overgrown":
case "mp_trailerpark":
case "mp_underpass":
case "mp_checkpoint":
case "mp_quarry":
case "mp_rundown":
case "mp_cargoship":
case "mp_estate":
case "mp_bloc":
case "mp_storm":
case "mp_strike":
case "mp_abandon":
case "mp_complex":
case "airport":
case "mp_storm_spring":
case "mp_crash_trop":
case "mp_cargoship_sh":
case "mp_estate_trop":
case "mp_compact":
case "mp_crash_tropical":
case "mp_estate_tropical":
case "mp_bloc_sh":
if(level.teambased)
return 14;
else
return 9;
case "mp_fuel2":
case "mp_invasion":
case "mp_derail":
if(level.teambased)
return 16;
else
return 10;
default:
return 2;
}
}
/*
Returns an array of all the bots in the game.
*/
getBotArray()
{
result = [];
playercount = level.players.size;
for(i = 0; i < playercount; i++)
{
player = level.players[i];
if(!player is_bot())
continue;
result[result.size] = player;
}
return result;
}
/*
We return a balanced KDTree from the waypoints.
*/
WaypointsToKDTree()
{
kdTree = KDTree();
kdTree _WaypointsToKDTree(level.waypoints, 0);
return kdTree;
}
/*
Recurive function. We construct a balanced KD tree by sorting the waypoints using heap sort.
*/
_WaypointsToKDTree(waypoints, dem)
{
if(!waypoints.size)
return;
callbacksort = undefined;
switch(dem)
{
case 0:
callbacksort = ::HeapSortCoordX;
break;
case 1:
callbacksort = ::HeapSortCoordY;
break;
case 2:
callbacksort = ::HeapSortCoordZ;
break;
}
heap = NewHeap(callbacksort);
for(i = 0; i < waypoints.size; i++)
{
heap HeapInsert(waypoints[i]);
}
sorted = [];
while(heap.data.size)
{
sorted[sorted.size] = heap.data[0];
heap HeapRemove();
}
median = int(sorted.size/2);//use divide and conq
left = [];
right = [];
for(i = 0; i < sorted.size; i++)
if(i < median)
right[right.size] = sorted[i];
else if(i > median)
left[left.size] = sorted[i];
self KDTreeInsert(sorted[median]);
_WaypointsToKDTree(left, (dem+1)%3);
_WaypointsToKDTree(right, (dem+1)%3);
}
/*
Returns a new list.
*/
List()
{
list = spawnStruct();
list.count = 0;
list.data = [];
return list;
}
/*
Adds a new thing to the list.
*/
ListAdd(thing)
{
self.data[self.count] = thing;
self.count++;
}
/*
Adds to the start of the list.
*/
ListAddFirst(thing)
{
for (i = self.count - 1; i >= 0; i--)
{
self.data[i + 1] = self.data[i];
}
self.data[0] = thing;
self.count++;
}
/*
Removes the thing from the list.
*/
ListRemove(thing)
{
for ( i = 0; i < self.count; i++ )
{
if ( self.data[i] == thing )
{
while ( i < self.count-1 )
{
self.data[i] = self.data[i+1];
i++;
}
self.data[i] = undefined;
self.count--;
break;
}
}
}
/*
Returns a new KDTree.
*/
KDTree()
{
kdTree = spawnStruct();
kdTree.root = undefined;
kdTree.count = 0;
return kdTree;
}
/*
Called on a KDTree. Will insert the object into the 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);
}
/*
Recurive function that insert the object into the KDTree.
*/
_KDTreeInsert(node, data, dem, x0, y0, z0, x1, y1, z1)
{
if(!isDefined(node))
{
r = spawnStruct();
r.data = data;
r.left = undefined;
r.right = undefined;
r.x0 = x0;
r.x1 = x1;
r.y0 = y0;
r.y1 = y1;
r.z0 = z0;
r.z1 = z1;
self.count++;
return r;
}
switch(dem)
{
case 0:
if(data.origin[0] < node.data.origin[0])
node.left = self _KDTreeInsert(node.left, data, 1, x0, y0, z0, node.data.origin[0], y1, z1);
else
node.right = self _KDTreeInsert(node.right, data, 1, node.data.origin[0], y0, z0, x1, y1, z1);
break;
case 1:
if(data.origin[1] < node.data.origin[1])
node.left = self _KDTreeInsert(node.left, data, 2, x0, y0, z0, x1, node.data.origin[1], z1);
else
node.right = self _KDTreeInsert(node.right, data, 2, x0, node.data.origin[1], z0, x1, y1, z1);
break;
case 2:
if(data.origin[2] < node.data.origin[2])
node.left = self _KDTreeInsert(node.left, data, 0, x0, y0, z0, x1, y1, node.data.origin[2]);
else
node.right = self _KDTreeInsert(node.right, data, 0, x0, y0, node.data.origin[2], x1, y1, z1);
break;
}
return node;
}
/*
Called on a KDTree, will return the nearest object to the given origin.
*/
KDTreeNearest(origin)
{
if(!isDefined(self.root))
return undefined;
return self _KDTreeNearest(self.root, origin, self.root.data, DistanceSquared(self.root.data.origin, origin), 0);
}
/*
Recurive function that will retrieve the closest object to the query.
*/
_KDTreeNearest(node, point, closest, closestdist, dem)
{
if(!isDefined(node))
{
return closest;
}
thisDis = DistanceSquared(node.data.origin, point);
if(thisDis < closestdist)
{
closestdist = thisDis;
closest = node.data;
}
if(node RectDistanceSquared(point) < closestdist)
{
near = node.left;
far = node.right;
if(point[dem] > node.data.origin[dem])
{
near = node.right;
far = node.left;
}
closest = self _KDTreeNearest(near, point, closest, closestdist, (dem+1)%3);
closest = self _KDTreeNearest(far, point, closest, DistanceSquared(closest.origin, point), (dem+1)%3);
}
return closest;
}
/*
Called on a rectangle, returns the distance from origin to the rectangle.
*/
RectDistanceSquared(origin)
{
dx = 0;
dy = 0;
dz = 0;
if(origin[0] < self.x0)
dx = origin[0] - self.x0;
else if(origin[0] > self.x1)
dx = origin[0] - self.x1;
if(origin[1] < self.y0)
dy = origin[1] - self.y0;
else if(origin[1] > self.y1)
dy = origin[1] - self.y1;
if(origin[2] < self.z0)
dz = origin[2] - self.z0;
else if(origin[2] > self.z1)
dz = origin[2] - self.z1;
return dx*dx + dy*dy + dz*dz;
}
/*
A heap invarient comparitor, used for objects, objects with a higher X coord will be first in the heap.
*/
HeapSortCoordX(item, item2)
{
return item.origin[0] > item2.origin[0];
}
/*
A heap invarient comparitor, used for objects, objects with a higher Y coord will be first in the heap.
*/
HeapSortCoordY(item, item2)
{
return item.origin[1] > item2.origin[1];
}
/*
A heap invarient comparitor, used for objects, objects with a higher Z coord will be first in the heap.
*/
HeapSortCoordZ(item, item2)
{
return item.origin[2] > item2.origin[2];
}
/*
A heap invarient comparitor, used for numbers, numbers with the highest number will be first in the heap.
*/
Heap(item, item2)
{
return item > item2;
}
/*
A heap invarient comparitor, used for numbers, numbers with the lowest number will be first in the heap.
*/
ReverseHeap(item, item2)
{
return item < item2;
}
/*
A heap invarient comparitor, used for traces. Wanting the trace with the largest length first in the heap.
*/
HeapTraceFraction(item, item2)
{
return item["fraction"] > item2["fraction"];
}
/*
Returns a new heap.
*/
NewHeap(compare)
{
heap_node = spawnStruct();
heap_node.data = [];
heap_node.compare = compare;
return heap_node;
}
/*
Inserts the item into the heap. Called on a heap.
*/
HeapInsert(item)
{
insert = self.data.size;
self.data[insert] = item;
current = insert+1;
while(current > 1)
{
last = current;
current = int(current/2);
if(![[self.compare]](item, self.data[current-1]))
break;
self.data[last-1] = self.data[current-1];
self.data[current-1] = item;
}
}
/*
Helper function to determine what is the next child of the bst.
*/
_HeapNextChild(node, hsize)
{
left = node * 2;
right = left + 1;
if(left > hsize)
return -1;
if(right > hsize)
return left;
if([[self.compare]](self.data[left-1], self.data[right-1]))
return left;
else
return right;
}
/*
Removes an item from the heap. Called on a heap.
*/
HeapRemove()
{
remove = self.data.size;
if(!remove)
return remove;
move = self.data[remove-1];
self.data[0] = move;
self.data[remove-1] = undefined;
remove--;
if(!remove)
return remove;
last = 1;
next = self _HeapNextChild(1, remove);
while(next != -1)
{
if([[self.compare]](move, self.data[next-1]))
break;
self.data[last-1] = self.data[next-1];
self.data[next-1] = move;
last = next;
next = self _HeapNextChild(next, remove);
}
return remove;
}
/*
A heap invarient comparitor, used for the astar's nodes, wanting the node with the lowest f to be first in the heap.
*/
ReverseHeapAStar(item, item2)
{
return item.f < item2.f;
}
/*
Will linearly search for the nearest waypoint to pos that has a direct line of sight.
*/
GetNearestWaypointWithSight(pos)
{
candidate = undefined;
dist = 9999999999;
for(i = 0; i < level.waypointCount; i++)
{
if(!bulletTracePassed(pos + (0, 0, 15), level.waypoints[i].origin + (0, 0, 15), false, undefined))
continue;
curdis = DistanceSquared(level.waypoints[i].origin, pos);
if(curdis > dist)
continue;
dist = curdis;
candidate = level.waypoints[i];
}
return candidate;
}
/*
Modified Pezbot astar search.
This makes use of sets for quick look up and a heap for a priority queue instead of simple lists which require to linearly search for elements everytime.
Also makes use of the KD tree to search for the nearest node to the goal. We only use the closest node from the KD tree if it has a direct line of sight, else we will have to linearly search for one that we have a line of sight on.
It is also modified to make paths with bots already on more expensive and will try a less congested path first. Thus spliting up the bots onto more paths instead of just one (the smallest).
*/
AStarSearch(start, goal, team, greedy_path)
{
open = NewHeap(::ReverseHeapAStar);//heap
openset = [];//set for quick lookup
closed = [];//set for quick lookup
startwp = level.waypointsKDTree KDTreeNearest(start);//balanced kdtree, for nns
if(!isDefined(startwp))
return [];
_startwp = undefined;
if(!bulletTracePassed(start + (0, 0, 15), startwp.origin + (0, 0, 15), false, undefined))
_startwp = GetNearestWaypointWithSight(start);
if(isDefined(_startwp))
startwp = _startwp;
startwp = startwp.index;
goalwp = level.waypointsKDTree KDTreeNearest(goal);
if(!isDefined(goalwp))
return [];
_goalwp = undefined;
if(!bulletTracePassed(goal + (0, 0, 15), goalwp.origin + (0, 0, 15), false, undefined))
_goalwp = GetNearestWaypointWithSight(goal);
if(isDefined(_goalwp))
goalwp = _goalwp;
goalwp = goalwp.index;
goalorg = level.waypoints[goalWp].origin;
node = spawnStruct();
node.g = 0; //path dist so far
node.h = DistanceSquared(level.waypoints[startWp].origin, goalorg); //herustic, distance to goal for path finding
//node.f = node.h + node.g; // combine path dist and heru, use reverse heap to sort the priority queue by this attru
node.f = node.h;
node.index = startwp;
node.parent = undefined; //we are start, so we have no parent
//push node onto queue
openset[node.index] = node;
open HeapInsert(node);
//while the queue is not empty
while(open.data.size)
{
//pop bestnode from queue
bestNode = open.data[0];
open HeapRemove();
openset[bestNode.index] = undefined;
//check if we made it to the goal
if(bestNode.index == goalwp)
{
path = [];
while(isDefined(bestNode))
{
if(isdefined(team))
level.waypoints[bestNode.index].bots[team]++;
//construct path
path[path.size] = bestNode.index;
bestNode = bestNode.parent;
}
return path;
}
nodeorg = level.waypoints[bestNode.index].origin;
childcount = level.waypoints[bestNode.index].childCount;
//for each child of bestnode
for(i = 0; i < childcount; i++)
{
child = level.waypoints[bestNode.index].children[i];
childorg = level.waypoints[child].origin;
penalty = 1;
if(!greedy_path && isdefined(team))
{
temppen = level.waypoints[child].bots[team];//consider how many bots are taking this path
if(temppen > 1)
penalty = temppen;
}
//calc the total path we have took
newg = bestNode.g + DistanceSquared(nodeorg, childorg)*penalty;//bots on same team's path are more expensive
//check if this child is in open or close with a g value less than newg
inopen = isDefined(openset[child]);
if(inopen && openset[child].g <= newg)
continue;
inclosed = isDefined(closed[child]);
if(inclosed && closed[child].g <= newg)
continue;
if(inopen)
node = openset[child];
else if(inclosed)
node = closed[child];
else
node = spawnStruct();
node.parent = bestNode;
node.g = newg;
node.h = DistanceSquared(childorg, goalorg);
node.f = node.g + node.h;
node.index = child;
//check if in closed, remove it
if(inclosed)
closed[child] = undefined;
//check if not in open, add it
if(!inopen)
{
open HeapInsert(node);
openset[child] = node;
}
}
//done with children, push onto closed
closed[bestNode.index] = bestNode;
}
return [];
}
/*
Taken from t5 gsc.
Returns an array of number's average.
*/
array_average( array )
{
assert( array.size > 0 );
total = 0;
for ( i = 0; i < array.size; i++ )
{
total += array[i];
}
return ( total / array.size );
}
/*
Taken from t5 gsc.
Returns an array of number's standard deviation.
*/
array_std_deviation( array, mean )
{
assert( array.size > 0 );
tmp = [];
for ( i = 0; i < array.size; i++ )
{
tmp[i] = ( array[i] - mean ) * ( array[i] - mean );
}
total = 0;
for ( i = 0; i < tmp.size; i++ )
{
total = total + tmp[i];
}
return Sqrt( total / array.size );
}
/*
Taken from t5 gsc.
Will produce a random number between lower_bound and upper_bound but with a bell curve distribution (more likely to be close to the mean).
*/
random_normal_distribution( mean, std_deviation, lower_bound, upper_bound )
{
x1 = 0;
x2 = 0;
w = 1;
y1 = 0;
while ( w >= 1 )
{
x1 = 2 * RandomFloatRange( 0, 1 ) - 1;
x2 = 2 * RandomFloatRange( 0, 1 ) - 1;
w = x1 * x1 + x2 * x2;
}
w = Sqrt( ( -2.0 * Log( w ) ) / w );
y1 = x1 * w;
number = mean + y1 * std_deviation;
if ( IsDefined( lower_bound ) && number < lower_bound )
{
number = lower_bound;
}
if ( IsDefined( upper_bound ) && number > upper_bound )
{
number = upper_bound;
}
return( number );
}
onUsePlantObjectFix( player )
{
// planted the bomb
if ( !self maps\mp\gametypes\_gameobjects::isFriendlyTeam( player.pers["team"] ) )
{
level thread bombPlantedFix( self, player );
//player logString( "bomb planted: " + self.label );
// disable all bomb zones except this one
for ( index = 0; index < level.bombZones.size; index++ )
{
if ( level.bombZones[index] == self )
continue;
level.bombZones[index] maps\mp\gametypes\_gameobjects::disableObject();
}
player playSound( "mp_bomb_plant" );
player notify ( "bomb_planted" );
//if ( !level.hardcoreMode )
// iPrintLn( &"MP_EXPLOSIVES_PLANTED_BY", player );
leaderDialog( "bomb_planted" );
level thread teamPlayerCardSplash( "callout_bombplanted", player );
level.bombOwner = player;
player thread maps\mp\gametypes\_hud_message::SplashNotify( "plant", maps\mp\gametypes\_rank::getScoreInfoValue( "plant" ) );
player thread maps\mp\gametypes\_rank::giveRankXP( "plant" );
player.bombPlantedTime = getTime();
maps\mp\gametypes\_gamescore::givePlayerScore( "plant", player );
player incPlayerStat( "bombsplanted", 1 );
player thread maps\mp\_matchdata::logGameEvent( "plant", player.origin );
}
}
bombPlantedFix( destroyedObj, player )
{
maps\mp\gametypes\_gamelogic::pauseTimer();
level.bombPlanted = true;
destroyedObj.visuals[0] thread maps\mp\gametypes\_gamelogic::playTickingSound();
level.tickingObject = destroyedObj.visuals[0];
level.timeLimitOverride = true;
setGameEndTime( int( gettime() + (level.bombTimer * 1000) ) );
setDvar( "ui_bomb_timer", 1 );
if ( !level.multiBomb )
{
level.sdBomb maps\mp\gametypes\_gameobjects::allowCarry( "none" );
level.sdBomb maps\mp\gametypes\_gameobjects::setVisibleTeam( "none" );
level.sdBomb maps\mp\gametypes\_gameobjects::setDropped();
level.sdBombModel = level.sdBomb.visuals[0];
}
else
{
for ( index = 0; index < level.players.size; index++ )
{
if ( isDefined( level.players[index].carryIcon ) )
level.players[index].carryIcon destroyElem();
}
trace = bulletTrace( player.origin + (0,0,20), player.origin - (0,0,2000), false, player );
tempAngle = randomfloat( 360 );
forward = (cos( tempAngle ), sin( tempAngle ), 0);
forward = vectornormalize( forward - common_scripts\utility::vector_multiply( trace["normal"], vectordot( forward, trace["normal"] ) ) );
dropAngles = vectortoangles( forward );
level.sdBombModel = spawn( "script_model", trace["position"] );
level.sdBombModel.angles = dropAngles;
level.sdBombModel setModel( "prop_suitcase_bomb" );
}
destroyedObj maps\mp\gametypes\_gameobjects::allowUse( "none" );
destroyedObj maps\mp\gametypes\_gameobjects::setVisibleTeam( "none" );
/*
destroyedObj maps\mp\gametypes\_gameobjects::set2DIcon( "friendly", undefined );
destroyedObj maps\mp\gametypes\_gameobjects::set2DIcon( "enemy", undefined );
destroyedObj maps\mp\gametypes\_gameobjects::set3DIcon( "friendly", undefined );
destroyedObj maps\mp\gametypes\_gameobjects::set3DIcon( "enemy", undefined );
*/
label = destroyedObj maps\mp\gametypes\_gameobjects::getLabel();
// create a new object to defuse with.
trigger = destroyedObj.bombDefuseTrig;
trigger.origin = level.sdBombModel.origin;
visuals = [];
defuseObject = maps\mp\gametypes\_gameobjects::createUseObject( game["defenders"], trigger, visuals, (0,0,32) );
defuseObject maps\mp\gametypes\_gameobjects::allowUse( "friendly" );
defuseObject maps\mp\gametypes\_gameobjects::setUseTime( level.defuseTime );
defuseObject maps\mp\gametypes\_gameobjects::setUseText( &"MP_DEFUSING_EXPLOSIVE" );
defuseObject maps\mp\gametypes\_gameobjects::setUseHintText( &"PLATFORM_HOLD_TO_DEFUSE_EXPLOSIVES" );
defuseObject maps\mp\gametypes\_gameobjects::setVisibleTeam( "any" );
defuseObject maps\mp\gametypes\_gameobjects::set2DIcon( "friendly", "waypoint_defuse" + label );
defuseObject maps\mp\gametypes\_gameobjects::set2DIcon( "enemy", "waypoint_defend" + label );
defuseObject maps\mp\gametypes\_gameobjects::set3DIcon( "friendly", "waypoint_defuse" + label );
defuseObject maps\mp\gametypes\_gameobjects::set3DIcon( "enemy", "waypoint_defend" + label );
defuseObject.label = label;
defuseObject.onBeginUse = maps\mp\gametypes\sd::onBeginUse;
defuseObject.onEndUse = maps\mp\gametypes\sd::onEndUse;
defuseObject.onUse = maps\mp\gametypes\sd::onUseDefuseObject;
defuseObject.useWeapon = "briefcase_bomb_defuse_mp";
level.defuseObject = defuseObject;
maps\mp\gametypes\sd::BombTimerWait();
setDvar( "ui_bomb_timer", 0 );
destroyedObj.visuals[0] maps\mp\gametypes\_gamelogic::stopTickingSound();
if ( level.gameEnded || level.bombDefused )
return;
level.bombExploded = true;
explosionOrigin = level.sdBombModel.origin;
level.sdBombModel hide();
if ( isdefined( player ) )
{
destroyedObj.visuals[0] radiusDamage( explosionOrigin, 512, 200, 20, player );
player incPlayerStat( "targetsdestroyed", 1 );
}
else
destroyedObj.visuals[0] radiusDamage( explosionOrigin, 512, 200, 20 );
rot = randomfloat(360);
explosionEffect = spawnFx( level._effect["bombexplosion"], explosionOrigin + (0,0,50), (0,0,1), (cos(rot),sin(rot),0) );
triggerFx( explosionEffect );
PlayRumbleOnPosition( "grenade_rumble", explosionOrigin );
earthquake( 0.75, 2.0, explosionOrigin, 2000 );
thread playSoundinSpace( "exp_suitcase_bomb_main", explosionOrigin );
if ( isDefined( destroyedObj.exploderIndex ) )
exploder( destroyedObj.exploderIndex );
for ( index = 0; index < level.bombZones.size; index++ )
level.bombZones[index] maps\mp\gametypes\_gameobjects::disableObject();
defuseObject maps\mp\gametypes\_gameobjects::disableObject();
setGameEndTime( 0 );
wait 3;
maps\mp\gametypes\sd::sd_endGame( game["attackers"], game["strings"]["target_destroyed"] );
}
botGiveLoadout( team, class, allowCopycat )
{
self takeAllWeapons();
primaryIndex = 0;
// initialize specialty array
self.specialty = [];
if ( !isDefined( allowCopycat ) )
allowCopycat = true;
primaryWeapon = undefined;
if ( isDefined( self.pers["copyCatLoadout"] ) && self.pers["copyCatLoadout"]["inUse"] && allowCopycat )
{
self maps\mp\gametypes\_class::setClass( "copycat" );
self.class_num = maps\mp\gametypes\_class::getClassIndex( "copycat" );
clonedLoadout = self.pers["copyCatLoadout"];
loadoutPrimary = clonedLoadout["loadoutPrimary"];
loadoutPrimaryAttachment = clonedLoadout["loadoutPrimaryAttachment"];
loadoutPrimaryAttachment2 = clonedLoadout["loadoutPrimaryAttachment2"] ;
loadoutPrimaryCamo = clonedLoadout["loadoutPrimaryCamo"];
loadoutSecondary = clonedLoadout["loadoutSecondary"];
loadoutSecondaryAttachment = clonedLoadout["loadoutSecondaryAttachment"];
loadoutSecondaryAttachment2 = clonedLoadout["loadoutSecondaryAttachment2"];
loadoutSecondaryCamo = clonedLoadout["loadoutSecondaryCamo"];
loadoutEquipment = clonedLoadout["loadoutEquipment"];
loadoutPerk1 = clonedLoadout["loadoutPerk1"];
loadoutPerk2 = clonedLoadout["loadoutPerk2"];
loadoutPerk3 = clonedLoadout["loadoutPerk3"];
loadoutOffhand = clonedLoadout["loadoutOffhand"];
loadoutDeathStreak = "specialty_copycat";
}
else if ( isSubstr( class, "custom" ) )
{
class_num = maps\mp\gametypes\_class::getClassIndex( class );
self.class_num = class_num;
loadoutPrimary = maps\mp\gametypes\_class::cac_getWeapon( class_num, 0 );
loadoutPrimaryAttachment = maps\mp\gametypes\_class::cac_getWeaponAttachment( class_num, 0 );
loadoutPrimaryAttachment2 = maps\mp\gametypes\_class::cac_getWeaponAttachmentTwo( class_num, 0 );
loadoutPrimaryCamo = maps\mp\gametypes\_class::cac_getWeaponCamo( class_num, 0 );
loadoutSecondaryCamo = maps\mp\gametypes\_class::cac_getWeaponCamo( class_num, 1 );
loadoutSecondary = maps\mp\gametypes\_class::cac_getWeapon( class_num, 1 );
loadoutSecondaryAttachment = maps\mp\gametypes\_class::cac_getWeaponAttachment( class_num, 1 );
loadoutSecondaryAttachment2 = maps\mp\gametypes\_class::cac_getWeaponAttachmentTwo( class_num, 1 );
loadoutSecondaryCamo = maps\mp\gametypes\_class::cac_getWeaponCamo( class_num, 1 );
loadoutEquipment = maps\mp\gametypes\_class::cac_getPerk( class_num, 0 );
loadoutPerk1 = maps\mp\gametypes\_class::cac_getPerk( class_num, 1 );
loadoutPerk2 = maps\mp\gametypes\_class::cac_getPerk( class_num, 2 );
loadoutPerk3 = maps\mp\gametypes\_class::cac_getPerk( class_num, 3 );
loadoutOffhand = maps\mp\gametypes\_class::cac_getOffhand( class_num );
loadoutDeathStreak = maps\mp\gametypes\_class::cac_getDeathstreak( class_num );
}
else
{
class_num = maps\mp\gametypes\_class::getClassIndex( class );
self.class_num = class_num;
loadoutPrimary = maps\mp\gametypes\_class::table_getWeapon( level.classTableName, class_num, 0 );
loadoutPrimaryAttachment = maps\mp\gametypes\_class::table_getWeaponAttachment( level.classTableName, class_num, 0 , 0);
loadoutPrimaryAttachment2 = maps\mp\gametypes\_class::table_getWeaponAttachment( level.classTableName, class_num, 0, 1 );
loadoutPrimaryCamo = maps\mp\gametypes\_class::table_getWeaponCamo( level.classTableName, class_num, 0 );
loadoutSecondaryCamo = maps\mp\gametypes\_class::table_getWeaponCamo( level.classTableName, class_num, 1 );
loadoutSecondary = maps\mp\gametypes\_class::table_getWeapon( level.classTableName, class_num, 1 );
loadoutSecondaryAttachment = maps\mp\gametypes\_class::table_getWeaponAttachment( level.classTableName, class_num, 1 , 0);
loadoutSecondaryAttachment2 = maps\mp\gametypes\_class::table_getWeaponAttachment( level.classTableName, class_num, 1, 1 );;
loadoutSecondaryCamo = maps\mp\gametypes\_class::table_getWeaponCamo( level.classTableName, class_num, 1 );
loadoutEquipment = maps\mp\gametypes\_class::table_getEquipment( level.classTableName, class_num, 0 );
loadoutPerk1 = maps\mp\gametypes\_class::table_getPerk( level.classTableName, class_num, 1 );
loadoutPerk2 = maps\mp\gametypes\_class::table_getPerk( level.classTableName, class_num, 2 );
loadoutPerk3 = maps\mp\gametypes\_class::table_getPerk( level.classTableName, class_num, 3 );
loadoutOffhand = maps\mp\gametypes\_class::table_getOffhand( level.classTableName, class_num );
loadoutDeathstreak = maps\mp\gametypes\_class::table_getDeathstreak( level.classTableName, class_num );
}
if ( loadoutPerk1 != "specialty_bling" )
{
loadoutPrimaryAttachment2 = "none";
loadoutSecondaryAttachment2 = "none";
}
if ( loadoutPerk1 != "specialty_onemanarmy" && loadoutSecondary == "onemanarmy" )
loadoutSecondary = maps\mp\gametypes\_class::table_getWeapon( level.classTableName, 10, 1 );
//loadoutSecondaryCamo = "none";
if ( level.killstreakRewards )
{
if ( getDvarInt( "scr_classic" ) == 1 )
{
loadoutKillstreak1 = "uav";
loadoutKillstreak2 = "precision_airstrike";
loadoutKillstreak3 = "helicopter";
}
else
{
loadoutKillstreak1 = self getPlayerData( "killstreaks", 0 );
loadoutKillstreak2 = self getPlayerData( "killstreaks", 1 );
loadoutKillstreak3 = self getPlayerData( "killstreaks", 2 );
}
}
else
{
loadoutKillstreak1 = "none";
loadoutKillstreak2 = "none";
loadoutKillstreak3 = "none";
}
secondaryName = maps\mp\gametypes\_class::buildWeaponName( loadoutSecondary, loadoutSecondaryAttachment, loadoutSecondaryAttachment2 );
self _giveWeapon( secondaryName, int(tableLookup( "mp/camoTable.csv", 1, loadoutSecondaryCamo, 0 ) ) );
self.loadoutPrimaryCamo = int(tableLookup( "mp/camoTable.csv", 1, loadoutPrimaryCamo, 0 ));
self.loadoutPrimary = loadoutPrimary;
self.loadoutSecondary = loadoutSecondary;
self.loadoutSecondaryCamo = int(tableLookup( "mp/camoTable.csv", 1, loadoutSecondaryCamo, 0 ));
self SetOffhandPrimaryClass( "other" );
// Action Slots
//self _SetActionSlot( 1, "" );
self _SetActionSlot( 1, "nightvision" );
self _SetActionSlot( 3, "altMode" );
self _SetActionSlot( 4, "" );
// Perks
self _clearPerks();
self maps\mp\gametypes\_class::_detachAll();
// these special case giving pistol death have to come before
// perk loadout to ensure player perk icons arent overwritten
if ( level.dieHardMode )
self maps\mp\perks\_perks::givePerk( "specialty_pistoldeath" );
// only give the deathstreak for the initial spawn for this life.
if ( loadoutDeathStreak != "specialty_null" && (getTime() - self.spawnTime) < 0.1 )
{
deathVal = int( tableLookup( "mp/perkTable.csv", 1, loadoutDeathStreak, 6 ) );
if ( self botGetPerkUpgrade( loadoutPerk1 ) == "specialty_rollover" || self botGetPerkUpgrade( loadoutPerk2 ) == "specialty_rollover" || self botGetPerkUpgrade( loadoutPerk3 ) == "specialty_rollover" )
deathVal -= 1;
if ( self.pers["cur_death_streak"] == deathVal )
{
self thread maps\mp\perks\_perks::givePerk( loadoutDeathStreak );
self thread maps\mp\gametypes\_hud_message::splashNotify( loadoutDeathStreak );
}
else if ( self.pers["cur_death_streak"] > deathVal )
{
self thread maps\mp\perks\_perks::givePerk( loadoutDeathStreak );
}
}
self botLoadoutAllPerks( loadoutEquipment, loadoutPerk1, loadoutPerk2, loadoutPerk3 );
self maps\mp\gametypes\_class::setKillstreaks( loadoutKillstreak1, loadoutKillstreak2, loadoutKillstreak3 );
if ( self hasPerk( "specialty_extraammo", true ) && getWeaponClass( secondaryName ) != "weapon_projectile" )
self giveMaxAmmo( secondaryName );
// Primary Weapon
primaryName = maps\mp\gametypes\_class::buildWeaponName( loadoutPrimary, loadoutPrimaryAttachment, loadoutPrimaryAttachment2 );
self _giveWeapon( primaryName, self.loadoutPrimaryCamo );
// fix changing from a riotshield class to a riotshield class during grace period not giving a shield
if ( primaryName == "riotshield_mp" && level.inGracePeriod )
self notify ( "weapon_change", "riotshield_mp" );
if ( self hasPerk( "specialty_extraammo", true ) )
self giveMaxAmmo( primaryName );
self setSpawnWeapon( primaryName );
primaryTokens = strtok( primaryName, "_" );
self.pers["primaryWeapon"] = primaryTokens[0];
// Primary Offhand was given by givePerk (it's your perk1)
// Secondary Offhand
offhandSecondaryWeapon = loadoutOffhand + "_mp";
if ( loadoutOffhand == "flash_grenade" )
self SetOffhandSecondaryClass( "flash" );
else
self SetOffhandSecondaryClass( "smoke" );
self giveWeapon( offhandSecondaryWeapon );
if( loadOutOffhand == "smoke_grenade" )
self setWeaponAmmoClip( offhandSecondaryWeapon, 1 );
else if( loadOutOffhand == "flash_grenade" )
self setWeaponAmmoClip( offhandSecondaryWeapon, 2 );
else if( loadOutOffhand == "concussion_grenade" )
self setWeaponAmmoClip( offhandSecondaryWeapon, 2 );
else
self setWeaponAmmoClip( offhandSecondaryWeapon, 1 );
primaryWeapon = primaryName;
self.primaryWeapon = primaryWeapon;
self.secondaryWeapon = secondaryName;
self botPlayerModelForWeapon( self.pers["primaryWeapon"], getBaseWeaponName( secondaryName ) );
self.isSniper = (weaponClass( self.primaryWeapon ) == "sniper");
self maps\mp\gametypes\_weapons::updateMoveSpeedScale( "primary" );
// cac specialties that require loop threads
self maps\mp\perks\_perks::cac_selector();
self notify ( "changed_kit" );
}
botGetPerkUpgrade( perkName )
{
perkUpgrade = tablelookup( "mp/perktable.csv", 1, perkName, 8 );
if ( perkUpgrade == "" || perkUpgrade == "specialty_null" )
return "specialty_null";
if ( !isDefined(self.pers["bots"]["unlocks"]["upgraded_"+perkName]) || !self.pers["bots"]["unlocks"]["upgraded_"+perkName] )
return "specialty_null";
return ( perkUpgrade );
}
botLoadoutAllPerks( loadoutEquipment, loadoutPerk1, loadoutPerk2, loadoutPerk3 )
{
loadoutEquipment = maps\mp\perks\_perks::validatePerk( 1, loadoutEquipment );
loadoutPerk1 = maps\mp\perks\_perks::validatePerk( 1, loadoutPerk1 );
loadoutPerk2 = maps\mp\perks\_perks::validatePerk( 2, loadoutPerk2 );
loadoutPerk3 = maps\mp\perks\_perks::validatePerk( 3, loadoutPerk3 );
self maps\mp\perks\_perks::givePerk( loadoutEquipment );
self maps\mp\perks\_perks::givePerk( loadoutPerk1 );
self maps\mp\perks\_perks::givePerk( loadoutPerk2 );
self maps\mp\perks\_perks::givePerk( loadoutPerk3 );
perks[0] = loadoutPerk1;
perks[1] = loadoutPerk2;
perks[2] = loadoutPerk3;
perkUpgrd[0] = tablelookup( "mp/perktable.csv", 1, loadoutPerk1, 8 );
perkUpgrd[1] = tablelookup( "mp/perktable.csv", 1, loadoutPerk2, 8 );
perkUpgrd[2] = tablelookup( "mp/perktable.csv", 1, loadoutPerk3, 8 );
for (i = 0; i < perkUpgrd.size; i++)
{
upgrade = perkUpgrd[i];
perk = perks[i];
if ( upgrade == "" || upgrade == "specialty_null" )
continue;
if ( isDefined(self.pers["bots"]["unlocks"]["upgraded_"+perk]) && self.pers["bots"]["unlocks"]["upgraded_"+perk] )
self maps\mp\perks\_perks::givePerk( upgrade );
}
}
botPlayerModelForWeapon( weapon, secondary )
{
team = self.team;
if ( isDefined( game[team + "_model"][weapon] ) )
{
[[game[team+"_model"][weapon]]]();
return;
}
weaponClass = tablelookup( "mp/statstable.csv", 4, weapon, 2 );
switch ( weaponClass )
{
case "weapon_smg":
[[game[team+"_model"]["SMG"]]]();
break;
case "weapon_assault":
weaponClass = tablelookup( "mp/statstable.csv", 4, secondary, 2 );
if ( weaponClass == "weapon_shotgun" )
[[game[team+"_model"]["SHOTGUN"]]]();
else
[[game[team+"_model"]["ASSAULT"]]]();
break;
case "weapon_sniper":
if ( level.environment != "" && isDefined(self.pers["bots"]["unlocks"]["ghillie"]) && self.pers["bots"]["unlocks"]["ghillie"] )
[[game[team+"_model"]["GHILLIE"]]]();
else
[[game[team+"_model"]["SNIPER"]]]();
break;
case "weapon_lmg":
[[game[team+"_model"]["LMG"]]]();
break;
case "weapon_riot":
[[game[team+"_model"]["RIOT"]]]();
break;
default:
[[game[team+"_model"]["ASSAULT"]]]();
break;
}
}
claymoreDetonationBotFix()
{
self endon( "death" );
self waittill( "missile_stuck" );
damagearea = spawn( "trigger_radius", self.origin + ( 0, 0, 0 - level.claymoreDetonateRadius ), 0, level.claymoreDetonateRadius, level.claymoreDetonateRadius * 2 );
self thread maps\mp\gametypes\_weapons::deleteOnDeath( damagearea );
while ( 1 )
{
damagearea waittill( "trigger", player );
if (!player is_bot())
continue;
if ( getdvarint( "scr_claymoredebug" ) != 1 )
{
if ( isdefined( self.owner ) && player == self.owner )
continue;
if ( !maps\mp\gametypes\_weapons::friendlyFireCheck( self.owner, player, 0 ) )
continue;
}
if ( lengthsquared( player getBotVelocity() ) < 10 )
continue;
if ( !player maps\mp\gametypes\_weapons::shouldAffectClaymore( self ) )
continue;
if ( player damageConeTrace( self.origin, self ) > 0 )
break;
}
self playsound ("claymore_activated");
if ( player _hasPerk( "specialty_delaymine" ) )
wait 3.0;
else
wait level.claymoreDetectionGracePeriod;
self detonate();
}