aboutsummaryrefslogtreecommitdiff
path: root/Northstar.Custom/mod/scripts/vscripts/burnmeter/sh_burnmeter.gnut
blob: ac9ffab375f06d1aec4cf0d15f17dc43bf0b380a (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
509
510
511
global function ShBurnMeter_Init
global function BurnMeter_SetNoTitansReplacement
global function BurnMeter_GetNoTitansReplacement
global function BurnReward_GetById
global function BurnReward_GetByRef
global function BurnReward_GetRandom
global function GetSelectedBurnCardRef
global function GetBoostSkin
#if SERVER || CLIENT
global function BurnMeterEnabled
global function CanUseWeaponAsBurnCard
global function TryUsingBurnCardWeapon
global function TryUsingBurnCardWeaponInCriticalSection
global function BurnMeterPlayer_CanUseEquipped
global function OnWeaponAttemptOffhandSwitch_burncardweapon
global function BurnMeterPlayer_CanUseReward
global function BurnMeterPlayer_GetRewardOrGoal
global function BurnReward_GetTopInventoryItemBurnCard
global function OnWeaponPrimaryAttack_nuke_eject
global function BurnMeter_HarvesterShieldCanUse
#endif

global function GetAllBoostTurretTypes

global const BURN_METER_SMALL_POINT_VALUE = 1
global const BURN_METER_MID_POINT_VALUE = 2
global const BURN_METER_LARGE_POINT_VALUE = 5
global const BURN_METER_EXTRA_LARGE_POINT_VALUE = 10

global const BURN_METER_RADAR_JAMMER_PULSE_DURATION = 6.0
global const BURN_METER_RADAR_JAMMER_EASE_OFF_TIME = 1.0

global enum eBurnMeterRewardAvailableFor
{
	PILOT_ONLY,
	TITAN_ONLY,
	PILOT_AND_TITAN,
	SPECIAL_PLAYERS_ONLY,
}

global struct BurnReward
{
	int id = -1
	string ref = ""
	string localizedName = ""
	string description = ""
	asset image = $""
	float cost
	int userType = -1
	int skinIndex = 0
	string weaponName = ""
	string extraWeaponMod = ""
	string activationText = ""

	void functionref( entity ) ornull rewardAvailableCallback = null
}

global struct BurnStruct
{
	table< string, BurnReward > burnRewards
	array< BurnReward > allCards //Kinda inefficient to store the same burn cards 3 times (once in burnRewards, once in foil/normal, once in allcards. Prefer speed over memory? )
	array< BurnReward > allowedCards

}

global BurnStruct burn

global table< string, bool functionref( entity ) >  burnMeterCanUseFuncTable

struct
{
	bool shouldCycleOnRelease = false
	table<string, string> noTitansReplacements
} file

array<string> turretBurnCards = [
	"burnmeter_ap_turret_weapon",
	"burnmeter_at_turret_weapon",
	"burnmeter_ap_turret_weapon_infinite",
	"burnmeter_at_turret_weapon_infinite",
]

void function ShBurnMeter_Init()
{

	#if SERVER || CLIENT
		//burnMeterCanUseFuncTable[ "burnmeter_random_foil" ] <- BurnMeter_RandomFoilCanUse
		burnMeterCanUseFuncTable[ "burnmeter_emergency_titan" ] <- BurnMeter_EmergencyTitanCanUse
		burnMeterCanUseFuncTable[ "burnmeter_nuke_titan" ] <- BurnMeter_NukeTitanCanUse
		// burnMeterCanUseFuncTable[ "burnmeter_harvester_shield" ] <- BurnMeter_HarvesterShieldCanUse
	#endif



	var dataTable = GetDataTable( $"datatable/burn_meter_rewards.rpak" )

	for ( int row = 0; row < GetDatatableRowCount( dataTable ); row++ )
	{
		BurnReward burnReward
		burnReward.id = row
		burnReward.ref = GetDataTableString( dataTable, row, GetDataTableColumnByName( dataTable, BURN_REF_COLUMN_NAME ) )
		burnReward.localizedName = GetDataTableString( dataTable, row, GetDataTableColumnByName( dataTable, BURN_NAME_COLUMN_NAME ) )
		burnReward.description = GetDataTableString( dataTable, row, GetDataTableColumnByName( dataTable, BURN_DESCRIPTION_COLUMN_NAME ) )
		burnReward.image = GetDataTableAsset( dataTable, row, GetDataTableColumnByName( dataTable, BURN_IMAGE_COLUMN_NAME ) )
		burnReward.cost = GetDataTableFloat( dataTable, row, GetDataTableColumnByName( dataTable, BURN_COST_COLUMN_NAME ) )
		string userType = GetDataTableString( dataTable, row, GetDataTableColumnByName( dataTable, BURN_AVAILABLE_COLUMN_NAME ) )
		burnReward.userType = eBurnMeterRewardAvailableFor[userType]
		burnReward.weaponName = GetDataTableString( dataTable, row, GetDataTableColumnByName( dataTable, BURN_WEAPON_COLUMN_NAME ) )
		burnReward.extraWeaponMod = GetDataTableString( dataTable, row, GetDataTableColumnByName( dataTable, BURN_EXTRA_WEAPON_MOD_NAME ) )
		burnReward.activationText = GetDataTableString( dataTable, row, GetDataTableColumnByName( dataTable, "activationText" ) )
		burnReward.skinIndex = GetDataTableInt( dataTable, row, GetDataTableColumnByName( dataTable, "skinIndex" ) )

		if ( IsDisabledRef( burnReward.ref ) )
			continue

#if SERVER || CLIENT
		if ( burnReward.weaponName != "" )
			PrecacheWeapon( burnReward.weaponName )
		#endif

		burn.burnRewards[burnReward.ref] <- burnReward

		bool selectable 		= GetDataTableBool( dataTable, row, GetDataTableColumnByName( dataTable, "selectable" ) )
		burn.allCards.append( burnReward )
		if ( selectable )
			burn.allowedCards.append( burnReward )
	}

	#if CLIENT
		//Registering here instead of in CreateWeaponData() in _items.nut to remove dependence on HUD script with items. If more weapons other than smart pistol run into this problem, just add a column in burn_meter_rewards to specify we need to do this. ( ticks are fine because they have a damagedef associated with them )
		RegisterWeaponDamageSourceName( "mp_weapon_smart_pistol", expect string( GetWeaponInfoFileKeyField_GlobalNotNull( "mp_weapon_smart_pistol", "shortprintname" ) ) )

		RegisterConCommandTriggeredCallback( "-offhand4", CycleOnRelease )
	#endif

	BurnMeter_SetNoTitansReplacement( "burnmeter_emergency_battery", "burnmeter_amped_weapons" )
	BurnMeter_SetNoTitansReplacement( "burnmeter_at_turret_weapon", "burnmeter_amped_weapons" )
	BurnMeter_SetNoTitansReplacement( "burnmeter_rodeo_grenade", "burnmeter_amped_weapons" )
	BurnMeter_SetNoTitansReplacement( "burnmeter_nuke_titan", "burnmeter_amped_weapons" )
}

void function BurnMeter_SetNoTitansReplacement( string original, string noTitansReplacement )
{
	file.noTitansReplacements[original] <- noTitansReplacement
}

string function BurnMeter_GetNoTitansReplacement( string burnRef )
{
	if ( burnRef in file.noTitansReplacements )
		return file.noTitansReplacements[burnRef]

	return burnRef
}

#if SERVER || CLIENT
bool function BurnMeterEnabled()
{
	return Riff_BoostAvailability() != eBoostAvailability.Disabled
}
#endif

BurnReward function BurnReward_GetById( int id )
{
	return burn.allCards[ id ]
}


BurnReward function BurnReward_GetByRef( string ref )
{
	Assert( ref in burn.burnRewards )

	// more hacks for arena
	if ( !( ref in burn.burnRewards ) && GetCurrentPlaylistVarString( "boost_store_mode", "off" ) == "arena" )
		return GetArenaLoadoutItemAsBurnReward( ref )

	return burn.burnRewards[ref]
}

int function GetBoostSkin( string ref )
{
	BurnReward reward = BurnReward_GetByRef( ref )
	//printt( "ref: " + ref + ", Boost skin: " + reward.skinIndex )
	return reward.skinIndex
}

BurnReward function BurnReward_GetRandom()
{
	string ref = burn.allowedCards.getrandom().ref

	#if SERVER || CLIENT
	if ( !EarnMeterMP_IsTitanEarnGametype() )
		ref = BurnMeter_GetNoTitansReplacement( ref )

	if ( GetCurrentPlaylistVarInt( "featured_mode_all_ticks", 0 ) >= 1 )
		if ( ref == "burnmeter_ticks" || ref == "burnmeter_random_foil"  )
			ref = "burnmeter_amped_weapons"
	#endif

	return BurnReward_GetByRef( ref )
}

string function GetSelectedBurnCardRef( entity player )
{
	int burnCardID = expect int ( player.GetPersistentVar( "burnmeterSlot" ) )
	BurnReward burnCard = burn.allCards[ burnCardID ]
	string ref = burnCard.ref

	#if SERVER
	if ( GetItemDisplayData( ref ).hidden )
		ClientCommand( player, "disconnect" )
	#endif

	#if SERVER || CLIENT
	if ( !EarnMeterMP_IsTitanEarnGametype() )
		ref = BurnMeter_GetNoTitansReplacement( ref )

	if ( GetCurrentPlaylistVarInt( "featured_mode_all_ticks", 0 ) >= 1 )
		if ( ref == "burnmeter_ticks" || ref == "burnmeter_random_foil"  )
			ref = "burnmeter_amped_weapons"
	#endif

	return ref
}


#if SERVER || CLIENT
bool function CanUseWeaponAsBurnCard( entity weapon, entity ownerPlayer )
{
	Assert( weapon.HasMod( "burn_card_weapon_mod" ) )

	if ( !BurnMeterPlayer_CanUseEquipped( ownerPlayer ) )
	{
		weapon.EmitWeaponSound( "CoOp_SentryGun_DeploymentDeniedBeep" )
		return false
	}

	return true
}

bool function TryUsingBurnCardWeapon( entity weapon, entity ownerPlayer )
{
	Assert( weapon.HasMod( "burn_card_weapon_mod" ) )

	if ( !CanUseWeaponAsBurnCard( weapon, ownerPlayer ) )
		return false

	#if SERVER
		UseBurnCardWeapon( weapon, ownerPlayer )
	#endif

	return true
}

bool function TryUsingBurnCardWeaponInCriticalSection( entity weapon, entity ownerPlayer )
{
	Assert( weapon.HasMod( "burn_card_weapon_mod" ) )

	if ( !CanUseWeaponAsBurnCard( weapon, ownerPlayer ) )
		return false

	#if SERVER
		UseBurnCardWeaponInCriticalSection( weapon, ownerPlayer )
	#endif

	return true
}



bool function BurnMeterPlayer_CanUseReward( entity player, BurnReward burnReward )
{
	if ( !BurnMeterPlayer_CanUseGlobal( player ) )
		return false

	switch( burnReward.userType )
	{
		case eBurnMeterRewardAvailableFor.PILOT_ONLY:
			return !player.IsTitan()

		case eBurnMeterRewardAvailableFor.TITAN_ONLY:
			return player.IsTitan()

		case eBurnMeterRewardAvailableFor.PILOT_AND_TITAN:
			return true

		case eBurnMeterRewardAvailableFor.SPECIAL_PLAYERS_ONLY:
			return BurnMeterPlayer_CanUseSpecial( player, burnReward ) //TODO: Note that for the case of reaperfall we also send the message to the client if they can't summon reaper in the validation function. Not ideal...
	}

	unreachable

}


bool function BurnMeterPlayer_CanUseEquipped( entity player )
{
	BurnReward burnReward = BurnReward_GetTopInventoryItemBurnCard( player )
	return BurnMeterPlayer_CanUseReward( player, burnReward )
}


EarnObject function BurnMeterPlayer_GetRewardOrGoal( entity player )
{
	if ( PlayerEarnMeter_IsRewardEnabled( player ) ) // TODO: more robust
		return PlayerEarnMeter_GetReward( player )
	else
		return PlayerEarnMeter_GetGoal( player )

	unreachable
}


bool function BurnMeterPlayer_CanUseGlobal( entity player )
{
	if ( player.IsBot() )
		return false

	if ( !IsAlive( player ) )
		return false

	if ( player.IsPhaseShifted() )
		return false

	if ( player.ContextAction_IsInVehicle() ) //no rewards when in dropship
		return false

	if ( !IsBurnMeterRewardAvailableForGameState() )
		return false

	return true
}


bool function IsBurnMeterRewardAvailableForGameState() //Based off IsReplacementTitanAvailable
{
	int currentGameState = GetGameState()

	switch ( currentGameState ) //need to add a new entry in here for every new game state we make
	{
		case eGameState.WaitingForCustomStart:
		case eGameState.WaitingForPlayers:
		case eGameState.PickLoadout:
		case eGameState.Prematch:
		case eGameState.SwitchingSides:
		case eGameState.Postmatch:
			return false

		case eGameState.Playing:
		case eGameState.SuddenDeath:
			return true

		case eGameState.WinnerDetermined:
		case eGameState.Epilogue:
		{
			if ( IsRoundBased() )
			{
				// TODO: make this work on the client
				#if SERVER
				if ( !IsRoundBasedGameOver() )
					return false

				if ( !ShouldRunEvac() )
					return false
				#endif
			}

			return true
		}

		default:
			Assert( false, "Unknown Game State: " + currentGameState )
			return false
	}

	unreachable
}


bool function OnWeaponAttemptOffhandSwitch_burncardweapon( entity weapon )
{
	entity ownerPlayer = weapon.GetWeaponOwner()
	Assert( ownerPlayer.IsPlayer() )

	entity activeWeapon = ownerPlayer.GetActiveWeapon()
	if ( ownerPlayer.IsUsingOffhandWeapon() && !( activeWeapon.GetWeaponInfoFileKeyField( "fire_mode" ) == "offhand_melee" && activeWeapon.IsReadyToFire() ) )
		return false

	if ( weapon.HasMod( "burn_card_weapon_mod" ) )
	{
		bool canUse = BurnMeterPlayer_CanUseEquipped( ownerPlayer )

		return canUse
	}
	else
		return true

	unreachable
}

BurnReward function BurnReward_GetTopInventoryItemBurnCard( entity player )
{
	int burnRewardID = player.GetPlayerNetInt( TOP_INVENTORY_ITEM_BURN_CARD_ID )
	return BurnReward_GetById( burnRewardID )
}

bool function BurnMeterPlayer_CanUseSpecial( entity player, BurnReward burnReward )
{
	Assert( burnReward.ref in burnMeterCanUseFuncTable )
	return burnMeterCanUseFuncTable[ burnReward.ref ]( player )
}

bool function BurnMeter_HarvesterShieldCanUse( entity player )
{
//	entity harvester = GetGlobalNetEnt( "FD_activeHarvester" )
	bool canUse = GetGlobalNetTime( "FD_harvesterInvulTime" ) < Time()
	// harvester.GetShieldHealth() < harvester.GetShieldHealthMax()

	if ( !canUse )
	{
		#if CLIENT
		AddPlayerHint( 3.0, 0.5, $"", "#HINT_HARVESTER_BOOST_CANT_USE" )
		#endif
	}

	return canUse
}

bool function BurnMeter_NukeTitanCanUse( entity player )
{
	bool canUse = BurnMeter_EmergencyTitanCanUse( player )

	if ( !canUse )
	{
		#if CLIENT
		AddPlayerHint( 5.0, 0.5, $"", "#HINT_NUKE_TITAN_CANT_USE" )
		#endif
	}

	return canUse
}

bool function BurnMeter_EmergencyTitanCanUse( entity player )
{
	if ( IsAlive( player.GetPetTitan() ) )
		return false

	#if SERVER
		if ( !IsReplacementTitanAvailableForGameState() )
			return false

		if ( player.isSpawning )
			return false

		return true
	#endif

	#if CLIENT
		return true
	#endif
}

bool function BurnMeter_SummonReaperCanUse( entity player )
{
	array< entity > allReapers = GetNPCArrayByClass( "npc_super_spectre" )

	int playerTeam = player.GetTeam()

	foreach( reaper in allReapers )
	{
		if ( reaper.GetTeam() == playerTeam )
		{
			#if SERVER
				MessageToPlayer( player, eEventNotifications.BurnMeter_CantSummonReaper )
			#endif

			return false
		}
	}
	return true
}

var function OnWeaponPrimaryAttack_nuke_eject( entity weapon, WeaponPrimaryAttackParams attackParams )
{
	#if CLIENT
		entity weaponOwner = weapon.GetWeaponOwner()
		if ( weaponOwner.IsPlayer() && IsValid( weaponOwner.GetCockpit() ) )
		{
			weaponOwner.ClientCommand( "TitanEject " + 3 )
			PlayerEjects( weaponOwner, weaponOwner.GetCockpit() ) //Note that this can be run multiple times in a frame, e.g. get damaged by 4 pellets of a shotgun that brings the Titan into a doomed state with auto eject. Not ideal
		}
	#endif
	return 0
}

#endif

#if CLIENT
void function CycleOnRelease( entity player )
{
	if ( file.shouldCycleOnRelease )
	{
		CycleInventory( player )
		file.shouldCycleOnRelease = false
	}
}
#endif

array<string> function GetAllBoostTurretTypes()
{
	return turretBurnCards
}