Compare commits

...

24 Commits

Author SHA1 Message Date
ineed bots
230d89edf5 fix this logic error 2025-01-23 23:45:36 -06:00
ineed bots
fa43b34dd5 wait for notifies 2024-10-22 10:03:29 -06:00
ineed bots
2e3ec92b07 import camping change 2024-09-21 20:19:16 -06:00
ineed bots
271ef3387d missing tag 2024-05-16 19:14:06 -06:00
ineed bots
87b2a1ca8b add bots_manage_fill_watchplayers 2024-05-16 18:51:20 -06:00
ineed bots
1f9c458c07 fix errors 2024-05-10 00:08:28 -06:00
ineed bots
eac093fa3e fixed menu var leak 2024-05-09 20:55:45 -06:00
ineed bots
c5c9102c5c remove messages 2024-05-09 15:04:24 -06:00
ineed bots
25052b2891 turret 2024-05-09 15:02:44 -06:00
ineed bots
dc03e0fbb3 update 2024-04-30 13:11:41 -06:00
ineed bots
24c1908c85 some updates 2024-04-30 11:20:18 -06:00
ineed bots
02761a1013 chat event 2024-04-25 15:37:43 -06:00
ineed bots
093dc9cd8c playername 2024-04-25 14:49:26 -06:00
ineed bots
a3266c6de3 init time 2024-04-25 14:16:28 -06:00
ineed bots
0ea6672804 connection events 2024-04-25 14:07:35 -06:00
ineed bots
b3967606f4 small fixes 2024-04-04 16:54:29 -06:00
ineed bots
fcd130a132 update bones 2024-04-04 15:38:50 -06:00
ineed bots
f39a4d008c waittillframeend for event 2024-03-12 14:58:28 -06:00
ineed bots
cff61ab690 modwarfare support 2024-01-20 00:17:29 -06:00
ineed bots
f044af8aea update gsc version 2024-01-18 15:08:18 -06:00
ineed bots
1d464fca49 check for target knife 2024-01-18 14:53:07 -06:00
ineed bots
538269517a Merge branch 'master' of github.com:ineedbots/iw3_bot_warfare 2024-01-18 14:13:04 -06:00
ineed bots
9efda21175 move scripts to mp folder 2024-01-18 14:13:01 -06:00
INeedGames
f536f2b65d
Update main.yml 2024-01-15 15:26:03 -06:00
15 changed files with 433 additions and 154 deletions

View File

@ -13,7 +13,9 @@ jobs:
- name: Setup gsc-tool - name: Setup gsc-tool
uses: xensik/setup-gsc-tool@v1 uses: xensik/setup-gsc-tool@v1
with:
version: '1.4.0'
- name: Compile test script - name: Compile test script
run: | run: |
gsc-tool.exe parse iw5 pc . gsc-tool.exe -m parse -g iw5 -s pc .

View File

@ -84,19 +84,20 @@ You can easily setup a local LAN dedicated server for you to join and play on. H
| bots_main_GUIDs | A comma separated list of GUIDs of players who will be given host. | | | bots_main_GUIDs | A comma separated list of GUIDs of players who will be given host. | |
| bots_main_waitForHostTime | How many seconds to wait for the host player to connect before adding bots to the match. | 10 | | bots_main_waitForHostTime | How many seconds to wait for the host player to connect before adding bots to the match. | 10 |
| bots_main_menu | Enable the in-game menu for hosts. | 1 | | bots_main_menu | Enable the in-game menu for hosts. | 1 |
| bots_main_debug | Enable the in-game waypoint editor. | 0 | | bots_main_debug | Enable the in-game waypoint editor at start of the game, or enable bot event prints. <ul><li>`0` - disable</li><li>`1` - for just debug events</li><li>`2` - for every event</li><ul> | 0 |
| bots_main_kickBotsAtEnd | Kick the bots at the end of a match. | 0 | | bots_main_kickBotsAtEnd | Kick the bots at the end of a match. | 0 |
| bots_main_chat | The rate bots will chat at, set to 0 to disable. | 1.0 | | 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_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 | 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 | | 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><li>`2` - exactly `0` but auto adjusts `bots_manage_fill` to map.</li><li>`3` - exactly `1` but auto adjusts `bots_manage_fill` to map.</li><li>`4` - bots are used for balancing teams.</li><li>`5` - exactly `4` but auto adjusts `bots_manage_fill` to map.</li></ul> | 0 |
| bots_manage_fill_kick | If the amount of players/bots in the match exceeds `bots_manage_fill`, kick bots until no longer exceeds. | 0 | | bots_manage_fill_watchplayers | Bots will not be added until one player is in the game | 0 |
| bots_manage_fill_kick | If the amount of players/bots in the match exceeds `bots_manage_fill`, kick bots until no longer exceeds. | 0 |
| bots_manage_fill_spec | If when counting players for `bots_manage_fill` should include spectators. | 1 | | bots_manage_fill_spec | If when counting players for `bots_manage_fill` should include spectators. | 1 |
| bots_team | One of `autoassign`, `allies`, `axis`, `spectator`, or `custom`. What team the bots should be on. | autoassign | | bots_team | One of `autoassign`, `allies`, `axis`, `spectator`, or `custom`. What team the bots should be on. | autoassign |
| bots_team_amount | When `bots_team` is set to `custom`. The amount of bots to be placed on the axis team. The remainder will be placed on the allies team. | 0 | | bots_team_amount | When `bots_team` is set to `custom`. The amount of bots to be placed on the axis team. The remainder will be placed on the allies team. | 0 |
| bots_team_force | If the server should force bots' teams according to the `bots_team` value. When `bots_team` is `autoassign`, unbalanced teams will be balanced. This dvar is ignored when `bots_team` is `custom`. | 0 | | bots_team_force | If the server should force bots' teams according to the `bots_team` value. When `bots_team` is `autoassign`, unbalanced teams will be balanced. This dvar is ignored when `bots_team` is `custom`. | 0 |
| bots_team_mode | When `bots_team_force` is `true` and `bots_team` is `autoassign`, players/bots counting method. <ul><li>`0` - counts both players and bots.</li><li>`1` - only counts bots</li></ul> | 0 | | bots_team_mode | When `bots_team_force` is `1` and `bots_team` is `autoassign`, players/bots counting method. <ul><li>`0` - counts both players and bots.</li><li>`1` - only counts bots</li></ul> | 0 |
| bots_skill | Bots' difficulty.<ul><li>`0` - Random difficulty for each bot.</li><li>`1` - Easiest difficulty for all bots.</li><li>`2` to `6` - Between easy and hard difficulty for all bots.</li><li>`7` - The hardest difficulty for all bots.</li><li>`8` - custom (look at the `bots_skill_<team>_<difficulty>` dvars)</li></ul> | 0 | | bots_skill | Bots' difficulty.<ul><li>`0` - Random difficulty for each bot.</li><li>`1` - Easiest difficulty for all bots.</li><li>`2` to `6` - Between easy and hard difficulty for all bots.</li><li>`7` - The hardest difficulty for all bots.</li><li>`8` - custom (look at the `bots_skill_<team>_<difficulty>` dvars)</li><li>`9` - Every difficulty parameter is randomized</li></ul> | 0 |
| bots_skill_axis_hard | When `bots_skill` is set to `8`, the amount of hard difficulty bots to set on the axis team. | 0 | | bots_skill_axis_hard | When `bots_skill` is set to `8`, the amount of hard difficulty bots to set on the axis team. | 0 |
| bots_skill_axis_med | When `bots_skill` is set to `8`, the amount of medium difficulty bots to set on the axis team. The remaining bots on the team will be set to easy difficulty. | 0 | | bots_skill_axis_med | When `bots_skill` is set to `8`, the amount of medium difficulty bots to set on the axis team. The remaining bots on the team will be set to easy difficulty. | 0 |
| bots_skill_allies_hard | When `bots_skill` is set to `8`, the amount of hard difficulty bots to set on the allies team. | 0 | | bots_skill_allies_hard | When `bots_skill` is set to `8`, the amount of hard difficulty bots to set on the allies team. | 0 |
@ -120,6 +121,13 @@ You can easily setup a local LAN dedicated server for you to join and play on. H
## Changelog ## Changelog
- v2.3.0 (not released yet)
- Smoothed bot aim at range
- Fixed bots_manage_fill_spec players being counted with bots_manage_fill_mode 1 (bot only)
- Added bots_manage_fill_watchplayers dvar
- Bots hop off turrets if they get stuck on one
- Fixed script variable leak with opening and closing the in-game menu
- v2.2.0 - v2.2.0
- Fixed some chat related script runtime errors - Fixed some chat related script runtime errors
- Waypoints only load from csv now - Waypoints only load from csv now

