using System; using Server.Items; using Server.Network; namespace Server.Factions; public enum AllowedPlacing { Everywhere, AnyFactionTown, ControlledFactionTown, FactionStronghold } public abstract class BaseFactionTrap : BaseTrap { private TimerExecutionToken _concealingTimerToken; public BaseFactionTrap(Faction f, Mobile m, int itemID) : base(itemID) { Visible = false; Faction = f; TimeOfPlacement = Core.Now; Placer = m; } public BaseFactionTrap(Serial serial) : base(serial) { } [CommandProperty(AccessLevel.GameMaster)] public Faction Faction { get; set; } [CommandProperty(AccessLevel.GameMaster)] public Mobile Placer { get; set; } [CommandProperty(AccessLevel.GameMaster)] public DateTime TimeOfPlacement { get; set; } public virtual int EffectSound => 0; public virtual int SilverFromDisarm => 100; public virtual int MessageHue => 0; public virtual int AttackMessage => 0; public virtual int DisarmMessage => 0; public virtual AllowedPlacing AllowedPlacing => AllowedPlacing.Everywhere; public virtual TimeSpan ConcealPeriod => TimeSpan.FromMinutes(1.0); public virtual TimeSpan DecayPeriod => Core.AOS ? TimeSpan.FromDays(1.0) : TimeSpan.MaxValue; public override void OnTrigger(Mobile from) { if (!IsEnemy(from)) { return; } Conceal(); DoVisibleEffect(); Effects.PlaySound(Location, Map, EffectSound); DoAttackEffect(from); var silverToAward = from.Alive ? 20 : 40; if (Placer != null && Faction != null) { var victimState = PlayerState.Find(from); if (victimState?.CanGiveSilverTo(Placer) == true && victimState.KillPoints > 0) { var silverGiven = Faction.AwardSilver(Placer, silverToAward); if (silverGiven > 0) { // TODO: Get real message if (from.Alive) { Placer.SendMessage( $"You have earned {silverGiven} silver pieces because {from.Name} fell for your trap." ); } else { // You have earned ~1_SILVER_AMOUNT~ pieces for vanquishing ~2_PLAYER_NAME~! Placer.SendLocalizedMessage(1042736, $"{silverGiven} silver\t{from.Name}"); } } victimState.OnGivenSilverTo(Placer); } } from.LocalOverheadMessage(MessageType.Regular, MessageHue, AttackMessage); } public abstract void DoVisibleEffect(); public abstract void DoAttackEffect(Mobile m); public virtual int IsValidLocation() => IsValidLocation(GetWorldLocation(), Map); public virtual int IsValidLocation(Point3D p, Map m) { if (m == null) { return 502956; // You cannot place a trap on that. } if (Core.ML) { foreach (var item in m.GetItemsAt(p)) { if (item is BaseFactionTrap trap && trap.Faction == Faction) { return 1075263; // There is already a trap belonging to your faction at this location.; } } } switch (AllowedPlacing) { case AllowedPlacing.FactionStronghold: { var region = Region.Find(p, m).GetRegion(); if (region != null && region.Faction == Faction) { return 0; } return 1010355; // This trap can only be placed in your stronghold } case AllowedPlacing.AnyFactionTown: { var town = Town.FromRegion(Region.Find(p, m)); if (town != null) { return 0; } return 1010356; // This trap can only be placed in a faction town } case AllowedPlacing.ControlledFactionTown: { var town = Town.FromRegion(Region.Find(p, m)); if (town != null && town.Owner == Faction) { return 0; } return 1010357; // This trap can only be placed in a town your faction controls } } return 0; } public override void OnMovement(Mobile m, Point3D oldLocation) { base.OnMovement(m, oldLocation); if (!CheckDecay() && CheckRange(m.Location, oldLocation, 6)) { if (Faction.Find(m) != null && (m.Skills.DetectHidden.Value - 80.0) / 20.0 > Utility.RandomDouble()) { PrivateOverheadLocalizedMessage(m, 1010154, MessageHue, "", ""); // [Faction Trap] } } } public void PrivateOverheadLocalizedMessage(Mobile to, int number, int hue, string name, string args) { to?.NetState.SendMessageLocalized(Serial, ItemID, MessageType.Regular, hue, 3, number, name, args); } public virtual bool CheckDecay() { var decayPeriod = DecayPeriod; if (decayPeriod == TimeSpan.MaxValue) { return false; } if (TimeOfPlacement + decayPeriod < Core.Now) { Timer.StartTimer(Delete); return true; } return false; } public virtual void BeginConceal() { _concealingTimerToken.Cancel(); Timer.StartTimer(ConcealPeriod, Conceal, out _concealingTimerToken); } public virtual void Conceal() { _concealingTimerToken.Cancel(); if (!Deleted) { Visible = false; } } public override void Serialize(IGenericWriter writer) { base.Serialize(writer); writer.Write(0); // version Faction.WriteReference(writer, Faction); writer.Write(Placer); writer.Write(TimeOfPlacement); if (Visible) { BeginConceal(); } } public override void Deserialize(IGenericReader reader) { base.Deserialize(reader); var version = reader.ReadInt(); Faction = Faction.ReadReference(reader); Placer = reader.ReadEntity(); TimeOfPlacement = reader.ReadDateTime(); if (Visible) { BeginConceal(); } CheckDecay(); } public override void OnDelete() { if (Faction?.Traps.Contains(this) == true) { Faction.Traps.Remove(this); } base.OnDelete(); } public virtual bool IsEnemy(Mobile mob) { if (mob.Hidden && mob.AccessLevel > AccessLevel.Player) { return false; } if (!mob.Alive || mob.IsDeadBondedPet) { return false; } var faction = Faction.Find(mob, true); if (faction == null && mob is BaseFactionGuard guard) { faction = guard.Faction; } if (faction == null) { return false; } return faction != Faction; } }