From 14d253445b1caa0ccdebb3cd7023da8a62042bce Mon Sep 17 00:00:00 2001 From: ineed bots Date: Thu, 27 Aug 2026 22:13:25 -0600 Subject: [PATCH] hq --- maps/mp/bots/_bot.gsc | 4 +- maps/mp/bots/_bot_internal.gsc | 37 +++- maps/mp/bots/_bot_script.gsc | 383 ++++++++++++++++++++++++++++++++- maps/mp/bots/_bot_utility.gsc | 42 +++- maps/mp/bots/_wp_editor.gsc | 18 +- 5 files changed, 455 insertions(+), 29 deletions(-) diff --git a/maps/mp/bots/_bot.gsc b/maps/mp/bots/_bot.gsc index 45b55fb..4122fc2 100644 --- a/maps/mp/bots/_bot.gsc +++ b/maps/mp/bots/_bot.gsc @@ -369,6 +369,8 @@ hook_callbacks() */ fixGamemodes() { + level.gametype = getcvar( "g_gametype" ); + for ( i = 0; i < 19; i++ ) { wait 0.05; @@ -630,7 +632,7 @@ connected() level.bots[ level.bots.size ] = self; self thread onDisconnect(); self thread watchBotDebugEvent(); - + waittillframeend; // wait for waittills to process level notify( "bot_connected", self ); } diff --git a/maps/mp/bots/_bot_internal.gsc b/maps/mp/bots/_bot_internal.gsc index 860b700..900169e 100644 --- a/maps/mp/bots/_bot_internal.gsc +++ b/maps/mp/bots/_bot_internal.gsc @@ -169,6 +169,20 @@ bot_skip_killcam() } } +/* + CoD2, hq for example will spawn twice without a death inbetween +*/ +doubleSpawnFix() +{ + self endon( "disconnect" ); + self endon( "death" ); + + self waittill( "spawned_player" ); + + waittillframeend; + self suicide(); +} + /* When the bot spawns. */ @@ -179,6 +193,7 @@ onPlayerSpawned() for ( ;; ) { self waittill( "spawned_player" ); + self thread doubleSpawnFix(); self resetBotVars(); self thread onWeaponChange(); @@ -673,8 +688,8 @@ updateAimOffset( obj ) if ( diffAimAmount > 0 ) { obj.aim_offset_base = ( randomfloatrange( 0 - diffAimAmount, diffAimAmount ), - randomfloatrange( 0 - diffAimAmount, diffAimAmount ), - randomfloatrange( 0 - diffAimAmount, diffAimAmount ) ); + randomfloatrange( 0 - diffAimAmount, diffAimAmount ), + randomfloatrange( 0 - diffAimAmount, diffAimAmount ) ); } else { @@ -868,15 +883,15 @@ target_loop() } canTargetPlayer = ( ( player checkTraceForBone( myEye, "j_head" ) || - player checkTraceForBone( myEye, "j_ankle_le" ) || - player checkTraceForBone( myEye, "j_ankle_ri" ) ) - - && ( SmokeTrace( myEye, player.origin, level.smokeradius ) || - daDist < level.bots_maxknifedistance * 4 ) - - && ( getConeDot( player.origin, self.origin, myAngles ) >= myFov || - ( isObjDef && obj.trace_time ) ) ); - + player checkTraceForBone( myEye, "j_ankle_le" ) || + player checkTraceForBone( myEye, "j_ankle_ri" ) ) + + && ( SmokeTrace( myEye, player.origin, level.smokeradius ) || + daDist < level.bots_maxknifedistance * 4 ) + + && ( getConeDot( player.origin, self.origin, myAngles ) >= myFov || + ( isObjDef && obj.trace_time ) ) ); + if ( isdefined( self.bot.target_this_frame ) && self.bot.target_this_frame == player ) { self.bot.target_this_frame = undefined; diff --git a/maps/mp/bots/_bot_script.gsc b/maps/mp/bots/_bot_script.gsc index d51f83c..81b3d38 100644 --- a/maps/mp/bots/_bot_script.gsc +++ b/maps/mp/bots/_bot_script.gsc @@ -670,7 +670,7 @@ start_bot_threads() self thread doReloadCancel(); self thread bot_weapon_think(); - + self thread bot_revenge_think(); self thread follow_target(); self thread bot_listen_to_steps(); @@ -682,19 +682,19 @@ start_bot_threads() self thread bot_think_follow(); self thread bot_think_camp(); } - + if ( getcvarint( "bots_play_nade" ) ) { self thread bot_use_grenade_think(); } - + if ( getcvarint( "bots_play_obj" ) ) { - // self thread bot_hq(); - + self thread bot_hq(); + // self thread bot_sd_defenders(); // self thread bot_sd_attackers(); - + // self thread bot_cap(); } } @@ -1497,7 +1497,7 @@ killCampAfterTime( time ) } wait 0.05; - + self ClearScriptGoal(); self ClearScriptAimPos(); @@ -1726,3 +1726,372 @@ bot_use_grenade_think() self bot_use_grenade_think_loop( data ); } } + +/* + CoD2 +*/ +getRadioEnemies( radio, myTeam ) +{ + enemys = radio.axis; + + if ( myTeam == "axis" ) + { + enemys = radio.allies; + } + + if ( !isdefined( enemys ) ) + { + enemys = 0; + } + + return enemys; +} + +/* + CoD2 +*/ +touchingRadio( radio ) +{ + if ( ( ( distance( self.origin, radio.origin ) ) <= radio.radius ) && ( distance( ( 0, 0, self.origin[2] ), ( 0, 0, radio.origin[2] ) ) <= level.zradioradius ) ) + { + return true; + } + + return false; +} + +/* + Bots play headquarters +*/ +bot_hq_loop() +{ + myTeam = self.pers[ "team" ]; + otherTeam = getotherteam( myTeam ); + radio = undefined; + + for ( i = 0; i < level.radio.size; i++ ) + { + if ( level.radio[ i ].hidden ) + { + continue; + } + + radio = level.radio[ i ]; + break; + } + + if ( !isdefined( radio ) ) + { + return; + } + + origin = ( radio.origin[ 0 ], radio.origin[ 1 ], radio.origin[ 2 ] + 5 ); + + // if neut or enemy + if ( radio.team != myTeam ) + { + // capture it + + self BotNotifyBotEvent( "hq", "go", "cap" ); + + self.bot_lock_goal = true; + self SetScriptGoal( origin, 64 ); + self thread bot_hq_go_cap( radio ); + + event = self waittill_any_return( "goal", "bad_path", "new_goal" ); + + if ( event != "new_goal" ) + { + self ClearScriptGoal(); + } + + if ( event != "goal" ) + { + self.bot_lock_goal = false; + return; + } + + if ( radio.hidden || !self touchingRadio( radio ) ) + { + self.bot_lock_goal = false; + return; + } + + self BotNotifyBotEvent( "hq", "start", "cap" ); + + self SetScriptGoal( self.origin, 64 ); + + while ( !radio.hidden && self touchingRadio( radio ) && radio.team != myTeam ) + { + wait 0.5; + + if ( getRadioEnemies( radio, myTeam ) ) + { + break; // no prog made, enemy must be capping + } + + self thread bot_do_random_action_for_objective( radio ); + } + + self ClearScriptGoal(); + self.bot_lock_goal = false; + + self BotNotifyBotEvent( "hq", "stop", "cap" ); + } + else // we own it + { + if ( getRadioEnemies( radio, myTeam ) ) // underattack + { + self BotNotifyBotEvent( "hq", "start", "defend" ); + + self.bot_lock_goal = true; + self SetScriptGoal( origin, 64 ); + self thread bot_hq_watch_flashing( radio ); + + if ( self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal" ) + { + self ClearScriptGoal(); + } + + self.bot_lock_goal = false; + + self BotNotifyBotEvent( "hq", "stop", "defend" ); + return; + } + + if ( self HasScriptGoal() ) + { + return; + } + + if ( distancesquared( origin, self.origin ) <= 1024 * 1024 ) + { + return; + } + + self SetScriptGoal( origin, 256 ); + + if ( self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal" ) + { + self ClearScriptGoal(); + } + } +} + +/* + Bots play headquarters +*/ +bot_hq() +{ + self endon( "death" ); + self endon( "disconnect" ); + + if ( level.gametype != "hq" ) + { + return; + } + + for ( ;; ) + { + wait( randomintrange( 3, 5 ) ); + + if ( self.bot_lock_goal ) + { + continue; + } + + if ( !isdefined( level.radio ) || !level.radio.size ) + { + continue; + } + + self bot_hq_loop(); + } +} + +/* + Waits until not touching the trigger and it is the current radio. +*/ +bot_hq_go_cap( radio ) +{ + self endon( "death" ); + self endon( "disconnect" ); + self endon( "goal" ); + self endon( "bad_path" ); + self endon( "new_goal" ); + + for ( ;; ) + { + wait randomintrange( 2, 4 ); + + if ( self touchingRadio( radio ) ) + { + break; + } + + if ( radio.hidden ) + { + break; + } + } + + if ( radio.hidden ) + { + self notify( "bad_path" ); + } + else + { + self notify( "goal" ); + } +} + +/* + Waits while the radio is under attack. +*/ +bot_hq_watch_flashing( radio ) +{ + self endon( "death" ); + self endon( "disconnect" ); + self endon( "goal" ); + self endon( "bad_path" ); + self endon( "new_goal" ); + + myteam = self.team; + + for ( ;; ) + { + wait 0.5; + + if ( !getRadioEnemies( radio, myteam ) ) + { + break; + } + + if ( radio.hidden ) + { + break; + } + } + + self notify( "bad_path" ); +} + +/* + Bots do random stance +*/ +BotRandomStance() +{ + if ( randomint( 100 ) < 80 ) + { + self BotSetStance( "prone" ); + } + else if ( randomint( 100 ) < 60 ) + { + self BotSetStance( "crouch" ); + } + else + { + self BotSetStance( "stand" ); + } +} + +/* + Bots will look at a random thing +*/ +BotLookAtRandomThing( obj_target ) +{ + self endon( "death" ); + self endon( "disconnect" ); + + if ( self HasScriptAimPos() ) + { + return; + } + + rand = randomint( 100 ); + + nearestEnemy = undefined; + + for ( i = 0; i < level.players.size; i++ ) + { + player = level.players[ i ]; + + if ( !isdefined( player ) || !isdefined( player.team ) ) + { + continue; + } + + if ( !isalive( player ) ) + { + continue; + } + + if ( level.teambased && self.team == player.team ) + { + continue; + } + + if ( !isdefined( nearestEnemy ) || distancesquared( self.origin, player.origin ) < distancesquared( self.origin, nearestEnemy.origin ) ) + { + nearestEnemy = player; + } + } + + origin = ( 0, 0, self getEyeHeight() ); + + if ( isdefined( nearestEnemy ) && distancesquared( self.origin, nearestEnemy.origin ) < 1024 * 1024 && rand < 40 ) + { + origin += ( nearestEnemy.origin[ 0 ], nearestEnemy.origin[ 1 ], self.origin[ 2 ] ); + } + else if ( isdefined( obj_target ) && rand < 50 ) + { + origin += ( obj_target.origin[ 0 ], obj_target.origin[ 1 ], self.origin[ 2 ] ); + } + else if ( rand < 85 ) + { + origin += self.origin + anglestoforward( ( 0, self.angles[ 1 ] - 180, 0 ) ) * 1024; + } + else + { + origin += self.origin + anglestoforward( ( 0, randomint( 360 ), 0 ) ) * 1024; + } + + self SetScriptAimPos( origin ); + wait 2; + self ClearScriptAimPos(); +} + +/* + Bots will do stuff while waiting for objective +*/ +bot_do_random_action_for_objective( obj_target ) +{ + self endon( "death" ); + self endon( "disconnect" ); + self notify( "bot_do_random_action_for_objective" ); + self endon( "bot_do_random_action_for_objective" ); + + if ( !isdefined( self.bot_random_obj_action ) ) + { + self.bot_random_obj_action = true; + + if ( randomint( 100 ) < 75 ) + { + self thread BotLookAtRandomThing( obj_target ); + } + } + else + { + if ( self getStance() != "prone" && randomint( 100 ) < 15 ) + { + self BotSetStance( "prone" ); + } + else if ( randomint( 100 ) < 5 ) + { + self thread BotLookAtRandomThing( obj_target ); + } + } + + wait 2; + self.bot_random_obj_action = undefined; +} diff --git a/maps/mp/bots/_bot_utility.gsc b/maps/mp/bots/_bot_utility.gsc index 86ef44a..66ea391 100644 --- a/maps/mp/bots/_bot_utility.gsc +++ b/maps/mp/bots/_bot_utility.gsc @@ -521,7 +521,7 @@ getGrenadeTypes() grenadeTypes[ 5 ] = "smoke_grenade_russian_mp"; grenadeTypes[ 6 ] = "frag_grenade_german_mp"; grenadeTypes[ 7 ] = "smoke_grenade_german_mp"; - + return grenadeTypes; } @@ -648,6 +648,27 @@ IsWeaponClipOnly( weap ) return issubstr( weap, "grenade_" ); } +/* + Set the bot's stance +*/ +BotSetStance( stance ) +{ + switch ( stance ) + { + case "stand": + self maps\mp\bots\_bot_internal::stand(); + break; + + case "crouch": + self maps\mp\bots\_bot_internal::crouch(); + break; + + case "prone": + self maps\mp\bots\_bot_internal::prone(); + break; + } +} + /* CoD2 */ @@ -1039,6 +1060,25 @@ bot_wait_for_host() } } +/* + CoD2 +*/ +getotherteam( team ) +{ + if ( team == "axis" ) + { + return "allies"; + } + else if ( team == "allies" ) + { + return "axis"; + } + else + { + return "none"; + } +} + /* Babylonian_method */ diff --git a/maps/mp/bots/_wp_editor.gsc b/maps/mp/bots/_wp_editor.gsc index ad5f6fc..922b8d5 100644 --- a/maps/mp/bots/_wp_editor.gsc +++ b/maps/mp/bots/_wp_editor.gsc @@ -28,7 +28,7 @@ init() map( getcvar( "mapname" ) ); return; } - + // cod2 only allows localized strings... precachestring( &"^1SecondaryOffhand - ^2Add Waypoint; ^3MeleeButton - ^4Link Waypoint; ^5FragButton - ^6delete Waypoint; ^7UseButton + AttackButton - ^8Save" ); @@ -39,7 +39,7 @@ init() setcvar( "bots_manage_add", 0 ); setcvar( "bots_manage_fill_kick", 1 ); setcvar( "bots_manage_fill_spec", 1 ); - + setcvar( "smoke", 0 ); // bind 2 "set smoke 1; wait 50; set smoke 0" setcvar( "frag", 0 ); // bind 3 "set frag 1; wait 50; set frag 0" setcvar( "speed", 0 ); // bind 4 "togglep speed 0 1" @@ -113,7 +113,7 @@ beginDebug() level.autolink = false; self.closest = -1; self.command = undefined; - + self.bot_model_fix = true; self freezecontrols( false ); @@ -369,7 +369,7 @@ watchSaveWaypointsCommand() println( "********* Start Bot Warfare WPDump *********" ); println( level.waypointcount ); - + // cod2 fprintln always ends the write with a comma... f = openfile( filename, "write" ); // besure to fix it in post ig @@ -416,7 +416,7 @@ watchSaveWaypointsCommand() fprintln( f, str + "\n" ); } } - + if ( f >= 0 ) { closefile( f ); @@ -709,8 +709,8 @@ destroyOnDeath( hud ) { hud endon( "death" ); self waittill_either( "death", "disconnect" ); - - if ( isdefined(hud) ) + + if ( isdefined( hud ) ) { hud destroy(); } @@ -723,7 +723,7 @@ textScroll( string ) text = newclienthudelem( self ); self thread destroyOnDeath( text ); - + text.font = "default"; text.fontscale = 1.5; text.alignx = "center"; @@ -738,7 +738,7 @@ textScroll( string ) text.x = 1200; text moveovertime( 20 ); text.x = -1200; - + wait 20; } }