View File

@ -8,7 +8,7 @@
*/ */
init() init()
{ {
level.bw_version = "2.1.0"; level.bw_version = "2.3.0";
if ( getdvar( "bots_main" ) == "" ) if ( getdvar( "bots_main" ) == "" )
{ {
@ -74,6 +74,11 @@ init()
setdvar( "bots_manage_fill_kick", false ); // kick bots if too many setdvar( "bots_manage_fill_kick", false ); // kick bots if too many
} }
if ( getdvar( "bots_manage_fill_watchplayers" ) == "" )
{
setdvar( "bots_manage_fill_watchplayers", false ); // add bots when player exists, kick if not
}
if ( getdvar( "bots_team" ) == "" ) if ( getdvar( "bots_team" ) == "" )
{ {
setdvar( "bots_team", "autoassign" ); // which team for bots to join setdvar( "bots_team", "autoassign" ); // which team for bots to join
@ -207,8 +212,11 @@ init()
if ( !isdefined( game[ "botWarfare" ] ) ) if ( !isdefined( game[ "botWarfare" ] ) )
{ {
game[ "botWarfare" ] = true; game[ "botWarfare" ] = true;
game[ "botWarfareInitTime" ] = gettime();
} }
level.bot_inittime = gettime();
level.defuseobject = undefined; level.defuseobject = undefined;
level.bots_smokelist = List(); level.bots_smokelist = List();
level.tbl_perkdata[ 0 ][ "reference_full" ] = true; level.tbl_perkdata[ 0 ][ "reference_full" ] = true;
@ -262,6 +270,9 @@ init()
level thread onPlayerConnect(); level thread onPlayerConnect();
level thread handleBots(); level thread handleBots();
level thread onPlayerChat();
array_thread( getentarray( "misc_turret", "classname" ), ::turret_monitoruse_watcher );
} }
/* /*
@ -440,6 +451,23 @@ fixPerksAndScriptKick()
self.pers[ "isBot" ] = true; self.pers[ "isBot" ] = true;
} }
/*
When a bot disconnects.
*/
onDisconnectPlayer()
{
name = self.name;
self waittill( "disconnect" );
waittillframeend;
for ( i = 0; i < level.bots.size; i++ )
{
bot = level.bots[ i ];
bot BotNotifyBotEvent( "connection", "disconnected", self, name );
}
}
/* /*
When a bot disconnects. When a bot disconnects.
*/ */
@ -457,6 +485,14 @@ connected()
{ {
self endon( "disconnect" ); self endon( "disconnect" );
for ( i = 0; i < level.bots.size; i++ )
{
bot = level.bots[ i ];
bot BotNotifyBotEvent( "connection", "connected", self, self.name );
}
self thread onDisconnectPlayer();
if ( !isdefined( self.pers[ "bot_host" ] ) ) if ( !isdefined( self.pers[ "bot_host" ] ) )
{ {
self thread doHostCheck(); self thread doHostCheck();
@ -486,10 +522,10 @@ connected()
level.bots[ level.bots.size ] = self; level.bots[ level.bots.size ] = self;
self thread onDisconnect(); self thread onDisconnect();
level notify( "bot_connected", self );
self thread watchBotDebugEvent(); self thread watchBotDebugEvent();
waittillframeend; // wait for waittills to process
level notify( "bot_connected", self );
} }
/* /*
@ -902,7 +938,7 @@ addBots_loop()
fillMode = getdvarint( "bots_manage_fill_mode" ); fillMode = getdvarint( "bots_manage_fill_mode" );
if ( fillMode == 2 || fillMode == 3 ) if ( fillMode == 2 || fillMode == 3 || fillMode == 5 )
{ {
setdvar( "bots_manage_fill", getGoodMapAmount() ); setdvar( "bots_manage_fill", getGoodMapAmount() );
} }
@ -912,6 +948,8 @@ addBots_loop()
players = 0; players = 0;
bots = 0; bots = 0;
spec = 0; spec = 0;
axisplayers = 0;
alliesplayers = 0;
playercount = level.players.size; playercount = level.players.size;
@ -930,37 +968,6 @@ addBots_loop()
else else
{ {
players++; 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" ) if ( player.pers[ "team" ] == "axis" )
{ {
@ -971,26 +978,19 @@ addBots_loop()
alliesplayers++; alliesplayers++;
} }
} }
}
result = fillAmount - abs( axisplayers - alliesplayers ) + bots; if ( getdvarint( "bots_manage_fill_spec" ) )
{
players += spec;
}
if ( players == 0 ) if ( !randomint( 999 ) )
{ {
if ( bots < fillAmount ) setdvar( "testclients_doreload", true );
{ wait 0.1;
result = fillAmount - 1; setdvar( "testclients_doreload", false );
} doExtraCheck();
else if ( bots > fillAmount )
{
result = fillAmount + 1;
}
else
{
result = fillAmount;
}
}
bots = result;
} }
amount = bots; amount = bots;
@ -1000,22 +1000,46 @@ addBots_loop()
amount += players; amount += players;
} }
if ( getdvarint( "bots_manage_fill_spec" ) ) // use bots as balance
if ( fillMode == 4 || fillMode == 5 )
{ {
amount += spec; diffPlayers = abs( alliesplayers - axisplayers );
amount = fillAmount - ( diffPlayers - bots );
if ( players + diffPlayers < fillAmount )
{
amount = players + bots;
}
}
if ( players <= 0 && getdvarint( "bots_manage_fill_watchplayers" ) )
{
amount = fillAmount + bots;
} }
if ( amount < fillAmount ) if ( amount < fillAmount )
{ {
setdvar( "bots_manage_add", 1 ); setdvar( "bots_manage_add", fillAmount - amount );
} }
else if ( amount > fillAmount && getdvarint( "bots_manage_fill_kick" ) ) else if ( amount > fillAmount && getdvarint( "bots_manage_fill_kick" ) )
{ {
tempBot = getBotToKick(); botsToKick = amount - fillAmount;
if ( isdefined( tempBot ) ) if ( botsToKick > 64 )
{ {
kick( tempBot getentitynumber() ); botsToKick = 64;
}
for ( i = 0; i < botsToKick; i++ )
{
tempBot = getBotToKick();
if ( isdefined( tempBot ) )
{
kick( tempBot getentitynumber(), "EXE_PLAYERKICKED" );
wait 0.25;
}
} }
} }
} }
@ -1247,3 +1271,65 @@ doFiringThread()
wait 1; wait 1;
self.bots_firing = false; self.bots_firing = false;
} }
/*
When a player chats
*/
onPlayerChat()
{
for ( ;; )
{
level waittill( "say", message, player, is_hidden );
for ( i = 0; i < level.bots.size; i++ )
{
bot = level.bots[ i ];
bot BotNotifyBotEvent( "chat", "chat", message, player, is_hidden );
}
}
}
/*
Monitors turret usage
*/
turret_monitoruse_watcher()
{
self endon( "death" );
for ( ;; )
{
self waittill ( "trigger", player );
self monitor_player_turret( player );
self.owner = undefined;
if ( isdefined( player ) )
{
player.turret = undefined;
}
}
}
/*
While player uses turret
*/
monitor_player_turret( player )
{
player endon( "death" );
player endon( "disconnect" );
player.turret = self;
self.owner = player;
while ( isdefined( player ) && player usebuttonpressed() )
{
wait 0.05;
}
while ( isdefined( player ) && !player usebuttonpressed() )
{
wait 0.05;
}
}

View File

@ -252,10 +252,43 @@ start_chat_watch()
case "sd": case "sd":
self thread bot_chat_sd_watch( a, b, c, d, e, f, g ); self thread bot_chat_sd_watch( a, b, c, d, e, f, g );
break; break;
case "connection":
self thread bot_chat_connection_player_watch( a, b, c, d, e, f, g );
break;
case "chat":
self thread bot_chat_chat_player_watch( a, b, c, d, e, f, g );
break;
} }
} }
} }
/*
When another player chats
*/
bot_chat_chat_player_watch( chatstr, message, player, is_hidden, e, f, g )
{
self endon( "disconnect" );
}
/*
When a player connected
*/
bot_chat_connection_player_watch( conn, player, playername, d, e, f, g )
{
self endon( "disconnect" );
switch ( conn )
{
case "connected":
break;
case "disconnected":
break;
}
}
/* /*
start_startgame_watch start_startgame_watch
*/ */

