diff options
Diffstat (limited to 'Northstar.CustomServers/mod/scripts/vscripts/mp')
7 files changed, 179 insertions, 1275 deletions
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/_ai_mp.gnut b/Northstar.CustomServers/mod/scripts/vscripts/mp/_ai_mp.gnut index 4cbab84c..87e3650f 100644 --- a/Northstar.CustomServers/mod/scripts/vscripts/mp/_ai_mp.gnut +++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/_ai_mp.gnut @@ -1,15 +1,22 @@ +untyped global function MpInitAILoadouts global function SetProficiency -global function IsAutoPopulateEnabled global function SPMP_UpdateNPCProficiency global function SPMP_Callback_ForceAIMissPlayer +global function IsAutoPopulateEnabled void function MpInitAILoadouts() { } -void function SetProficiency( entity soldier ) +void function SetProficiency( entity npc ) +{ + // unsure what the logic for deciding this should be so just going to default good + npc.kv.WeaponProfiency = eWeaponProficiency.VERYGOOD +} + +void function SPMP_UpdateNPCProficiency( entity npc ) { } @@ -30,12 +37,33 @@ bool function IsAutoPopulateEnabled( var team = null ) return true } -void function SPMP_UpdateNPCProficiency( entity ent ) -{ - -} - bool function SPMP_Callback_ForceAIMissPlayer( entity npc, entity player ) { - return true + if ( GetGameState() >= eGameState.Postmatch ) + return true + + if ( player.IsTitan() ) + return false + + int lethality = Riff_AILethality() + if ( lethality <= eAILethality.Medium ) + if ( player.GetTitanSoulBeingRodeoed() != null ) + return true + + if ( Bleedout_IsBleedoutLogicActive() && Bleedout_ShouldAIMissBleedingPlayer( player ) ) + return true + + if ( player.ContextAction_IsActive() ) + return RandomFloat( 1 ) >= 0.25 + + if ( IsFastPilot( player ) ) + { + float chance = ( lethality + 1 ) * 0.125 + if ( lethality <= eAILethality.Medium && npc.IsMechanical() ) + chance /= 1.25 + + return RandomFloat( 1 ) >= chance + } + + return false }
\ No newline at end of file diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/_base_gametype_mp.gnut b/Northstar.CustomServers/mod/scripts/vscripts/mp/_base_gametype_mp.gnut index cdefd8c8..70fa148e 100644 --- a/Northstar.CustomServers/mod/scripts/vscripts/mp/_base_gametype_mp.gnut +++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/_base_gametype_mp.gnut @@ -423,9 +423,7 @@ void function RespawnAsTitan( entity player, bool manualPosition = false ) asset model = GetPlayerSettingsAssetForClassName( titanLoadout.setFile, "bodymodel" ) Attachment warpAttach = GetAttachmentAtTimeFromModel( model, "at_hotdrop_01", "offset", spawnpoint.GetOrigin(), spawnpoint.GetAngles(), 0 ) PlayFX( TURBO_WARP_FX, warpAttach.position, warpAttach.angle ) - - player.RespawnPlayer( null ) // spawn player as pilot so they get their pilot loadout on embark - + entity titan = CreateAutoTitanForPlayer_FromTitanLoadout( player, titanLoadout, spawnpoint.GetOrigin(), spawnpoint.GetAngles() ) DispatchSpawn( titan ) player.SetPetTitan( null ) // prevent embark prompt from showing up @@ -459,6 +457,10 @@ void function RespawnAsTitan( entity player, bool manualPosition = false ) player.DeployWeapon() // let them use weapons again player.isSpawning = false + player.RespawnPlayer( null ) // spawn player as pilot so they get their pilot loadout on embark + player.SetOrigin( titan.GetOrigin() ) + WaitFrame() + PilotBecomesTitan( player, titan ) // make player titan titan.Destroy() // pilotbecomestitan leaves an npc titan that we need to delete } diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/_changemap.nut b/Northstar.CustomServers/mod/scripts/vscripts/mp/_changemap.nut index 94fda4d7..a2e95e08 100644 --- a/Northstar.CustomServers/mod/scripts/vscripts/mp/_changemap.nut +++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/_changemap.nut @@ -46,8 +46,8 @@ void function PopulatePostgameData() player.SetPersistentVar( "postGameData.myTeam", player.GetTeam() ) player.SetPersistentVar( "postGameData.myXuid", player.GetUID() ) - player.SetPersistentVar( "postGameData.gameMode", PersistenceGetEnumIndexForItemName( "gamemodes", GAMETYPE ) ) - player.SetPersistentVar( "postGameData.map", PersistenceGetEnumIndexForItemName( "maps", GetMapName() ) ) + player.SetPersistentVar( "postGameData.gameMode", enumModeIndex ) + player.SetPersistentVar( "postGameData.map", enumMapIndex ) player.SetPersistentVar( "postGameData.teams", standardTeams ) player.SetPersistentVar( "postGameData.maxTeamSize", teams ) player.SetPersistentVar( "postGameData.privateMatch", true ) diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/_gamestate_mp.nut b/Northstar.CustomServers/mod/scripts/vscripts/mp/_gamestate_mp.nut index e3f7e0b0..4263eb1a 100644 --- a/Northstar.CustomServers/mod/scripts/vscripts/mp/_gamestate_mp.nut +++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/_gamestate_mp.nut @@ -314,6 +314,20 @@ void function GameStateEnter_WinnerDetermined_Threaded() if ( killcamsWereEnabled ) SetKillcamsEnabled( true ) } + else + { + // these numbers are temp and should really be based on consts of some kind + foreach( entity player in GetPlayerArray() ) + { + player.FreezeControlsOnServer() + ScreenFadeToBlackForever( player, 2.0 ) + } + + wait 2.5 + + foreach( entity player in GetPlayerArray() ) + player.UnfreezeControlsOnServer() + } if ( IsRoundBased() ) { diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/_pickups.gnut b/Northstar.CustomServers/mod/scripts/vscripts/mp/_pickups.gnut deleted file mode 100644 index ecf9b3e5..00000000 --- a/Northstar.CustomServers/mod/scripts/vscripts/mp/_pickups.gnut +++ /dev/null @@ -1,1195 +0,0 @@ -untyped - -global function Pickups_Init -global function AddCollectible -global function UpdateCollectiblesAfterLoadSaveGame -global function CreateWeaponPickup -global function CreatePickup -global function WaitUntilPlayerPicksUp -global function AddClipToWeapon -global function AddRoundsToWeapon -global function AddRoundToWeapon -global function AddClipToMainWeapons -global function AddTwoClipToMainWeapons -global function AddRoundToOrdnance -global function AddRoundsToTactical -global function CreateScriptWeapon -global function GetAllLeveledScriptWeapons -global function TitanLoadoutWaitsForPickup // Needed only to be global to spawn pickups from dev menu. - -#if DEV - global function Dev_ResetCollectiblesProgress_Level -#endif - -const HEALTH_PICKUP_AMOUNT = 3000 -global const PICKUP_GLOW_FX = $"P_ar_titan_droppoint" -//const HEALTH_MODEL = $"models/vehicle/droppod_fireteam/droppod_fireteam_collision.mdl" -//const HEALTH_MODEL = $"models/domestic/trash_can_green_closed.mdl" -//const HEALTH_MODEL = $"models/weapons/bullets/projectile_rocket_large.mdl" -const HEALTH_MODEL = $"models/gameplay/health_pickup_small.mdl" -const HEALTH_MODEL_LARGE = $"models/gameplay/health_pickup_large.mdl" - -const GRENADE_AMMO_MODEL = $"models/Weapons/ammoboxes/ammobox_01.mdl" -const LION_MODEL = $"models/statues/lion_statue_bronze_green_small.mdl" -const HELMET_COLLECTIBLE_MODEL = $"models/humans/heroes/mlt_hero_jack_helmet_static.mdl" -const COLLECTIBLE_GLOW_EFFECT = $"P_item_bluelion" - - -global struct LeveledScriptedWeapons -{ - table<string, bool> foundScriptWeapons - array<entity> infoTargets -} - -struct HealthPickup -{ - float healAmount - float healTime - string pickupSound - string healSound - string endSound - asset model -} - -struct Collectible -{ - entity ent - int id - vector pos -} - -struct -{ - int nextHealthDropSmall - int nextHealthDropLarge - float lastHealthDropTime - table<string, HealthPickup> healthPickups - array<Collectible> collectibles - int testMapCollectibleValue -} file - -function Pickups_Init() -{ - HealthPickup small - small.healAmount = 0.4 - small.healTime = 1.0 - small.pickupSound = "Pilot_HealthPack_Small_Pickup" - small.healSound = "Pilot_HealthPack_Small_Healing" - small.endSound = "Pilot_HealthPack_Small_Healing_End" - small.model = HEALTH_MODEL - file.healthPickups[ "health_pickup_small" ] <- small - - HealthPickup large - large.healAmount = 0.8 - large.healTime = 2.0 - large.pickupSound = "Pilot_HealthPack_Large_Pickup" - large.healSound = "Pilot_HealthPack_Large_Healing" - large.endSound = "Pilot_HealthPack_Large_Healing_End" - large.model = HEALTH_MODEL_LARGE - file.healthPickups[ "health_pickup_large" ] <- large - - - //AddSpawnCallbackEditorClass( "script_ref", "script_pickup_health", HealthPickup_OnSpawned ) - //AddSpawnCallbackEditorClass( "script_ref", "script_pickup_health_large", HealthPickupLarge_OnSpawned ) - AddSpawnCallbackEditorClass( "script_mover_lightweight", "script_collectible", AddCollectible ) - AddSpawnCallbackEditorClass( "script_ref", "script_pickup_weapon", CreateWeaponPickup ) - //AddSpawnCallbackEditorClass( "script_ref", "script_pickup_grenades", CreateGrenadeAmmoPickup ) - //AddSpawnCallbackEditorClass( "script_ref", "script_pickup_ammo", CreateGrenadeAmmoPickup ) - AddSpawnCallbackEditorClass( "script_ref", "script_pickup_titan", CreateTitanPickup ) - - PrecacheModel( HEALTH_MODEL ) - PrecacheModel( HEALTH_MODEL_LARGE ) - PrecacheModel( GRENADE_AMMO_MODEL ) - PrecacheModel( LION_MODEL ) - PrecacheModel( HELMET_COLLECTIBLE_MODEL ) - PrecacheParticleSystem( COLLECTIBLE_PICKUP_EFFECT ) - PrecacheParticleSystem( COLLECTIBLE_GLOW_EFFECT ) - - RegisterSignal( "NewHealthPickup" ) - RegisterSignal( "CollectibleEndThink" ) - - SetNextHealthDropSmall() - SetNextHealthDropLarge() - - AddCallback_EntitiesDidLoad( EntitiesDidLoad_Pickups ) -} - -void function CreateScriptWeapon( entity point ) -{ - CreateWeaponPickup( point ) - array<entity> linkParents = point.GetLinkParentArray() - foreach ( entity linkParent in linkParents ) - { - linkParent.UnlinkFromEnt( point ) - } - //point.Destroy() -} - -void function EntitiesDidLoad_Pickups() -{ - if ( shGlobal.proto_pilotHealthPickupsEnabled ) - { - AddDeathCallback( "npc_soldier", OnNPCKilled_DropHealth ) - AddDeathCallback( "npc_spectre", OnNPCKilled_DropHealth ) - } - - SetupCollectibles() -} - -void function SetNextHealthDropSmall() -{ - if ( shGlobal.proto_pilotHealthRegenDisabled ) - file.nextHealthDropSmall = RandomInt( 6 ) + 6 - else - file.nextHealthDropSmall = RandomInt( 4 ) + 4 - - file.lastHealthDropTime = Time() -} - -void function SetNextHealthDropLarge() -{ - file.nextHealthDropLarge = RandomIntRange( 3, 6 ) -} - -void function OnNPCKilled_DropHealth( entity npc, var damageInfo ) -{ - entity attacker = DamageInfo_GetAttacker( damageInfo ) - if ( !IsValid( attacker ) ) - return - - switch ( npc.GetClassName() ) - { - case "npc_soldier": - case "npc_spectre": - case "npc_stalker": - break - - default: - return - } - - OnNPCKilled_DropHealth_Internal( npc, attacker, damageInfo ) -} - -void function OnNPCKilled_DropHealth_Internal( entity npc, entity attacker, var damageInfo ) -{ - if ( npc.GetTeam() != TEAM_IMC ) - return - - file.nextHealthDropSmall-- - - if ( !DropHealthFromDeath( npc, attacker ) ) - return - - SetNextHealthDropSmall() - - vector angles = npc.GetAngles() - angles.x = 0 - - vector forward = AnglesToForward( angles ) - - vector origin = npc.GetWorldSpaceCenter() + forward * 16 - - entity health - file.nextHealthDropLarge-- - if ( file.nextHealthDropLarge < 0 && shGlobal.proto_pilotHealthRegenDisabled ) - { - SetNextHealthDropLarge() - health = CreateHealthPickupSized( origin, angles, "health_pickup_large" ) - } - else - { - health = CreateHealthPickupSized( origin, angles, "health_pickup_small" ) - } - - EmitSoundOnEntity( health, "Pilot_HealthPack_Drop" ) - health.Fire( "Kill", "", 180 ) -} - -bool function DropHealthFromDeath( entity npc, entity attacker ) -{ - if ( !attacker.IsPlayer() ) - return file.nextHealthDropSmall < 0 - - if ( Time() - file.lastHealthDropTime < 3.0 ) - return false - - float healthRatio = float( attacker.GetHealth() ) / attacker.GetMaxHealth() - - float chanceFromNextHealth = GraphCapped( file.nextHealthDropSmall, 0, 5, 0.4, 1.1 ) - float chanceFromLowHealth = GraphCapped( healthRatio, 0.25, 0.80, 0.4, 1.1 ) - float dropChance = chanceFromNextHealth * chanceFromLowHealth - - return RandomFloat( 1.0 ) > dropChance - - /* - if ( file.nextHealthDropSmall <= 1 ) - { - float ratio = float( attacker.GetHealth() ) / attacker.GetMaxHealth() - 0.15 - float random = RandomFloat( 1.0 ) - if ( random > ratio ) - return true - } - - if ( Time() - file.lastHealthDropTime < 5.0 ) - return false - - if ( float( attacker.GetHealth() ) / attacker.GetMaxHealth() > 0.25 ) - return false - - return Distance( attacker.GetOrigin(), npc.GetOrigin() ) < 2000 - */ -} - -void function CreateTitanPickup( entity ent ) -{ - entity mover = CreatePickup( ent, LION_MODEL, DropTitanPickedUp ) - - mover.EndSignal( "OnDestroy" ) - wait 0.2 // for some buggy reason!? - EmitSoundOnEntity( mover, "health_pickup_loopsound_far" ) - EmitSoundOnEntity( mover, "health_pickup_loopsound_near" ) - return -} - -bool function DropTitanPickedUp( entity player ) -{ - if ( player.IsTitan() ) - return false - - AddPlayerScore( player, "PilotHealthPickup" ) - EmitSoundOnEntity( player, "titan_energyshield_up" ) - player.SetNextTitanRespawnAvailable( 0 ) - - return true -} - -function DisplayTempNameText( entity ent, string text ) -{ - ent.EndSignal( "OnDestroy" ) - for ( ;; ) - { - array<entity> players = GetPlayerArray() - if ( players.len() ) - { - entity nearestPlayer = GetClosest( players, ent.GetOrigin(), 2300 ) - if ( nearestPlayer != null ) - DebugDrawText( ent.GetWorldSpaceCenter(), text, true, 1 ) - } - wait 0.9 - } -} - -entity function CreateHealthPickupSized( vector origin, vector angles, string healthType ) -{ - HealthPickup pickup = file.healthPickups[ healthType ] - entity ent = CreatePropPhysics( pickup.model, origin, angles ) - ent.NotSolid() - - - angles = AnglesCompose( angles, < -45,0,0> ) - vector forward = AnglesToForward( angles ) - ent.SetVelocity( forward * 200 ) - - ent.SetAngularVelocity( RandomFloatRange( 300, 500 ), RandomFloatRange( -100, 100 ), 0 ) - - thread HealthPickupWaitsForPickup( ent, pickup ) - Highlight_SetNeutralHighlight( ent, "health_pickup" ) - return ent -} - -void function HealthPickup_OnSpawned( entity ent ) -{ - //if ( shGlobal.proto_pilotHealthRegenDisabled ) - { - CreateHealthPickupSized( ent.GetOrigin(), ent.GetAngles(), "health_pickup_small" ) - ent.Destroy() - return - } - -} - -void function HealthPickupLarge_OnSpawned( entity ent ) -{ - if ( shGlobal.proto_pilotHealthRegenDisabled ) - { - CreateHealthPickupSized( ent.GetOrigin(), ent.GetAngles(), "health_pickup_small" ) - ent.Destroy() - //HealthPickup_OnSpawned( ent ) - return - } - - CreateHealthPickupSized( ent.GetOrigin(), ent.GetAngles(), "health_pickup_large" ) - ent.Destroy() -} - -void function CreateHealthRegenField( entity ent ) -{ - ent.EndSignal( "OnDestroy" ) - OnThreadEnd( - function() : ( ent ) - { - if ( IsValid( ent ) ) - ent.Destroy() - } - ) - - PickupGlow pickupGlow = CreatePickupGlow( ent, 0, 255, 0 ) - - float healthRemainingMax = 1000 - float healthRemainingCurrent = healthRemainingMax - float useIncrement = 9 - float refillRate = 0 - float nextRegenTime = 0 - - bool available - entity player - entity lastPlayer - - for ( ;; ) - { - WaitFrame() - - float ratio = healthRemainingCurrent / healthRemainingMax - int green = int( Graph( ratio, 0, 1, 0, 255 ) ) - PickupGlow_SetColor( pickupGlow, 0, green, 0 ) - - if ( Time() > nextRegenTime ) - { - healthRemainingCurrent = min( healthRemainingCurrent + refillRate, healthRemainingMax ) - nextRegenTime = Time() + 1.2 - } - - if ( healthRemainingCurrent < useIncrement ) - continue - - player = GetHealthPickupPlayer( ent ) - - if ( lastPlayer != player ) - { - if ( IsValid( lastPlayer ) ) - { - //EmitSoundOnEntity( lastPlayer, "Pilot_Stimpack_Deactivate" ) - StopSoundOnEntity( lastPlayer, "Pilot_Stimpack_Loop" ) - } - - if ( player != null ) - { - // new player powers up - EmitSoundOnEntity( player, "Pilot_Stimpack_Activate" ) - EmitSoundOnEntity( player, "Pilot_Stimpack_Loop" ) - } - - lastPlayer = player - } - - if ( player == null ) - continue - - // recent damage reduces healing effect, so you cant abuse it - float recentDamage = TotalDamageOverTime_BlendedOut( player, 0.5, 5.0 ) - - // damage is ramped down based on how much damage was taken recently - float damageMod = GraphCapped( recentDamage, 0, 40, 1.0, 0.35 ) - float healthGain = useIncrement * damageMod - - healthRemainingCurrent -= healthGain - int newHealth = int( min( player.GetHealth() + healthGain, player.GetMaxHealth() ) ) - player.SetHealth( newHealth ) - - nextRegenTime = Time() + 10 - } -} - -entity function GetHealthPickupPlayer( entity ent ) -{ - // try to heal the player - entity player = GetPickupPlayer( ent ) - if ( player == null ) - return null - if ( !IsPilot( player ) ) - return null - if ( player.GetHealth() >= player.GetMaxHealth() ) - return null - - return player -} - -void function CreateGrenadeAmmoPickup( entity ent ) -{ - thread DisplayTempNameText( ent, "Ammo" ) - CreatePickup( ent, GRENADE_AMMO_MODEL, GenericAmmoPickup ) - CreatePickupGlow( ent, 13, 104, 255 ) -} - -entity function CreatePickup( entity ent, asset model, bool functionref( entity ) pickupFunc ) -{ - entity mover = CreateEntity( "script_mover" ) - mover.kv.solid = 0 - mover.SetValueForModelKey( model ) - mover.SetFadeDistance( 5000 ) - mover.kv.SpawnAsPhysicsMover = 0 - mover.SetOrigin( ent.GetOrigin() ) - mover.SetAngles( ent.GetAngles() ) - DispatchSpawn( mover ) - - ent.EndSignal( "OnDestroy" ) - - mover.SetOwner( ent ) - - ent.SetParent( mover ) - thread PickupWaitsForPickup( ent, mover, pickupFunc ) - - return mover -} - -void function PickupWaitsForPickup( entity ent, entity mover, bool functionref( entity ) pickupFunc ) -{ - ent.EndSignal( "OnDestroy" ) - OnThreadEnd( - function() : ( mover, ent ) - { - if ( IsValid( mover ) ) - mover.Destroy() - if ( IsValid( ent ) ) - ent.Destroy() - } - ) - -// local colorVec = Vector(r,g,b) -// local cpoint = CreateEntity( "info_placement_helper" ) -// SetTargetName( cpoint, UniqueString( "pickup_controlpoint" ) ) -// DispatchSpawn( cpoint ) -// cpoint.SetOrigin( colorVec ) -// local glowFX = PlayFXWithControlPoint( PICKUP_GLOW_FX, mover.GetOrigin(), cpoint, null, null, null, C_PLAYFX_LOOP ) -// -// OnThreadEnd( -// function() : ( ent, mover, glowFX, cpoint ) -// { -// cpoint.Fire( "Kill", "", 1.0 ) -// if ( IsValid(glowFX) ) -// { -// glowFX.Fire( "StopPlayEndCap" ) -// glowFX.Fire( "Kill", "", 1.0 ) -// } -// mover.Destroy() -// ent.Destroy() -// } -// ) - - for ( ;; ) - { - entity player = WaitUntilPlayerPicksUp( ent ) - if ( pickupFunc( player ) ) - return - WaitFrame() - } -} - -void function HealthPickupWaitsForPickup( entity ent, HealthPickup pickup ) -{ - ent.EndSignal( "OnDestroy" ) - OnThreadEnd( - function() : ( ent ) - { - if ( IsValid( ent ) ) - ent.Destroy() - } - ) - - for ( ;; ) - { - WaitFrame() - - entity player = WaitUntilPlayerPicksUp( ent ) - if ( player.IsTitan() ) - continue - if ( player.GetHealth() >= player.GetMaxHealth() ) - continue - - thread HealPlayerOverTime( player, pickup ) - return - } -} - - -void function TitanLoadoutWaitsForPickup( entity ent, bool functionref( entity, entity ) pickupFunc ) -{ - ent.EndSignal( "OnDestroy" ) - OnThreadEnd( - function() : ( ent ) - { - if ( IsValid( ent ) ) - ent.Destroy() - } - ) - - for ( ;; ) - { - entity player = WaitUntilPlayerPicksUp( ent ) - if ( pickupFunc( player, ent ) ) - return - WaitFrame() - } -} - - -entity function GetPickupPlayer( entity ent ) -{ - array<entity> players = GetPlayerArray() - - vector entOrigin = ent.GetCenter() - - foreach ( player in players ) - { - if ( !IsAlive( player ) ) - continue - - int pickupDist - if ( player.IsTitan() ) - pickupDist = 256 * 256 - else - pickupDist = 96 * 96 - - if ( GetEditorClass( ent ) == "script_collectible" ) - pickupDist = 72 * 72 - - vector playerOrigin = player.GetOrigin() - if ( DistanceSqr( playerOrigin, entOrigin ) < pickupDist ) - { - TraceResults trace - trace = TraceLine( entOrigin, playerOrigin, [ player, ent ], TRACE_MASK_SOLID, TRACE_COLLISION_GROUP_NONE ) - if ( trace.fraction >= 0.99 || trace.hitEnt == ent ) - return player - } - } - - return null -} - -entity function WaitUntilPlayerPicksUp( entity ent ) -{ - while ( true ) - { - entity player = GetPickupPlayer( ent ) - if ( player != null ) - return player - WaitFrame() - } - - unreachable -} - -function PickupHover( mover ) -{ - mover.EndSignal( "OnDestroy" ) - - int direction = 1 - - while ( 1 ) - { - mover.MoveTo( mover.GetOrigin() + Vector( 0, 0, 20*direction ), 1, 0.4, 0.4 ) - mover.RotateTo( mover.GetAngles() + Vector( 0, 90, 0 ), 1, 0, 0 ) - direction *= -1 - wait 1 - } -} - -int function AddClipToMainWeapons( entity player ) -{ - int gainedAmmo - foreach ( weapon in player.GetMainWeapons() ) - { - gainedAmmo += AddClipToWeapon( player, weapon ) - } - return gainedAmmo -} - -int function AddTwoClipToMainWeapons( entity player ) -{ - int gainedAmmo - for ( int i = 0; i < 2; i++ ) - { - foreach ( weapon in player.GetMainWeapons() ) - { - gainedAmmo += AddClipToWeapon( player, weapon ) - } - } - return gainedAmmo -} - -int function AddRoundToOrdnance( entity player ) -{ - int gainedAmmo - entity ordnance = player.GetOffhandWeapon( OFFHAND_ORDNANCE ) - if ( IsValid( ordnance ) ) - gainedAmmo += AddRoundToWeapon( player, ordnance ) - return gainedAmmo -} - -int function AddRoundsToTactical( entity player, int count = 1 ) -{ - int gainedAmmo - entity ordnance = player.GetOffhandWeapon( OFFHAND_SPECIAL ) - if ( IsValid( ordnance ) ) - gainedAmmo += AddRoundsToWeapon( player, ordnance, count ) - return gainedAmmo -} - -int function AddClipToWeapon( entity player, entity weapon ) -{ - int ammoPerClip = weapon.GetWeaponPrimaryClipCountMax() - int gainedAmmo = 0 - - switch ( weapon.GetWeaponInfoFileKeyField( "fire_mode" ) ) - { - case "offhand_hybrid": - case "offhand": - case "offhand_instant": - - // offhand weapons typically cant store ammo, so refill the current clip - if ( ammoPerClip > 0 ) - { - int primaryClipCount = weapon.GetWeaponPrimaryClipCount() - weapon.SetWeaponPrimaryClipCount( ammoPerClip ) - gainedAmmo = weapon.GetWeaponPrimaryClipCount() - primaryClipCount - } - break - - default: - int primaryAmmoCount = weapon.GetWeaponPrimaryAmmoCount() - // this weapon has off-clip ammo storage, so add ammo to storage - int stockpile = player.GetWeaponAmmoStockpile( weapon ) - weapon.SetWeaponPrimaryAmmoCount( primaryAmmoCount + ammoPerClip ) - gainedAmmo = player.GetWeaponAmmoStockpile( weapon ) - stockpile - break - } - - return gainedAmmo -} - -int function AddRoundToWeapon( entity player, entity weapon ) -{ - return AddRoundsToWeapon( player, weapon, 1 ) -} - -int function AddRoundsToWeapon( entity player, entity weapon, int rounds ) -{ - int ammoPerClip = weapon.GetWeaponPrimaryClipCountMax() - int gainedAmmo = 0 - - switch ( weapon.GetWeaponInfoFileKeyField( "fire_mode" ) ) - { - case "offhand_hybrid": - case "offhand": - case "offhand_instant": - - // offhand weapons typically cant store ammo, so refill the current clip - if ( ammoPerClip > 0 ) - { - int primaryAmmoInClipCount = weapon.GetWeaponPrimaryClipCount() - int newAmmo = minint( ammoPerClip, primaryAmmoInClipCount + rounds ) - if ( newAmmo > primaryAmmoInClipCount ) - { - weapon.SetWeaponPrimaryClipCount( newAmmo ) - gainedAmmo = weapon.GetWeaponPrimaryClipCount() - primaryAmmoInClipCount - } - } - break - - - default: - int primaryAmmoCount = weapon.GetWeaponPrimaryAmmoCount() - // this weapon has off-clip ammo storage, so add ammo to storage - int stockpile = player.GetWeaponAmmoStockpile( weapon ) - weapon.SetWeaponPrimaryAmmoCount( primaryAmmoCount + rounds ) - gainedAmmo = player.GetWeaponAmmoStockpile( weapon ) - stockpile - break - } - - return gainedAmmo -} - -bool function GenericAmmoPickup( entity player ) -{ - if ( player.IsTitan() ) - return false - - Assert( player.IsPlayer() ) - - int gainedAmmo = 0 - foreach ( weapon in player.GetMainWeapons() ) - { - gainedAmmo += AddClipToWeapon( player, weapon ) - gainedAmmo += AddClipToWeapon( player, weapon ) - gainedAmmo += AddClipToWeapon( player, weapon ) - gainedAmmo += AddClipToWeapon( player, weapon ) - } - foreach ( weapon in player.GetOffhandWeapons() ) - { - gainedAmmo += AddClipToWeapon( player, weapon ) - } - - if ( gainedAmmo > 0 ) - { - AddPlayerScore( player, "PilotAmmoPickup" ) - EmitSoundOnEntity( player, "BurnCard_GrenadeRefill_Refill" ) - EmitSoundOnEntity( player, "titan_energyshield_up" ) - return true - } - - return false -} - - -bool function GrenadeAmmoPickedUp( entity player ) -{ - if ( player.IsTitan() ) - return false - - Assert( player.IsPlayer() ) - - entity weapon = player.GetOffhandWeapon( 0 ) - if ( !IsValid( weapon ) ) - return false - - int ammo = weapon.GetWeaponPrimaryClipCount() - int newAmmo = minint( player.GetWeaponAmmoMaxLoaded( weapon ), ammo + 2 ) - weapon.SetWeaponPrimaryClipCount( newAmmo ) - - bool pickup = newAmmo > ammo - - if ( pickup ) - { - AddPlayerScore( player, "PilotAmmoPickup" ) - EmitSoundOnEntity( player, "BurnCard_GrenadeRefill_Refill" ) - EmitSoundOnEntity( player, "titan_energyshield_up" ) - return true - } - - return false -} - -void function HealPlayerOverTime( entity player, HealthPickup pickup ) -{ - //StimPlayer( player, pickup.healTime * 2.0 ) - - Assert( IsNewThread(), "Must be threaded off" ) - Assert( player.IsPlayer() ) - //AddPlayerScore( player, "PilotHealthPickup" ) - //EmitSoundOnEntity( player, "titan_energyshield_up" ) - - // cycle old effects - //player.Signal( "NewHealthPickup" ) - //player.EndSignal( "NewHealthPickup" ) - - Assert( IsAlive( player ) ) - player.EndSignal( "OnDeath" ) - - EmitSoundOnEntity( player, pickup.pickupSound ) - EmitSoundOnEntity( player, pickup.healSound ) - - - int frames = 10 * int( pickup.healTime ) - float amount = player.GetMaxHealth() * pickup.healAmount - float healthPerFrame = amount / frames - float midTime = Time() + pickup.healTime * 0.5 - float healthRemainder = 0 - - for ( int i = 0; i < frames; i++ ) - { - WaitFrame() - - float healthThisFrame - if ( Time() < midTime ) - healthThisFrame = healthPerFrame * 0.6 - else - healthThisFrame = healthPerFrame * 1.4 - - healthRemainder += healthThisFrame % 1 - healthThisFrame -= healthThisFrame % 1 - healthThisFrame += ( healthRemainder - healthRemainder % 1 ) - healthRemainder %= 1 -// printt( "healththisframe is " + healthThisFrame + " healthRemainder is " + healthRemainder ) - - float newHealth = min( player.GetHealth() + healthThisFrame, player.GetMaxHealth() ) - player.SetHealth( newHealth ) - } - - EmitSoundOnEntity( player, pickup.endSound ) -} - -LeveledScriptedWeapons function GetAllLeveledScriptWeapons() -{ - LeveledScriptedWeapons leveledScriptedWeapons - - table<string, bool> tableAllWeapons - - foreach ( weaponName in GetAllSPWeapons() ) - { - tableAllWeapons[ weaponName ] <- true - } - - foreach ( ent in GetEntArrayByClass_Expensive( "info_target" ) ) - { - if ( !ent.HasKey( "editorclass" ) ) - continue - - string editorclass = expect string( ent.kv.editorclass ) - if ( !( editorclass in tableAllWeapons ) ) - continue - - leveledScriptedWeapons.infoTargets.append( ent ) - leveledScriptedWeapons.foundScriptWeapons[ editorclass ] <- true - } - - // legacy support - foreach ( ent in GetEntArrayByClass_Expensive( "script_ref" ) ) - { - if ( !ent.HasKey( "editorclass" ) ) - continue - if ( ent.kv.editorclass != "script_pickup_weapon" ) - continue - - Assert( ent.HasKey( "script_weapon" ) ) - string weapon = expect string( ent.kv.script_weapon ) - if ( !( weapon in tableAllWeapons ) ) - continue - - //leveledScriptedWeapons.infoTargets.append( ent ) - leveledScriptedWeapons.foundScriptWeapons[ weapon ] <- true - } - - AddSpawnCallbackEditorClass( "script_ref", "script_pickup_weapon", CreateWeaponPickup ) - - return leveledScriptedWeapons -} - -void function CreateWeaponPickup( entity ent ) -{ - Assert( ent.HasKey( "script_weapon" ) ) - string weaponClass = ent.GetValueForKey( "script_weapon" ) - - #if DEV - if ( !IsTestMap() ) - { - if ( !WeaponIsPrecached( weaponClass ) ) - { - CodeWarning( "Weapon " + weaponClass + " is not precached, re-export auto precache script" ) - return - } - - if ( !GetWeaponInfoFileKeyField_Global( weaponClass, "leveled_pickup" ) ) - { - CodeWarning( "Tried to place illegal " + weaponClass + " in leveled at " + ent.GetOrigin() ) - return - } - } - - VerifyWeaponPickupModel( ent, weaponClass ) - - if ( GetWeaponInfoFileKeyField_Global( weaponClass, "offhand_default_inventory_slot" ) == OFFHAND_LEFT ) - { - CodeWarning( "Illegal pickup " + weaponClass + " at " + ent.GetOrigin() ) - return - } - - #endif - - - bool doMarkAsLoadoutPickup = false - int loadoutIndex = GetSPTitanLoadoutIndexForWeapon( weaponClass ) - if ( loadoutIndex >= 0 ) - { - if ( IsBTLoadoutUnlocked( loadoutIndex ) ) - return - else - doMarkAsLoadoutPickup = true - } - - bool constrain = !ent.HasKey( "start_constrained" ) || ( ent.HasKey( "start_constrained" ) && ent.GetValueForKey( "start_constrained" ) == "1" ) - entity weapon - - if ( constrain ) // make all weapons constrained for now - { - weapon = CreateWeaponEntityByNameConstrained( weaponClass, ent.GetOrigin(), ent.GetAngles() ) - } - else - { - weapon = CreateWeaponEntityByNameWithPhysics( weaponClass, ent.GetOrigin(), ent.GetAngles() ) - weapon.SetVelocity( <0,0,0> ) - } - - SetTargetName( weapon, "leveled_" + weaponClass ) - if ( ent.HasKey( "fadedist" ) ) - { - weapon.kv.fadedist = ent.kv.fadedist - } - else - { - weapon.kv.fadedist = -1 - } - - ApplyWeaponModsFromEnt( ent, weapon ) - - if ( ent.HasKey( "script_name" ) ) - { - weapon.kv.script_name = ent.kv.script_name - } - - if ( doMarkAsLoadoutPickup ) - { - weapon.MarkAsLoadoutPickup() - thread CreateTitanWeaponPickupHintTrigger( weapon ) - thread TitanLoadoutWaitsForPickup( weapon, SPTitanLoadoutPickup ) - } - - HighlightWeapon( weapon ) - - // for s2s -mo - // for sp_training (pickups travel with moving gun racks) -sean - if ( ent.GetParent() ) - { - weapon.SetParent( ent.GetParent(), "", true ) - } - - // for sp_training, to replenish the weapon when it's picked up -sean - ent.e.attachedEnts.append( weapon ) -} - -void function VerifyWeaponPickupModel( entity ent, string weaponClass ) -{ - var playermodel - var playermodel1 = GetWeaponInfoFileKeyFieldAsset_Global( weaponClass, "droppedmodel" ) - var playermodel2 = GetWeaponInfoFileKeyFieldAsset_Global( weaponClass, "playermodel" ) - if ( playermodel1 != $"" ) - playermodel = playermodel1 - else - playermodel = playermodel2 - playermodel = playermodel.tolower() - expect asset( playermodel ) - - asset modelName = ent.GetModelName().tolower() - if ( modelName != $"" && playermodel != modelName ) - CodeWarning( "Incorrect Model on weapon " + weaponClass + " at " + ent.GetOrigin() + ", replace with real weapon for auto fix. ( " + modelName + " != " + playermodel + ")" ) -} - -void function ApplyWeaponModsFromEnt( entity ent, entity weapon ) -{ - if ( ent.HasKey( "script_mods" ) ) - { - array<string> mods = split( ent.GetValueForKey( "script_mods" ), " " ) - if ( mods.len() > 0 ) - { - weapon.SetMods( mods ) - return - } - } - - array<string> mods = GetWeaponModsForCurrentLevel( weapon.GetWeaponClassName() ) - if ( mods.len() ) - { - weapon.SetMods( [ mods.getrandom() ] ) - } -} - - -void function AddCollectible( entity ent ) -{ - Assert( ent.GetClassName() == "script_mover_lightweight" ) - - if ( ent.GetModelName() != HELMET_COLLECTIBLE_MODEL ) - ent.SetModel( HELMET_COLLECTIBLE_MODEL ) - - ent.DisableFastPathRendering() // Workaround for glow effect not drawing (bug #177177) - - Collectible collectible - collectible.ent = ent - collectible.pos = ent.GetOrigin() - file.collectibles.append( collectible ) - - // Drop to ground - if ( !collectible.ent.HasKey( "hover" ) || collectible.ent.kv.hover == "0" ) - { - vector groundPos = OriginToGround( collectible.ent.GetOrigin() + <0,0,1> ) - collectible.ent.SetOrigin( groundPos + < 0, 0, 32 > ) - collectible.pos = collectible.ent.GetOrigin() - } - - // Effect and not solid - collectible.ent.DisableHibernation() - collectible.ent.NotSolid() - collectible.ent.EnableRenderAlways() - collectible.ent.kv.fadedist = 100000 -} - -void function SetupCollectibles() -{ - // Make sure that the number of collectibles in the level matches the hardcoded value in sh_consts so that SP menus know total number per level. - string mapName = GetMapName() - int saveIndex = GetCollectibleLevelIndex( mapName ) - Assert( saveIndex < 0 || file.collectibles.len() == GetMaxLionsInLevel( mapName ), "Collectibles count mismatch. Update LEVEL_UNLOCKS_COUNT in sh_consts.gnut to " + file.collectibles.len() ) - - // Index the collectibles so each is unique and it's status can be stored in a cvar. They are sorted by distance from map center to keep consistent on each map load - file.collectibles.sort( SortCollectiblesFunc ) - - foreach ( int i, Collectible collectible in file.collectibles ) - { - collectible.id = 1 << i - thread CollectibleThink( collectible ) - } -} - -int function SortCollectiblesFunc( Collectible a, Collectible b ) -{ - float distA = DistanceSqr( a.ent.GetOrigin(), <0,0,0> ) - float distB = DistanceSqr( b.ent.GetOrigin(), <0,0,0> ) - if ( distA > distB ) - return 1 - else if ( distA < distB ) - return -1 - return 0 -} - -void function UpdateCollectiblesAfterLoadSaveGame() -{ - // This has to run when a save game is loaded because the collectible may be there in the save game, but it was picked up after the save, so we need to delete the ones the player picked up - foreach ( Collectible collectible in file.collectibles ) - { - if ( HasCollectible( collectible ) ) - { - //DebugDrawSphere( collectible.pos, 40.0, 255, 0, 0, true, 600.0 ) - if ( IsValid( collectible.ent ) ) - collectible.ent.Destroy() - Signal( collectible, "CollectibleEndThink" ) - } - } - - // Delete all the weapons already unlocked in the level - array<string> unlockedLoadouts = GetSPTitanLoadoutsUnlocked() - SPTitanLoadout_RemoveOwnedLoadoutPickupsInLevel( unlockedLoadouts ) -} - -#if DEV - void function Dev_ResetCollectiblesProgress_Level() - { - printt( "RESETTING COLLECTIBLE PROGRESS (LEVEL)" ) - string mapName = GetMapName() - int saveIndex = GetCollectibleLevelIndex( mapName ) - if ( saveIndex == -1 ) - return - printt( " sp_unlocks_level_" + saveIndex, 0 ) - SetConVarInt( "sp_unlocks_level_" + saveIndex, 0 ) - } -#endif - -void function CollectibleThink( Collectible collectible ) -{ - EndSignal( collectible, "CollectibleEndThink" ) - - if ( HasCollectible( collectible ) ) - { - //printt( "Player already has collectible", collectible.id ) - //DebugDrawSphere( collectible.ent.GetOrigin(), 25.0, 150, 0, 0, true, 600.0 ) - collectible.ent.Destroy() - return - } - - entity glowEffect = StartParticleEffectOnEntity_ReturnEntity( collectible.ent, GetParticleSystemIndex( COLLECTIBLE_GLOW_EFFECT ), FX_PATTACH_ABSORIGIN_FOLLOW, 0 ) - - // Rotate the collectible - collectible.ent.NonPhysicsRotate( < 0, 0, 1 >, 35.0 ) - - WaitFrame() // emit sound doesn't work on first frame so we have to wait a frame so sound will play. Player can't pickup collectible in frame 1 anyways - - EmitSoundOnEntity( collectible.ent, "Emit_PilotHelmet_Collectible" ) - - //wait 1.0 - //DebugDrawText( collectible.ent.GetOrigin(), string(collectible.id), true, 600.0 ) - //DebugDrawSphere( collectible.ent.GetOrigin(), 25.0, 255, 0, 0, true, 600.0 ) - - // Wait until it's touched by a player - entity player = WaitUntilPlayerPicksUp( collectible.ent ) - - // Remove collectible - EmitSoundOnEntity( player, "Pilot_Collectible_Pickup" ) - if ( IsValid( glowEffect ) ) - EffectStop( glowEffect ) - collectible.ent.Destroy() - - // Save to player profile - string mapName = GetMapName() - int saveIndex = GetCollectibleLevelIndex( mapName ) - int bitMask - if ( saveIndex >= 0 ) - { - // If it's a real map we store it to player profile - string unlockVar = "sp_unlocks_level_" + saveIndex - bitMask = GetConVarInt( unlockVar ) - bitMask = bitMask | collectible.id - //printt( "Saving collectible state", unlockVar, bitMask ) - SetConVarInt( unlockVar, bitMask ) - } - else - { - // Not a real map, we store it to a file var that wont persist, just so we can pick them up and have kind of working collectibles in test maps - CodeWarning( "Collectible state not being saved because this map is not shipping" ) - file.testMapCollectibleValue = file.testMapCollectibleValue | collectible.id - bitMask = file.testMapCollectibleValue - } - - // See how many collectibles are found now to pass to the RUI - int numCollectiblesFound = GetCollectiblesFoundForLevel( mapName ) - int maxCollectibles = GetMaxLionsInLevel( mapName ) - - // Show message on HUD - Remote_CallFunction_NonReplay( player, "ServerCallback_CollectibleFoundMessage", numCollectiblesFound, maxCollectibles ) - - CollectiblePickupRumble( player ) - - UpdateHeroStatsForPlayer( player ) - - int totalLionsCollectedForGame = GetTotalLionsCollected() - - if ( totalLionsCollectedForGame >= GetTotalLionsInGame() ) - UnlockAchievement( player, achievements.COLLECTIBLES_3 ) - - if ( totalLionsCollectedForGame >= ACHIEVEMENT_COLLECTIBLES_2_COUNT ) - UnlockAchievement( player, achievements.COLLECTIBLES_2 ) - - if ( totalLionsCollectedForGame >= ACHIEVEMENT_COLLECTIBLES_1_COUNT ) - UnlockAchievement( player, achievements.COLLECTIBLES_1 ) -} - -void function CollectiblePickupRumble( entity player ) -{ - float rumbleAmplitude = 200.0 - float rumbleFrequency = 90.0 - float rumbleDuration = 2.2 - - CreateAirShakeRumbleOnly( player.GetOrigin(), rumbleAmplitude, rumbleFrequency, rumbleDuration ) -} - -bool function HasCollectible( Collectible collectible ) -{ - string mapName = GetMapName() - int saveIndex = GetCollectibleLevelIndex( mapName ) - - // Not a shipping map, so there is no saved var for this level. Just always make it available - if ( saveIndex == -1 ) - return false - - string unlockVar = "sp_unlocks_level_" + saveIndex - int bitMask = GetConVarInt( unlockVar ) - - return bool(bitMask & collectible.id) -} - - - - - - - - - diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/levels/_lf_maps_shared.gnut b/Northstar.CustomServers/mod/scripts/vscripts/mp/levels/_lf_maps_shared.gnut index 178b6560..695f096d 100644 --- a/Northstar.CustomServers/mod/scripts/vscripts/mp/levels/_lf_maps_shared.gnut +++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/levels/_lf_maps_shared.gnut @@ -5,4 +5,5 @@ void function SetupLiveFireMaps() { Riff_ForceTitanAvailability( eTitanAvailability.Never ) ClassicMP_SetLevelIntro( ClassicMP_DefaultNoIntro_Setup, ClassicMP_DefaultNoIntro_GetLength() ) + ClassicMP_ForceDisableEpilogue( true ) }
\ No newline at end of file diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/spawn.nut b/Northstar.CustomServers/mod/scripts/vscripts/mp/spawn.nut index 61fabb3c..b72e660e 100644 --- a/Northstar.CustomServers/mod/scripts/vscripts/mp/spawn.nut +++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/spawn.nut @@ -3,7 +3,6 @@ untyped global function InitRatings // temp for testing global function Spawn_Init -global function SetSpawnsUseFrontline global function SetRespawnsEnabled global function RespawnsEnabled global function SetSpawnpointGamemodeOverride @@ -11,7 +10,6 @@ global function GetSpawnpointGamemodeOverride global function CreateNoSpawnArea global function DeleteNoSpawnArea -global function GetCurrentFrontline global function FindSpawnPoint global function RateSpawnpoints_Generic @@ -32,11 +30,7 @@ struct { table<string, NoSpawnArea> noSpawnAreas - bool frontlineBased = false - float lastImcFrontlineRatingTime - float lastMilitiaFrontlineRatingTime - Frontline& lastImcFrontline - Frontline& lastMilitiaFrontline + array<vector> preferSpawnNodes } file void function Spawn_Init() @@ -45,6 +39,25 @@ void function Spawn_Init() AddSpawnCallback( "info_spawnpoint_human_start", InitSpawnpoint ) AddSpawnCallback( "info_spawnpoint_titan", InitSpawnpoint ) AddSpawnCallback( "info_spawnpoint_titan_start", InitSpawnpoint ) + + AddCallback_EntitiesDidLoad( InitPreferSpawnNodes ) +} + +void function InitPreferSpawnNodes() +{ + foreach ( entity hardpoint in GetEntArrayByClass_Expensive( "info_hardpoint" ) ) + { + if ( !hardpoint.HasKey( "hardpointGroup" ) ) + continue + + if ( hardpoint.kv.hardpointGroup != "A" && hardpoint.kv.hardpointGroup != "B" && hardpoint.kv.hardpointGroup != "C" ) + continue + + file.preferSpawnNodes.append( hardpoint.GetOrigin() ) + } + + //foreach ( entity frontline in GetEntArrayByClass_Expensive( "info_frontline" ) ) + // file.preferSpawnNodes.append( frontline.GetOrigin() ) } void function InitSpawnpoint( entity spawnpoint ) @@ -106,63 +119,10 @@ string function GetSpawnpointGamemodeOverride() unreachable } -void function SetSpawnsUseFrontline( bool useFrontline ) -{ - file.frontlineBased = useFrontline -} - -bool function InitRatings( entity player, int team ) +void function InitRatings( entity player, int team ) { - Frontline frontline = GetCurrentFrontline( team ) - print( team ) - print( frontline.friendlyCenter ) - - vector offsetOrigin = frontline.friendlyCenter + frontline.combatDir * 256 - SpawnPoints_InitFrontlineData( offsetOrigin, frontline.combatDir, frontline.line, frontline.friendlyCenter, 2.0 ) // temp - if ( player != null ) SpawnPoints_InitRatings( player, team ) // no idea what the second arg supposed to be lol - - return frontline.friendlyCenter == < 0, 0, 0 > && file.frontlineBased // if true, use startspawns -} - -// this sucks -Frontline function GetCurrentFrontline( int team ) -{ - float lastFrontlineRatingTime - Frontline lastFrontline - if ( team == TEAM_IMC ) - { - lastFrontline = file.lastImcFrontline - lastFrontlineRatingTime = file.lastImcFrontlineRatingTime - } - else - { - lastFrontline = file.lastMilitiaFrontline - lastFrontlineRatingTime = file.lastMilitiaFrontlineRatingTime - } - - // current frontline is old, get a new one - if ( lastFrontlineRatingTime + 20.0 < Time() || lastFrontline.friendlyCenter == < 0, 0, 0 > ) - { - print( "rerating frontline..." ) - Frontline frontline = GetFrontline( team ) - - if ( team == TEAM_IMC ) - { - file.lastImcFrontlineRatingTime = Time() - file.lastImcFrontline = frontline - } - else - { - file.lastMilitiaFrontlineRatingTime = Time() - file.lastMilitiaFrontline = frontline - } - - lastFrontline = frontline - } - - return lastFrontline } entity function FindSpawnPoint( entity player, bool isTitan, bool useStartSpawnpoint ) @@ -171,15 +131,14 @@ entity function FindSpawnPoint( entity player, bool isTitan, bool useStartSpawnp if ( HasSwitchedSides() ) team = GetOtherTeam( team ) - useStartSpawnpoint = InitRatings( player, player.GetTeam() ) || useStartSpawnpoint // force startspawns if no frontline - print( "useStartSpawnpoint: " + useStartSpawnpoint ) - array<entity> spawnpoints if ( useStartSpawnpoint ) spawnpoints = isTitan ? SpawnPoints_GetTitanStart( team ) : SpawnPoints_GetPilotStart( team ) else spawnpoints = isTitan ? SpawnPoints_GetTitan() : SpawnPoints_GetPilot() + InitRatings( player, team ) + void functionref( int, array<entity>, int, entity ) ratingFunc = isTitan ? GameMode_GetTitanSpawnpointsRatingFunc( GAMETYPE ) : GameMode_GetPilotSpawnpointsRatingFunc( GAMETYPE ) ratingFunc( isTitan ? TD_TITAN : TD_PILOT, spawnpoints, team, player ) @@ -238,6 +197,19 @@ entity function GetBestSpawnpoint( entity player, array<entity> spawnpoints ) } } + // last resort + if ( validSpawns.len() == 0 ) + { + print( "map has literally 0 spawnpoints, as such everything is fucked probably, attempting to use info_player_start if present" ) + entity start = GetEnt( "info_player_start" ) + + if ( IsValid( start ) ) + { + start.s.lastUsedTime <- -999 + validSpawns.append( start ) + } + } + return validSpawns[ RandomInt( validSpawns.len() ) ] // slightly randomize it } @@ -268,6 +240,9 @@ bool function IsSpawnpointValid( entity spawnpoint, int team ) if ( spawnpoint.IsOccupied() ) return false + if ( Time() - spawnpoint.s.lastUsedTime <= 1.0 ) + return false + foreach ( k, NoSpawnArea noSpawnArea in file.noSpawnAreas ) { if ( Distance( noSpawnArea.position, spawnpoint.GetOrigin() ) > noSpawnArea.radius ) @@ -309,9 +284,6 @@ bool function IsSpawnpointValid( entity spawnpoint, int team ) if ( TraceLineSimple( enemyPlayer.EyePosition(), spawnpoint.GetOrigin() + < 0, 0, 48 >, enemyPlayer ) == 1.0 ) return false } - - if ( Time() - spawnpoint.s.lastUsedTime <= 1.0 ) - return false return true } @@ -323,4 +295,86 @@ void function RateSpawnpoints_Generic( int checkClass, array<entity> spawnpoints // realistically, this should spawn people fairly spread out across the map, preferring being closer to their startspawns // should spawn like, near fights, but not in them + + + // using old func for tests rq + // calculate ratings for preferred nodes + // this tries to prefer nodes with more teammates, then activity on them + // todo: in the future it might be good to have this prefer nodes with enemies up to a limit of some sort + // especially in ffa modes i could deffo see this falling apart a bit rn + // perhaps dead players could be used to calculate some sort of activity rating? so high-activity points with an even balance of friendly/unfriendly players are preferred + array<float> preferSpawnNodeRatings + foreach ( vector preferSpawnNode in file.preferSpawnNodes ) + { + float currentRating + + // this seems weird, not using rn + //Frontline currentFrontline = GetCurrentFrontline( team ) + //if ( !IsFFAGame() || currentFrontline.friendlyCenter != < 0, 0, 0 > ) + // currentRating += max( 0.0, ( 1000.0 - Distance2D( currentFrontline.origin, preferSpawnNode ) ) / 200 ) + + foreach ( entity nodePlayer in GetPlayerArray() ) + { + float currentChange = 0.0 + + // the closer a player is to a node the more they matter + float dist = Distance2D( preferSpawnNode, nodePlayer.GetOrigin() ) + if ( dist > 600.0 ) + continue + + currentChange = ( 600.0 - dist ) / 5 + if ( player == nodePlayer ) + currentChange *= -3 // always try to stay away from places we've already spawned + else if ( !IsAlive( nodePlayer ) ) // dead players mean activity which is good, but they're also dead so they don't matter as much as living ones + currentChange *= 0.6 + if ( nodePlayer.GetTeam() != player.GetTeam() ) // if someone isn't on our team and alive they're probably bad + { + if ( IsFFAGame() ) // in ffa everyone is on different teams, so this isn't such a big deal + currentChange *= -0.2 + else + currentChange *= -0.6 + } + + currentRating += currentChange + } + + preferSpawnNodeRatings.append( currentRating ) + } + + foreach ( entity spawnpoint in spawnpoints ) + { + float currentRating + float petTitanModifier + // scale how much a given spawnpoint matters to us based on how far it is from each node + bool spawnHasRecievedInitialBonus = false + for ( int i = 0; i < file.preferSpawnNodes.len(); i++ ) + { + // bonus if autotitan is nearish + if ( IsAlive( player.GetPetTitan() ) && Distance( player.GetPetTitan().GetOrigin(), file.preferSpawnNodes[ i ] ) < 1200.0 ) + petTitanModifier += 10.0 + + float dist = Distance2D( spawnpoint.GetOrigin(), file.preferSpawnNodes[ i ] ) + if ( dist > 750.0 ) + continue + + if ( dist < 600.0 && !spawnHasRecievedInitialBonus ) + { + currentRating += 10.0 + spawnHasRecievedInitialBonus = true // should only get a bonus for simply being by a node once to avoid over-rating + } + + currentRating += ( preferSpawnNodeRatings[ i ] * ( ( 750.0 - dist ) / 75 ) ) + max( RandomFloat( 1.25 ), 0.9 ) + if ( dist < 250.0 ) // shouldn't get TOO close to an active node + currentRating *= 0.7 + + if ( spawnpoint.s.lastUsedTime < 10.0 ) + currentRating *= 0.7 + } + + float rating = spawnpoint.CalculateRating( checkClass, team, currentRating, currentRating + petTitanModifier ) + //print( "spawnpoint at " + spawnpoint.GetOrigin() + " has rating: " + ) + + if ( rating != 0.0 || currentRating != 0.0 ) + print( "rating = " + rating + ", internal rating = " + currentRating ) + } }
\ No newline at end of file |