636 lines
21 KiB
C#
636 lines
21 KiB
C#
using System;
|
|
using Server;
|
|
using System.Collections.Generic;
|
|
using Server.ContextMenus;
|
|
using Server.Engines.Despise;
|
|
using Server.Items;
|
|
using Server.Gumps;
|
|
using Server.Network;
|
|
using Server.Engines.Quests;
|
|
|
|
namespace Server.Mobiles
|
|
{
|
|
[CorpseName("a wisp corpse")]
|
|
public class MysteriousWisp : BaseCreature
|
|
{
|
|
private int m_ItemCountMin;
|
|
private int m_ItemCountMax;
|
|
private int m_IntensityMin;
|
|
private int m_IntensityMax;
|
|
private int m_RestockMin;
|
|
private int m_RestockMax;
|
|
private double m_MutateChance;
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int ItemCountMin { get { return m_ItemCountMin; } set { m_ItemCountMin = value; } }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int ItemCountMax { get { return m_ItemCountMax; } set { m_ItemCountMax = value; } }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int IntensityMin { get { return m_IntensityMin; } set { m_IntensityMin = value; } }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int IntensityMax { get { return m_IntensityMax; } set { m_IntensityMax = value; } }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int RestockMin { get { return m_RestockMin; } set { m_RestockMin = value; } }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int RestockMax { get { return m_RestockMax; } set { m_RestockMax = value; } }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public double MutateChance { get { return m_MutateChance; } set { m_MutateChance = value; } }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public bool ForceRestock { get { return false; } set { if (value) DoRestock(); } }
|
|
|
|
[Constructable]
|
|
public MysteriousWisp()
|
|
: base(AIType.AI_Mage, FightMode.None, 10, 1, 0.2, 0.4)
|
|
{
|
|
CantWalk = true;
|
|
Blessed = true;
|
|
SpeechHue = 52;
|
|
|
|
Name = "a mysterious wisp";
|
|
Body = 58;
|
|
BaseSoundID = 466;
|
|
|
|
SetStr(196, 225);
|
|
SetDex(196, 225);
|
|
SetInt(196, 225);
|
|
|
|
SetHits(118, 135);
|
|
|
|
SetDamage(17, 18);
|
|
|
|
SetDamageType(ResistanceType.Physical, 50);
|
|
SetDamageType(ResistanceType.Energy, 50);
|
|
|
|
SetResistance(ResistanceType.Physical, 35, 45);
|
|
SetResistance(ResistanceType.Fire, 20, 40);
|
|
SetResistance(ResistanceType.Cold, 10, 30);
|
|
SetResistance(ResistanceType.Poison, 5, 10);
|
|
SetResistance(ResistanceType.Energy, 50, 70);
|
|
|
|
SetSkill(SkillName.EvalInt, 80.0);
|
|
SetSkill(SkillName.Magery, 80.0);
|
|
SetSkill(SkillName.MagicResist, 80.0);
|
|
SetSkill(SkillName.Tactics, 80.0);
|
|
SetSkill(SkillName.Wrestling, 80.0);
|
|
|
|
if (Backpack != null)
|
|
Backpack.Delete();
|
|
|
|
m_ItemCountMin = 14;
|
|
m_ItemCountMax = 20;
|
|
m_IntensityMin = 40;
|
|
m_IntensityMax = 100;
|
|
m_RestockMin = 45;
|
|
m_RestockMax = 65;
|
|
m_MutateChance = 0.10;
|
|
|
|
AddItem(new BuyBackpack());
|
|
DoRestock();
|
|
}
|
|
|
|
public override bool HandlesOnSpeech(Mobile from)
|
|
{
|
|
return m_Conversation.ContainsKey(from);
|
|
}
|
|
|
|
private Dictionary<Mobile, int> m_Conversation = new Dictionary<Mobile, int>();
|
|
|
|
public override void OnDoubleClick(Mobile from)
|
|
{
|
|
if (from.InRange(this.Location, 5))
|
|
{
|
|
if (!m_Conversation.ContainsKey(from))
|
|
{
|
|
SayTo(from, m_Responses[0]);
|
|
m_Conversation[from] = 1;
|
|
}
|
|
}
|
|
|
|
if (from is PlayerMobile)
|
|
{
|
|
var pm = from as PlayerMobile;
|
|
|
|
if (QuestHelper.CheckDoneOnce(pm, typeof(WishesOfTheWispQuest), null, false))
|
|
{
|
|
var q = QuestHelper.GetQuest<WhisperingWithWispsQuest>(pm);
|
|
|
|
if (q == null)
|
|
{
|
|
BaseQuest quest = QuestHelper.RandomQuest(pm, new Type[] { typeof(WhisperingWithWispsQuest) }, this);
|
|
|
|
if (quest != null)
|
|
{
|
|
pm.CloseGump(typeof(MondainQuestGump));
|
|
pm.SendGump(new MondainQuestGump(quest));
|
|
}
|
|
}
|
|
else if (q.Completed)
|
|
{
|
|
q.CompleteQuest();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public override void OnMovement(Mobile m, Point3D oldLocation)
|
|
{
|
|
if (m is PlayerMobile && InRange(m.Location, 5) && !InRange(oldLocation, 5))
|
|
{
|
|
WishesOfTheWispQuest quest = QuestHelper.GetQuest<WishesOfTheWispQuest>((PlayerMobile)m);
|
|
|
|
if (quest != null)
|
|
{
|
|
quest.CompleteQuest();
|
|
}
|
|
}
|
|
}
|
|
|
|
public override void OnSpeech(SpeechEventArgs e)
|
|
{
|
|
if (!m_Conversation.ContainsKey(e.Mobile))
|
|
return;
|
|
|
|
string speech = e.Speech.ToLower();
|
|
int idx = m_Conversation[e.Mobile];
|
|
|
|
if (idx < 0 || idx >= m_Keywords.Length)
|
|
{
|
|
m_Conversation.Remove(e.Mobile);
|
|
return;
|
|
}
|
|
|
|
foreach (string str in m_Keywords[idx])
|
|
{
|
|
if (speech.Contains(str))
|
|
{
|
|
SayTo(e.Mobile, m_Responses[idx]);
|
|
|
|
if (idx + 1 >= m_Keywords.Length)
|
|
m_Conversation.Remove(e.Mobile);
|
|
else
|
|
m_Conversation[e.Mobile]++;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private string[][] m_Keywords = new string[][]
|
|
{
|
|
new string[] { },
|
|
new string[] { "corporeal" },
|
|
new string[] { "sentient" },
|
|
new string[] { "deal" },
|
|
new string[] { "learn", "teach" },
|
|
new string[] { "good and evil", "good", "evil" },
|
|
new string[] { "guide" },
|
|
new string[] { "follow" },
|
|
new string[] { "fight" },
|
|
new string[] { "resonate" },
|
|
new string[] { "join" },
|
|
new string[] { "command" },
|
|
new string[] { "trade" },
|
|
};
|
|
|
|
private int[] m_Responses = new int[]
|
|
{
|
|
1153441,
|
|
1153443,
|
|
1153445,
|
|
1153447,
|
|
1153449,
|
|
1153451,
|
|
1153453,
|
|
1153455,
|
|
1153457,
|
|
1153459,
|
|
1153461,
|
|
1153463,
|
|
1153467
|
|
};
|
|
|
|
public override void GetContextMenuEntries(Mobile from, List<ContextMenuEntry> list)
|
|
{
|
|
base.GetContextMenuEntries(from, list);
|
|
|
|
list.Add(new InternalEntry(this, from));
|
|
}
|
|
|
|
private Dictionary<Item, int> m_ItemTable = new Dictionary<Item, int>();
|
|
|
|
public void CheckRestock()
|
|
{
|
|
if (m_NextRestock <= DateTime.UtcNow)
|
|
DoRestock();
|
|
}
|
|
|
|
private void DoRestock()
|
|
{
|
|
List<Item> list = new List<Item>(this.Backpack.Items);
|
|
m_ItemTable.Clear();
|
|
|
|
InternalGump.Clear();
|
|
|
|
foreach (Item item in list)
|
|
item.Delete();
|
|
|
|
int count = Utility.RandomMinMax(m_ItemCountMin, m_ItemCountMax);
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
Item item;
|
|
|
|
switch (Utility.Random(3))
|
|
{
|
|
default:
|
|
case 0: item = Loot.RandomArmorOrShieldOrWeaponOrJewelry(false, false, true); break;
|
|
case 1: item = Loot.RandomArmorOrShieldOrWeaponOrJewelry(false, true, false); break;
|
|
case 2: item = Loot.RandomArmorOrShieldOrWeaponOrJewelry(true, false, false); break;
|
|
}
|
|
|
|
int weight = 0;
|
|
|
|
if (0.10 > Utility.RandomDouble())
|
|
{
|
|
int budget = Utility.RandomMinMax(500, 860);
|
|
RunicReforging.GenerateRandomItem(item, null, Utility.RandomMinMax(500, 860), 0, ReforgedPrefix.None, ReforgedSuffix.None);
|
|
weight += budget / 3;
|
|
}
|
|
else
|
|
{
|
|
int props = Utility.RandomMinMax(3, 5);
|
|
|
|
if (props == 5 && 0.05 > Utility.RandomDouble())
|
|
{
|
|
props++;
|
|
weight += 100;
|
|
}
|
|
|
|
if (item is BaseWeapon)
|
|
{
|
|
BaseRunicTool.ApplyAttributesTo((BaseWeapon)item, false, 0, props, m_IntensityMin, m_IntensityMax);
|
|
}
|
|
else if (item is BaseArmor)
|
|
{
|
|
BaseRunicTool.ApplyAttributesTo((BaseArmor)item, false, 0, props, m_IntensityMin, m_IntensityMax);
|
|
}
|
|
else if (item is BaseJewel)
|
|
{
|
|
BaseRunicTool.ApplyAttributesTo((BaseJewel)item, false, 0, props, m_IntensityMin, m_IntensityMax);
|
|
}
|
|
else if (item is BaseHat)
|
|
{
|
|
BaseRunicTool.ApplyAttributesTo((BaseHat)item, false, 0, props, m_IntensityMin, m_IntensityMax);
|
|
}
|
|
}
|
|
|
|
m_ItemTable[item] = (int)((weight + Server.SkillHandlers.Imbuing.GetTotalWeight(item, -1, false, true)) * 31.5);
|
|
item.Movable = false;
|
|
this.Backpack.DropItem(item);
|
|
}
|
|
|
|
m_NextRestock = DateTime.UtcNow + TimeSpan.FromMinutes(Utility.RandomMinMax(m_RestockMin, m_RestockMax));
|
|
}
|
|
|
|
public int GetCostFor(Item item)
|
|
{
|
|
if (!m_ItemTable.ContainsKey(item))
|
|
return -1;
|
|
|
|
return m_ItemTable[item];
|
|
}
|
|
|
|
public void TryBuyItem(Mobile from, Item item)
|
|
{
|
|
if (item.Deleted || !this.Backpack.Items.Contains(item) || !m_ItemTable.ContainsKey(item))
|
|
{
|
|
from.SendMessage("This item is no longer available.");
|
|
return;
|
|
}
|
|
|
|
int points = (int)Server.Engines.Points.PointsSystem.DespiseCrystals.GetPoints(from);
|
|
int cost = m_ItemTable[item];
|
|
|
|
if (points >= cost)
|
|
{
|
|
//DespiseController.Instance.DeductDespisePoints(from, cost);
|
|
Server.Engines.Points.PointsSystem.DespiseCrystals.DeductPoints(from, cost);
|
|
item.Movable = true;
|
|
|
|
if (from.Backpack == null || !from.Backpack.TryDropItem(from, item, false))
|
|
item.MoveToWorld(from.Location, from.Map);
|
|
|
|
from.SendLocalizedMessage(1153427, cost.ToString()); // You have spent ~1_AMT~ Dungeon Crystal Points of Despise.
|
|
|
|
InternalGump.Resend(this);
|
|
}
|
|
else
|
|
from.SendMessage("You need {0} more Despise dungeon crystal points for this.", cost - points);
|
|
}
|
|
|
|
private class InternalEntry : ContextMenuEntry
|
|
{
|
|
private Mobile m_Clicker;
|
|
private MysteriousWisp m_Wisp;
|
|
|
|
public InternalEntry(MysteriousWisp wisp, Mobile clicker)
|
|
: base(1150143, 3)
|
|
{
|
|
m_Clicker = clicker;
|
|
m_Wisp = wisp;
|
|
}
|
|
|
|
public override void OnClick()
|
|
{
|
|
if (m_Clicker.Alive && !m_Wisp.Deleted && m_Clicker.InRange(m_Wisp.Location, 3))
|
|
{
|
|
if (m_Wisp.Backpack == null)
|
|
{
|
|
Console.WriteLine("Error, wisp backpack is null");
|
|
return;
|
|
}
|
|
|
|
m_Wisp.CheckRestock();
|
|
m_Clicker.SendGump(new InternalGump(m_Wisp, m_Clicker));
|
|
}
|
|
}
|
|
}
|
|
|
|
private DateTime m_NextRestock;
|
|
|
|
public MysteriousWisp(Serial serial)
|
|
: base(serial)
|
|
{
|
|
}
|
|
|
|
public override void Serialize(GenericWriter writer)
|
|
{
|
|
base.Serialize(writer);
|
|
writer.Write((int)0);
|
|
|
|
writer.Write(m_ItemCountMin);
|
|
writer.Write(m_ItemCountMax);
|
|
writer.Write(m_IntensityMin);
|
|
writer.Write(m_IntensityMax);
|
|
writer.Write(m_MutateChance);
|
|
}
|
|
|
|
public override void Deserialize(GenericReader reader)
|
|
{
|
|
base.Deserialize(reader);
|
|
int version = reader.ReadInt();
|
|
|
|
m_ItemCountMin = reader.ReadInt();
|
|
m_ItemCountMax = reader.ReadInt();
|
|
m_IntensityMin = reader.ReadInt();
|
|
m_IntensityMax = reader.ReadInt();
|
|
m_MutateChance = reader.ReadDouble();
|
|
|
|
m_NextRestock = DateTime.UtcNow;
|
|
}
|
|
|
|
public class BuyBackpack : Backpack
|
|
{
|
|
public BuyBackpack()
|
|
{
|
|
Movable = false;
|
|
Layer = Layer.Backpack;
|
|
Weight = 1.0;
|
|
}
|
|
|
|
public override int DefaultMaxWeight { get { return 0; } }
|
|
|
|
public override bool IsAccessibleTo(Mobile m)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
public override bool CheckItemUse(Mobile from, Item item)
|
|
{
|
|
from.SendLocalizedMessage(500447); // That is not accessible.
|
|
return false;
|
|
}
|
|
|
|
public override void GetChildProperties(ObjectPropertyList list, Item item)
|
|
{
|
|
base.GetChildProperties(list, item);
|
|
|
|
MysteriousWisp wisp = RootParent as MysteriousWisp;
|
|
|
|
if (wisp == null)
|
|
return;
|
|
|
|
int cost = wisp.GetCostFor(item);
|
|
|
|
list.Add(1153425, cost.ToString()); // Price: ~1_AMT~ Despise Crystals
|
|
}
|
|
|
|
public BuyBackpack(Serial serial)
|
|
: base(serial)
|
|
{
|
|
}
|
|
|
|
public override void Serialize(GenericWriter writer)
|
|
{
|
|
base.Serialize(writer);
|
|
|
|
writer.Write((int)0); // version
|
|
}
|
|
|
|
public override void Deserialize(GenericReader reader)
|
|
{
|
|
base.Deserialize(reader);
|
|
|
|
int version = reader.ReadInt();
|
|
}
|
|
}
|
|
|
|
private class InternalGump : Gump
|
|
{
|
|
private static List<Mobile> m_Viewers = new List<Mobile>();
|
|
|
|
private MysteriousWisp m_Wisp;
|
|
private List<Item> m_Items;
|
|
|
|
public InternalGump(MysteriousWisp wisp, Mobile viewer) : base(40, 40)
|
|
{
|
|
m_Wisp = wisp;
|
|
if (!m_Viewers.Contains(viewer))
|
|
m_Viewers.Add(viewer);
|
|
|
|
AddBackground(0, 0, 320, 404, 0x13BE);
|
|
AddImageTiled(10, 10, 300, 20, 0xA40);
|
|
AddImageTiled(10, 40, 300, 324, 0xA40);
|
|
AddImageTiled(10, 374, 300, 20, 0xA40);
|
|
AddAlphaRegion(10, 10, 300, 384);
|
|
|
|
AddHtml(14, 12, 500, 20, "<BASEFONT COLOR=#FFFFFF>Mysterious Wisp Inventory</BASEFONT>", false, false);
|
|
|
|
AddButton(10, 374, 0xFB1, 0xFB2, 0, GumpButtonType.Reply, 0);
|
|
AddHtmlLocalized(45, 376, 450, 20, 1011012, 0x7FFF, false, false); // CANCEL
|
|
|
|
m_Items = new List<Item>(m_Wisp.Backpack.Items);
|
|
int current = 0;
|
|
|
|
for (int i = 0; i < m_Items.Count; i++)
|
|
{
|
|
Item item = m_Items[i];
|
|
|
|
int page = current / 5 + 1;
|
|
int pos = current % 5;
|
|
|
|
if (pos == 0)
|
|
{
|
|
if (page > 1)
|
|
{
|
|
AddButton(200, 374, 0xFA5, 0xFA7, 0, GumpButtonType.Page, page);
|
|
AddHtmlLocalized(240, 376, 60, 20, 1043353, 0x7FFF, false, false); // Next
|
|
}
|
|
|
|
AddPage(page);
|
|
|
|
if (page > 1)
|
|
{
|
|
AddButton(100, 374, 0xFAE, 0xFB0, 0, GumpButtonType.Page, page - 1);
|
|
AddHtmlLocalized(140, 376, 60, 20, 1011393, 0x7FFF, false, false); // Back
|
|
}
|
|
}
|
|
|
|
if (item != null && !item.Deleted)
|
|
{
|
|
int x = 14;
|
|
int y = (pos * 64) + 44;
|
|
|
|
Rectangle2D b = ItemBounds.Table[item.ItemID];
|
|
|
|
AddImageTiledButton(x, y, 0x918, 0x919, i + 1, GumpButtonType.Reply, 0, item.ItemID, item.Hue, 40 - b.Width / 2 - b.X, 30 - b.Height / 2 - b.Y);
|
|
AddItemProperty(item.Serial);
|
|
|
|
string cost = m_Wisp.GetCostFor(item) > -1 ? m_Wisp.GetCostFor(item).ToString("###,###,###") : "No Longer Available";
|
|
|
|
AddHtmlLocalized(x + 84, y, 250, 60, item.LabelNumber, 0x7FFF, false, false);
|
|
AddHtml(x + 84, y + 20, 250, 60, "<BASEFONT COLOR=#FFFFFF>" + cost + "</BASEFONT>", false, false);
|
|
|
|
current++;
|
|
}
|
|
}
|
|
}
|
|
|
|
public override void OnResponse(NetState state, RelayInfo info)
|
|
{
|
|
Mobile from = state.Mobile;
|
|
|
|
if (m_Viewers.Contains(from))
|
|
m_Viewers.Remove(from);
|
|
|
|
int idx = info.ButtonID - 1;
|
|
|
|
if (idx >= 0 && idx < m_Items.Count)
|
|
{
|
|
Item item = m_Items[idx];
|
|
|
|
if (item != null && !item.Deleted && m_Wisp.Backpack != null && m_Wisp.Backpack.Items.Contains(item))
|
|
{
|
|
from.SendGump(new InternalGump2(m_Wisp, item));
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void Resend(MysteriousWisp wisp)
|
|
{
|
|
if (m_Viewers == null || m_Viewers.Count == 0)
|
|
return;
|
|
|
|
List<Mobile> viewers = new List<Mobile>(m_Viewers);
|
|
Type t = typeof(Server.Mobiles.MysteriousWisp.InternalGump);
|
|
|
|
foreach (Mobile m in viewers)
|
|
{
|
|
if (m.HasGump(t))
|
|
{
|
|
m.CloseGump(t);
|
|
|
|
if (m_Viewers.Contains(m))
|
|
m_Viewers.Remove(m);
|
|
|
|
if(wisp.InRange(m.Location, 5) && wisp.Backpack != null)
|
|
m.SendGump(new InternalGump(wisp, m));
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void Clear()
|
|
{
|
|
if (m_Viewers == null || m_Viewers.Count == 0)
|
|
return;
|
|
|
|
List<Mobile> viewers = new List<Mobile>(m_Viewers);
|
|
Type t = typeof(Server.Mobiles.MysteriousWisp.InternalGump);
|
|
|
|
foreach (Mobile m in viewers)
|
|
{
|
|
if (m.HasGump(t))
|
|
m.CloseGump(t);
|
|
}
|
|
|
|
m_Viewers.Clear();
|
|
}
|
|
}
|
|
|
|
private class InternalGump2 : Gump
|
|
{
|
|
private MysteriousWisp m_Wisp;
|
|
private Item m_Item;
|
|
private bool m_Available;
|
|
|
|
public InternalGump2(MysteriousWisp wisp, Item item) : base(40, 40)
|
|
{
|
|
m_Wisp = wisp;
|
|
m_Item = item;
|
|
|
|
AddImageTiled(0, 0, 300, 180, 0xA8E);
|
|
AddAlphaRegion(0, 0, 300, 180);
|
|
AddBackground(150, 10, 130, 130, 0xBB8);
|
|
|
|
AddHtml(10, 10, 150, 20, "<BASEFONT COLOR=#FFFFFF>CONFIRM PURCHASE:</BASEFONT>", false, false);
|
|
AddHtmlLocalized(10, 30, 140, 20, item.LabelNumber, 0x7FFF, false, false);
|
|
|
|
m_Available = m_Wisp.GetCostFor(item) > -1;
|
|
string cost = m_Available ? m_Wisp.GetCostFor(item).ToString() : "No Longer Available";
|
|
|
|
AddHtml(10, 100, 140, 20, "<BASEFONT COLOR=#FFFFFF>Despise Crystals:</BASEFONT>", false, false);
|
|
AddHtml(10, 120, 140, 20, "<BASEFONT COLOR=#FFFFFF>"+cost+"</BASEFONT>", false, false);
|
|
|
|
Rectangle2D b = ItemBounds.Table[item.ItemID];
|
|
AddItem(210 - b.Width / 2 - b.X, 70 - b.Height / 2 - b.Y, item.ItemID, item.Hue);
|
|
AddItemProperty(item.Serial);
|
|
|
|
if (m_Available)
|
|
AddButton(90, 150, 2128, 2129, 1, GumpButtonType.Reply, 0);
|
|
|
|
AddButton(155, 150, 2119, 2120, 0, GumpButtonType.Reply, 0);
|
|
}
|
|
|
|
public override void OnResponse(NetState state, RelayInfo info)
|
|
{
|
|
Mobile from = state.Mobile;
|
|
|
|
if (info.ButtonID == 1)
|
|
{
|
|
if (m_Available)
|
|
m_Wisp.TryBuyItem(from, m_Item);
|
|
else
|
|
from.SendMessage("That item is no longer available");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|