From 0f71d94b6c968ddb34ec611d898cceea02638f99 Mon Sep 17 00:00:00 2001 From: BobTheBob <32057864+BobTheBob9@users.noreply.github.com> Date: Sun, 17 Oct 2021 22:29:19 +0100 Subject: finish burncard impl, add forced 1p fp sequences and classic rodeo --- .../mod/scripts/vscripts/burnmeter/_burnmeter.gnut | 267 +++++++++++++++------ .../vscripts/earn_meter/sv_earn_meter_mp.gnut | 50 +++- .../vscripts/gamemodes/_gamemode_coliseum.nut | 19 +- 3 files changed, 253 insertions(+), 83 deletions(-) (limited to 'Northstar.CustomServers') diff --git a/Northstar.CustomServers/mod/scripts/vscripts/burnmeter/_burnmeter.gnut b/Northstar.CustomServers/mod/scripts/vscripts/burnmeter/_burnmeter.gnut index fa6867b8..d4bc38d4 100644 --- a/Northstar.CustomServers/mod/scripts/vscripts/burnmeter/_burnmeter.gnut +++ b/Northstar.CustomServers/mod/scripts/vscripts/burnmeter/_burnmeter.gnut @@ -1,6 +1,8 @@ untyped global function BurnMeter_Init +global function ForceSetGlobalBurncardOverride +global function GetSelectedBurncardRefFromWeaponOrPlayer global function RunBurnCardUseFunc global function UseBurnCardWeapon global function UseBurnCardWeaponInCriticalSection @@ -8,10 +10,20 @@ global function BurnMeter_GiveRewardDirect global function GetBurnCardWeaponSkin global function InitBurnMeterPersistentData +const float PHASE_REWIND_LENGTH = 2.0 // taken from wraith portal in apex, assuming it's the same as tf2's const float PHASE_REWIND_PATH_SNAPSHOT_INTERVAL = 0.1 +const int PHASE_REWIND_MAX_SNAPSHOTS = int( PHASE_REWIND_LENGTH / PHASE_REWIND_PATH_SNAPSHOT_INTERVAL ) + const float AMPED_WEAPONS_LENGTH = 30.0 +const int MAPHACK_PULSE_COUNT = 4 +const float MAPHACK_PULSE_DELAY = 2.0 + +struct { + string forcedGlobalBurncardOverride = "" +} file + void function BurnMeter_Init() { // turret precaches @@ -24,35 +36,97 @@ void function BurnMeter_Init() 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 = + BurnReward_GetByRef( "burnmeter_maphack" ).rewardAvailableCallback = PlayerUsesMaphackBurncard + BurnReward_GetByRef( "burnmeter_phase_rewind" ).rewardAvailableCallback = PlayerUsesPhaseRewindBurncard - // these ones aren't so important - BurnReward_GetByRef( "burnmeter_nuke_titan" ).rewardAvailableCallback = PlayerUsesNukeTitanBurncard // unused in vanilla, fun though + // these ones aren't so important, they're either for fd ( unsupported rn ) or unused //BurnReward_GetByRef( "burnmeter_harvester_shield" ).rewardAvailableCallback = BurnReward_GetByRef( "burnmeter_rodeo_grenade" ).rewardAvailableCallback = PlayerUsesRodeoGrenadeBurncard + BurnReward_GetByRef( "burnmeter_nuke_titan" ).rewardAvailableCallback = PlayerUsesNukeTitanBurncard // unused in vanilla, fun though // setup player callbacks AddCallback_GameStateEnter( eGameState.Playing, InitBurncardsForIntroPlayers ) AddCallback_OnClientConnected( InitBurncardsForLateJoiner ) AddCallback_OnPlayerRespawned( StartPhaseRewindLifetime ) + + // necessary signals + RegisterSignal( "StopAmpedWeapons" ) } -void function InitPlayerBurncards( entity player ) +void function ForceSetGlobalBurncardOverride( string ref ) { - int burnmeterSlot = player.GetPersistentVarAsInt( "burnmeterSlot" ) - player.SetPlayerNetInt( TOP_INVENTORY_ITEM_BURN_CARD_ID, burnmeterSlot ) + file.forcedGlobalBurncardOverride = ref +} + +string function GetSelectedBurncardRefFromWeaponOrPlayer( 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 ) + + if ( file.forcedGlobalBurncardOverride.len() > 0 ) + ref = file.forcedGlobalBurncardOverride - if ( BurnReward_GetById( burnmeterSlot ).ref == "burnmeter_phase_rewind" ) + if ( IsValid( weapon ) ) { - player.s.hasPhaseRewind <- true - - if ( IsAlive( player ) ) - thread PhaseRewindLifetime( 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 + // this does crash with the burnmeter_doublexp mod, but who cares, it doesn't get hit normally + 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 and don't call burncard code at all, likely for tf1 infinite stim/cloak burncards? + + default: + print( "tried to use unknown burncard weapon " + weapon.GetWeaponClassName() ) + return "burnmeter_amped_weapons" + } + } } - else - player.s.hasPhaseRewind <- false + + return ref +} + +void function InitPlayerBurncards( entity player ) +{ + string ref = GetSelectedBurncardRefFromWeaponOrPlayer( null, player ) + BurnReward reward = BurnReward_GetByRef( ref ) + player.SetPlayerNetInt( TOP_INVENTORY_ITEM_BURN_CARD_ID, reward.id ) + + if ( IsAlive( player ) ) + thread PhaseRewindLifetime( player ) } void function InitBurncardsForIntroPlayers() @@ -71,8 +145,7 @@ void function InitBurncardsForLateJoiner( entity player ) void function StartPhaseRewindLifetime( entity player ) { - if ( "hasPhaseRewind" in player.s && player.s.hasPhaseRewind ) - thread PhaseRewindLifetime( player ) + thread PhaseRewindLifetime( player ) } void function PhaseRewindLifetime( entity player ) @@ -82,12 +155,28 @@ void function PhaseRewindLifetime( entity player ) OnThreadEnd( function() : ( player ) { - + player.p.burnCardPhaseRewindStruct.phaseRetreatSavedPositions.clear() }) while ( true ) { + PhaseRewindData rewindData + rewindData.origin = player.GetOrigin() + rewindData.angles = player.GetAngles() + rewindData.velocity = player.GetVelocity() + rewindData.wasInContextAction = player.ContextAction_IsActive() + rewindData.wasCrouched = player.IsCrouched() + if ( player.p.burnCardPhaseRewindStruct.phaseRetreatSavedPositions.len() >= PHASE_REWIND_MAX_SNAPSHOTS ) + { + // shift all snapshots left + for ( int i = 0; i < PHASE_REWIND_MAX_SNAPSHOTS - 1; i++ ) + player.p.burnCardPhaseRewindStruct.phaseRetreatSavedPositions[ i ] = player.p.burnCardPhaseRewindStruct.phaseRetreatSavedPositions[ i + 1 ] + + player.p.burnCardPhaseRewindStruct.phaseRetreatSavedPositions[ PHASE_REWIND_MAX_SNAPSHOTS - 1 ] = rewindData + } + else + player.p.burnCardPhaseRewindStruct.phaseRetreatSavedPositions.append( rewindData ) wait PHASE_REWIND_PATH_SNAPSHOT_INTERVAL } @@ -100,62 +189,9 @@ void function RunBurnCardUseFunc( entity player, string itemRef ) ( 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 ) + string ref = GetSelectedBurncardRefFromWeaponOrPlayer( weapon, player ) Remote_CallFunction_Replay( player, "ServerCallback_RewardUsed", BurnReward_GetByRef( ref ).id ) RunBurnCardUseFunc( player, ref ) @@ -163,12 +199,12 @@ void function UseBurnCardWeapon( entity weapon, entity player ) // 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 ) + player.TakeWeapon( BurnReward_GetByRef( ref ).weaponName ) } void function UseBurnCardWeaponInCriticalSection( entity weapon, entity ownerPlayer ) { - // ignoring critical section stuff, assuming it was necessary in tf1 where burncards were part of inventory + // ignoring critical section stuff, assuming it was necessary in tf1 where burncards were part of inventory, but not here UseBurnCardWeapon( weapon, ownerPlayer ) } @@ -179,7 +215,7 @@ void function BurnMeter_GiveRewardDirect( entity player, string itemRef ) int function GetBurnCardWeaponSkin( entity weapon ) { - return GetBoostSkin( GetBurncardRefFromWeaponOrPlayer( weapon, weapon.GetOwner() ) ) + return GetBoostSkin( GetSelectedBurncardRefFromWeaponOrPlayer( weapon, weapon.GetOwner() ) ) } // stub @@ -252,6 +288,77 @@ void function PlayerUsesRadarJammerBurncard( entity player ) } } +void function PlayerUsesMaphackBurncard( entity player ) +{ + thread PlayerUsesMaphackBurncardThreaded( player ) +} + +void function PlayerUsesMaphackBurncardThreaded( entity player ) +{ + player.EndSignal( "OnDestroy" ) + player.EndSignal( "OnDeath" ) + + // todo: potentially look into ScanMinimap in _passives for doing this better? boost is pretty likely based off it pretty heavily + for ( int i = 0; i < MAPHACK_PULSE_COUNT; i++ ) + { + EmitSoundOnEntityOnlyToPlayer( player, player, "Burn_Card_Map_Hack_Radar_Pulse_V1_1P" ) + + foreach ( entity otherPlayer in GetPlayerArray() ) + { + Remote_CallFunction_Replay( otherPlayer, "ServerCallback_SonarPulseFromPosition", player.GetOrigin().x, player.GetOrigin().y, player.GetOrigin().z, SONAR_GRENADE_RADIUS ) + + if ( otherPlayer.GetTeam() != player.GetTeam() ) + { + StatusEffect_AddTimed( player, eStatusEffect.maphack_detected, 1.0, MAPHACK_PULSE_DELAY / 2, 0.0 ) + SonarStart( otherPlayer, player.GetOrigin(), player.GetTeam(), player ) + } + } + + wait MAPHACK_PULSE_DELAY + } +} + +void function PlayerUsesPhaseRewindBurncard( entity player ) +{ + thread PlayerUsesPhaseRewindBurncardThreaded( player ) +} + +void function PlayerUsesPhaseRewindBurncardThreaded( entity player ) +{ + player.EndSignal( "OnDestroy" ) + player.EndSignal( "OnDeath" ) + + entity mover = CreateScriptMover( player.GetOrigin(), player.GetAngles() ) + player.SetParent( mover, "REF" ) + + OnThreadEnd( function() : ( player, mover ) + { + CancelPhaseShift( player ) + player.DeployWeapon() + player.SetPredictionEnabled( true ) + player.ClearParent() + ViewConeFree( player ) + mover.Destroy() + }) + + array positions = clone player.p.burnCardPhaseRewindStruct.phaseRetreatSavedPositions + + ViewConeZero( player ) + player.HolsterWeapon() + player.SetPredictionEnabled( false ) + PhaseShift( player, 0.0, positions.len() * PHASE_REWIND_PATH_SNAPSHOT_INTERVAL * 1.5 ) + + for ( int i = positions.len() - 1; i > -1; i-- ) + { + mover.NonPhysicsMoveTo( positions[ i ].origin, PHASE_REWIND_PATH_SNAPSHOT_INTERVAL, 0, 0 ) + mover.NonPhysicsRotateTo( positions[ i ].angles, PHASE_REWIND_PATH_SNAPSHOT_INTERVAL, 0, 0 ) + wait PHASE_REWIND_PATH_SNAPSHOT_INTERVAL + } + + // this isn't vanilla but it's cool lol, should prolly remove it tho + player.SetVelocity( -positions[ positions.len() - 1 ].velocity ) +} + void function PlayerUsesNukeTitanBurncard( entity player ) { thread PlayerUsesNukeBurncardThreaded( player ) @@ -292,5 +399,15 @@ void function PlayerUsesNukeBurncardThreaded( entity player ) void function PlayerUsesRodeoGrenadeBurncard( entity player ) { - player.SetPlayerNetInt( "numSuperRodeoGrenades", 7player.GetPlayerNetInt( "numSuperRodeoGrenades" ) + 1 ) + player.SetPlayerNetInt( "numSuperRodeoGrenades", player.GetPlayerNetInt( "numSuperRodeoGrenades" ) + 1 ) +} + +// unused burncard that's mentioned in a few areas and has a validiation function in sh_burnmeter ( BurnMeter_SummonReaperCanUse ), thought it'd be neat to add it +void function PlayerUsesReaperfallBurncard( entity player ) +{ + Point spawnpoint = GetTitanReplacementPoint( player, false ) + entity reaper = CreateSuperSpectre( player.GetTeam(), spawnpoint.origin, spawnpoint.angles ) + DispatchSpawn( reaper ) + + thread SuperSpectre_WarpFall( reaper ) } \ No newline at end of file diff --git a/Northstar.CustomServers/mod/scripts/vscripts/earn_meter/sv_earn_meter_mp.gnut b/Northstar.CustomServers/mod/scripts/vscripts/earn_meter/sv_earn_meter_mp.gnut index b41640ad..b9a8fc7c 100644 --- a/Northstar.CustomServers/mod/scripts/vscripts/earn_meter/sv_earn_meter_mp.gnut +++ b/Northstar.CustomServers/mod/scripts/vscripts/earn_meter/sv_earn_meter_mp.gnut @@ -1,3 +1,4 @@ +untyped global function Sv_EarnMeterMP_Init global function EarnMeterMP_SetTitanLoadout global function EarnMeterMP_SetPassiveMeterGainEnabled @@ -32,11 +33,19 @@ void function SetupPlayerEarnMeter( entity player ) { PlayerEarnMeter_Reset( player ) - // todo: need to do burnmeter stuff here ( e.g. rewards/boosts ) + if ( Riff_BoostAvailability() != eBoostAvailability.Disabled ) + { + string burncardRef = GetSelectedBurncardRefFromWeaponOrPlayer( null, player ) + PlayerEarnMeter_SetReward( player, EarnObject_GetByRef( burncardRef ) ) // pretty sure this works? + PlayerEarnMeter_SetRewardFrac( player, BurnReward_GetByRef( burncardRef ).cost ) + PlayerEarnMeter_EnableReward( player ) + } + if ( EarnMeterMP_IsTitanEarnGametype() ) + { PlayerEarnMeter_SetGoal( player, EarnObject_GetByRef( GetTitanLoadoutForPlayer( player ).titanClass ) ) - - PlayerEarnMeter_EnableGoal( player ) // prevents goalstate from being set incorrectly + PlayerEarnMeter_EnableGoal( player ) // prevents goalstate from being set incorrectly + } // catchup bonus for late joiners // todo: maths on this is fine but for some reason it won't set correctly, could be getting reset somewhere? @@ -49,15 +58,18 @@ void function OnPlaying() foreach ( entity player in GetPlayerArray() ) SetupPlayerEarnMeter( player ) + if ( Riff_BoostAvailability() != eBoostAvailability.Disabled ) + SetCallback_EarnMeterRewardEarned( EarnMeterMP_BoostEarned ) + // do this in playing so that gamemodes/maps can disable and this'll take affect if ( EarnMeterMP_IsTitanEarnGametype() ) // settitanavailable when earnmeter full { Riff_ForceTitanAvailability( eTitanAvailability.Custom ) // doesn't seem to affect anything aside from preventing some annoying client stuff svGlobal.titanAvailabilityCheck = IsTitanAvailable - AddEarnMeterThresholdEarnedCallback( 1.0, void function( entity player ) { SetTitanAvailable( player ) }, true ) + SetCallback_EarnMeterGoalEarned( EarnMeterMP_TitanEarned ) } else // if no titans from earnmeter in this mode, just reset when we finish meter - AddEarnMeterThresholdEarnedCallback( 1.0, PlayerEarnMeter_Reset, true ) + SetCallback_EarnMeterGoalEarned( PlayerEarnMeter_Reset ) } void function OnPlayerRespawned( entity player ) @@ -97,7 +109,14 @@ void function EarnMeterMP_PlayerLifeThink( entity player ) if ( desiredEarnMeterMode == eEarnMeterMode.DEFAULT ) { if ( !IsTitanAvailable( player ) && PlayerEarnMeter_GetOwnedFrac( player ) == 1.0 ) // this should only be the case after player has dropped their titan + { PlayerEarnMeter_Reset( player ) + // reset rewards + string burncardRef = GetSelectedBurncardRefFromWeaponOrPlayer( null, player ) + PlayerEarnMeter_SetReward( player, EarnObject_GetByRef( burncardRef ) ) + PlayerEarnMeter_SetRewardFrac( player, BurnReward_GetByRef( burncardRef ).cost ) + PlayerEarnMeter_EnableReward( player ) + } if ( PlayerEarnMeter_GetRewardFrac( player ) != 0 ) PlayerEarnMeter_EnableReward( player ) @@ -133,4 +152,25 @@ void function EarnMeterMP_PlayerLifeThink( entity player ) WaitFrame() } +} + +void function EarnMeterMP_BoostEarned( entity player ) +{ + BurnReward burncard = BurnReward_GetByRef( GetSelectedBurnCardRef( player ) ) + + while ( burncard.ref == "burnmeter_random_foil" ) + burncard = BurnReward_GetRandom() + + array mods = [ "burn_card_weapon_mod" ] + if ( burncard.extraWeaponMod != "" ) + mods.append( burncard.extraWeaponMod ) + + player.GiveOffhandWeapon( burncard.weaponName, OFFHAND_INVENTORY, mods ) + Remote_CallFunction_Replay( player, "ServerCallback_RewardReadyMessage", player.s.respawnTime ) +} + +void function EarnMeterMP_TitanEarned( entity player ) +{ + SetTitanAvailable( player ) + //Remote_CallFunction_Replay( player, "ServerCallback_TitanReadyMessage" ) // broken for some reason } \ No newline at end of file diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_coliseum.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_coliseum.nut index b1de4d4f..6198c5db 100644 --- a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_coliseum.nut +++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_coliseum.nut @@ -3,7 +3,20 @@ untyped global function GamemodeColiseum_Init global function GamemodeColiseum_CustomIntro -bool hasShownIntroScreen = false +// outro anims +// winner anims are for the winner, loser anims are for the loser +// the loss number refers to the number of rounds the winner lost +const array OUTROANIMS_WINNER_0LOSS = [ "pt_coliseum_winner_gunkick", "pt_coliseum_winner_compassion", "pt_coliseum_winner_drinking" ] +const array OUTROANIMS_WINNER_1LOSS = [ "pt_coliseum_winner_respect", "pt_coliseum_winner_headlock", "pt_coliseum_winner_authority" ] +const array OUTROANIMS_WINNER_2LOSS = [ "pt_coliseum_winner_punch", "pt_coliseum_winner_kick", "pt_coliseum_winner_stomp" ] + +const array OUTROANIMS_LOSER_0LOSS = [ "pt_coliseum_loser_gunkick", "pt_coliseum_loser_compassion", "pt_coliseum_loser_drinking" ] +const array OUTROANIMS_LOSER_1LOSS = [ "pt_coliseum_loser_respect", "pt_coliseum_loser_headlock", "pt_coliseum_loser_authority" ] +const array OUTROANIMS_LOSER_2LOSS = [ "pt_coliseum_loser_punch", "pt_coliseum_loser_kick", "pt_coliseum_loser_stomp" ] + +struct { + bool hasShownIntroScreen +} file void function GamemodeColiseum_Init() { @@ -28,10 +41,10 @@ void function GamemodeColiseum_CustomIntro( entity player ) void function ShowColiseumIntroScreen() { - if ( !hasShownIntroScreen ) + if ( !file.hasShownIntroScreen ) thread ShowColiseumIntroScreenThreaded() - hasShownIntroScreen = true + file.hasShownIntroScreen = true } void function ShowColiseumIntroScreenThreaded() -- cgit v1.2.3