1 #using scripts\codescripts\struct;
2 #using scripts\shared\array_shared;
3 #using scripts\shared\callbacks_shared;
4 #using scripts\shared\laststand_shared;
5 #using scripts\shared\math_shared;
6 #using scripts\shared\system_shared;
7 #using scripts\shared\util_shared;
9 #insert scripts\shared\shared.gsh;
10 #insert scripts\shared\bots\_bot.gsh;
12 #using scripts\shared\bots\_bot_combat;
13 #using scripts\shared\bots\bot_buttons;
14 #using scripts\shared\bots\bot_traversals;
18 #define BOT_MAX_STUCK_CYCLES 3
19 #define BOT_STUCK_DISTANCE 128
20 #define BOT_POSITION_HISTORY_SIZE 5
22 #define BOT_STUCK_RESOLUTION_S 1.5
24 #define BOT_DIVE_RADIUS 128
25 #define BOT_SWIM_HEIGHT_MIN 25
26 #define BOT_SWIM_HEIGHT_MAX 45
68 SetDvar(
"bot_maxMantleHeight", 200 );
99 for ( i = 0; i < count; i++ )
107 botEnt = AddTestClient();
109 if ( !isdefined( botEnt ) )
114 botEnt BotSetRandomCharacterCustomization();
116 if (
IS_TRUE( level.disableClassSelection ) )
118 botEnt.pers[
"class"] = level.defaultClass;
119 botEnt.curClass = level.defaultClass;
122 if ( level.teamBased && team !==
"autoassign" )
124 botEnt.pers[
"team" ] = team;
135 players = GetPlayers();
137 foreach( player
in players )
139 if ( !player IsTestClient() )
144 if ( isdefined( team ) && player.team != team )
151 if ( isdefined( count ) )
164 if ( !bot IsTestClient() )
169 bot [[level.onBotRemove]]();
181 foreach( player
in players )
185 bots[bots.size] = player;
198 if ( !
self IsTestClient() )
203 self endon (
"disconnect" );
206 self.bot = SpawnStruct();
207 self.bot.threat = SpawnStruct();
208 self.bot.damage = SpawnStruct();
210 self.pers[
"isBot"] =
true;
212 if ( level.teambased )
214 self notify(
"menuresponse", game[
"menu_team"],
self.team );
218 self notify(
"joined_team" );
221 self thread [[level.onBotConnect]]();
233 self.bot.prevWeapon = undefined;
235 self BotLookForward();
237 self thread [[level.onBotSpawned]]();
252 self thread [[level.onBotKilled]]();
254 self BotReleaseManualControl();
263 self endon(
"death" );
264 level endon(
"game_ended" );
270 wait level.botSettings.thinkInterval;
276 self BotReleaseButtons();
278 if ( level.inprematchperiod ||
291 self thread [[level.botUpdate]]();
293 self thread [[level.botPreCombat]]();
295 self thread [[level.botCombat]]();
297 self thread [[level.botPostCombat]]();
302 self thread [[level.botIdle]]();
314 if ( !
self IsPlayerSwimming() )
316 self.bot.resurfaceTime = undefined;
320 if (
self IsPlayerUnderwater() )
322 if ( !isdefined(
self.bot.resurfaceTime ) )
324 self.bot.resurfaceTime = GetTime() + level.botSettings.swimTime;
329 self.bot.resurfaceTime = undefined;
332 if (
self BotUnderManualControl() )
337 goalPosition =
self BotGetGoalPosition();
341 GetWaterHeight( goalPosition ) > 0 )
347 if ( isdefined(
self.bot.resurfaceTime ) &&
self.bot.resurfaceTime <= GetTime() )
355 bottomTrace = GroundTrace(
self.origin,
self.origin + ( 0, 0, -1000 ),
false,
self,
true );
356 swimHeight =
self.origin[2] - bottomTrace[
"position" ][2];
371 if ( isdefined( vertDist ) )
373 intervalDist = level.botSettings.swimVerticalSpeed * level.botSettings.thinkInterval;
375 if ( intervalDist > vertDist )
384 self endon(
"death" );
385 level endon(
"game_ended" );
398 level.botSettings = [[level.getBotSettings]]();
401 SetDvar(
"bot_AllowMelee",
VAL( level.botSettings.allowMelee, 0 ) );
402 SetDvar(
"bot_AllowGrenades",
VAL( level.botSettings.allowGrenades, 0 ) );
403 SetDvar(
"bot_AllowKillstreaks",
VAL( level.botSettings.allowKillstreaks, 0 ) );
404 SetDvar(
"bot_AllowHeroGadgets",
VAL( level.botSettings.allowHeroGadgets, 0 ) );
406 SetDvar(
"bot_Fov",
VAL( level.botSettings.fov, 0 ) );
407 SetDvar(
"bot_FovAds",
VAL( level.botSettings.fovAds, 0 ) );
408 SetDvar(
"bot_PitchSensitivity", level.botSettings.pitchSensitivity );
409 SetDvar(
"bot_YawSensitivity", level.botSettings.yawSensitivity );
411 SetDvar(
"bot_PitchSpeed",
VAL( level.botSettings.pitchSpeed, 0 ) );
412 SetDvar(
"bot_PitchSpeedAds",
VAL( level.botSettings.pitchSpeedAds, 0 ) );
413 SetDvar(
"bot_YawSpeed",
VAL( level.botSettings.yawSpeed, 0 ) );
414 SetDvar(
"bot_YawSpeedAds",
VAL( level.botSettings.yawSpeedAds, 0 ) );
416 SetDvar(
"pitchAccelerationTime",
VAL( level.botSettings.pitchAccelerationTime, 0 ) );
417 SetDvar(
"yawAccelerationTime",
VAL( level.botSettings.yawAccelerationTime, 0 ) );
419 SetDvar(
"pitchDecelerationThreshold",
VAL( level.botSettings.pitchDecelerationThreshold, 0 ) );
420 SetDvar(
"yawDecelerationThreshold",
VAL( level.botSettings.yawDecelerationThreshold, 0 ) );
423 meleeRange = GetDvarInt(
"player_meleeRangeDefault" ) *
VAL( level.botSettings.meleeRangeMultiplier, 0 );
424 level.botSettings.meleeRange = Int( meleeRange );
425 level.botSettings.meleeRangeSq = meleeRange * meleeRange;
427 level.botSettings.threatRadiusMinSq = level.botsettings.threatRadiusMin * level.botsettings.threatRadiusMin;
428 level.botSettings.threatRadiusMaxSq = level.botSettings.threatRadiusMax * level.botSettings.threatRadiusMax;
430 lethalDistanceMin =
VAL( level.botSettings.lethalDistanceMin, 0 );
431 level.botSettings.lethalDistanceMinSq = lethalDistanceMin * lethalDistanceMin;
433 lethalDistanceMax =
VAL( level.botSettings.lethalDistanceMax, 1024 );
434 level.botSettings.lethalDistanceMaxSq = lethalDistanceMax * lethalDistanceMax;
436 tacticalDistanceMin =
VAL( level.botSettings.tacticalDistanceMin, 0 );
437 level.botSettings.tacticalDistanceMinSq = tacticalDistanceMin * tacticalDistanceMin;
439 tacticalDistanceMax =
VAL( level.botSettings.tacticalDistanceMax, 1024 );
440 level.botSettings.tacticalDistanceMaxSq = tacticalDistanceMax * tacticalDistanceMax;
442 level.botSettings.swimVerticalSpeed = GetDvarFloat(
"player_swimVerticalSpeedMax" );
443 level.botSettings.swimTime = GetDvarFloat(
"player_swimTime", 5 ) * 1000;
456 self.bot.sprintToGoal =
true;
461 self.bot.sprintToGoal =
false;
466 if (
IS_TRUE(
self.bot.sprintToGoal ) )
468 if (
self BotGoalReached() )
483 return distanceSquared( trigger.origin,
self BotGetGoalPosition() ) <= radius * radius;
488 deltaSq = Distance2DSquared(
self BotGetGoalPosition(), point );
489 goalRadius =
self BotGetGoalRadius();
491 return deltaSq <= goalRadius * goalRadius;
497 if ( trigger.className ==
"trigger_use" ||
498 trigger.className ==
"trigger_use_touch" )
502 randomAngle = ( 0, RandomInt( 360 ), 0 );
503 randomVec = AnglesToForward( randomAngle );
505 point = trigger.origin + randomVec * radius;
507 self BotSetGoal( point );
512 self BotSetGoal( trigger.origin, Int( radius ) );
517 mins = trigger GetMins();
518 maxs = trigger GetMaxs();
520 radius = Min( maxs[0], maxs[1] );
521 height = maxs[2] - mins[2];
523 minOrigin = trigger.origin + ( 0, 0, mins[2] );
525 queryHeight = height / 4;
527 queryOrigin = minOrigin + ( 0, 0, queryHeight );
530 if ( GetDvarInt(
"bot_drawtriggerquery", 0 ) )
533 Circle( queryOrigin, radius, (0,1,0),
false,
true, 20*drawS );
534 Circle( queryOrigin + (0,0,queryHeight), radius, (0,1,0),
false,
true, 20*drawS );
535 Circle( queryOrigin - (0,0,queryHeight), radius, (0,1,0),
false,
true, 20*drawS );
538 queryResult = PositionQuery_Source_Navigation( queryOrigin, 0, radius, queryHeight, 17,
self );
540 best_point = undefined;
542 foreach ( point
in queryResult.data )
544 point.score = randomFloatRange( 0, 100 );
546 if ( !isdefined( best_point ) || point.score > best_point.score )
552 if ( isdefined( best_point ) )
563 maxs = trigger GetMaxs();
565 if ( trigger.classname ==
"trigger_radius" )
570 return Min( maxs[0], maxs[1] );
575 maxs = trigger GetMaxs();
577 if ( trigger.classname ==
"trigger_radius" )
591 if ( !GetDvarInt(
"bot_AllowMovement" ) )
597 if (
self BotUnderManualControl() ||
598 self BotGoalReached() ||
599 self util::isstunned() ||
601 self MeleeButtonPressed() ||
608 velocity =
self GetVelocity();
610 if ( velocity[0] == 0 &&
612 ( velocity[2] == 0 ||
self IsPlayerSwimming() ) )
614 DEFAULT(
self.bot.stuckCycles, 0 );
616 self.bot.stuckCycles++;
621 if ( GetDvarInt(
"bot_debugStuck" , 0 ) )
623 Sphere(
self.origin, 16, ( 1, 0, 0 ), 0.25,
false, 16, 1200 );
624 iprintln(
"Bot " +
self.
name +
" not moving at: "+
self.origin );
632 self.bot.stuckCycles = 0;
645 if ( GetTime() <
self.bot.checkPositionTime )
648 self.bot.checkPositionTime = GetTime() + 500;
650 self.bot.positionHistory[
self.bot.positionHistoryIndex] =
self.origin;
656 maxDistSq = undefined;
658 for( i = 0; i <
self.bot.positionHistory.size; i++ )
661 if ( GetDvarInt(
"bot_debugStuck" , 0 ) )
663 Line(
self.bot.positionHistory[i],
self.bot.positionHistory[i] + ( 0, 0, 72 ), ( 0, 1, 0 ), 1,
false, 10 );
666 for ( j = i + 1; j <
self.bot.positionHistory.size; j++ )
668 distSq = DistanceSquared(
self.bot.positionHistory[i],
self.bot.positionHistory[j] );
679 if ( GetDvarInt(
"bot_debugStuck" , 0 ) )
682 iprintln(
"Bot " +
self.
name +
" hanging out at: "+
self.origin );
690 self endon(
"death" );
691 level endon(
"game_ended" );
695 self BotTakeManualControl();
697 escapeAngle =
self GetAngles()[1] + 180 + RandomIntRange( -60, 60 );;
698 escapeDir = AnglesToForward( ( 0, escapeAngle, 0 ) );
700 self BotSetMoveAngle( escapeDir );
701 self BotSetMoveMagnitude( 1 );
705 self BotReleaseManualControl();
710 self.bot.stuckCycles = 0;
711 self.bot.positionHistory = [];
712 self.bot.positionHistoryIndex = 0;
713 self.bot.checkPositionTime = 0;
718 self BotSetGoal(
self.origin );
730 self endon(
"death" );
731 level endon(
"game_ended" );
735 self waittill(
"bot_path_failed", reason );
738 if ( GetDvarInt(
"bot_debugStuck" , 0 ) )
740 goalPosition =
self BotGetGoalPosition();
741 Box(
self.origin, ( -15, -15, 0 ), ( 15, 15, 72 ), 0, ( 0, 1, 0 ), 0.25,
false, 1200 );
742 Box( goalPosition, ( -15, -15, 0 ), ( 15, 15, 72 ), 0, ( 1, 0, 0 ), 0.25,
false, 1200 );
743 Line(
self.origin, goalPosition, ( 1, 1, 1 ), 1,
false, 1200 );
744 iprintln(
"Bot " +
self.
name +
" path failed from: " +
self.origin +
" to: " + goalPosition );
754 self endon(
"death" );
755 level endon(
"game_ended" );
759 self waittill(
"bot_goal_reached", reason );
770 currentWeapon =
self GetCurrentWeapon();
772 if (
self GetWeaponAmmoClip( currentWeapon ) ||
773 !currentWeapon.isheroweapon)
778 if ( isdefined(
self.lastDroppableWeapon ) &&
self hasWeapon(
self.lastDroppableWeapon) )
780 self SwitchToWeapon(
self.lastDroppableWeapon );
786 weapons =
self GetWeaponsList();
788 foreach( weapon
in weapons )
790 slot =
self GadgetGetSlot( weapon );
793 !
self GadgetIsReady( slot ) ||
794 self GadgetIsActive( slot ) )
802 return level.weaponNone;
807 weapons =
self GetWeaponsList();
809 foreach( weapon
in weapons )
816 slot =
self GadgetGetSlot( weapon );
819 !
self GadgetIsReady( slot ) ||
820 self GadgetIsActive( slot ) )
828 return level.weaponNone;
833 if ( !isdefined( weapon ) ||
834 weapon == level.weaponNone ||
835 !weapon.isHeroWeapon )
841 return weapon.isBulletWeapon ||
842 weapon.isProjectileWeapon ||
849 if ( !isdefined( weapon ) ||
850 weapon == level.weaponNone ||
858 self SwitchToWeapon( weapon );
860 else if ( weapon.isHeroWeapon )
866 self BotPressButtonForGadget( weapon );
883 if (
self IsReloading() ||
884 self IsSwitchingWeapons() ||
885 self IsThrowingGrenade() ||
886 self FragButtonPressed() ||
887 self SecondaryOffhandButtonPressed() ||
889 self IsRemoteControlling() ||
890 self IsInVehicle() ||
891 self IsWeaponViewOnlyLinked() )
914 self BotSetGoal(
self.origin );
926 #define COOP_FOLLOW_RADIUS_MIN 150
927 #define COOP_FOLLOW_RADIUS_MAX 300
934 if ( !IsAlive( host ) )
936 players = ArraySort( level.players,
self.origin );
938 foreach( player
in players )
941 player.team ==
self.team &&
953 if ( isdefined( player ) )
957 fwd = AnglesToForward( player.angles );
958 botDir =
self.origin - player.origin;
960 if ( VectorDot( botDir, fwd ) < 0 )
967 radiusMin = followMin - 32;
968 radiusMax = followMin;
973 queryResult = PositionQuery_Source_Navigation( player.origin, radiusMin, radiusMax, 150, 32,
self );
975 fwd = AnglesToForward( player.angles );
977 point = player.origin + fwd * 72;
979 self BotSetGoal( point, 42 );
986 DEFAULT( radiusMax, radiusMin + 1 );
990 radius = RandomIntRange( radiusMin, radiusMax );
991 self BotSetGoal( entity.origin, radius );
1002 DEFAULT( radiusMin,
VAL( level.botSettings.wanderMin, 0 ) );
1003 DEFAULT( radiusMax,
VAL( level.botSettings.wanderMax, 0 ) );
1004 DEFAULT( spacing,
VAL( level.botSettings.wanderSpacing, 0 ) );
1005 DEFAULT( fwdDot,
VAL( level.botSettings.wanderFwdDot, 0 ) );
1007 DEFAULT( fwd, AnglesToForward(
self.angles ) );
1010 fwd = VectorNormalize( ( fwd[0], fwd[1], 0 ) );
1011 queryResult = PositionQuery_Source_Navigation(
self.origin, radiusMin, radiusMax, 150, spacing,
self );
1013 best_point = undefined;
1015 origin = (
self.origin[0],
self.origin[1], 0 );
1017 foreach ( point
in queryResult.data )
1019 movePoint = ( point.origin[0], point.origin[1], 0 );
1020 moveDir = VectorNormalize( movePoint - origin );
1021 dot = VectorDot( moveDir, fwd );
1023 point.score = MapFloat( radiusMin, radiusMax, 0, 50, point.distToOrigin2D );
1027 point.score += randomFloatRange( 30, 50 );
1031 point.score += randomFloatRange( 10, 35 );
1035 point.score += randomFloatRange( 0, 15 );
1037 if ( !isdefined( best_point ) || point.score > best_point.score )
1043 if( isdefined( best_point ) )
1045 self BotSetGoal( best_point.origin, radiusMin );
1050 if ( GetDvarInt(
"bot_debugStuck" , 0 ) )
1052 Circle(
self.origin, radiusMin, ( 1, 0, 0 ),
false,
true, 1200 );
1053 Circle(
self.origin, radiusMax, ( 1, 0, 0 ),
false,
true, 1200 );
1054 Sphere(
self.origin, 16, ( 0, 1, 0 ), 0.25,
false, 16, 1200 );
1055 iprintln(
"Bot " +
self.
name +
" can't find wander point at: "+
self.origin );
1065 #define APPROACH_GOAL_RADIUS_MAX 1500
1066 #define APPROACH_GOAL_SPACING 128
1073 distSq = DistanceSquared(
self.origin, trigger.origin );
1075 if ( distSq < radiusMax * radiusMax )
1083 self approach_point( trigger.origin, radiusMin, radiusMax, spacing );
1092 distSq = DistanceSquared(
self.origin, point );
1094 if ( distSq < radiusMax * radiusMax )
1100 queryResult = PositionQuery_Source_Navigation( point, radiusMin, radiusMax, 150, spacing,
self );
1102 fwd = AnglesToForward(
self.angles );
1105 fwd = ( fwd[0], fwd[1], 0 );
1106 origin = (
self.origin[0],
self.origin[1], 0 );
1108 best_point = undefined;
1110 foreach ( point
in queryResult.data )
1112 movePoint = ( point.origin[0], point.origin[1], 0 );
1113 moveDir = VectorNormalize( movePoint - origin );
1114 dot = VectorDot( moveDir, fwd );
1116 point.score = randomFloatRange( 0, 50 );
1120 point.score += randomFloatRange( 30, 50 );
1124 point.score += randomFloatRange( 0, 15 );
1127 if ( !isdefined( best_point ) || point.score > best_point.score )
1133 if ( isdefined( best_point ) )
1147 if ( players.size > 0 )
1160 foreach( player
in level.players )
1164 players[players.size] = player;
1168 players = ArraySort( players,
self.origin );
1177 self BotSetGoal( player.origin, 64 );
1182 if (
self BotGoalReached() )
1184 self BotSetLookAnglesFromPoint( player GetCentroid() );
1193 #define DEFAULT_START_CORNER_DIST 64
1194 #define DEFAULT_CORNER_DIST 128
1198 self endon(
"death" );
1199 self endon(
"bot_combat_target" );
1200 level endon(
"game_ended" );
1205 startCornerDistSq = cornerDist * cornerDist;
1206 cornerDistSq = cornerDist * cornerDist;
1210 self waittill(
"bot_corner", centerPoint, enterPoint, leavePoint, angle, nextEnterPoint );
1217 if( Distance2DSquared(
self.origin, enterPoint ) < startCornerDistSq ||
1218 Distance2DSquared( leavePoint, nextEnterPoint ) < cornerDistSq )
1223 self thread
wait_corner_radius( startCornerDistSq, centerPoint, enterPoint, leavePoint, angle, nextEnterPoint );
1229 self endon(
"death" );
1230 self endon(
"bot_corner" );
1231 self endon(
"bot_goal_reached" );
1232 self endon(
"bot_combat_target" );
1233 level endon(
"game_ended" );
1235 while( Distance2DSquared(
self.origin, enterPoint ) > startCornerDistSq )
1246 self BotLookAtPoint( ( nextEnterPoint[0], nextEnterPoint[1], nextEnterPoint[1] + 60 ) );
1253 self endon(
"death" );
1254 self endon(
"combat_target" );
1255 level endon(
"game_ended" );
1259 self BotLookForward();
1268 players = GetPlayers();
1270 foreach( player
in players )
1272 if ( player IsHost() )
1283 angles =
self GetPlayerAngles();
1284 fwd = AnglesToForward( angles );
1286 delta = point -
self GetEye();
1287 delta = VectorNormalize( delta );
1289 dot = VectorDot( fwd, delta );
1297 weapons =
self GetWeaponsList();
1299 foreach( weapon
in weapons )
1301 if ( weapon.isRocketLauncher )
1312 self DoDamage(
self.health,
self.origin );
1322 foreach( player
in level.players )
1337 direction_vec = host.origin -
trace[
"position"];
1338 direction = VectorToAngles( direction_vec );
1343 if ( isdefined( bot ) )
1345 bot waittill(
"spawned_player" );
1347 bot SetOrigin(
trace[
"position" ] );
1348 bot SetPlayerAngles( ( bot.angles[0], yaw, bot.angles[2] ) );
1356 direction =
self GetPlayerAngles();
1357 direction_vec = AnglesToForward( direction );
1358 eye =
self GetEye();
1361 direction_vec = ( direction_vec[0] * scale, direction_vec[1] * scale, direction_vec[2] * scale );
1363 return bullettrace( eye, eye + direction_vec, 0, undefined );
1371 iprintln(
"Debug Patrol:" );
1374 if ( !isdefined( points ) || points.size == 0 )
1376 iprintln(
"Route Debug Cancelled" );
1380 iprintln(
"Sending bots to chosen points" );
1382 players = GetPlayers();
1383 foreach( player
in players )
1396 iprintln(
"Square (X) - Add Point" );
1397 iprintln(
"Cross (A) - Done" );
1398 iprintln(
"Circle (B) - Cancel" );
1406 if ( isdefined( point ) )
1408 point = GetClosestPointOnNavMesh( point, 128 );
1410 if ( isdefined( point ) )
1412 Sphere( point, 16, ( 0, 0, 1 ), 0.25,
false, 16, 1 );
1416 if (
self ButtonPressed(
"BUTTON_X" ) )
1418 if ( isdefined( point ) && ( points.size == 0 || Distance2D( point, points[points.size-1] ) > 16 ) )
1420 points[points.size] = point;
1423 else if (
self ButtonPressed(
"BUTTON_A" ) )
1427 else if (
self ButtonPressed(
"BUTTON_B" ) )
1432 for ( i = 0; i < points.size; i++ )
1434 Sphere( points[i], 16, ( 0, 1, 0 ), 0.25,
false, 16, 1 );
1441 self notify(
"debug_patrol" );
1442 self endon(
"death" );
1443 self endon(
"debug_patrol" );
1453 self waittill(
"bot_goal_reached" );
1455 i = ( i + 1 ) % points.size;
1469 cmd = GetDvarString(
"devgui_bot",
"" );
1471 if ( !isdefined( level.botDevguiCmd ) || ![[level.botDevguiCmd]](cmd) )
1491 SetDvar(
"devgui_bot",
"" );
1507 case "add_crosshair":
1524 if ( !isdefined( seconds ) )
1529 if ( !isdefined( color ) )
1531 color = ( 1, 0, 0 );
1534 frames = Int( 20 * seconds );
1535 DebugStar( origin, frames, color );