Formats UO Content (#201)
This commit is contained in:
parent
86b7b3aed1
commit
ad3775c4d7
3249 changed files with 431083 additions and 440981 deletions
|
|
@ -5,87 +5,110 @@ using Server.Targeting;
|
|||
|
||||
namespace Server.SkillHandlers
|
||||
{
|
||||
public static class Anatomy
|
||||
{
|
||||
public static void Initialize()
|
||||
public static class Anatomy
|
||||
{
|
||||
SkillInfo.Table[(int)SkillName.Anatomy].Callback = OnUse;
|
||||
public static void Initialize()
|
||||
{
|
||||
SkillInfo.Table[(int)SkillName.Anatomy].Callback = OnUse;
|
||||
}
|
||||
|
||||
public static TimeSpan OnUse(Mobile m)
|
||||
{
|
||||
m.Target = new InternalTarget();
|
||||
|
||||
m.SendLocalizedMessage(500321); // Whom shall I examine?
|
||||
|
||||
return TimeSpan.FromSeconds(1.0);
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
public InternalTarget() : base(8, false, TargetFlags.None)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnTarget(Mobile from, object targeted)
|
||||
{
|
||||
if (from == targeted)
|
||||
{
|
||||
from.LocalOverheadMessage(
|
||||
MessageType.Regular,
|
||||
0x3B2,
|
||||
500324
|
||||
); // You know yourself quite well enough already.
|
||||
}
|
||||
else if (targeted is TownCrier crier)
|
||||
{
|
||||
crier.PrivateOverheadMessage(
|
||||
MessageType.Regular,
|
||||
0x3B2,
|
||||
500322,
|
||||
from.NetState
|
||||
); // This person looks fine to me, though he may have some news...
|
||||
}
|
||||
else if (targeted is BaseVendor vendor && vendor.IsInvulnerable)
|
||||
{
|
||||
vendor.PrivateOverheadMessage(
|
||||
MessageType.Regular,
|
||||
0x3B2,
|
||||
500326,
|
||||
from.NetState
|
||||
); // That can not be inspected.
|
||||
}
|
||||
else if (targeted is Mobile targ)
|
||||
{
|
||||
var marginOfError = Math.Max(0, 25 - (int)(from.Skills.Anatomy.Value / 4));
|
||||
|
||||
var str = targ.Str + Utility.RandomMinMax(-marginOfError, +marginOfError);
|
||||
var dex = targ.Dex + Utility.RandomMinMax(-marginOfError, +marginOfError);
|
||||
var stm = targ.Stam * 100 / Math.Max(targ.StamMax, 1) +
|
||||
Utility.RandomMinMax(-marginOfError, +marginOfError);
|
||||
|
||||
var strMod = str / 10;
|
||||
var dexMod = dex / 10;
|
||||
var stmMod = stm / 10;
|
||||
|
||||
if (strMod < 0) strMod = 0;
|
||||
else if (strMod > 10) strMod = 10;
|
||||
|
||||
if (dexMod < 0) dexMod = 0;
|
||||
else if (dexMod > 10) dexMod = 10;
|
||||
|
||||
if (stmMod > 10) stmMod = 10;
|
||||
else if (stmMod < 0) stmMod = 0;
|
||||
|
||||
if (from.CheckTargetSkill(SkillName.Anatomy, targ, 0, 100))
|
||||
{
|
||||
targ.PrivateOverheadMessage(
|
||||
MessageType.Regular,
|
||||
0x3B2,
|
||||
1038045 + strMod * 11 + dexMod,
|
||||
from.NetState
|
||||
); // That looks [strong] and [dexterous].
|
||||
|
||||
if (from.Skills.Anatomy.Base >= 65.0)
|
||||
targ.PrivateOverheadMessage(
|
||||
MessageType.Regular,
|
||||
0x3B2,
|
||||
1038303 + stmMod,
|
||||
from.NetState
|
||||
); // That being is at [10,20,...] percent endurance.
|
||||
}
|
||||
else
|
||||
{
|
||||
targ.PrivateOverheadMessage(
|
||||
MessageType.Regular,
|
||||
0x3B2,
|
||||
1042666,
|
||||
from.NetState
|
||||
); // You can not quite get a sense of their physical characteristics.
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
(targeted as Item)?.SendLocalizedMessageTo(from, 500323, ""); // Only living things have anatomies!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static TimeSpan OnUse(Mobile m)
|
||||
{
|
||||
m.Target = new InternalTarget();
|
||||
|
||||
m.SendLocalizedMessage(500321); // Whom shall I examine?
|
||||
|
||||
return TimeSpan.FromSeconds(1.0);
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
public InternalTarget() : base(8, false, TargetFlags.None)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnTarget(Mobile from, object targeted)
|
||||
{
|
||||
if (from == targeted)
|
||||
{
|
||||
from.LocalOverheadMessage(MessageType.Regular, 0x3B2,
|
||||
500324); // You know yourself quite well enough already.
|
||||
}
|
||||
else if (targeted is TownCrier crier)
|
||||
{
|
||||
crier.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 500322,
|
||||
from.NetState); // This person looks fine to me, though he may have some news...
|
||||
}
|
||||
else if (targeted is BaseVendor vendor && vendor.IsInvulnerable)
|
||||
{
|
||||
vendor.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 500326,
|
||||
from.NetState); // That can not be inspected.
|
||||
}
|
||||
else if (targeted is Mobile targ)
|
||||
{
|
||||
int marginOfError = Math.Max(0, 25 - (int)(from.Skills.Anatomy.Value / 4));
|
||||
|
||||
int str = targ.Str + Utility.RandomMinMax(-marginOfError, +marginOfError);
|
||||
int dex = targ.Dex + Utility.RandomMinMax(-marginOfError, +marginOfError);
|
||||
int stm = targ.Stam * 100 / Math.Max(targ.StamMax, 1) +
|
||||
Utility.RandomMinMax(-marginOfError, +marginOfError);
|
||||
|
||||
int strMod = str / 10;
|
||||
int dexMod = dex / 10;
|
||||
int stmMod = stm / 10;
|
||||
|
||||
if (strMod < 0) strMod = 0;
|
||||
else if (strMod > 10) strMod = 10;
|
||||
|
||||
if (dexMod < 0) dexMod = 0;
|
||||
else if (dexMod > 10) dexMod = 10;
|
||||
|
||||
if (stmMod > 10) stmMod = 10;
|
||||
else if (stmMod < 0) stmMod = 0;
|
||||
|
||||
if (from.CheckTargetSkill(SkillName.Anatomy, targ, 0, 100))
|
||||
{
|
||||
targ.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 1038045 + strMod * 11 + dexMod,
|
||||
from.NetState); // That looks [strong] and [dexterous].
|
||||
|
||||
if (from.Skills.Anatomy.Base >= 65.0)
|
||||
targ.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 1038303 + stmMod,
|
||||
from.NetState); // That being is at [10,20,...] percent endurance.
|
||||
}
|
||||
else
|
||||
{
|
||||
targ.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 1042666,
|
||||
from.NetState); // You can not quite get a sense of their physical characteristics.
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
(targeted as Item)?.SendLocalizedMessageTo(from, 500323, ""); // Only living things have anatomies!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,366 +6,380 @@ using Server.Targeting;
|
|||
|
||||
namespace Server.SkillHandlers
|
||||
{
|
||||
public static class AnimalLore
|
||||
{
|
||||
public static void Initialize()
|
||||
public static class AnimalLore
|
||||
{
|
||||
SkillInfo.Table[(int)SkillName.AnimalLore].Callback = OnUse;
|
||||
}
|
||||
|
||||
public static TimeSpan OnUse(Mobile m)
|
||||
{
|
||||
m.Target = new InternalTarget();
|
||||
|
||||
m.SendLocalizedMessage(500328); // What animal should I look at?
|
||||
|
||||
return TimeSpan.FromSeconds(1.0);
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
public InternalTarget() : base(8, false, TargetFlags.None)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnTarget(Mobile from, object targeted)
|
||||
{
|
||||
if (!from.Alive)
|
||||
public static void Initialize()
|
||||
{
|
||||
from.SendLocalizedMessage(500331); // The spirits of the dead are not the province of animal lore.
|
||||
SkillInfo.Table[(int)SkillName.AnimalLore].Callback = OnUse;
|
||||
}
|
||||
else if (targeted is BaseCreature c)
|
||||
|
||||
public static TimeSpan OnUse(Mobile m)
|
||||
{
|
||||
if (!c.IsDeadPet)
|
||||
{
|
||||
if (c.Body.IsAnimal || c.Body.IsMonster || c.Body.IsSea)
|
||||
m.Target = new InternalTarget();
|
||||
|
||||
m.SendLocalizedMessage(500328); // What animal should I look at?
|
||||
|
||||
return TimeSpan.FromSeconds(1.0);
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
public InternalTarget() : base(8, false, TargetFlags.None)
|
||||
{
|
||||
if (!c.Controlled && from.Skills.AnimalLore.Value < 100.0)
|
||||
{
|
||||
from.SendLocalizedMessage(
|
||||
1049674); // At your skill level, you can only lore tamed creatures.
|
||||
}
|
||||
else if (!c.Controlled && !c.Tamable && from.Skills.AnimalLore.Value < 110.0)
|
||||
{
|
||||
from.SendLocalizedMessage(
|
||||
1049675); // At your skill level, you can only lore tamed or tameable creatures.
|
||||
}
|
||||
else if (!from.CheckTargetSkill(SkillName.AnimalLore, c, 0.0, 120.0))
|
||||
{
|
||||
from.SendLocalizedMessage(500334); // You can't think of anything you know offhand.
|
||||
}
|
||||
else
|
||||
{
|
||||
from.CloseGump<AnimalLoreGump>();
|
||||
from.SendGump(new AnimalLoreGump(c));
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnTarget(Mobile from, object targeted)
|
||||
{
|
||||
if (!from.Alive)
|
||||
{
|
||||
from.SendLocalizedMessage(500331); // The spirits of the dead are not the province of animal lore.
|
||||
}
|
||||
else if (targeted is BaseCreature c)
|
||||
{
|
||||
if (!c.IsDeadPet)
|
||||
{
|
||||
if (c.Body.IsAnimal || c.Body.IsMonster || c.Body.IsSea)
|
||||
{
|
||||
if (!c.Controlled && from.Skills.AnimalLore.Value < 100.0)
|
||||
{
|
||||
from.SendLocalizedMessage(
|
||||
1049674
|
||||
); // At your skill level, you can only lore tamed creatures.
|
||||
}
|
||||
else if (!c.Controlled && !c.Tamable && from.Skills.AnimalLore.Value < 110.0)
|
||||
{
|
||||
from.SendLocalizedMessage(
|
||||
1049675
|
||||
); // At your skill level, you can only lore tamed or tameable creatures.
|
||||
}
|
||||
else if (!from.CheckTargetSkill(SkillName.AnimalLore, c, 0.0, 120.0))
|
||||
{
|
||||
from.SendLocalizedMessage(500334); // You can't think of anything you know offhand.
|
||||
}
|
||||
else
|
||||
{
|
||||
from.CloseGump<AnimalLoreGump>();
|
||||
from.SendGump(new AnimalLoreGump(c));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(500329); // That's not an animal!
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(500331); // The spirits of the dead are not the province of animal lore.
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(500329); // That's not an animal!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class AnimalLoreGump : Gump
|
||||
{
|
||||
private const int LabelColor = 0x24E5;
|
||||
|
||||
public AnimalLoreGump(BaseCreature c) : base(250, 50)
|
||||
{
|
||||
AddPage(0);
|
||||
|
||||
AddImage(100, 100, 2080);
|
||||
AddImage(118, 137, 2081);
|
||||
AddImage(118, 207, 2081);
|
||||
AddImage(118, 277, 2081);
|
||||
AddImage(118, 347, 2083);
|
||||
|
||||
AddHtml(147, 108, 210, 18, $"<center><i>{c.Name}</i></center>");
|
||||
|
||||
AddButton(240, 77, 2093, 2093, 2);
|
||||
|
||||
AddImage(140, 138, 2091);
|
||||
AddImage(140, 335, 2091);
|
||||
|
||||
var pages = Core.AOS ? 5 : 3;
|
||||
var page = 0;
|
||||
|
||||
AddPage(++page);
|
||||
|
||||
AddImage(128, 152, 2086);
|
||||
AddHtmlLocalized(147, 150, 160, 18, 1049593, 200); // Attributes
|
||||
|
||||
AddHtmlLocalized(153, 168, 160, 18, 1049578, LabelColor); // Hits
|
||||
AddHtml(280, 168, 75, 18, FormatAttributes(c.Hits, c.HitsMax));
|
||||
|
||||
AddHtmlLocalized(153, 186, 160, 18, 1049579, LabelColor); // Stamina
|
||||
AddHtml(280, 186, 75, 18, FormatAttributes(c.Stam, c.StamMax));
|
||||
|
||||
AddHtmlLocalized(153, 204, 160, 18, 1049580, LabelColor); // Mana
|
||||
AddHtml(280, 204, 75, 18, FormatAttributes(c.Mana, c.ManaMax));
|
||||
|
||||
AddHtmlLocalized(153, 222, 160, 18, 1028335, LabelColor); // Strength
|
||||
AddHtml(320, 222, 35, 18, FormatStat(c.Str));
|
||||
|
||||
AddHtmlLocalized(153, 240, 160, 18, 3000113, LabelColor); // Dexterity
|
||||
AddHtml(320, 240, 35, 18, FormatStat(c.Dex));
|
||||
|
||||
AddHtmlLocalized(153, 258, 160, 18, 3000112, LabelColor); // Intelligence
|
||||
AddHtml(320, 258, 35, 18, FormatStat(c.Int));
|
||||
|
||||
if (Core.AOS)
|
||||
{
|
||||
var y = 276;
|
||||
|
||||
if (Core.SE)
|
||||
{
|
||||
var bd = BaseInstrument.GetBaseDifficulty(c);
|
||||
if (c.Uncalmable)
|
||||
bd = 0;
|
||||
|
||||
AddHtmlLocalized(153, 276, 160, 18, 1070793, LabelColor); // Barding Difficulty
|
||||
AddHtml(320, y, 35, 18, FormatDouble(bd));
|
||||
|
||||
y += 18;
|
||||
}
|
||||
|
||||
AddImage(128, y + 2, 2086);
|
||||
AddHtmlLocalized(147, y, 160, 18, 1049594, 200); // Loyalty Rating
|
||||
y += 18;
|
||||
|
||||
AddHtmlLocalized(
|
||||
153,
|
||||
y,
|
||||
160,
|
||||
18,
|
||||
!c.Controlled || c.Loyalty == 0 ? 1061643 : 1049595 + c.Loyalty / 10,
|
||||
LabelColor
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(500329); // That's not an animal!
|
||||
AddImage(128, 278, 2086);
|
||||
AddHtmlLocalized(147, 276, 160, 18, 3001016, 200); // Miscellaneous
|
||||
|
||||
AddHtmlLocalized(153, 294, 160, 18, 1049581, LabelColor); // Armor Rating
|
||||
AddHtml(320, 294, 35, 18, FormatStat(c.VirtualArmor));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(500331); // The spirits of the dead are not the province of animal lore.
|
||||
}
|
||||
|
||||
AddButton(340, 358, 5601, 5605, 0, GumpButtonType.Page, page + 1);
|
||||
AddButton(317, 358, 5603, 5607, 0, GumpButtonType.Page, pages);
|
||||
|
||||
if (Core.AOS)
|
||||
{
|
||||
AddPage(++page);
|
||||
|
||||
AddImage(128, 152, 2086);
|
||||
AddHtmlLocalized(147, 150, 160, 18, 1061645, 200); // Resistances
|
||||
|
||||
AddHtmlLocalized(153, 168, 160, 18, 1061646, LabelColor); // Physical
|
||||
AddHtml(320, 168, 35, 18, FormatElement(c.PhysicalResistance));
|
||||
|
||||
AddHtmlLocalized(153, 186, 160, 18, 1061647, LabelColor); // Fire
|
||||
AddHtml(320, 186, 35, 18, FormatElement(c.FireResistance));
|
||||
|
||||
AddHtmlLocalized(153, 204, 160, 18, 1061648, LabelColor); // Cold
|
||||
AddHtml(320, 204, 35, 18, FormatElement(c.ColdResistance));
|
||||
|
||||
AddHtmlLocalized(153, 222, 160, 18, 1061649, LabelColor); // Poison
|
||||
AddHtml(320, 222, 35, 18, FormatElement(c.PoisonResistance));
|
||||
|
||||
AddHtmlLocalized(153, 240, 160, 18, 1061650, LabelColor); // Energy
|
||||
AddHtml(320, 240, 35, 18, FormatElement(c.EnergyResistance));
|
||||
|
||||
AddButton(340, 358, 5601, 5605, 0, GumpButtonType.Page, page + 1);
|
||||
AddButton(317, 358, 5603, 5607, 0, GumpButtonType.Page, page - 1);
|
||||
}
|
||||
|
||||
if (Core.AOS)
|
||||
{
|
||||
AddPage(++page);
|
||||
|
||||
AddImage(128, 152, 2086);
|
||||
AddHtmlLocalized(147, 150, 160, 18, 1017319, 200); // Damage
|
||||
|
||||
AddHtmlLocalized(153, 168, 160, 18, 1061646, LabelColor); // Physical
|
||||
AddHtml(320, 168, 35, 18, FormatElement(c.PhysicalDamage));
|
||||
|
||||
AddHtmlLocalized(153, 186, 160, 18, 1061647, LabelColor); // Fire
|
||||
AddHtml(320, 186, 35, 18, FormatElement(c.FireDamage));
|
||||
|
||||
AddHtmlLocalized(153, 204, 160, 18, 1061648, LabelColor); // Cold
|
||||
AddHtml(320, 204, 35, 18, FormatElement(c.ColdDamage));
|
||||
|
||||
AddHtmlLocalized(153, 222, 160, 18, 1061649, LabelColor); // Poison
|
||||
AddHtml(320, 222, 35, 18, FormatElement(c.PoisonDamage));
|
||||
|
||||
AddHtmlLocalized(153, 240, 160, 18, 1061650, LabelColor); // Energy
|
||||
AddHtml(320, 240, 35, 18, FormatElement(c.EnergyDamage));
|
||||
|
||||
if (Core.ML)
|
||||
{
|
||||
AddHtmlLocalized(153, 258, 160, 18, 1076750, LabelColor); // Base Damage
|
||||
AddHtml(300, 258, 55, 18, FormatDamage(c.DamageMin, c.DamageMax));
|
||||
}
|
||||
|
||||
AddButton(340, 358, 5601, 5605, 0, GumpButtonType.Page, page + 1);
|
||||
AddButton(317, 358, 5603, 5607, 0, GumpButtonType.Page, page - 1);
|
||||
}
|
||||
|
||||
AddPage(++page);
|
||||
|
||||
AddImage(128, 152, 2086);
|
||||
AddHtmlLocalized(147, 150, 160, 18, 3001030, 200); // Combat Ratings
|
||||
|
||||
AddHtmlLocalized(153, 168, 160, 18, 1044103, LabelColor); // Wrestling
|
||||
AddHtml(320, 168, 35, 18, FormatSkill(c, SkillName.Wrestling));
|
||||
|
||||
AddHtmlLocalized(153, 186, 160, 18, 1044087, LabelColor); // Tactics
|
||||
AddHtml(320, 186, 35, 18, FormatSkill(c, SkillName.Tactics));
|
||||
|
||||
AddHtmlLocalized(153, 204, 160, 18, 1044086, LabelColor); // Magic Resistance
|
||||
AddHtml(320, 204, 35, 18, FormatSkill(c, SkillName.MagicResist));
|
||||
|
||||
AddHtmlLocalized(153, 222, 160, 18, 1044061, LabelColor); // Anatomy
|
||||
AddHtml(320, 222, 35, 18, FormatSkill(c, SkillName.Anatomy));
|
||||
|
||||
if (c is CuSidhe)
|
||||
{
|
||||
AddHtmlLocalized(153, 240, 160, 18, 1044077, LabelColor); // Healing
|
||||
AddHtml(320, 240, 35, 18, FormatSkill(c, SkillName.Healing));
|
||||
}
|
||||
else
|
||||
{
|
||||
AddHtmlLocalized(153, 240, 160, 18, 1044090, LabelColor); // Poisoning
|
||||
AddHtml(320, 240, 35, 18, FormatSkill(c, SkillName.Poisoning));
|
||||
}
|
||||
|
||||
AddImage(128, 260, 2086);
|
||||
AddHtmlLocalized(147, 258, 160, 18, 3001032, 200); // Lore & Knowledge
|
||||
|
||||
AddHtmlLocalized(153, 276, 160, 18, 1044085, LabelColor); // Magery
|
||||
AddHtml(320, 276, 35, 18, FormatSkill(c, SkillName.Magery));
|
||||
|
||||
AddHtmlLocalized(153, 294, 160, 18, 1044076, LabelColor); // Evaluating Intelligence
|
||||
AddHtml(320, 294, 35, 18, FormatSkill(c, SkillName.EvalInt));
|
||||
|
||||
AddHtmlLocalized(153, 312, 160, 18, 1044106, LabelColor); // Meditation
|
||||
AddHtml(320, 312, 35, 18, FormatSkill(c, SkillName.Meditation));
|
||||
|
||||
AddButton(340, 358, 5601, 5605, 0, GumpButtonType.Page, page + 1);
|
||||
AddButton(317, 358, 5603, 5607, 0, GumpButtonType.Page, page - 1);
|
||||
|
||||
AddPage(++page);
|
||||
|
||||
AddImage(128, 152, 2086);
|
||||
AddHtmlLocalized(147, 150, 160, 18, 1049563, 200); // Preferred Foods
|
||||
|
||||
var foodPref = 3000340;
|
||||
|
||||
if ((c.FavoriteFood & FoodType.FruitsAndVegies) != 0)
|
||||
foodPref = 1049565; // Fruits and Vegetables
|
||||
else if ((c.FavoriteFood & FoodType.GrainsAndHay) != 0)
|
||||
foodPref = 1049566; // Grains and Hay
|
||||
else if ((c.FavoriteFood & FoodType.Fish) != 0)
|
||||
foodPref = 1049568; // Fish
|
||||
else if ((c.FavoriteFood & FoodType.Meat) != 0)
|
||||
foodPref = 1049564; // Meat
|
||||
else if ((c.FavoriteFood & FoodType.Eggs) != 0)
|
||||
foodPref = 1044477; // Eggs
|
||||
|
||||
AddHtmlLocalized(153, 168, 160, 18, foodPref, LabelColor);
|
||||
|
||||
AddImage(128, 188, 2086);
|
||||
AddHtmlLocalized(147, 186, 160, 18, 1049569, 200); // Pack Instincts
|
||||
|
||||
var packInstinct = 3000340;
|
||||
|
||||
if ((c.PackInstinct & PackInstinct.Canine) != 0)
|
||||
packInstinct = 1049570; // Canine
|
||||
else if ((c.PackInstinct & PackInstinct.Ostard) != 0)
|
||||
packInstinct = 1049571; // Ostard
|
||||
else if ((c.PackInstinct & PackInstinct.Feline) != 0)
|
||||
packInstinct = 1049572; // Feline
|
||||
else if ((c.PackInstinct & PackInstinct.Arachnid) != 0)
|
||||
packInstinct = 1049573; // Arachnid
|
||||
else if ((c.PackInstinct & PackInstinct.Daemon) != 0)
|
||||
packInstinct = 1049574; // Daemon
|
||||
else if ((c.PackInstinct & PackInstinct.Bear) != 0)
|
||||
packInstinct = 1049575; // Bear
|
||||
else if ((c.PackInstinct & PackInstinct.Equine) != 0)
|
||||
packInstinct = 1049576; // Equine
|
||||
else if ((c.PackInstinct & PackInstinct.Bull) != 0)
|
||||
packInstinct = 1049577; // Bull
|
||||
|
||||
AddHtmlLocalized(153, 204, 160, 18, packInstinct, LabelColor);
|
||||
|
||||
if (!Core.AOS)
|
||||
{
|
||||
AddImage(128, 224, 2086);
|
||||
AddHtmlLocalized(147, 222, 160, 18, 1049594, 200); // Loyalty Rating
|
||||
|
||||
AddHtmlLocalized(
|
||||
153,
|
||||
240,
|
||||
160,
|
||||
18,
|
||||
!c.Controlled || c.Loyalty == 0 ? 1061643 : 1049595 + c.Loyalty / 10,
|
||||
LabelColor
|
||||
);
|
||||
}
|
||||
|
||||
AddButton(340, 358, 5601, 5605, 0, GumpButtonType.Page, 1);
|
||||
AddButton(317, 358, 5603, 5607, 0, GumpButtonType.Page, page - 1);
|
||||
}
|
||||
else
|
||||
|
||||
private static string FormatSkill(BaseCreature c, SkillName name)
|
||||
{
|
||||
from.SendLocalizedMessage(500329); // That's not an animal!
|
||||
var skill = c.Skills[name];
|
||||
|
||||
if (skill.Base < 10.0)
|
||||
return "<div align=right>---</div>";
|
||||
|
||||
return $"<div align=right>{skill.Value:F1}</div>";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class AnimalLoreGump : Gump
|
||||
{
|
||||
private const int LabelColor = 0x24E5;
|
||||
|
||||
public AnimalLoreGump(BaseCreature c) : base(250, 50)
|
||||
{
|
||||
AddPage(0);
|
||||
|
||||
AddImage(100, 100, 2080);
|
||||
AddImage(118, 137, 2081);
|
||||
AddImage(118, 207, 2081);
|
||||
AddImage(118, 277, 2081);
|
||||
AddImage(118, 347, 2083);
|
||||
|
||||
AddHtml(147, 108, 210, 18, $"<center><i>{c.Name}</i></center>");
|
||||
|
||||
AddButton(240, 77, 2093, 2093, 2);
|
||||
|
||||
AddImage(140, 138, 2091);
|
||||
AddImage(140, 335, 2091);
|
||||
|
||||
int pages = Core.AOS ? 5 : 3;
|
||||
int page = 0;
|
||||
|
||||
AddPage(++page);
|
||||
|
||||
AddImage(128, 152, 2086);
|
||||
AddHtmlLocalized(147, 150, 160, 18, 1049593, 200); // Attributes
|
||||
|
||||
AddHtmlLocalized(153, 168, 160, 18, 1049578, LabelColor); // Hits
|
||||
AddHtml(280, 168, 75, 18, FormatAttributes(c.Hits, c.HitsMax));
|
||||
|
||||
AddHtmlLocalized(153, 186, 160, 18, 1049579, LabelColor); // Stamina
|
||||
AddHtml(280, 186, 75, 18, FormatAttributes(c.Stam, c.StamMax));
|
||||
|
||||
AddHtmlLocalized(153, 204, 160, 18, 1049580, LabelColor); // Mana
|
||||
AddHtml(280, 204, 75, 18, FormatAttributes(c.Mana, c.ManaMax));
|
||||
|
||||
AddHtmlLocalized(153, 222, 160, 18, 1028335, LabelColor); // Strength
|
||||
AddHtml(320, 222, 35, 18, FormatStat(c.Str));
|
||||
|
||||
AddHtmlLocalized(153, 240, 160, 18, 3000113, LabelColor); // Dexterity
|
||||
AddHtml(320, 240, 35, 18, FormatStat(c.Dex));
|
||||
|
||||
AddHtmlLocalized(153, 258, 160, 18, 3000112, LabelColor); // Intelligence
|
||||
AddHtml(320, 258, 35, 18, FormatStat(c.Int));
|
||||
|
||||
if (Core.AOS)
|
||||
{
|
||||
int y = 276;
|
||||
|
||||
if (Core.SE)
|
||||
private static string FormatAttributes(int cur, int max)
|
||||
{
|
||||
double bd = BaseInstrument.GetBaseDifficulty(c);
|
||||
if (c.Uncalmable)
|
||||
bd = 0;
|
||||
if (max == 0)
|
||||
return "<div align=right>---</div>";
|
||||
|
||||
AddHtmlLocalized(153, 276, 160, 18, 1070793, LabelColor); // Barding Difficulty
|
||||
AddHtml(320, y, 35, 18, FormatDouble(bd));
|
||||
|
||||
y += 18;
|
||||
return $"<div align=right>{cur}/{max}</div>";
|
||||
}
|
||||
|
||||
AddImage(128, y + 2, 2086);
|
||||
AddHtmlLocalized(147, y, 160, 18, 1049594, 200); // Loyalty Rating
|
||||
y += 18;
|
||||
|
||||
AddHtmlLocalized(153, y, 160, 18, !c.Controlled || c.Loyalty == 0 ? 1061643 : 1049595 + c.Loyalty / 10,
|
||||
LabelColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddImage(128, 278, 2086);
|
||||
AddHtmlLocalized(147, 276, 160, 18, 3001016, 200); // Miscellaneous
|
||||
|
||||
AddHtmlLocalized(153, 294, 160, 18, 1049581, LabelColor); // Armor Rating
|
||||
AddHtml(320, 294, 35, 18, FormatStat(c.VirtualArmor));
|
||||
}
|
||||
|
||||
AddButton(340, 358, 5601, 5605, 0, GumpButtonType.Page, page + 1);
|
||||
AddButton(317, 358, 5603, 5607, 0, GumpButtonType.Page, pages);
|
||||
|
||||
if (Core.AOS)
|
||||
{
|
||||
AddPage(++page);
|
||||
|
||||
AddImage(128, 152, 2086);
|
||||
AddHtmlLocalized(147, 150, 160, 18, 1061645, 200); // Resistances
|
||||
|
||||
AddHtmlLocalized(153, 168, 160, 18, 1061646, LabelColor); // Physical
|
||||
AddHtml(320, 168, 35, 18, FormatElement(c.PhysicalResistance));
|
||||
|
||||
AddHtmlLocalized(153, 186, 160, 18, 1061647, LabelColor); // Fire
|
||||
AddHtml(320, 186, 35, 18, FormatElement(c.FireResistance));
|
||||
|
||||
AddHtmlLocalized(153, 204, 160, 18, 1061648, LabelColor); // Cold
|
||||
AddHtml(320, 204, 35, 18, FormatElement(c.ColdResistance));
|
||||
|
||||
AddHtmlLocalized(153, 222, 160, 18, 1061649, LabelColor); // Poison
|
||||
AddHtml(320, 222, 35, 18, FormatElement(c.PoisonResistance));
|
||||
|
||||
AddHtmlLocalized(153, 240, 160, 18, 1061650, LabelColor); // Energy
|
||||
AddHtml(320, 240, 35, 18, FormatElement(c.EnergyResistance));
|
||||
|
||||
AddButton(340, 358, 5601, 5605, 0, GumpButtonType.Page, page + 1);
|
||||
AddButton(317, 358, 5603, 5607, 0, GumpButtonType.Page, page - 1);
|
||||
}
|
||||
|
||||
if (Core.AOS)
|
||||
{
|
||||
AddPage(++page);
|
||||
|
||||
AddImage(128, 152, 2086);
|
||||
AddHtmlLocalized(147, 150, 160, 18, 1017319, 200); // Damage
|
||||
|
||||
AddHtmlLocalized(153, 168, 160, 18, 1061646, LabelColor); // Physical
|
||||
AddHtml(320, 168, 35, 18, FormatElement(c.PhysicalDamage));
|
||||
|
||||
AddHtmlLocalized(153, 186, 160, 18, 1061647, LabelColor); // Fire
|
||||
AddHtml(320, 186, 35, 18, FormatElement(c.FireDamage));
|
||||
|
||||
AddHtmlLocalized(153, 204, 160, 18, 1061648, LabelColor); // Cold
|
||||
AddHtml(320, 204, 35, 18, FormatElement(c.ColdDamage));
|
||||
|
||||
AddHtmlLocalized(153, 222, 160, 18, 1061649, LabelColor); // Poison
|
||||
AddHtml(320, 222, 35, 18, FormatElement(c.PoisonDamage));
|
||||
|
||||
AddHtmlLocalized(153, 240, 160, 18, 1061650, LabelColor); // Energy
|
||||
AddHtml(320, 240, 35, 18, FormatElement(c.EnergyDamage));
|
||||
|
||||
if (Core.ML)
|
||||
private static string FormatStat(int val)
|
||||
{
|
||||
AddHtmlLocalized(153, 258, 160, 18, 1076750, LabelColor); // Base Damage
|
||||
AddHtml(300, 258, 55, 18, FormatDamage(c.DamageMin, c.DamageMax));
|
||||
if (val == 0)
|
||||
return "<div align=right>---</div>";
|
||||
|
||||
return $"<div align=right>{val}</div>";
|
||||
}
|
||||
|
||||
AddButton(340, 358, 5601, 5605, 0, GumpButtonType.Page, page + 1);
|
||||
AddButton(317, 358, 5603, 5607, 0, GumpButtonType.Page, page - 1);
|
||||
}
|
||||
private static string FormatDouble(double val)
|
||||
{
|
||||
if (val == 0)
|
||||
return "<div align=right>---</div>";
|
||||
|
||||
AddPage(++page);
|
||||
return $"<div align=right>{val:F1}</div>";
|
||||
}
|
||||
|
||||
AddImage(128, 152, 2086);
|
||||
AddHtmlLocalized(147, 150, 160, 18, 3001030, 200); // Combat Ratings
|
||||
private static string FormatElement(int val)
|
||||
{
|
||||
if (val <= 0)
|
||||
return "<div align=right>---</div>";
|
||||
|
||||
AddHtmlLocalized(153, 168, 160, 18, 1044103, LabelColor); // Wrestling
|
||||
AddHtml(320, 168, 35, 18, FormatSkill(c, SkillName.Wrestling));
|
||||
return $"<div align=right>{val}%</div>";
|
||||
}
|
||||
|
||||
AddHtmlLocalized(153, 186, 160, 18, 1044087, LabelColor); // Tactics
|
||||
AddHtml(320, 186, 35, 18, FormatSkill(c, SkillName.Tactics));
|
||||
private static string FormatDamage(int min, int max)
|
||||
{
|
||||
if (min <= 0 || max <= 0)
|
||||
return "<div align=right>---</div>";
|
||||
|
||||
AddHtmlLocalized(153, 204, 160, 18, 1044086, LabelColor); // Magic Resistance
|
||||
AddHtml(320, 204, 35, 18, FormatSkill(c, SkillName.MagicResist));
|
||||
|
||||
AddHtmlLocalized(153, 222, 160, 18, 1044061, LabelColor); // Anatomy
|
||||
AddHtml(320, 222, 35, 18, FormatSkill(c, SkillName.Anatomy));
|
||||
|
||||
if (c is CuSidhe)
|
||||
{
|
||||
AddHtmlLocalized(153, 240, 160, 18, 1044077, LabelColor); // Healing
|
||||
AddHtml(320, 240, 35, 18, FormatSkill(c, SkillName.Healing));
|
||||
}
|
||||
else
|
||||
{
|
||||
AddHtmlLocalized(153, 240, 160, 18, 1044090, LabelColor); // Poisoning
|
||||
AddHtml(320, 240, 35, 18, FormatSkill(c, SkillName.Poisoning));
|
||||
}
|
||||
|
||||
AddImage(128, 260, 2086);
|
||||
AddHtmlLocalized(147, 258, 160, 18, 3001032, 200); // Lore & Knowledge
|
||||
|
||||
AddHtmlLocalized(153, 276, 160, 18, 1044085, LabelColor); // Magery
|
||||
AddHtml(320, 276, 35, 18, FormatSkill(c, SkillName.Magery));
|
||||
|
||||
AddHtmlLocalized(153, 294, 160, 18, 1044076, LabelColor); // Evaluating Intelligence
|
||||
AddHtml(320, 294, 35, 18, FormatSkill(c, SkillName.EvalInt));
|
||||
|
||||
AddHtmlLocalized(153, 312, 160, 18, 1044106, LabelColor); // Meditation
|
||||
AddHtml(320, 312, 35, 18, FormatSkill(c, SkillName.Meditation));
|
||||
|
||||
AddButton(340, 358, 5601, 5605, 0, GumpButtonType.Page, page + 1);
|
||||
AddButton(317, 358, 5603, 5607, 0, GumpButtonType.Page, page - 1);
|
||||
|
||||
AddPage(++page);
|
||||
|
||||
AddImage(128, 152, 2086);
|
||||
AddHtmlLocalized(147, 150, 160, 18, 1049563, 200); // Preferred Foods
|
||||
|
||||
int foodPref = 3000340;
|
||||
|
||||
if ((c.FavoriteFood & FoodType.FruitsAndVegies) != 0)
|
||||
foodPref = 1049565; // Fruits and Vegetables
|
||||
else if ((c.FavoriteFood & FoodType.GrainsAndHay) != 0)
|
||||
foodPref = 1049566; // Grains and Hay
|
||||
else if ((c.FavoriteFood & FoodType.Fish) != 0)
|
||||
foodPref = 1049568; // Fish
|
||||
else if ((c.FavoriteFood & FoodType.Meat) != 0)
|
||||
foodPref = 1049564; // Meat
|
||||
else if ((c.FavoriteFood & FoodType.Eggs) != 0)
|
||||
foodPref = 1044477; // Eggs
|
||||
|
||||
AddHtmlLocalized(153, 168, 160, 18, foodPref, LabelColor);
|
||||
|
||||
AddImage(128, 188, 2086);
|
||||
AddHtmlLocalized(147, 186, 160, 18, 1049569, 200); // Pack Instincts
|
||||
|
||||
int packInstinct = 3000340;
|
||||
|
||||
if ((c.PackInstinct & PackInstinct.Canine) != 0)
|
||||
packInstinct = 1049570; // Canine
|
||||
else if ((c.PackInstinct & PackInstinct.Ostard) != 0)
|
||||
packInstinct = 1049571; // Ostard
|
||||
else if ((c.PackInstinct & PackInstinct.Feline) != 0)
|
||||
packInstinct = 1049572; // Feline
|
||||
else if ((c.PackInstinct & PackInstinct.Arachnid) != 0)
|
||||
packInstinct = 1049573; // Arachnid
|
||||
else if ((c.PackInstinct & PackInstinct.Daemon) != 0)
|
||||
packInstinct = 1049574; // Daemon
|
||||
else if ((c.PackInstinct & PackInstinct.Bear) != 0)
|
||||
packInstinct = 1049575; // Bear
|
||||
else if ((c.PackInstinct & PackInstinct.Equine) != 0)
|
||||
packInstinct = 1049576; // Equine
|
||||
else if ((c.PackInstinct & PackInstinct.Bull) != 0)
|
||||
packInstinct = 1049577; // Bull
|
||||
|
||||
AddHtmlLocalized(153, 204, 160, 18, packInstinct, LabelColor);
|
||||
|
||||
if (!Core.AOS)
|
||||
{
|
||||
AddImage(128, 224, 2086);
|
||||
AddHtmlLocalized(147, 222, 160, 18, 1049594, 200); // Loyalty Rating
|
||||
|
||||
AddHtmlLocalized(153, 240, 160, 18, !c.Controlled || c.Loyalty == 0 ? 1061643 : 1049595 + c.Loyalty / 10,
|
||||
LabelColor);
|
||||
}
|
||||
|
||||
AddButton(340, 358, 5601, 5605, 0, GumpButtonType.Page, 1);
|
||||
AddButton(317, 358, 5603, 5607, 0, GumpButtonType.Page, page - 1);
|
||||
return $"<div align=right>{min}-{max}</div>";
|
||||
}
|
||||
}
|
||||
|
||||
private static string FormatSkill(BaseCreature c, SkillName name)
|
||||
{
|
||||
Skill skill = c.Skills[name];
|
||||
|
||||
if (skill.Base < 10.0)
|
||||
return "<div align=right>---</div>";
|
||||
|
||||
return $"<div align=right>{skill.Value:F1}</div>";
|
||||
}
|
||||
|
||||
private static string FormatAttributes(int cur, int max)
|
||||
{
|
||||
if (max == 0)
|
||||
return "<div align=right>---</div>";
|
||||
|
||||
return $"<div align=right>{cur}/{max}</div>";
|
||||
}
|
||||
|
||||
private static string FormatStat(int val)
|
||||
{
|
||||
if (val == 0)
|
||||
return "<div align=right>---</div>";
|
||||
|
||||
return $"<div align=right>{val}</div>";
|
||||
}
|
||||
|
||||
private static string FormatDouble(double val)
|
||||
{
|
||||
if (val == 0)
|
||||
return "<div align=right>---</div>";
|
||||
|
||||
return $"<div align=right>{val:F1}</div>";
|
||||
}
|
||||
|
||||
private static string FormatElement(int val)
|
||||
{
|
||||
if (val <= 0)
|
||||
return "<div align=right>---</div>";
|
||||
|
||||
return $"<div align=right>{val}%</div>";
|
||||
}
|
||||
|
||||
private static string FormatDamage(int min, int max)
|
||||
{
|
||||
if (min <= 0 || max <= 0)
|
||||
return "<div align=right>---</div>";
|
||||
|
||||
return $"<div align=right>{min}-{max}</div>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,414 +10,513 @@ using Server.Targeting;
|
|||
|
||||
namespace Server.SkillHandlers
|
||||
{
|
||||
public static class AnimalTaming
|
||||
{
|
||||
private static readonly HashSet<Mobile> m_BeingTamed = new HashSet<Mobile>();
|
||||
|
||||
public static bool DisableMessage { get; set; }
|
||||
|
||||
public static void Initialize()
|
||||
public static class AnimalTaming
|
||||
{
|
||||
SkillInfo.Table[(int)SkillName.AnimalTaming].Callback = OnUse;
|
||||
}
|
||||
private static readonly HashSet<Mobile> m_BeingTamed = new HashSet<Mobile>();
|
||||
|
||||
public static TimeSpan OnUse(Mobile m)
|
||||
{
|
||||
m.RevealingAction();
|
||||
public static bool DisableMessage { get; set; }
|
||||
|
||||
m.Target = new InternalTarget();
|
||||
m.RevealingAction();
|
||||
|
||||
if (!DisableMessage)
|
||||
m.SendLocalizedMessage(502789); // Tame which animal?
|
||||
|
||||
return TimeSpan.FromHours(6.0);
|
||||
}
|
||||
|
||||
public static bool CheckMastery(Mobile tamer, BaseCreature creature) =>
|
||||
SummonFamiliarSpell.Table.TryGetValue(tamer, out BaseCreature bc) && bc is DarkWolfFamiliar familiar &&
|
||||
!familiar.Deleted && (creature is DireWolf || creature is GreyWolf || creature is TimberWolf ||
|
||||
creature is WhiteWolf ||
|
||||
creature is BakeKitsune);
|
||||
|
||||
public static bool MustBeSubdued(BaseCreature bc) => bc.Owners.Count <= 0 && bc.SubdueBeforeTame && bc.Hits > bc.HitsMax / 10;
|
||||
|
||||
public static void ScaleStats(BaseCreature bc, double scalar)
|
||||
{
|
||||
if (bc.RawStr > 0)
|
||||
bc.RawStr = (int)Math.Max(1, bc.RawStr * scalar);
|
||||
|
||||
if (bc.RawDex > 0)
|
||||
bc.RawDex = (int)Math.Max(1, bc.RawDex * scalar);
|
||||
|
||||
if (bc.RawInt > 0)
|
||||
bc.RawInt = (int)Math.Max(1, bc.RawInt * scalar);
|
||||
|
||||
if (bc.HitsMaxSeed > 0)
|
||||
{
|
||||
bc.HitsMaxSeed = (int)Math.Max(1, bc.HitsMaxSeed * scalar);
|
||||
bc.Hits = bc.Hits;
|
||||
}
|
||||
|
||||
if (bc.StamMaxSeed > 0)
|
||||
{
|
||||
bc.StamMaxSeed = (int)Math.Max(1, bc.StamMaxSeed * scalar);
|
||||
bc.Stam = bc.Stam;
|
||||
}
|
||||
}
|
||||
|
||||
public static void ScaleSkills(BaseCreature bc, double scalar)
|
||||
{
|
||||
ScaleSkills(bc, scalar, scalar);
|
||||
}
|
||||
|
||||
public static void ScaleSkills(BaseCreature bc, double scalar, double capScalar)
|
||||
{
|
||||
for (int i = 0; i < bc.Skills.Length; ++i)
|
||||
{
|
||||
bc.Skills[i].Base *= scalar;
|
||||
|
||||
bc.Skills[i].Cap = Math.Max(100.0, bc.Skills[i].Cap * capScalar);
|
||||
|
||||
if (bc.Skills[i].Base > bc.Skills[i].Cap) bc.Skills[i].Cap = bc.Skills[i].Base;
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private bool m_SetSkillTime = true;
|
||||
|
||||
public InternalTarget() : base(Core.AOS ? 3 : 2, false, TargetFlags.None)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish(Mobile from)
|
||||
{
|
||||
if (m_SetSkillTime)
|
||||
from.NextSkillTime = Core.TickCount;
|
||||
}
|
||||
|
||||
protected override void OnTarget(Mobile from, object targeted)
|
||||
{
|
||||
from.RevealingAction();
|
||||
|
||||
if (!(targeted is Mobile mobile))
|
||||
public static void Initialize()
|
||||
{
|
||||
from.SendLocalizedMessage(502801); // You can't tame that!
|
||||
return;
|
||||
SkillInfo.Table[(int)SkillName.AnimalTaming].Callback = OnUse;
|
||||
}
|
||||
|
||||
if (!(mobile is BaseCreature creature))
|
||||
public static TimeSpan OnUse(Mobile m)
|
||||
{
|
||||
mobile.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 502469,
|
||||
from.NetState); // That being cannot be tamed.
|
||||
return;
|
||||
m.RevealingAction();
|
||||
|
||||
m.Target = new InternalTarget();
|
||||
m.RevealingAction();
|
||||
|
||||
if (!DisableMessage)
|
||||
m.SendLocalizedMessage(502789); // Tame which animal?
|
||||
|
||||
return TimeSpan.FromHours(6.0);
|
||||
}
|
||||
|
||||
if (!creature.Tamable)
|
||||
public static bool CheckMastery(Mobile tamer, BaseCreature creature) =>
|
||||
SummonFamiliarSpell.Table.TryGetValue(tamer, out var bc) && bc is DarkWolfFamiliar familiar &&
|
||||
!familiar.Deleted && (creature is DireWolf || creature is GreyWolf || creature is TimberWolf ||
|
||||
creature is WhiteWolf ||
|
||||
creature is BakeKitsune);
|
||||
|
||||
public static bool MustBeSubdued(BaseCreature bc) =>
|
||||
bc.Owners.Count <= 0 && bc.SubdueBeforeTame && bc.Hits > bc.HitsMax / 10;
|
||||
|
||||
public static void ScaleStats(BaseCreature bc, double scalar)
|
||||
{
|
||||
creature.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 1049655,
|
||||
from.NetState); // That creature cannot be tamed.
|
||||
return;
|
||||
}
|
||||
if (bc.RawStr > 0)
|
||||
bc.RawStr = (int)Math.Max(1, bc.RawStr * scalar);
|
||||
|
||||
if (creature.Controlled)
|
||||
{
|
||||
creature.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 502804,
|
||||
from.NetState); // That animal looks tame already.
|
||||
return;
|
||||
}
|
||||
if (bc.RawDex > 0)
|
||||
bc.RawDex = (int)Math.Max(1, bc.RawDex * scalar);
|
||||
|
||||
if (from.Female && !creature.AllowFemaleTamer)
|
||||
{
|
||||
creature.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 1049653,
|
||||
from.NetState); // That creature can only be tamed by males.
|
||||
return;
|
||||
}
|
||||
if (bc.RawInt > 0)
|
||||
bc.RawInt = (int)Math.Max(1, bc.RawInt * scalar);
|
||||
|
||||
if (!from.Female && !creature.AllowMaleTamer)
|
||||
{
|
||||
creature.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 1049652,
|
||||
from.NetState); // That creature can only be tamed by females.
|
||||
return;
|
||||
}
|
||||
|
||||
if (creature is CuSidhe && from.Race != Race.Elf)
|
||||
{
|
||||
creature.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 502801,
|
||||
from.NetState); // You can't tame that!
|
||||
return;
|
||||
}
|
||||
|
||||
if (from.Followers + creature.ControlSlots > from.FollowersMax)
|
||||
{
|
||||
from.SendLocalizedMessage(1049611); // You have too many followers to tame that creature.
|
||||
return;
|
||||
}
|
||||
if (creature.Owners.Count >= BaseCreature.MaxOwners && !creature.Owners.Contains(from))
|
||||
{
|
||||
creature.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 1005615,
|
||||
from.NetState); // This animal has had too many owners and is too upset for you to tame.
|
||||
return;
|
||||
}
|
||||
if (MustBeSubdued(creature))
|
||||
{
|
||||
creature.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 1054025,
|
||||
from.NetState); // You must subdue this creature before you can tame it!
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(CheckMastery(from, creature) || from.Skills.AnimalTaming.Value >= creature.MinTameSkill))
|
||||
{
|
||||
creature.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 502806,
|
||||
from.NetState); // You have no chance of taming this creature.
|
||||
return;
|
||||
}
|
||||
|
||||
if (creature is FactionWarHorse warHorse)
|
||||
{
|
||||
Faction faction = Faction.Find(from);
|
||||
|
||||
if (faction == null || faction != warHorse.Faction)
|
||||
{
|
||||
creature.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 1042590,
|
||||
from.NetState); // You cannot tame this creature.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_BeingTamed.Contains(creature))
|
||||
{
|
||||
creature.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 502802,
|
||||
from.NetState); // Someone else is already taming this.
|
||||
}
|
||||
else if (creature.CanAngerOnTame && Utility.RandomDouble() <= 0.95)
|
||||
{
|
||||
creature.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 502805,
|
||||
from.NetState); // You seem to anger the beast!
|
||||
creature.PlaySound(creature.GetAngerSound());
|
||||
creature.Direction = creature.GetDirectionTo(from);
|
||||
|
||||
if (creature.BardPacified && Utility.RandomDouble() > .24)
|
||||
Timer.DelayCall(TimeSpan.FromSeconds(2.0), Pacify, creature);
|
||||
else
|
||||
creature.BardEndTime = DateTime.UtcNow;
|
||||
|
||||
creature.BardPacified = false;
|
||||
|
||||
creature.AIObject?.DoMove(creature.Direction);
|
||||
|
||||
if (from is PlayerMobile pm &&
|
||||
!(pm.HonorActive ||
|
||||
TransformationSpellHelper.UnderTransformation(pm, typeof(EtherealVoyageSpell))))
|
||||
creature.Combatant = pm;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_BeingTamed.Add(creature);
|
||||
|
||||
from.LocalOverheadMessage(MessageType.Emote, 0x59,
|
||||
1010597); // You start to tame the creature.
|
||||
from.NonlocalOverheadMessage(MessageType.Emote, 0x59,
|
||||
1010598); // *begins taming a creature.*
|
||||
|
||||
new InternalTimer(from, creature, Utility.Random(3, 2)).Start();
|
||||
|
||||
m_SetSkillTime = false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void Pacify(BaseCreature bc) => bc.BardPacified = true; // Should use bc.Pacify with an end time?
|
||||
|
||||
private class InternalTimer : Timer
|
||||
{
|
||||
private int m_Count;
|
||||
private readonly BaseCreature m_Creature;
|
||||
private readonly int m_MaxCount;
|
||||
private bool m_Paralyzed;
|
||||
private readonly DateTime m_StartTime;
|
||||
private readonly Mobile m_Tamer;
|
||||
|
||||
public InternalTimer(Mobile tamer, BaseCreature creature, int count) : base(TimeSpan.FromSeconds(3.0),
|
||||
TimeSpan.FromSeconds(3.0), count)
|
||||
{
|
||||
m_Tamer = tamer;
|
||||
m_Creature = creature;
|
||||
m_MaxCount = count;
|
||||
m_Paralyzed = creature.Paralyzed;
|
||||
m_StartTime = DateTime.UtcNow;
|
||||
Priority = TimerPriority.TwoFiftyMS;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
m_Count++;
|
||||
|
||||
DamageEntry de = m_Creature.FindMostRecentDamageEntry(false);
|
||||
bool alreadyOwned = m_Creature.Owners.Contains(m_Tamer);
|
||||
|
||||
if (!m_Tamer.InRange(m_Creature, Core.AOS ? 7 : 6))
|
||||
{
|
||||
m_BeingTamed.Remove(m_Creature);
|
||||
m_Tamer.NextSkillTime = Core.TickCount;
|
||||
m_Creature.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 502795,
|
||||
m_Tamer.NetState); // You are too far away to continue taming.
|
||||
Stop();
|
||||
}
|
||||
else if (!m_Tamer.CheckAlive())
|
||||
{
|
||||
m_BeingTamed.Remove(m_Creature);
|
||||
m_Tamer.NextSkillTime = Core.TickCount;
|
||||
m_Creature.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 502796,
|
||||
m_Tamer.NetState); // You are dead, and cannot continue taming.
|
||||
Stop();
|
||||
}
|
||||
else if (!m_Tamer.CanSee(m_Creature) || !m_Tamer.InLOS(m_Creature) || !CanPath())
|
||||
{
|
||||
m_BeingTamed.Remove(m_Creature);
|
||||
m_Tamer.NextSkillTime = Core.TickCount;
|
||||
m_Tamer.SendLocalizedMessage(
|
||||
1049654); // You do not have a clear path to the animal you are taming, and must cease your attempt.
|
||||
Stop();
|
||||
}
|
||||
else if (!m_Creature.Tamable)
|
||||
{
|
||||
m_BeingTamed.Remove(m_Creature);
|
||||
m_Tamer.NextSkillTime = Core.TickCount;
|
||||
m_Creature.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 1049655,
|
||||
m_Tamer.NetState); // That creature cannot be tamed.
|
||||
Stop();
|
||||
}
|
||||
else if (m_Creature.Controlled)
|
||||
{
|
||||
m_BeingTamed.Remove(m_Creature);
|
||||
m_Tamer.NextSkillTime = Core.TickCount;
|
||||
m_Creature.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 502804,
|
||||
m_Tamer.NetState); // That animal looks tame already.
|
||||
Stop();
|
||||
}
|
||||
else if (m_Creature.Owners.Count >= BaseCreature.MaxOwners && !m_Creature.Owners.Contains(m_Tamer))
|
||||
{
|
||||
m_BeingTamed.Remove(m_Creature);
|
||||
m_Tamer.NextSkillTime = Core.TickCount;
|
||||
m_Creature.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 1005615,
|
||||
m_Tamer.NetState); // This animal has had too many owners and is too upset for you to tame.
|
||||
Stop();
|
||||
}
|
||||
else if (MustBeSubdued(m_Creature))
|
||||
{
|
||||
m_BeingTamed.Remove(m_Creature);
|
||||
m_Tamer.NextSkillTime = Core.TickCount;
|
||||
m_Creature.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 1054025,
|
||||
m_Tamer.NetState); // You must subdue this creature before you can tame it!
|
||||
Stop();
|
||||
}
|
||||
else if (de?.LastDamage > m_StartTime)
|
||||
{
|
||||
m_BeingTamed.Remove(m_Creature);
|
||||
m_Tamer.NextSkillTime = Core.TickCount;
|
||||
m_Creature.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 502794,
|
||||
m_Tamer.NetState); // The animal is too angry to continue taming.
|
||||
Stop();
|
||||
}
|
||||
else if (m_Count < m_MaxCount)
|
||||
{
|
||||
m_Tamer.RevealingAction();
|
||||
|
||||
switch (Utility.Random(3))
|
||||
if (bc.HitsMaxSeed > 0)
|
||||
{
|
||||
case 0:
|
||||
m_Tamer.PublicOverheadMessage(MessageType.Regular, 0x3B2, Utility.Random(502790, 4));
|
||||
break;
|
||||
case 1:
|
||||
m_Tamer.PublicOverheadMessage(MessageType.Regular, 0x3B2, Utility.Random(1005608, 6));
|
||||
break;
|
||||
case 2:
|
||||
m_Tamer.PublicOverheadMessage(MessageType.Regular, 0x3B2, Utility.Random(1010593, 4));
|
||||
break;
|
||||
bc.HitsMaxSeed = (int)Math.Max(1, bc.HitsMaxSeed * scalar);
|
||||
bc.Hits = bc.Hits;
|
||||
}
|
||||
|
||||
if (!alreadyOwned) // Passively check animal lore for gain
|
||||
m_Tamer.CheckTargetSkill(SkillName.AnimalLore, m_Creature, 0.0, 120.0);
|
||||
|
||||
if (m_Creature.Paralyzed)
|
||||
m_Paralyzed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Tamer.RevealingAction();
|
||||
m_Tamer.NextSkillTime = Core.TickCount;
|
||||
m_BeingTamed.Remove(m_Creature);
|
||||
|
||||
if (m_Creature.Paralyzed)
|
||||
m_Paralyzed = true;
|
||||
|
||||
if (!alreadyOwned) // Passively check animal lore for gain
|
||||
m_Tamer.CheckTargetSkill(SkillName.AnimalLore, m_Creature, 0.0, 120.0);
|
||||
|
||||
double minSkill = m_Creature.MinTameSkill + m_Creature.Owners.Count * 6.0;
|
||||
|
||||
if (minSkill > -24.9 && CheckMastery(m_Tamer, m_Creature))
|
||||
minSkill = -24.9; // 50% at 0.0?
|
||||
|
||||
minSkill += 24.9;
|
||||
|
||||
if (CheckMastery(m_Tamer, m_Creature) || alreadyOwned ||
|
||||
m_Tamer.CheckTargetSkill(SkillName.AnimalTaming, m_Creature, minSkill - 25.0, minSkill + 25.0))
|
||||
if (bc.StamMaxSeed > 0)
|
||||
{
|
||||
if (m_Creature.Owners.Count == 0) // First tame
|
||||
{
|
||||
if (m_Creature is GreaterDragon)
|
||||
bc.StamMaxSeed = (int)Math.Max(1, bc.StamMaxSeed * scalar);
|
||||
bc.Stam = bc.Stam;
|
||||
}
|
||||
}
|
||||
|
||||
public static void ScaleSkills(BaseCreature bc, double scalar)
|
||||
{
|
||||
ScaleSkills(bc, scalar, scalar);
|
||||
}
|
||||
|
||||
public static void ScaleSkills(BaseCreature bc, double scalar, double capScalar)
|
||||
{
|
||||
for (var i = 0; i < bc.Skills.Length; ++i)
|
||||
{
|
||||
bc.Skills[i].Base *= scalar;
|
||||
|
||||
bc.Skills[i].Cap = Math.Max(100.0, bc.Skills[i].Cap * capScalar);
|
||||
|
||||
if (bc.Skills[i].Base > bc.Skills[i].Cap) bc.Skills[i].Cap = bc.Skills[i].Base;
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private bool m_SetSkillTime = true;
|
||||
|
||||
public InternalTarget() : base(Core.AOS ? 3 : 2, false, TargetFlags.None)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish(Mobile from)
|
||||
{
|
||||
if (m_SetSkillTime)
|
||||
from.NextSkillTime = Core.TickCount;
|
||||
}
|
||||
|
||||
protected override void OnTarget(Mobile from, object targeted)
|
||||
{
|
||||
from.RevealingAction();
|
||||
|
||||
if (!(targeted is Mobile mobile))
|
||||
{
|
||||
ScaleSkills(m_Creature, 0.72, 0.90); // 72% of original skills trainable to 90%
|
||||
m_Creature.Skills.Magery.Base =
|
||||
m_Creature.Skills.Magery
|
||||
.Cap; // Greater dragons have a 90% cap reduction and 90% skill reduction on magery
|
||||
from.SendLocalizedMessage(502801); // You can't tame that!
|
||||
return;
|
||||
}
|
||||
else if (m_Paralyzed)
|
||||
|
||||
if (!(mobile is BaseCreature creature))
|
||||
{
|
||||
ScaleSkills(m_Creature,
|
||||
0.86); // 86% of original skills if they were paralyzed during the taming
|
||||
mobile.PrivateOverheadMessage(
|
||||
MessageType.Regular,
|
||||
0x3B2,
|
||||
502469,
|
||||
from.NetState
|
||||
); // That being cannot be tamed.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!creature.Tamable)
|
||||
{
|
||||
creature.PrivateOverheadMessage(
|
||||
MessageType.Regular,
|
||||
0x3B2,
|
||||
1049655,
|
||||
from.NetState
|
||||
); // That creature cannot be tamed.
|
||||
return;
|
||||
}
|
||||
|
||||
if (creature.Controlled)
|
||||
{
|
||||
creature.PrivateOverheadMessage(
|
||||
MessageType.Regular,
|
||||
0x3B2,
|
||||
502804,
|
||||
from.NetState
|
||||
); // That animal looks tame already.
|
||||
return;
|
||||
}
|
||||
|
||||
if (from.Female && !creature.AllowFemaleTamer)
|
||||
{
|
||||
creature.PrivateOverheadMessage(
|
||||
MessageType.Regular,
|
||||
0x3B2,
|
||||
1049653,
|
||||
from.NetState
|
||||
); // That creature can only be tamed by males.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!from.Female && !creature.AllowMaleTamer)
|
||||
{
|
||||
creature.PrivateOverheadMessage(
|
||||
MessageType.Regular,
|
||||
0x3B2,
|
||||
1049652,
|
||||
from.NetState
|
||||
); // That creature can only be tamed by females.
|
||||
return;
|
||||
}
|
||||
|
||||
if (creature is CuSidhe && from.Race != Race.Elf)
|
||||
{
|
||||
creature.PrivateOverheadMessage(
|
||||
MessageType.Regular,
|
||||
0x3B2,
|
||||
502801,
|
||||
from.NetState
|
||||
); // You can't tame that!
|
||||
return;
|
||||
}
|
||||
|
||||
if (from.Followers + creature.ControlSlots > from.FollowersMax)
|
||||
{
|
||||
from.SendLocalizedMessage(1049611); // You have too many followers to tame that creature.
|
||||
return;
|
||||
}
|
||||
|
||||
if (creature.Owners.Count >= BaseCreature.MaxOwners && !creature.Owners.Contains(from))
|
||||
{
|
||||
creature.PrivateOverheadMessage(
|
||||
MessageType.Regular,
|
||||
0x3B2,
|
||||
1005615,
|
||||
from.NetState
|
||||
); // This animal has had too many owners and is too upset for you to tame.
|
||||
return;
|
||||
}
|
||||
|
||||
if (MustBeSubdued(creature))
|
||||
{
|
||||
creature.PrivateOverheadMessage(
|
||||
MessageType.Regular,
|
||||
0x3B2,
|
||||
1054025,
|
||||
from.NetState
|
||||
); // You must subdue this creature before you can tame it!
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(CheckMastery(from, creature) || from.Skills.AnimalTaming.Value >= creature.MinTameSkill))
|
||||
{
|
||||
creature.PrivateOverheadMessage(
|
||||
MessageType.Regular,
|
||||
0x3B2,
|
||||
502806,
|
||||
from.NetState
|
||||
); // You have no chance of taming this creature.
|
||||
return;
|
||||
}
|
||||
|
||||
if (creature is FactionWarHorse warHorse)
|
||||
{
|
||||
var faction = Faction.Find(from);
|
||||
|
||||
if (faction == null || faction != warHorse.Faction)
|
||||
{
|
||||
creature.PrivateOverheadMessage(
|
||||
MessageType.Regular,
|
||||
0x3B2,
|
||||
1042590,
|
||||
from.NetState
|
||||
); // You cannot tame this creature.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_BeingTamed.Contains(creature))
|
||||
{
|
||||
creature.PrivateOverheadMessage(
|
||||
MessageType.Regular,
|
||||
0x3B2,
|
||||
502802,
|
||||
from.NetState
|
||||
); // Someone else is already taming this.
|
||||
}
|
||||
else if (creature.CanAngerOnTame && Utility.RandomDouble() <= 0.95)
|
||||
{
|
||||
creature.PrivateOverheadMessage(
|
||||
MessageType.Regular,
|
||||
0x3B2,
|
||||
502805,
|
||||
from.NetState
|
||||
); // You seem to anger the beast!
|
||||
creature.PlaySound(creature.GetAngerSound());
|
||||
creature.Direction = creature.GetDirectionTo(from);
|
||||
|
||||
if (creature.BardPacified && Utility.RandomDouble() > .24)
|
||||
Timer.DelayCall(TimeSpan.FromSeconds(2.0), Pacify, creature);
|
||||
else
|
||||
creature.BardEndTime = DateTime.UtcNow;
|
||||
|
||||
creature.BardPacified = false;
|
||||
|
||||
creature.AIObject?.DoMove(creature.Direction);
|
||||
|
||||
if (from is PlayerMobile pm &&
|
||||
!(pm.HonorActive ||
|
||||
TransformationSpellHelper.UnderTransformation(pm, typeof(EtherealVoyageSpell))))
|
||||
creature.Combatant = pm;
|
||||
}
|
||||
else
|
||||
{
|
||||
ScaleSkills(m_Creature, 0.90); // 90% of original skills
|
||||
m_BeingTamed.Add(creature);
|
||||
|
||||
from.LocalOverheadMessage(
|
||||
MessageType.Emote,
|
||||
0x59,
|
||||
1010597
|
||||
); // You start to tame the creature.
|
||||
from.NonlocalOverheadMessage(
|
||||
MessageType.Emote,
|
||||
0x59,
|
||||
1010598
|
||||
); // *begins taming a creature.*
|
||||
|
||||
new InternalTimer(from, creature, Utility.Random(3, 2)).Start();
|
||||
|
||||
m_SetSkillTime = false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void Pacify(BaseCreature bc) => bc.BardPacified = true; // Should use bc.Pacify with an end time?
|
||||
|
||||
private class InternalTimer : Timer
|
||||
{
|
||||
private readonly BaseCreature m_Creature;
|
||||
private readonly int m_MaxCount;
|
||||
private readonly DateTime m_StartTime;
|
||||
private readonly Mobile m_Tamer;
|
||||
private int m_Count;
|
||||
private bool m_Paralyzed;
|
||||
|
||||
public InternalTimer(Mobile tamer, BaseCreature creature, int count) : base(
|
||||
TimeSpan.FromSeconds(3.0),
|
||||
TimeSpan.FromSeconds(3.0),
|
||||
count
|
||||
)
|
||||
{
|
||||
m_Tamer = tamer;
|
||||
m_Creature = creature;
|
||||
m_MaxCount = count;
|
||||
m_Paralyzed = creature.Paralyzed;
|
||||
m_StartTime = DateTime.UtcNow;
|
||||
Priority = TimerPriority.TwoFiftyMS;
|
||||
}
|
||||
|
||||
if (m_Creature.StatLossAfterTame)
|
||||
ScaleStats(m_Creature, 0.50);
|
||||
}
|
||||
protected override void OnTick()
|
||||
{
|
||||
m_Count++;
|
||||
|
||||
if (alreadyOwned)
|
||||
{
|
||||
m_Tamer.SendLocalizedMessage(502797); // That wasn't even challenging.
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Creature.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 502799,
|
||||
m_Tamer.NetState); // It seems to accept you as master.
|
||||
m_Creature.Owners.Add(m_Tamer);
|
||||
}
|
||||
var de = m_Creature.FindMostRecentDamageEntry(false);
|
||||
var alreadyOwned = m_Creature.Owners.Contains(m_Tamer);
|
||||
|
||||
m_Creature.SetControlMaster(m_Tamer);
|
||||
m_Creature.IsBonded = false;
|
||||
if (!m_Tamer.InRange(m_Creature, Core.AOS ? 7 : 6))
|
||||
{
|
||||
m_BeingTamed.Remove(m_Creature);
|
||||
m_Tamer.NextSkillTime = Core.TickCount;
|
||||
m_Creature.PrivateOverheadMessage(
|
||||
MessageType.Regular,
|
||||
0x3B2,
|
||||
502795,
|
||||
m_Tamer.NetState
|
||||
); // You are too far away to continue taming.
|
||||
Stop();
|
||||
}
|
||||
else if (!m_Tamer.CheckAlive())
|
||||
{
|
||||
m_BeingTamed.Remove(m_Creature);
|
||||
m_Tamer.NextSkillTime = Core.TickCount;
|
||||
m_Creature.PrivateOverheadMessage(
|
||||
MessageType.Regular,
|
||||
0x3B2,
|
||||
502796,
|
||||
m_Tamer.NetState
|
||||
); // You are dead, and cannot continue taming.
|
||||
Stop();
|
||||
}
|
||||
else if (!m_Tamer.CanSee(m_Creature) || !m_Tamer.InLOS(m_Creature) || !CanPath())
|
||||
{
|
||||
m_BeingTamed.Remove(m_Creature);
|
||||
m_Tamer.NextSkillTime = Core.TickCount;
|
||||
m_Tamer.SendLocalizedMessage(
|
||||
1049654
|
||||
); // You do not have a clear path to the animal you are taming, and must cease your attempt.
|
||||
Stop();
|
||||
}
|
||||
else if (!m_Creature.Tamable)
|
||||
{
|
||||
m_BeingTamed.Remove(m_Creature);
|
||||
m_Tamer.NextSkillTime = Core.TickCount;
|
||||
m_Creature.PrivateOverheadMessage(
|
||||
MessageType.Regular,
|
||||
0x3B2,
|
||||
1049655,
|
||||
m_Tamer.NetState
|
||||
); // That creature cannot be tamed.
|
||||
Stop();
|
||||
}
|
||||
else if (m_Creature.Controlled)
|
||||
{
|
||||
m_BeingTamed.Remove(m_Creature);
|
||||
m_Tamer.NextSkillTime = Core.TickCount;
|
||||
m_Creature.PrivateOverheadMessage(
|
||||
MessageType.Regular,
|
||||
0x3B2,
|
||||
502804,
|
||||
m_Tamer.NetState
|
||||
); // That animal looks tame already.
|
||||
Stop();
|
||||
}
|
||||
else if (m_Creature.Owners.Count >= BaseCreature.MaxOwners && !m_Creature.Owners.Contains(m_Tamer))
|
||||
{
|
||||
m_BeingTamed.Remove(m_Creature);
|
||||
m_Tamer.NextSkillTime = Core.TickCount;
|
||||
m_Creature.PrivateOverheadMessage(
|
||||
MessageType.Regular,
|
||||
0x3B2,
|
||||
1005615,
|
||||
m_Tamer.NetState
|
||||
); // This animal has had too many owners and is too upset for you to tame.
|
||||
Stop();
|
||||
}
|
||||
else if (MustBeSubdued(m_Creature))
|
||||
{
|
||||
m_BeingTamed.Remove(m_Creature);
|
||||
m_Tamer.NextSkillTime = Core.TickCount;
|
||||
m_Creature.PrivateOverheadMessage(
|
||||
MessageType.Regular,
|
||||
0x3B2,
|
||||
1054025,
|
||||
m_Tamer.NetState
|
||||
); // You must subdue this creature before you can tame it!
|
||||
Stop();
|
||||
}
|
||||
else if (de?.LastDamage > m_StartTime)
|
||||
{
|
||||
m_BeingTamed.Remove(m_Creature);
|
||||
m_Tamer.NextSkillTime = Core.TickCount;
|
||||
m_Creature.PrivateOverheadMessage(
|
||||
MessageType.Regular,
|
||||
0x3B2,
|
||||
502794,
|
||||
m_Tamer.NetState
|
||||
); // The animal is too angry to continue taming.
|
||||
Stop();
|
||||
}
|
||||
else if (m_Count < m_MaxCount)
|
||||
{
|
||||
m_Tamer.RevealingAction();
|
||||
|
||||
switch (Utility.Random(3))
|
||||
{
|
||||
case 0:
|
||||
m_Tamer.PublicOverheadMessage(MessageType.Regular, 0x3B2, Utility.Random(502790, 4));
|
||||
break;
|
||||
case 1:
|
||||
m_Tamer.PublicOverheadMessage(MessageType.Regular, 0x3B2, Utility.Random(1005608, 6));
|
||||
break;
|
||||
case 2:
|
||||
m_Tamer.PublicOverheadMessage(MessageType.Regular, 0x3B2, Utility.Random(1010593, 4));
|
||||
break;
|
||||
}
|
||||
|
||||
if (!alreadyOwned) // Passively check animal lore for gain
|
||||
m_Tamer.CheckTargetSkill(SkillName.AnimalLore, m_Creature, 0.0, 120.0);
|
||||
|
||||
if (m_Creature.Paralyzed)
|
||||
m_Paralyzed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Tamer.RevealingAction();
|
||||
m_Tamer.NextSkillTime = Core.TickCount;
|
||||
m_BeingTamed.Remove(m_Creature);
|
||||
|
||||
if (m_Creature.Paralyzed)
|
||||
m_Paralyzed = true;
|
||||
|
||||
if (!alreadyOwned) // Passively check animal lore for gain
|
||||
m_Tamer.CheckTargetSkill(SkillName.AnimalLore, m_Creature, 0.0, 120.0);
|
||||
|
||||
var minSkill = m_Creature.MinTameSkill + m_Creature.Owners.Count * 6.0;
|
||||
|
||||
if (minSkill > -24.9 && CheckMastery(m_Tamer, m_Creature))
|
||||
minSkill = -24.9; // 50% at 0.0?
|
||||
|
||||
minSkill += 24.9;
|
||||
|
||||
if (CheckMastery(m_Tamer, m_Creature) || alreadyOwned ||
|
||||
m_Tamer.CheckTargetSkill(SkillName.AnimalTaming, m_Creature, minSkill - 25.0, minSkill + 25.0))
|
||||
{
|
||||
if (m_Creature.Owners.Count == 0) // First tame
|
||||
{
|
||||
if (m_Creature is GreaterDragon)
|
||||
{
|
||||
ScaleSkills(m_Creature, 0.72, 0.90); // 72% of original skills trainable to 90%
|
||||
m_Creature.Skills.Magery.Base =
|
||||
m_Creature.Skills.Magery
|
||||
.Cap; // Greater dragons have a 90% cap reduction and 90% skill reduction on magery
|
||||
}
|
||||
else if (m_Paralyzed)
|
||||
{
|
||||
ScaleSkills(
|
||||
m_Creature,
|
||||
0.86
|
||||
); // 86% of original skills if they were paralyzed during the taming
|
||||
}
|
||||
else
|
||||
{
|
||||
ScaleSkills(m_Creature, 0.90); // 90% of original skills
|
||||
}
|
||||
|
||||
if (m_Creature.StatLossAfterTame)
|
||||
ScaleStats(m_Creature, 0.50);
|
||||
}
|
||||
|
||||
if (alreadyOwned)
|
||||
{
|
||||
m_Tamer.SendLocalizedMessage(502797); // That wasn't even challenging.
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Creature.PrivateOverheadMessage(
|
||||
MessageType.Regular,
|
||||
0x3B2,
|
||||
502799,
|
||||
m_Tamer.NetState
|
||||
); // It seems to accept you as master.
|
||||
m_Creature.Owners.Add(m_Tamer);
|
||||
}
|
||||
|
||||
m_Creature.SetControlMaster(m_Tamer);
|
||||
m_Creature.IsBonded = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Creature.PrivateOverheadMessage(
|
||||
MessageType.Regular,
|
||||
0x3B2,
|
||||
502798,
|
||||
m_Tamer.NetState
|
||||
); // You fail to tame the creature.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool CanPath()
|
||||
{
|
||||
IPoint3D p = m_Tamer;
|
||||
|
||||
return p != null && (m_Creature.InRange(new Point3D(p), 1) ||
|
||||
new MovementPath(m_Creature, new Point3D(p)).Success);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Creature.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 502798,
|
||||
m_Tamer.NetState); // You fail to tame the creature.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool CanPath()
|
||||
{
|
||||
IPoint3D p = m_Tamer;
|
||||
|
||||
return p != null && (m_Creature.InRange(new Point3D(p), 1) ||
|
||||
new MovementPath(m_Creature, new Point3D(p)).Success);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,110 +6,110 @@ using Server.Targeting;
|
|||
|
||||
namespace Server.SkillHandlers
|
||||
{
|
||||
public static class ArmsLore
|
||||
{
|
||||
public static void Initialize()
|
||||
public static class ArmsLore
|
||||
{
|
||||
SkillInfo.Table[(int)SkillName.ArmsLore].Callback = OnUse;
|
||||
}
|
||||
|
||||
public static TimeSpan OnUse(Mobile m)
|
||||
{
|
||||
m.Target = new InternalTarget();
|
||||
|
||||
m.SendLocalizedMessage(500349); // What item do you wish to get information about?
|
||||
|
||||
return TimeSpan.FromSeconds(1.0);
|
||||
}
|
||||
|
||||
[PlayerVendorTarget]
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
public InternalTarget() : base(2, false, TargetFlags.None) => AllowNonlocal = true;
|
||||
|
||||
protected override void OnTarget(Mobile from, object targeted)
|
||||
{
|
||||
if (targeted is BaseWeapon weap)
|
||||
public static void Initialize()
|
||||
{
|
||||
if (from.CheckTargetSkill(SkillName.ArmsLore, weap, 0, 100))
|
||||
{
|
||||
if (weap.MaxHitPoints != 0)
|
||||
SkillInfo.Table[(int)SkillName.ArmsLore].Callback = OnUse;
|
||||
}
|
||||
|
||||
public static TimeSpan OnUse(Mobile m)
|
||||
{
|
||||
m.Target = new InternalTarget();
|
||||
|
||||
m.SendLocalizedMessage(500349); // What item do you wish to get information about?
|
||||
|
||||
return TimeSpan.FromSeconds(1.0);
|
||||
}
|
||||
|
||||
[PlayerVendorTarget]
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
public InternalTarget() : base(2, false, TargetFlags.None) => AllowNonlocal = true;
|
||||
|
||||
protected override void OnTarget(Mobile from, object targeted)
|
||||
{
|
||||
int hp = Math.Clamp((int)(weap.HitPoints / (double)weap.MaxHitPoints * 10), 0, 9);
|
||||
if (targeted is BaseWeapon weap)
|
||||
{
|
||||
if (from.CheckTargetSkill(SkillName.ArmsLore, weap, 0, 100))
|
||||
{
|
||||
if (weap.MaxHitPoints != 0)
|
||||
{
|
||||
var hp = Math.Clamp((int)(weap.HitPoints / (double)weap.MaxHitPoints * 10), 0, 9);
|
||||
|
||||
from.SendLocalizedMessage(1038285 + hp);
|
||||
from.SendLocalizedMessage(1038285 + hp);
|
||||
}
|
||||
|
||||
var damage = (weap.MaxDamage + weap.MinDamage) / 2;
|
||||
var hand = weap.Layer == Layer.OneHanded ? 0 : 1;
|
||||
|
||||
if (damage < 3)
|
||||
damage = 0;
|
||||
else
|
||||
damage = (int)Math.Ceiling(Math.Min(damage, 30) / 5.0);
|
||||
|
||||
var type = weap.Type;
|
||||
|
||||
if (type == WeaponType.Ranged)
|
||||
from.SendLocalizedMessage(1038224 + damage * 9);
|
||||
else if (type == WeaponType.Piercing)
|
||||
from.SendLocalizedMessage(1038218 + hand + damage * 9);
|
||||
else if (type == WeaponType.Slashing)
|
||||
from.SendLocalizedMessage(1038220 + hand + damage * 9);
|
||||
else if (type == WeaponType.Bashing)
|
||||
from.SendLocalizedMessage(1038222 + hand + damage * 9);
|
||||
else
|
||||
from.SendLocalizedMessage(1038216 + hand + damage * 9);
|
||||
|
||||
if (weap.Poison != null && weap.PoisonCharges > 0)
|
||||
from.SendLocalizedMessage(1038284); // It appears to have poison smeared on it.
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(500353); // You are not certain...
|
||||
}
|
||||
}
|
||||
else if (targeted is BaseArmor arm)
|
||||
{
|
||||
if (from.CheckTargetSkill(SkillName.ArmsLore, arm, 0, 100))
|
||||
{
|
||||
if (arm.MaxHitPoints != 0)
|
||||
{
|
||||
var hp = Math.Clamp((int)(arm.HitPoints / (double)arm.MaxHitPoints * 10), 0, 9);
|
||||
|
||||
from.SendLocalizedMessage(1038285 + hp);
|
||||
}
|
||||
|
||||
from.SendLocalizedMessage(1038295 + (int)Math.Ceiling(Math.Min(arm.ArmorRating, 35) / 5.0));
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(500353); // You are not certain...
|
||||
}
|
||||
}
|
||||
else if (targeted is SwampDragon pet && pet.HasBarding)
|
||||
{
|
||||
if (from.CheckTargetSkill(SkillName.ArmsLore, pet, 0, 100))
|
||||
{
|
||||
var perc = 4 * pet.BardingHP / pet.BardingMaxHP;
|
||||
|
||||
if (perc < 0)
|
||||
perc = 0;
|
||||
else if (perc > 4)
|
||||
perc = 4;
|
||||
|
||||
pet.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 1053021 - perc, from.NetState);
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(500353); // You are not certain...
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(500352); // This is neither weapon nor armor.
|
||||
}
|
||||
}
|
||||
|
||||
int damage = (weap.MaxDamage + weap.MinDamage) / 2;
|
||||
int hand = weap.Layer == Layer.OneHanded ? 0 : 1;
|
||||
|
||||
if (damage < 3)
|
||||
damage = 0;
|
||||
else
|
||||
damage = (int)Math.Ceiling(Math.Min(damage, 30) / 5.0);
|
||||
|
||||
WeaponType type = weap.Type;
|
||||
|
||||
if (type == WeaponType.Ranged)
|
||||
from.SendLocalizedMessage(1038224 + damage * 9);
|
||||
else if (type == WeaponType.Piercing)
|
||||
from.SendLocalizedMessage(1038218 + hand + damage * 9);
|
||||
else if (type == WeaponType.Slashing)
|
||||
from.SendLocalizedMessage(1038220 + hand + damage * 9);
|
||||
else if (type == WeaponType.Bashing)
|
||||
from.SendLocalizedMessage(1038222 + hand + damage * 9);
|
||||
else
|
||||
from.SendLocalizedMessage(1038216 + hand + damage * 9);
|
||||
|
||||
if (weap.Poison != null && weap.PoisonCharges > 0)
|
||||
from.SendLocalizedMessage(1038284); // It appears to have poison smeared on it.
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(500353); // You are not certain...
|
||||
}
|
||||
}
|
||||
else if (targeted is BaseArmor arm)
|
||||
{
|
||||
if (from.CheckTargetSkill(SkillName.ArmsLore, arm, 0, 100))
|
||||
{
|
||||
if (arm.MaxHitPoints != 0)
|
||||
{
|
||||
int hp = Math.Clamp((int)(arm.HitPoints / (double)arm.MaxHitPoints * 10), 0, 9);
|
||||
|
||||
from.SendLocalizedMessage(1038285 + hp);
|
||||
}
|
||||
|
||||
from.SendLocalizedMessage(1038295 + (int)Math.Ceiling(Math.Min(arm.ArmorRating, 35) / 5.0));
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(500353); // You are not certain...
|
||||
}
|
||||
}
|
||||
else if (targeted is SwampDragon pet && pet.HasBarding)
|
||||
{
|
||||
if (from.CheckTargetSkill(SkillName.ArmsLore, pet, 0, 100))
|
||||
{
|
||||
int perc = 4 * pet.BardingHP / pet.BardingMaxHP;
|
||||
|
||||
if (perc < 0)
|
||||
perc = 0;
|
||||
else if (perc > 4)
|
||||
perc = 4;
|
||||
|
||||
pet.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 1053021 - perc, from.NetState);
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(500353); // You are not certain...
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(500352); // This is neither weapon nor armor.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,172 +6,185 @@ using Server.Targeting;
|
|||
|
||||
namespace Server.SkillHandlers
|
||||
{
|
||||
public static class Begging
|
||||
{
|
||||
public static void Initialize()
|
||||
public static class Begging
|
||||
{
|
||||
SkillInfo.Table[(int)SkillName.Begging].Callback = OnUse;
|
||||
}
|
||||
|
||||
public static TimeSpan OnUse(Mobile m)
|
||||
{
|
||||
m.RevealingAction();
|
||||
|
||||
m.Target = new InternalTarget();
|
||||
m.RevealingAction();
|
||||
|
||||
m.SendLocalizedMessage(500397); // To whom do you wish to grovel?
|
||||
|
||||
return TimeSpan.FromHours(6.0);
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private bool m_SetSkillTime = true;
|
||||
|
||||
public InternalTarget() : base(12, false, TargetFlags.None)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish(Mobile from)
|
||||
{
|
||||
if (m_SetSkillTime)
|
||||
from.NextSkillTime = Core.TickCount;
|
||||
}
|
||||
|
||||
protected override void OnTarget(Mobile from, object targeted)
|
||||
{
|
||||
from.RevealingAction();
|
||||
|
||||
int number = -1;
|
||||
|
||||
if (targeted is Mobile targ)
|
||||
public static void Initialize()
|
||||
{
|
||||
if (targ.Player) // We can't beg from players
|
||||
{
|
||||
number = 500398; // Perhaps just asking would work better.
|
||||
}
|
||||
else if (!targ.Body.IsHuman) // Make sure the NPC is human
|
||||
{
|
||||
number = 500399; // There is little chance of getting money from that!
|
||||
}
|
||||
else if (!from.InRange(targ, 2))
|
||||
{
|
||||
if (!targ.Female)
|
||||
number = 500401; // You are too far away to beg from him.
|
||||
else
|
||||
number = 500402; // You are too far away to beg from her.
|
||||
}
|
||||
else if (!Core.ML && from.Mounted) // If we're on a mount, who would give us money? TODO: guessed it's removed since ML
|
||||
{
|
||||
number = 500404; // They seem unwilling to give you any money.
|
||||
}
|
||||
else
|
||||
{
|
||||
// Face each other
|
||||
from.Direction = from.GetDirectionTo(targ);
|
||||
targ.Direction = targ.GetDirectionTo(from);
|
||||
|
||||
from.Animate(32, 5, 1, true, false, 0); // Bow
|
||||
|
||||
new InternalTimer(from, targ).Start();
|
||||
|
||||
m_SetSkillTime = false;
|
||||
}
|
||||
}
|
||||
else // Not a Mobile
|
||||
{
|
||||
number = 500399; // There is little chance of getting money from that!
|
||||
SkillInfo.Table[(int)SkillName.Begging].Callback = OnUse;
|
||||
}
|
||||
|
||||
if (number != -1)
|
||||
from.SendLocalizedMessage(number);
|
||||
}
|
||||
|
||||
private class InternalTimer : Timer
|
||||
{
|
||||
private readonly Mobile m_From;
|
||||
private readonly Mobile m_Target;
|
||||
|
||||
public InternalTimer(Mobile from, Mobile target) : base(TimeSpan.FromSeconds(2.0))
|
||||
public static TimeSpan OnUse(Mobile m)
|
||||
{
|
||||
m_From = from;
|
||||
m_Target = target;
|
||||
Priority = TimerPriority.TwoFiftyMS;
|
||||
m.RevealingAction();
|
||||
|
||||
m.Target = new InternalTarget();
|
||||
m.RevealingAction();
|
||||
|
||||
m.SendLocalizedMessage(500397); // To whom do you wish to grovel?
|
||||
|
||||
return TimeSpan.FromHours(6.0);
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
Container theirPack = m_Target.Backpack;
|
||||
private bool m_SetSkillTime = true;
|
||||
|
||||
double badKarmaChance = 0.5 - (double)m_From.Karma / 8570;
|
||||
|
||||
if (theirPack == null)
|
||||
{
|
||||
m_From.SendLocalizedMessage(500404); // They seem unwilling to give you any money.
|
||||
}
|
||||
else if (m_From.Karma < 0 && badKarmaChance > Utility.RandomDouble())
|
||||
{
|
||||
m_Target.PublicOverheadMessage(MessageType.Regular, m_Target.SpeechHue,
|
||||
500406); // Thou dost not look trustworthy... no gold for thee today!
|
||||
}
|
||||
else if (m_From.CheckTargetSkill(SkillName.Begging, m_Target, 0.0, 100.0))
|
||||
{
|
||||
int toConsume = theirPack.GetAmount(typeof(Gold)) / 10;
|
||||
int max = 10 + m_From.Fame / 2500;
|
||||
|
||||
if (max > 14)
|
||||
max = 14;
|
||||
else if (max < 10)
|
||||
max = 10;
|
||||
|
||||
if (toConsume > max)
|
||||
toConsume = max;
|
||||
|
||||
if (toConsume > 0)
|
||||
public InternalTarget() : base(12, false, TargetFlags.None)
|
||||
{
|
||||
int consumed = theirPack.ConsumeUpTo(typeof(Gold), toConsume);
|
||||
}
|
||||
|
||||
if (consumed > 0)
|
||||
{
|
||||
m_Target.PublicOverheadMessage(MessageType.Regular, m_Target.SpeechHue,
|
||||
500405); // I feel sorry for thee...
|
||||
protected override void OnTargetFinish(Mobile from)
|
||||
{
|
||||
if (m_SetSkillTime)
|
||||
from.NextSkillTime = Core.TickCount;
|
||||
}
|
||||
|
||||
Gold gold = new Gold(consumed);
|
||||
protected override void OnTarget(Mobile from, object targeted)
|
||||
{
|
||||
from.RevealingAction();
|
||||
|
||||
m_From.AddToBackpack(gold);
|
||||
m_From.PlaySound(gold.GetDropSound());
|
||||
var number = -1;
|
||||
|
||||
if (m_From.Karma > -3000)
|
||||
if (targeted is Mobile targ)
|
||||
{
|
||||
int toLose = m_From.Karma + 3000;
|
||||
if (targ.Player) // We can't beg from players
|
||||
{
|
||||
number = 500398; // Perhaps just asking would work better.
|
||||
}
|
||||
else if (!targ.Body.IsHuman) // Make sure the NPC is human
|
||||
{
|
||||
number = 500399; // There is little chance of getting money from that!
|
||||
}
|
||||
else if (!from.InRange(targ, 2))
|
||||
{
|
||||
if (!targ.Female)
|
||||
number = 500401; // You are too far away to beg from him.
|
||||
else
|
||||
number = 500402; // You are too far away to beg from her.
|
||||
}
|
||||
else if (!Core.ML && from.Mounted
|
||||
) // If we're on a mount, who would give us money? TODO: guessed it's removed since ML
|
||||
{
|
||||
number = 500404; // They seem unwilling to give you any money.
|
||||
}
|
||||
else
|
||||
{
|
||||
// Face each other
|
||||
from.Direction = from.GetDirectionTo(targ);
|
||||
targ.Direction = targ.GetDirectionTo(from);
|
||||
|
||||
if (toLose > 40)
|
||||
toLose = 40;
|
||||
from.Animate(32, 5, 1, true, false, 0); // Bow
|
||||
|
||||
Titles.AwardKarma(m_From, -toLose, true);
|
||||
new InternalTimer(from, targ).Start();
|
||||
|
||||
m_SetSkillTime = false;
|
||||
}
|
||||
}
|
||||
else // Not a Mobile
|
||||
{
|
||||
number = 500399; // There is little chance of getting money from that!
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Target.PublicOverheadMessage(MessageType.Regular, m_Target.SpeechHue,
|
||||
500407); // I have not enough money to give thee any!
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Target.PublicOverheadMessage(MessageType.Regular, m_Target.SpeechHue,
|
||||
500407); // I have not enough money to give thee any!
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Target.SendLocalizedMessage(500404); // They seem unwilling to give you any money.
|
||||
}
|
||||
|
||||
m_From.NextSkillTime = Core.TickCount + 10000;
|
||||
if (number != -1)
|
||||
from.SendLocalizedMessage(number);
|
||||
}
|
||||
|
||||
private class InternalTimer : Timer
|
||||
{
|
||||
private readonly Mobile m_From;
|
||||
private readonly Mobile m_Target;
|
||||
|
||||
public InternalTimer(Mobile from, Mobile target) : base(TimeSpan.FromSeconds(2.0))
|
||||
{
|
||||
m_From = from;
|
||||
m_Target = target;
|
||||
Priority = TimerPriority.TwoFiftyMS;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
var theirPack = m_Target.Backpack;
|
||||
|
||||
var badKarmaChance = 0.5 - (double)m_From.Karma / 8570;
|
||||
|
||||
if (theirPack == null)
|
||||
{
|
||||
m_From.SendLocalizedMessage(500404); // They seem unwilling to give you any money.
|
||||
}
|
||||
else if (m_From.Karma < 0 && badKarmaChance > Utility.RandomDouble())
|
||||
{
|
||||
m_Target.PublicOverheadMessage(
|
||||
MessageType.Regular,
|
||||
m_Target.SpeechHue,
|
||||
500406
|
||||
); // Thou dost not look trustworthy... no gold for thee today!
|
||||
}
|
||||
else if (m_From.CheckTargetSkill(SkillName.Begging, m_Target, 0.0, 100.0))
|
||||
{
|
||||
var toConsume = theirPack.GetAmount(typeof(Gold)) / 10;
|
||||
var max = 10 + m_From.Fame / 2500;
|
||||
|
||||
if (max > 14)
|
||||
max = 14;
|
||||
else if (max < 10)
|
||||
max = 10;
|
||||
|
||||
if (toConsume > max)
|
||||
toConsume = max;
|
||||
|
||||
if (toConsume > 0)
|
||||
{
|
||||
var consumed = theirPack.ConsumeUpTo(typeof(Gold), toConsume);
|
||||
|
||||
if (consumed > 0)
|
||||
{
|
||||
m_Target.PublicOverheadMessage(
|
||||
MessageType.Regular,
|
||||
m_Target.SpeechHue,
|
||||
500405
|
||||
); // I feel sorry for thee...
|
||||
|
||||
var gold = new Gold(consumed);
|
||||
|
||||
m_From.AddToBackpack(gold);
|
||||
m_From.PlaySound(gold.GetDropSound());
|
||||
|
||||
if (m_From.Karma > -3000)
|
||||
{
|
||||
var toLose = m_From.Karma + 3000;
|
||||
|
||||
if (toLose > 40)
|
||||
toLose = 40;
|
||||
|
||||
Titles.AwardKarma(m_From, -toLose, true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Target.PublicOverheadMessage(
|
||||
MessageType.Regular,
|
||||
m_Target.SpeechHue,
|
||||
500407
|
||||
); // I have not enough money to give thee any!
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Target.PublicOverheadMessage(
|
||||
MessageType.Regular,
|
||||
m_Target.SpeechHue,
|
||||
500407
|
||||
); // I have not enough money to give thee any!
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Target.SendLocalizedMessage(500404); // They seem unwilling to give you any money.
|
||||
}
|
||||
|
||||
m_From.NextSkillTime = Core.TickCount + 10000;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,99 +6,102 @@ using Server.Targeting;
|
|||
|
||||
namespace Server.SkillHandlers
|
||||
{
|
||||
public static class DetectHidden
|
||||
{
|
||||
public static void Initialize()
|
||||
public static class DetectHidden
|
||||
{
|
||||
SkillInfo.Table[(int)SkillName.DetectHidden].Callback = OnUse;
|
||||
}
|
||||
|
||||
public static TimeSpan OnUse(Mobile src)
|
||||
{
|
||||
src.SendLocalizedMessage(500819); // Where will you search?
|
||||
src.Target = new InternalTarget();
|
||||
|
||||
return TimeSpan.FromSeconds(6.0);
|
||||
}
|
||||
|
||||
private 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 mobile)
|
||||
p = mobile.Location;
|
||||
else if (targ is Item item)
|
||||
p = item.Location;
|
||||
else if (targ is IPoint3D d)
|
||||
p = new Point3D(d);
|
||||
else
|
||||
p = src.Location;
|
||||
|
||||
double srcSkill = src.Skills.DetectHidden.Value;
|
||||
int range = (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?.IsFriend(src) == true;
|
||||
|
||||
if (inHouse)
|
||||
range = 22;
|
||||
|
||||
if (range > 0)
|
||||
public static void Initialize()
|
||||
{
|
||||
IPooledEnumerable<Mobile> 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.Hiding.Value + Utility.Random(21) - 10;
|
||||
|
||||
if (src.AccessLevel >= trg.AccessLevel && (ss >= ts || (inHouse && house.IsInside(trg))))
|
||||
{
|
||||
if (trg is ShadowKnight && (trg.X != p.X || trg.Y != p.Y))
|
||||
continue;
|
||||
|
||||
trg.RevealingAction();
|
||||
trg.SendLocalizedMessage(500814); // You have been revealed!
|
||||
foundAnyone = true;
|
||||
}
|
||||
}
|
||||
|
||||
inRange.Free();
|
||||
|
||||
if (Faction.Find(src) != null)
|
||||
{
|
||||
IPooledEnumerable<BaseFactionTrap> itemsInRange = src.Map.GetItemsInRange<BaseFactionTrap>(p, range);
|
||||
|
||||
foreach (BaseFactionTrap trap in itemsInRange)
|
||||
if (src.CheckTargetSkill(SkillName.DetectHidden, trap, 80.0, 100.0))
|
||||
{
|
||||
src.SendLocalizedMessage(1042712, true,
|
||||
$" {(trap.Faction == null ? "" : trap.Faction.Definition.FriendlyName)}"); // You reveal a trap placed by a faction:
|
||||
|
||||
trap.Visible = true;
|
||||
trap.BeginConceal();
|
||||
|
||||
foundAnyone = true;
|
||||
}
|
||||
|
||||
itemsInRange.Free();
|
||||
}
|
||||
SkillInfo.Table[(int)SkillName.DetectHidden].Callback = OnUse;
|
||||
}
|
||||
|
||||
if (!foundAnyone) src.SendLocalizedMessage(500817); // You can see nothing hidden there.
|
||||
}
|
||||
public static TimeSpan OnUse(Mobile src)
|
||||
{
|
||||
src.SendLocalizedMessage(500819); // Where will you search?
|
||||
src.Target = new InternalTarget();
|
||||
|
||||
return TimeSpan.FromSeconds(6.0);
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
public InternalTarget() : base(12, true, TargetFlags.None)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnTarget(Mobile src, object targ)
|
||||
{
|
||||
var foundAnyone = false;
|
||||
|
||||
Point3D p;
|
||||
if (targ is Mobile mobile)
|
||||
p = mobile.Location;
|
||||
else if (targ is Item item)
|
||||
p = item.Location;
|
||||
else if (targ is IPoint3D d)
|
||||
p = new Point3D(d);
|
||||
else
|
||||
p = src.Location;
|
||||
|
||||
var srcSkill = src.Skills.DetectHidden.Value;
|
||||
var range = (int)(srcSkill / 10.0);
|
||||
|
||||
if (!src.CheckSkill(SkillName.DetectHidden, 0.0, 100.0))
|
||||
range /= 2;
|
||||
|
||||
var house = BaseHouse.FindHouseAt(p, src.Map, 16);
|
||||
|
||||
var inHouse = house?.IsFriend(src) == true;
|
||||
|
||||
if (inHouse)
|
||||
range = 22;
|
||||
|
||||
if (range > 0)
|
||||
{
|
||||
var inRange = src.Map.GetMobilesInRange(p, range);
|
||||
|
||||
foreach (var trg in inRange)
|
||||
if (trg.Hidden && src != trg)
|
||||
{
|
||||
var ss = srcSkill + Utility.Random(21) - 10;
|
||||
var ts = trg.Skills.Hiding.Value + Utility.Random(21) - 10;
|
||||
|
||||
if (src.AccessLevel >= trg.AccessLevel && (ss >= ts || inHouse && house.IsInside(trg)))
|
||||
{
|
||||
if (trg is ShadowKnight && (trg.X != p.X || trg.Y != p.Y))
|
||||
continue;
|
||||
|
||||
trg.RevealingAction();
|
||||
trg.SendLocalizedMessage(500814); // You have been revealed!
|
||||
foundAnyone = true;
|
||||
}
|
||||
}
|
||||
|
||||
inRange.Free();
|
||||
|
||||
if (Faction.Find(src) != null)
|
||||
{
|
||||
var itemsInRange = src.Map.GetItemsInRange<BaseFactionTrap>(p, range);
|
||||
|
||||
foreach (var trap in itemsInRange)
|
||||
if (src.CheckTargetSkill(SkillName.DetectHidden, trap, 80.0, 100.0))
|
||||
{
|
||||
src.SendLocalizedMessage(
|
||||
1042712,
|
||||
true,
|
||||
$" {(trap.Faction == null ? "" : trap.Faction.Definition.FriendlyName)}"
|
||||
); // You reveal a trap placed by a faction:
|
||||
|
||||
trap.Visible = true;
|
||||
trap.BeginConceal();
|
||||
|
||||
foundAnyone = true;
|
||||
}
|
||||
|
||||
itemsInRange.Free();
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundAnyone) src.SendLocalizedMessage(500817); // You can see nothing hidden there.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,259 +6,285 @@ using Server.Targeting;
|
|||
|
||||
namespace Server.SkillHandlers
|
||||
{
|
||||
public static class Discordance
|
||||
{
|
||||
private static readonly Dictionary<Mobile, DiscordanceInfo> m_Table = new Dictionary<Mobile, DiscordanceInfo>();
|
||||
|
||||
public static void Initialize()
|
||||
public static class Discordance
|
||||
{
|
||||
SkillInfo.Table[(int)SkillName.Discordance].Callback = OnUse;
|
||||
}
|
||||
private static readonly Dictionary<Mobile, DiscordanceInfo> m_Table = new Dictionary<Mobile, DiscordanceInfo>();
|
||||
|
||||
public static TimeSpan OnUse(Mobile m)
|
||||
{
|
||||
m.RevealingAction();
|
||||
|
||||
BaseInstrument.PickInstrument(m, OnPickedInstrument);
|
||||
|
||||
return TimeSpan.FromSeconds(1.0); // Cannot use another skill for 1 second
|
||||
}
|
||||
|
||||
public static void OnPickedInstrument(Mobile from, BaseInstrument instrument)
|
||||
{
|
||||
from.RevealingAction();
|
||||
from.SendLocalizedMessage(1049541); // Choose the target for your song of discordance.
|
||||
from.Target = new DiscordanceTarget(from, instrument);
|
||||
from.NextSkillTime = Core.TickCount + 6000;
|
||||
}
|
||||
|
||||
public static bool GetEffect(Mobile targ, ref int effect)
|
||||
{
|
||||
if (!m_Table.TryGetValue(targ, out DiscordanceInfo info))
|
||||
return false;
|
||||
|
||||
effect = info.m_Effect;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void ProcessDiscordance(DiscordanceInfo info)
|
||||
{
|
||||
Mobile from = info.m_From;
|
||||
Mobile targ = info.m_Creature;
|
||||
bool ends = false;
|
||||
|
||||
// According to uoherald bard must remain alive, visible, and
|
||||
// within range of the target or the effect ends in 15 seconds.
|
||||
if (!targ.Alive || targ.Deleted || !from.Alive || from.Hidden)
|
||||
{
|
||||
ends = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
int range = (int)targ.GetDistanceToSqrt(from);
|
||||
int maxRange = BaseInstrument.GetBardRange(from, SkillName.Discordance);
|
||||
|
||||
if (from.Map != targ.Map || range > maxRange)
|
||||
ends = true;
|
||||
}
|
||||
|
||||
if (ends && info.m_Ending && info.m_EndTime < DateTime.UtcNow)
|
||||
{
|
||||
info.m_Timer?.Stop();
|
||||
|
||||
info.Clear();
|
||||
m_Table.Remove(targ);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ends && !info.m_Ending)
|
||||
public static void Initialize()
|
||||
{
|
||||
info.m_Ending = true;
|
||||
info.m_EndTime = DateTime.UtcNow + TimeSpan.FromSeconds(15);
|
||||
}
|
||||
else if (!ends)
|
||||
{
|
||||
info.m_Ending = false;
|
||||
info.m_EndTime = DateTime.UtcNow;
|
||||
SkillInfo.Table[(int)SkillName.Discordance].Callback = OnUse;
|
||||
}
|
||||
|
||||
targ.FixedEffect(0x376A, 1, 32);
|
||||
}
|
||||
}
|
||||
|
||||
private class DiscordanceInfo
|
||||
{
|
||||
public readonly Mobile m_Creature;
|
||||
public readonly int m_Effect;
|
||||
public bool m_Ending;
|
||||
public DateTime m_EndTime;
|
||||
public readonly Mobile m_From;
|
||||
public readonly List<object> m_Mods;
|
||||
public Timer m_Timer;
|
||||
|
||||
public DiscordanceInfo(Mobile from, Mobile creature, int effect, List<object> mods)
|
||||
{
|
||||
m_From = from;
|
||||
m_Creature = creature;
|
||||
m_EndTime = DateTime.UtcNow;
|
||||
m_Ending = false;
|
||||
m_Effect = effect;
|
||||
m_Mods = mods;
|
||||
|
||||
Apply();
|
||||
}
|
||||
|
||||
public void Apply()
|
||||
{
|
||||
for (int i = 0; i < m_Mods.Count; ++i)
|
||||
public static TimeSpan OnUse(Mobile m)
|
||||
{
|
||||
object mod = m_Mods[i];
|
||||
m.RevealingAction();
|
||||
|
||||
if (mod is ResistanceMod resistanceMod)
|
||||
m_Creature.AddResistanceMod(resistanceMod);
|
||||
else if (mod is StatMod statMod)
|
||||
m_Creature.AddStatMod(statMod);
|
||||
else if (mod is SkillMod skillMod)
|
||||
m_Creature.AddSkillMod(skillMod);
|
||||
BaseInstrument.PickInstrument(m, OnPickedInstrument);
|
||||
|
||||
return TimeSpan.FromSeconds(1.0); // Cannot use another skill for 1 second
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
for (int i = 0; i < m_Mods.Count; ++i)
|
||||
public static void OnPickedInstrument(Mobile from, BaseInstrument instrument)
|
||||
{
|
||||
object mod = m_Mods[i];
|
||||
|
||||
if (mod is ResistanceMod resistanceMod)
|
||||
m_Creature.RemoveResistanceMod(resistanceMod);
|
||||
else if (mod is StatMod statMod)
|
||||
m_Creature.RemoveStatMod(statMod.Name);
|
||||
else if (mod is SkillMod skillMod)
|
||||
m_Creature.RemoveSkillMod(skillMod);
|
||||
from.RevealingAction();
|
||||
from.SendLocalizedMessage(1049541); // Choose the target for your song of discordance.
|
||||
from.Target = new DiscordanceTarget(from, instrument);
|
||||
from.NextSkillTime = Core.TickCount + 6000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class DiscordanceTarget : Target
|
||||
{
|
||||
private readonly BaseInstrument m_Instrument;
|
||||
|
||||
public DiscordanceTarget(Mobile from, BaseInstrument inst) : base(
|
||||
BaseInstrument.GetBardRange(from, SkillName.Discordance), false, TargetFlags.None) =>
|
||||
m_Instrument = inst;
|
||||
|
||||
protected override void OnTarget(Mobile from, object target)
|
||||
{
|
||||
from.RevealingAction();
|
||||
from.NextSkillTime = Core.TickCount + 1000;
|
||||
|
||||
if (!m_Instrument.IsChildOf(from.Backpack))
|
||||
public static bool GetEffect(Mobile targ, ref int effect)
|
||||
{
|
||||
from.SendLocalizedMessage(
|
||||
1062488); // The instrument you are trying to play is no longer in your backpack!
|
||||
if (!m_Table.TryGetValue(targ, out var info))
|
||||
return false;
|
||||
|
||||
effect = info.m_Effect;
|
||||
return true;
|
||||
}
|
||||
else if (target is Mobile targ)
|
||||
|
||||
private static void ProcessDiscordance(DiscordanceInfo info)
|
||||
{
|
||||
if (targ == from || (targ is BaseCreature bc &&
|
||||
(bc.BardImmune || !from.CanBeHarmful(bc, false)) &&
|
||||
bc.ControlMaster != from))
|
||||
{
|
||||
from.SendLocalizedMessage(1049535); // A song of discord would have no effect on that.
|
||||
}
|
||||
else if (m_Table.ContainsKey(targ)) // Already discorded
|
||||
{
|
||||
from.SendLocalizedMessage(1049537); // Your target is already in discord.
|
||||
}
|
||||
else if (!targ.Player)
|
||||
{
|
||||
double diff = m_Instrument.GetDifficultyFor(targ) - 10.0;
|
||||
double music = from.Skills.Musicianship.Value;
|
||||
var from = info.m_From;
|
||||
var targ = info.m_Creature;
|
||||
var ends = false;
|
||||
|
||||
if (music > 100.0)
|
||||
diff -= (music - 100.0) * 0.5;
|
||||
|
||||
if (!BaseInstrument.CheckMusicianship(from))
|
||||
// According to uoherald bard must remain alive, visible, and
|
||||
// within range of the target or the effect ends in 15 seconds.
|
||||
if (!targ.Alive || targ.Deleted || !from.Alive || from.Hidden)
|
||||
{
|
||||
from.SendLocalizedMessage(500612); // You play poorly, and there is no effect.
|
||||
m_Instrument.PlayInstrumentBadly(from);
|
||||
m_Instrument.ConsumeUse(from);
|
||||
}
|
||||
else if (from.CheckTargetSkill(SkillName.Discordance, targ, diff - 25.0, diff + 25.0))
|
||||
{
|
||||
from.SendLocalizedMessage(1049539); // You play the song surpressing your targets strength
|
||||
m_Instrument.PlayInstrumentWell(from);
|
||||
m_Instrument.ConsumeUse(from);
|
||||
|
||||
List<object> mods = new List<object>();
|
||||
int effect;
|
||||
double scalar;
|
||||
|
||||
if (Core.AOS)
|
||||
{
|
||||
double discord = from.Skills.Discordance.Value;
|
||||
|
||||
if (discord > 100.0)
|
||||
effect = -20 + (int)((discord - 100.0) / -2.5);
|
||||
else
|
||||
effect = (int)(discord / -5.0);
|
||||
|
||||
if (Core.SE && BaseInstrument.GetBaseDifficulty(targ) >= 160.0)
|
||||
effect /= 2;
|
||||
|
||||
scalar = effect * 0.01;
|
||||
|
||||
mods.Add(new ResistanceMod(ResistanceType.Physical, effect));
|
||||
mods.Add(new ResistanceMod(ResistanceType.Fire, effect));
|
||||
mods.Add(new ResistanceMod(ResistanceType.Cold, effect));
|
||||
mods.Add(new ResistanceMod(ResistanceType.Poison, effect));
|
||||
mods.Add(new ResistanceMod(ResistanceType.Energy, effect));
|
||||
|
||||
for (int i = 0; i < targ.Skills.Length; ++i)
|
||||
if (targ.Skills[i].Value > 0)
|
||||
mods.Add(new DefaultSkillMod((SkillName)i, true, targ.Skills[i].Value * scalar));
|
||||
}
|
||||
else
|
||||
{
|
||||
effect = (int)(from.Skills.Discordance.Value / -5.0);
|
||||
scalar = effect * 0.01;
|
||||
|
||||
mods.Add(new StatMod(StatType.Str, "DiscordanceStr", (int)(targ.RawStr * scalar),
|
||||
TimeSpan.Zero));
|
||||
mods.Add(new StatMod(StatType.Int, "DiscordanceInt", (int)(targ.RawInt * scalar),
|
||||
TimeSpan.Zero));
|
||||
mods.Add(new StatMod(StatType.Dex, "DiscordanceDex", (int)(targ.RawDex * scalar),
|
||||
TimeSpan.Zero));
|
||||
|
||||
for (int i = 0; i < targ.Skills.Length; ++i)
|
||||
if (targ.Skills[i].Value > 0)
|
||||
mods.Add(new DefaultSkillMod((SkillName)i, true, targ.Skills[i].Value * scalar));
|
||||
}
|
||||
|
||||
DiscordanceInfo info = new DiscordanceInfo(from, targ, Math.Abs(effect), mods);
|
||||
info.m_Timer = Timer.DelayCall(TimeSpan.Zero, TimeSpan.FromSeconds(1.25), ProcessDiscordance,
|
||||
info);
|
||||
|
||||
m_Table[targ] = info;
|
||||
ends = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(1049540); // You fail to disrupt your target
|
||||
m_Instrument.PlayInstrumentBadly(from);
|
||||
m_Instrument.ConsumeUse(from);
|
||||
var range = (int)targ.GetDistanceToSqrt(from);
|
||||
var maxRange = BaseInstrument.GetBardRange(from, SkillName.Discordance);
|
||||
|
||||
if (from.Map != targ.Map || range > maxRange)
|
||||
ends = true;
|
||||
}
|
||||
|
||||
from.NextSkillTime = Core.TickCount + 12000;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Instrument.PlayInstrumentBadly(from);
|
||||
}
|
||||
if (ends && info.m_Ending && info.m_EndTime < DateTime.UtcNow)
|
||||
{
|
||||
info.m_Timer?.Stop();
|
||||
|
||||
info.Clear();
|
||||
m_Table.Remove(targ);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ends && !info.m_Ending)
|
||||
{
|
||||
info.m_Ending = true;
|
||||
info.m_EndTime = DateTime.UtcNow + TimeSpan.FromSeconds(15);
|
||||
}
|
||||
else if (!ends)
|
||||
{
|
||||
info.m_Ending = false;
|
||||
info.m_EndTime = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
targ.FixedEffect(0x376A, 1, 32);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
private class DiscordanceInfo
|
||||
{
|
||||
from.SendLocalizedMessage(1049535); // A song of discord would have no effect on that.
|
||||
public readonly Mobile m_Creature;
|
||||
public readonly int m_Effect;
|
||||
public readonly Mobile m_From;
|
||||
public readonly List<object> m_Mods;
|
||||
public bool m_Ending;
|
||||
public DateTime m_EndTime;
|
||||
public Timer m_Timer;
|
||||
|
||||
public DiscordanceInfo(Mobile from, Mobile creature, int effect, List<object> mods)
|
||||
{
|
||||
m_From = from;
|
||||
m_Creature = creature;
|
||||
m_EndTime = DateTime.UtcNow;
|
||||
m_Ending = false;
|
||||
m_Effect = effect;
|
||||
m_Mods = mods;
|
||||
|
||||
Apply();
|
||||
}
|
||||
|
||||
public void Apply()
|
||||
{
|
||||
for (var i = 0; i < m_Mods.Count; ++i)
|
||||
{
|
||||
var mod = m_Mods[i];
|
||||
|
||||
if (mod is ResistanceMod resistanceMod)
|
||||
m_Creature.AddResistanceMod(resistanceMod);
|
||||
else if (mod is StatMod statMod)
|
||||
m_Creature.AddStatMod(statMod);
|
||||
else if (mod is SkillMod skillMod)
|
||||
m_Creature.AddSkillMod(skillMod);
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
for (var i = 0; i < m_Mods.Count; ++i)
|
||||
{
|
||||
var mod = m_Mods[i];
|
||||
|
||||
if (mod is ResistanceMod resistanceMod)
|
||||
m_Creature.RemoveResistanceMod(resistanceMod);
|
||||
else if (mod is StatMod statMod)
|
||||
m_Creature.RemoveStatMod(statMod.Name);
|
||||
else if (mod is SkillMod skillMod)
|
||||
m_Creature.RemoveSkillMod(skillMod);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class DiscordanceTarget : Target
|
||||
{
|
||||
private readonly BaseInstrument m_Instrument;
|
||||
|
||||
public DiscordanceTarget(Mobile from, BaseInstrument inst) : base(
|
||||
BaseInstrument.GetBardRange(from, SkillName.Discordance),
|
||||
false,
|
||||
TargetFlags.None
|
||||
) =>
|
||||
m_Instrument = inst;
|
||||
|
||||
protected override void OnTarget(Mobile from, object target)
|
||||
{
|
||||
from.RevealingAction();
|
||||
from.NextSkillTime = Core.TickCount + 1000;
|
||||
|
||||
if (!m_Instrument.IsChildOf(from.Backpack))
|
||||
{
|
||||
from.SendLocalizedMessage(
|
||||
1062488
|
||||
); // The instrument you are trying to play is no longer in your backpack!
|
||||
}
|
||||
else if (target is Mobile targ)
|
||||
{
|
||||
if (targ == from || targ is BaseCreature bc &&
|
||||
(bc.BardImmune || !@from.CanBeHarmful(bc, false)) &&
|
||||
bc.ControlMaster != @from)
|
||||
{
|
||||
from.SendLocalizedMessage(1049535); // A song of discord would have no effect on that.
|
||||
}
|
||||
else if (m_Table.ContainsKey(targ)) // Already discorded
|
||||
{
|
||||
from.SendLocalizedMessage(1049537); // Your target is already in discord.
|
||||
}
|
||||
else if (!targ.Player)
|
||||
{
|
||||
var diff = m_Instrument.GetDifficultyFor(targ) - 10.0;
|
||||
var music = from.Skills.Musicianship.Value;
|
||||
|
||||
if (music > 100.0)
|
||||
diff -= (music - 100.0) * 0.5;
|
||||
|
||||
if (!BaseInstrument.CheckMusicianship(from))
|
||||
{
|
||||
from.SendLocalizedMessage(500612); // You play poorly, and there is no effect.
|
||||
m_Instrument.PlayInstrumentBadly(from);
|
||||
m_Instrument.ConsumeUse(from);
|
||||
}
|
||||
else if (from.CheckTargetSkill(SkillName.Discordance, targ, diff - 25.0, diff + 25.0))
|
||||
{
|
||||
from.SendLocalizedMessage(1049539); // You play the song surpressing your targets strength
|
||||
m_Instrument.PlayInstrumentWell(from);
|
||||
m_Instrument.ConsumeUse(from);
|
||||
|
||||
var mods = new List<object>();
|
||||
int effect;
|
||||
double scalar;
|
||||
|
||||
if (Core.AOS)
|
||||
{
|
||||
var discord = from.Skills.Discordance.Value;
|
||||
|
||||
if (discord > 100.0)
|
||||
effect = -20 + (int)((discord - 100.0) / -2.5);
|
||||
else
|
||||
effect = (int)(discord / -5.0);
|
||||
|
||||
if (Core.SE && BaseInstrument.GetBaseDifficulty(targ) >= 160.0)
|
||||
effect /= 2;
|
||||
|
||||
scalar = effect * 0.01;
|
||||
|
||||
mods.Add(new ResistanceMod(ResistanceType.Physical, effect));
|
||||
mods.Add(new ResistanceMod(ResistanceType.Fire, effect));
|
||||
mods.Add(new ResistanceMod(ResistanceType.Cold, effect));
|
||||
mods.Add(new ResistanceMod(ResistanceType.Poison, effect));
|
||||
mods.Add(new ResistanceMod(ResistanceType.Energy, effect));
|
||||
|
||||
for (var i = 0; i < targ.Skills.Length; ++i)
|
||||
if (targ.Skills[i].Value > 0)
|
||||
mods.Add(new DefaultSkillMod((SkillName)i, true, targ.Skills[i].Value * scalar));
|
||||
}
|
||||
else
|
||||
{
|
||||
effect = (int)(from.Skills.Discordance.Value / -5.0);
|
||||
scalar = effect * 0.01;
|
||||
|
||||
mods.Add(
|
||||
new StatMod(
|
||||
StatType.Str,
|
||||
"DiscordanceStr",
|
||||
(int)(targ.RawStr * scalar),
|
||||
TimeSpan.Zero
|
||||
)
|
||||
);
|
||||
mods.Add(
|
||||
new StatMod(
|
||||
StatType.Int,
|
||||
"DiscordanceInt",
|
||||
(int)(targ.RawInt * scalar),
|
||||
TimeSpan.Zero
|
||||
)
|
||||
);
|
||||
mods.Add(
|
||||
new StatMod(
|
||||
StatType.Dex,
|
||||
"DiscordanceDex",
|
||||
(int)(targ.RawDex * scalar),
|
||||
TimeSpan.Zero
|
||||
)
|
||||
);
|
||||
|
||||
for (var i = 0; i < targ.Skills.Length; ++i)
|
||||
if (targ.Skills[i].Value > 0)
|
||||
mods.Add(new DefaultSkillMod((SkillName)i, true, targ.Skills[i].Value * scalar));
|
||||
}
|
||||
|
||||
var info = new DiscordanceInfo(from, targ, Math.Abs(effect), mods);
|
||||
info.m_Timer = Timer.DelayCall(
|
||||
TimeSpan.Zero,
|
||||
TimeSpan.FromSeconds(1.25),
|
||||
ProcessDiscordance,
|
||||
info
|
||||
);
|
||||
|
||||
m_Table[targ] = info;
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(1049540); // You fail to disrupt your target
|
||||
m_Instrument.PlayInstrumentBadly(from);
|
||||
m_Instrument.ConsumeUse(from);
|
||||
}
|
||||
|
||||
from.NextSkillTime = Core.TickCount + 12000;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Instrument.PlayInstrumentBadly(from);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(1049535); // A song of discord would have no effect on that.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,83 +5,106 @@ using Server.Targeting;
|
|||
|
||||
namespace Server.SkillHandlers
|
||||
{
|
||||
public static class EvalInt
|
||||
{
|
||||
public static void Initialize()
|
||||
public static class EvalInt
|
||||
{
|
||||
SkillInfo.Table[16].Callback = OnUse;
|
||||
public static void Initialize()
|
||||
{
|
||||
SkillInfo.Table[16].Callback = OnUse;
|
||||
}
|
||||
|
||||
public static TimeSpan OnUse(Mobile m)
|
||||
{
|
||||
m.Target = new InternalTarget();
|
||||
|
||||
m.SendLocalizedMessage(500906); // What do you wish to evaluate?
|
||||
|
||||
return TimeSpan.FromSeconds(1.0);
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
public InternalTarget() : base(8, false, TargetFlags.None)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnTarget(Mobile from, object targeted)
|
||||
{
|
||||
if (from == targeted)
|
||||
{
|
||||
from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 500910); // Hmm, that person looks really silly.
|
||||
}
|
||||
else if (targeted is TownCrier crier)
|
||||
{
|
||||
crier.PrivateOverheadMessage(
|
||||
MessageType.Regular,
|
||||
0x3B2,
|
||||
500907,
|
||||
from.NetState
|
||||
); // He looks smart enough to remember the news. Ask him about it.
|
||||
}
|
||||
else if (targeted is BaseVendor vendor && vendor.IsInvulnerable)
|
||||
{
|
||||
vendor.PrivateOverheadMessage(
|
||||
MessageType.Regular,
|
||||
0x3B2,
|
||||
500909,
|
||||
from.NetState
|
||||
); // That person could probably calculate the cost of what you buy from them.
|
||||
}
|
||||
else if (targeted is Mobile targ)
|
||||
{
|
||||
var marginOfError = Math.Max(0, 20 - (int)(from.Skills.EvalInt.Value / 5));
|
||||
|
||||
var intel = targ.Int + Utility.RandomMinMax(-marginOfError, +marginOfError);
|
||||
var mana = targ.Mana * 100 / Math.Max(targ.ManaMax, 1) +
|
||||
Utility.RandomMinMax(-marginOfError, +marginOfError);
|
||||
|
||||
var intMod = Math.Clamp(intel / 10, 0, 10);
|
||||
var mnMod = Math.Clamp(mana / 10, 0, 10);
|
||||
|
||||
int body;
|
||||
|
||||
if (targ.Body.IsHuman)
|
||||
body = targ.Female ? 11 : 0;
|
||||
else
|
||||
body = 22;
|
||||
|
||||
if (from.CheckTargetSkill(SkillName.EvalInt, targ, 0.0, 120.0))
|
||||
{
|
||||
targ.PrivateOverheadMessage(
|
||||
MessageType.Regular,
|
||||
0x3B2,
|
||||
1038169 + intMod + body,
|
||||
from.NetState
|
||||
); // He/She/It looks [slighly less intelligent than a rock.] [Of Average intellect] [etc...]
|
||||
|
||||
if (from.Skills.EvalInt.Base >= 76.0)
|
||||
targ.PrivateOverheadMessage(
|
||||
MessageType.Regular,
|
||||
0x3B2,
|
||||
1038202 + mnMod,
|
||||
from.NetState
|
||||
); // That being is at [10,20,...] percent mental strength.
|
||||
}
|
||||
else
|
||||
{
|
||||
targ.PrivateOverheadMessage(
|
||||
MessageType.Regular,
|
||||
0x3B2,
|
||||
1038166 + body / 11,
|
||||
from.NetState
|
||||
); // You cannot judge his/her/its mental abilities.
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
(targeted as Item)?.SendLocalizedMessageTo(
|
||||
from,
|
||||
500908,
|
||||
""
|
||||
); // It looks smarter than a rock, but dumber than a piece of wood.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static TimeSpan OnUse(Mobile m)
|
||||
{
|
||||
m.Target = new InternalTarget();
|
||||
|
||||
m.SendLocalizedMessage(500906); // What do you wish to evaluate?
|
||||
|
||||
return TimeSpan.FromSeconds(1.0);
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
public InternalTarget() : base(8, false, TargetFlags.None)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnTarget(Mobile from, object targeted)
|
||||
{
|
||||
if (from == targeted)
|
||||
{
|
||||
from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 500910); // Hmm, that person looks really silly.
|
||||
}
|
||||
else if (targeted is TownCrier crier)
|
||||
{
|
||||
crier.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 500907,
|
||||
from.NetState); // He looks smart enough to remember the news. Ask him about it.
|
||||
}
|
||||
else if (targeted is BaseVendor vendor && vendor.IsInvulnerable)
|
||||
{
|
||||
vendor.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 500909,
|
||||
from.NetState); // That person could probably calculate the cost of what you buy from them.
|
||||
}
|
||||
else if (targeted is Mobile targ)
|
||||
{
|
||||
int marginOfError = Math.Max(0, 20 - (int)(from.Skills.EvalInt.Value / 5));
|
||||
|
||||
int intel = targ.Int + Utility.RandomMinMax(-marginOfError, +marginOfError);
|
||||
int mana = targ.Mana * 100 / Math.Max(targ.ManaMax, 1) +
|
||||
Utility.RandomMinMax(-marginOfError, +marginOfError);
|
||||
|
||||
int intMod = Math.Clamp(intel / 10, 0, 10);
|
||||
int mnMod = Math.Clamp(mana / 10, 0, 10);
|
||||
|
||||
int body;
|
||||
|
||||
if (targ.Body.IsHuman)
|
||||
body = targ.Female ? 11 : 0;
|
||||
else
|
||||
body = 22;
|
||||
|
||||
if (from.CheckTargetSkill(SkillName.EvalInt, targ, 0.0, 120.0))
|
||||
{
|
||||
targ.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 1038169 + intMod + body,
|
||||
from.NetState); // He/She/It looks [slighly less intelligent than a rock.] [Of Average intellect] [etc...]
|
||||
|
||||
if (from.Skills.EvalInt.Base >= 76.0)
|
||||
targ.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 1038202 + mnMod,
|
||||
from.NetState); // That being is at [10,20,...] percent mental strength.
|
||||
}
|
||||
else
|
||||
{
|
||||
targ.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 1038166 + body / 11,
|
||||
from.NetState); // You cannot judge his/her/its mental abilities.
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
(targeted as Item)?.SendLocalizedMessageTo(from, 500908,
|
||||
""); // It looks smarter than a rock, but dumber than a piece of wood.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,90 +6,96 @@ using Server.Targeting;
|
|||
|
||||
namespace Server.SkillHandlers
|
||||
{
|
||||
public static class ForensicEvaluation
|
||||
{
|
||||
public static void Initialize()
|
||||
public static class ForensicEvaluation
|
||||
{
|
||||
SkillInfo.Table[(int)SkillName.Forensics].Callback = OnUse;
|
||||
}
|
||||
|
||||
public static TimeSpan OnUse(Mobile m)
|
||||
{
|
||||
m.Target = new ForensicTarget();
|
||||
m.RevealingAction();
|
||||
|
||||
m.SendLocalizedMessage(501000); // Show me the crime.
|
||||
|
||||
return TimeSpan.FromSeconds(1.0);
|
||||
}
|
||||
|
||||
public class ForensicTarget : Target
|
||||
{
|
||||
public ForensicTarget() : base(10, false, TargetFlags.None)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnTarget(Mobile from, object target)
|
||||
{
|
||||
if (target is Mobile)
|
||||
public static void Initialize()
|
||||
{
|
||||
if (from.CheckTargetSkill(SkillName.Forensics, target, 40.0, 100.0))
|
||||
{
|
||||
if (target is PlayerMobile pm && pm.NpcGuild == NpcGuild.ThievesGuild)
|
||||
from.SendLocalizedMessage(501004); // That individual is a thief!
|
||||
else
|
||||
from.SendLocalizedMessage(501003); // You notice nothing unusual.
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(501001); // You cannot determain anything useful.
|
||||
}
|
||||
SkillInfo.Table[(int)SkillName.Forensics].Callback = OnUse;
|
||||
}
|
||||
else if (target is Corpse c)
|
||||
|
||||
public static TimeSpan OnUse(Mobile m)
|
||||
{
|
||||
if (from.CheckTargetSkill(SkillName.Forensics, c, 0.0, 100.0))
|
||||
{
|
||||
if (c.m_Forensicist != null)
|
||||
from.SendLocalizedMessage(1042750,
|
||||
c.m_Forensicist); // The forensicist ~1_NAME~ has already discovered that:
|
||||
else
|
||||
c.m_Forensicist = from.Name;
|
||||
m.Target = new ForensicTarget();
|
||||
m.RevealingAction();
|
||||
|
||||
if (((Body)c.Amount).IsHuman)
|
||||
from.SendLocalizedMessage(1042751,
|
||||
c.Killer == null ? "no one" : c.Killer.Name); // This person was killed by ~1_KILLER_NAME~
|
||||
m.SendLocalizedMessage(501000); // Show me the crime.
|
||||
|
||||
if (c.Looters.Count > 0)
|
||||
return TimeSpan.FromSeconds(1.0);
|
||||
}
|
||||
|
||||
public class ForensicTarget : Target
|
||||
{
|
||||
public ForensicTarget() : base(10, false, TargetFlags.None)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < c.Looters.Count; i++)
|
||||
{
|
||||
if (i > 0)
|
||||
sb.Append(", ");
|
||||
sb.Append(c.Looters[i].Name);
|
||||
}
|
||||
}
|
||||
|
||||
from.SendLocalizedMessage(1042752,
|
||||
sb.ToString()); // This body has been distrubed by ~1_PLAYER_NAMES~
|
||||
}
|
||||
else
|
||||
protected override void OnTarget(Mobile from, object target)
|
||||
{
|
||||
from.SendLocalizedMessage(501002); // The corpse has not be desecrated.
|
||||
if (target is Mobile)
|
||||
{
|
||||
if (from.CheckTargetSkill(SkillName.Forensics, target, 40.0, 100.0))
|
||||
{
|
||||
if (target is PlayerMobile pm && pm.NpcGuild == NpcGuild.ThievesGuild)
|
||||
from.SendLocalizedMessage(501004); // That individual is a thief!
|
||||
else
|
||||
from.SendLocalizedMessage(501003); // You notice nothing unusual.
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(501001); // You cannot determain anything useful.
|
||||
}
|
||||
}
|
||||
else if (target is Corpse c)
|
||||
{
|
||||
if (from.CheckTargetSkill(SkillName.Forensics, c, 0.0, 100.0))
|
||||
{
|
||||
if (c.m_Forensicist != null)
|
||||
from.SendLocalizedMessage(
|
||||
1042750,
|
||||
c.m_Forensicist
|
||||
); // The forensicist ~1_NAME~ has already discovered that:
|
||||
else
|
||||
c.m_Forensicist = from.Name;
|
||||
|
||||
if (((Body)c.Amount).IsHuman)
|
||||
from.SendLocalizedMessage(
|
||||
1042751,
|
||||
c.Killer == null ? "no one" : c.Killer.Name
|
||||
); // This person was killed by ~1_KILLER_NAME~
|
||||
|
||||
if (c.Looters.Count > 0)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
for (var i = 0; i < c.Looters.Count; i++)
|
||||
{
|
||||
if (i > 0)
|
||||
sb.Append(", ");
|
||||
sb.Append(c.Looters[i].Name);
|
||||
}
|
||||
|
||||
from.SendLocalizedMessage(
|
||||
1042752,
|
||||
sb.ToString()
|
||||
); // This body has been distrubed by ~1_PLAYER_NAMES~
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(501002); // The corpse has not be desecrated.
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(501001); // You cannot determain anything useful.
|
||||
}
|
||||
}
|
||||
else if (target is ILockpickable p)
|
||||
{
|
||||
if (p.Picker != null)
|
||||
from.SendLocalizedMessage(1042749, p.Picker.Name); // This lock was opened by ~1_PICKER_NAME~
|
||||
else
|
||||
from.SendLocalizedMessage(501003); // You notice nothing unusual.
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(501001); // You cannot determain anything useful.
|
||||
}
|
||||
}
|
||||
else if (target is ILockpickable p)
|
||||
{
|
||||
if (p.Picker != null)
|
||||
from.SendLocalizedMessage(1042749, p.Picker.Name); // This lock was opened by ~1_PICKER_NAME~
|
||||
else
|
||||
from.SendLocalizedMessage(501003); // You notice nothing unusual.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,82 +6,86 @@ using Server.Targeting;
|
|||
|
||||
namespace Server.SkillHandlers
|
||||
{
|
||||
public static class Hiding
|
||||
{
|
||||
public static bool CombatOverride { get; set; }
|
||||
|
||||
public static void Initialize()
|
||||
public static class Hiding
|
||||
{
|
||||
SkillInfo.Table[21].Callback = OnUse;
|
||||
public static bool CombatOverride { get; set; }
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
SkillInfo.Table[21].Callback = OnUse;
|
||||
}
|
||||
|
||||
public static TimeSpan OnUse(Mobile m)
|
||||
{
|
||||
if (m.Spell != null)
|
||||
{
|
||||
m.SendLocalizedMessage(501238); // You are busy doing something else and cannot hide.
|
||||
return TimeSpan.FromSeconds(1.0);
|
||||
}
|
||||
|
||||
if (Core.ML && m.Target != null) Target.Cancel(m);
|
||||
|
||||
var bonus = 0.0;
|
||||
|
||||
var house = BaseHouse.FindHouseAt(m);
|
||||
|
||||
if (house?.IsFriend(m) == true)
|
||||
{
|
||||
bonus = 100.0;
|
||||
}
|
||||
else if (!Core.AOS)
|
||||
{
|
||||
house ??= BaseHouse.FindHouseAt(new Point3D(m.X - 1, m.Y, 127), m.Map, 16) ??
|
||||
BaseHouse.FindHouseAt(new Point3D(m.X + 1, m.Y, 127), m.Map, 16) ??
|
||||
BaseHouse.FindHouseAt(new Point3D(m.X, m.Y - 1, 127), m.Map, 16) ??
|
||||
BaseHouse.FindHouseAt(new Point3D(m.X, m.Y + 1, 127), m.Map, 16);
|
||||
|
||||
if (house != null)
|
||||
bonus = 50.0;
|
||||
}
|
||||
|
||||
// int range = 18 - (int)(m.Skills.Hiding.Value / 10);
|
||||
var range = Math.Min(
|
||||
(int)((100 - m.Skills.Hiding.Value) / 2) + 8,
|
||||
18
|
||||
); // Cap of 18 not OSI-exact, intentional difference
|
||||
|
||||
var badCombat = !CombatOverride && m.Combatant != null && m.InRange(m.Combatant.Location, range) &&
|
||||
m.Combatant.InLOS(m);
|
||||
var ok = !badCombat;
|
||||
|
||||
if (ok)
|
||||
{
|
||||
if (!CombatOverride)
|
||||
if (m.GetMobilesInRange(range).Any(check => check.InLOS(m) && check.Combatant == m))
|
||||
badCombat = true;
|
||||
|
||||
ok = !badCombat && m.CheckSkill(SkillName.Hiding, 0.0 - bonus, 100.0 - bonus);
|
||||
}
|
||||
|
||||
if (badCombat)
|
||||
{
|
||||
m.RevealingAction();
|
||||
|
||||
m.LocalOverheadMessage(MessageType.Regular, 0x22, 501237); // You can't seem to hide right now.
|
||||
|
||||
return TimeSpan.FromSeconds(1.0);
|
||||
}
|
||||
|
||||
if (ok)
|
||||
{
|
||||
m.Hidden = true;
|
||||
m.Warmode = false;
|
||||
m.LocalOverheadMessage(MessageType.Regular, 0x1F4, 501240); // You have hidden yourself well.
|
||||
}
|
||||
else
|
||||
{
|
||||
m.RevealingAction();
|
||||
|
||||
m.LocalOverheadMessage(MessageType.Regular, 0x22, 501241); // You can't seem to hide here.
|
||||
}
|
||||
|
||||
return TimeSpan.FromSeconds(10.0);
|
||||
}
|
||||
}
|
||||
|
||||
public static TimeSpan OnUse(Mobile m)
|
||||
{
|
||||
if (m.Spell != null)
|
||||
{
|
||||
m.SendLocalizedMessage(501238); // You are busy doing something else and cannot hide.
|
||||
return TimeSpan.FromSeconds(1.0);
|
||||
}
|
||||
|
||||
if (Core.ML && m.Target != null) Target.Cancel(m);
|
||||
|
||||
double bonus = 0.0;
|
||||
|
||||
BaseHouse house = BaseHouse.FindHouseAt(m);
|
||||
|
||||
if (house?.IsFriend(m) == true)
|
||||
bonus = 100.0;
|
||||
else if (!Core.AOS)
|
||||
{
|
||||
house ??= BaseHouse.FindHouseAt(new Point3D(m.X - 1, m.Y, 127), m.Map, 16) ??
|
||||
BaseHouse.FindHouseAt(new Point3D(m.X + 1, m.Y, 127), m.Map, 16) ??
|
||||
BaseHouse.FindHouseAt(new Point3D(m.X, m.Y - 1, 127), m.Map, 16) ??
|
||||
BaseHouse.FindHouseAt(new Point3D(m.X, m.Y + 1, 127), m.Map, 16);
|
||||
|
||||
if (house != null)
|
||||
bonus = 50.0;
|
||||
}
|
||||
|
||||
// int range = 18 - (int)(m.Skills.Hiding.Value / 10);
|
||||
int range = Math.Min((int)((100 - m.Skills.Hiding.Value) / 2) + 8,
|
||||
18); // Cap of 18 not OSI-exact, intentional difference
|
||||
|
||||
bool badCombat = !CombatOverride && m.Combatant != null && m.InRange(m.Combatant.Location, range) &&
|
||||
m.Combatant.InLOS(m);
|
||||
bool ok = !badCombat;
|
||||
|
||||
if (ok)
|
||||
{
|
||||
if (!CombatOverride)
|
||||
if (m.GetMobilesInRange(range).Any(check => check.InLOS(m) && check.Combatant == m))
|
||||
badCombat = true;
|
||||
|
||||
ok = !badCombat && m.CheckSkill(SkillName.Hiding, 0.0 - bonus, 100.0 - bonus);
|
||||
}
|
||||
|
||||
if (badCombat)
|
||||
{
|
||||
m.RevealingAction();
|
||||
|
||||
m.LocalOverheadMessage(MessageType.Regular, 0x22, 501237); // You can't seem to hide right now.
|
||||
|
||||
return TimeSpan.FromSeconds(1.0);
|
||||
}
|
||||
|
||||
if (ok)
|
||||
{
|
||||
m.Hidden = true;
|
||||
m.Warmode = false;
|
||||
m.LocalOverheadMessage(MessageType.Regular, 0x1F4, 501240); // You have hidden yourself well.
|
||||
}
|
||||
else
|
||||
{
|
||||
m.RevealingAction();
|
||||
|
||||
m.LocalOverheadMessage(MessageType.Regular, 0x22, 501241); // You can't seem to hide here.
|
||||
}
|
||||
|
||||
return TimeSpan.FromSeconds(10.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,162 +6,164 @@ using Server.Targeting;
|
|||
|
||||
namespace Server.SkillHandlers
|
||||
{
|
||||
public static class Inscribe
|
||||
{
|
||||
private static readonly Dictionary<BaseBook, Mobile> m_UseTable = new Dictionary<BaseBook, Mobile>();
|
||||
|
||||
public static void Initialize()
|
||||
public static class Inscribe
|
||||
{
|
||||
SkillInfo.Table[(int)SkillName.Inscribe].Callback = OnUse;
|
||||
private static readonly Dictionary<BaseBook, Mobile> m_UseTable = new Dictionary<BaseBook, Mobile>();
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
SkillInfo.Table[(int)SkillName.Inscribe].Callback = OnUse;
|
||||
}
|
||||
|
||||
public static TimeSpan OnUse(Mobile m)
|
||||
{
|
||||
Target target = new InternalTargetSrc();
|
||||
m.Target = target;
|
||||
m.SendLocalizedMessage(1046295); // Target the book you wish to copy.
|
||||
target.BeginTimeout(m, TimeSpan.FromMinutes(1.0));
|
||||
|
||||
return TimeSpan.FromSeconds(1.0);
|
||||
}
|
||||
|
||||
private static void SetUser(BaseBook book, Mobile mob)
|
||||
{
|
||||
m_UseTable[book] = mob;
|
||||
}
|
||||
|
||||
private static void CancelUser(BaseBook book)
|
||||
{
|
||||
m_UseTable.Remove(book);
|
||||
}
|
||||
|
||||
public static Mobile GetUser(BaseBook book)
|
||||
{
|
||||
m_UseTable.TryGetValue(book, out var m);
|
||||
return m;
|
||||
}
|
||||
|
||||
public static bool IsEmpty(BaseBook book)
|
||||
{
|
||||
return book.Pages.SelectMany(page => page.Lines).All(line => line.Trim().Length == 0);
|
||||
}
|
||||
|
||||
public static void Copy(BaseBook bookSrc, BaseBook bookDst)
|
||||
{
|
||||
bookDst.Title = bookSrc.Title;
|
||||
bookDst.Author = bookSrc.Author;
|
||||
|
||||
var pagesSrc = bookSrc.Pages;
|
||||
var pagesDst = bookDst.Pages;
|
||||
for (var i = 0; i < pagesSrc.Length && i < pagesDst.Length; i++)
|
||||
{
|
||||
var pageSrc = pagesSrc[i];
|
||||
var pageDst = pagesDst[i];
|
||||
|
||||
var length = pageSrc.Lines.Length;
|
||||
pageDst.Lines = new string[length];
|
||||
|
||||
for (var j = 0; j < length; j++)
|
||||
pageDst.Lines[j] = pageSrc.Lines[j];
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalTargetSrc : Target
|
||||
{
|
||||
public InternalTargetSrc() : base(3, false, TargetFlags.None)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnTarget(Mobile from, object targeted)
|
||||
{
|
||||
if (!(targeted is BaseBook book))
|
||||
{
|
||||
from.SendLocalizedMessage(1046296); // That is not a book
|
||||
}
|
||||
else if (IsEmpty(book))
|
||||
{
|
||||
from.SendLocalizedMessage(501611); // Can't copy an empty book.
|
||||
}
|
||||
else if (GetUser(book) != null)
|
||||
{
|
||||
from.SendLocalizedMessage(501621); // Someone else is inscribing that item.
|
||||
}
|
||||
else
|
||||
{
|
||||
Target target = new InternalTargetDst(book);
|
||||
from.Target = target;
|
||||
from.SendLocalizedMessage(501612); // Select a book to copy this to.
|
||||
target.BeginTimeout(from, TimeSpan.FromMinutes(1.0));
|
||||
SetUser(book, from);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnTargetCancel(Mobile from, TargetCancelType cancelType)
|
||||
{
|
||||
if (cancelType == TargetCancelType.Timeout)
|
||||
from.SendLocalizedMessage(
|
||||
501619
|
||||
); // You have waited too long to make your inscribe selection, your inscription attempt has timed out.
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalTargetDst : Target
|
||||
{
|
||||
private readonly BaseBook m_BookSrc;
|
||||
|
||||
public InternalTargetDst(BaseBook bookSrc) : base(3, false, TargetFlags.None) => m_BookSrc = bookSrc;
|
||||
|
||||
protected override void OnTarget(Mobile from, object targeted)
|
||||
{
|
||||
if (m_BookSrc.Deleted)
|
||||
return;
|
||||
|
||||
if (!(targeted is BaseBook bookDst))
|
||||
{
|
||||
from.SendLocalizedMessage(1046296); // That is not a book
|
||||
}
|
||||
else if (IsEmpty(m_BookSrc))
|
||||
{
|
||||
from.SendLocalizedMessage(501611); // Can't copy an empty book.
|
||||
}
|
||||
else if (bookDst == m_BookSrc)
|
||||
{
|
||||
from.SendLocalizedMessage(501616); // Cannot copy a book onto itself.
|
||||
}
|
||||
else if (!bookDst.Writable)
|
||||
{
|
||||
from.SendLocalizedMessage(501614); // Cannot write into that book.
|
||||
}
|
||||
else if (GetUser(bookDst) != null)
|
||||
{
|
||||
from.SendLocalizedMessage(501621); // Someone else is inscribing that item.
|
||||
}
|
||||
else
|
||||
{
|
||||
if (from.CheckTargetSkill(SkillName.Inscribe, bookDst, 0, 50))
|
||||
{
|
||||
Copy(m_BookSrc, bookDst);
|
||||
|
||||
from.SendLocalizedMessage(501618); // You make a copy of the book.
|
||||
from.PlaySound(0x249);
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(501617); // You fail to make a copy of the book.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnTargetCancel(Mobile from, TargetCancelType cancelType)
|
||||
{
|
||||
if (cancelType == TargetCancelType.Timeout)
|
||||
from.SendLocalizedMessage(
|
||||
501619
|
||||
); // You have waited too long to make your inscribe selection, your inscription attempt has timed out.
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish(Mobile from)
|
||||
{
|
||||
CancelUser(m_BookSrc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static TimeSpan OnUse(Mobile m)
|
||||
{
|
||||
Target target = new InternalTargetSrc();
|
||||
m.Target = target;
|
||||
m.SendLocalizedMessage(1046295); // Target the book you wish to copy.
|
||||
target.BeginTimeout(m, TimeSpan.FromMinutes(1.0));
|
||||
|
||||
return TimeSpan.FromSeconds(1.0);
|
||||
}
|
||||
|
||||
private static void SetUser(BaseBook book, Mobile mob)
|
||||
{
|
||||
m_UseTable[book] = mob;
|
||||
}
|
||||
|
||||
private static void CancelUser(BaseBook book)
|
||||
{
|
||||
m_UseTable.Remove(book);
|
||||
}
|
||||
|
||||
public static Mobile GetUser(BaseBook book)
|
||||
{
|
||||
m_UseTable.TryGetValue(book, out Mobile m);
|
||||
return m;
|
||||
}
|
||||
|
||||
public static bool IsEmpty(BaseBook book)
|
||||
{
|
||||
return book.Pages.SelectMany(page => page.Lines).All(line => line.Trim().Length == 0);
|
||||
}
|
||||
|
||||
public static void Copy(BaseBook bookSrc, BaseBook bookDst)
|
||||
{
|
||||
bookDst.Title = bookSrc.Title;
|
||||
bookDst.Author = bookSrc.Author;
|
||||
|
||||
BookPageInfo[] pagesSrc = bookSrc.Pages;
|
||||
BookPageInfo[] pagesDst = bookDst.Pages;
|
||||
for (int i = 0; i < pagesSrc.Length && i < pagesDst.Length; i++)
|
||||
{
|
||||
BookPageInfo pageSrc = pagesSrc[i];
|
||||
BookPageInfo pageDst = pagesDst[i];
|
||||
|
||||
int length = pageSrc.Lines.Length;
|
||||
pageDst.Lines = new string[length];
|
||||
|
||||
for (int j = 0; j < length; j++)
|
||||
pageDst.Lines[j] = pageSrc.Lines[j];
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalTargetSrc : Target
|
||||
{
|
||||
public InternalTargetSrc() : base(3, false, TargetFlags.None)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnTarget(Mobile from, object targeted)
|
||||
{
|
||||
if (!(targeted is BaseBook book))
|
||||
{
|
||||
from.SendLocalizedMessage(1046296); // That is not a book
|
||||
}
|
||||
else if (IsEmpty(book))
|
||||
{
|
||||
from.SendLocalizedMessage(501611); // Can't copy an empty book.
|
||||
}
|
||||
else if (GetUser(book) != null)
|
||||
{
|
||||
from.SendLocalizedMessage(501621); // Someone else is inscribing that item.
|
||||
}
|
||||
else
|
||||
{
|
||||
Target target = new InternalTargetDst(book);
|
||||
from.Target = target;
|
||||
from.SendLocalizedMessage(501612); // Select a book to copy this to.
|
||||
target.BeginTimeout(from, TimeSpan.FromMinutes(1.0));
|
||||
SetUser(book, from);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnTargetCancel(Mobile from, TargetCancelType cancelType)
|
||||
{
|
||||
if (cancelType == TargetCancelType.Timeout)
|
||||
from.SendLocalizedMessage(
|
||||
501619); // You have waited too long to make your inscribe selection, your inscription attempt has timed out.
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalTargetDst : Target
|
||||
{
|
||||
private readonly BaseBook m_BookSrc;
|
||||
|
||||
public InternalTargetDst(BaseBook bookSrc) : base(3, false, TargetFlags.None) => m_BookSrc = bookSrc;
|
||||
|
||||
protected override void OnTarget(Mobile from, object targeted)
|
||||
{
|
||||
if (m_BookSrc.Deleted)
|
||||
return;
|
||||
|
||||
if (!(targeted is BaseBook bookDst))
|
||||
{
|
||||
from.SendLocalizedMessage(1046296); // That is not a book
|
||||
}
|
||||
else if (IsEmpty(m_BookSrc))
|
||||
{
|
||||
from.SendLocalizedMessage(501611); // Can't copy an empty book.
|
||||
}
|
||||
else if (bookDst == m_BookSrc)
|
||||
{
|
||||
from.SendLocalizedMessage(501616); // Cannot copy a book onto itself.
|
||||
}
|
||||
else if (!bookDst.Writable)
|
||||
{
|
||||
from.SendLocalizedMessage(501614); // Cannot write into that book.
|
||||
}
|
||||
else if (GetUser(bookDst) != null)
|
||||
{
|
||||
from.SendLocalizedMessage(501621); // Someone else is inscribing that item.
|
||||
}
|
||||
else
|
||||
{
|
||||
if (from.CheckTargetSkill(SkillName.Inscribe, bookDst, 0, 50))
|
||||
{
|
||||
Copy(m_BookSrc, bookDst);
|
||||
|
||||
from.SendLocalizedMessage(501618); // You make a copy of the book.
|
||||
from.PlaySound(0x249);
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(501617); // You fail to make a copy of the book.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnTargetCancel(Mobile from, TargetCancelType cancelType)
|
||||
{
|
||||
if (cancelType == TargetCancelType.Timeout)
|
||||
from.SendLocalizedMessage(
|
||||
501619); // You have waited too long to make your inscribe selection, your inscription attempt has timed out.
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish(Mobile from)
|
||||
{
|
||||
CancelUser(m_BookSrc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,54 +4,54 @@ using Server.Targeting;
|
|||
|
||||
namespace Server.Items
|
||||
{
|
||||
public static class ItemIdentification
|
||||
{
|
||||
public static void Initialize()
|
||||
public static class ItemIdentification
|
||||
{
|
||||
SkillInfo.Table[(int)SkillName.ItemID].Callback = OnUse;
|
||||
}
|
||||
|
||||
public static TimeSpan OnUse(Mobile from)
|
||||
{
|
||||
from.SendLocalizedMessage(500343); // What do you wish to appraise and identify?
|
||||
from.Target = new InternalTarget();
|
||||
|
||||
return TimeSpan.FromSeconds(1.0);
|
||||
}
|
||||
|
||||
[PlayerVendorTarget]
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
public InternalTarget() : base(8, false, TargetFlags.None) => AllowNonlocal = true;
|
||||
|
||||
protected override void OnTarget(Mobile from, object o)
|
||||
{
|
||||
if (o is Item item)
|
||||
public static void Initialize()
|
||||
{
|
||||
if (from.CheckTargetSkill(SkillName.ItemID, item, 0, 100))
|
||||
{
|
||||
if (item is BaseWeapon weapon)
|
||||
weapon.Identified = true;
|
||||
else if (item is BaseArmor armor)
|
||||
armor.Identified = true;
|
||||
SkillInfo.Table[(int)SkillName.ItemID].Callback = OnUse;
|
||||
}
|
||||
|
||||
if (!Core.AOS)
|
||||
item.OnSingleClick(from);
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(500353); // You are not certain...
|
||||
}
|
||||
}
|
||||
else if (o is Mobile mobile)
|
||||
public static TimeSpan OnUse(Mobile from)
|
||||
{
|
||||
mobile.OnSingleClick(from);
|
||||
from.SendLocalizedMessage(500343); // What do you wish to appraise and identify?
|
||||
from.Target = new InternalTarget();
|
||||
|
||||
return TimeSpan.FromSeconds(1.0);
|
||||
}
|
||||
else
|
||||
|
||||
[PlayerVendorTarget]
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
from.SendLocalizedMessage(500353); // You are not certain...
|
||||
public InternalTarget() : base(8, false, TargetFlags.None) => AllowNonlocal = true;
|
||||
|
||||
protected override void OnTarget(Mobile from, object o)
|
||||
{
|
||||
if (o is Item item)
|
||||
{
|
||||
if (from.CheckTargetSkill(SkillName.ItemID, item, 0, 100))
|
||||
{
|
||||
if (item is BaseWeapon weapon)
|
||||
weapon.Identified = true;
|
||||
else if (item is BaseArmor armor)
|
||||
armor.Identified = true;
|
||||
|
||||
if (!Core.AOS)
|
||||
item.OnSingleClick(from);
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(500353); // You are not certain...
|
||||
}
|
||||
}
|
||||
else if (o is Mobile mobile)
|
||||
{
|
||||
mobile.OnSingleClick(from);
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(500353); // You are not certain...
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,100 +4,100 @@ using Server.Misc;
|
|||
|
||||
namespace Server.SkillHandlers
|
||||
{
|
||||
internal static class Meditation
|
||||
{
|
||||
public static void Initialize()
|
||||
internal static class Meditation
|
||||
{
|
||||
SkillInfo.Table[46].Callback = OnUse;
|
||||
public static void Initialize()
|
||||
{
|
||||
SkillInfo.Table[46].Callback = OnUse;
|
||||
}
|
||||
|
||||
public static bool CheckOkayHolding(Item item)
|
||||
{
|
||||
if (item == null)
|
||||
return true;
|
||||
|
||||
if (item is Spellbook || item is Runebook)
|
||||
return true;
|
||||
|
||||
if (Core.AOS && item is BaseWeapon weapon && weapon.Attributes.SpellChanneling != 0)
|
||||
return true;
|
||||
|
||||
if (Core.AOS && item is BaseArmor armor && armor.Attributes.SpellChanneling != 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static TimeSpan OnUse(Mobile m)
|
||||
{
|
||||
m.RevealingAction();
|
||||
|
||||
if (m.Target != null)
|
||||
{
|
||||
m.SendLocalizedMessage(501845); // You are busy doing something else and cannot focus.
|
||||
|
||||
return TimeSpan.FromSeconds(5.0);
|
||||
}
|
||||
|
||||
if (!Core.AOS && m.Hits < m.HitsMax / 10) // Less than 10% health
|
||||
{
|
||||
m.SendLocalizedMessage(501849); // The mind is strong but the body is weak.
|
||||
|
||||
return TimeSpan.FromSeconds(5.0);
|
||||
}
|
||||
|
||||
if (m.Mana >= m.ManaMax)
|
||||
{
|
||||
m.SendLocalizedMessage(501846); // You are at peace.
|
||||
|
||||
return TimeSpan.FromSeconds(Core.AOS ? 10.0 : 5.0);
|
||||
}
|
||||
|
||||
if (Core.AOS && RegenRates.GetArmorOffset(m) > 0)
|
||||
{
|
||||
m.SendLocalizedMessage(500135); // Regenerative forces cannot penetrate your armor!
|
||||
|
||||
return TimeSpan.FromSeconds(10.0);
|
||||
}
|
||||
|
||||
var oneHanded = m.FindItemOnLayer(Layer.OneHanded);
|
||||
var twoHanded = m.FindItemOnLayer(Layer.TwoHanded);
|
||||
|
||||
if (Core.AOS && m.Player)
|
||||
{
|
||||
if (!CheckOkayHolding(oneHanded))
|
||||
m.AddToBackpack(oneHanded);
|
||||
|
||||
if (!CheckOkayHolding(twoHanded))
|
||||
m.AddToBackpack(twoHanded);
|
||||
}
|
||||
else if (!CheckOkayHolding(oneHanded) || !CheckOkayHolding(twoHanded))
|
||||
{
|
||||
m.SendLocalizedMessage(502626); // Your hands must be free to cast spells or meditate.
|
||||
|
||||
return TimeSpan.FromSeconds(2.5);
|
||||
}
|
||||
|
||||
var skillVal = m.Skills.Meditation.Value;
|
||||
var chance = (50.0 + (skillVal - (m.ManaMax - m.Mana)) * 2) / 100;
|
||||
|
||||
if (chance > Utility.RandomDouble())
|
||||
{
|
||||
m.CheckSkill(SkillName.Meditation, 0.0, 100.0);
|
||||
|
||||
m.SendLocalizedMessage(501851); // You enter a meditative trance.
|
||||
m.Meditating = true;
|
||||
BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.ActiveMeditation, 1075657));
|
||||
|
||||
if (m.Player || m.Body.IsHuman)
|
||||
m.PlaySound(0xF9);
|
||||
}
|
||||
else
|
||||
{
|
||||
m.SendLocalizedMessage(501850); // You cannot focus your concentration.
|
||||
}
|
||||
|
||||
return TimeSpan.FromSeconds(10.0);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool CheckOkayHolding(Item item)
|
||||
{
|
||||
if (item == null)
|
||||
return true;
|
||||
|
||||
if (item is Spellbook || item is Runebook)
|
||||
return true;
|
||||
|
||||
if (Core.AOS && item is BaseWeapon weapon && weapon.Attributes.SpellChanneling != 0)
|
||||
return true;
|
||||
|
||||
if (Core.AOS && item is BaseArmor armor && armor.Attributes.SpellChanneling != 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static TimeSpan OnUse(Mobile m)
|
||||
{
|
||||
m.RevealingAction();
|
||||
|
||||
if (m.Target != null)
|
||||
{
|
||||
m.SendLocalizedMessage(501845); // You are busy doing something else and cannot focus.
|
||||
|
||||
return TimeSpan.FromSeconds(5.0);
|
||||
}
|
||||
|
||||
if (!Core.AOS && m.Hits < m.HitsMax / 10) // Less than 10% health
|
||||
{
|
||||
m.SendLocalizedMessage(501849); // The mind is strong but the body is weak.
|
||||
|
||||
return TimeSpan.FromSeconds(5.0);
|
||||
}
|
||||
|
||||
if (m.Mana >= m.ManaMax)
|
||||
{
|
||||
m.SendLocalizedMessage(501846); // You are at peace.
|
||||
|
||||
return TimeSpan.FromSeconds(Core.AOS ? 10.0 : 5.0);
|
||||
}
|
||||
|
||||
if (Core.AOS && RegenRates.GetArmorOffset(m) > 0)
|
||||
{
|
||||
m.SendLocalizedMessage(500135); // Regenerative forces cannot penetrate your armor!
|
||||
|
||||
return TimeSpan.FromSeconds(10.0);
|
||||
}
|
||||
|
||||
Item oneHanded = m.FindItemOnLayer(Layer.OneHanded);
|
||||
Item twoHanded = m.FindItemOnLayer(Layer.TwoHanded);
|
||||
|
||||
if (Core.AOS && m.Player)
|
||||
{
|
||||
if (!CheckOkayHolding(oneHanded))
|
||||
m.AddToBackpack(oneHanded);
|
||||
|
||||
if (!CheckOkayHolding(twoHanded))
|
||||
m.AddToBackpack(twoHanded);
|
||||
}
|
||||
else if (!CheckOkayHolding(oneHanded) || !CheckOkayHolding(twoHanded))
|
||||
{
|
||||
m.SendLocalizedMessage(502626); // Your hands must be free to cast spells or meditate.
|
||||
|
||||
return TimeSpan.FromSeconds(2.5);
|
||||
}
|
||||
|
||||
double skillVal = m.Skills.Meditation.Value;
|
||||
double chance = (50.0 + (skillVal - (m.ManaMax - m.Mana)) * 2) / 100;
|
||||
|
||||
if (chance > Utility.RandomDouble())
|
||||
{
|
||||
m.CheckSkill(SkillName.Meditation, 0.0, 100.0);
|
||||
|
||||
m.SendLocalizedMessage(501851); // You enter a meditative trance.
|
||||
m.Meditating = true;
|
||||
BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.ActiveMeditation, 1075657));
|
||||
|
||||
if (m.Player || m.Body.IsHuman)
|
||||
m.PlaySound(0xF9);
|
||||
}
|
||||
else
|
||||
{
|
||||
m.SendLocalizedMessage(501850); // You cannot focus your concentration.
|
||||
}
|
||||
|
||||
return TimeSpan.FromSeconds(10.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,202 +6,209 @@ using Server.Targeting;
|
|||
|
||||
namespace Server.SkillHandlers
|
||||
{
|
||||
public static class Peacemaking
|
||||
{
|
||||
public static void Initialize()
|
||||
public static class Peacemaking
|
||||
{
|
||||
SkillInfo.Table[(int)SkillName.Peacemaking].Callback = OnUse;
|
||||
}
|
||||
|
||||
public static TimeSpan OnUse(Mobile m)
|
||||
{
|
||||
m.RevealingAction();
|
||||
|
||||
BaseInstrument.PickInstrument(m, OnPickedInstrument);
|
||||
|
||||
return TimeSpan.FromSeconds(1.0); // Cannot use another skill for 1 second
|
||||
}
|
||||
|
||||
public static void OnPickedInstrument(Mobile from, BaseInstrument instrument)
|
||||
{
|
||||
from.RevealingAction();
|
||||
from.SendLocalizedMessage(1049525); // Whom do you wish to calm?
|
||||
from.Target = new InternalTarget(from, instrument);
|
||||
from.NextSkillTime = Core.TickCount + 21600000;
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private readonly BaseInstrument m_Instrument;
|
||||
private bool m_SetSkillTime = true;
|
||||
|
||||
public InternalTarget(Mobile from, BaseInstrument instrument) : base(
|
||||
BaseInstrument.GetBardRange(from, SkillName.Peacemaking), false, TargetFlags.None) =>
|
||||
m_Instrument = instrument;
|
||||
|
||||
protected override void OnTargetFinish(Mobile from)
|
||||
{
|
||||
if (m_SetSkillTime)
|
||||
from.NextSkillTime = Core.TickCount;
|
||||
}
|
||||
|
||||
protected override void OnTarget(Mobile from, object targeted)
|
||||
{
|
||||
from.RevealingAction();
|
||||
|
||||
if (!(targeted is Mobile targ))
|
||||
public static void Initialize()
|
||||
{
|
||||
from.SendLocalizedMessage(1049528); // You cannot calm that!
|
||||
SkillInfo.Table[(int)SkillName.Peacemaking].Callback = OnUse;
|
||||
}
|
||||
else if (from.Region.IsPartOf<SafeZone>())
|
||||
{
|
||||
from.SendMessage("You may not peacemake in this area.");
|
||||
}
|
||||
else if (targ.Region.IsPartOf<SafeZone>())
|
||||
{
|
||||
from.SendMessage("You may not peacemake there.");
|
||||
}
|
||||
else if (!m_Instrument.IsChildOf(from.Backpack))
|
||||
{
|
||||
from.SendLocalizedMessage(
|
||||
1062488); // The instrument you are trying to play is no longer in your backpack!
|
||||
}
|
||||
else
|
||||
{
|
||||
m_SetSkillTime = false;
|
||||
from.NextSkillTime = Core.TickCount + 10000;
|
||||
|
||||
if (targeted == from)
|
||||
{
|
||||
// Standard mode : reset combatants for everyone in the area
|
||||
public static TimeSpan OnUse(Mobile m)
|
||||
{
|
||||
m.RevealingAction();
|
||||
|
||||
if (!BaseInstrument.CheckMusicianship(from))
|
||||
BaseInstrument.PickInstrument(m, OnPickedInstrument);
|
||||
|
||||
return TimeSpan.FromSeconds(1.0); // Cannot use another skill for 1 second
|
||||
}
|
||||
|
||||
public static void OnPickedInstrument(Mobile from, BaseInstrument instrument)
|
||||
{
|
||||
from.RevealingAction();
|
||||
from.SendLocalizedMessage(1049525); // Whom do you wish to calm?
|
||||
from.Target = new InternalTarget(from, instrument);
|
||||
from.NextSkillTime = Core.TickCount + 21600000;
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private readonly BaseInstrument m_Instrument;
|
||||
private bool m_SetSkillTime = true;
|
||||
|
||||
public InternalTarget(Mobile from, BaseInstrument instrument) : base(
|
||||
BaseInstrument.GetBardRange(from, SkillName.Peacemaking),
|
||||
false,
|
||||
TargetFlags.None
|
||||
) =>
|
||||
m_Instrument = instrument;
|
||||
|
||||
protected override void OnTargetFinish(Mobile from)
|
||||
{
|
||||
from.SendLocalizedMessage(500612); // You play poorly, and there is no effect.
|
||||
m_Instrument.PlayInstrumentBadly(from);
|
||||
m_Instrument.ConsumeUse(from);
|
||||
if (m_SetSkillTime)
|
||||
from.NextSkillTime = Core.TickCount;
|
||||
}
|
||||
else if (!from.CheckSkill(SkillName.Peacemaking, 0.0, 120.0))
|
||||
|
||||
protected override void OnTarget(Mobile from, object targeted)
|
||||
{
|
||||
from.SendLocalizedMessage(500613); // You attempt to calm everyone, but fail.
|
||||
m_Instrument.PlayInstrumentBadly(from);
|
||||
m_Instrument.ConsumeUse(from);
|
||||
}
|
||||
else
|
||||
{
|
||||
from.NextSkillTime = Core.TickCount + 5000;
|
||||
m_Instrument.PlayInstrumentWell(from);
|
||||
m_Instrument.ConsumeUse(from);
|
||||
from.RevealingAction();
|
||||
|
||||
Map map = from.Map;
|
||||
|
||||
if (map != null)
|
||||
{
|
||||
int range = BaseInstrument.GetBardRange(from, SkillName.Peacemaking);
|
||||
|
||||
bool calmed = false;
|
||||
|
||||
foreach (Mobile m in from.GetMobilesInRange(range))
|
||||
if (!(targeted is Mobile targ))
|
||||
{
|
||||
BaseCreature bc = m as BaseCreature;
|
||||
if (bc?.Uncalmable == true || bc?.AreaPeaceImmune == true || m == from ||
|
||||
!from.CanBeHarmful(m, false))
|
||||
continue;
|
||||
|
||||
calmed = true;
|
||||
|
||||
m.SendLocalizedMessage(
|
||||
500616); // You hear lovely music, and forget to continue battling!
|
||||
m.Combatant = null;
|
||||
m.Warmode = false;
|
||||
|
||||
if (bc?.BardPacified == false)
|
||||
bc.Pacify(from, DateTime.UtcNow + TimeSpan.FromSeconds(1.0));
|
||||
from.SendLocalizedMessage(1049528); // You cannot calm that!
|
||||
}
|
||||
|
||||
if (!calmed)
|
||||
from.SendLocalizedMessage(
|
||||
1049648); // You play hypnotic music, but there is nothing in range for you to calm.
|
||||
else
|
||||
from.SendLocalizedMessage(500615); // You play your hypnotic music, stopping the battle.
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Target mode : pacify a single target for a longer duration
|
||||
BaseCreature bc = targ as BaseCreature;
|
||||
|
||||
if (!from.CanBeHarmful(targ, false))
|
||||
{
|
||||
from.SendLocalizedMessage(1049528);
|
||||
m_SetSkillTime = true;
|
||||
}
|
||||
else if (bc?.Uncalmable == true)
|
||||
{
|
||||
from.SendLocalizedMessage(1049526); // You have no chance of calming that creature.
|
||||
m_SetSkillTime = true;
|
||||
}
|
||||
else if (bc?.BardPacified == true)
|
||||
{
|
||||
from.SendLocalizedMessage(1049527); // That creature is already being calmed.
|
||||
m_SetSkillTime = true;
|
||||
}
|
||||
else if (!BaseInstrument.CheckMusicianship(from))
|
||||
{
|
||||
from.SendLocalizedMessage(500612); // You play poorly, and there is no effect.
|
||||
from.NextSkillTime = Core.TickCount + 5000;
|
||||
m_Instrument.PlayInstrumentBadly(from);
|
||||
m_Instrument.ConsumeUse(from);
|
||||
}
|
||||
else
|
||||
{
|
||||
double diff = m_Instrument.GetDifficultyFor(targ) - 10.0;
|
||||
double music = from.Skills.Musicianship.Value;
|
||||
|
||||
if (music > 100.0)
|
||||
diff -= (music - 100.0) * 0.5;
|
||||
|
||||
if (!from.CheckTargetSkill(SkillName.Peacemaking, targ, diff - 25.0, diff + 25.0))
|
||||
{
|
||||
from.SendLocalizedMessage(1049531); // You attempt to calm your target, but fail.
|
||||
m_Instrument.PlayInstrumentBadly(from);
|
||||
m_Instrument.ConsumeUse(from);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Instrument.PlayInstrumentWell(from);
|
||||
m_Instrument.ConsumeUse(from);
|
||||
|
||||
from.NextSkillTime = Core.TickCount + 5000;
|
||||
targ.Combatant = null;
|
||||
targ.Warmode = false;
|
||||
|
||||
if (bc != null)
|
||||
else if (from.Region.IsPartOf<SafeZone>())
|
||||
{
|
||||
from.SendLocalizedMessage(1049532); // You play hypnotic music, calming your target.
|
||||
|
||||
double seconds = 100 - diff / 1.5;
|
||||
|
||||
if (seconds > 120)
|
||||
seconds = 120;
|
||||
else if (seconds < 10)
|
||||
seconds = 10;
|
||||
|
||||
bc.Pacify(from, DateTime.UtcNow + TimeSpan.FromSeconds(seconds));
|
||||
from.SendMessage("You may not peacemake in this area.");
|
||||
}
|
||||
else if (targ.Region.IsPartOf<SafeZone>())
|
||||
{
|
||||
from.SendMessage("You may not peacemake there.");
|
||||
}
|
||||
else if (!m_Instrument.IsChildOf(from.Backpack))
|
||||
{
|
||||
from.SendLocalizedMessage(
|
||||
1062488
|
||||
); // The instrument you are trying to play is no longer in your backpack!
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(1049532); // You play hypnotic music, calming your target.
|
||||
m_SetSkillTime = false;
|
||||
from.NextSkillTime = Core.TickCount + 10000;
|
||||
|
||||
targ.SendLocalizedMessage(
|
||||
500616); // You hear lovely music, and forget to continue battling!
|
||||
if (targeted == from)
|
||||
{
|
||||
// Standard mode : reset combatants for everyone in the area
|
||||
|
||||
if (!BaseInstrument.CheckMusicianship(from))
|
||||
{
|
||||
from.SendLocalizedMessage(500612); // You play poorly, and there is no effect.
|
||||
m_Instrument.PlayInstrumentBadly(from);
|
||||
m_Instrument.ConsumeUse(from);
|
||||
}
|
||||
else if (!from.CheckSkill(SkillName.Peacemaking, 0.0, 120.0))
|
||||
{
|
||||
from.SendLocalizedMessage(500613); // You attempt to calm everyone, but fail.
|
||||
m_Instrument.PlayInstrumentBadly(from);
|
||||
m_Instrument.ConsumeUse(from);
|
||||
}
|
||||
else
|
||||
{
|
||||
from.NextSkillTime = Core.TickCount + 5000;
|
||||
m_Instrument.PlayInstrumentWell(from);
|
||||
m_Instrument.ConsumeUse(from);
|
||||
|
||||
var map = from.Map;
|
||||
|
||||
if (map != null)
|
||||
{
|
||||
var range = BaseInstrument.GetBardRange(from, SkillName.Peacemaking);
|
||||
|
||||
var calmed = false;
|
||||
|
||||
foreach (var m in from.GetMobilesInRange(range))
|
||||
{
|
||||
var bc = m as BaseCreature;
|
||||
if (bc?.Uncalmable == true || bc?.AreaPeaceImmune == true || m == from ||
|
||||
!from.CanBeHarmful(m, false))
|
||||
continue;
|
||||
|
||||
calmed = true;
|
||||
|
||||
m.SendLocalizedMessage(
|
||||
500616
|
||||
); // You hear lovely music, and forget to continue battling!
|
||||
m.Combatant = null;
|
||||
m.Warmode = false;
|
||||
|
||||
if (bc?.BardPacified == false)
|
||||
bc.Pacify(from, DateTime.UtcNow + TimeSpan.FromSeconds(1.0));
|
||||
}
|
||||
|
||||
if (!calmed)
|
||||
from.SendLocalizedMessage(
|
||||
1049648
|
||||
); // You play hypnotic music, but there is nothing in range for you to calm.
|
||||
else
|
||||
from.SendLocalizedMessage(500615); // You play your hypnotic music, stopping the battle.
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Target mode : pacify a single target for a longer duration
|
||||
var bc = targ as BaseCreature;
|
||||
|
||||
if (!from.CanBeHarmful(targ, false))
|
||||
{
|
||||
from.SendLocalizedMessage(1049528);
|
||||
m_SetSkillTime = true;
|
||||
}
|
||||
else if (bc?.Uncalmable == true)
|
||||
{
|
||||
from.SendLocalizedMessage(1049526); // You have no chance of calming that creature.
|
||||
m_SetSkillTime = true;
|
||||
}
|
||||
else if (bc?.BardPacified == true)
|
||||
{
|
||||
from.SendLocalizedMessage(1049527); // That creature is already being calmed.
|
||||
m_SetSkillTime = true;
|
||||
}
|
||||
else if (!BaseInstrument.CheckMusicianship(from))
|
||||
{
|
||||
from.SendLocalizedMessage(500612); // You play poorly, and there is no effect.
|
||||
from.NextSkillTime = Core.TickCount + 5000;
|
||||
m_Instrument.PlayInstrumentBadly(from);
|
||||
m_Instrument.ConsumeUse(from);
|
||||
}
|
||||
else
|
||||
{
|
||||
var diff = m_Instrument.GetDifficultyFor(targ) - 10.0;
|
||||
var music = from.Skills.Musicianship.Value;
|
||||
|
||||
if (music > 100.0)
|
||||
diff -= (music - 100.0) * 0.5;
|
||||
|
||||
if (!from.CheckTargetSkill(SkillName.Peacemaking, targ, diff - 25.0, diff + 25.0))
|
||||
{
|
||||
from.SendLocalizedMessage(1049531); // You attempt to calm your target, but fail.
|
||||
m_Instrument.PlayInstrumentBadly(from);
|
||||
m_Instrument.ConsumeUse(from);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Instrument.PlayInstrumentWell(from);
|
||||
m_Instrument.ConsumeUse(from);
|
||||
|
||||
from.NextSkillTime = Core.TickCount + 5000;
|
||||
targ.Combatant = null;
|
||||
targ.Warmode = false;
|
||||
|
||||
if (bc != null)
|
||||
{
|
||||
from.SendLocalizedMessage(1049532); // You play hypnotic music, calming your target.
|
||||
|
||||
var seconds = 100 - diff / 1.5;
|
||||
|
||||
if (seconds > 120)
|
||||
seconds = 120;
|
||||
else if (seconds < 10)
|
||||
seconds = 10;
|
||||
|
||||
bc.Pacify(from, DateTime.UtcNow + TimeSpan.FromSeconds(seconds));
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(1049532); // You play hypnotic music, calming your target.
|
||||
|
||||
targ.SendLocalizedMessage(
|
||||
500616
|
||||
); // You hear lovely music, and forget to continue battling!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,166 +6,174 @@ using Server.Targeting;
|
|||
|
||||
namespace Server.SkillHandlers
|
||||
{
|
||||
public static class Poisoning
|
||||
{
|
||||
public static void Initialize()
|
||||
public static class Poisoning
|
||||
{
|
||||
SkillInfo.Table[(int)SkillName.Poisoning].Callback = OnUse;
|
||||
}
|
||||
|
||||
public static TimeSpan OnUse(Mobile m)
|
||||
{
|
||||
m.Target = new InternalTargetPoison();
|
||||
|
||||
m.SendLocalizedMessage(502137); // Select the poison you wish to use
|
||||
|
||||
return TimeSpan.FromSeconds(10.0); // 10 second delay before being able to re-use a skill
|
||||
}
|
||||
|
||||
private class InternalTargetPoison : Target
|
||||
{
|
||||
public InternalTargetPoison() : base(2, false, TargetFlags.None)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnTarget(Mobile from, object targeted)
|
||||
{
|
||||
if (targeted is BasePoisonPotion potion)
|
||||
public static void Initialize()
|
||||
{
|
||||
from.SendLocalizedMessage(502142); // To what do you wish to apply the poison?
|
||||
from.Target = new InternalTarget(potion);
|
||||
SkillInfo.Table[(int)SkillName.Poisoning].Callback = OnUse;
|
||||
}
|
||||
else // Not a Poison Potion
|
||||
|
||||
public static TimeSpan OnUse(Mobile m)
|
||||
{
|
||||
from.SendLocalizedMessage(502139); // That is not a poison potion.
|
||||
m.Target = new InternalTargetPoison();
|
||||
|
||||
m.SendLocalizedMessage(502137); // Select the poison you wish to use
|
||||
|
||||
return TimeSpan.FromSeconds(10.0); // 10 second delay before being able to re-use a skill
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private readonly BasePoisonPotion m_Potion;
|
||||
|
||||
public InternalTarget(BasePoisonPotion potion) : base(2, false, TargetFlags.None) => m_Potion = potion;
|
||||
|
||||
protected override void OnTarget(Mobile from, object targeted)
|
||||
private class InternalTargetPoison : Target
|
||||
{
|
||||
if (m_Potion.Deleted)
|
||||
return;
|
||||
|
||||
bool startTimer = false;
|
||||
|
||||
if (targeted is Food || targeted is FukiyaDarts || targeted is Shuriken)
|
||||
{
|
||||
startTimer = true;
|
||||
}
|
||||
else if (targeted is BaseWeapon weapon)
|
||||
{
|
||||
if (Core.AOS)
|
||||
startTimer = weapon.PrimaryAbility == WeaponAbility.InfectiousStrike ||
|
||||
weapon.SecondaryAbility == WeaponAbility.InfectiousStrike;
|
||||
else if (weapon.Layer == Layer.OneHanded)
|
||||
startTimer = weapon.Type == WeaponType.Slashing || weapon.Type == WeaponType.Piercing;
|
||||
}
|
||||
|
||||
if (startTimer)
|
||||
{
|
||||
new InternalTimer(from, (Item)targeted, m_Potion).Start();
|
||||
|
||||
from.PlaySound(0x4F);
|
||||
|
||||
if (!DuelContext.IsFreeConsume(from))
|
||||
public InternalTargetPoison() : base(2, false, TargetFlags.None)
|
||||
{
|
||||
m_Potion.Consume();
|
||||
from.AddToBackpack(new Bottle());
|
||||
}
|
||||
}
|
||||
else // Target can't be poisoned
|
||||
{
|
||||
if (Core.AOS)
|
||||
from.SendLocalizedMessage(
|
||||
1060204); // You cannot poison that! You can only poison infectious weapons, food or drink.
|
||||
else
|
||||
from.SendLocalizedMessage(
|
||||
502145); // You cannot poison that! You can only poison bladed or piercing weapons, food or drink.
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalTimer : Timer
|
||||
{
|
||||
private readonly Mobile m_From;
|
||||
private readonly double m_MinSkill;
|
||||
private readonly double m_MaxSkill;
|
||||
private readonly Poison m_Poison;
|
||||
private readonly Item m_Target;
|
||||
|
||||
public InternalTimer(Mobile from, Item target, BasePoisonPotion potion) : base(TimeSpan.FromSeconds(2.0))
|
||||
{
|
||||
m_From = from;
|
||||
m_Target = target;
|
||||
m_Poison = potion.Poison;
|
||||
m_MinSkill = potion.MinPoisoningSkill;
|
||||
m_MaxSkill = potion.MaxPoisoningSkill;
|
||||
Priority = TimerPriority.TwoFiftyMS;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
if (m_From.CheckTargetSkill(SkillName.Poisoning, m_Target, m_MinSkill, m_MaxSkill))
|
||||
protected override void OnTarget(Mobile from, object targeted)
|
||||
{
|
||||
if (m_Target is Food food)
|
||||
{
|
||||
food.Poison = m_Poison;
|
||||
}
|
||||
else if (m_Target is BaseWeapon weapon)
|
||||
{
|
||||
weapon.Poison = m_Poison;
|
||||
weapon.PoisonCharges = 18 - m_Poison.Level * 2;
|
||||
}
|
||||
else if (m_Target is FukiyaDarts darts)
|
||||
{
|
||||
darts.Poison = m_Poison;
|
||||
darts.PoisonCharges = Math.Min(18 - m_Poison.Level * 2,
|
||||
darts.UsesRemaining);
|
||||
}
|
||||
else if (m_Target is Shuriken shuriken)
|
||||
{
|
||||
shuriken.Poison = m_Poison;
|
||||
shuriken.PoisonCharges = Math.Min(18 - m_Poison.Level * 2,
|
||||
shuriken.UsesRemaining);
|
||||
}
|
||||
|
||||
m_From.SendLocalizedMessage(1010517); // You apply the poison
|
||||
|
||||
Titles.AwardKarma(m_From, -20, true);
|
||||
}
|
||||
else // Failed
|
||||
{
|
||||
// 5% of chance of getting poisoned if failed
|
||||
if (m_From.Skills.Poisoning.Base < 80.0 && Utility.Random(20) == 0)
|
||||
{
|
||||
m_From.SendLocalizedMessage(502148); // You make a grave mistake while applying the poison.
|
||||
m_From.ApplyPoison(m_From, m_Poison);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_Target is BaseWeapon weapon)
|
||||
if (targeted is BasePoisonPotion potion)
|
||||
{
|
||||
if (weapon.Type == WeaponType.Slashing)
|
||||
m_From.SendLocalizedMessage(
|
||||
1010516); // You fail to apply a sufficient dose of poison on the blade
|
||||
else
|
||||
m_From.SendLocalizedMessage(
|
||||
1010518); // You fail to apply a sufficient dose of poison
|
||||
from.SendLocalizedMessage(502142); // To what do you wish to apply the poison?
|
||||
from.Target = new InternalTarget(potion);
|
||||
}
|
||||
else
|
||||
else // Not a Poison Potion
|
||||
{
|
||||
m_From.SendLocalizedMessage(1010518); // You fail to apply a sufficient dose of poison
|
||||
from.SendLocalizedMessage(502139); // That is not a poison potion.
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private readonly BasePoisonPotion m_Potion;
|
||||
|
||||
public InternalTarget(BasePoisonPotion potion) : base(2, false, TargetFlags.None) => m_Potion = potion;
|
||||
|
||||
protected override void OnTarget(Mobile from, object targeted)
|
||||
{
|
||||
if (m_Potion.Deleted)
|
||||
return;
|
||||
|
||||
var startTimer = false;
|
||||
|
||||
if (targeted is Food || targeted is FukiyaDarts || targeted is Shuriken)
|
||||
{
|
||||
startTimer = true;
|
||||
}
|
||||
else if (targeted is BaseWeapon weapon)
|
||||
{
|
||||
if (Core.AOS)
|
||||
startTimer = weapon.PrimaryAbility == WeaponAbility.InfectiousStrike ||
|
||||
weapon.SecondaryAbility == WeaponAbility.InfectiousStrike;
|
||||
else if (weapon.Layer == Layer.OneHanded)
|
||||
startTimer = weapon.Type == WeaponType.Slashing || weapon.Type == WeaponType.Piercing;
|
||||
}
|
||||
|
||||
if (startTimer)
|
||||
{
|
||||
new InternalTimer(from, (Item)targeted, m_Potion).Start();
|
||||
|
||||
from.PlaySound(0x4F);
|
||||
|
||||
if (!DuelContext.IsFreeConsume(from))
|
||||
{
|
||||
m_Potion.Consume();
|
||||
from.AddToBackpack(new Bottle());
|
||||
}
|
||||
}
|
||||
else // Target can't be poisoned
|
||||
{
|
||||
if (Core.AOS)
|
||||
from.SendLocalizedMessage(
|
||||
1060204
|
||||
); // You cannot poison that! You can only poison infectious weapons, food or drink.
|
||||
else
|
||||
from.SendLocalizedMessage(
|
||||
502145
|
||||
); // You cannot poison that! You can only poison bladed or piercing weapons, food or drink.
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalTimer : Timer
|
||||
{
|
||||
private readonly Mobile m_From;
|
||||
private readonly double m_MaxSkill;
|
||||
private readonly double m_MinSkill;
|
||||
private readonly Poison m_Poison;
|
||||
private readonly Item m_Target;
|
||||
|
||||
public InternalTimer(Mobile from, Item target, BasePoisonPotion potion) : base(TimeSpan.FromSeconds(2.0))
|
||||
{
|
||||
m_From = from;
|
||||
m_Target = target;
|
||||
m_Poison = potion.Poison;
|
||||
m_MinSkill = potion.MinPoisoningSkill;
|
||||
m_MaxSkill = potion.MaxPoisoningSkill;
|
||||
Priority = TimerPriority.TwoFiftyMS;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
if (m_From.CheckTargetSkill(SkillName.Poisoning, m_Target, m_MinSkill, m_MaxSkill))
|
||||
{
|
||||
if (m_Target is Food food)
|
||||
{
|
||||
food.Poison = m_Poison;
|
||||
}
|
||||
else if (m_Target is BaseWeapon weapon)
|
||||
{
|
||||
weapon.Poison = m_Poison;
|
||||
weapon.PoisonCharges = 18 - m_Poison.Level * 2;
|
||||
}
|
||||
else if (m_Target is FukiyaDarts darts)
|
||||
{
|
||||
darts.Poison = m_Poison;
|
||||
darts.PoisonCharges = Math.Min(
|
||||
18 - m_Poison.Level * 2,
|
||||
darts.UsesRemaining
|
||||
);
|
||||
}
|
||||
else if (m_Target is Shuriken shuriken)
|
||||
{
|
||||
shuriken.Poison = m_Poison;
|
||||
shuriken.PoisonCharges = Math.Min(
|
||||
18 - m_Poison.Level * 2,
|
||||
shuriken.UsesRemaining
|
||||
);
|
||||
}
|
||||
|
||||
m_From.SendLocalizedMessage(1010517); // You apply the poison
|
||||
|
||||
Titles.AwardKarma(m_From, -20, true);
|
||||
}
|
||||
else // Failed
|
||||
{
|
||||
// 5% of chance of getting poisoned if failed
|
||||
if (m_From.Skills.Poisoning.Base < 80.0 && Utility.Random(20) == 0)
|
||||
{
|
||||
m_From.SendLocalizedMessage(502148); // You make a grave mistake while applying the poison.
|
||||
m_From.ApplyPoison(m_From, m_Poison);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_Target is BaseWeapon weapon)
|
||||
{
|
||||
if (weapon.Type == WeaponType.Slashing)
|
||||
m_From.SendLocalizedMessage(
|
||||
1010516
|
||||
); // You fail to apply a sufficient dose of poison on the blade
|
||||
else
|
||||
m_From.SendLocalizedMessage(
|
||||
1010518
|
||||
); // You fail to apply a sufficient dose of poison
|
||||
}
|
||||
else
|
||||
{
|
||||
m_From.SendLocalizedMessage(1010518); // You fail to apply a sufficient dose of poison
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,161 +5,173 @@ using Server.Targeting;
|
|||
|
||||
namespace Server.SkillHandlers
|
||||
{
|
||||
public static class Provocation
|
||||
{
|
||||
public static void Initialize()
|
||||
public static class Provocation
|
||||
{
|
||||
SkillInfo.Table[(int)SkillName.Provocation].Callback = OnUse;
|
||||
}
|
||||
|
||||
public static TimeSpan OnUse(Mobile m)
|
||||
{
|
||||
m.RevealingAction();
|
||||
|
||||
BaseInstrument.PickInstrument(m, OnPickedInstrument);
|
||||
|
||||
return TimeSpan.FromSeconds(1.0); // Cannot use another skill for 1 second
|
||||
}
|
||||
|
||||
public static void OnPickedInstrument(Mobile from, BaseInstrument instrument)
|
||||
{
|
||||
from.RevealingAction();
|
||||
from.SendLocalizedMessage(501587); // Whom do you wish to incite?
|
||||
from.Target = new InternalFirstTarget(from, instrument);
|
||||
}
|
||||
|
||||
private class InternalFirstTarget : Target
|
||||
{
|
||||
private readonly BaseInstrument m_Instrument;
|
||||
|
||||
public InternalFirstTarget(Mobile from, BaseInstrument instrument) : base(
|
||||
BaseInstrument.GetBardRange(from, SkillName.Provocation), false, TargetFlags.None) =>
|
||||
m_Instrument = instrument;
|
||||
|
||||
protected override void OnTarget(Mobile from, object targeted)
|
||||
{
|
||||
from.RevealingAction();
|
||||
|
||||
if (targeted is BaseCreature creature && from.CanBeHarmful(creature, true))
|
||||
public static void Initialize()
|
||||
{
|
||||
SkillInfo.Table[(int)SkillName.Provocation].Callback = OnUse;
|
||||
}
|
||||
|
||||
public static TimeSpan OnUse(Mobile m)
|
||||
{
|
||||
m.RevealingAction();
|
||||
|
||||
BaseInstrument.PickInstrument(m, OnPickedInstrument);
|
||||
|
||||
return TimeSpan.FromSeconds(1.0); // Cannot use another skill for 1 second
|
||||
}
|
||||
|
||||
public static void OnPickedInstrument(Mobile from, BaseInstrument instrument)
|
||||
{
|
||||
if (!m_Instrument.IsChildOf(from.Backpack))
|
||||
{
|
||||
from.SendLocalizedMessage(
|
||||
1062488); // The instrument you are trying to play is no longer in your backpack!
|
||||
}
|
||||
else if (creature.Controlled)
|
||||
{
|
||||
from.SendLocalizedMessage(501590); // They are too loyal to their master to be provoked.
|
||||
}
|
||||
else if (creature.IsParagon && BaseInstrument.GetBaseDifficulty(creature) >= 160.0)
|
||||
{
|
||||
from.SendLocalizedMessage(1049446); // You have no chance of provoking those creatures.
|
||||
}
|
||||
else
|
||||
{
|
||||
from.RevealingAction();
|
||||
m_Instrument.PlayInstrumentWell(from);
|
||||
from.SendLocalizedMessage(
|
||||
1008085); // You play your music and your target becomes angered. Whom do you wish them to attack?
|
||||
from.Target = new InternalSecondTarget(from, m_Instrument, creature);
|
||||
}
|
||||
from.SendLocalizedMessage(501587); // Whom do you wish to incite?
|
||||
from.Target = new InternalFirstTarget(from, instrument);
|
||||
}
|
||||
else
|
||||
|
||||
private class InternalFirstTarget : Target
|
||||
{
|
||||
from.SendLocalizedMessage(501589); // You can't incite that!
|
||||
}
|
||||
}
|
||||
}
|
||||
private readonly BaseInstrument m_Instrument;
|
||||
|
||||
private class InternalSecondTarget : Target
|
||||
{
|
||||
private readonly BaseCreature m_Creature;
|
||||
private readonly BaseInstrument m_Instrument;
|
||||
public InternalFirstTarget(Mobile from, BaseInstrument instrument) : base(
|
||||
BaseInstrument.GetBardRange(from, SkillName.Provocation),
|
||||
false,
|
||||
TargetFlags.None
|
||||
) =>
|
||||
m_Instrument = instrument;
|
||||
|
||||
public InternalSecondTarget(Mobile from, BaseInstrument instrument, BaseCreature creature) : base(
|
||||
BaseInstrument.GetBardRange(from, SkillName.Provocation), false, TargetFlags.None)
|
||||
{
|
||||
m_Instrument = instrument;
|
||||
m_Creature = creature;
|
||||
}
|
||||
|
||||
protected override void OnTarget(Mobile from, object targeted)
|
||||
{
|
||||
from.RevealingAction();
|
||||
|
||||
if (targeted is BaseCreature creature)
|
||||
{
|
||||
if (!m_Instrument.IsChildOf(from.Backpack))
|
||||
{
|
||||
from.SendLocalizedMessage(
|
||||
1062488); // The instrument you are trying to play is no longer in your backpack!
|
||||
}
|
||||
else if (m_Creature.Unprovokable)
|
||||
{
|
||||
from.SendLocalizedMessage(1049446); // You have no chance of provoking those creatures.
|
||||
}
|
||||
else if (creature.Unprovokable && !(creature is DemonKnight))
|
||||
{
|
||||
from.SendLocalizedMessage(1049446); // You have no chance of provoking those creatures.
|
||||
}
|
||||
else if (m_Creature.Map != creature.Map || !m_Creature.InRange(creature,
|
||||
BaseInstrument.GetBardRange(from, SkillName.Provocation)))
|
||||
{
|
||||
from.SendLocalizedMessage(
|
||||
1049450); // The creatures you are trying to provoke are too far away from each other for your music to have an effect.
|
||||
}
|
||||
else if (m_Creature != creature)
|
||||
{
|
||||
from.NextSkillTime = Core.TickCount + 10000;
|
||||
|
||||
double diff = (m_Instrument.GetDifficultyFor(m_Creature) + m_Instrument.GetDifficultyFor(creature)) *
|
||||
0.5 - 5.0;
|
||||
double music = from.Skills.Musicianship.Value;
|
||||
|
||||
if (music > 100.0)
|
||||
diff -= (music - 100.0) * 0.5;
|
||||
|
||||
if (from.CanBeHarmful(m_Creature, true) && from.CanBeHarmful(creature, true))
|
||||
protected override void OnTarget(Mobile from, object targeted)
|
||||
{
|
||||
if (!BaseInstrument.CheckMusicianship(from))
|
||||
{
|
||||
from.NextSkillTime = Core.TickCount + 5000;
|
||||
from.SendLocalizedMessage(500612); // You play poorly, and there is no effect.
|
||||
m_Instrument.PlayInstrumentBadly(from);
|
||||
m_Instrument.ConsumeUse(from);
|
||||
}
|
||||
else
|
||||
{
|
||||
// from.DoHarmful( m_Creature );
|
||||
// from.DoHarmful( creature );
|
||||
from.RevealingAction();
|
||||
|
||||
if (!from.CheckTargetSkill(SkillName.Provocation, creature, diff - 25.0, diff + 25.0))
|
||||
if (targeted is BaseCreature creature && from.CanBeHarmful(creature, true))
|
||||
{
|
||||
from.NextSkillTime = Core.TickCount + 5000;
|
||||
from.SendLocalizedMessage(501599); // Your music fails to incite enough anger.
|
||||
m_Instrument.PlayInstrumentBadly(from);
|
||||
m_Instrument.ConsumeUse(from);
|
||||
if (!m_Instrument.IsChildOf(from.Backpack))
|
||||
{
|
||||
from.SendLocalizedMessage(
|
||||
1062488
|
||||
); // The instrument you are trying to play is no longer in your backpack!
|
||||
}
|
||||
else if (creature.Controlled)
|
||||
{
|
||||
from.SendLocalizedMessage(501590); // They are too loyal to their master to be provoked.
|
||||
}
|
||||
else if (creature.IsParagon && BaseInstrument.GetBaseDifficulty(creature) >= 160.0)
|
||||
{
|
||||
from.SendLocalizedMessage(1049446); // You have no chance of provoking those creatures.
|
||||
}
|
||||
else
|
||||
{
|
||||
from.RevealingAction();
|
||||
m_Instrument.PlayInstrumentWell(from);
|
||||
from.SendLocalizedMessage(
|
||||
1008085
|
||||
); // You play your music and your target becomes angered. Whom do you wish them to attack?
|
||||
from.Target = new InternalSecondTarget(from, m_Instrument, creature);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(501602); // Your music succeeds, as you start a fight.
|
||||
m_Instrument.PlayInstrumentWell(from);
|
||||
m_Instrument.ConsumeUse(from);
|
||||
m_Creature.Provoke(from, creature, true);
|
||||
from.SendLocalizedMessage(501589); // You can't incite that!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(501593); // You can't tell someone to attack themselves!
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
private class InternalSecondTarget : Target
|
||||
{
|
||||
from.SendLocalizedMessage(501589); // You can't incite that!
|
||||
private readonly BaseCreature m_Creature;
|
||||
private readonly BaseInstrument m_Instrument;
|
||||
|
||||
public InternalSecondTarget(Mobile from, BaseInstrument instrument, BaseCreature creature) : base(
|
||||
BaseInstrument.GetBardRange(from, SkillName.Provocation),
|
||||
false,
|
||||
TargetFlags.None
|
||||
)
|
||||
{
|
||||
m_Instrument = instrument;
|
||||
m_Creature = creature;
|
||||
}
|
||||
|
||||
protected override void OnTarget(Mobile from, object targeted)
|
||||
{
|
||||
from.RevealingAction();
|
||||
|
||||
if (targeted is BaseCreature creature)
|
||||
{
|
||||
if (!m_Instrument.IsChildOf(from.Backpack))
|
||||
{
|
||||
from.SendLocalizedMessage(
|
||||
1062488
|
||||
); // The instrument you are trying to play is no longer in your backpack!
|
||||
}
|
||||
else if (m_Creature.Unprovokable)
|
||||
{
|
||||
from.SendLocalizedMessage(1049446); // You have no chance of provoking those creatures.
|
||||
}
|
||||
else if (creature.Unprovokable && !(creature is DemonKnight))
|
||||
{
|
||||
from.SendLocalizedMessage(1049446); // You have no chance of provoking those creatures.
|
||||
}
|
||||
else if (m_Creature.Map != creature.Map || !m_Creature.InRange(
|
||||
creature,
|
||||
BaseInstrument.GetBardRange(from, SkillName.Provocation)
|
||||
))
|
||||
{
|
||||
from.SendLocalizedMessage(
|
||||
1049450
|
||||
); // The creatures you are trying to provoke are too far away from each other for your music to have an effect.
|
||||
}
|
||||
else if (m_Creature != creature)
|
||||
{
|
||||
from.NextSkillTime = Core.TickCount + 10000;
|
||||
|
||||
var diff = (m_Instrument.GetDifficultyFor(m_Creature) + m_Instrument.GetDifficultyFor(creature)) *
|
||||
0.5 - 5.0;
|
||||
var music = from.Skills.Musicianship.Value;
|
||||
|
||||
if (music > 100.0)
|
||||
diff -= (music - 100.0) * 0.5;
|
||||
|
||||
if (from.CanBeHarmful(m_Creature, true) && from.CanBeHarmful(creature, true))
|
||||
{
|
||||
if (!BaseInstrument.CheckMusicianship(from))
|
||||
{
|
||||
from.NextSkillTime = Core.TickCount + 5000;
|
||||
from.SendLocalizedMessage(500612); // You play poorly, and there is no effect.
|
||||
m_Instrument.PlayInstrumentBadly(from);
|
||||
m_Instrument.ConsumeUse(from);
|
||||
}
|
||||
else
|
||||
{
|
||||
// from.DoHarmful( m_Creature );
|
||||
// from.DoHarmful( creature );
|
||||
|
||||
if (!from.CheckTargetSkill(SkillName.Provocation, creature, diff - 25.0, diff + 25.0))
|
||||
{
|
||||
from.NextSkillTime = Core.TickCount + 5000;
|
||||
from.SendLocalizedMessage(501599); // Your music fails to incite enough anger.
|
||||
m_Instrument.PlayInstrumentBadly(from);
|
||||
m_Instrument.ConsumeUse(from);
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(501602); // Your music succeeds, as you start a fight.
|
||||
m_Instrument.PlayInstrumentWell(from);
|
||||
m_Instrument.ConsumeUse(from);
|
||||
m_Creature.Provoke(from, creature, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(501593); // You can't tell someone to attack themselves!
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(501589); // You can't incite that!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,124 +6,134 @@ using Server.Targeting;
|
|||
|
||||
namespace Server.SkillHandlers
|
||||
{
|
||||
public static class RemoveTrap
|
||||
{
|
||||
public static void Initialize()
|
||||
public static class RemoveTrap
|
||||
{
|
||||
SkillInfo.Table[(int)SkillName.RemoveTrap].Callback = OnUse;
|
||||
}
|
||||
|
||||
public static TimeSpan OnUse(Mobile m)
|
||||
{
|
||||
if (m.Skills.Lockpicking.Value < 50)
|
||||
{
|
||||
m.SendLocalizedMessage(502366); // You do not know enough about locks. Become better at picking locks.
|
||||
}
|
||||
else if (m.Skills.DetectHidden.Value < 50)
|
||||
{
|
||||
m.SendLocalizedMessage(502367); // You are not perceptive enough. Become better at detect hidden.
|
||||
}
|
||||
else
|
||||
{
|
||||
m.Target = new InternalTarget();
|
||||
|
||||
m.SendLocalizedMessage(502368); // Which trap will you attempt to disarm?
|
||||
}
|
||||
|
||||
return TimeSpan.FromSeconds(10.0); // 10 second delay before being able to re-use a skill
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
public InternalTarget() : base(2, false, TargetFlags.None)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnTarget(Mobile from, object targeted)
|
||||
{
|
||||
if (targeted is Mobile)
|
||||
public static void Initialize()
|
||||
{
|
||||
from.SendLocalizedMessage(502816); // You feel that such an action would be inappropriate
|
||||
SkillInfo.Table[(int)SkillName.RemoveTrap].Callback = OnUse;
|
||||
}
|
||||
else if (targeted is TrappableContainer targ)
|
||||
|
||||
public static TimeSpan OnUse(Mobile m)
|
||||
{
|
||||
from.Direction = from.GetDirectionTo(targ);
|
||||
|
||||
if (targ.TrapType == TrapType.None)
|
||||
{
|
||||
from.SendLocalizedMessage(502373); // That doesn't appear to be trapped
|
||||
return;
|
||||
}
|
||||
|
||||
from.PlaySound(0x241);
|
||||
|
||||
if (from.CheckTargetSkill(SkillName.RemoveTrap, targ, targ.TrapPower, targ.TrapPower + 30))
|
||||
{
|
||||
targ.TrapPower = 0;
|
||||
targ.TrapLevel = 0;
|
||||
targ.TrapType = TrapType.None;
|
||||
from.SendLocalizedMessage(502377); // You successfully render the trap harmless
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(502372); // You fail to disarm the trap... but you don't set it off
|
||||
}
|
||||
}
|
||||
else if (targeted is BaseFactionTrap trap)
|
||||
{
|
||||
Faction faction = Faction.Find(from);
|
||||
FactionTrapRemovalKit kit = from.Backpack?.FindItemByType<FactionTrapRemovalKit>();
|
||||
|
||||
bool isOwner = trap.Placer == from || trap.Faction?.IsCommander(from) == true;
|
||||
|
||||
if (faction == null)
|
||||
{
|
||||
from.SendLocalizedMessage(
|
||||
1010538); // You may not disarm faction traps unless you are in an opposing faction
|
||||
}
|
||||
else if (trap.Faction != null && faction == trap.Faction && !isOwner)
|
||||
{
|
||||
from.SendLocalizedMessage(1010537); // You may not disarm traps set by your own faction!
|
||||
}
|
||||
else if (!isOwner && kit == null)
|
||||
{
|
||||
from.SendLocalizedMessage(
|
||||
1042530); // You must have a trap removal kit at the base level of your pack to disarm a faction trap.
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((Core.ML && isOwner) || (from.CheckTargetSkill(SkillName.RemoveTrap, trap, 80.0, 100.0) &&
|
||||
from.CheckTargetSkill(SkillName.Tinkering, trap, 80.0, 100.0)))
|
||||
if (m.Skills.Lockpicking.Value < 50)
|
||||
{
|
||||
from.PrivateOverheadMessage(MessageType.Regular, trap.MessageHue, trap.DisarmMessage,
|
||||
from.NetState);
|
||||
|
||||
if (!isOwner)
|
||||
{
|
||||
int silver = faction.AwardSilver(from, trap.SilverFromDisarm);
|
||||
|
||||
if (silver > 0)
|
||||
from.SendLocalizedMessage(1008113, true,
|
||||
silver.ToString(
|
||||
"N0")); // You have been granted faction silver for removing the enemy trap :
|
||||
}
|
||||
|
||||
trap.Delete();
|
||||
m.SendLocalizedMessage(502366); // You do not know enough about locks. Become better at picking locks.
|
||||
}
|
||||
else if (m.Skills.DetectHidden.Value < 50)
|
||||
{
|
||||
m.SendLocalizedMessage(502367); // You are not perceptive enough. Become better at detect hidden.
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(502372); // You fail to disarm the trap... but you don't set it off
|
||||
m.Target = new InternalTarget();
|
||||
|
||||
m.SendLocalizedMessage(502368); // Which trap will you attempt to disarm?
|
||||
}
|
||||
|
||||
if (!isOwner)
|
||||
kit.ConsumeCharge(from);
|
||||
}
|
||||
return TimeSpan.FromSeconds(10.0); // 10 second delay before being able to re-use a skill
|
||||
}
|
||||
else
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
from.SendLocalizedMessage(502373); // That doesn't appear to be trapped
|
||||
public InternalTarget() : base(2, false, TargetFlags.None)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnTarget(Mobile from, object targeted)
|
||||
{
|
||||
if (targeted is Mobile)
|
||||
{
|
||||
from.SendLocalizedMessage(502816); // You feel that such an action would be inappropriate
|
||||
}
|
||||
else if (targeted is TrappableContainer targ)
|
||||
{
|
||||
from.Direction = from.GetDirectionTo(targ);
|
||||
|
||||
if (targ.TrapType == TrapType.None)
|
||||
{
|
||||
from.SendLocalizedMessage(502373); // That doesn't appear to be trapped
|
||||
return;
|
||||
}
|
||||
|
||||
from.PlaySound(0x241);
|
||||
|
||||
if (from.CheckTargetSkill(SkillName.RemoveTrap, targ, targ.TrapPower, targ.TrapPower + 30))
|
||||
{
|
||||
targ.TrapPower = 0;
|
||||
targ.TrapLevel = 0;
|
||||
targ.TrapType = TrapType.None;
|
||||
from.SendLocalizedMessage(502377); // You successfully render the trap harmless
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(502372); // You fail to disarm the trap... but you don't set it off
|
||||
}
|
||||
}
|
||||
else if (targeted is BaseFactionTrap trap)
|
||||
{
|
||||
var faction = Faction.Find(from);
|
||||
var kit = from.Backpack?.FindItemByType<FactionTrapRemovalKit>();
|
||||
|
||||
var isOwner = trap.Placer == from || trap.Faction?.IsCommander(from) == true;
|
||||
|
||||
if (faction == null)
|
||||
{
|
||||
from.SendLocalizedMessage(
|
||||
1010538
|
||||
); // You may not disarm faction traps unless you are in an opposing faction
|
||||
}
|
||||
else if (trap.Faction != null && faction == trap.Faction && !isOwner)
|
||||
{
|
||||
from.SendLocalizedMessage(1010537); // You may not disarm traps set by your own faction!
|
||||
}
|
||||
else if (!isOwner && kit == null)
|
||||
{
|
||||
from.SendLocalizedMessage(
|
||||
1042530
|
||||
); // You must have a trap removal kit at the base level of your pack to disarm a faction trap.
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Core.ML && isOwner || @from.CheckTargetSkill(SkillName.RemoveTrap, trap, 80.0, 100.0) &&
|
||||
@from.CheckTargetSkill(SkillName.Tinkering, trap, 80.0, 100.0))
|
||||
{
|
||||
from.PrivateOverheadMessage(
|
||||
MessageType.Regular,
|
||||
trap.MessageHue,
|
||||
trap.DisarmMessage,
|
||||
from.NetState
|
||||
);
|
||||
|
||||
if (!isOwner)
|
||||
{
|
||||
var silver = faction.AwardSilver(from, trap.SilverFromDisarm);
|
||||
|
||||
if (silver > 0)
|
||||
from.SendLocalizedMessage(
|
||||
1008113,
|
||||
true,
|
||||
silver.ToString(
|
||||
"N0"
|
||||
)
|
||||
); // You have been granted faction silver for removing the enemy trap :
|
||||
}
|
||||
|
||||
trap.Delete();
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(502372); // You fail to disarm the trap... but you don't set it off
|
||||
}
|
||||
|
||||
if (!isOwner)
|
||||
kit.ConsumeCharge(from);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(502373); // That doesn't appear to be trapped
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,98 +1,97 @@
|
|||
using Server.Items;
|
||||
using Server.Misc;
|
||||
using Server.Mobiles;
|
||||
using Server.Network;
|
||||
using Server.Regions;
|
||||
|
||||
namespace Server.SkillHandlers
|
||||
{
|
||||
public static class Snooping
|
||||
{
|
||||
public static void Configure()
|
||||
public static class Snooping
|
||||
{
|
||||
Container.SnoopHandler = Container_Snoop;
|
||||
public static void Configure()
|
||||
{
|
||||
Container.SnoopHandler = Container_Snoop;
|
||||
}
|
||||
|
||||
public static bool CheckSnoopAllowed(Mobile from, Mobile to)
|
||||
{
|
||||
var map = from.Map;
|
||||
|
||||
if (to.Player)
|
||||
return from.CanBeHarmful(to, false, true); // normal restrictions
|
||||
|
||||
if ((map?.Rules & MapRules.HarmfulRestrictions) == 0)
|
||||
return true; // felucca you can snoop anybody
|
||||
|
||||
var reg = to.Region.GetRegion<GuardedRegion>();
|
||||
|
||||
if (reg?.IsDisabled() != true)
|
||||
return true; // not in town? we can snoop any npc
|
||||
|
||||
return !to.Body.IsHuman || to is BaseCreature cret && (cret.AlwaysAttackable || cret.AlwaysMurderer);
|
||||
}
|
||||
|
||||
public static void Container_Snoop(Container cont, Mobile from)
|
||||
{
|
||||
if (from.AccessLevel > AccessLevel.Player || from.InRange(cont.GetWorldLocation(), 1))
|
||||
{
|
||||
var root = cont.RootParent as Mobile;
|
||||
|
||||
if (root?.Alive == false)
|
||||
return;
|
||||
|
||||
if (root?.AccessLevel > AccessLevel.Player && from.AccessLevel == AccessLevel.Player)
|
||||
{
|
||||
from.SendLocalizedMessage(500209); // You can not peek into the container.
|
||||
return;
|
||||
}
|
||||
|
||||
if (root?.AccessLevel == AccessLevel.Player && !CheckSnoopAllowed(from, root))
|
||||
{
|
||||
from.SendLocalizedMessage(1001018); // You cannot perform negative acts on your target.
|
||||
return;
|
||||
}
|
||||
|
||||
if (root?.AccessLevel == AccessLevel.Player &&
|
||||
from.Skills.Snooping.Value < Utility.Random(100))
|
||||
{
|
||||
var map = from.Map;
|
||||
|
||||
if (map != null)
|
||||
{
|
||||
var message = $"You notice {from.Name} attempting to peek into {root.Name}'s belongings.";
|
||||
|
||||
var eable = map.GetClientsInRange(from.Location, 8);
|
||||
|
||||
foreach (var ns in eable)
|
||||
if (ns.Mobile != from)
|
||||
ns.Mobile.SendMessage(message);
|
||||
|
||||
eable.Free();
|
||||
}
|
||||
}
|
||||
|
||||
if (from.AccessLevel == AccessLevel.Player)
|
||||
Titles.AwardKarma(from, -4, true);
|
||||
|
||||
if (from.AccessLevel > AccessLevel.Player || from.CheckTargetSkill(SkillName.Snooping, cont, 0.0, 100.0))
|
||||
{
|
||||
if (cont is TrappableContainer container && container.ExecuteTrap(from))
|
||||
return;
|
||||
|
||||
cont.DisplayTo(from);
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(500210); // You failed to peek into the container.
|
||||
|
||||
if (from.Skills.Hiding.Value / 2 < Utility.Random(100))
|
||||
from.RevealingAction();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(500446); // That is too far away.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool CheckSnoopAllowed(Mobile from, Mobile to)
|
||||
{
|
||||
Map map = from.Map;
|
||||
|
||||
if (to.Player)
|
||||
return from.CanBeHarmful(to, false, true); // normal restrictions
|
||||
|
||||
if ((map?.Rules & MapRules.HarmfulRestrictions) == 0)
|
||||
return true; // felucca you can snoop anybody
|
||||
|
||||
GuardedRegion reg = to.Region.GetRegion<GuardedRegion>();
|
||||
|
||||
if (reg?.IsDisabled() != true)
|
||||
return true; // not in town? we can snoop any npc
|
||||
|
||||
return !to.Body.IsHuman || (to is BaseCreature cret && (cret.AlwaysAttackable || cret.AlwaysMurderer));
|
||||
}
|
||||
|
||||
public static void Container_Snoop(Container cont, Mobile from)
|
||||
{
|
||||
if (from.AccessLevel > AccessLevel.Player || from.InRange(cont.GetWorldLocation(), 1))
|
||||
{
|
||||
Mobile root = cont.RootParent as Mobile;
|
||||
|
||||
if (root?.Alive == false)
|
||||
return;
|
||||
|
||||
if (root?.AccessLevel > AccessLevel.Player && from.AccessLevel == AccessLevel.Player)
|
||||
{
|
||||
from.SendLocalizedMessage(500209); // You can not peek into the container.
|
||||
return;
|
||||
}
|
||||
|
||||
if (root?.AccessLevel == AccessLevel.Player && !CheckSnoopAllowed(from, root))
|
||||
{
|
||||
from.SendLocalizedMessage(1001018); // You cannot perform negative acts on your target.
|
||||
return;
|
||||
}
|
||||
|
||||
if (root?.AccessLevel == AccessLevel.Player &&
|
||||
from.Skills.Snooping.Value < Utility.Random(100))
|
||||
{
|
||||
Map map = from.Map;
|
||||
|
||||
if (map != null)
|
||||
{
|
||||
string message = $"You notice {from.Name} attempting to peek into {root.Name}'s belongings.";
|
||||
|
||||
IPooledEnumerable<NetState> eable = map.GetClientsInRange(from.Location, 8);
|
||||
|
||||
foreach (NetState ns in eable)
|
||||
if (ns.Mobile != from)
|
||||
ns.Mobile.SendMessage(message);
|
||||
|
||||
eable.Free();
|
||||
}
|
||||
}
|
||||
|
||||
if (from.AccessLevel == AccessLevel.Player)
|
||||
Titles.AwardKarma(from, -4, true);
|
||||
|
||||
if (from.AccessLevel > AccessLevel.Player || from.CheckTargetSkill(SkillName.Snooping, cont, 0.0, 100.0))
|
||||
{
|
||||
if (cont is TrappableContainer container && container.ExecuteTrap(from))
|
||||
return;
|
||||
|
||||
cont.DisplayTo(from);
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(500210); // You failed to peek into the container.
|
||||
|
||||
if (from.Skills.Hiding.Value / 2 < Utility.Random(100))
|
||||
from.RevealingAction();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(500446); // That is too far away.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,180 +6,180 @@ using Server.Spells;
|
|||
|
||||
namespace Server.SkillHandlers
|
||||
{
|
||||
public static class SpiritSpeak
|
||||
{
|
||||
public static void Initialize()
|
||||
public static class SpiritSpeak
|
||||
{
|
||||
SkillInfo.Table[32].Callback = OnUse;
|
||||
}
|
||||
|
||||
public static TimeSpan OnUse(Mobile m)
|
||||
{
|
||||
if (Core.AOS)
|
||||
{
|
||||
Spell spell = new SpiritSpeakSpell(m);
|
||||
|
||||
spell.Cast();
|
||||
|
||||
if (spell.IsCasting)
|
||||
return TimeSpan.FromSeconds(5.0);
|
||||
|
||||
return TimeSpan.Zero;
|
||||
}
|
||||
|
||||
m.RevealingAction();
|
||||
|
||||
if (m.CheckSkill(SkillName.SpiritSpeak, 0, 100))
|
||||
{
|
||||
if (!m.CanHearGhosts)
|
||||
public static void Initialize()
|
||||
{
|
||||
Timer t = new SpiritSpeakTimer(m);
|
||||
double secs = m.Skills.SpiritSpeak.Base / 50;
|
||||
secs *= 90;
|
||||
if (secs < 15)
|
||||
secs = 15;
|
||||
|
||||
t.Delay = TimeSpan.FromSeconds(secs); // 15seconds to 3 minutes
|
||||
t.Start();
|
||||
m.CanHearGhosts = true;
|
||||
SkillInfo.Table[32].Callback = OnUse;
|
||||
}
|
||||
|
||||
m.PlaySound(0x24A);
|
||||
m.SendLocalizedMessage(502444); // You contact the neitherworld.
|
||||
}
|
||||
else
|
||||
{
|
||||
m.SendLocalizedMessage(502443); // You fail to contact the neitherworld.
|
||||
m.CanHearGhosts = false;
|
||||
}
|
||||
|
||||
return TimeSpan.FromSeconds(1.0);
|
||||
}
|
||||
|
||||
private class SpiritSpeakTimer : Timer
|
||||
{
|
||||
private readonly Mobile m_Owner;
|
||||
|
||||
public SpiritSpeakTimer(Mobile m) : base(TimeSpan.FromMinutes(2.0))
|
||||
{
|
||||
m_Owner = m;
|
||||
Priority = TimerPriority.FiveSeconds;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
m_Owner.CanHearGhosts = false;
|
||||
m_Owner.SendLocalizedMessage(502445); // You feel your contact with the neitherworld fading.
|
||||
}
|
||||
}
|
||||
|
||||
private class SpiritSpeakSpell : Spell
|
||||
{
|
||||
private static readonly SpellInfo m_Info = new SpellInfo("Spirit Speak", "", 269);
|
||||
|
||||
public SpiritSpeakSpell(Mobile caster) : base(caster, null, m_Info)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool BlockedByHorrificBeast => false;
|
||||
|
||||
public override bool ClearHandsOnCast => false;
|
||||
|
||||
public override double CastDelayFastScalar => 0;
|
||||
public override TimeSpan CastDelayBase => TimeSpan.FromSeconds(1.0);
|
||||
|
||||
public override bool CheckNextSpellTime => false;
|
||||
|
||||
public override int GetMana() => 0;
|
||||
|
||||
public override void OnCasterHurt()
|
||||
{
|
||||
if (IsCasting)
|
||||
Disturb(DisturbType.Hurt, false, true);
|
||||
}
|
||||
|
||||
public override bool ConsumeReagents() => true;
|
||||
|
||||
public override bool CheckFizzle() => true;
|
||||
|
||||
public override void OnDisturb(DisturbType type, bool message)
|
||||
{
|
||||
Caster.NextSkillTime = Core.TickCount;
|
||||
|
||||
base.OnDisturb(type, message);
|
||||
}
|
||||
|
||||
public override bool CheckDisturb(DisturbType type, bool checkFirst, bool resistable)
|
||||
{
|
||||
if (type == DisturbType.EquipRequest || type == DisturbType.UseRequest)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void SayMantra()
|
||||
{
|
||||
// Anh Mi Sah Ko
|
||||
Caster.PublicOverheadMessage(MessageType.Regular, 0x3B2, 1062074, "", false);
|
||||
Caster.PlaySound(0x24A);
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
IPooledEnumerable<Corpse> eable = Caster.GetItemsInRange<Corpse>(3);
|
||||
Corpse toChannel = eable.FirstOrDefault(corpse => !corpse.Channeled);
|
||||
eable.Free();
|
||||
|
||||
int min = 1 + (int)(Caster.Skills.SpiritSpeak.Value * 0.25);
|
||||
int max = min + 4;
|
||||
|
||||
int mana, number;
|
||||
|
||||
if (toChannel != null)
|
||||
public static TimeSpan OnUse(Mobile m)
|
||||
{
|
||||
mana = 0;
|
||||
number = 1061287; // You channel energy from a nearby corpse to heal your wounds.
|
||||
}
|
||||
else
|
||||
{
|
||||
mana = 10;
|
||||
number = 1061286; // You channel your own spiritual energy to heal your wounds.
|
||||
}
|
||||
|
||||
if (Caster.Mana < mana)
|
||||
{
|
||||
Caster.SendLocalizedMessage(1061285); // You lack the mana required to use this skill.
|
||||
}
|
||||
else
|
||||
{
|
||||
Caster.CheckSkill(SkillName.SpiritSpeak, 0.0, 120.0);
|
||||
|
||||
if (Utility.RandomDouble() > Caster.Skills.SpiritSpeak.Value / 100.0)
|
||||
{
|
||||
Caster.SendLocalizedMessage(502443); // You fail your attempt at contacting the netherworld.
|
||||
}
|
||||
else
|
||||
{
|
||||
if (toChannel != null)
|
||||
if (Core.AOS)
|
||||
{
|
||||
toChannel.Channeled = true;
|
||||
toChannel.Hue = 0x835;
|
||||
Spell spell = new SpiritSpeakSpell(m);
|
||||
|
||||
spell.Cast();
|
||||
|
||||
if (spell.IsCasting)
|
||||
return TimeSpan.FromSeconds(5.0);
|
||||
|
||||
return TimeSpan.Zero;
|
||||
}
|
||||
|
||||
Caster.Mana -= mana;
|
||||
Caster.SendLocalizedMessage(number);
|
||||
m.RevealingAction();
|
||||
|
||||
if (min > max)
|
||||
min = max;
|
||||
if (m.CheckSkill(SkillName.SpiritSpeak, 0, 100))
|
||||
{
|
||||
if (!m.CanHearGhosts)
|
||||
{
|
||||
Timer t = new SpiritSpeakTimer(m);
|
||||
var secs = m.Skills.SpiritSpeak.Base / 50;
|
||||
secs *= 90;
|
||||
if (secs < 15)
|
||||
secs = 15;
|
||||
|
||||
Caster.Hits += Utility.RandomMinMax(min, max);
|
||||
t.Delay = TimeSpan.FromSeconds(secs); // 15seconds to 3 minutes
|
||||
t.Start();
|
||||
m.CanHearGhosts = true;
|
||||
}
|
||||
|
||||
Caster.FixedParticles(0x375A, 1, 15, 9501, 2100, 4, EffectLayer.Waist);
|
||||
}
|
||||
m.PlaySound(0x24A);
|
||||
m.SendLocalizedMessage(502444); // You contact the neitherworld.
|
||||
}
|
||||
else
|
||||
{
|
||||
m.SendLocalizedMessage(502443); // You fail to contact the neitherworld.
|
||||
m.CanHearGhosts = false;
|
||||
}
|
||||
|
||||
return TimeSpan.FromSeconds(1.0);
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
private class SpiritSpeakTimer : Timer
|
||||
{
|
||||
private readonly Mobile m_Owner;
|
||||
|
||||
public SpiritSpeakTimer(Mobile m) : base(TimeSpan.FromMinutes(2.0))
|
||||
{
|
||||
m_Owner = m;
|
||||
Priority = TimerPriority.FiveSeconds;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
m_Owner.CanHearGhosts = false;
|
||||
m_Owner.SendLocalizedMessage(502445); // You feel your contact with the neitherworld fading.
|
||||
}
|
||||
}
|
||||
|
||||
private class SpiritSpeakSpell : Spell
|
||||
{
|
||||
private static readonly SpellInfo m_Info = new SpellInfo("Spirit Speak", "", 269);
|
||||
|
||||
public SpiritSpeakSpell(Mobile caster) : base(caster, null, m_Info)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool BlockedByHorrificBeast => false;
|
||||
|
||||
public override bool ClearHandsOnCast => false;
|
||||
|
||||
public override double CastDelayFastScalar => 0;
|
||||
public override TimeSpan CastDelayBase => TimeSpan.FromSeconds(1.0);
|
||||
|
||||
public override bool CheckNextSpellTime => false;
|
||||
|
||||
public override int GetMana() => 0;
|
||||
|
||||
public override void OnCasterHurt()
|
||||
{
|
||||
if (IsCasting)
|
||||
Disturb(DisturbType.Hurt, false, true);
|
||||
}
|
||||
|
||||
public override bool ConsumeReagents() => true;
|
||||
|
||||
public override bool CheckFizzle() => true;
|
||||
|
||||
public override void OnDisturb(DisturbType type, bool message)
|
||||
{
|
||||
Caster.NextSkillTime = Core.TickCount;
|
||||
|
||||
base.OnDisturb(type, message);
|
||||
}
|
||||
|
||||
public override bool CheckDisturb(DisturbType type, bool checkFirst, bool resistable)
|
||||
{
|
||||
if (type == DisturbType.EquipRequest || type == DisturbType.UseRequest)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void SayMantra()
|
||||
{
|
||||
// Anh Mi Sah Ko
|
||||
Caster.PublicOverheadMessage(MessageType.Regular, 0x3B2, 1062074, "", false);
|
||||
Caster.PlaySound(0x24A);
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
var eable = Caster.GetItemsInRange<Corpse>(3);
|
||||
var toChannel = eable.FirstOrDefault(corpse => !corpse.Channeled);
|
||||
eable.Free();
|
||||
|
||||
var min = 1 + (int)(Caster.Skills.SpiritSpeak.Value * 0.25);
|
||||
var max = min + 4;
|
||||
|
||||
int mana, number;
|
||||
|
||||
if (toChannel != null)
|
||||
{
|
||||
mana = 0;
|
||||
number = 1061287; // You channel energy from a nearby corpse to heal your wounds.
|
||||
}
|
||||
else
|
||||
{
|
||||
mana = 10;
|
||||
number = 1061286; // You channel your own spiritual energy to heal your wounds.
|
||||
}
|
||||
|
||||
if (Caster.Mana < mana)
|
||||
{
|
||||
Caster.SendLocalizedMessage(1061285); // You lack the mana required to use this skill.
|
||||
}
|
||||
else
|
||||
{
|
||||
Caster.CheckSkill(SkillName.SpiritSpeak, 0.0, 120.0);
|
||||
|
||||
if (Utility.RandomDouble() > Caster.Skills.SpiritSpeak.Value / 100.0)
|
||||
{
|
||||
Caster.SendLocalizedMessage(502443); // You fail your attempt at contacting the netherworld.
|
||||
}
|
||||
else
|
||||
{
|
||||
if (toChannel != null)
|
||||
{
|
||||
toChannel.Channeled = true;
|
||||
toChannel.Hue = 0x835;
|
||||
}
|
||||
|
||||
Caster.Mana -= mana;
|
||||
Caster.SendLocalizedMessage(number);
|
||||
|
||||
if (min > max)
|
||||
min = max;
|
||||
|
||||
Caster.Hits += Utility.RandomMinMax(min, max);
|
||||
|
||||
Caster.FixedParticles(0x375A, 1, 15, 9501, 2100, 4, EffectLayer.Waist);
|
||||
}
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ using Server.Engines.ConPVP;
|
|||
using Server.Factions;
|
||||
using Server.Items;
|
||||
using Server.Mobiles;
|
||||
using Server.Network;
|
||||
using Server.Spells;
|
||||
using Server.Spells.Fifth;
|
||||
using Server.Spells.Ninjitsu;
|
||||
|
|
@ -13,451 +12,469 @@ using Server.Targeting;
|
|||
|
||||
namespace Server.SkillHandlers
|
||||
{
|
||||
public static class Stealing
|
||||
{
|
||||
public static readonly bool ClassicMode = false;
|
||||
public static readonly bool SuspendOnMurder = false;
|
||||
|
||||
public static void Initialize()
|
||||
public static class Stealing
|
||||
{
|
||||
SkillInfo.Table[33].Callback = OnUse;
|
||||
}
|
||||
public static readonly bool ClassicMode = false;
|
||||
public static readonly bool SuspendOnMurder = false;
|
||||
|
||||
public static bool IsInGuild(Mobile m) => m is PlayerMobile mobile && mobile.NpcGuild == NpcGuild.ThievesGuild;
|
||||
|
||||
public static bool IsInnocentTo(Mobile from, Mobile to) => Notoriety.Compute(from, to) == Notoriety.Innocent;
|
||||
|
||||
public static bool IsEmptyHanded(Mobile from)
|
||||
{
|
||||
if (from.FindItemOnLayer(Layer.OneHanded) != null)
|
||||
return false;
|
||||
|
||||
if (from.FindItemOnLayer(Layer.TwoHanded) != null)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static TimeSpan OnUse(Mobile m)
|
||||
{
|
||||
if (!IsEmptyHanded(m))
|
||||
{
|
||||
m.SendLocalizedMessage(1005584); // Both hands must be free to steal.
|
||||
}
|
||||
else if (m.Region.IsPartOf<SafeZone>())
|
||||
{
|
||||
m.SendMessage("You may not steal in this area.");
|
||||
}
|
||||
else
|
||||
{
|
||||
m.Target = new StealingTarget(m);
|
||||
m.RevealingAction();
|
||||
|
||||
m.SendLocalizedMessage(502698); // Which item do you want to steal?
|
||||
}
|
||||
|
||||
return TimeSpan.FromSeconds(10.0);
|
||||
}
|
||||
|
||||
private class StealingTarget : Target
|
||||
{
|
||||
private readonly Mobile m_Thief;
|
||||
|
||||
public StealingTarget(Mobile thief) : base(1, false, TargetFlags.None)
|
||||
{
|
||||
m_Thief = thief;
|
||||
AllowNonlocal = true;
|
||||
}
|
||||
|
||||
private Item TryStealItem(Item toSteal, ref bool caught)
|
||||
{
|
||||
Item stolen = null;
|
||||
|
||||
IEntity root = toSteal.RootParent;
|
||||
Mobile mobRoot = root as Mobile;
|
||||
|
||||
StealableArtifactsSpawner.StealableInstance si = null;
|
||||
if (toSteal.Parent == null || !toSteal.Movable)
|
||||
si = StealableArtifactsSpawner.GetStealableInstance(toSteal);
|
||||
|
||||
if (!IsEmptyHanded(m_Thief))
|
||||
public static void Initialize()
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(1005584); // Both hands must be free to steal.
|
||||
SkillInfo.Table[33].Callback = OnUse;
|
||||
}
|
||||
else if (m_Thief.Region.IsPartOf<SafeZone>())
|
||||
{
|
||||
m_Thief.SendMessage("You may not steal in this area.");
|
||||
}
|
||||
else if (mobRoot?.Player == true && !IsInGuild(m_Thief))
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(1005596); // You must be in the thieves guild to steal from other players.
|
||||
}
|
||||
else if (SuspendOnMurder && mobRoot?.Player == true && IsInGuild(m_Thief) &&
|
||||
m_Thief.Kills > 0)
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(502706); // You are currently suspended from the thieves guild.
|
||||
}
|
||||
else if (root is BaseVendor vendor && vendor.IsInvulnerable)
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(1005598); // You can't steal from shopkeepers.
|
||||
}
|
||||
else if (root is PlayerVendor)
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(502709); // You can't steal from vendors.
|
||||
}
|
||||
else if (!m_Thief.CanSee(toSteal))
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(500237); // Target can not be seen.
|
||||
}
|
||||
else if (m_Thief.Backpack?.CheckHold(m_Thief, toSteal, false, true) != true)
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(1048147); // Your backpack can't hold anything else.
|
||||
}
|
||||
else if (toSteal is Sigil sig)
|
||||
{
|
||||
PlayerState pl = PlayerState.Find(m_Thief);
|
||||
Faction faction = pl?.Faction;
|
||||
|
||||
if (!m_Thief.InRange(sig.GetWorldLocation(), 1))
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(502703); // You must be standing next to an item to steal it.
|
||||
}
|
||||
else if (root != null) // not on the ground
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(502710); // You can't steal that!
|
||||
}
|
||||
else if (faction != null)
|
||||
{
|
||||
if (!m_Thief.CanBeginAction<IncognitoSpell>())
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(1010581); // You cannot steal the sigil when you are incognito
|
||||
}
|
||||
else if (DisguiseTimers.IsDisguised(m_Thief))
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(1010583); // You cannot steal the sigil while disguised
|
||||
}
|
||||
else if (!m_Thief.CanBeginAction<PolymorphSpell>())
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(1010582); // You cannot steal the sigil while polymorphed
|
||||
}
|
||||
else if (TransformationSpellHelper.UnderTransformation(m_Thief))
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(1061622); // You cannot steal the sigil while in that form.
|
||||
}
|
||||
else if (AnimalForm.UnderTransformation(m_Thief))
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(1063222); // You cannot steal the sigil while mimicking an animal.
|
||||
}
|
||||
else if (pl.IsLeaving)
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(
|
||||
1005589); // You are currently quitting a faction and cannot steal the town sigil
|
||||
}
|
||||
else if (sig.IsBeingCorrupted && sig.LastMonolith.Faction == faction)
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(1005590); // You cannot steal your own sigil
|
||||
}
|
||||
else if (sig.IsPurifying)
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(1005592); // You cannot steal this sigil until it has been purified
|
||||
}
|
||||
else if (m_Thief.CheckTargetSkill(SkillName.Stealing, toSteal, 80.0, 80.0))
|
||||
{
|
||||
if (Sigil.ExistsOn(m_Thief))
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(
|
||||
1010258); // The sigil has gone back to its home location because you already have a sigil.
|
||||
}
|
||||
else if (m_Thief?.Backpack.CheckHold(m_Thief, sig, false, true) != true)
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(
|
||||
1010259); // The sigil has gone home because your backpack is full
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sig.IsBeingCorrupted)
|
||||
sig.GraceStart = DateTime.UtcNow; // begin grace period
|
||||
public static bool IsInGuild(Mobile m) => m is PlayerMobile mobile && mobile.NpcGuild == NpcGuild.ThievesGuild;
|
||||
|
||||
m_Thief.SendLocalizedMessage(1010586); // YOU STOLE THE SIGIL!!! (woah, calm down now)
|
||||
public static bool IsInnocentTo(Mobile from, Mobile to) => Notoriety.Compute(from, to) == Notoriety.Innocent;
|
||||
|
||||
if (sig.LastMonolith?.Sigil != null)
|
||||
public static bool IsEmptyHanded(Mobile from)
|
||||
{
|
||||
if (from.FindItemOnLayer(Layer.OneHanded) != null)
|
||||
return false;
|
||||
|
||||
if (from.FindItemOnLayer(Layer.TwoHanded) != null)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static TimeSpan OnUse(Mobile m)
|
||||
{
|
||||
if (!IsEmptyHanded(m))
|
||||
{
|
||||
m.SendLocalizedMessage(1005584); // Both hands must be free to steal.
|
||||
}
|
||||
else if (m.Region.IsPartOf<SafeZone>())
|
||||
{
|
||||
m.SendMessage("You may not steal in this area.");
|
||||
}
|
||||
else
|
||||
{
|
||||
m.Target = new StealingTarget(m);
|
||||
m.RevealingAction();
|
||||
|
||||
m.SendLocalizedMessage(502698); // Which item do you want to steal?
|
||||
}
|
||||
|
||||
return TimeSpan.FromSeconds(10.0);
|
||||
}
|
||||
|
||||
private class StealingTarget : Target
|
||||
{
|
||||
private readonly Mobile m_Thief;
|
||||
|
||||
public StealingTarget(Mobile thief) : base(1, false, TargetFlags.None)
|
||||
{
|
||||
m_Thief = thief;
|
||||
AllowNonlocal = true;
|
||||
}
|
||||
|
||||
private Item TryStealItem(Item toSteal, ref bool caught)
|
||||
{
|
||||
Item stolen = null;
|
||||
|
||||
var root = toSteal.RootParent;
|
||||
var mobRoot = root as Mobile;
|
||||
|
||||
StealableArtifactsSpawner.StealableInstance si = null;
|
||||
if (toSteal.Parent == null || !toSteal.Movable)
|
||||
si = StealableArtifactsSpawner.GetStealableInstance(toSteal);
|
||||
|
||||
if (!IsEmptyHanded(m_Thief))
|
||||
{
|
||||
sig.LastMonolith.Sigil = null;
|
||||
sig.LastStolen = DateTime.UtcNow;
|
||||
m_Thief.SendLocalizedMessage(1005584); // Both hands must be free to steal.
|
||||
}
|
||||
else if (m_Thief.Region.IsPartOf<SafeZone>())
|
||||
{
|
||||
m_Thief.SendMessage("You may not steal in this area.");
|
||||
}
|
||||
else if (mobRoot?.Player == true && !IsInGuild(m_Thief))
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(1005596); // You must be in the thieves guild to steal from other players.
|
||||
}
|
||||
else if (SuspendOnMurder && mobRoot?.Player == true && IsInGuild(m_Thief) &&
|
||||
m_Thief.Kills > 0)
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(502706); // You are currently suspended from the thieves guild.
|
||||
}
|
||||
else if (root is BaseVendor vendor && vendor.IsInvulnerable)
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(1005598); // You can't steal from shopkeepers.
|
||||
}
|
||||
else if (root is PlayerVendor)
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(502709); // You can't steal from vendors.
|
||||
}
|
||||
else if (!m_Thief.CanSee(toSteal))
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(500237); // Target can not be seen.
|
||||
}
|
||||
else if (m_Thief.Backpack?.CheckHold(m_Thief, toSteal, false, true) != true)
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(1048147); // Your backpack can't hold anything else.
|
||||
}
|
||||
else if (toSteal is Sigil sig)
|
||||
{
|
||||
var pl = PlayerState.Find(m_Thief);
|
||||
var faction = pl?.Faction;
|
||||
|
||||
if (!m_Thief.InRange(sig.GetWorldLocation(), 1))
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(502703); // You must be standing next to an item to steal it.
|
||||
}
|
||||
else if (root != null) // not on the ground
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(502710); // You can't steal that!
|
||||
}
|
||||
else if (faction != null)
|
||||
{
|
||||
if (!m_Thief.CanBeginAction<IncognitoSpell>())
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(1010581); // You cannot steal the sigil when you are incognito
|
||||
}
|
||||
else if (DisguiseTimers.IsDisguised(m_Thief))
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(1010583); // You cannot steal the sigil while disguised
|
||||
}
|
||||
else if (!m_Thief.CanBeginAction<PolymorphSpell>())
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(1010582); // You cannot steal the sigil while polymorphed
|
||||
}
|
||||
else if (TransformationSpellHelper.UnderTransformation(m_Thief))
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(1061622); // You cannot steal the sigil while in that form.
|
||||
}
|
||||
else if (AnimalForm.UnderTransformation(m_Thief))
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(1063222); // You cannot steal the sigil while mimicking an animal.
|
||||
}
|
||||
else if (pl.IsLeaving)
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(
|
||||
1005589
|
||||
); // You are currently quitting a faction and cannot steal the town sigil
|
||||
}
|
||||
else if (sig.IsBeingCorrupted && sig.LastMonolith.Faction == faction)
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(1005590); // You cannot steal your own sigil
|
||||
}
|
||||
else if (sig.IsPurifying)
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(1005592); // You cannot steal this sigil until it has been purified
|
||||
}
|
||||
else if (m_Thief.CheckTargetSkill(SkillName.Stealing, toSteal, 80.0, 80.0))
|
||||
{
|
||||
if (Sigil.ExistsOn(m_Thief))
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(
|
||||
1010258
|
||||
); // The sigil has gone back to its home location because you already have a sigil.
|
||||
}
|
||||
else if (m_Thief?.Backpack.CheckHold(m_Thief, sig, false, true) != true)
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(
|
||||
1010259
|
||||
); // The sigil has gone home because your backpack is full
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sig.IsBeingCorrupted)
|
||||
sig.GraceStart = DateTime.UtcNow; // begin grace period
|
||||
|
||||
m_Thief.SendLocalizedMessage(1010586); // YOU STOLE THE SIGIL!!! (woah, calm down now)
|
||||
|
||||
if (sig.LastMonolith?.Sigil != null)
|
||||
{
|
||||
sig.LastMonolith.Sigil = null;
|
||||
sig.LastStolen = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
return sig;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(1005594); // You do not have enough skill to steal the sigil
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(1005588); // You must join a faction to do that
|
||||
}
|
||||
}
|
||||
else if (si == null && (toSteal.Parent == null || !toSteal.Movable))
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(502710); // You can't steal that!
|
||||
}
|
||||
else if (toSteal.LootType == LootType.Newbied || toSteal.CheckBlessed(mobRoot))
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(502710); // You can't steal that!
|
||||
}
|
||||
else if (Core.AOS && si == null && toSteal is Container)
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(502710); // You can't steal that!
|
||||
}
|
||||
else if (!m_Thief.InRange(toSteal.GetWorldLocation(), 1))
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(502703); // You must be standing next to an item to steal it.
|
||||
}
|
||||
else if (si != null && m_Thief.Skills.Stealing.Value < 100.0)
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(
|
||||
1060025,
|
||||
"",
|
||||
0x66D
|
||||
); // You're not skilled enough to attempt the theft of this item.
|
||||
}
|
||||
else if (toSteal.Parent is Mobile)
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(1005585); // You cannot steal items which are equipped.
|
||||
}
|
||||
else if (root == m_Thief)
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(502704); // You catch yourself red-handed.
|
||||
}
|
||||
else if (mobRoot?.AccessLevel > AccessLevel.Player)
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(502710); // You can't steal that!
|
||||
}
|
||||
else if (mobRoot != null && !m_Thief.CanBeHarmful(mobRoot))
|
||||
{
|
||||
}
|
||||
else if (root is Corpse)
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(502710); // You can't steal that!
|
||||
}
|
||||
else
|
||||
{
|
||||
var w = toSteal.Weight + toSteal.TotalWeight;
|
||||
|
||||
if (w > 10)
|
||||
{
|
||||
m_Thief.SendMessage("That is too heavy to steal.");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (toSteal.Stackable && toSteal.Amount > 1)
|
||||
{
|
||||
var maxAmount = Math.Clamp(
|
||||
(int)(m_Thief.Skills.Stealing.Value / 10.0 / toSteal.Weight),
|
||||
1,
|
||||
toSteal.Amount
|
||||
);
|
||||
|
||||
var amount = Utility.RandomMinMax(1, maxAmount);
|
||||
|
||||
if (amount >= toSteal.Amount)
|
||||
{
|
||||
var pileWeight = (int)Math.Ceiling(toSteal.Weight * toSteal.Amount);
|
||||
pileWeight *= 10;
|
||||
|
||||
if (m_Thief.CheckTargetSkill(
|
||||
SkillName.Stealing,
|
||||
toSteal,
|
||||
pileWeight - 22.5,
|
||||
pileWeight + 27.5
|
||||
))
|
||||
stolen = toSteal;
|
||||
}
|
||||
else
|
||||
{
|
||||
var pileWeight = (int)Math.Ceiling(toSteal.Weight * amount);
|
||||
pileWeight *= 10;
|
||||
|
||||
if (m_Thief.CheckTargetSkill(
|
||||
SkillName.Stealing,
|
||||
toSteal,
|
||||
pileWeight - 22.5,
|
||||
pileWeight + 27.5
|
||||
))
|
||||
stolen = Mobile.LiftItemDupe(toSteal, toSteal.Amount - amount) ?? toSteal;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var iw = (int)Math.Ceiling(w);
|
||||
iw *= 10;
|
||||
|
||||
if (m_Thief.CheckTargetSkill(SkillName.Stealing, toSteal, iw - 22.5, iw + 27.5))
|
||||
stolen = toSteal;
|
||||
}
|
||||
|
||||
if (stolen != null)
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(502724); // You successfully steal the item.
|
||||
|
||||
if (si != null)
|
||||
{
|
||||
toSteal.Movable = true;
|
||||
si.Item = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(502723); // You fail to steal the item.
|
||||
}
|
||||
|
||||
caught = m_Thief.Skills.Stealing.Value < Utility.Random(150);
|
||||
}
|
||||
}
|
||||
|
||||
return sig;
|
||||
}
|
||||
return stolen;
|
||||
}
|
||||
else
|
||||
|
||||
protected override void OnTarget(Mobile from, object target)
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(1005594); // You do not have enough skill to steal the sigil
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(1005588); // You must join a faction to do that
|
||||
}
|
||||
}
|
||||
else if (si == null && (toSteal.Parent == null || !toSteal.Movable))
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(502710); // You can't steal that!
|
||||
}
|
||||
else if (toSteal.LootType == LootType.Newbied || toSteal.CheckBlessed(mobRoot))
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(502710); // You can't steal that!
|
||||
}
|
||||
else if (Core.AOS && si == null && toSteal is Container)
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(502710); // You can't steal that!
|
||||
}
|
||||
else if (!m_Thief.InRange(toSteal.GetWorldLocation(), 1))
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(502703); // You must be standing next to an item to steal it.
|
||||
}
|
||||
else if (si != null && m_Thief.Skills.Stealing.Value < 100.0)
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(1060025, "",
|
||||
0x66D); // You're not skilled enough to attempt the theft of this item.
|
||||
}
|
||||
else if (toSteal.Parent is Mobile)
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(1005585); // You cannot steal items which are equipped.
|
||||
}
|
||||
else if (root == m_Thief)
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(502704); // You catch yourself red-handed.
|
||||
}
|
||||
else if (mobRoot?.AccessLevel > AccessLevel.Player)
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(502710); // You can't steal that!
|
||||
}
|
||||
else if (mobRoot != null && !m_Thief.CanBeHarmful(mobRoot))
|
||||
{
|
||||
}
|
||||
else if (root is Corpse)
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(502710); // You can't steal that!
|
||||
}
|
||||
else
|
||||
{
|
||||
double w = toSteal.Weight + toSteal.TotalWeight;
|
||||
from.RevealingAction();
|
||||
|
||||
if (w > 10)
|
||||
{
|
||||
m_Thief.SendMessage("That is too heavy to steal.");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (toSteal.Stackable && toSteal.Amount > 1)
|
||||
Item stolen = null;
|
||||
IEntity root = null;
|
||||
var caught = false;
|
||||
|
||||
if (target is Item item)
|
||||
{
|
||||
root = item.RootParent;
|
||||
stolen = TryStealItem(item, ref caught);
|
||||
}
|
||||
else if (target is Mobile mobile)
|
||||
{
|
||||
var pack = mobile.Backpack;
|
||||
|
||||
if (pack?.Items.Count > 0)
|
||||
{
|
||||
root = mobile;
|
||||
stolen = TryStealItem(pack.Items.RandomElement(), ref caught);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(502710); // You can't steal that!
|
||||
}
|
||||
|
||||
var mobRoot = root as Mobile;
|
||||
|
||||
if (stolen != null)
|
||||
{
|
||||
from.AddToBackpack(stolen);
|
||||
|
||||
if (!(stolen is Container || stolen.Stackable))
|
||||
StolenItem.Add(stolen, m_Thief, mobRoot);
|
||||
}
|
||||
|
||||
var corpse = root as Corpse;
|
||||
|
||||
if (caught)
|
||||
{
|
||||
if (root == null || corpse?.IsCriminalAction(m_Thief) == true)
|
||||
{
|
||||
m_Thief.CriminalAction(false);
|
||||
}
|
||||
else if (mobRoot != null)
|
||||
{
|
||||
if (!IsInGuild(mobRoot) && IsInnocentTo(m_Thief, mobRoot))
|
||||
m_Thief.CriminalAction(false);
|
||||
|
||||
var message = $"You notice {m_Thief.Name} trying to steal from {mobRoot.Name}.";
|
||||
|
||||
foreach (var ns in m_Thief.GetClientsInRange(8))
|
||||
if (ns.Mobile != m_Thief)
|
||||
ns.Mobile.SendMessage(message);
|
||||
}
|
||||
}
|
||||
else if (corpse?.IsCriminalAction(m_Thief) == true)
|
||||
{
|
||||
m_Thief.CriminalAction(false);
|
||||
}
|
||||
|
||||
if (mobRoot?.Player == true && m_Thief is PlayerMobile pm &&
|
||||
IsInnocentTo(pm, mobRoot) && !IsInGuild(mobRoot))
|
||||
{
|
||||
pm.PermaFlags.Add(mobRoot);
|
||||
pm.Delta(MobileDelta.Noto);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class StolenItem
|
||||
{
|
||||
public static readonly TimeSpan StealTime = TimeSpan.FromMinutes(2.0);
|
||||
|
||||
private static readonly Queue<StolenItem> m_Queue = new Queue<StolenItem>();
|
||||
|
||||
public StolenItem(Item stolen, Mobile thief, Mobile victim)
|
||||
{
|
||||
Stolen = stolen;
|
||||
Thief = thief;
|
||||
Victim = victim;
|
||||
|
||||
Expires = DateTime.UtcNow + StealTime;
|
||||
}
|
||||
|
||||
public Item Stolen { get; }
|
||||
|
||||
public Mobile Thief { get; }
|
||||
|
||||
public Mobile Victim { get; }
|
||||
|
||||
public DateTime Expires { get; private set; }
|
||||
|
||||
public bool IsExpired => DateTime.UtcNow >= Expires;
|
||||
|
||||
public static void Add(Item item, Mobile thief, Mobile victim)
|
||||
{
|
||||
Clean();
|
||||
|
||||
m_Queue.Enqueue(new StolenItem(item, thief, victim));
|
||||
}
|
||||
|
||||
public static bool IsStolen(Item item)
|
||||
{
|
||||
Mobile victim = null;
|
||||
|
||||
return IsStolen(item, ref victim);
|
||||
}
|
||||
|
||||
public static bool IsStolen(Item item, ref Mobile victim)
|
||||
{
|
||||
Clean();
|
||||
|
||||
foreach (var si in m_Queue)
|
||||
if (si.Stolen == item && !si.IsExpired)
|
||||
{
|
||||
victim = si.Victim;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void ReturnOnDeath(Mobile killed, Container corpse)
|
||||
{
|
||||
Clean();
|
||||
|
||||
foreach (var si in m_Queue)
|
||||
if (si.Stolen.RootParent == corpse && si.Victim != null && !si.IsExpired)
|
||||
{
|
||||
if (si.Victim.AddToBackpack(si.Stolen))
|
||||
si.Victim.SendLocalizedMessage(1010464); // the item that was stolen is returned to you.
|
||||
else
|
||||
si.Victim.SendLocalizedMessage(1010463); // the item that was stolen from you falls to the ground.
|
||||
|
||||
si.Expires = DateTime.UtcNow; // such a hack
|
||||
}
|
||||
}
|
||||
|
||||
public static void Clean()
|
||||
{
|
||||
while (m_Queue.Count > 0)
|
||||
{
|
||||
int maxAmount = Math.Clamp((int)(m_Thief.Skills.Stealing.Value / 10.0 / toSteal.Weight), 1, toSteal.Amount);
|
||||
var si = m_Queue.Peek();
|
||||
|
||||
int amount = Utility.RandomMinMax(1, maxAmount);
|
||||
|
||||
if (amount >= toSteal.Amount)
|
||||
{
|
||||
int pileWeight = (int)Math.Ceiling(toSteal.Weight * toSteal.Amount);
|
||||
pileWeight *= 10;
|
||||
|
||||
if (m_Thief.CheckTargetSkill(SkillName.Stealing, toSteal, pileWeight - 22.5,
|
||||
pileWeight + 27.5))
|
||||
stolen = toSteal;
|
||||
}
|
||||
else
|
||||
{
|
||||
int pileWeight = (int)Math.Ceiling(toSteal.Weight * amount);
|
||||
pileWeight *= 10;
|
||||
|
||||
if (m_Thief.CheckTargetSkill(SkillName.Stealing, toSteal, pileWeight - 22.5,
|
||||
pileWeight + 27.5))
|
||||
stolen = Mobile.LiftItemDupe(toSteal, toSteal.Amount - amount) ?? toSteal;
|
||||
}
|
||||
if (si.IsExpired)
|
||||
m_Queue.Dequeue();
|
||||
else
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
int iw = (int)Math.Ceiling(w);
|
||||
iw *= 10;
|
||||
|
||||
if (m_Thief.CheckTargetSkill(SkillName.Stealing, toSteal, iw - 22.5, iw + 27.5))
|
||||
stolen = toSteal;
|
||||
}
|
||||
|
||||
if (stolen != null)
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(502724); // You successfully steal the item.
|
||||
|
||||
if (si != null)
|
||||
{
|
||||
toSteal.Movable = true;
|
||||
si.Item = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(502723); // You fail to steal the item.
|
||||
}
|
||||
|
||||
caught = m_Thief.Skills.Stealing.Value < Utility.Random(150);
|
||||
}
|
||||
}
|
||||
|
||||
return stolen;
|
||||
}
|
||||
|
||||
protected override void OnTarget(Mobile from, object target)
|
||||
{
|
||||
from.RevealingAction();
|
||||
|
||||
Item stolen = null;
|
||||
IEntity root = null;
|
||||
bool caught = false;
|
||||
|
||||
if (target is Item item)
|
||||
{
|
||||
root = item.RootParent;
|
||||
stolen = TryStealItem(item, ref caught);
|
||||
}
|
||||
else if (target is Mobile mobile)
|
||||
{
|
||||
Container pack = mobile.Backpack;
|
||||
|
||||
if (pack?.Items.Count > 0)
|
||||
{
|
||||
root = mobile;
|
||||
stolen = TryStealItem(pack.Items.RandomElement(), ref caught);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Thief.SendLocalizedMessage(502710); // You can't steal that!
|
||||
}
|
||||
|
||||
Mobile mobRoot = root as Mobile;
|
||||
|
||||
if (stolen != null)
|
||||
{
|
||||
from.AddToBackpack(stolen);
|
||||
|
||||
if (!(stolen is Container || stolen.Stackable))
|
||||
StolenItem.Add(stolen, m_Thief, mobRoot);
|
||||
}
|
||||
|
||||
Corpse corpse = root as Corpse;
|
||||
|
||||
if (caught)
|
||||
{
|
||||
if (root == null || corpse?.IsCriminalAction(m_Thief) == true)
|
||||
{
|
||||
m_Thief.CriminalAction(false);
|
||||
}
|
||||
else if (mobRoot != null)
|
||||
{
|
||||
if (!IsInGuild(mobRoot) && IsInnocentTo(m_Thief, mobRoot))
|
||||
m_Thief.CriminalAction(false);
|
||||
|
||||
string message = $"You notice {m_Thief.Name} trying to steal from {mobRoot.Name}.";
|
||||
|
||||
foreach (NetState ns in m_Thief.GetClientsInRange(8))
|
||||
if (ns.Mobile != m_Thief)
|
||||
ns.Mobile.SendMessage(message);
|
||||
}
|
||||
}
|
||||
else if (corpse?.IsCriminalAction(m_Thief) == true)
|
||||
{
|
||||
m_Thief.CriminalAction(false);
|
||||
}
|
||||
|
||||
if (mobRoot?.Player == true && m_Thief is PlayerMobile pm &&
|
||||
IsInnocentTo(pm, mobRoot) && !IsInGuild(mobRoot))
|
||||
{
|
||||
pm.PermaFlags.Add(mobRoot);
|
||||
pm.Delta(MobileDelta.Noto);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class StolenItem
|
||||
{
|
||||
public static readonly TimeSpan StealTime = TimeSpan.FromMinutes(2.0);
|
||||
|
||||
private static readonly Queue<StolenItem> m_Queue = new Queue<StolenItem>();
|
||||
|
||||
public StolenItem(Item stolen, Mobile thief, Mobile victim)
|
||||
{
|
||||
Stolen = stolen;
|
||||
Thief = thief;
|
||||
Victim = victim;
|
||||
|
||||
Expires = DateTime.UtcNow + StealTime;
|
||||
}
|
||||
|
||||
public Item Stolen { get; }
|
||||
|
||||
public Mobile Thief { get; }
|
||||
|
||||
public Mobile Victim { get; }
|
||||
|
||||
public DateTime Expires { get; private set; }
|
||||
|
||||
public bool IsExpired => DateTime.UtcNow >= Expires;
|
||||
|
||||
public static void Add(Item item, Mobile thief, Mobile victim)
|
||||
{
|
||||
Clean();
|
||||
|
||||
m_Queue.Enqueue(new StolenItem(item, thief, victim));
|
||||
}
|
||||
|
||||
public static bool IsStolen(Item item)
|
||||
{
|
||||
Mobile victim = null;
|
||||
|
||||
return IsStolen(item, ref victim);
|
||||
}
|
||||
|
||||
public static bool IsStolen(Item item, ref Mobile victim)
|
||||
{
|
||||
Clean();
|
||||
|
||||
foreach (StolenItem si in m_Queue)
|
||||
if (si.Stolen == item && !si.IsExpired)
|
||||
{
|
||||
victim = si.Victim;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void ReturnOnDeath(Mobile killed, Container corpse)
|
||||
{
|
||||
Clean();
|
||||
|
||||
foreach (StolenItem si in m_Queue)
|
||||
if (si.Stolen.RootParent == corpse && si.Victim != null && !si.IsExpired)
|
||||
{
|
||||
if (si.Victim.AddToBackpack(si.Stolen))
|
||||
si.Victim.SendLocalizedMessage(1010464); // the item that was stolen is returned to you.
|
||||
else
|
||||
si.Victim.SendLocalizedMessage(1010463); // the item that was stolen from you falls to the ground.
|
||||
|
||||
si.Expires = DateTime.UtcNow; // such a hack
|
||||
}
|
||||
}
|
||||
|
||||
public static void Clean()
|
||||
{
|
||||
while (m_Queue.Count > 0)
|
||||
{
|
||||
StolenItem si = m_Queue.Peek();
|
||||
|
||||
if (si.IsExpired)
|
||||
m_Queue.Dequeue();
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,105 +4,109 @@ using Server.Mobiles;
|
|||
|
||||
namespace Server.SkillHandlers
|
||||
{
|
||||
// Stealth cannot be static because its used as a generic for CanBeginAction.
|
||||
// Stealth cannot be static because its used as a generic for CanBeginAction.
|
||||
#pragma warning disable CA1052 // Static holder types should be Static or NotInheritable
|
||||
public class Stealth
|
||||
{
|
||||
public static double HidingRequirement => Core.ML ? 30.0 : Core.SE ? 50.0 : 80.0;
|
||||
|
||||
// TODO: Move to configuration
|
||||
public static int[,] ArmorTable { get; } =
|
||||
public class Stealth
|
||||
{
|
||||
// Gorget Glove Helmet Arms Legs Chest Shield
|
||||
/* Cloth */ { 0, 0, 0, 0, 0, 0, 0 },
|
||||
/* Leather */ { 0, 0, 0, 0, 0, 0, 0 },
|
||||
/* Studded */ { 2, 2, 0, 4, 6, 10, 0 },
|
||||
/* Bone */ { 0, 5, 10, 10, 15, 25, 0 },
|
||||
/* Spined */ { 0, 0, 0, 0, 0, 0, 0 },
|
||||
/* Horned */ { 0, 0, 0, 0, 0, 0, 0 },
|
||||
/* Barbed */ { 0, 0, 0, 0, 0, 0, 0 },
|
||||
/* Ring */ { 0, 5, 0, 10, 15, 25, 0 },
|
||||
/* Chain */ { 0, 0, 10, 0, 15, 25, 0 },
|
||||
/* Plate */ { 5, 5, 10, 10, 15, 25, 0 },
|
||||
/* Dragon */ { 0, 5, 10, 10, 15, 25, 0 }
|
||||
};
|
||||
public static double HidingRequirement => Core.ML ? 30.0 :
|
||||
Core.SE ? 50.0 : 80.0;
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
SkillInfo.Table[(int)SkillName.Stealth].Callback = OnUse;
|
||||
}
|
||||
|
||||
public static int GetArmorRating(Mobile m)
|
||||
{
|
||||
if (!Core.AOS)
|
||||
return (int)m.ArmorRating;
|
||||
|
||||
int ar = 0;
|
||||
|
||||
for (int i = 0; i < m.Items.Count; i++)
|
||||
{
|
||||
if (!(m.Items[i] is BaseArmor armor))
|
||||
continue;
|
||||
|
||||
int materialType = (int)armor.MaterialType;
|
||||
int bodyPosition = (int)armor.BodyPosition;
|
||||
|
||||
if (materialType >= ArmorTable.GetLength(0) || bodyPosition >= ArmorTable.GetLength(1))
|
||||
continue;
|
||||
|
||||
if (armor.ArmorAttributes.MageArmor == 0)
|
||||
ar += ArmorTable[materialType, bodyPosition];
|
||||
}
|
||||
|
||||
return ar;
|
||||
}
|
||||
|
||||
public static TimeSpan OnUse(Mobile m)
|
||||
{
|
||||
if (!m.Hidden)
|
||||
{
|
||||
m.SendLocalizedMessage(502725); // You must hide first
|
||||
}
|
||||
else if (m.Skills.Hiding.Base < HidingRequirement)
|
||||
{
|
||||
m.SendLocalizedMessage(502726); // You are not hidden well enough. Become better at hiding.
|
||||
m.RevealingAction();
|
||||
}
|
||||
else if (!m.CanBeginAction<Stealth>())
|
||||
{
|
||||
m.SendLocalizedMessage(1063086); // You cannot use this skill right now.
|
||||
m.RevealingAction();
|
||||
}
|
||||
else
|
||||
{
|
||||
int armorRating = GetArmorRating(m);
|
||||
|
||||
if (armorRating >= (Core.AOS ? 42 : 26)) // I have a hunch '42' was chosen cause someone's a fan of DNA
|
||||
// TODO: Move to configuration
|
||||
public static int[,] ArmorTable { get; } =
|
||||
{
|
||||
m.SendLocalizedMessage(502727); // You could not hope to move quietly wearing this much armor.
|
||||
m.RevealingAction();
|
||||
}
|
||||
else if (m.CheckSkill(SkillName.Stealth, -20.0 + armorRating * 2,
|
||||
(Core.AOS ? 60.0 : 80.0) + armorRating * 2))
|
||||
// Gorget Glove Helmet Arms Legs Chest Shield
|
||||
/* Cloth */ { 0, 0, 0, 0, 0, 0, 0 },
|
||||
/* Leather */ { 0, 0, 0, 0, 0, 0, 0 },
|
||||
/* Studded */ { 2, 2, 0, 4, 6, 10, 0 },
|
||||
/* Bone */ { 0, 5, 10, 10, 15, 25, 0 },
|
||||
/* Spined */ { 0, 0, 0, 0, 0, 0, 0 },
|
||||
/* Horned */ { 0, 0, 0, 0, 0, 0, 0 },
|
||||
/* Barbed */ { 0, 0, 0, 0, 0, 0, 0 },
|
||||
/* Ring */ { 0, 5, 0, 10, 15, 25, 0 },
|
||||
/* Chain */ { 0, 0, 10, 0, 15, 25, 0 },
|
||||
/* Plate */ { 5, 5, 10, 10, 15, 25, 0 },
|
||||
/* Dragon */ { 0, 5, 10, 10, 15, 25, 0 }
|
||||
};
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
m.AllowedStealthSteps = Math.Max((int)(m.Skills.Stealth.Value / (Core.AOS ? 5.0 : 10.0)), 1);
|
||||
|
||||
if (m is PlayerMobile pm)
|
||||
pm.IsStealthing = true;
|
||||
|
||||
m.SendLocalizedMessage(502730); // You begin to move quietly.
|
||||
|
||||
return TimeSpan.FromSeconds(10.0);
|
||||
SkillInfo.Table[(int)SkillName.Stealth].Callback = OnUse;
|
||||
}
|
||||
else
|
||||
|
||||
public static int GetArmorRating(Mobile m)
|
||||
{
|
||||
m.SendLocalizedMessage(502731); // You fail in your attempt to move unnoticed.
|
||||
m.RevealingAction();
|
||||
}
|
||||
}
|
||||
if (!Core.AOS)
|
||||
return (int)m.ArmorRating;
|
||||
|
||||
return TimeSpan.FromSeconds(10.0);
|
||||
var ar = 0;
|
||||
|
||||
for (var i = 0; i < m.Items.Count; i++)
|
||||
{
|
||||
if (!(m.Items[i] is BaseArmor armor))
|
||||
continue;
|
||||
|
||||
var materialType = (int)armor.MaterialType;
|
||||
var bodyPosition = (int)armor.BodyPosition;
|
||||
|
||||
if (materialType >= ArmorTable.GetLength(0) || bodyPosition >= ArmorTable.GetLength(1))
|
||||
continue;
|
||||
|
||||
if (armor.ArmorAttributes.MageArmor == 0)
|
||||
ar += ArmorTable[materialType, bodyPosition];
|
||||
}
|
||||
|
||||
return ar;
|
||||
}
|
||||
|
||||
public static TimeSpan OnUse(Mobile m)
|
||||
{
|
||||
if (!m.Hidden)
|
||||
{
|
||||
m.SendLocalizedMessage(502725); // You must hide first
|
||||
}
|
||||
else if (m.Skills.Hiding.Base < HidingRequirement)
|
||||
{
|
||||
m.SendLocalizedMessage(502726); // You are not hidden well enough. Become better at hiding.
|
||||
m.RevealingAction();
|
||||
}
|
||||
else if (!m.CanBeginAction<Stealth>())
|
||||
{
|
||||
m.SendLocalizedMessage(1063086); // You cannot use this skill right now.
|
||||
m.RevealingAction();
|
||||
}
|
||||
else
|
||||
{
|
||||
var armorRating = GetArmorRating(m);
|
||||
|
||||
if (armorRating >= (Core.AOS ? 42 : 26)) // I have a hunch '42' was chosen cause someone's a fan of DNA
|
||||
{
|
||||
m.SendLocalizedMessage(502727); // You could not hope to move quietly wearing this much armor.
|
||||
m.RevealingAction();
|
||||
}
|
||||
else if (m.CheckSkill(
|
||||
SkillName.Stealth,
|
||||
-20.0 + armorRating * 2,
|
||||
(Core.AOS ? 60.0 : 80.0) + armorRating * 2
|
||||
))
|
||||
{
|
||||
m.AllowedStealthSteps = Math.Max((int)(m.Skills.Stealth.Value / (Core.AOS ? 5.0 : 10.0)), 1);
|
||||
|
||||
if (m is PlayerMobile pm)
|
||||
pm.IsStealthing = true;
|
||||
|
||||
m.SendLocalizedMessage(502730); // You begin to move quietly.
|
||||
|
||||
return TimeSpan.FromSeconds(10.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
m.SendLocalizedMessage(502731); // You fail in your attempt to move unnoticed.
|
||||
m.RevealingAction();
|
||||
}
|
||||
}
|
||||
|
||||
return TimeSpan.FromSeconds(10.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore CA1052 // Static holder types should be Static or NotInheritable
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,76 +5,76 @@ using Server.Targeting;
|
|||
|
||||
namespace Server.SkillHandlers
|
||||
{
|
||||
public static class TasteID
|
||||
{
|
||||
public static void Initialize()
|
||||
public static class TasteID
|
||||
{
|
||||
SkillInfo.Table[(int)SkillName.TasteID].Callback = OnUse;
|
||||
public static void Initialize()
|
||||
{
|
||||
SkillInfo.Table[(int)SkillName.TasteID].Callback = OnUse;
|
||||
}
|
||||
|
||||
public static TimeSpan OnUse(Mobile m)
|
||||
{
|
||||
m.Target = new InternalTarget();
|
||||
|
||||
m.SendLocalizedMessage(502807); // What would you like to taste?
|
||||
|
||||
return TimeSpan.FromSeconds(1.0);
|
||||
}
|
||||
|
||||
[PlayerVendorTarget]
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
public InternalTarget() : base(2, false, TargetFlags.None) => AllowNonlocal = true;
|
||||
|
||||
protected override void OnTarget(Mobile from, object targeted)
|
||||
{
|
||||
if (targeted is Mobile)
|
||||
{
|
||||
from.SendLocalizedMessage(502816); // You feel that such an action would be inappropriate.
|
||||
}
|
||||
else if (targeted is Food food)
|
||||
{
|
||||
if (from.CheckTargetSkill(SkillName.TasteID, food, 0, 100))
|
||||
{
|
||||
if (food.Poison != null)
|
||||
food.SendLocalizedMessageTo(from, 1038284); // It appears to have poison smeared on it.
|
||||
else
|
||||
food.SendLocalizedMessageTo(from, 1010600); // You detect nothing unusual about this substance.
|
||||
}
|
||||
else
|
||||
{
|
||||
// Skill check failed
|
||||
food.SendLocalizedMessageTo(from, 502823); // You cannot discern anything about this substance.
|
||||
}
|
||||
}
|
||||
else if (targeted is BasePotion potion)
|
||||
{
|
||||
potion.SendLocalizedMessageTo(from, 502813); // You already know what kind of potion that is.
|
||||
potion.SendLocalizedMessageTo(from, potion.LabelNumber);
|
||||
}
|
||||
else if (targeted is PotionKeg keg)
|
||||
{
|
||||
if (keg.Held <= 0)
|
||||
{
|
||||
keg.SendLocalizedMessageTo(from, 502228); // There is nothing in the keg to taste!
|
||||
}
|
||||
else
|
||||
{
|
||||
keg.SendLocalizedMessageTo(from, 502229); // You are already familiar with this keg's contents.
|
||||
keg.SendLocalizedMessageTo(from, keg.LabelNumber);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The target is not food or potion or potion keg.
|
||||
from.SendLocalizedMessage(502820); // That's not something you can taste.
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnTargetOutOfRange(Mobile from, object targeted)
|
||||
{
|
||||
from.SendLocalizedMessage(502815); // You are too far away to taste that.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static TimeSpan OnUse(Mobile m)
|
||||
{
|
||||
m.Target = new InternalTarget();
|
||||
|
||||
m.SendLocalizedMessage(502807); // What would you like to taste?
|
||||
|
||||
return TimeSpan.FromSeconds(1.0);
|
||||
}
|
||||
|
||||
[PlayerVendorTarget]
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
public InternalTarget() : base(2, false, TargetFlags.None) => AllowNonlocal = true;
|
||||
|
||||
protected override void OnTarget(Mobile from, object targeted)
|
||||
{
|
||||
if (targeted is Mobile)
|
||||
{
|
||||
from.SendLocalizedMessage(502816); // You feel that such an action would be inappropriate.
|
||||
}
|
||||
else if (targeted is Food food)
|
||||
{
|
||||
if (from.CheckTargetSkill(SkillName.TasteID, food, 0, 100))
|
||||
{
|
||||
if (food.Poison != null)
|
||||
food.SendLocalizedMessageTo(from, 1038284); // It appears to have poison smeared on it.
|
||||
else
|
||||
food.SendLocalizedMessageTo(from, 1010600); // You detect nothing unusual about this substance.
|
||||
}
|
||||
else
|
||||
{
|
||||
// Skill check failed
|
||||
food.SendLocalizedMessageTo(from, 502823); // You cannot discern anything about this substance.
|
||||
}
|
||||
}
|
||||
else if (targeted is BasePotion potion)
|
||||
{
|
||||
potion.SendLocalizedMessageTo(from, 502813); // You already know what kind of potion that is.
|
||||
potion.SendLocalizedMessageTo(from, potion.LabelNumber);
|
||||
}
|
||||
else if (targeted is PotionKeg keg)
|
||||
{
|
||||
if (keg.Held <= 0)
|
||||
{
|
||||
keg.SendLocalizedMessageTo(from, 502228); // There is nothing in the keg to taste!
|
||||
}
|
||||
else
|
||||
{
|
||||
keg.SendLocalizedMessageTo(from, 502229); // You are already familiar with this keg's contents.
|
||||
keg.SendLocalizedMessageTo(from, keg.LabelNumber);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The target is not food or potion or potion keg.
|
||||
from.SendLocalizedMessage(502820); // That's not something you can taste.
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnTargetOutOfRange(Mobile from, object targeted)
|
||||
{
|
||||
from.SendLocalizedMessage(502815); // You are too far away to taste that.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,370 +8,376 @@ using Server.Spells.Necromancy;
|
|||
|
||||
namespace Server.SkillHandlers
|
||||
{
|
||||
public static class Tracking
|
||||
{
|
||||
private static readonly Dictionary<Mobile, TrackingInfo> m_Table = new Dictionary<Mobile, TrackingInfo>();
|
||||
|
||||
public static void Initialize()
|
||||
public static class Tracking
|
||||
{
|
||||
SkillInfo.Table[(int)SkillName.Tracking].Callback = OnUse;
|
||||
}
|
||||
private static readonly Dictionary<Mobile, TrackingInfo> m_Table = new Dictionary<Mobile, TrackingInfo>();
|
||||
|
||||
public static TimeSpan OnUse(Mobile m)
|
||||
{
|
||||
m.SendLocalizedMessage(1011350); // What do you wish to track?
|
||||
|
||||
m.CloseGump<TrackWhatGump>();
|
||||
m.CloseGump<TrackWhoGump>();
|
||||
m.SendGump(new TrackWhatGump(m));
|
||||
|
||||
return TimeSpan.FromSeconds(10.0); // 10 second delay before being able to re-use a skill
|
||||
}
|
||||
|
||||
public static void AddInfo(Mobile tracker, Mobile target)
|
||||
{
|
||||
TrackingInfo info = new TrackingInfo(tracker, target);
|
||||
m_Table[tracker] = info;
|
||||
}
|
||||
|
||||
public static double GetStalkingBonus(Mobile tracker, Mobile target)
|
||||
{
|
||||
if (!m_Table.TryGetValue(tracker, out TrackingInfo info) || info.m_Target != target || info.m_Map != target.Map)
|
||||
return 0.0;
|
||||
|
||||
int xDelta = info.m_Location.X - target.X;
|
||||
int yDelta = info.m_Location.Y - target.Y;
|
||||
|
||||
double bonus = Math.Sqrt(xDelta * xDelta + yDelta * yDelta);
|
||||
|
||||
m_Table.Remove(tracker); // Reset as of Pub 40, counting it as bug for Core.SE.
|
||||
|
||||
return Core.ML ? Math.Min(bonus, 10 + tracker.Skills.Tracking.Value / 10) : bonus;
|
||||
}
|
||||
|
||||
public static void ClearTrackingInfo(Mobile tracker)
|
||||
{
|
||||
m_Table.Remove(tracker);
|
||||
}
|
||||
|
||||
public class TrackingInfo
|
||||
{
|
||||
public Point2D m_Location;
|
||||
public Map m_Map;
|
||||
public Mobile m_Target;
|
||||
public Mobile m_Tracker;
|
||||
|
||||
public TrackingInfo(Mobile tracker, Mobile target)
|
||||
{
|
||||
m_Tracker = tracker;
|
||||
m_Target = target;
|
||||
m_Location = new Point2D(target.X, target.Y);
|
||||
m_Map = target.Map;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class TrackWhatGump : Gump
|
||||
{
|
||||
private readonly Mobile m_From;
|
||||
private readonly bool m_Success;
|
||||
|
||||
public TrackWhatGump(Mobile from) : base(20, 30)
|
||||
{
|
||||
m_From = from;
|
||||
m_Success = from.CheckSkill(SkillName.Tracking, 0.0, 21.1);
|
||||
|
||||
AddPage(0);
|
||||
|
||||
AddBackground(0, 0, 440, 135, 5054);
|
||||
|
||||
AddBackground(10, 10, 420, 75, 2620);
|
||||
AddBackground(10, 85, 420, 25, 3000);
|
||||
|
||||
AddItem(20, 20, 9682);
|
||||
AddButton(20, 110, 4005, 4007, 1);
|
||||
AddHtmlLocalized(20, 90, 100, 20, 1018087); // Animals
|
||||
|
||||
AddItem(120, 20, 9607);
|
||||
AddButton(120, 110, 4005, 4007, 2);
|
||||
AddHtmlLocalized(120, 90, 100, 20, 1018088); // Monsters
|
||||
|
||||
AddItem(220, 20, 8454);
|
||||
AddButton(220, 110, 4005, 4007, 3);
|
||||
AddHtmlLocalized(220, 90, 100, 20, 1018089); // Human NPCs
|
||||
|
||||
AddItem(320, 20, 8455);
|
||||
AddButton(320, 110, 4005, 4007, 4);
|
||||
AddHtmlLocalized(320, 90, 100, 20, 1018090); // Players
|
||||
}
|
||||
|
||||
public override void OnResponse(NetState state, RelayInfo info)
|
||||
{
|
||||
if (info.ButtonID >= 1 && info.ButtonID <= 4)
|
||||
TrackWhoGump.DisplayTo(m_Success, m_From, info.ButtonID - 1);
|
||||
}
|
||||
}
|
||||
|
||||
public delegate bool TrackTypeDelegate(Mobile m);
|
||||
|
||||
public class TrackWhoGump : Gump
|
||||
{
|
||||
private static readonly TrackTypeDelegate[] m_Delegates =
|
||||
{
|
||||
IsAnimal,
|
||||
IsMonster,
|
||||
IsHumanNPC,
|
||||
IsPlayer
|
||||
};
|
||||
|
||||
private readonly Mobile m_From;
|
||||
|
||||
private readonly List<Mobile> m_List;
|
||||
private readonly int m_Range;
|
||||
|
||||
private TrackWhoGump(Mobile from, List<Mobile> list, int range) : base(20, 30)
|
||||
{
|
||||
m_From = from;
|
||||
m_List = list;
|
||||
m_Range = range;
|
||||
|
||||
AddPage(0);
|
||||
|
||||
AddBackground(0, 0, 440, 155, 5054);
|
||||
|
||||
AddBackground(10, 10, 420, 75, 2620);
|
||||
AddBackground(10, 85, 420, 45, 3000);
|
||||
|
||||
if (list.Count > 4)
|
||||
{
|
||||
AddBackground(0, 155, 440, 155, 5054);
|
||||
|
||||
AddBackground(10, 165, 420, 75, 2620);
|
||||
AddBackground(10, 240, 420, 45, 3000);
|
||||
|
||||
if (list.Count > 8)
|
||||
public static void Initialize()
|
||||
{
|
||||
AddBackground(0, 310, 440, 155, 5054);
|
||||
|
||||
AddBackground(10, 320, 420, 75, 2620);
|
||||
AddBackground(10, 395, 420, 45, 3000);
|
||||
SkillInfo.Table[(int)SkillName.Tracking].Callback = OnUse;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < list.Count && i < 12; ++i)
|
||||
{
|
||||
Mobile m = list[i];
|
||||
public static TimeSpan OnUse(Mobile m)
|
||||
{
|
||||
m.SendLocalizedMessage(1011350); // What do you wish to track?
|
||||
|
||||
AddItem(20 + i % 4 * 100, 20 + i / 4 * 155, ShrinkTable.Lookup(m));
|
||||
AddButton(20 + i % 4 * 100, 130 + i / 4 * 155, 4005, 4007, i + 1);
|
||||
m.CloseGump<TrackWhatGump>();
|
||||
m.CloseGump<TrackWhoGump>();
|
||||
m.SendGump(new TrackWhatGump(m));
|
||||
|
||||
if (m.Name != null)
|
||||
AddHtml(20 + i % 4 * 100, 90 + i / 4 * 155, 90, 40, m.Name);
|
||||
}
|
||||
return TimeSpan.FromSeconds(10.0); // 10 second delay before being able to re-use a skill
|
||||
}
|
||||
|
||||
public static void AddInfo(Mobile tracker, Mobile target)
|
||||
{
|
||||
var info = new TrackingInfo(tracker, target);
|
||||
m_Table[tracker] = info;
|
||||
}
|
||||
|
||||
public static double GetStalkingBonus(Mobile tracker, Mobile target)
|
||||
{
|
||||
if (!m_Table.TryGetValue(tracker, out var info) || info.m_Target != target || info.m_Map != target.Map)
|
||||
return 0.0;
|
||||
|
||||
var xDelta = info.m_Location.X - target.X;
|
||||
var yDelta = info.m_Location.Y - target.Y;
|
||||
|
||||
var bonus = Math.Sqrt(xDelta * xDelta + yDelta * yDelta);
|
||||
|
||||
m_Table.Remove(tracker); // Reset as of Pub 40, counting it as bug for Core.SE.
|
||||
|
||||
return Core.ML ? Math.Min(bonus, 10 + tracker.Skills.Tracking.Value / 10) : bonus;
|
||||
}
|
||||
|
||||
public static void ClearTrackingInfo(Mobile tracker)
|
||||
{
|
||||
m_Table.Remove(tracker);
|
||||
}
|
||||
|
||||
public class TrackingInfo
|
||||
{
|
||||
public Point2D m_Location;
|
||||
public Map m_Map;
|
||||
public Mobile m_Target;
|
||||
public Mobile m_Tracker;
|
||||
|
||||
public TrackingInfo(Mobile tracker, Mobile target)
|
||||
{
|
||||
m_Tracker = tracker;
|
||||
m_Target = target;
|
||||
m_Location = new Point2D(target.X, target.Y);
|
||||
m_Map = target.Map;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void DisplayTo(bool success, Mobile from, int type)
|
||||
public class TrackWhatGump : Gump
|
||||
{
|
||||
if (!success)
|
||||
{
|
||||
from.SendLocalizedMessage(1018092); // You see no evidence of those in the area.
|
||||
return;
|
||||
}
|
||||
private readonly Mobile m_From;
|
||||
private readonly bool m_Success;
|
||||
|
||||
Map map = from.Map;
|
||||
public TrackWhatGump(Mobile from) : base(20, 30)
|
||||
{
|
||||
m_From = from;
|
||||
m_Success = from.CheckSkill(SkillName.Tracking, 0.0, 21.1);
|
||||
|
||||
if (map == null)
|
||||
return;
|
||||
AddPage(0);
|
||||
|
||||
TrackTypeDelegate check = m_Delegates[type];
|
||||
AddBackground(0, 0, 440, 135, 5054);
|
||||
|
||||
from.CheckSkill(SkillName.Tracking, 21.1, 100.0); // Passive gain
|
||||
AddBackground(10, 10, 420, 75, 2620);
|
||||
AddBackground(10, 85, 420, 25, 3000);
|
||||
|
||||
int range = 10 + (int)(from.Skills.Tracking.Value / 10);
|
||||
AddItem(20, 20, 9682);
|
||||
AddButton(20, 110, 4005, 4007, 1);
|
||||
AddHtmlLocalized(20, 90, 100, 20, 1018087); // Animals
|
||||
|
||||
List<Mobile> list = from.GetMobilesInRange(range)
|
||||
.Where(m => m != from && (!Core.AOS || m.Alive) && (!m.Hidden || m.AccessLevel == AccessLevel.Player || from.AccessLevel > m.AccessLevel) && check(m) && CheckDifficulty(from, m))
|
||||
.ToList();
|
||||
AddItem(120, 20, 9607);
|
||||
AddButton(120, 110, 4005, 4007, 2);
|
||||
AddHtmlLocalized(120, 90, 100, 20, 1018088); // Monsters
|
||||
|
||||
if (list.Count > 0)
|
||||
{
|
||||
list.Sort(new InternalSorter(from));
|
||||
AddItem(220, 20, 8454);
|
||||
AddButton(220, 110, 4005, 4007, 3);
|
||||
AddHtmlLocalized(220, 90, 100, 20, 1018089); // Human NPCs
|
||||
|
||||
from.SendGump(new TrackWhoGump(from, list, range));
|
||||
from.SendLocalizedMessage(1018093); // Select the one you would like to track.
|
||||
}
|
||||
else
|
||||
{
|
||||
if (type == 0)
|
||||
from.SendLocalizedMessage(502991); // You see no evidence of animals in the area.
|
||||
else if (type == 1)
|
||||
from.SendLocalizedMessage(502993); // You see no evidence of creatures in the area.
|
||||
else
|
||||
from.SendLocalizedMessage(502995); // You see no evidence of people in the area.
|
||||
}
|
||||
AddItem(320, 20, 8455);
|
||||
AddButton(320, 110, 4005, 4007, 4);
|
||||
AddHtmlLocalized(320, 90, 100, 20, 1018090); // Players
|
||||
}
|
||||
|
||||
public override void OnResponse(NetState state, RelayInfo info)
|
||||
{
|
||||
if (info.ButtonID >= 1 && info.ButtonID <= 4)
|
||||
TrackWhoGump.DisplayTo(m_Success, m_From, info.ButtonID - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Tracking players uses tracking and detect hidden vs. hiding and stealth
|
||||
private static bool CheckDifficulty(Mobile from, Mobile m)
|
||||
public delegate bool TrackTypeDelegate(Mobile m);
|
||||
|
||||
public class TrackWhoGump : Gump
|
||||
{
|
||||
if (!Core.AOS || !m.Player)
|
||||
return true;
|
||||
private static readonly TrackTypeDelegate[] m_Delegates =
|
||||
{
|
||||
IsAnimal,
|
||||
IsMonster,
|
||||
IsHumanNPC,
|
||||
IsPlayer
|
||||
};
|
||||
|
||||
int tracking = from.Skills.Tracking.Fixed;
|
||||
int detectHidden = from.Skills.DetectHidden.Fixed;
|
||||
private readonly Mobile m_From;
|
||||
|
||||
if (Core.ML && m.Race == Race.Elf)
|
||||
tracking /= 2; // The 'Guide' says that it requires twice as Much tracking SKILL to track an elf. Not the total difficulty to track.
|
||||
private readonly List<Mobile> m_List;
|
||||
private readonly int m_Range;
|
||||
|
||||
int hiding = m.Skills.Hiding.Fixed;
|
||||
int stealth = m.Skills.Stealth.Fixed;
|
||||
int divisor = hiding + stealth;
|
||||
private TrackWhoGump(Mobile from, List<Mobile> list, int range) : base(20, 30)
|
||||
{
|
||||
m_From = from;
|
||||
m_List = list;
|
||||
m_Range = range;
|
||||
|
||||
// Necromancy forms affect tracking difficulty
|
||||
if (TransformationSpellHelper.UnderTransformation(m, typeof(HorrificBeastSpell)))
|
||||
divisor -= 200;
|
||||
else if (TransformationSpellHelper.UnderTransformation(m, typeof(VampiricEmbraceSpell)) && divisor < 500)
|
||||
divisor = 500;
|
||||
else if (TransformationSpellHelper.UnderTransformation(m, typeof(WraithFormSpell)) && divisor <= 2000)
|
||||
divisor += 200;
|
||||
AddPage(0);
|
||||
|
||||
int chance;
|
||||
if (divisor > 0)
|
||||
{
|
||||
if (Core.SE)
|
||||
chance = 50 * (tracking * 2 + detectHidden) / divisor;
|
||||
else
|
||||
chance = 50 * (tracking + detectHidden + 10 * Utility.RandomMinMax(1, 20)) / divisor;
|
||||
}
|
||||
else
|
||||
{
|
||||
chance = 100;
|
||||
}
|
||||
AddBackground(0, 0, 440, 155, 5054);
|
||||
|
||||
return chance > Utility.Random(100);
|
||||
AddBackground(10, 10, 420, 75, 2620);
|
||||
AddBackground(10, 85, 420, 45, 3000);
|
||||
|
||||
if (list.Count > 4)
|
||||
{
|
||||
AddBackground(0, 155, 440, 155, 5054);
|
||||
|
||||
AddBackground(10, 165, 420, 75, 2620);
|
||||
AddBackground(10, 240, 420, 45, 3000);
|
||||
|
||||
if (list.Count > 8)
|
||||
{
|
||||
AddBackground(0, 310, 440, 155, 5054);
|
||||
|
||||
AddBackground(10, 320, 420, 75, 2620);
|
||||
AddBackground(10, 395, 420, 45, 3000);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < list.Count && i < 12; ++i)
|
||||
{
|
||||
var m = list[i];
|
||||
|
||||
AddItem(20 + i % 4 * 100, 20 + i / 4 * 155, ShrinkTable.Lookup(m));
|
||||
AddButton(20 + i % 4 * 100, 130 + i / 4 * 155, 4005, 4007, i + 1);
|
||||
|
||||
if (m.Name != null)
|
||||
AddHtml(20 + i % 4 * 100, 90 + i / 4 * 155, 90, 40, m.Name);
|
||||
}
|
||||
}
|
||||
|
||||
public static void DisplayTo(bool success, Mobile from, int type)
|
||||
{
|
||||
if (!success)
|
||||
{
|
||||
from.SendLocalizedMessage(1018092); // You see no evidence of those in the area.
|
||||
return;
|
||||
}
|
||||
|
||||
var map = from.Map;
|
||||
|
||||
if (map == null)
|
||||
return;
|
||||
|
||||
var check = m_Delegates[type];
|
||||
|
||||
from.CheckSkill(SkillName.Tracking, 21.1, 100.0); // Passive gain
|
||||
|
||||
var range = 10 + (int)(from.Skills.Tracking.Value / 10);
|
||||
|
||||
var list = from.GetMobilesInRange(range)
|
||||
.Where(
|
||||
m => m != from && (!Core.AOS || m.Alive) &&
|
||||
(!m.Hidden || m.AccessLevel == AccessLevel.Player || from.AccessLevel > m.AccessLevel) &&
|
||||
check(m) && CheckDifficulty(from, m)
|
||||
)
|
||||
.ToList();
|
||||
|
||||
if (list.Count > 0)
|
||||
{
|
||||
list.Sort(new InternalSorter(from));
|
||||
|
||||
from.SendGump(new TrackWhoGump(from, list, range));
|
||||
from.SendLocalizedMessage(1018093); // Select the one you would like to track.
|
||||
}
|
||||
else
|
||||
{
|
||||
if (type == 0)
|
||||
from.SendLocalizedMessage(502991); // You see no evidence of animals in the area.
|
||||
else if (type == 1)
|
||||
from.SendLocalizedMessage(502993); // You see no evidence of creatures in the area.
|
||||
else
|
||||
from.SendLocalizedMessage(502995); // You see no evidence of people in the area.
|
||||
}
|
||||
}
|
||||
|
||||
// Tracking players uses tracking and detect hidden vs. hiding and stealth
|
||||
private static bool CheckDifficulty(Mobile from, Mobile m)
|
||||
{
|
||||
if (!Core.AOS || !m.Player)
|
||||
return true;
|
||||
|
||||
var tracking = from.Skills.Tracking.Fixed;
|
||||
var detectHidden = from.Skills.DetectHidden.Fixed;
|
||||
|
||||
if (Core.ML && m.Race == Race.Elf)
|
||||
tracking /= 2; // The 'Guide' says that it requires twice as Much tracking SKILL to track an elf. Not the total difficulty to track.
|
||||
|
||||
var hiding = m.Skills.Hiding.Fixed;
|
||||
var stealth = m.Skills.Stealth.Fixed;
|
||||
var divisor = hiding + stealth;
|
||||
|
||||
// Necromancy forms affect tracking difficulty
|
||||
if (TransformationSpellHelper.UnderTransformation(m, typeof(HorrificBeastSpell)))
|
||||
divisor -= 200;
|
||||
else if (TransformationSpellHelper.UnderTransformation(m, typeof(VampiricEmbraceSpell)) && divisor < 500)
|
||||
divisor = 500;
|
||||
else if (TransformationSpellHelper.UnderTransformation(m, typeof(WraithFormSpell)) && divisor <= 2000)
|
||||
divisor += 200;
|
||||
|
||||
int chance;
|
||||
if (divisor > 0)
|
||||
{
|
||||
if (Core.SE)
|
||||
chance = 50 * (tracking * 2 + detectHidden) / divisor;
|
||||
else
|
||||
chance = 50 * (tracking + detectHidden + 10 * Utility.RandomMinMax(1, 20)) / divisor;
|
||||
}
|
||||
else
|
||||
{
|
||||
chance = 100;
|
||||
}
|
||||
|
||||
return chance > Utility.Random(100);
|
||||
}
|
||||
|
||||
private static bool IsAnimal(Mobile m) => !m.Player && m.Body.IsAnimal;
|
||||
|
||||
private static bool IsMonster(Mobile m) => !m.Player && m.Body.IsMonster;
|
||||
|
||||
private static bool IsHumanNPC(Mobile m) => !m.Player && m.Body.IsHuman;
|
||||
|
||||
private static bool IsPlayer(Mobile m) => m.Player;
|
||||
|
||||
public override void OnResponse(NetState state, RelayInfo info)
|
||||
{
|
||||
var index = info.ButtonID - 1;
|
||||
|
||||
if (index >= 0 && index < m_List.Count && index < 12)
|
||||
{
|
||||
var m = m_List[index];
|
||||
|
||||
m_From.QuestArrow = new TrackArrow(m_From, m, m_Range * 2);
|
||||
|
||||
if (Core.SE)
|
||||
Tracking.AddInfo(m_From, m);
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalSorter : IComparer<Mobile>
|
||||
{
|
||||
private readonly Mobile m_From;
|
||||
|
||||
public InternalSorter(Mobile from) => m_From = from;
|
||||
|
||||
public int Compare(Mobile x, Mobile y)
|
||||
{
|
||||
if (x == null && y == null)
|
||||
return 0;
|
||||
if (x == null)
|
||||
return -1;
|
||||
if (y == null)
|
||||
return 1;
|
||||
|
||||
return m_From.GetDistanceToSqrt(x).CompareTo(m_From.GetDistanceToSqrt(y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsAnimal(Mobile m) => !m.Player && m.Body.IsAnimal;
|
||||
|
||||
private static bool IsMonster(Mobile m) => !m.Player && m.Body.IsMonster;
|
||||
|
||||
private static bool IsHumanNPC(Mobile m) => !m.Player && m.Body.IsHuman;
|
||||
|
||||
private static bool IsPlayer(Mobile m) => m.Player;
|
||||
|
||||
public override void OnResponse(NetState state, RelayInfo info)
|
||||
public class TrackArrow : QuestArrow
|
||||
{
|
||||
int index = info.ButtonID - 1;
|
||||
private readonly Timer m_Timer;
|
||||
private Mobile m_From;
|
||||
|
||||
if (index >= 0 && index < m_List.Count && index < 12)
|
||||
{
|
||||
Mobile m = m_List[index];
|
||||
public TrackArrow(Mobile from, Mobile target, int range) : base(from, target)
|
||||
{
|
||||
m_From = from;
|
||||
m_Timer = new TrackTimer(from, target, range, this);
|
||||
m_Timer.Start();
|
||||
}
|
||||
|
||||
m_From.QuestArrow = new TrackArrow(m_From, m, m_Range * 2);
|
||||
public override void OnClick(bool rightClick)
|
||||
{
|
||||
if (rightClick)
|
||||
{
|
||||
Tracking.ClearTrackingInfo(m_From);
|
||||
|
||||
if (Core.SE)
|
||||
Tracking.AddInfo(m_From, m);
|
||||
}
|
||||
m_From = null;
|
||||
|
||||
Stop();
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnStop()
|
||||
{
|
||||
m_Timer.Stop();
|
||||
|
||||
if (m_From != null)
|
||||
{
|
||||
Tracking.ClearTrackingInfo(m_From);
|
||||
|
||||
m_From.SendLocalizedMessage(503177); // You have lost your quarry.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalSorter : IComparer<Mobile>
|
||||
public class TrackTimer : Timer
|
||||
{
|
||||
private readonly Mobile m_From;
|
||||
private readonly QuestArrow m_Arrow;
|
||||
private readonly Mobile m_From;
|
||||
private readonly int m_Range;
|
||||
private readonly Mobile m_Target;
|
||||
private int m_LastX, m_LastY;
|
||||
|
||||
public InternalSorter(Mobile from) => m_From = from;
|
||||
public TrackTimer(Mobile from, Mobile target, int range, QuestArrow arrow) : base(
|
||||
TimeSpan.FromSeconds(0.25),
|
||||
TimeSpan.FromSeconds(2.5)
|
||||
)
|
||||
{
|
||||
m_From = from;
|
||||
m_Target = target;
|
||||
m_Range = range;
|
||||
|
||||
public int Compare(Mobile x, Mobile y)
|
||||
{
|
||||
if (x == null && y == null)
|
||||
return 0;
|
||||
if (x == null)
|
||||
return -1;
|
||||
if (y == null)
|
||||
return 1;
|
||||
m_Arrow = arrow;
|
||||
}
|
||||
|
||||
return m_From.GetDistanceToSqrt(x).CompareTo(m_From.GetDistanceToSqrt(y));
|
||||
}
|
||||
protected override void OnTick()
|
||||
{
|
||||
if (!m_Arrow.Running)
|
||||
{
|
||||
Stop();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_From.NetState == null || m_From.Deleted || m_Target.Deleted || m_From.Map != m_Target.Map ||
|
||||
!m_From.InRange(m_Target, m_Range) || m_Target.Hidden && m_Target.AccessLevel > m_From.AccessLevel)
|
||||
{
|
||||
m_Arrow.Stop();
|
||||
Stop();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_LastX != m_Target.X || m_LastY != m_Target.Y)
|
||||
{
|
||||
m_LastX = m_Target.X;
|
||||
m_LastY = m_Target.Y;
|
||||
|
||||
m_Arrow.Update();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class TrackArrow : QuestArrow
|
||||
{
|
||||
private Mobile m_From;
|
||||
private readonly Timer m_Timer;
|
||||
|
||||
public TrackArrow(Mobile from, Mobile target, int range) : base(from, target)
|
||||
{
|
||||
m_From = from;
|
||||
m_Timer = new TrackTimer(from, target, range, this);
|
||||
m_Timer.Start();
|
||||
}
|
||||
|
||||
public override void OnClick(bool rightClick)
|
||||
{
|
||||
if (rightClick)
|
||||
{
|
||||
Tracking.ClearTrackingInfo(m_From);
|
||||
|
||||
m_From = null;
|
||||
|
||||
Stop();
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnStop()
|
||||
{
|
||||
m_Timer.Stop();
|
||||
|
||||
if (m_From != null)
|
||||
{
|
||||
Tracking.ClearTrackingInfo(m_From);
|
||||
|
||||
m_From.SendLocalizedMessage(503177); // You have lost your quarry.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class TrackTimer : Timer
|
||||
{
|
||||
private readonly QuestArrow m_Arrow;
|
||||
private readonly Mobile m_From;
|
||||
private readonly Mobile m_Target;
|
||||
private int m_LastX, m_LastY;
|
||||
private readonly int m_Range;
|
||||
|
||||
public TrackTimer(Mobile from, Mobile target, int range, QuestArrow arrow) : base(TimeSpan.FromSeconds(0.25),
|
||||
TimeSpan.FromSeconds(2.5))
|
||||
{
|
||||
m_From = from;
|
||||
m_Target = target;
|
||||
m_Range = range;
|
||||
|
||||
m_Arrow = arrow;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
if (!m_Arrow.Running)
|
||||
{
|
||||
Stop();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_From.NetState == null || m_From.Deleted || m_Target.Deleted || m_From.Map != m_Target.Map ||
|
||||
!m_From.InRange(m_Target, m_Range) || (m_Target.Hidden && m_Target.AccessLevel > m_From.AccessLevel))
|
||||
{
|
||||
m_Arrow.Stop();
|
||||
Stop();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_LastX != m_Target.X || m_LastY != m_Target.Y)
|
||||
{
|
||||
m_LastX = m_Target.X;
|
||||
m_LastY = m_Target.Y;
|
||||
|
||||
m_Arrow.Update();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue