diff --git a/maps/bots/_bot.gsc b/maps/bots/_bot.gsc index 46f7d74..81503d4 100644 --- a/maps/bots/_bot.gsc +++ b/maps/bots/_bot.gsc @@ -93,6 +93,14 @@ init() level.bots_maxShotgunDistance = 500; level.bots_maxShotgunDistance *= level.bots_maxShotgunDistance; + level.bot_allowed_negotiation_links = []; + level.bot_allowed_negotiation_links[ level.bot_allowed_negotiation_links.size ] = "zombie_jump_down_72"; + level.bot_allowed_negotiation_links[ level.bot_allowed_negotiation_links.size ] = "zombie_jump_down_96"; + level.bot_allowed_negotiation_links[ level.bot_allowed_negotiation_links.size ] = "zombie_jump_down_120"; + level.bot_allowed_negotiation_links[ level.bot_allowed_negotiation_links.size ] = "zombie_jump_down_127"; + level.bot_allowed_negotiation_links[ level.bot_allowed_negotiation_links.size ] = "zombie_jump_down_184"; + level.bot_allowed_negotiation_links[ level.bot_allowed_negotiation_links.size ] = "zombie_jump_down_190"; + level.players = []; level.bots = []; diff --git a/maps/bots/_bot_internal.gsc b/maps/bots/_bot_internal.gsc index ec965df..b3fe6c8 100644 --- a/maps/bots/_bot_internal.gsc +++ b/maps/bots/_bot_internal.gsc @@ -1538,9 +1538,11 @@ doWalk( goal, dist, isScriptGoal ) path_was_truncated = ( current + 1 ) >= 32; //Couldn't generate path to goal + self.path_inaccessible = false; if ( current <= -1 ) { self notify( "bad_path_internal" ); + self.path_inaccessible = true; return; } @@ -1757,7 +1759,7 @@ getRandomLargestStafe( dist ) */ initAStar( goal ) { - nodes = generatePath( self.origin, goal, self.team, false ); + nodes = generatePath( self.origin, goal, self.team, level.bot_allowed_negotiation_links ); if ( !isDefined( nodes ) ) { diff --git a/maps/bots/_bot_script.gsc b/maps/bots/_bot_script.gsc index dc0764a..3138578 100644 --- a/maps/bots/_bot_script.gsc +++ b/maps/bots/_bot_script.gsc @@ -23,9 +23,12 @@ connected() self thread onBotSpawned(); self thread onSpawned(); self thread initialize_bot_actions_queue(); + self thread bot_valid_pump(); + //self thread bot_objective_inaccessible_pump(); self.on_powerup_grab_func = ::bot_on_powerup_grab; self.on_revive_success_func = ::bot_on_revive_success; + self.path_inaccessible = false; } /* @@ -409,6 +412,7 @@ add_possible_bot_objective( objective_group, target_ent, is_global_shared ) objective_struct.target_ent = target_ent; objective_struct.owner = undefined; objective_struct.is_objective = true; + objective_struct.bad = false; level.zbot_objective_glob[ objective_group ].active_objectives[ "obj_id_" + id ] = objective_struct; } @@ -437,7 +441,17 @@ get_all_objectives_for_group( objective_group ) return level.zbot_objective_glob[ objective_group ].active_objectives; } -set_objective_for_bot( objective_group, ent ) +bot_get_objective() +{ + return self.zbot_current_objective; +} + +bot_has_objective() +{ + return isDefined( self.zbot_current_objective ); +} + +bot_set_objective( objective_group, ent ) { if ( !isDefined( ent ) ) { @@ -534,6 +548,36 @@ set_bot_objective_blocked_by_objective( primary_objective_group, primary_ent, bl } } +bot_is_objective_owner( objective_group, ent ) +{ + if ( !isDefined( ent ) ) + { + assertMsg( "Ent is undefined" ); + return false; + } + + id = ent getEntityNumber(); + + active_objectives = level.zbot_objective_glob[ objective_group ].active_objectives; + + objective = active_objectives[ "obj_id_" + id ]; + + objective_exists = isDefined( objective ); + assert( objective_exists, "Objective with " + id + " id number does not point to a objective in group " + objective_group ); + if ( !objective_exists ) + { + return false; + } + + assert( objective.is_global_shared, "Objective with " + id + " id number cannot be set to have an owner because is_global_shared field is false in group " + objective_group ); + if ( !objective.is_global_shared ) + { + return false; + } + + return isDefined( objective.owner ) && objective.owner == self; +} + set_bot_global_shared_objective_owner_by_ent( objective_group, ent, new_owner ) { if ( !isDefined( ent ) ) @@ -581,6 +625,30 @@ set_bot_global_shared_objective_owner_by_reference( objective_group, objective, objective.owner = new_owner; } +mark_objective_bad( objective_group, ent ) +{ + if ( !isDefined( ent ) ) + { + assertMsg( "Ent is undefined" ); + return; + } + + id = ent getEntityNumber(); + + active_objectives = level.zbot_objective_glob[ objective_group ].active_objectives; + + objective = active_objectives[ "obj_id_" + id ]; + + objective_exists = isDefined( objective ); + assert( objective_exists, "Objective with " + id + " id number does not point to a objective in group " + objective_group ); + if ( !objective_exists ) + { + return; + } + + objective.bad = true; +} + free_bot_objective( objective_group, ent ) { if ( !isDefined( ent ) ) @@ -605,9 +673,9 @@ free_bot_objective( objective_group, ent ) players = getPlayers(); for ( i = 0; i < players.size; i++ ) { - if ( players[ i ].pers[ "isBot" ] ) + if ( isDefined( players[ i ].pers[ "isBot" ] ) && players[ i ].pers[ "isBot" ] ) { - if ( players[ i ].zbot_current_objective == objective ) + if ( isDefined( players[ i ].zbot_current_objective ) && players[ i ].zbot_current_objective == objective ) { players[ i ].zbot_current_objective = undefined; } @@ -678,20 +746,22 @@ register_bot_objective_action_for_queue( group_name, action_name ) process_next_queued_action( group_name ) { - if ( self.zbot_actions_in_queue[ group_name ][ self.action_queue[ group_name ][ 0 ].action_name ].queued ) + if ( self.zbot_actions_in_queue[ group_name ][ self.action_queue[ group_name ][ 0 ].action_name ].is_current ) { return; } self.action_queue[ group_name ] = self sort_array_by_priority_field( self.action_queue[ group_name ] ); - self [[ self.action_queue[ group_name ][ 0 ].init_func ]](); + action_name = self.action_queue[ group_name ][ 0 ].action_name; - self thread [[ self.action_queue[ group_name ][ 0 ].action ]](); + self [[ level.zbots_actions[ group_name ][ action_name ].init_func ]](); - self.zbot_actions_in_queue[ group_name ][ self.action_queue[ group_name ][ 0 ].action_name ].is_current = true; + self thread [[ level.zbots_actions[ group_name ][ action_name ].action ]](); - self thread wait_for_action_completion( group_name, self.action_queue[ group_name ][ 0 ].action_name ); + self.zbot_actions_in_queue[ group_name ][ action_name ].is_current = true; + + self thread wait_for_action_completion( group_name, action_name ); } wait_for_action_completion( group_name, action_name ) @@ -733,7 +803,7 @@ wait_for_action_completion( group_name, action_name ) self notify( action_name + "_end_think" ); - self [[ self.action_queue[ group_name ][ 0 ].post_think_func ]]( end_state ); + self [[ level.zbots_actions[ group_name ][ action_name ].post_think_func ]]( end_state ); if ( save_action ) { @@ -761,6 +831,13 @@ pick_actions_to_add_to_queue( group_name ) } */ + //Reboot the action queue because the last member was deleted which deletes the array + if ( !isDefined( self.action_queue ) || !isDefined( self.action_queue[ group_name ] ) ) + { + self.action_queue = []; + self.action_queue[ group_name ] = []; + } + if ( !isDefined( self.action_id ) ) { self.action_id = 0; @@ -794,43 +871,45 @@ bot_clear_actions_queue() } } -check_if_action_is_completed_in_group( group_name ) +check_if_action_is_completed_in_group( group_name, action_name ) { - if ( [[ level.zbots_actions[ group_name ][ self.action_queue[ group_name ][ 0 ].action_name ].check_if_complete_func ]]() ) + assert( isDefined( level.zbots_actions[ group_name ][ action_name ].check_if_complete_func ) ); + + if ( self [[ level.zbots_actions[ group_name ][ action_name ].check_if_complete_func ]]() ) { - self notify( self.action_queue[ group_name ][ 0 ].action_name + "_complete" ); + self notify( action_name + "_complete" ); } } -check_if_action_should_be_postponed_in_group( group_name ) +check_if_action_should_be_postponed_in_group( group_name, action_name ) { - if ( [[ level.zbots_actions[ group_name ][ self.action_queue[ group_name ][ 0 ].action_name ].should_postpone_func ]]() ) + if ( self [[ level.zbots_actions[ group_name ][ action_name ].should_postpone_func ]]() ) { - self notify( self.action_queue[ group_name ][ 0 ].action_name + "_postpone" ); + self notify( action_name + "_postpone" ); } } -check_if_action_should_be_canceled_in_group( group_name ) +check_if_action_should_be_canceled_in_group( group_name, action_name ) { - if ( [[ level.zbots_actions[ group_name ][ self.action_queue[ group_name ][ 0 ].action_name ].should_cancel_func ]]() ) + if ( self [[ level.zbots_actions[ group_name ][ action_name ].should_cancel_func ]]() ) { - self notify( self.action_queue[ group_name ][ 0 ].action_name + "_cancel" ); + self notify( action_name + "_cancel" ); } } -check_if_action_should_be_postponed_globally( group_name ) +check_if_action_should_be_postponed_globally( group_name, action_name ) { - if ( action_should_be_postponed_global( group_name, self.action_queue[ group_name ][ 0 ].action_name ) ) + if ( action_should_be_postponed_global( group_name, action_name ) ) { - self notify( self.action_queue[ group_name ][ 0 ].action_name + "_postpone" ); + self notify( action_name + "_postpone" ); } } -check_if_action_should_be_canceled_globally( group_name ) +check_if_action_should_be_canceled_globally( group_name, action_name ) { - if ( action_should_be_canceled_global( group_name, self.action_queue[ group_name ][ 0 ].action_name ) ) + if ( action_should_be_canceled_global( group_name, action_name ) ) { - self notify( self.action_queue[ group_name ][ 0 ].action_name + "_cancel" ); + self notify( action_name + "_cancel" ); } } @@ -861,7 +940,9 @@ bot_action_think() while ( true ) { - wait 1; + wait 0.05; + //Wait until the end of the frame so any variables set by _bot_internal in the current frame will have up to date values + waittillframeend; group_name = "objective"; @@ -869,19 +950,21 @@ bot_action_think() //self check_for_forced_action( group_name ); - if ( self.action_queue[ group_name ].size <= 0 ) + if ( !isDefined( self.action_queue[ group_name ][ 0 ] ) ) { continue; } self process_next_queued_action( group_name ); - self check_if_action_is_completed_in_group( group_name ); - self check_if_action_should_be_postponed_in_group( group_name ); - self check_if_action_should_be_canceled_in_group( group_name ); + action_name = self.action_queue[ group_name ][ 0 ].action_name; - self check_if_action_should_be_postponed_globally( group_name ); - self check_if_action_should_be_canceled_globally( group_name ); + self check_if_action_is_completed_in_group( group_name, action_name ); + self check_if_action_should_be_postponed_in_group( group_name, action_name ); + self check_if_action_should_be_canceled_in_group( group_name, action_name ); + + self check_if_action_should_be_postponed_globally( group_name, action_name ); + self check_if_action_should_be_canceled_globally( group_name, action_name ); } } @@ -892,6 +975,11 @@ action_should_be_postponed_global( primary_group_name, action_name ) action_should_be_canceled_global( primary_group_name, action_name ) { + obj = self bot_get_objective(); + if ( self.path_inaccessible || obj.bad ) + { + return true; + } return false; } @@ -904,15 +992,62 @@ action_should_be_paused_global( primary_group_name, action_name ) bot_grab_powerup() { + self endon( "disconnect" ); self endon( "powerup_end_think" ); + self endon( "bot_in_invalid_state" ); + level endon( "end_game" ); if ( !isDefined( self.available_powerups ) || self.available_powerups.size <= 0 ) { return; } - set_bot_global_shared_objective_owner_by_ent( "powerup", self.available_powerups[ 0 ].target_ent, self ); - self SetScriptGoal( self.available_powerups[ 0 ].target_ent.origin ); + powerup_obj = self.available_powerups[ 0 ]; + powerup_obj_ent = powerup_obj.target_ent; + + set_bot_global_shared_objective_owner_by_ent( "powerup", powerup_obj_ent, self ); + self bot_set_objective( "powerup", powerup_obj_ent ); + while ( isDefined( powerup_obj_ent ) && isDefined( powerup_obj ) && self bot_is_objective_owner( "powerup", powerup_obj_ent ) ) + { + wait 1; + self ClearScriptAimPos(); + self SetScriptGoal( powerup_obj_ent.origin ); + + result = self waittill_any_return( "goal", "bad_path", "new_goal" ); + + if ( result != "goal" ) + { + continue; + } + //Wait to see if the bot was able to grab the powerup + wait 0.5; + //Check if powerup still exists + if ( isDefined( powerup_obj_ent ) ) + { + height_difference = self.origin[ 2 ] - powerup_obj_ent.origin[ 2 ]; + if ( height_difference < 49 ) + { + self BotJump(); + wait 0.5; + waittillframeend; + //Check if bot was able to grab the powerup by jumping + if ( self bot_has_objective() || isDefined( powerup_obj_ent ) ) + { + //Mark objective as bad so bots will ignore it from now on + powerup_obj.bad = true; + } + } + else + { + powerup_obj.bad = true; + } + + if ( powerup_obj.bad ) + { + break; + } + } + } } bot_powerup_process_order() @@ -930,6 +1065,7 @@ bot_powerup_post_think( state ) self.successfully_grabbed_powerup = false; self ClearScriptGoal(); self ClearScriptAimPos(); + self clear_objective_for_bot(); } bot_should_grab_powerup() @@ -952,6 +1088,10 @@ bot_should_grab_powerup() { continue; } + if ( obj.bad ) + { + continue; + } if ( isDefined( obj.owner ) ) { continue; @@ -966,7 +1106,7 @@ bot_should_grab_powerup() { continue; } - if ( !isDefined( generatePath( self.origin, powerup.origin, self.team, false ) ) ) + if ( !isDefined( generatePath( self.origin, powerup.origin, self.team, level.bot_allowed_negotiation_links ) ) ) { continue; } @@ -1010,7 +1150,7 @@ bot_revive_player() self endon( "disconnect" ); self endon( "revive_end_think" ); - self endon( "player_downed" ); + self endon( "bot_in_invalid_state" ); level endon( "end_game" ); player_to_revive_obj = self.available_revives[ 0 ]; @@ -1021,32 +1161,37 @@ bot_revive_player() //If player is no longer valid to revive stop trying to revive //If bot doesn't have an objective anymore or the objective has changed stop trying to revive - while ( isDefined( player_to_revive ) && isDefined( player_to_revive_obj ) ) + while ( isDefined( player_to_revive ) && isDefined( player_to_revive_obj ) && self bot_is_objective_owner( "powerup", powerup_obj_ent ) ) { - wait 0.05; + wait 1; //Constantly update the goal just in case the player is moving(T5 or higher only) self ClearScriptAimPos(); - self SetScriptGoal( player_to_revive.origin, 16 ); + self SetScriptGoal( player_to_revive.origin, 32 ); - if ( !self AtScriptGoal() ) + result = self waittill_any_return( "goal", "bad_path", "new_goal" ); + + //printConsole( result ); + if ( result != "goal" ) { + //printConsole( "Bot is not at goal" ); continue; //TODO: Add check to see if another player is reviving target player //TODO: Add code to revive player, possibly add the ability to circle revive? } if ( !isDefined( player_to_revive.revivetrigger ) ) { + self.should_cancel_revive_obj = true; return; } //Check if the bot is reviving the player already and also that the player isn't being revived already - if ( player_to_revive.revivetrigger.beingrevived && !self maps\_laststand::is_reviving( player_to_revive ) ) + if ( player_to_revive.revivetrigger.beingrevived ) { continue; } SetScriptAimPos( player_to_revive.origin ); - time = 3; + time = 3.2; if ( self hasPerk( "specialty_quickrevive" ) ) { time /= 2; @@ -1069,14 +1214,17 @@ bot_revive_process_order() bot_revive_player_init() { + self.should_cancel_revive_obj = false; self.successfully_revived_player = false; } bot_revive_player_post_think( state ) { + self.should_cancel_revive_obj = false; self.successfully_revived_player = false; self ClearScriptGoal(); self ClearScriptAimPos(); + self clear_objective_for_bot(); } bot_should_revive_player() @@ -1096,7 +1244,10 @@ bot_should_revive_player() { continue; } - + if ( obj.bad ) + { + continue; + } self.available_revives[ self.available_revives.size ] = downed_players_objs[ obj_keys[ i ] ]; } return self.available_revives.size > 0; @@ -1109,7 +1260,14 @@ bot_check_complete_revive_player() bot_revive_player_should_cancel() { - return !isDefined( self.available_revives[ 0 ] ) || !isDefined( self.available_revives[ 0 ].target_ent ) || !isDefined( self.available_revives[ 0 ].target_ent.revivetrigger ); + if ( !isDefined( self.available_revives[ 0 ] ) + || !isDefined( self.available_revives[ 0 ].target_ent ) + || !isDefined( self.available_revives[ 0 ].target_ent.revivetrigger ) + || self.should_cancel_revive_obj ) + { + return true; + } + return false; } bot_revive_player_should_postpone() @@ -1132,6 +1290,7 @@ store_powerups_dropped() while ( true ) { level waittill( "powerup_dropped", powerup ); + waittillframeend; if ( !isDefined( powerup ) ) { continue; @@ -1182,6 +1341,77 @@ free_revive_objective_when_needed() free_bot_objective( "revive", self ); } +bot_valid_pump() +{ + level endon( "end_game" ); + + obj_sav = undefined; + + while ( true ) + { + obj_sav = self.zbot_current_objective; + wait 0.5; + if ( !maps\so\zm_common\_zm_utility::is_player_valid( self ) ) + { + if ( isDefined( self ) ) + { + self notify( "bot_in_invalid_state" ); + self clear_objective_for_bot(); + } + else if ( isDefined( obj_sav ) ) + { + set_bot_global_shared_objective_owner_by_ent( obj_sav.group, obj_sav.target_ent, undefined ); + } + + while ( isDefined( self ) && !maps\so\zm_common\_zm_utility::is_player_valid( self ) ) + { + wait 0.5; + } + if ( !isDefined( self ) ) + { + return; + } + } + } +} + +bot_objective_inaccessible_pump() +{ + self endon( "disconnect" ); + level endon( "end_game" ); + + while ( true ) + { + invalid_obj = false; + wait 0.5; + while ( !self bot_has_objective() ) + { + wait 0.5; + } + + while ( self bot_has_objective() ) + { + wait 1; + obj = self bot_get_objective(); + + if ( !isDefined( obj ) || !isDefined( obj.target_ent ) ) + { + invalid_obj = true; + } + else if ( !isDefined( generatePath( self.origin, obj.target_ent.origin, self.team, level.bot_allowed_negotiation_links ) ) ) + { + invalid_obj = true; + } + if ( invalid_obj ) + { + self notify( "bot_objective_inaccessible" ); + self.path_inaccessible = true; + break; + } + } + } +} + bot_on_powerup_grab( powerup ) { self.successfully_grabbed_powerup = true; diff --git a/maps/bots/_bot_utility.gsc b/maps/bots/_bot_utility.gsc index d9cde5a..1ee8cfa 100644 --- a/maps/bots/_bot_utility.gsc +++ b/maps/bots/_bot_utility.gsc @@ -130,6 +130,15 @@ BotPressSmoke( time ) self maps\bots\_bot_internal::smoke( time ); } +/* + Bot jumps +*/ + +BotJump() +{ + self maps\bots\_bot_internal::jump(); +} + /* Returns the bot's random assigned number. */ @@ -273,7 +282,11 @@ ClearScriptGoal() AtScriptGoal() { - return distanceSquared( self.bot.script_goal, self.origin ) <= self.bot.script_goal_dist * self.bot.script_goal_dist; + if ( !isDefined( self.bot.script_goal ) ) + { + return false; + } + return distanceSquared( self.origin, self.bot.script_goal ) <= ( self.bot.script_goal_dist * self.bot.script_goal_dist ); } /* @@ -1125,7 +1138,7 @@ assign_priority_to_powerup( powerup ) weapons = players[ i ] getWeaponsListPrimaries(); for ( j = 0; j < weapons.size; j++ ) { - if ( self getWeaponAmmoStock( weapons[ j ] ) <= int( weaponmaxammo( weapons[ j ] ) * LOW_AMMO_THRESHOLD ) ) + if ( players[ i ] getWeaponAmmoStock( weapons[ j ] ) <= int( weaponmaxammo( weapons[ j ] ) * LOW_AMMO_THRESHOLD ) ) { priority += 1; break; diff --git a/scripts/sp/killtest.gsc b/scripts/sp/killtest.gsc index 86780af..211d3c4 100644 --- a/scripts/sp/killtest.gsc +++ b/scripts/sp/killtest.gsc @@ -20,6 +20,8 @@ init() if ( getDvar( "killtest_bot_debug" ) == "" ) setDvar( "killtest_bot_debug", 1 ); + setDvar( "bots_skill", 7 ); + level thread addBot(); level thread setupcallbacks();