diff options
author | Will Castro <39478251+VITALISED@users.noreply.github.com> | 2022-01-19 17:09:54 +1100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-01-19 17:09:54 +1100 |
commit | 9693b43e54659cf6049276ab66ca7cd59415460a (patch) | |
tree | e1f3821c3242dba9833c688855abb5a74e6f606d | |
parent | 2b74dda6be49895652d02613d6cdd7093f8c6c45 (diff) | |
parent | 72d1ab3492ea20ffb75a4d627b09bedd26df41a6 (diff) | |
download | NorthstarMods-9693b43e54659cf6049276ab66ca7cd59415460a.tar.gz NorthstarMods-9693b43e54659cf6049276ab66ca7cd59415460a.zip |
Merge branch 'main' into attributes-localisation
9 files changed, 1266 insertions, 308 deletions
diff --git a/Northstar.Client/mod/resource/northstar_client_localisation_english.txt b/Northstar.Client/mod/resource/northstar_client_localisation_english.txt index 89ecc5fb..96e4f04e 100644 --- a/Northstar.Client/mod/resource/northstar_client_localisation_english.txt +++ b/Northstar.Client/mod/resource/northstar_client_localisation_english.txt @@ -4,7 +4,7 @@ "Tokens" { // This file needs to be encoded as UTF-16 LE - // test BOM utf fix + "MENU_LAUNCH_NORTHSTAR" "Launch Northstar" "MENU_TITLE_MODS" "Mods" "RELOAD_MODS" "Reload Mods" @@ -288,4 +288,4 @@ Press Yes if you agree to this. This choice can be changed in the mods menu at a "INGAME_PLAYERS" "Players:" "TOTAL_SERVERS" "Servers:" } -} +}
\ No newline at end of file diff --git a/Northstar.Client/mod/scripts/vscripts/client/rui/cl_weapon_status.gnut b/Northstar.Client/mod/scripts/vscripts/client/rui/cl_weapon_status.gnut new file mode 100644 index 00000000..26f5efd8 --- /dev/null +++ b/Northstar.Client/mod/scripts/vscripts/client/rui/cl_weapon_status.gnut @@ -0,0 +1,860 @@ +global function ClWeaponStatus_Init +global function ClWeaponStatus_SetOffhandVisible +global function ClWeaponStatus_SetWeaponVisible +global function ClWeaponStatus_GetWeaponHudRui +global function ClWeaponStatus_RefreshWeaponStatus + +struct +{ + var ammo_status_hint + var ability_left_hud + var ability_center_hud + var ability_right_hud + var dpad_left_hud + var ammo_counter + + bool[6] slotVisible = [true, true, true, true, true, true] + bool ammo_counter_visible = true + + int lastSelectedIndex + + entity lastCenterWeapon +} file + +void function ClWeaponStatus_Init() +{ + AddCallback_OnClientScriptInit( ClWeaponStatus_AddClient ) + AddCallback_OnSelectedWeaponChanged( OnSelectedWeaponChanged ) + + AddCallback_OnPlayerLifeStateChanged( OnLifeStateChanged ) + AddCallback_PlayerClassChanged( OnPlayerClassChanged ) + + AddCallback_KillReplayEnded( OnKillReplayEnded ) + + RegisterSignal( "EndTrackOffhandWeaponSlot" ) + RegisterSignal( "EndTrackCenterStuff" ) +} + +void function ClWeaponStatus_RefreshWeaponStatus( entity player ) +{ + if ( !IsValid( player ) ) + return + + UpdatePrimaryWeaponHint() + + if ( !IsValid( GetLocalViewPlayer() ) ) + return + + InitWeaponStatusRuis( GetLocalViewPlayer() ) +} + +var function ClWeaponStatus_GetWeaponHudRui( entity player, entity weapon ) +{ + var index = weapon.GetWeaponInfoFileKeyField( "offhand_default_inventory_slot" ) + + if ( index == null ) + return file.ammo_counter + + expect int( index ) + + return GetRuiForIndex( player, index ) +} + +void function ClWeaponStatus_AddClient( entity player ) +{ + { + var rui = CreateCockpitRui( $"ui/ammo_status_hint.rpak" ) + RuiTrackFloat( rui, "ammoFrac", GetLocalClientPlayer(), RUI_TRACK_WEAPON_CLIP_AMMO_FRACTION ) + RuiTrackFloat( rui, "remainingAmmoFrac", GetLocalClientPlayer(), RUI_TRACK_WEAPON_REMAINING_AMMO_FRACTION ) + RuiTrackFloat( rui, "readyToFireFrac", GetLocalClientPlayer(), RUI_TRACK_WEAPON_READY_TO_FIRE_FRACTION ) + RuiTrackFloat( rui, "reloadingFrac", GetLocalClientPlayer(), RUI_TRACK_WEAPON_RELOAD_FRACTION ) + + file.ammo_status_hint = rui + } + + { + var rui = CreateCockpitRui( $"ui/ability_hud.rpak" ) + RuiSetInt( rui, "xPos", 0 ) + file.ability_left_hud = rui + } + + { + var rui = CreateCockpitRui( $"ui/ability_hud.rpak" ) + RuiSetInt( rui, "xPos", 1 ) + file.ability_center_hud = rui + } + + { + var rui = CreateCockpitRui( $"ui/ability_hud.rpak" ) + RuiSetInt( rui, "xPos", 2 ) + file.ability_right_hud = rui + } + + { + var rui = CreateCockpitRui( $"ui/inventory_hud.rpak" ) + RuiSetInt( rui, "xPos", 1 ) + RuiSetInt( rui, "yPos", 1 ) + file.dpad_left_hud = rui + } + + { + var rui = CreateCockpitRui( $"ui/ammo_counter.rpak" ) + file.ammo_counter = rui + } +} + + +void function OnSelectedWeaponChanged( entity selectedWeapon ) +{ + if ( !IsValid( selectedWeapon ) ) + { + RuiSetFloat( file.ammo_status_hint, "lowAmmoFrac", 0.0 ) + RuiSetBool( file.ammo_counter, "isVisible", false ) + return + } + + if ( GetLocalViewPlayer().PlayerMelee_IsAttackActive() ) + return + + if ( GetLocalViewPlayer().IsUsingOffhandWeapon() ) + return + + asset primaryIcon = $"" + asset secondaryIcon = $"" + asset tertiaryIcon = $"" + array<entity> mainWeapons = GetLocalViewPlayer().GetMainWeapons() + #if MP + int activeIndex + foreach ( index, weapon in mainWeapons ) + { + asset hudIcon = GetWeaponInfoFileKeyFieldAsset_WithMods_Global( weapon.GetWeaponClassName(), weapon.GetMods(), "hud_icon" ) + if ( index == 0 ) + primaryIcon = hudIcon + else if ( index == 1 ) + secondaryIcon = hudIcon + else if ( index == 2 ) + tertiaryIcon = hudIcon + + if ( weapon == selectedWeapon ) + activeIndex = index + } + + if ( activeIndex == 0 ) + { + RuiSetImage( file.ammo_counter, "tertiaryHudIcon", tertiaryIcon ) + RuiSetImage( file.ammo_counter, "secondaryHudIcon", secondaryIcon ) + RuiSetGameTime( file.ammo_counter, "tapPingTime", Time() ) + } + else if ( activeIndex == 1 ) + { + RuiSetImage( file.ammo_counter, "tertiaryHudIcon", tertiaryIcon ) + RuiSetImage( file.ammo_counter, "secondaryHudIcon", primaryIcon ) + RuiSetGameTime( file.ammo_counter, "tapPingTime", Time() ) + } + else if ( activeIndex == 2 ) + { + if ( file.lastSelectedIndex == 0 ) + { + RuiSetImage( file.ammo_counter, "tertiaryHudIcon", $"" ) + RuiSetImage( file.ammo_counter, "secondaryHudIcon", primaryIcon ) + } + else if ( file.lastSelectedIndex == 1 ) + { + RuiSetImage( file.ammo_counter, "tertiaryHudIcon", $"" ) + RuiSetImage( file.ammo_counter, "secondaryHudIcon", secondaryIcon ) + } + } + + file.lastSelectedIndex = activeIndex + #else + foreach ( weapon in mainWeapons ) + { + if ( weapon == selectedWeapon ) + continue + + if ( selectedWeapon.GetWeaponType() == WT_ANTITITAN ) + continue + + if ( weapon.GetWeaponType() == WT_ANTITITAN ) + continue + + if ( GetLocalViewPlayer().IsTitan() ) + continue + + asset hudIcon = GetWeaponInfoFileKeyFieldAsset_WithMods_Global( weapon.GetWeaponClassName(), weapon.GetMods(), "hud_icon" ) + secondaryIcon = hudIcon + } + RuiSetImage( file.ammo_counter, "secondaryHudIcon", secondaryIcon ) + RuiSetImage( file.ammo_counter, "tertiaryHudIcon", $"" ) + #endif + + InitAmmoCounterRui( file.ammo_counter, GetLocalViewPlayer(), selectedWeapon ) + + float lowAmmoFrac = selectedWeapon.GetWeaponSettingFloat( eWeaponVar.low_ammo_fraction ) + RuiSetFloat( file.ammo_status_hint, "lowAmmoFrac", lowAmmoFrac ) + RuiSetBool( file.ammo_counter, "isVisible", file.ammo_counter_visible ) + RuiSetBool( file.ammo_counter, "isTitan", CheckCenterSlotOccupied(GetLocalViewPlayer()) ) + + RuiSetBool( file.ammo_counter, "isWeaponAmped", selectedWeapon.GetWeaponSettingBool( eWeaponVar.is_burn_mod ) ) + + UpdatePrimaryWeaponHint() +} + +void function UpdatePrimaryWeaponHint() +{ + // Show/Hide the BT loudout switch button hint if single player + if ( IsSingleplayer() && GetLocalViewPlayer().IsTitan() && GetConVarInt( "hud_setting_showButtonHints" ) != 0 ) + RuiSetString( file.ammo_counter, "hintText", Localize( "#HUD_SP_BT_LOADOUT_SWAP" ) ) + else + RuiSetString( file.ammo_counter, "hintText", "" ) +} + + +void function InitAmmoCounterRui( var rui, entity player, entity weapon ) +{ + RuiTrackFloat( rui, "maxMagAmmo", player, RUI_TRACK_WEAPON_CLIP_AMMO_MAX ) + RuiTrackFloat( rui, "maxStockpileAmmo", player, RUI_TRACK_WEAPON_STOCKPILE_AMMO_MAX ) + RuiTrackFloat( rui, "clipAmmoFrac", player, RUI_TRACK_WEAPON_CLIP_AMMO_FRACTION ) + RuiTrackFloat( rui, "remainingAmmoFrac", player, RUI_TRACK_WEAPON_REMAINING_AMMO_FRACTION ) + RuiTrackFloat( rui, "lifetimeShots", player, RUI_TRACK_WEAPON_LIFETIME_SHOTS ) + RuiTrackFloat( rui, "ammoRegenRate", player, RUI_TRACK_WEAPON_AMMO_REGEN_RATE ) + + RuiTrackImage( rui, "hudIcon", player, RUI_TRACK_WEAPON_HUD_ICON ) +} + + +void function OnPlayerClassChanged( entity player ) +{ + if ( player != GetLocalViewPlayer() ) + return + + InitWeaponStatusRuis( player ) +} + + +void function OnLifeStateChanged( entity player, int oldLifeState, int newLifeState ) +{ + if ( player != GetLocalViewPlayer() ) + return + + if ( newLifeState != LIFE_ALIVE ) + return + + InitWeaponStatusRuis( player ) +} + +void function OnKillReplayEnded() +{ + entity player = GetLocalViewPlayer() + + InitWeaponStatusRuis( player ) +} + +void function UpdateOffhandRuis( entity player ) +{ + UpdateOffhandRuiVisibility( file.ability_left_hud, "%offhand1%" ) + + if ( CheckCenterSlotOccupied(player) ) + UpdateOffhandRuiVisibility( file.ability_center_hud, "%offhand2%" ) + else + UpdateOffhandRuiVisibility( file.ability_center_hud, "%offhand0%" ) + + UpdateOffhandRuiVisibility( file.ability_right_hud, "%offhand0%" ) + + if ( IsMultiplayer() ) + { + // need to recreate this since RuiTrackInt cannot be undone with RuiSetInt + RuiDestroy( file.dpad_left_hud ) + var rui = CreateCockpitRui( $"ui/inventory_hud.rpak" ) + RuiSetInt( rui, "xPos", 1 ) + RuiSetInt( rui, "yPos", 1 ) + file.dpad_left_hud = rui + UpdateOffhandRuiVisibility( file.dpad_left_hud, "%offhand4%" ) + } + else + { + UpdateOffhandRuiVisibility( file.dpad_left_hud, "%weaponSelectOrdnance%" ) + } +} + +void function InitWeaponStatusRuis( entity player ) +{ + player.Signal( "EndTrackOffhandWeaponSlot" ) + + UpdateOffhandRuis( player ) + + if ( CheckCenterSlotOccupied(player) ) + { + thread TrackOffhandWeaponSlot( player, file.ability_left_hud, OFFHAND_LEFT ) + thread TrackOffhandWeaponSlot( player, file.ability_center_hud, OFFHAND_TITAN_CENTER ) + thread TrackOffhandWeaponSlot( player, file.ability_right_hud, OFFHAND_RIGHT ) + if ( IsMultiplayer() ) + thread TrackOffhandWeaponSlot( player, file.dpad_left_hud, OFFHAND_INVENTORY ) + } + else + { + thread TrackOffhandWeaponSlot( player, file.ability_left_hud, OFFHAND_LEFT ) + thread TrackOffhandWeaponSlot( player, file.ability_center_hud, OFFHAND_RIGHT ) + if ( IsMultiplayer() ) + { + thread TrackOffhandWeaponSlot( player, file.dpad_left_hud, OFFHAND_INVENTORY ) + //thread TrackHoldToSwapSlot( player, file.ammo_counter ) + } + else + { + thread TrackATWeaponSlot( player, file.dpad_left_hud ) + } + RuiSetBool( file.ability_right_hud, "isVisible", false ) + } + + thread TrackCenterSlot( player ) +} + +void function UpdateOffhandRuiVisibility( var rui, string hintText ) +{ + if ( GetConVarInt( "hud_setting_showButtonHints" ) != 0 ) + RuiSetString( rui, "hintText", hintText ) + else + RuiSetString( rui, "hintText", "" ) +} + +void function UpdateForChangedCenter( entity player ) +{ + player.Signal( "EndTrackCenterStuff" ) + + bool centerOccupied = CheckCenterSlotOccupied(player) + + if ( centerOccupied ) + { + UpdateOffhandRuiVisibility( file.ability_center_hud, "%offhand2%" ) + + thread TrackOffhandWeaponSlot( player, file.ability_center_hud, OFFHAND_TITAN_CENTER ) + thread TrackOffhandWeaponSlot( player, file.ability_right_hud, OFFHAND_RIGHT ) + } + else + { + UpdateOffhandRuiVisibility( file.ability_center_hud, "%offhand0%" ) + + thread TrackOffhandWeaponSlot( player, file.ability_center_hud, OFFHAND_RIGHT ) + } + + RuiSetBool( file.ammo_counter, "isTitan", centerOccupied ) +} + +void function TrackCenterSlot( entity player ) +{ + player.EndSignal( "EndTrackOffhandWeaponSlot" ) + player.EndSignal( "OnDeath" ) + + entity lastWeapon = null + while ( IsAlive( player ) ) + { + entity activeWeapon = player.GetOffhandWeapon( OFFHAND_TITAN_CENTER ) + if ( activeWeapon != lastWeapon ) + { + UpdateForChangedCenter( player ) + } + + lastWeapon = activeWeapon + WaitFrame() + } +} + +void function TrackHoldToSwapSlot( entity player, var rui ) +{ + player.EndSignal( "EndTrackOffhandWeaponSlot" ) + player.EndSignal( "OnDeath" ) + + OnThreadEnd( + function() : ( rui ) + { + //RuiSetBool( rui, "isVisible", false ) + } + ) + + float holdWeaponSwapTime = GetConVarFloat( "holdWeaponSwapTime" ) + RuiSetFloat( rui, "holdWeaponSwapTime", holdWeaponSwapTime ) + RuiSetFloat( rui, "holdTime", 0.0 ) + RuiSetBool( rui, "holdHintVisible", false ) + + entity lastWeapon = null + bool holdWeaponActive = false + bool lastIsHolding = false + float holdChangeTime = 0.0 + while ( IsAlive( player ) ) + { + entity activeWeapon = player.GetActiveWeapon() + if ( activeWeapon != lastWeapon ) + { + array<entity> mainWeapons = player.GetMainWeapons() + if ( mainWeapons.len() > 2 ) + { + if ( mainWeapons[2] == activeWeapon ) + { + holdWeaponActive = true + } + else + { + holdWeaponActive = false + } + } + } + + if ( !holdWeaponActive ) + { + bool isHolding = player.IsInputCommandHeld( IN_WEAPON_CYCLE ) + if ( isHolding != lastIsHolding ) + holdChangeTime = Time() + + if ( isHolding ) + { + RuiSetFloat( rui, "holdTime", Time() - holdChangeTime ) + } + else + { + RuiSetFloat( rui, "holdTime", 0.0 ) + } + lastIsHolding = isHolding + } + else + { + RuiSetFloat( rui, "holdTime", 0.0 ) + } + RuiSetBool( rui, "holdHintVisible", IsControllerModeActive() ) + + lastWeapon = activeWeapon + WaitFrame() + } +} + +void function TrackOffhandWeaponSlot( entity player, var rui, int slot ) +{ + if ( slot == OFFHAND_TITAN_CENTER || slot == OFFHAND_RIGHT ) + { + player.EndSignal( "EndTrackCenterStuff" ) + } + + player.EndSignal( "EndTrackOffhandWeaponSlot" ) + player.EndSignal( "OnDeath" ) + + OnThreadEnd( + function() : ( rui ) + { + RuiSetBool( rui, "isVisible", false ) + } + ) + + entity lastWeapon = null + bool wasVisible = file.slotVisible[slot] + while ( IsAlive( player ) ) + { + entity weapon = player.GetOffhandWeapon( slot ) + if ( weapon != lastWeapon || file.slotVisible[slot] != wasVisible ) + { + if ( IsValid( weapon ) && file.slotVisible[slot] ) + { + InitOffhandRui( rui, player, weapon ) + #if MP + if ( slot == OFFHAND_INVENTORY ) + { + if ( player.IsTitan() ) + { + int segments = player.GetWeaponAmmoStockpile( player.GetOffhandWeapon( OFFHAND_INVENTORY ) ) + segments += player.GetOffhandWeapon( OFFHAND_INVENTORY ).GetWeaponPrimaryClipCount() + RuiSetInt( rui, "segments", segments ) + RuiSetFloat( rui, "minFireFrac", 0.01 ) + } + else + { + //RuiSetInt( rui, "segments", PlayerInventory_ItemCount( player ) ) + RuiTrackInt( rui, "segments", player, RUI_TRACK_SCRIPT_NETWORK_VAR_INT, GetNetworkedVariableIndex( "itemInventoryCount" ) ) + RuiSetFloat( rui, "minFireFrac", 0.01 ) + } + } + #elseif SP + if ( slot == OFFHAND_INVENTORY ) + { + RuiSetInt( rui, "segments", 1 ) + RuiSetFloat( rui, "minFireFrac", 0.01 ) + } + #endif + } + else + { + RuiSetBool( rui, "isVisible", false ) + } + } + #if MP + if ( slot == OFFHAND_INVENTORY && file.slotVisible[slot] && player.IsTitan() && IsValid( weapon ) ) + { + int segments = player.GetWeaponAmmoStockpile( player.GetOffhandWeapon( OFFHAND_INVENTORY ) ) + segments += player.GetOffhandWeapon( OFFHAND_INVENTORY ).GetWeaponPrimaryClipCount() + RuiSetInt( rui, "segments", segments ) + RuiSetFloat( rui, "minFireFrac", 0.01 ) + } + #endif + + lastWeapon = weapon + wasVisible = file.slotVisible[slot] + WaitFrame() + } +} + +void function TrackATWeaponSlot( entity player, var rui ) +{ + player.EndSignal( "EndTrackOffhandWeaponSlot" ) + player.EndSignal( "OnDeath" ) + + OnThreadEnd( + function() : ( rui ) + { + RuiSetBool( rui, "isVisible", false ) + } + ) + + entity lastWeapon = null + bool wasVisible = file.slotVisible[OFFHAND_INVENTORY] + bool wasHoldingWeapon = player.GetActiveWeapon() == player.GetAntiTitanWeapon() + while ( IsAlive( player ) ) + { + entity weapon = player.GetAntiTitanWeapon() + entity activeWeapon = player.GetActiveWeapon() + bool isHoldingWeapon = weapon == activeWeapon + if ( weapon != lastWeapon || file.slotVisible[OFFHAND_INVENTORY] != wasVisible || isHoldingWeapon != wasHoldingWeapon ) + { + if ( IsValid( weapon ) && file.slotVisible[OFFHAND_INVENTORY] && !isHoldingWeapon ) + { + InitOffhandRui( rui, player, weapon ) + RuiSetFloat2( rui, "iconSizeScale", <1.5,0.75,0> ) + } + else + { + RuiSetBool( rui, "isVisible", false ) + } + } + + lastWeapon = weapon + wasVisible = file.slotVisible[OFFHAND_INVENTORY] + wasHoldingWeapon = isHoldingWeapon + WaitFrame() + } +} +/* + // VECTOR TYPES + RUI_TRACK_ABSORIGIN_FOLLOW, // Create at absorigin, and update to follow the entity + RUI_TRACK_POINT_FOLLOW, // Create on attachment point, and update to follow the entity + RUI_TRACK_OVERHEAD_FOLLOW, // Create at the top of the entity's bbox + RUI_TRACK_EYEANGLES_FOLLOW, + + // FLOAT TYPES + RUI_TRACK_HEALTH, // Health as fraction from 0 to 1 + RUI_TRACK_FRIENDLINESS, // 0 if ent is enemy, 1 if it's friendly + RUI_TRACK_PLAYER_SUIT_POWER, // Player's suit power from 0 to 1 + RUI_TRACK_PLAYER_GRAPPLE_POWER, // Player's grapple power from 0 to 1 + RUI_TRACK_PLAYER_SHARED_ENERGY, // Players shared energy value + RUI_TRACK_WEAPON_CHARGE_FRACTION, // Weapon charge as fraction from 0 to 1 + RUI_TRACK_WEAPON_SMART_AMMO_LOCK_FRACTION, // Smart ammo weapon lock fraction from 0 to N + RUI_TRACK_WEAPON_READY_TO_FIRE_FRACTION, // Weapon cooldown as fraction from 0 to 1 + RUI_TRACK_WEAPON_RELOAD_FRACTION, // Weapon reloading as fraction from 0 to 1 + RUI_TRACK_WEAPON_DRYFIRE_FRACTION, // + RUI_TRACK_WEAPON_CLIP_AMMO_FRACTION, // Weapon clip ammo as fraction from 0 to 1 + RUI_TRACK_WEAPON_REMAINING_AMMO_FRACTION, // Weapon remaining ammo as fraction from 0 to 1 + RUI_TRACK_WEAPON_CLIP_AMMO_MAX, // + RUI_TRACK_WEAPON_STOCKPILE_AMMO_MAX, // + RUI_TRACK_WEAPON_LIFETIME_SHOTS, // + RUI_TRACK_WEAPON_AMMO_REGEN_RATE, // + RUI_TRACK_BOOST_METER_FRACTION, // Player boost meter as fraction from 0 to 1 + RUI_TRACK_GLIDE_METER_FRACTION, // Player glide meter as fraction from 0 to 1 + RUI_TRACK_SHIELD_FRACTION, // Shield health as fraction from 0 to 1 + RUI_TRACK_STATUS_EFFECT_SEVERITY, // Status effect severity as fraction from 0 to 1; attachmentIndex used as status effect index + RUI_TRACK_SCRIPT_NETWORK_VAR, // Value of a script network variable (use GetNetworkedVariableIndex()) + RUI_TRACK_SCRIPT_NETWORK_VAR_GLOBAL, // Value of a script network variable without an entity (use GetNetworkedVariableIndex()) + RUI_TRACK_SCRIPT_NETWORK_VAR_LOCAL_VIEW_PLAYER, // Value of a script network variable on the local view player (changes automatically during kill replay) (use GetNetworkedVariableIndex()) + RUI_TRACK_FRIENDLY_TEAM_SCORE, // + RUI_TRACK_FRIENDLY_TEAM_ROUND_SCORE, // The value of score2 for friendlies + RUI_TRACK_ENEMY_TEAM_SCORE, // + RUI_TRACK_ENEMY_TEAM_ROUND_SCORE, // The value of score2 for enemies + RUI_TRACK_MINIMAP_SCALE, // + RUI_TRACK_SOUND_METER, // Sound meter as fraction from 0 to 1. + + // INT TYPES + RUI_TRACK_MINIMAP_FLAGS, + RUI_TRACK_MINIMAP_CUSTOM_STATE, + RUI_TRACK_TEAM_RELATION_VIEWPLAYER, // ENEMY: -1, NEUTRAL: 0, FRIENDLY: 1 + RUI_TRACK_TEAM_RELATION_CLIENTPLAYER, // ENEMY: -1, NEUTRAL: 0, FRIENDLY: 1 + RUI_TRACK_SCRIPT_NETWORK_VAR_INT, // Value of a script network variable (use GetNetworkedVariableIndex()) + RUI_TRACK_SCRIPT_NETWORK_VAR_GLOBAL_INT, // Value of a script network variable without an entity (use GetNetworkedVariableIndex()) + RUI_TRACK_SCRIPT_NETWORK_VAR_LOCAL_VIEW_PLAYER_INT, // Value of a script network variable on the local view player (changes automatically during kill replay) (use GetNetworkedVariableIndex()) + + // GAMETIME TYPES + RUI_TRACK_LAST_FIRED_TIME, + RUI_TRACK_MINIMAP_THREAT_SECTOR, + + // IMAGE TYPES + RUI_TRACK_WEAPON_MENU_ICON, + RUI_TRACK_WEAPON_HUD_ICON, +*/ + +void function InitOffhandRui( var rui, entity player, entity weapon ) +{ + RuiSetGameTime( rui, "hintTime", Time() ) + + RuiSetBool( rui, "isTitan", player.IsTitan() ) + RuiSetBool( rui, "isVisible", true ) + RuiSetBool( rui, "isReverseCharge", false ) + + RuiSetFloat( rui, "chargeFrac", 0.0 ) + RuiSetFloat( rui, "useFrac", 0.0 ) + RuiSetFloat( rui, "chargeMaxFrac", 1.0 ) + RuiSetFloat( rui, "minFireFrac", 1.0 ) + RuiSetInt( rui, "segments", 1 ) + RuiSetFloat( rui, "refillRate", 1 ) // default to 1 to preserve default behavior. some abilities draw the refillRecharge, even without a rate setting + + RuiTrackImage( rui, "hudIcon", weapon, RUI_TRACK_WEAPON_HUD_ICON ) + + RuiTrackFloat( rui, "readyFrac", weapon, RUI_TRACK_WEAPON_READY_TO_FIRE_FRACTION ) + RuiTrackFloat( rui, "dryfireFrac", weapon, RUI_TRACK_WEAPON_DRYFIRE_FRACTION ) + + RuiSetFloat( rui, "chargeFracCaution", 0.0 ) + RuiSetFloat( rui, "chargeFracAlert", 0.0 ) + RuiSetFloat( rui, "chargeFracAlertSpeed", 16.0 ) + RuiSetFloat( rui, "chargeFracAlertScale", 1.0 ) + + switch ( weapon.GetWeaponInfoFileKeyField( "cooldown_type" ) ) + { + case "ammo": + int ammoClipSize = weapon.GetWeaponSettingInt( eWeaponVar.ammo_clip_size ) + int ammoPerShot = weapon.GetWeaponSettingInt( eWeaponVar.ammo_per_shot ) + int ammoMinToFire = weapon.GetWeaponSettingInt( eWeaponVar.ammo_min_to_fire ) + + RuiSetFloat( rui, "minFireFrac", float ( ammoMinToFire ) / float ( ammoClipSize ) ) + RuiSetInt( rui, "segments", ammoClipSize / ammoPerShot ) + + RuiTrackFloat( rui, "chargeFrac", weapon, RUI_TRACK_WEAPON_CLIP_AMMO_FRACTION ) + + RuiSetFloat( rui, "refillRate", weapon.GetWeaponSettingFloat( eWeaponVar.regen_ammo_refill_rate ) ) + break + + case "ammo_swordblock": + RuiTrackFloat( rui, "chargeFrac", weapon, RUI_TRACK_WEAPON_STOCKPILE_REGEN_FRAC ) + RuiSetFloat( rui, "readyFrac", 0.0 ) + RuiSetFloat( rui, "minFireFrac", 0.0 ) + + RuiSetFloat( rui, "chargeFracCaution", 0.6 ) + RuiSetFloat( rui, "chargeFracAlert", 0.0 ) + break + + case "ammo_alert": + RuiSetFloat( rui, "chargeFrac", 0.0 ) + RuiSetFloat( rui, "readyFrac", 0.0 ) + RuiSetFloat( rui, "minFireFrac", 0.0 ) + + RuiSetFloat( rui, "chargeFracCaution", 0.01 ) + RuiSetFloat( rui, "chargeFracAlert", -1.0 ) + RuiSetFloat( rui, "chargeFracAlertSpeed", 5.0 ) + RuiSetFloat( rui, "chargeFracAlertScale", 0.6 ) + break + + case "ammo_instant": + case "ammo_deployed": + int ammoClipSize = weapon.GetWeaponSettingInt( eWeaponVar.ammo_clip_size ) + int ammoPerShot = weapon.GetWeaponSettingInt( eWeaponVar.ammo_per_shot ) + int ammoMinToFire = weapon.GetWeaponSettingInt( eWeaponVar.ammo_min_to_fire ) + + RuiSetFloat( rui, "minFireFrac", float ( ammoMinToFire ) / float ( ammoClipSize ) ) + RuiSetInt( rui, "segments", ammoClipSize / ammoPerShot ) + + RuiTrackFloat( rui, "chargeFrac", weapon, RUI_TRACK_WEAPON_CLIP_AMMO_FRACTION ) + RuiSetFloat( rui, "readyFrac", 0.0 ) + + RuiSetFloat( rui, "refillRate", weapon.GetWeaponSettingFloat( eWeaponVar.regen_ammo_refill_rate ) ) + break + + case "ammo_per_shot": + int ammoClipSize = weapon.GetWeaponSettingInt( eWeaponVar.ammo_clip_size ) + int ammoPerShot = weapon.GetWeaponSettingInt( eWeaponVar.ammo_per_shot ) + int ammoMinToFire = weapon.GetWeaponSettingInt( eWeaponVar.ammo_min_to_fire ) + + RuiSetFloat( rui, "minFireFrac", float ( ammoMinToFire ) / float ( ammoClipSize ) ) + RuiSetInt( rui, "segments", 1 ) + + RuiTrackFloat( rui, "chargeFrac", weapon, RUI_TRACK_WEAPON_CLIP_AMMO_FRACTION ) + RuiSetFloat( rui, "readyFrac", 0.0 ) + + RuiSetFloat( rui, "refillRate", weapon.GetWeaponSettingFloat( eWeaponVar.regen_ammo_refill_rate ) ) + break + + case "ammo_timed": + int ammoClipSize = weapon.GetWeaponSettingInt( eWeaponVar.ammo_clip_size ) + int ammoPerShot = weapon.GetWeaponSettingInt( eWeaponVar.ammo_per_shot ) + int ammoMinToFire = weapon.GetWeaponSettingInt( eWeaponVar.ammo_min_to_fire ) + + RuiSetFloat( rui, "minFireFrac", float ( ammoMinToFire ) / float ( ammoClipSize ) ) + RuiSetInt( rui, "segments", ammoClipSize / ammoPerShot ) + + RuiTrackFloat( rui, "chargeFrac", weapon, RUI_TRACK_WEAPON_CLIP_AMMO_FRACTION ) + RuiSetFloat( rui, "readyFrac", 0.0 ) + + RuiTrackFloat( rui, "useFrac", weapon, RUI_TRACK_STATUS_EFFECT_SEVERITY, eStatusEffect.simple_timer ) + + RuiSetFloat( rui, "refillRate", weapon.GetWeaponSettingFloat( eWeaponVar.regen_ammo_refill_rate ) ) + break + + case "shared_energy": + int curCost = weapon.GetWeaponCurrentEnergyCost() // 350 + + RuiSetFloat( rui, "readyFrac", 0.0 ) + RuiSetFloat( rui, "chargeMaxFrac", float( curCost ) ) + RuiTrackFloat( rui, "chargeFrac", player, RUI_TRACK_PLAYER_SHARED_ENERGY ) + break + + case "shared_energy_drain": + RuiSetFloat( rui, "readyFrac", 0.0 ) + RuiSetFloat( rui, "minFireFrac", 0.0 ) + RuiTrackFloat( rui, "chargeFrac", player, RUI_TRACK_PLAYER_SHARED_ENERGY ) + RuiSetFloat( rui, "chargeMaxFrac", float( ION_ENERGY_MAX ) ) + break + + case "vortex_drain": + RuiSetBool( rui, "isReverseCharge", true ) + RuiSetFloat( rui, "chargeFrac", 1.0 ) + RuiSetFloat( rui, "readyFrac", 0.0 ) + RuiSetFloat( rui, "minFireFrac", 0.0 ) + + RuiTrackFloat( rui, "chargeFrac", weapon, RUI_TRACK_WEAPON_CHARGE_FRACTION ) + break + + case "charged_shot": + RuiSetBool( rui, "isReverseCharge", true ) + RuiSetFloat( rui, "chargeFrac", 1.0 ) + RuiSetFloat( rui, "readyFrac", 0.0 ) + RuiSetFloat( rui, "minFireFrac", 0.2 ) + + RuiTrackFloat( rui, "chargeFrac", weapon, RUI_TRACK_WEAPON_CHARGE_FRACTION ) + break + + case "chargeFrac": + RuiTrackFloat( rui, "chargeFrac", weapon, RUI_TRACK_WEAPON_CHARGE_FRACTION ) + break + + case "smart": + RuiSetBool( rui, "isReverseCharge", true ) + RuiTrackFloat( rui, "chargeFrac", weapon, RUI_TRACK_WEAPON_CHARGE_FRACTION ) + RuiSetFloat( rui, "readyFrac", 0.0 ) + RuiTrackFloat( rui, "dryfireFrac", weapon, RUI_TRACK_WEAPON_DRYFIRE_FRACTION ) + break + + case "debug": + RuiTrackFloat( rui, "chargeFrac", weapon, RUI_TRACK_WEAPON_CHARGE_FRACTION ) + RuiTrackFloat( rui, "readyFrac", weapon, RUI_TRACK_WEAPON_READY_TO_FIRE_FRACTION ) + //RuiTrackFloat( rui, "dryfireFrac", weapon, RUI_TRACK_WEAPON_DRYFIRE_FRACTION ) + RuiTrackFloat( rui, "dryfireFrac", weapon, RUI_TRACK_WEAPON_SMART_AMMO_LOCK_FRACTION ) + break + + case "grapple": + int ammoClipSize = 100 + float ammoMinToFire = weapon.GetWeaponSettingFloat( eWeaponVar.grapple_power_required ) + + RuiSetFloat( rui, "minFireFrac", ammoMinToFire / float ( ammoClipSize ) ) + RuiSetInt( rui, "segments", int( ammoClipSize / ammoMinToFire ) ) + RuiTrackFloat( rui, "chargeFrac", player, RUI_TRACK_PLAYER_GRAPPLE_POWER ) + break + + default: + float refillRate = weapon.GetWeaponSettingFloat( eWeaponVar.regen_ammo_refill_rate ) + + if ( refillRate > 0 ) + { + //printt( "HUD: ", weapon.GetWeaponClassName(), "using", "refillRate" ) + int ammoClipSize = weapon.GetWeaponSettingInt( eWeaponVar.ammo_clip_size ) + int ammoPerShot = weapon.GetWeaponSettingInt( eWeaponVar.ammo_per_shot ) + int ammoMinToFire = weapon.GetWeaponSettingInt( eWeaponVar.ammo_min_to_fire ) + + RuiSetFloat( rui, "minFireFrac", float ( ammoMinToFire ) / float ( ammoClipSize ) ) + RuiSetInt( rui, "segments", ammoClipSize / ammoPerShot ) + + RuiTrackFloat( rui, "chargeFrac", weapon, RUI_TRACK_WEAPON_CLIP_AMMO_FRACTION ) + } + else + { + float chargeTime = weapon.GetWeaponSettingFloat( eWeaponVar.charge_time ) + if ( chargeTime == 0 ) + { + //printt( "HUD: ", weapon.GetWeaponClassName(), "using", "chargeTime == 0" ) + float fireDuration = weapon.GetWeaponSettingFloat( eWeaponVar.fire_duration ) + printt( weapon.GetWeaponClassName(), fireDuration ) + RuiSetBool( rui, "isReverseCharge", true ) + RuiTrackFloat( rui, "chargeFrac", weapon, RUI_TRACK_WEAPON_READY_TO_FIRE_FRACTION ) + } + else + { + //printt( "HUD: ", weapon.GetWeaponClassName(), "using", "chargeTime" ) + RuiTrackFloat( rui, "chargeFrac", weapon, RUI_TRACK_WEAPON_CHARGE_FRACTION ) + } + } + break + } +} + +void function ClWeaponStatus_SetOffhandVisible( int offhandIndex, bool newState ) +{ + entity player = GetLocalClientPlayer() + + var rui = GetRuiForIndex( player, offhandIndex ) + + file.slotVisible[offhandIndex] = newState + RuiSetBool( rui, "isVisible", newState ) +} + +var function GetRuiForIndex( entity player, int offhandIndex ) +{ + var rui + + if ( CheckCenterSlotOccupied(player) ) + { + switch ( offhandIndex ) + { + case OFFHAND_LEFT: + rui = file.ability_left_hud + break + case OFFHAND_TITAN_CENTER: + rui = file.ability_center_hud + break + case OFFHAND_RIGHT: + rui = file.ability_right_hud + break + case OFFHAND_INVENTORY: + rui = file.dpad_left_hud + break + } + } + else + { + switch ( offhandIndex ) + { + case OFFHAND_LEFT: + rui = file.ability_left_hud + break + case OFFHAND_RIGHT: + rui = file.ability_center_hud + break + case OFFHAND_INVENTORY: + rui = file.dpad_left_hud + break + } + } + + return rui +} + +void function ClWeaponStatus_SetWeaponVisible( bool newState ) +{ + RuiSetBool( file.ammo_counter, "isVisible", newState ) + file.ammo_counter_visible = newState +} + +bool function CheckCenterSlotOccupied( entity player ) +{ + entity weapon = player.GetOffhandWeapon( OFFHAND_TITAN_CENTER ) + return IsValid( weapon ) +}
\ No newline at end of file diff --git a/Northstar.Custom/mod/scripts/vscripts/_northstar_devcommands.gnut b/Northstar.Custom/mod/scripts/vscripts/_northstar_devcommands.gnut index e629e5ae..850855a0 100644 --- a/Northstar.Custom/mod/scripts/vscripts/_northstar_devcommands.gnut +++ b/Northstar.Custom/mod/scripts/vscripts/_northstar_devcommands.gnut @@ -23,7 +23,7 @@ bool function ClientCommandCallbackToggleNoclip( entity player, array<string> ar bool function ClientCommandCallbackKill( entity player, array<string> args ) { - if ( IsAlive( player ) && GetConVarInt( "sv_cheats" ) == 1 ) + if ( IsAlive( player ) ) player.Die() return true @@ -31,8 +31,8 @@ bool function ClientCommandCallbackKill( entity player, array<string> args ) bool function ClientCommandCallbackExplode( entity player, array<string> args ) { - if ( IsAlive( player ) && GetConVarInt( "sv_cheats" ) == 1 ) + if ( IsAlive( player ) ) player.Die( null, null, { scriptType = DF_GIB } ) return true -}
\ No newline at end of file +} diff --git a/Northstar.Custom/mod/scripts/vscripts/gamemodes/cl_gamemode_fastball.gnut b/Northstar.Custom/mod/scripts/vscripts/gamemodes/cl_gamemode_fastball.gnut index b7c225c7..f64658cf 100644 --- a/Northstar.Custom/mod/scripts/vscripts/gamemodes/cl_gamemode_fastball.gnut +++ b/Northstar.Custom/mod/scripts/vscripts/gamemodes/cl_gamemode_fastball.gnut @@ -68,7 +68,7 @@ void function ServerCallback_FastballPanelHacked( int panelHandle, int id, int c AnnouncementData announcement = Announcement_Create( Localize( "#FASTBALL_PANEL_CAPTURED", capturingPlayer.GetPlayerName(), panelIdString ) ) - if ( capturingPlayer.GetTeam() == GetLocalViewPlayer().GetTeam() ) + if ( capturingPlayer.GetTeam() == GetLocalClientPlayer().GetTeam() ) Announcement_SetTitleColor( announcement, < 0, 0, 1 > ) else Announcement_SetTitleColor( announcement, < 1, 0, 0 > ) @@ -77,7 +77,7 @@ void function ServerCallback_FastballPanelHacked( int panelHandle, int id, int c 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 ) + AnnouncementFromClass( GetLocalClientPlayer(), announcement ) } void function ServerCallback_FastballRespawnPlayer() @@ -88,7 +88,7 @@ void function ServerCallback_FastballRespawnPlayer() 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() + entity player = GetLocalClientPlayer() while ( !IsAlive( player ) ) WaitFrame() diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_mfd.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_mfd.nut index 1c776ede..11cb71f6 100644 --- a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_mfd.nut +++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_mfd.nut @@ -1,6 +1,5 @@ untyped global function GamemodeMfd_Init -global function RateSpawnpoints_Frontline struct { entity imcLastMark @@ -191,59 +190,4 @@ void function UpdateMarksForKill( entity victim, entity attacker, var damageInfo if ( attacker.IsPlayer() ) attacker.SetPlayerGameStat( PGS_ASSAULT_SCORE, attacker.GetPlayerGameStat( PGS_ASSAULT_SCORE ) + 1 ) } -} - -// could probably put this in spawn.nut? only here because i believe it's the main mode that uses this func -void function RateSpawnpoints_Frontline( int checkClass, array<entity> spawnpoints, int team, entity player ) -{ - Frontline frontline = GetFrontline( player.GetTeam() ) - - // heavily based on ctf spawn algo iteration 4, only changes it at the end - array<entity> startSpawns = SpawnPoints_GetPilotStart( team ) - array<entity> enemyStartSpawns = SpawnPoints_GetPilotStart( GetOtherTeam( team ) ) - array<entity> enemyPlayers = GetPlayerArrayOfTeam_Alive( team ) - - // get average startspawn position and max dist between spawns - // could probably cache this, tbh, not like it should change outside of halftimes - vector averageFriendlySpawns - float maxFriendlySpawnDist - - foreach ( entity spawn in startSpawns ) - { - foreach ( entity otherSpawn in startSpawns ) - { - float dist = Distance2D( spawn.GetOrigin(), otherSpawn.GetOrigin() ) - if ( dist > maxFriendlySpawnDist ) - maxFriendlySpawnDist = dist - } - - averageFriendlySpawns += spawn.GetOrigin() - } - - averageFriendlySpawns /= startSpawns.len() - - // get average enemy startspawn position - vector averageEnemySpawns - - foreach ( entity spawn in enemyStartSpawns ) - averageEnemySpawns += spawn.GetOrigin() - - averageEnemySpawns /= enemyStartSpawns.len() - - // from here, rate spawns - float baseDistance = Distance2D( averageFriendlySpawns, averageEnemySpawns ) - float spawnIterations = ( baseDistance / maxFriendlySpawnDist ) / 2 - - foreach ( entity spawn in spawnpoints ) - { - // ratings should max/min out at 100 / -100 - // start by prioritizing closer spawns, but not so much that enemies won't really affect them - float rating = 10 * ( 1.0 - Distance2D( averageFriendlySpawns, spawn.GetOrigin() ) / baseDistance ) - - // rate based on distance to frontline, and then prefer spawns in the same dir from the frontline as the combatdir - rating += rating * ( 1.0 - ( Distance2D( spawn.GetOrigin(), frontline.friendlyCenter ) / baseDistance ) ) - rating *= fabs( frontline.combatDir.y - Normalize( spawn.GetOrigin() - averageFriendlySpawns ).y ) - - spawn.CalculateRating( checkClass, player.GetTeam(), rating, rating ) - } }
\ No newline at end of file diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ps.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ps.nut index a02b9072..57355ad8 100644 --- a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ps.nut +++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ps.nut @@ -1,6 +1,6 @@ untyped
global function GamemodePs_Init
-global function RateSpawnpoints_SpawnZones
+//global function RateSpawnpoints_SpawnZones
struct {
array<entity> spawnzones
@@ -21,8 +21,10 @@ void function GamemodePs_Init() SetTimeoutWinnerDecisionFunc( CheckScoreForDraw )
// spawnzone stuff
- AddCallback_OnPlayerKilled( CheckSpawnzoneSuspiciousDeaths )
- AddSpawnCallbackEditorClass( "trigger_multiple", "trigger_mp_spawn_zone", SpawnzoneTriggerInit )
+ SetShouldCreateMinimapSpawnZones( true )
+
+ //AddCallback_OnPlayerKilled( CheckSpawnzoneSuspiciousDeaths )
+ //AddSpawnCallbackEditorClass( "trigger_multiple", "trigger_mp_spawn_zone", SpawnzoneTriggerInit )
file.militiaPreviousSpawnZones = [ null, null, null ]
file.imcPreviousSpawnZones = [ null, null, null ]
@@ -30,20 +32,8 @@ void function GamemodePs_Init() void function GiveScoreForPlayerKill( entity victim, entity attacker, var damageInfo )
{
- if ( victim != attacker && victim.IsPlayer() && attacker.IsPlayer() && GetGameState() == eGameState.Playing )
+ if ( victim != attacker && victim.IsPlayer() && attacker.IsPlayer() || GetGameState() != eGameState.Playing )
AddTeamScore( attacker.GetTeam(), 1 )
-
- table<int, bool> alreadyAssisted
- foreach( DamageHistoryStruct attackerInfo in victim.e.recentDamageHistory )
- {
- bool exists = attackerInfo.attacker.GetEncodedEHandle() in alreadyAssisted ? true : false
- if( attackerInfo.attacker != attacker && !exists )
- {
- alreadyAssisted[attackerInfo.attacker.GetEncodedEHandle()] <- true
- attackerInfo.attacker.AddToPlayerGameStat( PGS_ASSISTS, 1 )
- }
- }
-
}
int function CheckScoreForDraw()
@@ -54,180 +44,4 @@ int function CheckScoreForDraw() return TEAM_MILITIA
return TEAM_UNASSIGNED
-}
-
-// spawnzone logic
-void function SpawnzoneTriggerInit( entity spawnzone )
-{
- // initialise spawnzone script vars
- spawnzone.s.lastDeathRateCheck <- 0.0
- spawnzone.s.numRecentSuspiciousDeaths <- 0
- spawnzone.s.minimapObj <- null
-
- file.spawnzones.append( spawnzone )
-}
-
-void function SetNewSpawnzoneForTeam( int team, entity spawnzone )
-{
- entity minimapObj = CreatePropScript( $"models/dev/empty_model.mdl", spawnzone.GetOrigin() )
- SetTeam( minimapObj, team )
- minimapObj.Minimap_SetObjectScale( 100.0 / Distance2D( < 0, 0, 0 >, spawnzone.GetBoundingMaxs() ) )
- minimapObj.Minimap_SetAlignUpright( true )
- minimapObj.Minimap_AlwaysShow( TEAM_IMC, null )
- minimapObj.Minimap_AlwaysShow( TEAM_MILITIA, null )
- minimapObj.Minimap_SetHeightTracking( true )
- minimapObj.Minimap_SetZOrder( MINIMAP_Z_OBJECT )
-
- if ( team == TEAM_IMC )
- {
- if ( IsValid( file.imcActiveSpawnZone ) )
- file.imcActiveSpawnZone.s.minimapObj.Destroy()
-
- // update last 3 zones
- file.imcPreviousSpawnZones[ 2 ] = file.imcPreviousSpawnZones[ 1 ]
- file.imcPreviousSpawnZones[ 1 ] = file.imcPreviousSpawnZones[ 0 ]
- file.imcPreviousSpawnZones[ 0 ] = file.imcActiveSpawnZone
-
- file.imcActiveSpawnZone = spawnzone
- minimapObj.Minimap_SetCustomState( eMinimapObject_prop_script.SPAWNZONE_IMC )
- }
- else
- {
- if ( IsValid( file.militiaActiveSpawnZone ) )
- file.militiaActiveSpawnZone.s.minimapObj.Destroy()
-
- // update last 3 zones
- file.militiaPreviousSpawnZones[ 2 ] = file.militiaPreviousSpawnZones[ 1 ]
- file.militiaPreviousSpawnZones[ 1 ] = file.militiaPreviousSpawnZones[ 0 ]
- file.militiaPreviousSpawnZones[ 0 ] = file.militiaActiveSpawnZone
-
- file.militiaActiveSpawnZone = spawnzone
- minimapObj.Minimap_SetCustomState( eMinimapObject_prop_script.SPAWNZONE_MIL )
- }
-
- minimapObj.DisableHibernation()
-
- spawnzone.s.minimapObj = minimapObj
- spawnzone.s.lastDeathRateCheck = 0.0
- spawnzone.s.numRecentSuspiciousDeaths = Time()
-}
-
-void function CheckSpawnzoneSuspiciousDeaths( entity victim, entity attacker, var damageInfo )
-{
- if ( victim.s.respawnTime + 10.0 < Time() )
- return
-
- entity spawnzone
- if ( victim.GetTeam() == TEAM_IMC )
- spawnzone = file.imcActiveSpawnZone
- else
- spawnzone = file.militiaActiveSpawnZone
-
- if ( Distance2D( victim.GetOrigin(), spawnzone.GetOrigin() ) <= Distance2D( < 0, 0, 0 >, spawnzone.GetBoundingMaxs() ) * 1.2 )
- spawnzone.s.numRecentSuspiciousDeaths++
-}
-
-entity function FindNewSpawnZone( int team )
-{
- array<entity> startSpawns = SpawnPoints_GetPilotStart( team )
- array<entity> enemyStartSpawns = SpawnPoints_GetPilotStart( GetOtherTeam( team ) )
-
- // get average friendly startspawn position
- vector averageFriendlySpawns
- foreach ( entity spawn in startSpawns )
- averageFriendlySpawns += spawn.GetOrigin()
- averageFriendlySpawns /= startSpawns.len()
-
- // get average enemy startspawn position
- vector averageEnemySpawns
- foreach ( entity spawn in enemyStartSpawns )
- averageEnemySpawns += spawn.GetOrigin()
- averageEnemySpawns /= enemyStartSpawns.len()
-
- array<entity> validZones
- array<entity> enemyPlayers = GetPlayerArrayOfTeam( GetOtherTeam( team ) )
- float averageFriendlySpawnDist
-
- foreach ( entity spawnzone in file.spawnzones )
- {
- if ( team == TEAM_IMC && file.imcPreviousSpawnZones.contains( spawnzone ) )
- continue
- else if ( file.militiaPreviousSpawnZones.contains( spawnzone ) )
- continue
-
- // check if it's too far from startspawns
- float friendlySpawnDist = Distance2D( spawnzone.GetOrigin(), averageFriendlySpawns )
- if ( friendlySpawnDist > Distance2D( averageFriendlySpawns, averageEnemySpawns ) * 1.2 )
- continue
-
- // check if it's safe atm
- bool safe = true
- foreach ( entity enemy in enemyPlayers )
- {
- if ( Distance2D( enemy.GetOrigin(), spawnzone.GetOrigin() ) < Distance2D( < 0, 0, 0 >, spawnzone.GetBoundingMaxs() ) * 1.2 )
- {
- safe = false
- break
- }
- }
-
- if ( !safe )
- continue
-
- averageFriendlySpawnDist += friendlySpawnDist
- validZones.append( spawnzone )
- }
-
- averageFriendlySpawnDist /= validZones.len()
-
- array<entity> realValidZones = clone validZones
- foreach ( entity validzone in validZones )
- {
- if ( Distance2D( averageFriendlySpawns, validzone.GetOrigin() ) < averageFriendlySpawnDist * 1.4 )
- realValidZones.append( validzone )
- }
-
- entity spawnzone = realValidZones.getrandom()
- SetNewSpawnzoneForTeam( team, spawnzone )
-
- return spawnzone
-}
-
-void function RateSpawnpoints_SpawnZones( int checkClass, array<entity> spawnpoints, int team, entity player )
-{
- entity spawnzone
- if ( player.GetTeam() == TEAM_IMC )
- spawnzone = file.imcActiveSpawnZone
- else
- spawnzone = file.militiaActiveSpawnZone
-
- // spawnzones don't exist yet, create them now
- if ( !IsValid( spawnzone ) )
- spawnzone = FindNewSpawnZone( player.GetTeam() )
-
- // check if we should shift spawnzones
- // if it's been more than 15 seconds since last check, reset
- if ( spawnzone.s.lastDeathRateCheck + 15.0 < Time() )
- {
- spawnzone.s.numRecentSuspiciousDeaths = 0
- spawnzone.s.lastDeathRateCheck = Time()
- }
-
- // check if we've gone over the threshold for recent deaths too close to our current spawnzone
- if ( spawnzone.s.numRecentSuspiciousDeaths >= GetPlayerArrayOfTeam( player.GetTeam() ).len() * 0.4 )
- // over the threshold, find a new spawn zone
- spawnzone = FindNewSpawnZone( player.GetTeam() )
-
- // rate spawnpoints
- foreach ( entity spawn in spawnpoints )
- {
- float rating = 0.0
- float distance = Distance2D( spawn.GetOrigin(), spawnzone.GetOrigin() )
- if ( distance < Distance2D( < 0, 0, 0 >, spawnzone.GetBoundingMaxs() ) )
- rating = 100.0
- else // max 35 rating if not in zone, rate by closest
- rating = 35.0 * ( 1 - ( distance / 5000.0 ) )
-
- spawn.CalculateRating( checkClass, player.GetTeam(), rating, rating )
- }
-}
+}
\ No newline at end of file diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/levels/mp_colony02.nut b/Northstar.CustomServers/mod/scripts/vscripts/mp/levels/mp_colony02.nut index 37b89169..40cf942e 100644 --- a/Northstar.CustomServers/mod/scripts/vscripts/mp/levels/mp_colony02.nut +++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/levels/mp_colony02.nut @@ -1 +1,14 @@ -//fuck
\ No newline at end of file +global function CodeCallback_MapInit + +void function CodeCallback_MapInit() +{ + AddEvacNode( CreateScriptRef( < -475.129913, 1480.167847, 527.363953 >, < 8.841560, 219.338501, 0 > ) ) + AddEvacNode( CreateScriptRef( < 1009.315186, 3999.888916, 589.914917 >, < 23.945116, -146.680725, 0 > ) ) + AddEvacNode( CreateScriptRef( < 2282.868896, -1363.706543, 846.188660 >, < 23.945116, -146.680725, 0 > ) ) + AddEvacNode( CreateScriptRef( < 1911.771606, -752.053101, 664.741821 >, < 9.955260, 138.721191, 0 > ) ) + AddEvacNode( CreateScriptRef( < 1985.563232, -1205.455078, 677.444763 >, < 13.809734, -239.877441, 0 > ) ) + AddEvacNode( CreateScriptRef( < -59.625496, -1858.108887, 811.592407 >, < 20.556290, -252.775146, 0 > ) ) + AddEvacNode( CreateScriptRef( < -1035.991211, -671.114380, 824.180908 >, < 16.220453, -24.511070, 0 > ) ) + + SetEvacSpaceNode( GetEnt( "intro_spacenode" ) ) +}
\ No newline at end of file diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/levels/mp_wargames.nut b/Northstar.CustomServers/mod/scripts/vscripts/mp/levels/mp_wargames.nut index 11587947..3f3a05f2 100644 --- a/Northstar.CustomServers/mod/scripts/vscripts/mp/levels/mp_wargames.nut +++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/levels/mp_wargames.nut @@ -2,6 +2,8 @@ untyped global function CodeCallback_MapInit struct { + array<entity> marvinSpawners + float introStartTime entity militiaPod entity imcPod @@ -20,13 +22,14 @@ void function CodeCallback_MapInit() SetEvacSpaceNode( GetEnt( "end_spacenode" ) ) // dissolve effects - AddDeathCallback( "player", WargamesDissolveDeadEntity ) + AddDeathCallback( "player", WargamesDissolveDeadEntity ) AddDeathCallback( "npc_soldier", WargamesDissolveDeadEntity ) AddDeathCallback( "npc_spectre", WargamesDissolveDeadEntity ) AddDeathCallback( "npc_pilot_elite", WargamesDissolveDeadEntity ) AddDeathCallback( "npc_marvin", WargamesDissolveDeadEntity ) - FlagClear( "Disable_Marvins" ) + AddSpawnCallback( "info_spawnpoint_marvin", AddMarvinSpawner ) + AddCallback_GameStateEnter( eGameState.Prematch, SpawnMarvinsForRound ) // currently disabled until finished: intro if ( !IsFFAGame() ) @@ -40,6 +43,44 @@ void function WargamesDissolveDeadEntity( entity deadEnt, var damageInfo ) { deadEnt.Dissolve( ENTITY_DISSOLVE_CHAR, < 0, 0, 0 >, 0 ) EmitSoundAtPosition( TEAM_UNASSIGNED, deadEnt.GetOrigin(), "Object_Dissolve" ) + + if ( deadEnt.IsPlayer() ) + thread EnsureWargamesDeathEffectIsClearedForPlayer( deadEnt ) + } +} + +void function EnsureWargamesDeathEffectIsClearedForPlayer( entity player ) +{ + // this is slightly shit but whatever lol + player.EndSignal( "OnDestroy" ) + + float startTime = Time() + while ( player.kv.VisibilityFlags != "0" ) + { + if ( Time() > startTime + 4.0 ) // if we wait too long, just ignore + return + + WaitFrame() + } + + player.kv.VisibilityFlags = ENTITY_VISIBLE_TO_EVERYONE +} + +void function AddMarvinSpawner( entity spawn ) +{ + file.marvinSpawners.append( spawn ) +} + +void function SpawnMarvinsForRound() +{ + foreach ( entity spawner in file.marvinSpawners ) + { + entity marvin = CreateMarvin( TEAM_UNASSIGNED, spawner.GetOrigin(), spawner.GetAngles() ) + marvin.kv.health = 1 + marvin.kv.max_health = 1 + marvin.kv.spawnflags = 516 + DispatchSpawn( marvin ) + HideName( marvin ) } } diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/spawn.nut b/Northstar.CustomServers/mod/scripts/vscripts/mp/spawn.nut index 9b293524..42e107e4 100644 --- a/Northstar.CustomServers/mod/scripts/vscripts/mp/spawn.nut +++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/spawn.nut @@ -14,6 +14,13 @@ global function DeleteNoSpawnArea global function FindSpawnPoint global function RateSpawnpoints_Generic +global function RateSpawnpoints_Frontline + +global function SetSpawnZoneRatingFunc +global function SetShouldCreateMinimapSpawnZones +global function CreateTeamSpawnZoneEntity +global function RateSpawnpoints_SpawnZones +global function DecideSpawnZone_Generic struct NoSpawnArea { @@ -31,8 +38,6 @@ struct { array< bool functionref( entity, int ) > customSpawnpointValidationRules table<string, NoSpawnArea> noSpawnAreas - - array<vector> preferSpawnNodes } file void function Spawn_Init() @@ -42,24 +47,12 @@ void function Spawn_Init() AddSpawnCallback( "info_spawnpoint_titan", InitSpawnpoint ) AddSpawnCallback( "info_spawnpoint_titan_start", InitSpawnpoint ) + // callbacks for generic spawns 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() ) + // callbacks for spawnzone spawns + AddCallback_GameStateEnter( eGameState.Prematch, ResetSpawnzones ) + AddSpawnCallbackEditorClass( "trigger_multiple", "trigger_mp_spawn_zone", AddSpawnZoneTrigger ) } void function InitSpawnpoint( entity spawnpoint ) @@ -273,36 +266,32 @@ bool function IsSpawnpointValid( entity spawnpoint, int team ) return false // los check - array<entity> enemyLosPlayers - if ( IsFFAGame() ) - enemyLosPlayers = GetPlayerArray() - else - enemyLosPlayers = GetPlayerArrayOfTeam( GetOtherTeam( team ) ) - - foreach ( entity enemyPlayer in enemyLosPlayers ) - { - if ( enemyPlayer.GetTeam() == team || !IsAlive( enemyPlayer ) ) - continue - - float dist = 1000.0 - // check fov, constant here is stolen from every other place this is done - if ( VectorDot_PlayerToOrigin( enemyPlayer, spawnpoint.GetOrigin() ) > 0.8 ) - dist /= 0.75 - - // check distance, constant here is basically arbitrary - if ( Distance( enemyPlayer.GetOrigin(), spawnpoint.GetOrigin() ) > dist ) - continue - - // check actual los - if ( TraceLineSimple( enemyPlayer.EyePosition(), spawnpoint.GetOrigin() + < 0, 0, 48 >, enemyPlayer ) == 1.0 ) - return false - } - - return true + return !spawnpoint.IsVisibleToEnemies( team ) } + +// SPAWNPOINT RATING FUNCS BELOW + +// generic +struct { + array<vector> preferSpawnNodes +} spawnStateGeneric + void function RateSpawnpoints_Generic( int checkClass, array<entity> spawnpoints, int team, entity player ) { + if ( !IsFFAGame() ) + { + // use frontline spawns in 2-team modes + RateSpawnpoints_Frontline( checkClass, spawnpoints, team, player ) + return + } + else + { + // todo: ffa spawns :terror: + } + + // old algo: keeping until we have a better ffa spawn algo + // i'm not a fan of this func, but i really don't have a better way to do this rn, and it's surprisingly good with los checks implemented now // calculate ratings for preferred nodes @@ -311,7 +300,7 @@ void function RateSpawnpoints_Generic( int checkClass, array<entity> spawnpoints // 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 ) + foreach ( vector preferSpawnNode in spawnStateGeneric.preferSpawnNodes ) { float currentRating @@ -354,13 +343,13 @@ void function RateSpawnpoints_Generic( int checkClass, array<entity> spawnpoints 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++ ) + for ( int i = 0; i < spawnStateGeneric.preferSpawnNodes.len(); i++ ) { // bonus if autotitan is nearish - if ( IsAlive( player.GetPetTitan() ) && Distance( player.GetPetTitan().GetOrigin(), file.preferSpawnNodes[ i ] ) < 1200.0 ) + if ( IsAlive( player.GetPetTitan() ) && Distance( player.GetPetTitan().GetOrigin(), spawnStateGeneric.preferSpawnNodes[ i ] ) < 1200.0 ) petTitanModifier += 10.0 - float dist = Distance2D( spawnpoint.GetOrigin(), file.preferSpawnNodes[ i ] ) + float dist = Distance2D( spawnpoint.GetOrigin(), spawnStateGeneric.preferSpawnNodes[ i ] ) if ( dist > 750.0 ) continue @@ -384,4 +373,301 @@ void function RateSpawnpoints_Generic( int checkClass, array<entity> spawnpoints if ( rating != 0.0 || currentRating != 0.0 ) print( "rating = " + rating + ", internal rating = " + currentRating ) } +} + +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 + + spawnStateGeneric.preferSpawnNodes.append( hardpoint.GetOrigin() ) + } + + //foreach ( entity frontline in GetEntArrayByClass_Expensive( "info_frontline" ) ) + // spawnStateGeneric.preferSpawnNodes.append( frontline.GetOrigin() ) +} + +// frontline +void function RateSpawnpoints_Frontline( int checkClass, array<entity> spawnpoints, int team, entity player ) +{ + Frontline frontline = GetFrontline( player.GetTeam() ) + + // heavily based on ctf spawn algo iteration 4, only changes it at the end + array<entity> startSpawns = SpawnPoints_GetPilotStart( team ) + array<entity> enemyStartSpawns = SpawnPoints_GetPilotStart( GetOtherTeam( team ) ) + + if ( startSpawns.len() == 0 || enemyStartSpawns.len() == 0 ) // ensure we don't crash + return + + // get average startspawn position and max dist between spawns + // could probably cache this, tbh, not like it should change outside of halftimes + vector averageFriendlySpawns + float maxFriendlySpawnDist + + foreach ( entity spawn in startSpawns ) + { + foreach ( entity otherSpawn in startSpawns ) + { + float dist = Distance2D( spawn.GetOrigin(), otherSpawn.GetOrigin() ) + if ( dist > maxFriendlySpawnDist ) + maxFriendlySpawnDist = dist + } + + averageFriendlySpawns += spawn.GetOrigin() + } + + averageFriendlySpawns /= startSpawns.len() + + // get average enemy startspawn position + vector averageEnemySpawns + + foreach ( entity spawn in enemyStartSpawns ) + averageEnemySpawns += spawn.GetOrigin() + + averageEnemySpawns /= enemyStartSpawns.len() + + // from here, rate spawns + float baseDistance = Distance2D( averageFriendlySpawns, averageEnemySpawns ) + foreach ( entity spawn in spawnpoints ) + { + // ratings should max/min out at 100 / -100 + // start by prioritizing closer spawns, but not so much that enemies won't really affect them + float rating = 10 * ( 1.0 - Distance2D( averageFriendlySpawns, spawn.GetOrigin() ) / baseDistance ) + + // rate based on distance to frontline, and then prefer spawns in the same dir from the frontline as the combatdir + rating += rating * ( 1.0 - ( Distance2D( spawn.GetOrigin(), frontline.friendlyCenter ) / baseDistance ) ) + rating *= fabs( frontline.combatDir.y - Normalize( spawn.GetOrigin() - averageFriendlySpawns ).y ) + + spawn.CalculateRating( checkClass, player.GetTeam(), rating, rating ) + } +} + +// spawnzones +struct { + array<entity> mapSpawnzoneTriggers + entity functionref( array<entity>, int ) spawnzoneRatingFunc + bool shouldCreateMinimapSpawnzones = false + + // for DecideSpawnZone_Generic + table<int, entity> activeTeamSpawnzones + table<int, entity> activeTeamSpawnzoneMinimapEnts +} spawnStateSpawnzones + +void function ResetSpawnzones() +{ + spawnStateSpawnzones.activeTeamSpawnzones.clear() + + foreach ( int team, entity minimapEnt in spawnStateSpawnzones.activeTeamSpawnzoneMinimapEnts ) + if ( IsValid( minimapEnt ) ) + minimapEnt.Destroy() + + spawnStateSpawnzones.activeTeamSpawnzoneMinimapEnts.clear() +} + +void function AddSpawnZoneTrigger( entity trigger ) +{ + trigger.s.spawnzoneRating <- 0.0 + spawnStateSpawnzones.mapSpawnzoneTriggers.append( trigger ) +} + +void function SetSpawnZoneRatingFunc( entity functionref( array<entity>, int ) ratingFunc ) +{ + spawnStateSpawnzones.spawnzoneRatingFunc = ratingFunc +} + +void function SetShouldCreateMinimapSpawnZones( bool shouldCreateMinimapSpawnzones ) +{ + spawnStateSpawnzones.shouldCreateMinimapSpawnzones = shouldCreateMinimapSpawnzones +} + +entity function CreateTeamSpawnZoneEntity( entity spawnzone, int team ) +{ + entity minimapObj = CreatePropScript( $"models/dev/empty_model.mdl", spawnzone.GetOrigin() ) + SetTeam( minimapObj, team ) + minimapObj.Minimap_SetObjectScale( 100.0 / Distance2D( < 0, 0, 0 >, spawnzone.GetBoundingMaxs() ) ) + minimapObj.Minimap_SetAlignUpright( true ) + minimapObj.Minimap_AlwaysShow( TEAM_IMC, null ) + minimapObj.Minimap_AlwaysShow( TEAM_MILITIA, null ) + minimapObj.Minimap_SetHeightTracking( true ) + minimapObj.Minimap_SetZOrder( MINIMAP_Z_OBJECT ) + + if ( team == TEAM_IMC ) + minimapObj.Minimap_SetCustomState( eMinimapObject_prop_script.SPAWNZONE_IMC ) + else + minimapObj.Minimap_SetCustomState( eMinimapObject_prop_script.SPAWNZONE_MIL ) + + minimapObj.DisableHibernation() + return minimapObj +} + +void function RateSpawnpoints_SpawnZones( int checkClass, array<entity> spawnpoints, int team, entity player ) +{ + if ( spawnStateSpawnzones.spawnzoneRatingFunc == null ) + spawnStateSpawnzones.spawnzoneRatingFunc = DecideSpawnZone_Generic + + // don't use spawnzones if we're using start spawns + if ( ShouldStartSpawn( player ) ) + { + RateSpawnpoints_Generic( checkClass, spawnpoints, team, player ) + return + } + + entity spawnzone = spawnStateSpawnzones.spawnzoneRatingFunc( spawnStateSpawnzones.mapSpawnzoneTriggers, player.GetTeam() ) + if ( !IsValid( spawnzone ) ) // no spawn zone, use generic algo + { + RateSpawnpoints_Generic( checkClass, spawnpoints, team, player ) + return + } + + // rate spawnpoints + foreach ( entity spawn in spawnpoints ) + { + float rating = 0.0 + float distance = Distance2D( spawn.GetOrigin(), spawnzone.GetOrigin() ) + if ( distance < Distance2D( < 0, 0, 0 >, spawnzone.GetBoundingMaxs() ) ) + rating = 100.0 + else // max 35 rating if not in zone, rate by closest + rating = 35.0 * ( 1 - ( distance / 5000.0 ) ) + + spawn.CalculateRating( checkClass, player.GetTeam(), rating, rating ) + } +} + +entity function DecideSpawnZone_Generic( array<entity> spawnzones, int team ) +{ + if ( spawnzones.len() == 0 ) + return null + + // get average team startspawn positions + int spawnCompareTeam = team + if ( HasSwitchedSides() ) + spawnCompareTeam = GetOtherTeam( team ) + + array<entity> startSpawns = SpawnPoints_GetPilotStart( spawnCompareTeam ) + array<entity> enemyStartSpawns = SpawnPoints_GetPilotStart( GetOtherTeam( spawnCompareTeam ) ) + + if ( startSpawns.len() == 0 || enemyStartSpawns.len() == 0 ) // ensure we don't crash + return null + + // get average startspawn position and max dist between spawns + // could probably cache this, tbh, not like it should change outside of halftimes + vector averageFriendlySpawns + foreach ( entity spawn in startSpawns ) + averageFriendlySpawns += spawn.GetOrigin() + + averageFriendlySpawns /= startSpawns.len() + + // get average enemy startspawn position + vector averageEnemySpawns + foreach ( entity spawn in enemyStartSpawns ) + averageEnemySpawns += spawn.GetOrigin() + + averageEnemySpawns /= enemyStartSpawns.len() + + float baseDistance = Distance2D( averageFriendlySpawns, averageEnemySpawns ) + + bool needNewZone = true + if ( team in spawnStateSpawnzones.activeTeamSpawnzones ) + { + foreach ( entity player in GetPlayerArray() ) + { + // couldn't get IsTouching, GetTouchingEntities or enter callbacks to work in testing, so doing this + if ( player.GetTeam() != team && spawnStateSpawnzones.activeTeamSpawnzones[ team ].ContainsPoint( player.GetOrigin() ) ) + break + } + + needNewZone = false + } + + if ( needNewZone ) + { + // find new zone + array<entity> possibleZones + foreach ( entity spawnzone in spawnStateSpawnzones.mapSpawnzoneTriggers ) + { + // don't remeber if you can do a "value in table.values" sorta thing in squirrel so doing manual lookup + bool spawnzoneTaken = false + foreach ( int otherTeam, entity otherSpawnzone in spawnStateSpawnzones.activeTeamSpawnzones ) + { + if ( otherSpawnzone == spawnzone ) + { + spawnzoneTaken = true + break + } + } + + if ( spawnzoneTaken ) + continue + + // check zone validity + bool spawnzoneEvil = false + foreach ( entity player in GetPlayerArray() ) + { + // couldn't get IsTouching, GetTouchingEntities or enter callbacks to work in testing, so doing this + if ( player.GetTeam() != team && spawnzone.ContainsPoint( player.GetOrigin() ) ) + { + spawnzoneEvil = true + break + } + } + + // don't choose spawnzones that are closer to enemy base than friendly base + if ( !spawnzoneEvil && Distance2D( spawnzone.GetOrigin(), averageFriendlySpawns ) > Distance2D( spawnzone.GetOrigin(), averageEnemySpawns ) ) + spawnzoneEvil = true + + if ( spawnzoneEvil ) + continue + + // rate spawnzone based on distance to frontline + Frontline frontline = GetFrontline( team ) + + // prefer spawns close to base pos + float rating = 10 * ( 1.0 - Distance2D( averageFriendlySpawns, spawnzone.GetOrigin() ) / baseDistance ) + + if ( frontline.friendlyCenter != < 0, 0, 0 > ) + { + // rate based on distance to frontline, and then prefer spawns in the same dir from the frontline as the combatdir + rating += rating * ( 1.0 - ( Distance2D( spawnzone.GetOrigin(), frontline.friendlyCenter ) / baseDistance ) ) + rating *= fabs( frontline.combatDir.y - Normalize( spawnzone.GetOrigin() - averageFriendlySpawns ).y ) + } + + spawnzone.s.spawnzoneRating = rating + possibleZones.append( spawnzone ) + } + + if ( possibleZones.len() == 0 ) + return null + + possibleZones.sort( int function( entity a, entity b ) + { + if ( a.s.spawnzoneRating > b.s.spawnzoneRating ) + return -1 + + if ( b.s.spawnzoneRating > a.s.spawnzoneRating ) + return 1 + + return 0 + } ) + entity chosenZone = possibleZones[ minint( RandomInt( 3 ), possibleZones.len() ) ] + + if ( spawnStateSpawnzones.shouldCreateMinimapSpawnzones ) + { + entity oldEnt + if ( team in spawnStateSpawnzones.activeTeamSpawnzoneMinimapEnts ) + oldEnt = spawnStateSpawnzones.activeTeamSpawnzoneMinimapEnts[ team ] + + spawnStateSpawnzones.activeTeamSpawnzoneMinimapEnts[ team ] <- CreateTeamSpawnZoneEntity( chosenZone, team ) + if ( IsValid( oldEnt ) ) + oldEnt.Destroy() + } + + spawnStateSpawnzones.activeTeamSpawnzones[ team ] <- chosenZone + } + + return spawnStateSpawnzones.activeTeamSpawnzones[ team ] }
\ No newline at end of file |