aboutsummaryrefslogtreecommitdiff
path: root/Northstar.CustomServers/scripts/vscripts/ai/_ai_stationary_firing_positions.gnut
diff options
context:
space:
mode:
Diffstat (limited to 'Northstar.CustomServers/scripts/vscripts/ai/_ai_stationary_firing_positions.gnut')
-rw-r--r--Northstar.CustomServers/scripts/vscripts/ai/_ai_stationary_firing_positions.gnut261
1 files changed, 261 insertions, 0 deletions
diff --git a/Northstar.CustomServers/scripts/vscripts/ai/_ai_stationary_firing_positions.gnut b/Northstar.CustomServers/scripts/vscripts/ai/_ai_stationary_firing_positions.gnut
new file mode 100644
index 000000000..50b6cc759
--- /dev/null
+++ b/Northstar.CustomServers/scripts/vscripts/ai/_ai_stationary_firing_positions.gnut
@@ -0,0 +1,261 @@
+global function AddStationaryAIPosition //Add stationary positions to pending list.
+global function AddTestTargetPosForStationaryPositionValidation //Add test target location for validating stationary positions.
+global function ValidateAndFinalizePendingStationaryPositions //Runs error-checking/validation logic on stationary positions and finalizes them for use by AI.
+global function GetRandomStationaryPosition
+global function GetClosestAvailableStationaryPosition
+global function ClaimStationaryAIPosition
+global function ReleaseStationaryAIPosition
+
+global enum eStationaryAIPositionTypes
+{
+ MORTAR_TITAN,
+ MORTAR_SPECTRE,
+ SNIPER_TITAN,
+ LAUNCHER_REAPER
+}
+
+global struct StationaryAIPosition
+{
+ vector origin
+ bool inUse
+}
+
+global struct ArrayDistanceEntryForStationaryAIPosition
+{
+ float distanceSqr
+ StationaryAIPosition& ent
+ vector origin
+}
+
+struct
+{
+ array<vector> validationTestTargets
+ table<int, array<vector> > pendingPositions
+ table<int, array<StationaryAIPosition> > stationaryPositions
+} file
+
+void function AddTestTargetPosForStationaryPositionValidation( vector origin )
+{
+ file.validationTestTargets.append( origin )
+}
+
+void function AddStationaryAIPosition( vector origin, int type )
+{
+ AddPendingStationaryAIPosition_Internal( origin, type )
+}
+
+void function AddStationaryAIPosition_Internal( vector origin, int type )
+{
+ StationaryAIPosition pos
+ pos.origin = origin
+ pos.inUse = false
+
+ //Throw warnings for bad positions
+ foreach ( vector testTarget in file.validationTestTargets )
+ {
+ switch( type )
+ {
+ case eStationaryAIPositionTypes.MORTAR_TITAN:
+ if ( NavMesh_ClampPointForHullWithExtents( origin, HULL_TITAN, <100, 100, 20> ) == null )
+ {
+ CodeWarning( "Mortar Titan Firing Position at " + origin + " does not have enough space to accomidate Titan, skipping." )
+ return
+ }
+ break
+
+ #if MP
+ case eStationaryAIPositionTypes.MORTAR_SPECTRE:
+
+ array<vector> testLocations = MortarSpectreGetSquadFiringPositions( origin, testTarget )
+
+ foreach ( vector testLocation in testLocations )
+ {
+ if ( NavMesh_ClampPointForHullWithExtents( testLocation, HULL_HUMAN, <100, 100, 20> ) == null )
+ {
+ CodeWarning( "Mortar Spectre Firing Position at " + origin + " does not have enough space to accomidate squad, skipping." )
+ return
+ }
+ }
+
+ break
+ #endif //MP
+
+ case eStationaryAIPositionTypes.SNIPER_TITAN:
+ if ( NavMesh_ClampPointForHullWithExtents( origin, HULL_TITAN, <100, 100, 20> ) == null )
+ {
+ CodeWarning( "Sniper Titan Firing Position at " + origin + " does not have enough space to accomidate Titan, skipping." )
+ return
+ }
+ break
+
+ case eStationaryAIPositionTypes.LAUNCHER_REAPER:
+ if ( NavMesh_ClampPointForHullWithExtents( origin, HULL_MEDIUM, <100, 100, 20> ) == null )
+ {
+ CodeWarning( "Tick Launching Reaper Firing Position at " + origin + " does not have enough space to accomidate Reaper, skipping." )
+ return
+ }
+ break
+ }
+ }
+
+ if ( !( type in file.stationaryPositions ) )
+ {
+ file.stationaryPositions[ type ] <- []
+ }
+
+ file.stationaryPositions[ type ].append( pos )
+}
+
+//Function tests stationary AI positions for given type relative to given mortar target.
+void function AddPendingStationaryAIPosition_Internal( vector origin, int type )
+{
+ if ( !( type in file.pendingPositions ) )
+ file.pendingPositions[ type ] <- []
+
+ //Add position to table so we can validate and add it when all entities finish loading.
+ file.pendingPositions[ type ].append( origin )
+}
+
+void function ValidateAndFinalizePendingStationaryPositions()
+{
+
+ Assert( file.validationTestTargets.len(), "Test targets are required to validate stationary positions. Use AddTestTargetPosForStationaryPositionValidation to add them before running validation." )
+
+ foreach ( type, origins in file.pendingPositions )
+ {
+ //Make sure we have pending positions for given ai type.
+ Assert( file.pendingPositions[ type ].len(), "Stationary Positions for type " + type + " could not be found in this map. Add Some." )
+
+ foreach ( vector origin in origins )
+ {
+ AddStationaryAIPosition_Internal( origin, type )
+ }
+
+ //Make sure we have positions for given AI type after we validate and finalize positions.
+ Assert( file.stationaryPositions[ type ].len(), "No valid stationary positions for type " + type + " remain after validation. Adjust positions and retry." )
+ }
+}
+
+StationaryAIPosition function GetClosestAvailableStationaryPosition( vector origin, float maxDist, int type )
+{
+
+ array<StationaryAIPosition> resultArray = []
+ float maxDistSqr = maxDist * maxDist
+
+ array<StationaryAIPosition> positions = file.stationaryPositions[type]
+
+ array<ArrayDistanceEntryForStationaryAIPosition> allResults = ArrayDistanceResultsForStationaryAIPosition( positions, origin )
+ allResults.sort( DistanceCompareClosestForStationaryAIPosition )
+
+ //Remove all in use stationary positions up front.
+ array<ArrayDistanceEntryForStationaryAIPosition> freePositions
+ foreach ( result in allResults )
+ {
+ StationaryAIPosition position = result.ent
+ if ( position.inUse )
+ continue
+
+ freePositions.append( result )
+ }
+
+ //Tell us if all spots for a given AI type are taken.
+ Assert( freePositions.len() > 0, "Could not find free mortar positions for type " + type + ", all positions are currently in use. Add more AddStationaryTitanPosition to the map." )
+
+ foreach( result in freePositions )
+ {
+ StationaryAIPosition position = result.ent
+
+ // if too far, throw warning and continue search beyond maxDist
+ if ( result.distanceSqr > maxDistSqr )
+ {
+ CodeWarning( "Couldn't find a mortar position within " + maxDist + " units for type " + type + " around " + origin.tostring() + " that wasn't in use. Expanding Search. Add more AddStationaryTitanPositions to the map near this point." )
+ }
+
+ return position
+ }
+
+ unreachable
+}
+
+StationaryAIPosition function GetRandomStationaryPosition( vector origin, float maxDist, int type )
+{
+ array<StationaryAIPosition> resultArray = []
+ array<StationaryAIPosition> positions = file.stationaryPositions[type]
+
+ //Remove all in use stationary positions up front.
+ array<StationaryAIPosition> freePositions
+ foreach ( position in positions )
+ {
+ if ( position.inUse )
+ continue
+
+ freePositions.append( position )
+ }
+
+ //Tell us if all spots for a given AI type are taken.
+ Assert( freePositions.len() > 0, "Could not find free mortar positions for type " + type + ", all positions are currently in use. Add more AddStationaryTitanPosition to the map." )
+
+ int attemptCount = 1
+ while ( resultArray.len() == 0 )
+ {
+
+ //Expand our search radius each time we reattempt our search.
+ float maxDistSqr = ( maxDist * attemptCount ) * ( maxDist * attemptCount )
+
+ foreach( position in freePositions )
+ {
+ float dist = Distance2DSqr( origin, position.origin )
+ if ( dist <= maxDistSqr )
+ resultArray.append( position )
+ }
+
+ if ( resultArray.len() == 0 )
+ {
+ CodeWarning( "Couldn't find a mortar position within " + maxDist + " units for type " + type + " around " + origin.tostring() + " that wasn't in use. Expanding Search. Add more AddStationaryTitanPositions to the map near this point." )
+ attemptCount += 1
+ }
+ }
+
+ return resultArray.getrandom()
+}
+
+void function ClaimStationaryAIPosition( StationaryAIPosition stationaryTitanPositions )
+{
+ Assert( stationaryTitanPositions.inUse == false )
+ stationaryTitanPositions.inUse = true
+}
+
+void function ReleaseStationaryAIPosition( StationaryAIPosition stationaryTitanPositions )
+{
+ Assert( stationaryTitanPositions.inUse == true )
+ stationaryTitanPositions.inUse = false
+}
+
+array<ArrayDistanceEntryForStationaryAIPosition> function ArrayDistanceResultsForStationaryAIPosition( array<StationaryAIPosition> entArray, vector origin )
+{
+ array<ArrayDistanceEntryForStationaryAIPosition> allResults
+
+ foreach ( ent in entArray )
+ {
+ ArrayDistanceEntryForStationaryAIPosition entry
+
+ vector entOrigin = ent.origin
+ entry.distanceSqr = DistanceSqr( entOrigin, origin )
+ entry.ent = ent
+ entry.origin = entOrigin
+
+ allResults.append( entry )
+ }
+
+ return allResults
+}
+
+int function DistanceCompareClosestForStationaryAIPosition( ArrayDistanceEntryForStationaryAIPosition a, ArrayDistanceEntryForStationaryAIPosition b )
+{
+ if ( a.distanceSqr > b.distanceSqr )
+ return 1
+ else if ( a.distanceSqr < b.distanceSqr )
+ return -1
+
+ return 0;
+} \ No newline at end of file