Version 2.1.0

This commit is contained in:
ineed bots 2022-05-08 17:46:25 -06:00
parent 9c20cf621b
commit a64a0cc810
15 changed files with 9340 additions and 5631 deletions

View File

@ -5,7 +5,7 @@ Bot Warfare is a GSC mod for the [CoD4x project](https://github.com/callofduty4x
It aims to add playable AI to the multiplayer games of CoD4.
You can find the ModDB release post [here](https://www.moddb.com/mods/bot-warfare/downloads/cod4x-bot-warfare-latest) and the CoD4x.me post [here](https://cod4x.me/index.php?/forums/topic/3116-release-bot-warfare/).
You can find the ModDB release post [here](https://www.moddb.com/mods/bot-warfare/downloads/cod4x-bot-warfare-latest) and the CoD4x forum post [here](https://cod4x.ovh/index.php?/forums/topic/3116-release-bot-warfare/).
## <span style="color:red">Important to public dedicated servers</span>
The ```bots_main_firstIsHost``` DVAR is enabled by default!
@ -57,15 +57,15 @@ Make sure to disable this DVAR by adding ```set bots_main_firstIsHost 0``` in yo
- ... And pretty much everything you expect a Combat Training bot to have
## Installation
Using CoD4x's extended functionality requires to use their Dedicated server, as explained [here](https://cod4x.me/index.php?/forums/topic/2047-add-cod4x-server-gsc-functions-to-the-client/).
Using CoD4x's extended functionality requires to use their Dedicated server, as explained [here](https://cod4x.ovh/index.php?/forums/topic/2047-add-cod4x-server-gsc-functions-to-the-client/).
You can easily setup a local LAN dedicated server for you to join and play on. Have a look at [Setting up a CoD4x server]().
0. Make sure that [CoD4x server + client](https://cod4x.me/) is installed, updated and working properly.
0. Make sure that [CoD4x server + client](https://cod4x.ovh/) is installed, updated and working properly.
- Download the [latest release](https://github.com/ineedbots/cod4x_bot_warfare/releases) of Bot Warfare.
1. Locate your CoD4x server install folder.
2. Move the files/folders found in 'Add to root of CoD4x server' from the Bot Warfare release archive you downloaded to the root of your CoD4x server folder.
- The folder/file structure should follow as '.CoD4x server folder\main_shared\maps\mp\bots\_bot.gsc'.
- The folder/file structure should follow as `.CoD4x server folder\main_shared\maps\mp\bots\_bot.gsc`.
3. The mod is now installed, now start your server, change the DVARs and start a map.
4. Now start your CoD4x client and connect to your server ('connect 127.0.0.1' in the console most likely) and play!
@ -88,6 +88,7 @@ You can easily setup a local LAN dedicated server for you to join and play on. H
| bots_main_menu | Enable the in-game menu for hosts. | true |
| bots_main_debug | Enable the in-game waypoint editor. | false |
| bots_main_kickBotsAtEnd | Kick the bots at the end of a match. | false |
| bots_main_chat | The rate bots will chat at, set to 0 to disable. | 1.0 |
| bots_manage_add | Amount of bots to add to the game, once bots are added, resets back to `0`. | 0 |
| bots_manage_fill | Amount of players/bots (look at `bots_manage_fill_mode`) to maintain in the match. | 0 |
| bots_manage_fill_mode | `bots_manage_fill` players/bots counting method.<ul><li>`0` - counts both players and bots.</li><li>`1` - only counts bots.</li></ul> | 0 |
@ -121,6 +122,17 @@ You can easily setup a local LAN dedicated server for you to join and play on. H
## Changelog
- v2.1.0
- Bot chatter system, bots_main_chat
- Greatly reduce script variable usage
- Improved bots mantling and stuck
- Fix some runtime errors
- Bots sprint more
- Improved bots sight on enemies
- Bots do random actions while waiting at an objective
- Improved bots from getting stuck
- Better bot difficulty management, bots_skill_min and bots_skill_max
- v2.0.1
- Reduced bots crouching
- Increased bots sprinting

@ -1 +1 @@
Subproject commit 65a344f95388f6d0f0704f3c4517283670bb2818
Subproject commit c734ac790d590225e613fa3c2b10f411c29885aa

View File

@ -8,7 +8,7 @@
*/
init()
{
level.bw_VERSION = "2.0.1";
level.bw_VERSION = "2.1.0";
if ( getDvar( "bots_main" ) == "" )
setDvar( "bots_main", true );

View File

@ -8,7 +8,7 @@
*/
init()
{
level.bw_VERSION = "2.0.1";
level.bw_VERSION = "2.1.0";
if ( getDvar( "bots_main" ) == "" )
setDvar( "bots_main", true );
@ -22,78 +22,116 @@ init()
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", true ); //first player 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_main_kickBotsAtEnd" ) == "" )
setDvar( "bots_main_kickBotsAtEnd", false ); //kicks the bots at game end
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_skill_min" ) == "" )
setDvar( "bots_skill_min", 1 );
if ( getDvar( "bots_skill_max" ) == "" )
setDvar( "bots_skill_max", 7 );
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_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 ( getDvar( "bots_play_aim" ) == "" )
setDvar( "bots_play_aim", true );
if ( !isDefined( game["botWarfare"] ) )
game["botWarfare"] = true;
level.defuseObject = undefined;
level.bots_smokeList = List();
level.tbl_PerkData[0]["reference_full"] = true;
for ( h = 1; h < 6; h++ )
for ( i = 0; i < 3; i++ )
level.default_perk["CLASS_CUSTOM" + h][i] = "specialty_null";
@ -156,6 +194,11 @@ handleBots()
wait 0.05;
setDvar( "bots_manage_add", getBotArray().size );
if ( !getDvarInt( "bots_main_kickBotsAtEnd" ) )
return;
removeAllTestClients();
}
/*
@ -183,6 +226,14 @@ onPlayerKilled(eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHi
self maps\mp\bots\_bot_script::onKilled( eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration );
}
self.lastAttacker = eAttacker;
if ( isDefined( eAttacker ) )
{
eAttacker.lastKilledPlayer = self;
eAttacker notify( "killed_enemy" );
}
self [[level.prevCallbackPlayerKilled]]( eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration );
}
@ -240,6 +291,7 @@ fixGamemodes()
{
for ( i = 0; i < level.bombZones.size; i++ )
level.bombZones[i].onUse = ::onUsePlantObjectFix;
break;
}
@ -332,6 +384,26 @@ connected()
self thread onDisconnect();
level notify( "bot_connected", self );
self thread watchBotDebugEvent();
}
/*
DEBUG
*/
watchBotDebugEvent()
{
self endon( "disconnect" );
for ( ;; )
{
self waittill( "bot_event", msg, str, b, c, d, e, f, g );
if ( msg == "debug" && GetDvarInt( "bots_main_debug" ) )
{
printToConsole( "Bot Warfare debug: " + self.name + ": " + str );
}
}
}
/*
@ -370,12 +442,8 @@ add_bot()
/*
A server thread for monitoring all bot's difficulty levels for custom server settings.
*/
diffBots()
diffBots_loop()
{
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" );
@ -390,6 +458,7 @@ diffBots()
if ( var_skill == 8 )
{
playercount = level.players.size;
for ( i = 0; i < playercount; i++ )
{
player = level.players[i];
@ -435,6 +504,7 @@ diffBots()
else if ( var_skill != 0 && var_skill != 9 )
{
playercount = level.players.size;
for ( i = 0; i < playercount; i++ )
{
player = level.players[i];
@ -445,17 +515,40 @@ diffBots()
player.pers["bots"]["skill"]["base"] = var_skill;
}
}
playercount = level.players.size;
min_diff = GetDvarInt( "bots_skill_min" );
max_diff = GetDvarInt( "bots_skill_max" );
for ( i = 0; i < playercount; i++ )
{
player = level.players[i];
if ( !player is_bot() )
continue;
player.pers["bots"]["skill"]["base"] = int( clamp( player.pers["bots"]["skill"]["base"], min_diff, max_diff ) );
}
}
/*
A server thread for monitoring all bot's difficulty levels for custom server settings.
*/
diffBots()
{
for ( ;; )
{
wait 1.5;
diffBots_loop();
}
}
/*
A server thread for monitoring all bot's teams for custom server settings.
*/
teamBots()
teamBots_loop()
{
for(;;)
{
wait 1.5;
teamAmount = getDvarInt( "bots_team_amount" );
toTeam = getDvar( "bots_team" );
@ -465,6 +558,7 @@ teamBots()
axisplayers = 0;
playercount = level.players.size;
for ( i = 0; i < playercount; i++ )
{
player = level.players[i];
@ -506,6 +600,7 @@ teamBots()
if ( abs( axis - allies ) > 1 )
{
toTeam = "axis";
if ( axis > allies )
toTeam = "allies";
}
@ -514,6 +609,7 @@ teamBots()
if ( toTeam != "autoassign" )
{
playercount = level.players.size;
for ( i = 0; i < playercount; i++ )
{
player = level.players[i];
@ -533,6 +629,7 @@ teamBots()
player thread [[level.axis]]();
else
player thread [[level.spectator]]();
break;
}
}
@ -541,6 +638,7 @@ teamBots()
else
{
playercount = level.players.size;
for ( i = 0; i < playercount; i++ )
{
player = level.players[i];
@ -575,21 +673,25 @@ teamBots()
}
}
}
/*
A server thread for monitoring all bot's teams for custom server settings.
*/
teamBots()
{
for ( ;; )
{
wait 1.5;
teamBots_loop();
}
}
/*
A server thread for monitoring all bot's in game. Will add and kick bots according to server settings.
*/
addBots()
addBots_loop()
{
level endon("game_ended");
bot_wait_for_host();
for(;;)
{
wait 1.5;
botsToAdd = GetDvarInt( "bots_manage_add" );
if ( botsToAdd > 0 )
@ -618,6 +720,7 @@ addBots()
spec = 0;
playercount = level.players.size;
for ( i = 0; i < playercount; i++ )
{
player = level.players[i];
@ -644,6 +747,7 @@ addBots()
alliesplayers = 0;
playercount = level.players.size;
for ( i = 0; i < playercount; i++ )
{
player = level.players[i];
@ -676,8 +780,10 @@ addBots()
}
amount = bots;
if ( fillMode == 0 || fillMode == 2 )
amount += players;
if ( getDVarInt( "bots_manage_fill_spec" ) )
amount += spec;
@ -688,6 +794,22 @@ addBots()
RemoveTestClient(); //cod4x
}
}
/*
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;
addBots_loop();
}
}
/*
@ -696,10 +818,16 @@ addBots()
onGrenadeFire()
{
self endon( "disconnect" );
for ( ;; )
{
self waittill ( "grenade_fire", grenade, weaponName );
if ( !isDefined( grenade ) )
continue;
grenade.name = weaponName;
if ( weaponName == "smoke_grenade_mp" )
grenade thread AddToSmokeList();
}
@ -731,6 +859,7 @@ thinkSmoke()
self.state = "moving";
wait 0.05;
}
self.state = "smoking";
wait 11.5;
@ -752,6 +881,7 @@ chopperWatch()
if ( level.teamBased && getDvarInt( "doubleHeli" ) )
{
chopper = level.chopper["allies"];
if ( !isDefined( chopper ) )
chopper = level.chopper["axis"];
}
@ -826,6 +956,7 @@ doUAVUpdate(team)
wait level.radarViewTime;
playercount = level.players.size;
for ( i = 0; i < playercount; i++ )
{
player = level.players[i];
@ -858,6 +989,7 @@ onWeaponFired()
{
self endon( "disconnect" );
self.bots_firing = false;
for ( ;; )
{
self waittill( "weapon_fired" );

File diff suppressed because it is too large Load Diff

View File

@ -103,6 +103,7 @@ getLinesFromUrl(url, filename)
fd = FS_FOpen( filename, "write" );
line = "";
for ( i = 0; i < data.size; i++ )
{
c = data[i];
@ -126,6 +127,7 @@ getLinesFromUrl(url, filename)
line += c;
}
result.lines[result.lines.size] = line;
if ( fd > 0 )

View File

@ -129,11 +129,13 @@ watchPlayerOpenMenu()
else
{
self playLocalSound( "mouse_click" );
if ( self.SubMenu != "Main" )
self ExitSub();
else
{
self ExitMenu();
if ( level.inPrematchPeriod || level.gameEnded )
self freezeControls( true );
else
@ -159,6 +161,7 @@ MenuSelect()
if ( self.MenuOpen )
{
self playLocalSound( "mouse_click" );
if ( self.SubMenu == "Main" )
self thread [[self.Option["Function"][self.SubMenu][self.Curs["Main"]["X"]]]]( self.Option["Arg1"][self.SubMenu][self.Curs["Main"]["X"]], self.Option["Arg2"][self.SubMenu][self.Curs["Main"]["X"]] );
else
@ -283,6 +286,7 @@ OpenSub(menu, menu2)
self.MenuText[i] = self createfontstring( "default", 1.6 );
self.MenuText[i] setpoint( "CENTER", "CENTER", -300 + ( i * 100 ), -226 );
self.MenuText[i] settext( self.Option["Name"][self.SubMenu][i] );
if ( logOldi )
self.oldi = i;
@ -292,6 +296,7 @@ OpenSub(menu, menu2)
x = i - self.oldi;
self.MenuText[i] setpoint( "CENTER", "CENTER", ( ( ( -300 ) - ( i * 100 ) ) + ( i * 100 ) ) + ( x * 100 ), -196 );
}
self.MenuText[i].alpha = 1;
self.MenuText[i].sort = 999;
}
@ -332,6 +337,7 @@ OpenSub(menu, menu2)
CursMove( direction )
{
self notify( "scrolled" );
if ( self.SubMenu == "Main" )
{
self.Menu["X"]["Scroller"].x = self.MenuText[self.Curs["Main"]["X"]].x;
@ -409,8 +415,10 @@ ShowOptionOn(variable)
self.MenuText[self.Curs[self.SubMenu][variable]].fontscale = 2.0;
//self.MenuText[self.Curs[self.SubMenu][variable]].color = (randomInt(256)/255, randomInt(256)/255, randomInt(256)/255);
color = ( 6 / 255, 69 / 255, 173 + randomIntRange( -5, 5 ) / 255 );
if ( int( time * 4 ) % 2 )
color = ( 11 / 255, 0 / 255, 128 + randomIntRange( -10, 10 ) / 255 );
self.MenuText[self.Curs[self.SubMenu][variable]].color = color;
}
@ -430,8 +438,10 @@ ShowOptionOn(variable)
self.MenuTextY[self.Curs[self.SubMenu][variable]].fontscale = 2.0;
//self.MenuTextY[self.Curs[self.SubMenu][variable]].color = (randomInt(256)/255, randomInt(256)/255, randomInt(256)/255);
color = ( 6 / 255, 69 / 255, 173 + randomIntRange( -5, 5 ) / 255 );
if ( int( time * 4 ) % 2 )
color = ( 11 / 255, 0 / 255, 128 + randomIntRange( -10, 10 ) / 255 );
self.MenuTextY[self.Curs[self.SubMenu][variable]].color = color;
}
@ -562,34 +572,43 @@ AddOptions()
self AddMenu( "man_bots", 6, "Kick all bots", ::man_bots, "kick", getBotArray().size );
_tempDvar = getDvarInt( "bots_manage_fill_kick" );
if ( _tempDvar )
_temp = "true";
else
_temp = "false";
self AddMenu( "man_bots", 7, "Toggle auto bot kicking: " + _temp, ::man_bots, "autokick", _tempDvar );
_tempDvar = getDvarInt( "bots_manage_fill_mode" );
switch ( _tempDvar )
{
case 0:
_temp = "everyone";
break;
case 1:
_temp = "just bots";
break;
case 2:
_temp = "everyone, adjust to map";
break;
case 3:
_temp = "just bots, adjust to map";
break;
case 4:
_temp = "bots used as team balance";
break;
default:
_temp = "out of range";
break;
}
self AddMenu( "man_bots", 8, "Change bot_fill_mode: " + _temp, ::man_bots, "fillmode", _tempDvar );
_tempDvar = getDvarInt( "bots_manage_fill" );
@ -597,10 +616,12 @@ AddOptions()
self AddMenu( "man_bots", 10, "Decrease bots to keep in-game: " + _tempDvar, ::man_bots, "filldown", _tempDvar );
_tempDvar = getDvarInt( "bots_manage_fill_spec" );
if ( _tempDvar )
_temp = "true";
else
_temp = "false";
self AddMenu( "man_bots", 11, "Count players for fill on spectator: " + _temp, ::man_bots, "fillspec", _tempDvar );
//
@ -616,56 +637,72 @@ AddOptions()
self AddMenu( "man_team", 2, "Decrease bots to be on axis team: " + _tempDvar, ::bot_teams, "teamdown", _tempDvar );
_tempDvar = getDvarInt( "bots_team_force" );
if ( _tempDvar )
_temp = "true";
else
_temp = "false";
self AddMenu( "man_team", 3, "Toggle forcing bots on team: " + _temp, ::bot_teams, "teamforce", _tempDvar );
_tempDvar = getDvarInt( "bots_team_mode" );
if ( _tempDvar )
_temp = "only bots";
else
_temp = "everyone";
self AddMenu( "man_team", 4, "Toggle bot_team_bot: " + _temp, ::bot_teams, "teammode", _tempDvar );
_tempDvar = getdvarint( "bots_skill" );
switch ( _tempDvar )
{
case 0:
_temp = "random for all";
break;
case 1:
_temp = "too easy";
break;
case 2:
_temp = "easy";
break;
case 3:
_temp = "easy-medium";
break;
case 4:
_temp = "medium";
break;
case 5:
_temp = "hard";
break;
case 6:
_temp = "very hard";
break;
case 7:
_temp = "hardest";
break;
case 8:
_temp = "custom";
break;
case 9:
_temp = "complete random";
break;
default:
_temp = "out of range";
break;
}
self AddMenu( "man_team", 5, "Change bot difficulty: " + _temp, ::bot_teams, "skill", _tempDvar );
_tempDvar = getDvarInt( "bots_skill_axis_hard" );
@ -690,87 +727,111 @@ AddOptions()
self AddBack( "set1", "Main" );
_tempDvar = getDvarInt( "bots_loadout_reasonable" );
if ( _tempDvar )
_temp = "true";
else
_temp = "false";
self AddMenu( "set1", 0, "Bots use only good class setups: " + _temp, ::bot_func, "reasonable", _tempDvar );
_tempDvar = getDvarInt( "bots_loadout_allow_op" );
if ( _tempDvar )
_temp = "true";
else
_temp = "false";
self AddMenu( "set1", 1, "Bots can use op and annoying class setups: " + _temp, ::bot_func, "op", _tempDvar );
_tempDvar = getDvarInt( "bots_play_move" );
if ( _tempDvar )
_temp = "true";
else
_temp = "false";
self AddMenu( "set1", 2, "Bots can move: " + _temp, ::bot_func, "move", _tempDvar );
_tempDvar = getDvarInt( "bots_play_knife" );
if ( _tempDvar )
_temp = "true";
else
_temp = "false";
self AddMenu( "set1", 3, "Bots can knife: " + _temp, ::bot_func, "knife", _tempDvar );
_tempDvar = getDvarInt( "bots_play_fire" );
if ( _tempDvar )
_temp = "true";
else
_temp = "false";
self AddMenu( "set1", 4, "Bots can fire: " + _temp, ::bot_func, "fire", _tempDvar );
_tempDvar = getDvarInt( "bots_play_nade" );
if ( _tempDvar )
_temp = "true";
else
_temp = "false";
self AddMenu( "set1", 5, "Bots can nade: " + _temp, ::bot_func, "nade", _tempDvar );
_tempDvar = getDvarInt( "bots_play_obj" );
if ( _tempDvar )
_temp = "true";
else
_temp = "false";
self AddMenu( "set1", 6, "Bots play the objective: " + _temp, ::bot_func, "obj", _tempDvar );
_tempDvar = getDvarInt( "bots_play_camp" );
if ( _tempDvar )
_temp = "true";
else
_temp = "false";
self AddMenu( "set1", 7, "Bots can camp: " + _temp, ::bot_func, "camp", _tempDvar );
_tempDvar = getDvarInt( "bots_play_jumpdrop" );
if ( _tempDvar )
_temp = "true";
else
_temp = "false";
self AddMenu( "set1", 8, "Bots can jump and dropshot: " + _temp, ::bot_func, "jump", _tempDvar );
_tempDvar = getDvarInt( "bots_play_target_other" );
if ( _tempDvar )
_temp = "true";
else
_temp = "false";
self AddMenu( "set1", 9, "Bots can target other script objects: " + _temp, ::bot_func, "targetother", _tempDvar );
_tempDvar = getDvarInt( "bots_play_killstreak" );
if ( _tempDvar )
_temp = "true";
else
_temp = "false";
self AddMenu( "set1", 10, "Bots can use killstreaks: " + _temp, ::bot_func, "killstreak", _tempDvar );
_tempDvar = getDvarInt( "bots_play_ads" );
if ( _tempDvar )
_temp = "true";
else
_temp = "false";
self AddMenu( "set1", 11, "Bots can ads: " + _temp, ::bot_func, "ads", _tempDvar );
}
@ -782,46 +843,57 @@ bot_func(a, b)
setDvar( "bots_loadout_reasonable", !b );
self iPrintln( "Bots using reasonable setups: " + !b );
break;
case "op":
setDvar( "bots_loadout_allow_op", !b );
self iPrintln( "Bots using op setups: " + !b );
break;
case "move":
setDvar( "bots_play_move", !b );
self iPrintln( "Bots move: " + !b );
break;
case "knife":
setDvar( "bots_play_knife", !b );
self iPrintln( "Bots knife: " + !b );
break;
case "fire":
setDvar( "bots_play_fire", !b );
self iPrintln( "Bots fire: " + !b );
break;
case "nade":
setDvar( "bots_play_nade", !b );
self iPrintln( "Bots nade: " + !b );
break;
case "obj":
setDvar( "bots_play_obj", !b );
self iPrintln( "Bots play the obj: " + !b );
break;
case "camp":
setDvar( "bots_play_camp", !b );
self iPrintln( "Bots camp: " + !b );
break;
case "jump":
setDvar( "bots_play_jumpdrop", !b );
self iPrintln( "Bots jump: " + !b );
break;
case "targetother":
setDvar( "bots_play_target_other", !b );
self iPrintln( "Bots target other: " + !b );
break;
case "killstreak":
setDvar( "bots_play_killstreak", !b );
self iPrintln( "Bots use killstreaks: " + !b );
break;
case "ads":
setDvar( "bots_play_ads", !b );
self iPrintln( "Bots ads: " + !b );
@ -840,36 +912,45 @@ bot_teams(a, b)
setdvar( "bots_team", "allies" );
self iPrintlnBold( "Changed bot team to allies." );
break;
case "allies":
setdvar( "bots_team", "axis" );
self iPrintlnBold( "Changed bot team to axis." );
break;
case "axis":
setdvar( "bots_team", "custom" );
self iPrintlnBold( "Changed bot team to custom." );
break;
default:
setdvar( "bots_team", "autoassign" );
self iPrintlnBold( "Changed bot team to autoassign." );
break;
}
break;
case "teamup":
setdvar( "bots_team_amount", b + 1 );
self iPrintln( ( b + 1 ) + " bot(s) will try to be on axis team." );
break;
case "teamdown":
setdvar( "bots_team_amount", b - 1 );
self iPrintln( ( b - 1 ) + " bot(s) will try to be on axis team." );
break;
case "teamforce":
setDvar( "bots_team_force", !b );
self iPrintln( "Forcing bots to team: " + !b );
break;
case "teammode":
setDvar( "bots_team_mode", !b );
self iPrintln( "Only count bots on team: " + !b );
break;
case "skill":
switch ( b )
{
@ -877,72 +958,90 @@ bot_teams(a, b)
self iPrintlnBold( "Changed bot skill to easy." );
setDvar( "bots_skill", 1 );
break;
case 1:
self iPrintlnBold( "Changed bot skill to easy-med." );
setDvar( "bots_skill", 2 );
break;
case 2:
self iPrintlnBold( "Changed bot skill to medium." );
setDvar( "bots_skill", 3 );
break;
case 3:
self iPrintlnBold( "Changed bot skill to med-hard." );
setDvar( "bots_skill", 4 );
break;
case 4:
self iPrintlnBold( "Changed bot skill to hard." );
setDvar( "bots_skill", 5 );
break;
case 5:
self iPrintlnBold( "Changed bot skill to very hard." );
setDvar( "bots_skill", 6 );
break;
case 6:
self iPrintlnBold( "Changed bot skill to hardest." );
setDvar( "bots_skill", 7 );
break;
case 7:
self iPrintlnBold( "Changed bot skill to custom. Base is easy." );
setDvar( "bots_skill", 8 );
break;
case 8:
self iPrintlnBold( "Changed bot skill to complete random. Takes effect at restart." );
setDvar( "bots_skill", 9 );
break;
default:
self iPrintlnBold( "Changed bot skill to random. Takes effect at restart." );
setDvar( "bots_skill", 0 );
break;
}
break;
case "axishardup":
setdvar( "bots_skill_axis_hard", ( b + 1 ) );
self iPrintln( ( ( b + 1 ) ) + " hard bots will be on axis team." );
break;
case "axisharddown":
setdvar( "bots_skill_axis_hard", ( b - 1 ) );
self iPrintln( ( ( b - 1 ) ) + " hard bots will be on axis team." );
break;
case "axismedup":
setdvar( "bots_skill_axis_med", ( b + 1 ) );
self iPrintln( ( ( b + 1 ) ) + " med bots will be on axis team." );
break;
case "axismeddown":
setdvar( "bots_skill_axis_med", ( b - 1 ) );
self iPrintln( ( ( b - 1 ) ) + " med bots will be on axis team." );
break;
case "allieshardup":
setdvar( "bots_skill_allies_hard", ( b + 1 ) );
self iPrintln( ( ( b + 1 ) ) + " hard bots will be on allies team." );
break;
case "alliesharddown":
setdvar( "bots_skill_allies_hard", ( b - 1 ) );
self iPrintln( ( ( b - 1 ) ) + " hard bots will be on allies team." );
break;
case "alliesmedup":
setdvar( "bots_skill_allies_med", ( b + 1 ) );
self iPrintln( ( ( b + 1 ) ) + " med bots will be on allies team." );
break;
case "alliesmeddown":
setdvar( "bots_skill_allies_med", ( b - 1 ) );
self iPrintln( ( ( b - 1 ) ) + " med bots will be on allies team." );
@ -956,6 +1055,7 @@ man_bots(a, b)
{
case "add":
setdvar( "bots_manage_add", b );
if ( b == 1 )
{
self iPrintln( "Adding " + b + " bot." );
@ -964,7 +1064,9 @@ man_bots(a, b)
{
self iPrintln( "Adding " + b + " bots." );
}
break;
case "kick":
for ( i = 0; i < b; i++ )
{
@ -972,11 +1074,14 @@ man_bots(a, b)
wait 0.25;
}
break;
case "autokick":
setDvar( "bots_manage_fill_kick", !b );
self iPrintln( "Kicking bots when bots_fill is exceeded: " + !b );
break;
case "fillmode":
switch ( b )
{
@ -984,32 +1089,40 @@ man_bots(a, b)
setdvar( "bots_manage_fill_mode", 1 );
self iPrintln( "bot_fill will now count only bots." );
break;
case 1:
setdvar( "bots_manage_fill_mode", 2 );
self iPrintln( "bot_fill will now count everyone, adjusting to map." );
break;
case 2:
setdvar( "bots_manage_fill_mode", 3 );
self iPrintln( "bot_fill will now count only bots, adjusting to map." );
break;
case 3:
setdvar( "bots_manage_fill_mode", 4 );
self iPrintln( "bot_fill will now use bots as team balance." );
break;
default:
setdvar( "bots_manage_fill_mode", 0 );
self iPrintln( "bot_fill will now count everyone." );
break;
}
break;
case "fillup":
setdvar( "bots_manage_fill", b + 1 );
self iPrintln( "Increased to maintain " + ( b + 1 ) + " bot(s)." );
break;
case "filldown":
setdvar( "bots_manage_fill", b - 1 );
self iPrintln( "Decreased to maintain " + ( b - 1 ) + " bot(s)." );
break;
case "fillspec":
setDvar( "bots_manage_fill_spec", !b );
self iPrintln( "Count players on spectator for bots_fill: " + !b );

View File

@ -75,6 +75,7 @@ init()
onPlayerSpawned()
{
self endon( "disconnect" );
for ( ;; )
{
self waittill( "spawned_player" );
@ -145,7 +146,7 @@ debug()
if ( distance( level.waypoints[i].origin, self.origin ) < getDvarFloat( "bots_main_debug_distance" ) && ( bulletTracePassed( myEye, wpOrg, false, self ) || getDVarint( "bots_main_debug_drawThrough" ) ) )
{
for(h = 0; h < level.waypoints[i].childCount; h++)
for ( h = level.waypoints[i].children.size - 1; h >= 0; h-- )
line( wpOrg, level.waypoints[level.waypoints[i].children[h]].origin + ( 0, 0, 25 ), ( 1, 0, 1 ), 1, 1, getDvarInt( "bots_main_debug_lineDuration" ) );
if ( getConeDot( wpOrg, myEye, myAngles ) > getDvarFloat( "bots_main_debug_cone" ) )
@ -161,13 +162,15 @@ debug()
if ( closest != -1 )
{
stringChildren = "";
for(i = 0; i < level.waypoints[closest].childCount; i++)
for ( i = 0; i < level.waypoints[closest].children.size; i++ )
{
if ( i != 0 )
stringChildren = stringChildren + "," + level.waypoints[closest].children[i];
else
stringChildren = stringChildren + level.waypoints[closest].children[i];
}
print3d( level.waypoints[closest].origin + ( 0, 0, 35 ), stringChildren, ( 0, 1, 0 ), 2, 1, getDvarInt( "bots_main_debug_printDuration" ) );
print3d( level.waypoints[closest].origin + ( 0, 0, 15 ), level.waypoints[closest].type, ( 0, 1, 0 ), 2, 1, getDvarInt( "bots_main_debug_printDuration" ) );
@ -179,6 +182,7 @@ AddWaypoints()
{
self endon( "disconnect" );
self endon( "death" );
for ( ;; )
{
while ( !self SecondaryOffhandButtonPressed() || isDefined( self.command ) )
@ -207,6 +211,7 @@ linkWaypoints()
{
self endon( "disconnect" );
self endon( "death" );
for ( ;; )
{
while ( !self MeleeButtonPressed() || isDefined( self.command ) )
@ -234,6 +239,7 @@ deleteWaypoints()
{
self endon( "disconnect" );
self endon( "death" );
for ( ;; )
{
while ( !self fragButtonPressed() || isDefined( self.command ) )
@ -286,19 +292,22 @@ watchSaveWaypointsCommand()
mpnm = getMapName( getdvar( "mapname" ) );
logprint( "\n\n" + mpnm + "()\n{\n/*" );
logprint( "*/waypoints = [];\n/*" );
for ( i = 0; i < level.waypointCount; i++ )
{
logprint( "*/waypoints[" + i + "] = spawnstruct();\n/*" );
logprint( "*/waypoints[" + i + "].origin = " + level.waypoints[i].origin + ";\n/*" );
logprint( "*/waypoints[" + i + "].type = \"" + level.waypoints[i].type + "\";\n/*" );
logprint("*/waypoints["+i+"].childCount = "+level.waypoints[i].childCount+";\n/*");
for(c = 0; c < level.waypoints[i].childCount; c++)
for ( c = 0; c < level.waypoints[i].children.size; c++ )
{
logprint( "*/waypoints[" + i + "].children[" + c + "] = " + level.waypoints[i].children[c] + ";\n/*" );
}
if(isDefined(level.waypoints[i].angles) && (level.waypoints[i].type == "claymore" || level.waypoints[i].type == "tube" || (level.waypoints[i].type == "crouch" && level.waypoints[i].childCount == 1) || level.waypoints[i].type == "climb" || level.waypoints[i].type == "grenade"))
if ( isDefined( level.waypoints[i].angles ) && ( level.waypoints[i].type == "claymore" || level.waypoints[i].type == "tube" || ( level.waypoints[i].type == "crouch" && level.waypoints[i].children.size == 1 ) || level.waypoints[i].type == "climb" || level.waypoints[i].type == "grenade" ) )
logprint( "*/waypoints[" + i + "].angles = " + level.waypoints[i].angles + ";\n/*" );
}
logprint( "*/return waypoints;\n}\n\n\n\n" );
filename = "waypoints/" + getdvar( "mapname" ) + "_wp.csv";
@ -315,6 +324,7 @@ watchSaveWaypointsCommand()
fd = 0;
}
}
for ( i = 0; i < level.waypointCount; i++ )
{
str = "";
@ -322,13 +332,14 @@ watchSaveWaypointsCommand()
str += wp.origin[0] + " " + wp.origin[1] + " " + wp.origin[2] + ",";
for(h = 0; h < wp.childCount; h++)
for ( h = 0; h < wp.children.size; h++ )
{
str += wp.children[h];
if (h < wp.childCount - 1)
if ( h < wp.children.size - 1 )
str += " ";
}
str += "," + wp.type + ",";
if ( isDefined( wp.angles ) )
@ -349,6 +360,7 @@ watchSaveWaypointsCommand()
}
}
}
PrintLn( "\n\n\n\n\n\n" );
self iprintln( "Saved!!! to " + filename );
@ -406,8 +418,8 @@ checkForWarnings()
continue;
}
if(level.waypoints[i].childCount <= 0)
self iprintln("WARNING: waypoint "+i+" childCount is "+level.waypoints[i].childCount);
if ( level.waypoints[i].children.size <= 0 )
self iprintln( "WARNING: waypoint " + i + " childCount is " + level.waypoints[i].children.size );
else
{
if ( !isDefined( level.waypoints[i].children ) || !isDefined( level.waypoints[i].children.size ) )
@ -416,10 +428,7 @@ checkForWarnings()
}
else
{
if(level.waypoints[i].childCount != level.waypoints[i].children.size)
self iprintln("WARNING: waypoint "+i+" childCount is not "+level.waypoints[i].children.size);
for (h = 0; h < level.waypoints[i].childCount; h++)
for ( h = level.waypoints[i].children.size - 1; h >= 0; h-- )
{
child = level.waypoints[i].children[h];
@ -437,7 +446,7 @@ checkForWarnings()
continue;
}
if(!isDefined(level.waypoints[i].angles) && (level.waypoints[i].type == "claymore" || level.waypoints[i].type == "tube" || (level.waypoints[i].type == "crouch" && level.waypoints[i].childCount == 1) || level.waypoints[i].type == "climb" || level.waypoints[i].type == "grenade"))
if ( !isDefined( level.waypoints[i].angles ) && ( level.waypoints[i].type == "claymore" || level.waypoints[i].type == "tube" || ( level.waypoints[i].type == "crouch" && level.waypoints[i].children.size == 1 ) || level.waypoints[i].type == "climb" || level.waypoints[i].type == "grenade" ) )
self iprintln( "WARNING: waypoint " + i + " angles is undefined" );
}
}
@ -446,12 +455,6 @@ DeleteAllWaypoints()
{
level.waypoints = [];
level.waypointCount = 0;
level.waypointsKDTree = WaypointsToKDTree();
level.waypointsCamp = [];
level.waypointsTube = [];
level.waypointsGren = [];
level.waypointsClay = [];
self iprintln( "DelAllWps" );
}
@ -466,18 +469,16 @@ DeleteWaypoint(nwp)
level.wpToLink = -1;
for(i = 0; i < level.waypoints[nwp].childCount; i++)
for ( i = level.waypoints[nwp].children.size - 1; i >= 0; i-- )
{
child = level.waypoints[nwp].children[i];
level.waypoints[child].children = array_remove( level.waypoints[child].children, nwp );
level.waypoints[child].childCount = level.waypoints[child].children.size;
}
for ( i = 0; i < level.waypointCount; i++ )
{
for(h = 0; h < level.waypoints[i].childCount; h++)
for ( h = level.waypoints[i].children.size - 1; h >= 0; h-- )
{
if ( level.waypoints[i].children[h] > nwp )
level.waypoints[i].children[h]--;
@ -493,10 +494,12 @@ DeleteWaypoint(nwp)
level.waypoints[entry] = level.waypoints[entry + 1];
entry++;
}
level.waypoints[entry] = undefined;
break;
}
}
level.waypointCount--;
self iprintln( "DelWp " + nwp );
@ -522,7 +525,6 @@ addWaypoint(pos)
level.waypoints[level.waypointCount].angles = self getPlayerAngles();
level.waypoints[level.waypointCount].children = [];
level.waypoints[level.waypointCount].childCount = 0;
self iprintln( level.waypoints[level.waypointCount].type + " Waypoint " + level.waypointCount + " Added at " + pos );
@ -559,9 +561,6 @@ UnLinkWaypoint(nwp)
level.waypoints[nwp].children = array_remove( level.waypoints[nwp].children, level.wpToLink );
level.waypoints[level.wpToLink].children = array_remove( level.waypoints[level.wpToLink].children, nwp );
level.waypoints[nwp].childCount = level.waypoints[nwp].children.size;
level.waypoints[level.wpToLink].childCount = level.waypoints[level.wpToLink].children.size;
self iprintln( "Waypoint " + nwp + " Broken to " + level.wpToLink );
level.wpToLink = -1;
}
@ -583,7 +582,8 @@ LinkWaypoint(nwp)
}
weGood = true;
for(i = 0; i < level.waypoints[level.wpToLink].childCount; i++)
for ( i = level.waypoints[level.wpToLink].children.size - 1; i >= 0; i-- )
{
if ( level.waypoints[level.wpToLink].children[i] == nwp )
{
@ -591,9 +591,10 @@ LinkWaypoint(nwp)
break;
}
}
if ( weGood )
{
for(i = 0; i < level.waypoints[nwp].childCount; i++)
for ( i = level.waypoints[nwp].children.size - 1; i >= 0; i-- )
{
if ( level.waypoints[nwp].children[i] == level.wpToLink )
{
@ -610,10 +611,8 @@ LinkWaypoint(nwp)
return;
}
level.waypoints[level.wpToLink].children[level.waypoints[level.wpToLink].childcount] = nwp;
level.waypoints[level.wpToLink].childcount++;
level.waypoints[nwp].children[level.waypoints[nwp].childcount] = level.wpToLink;
level.waypoints[nwp].childcount++;
level.waypoints[level.wpToLink].children[level.waypoints[level.wpToLink].children.size] = nwp;
level.waypoints[nwp].children[level.waypoints[nwp].children.size] = level.wpToLink;
self iprintln( "Waypoint " + nwp + " Linked to " + level.wpToLink );
level.wpToLink = -1;

View File

@ -1,4 +1,4 @@
# CoD4x Bot Warfare
# CoD4x Bot Warfare v2.1.0
Bot Warfare is a GSC mod for the CoD4x project.
It aims to add playable AI to the multiplayer games of CoD4.
@ -12,7 +12,7 @@ Make sure to disable this DVAR by adding 'set bots_main_firstIsHost 0' in your s
## Installation
0. Make sure that CoD4x server + client is installed, updated and working properly.
- Go to https://cod4x.me/ and download the Windows Server zip file. Move the contents of 'cod4x-windows-server' into your CoD4 game folder.
- Go to https://cod4x.ovh/ and download the Windows Server zip file. Move the contents of 'cod4x-windows-server' into your CoD4 game folder.
1. Locate your CoD4x server install folder.
2. Move the files/folders found in 'Add to root of CoD4x server' from the Bot Warfare release archive you downloaded to the root of your CoD4x server folder.
- The folder/file structure should follow as '.CoD4x server folder\main_shared\maps\mp\bots\_bot.gsc'.
@ -30,6 +30,17 @@ 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.1.0
- Bot chatter system, bots_main_chat
- Greatly reduce script variable usage
- Improved bots mantling and stuck
- Fix some runtime errors
- Bots sprint more
- Improved bots sight on enemies
- Bots do random actions while waiting at an objective
- Improved bots from getting stuck
- Better bot difficulty management, bots_skill_min and bots_skill_max
- v2.0.1
- Reduced bots crouching
- Increased bots sprinting

BIN
out/ss.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 MiB