using System; using ACE.Common; using ACE.DatLoader; using ACE.DatLoader.FileTypes; using ACE.Entity; using ACE.Entity.Enum; using ACE.Entity.Enum.Properties; using ACE.Entity.Models; using ACE.Server.Entity; using ACE.Server.Entity.Actions; using ACE.Server.Managers; using ACE.Server.Network.GameEvent.Events; using ACE.Server.Network.GameMessages.Messages; namespace ACE.Server.WorldObjects { public class PKModifier : WorldObject { /// /// A new biota be created taking all of its values from weenie. /// public PKModifier(Weenie weenie, ObjectGuid guid) : base(weenie, guid) { SetEphemeralValues(); } /// /// Restore a WorldObject from the database. /// public PKModifier(Biota biota) : base(biota) { SetEphemeralValues(); } public bool IsPKSwitch => PkLevelModifier == 1; public bool IsNPKSwitch => PkLevelModifier == -1; private void SetEphemeralValues() { CurrentMotionState = new Motion(MotionStance.NonCombat); if (IsNPKSwitch) ObjectDescriptionFlags |= ObjectDescriptionFlag.NpkSwitch; if (IsPKSwitch) ObjectDescriptionFlags |= ObjectDescriptionFlag.PkSwitch; } public override ActivationResult CheckUseRequirements(WorldObject activator) { if (!(activator is Player player)) return new ActivationResult(false); if (player.IsOlthoiPlayer) { player.SendWeenieError(WeenieError.OlthoiCannotInteractWithThat); return new ActivationResult(false); } if (player.PkLevel > PKLevel.PK || PropertyManager.GetBool("pk_server").Item || PropertyManager.GetBool("pkl_server").Item) { if (!string.IsNullOrWhiteSpace(GetProperty(PropertyString.UsePkServerError))) player.Session.Network.EnqueueSend(new GameMessageSystemChat(GetProperty(PropertyString.UsePkServerError), ChatMessageType.Broadcast)); return new ActivationResult(false); } if (player.PlayerKillerStatus == PlayerKillerStatus.PKLite) { if (!string.IsNullOrWhiteSpace(GetProperty(PropertyString.UsePkServerError))) player.Session.Network.EnqueueSend(new GameMessageSystemChat(GetProperty(PropertyString.UsePkServerError), ChatMessageType.Broadcast)); player.Session.Network.EnqueueSend(new GameMessageSystemChat("Player Killer Lites may not change their PK status.", ChatMessageType.Broadcast)); // not sure how retail handled this case return new ActivationResult(false); } if (player.Teleporting) return new ActivationResult(false); if (player.IsBusy) return new ActivationResult(false); if (player.IsAdvocate || player.AdvocateQuest || player.AdvocateState) { return new ActivationResult(new GameEventWeenieError(player.Session, WeenieError.AdvocatesCannotChangePKStatus)); } if (player.MinimumTimeSincePk != null) { return new ActivationResult(new GameEventWeenieError(player.Session, WeenieError.CannotChangePKStatusWhileRecovering)); } if (IsBusy) { return new ActivationResult(new GameEventWeenieErrorWithString(player.Session, WeenieErrorWithString.The_IsCurrentlyInUse, Name)); } return new ActivationResult(true); } public override void ActOnUse(WorldObject activator) { if (!(activator is Player player)) return; if (IsBusy) { player.Session.Network.EnqueueSend(new GameEventWeenieErrorWithString(player.Session, WeenieErrorWithString.The_IsCurrentlyInUse, Name)); return; } if (player.PkLevel == PKLevel.PK && IsNPKSwitch && (Time.GetUnixTime() - player.PkTimestamp) < MinimumTimeSincePk) { IsBusy = true; player.IsBusy = true; var actionChain = new ActionChain(); if (UseTargetFailureAnimation != MotionCommand.Invalid) { var useMotion = UseTargetFailureAnimation; EnqueueBroadcastMotion(new Motion(this, useMotion)); var motionTable = DatManager.PortalDat.ReadFromDat(MotionTableId); var useTime = motionTable.GetAnimationLength(useMotion); player.LastUseTime += useTime; actionChain.AddDelaySeconds(useTime); } actionChain.AddAction(player, () => { player.Session.Network.EnqueueSend(new GameEventWeenieError(player.Session, WeenieError.YouFeelAHarshDissonance)); player.IsBusy = false; Reset(); }); actionChain.EnqueueChain(); return; } if ((player.PkLevel == PKLevel.NPK && IsPKSwitch) || (player.PkLevel == PKLevel.PK && IsNPKSwitch)) { IsBusy = true; player.IsBusy = true; var useMotion = UseTargetSuccessAnimation != MotionCommand.Invalid ? UseTargetSuccessAnimation : MotionCommand.Twitch1; EnqueueBroadcastMotion(new Motion(this, useMotion)); var motionTable = DatManager.PortalDat.ReadFromDat(MotionTableId); var useTime = motionTable.GetAnimationLength(useMotion); player.LastUseTime += useTime; var actionChain = new ActionChain(); actionChain.AddDelaySeconds(useTime); actionChain.AddAction(player, () => { player.Session.Network.EnqueueSend(new GameMessageSystemChat(GetProperty(PropertyString.UseMessage), ChatMessageType.Broadcast)); player.PkLevelModifier += PkLevelModifier; if (player.PkLevel == PKLevel.PK) player.PlayerKillerStatus = PlayerKillerStatus.PK; else player.PlayerKillerStatus = PlayerKillerStatus.NPK; player.EnqueueBroadcast(new GameMessagePublicUpdatePropertyInt(player, PropertyInt.PlayerKillerStatus, (int)player.PlayerKillerStatus)); //player.ApplySoundEffects(Sound.Open); // in pcaps, but makes no sound/has no effect. ? player.IsBusy = false; Reset(); }); actionChain.EnqueueChain(); } else player.Session.Network.EnqueueSend(new GameMessageSystemChat(GetProperty(PropertyString.ActivationFailure), ChatMessageType.Broadcast)); } public void Reset() { IsBusy = false; } public double? MinimumTimeSincePk { get => GetProperty(PropertyFloat.MinimumTimeSincePk); set { if (!value.HasValue) RemoveProperty(PropertyFloat.MinimumTimeSincePk); else SetProperty(PropertyFloat.MinimumTimeSincePk, value.Value); } } } }