Merge branch 'main' into addon-tutorial

This commit is contained in:
Leath Cooper 2025-01-18 14:13:55 -05:00
commit 158a43a168
42 changed files with 715 additions and 308 deletions

View file

@ -375,7 +375,6 @@ public class GenericEntityPersistence<T> : GenericPersistence, IGenericEntityPer
// Skip this entry
if (t == null)
{
dataReader.Seek(entry.Length, SeekOrigin.Current);
continue;
}
@ -383,7 +382,9 @@ public class GenericEntityPersistence<T> : GenericPersistence, IGenericEntityPer
try
{
var pos = dataReader.Position;
dataReader.Seek(entry.Position, SeekOrigin.Begin);
var pos = entry.Position;
t.Deserialize(dataReader);
var lengthDeserialized = dataReader.Position - pos;

View file

@ -18,6 +18,7 @@ using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Server.Buffers;
using Server.Text;
namespace Server;
@ -286,4 +287,18 @@ public static class StringHelpers
str.CopyTo(chars);
return chars;
}
public static void AppendSpaceWithArticle(this ref ValueStringBuilder builder, string text, bool articleAn)
{
if (builder.Length != 0)
{
builder.Append(' ');
}
else
{
builder.Append(articleAn ? "an " : "a ");
}
builder.Append(text);
}
}

View file

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Net;
using System.Runtime.CompilerServices;
using System.Xml;
using ModernUO.CodeGeneratedEvents;
using ModernUO.Serialization;
using Server.Accounting.Security;
using Server.Misc;
@ -768,31 +769,24 @@ public partial class Account : IAccount, IComparable<Account>
acc.TotalGameTime += Core.Now - pm.SessionStart;
}
public static void OnLogin(Mobile m)
[OnEvent(nameof(PlayerMobile.PlayerLoginEvent))]
public static void OnLogin(PlayerMobile pm)
{
if (m is not PlayerMobile pm)
if (pm.Account is not Account acc || !pm.Young || !acc.Young)
{
return;
}
if (m.Account is not Account acc)
var ts = YoungDuration - acc.TotalGameTime;
var hours = Math.Max((int)ts.TotalHours, 0);
if (hours == 1)
{
return;
pm.SendAsciiMessage($"You will enjoy the benefits and relatively safe status of a young player for {hours} more hour.");
}
if (pm.Young && acc.Young)
else
{
var ts = YoungDuration - acc.TotalGameTime;
var hours = Math.Max((int)ts.TotalHours, 0);
if (hours == 1)
{
m.SendAsciiMessage($"You will enjoy the benefits and relatively safe status of a young player for {hours} more hour.");
}
else
{
m.SendAsciiMessage($"You will enjoy the benefits and relatively safe status of a young player for {hours} more hours.");
}
pm.SendAsciiMessage($"You will enjoy the benefits and relatively safe status of a young player for {hours} more hours.");
}
}

View file

@ -1,7 +1,9 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using ModernUO.CodeGeneratedEvents;
using Server.Gumps;
using Server.Mobiles;
using Server.Network;
namespace Server.Assistants;
@ -45,22 +47,23 @@ public static class AssistantHandler
m.NetState.LogInfo("Failed to negotiate assistant features.");
}
public static void OnLogin(Mobile m)
[OnEvent(nameof(PlayerMobile.PlayerLoginEvent))]
public static void OnLogin(PlayerMobile pm)
{
if (m?.NetState?.Running != true || !Enabled)
if (pm?.NetState?.Running != true || !Enabled)
{
return;
}
m.NetState.SendAssistVersionReq();
m.NetState.SendAssistHandshake();
pm.NetState.SendAssistVersionReq();
pm.NetState.SendAssistHandshake();
if (_handshakes.TryGetValue(m, out var t))
if (_handshakes.TryGetValue(pm, out var t))
{
t?.Stop();
}
_handshakes[m] = Timer.DelayCall(TimeSpan.FromSeconds(30), OnTimeout, m);
_handshakes[pm] = Timer.DelayCall(TimeSpan.FromSeconds(30), OnTimeout, pm);
}
public static void AssistVersion(NetState state, SpanReader reader)

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using ModernUO.CodeGeneratedEvents;
using Server.Mobiles;
using Server.Network;
using Server.Targeting;
@ -15,10 +16,8 @@ namespace Server.Commands
CommandSystem.Register("VisClear", AccessLevel.Counselor, VisClear_OnCommand);
}
public static void OnLogin(Mobile m)
{
(m as PlayerMobile)?.VisibilityList.Clear();
}
[OnEvent(nameof(PlayerMobile.PlayerLoginEvent))]
public static void OnLogin(PlayerMobile pm) => pm.VisibilityList.Clear();
[Usage("Vis")]
[Description("Adds or removes a targeted player from your visibility list. Anyone on your visibility list will be able to see you at all times, even when you're hidden.")]

View file

@ -1291,13 +1291,9 @@ namespace Server.Engines.ConPVP
return false;
}
public static void OnLogin(Mobile m)
[OnEvent(nameof(PlayerMobile.PlayerLoginEvent))]
public static void OnLogin(PlayerMobile pm)
{
if (m is not PlayerMobile pm)
{
return;
}
var dc = pm.DuelContext;
if (dc == null)

View file

@ -1,3 +1,4 @@
using ModernUO.CodeGeneratedEvents;
using Server.Mobiles;
using Server.Regions;
@ -14,12 +15,13 @@ namespace Server.Engines.Doom
Register();
}
public static void OnLogin(Mobile m)
[OnEvent(nameof(PlayerMobile.PlayerLoginEvent))]
public static void OnLogin(PlayerMobile pm)
{
var rect = LeverPuzzleController.lr_Rect;
if (m.X >= rect.X && m.X <= rect.X + 10 && m.Y >= rect.Y && m.Y <= rect.Y + 10 && m.Map == Map.Internal)
if (pm.X >= rect.X && pm.X <= rect.X + 10 && pm.Y >= rect.Y && pm.Y <= rect.Y + 10 && pm.Map == Map.Internal)
{
Timer kick = new LeverPuzzleController.LampRoomKickTimer(m);
Timer kick = new LeverPuzzleController.LampRoomKickTimer(pm);
kick.Start();
}
}

View file

