using Server.Gumps; using Server.Network; using System; using System.Collections; namespace Server.Items { [Flipable(0x100A/*East*/, 0x100B/*South*/)] public class ArcheryButte : AddonComponent { private static readonly TimeSpan UseDelay = TimeSpan.FromSeconds(2.0); private double m_MinSkill; private double m_MaxSkill; private int m_Arrows, m_Bolts; private DateTime m_LastUse; private Hashtable m_Entries; public ArcheryButte() : this(0x100A) { } public ArcheryButte(int itemID) : base(itemID) { m_MinSkill = -25.0; m_MaxSkill = +25.0; } public ArcheryButte(Serial serial) : base(serial) { } [CommandProperty(AccessLevel.GameMaster)] public double MinSkill { get { return m_MinSkill; } set { m_MinSkill = value; } } [CommandProperty(AccessLevel.GameMaster)] public double MaxSkill { get { return m_MaxSkill; } set { m_MaxSkill = value; } } [CommandProperty(AccessLevel.GameMaster)] public DateTime LastUse { get { return m_LastUse; } set { m_LastUse = value; } } [CommandProperty(AccessLevel.GameMaster)] public bool FacingEast { get { return (ItemID == 0x100A); } set { ItemID = value ? 0x100A : 0x100B; } } [CommandProperty(AccessLevel.GameMaster)] public int Arrows { get { return m_Arrows; } set { m_Arrows = value; } } [CommandProperty(AccessLevel.GameMaster)] public int Bolts { get { return m_Bolts; } set { m_Bolts = value; } } public override void OnDoubleClick(Mobile from) { if ((from.Weapon is Boomerang || from.Weapon is Cyclone || from.Weapon is BaseThrown) && from.InRange(GetWorldLocation(), 1)) Fire(from); if ((m_Arrows > 0 || m_Bolts > 0) && from.InRange(GetWorldLocation(), 1)) Gather(from); else Fire(from); } public void Gather(Mobile from) { from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 500592); // You gather the arrows and bolts. if (m_Arrows > 0) from.AddToBackpack(new Arrow(m_Arrows)); if (m_Bolts > 0) from.AddToBackpack(new Bolt(m_Bolts)); m_Arrows = 0; m_Bolts = 0; m_Entries = null; } public void Fire(Mobile from) { BaseRanged ranged = from.Weapon as BaseRanged; if (ranged == null) { SendLocalizedMessageTo(from, 500593); // You must practice with ranged weapons on return; } if (DateTime.UtcNow < (m_LastUse + UseDelay)) return; Point3D worldLoc = GetWorldLocation(); if (FacingEast ? from.X <= worldLoc.X : from.Y <= worldLoc.Y) { from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 500596); // You would do better to stand in front of the archery butte. return; } if (FacingEast ? from.Y != worldLoc.Y : from.X != worldLoc.X) { from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 500597); // You aren't properly lined up with the archery butte to get an accurate shot. return; } if (!from.InRange(worldLoc, 6)) { from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 500598); // You are too far away from the archery butte to get an accurate shot. return; } if (from.InRange(worldLoc, 4)) { from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 500599); // You are too close to the target. return; } Type ammoType = ranged.AmmoType; bool isArrow = (ammoType == typeof(Arrow)); bool isBolt = (ammoType == typeof(Bolt)); BaseThrown thrown = ranged as BaseThrown; if (ammoType == null && thrown == null) { isArrow = ranged.Animation == WeaponAnimation.ShootBow; isBolt = ranged.Animation == WeaponAnimation.ShootXBow; } bool isKnown = (isArrow || isBolt); if (thrown == null) { Container pack = from.Backpack; if (pack == null || ammoType == null || !pack.ConsumeTotal(ammoType, 1)) { if (isArrow) from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 500594); // You do not have any arrows with which to practice. else if (isBolt) from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 500595); // You do not have any crossbow bolts with which to practice. else SendLocalizedMessageTo(from, 500593); // You must practice with ranged weapons on return; } } m_LastUse = DateTime.UtcNow; from.MovingEffect(this, ranged.EffectID, 18, 1, false, false); from.Direction = from.GetDirectionTo(GetWorldLocation()); ranged.PlaySwingAnimation(from); ScoreEntry se = GetEntryFor(from); if (!from.CheckSkill(ranged.Skill, m_MinSkill, m_MaxSkill)) { from.PlaySound(ranged.MissSound); PublicOverheadMessage(MessageType.Regular, 0x3B2, 500604, from.Name); // You miss the target altogether. se.Record(0); if (se.Count == 1) PublicOverheadMessage(MessageType.Regular, 0x3B2, 1062719, se.Total.ToString()); else PublicOverheadMessage(MessageType.Regular, 0x3B2, 1042683, string.Format("{0}\t{1}", se.Total, se.Count)); return; } Effects.PlaySound(Location, Map, 0x2B1); double rand = Utility.RandomDouble(); int area, score, splitScore; if (0.10 > rand) { area = 0; // bullseye score = 50; splitScore = 100; } else if (0.25 > rand) { area = 1; // inner ring score = 10; splitScore = 20; } else if (0.50 > rand) { area = 2; // middle ring score = 5; splitScore = 15; } else { area = 3; // outer ring score = 2; splitScore = 5; } bool split = (isKnown && ((m_Arrows + m_Bolts) * 0.02) > Utility.RandomDouble()); if (split) { PublicOverheadMessage(MessageType.Regular, 0x3B2, 1010027 + area, string.Format("{0}\t{1}", from.Name, isArrow ? "arrow" : "bolt")); } else { PublicOverheadMessage(MessageType.Regular, 0x3B2, 1010035 + area, from.Name); if (ammoType != null) { if (isArrow) ++m_Arrows; else if (isBolt) ++m_Bolts; } } se.Record(split ? splitScore : score); if (se.Count == 1) PublicOverheadMessage(MessageType.Regular, 0x3B2, 1062719, se.Total.ToString()); else PublicOverheadMessage(MessageType.Regular, 0x3B2, 1042683, string.Format("{0}\t{1}", se.Total, se.Count)); } public override void Serialize(GenericWriter writer) { base.Serialize(writer); writer.Write(0); writer.Write(m_MinSkill); writer.Write(m_MaxSkill); writer.Write(m_Arrows); writer.Write(m_Bolts); } public override void Deserialize(GenericReader reader) { base.Deserialize(reader); int version = reader.ReadInt(); switch (version) { case 0: { m_MinSkill = reader.ReadDouble(); m_MaxSkill = reader.ReadDouble(); m_Arrows = reader.ReadInt(); m_Bolts = reader.ReadInt(); if (m_MinSkill == 0.0 && m_MaxSkill == 30.0) { m_MinSkill = -25.0; m_MaxSkill = +25.0; } break; } } } private ScoreEntry GetEntryFor(Mobile from) { if (m_Entries == null) m_Entries = new Hashtable(); ScoreEntry e = (ScoreEntry)m_Entries[from]; if (e == null) m_Entries[from] = e = new ScoreEntry(); return e; } private class ScoreEntry { private int m_Total; private int m_Count; public int Total { get { return m_Total; } set { m_Total = value; } } public int Count { get { return m_Count; } set { m_Count = value; } } public void Record(int score) { m_Total += score; m_Count += 1; } } } public class ArcheryButteAddon : BaseAddon { [Constructable] public ArcheryButteAddon(AddonFacing facing) { switch (facing) { case AddonFacing.East: AddComponent(new ArcheryButte(0x100A), 0, 0, 0); break; case AddonFacing.South: AddComponent(new ArcheryButte(0x100B), 0, 0, 0); break; } } public ArcheryButteAddon(Serial serial) : base(serial) { } public override BaseAddonDeed Deed => new ArcheryButteDeed(); public override void Serialize(GenericWriter writer) { base.Serialize(writer); writer.Write(0); // version } public override void Deserialize(GenericReader reader) { base.Deserialize(reader); reader.ReadInt(); } } public class ArcheryButteDeed : BaseAddonDeed, IRewardOption { private AddonFacing Facing { get; set; } [Constructable] public ArcheryButteDeed() { } public ArcheryButteDeed(Serial serial) : base(serial) { } public override BaseAddon Addon => new ArcheryButteAddon(Facing); public override int LabelNumber => 1080205;// archery butte public override void OnDoubleClick(Mobile from) { if (IsChildOf(from.Backpack)) { from.CloseGump(typeof(RewardOptionGump)); from.SendGump(new RewardOptionGump(this)); } else from.SendLocalizedMessage(1062334); // This item must be in your backpack to be used. } public override void Serialize(GenericWriter writer) { base.Serialize(writer); writer.Write(0); // version } public override void Deserialize(GenericReader reader) { base.Deserialize(reader); reader.ReadInt(); } public void GetOptions(RewardOptionList list) { list.Add((int)AddonFacing.South, 1080204); list.Add((int)AddonFacing.East, 1080203); } public void OnOptionSelected(Mobile from, int choice) { Facing = (AddonFacing)choice; if (!Deleted) base.OnDoubleClick(from); } } }