diff options
Diffstat (limited to 'Northstar.Custom/scripts/vscripts')
35 files changed, 5188 insertions, 0 deletions
diff --git a/Northstar.Custom/scripts/vscripts/_northstar_devcommands.gnut b/Northstar.Custom/scripts/vscripts/_northstar_devcommands.gnut new file mode 100644 index 00000000..ebac1e3c --- /dev/null +++ b/Northstar.Custom/scripts/vscripts/_northstar_devcommands.gnut @@ -0,0 +1,60 @@ +untyped +global function NorthstarDevCommands_Init + +void function NorthstarDevCommands_Init() +{ + AddClientCommandCallback( "noclip", ClientCommandCallbackToggleNoclip ) + AddClientCommandCallback( "script", ClientCommandCallbackEvalScript ) + AddClientCommandCallback( "kill", ClientCommandCallbackKill ) +} + +bool function ClientCommandCallbackToggleNoclip( entity player, array<string> args ) +{ + if ( GetConVarInt( "sv_cheats" ) != 1 ) + return true + + //if ( player.IsNoclipping() ) + // DisableNoclipForEntityIndex( player.GetIndexForEntity() ) + //else + // EnableNoclipForEntityIndex( player.GetIndexForEntity() ) + + // new way that doesn't require native stuff yay + if ( player.IsNoclipping() ) + player.SetPhysics( MOVETYPE_WALK ) // just hoping this is the right movetype, not much of a way to check + else + player.SetPhysics( MOVETYPE_NOCLIP ) + + return true +} + +bool function ClientCommandCallbackEvalScript( entity player, array<string> args ) +{ + if ( args.len() < 1 || GetConVarInt( "sv_cheats" ) != 1 ) + return true + + // todo: rewrite this at some point to use a concommand because clientcommands can't just take in a single string with spaces, quotes etc + // should just have the concommand call a clientcommand manually with properly formatted args + string joinedArgs = args[0] + for ( int i = 1; i < args.len(); i++ ) + joinedArgs += " " + args[i] + + try + { + compilestring( joinedArgs )() + } + catch (exception) + { + // should probably send this to the client at some point + // no need to log here because compilestring errors already do that + } + + return true +} + +bool function ClientCommandCallbackKill( entity player, array<string> args ) +{ + if ( IsAlive( player ) && GetConVarInt( "sv_cheats" ) == 1 ) + player.Die() + + return true +}
\ No newline at end of file diff --git a/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_arena.gnut b/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_arena.gnut new file mode 100644 index 00000000..4e6217e8 --- /dev/null +++ b/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_arena.gnut @@ -0,0 +1,99 @@ +global function GameModeArena_Init + +struct { + entity imcBoostStore + entity militiaBoostStore + + entity imcShield + entity militiaShield +} file + +void function GameModeArena_Init() +{ + AddCallback_EntitiesDidLoad( CreateBoostStores ) + AddCallback_GameStateEnter( eGameState.Prematch, StartBuyPhase ) + AddCallback_GameStateEnter( eGameState.Playing, FinishBuyPhase ) + + // todo: need a custom intro for this that allows players to move, buy etc in prematch + // if that's actually possible lol not sure it is +} + +void function CreateBoostStores() +{ + array<entity> startspawns = GetEntArrayByClass_Expensive( "info_spawnpoint_human_start" ) // easier to do this than use a spawn callback imo + + vector imcAverageOrigin + float imcAverageAngle + int imcNumSpawns + + vector militiaAverageOrigin + float militiaAverageAngle + int militiaNumSpawns + + foreach ( entity startspawn in startspawns ) + { + if ( startspawn.GetTeam() == TEAM_IMC ) + { + imcAverageOrigin += startspawn.GetOrigin() + imcAverageAngle += startspawn.GetAngles().y + imcNumSpawns++ + } + else + { + militiaAverageOrigin += startspawn.GetOrigin() + militiaAverageAngle += startspawn.GetAngles().y + militiaNumSpawns++ + } + } + + // create imc boost store + vector finalPositionImc = < imcAverageOrigin.x / imcNumSpawns, imcAverageOrigin.y / imcNumSpawns, imcAverageOrigin.z / imcNumSpawns > + finalPositionImc += ( 200 * AnglesToForward( < 0, imcAverageAngle / imcNumSpawns, 0 > ) ) + CreateBoostStoreLocation( TEAM_IMC, finalPositionImc, < 0, 0, 0 >, true ) + + vector finalPositionMilitia = < militiaAverageOrigin.x / militiaNumSpawns, militiaAverageOrigin.y / militiaNumSpawns, militiaAverageOrigin.z / militiaNumSpawns > + finalPositionMilitia += ( 200 * AnglesToForward( < 0, militiaAverageAngle / militiaNumSpawns, 0 > ) ) + CreateBoostStoreLocation( TEAM_MILITIA, finalPositionMilitia, < 0, 0, 0 >, true ) + + // createbooststorelocation is void so have to do this + // also boost store code is just fully fucked lol, teams only get set on open so can't compare teams at this point + // sorry if someone else makes their own boost stores lol this'll just break + // if there's some way to get the invisible crates used for boost stores i will be very happy + + if ( GetBoostStores().len() != 2 ) + print( "_gamemode_arena.gnut: there are more than 2 boost stores, very bad no good" ) + + file.imcBoostStore = GetBoostStores()[0] + file.militiaBoostStore = GetBoostStores()[1] +} + +void function StartBuyPhase() +{ + //file.imcShield = CreateBubbleShieldWithSettings( TEAM_IMC, file.imcBoostStore.GetOrigin(), <0,0,0>, null, 15.0 ) + //file.militiaShield = CreateBubbleShieldWithSettings( TEAM_MILITIA, file.militiaBoostStore.GetOrigin(), <0,0,0>, null, 15.0 ) + + entity bubbleShield = CreateEntity( "prop_dynamic" ) + bubbleShield.SetValueForModelKey( $"models/fx/xo_shield.mdl" ) + bubbleShield.kv.solid = 0 + bubbleShield.kv.rendercolor = "255 255 255" // white + bubbleShield.kv.modelscale = 2.25 + bubbleShield.SetOrigin( file.imcBoostStore.GetOrigin() ) + DispatchSpawn( bubbleShield ) + + file.imcShield = bubbleShield + + //SetTeam( bubbleShield, TEAM_IMC ) + + // current problem, there is seemingly no way of getting a shield we can resize which actually resizes the collision + // could probably just damage players that try to leave lol + + OpenBoostStores() +} + +void function FinishBuyPhase() +{ + file.imcShield.Destroy() + //file.militiaShield.Destroy() + + CloseBoostStores() +}
\ No newline at end of file diff --git a/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_fastball.gnut b/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_fastball.gnut new file mode 100644 index 00000000..52b563d4 --- /dev/null +++ b/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_fastball.gnut @@ -0,0 +1,194 @@ +untyped +global function GamemodeFastball_Init + +struct { + // first panel is a, second is b, third is c + array<Point> panelSpawns +} file + +void function GamemodeFastball_Init() +{ + // used for intro + PrecacheModel( $"models/titans/buddy/titan_buddy.mdl" ) + PrecacheParticleSystem( $"P_BT_eye_SM" ) + + // used for respawn + PrecacheParticleSystem( $"P_pod_screen_lasers_OUT" ) + + SetRoundBased( true ) + SetRespawnsEnabled( false ) + Riff_ForceSetEliminationMode( eEliminationMode.Pilots ) + + // implementing intro in a different file because it'll likely be big + ClassicMP_SetCustomIntro( GamemodeFastballIntroSetup, 14.5 ) // bit of a guess number + AddCallback_EntitiesDidLoad( SpawnPanelsForLevel ) + AddCallback_GameStateEnter( eGameState.Prematch, ResetPanels ) + SetTimeoutWinnerDecisionFunc( FastballDecideWinner ) + + AddCallback_OnClientConnected( FastballInitPlayer ) + AddCallback_OnPlayerKilled( FastballOnPlayerKilled ) // move this to a system in _gamestate soon!! + + // setup spawns + // first is a, second is b, third is c + + FastballAddBuddySpawnForLevel( "mp_angel_city", TEAM_IMC, < 2281.39, -3333.06, 200.031 >, < 0, 91.23, 0 > ) + FastballAddBuddySpawnForLevel( "mp_angel_city", TEAM_MILITIA, < -4139.57, 4684.4, 41.0313 >, <0, -14.326, 0 > ) + FastballAddPanelSpawnsForLevel( "mp_angel_city", [ + < 2055.94, 2040.03, 128.031 >, < 0, -180, 0 >, + < -274.757, 2255.22, 400.031 >, < 0, -176.251, 0 >, + < -3208.28, 2741.17, 128.031 >, < 0, -0.0821686, 0 > + ]) + + FastballAddBuddySpawnForLevel( "mp_thaw", TEAM_MILITIA, < 2049.29, -4085.22, -274.839 >, < 0, 89.2991, 0 > ) + FastballAddBuddySpawnForLevel( "mp_thaw", TEAM_IMC, < 834.484, 2664.28, -380.515 >, < 0.532141, -90.875, -0.542593 > ) + FastballAddPanelSpawnsForLevel( "mp_thaw", [ + < -1026.71, -1691.93, -319.969 >, < 0, 90, 0 >, + < 1836.07, -538.823, -64.1245 >, < 0, -135, 0 >, + < 2840.15, 1321.17, -63.9688 >, < 0, 0, 0 > + ]) + + FastballAddBuddySpawnForLevel( "mp_wargames", TEAM_MILITIA, < -4848.87, 682.17, -127.969 >, < 0, 0, 0 > ) + FastballAddBuddySpawnForLevel( "mp_wargames", TEAM_IMC, < 2960.78, 1229.36, -127.969 >, < 7.89891e-005, 146.505, 1.38387e-005 > ) + FastballAddPanelSpawnsForLevel( "mp_wargames", [ + < -691.961, 1888.56, 112.031 >, < 0, 90, 0 >, + < -1072.03, -508.229, -127.969 >, < 0, 180, 0 >, + < -24.291, -1403.69, -119.969 >, < 0, -90, 0 > + ]) + + FastballAddBuddySpawnForLevel( "mp_eden", TEAM_MILITIA, < -2404.4, 1738.07, 167.767 >, <0, -40.0894, 0> ) + FastballAddBuddySpawnForLevel( "mp_eden", TEAM_IMC, < 5248.01, 414.698, 77.1051 >, < 0, 180, 0 > ) + FastballAddPanelSpawnsForLevel( "mp_eden", [ + < 704.333, 1530.18, 144.031 >, < 0, 90, 0 >, + < -108.43, 272.638, 72.0313 >, < 0, -90, 0 >, + <1044.25, -1145.68, 68.0313>, < 0, 180, 0 > + ]) + + FastballAddBuddySpawnForLevel( "mp_black_water_canal", TEAM_MILITIA, < 1222.88, -5050.63, -187.763 >, < 0, 45, 0 > ) + FastballAddBuddySpawnForLevel( "mp_black_water_canal", TEAM_IMC, < -235.375, 4736.75, -255.969 >, < 0, -90, 0 > ) + FastballAddPanelSpawnsForLevel( "mp_black_water_canal", [ + < 1566.13, -731.539, -63.9688 >, < 0, -90, 0 >, + < 502.603, 1102.92, 260.031 >, < 0, 152.328, 0 >, + < 2337.37, 2099.91, -26.9688 >, < 0, 0, 0 > + ]) + + FastballAddBuddySpawnForLevel( "mp_grave", TEAM_MILITIA, < 11026.8, -5163.18, 1885.64 >, < 0, 155.05, 0 > ) + FastballAddBuddySpawnForLevel( "mp_grave", TEAM_IMC, < -1952, -3120, 1993.33 >, < 0, 0, 0 > ) + FastballAddPanelSpawnsForLevel( "mp_grave", [ + < 5204.54, -2726.54, 2376.03 >, < 0, 90, 0 >, + < 6001.58, -4126.61, 2252.03 >, < 0, -135, 0 >, + < 3595.96, -4568.04, 2376.03 >, < 0, -135, 0 > + ]) +} + +void function SpawnPanelsForLevel() +{ + int panelId + foreach ( Point panelSpawn in file.panelSpawns ) + { + entity panel = CreatePanel( panelSpawn.origin, panelSpawn.angles ) + panel.s.panelId <- panelId++ + } +} + +void function ResetPanels() +{ + foreach ( entity panel in GetAllControlPanels() ) + SetTeam( panel, TEAM_UNASSIGNED ) +} + +void function FastballInitPlayer( entity player ) +{ + foreach ( entity panel in GetAllControlPanels() ) + Remote_CallFunction_NonReplay( player, "ServerCallback_FastballUpdatePanelRui", panel.GetEncodedEHandle(), panel.s.panelId ) +} + +void function FastballAddPanelSpawnsForLevel( string level, array<vector> positionsAndOrigins ) +{ + if ( GetMapName() != level ) + return + + for ( int i = 0; i < positionsAndOrigins.len(); i += 2 ) + { + Point spawnPoint + spawnPoint.origin = positionsAndOrigins[ i ] + spawnPoint.angles = positionsAndOrigins[ i + 1 ] + + file.panelSpawns.append( spawnPoint ) + } +} + +entity function CreatePanel( vector origin, vector angles ) +{ + entity panel = CreateEntity( "prop_control_panel" ) + panel.SetValueForModelKey( $"models/communication/terminal_usable_imc_01.mdl" ) + panel.SetOrigin( origin ) + panel.SetAngles( angles ) + panel.kv.solid = SOLID_VPHYSICS + DispatchSpawn( panel ) + + panel.SetModel( $"models/communication/terminal_usable_imc_01.mdl" ) + panel.s.onPlayerFinishesUsing_func = FastballOnPanelHacked + + Highlight_SetNeutralHighlight( panel, "sp_enemy_pilot" ) + + return panel +} + +// control panel code isn't very statically typed, pain +function FastballOnPanelHacked( panel, player, success ) +{ + expect entity( panel ) + expect entity( player ) + expect bool( success ) + + if ( !success ) + return + + print( panel + " was hacked by " + player ) + PanelFlipsToPlayerTeamAndUsableByEnemies( panel, player ) + player.SetPlayerGameStat( PGS_ASSAULT_SCORE, player.GetPlayerGameStat( PGS_ASSAULT_SCORE ) + 1 ) + + foreach ( entity otherPlayer in GetPlayerArray() ) + Remote_CallFunction_NonReplay( otherPlayer, "ServerCallback_FastballPanelHacked", panel.GetEncodedEHandle(), panel.s.panelId, player.GetEncodedEHandle() ) + + // respawn dead players + foreach ( entity deadPlayer in GetPlayerArrayOfTeam( player.GetTeam() ) ) + { + if ( !IsAlive( deadPlayer ) ) + { + deadPlayer.SetOrigin( player.GetOrigin() ) + deadPlayer.RespawnPlayer( null ) + Remote_CallFunction_NonReplay( deadPlayer, "ServerCallback_FastballRespawnPlayer" ) + } + } +} + +void function FastballOnPlayerKilled( entity victim, entity attacker, var damageInfo ) +{ + if ( !victim.IsPlayer() || GetGameState() != eGameState.Playing ) + return + + if ( GetPlayerArrayOfTeam_Alive( victim.GetTeam() ).len() == 0 ) + SetWinner( GetOtherTeam( victim.GetTeam() ) ) +} + +int function FastballDecideWinner() +{ + int militiaPanels + int imcPanels + + foreach ( entity panel in GetAllControlPanels() ) + { + if ( panel.GetTeam() == TEAM_MILITIA ) + militiaPanels++ + else if ( panel.GetTeam() == TEAM_IMC ) + imcPanels++ + } + + if ( militiaPanels > imcPanels ) + return TEAM_MILITIA + else if ( imcPanels > militiaPanels ) + return TEAM_IMC + + return TEAM_UNASSIGNED +}
\ No newline at end of file diff --git a/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_fastball_intro.gnut b/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_fastball_intro.gnut new file mode 100644 index 00000000..6a1d0bbd --- /dev/null +++ b/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_fastball_intro.gnut @@ -0,0 +1,208 @@ +untyped +global function GamemodeFastballIntroSetup +global function FastballAddBuddySpawnForLevel + +struct { + float introStartTime + + table< int, Point > buddySpawns + entity militiaBuddy + entity imcBuddy + + table<entity, bool> playersHoldingJump +} file + +void function GamemodeFastballIntroSetup() +{ + RegisterSignal( "fastball_start_throw" ) + RegisterSignal( "fastball_release" ) + + AddCallback_GameStateEnter( eGameState.Prematch, OnPrematchStart ) + AddCallback_OnClientConnected( AddPlayerToFastballIntro ) +} + +void function FastballAddBuddySpawnForLevel( string level, int team, vector origin, vector angles ) +{ + if ( GetMapName() != level ) + return + + Point spawnPoint + spawnPoint.origin = origin + spawnPoint.angles = angles + + file.buddySpawns[ team ] <- spawnPoint +} + +void function OnPrematchStart() +{ + ClassicMP_OnIntroStarted() + + file.introStartTime = Time() + file.playersHoldingJump = {} // clear it + + Point militiaBuddySpawn + Point imcBuddySpawn + + // figure out positions if there's none manually specified + if ( file.buddySpawns.len() == 0 ) + { + array<entity> militiaSpawns + array<entity> imcSpawns + + foreach ( entity spawnpoint in GetEntArrayByClass_Expensive( "info_spawnpoint_titan_start" ) ) + { + // trace from top to bottom + float result = TraceHullSimple( spawnpoint.GetOrigin() + < 0, 0, 250 >, spawnpoint.GetOrigin(), < -200, -200, 0 >, < 200, 200, 400 >, null ) + + // don't need to trace much, as long as it's over 0 that means it fits + // cases where it's over 0 but less than 1 are usually caused by terrain + if ( result > 0 ) + { + if ( spawnpoint.GetTeam() == TEAM_MILITIA ) + militiaSpawns.append( spawnpoint ) + else + imcSpawns.append( spawnpoint ) + } + } + + entity milititaSpawnEnt = militiaSpawns[ RandomInt( militiaSpawns.len() ) ] + militiaBuddySpawn.origin = milititaSpawnEnt.GetOrigin() + militiaBuddySpawn.angles = milititaSpawnEnt.GetAngles() + + entity imcSpawnEnt = imcSpawns[ RandomInt( imcSpawns.len() ) ] + imcBuddySpawn.origin = imcSpawnEnt.GetOrigin() + imcBuddySpawn.angles = imcSpawnEnt.GetAngles() + } + else + { + militiaBuddySpawn = file.buddySpawns[ TEAM_MILITIA ] + imcBuddySpawn = file.buddySpawns[ TEAM_IMC ] + } + + file.militiaBuddy = CreatePropDynamic( $"models/titans/buddy/titan_buddy.mdl" ) + file.militiaBuddy.SetOrigin( militiaBuddySpawn.origin ) + file.militiaBuddy.SetAngles( militiaBuddySpawn.angles ) + thread AnimateBuddy( file.militiaBuddy ) + + file.imcBuddy = CreatePropDynamic( $"models/titans/buddy/titan_buddy.mdl" ) + file.imcBuddy.SetOrigin( imcBuddySpawn.origin ) + file.imcBuddy.SetAngles( imcBuddySpawn.angles ) + thread AnimateBuddy( file.imcBuddy ) + + foreach ( entity player in GetPlayerArray() ) + thread FastballPlayer( player ) +} + +void function AnimateBuddy( entity buddy ) +{ + print( "buddy spawn at " + buddy.GetOrigin() + " " + buddy.GetAngles() ) + + thread PlayAnim( buddy, "bt_beacon_fastball_throw_end" ) + + // play dialogue at the right time + buddy.WaitSignal( "fastball_start_throw" ) + float diagDuration = EmitSoundOnEntity( buddy, "diag_sp_spoke1_BE117_04_01_mcor_bt" ) // trust me + StartParticleEffectOnEntity( buddy, GetParticleSystemIndex( $"P_BT_eye_SM" ), FX_PATTACH_POINT_FOLLOW, buddy.LookupAttachment( "EYEGLOW" ) ) + + wait diagDuration + if ( GetGameState() != eGameState.Playing ) + ClassicMP_OnIntroFinished() + + buddy.WaitSignal( "fastball_release" ) + wait 5.0 + buddy.Destroy() +} + +void function AddPlayerToFastballIntro( entity player ) +{ + if ( GetGameState() == eGameState.Prematch ) + thread FastballPlayer( player ) +} + +void function FastballPlayer( entity player ) +{ + player.EndSignal( "OnDeath" ) + player.EndSignal( "OnDestroy" ) + + OnThreadEnd( function() : ( player ) + { + RemoveCinematicFlag( player, CE_FLAG_CLASSIC_MP_SPAWNING ) + player.ClearParent() + ClearPlayerAnimViewEntity( player ) + player.DeployWeapon() + player.PlayerCone_Disable() + + RemoveButtonPressedPlayerInputCallback( player, IN_JUMP, PlayerHoldingJumpInIntro ) + RemoveButtonReleasedPlayerInputCallback( player, IN_JUMP, PlayerNoLongerHoldingJumpInIntro ) + }) + + FirstPersonSequenceStruct throwSequence + throwSequence.attachment = "REF" + throwSequence.useAnimatedRefAttachment = true + throwSequence.hideProxy = true + throwSequence.viewConeFunction = ViewConeFastball // this seemingly does not trigger for some reason + throwSequence.firstPersonAnim = "ptpov_beacon_fastball_throw_end" + // mp models seemingly have no 3p animation for this + throwSequence.firstPersonBlendOutTime = 0.0 + throwSequence.teleport = true + throwSequence.setInitialTime = Time() - file.introStartTime + + // get our buddy + entity buddy + if ( player.GetTeam() == TEAM_MILITIA ) + buddy = file.militiaBuddy + else + buddy = file.imcBuddy + + // respawn the player + player.SetOrigin( buddy.GetOrigin() ) + player.RespawnPlayer( null ) + player.Hide() + player.HolsterWeapon() + + // hide hud, fade screen out from black + AddCinematicFlag( player, CE_FLAG_CLASSIC_MP_SPAWNING ) + ScreenFadeFromBlack( player, 0.5, 0.5 ) + + // start fp sequence + thread FirstPersonSequence( throwSequence, player, buddy ) + + // manually do this because i can't get viewconefastball to work + player.PlayerCone_FromAnim() + player.PlayerCone_SetMinYaw( -50 ) + player.PlayerCone_SetMaxYaw( 25 ) + player.PlayerCone_SetMinPitch( -15 ) + player.PlayerCone_SetMaxPitch( 15 ) + + buddy.WaitSignal( "fastball_start_throw" ) + // lock in their final angles at this point + vector throwVel = AnglesToForward( player.EyeAngles() ) * 950 + throwVel.z = 675.0 + + // allow players to gain extra height by holding jump after this point too + AddButtonPressedPlayerInputCallback( player, IN_JUMP, PlayerHoldingJumpInIntro ) + AddButtonReleasedPlayerInputCallback( player, IN_JUMP, PlayerNoLongerHoldingJumpInIntro ) + + // wait for it to finish + buddy.WaitSignal( "fastball_release" ) + + if ( player in file.playersHoldingJump && file.playersHoldingJump[ player ] ) + throwVel.z = 850.0 + + // have to correct this manually here since due to no 3p animation our position isn't set right during this sequence + player.SetOrigin( buddy.GetAttachmentOrigin( buddy.LookupAttachment( "FASTBALL_R" ) ) ) + player.Show() + player.SetVelocity( throwVel ) + + TryGameModeAnnouncement( player ) +} + +void function PlayerHoldingJumpInIntro( entity player ) +{ + file.playersHoldingJump[ player ] <- true +} + +void function PlayerNoLongerHoldingJumpInIntro( entity player ) +{ + file.playersHoldingJump[ player ] <- false +}
\ No newline at end of file diff --git a/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_gg.gnut b/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_gg.gnut new file mode 100644 index 00000000..fd41236c --- /dev/null +++ b/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_gg.gnut @@ -0,0 +1,111 @@ +global function GamemodeGG_Init + +void function GamemodeGG_Init() +{ + SetSpawnpointGamemodeOverride( FFA ) + + Evac_SetEnabled( false ) + SetLoadoutGracePeriodEnabled( false ) // prevent modifying loadouts with grace period + SetWeaponDropsEnabled( false ) + Riff_ForceTitanAvailability( eTitanAvailability.Never ) + Riff_ForceBoostAvailability( eBoostAvailability.Disabled ) + + AddCallback_OnPlayerRespawned( OnPlayerRespawned ) + AddCallback_OnPlayerKilled( OnPlayerKilled ) + + AddCallback_GameStateEnter( eGameState.WinnerDetermined, OnWinnerDetermined ) +} + +void function OnPlayerRespawned( entity player ) +{ + UpdateLoadout( player ) + thread OnPlayerRespawned_Threaded( player ) +} + +void function OnPlayerRespawned_Threaded( entity player ) +{ + // bit of a hack, need to rework earnmeter code to have better support for completely disabling it + // rn though this just waits for earnmeter code to set the mode before we set it back + WaitFrame() + PlayerEarnMeter_SetMode( player, eEarnMeterMode.DISABLED ) +} + +void function OnPlayerKilled( entity victim, entity attacker, var damageInfo ) +{ + if ( !victim.IsPlayer() || !attacker.IsPlayer() ) + return + + if ( attacker == victim ) // suicide + { + string message = victim.GetPlayerName() + " committed suicide." + foreach ( entity player in GetPlayerArray() ) + SendHudMessage( player, message, -1, 0.4, 255, 0, 0, 0, 0, 3, 0.15 ) + + if ( GameRules_GetTeamScore( victim.GetTeam() ) != 0 ) + { + AddTeamScore( victim.GetTeam(), -1 ) // get absolutely fucking destroyed lol + victim.AddToPlayerGameStat( PGS_ASSAULT_SCORE, -1 ) + } + } + else + { + if ( DamageInfo_GetDamageSourceIdentifier( damageInfo ) != eDamageSourceId.melee_pilot_emptyhanded ) + { + AddTeamScore( attacker.GetTeam(), 1 ) + attacker.AddToPlayerGameStat( PGS_ASSAULT_SCORE, 1 ) + UpdateLoadout( attacker ) + } + + if ( DamageInfo_GetDamageSourceIdentifier( damageInfo ) == eDamageSourceId.human_execution ) + { + string message = victim.GetPlayerName() + " got executed." + foreach ( entity player in GetPlayerArray() ) + SendHudMessage( player, message, -1, 0.4, 255, 0, 0, 0, 0, 3, 0.15 ) + + if ( GameRules_GetTeamScore( victim.GetTeam() ) != 0 ) + { + AddTeamScore( victim.GetTeam(), -1 ) // get absolutely fucking destroyed lol + victim.AddToPlayerGameStat( PGS_ASSAULT_SCORE, -1 ) + } + } + } +} + +void function UpdateLoadout( entity player ) +{ + int currentWeaponIndex = GameRules_GetTeamScore( player.GetTeam() ) + array<GunGameWeapon> weapons = GetGunGameWeapons() + + if ( currentWeaponIndex >= weapons.len() ) + currentWeaponIndex = weapons.len() - 1 + + if ( currentWeaponIndex > 18 ) // play end of game music for special weapons + PlayMusicToAll( eMusicPieceID.LEVEL_LAST_MINUTE ) // this *shouldn't* overlap if done multiple times + + GunGameWeapon weapon = weapons[ currentWeaponIndex ] + + foreach ( entity weapon in player.GetMainWeapons() ) + player.TakeWeaponNow( weapon.GetWeaponClassName() ) + + foreach ( entity weapon in player.GetOffhandWeapons() ) + player.TakeWeaponNow( weapon.GetWeaponClassName() ) + + if ( weapon.offhandSlot != -1 ) + { + // TEMP: give archer so player so player has a weapon which lets them use offhands + // need to replace this with a custom empty weapon at some point + player.GiveWeapon( "mp_weapon_rocket_launcher" ) + + player.GiveOffhandWeapon( weapon.weapon, weapon.offhandSlot, weapon.mods ) + } + else + player.GiveWeapon( weapon.weapon, weapon.mods ) + + player.GiveOffhandWeapon( "melee_pilot_emptyhanded", OFFHAND_MELEE ) +} + +void function OnWinnerDetermined() +{ + SetRespawnsEnabled( false ) + SetKillcamsEnabled( false ) +}
\ No newline at end of file diff --git a/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_inf.gnut b/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_inf.gnut new file mode 100644 index 00000000..b7af9b85 --- /dev/null +++ b/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_inf.gnut @@ -0,0 +1,176 @@ +global function GamemodeInfection_Init + +struct { + bool hasHadFirstInfection = false + array<entity> playersToNotifyOfInfection +} file + +void function GamemodeInfection_Init() +{ + SetSpawnpointGamemodeOverride( FFA ) + SetLoadoutGracePeriodEnabled( false ) // prevent modifying loadouts with grace period + SetWeaponDropsEnabled( false ) + Riff_ForceTitanAvailability( eTitanAvailability.Never ) + Riff_ForceBoostAvailability( eBoostAvailability.Disabled ) + + AddCallback_OnClientConnected( InfectionInitPlayer ) + AddCallback_OnPlayerKilled( InfectionOnPlayerKilled ) + AddCallback_OnPlayerRespawned( RespawnInfected ) + AddCallback_GameStateEnter( eGameState.Playing, SelectFirstInfected ) + + SetTimeoutWinnerDecisionFunc( TimeoutCheckSurvivors ) +} + +void function InfectionInitPlayer( entity player ) +{ + if ( GetGameState() < eGameState.Playing ) + SetTeam( player, INFECTION_TEAM_SURVIVOR ) + else + InfectPlayer( player ) +} + +void function SelectFirstInfected() +{ + thread SelectFirstInfectedDelayed() +} + +void function SelectFirstInfectedDelayed() +{ + wait 10.0 + RandomFloat( 5.0 ) + + array<entity> players = GetPlayerArray() + entity infected = players[ RandomInt( players.len() ) ] + + InfectPlayer( infected ) + RespawnInfected( infected ) +} + +void function InfectionOnPlayerKilled( entity victim, entity attacker, var damageInfo ) +{ + if ( !victim.IsPlayer() ) + return + + if ( victim.GetTeam() == INFECTION_TEAM_SURVIVOR ) + InfectPlayer( victim ) + + if ( attacker.IsPlayer() ) + attacker.SetPlayerGameStat( PGS_ASSAULT_SCORE, attacker.GetPlayerGameStat( PGS_ASSAULT_SCORE ) + 1 ) +} + +void function InfectPlayer( entity player ) +{ + SetTeam( player, INFECTION_TEAM_INFECTED ) + player.SetPlayerGameStat( PGS_ASSAULT_SCORE, 0 ) // reset kills + file.playersToNotifyOfInfection.append( player ) + + // check how many survivors there are + array<entity> survivors = GetPlayerArrayOfTeam( INFECTION_TEAM_SURVIVOR ) + if ( survivors.len() == 0 ) + SetWinner( INFECTION_TEAM_INFECTED ) + else if ( survivors.len() == 1 ) + SetLastSurvivor( survivors[ 0 ] ) + + if ( !file.hasHadFirstInfection ) + { + file.hasHadFirstInfection = true + + foreach ( entity otherPlayer in GetPlayerArray() ) + if ( player != otherPlayer ) + Remote_CallFunction_NonReplay( otherPlayer, "ServerCallback_AnnounceFirstInfected", player.GetEncodedEHandle() ) + + PlayMusicToAll( eMusicPieceID.GAMEMODE_1 ) + } +} + +void function RespawnInfected( entity player ) +{ + if ( player.GetTeam() != INFECTION_TEAM_INFECTED ) + return + + // notify newly infected players of infection + if ( file.playersToNotifyOfInfection.contains( player ) ) + { + Remote_CallFunction_NonReplay( player, "ServerCallback_YouAreInfected" ) + file.playersToNotifyOfInfection.remove( file.playersToNotifyOfInfection.find( player ) ) + } + + // set camo to pond scum + player.SetSkin( 1 ) + player.SetCamo( 110 ) + + // stats for infected + StimPlayer( player, 9999.9 ) // can't do endless since we don't get the visual effect in endless + player.SetMaxHealth( 50 ) + + // set loadout + foreach ( entity weapon in player.GetMainWeapons() ) + player.TakeWeaponNow( weapon.GetWeaponClassName() ) + + foreach ( entity weapon in player.GetOffhandWeapons() ) + player.TakeWeaponNow( weapon.GetWeaponClassName() ) + + // TEMP: give archer so player so player has a weapon which lets them use offhands + // need to replace this with a custom empty weapon at some point + //player.GiveWeapon( "mp_weapon_rocket_launcher" ) + player.GiveWeapon( "mp_weapon_mgl" ) + player.GiveOffhandWeapon( "melee_pilot_emptyhanded", OFFHAND_MELEE ) + + thread PlayInfectedSounds( player ) +} + +void function PlayInfectedSounds( entity player ) +{ + player.EndSignal( "OnDeath" ) + player.EndSignal( "OnDestroy" ) + + float nextRandomSound + while ( true ) + { + WaitFrame() + + int meleeState = player.PlayerMelee_GetState() + if ( nextRandomSound < Time() || meleeState != 0 ) + { + string selectedSound + if ( CoinFlip() ) + selectedSound = "prowler_vocal_attack" + else + selectedSound = "prowler_vocal_attackmiss" + + bool canSeeSurvivor + foreach ( entity survivor in GetPlayerArrayOfTeam( INFECTION_TEAM_SURVIVOR ) ) + if ( TraceLineSimple( player.GetOrigin(), survivor.GetOrigin(), survivor ) == 1.0 ) + canSeeSurvivor = true + + // _int sounds are less agressive so only play them if we aren't in some sorta fight + if ( player.GetHealth() == player.GetMaxHealth() || !canSeeSurvivor || meleeState != 0 ) + selectedSound += "_int" + + EmitSoundOnEntity( player, selectedSound ) + + nextRandomSound = Time() + max( 2.5, RandomFloat( 12.0 ) ) + while ( player.PlayerMelee_GetState() != 0 ) // need to ensure this is updated + WaitFrame() + } + } +} + +void function SetLastSurvivor( entity player ) +{ + foreach ( entity otherPlayer in GetPlayerArray() ) + Remote_CallFunction_NonReplay( otherPlayer, "ServerCallback_AnnounceLastSurvivor", player.GetEncodedEHandle() ) + + Highlight_SetEnemyHighlight( player, "enemy_sonar" ) + thread CreateTitanForPlayerAndHotdrop( player, GetTitanReplacementPoint( player, false ) ) + + if ( GameTime_TimeLeftSeconds() > 45 ) + SetServerVar( "gameEndTime", Time() + 45.0 ) +} + +int function TimeoutCheckSurvivors() +{ + if ( GetPlayerArrayOfTeam( INFECTION_TEAM_SURVIVOR ).len() > 0 ) + return INFECTION_TEAM_SURVIVOR + + return INFECTION_TEAM_INFECTED +}
\ No newline at end of file diff --git a/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_kr.gnut b/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_kr.gnut new file mode 100644 index 00000000..cf9d6bc5 --- /dev/null +++ b/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_kr.gnut @@ -0,0 +1,126 @@ +global function GamemodeKR_Init + +struct { + float currentHighestKillraceAmount + int currentKillraceScore + entity currentRacer + array<vector> flagSpawnPoints +} file + +void function GamemodeKR_Init() +{ + PrecacheModel( CTF_FLAG_MODEL ) + + SetSpawnpointGamemodeOverride( FFA ) + + Evac_SetEnabled( false ) + Riff_ForceTitanAvailability( eTitanAvailability.Never ) + Riff_ForceBoostAvailability( eBoostAvailability.Disabled ) + + AddSpawnCallback( "info_hardpoint", AddFlagSpawnPoint ) + + AddCallback_OnPlayerKilled( OnPlayerKilled ) + AddCallback_OnTouchHealthKit( "item_flag", StartPlayerKillrace ) + + AddCallback_GameStateEnter( eGameState.Playing, StartKillraceSpawnThink ) +} + +void function AddFlagSpawnPoint( entity hardpoint ) +{ + if ( hardpoint.HasKey( "hardpointGroup" ) && ( hardpoint.kv.hardpointGroup == "A" || hardpoint.kv.hardpointGroup == "B" || hardpoint.kv.hardpointGroup == "C" ) ) + file.flagSpawnPoints.append( hardpoint.GetOrigin() ) +} + +void function OnPlayerKilled( entity victim, entity attacker, var damageInfo ) +{ + if ( !victim.IsPlayer() || !attacker.IsPlayer() || attacker == victim ) + return + + float killRaceTime = attacker.GetPlayerNetTime( "killRaceTime" ) + 5.0 + attacker.SetPlayerNetTime( "killRaceTime", killRaceTime ) + if ( killRaceTime > file.currentHighestKillraceAmount ) + file.currentHighestKillraceAmount = killRaceTime + if ( file.currentRacer != null ) + file.currentKillraceScore++ +} + +bool function StartPlayerKillrace( entity player, entity flag ) +{ + float killRaceTime = player.GetPlayerNetTime( "killRaceTime" ) + if ( killRaceTime > 0.0 ) + { + thread PlayerKillrace( player, killRaceTime ) + return true // delete the flag entity + } + + return false // keep it alive +} + +void function PlayerKillrace( entity player, float raceTime ) +{ + file.currentKillraceScore = 0 + file.currentRacer = player + int oldMaxHealth = player.GetMaxHealth() + + player.SetMaxHealth( oldMaxHealth * 10 ) + player.SetHealth( player.GetMaxHealth() ) + + foreach ( entity weapon in player.GetMainWeapons() ) + foreach ( string mod in GetWeaponBurnMods( weapon.GetWeaponClassName() ) ) + weapon.AddMod( mod ) + + foreach ( entity otherPlayer in GetPlayerArray() ) + Remote_CallFunction_NonReplay( otherPlayer, "ServerCallback_NewKillRacer", player.GetEncodedEHandle(), Time() + raceTime ) + + float raceEnd = Time() + raceTime + while ( raceEnd > Time() && IsAlive( player ) ) + WaitFrame() + + player.SetPlayerNetTime( "killRaceTime", 0.0 ) + player.SetMaxHealth( oldMaxHealth ) + + foreach ( entity weapon in player.GetMainWeapons() ) + foreach ( string mod in GetWeaponBurnMods( weapon.GetWeaponClassName() ) ) + weapon.RemoveMod( mod ) + + foreach ( entity otherPlayer in GetPlayerArray() ) + Remote_CallFunction_NonReplay( otherPlayer, "ServerCallback_EndKillrace", player.GetEncodedEHandle(), file.currentKillraceScore ) + + if ( GameRules_GetTeamScore( player.GetTeam() ) < file.currentKillraceScore ) + { + GameRules_SetTeamScore( player.GetTeam(), file.currentKillraceScore ) + player.SetPlayerGameStat( PGS_ASSAULT_SCORE, file.currentKillraceScore ) + } + + thread KillraceSpawnThink() // go to spawn next flag +} + +void function StartKillraceSpawnThink() +{ + thread KillraceSpawnThink() +} + +void function KillraceSpawnThink() +{ + file.currentHighestKillraceAmount = 0 + file.currentRacer = null + file.currentKillraceScore = 0 + float time = Time() + while ( time + 20.0 > Time() && file.currentHighestKillraceAmount < 25 ) + WaitFrame() + + vector spawnpos = file.flagSpawnPoints[ RandomInt( file.flagSpawnPoints.len() ) ] + foreach ( entity player in GetPlayerArray() ) + Remote_CallFunction_NonReplay( player, "ServerCallback_FlagSpawnIncoming", spawnpos.x, spawnpos.y, spawnpos.z, Time() + 15 ) + + wait 15.0 + + // create a flag + entity flag = CreateEntity( "item_flag" ) + flag.SetValueForModelKey( CTF_FLAG_MODEL ) + SetTargetName( flag, "krflag" ) + DispatchSpawn( flag ) + flag.SetModel( CTF_FLAG_MODEL ) + flag.SetOrigin( spawnpos + < 0, 0, flag.GetBoundingMaxs().z / 2 > ) // get it out of the ground + flag.SetVelocity( < 0, 0, 1 > ) // make it do gravity again +}
\ No newline at end of file diff --git a/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_sbox.gnut b/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_sbox.gnut new file mode 100644 index 00000000..27581aea --- /dev/null +++ b/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_sbox.gnut @@ -0,0 +1,49 @@ +untyped +global function GamemodeSbox_Init + +struct { + array<entity> spawnpoints +} file + +void function GamemodeSbox_Init() +{ + SetConVarInt( "sv_cheats", 1 ) // cheats on by default + + // cache spawnpoints + //file.spawnpoints = GetEntArrayByClass_Expensive( "info_spawnpoint_human" ) + // todo just use a spawn callback for this rather than weird late cache in spawn + + AddCallback_OnClientConnected( SboxSpawnPlayer ) + AddDeathCallback( "player", SboxRespawnPlayer ) +} + +void function SboxSpawnPlayer( entity player ) +{ + if ( player.GetPlayerSettings() == "spectator" ) // if they haven't spawned yet + player.SetPlayerSettings( "pilot_grapple_male" ) + + if ( file.spawnpoints.len() == 0 ) // have to cache late rather than on init due to spawnpoints not existing in init + file.spawnpoints = GetEntArrayByClass_Expensive( "info_spawnpoint_human" ) + + if ( GetGameState() != eGameState.Playing ) // hacky but can't set this in init either + SetGameState( eGameState.Playing ) + + entity spawnpoint = file.spawnpoints[ RandomInt( file.spawnpoints.len() ) ] + + ScreenFadeFromBlack( player, 0.0, 0.0 ) // HACK before non-classicmp intros are ready, remove the blackscreen we get from waitingforplayers + player.RespawnPlayer( spawnpoint ) + player.GiveWeapon( "mp_weapon_toolgun" ) +} + +void function SboxRespawnPlayer( entity player, var damageInfo ) +{ + thread SboxRespawnPlayerThreaded( player ) +} + +void function SboxRespawnPlayerThreaded( entity player ) +{ + // todo: replace this with real respawn logic when that's ready + + wait 2.5 + SboxSpawnPlayer( player ) +}
\ No newline at end of file diff --git a/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_tt.gnut b/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_tt.gnut new file mode 100644 index 00000000..6a53ef87 --- /dev/null +++ b/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_tt.gnut @@ -0,0 +1,71 @@ +global function GamemodeTT_Init + +struct { + entity lastPlayerDropped +} file + +void function GamemodeTT_Init() +{ + SetSpawnpointGamemodeOverride( TEAM_DEATHMATCH ) + Riff_ForceTitanAvailability( eTitanAvailability.Never ) + + AddCallback_GameStateEnter( eGameState.Playing, OnEnterPlaying ) + + AddCallback_OnPlayerKilled( OnPlayerKilled ) + AddDeathCallback( "npc_titan", OnTitanKilled ) +} + +void function OnEnterPlaying() +{ + thread DropRandomTitan() +} + +void function DropRandomTitan() +{ + array<entity> players = GetPlayerArray() + + if ( players.len() == 1 ) + file.lastPlayerDropped = null // don't wanna loop forever if only 1 player + + entity titanPlayer + do { + titanPlayer = players[ RandomInt( players.len() ) ] + } while ( titanPlayer == file.lastPlayerDropped ) + + DropTitanForPlayer( titanPlayer, 5.0 ) +} + +void function DropTitanForPlayer( entity player, float delay ) +{ + wait delay + + file.lastPlayerDropped = player + CreateTitanForPlayerAndHotdrop( player, GetTitanReplacementPoint( player, false ) ) +} + +void function AttemptToDropTitanForKill( entity victim, entity attacker, var damageInfo ) +{ + if ( !victim.IsTitan() ) + return + + if ( !attacker.IsPlayer() || victim == attacker ) + thread DropRandomTitan() + else + thread DropTitanForPlayer( attacker, 2.0 ) +} + +void function OnPlayerKilled( entity victim, entity attacker, var damageInfo ) +{ + if ( victim.IsTitan() ) + AttemptToDropTitanForKill( victim, attacker, damageInfo ) + else if ( attacker.IsTitan() ) + { + AddTeamScore( attacker.GetTeam(), 1 ) + } +} + +void function OnTitanKilled( entity victim, var damageInfo ) +{ + if ( IsPetTitan( victim ) ) + AttemptToDropTitanForKill( victim, DamageInfo_GetAttacker( damageInfo ), damageInfo ) +}
\ No newline at end of file diff --git a/Northstar.Custom/scripts/vscripts/gamemodes/_riff_instagib.gnut b/Northstar.Custom/scripts/vscripts/gamemodes/_riff_instagib.gnut new file mode 100644 index 00000000..b3868359 --- /dev/null +++ b/Northstar.Custom/scripts/vscripts/gamemodes/_riff_instagib.gnut @@ -0,0 +1,65 @@ +global function RiffInstagib_Init + +struct { + table<entity, int> playerWeapons + array<string> instagibWeapons +} file + +void function RiffInstagib_Init() +{ + if ( GetCurrentPlaylistVarInt( "riff_instagib", 0 ) == 0 ) + return + + SetLoadoutGracePeriodEnabled( false ) + SetWeaponDropsEnabled( false ) + + file.instagibWeapons = [ + "mp_weapon_sniper", + "mp_weapon_wingman", + "mp_weapon_defender", + "mp_weapon_arena3", + "mp_weapon_wingman_n", + "mp_weapon_doubletake", + ] + file.instagibWeapons.randomize() + + AddCallback_OnPlayerRespawned( InstagibSetWeapons ) + AddCallback_OnPlayerKilled( InstagibCycleWeaponsForKill ) + AddCallback_OnClientDisconnected( InstagibCleanupClient ) +} + +void function InstagibSetWeapons( entity player ) +{ + if ( !( player in file.playerWeapons ) ) + file.playerWeapons[ player ] <- 0 + + player.SetMaxHealth( 1 ) + InstagibUpdateWeapons( player ) +} + +void function InstagibUpdateWeapons( entity player ) +{ + foreach( entity weapon in player.GetMainWeapons() ) + player.TakeWeaponNow( weapon.GetWeaponClassName() ) + + player.TakeWeaponNow( player.GetOffhandWeapon( OFFHAND_RIGHT ).GetWeaponClassName() ) + if ( !HasOffhandWeapon( player, "mp_weapon_grenade_sonar" ) ) + player.GiveOffhandWeapon( "mp_weapon_grenade_sonar", OFFHAND_RIGHT ) + + player.GiveWeapon( file.instagibWeapons[ file.playerWeapons[ player ] ] ) +} + +void function InstagibCycleWeaponsForKill( entity victim, entity attacker, var damageInfo ) +{ + if ( !victim.IsPlayer() || !attacker.IsPlayer() || victim == attacker ) + return + + file.playerWeapons[ attacker ] = ( file.playerWeapons[ attacker ] + 1 ) % file.instagibWeapons.len() + InstagibUpdateWeapons( attacker ) +} + +void function InstagibCleanupClient( entity player ) +{ + if ( player in file.playerWeapons ) + delete file.playerWeapons[ player ] +}
\ No newline at end of file diff --git a/Northstar.Custom/scripts/vscripts/gamemodes/cl_gamemode_arena.gnut b/Northstar.Custom/scripts/vscripts/gamemodes/cl_gamemode_arena.gnut new file mode 100644 index 00000000..a0e8618f --- /dev/null +++ b/Northstar.Custom/scripts/vscripts/gamemodes/cl_gamemode_arena.gnut @@ -0,0 +1,35 @@ +global function ClGameModeArena_Init +global function ServerCallback_CreateMoneyParticles + +struct { + var moneyRui +} file + +void function ClGameModeArena_Init() +{ + AddCallback_OnClientScriptInit( CreateArenaUI ) +} + +void function CreateArenaUI( entity player ) +{ + var rui = CreateCockpitRui( $"ui/fd_score_splash.rpak", 500 ) + RuiTrackInt( rui, "pointValue", player, RUI_TRACK_SCRIPT_NETWORK_VAR_INT, GetNetworkedVariableIndex( "FD_money" ) ) + RuiTrackInt( rui, "pointStack", player, RUI_TRACK_SCRIPT_NETWORK_VAR_INT, GetNetworkedVariableIndex( "FD_money256" ) ) + file.moneyRui = rui +} + +void function ServerCallback_CreateMoneyParticles( int playerHandle, int amount ) +{ + // largely taken from cl_gamemode_fd + entity player = GetEntityFromEncodedEHandle( playerHandle ) + + vector randDir2D = < RandomFloatRange( -1, 1 ), 1, 0 > + randDir2D = Normalize( randDir2D ) + + var rui = RuiCreate( $"ui/at_score_popup.rpak", clGlobal.topoFullScreen, RUI_DRAW_HUD, 100 ) + RuiSetInt( rui, "scoreVal", amount ) + RuiSetGameTime( rui, "startTime", Time() ) + RuiSetFloat3( rui, "pos", player.EyePosition() ) + RuiSetFloat2( rui, "driftDir", randDir2D ) + RuiSetBool( rui, "showNormalPoints", false ) +}
\ No newline at end of file diff --git a/Northstar.Custom/scripts/vscripts/gamemodes/cl_gamemode_fastball.gnut b/Northstar.Custom/scripts/vscripts/gamemodes/cl_gamemode_fastball.gnut new file mode 100644 index 00000000..80dc548a --- /dev/null +++ b/Northstar.Custom/scripts/vscripts/gamemodes/cl_gamemode_fastball.gnut @@ -0,0 +1,96 @@ +global function ClGamemodeFastball_Init +global function ServerCallback_FastballUpdatePanelRui +global function ServerCallback_FastballPanelHacked +global function ServerCallback_FastballRespawnPlayer + +struct { + var panelARui + var panelBRui + var panelCRui +} file + +void function ClGamemodeFastball_Init() +{ + ClGameState_RegisterGameStateAsset( $"ui/gamestate_info_lts.rpak" ) + + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_INTRO, "Music_Beacon_14_BTThrowThruFirstCrane", TEAM_MILITIA ) + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_INTRO, "Music_Beacon_14_BTThrowThruFirstCrane", TEAM_MILITIA ) + + AddCallback_OnClientScriptInit( FastballCreateRui ) +} + +void function FastballCreateRui( entity player ) +{ + file.panelARui = CreateCockpitRui( $"ui/cp_hardpoint_marker.rpak", 200 ) + file.panelBRui = CreateCockpitRui( $"ui/cp_hardpoint_marker.rpak", 200 ) + file.panelCRui = CreateCockpitRui( $"ui/cp_hardpoint_marker.rpak", 200 ) + +} + +void function ServerCallback_FastballUpdatePanelRui( int panelHandle, int id ) +{ + entity panel = GetEntityFromEncodedEHandle( panelHandle ) + var rui + if ( id == 0 ) + rui = file.panelARui + else if ( id == 1 ) + rui = file.panelBRui + else if ( id == 2 ) + rui = file.panelCRui + + RuiSetInt( rui, "hardpointId", id ) + RuiTrackFloat3( rui, "pos", panel, RUI_TRACK_OVERHEAD_FOLLOW ) + RuiSetInt( rui, "viewerTeam", GetLocalClientPlayer().GetTeam() ) + ////RuiTrackInt( rui, "cappingTeam", null, RUI_TRACK_SCRIPT_NETWORK_VAR_GLOBAL_INT, GetNetworkedVariableIndex( "panel" + id + "progress" ) ) + RuiTrackInt( rui, "hardpointTeamRelation", panel, RUI_TRACK_TEAM_RELATION_VIEWPLAYER ) + + RuiSetBool( rui, "isVisible", true ) +} + +void function ServerCallback_FastballPanelHacked( int panelHandle, int id, int capturingPlayerHandle ) +{ + ServerCallback_FastballUpdatePanelRui( panelHandle, id ) // may not be necessary, just wanna ensure this is always right + + entity panel = GetEntityFromEncodedEHandle( panelHandle ) + entity capturingPlayer = GetEntityFromEncodedEHandle( capturingPlayerHandle ) + + if ( capturingPlayer == GetLocalViewPlayer() ) + return + + string panelIdString + if ( id == 0 ) + panelIdString = "A" + if ( id == 1 ) + panelIdString = "B" + else if ( id == 2 ) + panelIdString = "C" + + AnnouncementData announcement = Announcement_Create( Localize( "#FASTBALL_PANEL_CAPTURED", capturingPlayer.GetPlayerName(), panelIdString ) ) + + if ( capturingPlayer.GetTeam() == GetLocalViewPlayer().GetTeam() ) + Announcement_SetTitleColor( announcement, < 0, 0, 1 > ) + else + Announcement_SetTitleColor( announcement, < 1, 0, 0 > ) + + Announcement_SetPurge( announcement, true ) + Announcement_SetPriority( announcement, 200 ) //Be higher priority than Titanfall ready indicator etc + Announcement_SetSoundAlias( announcement, SFX_HUD_ANNOUNCE_QUICK ) + Announcement_SetStyle( announcement, ANNOUNCEMENT_STYLE_QUICK ) + AnnouncementFromClass( GetLocalViewPlayer(), announcement ) +} + +void function ServerCallback_FastballRespawnPlayer() +{ + thread FastballRespawnPlayerEffects_Threaded() +} + +void function FastballRespawnPlayerEffects_Threaded() +{ + // sometimes this seems to get called before the player has respawned clientside, so we just wait until the client thinks they're alive + entity player = GetLocalViewPlayer() + + while ( !IsAlive( player ) ) + WaitFrame() + + StartParticleEffectOnEntity( player.GetCockpit(), GetParticleSystemIndex( $"P_pod_screen_lasers_OUT" ), FX_PATTACH_ABSORIGIN_FOLLOW, -1 ) +}
\ No newline at end of file diff --git a/Northstar.Custom/scripts/vscripts/gamemodes/cl_gamemode_gg.gnut b/Northstar.Custom/scripts/vscripts/gamemodes/cl_gamemode_gg.gnut new file mode 100644 index 00000000..de8a3449 --- /dev/null +++ b/Northstar.Custom/scripts/vscripts/gamemodes/cl_gamemode_gg.gnut @@ -0,0 +1,26 @@ +global function ClGamemodeGG_Init + +void function ClGamemodeGG_Init() +{ + // add ffa gamestate asset + ClGameState_RegisterGameStateAsset( $"ui/gamestate_info_ffa.rpak" ) + + // add music for mode, this is copied directly from the ffa/fra music registered in cl_music.gnut + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_INTRO, "music_mp_freeagents_intro", TEAM_IMC ) + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_INTRO, "music_mp_freeagents_intro", TEAM_MILITIA ) + + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_WIN, "music_mp_freeagents_outro_win", TEAM_IMC ) + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_WIN, "music_mp_freeagents_outro_win", TEAM_MILITIA ) + + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_DRAW, "music_mp_freeagents_outro_lose", TEAM_IMC ) + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_DRAW, "music_mp_freeagents_outro_lose", TEAM_MILITIA ) + + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_LOSS, "music_mp_freeagents_outro_lose", TEAM_IMC ) + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_LOSS, "music_mp_freeagents_outro_lose", TEAM_MILITIA ) + + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_THREE_MINUTE, "music_mp_freeagents_almostdone", TEAM_IMC ) + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_THREE_MINUTE, "music_mp_freeagents_almostdone", TEAM_MILITIA ) + + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_LAST_MINUTE, "music_mp_freeagents_lastminute", TEAM_IMC ) + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_LAST_MINUTE, "music_mp_freeagents_lastminute", TEAM_MILITIA ) +}
\ No newline at end of file diff --git a/Northstar.Custom/scripts/vscripts/gamemodes/cl_gamemode_inf.gnut b/Northstar.Custom/scripts/vscripts/gamemodes/cl_gamemode_inf.gnut new file mode 100644 index 00000000..56763bd4 --- /dev/null +++ b/Northstar.Custom/scripts/vscripts/gamemodes/cl_gamemode_inf.gnut @@ -0,0 +1,75 @@ +global function ClGamemodeInfection_Init +global function ServerCallback_YouAreInfected +global function ServerCallback_AnnounceFirstInfected +global function ServerCallback_AnnounceLastSurvivor + +void function ClGamemodeInfection_Init() +{ + //ClGameState_RegisterGameStateAsset( $"ui/gamestate_info_ffa.rpak" ) + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_INTRO, "music_beacon_8a_jumpingsuccess", TEAM_MILITIA ) + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_INTRO, "music_beacon_8a_jumpingsuccess", TEAM_IMC ) + RegisterLevelMusicForTeam( eMusicPieceID.GAMEMODE_1, "Music_Beacon_24_BTLob", TEAM_MILITIA ) + RegisterLevelMusicForTeam( eMusicPieceID.GAMEMODE_1, "Music_Beacon_24_BTLob", TEAM_IMC ) + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_LAST_MINUTE, "music_mp_titanwar_lastminute", TEAM_MILITIA ) + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_LAST_MINUTE, "music_mp_titanwar_lastminute", TEAM_IMC ) + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_WIN, "music_mp_freeagents_outro_lose", TEAM_MILITIA ) + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_WIN, "music_mp_freeagents_outro_lose", TEAM_IMC ) + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_DRAW, "music_mp_freeagents_outro_lose", TEAM_MILITIA ) + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_DRAW, "music_mp_freeagents_outro_lose", TEAM_IMC ) + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_LOSS, "music_mp_freeagents_outro_lose", TEAM_MILITIA ) + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_LOSS, "music_mp_freeagents_outro_lose", TEAM_IMC ) +} + +void function ServerCallback_YouAreInfected() +{ + // heavily based on mfd code + entity localPlayer = GetLocalViewPlayer() + + StartParticleEffectOnEntity( localPlayer.GetCockpit(), GetParticleSystemIndex( $"P_MFD" ), FX_PATTACH_ABSORIGIN_FOLLOW, -1 ) + EmitSoundOnEntity( localPlayer, "UI_InGame_MarkedForDeath_PlayerMarked" ) + HideEventNotification() + AnnouncementData announcement = Announcement_Create( "#INFECTION_YOU_ARE_INFECTED" ) + Announcement_SetSubText( announcement, "#INFECTION_KILL_SURVIVORS" ) + Announcement_SetTitleColor( announcement, <1,0,0> ) + Announcement_SetPurge( announcement, true ) + Announcement_SetPriority( announcement, 200 ) //Be higher priority than Titanfall ready indicator etc + Announcement_SetSoundAlias( announcement, SFX_HUD_ANNOUNCE_QUICK ) + Announcement_SetStyle( announcement, ANNOUNCEMENT_STYLE_QUICK ) + AnnouncementFromClass( localPlayer, announcement ) +} + +void function ServerCallback_AnnounceFirstInfected( int survivorEHandle ) +{ + entity player = GetEntityFromEncodedEHandle( survivorEHandle ) + + AnnouncementData announcement = Announcement_Create( Localize( "#INFECTION_FIRST_INFECTED", player.GetPlayerName() ) ) + //Announcement_SetSubText( announcement, "#INFECTION_KILL_LAST_SURVIVOR" ) + Announcement_SetTitleColor( announcement, <1,0,0> ) + Announcement_SetPurge( announcement, true ) + Announcement_SetPriority( announcement, 200 ) //Be higher priority than Titanfall ready indicator etc + Announcement_SetSoundAlias( announcement, SFX_HUD_ANNOUNCE_QUICK ) + Announcement_SetStyle( announcement, ANNOUNCEMENT_STYLE_QUICK ) + AnnouncementFromClass( GetLocalViewPlayer(), announcement ) +} + +void function ServerCallback_AnnounceLastSurvivor( int survivorEHandle ) +{ + entity player = GetEntityFromEncodedEHandle( survivorEHandle ) + + string announcementString = Localize( "#INFECTION_LAST_SURVIVOR", player.GetPlayerName() ) + string announcementSubString = "#INFECTION_KILL_LAST_SURVIVOR" + if ( player == GetLocalViewPlayer() ) + { + announcementString = "#INFECTION_YOU_ARE_LAST_SURVIVOR" + announcementSubString = "#INFECTION_SURVIVE_LAST_SURVIVOR" + } + + AnnouncementData announcement = Announcement_Create( announcementString ) + Announcement_SetSubText( announcement, announcementSubString ) + Announcement_SetTitleColor( announcement, <1,0,0> ) + Announcement_SetPurge( announcement, true ) + Announcement_SetPriority( announcement, 200 ) //Be higher priority than Titanfall ready indicator etc + Announcement_SetSoundAlias( announcement, SFX_HUD_ANNOUNCE_QUICK ) + Announcement_SetStyle( announcement, ANNOUNCEMENT_STYLE_QUICK ) + AnnouncementFromClass( GetLocalViewPlayer(), announcement ) +}
\ No newline at end of file diff --git a/Northstar.Custom/scripts/vscripts/gamemodes/cl_gamemode_kr.gnut b/Northstar.Custom/scripts/vscripts/gamemodes/cl_gamemode_kr.gnut new file mode 100644 index 00000000..269057c7 --- /dev/null +++ b/Northstar.Custom/scripts/vscripts/gamemodes/cl_gamemode_kr.gnut @@ -0,0 +1,241 @@ +global function ClGamemodeKR_Init + +global function ShowTimeGainOnKill +global function ServerCallback_FlagSpawnIncoming +global function ServerCallback_NewKillRacer +global function ServerCallback_EndKillrace + +struct { + var currentTimeRui + var currentTimeAdditionRui + var flagRui + var flagIncomingRui + var killRacerRui + + bool isCurrentlyInRace = false + float currentTimeAmount + float currentTimeLastAdditionTime + float currentTimeAdditionCombined +} file + +void function ClGamemodeKR_Init() +{ + // add ffa gamestate asset + ClGameState_RegisterGameStateAsset( $"ui/gamestate_info_ffa.rpak" ) + + // add music for mode, this is copied directly from the ffa/fra music registered in cl_music.gnut + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_INTRO, "music_mp_freeagents_intro", TEAM_IMC ) + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_INTRO, "music_mp_freeagents_intro", TEAM_MILITIA ) + + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_WIN, "music_mp_freeagents_outro_win", TEAM_IMC ) + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_WIN, "music_mp_freeagents_outro_win", TEAM_MILITIA ) + + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_DRAW, "music_mp_freeagents_outro_lose", TEAM_IMC ) + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_DRAW, "music_mp_freeagents_outro_lose", TEAM_MILITIA ) + + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_LOSS, "music_mp_freeagents_outro_lose", TEAM_IMC ) + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_LOSS, "music_mp_freeagents_outro_lose", TEAM_MILITIA ) + + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_THREE_MINUTE, "music_mp_freeagents_almostdone", TEAM_IMC ) + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_THREE_MINUTE, "music_mp_freeagents_almostdone", TEAM_MILITIA ) + + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_LAST_MINUTE, "music_mp_freeagents_lastminute", TEAM_IMC ) + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_LAST_MINUTE, "music_mp_freeagents_lastminute", TEAM_MILITIA ) + + AddCallback_OnClientScriptInit( CreateKRUI ) + AddCreateCallback( "item_flag", OnFlagCreated ) + AddDestroyCallback( "item_flag", OnFlagDestroyed ) +} + +void function CreateKRUI( entity player ) +{ + file.currentTimeRui = CreateCockpitRui( $"ui/titan_protocol_text_center.rpak", 200 ) + RuiSetInt( file.currentTimeRui, "lineNum", 1 ) + RuiSetGameTime( file.currentTimeRui, "startTime", 0.0 ) + UpdateCurrentTimeAmount() + + file.flagRui = CreateCockpitRui( $"ui/speedball_flag_marker.rpak", 200 ) + RuiSetBool( file.flagRui, "playerIsCarrying", false ) + RuiSetBool( file.flagRui, "isCarried", false ) + + file.killRacerRui = CreateCockpitRui( $"ui/mfd_target_marker.rpak", 200 ) + RuiSetBool( file.killRacerRui, "isVisible", false ) + RuiSetImage( file.killRacerRui, "markedIcon", $"rui/hud/gametype_icons/mfd/mfd_enemy" ) + RuiSetBool( file.killRacerRui, "isMarked", true ) +} + +void function OnFlagCreated( entity flag ) +{ + if ( IsValid( file.flagIncomingRui ) ) + RuiDestroy( file.flagIncomingRui ) + + RuiSetBool( file.flagRui, "isVisible", true ) + RuiTrackFloat3( file.flagRui, "pos", flag, RUI_TRACK_OVERHEAD_FOLLOW ) +} + +void function OnFlagDestroyed( entity flag ) +{ + RuiSetBool( file.flagRui, "isVisible", false ) +} + +void function ShowTimeGainOnKill( entity player, float oldVal, float newVal, bool actuallyChanged ) +{ + if ( file.isCurrentlyInRace || player != GetLocalViewPlayer() || !actuallyChanged ) + return + + if ( newVal > oldVal ) // time increase: likely given on kill + { + float amount = newVal - oldVal + + // show a combined number on the addition rui if last addition was recent enough + float additionShowAmount = amount + file.currentTimeAdditionCombined += amount + if ( Time() - file.currentTimeLastAdditionTime < 1.25 ) + amount = file.currentTimeAdditionCombined + else + { + file.currentTimeAdditionRui = CreateCockpitRui( $"ui/titan_protocol_text_center.rpak", 200 ) + RuiSetInt( file.currentTimeAdditionRui, "lineNum", 2 ) + file.currentTimeAdditionCombined = amount + } + + RuiSetString( file.currentTimeAdditionRui, "displayString", "+" + amount + "s 00ms " ) // formatted so that it lines up with other rui + RuiSetGameTime( file.currentTimeAdditionRui, "startTime", Time() ) + RuiSetGameTime( file.currentTimeAdditionRui, "endTime", Time() + 1.5 ) + + file.currentTimeLastAdditionTime = Time() + + thread UpdateFullTimeAmountAfterAdditionDone( file.currentTimeLastAdditionTime ) + } + else // time decrease either a reset or + UpdateCurrentTimeAmount() +} + +void function UpdateFullTimeAmountAfterAdditionDone( float previousAdditionTime ) +{ + wait 1.25 + + if ( previousAdditionTime == file.currentTimeLastAdditionTime ) // if not, there's been another addition since this was last updated and we'll wait for that instead + UpdateCurrentTimeAmount() +} + +void function UpdateCurrentTimeAmount( float overrideTime = -1 ) +{ + if ( overrideTime == -1 ) + file.currentTimeAmount = GetLocalViewPlayer().GetPlayerNetTime( "killRaceTime" ) + else + file.currentTimeAmount = overrideTime + + string currentTimeString + int seconds = file.currentTimeAmount.tointeger() + + string secondsString = seconds.tostring() + if ( secondsString.len() < 2 ) // pad to 2 chars + secondsString = "0" + secondsString + currentTimeString += secondsString + "s " + + string msString = ( ( file.currentTimeAmount - file.currentTimeAmount.tointeger() ) * 100 ).tostring() + if ( msString.len() < 2 ) // pad to 2 chars + msString = "0" + msString + currentTimeString += msString.slice( 0, 2 ) + "ms " + + RuiSetString( file.currentTimeRui, "displayString", currentTimeString ) + RuiSetGameTime( file.currentTimeRui, "endTime", Time() + 99999.0 ) // arbitrarily large number so this doesn't disappear +} + +void function ServerCallback_FlagSpawnIncoming( float x, float y, float z , float spawnTime ) +{ + print( "flagspawn: < " + x + ", " + y + ", " + z + " > in " + ( spawnTime - Time() ) + " seconds" ) + + AnnouncementData announcement = Announcement_Create( Localize( "#KR_FLAG_INCOMING", spawnTime.tostring() ) ) + Announcement_SetSubText( announcement, "#KR_COLLECT_FLAG" ) + Announcement_SetTitleColor( announcement, < 0, 0, 1 > ) + Announcement_SetPurge( announcement, true ) + Announcement_SetPriority( announcement, 200 ) + Announcement_SetSoundAlias( announcement, SFX_HUD_ANNOUNCE_QUICK ) + Announcement_SetStyle( announcement, ANNOUNCEMENT_STYLE_QUICK ) + AnnouncementFromClass( GetLocalViewPlayer(), announcement ) + + RuiSetFloat3( file.flagRui, "pos", < x, y, z > ) + RuiSetBool( file.flagRui, "isVisible", true ) + + file.flagIncomingRui = RuiCreate( $"ui/titanfall_timer.rpak", clGlobal.topoFullScreen, RUI_DRAW_HUD, 0 ) + RuiTrackFloat3( file.flagIncomingRui, "playerPos", GetLocalViewPlayer(), RUI_TRACK_ABSORIGIN_FOLLOW ) + RuiSetFloat3( file.flagIncomingRui, "pos", < x, y, z > + < 0, 0, 48 > ) + RuiSetGameTime( file.flagIncomingRui, "impactTime", spawnTime ) +} + +void function ServerCallback_NewKillRacer( int playerHandle, float endTime ) +{ + entity player = GetEntityFromEncodedEHandle( playerHandle ) + + string announcementMessage = Localize( "#KR_NEW_RACER", player.GetPlayerName() ) + string announcementSubMessage + if ( player == GetLocalViewPlayer() ) + { + file.isCurrentlyInRace = true + thread LerpTimeDuringRace( endTime ) + + StartParticleEffectOnEntity( player.GetCockpit(), GetParticleSystemIndex( $"P_MFD" ), FX_PATTACH_ABSORIGIN_FOLLOW, -1 ) + EmitSoundOnEntity( player, "UI_InGame_MarkedForDeath_PlayerMarked" ) + HideEventNotification() + + announcementMessage = "#KR_YOU_ARE_NEW_RACER" + announcementSubMessage = "#KR_YOU_SET_NEW_RECORD" + } + else + { + // mark the player + RuiTrackFloat3( file.killRacerRui, "pos", player, RUI_TRACK_OVERHEAD_FOLLOW ) + RuiSetBool( file.killRacerRui, "isVisible", true ) + } + + AnnouncementData announcement = Announcement_Create( announcementMessage ) + Announcement_SetSubText( announcement, announcementSubMessage ) + Announcement_SetTitleColor( announcement, < 1, 0, 0 > ) + Announcement_SetPurge( announcement, true ) + Announcement_SetPriority( announcement, 200 ) + Announcement_SetSoundAlias( announcement, SFX_HUD_ANNOUNCE_QUICK ) + Announcement_SetStyle( announcement, ANNOUNCEMENT_STYLE_QUICK ) + AnnouncementFromClass( GetLocalViewPlayer(), announcement ) +} + +void function LerpTimeDuringRace( float endTime ) +{ + while ( Time() < endTime ) + { + // manually update this here so we can get more frequent updates than what we'd get with networked vars + UpdateCurrentTimeAmount( endTime - Time() ) + WaitFrame() + } + + UpdateCurrentTimeAmount( 0.0 ) + file.isCurrentlyInRace = false +} + +void function ServerCallback_EndKillrace( int playerHandle, int score ) +{ + entity player = GetEntityFromEncodedEHandle( playerHandle ) + + vector colour = < 0, 0, 1 > + string announcementMessage = Localize( "#KR_ENEMY_KILLRACE_OVER", player.GetPlayerName() ) + string announcementSubMessage + if ( player == GetLocalViewPlayer() ) + { + StartParticleEffectOnEntity( GetLocalViewPlayer().GetCockpit(), GetParticleSystemIndex( $"P_MFD_unmark" ), FX_PATTACH_ABSORIGIN_FOLLOW, -1 ) + colour = < 1, 0, 0 > + announcementMessage = "#KR_YOUR_KILLRACE_OVER" + announcementSubMessage = Localize( "#KR_YOUR_KILLRACE_SCORE", score ) + } + + AnnouncementData announcement = Announcement_Create( announcementMessage ) + Announcement_SetSubText( announcement, announcementSubMessage ) + Announcement_SetTitleColor( announcement, colour ) + Announcement_SetPurge( announcement, true ) + Announcement_SetPriority( announcement, 200 ) + Announcement_SetSoundAlias( announcement, SFX_HUD_ANNOUNCE_QUICK ) + Announcement_SetStyle( announcement, ANNOUNCEMENT_STYLE_QUICK ) + AnnouncementFromClass( GetLocalViewPlayer(), announcement ) + + RuiSetBool( file.killRacerRui, "isVisible", false ) +}
\ No newline at end of file diff --git a/Northstar.Custom/scripts/vscripts/gamemodes/cl_gamemode_tt.gnut b/Northstar.Custom/scripts/vscripts/gamemodes/cl_gamemode_tt.gnut new file mode 100644 index 00000000..b73ed958 --- /dev/null +++ b/Northstar.Custom/scripts/vscripts/gamemodes/cl_gamemode_tt.gnut @@ -0,0 +1,26 @@ +global function ClGamemodeTT_Init + +void function ClGamemodeTT_Init() +{ + // register gamestate asset, this is default so not necessary but doing it anyway + ClGameState_RegisterGameStateAsset( $"ui/gamestate_info_ps.rpak" ) + + // add music for mode, this is copied directly from the attrition/tdm music registered in cl_music.gnut + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_INTRO, "music_mp_pilothunt_intro_flyin", TEAM_IMC ) + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_INTRO, "music_mp_pilothunt_intro_flyin", TEAM_MILITIA ) + + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_WIN, "music_mp_pilothunt_epilogue_win", TEAM_IMC ) + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_WIN, "music_mp_pilothunt_epilogue_win", TEAM_MILITIA ) + + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_DRAW, "music_mp_pilothunt_epilogue_win", TEAM_IMC ) + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_DRAW, "music_mp_pilothunt_epilogue_win", TEAM_MILITIA ) + + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_LOSS, "music_mp_pilothunt_epilogue_lose", TEAM_IMC ) + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_LOSS, "music_mp_pilothunt_epilogue_lose", TEAM_MILITIA ) + + RegisterLevelMusicForTeam( eMusicPieceID.GAMEMODE_1, "music_mp_pilothunt_almostdone", TEAM_IMC ) + RegisterLevelMusicForTeam( eMusicPieceID.GAMEMODE_1, "music_mp_pilothunt_almostdone", TEAM_MILITIA ) + + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_LAST_MINUTE, "music_mp_pilothunt_lastminute", TEAM_IMC ) + RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_LAST_MINUTE, "music_mp_pilothunt_lastminute", TEAM_MILITIA ) +}
\ No newline at end of file diff --git a/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_arena.gnut b/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_arena.gnut new file mode 100644 index 00000000..b634f1d3 --- /dev/null +++ b/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_arena.gnut @@ -0,0 +1,58 @@ +global function Sh_GamemodeArena_Init + +global const string GAMEMODE_ARENA = "arena" + +void function Sh_GamemodeArena_Init() +{ + // create custom gamemode + AddCallback_OnCustomGamemodesInit( CreateGamemodeArena ) + AddCallback_OnRegisteringCustomNetworkVars( ArenaRegisterNetworkVars ) +} + +void function CreateGamemodeArena() +{ + GameMode_Create( GAMEMODE_ARENA ) + GameMode_SetName( GAMEMODE_ARENA, "#GAMEMODE_arena" ) + GameMode_SetDesc( GAMEMODE_ARENA, "#PL_arena_desc" ) + GameMode_SetGameModeAnnouncement( GAMEMODE_ARENA, "gnrc_modeDesc" ) + GameMode_SetDefaultTimeLimits( GAMEMODE_ARENA, 5, 0.0 ) + GameMode_AddScoreboardColumnData( GAMEMODE_ARENA, "#SCOREBOARD_PILOT_KILLS", PGS_PILOT_KILLS, 2 ) + GameMode_SetColor( GAMEMODE_ARENA, [147, 204, 57, 255] ) + + #if SERVER + GameMode_AddServerInit( GAMEMODE_ARENA, GameModeArena_Init ) + GameMode_SetPilotSpawnpointsRatingFunc( GAMEMODE_ARENA, RateSpawnpoints_Generic ) + GameMode_SetTitanSpawnpointsRatingFunc( GAMEMODE_ARENA, RateSpawnpoints_Generic ) + #elseif CLIENT + GameMode_AddClientInit( GAMEMODE_ARENA, ClGameModeArena_Init ) + #endif + #if !UI + GameMode_SetScoreCompareFunc( GAMEMODE_ARENA, CompareAssaultScore ) + #endif +} + +void function ArenaRegisterNetworkVars() +{ + if ( GAMETYPE != GAMEMODE_ARENA ) + return + + // boost store stuff + Remote_RegisterFunction( "ServerCallback_OpenBoostStore" ) + Remote_RegisterFunction( "ServerCallback_UpdateMoney" ) + Remote_RegisterFunction( "ServerCallback_UpdateTeamReserve" ) + Remote_RegisterFunction( "ServerCallback_UpdatePlayerHasBattery" ) + Remote_RegisterFunction( "ServerCallback_UpdateAmpedWeaponState" ) + Remote_RegisterFunction( "ServerCallback_BoostStoreTitanHint" ) + Remote_RegisterFunction( "ServerCallback_UpdateTurretCount" ) + + RegisterNetworkedVariable( "boostStoreOpen", SNDC_GLOBAL, SNVT_BOOL, false ) + RegisterNetworkedVariable( "FD_money", SNDC_PLAYER_GLOBAL, SNVT_UNSIGNED_INT, 0 ) + RegisterNetworkedVariable( "FD_money256", SNDC_PLAYER_GLOBAL, SNVT_UNSIGNED_INT, 0 ) + + // these are required to prevent crashes in fd code that's called from menu_boost_store + RegisterNetworkedVariable( "numSuperRodeoGrenades", SNDC_PLAYER_GLOBAL, SNVT_INT, 0 ) + RegisterNetworkedVariable( "FD_waveActive", SNDC_GLOBAL, SNVT_BOOL, false ) + + // arena-exclusive stuff + Remote_RegisterFunction( "ServerCallback_CreateMoneyParticles" ) +}
\ No newline at end of file diff --git a/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_bomb.gnut b/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_bomb.gnut new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_bomb.gnut diff --git a/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_fastball.gnut b/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_fastball.gnut new file mode 100644 index 00000000..734e24ce --- /dev/null +++ b/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_fastball.gnut @@ -0,0 +1,45 @@ +global function Sh_GamemodeFastball_Init + +global const string GAMEMODE_FASTBALL = "fastball" + +void function Sh_GamemodeFastball_Init() +{ + // create custom gamemode + AddCallback_OnCustomGamemodesInit( CreateGamemodeFastball ) + AddCallback_OnRegisteringCustomNetworkVars( FastballRegisterNetworkVars ) +} + +void function CreateGamemodeFastball() +{ + GameMode_Create( GAMEMODE_FASTBALL ) + GameMode_SetName( GAMEMODE_FASTBALL, "#GAMEMODE_FASTBALL" ) + GameMode_SetDesc( GAMEMODE_FASTBALL, "#PL_fastball_desc" ) + GameMode_SetGameModeAnnouncement( GAMEMODE_FASTBALL, "grnc_modeDesc" ) + GameMode_SetDefaultTimeLimits( GAMEMODE_FASTBALL, 1, 0 ) + GameMode_SetDefaultScoreLimits( GAMEMODE_FASTBALL, 5, 0 ) + GameMode_AddScoreboardColumnData( GAMEMODE_FASTBALL, "#SCOREBOARD_FASTBALL_HACKS", PGS_ASSAULT_SCORE, 2 ) + GameMode_AddScoreboardColumnData( GAMEMODE_FASTBALL, "#SCOREBOARD_PILOT_KILLS", PGS_PILOT_KILLS, 2 ) + GameMode_AddScoreboardColumnData( GAMEMODE_FASTBALL, "#SCOREBOARD_DEATHS", PGS_DEATHS, 2 ) + GameMode_SetColor( GAMEMODE_FASTBALL, [147, 204, 57, 255] ) + + #if SERVER + GameMode_AddServerInit( GAMEMODE_FASTBALL, GamemodeFastball_Init ) + GameMode_SetPilotSpawnpointsRatingFunc( GAMEMODE_FASTBALL, RateSpawnpoints_Generic ) + GameMode_SetTitanSpawnpointsRatingFunc( GAMEMODE_FASTBALL, RateSpawnpoints_Generic ) + #elseif CLIENT + GameMode_AddClientInit( GAMEMODE_FASTBALL, ClGamemodeFastball_Init ) + #endif + #if !UI + GameMode_SetScoreCompareFunc( GAMEMODE_FASTBALL, CompareAssaultScore ) + #endif +} + +void function FastballRegisterNetworkVars() +{ + if ( GAMETYPE != GAMEMODE_FASTBALL ) + return + + Remote_RegisterFunction( "ServerCallback_FastballUpdatePanelRui" ) + Remote_RegisterFunction( "ServerCallback_FastballPanelHacked" ) + Remote_RegisterFunction( "ServerCallback_FastballRespawnPlayer" ) +}
\ No newline at end of file diff --git a/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_fw_custom.nut b/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_fw_custom.nut new file mode 100644 index 00000000..8c6e3f63 --- /dev/null +++ b/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_fw_custom.nut @@ -0,0 +1,73 @@ +// this script only exists to create the fw gamemode +// all client/shared gamelogic is still done in the gamemode's respective client and shared scripts +// these scripts are shipped with the game's official build so no need to recreate these +// their paths are gamemodes/cl_gamemode_fw.nut and gamemodes/sh_gamemode_fw.nut, respectively + +global function SHCreateGamemodeFW_Init + +void function SHCreateGamemodeFW_Init() +{ + AddCallback_OnCustomGamemodesInit( CreateGamemodeFW ) + AddCallback_OnRegisteringCustomNetworkVars( FWOnRegisteringNetworkVars ) +} + +void function CreateGamemodeFW() +{ + //entity e = CreateEntity("npc_turret_mega"); SetAISettingsWrapper( e, "npc_turret_mega_fortwar" ); e.SetOrigin(GetPlayerArray()[0].GetOrigin()); SetTeam(e,3); DispatchSpawn(e) + + // we have to manually add the client/shared scripts to scripts.rson atm so we need to prevent compile errors when they aren't included + // best way to do this is to just ignore this whole block for now and wait until we don't have to add them manually + + GameMode_Create( FORT_WAR ) + GameMode_SetName( FORT_WAR, "#GAMEMODE_fw" ) + GameMode_SetDesc( FORT_WAR, "#PL_fw_desc" ) + GameMode_SetGameModeAnnouncement( FORT_WAR, "ffa_modeDesc" ) // fw lines are unfortunately not registered to faction dialogue + + #if SERVER + //GameMode_AddServerInit( FORT_WAR, GamemodeFW_Init ) // doesn't exist yet lol + #elseif CLIENT + GameMode_AddClientInit( FORT_WAR, CLGamemodeFW_Init ) + #endif + #if !UI + GameMode_AddSharedInit( FORT_WAR, SHGamemodeFW_Init ) + #endif +} + +void function FWOnRegisteringNetworkVars() +{ + if ( GAMETYPE != FORT_WAR ) + return + + RegisterNetworkedVariable( "turretSite1", SNDC_GLOBAL, SNVT_ENTITY ) + RegisterNetworkedVariable( "turretSite2", SNDC_GLOBAL, SNVT_ENTITY ) + RegisterNetworkedVariable( "turretSite3", SNDC_GLOBAL, SNVT_ENTITY ) + RegisterNetworkedVariable( "turretSite4", SNDC_GLOBAL, SNVT_ENTITY ) + RegisterNetworkedVariable( "turretSite5", SNDC_GLOBAL, SNVT_ENTITY ) + RegisterNetworkedVariable( "turretSite6", SNDC_GLOBAL, SNVT_ENTITY ) + RegisterNetworkedVariable( "turretSite7", SNDC_GLOBAL, SNVT_ENTITY ) + RegisterNetworkedVariable( "turretSite8", SNDC_GLOBAL, SNVT_ENTITY ) + RegisterNetworkedVariable( "turretSite9", SNDC_GLOBAL, SNVT_ENTITY ) + + RegisterNetworkedVariable( "turretStateFlags1", SNDC_GLOBAL, SNVT_INT ) + RegisterNetworkedVariable( "turretStateFlags2", SNDC_GLOBAL, SNVT_INT ) + RegisterNetworkedVariable( "turretStateFlags3", SNDC_GLOBAL, SNVT_INT ) + RegisterNetworkedVariable( "turretStateFlags4", SNDC_GLOBAL, SNVT_INT ) + RegisterNetworkedVariable( "turretStateFlags5", SNDC_GLOBAL, SNVT_INT ) + RegisterNetworkedVariable( "turretStateFlags6", SNDC_GLOBAL, SNVT_INT ) + RegisterNetworkedVariable( "turretStateFlags7", SNDC_GLOBAL, SNVT_INT ) + RegisterNetworkedVariable( "turretStateFlags8", SNDC_GLOBAL, SNVT_INT ) + RegisterNetworkedVariable( "turretStateFlags9", SNDC_GLOBAL, SNVT_INT ) + + RegisterNetworkedVariable( "imcTowerThreatLevel", SNDC_GLOBAL, SNVT_INT ) + RegisterNetworkedVariable( "milTowerThreatLevel", SNDC_GLOBAL, SNVT_INT ) + RegisterNetworkedVariable( "fwCampAlertA", SNDC_GLOBAL, SNVT_INT ) + RegisterNetworkedVariable( "fwCampStressA", SNDC_GLOBAL, SNVT_INT ) + RegisterNetworkedVariable( "fwCampAlertB", SNDC_GLOBAL, SNVT_INT ) + RegisterNetworkedVariable( "fwCampStressB", SNDC_GLOBAL, SNVT_INT ) + RegisterNetworkedVariable( "fwCampAlertC", SNDC_GLOBAL, SNVT_INT ) + RegisterNetworkedVariable( "fwCampStressC", SNDC_GLOBAL, SNVT_INT ) + + #if CLIENT + CLFortWar_RegisterNetworkFunctions() + #endif +}
\ No newline at end of file diff --git a/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_gg.gnut b/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_gg.gnut new file mode 100644 index 00000000..4ea9ac20 --- /dev/null +++ b/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_gg.gnut @@ -0,0 +1,164 @@ +global function Sh_GamemodeGG_Init +global function GetGunGameWeapons + +global const string GAMEMODE_GG = "gg" + +global struct GunGameWeapon +{ + string weapon + array<string> mods + int offhandSlot = -1 +} + +struct { + array<GunGameWeapon> weapons +} file + +void function Sh_GamemodeGG_Init() +{ + // create custom gamemode + AddCallback_OnCustomGamemodesInit( CreateGamemodeGG ) +} + +void function CreateGamemodeGG() +{ + GameMode_Create( GAMEMODE_GG ) + GameMode_SetName( GAMEMODE_GG, "#GAMEMODE_GG" ) + GameMode_SetDesc( GAMEMODE_GG, "#PL_gg_desc" ) + GameMode_SetGameModeAnnouncement( GAMEMODE_GG, "ffa_modeDesc" ) + GameMode_SetDefaultTimeLimits( GAMEMODE_GG, 10, 0.0 ) + GameMode_AddScoreboardColumnData( GAMEMODE_GG, "#SCOREBOARD_SCORE", PGS_ASSAULT_SCORE, 2 ) + GameMode_AddScoreboardColumnData( GAMEMODE_GG, "#SCOREBOARD_PILOT_KILLS", PGS_PILOT_KILLS, 2 ) + GameMode_SetColor( GAMEMODE_GG, [147, 204, 57, 255] ) + + + // setup guns + + // smgs + // car + GunGameWeapon ggCar = { weapon = "mp_weapon_car", mods = [ "pas_run_and_gun" ], ... } + file.weapons.append( ggCar ) + + // alternator + GunGameWeapon ggAlternator = { weapon = "mp_weapon_alternator_smg", mods = [ "pas_run_and_gun" ], ... } + file.weapons.append( ggAlternator ) + + // volt + GunGameWeapon ggVolt = { weapon = "mp_weapon_hemlok_smg", ... } + file.weapons.append( ggVolt ) + + + // rifles + // hemlok + GunGameWeapon ggHemlok = { weapon = "mp_weapon_hemlok", mods = [ ], ... } + file.weapons.append( ggHemlok ) + + // flatline + GunGameWeapon ggFlatline = { weapon = "mp_weapon_vinson", mods = [ "hcog" ], ... } + file.weapons.append( ggFlatline ) + + // r201 + GunGameWeapon ggR101 = { weapon = "mp_weapon_rspn101", ... } + file.weapons.append( ggR101 ) + + + // lmgs + // devotion + GunGameWeapon ggDevotion = { weapon = "mp_weapon_esaw", ... } + file.weapons.append( ggDevotion ) + + // l-star + GunGameWeapon ggLstar = { weapon = "mp_weapon_lstar", mods = [ "pas_run_and_gun" ], ... } + if ( RandomInt( 100 ) <= 5 ) + ggLstar.mods.append( "rcee" ) // easter egg mod that changes the screen of the lstar + + file.weapons.append( ggLstar ) + + + // shotguns + // eva-8 + GunGameWeapon ggEva = { weapon = "mp_weapon_shotgun", ... } + file.weapons.append( ggEva ) + + // mastiff + GunGameWeapon ggMastiff = { weapon = "mp_weapon_mastiff", ... } + file.weapons.append( ggMastiff ) + + + // grenadiers + // softball + GunGameWeapon ggSoftball = { weapon = "mp_weapon_softball", ... } + file.weapons.append( ggSoftball ) + + // epg + GunGameWeapon ggEpg = { weapon = "mp_weapon_epg", mods = [ "jump_kit" ], ... } + file.weapons.append( ggEpg ) + + + // primary pistols + // mozambique + GunGameWeapon ggMozam = { weapon = "mp_weapon_shotgun_pistol", mods = [ "pas_run_and_gun" ], ... } + file.weapons.append( ggMozam ) + + // wingman elite + GunGameWeapon ggWme = { weapon = "mp_weapon_wingman_n", mods = [ "pas_run_and_gun", "ricochet" ], ... } + file.weapons.append( ggWme ) + + + // snipers + // double take + GunGameWeapon ggTaketake = { weapon = "mp_weapon_doubletake", ... } + file.weapons.append( ggTaketake ) + + // kraber + GunGameWeapon ggKraber = { weapon = "mp_weapon_sniper", mods = [ "pas_fast_ads", "ricochet" ], ... } + file.weapons.append( ggKraber ) + + + // secondary pistols + // re-45 + GunGameWeapon ggRe45 = { weapon = "mp_weapon_autopistol", mods = [ "pas_run_and_gun", "temp_sight" ], ... } + file.weapons.append( ggRe45 ) + + // p2016 + GunGameWeapon ggP2016 = { weapon = "mp_weapon_semipistol", mods = [ "pas_run_and_gun" ], ... } + file.weapons.append( ggP2016 ) + + // wingman + GunGameWeapon ggWingman = { weapon = "mp_weapon_wingman", mods = [ "pas_run_and_gun" ], ... } + file.weapons.append( ggWingman ) + + + // final/special weapons + // charge rifle + GunGameWeapon ggChargeRifle = { weapon = "mp_weapon_defender", ... } + file.weapons.append( ggChargeRifle ) + + // pulse blade + GunGameWeapon ggPulseBlade = { weapon = "mp_weapon_grenade_sonar", mods = [ "pas_power_cell", "amped_tacticals" ], offhandSlot = 0 } + file.weapons.append( ggPulseBlade ) + + + // set this to the number of guns + GameMode_SetDefaultScoreLimits( GAMEMODE_GG, file.weapons.len(), 0 ) + + #if SERVER + GameMode_AddServerInit( GAMEMODE_GG, GamemodeGG_Init ) + GameMode_AddServerInit( GAMEMODE_GG, GamemodeFFAShared_Init ) + GameMode_SetPilotSpawnpointsRatingFunc( GAMEMODE_GG, RateSpawnpoints_Generic ) + GameMode_SetTitanSpawnpointsRatingFunc( GAMEMODE_GG, RateSpawnpoints_Generic ) + #elseif CLIENT + GameMode_AddClientInit( GAMEMODE_GG, ClGamemodeGG_Init ) + GameMode_AddClientInit( GAMEMODE_GG, GamemodeFFAShared_Init ) + GameMode_AddClientInit( GAMEMODE_GG, ClGamemodeFFA_Init ) + #endif + #if !UI + GameMode_SetScoreCompareFunc( GAMEMODE_GG, CompareAssaultScore ) + GameMode_AddSharedInit( GAMEMODE_GG, GamemodeFFA_Dialogue_Init ) + #endif +} + +array<GunGameWeapon> function GetGunGameWeapons() +{ + return file.weapons +}
\ No newline at end of file diff --git a/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_inf.gnut b/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_inf.gnut new file mode 100644 index 00000000..b73dc194 --- /dev/null +++ b/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_inf.gnut @@ -0,0 +1,56 @@ +global function Sh_GamemodeInfection_Init + +global const string GAMEMODE_INFECTION = "inf" +global const int INFECTION_TEAM_SURVIVOR = TEAM_MILITIA +global const int INFECTION_TEAM_INFECTED = TEAM_IMC + +void function Sh_GamemodeInfection_Init() +{ + // create custom gamemode + AddCallback_OnCustomGamemodesInit( CreateGamemodeInfection ) + AddCallback_OnRegisteringCustomNetworkVars( InfectionRegisterNetworkVars ) +} + +void function CreateGamemodeInfection() +{ + GameMode_Create( GAMEMODE_INFECTION ) + GameMode_SetName( GAMEMODE_INFECTION, "#GAMEMODE_inf" ) + GameMode_SetDesc( GAMEMODE_INFECTION, "#PL_inf_desc" ) + GameMode_SetGameModeAnnouncement( GAMEMODE_INFECTION, "ffa_modeDesc" ) + GameMode_SetDefaultTimeLimits( GAMEMODE_INFECTION, 5, 0.0 ) + GameMode_AddScoreboardColumnData( GAMEMODE_INFECTION, "#SCOREBOARD_KILLS", PGS_ASSAULT_SCORE, 2 ) + GameMode_SetColor( GAMEMODE_INFECTION, [147, 204, 57, 255] ) + + #if SERVER + GameMode_AddServerInit( GAMEMODE_INFECTION, GamemodeInfection_Init ) + GameMode_SetPilotSpawnpointsRatingFunc( GAMEMODE_INFECTION, RateSpawnpoints_Generic ) + GameMode_SetTitanSpawnpointsRatingFunc( GAMEMODE_INFECTION, RateSpawnpoints_Generic ) + #elseif CLIENT + GameMode_AddClientInit( GAMEMODE_INFECTION, ClGamemodeInfection_Init ) + #endif + #if !UI + GameMode_SetScoreCompareFunc( GAMEMODE_INFECTION, CompareAssaultScoreAndInfection ) + #endif +} + +void function InfectionRegisterNetworkVars() +{ + if ( GAMETYPE != GAMEMODE_INFECTION ) + return + + Remote_RegisterFunction( "ServerCallback_YouAreInfected" ) + Remote_RegisterFunction( "ServerCallback_AnnounceFirstInfected" ) + Remote_RegisterFunction( "ServerCallback_AnnounceLastSurvivor" ) +} + +int function CompareAssaultScoreAndInfection( entity a, entity b ) +{ + // survivors should be on top, then sort by assault score + + if ( a.GetTeam() == INFECTION_TEAM_INFECTED && b.GetTeam() == INFECTION_TEAM_SURVIVOR ) + return 1 + else if ( a.GetTeam() == INFECTION_TEAM_SURVIVOR && b.GetTeam() == INFECTION_TEAM_INFECTED ) + return -1 + + return CompareAssaultScore( a, b ) +}
\ No newline at end of file diff --git a/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_kr.gnut b/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_kr.gnut new file mode 100644 index 00000000..2a320077 --- /dev/null +++ b/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_kr.gnut @@ -0,0 +1,53 @@ +global function Sh_GamemodeKR_Init + +global const string GAMEMODE_KR = "kr" + +void function Sh_GamemodeKR_Init() +{ + // create custom gamemode + AddCallback_OnCustomGamemodesInit( CreateGamemodeKR ) + AddCallback_OnRegisteringCustomNetworkVars( KRRegisterNetworkVars ) +} + +void function CreateGamemodeKR() +{ + GameMode_Create( GAMEMODE_KR ) + GameMode_SetName( GAMEMODE_KR, "#GAMEMODE_kr" ) + GameMode_SetDesc( GAMEMODE_KR, "#PL_kr_desc" ) + GameMode_SetGameModeAnnouncement( GAMEMODE_KR, "ffa_modeDesc" ) + GameMode_SetDefaultTimeLimits( GAMEMODE_KR, 10, 0.0 ) + GameMode_AddScoreboardColumnData( GAMEMODE_KR, "#SCOREBOARD_KR_RECORD", PGS_ASSAULT_SCORE, 2 ) + GameMode_AddScoreboardColumnData( GAMEMODE_KR, "#SCOREBOARD_PILOT_KILLS", PGS_PILOT_KILLS, 2 ) + GameMode_SetColor( GAMEMODE_KR, [147, 204, 57, 255] ) + + #if SERVER + GameMode_AddServerInit( GAMEMODE_KR, GamemodeKR_Init ) + GameMode_AddServerInit( GAMEMODE_KR, GamemodeFFAShared_Init ) + GameMode_SetPilotSpawnpointsRatingFunc( GAMEMODE_KR, RateSpawnpoints_Generic ) + GameMode_SetTitanSpawnpointsRatingFunc( GAMEMODE_KR, RateSpawnpoints_Generic ) + #elseif CLIENT + GameMode_AddClientInit( GAMEMODE_KR, ClGamemodeKR_Init ) + GameMode_AddClientInit( GAMEMODE_KR, GamemodeFFAShared_Init ) + GameMode_AddClientInit( GAMEMODE_KR, ClGamemodeFFA_Init ) + #endif + #if !UI + GameMode_SetScoreCompareFunc( GAMEMODE_KR, CompareAssaultScore ) + GameMode_AddSharedInit( GAMEMODE_KR, GamemodeFFA_Dialogue_Init ) + #endif +} + +void function KRRegisterNetworkVars() +{ + if ( GAMETYPE != GAMEMODE_KR ) + return + + Remote_RegisterFunction( "ServerCallback_FlagSpawnIncoming" ) + Remote_RegisterFunction( "ServerCallback_NewKillRacer" ) + Remote_RegisterFunction( "ServerCallback_EndKillrace" ) + + RegisterNetworkedVariable( "killRaceTime", SNDC_PLAYER_EXCLUSIVE, SNVT_TIME, 0.0 ) + + #if CLIENT + RegisterNetworkedVariableChangeCallback_time( "killRaceTime", ShowTimeGainOnKill ) + #endif +}
\ No newline at end of file diff --git a/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_sbox.gnut b/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_sbox.gnut new file mode 100644 index 00000000..893d9410 --- /dev/null +++ b/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_sbox.gnut @@ -0,0 +1,20 @@ +global function Sh_GamemodeSbox_Init + +global const string GAMEMODE_SBOX = "sbox" + +void function Sh_GamemodeSbox_Init() +{ + // create custom gametype + AddCallback_OnCustomGamemodesInit( CreateGamemodeSbox ) +} + +void function CreateGamemodeSbox() +{ + GameMode_Create( GAMEMODE_SBOX ) + GameMode_SetName( GAMEMODE_SBOX, "#PL_sbox" ) + GameMode_SetDesc( GAMEMODE_SBOX, "#PL_sbox_desc" ) + + #if SERVER + GameMode_AddServerInit( GAMEMODE_SBOX, GamemodeSbox_Init ) + #endif +}
\ No newline at end of file diff --git a/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_tt.gnut b/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_tt.gnut new file mode 100644 index 00000000..9e879843 --- /dev/null +++ b/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_tt.gnut @@ -0,0 +1,34 @@ +global function Sh_GamemodeTT_Init + +global const string GAMEMODE_TT = "tt" + +void function Sh_GamemodeTT_Init() +{ + // create custom gamemode + AddCallback_OnCustomGamemodesInit( CreateGamemodeTT ) +} + +void function CreateGamemodeTT() +{ + GameMode_Create( GAMEMODE_TT ) + GameMode_SetName( GAMEMODE_TT, "#GAMEMODE_TT" ) + GameMode_SetDesc( GAMEMODE_TT, "#PL_tt_desc" ) + GameMode_SetGameModeAnnouncement( GAMEMODE_TT, "gnrc_modeDesc" ) + GameMode_SetDefaultScoreLimits( GAMEMODE_TT, 20, 0 ) + GameMode_SetDefaultTimeLimits( GAMEMODE_TT, 15, 0.0 ) + GameMode_AddScoreboardColumnData( GAMEMODE_TT, "#SCOREBOARD_SCORE", PGS_ASSAULT_SCORE, 2 ) + GameMode_AddScoreboardColumnData( GAMEMODE_TT, "#SCOREBOARD_TITAN_KILLS", PGS_TITAN_KILLS, 1 ) + GameMode_AddScoreboardColumnData( GAMEMODE_TT, "#SCOREBOARD_PILOT_KILLS", PGS_PILOT_KILLS, 2 ) + GameMode_SetColor( GAMEMODE_TT, [200, 40, 40, 255] ) + + #if SERVER + GameMode_AddServerInit( GAMEMODE_TT, GamemodeTT_Init ) + GameMode_SetPilotSpawnpointsRatingFunc( GAMEMODE_TT, RateSpawnpoints_Generic ) + GameMode_SetTitanSpawnpointsRatingFunc( GAMEMODE_TT, RateSpawnpoints_Generic ) + #elseif CLIENT + GameMode_AddClientInit( GAMEMODE_TT, ClGamemodeTT_Init ) + #endif + #if !UI + GameMode_SetScoreCompareFunc( GAMEMODE_TT, CompareAssaultScore ) + #endif +}
\ No newline at end of file diff --git a/Northstar.Custom/scripts/vscripts/gamemodes/sh_riff_floor_is_lava.nut b/Northstar.Custom/scripts/vscripts/gamemodes/sh_riff_floor_is_lava.nut new file mode 100644 index 00000000..3a8ac8e7 --- /dev/null +++ b/Northstar.Custom/scripts/vscripts/gamemodes/sh_riff_floor_is_lava.nut @@ -0,0 +1,262 @@ +untyped + +global function RiffFloorIsLavaShared_Init + +global function GetFogHeight +global function GetLethalFogTopTitan +global function GetLethalFogTop +global function GetLethalFogBottom +global function GetVisibleFogTop +global function GetVisibleFogBottom +global function GetMaxTitanSpawnFogHeight + +global function IsEntInSafeVolume +global function IsEntInLethalVolume + +struct +{ + float fogDepth = 64.0 + float maxTitanSpawnFogDepth = 170.0 + array lethalTitanVolumes + array lethalPilotVolumes + array safePilotVolumes + bool volumesDebug = false + table< string, float > lethalFogHeights +} file + +function RiffFloorIsLavaShared_Init() +{ + switch ( GetMapName() ) + { + case "mp_lagoon": + AddLethalTitanVolume( Vector( -45.656845, 3555.449463, 40.422455 ), Vector( 1209.944092, 5599.152832, 234.813217 ) ) + AddLethalTitanVolume( Vector( -5232.395020, 205.406250, 0.031250 ), Vector( -777.285400, 4075.119385, 300.634771 ) ) + AddLethalTitanVolume( Vector( -4686.448730, 4190.655273, 20.642021 ), Vector( -41.171387, 9072.019043, 200.697632 ) ) + AddLethalTitanVolume( Vector( -7586.861328, 4072.843994, 0.031254 ), Vector( -7012.854004, 4614.723145, 302.714966 ) ) + break + + case "mp_nexus": + AddLethalTitanVolume( Vector( 1567.173523, -27.374023, 209.422455 ), Vector( 2516.944092, 2585.152832, 500.813217 ) ) + AddLethalTitanVolume( Vector( -2825.766113, 5056.203125, 243.706253 ), Vector( -2255.893555, 5688.334961, 400.251160 ) ) + AddLethalTitanVolume( Vector( -5717.068359, -349.599976, 189.669785 ), Vector( -4960.125000, 758.196350, 400.268097 ) ) + AddLethalTitanVolume( Vector( -3292.942139, 1713.916626, 233.749817 ), Vector( -2322.137695, 3091.497070, 477.462799 ) ) + AddLethalTitanVolume( Vector( -878.712769, -5878.528809, 71.145332 ), Vector( 338.741943, -5014.183594, 443.146179 ) ) + AddLethalTitanVolume( Vector( -6930.957031, -1277.388550, 107.619537 ), Vector( -6574.779297, -779.338013, 685.485901 ) ) + break + + case "mp_outpost_207": + AddSafePilotVolume( Vector( 2359.524658, -631.065918, -256.714142 ), Vector( 2623.051270, -182.453323, -220.125641 ) ) + + AddLethalTitanVolume( Vector( -100.349350, 2218.763916, -330.968750 ), Vector( 2561.511230, 4030.028320, -133.065369 ) ) + AddLethalTitanVolume( Vector( -452.031647, 282.244629, -255.968750 ), Vector( 2241.971069, 1594.146851, -100.212967 ) ) + break + + case "mp_training_ground": + AddSafePilotVolume( Vector( -2618.053223, -3435.505615, 40.215054 ), Vector( -2309.167236, -3321.788330, 146.218491 ) ) + AddSafePilotVolume( Vector( -3187.767090, -2886.333496, 45.746925 ), Vector( -2865.753174, -2681.679443, 109.089279 ) ) + AddSafePilotVolume( Vector( -3717.815674, -2350.831543, 47.694588 ), Vector( -3431.980957, -2145.194092, 120.640717 ) ) + + AddLethalTitanVolume( Vector( -3439.702179, -2227.359741, -8.036909 ), Vector( 2185.765076, 2384.459412, 225.199013 ) ) + AddLethalTitanVolume( Vector( -3200.747681, -4456.148926, 0.0 ), Vector( -1261.621826, -3000.667480, 160.689011 ) ) + AddLethalTitanVolume( Vector( 1261.621826, 3000.667480, 0.0 ), Vector( 2700.747681, 4456.148926, 160.689011 ) ) + AddLethalTitanVolume( Vector( -3291.510986, 3483.724609, 4.031250 ), Vector( -2018.871826, 4463.995850, 122.675621 ) ) + AddLethalTitanVolume( Vector( 2018.871826, -3638.995850, 4.031250 ), Vector( 2241.510986, -3483.724609, 122.675621 ) ) + AddLethalTitanVolume( Vector( -2798.816528, -2302.519897, -30.285933 ), Vector( -1561.589355, -791.616699, 300.917297 ) ) + AddLethalTitanVolume( Vector( 3809.276123, 1639.001587, 11.272846 ), Vector( 4056.847412, 1862.587036, 100.205643 ) ) + AddLethalTitanVolume( Vector( -4189.979492, -3298.505127, -5.597572 ), Vector( -3398.622803, -560.027344, 147.054291 ) ) + break + + case "mp_runoff": + AddLethalPilotVolume( Vector( -621.502319, -5743.472656, 299.838928 ), Vector( -397.317047, -5578.512207, 425.437927 ) ) + break + } +} + +float function GetFogHeight() +{ + string mapName = GetMapName() + + file.lethalFogHeights = {} + file.lethalFogHeights[ "mp_angel_city" ] <- 216.0 // cp ctf mfd + file.lethalFogHeights[ "mp_lagoon" ] <- 98.0 // cp mfd + file.lethalFogHeights[ "mp_nexus" ] <- 310.0 // mfd + file.lethalFogHeights[ "mp_o2" ] <- 40.0 // mfd + file.lethalFogHeights[ "mp_outpost_207" ] <- -225.0 // mfd + file.lethalFogHeights[ "mp_training_ground" ] <- 80.0 // cp mfd + file.lethalFogHeights[ "mp_harmony_mines" ] <- 260.0 // cp ctf mfd + file.lethalFogHeights[ "mp_haven" ] <- 128.0 // mfd + + // good map, needs spawns, etc... + file.lethalFogHeights[ "mp_rise" ] <- 420.0 // mfd + file.lethalFogHeights[ "mp_runoff" ] <- 340.0 // mfd + file.lethalFogHeights[ "mp_zone_18" ] <- 460.0 // mfd + file.lethalFogHeights[ "mp_sandtrap" ] <- 64.0 + + // these don't work as well + file.lethalFogHeights[ "mp_swampland" ] <- 350.0 // mfd + file.lethalFogHeights[ "mp_backwater" ] <- 320.0 // mfd + file.lethalFogHeights[ "mp_airbase" ] <- 450.0 + file.lethalFogHeights[ "mp_boneyard" ] <- 64.0 + file.lethalFogHeights[ "mp_colony" ] <- 270.0 + file.lethalFogHeights[ "mp_corporate" ] <- -765.0 + file.lethalFogHeights[ "mp_fracture" ] <- 270.0 + file.lethalFogHeights[ "mp_overlook" ] <- 16.0 + file.lethalFogHeights[ "mp_relic" ] <- 475.0 + file.lethalFogHeights[ "mp_smugglers_cove" ] <- 400.0 + file.lethalFogHeights[ "mp_wargames" ] <- 64.0 + file.lethalFogHeights[ "mp_switchback" ] <- 840.0 + + file.lethalFogHeights[ "mp_chin_rodeo_express" ] <- 1580.0 + + // custom: titanfall 2 maps + // TODO: really need a modular system here + file.lethalFogHeights[ "mp_colony02" ] <- 270.0 // map changed name from tf1 => tf2 + file.lethalFogHeights[ "mp_glitch" ] <- 200.0 + file.lethalFogHeights[ "mp_grave" ] <- 2350.0 + file.lethalFogHeights[ "mp_homestead" ] <- 64.0 + file.lethalFogHeights[ "mp_forwardbase_kodai" ] <- 930.0 + file.lethalFogHeights[ "mp_thaw" ] <- 32.0 + file.lethalFogHeights[ "mp_black_water_canal" ] <- 32.0 + file.lethalFogHeights[ "mp_eden" ] <- 175.0 + file.lethalFogHeights[ "mp_drydock" ] <- 300.0 + file.lethalFogHeights[ "mp_crashsite3" ] <- 800.0 // crashsite is just as awful for this as it is for anything else + file.lethalFogHeights[ "mp_complex3" ] <- 630.0 + file.lethalFogHeights[ "mp_relic02" ] <- 250.0 // not great, tf1's would honestly be worse though imo + + // lf maps: overall a bit hit or miss, many likely have spawn issues + file.lethalFogHeights[ "mp_lf_stacks" ] <- -9999.0 // entirely nonworking, breaks spawns no matter what from what i can tell, could potentially use safe zones for this? + file.lethalFogHeights[ "mp_lf_deck" ] <- -9999.0 // nonworking fogcontroller so fog is invisible + file.lethalFogHeights[ "mp_lf_uma" ] <- 64.0 + file.lethalFogHeights[ "mp_lf_meadow" ] <- 64.0 + file.lethalFogHeights[ "mp_lf_traffic" ] <- 50.0 + file.lethalFogHeights[ "mp_lf_township" ] <- 64.0 + + if ( mapName in file.lethalFogHeights ) + return file.lethalFogHeights[ mapName ] + + return 64.0 +} + +float function GetLethalFogTopTitan() +{ + float fogTop = GetLethalFogTop() + + switch ( GetMapName() ) + { + case "mp_lagoon": + case "mp_nexus": + case "mp_outpost_207": + case "mp_training_ground": + case "mp_chin_rodeo_express": + return fogTop + } + + return fogTop + 256.0 +} + +float function GetLethalFogTop() +{ + return GetFogHeight() - file.fogDepth * 0.2 +} + +float function GetLethalFogBottom() +{ + return GetFogHeight() - file.fogDepth * 0.7 +} + +float function GetVisibleFogTop() +{ + return GetFogHeight() + file.fogDepth * 0.5 +} + +float function GetVisibleFogBottom() +{ + return GetFogHeight() - file.fogDepth * 0.5 +} + +float function GetMaxTitanSpawnFogHeight() +{ + return GetFogHeight() - file.maxTitanSpawnFogDepth +} + +function AddLethalTitanVolume( vector volumeMins, vector volumeMaxs ) +{ + Assert( volumeMins.x < volumeMaxs.x ) + Assert( volumeMins.y < volumeMaxs.y ) + Assert( volumeMins.z < volumeMaxs.z ) + + file.lethalTitanVolumes.append( { mins = volumeMins, maxs = volumeMaxs } ) +} + +function AddLethalPilotVolume( vector volumeMins, vector volumeMaxs ) +{ + Assert( volumeMins.x < volumeMaxs.x ) + Assert( volumeMins.y < volumeMaxs.y ) + Assert( volumeMins.z < volumeMaxs.z ) + + file.lethalPilotVolumes.append( { mins = volumeMins, maxs = volumeMaxs } ) +} + +function AddSafePilotVolume( vector volumeMins, vector volumeMaxs ) +{ + Assert( volumeMins.x < volumeMaxs.x ) + Assert( volumeMins.y < volumeMaxs.y ) + Assert( volumeMins.z < volumeMaxs.z ) + + file.safePilotVolumes.append( { mins = volumeMins, maxs = volumeMaxs } ) +} + +function IsEntInSafeVolume( entity ent ) +{ + if ( ent.IsPlayer() ) + { + foreach ( volume in file.safePilotVolumes ) + { + vector entOrg = ent.GetOrigin() + + #if SERVER + if ( file.volumesDebug ) + DebugDrawBox( Vector( 0.0, 0.0, 0.0 ), volume.mins, volume.maxs, 0, 0, 255, 1, 0.1 ) + #endif + + if ( PointIsWithinBounds( entOrg, expect vector( volume.mins ), expect vector( volume.maxs ) ) ) + return true + } + } +} + +function IsEntInLethalVolume( entity ent ) +{ + if ( ent.IsTitan() ) + { + foreach ( volume in file.lethalTitanVolumes ) + { + vector entOrg = ent.GetOrigin() + + #if SERVER + if ( file.volumesDebug ) + DebugDrawBox( Vector( 0.0, 0.0, 0.0 ), volume.mins, volume.maxs, 255, 255, 0, 1, 0.1 ) + #endif + + if ( PointIsWithinBounds( entOrg, expect vector( volume.mins ), expect vector( volume.maxs ) ) ) + return true + } + } + else if ( ent.IsPlayer() ) + { + foreach ( volume in file.lethalPilotVolumes ) + { + vector entOrg = ent.GetOrigin() + + #if SERVER + if ( file.volumesDebug ) + DebugDrawBox( Vector( 0.0, 0.0, 0.0 ), volume.mins, volume.maxs, 255, 255, 0, 1, 0.1 ) + #endif + + if ( PointIsWithinBounds( entOrg, expect vector( volume.mins ), expect vector( volume.maxs ) ) ) + return true + } + } + + return false +} diff --git a/Northstar.Custom/scripts/vscripts/mp/levels/mp_box.nut b/Northstar.Custom/scripts/vscripts/mp/levels/mp_box.nut new file mode 100644 index 00000000..a1828009 --- /dev/null +++ b/Northstar.Custom/scripts/vscripts/mp/levels/mp_box.nut @@ -0,0 +1,6 @@ +global function CodeCallback_MapInit + +void function CodeCallback_MapInit() +{ + +}
\ No newline at end of file diff --git a/Northstar.Custom/scripts/vscripts/northstar_custom_autoprecache.gnut b/Northstar.Custom/scripts/vscripts/northstar_custom_autoprecache.gnut new file mode 100644 index 00000000..62d089f7 --- /dev/null +++ b/Northstar.Custom/scripts/vscripts/northstar_custom_autoprecache.gnut @@ -0,0 +1,14 @@ +untyped +global function NorthstarCustomAutoprecache + +void function NorthstarCustomAutoprecache() +{ + PrecacheWeapon( "mp_weapon_peacekraber" ) + PrecacheWeapon( "melee_pilot_kunai" ) + + if ( GAMETYPE == GAMEMODE_SBOX ) + PrecacheWeapon( "mp_weapon_toolgun" ) + + // will include this as a custom asset when mod v2 is written + //PrecacheModel( $"models/titans/buddy/titan_buddy.mdl" ) +}
\ No newline at end of file diff --git a/Northstar.Custom/scripts/vscripts/titan/sh_first_person_embark.gnut b/Northstar.Custom/scripts/vscripts/titan/sh_first_person_embark.gnut new file mode 100644 index 00000000..0c95ae4c --- /dev/null +++ b/Northstar.Custom/scripts/vscripts/titan/sh_first_person_embark.gnut @@ -0,0 +1,41 @@ +global function FirstPersonEmbark_Init + +#if CLIENT + global function ServerCallback_HideHudForFPEmbark +#endif + +void function FirstPersonEmbark_Init() +{ + // atm do this no matter what playlist we're on since playlist overrides seem to get sent to clients after networkvar registration + // not nice but whatever lol + AddCallback_OnRegisteringCustomNetworkVars( FirstPersonEmbark_RegisterCustomNetworkFunctions ) + + // busted rn lol + if ( GetCurrentPlaylistVarInt( "fp_embark_enabled", 0 ) == 0 ) + return + + #if CLIENT + AddCallback_PlayerClassChanged( ShowHudOnEmbarkFinished ) + #endif +} + +void function FirstPersonEmbark_RegisterCustomNetworkFunctions() +{ + Remote_RegisterFunction( "ServerCallback_HideHudForFPEmbark" ) +} + +#if CLIENT +void function ServerCallback_HideHudForFPEmbark() +{ + thread MainHud_TurnOff_RUI( true ) + HidePermanentCockpitRui() +} + +void function ShowHudOnEmbarkFinished( entity player ) +{ + if ( !player.IsTitan() ) + return + + ShowPermanentCockpitRui() +} +#endif
\ No newline at end of file diff --git a/Northstar.Custom/scripts/vscripts/titan/sh_titan_embark.gnut b/Northstar.Custom/scripts/vscripts/titan/sh_titan_embark.gnut new file mode 100644 index 00000000..f9df2730 --- /dev/null +++ b/Northstar.Custom/scripts/vscripts/titan/sh_titan_embark.gnut @@ -0,0 +1,2253 @@ +untyped + +global function TitanEmbark_Init + +global function TitanCanStand +global function DebugEmbarkTimes +global function TitanIsCurrentlyEmbarkableForPlayer +global function PlayerCanEmbarkTitan +global function PlayerCanImmediatelyEmbarkTitan +global function FindBestEmbark +global function GenerateEmbarkActionTable +global function FindEmbarkActionForCriteria +global function GetRandomEmbarkAction +global function PlayerCanDisembarkTitan +global function IsPlayerDisembarking + +#if SERVER + global function ForceScriptedEmbark + global function PlayerCanEmbarkIntoTitan + global function IsPlayerEmbarking + global function PlayerEmbarksTitan + global function PlayerLungesToEmbark + global function FindBestEmbarkForNpcAnim + global function PlayerDisembarksTitan + global function ForcedTitanDisembark + global function ForcedTitanDisembarkCustomAnims + global function PhaseEmbarkPhaseStart + global function PhaseEmbarkPhaseStop + global function OverrideCockpitLightFX + global function StartCockpitLightThink + global function CockpitLightStop + global function Embark_DelayedFadeOut + global function PlayerIsFarOffTheGround + + global function SetSmallDisembarkFailSafeTeleportVector //TODO: Re-examine this for next game, probably should have different values for SP versus MP + global function SetLargeDisembarkFailSafeTeleportVector //TODO: Re-examine this for next game, probably should have different values for SP versus MP +#endif + +#if DEV + global function SetEmbarkDebugPrint +#endif + +const FAST_EMBARK = 1 +const SKIP_AHEAD_TIME = 2.0 + +#if MP +const EMBARK_FADE_TIME = 0.2 +#else +const EMBARK_FADE_TIME = 0.3 +#endif + +struct +{ + asset cockpitLightFX = $"xo_cockpit_dlight" + bool embarkDebugPrint = false + vector smallDisembarkFailSafeTeleportVector = < 400, 400, 200 > + vector largeDisembarkFailSafeTeleportVector = < 600, 600, 600 > +} file + +function TitanEmbark_Init() +{ + if ( reloadingScripts ) + return + + level.pilotDisembarkBounds <- {} + local end = {} + end.up <- 50.363811 + end.forward <- 110.146927 + end.right <- 13.045869 + end.yaw <- -8.381051 + + local start = {} + start.up <- 156.750015 + start.forward <- -13.429688 + start.right <- -11.374998 + start.yaw <- 0.409042 + + RefreshTitanEmbarkActions() + + level.pilotDisembarkBounds.end <- end + level.pilotDisembarkBounds.start <- start + + RegisterSignal( "OnComplete" ) + RegisterSignal( "startembark" ) // temp + + RegisterSignal( "DisembarkingTitan" ) + RegisterSignal( "player_embarks_titan" ) + + #if SERVER + // add all the embark anims with this suffix + AddEmbarkAnims( "titan_atlas", "atlas", true ) + AddEmbarkAnims( "titan_buddy", "buddy", true ) + AddEmbarkAnims( "titan_ogre", "ogre", true ) + AddEmbarkAnims( "titan_stryder", "stryder", true ) + + // AddEmbarkAudio( "titan_atlas", "atlas" ) + // AddEmbarkAudio( "titan_buddy", "buddy" ) + // AddEmbarkAudio( "titan_ogre", "ogre" ) + // AddEmbarkAudio( "titan_stryder", "stryder" ) + + RegisterSignal( "titanKneel" ) + RegisterSignal( "titanStand" ) + RegisterSignal( "titanEmbark" ) + RegisterSignal( "PhaseEmbarkPhaseStop" ) + + RegisterSignal( "CockpitLightStop" ) + PrecacheParticleSystem( $"xo_cockpit_dlight" ) + + AddClientCommandCallback( "TitanDisembark", ClientCommand_TitanDisembark ) // + AddClientCommandCallback( "TitanKneel", ClientCommand_TitanKneel ) // + //AddClientCommandCallback( "TitanStand", ClientCommand_TitanStand ) // + AddClientCommandCallback( "TitanNextMode", ClientCommand_TitanNextMode ) // + + AddCallback_OnTitanBecomesPilot( TitanBecomesPilot_UpdateRodeoRiderHud ) + AddCallback_OnPilotBecomesTitan( PilotBecomesTitan_UpdateRodeoRiderHud ) + #endif +} + +void function OverrideCockpitLightFX( asset fx ) +{ + file.cockpitLightFX = fx +} + +void function AddEmbarkAnims( string titan, string titanSubClass, bool thirdPersonOnly = false ) +{ + // anims are string-constructed from these types: + local Array = + [ + "kneel_front", + "kneel_behind", + "kneel_right", + "kneel_left", + "kneel_airgrab", + + "stand_front", + "stand_right", + "stand_left", + "stand_behind", + "stand_airgrab", + + "above_right", + "above_left", + "kneel_above_right", + "kneel_above_left", + ] + + + // force consistency in animation names + foreach ( item in Array ) + { + array<string> suffixes = [ "", "_fast" ] + foreach ( suffix in suffixes ) + { + //printt( "Adding base " + item + " to " + titan ) + local thirdPersonAlias = "pt_mount_" + item + suffix + local firstPersonAlias = "ptpov_mount_" + item + suffix + local thirdPersonAnim = "pt_mount_" + titanSubClass + "_" + item + suffix + local firstPersonAnim = "ptpov_mount_" + titanSubClass + "_" + item + suffix + + if ( thirdPersonOnly ) + firstPersonAnim = "" + + AddAnimAlias( titanSubClass, thirdPersonAlias, thirdPersonAnim ) + AddAnimAlias( titanSubClass, firstPersonAlias, firstPersonAnim ) + } + } +} + +function AddEmbarkAudio( titan, titanSubClass ) +{ + // audio files are string-constructed from these types: + local Array = + [ + "Kneeling_Front", + "Kneeling_Behind", + "Kneeling_Right", + "Kneeling_Left", + "Kneeling_AboveRight", + "Kneeling_AboveLeft", + + "Standing_Front", + "Standing_Behind", + "Standing_Airgrab", + "Standing_AboveRight", + "Standing_AboveLeft" + ] + + + // force consistency in audio file names + foreach ( item in Array ) + { + //printt( "Adding base " + item + " to " + titan ) + local thirdPersonAlias = "Embark_" + item + "_3P" + local firstPersonAlias = "Embark_" + item + "_1P" + local thirdPersonAnim = titanSubClass + "_Embark_" + item + "_3P" + local firstPersonAnim = titanSubClass + "_Embark_" + item + "_1P" + + AddAudioAlias( titanSubClass, thirdPersonAlias, thirdPersonAnim ) + AddAudioAlias( titanSubClass, firstPersonAlias, firstPersonAnim ) + } +} + +function RefreshTitanEmbarkActions() +{ + if ( "titanEmbarkActions" in level ) + { + delete level.titanEmbarkActions + delete level.titanEmbarkFarthestDistance + } + + local groundDist = 260 + + level.titanEmbarkActions <- [] + level.titanEmbarkFarthestDistance <- 0 + local action + + action = + { + direction = Vector( 1, 0, 0 ) + distance = groundDist + embark = "front" + minDot = 0.4 + priority = 1 // tried after priority 0 actions + titanCanStandRequired = false + //onGround = null // either + useAnimatedRefAttachment = true + alignFrontEnabled = true + canSkipAhead = true + + animSet = + { + firstPersonKneelingAlias = "ptpov_mount_kneel_front" + thirdPersonKneelingAlias = "pt_mount_kneel_front" + firstPersonStandingAlias = "ptpov_mount_stand_front" + thirdPersonStandingAlias = "pt_mount_stand_front" + titanKneelingAnim = "at_mount_kneel_front" + titanStandingAnim = "at_mount_stand_front" + + } + + audioSet = + { + firstPersonKneelingAudioAlias = "Embark_Kneeling_Front_1P" + thirdPersonKneelingAudioAlias = "Embark_Kneeling_Front_3P" + firstPersonStandingAudioAlias = "Embark_Standing_Front_1P" + thirdPersonStandingAudioAlias = "Embark_Standing_Front_3P" + + } + } + level.titanEmbarkActions.append( action ) + + action = + { + direction = Vector( 1, 0, 0 ) + distance = groundDist + embark = "front" + minDot = -1 + priority = 2 // tried after priority 1 actions + titanCanStandRequired = false + //onGround = null // either + useAnimatedRefAttachment = true + alignFrontEnabled = true + canSkipAhead = true + + animSet = + { + firstPersonKneelingAlias = "ptpov_mount_kneel_front" + thirdPersonKneelingAlias = "pt_mount_kneel_front" + firstPersonStandingAlias = "ptpov_mount_stand_front" + thirdPersonStandingAlias = "pt_mount_stand_front" + titanKneelingAnim = "at_mount_kneel_front" + titanStandingAnim = "at_mount_stand_front" + } + + audioSet = + { + firstPersonKneelingAudioAlias = "Embark_Kneeling_Front_1P" + thirdPersonKneelingAudioAlias = "Embark_Kneeling_Front_3P" + firstPersonStandingAudioAlias = "Embark_Standing_Front_1P" + thirdPersonStandingAudioAlias = "Embark_Standing_Front_3P" + } + } + level.titanEmbarkActions.append( action ) + + action = + { + direction = Vector( 0, 1, 0 ) + distance = groundDist + embark = "left" + minDot = 0.4 + priority = 1 // tried after priority 0 actions + titanCanStandRequired = true + useAnimatedRefAttachment = true + alignFrontEnabled = true + canSkipAhead = true + //onGround = null // either + + animSet = + { + firstPersonKneelingAlias = "ptpov_mount_kneel_left" + thirdPersonKneelingAlias = "pt_mount_kneel_left" + firstPersonStandingAlias = "ptpov_mount_stand_front" + thirdPersonStandingAlias = "pt_mount_stand_left" + titanKneelingAnim = "at_mount_kneel_left" + titanStandingAnim = "at_mount_stand_left" + } + + audioSet = + { + firstPersonKneelingAudioAlias = "Embark_Kneeling_Left_1P" + thirdPersonKneelingAudioAlias = "Embark_Kneeling_Left_3P" + firstPersonStandingAudioAlias = "Embark_Standing_Front_1P" + thirdPersonStandingAudioAlias = "Embark_Standing_Front_3P" + } + } + level.titanEmbarkActions.append( action ) + + action = + { + direction = Vector( 0, -1, 0 ) + distance = groundDist + embark = "right" + minDot = 0.4 + priority = 1 // tried after priority 0 actions + titanCanStandRequired = true + useAnimatedRefAttachment = true + alignFrontEnabled = true + canSkipAhead = true + //onGround = null // either + animSet = + { + firstPersonKneelingAlias = "ptpov_mount_kneel_right" + thirdPersonKneelingAlias = "pt_mount_kneel_right" + firstPersonStandingAlias = "ptpov_mount_stand_front" + thirdPersonStandingAlias = "pt_mount_stand_right" + titanKneelingAnim = "at_mount_kneel_right" + titanStandingAnim = "at_mount_stand_right" + } + + audioSet = + { + firstPersonKneelingAudioAlias = "Embark_Kneeling_Right_1P" + thirdPersonKneelingAudioAlias = "Embark_Kneeling_Right_3P" + firstPersonStandingAudioAlias = "Embark_Standing_Front_1P" + thirdPersonStandingAudioAlias = "Embark_Standing_Front_3P" + } + } + level.titanEmbarkActions.append( action ) + + + action = + { + direction = Vector( -1, 0, 0 ) + distance = groundDist + embark = "behind" + minDot = 0.4 + priority = 1 // tried after priority 0 actions + titanCanStandRequired = true + useAnimatedRefAttachment = true + canSkipAhead = false + //onGround = null // either + + animSet = + { + firstPersonKneelingAlias = "ptpov_mount_kneel_behind" + thirdPersonKneelingAlias = "pt_mount_kneel_behind" + firstPersonStandingAlias = "ptpov_mount_stand_behind" + thirdPersonStandingAlias = "pt_mount_stand_behind" + titanKneelingAnim = "at_mount_kneel_behind" + titanStandingAnim = "at_mount_stand_behind" + } + + audioSet = + { + firstPersonKneelingAudioAlias = "Embark_Kneeling_Behind_1P" + thirdPersonKneelingAudioAlias = "Embark_Kneeling_Behind_3P" + firstPersonStandingAudioAlias = "Embark_Standing_Behind_1P" + thirdPersonStandingAudioAlias = "Embark_Standing_Behind_3P" + } + } + level.titanEmbarkActions.append( action ) + + action = + { + direction = Vector( 0, 0, 1 ) // 0 -1 1 + distance = 350 + embark = "above_close" + minDot = 0.88 + canSkipAhead = false + priority = 0 // priority actions are checked first + + titanCanStandRequired = true + useAnimatedRefAttachment = true + //onGround = false // must be in air + + animSets = + { + right = + { + direction = Vector( 0, -1, 0 ) // 0 -1 1 + firstPersonKneelingAlias = "ptpov_mount_kneel_above_right" + thirdPersonKneelingAlias = "pt_mount_kneel_above_right" + firstPersonStandingAlias = "ptpov_mount_above_right" + thirdPersonStandingAlias = "pt_mount_above_right" + titanKneelingAnim = "at_mount_kneel_above" + titanStandingAnim = "at_mount_above_right" + + audioSet = //An annoying exception to how the audioSet is organized, better this than the alternative and having to find the "best audio set" + { + firstPersonKneelingAudioAlias = "Embark_Kneeling_AboveRight_1P" + thirdPersonKneelingAudioAlias = "Embark_Kneeling_AboveRight_3P" + firstPersonStandingAudioAlias = "Embark_Standing_AboveRight_1P" + thirdPersonStandingAudioAlias = "Embark_Standing_AboveRight_3P" + } + } + + left = + { + direction = Vector( 0, 1, 0 ) // 0 -1 1 + firstPersonKneelingAlias = "ptpov_mount_kneel_above_left" + thirdPersonKneelingAlias = "pt_mount_kneel_above_left" + firstPersonStandingAlias = "ptpov_mount_above_left" + thirdPersonStandingAlias = "pt_mount_above_left" + titanKneelingAnim = "at_mount_kneel_above" + titanStandingAnim = "at_mount_above_left" + + audioSet = //An annoying exception to how the audioSet is organized, better this than the alternative and having to find the "best audio set" + { + firstPersonKneelingAudioAlias = "Embark_Kneeling_AboveLeft_1P" + thirdPersonKneelingAudioAlias = "Embark_Kneeling_AboveLeft_3P" + firstPersonStandingAudioAlias = "Embark_Standing_AboveLeft_1P" + thirdPersonStandingAudioAlias = "Embark_Standing_AboveLeft_3P" + } + } + } + } + level.titanEmbarkActions.append( action ) + + action = + { + direction = Vector( 0, 0, 1 ) + distance = 275 + embark = "above_grab" + minDot = 0.3 + titanCanStandRequired = true + //onGround = null // false // must be in air + useAnimatedRefAttachment = true + //lungeCheck = true + canSkipAhead = true + + alignFrontEnabled = true + + priority = 0 // priority actions are checked first + + animSet = + { + firstPersonKneelingAlias = "ptpov_mount_kneel_airgrab" + thirdPersonKneelingAlias = "pt_mount_kneel_airgrab" + titanKneelingAnim = "at_mount_kneel_airgrab" + + firstPersonStandingAlias = "ptpov_mount_stand_airgrab" + thirdPersonStandingAlias = "pt_mount_stand_airgrab" + titanStandingAnim = "at_mount_stand_airgrab" + } + + audioSet = + { + firstPersonKneelingAudioAlias = "Embark_Kneeling_Front_1P" + thirdPersonKneelingAudioAlias = "Embark_Kneeling_Front_3P" + firstPersonStandingAudioAlias = "Embark_Standing_Front_1P" + thirdPersonStandingAudioAlias = "Embark_Standing_Front_3P" + } + } + level.titanEmbarkActions.append( action ) + + local autoParms = + [ + "lungeCheck" + "alignFrontEnabled" + ] + + foreach ( action in level.titanEmbarkActions ) + { + if ( action.distance > level.titanEmbarkFarthestDistance ) + { + level.titanEmbarkFarthestDistance = action.distance + } + foreach ( parm in autoParms ) + { + if ( !( parm in action ) ) + action[ parm ] <- false + } + } +} + +function DebugEmbarkTimes() +{ + local settings = [ "atlas", "ogre", "stryder" ] + + array< asset > models = [ $"models/Humans/imc_pilot/male_cq/imc_pilot_male_cq.mdl", $"models/humans/pilot/female_cq/pilot_female_cq.mdl" ] + local times = {} + + foreach ( model in models ) + { + times[ model ] <- [] + entity prop = CreatePropDynamic( model, Vector(0,0,0), Vector(0,0,0) ) + printt( "Human model: " + model ) + + foreach ( setting in settings ) + { + printt( "Titan: " + setting ) + foreach ( action in level.titanEmbarkActions ) + { + printt( "Embark Direction: " + action.embark ) + if ( "animSet" in action ) + { + local animation = GetAnimFromAlias( setting, action.animSet.thirdPersonKneelingAlias ) + local time = prop.GetSequenceDuration( animation ) + times[ model ].append( { time = time, animation = animation } ) + printt( "Kneeling: " + time ) + + animation = GetAnimFromAlias( setting, action.animSet.thirdPersonStandingAlias ) + time = prop.GetSequenceDuration( animation ) + times[ model ].append( { time = time, animation = animation } ) + printt( "Standing: " + time ) + } + + if ( "animSets" in action ) + { + local animation = GetAnimFromAlias( setting, action.animSets.left.thirdPersonKneelingAlias ) + local time = prop.GetSequenceDuration( animation ) + times[ model ].append( { time = time, animation = animation } ) + printt( "Kneeling Left: " + time ) + + animation = GetAnimFromAlias( setting, action.animSets.left.thirdPersonStandingAlias ) + time = prop.GetSequenceDuration( animation ) + times[ model ].append( { time = time, animation = animation } ) + printt( "Standing Left: " + time ) + + animation = GetAnimFromAlias( setting, action.animSets.right.thirdPersonKneelingAlias ) + time = prop.GetSequenceDuration( animation ) + times[ model ].append( { time = time, animation = animation } ) + printt( "Kneeling Right: " + time ) + + animation = GetAnimFromAlias( setting, action.animSets.right.thirdPersonStandingAlias ) + time = prop.GetSequenceDuration( animation ) + times[ model ].append( { time = time, animation = animation } ) + printt( "Standing Right: " + time ) + } + + printt( " " ) + } + } + + prop.Kill_Deprecated_UseDestroyInstead() + } + + printt( "Time comparison: " ) + bool wrong = false + for ( int i = 0; i < times[ models[0] ].len(); i++ ) + { + if ( times[models[0]][i].time == times[models[1]][i].time ) + { + printt( "MATCH: " + ( i + 1 ) + " times: " + times[models[0]][i].time + " " + times[models[1]][i].time + " " + times[models[1]][i].animation ) + } + else + { + printt( "MISMATCH: " + ( i + 1 ) + " times: " + times[models[0]][i].time + " " + times[models[1]][i].time + " " + times[models[1]][i].animation ) + wrong = true + } + } +// Assert( !wrong, "Times did not match between male and female, see above" ) +} + + +#if SERVER +bool function ClientCommand_TitanKneel( entity player, array<string> args ) +{ + entity titan = player.GetPetTitan() + if ( !IsAlive( titan ) ) + return true + + titan.Signal( "titanKneel" ) + titan.s.standQueued = false + return true +} + +/*bool function ClientCommand_TitanStand( entity player, array<string> args ) +{ + entity titan = player.GetPetTitan() + if ( !IsAlive( titan ) ) + return true + + titan.Signal( "titanStand" ) + titan.s.standQueued = true + titan.s.kneelQueued = false + return true +}*/ + +bool function ClientCommand_TitanNextMode( entity player, array<string> args ) +{ + if ( !IsAlive( player ) ) + return true + + entity titan = player.GetPetTitan() + if ( IsAlive( titan ) ) + NPCTitanNextMode( titan, player ) + + return true +} +#endif // SERVER + + +function EmbarkLine( player, titan ) +{ + player.EndSignal( "startembark" ) + local ref = player.LookupAttachment( "ref" ) + local hijack = titan.LookupAttachment( "hijack" ) + local origin + for ( ;; ) + { + origin = titan.GetAttachmentOrigin( hijack ) + DebugDrawLine( player.GetOrigin(), origin, 255, 0, 0, true, 0.15 ) + + origin = player.GetAttachmentOrigin( ref ) + DebugDrawLine( player.GetOrigin(), origin, 0, 255, 0, true, 0.15 ) + WaitFrame() + } +} + + +#if SERVER +function PlayerEmbarksTitan( entity player, entity titan, table embark ) +{ + //player.SetOrigin( Vector(314.971405, -1826.728638, 116.031250)) + //player.SetAngles( Vector(0.000000, 133.945892, 0.000000)) + //titan.SetOrigin( Vector(284.887970, -1622.180542, 112.093750)) + //titan.SetAngles(Vector(0.000000, 112.264252, 0)) + + entity soul = titan.GetTitanSoul() + string settings = GetSoulTitanSubClass( soul ) + printt( "TitanEmbarkDebug: Player ", player.GetOrigin(), player.GetAngles(), " Titan ", titan.GetOrigin(), titan.GetAngles(), settings, GetMapName() ) + + + Assert( IsAlive( titan ) ) + Assert( IsAlive( player ) ) + + player.SetInvulnerable() + + player.EndSignal( "OnDeath" ) + player.EndSignal( "TitanEjectionStarted" ) + titan.EndSignal( "OnDeath" ) + + titan.Signal( "player_embarks_titan" ) + player.Signal( "player_embarks_titan" ) + +// Assert( !InSolid( titan ), titan + " is in solid" ) + + DisableCloak( player ) + + entity groundEntity = titan.GetGroundEntity() + vector startOrigin = titan.GetGroundRelativePos() + vector startAngles = titan.GetAngles() + + OnThreadEnd( + function() : ( player, groundEntity, startOrigin, startAngles ) + { + if ( IsValid( player ) ) + { + player.SetCinematicEventFlags( player.GetCinematicEventFlags() & (~CE_FLAG_EMBARK) ) + TitanEmbark_PlayerCleanup( player ) + player.p.isEmbarking = false + + if ( IsAlive( player ) ) + { + if ( player.IsTitan() ) //Defensive fix, sometimes in SP (when game is shutting down etc ) this can run without the player being alive + { + TitanEmbarkFailsafe( player, null, groundEntity, startOrigin ) + Remote_CallFunction_Replay( player, "ServerCallback_TitanEmbark" ) + LetTitanPlayerShootThroughBubbleShield( player ) + } + else + { + player.Die() //Defensive fix, sometimes in SP (when game is shutting down etc ) this can run without the player being alive + } + + } + } + } + ) + + // track the embarking player so we can kill him if the titan dies + titan.s.embarkingPlayer <- player + player.p.isEmbarking = true + + #if HAS_STATS + UpdatePlayerStat( player, "misc_stats", "titanEmbarks", 1 ) + #endif + #if SERVER && MP + PIN_AddToPlayerCountStat( player, "embarks" ) + PIN_PlayerAbility( player, "", "embark", {}, 0 ) + #endif + + player.SetCinematicEventFlags( player.GetCinematicEventFlags() | CE_FLAG_EMBARK ) + + waitthread PlayerEmbarksTitan_PlayerBecomesTitan( player, titan, embark ) +} + +function PlayerEmbarksTitan_PlayerBecomesTitan( entity player, entity titan, table embark ) +{ + // a place to store the player finish time + table e + + e.threads <- 0 + e.embarkAction <- embark.action + e.animSet <- embark.animSet + e.audioSet <- embark.audioSet + + e.canStand <- TitanCanStand( titan ) + + e.shouldDoRegularEmbark <- ShouldDoRegularEmbark( titan ) + + // player and titan do their anims and wait for each other to finish + thread TitanEmbark_TitanEmbarks( player, titan, e ) + waitthread TitanEmbark_PlayerEmbarks( player, titan, e ) +} + +bool function ShouldDoRegularEmbark( entity titan ) +{ + entity soul = titan.GetTitanSoul() + + if ( IsSingleplayer() && GetSoulPlayerSettings( soul ) == "titan_buddy" ) + { + return titan.GetNPCState() == "combat" || titan.GetNPCState() == "alert" + } + + return true +} + +bool function TitanHasLeftAndRightEmbarkAnims( entity titan ) +{ + entity soul = titan.GetTitanSoul() + string settings = GetSoulPlayerSettings( soul ) + var hasAnims = Dev_GetPlayerSettingByKeyField_Global( settings, "hasLeftRightEmbarks" ) + if ( hasAnims != null && hasAnims == 1 ) + { + return true + } + + return false +} + +function TitanEmbark_PlayerCleanup( player ) +{ + expect entity( player ) + + player.SetSyncedEntity( null ) + DeployViewModelAndEnableWeapons( player ) + player.UnforceStand() + player.UnforceCrouch() + //printt("Clearing invulnerable") + player.ClearInvulnerable() + player.ClearParent() + //Let player jump in air after getting out if he wants to + player.TouchGround() + player.Anim_Stop() +} + + +void function ForceScriptedEmbark( entity player, entity titan ) +{ + HolsterViewModelAndDisableWeapons( player ) + ClearPlayerAnimViewEntity( player ) + player.ClearParent() + PilotBecomesTitan( player, titan ) + + thread PlayAnim( player, "cqb_idle_mp" ) + player.Anim_Stop() + + player.SetOrigin( titan.GetOrigin() ) + vector angles = titan.GetAngles() + angles.z = 0 + angles.x = 0 + player.SetAngles( angles ) + player.SnapEyeAngles( angles ) + + SetStanceStand( player.GetTitanSoul() ) + + TitanEmbark_PlayerCleanup( player ) + if ( player.ContextAction_IsBusy() ) + player.ContextAction_ClearBusy() +} + + +function TitanEmbarkFailsafe( entity player, entity titan, entity groundEnt, vector startOrigin ) +{ + if ( GetCurrentPlaylistVarInt( "player_embark_in_solid_checks", 0 ) != 1 ) + return + + #if DEV + if ( file.embarkDebugPrint ) + { + if ( IsValid( titan ) ) + printt( "TitanEmbarkFailsafe, before PutEntityInSafeSpot: player origin: " + player.GetOrigin() + ", titan origin: " + titan.GetOrigin() + " safeStartPoint: " + startOrigin ) + else + printt( "TitanEmbarkFailsafe, before PutEntityInSafeSpot: player origin: " + player.GetOrigin() + ", null titan, safeStartPoint: " + startOrigin ) + } + #endif + + if ( !PutEntityInSafeSpot( player, titan, groundEnt, startOrigin, player.GetOrigin() ) ) + player.SetOrigin( startOrigin ) + + #if DEV + if ( file.embarkDebugPrint ) + { + if ( IsValid( titan ) ) + printt( "TitanEmbarkFailsafe, after PutEntityInSafeSpot: player origin: " + player.GetOrigin() + ", titan origin: " + titan.GetOrigin() + " safeStartPoint: " + startOrigin ) + else + printt( "TitanEmbarkFailsafe, after PutEntityInSafeSpot: player origin: " + player.GetOrigin() + ", null titan, safeStartPoint: " + startOrigin ) + } + #endif +} + +function TitanEmbark_PlayerEmbarks( entity player, entity titan, table e ) +{ + player.EndSignal( "OnDeath" ) + player.EndSignal( "TitanEjectionStarted" ) + titan.EndSignal( "OnDeath" ) + + e.threads++ + OnThreadEnd( + function() : ( player, e ) + { + if ( IsValid( player ) ) + { + // ensure these are cleared regardless + ClearPlayerAnimViewEntity( player ) + + if ( player.ContextAction_IsBusy() ) + player.ContextAction_ClearBusy() + } + + e.threads-- + if ( !e.threads ) + { + Signal( e, "OnComplete" ) + } + } + ) + + player.ContextAction_SetBusy() + + bool standing = false + + if ( e.canStand ) + { + if ( e.shouldDoRegularEmbark ) + { + player.ForceStand() + switch ( titan.GetTitanSoul().GetStance() ) + { + case STANCE_KNEELING: + case STANCE_KNEEL: + standing = false + break + + default: + standing = true + break + } + } + else + { + standing = false + } + } + else + { + player.ForceCrouch() + } + + HolsterViewModelAndDisableWeapons( player ) + + FirstPersonSequenceStruct sequence + sequence.attachment = "hijack" + sequence.useAnimatedRefAttachment = expect bool ( e.embarkAction.useAnimatedRefAttachment ) + sequence.blendTime = 0.5 + + entity soul = titan.GetTitanSoul() + string settings = GetSoulTitanSubClass( soul ) + + local hasViewCone + + // string thirdPersonAudio + // string firstPersonAudio + + if ( standing ) + { + sequence.firstPersonAnim = GetAnimFromAlias( settings, e.animSet.firstPersonStandingAlias ) + sequence.thirdPersonAnim = GetAnimFromAlias( settings, e.animSet.thirdPersonStandingAlias ) + hasViewCone = false + // thirdPersonAudio = GetAudioFromAlias( settings, e.audioSet.thirdPersonStandingAudioAlias ) + // firstPersonAudio = GetAudioFromAlias( settings, e.audioSet.firstPersonStandingAudioAlias ) + + } + else + { + sequence.firstPersonAnim = GetAnimFromAlias( settings, e.animSet.firstPersonKneelingAlias ) + sequence.thirdPersonAnim = GetAnimFromAlias( settings, e.animSet.thirdPersonKneelingAlias ) + hasViewCone = true + // thirdPersonAudio = GetAudioFromAlias( settings, e.audioSet.thirdPersonKneelingAudioAlias ) + // firstPersonAudio = GetAudioFromAlias( settings, e.audioSet.firstPersonKneelingAudioAlias ) + } + + sequence.thirdPersonAnimIdle = "pt_mount_idle" + + bool doFirstPersonAnim = true + + if ( sequence.firstPersonAnim == "" ) + {// if there is no first person anim, then there must be a third person camera + sequence.thirdPersonCameraAttachments.append( "VDU" ) + sequence.thirdPersonCameraVisibilityChecks = true + doFirstPersonAnim = false + } + + if ( hasViewCone ) + { + sequence.viewConeFunction = EmbarkViewCone + thread DelayedClearViewCone( player ) + } + + //thread DelayedDisableEmbarkPlayerHud( player, sequence ) + + AddAnimEvent( player, "phase_shift_start", PhaseEmbarkPhaseStart ) + AddAnimEvent( player, "phase_shift_stop", PhaseEmbarkPhaseStop ) + AddAnimEvent( titan, "cockpit_light_start", CockpitLightStart ) + AddAnimEvent( titan, "cockpit_light_stop", CockpitLightStop ) + + OnThreadEnd( + function() : ( player, titan ) + { + if ( IsValid( player ) ) + { + Signal( player, "PhaseEmbarkPhaseStop" ) + DeleteAnimEvent( player, "phase_shift_start" ) + DeleteAnimEvent( player, "phase_shift_stop" ) + } + + if ( IsAlive( titan ) ) //Consider clearing titan.s.embarkingPlayer here? + titan.Die() + } + ) + + if ( GetCurrentPlaylistVarInt( "fp_embark_enabled", 0 ) == 1 ) + { + Remote_CallFunction_NonReplay( player, "ServerCallback_HideHudForFPEmbark" ) + + // fp embark hacks + entity viewControl = CreateEntity( "point_viewcontrol" ) + viewControl.kv.spawnflags = 56 + DispatchSpawn( viewControl ) + + viewControl.SetParent( player, "headshot" ) + viewControl.SetOrigin( < 4, 0, 0 > ) + viewControl.SetAngles( < 0, 0, 0 > ) + player.SetViewEntity( viewControl, false ) + } + + thread FirstPersonSequence( sequence, player, titan ) + // EmitDifferentSoundsOnEntityForPlayerAndWorld( firstPersonAudio, thirdPersonAudio, titan, player ) + + float animDuration = player.GetSequenceDuration( sequence.thirdPersonAnim ) + + if ( ShouldSkipAheadIntoEmbark( standing, player, titan, e ) ) + { + local duration = player.GetSequenceDuration( sequence.thirdPersonAnim ) + if ( duration >= SKIP_AHEAD_TIME ) + { + player.Anim_SetInitialTime( duration - SKIP_AHEAD_TIME ) + entity viewModel = player.GetFirstPersonProxy() + + if ( IsValid( viewModel ) && EntHasModelSet( viewModel ) && doFirstPersonAnim ) //JFS: Defensive fix for player not having view models sometimes + viewModel.Anim_SetInitialTime( duration - SKIP_AHEAD_TIME ) + + animDuration = SKIP_AHEAD_TIME + } + } + + thread Embark_DelayedFadeOut( player, titan, animDuration ) + + WaittillAnimDone( player ) + + Signal( player, "PhaseEmbarkPhaseStop" ) + + ClearPlayerAnimViewEntity( player ) + PilotBecomesTitan( player, titan ) + + thread PlayAnim( player, "cqb_idle_mp" ) + player.Anim_Stop() + player.SetVelocity( <0,0,0> ) + + player.SetOrigin( titan.GetOrigin() ) + local angles = titan.GetAngles() + angles.z = 0 + angles.x = 0 + player.SetAngles( angles ) + player.SnapEyeAngles( angles ) + + // soul stuff should be from anim event + Assert( IsServer() ) + SetStanceStand( player.GetTitanSoul() ) + titan.Destroy() +} + +void function Embark_DelayedFadeOut( entity player, entity titan, float delay ) +{ + if ( !IsAlive( player ) ) + return + + if ( !IsValid( titan ) ) + return + + player.EndSignal( "OnDeath" ) + titan.EndSignal( "OnDestroy" ) + + wait delay - EMBARK_FADE_TIME + + if ( GetCurrentPlaylistVarInt( "fp_embark_enabled", 0 ) == 0 ) + { + ScreenFadeToBlack( player, EMBARK_FADE_TIME, EMBARK_FADE_TIME + 0.2 ) // a little extra so we stay black + wait EMBARK_FADE_TIME + } + else + { + OnThreadEnd( function() : ( player ) + { + player.ClearViewEntity() + }) + + wait EMBARK_FADE_TIME - 0.2 + ScreenFadeToBlack( player, 0.2, 0.4 ) + wait 0.2 + player.ClearViewEntity() // make sure player is in normal first person again + } + + ScreenFadeFromBlack( player, EMBARK_FADE_TIME, EMBARK_FADE_TIME ) +} + +void function PlayStartupSounds( entity titan ) +{ + entity soul = titan.GetTitanSoul() + if ( !IsValid( soul ) ) + return + entity player = soul.GetBossPlayer() + if ( !IsValid( player ) ) + return + + player.EndSignal( "OnDeath" ) + + string titanSettings = GetSoulPlayerSettings( soul ) + var startupSound = Dev_GetPlayerSettingByKeyField_Global( titanSettings, "startup_sound" ) + + if ( startupSound != null ) + EmitSoundOnEntityOnlyToPlayer( player, player, expect string(startupSound) ) +} + + +void function CockpitLightStart( entity titan ) +{ + thread StartCockpitLightThink( titan, 5.0 ) +} + +void function StartCockpitLightThink( entity titan, float timeout ) +{ + titan.EndSignal( "OnDestroy" ) + titan.EndSignal( "CockpitLightStop" ) + + int attachID = titan.LookupAttachment( "HIJACK" ) + int fxID = GetParticleSystemIndex( file.cockpitLightFX ) + entity fx = StartParticleEffectOnEntity_ReturnEntity( titan, fxID, FX_PATTACH_POINT_FOLLOW, attachID ) + + OnThreadEnd( + function() : ( fx ) + { + if ( IsValid( fx ) ) + EffectStop( fx ) + } + ) + + if ( timeout < 0 ) + WaitForever() + else + wait timeout +} + +void function CockpitLightStop( entity titan ) +{ + titan.Signal( "CockpitLightStop" ) +} + +void function PhaseEmbarkPhaseStart( entity player ) +{ + player.MakeInvisible() + PlayPhaseShiftDisappearFX( player ) + EmitSoundOnEntity( player, "pilot_phaseembark_activate_3p" ) + + if ( GetCurrentPlaylistVarInt( "fp_embark_enabled", 0 ) == 1 ) + { + player.PhaseShiftBegin( 0.0, 0.2 ) + player.GetPetTitan().SetForceVisibleInPhaseShift( true ) // doesn't work for some reason + } + + thread PhaseEmbarkPhaseCleanup( player ) +} + +void function PhaseEmbarkPhaseCleanup( player ) +{ + EndSignal( player, "OnDeath" ) + + OnThreadEnd( + function() : ( player ) + { + if ( IsValid( player ) ) + { + player.MakeVisible() + } + } + ) + + WaitSignal( player, "PhaseEmbarkPhaseStop" ) +} + +void function PhaseEmbarkPhaseStop( entity player ) +{ + Signal( player, "PhaseEmbarkPhaseStop" ) + PlayPhaseShiftDisappearFX( player ) + EmitSoundOnEntity( player, "pilot_phaseembark_end_3p" ) + + if ( GetCurrentPlaylistVarInt( "fp_embark_enabled", 0 ) == 1 ) + player.PhaseShiftCancel() +} + +function ShouldSkipAheadIntoEmbark( standing, player, titan, e ) +{ + if ( !standing ) + return false + + if ( !e.embarkAction.canSkipAhead ) + return false + + local playerEye = player.EyePosition() + local titanOrg = titan.GetOrigin() + local vec = playerEye - titanOrg + vec.Norm() + vec.z = 0 + local start = playerEye + local end = playerEye + vec * 24 + + if ( Distance( player.GetOrigin(), titan.GetOrigin() ) >= 145 ) + return false + + local mask = TRACE_MASK_PLAYERSOLID + TraceResults result = TraceLine( start, end, [ titan, player ], mask, TRACE_COLLISION_GROUP_NONE ) + //DebugDrawLine( start, result.endPos, 0, 255, 0, true, 10.0 ) + //DebugDrawLine( result.endPos, end, 255, 0, 0, true, 10.0 ) + return result.fraction < 1.0 +} +#endif // SERVER + +function DelayedDisableEmbarkPlayerHud( player, sequence ) +{ + player.EndSignal( "OnDeath" ) + + local duration = player.GetSequenceDuration( sequence.thirdPersonAnim ) + wait duration - 1.0 + player.SetCinematicEventFlags( player.GetCinematicEventFlags() | CE_FLAG_EMBARK ) +} + +#if SERVER +function DelayedClearViewCone( player ) +{ + player.EndSignal( "OnDeath" ) + wait 1.0 + player.PlayerCone_SetLerpTime( 0.5 ) + player.PlayerCone_FromAnim() + player.PlayerCone_SetMinYaw( 0 ) + player.PlayerCone_SetMaxYaw( 0 ) + player.PlayerCone_SetMinPitch( 0 ) + player.PlayerCone_SetMaxPitch( 0 ) +} + +void function EmbarkViewCone( entity player ) +{ + player.PlayerCone_FromAnim() + player.PlayerCone_SetMinYaw( -70 ) + player.PlayerCone_SetMaxYaw( 60 ) + player.PlayerCone_SetMinPitch( -80 ) + player.PlayerCone_SetMaxPitch( 30 ) +} + +function TitanEmbark_TitanEmbarks( player, titan, e ) +{ + expect entity( player ) + expect entity( titan ) + + player.EndSignal( "OnDeath" ) + player.EndSignal( "TitanEjectionStarted" ) + titan.EndSignal( "OnDeath" ) + + titan.ContextAction_SetBusy() + + AddAnimEvent( titan, "play_startup_sound", PlayStartupSounds ) + + e.threads++ + OnThreadEnd( + function() : ( e, player, titan ) + { + e.threads-- + if ( !e.threads ) + { + Signal( e, "OnComplete" ) + } + + if ( !IsAlive( player ) && IsAlive( titan ) ) + { + titan.Anim_Stop() + titan.ContextAction_ClearBusy() + DeleteAnimEvent( titan, "play_startup_sound" ) + } + } + ) + + local soul = titan.GetTitanSoul() + + // dont let other players get in + + local animation +// local waittillAnimDone + local alignFront + bool standing = false + + if ( e.canStand ) + { + if ( e.shouldDoRegularEmbark ) + { + // default + switch ( titan.GetTitanSoul().GetStance() ) + { + case STANCE_KNEELING: + case STANCE_KNEEL: + animation = e.animSet.titanKneelingAnim + alignFront = false + break + + default: + animation = e.animSet.titanStandingAnim + if ( TitanHasLeftAndRightEmbarkAnims( titan ) ) + alignFront = false + else + alignFront = true + standing = true + break + } + } + else + { + // special for BT if he is in casual mode + animation = e.animSet.titanKneelingAnim + alignFront = false + } + } + else + { + animation = "at_mount_kneel_without_standing" + alignFront = false +// waittillAnimDone = false + } + + //sequence.blendTime = 0.5 + + printt("This is mount animation name: " + animation ) + if ( e.embarkAction.alignFrontEnabled && alignFront ) + { + local titanOrg = titan.GetOrigin() + local vec = player.GetOrigin() - titanOrg + local angles = VectorToAngles( vec ) + angles.x = 0 + angles.z = 0 + thread PlayAnimGravityClientSyncing( titan, animation, titanOrg, angles ) + } + else + { + thread PlayAnimGravityClientSyncing( titan, animation ) + } + + if ( ShouldSkipAheadIntoEmbark( standing, player, titan, e ) ) + { + local duration = titan.GetSequenceDuration( animation ) + + if ( duration >= SKIP_AHEAD_TIME ) // failsafe + titan.Anim_SetInitialTime( duration - SKIP_AHEAD_TIME ) + } + + // titan will become player now + WaitForever() +} + +bool function ClientCommand_TitanDisembark( entity player, array<string> args ) +{ + if ( !PlayerCanDisembarkTitan( player ) ) + return true + + ScreenFade( player, 0, 1, 0, 255, 0.2, 0.2, FFADE_IN | FFADE_PURGE ) + player.CockpitStartDisembark() + Remote_CallFunction_Replay( player, "ServerCallback_TitanDisembark" ) + + thread PlayerDisembarksTitan( player ) + + return true +} + +void function ForcedTitanDisembark( entity player ) +{ + Assert( PlayerCanDisembarkTitan( player ) ) + + player.CockpitStartDisembark() + Remote_CallFunction_Replay( player, "ServerCallback_TitanDisembark" ) + + waitthread PlayerDisembarksTitan( player ) +} + +function ForcedTitanDisembarkCustomAnims( entity player, FirstPersonSequenceStruct functionref( entity, entity ) playerSequenceFunc, FirstPersonSequenceStruct functionref( entity, entity ) titanSequenceFunc ) +{ + Assert( PlayerCanDisembarkTitan( player ) ) + + player.CockpitStartDisembark() + Remote_CallFunction_Replay( player, "ServerCallback_TitanDisembark" ) + + player.p.isCustomDisembark = true + waitthread PlayerDisembarksTitanWithSequenceFuncs( player, playerSequenceFunc, titanSequenceFunc ) + player.p.isCustomDisembark = false +} + +#endif // SERVER + +function PlayerCanDisembarkTitan( entity player ) +{ + if ( !player.IsTitan() ) + return false + + if ( !IsAlive( player ) ) + return false + + if ( IsValid( player.GetParent() ) ) + return false + + if ( Riff_TitanExitEnabled() == eTitanExitEnabled.Never ) + return false + + if ( !CanDisembark( player ) ) + return false + + #if SERVER + if ( player.IsNoclipping() ) + return false + // client doesn't know these things + if ( IsPlayerDisembarking( player ) ) + return false + if ( IsPlayerEmbarking( player ) ) + return false + #endif + + if ( !HasSoul( player ) ) + return false + + local soul = player.GetTitanSoul() + if ( soul.IsEjecting() ) + return false + + Assert( soul == player.GetTitanSoul() ) + + return true +} + +#if SERVER + +function PlayerDisembarksTitan( player ) +{ + expect entity( player ) + PlayerDisembarksTitanWithSequenceFuncs( player, GetDisembarkSequenceForPlayer, GetDisembarkSequenceForTitan ) +} + +void function PlayerDisembarksTitanWithSequenceFuncs( entity player, FirstPersonSequenceStruct functionref( entity, entity ) playerSequenceFunc, FirstPersonSequenceStruct functionref( entity, entity ) titanSequenceFunc ) +{ + //printt( "Player disembarking with origin " + player.GetOrigin() + " and yaw " + player.GetAngles().y ) + + //player.SetOrigin( Vector(420.847626, -5214.960938, 173.789520) ) + //player.SetAngles( Vector(0.000000, 179.572052, 0.000000 ) ) + + printt( "TitanDisembarkDebug: Player ", player.GetOrigin(), player.GetAngles(), GetMapName() ) + + player.EndSignal( "OnDeath" ) + player.Signal( "DisembarkingTitan" ) + + //Assert( !InSolid( player ), player + " is in solid" ) + + local e = {} + e.titan <- null + e.PilotCleanUpDone <- false + + e.startOrigin <- player.GetOrigin() + //e.startAngles <- player.GetAngles() + + player.p.isDisembarking = true + player.SetCinematicEventFlags( player.GetCinematicEventFlags() | CE_FLAG_DISEMBARK ) + + bool wasCustomDisembark = player.p.isCustomDisembark + + player.ContextAction_SetBusy() + + OnThreadEnd( + function() : ( player, e ) + { + if ( IsValid( player ) ) + { + PlayerEndsDisembark( player, e ) + + local titan = e.titan + if ( !IsValid( titan ) ) + titan = null + } + + if ( IsAlive( expect entity( e.titan ) ) ) + { + if ( IsAlive( player ) ) + { + thread PlayerOwnsTitanUntilSeparation( player, e.titan, 80 ) + } + + delete e.titan.s.disembarkingPlayer + ClearInvincible( expect entity( e.titan ) ) + + + //Make Titan get up immediately if he's kneeling and can stand + //If he can't get up, well, then since the titan doesn't move when crouched he's going to be stuck... + if ( !( "embarkingPlayer" in e.titan.s ) ) + { + thread TitanNPC_Think( expect entity( e.titan ) ) //titan.s.disableAutoTitanConversation is deleted inside here + } + } + } + ) + + bool standing = player.IsStanding() + + player.SetInvulnerable() + player.SnapFeetToEyes() + + entity titan = CreateAutoTitanForPlayer_ForTitanBecomesPilot( player ) + DispatchSpawn( titan ) + e.titan = titan + + if ( !PlayerIsFarOffTheGround( player, [ player,titan ] ) ) //PlayerIsFarOffTheGround() necessary now for R2 because we have Titans that can jump/geo where Titans can fall down from large heights. Without check, mid-air disembarking will cause player to be teleported to the ground + { + vector ornull clampedPos = NavMesh_ClampPointForAIWithExtents( titan.GetOrigin(), titan, file.smallDisembarkFailSafeTeleportVector ) + if ( clampedPos == null ) + clampedPos = NavMesh_ClampPointForAIWithExtents( titan.GetOrigin(), titan, file.largeDisembarkFailSafeTeleportVector ) + + if ( clampedPos != null ) + { + expect vector( clampedPos ) + vector titanOrigin = titan.GetOrigin() + + array<entity> ignoreEnts = [] + ignoreEnts.append( titan ) + + TraceResults result = TraceHull( titanOrigin, titanOrigin, titan.GetBoundingMins(), titan.GetBoundingMaxs(), ignoreEnts, TRACE_MASK_TITANSOLID, TRACE_COLLISION_GROUP_NONE ) + + // expensive checks to make sure titan doesn't teleport to navmesh on other side of wall usually in invalid places + if ( result.startSolid || + TraceLineSimple( titanOrigin + Vector( 0, 0, 128 ), clampedPos + Vector( 0, 0, 0 ), titan ) == 1.0 || + TraceLineSimple( titanOrigin + Vector( 0, 0, 200 ), clampedPos + Vector( 0, 0, 0 ), titan ) == 1.0 || + TraceLineSimple( titanOrigin + Vector( 0, 0, 200 ), clampedPos + Vector( 0, 0, 128 ), titan ) == 1.0 ) + { + #if DEV + if ( file.embarkDebugPrint ) + { + printt( "PlayerDisembarksTitanWithSequenceFuncs, player origin: " + player.GetOrigin()+ ", titan origin: " + titan.GetOrigin() + ", clampedPos: " + clampedPos ) + } + #endif + titan.SetOrigin( clampedPos ) + titan.ForceCheckGroundEntity() + } + } + } + else + { + #if DEV + if ( file.embarkDebugPrint ) + { + printt( "PlayerIsFarOffGround() returned true, skip doing NavMesh_ClampPointForAIWithExtents checks" ) + } + #endif + } + + titan.s.disembarkingPlayer <- player + titan.EndSignal( "OnDeath" ) + + player.SetSyncedEntity( titan ) + titan.s.disableAutoTitanConversation <- true + + Assert( titan.IsTitan() ) + Assert( IsAlive( player ) ) + Assert( player.IsTitan() ) + //Set player to be temporarily invulnerable. Will be removed at end of animation + //printt("Set player invulnerable") + HolsterViewModelAndDisableWeapons( player ) //Holstering weapon before becoming pilot so we don't play the holster animation as a pilot. Player as Titan won't play the holster animation either since it'll be interrupted by the disembark animation + + TitanBecomesPilot( player, titan ) + + string titanSubClass = GetSoulTitanSubClass( titan.GetTitanSoul() ) + switch ( titanSubClass ) + { + case "ogre": + ShowMainTitanWeapons( titan ) //JFS: Because we hide the Titan's weapons upon kneeling for ogre + break + } + + titan.s.disembarkTime <- Time() // disembark debounce + + // compound strings into these animations: + // pt_dismount_atlas_stand + // pt_dismount_ogre_stand + // pt_dismount_stryder_stand + // pt_dismount_atlas_crouch + // pt_dismount_ogre_crouch + // pt_dismount_stryder_crouch + // ptpov_dismount_atlas_stand + // ptpov_dismount_ogre_stand + // ptpov_dismount_stryder_stand + // ptpov_dismount_atlas_crouch + // ptpov_dismount_ogre_crouch + // ptpov_dismount_stryder_crouch + + FirstPersonSequenceStruct playerSequence = playerSequenceFunc( player, titan ) + FirstPersonSequenceStruct titanSequence = titanSequenceFunc( player, titan ) + + #if SERVER + StatusEffect_StopAll( player, eStatusEffect.lockon_detected_titan ) + #endif + + player.ForceStand() + + thread FirstPersonSequence( titanSequence, titan ) + thread FirstPersonSequence( playerSequence, player, titan ) + + if ( !wasCustomDisembark ) + thread ClearParentBeforeIntersect( player, titan, playerSequence.thirdPersonAnim, e ) + + if ( !standing ) + { + SetStanceKneel( titan.GetTitanSoul() ) + } + + //player.Anim_EnablePlanting() + + #if SERVER && MP + PIN_AddToPlayerCountStat( player, "disembarks" ) + PIN_PlayerAbility( player, "", "disembark", {}, 0 ) + #endif + + WaittillAnimDone( player ) +} + +bool function PlayerIsFarOffTheGround( entity player, array<entity> ignoreEnts ) +{ + vector boundingMaxs = player.GetBoundingMaxs() + float halfHeight = boundingMaxs.z / 2.0 + + vector startpos = player.GetOrigin() + vector endpos = startpos + endpos.z -= halfHeight + + TraceResults result = TraceHull( startpos, endpos, player.GetBoundingMins(), player.GetBoundingMaxs(), ignoreEnts, TRACE_MASK_PLAYERSOLID, TRACE_COLLISION_GROUP_PLAYER ) + //PrintTraceResults( result ) + + if ( result.startSolid ) + return false + + if ( result.allSolid ) + return false + + return ( result.fraction >= 1.0 ) +} + +FirstPersonSequenceStruct function GetDisembarkSequenceForPlayer( entity player, entity titan ) +{ + string titanSubClass = GetSoulTitanSubClass( titan.GetTitanSoul() ) + + string player3pAnim, player1pAnim + if ( player.IsStanding() ) + { + player3pAnim = "pt_dismount_" + titanSubClass + "_stand" + player1pAnim = "ptpov_dismount_" + titanSubClass + "_stand" + } + else + { + player3pAnim = "pt_dismount_" + titanSubClass + "_crouch" + player1pAnim = "ptpov_dismount_" + titanSubClass + "_crouch" + } + + if ( player.HasPassive( ePassives.PAS_FAST_EMBARK ) ) + { + player1pAnim += "_fast" + player3pAnim += "_fast" + } + + FirstPersonSequenceStruct playerSequence + playerSequence.blendTime = 0 + playerSequence.teleport = true + playerSequence.attachment = "hijack" + playerSequence.thirdPersonAnim = player3pAnim + playerSequence.firstPersonAnim = player1pAnim + playerSequence.useAnimatedRefAttachment = true + + return playerSequence +} + +FirstPersonSequenceStruct function GetDisembarkSequenceForTitan( entity player, entity titan ) +{ + bool standing = player.IsStanding() + + string titanDisembarkAnim + if ( standing ) + titanDisembarkAnim = "at_dismount_stand" + else + titanDisembarkAnim = "at_dismount_crouch" + + if ( player.HasPassive( ePassives.PAS_FAST_EMBARK ) ) + titanDisembarkAnim += "_fast" + + vector origin = titan.GetOrigin() + vector angles = titan.EyeAngles() + angles.z = 0 + angles.x = 0 + + FirstPersonSequenceStruct titanSequence + titanSequence.blendTime = 0.3 + titanSequence.thirdPersonAnim = titanDisembarkAnim + if ( !standing ) + titanSequence.thirdPersonAnimIdle = "at_MP_embark_idle_blended" + titanSequence.gravity = true + titanSequence.origin = origin + titanSequence.angles = angles + + return titanSequence +} + +function DelayedSafePlayerLocationForDisembark( entity player, entity titan ) +{ + float currentTime = Time() + float allowedTime = player.p.isCustomDisembark ? 10.0 : 2.0 + + player.EndSignal( "OnDestroy" ) + + while( IsPlayerDisembarking( player ) ) + { + Assert( Time() - currentTime < allowedTime ) // Failsafe of waiting 2 seconds in case SOMETHING REALLY GOES WRONG. + if ( !IsAlive( player ) ) + return + + WaitFrame() + } + + if ( player.ContextAction_IsActive() ) //Immediately after disembarking player might have gotten pulled into another context action e.g. embarking into evac dropship + return + + player.ClearParent() + player.PlayerCone_Disable() + player.ViewOffsetEntity_Clear() + player.GetFirstPersonProxy().HideFirstPersonProxy() + + vector safeStartPoint + + if ( IsValid( titan ) ) + { + vector titanBoundingMaxs = titan.GetBoundingMaxs() + float halfTitanHeight = titanBoundingMaxs.z * 0.5 + safeStartPoint = titan.GetOrigin() + < 0, 0, halfTitanHeight > //Let the start point of PutEntityInSafeSpot be closer to where the player is when disebmarking instead of the ground origin + } + else + { + titan = null + safeStartPoint = player.GetOrigin() + } + + #if DEV + if ( file.embarkDebugPrint ) + printt( "DelayedSafePlayerLocationForDisembark, before PutEntityInSafeSpot: player origin: " + player.GetOrigin() + ", titan origin: " + titan.GetOrigin() + " safeStartPoint: " + safeStartPoint ) + #endif + + + + if ( !PutEntityInSafeSpot( player, titan, null, safeStartPoint, player.GetOrigin() ) ) + player.SetOrigin( safeStartPoint ) + + #if DEV + if ( file.embarkDebugPrint ) + printt( "DelayedSafePlayerLocationForDisembark, after PutEntityInSafeSpot: player origin: " + player.GetOrigin() + ", titan origin: " + titan.GetOrigin() + " safeStartPoint: " + safeStartPoint ) + #endif + +} + +function ClearParentBeforeIntersect( entity player, entity titan, anim, e ) +{ + player.EndSignal( "OnDeath" ) + player.EndSignal( "OnAnimationDone" ) + player.EndSignal( "OnAnimationInterrupted" ) + //local mins = player.GetBoundingMins() + //local maxs = player.GetBoundingMaxs() + + OnThreadEnd( + function() : ( player, e ) + { + if ( IsValid( player ) ) + thread DelayedSafePlayerLocationForDisembark( player, expect entity( e.titan ) ) + } + ) + + wait 0.25 + + vector lastOrigin = player.GetOrigin() + for ( ;; ) + { + if ( EntityInSolid( player, titan, 24 ) ) + break + + lastOrigin = player.GetOrigin() + WaitFrame() + } + + player.SetOrigin( lastOrigin ) +} + +function LockedViewCone( human ) +{ + human.PlayerCone_FromAnim() + human.PlayerCone_SetMinYaw( 0 ) + human.PlayerCone_SetMaxYaw( 0 ) + human.PlayerCone_SetMinPitch( 0 ) + human.PlayerCone_SetMaxPitch( 0 ) +} + + +function PlayerOwnsTitanUntilSeparation( player, titan, dist ) +{ + titan.SetOwner( player ) + + player.EndSignal( "OnDeath" ) + titan.EndSignal( "OnDeath" ) + + OnThreadEnd( + function () : ( player, titan ) + { + if ( !IsValid( titan ) ) + return + + titan.SetOwner( null ) + } + ) + + // wait until player moves away + local distSqr = dist * dist + for ( ;; ) + { + if ( DistanceSqr( titan.GetOrigin(), player.GetOrigin() ) > distSqr ) + break + + wait 0.5 + } +} + +function PlayerEndsDisembark( player, e ) +{ + thread PlayerEndsDisembarkThread( player, e ) +} + +function PlayerEndsDisembarkThread( player, e ) +{ + expect entity( player ) + player.EndSignal( "OnDestroy" ) + + if ( e.PilotCleanUpDone ) + return + + e.PilotCleanUpDone = true + bool wasCustomDisembark = player.p.isCustomDisembark + + wait 0.1 + + ClearPlayerAnimViewEntity( player ) + if ( player.ContextAction_IsBusy() ) + player.ContextAction_ClearBusy() + + player.SetCinematicEventFlags( player.GetCinematicEventFlags() & (~CE_FLAG_DISEMBARK) ) + + player.Show() + TitanEmbark_PlayerCleanup( player ) + player.p.isDisembarking = false + + //// give a player a boost out the door + // + // + if ( IsAlive( player ) && !wasCustomDisembark ) + { + local angles = player.EyeAngles() + if ( IsValid( e.titan ) ) + angles = e.titan.GetAngles() + + angles.x = 0 + angles.z = 0 + local forward = AnglesToForward( angles ) + local up = AnglesToUp( angles ) + local vel = forward * 250 + up * 200 + player.SetVelocity( vel ) + //DebugDrawLine( player.GetOrigin(), player.GetOrigin() + forward * 500, 255, 0, 0, true, 5.0 ) + } +} + +function IsPlayerEmbarking( player ) +{ + expect entity ( player ) + return player.p.isEmbarking +} +#endif // SERVER + +function IsPlayerDisembarking( player ) +{ + expect entity ( player ) + return player.p.isDisembarking +} + +function PlayerCanEmbarkIntoTitan( entity player, entity titan ) //TODO: Collapse PlayerCanEmbarkTitan(), PlayerCanEmbarkIntoTitan(), PlayerCanImmediatelyEmbarkTitan() and TitanIsCurrentlyEmbarkableForPlayer() into one function +{ + if ( player.IsNoclipping() ) + return false + + if ( !TitanIsCurrentlyEmbarkableForPlayer( player, titan ) ) + return false + + return FindBestEmbark( player, titan ) != null +} + +bool function TitanIsCurrentlyEmbarkableForPlayer( entity player, entity titan ) //TODO: Collapse PlayerCanEmbarkTitan(), PlayerCanEmbarkIntoTitan(), PlayerCanImmediatelyEmbarkTitan() and TitanIsCurrentlyEmbarkableForPlayer() into one function +{ + if ( !CanEmbark( player ) ) + return false + + if ( player.Anim_IsActive() ) + return false + + if ( !player.IsHuman() ) + return false + + if ( player.ContextAction_IsActive() ) + return false + + if ( !titan.IsEntAlive() ) + return false + + if ( titan.ContextAction_IsActive() ) + return false + + if ( !titan.IsInterruptable() ) + return false + + if ( IsValid( titan.GetParent() ) ) + return false + + if ( !HasSoul( titan ) ) + return false + + local soul = titan.GetTitanSoul() + + if ( GetDoomedState( titan ) && !PROTO_AlternateDoomedState() ) + return false + + if ( soul.IsEjecting() ) + return false + + #if SERVER + // client doesn't know these things + if ( IsPlayerEmbarking( player ) ) + return false + + if ( IsPlayerDisembarking( player ) ) + return false + #endif + + if ( "disembarkTime" in titan.s ) + { + if ( Time() - titan.s.disembarkTime < 1.65 ) + return false + } + + return true +} + +function FindEmbarkActionForCriteria( criteria ) +{ + local embarkAction + foreach ( option in level.titanEmbarkActions ) + { + bool failed = false + foreach ( key, value in criteria ) + { + if ( value != option[key] ) + { + failed = true + break + } + } + + if ( !failed ) + { + embarkAction = option + break + } + } + + return embarkAction + +} + +function GetRandomEmbarkAction() +{ + return level.titanEmbarkActions[ RandomInt( level.titanEmbarkActions.len() ) ] +} + +function FindBestEmbark( entity player, entity titan, bool doDistCheck = true ) +{ +// if ( IsServer() ) +// printt( "finding best embark for " + player + " to " + titan ) + vector playerPos = player.GetOrigin() + vector titanPos = titan.GetOrigin() + + vector relTitanToPlayerDir = CalculateTitanToPlayerDir( titan, player ) + + local bestAction = null + float bestDot = -2 + float dist = 0 + + if ( doDistCheck ) + { + dist = Distance( playerPos, titanPos ) + if ( dist > level.titanEmbarkFarthestDistance ) + return null + } + //if ( IsServer() ) + // printt( "dist: " + dist ) + + for ( int i = 0; i < 3; i++ ) + { + bestAction = GetBestEmbarkAction( i, player, titan, dist, relTitanToPlayerDir ) + if ( bestAction != null ) + break + } + + if ( bestAction == null ) + return null + + return GenerateEmbarkActionTable( player, titan, bestAction, relTitanToPlayerDir ) +} + +vector function CalculateTitanToPlayerDir( entity titan, entity player ) +{ + vector playerPos = player.GetOrigin() + vector titanPos = titan.GetOrigin() + + vector absTitanToPlayerDir + if ( playerPos == titanPos ) + { + absTitanToPlayerDir = Vector( 1, 0, 0 ) + } + else + { + vector angles = player.EyeAngles() + vector forward = AnglesToForward( angles ) + + absTitanToPlayerDir = ( playerPos - titanPos ) + + + absTitanToPlayerDir.Norm() + +// not needed cause we can't get in without a legal use +// // is the target in my fov? +// if ( forward.Dot( absTitanToPlayerDir * -1 ) < 0.77 ) +// return null + } + + vector titanAngles = titan.GetAngles() + titanAngles.x = 0 + if ( titan.GetTitanSoul().GetStance() >= STANCE_STANDING ) + titanAngles = AnglesCompose( titanAngles, Vector( 0, -30, 0 ) ) + + vector relTitanToPlayerDir = CalcRelativeVector( titanAngles, absTitanToPlayerDir ) + return relTitanToPlayerDir +} + +function GenerateEmbarkActionTable( entity player, entity titan, bestAction, var relTitanToPlayerDir = null ) +{ + bool useFastAnims = player.IsPlayer() && player.HasPassive( ePassives.PAS_FAST_EMBARK ) + + if ( relTitanToPlayerDir == null ) + { + relTitanToPlayerDir = CalculateTitanToPlayerDir( titan, player ) + expect vector( relTitanToPlayerDir ) + } + else + { + expect vector( relTitanToPlayerDir ) + } + + local Table = {} + Table.action <- bestAction + + if ( "animSet" in bestAction ) + { + Table.animSet <- bestAction.animSet + Table.audioSet <- bestAction.audioSet + } + else + { + local bestAnimSet + local bestAudioSet + local bestDot = -2 + Assert( "animSets" in bestAction, "Table has no animSet and no animSets!" ) + foreach ( animSet in bestAction.animSets ) + { + local dot = relTitanToPlayerDir.Dot( animSet.direction ) + + if ( dot > bestDot ) + { + bestAnimSet = animSet + bestAudioSet = animSet.audioSet + bestDot = dot + } + } + + Table.animSet <- bestAnimSet + Table.audioSet <- bestAudioSet + } + + if ( useFastAnims ) + { + Table.animSet = clone Table.animSet + + foreach ( string idx, item in Table.animSet ) + { + if ( IsString( item ) ) + Table.animSet[ idx ] = item + "_fast" + } + } + + return Table +} + +function GetBestEmbarkAction( int priority, entity player, entity titan, float dist, vector relTitanToPlayerDir ) +{ + local bestAction = null + local bestDot = -2 + + foreach ( action in level.titanEmbarkActions ) + { + if ( action.priority != priority ) + continue + + if ( dist > action.distance ) + { + //if ( IsServer() ) + //printt( "Failed: Action " + action.embark + " had dist " + action.distance + " vs actual dist " + dist ) + continue + } + + if ( action.lungeCheck ) + { + if ( player.IsNPC() ) + continue + + if ( player.Lunge_IsActive() != action.lungeCheck ) + continue + } + + local dot = relTitanToPlayerDir.Dot( action.direction ) + + if ( dot < action.minDot ) + { + //if ( IsServer() ) + //printt( "Failed: Action " + action.embark + " had dot " + dot ) + continue + } + + if ( expect bool( action.titanCanStandRequired ) && !TitanCanStand( titan ) ) + { + //if ( IsServer() ) + //printt( "Failed: Action " + action.embark + " cant stand" ) + continue + } + + if ( dot > bestDot ) + { + //if ( IsServer() ) + //printt( "Action " + action.embark + " had dot " + dot ) + bestAction = action + bestDot = dot + } + } + + return bestAction +} + +function FindBestEmbarkForNpcAnim( entity npc, entity titan ) +{ + bool doDistCheck = false + return FindBestEmbark( npc, titan, doDistCheck ) +} + + + + +bool function TitanCanStand( entity titan ) +{ + #if SERVER + vector maxs = titan.GetBoundingMaxs() + vector mins = titan.GetBoundingMins() + + vector start = titan.GetOrigin() + vector end = titan.GetOrigin() + entity soul = titan.GetTitanSoul() + entity ignoreEnt = null + + if ( IsValid( soul.soul.bubbleShield ) ) + ignoreEnt = soul.soul.bubbleShield + int mask = titan.GetPhysicsSolidMask() + //printt( "mask has " + MaskTester( mask ) ) + TraceResults result = TraceHull( start, end, mins, maxs, ignoreEnt, mask, TRACE_COLLISION_GROUP_NONE ) + //printt( "start " + start + " end " + end ) + + //DebugDrawLine( start, result.endPos, 0, 255, 0, true, 5.0 ) + //DebugDrawLine( result.endPos, end, 255, 0, 0, true, 5.0 ) + + bool canStand = result.fraction >= 1.0 + titan.SetCanStand( canStand ) + return canStand + #else + return titan.GetCanStand() != 0 + #endif +} + +bool function PlayerCanEmbarkTitan( entity player, entity titan ) //TODO: Collapse PlayerCanEmbarkTitan(), PlayerCanEmbarkIntoTitan(), PlayerCanImmediatelyEmbarkTitan() and TitanIsCurrentlyEmbarkableForPlayer() into one function +{ + PerfStart( PerfIndexClient.PlayerCanEmbarkTitan1 ) + if ( !TitanIsCurrentlyEmbarkableForPlayer( player, titan ) ) + { + PerfEnd( PerfIndexClient.PlayerCanEmbarkTitan1 ) + return false + } + PerfEnd( PerfIndexClient.PlayerCanEmbarkTitan1 ) + + PerfStart( PerfIndexClient.FindBestEmbark ) + bool res = FindBestEmbark( player, titan ) != null + PerfEnd( PerfIndexClient.FindBestEmbark ) + + return res +} + +bool function PlayerCanImmediatelyEmbarkTitan( entity player, entity titan ) //TODO: Collapse PlayerCanEmbarkTitan(), PlayerCanEmbarkIntoTitan(), PlayerCanImmediatelyEmbarkTitan() and TitanIsCurrentlyEmbarkableForPlayer() into one function +{ + if ( "embarkingPlayer" in titan.s ) + return false + + if ( player.IsNoclipping() ) + return false + + if ( !IsAlive( player ) || !IsAlive( titan ) ) + return false + + return FindBestEmbark( player, titan ) != null +} + +#if SERVER +function PlayerLungesToEmbark( entity player, entity ent ) +{ + Assert( TitanIsCurrentlyEmbarkableForPlayer( player, ent ) ) + + if ( PlayerCanImmediatelyEmbarkTitan( player, ent ) ) + { + table embarkDirection = expect table( FindBestEmbark( player, ent ) ) + thread PlayerEmbarksTitan( player, ent, embarkDirection ) + return + } + + if ( player.IsNoclipping() ) + return + + // already lunging + if ( player.Lunge_IsActive() ) + return + + if ( ShouldStopLunging( player, ent ) ) + return + + player.Lunge_SetTargetEntity( ent, false ) + player.Lunge_SetSmoothTime( 3.0 ) +} + +void function TitanBecomesPilot_UpdateRodeoRiderHud( entity playerTitan, entity npc_titan ) +{ + entity rodeoPilot = GetRodeoPilot( npc_titan ) + if ( !IsValid( rodeoPilot ) ) + return + + Remote_CallFunction_Replay( rodeoPilot, "ServerCallback_UpdateRodeoRiderHud" ) +} + +void function PilotBecomesTitan_UpdateRodeoRiderHud( entity playerTitan, entity npc_titan ) +{ + entity rodeoPilot = GetRodeoPilot( playerTitan ) + if ( !IsValid( rodeoPilot ) ) + return + + Remote_CallFunction_Replay( rodeoPilot, "ServerCallback_UpdateRodeoRiderHud" ) + +} + +void function SetSmallDisembarkFailSafeTeleportVector( vector value ) //TODO: Re-examine this for next game, probably should have different values for SP versus MP +{ + file.smallDisembarkFailSafeTeleportVector = value +} + +void function SetLargeDisembarkFailSafeTeleportVector( vector value ) //TODO: Re-examine this for next game, probably should have different values for SP versus MP +{ + file.largeDisembarkFailSafeTeleportVector = value +} + +#endif // SERVER + +#if DEV +void function SetEmbarkDebugPrint( bool value ) +{ + file.embarkDebugPrint = value +} +#endif
\ No newline at end of file diff --git a/Northstar.Custom/scripts/vscripts/weapons/mp_weapon_peacekraber.nut b/Northstar.Custom/scripts/vscripts/weapons/mp_weapon_peacekraber.nut new file mode 100644 index 00000000..a9da541f --- /dev/null +++ b/Northstar.Custom/scripts/vscripts/weapons/mp_weapon_peacekraber.nut @@ -0,0 +1,162 @@ +untyped + +// created by JustANormalUser#0001 on discord + +global function OnWeaponPrimaryAttack_peacekraber; +global function OnWeaponDeactivate_peacekraber +global function OnWeaponActivate_peacekraber + +#if SERVER +global function OnWeaponNpcPrimaryAttack_peacekraber +#endif // #if SERVER + + +const PEACEKRABER_MAX_BOLTS = 11 // this is the code limit for bolts per frame... do not increase. +bool isWeaponActive = false; +entity clientWeapon = null; + +struct +{ + float[2][PEACEKRABER_MAX_BOLTS] boltOffsets = [ + [0.0, 0.0], // center + [0.0, 1.0], // top + [-0.683, 0.327], + [0.683, 0.327], + [-0.636, -0.683], + [0.636, -0.683], + [0.0, 0.5], + [-0.342, 0.174], + [0.342, 0.174], + [-0.318, -0.342], + [0.318, -0.342], + ] + + int maxAmmo + float ammoRegenTime +} file +// "OnWeaponActivate" "OnWeaponActivate_peacekraber" +// "OnWeaponDeactivate" "OnWeaponDeactivate_peacekraber" +void function OnWeaponActivate_peacekraber (entity weapon) { + #if CLIENT + if (!weapon.GetWeaponOwner().IsPlayer() || weapon.GetWeaponOwner() != GetLocalViewPlayer()) return; + isWeaponActive = true; + clientWeapon = weapon; + thread CrosshairCycle(); + #endif +} + +void function OnWeaponDeactivate_peacekraber (entity weapon) { + #if CLIENT + if (!weapon.GetWeaponOwner().IsPlayer() || weapon.GetWeaponOwner() != GetLocalViewPlayer()) return; + isWeaponActive = false; + #endif +} +#if CLIENT +void function CrosshairCycle() { + var rui = RuiCreate( $"ui/crosshair_shotgun.rpak", clGlobal.topoCockpitHudPermanent, RUI_DRAW_COCKPIT, 0 ) + RuiSetFloat(rui, "adjustedSpread", 0.1) + array<int> spreadFrac = [1, 0.65, 0.45, 0.2] + array<vector> colors = [<1, 1, 1>, <0.666, 1, 1>, <0.333, 1, 1>, <0, 1, 1>] + int chargeLevel; + float chargeFrac; + while (isWeaponActive) { + WaitFrame() + chargeLevel = clientWeapon.GetWeaponChargeLevel(); + chargeFrac = clientWeapon.GetWeaponChargeFraction(); + RuiSetFloat3(rui, "teamColor", colors[chargeLevel]); + switch (chargeLevel) { + case 0: + if (chargeFrac > 0.266) { + RuiSetFloat(rui, "adjustedSpread", Graph(chargeFrac, 0.266, 0.333, 0.1, 0.065)) + } + else RuiSetFloat(rui, "adjustedSpread", 0.1) + break; + case 1: + if (chargeFrac > 0.6) { + RuiSetFloat(rui, "adjustedSpread", Graph(chargeFrac, 0.6, 0.666, 0.065, 0.045)) + } + else RuiSetFloat(rui, "adjustedSpread", 0.065) + break; + case 2: + if (chargeFrac > 0.933) { + RuiSetFloat(rui, "adjustedSpread", Graph(chargeFrac, 0.933, 1, 0.045, 0.02)) + } + else RuiSetFloat(rui, "adjustedSpread", 0.045) + break; + case 3: + RuiSetFloat(rui, "adjustedSpread", 0.025) + break; + default: + break; + } + } + + RuiDestroy(rui); + clientWeapon = null; +} +#endif + +var function OnWeaponPrimaryAttack_peacekraber( entity weapon, WeaponPrimaryAttackParams attackParams ) +{ + #if CLIENT + weapon.EmitWeaponSound( "Weapon_Titan_Sniper_LevelTick_2" ) + #endif + + return FireWeaponPlayerAndNPC( attackParams, true, weapon ) +} + +#if SERVER +var function OnWeaponNpcPrimaryAttack_peacekraber( entity weapon, WeaponPrimaryAttackParams attackParams ) +{ + return FireWeaponPlayerAndNPC( attackParams, false, weapon ) +} +#endif // #if SERVER + +function FireWeaponPlayerAndNPC( WeaponPrimaryAttackParams attackParams, bool playerFired, entity weapon ) +{ + entity owner = weapon.GetWeaponOwner() + bool shouldCreateProjectile = false + if ( IsServer() || weapon.ShouldPredictProjectiles() ) + shouldCreateProjectile = true + #if CLIENT + if ( !playerFired ) + shouldCreateProjectile = false + #endif + + vector attackAngles = VectorToAngles( attackParams.dir ) + vector baseUpVec = AnglesToUp( attackAngles ) + vector baseRightVec = AnglesToRight( attackAngles ) + + if ( shouldCreateProjectile ) + { + weapon.EmitWeaponNpcSound( LOUD_WEAPON_AI_SOUND_RADIUS_MP, 0.2 ) + array<int> spreadFrac = [1, 0.65, 0.45, 0.2] + + for ( int index = 0; index < PEACEKRABER_MAX_BOLTS; index++ ) + { + vector upVec = baseUpVec * file.boltOffsets[index][1] * 0.05 * spreadFrac[weapon.GetWeaponChargeLevel()] + vector rightVec = baseRightVec * file.boltOffsets[index][0] * 0.05 * spreadFrac[weapon.GetWeaponChargeLevel()] + + vector attackDir = attackParams.dir + upVec + rightVec + float projectileSpeed = 2800 + + if ( weapon.GetWeaponClassName() == "mp_weapon_peacekraber" ) + { + projectileSpeed = 6400 + } + + entity bolt = weapon.FireWeaponBolt( attackParams.pos, attackDir, projectileSpeed, damageTypes.largeCaliber | DF_SHOTGUN, damageTypes.largeCaliber | DF_SHOTGUN, playerFired, index ) + if ( bolt ) + { + bolt.kv.gravity = 0.4 // 0.09 + + if ( weapon.GetWeaponClassName() == "mp_weapon_peacekraber" ) + bolt.SetProjectileLifetime( RandomFloatRange( 1.0, 1.3 ) ) + else + bolt.SetProjectileLifetime( RandomFloatRange( 0.50, 0.65 ) ) + } + } + } + + return 1 +}
\ No newline at end of file diff --git a/Northstar.Custom/scripts/vscripts/weapons/mp_weapon_toolgun.nut b/Northstar.Custom/scripts/vscripts/weapons/mp_weapon_toolgun.nut new file mode 100644 index 00000000..94bd7429 --- /dev/null +++ b/Northstar.Custom/scripts/vscripts/weapons/mp_weapon_toolgun.nut @@ -0,0 +1,39 @@ +untyped +global function OnWeaponActivate_weapon_toolgun +global function OnWeaponDeactivate_weapon_toolgun +global function OnWeaponPrimaryAttack_weapon_toolgun +global function OnWeaponStartZoomIn_weapon_toolgun +global function OnWeaponStartZoomOut_weapon_toolgun +#if SERVER +global function OnWeaponNpcPrimaryAttack_weapon_toolgun +#endif + +void function OnWeaponActivate_weapon_toolgun( entity weapon ) +{ + CallToolOnEquipped( weapon.GetOwner(), weapon ) +} + +void function OnWeaponDeactivate_weapon_toolgun( entity weapon ) +{ + CallToolOnUnequipped( weapon.GetOwner(), weapon ) +} + +var function OnWeaponPrimaryAttack_weapon_toolgun( entity weapon, WeaponPrimaryAttackParams attackParams ) +{ + CallToolOnFired( weapon.GetOwner(), weapon, attackParams ) +} + +void function OnWeaponStartZoomIn_weapon_toolgun( entity weapon ) +{ + CallToolOnAds( weapon.GetOwner(), weapon ) +} + +void function OnWeaponStartZoomOut_weapon_toolgun( entity weapon ) +{ + CallToolOnUnAds( weapon.GetOwner(), weapon ) +} + +var function OnWeaponNpcPrimaryAttack_weapon_toolgun( entity weapon, WeaponPrimaryAttackParams attackParams ) +{ + // do nothing for now, maybe make it launch nukes or something later that could be funny +}
\ No newline at end of file diff --git a/Northstar.Custom/scripts/vscripts/weapons/toolgun/sh_toolgun_tool_explode.nut b/Northstar.Custom/scripts/vscripts/weapons/toolgun/sh_toolgun_tool_explode.nut new file mode 100644 index 00000000..512c538c --- /dev/null +++ b/Northstar.Custom/scripts/vscripts/weapons/toolgun/sh_toolgun_tool_explode.nut @@ -0,0 +1,30 @@ +global function ToolgunToolCreateExplosion_Init + +void function ToolgunToolCreateExplosion_Init() +{ + AddCallback_OnToolgunToolsInit( CreateToolgunToolCreateExplosion ) +} + +void function CreateToolgunToolCreateExplosion() +{ + ToolgunTool createExplosionTool + createExplosionTool.toolName = "Create explosion" + createExplosionTool.toolDescription = "Creates an explosion" + + createExplosionTool.onFired = ToolgunToolCreateExplosion_Fire + + RegisterTool( createExplosionTool ) +} + +void function ToolgunToolCreateExplosion_Fire( entity player, entity weapon, WeaponPrimaryAttackParams attackParams ) +{ + #if SERVER + int dist = 55555 // should hit edge of map at all times ideally + vector forward = AnglesToForward( player.EyeAngles() ) + + // raycast to explosion position + TraceResults trace = TraceLine( player.EyePosition(), player.EyePosition() + ( dist * forward ), null, TRACE_MASK_NPCSOLID ) + // make explosion + Explosion( trace.endPos, player, player, 90, 90, 100, 100, 0, trace.endPos, 5000, damageTypes.explosive, eDamageSourceId.burn, "exp_small" ) + #endif +}
\ No newline at end of file diff --git a/Northstar.Custom/scripts/vscripts/weapons/toolgun/sh_toolgun_tool_throw.nut b/Northstar.Custom/scripts/vscripts/weapons/toolgun/sh_toolgun_tool_throw.nut new file mode 100644 index 00000000..d6975c6d --- /dev/null +++ b/Northstar.Custom/scripts/vscripts/weapons/toolgun/sh_toolgun_tool_throw.nut @@ -0,0 +1,24 @@ +global function ToolgunToolThrowEntity_Init + +void function ToolgunToolThrowEntity_Init() +{ + AddCallback_OnToolgunToolsInit( CreateToolgunToolThrow ) +} + +void function CreateToolgunToolThrow() +{ + ToolgunTool throwTool + throwTool.toolName = "Throw entity" + throwTool.toolDescription = "Spawns and throws the currently selected entity type" + + throwTool.onFired = ToolgunToolThrow_OnFired + + RegisterTool( throwTool ) +} + +void function ToolgunToolThrow_OnFired( entity player, entity weapon, WeaponPrimaryAttackParams attackParams ) +{ + #if SERVER + ClientCommand( player, "ent_throw npc_frag_drone" ) + #endif +}
\ No newline at end of file diff --git a/Northstar.Custom/scripts/vscripts/weapons/toolgun/sh_toolgun_tools.gnut b/Northstar.Custom/scripts/vscripts/weapons/toolgun/sh_toolgun_tools.gnut new file mode 100644 index 00000000..4d7e9d89 --- /dev/null +++ b/Northstar.Custom/scripts/vscripts/weapons/toolgun/sh_toolgun_tools.gnut @@ -0,0 +1,196 @@ +untyped +global function ToolgunTools_Init +global function AddCallback_OnToolgunToolsInit + +global function RegisterTool +global function CallToolOnEquipped +global function CallToolOnUnequipped +global function CallToolOnFired +global function CallToolOnAds +global function CallToolOnUnAds + +#if SERVER +global function SetToolgunAmmoCount +global function SetToolgunProscreen +#endif // #if SERVER + +global struct ToolgunTool +{ + string toolName = "" + string toolDescription = "" + + void functionref( entity, entity ) onEquipped + void functionref( entity, entity ) onUnequipped + void functionref( entity, entity, WeaponPrimaryAttackParams ) onFired + void functionref( entity, entity ) onAds + void functionref( entity, entity ) onUnAds +} + +struct ToolgunPlayerSettings +{ + int selectedToolIndex + int ammoCount + int proscreenNumber +} + +// doing preprocessor defs inside the struct seemed to cause compiler errors so we define them separately +#if CLIENT +struct { + array<void functionref()> onToolgunToolsInitCallbacks + + array<ToolgunTool> tools + ToolgunPlayerSettings clientPlayerSettings +} file +#endif // #if CLIENT + +#if SERVER +struct { + array<void functionref()> onToolgunToolsInitCallbacks + + array<ToolgunTool> tools + // serverside playersettings + table<int, ToolgunPlayerSettings> playerSettings +} file +#endif // #if SERVER + +void function AddCallback_OnToolgunToolsInit( void functionref() callback ) +{ + file.onToolgunToolsInitCallbacks.append( callback ) +} + +void function ToolgunTools_Init() +{ + //#if SERVER + //AddCallback_OnClientConnecting( InitialiseToolgunSettings ) + //AddCallback_OnClientDisconnected( DestroyToolgunSettings ) + //#endif // #if SERVER + // + //// need this threaded so we can wait a frame + //thread ToolgunTools_InitThreaded() +} + +void function ToolgunTools_InitThreaded() +{ + // wait a frame for tools to all init and add their callbacks + WaitFrame() + + // call callbacks + foreach ( void functionref() callback in file.onToolgunToolsInitCallbacks ) + callback() +} + +void function RegisterTool( ToolgunTool toolStruct ) +{ + file.tools.append( toolStruct ) +} + +void function CallToolOnEquipped( entity player, entity weapon ) +{ + #if CLIENT + if ( file.tools[ file.clientPlayerSettings.selectedToolIndex ].onEquipped != null ) + file.tools[ file.clientPlayerSettings.selectedToolIndex ].onEquipped( player, weapon ) + #endif // #if CLIENT + + #if SERVER + // set ammo and proscreen numbers when equipped + weapon.SetProScreenIntValForIndex( PRO_SCREEN_INT_LIFETIME_KILLS, file.playerSettings[ player.GetPlayerIndex() ].proscreenNumber ) + weapon.SetWeaponPrimaryClipCount( file.playerSettings[ player.GetPlayerIndex() ].ammoCount ) + + if ( file.tools[ file.playerSettings[ player.GetPlayerIndex() ].selectedToolIndex ].onEquipped != null ) + file.tools[ file.playerSettings[ player.GetPlayerIndex() ].selectedToolIndex ].onEquipped( player, weapon ) + #endif // #if SERVER +} + +void function CallToolOnUnequipped( entity player, entity weapon ) +{ + #if CLIENT + if ( file.tools[ file.clientPlayerSettings.selectedToolIndex ].onUnequipped != null ) + file.tools[ file.clientPlayerSettings.selectedToolIndex ].onUnequipped( player, weapon ) + #endif // #if CLIENT + + #if SERVER + if ( file.tools[ file.playerSettings[ player.GetPlayerIndex() ].selectedToolIndex ].onUnequipped != null ) + file.tools[ file.playerSettings[ player.GetPlayerIndex() ].selectedToolIndex ].onUnequipped( player, weapon ) + #endif // #if SERVER +} + +void function CallToolOnFired( entity player, entity weapon, WeaponPrimaryAttackParams attackParams ) +{ + #if CLIENT + if ( file.tools[ file.clientPlayerSettings.selectedToolIndex ].onFired != null ) + file.tools[ file.clientPlayerSettings.selectedToolIndex ].onFired( player, weapon, attackParams ) + #endif // #if CLIENT + + #if SERVER + // ammocount needs to be +1 because we lose 1 ammo immediately after this function is run + weapon.SetWeaponPrimaryClipCount( file.playerSettings[ player.GetPlayerIndex() ].ammoCount + 1 ) + + if ( file.tools[ file.playerSettings[ player.GetPlayerIndex() ].selectedToolIndex ].onFired != null ) + file.tools[ file.playerSettings[ player.GetPlayerIndex() ].selectedToolIndex ].onFired( player, weapon, attackParams ) + #endif // #if SERVER +} + +void function CallToolOnAds( entity player, entity weapon ) +{ + #if CLIENT + if ( file.tools[ file.clientPlayerSettings.selectedToolIndex ].onAds != null ) + file.tools[ file.clientPlayerSettings.selectedToolIndex ].onAds( player, weapon ) + #endif // #if CLIENT + + #if SERVER + if ( file.tools[ file.playerSettings[ player.GetPlayerIndex() ].selectedToolIndex ].onUnequipped != null ) + file.tools[ file.playerSettings[ player.GetPlayerIndex() ].selectedToolIndex ].onUnequipped( player, weapon ) + #endif // #if SERVER +} + +void function CallToolOnUnAds( entity player, entity weapon ) +{ + #if CLIENT + if ( file.tools[ file.clientPlayerSettings.selectedToolIndex ].onUnAds != null ) + file.tools[ file.clientPlayerSettings.selectedToolIndex ].onUnAds( player, weapon ) + #endif // #if CLIENT + + #if SERVER + if ( file.tools[ file.playerSettings[ player.GetPlayerIndex() ].selectedToolIndex ].onUnequipped != null ) + file.tools[ file.playerSettings[ player.GetPlayerIndex() ].selectedToolIndex ].onUnequipped( player, weapon ) + #endif // #if SERVER +} + +#if SERVER +void function InitialiseToolgunSettings( entity player ) +{ + print( "initialising toolgun settings for player " + player ) + + ToolgunPlayerSettings playerSettings + playerSettings.selectedToolIndex = 0 + + file.playerSettings[ player.GetPlayerIndex() ] <- playerSettings +} + +void function DestroyToolgunSettings( entity player ) +{ + delete file.playerSettings[ player.GetPlayerIndex() ] +} + +void function SetToolgunAmmoCount( entity player, int ammoCount ) +{ + entity currentWeapon = player.GetActiveWeapon() + if ( ammoCount > currentWeapon.GetWeaponPrimaryClipCountMax() ) // setting clipcount to over max clipcount crashes so we need to prevent that + return + + ToolgunPlayerSettings playerSettings = file.playerSettings[ player.GetPlayerIndex() ] + playerSettings.ammoCount = ammoCount + + if ( currentWeapon.GetWeaponClassName() == "mp_weapon_toolgun" ) // set ammocount immediately if we've got toolgun equipped already + currentWeapon.SetWeaponPrimaryClipCount( ammoCount ) +} + +void function SetToolgunProscreen( entity player, int proscreenNumber ) +{ + ToolgunPlayerSettings playerSettings = file.playerSettings[ player.GetPlayerIndex() ] + playerSettings.proscreenNumber = proscreenNumber + + if ( player.GetActiveWeapon().GetWeaponClassName() == "mp_weapon_toolgun" ) // set proscreen number immediately if we've got toolgun equipped already + player.GetActiveWeapon().SetProScreenIntValForIndex( PRO_SCREEN_INT_LIFETIME_KILLS, proscreenNumber ) +} +#endif // #if SERVER
\ No newline at end of file |