@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using ModernUO.CodeGeneratedEvents;
using Server.Accounting;
using Server.Commands.Generic;
using Server.Engines.ConPVP;
@ -1259,7 +1261,9 @@ public abstract class Faction : IComparable<Faction>
}
}
public static void OnLogin(Mobile m) => CheckLeaveTimer(m);
[OnEvent(nameof(PlayerMobile.PlayerLoginEvent))]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void OnLogin(PlayerMobile pm) => CheckLeaveTimer(pm);
public static void WriteReference(IGenericWriter writer, Faction fact)
{

View file

@ -0,0 +1,27 @@
using System;
using System.Runtime.CompilerServices;
namespace Server.Engines.Help;
public static class HelpEvents
{
public static event Action<PageEntry> PageEnqueued;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void InvokePageEnqueued(PageEntry e) => PageEnqueued?.Invoke(e);
public static event Action<PageEntry> PageRemoved;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void InvokePageRemoved(PageEntry e) => PageRemoved?.Invoke(e);
public static event Action<Mobile, Mobile, PageEntry> PageHandlerChanged;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void InvokePageHandlerChanged(Mobile old, Mobile value, PageEntry e) => PageHandlerChanged?.Invoke(old, value, e);
public static event Action<PageEntry> PageWaiting;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void InvokePageWaiting(PageEntry e) => PageWaiting?.Invoke(e);
public static event Action<Mobile> StuckMenu;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void InvokeStuckMenu(Mobile m) => StuckMenu?.Invoke(m);
}

View file

@ -103,6 +103,8 @@ namespace Server.Engines.Help
{
m_Entry.Handler = null;
}
HelpEvents.InvokePageWaiting(m_Entry);
}
else
{
@ -171,6 +173,13 @@ namespace Server.Engines.Help
{
m_KeyedByHandler[value] = entry;
}
if (old == null || value == null)
{
return;
}
HelpEvents.InvokePageHandlerChanged(old, value, entry);
}
[Usage("Pages"), Description("Opens the page queue menu.")]
@ -212,6 +221,8 @@ namespace Server.Engines.Help
{
m_KeyedByHandler.Remove(e.Handler);
}
HelpEvents.InvokePageRemoved(e);
}
public static PageEntry GetEntry(Mobile sender)
@ -258,6 +269,8 @@ namespace Server.Engines.Help
{
Email.SendQueueEmail(entry, GetPageTypeName(entry.Type));
}
HelpEvents.InvokePageEnqueued(entry);
}
}
}

View file

@ -1,4 +1,5 @@
using System;
using Server.Engines.Help;
using Server.Factions;
using Server.Gumps;
using Server.Mobiles;
@ -194,16 +195,16 @@ namespace Server.Menus.Questions
m_Mobile.SendLocalizedMessage(1010588); // You choose not to go to any city.
}
}
else
{
var index = info.ButtonID - 1;
var entries = IsInSecondAgeArea(m_Mobile) ? m_T2AEntries : m_Entries;
if (index >= 0 && index < entries.Length)
{
Teleport(entries[index]);
}
var index = info.ButtonID - 1;
var entries = IsInSecondAgeArea(m_Mobile) ? m_T2AEntries : m_Entries;
if (index >= 0 && index < entries.Length)
{
Teleport(entries[index]);
}
HelpEvents.InvokeStuckMenu(m_Mobile);
}
private void Teleport(StuckMenuEntry entry)

View file

@ -160,7 +160,8 @@ namespace Server.Engines.PartySystem
}
}
public static void OnLogin(Mobile from)
[OnEvent(nameof(PlayerMobile.PlayerLoginEvent))]
public static void OnLogin(PlayerMobile from)
{
var p = Get(from);

View file

@ -1,7 +1,9 @@
using System;
using ModernUO.CodeGeneratedEvents;
using ModernUO.Serialization;
using Server.Items;
using Server.Misc;
using Server.Mobiles;
namespace Server.Engines.Plants
{
@ -432,7 +434,8 @@ namespace Server.Engines.Plants
}
}
public static void OnLogin(Mobile from)
[OnEvent(nameof(PlayerMobile.PlayerLoginEvent))]
public static void OnLogin(PlayerMobile from)
{
Container cont = from.Backpack;
if (cont != null)

View file

@ -80,9 +80,10 @@ public class PlayerMurderSystem : GenericPersistence
UpdateMurderContext(context);
}
public static void OnLogin(Mobile m)
[OnEvent(nameof(PlayerMobile.PlayerLoginEvent))]
public static void OnLogin(PlayerMobile pm)
{
if (m is not PlayerMobile pm || !GetMurderContext(pm, out var context))
if (!GetMurderContext(pm, out var context))
{
return;
}

View file

@ -1,4 +1,5 @@
using System;
using ModernUO.CodeGeneratedEvents;
using Server.Accounting;
using Server.Gumps;
using Server.Items;
@ -562,35 +563,31 @@ namespace Server.Engines.VeteranRewards
RewardInterval = ServerConfiguration.GetOrUpdateSetting("vetRewards.rewardInterval", TimeSpan.FromDays(30.0));
}
public static void OnLogin(Mobile m)
[OnEvent(nameof(PlayerMobile.PlayerLoginEvent))]
public static void OnLogin(PlayerMobile pm)
{
if (!Enabled)
if (!Enabled || !pm.Alive)
{
return;
}
if (!m.Alive)
{
return;
}
ComputeRewardInfo(pm, out var cur, out var max, out var level);
ComputeRewardInfo(m, out var cur, out var max, out var level);
if (m.SkillsCap is 7000 or 7050 or 7100 or 7150 or 7200)
if (pm.SkillsCap is 7000 or 7050 or 7100 or 7150 or 7200)
{
level = Math.Clamp(level, 0, 4);
if (SkillCapRewards)
{
m.SkillsCap = 7000 + level * 50;
pm.SkillsCap = 7000 + level * 50;
}
else
{
m.SkillsCap = 7000;
pm.SkillsCap = 7000;
}
}
if (Core.ML && m is PlayerMobile pm && !pm.HasStatReward && HasHalfLevel(pm))
if (Core.ML && !pm.HasStatReward && HasHalfLevel(pm))
{
pm.HasStatReward = true;
pm.StatCap += 5;
@ -598,7 +595,7 @@ namespace Server.Engines.VeteranRewards
if (cur < max)
{
m.SendGump(new RewardNoticeGump(m));
pm.SendGump(new RewardNoticeGump(pm));
}
}
}

View file

@ -52,10 +52,9 @@ public class AddDoorGump : DynamicGump
]
];
private static int _maxCount;
private Type _type;
private int _baseId;
private int _page;
public AddDoorGump() : base(50, 40)
{
@ -72,66 +71,38 @@ public class AddDoorGump : DynamicGump
AddBlueBack(ref builder, 155, 174);
builder.AddItem(25, 24, _baseId);
builder.AddButton(26, 37, 0x5782, 0x5782, 1);
builder.AddButton(26, 37, 0x5782, 0x5782, 10);
builder.AddItem(47, 45, _baseId + 2);
builder.AddButton(43, 57, 0x5783, 0x5783, 2);
builder.AddButton(43, 57, 0x5783, 0x5783, 11);
builder.AddItem(87, 22, _baseId + 10);
builder.AddButton(116, 35, 0x5785, 0x5785, 6);
builder.AddButton(116, 35, 0x5785, 0x5785, 15);
builder.AddItem(65, 45, _baseId + 8);
builder.AddButton(96, 55, 0x5784, 0x5784, 5);
builder.AddButton(96, 55, 0x5784, 0x5784, 14);
builder.AddButton(73, 36, 0x2716, 0x2716, 9);
builder.AddButton(73, 36, 0x2716, 0x2716, 18);
}
else
{
var pages = _types.Length;
if (_maxCount == 0)
{
for (var i = 0; i < pages; i++)
{
_maxCount = Math.Max(_maxCount, _types[i].Length);
}
}
var types = _types[_page];
AddBlueBack(ref builder, 20 + (_maxCount + 1) * 50, 165);
AddBlueBack(ref builder, 20 + (types.Length + 1) * 50, 165);
builder.AddHtmlLocalized(30, 45, 60, 20, 1043353, 0x7FFF); // Next
builder.AddHtmlLocalized(30, 85, 60, 20, 1011393, 0x7FFF); // Back
for (var i = 0; i < pages; ++i)
builder.AddButton(30, 60, 0xFA5, 0xFA7, 1);
builder.AddButton(30, 100, 0xFAE, 0xFB0, 2);
for (var i = 0; i < types.Length; i++)
{
var page = i + 1;
var x = (i + 1) * 50;
builder.AddPage(page);
if (page < pages)
{
builder.AddButton(30, 60, 0xFA5, 0xFA7, 0, GumpButtonType.Page, page + 1);
}
else
{
builder.AddButton(30, 60, 0xFA5, 0xFA7, 0, GumpButtonType.Page, 1);
}
if (page > 1)
{
builder.AddButton(30, 100, 0xFAE, 0xFB0, 0, GumpButtonType.Page, page - 1);
}
else
{
builder.AddButton(30, 100, 0xFAE, 0xFB0, 0, GumpButtonType.Page, pages);
}
for (var j = 0; j < _types[i].Length; j++)
{
var x = (j + 1) * 50;
builder.AddButton(30 + x, 20, 0x2624, 0x2625, i * _maxCount + j + 1);
builder.AddItem(15 + x, 30, _types[i][j].BaseID);
}
builder.AddButton(30 + x, 20, 0x2624, 0x2625, _page * types.Length + i + 10);
builder.AddItem(15 + x, 30, types[i].BaseID);
}
}
}
@ -147,35 +118,64 @@ public class AddDoorGump : DynamicGump
public override void OnResponse(NetState sender, in RelayInfo info)
{
var from = sender.Mobile;
var button = info.ButtonID - 1;
var button = info.ButtonID;
if (_type == null)
if (button == 0)
{
if (button < 0 || button >= _types.Length)
if (_type != null)
{
_type = null;
_baseId = 0;
}
else
{
return;
}
var page = Math.DivRem(button, _maxCount, out var pos);
var door = _types[page][pos];
_type = door.Type;
_baseId = door.BaseID;
}
else if (button is >= 0 and < 8)
else if (button == 1) // Next
{
CommandSystem.Handle(
from,
$"{CommandSystem.Prefix}Add {_type.Name} {(DoorFacing)button}"
);
_page++;
if (_page >= _types.Length)
{
_page = 0;
}
}
else if (button == 8)
else if (button == 2) // Prev
{
CommandSystem.Handle(from, $"{CommandSystem.Prefix}Link");
_page--;
if (_page < 0)
{
_page = _types.Length - 1;
}
}
else
{
_type = null;
_baseId = 0;
button -= 10;
if (_type == null)
{
var types = _types[_page];
var page = Math.DivRem(button, types.Length, out var pos);
if (page < 0 || pos >= types.Length)
{
return;
}
var door = types[pos];
_type = door.Type;
_baseId = door.BaseID;
}
else if (button is >= 0 and < 8)
{
CommandSystem.Handle(
from,
$"{CommandSystem.Prefix}Add {_type.Name} {(DoorFacing)button}"
);
}
else if (button == 8)
{
CommandSystem.Handle(from, $"{CommandSystem.Prefix}Link");
}
}
from.SendGump(this);

View file

@ -12,6 +12,7 @@ using Server.Misc;
using Server.Multis;
using Server.Network;
using Server.Prompts;
using Server.Saves;
using Server.Text;
namespace Server.Gumps
@ -295,6 +296,9 @@ namespace Server.Gumps
AddButtonLabeled(20, 230, GetButtonID(3, 203), "Shutdown & Restart (With Save)");
AddButtonLabeled(20, 250, GetButtonID(3, 204), "Shutdown & Restart (Without Save)");
AddButtonLabeled(20, 270, GetButtonID(3, 205), "Shutdown (With 15m Delay & Save)");
/*}
else
{
@ -2147,6 +2151,12 @@ namespace Server.Gumps
Shutdown(true, false);
break;
}
case 205: // shutdown with delay and save
{
var t = new ShutdownTimer(this);
t.Start();
break;
}
case 210:
case 211:
{
@ -3927,32 +3937,32 @@ namespace Server.Gumps
var availableMaps = ExpansionInfo.CoreExpansion.MapSelectionFlags;
if (Core.SA && availableMaps.Includes(MapSelectionFlags.TerMur))
{
InvokeCommand("GenerateSpawners Data/Spawns/post-uoml/termur/*.json");
InvokeCommand("GenerateSpawners Data/Spawns/post-uoml/termur/**.json");
}
if (availableMaps.Includes(MapSelectionFlags.Malas))
{
InvokeCommand($"GenerateSpawners Data/Spawns/{folder}/malas/*.json");
InvokeCommand($"GenerateSpawners Data/Spawns/{folder}/malas/**.json");
}
if (availableMaps.Includes(MapSelectionFlags.Tokuno))
{
InvokeCommand($"GenerateSpawners Data/Spawns/{folder}/tokuno/*.json");
InvokeCommand($"GenerateSpawners Data/Spawns/{folder}/tokuno/**.json");
}
if (availableMaps.Includes(MapSelectionFlags.Ilshenar))
{
InvokeCommand($"GenerateSpawners Data/Spawns/{folder}/ilshenar/*.json");
InvokeCommand($"GenerateSpawners Data/Spawns/{folder}/ilshenar/**.json");
}
if (availableMaps.Includes(MapSelectionFlags.Trammel))
{
InvokeCommand($"GenerateSpawners Data/Spawns/{folder}/trammel/*.json");
InvokeCommand($"GenerateSpawners Data/Spawns/{folder}/trammel/**.json");
}
if (availableMaps.Includes(MapSelectionFlags.Felucca))
{
InvokeCommand($"GenerateSpawners Data/Spawns/{folder}/felucca/*.json");
InvokeCommand($"GenerateSpawners Data/Spawns/{folder}/felucca/**.json");
}
}
@ -4251,5 +4261,30 @@ namespace Server.Gumps
public AdminNoticeGump(string content, Action callback) : base(callback) => Content = content;
}
public class ShutdownTimer : Timer
{
private readonly AdminGump _adminGump;
public ShutdownTimer(AdminGump gump) : base(TimeSpan.Zero, TimeSpan.Zero, 8) =>
_adminGump = gump;
protected override void OnTick()
{
if (Index >= 7)
{
AutoSave.SavesEnabled = false;
_adminGump.Shutdown(false, true);
return;
}
ReadOnlySpan<int> times = [15, 10, 5, 4, 3, 2, 1, 0];
var time = times[Index];
_adminGump.m_From.SendMessage(
$"The shard will shutdown in {time} minute{(time == 1 ? "s" : "")} for maintenance."
);
Interval = TimeSpan.FromMinutes(time - times[Index + 1]);
}
}
}
}

View file

@ -1,17 +1,20 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using ModernUO.Serialization;
using Server.Engines.Craft;
using Server.Ethics;
using Server.Factions;
using Server.Network;
using Server.Text;
using AMA = Server.Items.ArmorMeditationAllowance;
using AMT = Server.Items.ArmorMaterialType;
namespace Server.Items
{
[SerializationGenerator(9, false)]
public abstract partial class BaseArmor : Item, IScissorable, IFactionItem, ICraftable, IWearableDurability, IAosItem
public abstract partial class BaseArmor
: Item, IScissorable, IFactionItem, ICraftable, IWearableDurability, IAosItem, IIdentifiable
{
[SerializedIgnoreDupe]
[SerializableField(0, setter: "private")]
@ -101,7 +104,7 @@ namespace Server.Items
private string _crafter;
[SerializableFieldSaveFlag(10)]
private bool ShouldSerializeCrafter() => _crafter != null;
private bool ShouldSerializeCrafter() => !string.IsNullOrEmpty(_crafter);
[SerializableFieldSaveFlag(14)]
private bool ShouldSerializeResource() => _resource != DefaultResource;
@ -559,6 +562,7 @@ namespace Server.Items
Resource = CraftResources.GetFromType(resourceType);
PlayerConstructed = true;
Identified = true;
var context = craftSystem.GetContext(from);
@ -1451,6 +1455,12 @@ namespace Server.Items
public override void OnSingleClick(Mobile from)
{
if (!Core.UOTD)
{
OnSingleClickPreUOTD(from);
return;
}
var attrs = new List<EquipInfoAttribute>();
if (DisplayLootType)
@ -1482,13 +1492,12 @@ namespace Server.Items
attrs.Add(new EquipInfoAttribute(1038000 + (int)_durability));
}
if (_protectionLevel > ArmorProtectionLevel.Regular && _protectionLevel <= ArmorProtectionLevel.Invulnerability)
if (_protectionLevel != ArmorProtectionLevel.Regular)
{
attrs.Add(new EquipInfoAttribute(1038005 + (int)_protectionLevel));
}
}
else if (_durability != ArmorDurabilityLevel.Regular || _protectionLevel > ArmorProtectionLevel.Regular &&
_protectionLevel <= ArmorProtectionLevel.Invulnerability)
else if (_durability != ArmorDurabilityLevel.Regular || _protectionLevel != ArmorProtectionLevel.Regular)
{
attrs.Add(new EquipInfoAttribute(1038000)); // Unidentified
}
@ -1513,6 +1522,100 @@ namespace Server.Items
from.NetState.SendDisplayEquipmentInfo(Serial, number, _crafter, false, attrs);
}
public void OnSingleClickPreUOTD(Mobile from)
{
var isMagicItem = _durability != ArmorDurabilityLevel.Regular ||
_protectionLevel != ArmorProtectionLevel.Regular;
if (isMagicItem && !_identified)
{
LabelTo(from, $"an unidentified {Name ?? Localization.GetText(LabelNumber).ToLowerInvariant()}");
return;
}
var name = Name;
var articleAnName = (TileData.ItemTable[ItemID].Flags & TileFlag.ArticleAn) != 0;
if (isMagicItem)
{
var builder = ValueStringBuilder.Create(128);
var durabilityText = DurabilityText(out var articleAnDurability);
if (durabilityText != null)
{
builder.AppendSpaceWithArticle(durabilityText, articleAnDurability);
}
if (name == null)
{
builder.AppendSpaceWithArticle(Localization.GetText(LabelNumber).ToLowerInvariant(), articleAnName);
}
else if (builder.Length != 0)
{
builder.Append($" {name}");
}
else
{
builder.Append(name);
}
var protectionText = ProtectionText;
if (protectionText != null)
{
builder.Append($" of {protectionText}");
}
LabelTo(from, builder.ToString());
builder.Dispose();
return;
}
name ??= $"{(articleAnName ? "an" : "a")} {Localization.GetText(LabelNumber).ToLowerInvariant()}";
if (Crafter == null)
{
LabelTo(from, Quality == ArmorQuality.Exceptional ? $"{name} of exceptional quality" : name);
return;
}
LabelTo(
from,
Quality == ArmorQuality.Exceptional
? $"{name} crafted with exceptional quality by {Crafter}"
: $"{name} crafted by {Crafter}"
);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private string DurabilityText(out bool articleAn)
{
articleAn = _durability is ArmorDurabilityLevel.Indestructible;
return _durability switch
{
ArmorDurabilityLevel.Durable => "durable",
ArmorDurabilityLevel.Substantial => "substantial",
ArmorDurabilityLevel.Massive => "massive",
ArmorDurabilityLevel.Fortified => "fortified",
ArmorDurabilityLevel.Indestructible => "indestructable",
_ => null
};
}
private string ProtectionText
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get =>
_protectionLevel switch
{
ArmorProtectionLevel.Defense => "defense",
ArmorProtectionLevel.Guarding => "guarding",
ArmorProtectionLevel.Hardening => "hardening",
ArmorProtectionLevel.Fortification => "fortification",
ArmorProtectionLevel.Invulnerability => "invulnerability",
_ => null
};
}
[Flags]
private enum OldSaveFlag
{

View file

@ -95,7 +95,7 @@ namespace Server.Items
private string _crafter;
[SerializableFieldSaveFlag(8)]
private bool ShouldSerializeCrafter() => _crafter != null;
private bool ShouldSerializeCrafter() => !string.IsNullOrEmpty(_crafter);
[InvalidateProperties]
[SerializableField(9)]
@ -907,8 +907,13 @@ namespace Server.Items
public override void OnSingleClick(Mobile from)
{
var attrs = new List<EquipInfoAttribute>();
if (!Core.UOTD)
{
OnSingleClickPreUOTD(from);
return;
}
var attrs = new List<EquipInfoAttribute>();
AddEquipInfoAttributes(from, attrs);
int number;
@ -931,6 +936,30 @@ namespace Server.Items
from.NetState.SendDisplayEquipmentInfo(Serial, number, _crafter, false, attrs);
}
public void OnSingleClickPreUOTD(Mobile from)
{
var name = Name;
if (name == null)
{
var articleAnName = (TileData.ItemTable[ItemID].Flags & TileFlag.ArticleAn) != 0;
name = $"{(articleAnName ? "an" : "a")} {Localization.GetText(LabelNumber).ToLowerInvariant()}";
}
if (Crafter == null)
{
LabelTo(from, Quality == ClothingQuality.Exceptional ? $"{name} of exceptional quality" : name);
return;
}
LabelTo(
from,
Quality == ClothingQuality.Exceptional
? $"{name} crafted with exceptional quality by {Crafter}"
: $"{name} crafted by {Crafter}"
);
}
public virtual void AddEquipInfoAttributes(Mobile from, List<EquipInfoAttribute> attrs)
{
if (DisplayLootType)

View file

@ -19,7 +19,11 @@ public abstract partial class FillableContainer : LockableContainer
}
}
public FillableContainer(int itemID) : base(itemID) => Movable = false;
public FillableContainer(int itemID) : base(itemID)
{
Movable = false;
_contentType = FillableContentType.None;
}
public virtual int MinRespawnMinutes => 60;
public virtual int MaxRespawnMinutes => 90;

View file

@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using ModernUO.CodeGeneratedEvents;
using ModernUO.Serialization;
using Server.Collections;
using Server.Engines.Plants;
@ -711,10 +713,9 @@ public abstract partial class BaseBeverage : Item, IHasQuantity
_quantity = reader.ReadInt();
}
public static void OnLogin(Mobile m)
{
CheckHeaveTimer(m);
}
[OnEvent(nameof(PlayerMobile.PlayerLoginEvent))]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void OnLogin(PlayerMobile pm) => CheckHeaveTimer(pm);
public static void CheckHeaveTimer(Mobile from)
{

View file

@ -50,7 +50,7 @@ public partial class BaseQuiver : Container, ICraftable, IAosItem
private string _crafter;
[SerializableFieldSaveFlag(4)]
private bool ShouldSerializeCrafter() => _crafter != null;
private bool ShouldSerializeCrafter() => !string.IsNullOrEmpty(_crafter);
[InvalidateProperties]
[SerializableField(5)]

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using ModernUO.Serialization;
using Server.Network;
using Server.Spells;
@ -168,6 +169,12 @@ public abstract partial class BaseWand : BaseBashing
public override void OnSingleClick(Mobile from)
{
if (!Core.UOTD)
{
OnSingleClickPreUOTD(from);
return;
}
var attrs = new List<EquipInfoAttribute>();
if (DisplayLootType)
@ -230,6 +237,45 @@ public abstract partial class BaseWand : BaseBashing
from.NetState.SendDisplayEquipmentInfo(Serial, number, Crafter, false, attrs);
}
public void OnSingleClickPreUOTD(Mobile from)
{
var isMagicItem = _charges > 0;
var name = Name;
if (name == null)
{
var articleAnName = (TileData.ItemTable[ItemID].Flags & TileFlag.ArticleAn) != 0;
name = $"{(articleAnName ? "an" : "a")} {Localization.GetText(LabelNumber).ToLowerInvariant()}";
}
if (isMagicItem && !Identified)
{
LabelTo(from, $"an unidentified {name}");
return;
}
LabelTo(from, isMagicItem ? $"{name} of {WandEffectText}" : name);
}
private string WandEffectText
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _wandEffect switch
{
WandEffect.Clumsiness => "clumsiness",
WandEffect.Identification => "identification",
WandEffect.Healing => "healing",
WandEffect.Feeblemindedness => "feeblemindedness",
WandEffect.Weakness => "weakness",
WandEffect.MagicArrow => "magic arrow",
WandEffect.Harming => "harming",
WandEffect.Fireball => "fireball",
WandEffect.GreaterHealing => "greater healing",
WandEffect.Lightning => "lightning",
WandEffect.ManaDraining => "mana draining",
_ => null
};
}
public void Cast(Spell spell)
{
var m = Movable;

View file

@ -17,6 +17,7 @@ using Server.Spells.Necromancy;
using Server.Spells.Ninjitsu;
using Server.Spells.Sixth;
using Server.Spells.Spellweaving;
using Server.Text;
namespace Server.Items;
@ -27,7 +28,8 @@ public interface ISlayer
}
[SerializationGenerator(10, false)]
public abstract partial class BaseWeapon : Item, IWeapon, IFactionItem, ICraftable, ISlayer, IDurability, IAosItem
public abstract partial class BaseWeapon
: Item, IWeapon, IFactionItem, ICraftable, ISlayer, IDurability, IAosItem, IIdentifiable
{
private static bool _enableInstaHit;
@ -104,7 +106,7 @@ public abstract partial class BaseWeapon : Item, IWeapon, IFactionItem, ICraftab
[SerializableFieldSaveFlag(9)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool ShouldSerializeCrafter() => string.IsNullOrEmpty(_crafter);
private bool ShouldSerializeCrafter() => !string.IsNullOrEmpty(_crafter);
[InvalidateProperties]
[SerializableField(10)]
@ -673,6 +675,7 @@ public abstract partial class BaseWeapon : Item, IWeapon, IFactionItem, ICraftab
}
PlayerConstructed = true;
Identified = true;
var resourceType = typeRes ?? craftItem.Resources[0].ItemType;
@ -728,21 +731,18 @@ public abstract partial class BaseWeapon : Item, IWeapon, IFactionItem, ICraftab
{
case CraftResource.DullCopper:
{
Identified = true;
DurabilityLevel = WeaponDurabilityLevel.Durable;
AccuracyLevel = WeaponAccuracyLevel.Accurate;
break;
}
case CraftResource.ShadowIron:
{
Identified = true;
DurabilityLevel = WeaponDurabilityLevel.Durable;
DamageLevel = WeaponDamageLevel.Ruin;
break;
}
case CraftResource.Copper:
{
Identified = true;
DurabilityLevel = WeaponDurabilityLevel.Fortified;
DamageLevel = WeaponDamageLevel.Ruin;
AccuracyLevel = WeaponAccuracyLevel.Surpassingly;
@ -750,7 +750,6 @@ public abstract partial class BaseWeapon : Item, IWeapon, IFactionItem, ICraftab
}
case CraftResource.Bronze:
{
Identified = true;
DurabilityLevel = WeaponDurabilityLevel.Fortified;
DamageLevel = WeaponDamageLevel.Might;
AccuracyLevel = WeaponAccuracyLevel.Surpassingly;
@ -758,7 +757,6 @@ public abstract partial class BaseWeapon : Item, IWeapon, IFactionItem, ICraftab
}
case CraftResource.Gold:
{
Identified = true;
DurabilityLevel = WeaponDurabilityLevel.Indestructible;
DamageLevel = WeaponDamageLevel.Force;
AccuracyLevel = WeaponAccuracyLevel.Eminently;
@ -766,7 +764,6 @@ public abstract partial class BaseWeapon : Item, IWeapon, IFactionItem, ICraftab
}
case CraftResource.Agapite:
{
Identified = true;
DurabilityLevel = WeaponDurabilityLevel.Indestructible;
DamageLevel = WeaponDamageLevel.Power;
AccuracyLevel = WeaponAccuracyLevel.Eminently;
@ -774,7 +771,6 @@ public abstract partial class BaseWeapon : Item, IWeapon, IFactionItem, ICraftab
}
case CraftResource.Verite:
{
Identified = true;
DurabilityLevel = WeaponDurabilityLevel.Indestructible;
DamageLevel = WeaponDamageLevel.Power;
AccuracyLevel = WeaponAccuracyLevel.Exceedingly;
@ -782,7 +778,6 @@ public abstract partial class BaseWeapon : Item, IWeapon, IFactionItem, ICraftab
}
case CraftResource.Valorite:
{
Identified = true;
DurabilityLevel = WeaponDurabilityLevel.Indestructible;
DamageLevel = WeaponDamageLevel.Vanq;
AccuracyLevel = WeaponAccuracyLevel.Supremely;
@ -3332,6 +3327,12 @@ public abstract partial class BaseWeapon : Item, IWeapon, IFactionItem, ICraftab
public override void OnSingleClick(Mobile from)
{
if (!Core.UOTD)
{
OnSingleClickPreUOTD(from);
return;
}
var attrs = new List<EquipInfoAttribute>();
if (DisplayLootType)
@ -3423,6 +3424,130 @@ public abstract partial class BaseWeapon : Item, IWeapon, IFactionItem, ICraftab
from.NetState.SendDisplayEquipmentInfo(Serial, number, _crafter, false, attrs);
}
public virtual void OnSingleClickPreUOTD(Mobile from)
{
var isMagicItem = _durabilityLevel > WeaponDurabilityLevel.Regular ||
_accuracyLevel > WeaponAccuracyLevel.Regular ||
_damageLevel > WeaponDamageLevel.Regular ||
_slayer != SlayerName.None;
if (isMagicItem && !_identified)
{
LabelTo(from, $"an unidentified {Name ?? Localization.GetText(LabelNumber).ToLowerInvariant()}");
return;
}
var name = Name;
var articleAnName = (TileData.ItemTable[ItemID].Flags & TileFlag.ArticleAn) != 0;
if (isMagicItem)
{
var builder = ValueStringBuilder.Create(128);
var durabilityText = DurabilityText(out var articleAnDurability);
if (durabilityText != null)
{
builder.AppendSpaceWithArticle(durabilityText, articleAnDurability);
}
var accuracyText = AccuracyText(out var articleAnAccuracy);
if (accuracyText != null)
{
builder.AppendSpaceWithArticle(accuracyText, articleAnAccuracy);
}
var slayerEntry = SlayerGroup.GetEntryByName(_slayer);
if (slayerEntry != null)
{
builder.AppendSpaceWithArticle(slayerEntry.SlayerText(out var articleAnSlayer), articleAnSlayer);
}
if (name == null)
{
builder.AppendSpaceWithArticle(Localization.GetText(LabelNumber).ToLowerInvariant(), articleAnName);
}
else if (builder.Length != 0)
{
builder.Append($" {name}");
}
else
{
builder.Append(name);
}
var weaponDamageText = WeaponDamageText;
if (weaponDamageText != null)
{
builder.Append($" of {weaponDamageText}");
}
// TODO: Spells (of Ghoul's Touch)
LabelTo(from, builder.ToString());
builder.Dispose();
return;
}
name ??= $"{(articleAnName ? "an" : "a")} {Localization.GetText(LabelNumber).ToLowerInvariant()}";
if (Crafter == null)
{
LabelTo(from, Quality == WeaponQuality.Exceptional ? $"{name} of exceptional quality" : name);
return;
}
LabelTo(
from,
Quality == WeaponQuality.Exceptional
? $"{name} crafted with exceptional quality by {Crafter}"
: $"{name} crafted by {Crafter}"
);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private string DurabilityText(out bool articleAn)
{
articleAn = _durabilityLevel is WeaponDurabilityLevel.Indestructible;
return _durabilityLevel switch
{
WeaponDurabilityLevel.Durable => "durable",
WeaponDurabilityLevel.Substantial => "substantial",
WeaponDurabilityLevel.Massive => "massive",
WeaponDurabilityLevel.Fortified => "fortified",
WeaponDurabilityLevel.Indestructible => "indestructible",
_ => null
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private string AccuracyText(out bool articleAn)
{
articleAn = _accuracyLevel is WeaponAccuracyLevel.Accurate or WeaponAccuracyLevel.Eminently;
return _accuracyLevel switch
{
WeaponAccuracyLevel.Accurate => "accurate",
WeaponAccuracyLevel.Surpassingly => "surpassingly accurate",
WeaponAccuracyLevel.Eminently => "eminently accurate",
WeaponAccuracyLevel.Exceedingly => "exceedingly accurate",
WeaponAccuracyLevel.Supremely => "supremely accurate",
_ => null
};
}
private string WeaponDamageText
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _damageLevel switch
{
WeaponDamageLevel.Ruin => "ruin",
WeaponDamageLevel.Might => "might",
WeaponDamageLevel.Force => "force",
WeaponDamageLevel.Power => "power",
WeaponDamageLevel.Vanq => "vanquishing",
_ => null
};
}
public virtual int GetHitAttackSound(Mobile attacker, Mobile defender)
{
var sound = attacker.GetAttackSound();

View file

@ -98,5 +98,10 @@ namespace Server.Items
Effects.PlaySound(endLoc, map, Utility.Random(0x11B, 4));
Effects.SendLocationEffect(endLoc, map, 0x373A + 0x10 * Utility.Random(4), 16, 10, hue, renderMode);
}
public override void OnSingleClickPreUOTD(Mobile from)
{
LabelTo(from, Name ?? Localization.GetText(LabelNumber).ToLowerInvariant());
}
}
}

View file

@ -88,6 +88,20 @@ namespace Server.Items
}
}
public string SlayerText(out bool articleAn)
{
if (Name == SlayerName.None)
{
articleAn = false;
return null;
}
articleAn = Name is SlayerName.OrcSlaying or SlayerName.OgreTrashing or SlayerName.Exorcism or SlayerName.Ophidian
or SlayerName.ArachnidDoom or SlayerName.ElementalBan or SlayerName.ElementalHealth or SlayerName.EarthShatter;
return Localization.GetText(Title)?.ToLowerInvariant();
}
public bool Slays(Mobile m)
{
var t = m.GetType();

View file

@ -1,4 +1,5 @@
using System;
using ModernUO.CodeGeneratedEvents;
using Server.Mobiles;
using Server.Network;
@ -108,17 +109,15 @@ namespace Server
Enabled = ServerConfiguration.GetOrUpdateSetting("buffIcons.enable", Core.ML);
}
public static void ResendBuffsOnClientVersionReceived(NetState ns, ClientVersion cv)
[OnEvent(nameof(PlayerMobile.PlayerLoginEvent))]
public static void OnLogin(PlayerMobile pm)
{
if (!Enabled)
{
return;
}
if (ns.Mobile is PlayerMobile pm)
{
Timer.StartTimer(pm.ResendBuffs);
}
pm.ResendBuffs();
}
public static void AddBuff(Mobile m, BuffInfo b)

View file

@ -1,5 +1,7 @@
using System;
using ModernUO.CodeGeneratedEvents;
using Server.Items;
using Server.Mobiles;
using Server.Network;
namespace Server
@ -55,10 +57,8 @@ namespace Server
}
}
public static void OnLogin(Mobile m)
{
m.CheckLightLevels(true);
}
[OnEvent(nameof(PlayerMobile.PlayerLoginEvent))]
public static void OnLogin(PlayerMobile pm) => pm.CheckLightLevels(true);
public static int ComputeLevelFor(Mobile from)
{

View file

@ -1,17 +1,18 @@
using Server.Mobiles;
using Server.Network;
namespace Server.Misc
{
public static class LoginStats
{
public static void OnLogin(Mobile m)
public static void OnLogin(PlayerMobile pm)
{
var userCount = NetState.Instances.Count;
var itemCount = World.Items.Count;
var mobileCount = World.Mobiles.Count;
m.SendMessage(
$"Welcome, {m.Name}! There {(userCount == 1 ? "is" : "are")} currently {userCount} user{(userCount == 1 ? "" : "s")} " +
pm.SendMessage(
$"Welcome, {pm.Name}! There {(userCount == 1 ? "is" : "are")} currently {userCount} user{(userCount == 1 ? "" : "s")} " +
$"online, with {itemCount} item{(itemCount == 1 ? "" : "s")} and {mobileCount} mobile{(mobileCount == 1 ? "" : "s")} in the world."
);
}

View file

@ -2,8 +2,10 @@ using System;
using System.Collections.Generic;
using System.Net;
using System.Text.RegularExpressions;
using ModernUO.CodeGeneratedEvents;
using ModernUO.Serialization;
using Server.Gumps;
using Server.Mobiles;
using Server.Network;
using Server.Prompts;
@ -125,14 +127,15 @@ public partial class ShardPoller : Item
Options[^1] = option;
}
public static void OnLogin(Mobile m)
[OnEvent(nameof(PlayerMobile.PlayerLoginEvent))]
public static void OnLogin(PlayerMobile pm)
{
if (_activePollers.Count == 0)
{
return;
}
Timer.StartTimer(TimeSpan.FromSeconds(1.0), () => EventSink_Login_Callback(m));
Timer.StartTimer(TimeSpan.FromSeconds(1.0), () => EventSink_Login_Callback(pm));
}
private static void EventSink_Login_Callback(Mobile from)

View file

@ -82,17 +82,19 @@ public static class StaminaSystem
RemoveEntry(m as IHasSteps);
}
public static void OnLogin(Mobile m)
[OnEvent(nameof(PlayerMobile.PlayerLoginEvent))]
public static void OnLogin(PlayerMobile pm)
{
bool exists;
if (EnableMountStamina)
{
// Start idle for mount
ref var stepsTaken = ref GetStepsTaken(m.Mount, out var exists);
ref var stepsTaken = ref GetStepsTaken(pm.Mount, out exists);
if (exists)
{
if (stepsTaken.Steps <= 0 || Core.Now >= stepsTaken.IdleStartTime + ResetDuration)
{
_stepsTaken.Remove(m.Mount);
_stepsTaken.Remove(pm.Mount);
}
else
{
@ -100,16 +102,13 @@ public static class StaminaSystem
}
}
_resetHash.Remove(m.Mount);
_resetHash.Remove(pm.Mount);
}
if (m is PlayerMobile pm)
ref var regenStepsTaken = ref RegenSteps(pm, out exists);
if (exists)
{
ref var stepsTaken = ref RegenSteps(pm, out var exists);
if (exists)
{
stepsTaken.IdleStartTime = Core.Now;
}
regenStepsTaken.IdleStartTime = Core.Now;
}
}

View file

@ -1220,6 +1220,10 @@ namespace Server.Mobiles
}
}
[GeneratedEvent(nameof(PlayerLoginEvent))]
public static partial void PlayerLoginEvent(PlayerMobile pm);
[OnEvent(nameof(PlayerLoginEvent))]
public static void OnLogin(PlayerMobile from)
{
if (AccountHandler.LockdownLevel > AccessLevel.Player)
@ -4474,20 +4478,39 @@ namespace Server.Mobiles
public void SendAddBuffPacket(BuffInfo buffInfo)
{
if (buffInfo == null)
if (buffInfo == null || NetState?.BuffIcon != true)
{
return;
}
// Synchronize the buff icon as close to _on the second_ as we can.
var msecs = buffInfo.TimeLength.Milliseconds;
if (msecs >= 8)
{
Timer.DelayCall(TimeSpan.FromMilliseconds(msecs), () =>
{
// They are still online, we still have the buff icon in the table, and it is the same buff icon
if (NetState != null && m_BuffTable?.GetValueOrDefault(buffInfo.ID) == buffInfo)
{
SendAddBuffPacket(buffInfo, (long)buffInfo.TimeLength.TotalMilliseconds - msecs);
}
});
}
else
{
SendAddBuffPacket(buffInfo, (long)buffInfo.TimeLength.TotalMilliseconds);
}
}
private void SendAddBuffPacket(BuffInfo buffInfo, long ticks)
{
NetState.SendAddBuffPacket(
Serial,
buffInfo.ID,
buffInfo.TitleCliloc,
buffInfo.SecondaryCliloc,
buffInfo.Args,
buffInfo.TimeStart == 0
? 0
: Math.Max(buffInfo.TimeStart + (long)buffInfo.TimeLength.TotalMilliseconds - Core.TickCount, 0)
ticks
);
}
@ -4512,29 +4535,9 @@ namespace Server.Mobiles
RemoveBuff(b); // Check & subsequently remove the old one.
m_BuffTable ??= new Dictionary<BuffIcon, BuffInfo>();
m_BuffTable.Add(b.ID, b);
if (NetState?.BuffIcon == true)
{
// Synchronize the buff icon as close to _on the second_ as we can.
var msecs = b.TimeLength.Milliseconds;
if (msecs >= 8)
{
Timer.DelayCall(TimeSpan.FromMilliseconds(msecs), (buffInfo, pm) =>
{
// They are still online, we still have the buff icon in the table, and it is the same buff icon
if (pm.NetState != null && pm.m_BuffTable?.GetValueOrDefault(buffInfo.ID) == buffInfo)
{
pm.SendAddBuffPacket(buffInfo);
}
}, b, this);
}
else
{
SendAddBuffPacket(b);
}
}
SendAddBuffPacket(b);
}
public void RemoveBuff(BuffInfo b)

View file

@ -1,3 +1,6 @@
using ModernUO.CodeGeneratedEvents;
using Server.Mobiles;
namespace Server.Misc
{
public static class Strandedness
@ -112,7 +115,8 @@ namespace Server.Misc
return false;
}
public static void OnLogin(Mobile from)
[OnEvent(nameof(PlayerMobile.PlayerLoginEvent))]
public static void OnLogin(PlayerMobile from)
{
if (!IsStranded(from))
{

View file

@ -17,23 +17,9 @@ using System;
using System.Buffers;
using System.Collections.Generic;
using System.IO;
using Server.Accounting;
using Server.Assistants;
using Server.Commands;
using Server.Engines.CharacterCreation;
using Server.Engines.ConPVP;
using Server.Engines.Doom;
using Server.Engines.PartySystem;
using Server.Engines.Plants;
using Server.Engines.PlayerMurderSystem;
using Server.Engines.VeteranRewards;
using Server.Factions;
using Server.Items;
using Server.Misc;
using Server.Mobiles;
using Server.Regions;
using Server.Spells.Ninjitsu;
using Server.Spells.Spellweaving;
namespace Server.Network;
@ -314,34 +300,10 @@ public static class IncomingAccountPackets
state.SendPlayMusic(m.Region.Music);
StaminaSystem.OnLogin(m);
DuelContext.OnLogin(m);
LightCycle.OnLogin(m);
LoginStats.OnLogin(m);
AnimalForm.OnLogin(m);
BaseBeverage.OnLogin(m);
AntiMacroSystem.OnLogin(m);
Strandedness.OnLogin(m);
ShardPoller.OnLogin(m);
ReaperFormSpell.OnLogin(m);
Party.OnLogin(m);
PlantSystem.OnLogin(m);
LampRoomRegion.OnLogin(m);
HouseRegion.OnLogin(m);
Faction.OnLogin(m);
PlayerMurderSystem.OnLogin(m);
AssistantHandler.OnLogin(m);
VisibilityList.OnLogin(m);
if (m is PlayerMobile pm)
{
PlayerMobile.OnLogin(pm);
PlayerMobile.PlayerLoginEvent(pm);
}
Account.OnLogin(m);
GiftGiving.OnLogin(m);
PreventInaccess.OnLogin(m);
TwistedWealdDesertRegion.OnLogin(m);
RewardSystem.OnLogin(m);
}
private static int GenerateAuthID(this NetState state)

View file

@ -1,4 +1,5 @@
using System;
using ModernUO.CodeGeneratedEvents;
using Server.Gumps;
using Server.Items;
using Server.Mobiles;
@ -29,13 +30,14 @@ public class HouseRegion : BaseRegion
{
}
public static void OnLogin(Mobile m)
[OnEvent(nameof(PlayerMobile.PlayerLoginEvent))]
public static void OnLogin(PlayerMobile pm)
{
var house = BaseHouse.FindHouseAt(m);
var house = BaseHouse.FindHouseAt(pm);
if (house?.Public == false && !house.IsFriend(m))
if (house?.Public == false && !house.IsFriend(pm))
{
m.Location = house.BanLocation;
pm.Location = house.BanLocation;
}
}

View file

@ -1,4 +1,6 @@
using System.Text.Json.Serialization;
using ModernUO.CodeGeneratedEvents;
using Server.Mobiles;
using Server.Network;
using Server.Spells;
using Server.Spells.Ninjitsu;
@ -41,11 +43,12 @@ public class TwistedWealdDesertRegion : MondainRegion
}
}
public static void OnLogin(Mobile m)
[OnEvent(nameof(PlayerMobile.PlayerLoginEvent))]
public static void OnLogin(PlayerMobile pm)
{
if (m.Region.IsPartOf<TwistedWealdDesertRegion>() && m.AccessLevel == AccessLevel.Player)
if (pm.AccessLevel == AccessLevel.Player && pm.Region.IsPartOf<TwistedWealdDesertRegion>())
{
m.NetState.SendSpeedControl(SpeedControlSetting.Walk);
pm.NetState.SendSpeedControl(SpeedControlSetting.Walk);
}
}
}

View file

@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Text.Json.Serialization;
using ModernUO.CodeGeneratedEvents;
using Server.Collections;
using Server.Json;
using Server.Mobiles;
@ -138,10 +139,11 @@ public static class AntiMacroSystem
}
}
public static void OnLogin(Mobile m)
[OnEvent(nameof(PlayerMobile.PlayerLoginEvent))]
public static void OnLogin(PlayerMobile pm)
{
// Stop the clear out timer
if (_logoutCleanup?.Remove(m, out var timer) == true)
if (_logoutCleanup?.Remove(pm, out var timer) == true)
{
timer.Stop();
}

View file

@ -2,62 +2,62 @@ using System;
using Server.Mobiles;
using Server.Targeting;
namespace Server.Items
namespace Server.Items;
public interface IIdentifiable
{
public static class ItemIdentification
bool Identified { get; set; }
}
public static class ItemIdentification
{
public static void Initialize()
{
public static void Initialize()
SkillInfo.Table[(int)SkillName.ItemID].Callback = OnUse;
}
public static TimeSpan OnUse(Mobile from)
{
from.SendLocalizedMessage(500343); // What do you wish to appraise and identify?
from.Target = new InternalTarget();
return TimeSpan.FromSeconds(1.0);
}
[PlayerVendorTarget]
private class InternalTarget : Target
{
public InternalTarget() : base(8, false, TargetFlags.None) => AllowNonlocal = true;
protected override void OnTarget(Mobile from, object o)
{
SkillInfo.Table[(int)SkillName.ItemID].Callback = OnUse;
}
public static TimeSpan OnUse(Mobile from)
{
from.SendLocalizedMessage(500343); // What do you wish to appraise and identify?
from.Target = new InternalTarget();
return TimeSpan.FromSeconds(1.0);
}
[PlayerVendorTarget]
private class InternalTarget : Target
{
public InternalTarget() : base(8, false, TargetFlags.None) => AllowNonlocal = true;
protected override void OnTarget(Mobile from, object o)
if (o is Mobile mobile)
{
if (o is Item item)
{
if (from.CheckTargetSkill(SkillName.ItemID, item, 0, 100))
{
if (item is BaseWeapon weapon)
{
weapon.Identified = true;
}
else if (item is BaseArmor armor)
{
armor.Identified = true;
}
mobile.OnSingleClick(from);
return;
}
if (!Core.AOS)
{
item.OnSingleClick(from);
}
}
else
{
from.SendLocalizedMessage(500353); // You are not certain...
}
}
else if (o is Mobile mobile)
bool identified = false;
if (o is Item item)
{
if (item is IIdentifiable identifiable && from.CheckTargetSkill(SkillName.ItemID, item, 0, 100))
{
mobile.OnSingleClick(from);
identifiable.Identified = true;
identified = true;
}
else
if (!Core.AOS)
{
from.SendLocalizedMessage(500353); // You are not certain...
item.OnSingleClick(from);
}
}
if (!identified)
{
from.SendLocalizedMessage(500353); // You are not certain...
}
}
}
}

View file

@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using ModernUO.CodeGeneratedEvents;
using Server.Accounting;
using Server.Mobiles;
namespace Server.Misc
{
@ -19,9 +21,10 @@ namespace Server.Misc
m_Givers.Add(giver);
}
public static void OnLogin(Mobile m)
[OnEvent(nameof(PlayerMobile.PlayerLoginEvent))]
public static void OnLogin(PlayerMobile pm)
{
if (m.Account is not Account acct)
if (pm.Account is not Account acct)
{
return;
}
@ -47,7 +50,7 @@ namespace Server.Misc
continue; // already got one
}
giver.DelayGiveGift(TimeSpan.FromSeconds(5.0), m);
giver.DelayGiveGift(TimeSpan.FromSeconds(5.0), pm);
}
acct.LastLogin = now;

View file

@ -1,4 +1,6 @@
using System.Collections.Generic;
using ModernUO.CodeGeneratedEvents;
using Server.Mobiles;
namespace Server.Misc
{
@ -34,7 +36,8 @@ namespace Server.Misc
m_MoveHistory = new Dictionary<Mobile, LocationInfo>();
}
public static void OnLogin(Mobile from)
[OnEvent(nameof(PlayerMobile.PlayerLoginEvent))]
public static void OnLogin(PlayerMobile from)
{
if (!Enabled)
{

View file

@ -64,11 +64,12 @@ public class AnimalForm : NinjaSpell
new(typeof(Reptalon), 1075202, 11669, 0, 1075222, 90.0, 0x114, 0, 0, false, false)
};
public static void OnLogin(Mobile m)
[OnEvent(nameof(PlayerMobile.PlayerLoginEvent))]
public static void OnLogin(PlayerMobile pm)
{
if (GetContext(m)?.SpeedBoost == true)
if (GetContext(pm)?.SpeedBoost == true)
{
m.NetState.SendSpeedControl(SpeedControlSetting.Mount);
pm.NetState.SendSpeedControl(SpeedControlSetting.Mount);
}
}

View file

@ -1,4 +1,6 @@
using System;
using ModernUO.CodeGeneratedEvents;
using Server.Mobiles;
using Server.Network;
namespace Server.Spells.Spellweaving
@ -27,13 +29,14 @@ namespace Server.Spells.Spellweaving
public virtual int SwingSpeedBonus => 10 + FocusLevel;
public virtual int SpellDamageBonus => 10 + FocusLevel;
public static void OnLogin(Mobile m)
[OnEvent(nameof(PlayerMobile.PlayerLoginEvent))]
public static void OnLogin(PlayerMobile pm)
{
var context = TransformationSpellHelper.GetContext(m);
var context = TransformationSpellHelper.GetContext(pm);
if (context?.Type == typeof(ReaperFormSpell))
{
m.NetState.SendSpeedControl(SpeedControlSetting.Walk);
pm.NetState.SendSpeedControl(SpeedControlSetting.Walk);
}
}