From 852f3bcb04f7dfbf8f41676d47c01d4d6fbf084e Mon Sep 17 00:00:00 2001 From: William Miller Date: Sat, 6 Jul 2024 19:02:53 -0300 Subject: Rework Intro Dropship Script to avoid player overlap (#809) This overhaul makes the Dropships behave consistently now like vanilla and players will only start overlapping each other when both Dropships are full, otherwise code will always attempt to populate them properly instead. --- .../vscripts/mp/_classic_mp_dropship_intro.gnut | 177 ++++++++++----------- 1 file changed, 86 insertions(+), 91 deletions(-) (limited to 'Northstar.CustomServers/mod/scripts/vscripts') diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/_classic_mp_dropship_intro.gnut b/Northstar.CustomServers/mod/scripts/vscripts/mp/_classic_mp_dropship_intro.gnut index 23ae37a17..7b6c7e9f8 100644 --- a/Northstar.CustomServers/mod/scripts/vscripts/mp/_classic_mp_dropship_intro.gnut +++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/_classic_mp_dropship_intro.gnut @@ -26,18 +26,9 @@ const int MAX_DROPSHIP_PLAYERS = 4 global const float DROPSHIP_INTRO_LENGTH = 15.0 // TODO tweak this -struct IntroDropship -{ - entity dropship - - int playersInDropship - entity[MAX_DROPSHIP_PLAYERS] players -} - struct { - // these used to be IntroDropship[2]s but i wanted to be able to use array.getrandom so they have to be actual arrays - array militiaDropships - array imcDropships + table< entity, array > militiaDropships + table< entity, array > imcDropships float introStartTime } file @@ -52,7 +43,12 @@ void function ClassicMP_DefaultDropshipIntro_Setup() void function DropshipIntro_OnClientConnected( entity player ) { if ( GetGameState() == eGameState.Prematch ) - thread SpawnPlayerIntoDropship( player ) + { + if( PlayerCanSpawn( player ) ) + DoRespawnPlayer( player, null ) + + PutPlayerInDropship( player ) + } } void function OnPrematchStart() @@ -62,11 +58,11 @@ void function OnPrematchStart() print( "starting dropship intro!" ) file.introStartTime = Time() - // make 2 empty dropship structs per team - IntroDropship emptyDropship + // Clear Dropship arrays of Teams for Match Restarts (i.e Half-Times) file.militiaDropships.clear() file.imcDropships.clear() + // Try to gather all possible Dropship spawn points for Team array validDropshipSpawns array dropshipSpawns = GetEntArrayByClass_Expensive( "info_spawnpoint_dropship_start" ) foreach ( entity dropshipSpawn in dropshipSpawns ) @@ -78,47 +74,47 @@ void function OnPrematchStart() validDropshipSpawns.append( dropshipSpawn ) } - // if no dropship spawns for this mode, just allow any dropship spawns + // Use any spawn point if not enough valid for this Gamemode exists if ( validDropshipSpawns.len() < 2 ) validDropshipSpawns = dropshipSpawns // spawn dropships foreach ( entity dropshipSpawn in validDropshipSpawns ) { - // todo: possibly make this only spawn dropships if we've got enough players to need them int createTeam = HasSwitchedSides() ? GetOtherTeam( dropshipSpawn.GetTeam() ) : dropshipSpawn.GetTeam() - array teamDropships = createTeam == TEAM_MILITIA ? file.militiaDropships : file.imcDropships + table< entity, array > teamDropships = createTeam == TEAM_MILITIA ? file.militiaDropships : file.imcDropships if ( teamDropships.len() >= 2 ) - continue + break - // create entity entity dropship = CreateDropship( createTeam, dropshipSpawn.GetOrigin(), dropshipSpawn.GetAngles() ) - - teamDropships.append( clone emptyDropship ) - teamDropships[ teamDropships.len() - 1 ].dropship = dropship - AddAnimEvent( dropship, "dropship_warpout", WarpoutEffect ) + dropship.SetValueForModelKey( $"models/vehicle/crow_dropship/crow_dropship_hero.mdl" ) - dropship.SetModel( $"models/vehicle/crow_dropship/crow_dropship_hero.mdl" ) + if ( dropshipSpawn.GetTeam() == TEAM_IMC ) + dropship.SetValueForModelKey( $"models/vehicle/goblin_dropship/goblin_dropship_hero.mdl" ) DispatchSpawn( dropship ) - // have to do this after dispatch otherwise it won't work for some reason - // weirdly enough, tf2 actually does use different dropships for imc and militia, despite these concepts not really being a thing for players in tf2 - // probably was just missed by devs, but keeping it in for accuracy + dropship.SetModel( $"models/vehicle/crow_dropship/crow_dropship_hero.mdl" ) if ( dropshipSpawn.GetTeam() == TEAM_IMC ) dropship.SetModel( $"models/vehicle/goblin_dropship/goblin_dropship_hero.mdl" ) - else - dropship.SetModel( $"models/vehicle/crow_dropship/crow_dropship_hero.mdl" ) + + teamDropships[ dropship ] <- [ null, null, null, null ] thread PlayAnim( dropship, "dropship_classic_mp_flyin" ) } + // Populate Dropships foreach ( entity player in GetPlayerArray() ) { if ( !IsPrivateMatchSpectator( player ) ) - thread SpawnPlayerIntoDropship( player ) + { + if( PlayerCanSpawn( player ) ) + DoRespawnPlayer( player, null ) + + PutPlayerInDropship( player ) + } else RespawnPrivateMatchSpectator( player ) } @@ -128,68 +124,69 @@ void function OnPrematchStart() void function EndIntroWhenFinished() { - wait 15.0 + wait DROPSHIP_INTRO_LENGTH ClassicMP_OnIntroFinished() } -void function SpawnPlayerIntoDropship( entity player ) +void function PutPlayerInDropship( entity player ) { - player.EndSignal( "OnDestroy" ) + //Find the player's dropship and seat + table< entity, array > teamDropships + if ( player.GetTeam() == TEAM_MILITIA ) + teamDropships = file.militiaDropships + else + teamDropships = file.imcDropships + + entity playerDropship + array< int > availableShipSlots + array< entity > introDropships + int playerDropshipIndex = RandomInt( MAX_DROPSHIP_PLAYERS ) + foreach( dropship, playerslot in teamDropships ) + { + introDropships.append( dropship ) + for ( int i = 0; i < MAX_DROPSHIP_PLAYERS; i++ ) + { + if ( !IsValidPlayer( playerslot[i] ) ) + availableShipSlots.append( i ) + } + + if( !availableShipSlots.len() ) + continue + + int slotPick = availableShipSlots.getrandom() + playerslot[slotPick] = player + playerDropship = dropship + playerDropshipIndex = slotPick + break + } + + if( !IsAlive( playerDropship ) ) //If we're at this point, we have more players than we do dropships, so just pick a random one + playerDropship = introDropships.getrandom() + + thread SpawnPlayerIntoDropship( player, playerDropshipIndex, playerDropship ) +} - if ( IsAlive( player ) ) - player.Die() // kill them so we don't have any issues respawning them later +void function SpawnPlayerIntoDropship( entity player, int playerDropshipIndex, entity playerDropship ) +{ + player.EndSignal( "OnDestroy" ) + player.EndSignal( "OnDeath" ) - player.s.dropshipIntroIsJumping <- false - OnThreadEnd( function() : ( player ) + OnThreadEnd( function() : ( player, playerDropshipIndex, playerDropship ) { if ( IsValid( player ) ) { player.ClearParent() ClearPlayerAnimViewEntity( player ) - - if ( !player.s.dropshipIntroIsJumping ) - { - player.MovementEnable() - player.EnableWeaponViewModel() - RemoveCinematicFlag( player, CE_FLAG_CLASSIC_MP_SPAWNING ) - } + } + if( IsAlive( playerDropship ) ) + { + if ( playerDropship.GetTeam() == TEAM_MILITIA ) + file.militiaDropships[ playerDropship ][ playerDropshipIndex ] = null + else + file.imcDropships[ playerDropship ][ playerDropshipIndex ] = null } }) - WaitFrame() - - player.EndSignal( "OnDeath" ) - - // find the player's dropship and seat - array teamDropships - if ( player.GetTeam() == TEAM_MILITIA ) - teamDropships = file.militiaDropships - else - teamDropships = file.imcDropships - - IntroDropship playerDropship - int playerDropshipIndex = -1 - foreach ( IntroDropship dropship in teamDropships ) - for ( int i = 0; i < dropship.players.len(); i++ ) - if ( dropship.players[ i ] == null ) - { - playerDropship = dropship - playerDropshipIndex = i - - dropship.players[ i ] = player - break - } - - if ( playerDropship.dropship == null ) - { - // if we're at this point, we have more players than we do dropships, so just pick a random one - playerDropship = teamDropships.getrandom() - playerDropshipIndex = RandomInt( MAX_DROPSHIP_PLAYERS ) - } - - // respawn player and holster their weapons so they aren't out - if ( !IsAlive( player ) ) - player.RespawnPlayer( null ) HolsterAndDisableWeapons(player) player.DisableWeaponViewModel() @@ -208,9 +205,7 @@ void function SpawnPlayerIntoDropship( entity player ) idleSequence.viewConeFunction = ViewConeRampFree idleSequence.hideProxy = true idleSequence.setInitialTime = Time() - file.introStartTime - thread FirstPersonSequence( idleSequence, player, playerDropship.dropship ) - WaittillAnimDone( player ) - + waitthread FirstPersonSequence( idleSequence, player, playerDropship ) // todo: possibly rework this to actually get the time the idle anim takes and start the starttime of the jump sequence for very late joiners using that // jump sequence @@ -218,13 +213,12 @@ void function SpawnPlayerIntoDropship( entity player ) jumpSequence.firstPersonAnim = DROPSHIP_JUMP_ANIMS_POV[ playerDropshipIndex ] jumpSequence.thirdPersonAnim = DROPSHIP_JUMP_ANIMS[ playerDropshipIndex ] jumpSequence.attachment = "ORIGIN" + jumpSequence.viewConeFunction = ViewConeFree jumpSequence.setInitialTime = max( 0.0, Time() - ( file.introStartTime + 11.0 ) ) // pretty sure you should do this with GetScriptedAnimEventCycleFrac? // idk unsure how to use that, all i know is getsequenceduration > the length it actually should be - thread FirstPersonSequence( jumpSequence, player, playerDropship.dropship ) - WaittillAnimDone( player ) // somehow this is better than just waiting for the blocking FirstPersonSequence call? + waitthread FirstPersonSequence( jumpSequence, player, playerDropship ) - player.s.dropshipIntroIsJumping <- true thread PlayerJumpsFromDropship( player ) } @@ -244,16 +238,17 @@ void function PlayerJumpsFromDropship( entity player ) RemoveCinematicFlag( player, CE_FLAG_CLASSIC_MP_SPAWNING ) } }) - - // wait for intro timer to be fully done - wait ( file.introStartTime + DROPSHIP_INTRO_LENGTH ) - Time() - player.MovementDisable() // disable all movement but let them look around still - player.ConsumeDoubleJump() // movementdisable doesn't prevent double jumps // wait for player to hit the ground - wait 0.1 // assume players will never actually hit ground before this + player.ClearParent() + WaitFrame() + player.SetVelocity( < 0, 0, -100 > ) // Toss players a bit down so it makes a smoother transition when jumping off the Dropship + player.MovementDisable() // Disable all movement but let them look around still + player.ConsumeDoubleJump() // MovementDisable doesn't prevent double jumps + WaitFrame() while ( !player.IsOnGround() && !player.IsWallRunning() && !player.IsWallHanging() ) // todo this needs tweaking WaitFrame() - TryGameModeAnnouncement( player ) + if ( GetRoundsPlayed() == 0 ) //Intro is announced only for the first round in Vanilla as certain gamemodes have different announcements for rounds restarts + TryGameModeAnnouncement( player ) } -- cgit v1.2.3