Some cleanup of contains keys and try get values (#246)
This commit is contained in:
parent
9cbb0cfd23
commit
a236617ad9
69 changed files with 3508 additions and 350 deletions
|
|
@ -31,11 +31,11 @@ namespace Server.ContextMenus
|
|||
|
||||
if (target is Mobile mobile)
|
||||
{
|
||||
mobile.GetContextMenuEntries(@from, list);
|
||||
mobile.GetContextMenuEntries(from, list);
|
||||
}
|
||||
else if (target is Item item)
|
||||
{
|
||||
item.GetContextMenuEntries(@from, list);
|
||||
item.GetContextMenuEntries(from, list);
|
||||
}
|
||||
|
||||
Entries = list.ToArray();
|
||||
|
|
|
|||
|
|
@ -570,7 +570,7 @@ namespace Server.Items
|
|||
|
||||
for (var i = 0; i < stackItems.Count; i++)
|
||||
{
|
||||
stackItems[i].m_StackItem.StackWith(@from, stackItems[i].m_DropItem, false);
|
||||
stackItems[i].m_StackItem.StackWith(from, stackItems[i].m_DropItem, false);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -674,7 +674,7 @@ namespace Server.Items
|
|||
|
||||
if (CheckContentDisplay(from))
|
||||
{
|
||||
LabelTo(@from, "({0} item{2}, {1} stones)", TotalItems, TotalWeight, TotalItems != 1 ? "s" : string.Empty);
|
||||
LabelTo(from, "({0} item{2}, {1} stones)", TotalItems, TotalWeight, TotalItems != 1 ? "s" : string.Empty);
|
||||
}
|
||||
|
||||
// LabelTo( from, 1050044, String.Format( "{0}\t{1}", TotalItems.ToString(), TotalWeight.ToString() ) );
|
||||
|
|
@ -824,11 +824,11 @@ namespace Server.Items
|
|||
{
|
||||
if (from.AccessLevel > AccessLevel.Player || from.InRange(GetWorldLocation(), 2))
|
||||
{
|
||||
DisplayTo(@from);
|
||||
DisplayTo(from);
|
||||
}
|
||||
else
|
||||
{
|
||||
@from.SendLocalizedMessage(500446); // That is too far away.
|
||||
from.SendLocalizedMessage(500446); // That is too far away.
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2254,11 +2254,11 @@ namespace Server
|
|||
{
|
||||
if (m_Parent is Item parentItem)
|
||||
{
|
||||
parentItem.GetChildContextMenuEntries(@from, list, item);
|
||||
parentItem.GetChildContextMenuEntries(from, list, item);
|
||||
}
|
||||
else if (m_Parent is Mobile parentMobile)
|
||||
{
|
||||
parentMobile.GetChildContextMenuEntries(@from, list, item);
|
||||
parentMobile.GetChildContextMenuEntries(from, list, item);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2266,11 +2266,11 @@ namespace Server
|
|||
{
|
||||
if (m_Parent is Item item)
|
||||
{
|
||||
item.GetChildContextMenuEntries(@from, list, this);
|
||||
item.GetChildContextMenuEntries(from, list, this);
|
||||
}
|
||||
else if (m_Parent is Mobile mobile)
|
||||
{
|
||||
mobile.GetChildContextMenuEntries(@from, list, this);
|
||||
mobile.GetChildContextMenuEntries(from, list, this);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3241,7 +3241,7 @@ namespace Server
|
|||
// OSI sends 1074769, bug!
|
||||
if (QuestItem)
|
||||
{
|
||||
@from.SendLocalizedMessage(
|
||||
from.SendLocalizedMessage(
|
||||
1049343
|
||||
); // You can only drop quest items into the top-most level of your backpack while you still need them for your quest.
|
||||
}
|
||||
|
|
@ -3667,7 +3667,7 @@ namespace Server
|
|||
|
||||
if (target is Container container && p.m_X != -1 && p.m_Y != -1)
|
||||
{
|
||||
return OnDroppedInto(@from, container, p);
|
||||
return OnDroppedInto(from, container, p);
|
||||
}
|
||||
|
||||
return OnDroppedOnto(from, target);
|
||||
|
|
@ -4212,11 +4212,11 @@ namespace Server
|
|||
{
|
||||
if (m_Parent is Item parentItem)
|
||||
{
|
||||
parentItem.OnItemUsed(@from, item);
|
||||
parentItem.OnItemUsed(from, item);
|
||||
}
|
||||
else if (m_Parent is Mobile parentMobile)
|
||||
{
|
||||
parentMobile.OnItemUsed(@from, item);
|
||||
parentMobile.OnItemUsed(from, item);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4234,11 +4234,11 @@ namespace Server
|
|||
{
|
||||
if (m_Parent is Item parentItem)
|
||||
{
|
||||
parentItem.OnItemLifted(@from, item);
|
||||
parentItem.OnItemLifted(from, item);
|
||||
}
|
||||
else if (m_Parent is Mobile parentMobile)
|
||||
{
|
||||
parentMobile.OnItemLifted(@from, item);
|
||||
parentMobile.OnItemLifted(from, item);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4261,7 +4261,7 @@ namespace Server
|
|||
{
|
||||
if (m_Parent is Item parentItem)
|
||||
{
|
||||
parentItem.OnSingleClickContained(@from, item);
|
||||
parentItem.OnSingleClickContained(from, item);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4271,7 +4271,7 @@ namespace Server
|
|||
|
||||
if (opl.Header > 0)
|
||||
{
|
||||
@from.Send(
|
||||
from.Send(
|
||||
new MessageLocalized(
|
||||
Serial,
|
||||
m_ItemID,
|
||||
|
|
@ -4295,7 +4295,7 @@ namespace Server
|
|||
|
||||
if (DisplayLootType)
|
||||
{
|
||||
LabelLootTypeTo(@from);
|
||||
LabelLootTypeTo(from);
|
||||
}
|
||||
|
||||
var ns = from.NetState;
|
||||
|
|
|
|||
|
|
@ -3758,7 +3758,7 @@ namespace Server
|
|||
}
|
||||
else
|
||||
{
|
||||
hue = Notoriety.GetHue(Notoriety.Compute(@from, this));
|
||||
hue = Notoriety.GetHue(Notoriety.Compute(from, this));
|
||||
}
|
||||
|
||||
from.Send(new MessageLocalized(Serial, Body, MessageType.Label, hue, 3, opl.Header, Name, opl.HeaderArgs));
|
||||
|
|
@ -5571,7 +5571,7 @@ namespace Server
|
|||
{
|
||||
if (item.QuestItem)
|
||||
{
|
||||
@from.SendLocalizedMessage(1074868); // Stacks of quest items cannot be unstacked.
|
||||
from.SendLocalizedMessage(1074868); // Stacks of quest items cannot be unstacked.
|
||||
}
|
||||
|
||||
reject = LRReason.CannotLift;
|
||||
|
|
@ -5634,7 +5634,7 @@ namespace Server
|
|||
|
||||
foreach (var ns in eable)
|
||||
{
|
||||
if (ns.Mobile != @from && ns.Mobile.CanSee(@from) && ns.Mobile.InLOS(@from) &&
|
||||
if (ns.Mobile != from && ns.Mobile.CanSee(from) && ns.Mobile.InLOS(from) &&
|
||||
ns.Mobile.CanSee(root))
|
||||
{
|
||||
if (p == null)
|
||||
|
|
@ -5645,7 +5645,7 @@ namespace Server
|
|||
map
|
||||
);
|
||||
|
||||
p = Packet.Acquire(new DragEffect(src, @from, item.ItemID, item.Hue, amount));
|
||||
p = Packet.Acquire(new DragEffect(src, from, item.ItemID, item.Hue, amount));
|
||||
}
|
||||
|
||||
ns.Send(p);
|
||||
|
|
@ -5671,7 +5671,7 @@ namespace Server
|
|||
|
||||
if (liftSound != -1)
|
||||
{
|
||||
@from.Send(new PlaySound(liftSound, @from));
|
||||
from.Send(new PlaySound(liftSound, from));
|
||||
}
|
||||
|
||||
from.NextActionTime = Core.TickCount + ActionDelay;
|
||||
|
|
@ -5845,7 +5845,7 @@ namespace Server
|
|||
|
||||
if (to == null || !item.DropToItem(from, to, loc))
|
||||
{
|
||||
item.Bounce(@from);
|
||||
item.Bounce(from);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -5882,7 +5882,7 @@ namespace Server
|
|||
|
||||
if (!item.DropToWorld(from, loc))
|
||||
{
|
||||
item.Bounce(@from);
|
||||
item.Bounce(from);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -5919,7 +5919,7 @@ namespace Server
|
|||
|
||||
if (to == null || !item.DropToMobile(from, to, loc))
|
||||
{
|
||||
item.Bounce(@from);
|
||||
item.Bounce(from);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -6443,7 +6443,7 @@ namespace Server
|
|||
|
||||
if (from != null)
|
||||
{
|
||||
RegisterDamage(amount, @from);
|
||||
RegisterDamage(amount, from);
|
||||
}
|
||||
|
||||
DisruptiveAction();
|
||||
|
|
@ -6473,7 +6473,7 @@ namespace Server
|
|||
|
||||
if (informMount)
|
||||
{
|
||||
Mount?.OnRiderDamaged(amount, @from, newHits < 0);
|
||||
Mount?.OnRiderDamaged(amount, from, newHits < 0);
|
||||
}
|
||||
|
||||
if (newHits < 0)
|
||||
|
|
@ -8624,7 +8624,7 @@ namespace Server
|
|||
{
|
||||
if (from.Map == Map && Utility.InUpdateRange(this, from) && from.CanSee(this))
|
||||
{
|
||||
@from.Send(new MobileStatus(@from, this, m_NetState));
|
||||
from.Send(new MobileStatus(from, this, m_NetState));
|
||||
}
|
||||
|
||||
if (from == this)
|
||||
|
|
@ -8634,7 +8634,7 @@ namespace Server
|
|||
|
||||
if (Party is IParty ip)
|
||||
{
|
||||
ip.OnStatsQuery(@from, this);
|
||||
ip.OnStatsQuery(from, this);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -8708,7 +8708,7 @@ namespace Server
|
|||
}
|
||||
else
|
||||
{
|
||||
hue = Notoriety.GetHue(Notoriety.Compute(@from, this));
|
||||
hue = Notoriety.GetHue(Notoriety.Compute(from, this));
|
||||
}
|
||||
|
||||
var name = Name ?? string.Empty;
|
||||
|
|
@ -10143,7 +10143,7 @@ namespace Server
|
|||
|
||||
if (CanPaperdollBeOpenedBy(from))
|
||||
{
|
||||
DisplayPaperdollTo(@from);
|
||||
DisplayPaperdollTo(from);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -10175,7 +10175,7 @@ namespace Server
|
|||
{
|
||||
if (CanPaperdollBeOpenedBy(from))
|
||||
{
|
||||
DisplayPaperdollTo(@from);
|
||||
DisplayPaperdollTo(from);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -10590,11 +10590,11 @@ namespace Server
|
|||
{
|
||||
if (m_CallbackHandlesCancel && m_Callback != null)
|
||||
{
|
||||
m_Callback(@from, "");
|
||||
m_Callback(from, "");
|
||||
}
|
||||
else
|
||||
{
|
||||
m_CancelCallback?.Invoke(@from, "");
|
||||
m_CancelCallback?.Invoke(from, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -433,7 +433,7 @@ namespace Server.Network
|
|||
|
||||
if (targ != null)
|
||||
{
|
||||
EventSink.InvokeRenameRequest(@from, targ, pvSrc.ReadStringSafe());
|
||||
EventSink.InvokeRenameRequest(from, targ, pvSrc.ReadStringSafe());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -631,7 +631,7 @@ namespace Server.Network
|
|||
if (item != null && from.Map == item.Map && Utility.InUpdateRange(item.GetWorldLocation(), from.Location) &&
|
||||
from.CanSee(item))
|
||||
{
|
||||
item.OnHelpRequest(@from);
|
||||
item.OnHelpRequest(from);
|
||||
}
|
||||
}
|
||||
else if (serial.IsMobile)
|
||||
|
|
@ -668,7 +668,7 @@ namespace Server.Network
|
|||
|
||||
if (m != null)
|
||||
{
|
||||
@from.Attack(m);
|
||||
from.Attack(m);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -827,11 +827,11 @@ namespace Server.Network
|
|||
|
||||
if (type == 0)
|
||||
{
|
||||
p.OnCancel(@from);
|
||||
p.OnCancel(from);
|
||||
}
|
||||
else
|
||||
{
|
||||
p.OnResponse(@from, text);
|
||||
p.OnResponse(from, text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -858,11 +858,11 @@ namespace Server.Network
|
|||
|
||||
if (type == 0)
|
||||
{
|
||||
p.OnCancel(@from);
|
||||
p.OnCancel(from);
|
||||
}
|
||||
else
|
||||
{
|
||||
p.OnResponse(@from, text);
|
||||
p.OnResponse(from, text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -970,7 +970,7 @@ namespace Server.Network
|
|||
|
||||
if (!to.AllowEquipFrom(from) || !to.EquipItem(item))
|
||||
{
|
||||
item.Bounce(@from);
|
||||
item.Bounce(from);
|
||||
}
|
||||
|
||||
item.ClearBounce();
|
||||
|
|
@ -1458,7 +1458,7 @@ namespace Server.Network
|
|||
|
||||
if (m?.Deleted == false)
|
||||
{
|
||||
@from.Use(m);
|
||||
from.Use(m);
|
||||
}
|
||||
}
|
||||
else if (s.IsItem)
|
||||
|
|
@ -1467,7 +1467,7 @@ namespace Server.Network
|
|||
|
||||
if (item?.Deleted == false)
|
||||
{
|
||||
@from.Use(item);
|
||||
from.Use(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1500,7 +1500,7 @@ namespace Server.Network
|
|||
{
|
||||
if (from.Region.OnSingleClick(from, m))
|
||||
{
|
||||
m.OnSingleClick(@from);
|
||||
m.OnSingleClick(from);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1520,7 +1520,7 @@ namespace Server.Network
|
|||
{
|
||||
if (item.Parent is Item parentItem)
|
||||
{
|
||||
parentItem.OnSingleClickContained(@from, item);
|
||||
parentItem.OnSingleClickContained(from, item);
|
||||
}
|
||||
|
||||
item.OnSingleClick(from);
|
||||
|
|
@ -1581,7 +1581,7 @@ namespace Server.Network
|
|||
|
||||
if (from != null && ok && from.Alive && from.Body.IsHuman && !from.Mounted)
|
||||
{
|
||||
@from.Animate(action, 7, 1, true, false, 0);
|
||||
from.Animate(action, 7, 1, true, false, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1711,7 +1711,7 @@ namespace Server.Network
|
|||
|
||||
if (m != null && from.CanSee(m) && Utility.InUpdateRange(from, m))
|
||||
{
|
||||
m.SendPropertiesTo(@from);
|
||||
m.SendPropertiesTo(from);
|
||||
}
|
||||
}
|
||||
else if (s.IsItem)
|
||||
|
|
@ -1721,7 +1721,7 @@ namespace Server.Network
|
|||
if (item?.Deleted == false && from.CanSee(item) &&
|
||||
Utility.InUpdateRange(from.Location, item.GetWorldLocation()))
|
||||
{
|
||||
item.SendPropertiesTo(@from);
|
||||
item.SendPropertiesTo(from);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1744,7 +1744,7 @@ namespace Server.Network
|
|||
|
||||
if (m != null && from.CanSee(m) && Utility.InUpdateRange(from, m))
|
||||
{
|
||||
m.SendPropertiesTo(@from);
|
||||
m.SendPropertiesTo(from);
|
||||
}
|
||||
}
|
||||
else if (s.IsItem)
|
||||
|
|
@ -1754,7 +1754,7 @@ namespace Server.Network
|
|||
if (item?.Deleted == false && from.CanSee(item) &&
|
||||
Utility.InUpdateRange(from.Location, item.GetWorldLocation()))
|
||||
{
|
||||
item.SendPropertiesTo(@from);
|
||||
item.SendPropertiesTo(from);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2031,12 +2031,12 @@ namespace Server.Network
|
|||
{
|
||||
case 0x04: // Stats
|
||||
{
|
||||
m.OnStatsQuery(@from);
|
||||
m.OnStatsQuery(from);
|
||||
break;
|
||||
}
|
||||
case 0x05:
|
||||
{
|
||||
m.OnSkillsQuery(@from);
|
||||
m.OnSkillsQuery(from);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -25,22 +25,22 @@ namespace Server
|
|||
|
||||
if (from6017)
|
||||
{
|
||||
@from.Send(new SecureTradeEquip6017(To.Container, to));
|
||||
from.Send(new SecureTradeEquip6017(To.Container, to));
|
||||
}
|
||||
else
|
||||
{
|
||||
@from.Send(new SecureTradeEquip(To.Container, to));
|
||||
from.Send(new SecureTradeEquip(To.Container, to));
|
||||
}
|
||||
|
||||
from.Send(new UpdateSecureTrade(From.Container, false, false));
|
||||
|
||||
if (from6017)
|
||||
{
|
||||
@from.Send(new SecureTradeEquip6017(From.Container, @from));
|
||||
from.Send(new SecureTradeEquip6017(From.Container, from));
|
||||
}
|
||||
else
|
||||
{
|
||||
@from.Send(new SecureTradeEquip(From.Container, @from));
|
||||
from.Send(new SecureTradeEquip(From.Container, from));
|
||||
}
|
||||
|
||||
from.Send(new DisplaySecureTrade(to, From.Container, To.Container, to.Name));
|
||||
|
|
@ -48,12 +48,12 @@ namespace Server
|
|||
|
||||
if (from.Account != null && from704565)
|
||||
{
|
||||
@from.Send(
|
||||
from.Send(
|
||||
new UpdateSecureTrade(
|
||||
From.Container,
|
||||
TradeFlag.UpdateLedger,
|
||||
@from.Account.TotalGold,
|
||||
@from.Account.TotalPlat
|
||||
from.Account.TotalGold,
|
||||
from.Account.TotalPlat
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
@ -63,11 +63,11 @@ namespace Server
|
|||
|
||||
if (to6017)
|
||||
{
|
||||
to.Send(new SecureTradeEquip6017(From.Container, @from));
|
||||
to.Send(new SecureTradeEquip6017(From.Container, from));
|
||||
}
|
||||
else
|
||||
{
|
||||
to.Send(new SecureTradeEquip(From.Container, @from));
|
||||
to.Send(new SecureTradeEquip(From.Container, from));
|
||||
}
|
||||
|
||||
to.Send(new UpdateSecureTrade(To.Container, false, false));
|
||||
|
|
|
|||
|
|
@ -168,31 +168,31 @@ namespace Server.Targeting
|
|||
{
|
||||
if (!from.CanSee(targeted))
|
||||
{
|
||||
OnCantSeeTarget(@from, targeted);
|
||||
OnCantSeeTarget(from, targeted);
|
||||
}
|
||||
else if (CheckLOS && !from.InLOS(targeted))
|
||||
{
|
||||
OnTargetOutOfLOS(@from, targeted);
|
||||
OnTargetOutOfLOS(from, targeted);
|
||||
}
|
||||
else if (item?.InSecureTrade == true)
|
||||
{
|
||||
OnTargetInSecureTrade(@from, targeted);
|
||||
OnTargetInSecureTrade(from, targeted);
|
||||
}
|
||||
else if (item?.IsAccessibleTo(from) == false)
|
||||
{
|
||||
OnTargetNotAccessible(@from, targeted);
|
||||
OnTargetNotAccessible(from, targeted);
|
||||
}
|
||||
else if (item?.CheckTarget(from, this, targeted) == false)
|
||||
{
|
||||
OnTargetUntargetable(@from, targeted);
|
||||
OnTargetUntargetable(from, targeted);
|
||||
}
|
||||
else if (mobile?.CheckTarget(from, this, mobile) == false)
|
||||
{
|
||||
OnTargetUntargetable(@from, mobile);
|
||||
OnTargetUntargetable(from, mobile);
|
||||
}
|
||||
else if (from.Region.OnTarget(from, this, targeted))
|
||||
{
|
||||
OnTarget(@from, targeted);
|
||||
OnTarget(from, targeted);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -63,7 +63,9 @@ namespace Server.Accounting
|
|||
|
||||
// Automatically upgrade plain passwords to current algorithm.
|
||||
if (!upgraded)
|
||||
{
|
||||
SetPassword(Utility.GetText(node["password"], null));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -85,19 +87,31 @@ namespace Server.Accounting
|
|||
IPRestrictions = LoadAccessCheck(node);
|
||||
|
||||
for (var i = 0; i < m_Mobiles.Length; ++i)
|
||||
{
|
||||
if (m_Mobiles[i] != null)
|
||||
{
|
||||
m_Mobiles[i].Account = this;
|
||||
}
|
||||
}
|
||||
|
||||
var totalGameTime = Utility.GetXMLTimeSpan(Utility.GetText(node["totalGameTime"], null), TimeSpan.Zero);
|
||||
if (totalGameTime == TimeSpan.Zero)
|
||||
{
|
||||
for (var i = 0; i < m_Mobiles.Length; i++)
|
||||
{
|
||||
if (m_Mobiles[i] is PlayerMobile m)
|
||||
{
|
||||
totalGameTime += m.GameTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_TotalGameTime = totalGameTime;
|
||||
|
||||
if (Young)
|
||||
{
|
||||
CheckYoung();
|
||||
}
|
||||
|
||||
Accounts.Add(this);
|
||||
}
|
||||
|
|
@ -148,15 +162,19 @@ namespace Server.Accounting
|
|||
var isBanned = GetFlag(0);
|
||||
|
||||
if (!isBanned)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (GetBanTags(out var banTime, out var banDuration))
|
||||
{
|
||||
if (banDuration != TimeSpan.MaxValue && DateTime.UtcNow >= banTime + banDuration)
|
||||
{
|
||||
SetUnspecifiedBan(null); // clear
|
||||
Banned = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -197,7 +215,9 @@ namespace Server.Accounting
|
|||
get
|
||||
{
|
||||
if (AccessLevel != AccessLevel.Player)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var inactiveLength = DateTime.UtcNow - LastLogin;
|
||||
|
||||
|
|
@ -214,8 +234,12 @@ namespace Server.Accounting
|
|||
get
|
||||
{
|
||||
for (var i = 0; i < m_Mobiles.Length; i++)
|
||||
{
|
||||
if (m_Mobiles[i] is PlayerMobile m && m.NetState != null)
|
||||
{
|
||||
return m_TotalGameTime + (DateTime.UtcNow - m.SessionStart);
|
||||
}
|
||||
}
|
||||
|
||||
return m_TotalGameTime;
|
||||
}
|
||||
|
|
@ -231,12 +255,16 @@ namespace Server.Accounting
|
|||
var m = this[i];
|
||||
|
||||
if (m == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var list = BaseHouse.GetHouses(m);
|
||||
|
||||
for (var j = 0; j < list.Count; ++j)
|
||||
{
|
||||
list[j].Delete();
|
||||
}
|
||||
|
||||
m.Delete();
|
||||
|
||||
|
|
@ -245,7 +273,9 @@ namespace Server.Accounting
|
|||
}
|
||||
|
||||
if (LoginIPs.Length != 0 && AccountHandler.IPTable.ContainsKey(LoginIPs[0]))
|
||||
{
|
||||
--AccountHandler.IPTable[LoginIPs[0]];
|
||||
}
|
||||
|
||||
Accounts.Remove(Username);
|
||||
}
|
||||
|
|
@ -283,11 +313,15 @@ namespace Server.Accounting
|
|||
|
||||
var ok = AccountSecurity.GetPasswordProtection(m_PasswordAlgorithm).ValidatePassword(Password, phrase);
|
||||
if (!ok)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Upgrade the password protection in case we change the algorithm
|
||||
if (m_PasswordAlgorithm != AccountSecurity.CurrentAlgorithm)
|
||||
{
|
||||
SetPassword(plainPassword);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -302,8 +336,12 @@ namespace Server.Accounting
|
|||
var count = 0;
|
||||
|
||||
for (var i = 0; i < Length; i++)
|
||||
{
|
||||
if (this[i] != null)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
|
@ -334,7 +372,9 @@ namespace Server.Accounting
|
|||
var m = m_Mobiles[index];
|
||||
|
||||
if (m?.Deleted != true)
|
||||
{
|
||||
return m;
|
||||
}
|
||||
|
||||
// This is the only place that clears a mobile for garbage collection
|
||||
// outside of an entire account deletion.
|
||||
|
|
@ -349,12 +389,16 @@ namespace Server.Accounting
|
|||
if (index >= 0 && index < m_Mobiles.Length)
|
||||
{
|
||||
if (m_Mobiles[index] != null)
|
||||
{
|
||||
m_Mobiles[index].Account = null;
|
||||
}
|
||||
|
||||
m_Mobiles[index] = value;
|
||||
|
||||
if (m_Mobiles[index] != null)
|
||||
{
|
||||
m_Mobiles[index].Account = this;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -387,7 +431,10 @@ namespace Server.Accounting
|
|||
/// <returns>True if successful, false if amount given is less than or equal to zero.</returns>
|
||||
public bool DepositGold(int amount)
|
||||
{
|
||||
if (amount <= 0) return false;
|
||||
if (amount <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var plat = Math.DivRem(amount, AccountGold.CurrencyThreshold, out var gold);
|
||||
TotalPlat += plat;
|
||||
|
|
@ -403,7 +450,10 @@ namespace Server.Accounting
|
|||
/// <returns>True if successful, false if amount given is less than or equal to zero.</returns>
|
||||
public bool DepositPlat(int amount)
|
||||
{
|
||||
if (amount <= 0) return false;
|
||||
if (amount <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
TotalPlat += amount;
|
||||
return true;
|
||||
|
|
@ -418,8 +468,15 @@ namespace Server.Accounting
|
|||
/// <returns>True if successful, false if balance was too low.</returns>
|
||||
public bool WithdrawGold(int amount)
|
||||
{
|
||||
if (amount <= 0) return true;
|
||||
if (amount > TotalGold) return false;
|
||||
if (amount <= 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (amount > TotalGold)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
TotalGold -= amount;
|
||||
|
||||
|
|
@ -433,8 +490,15 @@ namespace Server.Accounting
|
|||
/// <returns>True if successful, false if balance was too low.</returns>
|
||||
public bool WithdrawPlat(int amount)
|
||||
{
|
||||
if (amount <= 0) return true;
|
||||
if (amount > TotalPlat) return false;
|
||||
if (amount <= 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (amount > TotalPlat)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
TotalPlat -= amount;
|
||||
|
||||
|
|
@ -464,9 +528,13 @@ namespace Server.Accounting
|
|||
public void SetFlag(int index, bool value)
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
Flags |= 1 << index;
|
||||
}
|
||||
else
|
||||
{
|
||||
Flags &= ~(1 << index);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -488,12 +556,16 @@ namespace Server.Accounting
|
|||
for (var i = Tags.Count - 1; i >= 0; --i)
|
||||
{
|
||||
if (i >= Tags.Count)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var tag = Tags[i];
|
||||
|
||||
if (tag.Name == name)
|
||||
{
|
||||
Tags.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -529,7 +601,9 @@ namespace Server.Accounting
|
|||
var tag = Tags[i];
|
||||
|
||||
if (tag.Name == name)
|
||||
{
|
||||
return tag.Value;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
@ -543,19 +617,31 @@ namespace Server.Accounting
|
|||
public void SetBanTags(Mobile from, DateTime banTime, TimeSpan banDuration)
|
||||
{
|
||||
if (from == null)
|
||||
{
|
||||
RemoveTag("BanDealer");
|
||||
}
|
||||
else
|
||||
{
|
||||
SetTag("BanDealer", from.ToString());
|
||||
}
|
||||
|
||||
if (banTime == DateTime.MinValue)
|
||||
{
|
||||
RemoveTag("BanTime");
|
||||
}
|
||||
else
|
||||
{
|
||||
SetTag("BanTime", XmlConvert.ToString(banTime, XmlDateTimeSerializationMode.Utc));
|
||||
}
|
||||
|
||||
if (banDuration == TimeSpan.Zero)
|
||||
{
|
||||
RemoveTag("BanDuration");
|
||||
}
|
||||
else
|
||||
{
|
||||
SetTag("BanDuration", banDuration.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public bool GetBanTags(out DateTime banTime, out TimeSpan banDuration)
|
||||
|
|
@ -565,11 +651,17 @@ namespace Server.Accounting
|
|||
banTime = Utility.GetXMLDateTime(GetTag("BanTime"), DateTime.MinValue);
|
||||
|
||||
if (tagDuration == "Infinite")
|
||||
{
|
||||
banDuration = TimeSpan.MaxValue;
|
||||
}
|
||||
else if (tagDuration != null)
|
||||
{
|
||||
banDuration = Utility.ToTimeSpan(tagDuration);
|
||||
}
|
||||
else
|
||||
{
|
||||
banDuration = TimeSpan.Zero;
|
||||
}
|
||||
|
||||
return banTime != DateTime.MinValue && banDuration != TimeSpan.Zero;
|
||||
}
|
||||
|
|
@ -584,7 +676,9 @@ namespace Server.Accounting
|
|||
private static void EventSink_Connected(Mobile m)
|
||||
{
|
||||
if (!(m.Account is Account acc))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (acc.Young && acc.m_YoungTimer == null)
|
||||
{
|
||||
|
|
@ -596,7 +690,9 @@ namespace Server.Accounting
|
|||
private static void EventSink_Disconnected(Mobile m)
|
||||
{
|
||||
if (!(m.Account is Account acc))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (acc.m_YoungTimer != null)
|
||||
{
|
||||
|
|
@ -605,7 +701,9 @@ namespace Server.Accounting
|
|||
}
|
||||
|
||||
if (!(m is PlayerMobile pm))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
acc.m_TotalGameTime += DateTime.UtcNow - pm.SessionStart;
|
||||
}
|
||||
|
|
@ -613,10 +711,14 @@ namespace Server.Accounting
|
|||
private static void EventSink_Login(Mobile m)
|
||||
{
|
||||
if (!(m is PlayerMobile pm))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(m.Account is Account acc))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (pm.Young && acc.Young)
|
||||
{
|
||||
|
|
@ -636,6 +738,7 @@ namespace Server.Accounting
|
|||
Young = false;
|
||||
|
||||
for (var i = 0; i < m_Mobiles.Length; i++)
|
||||
{
|
||||
if (m_Mobiles[i] is PlayerMobile m && m.Young)
|
||||
{
|
||||
m.Young = false;
|
||||
|
|
@ -643,26 +746,34 @@ namespace Server.Accounting
|
|||
if (m.NetState != null)
|
||||
{
|
||||
if (message > 0)
|
||||
{
|
||||
m.SendLocalizedMessage(message);
|
||||
}
|
||||
|
||||
m.SendLocalizedMessage(
|
||||
1019039
|
||||
); // You are no longer considered a young player of Ultima Online, and are no longer subject to the limitations and benefits of being in that caste.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void CheckYoung()
|
||||
{
|
||||
if (TotalGameTime >= YoungDuration)
|
||||
{
|
||||
RemoveYoungStatus(
|
||||
1019038
|
||||
); // You are old enough to be considered an adult, and have outgrown your status as a young player!
|
||||
}
|
||||
}
|
||||
|
||||
private bool UpgradePassword(string password, PasswordProtectionAlgorithm algorithm)
|
||||
{
|
||||
if (password == null || algorithm < m_PasswordAlgorithm) return false;
|
||||
if (password == null || algorithm < m_PasswordAlgorithm)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_PasswordAlgorithm = algorithm;
|
||||
Password = password?.Replace("-", string.Empty);
|
||||
|
|
@ -688,7 +799,9 @@ namespace Server.Accounting
|
|||
var text = Utility.GetText(ip, null);
|
||||
|
||||
if (text != null)
|
||||
{
|
||||
list.Add(text);
|
||||
}
|
||||
}
|
||||
|
||||
stringList = list.ToArray();
|
||||
|
|
@ -720,12 +833,16 @@ namespace Server.Accounting
|
|||
count = 0;
|
||||
|
||||
foreach (XmlElement ip in addressList.GetElementsByTagName("ip"))
|
||||
{
|
||||
if (count < list.Length)
|
||||
{
|
||||
if (IPAddress.TryParse(Utility.GetText(ip, null), out var address))
|
||||
{
|
||||
list[count] = Utility.Intern(address);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count != list.Length)
|
||||
{
|
||||
|
|
@ -733,7 +850,9 @@ namespace Server.Accounting
|
|||
list = new IPAddress[count];
|
||||
|
||||
for (var i = 0; i < count && i < old.Length; ++i)
|
||||
{
|
||||
list[i] = old[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -759,19 +878,25 @@ namespace Server.Accounting
|
|||
// Above is legacy, no longer used
|
||||
|
||||
if (chars != null)
|
||||
{
|
||||
foreach (XmlElement ele in chars.GetElementsByTagName("char"))
|
||||
{
|
||||
try
|
||||
{
|
||||
var index = Utility.GetXMLInt32(Utility.GetAttribute(ele, "index", "0"), 0);
|
||||
var serial = Utility.GetXMLUInt32(Utility.GetText(ele, "0"), 0);
|
||||
|
||||
if (index >= 0 && index < list.Length)
|
||||
{
|
||||
list[index] = World.FindMobile(serial);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
|
@ -791,6 +916,7 @@ namespace Server.Accounting
|
|||
list = new List<AccountComment>();
|
||||
|
||||
foreach (XmlElement comment in comments.GetElementsByTagName("comment"))
|
||||
{
|
||||
try
|
||||
{
|
||||
list.Add(new AccountComment(comment));
|
||||
|
|
@ -799,6 +925,7 @@ namespace Server.Accounting
|
|||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
|
|
@ -819,6 +946,7 @@ namespace Server.Accounting
|
|||
list = new List<AccountTag>();
|
||||
|
||||
foreach (XmlElement tag in tags.GetElementsByTagName("tag"))
|
||||
{
|
||||
try
|
||||
{
|
||||
list.Add(new AccountTag(tag));
|
||||
|
|
@ -827,6 +955,7 @@ namespace Server.Accounting
|
|||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
|
|
@ -848,26 +977,36 @@ namespace Server.Accounting
|
|||
var hasAccess = false;
|
||||
|
||||
if (m_AccessLevel >= level)
|
||||
{
|
||||
hasAccess = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; !hasAccess && i < Length; ++i)
|
||||
{
|
||||
var m = this[i];
|
||||
|
||||
if (m?.AccessLevel >= level)
|
||||
{
|
||||
hasAccess = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine("{0} {1}", hasAccess ? "yes" : "no", m_AccessLevel);
|
||||
|
||||
if (!hasAccess)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var accessAllowed = IPRestrictions.Length == 0 || IPLimiter.IsExempt(ipAddress);
|
||||
|
||||
for (var i = 0; !accessAllowed && i < IPRestrictions.Length; ++i)
|
||||
{
|
||||
accessAllowed = Utility.IPMatch(IPRestrictions[i], ipAddress);
|
||||
}
|
||||
|
||||
return accessAllowed;
|
||||
}
|
||||
|
|
@ -878,35 +1017,44 @@ namespace Server.Accounting
|
|||
/// <param name="ns">NetState instance to record.</param>
|
||||
public void LogAccess(NetState ns)
|
||||
{
|
||||
if (ns != null) LogAccess(ns.Address);
|
||||
if (ns != null)
|
||||
{
|
||||
LogAccess(ns.Address);
|
||||
}
|
||||
}
|
||||
|
||||
public void LogAccess(IPAddress ipAddress)
|
||||
{
|
||||
if (IPLimiter.IsExempt(ipAddress))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (LoginIPs.Length == 0)
|
||||
{
|
||||
if (AccountHandler.IPTable.ContainsKey(ipAddress))
|
||||
AccountHandler.IPTable[ipAddress]++;
|
||||
else
|
||||
AccountHandler.IPTable[ipAddress] = 1;
|
||||
AccountHandler.IPTable.TryGetValue(ipAddress, out var result);
|
||||
AccountHandler.IPTable[ipAddress] = result + 1;
|
||||
}
|
||||
|
||||
var contains = false;
|
||||
|
||||
for (var i = 0; !contains && i < LoginIPs.Length; ++i)
|
||||
{
|
||||
contains = LoginIPs[i].Equals(ipAddress);
|
||||
}
|
||||
|
||||
if (contains)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var old = LoginIPs;
|
||||
LoginIPs = new IPAddress[old.Length + 1];
|
||||
|
||||
for (var i = 0; i < old.Length; ++i)
|
||||
{
|
||||
LoginIPs[i] = old[i];
|
||||
}
|
||||
|
||||
LoginIPs[old.Length] = ipAddress;
|
||||
}
|
||||
|
|
@ -924,7 +1072,9 @@ namespace Server.Accounting
|
|||
var hasAccess = HasAccess(ipAddress);
|
||||
|
||||
if (hasAccess)
|
||||
{
|
||||
LogAccess(ipAddress);
|
||||
}
|
||||
|
||||
return hasAccess;
|
||||
}
|
||||
|
|
@ -997,7 +1147,9 @@ namespace Server.Accounting
|
|||
xml.WriteStartElement("comments");
|
||||
|
||||
for (var i = 0; i < m_Comments.Count; ++i)
|
||||
{
|
||||
m_Comments[i].Save(xml);
|
||||
}
|
||||
|
||||
xml.WriteEndElement();
|
||||
}
|
||||
|
|
@ -1007,7 +1159,9 @@ namespace Server.Accounting
|
|||
xml.WriteStartElement("tags");
|
||||
|
||||
for (var i = 0; i < m_Tags.Count; ++i)
|
||||
{
|
||||
m_Tags[i].Save(xml);
|
||||
}
|
||||
|
||||
xml.WriteEndElement();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ namespace Server.Misc
|
|||
// Jhelom
|
||||
// ..
|
||||
new CityInfo( "Haven", "Buckler's Hideaway", 3667, 2625, 0 )
|
||||
|
||||
|
||||
if (Core.AOS)
|
||||
{
|
||||
//CityInfo haven = new CityInfo( "Haven", "Uzeraan's Mansion", 3618, 2591, 0 );
|
||||
|
|
@ -65,11 +65,13 @@ namespace Server.Misc
|
|||
m_IPTable = new Dictionary<IPAddress, int>();
|
||||
|
||||
foreach (Account a in Accounts.GetAccounts())
|
||||
{
|
||||
if (a.LoginIPs.Length > 0)
|
||||
{
|
||||
var ip = a.LoginIPs[0];
|
||||
m_IPTable[ip] = (m_IPTable.TryGetValue(ip, out var value) ? value : 0) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return m_IPTable;
|
||||
|
|
@ -93,7 +95,9 @@ namespace Server.Misc
|
|||
EventSink.GameLogin += EventSink_GameLogin;
|
||||
|
||||
if (PasswordCommandEnabled)
|
||||
{
|
||||
CommandSystem.Register("Password", AccessLevel.Player, Password_OnCommand);
|
||||
}
|
||||
}
|
||||
|
||||
[Usage("Password <newPassword> <repeatPassword>")]
|
||||
|
|
@ -105,17 +109,23 @@ namespace Server.Misc
|
|||
var from = e.Mobile;
|
||||
|
||||
if (!(from.Account is Account acct))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var accessList = acct.LoginIPs;
|
||||
|
||||
if (accessList.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var ns = from.NetState;
|
||||
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.Length == 0)
|
||||
{
|
||||
|
|
@ -142,7 +152,9 @@ namespace Server.Misc
|
|||
var isSafe = true;
|
||||
|
||||
for (var i = 0; isSafe && i < pass.Length; ++i)
|
||||
{
|
||||
isSafe = pass[i] >= 0x20 && pass[i] < 0x7F;
|
||||
}
|
||||
|
||||
if (!isSafe)
|
||||
{
|
||||
|
|
@ -166,9 +178,13 @@ namespace Server.Misc
|
|||
if (entry != null)
|
||||
{
|
||||
if (entry.Message.StartsWith("[Automated: Change Password]"))
|
||||
{
|
||||
from.SendMessage("You already have a password change request in the help system queue.");
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendMessage("Your IP address does not match that which created this account.");
|
||||
}
|
||||
}
|
||||
else if (PageQueue.CheckAllowedToPage(from))
|
||||
{
|
||||
|
|
@ -246,19 +262,18 @@ namespace Server.Misc
|
|||
}
|
||||
}
|
||||
|
||||
public static bool CanCreate(IPAddress ip)
|
||||
{
|
||||
if (!IPTable.ContainsKey(ip))
|
||||
return true;
|
||||
|
||||
return IPTable[ip] < MaxAccountsPerIP;
|
||||
}
|
||||
public static bool CanCreate(IPAddress ip) =>
|
||||
!IPTable.TryGetValue(ip, out var result) || result < MaxAccountsPerIP;
|
||||
|
||||
private static bool IsForbiddenChar(char c)
|
||||
{
|
||||
for (var i = 0; i < m_ForbiddenChars.Length; ++i)
|
||||
{
|
||||
if (c == m_ForbiddenChars[i])
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
@ -266,18 +281,26 @@ namespace Server.Misc
|
|||
private static Account CreateAccount(NetState state, string un, string pw)
|
||||
{
|
||||
if (un.Length == 0 || pw.Length == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var isSafe = !(un.StartsWith(" ") || un.EndsWith(" ") || un.EndsWith("."));
|
||||
|
||||
for (var i = 0; isSafe && i < un.Length; ++i)
|
||||
{
|
||||
isSafe = un[i] >= 0x20 && un[i] < 0x7F && !IsForbiddenChar(un[i]);
|
||||
}
|
||||
|
||||
for (var i = 0; isSafe && i < pw.Length; ++i)
|
||||
{
|
||||
isSafe = pw[i] >= 0x20 && pw[i] < 0x7F;
|
||||
}
|
||||
|
||||
if (!isSafe)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!CanCreate(state.Address))
|
||||
{
|
||||
|
|
@ -307,10 +330,8 @@ namespace Server.Misc
|
|||
|
||||
Console.WriteLine("Login: {0}: Past IP limit threshold", e.State);
|
||||
|
||||
using (var op = new StreamWriter("ipLimits.log", true))
|
||||
{
|
||||
op.WriteLine("{0}\tPast IP limit threshold\t{1}", e.State, DateTime.UtcNow);
|
||||
}
|
||||
using var op = new StreamWriter("ipLimits.log", true);
|
||||
op.WriteLine("{0}\tPast IP limit threshold\t{1}", e.State, DateTime.UtcNow);
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
@ -329,7 +350,9 @@ namespace Server.Misc
|
|||
e.Accepted = acct?.CheckAccess(e.State) ?? false;
|
||||
|
||||
if (!e.Accepted)
|
||||
{
|
||||
e.RejectReason = ALRReason.BadComm;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -362,7 +385,9 @@ namespace Server.Misc
|
|||
}
|
||||
|
||||
if (!e.Accepted)
|
||||
{
|
||||
AccountAttackLimiter.RegisterInvalidAccess(e.State);
|
||||
}
|
||||
}
|
||||
|
||||
public static void EventSink_GameLogin(GameLoginEventArgs e)
|
||||
|
|
@ -414,15 +439,23 @@ namespace Server.Misc
|
|||
}
|
||||
|
||||
if (!e.Accepted)
|
||||
{
|
||||
AccountAttackLimiter.RegisterInvalidAccess(e.State);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool CheckAccount(Mobile mobCheck, Mobile accCheck)
|
||||
{
|
||||
if (accCheck?.Account is Account a)
|
||||
{
|
||||
for (var i = 0; i < a.Length; ++i)
|
||||
{
|
||||
if (a[i] == mobCheck)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,9 +36,13 @@ namespace Server.Commands
|
|||
var m = e.Mobile;
|
||||
|
||||
if (m.AccessLevel >= c.AccessLevel)
|
||||
{
|
||||
m.SendGump(new CommandInfoGump(c));
|
||||
}
|
||||
else
|
||||
{
|
||||
m.SendMessage("You don't have access to that command.");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
@ -67,17 +71,23 @@ namespace Server.Commands
|
|||
var attrs = mi.GetCustomAttributes(typeof(UsageAttribute), false);
|
||||
|
||||
if (attrs.Length == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var usage = attrs[0] as UsageAttribute;
|
||||
|
||||
attrs = mi.GetCustomAttributes(typeof(DescriptionAttribute), false);
|
||||
|
||||
if (attrs.Length == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (usage == null || !(attrs[0] is DescriptionAttribute desc))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
attrs = mi.GetCustomAttributes(typeof(AliasesAttribute), false);
|
||||
|
||||
|
|
@ -114,14 +124,18 @@ namespace Server.Commands
|
|||
var desc = command.Description;
|
||||
|
||||
if (usage == null || desc == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var cmds = command.Commands;
|
||||
var cmd = cmds[0];
|
||||
var aliases = new string[cmds.Length - 1];
|
||||
|
||||
for (var j = 0; j < aliases.Length; ++j)
|
||||
{
|
||||
aliases[j] = cmds[j + 1];
|
||||
}
|
||||
|
||||
desc = desc.Replace("<", "(").Replace(">", ")");
|
||||
|
||||
|
|
@ -132,25 +146,39 @@ namespace Server.Commands
|
|||
sb.Append("Modifiers: ");
|
||||
|
||||
if ((command.Supports & CommandSupport.Global) != 0)
|
||||
{
|
||||
sb.Append("<i>Global</i>, ");
|
||||
}
|
||||
|
||||
if ((command.Supports & CommandSupport.Online) != 0)
|
||||
{
|
||||
sb.Append("<i>Online</i>, ");
|
||||
}
|
||||
|
||||
if ((command.Supports & CommandSupport.Region) != 0)
|
||||
{
|
||||
sb.Append("<i>Region</i>, ");
|
||||
}
|
||||
|
||||
if ((command.Supports & CommandSupport.Contained) != 0)
|
||||
{
|
||||
sb.Append("<i>Contained</i>, ");
|
||||
}
|
||||
|
||||
if ((command.Supports & CommandSupport.Multi) != 0)
|
||||
{
|
||||
sb.Append("<i>Multi</i>, ");
|
||||
}
|
||||
|
||||
if ((command.Supports & CommandSupport.Area) != 0)
|
||||
{
|
||||
sb.Append("<i>Area</i>, ");
|
||||
}
|
||||
|
||||
if ((command.Supports & CommandSupport.Self) != 0)
|
||||
{
|
||||
sb.Append("<i>Self</i>, ");
|
||||
}
|
||||
|
||||
sb.Remove(sb.Length - 2, 2);
|
||||
sb.Append("<br>");
|
||||
|
|
@ -183,14 +211,18 @@ namespace Server.Commands
|
|||
var desc = command.Description;
|
||||
|
||||
if (usage == null || desc == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var cmds = command.Accessors;
|
||||
var cmd = cmds[0];
|
||||
var aliases = new string[cmds.Length - 1];
|
||||
|
||||
for (var j = 0; j < aliases.Length; ++j)
|
||||
{
|
||||
aliases[j] = cmds[j + 1];
|
||||
}
|
||||
|
||||
desc = desc.Replace("<", ")").Replace(">", ")");
|
||||
|
||||
|
|
@ -213,8 +245,9 @@ namespace Server.Commands
|
|||
SortedHelpInfo = list;
|
||||
|
||||
foreach (var c in SortedHelpInfo)
|
||||
if (!HelpInfos.ContainsKey(c.Name.ToLower()))
|
||||
HelpInfos.Add(c.Name.ToLower(), c);
|
||||
{
|
||||
HelpInfos.TryAdd(c.Name.ToLower(), c);
|
||||
}
|
||||
}
|
||||
|
||||
public class CommandListGump : BaseGridGump
|
||||
|
|
@ -234,8 +267,12 @@ namespace Server.Commands
|
|||
m_List = new List<CommandInfo>();
|
||||
|
||||
foreach (var c in SortedHelpInfo)
|
||||
{
|
||||
if (from.AccessLevel >= c.AccessLevel)
|
||||
{
|
||||
m_List.Add(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -245,9 +282,13 @@ namespace Server.Commands
|
|||
AddNewPage();
|
||||
|
||||
if (m_Page > 0)
|
||||
{
|
||||
AddEntryButton(20, ArrowLeftID1, ArrowLeftID2, 1, ArrowLeftWidth, ArrowLeftHeight);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddEntryHeader(20);
|
||||
}
|
||||
|
||||
AddEntryHtml(
|
||||
160,
|
||||
|
|
@ -257,9 +298,13 @@ namespace Server.Commands
|
|||
);
|
||||
|
||||
if ((m_Page + 1) * EntriesPerPage < m_List.Count)
|
||||
{
|
||||
AddEntryButton(20, ArrowRightID1, ArrowRightID2, 2, ArrowRightWidth, ArrowRightHeight);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddEntryHeader(20);
|
||||
}
|
||||
|
||||
var last = (int)AccessLevel.Player - 1;
|
||||
|
||||
|
|
@ -300,14 +345,18 @@ namespace Server.Commands
|
|||
case 1:
|
||||
{
|
||||
if (m_Page > 0)
|
||||
{
|
||||
m.SendGump(new CommandListGump(m_Page - 1, m, m_List));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
if ((m_Page + 1) * EntriesPerPage < SortedHelpInfo.Count)
|
||||
{
|
||||
m.SendGump(new CommandListGump(m_Page + 1, m, m_List));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
|
@ -369,7 +418,9 @@ namespace Server.Commands
|
|||
for (var i = 0; i < aliases.Length; ++i)
|
||||
{
|
||||
if (i != 0)
|
||||
{
|
||||
sb.Append(", ");
|
||||
}
|
||||
|
||||
sb.Append(aliases[i]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,9 @@ namespace Server.Engines.Craft
|
|||
CraftItem = item;
|
||||
|
||||
if (Recipes.ContainsKey(id))
|
||||
{
|
||||
throw new Exception("Attempting to create recipe with preexisting ID.");
|
||||
}
|
||||
|
||||
Recipes.Add(id, this);
|
||||
LargestRecipeID = Math.Max(id, LargestRecipeID);
|
||||
|
|
|
|||
|
|
@ -33,18 +33,20 @@ namespace Server.Engines.Spawners
|
|||
for (var i = 0; i < count; ++i)
|
||||
// IEntity e = World.FindEntity( reader.ReadInt() );
|
||||
|
||||
{
|
||||
if (reader.ReadEntity() is ISpawnable e)
|
||||
{
|
||||
e.Spawner = parent;
|
||||
|
||||
if (e is BaseCreature creature)
|
||||
{
|
||||
creature.RemoveIfUntamed = true;
|
||||
}
|
||||
|
||||
Spawned.Add(e);
|
||||
|
||||
if (!parent.Spawned.ContainsKey(e))
|
||||
parent.Spawned.Add(e, this);
|
||||
parent.Spawned.TryAdd(e, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[JsonPropertyName("probability")] public int SpawnedProbability { get; set; }
|
||||
|
|
@ -81,11 +83,17 @@ namespace Server.Engines.Spawners
|
|||
object o = Spawned[i];
|
||||
|
||||
if (o is Item item)
|
||||
{
|
||||
writer.Write(item);
|
||||
}
|
||||
else if (o is Mobile mobile)
|
||||
{
|
||||
writer.Write(mobile);
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write(Serial.MinusOne);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -172,7 +172,9 @@ namespace Server.Gumps
|
|||
m_Object = o;
|
||||
|
||||
if (o != null)
|
||||
{
|
||||
m_Type = o.GetType();
|
||||
}
|
||||
|
||||
m_List = list;
|
||||
m_Stack = stack;
|
||||
|
|
@ -189,7 +191,9 @@ namespace Server.Gumps
|
|||
var lastIndex = page * EntryCount + count - 1;
|
||||
|
||||
if (lastIndex >= 0 && lastIndex < m_List.Count && m_List[lastIndex] == null)
|
||||
{
|
||||
--count;
|
||||
}
|
||||
|
||||
var totalHeight = OffsetSize + (EntryHeight + OffsetSize) * (count + 1);
|
||||
|
||||
|
|
@ -210,24 +214,33 @@ namespace Server.Gumps
|
|||
var emptyWidth = TotalWidth - PrevWidth - NextWidth - OffsetSize * 4 - (OldStyle ? SetWidth + OffsetSize : 0);
|
||||
|
||||
if (OldStyle)
|
||||
{
|
||||
AddImageTiled(x, y, TotalWidth - OffsetSize * 3 - SetWidth, EntryHeight, HeaderGumpID);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddImageTiled(x, y, PrevWidth, EntryHeight, HeaderGumpID);
|
||||
}
|
||||
|
||||
if (page > 0)
|
||||
{
|
||||
AddButton(x + PrevOffsetX, y + PrevOffsetY, PrevButtonID1, PrevButtonID2, 1);
|
||||
|
||||
if (PrevLabel)
|
||||
{
|
||||
AddLabel(x + PrevLabelOffsetX, y + PrevLabelOffsetY, TextHue, "Previous");
|
||||
}
|
||||
}
|
||||
|
||||
x += PrevWidth + OffsetSize;
|
||||
|
||||
if (!OldStyle)
|
||||
{
|
||||
AddImageTiled(x, y, emptyWidth, EntryHeight, HeaderGumpID);
|
||||
}
|
||||
|
||||
if (TypeLabel && m_Type != null)
|
||||
{
|
||||
AddHtml(
|
||||
x,
|
||||
y,
|
||||
|
|
@ -235,18 +248,23 @@ namespace Server.Gumps
|
|||
EntryHeight,
|
||||
$"<BASEFONT COLOR=#FAFAFA><CENTER>{m_Type.Name}</CENTER></BASEFONT>"
|
||||
);
|
||||
}
|
||||
|
||||
x += emptyWidth + OffsetSize;
|
||||
|
||||
if (!OldStyle)
|
||||
{
|
||||
AddImageTiled(x, y, NextWidth, EntryHeight, HeaderGumpID);
|
||||
}
|
||||
|
||||
if ((page + 1) * EntryCount < m_List.Count)
|
||||
{
|
||||
AddButton(x + NextOffsetX, y + NextOffsetY, NextButtonID1, NextButtonID2, 2, GumpButtonType.Reply, 1);
|
||||
|
||||
if (NextLabel)
|
||||
{
|
||||
AddLabel(x + NextLabelOffsetX, y + NextLabelOffsetY, TextHue, "Next");
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0, index = page * EntryCount; i < count && index < m_List.Count; ++i, ++index)
|
||||
|
|
@ -267,7 +285,9 @@ namespace Server.Gumps
|
|||
x += TypeWidth + OffsetSize;
|
||||
|
||||
if (SetGumpID != 0)
|
||||
{
|
||||
AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID);
|
||||
}
|
||||
}
|
||||
else if (o is PropertyInfo prop)
|
||||
{
|
||||
|
|
@ -279,13 +299,17 @@ namespace Server.Gumps
|
|||
x += ValueWidth + OffsetSize;
|
||||
|
||||
if (SetGumpID != 0)
|
||||
{
|
||||
AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID);
|
||||
}
|
||||
|
||||
var cpa = GetCPA(prop);
|
||||
|
||||
if ((!prop.GetType().IsValueType || prop.CanWrite) && cpa != null &&
|
||||
m_Mobile.AccessLevel >= cpa.WriteLevel && !cpa.ReadOnly)
|
||||
{
|
||||
AddButton(x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, i + 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -315,14 +339,18 @@ namespace Server.Gumps
|
|||
case 1: // Previous
|
||||
{
|
||||
if (m_Page > 0)
|
||||
{
|
||||
from.SendGump(new PropertiesGump(from, m_Object, m_Stack, m_List, m_Page - 1));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 2: // Next
|
||||
{
|
||||
if ((m_Page + 1) * EntryCount < m_List.Count)
|
||||
{
|
||||
from.SendGump(new PropertiesGump(from, m_Object, m_Stack, m_List, m_Page + 1));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
|
@ -335,13 +363,17 @@ namespace Server.Gumps
|
|||
var prop = m_List[index] as PropertyInfo;
|
||||
|
||||
if (prop == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var attr = GetCPA(prop);
|
||||
|
||||
if (prop.GetType().IsValueType && !prop.CanWrite || attr == null ||
|
||||
from.AccessLevel < attr.WriteLevel || attr.ReadOnly)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var type = prop.PropertyType;
|
||||
|
||||
|
|
@ -454,9 +486,13 @@ namespace Server.Gumps
|
|||
var obj = prop.GetValue(m_Object, null);
|
||||
|
||||
if (obj != null)
|
||||
{
|
||||
from.SendGump(new PropertiesGump(from, obj, m_Stack, new StackEntry(m_Object, prop)));
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendGump(new PropertiesGump(from, m_Object, m_Stack, m_List, m_Page));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -470,7 +506,9 @@ namespace Server.Gumps
|
|||
var list = new object[a.Length];
|
||||
|
||||
for (var i = 0; i < list.Length; ++i)
|
||||
{
|
||||
list[i] = a.GetValue(i);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
|
@ -480,15 +518,21 @@ namespace Server.Gumps
|
|||
public static void OnValueChanged(object obj, PropertyInfo prop, Stack<StackEntry> stack)
|
||||
{
|
||||
if (stack == null || stack.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!prop.PropertyType.IsValueType)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var peek = stack.Peek();
|
||||
|
||||
if (peek.m_Property.CanWrite)
|
||||
{
|
||||
peek.m_Property.SetValue(peek.m_Object, obj, null);
|
||||
}
|
||||
}
|
||||
|
||||
private static string[] GetCustomEnumNames(Type type)
|
||||
|
|
@ -496,10 +540,14 @@ namespace Server.Gumps
|
|||
var attrs = type.GetCustomAttributes(typeofCustomEnum, false);
|
||||
|
||||
if (attrs.Length == 0)
|
||||
{
|
||||
return Array.Empty<string>();
|
||||
}
|
||||
|
||||
if (!(attrs[0] is CustomEnumAttribute ce))
|
||||
{
|
||||
return Array.Empty<string>();
|
||||
}
|
||||
|
||||
return ce.Names;
|
||||
}
|
||||
|
|
@ -512,8 +560,12 @@ namespace Server.Gumps
|
|||
private static bool IsType(Type type, Type[] check)
|
||||
{
|
||||
for (var i = 0; i < check.Length; ++i)
|
||||
{
|
||||
if (IsType(type, check[i]))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
@ -534,27 +586,68 @@ namespace Server.Gumps
|
|||
|
||||
public static string ValueToString(object o)
|
||||
{
|
||||
if (o == null) return "-null-";
|
||||
if (o is string s) return $"\"{s}\"";
|
||||
if (o is bool) return o.ToString();
|
||||
if (o is char c) return $"0x{(int)c:X} '{c}'";
|
||||
if (o == null)
|
||||
{
|
||||
return "-null-";
|
||||
}
|
||||
|
||||
if (o is string s)
|
||||
{
|
||||
return $"\"{s}\"";
|
||||
}
|
||||
|
||||
if (o is bool)
|
||||
{
|
||||
return o.ToString();
|
||||
}
|
||||
|
||||
if (o is char c)
|
||||
{
|
||||
return $"0x{(int)c:X} '{c}'";
|
||||
}
|
||||
|
||||
if (o is Serial serial)
|
||||
{
|
||||
if (serial.IsValid)
|
||||
{
|
||||
if (serial.IsItem) return $"(I) 0x{serial.Value:X}";
|
||||
if (serial.IsMobile) return $"(M) 0x{serial.Value:X}";
|
||||
if (serial.IsItem)
|
||||
{
|
||||
return $"(I) 0x{serial.Value:X}";
|
||||
}
|
||||
|
||||
if (serial.IsMobile)
|
||||
{
|
||||
return $"(M) 0x{serial.Value:X}";
|
||||
}
|
||||
}
|
||||
|
||||
return $"(?) 0x{serial.Value:X}";
|
||||
}
|
||||
|
||||
if (o is byte || o is sbyte || o is short || o is ushort || o is int || o is uint || o is long || o is ulong)
|
||||
{
|
||||
return $"{o} (0x{o:X})";
|
||||
if (o is Mobile mobile) return $"(M) 0x{mobile.Serial.Value:X} \"{mobile.Name}\"";
|
||||
if (o is Item item) return $"(I) 0x{item.Serial.Value:X}";
|
||||
if (o is Type type) return type.Name;
|
||||
if (o is TextDefinition definition) return definition.Format(true);
|
||||
}
|
||||
|
||||
if (o is Mobile mobile)
|
||||
{
|
||||
return $"(M) 0x{mobile.Serial.Value:X} \"{mobile.Name}\"";
|
||||
}
|
||||
|
||||
if (o is Item item)
|
||||
{
|
||||
return $"(I) 0x{item.Serial.Value:X}";
|
||||
}
|
||||
|
||||
if (o is Type type)
|
||||
{
|
||||
return type.Name;
|
||||
}
|
||||
|
||||
if (o is TextDefinition definition)
|
||||
{
|
||||
return definition.Format(true);
|
||||
}
|
||||
|
||||
return o.ToString();
|
||||
}
|
||||
|
|
@ -564,7 +657,9 @@ namespace Server.Gumps
|
|||
var list = new List<object>();
|
||||
|
||||
if (m_Type == null)
|
||||
{
|
||||
return list;
|
||||
}
|
||||
|
||||
var props = m_Type.GetProperties(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public);
|
||||
|
||||
|
|
@ -575,10 +670,14 @@ namespace Server.Gumps
|
|||
var kvp = groups[i];
|
||||
|
||||
if (!HasAttribute(kvp.Key, typeofNoSort, false))
|
||||
{
|
||||
kvp.Value.Sort(PropertySorter.Instance);
|
||||
}
|
||||
|
||||
if (i != 0)
|
||||
{
|
||||
list.Add(null);
|
||||
}
|
||||
|
||||
list.Add(kvp.Key);
|
||||
list.AddRange(kvp.Value);
|
||||
|
|
@ -592,7 +691,10 @@ namespace Server.Gumps
|
|||
var attrs = prop.GetCustomAttributes(typeofCPA, false);
|
||||
|
||||
if (attrs.Length > 0)
|
||||
{
|
||||
return attrs[0] as CPA;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -617,15 +719,24 @@ namespace Server.Gumps
|
|||
var baseType = type?.BaseType;
|
||||
|
||||
if (baseType == typeofObject || baseType?.GetProperty(prop.Name, prop.PropertyType) == null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
type = baseType;
|
||||
}
|
||||
|
||||
if (type != null && !groups.ContainsKey(type))
|
||||
groups[type] = new List<PropertyInfo> { prop };
|
||||
else
|
||||
groups[type].Add(prop);
|
||||
if (type != null)
|
||||
{
|
||||
if (groups.TryGetValue(type, out var result))
|
||||
{
|
||||
result.Add(prop);
|
||||
}
|
||||
else
|
||||
{
|
||||
groups[type] = new List<PropertyInfo> { prop };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -638,7 +749,10 @@ namespace Server.Gumps
|
|||
|
||||
public static object GetObjectFromString(Type t, string s)
|
||||
{
|
||||
if (t == typeof(string)) return s;
|
||||
if (t == typeof(string))
|
||||
{
|
||||
return s;
|
||||
}
|
||||
|
||||
if (t == typeof(byte) || t == typeof(sbyte) || t == typeof(short) || t == typeof(ushort) || t == typeof(int) ||
|
||||
t == typeof(uint) || t == typeof(long) || t == typeof(ulong))
|
||||
|
|
@ -646,7 +760,9 @@ namespace Server.Gumps
|
|||
if (s.StartsWith("0x"))
|
||||
{
|
||||
if (t == typeof(ulong) || t == typeof(uint) || t == typeof(ushort) || t == typeof(byte))
|
||||
{
|
||||
return Convert.ChangeType(Convert.ToUInt64(s.Substring(2), 16), t);
|
||||
}
|
||||
|
||||
return Convert.ChangeType(Convert.ToInt64(s.Substring(2), 16), t);
|
||||
}
|
||||
|
|
@ -654,7 +770,11 @@ namespace Server.Gumps
|
|||
return Convert.ChangeType(s, t);
|
||||
}
|
||||
|
||||
if (t == typeof(double) || t == typeof(float)) return Convert.ChangeType(s, t);
|
||||
if (t == typeof(double) || t == typeof(float))
|
||||
{
|
||||
return Convert.ChangeType(s, t);
|
||||
}
|
||||
|
||||
if (t.IsDefined(typeof(ParsableAttribute), false))
|
||||
{
|
||||
var parseMethod = t.GetMethod("Parse", new[] { typeof(string) });
|
||||
|
|
@ -667,26 +787,63 @@ namespace Server.Gumps
|
|||
|
||||
private static string GetStringFromObject(object o)
|
||||
{
|
||||
if (o == null) return "-null-";
|
||||
if (o is string s) return $"\"{s}\"";
|
||||
if (o is bool) return o.ToString();
|
||||
if (o is char c) return $"0x{(int)c:X} '{c}'";
|
||||
if (o == null)
|
||||
{
|
||||
return "-null-";
|
||||
}
|
||||
|
||||
if (o is string s)
|
||||
{
|
||||
return $"\"{s}\"";
|
||||
}
|
||||
|
||||
if (o is bool)
|
||||
{
|
||||
return o.ToString();
|
||||
}
|
||||
|
||||
if (o is char c)
|
||||
{
|
||||
return $"0x{(int)c:X} '{c}'";
|
||||
}
|
||||
|
||||
if (o is Serial serial)
|
||||
{
|
||||
if (serial.IsValid)
|
||||
{
|
||||
if (serial.IsItem) return $"(I) 0x{serial.Value:X}";
|
||||
if (serial.IsMobile) return $"(M) 0x{serial.Value:X}";
|
||||
if (serial.IsItem)
|
||||
{
|
||||
return $"(I) 0x{serial.Value:X}";
|
||||
}
|
||||
|
||||
if (serial.IsMobile)
|
||||
{
|
||||
return $"(M) 0x{serial.Value:X}";
|
||||
}
|
||||
}
|
||||
|
||||
return $"(?) 0x{serial.Value:X}";
|
||||
}
|
||||
|
||||
if (o is byte || o is sbyte || o is short || o is ushort || o is int || o is uint || o is long || o is ulong)
|
||||
{
|
||||
return $"{o} (0x{o:X})";
|
||||
if (o is Mobile mobile) return $"(M) 0x{mobile.Serial.Value:X} \"{mobile.Name}\"";
|
||||
if (o is Item item) return $"(I) 0x{item.Serial.Value:X}";
|
||||
if (o is Type type) return type.Name;
|
||||
}
|
||||
|
||||
if (o is Mobile mobile)
|
||||
{
|
||||
return $"(M) 0x{mobile.Serial.Value:X} \"{mobile.Name}\"";
|
||||
}
|
||||
|
||||
if (o is Item item)
|
||||
{
|
||||
return $"(I) 0x{item.Serial.Value:X}";
|
||||
}
|
||||
|
||||
if (o is Type type)
|
||||
{
|
||||
return type.Name;
|
||||
}
|
||||
|
||||
return o.ToString();
|
||||
}
|
||||
|
|
@ -702,9 +859,14 @@ namespace Server.Gumps
|
|||
public int Compare(PropertyInfo x, PropertyInfo y)
|
||||
{
|
||||
if (x == null && y == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (x == null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return y == null ? 1 : x.Name.CompareTo(x.Name);
|
||||
}
|
||||
|
|
@ -726,7 +888,9 @@ namespace Server.Gumps
|
|||
int dist;
|
||||
|
||||
for (dist = 0; current != null && current != typeofObject && current != type; ++dist)
|
||||
{
|
||||
current = current.BaseType;
|
||||
}
|
||||
|
||||
return dist;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,7 +69,9 @@ namespace Server.Engines.Events
|
|||
{
|
||||
if (m is PlayerMobile pm && !pm.Deleted && m_Timer.Running && !m_DeathQueue.Contains(pm) &&
|
||||
m_DeathQueue.Count < m_DeathQueueLimit)
|
||||
{
|
||||
m_DeathQueue.Add(pm);
|
||||
}
|
||||
}
|
||||
|
||||
private static void Clear_Callback()
|
||||
|
|
@ -78,7 +80,10 @@ namespace Server.Engines.Events
|
|||
|
||||
m_DeathQueue.Clear();
|
||||
|
||||
if (DateTime.UtcNow <= HolidaySettings.FinishHalloween) m_ClearTimer.Stop();
|
||||
if (DateTime.UtcNow <= HolidaySettings.FinishHalloween)
|
||||
{
|
||||
m_ClearTimer.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
private static void Timer_Callback()
|
||||
|
|
@ -88,12 +93,15 @@ namespace Server.Engines.Events
|
|||
if (DateTime.UtcNow <= HolidaySettings.FinishHalloween)
|
||||
{
|
||||
for (var index = 0; m_DeathQueue.Count > 0 && index < m_DeathQueue.Count; index++)
|
||||
if (!ReAnimated.ContainsKey(m_DeathQueue[index]))
|
||||
{
|
||||
player = m_DeathQueue[index];
|
||||
{
|
||||
var entry = m_DeathQueue[index];
|
||||
|
||||
if (!ReAnimated.ContainsKey(entry))
|
||||
{
|
||||
player = entry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (player?.Deleted == false && ReAnimated.Count < m_TotalZombieLimit)
|
||||
{
|
||||
|
|
@ -244,7 +252,9 @@ namespace Server.Engines.Events
|
|||
public override void OnDelete()
|
||||
{
|
||||
if (m_DeadPlayer?.Deleted == false)
|
||||
{
|
||||
HalloweenHauntings.ReAnimated?.Remove(m_DeadPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Serialize(IGenericWriter writer)
|
||||
|
|
|
|||
|
|
@ -197,7 +197,9 @@ namespace Server.Items
|
|||
var version = reader.ReadInt();
|
||||
|
||||
if (version == 0 && Weight == 1.0)
|
||||
{
|
||||
Weight = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -263,7 +265,9 @@ namespace Server.Items
|
|||
public override void DisplayTo(Mobile m)
|
||||
{
|
||||
if (DynamicFurniture.Open(this, m))
|
||||
{
|
||||
base.DisplayTo(m);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Serialize(IGenericWriter writer)
|
||||
|
|
@ -295,7 +299,9 @@ namespace Server.Items
|
|||
public override void DisplayTo(Mobile m)
|
||||
{
|
||||
if (DynamicFurniture.Open(this, m))
|
||||
{
|
||||
base.DisplayTo(m);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Serialize(IGenericWriter writer)
|
||||
|
|
@ -349,13 +355,13 @@ namespace Server.Items
|
|||
|
||||
public static void Close(Container c)
|
||||
{
|
||||
if (m_Table.TryGetValue(c, out var t))
|
||||
if (m_Table.Remove(c, out var t))
|
||||
{
|
||||
t.Stop();
|
||||
m_Table.Remove(c);
|
||||
}
|
||||
|
||||
if (c is Armoire || c is FancyArmoire)
|
||||
{
|
||||
c.ItemID = c.ItemID switch
|
||||
{
|
||||
0xA4C => 0xA4D,
|
||||
|
|
@ -364,6 +370,7 @@ namespace Server.Items
|
|||
0xA52 => 0xA53,
|
||||
_ => c.ItemID
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -383,7 +390,9 @@ namespace Server.Items
|
|||
protected override void OnTick()
|
||||
{
|
||||
if (m_Mobile.Map != m_Container.Map || !m_Mobile.InRange(m_Container.GetWorldLocation(), 3))
|
||||
{
|
||||
DynamicFurniture.Close(m_Container);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ namespace Server.Items
|
|||
public override int ComputeItemID()
|
||||
{
|
||||
if (!IsEmpty)
|
||||
{
|
||||
switch (Content)
|
||||
{
|
||||
case BeverageType.Ale: return 0x99F;
|
||||
|
|
@ -61,6 +62,7 @@ namespace Server.Items
|
|||
case BeverageType.Wine: return 0x9C7;
|
||||
case BeverageType.Water: return 0x99B;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -127,7 +129,9 @@ namespace Server.Items
|
|||
public override int ComputeItemID()
|
||||
{
|
||||
if (!IsEmpty)
|
||||
{
|
||||
return 0x9C8;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -168,9 +172,14 @@ namespace Server.Items
|
|||
public override int ComputeItemID()
|
||||
{
|
||||
if (ItemID >= 0x995 && ItemID <= 0x999)
|
||||
{
|
||||
return ItemID;
|
||||
}
|
||||
|
||||
if (ItemID == 0x9CA)
|
||||
{
|
||||
return ItemID;
|
||||
}
|
||||
|
||||
return 0x995;
|
||||
}
|
||||
|
|
@ -211,7 +220,9 @@ namespace Server.Items
|
|||
public override int ComputeItemID()
|
||||
{
|
||||
if (ItemID >= 0xFFF && ItemID <= 0x1002)
|
||||
{
|
||||
return ItemID;
|
||||
}
|
||||
|
||||
return 0xFFF;
|
||||
}
|
||||
|
|
@ -252,7 +263,9 @@ namespace Server.Items
|
|||
public override int ComputeItemID()
|
||||
{
|
||||
if (ItemID == 0x99A || ItemID == 0x9B3 || ItemID == 0x9BF || ItemID == 0x9CB)
|
||||
{
|
||||
return ItemID;
|
||||
}
|
||||
|
||||
return 0x99A;
|
||||
}
|
||||
|
|
@ -302,7 +315,9 @@ namespace Server.Items
|
|||
public override int ComputeItemID()
|
||||
{
|
||||
if (IsEmpty)
|
||||
{
|
||||
return ItemID >= 0x1F81 && ItemID <= 0x1F84 ? ItemID : 0x1F81;
|
||||
}
|
||||
|
||||
return Content switch
|
||||
{
|
||||
|
|
@ -406,7 +421,9 @@ namespace Server.Items
|
|||
if (IsEmpty)
|
||||
{
|
||||
if (ItemID == 0x9A7 || ItemID == 0xFF7)
|
||||
{
|
||||
return ItemID;
|
||||
}
|
||||
|
||||
return 0xFF6;
|
||||
}
|
||||
|
|
@ -416,42 +433,54 @@ namespace Server.Items
|
|||
case BeverageType.Ale:
|
||||
{
|
||||
if (ItemID == 0x1F96)
|
||||
{
|
||||
return ItemID;
|
||||
}
|
||||
|
||||
return 0x1F95;
|
||||
}
|
||||
case BeverageType.Cider:
|
||||
{
|
||||
if (ItemID == 0x1F98)
|
||||
{
|
||||
return ItemID;
|
||||
}
|
||||
|
||||
return 0x1F97;
|
||||
}
|
||||
case BeverageType.Liquor:
|
||||
{
|
||||
if (ItemID == 0x1F9A)
|
||||
{
|
||||
return ItemID;
|
||||
}
|
||||
|
||||
return 0x1F99;
|
||||
}
|
||||
case BeverageType.Milk:
|
||||
{
|
||||
if (ItemID == 0x9AD)
|
||||
{
|
||||
return ItemID;
|
||||
}
|
||||
|
||||
return 0x9F0;
|
||||
}
|
||||
case BeverageType.Wine:
|
||||
{
|
||||
if (ItemID == 0x1F9C)
|
||||
{
|
||||
return ItemID;
|
||||
}
|
||||
|
||||
return 0x1F9B;
|
||||
}
|
||||
case BeverageType.Water:
|
||||
{
|
||||
if (ItemID == 0xFF8 || ItemID == 0xFF9 || ItemID == 0x1F9E)
|
||||
{
|
||||
return ItemID;
|
||||
}
|
||||
|
||||
return 0x1F9D;
|
||||
}
|
||||
|
|
@ -470,9 +499,13 @@ namespace Server.Items
|
|||
public override void Deserialize(IGenericReader reader)
|
||||
{
|
||||
if (CheckType("PitcherWater") || CheckType("GlassPitcher"))
|
||||
{
|
||||
InternalDeserialize(reader, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
InternalDeserialize(reader, true);
|
||||
}
|
||||
|
||||
var version = reader.ReadInt();
|
||||
|
||||
|
|
@ -565,7 +598,9 @@ namespace Server.Items
|
|||
var num = BaseLabelNumber;
|
||||
|
||||
if (IsEmpty || num == 0)
|
||||
{
|
||||
return EmptyLabelNumber;
|
||||
}
|
||||
|
||||
return BaseLabelNumber + (int)m_Content;
|
||||
}
|
||||
|
|
@ -608,9 +643,13 @@ namespace Server.Items
|
|||
var itemID = ComputeItemID();
|
||||
|
||||
if (itemID > 0)
|
||||
{
|
||||
ItemID = itemID;
|
||||
}
|
||||
else
|
||||
{
|
||||
Delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -627,9 +666,13 @@ namespace Server.Items
|
|||
var itemID = ComputeItemID();
|
||||
|
||||
if (itemID > 0)
|
||||
{
|
||||
ItemID = itemID;
|
||||
}
|
||||
else
|
||||
{
|
||||
Delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -640,11 +683,20 @@ namespace Server.Items
|
|||
var perc = m_Quantity * 100 / MaxQuantity;
|
||||
|
||||
if (perc <= 0)
|
||||
{
|
||||
return 1042975; // It's empty.
|
||||
}
|
||||
|
||||
if (perc <= 33)
|
||||
{
|
||||
return 1042974; // It's nearly empty.
|
||||
}
|
||||
|
||||
if (perc <= 66)
|
||||
{
|
||||
return 1042973; // It's half full.
|
||||
}
|
||||
|
||||
return 1042972; // It's full.
|
||||
}
|
||||
|
||||
|
|
@ -653,7 +705,9 @@ namespace Server.Items
|
|||
base.GetProperties(list);
|
||||
|
||||
if (ShowQuantity)
|
||||
{
|
||||
list.Add(GetQuantityDescription());
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnSingleClick(Mobile from)
|
||||
|
|
@ -661,13 +715,17 @@ namespace Server.Items
|
|||
base.OnSingleClick(from);
|
||||
|
||||
if (ShowQuantity)
|
||||
{
|
||||
LabelTo(from, GetQuantityDescription());
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool ValidateUse(Mobile from, bool message)
|
||||
{
|
||||
if (Deleted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Movable && !Fillable)
|
||||
{
|
||||
|
|
@ -676,7 +734,9 @@ namespace Server.Items
|
|||
if (house?.HasLockedDownItem(this) != true)
|
||||
{
|
||||
if (message)
|
||||
{
|
||||
from.SendLocalizedMessage(502946, "", 0x59); // That belongs to someone else.
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
@ -685,7 +745,9 @@ namespace Server.Items
|
|||
if (from.Map != Map || !from.InRange(GetWorldLocation(), 2) || !from.InLOS(this))
|
||||
{
|
||||
if (message)
|
||||
{
|
||||
from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that.
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
@ -696,12 +758,16 @@ namespace Server.Items
|
|||
public virtual void Fill_OnTarget(Mobile from, object targ)
|
||||
{
|
||||
if (!IsEmpty || !Fillable || !ValidateUse(from, false))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (targ is BaseBeverage bev)
|
||||
{
|
||||
if (bev.IsEmpty || !bev.ValidateUse(from, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Content = bev.Content;
|
||||
Poison = bev.Poison;
|
||||
|
|
@ -739,10 +805,14 @@ namespace Server.Items
|
|||
var src = item as IWaterSource;
|
||||
|
||||
if (src == null && item is AddonComponent component)
|
||||
{
|
||||
src = component.Addon as IWaterSource;
|
||||
}
|
||||
|
||||
if (src == null || src.Quantity <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (from.Map != item.Map || !from.InRange(item.GetWorldLocation(), 2) || !from.InLOS(item))
|
||||
{
|
||||
|
|
@ -785,7 +855,9 @@ namespace Server.Items
|
|||
var qs = player.Quest;
|
||||
|
||||
if (!(qs is WitchApprenticeQuest))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var obj = qs.FindObjective<FindIngredientObjective>();
|
||||
|
||||
|
|
@ -794,7 +866,9 @@ namespace Server.Items
|
|||
var contains = false;
|
||||
|
||||
for (var i = 0; !contains && i < m_SwampTiles.Length; i += 2)
|
||||
{
|
||||
contains = tileID >= m_SwampTiles[i] && tileID <= m_SwampTiles[i + 1];
|
||||
}
|
||||
|
||||
if (contains)
|
||||
{
|
||||
|
|
@ -813,12 +887,16 @@ namespace Server.Items
|
|||
public virtual void Pour_OnTarget(Mobile from, object targ)
|
||||
{
|
||||
if (IsEmpty || !Pourable || !ValidateUse(from, false))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (targ is BaseBeverage bev)
|
||||
{
|
||||
if (!bev.ValidateUse(from, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (bev.IsFull && bev.Content == Content)
|
||||
{
|
||||
|
|
@ -851,7 +929,9 @@ namespace Server.Items
|
|||
else if (from == targ)
|
||||
{
|
||||
if (from.Thirst < 20)
|
||||
{
|
||||
from.Thirst += 1;
|
||||
}
|
||||
|
||||
if (ContainsAlchohol)
|
||||
{
|
||||
|
|
@ -872,7 +952,9 @@ namespace Server.Items
|
|||
from.PlaySound(Utility.RandomList(0x30, 0x2D6));
|
||||
|
||||
if (Poison != null)
|
||||
{
|
||||
from.ApplyPoison(Poisoner, Poison);
|
||||
}
|
||||
|
||||
--Quantity;
|
||||
}
|
||||
|
|
@ -908,6 +990,7 @@ namespace Server.Items
|
|||
Content == BeverageType.Water)
|
||||
{
|
||||
if (from is PlayerMobile player)
|
||||
{
|
||||
if (player.Quest is SolenMatriarchQuest qs)
|
||||
{
|
||||
QuestObjective obj = qs.FindObjective<GatherWaterObjective>();
|
||||
|
|
@ -934,6 +1017,7 @@ namespace Server.Items
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -946,7 +1030,9 @@ namespace Server.Items
|
|||
if (IsEmpty)
|
||||
{
|
||||
if (!Fillable || !ValidateUse(from, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
from.BeginTarget(-1, true, TargetFlags.None, Fill_OnTarget);
|
||||
SendLocalizedMessageTo(from, 500837); // Fill from what?
|
||||
|
|
@ -969,8 +1055,12 @@ namespace Server.Items
|
|||
var total = 0;
|
||||
|
||||
for (var i = 0; i < items.Length; ++i)
|
||||
{
|
||||
if (items[i] is BaseBeverage bev && bev.Content == content && !bev.IsEmpty)
|
||||
{
|
||||
total += bev.Quantity;
|
||||
}
|
||||
}
|
||||
|
||||
if (total >= quantity)
|
||||
{
|
||||
|
|
@ -981,7 +1071,9 @@ namespace Server.Items
|
|||
for (var i = 0; i < items.Length; ++i)
|
||||
{
|
||||
if (!(items[i] is BaseBeverage bev) || bev.Content != content || bev.IsEmpty)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var theirQuantity = bev.Quantity;
|
||||
|
||||
|
|
@ -1026,7 +1118,9 @@ namespace Server.Items
|
|||
base.Deserialize(reader);
|
||||
|
||||
if (!read)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var version = reader.ReadInt();
|
||||
|
||||
|
|
@ -1062,20 +1156,23 @@ namespace Server.Items
|
|||
if (from.BAC > 0 && from.Map != Map.Internal && !from.Deleted)
|
||||
{
|
||||
if (m_Table.ContainsKey(from))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (from.BAC > 60)
|
||||
{
|
||||
from.BAC = 60;
|
||||
}
|
||||
|
||||
Timer t = new HeaveTimer(from);
|
||||
t.Start();
|
||||
|
||||
m_Table[from] = t;
|
||||
}
|
||||
else if (m_Table.TryGetValue(from, out var t))
|
||||
else if (m_Table.Remove(from, out var t))
|
||||
{
|
||||
t.Stop();
|
||||
m_Table.Remove(from);
|
||||
|
||||
from.SendLocalizedMessage(500850); // You feel sober.
|
||||
}
|
||||
|
|
@ -1103,11 +1200,15 @@ namespace Server.Items
|
|||
else if (m_Drunk.Alive)
|
||||
{
|
||||
if (m_Drunk.BAC > 60)
|
||||
{
|
||||
m_Drunk.BAC = 60;
|
||||
}
|
||||
|
||||
// chance to get sober
|
||||
if (Utility.Random(100) < 10)
|
||||
{
|
||||
--m_Drunk.BAC;
|
||||
}
|
||||
|
||||
// lose some stats
|
||||
m_Drunk.Stam -= 1;
|
||||
|
|
|
|||
|
|
@ -147,7 +147,9 @@ namespace Server.Items
|
|||
}
|
||||
|
||||
if (!isBaseCreature && !info.CriminalAggression)
|
||||
{
|
||||
Aggressors.Add(info.Attacker);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < owner.Aggressed.Count; ++i)
|
||||
|
|
@ -161,7 +163,9 @@ namespace Server.Items
|
|||
}
|
||||
|
||||
if (!isBaseCreature)
|
||||
{
|
||||
Aggressors.Add(info.Defender);
|
||||
}
|
||||
}
|
||||
|
||||
if (isBaseCreature)
|
||||
|
|
@ -170,7 +174,9 @@ namespace Server.Items
|
|||
|
||||
var master = bc.GetMaster();
|
||||
if (master != null)
|
||||
{
|
||||
Aggressors.Add(master);
|
||||
}
|
||||
|
||||
var rights = BaseCreature.GetLootingRights(bc.DamageEntries, bc.HitsMax);
|
||||
for (var i = 0; i < rights.Count; ++i)
|
||||
|
|
@ -178,7 +184,9 @@ namespace Server.Items
|
|||
var ds = rights[i];
|
||||
|
||||
if (ds.m_HasRight)
|
||||
{
|
||||
Aggressors.Add(ds.m_Mobile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -282,9 +290,13 @@ namespace Server.Items
|
|||
if (IsCriminalAction(from) && (Map?.Rules & MapRules.HarmfulRestrictions) != 0)
|
||||
{
|
||||
if (Owner?.Player != true)
|
||||
{
|
||||
from.SendLocalizedMessage(1005035); // You did not earn the right to loot this creature!
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(1010049); // You may not loot this corpse.
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
@ -315,7 +327,9 @@ namespace Server.Items
|
|||
ProcessDelta();
|
||||
|
||||
if (IsCriminalAction(from))
|
||||
{
|
||||
from.CriminalAction(true);
|
||||
}
|
||||
}
|
||||
else if (dead is BaseCreature creature)
|
||||
{
|
||||
|
|
@ -335,7 +349,9 @@ namespace Server.Items
|
|||
private void AssignInstancedLoot()
|
||||
{
|
||||
if (Aggressors.Count == 0 || Items.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_InstancedItems ??= new Dictionary<Item, InstancedItemInfo>();
|
||||
|
||||
|
|
@ -349,9 +365,13 @@ namespace Server.Items
|
|||
if (item.LootType != LootType.Cursed) // Don't have cursed items take up someone's item spot.. (?)
|
||||
{
|
||||
if (item.Stackable)
|
||||
{
|
||||
m_Stackables.Add(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Unstackables.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -392,9 +412,13 @@ namespace Server.Items
|
|||
}
|
||||
|
||||
if (remainder == 0)
|
||||
{
|
||||
m_InstancedItems.Add(item, new InstancedItemInfo(item, attackers[^1]));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Unstackables.Add(item);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -427,7 +451,9 @@ namespace Server.Items
|
|||
public void TurnToBones()
|
||||
{
|
||||
if (Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ProcessDelta();
|
||||
SendRemovePacket();
|
||||
|
|
@ -481,18 +507,28 @@ namespace Server.Items
|
|||
var item = initialContent[i];
|
||||
|
||||
if (Core.AOS && owner.Player && item.Parent == owner.Backpack)
|
||||
{
|
||||
c.AddItem(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
c.DropItem(item);
|
||||
}
|
||||
|
||||
if (owner.Player && Core.AOS)
|
||||
{
|
||||
c.SetRestoreInfo(item, item.Location);
|
||||
}
|
||||
}
|
||||
|
||||
if (Core.SE && !owner.Player)
|
||||
{
|
||||
c.AssignInstancedLoot();
|
||||
}
|
||||
else if (Core.AOS && owner is PlayerMobile pm)
|
||||
{
|
||||
c.RestoreEquip = pm.EquipSnapshot;
|
||||
}
|
||||
|
||||
var loc = owner.Location;
|
||||
var map = owner.Map;
|
||||
|
|
@ -539,6 +575,7 @@ namespace Server.Items
|
|||
writer.Write(count);
|
||||
|
||||
if (m_RestoreTable != null)
|
||||
{
|
||||
foreach (var (item, loc) in m_RestoreTable)
|
||||
{
|
||||
writer.Write(item);
|
||||
|
|
@ -553,11 +590,14 @@ namespace Server.Items
|
|||
writer.Write(loc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
writer.Write(m_DecayTimer != null);
|
||||
|
||||
if (m_DecayTimer != null)
|
||||
{
|
||||
writer.WriteDeltaTime(m_DecayTime);
|
||||
}
|
||||
|
||||
writer.Write(Looters);
|
||||
writer.Write(Killer);
|
||||
|
|
@ -586,7 +626,9 @@ namespace Server.Items
|
|||
case 12:
|
||||
{
|
||||
if (reader.ReadBool())
|
||||
{
|
||||
RestoreEquip = reader.ReadStrongItemList();
|
||||
}
|
||||
|
||||
goto case 11;
|
||||
}
|
||||
|
|
@ -604,13 +646,19 @@ namespace Server.Items
|
|||
var item = reader.ReadItem();
|
||||
|
||||
if (reader.ReadBool())
|
||||
{
|
||||
SetRestoreInfo(item, reader.ReadPoint3D());
|
||||
}
|
||||
else if (item != null)
|
||||
{
|
||||
SetRestoreInfo(item, item.Location);
|
||||
}
|
||||
}
|
||||
|
||||
if (reader.ReadBool())
|
||||
{
|
||||
BeginDecay(reader.ReadDeltaTime() - DateTime.UtcNow);
|
||||
}
|
||||
|
||||
Looters = reader.ReadStrongMobileList();
|
||||
Killer = reader.ReadMobile();
|
||||
|
|
@ -642,9 +690,13 @@ namespace Server.Items
|
|||
var item = reader.ReadItem();
|
||||
|
||||
if (reader.ReadBool())
|
||||
{
|
||||
SetRestoreInfo(item, reader.ReadPoint3D());
|
||||
}
|
||||
else if (item != null)
|
||||
{
|
||||
SetRestoreInfo(item, item.Location);
|
||||
}
|
||||
}
|
||||
|
||||
goto case 8;
|
||||
|
|
@ -658,7 +710,9 @@ namespace Server.Items
|
|||
case 7:
|
||||
{
|
||||
if (reader.ReadBool())
|
||||
{
|
||||
BeginDecay(reader.ReadDeltaTime() - DateTime.UtcNow);
|
||||
}
|
||||
|
||||
goto case 6;
|
||||
}
|
||||
|
|
@ -702,16 +756,24 @@ namespace Server.Items
|
|||
case 0:
|
||||
{
|
||||
if (version < 10)
|
||||
{
|
||||
TimeOfDeath = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
if (version < 7)
|
||||
{
|
||||
BeginDecay(m_DefaultDecayTime);
|
||||
}
|
||||
|
||||
if (version < 6)
|
||||
{
|
||||
Looters = new List<Mobile>();
|
||||
}
|
||||
|
||||
if (version < 4)
|
||||
{
|
||||
Aggressors = new List<Mobile>();
|
||||
}
|
||||
|
||||
AccessLevel = (AccessLevel)reader.ReadInt();
|
||||
reader.ReadInt(); // guild reserve
|
||||
|
|
@ -729,7 +791,9 @@ namespace Server.Items
|
|||
{
|
||||
if (Devoured || Deleted || Killer?.Deleted != false || !Killer.Alive || !(Killer is IDevourer devourer) ||
|
||||
Owner?.Deleted != false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_Devourer = devourer; // Set the devourer the killer
|
||||
return m_Devourer.Devour(this); // Devour the corpse if it hasn't
|
||||
|
|
@ -740,12 +804,18 @@ namespace Server.Items
|
|||
base.SendInfoTo(state, sendOplPacket);
|
||||
|
||||
if (!(((Body)Amount).IsHuman && ItemID == 0x2006))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.ContainerGridLines)
|
||||
{
|
||||
state.Send(new CorpseContent6017(state.Mobile, this));
|
||||
}
|
||||
else
|
||||
{
|
||||
state.Send(new CorpseContent(state.Mobile, this));
|
||||
}
|
||||
|
||||
state.Send(new CorpseEquip(state.Mobile, this));
|
||||
}
|
||||
|
|
@ -753,7 +823,9 @@ namespace Server.Items
|
|||
public bool IsCriminalAction(Mobile from)
|
||||
{
|
||||
if (from == Owner || from.AccessLevel >= AccessLevel.GameMaster)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var p = Party.Get(Owner);
|
||||
|
||||
|
|
@ -762,7 +834,9 @@ namespace Server.Items
|
|||
var pmi = p[Owner];
|
||||
|
||||
if (pmi?.CanLoot == true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return NotorietyHandlers.CorpseNotoriety(from, this) == Notoriety.Innocent;
|
||||
|
|
@ -779,16 +853,22 @@ namespace Server.Items
|
|||
base.OnItemUsed(from, item);
|
||||
|
||||
if (item is Food)
|
||||
{
|
||||
from.RevealingAction();
|
||||
}
|
||||
|
||||
if (item != this && IsCriminalAction(from))
|
||||
{
|
||||
from.CriminalAction(true);
|
||||
}
|
||||
|
||||
if (!Looters.Contains(from))
|
||||
{
|
||||
Looters.Add(from);
|
||||
}
|
||||
|
||||
m_InstancedItems?.Remove(item);
|
||||
|
||||
if (m_InstancedItems?.ContainsKey(item) == true)
|
||||
m_InstancedItems.Remove(item);
|
||||
}
|
||||
|
||||
public override void OnItemLifted(Mobile from, Item item)
|
||||
|
|
@ -796,16 +876,21 @@ namespace Server.Items
|
|||
base.OnItemLifted(from, item);
|
||||
|
||||
if (item != this && from != Owner)
|
||||
{
|
||||
from.RevealingAction();
|
||||
}
|
||||
|
||||
if (item != this && IsCriminalAction(from))
|
||||
{
|
||||
from.CriminalAction(true);
|
||||
}
|
||||
|
||||
if (!Looters.Contains(from))
|
||||
{
|
||||
Looters.Add(from);
|
||||
}
|
||||
|
||||
if (m_InstancedItems?.ContainsKey(item) == true)
|
||||
m_InstancedItems.Remove(item);
|
||||
m_InstancedItems?.Remove(item);
|
||||
}
|
||||
|
||||
public override void GetContextMenuEntries(Mobile from, List<ContextMenuEntry> list)
|
||||
|
|
@ -813,7 +898,9 @@ namespace Server.Items
|
|||
base.GetContextMenuEntries(from, list);
|
||||
|
||||
if (Core.AOS && Owner == from && from.Alive)
|
||||
{
|
||||
list.Add(new OpenCorpseEntry());
|
||||
}
|
||||
}
|
||||
|
||||
public bool GetRestoreInfo(Item item, ref Point3D loc) =>
|
||||
|
|
@ -822,7 +909,9 @@ namespace Server.Items
|
|||
public void SetRestoreInfo(Item item, Point3D loc)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_RestoreTable ??= new Dictionary<Item, Point3D>();
|
||||
|
||||
|
|
@ -832,12 +921,16 @@ namespace Server.Items
|
|||
public void ClearRestoreInfo(Item item)
|
||||
{
|
||||
if (m_RestoreTable == null || item == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_RestoreTable.Remove(item);
|
||||
|
||||
if (m_RestoreTable.Count == 0)
|
||||
{
|
||||
m_RestoreTable = null;
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanLoot(Mobile from, Item item) =>
|
||||
|
|
@ -848,9 +941,13 @@ namespace Server.Items
|
|||
if (!CanLoot(from, item))
|
||||
{
|
||||
if (Owner?.Player != true)
|
||||
{
|
||||
from.SendLocalizedMessage(1005035); // You did not earn the right to loot this creature!
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(1010049); // You may not loot this corpse.
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
@ -858,9 +955,13 @@ namespace Server.Items
|
|||
if (IsCriminalAction(from))
|
||||
{
|
||||
if (Owner?.Player != true)
|
||||
{
|
||||
from.SendLocalizedMessage(1005036); // Looting this monster corpse will be a criminal act!
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(1005038); // Looting this corpse will be a criminal act!
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -898,7 +999,9 @@ namespace Server.Items
|
|||
var packItem = packItems[i];
|
||||
|
||||
if (RestoreEquip.Contains(packItem) && packItem.Movable)
|
||||
{
|
||||
from.EquipItem(packItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -913,7 +1016,9 @@ namespace Server.Items
|
|||
|
||||
if (item.Layer == Layer.Hair || item.Layer == Layer.FacialHair || !item.Movable ||
|
||||
!GetRestoreInfo(item, ref loc))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pack?.CheckHold(from, item, false, true) == true)
|
||||
{
|
||||
|
|
@ -921,7 +1026,9 @@ namespace Server.Items
|
|||
pack.AddItem(item);
|
||||
|
||||
if (RestoreEquip?.Contains(item) == true)
|
||||
{
|
||||
from.EquipItem(item);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -955,9 +1062,14 @@ namespace Server.Items
|
|||
}
|
||||
|
||||
if (!CheckLoot(from, null))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(from is PlayerMobile player)) return;
|
||||
if (!(from is PlayerMobile player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var qs = player.Quest;
|
||||
|
||||
|
|
@ -978,7 +1090,9 @@ namespace Server.Items
|
|||
); // You rummage through the bones and find a Daemon Bone! You quickly place the item in your pack.
|
||||
|
||||
if (!obj.Completed)
|
||||
{
|
||||
obj.Complete();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1033,9 +1147,13 @@ namespace Server.Items
|
|||
if (ItemID == 0x2006) // Corpse form
|
||||
{
|
||||
if (m_CorpseName != null)
|
||||
{
|
||||
list.Add(m_CorpseName);
|
||||
}
|
||||
else
|
||||
{
|
||||
list.Add(1046414, Name); // the remains of ~1_NAME~
|
||||
}
|
||||
}
|
||||
else // Bone form
|
||||
{
|
||||
|
|
@ -1049,7 +1167,9 @@ namespace Server.Items
|
|||
var opl = PropertyList;
|
||||
|
||||
if (opl.Header > 0)
|
||||
{
|
||||
from.Send(new MessageLocalized(Serial, ItemID, MessageType.Label, hue, 3, opl.Header, Name, opl.HeaderArgs));
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnSingleClick(Mobile from)
|
||||
|
|
@ -1059,9 +1179,13 @@ namespace Server.Items
|
|||
if (ItemID == 0x2006) // Corpse form
|
||||
{
|
||||
if (m_CorpseName != null)
|
||||
{
|
||||
from.Send(new AsciiMessage(Serial, ItemID, MessageType.Label, hue, 3, "", m_CorpseName));
|
||||
}
|
||||
else
|
||||
{
|
||||
from.Send(new MessageLocalized(Serial, ItemID, MessageType.Label, hue, 3, 1046414, "", Name));
|
||||
}
|
||||
}
|
||||
else // Bone form
|
||||
{
|
||||
|
|
@ -1085,13 +1209,19 @@ namespace Server.Items
|
|||
public bool IsOwner(Mobile m)
|
||||
{
|
||||
if (m_Item.LootType == LootType.Cursed) // Cursed Items are part of everyone's instanced corpse... (?)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m == null)
|
||||
{
|
||||
return false; // sanity
|
||||
}
|
||||
|
||||
if (m_Mobile == m)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var myParty = Party.Get(m_Mobile);
|
||||
|
||||
|
|
@ -1112,9 +1242,13 @@ namespace Server.Items
|
|||
protected override void OnTick()
|
||||
{
|
||||
if (!m_Corpse.GetFlag(CorpseFlag.NoBones))
|
||||
{
|
||||
m_Corpse.TurnToBones();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Corpse.Delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1127,7 +1261,9 @@ namespace Server.Items
|
|||
public override void OnClick()
|
||||
{
|
||||
if (Owner.Target is Corpse corpse && Owner.From.CheckAlive())
|
||||
{
|
||||
corpse.Open(Owner.From, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,7 +75,9 @@ namespace Server.Items
|
|||
public static void RemoveKeys(Mobile m, uint keyValue)
|
||||
{
|
||||
if (keyValue == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RemoveKeys(m.Backpack, keyValue);
|
||||
RemoveKeys(m.BankBox, keyValue);
|
||||
|
|
@ -84,15 +86,20 @@ namespace Server.Items
|
|||
public static void RemoveKeys(Container cont, uint keyValue)
|
||||
{
|
||||
if (cont == null || keyValue == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var items = cont.FindItemsByType(new[] { typeof(Key), typeof(KeyRing) });
|
||||
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (item is Key key)
|
||||
{
|
||||
if (key.KeyValue == keyValue)
|
||||
{
|
||||
key.Delete();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -100,28 +107,37 @@ namespace Server.Items
|
|||
|
||||
keyRing.RemoveKeys(keyValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool ContainsKey(Container cont, uint keyValue)
|
||||
{
|
||||
if (cont == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var items = cont.FindItemsByType(new[] { typeof(Key), typeof(KeyRing) });
|
||||
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (item is Key key)
|
||||
{
|
||||
if (key.KeyValue == keyValue)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var keyRing = (KeyRing)item;
|
||||
|
||||
if (keyRing.ContainsKey(keyValue))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
@ -163,7 +179,9 @@ namespace Server.Items
|
|||
case 0:
|
||||
{
|
||||
if (version < 2 || MaxRange == 0)
|
||||
{
|
||||
MaxRange = 3;
|
||||
}
|
||||
|
||||
m_Description = reader.ReadString();
|
||||
|
||||
|
|
@ -207,12 +225,18 @@ namespace Server.Items
|
|||
string desc;
|
||||
|
||||
if (m_KeyVal == 0)
|
||||
{
|
||||
desc = "(blank)";
|
||||
}
|
||||
else if ((desc = m_Description) == null || (desc = desc.Trim()).Length <= 0)
|
||||
{
|
||||
desc = null;
|
||||
}
|
||||
|
||||
if (desc != null)
|
||||
{
|
||||
list.Add(desc);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnSingleClick(Mobile from)
|
||||
|
|
@ -222,12 +246,18 @@ namespace Server.Items
|
|||
string desc;
|
||||
|
||||
if (m_KeyVal == 0)
|
||||
{
|
||||
desc = "(blank)";
|
||||
}
|
||||
else if ((desc = m_Description) == null || (desc = desc.Trim()).Length <= 0)
|
||||
{
|
||||
desc = "";
|
||||
}
|
||||
|
||||
if (desc.Length > 0)
|
||||
{
|
||||
from.Send(new UnicodeMessage(Serial, ItemID, MessageType.Regular, 0x3B2, 3, "ENU", "", desc));
|
||||
}
|
||||
}
|
||||
|
||||
public bool UseOn(Mobile from, ILockable o)
|
||||
|
|
@ -235,30 +265,44 @@ namespace Server.Items
|
|||
if (o.KeyValue == KeyValue)
|
||||
{
|
||||
if (o is BaseDoor door && !door.UseLocks())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
o.Locked = !o.Locked;
|
||||
|
||||
if (o is LockableContainer cont1)
|
||||
{
|
||||
if (cont1.LockLevel == -255)
|
||||
{
|
||||
cont1.LockLevel = cont1.RequiredSkill - 10;
|
||||
}
|
||||
}
|
||||
|
||||
if (o is Item item)
|
||||
{
|
||||
if (o.Locked)
|
||||
{
|
||||
item.SendLocalizedMessageTo(from, 1048000); // You lock it.
|
||||
}
|
||||
else
|
||||
{
|
||||
item.SendLocalizedMessageTo(from, 1048001); // You unlock it.
|
||||
}
|
||||
|
||||
if (item is LockableContainer cont && cont.TrapType != TrapType.None && cont.TrapOnLockpick)
|
||||
{
|
||||
if (o.Locked)
|
||||
{
|
||||
cont.SendLocalizedMessageTo(from, 501673); // You re-enable the trap.
|
||||
}
|
||||
else
|
||||
{
|
||||
cont.SendLocalizedMessageTo(
|
||||
from,
|
||||
501672
|
||||
); // You disable the trap temporarily. Lock it again to re-enable it.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -315,16 +359,23 @@ namespace Server.Items
|
|||
else if (targeted is ILockable lockable)
|
||||
{
|
||||
if (m_Key.UseOn(from, lockable))
|
||||
{
|
||||
number = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
number = 501668; // This key doesn't seem to unlock that.
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
number = 501666; // You can't unlock that!
|
||||
}
|
||||
|
||||
if (number != -1) from.SendLocalizedMessage(number);
|
||||
if (number != -1)
|
||||
{
|
||||
from.SendLocalizedMessage(number);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -62,7 +62,10 @@ namespace Server.Items
|
|||
{
|
||||
base.OnDelete();
|
||||
|
||||
foreach (var key in Keys) key.Delete();
|
||||
foreach (var key in Keys)
|
||||
{
|
||||
key.Delete();
|
||||
}
|
||||
|
||||
Keys.Clear();
|
||||
}
|
||||
|
|
@ -78,14 +81,18 @@ namespace Server.Items
|
|||
public void Open(Mobile from)
|
||||
{
|
||||
if (!(Parent is Container cont))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = Keys.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var key = Keys[i];
|
||||
|
||||
if (!key.Deleted && !cont.TryDropItem(from, key, true))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
Keys.RemoveAt(i);
|
||||
}
|
||||
|
|
@ -112,8 +119,12 @@ namespace Server.Items
|
|||
public bool ContainsKey(uint keyValue)
|
||||
{
|
||||
foreach (var key in Keys)
|
||||
{
|
||||
if (key.KeyValue == keyValue)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
@ -121,13 +132,21 @@ namespace Server.Items
|
|||
private void UpdateItemID()
|
||||
{
|
||||
if (Keys.Count < 1)
|
||||
{
|
||||
ItemID = 0x1011;
|
||||
}
|
||||
else if (Keys.Count < 3)
|
||||
{
|
||||
ItemID = 0x1769;
|
||||
}
|
||||
else if (Keys.Count < 5)
|
||||
{
|
||||
ItemID = 0x176A;
|
||||
}
|
||||
else
|
||||
{
|
||||
ItemID = 0x176B;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Serialize(IGenericWriter writer)
|
||||
|
|
@ -170,8 +189,12 @@ namespace Server.Items
|
|||
else if (targeted is ILockable o)
|
||||
{
|
||||
foreach (var key in m_KeyRing.Keys)
|
||||
{
|
||||
if (key.UseOn(from, o))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
from.SendLocalizedMessage(1008140); // You do not have a key for that.
|
||||
}
|
||||
|
|
|
|||
|
|
@ -652,11 +652,10 @@ namespace Server.Items
|
|||
|
||||
public static void EventSink_Logout(Mobile from)
|
||||
{
|
||||
if (from == null || !m_Table.TryGetValue(from, out var info))
|
||||
return;
|
||||
|
||||
info.Timer.Stop();
|
||||
m_Table.Remove(from);
|
||||
if (from != null && m_Table.Remove(from, out var info))
|
||||
{
|
||||
info.Timer.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
public static string FormatTime(TimeSpan ts)
|
||||
|
|
@ -802,10 +801,9 @@ namespace Server.Items
|
|||
|
||||
public void StopTimer(Mobile m)
|
||||
{
|
||||
if (m_Teleporting.TryGetValue(m, out var t))
|
||||
if (m_Teleporting.Remove(m, out var t))
|
||||
{
|
||||
t.Stop();
|
||||
m_Teleporting.Remove(m);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -107,10 +107,9 @@ namespace Server.Items
|
|||
|
||||
public static void EndDelay(Mobile m)
|
||||
{
|
||||
if (m_Delay.TryGetValue(m, out var timer))
|
||||
if (m_Delay.Remove(m, out var timer))
|
||||
{
|
||||
timer.Stop();
|
||||
m_Delay.Remove(m);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -123,10 +123,9 @@ namespace Server.Items
|
|||
|
||||
public static void EndDelay(Mobile m)
|
||||
{
|
||||
if (m_Delay.TryGetValue(m, out var timer))
|
||||
if (m_Delay.Remove(m, out var timer))
|
||||
{
|
||||
timer.Stop();
|
||||
m_Delay.Remove(m);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -66,12 +66,14 @@ namespace Server.Items
|
|||
|
||||
public static void RemoveTimer(Mobile m, bool interrupted = false)
|
||||
{
|
||||
if (m_Table.TryGetValue(m, out var timer))
|
||||
if (m_Table.Remove(m, out var timer))
|
||||
{
|
||||
if (interrupted)
|
||||
{
|
||||
m.SendLocalizedMessage(1073187); // The invisibility effect is interrupted.
|
||||
}
|
||||
|
||||
timer.Stop();
|
||||
m_Table.Remove(m);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -283,10 +283,9 @@ namespace Server.Items
|
|||
|
||||
public static void RemoveTimer(Mobile m)
|
||||
{
|
||||
if (Timers.TryGetValue(m, out var t))
|
||||
if (Timers.Remove(m, out var t))
|
||||
{
|
||||
t.Stop();
|
||||
Timers.Remove(m);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,9 @@ namespace Server.Items
|
|||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (!Validate(attacker) || !CheckMana(attacker, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ClearCurrentAbility(attacker);
|
||||
|
||||
|
|
@ -73,7 +75,9 @@ namespace Server.Items
|
|||
var damage = Utility.RandomMinMax(level, level * 2);
|
||||
|
||||
if (!m.Player)
|
||||
{
|
||||
damage *= 2;
|
||||
}
|
||||
|
||||
m.PlaySound(0x133);
|
||||
m.Damage(damage, from);
|
||||
|
|
@ -89,14 +93,17 @@ namespace Server.Items
|
|||
|
||||
public static void EndBleed(Mobile m, bool message)
|
||||
{
|
||||
if (!m_Table.TryGetValue(m, out var t))
|
||||
if (!m_Table.Remove(m, out var t))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
t.Stop();
|
||||
m_Table.Remove(m);
|
||||
|
||||
if (message)
|
||||
{
|
||||
m.SendLocalizedMessage(1060167); // The bleeding wounds have healed, you are no longer bleeding!
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalTimer : Timer
|
||||
|
|
@ -117,7 +124,9 @@ namespace Server.Items
|
|||
DoBleed(m_Mobile, m_From, 5 - m_Count);
|
||||
|
||||
if (++m_Count == 5)
|
||||
{
|
||||
EndBleed(m_Mobile, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,9 @@ namespace Server.Items
|
|||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (!Validate(attacker) || !CheckMana(attacker, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ClearCurrentAbility(attacker);
|
||||
|
||||
|
|
@ -49,7 +51,9 @@ namespace Server.Items
|
|||
public static bool GetBonus(Mobile targ, ref int bonus)
|
||||
{
|
||||
if (!m_Table.TryGetValue(targ, out var info))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bonus = info.m_Bonus;
|
||||
return true;
|
||||
|
|
@ -63,11 +67,10 @@ namespace Server.Items
|
|||
|
||||
public static void EndBlock(Mobile m)
|
||||
{
|
||||
if (!m_Table.TryGetValue(m, out var info))
|
||||
return;
|
||||
|
||||
info.m_Timer?.Stop();
|
||||
m_Table.Remove(m);
|
||||
if (m_Table.Remove(m, out var info))
|
||||
{
|
||||
info.m_Timer?.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
private class BlockInfo
|
||||
|
|
|
|||
|
|
@ -19,7 +19,10 @@ namespace Server.Items
|
|||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (!Validate(attacker) || !CheckMana(attacker, true)) return;
|
||||
if (!Validate(attacker) || !CheckMana(attacker, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ClearCurrentAbility(attacker);
|
||||
|
||||
|
|
@ -31,7 +34,9 @@ namespace Server.Items
|
|||
|
||||
// Do not reset timer if one is already in place.
|
||||
if (!IsWounded(defender))
|
||||
{
|
||||
BeginWound(defender, defender.Player ? PlayerDuration : NPCDuration);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsWounded(Mobile m) => m_Table.ContainsKey(m);
|
||||
|
|
@ -39,7 +44,9 @@ namespace Server.Items
|
|||
public static void BeginWound(Mobile m, TimeSpan duration)
|
||||
{
|
||||
if (m_Table.TryGetValue(m, out var timer))
|
||||
{
|
||||
timer?.Stop();
|
||||
}
|
||||
|
||||
m_Table[m] = timer = new InternalTimer(m, duration);
|
||||
timer.Start();
|
||||
|
|
@ -49,10 +56,9 @@ namespace Server.Items
|
|||
|
||||
public static void EndWound(Mobile m)
|
||||
{
|
||||
if (m_Table.TryGetValue(m, out var timer))
|
||||
if (m_Table.Remove(m, out var timer))
|
||||
{
|
||||
timer.Stop();
|
||||
m_Table.Remove(m);
|
||||
}
|
||||
|
||||
m.YellowHealthbar = false;
|
||||
|
|
|
|||
|
|
@ -22,17 +22,17 @@ namespace Server.Items
|
|||
{
|
||||
if (!base.CheckSkills( from ))
|
||||
return false;
|
||||
|
||||
|
||||
if (!(from.Weapon is Fists))
|
||||
return true;
|
||||
|
||||
|
||||
Skill skill = from.Skills.Anatomy;
|
||||
|
||||
|
||||
if (skill?.Base >= 80.0)
|
||||
return true;
|
||||
|
||||
|
||||
from.SendLocalizedMessage( 1061811 ); // You lack the required anatomy skill to perform that attack!
|
||||
|
||||
|
||||
return false;
|
||||
}*/
|
||||
|
||||
|
|
@ -91,10 +91,9 @@ namespace Server.Items
|
|||
|
||||
public static void EndImmunity(Mobile m)
|
||||
{
|
||||
if (m_Table.TryGetValue(m, out var timer))
|
||||
if (m_Table.Remove(m, out var timer))
|
||||
{
|
||||
timer?.Stop();
|
||||
m_Table.Remove(m);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -71,11 +71,15 @@ namespace Server.Items
|
|||
canSwing = !attacker.Paralyzed && !attacker.Frozen;
|
||||
|
||||
if (canSwing)
|
||||
{
|
||||
canSwing = !(attacker.Spell is Spell sp) || !sp.IsCasting || !sp.BlocksMovement;
|
||||
}
|
||||
}
|
||||
|
||||
if ((attacker as PlayerMobile)?.DuelContext?.CheckItemEquip(attacker, this) == false)
|
||||
{
|
||||
canSwing = false;
|
||||
}
|
||||
|
||||
if (canSwing && attacker.HarmfulCheck(defender))
|
||||
{
|
||||
|
|
@ -85,9 +89,13 @@ namespace Server.Items
|
|||
if (OnFired(attacker, defender))
|
||||
{
|
||||
if (CheckHit(attacker, defender))
|
||||
{
|
||||
OnHit(attacker, defender);
|
||||
}
|
||||
else
|
||||
{
|
||||
OnMiss(attacker, defender);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -105,7 +113,9 @@ namespace Server.Items
|
|||
{
|
||||
if (attacker.Player && !defender.Player && (defender.Body.IsAnimal || defender.Body.IsMonster) &&
|
||||
Utility.RandomDouble() <= 0.4)
|
||||
{
|
||||
defender.AddToBackpack(Ammo);
|
||||
}
|
||||
|
||||
if (Core.ML && m_Velocity > 0)
|
||||
{
|
||||
|
|
@ -116,10 +126,14 @@ namespace Server.Items
|
|||
AOS.Damage(defender, attacker, bonus * 3, 100, 0, 0, 0, 0);
|
||||
|
||||
if (attacker.Player)
|
||||
{
|
||||
attacker.SendLocalizedMessage(1072794); // Your arrow hits its mark with velocity!
|
||||
}
|
||||
|
||||
if (defender.Player)
|
||||
{
|
||||
defender.SendLocalizedMessage(1072795); // You have been hit by an arrow with velocity!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -136,17 +150,17 @@ namespace Server.Items
|
|||
{
|
||||
var ammo = AmmoType;
|
||||
|
||||
if (pm.RecoverableAmmo.ContainsKey(ammo))
|
||||
pm.RecoverableAmmo[ammo]++;
|
||||
else
|
||||
pm.RecoverableAmmo.Add(ammo, 1);
|
||||
pm.RecoverableAmmo.TryGetValue(ammo, out var result);
|
||||
pm.RecoverableAmmo[ammo] = result + 1;
|
||||
|
||||
if (!pm.Warmode)
|
||||
{
|
||||
m_RecoveryTimer ??= Timer.DelayCall(TimeSpan.FromSeconds(10), pm.RecoverAmmo);
|
||||
|
||||
if (!m_RecoveryTimer.Running)
|
||||
{
|
||||
m_RecoveryTimer.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -177,9 +191,13 @@ namespace Server.Items
|
|||
{
|
||||
// consume ammo
|
||||
if (quiver?.ConsumeTotal(AmmoType) == true)
|
||||
{
|
||||
quiver.InvalidateWeight();
|
||||
}
|
||||
else if (pack?.ConsumeTotal(AmmoType) != true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (quiver.FindItemByType(AmmoType) == null && pack?.FindItemByType(AmmoType) == null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ namespace Server
|
|||
|
||||
if (archer && from != null)
|
||||
{
|
||||
quiver = @from.FindItemOnLayer(Layer.Cloak) as BaseQuiver;
|
||||
quiver = from.FindItemOnLayer(Layer.Cloak) as BaseQuiver;
|
||||
}
|
||||
|
||||
int totalDamage;
|
||||
|
|
|
|||
|
|
@ -66,9 +66,13 @@ namespace Server.Guilds
|
|||
public void SetFlag(RankFlags flag, bool value)
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
Flags |= flag;
|
||||
}
|
||||
else
|
||||
{
|
||||
Flags &= ~flag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -89,8 +93,7 @@ namespace Server.Guilds
|
|||
leader.Alliance = this;
|
||||
partner.Alliance = this;
|
||||
|
||||
if (!Alliances.ContainsKey(Name.ToLower()))
|
||||
Alliances.Add(Name.ToLower(), this);
|
||||
Alliances.TryAdd(Name.ToLower(), this);
|
||||
}
|
||||
|
||||
public AllianceInfo(IGenericReader reader)
|
||||
|
|
@ -126,12 +129,16 @@ namespace Server.Guilds
|
|||
set
|
||||
{
|
||||
if (m_Leader != value && value != null)
|
||||
{
|
||||
AllianceMessage(1070765, value.Name); // Your Alliance is now led by ~1_GUILDNAME~
|
||||
}
|
||||
|
||||
m_Leader = value;
|
||||
|
||||
if (m_Leader == null)
|
||||
{
|
||||
CalculateAllianceLeader();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -147,14 +154,18 @@ namespace Server.Guilds
|
|||
CalculateAllianceLeader();
|
||||
|
||||
if (m_Leader == null)
|
||||
{
|
||||
Disband();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsPendingMember(Guild g)
|
||||
{
|
||||
if (g.Alliance != this)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_PendingMembers.Contains(g);
|
||||
}
|
||||
|
|
@ -162,7 +173,9 @@ namespace Server.Guilds
|
|||
public bool IsMember(Guild g)
|
||||
{
|
||||
if (g.Alliance != this)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_Members.Contains(g);
|
||||
}
|
||||
|
|
@ -177,14 +190,15 @@ namespace Server.Guilds
|
|||
writer.WriteGuildList(m_Members, true);
|
||||
writer.WriteGuildList(m_PendingMembers, true);
|
||||
|
||||
if (!Alliances.ContainsKey(Name.ToLower()))
|
||||
Alliances.Add(Name.ToLower(), this);
|
||||
Alliances.TryAdd(Name.ToLower(), this);
|
||||
}
|
||||
|
||||
public void AddPendingGuild(Guild g)
|
||||
{
|
||||
if (g.Alliance != this || m_PendingMembers.Contains(g) || m_Members.Contains(g))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_PendingMembers.Add(g);
|
||||
}
|
||||
|
|
@ -192,7 +206,9 @@ namespace Server.Guilds
|
|||
public void TurnToMember(Guild g)
|
||||
{
|
||||
if (g.Alliance != this || !m_PendingMembers.Contains(g) || m_Members.Contains(g))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
g.GuildMessage(1070760, Name); // Your Guild has joined the ~1_ALLIANCENAME~ Alliance.
|
||||
AllianceMessage(1070761, g.Name); // A new Guild has joined your Alliance: ~1_GUILDNAME~
|
||||
|
|
@ -204,7 +220,10 @@ namespace Server.Guilds
|
|||
|
||||
public void RemoveGuild(Guild g)
|
||||
{
|
||||
if (m_PendingMembers.Contains(g)) m_PendingMembers.Remove(g);
|
||||
if (m_PendingMembers.Contains(g))
|
||||
{
|
||||
m_PendingMembers.Remove(g);
|
||||
}
|
||||
|
||||
if (m_Members.Contains(g)) // Sanity, just incase someone with a custom script adds a character to BOTH arrays
|
||||
{
|
||||
|
|
@ -219,10 +238,15 @@ namespace Server.Guilds
|
|||
// to check on OSI: have 3 guilds, make 2 of them a member, one pending. remove one of the memebers. alliance still exist?
|
||||
// ANSWER: NO
|
||||
|
||||
if (g == m_Leader) CalculateAllianceLeader();
|
||||
if (g == m_Leader)
|
||||
{
|
||||
CalculateAllianceLeader();
|
||||
}
|
||||
|
||||
if (m_Members.Count < 2)
|
||||
{
|
||||
Disband();
|
||||
}
|
||||
}
|
||||
|
||||
public void Disband()
|
||||
|
|
@ -230,13 +254,21 @@ namespace Server.Guilds
|
|||
AllianceMessage(1070762); // Your Alliance has dissolved.
|
||||
|
||||
for (var i = 0; i < m_PendingMembers.Count; i++)
|
||||
{
|
||||
m_PendingMembers[i].Alliance = null;
|
||||
}
|
||||
|
||||
for (var i = 0; i < m_Members.Count; i++)
|
||||
{
|
||||
m_Members[i].Alliance = null;
|
||||
}
|
||||
|
||||
if (Alliances.TryGetValue(Name.ToLower(), out var aInfo) && aInfo == this)
|
||||
Alliances.Remove(Name.ToLower());
|
||||
var lowerName = Name.ToLower();
|
||||
|
||||
if (Alliances.TryGetValue(lowerName, out var aInfo) && aInfo == this)
|
||||
{
|
||||
Alliances.Remove(lowerName);
|
||||
}
|
||||
}
|
||||
|
||||
public void InvalidateMemberProperties(bool onlyOPL = false)
|
||||
|
|
@ -252,7 +284,9 @@ namespace Server.Guilds
|
|||
public void InvalidateMemberNotoriety()
|
||||
{
|
||||
for (var i = 0; i < m_Members.Count; i++)
|
||||
{
|
||||
m_Members[i].InvalidateMemberNotoriety();
|
||||
}
|
||||
}
|
||||
|
||||
public void AllianceMessage(int num, bool append, string format, params object[] args)
|
||||
|
|
@ -263,19 +297,25 @@ namespace Server.Guilds
|
|||
public void AllianceMessage(int number)
|
||||
{
|
||||
for (var i = 0; i < m_Members.Count; ++i)
|
||||
{
|
||||
m_Members[i].GuildMessage(number);
|
||||
}
|
||||
}
|
||||
|
||||
public void AllianceMessage(int number, string args, int hue = 0x3B2)
|
||||
{
|
||||
for (var i = 0; i < m_Members.Count; ++i)
|
||||
{
|
||||
m_Members[i].GuildMessage(number, args, hue);
|
||||
}
|
||||
}
|
||||
|
||||
public void AllianceMessage(int number, bool append, string affix, string args = "", int hue = 0x3B2)
|
||||
{
|
||||
for (var i = 0; i < m_Members.Count; ++i)
|
||||
{
|
||||
m_Members[i].GuildMessage(number, append, affix, args, hue);
|
||||
}
|
||||
}
|
||||
|
||||
public void AllianceTextMessage(string text)
|
||||
|
|
@ -291,7 +331,9 @@ namespace Server.Guilds
|
|||
public void AllianceTextMessage(int hue, string text)
|
||||
{
|
||||
for (var i = 0; i < m_Members.Count; ++i)
|
||||
{
|
||||
m_Members[i].GuildTextMessage(hue, text);
|
||||
}
|
||||
}
|
||||
|
||||
public void AllianceTextMessage(int hue, string format, params object[] args)
|
||||
|
|
@ -383,7 +425,9 @@ namespace Server.Guilds
|
|||
public override void OnResponse(NetState sender, RelayInfo info)
|
||||
{
|
||||
if (info.ButtonID != 8) // So that they can't get to the AdvancedSearch button
|
||||
{
|
||||
base.OnResponse(sender, info);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -451,23 +495,33 @@ namespace Server.Guilds
|
|||
get
|
||||
{
|
||||
if (Opponent?.Disbanded != false)
|
||||
{
|
||||
return WarStatus.Win;
|
||||
}
|
||||
|
||||
if (Guild?.Disbanded != false)
|
||||
{
|
||||
return WarStatus.Lose;
|
||||
}
|
||||
|
||||
var w = Opponent.FindActiveWar(Guild);
|
||||
|
||||
if (Opponent.FindPendingWar(Guild) != null && Guild.FindPendingWar(Opponent) != null)
|
||||
{
|
||||
return WarStatus.Pending;
|
||||
}
|
||||
|
||||
if (w == null)
|
||||
{
|
||||
return WarStatus.Win;
|
||||
}
|
||||
|
||||
if (WarLength != TimeSpan.Zero && WarBeginning + WarLength < DateTime.UtcNow)
|
||||
{
|
||||
if (Kills > w.Kills)
|
||||
{
|
||||
return WarStatus.Win;
|
||||
}
|
||||
|
||||
return Kills < w.Kills ? WarStatus.Lose : WarStatus.Draw;
|
||||
}
|
||||
|
|
@ -475,9 +529,14 @@ namespace Server.Guilds
|
|||
if (MaxKills > 0)
|
||||
{
|
||||
if (Kills >= MaxKills)
|
||||
{
|
||||
return WarStatus.Win;
|
||||
}
|
||||
|
||||
if (w.Kills >= w.MaxKills)
|
||||
{
|
||||
return WarStatus.Lose;
|
||||
}
|
||||
}
|
||||
|
||||
return WarStatus.InProgress;
|
||||
|
|
@ -509,13 +568,17 @@ namespace Server.Guilds
|
|||
public static void Initialize()
|
||||
{
|
||||
if (Guild.NewGuildSystem)
|
||||
{
|
||||
new WarTimer().Start();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
foreach (var g in BaseGuild.List.Values)
|
||||
{
|
||||
(g as Guild)?.CheckExpiredWars();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -561,7 +624,9 @@ namespace Server.Guilds
|
|||
AddMember(m_Leader);
|
||||
|
||||
if (m_Leader is PlayerMobile mobile)
|
||||
{
|
||||
mobile.GuildRank = RankDefinition.Leader;
|
||||
}
|
||||
|
||||
AcceptedWars = new List<WarDeclaration>();
|
||||
PendingWars = new List<WarDeclaration>();
|
||||
|
|
@ -580,22 +645,30 @@ namespace Server.Guilds
|
|||
get
|
||||
{
|
||||
if (Disbanded || m_Leader.Guild != this)
|
||||
{
|
||||
CalculateGuildmaster();
|
||||
}
|
||||
|
||||
return m_Leader;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
AddMember(value); // Also removes from old guild.
|
||||
}
|
||||
|
||||
if (m_Leader is PlayerMobile leader && leader.Guild == this)
|
||||
{
|
||||
leader.GuildRank = RankDefinition.Member;
|
||||
}
|
||||
|
||||
m_Leader = value;
|
||||
|
||||
if (m_Leader is PlayerMobile mobile)
|
||||
{
|
||||
mobile.GuildRank = RankDefinition.Leader;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -606,7 +679,9 @@ namespace Server.Guilds
|
|||
get
|
||||
{
|
||||
if (m_AllianceInfo != null)
|
||||
{
|
||||
return m_AllianceInfo;
|
||||
}
|
||||
|
||||
return m_AllianceLeader?.m_AllianceInfo;
|
||||
}
|
||||
|
|
@ -615,16 +690,22 @@ namespace Server.Guilds
|
|||
var current = Alliance;
|
||||
|
||||
if (value == current)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
current?.RemoveGuild(this);
|
||||
|
||||
if (value != null)
|
||||
{
|
||||
if (value.Leader == this)
|
||||
{
|
||||
m_AllianceInfo = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_AllianceLeader = value.Leader;
|
||||
}
|
||||
|
||||
value.AddPendingGuild(this);
|
||||
}
|
||||
|
|
@ -748,14 +829,18 @@ namespace Server.Guilds
|
|||
m.InvalidateProperties();
|
||||
|
||||
if (!onlyOPL)
|
||||
{
|
||||
m.Delta(MobileDelta.Noto);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void InvalidateMemberNotoriety()
|
||||
{
|
||||
for (var i = 0; i < Members?.Count; i++)
|
||||
{
|
||||
Members[i].Delta(MobileDelta.Noto);
|
||||
}
|
||||
}
|
||||
|
||||
public void InvalidateWarNotoriety()
|
||||
|
|
@ -763,21 +848,31 @@ namespace Server.Guilds
|
|||
var g = GetAllianceLeader(this);
|
||||
|
||||
if (g.Alliance != null)
|
||||
{
|
||||
g.Alliance.InvalidateMemberNotoriety();
|
||||
}
|
||||
else
|
||||
{
|
||||
g.InvalidateMemberNotoriety();
|
||||
}
|
||||
|
||||
if (g.AcceptedWars == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var warDec in g.AcceptedWars)
|
||||
{
|
||||
var opponent = warDec.Opponent;
|
||||
|
||||
if (opponent.Alliance != null)
|
||||
{
|
||||
opponent.Alliance.InvalidateMemberNotoriety();
|
||||
}
|
||||
else
|
||||
{
|
||||
opponent.InvalidateMemberNotoriety();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -797,7 +892,9 @@ namespace Server.Guilds
|
|||
m.SendLocalizedMessage(502131); // Your guild has disbanded.
|
||||
|
||||
if (m is PlayerMobile mobile)
|
||||
{
|
||||
mobile.GuildRank = RankDefinition.Lowest;
|
||||
}
|
||||
|
||||
m.Guild = null;
|
||||
}
|
||||
|
|
@ -805,15 +902,25 @@ namespace Server.Guilds
|
|||
Members.Clear();
|
||||
|
||||
for (var i = Allies.Count - 1; i >= 0; --i)
|
||||
{
|
||||
if (i < Allies.Count)
|
||||
{
|
||||
RemoveAlly(Allies[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = Enemies.Count - 1; i >= 0; --i)
|
||||
{
|
||||
if (i < Enemies.Count)
|
||||
{
|
||||
RemoveEnemy(Enemies[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!NewGuildSystem)
|
||||
{
|
||||
Guildstone?.Delete();
|
||||
}
|
||||
|
||||
Guildstone = null;
|
||||
|
||||
|
|
@ -846,7 +953,9 @@ namespace Server.Guilds
|
|||
from.SendGump(new PropertiesGump(from, g));
|
||||
|
||||
if (NewGuildSystem && from.AccessLevel >= AccessLevel.GameMaster && from is PlayerMobile mobile)
|
||||
{
|
||||
mobile.SendGump(new GuildInfoGump(mobile, g));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -854,12 +963,18 @@ namespace Server.Guilds
|
|||
public static void EventSink_GuildGumpRequest(Mobile m)
|
||||
{
|
||||
if (!NewGuildSystem || !(m is PlayerMobile pm))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (pm.Guild == null)
|
||||
{
|
||||
pm.SendGump(new CreateGuildGump(pm));
|
||||
}
|
||||
else
|
||||
{
|
||||
pm.SendGump(new GuildInfoGump(pm, pm.Guild as Guild));
|
||||
}
|
||||
}
|
||||
|
||||
public static void EventSink_CreateGuild(CreateGuildEventArgs args)
|
||||
|
|
@ -872,7 +987,9 @@ namespace Server.Guilds
|
|||
var alliance = g.Alliance;
|
||||
|
||||
if (alliance?.Leader != null && alliance.IsMember(g))
|
||||
{
|
||||
return alliance.Leader;
|
||||
}
|
||||
|
||||
return g;
|
||||
}
|
||||
|
|
@ -884,7 +1001,9 @@ namespace Server.Guilds
|
|||
var w = PendingWars[i];
|
||||
|
||||
if (w.Opponent == g)
|
||||
{
|
||||
return w;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
@ -897,7 +1016,9 @@ namespace Server.Guilds
|
|||
var w = AcceptedWars[i];
|
||||
|
||||
if (w.Opponent == g)
|
||||
{
|
||||
return w;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
@ -942,10 +1063,14 @@ namespace Server.Guilds
|
|||
AcceptedWars.Remove(w);
|
||||
|
||||
if (g == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (status != WarStatus.Draw)
|
||||
{
|
||||
status = (WarStatus)((int)status + 1 % 2);
|
||||
}
|
||||
|
||||
if (otherInAlliance)
|
||||
{
|
||||
|
|
@ -980,12 +1105,16 @@ namespace Server.Guilds
|
|||
public static void HandleDeath(Mobile victim, Mobile killer = null)
|
||||
{
|
||||
if (!NewGuildSystem)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
killer ??= victim.FindMostRecentDamager(false);
|
||||
|
||||
if (killer?.Guild == null || victim.Guild == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var victimGuild = GetAllianceLeader(victim.Guild as Guild);
|
||||
var killerGuild = GetAllianceLeader(killer.Guild as Guild);
|
||||
|
|
@ -993,14 +1122,20 @@ namespace Server.Guilds
|
|||
var war = killerGuild.FindActiveWar(victimGuild);
|
||||
|
||||
if (war == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
war.Kills++;
|
||||
|
||||
if (war.Opponent == victimGuild)
|
||||
{
|
||||
killerGuild.CheckExpiredWars();
|
||||
}
|
||||
else
|
||||
{
|
||||
victimGuild.CheckExpiredWars();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsMember(Mobile m) => Members.Contains(m);
|
||||
|
|
@ -1014,10 +1149,14 @@ namespace Server.Guilds
|
|||
public bool IsWar(Guild g)
|
||||
{
|
||||
if (g == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!NewGuildSystem)
|
||||
{
|
||||
return Enemies.Contains(g);
|
||||
}
|
||||
|
||||
var guild = GetAllianceLeader(this);
|
||||
var otherGuild = GetAllianceLeader(g);
|
||||
|
|
@ -1028,7 +1167,9 @@ namespace Server.Guilds
|
|||
public override void Serialize(IGenericWriter writer)
|
||||
{
|
||||
if (LastFealty + TimeSpan.FromDays(1.0) < DateTime.UtcNow)
|
||||
{
|
||||
CalculateGuildmaster();
|
||||
}
|
||||
|
||||
CheckExpiredWars();
|
||||
|
||||
|
|
@ -1038,19 +1179,29 @@ namespace Server.Guilds
|
|||
|
||||
writer.Write(PendingWars.Count);
|
||||
|
||||
for (var i = 0; i < PendingWars.Count; i++) PendingWars[i].Serialize(writer);
|
||||
for (var i = 0; i < PendingWars.Count; i++)
|
||||
{
|
||||
PendingWars[i].Serialize(writer);
|
||||
}
|
||||
|
||||
writer.Write(AcceptedWars.Count);
|
||||
|
||||
for (var i = 0; i < AcceptedWars.Count; i++) AcceptedWars[i].Serialize(writer);
|
||||
for (var i = 0; i < AcceptedWars.Count; i++)
|
||||
{
|
||||
AcceptedWars[i].Serialize(writer);
|
||||
}
|
||||
|
||||
var isAllianceLeader = m_AllianceLeader == null && m_AllianceInfo != null;
|
||||
writer.Write(isAllianceLeader);
|
||||
|
||||
if (isAllianceLeader)
|
||||
{
|
||||
m_AllianceInfo.Serialize(writer);
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write(m_AllianceLeader);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
|
|
@ -1094,18 +1245,28 @@ namespace Server.Guilds
|
|||
var count = reader.ReadInt();
|
||||
|
||||
PendingWars = new List<WarDeclaration>();
|
||||
for (var i = 0; i < count; i++) PendingWars.Add(new WarDeclaration(reader));
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
PendingWars.Add(new WarDeclaration(reader));
|
||||
}
|
||||
|
||||
count = reader.ReadInt();
|
||||
AcceptedWars = new List<WarDeclaration>();
|
||||
for (var i = 0; i < count; i++) AcceptedWars.Add(new WarDeclaration(reader));
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
AcceptedWars.Add(new WarDeclaration(reader));
|
||||
}
|
||||
|
||||
var isAllianceLeader = reader.ReadBool();
|
||||
|
||||
if (isAllianceLeader)
|
||||
{
|
||||
m_AllianceInfo = new AllianceInfo(reader);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_AllianceLeader = reader.ReadGuild() as Guild;
|
||||
}
|
||||
|
||||
goto case 4;
|
||||
}
|
||||
|
|
@ -1139,7 +1300,9 @@ namespace Server.Guilds
|
|||
m_Leader = reader.ReadMobile();
|
||||
|
||||
if (m_Leader is PlayerMobile mobile)
|
||||
{
|
||||
mobile.GuildRank = RankDefinition.Leader;
|
||||
}
|
||||
|
||||
m_Name = reader.ReadString();
|
||||
m_Abbreviation = reader.ReadString();
|
||||
|
|
@ -1174,7 +1337,9 @@ namespace Server.Guilds
|
|||
private void VerifyGuild_Callback()
|
||||
{
|
||||
if (!NewGuildSystem && Guildstone == null || Members.Count == 0)
|
||||
{
|
||||
Disband();
|
||||
}
|
||||
|
||||
CheckExpiredWars();
|
||||
|
||||
|
|
@ -1186,7 +1351,9 @@ namespace Server.Guilds
|
|||
|
||||
if (alliance?.IsMember(this) == false && !alliance.IsPendingMember(this)
|
||||
) // This block is there to fix a bug in the code in an older version.
|
||||
{
|
||||
Alliance = null; // Will call Alliance.RemoveGuild which will set it null & perform all the pertient checks as far as alliacne disbanding
|
||||
}
|
||||
}
|
||||
|
||||
public void AddMember(Mobile m)
|
||||
|
|
@ -1194,7 +1361,9 @@ namespace Server.Guilds
|
|||
if (!Members.Contains(m))
|
||||
{
|
||||
if (m.Guild != null && m.Guild != this)
|
||||
{
|
||||
((Guild)m.Guild).RemoveMember(m);
|
||||
}
|
||||
|
||||
Members.Add(m);
|
||||
m.Guild = this;
|
||||
|
|
@ -1202,7 +1371,9 @@ namespace Server.Guilds
|
|||
m.GuildFealty = !NewGuildSystem ? m_Leader : null;
|
||||
|
||||
if (m is PlayerMobile mobile)
|
||||
{
|
||||
mobile.GuildRank = RankDefinition.Lowest;
|
||||
}
|
||||
|
||||
((Guild)m.Guild).InvalidateWarNotoriety();
|
||||
}
|
||||
|
|
@ -1219,21 +1390,29 @@ namespace Server.Guilds
|
|||
m.Guild = null;
|
||||
|
||||
if (m is PlayerMobile mobile)
|
||||
{
|
||||
mobile.GuildRank = RankDefinition.Lowest;
|
||||
}
|
||||
|
||||
if (message > 0)
|
||||
{
|
||||
m.SendLocalizedMessage(message);
|
||||
}
|
||||
|
||||
if (m == m_Leader)
|
||||
{
|
||||
CalculateGuildmaster();
|
||||
|
||||
if (m_Leader == null)
|
||||
{
|
||||
Disband();
|
||||
}
|
||||
}
|
||||
|
||||
if (Members.Count == 0)
|
||||
{
|
||||
Disband();
|
||||
}
|
||||
|
||||
guild?.InvalidateWarNotoriety();
|
||||
|
||||
|
|
@ -1289,19 +1468,25 @@ namespace Server.Guilds
|
|||
public void GuildMessage(int number)
|
||||
{
|
||||
for (var i = 0; i < Members.Count; ++i)
|
||||
{
|
||||
Members[i].SendLocalizedMessage(number);
|
||||
}
|
||||
}
|
||||
|
||||
public void GuildMessage(int number, string args, int hue = 0x3B2)
|
||||
{
|
||||
for (var i = 0; i < Members.Count; ++i)
|
||||
{
|
||||
Members[i].SendLocalizedMessage(number, args, hue);
|
||||
}
|
||||
}
|
||||
|
||||
public void GuildMessage(int number, bool append, string affix, string args = "", int hue = 0x3B2)
|
||||
{
|
||||
for (var i = 0; i < Members.Count; ++i)
|
||||
{
|
||||
Members[i].SendLocalizedMessage(number, append, affix, args, hue);
|
||||
}
|
||||
}
|
||||
|
||||
public void GuildTextMessage(string text)
|
||||
|
|
@ -1317,7 +1502,9 @@ namespace Server.Guilds
|
|||
public void GuildTextMessage(int hue, string text)
|
||||
{
|
||||
for (var i = 0; i < Members.Count; ++i)
|
||||
{
|
||||
Members[i].SendMessage(hue, text);
|
||||
}
|
||||
}
|
||||
|
||||
public void GuildTextMessage(int hue, string format, params object[] args)
|
||||
|
|
@ -1371,20 +1558,28 @@ namespace Server.Guilds
|
|||
var memb = Members[i];
|
||||
|
||||
if (!CanVote(memb))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var m = memb.GuildFealty;
|
||||
|
||||
if (!CanBeVotedFor(m))
|
||||
{
|
||||
if (!Disbanded && m_Leader.Guild == this)
|
||||
{
|
||||
m = m_Leader;
|
||||
}
|
||||
else
|
||||
{
|
||||
m = memb;
|
||||
}
|
||||
}
|
||||
|
||||
if (m == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
votes[m] = 1 + (votes.TryGetValue(m, out var v) ? v : 0);
|
||||
votingMembers++;
|
||||
|
|
@ -1407,10 +1602,14 @@ namespace Server.Guilds
|
|||
|
||||
if (NewGuildSystem && highVotes * 100 / Math.Max(votingMembers, 1) < MajorityPercentage && !Disbanded &&
|
||||
winner != m_Leader && m_Leader.Guild == this)
|
||||
{
|
||||
winner = m_Leader;
|
||||
}
|
||||
|
||||
if (m_Leader != winner && winner != null)
|
||||
{
|
||||
GuildMessage(1018015, true, winner.Name); // Guild Message: Guildmaster changed to:
|
||||
}
|
||||
|
||||
Leader = winner;
|
||||
LastFealty = DateTime.UtcNow;
|
||||
|
|
@ -1456,7 +1655,9 @@ namespace Server.Guilds
|
|||
from.SendGump(new PropertiesGump(from, g));
|
||||
|
||||
if (NewGuildSystem && from.AccessLevel >= AccessLevel.GameMaster && from is PlayerMobile pm)
|
||||
{
|
||||
pm.SendGump(new GuildInfoGump(pm, g));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -288,7 +288,9 @@ namespace Server.Misc
|
|||
m_Keywords = value;
|
||||
m_KeywordHash = new Dictionary<string, string>(m_Keywords.Length, StringComparer.OrdinalIgnoreCase);
|
||||
for (var i = 0; i < m_Keywords.Length; ++i)
|
||||
{
|
||||
m_KeywordHash[m_Keywords[i]] = m_Keywords[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -307,7 +309,9 @@ namespace Server.Misc
|
|||
var syllables = new string[syllableCount];
|
||||
|
||||
for (var i = 0; i < syllableCount; ++i)
|
||||
{
|
||||
syllables[i] = GetRandomSyllable();
|
||||
}
|
||||
|
||||
return string.Concat(syllables);
|
||||
}
|
||||
|
|
@ -333,33 +337,47 @@ namespace Server.Misc
|
|||
needUpperCase = true;
|
||||
|
||||
if (random > 13)
|
||||
{
|
||||
sentance.Append("! ");
|
||||
}
|
||||
else
|
||||
{
|
||||
sentance.Append(". ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int syllableCount;
|
||||
|
||||
if (Utility.Random(100) < 30)
|
||||
{
|
||||
syllableCount = Utility.Random(1, 5);
|
||||
}
|
||||
else
|
||||
{
|
||||
syllableCount = Utility.Random(1, 3);
|
||||
}
|
||||
|
||||
var word = ConstructWord(syllableCount);
|
||||
|
||||
sentance.Append(word);
|
||||
|
||||
if (needUpperCase)
|
||||
{
|
||||
sentance.Replace(word[0], char.ToUpper(word[0]), sentance.Length - word.Length, 1);
|
||||
}
|
||||
|
||||
needUpperCase = false;
|
||||
}
|
||||
|
||||
if (Utility.RandomMinMax(1, 5) == 1)
|
||||
{
|
||||
sentance.Append('!');
|
||||
}
|
||||
else
|
||||
{
|
||||
sentance.Append('.');
|
||||
}
|
||||
|
||||
return sentance.ToString();
|
||||
}
|
||||
|
|
@ -380,35 +398,53 @@ namespace Server.Misc
|
|||
public bool OnSpeech(Mobile mob, Mobile speaker, string text)
|
||||
{
|
||||
if ((Flags & IHSFlags.OnSpeech) == 0 || m_Keywords == null || Responses == null || m_KeywordHash == null)
|
||||
{
|
||||
return false; // not enabled
|
||||
}
|
||||
|
||||
if (!speaker.Alive)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!speaker.InRange(mob, 3))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((speaker.Direction & Direction.Mask) != speaker.GetDirectionTo(mob))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((mob.Direction & Direction.Mask) != mob.GetDirectionTo(speaker))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var split = text.Split(' ');
|
||||
var keywordsFound = new List<string>();
|
||||
|
||||
for (var i = 0; i < split.Length; ++i)
|
||||
{
|
||||
if (m_KeywordHash.TryGetValue(split[i], out var keyword))
|
||||
{
|
||||
keywordsFound.Add(keyword);
|
||||
}
|
||||
}
|
||||
|
||||
if (keywordsFound.Count > 0)
|
||||
{
|
||||
string responseWord;
|
||||
|
||||
if (Utility.RandomBool())
|
||||
{
|
||||
responseWord = GetRandomResponseWord(keywordsFound);
|
||||
}
|
||||
else
|
||||
{
|
||||
responseWord = keywordsFound.RandomElement();
|
||||
}
|
||||
|
||||
var secondResponseWord = GetRandomResponseWord(keywordsFound);
|
||||
|
||||
|
|
@ -469,9 +505,13 @@ namespace Server.Misc
|
|||
var maxWords = split.Length / 2 + 1;
|
||||
|
||||
if (maxWords < 2)
|
||||
{
|
||||
maxWords = 2;
|
||||
}
|
||||
else if (maxWords > 6)
|
||||
{
|
||||
maxWords = 6;
|
||||
}
|
||||
|
||||
SaySentance(mob, Utility.RandomMinMax(2, maxWords));
|
||||
mob.Say(response.ToString());
|
||||
|
|
@ -485,10 +525,14 @@ namespace Server.Misc
|
|||
public void OnDeath(Mobile mob)
|
||||
{
|
||||
if ((Flags & IHSFlags.OnDeath) == 0)
|
||||
{
|
||||
return; // not enabled
|
||||
}
|
||||
|
||||
if (Utility.Random(100) < 90)
|
||||
{
|
||||
return; // 90% chance to do nothing; 10% chance to talk
|
||||
}
|
||||
|
||||
SayRandomTranslate(
|
||||
mob,
|
||||
|
|
@ -506,16 +550,24 @@ namespace Server.Misc
|
|||
public void OnMovement(Mobile mob, Mobile mover, Point3D oldLocation)
|
||||
{
|
||||
if ((Flags & IHSFlags.OnMovement) == 0)
|
||||
{
|
||||
return; // not enabled
|
||||
}
|
||||
|
||||
if (!mover.Player || mover.Hidden && mover.AccessLevel > AccessLevel.Player)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mob.InRange(mover, 5) || mob.InRange(oldLocation, 5))
|
||||
{
|
||||
return; // only talk when they enter 5 tile range
|
||||
}
|
||||
|
||||
if (Utility.Random(100) < 90)
|
||||
{
|
||||
return; // 90% chance to do nothing; 10% chance to talk
|
||||
}
|
||||
|
||||
SaySentance(mob, 6);
|
||||
}
|
||||
|
|
@ -523,12 +575,17 @@ namespace Server.Misc
|
|||
public void OnDamage(Mobile mob, int amount)
|
||||
{
|
||||
if ((Flags & IHSFlags.OnDamaged) == 0)
|
||||
{
|
||||
return; // not enabled
|
||||
}
|
||||
|
||||
if (Utility.Random(100) < 90)
|
||||
{
|
||||
return; // 90% chance to do nothing; 10% chance to talk
|
||||
}
|
||||
|
||||
if (amount < 5)
|
||||
{
|
||||
SayRandomTranslate(
|
||||
mob,
|
||||
"Ouch!",
|
||||
|
|
@ -537,7 +594,9 @@ namespace Server.Misc
|
|||
"Thy blows soft!",
|
||||
"You bad with weapon!"
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
SayRandomTranslate(
|
||||
mob,
|
||||
"Ouch! Me hurt!",
|
||||
|
|
@ -548,6 +607,7 @@ namespace Server.Misc
|
|||
"Aaah! That hurt...",
|
||||
"Good blow!"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnConstruct(Mobile mob)
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@ namespace Server
|
|||
if (m_Table == null)
|
||||
LoadTable();
|
||||
|
||||
return m_Table.ContainsKey(obj.GetType());
|
||||
return m_Table!.ContainsKey(obj.GetType());
|
||||
}
|
||||
|
||||
public static bool GetSpeeds(object obj, ref double activeSpeed, ref double passiveSpeed)
|
||||
|
|
@ -190,7 +190,7 @@ namespace Server
|
|||
if (m_Table == null)
|
||||
LoadTable();
|
||||
|
||||
if (!m_Table.TryGetValue(obj.GetType(), out var sp))
|
||||
if (!m_Table!.TryGetValue(obj.GetType(), out var sp))
|
||||
return false;
|
||||
|
||||
activeSpeed = sp.ActiveSpeed;
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ namespace Server.Mobiles
|
|||
|
||||
public static void StopEffect(Mobile m, bool message)
|
||||
{
|
||||
if (m_Table.TryGetValue(m, out var timer))
|
||||
if (m_Table.Remove(m, out var timer))
|
||||
{
|
||||
if (message)
|
||||
m.PublicOverheadMessage(
|
||||
|
|
@ -157,7 +157,6 @@ namespace Server.Mobiles
|
|||
);
|
||||
|
||||
timer.Stop();
|
||||
m_Table.Remove(m);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -120,10 +120,9 @@ namespace Server.Mobiles
|
|||
|
||||
public static void EndLifeDrain(Mobile m)
|
||||
{
|
||||
if (m_Table.TryGetValue(m, out var timer))
|
||||
if (m_Table.Remove(m, out var timer))
|
||||
{
|
||||
timer?.Stop();
|
||||
m_Table.Remove(m);
|
||||
m.SendLocalizedMessage(1070849); // The drain on your life force is gone.
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1050,7 +1050,7 @@ namespace Server.Mobiles
|
|||
|
||||
if (from.UseSkill(skillId))
|
||||
{
|
||||
@from.Target?.Invoke(@from, target);
|
||||
from.Target?.Invoke(from, target);
|
||||
}
|
||||
|
||||
if (skillId == 35)
|
||||
|
|
@ -1294,7 +1294,7 @@ namespace Server.Mobiles
|
|||
|
||||
if (from.NetState != null)
|
||||
{
|
||||
Timer.DelayCall(TimeSpan.FromSeconds(1.0), @from.NetState.Dispose);
|
||||
Timer.DelayCall(TimeSpan.FromSeconds(1.0), from.NetState.Dispose);
|
||||
}
|
||||
}
|
||||
else if (from.AccessLevel >= AccessLevel.Administrator)
|
||||
|
|
@ -1470,11 +1470,11 @@ namespace Server.Mobiles
|
|||
|
||||
if (armor is BaseShield)
|
||||
{
|
||||
@from.SendLocalizedMessage(1062003, name); // You can no longer equip your ~1_SHIELD~
|
||||
from.SendLocalizedMessage(1062003, name); // You can no longer equip your ~1_SHIELD~
|
||||
}
|
||||
else
|
||||
{
|
||||
@from.SendLocalizedMessage(1062002, name); // You can no longer wear your ~1_ARMOR~
|
||||
from.SendLocalizedMessage(1062002, name); // You can no longer wear your ~1_ARMOR~
|
||||
}
|
||||
|
||||
from.AddToBackpack(armor);
|
||||
|
|
@ -1547,7 +1547,7 @@ namespace Server.Mobiles
|
|||
|
||||
if (moved)
|
||||
{
|
||||
@from.SendLocalizedMessage(500647); // Some equipment has been moved to your backpack.
|
||||
from.SendLocalizedMessage(500647); // Some equipment has been moved to your backpack.
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
|
|
@ -2011,7 +2011,7 @@ namespace Server.Mobiles
|
|||
{
|
||||
if (Core.TOL && from.InRange(this, 2))
|
||||
{
|
||||
list.Add(new CallbackEntry(1077728, () => OpenTrade(@from))); // Trade
|
||||
list.Add(new CallbackEntry(1077728, () => OpenTrade(from))); // Trade
|
||||
}
|
||||
|
||||
if (Alive && Core.Expansion >= Expansion.AOS)
|
||||
|
|
@ -2027,11 +2027,11 @@ namespace Server.Mobiles
|
|||
{
|
||||
if (ourParty == null)
|
||||
{
|
||||
list.Add(new AddToPartyEntry(@from, this));
|
||||
list.Add(new AddToPartyEntry(from, this));
|
||||
}
|
||||
else if (ourParty == theirParty)
|
||||
{
|
||||
list.Add(new RemoveFromPartyEntry(@from, this));
|
||||
list.Add(new RemoveFromPartyEntry(from, this));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2041,7 +2041,7 @@ namespace Server.Mobiles
|
|||
if (curhouse != null && Alive && Core.Expansion >= Expansion.AOS && curhouse.IsAosRules &&
|
||||
curhouse.IsFriend(from))
|
||||
{
|
||||
list.Add(new EjectPlayerEntry(@from, this));
|
||||
list.Add(new EjectPlayerEntry(from, this));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2902,11 +2902,11 @@ namespace Server.Mobiles
|
|||
|
||||
if (Core.ML)
|
||||
{
|
||||
@from.Damage((int)(amount * (1 - (@from.Skills.MagicResist.Value * .5 + 10) / 100)), this);
|
||||
from.Damage((int)(amount * (1 - (from.Skills.MagicResist.Value * .5 + 10) / 100)), this);
|
||||
}
|
||||
else
|
||||
{
|
||||
@from.Damage(amount, this);
|
||||
from.Damage(amount, this);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2916,7 +2916,7 @@ namespace Server.Mobiles
|
|||
{
|
||||
var type = talisman.Protection.Type;
|
||||
|
||||
if (type.IsInstanceOfType(@from))
|
||||
if (type.IsInstanceOfType(from))
|
||||
{
|
||||
amount = (int)(amount * (1 - (double)talisman.Protection.Amount / 100));
|
||||
}
|
||||
|
|
@ -4188,7 +4188,7 @@ namespace Server.Mobiles
|
|||
|
||||
if (from != null && result == ApplyPoisonResult.Poisoned && PoisonTimer is PoisonImpl.PoisonTimer timer)
|
||||
{
|
||||
timer.From = @from;
|
||||
timer.From = from;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
@ -4207,7 +4207,7 @@ namespace Server.Mobiles
|
|||
}
|
||||
else
|
||||
{
|
||||
base.OnPoisonImmunity(@from, poison);
|
||||
base.OnPoisonImmunity(from, poison);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4685,20 +4685,16 @@ namespace Server.Mobiles
|
|||
|
||||
public void RemoveBuff(BuffIcon b)
|
||||
{
|
||||
if (m_BuffTable?.ContainsKey(b) != true)
|
||||
if (!m_BuffTable.Remove(b, out var info))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var info = m_BuffTable[b];
|
||||
|
||||
if (info.Timer?.Running == true)
|
||||
{
|
||||
info.Timer.Stop();
|
||||
}
|
||||
|
||||
m_BuffTable.Remove(b);
|
||||
|
||||
if (NetState?.BuffIcon == true)
|
||||
{
|
||||
NetState.Send(new RemoveBuffPacket(this, b));
|
||||
|
|
|
|||
|
|
@ -149,11 +149,13 @@ namespace Server.Mobiles
|
|||
var okay = true;
|
||||
|
||||
foreach (var obj in quest.Objectives)
|
||||
{
|
||||
if (obj is EscortObjective objective && objective.Destination.Contains(reg))
|
||||
{
|
||||
okay = false; // We're already there!
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (okay)
|
||||
{
|
||||
|
|
@ -175,7 +177,9 @@ namespace Server.Mobiles
|
|||
if (m_MLQuest == null)
|
||||
{
|
||||
if (MLQuestSystem.Debug)
|
||||
{
|
||||
Console.WriteLine("Warning: No suitable quest found for escort {0}", Serial);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
|
@ -232,7 +236,9 @@ namespace Server.Mobiles
|
|||
var dest = GetDestination();
|
||||
|
||||
if (dest == null || !m.Alive)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var escorter = GetEscorter();
|
||||
|
||||
|
|
@ -262,10 +268,14 @@ namespace Server.Mobiles
|
|||
var dest = GetDestination();
|
||||
|
||||
if (dest == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (GetEscorter() != null || !m.Alive)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (EscortTable.TryGetValue(m, out var escortable) && escortable?.Deleted == false &&
|
||||
escortable.GetEscorter() == m)
|
||||
|
|
@ -288,7 +298,9 @@ namespace Server.Mobiles
|
|||
m_LastSeenEscorter = DateTime.UtcNow;
|
||||
|
||||
if (m is PlayerMobile playerMobile)
|
||||
{
|
||||
playerMobile.LastEscortTime = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
Say(
|
||||
"Lead on! Payment will be made when we arrive in {0}.",
|
||||
|
|
@ -310,12 +322,18 @@ namespace Server.Mobiles
|
|||
base.OnSpeech(e);
|
||||
|
||||
if (GetDestination() == null || e.Handled || !e.Mobile.InRange(Location, 3))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.HasKeyword(0x1D)) // *destination*
|
||||
{
|
||||
e.Handled = SayDestinationTo(e.Mobile);
|
||||
}
|
||||
else if (e.HasKeyword(0x1E)) // *i will take thee*
|
||||
{
|
||||
e.Handled = AcceptEscorter(e.Mobile);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnAfterDelete()
|
||||
|
|
@ -336,7 +354,9 @@ namespace Server.Mobiles
|
|||
protected override bool OnMove(Direction d)
|
||||
{
|
||||
if (!base.OnMove(d))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CheckAtDestination();
|
||||
|
||||
|
|
@ -353,7 +373,9 @@ namespace Server.Mobiles
|
|||
public virtual void StartFollow(Mobile escorter)
|
||||
{
|
||||
if (escorter == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ActiveSpeed = 0.1;
|
||||
PassiveSpeed = 0.2;
|
||||
|
|
@ -361,7 +383,11 @@ namespace Server.Mobiles
|
|||
ControlOrder = OrderType.Follow;
|
||||
ControlTarget = escorter;
|
||||
|
||||
if (IsPrisoner && CantWalk) CantWalk = false;
|
||||
if (IsPrisoner && CantWalk)
|
||||
{
|
||||
CantWalk = false;
|
||||
}
|
||||
|
||||
CurrentSpeed = 0.1;
|
||||
}
|
||||
|
||||
|
|
@ -379,12 +405,16 @@ namespace Server.Mobiles
|
|||
public virtual Mobile GetEscorter()
|
||||
{
|
||||
if (!Controlled)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var master = ControlMaster;
|
||||
|
||||
if (MLQuestSystem.Enabled || master == null)
|
||||
{
|
||||
return master;
|
||||
}
|
||||
|
||||
if (master.Deleted || master.Map != Map || !master.InRange(Location, 30) || !master.Alive)
|
||||
{
|
||||
|
|
@ -409,7 +439,9 @@ namespace Server.Mobiles
|
|||
}
|
||||
|
||||
if (ControlOrder != OrderType.Follow)
|
||||
{
|
||||
StartFollow(master);
|
||||
}
|
||||
|
||||
m_LastSeenEscorter = DateTime.UtcNow;
|
||||
return master;
|
||||
|
|
@ -428,17 +460,23 @@ namespace Server.Mobiles
|
|||
public virtual bool CheckAtDestination()
|
||||
{
|
||||
if (MLQuestSystem.Enabled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var dest = GetDestination();
|
||||
|
||||
if (dest == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var escorter = GetEscorter();
|
||||
|
||||
if (escorter == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dest.Contains(Location))
|
||||
{
|
||||
|
|
@ -456,7 +494,9 @@ namespace Server.Mobiles
|
|||
var gold = new Gold(500, 1000);
|
||||
|
||||
if (!cont.TryDropItem(escorter, gold, false))
|
||||
{
|
||||
gold.MoveToWorld(escorter.Location, escorter.Map);
|
||||
}
|
||||
|
||||
StopFollow();
|
||||
SetControlMaster(null);
|
||||
|
|
@ -484,18 +524,24 @@ namespace Server.Mobiles
|
|||
else if (VirtueHelper.Award(pm, VirtueName.Compassion, IsPrisoner ? 400 : 200, ref gainedPath))
|
||||
{
|
||||
if (gainedPath)
|
||||
{
|
||||
pm.SendLocalizedMessage(1053005); // You have achieved a path in compassion!
|
||||
}
|
||||
else
|
||||
{
|
||||
pm.SendLocalizedMessage(1053002); // You have gained in compassion.
|
||||
}
|
||||
|
||||
pm.NextCompassionDay =
|
||||
DateTime.UtcNow + TimeSpan.FromDays(1.0); // in one day CompassionGains gets reset to 0
|
||||
++pm.CompassionGains;
|
||||
|
||||
if (pm.CompassionGains >= 5)
|
||||
{
|
||||
pm.SendLocalizedMessage(
|
||||
1053004
|
||||
); // You must wait about a day before you can gain in compassion again.
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -529,12 +575,16 @@ namespace Server.Mobiles
|
|||
writer.Write(dest != null);
|
||||
|
||||
if (dest != null)
|
||||
{
|
||||
writer.Write(dest.Name);
|
||||
}
|
||||
|
||||
writer.Write(m_DeleteTimer != null);
|
||||
|
||||
if (m_DeleteTimer != null)
|
||||
{
|
||||
writer.WriteDeltaTime(m_DeleteTime);
|
||||
}
|
||||
|
||||
MLQuestSystem.WriteQuestRef(writer, StaticMLQuester ? null : m_MLQuest);
|
||||
}
|
||||
|
|
@ -546,8 +596,10 @@ namespace Server.Mobiles
|
|||
var version = reader.ReadInt();
|
||||
|
||||
if (reader.ReadBool())
|
||||
{
|
||||
m_DestinationString =
|
||||
reader.ReadString(); // NOTE: We cannot EDI.Find here, regions have not yet been loaded :-(
|
||||
}
|
||||
|
||||
if (reader.ReadBool())
|
||||
{
|
||||
|
|
@ -561,7 +613,9 @@ namespace Server.Mobiles
|
|||
var quest = MLQuestSystem.ReadQuestRef(reader);
|
||||
|
||||
if (MLQuestSystem.Enabled && quest != null && !StaticMLQuester)
|
||||
{
|
||||
m_MLQuest = quest;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -576,14 +630,20 @@ namespace Server.Mobiles
|
|||
if (!MLQuestSystem.Enabled && GetDestination() != null)
|
||||
{
|
||||
if (escorter == null || escorter == from)
|
||||
{
|
||||
list.Add(new AskDestinationEntry(this, from));
|
||||
}
|
||||
|
||||
if (escorter == null)
|
||||
{
|
||||
list.Add(new AcceptEscortEntry(this, from));
|
||||
}
|
||||
}
|
||||
|
||||
if (escorter == from)
|
||||
{
|
||||
list.Add(new AbandonEscortEntry(this));
|
||||
}
|
||||
}
|
||||
|
||||
base.AddCustomContextEntries(from, list);
|
||||
|
|
@ -594,7 +654,9 @@ namespace Server.Mobiles
|
|||
public virtual string PickRandomDestination()
|
||||
{
|
||||
if (Map.Felucca.Regions.Count == 0 || Map == null || Map == Map.Internal || Location == Point3D.Zero)
|
||||
{
|
||||
return null; // Not yet fully initialized
|
||||
}
|
||||
|
||||
var possible = GetPossibleDestinations();
|
||||
string picked = null;
|
||||
|
|
@ -605,7 +667,9 @@ namespace Server.Mobiles
|
|||
var test = EDI.Find(picked);
|
||||
|
||||
if (test.Contains(Location))
|
||||
{
|
||||
picked = null;
|
||||
}
|
||||
}
|
||||
|
||||
return picked;
|
||||
|
|
@ -614,16 +678,24 @@ namespace Server.Mobiles
|
|||
public EDI GetDestination()
|
||||
{
|
||||
if (MLQuestSystem.Enabled)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (m_DestinationString == null && m_DeleteTimer == null)
|
||||
{
|
||||
m_DestinationString = PickRandomDestination();
|
||||
}
|
||||
|
||||
if (m_Destination != null && m_Destination.Name == m_DestinationString)
|
||||
{
|
||||
return m_Destination;
|
||||
}
|
||||
|
||||
if (Map.Felucca.Regions.Count > 0)
|
||||
{
|
||||
return m_Destination = EDI.Find(m_DestinationString);
|
||||
}
|
||||
|
||||
return m_Destination = null;
|
||||
}
|
||||
|
|
@ -668,22 +740,32 @@ namespace Server.Mobiles
|
|||
ICollection list = Map.Felucca.Regions.Values;
|
||||
|
||||
if (list.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_Table = new Dictionary<string, EscortDestinationInfo>();
|
||||
|
||||
foreach (Region r in list)
|
||||
{
|
||||
if (r.Name != null && (r is DungeonRegion || r is TownRegion))
|
||||
{
|
||||
m_Table[r.Name] = new EscortDestinationInfo(r.Name, r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static EDI Find(string name)
|
||||
{
|
||||
if (m_Table == null)
|
||||
{
|
||||
LoadTable();
|
||||
}
|
||||
|
||||
if (name == null || m_Table == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
m_Table.TryGetValue(name, out var info);
|
||||
return info;
|
||||
|
|
|
|||
|
|
@ -68,10 +68,15 @@ namespace Server.Multis
|
|||
{
|
||||
base.OnAfterDelete();
|
||||
|
||||
if (Fixtures == null) return;
|
||||
if (Fixtures == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var item in Fixtures)
|
||||
{
|
||||
item.Delete();
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnLocationChange(Point3D oldLocation)
|
||||
|
|
@ -82,20 +87,30 @@ namespace Server.Multis
|
|||
var y = Location.Y - oldLocation.Y;
|
||||
var z = Location.Z - oldLocation.Z;
|
||||
|
||||
if (Fixtures == null) return;
|
||||
if (Fixtures == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var item in Fixtures)
|
||||
{
|
||||
item.Location = new Point3D(item.X + x, item.Y + y, item.Z + z);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnMapChange()
|
||||
{
|
||||
base.OnMapChange();
|
||||
|
||||
if (Fixtures == null) return;
|
||||
if (Fixtures == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var item in Fixtures)
|
||||
{
|
||||
item.Map = Map;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddTeleporters(int id, Point3D offset1, Point3D offset2)
|
||||
|
|
@ -131,12 +146,17 @@ namespace Server.Multis
|
|||
|
||||
foreach (var entry in components.List.Where(e => e.Flags == 0))
|
||||
// Teleporters
|
||||
{
|
||||
if (entry.ItemId >= 0x181D && entry.ItemId <= 0x1828)
|
||||
{
|
||||
if (teleporters.ContainsKey(entry.ItemId))
|
||||
teleporters[entry.ItemId].Add(entry);
|
||||
if (teleporters.TryGetValue(entry.ItemId, out var result))
|
||||
{
|
||||
result.Add(entry);
|
||||
}
|
||||
else
|
||||
teleporters[entry.ItemId] = new List<MultiTileEntry> { entry };
|
||||
{
|
||||
teleporters.Add(entry.ItemId, new List<MultiTileEntry> { entry });
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -155,14 +175,19 @@ namespace Server.Multis
|
|||
AddFixture(st);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var door in Doors)
|
||||
{
|
||||
foreach (var check in Doors.Where(d => d != door))
|
||||
{
|
||||
if (door.InRange(check.Location, 1))
|
||||
{
|
||||
door.Link = check;
|
||||
check.Link = door;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var (key, value) in teleporters)
|
||||
{
|
||||
|
|
@ -195,9 +220,13 @@ namespace Server.Multis
|
|||
writer.Write((int)HouseType);
|
||||
|
||||
if (Fixtures != null)
|
||||
{
|
||||
writer.WriteItemList(Fixtures, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write(0);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Deserialize(IGenericReader reader)
|
||||
|
|
@ -210,7 +239,9 @@ namespace Server.Multis
|
|||
var count = reader.ReadInt();
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
AddFixture(reader.ReadItem());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -311,10 +311,9 @@ namespace Server.Regions
|
|||
if (IsGuardCandidate(m) &&
|
||||
(!AllowReds && m.Kills >= 5 && m.Region.IsPartOf(this) || m_GuardCandidates.ContainsKey(m)))
|
||||
{
|
||||
if (m_GuardCandidates.TryGetValue(m, out var timer))
|
||||
if (m_GuardCandidates.Remove(m, out var timer))
|
||||
{
|
||||
timer.Stop();
|
||||
m_GuardCandidates.Remove(m);
|
||||
}
|
||||
|
||||
MakeGuard(m);
|
||||
|
|
|
|||
|
|
@ -36,16 +36,16 @@ namespace Server.SkillHandlers
|
|||
|
||||
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)
|
||||
if (!m_Table.Remove(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;
|
||||
}
|
||||
|
||||
|
|
@ -108,7 +108,9 @@ namespace Server.SkillHandlers
|
|||
public override void OnResponse(NetState state, RelayInfo info)
|
||||
{
|
||||
if (info.ButtonID >= 1 && info.ButtonID <= 4)
|
||||
{
|
||||
TrackWhoGump.DisplayTo(m_Success, m_From, info.ButtonID - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -166,7 +168,9 @@ namespace Server.SkillHandlers
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -181,7 +185,9 @@ namespace Server.SkillHandlers
|
|||
var map = from.Map;
|
||||
|
||||
if (map == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var check = m_Delegates[type];
|
||||
|
||||
|
|
@ -207,11 +213,17 @@ namespace Server.SkillHandlers
|
|||
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.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -219,13 +231,17 @@ namespace Server.SkillHandlers
|
|||
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;
|
||||
|
|
@ -233,19 +249,29 @@ namespace Server.SkillHandlers
|
|||
|
||||
// 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
|
||||
{
|
||||
|
|
@ -274,7 +300,9 @@ namespace Server.SkillHandlers
|
|||
m_From.QuestArrow = new TrackArrow(m_From, m, m_Range * 2);
|
||||
|
||||
if (Core.SE)
|
||||
{
|
||||
Tracking.AddInfo(m_From, m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -287,11 +315,19 @@ namespace Server.SkillHandlers
|
|||
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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,33 +34,37 @@ namespace Server.Misc
|
|||
m_MoveHistory = new Dictionary<Mobile, LocationInfo>();
|
||||
|
||||
if (Enabled)
|
||||
{
|
||||
EventSink.Login += OnLogin;
|
||||
}
|
||||
}
|
||||
|
||||
public static void OnLogin(Mobile from)
|
||||
{
|
||||
if (from == null || from.AccessLevel < AccessLevel.Counselor)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (HasDisconnected(from))
|
||||
{
|
||||
if (!m_MoveHistory.ContainsKey(from))
|
||||
{
|
||||
m_MoveHistory[from] = new LocationInfo(from.Location, from.Map);
|
||||
}
|
||||
|
||||
var dest = GetRandomDestination();
|
||||
|
||||
from.Location = dest.Location;
|
||||
from.Map = dest.Map;
|
||||
}
|
||||
else if (m_MoveHistory.TryGetValue(from, out var orig))
|
||||
else if (m_MoveHistory.Remove(from, out var orig))
|
||||
{
|
||||
from.SendMessage(
|
||||
"Your character was moved from {0} ({1}) due to a detected client crash.",
|
||||
orig.Location,
|
||||
orig.Map
|
||||
);
|
||||
|
||||
m_MoveHistory.Remove(from);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -82,7 +82,9 @@ namespace Server.Spells
|
|||
var scalar = 1.0;
|
||||
|
||||
if (!MindRotSpell.GetMindRotScalar(m, ref scalar))
|
||||
{
|
||||
scalar = 1.0;
|
||||
}
|
||||
|
||||
// Lower Mana Cost = 40%
|
||||
var lmc = Math.Min(AosAttributes.GetValue(m, AosAttribute.LowerManaCost), 40);
|
||||
|
|
@ -92,7 +94,9 @@ namespace Server.Spells
|
|||
var total = (int)(mana * scalar);
|
||||
|
||||
if (m.Skills[MoveSkill].Value < 50.0 && GetContext(m) != null)
|
||||
{
|
||||
total *= 2;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
|
@ -113,7 +117,9 @@ namespace Server.Spells
|
|||
if (consume)
|
||||
{
|
||||
if (!DelayedContext)
|
||||
{
|
||||
SetContext(from);
|
||||
}
|
||||
|
||||
from.Mana -= mana;
|
||||
}
|
||||
|
|
@ -124,6 +130,7 @@ namespace Server.Spells
|
|||
public virtual void SetContext(Mobile from)
|
||||
{
|
||||
if (GetContext(from) == null)
|
||||
{
|
||||
if (DelayedContext || from.Skills[MoveSkill].Value < 50.0)
|
||||
{
|
||||
Timer timer = new SpecialMoveTimer(from);
|
||||
|
|
@ -131,12 +138,15 @@ namespace Server.Spells
|
|||
|
||||
AddContext(from, new SpecialMoveContext(timer, GetType()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool Validate(Mobile from)
|
||||
{
|
||||
if (!from.Player)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (HonorableExecution.IsUnderPenalty(from))
|
||||
{
|
||||
|
|
@ -153,24 +163,42 @@ namespace Server.Spells
|
|||
string option = null;
|
||||
|
||||
if (this is Backstab)
|
||||
{
|
||||
option = "Backstab";
|
||||
}
|
||||
else if (this is DeathStrike)
|
||||
{
|
||||
option = "Death Strike";
|
||||
}
|
||||
else if (this is FocusAttack)
|
||||
{
|
||||
option = "Focus Attack";
|
||||
}
|
||||
else if (this is KiAttack)
|
||||
{
|
||||
option = "Ki Attack";
|
||||
}
|
||||
else if (this is SurpriseAttack)
|
||||
{
|
||||
option = "Surprise Attack";
|
||||
}
|
||||
else if (this is HonorableExecution)
|
||||
{
|
||||
option = "Honorable Execution";
|
||||
}
|
||||
else if (this is LightningStrike)
|
||||
{
|
||||
option = "Lightning Strike";
|
||||
}
|
||||
else if (this is MomentumStrike)
|
||||
{
|
||||
option = "Momentum Strike";
|
||||
}
|
||||
|
||||
if (option != null && !DuelContext.AllowSpecialMove(from, option, this))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return CheckSkills(from) && CheckMana(from, false);
|
||||
}
|
||||
|
|
@ -187,14 +215,18 @@ namespace Server.Spells
|
|||
var moveID = kvp.Key;
|
||||
|
||||
if (moveID != -1)
|
||||
{
|
||||
m.Send(new ToggleSpecialAbility(moveID + 1, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static SpecialMove GetCurrentMove(Mobile m)
|
||||
{
|
||||
if (m == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!Core.SE)
|
||||
{
|
||||
|
|
@ -230,7 +262,9 @@ namespace Server.Spells
|
|||
ClearCurrentMove(m);
|
||||
|
||||
if (sameMove)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (move != null)
|
||||
{
|
||||
|
|
@ -243,7 +277,9 @@ namespace Server.Spells
|
|||
var moveID = SpellRegistry.GetRegistryNumber(move);
|
||||
|
||||
if (moveID > 0)
|
||||
{
|
||||
m.Send(new ToggleSpecialAbility(moveID + 1, true));
|
||||
}
|
||||
|
||||
TextDefinition.SendMessageTo(m, move.AbilityMessage);
|
||||
}
|
||||
|
|
@ -253,17 +289,17 @@ namespace Server.Spells
|
|||
|
||||
public static void ClearCurrentMove(Mobile m)
|
||||
{
|
||||
if (Table.TryGetValue(m, out var move))
|
||||
if (Table.Remove(m, out var move))
|
||||
{
|
||||
move.OnClearMove(m);
|
||||
|
||||
var moveID = SpellRegistry.GetRegistryNumber(move);
|
||||
|
||||
if (moveID > 0)
|
||||
{
|
||||
m.Send(new ToggleSpecialAbility(moveID + 1, false));
|
||||
}
|
||||
}
|
||||
|
||||
Table.Remove(m);
|
||||
}
|
||||
|
||||
private static void AddContext(Mobile m, SpecialMoveContext context)
|
||||
|
|
|
|||
|
|
@ -86,7 +86,9 @@ namespace Server.Spells
|
|||
// Confirm: Monsters and pets cannot be disturbed.
|
||||
if (Caster.Player && IsCasting && ProtectionSpell.Registry.TryGetValue(Caster, out var d) &&
|
||||
d <= Utility.RandomDouble() * 100.0)
|
||||
{
|
||||
Disturb(DisturbType.Hurt, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void OnCasterKilled()
|
||||
|
|
@ -113,7 +115,9 @@ namespace Server.Spells
|
|||
public virtual bool OnCasterEquipping(Item item)
|
||||
{
|
||||
if (IsCasting)
|
||||
{
|
||||
Disturb(DisturbType.EquipRequest);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -121,7 +125,9 @@ namespace Server.Spells
|
|||
public virtual bool OnCasterUsingObject(IEntity entity)
|
||||
{
|
||||
if (State == SpellState.Sequencing)
|
||||
{
|
||||
Disturb(DisturbType.UseRequest);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -133,16 +139,22 @@ namespace Server.Spells
|
|||
State = SpellState.None;
|
||||
|
||||
if (Caster.Spell == this)
|
||||
{
|
||||
Caster.Spell = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void StartDelayedDamageContext(Mobile m, Timer t)
|
||||
{
|
||||
if (DelayedDamageStacking)
|
||||
{
|
||||
return; // Sanity
|
||||
}
|
||||
|
||||
if (!m_ContextTable.TryGetValue(GetType(), out var contexts))
|
||||
{
|
||||
m_ContextTable[GetType()] = contexts = new DelayedDamageContextWrapper();
|
||||
}
|
||||
|
||||
contexts.Add(m, t);
|
||||
}
|
||||
|
|
@ -150,7 +162,9 @@ namespace Server.Spells
|
|||
public void RemoveDelayedDamageContext(Mobile m)
|
||||
{
|
||||
if (m_ContextTable.TryGetValue(GetType(), out var contexts))
|
||||
{
|
||||
contexts.Remove(m);
|
||||
}
|
||||
}
|
||||
|
||||
public void HarmfulSpell(Mobile m)
|
||||
|
|
@ -161,6 +175,7 @@ namespace Server.Spells
|
|||
public virtual int GetNewAosDamage(int bonus, uint dice, uint sides, Mobile singleTarget)
|
||||
{
|
||||
if (singleTarget != null)
|
||||
{
|
||||
return GetNewAosDamage(
|
||||
bonus,
|
||||
dice,
|
||||
|
|
@ -168,6 +183,7 @@ namespace Server.Spells
|
|||
Caster.Player && singleTarget.Player,
|
||||
GetDamageScalar(singleTarget)
|
||||
);
|
||||
}
|
||||
|
||||
return GetNewAosDamage(bonus, dice, sides, false);
|
||||
}
|
||||
|
|
@ -189,14 +205,18 @@ namespace Server.Spells
|
|||
var sdiBonus = AosAttributes.GetValue(Caster, AosAttribute.SpellDamage);
|
||||
// PvP spell damage increase cap of 15% from an item<65>s magic property
|
||||
if (playerVsPlayer && sdiBonus > 15)
|
||||
{
|
||||
sdiBonus = 15;
|
||||
}
|
||||
|
||||
damageBonus += sdiBonus;
|
||||
|
||||
var context = TransformationSpellHelper.GetContext(Caster);
|
||||
|
||||
if (context?.Spell is ReaperFormSpell spell)
|
||||
{
|
||||
damageBonus += spell.SpellDamageBonus;
|
||||
}
|
||||
|
||||
damage = AOS.Scale(damage, 100 + damageBonus);
|
||||
|
||||
|
|
@ -242,15 +262,21 @@ namespace Server.Spells
|
|||
// m_Caster.CheckSkill( DamageSkill, 0.0, 120.0 );
|
||||
|
||||
if (casterEI > targetRS)
|
||||
{
|
||||
scalar = 1.0 + (casterEI - targetRS) / 500.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
scalar = 1.0 + (casterEI - targetRS) / 200.0;
|
||||
}
|
||||
|
||||
// magery damage bonus, -25% at 0 skill, +0% at 100 skill, +5% at 120 skill
|
||||
scalar += (Caster.Skills[CastSkill].Value - 100.0) / 400.0;
|
||||
|
||||
if (!target.Player && !target.Body.IsHuman /*&& !Core.AOS*/)
|
||||
{
|
||||
scalar *= 2.0; // Double magery damage to monsters/animals if not AOS
|
||||
}
|
||||
}
|
||||
|
||||
(target as BaseCreature)?.AlterDamageScalarFrom(Caster, ref scalar);
|
||||
|
|
@ -258,12 +284,16 @@ namespace Server.Spells
|
|||
(Caster as BaseCreature)?.AlterDamageScalarTo(target, ref scalar);
|
||||
|
||||
if (Core.SE)
|
||||
{
|
||||
scalar *= GetSlayerDamageScalar(target);
|
||||
}
|
||||
|
||||
target.Region.SpellDamageScalar(Caster, target, ref scalar);
|
||||
|
||||
if (Evasion.CheckSpellEvasion(target)) // Only single target spells an be evaded
|
||||
{
|
||||
scalar = 0;
|
||||
}
|
||||
|
||||
return scalar;
|
||||
}
|
||||
|
|
@ -288,10 +318,14 @@ namespace Server.Spells
|
|||
|
||||
if ((atkBook.Slayer == SlayerName.Silver || atkBook.Slayer2 == SlayerName.Silver) && context != null &&
|
||||
context.Type != typeof(HorrificBeastSpell))
|
||||
{
|
||||
scalar += .25; // Every necromancer transformation other than horrific beast take an additional 25% damage
|
||||
}
|
||||
|
||||
if (scalar != 1.0)
|
||||
{
|
||||
return scalar;
|
||||
}
|
||||
}
|
||||
|
||||
var defISlayer = Spellbook.FindEquippedSpellbook(defender) ?? defender.Weapon as ISlayer;
|
||||
|
|
@ -303,7 +337,9 @@ namespace Server.Spells
|
|||
|
||||
if (defSlayer?.Group.OppositionSuperSlays(Caster) == true ||
|
||||
defSlayer2?.Group.OppositionSuperSlays(Caster) == true)
|
||||
{
|
||||
scalar = 2.0;
|
||||
}
|
||||
}
|
||||
|
||||
return scalar;
|
||||
|
|
@ -316,9 +352,13 @@ namespace Server.Spells
|
|||
if (Caster.Player)
|
||||
{
|
||||
if (Core.AOS)
|
||||
{
|
||||
Caster.FixedParticles(0x3735, 1, 30, 9503, EffectLayer.Waist);
|
||||
}
|
||||
else
|
||||
{
|
||||
Caster.FixedEffect(0x3735, 6, 30);
|
||||
}
|
||||
|
||||
Caster.PlaySound(0x5C);
|
||||
}
|
||||
|
|
@ -330,12 +370,16 @@ namespace Server.Spells
|
|||
public void Disturb(DisturbType type, bool firstCircle = true, bool resistable = false)
|
||||
{
|
||||
if (!CheckDisturb(type, firstCircle, resistable))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (State == SpellState.Casting)
|
||||
{
|
||||
if (!firstCircle && !Core.AOS && this is MagerySpell && ((MagerySpell)this).Circle == SpellCircle.First)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
State = SpellState.None;
|
||||
Caster.Spell = null;
|
||||
|
|
@ -347,14 +391,18 @@ namespace Server.Spells
|
|||
m_AnimTimer?.Stop();
|
||||
|
||||
if (Core.AOS && Caster.Player && type == DisturbType.Hurt)
|
||||
{
|
||||
DoHurtFizzle();
|
||||
}
|
||||
|
||||
Caster.NextSpellTime = Core.TickCount + (int)GetDisturbRecovery().TotalMilliseconds;
|
||||
}
|
||||
else if (State == SpellState.Sequencing)
|
||||
{
|
||||
if (!firstCircle && !Core.AOS && this is MagerySpell && ((MagerySpell)this).Circle == SpellCircle.First)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
State = SpellState.None;
|
||||
Caster.Spell = null;
|
||||
|
|
@ -364,7 +412,9 @@ namespace Server.Spells
|
|||
Target.Cancel(Caster);
|
||||
|
||||
if (Core.AOS && Caster.Player && type == DisturbType.Hurt)
|
||||
{
|
||||
DoHurtFizzle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -377,7 +427,9 @@ namespace Server.Spells
|
|||
public virtual void OnDisturb(DisturbType type, bool message)
|
||||
{
|
||||
if (message)
|
||||
{
|
||||
Caster.SendLocalizedMessage(500641); // Your concentration is disturbed, thus ruining thy spell.
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool CheckCast() => true;
|
||||
|
|
@ -385,10 +437,14 @@ namespace Server.Spells
|
|||
public virtual void SayMantra()
|
||||
{
|
||||
if (Scroll is BaseWand)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(Info.Mantra) && Caster.Player)
|
||||
{
|
||||
Caster.PublicOverheadMessage(MessageType.Spell, Caster.SpeechHue, true, Info.Mantra, false);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Cast()
|
||||
|
|
@ -396,9 +452,14 @@ namespace Server.Spells
|
|||
StartCastTime = Core.TickCount;
|
||||
|
||||
if (Core.AOS && Caster.Spell is Spell spell && spell.State == SpellState.Sequencing)
|
||||
{
|
||||
spell.Disturb(DisturbType.NewCast);
|
||||
}
|
||||
|
||||
if (!Caster.CheckAlive()) return false;
|
||||
if (!Caster.CheckAlive())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Scroll is BaseWand && Caster.Spell?.IsCasting == true)
|
||||
{
|
||||
|
|
@ -438,7 +499,9 @@ namespace Server.Spells
|
|||
Caster.Spell = this;
|
||||
|
||||
if (!(Scroll is BaseWand) && RevealOnCast)
|
||||
{
|
||||
Caster.RevealingAction();
|
||||
}
|
||||
|
||||
SayMantra();
|
||||
|
||||
|
|
@ -455,17 +518,25 @@ namespace Server.Spells
|
|||
}
|
||||
|
||||
if (Info.LeftHandEffect > 0)
|
||||
{
|
||||
Caster.FixedParticles(0, 10, 5, Info.LeftHandEffect, EffectLayer.LeftHand);
|
||||
}
|
||||
|
||||
if (Info.RightHandEffect > 0)
|
||||
{
|
||||
Caster.FixedParticles(0, 10, 5, Info.RightHandEffect, EffectLayer.RightHand);
|
||||
}
|
||||
}
|
||||
|
||||
if (ClearHandsOnCast)
|
||||
{
|
||||
Caster.ClearHands();
|
||||
}
|
||||
|
||||
if (Core.ML)
|
||||
{
|
||||
WeaponAbility.ClearCurrentAbility(Caster);
|
||||
}
|
||||
|
||||
m_CastTimer = new CastTimer(this, castDelay);
|
||||
// m_CastTimer.Start();
|
||||
|
|
@ -473,9 +544,13 @@ namespace Server.Spells
|
|||
OnBeginCast();
|
||||
|
||||
if (castDelay > TimeSpan.Zero)
|
||||
{
|
||||
m_CastTimer.Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_CastTimer.Tick();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -504,12 +579,16 @@ namespace Server.Spells
|
|||
public virtual bool CheckFizzle()
|
||||
{
|
||||
if (Scroll is BaseWand)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
GetCastSkills(out var minSkill, out var maxSkill);
|
||||
|
||||
if (DamageSkill != CastSkill)
|
||||
{
|
||||
Caster.CheckSkill(DamageSkill, 0.0, Caster.Skills[DamageSkill].Cap);
|
||||
}
|
||||
|
||||
return Caster.CheckSkill(CastSkill, minSkill, maxSkill);
|
||||
}
|
||||
|
|
@ -521,12 +600,16 @@ namespace Server.Spells
|
|||
var scalar = 1.0;
|
||||
|
||||
if (!MindRotSpell.GetMindRotScalar(Caster, ref scalar))
|
||||
{
|
||||
scalar = 1.0;
|
||||
}
|
||||
|
||||
// Lower Mana Cost = 40%
|
||||
var lmc = AosAttributes.GetValue(Caster, AosAttribute.LowerManaCost);
|
||||
if (lmc > 40)
|
||||
{
|
||||
lmc = 40;
|
||||
}
|
||||
|
||||
scalar -= (double)lmc / 100;
|
||||
|
||||
|
|
@ -536,7 +619,9 @@ namespace Server.Spells
|
|||
public virtual TimeSpan GetDisturbRecovery()
|
||||
{
|
||||
if (Core.AOS)
|
||||
{
|
||||
return TimeSpan.Zero;
|
||||
}
|
||||
|
||||
var delay = Math.Max(
|
||||
1.0 - Math.Sqrt((Core.TickCount - StartCastTime) / 1000.0 / GetCastDelay().TotalSeconds),
|
||||
|
|
@ -549,7 +634,9 @@ namespace Server.Spells
|
|||
public virtual TimeSpan GetCastRecovery()
|
||||
{
|
||||
if (!Core.AOS)
|
||||
{
|
||||
return NextSpellDelay;
|
||||
}
|
||||
|
||||
var fcr = AosAttributes.GetValue(Caster, AosAttribute.CastRecovery) -
|
||||
ThunderstormSpell.GetCastRecoveryMalus(Caster);
|
||||
|
|
@ -559,7 +646,9 @@ namespace Server.Spells
|
|||
var delay = CastRecoveryBase + fcrDelay;
|
||||
|
||||
if (delay < CastRecoveryMinimum)
|
||||
{
|
||||
delay = CastRecoveryMinimum;
|
||||
}
|
||||
|
||||
return TimeSpan.FromSeconds((double)delay / CastRecoveryPerSecond);
|
||||
}
|
||||
|
|
@ -572,7 +661,9 @@ namespace Server.Spells
|
|||
public virtual TimeSpan GetCastDelay()
|
||||
{
|
||||
if (Scroll is BaseWand)
|
||||
{
|
||||
return Core.ML ? CastDelayBase : TimeSpan.Zero; // TODO: Should FC apply to wands?
|
||||
}
|
||||
|
||||
// Faster casting cap of 2 (if not using the protection spell)
|
||||
// Faster casting cap of 0 (if using the protection spell)
|
||||
|
|
@ -582,15 +673,21 @@ namespace Server.Spells
|
|||
|
||||
if (CastSkill == SkillName.Magery || CastSkill == SkillName.Necromancy ||
|
||||
CastSkill == SkillName.Chivalry && Caster.Skills.Magery.Value >= 70.0)
|
||||
{
|
||||
fcMax = 2;
|
||||
}
|
||||
|
||||
var fc = Math.Min(AosAttributes.GetValue(Caster, AosAttribute.CastSpeed), fcMax);
|
||||
|
||||
if (ProtectionSpell.Registry.ContainsKey(Caster))
|
||||
{
|
||||
fc -= 2;
|
||||
}
|
||||
|
||||
if (EssenceOfWindSpell.IsDebuffed(Caster))
|
||||
{
|
||||
fc -= EssenceOfWindSpell.GetFCMalus(Caster);
|
||||
}
|
||||
|
||||
var fcDelay = TimeSpan.FromSeconds(-(CastDelayFastScalar * fc * CastDelaySecondsPerTick));
|
||||
|
||||
|
|
@ -652,27 +749,35 @@ namespace Server.Spells
|
|||
Scroll.Movable = false;
|
||||
|
||||
if (ClearHandsOnCast)
|
||||
{
|
||||
Caster.ClearHands();
|
||||
}
|
||||
|
||||
Scroll.Movable = m;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ClearHandsOnCast)
|
||||
{
|
||||
Caster.ClearHands();
|
||||
}
|
||||
}
|
||||
|
||||
var karma = ComputeKarmaAward();
|
||||
|
||||
if (karma != 0)
|
||||
{
|
||||
Titles.AwardKarma(Caster, karma, true);
|
||||
}
|
||||
|
||||
if (TransformationSpellHelper.UnderTransformation(Caster, typeof(VampiricEmbraceSpell)))
|
||||
{
|
||||
var garlic = false;
|
||||
|
||||
for (var i = 0; !garlic && i < Info.Reagents.Length; ++i)
|
||||
{
|
||||
garlic = Info.Reagents[i] == Reagent.Garlic;
|
||||
}
|
||||
|
||||
if (garlic)
|
||||
{
|
||||
|
|
@ -770,13 +875,19 @@ namespace Server.Spells
|
|||
if (!m_Spell.Caster.Mounted && m_Spell.Info.Action >= 0)
|
||||
{
|
||||
if (m_Spell.Caster.Body.IsHuman)
|
||||
{
|
||||
m_Spell.Caster.Animate(m_Spell.Info.Action, 7, 1, true, false, 0);
|
||||
}
|
||||
else if (m_Spell.Caster.Player && m_Spell.Caster.Body.IsMonster)
|
||||
{
|
||||
m_Spell.Caster.Animate(12, 7, 1, true, false, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (!Running)
|
||||
{
|
||||
m_Spell.m_AnimTimer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -793,7 +904,10 @@ namespace Server.Spells
|
|||
|
||||
protected override void OnTick()
|
||||
{
|
||||
if (m_Spell?.Caster == null) return;
|
||||
if (m_Spell?.Caster == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_Spell.State == SpellState.Casting && m_Spell.Caster.Spell == m_Spell)
|
||||
{
|
||||
|
|
@ -809,7 +923,9 @@ namespace Server.Spells
|
|||
m_Spell.OnCast();
|
||||
|
||||
if (m_Spell.Caster.Player && m_Spell.Caster.Target != originalTarget)
|
||||
{
|
||||
m_Spell.Caster.Target?.BeginTimeout(m_Spell.Caster, TimeSpan.FromSeconds(30.0));
|
||||
}
|
||||
|
||||
m_Spell.m_CastTimer = null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,9 @@ namespace Server
|
|||
public static void Nullify(Mobile from)
|
||||
{
|
||||
if (!from.CanBeginAction<DefensiveSpell>())
|
||||
{
|
||||
new InternalTimer(from).Start();
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalTimer : Timer
|
||||
|
|
@ -155,7 +157,9 @@ namespace Server.Spells
|
|||
public static bool CheckMulti(Point3D p, Map map, bool houses = true, int housingrange = 0)
|
||||
{
|
||||
if (map == null || map == Map.Internal)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var sector = map.GetSector(p.X, p.Y);
|
||||
|
||||
|
|
@ -166,7 +170,9 @@ namespace Server.Spells
|
|||
if (multi is BaseHouse bh)
|
||||
{
|
||||
if (houses && bh.IsInside(p, 16) || housingrange > 0 && bh.InRange(p, housingrange))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (multi.Contains(p))
|
||||
{
|
||||
|
|
@ -180,12 +186,16 @@ namespace Server.Spells
|
|||
public static void Turn(Mobile from, object to)
|
||||
{
|
||||
if (!(to is IPoint3D target))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (target is Item item)
|
||||
{
|
||||
if (item.RootParent != from)
|
||||
{
|
||||
from.Direction = from.GetDirectionTo(item.GetWorldLocation());
|
||||
}
|
||||
}
|
||||
else if (from != target)
|
||||
{
|
||||
|
|
@ -196,24 +206,32 @@ namespace Server.Spells
|
|||
public static bool CheckCombat(Mobile m)
|
||||
{
|
||||
if (!RestrictTravelCombat)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < m.Aggressed.Count; ++i)
|
||||
{
|
||||
var info = m.Aggressed[i];
|
||||
|
||||
if (info.Defender.Player && DateTime.UtcNow - info.LastCombatTime < CombatHeatDelay)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (Core.Expansion == Expansion.AOS)
|
||||
{
|
||||
for (var i = 0; i < m.Aggressors.Count; ++i)
|
||||
{
|
||||
var info = m.Aggressors[i];
|
||||
|
||||
if (info.Attacker.Player && DateTime.UtcNow - info.LastCombatTime < CombatHeatDelay)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
@ -221,7 +239,9 @@ namespace Server.Spells
|
|||
public static bool AdjustField(ref Point3D p, Map map, int height, bool mobsBlock)
|
||||
{
|
||||
if (map == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var offset = 0; offset < 10; ++offset)
|
||||
{
|
||||
|
|
@ -250,7 +270,9 @@ namespace Server.Spells
|
|||
var z = t.Z;
|
||||
|
||||
if ((t.Flags & TileFlag.Surface) == 0)
|
||||
{
|
||||
z -= TileData.ItemTable[t.ItemID & TileData.MaxItemValue].CalcHeight;
|
||||
}
|
||||
|
||||
p = new Point3D(t.X, t.Y, z);
|
||||
}
|
||||
|
|
@ -331,9 +353,13 @@ namespace Server.Spells
|
|||
double percent;
|
||||
|
||||
if (curse)
|
||||
{
|
||||
percent = 8 + caster.Skills.EvalInt.Fixed / 100 - target.Skills.MagicResist.Fixed / 100;
|
||||
}
|
||||
else
|
||||
{
|
||||
percent = 1 + caster.Skills.EvalInt.Fixed / 100;
|
||||
}
|
||||
|
||||
percent *= 0.01;
|
||||
|
||||
|
|
@ -349,7 +375,9 @@ namespace Server.Spells
|
|||
caster.CheckSkill(SkillName.EvalInt, 0.0, 120.0);
|
||||
|
||||
if (curse)
|
||||
{
|
||||
target.CheckSkill(SkillName.MagicResist, 0.0, 120.0);
|
||||
}
|
||||
}
|
||||
|
||||
var percent = GetOffsetScalar(caster, target, curse);
|
||||
|
|
@ -377,14 +405,18 @@ namespace Server.Spells
|
|||
m = c.ControlMaster;
|
||||
|
||||
if (m != null)
|
||||
{
|
||||
g = m.Guild as Guild;
|
||||
}
|
||||
|
||||
if (g == null)
|
||||
{
|
||||
m = c.SummonMaster;
|
||||
|
||||
if (m != null)
|
||||
{
|
||||
g = m.Guild as Guild;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -394,10 +426,14 @@ namespace Server.Spells
|
|||
public static bool ValidIndirectTarget(Mobile from, Mobile to)
|
||||
{
|
||||
if (from == to)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (to.Hidden && to.AccessLevel > from.AccessLevel)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var bcFrom = from as BaseCreature;
|
||||
var bcTarg = to as BaseCreature;
|
||||
|
|
@ -406,48 +442,70 @@ namespace Server.Spells
|
|||
PlayerMobile pmTarg;
|
||||
|
||||
if (bcFrom?.Summoned == true)
|
||||
{
|
||||
pmFrom = bcFrom.SummonMaster as PlayerMobile;
|
||||
}
|
||||
else
|
||||
{
|
||||
pmFrom = from as PlayerMobile;
|
||||
}
|
||||
|
||||
if (bcTarg?.Summoned == true)
|
||||
{
|
||||
pmTarg = bcTarg.SummonMaster as PlayerMobile;
|
||||
}
|
||||
else
|
||||
{
|
||||
pmTarg = to as PlayerMobile;
|
||||
}
|
||||
|
||||
if (pmFrom?.DuelContext != null && pmFrom.DuelContext == pmTarg?.DuelContext && pmFrom.DuelContext.Started &&
|
||||
pmFrom.DuelPlayer != null && pmTarg?.DuelPlayer != null)
|
||||
{
|
||||
return pmFrom.DuelPlayer.Participant != pmTarg.DuelPlayer.Participant;
|
||||
}
|
||||
|
||||
var fromGuild = GetGuildFor(from);
|
||||
var toGuild = GetGuildFor(to);
|
||||
|
||||
if (fromGuild != null && toGuild != null && (fromGuild == toGuild || fromGuild.IsAlly(toGuild)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var p = Party.Get(from);
|
||||
|
||||
if (p?.Contains(to) == true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bcTarg != null && (bcTarg.Controlled || bcTarg.Summoned))
|
||||
{
|
||||
if (bcTarg.ControlMaster == from || bcTarg.SummonMaster == from)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (p != null && (p.Contains(bcTarg.ControlMaster) || p.Contains(bcTarg.SummonMaster)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (bcFrom != null && (bcFrom.Controlled || bcFrom.Summoned))
|
||||
{
|
||||
if (bcFrom.ControlMaster == to || bcFrom.SummonMaster == to)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
p = Party.Get(to);
|
||||
|
||||
if (p != null && (p.Contains(bcFrom.ControlMaster) || p.Contains(bcFrom.SummonMaster)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return bcTarg?.Controlled == false && bcTarg.InitialInnocent ||
|
||||
|
|
@ -462,12 +520,16 @@ namespace Server.Spells
|
|||
var map = caster.Map;
|
||||
|
||||
if (map == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var scale = 1.0 + (caster.Skills.Magery.Value - 100.0) / 200.0;
|
||||
|
||||
if (scaleDuration)
|
||||
{
|
||||
duration = TimeSpan.FromSeconds(duration.TotalSeconds * scale);
|
||||
}
|
||||
|
||||
if (scaleStats)
|
||||
{
|
||||
|
|
@ -491,12 +553,12 @@ namespace Server.Spells
|
|||
|
||||
/*
|
||||
int offset = Utility.Random( 8 ) * 2;
|
||||
|
||||
|
||||
for ( int i = 0; i < m_Offsets.Length; i += 2 )
|
||||
{
|
||||
int x = caster.X + m_Offsets[(offset + i) % m_Offsets.Length];
|
||||
int y = caster.Y + m_Offsets[(offset + i + 1) % m_Offsets.Length];
|
||||
|
||||
|
||||
if (map.CanSpawnMobile( x, y, caster.Z ))
|
||||
{
|
||||
BaseCreature.Summon( creature, caster, new Point3D( x, y, caster.Z ), sound, duration );
|
||||
|
|
@ -505,7 +567,7 @@ namespace Server.Spells
|
|||
else
|
||||
{
|
||||
int z = map.GetAverageZ( x, y );
|
||||
|
||||
|
||||
if (map.CanSpawnMobile( x, y, z ))
|
||||
{
|
||||
BaseCreature.Summon( creature, caster, new Point3D( x, y, z ), sound, duration );
|
||||
|
|
@ -522,7 +584,9 @@ namespace Server.Spells
|
|||
public static bool FindValidSpawnLocation(Map map, ref Point3D p, bool surroundingsOnly)
|
||||
{
|
||||
if (map == null) // sanity
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!surroundingsOnly)
|
||||
{
|
||||
|
|
@ -569,11 +633,17 @@ namespace Server.Spells
|
|||
public static void SendInvalidMessage(Mobile caster, TravelCheckType type)
|
||||
{
|
||||
if (type == TravelCheckType.RecallTo || type == TravelCheckType.GateTo)
|
||||
{
|
||||
caster.SendLocalizedMessage(1019004); // You are not allowed to travel there.
|
||||
}
|
||||
else if (type == TravelCheckType.TeleportTo)
|
||||
{
|
||||
caster.SendLocalizedMessage(501035); // You cannot teleport from here to the destination.
|
||||
}
|
||||
else
|
||||
{
|
||||
caster.SendLocalizedMessage(501802); // Thy spell doth not appear to work...
|
||||
}
|
||||
}
|
||||
|
||||
public static bool CheckTravel(Mobile caster, TravelCheckType type) =>
|
||||
|
|
@ -586,7 +656,9 @@ namespace Server.Spells
|
|||
if (IsInvalid(map, loc)) // null, internal, out of bounds
|
||||
{
|
||||
if (caster != null)
|
||||
{
|
||||
SendInvalidMessage(caster, type);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
@ -594,7 +666,9 @@ namespace Server.Spells
|
|||
// Always allow monsters to teleport
|
||||
if (caster is BaseCreature bc && !bc.Controlled && !bc.Summoned &&
|
||||
(type == TravelCheckType.TeleportTo || type == TravelCheckType.TeleportFrom))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
m_TravelCaster = caster;
|
||||
m_TravelType = type;
|
||||
|
|
@ -608,14 +682,20 @@ namespace Server.Spells
|
|||
var current = Region.Find(caster.Location, map) as BaseRegion;
|
||||
|
||||
if (destination?.CheckTravel(caster, loc, type) == false || current?.CheckTravel(caster, loc, type) == false)
|
||||
{
|
||||
isValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; isValid && i < m_Validators.Length; ++i)
|
||||
{
|
||||
isValid = m_Rules[v, i] || !m_Validators[i](map, loc);
|
||||
}
|
||||
|
||||
if (!isValid && caster != null)
|
||||
{
|
||||
SendInvalidMessage(caster, type);
|
||||
}
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
|
@ -669,7 +749,9 @@ namespace Server.Spells
|
|||
public static bool IsCrystalCave(Map map, Point3D loc)
|
||||
{
|
||||
if (map != Map.Malas || loc.Z >= -80)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int x = loc.X, y = loc.Y;
|
||||
|
||||
|
|
@ -691,7 +773,9 @@ namespace Server.Spells
|
|||
public static bool IsDoomFerry(Map map, Point3D loc)
|
||||
{
|
||||
if (map != Map.Malas)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int x = loc.X, y = loc.Y;
|
||||
|
||||
|
|
@ -702,7 +786,9 @@ namespace Server.Spells
|
|||
{
|
||||
// The tokuno dungeons are really inside malas
|
||||
if (map != Map.Malas)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int x = loc.X, y = loc.Y, z = loc.Z;
|
||||
|
||||
|
|
@ -715,7 +801,9 @@ namespace Server.Spells
|
|||
public static bool IsDoomGauntlet(Map map, Point3D loc)
|
||||
{
|
||||
if (map != Map.Malas)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int x = loc.X - 256, y = loc.Y - 304;
|
||||
|
||||
|
|
@ -725,7 +813,9 @@ namespace Server.Spells
|
|||
public static bool IsLampRoom(Map map, Point3D loc)
|
||||
{
|
||||
if (map != Map.Malas)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int x = loc.X, y = loc.Y;
|
||||
|
||||
|
|
@ -735,7 +825,9 @@ namespace Server.Spells
|
|||
public static bool IsGuardianRoom(Map map, Point3D loc)
|
||||
{
|
||||
if (map != Map.Malas)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int x = loc.X, y = loc.Y;
|
||||
|
||||
|
|
@ -754,7 +846,9 @@ namespace Server.Spells
|
|||
public static bool IsInvalid(Map map, Point3D loc)
|
||||
{
|
||||
if (map == null || map == Map.Internal)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int x = loc.X, y = loc.Y;
|
||||
|
||||
|
|
@ -765,7 +859,9 @@ namespace Server.Spells
|
|||
public static bool IsTown(IPoint3D ip, Mobile caster)
|
||||
{
|
||||
if (ip is Item item)
|
||||
{
|
||||
ip = item.GetWorldLocation();
|
||||
}
|
||||
|
||||
return IsTown(new Point3D(ip), caster);
|
||||
}
|
||||
|
|
@ -775,11 +871,17 @@ namespace Server.Spells
|
|||
var map = caster.Map;
|
||||
|
||||
if (map == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Region.Find(loc, map).GetRegion<SafeZone>() != null)
|
||||
{
|
||||
if (caster is PlayerMobile pm && (pm.DuelContext?.Started != true || pm.DuelPlayer?.Eliminated != false))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
var reg = Region.Find(loc, map).GetRegion<GuardedRegion>();
|
||||
|
||||
|
|
@ -789,7 +891,9 @@ namespace Server.Spells
|
|||
public static bool CheckTown(IPoint3D ip, Mobile caster)
|
||||
{
|
||||
if (ip is Item item)
|
||||
{
|
||||
ip = item.GetWorldLocation();
|
||||
}
|
||||
|
||||
return CheckTown(new Point3D(ip), caster);
|
||||
}
|
||||
|
|
@ -953,7 +1057,9 @@ namespace Server.Spells
|
|||
var damageGiven = AOS.Damage(target, from, dmg, phys, fire, cold, pois, nrgy, chaos);
|
||||
|
||||
if (from != null) // sanity check
|
||||
{
|
||||
DoLeech(damageGiven, from, target);
|
||||
}
|
||||
|
||||
WeightOverloading.DFA = DFAlgorithm.Standard;
|
||||
}
|
||||
|
|
@ -974,7 +1080,9 @@ namespace Server.Spells
|
|||
var context = TransformationSpellHelper.GetContext(from);
|
||||
|
||||
if (context == null) /* cleanup */
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (context.Type == typeof(WraithFormSpell))
|
||||
{
|
||||
|
|
@ -1019,7 +1127,9 @@ namespace Server.Spells
|
|||
m_Spell = s;
|
||||
|
||||
if (m_Spell?.DelayedDamage == true && !m_Spell.DelayedDamageStacking)
|
||||
{
|
||||
m_Spell.StartDelayedDamageContext(target, this);
|
||||
}
|
||||
|
||||
Priority = TimerPriority.TwentyFiveMS;
|
||||
}
|
||||
|
|
@ -1067,7 +1177,9 @@ namespace Server.Spells
|
|||
m_DFA = dfa;
|
||||
m_Spell = s;
|
||||
if (m_Spell?.DelayedDamage == true && !m_Spell.DelayedDamageStacking)
|
||||
{
|
||||
m_Spell.StartDelayedDamageContext(target, this);
|
||||
}
|
||||
|
||||
Priority = TimerPriority.TwentyFiveMS;
|
||||
}
|
||||
|
|
@ -1078,17 +1190,23 @@ namespace Server.Spells
|
|||
var bcTarg = m_Target as BaseCreature;
|
||||
|
||||
if (bcFrom != null && m_Target != null)
|
||||
{
|
||||
bcFrom.AlterSpellDamageTo(m_Target, ref m_Damage);
|
||||
}
|
||||
|
||||
if (bcTarg != null && m_From != null)
|
||||
{
|
||||
bcTarg.AlterSpellDamageFrom(m_From, ref m_Damage);
|
||||
}
|
||||
|
||||
WeightOverloading.DFA = m_DFA;
|
||||
|
||||
var damageGiven = AOS.Damage(m_Target, m_From, m_Damage, m_Phys, m_Fire, m_Cold, m_Pois, m_Nrgy, m_Chaos);
|
||||
|
||||
if (m_From != null) // sanity check
|
||||
{
|
||||
DoLeech(damageGiven, m_From, m_Target);
|
||||
}
|
||||
|
||||
WeightOverloading.DFA = DFAlgorithm.Standard;
|
||||
|
||||
|
|
@ -1133,7 +1251,9 @@ namespace Server.Spells
|
|||
public static bool OnCast(Mobile caster, Spell spell)
|
||||
{
|
||||
if (!(spell is ITransformationSpell transformSpell))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Sigil.ExistsOn(caster))
|
||||
{
|
||||
|
|
@ -1180,33 +1300,47 @@ namespace Server.Spells
|
|||
var mods = new List<ResistanceMod>();
|
||||
|
||||
if (transformSpell.PhysResistOffset != 0)
|
||||
{
|
||||
mods.Add(new ResistanceMod(ResistanceType.Physical, transformSpell.PhysResistOffset));
|
||||
}
|
||||
|
||||
if (transformSpell.FireResistOffset != 0)
|
||||
{
|
||||
mods.Add(new ResistanceMod(ResistanceType.Fire, transformSpell.FireResistOffset));
|
||||
}
|
||||
|
||||
if (transformSpell.ColdResistOffset != 0)
|
||||
{
|
||||
mods.Add(new ResistanceMod(ResistanceType.Cold, transformSpell.ColdResistOffset));
|
||||
}
|
||||
|
||||
if (transformSpell.PoisResistOffset != 0)
|
||||
{
|
||||
mods.Add(new ResistanceMod(ResistanceType.Poison, transformSpell.PoisResistOffset));
|
||||
}
|
||||
|
||||
if (transformSpell.NrgyResistOffset != 0)
|
||||
{
|
||||
mods.Add(new ResistanceMod(ResistanceType.Energy, transformSpell.NrgyResistOffset));
|
||||
}
|
||||
|
||||
if (!((Body)transformSpell.Body).IsHuman)
|
||||
{
|
||||
var mt = caster.Mount;
|
||||
|
||||
if (mt != null)
|
||||
{
|
||||
mt.Rider = null;
|
||||
}
|
||||
}
|
||||
|
||||
caster.BodyMod = transformSpell.Body;
|
||||
caster.HueMod = transformSpell.Hue;
|
||||
|
||||
for (var i = 0; i < mods.Count; ++i)
|
||||
{
|
||||
caster.AddResistanceMod(mods[i]);
|
||||
}
|
||||
|
||||
transformSpell.DoEffect(caster);
|
||||
|
||||
|
|
@ -1231,20 +1365,24 @@ namespace Server.Spells
|
|||
var context = GetContext(m);
|
||||
|
||||
if (context != null)
|
||||
{
|
||||
RemoveContext(m, context, resetGraphics);
|
||||
}
|
||||
}
|
||||
|
||||
public static void RemoveContext(Mobile m, TransformContext context, bool resetGraphics)
|
||||
{
|
||||
if (!m_Table.ContainsKey(m))
|
||||
if (!m_Table.Remove(m))
|
||||
{
|
||||
return;
|
||||
|
||||
m_Table.Remove(m);
|
||||
}
|
||||
|
||||
var mods = context.Mods;
|
||||
|
||||
for (var i = 0; i < mods.Count; ++i)
|
||||
{
|
||||
m.RemoveResistanceMod(mods[i]);
|
||||
}
|
||||
|
||||
if (resetGraphics)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -75,8 +75,7 @@ namespace Server.Spells
|
|||
|
||||
m_Types[spellID] = type;
|
||||
|
||||
if (!m_IDsFromTypes.ContainsKey(type))
|
||||
m_IDsFromTypes.Add(type, spellID);
|
||||
m_IDsFromTypes.TryAdd(type, spellID);
|
||||
|
||||
if (type.IsSubclassOf(typeof(SpecialMove)))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -62,10 +62,9 @@ namespace Server.Spells.Bushido
|
|||
|
||||
public static void EndConfidence(Mobile m)
|
||||
{
|
||||
if (m_Table.TryGetValue(m, out var timer))
|
||||
if (m_Table.Remove(m, out var timer))
|
||||
{
|
||||
timer.Stop();
|
||||
m_Table.Remove(m);
|
||||
}
|
||||
|
||||
OnEffectEnd(m, typeof(Confidence));
|
||||
|
|
@ -85,10 +84,9 @@ namespace Server.Spells.Bushido
|
|||
|
||||
public static void StopRegenerating(Mobile m)
|
||||
{
|
||||
if (m_RegenTable.TryGetValue(m, out var timer))
|
||||
if (m_RegenTable.Remove(m, out var timer))
|
||||
{
|
||||
timer.Stop();
|
||||
m_RegenTable.Remove(m);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -77,10 +77,9 @@ namespace Server.Spells.Bushido
|
|||
|
||||
public static void StopCountering(Mobile m)
|
||||
{
|
||||
if (m_Table.TryGetValue(m, out var timer))
|
||||
if (m_Table.Remove(m, out var timer))
|
||||
{
|
||||
timer.Stop();
|
||||
m_Table.Remove(m);
|
||||
}
|
||||
|
||||
OnEffectEnd(m, typeof(CounterAttack));
|
||||
|
|
|
|||
|
|
@ -181,10 +181,9 @@ namespace Server.Spells.Bushido
|
|||
|
||||
public static void EndEvasion(Mobile m)
|
||||
{
|
||||
if (m_Table.TryGetValue(m, out var timer))
|
||||
if (m_Table.Remove(m, out var timer))
|
||||
{
|
||||
timer.Stop();
|
||||
m_Table.Remove(m);
|
||||
}
|
||||
|
||||
OnEffectEnd(m, typeof(Evasion));
|
||||
|
|
|
|||
|
|
@ -25,7 +25,9 @@ namespace Server.Spells.Bushido
|
|||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (!Validate(attacker) || !CheckMana(attacker, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ClearCurrentMove(attacker);
|
||||
|
||||
|
|
@ -64,7 +66,9 @@ namespace Server.Spells.Bushido
|
|||
var resSpells = attacker.Skills.MagicResist.Value;
|
||||
|
||||
if (resSpells > 0.0)
|
||||
{
|
||||
mods.Add(new DefaultSkillMod(SkillName.MagicResist, true, -resSpells));
|
||||
}
|
||||
|
||||
info = new HonorableExecutionInfo(attacker, mods);
|
||||
info.m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(7.0), RemovePenalty, info.m_Mobile);
|
||||
|
|
@ -81,12 +85,13 @@ namespace Server.Spells.Bushido
|
|||
|
||||
public static void RemovePenalty(Mobile target)
|
||||
{
|
||||
if (!m_Table.TryGetValue(target, out var info) || !info.m_Penalty)
|
||||
if (!m_Table.Remove(target, out var info) || !info.m_Penalty)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
info.Clear();
|
||||
info.m_Timer?.Stop();
|
||||
m_Table.Remove(target);
|
||||
}
|
||||
|
||||
private class HonorableExecutionInfo
|
||||
|
|
@ -114,32 +119,44 @@ namespace Server.Spells.Bushido
|
|||
public void Apply()
|
||||
{
|
||||
if (m_Mods == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < m_Mods.Count; ++i)
|
||||
{
|
||||
var mod = m_Mods[i];
|
||||
|
||||
if (mod is ResistanceMod resistanceMod)
|
||||
{
|
||||
m_Mobile.AddResistanceMod(resistanceMod);
|
||||
}
|
||||
else if (mod is SkillMod skillMod)
|
||||
{
|
||||
m_Mobile.AddSkillMod(skillMod);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
if (m_Mods == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < m_Mods.Count; ++i)
|
||||
{
|
||||
var mod = m_Mods[i];
|
||||
|
||||
if (mod is ResistanceMod resistanceMod)
|
||||
{
|
||||
m_Mobile.RemoveResistanceMod(resistanceMod);
|
||||
}
|
||||
else if (mod is SkillMod skillMod)
|
||||
{
|
||||
m_Mobile.RemoveSkillMod(skillMod);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,9 @@ namespace Server.Spells.Fifth
|
|||
public override bool CheckCast()
|
||||
{
|
||||
if (Core.AOS)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Caster.MagicDamageAbsorb > 0)
|
||||
{
|
||||
|
|
@ -57,15 +59,15 @@ namespace Server.Spells.Fifth
|
|||
{
|
||||
var targ = Caster;
|
||||
|
||||
if (m_Table.TryGetValue(targ, out var mods))
|
||||
if (m_Table.Remove(targ, out var mods))
|
||||
{
|
||||
targ.PlaySound(0x1ED);
|
||||
targ.FixedParticles(0x375A, 10, 15, 5037, EffectLayer.Waist);
|
||||
|
||||
m_Table.Remove(targ);
|
||||
|
||||
for (var i = 0; i < mods.Length; ++i)
|
||||
{
|
||||
targ.RemoveResistanceMod(mods[i]);
|
||||
}
|
||||
|
||||
BuffInfo.RemoveBuff(targ, BuffIcon.MagicReflection);
|
||||
}
|
||||
|
|
@ -89,7 +91,9 @@ namespace Server.Spells.Fifth
|
|||
m_Table[targ] = mods;
|
||||
|
||||
for (var i = 0; i < mods.Length; ++i)
|
||||
{
|
||||
targ.AddResistanceMod(mods[i]);
|
||||
}
|
||||
|
||||
var buffFormat = $"{physiMod}\t+{otherMod}\t+{otherMod}\t+{otherMod}\t+{otherMod}";
|
||||
|
||||
|
|
@ -133,13 +137,16 @@ namespace Server.Spells.Fifth
|
|||
|
||||
public static void EndReflect(Mobile m)
|
||||
{
|
||||
if (!m_Table.TryGetValue(m, out var mods))
|
||||
if (!m_Table.Remove(m, out var mods))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < mods?.Length; ++i)
|
||||
{
|
||||
m.RemoveResistanceMod(mods[i]);
|
||||
}
|
||||
|
||||
m_Table.Remove(m);
|
||||
BuffInfo.RemoveBuff(m, BuffIcon.MagicReflection);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,9 @@ namespace Server.Spells.First
|
|||
public override bool CheckCast()
|
||||
{
|
||||
if (Core.AOS)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Caster.MeleeDamageAbsorb > 0)
|
||||
{
|
||||
|
|
@ -59,15 +61,15 @@ namespace Server.Spells.First
|
|||
{
|
||||
var targ = Caster;
|
||||
|
||||
if (m_Table.TryGetValue(targ, out var mods))
|
||||
if (m_Table.Remove(targ, out var mods))
|
||||
{
|
||||
targ.PlaySound(0x1ED);
|
||||
targ.FixedParticles(0x376A, 9, 32, 5008, EffectLayer.Waist);
|
||||
|
||||
m_Table.Remove(targ);
|
||||
|
||||
for (var i = 0; i < mods.Length; ++i)
|
||||
{
|
||||
targ.RemoveResistanceMod(mods[i]);
|
||||
}
|
||||
|
||||
BuffInfo.RemoveBuff(Caster, BuffIcon.ReactiveArmor);
|
||||
}
|
||||
|
|
@ -91,7 +93,9 @@ namespace Server.Spells.First
|
|||
m_Table[targ] = mods;
|
||||
|
||||
for (var i = 0; i < mods.Length; ++i)
|
||||
{
|
||||
targ.AddResistanceMod(mods[i]);
|
||||
}
|
||||
|
||||
var physresist = 15 + (int)(targ.Skills.Inscribe.Value / 20);
|
||||
var args = $"{physresist}\t{5}\t{5}\t{5}\t{5}";
|
||||
|
|
@ -140,13 +144,16 @@ namespace Server.Spells.First
|
|||
|
||||
public static void EndArmor(Mobile m)
|
||||
{
|
||||
if (!m_Table.TryGetValue(m, out var mods))
|
||||
if (!m_Table.Remove(m, out var mods))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < mods?.Length; ++i)
|
||||
{
|
||||
m.RemoveResistanceMod(mods[i]);
|
||||
}
|
||||
|
||||
m_Table.Remove(m);
|
||||
BuffInfo.RemoveBuff(m, BuffIcon.ReactiveArmor);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,9 +97,8 @@ namespace Server.Spells.Fourth
|
|||
|
||||
public static void RemoveEntry(Mobile m)
|
||||
{
|
||||
if (_Table.TryGetValue(m, out var v))
|
||||
if (_Table.Remove(m, out var v))
|
||||
{
|
||||
_Table.Remove(m);
|
||||
m.EndAction<ArchProtectionSpell>();
|
||||
m.VirtualArmorMod -= Math.Min(v, m.VirtualArmorMod);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,7 +95,9 @@ namespace Server.Spells.Mysticism
|
|||
var mount = Caster.Mount;
|
||||
|
||||
if (mount != null)
|
||||
{
|
||||
mount.Rider = null;
|
||||
}
|
||||
|
||||
Caster.BodyMod = 0x2C1;
|
||||
Caster.HueMod = 0;
|
||||
|
|
@ -112,7 +114,9 @@ namespace Server.Spells.Mysticism
|
|||
};
|
||||
|
||||
for (var i = 0; i < mods.Length; ++i)
|
||||
{
|
||||
Caster.AddResistanceMod(mods[i]);
|
||||
}
|
||||
|
||||
m_Table[Caster] = mods;
|
||||
|
||||
|
|
@ -141,17 +145,19 @@ namespace Server.Spells.Mysticism
|
|||
|
||||
public static void RemoveEffects(Mobile m)
|
||||
{
|
||||
if (!m_Table.TryGetValue(m, out var mods))
|
||||
if (!m_Table.Remove(m, out var mods))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < mods.Length; ++i)
|
||||
{
|
||||
m.RemoveResistanceMod(mods[i]);
|
||||
}
|
||||
|
||||
m.BodyMod = 0;
|
||||
m.HueMod = -1;
|
||||
|
||||
m_Table.Remove(m);
|
||||
|
||||
BuffInfo.RemoveBuff(m, BuffIcon.StoneForm);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -151,7 +151,10 @@ namespace Server.Spells.Necromancy
|
|||
{
|
||||
Type type = null;
|
||||
|
||||
if (c.Owner != null) type = c.Owner.GetType();
|
||||
if (c.Owner != null)
|
||||
{
|
||||
type = c.Owner.GetType();
|
||||
}
|
||||
|
||||
if (c.ItemID != 0x2006 || c.Animated || type == typeof(PlayerMobile) || type == null ||
|
||||
c.Owner != null && c.Owner.Fame < 100 ||
|
||||
|
|
@ -217,10 +220,14 @@ namespace Server.Spells.Necromancy
|
|||
var contains = types.Length == 0;
|
||||
|
||||
for (var j = 0; !contains && j < types.Length; ++j)
|
||||
{
|
||||
contains = types[j].IsAssignableFrom(type);
|
||||
}
|
||||
|
||||
if (contains)
|
||||
return group;
|
||||
{
|
||||
return @group;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
@ -228,41 +235,52 @@ namespace Server.Spells.Necromancy
|
|||
|
||||
public static void Unregister(Mobile master, Mobile summoned)
|
||||
{
|
||||
if (master == null)
|
||||
return;
|
||||
|
||||
if (!m_Table.TryGetValue(master, out var list))
|
||||
if (master == null || !m_Table.TryGetValue(master, out var list))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
list.Remove(summoned);
|
||||
|
||||
if (list.Count == 0)
|
||||
{
|
||||
m_Table.Remove(master);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Register(Mobile master, Mobile summoned)
|
||||
{
|
||||
if (master == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_Table.TryGetValue(master, out var list))
|
||||
{
|
||||
m_Table[master] = list = new List<Mobile>();
|
||||
}
|
||||
|
||||
for (var i = list.Count - 1; i >= 0; --i)
|
||||
{
|
||||
if (i >= list.Count)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var mob = list[i];
|
||||
|
||||
if (mob.Deleted)
|
||||
{
|
||||
list.RemoveAt(i--);
|
||||
}
|
||||
}
|
||||
|
||||
list.Add(summoned);
|
||||
|
||||
if (list.Count > 3)
|
||||
{
|
||||
Timer.DelayCall(list[0].Kill);
|
||||
}
|
||||
|
||||
Timer.DelayCall(TimeSpan.FromSeconds(2.0), TimeSpan.FromSeconds(2.0), Summoned_Damage, summoned);
|
||||
}
|
||||
|
|
@ -270,20 +288,28 @@ namespace Server.Spells.Necromancy
|
|||
private static void Summoned_Damage(Mobile mob)
|
||||
{
|
||||
if (mob.Hits > 0)
|
||||
{
|
||||
--mob.Hits;
|
||||
}
|
||||
else
|
||||
{
|
||||
mob.Kill();
|
||||
}
|
||||
}
|
||||
|
||||
private static void SummonDelay_Callback(Mobile caster, Corpse corpse, Point3D loc, Map map, CreatureGroup group)
|
||||
{
|
||||
if (corpse.Animated)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var owner = corpse.Owner;
|
||||
|
||||
if (owner == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var necromancy = caster.Skills.Necromancy.Value;
|
||||
var spiritSpeak = caster.Skills.SpiritSpeak.Value;
|
||||
|
|
@ -299,7 +325,9 @@ namespace Server.Spells.Necromancy
|
|||
var entry = entries[i];
|
||||
|
||||
if (casterAbility < entry.m_Requirement)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var animates = entry.m_ToSummon;
|
||||
|
||||
|
|
@ -307,7 +335,9 @@ namespace Server.Spells.Necromancy
|
|||
}
|
||||
|
||||
if (toSummon == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Mobile summoned = null;
|
||||
|
||||
|
|
@ -321,7 +351,9 @@ namespace Server.Spells.Necromancy
|
|||
}
|
||||
|
||||
if (summoned == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (summoned is BaseCreature bc)
|
||||
{
|
||||
|
|
@ -336,7 +368,9 @@ namespace Server.Spells.Necromancy
|
|||
}
|
||||
|
||||
if (summoned is SkeletalDragon dragon)
|
||||
{
|
||||
Scale(dragon, 50); // lose 50% hp and strength
|
||||
}
|
||||
|
||||
summoned.Fame = 0;
|
||||
summoned.Karma = -1500;
|
||||
|
|
@ -357,7 +391,9 @@ namespace Server.Spells.Necromancy
|
|||
toScale = bc.HitsMaxSeed;
|
||||
|
||||
if (toScale > 0)
|
||||
{
|
||||
bc.HitsMaxSeed = AOS.Scale(toScale, scalar);
|
||||
}
|
||||
|
||||
bc.Hits = bc.Hits; // refresh hits
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,9 +45,13 @@ namespace Server.Spells.Necromancy
|
|||
else if (m_OathTable.ContainsKey(m))
|
||||
{
|
||||
if (m.Player)
|
||||
{
|
||||
Caster.SendLocalizedMessage(1061608); // That player is already bonded in a Blood Oath.
|
||||
}
|
||||
else
|
||||
{
|
||||
Caster.SendLocalizedMessage(1061609); // That creature is already bonded in a Blood Oath.
|
||||
}
|
||||
}
|
||||
else if (CheckHSequence(m))
|
||||
{
|
||||
|
|
@ -128,21 +132,22 @@ namespace Server.Spells.Necromancy
|
|||
protected override void OnTick()
|
||||
{
|
||||
if (m_Caster.Deleted || m_Target.Deleted || !m_Caster.Alive || !m_Target.Alive ||
|
||||
DateTime.UtcNow >= m_End) DoExpire();
|
||||
DateTime.UtcNow >= m_End)
|
||||
{
|
||||
DoExpire();
|
||||
}
|
||||
}
|
||||
|
||||
public void DoExpire()
|
||||
{
|
||||
if (m_OathTable.ContainsKey(m_Caster))
|
||||
if (m_OathTable.Remove(m_Caster))
|
||||
{
|
||||
m_Caster.SendLocalizedMessage(1061620); // Your Blood Oath has been broken.
|
||||
m_OathTable.Remove(m_Caster);
|
||||
}
|
||||
|
||||
if (m_OathTable.ContainsKey(m_Target))
|
||||
if (m_OathTable.Remove(m_Target))
|
||||
{
|
||||
m_Target.SendLocalizedMessage(1061620); // Your Blood Oath has been broken.
|
||||
m_OathTable.Remove(m_Target);
|
||||
}
|
||||
|
||||
Stop();
|
||||
|
|
|
|||
|
|
@ -57,7 +57,9 @@ namespace Server.Spells.Necromancy
|
|||
var mod = new DefaultSkillMod(SkillName.MagicResist, false, 50.0);
|
||||
|
||||
if (m.Skills.MagicResist.Base > 50.0)
|
||||
{
|
||||
m.AddSkillMod(mod);
|
||||
}
|
||||
|
||||
m_Table[m] = mod;
|
||||
}
|
||||
|
|
@ -81,10 +83,11 @@ namespace Server.Spells.Necromancy
|
|||
|
||||
public static bool TryEndEffect(Mobile m)
|
||||
{
|
||||
if (!m_Table.TryGetValue(m, out var mod))
|
||||
if (!m_Table.Remove(m, out var mod))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_Table.Remove(m);
|
||||
mod?.Remove();
|
||||
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -73,12 +73,13 @@ namespace Server.Spells.Necromancy
|
|||
|
||||
public static void ClearMindRotScalar(Mobile m)
|
||||
{
|
||||
if (!m_Table.TryGetValue(m, out var tmpB))
|
||||
if (!m_Table.Remove(m, out var tmpB))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BuffInfo.RemoveBuff(m, BuffIcon.Mindrot);
|
||||
tmpB.m_MRExpireTimer.Stop();
|
||||
m_Table.Remove(m);
|
||||
m.SendLocalizedMessage(1060872); // Your mind feels normal again.
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,9 @@ namespace Server.Spells.Necromancy
|
|||
public void Target(Mobile m)
|
||||
{
|
||||
if (m == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (CheckHSequence(m))
|
||||
{
|
||||
|
|
@ -70,7 +72,10 @@ namespace Server.Spells.Necromancy
|
|||
// Calculations for the buff bar
|
||||
var spiritlevel = Caster.Skills.SpiritSpeak.Value / 10;
|
||||
if (spiritlevel < 4)
|
||||
{
|
||||
spiritlevel = 4;
|
||||
}
|
||||
|
||||
var d_MinDamage = 4;
|
||||
var d_MaxDamage = ((int)spiritlevel + 1) * 3;
|
||||
var args = $"{d_MinDamage}\t{d_MaxDamage}";
|
||||
|
|
@ -113,13 +118,13 @@ namespace Server.Spells.Necromancy
|
|||
|
||||
public static bool RemoveCurse(Mobile m)
|
||||
{
|
||||
if (!m_Table.TryGetValue(m, out var timer))
|
||||
if (!m_Table.Remove(m, out var timer))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
timer.Stop();
|
||||
m.SendLocalizedMessage(1061687); // You can breath normally again.
|
||||
|
||||
m_Table.Remove(m);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -153,7 +158,9 @@ namespace Server.Spells.Necromancy
|
|||
m_Count = (int)spiritLevel;
|
||||
|
||||
if (m_Count < 4)
|
||||
{
|
||||
m_Count = 4;
|
||||
}
|
||||
|
||||
m_MaxCount = m_Count;
|
||||
}
|
||||
|
|
@ -167,7 +174,9 @@ namespace Server.Spells.Necromancy
|
|||
}
|
||||
|
||||
if (!m_Target.Alive || DateTime.UtcNow < m_NextHit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
--m_Count;
|
||||
|
||||
|
|
@ -182,9 +191,13 @@ namespace Server.Spells.Necromancy
|
|||
var delay = (int)Math.Ceiling((1.0 + 5 * m_Count) / m_MaxCount);
|
||||
|
||||
if (delay <= 5)
|
||||
{
|
||||
m_HitDelay = delay;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_HitDelay = 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -203,16 +216,22 @@ namespace Server.Spells.Necromancy
|
|||
damage *= 3 - (double)m_Target.Stam / m_Target.StamMax * 2;
|
||||
|
||||
if (damage < 1)
|
||||
{
|
||||
damage = 1;
|
||||
}
|
||||
|
||||
if (!m_Target.Player)
|
||||
{
|
||||
damage *= 1.75;
|
||||
}
|
||||
|
||||
AOS.Damage(m_Target, m_From, (int)damage, 0, 0, 0, 100, 0);
|
||||
|
||||
if (Utility.RandomDouble() >= 0.60
|
||||
) // OSI: randomly revealed between first and third damage tick, guessing 60% chance
|
||||
{
|
||||
m_Target.RevealingAction();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,9 @@ namespace Server.Spells.Ninjitsu
|
|||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (!Validate(attacker) || !CheckMana(attacker, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ClearCurrentMove(attacker);
|
||||
|
||||
|
|
@ -32,9 +34,13 @@ namespace Server.Spells.Ninjitsu
|
|||
var isRanged = attacker.Weapon is BaseRanged;
|
||||
|
||||
if (ninjitsu < 100) // This formula is an approximation from OSI data. TODO: find correct formula
|
||||
{
|
||||
chance = 30 + (ninjitsu - 85) * 2.2;
|
||||
}
|
||||
else
|
||||
{
|
||||
chance = 63 + (ninjitsu - 100) * 1.1;
|
||||
}
|
||||
|
||||
if (chance / 100 < Utility.RandomDouble())
|
||||
{
|
||||
|
|
@ -44,16 +50,16 @@ namespace Server.Spells.Ninjitsu
|
|||
|
||||
var damageBonus = 0;
|
||||
|
||||
if (m_Table.TryGetValue(defender, out var info))
|
||||
if (m_Table.Remove(defender, out var info))
|
||||
{
|
||||
defender.SendLocalizedMessage(1063092); // Your opponent lands another Death Strike!
|
||||
|
||||
if (info.m_Steps > 0)
|
||||
{
|
||||
damageBonus = attacker.Skills.Ninjitsu.Fixed / 150;
|
||||
}
|
||||
|
||||
info.m_Timer?.Stop();
|
||||
|
||||
m_Table.Remove(defender);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -78,13 +84,17 @@ namespace Server.Spells.Ninjitsu
|
|||
public static void AddStep(Mobile m)
|
||||
{
|
||||
if (m_Table.TryGetValue(m, out var info) && ++info.m_Steps >= 5)
|
||||
{
|
||||
ProcessDeathStrike(m);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ProcessDeathStrike(Mobile defender)
|
||||
{
|
||||
if (!m_Table.TryGetValue(defender, out var info))
|
||||
if (!m_Table.Remove(defender, out var info))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int damage;
|
||||
|
||||
|
|
@ -97,16 +107,24 @@ namespace Server.Spells.Ninjitsu
|
|||
info.m_Attacker.Skills.Stealth.Value) / 220;
|
||||
|
||||
if (scalar > 1)
|
||||
{
|
||||
scalar = 1;
|
||||
}
|
||||
|
||||
// New formula doesn't apply DamageBonus anymore, caps must be, directly, 60/30.
|
||||
if (info.m_Steps >= 5)
|
||||
{
|
||||
damage = (int)Math.Floor(Math.Min(60, ninjitsu / 3 * (0.3 + 0.7 * scalar) + stalkingBonus));
|
||||
}
|
||||
else
|
||||
{
|
||||
damage = (int)Math.Floor(Math.Min(30, ninjitsu / 9 * (0.3 + 0.7 * scalar) + stalkingBonus));
|
||||
}
|
||||
|
||||
if (info.m_isRanged)
|
||||
{
|
||||
damage /= 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -118,8 +136,11 @@ namespace Server.Spells.Ninjitsu
|
|||
}
|
||||
|
||||
if (Core.ML)
|
||||
{
|
||||
info.m_Target.Damage(damage, info.m_Attacker); // Damage is direct.
|
||||
}
|
||||
else
|
||||
{
|
||||
AOS.Damage(
|
||||
info.m_Target,
|
||||
info.m_Attacker,
|
||||
|
|
@ -136,10 +157,9 @@ namespace Server.Spells.Ninjitsu
|
|||
false,
|
||||
true
|
||||
); // Damage is physical.
|
||||
}
|
||||
|
||||
info.m_Timer?.Stop();
|
||||
|
||||
m_Table.Remove(info.m_Target);
|
||||
}
|
||||
|
||||
private class DeathStrikeInfo
|
||||
|
|
|
|||
|
|
@ -81,11 +81,10 @@ namespace Server.Spells.Ninjitsu
|
|||
|
||||
public override void OnClearMove(Mobile from)
|
||||
{
|
||||
if (!m_Table.TryGetValue(from, out var info))
|
||||
return;
|
||||
|
||||
info.m_Timer.Stop();
|
||||
m_Table.Remove(info.m_Mobile);
|
||||
if (m_Table.Remove(from, out var info))
|
||||
{
|
||||
info.m_Timer.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
public static double GetBonus(Mobile from)
|
||||
|
|
|
|||
|
|
@ -53,11 +53,9 @@ namespace Server.Spells.Ninjitsu
|
|||
|
||||
attacker.RevealingAction();
|
||||
|
||||
if (m_Table.TryGetValue(defender, out var info))
|
||||
if (m_Table.Remove(defender, out var info))
|
||||
{
|
||||
info.m_Timer?.Stop();
|
||||
|
||||
m_Table.Remove(defender);
|
||||
}
|
||||
|
||||
var ninjitsu = attacker.Skills.Ninjitsu.Fixed;
|
||||
|
|
|
|||
|
|
@ -29,7 +29,9 @@ namespace Server.Spells.Second
|
|||
public override bool CheckCast()
|
||||
{
|
||||
if (Core.AOS)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Registry.ContainsKey(Caster))
|
||||
{
|
||||
|
|
@ -38,7 +40,9 @@ namespace Server.Spells.Second
|
|||
}
|
||||
|
||||
if (Caster.CanBeginAction<DefensiveSpell>())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Caster.SendLocalizedMessage(1005385); // The spell will not adhere to you at this time.
|
||||
return false;
|
||||
|
|
@ -55,12 +59,11 @@ namespace Server.Spells.Second
|
|||
* even after dying<EFBFBD>until you <EFBFBD>turn them off<EFBFBD> by casting them again.
|
||||
*/
|
||||
|
||||
if (m_Table.TryGetValue(target, out var mods))
|
||||
if (m_Table.Remove(target, out var mods))
|
||||
{
|
||||
target.PlaySound(0x1ED);
|
||||
target.FixedParticles(0x375A, 9, 20, 5016, EffectLayer.Waist);
|
||||
|
||||
m_Table.Remove(target);
|
||||
Registry.Remove(target);
|
||||
|
||||
target.RemoveResistanceMod(mods.Item1);
|
||||
|
|
@ -100,10 +103,11 @@ namespace Server.Spells.Second
|
|||
|
||||
public static void EndProtection(Mobile m)
|
||||
{
|
||||
if (!m_Table.TryGetValue(m, out var mods))
|
||||
if (!m_Table.Remove(m, out var mods))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_Table.Remove(m);
|
||||
Registry.Remove(m);
|
||||
|
||||
m.RemoveResistanceMod(mods.Item1);
|
||||
|
|
@ -117,7 +121,9 @@ namespace Server.Spells.Second
|
|||
if (Core.AOS)
|
||||
{
|
||||
if (CheckSequence())
|
||||
{
|
||||
Toggle(Caster, Caster);
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,11 +93,10 @@ namespace Server.Spells.Sixth
|
|||
|
||||
public static void RemoveTimer(Mobile m)
|
||||
{
|
||||
if (!m_Table.TryGetValue(m, out var t))
|
||||
return;
|
||||
|
||||
t.Stop();
|
||||
m_Table.Remove(m);
|
||||
if (m_Table.Remove(m, out var t))
|
||||
{
|
||||
t.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalTimer : Timer
|
||||
|
|
|
|||
|
|
@ -27,7 +27,9 @@ namespace Server.Spells.Spellweaving
|
|||
public void Target(Mobile m)
|
||||
{
|
||||
if (m == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Caster.CanSee(m))
|
||||
{
|
||||
|
|
@ -94,10 +96,10 @@ namespace Server.Spells.Spellweaving
|
|||
|
||||
public static bool StopEffect(Mobile m)
|
||||
{
|
||||
if (!m_Table.TryGetValue(m, out var info))
|
||||
if (!m_Table.Remove(m, out var info))
|
||||
{
|
||||
return false;
|
||||
|
||||
m_Table.Remove(m);
|
||||
}
|
||||
|
||||
info.m_Timer.Stop();
|
||||
BuffInfo.RemoveBuff(m, BuffIcon.GiftOfRenewal);
|
||||
|
|
@ -151,7 +153,9 @@ namespace Server.Spells.Spellweaving
|
|||
}
|
||||
|
||||
if (m.Hits >= m.HitsMax)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var toHeal = m_GiftInfo.m_HitsPerRound;
|
||||
|
||||
|
|
|
|||
|
|
@ -77,17 +77,20 @@ namespace Server.Spells.Spellweaving
|
|||
private static void FinishEffect(DelayedEffectEntry effect)
|
||||
{
|
||||
if (m_WeaponDamageTable.TryGetValue(effect.m_Weapon, out var entry))
|
||||
{
|
||||
AOS.Damage(effect.m_Target, entry.m_Caster, entry.m_Damage, 0, 100, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public static void StopImmolating(BaseWeapon weapon)
|
||||
{
|
||||
if (!m_WeaponDamageTable.TryGetValue(weapon, out var entry))
|
||||
if (!m_WeaponDamageTable.Remove(weapon, out var entry))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
entry.m_Caster?.PlaySound(0x27);
|
||||
entry.m_Timer.Stop();
|
||||
m_WeaponDamageTable.Remove(weapon);
|
||||
|
||||
weapon.InvalidateProperties();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,9 @@ namespace Server.Spells.Spellweaving
|
|||
{
|
||||
if (Caster == m || !SpellHelper.ValidIndirectTarget(Caster, m) || !Caster.CanBeHarmful(m, false) ||
|
||||
!Caster.InLOS(m))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Caster.DoHarmful(m);
|
||||
|
||||
|
|
@ -57,7 +59,9 @@ namespace Server.Spells.Spellweaving
|
|||
SpellHelper.Damage(this, m, m.Player && Caster.Player ? pvpDamage : pvmDamage, 0, 0, 0, 0, 100);
|
||||
|
||||
if (oldSpell == null || oldSpell == m.Spell || CheckResisted(m))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
m_Table[m] = Timer.DelayCall(duration, DoExpire, m);
|
||||
|
||||
|
|
@ -77,11 +81,12 @@ namespace Server.Spells.Spellweaving
|
|||
|
||||
public static void DoExpire(Mobile m)
|
||||
{
|
||||
if (!m_Table.TryGetValue(m, out var t))
|
||||
if (!m_Table.Remove(m, out var t))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
t.Stop();
|
||||
m_Table.Remove(m);
|
||||
|
||||
BuffInfo.RemoveBuff(m, BuffIcon.Thunderstorm);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue