From 7c9f6fca117345df51cd2277381e0b5ea50109b6 Mon Sep 17 00:00:00 2001 From: ineed bots Date: Thu, 13 Jul 2023 01:52:35 -0600 Subject: [PATCH] new objective system --- maps/bots/_bot_script.gsc | 11 +- maps/bots/_bot_utility.gsc | 2 +- maps/bots/objectives/_manager.gsc | 146 ++++++++++++++++++++++ maps/bots/objectives/_powerup.gsc | 126 +++++++++++++++++++ maps/bots/objectives/_revive.gsc | 201 ++++++++++++++++++++++++++++++ maps/bots/objectives/_utility.gsc | 191 ++++++++++++++++++++++++++++ 6 files changed, 669 insertions(+), 8 deletions(-) create mode 100644 maps/bots/objectives/_manager.gsc create mode 100644 maps/bots/objectives/_powerup.gsc create mode 100644 maps/bots/objectives/_revive.gsc create mode 100644 maps/bots/objectives/_utility.gsc diff --git a/maps/bots/_bot_script.gsc b/maps/bots/_bot_script.gsc index 05239c9..2914648 100644 --- a/maps/bots/_bot_script.gsc +++ b/maps/bots/_bot_script.gsc @@ -7,7 +7,7 @@ */ bot_script_init() { - level thread maps\bots\script_objectives\_obj_init::init(); + level thread maps\bots\objectives\_manager::init(); } /* @@ -31,7 +31,7 @@ connected() self thread onBotSpawned(); self thread onSpawned(); - self thread maps\bots\script_objectives\_obj_init::connected(); + self thread maps\bots\objectives\_manager::connected(); } /* @@ -389,10 +389,7 @@ onSpawned() { self waittill( "spawned_player" ); - self.bot_lock_goal = false; - self.bot_was_follow_script_update = undefined; - - self thread maps\bots\script_objectives\_obj_init::spawned(); + self thread maps\bots\objectives\_manager::spawned(); } } @@ -407,7 +404,7 @@ start_bot_threads() self thread doReloadCancel(); - self thread maps\bots\script_objectives\_obj_init::start_bot_threads(); + self thread maps\bots\objectives\_manager::start_bot_threads(); } /* diff --git a/maps/bots/_bot_utility.gsc b/maps/bots/_bot_utility.gsc index 7244a22..4fdff4e 100644 --- a/maps/bots/_bot_utility.gsc +++ b/maps/bots/_bot_utility.gsc @@ -822,7 +822,7 @@ ReverseHeap( item, item2 ) HeapPriority( item, item2 ) { - return item.priority > item2.priority; + return item.fPriority > item2.fPriority; } /* diff --git a/maps/bots/objectives/_manager.gsc b/maps/bots/objectives/_manager.gsc new file mode 100644 index 0000000..1133ad4 --- /dev/null +++ b/maps/bots/objectives/_manager.gsc @@ -0,0 +1,146 @@ +#include common_scripts\utility; +#include maps\_utility; +#include maps\bots\_bot_utility; +#include maps\bots\objectives\_utility; + +init() +{ + level.bot_objectives = []; + level.bot_objectives[level.bot_objectives.size] = CreateObjectiveForManger( "revive", maps\bots\objectives\_revive::Finder, maps\bots\objectives\_revive::Executer, maps\bots\objectives\_revive::Priority ); + level.bot_objectives[level.bot_objectives.size] = CreateObjectiveForManger( "powerup", maps\bots\objectives\_powerup::Finder, maps\bots\objectives\_powerup::Executer, maps\bots\objectives\_powerup::Priority ); +} + +connected() +{ + self.bot_current_objective = undefined; +} + +spawned() +{ + self.bot_current_objective = undefined; + + self thread clean_objective_on_completion(); + self thread watch_for_objective_canceled(); +} + +watch_for_objective_canceled() +{ + self endon( "disconnect" ); + level endon( "intermission" ); + self endon( "zombified" ); + + for ( ;; ) + { + self waittill( "cancel_bot_objective", reason ); + + obj_name = "undefined"; + + if ( isDefined( self.bot_current_objective ) ) + { + obj_name = self.bot_current_objective.sName; + } + + PrintConsole( "watch_for_objective_canceled: " + self.playername + ": " + obj_name + ": " + reason ); + } +} + +clean_objective_on_completion() +{ + self endon( "disconnect" ); + level endon( "intermission" ); + self endon( "zombified" ); + + for ( ;; ) + { + self waittill( "completed_bot_objective", successful, reason ); + + obj_name = "undefined"; + + if ( isDefined( self.bot_current_objective ) ) + { + obj_name = self.bot_current_objective.sName; + } + + PrintConsole( "clean_objective_on_completion: " + self.playername + ": " + obj_name + ": " + successful + ": " + reason ); + + waittillframeend; + self.bot_current_objective = undefined; + } +} + +start_bot_threads() +{ + self endon( "disconnect" ); + level endon( "intermission" ); + self endon( "zombified" ); + + self thread bot_objective_think(); +} + +bot_objective_think() +{ + self endon( "disconnect" ); + level endon( "intermission" ); + self endon( "zombified" ); + + for ( ;; ) + { + wait 1; + + // find all avail objectives + objectives = []; + + for ( i = 0; i < level.bot_objectives.size; i++ ) + { + objective = level.bot_objectives[i]; + + objectives = array_merge( objectives, self [[objective.fpFinder]]( objective ) ); + } + + if ( objectives.size <= 0 ) + { + continue; + } + + // sort according to priority + heap = NewHeap( ::HeapPriority ); + + for ( i = 0; i < objectives.size; i++ ) + { + heap HeapInsert( objectives[i] ); + } + + // pop the top! + best_prio = heap.data[0]; + + if ( !isDefined( best_prio ) ) + { + continue; + } + + // already on a better obj + if ( self HasBotObjective() && ( best_prio.GUID == self.bot_current_objective.GUID || best_prio.fPriority < self.bot_current_objective.fPriority ) ) + { + continue; + } + + // DO THE OBJ + // cancel the old obj + if ( self HasBotObjective() ) + { + // cancel it + self CancelObjective( "new obj: " + best_prio.sName ); + + // wait for it to clean up + self waittill( "completed_bot_objective" ); + + // redo the loop, should do the obj next iteration + continue; + } + + // ready to execute + PrintConsole( "bot_objective_think: " + self.playername + ": " + best_prio.sName ); + self.bot_current_objective = best_prio; + self thread [[best_prio.eParentObj.fpExecuter]]( best_prio ); + } +} diff --git a/maps/bots/objectives/_powerup.gsc b/maps/bots/objectives/_powerup.gsc new file mode 100644 index 0000000..f327975 --- /dev/null +++ b/maps/bots/objectives/_powerup.gsc @@ -0,0 +1,126 @@ +#include common_scripts\utility; +#include maps\_utility; +#include maps\bots\_bot_utility; +#include maps\bots\objectives\_utility; + +Finder( eObj ) +{ + answer = []; + ents = getentarray( "script_model", "classname" ); + + if ( self inLastStand() ) + { + return Answer; + } + + for ( i = 0; i < ents.size; i++ ) + { + if ( !isDefined( ents[i].powerup_name ) ) + { + continue; + } + + if ( GetPathIsInaccessible( self.origin, ents[i].origin ) ) + { + continue; + } + + if ( self GetBotsAmountForEntity( ents[i] ) >= 1 ) + { + continue; + } + + answer[answer.size] = self CreateFinderObjective( eObj, eObj.sName + "_" + ents[i] GetEntityNumber(), ents[i], self [[eObj.fpPriority]]( eObj, ents[i] ) ); + } + + return answer; +} + +Priority( eObj, eEnt ) +{ + // TODO: check powerup type + base_priority = 0; + base_priority += ClampLerp( Distance( self.origin, eEnt.origin ), 300, 700, 2, -2 ); + + if ( self HasBotObjective() ) + { + base_priority -= 1; + } + + return base_priority; +} + +Executer( eObj ) +{ + self endon( "disconnect" ); + self endon( "zombified" ); + + powerup = eObj.eEnt; + + self thread IncrementBotsForEntity( powerup ); + self thread WatchForCancel( powerup ); + + self GoDoPowerup( eObj ); + + self WatchForCancelCleanup(); + self DecrementBotsForEntity( powerup ); + self ClearScriptGoal(); + + self CompletedObjective( eObj.bWasSuccessful, eObj.sReason ); +} + +WatchForCancelCleanup() +{ + self notify( "WatchForCancelPowerup" ); +} + +WatchForCancel( powerup ) +{ + self endon( "disconnect" ); + self endon( "zombified" ); + self endon( "WatchForCancelPowerup" ); + + for ( ;; ) + { + wait 0.05; + + if ( self inLastStand() ) + { + self CancelObjective( "self inLastStand()" ); + break; + } + + if ( !isdefined( powerup ) ) + { + self CancelObjective( "!isdefined( powerup )" ); + break; + } + } +} + +GoDoPowerup( eObj ) +{ + self endon( "cancel_bot_objective" ); + + powerup = eObj.eEnt; + + // go to it + self SetScriptGoal( powerup.origin, 32 ); + + result = self waittill_any_return( "goal", "bad_path", "new_goal" ); + + if ( result != "goal" ) + { + eObj.sReason = "didn't go to powerup"; + return; + } + + if ( distance( powerup.origin, self.origin ) > 64 ) + { + eObj.sReason = "not touching it"; + return; + } + + eObj.sReason = "completed"; + eObj.bWasSuccessful = true; +} diff --git a/maps/bots/objectives/_revive.gsc b/maps/bots/objectives/_revive.gsc new file mode 100644 index 0000000..5b0e75b --- /dev/null +++ b/maps/bots/objectives/_revive.gsc @@ -0,0 +1,201 @@ +#include common_scripts\utility; +#include maps\_utility; +#include maps\bots\_bot_utility; +#include maps\bots\objectives\_utility; + +Finder( eObj ) +{ + Players = get_players(); + Answer = []; + + if ( self inLastStand() ) + { + return Answer; + } + + for ( i = 0; i < Players.size; i++ ) + { + Player = Players[i]; + + if ( !IsDefined( Player ) || !IsDefined( Player.team ) ) + { + continue; + } + + if ( Player == self ) + { + continue; + } + + if ( Player.sessionstate != "playing" ) + { + continue; + } + + if ( !Player inLastStand() || Player.revivetrigger.beingrevived ) + { + continue; + } + + if ( GetPathIsInaccessible( self.origin, Player.origin ) ) + { + continue; + } + + if ( self GetBotsAmountForEntity( Player ) >= 1 ) + { + continue; + } + + Answer[Answer.size] = self CreateFinderObjective( eObj, eObj.sName + "_" + Player GetEntityNumber(), Player, self [[eObj.fpPriority]]( eObj, Player ) ); + } + + return Answer; +} + +Priority( eObj, eEnt ) +{ + base_priority = 3; + base_priority += ClampLerp( Distance( self.origin, eEnt.origin ), 500, 1200, 2, -2 ); + + if ( self HasBotObjective() ) + { + base_priority -= 1; + } + + return base_priority; +} + +Executer( eObj ) +{ + self endon( "disconnect" ); + self endon( "zombified" ); + + revivee = eObj.eEnt; + + self thread IncrementBotsForEntity( revivee ); + self thread WatchForCancelRevive( revivee ); + + self GoDoRevive( eObj ); + + self WatchForCancelReviveCleanup(); + self DecrementBotsForEntity( revivee ); + self ClearScriptAimPos(); + self ClearScriptGoal(); + self ClearPriorityObjective(); + + self CompletedObjective( eObj.bWasSuccessful, eObj.sReason ); +} + +WatchForCancelReviveCleanup() +{ + self notify( "WatchForCancelRevive" ); +} + +WatchForCancelRevive( revivee ) +{ + self endon( "disconnect" ); + self endon( "zombified" ); + self endon( "WatchForCancelRevive" ); + + for ( ;; ) + { + wait 0.05; + + if ( self inLastStand() ) + { + self CancelObjective( "self inLastStand()" ); + break; + } + + if ( !isdefined( revivee ) || !revivee inLastStand() ) + { + self CancelObjective( "!isdefined( revivee ) || !revivee inLastStand()" ); + break; + } + + if ( revivee.revivetrigger.beingrevived && !self maps\_laststand::is_reviving( revivee ) ) + { + self CancelObjective( "revivee.revivetrigger.beingrevived && !self maps\_laststand::is_reviving( revivee )" ); + break; + } + } +} + +WatchToGoToGuy( revivee ) +{ + self endon( "cancel_bot_objective" ); + self endon( "disconnect" ); + self endon( "zombified" ); + self endon( "goal" ); + self endon( "bad_path" ); + self endon( "new_goal" ); + + for ( ;; ) + { + wait 1; + + if ( self IsTouching( revivee.revivetrigger ) ) + { + self notify( "goal" ); + break; // is this needed? + } + } +} + +WatchForSuccessRevive( eObj ) +{ + self endon( "disconnect" ); + self endon( "zombified" ); + + revivee = eObj.eEnt; + + ret = self waittill_either_return( "cancel_bot_objective", "completed_bot_objective" ); + + if ( ret == "cancel_bot_objective" && isDefined( revivee ) && !revivee inLastStand() ) + { + eObj.bWasSuccessful = true; + eObj.sReason = "revived him!"; + } +} + +GoDoRevive( eObj ) +{ + self endon( "cancel_bot_objective" ); + + revivee = eObj.eEnt; + + // go to guy + self thread WatchToGoToGuy( revivee ); + self SetPriorityObjective(); + self SetScriptGoal( revivee.origin, 32 ); + + result = self waittill_any_return( "goal", "bad_path", "new_goal" ); + + if ( result != "goal" ) + { + eObj.sReason = "didn't go to guy"; + return; + } + + if ( !self IsTouching( revivee.revivetrigger ) ) + { + eObj.sReason = "not touching guy"; + return; + } + + // ok we are touching guy, lets look at him + self SetScriptAimPos( revivee.origin ); + + // now lets hold use until he is up or otherwise + self thread WatchForSuccessRevive( eObj ); + + while ( self IsTouching( revivee.revivetrigger ) ) + { + self thread BotPressUse( 0.15 ); + + wait 0.1; + } + + eObj.sReason = "not touching guy"; +} diff --git a/maps/bots/objectives/_utility.gsc b/maps/bots/objectives/_utility.gsc new file mode 100644 index 0000000..6fcebbf --- /dev/null +++ b/maps/bots/objectives/_utility.gsc @@ -0,0 +1,191 @@ +#include common_scripts\utility; +#include maps\_utility; +#include maps\bots\_bot_utility; + +CreateObjectiveForManger( sName, fpFinder, fpExecuter, fpPriority ) +{ + Answer = SpawnStruct(); + + Answer.sName = sName; + Answer.fpFinder = fpFinder; + Answer.fpExecuter = fpExecuter; + + if ( !IsDefined( fpPriority ) ) + { + Answer.fpPriority = ::DefaultPriority; + } + else + { + Answer.fpPriority = fpPriority; + } + + return Answer; +} + +CreateFinderObjective( eObj, sName, eEnt, fPriority ) +{ + Answer = SpawnStruct(); + + Answer.eParentObj = eObj; + Answer.sName = sName; + Answer.eEnt = eEnt; + Answer.fPriority = fPriority; + Answer.GUID = eEnt GetEntityNumber(); + + Answer.bWasSuccessful = false; + Answer.sReason = "canceled"; + + return Answer; +} + +DefaultPriority( eObj, eEnt ) +{ + return 0; +} + +/* + Checks whether the path generated by the ASTAR path finding is inaccessible +*/ +GetPathIsInaccessible( from, to, team, best_effort ) +{ + if ( isDefined( best_effort ) ) + { + path = generatePath( from, to, team, level.bot_allowed_negotiation_links, best_effort ); + } + else + { + path = generatePath( from, to, team, level.bot_allowed_negotiation_links ); + } + + return ( !isDefined( path ) || ( path.size <= 0 ) ); +} + +get_path_dist( start, end, team ) +{ + path = generatePath( start, end, team, level.bot_allowed_negotiation_links, 192.0 ); + + if ( !isDefined( path ) || path.size <= 0 ) + { + return 999999999; + } + + dist = 0; + prev_node = undefined; + + for ( i = 0; i < path.size; i++ ) + { + if ( i == 0 ) + { + prev_node = path[ i ]; + continue; + } + + dist += distance( prev_node.origin, path[ i ].origin ); + prev_node = path[ i ]; + } + + return dist; +} + +ClampLerp( dist, min_dist, max_dist, max_bonus, min_bonus ) +{ + answer = 0; + + if ( dist <= min_dist ) + { + answer += max_bonus; + } + else if ( dist <= max_dist ) + { + answer += min_bonus; + } + else + { + dist_multi = 1 - ( ( dist - min_dist ) / ( max_dist - min_dist ) ); + answer += min_bonus + ( ( max_bonus - min_bonus ) * dist_multi ); + } + + return answer; +} + +GetBotsAmountForEntity( eEnt ) +{ + if ( !isDefined( eEnt.bots ) ) + { + eEnt.bots = 0; + } + + return eEnt.bots; +} + +IncrementBotsForEntity( eEnt ) +{ + self endon( "bots_for_entity_cleanup" ); + + eEnt.bots++; + + self waittill_either( "disconnect", "zombified" ); + + if ( isDefined( eEnt ) ) + { + eEnt.bots--; + } +} + +DecrementBotsForEntity( eEnt ) +{ + self notify( "bots_for_entity_cleanup" ); + + if ( isDefined( eEnt ) ) + { + eEnt.bots--; + } +} + +CleanupBotsForEntity( eEnt ) +{ + self notify( "bots_for_entity_cleanup" ); +} + +CancelObjective( reason ) +{ + self notify( "cancel_bot_objective", reason ); +} + +CompletedObjective( successful, reason ) +{ + self notify( "completed_bot_objective", successful, reason ); +} + +HasBotObjective() +{ + return isDefined( self.bot_current_objective ); +} + +get_angle_offset_node( forward_size, angle_offset, offset ) +{ + if ( !isDefined( forward_size ) ) + { + forward_size = 40; + } + if ( !isDefined( angle_offset ) ) + { + angle_offset = ( 0, 0, 0 ); + } + if ( !isDefined( offset ) ) + { + offset = ( 0, 0, 0 ); + } + + angles = ( 0, self.angles[ 1 ], 0 ); + angles += angle_offset; + node = self.origin + ( AnglesToForward( angles ) * forward_size ) + offset; + node = clamp_to_ground( node ); + return node; +} + +clamp_to_ground( org ) +{ + trace = playerPhysicsTrace( org + ( 0, 0, 20 ), org - ( 0, 0, 2000 ) ); + return trace; +}