aboutsummaryrefslogtreecommitdiff
path: root/Northstar.CustomServers/mod/scripts/vscripts/_on_spawned.gnut
blob: d1935d629ba851d4e6deb93f7ee4cc7d17a05e8a (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
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
untyped

global function CodeCallback_PreSpawn
global function CodeCallback_OnSpawned
global function RunMySpawnFunctions
global function AddScriptNoteworthySpawnCallback
global function SpawnFromSpawnerArray
global function AddSpawnCallback
global function AddSpawnCallbackEditorClass
global function AddSpawnCallback_ScriptName
global function GetLeveledAISettings
global function GetSpawnAISettings
global function GetDefaultAISetting

void function CodeCallback_PreSpawn( entity npc )
{
	/*
	SCRIPTERS READ THIS

	The purpose of this function is to fixup npc fields coming from all the many places they can come from, so that code doesn't break them during DispatchSpawn.
	If you want to fix an AI field on spawned, you should do it in ai_spawn_content, unless the field change is related to code functionality and needs
	to change before code spawns the AI. Then, it should be here.

	Thanks
	-Mackey
	*/
	Assert( npc.IsNPC() )

	if ( !npc.HasAISettings() )
	{
		// this ai has no ai settings file
		string aisettings = GetDefaultAISetting( npc )
		Assert( aisettings != "" )
		SetAISettingsWrapper( npc, aisettings )
	}

	if ( npc.IsTitan() )
	{
		if ( npc.ai.titanSettings.titanSetFile == "" )
		{
			if ( npc.HasKey( "leveled_titan_settings" ) )
			{
				SetTitanSettings( npc.ai.titanSettings, expect string( npc.kv.leveled_titan_settings ) )
			}
		}

		var builtInLoadout

		if ( npc.HasKey( "leveled_titan_loadout" ) )
		{
			builtInLoadout = npc.GetValueForKey( "leveled_titan_loadout" )
		}
		else
		{
			if ( npc.Dev_GetAISettingByKeyField( "WeaponCapacity" ) == "FromLoadout" )
				builtInLoadout = npc.Dev_GetAISettingByKeyField( "npc_titan_player_settings" )
		}

		if ( builtInLoadout != null )
		{
			// derive loadout from built in loadout in this case
			expect string( builtInLoadout )
			if ( npc.ai.titanSettings.titanSetFile != builtInLoadout )
				SetTitanSettings( npc.ai.titanSettings, builtInLoadout )

			npc.ai.titanSpawnLoadout.setFile = builtInLoadout
			// OverwriteLoadoutWithDefaultsForSetFile_ExceptSpecialAndAntiRodeo( npc.ai.titanSpawnLoadout )
			OverwriteLoadoutWithDefaultsForSetFile( npc.ai.titanSpawnLoadout ) // get the entire loadout, including defensive and tactical

			//Set camo, decal, and skin indices from npc settings.
			//Using Dev_GetAISettingsByKeyField_Global because titan has not spawned yet, so the non-global version of this function does not work.
			var camoIndex 	= Dev_GetAISettingByKeyField_Global( npc.GetAISettingsName(), "titanCamoIndex" )
			var decalIndex 	= Dev_GetAISettingByKeyField_Global( npc.GetAISettingsName(), "titanDecalIndex" )
			var skinIndex	= Dev_GetAISettingByKeyField_Global( npc.GetAISettingsName(), "titanSkinIndex" )
			if ( camoIndex != null )
				npc.ai.titanSpawnLoadout.camoIndex = expect int ( camoIndex )
			if ( decalIndex != null )
				npc.ai.titanSpawnLoadout.decalIndex = expect int ( decalIndex )
			if ( skinIndex != null )
				npc.ai.titanSpawnLoadout.skinIndex = expect int ( skinIndex )
		}

		AssignSpawnOptionsFromLeveled( npc, SetSpawnOption_Weapon, "additionalequipment", "primaryWeapon_mods" )
		npc.kv.additionalequipment = ""
		AssignSpawnOptionsFromLeveled( npc, SetSpawnOption_Ordnance, "titanOrdnance", "titanOrdnance_mods" )
		AssignSpawnOptionsFromLeveled( npc, SetSpawnOption_Special, "titanSpecial", "titanSpecial_mods" )
		AssignSpawnOptionsFromLeveled( npc, SetSpawnOption_Antirodeo, "titanAntiRodeo", "titanAntiRodeo_mods" )

		// temp fix for npc_create npc_titan, probably should refactor away npc.ai.titanSettings
		if ( npc.ai.titanSettings.titanSetFile == "" )
			SetTitanSettings( npc.ai.titanSettings, expect string( npc.Dev_GetAISettingByKeyField( "npc_titan_player_settings" ) ) )

		CreateTitanModelAndSkinSetup( npc )

		if ( npc.GetAIClass() == AIC_TITAN_BUDDY )
			npc.kv.squadname = "bt"
	}
	else
	{
		npc.SetValueForModelKey( npc.GetSettingModelName() )
	}

	if ( !IsTurret( npc ) && IsSingleplayer() && npc.kv.squadname == "" && npc.GetTeam() >= FIRST_GAME_TEAM )
		npc.SetAutoSquad()
		//AutoSquadnameAssignment( npc )

	if ( !npc.IsTitan() )
	{
		AssignGrenadeWeaponFromAISettings( npc )

		AssignDroneSpawnAISettings( npc )

		if ( npc.ai.mySpawnOptions_weapon == null )
		{
			if ( !IsTurret( npc ) )
			{
				NPCDefaultWeapon ornull defaultWeapon = GetNPCDefaultWeaponForLevel( npc )
				if ( defaultWeapon != null )
				{
					expect NPCDefaultWeapon( defaultWeapon )
					SetSpawnOption_Weapon( npc, defaultWeapon.wep, defaultWeapon.mods )
					npc.kv.additionalequipment = ""
					return
				}
			}

			switch ( npc.kv.additionalequipment )
			{
				case "":
				case "auto_weapon":
				case "auto_weapon_antititan":
				case "auto_weapon_sidearm":
				case "auto_weapon_rifle":
				case "auto_weapon_lmg":
				case "auto_weapon_shield_captain":
				case "auto_weapon_shotgun":
				case "auto_weapon_smg":
				case "auto_weapon_sniper":
				case "auto_weapon_specialist":

					// fill weapon in from ai settings file
					npc.kv.additionalequipment = ""
					string aiSettingsWeapon = npc.AISetting_GetDefaultWeapon()
					if ( aiSettingsWeapon != "" )
						SetSpawnOption_Weapon( npc, aiSettingsWeapon )
					break

				case "none":
					npc.kv.additionalequipment = ""
					break
			}
		}
	}
}

void function AssignDroneSpawnAISettings( entity npc )
{
	if ( npc.HasKey( "script_drone_type" ) )
	{
		var droneType = npc.kv.script_drone_type
		if ( droneType != null )
		{
			expect string( droneType )
			if ( droneType.tolower() == "none" )
				droneType = ""
			npc.ai.droneSpawnAISettings = droneType
		}
		return
	}

	npc.ai.droneSpawnAISettings = npc.AISetting_SummonDrone()
}


void function AssignGrenadeWeaponFromAISettings( entity npc )
{
	if( npc.kv.grenadeWeaponName == "none" )
	{
		npc.kv.grenadeWeaponName = ""
		return
	}

	if ( npc.kv.GrenadeWeaponName != "" )
		return

	string grenadeWeaponName = npc.AISetting_GetGrenadeWeapon()
	if ( grenadeWeaponName == "" )
		return

	npc.kv.grenadeWeaponName = grenadeWeaponName
}

void function AssignSpawnOptionsFromLeveled( entity npc, void functionref( entity, string, array<string> = 0 ) spawnSettingsFunc, string kvWeapon, string kvWeaponMods )
{
	if ( !npc.HasKey( kvWeapon ) )
		return
	string weapon = npc.GetValueForKey( kvWeapon )
	if ( weapon == "" )
		return

	array<string> mods
	if ( npc.HasKey( kvWeaponMods ) )
	{
		mods = split( npc.GetValueForKey( kvWeaponMods ), " " )
	}

	spawnSettingsFunc( npc, weapon, mods )
}

string function GetDefaultAISetting( entity npc )
{
//	change this to map directly by file name from subClass, and error if its not there.
//	This insures consistent settings file naming and makes settings files less of a mix-and-match concept.
//	subclasses should also sub-name off their class (except for craaaaaazy soldier/grunt guy)

	if ( npc.mySpawnOptions_aiSettings != null )
	{
		// you have to include base if you use SpawnOption_AISettings
		return string( npc.mySpawnOptions_aiSettings )
	}

	if ( npc.HasKey( "leveled_aisettings" ) )
	{
		return GetLeveledAISettings( npc )
	}

	if ( npc.IsTitan() && npc.ai.titanSettings.titanSetFile != "" )
	{
		// from titan player set file
		string settingsKey = GetAISettingsStringForMode()

		var aiSettings = Dev_GetPlayerSettingByKeyField_Global( npc.ai.titanSettings.titanSetFile, settingsKey )
		if ( aiSettings != null )
			return expect string( aiSettings )
	}

	return npc.GetClassName()
}

string function GetLeveledAISettings( entity npc )
{
	Assert( npc.IsNPC() )
	Assert( npc.HasKey( "leveled_aisettings" ) )
	string settings = expect string( npc.kv.leveled_aisettings )
	switch ( settings )
	{
		// remap deprecated substrings for awhile
		case "npc_soldier_drone_summoner_shield":
			return "npc_soldier_drone_summoner"
	}
	return settings
}

string function GetSpawnAISettings( entity npc )
{
	if ( npc.mySpawnOptions_aiSettings != null)
		return expect string( npc.mySpawnOptions_aiSettings )
	else if ( npc.HasKey( "leveled_aisettings" ) )
		return expect string( npc.kv.leveled_aisettings )

	return ""
}

void function CodeCallback_OnSpawned( entity ent )
{
	if ( IsSpawner( ent ) )
	{
		var spawnerKVs = ent.GetSpawnEntityKeyValues()
		if ( "script_flag_killed" in spawnerKVs )
			thread SetupFlagKilledForNPC( ent )
		return
	}

	string classname = ent.GetClassName()

	if ( classname in _entityClassVars )
	{
		if ( !ent._entityVars )
			InitEntityVars( ent )

		//ent.ConnectOutput( "OnDestroy", "_RemoveFromEntityList" )
	}

	int teamNum = int( expect string( ent.kv.teamnumber ) )
	if ( teamNum != 0 )
		SetTeam( ent, teamNum )

	SetModelSkinFromLeveled( ent )

    if ( IsLobby() )
    {
		RunMySpawnFunctions( ent )
        return
    }

	if ( ent.IsNPC() )
	{
		CommonNPCOnSpawned( ent )
	}

	if ( ent instanceof CBaseCombatCharacter && ent.GetModelName() != $"" )
		InitDamageStates( ent )

	if ( ent instanceof CProjectile || ent instanceof CBaseGrenade )
		thread PROTO_InitTrackedProjectile( ent )

	/*
	if ( !( "totalSpawned" in level ) )
	{
		level.totalSpawned <- {}
		level.totalSpawned.total <- 0
	}

	if ( !( "classname" in level.totalSpawned ) )
	{
		level.totalSpawned[ classname ] <- {}
	}

	level.totalSpawned[ classname ][ ent ] <- ent
	level.totalSpawned.total++
	*/

	RegisterForDamageDeathCallbacks( ent )

	RunMySpawnFunctions( ent )
}

function RunMySpawnFunctions( entity self )
{
	if ( !IsValid( self ) )
	{
		// entity was deleted already
		return
	}

	RunSpawnCallbacks( self )
	RunEditorClassCallbacks( self )
	RunScriptNoteworthyCallbacks( self )
	RunScriptNameCallbacks( self )
}

void function AddSpawnCallback( string classname, void functionref( entity ) func )
{
	foreach ( spawnCallbackFuncArray funcArray in svGlobal.spawnCallbackFuncs )
	{
		if ( funcArray.entityClassname == classname )
		{
			funcArray.callbackArray.append( func )
			return
		}
	}

	spawnCallbackFuncArray funcArray
	funcArray.entityClassname = classname
	funcArray.callbackArray.append( func )
	svGlobal.spawnCallbackFuncs.append( funcArray )
}

void function AddSpawnCallbackEditorClass( string classname, string editorClassname, void functionref( entity ) func )
{
	foreach ( spawnCallbackEditorClassFuncArray funcArray in svGlobal.spawnCallbackEditorClassFuncs )
	{
		if ( funcArray.entityClassname == classname && funcArray.entityEditorClassname == editorClassname )
		{
			funcArray.callbackArray.append( func )
			return
		}
	}

	spawnCallbackEditorClassFuncArray funcArray
	funcArray.entityClassname = classname
	funcArray.entityEditorClassname = editorClassname
	funcArray.callbackArray.append( func )
	svGlobal.spawnCallbackEditorClassFuncs.append( funcArray )
}

function RunSpawnCallbacks( entity self )
{
	string classname = self.GetClassName()

	foreach ( spawnCallbackFuncArray funcArray in svGlobal.spawnCallbackFuncs )
	{
		if ( funcArray.entityClassname == classname )
		{
			foreach ( func in funcArray.callbackArray )
			{
				func( self )
			}
		}
	}
}

function RunEditorClassCallbacks( entity self )
{
	string editorClassname = GetEditorClass( self )
	if ( editorClassname == "" )
		return

	string classname = self.GetClassName()

	foreach ( spawnCallbackEditorClassFuncArray funcArray in svGlobal.spawnCallbackEditorClassFuncs )
	{
		if ( funcArray.entityEditorClassname == editorClassname )
		{
			//Assert( funcArray.entityClassname == classname, "Editor classname callback was set on entity with wrong base classname type" )
			if ( funcArray.entityClassname != classname )
				CodeWarning( "Entity " + editorClassname + " is expecting alias of " + funcArray.entityClassname + " but found a " + classname + ". You may just need to reexport from LevelEd and recompile the map to fix this." )

			foreach ( func in funcArray.callbackArray )
			{
				thread func( self )
			}
		}
	}
}

array<entity> function SpawnFromSpawnerArray( array<entity> spawners, void functionref( entity ) ornull spawnSettingsFunc = null )
{
	array<entity> spawned
	if ( spawnSettingsFunc == null )
	{
		foreach ( entity spawner in spawners )
		{
			entity ent = spawner.SpawnEntity()
			DispatchSpawn( ent )
			spawned.append( ent )
		}
	}
	else
	{
		expect void functionref( entity )( spawnSettingsFunc )
		foreach ( entity spawner in spawners )
		{
			entity ent = spawner.SpawnEntity()
			spawnSettingsFunc( ent )
			DispatchSpawn( ent )
			spawned.append( ent )
		}
	}

	return spawned
}

void function RunScriptNameCallbacks( entity ent )
{
	string name = ent.GetScriptName()
	if ( !( name in svGlobal.spawnCallbacks_scriptName ) )
		return

	foreach ( callback in svGlobal.spawnCallbacks_scriptName[ name ] )
	{
		thread callback( ent )
	}
}

void function AddSpawnCallback_ScriptName( string scriptName, void functionref( entity ) func )
{
	if ( !( scriptName in svGlobal.spawnCallbacks_scriptName ) )
		svGlobal.spawnCallbacks_scriptName[ scriptName ] <- []
	svGlobal.spawnCallbacks_scriptName[ scriptName ].append( func )
}

void function RunScriptNoteworthyCallbacks( entity ent )
{
	if ( !( ent.HasKey( "script_noteworthy" ) ) )
		return

	foreach ( noteworthyCallback in svGlobal.spawnCallbackFuncs_scriptNoteworthy )
	{
		if ( ent.kv.script_noteworthy != noteworthyCallback.scriptNoteworthy )
			continue

		foreach ( func in noteworthyCallback.callbackArray )
		{
			func( ent )
		}

		break // ??? break?
	}
}

void function AddScriptNoteworthySpawnCallback( string script_noteworthy, void functionref( entity ) func )
{
	foreach ( noteworthyCallback in svGlobal.spawnCallbackFuncs_scriptNoteworthy )
	{
		if ( script_noteworthy != noteworthyCallback.scriptNoteworthy )
			continue

		noteworthyCallback.callbackArray.append( func )
		return
	}

	spawnCallbackFuncArray_scriptNoteworthy newNoteworthyCallback
	newNoteworthyCallback.scriptNoteworthy = script_noteworthy
	newNoteworthyCallback.callbackArray.append( func )
	svGlobal.spawnCallbackFuncs_scriptNoteworthy.append( newNoteworthyCallback )
}

void function SetModelSkinFromLeveled( entity ent )
{
	// Hack that we have to wait a frame for it to work. Code should just do this for us anyways.
	if ( !ent.HasKey( "modelskin" ) )
		return

	int skin = expect int( ent.kv.modelskin.tointeger() )
	if ( skin > 0 )
		ent.SetSkin( skin )
}