diff --git a/Fika.Core/AkiSupport/Overrides/OfflineRaidSettingsMenuPatchOverride.cs b/Fika.Core/AkiSupport/Overrides/OfflineRaidSettingsMenuPatchOverride.cs index 45bb41d5..e1b3038e 100644 --- a/Fika.Core/AkiSupport/Overrides/OfflineRaidSettingsMenuPatchOverride.cs +++ b/Fika.Core/AkiSupport/Overrides/OfflineRaidSettingsMenuPatchOverride.cs @@ -21,7 +21,7 @@ protected override MethodBase GetTargetMethod() [PatchPostfix] private static void PatchPostfix(RaidSettingsWindow __instance, UiElementBlocker ____coopModeBlocker, List ____weatherCanvasGroups, - UpdatableToggle ____randomTimeToggle, UpdatableToggle ____randomWeatherToggle, List ____waterAndFoodCanvasGroups) + UpdatableToggle ____randomTimeToggle, UpdatableToggle ____randomWeatherToggle, List ____waterAndFoodCanvasGroups, List ____playersSpawnPlaceCanvasGroups, DropDownBox ____playersSpawnPlaceDropdown) { // Always disable the Coop Mode checkbox ____coopModeBlocker.SetBlock(true, "Co-op is always enabled in Fika"); @@ -36,6 +36,26 @@ private static void PatchPostfix(RaidSettingsWindow __instance, UiElementBlocker canvasGroup.SetUnlockStatus(true, true); } + foreach (CanvasGroup canvasGroup in ____playersSpawnPlaceCanvasGroups) + { + canvasGroup.SetUnlockStatus(true, true); + } + + // Remove redundant settings and add our own "Random" to make the setting clear, while also renaming index 0 to "Together" + List labelList = Traverse.Create(____playersSpawnPlaceDropdown).Field("list_0").GetValue>(); + labelList.Clear(); + labelList.Add(new() + { + Label = "Together", + Enabled = true + }); + labelList.Add(new() + { + Label = "Random", + Enabled = true + }); + ____playersSpawnPlaceDropdown.SetTextInternal("Together"); + instance = __instance; ____randomWeatherToggle.Bind(new Action(ToggleWeather)); diff --git a/Fika.Core/Coop/BotClasses/CoopBotHealthController.cs b/Fika.Core/Coop/BotClasses/CoopBotHealthController.cs new file mode 100644 index 00000000..67876d7f --- /dev/null +++ b/Fika.Core/Coop/BotClasses/CoopBotHealthController.cs @@ -0,0 +1,35 @@ +// © 2024 Lacyway All Rights Reserved + +using EFT; +using EFT.HealthSystem; +using Fika.Core.Coop.Players; + +namespace Fika.Core.Coop.ClientClasses +{ + public sealed class CoopBotHealthController(Profile.GClass1756 healthInfo, Player player, InventoryControllerClass inventoryController, SkillManager skillManager, bool aiHealth) + : PlayerHealthController(healthInfo, player, inventoryController, skillManager, aiHealth) + { + private readonly CoopBot coopBot = (CoopBot)player; + public override bool _sendNetworkSyncPackets + { + get + { + return true; + } + } + + public override void SendNetworkSyncPacket(GStruct346 packet) + { + if (packet.SyncType == GStruct346.ESyncType.IsAlive && !packet.Data.IsAlive.IsAlive) + { + coopBot?.PacketSender?.HealthSyncPackets.Enqueue(coopBot.SetupDeathPacket(packet)); + return; + } + + coopBot?.PacketSender?.HealthSyncPackets.Enqueue(new(coopBot.NetId) + { + Packet = packet + }); + } + } +} diff --git a/Fika.Core/Coop/ClientClasses/CoopClientHealthController.cs b/Fika.Core/Coop/ClientClasses/CoopClientHealthController.cs index 903d1afc..8ab98cf0 100644 --- a/Fika.Core/Coop/ClientClasses/CoopClientHealthController.cs +++ b/Fika.Core/Coop/ClientClasses/CoopClientHealthController.cs @@ -10,8 +10,6 @@ public sealed class CoopClientHealthController(Profile.GClass1756 healthInfo, Pl : PlayerHealthController(healthInfo, player, inventoryController, skillManager, aiHealth) { private readonly CoopPlayer coopPlayer = (CoopPlayer)player; - private readonly bool isBot = aiHealth; - private readonly CoopBot coopBot = aiHealth ? (CoopBot)player : null; public override bool _sendNetworkSyncPackets { get @@ -24,31 +22,14 @@ public override void SendNetworkSyncPacket(GStruct346 packet) { if (packet.SyncType == GStruct346.ESyncType.IsAlive && !packet.Data.IsAlive.IsAlive) { - if (!isBot) - { - coopPlayer?.PacketSender?.HealthSyncPackets.Enqueue(coopPlayer.SetupDeathPacket(packet)); - } - else - { - coopBot?.PacketSender?.HealthSyncPackets.Enqueue(coopBot.SetupDeathPacket(packet)); - } + coopPlayer?.PacketSender?.HealthSyncPackets.Enqueue(coopPlayer.SetupDeathPacket(packet)); return; } - if (!isBot) + coopPlayer?.PacketSender?.HealthSyncPackets.Enqueue(new(coopPlayer.NetId) { - coopPlayer?.PacketSender?.HealthSyncPackets.Enqueue(new(coopPlayer.NetId) - { - Packet = packet - }); - } - else - { - coopBot?.PacketSender?.HealthSyncPackets.Enqueue(new(coopBot.NetId) - { - Packet = packet - }); - } + Packet = packet + }); } } } diff --git a/Fika.Core/Coop/ClientClasses/HandsControllers/CoopClientFirearmController.cs b/Fika.Core/Coop/ClientClasses/HandsControllers/CoopClientFirearmController.cs index bd2b947b..f15ea5f6 100644 --- a/Fika.Core/Coop/ClientClasses/HandsControllers/CoopClientFirearmController.cs +++ b/Fika.Core/Coop/ClientClasses/HandsControllers/CoopClientFirearmController.cs @@ -446,7 +446,8 @@ public override void ShotMisfired(BulletClass ammo, Weapon.EMalfunctionState mal IsPrimaryActive = true, ShotType = shotType, AmmoAfterShot = Item.GetCurrentMagazineCount(), - Overheat = overheat + Overheat = overheat, + AmmoTemplate = ammo.TemplateId } }); diff --git a/Fika.Core/Coop/Components/CoopHandler.cs b/Fika.Core/Coop/Components/CoopHandler.cs index b39a573c..68c7d189 100644 --- a/Fika.Core/Coop/Components/CoopHandler.cs +++ b/Fika.Core/Coop/Components/CoopHandler.cs @@ -88,7 +88,9 @@ public static string GetServerId() { CoopHandler coopGC = GetCoopHandler(); if (coopGC == null) - return null; + { + return MatchmakerAcceptPatches.GetGroupId(); + } return coopGC.ServerId; } diff --git a/Fika.Core/Coop/GameMode/CoopGame.cs b/Fika.Core/Coop/GameMode/CoopGame.cs index 125a443e..ab792556 100644 --- a/Fika.Core/Coop/GameMode/CoopGame.cs +++ b/Fika.Core/Coop/GameMode/CoopGame.cs @@ -213,7 +213,7 @@ public async Task CreateCoopHandler() private List GetPlayers(CoopHandler coopHandler) { - List humanPlayers = new List(); + List humanPlayers = new(); // Grab all players foreach (CoopPlayer player in coopHandler.Players.Values) @@ -498,7 +498,7 @@ private async void DeployScreen(float timeBeforeDeploy) Destroy(fikaStartButton); } - SetStatusModel status = new SetStatusModel(coopHandler.MyPlayer.ProfileId, LobbyEntry.ELobbyStatus.IN_GAME); + SetStatusModel status = new(coopHandler.MyPlayer.ProfileId, LobbyEntry.ELobbyStatus.IN_GAME); await FikaRequestHandler.UpdateSetStatus(status); Singleton.Instance.ReadyClients++; @@ -515,7 +515,7 @@ private async void DeployScreen(float timeBeforeDeploy) if (MatchmakerAcceptPatches.IsServer) { - SetStatusModel status = new SetStatusModel(coopHandler.MyPlayer.ProfileId, LobbyEntry.ELobbyStatus.IN_GAME); + SetStatusModel status = new(coopHandler.MyPlayer.ProfileId, LobbyEntry.ELobbyStatus.IN_GAME); await FikaRequestHandler.UpdateSetStatus(status); do @@ -581,26 +581,44 @@ private async Task SendOrReceiveSpawnPoint() { if (MatchmakerAcceptPatches.IsServer) { - UpdateSpawnPointRequest body = new UpdateSpawnPointRequest(spawnPoint.Id); - Logger.LogInfo($"Setting Spawn Point to: {spawnPoint.Id}"); + bool spawnTogether = RaidSettings.PlayersSpawnPlace == EPlayersSpawnPlace.SamePlace; + UpdateSpawnPointRequest body = new(spawnTogether ? spawnPoint.Id : ""); + if (spawnTogether) + { + Logger.LogInfo($"Setting Spawn Point to: {spawnPoint.Id}"); + } + else + { + Logger.LogInfo("Using random spawn points!"); + NotificationManagerClass.DisplayMessageNotification("Using random spawn points", iconType: EFT.Communications.ENotificationIconType.Alert); + } await FikaRequestHandler.UpdateSpawnPoint(body); } else if (MatchmakerAcceptPatches.IsClient) { - SpawnPointRequest body = new SpawnPointRequest(); + SpawnPointRequest body = new(); SpawnPointResponse response = await FikaRequestHandler.RaidSpawnPoint(body); string name = response.SpawnPoint; - Logger.LogInfo($"Retrieved Spawn Point '{name}' from server"); - - Dictionary allSpawnPoints = Traverse.Create(spawnPoints).Field("dictionary_0").GetValue>(); - foreach (ISpawnPoint spawnPointObject in allSpawnPoints.Keys) + if (!string.IsNullOrEmpty(name)) { - if (spawnPointObject.Id == name) + Logger.LogInfo($"Retrieved Spawn Point '{name}' from server"); + + Dictionary allSpawnPoints = Traverse.Create(spawnPoints).Field("dictionary_0").GetValue>(); + foreach (ISpawnPoint spawnPointObject in allSpawnPoints.Keys) { - spawnPoint = spawnPointObject; + if (spawnPointObject.Id == name) + { + spawnPoint = spawnPointObject; + } } } + else + { + Logger.LogInfo("Spawn Point was empty, selecting random."); + NotificationManagerClass.DisplayMessageNotification("Using random spawn points", iconType: EFT.Communications.ENotificationIconType.Alert); + spawnPoint = SpawnSystem.SelectSpawnPoint(ESpawnCategory.Player, Profile_0.Info.Side); + } } } @@ -654,7 +672,7 @@ public override async Task vmethod_2(int playerId, Vector3 position if (spawnPoint == null) { Logger.LogWarning("SpawnPoint was null after retrieving it from the server!"); - SpawnSystem.SelectSpawnPoint(ESpawnCategory.Player, Profile_0.Info.Side); + spawnPoint = SpawnSystem.SelectSpawnPoint(ESpawnCategory.Player, Profile_0.Info.Side); } } @@ -683,7 +701,7 @@ public override async Task vmethod_2(int playerId, Vector3 position CoopPlayer coopPlayer = (CoopPlayer)myPlayer; coopHandler.Players.Add(coopPlayer.NetId, coopPlayer); - PlayerSpawnRequest body = new PlayerSpawnRequest(myPlayer.ProfileId, MatchmakerAcceptPatches.GetGroupId()); + PlayerSpawnRequest body = new(myPlayer.ProfileId, MatchmakerAcceptPatches.GetGroupId()); await FikaRequestHandler.UpdatePlayerSpawn(body); myPlayer.SpawnPoint = spawnPoint; @@ -710,6 +728,8 @@ public override async Task vmethod_2(int playerId, Vector3 position ErrorScreen.EButtonType.OkButton, 15f, () => { StopFromError(myPlayer.ProfileId, ExitStatus.Runner); + PlayerLeftRequest playerLeftRequest = new(coopPlayer.ProfileId); + FikaRequestHandler.RaidLeave(playerLeftRequest); }, null); }); Traverse.Create(backButtonComponent).Field("OnClick").SetValue(newEvent); @@ -902,7 +922,7 @@ private async Task WaitForPlayers() private async Task SetStatus(LocalPlayer myPlayer, LobbyEntry.ELobbyStatus status) { - SetStatusModel statusBody = new SetStatusModel(myPlayer.ProfileId, status); + SetStatusModel statusBody = new(myPlayer.ProfileId, status); await FikaRequestHandler.UpdateSetStatus(statusBody); Logger.LogInfo("Setting game status to: " + status.ToString()); } @@ -1482,7 +1502,7 @@ private void StopFromError(string profileId, ExitStatus exitStatus) string exitName = null; float delay = 0f; - PlayerLeftRequest body = new PlayerLeftRequest(profileId); + PlayerLeftRequest body = new(profileId); FikaRequestHandler.RaidLeave(body); if (CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler)) diff --git a/Fika.Core/Coop/ObservedClasses/HandsControllers/CoopObservedFirearmController.cs b/Fika.Core/Coop/ObservedClasses/HandsControllers/CoopObservedFirearmController.cs index 3c5495b8..2fd070bc 100644 --- a/Fika.Core/Coop/ObservedClasses/HandsControllers/CoopObservedFirearmController.cs +++ b/Fika.Core/Coop/ObservedClasses/HandsControllers/CoopObservedFirearmController.cs @@ -197,6 +197,12 @@ public void HandleFirearmPacket(in WeaponPacket packet, InventoryControllerClass break; } + if (string.IsNullOrEmpty(packet.ShotInfoPacket.AmmoTemplate)) + { + FikaPlugin.Instance.FikaLogger.LogError("CoopObservedFirearmController::HandleFirearmPacket: AmmoTemplate was null or empty!"); + return; + } + Weapon.MalfState.MalfunctionedAmmo = (BulletClass)Singleton.Instance.CreateItem(MongoID.Generate(), packet.ShotInfoPacket.AmmoTemplate, null); if (weaponPrefab != null) { diff --git a/Fika.Core/Coop/Players/CoopBot.cs b/Fika.Core/Coop/Players/CoopBot.cs index 1d97be72..147132dc 100644 --- a/Fika.Core/Coop/Players/CoopBot.cs +++ b/Fika.Core/Coop/Players/CoopBot.cs @@ -76,7 +76,7 @@ public static async Task CreateBot( InventoryControllerClass inventoryController = new CoopBotInventoryController(player, profile, true); await player.Init(rotation, layerName, pointOfView, profile, inventoryController, - new CoopClientHealthController(profile.Health, player, inventoryController, profile.Skills, aiControl), + new CoopBotHealthController(profile.Health, player, inventoryController, profile.Skills, aiControl), new CoopObservedStatisticsManager(), questController, achievementController, filter, EVoipState.NotAvailable, aiControl, async: false); @@ -180,6 +180,11 @@ protected override void Start() { dynamicAi = gameObject.AddComponent(); } + + if (FikaPlugin.DisableBotMetabolism.Value) + { + HealthController.DisableMetabolism(); + } } public override void BtrInteraction() @@ -214,22 +219,35 @@ private IEnumerator WaitForPlayersToLoadBot() Vector3 spawnPosition = Position; int connectedPeers = Singleton.Instance.NetServer.ConnectedPeersCount; float randomY = UnityEngine.Random.RandomRange(-500, -1000); - DateTime start = DateTime.Now; + float start = 0f; Teleport(new(0, randomY, 0)); - while (loadedPlayers < connectedPeers && Math.Abs((start - DateTime.Now).TotalSeconds) < 30) + while (loadedPlayers < connectedPeers && start < 30f) { - yield return new WaitForSeconds(1); + start += Time.deltaTime; + yield return new WaitForEndOfFrame(); } Teleport(new Vector3(spawnPosition.x, spawnPosition.y + 1f, spawnPosition.z)); AIData.BotOwner.BotState = EBotState.PreActive; isStarted = true; - yield return new WaitUntil(() => { return MovementContext.IsGrounded; }); + float fallStart = 0f; + + while (!MovementContext.IsGrounded || fallStart < 5f) + { + fallStart += Time.deltaTime; + yield return new WaitForEndOfFrame(); + } + + if (!MovementContext.IsGrounded) + { + FikaPlugin.Instance.FikaLogger.LogWarning($"{gameObject.name} was still not grounded after being loaded in! Forcing to be grounded."); + MovementContext.IsGrounded = true; + } #if DEBUG FikaPlugin.Instance.FikaLogger.LogWarning($"{gameObject.name} is now grounded, started at Y: {randomY}, now at {Position.ToStringVerbose()}"); #endif - yield return new WaitForFixedUpdate(); + yield return new WaitForEndOfFrame(); ActiveHealthController.SetDamageCoeff(originalDamageCoeff); } @@ -320,19 +338,6 @@ private IEnumerator DestroyNetworkedComponents() public override void UpdateTick() { base.UpdateTick(); - - // Temp test to fix AI dying from hydration and energy... - /*if (ActiveHealthController.Energy.Current < 1) - { - logger($"Setting energy to 50 on {ProfileId}"); - ActiveHealthController.ChangeEnergy(50); - } - - if (ActiveHealthController.Hydration.Current < 1) - { - logger($"Setting hydration to 50 on {ProfileId}"); - ActiveHealthController.ChangeHydration(50); - }*/ } public override void OnDestroy() diff --git a/Fika.Core/Coop/Players/CoopPlayer.cs b/Fika.Core/Coop/Players/CoopPlayer.cs index 6d772ebf..77e6f4f2 100644 --- a/Fika.Core/Coop/Players/CoopPlayer.cs +++ b/Fika.Core/Coop/Players/CoopPlayer.cs @@ -100,8 +100,7 @@ public static async Task Create( achievementsController.Run(); } - IStatisticsManager statisticsManager = null; - statisticsManager = new CoopClientStatisticsManager(player.Profile); + IStatisticsManager statisticsManager = new CoopClientStatisticsManager(player.Profile); await player.Init(rotation, layerName, pointOfView, profile, inventoryController, new CoopClientHealthController(profile.Health, player, inventoryController, profile.Skills, aiControl), @@ -219,6 +218,11 @@ public override void ApplyDamageInfo(DamageInfo damageInfo, EBodyPart bodyPartTy { damageInfo.Damage *= FikaPlugin.ArmpitDamageMultiplier.Value; } + + if (bodyPartType is EBodyPart.Stomach) + { + damageInfo.Damage *= FikaPlugin.StomachDamageMultiplier.Value; + } } base.ApplyDamageInfo(damageInfo, bodyPartType, colliderType, absorbed); diff --git a/Fika.Core/Coop/Players/ObservedCoopPlayer.cs b/Fika.Core/Coop/Players/ObservedCoopPlayer.cs index ff78b41b..22f9a6c5 100644 --- a/Fika.Core/Coop/Players/ObservedCoopPlayer.cs +++ b/Fika.Core/Coop/Players/ObservedCoopPlayer.cs @@ -710,21 +710,25 @@ public override void OnDead(EDamageType damageType) { if (!IsObservedAI) { + string nickname = !string.IsNullOrEmpty(Profile.Info.MainProfileNickname) ? Profile.Info.MainProfileNickname : Profile.Nickname; if (damageType != EDamageType.Undefined) { - NotificationManagerClass.DisplayWarningNotification($"Group member '{Profile.Nickname}' has died from '{("DamageType_" + damageType.ToString()).Localized()}'"); + NotificationManagerClass.DisplayWarningNotification($"Group member '{nickname}' has died from '{("DamageType_" + damageType.ToString()).Localized()}'"); } else { - NotificationManagerClass.DisplayWarningNotification($"Group member '{Profile.Nickname}' has died"); + NotificationManagerClass.DisplayWarningNotification($"Group member '{nickname}' has died"); } } if (IsBoss(Profile.Info.Settings.Role, out string name) && IsObservedAI && LastAggressor != null) { if (LastAggressor is CoopPlayer aggressor) { + string aggressorNickname = !string.IsNullOrEmpty(LastAggressor.Profile.Info.MainProfileNickname) ? LastAggressor.Profile.Info.MainProfileNickname : LastAggressor.Profile.Nickname; if (aggressor.gameObject.name.StartsWith("Player_") || aggressor.IsYourPlayer) - NotificationManagerClass.DisplayMessageNotification($"{LastAggressor.Profile.Nickname} killed boss {name}", iconType: EFT.Communications.ENotificationIconType.Friend); + { + NotificationManagerClass.DisplayMessageNotification($"{LastAggressor.Profile.Info.MainProfileNickname} killed boss {name}", iconType: EFT.Communications.ENotificationIconType.Friend); + } } } } diff --git a/Fika.Core/FikaPlugin.cs b/Fika.Core/FikaPlugin.cs index 0719446b..c4ed87ff 100644 --- a/Fika.Core/FikaPlugin.cs +++ b/Fika.Core/FikaPlugin.cs @@ -163,6 +163,8 @@ public class FikaPlugin : BaseUnityPlugin // Gameplay public static ConfigEntry HeadDamageMultiplier { get; set; } public static ConfigEntry ArmpitDamageMultiplier { get; set; } + public static ConfigEntry StomachDamageMultiplier { get; set; } + public static ConfigEntry DisableBotMetabolism { get; set; } #endregion #region client config @@ -367,9 +369,13 @@ private void SetupConfig() // 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.2f, 1f), new ConfigurationManagerAttributes() { Order = 2 })); + 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.2f, 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.2f, 1f), new ConfigurationManagerAttributes() { Order = 1 })); + 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.2f, 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.2f, 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 })); } private string[] GetLocalAddresses() diff --git a/Fika.Core/Networking/FikaClient.cs b/Fika.Core/Networking/FikaClient.cs index 1de34e06..5f1a0f4f 100644 --- a/Fika.Core/Networking/FikaClient.cs +++ b/Fika.Core/Networking/FikaClient.cs @@ -11,7 +11,6 @@ using EFT.Weather; using Fika.Core.Coop.Components; using Fika.Core.Coop.GameMode; -using Fika.Core.Coop.Matchmaker; using Fika.Core.Coop.Players; using Fika.Core.Modding; using Fika.Core.Modding.Events; @@ -382,7 +381,8 @@ private void OnGenericPacketReceived(GenericPacket packet) if (FikaPlugin.ShowNotifications.Value) { - NotificationManagerClass.DisplayMessageNotification($"Group member '{playerToApply.Profile.Nickname}' has extracted.", + string nickname = !string.IsNullOrEmpty(playerToApply.Profile.Info.MainProfileNickname) ? playerToApply.Profile.Info.MainProfileNickname : playerToApply.Profile.Nickname; + NotificationManagerClass.DisplayMessageNotification($"Group member '{nickname}' has extracted.", EFT.Communications.ENotificationDurationType.Default, EFT.Communications.ENotificationIconType.EntryPoint); } } diff --git a/Fika.Core/Networking/FikaServer.cs b/Fika.Core/Networking/FikaServer.cs index 54c419cd..97cc0b29 100644 --- a/Fika.Core/Networking/FikaServer.cs +++ b/Fika.Core/Networking/FikaServer.cs @@ -361,7 +361,8 @@ private void OnGenericPacketReceived(GenericPacket packet, NetPeer peer) if (FikaPlugin.ShowNotifications.Value) { - NotificationManagerClass.DisplayMessageNotification($"Group member '{playerToApply.Profile.Nickname}' has extracted.", + string nickname = !string.IsNullOrEmpty(playerToApply.Profile.Info.MainProfileNickname) ? playerToApply.Profile.Info.MainProfileNickname : playerToApply.Profile.Nickname; + NotificationManagerClass.DisplayMessageNotification($"Group member '{nickname}' has extracted.", EFT.Communications.ENotificationDurationType.Default, EFT.Communications.ENotificationIconType.EntryPoint); } }