diff --git a/maps/mp/bots/_bot.gsc b/maps/mp/bots/_bot.gsc index b534553..48e59ad 100644 --- a/maps/mp/bots/_bot.gsc +++ b/maps/mp/bots/_bot.gsc @@ -1,8 +1,910 @@ +/* + _bot + Author: INeedGames + Date: 09/26/2020 + The entry point and manager of the bots. +*/ + #include common_scripts\utility; #include maps\mp\_utility; #include maps\mp\bots\_bot_utility; +/* + Initiates the whole bot scripts. +*/ init() { - maps\mp\bots\_bot_utility::onUsePlantObjectFix(level); + level.bw_VERSION = "1.0.0"; + + if(getDvar("bots_main") == "") + setDvar("bots_main", true); + + if (!getDvarInt("bots_main")) + return; + + thread load_waypoints(); + thread hook_callbacks(); + + if(getDvar("bots_main_GUIDs") == "") + setDvar("bots_main_GUIDs", "");//guids of players who will be given host powers, comma seperated + if(getDvar("bots_main_firstIsHost") == "") + setDvar("bots_main_firstIsHost", false);//first play to connect is a host + if(getDvar("bots_main_waitForHostTime") == "") + setDvar("bots_main_waitForHostTime", 10.0);//how long to wait to wait for the host player + + if(getDvar("bots_manage_add") == "") + setDvar("bots_manage_add", 0);//amount of bots to add to the game + if(getDvar("bots_manage_fill") == "") + setDvar("bots_manage_fill", 0);//amount of bots to maintain + if(getDvar("bots_manage_fill_spec") == "") + setDvar("bots_manage_fill_spec", true);//to count for fill if player is on spec team + if(getDvar("bots_manage_fill_mode") == "") + setDvar("bots_manage_fill_mode", 0);//fill mode, 0 adds everyone, 1 just bots, 2 maintains at maps, 3 is 2 with 1 + if(getDvar("bots_manage_fill_kick") == "") + setDvar("bots_manage_fill_kick", false);//kick bots if too many + + if(getDvar("bots_team") == "") + setDvar("bots_team", "autoassign");//which team for bots to join + if(getDvar("bots_team_amount") == "") + setDvar("bots_team_amount", 0);//amount of bots on axis team + if(getDvar("bots_team_force") == "") + setDvar("bots_team_force", false);//force bots on team + if(getDvar("bots_team_mode") == "") + setDvar("bots_team_mode", 0);//counts just bots when 1 + + if(getDvar("bots_skill") == "") + setDvar("bots_skill", 0);//0 is random, 1 is easy 7 is hard, 8 is custom, 9 is completely random + if(getDvar("bots_skill_axis_hard") == "") + setDvar("bots_skill_axis_hard", 0);//amount of hard bots on axis team + if(getDvar("bots_skill_axis_med") == "") + setDvar("bots_skill_axis_med", 0); + if(getDvar("bots_skill_allies_hard") == "") + setDvar("bots_skill_allies_hard", 0); + if(getDvar("bots_skill_allies_med") == "") + setDvar("bots_skill_allies_med", 0); + + if(getDvar("bots_loadout_reasonable") == "")//filter out the bad 'guns' and perks + setDvar("bots_loadout_reasonable", false); + if(getDvar("bots_loadout_allow_op") == "")//allows jug, marty and laststand + setDvar("bots_loadout_allow_op", true); + if(getDvar("bots_loadout_rank") == "")// what rank the bots should be around, -1 is around the players, 0 is all random + setDvar("bots_loadout_rank", -1); + if(getDvar("bots_loadout_prestige") == "")// what pretige the bots will be, -1 is the players, -2 is random + setDvar("bots_loadout_prestige", -1); + + if(getDvar("bots_play_move") == "")//bots move + setDvar("bots_play_move", true); + if(getDvar("bots_play_knife") == "")//bots knife + setDvar("bots_play_knife", true); + if(getDvar("bots_play_fire") == "")//bots fire + setDvar("bots_play_fire", true); + if(getDvar("bots_play_nade") == "")//bots grenade + setDvar("bots_play_nade", true); + if(getDvar("bots_play_take_carepackages") == "")//bots take carepackages + setDvar("bots_play_take_carepackages", true); + if(getDvar("bots_play_obj") == "")//bots play the obj + setDvar("bots_play_obj", true); + if(getDvar("bots_play_camp") == "")//bots camp and follow + setDvar("bots_play_camp", true); + if(getDvar("bots_play_jumpdrop") == "")//bots jump and dropshot + setDvar("bots_play_jumpdrop", true); + if(getDvar("bots_play_target_other") == "")//bot target non play ents (vehicles) + setDvar("bots_play_target_other", true); + if(getDvar("bots_play_killstreak") == "")//bot use killstreaks + setDvar("bots_play_killstreak", true); + if(getDvar("bots_play_ads") == "")//bot ads + setDvar("bots_play_ads", true); + + if(!isDefined(game["botWarfare"])) + game["botWarfare"] = true; + + level.defuseObject = undefined; + level.bots_smokeList = List(); + level.bots_fragList = List(); + + level.bots_minSprintDistance = 315; + level.bots_minSprintDistance *= level.bots_minSprintDistance; + level.bots_minGrenadeDistance = 256; + level.bots_minGrenadeDistance *= level.bots_minGrenadeDistance; + level.bots_maxGrenadeDistance = 1024; + level.bots_maxGrenadeDistance *= level.bots_maxGrenadeDistance; + level.bots_maxKnifeDistance = 80; + level.bots_maxKnifeDistance *= level.bots_maxKnifeDistance; + level.bots_goalDistance = 27.5; + level.bots_goalDistance *= level.bots_goalDistance; + level.bots_noADSDistance = 200; + level.bots_noADSDistance *= level.bots_noADSDistance; + level.bots_maxShotgunDistance = 500; + level.bots_maxShotgunDistance *= level.bots_maxShotgunDistance; + level.bots_listenDist = 100; + + level.smokeRadius = 255; + + level.bots = []; + + level.bots_fullautoguns = []; + level.bots_fullautoguns["aa12"] = true; + level.bots_fullautoguns["ak47"] = true; + level.bots_fullautoguns["aug"] = true; + level.bots_fullautoguns["fn2000"] = true; + level.bots_fullautoguns["glock"] = true; + level.bots_fullautoguns["kriss"] = true; + level.bots_fullautoguns["m4"] = true; + level.bots_fullautoguns["m240"] = true; + level.bots_fullautoguns["masada"] = true; + level.bots_fullautoguns["mg4"] = true; + level.bots_fullautoguns["mp5k"] = true; + level.bots_fullautoguns["p90"] = true; + level.bots_fullautoguns["pp2000"] = true; + level.bots_fullautoguns["rpd"] = true; + level.bots_fullautoguns["sa80"] = true; + level.bots_fullautoguns["scar"] = true; + level.bots_fullautoguns["tavor"] = true; + level.bots_fullautoguns["tmp"] = true; + level.bots_fullautoguns["ump45"] = true; + level.bots_fullautoguns["uzi"] = true; + + level.bots_fullautoguns["ac130"] = true; + level.bots_fullautoguns["heli"] = true; + + level.bots_fullautoguns["ak47classic"] = true; + level.bots_fullautoguns["ak74u"] = true; + level.bots_fullautoguns["peacekeeper"] = true; + + level thread fixGamemodes(); + + level thread onPlayerConnect(); + level thread addNotifyOnAirdrops(); + level thread watchScrabler(); + + level thread handleBots(); + + //level thread maps\mp\bots\_bot_http::doVersionCheck(); +} + +/* + Starts the threads for bots. +*/ +handleBots() +{ + level thread teamBots(); + level thread diffBots(); + level addBots(); + + while(!level.intermission) + wait 0.05; + + setDvar("bots_manage_add", getBotArray().size); +} + +/* + The hook callback for when any player becomes damaged. +*/ +onPlayerDamage(eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset) +{ + if(self is_bot()) + { + //self maps\mp\bots\_bot_internal::onDamage(eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset); + //self maps\mp\bots\_bot_script::onDamage(eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset); + } + + self [[level.prevCallbackPlayerDamage]](eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset); +} + +/* + The hook callback when any player gets killed. +*/ +onPlayerKilled(eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration) +{ + if(self is_bot()) + { + //self maps\mp\bots\_bot_internal::onKilled(eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration); + //self maps\mp\bots\_bot_script::onKilled(eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration); + } + + self [[level.prevCallbackPlayerKilled]](eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration); +} + +/* + Starts the callbacks. +*/ +hook_callbacks() +{ + level waittill( "prematch_over" ); // iw4madmin waits this long for some reason... + wait 0.05; // so we need to be one frame after it sets up its callbacks. + level.prevCallbackPlayerDamage = level.callbackPlayerDamage; + level.callbackPlayerDamage = ::onPlayerDamage; + + level.prevCallbackPlayerKilled = level.callbackPlayerKilled; + level.callbackPlayerKilled = ::onPlayerKilled; +} + +/* + Fixes gamemodes when level starts. +*/ +fixGamemodes() +{ + for(i=0;i<19;i++) + { + if(isDefined(level.bombZones) && level.gametype == "sd") + { + for(i = 0; i < level.bombZones.size; i++) + level.bombZones[i].onUse = ::onUsePlantObjectFix; + break; + } + + if(isDefined(level.radios) && level.gametype == "koth") + { + level thread fixKoth(); + + break; + } + + if (isDefined(level.bombZones) && level.gametype == "dd") + { + level thread fixDem(); + + break; + } + + wait 0.05; + } +} + +/* + Converts t5 dd to iw4 +*/ +fixDem() +{ + for (;;) + { + level.bombAPlanted = level.aPlanted; + level.bombBPlanted = level.bPlanted; + + for (i = 0; i < level.bombZones.size; i++) + { + bombzone = level.bombZones[i]; + + if (isDefined(bombzone.trigger.trigger_off)) + bombzone.bombExploded = true; + else + bombzone.bombExploded = undefined; + } + + wait 0.05; + } +} + +/* + Fixes the king of the hill headquarters obj +*/ +fixKoth() +{ + level.radio = undefined; + + for(;;) + { + wait 0.05; + + if(!isDefined(level.radioObject)) + { + continue; + } + + for(i = level.radios.size - 1; i >= 0; i--) + { + if(level.radioObject != level.radios[i].gameobject) + continue; + + level.radio = level.radios[i]; + break; + } + + while(isDefined(level.radioObject) && level.radio.gameobject == level.radioObject) + wait 0.05; + } +} + +/* + Adds a notify when the airdrop is dropped +*/ +addNotifyOnAirdrops() +{ + for (;;) + { + wait 1; + dropCrates = getEntArray( "care_package", "targetname" ); + + for (i = dropCrates.size - 1; i >= 0; i--) + { + airdrop = dropCrates[i]; + + if (!isDefined(airdrop.owner)) + continue; + + if (isDefined(airdrop.doingPhysics)) + continue; + + airdrop.doingPhysics = true; + airdrop thread doNotifyOnAirdrop(); + } + } +} + +/* + Does the notify +*/ +doNotifyOnAirdrop() +{ + self endon( "death" ); + self waittill( "physics_finished" ); + + self.doingPhysics = false; + self.owner notify("crate_physics_done"); +} + +/* + Thread when any player connects. Starts the threads needed. +*/ +onPlayerConnect() +{ + for(;;) + { + level waittill("connected", player); + + player thread onGrenadeFire(); + player thread onWeaponFired(); + + player thread connected(); + } +} + +/* + Watches players with scrambler perk +*/ +watchScrabler() +{ + for (;;) + { + wait 1; + + for ( i = level.players.size - 1; i >= 0; i-- ) + { + player = level.players[i]; + player.bot_isScrambled = false; + } + + for ( i = level.players.size - 1; i >= 0; i-- ) + { + player = level.players[i]; + + if (!player _HasPerk("specialty_localjammer") || !isReallyAlive(player)) + continue; + + if (player isEMPed()) + continue; + + for ( h = level.players.size - 1; h >= 0; h-- ) + { + player2 = level.players[h]; + + if (player2 == player) + continue; + + if(level.teamBased && player2.team == player.team) + continue; + + if (DistanceSquared(player2.origin, player.origin) > 256*256) + continue; + + player2.bot_isScrambled = true; + } + } + } +} + +/* + When a bot disconnects. +*/ +onDisconnect() +{ + self waittill("disconnect"); + + level.bots = array_remove(level.bots, self); +} + +/* + Called when a player connects. +*/ +connected() +{ + self endon("disconnect"); + + if (!isDefined(self.pers["bot_host"])) + self thread doHostCheck(); + + if(!self is_bot()) + return; + + if (!isDefined(self.pers["isBot"])) + { + // fast_restart occured... + self.pers["isBot"] = true; + } + + if (!isDefined(self.pers["isBotWarfare"])) + { + self.pers["isBotWarfare"] = true; + self thread added(); + } + + //self thread maps\mp\bots\_bot_internal::connected(); + //self thread maps\mp\bots\_bot_script::connected(); + + level.bots[level.bots.size] = self; + self thread onDisconnect(); + + level notify("bot_connected", self); +} + +/* + When a bot gets added into the game. +*/ +added() +{ + self endon("disconnect"); + + //self thread maps\mp\bots\_bot_internal::added(); + //self thread maps\mp\bots\_bot_script::added(); +} + +/* + Adds a bot to the game. +*/ +add_bot() +{ + bot = addtestclient(); + + if (isdefined(bot)) + { + bot.pers["isBot"] = true; + bot.pers["isBotWarfare"] = true; + bot thread added(); + } +} + +/* + A server thread for monitoring all bot's difficulty levels for custom server settings. +*/ +diffBots() +{ + for(;;) + { + wait 1.5; + + var_allies_hard = getDVarInt("bots_skill_allies_hard"); + var_allies_med = getDVarInt("bots_skill_allies_med"); + var_axis_hard = getDVarInt("bots_skill_axis_hard"); + var_axis_med = getDVarInt("bots_skill_axis_med"); + var_skill = getDvarInt("bots_skill"); + + allies_hard = 0; + allies_med = 0; + axis_hard = 0; + axis_med = 0; + + if(var_skill == 8) + { + playercount = level.players.size; + for(i = 0; i < playercount; i++) + { + player = level.players[i]; + + if(!isDefined(player.pers["team"])) + continue; + + if(!player is_bot()) + continue; + + if(player.pers["team"] == "axis") + { + if(axis_hard < var_axis_hard) + { + axis_hard++; + player.pers["bots"]["skill"]["base"] = 7; + } + else if(axis_med < var_axis_med) + { + axis_med++; + player.pers["bots"]["skill"]["base"] = 4; + } + else + player.pers["bots"]["skill"]["base"] = 1; + } + else if(player.pers["team"] == "allies") + { + if(allies_hard < var_allies_hard) + { + allies_hard++; + player.pers["bots"]["skill"]["base"] = 7; + } + else if(allies_med < var_allies_med) + { + allies_med++; + player.pers["bots"]["skill"]["base"] = 4; + } + else + player.pers["bots"]["skill"]["base"] = 1; + } + } + } + else if (var_skill != 0 && var_skill != 9) + { + playercount = level.players.size; + for(i = 0; i < playercount; i++) + { + player = level.players[i]; + + if(!player is_bot()) + continue; + + player.pers["bots"]["skill"]["base"] = var_skill; + } + } + } +} + +/* + A server thread for monitoring all bot's teams for custom server settings. +*/ +teamBots() +{ + for(;;) + { + wait 1.5; + teamAmount = getDvarInt("bots_team_amount"); + toTeam = getDvar("bots_team"); + + alliesbots = 0; + alliesplayers = 0; + axisbots = 0; + axisplayers = 0; + + playercount = level.players.size; + for(i = 0; i < playercount; i++) + { + player = level.players[i]; + + if(!isDefined(player.pers["team"])) + continue; + + if(player is_bot()) + { + if(player.pers["team"] == "allies") + alliesbots++; + else if(player.pers["team"] == "axis") + axisbots++; + } + else + { + if(player.pers["team"] == "allies") + alliesplayers++; + else if(player.pers["team"] == "axis") + axisplayers++; + } + } + + allies = alliesbots; + axis = axisbots; + + if(!getDvarInt("bots_team_mode")) + { + allies += alliesplayers; + axis += axisplayers; + } + + if(toTeam != "custom") + { + if(getDvarInt("bots_team_force")) + { + if(toTeam == "autoassign") + { + if(abs(axis - allies) > 1) + { + toTeam = "axis"; + if(axis > allies) + toTeam = "allies"; + } + } + + if(toTeam != "autoassign") + { + playercount = level.players.size; + for(i = 0; i < playercount; i++) + { + player = level.players[i]; + + if(!isDefined(player.pers["team"])) + continue; + + if(!player is_bot()) + continue; + + if(player.pers["team"] == toTeam) + continue; + + if (toTeam == "allies") + player thread [[level.allies]](); + else if (toTeam == "axis") + player thread [[level.axis]](); + else + player thread [[level.spectator]](); + break; + } + } + } + } + else + { + playercount = level.players.size; + for(i = 0; i < playercount; i++) + { + player = level.players[i]; + + if(!isDefined(player.pers["team"])) + continue; + + if(!player is_bot()) + continue; + + if(player.pers["team"] == "axis") + { + if(axis > teamAmount) + { + player thread [[level.allies]](); + break; + } + } + else + { + if(axis < teamAmount) + { + player thread [[level.axis]](); + break; + } + else if(player.pers["team"] != "allies") + { + player thread [[level.allies]](); + break; + } + } + } + } + } +} + +/* + A server thread for monitoring all bot's in game. Will add and kick bots according to server settings. +*/ +addBots() +{ + level endon("game_ended"); + + bot_wait_for_host(); + + for(;;) + { + wait 1.5; + + botsToAdd = GetDvarInt("bots_manage_add"); + + if(botsToAdd > 0) + { + SetDvar("bots_manage_add", 0); + + if(botsToAdd > 64) + botsToAdd = 64; + + for(; botsToAdd > 0; botsToAdd--) + { + level add_bot(); + wait 0.25; + } + } + + fillMode = getDVarInt("bots_manage_fill_mode"); + + if(fillMode == 2 || fillMode == 3) + setDvar("bots_manage_fill", getGoodMapAmount()); + + fillAmount = getDvarInt("bots_manage_fill"); + + players = 0; + bots = 0; + spec = 0; + + playercount = level.players.size; + for(i = 0; i < playercount; i++) + { + player = level.players[i]; + + if(player is_bot()) + bots++; + else if(!isDefined(player.pers["team"]) || (player.pers["team"] != "axis" && player.pers["team"] != "allies")) + spec++; + else + players++; + } + + if (!randomInt(999)) + { + setDvar("testclients_doreload", true); + wait 0.1; + setDvar("testclients_doreload", false); + doExtraCheck(); + } + + if(fillMode == 4) + { + axisplayers = 0; + alliesplayers = 0; + + playercount = level.players.size; + for(i = 0; i < playercount; i++) + { + player = level.players[i]; + + if(player is_bot()) + continue; + + if(!isDefined(player.pers["team"])) + continue; + + if(player.pers["team"] == "axis") + axisplayers++; + else if(player.pers["team"] == "allies") + alliesplayers++; + } + + result = fillAmount - abs(axisplayers - alliesplayers) + bots; + + if (players == 0) + { + if(bots < fillAmount) + result = fillAmount-1; + else if (bots > fillAmount) + result = fillAmount+1; + else + result = fillAmount; + } + + bots = result; + } + + amount = bots; + if(fillMode == 0 || fillMode == 2) + amount += players; + if(getDVarInt("bots_manage_fill_spec")) + amount += spec; + + if(amount < fillAmount) + setDvar("bots_manage_add", 1); + else if(amount > fillAmount && getDvarInt("bots_manage_fill_kick")) + { + tempBot = random(getBotArray()); + if (isDefined(tempBot)) + kick( tempBot getEntityNumber(), "EXE_PLAYERKICKED" ); + } + } +} + +/* + A thread for ALL players, will monitor and grenades thrown. +*/ +onGrenadeFire() +{ + self endon("disconnect"); + for(;;) + { + self waittill ( "grenade_fire", grenade, weaponName ); + grenade.name = weaponName; + + if(weaponName == "smoke_grenade_mp") + grenade thread AddToSmokeList(); + else if (isSubStr(weaponName, "frag_")) + grenade thread AddToFragList(self); + } +} + +/* + Adds a frag grenade to the list of all frags +*/ +AddToFragList(who) +{ + grenade = spawnstruct(); + grenade.origin = self getOrigin(); + grenade.velocity = (0, 0, 0); + grenade.grenade = self; + grenade.owner = who; + grenade.team = who.team; + grenade.throwback = undefined; + + grenade thread thinkFrag(); + + level.bots_fragList ListAdd(grenade); +} + +/* + Watches while the frag exists +*/ +thinkFrag() +{ + while(isDefined(self.grenade)) + { + nowOrigin = self.grenade getOrigin(); + self.velocity = (nowOrigin - self.origin)*20; + self.origin = nowOrigin; + + wait 0.05; + } + + level.bots_fragList ListRemove(self); +} + +/* + Adds a smoke grenade to the list of smokes in the game. Used to prevent bots from seeing through smoke. +*/ +AddToSmokeList() +{ + grenade = spawnstruct(); + grenade.origin = self getOrigin(); + grenade.state = "moving"; + grenade.grenade = self; + + grenade thread thinkSmoke(); + + level.bots_smokeList ListAdd(grenade); +} + +/* + The smoke grenade logic. +*/ +thinkSmoke() +{ + while(isDefined(self.grenade)) + { + self.origin = self.grenade getOrigin(); + self.state = "moving"; + wait 0.05; + } + self.state = "smoking"; + wait 11.5; + + level.bots_smokeList ListRemove(self); +} + +/* + A thread for ALL players when they fire. +*/ +onWeaponFired() +{ + self endon("disconnect"); + self.bots_firing = false; + for(;;) + { + self waittill( "weapon_fired" ); + self thread doFiringThread(); + } +} + +/* + Lets bot's know that the player is firing. +*/ +doFiringThread() +{ + self endon("disconnect"); + self endon("weapon_fired"); + self.bots_firing = true; + wait 1; + self.bots_firing = false; } diff --git a/maps/mp/bots/_bot_utility.gsc b/maps/mp/bots/_bot_utility.gsc index 9f0dacc..5209771 100644 --- a/maps/mp/bots/_bot_utility.gsc +++ b/maps/mp/bots/_bot_utility.gsc @@ -858,6 +858,8 @@ readWpsFromFile(mapname) } return waypoints;*/ + + return []; } /*