aboutsummaryrefslogtreecommitdiff
path: root/Northstar.CustomServers/scripts/vscripts/ai/_ai_stationary_firing_positions.gnut
blob: 50b6cc759a7a26311dc45d3d72af3bc08112c2c3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
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;
}