From da170b8f9e06dab2b018102dc41f7c5c12414b13 Mon Sep 17 00:00:00 2001 From: JezuzLizard Date: Mon, 1 May 2023 04:45:26 -0700 Subject: [PATCH] Prototype for bot objective behavior. --- maps/bots/_bot.gsc | 28 ++ maps/bots/_bot_script.gsc | 753 ++++++++++++++++++++++++++++++++++++- maps/bots/_bot_utility.gsc | 179 +++++++++ 3 files changed, 959 insertions(+), 1 deletion(-) diff --git a/maps/bots/_bot.gsc b/maps/bots/_bot.gsc index 4c50df5..5006295 100644 --- a/maps/bots/_bot.gsc +++ b/maps/bots/_bot.gsc @@ -111,6 +111,34 @@ init() level thread onPlayerConnect(); level thread handleBots(); + + maps\_bot_script::register_bot_action( "objective", "powerup", maps\_bot_script::bot_grab_powerup, + maps\_bot_script::bot_powerup_process_order, + maps\_bot_script::bot_should_grab_powerup, + maps\_bot_script::bot_check_complete_grab_powerup, + maps\_bot_script::bot_set_complete_grab_powerup, + maps\_bot_script::bot_powerup_on_completion, + maps\_bot_script::bot_powerup_should_cancel, + maps\_bot_script::bot_powerup_on_cancel, + maps\_bot_script::bot_powerup_should_postpone, + maps\_bot_script::bot_powerup_on_postpone, + maps\_bot_script::bot_powerup_priority ); + + maps\_bot_script::register_bot_action( "objective", "revive", maps\_bot_script::bot_revive_player, + maps\_bot_script::bot_revive_process_order, + maps\_bot_script::bot_should_revive_player, + maps\_bot_script::bot_check_complete_revive_player, + maps\_bot_script::bot_set_complete_revive_player, + maps\_bot_script::bot_revive_player_on_completion, + maps\_bot_script::bot_revive_player_should_cancel, + maps\_bot_script::bot_revive_player_on_cancel, + maps\_bot_script::bot_revive_player_should_postpone, + maps\_bot_script::bot_revive_player_on_postpone, + maps\_bot_script::bot_revive_player_priority ); + maps\_bot_script::register_bot_objective( "powerup" ); + + level thread maps\_bot_script::store_powerups_dropped(); + level thread maps\_bot_script::watch_for_downed_players(); } /* diff --git a/maps/bots/_bot_script.gsc b/maps/bots/_bot_script.gsc index d004e6e..7cba1f2 100644 --- a/maps/bots/_bot_script.gsc +++ b/maps/bots/_bot_script.gsc @@ -356,7 +356,7 @@ onSpawned() for ( ;; ) { self waittill( "spawned_player" ); - + self thread bot_action_think(); self.bot_lock_goal = false; self.bot_was_follow_script_update = undefined; } @@ -371,3 +371,754 @@ start_bot_threads() level endon( "intermission" ); self endon( "zombified" ); } + +register_bot_objective( objective_group ) +{ + if ( !isDefined( level.zbot_objective_glob ) ) + { + level.zbot_objective_glob = []; + } + if ( !isDefined( level.zbot_objective_glob[ objective_group ] ) ) + { + level.zbot_objective_glob[ objective_group ] = spawnStruct(); + level.zbot_objective_glob[ objective_group ].active_objectives = []; + } +} + +add_possible_bot_objective( objective_group, id, is_global_shared, target_ent ) +{ + assert( isDefined( level.zbot_objective_glob ), "Trying to add objective before calling register_bot_objective" ); + + assert( isDefined( level.zbot_objective_glob[ objective_group ] ), "Trying to add objective to group " + objective_group + " before calling register_bot_objective" ); + + objective_struct = spawnStruct(); + objective_struct.group = objective_group; + objective_struct.id = id; + objective_struct.is_global_shared = is_global_shared; + objective_struct.target_ent = target_ent; + objective_struct.owner = undefined; + objective_struct.is_objective = true; + + level.zbot_objective_glob[ objective_group ].active_objectives[ "obj_id_" + id ] = objective_struct; +} + +get_bot_objective_by_id( objective_group, id ) +{ + active_objectives = level.zbot_objective_glob[ objective_group ].active_objectives; + + objective = active_objectives[ "obj_id_" + id ]; + + assert( isDefined( objective ), "Objective with " + id + " id does not point to a objective in group " + objective_group ); + + return objective; +} + +get_all_objectives_for_group( objective_group ) +{ + return level.zbot_objective_glob[ objective_group ].active_objectives; +} + +set_objective_for_bot( objective_group, id ) +{ + possible_objectives = level.zbot_objective_glob[ objective_group ].active_objectives; + + objective = possible_objectives[ "obj_id_" + id ]; + + objective_exists = isDefined( objective ); + + assert( objective_exists, "Objective with " + id + " id does not point to a objective in group " + objective_group ); + if ( !objective_exists ) + { + return; + } + + self.zbot_current_objective = objective; +} + +clear_objective_for_bot() +{ + self.zbot_current_objective = undefined; +} + +set_bot_objective_blocked_by_objective( primary_objective_group, primary_id, blocked_by_objective_group, blocked_by_id ) +{ + primary_active_objectives = level.zbot_objective_glob[ primary_objective_group ].active_objectives; + + primary_objective = primary_active_objectives[ "obj_id_" + primary_id ]; + + primary_objective_exists = isDefined( primary_objective ); + + assert( primary_objective_exists, "Objective with " + primary_id + " id does not point to a objective in group " + primary_objective_group ); + if ( !primary_objective_exists ) + { + return; + } + if ( primary_objective_group == blocked_by_objective_group ) + { + assert( primary_id != blocked_by_id, "Objective with " + primary_id + " id should not be the same as the blocked_by_id if the objectives are in the same group of " + primary_objective_group ); + if ( primary_id == blocked_by_id ) + { + return; + } + + blocking_objective = primary_active_objectives[ "obj_id_" + blocked_by_id ]; + + blocking_objective_exists = isDefined( blocking_objective ); + + assert( blocking_objective_exists, "Objective with " + blocked_by_id + " id does not point to a objective in group " + blocked_by_objective_group ); + if ( !blocking_objective_exists ) + { + return; + } + + primary_objective.blocking_objective = blocking_objective; + } + else + { + secondary_active_objectives = level.zbot_objective_glob[ blocked_by_objective_group ].active_objectives; + + blocking_objective = secondary_active_objectives[ "obj_id_" + blocked_by_id ]; + + blocking_objective_exists = isDefined( blocking_objective ); + + assert( blocking_objective_exists, "Objective with " + blocked_by_id + " id does not point to a objective in group " + blocked_by_objective_group ); + if ( !blocking_objective_exists ) + { + return; + } + + primary_objective.blocking_objective = blocking_objective; + } +} + +set_bot_global_shared_objective_owner_by_id( objective_group, id, new_owner ) +{ + 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; + } + + 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; + } + + objective.owner = new_owner; +} + +set_bot_global_shared_objective_owner_by_reference( objective_group, objective, new_owner ) +{ + is_objective = isDefined( objective.is_objective ); + assert( is_objective, "Objective arg is not a valid objective object" ); + if ( !is_objective ) + { + return; + } + assert( objective.is_global_shared, "Objective with " + objective.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; + } + + objective.owner = new_owner; +} + +free_bot_objective( objective_group, id ) +{ + active_objectives = level.zbot_global_shared_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; + } + + players = getPlayers(); + for ( i = 0; i < players.size; i++ ) + { + if ( players[ i ].pers[ "isBot" ] ) + { + if ( players[ i ].zbot_current_objective == objective ) + { + players[ i ].zbot_current_objective = undefined; + } + } + } + + objective = undefined; +} + +register_bot_action( group_name, action_name, action_func, action_process_order_func, should_do_func, check_if_complete_func, set_complete_func, on_completion_func, should_cancel_func, on_cancel_func, should_postpone_func, on_postpone_func, priority_func ) +{ + if ( !isDefined( level.zbots_actions ) ) + { + level.zbots_actions = []; + } + if ( !isDefined( level.zbots_actions[ group_name ] ) ) + { + level.zbots_actions[ group_name ] = []; + } + if ( !isDefined( level.zbots_actions[ group_name ][ action_name ] ) ) + { + level.zbots_actions[ group_name ][ action_name ] = spawnStruct(); + } + level.zbots_actions[ group_name ][ action_name ].action = action_func; + level.zbots_actions[ group_name ][ action_name ].should_do_func = should_do_func; + level.zbots_actions[ group_name ][ action_name ].action_process_order_func = action_process_order_func; + level.zbots_actions[ group_name ][ action_name ].check_if_complete_func = check_if_complete_func; + level.zbots_actions[ group_name ][ action_name ].set_complete_func = set_complete_func; + level.zbots_actions[ group_name ][ action_name ].on_completion_func = on_completion_func; + level.zbots_actions[ group_name ][ action_name ].should_cancel_func = should_cancel_func; + level.zbots_actions[ group_name ][ action_name ].on_cancel_func = on_cancel_func; + level.zbots_actions[ group_name ][ action_name ].should_postpone_func = should_postpone_func; + level.zbots_actions[ group_name ][ action_name ].on_postpone_func = on_postpone_func; + level.zbots_actions[ group_name ][ action_name ].priority_func = priority_func; +} + +initialize_bot_actions_queue() +{ + group_keys = getArrayKeys( level.zbots_actions ); + for ( i = 0; i < group_keys.size; i++ ) + { + action_keys = getArrayKeys( level.zbots_actions[ group_keys[ i ] ] ); + for ( j = 0; j < action_keys.size; j++ ) + { + self register_bot_objective_action_for_queue( group_keys[ i ], action_keys[ j ] ); + } + } +} + +register_bot_objective_action_for_queue( group_name, action_name ) +{ + if ( !isDefined( self.zbot_actions_in_queue ) ) + { + self.zbot_actions_in_queue = []; + } + if ( !isDefined( self.zbot_actions_in_queue[ group_name ] ) ) + { + self.zbot_actions_in_queue[ group_name ] = []; + } + if ( !isDefined( self.zbot_actions_in_queue[ group_name ][ action_name ] ) ) + { + self.zbot_actions_in_queue[ group_name ][ action_name ] = spawnStruct(); + } + self.zbot_actions_in_queue[ group_name ][ action_name ].postponed = false; + self.zbot_actions_in_queue[ group_name ][ action_name ].canceled = false; + self.zbot_actions_in_queue[ group_name ][ action_name ].queued = false; + self.zbot_actions_in_queue[ group_name ][ action_name ].completed = false; + self.zbot_actions_in_queue[ group_name ][ action_name ].is_current = false; +} + +process_next_queued_action( group_name ) +{ + if ( self.zbot_actions_in_queue[ group_name ][ self.action_queue[ group_name ][ 0 ].action_name ].queued ) + { + return; + } + + self.action_queue[ group_name ] = self sort_array_by_priority_field( self.action_queue[ group_name ] ); + + self thread [[ self.action_queue[ group_name ][ 0 ].action ]](); + + self.zbot_actions_in_queue[ group_name ][ self.action_queue[ group_name ][ 0 ].action_name ].is_current = true; + + self thread wait_for_action_completion( group_name, self.action_queue[ group_name ][ 0 ].action_name ); +} + +wait_for_action_completion( group_name, action_name ) +{ + self endon( "disconnect" ); + self endon( "stop_action_think" ); + level endon( "end_game" ); + + action_complete_name = action_name + "_complete"; + action_cancel_name = action_name + "_cancel"; + action_postpone_name = action_name + "_postpone"; + + result = self waittill_any_return( action_complete_name, action_cancel_name, action_postpone_name ); + if ( ( result == action_complete_name ) ) + { + self.zbot_actions_in_queue[ group_name ][ action_name ].postponed = false; + self.zbot_actions_in_queue[ group_name ][ action_name ].queued = false; + self.zbot_actions_in_queue[ group_name ][ action_name ].completed = false; + self.action_queue[ group_name ][ 0 ] = undefined; + self thread [[ self.action_queue[ group_name ][ 0 ].on_completion_func ]](); + } + else if ( result == action_cancel_name ) + { + self.zbot_actions_in_queue[ group_name ][ action_name ].postponed = false; + self.zbot_actions_in_queue[ group_name ][ action_name ].queued = false; + self.zbot_actions_in_queue[ group_name ][ action_name ].completed = false; + self.action_queue[ group_name ][ 0 ] = undefined; + self thread [[ self.action_queue[ group_name ][ 0 ].on_cancel_func ]](); + } + else if ( result == action_postpone_name ) + { + self.zbot_actions_in_queue[ group_name ][ action_name ].postponed = true; + postponed_action = self.action_queue[ group_name ][ 0 ]; + self.action_queue[ group_name ][ 0 ] = undefined; + postponed_action.priority = self [[ level.zbots_actions[ group_name ][ action_name ].priority_func ]](); + self.action_queue[ group_name ] = array_insert( self.action_queue[ group_name ], postponed_action, 1 ); + self thread [[ self.action_queue[ group_name ][ 0 ].on_postpone_func ]](); + } + + self notify( action_name + "_end_think" ); + + self.zbot_actions_in_queue[ group_name ][ action_name ].is_current = false; +} + +copy_default_action_settings_to_queue( group_name, action_name ) +{ + //self.group = level.zbots_actions[ group_name ][ action_name ].group; + self.action = level.zbots_actions[ group_name ][ action_name ].action; + //self.should_do_func = level.zbots_actions[ group_name ][ action_name ].should_do_func; + self.on_completion_func = level.zbots_actions[ group_name ][ action_name ].on_completion_func; + self.should_cancel_func = level.zbots_actions[ group_name ][ action_name ].should_cancel_func; + self.on_cancel_func = level.zbots_actions[ group_name ][ action_name ].on_cancel_func; + self.should_postpone_func = level.zbots_actions[ group_name ][ action_name ].should_postpone_func; + self.on_postpone_func = level.zbots_actions[ group_name ][ action_name ].on_postpone_func; + self.priority_func = level.zbots_actions[ group_name ][ action_name ].priority_func; +} + +pick_actions_to_add_to_queue( group_name ) +{ + action_keys = getArrayKeys( level.zbots_actions[ group_name ] ); + + //TODO: Use process order funcs to determine the order of actions being added to the queue + //For now just randomize the order of the keys + /* + for ( i = 0; i < action_keys; i++ ) + { + + } + */ + + if ( !isDefined( self.action_id ) ) + { + self.action_id = 0; + } + + for ( i = 0; i < action_keys.size; i++ ) + { + if ( !self.zbot_actions_in_queue[ group_name ][ action_keys[ i ] ].queued && [[ level.zbots_actions[ group_name ][ action_keys[ i ] ].should_do_func ]]() ) + { + self.action_queue[ group_name ][ self.action_queue[ group_name ].size ] = spawnStruct(); + self.action_queue[ group_name ][ self.action_queue[ group_name ].size - 1 ].action_name = action_keys[ i ]; + self.action_queue[ group_name ][ self.action_queue[ group_name ].size - 1 ].action_id = self.action_id; + self.action_queue[ group_name ][ self.action_queue[ group_name ].size - 1 ].priority = self [[ level.zbots_actions[ group_name ][ action_keys[ i ] ].priority_func ]](); + self.zbot_actions_in_queue[ group_name ][ action_keys[ i ] ].queued = true; + self.action_id++; + } + } +} + +bot_clear_actions_queue() +{ + group_keys = getArrayKeys( level.zbots_actions ); + for ( i = 0; i < group_keys.size; i++ ) + { + self.action_queue[ group_keys[ i ] ] = []; + action_keys = getArrayKeys( level.zbots_actions[ group_keys[ i ] ] ); + for ( j = 0; j < action_keys.size; j++ ) + { + self register_bot_objective_action_for_queue( group_keys[ i ], action_keys[ j ] ); + } + } +} + +check_if_action_is_completed_in_group( group_name ) +{ + if ( [[ level.zbots_actions[ group_name ][ self.action_queue[ group_name ][ 0 ].action_name ].check_if_complete_func ]]() ) + { + self notify( self.action_queue[ group_name ][ 0 ].action_name + "_complete" ); + } +} + +check_if_action_should_be_postponed_in_group( group_name ) +{ + if ( [[ level.zbots_actions[ group_name ][ self.action_queue[ group_name ][ 0 ].action_name ].should_postpone_func ]]() ) + { + self notify( self.action_queue[ group_name ][ 0 ].action_name + "_postpone" ); + } +} + +check_if_action_should_be_canceled_in_group( group_name ) +{ + if ( [[ level.zbots_actions[ group_name ][ self.action_queue[ group_name ][ 0 ].action_name ].should_cancel_func ]]() ) + { + self notify( self.action_queue[ group_name ][ 0 ].action_name + "_cancel" ); + } +} + +check_if_action_should_be_postponed_globally( group_name ) +{ + if ( action_should_be_postponed_global( group_name, self.action_queue[ group_name ][ 0 ].action_name ) ) + { + self notify( self.action_queue[ group_name ][ 0 ].action_name + "_postpone" ); + } +} + +check_if_action_should_be_canceled_globally( group_name ) +{ + if ( action_should_be_canceled_global( group_name, self.action_queue[ group_name ][ 0 ].action_name ) ) + { + self notify( self.action_queue[ group_name ][ 0 ].action_name + "_cancel" ); + } +} + +//TODO: Figure out way of overriding the current action for flee movement action +check_for_forced_action( group_name ) +{ + action_keys = getArrayKeys( level.zbots_actions[ group_name ] ); + action_priorities_array = []; + for ( i = 0; i < action_keys.size; i++ ) + { + action_priorities_array[ action_priorities_array.size ] = spawnStruct(); + action_priorities_array[ action_priorities_array.size - 1 ].priority = self [[ level.zbots_actions[ group_name ][ action_keys[ i ] ].priority_func ]](); + action_priorities_array[ action_priorities_array.size - 1 ].action_name = action_keys[ i ]; + } + + action_priorities_array = sort_array_by_priority_field( action_priorities_array ); + + if ( self.action_queue[ group_name ][ 0 ].priority < action_priorities_array[ 0 ].priority ) + { + self notify( self.action_queue[ group_name ][ 0 ].action_name + "_cancel" ); + } +} + +bot_action_think() +{ + self endon( "disconnect" ); + self endon( "zombified" ); + + while ( true ) + { + wait 0.05; + + group_name = "objective"; + + self pick_actions_to_add_to_queue( group_name ); + + //self check_for_forced_action( group_name ); + + if ( self.action_queue[ group_name ].size <= 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 ); + + self check_if_action_should_be_postponed_globally( group_name ); + self check_if_action_should_be_canceled_globally( group_name ); + } +} + +action_should_be_postponed_global( primary_group_name, action_name ) +{ + return false; +} + +action_should_be_canceled_global( primary_group_name, action_name ) +{ + return false; +} + +//TODO: Add ability to pause an action so the bot won't be doing it while its paused but when its unpaused they can resume the action with the same settings +//Similar to postpone except instead of selecting a new action the current action is preserved +action_should_be_paused_global( primary_group_name, action_name ) +{ + return false; +} + +bot_grab_powerup() +{ + self endon( "powerup_end_think" ); + + if ( !isDefined( self.available_powerups ) || self.available_powerups.size <= 0 ) + { + return; + } + set_bot_global_shared_objective_owner_by_reference( "powerup", self.available_powerups[ 0 ], self ); + while ( true ) + { + self SetScriptGoal( self.available_powerups[ 0 ].target_ent.origin ); + wait 0.05; + } +} + +bot_powerup_process_order() +{ + return 0; +} + +bot_should_grab_powerup() +{ + if ( level.zbot_objective_glob[ "powerup" ].active_objectives.size <= 0 ) + { + return false; + } + MAX_DISTANCE_SQ = 10000 * 10000; + BOT_SPEED_WHILE_SPRINTING_SQ = 285 * 285; + self.available_powerups = []; + + powerup_objectives = level.zbot_objective_glob[ "powerup" ].active_objectives; + obj_keys = getArrayKeys( powerup_objectives ); + for ( i = 0; i < powerup_objectives.size; i++ ) + { + obj = powerup_objectives[ obj_keys[ i ] ]; + powerup = obj.target_ent; + if ( isDefined( obj.owner ) ) + { + continue; + } + time_left = powerup.time_left_until_timeout; + distance_required_to_reach_powerup = distanceSquared( powerup.origin, self.origin ); + if ( distance_required_to_reach_powerup > BOT_SPEED_WHILE_SPRINTING_SQ * time_left ) + { + continue; + } + if ( distanceSquared( powerup.origin, self.origin ) > MAX_DISTANCE_SQ ) + { + continue; + } + if ( !isDefined( generatePath( self.origin, powerup.origin, self.team, false ) ) ) + { + continue; + } + self.available_powerups[ self.available_powerups.size ] = obj; + } + + //TODO: Sort powerups by priority here + return self.available_powerups.size > 0; +} + +bot_check_complete_grab_powerup() +{ + if ( self.successfully_grabbed_powerup ) + { + return true; + } + return false; +} + +bot_set_complete_grab_powerup() +{ + +} + +bot_powerup_on_completion() +{ + self.successfully_grabbed_powerup = false; +} + +bot_powerup_should_cancel() +{ + return ( !isDefined( self.available_powerups ) || self.available_powerups.size <= 0 ); +} + +bot_powerup_on_cancel() +{ + +} + +bot_powerup_should_postpone() +{ + return false; +} + +bot_powerup_on_postpone() +{ + +} + +bot_powerup_priority() +{ + if ( !isDefined( self.available_powerups ) ) + { + return 0; + } + return self.available_powerups[ 0 ].target_ent.priority; +} + +bot_revive_player() +{ + if ( !isDefined( self.available_revives ) || self.available_revives.size <= 0 ) + { + return; + } + + self endon( "disconnect" ); + level endon( "end_game" ); + + player_to_revive_obj = self.available_revives[ 0 ]; + + set_bot_global_shared_objective_owner_by_reference( "revive", player_to_revive_obj, self ); + + player_to_revive = player_to_revive_obj.target_ent; + + action_id = self.action_queue[ "objective" ][ 0 ].action_id; + + //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 ) && isDefined( self.action_queue[ "objective" ][ 0 ] ) && action_id == self.action_queue[ "objective" ][ 0 ].action_id ) + { + self.target_pos = player_to_revive.origin; + + if ( self.can_do_objective_now ) + { + //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? + } + wait 0.2; + } +} + +bot_revive_process_order() +{ + return 0; +} + +bot_should_revive_player() +{ + downed_players_objs = get_all_objectives_for_group( "revive" ); + if ( downed_players_objs.size <= 0 ) + { + return false; + } + + self.available_revives = []; + + obj_keys = getArrayKeys( downed_players_objs ); + for ( i = 0; i < downed_players_objs.size; i++ ) + { + if ( isDefined( downed_players_objs[ obj_keys[ i ] ].owner ) ) + { + continue; + } + + self.available_revives[ self.available_revives.size ] = downed_players_objs[ obj_keys[ i ] ]; + } + return self.available_revives.size > 0; +} + +bot_check_complete_revive_player() +{ + if ( self.successfully_revived_player ) + { + return true; + } + return false; +} + +bot_set_complete_revive_player() +{ + self.successfully_revived_player = true; +} + +bot_revive_player_on_completion() +{ + self.successfully_revived_player = false; +} + +bot_revive_player_should_cancel() +{ + return !isDefined( self.available_revives[ 0 ].target_ent.revivetrigger ); +} + +bot_revive_player_on_cancel() +{ + +} + +bot_revive_player_should_postpone() +{ + return false; +} + +bot_revive_player_on_postpone() +{ + +} + +bot_revive_player_priority() +{ + return 0; +} + +store_powerups_dropped() +{ + level endon( "end_game" ); + + level thread free_powerups_dropped(); + + level.zbots_powerups = []; + id = 0; + while ( true ) + { + level waittill( "powerup_dropped", powerup ); + if ( !isDefined( powerup ) ) + { + continue; + } + powerup.id = id; + add_possible_bot_objective( "powerup", id, true, powerup ); + level thread objective_think( "powerup", id ); + scripts\sp\bots\bot_utility::assign_priority_to_powerup( powerup ); + level.zbots_powerups = scripts\sp\bots\bot_utility::sort_array_by_priority_field( level.zbots_powerups, powerup ); + id++; + } +} + +free_powerups_dropped() +{ + level endon( "end_game" ); + + while ( true ) + { + level waittill( "powerup_freed", powerup ); + free_bot_objective( "powerup", powerup.id ); + } +} + +watch_for_downed_players() +{ + level endon( "end_game" ); + + while ( true ) + { + level waittill( "player_entered_laststand", player ); + if ( !isDefined( player ) ) + { + continue; + } + add_possible_bot_objective( "revive", player.client_id, true, player ); + player thread free_revive_objective_when_needed(); + } +} + +free_revive_objective_when_needed() +{ + level endon( "end_game" ); + + id = self.id; + while ( isDefined( self ) && isDefined( self.revivetrigger ) ) + { + wait 0.05; + } + + free_bot_objective( "revive", id ); +} \ No newline at end of file diff --git a/maps/bots/_bot_utility.gsc b/maps/bots/_bot_utility.gsc index a3d4bc6..f3aa1c6 100644 --- a/maps/bots/_bot_utility.gsc +++ b/maps/bots/_bot_utility.gsc @@ -1008,3 +1008,182 @@ targetIsGibbed() { return isDefined( self.gibbed ) && self.gibbed; } + +quickSort(array, compare_func) +{ + return quickSortMid(array, 0, array.size - 1, compare_func); +} + +quickSortMid( array, start, end, compare_func, compare_func_arg1 ) +{ + i = start; + k = end; + + if(!IsDefined(compare_func)) + compare_func = ::quicksort_compare; + + if (end - start >= 1) + { + pivot = array[start]; + + while (k > i) + { + while ( [[ compare_func ]](array[i], pivot, compare_func_arg1) && i <= end && k > i) + i++; + while ( ![[ compare_func ]](array[k], pivot, compare_func_arg1) && k >= start && k >= i) + k--; + if (k > i) + array = swap(array, i, k); + } + array = swap(array, start, k); + array = quickSortMid(array, start, k - 1, compare_func); + array = quickSortMid(array, k + 1, end, compare_func); + } + else + return array; + + return array; +} + +quicksort_compare(left, right, compare_func_arg1) +{ + return left <= right; +} + +quicksort_compare_priority_field(left, right, compare_func_arg1) +{ + return left.priority <= right.priority; +} + +quicksort_compare_pers_value_highest_to_lowest( left, right, compare_func_arg1 ) +{ + return left.pers[ compare_func_arg1 ] <= right.pers[ compare_func_arg1 ]; +} + +quicksort_compare_pers_value_lowest_to_highest( left, right, compare_func_arg1 ) +{ + return left.pers[ compare_func_arg1 ] >= right.pers[ compare_func_arg1 ]; +} + +assign_priority_to_powerup( powerup ) +{ + if ( !isDefined( powerup ) ) + { + return; + } + priority = 0; + powerup_is_max_ammo = false; + switch ( powerup.powerup_name ) + { + case "zombie_blood": + case "insta_kill": + case "nuke": + priority += 2; + break; + case "full_ammo": + powerup_is_max_ammo = true; + priority += 1; + break; + case "double_points": + case "fire_sale": + case "carpenter": + case "free_perk": + priority += 1; + break; + default: + priority += 0; + break; + } + if ( powerup_is_max_ammo ) + { + LOW_AMMO_THRESHOLD = 0.3; + + for ( i = 0; i < level.players.size; i++ ) + { + weapons = level.players[ i ] getWeaponsListPrimaries(); + for ( j = 0; j < weapons.size; j++ ) + { + if ( self getWeaponAmmoStock( weapons[ j ] ) <= int( weaponmaxammo( weapons[ j ] ) * LOW_AMMO_THRESHOLD ) ) + { + priority += 1; + break; + } + } + if ( priority > 3 ) + { + break; + } + } + } + + if ( maps\_laststand::player_any_player_in_laststand() ) + { + switch ( powerup.powerup_name ) + { + case "zombie_blood": + case "insta_kill": + case "nuke": + priority += 1; + break; + case "full_ammo": + priority += 0; + break; + case "double_points": + case "fire_sale": + case "carpenter": + case "free_perk": + priority -= 1; + break; + default: + priority += 0; + break; + } + } + + if ( powerup.time_left_until_timeout < 10.0 ) + { + priority += 1; + } + if ( priority < 0 ) + { + priority = 0; + } + powerup.priority = priority; +} + +sort_array_by_priority_field( array, item ) +{ + if ( isDefined( item ) ) + { + array[ array.size ] = item; + } + + array = quickSort( array, ::quicksort_compare_priority_field, undefined ); + return array; +} + +get_players_sorted_by_highest_pers_value( pers_name ) +{ + players = getPlayers(); + + if ( !isDefined( players[ 0 ].pers[ pers_name ] ) ) + { + assertMsg( "Uninitialized pers value: " + pers_name ); + return undefined; + } + + return quickSort( players, ::quicksort_compare_pers_value_highest_to_lowest, pers_name ); +} + +get_players_sorted_by_lowest_pers_value( pers_name ) +{ + players = getPlayers(); + + if ( !isDefined( players[ 0 ].pers[ pers_name ] ) ) + { + assertMsg( "Uninitialized pers value: " + pers_name ); + return undefined; + } + + return quickSort( players, ::quicksort_compare_pers_value_lowest_to_highest, pers_name ); +} \ No newline at end of file