View File

@ -251,6 +251,13 @@ watchPickupGun()
continue; continue;
} }
// todo have bots use turrets instead of just kicking them off of it
if ( isdefined( self.turret ) )
{
self thread use( 0.5 );
continue;
}
weap = self getcurrentweapon(); weap = self getcurrentweapon();
if ( weap != "none" && self getammocount( weap ) ) if ( weap != "none" && self getammocount( weap ) )
@ -485,7 +492,7 @@ IsWeapSniper( weap )
return false; return false;
} }
if ( maps\mp\gametypes\_missions::getweaponclass( weap ) != "weapon_sniper" ) if ( getweaponclass( weap ) != "weapon_sniper" )
{ {
return false; return false;
} }
@ -556,6 +563,12 @@ onWeaponChange()
{ {
first = false; first = false;
newWeapon = self getcurrentweapon(); newWeapon = self getcurrentweapon();
// hack fix for botstop overridding weapon
if ( newWeapon != "none" )
{
self switchtoweapon( newWeapon );
}
} }
else else
{ {
@ -903,19 +916,22 @@ updateBones()
self endon( "disconnect" ); self endon( "disconnect" );
self endon( "death" ); self endon( "death" );
bones = strtok( self.pers[ "bots" ][ "skill" ][ "bones" ], "," );
waittime = self.pers[ "bots" ][ "skill" ][ "bone_update_interval" ];
for ( ;; ) for ( ;; )
{ {
self waittill_notify_or_timeout( "new_enemy", waittime ); oldbones = self.pers[ "bots" ][ "skill" ][ "bones" ];
bones = strtok( oldbones, "," );
if ( !isdefined( self.bot.target ) ) while ( oldbones == self.pers[ "bots" ][ "skill" ][ "bones" ] )
{ {
continue; self waittill_notify_or_timeout( "new_enemy", self.pers[ "bots" ][ "skill" ][ "bone_update_interval" ] );
}
self.bot.target.bone = random( bones ); if ( !isdefined( self.bot.target ) )
{
continue;
}
self.bot.target.bone = random( bones );
}
} }
} }
@ -1024,6 +1040,23 @@ targetObjUpdateNoTrace( obj )
obj.didlook = false; obj.didlook = false;
} }
/*
Returns true if myEye can see the bone of self
*/
checkTraceForBone( myEye, bone )
{
boneLoc = self gettagorigin( bone );
if ( !isdefined( boneLoc ) )
{
return false;
}
trace = bullettrace( myEye, boneLoc, false, undefined );
return ( sighttracepassed( myEye, boneLoc, false, undefined ) && ( trace[ "fraction" ] >= 1.0 || trace[ "surfacetype" ] == "glass" ) );
}
/* /*
The main target thread, will update the bot's main target. Will auto target enemy players and handle script targets. The main target thread, will update the bot's main target. Will auto target enemy players and handle script targets.
*/ */
@ -1135,21 +1168,9 @@ target_loop()
continue; continue;
} }
targetHead = player gettagorigin( "j_head" ); canTargetPlayer = ( ( player checkTraceForBone( myEye, "j_head" ) ||
targetAnkleLeft = player gettagorigin( "j_ankle_le" ); player checkTraceForBone( myEye, "j_ankle_le" ) ||
targetAnkleRight = player gettagorigin( "j_ankle_ri" ); player checkTraceForBone( myEye, "j_ankle_ri" ) )
traceHead = bullettrace( myEye, targetHead, false, undefined );
traceAnkleLeft = bullettrace( myEye, targetAnkleLeft, false, undefined );
traceAnkleRight = bullettrace( myEye, targetAnkleRight, false, undefined );
canTargetPlayer = ( ( sighttracepassed( myEye, targetHead, false, undefined ) ||
sighttracepassed( myEye, targetAnkleLeft, false, undefined ) ||
sighttracepassed( myEye, targetAnkleRight, false, undefined ) )
&& ( ( traceHead[ "fraction" ] >= 1.0 || traceHead[ "surfacetype" ] == "glass" ) ||
( traceAnkleLeft[ "fraction" ] >= 1.0 || traceAnkleLeft[ "surfacetype" ] == "glass" ) ||
( traceAnkleRight[ "fraction" ] >= 1.0 || traceAnkleRight[ "surfacetype" ] == "glass" ) )
&& ( SmokeTrace( myEye, player.origin, level.smokeradius ) || && ( SmokeTrace( myEye, player.origin, level.smokeradius ) ||
daDist < level.bots_maxknifedistance * 4 ) daDist < level.bots_maxknifedistance * 4 )
@ -1508,7 +1529,7 @@ aim_loop()
} }
else if ( curweap != "none" && weaponclass( curweap ) == "grenade" ) else if ( curweap != "none" && weaponclass( curweap ) == "grenade" )
{ {
if ( maps\mp\gametypes\_missions::getweaponclass( curweap ) == "weapon_projectile" ) if ( getweaponclass( curweap ) == "weapon_projectile" )
{ {
nadeAimOffset = dist / 16000; nadeAimOffset = dist / 16000;
} }
@ -1582,11 +1603,11 @@ aim_loop()
conedot = getConeDot( aimpos, eyePos, angles ); conedot = getConeDot( aimpos, eyePos, angles );
if ( isdefined( self.bot.knifing_target ) ) if ( isdefined( self.bot.knifing_target ) && self.bot.knifing_target == target )
{ {
self thread bot_lookat( target gettagorigin( "j_spine4" ), 0.05 ); self thread bot_lookat( target gettagorigin( "j_spine4" ), 0.05 );
} }
else if ( !nadeAimOffset && conedot > 0.999 && lengthsquared( aimoffset ) < 0.05 ) else if ( !nadeAimOffset && conedot > 0.999995 && lengthsquared( aimoffset ) < 0.05 )
{ {
self thread bot_lookat( aimpos, 0.05 ); self thread bot_lookat( aimpos, 0.05 );
} }
@ -1604,7 +1625,7 @@ aim_loop()
conedot = getConeDot( aimpos, eyePos, angles ); conedot = getConeDot( aimpos, eyePos, angles );
if ( !nadeAimOffset && conedot > 0.999 && lengthsquared( aimoffset ) < 0.05 ) if ( !nadeAimOffset && conedot > 0.999995 && lengthsquared( aimoffset ) < 0.05 )
{ {
self thread bot_lookat( aimpos, 0.05 ); self thread bot_lookat( aimpos, 0.05 );
} }
@ -1680,7 +1701,7 @@ aim_loop()
} }
else if ( curweap != "none" && weaponclass( curweap ) == "grenade" ) else if ( curweap != "none" && weaponclass( curweap ) == "grenade" )
{ {
if ( maps\mp\gametypes\_missions::getweaponclass( curweap ) == "weapon_projectile" ) if ( getweaponclass( curweap ) == "weapon_projectile" )
{ {
nadeAimOffset = dist / 16000; nadeAimOffset = dist / 16000;
} }
@ -1773,6 +1794,7 @@ aim()
for ( ;; ) for ( ;; )
{ {
wait 0.05; wait 0.05;
waittillframeend;
if ( level.inprematchperiod || level.gameended || self.bot.isfrozen || self maps\mp\_flashgrenades::isflashbanged() ) if ( level.inprematchperiod || level.gameended || self.bot.isfrozen || self maps\mp\_flashgrenades::isflashbanged() )
{ {
@ -1954,9 +1976,9 @@ walk_loop()
dist = 16; dist = 16;
if ( level.waypointcount ) if ( level.waypoints.size )
{ {
goal = level.waypoints[ randomint( level.waypointcount ) ].origin; goal = level.waypoints[ randomint( level.waypoints.size ) ].origin;
} }
else else
{ {
@ -2843,7 +2865,7 @@ bot_lookat( pos, time, vel, doAimPredict )
for ( i = 0; i < steps; i++ ) for ( i = 0; i < steps; i++ )
{ {
myAngle = ( angleclamp180( myAngle[ 0 ] + X ), angleclamp180( myAngle[ 1 ] + Y ), 0 ); myAngle = ( angleclamp180( myAngle[ 0 ] + X ), angleclamp180( myAngle[ 1 ] + Y ), 0 );
self setplayerangles( myAngle ); self BotBuiltinBotAngles( myAngle );
wait 0.05; wait 0.05;
} }
} }

View File

@ -347,6 +347,21 @@ classWatch()
if ( !maps\mp\gametypes\_globallogic::isvalidclass( self.class ) || !isdefined( self.bot_change_class ) ) if ( !maps\mp\gametypes\_globallogic::isvalidclass( self.class ) || !isdefined( self.bot_change_class ) )
{ {
// mod warfare shtuff
if ( isdefined( level.serverdvars ) )
{
a = [];
a[ a.size ] = "assault";
a[ a.size ] = "specops";
a[ a.size ] = "heavygunner";
a[ a.size ] = "demolitions";
a[ a.size ] = "sniper";
self notify( "menuresponse", game[ "menu_changeclass_" + self.pers[ "team" ] ], random( a ) );
wait 0.5;
}
self notify( "menuresponse", game[ "menu_changeclass" ], self chooseRandomClass() ); self notify( "menuresponse", game[ "menu_changeclass" ], self chooseRandomClass() );
} }
@ -1557,7 +1572,7 @@ bot_think_camp_loop()
self SetScriptGoal( campSpot.origin, 16 ); self SetScriptGoal( campSpot.origin, 16 );
time = randomintrange( 10, 20 ); time = randomintrange( 30, 90 );
self BotNotifyBotEvent( "camp", "go", campSpot, time ); self BotNotifyBotEvent( "camp", "go", campSpot, time );
@ -1616,7 +1631,17 @@ killCampAfterTime( time )
self endon( "disconnect" ); self endon( "disconnect" );
self endon( "kill_camp_bot" ); self endon( "kill_camp_bot" );
wait time + 0.05; timeleft = maps\mp\gametypes\_globallogic::gettimeremaining() / 1000;
while ( time > 0 && timeleft >= 60 )
{
wait 1;
timeleft = maps\mp\gametypes\_globallogic::gettimeremaining() / 1000;
time--;
}
wait 0.05;
self ClearScriptGoal(); self ClearScriptGoal();
self ClearScriptAimPos(); self ClearScriptAimPos();
@ -2841,6 +2866,12 @@ bot_killstreak_think_loop()
{ {
curWeap = self getcurrentweapon(); curWeap = self getcurrentweapon();
if ( curWeap == "radar_mp" || curWeap == "helicopter_mp" || curWeap == "airstrike_mp" )
{
self thread changeToWeapon( self.lastdroppableweapon );
return;
}
if ( curWeap == "none" || !isWeaponDroppable( curWeap ) ) if ( curWeap == "none" || !isWeaponDroppable( curWeap ) )
{ {
curWeap = self.lastdroppableweapon; curWeap = self.lastdroppableweapon;

View File

@ -133,6 +133,17 @@ BotBuiltinBotMeleeParams( yaw, dist )
} }
} }
/*
Sets angles
*/
BotBuiltinBotAngles( angles )
{
if ( isdefined( level.bot_builtins ) && isdefined( level.bot_builtins[ "botangles" ] ) )
{
self [[ level.bot_builtins[ "botangles" ] ]]( angles );
}
}
/* /*
Test if is a bot Test if is a bot
*/ */
@ -426,12 +437,22 @@ BotStopMoving( what )
} }
} }
/*
Waits till frame end so that if two notifies happen in the same frame, the other will not be missed.
*/
BotNotifyBotEvent_( msg, a, b, c, d, e, f, g )
{
self endon( "disconnect" );
waittillframeend; // wait for the waittills to setup again
self notify( "bot_event", msg, a, b, c, d, e, f, g );
}
/* /*
Notify the bot chat message Notify the bot chat message
*/ */
BotNotifyBotEvent( msg, a, b, c, d, e, f, g ) BotNotifyBotEvent( msg, a, b, c, d, e, f, g )
{ {
self notify( "bot_event", msg, a, b, c, d, e, f, g ); self thread BotNotifyBotEvent_( msg, a, b, c, d, e, f, g );
} }
/* /*
@ -1029,6 +1050,22 @@ isItemUnlocked( what, lvl )
} }
} }
/*
ModWarfare removes this func from _missions
*/
getweaponclass( weapon )
{
tokens = strtok( weapon, "_" );
weaponClass = tablelookup( "mp/statstable.csv", 4, tokens[ 0 ], 2 );
if ( ismg( weapon ) )
{
weaponClass = "weapon_mg";
}
return weaponClass;
}
/* /*
If the weapon is allowed to be dropped If the weapon is allowed to be dropped
*/ */
@ -1724,7 +1761,6 @@ load_waypoints()
{ {
mapname = getdvar( "mapname" ); mapname = getdvar( "mapname" );
level.waypointcount = 0;
level.waypointusage = []; level.waypointusage = [];
level.waypointusage[ "allies" ] = []; level.waypointusage[ "allies" ] = [];
level.waypointusage[ "axis" ] = []; level.waypointusage[ "axis" ] = [];
@ -1771,9 +1807,7 @@ load_waypoints()
BotBuiltinPrintConsole( "No waypoints loaded!" ); BotBuiltinPrintConsole( "No waypoints loaded!" );
} }
level.waypointcount = level.waypoints.size; for ( i = level.waypoints.size - 1; i >= 0; i-- )
for ( i = 0; i < level.waypointcount; i++ )
{ {
if ( !isdefined( level.waypoints[ i ].children ) || !isdefined( level.waypoints[ i ].children.size ) ) if ( !isdefined( level.waypoints[ i ].children ) || !isdefined( level.waypoints[ i ].children.size ) )
{ {
@ -1872,7 +1906,7 @@ getWaypointsOfType( type )
{ {
answer = []; answer = [];
for ( i = 0; i < level.waypointcount; i++ ) for ( i = level.waypoints.size - 1; i >= 0; i-- )
{ {
wp = level.waypoints[ i ]; wp = level.waypoints[ i ];
@ -2679,16 +2713,18 @@ RemoveWaypointUsage( wp, team )
return; return;
} }
if ( !isdefined( level.waypointusage[ team ][ wp + "" ] ) ) wpstr = wp + "";
if ( !isdefined( level.waypointusage[ team ][ wpstr ] ) )
{ {
return; return;
} }
level.waypointusage[ team ][ wp + "" ]--; level.waypointusage[ team ][ wpstr ]--;
if ( level.waypointusage[ team ][ wp + "" ] <= 0 ) if ( level.waypointusage[ team ][ wpstr ] <= 0 )
{ {
level.waypointusage[ team ][ wp + "" ] = undefined; level.waypointusage[ team ][ wpstr ] = undefined;
} }
} }
@ -2700,7 +2736,7 @@ GetNearestWaypointWithSight( pos )
candidate = undefined; candidate = undefined;
dist = 2147483647; dist = 2147483647;
for ( i = 0; i < level.waypointcount; i++ ) for ( i = level.waypoints.size - 1; i >= 0; i-- )
{ {
if ( !bullettracepassed( pos + ( 0, 0, 15 ), level.waypoints[ i ].origin + ( 0, 0, 15 ), false, undefined ) ) if ( !bullettracepassed( pos + ( 0, 0, 15 ), level.waypoints[ i ].origin + ( 0, 0, 15 ), false, undefined ) )
{ {
@ -2729,7 +2765,7 @@ getNearestWaypoint( pos )
candidate = undefined; candidate = undefined;
dist = 2147483647; dist = 2147483647;
for ( i = 0; i < level.waypointcount; i++ ) for ( i = level.waypoints.size - 1; i >= 0; i-- )
{ {
curdis = distancesquared( level.waypoints[ i ].origin, pos ); curdis = distancesquared( level.waypoints[ i ].origin, pos );
@ -2814,7 +2850,8 @@ AStarSearch( start, goal, team, greedy_path )
// pop bestnode from queue // pop bestnode from queue
bestNode = open.data[ 0 ]; bestNode = open.data[ 0 ];
open HeapRemove(); open HeapRemove();
openset[ bestNode.index + "" ] = undefined; bestNodeStr = bestNode.index + "";
openset[ bestNodeStr ] = undefined;
wp = level.waypoints[ bestNode.index ]; wp = level.waypoints[ bestNode.index ];
// check if we made it to the goal // check if we made it to the goal
@ -2824,14 +2861,16 @@ AStarSearch( start, goal, team, greedy_path )
while ( isdefined( bestNode ) ) while ( isdefined( bestNode ) )
{ {
bestNodeStr = bestNode.index + "";
if ( isdefined( team ) && isdefined( level.waypointusage ) ) if ( isdefined( team ) && isdefined( level.waypointusage ) )
{ {
if ( !isdefined( level.waypointusage[ team ][ bestNode.index + "" ] ) ) if ( !isdefined( level.waypointusage[ team ][ bestNodeStr ] ) )
{ {
level.waypointusage[ team ][ bestNode.index + "" ] = 0; level.waypointusage[ team ][ bestNodeStr ] = 0;
} }
level.waypointusage[ team ][ bestNode.index + "" ]++; level.waypointusage[ team ][ bestNodeStr ]++;
} }
// construct path // construct path
@ -2847,6 +2886,7 @@ AStarSearch( start, goal, team, greedy_path )
for ( i = wp.children.size - 1; i >= 0; i-- ) for ( i = wp.children.size - 1; i >= 0; i-- )
{ {
child = wp.children[ i ]; child = wp.children[ i ];
childStr = child + "";
childWp = level.waypoints[ child ]; childWp = level.waypoints[ child ];
penalty = 1; penalty = 1;
@ -2855,9 +2895,9 @@ AStarSearch( start, goal, team, greedy_path )
{ {
temppen = 1; temppen = 1;
if ( isdefined( level.waypointusage[ team ][ child + "" ] ) ) if ( isdefined( level.waypointusage[ team ][ childStr ] ) )
{ {
temppen = level.waypointusage[ team ][ child + "" ]; // consider how many bots are taking this path temppen = level.waypointusage[ team ][ childStr ]; // consider how many bots are taking this path
} }
if ( temppen > 1 ) if ( temppen > 1 )
@ -2876,16 +2916,16 @@ AStarSearch( start, goal, team, greedy_path )
newg = bestNode.g + distancesquared( wp.origin, childWp.origin ) * penalty; // bots on same team's path are more expensive newg = bestNode.g + distancesquared( wp.origin, childWp.origin ) * 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 // check if this child is in open or close with a g value less than newg
inopen = isdefined( openset[ child + "" ] ); inopen = isdefined( openset[ childStr ] );
if ( inopen && openset[ child + "" ].g <= newg ) if ( inopen && openset[ childStr ].g <= newg )
{ {
continue; continue;
} }
inclosed = isdefined( closed[ child + "" ] ); inclosed = isdefined( closed[ childStr ] );
if ( inclosed && closed[ child + "" ].g <= newg ) if ( inclosed && closed[ childStr ].g <= newg )
{ {
continue; continue;
} }
@ -2894,11 +2934,11 @@ AStarSearch( start, goal, team, greedy_path )
if ( inopen ) if ( inopen )
{ {
node = openset[ child + "" ]; node = openset[ childStr ];
} }
else if ( inclosed ) else if ( inclosed )
{ {
node = closed[ child + "" ]; node = closed[ childStr ];
} }
else else
{ {
@ -2914,19 +2954,19 @@ AStarSearch( start, goal, team, greedy_path )
// check if in closed, remove it // check if in closed, remove it
if ( inclosed ) if ( inclosed )
{ {
closed[ child + "" ] = undefined; closed[ childStr ] = undefined;
} }
// check if not in open, add it // check if not in open, add it
if ( !inopen ) if ( !inopen )
{ {
open HeapInsert( node ); open HeapInsert( node );
openset[ child + "" ] = node; openset[ childStr ] = node;
} }
} }
// done with children, push onto closed // done with children, push onto closed
closed[ bestNode.index + "" ] = bestNode; closed[ bestNodeStr ] = bestNode;
} }
return []; return [];

View File

@ -60,7 +60,6 @@ init_menu()
self.menuinit = true; self.menuinit = true;
self.menuopen = false; self.menuopen = false;
self.menu_player = undefined;
self.submenu = "Main"; self.submenu = "Main";
self.curs[ "Main" ][ "X" ] = 0; self.curs[ "Main" ][ "X" ] = 0;
self addOptions(); self addOptions();
@ -75,6 +74,47 @@ init_menu()
self thread doGreetings(); self thread doGreetings();
} }
destroyFixed()
{
if ( !isdefined( self ) )
{
return;
}
self destroy();
}
removeChildFixed( element )
{
temp = [];
for ( i = 0; i < self.children.size ; i++ )
{
if ( isdefined( self.children[ i ] ) && self.children[ i ] != element )
{
self.children[ i ].index = temp.size;
temp[ temp.size ] = self.children[ i ];
}
}
self.children = temp;
}
destroyElemFixed()
{
if ( !isdefined( self ) )
{
return;
}
if ( isdefined( self.parent ) )
{
self.parent removeChildFixed( self );
}
self destroyelem();
}
kill_menu() kill_menu()
{ {
self notify( "bots_kill_menu" ); self notify( "bots_kill_menu" );
@ -93,7 +133,7 @@ watchDisconnect()
{ {
if ( isdefined( self.menutexty[ i ] ) ) if ( isdefined( self.menutexty[ i ] ) )
{ {
self.menutexty[ i ] destroy(); self.menutexty[ i ] destroyElemFixed();
} }
} }
} }
@ -104,7 +144,7 @@ watchDisconnect()
{ {
if ( isdefined( self.menutext[ i ] ) ) if ( isdefined( self.menutext[ i ] ) )
{ {
self.menutext[ i ] destroy(); self.menutext[ i ] destroyElemFixed();
} }
} }
} }
@ -113,18 +153,18 @@ watchDisconnect()
{ {
if ( isdefined( self.menu[ "X" ][ "Shader" ] ) ) if ( isdefined( self.menu[ "X" ][ "Shader" ] ) )
{ {
self.menu[ "X" ][ "Shader" ] destroy(); self.menu[ "X" ][ "Shader" ] destroyElemFixed();
} }
if ( isdefined( self.menu[ "X" ][ "Scroller" ] ) ) if ( isdefined( self.menu[ "X" ][ "Scroller" ] ) )
{ {
self.menu[ "X" ][ "Scroller" ] destroy(); self.menu[ "X" ][ "Scroller" ] destroyElemFixed();
} }
} }
if ( isdefined( self.menuversionhud ) ) if ( isdefined( self.menuversionhud ) )
{ {
self.menuversionhud destroy(); self.menuversionhud destroyFixed();
} }
} }
} }
@ -332,7 +372,7 @@ OpenSub( menu, menu2 )
{ {
if ( isdefined( self.menutext[ i ] ) ) if ( isdefined( self.menutext[ i ] ) )
{ {
self.menutext[ i ] destroy(); self.menutext[ i ] destroyElemFixed();
} }
} }
} }
@ -341,18 +381,18 @@ OpenSub( menu, menu2 )
{ {
if ( isdefined( self.menu[ "X" ][ "Shader" ] ) ) if ( isdefined( self.menu[ "X" ][ "Shader" ] ) )
{ {
self.menu[ "X" ][ "Shader" ] destroy(); self.menu[ "X" ][ "Shader" ] destroyElemFixed();
} }
if ( isdefined( self.menu[ "X" ][ "Scroller" ] ) ) if ( isdefined( self.menu[ "X" ][ "Scroller" ] ) )
{ {
self.menu[ "X" ][ "Scroller" ] destroy(); self.menu[ "X" ][ "Scroller" ] destroyElemFixed();
} }
} }
if ( isdefined( self.menuversionhud ) ) if ( isdefined( self.menuversionhud ) )
{ {
self.menuversionhud destroy(); self.menuversionhud destroyFixed();
} }
for ( i = 0 ; i < self.option[ "Name" ][ self.submenu ].size ; i++ ) for ( i = 0 ; i < self.option[ "Name" ][ self.submenu ].size ; i++ )
@ -402,7 +442,7 @@ OpenSub( menu, menu2 )
{ {
if ( isdefined( self.menutexty[ i ] ) ) if ( isdefined( self.menutexty[ i ] ) )
{ {
self.menutexty[ i ] destroy(); self.menutexty[ i ] destroyElemFixed();
} }
} }
} }
@ -426,11 +466,11 @@ CursMove( direction )
if ( self.submenu == "Main" ) if ( self.submenu == "Main" )
{ {
self.menu[ "X" ][ "Scroller" ].x = self.menutext[ self.curs[ "Main" ][ "X" ] ].x;
self.menu[ "X" ][ "Scroller" ].y = self.menutext[ self.curs[ "Main" ][ "X" ] ].y;
if ( isdefined( self.menutext ) ) if ( isdefined( self.menutext ) )
{ {
self.menu[ "X" ][ "Scroller" ].x = self.menutext[ self.curs[ "Main" ][ "X" ] ].x;
self.menu[ "X" ][ "Scroller" ].y = self.menutext[ self.curs[ "Main" ][ "X" ] ].y;
for ( i = 0; i < self.menutext.size; i++ ) for ( i = 0; i < self.menutext.size; i++ )
{ {
if ( isdefined( self.menutext[ i ] ) ) if ( isdefined( self.menutext[ i ] ) )
@ -578,7 +618,7 @@ ExitSub()
{ {
if ( isdefined( self.menutexty[ i ] ) ) if ( isdefined( self.menutexty[ i ] ) )
{ {
self.menutexty[ i ] destroy(); self.menutexty[ i ] destroyElemFixed();
} }
} }
} }
@ -603,7 +643,7 @@ ExitMenu()
{ {
if ( isdefined( self.menutext[ i ] ) ) if ( isdefined( self.menutext[ i ] ) )
{ {
self.menutext[ i ] destroy(); self.menutext[ i ] destroyElemFixed();
} }
} }
} }
@ -612,18 +652,18 @@ ExitMenu()
{ {
if ( isdefined( self.menu[ "X" ][ "Shader" ] ) ) if ( isdefined( self.menu[ "X" ][ "Shader" ] ) )
{ {
self.menu[ "X" ][ "Shader" ] destroy(); self.menu[ "X" ][ "Shader" ] destroyElemFixed();
} }
if ( isdefined( self.menu[ "X" ][ "Scroller" ] ) ) if ( isdefined( self.menu[ "X" ][ "Scroller" ] ) )
{ {
self.menu[ "X" ][ "Scroller" ] destroy(); self.menu[ "X" ][ "Scroller" ] destroyElemFixed();
} }
} }
if ( isdefined( self.menuversionhud ) ) if ( isdefined( self.menuversionhud ) )
{ {
self.menuversionhud destroy(); self.menuversionhud destroyFixed();
} }
self.menuopen = false; self.menuopen = false;
@ -728,6 +768,10 @@ addOptions()
_temp = "bots used as team balance"; _temp = "bots used as team balance";
break; break;
case 5:
_temp = "bots used as team balance, adjust to map";
break;
default: default:
_temp = "out of range"; _temp = "out of range";
break; break;
@ -1294,6 +1338,11 @@ man_bots( a, b )
self iprintln( "bot_fill will now use bots as team balance." ); self iprintln( "bot_fill will now use bots as team balance." );
break; break;
case 4:
setdvar( "bots_manage_fill_mode", 5 );
self iprintln( "bot_fill will now use bots as team balance, adjusting to map." );
break;
default: default:
setdvar( "bots_manage_fill_mode", 0 ); setdvar( "bots_manage_fill_mode", 0 );
self iprintln( "bot_fill will now count everyone." ); self iprintln( "bot_fill will now count everyone." );

View File

@ -461,6 +461,7 @@ LoadWaypoints()
self deleteAllWaypoints(); self deleteAllWaypoints();
self iprintlnbold( "Loading WPS..." ); self iprintlnbold( "Loading WPS..." );
load_waypoints(); load_waypoints();
level.waypointcount = level.waypoints.size;
wait 1; wait 1;

View File

@ -17,11 +17,11 @@ CodeCallback_StartGameType()
level.gametypestarted = true; // so we know that the gametype has been started up level.gametypestarted = true; // so we know that the gametype has been started up
level thread scripts\bots_adapter_cod4x::init(); level thread scripts\mp\bots_adapter_cod4x::init();
level thread scripts\bots_chat::init(); level thread scripts\mp\bots_chat::init();
level thread scripts\bots_menu::init(); level thread scripts\mp\bots_menu::init();
level thread scripts\bots_wp_editor::init(); level thread scripts\mp\bots_wp_editor::init();
level thread scripts\bots::init(); level thread scripts\mp\bots::init();
} }
} }

View File

@ -7,6 +7,7 @@ init()
level.bot_builtins[ "botmovement" ] = ::do_botmovement; level.bot_builtins[ "botmovement" ] = ::do_botmovement;
level.bot_builtins[ "botmoveto" ] = ::do_botmoveto; level.bot_builtins[ "botmoveto" ] = ::do_botmoveto;
level.bot_builtins[ "botmeleeparams" ] = ::do_botmeleeparams; level.bot_builtins[ "botmeleeparams" ] = ::do_botmeleeparams;
level.bot_builtins[ "botangles" ] = ::do_botangles;
level.bot_builtins[ "isbot" ] = ::do_isbot; level.bot_builtins[ "isbot" ] = ::do_isbot;
level.bot_builtins[ "fs_fopen" ] = ::do_fs_fopen; level.bot_builtins[ "fs_fopen" ] = ::do_fs_fopen;
level.bot_builtins[ "fs_fclose" ] = ::do_fs_fclose; level.bot_builtins[ "fs_fclose" ] = ::do_fs_fclose;
@ -49,6 +50,12 @@ do_botmeleeparams( yaw, dist )
// cod4x removed lunging due to movement exploits // cod4x removed lunging due to movement exploits
} }
do_botangles( angles )
{
self setplayerangles( angles );
// self botangles( angles[ 0 ], angles[ 1 ], angles[ 2 ] );
}
do_isbot() do_isbot()
{ {
return self.isbot; return self.isbot;