aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/burnmeter/_burnmeter.gnut270
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/mp/_base_gametype_mp.gnut2
2 files changed, 263 insertions, 9 deletions
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/burnmeter/_burnmeter.gnut b/Northstar.CustomServers/mod/scripts/vscripts/burnmeter/_burnmeter.gnut
index 8e1cb71f..fa6867b8 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/burnmeter/_burnmeter.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/burnmeter/_burnmeter.gnut
@@ -1,19 +1,175 @@
+untyped
+
global function BurnMeter_Init
-global function InitBurnMeterPersistentData
-global function BurnMeter_GiveRewardDirect
global function RunBurnCardUseFunc
global function UseBurnCardWeapon
global function UseBurnCardWeaponInCriticalSection
+global function BurnMeter_GiveRewardDirect
global function GetBurnCardWeaponSkin
+global function InitBurnMeterPersistentData
+
+// taken from wraith portal in apex, assuming it's the same as tf2's
+const float PHASE_REWIND_PATH_SNAPSHOT_INTERVAL = 0.1
+const float AMPED_WEAPONS_LENGTH = 30.0
void function BurnMeter_Init()
{
+ // turret precaches
+ // do we have to cache these on client? release builds sure don't
+ PrecacheModel( Dev_GetAISettingAssetByKeyField_Global( "npc_turret_sentry_burn_card_ap", "DefaultModelName" ) )
+ PrecacheModel( Dev_GetAISettingAssetByKeyField_Global( "npc_turret_sentry_burn_card_at", "DefaultModelName" ) )
+
+ // setup burncard use funcs
+ BurnReward_GetByRef( "burnmeter_amped_weapons" ).rewardAvailableCallback = PlayerUsesAmpedWeaponsBurncard
+ BurnReward_GetByRef( "burnmeter_smart_pistol" ).rewardAvailableCallback = PlayerUsesSmartPistolBurncard
+ BurnReward_GetByRef( "burnmeter_emergency_battery" ).rewardAvailableCallback = PlayerUsesBatteryBurncard
+ BurnReward_GetByRef( "burnmeter_radar_jammer" ).rewardAvailableCallback = PlayerUsesRadarJammerBurncard
+ //BurnReward_GetByRef( "burnmeter_maphack" ).rewardAvailableCallback =
+ //BurnReward_GetByRef( "burnmeter_phase_rewind" ).rewardAvailableCallback =
+
+ // these ones aren't so important
+ BurnReward_GetByRef( "burnmeter_nuke_titan" ).rewardAvailableCallback = PlayerUsesNukeTitanBurncard // unused in vanilla, fun though
+ //BurnReward_GetByRef( "burnmeter_harvester_shield" ).rewardAvailableCallback =
+ BurnReward_GetByRef( "burnmeter_rodeo_grenade" ).rewardAvailableCallback = PlayerUsesRodeoGrenadeBurncard
+
+ // setup player callbacks
+ AddCallback_GameStateEnter( eGameState.Playing, InitBurncardsForIntroPlayers )
+ AddCallback_OnClientConnected( InitBurncardsForLateJoiner )
+
+ AddCallback_OnPlayerRespawned( StartPhaseRewindLifetime )
+}
+
+void function InitPlayerBurncards( entity player )
+{
+ int burnmeterSlot = player.GetPersistentVarAsInt( "burnmeterSlot" )
+ player.SetPlayerNetInt( TOP_INVENTORY_ITEM_BURN_CARD_ID, burnmeterSlot )
+
+ if ( BurnReward_GetById( burnmeterSlot ).ref == "burnmeter_phase_rewind" )
+ {
+ player.s.hasPhaseRewind <- true
+
+ if ( IsAlive( player ) )
+ thread PhaseRewindLifetime( player )
+ }
+ else
+ player.s.hasPhaseRewind <- false
+}
+void function InitBurncardsForIntroPlayers()
+{
+ // gotta do this, since sh_burnmeter uses this netint
+ foreach ( entity player in GetPlayerArray() )
+ InitPlayerBurncards( player )
}
-void function InitBurnMeterPersistentData(entity player)
+void function InitBurncardsForLateJoiner( entity player )
{
+ // gotta do this, since sh_burnmeter uses this netint
+ if ( GetGameState() > eGameState.Prematch )
+ InitPlayerBurncards( player )
+}
+void function StartPhaseRewindLifetime( entity player )
+{
+ if ( "hasPhaseRewind" in player.s && player.s.hasPhaseRewind )
+ thread PhaseRewindLifetime( player )
+}
+
+void function PhaseRewindLifetime( entity player )
+{
+ player.EndSignal( "OnDestroy" )
+ player.EndSignal( "OnDeath" )
+
+ OnThreadEnd( function() : ( player )
+ {
+
+ })
+
+ while ( true )
+ {
+
+
+ wait PHASE_REWIND_PATH_SNAPSHOT_INTERVAL
+ }
+}
+
+void function RunBurnCardUseFunc( entity player, string itemRef )
+{
+ void functionref( entity ) ornull func = BurnReward_GetByRef( itemRef ).rewardAvailableCallback
+ if ( func != null )
+ ( expect void functionref( entity ) ( func ) )( player )
+}
+
+string function GetBurncardRefFromWeaponOrPlayer( entity weapon, entity player )
+{
+ // determine the burncard we're using
+ // in actual gameplay, this will always be the player's selected burncard
+ // however, if we want to manually give burncards and such, we want to make sure they'll still work
+ // so some extra work goes into this
+
+ string ref = GetSelectedBurnCardRef( player )
+ // determine via weapon mods, this assumes weapon mod names are the same as burn refs, which works in practice but is a bit weird
+ // though, this does crash with the burnmeter_doublexp mod
+ if ( weapon.GetWeaponClassName() == "mp_ability_burncardweapon" )
+ {
+ foreach ( string mod in weapon.GetMods() )
+ if ( mod.find( "burnmeter_" ) == 0 )
+ return mod
+ }
+ // determine via weapon name in the case of stuff like holopilot etc
+ else
+ {
+ // unfortunately, we have to hardcode this, we don't have a way of getting refs directly from weapons other than the burncard weapon
+ // this should be modular at some point, wish we could just iterate over burncards and find ones with the current weapon, but this isn't possible
+ switch ( weapon.GetWeaponClassName() )
+ {
+ case "mp_ability_holopilot_nova":
+ return "burnmeter_holopilot_nova"
+
+ case "mp_weapon_arc_trap":
+ return "burnmeter_arc_trap"
+
+ case "mp_weapon_frag_drone":
+ return "burnmeter_ticks"
+
+ case "mp_weapon_hard_cover":
+ return "burnmeter_hard_cover"
+
+ case "mp_ability_turretweapon":
+ // turret has 2 burncards, antititan and antipilot
+ if( weapon.HasMod( "burnmeter_at_turret_weapon" ) || weapon.HasMod( "burnmeter_at_turret_weapon_inf" ) )
+ return "burnmeter_at_turret_weapon"
+ else
+ return "burnmeter_ap_turret_weapon"
+
+ // note: cloak and stim both have burn_card_weapon_mod mods, but they aren't used, likely for infinite stim/cloak burncards?
+
+ default:
+ print( "tried to use unknown burncard weapon " + weapon.GetWeaponClassName() )
+ return "burnmeter_amped_weapons" // shouldn't hit this
+ }
+ }
+
+ return ref
+}
+
+void function UseBurnCardWeapon( entity weapon, entity player )
+{
+ string ref = GetBurncardRefFromWeaponOrPlayer( weapon, player )
+
+ Remote_CallFunction_Replay( player, "ServerCallback_RewardUsed", BurnReward_GetByRef( ref ).id )
+ RunBurnCardUseFunc( player, ref )
+
+ // dont remove in RunBurnCardUseFunc because it can be called in non-burn_card_weapon_mod contexts
+ // TODO: currently not sure how burncards can be stacked ( max clipcount for all burncards is 1, so can't just set that )
+ // if this gets figured out, add a conditional check here to prevent removes if they've got burncards left
+ player.TakeOffhandWeapon( 4 )
+}
+
+void function UseBurnCardWeaponInCriticalSection( entity weapon, entity ownerPlayer )
+{
+ // ignoring critical section stuff, assuming it was necessary in tf1 where burncards were part of inventory
+ UseBurnCardWeapon( weapon, ownerPlayer )
}
void function BurnMeter_GiveRewardDirect( entity player, string itemRef )
@@ -21,22 +177,120 @@ void function BurnMeter_GiveRewardDirect( entity player, string itemRef )
}
-void function RunBurnCardUseFunc(entity player, string itemRef)
+int function GetBurnCardWeaponSkin( entity weapon )
+{
+ return GetBoostSkin( GetBurncardRefFromWeaponOrPlayer( weapon, weapon.GetOwner() ) )
+}
+
+// stub
+void function InitBurnMeterPersistentData( entity player )
+{}
+
+
+// burncard use funcs
+
+void function PlayerUsesAmpedWeaponsBurncard( entity player )
+{
+ thread PlayerUsesAmpedWeaponsBurncardThreaded( player )
+}
+
+void function PlayerUsesAmpedWeaponsBurncardThreaded( entity player )
+{
+ array<entity> weapons = player.GetMainWeapons()
+ //weapons.extend( player.GetOffhandWeapons() ) // idk? unsure of vanilla behaviour here
+ foreach ( entity weapon in weapons )
+ {
+ foreach ( string mod in GetWeaponBurnMods( weapon.GetWeaponClassName() ) )
+ weapon.AddMod( mod )
+
+ // needed to display amped weapon time left
+ weapon.SetScriptFlags0( weapon.GetScriptFlags0() | WEAPONFLAG_AMPED )
+ weapon.SetScriptTime0( Time() + AMPED_WEAPONS_LENGTH )
+ }
+
+ wait AMPED_WEAPONS_LENGTH
+
+ // note: weapons may have been destroyed or picked up by other people by this point, so need to verify this
+ foreach ( entity weapon in weapons )
+ {
+ if ( !IsValid( weapon ) )
+ continue
+
+ foreach ( string mod in GetWeaponBurnMods( weapon.GetWeaponClassName() ) )
+ weapon.RemoveMod( mod )
+
+ weapon.SetScriptFlags0( weapon.GetScriptFlags0() & ~WEAPONFLAG_AMPED )
+ }
+}
+
+void function PlayerUsesSmartPistolBurncard( entity player )
{
+ // take secondary weapon
+ array<entity> sidearms = player.GetMainWeapons()
+ if ( sidearms.len() > 1 )
+ player.TakeWeaponNow( sidearms[ 1 ].GetWeaponClassName() ) // take secondary weapon
+
+ player.GiveWeapon( "mp_weapon_smart_pistol" )
+ player.SetActiveWeaponByName( "mp_weapon_smart_pistol" )
+
+ // do we need to track the player losing smart pistol, then give their old weapon back? idk not implementing for now, check later
+}
+void function PlayerUsesBatteryBurncard( entity player )
+{
+ Rodeo_GiveBatteryToPlayer( player )
}
-void function UseBurnCardWeapon( entity weapon, entity ownerPlayer )
+void function PlayerUsesRadarJammerBurncard( entity player )
{
+ foreach ( entity otherPlayer in GetPlayerArray() )
+ {
+ MessageToPlayer( otherPlayer, eEventNotifications.BurnMeter_RadarJammerUsed, player )
+
+ if ( otherPlayer.GetTeam() != player.GetTeam() )
+ StatusEffect_AddTimed( otherPlayer, eStatusEffect.minimap_jammed, 1.0, RADAR_JAM_TIME, RADAR_JAM_TIME )
+ }
+}
+void function PlayerUsesNukeTitanBurncard( entity player )
+{
+ thread PlayerUsesNukeBurncardThreaded( player )
}
-void function UseBurnCardWeaponInCriticalSection( entity weapon, entity ownerPlayer )
+void function PlayerUsesNukeBurncardThreaded( entity player )
{
+ // if this is given manually ( i.e. not the equipped burnreward in inventory ), this will run at bad times
+ // so do this check here, yes, this will cause people to lose their cards and get nothing, but better than free titan regens
+ if ( !BurnMeterPlayer_CanUseReward( player, BurnReward_GetByRef( "burnmeter_nuke_titan" ) ) )
+ return
+
+ float ownedFrac = PlayerEarnMeter_GetOwnedFrac( player )
+
+ // use player's titan loadout, but with warpfall so faster and no dome
+ TitanLoadoutDef titanLoadout = GetTitanLoadoutForPlayer( player )
+ titanLoadout.passive3 = "pas_warpfall"
+
+ thread CreateTitanForPlayerAndHotdrop( player, GetTitanReplacementPoint( player, false ) )
+
+ entity titan = player.GetPetTitan()
+ SetTeam( titan, TEAM_UNASSIGNED ) // make it so you can kill yourself lol
+ DoomTitan( titan )
+ NPC_SetNuclearPayload( titan )
+ // this should get run after the vanilla set_usable's event, so titan is never embarkable
+ // embarking a titan in this state WILL kill the server so uhh, pretty bad
+ AddAnimEvent( titan, "set_usable", void function( entity titan ) { titan.UnsetUsable() } )
+
+ titan.WaitSignal( "TitanHotDropComplete" )
+ AutoTitan_SelfDestruct( titan )
+ while ( PlayerEarnMeter_GetMode( player ) == eEarnMeterMode.PET )
+ WaitFrame()
+
+ // restore original earnmeter values, no way to set earned that's exposed unfortunately
+ PlayerEarnMeter_SetOwnedFrac( player, ownedFrac )
}
-int function GetBurnCardWeaponSkin(entity weapon)
+void function PlayerUsesRodeoGrenadeBurncard( entity player )
{
- return 0
+ player.SetPlayerNetInt( "numSuperRodeoGrenades", 7player.GetPlayerNetInt( "numSuperRodeoGrenades" ) + 1 )
} \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/_base_gametype_mp.gnut b/Northstar.CustomServers/mod/scripts/vscripts/mp/_base_gametype_mp.gnut
index 38803e04..c42899e3 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/mp/_base_gametype_mp.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/_base_gametype_mp.gnut
@@ -286,7 +286,7 @@ void function PostDeathThread_MP( entity player, var damageInfo ) // based on ga
// do some pre-replay stuff if we're gonna do a replay
float replayLength = CalculateLengthOfKillReplay( player, methodOfDeath )
- bool shouldDoReplay = Replay_IsEnabled() && KillcamsEnabled() && ShouldDoReplay( player, attacker, replayLength, methodOfDeath )
+ bool shouldDoReplay = Replay_IsEnabled() && KillcamsEnabled() && IsValid( attacker ) && ShouldDoReplay( player, attacker, replayLength, methodOfDeath )
table replayTracker = { validTime = null }
if ( shouldDoReplay )
thread TrackDestroyTimeForReplay( attacker, replayTracker )