diff --git a/Fika.Core/Bundles/Files/playerui.bundle b/Fika.Core/Bundles/Files/playerui.bundle index d8953017..8361a374 100644 Binary files a/Fika.Core/Bundles/Files/playerui.bundle and b/Fika.Core/Bundles/Files/playerui.bundle differ diff --git a/Fika.Core/Console/FikaCommands.cs b/Fika.Core/Console/FikaCommands.cs index 169ccf9b..712195bb 100644 --- a/Fika.Core/Console/FikaCommands.cs +++ b/Fika.Core/Console/FikaCommands.cs @@ -128,7 +128,7 @@ public static void DespawnAllAI() CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler); - List Bots = new List(game.BotsController.Players); + List Bots = new(game.BotsController.Players); foreach (Player bot in Bots) { diff --git a/Fika.Core/Coop/Airdrops/FikaAirdropsManager.cs b/Fika.Core/Coop/Airdrops/FikaAirdropsManager.cs index 62075618..b371fbc8 100644 --- a/Fika.Core/Coop/Airdrops/FikaAirdropsManager.cs +++ b/Fika.Core/Coop/Airdrops/FikaAirdropsManager.cs @@ -113,6 +113,7 @@ protected async void Start() AirdropParameters.DropHeight, AirdropParameters.Config.PlaneVolume, AirdropParameters.Config.PlaneSpeed); AirdropBox = await AirdropBox.Init(AirdropParameters.Config.CrateFallSpeed); + AirdropBox.container.Id = "FikaAirdropContainer"; factory = new FikaItemFactoryUtil(); } catch diff --git a/Fika.Core/Coop/ClientClasses/ClientMovementContext.cs b/Fika.Core/Coop/ClientClasses/ClientMovementContext.cs new file mode 100644 index 00000000..5e6a8973 --- /dev/null +++ b/Fika.Core/Coop/ClientClasses/ClientMovementContext.cs @@ -0,0 +1,38 @@ +using EFT; +using System; +using UnityEngine; + +namespace Fika.Core.Coop.ClientClasses +{ + public class ClientMovementContext : MovementContext + { + private bool doGravity; + + public new static ClientMovementContext Create(Player player, Func animatorGetter, Func characterControllerGetter, LayerMask groundMask) + { + ClientMovementContext movementContext = Create(player, animatorGetter, characterControllerGetter, groundMask); + return movementContext; + } + + public override void Init() + { + doGravity = true; + base.Init(); + } + + public override void ApplyGravity(ref Vector3 motion, float deltaTime, bool stickToGround) + { + if (!doGravity) + { + return; + } + + base.ApplyGravity(ref motion, deltaTime, stickToGround); + } + + public void SetGravity(bool enabled) + { + doGravity = enabled; + } + } +} diff --git a/Fika.Core/Coop/ClientClasses/CoopClientInventoryController.cs b/Fika.Core/Coop/ClientClasses/CoopClientInventoryController.cs index 9409d432..fc48617a 100644 --- a/Fika.Core/Coop/ClientClasses/CoopClientInventoryController.cs +++ b/Fika.Core/Coop/ClientClasses/CoopClientInventoryController.cs @@ -49,7 +49,7 @@ public override void Execute(GClass2854 operation, [CanBeNull] Callback callback // We use templateId because each client gets a unique itemId QuestItemPacket packet = new(CoopPlayer.Profile.Info.MainProfileNickname, lootedItem.TemplateId); - CoopPlayer.PacketSender.SendQuestItemPacket(ref packet); + CoopPlayer.PacketSender.SendQuestPacket(ref packet); } } base.Execute(operation, callback); diff --git a/Fika.Core/Coop/ClientClasses/CoopClientSharedQuestController.cs b/Fika.Core/Coop/ClientClasses/CoopClientSharedQuestController.cs index eca329ce..132e08e9 100644 --- a/Fika.Core/Coop/ClientClasses/CoopClientSharedQuestController.cs +++ b/Fika.Core/Coop/ClientClasses/CoopClientSharedQuestController.cs @@ -3,9 +3,9 @@ using EFT.Quests; using Fika.Core.Coop.Players; using Fika.Core.Networking.Packets; -using SPT.Custom.BTR.Patches; using System; using System.Collections.Generic; +using System.Linq; namespace Fika.Core.Coop.ClientClasses { @@ -16,6 +16,7 @@ public sealed class CoopClientSharedQuestController(Profile profile, InventoryCo private readonly List lastFromNetwork = []; private readonly HashSet acceptedTypes = []; private readonly HashSet lootedTemplateIds = []; + private readonly HashSet droppedZoneIds = []; private bool canSendAndReceive = true; public override void Init() @@ -48,6 +49,32 @@ public override void Init() } } + /// + /// Used to prevent errors when subscribing to the event + /// + public void LateInit() + { + if (acceptedTypes.Contains("PlaceBeacon")) + { + player.Profile.OnItemZoneDropped += Profile_OnItemZoneDropped; + } + } + + private void Profile_OnItemZoneDropped(string itemId, string zoneId) + { + if (droppedZoneIds.Contains(itemId)) + { + return; + } + + droppedZoneIds.Add(zoneId); + QuestDropItemPacket packet = new(player.Profile.Info.MainProfileNickname, itemId, zoneId); +#if DEBUG + FikaPlugin.Instance.FikaLogger.LogInfo("Profile_OnItemZoneDropped: Sending quest progress"); +#endif + player.PacketSender.SendQuestPacket(ref packet); + } + public override void OnConditionValueChanged(IConditionCounter conditional, EQuestStatus status, Condition condition, bool notify = true) { base.OnConditionValueChanged(conditional, status, condition, notify); @@ -93,6 +120,11 @@ public void ToggleQuestSharing(bool state) private void SendQuestPacket(IConditionCounter conditional, Condition condition) { + if (!canSendAndReceive) + { + return; + } + if (conditional is QuestClass quest) { TaskConditionCounterClass counter = quest.ConditionCountersManager.GetCounter(condition.id); @@ -136,7 +168,7 @@ internal void ReceiveQuestPacket(ref QuestConditionPacket packet) if (FikaPlugin.QuestSharingNotifications.Value) { NotificationManagerClass.DisplayMessageNotification($"Received shared quest progression from {packet.Nickname}", - iconType: EFT.Communications.ENotificationIconType.Quest); + iconType: EFT.Communications.ENotificationIconType.Quest); } } } @@ -167,13 +199,50 @@ internal void ReceiveQuestItemPacket(ref QuestItemPacket packet) if (FikaPlugin.QuestSharingNotifications.Value) { NotificationManagerClass.DisplayMessageNotification($"{packet.Nickname} picked up {item.Name.Localized()}", - iconType: EFT.Communications.ENotificationIconType.Quest); + iconType: EFT.Communications.ENotificationIconType.Quest); } } } } } + internal void ReceiveQuestDropItemPacket(ref QuestDropItemPacket packet) + { + if (!canSendAndReceive) + { + return; + } + + string itemId = packet.ItemId; + string zoneId = packet.ZoneId; + + if (DroppedItemAlreadyExists(itemId, zoneId)) + { + return; + } + + if (FikaPlugin.QuestSharingNotifications.Value) + { + NotificationManagerClass.DisplayMessageNotification($"{packet.Nickname} planted an item.", + iconType: EFT.Communications.ENotificationIconType.Quest); + } + + droppedZoneIds.Add(zoneId); + player.Profile.ItemDroppedAtPlace(itemId, zoneId); + } + + private bool DroppedItemAlreadyExists(string itemId, string zoneId) + { + if (player.Profile.EftStats.DroppedItems.Any(x => x.ItemId == itemId && x.ZoneId == zoneId) || droppedZoneIds.Contains(zoneId)) + { +#if DEBUG + FikaPlugin.Instance.FikaLogger.LogWarning($"Quest already existed in 'DroppedItems', itemId: {itemId}, zoneId: {zoneId}"); +#endif + return true; + } + return false; + } + /// /// Validates quest typing, some quests use CounterCreator which we also need to validate. /// diff --git a/Fika.Core/Coop/ClientClasses/NoInertiaMovementContext.cs b/Fika.Core/Coop/ClientClasses/NoInertiaMovementContext.cs index 0d39124d..edaa4a4c 100644 --- a/Fika.Core/Coop/ClientClasses/NoInertiaMovementContext.cs +++ b/Fika.Core/Coop/ClientClasses/NoInertiaMovementContext.cs @@ -8,7 +8,7 @@ namespace Fika.Core.Coop.ClientClasses /// /// Used to simulate having near no inertia /// - public class NoInertiaMovementContext : MovementContext + public class NoInertiaMovementContext : ClientMovementContext { public new static NoInertiaMovementContext Create(Player player, Func animatorGetter, Func characterControllerGetter, LayerMask groundMask) { diff --git a/Fika.Core/Coop/Custom/FikaHealthBar.cs b/Fika.Core/Coop/Custom/FikaHealthBar.cs index 6612a4ed..902d023d 100644 --- a/Fika.Core/Coop/Custom/FikaHealthBar.cs +++ b/Fika.Core/Coop/Custom/FikaHealthBar.cs @@ -3,11 +3,14 @@ using Comfort.Common; using EFT; using EFT.Animations; +using EFT.HealthSystem; using EFT.UI; using Fika.Core.Bundles; using Fika.Core.Coop.Players; using Fika.Core.Utils; using System; +using System.Collections.Generic; +using TMPro; using UnityEngine; using UnityEngine.UI; @@ -23,11 +26,17 @@ public class FikaHealthBar : MonoBehaviour private CoopPlayer mainPlayer; private PlayerPlateUI playerPlate; private float screenScale = 1f; + private Dictionary effectIcons; + private List effects; + private List ignoredTypes; // Check for GClass increments protected void Awake() { currentPlayer = GetComponent(); mainPlayer = (CoopPlayer)Singleton.Instance.MainPlayer; + effectIcons = EFTHardSettings.Instance.StaticIcons.EffectIcons.EffectIcons; + effects = []; + ignoredTypes = [typeof(GInterface279), typeof(GInterface281), typeof(GInterface282)]; CreateHealthBar(); } @@ -172,24 +181,128 @@ private void CreateHealthBar() SetPlayerPlateHealthVisibility(FikaPlugin.HideHealthBar.Value); playerPlate.gameObject.SetActive(FikaPlugin.UseNamePlates.Value); + if (FikaPlugin.ShowEffects.Value) + { + currentPlayer.HealthController.EffectAddedEvent += HealthController_EffectAddedEvent; + currentPlayer.HealthController.EffectRemovedEvent += HealthController_EffectRemovedEvent; + AddAllActiveEffects(); + } + FikaPlugin.UsePlateFactionSide.SettingChanged += UsePlateFactionSide_SettingChanged; FikaPlugin.HideHealthBar.SettingChanged += HideHealthBar_SettingChanged; FikaPlugin.UseNamePlates.SettingChanged += UseNamePlates_SettingChanged; + FikaPlugin.UseHealthNumber.SettingChanged += UseHealthNumber_SettingChanged; + FikaPlugin.ShowEffects.SettingChanged += ShowEffects_SettingChanged; currentPlayer.HealthController.HealthChangedEvent += HealthController_HealthChangedEvent; currentPlayer.HealthController.BodyPartDestroyedEvent += HealthController_BodyPartDestroyedEvent; currentPlayer.HealthController.BodyPartRestoredEvent += HealthController_BodyPartRestoredEvent; currentPlayer.HealthController.DiedEvent += HealthController_DiedEvent; + playerPlate.SetHealthNumberText("100%"); + UpdateHealth(); } + #region events + private void UseHealthNumber_SettingChanged(object sender, EventArgs e) + { + UpdateHealth(); + } + + private void HealthController_EffectRemovedEvent(IEffect effect) + { + for (int i = 0; i < effects.Count; i++) + { + HealthBarEffect currentEffect = effects[i]; + if (currentEffect.effectType == effect.Type) + { + currentEffect.DecreaseAmount(); + if (currentEffect.GetAmount() == 0) + { + currentEffect.Remove(); + effects.Remove(currentEffect); + } + break; + } + } + } + + private void HealthController_EffectAddedEvent(IEffect effect) + { + AddEffect(effect); + } + + private void AddEffect(IEffect effect) + { + if (ignoredTypes.Contains(effect.Type)) + { + return; + } + + bool found = false; + foreach (HealthBarEffect currentEffect in effects) + { + if (currentEffect.effectType == effect.Type) + { + currentEffect.IncreaseAmount(); + found = true; + } + } + + if (found) + { + return; + } + + if (effectIcons.TryGetValue(effect.Type, out Sprite effectSprite)) + { + GameObject newEffect = Instantiate(playerPlate.EffectImageTemplate, playerPlate.EffectsBackground.transform); + HealthBarEffect healthBarEffect = new(); + healthBarEffect.Init(newEffect, effect, effectSprite); + effects.Add(healthBarEffect); + } + } + + private void ShowEffects_SettingChanged(object sender, EventArgs e) + { + if (FikaPlugin.ShowEffects.Value) + { + currentPlayer.HealthController.EffectAddedEvent += HealthController_EffectAddedEvent; + currentPlayer.HealthController.EffectRemovedEvent += HealthController_EffectRemovedEvent; + AddAllActiveEffects(); + } + else + { + currentPlayer.HealthController.EffectAddedEvent -= HealthController_EffectAddedEvent; + currentPlayer.HealthController.EffectRemovedEvent -= HealthController_EffectRemovedEvent; + + List tempList = new(effects); + foreach (HealthBarEffect effect in tempList) + { + effect.Remove(); + } + effects.Clear(); + tempList.Clear(); + tempList = null; + } + } + + private void AddAllActiveEffects() + { + IEnumerable currentEffects = currentPlayer.HealthController.GetAllActiveEffects(); + foreach (IEffect effect in currentEffects) + { + AddEffect(effect); + } + } + private void HealthController_DiedEvent(EDamageType obj) { Destroy(this); } - private void HealthController_BodyPartRestoredEvent(EBodyPart arg1, EFT.HealthSystem.ValueStruct arg2) + private void HealthController_BodyPartRestoredEvent(EBodyPart arg1, ValueStruct arg2) { UpdateHealth(); } @@ -208,6 +321,7 @@ private void UsePlateFactionSide_SettingChanged(object sender, EventArgs e) { SetPlayerPlateFactionVisibility(FikaPlugin.UsePlateFactionSide.Value); } + private void HideHealthBar_SettingChanged(object sender, EventArgs e) { SetPlayerPlateHealthVisibility(FikaPlugin.HideHealthBar.Value); @@ -217,6 +331,7 @@ private void UseNamePlates_SettingChanged(object sender, EventArgs e) { playerPlate.gameObject.SetActive(FikaPlugin.UseNamePlates.Value); } + #endregion /// /// Updates the health on the HealthBar, this is invoked from events on the healthcontroller @@ -229,8 +344,7 @@ private void UpdateHealth() { if (!playerPlate.healthNumberBackgroundScreen.gameObject.activeSelf) { - playerPlate.healthNumberBackgroundScreen.gameObject.SetActive(true); - playerPlate.healthBarBackgroundScreen.gameObject.SetActive(false); + SetPlayerPlateHealthVisibility(false); } int healthNumberPercentage = (int)Math.Round(currentHealth / maxHealth * 100); playerPlate.SetHealthNumberText($"{healthNumberPercentage}%"); @@ -239,8 +353,7 @@ private void UpdateHealth() { if (!playerPlate.healthBarBackgroundScreen.gameObject.activeSelf) { - playerPlate.healthNumberBackgroundScreen.gameObject.SetActive(false); - playerPlate.healthBarBackgroundScreen.gameObject.SetActive(true); + SetPlayerPlateHealthVisibility(false); } float normalizedHealth = Mathf.Clamp01(currentHealth / maxHealth); @@ -302,14 +415,86 @@ protected void OnDestroy() FikaPlugin.UsePlateFactionSide.SettingChanged -= UsePlateFactionSide_SettingChanged; FikaPlugin.HideHealthBar.SettingChanged -= HideHealthBar_SettingChanged; FikaPlugin.UseNamePlates.SettingChanged -= UseNamePlates_SettingChanged; + FikaPlugin.UseHealthNumber.SettingChanged -= UseHealthNumber_SettingChanged; + FikaPlugin.ShowEffects.SettingChanged -= ShowEffects_SettingChanged; currentPlayer.HealthController.HealthChangedEvent -= HealthController_HealthChangedEvent; currentPlayer.HealthController.BodyPartDestroyedEvent -= HealthController_BodyPartDestroyedEvent; currentPlayer.HealthController.BodyPartRestoredEvent -= HealthController_BodyPartRestoredEvent; currentPlayer.HealthController.DiedEvent -= HealthController_DiedEvent; + currentPlayer.HealthController.EffectAddedEvent -= HealthController_EffectAddedEvent; + currentPlayer.HealthController.EffectRemovedEvent -= HealthController_EffectRemovedEvent; + + playerPlate.gameObject.SetActive(false); + effects.Clear(); Destroy(this); } + + private class HealthBarEffect + { + public Type effectType; + private int amount; + private GameObject effectObject; + private Image effectImage; + private TextMeshProUGUI tmpText; + + public void Init(GameObject initObject, IEffect effect, Sprite effectSprite) + { + effectObject = initObject; + effectObject.SetActive(true); + effectImage = effectObject.transform.GetChild(0).GetComponent(); + effectImage.sprite = effectSprite; + tmpText = effectObject.transform.GetChild(1).GetComponent(); + amount = 1; + tmpText.text = amount.ToString(); + tmpText.enabled = false; + effectType = effect.Type; + } + + public void Remove() + { + Destroy(effectImage); + Destroy(tmpText); + Destroy(effectObject); + } + + public int GetAmount() + { + return amount; + } + + public void IncreaseAmount() + { + amount++; + tmpText.text = amount.ToString(); + + if (amount > 1) + { + tmpText.enabled = true; + } + } + + public void DecreaseAmount() + { + amount = Math.Max(0, amount - 1); + + if (amount == 1) + { + tmpText.enabled = false; + } + + if (amount == 0) + { + Remove(); + return; + } + else + { + tmpText.text = amount.ToString(); + } + } + } } } \ No newline at end of file diff --git a/Fika.Core/Coop/Custom/PlayerPlateUI.cs b/Fika.Core/Coop/Custom/PlayerPlateUI.cs index 8063c70d..bf12ee56 100644 --- a/Fika.Core/Coop/Custom/PlayerPlateUI.cs +++ b/Fika.Core/Coop/Custom/PlayerPlateUI.cs @@ -28,6 +28,10 @@ public class PlayerPlateUI : MonoBehaviour public Image usecPlateScreen; [SerializeField] public Image bearPlateScreen; + [SerializeField] + public GameObject EffectsBackground; + [SerializeField] + public GameObject EffectImageTemplate; public void SetNameText(string text) { diff --git a/Fika.Core/Coop/GameMode/CoopGame.cs b/Fika.Core/Coop/GameMode/CoopGame.cs index e7bfb30e..31db13de 100644 --- a/Fika.Core/Coop/GameMode/CoopGame.cs +++ b/Fika.Core/Coop/GameMode/CoopGame.cs @@ -16,11 +16,11 @@ using EFT.UI.Matchmaker; using EFT.UI.Screens; using EFT.Weather; -using Fika.Core.Coop.BTR; using Fika.Core.Coop.ClientClasses; using Fika.Core.Coop.Components; using Fika.Core.Coop.Custom; using Fika.Core.Coop.FreeCamera; +using Fika.Core.Coop.Patches.Overrides; using Fika.Core.Coop.Players; using Fika.Core.Coop.Utils; using Fika.Core.Modding; @@ -74,6 +74,7 @@ public sealed class CoopGame : BaseLocalGame, IBotGame, IFik private CoopTimeManager timeManager; private FikaDebug fikaDebug; private bool isServer; + private List localTriggerZones; public FikaDynamicAI DynamicAI { get; private set; } public RaidSettings RaidSettings { get; private set; } @@ -127,10 +128,7 @@ internal static CoopGame Create(IInputTree inputTree, Profile profile, GameDateT Callback callback, float fixedDeltaTime, EUpdateQueue updateQueue, ISession backEndSession, TimeSpan sessionTime, RaidSettings raidSettings) { - Logger = BepInEx.Logging.Logger.CreateLogSource("CoopGame"); - - bool useCustomWeather = timeAndWeather.IsRandomWeather; - timeAndWeather.IsRandomWeather = false; + Logger = BepInEx.Logging.Logger.CreateLogSource("CoopGame"); CoopGame coopGame = smethod_0(inputTree, profile, backendDateTime, insurance, menuUI, gameUI, location, timeAndWeather, wavesSettings, dateTime, callback, fixedDeltaTime, updateQueue, backEndSession, @@ -149,12 +147,14 @@ internal static CoopGame Create(IInputTree inputTree, Profile profile, GameDateT BossLocationSpawn[] bossSpawns = LocalGame.smethod_8(wavesSettings, location.BossLocationSpawn); coopGame.BossSpawnWaveManagerClass = BossSpawnWaveManagerClass.smethod_0(bossSpawns, new Action(coopGame.botsController_0.ActivateBotsByWave)); - if (useCustomWeather && coopGame.isServer) + if (OfflineRaidSettingsMenuPatch_Override.UseRandomWeather && coopGame.isServer) { Logger.LogInfo("Custom weather enabled, initializing curves"); coopGame.SetupCustomWeather(timeAndWeather); } + OfflineRaidSettingsMenuPatch_Override.UseRandomWeather = false; + SetupGamePlayerOwnerHandler setupGamePlayerOwnerHandler = new(inputTree, insurance, backEndSession, gameUI, coopGame, location); coopGame.func_1 = new Func(setupGamePlayerOwnerHandler.HandleSetup); @@ -1534,7 +1534,7 @@ public override void vmethod_5() exfilManager = gameObject.AddComponent(); exfilManager.Run(exfilPoints); - if (FikaPlugin.Instance.UseBTR) + /*if (FikaPlugin.Instance.UseBTR) { try { @@ -1562,7 +1562,7 @@ public override void vmethod_5() { Logger.LogError("vmethod_5: Exception thrown during BTR init, check logs."); } - } + }*/ dateTime_0 = EFTDateTimeClass.Now; Status = GameStatus.Started; @@ -1599,10 +1599,17 @@ public void ResetExfilPointsFromServer(ExfiltrationPoint[] points) /// When the local player successfully extracts, enable freecam, notify other players about the extract /// /// The local player to start the Coroutine on + /// The point that was used to extract /// public void Extract(CoopPlayer player, ExfiltrationPoint point) { PreloaderUI preloaderUI = Singleton.Instance; + localTriggerZones = new(player.TriggerZones); + + player.ClientMovementContext.SetGravity(false); + Vector3 position = player.Position; + position.y += 500; + player.Teleport(position); if (MyExitStatus == ExitStatus.MissingInAction) { @@ -1730,7 +1737,7 @@ private IEnumerator ExtractRoutine(CoopPlayer player) } public void ClearHostAI(Player player) - { + {/* if (player != null) { if (botsController_0 != null) @@ -1752,7 +1759,8 @@ public void ClearHostAI(Player player) bot.Memory.DeleteInfoAboutEnemy(player); } } - } + }*/ + botsController_0.DestroyInfo(player); } /// @@ -1795,6 +1803,7 @@ public override void Stop(string profileId, ExitStatus exitStatus, string exitNa Logger.LogDebug("Stop"); CoopPlayer myPlayer = (CoopPlayer)Singleton.Instance.MainPlayer; + myPlayer.TriggerZones = localTriggerZones; myPlayer.PacketSender.DestroyThis(); if (myPlayer.Side != EPlayerSide.Savage) @@ -1826,7 +1835,6 @@ public override void Stop(string profileId, ExitStatus exitStatus, string exitNa if (isServer) { botsController_0.Stop(); - botsController_0.DestroyInfo(gparam_0.Player); } if (BossSpawnWaveManagerClass != null) diff --git a/Fika.Core/Coop/PacketHandlers/BotPacketSender.cs b/Fika.Core/Coop/PacketHandlers/BotPacketSender.cs index dda7cfd5..4538452b 100644 --- a/Fika.Core/Coop/PacketHandlers/BotPacketSender.cs +++ b/Fika.Core/Coop/PacketHandlers/BotPacketSender.cs @@ -3,7 +3,6 @@ using Comfort.Common; using Fika.Core.Coop.Players; using Fika.Core.Networking; -using Fika.Core.Networking.Packets; using LiteNetLib; using LiteNetLib.Utils; using System.Collections.Generic; @@ -36,12 +35,7 @@ public void Init() } - public void SendQuestPacket(ref QuestConditionPacket packet) - { - - } - - public void SendQuestItemPacket(ref QuestItemPacket packet) + public void SendQuestPacket(ref T packet) where T : INetSerializable { } diff --git a/Fika.Core/Coop/PacketHandlers/ClientPacketSender.cs b/Fika.Core/Coop/PacketHandlers/ClientPacketSender.cs index 0f65a5d0..dbacf71b 100644 --- a/Fika.Core/Coop/PacketHandlers/ClientPacketSender.cs +++ b/Fika.Core/Coop/PacketHandlers/ClientPacketSender.cs @@ -3,10 +3,10 @@ using Comfort.Common; using EFT; using EFT.Weather; +using Fika.Core.Coop.ClientClasses; using Fika.Core.Coop.GameMode; using Fika.Core.Coop.Players; using Fika.Core.Networking; -using Fika.Core.Networking.Packets; using LiteNetLib; using LiteNetLib.Utils; using System.Collections; @@ -41,17 +41,15 @@ protected void Awake() public void Init() { enabled = true; + if (player.AbstractQuestControllerClass is CoopClientSharedQuestController sharedQuestController) + { + sharedQuestController.LateInit(); + } StartCoroutine(SyncWorld()); StartCoroutine(SyncWeather()); } - public void SendQuestPacket(ref QuestConditionPacket packet) - { - Writer.Reset(); - Client.SendData(Writer, ref packet, DeliveryMethod.ReliableUnordered); - } - - public void SendQuestItemPacket(ref QuestItemPacket packet) + public void SendQuestPacket(ref T packet) where T : INetSerializable { Writer.Reset(); Client.SendData(Writer, ref packet, DeliveryMethod.ReliableUnordered); diff --git a/Fika.Core/Coop/PacketHandlers/IPacketSender.cs b/Fika.Core/Coop/PacketHandlers/IPacketSender.cs index 27ad5e43..dfb2c496 100644 --- a/Fika.Core/Coop/PacketHandlers/IPacketSender.cs +++ b/Fika.Core/Coop/PacketHandlers/IPacketSender.cs @@ -1,7 +1,6 @@ // © 2024 Lacyway All Rights Reserved using Fika.Core.Networking; -using Fika.Core.Networking.Packets; using LiteNetLib.Utils; using System.Collections.Generic; @@ -21,8 +20,7 @@ public interface IPacketSender public Queue HealthSyncPackets { get; set; } public void Init(); - public void SendQuestPacket(ref QuestConditionPacket packet); - public void SendQuestItemPacket(ref QuestItemPacket packet); + public void SendQuestPacket(ref T packet) where T : INetSerializable; public void DestroyThis(); } } diff --git a/Fika.Core/Coop/PacketHandlers/ObservedPacketSender.cs b/Fika.Core/Coop/PacketHandlers/ObservedPacketSender.cs index 7cdd48b5..e18b3e44 100644 --- a/Fika.Core/Coop/PacketHandlers/ObservedPacketSender.cs +++ b/Fika.Core/Coop/PacketHandlers/ObservedPacketSender.cs @@ -4,7 +4,6 @@ using Fika.Core.Coop.Players; using Fika.Core.Coop.Utils; using Fika.Core.Networking; -using Fika.Core.Networking.Packets; using LiteNetLib; using LiteNetLib.Utils; using System.Collections.Generic; @@ -46,12 +45,7 @@ public void Init() } - public void SendQuestPacket(ref QuestConditionPacket packet) - { - - } - - public void SendQuestItemPacket(ref QuestItemPacket packet) + public void SendQuestPacket(ref T packet) where T : INetSerializable { } diff --git a/Fika.Core/Coop/PacketHandlers/PacketReceiver.cs b/Fika.Core/Coop/PacketHandlers/PacketReceiver.cs index c5bf0534..bd8b878a 100644 --- a/Fika.Core/Coop/PacketHandlers/PacketReceiver.cs +++ b/Fika.Core/Coop/PacketHandlers/PacketReceiver.cs @@ -78,10 +78,7 @@ protected void Update() if (packet.TriggerZones.Length > 0) { observedPlayer.TriggerZones.Clear(); - foreach (string triggerZone in packet.TriggerZones) - { - observedPlayer.TriggerZones.Add(triggerZone); - } + observedPlayer.TriggerZones = new(packet.TriggerZones); } } observedPlayer.NetworkHealthController.HandleSyncPacket(packet.Packet); diff --git a/Fika.Core/Coop/PacketHandlers/ServerPacketSender.cs b/Fika.Core/Coop/PacketHandlers/ServerPacketSender.cs index d02c8a51..0dcaacf0 100644 --- a/Fika.Core/Coop/PacketHandlers/ServerPacketSender.cs +++ b/Fika.Core/Coop/PacketHandlers/ServerPacketSender.cs @@ -4,10 +4,10 @@ using Comfort.Common; using EFT; using EFT.MovingPlatforms; +using Fika.Core.Coop.ClientClasses; using Fika.Core.Coop.GameMode; using Fika.Core.Coop.Players; using Fika.Core.Networking; -using Fika.Core.Networking.Packets; using HarmonyLib; using LiteNetLib; using LiteNetLib.Utils; @@ -46,16 +46,14 @@ protected void Awake() public void Init() { enabled = true; + if (player.AbstractQuestControllerClass is CoopClientSharedQuestController sharedQuestController) + { + sharedQuestController.LateInit(); + } StartCoroutine(SendTrainTime()); } - public void SendQuestPacket(ref QuestConditionPacket packet) - { - Writer.Reset(); - Server.SendDataToAll(Writer, ref packet, DeliveryMethod.ReliableUnordered); - } - - public void SendQuestItemPacket(ref QuestItemPacket packet) + public void SendQuestPacket(ref T packet) where T : INetSerializable { Writer.Reset(); Server.SendDataToAll(Writer, ref packet, DeliveryMethod.ReliableUnordered); diff --git a/Fika.Core/Coop/Patches/Airdrop/AirdropBox_Patch.cs b/Fika.Core/Coop/Patches/Airdrop/AirdropBox_Patch.cs index 7307babd..543d0b39 100644 --- a/Fika.Core/Coop/Patches/Airdrop/AirdropBox_Patch.cs +++ b/Fika.Core/Coop/Patches/Airdrop/AirdropBox_Patch.cs @@ -14,7 +14,7 @@ public class AirdropBox_Patch : ModulePatch public static bool PatchPrefix(AirdropBox __instance) { //Allow method to go through - if(FikaBackendUtils.IsServer) + if (FikaBackendUtils.IsServer) { return true; } diff --git a/Fika.Core/Coop/Patches/Overrides/OfflineRaidSettingsMenuPatch_Override.cs b/Fika.Core/Coop/Patches/Overrides/OfflineRaidSettingsMenuPatch_Override.cs index cc624031..cf29ee69 100644 --- a/Fika.Core/Coop/Patches/Overrides/OfflineRaidSettingsMenuPatch_Override.cs +++ b/Fika.Core/Coop/Patches/Overrides/OfflineRaidSettingsMenuPatch_Override.cs @@ -1,4 +1,5 @@ -using EFT.UI; +using EFT; +using EFT.UI; using EFT.UI.Matchmaker; using HarmonyLib; using SPT.Reflection.Patching; @@ -16,13 +17,23 @@ protected override MethodBase GetTargetMethod() return AccessTools.Method(typeof(RaidSettingsWindow), nameof(RaidSettingsWindow.Show)); } - static RaidSettingsWindow instance; - static List weatherCanvasGroups; + private static RaidSettingsWindow instance; + private static List weatherCanvasGroups; + private static bool randomWeather; + public static bool UseRandomWeather + { + get => randomWeather; + set => randomWeather = value; + } [PatchPostfix] - private static void PatchPostfix(RaidSettingsWindow __instance, UiElementBlocker ____coopModeBlocker, List ____weatherCanvasGroups, - UpdatableToggle ____randomTimeToggle, UpdatableToggle ____randomWeatherToggle, List ____waterAndFoodCanvasGroups, List ____playersSpawnPlaceCanvasGroups, DropDownBox ____playersSpawnPlaceDropdown) + private static void PatchPostfix(RaidSettingsWindow __instance, UiElementBlocker ____coopModeBlocker, + List ____weatherCanvasGroups, UpdatableToggle ____randomTimeToggle, + UpdatableToggle ____randomWeatherToggle, List ____waterAndFoodCanvasGroups, + List ____playersSpawnPlaceCanvasGroups, DropDownBox ____playersSpawnPlaceDropdown, + RaidSettings raidSettings) { + randomWeather = false; // Always disable the Coop Mode checkbox ____coopModeBlocker.SetBlock(true, "Co-op is always enabled in Fika"); @@ -58,17 +69,21 @@ private static void PatchPostfix(RaidSettingsWindow __instance, UiElementBlocker instance = __instance; - ____randomWeatherToggle.Bind(new Action(ToggleWeather)); - ____randomTimeToggle.gameObject.GetComponent().SetUnlockStatus(false, false); - - GameObject weatherToggle = GameObject.Find("RandomWeatherCheckmark"); - if (weatherToggle != null) + // If enforced from server, this will be true by default + if (!raidSettings.TimeAndWeatherSettings.IsRandomWeather) { - CustomTextMeshProUGUI customTmp = weatherToggle.GetComponentInChildren(); - if (customTmp != null) + ____randomWeatherToggle.Bind(new Action(ToggleWeather)); + ____randomTimeToggle.gameObject.GetComponent().SetUnlockStatus(false, false); + + GameObject weatherToggle = GameObject.Find("RandomWeatherCheckmark"); + if (weatherToggle != null) { - customTmp.text = "Use custom weather"; - } + CustomTextMeshProUGUI customTmp = weatherToggle.GetComponentInChildren(); + if (customTmp != null) + { + customTmp.text = "Use custom weather"; + } + } } } @@ -89,6 +104,7 @@ private static void ToggleWeather(bool enabled) item.SetUnlockStatus(enabled, enabled); } + randomWeather = enabled; instance.method_4(); } } diff --git a/Fika.Core/Coop/Players/CoopBot.cs b/Fika.Core/Coop/Players/CoopBot.cs index 5afc3376..90e32ed1 100644 --- a/Fika.Core/Coop/Players/CoopBot.cs +++ b/Fika.Core/Coop/Players/CoopBot.cs @@ -3,7 +3,6 @@ using Comfort.Common; using EFT; using EFT.Ballistics; -using EFT.Counters; using EFT.HealthSystem; using EFT.InventoryLogic; using Fika.Core.Coop.BotClasses; diff --git a/Fika.Core/Coop/Players/CoopPlayer.cs b/Fika.Core/Coop/Players/CoopPlayer.cs index d6d4fe23..6a64a80d 100644 --- a/Fika.Core/Coop/Players/CoopPlayer.cs +++ b/Fika.Core/Coop/Players/CoopPlayer.cs @@ -50,6 +50,13 @@ public class CoopPlayer : LocalPlayer public int NetId; public bool IsObservedAI = false; public Dictionary> OperationCallbacks = []; + public ClientMovementContext ClientMovementContext + { + get + { + return MovementContext as ClientMovementContext; + } + } #endregion public static async Task Create(int playerId, Vector3 position, Quaternion rotation, @@ -129,7 +136,7 @@ public override void CreateMovementContext() LayerMask movement_MASK = EFTHardSettings.Instance.MOVEMENT_MASK; if (FikaPlugin.Instance.UseInertia) { - MovementContext = MovementContext.Create(this, new Func(GetBodyAnimatorCommon), + MovementContext = ClientMovementContext.Create(this, new Func(GetBodyAnimatorCommon), new Func(GetCharacterControllerCommon), movement_MASK); } else @@ -398,7 +405,7 @@ public override void Proceed(Item item, Callback callback, boo { // what is this base.Proceed(item, callback, scheduled); - } + } #endregion public override void DropCurrentController(Action callback, bool fastDrop, Item nextControllerItem = null) @@ -418,7 +425,7 @@ public override void DropCurrentController(Action callback, bool fastDrop, Item public override void OnBeenKilledByAggressor(IPlayer aggressor, DamageInfo damageInfo, EBodyPart bodyPart, EDamageType lethalDamageType) { - base.OnBeenKilledByAggressor(aggressor, damageInfo, bodyPart, lethalDamageType); + base.OnBeenKilledByAggressor(aggressor, damageInfo, bodyPart, lethalDamageType); // Handle 'Help Scav' rep gains if (aggressor is CoopPlayer coopPlayer) @@ -448,7 +455,7 @@ public override void ShowStringNotification(string message) ConsoleScreen.Log(message); FikaPlugin.Instance.FikaLogger.LogInfo(message); } - } + } #endif public override void SetInventoryOpened(bool opened) @@ -607,6 +614,13 @@ public override void vmethod_3(EGesture gesture) }); } + public override Corpse CreateCorpse() + { + Vector3 normalized = Velocity.normalized; + Vector3 velocityToApply = normalized.Equals(Vector3.up) ? normalized : Vector3.ClampMagnitude(Velocity, 2f); + return CreateCorpse(velocityToApply); + } + public override void ApplyCorpseImpulse() { Corpse.Ragdoll.ApplyImpulse(LastDamageInfo.HitCollider, LastDamageInfo.Direction, LastDamageInfo.HitPoint, _corpseAppliedForce); @@ -1020,7 +1034,7 @@ private string GetDogTagTemplateId() { if (Side is EPlayerSide.Usec) { - switch (Profile.Info.MemberCategory) + switch (Profile.Info.SelectedMemberCategory) { case EMemberCategory.Default: return "59f32c3b86f77472a31742f0"; @@ -1032,7 +1046,7 @@ private string GetDogTagTemplateId() } else if (Side is EPlayerSide.Bear) { - switch (Profile.Info.MemberCategory) + switch (Profile.Info.SelectedMemberCategory) { case EMemberCategory.Default: return "59f32bb586f774757e1e8442"; diff --git a/Fika.Core/Coop/Players/ObservedCoopPlayer.cs b/Fika.Core/Coop/Players/ObservedCoopPlayer.cs index 55b8b152..e7c61636 100644 --- a/Fika.Core/Coop/Players/ObservedCoopPlayer.cs +++ b/Fika.Core/Coop/Players/ObservedCoopPlayer.cs @@ -3,7 +3,6 @@ using Comfort.Common; using EFT; using EFT.Ballistics; -using EFT.Counters; using EFT.HealthSystem; using EFT.Interactive; using EFT.InventoryLogic; @@ -405,7 +404,9 @@ public override void SetControllerInsteadRemovedOne(Item removingItem, Callback public override Corpse CreateCorpse() { - return CreateCorpse(RagdollPacket.OverallVelocity); + Vector3 normalized = RagdollPacket.OverallVelocity.normalized; + Vector3 velocityToApply = normalized.Equals(Vector3.up) ? normalized : Vector3.ClampMagnitude(RagdollPacket.OverallVelocity, 2f); + return CreateCorpse(velocityToApply); } public override void ApplyCorpseImpulse() diff --git a/Fika.Core/Coop/Utils/NetManagerUtils.cs b/Fika.Core/Coop/Utils/NetManagerUtils.cs index 7f7032ec..175e2f2f 100644 --- a/Fika.Core/Coop/Utils/NetManagerUtils.cs +++ b/Fika.Core/Coop/Utils/NetManagerUtils.cs @@ -79,7 +79,7 @@ public static void DestroyNetManager(bool isServer) public static void DestroyPingingClient() { if (FikaGameObject != null) - { + { FikaPingingClient pingingClient = Singleton.Instance; pingingClient.StopKeepAliveRoutine(); pingingClient.NetClient.Stop(); diff --git a/Fika.Core/FikaPlugin.cs b/Fika.Core/FikaPlugin.cs index 46ff0054..5e08fb0f 100644 --- a/Fika.Core/FikaPlugin.cs +++ b/Fika.Core/FikaPlugin.cs @@ -118,6 +118,7 @@ public class FikaPlugin : BaseUnityPlugin public static ConfigEntry MaxDistanceToShow { get; set; } public static ConfigEntry MinimumOpacity { get; set; } public static ConfigEntry MinimumNamePlateScale { get; set; } + public static ConfigEntry ShowEffects { get; set; } // Coop | Quest Sharing public static ConfigEntry QuestTypesToShareAndReceive { get; set; } @@ -298,100 +299,139 @@ private void SetupConfig() { // Hidden - AcceptedTOS = Config.Bind("Hidden", "Accepted TOS", false, new ConfigDescription("Has accepted TOS", tags: new ConfigurationManagerAttributes() { Browsable = false })); + AcceptedTOS = Config.Bind("Hidden", "Accepted TOS", false, + new ConfigDescription("Has accepted TOS", tags: new ConfigurationManagerAttributes() { Browsable = false })); // Advanced - OfficialVersion = Config.Bind("Advanced", "Official Version", false, new ConfigDescription("Show official version instead of Fika version.", tags: new ConfigurationManagerAttributes() { IsAdvanced = true })); - // Coop - - ShowNotifications = Instance.Config.Bind("Coop", "Show Feed", true, new ConfigDescription("Enable custom notifications when a player dies, extracts, kills a boss, etc.", tags: new ConfigurationManagerAttributes() { Order = 7 })); + OfficialVersion = Config.Bind("Advanced", "Official Version", false, + new ConfigDescription("Show official version instead of Fika version.", tags: new ConfigurationManagerAttributes() { IsAdvanced = true })); - AutoExtract = Config.Bind("Coop", "Auto Extract", false, new ConfigDescription("Automatically extracts after the extraction countdown. As a host, this will only work if there are no clients connected.", tags: new ConfigurationManagerAttributes() { Order = 6 })); + // Coop - ShowExtractMessage = Config.Bind("Coop", "Show Extract Message", true, new ConfigDescription("Whether to show the extract message after dying/extracting.", tags: new ConfigurationManagerAttributes() { Order = 5 })); + ShowNotifications = Instance.Config.Bind("Coop", "Show Feed", true, + new ConfigDescription("Enable custom notifications when a player dies, extracts, kills a boss, etc.", tags: new ConfigurationManagerAttributes() { Order = 7 })); - //FasterInventoryScroll = Config.Bind("Coop", "Faster Inventory Scroll", false, new ConfigDescription("Toggle to increase the inventory scroll speed", tags: new ConfigurationManagerAttributes() { Order = 4 })); + AutoExtract = Config.Bind("Coop", "Auto Extract", false, + new ConfigDescription("Automatically extracts after the extraction countdown. As a host, this will only work if there are no clients connected.", tags: new ConfigurationManagerAttributes() { Order = 6 })); - //FasterInventoryScrollSpeed = Config.Bind("Coop", "Faster Inventory Scroll Speed", 63, new ConfigDescription("The speed at which the inventory scrolls at. Default is 63.", new AcceptableValueRange(63, 500), new ConfigurationManagerAttributes() { Order = 3 })); + ShowExtractMessage = Config.Bind("Coop", "Show Extract Message", true, + new ConfigDescription("Whether to show the extract message after dying/extracting.", tags: new ConfigurationManagerAttributes() { Order = 5 })); - ExtractKey = Config.Bind("Coop", "Extract Key", new KeyboardShortcut(KeyCode.F8), new ConfigDescription("The key used to extract from the raid.", tags: new ConfigurationManagerAttributes() { Order = 2 })); + ExtractKey = Config.Bind("Coop", "Extract Key", new KeyboardShortcut(KeyCode.F8), + new ConfigDescription("The key used to extract from the raid.", tags: new ConfigurationManagerAttributes() { Order = 2 })); - EnableChat = Config.Bind("Coop", "Enable Chat", false, new ConfigDescription("Toggle to enable chat in game. Cannot be change mid raid", tags: new ConfigurationManagerAttributes() { Order = 1 })); + EnableChat = Config.Bind("Coop", "Enable Chat", false, + new ConfigDescription("Toggle to enable chat in game. Cannot be change mid raid", tags: new ConfigurationManagerAttributes() { Order = 1 })); - ChatKey = Config.Bind("Coop", "Chat Key", new KeyboardShortcut(KeyCode.RightControl), new ConfigDescription("The key used to open the chat window.", tags: new ConfigurationManagerAttributes() { Order = 0 })); + ChatKey = Config.Bind("Coop", "Chat Key", new KeyboardShortcut(KeyCode.RightControl), + new ConfigDescription("The key used to open the chat window.", tags: new ConfigurationManagerAttributes() { Order = 0 })); // Coop | Name Plates - UseNamePlates = Config.Bind("Coop | Name Plates", "Show Player Name Plates", false, new ConfigDescription("Toggle Health-Bars & Names.", tags: new ConfigurationManagerAttributes() { Order = 11 })); + UseNamePlates = Config.Bind("Coop | Name Plates", "Show Player Name Plates", false, + new ConfigDescription("Toggle Health-Bars & Names.", tags: new ConfigurationManagerAttributes() { Order = 12 })); + + HideHealthBar = Config.Bind("Coop | Name Plates", "Hide Health Bar", false, + new ConfigDescription("Completely hides the health bar.", tags: new ConfigurationManagerAttributes() { Order = 11 })); - HideHealthBar = Config.Bind("Coop | Name Plates", "Hide Health Bar", false, new ConfigDescription("Completely hides the health bar.", tags: new ConfigurationManagerAttributes() { Order = 10 })); + UseHealthNumber = Config.Bind("Coop | Name Plates", "Show HP% instead of bar", false, + new ConfigDescription("Shows health in % amount instead of using the bar.", tags: new ConfigurationManagerAttributes() { Order = 10 })); - UseHealthNumber = Config.Bind("Coop | Name Plates", "Show HP% instead of bar", false, new ConfigDescription("Shows health in % amount instead of using the bar.", tags: new ConfigurationManagerAttributes() { Order = 9 })); + ShowEffects = Config.Bind("Coop | Name Plates", "Show Effects", true, + new ConfigDescription("If status effects should be displayed below the health bar.", tags: new ConfigurationManagerAttributes() { Order = 9 })); - UsePlateFactionSide = Config.Bind("Coop | Name Plates", "Show Player Faction Icon", true, new ConfigDescription("Shows the player faction icon next to the HP bar.", tags: new ConfigurationManagerAttributes() { Order = 8 })); + UsePlateFactionSide = Config.Bind("Coop | Name Plates", "Show Player Faction Icon", true, + new ConfigDescription("Shows the player faction icon next to the HP bar.", tags: new ConfigurationManagerAttributes() { Order = 8 })); - HideNamePlateInOptic = Config.Bind("Coop | Name Plates", "Hide Name Plate in Optic", true, new ConfigDescription("Hides the name plate when viewing through PiP scopes.", tags: new ConfigurationManagerAttributes() { Order = 7 })); + HideNamePlateInOptic = Config.Bind("Coop | Name Plates", "Hide Name Plate in Optic", true, + new ConfigDescription("Hides the name plate when viewing through PiP scopes.", tags: new ConfigurationManagerAttributes() { Order = 7 })); - NamePlateUseOpticZoom = Config.Bind("Coop | Name Plates", "Name Plates Use Optic Zoom", true, new ConfigDescription("If name plate location should be displayed using the PiP optic camera.", tags: new ConfigurationManagerAttributes() { Order = 6, IsAdvanced = true })); + NamePlateUseOpticZoom = Config.Bind("Coop | Name Plates", "Name Plates Use Optic Zoom", true, + new ConfigDescription("If name plate location should be displayed using the PiP optic camera.", tags: new ConfigurationManagerAttributes() { Order = 6, IsAdvanced = true })); - DecreaseOpacityNotLookingAt = Config.Bind("Coop | Name Plates", "Decrease Opacity In Peripheral", true, new ConfigDescription("Decreases the opacity of the name plates when not looking at a player.", tags: new ConfigurationManagerAttributes() { Order = 5 })); + DecreaseOpacityNotLookingAt = Config.Bind("Coop | Name Plates", "Decrease Opacity In Peripheral", true, + new ConfigDescription("Decreases the opacity of the name plates when not looking at a player.", tags: new ConfigurationManagerAttributes() { Order = 5 })); - NamePlateScale = Config.Bind("Coop | Name Plates", "Name Plate Scale", 0.22f, new ConfigDescription("Size of the name plates", new AcceptableValueRange(0.05f, 1f), new ConfigurationManagerAttributes() { Order = 4 })); + NamePlateScale = Config.Bind("Coop | Name Plates", "Name Plate Scale", 0.22f, + new ConfigDescription("Size of the name plates", new AcceptableValueRange(0.05f, 1f), new ConfigurationManagerAttributes() { Order = 4 })); - OpacityInADS = Config.Bind("Coop | Name Plates", "Opacity in ADS", 0.75f, new ConfigDescription("The opacity of the name plates when aiming down sights.", new AcceptableValueRange(0.1f, 1f), new ConfigurationManagerAttributes() { Order = 3 })); + OpacityInADS = Config.Bind("Coop | Name Plates", "Opacity in ADS", 0.75f, + new ConfigDescription("The opacity of the name plates when aiming down sights.", new AcceptableValueRange(0.1f, 1f), new ConfigurationManagerAttributes() { Order = 3 })); - MaxDistanceToShow = Config.Bind("Coop | Name Plates", "Max Distance to Show", 500f, new ConfigDescription("The maximum distance at which name plates will become invisible, starts to fade at half the input value.", new AcceptableValueRange(10f, 1000f), new ConfigurationManagerAttributes() { Order = 2 })); + MaxDistanceToShow = Config.Bind("Coop | Name Plates", "Max Distance to Show", 500f, + new ConfigDescription("The maximum distance at which name plates will become invisible, starts to fade at half the input value.", new AcceptableValueRange(10f, 1000f), new ConfigurationManagerAttributes() { Order = 2 })); - MinimumOpacity = Config.Bind("Coop | Name Plates", "Minimum Opacity", 0.1f, new ConfigDescription("The minimum opacity of the name plates.", new AcceptableValueRange(0.0f, 1f), new ConfigurationManagerAttributes() { Order = 1 })); + MinimumOpacity = Config.Bind("Coop | Name Plates", "Minimum Opacity", 0.1f, + new ConfigDescription("The minimum opacity of the name plates.", new AcceptableValueRange(0.0f, 1f), new ConfigurationManagerAttributes() { Order = 1 })); - MinimumNamePlateScale = Config.Bind("Coop | Name Plates", "Minimum Name Plate Scale", 0.01f, new ConfigDescription("The minimum scale of the name plates.", new AcceptableValueRange(0.0f, 1f), new ConfigurationManagerAttributes() { Order = 0 })); + MinimumNamePlateScale = Config.Bind("Coop | Name Plates", "Minimum Name Plate Scale", 0.01f, + new ConfigDescription("The minimum scale of the name plates.", new AcceptableValueRange(0.0f, 1f), new ConfigurationManagerAttributes() { Order = 0 })); // Coop | Quest Sharing - QuestTypesToShareAndReceive = Config.Bind("Coop | Quest Sharing", "Quest Types", EQuestSharingTypes.All, new ConfigDescription("Which quest types to receive and send.", tags: new ConfigurationManagerAttributes() { Order = 2 })); + QuestTypesToShareAndReceive = Config.Bind("Coop | Quest Sharing", "Quest Types", EQuestSharingTypes.All, + new ConfigDescription("Which quest types to receive and send. PlaceBeacon is both markers and items.", tags: new ConfigurationManagerAttributes() { Order = 2 })); - QuestSharingNotifications = Config.Bind("Coop | Quest Sharing", "Show Notifications", true, new ConfigDescription("If a notification should be shown when quest progress is shared with out.", tags: new ConfigurationManagerAttributes() { Order = 1 })); + QuestSharingNotifications = Config.Bind("Coop | Quest Sharing", "Show Notifications", true, + new ConfigDescription("If a notification should be shown when quest progress is shared with out.", tags: new ConfigurationManagerAttributes() { Order = 1 })); // Coop | Custom - UsePingSystem = Config.Bind("Coop | Custom", "Ping System", false, new ConfigDescription("Toggle Ping System. If enabled you can receive and send pings by pressing the ping key.", tags: new ConfigurationManagerAttributes() { Order = 9 })); + UsePingSystem = Config.Bind("Coop | Custom", "Ping System", false, + new ConfigDescription("Toggle Ping System. If enabled you can receive and send pings by pressing the ping key.", tags: new ConfigurationManagerAttributes() { Order = 9 })); - PingButton = Config.Bind("Coop | Custom", "Ping Button", new KeyboardShortcut(KeyCode.U), new ConfigDescription("Button used to send pings.", tags: new ConfigurationManagerAttributes() { Order = 8 })); + PingButton = Config.Bind("Coop | Custom", "Ping Button", new KeyboardShortcut(KeyCode.U), + new ConfigDescription("Button used to send pings.", tags: new ConfigurationManagerAttributes() { Order = 8 })); - PingColor = Config.Bind("Coop | Custom", "Ping Color", Color.white, new ConfigDescription("The color of your pings when displayed for other players.", tags: new ConfigurationManagerAttributes() { Order = 7 })); + PingColor = Config.Bind("Coop | Custom", "Ping Color", Color.white, + new ConfigDescription("The color of your pings when displayed for other players.", tags: new ConfigurationManagerAttributes() { Order = 7 })); - PingSize = Config.Bind("Coop | Custom", "Ping Size", 1f, new ConfigDescription("The multiplier of the ping size.", new AcceptableValueRange(0.1f, 2f), new ConfigurationManagerAttributes() { Order = 6 })); + PingSize = Config.Bind("Coop | Custom", "Ping Size", 1f, + new ConfigDescription("The multiplier of the ping size.", new AcceptableValueRange(0.1f, 2f), new ConfigurationManagerAttributes() { Order = 6 })); - PingTime = Config.Bind("Coop | Custom", "Ping Time", 3, new ConfigDescription("How long pings should be displayed.", new AcceptableValueRange(2, 10), new ConfigurationManagerAttributes() { Order = 5 })); + PingTime = Config.Bind("Coop | Custom", "Ping Time", 3, + new ConfigDescription("How long pings should be displayed.", new AcceptableValueRange(2, 10), new ConfigurationManagerAttributes() { Order = 5 })); - PlayPingAnimation = Config.Bind("Coop | Custom", "Play Ping Animation", false, new ConfigDescription("Plays the pointing animation automatically when pinging. Can interfere with gameplay.", tags: new ConfigurationManagerAttributes() { Order = 4 })); + PlayPingAnimation = Config.Bind("Coop | Custom", "Play Ping Animation", false, + new ConfigDescription("Plays the pointing animation automatically when pinging. Can interfere with gameplay.", tags: new ConfigurationManagerAttributes() { Order = 4 })); - ShowPingDuringOptics = Config.Bind("Coop | Custom", "Show Ping During Optics", false, new ConfigDescription("If pings should be displayed while aiming down an optics scope.", tags: new ConfigurationManagerAttributes() { Order = 3 })); + ShowPingDuringOptics = Config.Bind("Coop | Custom", "Show Ping During Optics", false, + new ConfigDescription("If pings should be displayed while aiming down an optics scope.", tags: new ConfigurationManagerAttributes() { Order = 3 })); - PingUseOpticZoom = Config.Bind("Coop | Custom", "Ping Use Optic Zoom", true, new ConfigDescription("If ping location should be displayed using the PiP optic camera.", tags: new ConfigurationManagerAttributes() { Order = 2, IsAdvanced = true })); + PingUseOpticZoom = Config.Bind("Coop | Custom", "Ping Use Optic Zoom", true, + new ConfigDescription("If ping location should be displayed using the PiP optic camera.", tags: new ConfigurationManagerAttributes() { Order = 2, IsAdvanced = true })); - PingScaleWithDistance = Config.Bind("Coop | Custom", "Ping Scale With Distance", true, new ConfigDescription("If ping size should scale with distance from player.", tags: new ConfigurationManagerAttributes() { Order = 1, IsAdvanced = true })); + PingScaleWithDistance = Config.Bind("Coop | Custom", "Ping Scale With Distance", true, + new ConfigDescription("If ping size should scale with distance from player.", tags: new ConfigurationManagerAttributes() { Order = 1, IsAdvanced = true })); - PingMinimumOpacity = Config.Bind("Coop | Custom", "Ping Minimum Opacity", 0.05f, new ConfigDescription("The minimum opacity of pings when looking straight at them.", new AcceptableValueRange(0f, 0.5f), new ConfigurationManagerAttributes() { Order = 0, IsAdvanced = true })); + PingMinimumOpacity = Config.Bind("Coop | Custom", "Ping Minimum Opacity", 0.05f, + new ConfigDescription("The minimum opacity of pings when looking straight at them.", new AcceptableValueRange(0f, 0.5f), new ConfigurationManagerAttributes() { Order = 0, IsAdvanced = true })); // Coop | Debug - FreeCamButton = Config.Bind("Coop | Debug", "Free Camera Button", new KeyboardShortcut(KeyCode.F9), "Button used to toggle free camera."); + FreeCamButton = Config.Bind("Coop | Debug", "Free Camera Button", new KeyboardShortcut(KeyCode.F9), + "Button used to toggle free camera."); - AZERTYMode = Config.Bind("Coop | Debug", "AZERTY Mode", false, "If free camera should use AZERTY keys for input."); + AZERTYMode = Config.Bind("Coop | Debug", "AZERTY Mode", false, + "If free camera should use AZERTY keys for input."); - KeybindOverlay = Config.Bind("Coop | Debug", "Keybind Overlay", true, "If an overlay with all free cam keybinds should show."); + KeybindOverlay = Config.Bind("Coop | Debug", "Keybind Overlay", true, + "If an overlay with all free cam keybinds should show."); // Performance - DynamicAI = Config.Bind("Performance", "Dynamic AI", false, new ConfigDescription("Use the dynamic AI system, disabling AI when they are outside of any player's range.", tags: new ConfigurationManagerAttributes() { Order = 3 })); + DynamicAI = Config.Bind("Performance", "Dynamic AI", false, + new ConfigDescription("Use the dynamic AI system, disabling AI when they are outside of any player's range.", tags: new ConfigurationManagerAttributes() { Order = 3 })); - DynamicAIRange = Config.Bind("Performance", "Dynamic AI Range", 100f, new ConfigDescription("The range at which AI will be disabled dynamically.", new AcceptableValueRange(150f, 1000f), new ConfigurationManagerAttributes() { Order = 2 })); + DynamicAIRange = Config.Bind("Performance", "Dynamic AI Range", 100f, + new ConfigDescription("The range at which AI will be disabled dynamically.", new AcceptableValueRange(150f, 1000f), new ConfigurationManagerAttributes() { Order = 2 })); - DynamicAIRate = Config.Bind("Performance", "Dynamic AI Rate", EDynamicAIRates.Medium, new ConfigDescription("How often DynamicAI should scan for the range from all players.", tags: new ConfigurationManagerAttributes() { Order = 1 })); + DynamicAIRate = Config.Bind("Performance", "Dynamic AI Rate", EDynamicAIRates.Medium, + new ConfigDescription("How often DynamicAI should scan for the range from all players.", tags: new ConfigurationManagerAttributes() { Order = 1 })); - DynamicAIIgnoreSnipers = Config.Bind("Performance", "Dynamic AI - Ignore Snipers", true, new ConfigDescription("Whether Dynamic AI should ignore sniper scavs.", tags: new ConfigurationManagerAttributes() { Order = 0 })); + DynamicAIIgnoreSnipers = Config.Bind("Performance", "Dynamic AI - Ignore Snipers", true, + new ConfigDescription("Whether Dynamic AI should ignore sniper scavs.", tags: new ConfigurationManagerAttributes() { Order = 0 })); //CullPlayers = Config.Bind("Performance", "Culling System", true, new ConfigDescription("Whether to use the culling system or not. When players are outside of the culling range, their animations will be simplified. This can dramatically improve performance in certain scenarios.", tags: new ConfigurationManagerAttributes() { Order = 2 })); @@ -399,59 +439,84 @@ private void SetupConfig() // Performance | Max Bots - EnforcedSpawnLimits = Config.Bind("Performance | Max Bots", "Enforced Spawn Limits", false, new ConfigDescription("Enforces spawn limits when spawning bots, making sure to not go over the vanilla limits. This mainly takes affect when using spawn mods or anything that modifies the bot limits. Will not block spawns of special bots like bosses.", tags: new ConfigurationManagerAttributes() { Order = 14 })); + EnforcedSpawnLimits = Config.Bind("Performance | Max Bots", "Enforced Spawn Limits", false, + new ConfigDescription("Enforces spawn limits when spawning bots, making sure to not go over the vanilla limits. This mainly takes affect when using spawn mods or anything that modifies the bot limits. Will not block spawns of special bots like bosses.", tags: new ConfigurationManagerAttributes() { Order = 14 })); - DespawnFurthest = Config.Bind("Performance | Max Bots", "Despawn Furthest", false, new ConfigDescription("When enforcing spawn limits, should the furthest bot be de-spawned instead of blocking the spawn. This will make for a much more active raid on a lower Max Bots count. Helpful for weaker PCs. Will only despawn pmcs and scavs. If you don't run a dynamic spawn mod, this will however quickly exhaust the spawns on the map, making the raid very dead instead.", tags: new ConfigurationManagerAttributes() { Order = 13 })); + DespawnFurthest = Config.Bind("Performance | Max Bots", "Despawn Furthest", false, + new ConfigDescription("When enforcing spawn limits, should the furthest bot be de-spawned instead of blocking the spawn. This will make for a much more active raid on a lower Max Bots count. Helpful for weaker PCs. Will only despawn pmcs and scavs. If you don't run a dynamic spawn mod, this will however quickly exhaust the spawns on the map, making the raid very dead instead.", tags: new ConfigurationManagerAttributes() { Order = 13 })); - DespawnMinimumDistance = Config.Bind("Performance | Max Bots", "Despawn Minimum Distance", 200.0f, new ConfigDescription("Don't despawn bots within this distance.", new AcceptableValueRange(50f, 3000f), new ConfigurationManagerAttributes() { Order = 12 })); + DespawnMinimumDistance = Config.Bind("Performance | Max Bots", "Despawn Minimum Distance", 200.0f, + new ConfigDescription("Don't despawn bots within this distance.", new AcceptableValueRange(50f, 3000f), new ConfigurationManagerAttributes() { Order = 12 })); - MaxBotsFactory = Config.Bind("Performance | Max Bots", "Max Bots Factory", 0, new ConfigDescription("Max amount of bots that can be active at the same time on Factory. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", new AcceptableValueRange(0, 50), new ConfigurationManagerAttributes() { Order = 11 })); + MaxBotsFactory = Config.Bind("Performance | Max Bots", "Max Bots Factory", 0, + new ConfigDescription("Max amount of bots that can be active at the same time on Factory. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", new AcceptableValueRange(0, 50), new ConfigurationManagerAttributes() { Order = 11 })); - MaxBotsCustoms = Config.Bind("Performance | Max Bots", "Max Bots Customs", 0, new ConfigDescription("Max amount of bots that can be active at the same time on Customs. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", new AcceptableValueRange(0, 50), new ConfigurationManagerAttributes() { Order = 10 })); + MaxBotsCustoms = Config.Bind("Performance | Max Bots", "Max Bots Customs", 0, + new ConfigDescription("Max amount of bots that can be active at the same time on Customs. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", new AcceptableValueRange(0, 50), new ConfigurationManagerAttributes() { Order = 10 })); - MaxBotsInterchange = Config.Bind("Performance | Max Bots", "Max Bots Interchange", 0, new ConfigDescription("Max amount of bots that can be active at the same time on Interchange. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", new AcceptableValueRange(0, 50), new ConfigurationManagerAttributes() { Order = 8 })); + MaxBotsInterchange = Config.Bind("Performance | Max Bots", "Max Bots Interchange", 0, + new ConfigDescription("Max amount of bots that can be active at the same time on Interchange. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", new AcceptableValueRange(0, 50), new ConfigurationManagerAttributes() { Order = 8 })); - MaxBotsReserve = Config.Bind("Performance | Max Bots", "Max Bots Reserve", 0, new ConfigDescription("Max amount of bots that can be active at the same time on Reserve. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", new AcceptableValueRange(0, 50), new ConfigurationManagerAttributes() { Order = 7 })); + MaxBotsReserve = Config.Bind("Performance | Max Bots", "Max Bots Reserve", 0, + new ConfigDescription("Max amount of bots that can be active at the same time on Reserve. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", new AcceptableValueRange(0, 50), new ConfigurationManagerAttributes() { Order = 7 })); - MaxBotsWoods = Config.Bind("Performance | Max Bots", "Max Bots Woods", 0, new ConfigDescription("Max amount of bots that can be active at the same time on Woods. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", new AcceptableValueRange(0, 50), new ConfigurationManagerAttributes() { Order = 6 })); + MaxBotsWoods = Config.Bind("Performance | Max Bots", "Max Bots Woods", 0, + new ConfigDescription("Max amount of bots that can be active at the same time on Woods. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", new AcceptableValueRange(0, 50), new ConfigurationManagerAttributes() { Order = 6 })); - MaxBotsShoreline = Config.Bind("Performance | Max Bots", "Max Bots Shoreline", 0, new ConfigDescription("Max amount of bots that can be active at the same time on Shoreline. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", new AcceptableValueRange(0, 50), new ConfigurationManagerAttributes() { Order = 5 })); + MaxBotsShoreline = Config.Bind("Performance | Max Bots", "Max Bots Shoreline", 0, + new ConfigDescription("Max amount of bots that can be active at the same time on Shoreline. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", new AcceptableValueRange(0, 50), new ConfigurationManagerAttributes() { Order = 5 })); - MaxBotsStreets = Config.Bind("Performance | Max Bots", "Max Bots Streets", 0, new ConfigDescription("Max amount of bots that can be active at the same time on Streets of Tarkov. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", new AcceptableValueRange(0, 50), new ConfigurationManagerAttributes() { Order = 4 })); + MaxBotsStreets = Config.Bind("Performance | Max Bots", "Max Bots Streets", 0, + new ConfigDescription("Max amount of bots that can be active at the same time on Streets of Tarkov. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", new AcceptableValueRange(0, 50), new ConfigurationManagerAttributes() { Order = 4 })); - MaxBotsGroundZero = Config.Bind("Performance | Max Bots", "Max Bots Ground Zero", 0, new ConfigDescription("Max amount of bots that can be active at the same time on Ground Zero. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", new AcceptableValueRange(0, 50), new ConfigurationManagerAttributes() { Order = 3 })); + MaxBotsGroundZero = Config.Bind("Performance | Max Bots", "Max Bots Ground Zero", 0, + new ConfigDescription("Max amount of bots that can be active at the same time on Ground Zero. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", new AcceptableValueRange(0, 50), new ConfigurationManagerAttributes() { Order = 3 })); - MaxBotsLabs = Config.Bind("Performance | Max Bots", "Max Bots Labs", 0, new ConfigDescription("Max amount of bots that can be active at the same time on Labs. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", new AcceptableValueRange(0, 50), new ConfigurationManagerAttributes() { Order = 2 })); + MaxBotsLabs = Config.Bind("Performance | Max Bots", "Max Bots Labs", 0, + new ConfigDescription("Max amount of bots that can be active at the same time on Labs. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", new AcceptableValueRange(0, 50), new ConfigurationManagerAttributes() { Order = 2 })); - MaxBotsLighthouse = Config.Bind("Performance | Max Bots", "Max Bots Lighthouse", 0, new ConfigDescription("Max amount of bots that can be active at the same time on Lighthouse. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", new AcceptableValueRange(0, 50), new ConfigurationManagerAttributes() { Order = 1 })); + MaxBotsLighthouse = Config.Bind("Performance | Max Bots", "Max Bots Lighthouse", 0, + new ConfigDescription("Max amount of bots that can be active at the same time on Lighthouse. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", new AcceptableValueRange(0, 50), new ConfigurationManagerAttributes() { Order = 1 })); // Network - NativeSockets = Config.Bind(section: "Network", "Native Sockets", false, new ConfigDescription("Use NativeSockets for gameplay traffic. This uses direct socket calls for send/receive to drastically increase speed and reduce GC pressure. Only for Windows/Linux and might not always work.", tags: new ConfigurationManagerAttributes() { Order = 8 })); + NativeSockets = Config.Bind(section: "Network", "Native Sockets", false, + new ConfigDescription("Use NativeSockets for gameplay traffic. This uses direct socket calls for send/receive to drastically increase speed and reduce GC pressure. Only for Windows/Linux and might not always work.", tags: new ConfigurationManagerAttributes() { Order = 8 })); - ForceIP = Config.Bind("Network", "Force IP", "", new ConfigDescription("Forces the server when hosting to use this IP when broadcasting to the backend instead of automatically trying to fetch it. Leave empty to disable.", tags: new ConfigurationManagerAttributes() { Order = 7 })); + ForceIP = Config.Bind("Network", "Force IP", "", + new ConfigDescription("Forces the server when hosting to use this IP when broadcasting to the backend instead of automatically trying to fetch it. Leave empty to disable.", tags: new ConfigurationManagerAttributes() { Order = 7 })); - ForceBindIP = Config.Bind("Network", "Force Bind IP", "", new ConfigDescription("Forces the server when hosting to use this local IP when starting the server. Useful if you are hosting on a VPN.", new AcceptableValueList(GetLocalAddresses()), new ConfigurationManagerAttributes() { Order = 6 })); + ForceBindIP = Config.Bind("Network", "Force Bind IP", "", + new ConfigDescription("Forces the server when hosting to use this local IP when starting the server. Useful if you are hosting on a VPN.", new AcceptableValueList(GetLocalAddresses()), new ConfigurationManagerAttributes() { Order = 6 })); - AutoRefreshRate = Config.Bind("Network", "Auto Server Refresh Rate", 10f, new ConfigDescription("Every X seconds the client will ask the server for the list of matches while at the lobby screen.", new AcceptableValueRange(3f, 60f), new ConfigurationManagerAttributes() { Order = 5 })); + AutoRefreshRate = Config.Bind("Network", "Auto Server Refresh Rate", 10f, + new ConfigDescription("Every X seconds the client will ask the server for the list of matches while at the lobby screen.", new AcceptableValueRange(3f, 60f), new ConfigurationManagerAttributes() { Order = 5 })); - UDPPort = Config.Bind("Network", "UDP Port", 25565, new ConfigDescription("Port to use for UDP gameplay packets.", tags: new ConfigurationManagerAttributes() { Order = 4 })); + UDPPort = Config.Bind("Network", "UDP Port", 25565, + new ConfigDescription("Port to use for UDP gameplay packets.", tags: new ConfigurationManagerAttributes() { Order = 4 })); - UseUPnP = Config.Bind("Network", "Use UPnP", false, new ConfigDescription("Attempt to open ports using UPnP. Useful if you cannot open ports yourself but the router supports UPnP.", tags: new ConfigurationManagerAttributes() { Order = 3 })); + UseUPnP = Config.Bind("Network", "Use UPnP", false, + new ConfigDescription("Attempt to open ports using UPnP. Useful if you cannot open ports yourself but the router supports UPnP.", tags: new ConfigurationManagerAttributes() { Order = 3 })); - UseNatPunching = Config.Bind("Network", "Use NAT Punching", false, new ConfigDescription("Use NAT punching when hosting a raid. Only works with fullcone NAT type routers and requires NatPunchServer to be running on the SPT server. UPnP, Force IP and Force Bind IP are disabled with this mode.", tags: new ConfigurationManagerAttributes() { Order = 2 })); + UseNatPunching = Config.Bind("Network", "Use NAT Punching", false, + new ConfigDescription("Use NAT punching when hosting a raid. Only works with fullcone NAT type routers and requires NatPunchServer to be running on the SPT server. UPnP, Force IP and Force Bind IP are disabled with this mode.", tags: new ConfigurationManagerAttributes() { Order = 2 })); - ConnectionTimeout = Config.Bind("Network", "Connection Timeout", 15, new ConfigDescription("How long it takes for a connection to be considered dropped if no packets are received.", new AcceptableValueRange(5, 60), new ConfigurationManagerAttributes() { Order = 1 })); + ConnectionTimeout = Config.Bind("Network", "Connection Timeout", 15, + new ConfigDescription("How long it takes for a connection to be considered dropped if no packets are received.", new AcceptableValueRange(5, 60), new ConfigurationManagerAttributes() { Order = 1 })); // Gameplay - HeadDamageMultiplier = Config.Bind("Gameplay", "Head Damage Multiplier", 1f, new ConfigDescription("X multiplier to damage taken on the head collider. 0.2 = 20%", new AcceptableValueRange(0.05f, 1f), new ConfigurationManagerAttributes() { Order = 4 })); + HeadDamageMultiplier = Config.Bind("Gameplay", "Head Damage Multiplier", 1f, + new ConfigDescription("X multiplier to damage taken on the head collider. 0.2 = 20%", new AcceptableValueRange(0.05f, 1f), new ConfigurationManagerAttributes() { Order = 4 })); - ArmpitDamageMultiplier = Config.Bind("Gameplay", "Armpit Damage Multiplier", 1f, new ConfigDescription("X multiplier to damage taken on the armpits collider. 0.2 = 20%", new AcceptableValueRange(0.05f, 1f), new ConfigurationManagerAttributes() { Order = 3 })); + ArmpitDamageMultiplier = Config.Bind("Gameplay", "Armpit Damage Multiplier", 1f, + new ConfigDescription("X multiplier to damage taken on the armpits collider. 0.2 = 20%", new AcceptableValueRange(0.05f, 1f), new ConfigurationManagerAttributes() { Order = 3 })); - StomachDamageMultiplier = Config.Bind("Gameplay", "Stomach Damage Multiplier", 1f, new ConfigDescription("X multiplier to damage taken on the stomach collider. 0.2 = 20%", new AcceptableValueRange(0.05f, 1f), new ConfigurationManagerAttributes() { Order = 2 })); + StomachDamageMultiplier = Config.Bind("Gameplay", "Stomach Damage Multiplier", 1f, + new ConfigDescription("X multiplier to damage taken on the stomach collider. 0.2 = 20%", new AcceptableValueRange(0.05f, 1f), new ConfigurationManagerAttributes() { Order = 2 })); - DisableBotMetabolism = Config.Bind("Gameplay", "Disable Bot Metabolism", false, new ConfigDescription("Disables metabolism on bots, preventing them from dying from loss of energy/hydration during long raids.", tags: new ConfigurationManagerAttributes() { Order = 1 })); + DisableBotMetabolism = Config.Bind("Gameplay", "Disable Bot Metabolism", false, + new ConfigDescription("Disables metabolism on bots, preventing them from dying from loss of energy/hydration during long raids.", tags: new ConfigurationManagerAttributes() { Order = 1 })); } private void OfficialVersion_SettingChanged(object sender, EventArgs e) @@ -516,6 +581,7 @@ private void DisableSPTPatches() new OfflineSaveProfilePatch().Disable(); // We handle this with our own exit manager new ScavRepAdjustmentPatch().Disable(); new DisablePvEPatch().Disable(); + new ClampRagdollPatch().Disable(); new BTRInteractionPatch().Disable(); new BTRExtractPassengersPatch().Disable(); diff --git a/Fika.Core/Networking/FikaClient.cs b/Fika.Core/Networking/FikaClient.cs index cd65db1f..6d825ea3 100644 --- a/Fika.Core/Networking/FikaClient.cs +++ b/Fika.Core/Networking/FikaClient.cs @@ -70,6 +70,9 @@ public bool Started public void Init() { + NetworkGameSession.RTT = 0; + NetworkGameSession.LossPercent = 0; + packetProcessor.SubscribeNetSerializable(OnPlayerStatePacketReceived); packetProcessor.SubscribeNetSerializable(OnGameTimerPacketReceived); packetProcessor.SubscribeNetSerializable(OnFirearmPacketReceived); @@ -96,6 +99,7 @@ public void Init() packetProcessor.SubscribeNetSerializable(OnTextMessagePacketReceived); packetProcessor.SubscribeNetSerializable(OnQuestConditionPacketReceived); packetProcessor.SubscribeNetSerializable(OnQuestItemPacketReceived); + packetProcessor.SubscribeNetSerializable(OnQuestDropItemPacketReceived); _netClient = new NetManager(this) { @@ -130,6 +134,17 @@ public void Init() FikaEventDispatcher.DispatchEvent(new FikaClientCreatedEvent(this)); } + private void OnQuestDropItemPacketReceived(QuestDropItemPacket packet, NetPeer peer) + { + if (MyPlayer.HealthController.IsAlive) + { + if (MyPlayer.AbstractQuestControllerClass is CoopClientSharedQuestController sharedQuestController) + { + sharedQuestController.ReceiveQuestDropItemPacket(ref packet); + } + } + } + private void OnQuestItemPacketReceived(QuestItemPacket packet) { if (MyPlayer.HealthController.IsAlive) @@ -168,7 +183,7 @@ public void SetupGameVariables(CoopPlayer coopPlayer) MyPlayer = coopPlayer; if (FikaPlugin.EnableChat.Value) { - fikaChat = gameObject.AddComponent(); + fikaChat = gameObject.AddComponent(); } } diff --git a/Fika.Core/Networking/FikaServer.cs b/Fika.Core/Networking/FikaServer.cs index 1bf5a15e..83ed06bb 100644 --- a/Fika.Core/Networking/FikaServer.cs +++ b/Fika.Core/Networking/FikaServer.cs @@ -77,6 +77,9 @@ public bool Started public async Task Init() { + NetworkGameSession.RTT = 0; + NetworkGameSession.LossPercent = 0; + // Start at 1 to avoid having 0 and making us think it's working when it's not _currentNetId = 1; @@ -101,6 +104,7 @@ public async Task Init() packetProcessor.SubscribeNetSerializable(OnTextMessagePacketReceived); packetProcessor.SubscribeNetSerializable(OnQuestConditionPacketReceived); packetProcessor.SubscribeNetSerializable(OnQuestItemPacketReceived); + packetProcessor.SubscribeNetSerializable(OnQuestDropItemPacketReceived); _netServer = new NetManager(this) { @@ -212,9 +216,20 @@ public async Task Init() FikaEventDispatcher.DispatchEvent(new FikaServerCreatedEvent(this)); } + private void OnQuestDropItemPacketReceived(QuestDropItemPacket packet, NetPeer peer) + { + if (MyPlayer.HealthController.IsAlive) + { + if (MyPlayer.AbstractQuestControllerClass is CoopClientSharedQuestController sharedQuestController) + { + sharedQuestController.ReceiveQuestDropItemPacket(ref packet); + } + } + } + private bool ValidateLocalIP(string LocalIP) { - if(LocalIP.StartsWith("192.168") || LocalIP.StartsWith("10")) + if (LocalIP.StartsWith("192.168") || LocalIP.StartsWith("10")) { return true; } @@ -301,7 +316,7 @@ public void SetupGameVariables(CoopPlayer coopPlayer) MyPlayer = coopPlayer; if (FikaPlugin.EnableChat.Value) { - fikaChat = gameObject.AddComponent(); + fikaChat = gameObject.AddComponent(); } } diff --git a/Fika.Core/Networking/Packets/Communication/QuestDropItemPacket.cs b/Fika.Core/Networking/Packets/Communication/QuestDropItemPacket.cs new file mode 100644 index 00000000..a100b7fa --- /dev/null +++ b/Fika.Core/Networking/Packets/Communication/QuestDropItemPacket.cs @@ -0,0 +1,23 @@ +using LiteNetLib.Utils; + +namespace Fika.Core.Networking.Packets +{ + public struct QuestDropItemPacket(string nickname, string itemId, string zoneId) : INetSerializable + { + public string Nickname = nickname; + public string ItemId = itemId; + public string ZoneId = zoneId; + + public void Deserialize(NetDataReader reader) + { + ItemId = reader.GetString(); + ZoneId = reader.GetString(); + } + + public void Serialize(NetDataWriter writer) + { + writer.Put(ItemId); + writer.Put(ZoneId); + } + } +} diff --git a/Fika.Core/UI/Custom/MatchMakerUIScript.cs b/Fika.Core/UI/Custom/MatchMakerUIScript.cs index 6142daa7..6fa94ff3 100644 --- a/Fika.Core/UI/Custom/MatchMakerUIScript.cs +++ b/Fika.Core/UI/Custom/MatchMakerUIScript.cs @@ -96,6 +96,7 @@ private void CreateMatchMakerUI() { Singleton.Instance.PlayUISound(EUISoundType.MenuCheckBox); }); + fikaMatchMakerUi.DedicatedToggle.gameObject.SetActive(false); // Until implemented fikaMatchMakerUi.RaidGroupHostButton.onClick.AddListener(() => { diff --git a/Fika.Core/UI/Patches/ItemContext_Patch.cs b/Fika.Core/UI/Patches/ItemContext_Patch.cs index 4f037a43..ba282958 100644 --- a/Fika.Core/UI/Patches/ItemContext_Patch.cs +++ b/Fika.Core/UI/Patches/ItemContext_Patch.cs @@ -55,6 +55,11 @@ private static void Prefix(ItemInfoInteractionsAbstractClass co return; } + if (item.Parent.Item.TemplateId == "55d7217a4bdc2d86028b456d") // Fix for UI Fixes + { + return; + } + // Check for GClass increments Dictionary dynamicInteractions = Traverse.Create(contextInteractions) .Field>("dictionary_0").Value; diff --git a/Fika.Core/Utils/FikaModHandler.cs b/Fika.Core/Utils/FikaModHandler.cs index 84ecb295..31217277 100644 --- a/Fika.Core/Utils/FikaModHandler.cs +++ b/Fika.Core/Utils/FikaModHandler.cs @@ -8,6 +8,7 @@ using LiteNetLib.Utils; using Newtonsoft.Json; using SPT.Common.Http; +using SPT.Custom.Utils; using System.Collections; using System.Collections.Generic; using System.IO; @@ -46,8 +47,14 @@ public void VerifyMods() string validationJson = RequestHandler.PostJson("/fika/client/check/mods", modValidationRequestJson); logger.LogDebug(validationJson); - ModValidationResponse validationResult = - JsonConvert.DeserializeObject(validationJson); + ModValidationResponse validationResult = JsonConvert.DeserializeObject(validationJson); + if (validationResult.Forbidden == null || validationResult.MissingRequired == null || validationResult.HashMismatch == null) + { + FikaPlugin.Instance.FikaLogger.LogError("FikaModHandler::VerifyMods: Response was invalid!"); + MessageBoxHelper.Show($"Failed to verify mods with server.\nMake sure that the server mod is installed!", "FIKA ERROR", MessageBoxHelper.MessageBoxType.OK); + Application.Quit(); + return; + } // If any errors were detected we will print what has happened bool installationError =