diff --git a/project/Aki.SinglePlayer/Models/Progression/LighthouseProgressionClass.cs b/project/Aki.SinglePlayer/Models/Progression/LighthouseProgressionClass.cs index 02b92d5..9129eee 100644 --- a/project/Aki.SinglePlayer/Models/Progression/LighthouseProgressionClass.cs +++ b/project/Aki.SinglePlayer/Models/Progression/LighthouseProgressionClass.cs @@ -10,142 +10,189 @@ namespace Aki.SinglePlayer.Models.Progression { private bool _isScav; private GameWorld _gameWorld; + private Player _player; private float _timer; - private bool _addedToEnemy; - private List _mines; + private bool _playerFlaggedAsEnemyToBosses; + private List _bridgeMines; private RecodableItemClass _transmitter; - private List _bosses; + private List _zryachiyAndFollowers = new List(); private bool _aggressor; - private bool _disabledDoor; + private bool _isDoorDisabled; private readonly string _transmitterId = "62e910aaf957f2915e0a5e36"; + private readonly string _lightKeeperTid = "638f541a29ffd1183d187f57"; public void Start() { _gameWorld = Singleton.Instance; - _bosses = new List(); - _mines = GameObject.FindObjectsOfType().ToList(); + _player = _gameWorld?.MainPlayer; - if (_gameWorld == null || !string.Equals(_gameWorld.MainPlayer.Location, "lighthouse", System.StringComparison.OrdinalIgnoreCase)) + // Exit if not on lighthouse + if (_gameWorld == null || !string.Equals(_player.Location, "lighthouse", System.StringComparison.OrdinalIgnoreCase)) { return; } - // if player is a scav, there is no need to continue this method. - if (_gameWorld.MainPlayer.Side == EPlayerSide.Savage) + // Expensive, run after gameworld / lighthouse checks above + _bridgeMines = FindObjectsOfType().ToList(); + + // Player is a scav, exit + if (_player.Side == EPlayerSide.Savage) { _isScav = true; + return; } - // Get the players Transmitter. - _transmitter = (RecodableItemClass) _gameWorld.MainPlayer.Profile.Inventory.AllRealPlayerItems.FirstOrDefault(x => x.TemplateId == _transmitterId); - - if (_transmitter != null) + _transmitter = GetTransmitterFromInventory(); + if (PlayerHasTransmitterInInventory()) { GameObject.Find("Attack").SetActive(false); - // this zone was added in a newer version and the gameObject actually has a \ + // Zone was added in a newer version and the gameObject actually has a \ GameObject.Find("CloseZone\\").SetActive(false); - // Give access to the Lightkeepers door. - _gameWorld.BufferZoneController.SetPlayerAccessStatus(_gameWorld.MainPlayer.ProfileId, true); + // Give access to Lightkeepers door + _gameWorld.BufferZoneController.SetPlayerAccessStatus(_player.ProfileId, true); } } public void Update() { - if (_gameWorld == null || _addedToEnemy || _disabledDoor || _transmitter == null) return; - - _timer += Time.deltaTime; + IncrementLastUpdateTimer(); + // Exit early if last update() run time was < 10 secs ago if (_timer < 10f) { return; } - if (_bosses.Count == 0) + // Skip if: + // GameWorld missing + // Player not an enemy to Zryachiy + // Lk door not accessible + // Player has no transmitter on thier person + if (_gameWorld == null || _playerFlaggedAsEnemyToBosses || _isDoorDisabled || _transmitter == null) { - SetupBosses(); + return; + } + + // Find Zryachiy and prep him + if (_zryachiyAndFollowers.Count == 0) + { + SetupZryachiyAndFollowerHostility(); } if (_isScav) { - PlayerIsScav(); + MakeZryachiyAndFollowersHostileToPlayer(); + return; } - if (_gameWorld?.MainPlayer?.HandsController?.Item?.TemplateId == _transmitterId) + // (active/green) + if (PlayerHasActiveTransmitterInHands()) { - if (_transmitter?.RecodableComponent?.Status == RadioTransmitterStatus.Green) - { - foreach (var mine in _mines) - { - if (mine.gameObject.activeSelf) - { - mine.gameObject.SetActive(false); - } - } - } + SetBridgeMinesStatus(false); } else { - foreach (var mine in _mines) - { - if (!mine.gameObject.activeSelf) - { - mine.gameObject.SetActive(true); - } - } + SetBridgeMinesStatus(true); } if (_aggressor) { - PlayerIsAggressor(); + DisableAccessToLightKeeper(); } } - private void SetupBosses() + private RecodableItemClass GetTransmitterFromInventory() { - foreach (var aiBot in _gameWorld.AllAlivePlayersList) - { - if (!aiBot.IsYourPlayer) - { - // Edge case of bossZryachiy not being hostile to player - if (aiBot.AIData.BotOwner.IsRole(WildSpawnType.bossZryachiy) || aiBot.AIData.BotOwner.IsRole(WildSpawnType.followerZryachiy)) - { - // Subscribe to Bosses OnDeath event - aiBot.OnPlayerDeadOrUnspawn += player1 => - { - if (player1?.KillerId == _gameWorld.MainPlayer.ProfileId) - { - _aggressor = true; - } - }; + return (RecodableItemClass)_player.Profile.Inventory.AllRealPlayerItems.FirstOrDefault(x => x.TemplateId == _transmitterId); + } - _bosses.Add(aiBot); - } + private bool PlayerHasTransmitterInInventory() + { + return _transmitter != null; + } + + /// + /// Update _time to diff from last run of update() + /// + private void IncrementLastUpdateTimer() + { + _timer += Time.deltaTime; + } + + private bool PlayerHasActiveTransmitterInHands() + { + return _gameWorld?.MainPlayer?.HandsController?.Item?.TemplateId == _transmitterId + && _transmitter?.RecodableComponent?.Status == RadioTransmitterStatus.Green; + } + + /// + /// Set all brdige mines to desire state + /// + /// What state mines should be + private void SetBridgeMinesStatus(bool active) + { + // Find mines with opposite state of what we want + foreach (var mine in _bridgeMines.Where(mine => mine.gameObject.activeSelf == !active)) + { + mine.gameObject.SetActive(active); + } + } + + private void SetupZryachiyAndFollowerHostility() + { + // only process non-players (ai) + foreach (var aiBot in _gameWorld.AllAlivePlayersList.Where(x => !x.IsYourPlayer)) + { + // Edge case of bossZryachiy not being hostile to player + if (aiBot.AIData.BotOwner.IsRole(WildSpawnType.bossZryachiy) || aiBot.AIData.BotOwner.IsRole(WildSpawnType.followerZryachiy)) + { + // Subscribe to bots OnDeath event + aiBot.OnPlayerDeadOrUnspawn += player1 => + { + // If player kills zryachiy or follower, force aggressor state + // Also set players Lk standing to negative (allows access to quest chain (Making Amends)) + if (player1?.KillerId == _player.ProfileId) + { + _aggressor = true; + _player.Profile.TradersInfo[_lightKeeperTid].SetStanding(-0.01); + } + }; + + // Save bot to list for later access + _zryachiyAndFollowers.Add(aiBot); } } } - private void PlayerIsScav() + /// + /// Iterate over bots gathered from SetupZryachiyHostility() + /// + private void MakeZryachiyAndFollowersHostileToPlayer() { // If player is a scav, they must be added to the bosses enemy list otherwise they wont kill them - foreach (var boss in _bosses) + foreach (var bot in _zryachiyAndFollowers) { - boss.AIData.BotOwner.BotsGroup.AddEnemy(_gameWorld.MainPlayer); + bot.AIData.BotOwner.BotsGroup.AddEnemy(_player); } - _addedToEnemy = true; + // Flag player was added to enemy list + _playerFlaggedAsEnemyToBosses = true; } - private void PlayerIsAggressor() + /// + /// Disable door + set transmitter to 'red' + /// + private void DisableAccessToLightKeeper() { // Disable access to Lightkeepers door for the player _gameWorld.BufferZoneController.SetPlayerAccessStatus(_gameWorld.MainPlayer.ProfileId, false); _transmitter?.RecodableComponent?.SetStatus(RadioTransmitterStatus.Yellow); _transmitter?.RecodableComponent?.SetEncoded(false); - _disabledDoor = true; + _isDoorDisabled = true; } } } diff --git a/project/Aki.SinglePlayer/Patches/Progression/LighthouseBridgePatch.cs b/project/Aki.SinglePlayer/Patches/Progression/LighthouseBridgePatch.cs index 6be4e98..5b4ae15 100644 --- a/project/Aki.SinglePlayer/Patches/Progression/LighthouseBridgePatch.cs +++ b/project/Aki.SinglePlayer/Patches/Progression/LighthouseBridgePatch.cs @@ -17,8 +17,10 @@ namespace Aki.SinglePlayer.Patches.Progression private static void PatchPostfix() { var gameWorld = Singleton.Instance; - - if (gameWorld == null || gameWorld.MainPlayer.Location.ToLower() != "lighthouse") return; + if (gameWorld == null || gameWorld.MainPlayer.Location.ToLower() != "lighthouse") + { + return; + } gameWorld.GetOrAddComponent(); }