using System; using System.Collections.Generic; using System.Linq; using Server.Factions; using Server.Mobiles; using Server.Multis; using Server.Targeting; using Server.Engines.VvV; using Server.Items; using Server.Spells; using Server.Network; namespace Server.Items { public interface IRevealableItem { bool CheckReveal(Mobile m); bool CheckPassiveDetect(Mobile m); void OnRevealed(Mobile m); bool CheckWhenHidden { get; } } } namespace Server.SkillHandlers { public class DetectHidden { public static void Initialize() { SkillInfo.Table[(int)SkillName.DetectHidden].Callback = new SkillUseCallback(OnUse); } public static TimeSpan OnUse(Mobile src) { src.SendLocalizedMessage(500819);//Where will you search? src.Target = new InternalTarget(); return TimeSpan.FromSeconds(10.0); } public class InternalTarget : Target { public InternalTarget() : base(12, true, TargetFlags.None) { } protected override void OnTarget(Mobile src, object targ) { bool foundAnyone = false; Point3D p; if (targ is Mobile) p = ((Mobile)targ).Location; else if (targ is Item) p = ((Item)targ).Location; else if (targ is IPoint3D) p = new Point3D((IPoint3D)targ); else p = src.Location; double srcSkill = src.Skills[SkillName.DetectHidden].Value; int range = Math.Max(2, (int)(srcSkill / 10.0)); if (!src.CheckSkill(SkillName.DetectHidden, 0.0, 100.0)) range /= 2; BaseHouse house = BaseHouse.FindHouseAt(p, src.Map, 16); bool inHouse = house != null && house.IsFriend(src); if (inHouse) range = 22; if (range > 0) { IPooledEnumerable inRange = src.Map.GetMobilesInRange(p, range); foreach (Mobile trg in inRange) { if (trg.Hidden && src != trg) { double ss = srcSkill + Utility.Random(21) - 10; double ts = trg.Skills[SkillName.Hiding].Value + Utility.Random(21) - 10; double shadow = Server.Spells.SkillMasteries.ShadowSpell.GetDifficultyFactor(trg); bool houseCheck = inHouse && house.IsInside(trg); if (src.AccessLevel >= trg.AccessLevel && (ss >= ts || houseCheck) && Utility.RandomDouble() > shadow) { if ((trg is ShadowKnight && (trg.X != p.X || trg.Y != p.Y)) || (!houseCheck && !CanDetect(src, trg))) continue; trg.RevealingAction(); trg.SendLocalizedMessage(500814); // You have been revealed! trg.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 500814, trg.NetState); foundAnyone = true; } } } inRange.Free(); IPooledEnumerable itemsInRange = src.Map.GetItemsInRange(p, range); foreach (Item item in itemsInRange) { if (item is LibraryBookcase && Server.Engines.Khaldun.GoingGumshoeQuest3.CheckBookcase(src, item)) { foundAnyone = true; } else { IRevealableItem dItem = item as IRevealableItem; if (dItem == null || (item.Visible && dItem.CheckWhenHidden)) continue; if (dItem.CheckReveal(src)) { dItem.OnRevealed(src); foundAnyone = true; } } } itemsInRange.Free(); } if (!foundAnyone) { src.SendLocalizedMessage(500817); // You can see nothing hidden there. } } } public static void DoPassiveDetect(Mobile src) { if (src == null || src.Map == null || src.Location == Point3D.Zero || src.IsStaff()) return; double ss = src.Skills[SkillName.DetectHidden].Value; if (ss <= 0) return; IPooledEnumerable eable = src.Map.GetMobilesInRange(src.Location, 4); if (eable == null) return; foreach (Mobile m in eable) { if (m == null || m == src || m is ShadowKnight || !CanDetect(src, m)) continue; double ts = (m.Skills[SkillName.Hiding].Value + m.Skills[SkillName.Stealth].Value) / 2; if (src.Race == Race.Elf) ss += 20; if (src.AccessLevel >= m.AccessLevel && Utility.Random(1000) < (ss - ts) + 1) { m.RevealingAction(); m.SendLocalizedMessage(500814); // You have been revealed! } } eable.Free(); eable = src.Map.GetItemsInRange(src.Location, 8); foreach (Item item in eable) { if (!item.Visible && item is IRevealableItem && ((IRevealableItem)item).CheckPassiveDetect(src)) { src.SendLocalizedMessage(1153493); // Your keen senses detect something hidden in the area... } } eable.Free(); } public static bool CanDetect(Mobile src, Mobile target) { if (src.Map == null || target.Map == null || !src.CanBeHarmful(target, false)) return false; // No invulnerable NPC's if (src.Blessed || (src is BaseCreature && ((BaseCreature)src).IsInvulnerable)) return false; if (target.Blessed || (target is BaseCreature && ((BaseCreature)target).IsInvulnerable)) return false; // pet owner, guild/alliance, party if (!Server.Spells.SpellHelper.ValidIndirectTarget(target, src)) return false; // Checked aggressed/aggressors if (src.Aggressed.Any(x => x.Defender == target) || src.Aggressors.Any(x => x.Attacker == target)) return true; // In Fel or Follow the same rules as indirect spells such as wither return src.Map.Rules == MapRules.FeluccaRules; } } }