Reorganizes Project (#41)

This commit is contained in:
Kamron Batman 2019-08-02 18:13:40 -07:00 committed by GitHub
parent 08bf44af9a
commit 3614a66aee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3499 changed files with 79 additions and 55 deletions

1495
Projects/Scripts/Misc/AOS.cs Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,39 @@
using System;
using Server.Accounting;
namespace Server.Misc
{
public class AccountPrompt
{
public static void Initialize()
{
if (Accounts.Count == 0 && !Core.Service)
{
Console.WriteLine("This server has no accounts.");
Console.Write("Do you want to create the owner account now? (y/n)");
if (Console.ReadKey(true).Key == ConsoleKey.Y)
{
Console.WriteLine();
Console.Write("Username: ");
string username = Console.ReadLine();
Console.Write("Password: ");
string password = Console.ReadLine();
Account a = new Account(username, password);
a.AccessLevel = AccessLevel.Owner;
Console.WriteLine("Account created.");
}
else
{
Console.WriteLine();
Console.WriteLine("Account not created.");
}
}
}
}
}

View file

@ -0,0 +1,31 @@
namespace Server.Misc
{
public class Animations
{
public static void Initialize()
{
EventSink.AnimateRequest += EventSink_AnimateRequest;
}
private static void EventSink_AnimateRequest(AnimateRequestEventArgs e)
{
Mobile from = e.Mobile;
int action;
switch (e.Action)
{
case "bow":
action = 32;
break;
case "salute":
action = 33;
break;
default: return;
}
if (from.Alive && !from.Mounted && from.Body.IsHuman)
from.Animate(action, 5, 1, true, false, 0);
}
}
}

View file

@ -0,0 +1,182 @@
using System;
using System.Collections.Generic;
using Server.Gumps;
using Server.Network;
namespace Server.Misc
{
public static class Assistants
{
private static class Settings
{
[Flags]
public enum Features : ulong
{
None = 0,
FilterWeather = 1 << 0, // Weather Filter
FilterLight = 1 << 1, // Light Filter
SmartTarget = 1 << 2, // Smart Last Target
RangedTarget = 1 << 3, // Range Check Last Target
AutoOpenDoors = 1 << 4, // Automatically Open Doors
DequipOnCast = 1 << 5, // Unequip Weapon on spell cast
AutoPotionEquip = 1 << 6, // Un/re-equip weapon on potion use
PoisonedChecks = 1 << 7, // Block heal If poisoned/Macro If Poisoned condition/Heal or Cure self
LoopedMacros = 1 << 8, // Disallow looping or recursive macros
UseOnceAgent = 1 << 9, // The use once agent
RestockAgent = 1 << 10, // The restock agent
SellAgent = 1 << 11, // The sell agent
BuyAgent = 1 << 12, // The buy agent
PotionHotkeys = 1 << 13, // All potion hotkeys
RandomTargets = 1 << 14, // All random target hotkeys (not target next, last target, target self)
ClosestTargets = 1 << 15, // All closest target hotkeys
OverheadHealth = 1 << 16, // Health and Mana/Stam messages shown over player's heads
AutolootAgent = 1 << 17, // The autoloot agent
BoneCutterAgent = 1 << 18, // The bone cutter agent
AdvancedMacros = 1 << 19, // Advanced macro engine
AutoRemount = 1 << 20, // Auto remount after dismount
AutoBandage = 1 << 21, // Auto bandage friends, self, last and mount option
EnemyTargetShare = 1 << 22, // Enemy target share on guild, party or alliance chat
FilterSeason = 1 << 23, // Season Filter
SpellTargetShare = 1 << 24, // Spell target share on guild, party or alliance chat
All = ulong.MaxValue
}
public const bool Enabled = false;
public const bool KickOnFailure = true; // It will also kick clients running without assistants
public const string WarningMessage = "The server was unable to negotiate features with your assistant. "
+ "You must download and run an updated version of <A HREF=\"http://uosteam.com\">UOSteam</A>"
+ " or <A HREF=\"https://bitbucket.org/msturgill/razor-releases/downloads\">Razor</A>."
+ "<BR><BR>Make sure you've checked the option <B>Negotiate features with server</B>, "
+ "once you have this box checked you may log in and play normally."
+ "<BR><BR>You will be disconnected shortly.";
public static readonly TimeSpan HandshakeTimeout = TimeSpan.FromSeconds(30.0);
public static readonly TimeSpan DisconnectDelay = TimeSpan.FromSeconds(15.0);
public static Features DisallowedFeatures{ get; private set; } = Features.None;
public static void Configure()
{
//DisallowFeature( Features.FilterWeather );
}
public static void DisallowFeature(Features feature)
{
SetDisallowed(feature, true);
}
public static void AllowFeature(Features feature)
{
SetDisallowed(feature, false);
}
public static void SetDisallowed(Features feature, bool value)
{
if (value)
DisallowedFeatures |= feature;
else
DisallowedFeatures &= ~feature;
}
}
private static class Negotiator
{
private static Dictionary<Mobile, Timer> m_Dictionary = new Dictionary<Mobile, Timer>();
public static void Initialize()
{
/* if (Settings.Enabled)
{
EventSink.Login += EventSink_Login;
ProtocolExtensions.Register(0xFF, true, OnHandshakeResponse);
}*/
}
private static void EventSink_Login(LoginEventArgs e)
{
Mobile m = e.Mobile;
if (m?.NetState != null && m.NetState.Running)
{
m.Send(new BeginHandshake());
if (Settings.KickOnFailure)
m.Send(new BeginHandshake());
if (m_Dictionary.TryGetValue(m, out Timer t))
t.Stop();
m_Dictionary[m] = t = Timer.DelayCall(Settings.HandshakeTimeout, OnHandshakeTimeout, m);
t.Start();
}
}
private static void OnHandshakeResponse(NetState state, PacketReader pvSrc)
{
pvSrc.Trace(state);
if (state?.Mobile == null || !state.Running)
return;
Mobile m = state.Mobile;
if (m_Dictionary.TryGetValue(m, out Timer t))
{
t.Stop();
m_Dictionary.Remove(m);
}
}
private static void OnHandshakeTimeout(Mobile m)
{
if (m == null)
return;
m_Dictionary.Remove(m);
// if (!Settings.KickOnFailure)
// {
// Console.WriteLine("Player '{0}' failed to negotiate features.", m);
// }
if (m.NetState?.Running == true)
{
m.SendGump(new WarningGump(1060635, 30720, Settings.WarningMessage, 0xFFC000, 420, 250));
if (m.AccessLevel <= AccessLevel.Player)
{
Timer t;
m_Dictionary[m] = t = Timer.DelayCall(Settings.DisconnectDelay, OnForceDisconnect, m);
t.Start();
}
}
}
private static void OnForceDisconnect(Mobile m)
{
if (m == null)
return;
if (m.NetState != null && m.NetState.Running)
m.NetState.Dispose();
m_Dictionary.Remove(m);
Console.WriteLine("Player {0} kicked (Failed assistant handshake)", m);
}
private sealed class BeginHandshake : ProtocolExtension
{
public BeginHandshake()
: base(0xFE, 8)
{
m_Stream.Write((uint)((ulong)Settings.DisallowedFeatures >> 32));
m_Stream.Write((uint)((ulong)Settings.DisallowedFeatures & 0xFFFFFFFF));
}
}
}
}
}

View file

@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
using Server.Network;
namespace Server.Misc
{
public class AttackMessage
{
private const string AggressorFormat = "You are attacking {0}!";
private const string AggressedFormat = "{0} is attacking you!";
private const int Hue = 0x22;
private static TimeSpan Delay = TimeSpan.FromMinutes(1.0);
public static void Initialize()
{
EventSink.AggressiveAction += EventSink_AggressiveAction;
}
public static void EventSink_AggressiveAction(AggressiveActionEventArgs e)
{
Mobile aggressor = e.Aggressor;
Mobile aggressed = e.Aggressed;
if (!aggressor.Player || !aggressed.Player)
return;
if (!CheckAggressions(aggressor, aggressed))
{
aggressor.LocalOverheadMessage(MessageType.Regular, Hue, true,
string.Format(AggressorFormat, aggressed.Name));
aggressed.LocalOverheadMessage(MessageType.Regular, Hue, true,
string.Format(AggressedFormat, aggressor.Name));
}
}
public static bool CheckAggressions(Mobile m1, Mobile m2)
{
List<AggressorInfo> list = m1.Aggressors;
for (int i = 0; i < list.Count; ++i)
{
AggressorInfo info = list[i];
if (info.Attacker == m2 && DateTime.UtcNow < info.LastCombatTime + Delay)
return true;
}
list = m2.Aggressors;
for (int i = 0; i < list.Count; ++i)
{
AggressorInfo info = list[i];
if (info.Attacker == m1 && DateTime.UtcNow < info.LastCombatTime + Delay)
return true;
}
return false;
}
}
}

View file

@ -0,0 +1,84 @@
using System;
using Server.Commands;
namespace Server.Misc
{
public class AutoRestart : Timer
{
public static bool Enabled; // is the script enabled?
private static TimeSpan RestartTime = TimeSpan.FromHours(2.0); // time of day at which to restart
private static TimeSpan
RestartDelay =
TimeSpan.Zero; // how long the server should remain active before restart (period of 'server wars')
private static TimeSpan
WarningDelay = TimeSpan.FromMinutes(1.0); // at what interval should the shutdown message be displayed?
private static DateTime m_RestartTime;
public AutoRestart() : base(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0))
{
Priority = TimerPriority.FiveSeconds;
m_RestartTime = DateTime.UtcNow.Date + RestartTime;
if (m_RestartTime < DateTime.UtcNow)
m_RestartTime += TimeSpan.FromDays(1.0);
}
public static bool Restarting{ get; private set; }
public static void Initialize()
{
CommandSystem.Register("Restart", AccessLevel.Administrator, Restart_OnCommand);
new AutoRestart().Start();
}
public static void Restart_OnCommand(CommandEventArgs e)
{
if (Restarting)
{
e.Mobile.SendMessage("The server is already restarting.");
}
else
{
e.Mobile.SendMessage("You have initiated server shutdown.");
Enabled = true;
m_RestartTime = DateTime.UtcNow;
}
}
private void Warning_Callback()
{
World.Broadcast(0x22, true, "The server is going down shortly.");
}
private void Restart_Callback()
{
Core.Kill(true);
}
protected override void OnTick()
{
if (Restarting || !Enabled)
return;
if (DateTime.UtcNow < m_RestartTime)
return;
if (WarningDelay > TimeSpan.Zero)
{
Warning_Callback();
DelayCall(WarningDelay, WarningDelay, Warning_Callback);
}
AutoSave.Save();
Restarting = true;
DelayCall(RestartDelay, Restart_Callback);
}
}
}

View file

@ -0,0 +1,191 @@
using System;
using System.IO;
using Server.Commands;
namespace Server.Misc
{
public class AutoSave : Timer
{
private static TimeSpan m_Delay = TimeSpan.FromMinutes(5.0);
private static TimeSpan m_Warning = TimeSpan.Zero;
private static string[] m_Backups =
{
"Third Backup",
"Second Backup",
"Most Recent"
};
public AutoSave() : base(m_Delay - m_Warning, m_Delay)
{
Priority = TimerPriority.OneMinute;
}
public static bool SavesEnabled{ get; set; } = true;
//private static TimeSpan m_Warning = TimeSpan.FromSeconds( 15.0 );
public static void Initialize()
{
new AutoSave().Start();
CommandSystem.Register("SetSaves", AccessLevel.Administrator, SetSaves_OnCommand);
}
[Usage("SetSaves <true | false>")]
[Description("Enables or disables automatic shard saving.")]
public static void SetSaves_OnCommand(CommandEventArgs e)
{
if (e.Length == 1)
{
SavesEnabled = e.GetBoolean(0);
e.Mobile.SendMessage("Saves have been {0}.", SavesEnabled ? "enabled" : "disabled");
}
else
{
e.Mobile.SendMessage("Format: SetSaves <true | false>");
}
}
protected override void OnTick()
{
if (!SavesEnabled || AutoRestart.Restarting)
return;
if (m_Warning == TimeSpan.Zero)
{
Save(true);
}
else
{
int s = (int)m_Warning.TotalSeconds;
int m = s / 60;
s %= 60;
if (m > 0 && s > 0)
World.Broadcast(0x35, true, "The world will save in {0} minute{1} and {2} second{3}.", m,
m != 1 ? "s" : "", s, s != 1 ? "s" : "");
else if (m > 0)
World.Broadcast(0x35, true, "The world will save in {0} minute{1}.", m, m != 1 ? "s" : "");
else
World.Broadcast(0x35, true, "The world will save in {0} second{1}.", s, s != 1 ? "s" : "");
DelayCall(m_Warning, Save);
}
}
public static void Save()
{
Save(false);
}
public static void Save(bool permitBackgroundWrite)
{
if (AutoRestart.Restarting)
return;
World.WaitForWriteCompletion();
try
{
Backup();
}
catch (Exception e)
{
Console.WriteLine("WARNING: Automatic backup FAILED: {0}", e);
}
World.Save(true, permitBackgroundWrite);
}
private static void Backup()
{
if (m_Backups.Length == 0)
return;
string root = Path.Combine(Core.BaseDirectory, "Backups/Automatic");
if (!Directory.Exists(root))
Directory.CreateDirectory(root);
string[] existing = Directory.GetDirectories(root);
for (int i = 0; i < m_Backups.Length; ++i)
{
DirectoryInfo dir = Match(existing, m_Backups[i]);
if (dir == null)
continue;
if (i > 0)
{
string timeStamp = FindTimeStamp(dir.Name);
if (timeStamp != null)
try
{
dir.MoveTo(FormatDirectory(root, m_Backups[i - 1], timeStamp));
}
catch
{
// ignored
}
}
else
{
try
{
dir.Delete(true);
}
catch
{
// ignored
}
}
}
string saves = Path.Combine(Core.BaseDirectory, "Saves");
if (Directory.Exists(saves))
Directory.Move(saves, FormatDirectory(root, m_Backups[m_Backups.Length - 1], GetTimeStamp()));
}
private static DirectoryInfo Match(string[] paths, string match)
{
for (int i = 0; i < paths.Length; ++i)
{
DirectoryInfo info = new DirectoryInfo(paths[i]);
if (info.Name.StartsWith(match))
return info;
}
return null;
}
private static string FormatDirectory(string root, string name, string timeStamp)
{
return Path.Combine(root, $"{name} ({timeStamp})");
}
private static string FindTimeStamp(string input)
{
int start = input.IndexOf('(');
if (start >= 0)
{
int end = input.IndexOf(')', ++start);
if (end >= start)
return input.Substring(start, end - start);
}
return null;
}
private static string GetTimeStamp()
{
DateTime now = DateTime.UtcNow;
return $"{now.Day}-{now.Month}-{now.Year} {now.Hour}-{now.Minute:D2}-{now.Second:D2}";
}
}
}

View file

@ -0,0 +1,35 @@
namespace Server.Misc
{
public class Broadcasts
{
public static void Initialize()
{
EventSink.Crashed += EventSink_Crashed;
EventSink.Shutdown += EventSink_Shutdown;
}
public static void EventSink_Crashed(CrashedEventArgs e)
{
try
{
World.Broadcast(0x35, true, "The server has crashed.");
}
catch
{
// ignored
}
}
public static void EventSink_Shutdown(ShutdownEventArgs e)
{
/* try
{
World.Broadcast(0x35, true, "The server has shut down.");
}
catch
{
// ignored
}*/
}
}
}

View file

@ -0,0 +1,295 @@
using System;
using Server.Mobiles;
using Server.Network;
namespace Server
{
public class BuffInfo
{
public static bool Enabled => Core.ML;
public static void Initialize()
{
if (Enabled)
EventSink.ClientVersionReceived += delegate(ClientVersionReceivedArgs args)
{
if (args.State.Mobile is PlayerMobile pm)
Timer.DelayCall(TimeSpan.Zero, pm.ResendBuffs);
};
}
#region Properties
public BuffIcon ID{ get; }
public int TitleCliloc{ get; }
public int SecondaryCliloc{ get; }
public TimeSpan TimeLength{ get; }
public DateTime TimeStart{ get; }
public Timer Timer{ get; }
public bool RetainThroughDeath{ get; }
public TextDefinition Args{ get; }
#endregion
#region Constructors
public BuffInfo(BuffIcon iconID, int titleCliloc)
: this(iconID, titleCliloc, titleCliloc + 1)
{
}
public BuffInfo(BuffIcon iconID, int titleCliloc, int secondaryCliloc)
{
ID = iconID;
TitleCliloc = titleCliloc;
SecondaryCliloc = secondaryCliloc;
}
public BuffInfo(BuffIcon iconID, int titleCliloc, TimeSpan length, Mobile m)
: this(iconID, titleCliloc, titleCliloc + 1, length, m)
{
}
//Only the timed one needs to Mobile to know when to automagically remove it.
public BuffInfo(BuffIcon iconID, int titleCliloc, int secondaryCliloc, TimeSpan length, Mobile m)
: this(iconID, titleCliloc, secondaryCliloc)
{
TimeLength = length;
TimeStart = DateTime.UtcNow;
Timer = Timer.DelayCall(length, delegate
{
if (!(m is PlayerMobile pm))
return;
pm.RemoveBuff(this);
});
}
public BuffInfo(BuffIcon iconID, int titleCliloc, TextDefinition args)
: this(iconID, titleCliloc, titleCliloc + 1, args)
{
}
public BuffInfo(BuffIcon iconID, int titleCliloc, int secondaryCliloc, TextDefinition args)
: this(iconID, titleCliloc, secondaryCliloc)
{
Args = args;
}
public BuffInfo(BuffIcon iconID, int titleCliloc, bool retainThroughDeath)
: this(iconID, titleCliloc, titleCliloc + 1, retainThroughDeath)
{
}
public BuffInfo(BuffIcon iconID, int titleCliloc, int secondaryCliloc, bool retainThroughDeath)
: this(iconID, titleCliloc, secondaryCliloc)
{
RetainThroughDeath = retainThroughDeath;
}
public BuffInfo(BuffIcon iconID, int titleCliloc, TextDefinition args, bool retainThroughDeath)
: this(iconID, titleCliloc, titleCliloc + 1, args, retainThroughDeath)
{
}
public BuffInfo(BuffIcon iconID, int titleCliloc, int secondaryCliloc, TextDefinition args, bool retainThroughDeath)
: this(iconID, titleCliloc, secondaryCliloc, args)
{
RetainThroughDeath = retainThroughDeath;
}
public BuffInfo(BuffIcon iconID, int titleCliloc, TimeSpan length, Mobile m, TextDefinition args)
: this(iconID, titleCliloc, titleCliloc + 1, length, m, args)
{
}
public BuffInfo(BuffIcon iconID, int titleCliloc, int secondaryCliloc, TimeSpan length, Mobile m,
TextDefinition args)
: this(iconID, titleCliloc, secondaryCliloc, length, m)
{
Args = args;
}
public BuffInfo(BuffIcon iconID, int titleCliloc, TimeSpan length, Mobile m, TextDefinition args,
bool retainThroughDeath)
: this(iconID, titleCliloc, titleCliloc + 1, length, m, args, retainThroughDeath)
{
}
public BuffInfo(BuffIcon iconID, int titleCliloc, int secondaryCliloc, TimeSpan length, Mobile m,
TextDefinition args, bool retainThroughDeath)
: this(iconID, titleCliloc, secondaryCliloc, length, m)
{
Args = args;
RetainThroughDeath = retainThroughDeath;
}
#endregion
#region Convenience Methods
public static void AddBuff(Mobile m, BuffInfo b)
{
if (m is PlayerMobile pm)
pm.AddBuff(b);
}
public static void RemoveBuff(Mobile m, BuffInfo b)
{
if (m is PlayerMobile pm)
pm.RemoveBuff(b);
}
public static void RemoveBuff(Mobile m, BuffIcon b)
{
if (m is PlayerMobile pm)
pm.RemoveBuff(b);
}
#endregion
}
public enum BuffIcon : short
{
DismountPrevention = 0x3E9,
NoRearm = 0x3EA,
//Currently, no 0x3EB or 0x3EC
NightSight = 0x3ED, //*
DeathStrike,
EvilOmen,
UnknownStandingSwirl, //Which is healing throttle & Stamina throttle?
UnknownKneelingSword,
DivineFury, //*
EnemyOfOne, //*
HidingAndOrStealth, //*
ActiveMeditation, //*
BloodOathCaster, //*
BloodOathCurse, //*
CorpseSkin, //*
Mindrot, //*
PainSpike, //*
Strangle,
GiftOfRenewal, //*
AttuneWeapon, //*
Thunderstorm, //*
EssenceOfWind, //*
EtherealVoyage, //*
GiftOfLife, //*
ArcaneEmpowerment, //*
MortalStrike,
ReactiveArmor, //*
Protection, //*
ArchProtection,
MagicReflection, //*
Incognito, //*
Disguised,
AnimalForm,
Polymorph,
Invisibility, //*
Paralyze, //*
Poison,
Bleed,
Clumsy, //*
FeebleMind, //*
Weaken, //*
Curse, //*
MassCurse,
Agility, //*
Cunning, //*
Strength, //*
Bless, //*
Sleep,
StoneForm,
SpellPlague,
SpellTrigger,
NetherBolt,
Fly
}
public sealed class AddBuffPacket : Packet
{
public AddBuffPacket(Mobile m, BuffInfo info)
: this(m, info.ID, info.TitleCliloc, info.SecondaryCliloc, info.Args,
info.TimeStart != DateTime.MinValue ? info.TimeStart + info.TimeLength - DateTime.UtcNow : TimeSpan.Zero)
{
}
public AddBuffPacket(Mobile mob, BuffIcon iconID, int titleCliloc, int secondaryCliloc, TextDefinition args,
TimeSpan length)
: base(0xDF)
{
bool hasArgs = args != null;
EnsureCapacity(hasArgs ? 48 + args.ToString().Length * 2 : 44);
m_Stream.Write(mob.Serial);
m_Stream.Write((short)iconID); //ID
m_Stream.Write((short)0x1); //Type 0 for removal. 1 for add 2 for Data
m_Stream.Fill(4);
m_Stream.Write((short)iconID); //ID
m_Stream.Write((short)0x01); //Type 0 for removal. 1 for add 2 for Data
m_Stream.Fill(4);
if (length < TimeSpan.Zero)
length = TimeSpan.Zero;
m_Stream.Write((short)length.TotalSeconds); //Time in seconds
m_Stream.Fill(3);
m_Stream.Write(titleCliloc);
m_Stream.Write(secondaryCliloc);
if (!hasArgs)
{
//m_Stream.Fill( 2 );
m_Stream.Fill(10);
}
else
{
m_Stream.Fill(4);
m_Stream.Write((short)0x1); //Unknown -> Possibly something saying 'hey, I have more data!'?
m_Stream.Fill(2);
//m_Stream.WriteLittleUniNull( "\t#1018280" );
m_Stream.WriteLittleUniNull($"\t{args}");
m_Stream.Write((short)0x1); //Even more Unknown -> Possibly something saying 'hey, I have more data!'?
m_Stream.Fill(2);
}
}
}
public sealed class RemoveBuffPacket : Packet
{
public RemoveBuffPacket(Mobile mob, BuffInfo info)
: this(mob, info.ID)
{
}
public RemoveBuffPacket(Mobile mob, BuffIcon iconID)
: base(0xDF)
{
EnsureCapacity(13);
m_Stream.Write(mob.Serial);
m_Stream.Write((short)iconID); //ID
m_Stream.Write((short)0x0); //Type 0 for removal. 1 for add 2 for Data
m_Stream.Fill(4);
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,165 @@
using System;
using System.Collections.Generic;
using Server.Items;
using Server.Mobiles;
using Server.Multis;
namespace Server.Misc
{
public class Cleanup
{
public static void Initialize()
{
Timer.DelayCall(TimeSpan.FromSeconds(2.5), Run);
}
public static void Run()
{
List<Item> items = new List<Item>();
List<Item> validItems = new List<Item>();
List<Mobile> hairCleanup = new List<Mobile>();
int boxes = 0;
foreach (Item item in World.Items.Values)
{
if (item.Map == null)
{
items.Add(item);
continue;
}
if (item is CommodityDeed deed)
{
if (deed.Commodity != null)
validItems.Add(deed.Commodity);
continue;
}
if (item is BaseHouse house)
{
foreach (RelocatedEntity relEntity in house.RelocatedEntities)
if (relEntity.Entity is Item item1)
validItems.Add(item1);
foreach (VendorInventory inventory in house.VendorInventories)
foreach (Item subItem in inventory.Items)
validItems.Add(subItem);
}
else if (item is BankBox box)
{
Mobile owner = box.Owner;
if (owner == null)
{
items.Add(box);
++boxes;
}
else if (box.Items.Count == 0)
{
items.Add(box);
++boxes;
}
continue;
}
else if (item.Layer == Layer.Hair || item.Layer == Layer.FacialHair)
{
if (item.RootParent is Mobile rootMobile)
{
if (item.Parent != rootMobile && rootMobile.AccessLevel == AccessLevel.Player)
{
items.Add(item);
continue;
}
if (item.Parent == rootMobile)
{
hairCleanup.Add(rootMobile);
continue;
}
}
}
if (item.Parent != null || item.Map != Map.Internal || item.HeldBy != null)
continue;
if (item.Location != Point3D.Zero)
continue;
if (!IsBuggable(item))
continue;
items.Add(item);
}
for (int i = 0; i < validItems.Count; ++i)
items.Remove(validItems[i]);
if (items.Count > 0)
{
if (boxes > 0)
Console.WriteLine("Cleanup: Detected {0} inaccessible items, including {1} bank boxes, removing..",
items.Count, boxes);
else
Console.WriteLine("Cleanup: Detected {0} inaccessible items, removing..", items.Count);
for (int i = 0; i < items.Count; ++i)
items[i].Delete();
}
if (hairCleanup.Count > 0)
{
Console.WriteLine(
"Cleanup: Detected {0} hair and facial hair items being worn, converting to their virtual counterparts..",
hairCleanup.Count);
for (int i = 0; i < hairCleanup.Count; i++)
hairCleanup[i].ConvertHair();
}
}
public static bool IsBuggable(Item item)
{
if (item is Fists)
return false;
if (item is ICommodity || item is BaseBoat
|| item is Fish || item is BigFish || item is Food || item is CookableFood
|| item is SpecialFishingNet || item is BaseMagicFish
|| item is Shoes || item is Sandals
|| item is Boots || item is ThighBoots
|| item is TreasureMap || item is MessageInABottle
|| item is BaseArmor || item is BaseWeapon
|| item is BaseClothing
|| item is BaseJewel && Core.AOS
#region Champion artifacts
|| item is SkullPole
|| item is EvilIdolSkull
|| item is MonsterStatuette
|| item is Pier
|| item is ArtifactLargeVase
|| item is ArtifactVase
|| item is MinotaurStatueDeed
|| item is SwampTile
|| item is WallBlood
|| item is TatteredAncientMummyWrapping
|| item is LavaTile
|| item is DemonSkull
|| item is Web
|| item is WaterTile
|| item is WindSpirit
|| item is DirtPatch
|| item is Futon)
#endregion
return true;
return false;
}
}
}

View file

@ -0,0 +1,176 @@
using System;
using System.Diagnostics;
using System.IO;
using Server.Gumps;
using Server.Mobiles;
using Server.Network;
namespace Server.Misc
{
public class ClientVerification
{
private static bool m_DetectClientRequirement = true;
private static OldClientResponse m_OldClientResponse = OldClientResponse.LenientKick;
private static TimeSpan m_AgeLeniency = TimeSpan.FromDays(10);
private static TimeSpan m_GameTimeLeniency = TimeSpan.FromHours(25);
public static ClientVersion Required{ get; set; }
public static bool AllowRegular{ get; set; } = true;
public static bool AllowUOTD{ get; set; } = true;
public static bool AllowGod{ get; set; } = true;
public static TimeSpan KickDelay{ get; set; } = TimeSpan.FromSeconds(20.0);
public static void Initialize()
{
EventSink.ClientVersionReceived += EventSink_ClientVersionReceived;
//ClientVersion.Required = null;
//Required = new ClientVersion( "6.0.0.0" );
if (m_DetectClientRequirement)
{
string path = Core.FindDataFile("client.exe");
if (File.Exists(path))
{
FileVersionInfo info = FileVersionInfo.GetVersionInfo(path);
if (info.FileMajorPart != 0 || info.FileMinorPart != 0 || info.FileBuildPart != 0 ||
info.FilePrivatePart != 0)
Required = new ClientVersion(info.FileMajorPart, info.FileMinorPart, info.FileBuildPart,
info.FilePrivatePart);
}
}
if (Required != null)
{
Utility.PushColor(ConsoleColor.White);
Console.WriteLine("Restricting client version to {0}. Action to be taken: {1}", Required,
m_OldClientResponse);
Utility.PopColor();
}
}
private static void EventSink_ClientVersionReceived(ClientVersionReceivedArgs e)
{
string kickMessage = null;
NetState state = e.State;
ClientVersion version = e.Version;
if (state.Mobile == null || state.Mobile.AccessLevel > AccessLevel.Player)
return;
if (Required != null && version < Required && (m_OldClientResponse == OldClientResponse.Kick ||
m_OldClientResponse == OldClientResponse.LenientKick &&
DateTime.UtcNow - state.Mobile.CreationTime > m_AgeLeniency &&
state.Mobile is PlayerMobile mobile &&
mobile.GameTime > m_GameTimeLeniency))
{
kickMessage = $"This server requires your client version be at least {Required}.";
}
else if (!AllowGod || !AllowRegular || !AllowUOTD)
{
if (!AllowGod && version.Type == ClientType.God)
kickMessage = "This server does not allow god clients to connect.";
else if (!AllowRegular && version.Type == ClientType.Regular)
kickMessage = "This server does not allow regular clients to connect.";
else if (!AllowUOTD && state.IsUOTDClient)
kickMessage = "This server does not allow UO:TD clients to connect.";
if (!AllowGod && !AllowRegular && !AllowUOTD)
{
kickMessage = "This server does not allow any clients to connect.";
}
else if (AllowGod && !AllowRegular && !AllowUOTD && version.Type != ClientType.God)
{
kickMessage = "This server requires you to use the god client.";
}
else if (kickMessage != null)
{
if (AllowRegular && AllowUOTD)
kickMessage += " You can use regular or UO:TD clients.";
else if (AllowRegular)
kickMessage += " You can use regular clients.";
else if (AllowUOTD)
kickMessage += " You can use UO:TD clients.";
}
}
if (kickMessage != null)
{
state.Mobile.SendMessage(0x22, kickMessage);
state.Mobile.SendMessage(0x22, "You will be disconnected in {0} seconds.", KickDelay.TotalSeconds);
Timer.DelayCall(KickDelay, delegate
{
if (state.Socket != null)
{
Console.WriteLine("Client: {0}: Disconnecting, bad version", state);
state.Dispose();
}
});
}
else if (Required != null && version < Required)
{
switch (m_OldClientResponse)
{
case OldClientResponse.Warn:
{
state.Mobile.SendMessage(0x22, "Your client is out of date. Please update your client.", Required);
state.Mobile.SendMessage(0x22, "This server recommends that your client version be at least {0}.",
Required);
break;
}
case OldClientResponse.LenientKick:
case OldClientResponse.Annoy:
{
SendAnnoyGump(state.Mobile);
break;
}
}
}
}
private static void KickMessage(Mobile from, bool okay)
{
from.SendMessage("You will be reminded of this again.");
if (m_OldClientResponse == OldClientResponse.LenientKick)
from.SendMessage(
"Old clients will be kicked after {0} days of character age and {1} hours of play time",
m_AgeLeniency, m_GameTimeLeniency);
Timer.DelayCall(TimeSpan.FromMinutes(Utility.Random(5, 15)), () => SendAnnoyGump(from));
}
private static void SendAnnoyGump(Mobile m)
{
if (m.NetState != null && m.NetState.Version < Required)
{
Gump g = new WarningGump(1060637, 30720,
$"Your client is out of date. Please update your client.<br>This server recommends that your client version be at least {Required}.<br> <br>You are currently using version {m.NetState.Version}.<br> <br>To patch, run UOPatch.exe inside your Ultima Online folder.",
0xFFC000, 480, 360, okay => KickMessage(m, okay), false);
g.Draggable = false;
g.Closable = false;
g.Resizable = false;
m.SendGump(g);
}
}
private enum OldClientResponse
{
Ignore,
Warn,
Annoy,
LenientKick,
Kick
}
}
}

View file

@ -0,0 +1,266 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net.Mail;
using Server.Accounting;
using Server.Network;
namespace Server.Misc
{
public class CrashGuard
{
private static bool Enabled = true;
private static bool SaveBackup = true;
private static bool RestartServer = true;
private static bool GenerateReport = true;
public static void Initialize()
{
if (Enabled) // If enabled, register our crash event handler
EventSink.Crashed += CrashGuard_OnCrash;
}
public static void CrashGuard_OnCrash(CrashedEventArgs e)
{
if (GenerateReport)
GenerateCrashReport(e);
World.WaitForWriteCompletion();
if (SaveBackup)
Backup();
/*if ( Core.Service )
e.Close = true;
else */
if (RestartServer)
Restart(e);
}
private static void SendEmail(string filePath)
{
Console.Write("Crash: Sending email...");
MailMessage message = new MailMessage(Email.FromAddress, Email.CrashAddresses);
message.Subject = "Automated RunUO Crash Report";
message.Body = "Automated RunUO Crash Report. See attachment for details.";
message.Attachments.Add(new Attachment(filePath));
if (Email.Send(message))
Console.WriteLine("done");
else
Console.WriteLine("failed");
}
private static string GetRoot()
{
try
{
return Path.GetDirectoryName(Environment.GetCommandLineArgs()[0]);
}
catch
{
return "";
}
}
private static string Combine(string path1, string path2)
{
if (path1.Length == 0)
return path2;
return Path.Combine(path1, path2);
}
private static void Restart(CrashedEventArgs e)
{
string root = GetRoot();
Console.Write("Crash: Restarting...");
try
{
Process.Start(Core.ExePath, Core.Arguments);
Console.WriteLine("done");
e.Close = true;
}
catch
{
Console.WriteLine("failed");
}
}
private static void CreateDirectory(string path)
{
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
}
private static void CreateDirectory(string path1, string path2)
{
CreateDirectory(Combine(path1, path2));
}
private static void CopyFile(string rootOrigin, string rootBackup, string path)
{
string originPath = Combine(rootOrigin, path);
string backupPath = Combine(rootBackup, path);
try
{
if (File.Exists(originPath))
File.Copy(originPath, backupPath);
}
catch
{
// ignored
}
}
private static void Backup()
{
Console.Write("Crash: Backing up...");
try
{
string timeStamp = GetTimeStamp();
string root = GetRoot();
string rootBackup = Combine(root, $"Backups/Crashed/{timeStamp}/");
string rootOrigin = Combine(root, "Saves/");
// Create new directories
CreateDirectory(rootBackup);
CreateDirectory(rootBackup, "Accounts/");
CreateDirectory(rootBackup, "Items/");
CreateDirectory(rootBackup, "Mobiles/");
CreateDirectory(rootBackup, "Guilds/");
CreateDirectory(rootBackup, "Regions/");
// Copy files
CopyFile(rootOrigin, rootBackup, "Accounts/Accounts.xml");
CopyFile(rootOrigin, rootBackup, "Items/Items.bin");
CopyFile(rootOrigin, rootBackup, "Items/Items.idx");
CopyFile(rootOrigin, rootBackup, "Items/Items.tdb");
CopyFile(rootOrigin, rootBackup, "Mobiles/Mobiles.bin");
CopyFile(rootOrigin, rootBackup, "Mobiles/Mobiles.idx");
CopyFile(rootOrigin, rootBackup, "Mobiles/Mobiles.tdb");
CopyFile(rootOrigin, rootBackup, "Guilds/Guilds.bin");
CopyFile(rootOrigin, rootBackup, "Guilds/Guilds.idx");
CopyFile(rootOrigin, rootBackup, "Regions/Regions.bin");
CopyFile(rootOrigin, rootBackup, "Regions/Regions.idx");
Console.WriteLine("done");
}
catch
{
Console.WriteLine("failed");
}
}
private static void GenerateCrashReport(CrashedEventArgs e)
{
Console.Write("Crash: Generating report...");
try
{
string timeStamp = GetTimeStamp();
string fileName = $"Crash {timeStamp}.log";
string root = GetRoot();
string filePath = Combine(root, fileName);
using (StreamWriter op = new StreamWriter(filePath))
{
Version ver = Core.Assembly.GetName().Version;
op.WriteLine("Server Crash Report");
op.WriteLine("===================");
op.WriteLine();
op.WriteLine("RunUO Version {0}.{1}, Build {2}.{3}", ver.Major, ver.Minor, ver.Build, ver.Revision);
op.WriteLine("Operating System: {0}", Environment.OSVersion);
op.WriteLine(".NET Framework: {0}", Environment.Version);
op.WriteLine("Time: {0}", DateTime.UtcNow);
try
{
op.WriteLine("Mobiles: {0}", World.Mobiles.Count);
}
catch
{
// ignored
}
try
{
op.WriteLine("Items: {0}", World.Items.Count);
}
catch
{
// ignored
}
op.WriteLine("Exception:");
op.WriteLine(e.Exception);
op.WriteLine();
op.WriteLine("Clients:");
try
{
List<NetState> states = NetState.Instances;
op.WriteLine("- Count: {0}", states.Count);
for (int i = 0; i < states.Count; ++i)
{
NetState state = states[i];
op.Write("+ {0}:", state);
if (state.Account is Account a)
op.Write(" (account = {0})", a.Username);
Mobile m = state.Mobile;
if (m != null)
op.Write(" (mobile = 0x{0:X} '{1}')", m.Serial.Value, m.Name);
op.WriteLine();
}
}
catch
{
op.WriteLine("- Failed");
}
}
Console.WriteLine("done");
if (Email.FromAddress != null && Email.CrashAddresses != null)
SendEmail(filePath);
}
catch
{
Console.WriteLine("failed");
}
}
private static string GetTimeStamp()
{
DateTime now = DateTime.UtcNow;
return $"{now.Day}-{now.Month}-{now.Year}-{now.Hour}-{now.Minute}-{now.Second}";
}
}
}

View file

@ -0,0 +1,40 @@
using Server.Accounting;
using Server.Network;
namespace Server
{
public class CurrentExpansion
{
private static readonly Expansion Expansion = Expansion.TOL;
public static void Configure()
{
Core.Expansion = Expansion;
AccountGold.Enabled = Core.TOL;
AccountGold.ConvertOnBank = true;
AccountGold.ConvertOnTrade = false;
VirtualCheck.UseEditGump = true;
bool Enabled = Core.AOS;
Mobile.InsuranceEnabled = Enabled;
ObjectPropertyList.Enabled = Enabled;
Mobile.VisibleDamageType = Enabled ? VisibleDamageType.Related : VisibleDamageType.None;
Mobile.GuildClickMessage = !Enabled;
Mobile.AsciiClickMessage = !Enabled;
if (Enabled)
{
AOS.DisableStatInfluences();
if (ObjectPropertyList.Enabled)
PacketHandlers.SingleClickProps =
true; // single click for everything is overridden to check object property list
Mobile.ActionDelay = 1000;
Mobile.AOSStatusHandler = AOS.GetStatus;
}
}
}
}

View file

@ -0,0 +1,108 @@
using System;
namespace Server.Misc
{
public class DataPath
{
/* If you have not installed Ultima Online,
* or wish the server to use a separate set of datafiles,
* change the 'CustomPath' value.
* Example:
* private static string CustomPath = @"C:\Program Files\Ultima Online";
*/
private static string CustomPath = @"C:\Ultima Online Classic";
// private static string CustomPath = @"/Users/kamronbatman/UOC";
/* The following is a list of files which a required for proper execution:
*
* Multi.idx
* Multi.mul
* VerData.mul
* TileData.mul
* Map*.mul or Map*LegacyMUL.uop
* StaIdx*.mul
* Statics*.mul
* MapDif*.mul
* MapDifL*.mul
* StaDif*.mul
* StaDifL*.mul
* StaDifI*.mul
*/
public static void Configure()
{
string pathUO = GetPath(@"Origin Worlds Online\Ultima Online\1.0", "ExePath");
string pathTD = GetPath(@"Origin Worlds Online\Ultima Online Third Dawn\1.0",
"ExePath"); //These refer to 2D & 3D, not the Third Dawn expansion
string pathKR = GetPath(@"Origin Worlds Online\Ultima Online\KR Legacy Beta",
"ExePath"); //After KR, This is the new registry key for the 2D client
string pathSA = GetPath(@"Electronic Arts\EA Games\Ultima Online Stygian Abyss Classic", "InstallDir");
string pathHS = GetPath(@"Electronic Arts\EA Games\Ultima Online Classic", "InstallDir");
if (CustomPath != null)
Core.DataDirectories.Add(CustomPath);
if (pathUO != null)
Core.DataDirectories.Add(pathUO);
if (pathTD != null)
Core.DataDirectories.Add(pathTD);
if (pathKR != null)
Core.DataDirectories.Add(pathKR);
if (pathSA != null)
Core.DataDirectories.Add(pathSA);
if (pathHS != null)
Core.DataDirectories.Add(pathHS);
if (Core.DataDirectories.Count == 0 && !Core.Service)
{
Console.WriteLine("Enter the Ultima Online directory:");
Console.Write("> ");
Core.DataDirectories.Add(Console.ReadLine());
}
}
private static string GetPath(string subName, string keyName)
{
return null;
/*try
{
string keyString;
if (Core.Is64Bit)
keyString = @"SOFTWARE\Wow6432Node\{0}";
else
keyString = @"SOFTWARE\{0}";
using (RegistryKey key = Registry.LocalMachine.OpenSubKey(string.Format(keyString, subName)))
{
if (key == null)
return null;
string v = key.GetValue(keyName) as string;
if (string.IsNullOrEmpty(v))
return null;
if (keyName == "InstallDir")
v = v + @"\";
v = Path.GetDirectoryName(v);
if (string.IsNullOrEmpty(v))
return null;
return v;
}
}
catch
{
return null;
}*/
}
}
}

View file

@ -0,0 +1,9 @@
using System;
namespace Server.Misc
{
[AttributeUsage(AttributeTargets.Class)]
public class DispellableAttribute : Attribute
{
}
}

View file

@ -0,0 +1,9 @@
using System;
namespace Server.Misc
{
[AttributeUsage(AttributeTargets.Class)]
public class DispellableFieldAttribute : Attribute
{
}
}

View file

@ -0,0 +1,557 @@
using Server.Commands;
using Server.Items;
using Server.Network;
namespace Server
{
public class DoorGenerator
{
private static Rectangle2D[] m_BritRegions =
{
new Rectangle2D(new Point2D(250, 750), new Point2D(775, 1330)),
new Rectangle2D(new Point2D(525, 2095), new Point2D(925, 2430)),
new Rectangle2D(new Point2D(1025, 2155), new Point2D(1265, 2310)),
new Rectangle2D(new Point2D(1635, 2430), new Point2D(1705, 2508)),
new Rectangle2D(new Point2D(1775, 2605), new Point2D(2165, 2975)),
new Rectangle2D(new Point2D(1055, 3520), new Point2D(1570, 4075)),
new Rectangle2D(new Point2D(2860, 3310), new Point2D(3120, 3630)),
new Rectangle2D(new Point2D(2470, 1855), new Point2D(3950, 3045)),
new Rectangle2D(new Point2D(3425, 990), new Point2D(3900, 1455)),
new Rectangle2D(new Point2D(4175, 735), new Point2D(4840, 1600)),
new Rectangle2D(new Point2D(2375, 330), new Point2D(3100, 1045)),
new Rectangle2D(new Point2D(2100, 1090), new Point2D(2310, 1450)),
new Rectangle2D(new Point2D(1495, 1400), new Point2D(1550, 1475)),
new Rectangle2D(new Point2D(1085, 1520), new Point2D(1415, 1910)),
new Rectangle2D(new Point2D(1410, 1500), new Point2D(1745, 1795)),
new Rectangle2D(new Point2D(5120, 2300), new Point2D(6143, 4095))
};
private static Rectangle2D[] m_IlshRegions =
{
new Rectangle2D(new Point2D(0, 0), new Point2D(288 * 8, 200 * 8))
};
private static Rectangle2D[] m_MalasRegions =
{
new Rectangle2D(new Point2D(0, 0), new Point2D(320 * 8, 256 * 8))
};
private static int[] m_SouthFrames =
{
0x0006,
0x0008,
0x000B,
0x001A,
0x001B,
0x001F,
0x0038,
0x0057,
0x0059,
0x005B,
0x005D,
0x0080,
0x0081,
0x0082,
0x0084,
0x0090,
0x0091,
0x0094,
0x0096,
0x0099,
0x00A6,
0x00A7,
0x00AA,
0x00AE,
0x00B0,
0x00B3,
0x00C7,
0x00C9,
0x00F8,
0x00FA,
0x00FD,
0x00FE,
0x0100,
0x0103,
0x0104,
0x0106,
0x0109,
0x0127,
0x0129,
0x012B,
0x012D,
0x012F,
0x0131,
0x0132,
0x0134,
0x0135,
0x0137,
0x0139,
0x013B,
0x014C,
0x014E,
0x014F,
0x0151,
0x0153,
0x0155,
0x0157,
0x0158,
0x015A,
0x015D,
0x015E,
0x015F,
0x0162,
0x01CF,
0x01D1,
0x01D4,
0x01FF,
0x0204,
0x0206,
0x0208,
0x020A
};
private static int[] m_NorthFrames =
{
0x0006,
0x0008,
0x000D,
0x001A,
0x001B,
0x0020,
0x003A,
0x0057,
0x0059,
0x005B,
0x005D,
0x0080,
0x0081,
0x0082,
0x0084,
0x0090,
0x0091,
0x0094,
0x0096,
0x0099,
0x00A6,
0x00A7,
0x00AC,
0x00AE,
0x00B0,
0x00C7,
0x00C9,
0x00F8,
0x00FA,
0x00FD,
0x00FE,
0x0100,
0x0103,
0x0104,
0x0106,
0x0109,
0x0127,
0x0129,
0x012B,
0x012D,
0x012F,
0x0131,
0x0132,
0x0134,
0x0135,
0x0137,
0x0139,
0x013B,
0x014C,
0x014E,
0x014F,
0x0151,
0x0153,
0x0155,
0x0157,
0x0158,
0x015A,
0x015D,
0x015E,
0x015F,
0x0162,
0x01CF,
0x01D1,
0x01D4,
0x01FF,
0x0201,
0x0204,
0x0208,
0x020A
};
private static int[] m_EastFrames =
{
0x0007,
0x000A,
0x001A,
0x001C,
0x001E,
0x0037,
0x0058,
0x0059,
0x005C,
0x005E,
0x0080,
0x0081,
0x0082,
0x0084,
0x0090,
0x0092,
0x0095,
0x0097,
0x0098,
0x00A6,
0x00A8,
0x00AB,
0x00AE,
0x00AF,
0x00B2,
0x00C7,
0x00C8,
0x00EA,
0x00F8,
0x00F9,
0x00FC,
0x00FE,
0x00FF,
0x0102,
0x0104,
0x0105,
0x0108,
0x0127,
0x0128,
0x012B,
0x012C,
0x012E,
0x0130,
0x0132,
0x0133,
0x0135,
0x0136,
0x0138,
0x013A,
0x014C,
0x014D,
0x014F,
0x0150,
0x0152,
0x0154,
0x0156,
0x0158,
0x0159,
0x015C,
0x015E,
0x0160,
0x0163,
0x01CF,
0x01D0,
0x01D3,
0x01FF,
0x0203,
0x0205,
0x0207,
0x0209
};
private static int[] m_WestFrames =
{
0x0007,
0x000C,
0x001A,
0x001C,
0x0021,
0x0039,
0x0058,
0x0059,
0x005C,
0x005E,
0x0080,
0x0081,
0x0082,
0x0084,
0x0090,
0x0092,
0x0095,
0x0097,
0x0098,
0x00A6,
0x00A8,
0x00AD,
0x00AE,
0x00AF,
0x00B5,
0x00C7,
0x00C8,
0x00EA,
0x00F8,
0x00F9,
0x00FC,
0x00FE,
0x00FF,
0x0102,
0x0104,
0x0105,
0x0108,
0x0127,
0x0128,
0x012C,
0x012E,
0x0130,
0x0132,
0x0133,
0x0135,
0x0136,
0x0138,
0x013A,
0x014C,
0x014D,
0x014F,
0x0150,
0x0152,
0x0154,
0x0156,
0x0158,
0x0159,
0x015C,
0x015E,
0x0160,
0x0163,
0x01CF,
0x01D0,
0x01D3,
0x01FF,
0x0200,
0x0203,
0x0207,
0x0209
};
private static Map m_Map;
private static int m_Count;
public static void Initialize()
{
CommandSystem.Register("DoorGen", AccessLevel.Administrator, DoorGen_OnCommand);
}
[Usage("DoorGen")]
[Description("Generates doors by analyzing the map. Slow.")]
public static void DoorGen_OnCommand(CommandEventArgs e)
{
Generate();
}
public static void Generate()
{
World.Broadcast(0x35, true, "Generating doors, please wait.");
NetState.Pause();
m_Map = Map.Trammel;
m_Count = 0;
for (int i = 0; i < m_BritRegions.Length; ++i)
Generate(m_BritRegions[i]);
int trammelCount = m_Count;
m_Map = Map.Felucca;
m_Count = 0;
for (int i = 0; i < m_BritRegions.Length; ++i)
Generate(m_BritRegions[i]);
int feluccaCount = m_Count;
m_Map = Map.Ilshenar;
m_Count = 0;
for (int i = 0; i < m_IlshRegions.Length; ++i)
Generate(m_IlshRegions[i]);
int ilshenarCount = m_Count;
m_Map = Map.Malas;
m_Count = 0;
for (int i = 0; i < m_MalasRegions.Length; ++i)
Generate(m_MalasRegions[i]);
int malasCount = m_Count;
NetState.Resume();
World.Broadcast(0x35, true, "Door generation complete. Trammel: {0}; Felucca: {1}; Ilshenar: {2}; Malas: {3};",
trammelCount, feluccaCount, ilshenarCount, malasCount);
}
public static bool IsFrame(int id, int[] list)
{
if (id > list[list.Length - 1])
return false;
for (int i = 0; i < list.Length; ++i)
{
int delta = id - list[i];
if (delta < 0)
return false;
if (delta == 0)
return true;
}
return false;
}
public static bool IsNorthFrame(int id)
{
return IsFrame(id, m_NorthFrames);
}
public static bool IsSouthFrame(int id)
{
return IsFrame(id, m_SouthFrames);
}
public static bool IsWestFrame(int id)
{
return IsFrame(id, m_WestFrames);
}
public static bool IsEastFrame(int id)
{
return IsFrame(id, m_EastFrames);
}
public static bool IsEastFrame(int x, int y, int z)
{
StaticTile[] tiles = m_Map.Tiles.GetStaticTiles(x, y);
for (int i = 0; i < tiles.Length; ++i)
{
StaticTile tile = tiles[i];
if (tile.Z == z && IsEastFrame(tile.ID))
return true;
}
return false;
}
public static bool IsSouthFrame(int x, int y, int z)
{
StaticTile[] tiles = m_Map.Tiles.GetStaticTiles(x, y);
for (int i = 0; i < tiles.Length; ++i)
{
StaticTile tile = tiles[i];
if (tile.Z == z && IsSouthFrame(tile.ID))
return true;
}
return false;
}
public static BaseDoor AddDoor(int x, int y, int z, DoorFacing facing)
{
int doorZ = z;
int doorTop = doorZ + 20;
if (!m_Map.CanFit(x, y, z, 16, false, false))
return null;
if (y == 1743 && x >= 1343 && x <= 1344)
return null;
if (y == 1679 && x >= 1392 && x <= 1393)
return null;
if (x == 1320 && y >= 1618 && y <= 1640)
return null;
if (x == 1383 && y >= 1642 && y <= 1643)
return null;
BaseDoor door = new DarkWoodDoor(facing);
door.MoveToWorld(new Point3D(x, y, z), m_Map);
++m_Count;
return door;
}
public static void Generate(Rectangle2D region)
{
for (int rx = 0; rx < region.Width; ++rx)
for (int ry = 0; ry < region.Height; ++ry)
{
int vx = rx + region.X;
int vy = ry + region.Y;
StaticTile[] tiles = m_Map.Tiles.GetStaticTiles(vx, vy);
for (int i = 0; i < tiles.Length; ++i)
{
StaticTile tile = tiles[i];
int id = tile.ID;
int z = tile.Z;
if (IsWestFrame(id))
{
if (IsEastFrame(vx + 2, vy, z))
{
AddDoor(vx + 1, vy, z, DoorFacing.WestCW);
}
else if (IsEastFrame(vx + 3, vy, z))
{
BaseDoor first = AddDoor(vx + 1, vy, z, DoorFacing.WestCW);
BaseDoor second = AddDoor(vx + 2, vy, z, DoorFacing.EastCCW);
if (first != null && second != null)
{
first.Link = second;
second.Link = first;
}
else
{
first?.Delete();
second?.Delete();
}
}
}
else if (IsNorthFrame(id))
{
if (IsSouthFrame(vx, vy + 2, z))
{
AddDoor(vx, vy + 1, z, DoorFacing.SouthCW);
}
else if (IsSouthFrame(vx, vy + 3, z))
{
BaseDoor first = AddDoor(vx, vy + 1, z, DoorFacing.NorthCCW);
BaseDoor second = AddDoor(vx, vy + 2, z, DoorFacing.SouthCW);
if (first != null && second != null)
{
first.Link = second;
second.Link = first;
}
else
{
first?.Delete();
second?.Delete();
}
}
}
}
}
}
}
}

View file

@ -0,0 +1,98 @@
using System;
using System.Net;
using System.Net.Mail;
using System.Text.RegularExpressions;
using System.Threading;
namespace Server.Misc
{
public class Email
{
/* In order to support emailing, fill in EmailServer and FromAddress:
* Example:
* public static readonly string EmailServer = "mail.domain.com";
* public static readonly string FromAddress = "runuo@domain.com";
*
* If you want to add crash reporting emailing, fill in CrashAddresses:
* Example:
* public static readonly string CrashAddresses = "first@email.here,second@email.here,third@email.here";
*
* If you want to add speech log page emailing, fill in SpeechLogPageAddresses:
* Example:
* public static readonly string SpeechLogPageAddresses = "first@email.here,second@email.here,third@email.here";
*/
public static readonly string EmailServer = null;
public static readonly int EmailPort = 25;
public static readonly string FromAddress = null;
public static readonly string EmailUsername = null;
public static readonly string EmailPassword = null;
public static readonly string CrashAddresses = null;
public static readonly string SpeechLogPageAddresses = null;
private static Regex _pattern = new Regex(@"^[a-z0-9.+_-]+@([a-z0-9-]+\.)+[a-z]+$",
RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static SmtpClient _Client;
public static bool IsValid(string address)
{
if (address == null || address.Length > 320)
return false;
return _pattern.IsMatch(address);
}
public static void Configure()
{
if (EmailServer != null)
{
_Client = new SmtpClient(EmailServer, EmailPort);
if (EmailUsername != null) _Client.Credentials = new NetworkCredential(EmailUsername, EmailPassword);
}
}
public static bool Send(MailMessage message)
{
try
{
// .NET relies on the MTA to generate Message-ID header. Not all MTAs will add this header.
DateTime now = DateTime.UtcNow;
string messageID = $"<{now.ToString("yyyyMMdd")}.{now.ToString("HHmmssff")}@{EmailServer}>";
message.Headers.Add("Message-ID", messageID);
message.Headers.Add("X-Mailer", "RunUO");
lock (_Client)
{
_Client.Send(message);
}
}
catch
{
return false;
}
return true;
}
public static void AsyncSend(MailMessage message)
{
ThreadPool.QueueUserWorkItem(SendCallback, message);
}
private static void SendCallback(object state)
{
MailMessage message = (MailMessage)state;
if (Send(message))
Console.WriteLine("Sent e-mail '{0}' to '{1}'.", message.Subject, message.To);
else
Console.WriteLine("Failure sending e-mail '{0}' to '{1}'.", message.Subject, message.To);
}
}
}

View file

@ -0,0 +1,677 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
namespace Server
{
public class AssemblyEmitter
{
private string m_AssemblyName;
private AppDomain m_AppDomain;
private AssemblyBuilder m_AssemblyBuilder;
private ModuleBuilder m_ModuleBuilder;
public AssemblyEmitter( string assemblyName )
{
m_AssemblyName = assemblyName;
m_AppDomain = AppDomain.CurrentDomain;
m_AssemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(
new AssemblyName( assemblyName ),
AssemblyBuilderAccess.Run
);
m_ModuleBuilder = m_AssemblyBuilder.DefineDynamicModule(assemblyName);
}
public TypeBuilder DefineType( string typeName, TypeAttributes attrs, Type parentType )
{
return m_ModuleBuilder.DefineType( typeName, attrs, parentType );
}
}
public class MethodEmitter
{
private Type[] m_ArgumentTypes;
public TypeBuilder Type { get; }
public ILGenerator Generator { get; private set; }
private class CallInfo
{
public Type type;
public MethodInfo method;
public int index;
public ParameterInfo[] parms;
public CallInfo( Type type, MethodInfo method )
{
this.type = type;
this.method = method;
parms = method.GetParameters();
}
}
private Stack<Type> m_Stack;
private Stack<CallInfo> m_Calls;
private Dictionary<Type, Queue<LocalBuilder>> m_Temps;
public MethodBuilder Method { get; private set; }
public MethodEmitter( TypeBuilder typeBuilder )
{
Type = typeBuilder;
m_Temps = new Dictionary<Type, Queue<LocalBuilder>>();
m_Stack = new Stack<Type>();
m_Calls = new Stack<CallInfo>();
}
public void Define( string name, MethodAttributes attr, Type returnType, Type[] parms )
{
Method = Type.DefineMethod( name, attr, returnType, parms );
Generator = Method.GetILGenerator();
m_ArgumentTypes = parms;
}
public LocalBuilder CreateLocal( Type localType )
{
return Generator.DeclareLocal( localType );
}
public LocalBuilder AcquireTemp( Type localType )
{
if (!m_Temps.TryGetValue( localType, out Queue<LocalBuilder> list ))
m_Temps[localType] = list = new Queue<LocalBuilder>();
return list.Count > 0 ? list.Dequeue() : CreateLocal( localType );
}
public void ReleaseTemp( LocalBuilder local )
{
if (local.LocalType == null)
return;
if (!m_Temps.TryGetValue( local.LocalType, out Queue<LocalBuilder> list ))
m_Temps[local.LocalType] = list = new Queue<LocalBuilder>();
list.Enqueue( local );
}
public void Branch( Label label )
{
Generator.Emit( OpCodes.Br, label );
}
public void BranchIfFalse( Label label )
{
Pop( typeof( object ) );
Generator.Emit( OpCodes.Brfalse, label );
}
public void BranchIfTrue( Label label )
{
Pop( typeof( object ) );
Generator.Emit( OpCodes.Brtrue, label );
}
public Label CreateLabel()
{
return Generator.DefineLabel();
}
public void MarkLabel( Label label )
{
Generator.MarkLabel( label );
}
public void Pop()
{
m_Stack.Pop();
}
public void Pop( Type expected )
{
if ( expected == null )
throw new InvalidOperationException( "Expected type cannot be null." );
Type onStack = m_Stack.Pop();
if ( expected == typeof( bool ) )
expected = typeof( int );
if ( onStack == typeof( bool ) )
onStack = typeof( int );
if ( !expected.IsAssignableFrom( onStack ) )
throw new InvalidOperationException( "Unexpected stack state." );
}
public void Push( Type type )
{
m_Stack.Push( type );
}
public void Return()
{
if ( m_Stack.Count != ( Method.ReturnType == typeof( void ) ? 0 : 1 ) )
throw new InvalidOperationException( "Stack return mismatch." );
Generator.Emit( OpCodes.Ret );
}
public void LoadNull()
{
LoadNull( typeof( object ) );
}
public void LoadNull( Type type )
{
Push( type );
Generator.Emit( OpCodes.Ldnull );
}
public void Load( string value )
{
Push( typeof( string ) );
if ( value != null )
Generator.Emit( OpCodes.Ldstr, value );
else
Generator.Emit( OpCodes.Ldnull );
}
public void Load( Enum value )
{
int toLoad = ((IConvertible)value).ToInt32( null );
Load( toLoad );
Pop();
Push( value.GetType() );
}
public void Load( long value )
{
Push( typeof( long ) );
Generator.Emit( OpCodes.Ldc_I8, value );
}
public void Load( float value )
{
Push( typeof( float ) );
Generator.Emit( OpCodes.Ldc_R4, value );
}
public void Load( double value )
{
Push( typeof( double ) );
Generator.Emit( OpCodes.Ldc_R8, value );
}
public void Load( char value )
{
Load( (int) value );
Pop();
Push( typeof( char ) );
}
public void Load( bool value )
{
Push( typeof( bool ) );
if ( value )
Generator.Emit( OpCodes.Ldc_I4_1 );
else
Generator.Emit( OpCodes.Ldc_I4_0 );
}
public void Load( int value )
{
Push( typeof( int ) );
switch ( value )
{
case -1:
Generator.Emit( OpCodes.Ldc_I4_M1 );
break;
case 0:
Generator.Emit( OpCodes.Ldc_I4_0 );
break;
case 1:
Generator.Emit( OpCodes.Ldc_I4_1 );
break;
case 2:
Generator.Emit( OpCodes.Ldc_I4_2 );
break;
case 3:
Generator.Emit( OpCodes.Ldc_I4_3 );
break;
case 4:
Generator.Emit( OpCodes.Ldc_I4_4 );
break;
case 5:
Generator.Emit( OpCodes.Ldc_I4_5 );
break;
case 6:
Generator.Emit( OpCodes.Ldc_I4_6 );
break;
case 7:
Generator.Emit( OpCodes.Ldc_I4_7 );
break;
case 8:
Generator.Emit( OpCodes.Ldc_I4_8 );
break;
default:
if ( value >= sbyte.MinValue && value <= sbyte.MaxValue )
Generator.Emit( OpCodes.Ldc_I4_S, (sbyte) value );
else
Generator.Emit( OpCodes.Ldc_I4, value );
break;
}
}
public void LoadField( FieldInfo field )
{
Pop( field.DeclaringType );
Push( field.FieldType );
Generator.Emit( OpCodes.Ldfld, field );
}
public void LoadLocal( LocalBuilder local )
{
Push( local.LocalType );
int index = local.LocalIndex;
switch ( index )
{
case 0:
Generator.Emit( OpCodes.Ldloc_0 );
break;
case 1:
Generator.Emit( OpCodes.Ldloc_1 );
break;
case 2:
Generator.Emit( OpCodes.Ldloc_2 );
break;
case 3:
Generator.Emit( OpCodes.Ldloc_3 );
break;
default:
if ( index >= byte.MinValue && index <= byte.MinValue )
Generator.Emit( OpCodes.Ldloc_S, (byte) index );
else
Generator.Emit( OpCodes.Ldloc, (short) index );
break;
}
}
public void StoreLocal( LocalBuilder local )
{
Pop( local.LocalType );
Generator.Emit( OpCodes.Stloc, local );
}
public void LoadArgument( int index )
{
if ( index > 0 )
Push( m_ArgumentTypes[index - 1] );
else
Push( Type );
switch ( index )
{
case 0:
Generator.Emit( OpCodes.Ldarg_0 );
break;
case 1:
Generator.Emit( OpCodes.Ldarg_1 );
break;
case 2:
Generator.Emit( OpCodes.Ldarg_2 );
break;
case 3:
Generator.Emit( OpCodes.Ldarg_3 );
break;
default:
if ( index >= byte.MinValue && index <= byte.MaxValue )
Generator.Emit( OpCodes.Ldarg_S, (byte) index );
else
Generator.Emit( OpCodes.Ldarg, (short) index );
break;
}
}
public void CastAs( Type type )
{
Pop( typeof( object ) );
Push( type );
Generator.Emit( OpCodes.Isinst, type );
}
public void Neg()
{
Pop( typeof( int ) );
Push( typeof( int ) );
Generator.Emit( OpCodes.Neg );
}
public void Compare( OpCode opCode )
{
Pop();
Pop();
Push( typeof( int ) );
Generator.Emit( opCode );
}
public void LogicalNot()
{
Pop( typeof( int ) );
Push( typeof( int ) );
Generator.Emit( OpCodes.Ldc_I4_0 );
Generator.Emit( OpCodes.Ceq );
}
public void Xor()
{
Pop( typeof( int ) );
Pop( typeof( int ) );
Push( typeof( int ) );
Generator.Emit( OpCodes.Xor );
}
public Type Active => m_Stack.Peek();
public void Chain( Property prop )
{
for ( int i = 0; i < prop.Chain.Length; ++i )
Call( prop.Chain[i].GetGetMethod() );
}
public void Call( MethodInfo method )
{
BeginCall( method );
CallInfo call = m_Calls.Peek();
if ( call.parms.Length > 0 )
throw new InvalidOperationException( "Method requires parameters." );
FinishCall();
}
public delegate void Callback();
public bool CompareTo( int sign, Callback argGenerator )
{
Type active = Active;
MethodInfo compareTo = active.GetMethod( "CompareTo", new[] { active } );
if ( compareTo == null )
{
/* This gets a little tricky...
*
* There's a scenario where we might be trying to use CompareTo on an interface
* which, while it doesn't explicitly implement CompareTo itself, is said to
* extend IComparable indirectly. The implementation is implicitly passed off
* to implementers...
*
* interface ISomeInterface : IComparable
* {
* void SomeMethod();
* }
*
* class SomeClass : ISomeInterface
* {
* void SomeMethod() { ... }
* int CompareTo( object other ) { ... }
* }
*
* In this case, calling ISomeInterface.GetMethod( "CompareTo" ) will return null.
*
* Bleh.
*/
Type[] ifaces = active.FindInterfaces((type, obj) => type.IsGenericType
&& type.GetGenericTypeDefinition() == typeof(IComparable<>)
&& type.GetGenericArguments()[0].IsAssignableFrom(active), null );
if ( ifaces.Length > 0 )
{
compareTo = ifaces[0].GetMethod( "CompareTo", new[] { active } );
}
else
{
ifaces = active.FindInterfaces((type, obj) => type == typeof(IComparable), null );
if ( ifaces.Length > 0 )
compareTo = ifaces[0].GetMethod( "CompareTo", new[] { active } );
}
}
if ( compareTo == null )
return false;
if ( !active.IsValueType )
{
/* This object is a reference type, so we have to make it behave
*
* null.CompareTo( null ) = 0
* real.CompareTo( null ) = -1
* null.CompareTo( real ) = +1
*
*/
LocalBuilder aValue = AcquireTemp( active );
LocalBuilder bValue = AcquireTemp( active );
StoreLocal( aValue );
argGenerator();
StoreLocal( bValue );
/* if ( aValue == null )
* {
* if ( bValue == null )
* v = 0;
* else
* v = +1;
* }
* else if ( bValue == null )
* {
* v = -1;
* }
* else
* {
* v = aValue.CompareTo( bValue );
* }
*/
Label store = CreateLabel();
Label aNotNull = CreateLabel();
LoadLocal( aValue );
BranchIfTrue( aNotNull );
// if ( aValue == null )
{
Label bNotNull = CreateLabel();
LoadLocal( bValue );
BranchIfTrue( bNotNull );
// if ( bValue == null )
{
Load( 0 );
Pop( typeof( int ) );
Branch( store );
}
MarkLabel( bNotNull );
// else
{
Load( sign );
Pop( typeof( int ) );
Branch( store );
}
}
MarkLabel( aNotNull );
// else
{
Label bNotNull = CreateLabel();
LoadLocal( bValue );
BranchIfTrue( bNotNull );
// bValue == null
{
Load( -sign );
Pop( typeof( int ) );
Branch( store );
}
MarkLabel( bNotNull );
// else
{
LoadLocal( aValue );
BeginCall( compareTo );
LoadLocal( bValue );
ArgumentPushed();
FinishCall();
if ( sign == -1 )
Neg();
}
}
MarkLabel( store );
ReleaseTemp( aValue );
ReleaseTemp( bValue );
}
else
{
BeginCall( compareTo );
argGenerator();
ArgumentPushed();
FinishCall();
if ( sign == -1 )
Neg();
}
return true;
}
public void BeginCall( MethodInfo method )
{
Type type;
if ( ( method.CallingConvention & CallingConventions.HasThis ) != 0 )
type = m_Stack.Peek();
else
type = method.DeclaringType;
m_Calls.Push( new CallInfo( type, method ) );
if ( type.IsValueType )
{
LocalBuilder temp = AcquireTemp( type );
Generator.Emit( OpCodes.Stloc, temp );
Generator.Emit( OpCodes.Ldloca, temp );
ReleaseTemp( temp );
}
}
public void FinishCall()
{
CallInfo call = m_Calls.Pop();
if ( ( call.type.IsValueType || call.type.IsByRef ) && call.method.DeclaringType != call.type )
Generator.Emit( OpCodes.Constrained, call.type );
if ( call.method.DeclaringType?.IsValueType == true || call.method.IsStatic )
Generator.Emit( OpCodes.Call, call.method );
else
Generator.Emit( OpCodes.Callvirt, call.method );
for ( int i = call.parms.Length - 1; i >= 0; --i )
Pop( call.parms[i].ParameterType );
if ( ( call.method.CallingConvention & CallingConventions.HasThis ) != 0 )
Pop( call.method.DeclaringType );
if ( call.method.ReturnType != typeof( void ) )
Push( call.method.ReturnType );
}
public void ArgumentPushed()
{
CallInfo call = m_Calls.Peek();
ParameterInfo parm = call.parms[call.index++];
Type argumentType = m_Stack.Peek();
if ( !parm.ParameterType.IsAssignableFrom( argumentType ) )
throw new InvalidOperationException( "Parameter type mismatch." );
if ( argumentType.IsValueType && !parm.ParameterType.IsValueType )
Generator.Emit( OpCodes.Box, argumentType );
}
}
}

View file

@ -0,0 +1,33 @@
using System;
namespace Server.Misc
{
// This fastwalk detection is no longer required
// As of B36 PlayerMobile implements movement packet throttling which more reliably controls movement speeds
public class Fastwalk
{
private static int MaxSteps = 4; // Maximum number of queued steps until fastwalk is detected
private static bool Enabled = false; // Is fastwalk detection enabled?
private static bool UOTDOverride = false; // Should UO:TD clients not be checked for fastwalk?
private static AccessLevel
AccessOverride = AccessLevel.GameMaster; // Anyone with this or higher access level is not checked for fastwalk
public static void Initialize()
{
Mobile.FwdMaxSteps = MaxSteps;
Mobile.FwdEnabled = Enabled;
Mobile.FwdUOTDOverride = UOTDOverride;
Mobile.FwdAccessOverride = AccessOverride;
if (Enabled)
EventSink.FastWalk += OnFastWalk;
}
public static void OnFastWalk(FastWalkEventArgs e)
{
e.Blocked = true; //disallow this fastwalk
Console.WriteLine("Client: {0}: Fast movement detected (name={1})", e.NetState, e.NetState.Mobile.Name);
}
}
}

View file

@ -0,0 +1,44 @@
using System;
using Server.Network;
namespace Server.Misc
{
public class FoodDecayTimer : Timer
{
public FoodDecayTimer() : base(TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5))
{
Priority = TimerPriority.OneMinute;
}
public static void Initialize()
{
new FoodDecayTimer().Start();
}
protected override void OnTick()
{
FoodDecay();
}
public static void FoodDecay()
{
foreach (NetState state in NetState.Instances)
{
HungerDecay(state.Mobile);
ThirstDecay(state.Mobile);
}
}
public static void HungerDecay(Mobile m)
{
if (m?.Hunger >= 1)
m.Hunger -= 1;
}
public static void ThirstDecay(Mobile m)
{
if (m?.Thirst >= 1)
m.Thirst -= 1;
}
}
}

View file

@ -0,0 +1,227 @@
using System;
namespace Server.Misc
{
public delegate void DoEffect_Callback(Point3D p, Map map);
public static class Geometry
{
public static void Swap<T>(ref T a, ref T b)
{
T temp = a;
a = b;
b = temp;
}
public static double RadiansToDegrees(double angle)
{
return angle * (180.0 / Math.PI);
}
public static double DegreesToRadians(double angle)
{
return angle * (Math.PI / 180.0);
}
public static Point2D ArcPoint(Point3D loc, int radius, int angle)
{
int sideA, sideB;
if (angle < 0)
angle = 0;
if (angle > 90)
angle = 90;
sideA = (int)Math.Round(radius * Math.Sin(DegreesToRadians(angle)));
sideB = (int)Math.Round(radius * Math.Cos(DegreesToRadians(angle)));
return new Point2D(loc.X - sideB, loc.Y - sideA);
}
public static void Circle2D(Point3D loc, Map map, int radius, DoEffect_Callback effect)
{
Circle2D(loc, map, radius, effect, 0, 360);
}
public static void Circle2D(Point3D loc, Map map, int radius, DoEffect_Callback effect, int angleStart, int angleEnd)
{
if (angleStart < 0 || angleStart > 360)
angleStart = 0;
if (angleEnd > 360 || angleEnd < 0)
angleEnd = 360;
if (angleStart == angleEnd)
return;
bool opposite = angleStart > angleEnd;
int startQuadrant = angleStart / 90;
int endQuadrant = angleEnd / 90;
Point2D start = ArcPoint(loc, radius, angleStart % 90);
Point2D end = ArcPoint(loc, radius, angleEnd % 90);
if (opposite)
{
Swap(ref start, ref end);
Swap(ref startQuadrant, ref endQuadrant);
}
CirclePoint startPoint = new CirclePoint(start, angleStart, startQuadrant);
CirclePoint endPoint = new CirclePoint(end, angleEnd, endQuadrant);
int error = -radius;
int x = radius;
int y = 0;
while (x > y)
{
plot4points(loc, map, x, y, startPoint, endPoint, effect, opposite);
plot4points(loc, map, y, x, startPoint, endPoint, effect, opposite);
error += y * 2 + 1;
++y;
if (error >= 0)
{
--x;
error -= x * 2;
}
}
plot4points(loc, map, x, y, startPoint, endPoint, effect, opposite);
}
public static void plot4points(Point3D loc, Map map, int x, int y, CirclePoint start, CirclePoint end,
DoEffect_Callback effect, bool opposite)
{
Point2D pointA = new Point2D(loc.X - x, loc.Y - y);
Point2D pointB = new Point2D(loc.X - y, loc.Y - x);
int quadrant = 2;
if (x == 0 && start.Quadrant == 3)
quadrant = 3;
if (WithinCircleBounds(quadrant == 3 ? pointB : pointA, quadrant, loc, start, end, opposite))
effect(new Point3D(loc.X + x, loc.Y + y, loc.Z), map);
quadrant = 3;
if (y == 0 && start.Quadrant == 0)
quadrant = 0;
if (x != 0 && WithinCircleBounds(quadrant == 0 ? pointA : pointB, quadrant, loc, start, end, opposite))
effect(new Point3D(loc.X - x, loc.Y + y, loc.Z), map);
if (y != 0 && WithinCircleBounds(pointB, 1, loc, start, end, opposite))
effect(new Point3D(loc.X + x, loc.Y - y, loc.Z), map);
if (x != 0 && y != 0 && WithinCircleBounds(pointA, 0, loc, start, end, opposite))
effect(new Point3D(loc.X - x, loc.Y - y, loc.Z), map);
}
public static bool WithinCircleBounds(Point2D pointLoc, int pointQuadrant, Point3D center, CirclePoint start,
CirclePoint end, bool opposite)
{
if (start.Angle == 0 && end.Angle == 360)
return true;
int startX = start.Point.X;
int startY = start.Point.Y;
int endX = end.Point.X;
int endY = end.Point.Y;
int x = pointLoc.X;
int y = pointLoc.Y;
if (pointQuadrant < start.Quadrant || pointQuadrant > end.Quadrant)
return opposite;
if (pointQuadrant > start.Quadrant && pointQuadrant < end.Quadrant)
return !opposite;
bool withinBounds = true;
if (start.Quadrant == end.Quadrant)
{
if (startX == endX && (x > startX || y > startY || y < endY))
withinBounds = false;
else if (startY == endY && (y < startY || x < startX || x > endX))
withinBounds = false;
else if (x < startX || x > endX || y > startY || y < endY)
withinBounds = false;
}
else if (pointQuadrant == start.Quadrant && (x < startX || y > startY))
{
withinBounds = false;
}
else if (pointQuadrant == end.Quadrant && (x > endX || y < endY))
{
withinBounds = false;
}
return opposite ? !withinBounds : withinBounds;
}
public static void Line2D(Point3D start, Point3D end, Map map, DoEffect_Callback effect)
{
bool steep = Math.Abs(end.Y - start.Y) > Math.Abs(end.X - start.X);
int x0 = start.X;
int x1 = end.X;
int y0 = start.Y;
int y1 = end.Y;
if (steep)
{
Swap(ref x0, ref y0);
Swap(ref x1, ref y1);
}
if (x0 > x1)
{
Swap(ref x0, ref x1);
Swap(ref y0, ref y1);
}
int deltax = x1 - x0;
int deltay = Math.Abs(y1 - y0);
int error = deltax / 2;
int ystep = y0 < y1 ? 1 : -1;
int y = y0;
for (int x = x0; x <= x1; x++)
{
if (steep)
effect(new Point3D(y, x, start.Z), map);
else
effect(new Point3D(x, y, start.Z), map);
error -= deltay;
if (error < 0)
{
y += ystep;
error += deltax;
}
}
}
public class CirclePoint
{
public CirclePoint(Point2D point, int angle, int quadrant)
{
Point = point;
Angle = angle;
Quadrant = quadrant;
}
public Point2D Point{ get; }
public int Angle{ get; }
public int Quadrant{ get; }
}
}
}

View file

@ -0,0 +1,44 @@
namespace Server.Items
{
public class DecorativeTopiary : Item
{
[Constructible]
public DecorativeTopiary() : base(0x2378)
{
Weight = 1.0;
LootType = LootType.Blessed;
}
public DecorativeTopiary(Serial serial) : base(serial)
{
}
public override void OnSingleClick(Mobile from)
{
base.OnSingleClick(from);
LabelTo(from, 1070880); // Winter 2004
}
public override void GetProperties(ObjectPropertyList list)
{
base.GetProperties(list);
list.Add(1070880); // Winter 2004
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,44 @@
namespace Server.Items
{
public class FestiveCactus : Item
{
[Constructible]
public FestiveCactus() : base(0x2376)
{
Weight = 1.0;
LootType = LootType.Blessed;
}
public FestiveCactus(Serial serial) : base(serial)
{
}
public override void OnSingleClick(Mobile from)
{
base.OnSingleClick(from);
LabelTo(from, 1070880); // Winter 2004
}
public override void GetProperties(ObjectPropertyList list)
{
base.GetProperties(list);
list.Add(1070880); // Winter 2004
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,93 @@
namespace Server.Items
{
[Flippable(0x236E, 0x2371)]
public class LightOfTheWinterSolstice : Item
{
private static string[] m_StaffNames =
{
"Aenima",
"Alkiser",
"ASayre",
"David",
"Krrios",
"Mark",
"Merlin",
"Merlix", //LordMerlix
"Phantom",
"Phenos",
"psz",
"Ryan",
"Quantos",
"Outkast", //TheOutkastDev
"V", //Admin_V
"Zippy"
};
[Constructible]
public LightOfTheWinterSolstice(string dipper = null) : base(0x236E)
{
Dipper = dipper ?? m_StaffNames[Utility.Random(m_StaffNames.Length)];
Weight = 1.0;
LootType = LootType.Blessed;
Light = LightType.Circle300;
Hue = Utility.RandomDyedHue();
}
public LightOfTheWinterSolstice(Serial serial) : base(serial)
{
}
[CommandProperty(AccessLevel.GameMaster)]
public string Dipper{ get; set; }
public override void OnSingleClick(Mobile from)
{
base.OnSingleClick(from);
LabelTo(from, 1070881, Dipper); // Hand Dipped by ~1_name~
LabelTo(from, 1070880); // Winter 2004
}
public override void GetProperties(ObjectPropertyList list)
{
base.GetProperties(list);
list.Add(1070881, Dipper); // Hand Dipped by ~1_name~
list.Add(1070880); // Winter 2004
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(1); // version
writer.Write(Dipper);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 1:
{
Dipper = reader.ReadString();
break;
}
case 0:
{
Dipper = m_StaffNames[Utility.Random(m_StaffNames.Length)];
break;
}
}
if (Dipper != null)
Dipper = string.Intern(Dipper);
}
}
}

View file

@ -0,0 +1,322 @@
using System;
using Server.Gumps;
using Server.Multis;
using Server.Network;
using Server.Targeting;
namespace Server.Items
{
public class MistletoeAddon : Item, IDyable, IAddon
{
[Constructible]
public MistletoeAddon() : this(Utility.RandomDyedHue())
{
}
[Constructible]
public MistletoeAddon(int hue) : base(0x2375)
{
Hue = hue;
Movable = false;
}
public MistletoeAddon(Serial serial) : base(serial)
{
}
public bool CouldFit(IPoint3D p, Map map)
{
if (!map.CanFit(p.X, p.Y, p.Z, ItemData.Height))
return false;
if (ItemID == 0x2375)
return BaseAddon.IsWall(p.X, p.Y - 1, p.Z, map); // North wall
return BaseAddon.IsWall(p.X - 1, p.Y, p.Z, map); // West wall
}
public Item Deed => new MistletoeDeed(Hue);
public virtual bool Dye(Mobile from, DyeTub sender)
{
if (Deleted)
return false;
BaseHouse house = BaseHouse.FindHouseAt(this);
if (house?.IsCoOwner(from) == true)
{
if (from.InRange(GetWorldLocation(), 1))
{
Hue = sender.DyedHue;
return true;
}
from.SendLocalizedMessage(500295); // You are too far away to do that.
return false;
}
return false;
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
Timer.DelayCall(TimeSpan.Zero, FixMovingCrate);
}
private void FixMovingCrate()
{
if (Deleted)
return;
if (Movable || IsLockedDown)
{
Item deed = Deed;
if (Parent is Item item)
{
item.AddItem(deed);
deed.Location = Location;
}
else
{
deed.MoveToWorld(Location, Map);
}
Delete();
}
}
public override void OnDoubleClick(Mobile from)
{
BaseHouse house = BaseHouse.FindHouseAt(this);
if (house?.IsCoOwner(from) == true)
{
if (from.InRange(GetWorldLocation(), 3))
{
from.CloseGump<MistletoeAddonGump>();
from.SendGump(new MistletoeAddonGump(from, this));
}
else
{
from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that.
}
}
}
private class MistletoeAddonGump : Gump
{
private MistletoeAddon m_Addon;
private Mobile m_From;
public MistletoeAddonGump(Mobile from, MistletoeAddon addon) : base(150, 50)
{
m_From = from;
m_Addon = addon;
AddPage(0);
AddBackground(0, 0, 220, 170, 0x13BE);
AddBackground(10, 10, 200, 150, 0xBB8);
AddHtmlLocalized(20, 30, 180, 60, 1062839); // Do you wish to re-deed this decoration?
AddHtmlLocalized(55, 100, 160, 25, 1011011); // CONTINUE
AddButton(20, 100, 0xFA5, 0xFA7, 1);
AddHtmlLocalized(55, 125, 160, 25, 1011012); // CANCEL
AddButton(20, 125, 0xFA5, 0xFA7, 0);
}
public override void OnResponse(NetState sender, RelayInfo info)
{
if (m_Addon.Deleted || info.ButtonID != 1)
return;
if (m_From.InRange(m_Addon.GetWorldLocation(), 3))
{
m_From.AddToBackpack(m_Addon.Deed);
m_Addon.Delete();
}
else
{
m_From.SendLocalizedMessage(500295); // You are too far away to do that.
}
}
}
}
[Flippable(0x14F0, 0x14EF)]
public class MistletoeDeed : Item
{
[Constructible]
public MistletoeDeed(int hue = 0) : base(0x14F0)
{
Hue = hue;
Weight = 1.0;
LootType = LootType.Blessed;
}
public MistletoeDeed(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1070882; // Mistletoe Deed
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
public override void OnSingleClick(Mobile from)
{
base.OnSingleClick(from);
LabelTo(from, 1070880); // Winter 2004
}
public override void GetProperties(ObjectPropertyList list)
{
base.GetProperties(list);
list.Add(1070880); // Winter 2004
}
public override void OnDoubleClick(Mobile from)
{
if (IsChildOf(from.Backpack))
{
BaseHouse house = BaseHouse.FindHouseAt(from);
if (house?.IsCoOwner(from) == true)
{
from.SendLocalizedMessage(1062838); // Where would you like to place this decoration?
from.BeginTarget(-1, true, TargetFlags.None, Placement_OnTarget);
}
else
{
from.SendLocalizedMessage(502092); // You must be in your house to do this.
}
}
else
{
from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it.
}
}
public void Placement_OnTarget(Mobile from, object targeted)
{
if (!(targeted is IPoint3D p))
return;
Point3D loc = new Point3D(p);
BaseHouse house = BaseHouse.FindHouseAt(loc, from.Map, 16);
if (house?.IsCoOwner(from) == true)
{
bool northWall = BaseAddon.IsWall(loc.X, loc.Y - 1, loc.Z, from.Map);
bool westWall = BaseAddon.IsWall(loc.X - 1, loc.Y, loc.Z, from.Map);
if (northWall && westWall)
from.SendGump(new MistletoeDeedGump(from, loc, this));
else
PlaceAddon(from, loc, northWall, westWall);
}
else
{
from.SendLocalizedMessage(1042036); // That location is not in your house.
}
}
private void PlaceAddon(Mobile from, Point3D loc, bool northWall, bool westWall)
{
if (Deleted)
return;
BaseHouse house = BaseHouse.FindHouseAt(loc, from.Map, 16);
if (house == null || !house.IsCoOwner(from))
{
from.SendLocalizedMessage(1042036); // That location is not in your house.
return;
}
int itemID = 0;
if (northWall)
itemID = 0x2374;
else if (westWall)
itemID = 0x2375;
else
from.SendLocalizedMessage(1070883); // The mistletoe must be placed next to a wall.
if (itemID > 0)
{
Item addon = new MistletoeAddon(Hue);
addon.ItemID = itemID;
addon.MoveToWorld(loc, from.Map);
house.Addons.Add(addon);
Delete();
}
}
private class MistletoeDeedGump : Gump
{
private MistletoeDeed m_Deed;
private Mobile m_From;
private Point3D m_Loc;
public MistletoeDeedGump(Mobile from, Point3D loc, MistletoeDeed deed) : base(150, 50)
{
m_From = from;
m_Loc = loc;
m_Deed = deed;
AddBackground(0, 0, 300, 150, 0xA28);
AddPage(0);
AddItem(90, 30, 0x2375);
AddItem(180, 30, 0x2374);
AddButton(50, 35, 0x868, 0x869, 1);
AddButton(145, 35, 0x868, 0x869, 2);
}
public override void OnResponse(NetState sender, RelayInfo info)
{
if (m_Deed.Deleted)
return;
switch (info.ButtonID)
{
case 1:
m_Deed.PlaceAddon(m_From, m_Loc, false, true);
break;
case 2:
m_Deed.PlaceAddon(m_From, m_Loc, true, false);
break;
}
}
}
}
}

View file

@ -0,0 +1,153 @@
using System;
using Server.Engines.ConPVP;
using Server.Targeting;
namespace Server.Items
{
public class PileOfGlacialSnow : Item
{
[Constructible]
public PileOfGlacialSnow() : base(0x913)
{
Hue = 0x480;
Weight = 1.0;
LootType = LootType.Blessed;
}
public PileOfGlacialSnow(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1070874; // a Pile of Glacial Snow
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(1); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
if (version == 0)
{
Weight = 1.0;
LootType = LootType.Blessed;
}
}
public override void OnSingleClick(Mobile from)
{
base.OnSingleClick(from);
LabelTo(from, 1070880); // Winter 2004
}
public override void GetProperties(ObjectPropertyList list)
{
base.GetProperties(list);
list.Add(1070880); // Winter 2004
}
public override void OnDoubleClick(Mobile from)
{
if (!IsChildOf(from.Backpack))
{
from.SendLocalizedMessage(1042010); // You must have the object in your backpack to use it.
}
else if (from.Mounted)
{
from.SendLocalizedMessage(1010097); // You cannot use this while mounted.
}
else if (from.CanBeginAction<SnowPile>())
{
from.SendLocalizedMessage(1005575); // You carefully pack the snow into a ball...
from.Target = new SnowTarget(from, this);
}
else
{
from.SendLocalizedMessage(1005574); // The snow is not ready to be packed yet. Keep trying.
}
}
private class InternalTimer : Timer
{
private Mobile m_From;
public InternalTimer(Mobile from) : base(TimeSpan.FromSeconds(5.0))
{
m_From = from;
}
protected override void OnTick()
{
m_From.EndAction<SnowPile>();
}
}
private class SnowTarget : Target
{
private Item m_Snow;
private Mobile m_Thrower;
public SnowTarget(Mobile thrower, Item snow) : base(10, false, TargetFlags.None)
{
m_Thrower = thrower;
m_Snow = snow;
}
protected override void OnTarget(Mobile from, object target)
{
if (target == from)
{
from.SendLocalizedMessage(1005576); // You can't throw this at yourself.
}
else if (target is Mobile targ)
{
Container pack = targ.Backpack;
if (from.Region.IsPartOf<SafeZone>() || targ.Region.IsPartOf<SafeZone>())
{
from.SendMessage("You may not throw snow here.");
}
else if (pack?.FindItemByType(new[] { typeof(SnowPile), typeof(PileOfGlacialSnow) }) != null)
{
if (from.BeginAction<SnowPile>())
{
new InternalTimer(from).Start();
from.PlaySound(0x145);
from.Animate(9, 1, 1, true, false, 0);
targ.SendLocalizedMessage(1010572); // You have just been hit by a snowball!
from.SendLocalizedMessage(1010573); // You throw the snowball and hit the target!
Effects.SendMovingEffect(from, targ, 0x36E4, 7, 0, false, true, 0x47F);
}
else
{
from.SendLocalizedMessage(1005574); // The snow is not ready to be packed yet. Keep trying.
}
}
else
{
from.SendLocalizedMessage(
1005577); // You can only throw a snowball at something that can throw one back.
}
}
else
{
from.SendLocalizedMessage(
1005577); // You can only throw a snowball at something that can throw one back.
}
}
}
}
}

View file

@ -0,0 +1,139 @@
using System;
using Server.Engines.ConPVP;
using Server.Targeting;
namespace Server.Items
{
public class SnowPile : Item
{
[Constructible]
public SnowPile() : base(0x913)
{
Hue = 0x481;
Weight = 1.0;
LootType = LootType.Blessed;
}
public SnowPile(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1005578; // a pile of snow
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(1); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
if (version == 0)
{
Weight = 1.0;
LootType = LootType.Blessed;
}
}
public override void OnDoubleClick(Mobile from)
{
if (!IsChildOf(from.Backpack))
{
from.SendLocalizedMessage(1042010); // You must have the object in your backpack to use it.
}
else if (from.Mounted)
{
from.SendLocalizedMessage(1010097); // You cannot use this while mounted.
}
else if (from.CanBeginAction<SnowPile>())
{
from.SendLocalizedMessage(1005575); // You carefully pack the snow into a ball...
from.Target = new SnowTarget(from, this);
}
else
{
from.SendLocalizedMessage(1005574); // The snow is not ready to be packed yet. Keep trying.
}
}
private class InternalTimer : Timer
{
private Mobile m_From;
public InternalTimer(Mobile from) : base(TimeSpan.FromSeconds(5.0))
{
m_From = from;
}
protected override void OnTick()
{
m_From.EndAction<SnowPile>();
}
}
private class SnowTarget : Target
{
private Item m_Snow;
private Mobile m_Thrower;
public SnowTarget(Mobile thrower, Item snow) : base(10, false, TargetFlags.None)
{
m_Thrower = thrower;
m_Snow = snow;
}
protected override void OnTarget(Mobile from, object target)
{
if (target == from)
{
from.SendLocalizedMessage(1005576); // You can't throw this at yourself.
}
else if (target is Mobile targ)
{
Container pack = targ.Backpack;
if (from.Region.IsPartOf<SafeZone>() || targ.Region.IsPartOf<SafeZone>())
{
from.SendMessage("You may not throw snow here.");
}
else if (pack?.FindItemByType(new[] { typeof(SnowPile), typeof(PileOfGlacialSnow) }) != null)
{
if (from.BeginAction<SnowPile>())
{
new InternalTimer(from).Start();
from.PlaySound(0x145);
from.Animate(9, 1, 1, true, false, 0);
targ.SendLocalizedMessage(1010572); // You have just been hit by a snowball!
from.SendLocalizedMessage(1010573); // You throw the snowball and hit the target!
Effects.SendMovingEffect(from, targ, 0x36E4, 7, 0, false, true, 0x480);
}
else
{
from.SendLocalizedMessage(1005574); // The snow is not ready to be packed yet. Keep trying.
}
}
else
{
from.SendLocalizedMessage(
1005577); // You can only throw a snowball at something that can throw one back.
}
}
else
{
from.SendLocalizedMessage(
1005577); // You can only throw a snowball at something that can throw one back.
}
}
}
}
}

View file

@ -0,0 +1,44 @@
namespace Server.Items
{
public class SnowyTree : Item
{
[Constructible]
public SnowyTree() : base(0x2377)
{
Weight = 1.0;
LootType = LootType.Blessed;
}
public SnowyTree(Serial serial) : base(serial)
{
}
public override void OnSingleClick(Mobile from)
{
base.OnSingleClick(from);
LabelTo(from, 1070880); // Winter 2004
}
public override void GetProperties(ObjectPropertyList list)
{
base.GetProperties(list);
list.Add(1070880); // Winter 2004
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,44 @@
using System;
using Server.Items;
namespace Server.Misc
{
public class WinterGiftGiver2004 : GiftGiver
{
public override DateTime Start => new DateTime(2004, 12, 24);
public override DateTime Finish => new DateTime(2005, 1, 1);
public static void Initialize()
{
GiftGiving.Register(new WinterGiftGiver2004());
}
public override void GiveGift(Mobile mob)
{
GiftBox box = new GiftBox();
box.DropItem(new MistletoeDeed());
box.DropItem(new PileOfGlacialSnow());
box.DropItem(new LightOfTheWinterSolstice());
int random = Utility.Random(100);
if (random < 60)
box.DropItem(new DecorativeTopiary());
else if (random < 84)
box.DropItem(new FestiveCactus());
else
box.DropItem(new SnowyTree());
switch (GiveGift(mob, box))
{
case GiftResult.Backpack:
mob.SendMessage(0x482, "Happy Holidays from the team! Gift items have been placed in your backpack.");
break;
case GiftResult.BankBox:
mob.SendMessage(0x482, "Happy Holidays from the team! Gift items have been placed in your bank box.");
break;
}
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,172 @@
using System;
using Server.Accounting;
using Server.Commands;
using Server.Gumps;
using Server.Network;
using Server.Targeting;
namespace Server
{
public class HardwareInfo
{
[CommandProperty(AccessLevel.GameMaster)]
public int CpuModel{ get; private set; }
[CommandProperty(AccessLevel.GameMaster)]
public int CpuClockSpeed{ get; private set; }
[CommandProperty(AccessLevel.GameMaster)]
public int CpuQuantity{ get; private set; }
[CommandProperty(AccessLevel.GameMaster)]
public int OSMajor{ get; private set; }
[CommandProperty(AccessLevel.GameMaster)]
public int OSMinor{ get; private set; }
[CommandProperty(AccessLevel.GameMaster)]
public int OSRevision{ get; private set; }
[CommandProperty(AccessLevel.GameMaster)]
public int InstanceID{ get; private set; }
[CommandProperty(AccessLevel.GameMaster)]
public int ScreenWidth{ get; private set; }
[CommandProperty(AccessLevel.GameMaster)]
public int ScreenHeight{ get; private set; }
[CommandProperty(AccessLevel.GameMaster)]
public int ScreenDepth{ get; private set; }
[CommandProperty(AccessLevel.GameMaster)]
public int PhysicalMemory{ get; private set; }
[CommandProperty(AccessLevel.GameMaster)]
public int CpuManufacturer{ get; private set; }
[CommandProperty(AccessLevel.GameMaster)]
public int CpuFamily{ get; private set; }
[CommandProperty(AccessLevel.GameMaster)]
public int VCVendorID{ get; private set; }
[CommandProperty(AccessLevel.GameMaster)]
public int VCDeviceID{ get; private set; }
[CommandProperty(AccessLevel.GameMaster)]
public int VCMemory{ get; private set; }
[CommandProperty(AccessLevel.GameMaster)]
public int DXMajor{ get; private set; }
[CommandProperty(AccessLevel.GameMaster)]
public int DXMinor{ get; private set; }
[CommandProperty(AccessLevel.GameMaster)]
public string VCDescription{ get; private set; }
[CommandProperty(AccessLevel.GameMaster)]
public string Language{ get; private set; }
[CommandProperty(AccessLevel.GameMaster)]
public int Distribution{ get; private set; }
[CommandProperty(AccessLevel.GameMaster)]
public int ClientsRunning{ get; private set; }
[CommandProperty(AccessLevel.GameMaster)]
public int ClientsInstalled{ get; private set; }
[CommandProperty(AccessLevel.GameMaster)]
public int PartialInstalled{ get; private set; }
[CommandProperty(AccessLevel.GameMaster)]
public string Unknown{ get; private set; }
[CommandProperty(AccessLevel.GameMaster)]
public DateTime TimeReceived{ get; private set; }
public static void Initialize()
{
PacketHandlers.Register(0xD9, 0x10C, false, OnReceive);
CommandSystem.Register("HWInfo", AccessLevel.GameMaster, HWInfo_OnCommand);
}
[Usage("HWInfo")]
[Description("Displays information about a targeted player's hardware.")]
public static void HWInfo_OnCommand(CommandEventArgs e)
{
e.Mobile.BeginTarget(-1, false, TargetFlags.None, HWInfo_OnTarget);
e.Mobile.SendMessage("Target a player to view their hardware information.");
}
public static void HWInfo_OnTarget(Mobile from, object obj)
{
if (obj is Mobile m && m.Player)
{
if (m.Account is Account acct)
{
HardwareInfo hwInfo = acct.HardwareInfo;
if (hwInfo != null)
CommandLogging.WriteLine(from, "{0} {1} viewing hardware info of {2}", from.AccessLevel,
CommandLogging.Format(from), CommandLogging.Format(m));
if (hwInfo != null)
from.SendGump(new PropertiesGump(from, hwInfo));
else
from.SendMessage("No hardware information for that account was found.");
}
else
{
from.SendMessage("No account has been attached to that player.");
}
}
else
{
from.BeginTarget(-1, false, TargetFlags.None, HWInfo_OnTarget);
from.SendMessage("That is not a player. Try again.");
}
}
public static void OnReceive(NetState state, PacketReader pvSrc)
{
pvSrc.ReadByte(); // 1: <4.0.1a, 2>=4.0.1a
HardwareInfo info = new HardwareInfo();
info.InstanceID = pvSrc.ReadInt32();
info.OSMajor = pvSrc.ReadInt32();
info.OSMinor = pvSrc.ReadInt32();
info.OSRevision = pvSrc.ReadInt32();
info.CpuManufacturer = pvSrc.ReadByte();
info.CpuFamily = pvSrc.ReadInt32();
info.CpuModel = pvSrc.ReadInt32();
info.CpuClockSpeed = pvSrc.ReadInt32();
info.CpuQuantity = pvSrc.ReadByte();
info.PhysicalMemory = pvSrc.ReadInt32();
info.ScreenWidth = pvSrc.ReadInt32();
info.ScreenHeight = pvSrc.ReadInt32();
info.ScreenDepth = pvSrc.ReadInt32();
info.DXMajor = pvSrc.ReadInt16();
info.DXMinor = pvSrc.ReadInt16();
info.VCDescription = pvSrc.ReadUnicodeStringLESafe(64);
info.VCVendorID = pvSrc.ReadInt32();
info.VCDeviceID = pvSrc.ReadInt32();
info.VCMemory = pvSrc.ReadInt32();
info.Distribution = pvSrc.ReadByte();
info.ClientsRunning = pvSrc.ReadByte();
info.ClientsInstalled = pvSrc.ReadByte();
info.PartialInstalled = pvSrc.ReadByte();
info.Language = pvSrc.ReadUnicodeStringLESafe(4);
info.Unknown = pvSrc.ReadStringSafe(64);
info.TimeReceived = DateTime.UtcNow;
if (state.Account is Account acct)
acct.HardwareInfo = info;
}
}
}

View file

@ -0,0 +1,564 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Server.Misc
{
[Flags]
public enum IHSFlags
{
None = 0x00,
OnDamaged = 0x01,
OnDeath = 0x02,
OnMovement = 0x04,
OnSpeech = 0x08,
All = OnDamaged | OnDeath | OnMovement
} // NOTE: To enable monster conversations, add " | OnSpeech" to the "All" line
public class InhumanSpeech
{
private static InhumanSpeech m_RatmanSpeech;
public static InhumanSpeech Ratman
{
get
{
if ( m_RatmanSpeech == null )
{
m_RatmanSpeech = new InhumanSpeech();
m_RatmanSpeech.Hue = 149;
m_RatmanSpeech.Sound = 438;
m_RatmanSpeech.Flags = IHSFlags.All;
m_RatmanSpeech.Keywords = new[]
{
"meat", "gold", "kill", "killing", "slay",
"sword", "axe", "spell", "magic", "spells",
"swords", "axes", "mace", "maces", "monster",
"monsters", "food", "run", "escape", "away",
"help", "dead", "die", "dying", "lose",
"losing", "life", "lives", "death", "ghost",
"ghosts", "british", "blackthorn", "guild",
"guilds", "dragon", "dragons", "game", "games",
"ultima", "silly", "stupid", "dumb", "idiot",
"idiots", "cheesy", "cheezy", "crazy", "dork",
"jerk", "fool", "foolish", "ugly", "insult", "scum"
};
m_RatmanSpeech.Responses = new[]
{
"meat", "kill", "pound", "crush", "yum yum",
"crunch", "destroy", "murder", "eat", "munch",
"massacre", "food", "monster", "evil", "run",
"die", "lose", "dumb", "idiot", "fool", "crazy",
"dinner", "lunch", "breakfast", "fight", "battle",
"doomed", "rip apart", "tear apart", "smash",
"edible?", "shred", "disembowel", "ugly", "smelly",
"stupid", "hideous", "smell", "tasty", "invader",
"attack", "raid", "plunder", "pillage", "treasure",
"loser", "lose", "scum"
};
m_RatmanSpeech.Syllables = new[]
{
"skrit",
"ch", "ch",
"it", "ti", "it", "ti",
"ak", "ek", "ik", "ok", "uk", "yk",
"ka", "ke", "ki", "ko", "ku", "ky",
"at", "et", "it", "ot", "ut", "yt",
"cha", "che", "chi", "cho", "chu", "chy",
"ach", "ech", "ich", "och", "uch", "ych",
"att", "ett", "itt", "ott", "utt", "ytt",
"tat", "tet", "tit", "tot", "tut", "tyt",
"tta", "tte", "tti", "tto", "ttu", "tty",
"tak", "tek", "tik", "tok", "tuk", "tyk",
"ack", "eck", "ick", "ock", "uck", "yck",
"cka", "cke", "cki", "cko", "cku", "cky",
"rak", "rek", "rik", "rok", "ruk", "ryk",
"tcha", "tche", "tchi", "tcho", "tchu", "tchy",
"rach", "rech", "rich", "roch", "ruch", "rych",
"rrap", "rrep", "rrip", "rrop", "rrup", "rryp",
"ccka", "ccke", "ccki", "ccko", "ccku", "ccky"
};
}
return m_RatmanSpeech;
}
}
private static InhumanSpeech m_OrcSpeech;
public static InhumanSpeech Orc
{
get
{
if ( m_OrcSpeech == null )
{
m_OrcSpeech = new InhumanSpeech();
m_OrcSpeech.Hue = 34;
m_OrcSpeech.Sound = 432;
m_OrcSpeech.Flags = IHSFlags.All;
m_OrcSpeech.Keywords = new[]
{
"meat", "gold", "kill", "killing", "slay",
"sword", "axe", "spell", "magic", "spells",
"swords", "axes", "mace", "maces", "monster",
"monsters", "food", "run", "escape", "away",
"help", "dead", "die", "dying", "lose",
"losing", "life", "lives", "death", "ghost",
"ghosts", "british", "blackthorn", "guild",
"guilds", "dragon", "dragons", "game", "games",
"ultima", "silly", "stupid", "dumb", "idiot",
"idiots", "cheesy", "cheezy", "crazy", "dork",
"jerk", "fool", "foolish", "ugly", "insult", "scum"
};
m_OrcSpeech.Responses = new[]
{
"meat", "kill", "pound", "crush", "yum yum",
"crunch", "destroy", "murder", "eat", "munch",
"massacre", "food", "monster", "evil", "run",
"die", "lose", "dumb", "idiot", "fool", "crazy",
"dinner", "lunch", "breakfast", "fight", "battle",
"doomed", "rip apart", "tear apart", "smash",
"edible?", "shred", "disembowel", "ugly", "smelly",
"stupid", "hideous", "smell", "tasty", "invader",
"attack", "raid", "plunder", "pillage", "treasure",
"loser", "lose", "scum"
};
m_OrcSpeech.Syllables = new[]
{
"bu", "du", "fu", "ju", "gu",
"ulg", "gug", "gub", "gur", "oog",
"gub", "log", "ru", "stu", "glu",
"ug", "ud", "og", "log", "ro", "flu",
"bo", "duf", "fun", "nog", "dun", "bog",
"dug", "gh", "ghu", "gho", "nug", "ig",
"igh", "ihg", "luh", "duh", "bug", "dug",
"dru", "urd", "gurt", "grut", "grunt",
"snarf", "urgle", "igg", "glu", "glug",
"foo", "bar", "baz", "ghat", "ab", "ad",
"gugh", "guk", "ag", "alm", "thu", "log",
"bilge", "augh", "gha", "gig", "goth",
"zug", "pig", "auh", "gan", "azh", "bag",
"hig", "oth", "dagh", "gulg", "ugh", "ba",
"bid", "gug", "bug", "rug", "hat", "brui",
"gagh", "buad", "buil", "buim", "bum",
"hug", "hug", "buo", "ma", "buor", "ghed",
"buu", "ca", "guk", "clog", "thurg", "car",
"cro", "thu", "da", "cuk", "gil", "cur", "dak",
"dar", "deak", "der", "dil", "dit", "at", "ag",
"dor", "gar", "dre", "tk", "dri", "gka", "rim",
"eag", "egg", "ha", "rod", "eg", "lat", "eichel",
"ek", "ep", "ka", "it", "ut", "ewk", "ba", "dagh",
"faugh", "foz", "fog", "fid", "fruk", "gag", "fub",
"fud", "fur", "bog", "fup", "hagh", "gaa", "kt",
"rekk", "lub", "lug", "tug", "gna", "urg", "l",
"gno", "gnu", "gol", "gom", "kug", "ukk", "jak",
"jek", "rukk", "jja", "akt", "nuk", "hok", "hrol",
"olm", "natz", "i", "i", "o", "u", "ikk", "ign",
"juk", "kh", "kgh", "ka", "hig", "ke", "ki", "klap",
"klu", "knod", "kod", "knu", "thnu", "krug", "nug",
"nar", "nag", "neg", "neh", "oag", "ob", "ogh", "oh",
"om", "dud", "oo", "pa", "hrak", "qo", "quad", "quil",
"ghig", "rur", "sag", "sah", "sg"
};
}
return m_OrcSpeech;
}
}
private static InhumanSpeech m_LizardmanSpeech;
public static InhumanSpeech Lizardman
{
get
{
if ( m_LizardmanSpeech == null )
{
m_LizardmanSpeech = new InhumanSpeech();
m_LizardmanSpeech.Hue = 58;
m_LizardmanSpeech.Sound = 418;
m_LizardmanSpeech.Flags = IHSFlags.All;
m_LizardmanSpeech.Keywords = new[]
{
"meat", "gold", "kill", "killing", "slay",
"sword", "axe", "spell", "magic", "spells",
"swords", "axes", "mace", "maces", "monster",
"monsters", "food", "run", "escape", "away",
"help", "dead", "die", "dying", "lose",
"losing", "life", "lives", "death", "ghost",
"ghosts", "british", "blackthorn", "guild",
"guilds", "dragon", "dragons", "game", "games",
"ultima", "silly", "stupid", "dumb", "idiot",
"idiots", "cheesy", "cheezy", "crazy", "dork",
"jerk", "fool", "foolish", "ugly", "insult", "scum"
};
m_LizardmanSpeech.Responses = new[]
{
"meat", "kill", "pound", "crush", "yum yum",
"crunch", "destroy", "murder", "eat", "munch",
"massacre", "food", "monster", "evil", "run",
"die", "lose", "dumb", "idiot", "fool", "crazy",
"dinner", "lunch", "breakfast", "fight", "battle",
"doomed", "rip apart", "tear apart", "smash",
"edible?", "shred", "disembowel", "ugly", "smelly",
"stupid", "hideous", "smell", "tasty", "invader",
"attack", "raid", "plunder", "pillage", "treasure",
"loser", "lose", "scum"
};
m_LizardmanSpeech.Syllables = new[]
{
"ss", "sth", "iss", "is", "ith", "kth",
"sith", "this", "its", "sit", "tis", "tsi",
"ssi", "sil", "lis", "sis", "lil", "thil",
"lith", "sthi", "lish", "shi", "shash", "sal",
"miss", "ra", "tha", "thes", "ses", "sas", "las",
"les", "sath", "sia", "ais", "isa", "asi", "asth",
"stha", "sthi", "isth", "asa", "ath", "tha", "als",
"sla", "thth", "ci", "ce", "cy", "yss", "ys", "yth",
"syth", "thys", "yts", "syt", "tys", "tsy", "ssy",
"syl", "lys", "sys", "lyl", "thyl", "lyth", "sthy",
"lysh", "shy", "myss", "ysa", "sthy", "ysth"
};
}
return m_LizardmanSpeech;
}
}
private static InhumanSpeech m_WispSpeech;
public static InhumanSpeech Wisp
{
get
{
if ( m_WispSpeech == null )
{
m_WispSpeech = new InhumanSpeech();
m_WispSpeech.Hue = 89;
m_WispSpeech.Sound = 466;
m_WispSpeech.Flags = IHSFlags.OnMovement;
m_WispSpeech.Syllables = new[]
{
"b", "c", "d", "f", "g", "h", "i",
"j", "k", "l", "m", "n", "p", "r",
"s", "t", "v", "w", "x", "z", "c",
"c", "x", "x", "x", "x", "x", "y",
"y", "y", "y", "t", "t", "k", "k",
"l", "l", "m", "m", "m", "m", "z"
};
}
return m_WispSpeech;
}
}
private string[] m_Keywords;
private Dictionary<string, string> m_KeywordHash;
public string[] Syllables { get; set; }
public string[] Keywords
{
get => m_Keywords;
set
{
m_Keywords = value;
m_KeywordHash = new Dictionary<string, string>( m_Keywords.Length, StringComparer.OrdinalIgnoreCase );
for ( int i = 0; i < m_Keywords.Length; ++i )
m_KeywordHash[m_Keywords[i]] = m_Keywords[i];
}
}
public string[] Responses { get; set; }
public int Hue { get; set; }
public int Sound { get; set; }
public IHSFlags Flags { get; set; }
public string GetRandomSyllable()
{
return Syllables[Utility.Random( Syllables.Length )];
}
public string ConstructWord( int syllableCount )
{
string[] syllables = new string[syllableCount];
for ( int i = 0; i < syllableCount; ++i )
syllables[i] = GetRandomSyllable();
return string.Concat( syllables );
}
public string ConstructSentance( int wordCount )
{
StringBuilder sentance = new StringBuilder();
bool needUpperCase = true;
for ( int i = 0; i < wordCount; ++i )
{
if ( i > 0 ) // not first word )
{
int random = Utility.RandomMinMax( 1, 15 );
if ( random < 11 )
{
sentance.Append( ' ' );
}
else
{
needUpperCase = true;
if ( random > 13 )
sentance.Append( "! " );
else
sentance.Append( ". " );
}
}
int syllableCount;
if ( 30 > Utility.Random( 100 ) )
syllableCount = Utility.Random( 1, 5 );
else
syllableCount = Utility.Random( 1, 3 );
string 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();
}
public void SayRandomTranslate( Mobile mob, params string[] sentancesInEnglish )
{
SaySentance( mob, Utility.RandomMinMax( 2, 3 ) );
mob.Say( sentancesInEnglish[Utility.Random( sentancesInEnglish.Length )] );
}
private string GetRandomResponseWord( List<string> keywordsFound )
{
int random = Utility.Random( keywordsFound.Count + Responses.Length );
if ( random < keywordsFound.Count )
return keywordsFound[random];
return Responses[random - keywordsFound.Count];
}
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;
string[] split = text.Split( ' ' );
List<string> keywordsFound = new List<string>();
for ( int i = 0; i < split.Length; ++i )
{
if (m_KeywordHash.TryGetValue( split[i], out string keyword ))
keywordsFound.Add( keyword );
}
if ( keywordsFound.Count > 0 )
{
string responseWord;
if ( Utility.RandomBool() )
responseWord = GetRandomResponseWord( keywordsFound );
else
responseWord = keywordsFound[Utility.Random( keywordsFound.Count )];
string secondResponseWord = GetRandomResponseWord( keywordsFound );
StringBuilder response = new StringBuilder();
switch ( Utility.Random( 6 ) )
{
default:
case 0:
{
response.Append( "Me " ).Append( responseWord ).Append( '?' );
break;
}
case 1:
{
response.Append( responseWord ).Append( " thee!" );
response.Replace( responseWord[0], char.ToUpper( responseWord[0] ), 0, 1 );
break;
}
case 2:
{
response.Append( responseWord ).Append( '?' );
response.Replace( responseWord[0], char.ToUpper( responseWord[0] ), 0, 1 );
break;
}
case 3:
{
response.Append( responseWord ).Append( "! " ).Append( secondResponseWord ).Append( '.' );
response.Replace( responseWord[0], char.ToUpper( responseWord[0] ), 0, 1 );
response.Replace( secondResponseWord[0], char.ToUpper( secondResponseWord[0] ), responseWord.Length + 2, 1 );
break;
}
case 4:
{
response.Append( responseWord ).Append( '.' );
response.Replace( responseWord[0], char.ToUpper( responseWord[0] ), 0, 1 );
break;
}
case 5:
{
response.Append( responseWord ).Append( "? " ).Append( secondResponseWord ).Append( '.' );
response.Replace( responseWord[0], char.ToUpper( responseWord[0] ), 0, 1 );
response.Replace( secondResponseWord[0], char.ToUpper( secondResponseWord[0] ), responseWord.Length + 2, 1 );
break;
}
}
int 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() );
return true;
}
return false;
}
public void OnDeath( Mobile mob )
{
if ( (Flags & IHSFlags.OnDeath) == 0 )
return; // not enabled
if ( 90 > Utility.Random( 100 ) )
return; // 90% chance to do nothing; 10% chance to talk
SayRandomTranslate( mob,
"Revenge!",
"NOOooo!",
"I... I...",
"Me no die!",
"Me die!",
"Must... not die...",
"Oooh, me hurt...",
"Me dying?" );
}
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 ( 90 > Utility.Random( 100 ) )
return; // 90% chance to do nothing; 10% chance to talk
SaySentance( mob, 6 );
}
public void OnDamage( Mobile mob, int amount )
{
if ( (Flags & IHSFlags.OnDamaged) == 0 )
return; // not enabled
if ( 90 > Utility.Random( 100 ) )
return; // 90% chance to do nothing; 10% chance to talk
if ( amount < 5 )
{
SayRandomTranslate( mob,
"Ouch!",
"Me not hurt bad!",
"Thou fight bad.",
"Thy blows soft!",
"You bad with weapon!" );
}
else
{
SayRandomTranslate( mob,
"Ouch! Me hurt!",
"No, kill me not!",
"Me hurt!",
"Away with thee!",
"Oof! That hurt!",
"Aaah! That hurt...",
"Good blow!" );
}
}
public void OnConstruct( Mobile mob )
{
mob.SpeechHue = Hue;
}
public void SaySentance( Mobile mob, int wordCount )
{
mob.Say( ConstructSentance( wordCount ) );
mob.PlaySound( Sound );
}
public InhumanSpeech()
{
}
}
}

View file

@ -0,0 +1,54 @@
using Server.Guilds;
using Server.Gumps;
using Server.Mobiles;
namespace Server.Misc
{
public class Keywords
{
public static void Initialize()
{
// Register our speech handler
EventSink.Speech += EventSink_Speech;
}
public static void EventSink_Speech(SpeechEventArgs args)
{
Mobile from = args.Mobile;
int[] keywords = args.Keywords;
for (int i = 0; i < keywords.Length; ++i)
switch (keywords[i])
{
case 0x002A: // *i resign from my guild*
{
((Guild)from.Guild)?.RemoveMember(from);
break;
}
case 0x0032: // *i must consider my sins*
{
if (!Core.SE)
{
from.SendMessage("Short Term Murders : {0}", from.ShortTermMurders);
from.SendMessage("Long Term Murders : {0}", from.Kills);
}
else
{
from.SendMessage(0x3B2, "Short Term Murders: {0} Long Term Murders: {1}", from.ShortTermMurders,
from.Kills);
}
break;
}
case 0x0035: // i renounce my young player status*
{
if (from is PlayerMobile mobile && mobile.Young && !mobile.HasGump<RenounceYoungGump>())
mobile.SendGump(new RenounceYoungGump());
break;
}
}
}
}
}

View file

@ -0,0 +1,353 @@
using System.Collections.Generic;
using System.IO;
using Server.Accounting;
using Server.Commands;
namespace Server.Misc
{
/**
* This file requires to be saved in a Unicode
* compatible format.
*
* Warning: if you change String.Format methods,
* please note that the following character
* is suggested before any left-to-right text
* in order to prevent undesired formatting
* resulting from mixing LR and RL text:
*
* Use this one if you need to force RL:
*
* If you do not see the above chars, please
* enable showing of unicode control chars
**/
public class LanguageStatistics
{
private static InternationalCode[] InternationalCodes =
{
new InternationalCode("ARA", "Arabic", "Saudi Arabia", "العربية", "السعودية"),
new InternationalCode("ARI", "Arabic", "Iraq", "العربية", "العراق"),
new InternationalCode("ARE", "Arabic", "Egypt", "العربية", "مصر"),
new InternationalCode("ARL", "Arabic", "Libya", "العربية", "ليبيا"),
new InternationalCode("ARG", "Arabic", "Algeria", "العربية", "الجزائر"),
new InternationalCode("ARM", "Arabic", "Morocco", "العربية", "المغرب"),
new InternationalCode("ART", "Arabic", "Tunisia", "العربية", "تونس"),
new InternationalCode("ARO", "Arabic", "Oman", "العربية", "عمان"),
new InternationalCode("ARY", "Arabic", "Yemen", "العربية", "اليمن"),
new InternationalCode("ARS", "Arabic", "Syria", "العربية", "سورية"),
new InternationalCode("ARJ", "Arabic", "Jordan", "العربية", "الأردن"),
new InternationalCode("ARB", "Arabic", "Lebanon", "العربية", "لبنان"),
new InternationalCode("ARK", "Arabic", "Kuwait", "العربية", "الكويت"),
new InternationalCode("ARU", "Arabic", "U.A.E.", "العربية", "الامارات"),
new InternationalCode("ARH", "Arabic", "Bahrain", "العربية", "البحرين"),
new InternationalCode("ARQ", "Arabic", "Qatar", "العربية", "قطر"),
new InternationalCode("BGR", "Bulgarian", "Bulgaria", "Български", "България"),
new InternationalCode("CAT", "Catalan", "Spain", "Català", "Espanya"),
new InternationalCode("CHT", "Chinese", "Taiwan", "台語", "臺灣"),
new InternationalCode("CHS", "Chinese", "PRC", "中文", "中国"),
new InternationalCode("ZHH", "Chinese", "Hong Kong", "中文", "香港"),
new InternationalCode("ZHI", "Chinese", "Singapore", "中文", "新加坡"),
new InternationalCode("ZHM", "Chinese", "Macau", "中文", "澳門"),
new InternationalCode("CSY", "Czech", "Czech Republic", "Čeština", "Česká republika"),
new InternationalCode("DAN", "Danish", "Denmark", "Dansk", "Danmark"),
new InternationalCode("DEU", "German", "Germany", "Deutsch", "Deutschland"),
new InternationalCode("DES", "German", "Switzerland", "Deutsch", "der Schweiz"),
new InternationalCode("DEA", "German", "Austria", "Deutsch", "Österreich"),
new InternationalCode("DEL", "German", "Luxembourg", "Deutsch", "Luxembourg"),
new InternationalCode("DEC", "German", "Liechtenstein", "Deutsch", "Liechtenstein"),
new InternationalCode("ELL", "Greek", "Greece", "Ελληνικά", "Ελλάδα"),
new InternationalCode("ENU", "English", "United States"),
new InternationalCode("ENG", "English", "United Kingdom"),
new InternationalCode("ENA", "English", "Australia"),
new InternationalCode("ENC", "English", "Canada"),
new InternationalCode("ENZ", "English", "New Zealand"),
new InternationalCode("ENI", "English", "Ireland"),
new InternationalCode("ENS", "English", "South Africa"),
new InternationalCode("ENJ", "English", "Jamaica"),
new InternationalCode("ENB", "English", "Caribbean"),
new InternationalCode("ENL", "English", "Belize"),
new InternationalCode("ENT", "English", "Trinidad"),
new InternationalCode("ENW", "English", "Zimbabwe"),
new InternationalCode("ENP", "English", "Philippines"),
new InternationalCode("ESP", "Spanish", "Spain (Traditional Sort)", "Español", "España (tipo tradicional)"),
new InternationalCode("ESM", "Spanish", "Mexico", "Español", "México"),
new InternationalCode("ESN", "Spanish", "Spain (International Sort)", "Español", "España (tipo internacional)"),
new InternationalCode("ESG", "Spanish", "Guatemala", "Español", "Guatemala"),
new InternationalCode("ESC", "Spanish", "Costa Rica", "Español", "Costa Rica"),
new InternationalCode("ESA", "Spanish", "Panama", "Español", "Panama"),
new InternationalCode("ESD", "Spanish", "Dominican Republic", "Español", "Republica Dominicana"),
new InternationalCode("ESV", "Spanish", "Venezuela", "Español", "Venezuela"),
new InternationalCode("ESO", "Spanish", "Colombia", "Español", "Colombia"),
new InternationalCode("ESR", "Spanish", "Peru", "Español", "Peru"),
new InternationalCode("ESS", "Spanish", "Argentina", "Español", "Argentina"),
new InternationalCode("ESF", "Spanish", "Ecuador", "Español", "Ecuador"),
new InternationalCode("ESL", "Spanish", "Chile", "Español", "Chile"),
new InternationalCode("ESY", "Spanish", "Uruguay", "Español", "Uruguay"),
new InternationalCode("ESZ", "Spanish", "Paraguay", "Español", "Paraguay"),
new InternationalCode("ESB", "Spanish", "Bolivia", "Español", "Bolivia"),
new InternationalCode("ESE", "Spanish", "El Salvador", "Español", "El Salvador"),
new InternationalCode("ESH", "Spanish", "Honduras", "Español", "Honduras"),
new InternationalCode("ESI", "Spanish", "Nicaragua", "Español", "Nicaragua"),
new InternationalCode("ESU", "Spanish", "Puerto Rico", "Español", "Puerto Rico"),
new InternationalCode("FIN", "Finnish", "Finland", "Suomi", "Suomi"),
new InternationalCode("FRA", "French", "France", "Français", "France"),
new InternationalCode("FRB", "French", "Belgium", "Français", "Belgique"),
new InternationalCode("FRC", "French", "Canada", "Français", "Canada"),
new InternationalCode("FRS", "French", "Switzerland", "Français", "Suisse"),
new InternationalCode("FRL", "French", "Luxembourg", "Français", "Luxembourg"),
new InternationalCode("FRM", "French", "Monaco", "Français", "Monaco"),
new InternationalCode("HEB", "Hebrew", "Israel", "עִבְרִית", "ישׂראל"),
new InternationalCode("HUN", "Hungarian", "Hungary", "Magyar", "Magyarország"),
new InternationalCode("ISL", "Icelandic", "Iceland", "Íslenska", "Ísland"),
new InternationalCode("ITA", "Italian", "Italy", "Italiano", "Italia"),
new InternationalCode("ITS", "Italian", "Switzerland", "Italiano", "Svizzera"),
new InternationalCode("JPN", "Japanese", "Japan", "日本語", "日本"),
new InternationalCode("KOR", "Korean (Extended Wansung)", "Korea", "한국어", "한국"),
new InternationalCode("NLD", "Dutch", "Netherlands", "Nederlands", "Nederland"),
new InternationalCode("NLB", "Dutch", "Belgium", "Nederlands", "België"),
new InternationalCode("NOR", "Norwegian", "Norway (Bokmål)", "Norsk", "Norge (Bokmål)"),
new InternationalCode("NON", "Norwegian", "Norway (Nynorsk)", "Norsk", "Norge (Nynorsk)"),
new InternationalCode("PLK", "Polish", "Poland", "Polski", "Polska"),
new InternationalCode("PTB", "Portuguese", "Brazil", "Português", "Brasil"),
new InternationalCode("PTG", "Portuguese", "Portugal", "Português", "Brasil"),
new InternationalCode("ROM", "Romanian", "Romania", "Limba Română", "România"),
new InternationalCode("RUS", "Russian", "Russia", "Русский", "Россия"),
new InternationalCode("HRV", "Croatian", "Croatia", "Hrvatski", "Hrvatska"),
new InternationalCode("SRL", "Serbian", "Serbia (Latin)", "Srpski", "Srbija i Crna Gora"),
new InternationalCode("SRB", "Serbian", "Serbia (Cyrillic)", "Српски", "Србија и Црна Гора"),
new InternationalCode("SKY", "Slovak", "Slovakia", "Slovenčina", "Slovensko"),
new InternationalCode("SQI", "Albanian", "Albania", "Shqip", "Shqipëria"),
new InternationalCode("SVE", "Swedish", "Sweden", "Svenska", "Sverige"),
new InternationalCode("SVF", "Swedish", "Finland", "Svenska", "Finland"),
new InternationalCode("THA", "Thai", "Thailand", "ภาษาไทย", "ประเทศไทย"),
new InternationalCode("TRK", "Turkish", "Turkey", "Türkçe", "Türkiye"),
new InternationalCode("URP", "Urdu", "Pakistan", "اردو", "پاکستان"),
new InternationalCode("IND", "Indonesian", "Indonesia", "Bahasa Indonesia", "Indonesia"),
new InternationalCode("UKR", "Ukrainian", "Ukraine", "Українська", "Украина"),
new InternationalCode("BEL", "Belarusian", "Belarus", "Беларускі", "Беларусь"),
new InternationalCode("SLV", "Slovene", "Slovenia", "Slovenščina", "Slovenija"),
new InternationalCode("ETI", "Estonian", "Estonia", "Eesti", "Eesti"),
new InternationalCode("LVI", "Latvian", "Latvia", "Latviešu", "Latvija"),
new InternationalCode("LTH", "Lithuanian", "Lithuania", "Lietuvių", "Lietuva"),
new InternationalCode("LTC", "Classic Lithuanian", "Lithuania", "Lietuviškai", "Lietuva"),
new InternationalCode("FAR", "Farsi", "Iran", "فارسى", "ايران"),
new InternationalCode("VIT", "Vietnamese", "Viet Nam", "tiếng Việt", "Việt Nam"),
new InternationalCode("HYE", "Armenian", "Armenia", "Հայերէն", "Հայաստան"),
new InternationalCode("AZE", "Azeri", "Azerbaijan (Latin)", "Azərbaycanca", "Azərbaycan"),
new InternationalCode("AZE", "Azeri", "Azerbaijan (Cyrillic)", "Азәрбајҹанҹа", "Азәрбајҹан"),
new InternationalCode("EUQ", "Basque", "Spain", "Euskera", "Espainia"),
new InternationalCode("MKI", "Macedonian", "Macedonia", "Македонски", "Македонија"),
new InternationalCode("AFK", "Afrikaans", "South Africa", "Afrikaans", "Republiek van Suid-Afrika"),
new InternationalCode("KAT", "Georgian", "Georgia", "ქართული", "საკარტველო"),
new InternationalCode("FOS", "Faeroese", "Faeroe Islands", "Føroyska", "Føroya"),
new InternationalCode("HIN", "Hindi", "India", "हिन्दी", "भारत"),
new InternationalCode("MSL", "Malay", "Malaysia", "Bahasa melayu", "Malaysia"),
new InternationalCode("MSB", "Malay", "Brunei Darussalam", "Bahasa melayu", "Negara Brunei Darussalam"),
new InternationalCode("KAZ", "Kazak", "Kazakstan", "Қазақ", "Қазақстан"),
new InternationalCode("SWK", "Swahili", "Kenya", "Kiswahili", "Kenya"),
new InternationalCode("UZB", "Uzbek", "Uzbekistan (Latin)", "O'zbek", "O'zbekiston"),
new InternationalCode("UZB", "Uzbek", "Uzbekistan (Cyrillic)", "Ўзбек", "Ўзбекистон"),
new InternationalCode("TAT", "Tatar", "Tatarstan", "Татарча", "Татарстан"),
new InternationalCode("BEN", "Bengali", "India", "বাংলা", "ভারত"),
new InternationalCode("PAN", "Punjabi", "India", "ਪੰਜਾਬੀ", "ਭਾਰਤ"),
new InternationalCode("GUJ", "Gujarati", "India", "ગુજરાતી", "ભારત"),
new InternationalCode("ORI", "Oriya", "India", "ଓଡ଼ିଆ", "ଭାରତ"),
new InternationalCode("TAM", "Tamil", "India", "தமிழ்", "இந்தியா"),
new InternationalCode("TEL", "Telugu", "India", "తెలుగు", "భారత"),
new InternationalCode("KAN", "Kannada", "India", "ಕನ್ನಡ", "ಭಾರತ"),
new InternationalCode("MAL", "Malayalam", "India", "മലയാളം", "ഭാരത"),
new InternationalCode("ASM", "Assamese", "India", "অসমিয়া", "Bhārat"), // missing correct country name
new InternationalCode("MAR", "Marathi", "India", "मराठी", "भारत"),
new InternationalCode("SAN", "Sanskrit", "India", "संस्कृत", "भारतम्"),
new InternationalCode("KOK", "Konkani", "India", "कोंकणी", "भारत")
};
private static bool DefaultLocalNames = false;
private static bool ShowAlternatives = true;
private static bool CountAccounts = true; // will consider only first character's valid language
private static string GetFormattedInfo(string code)
{
if (code == null || code.Length != 3)
return $"Unknown code {code}";
for (int i = 0; i < InternationalCodes.Length; i++)
if (code == InternationalCodes[i].Code)
return $"{InternationalCodes[i].GetName()}";
return $"Unknown code {code}";
}
public static void Initialize()
{
CommandSystem.Register("LanguageStatistics", AccessLevel.Administrator, LanguageStatistics_OnCommand);
}
[Usage("LanguageStatistics")]
[Description("Generate a file containing the list of languages for each PlayerMobile.")]
public static void LanguageStatistics_OnCommand(CommandEventArgs e)
{
Dictionary<string, InternationalCodeCounter> ht = new Dictionary<string, InternationalCodeCounter>();
using (StreamWriter writer = new StreamWriter("languages.txt"))
{
if (CountAccounts)
foreach (Account acc in Accounts.GetAccounts())
for (int i = 0; i < acc.Length; i++)
{
Mobile mob = acc[i];
string lang = mob?.Language;
if (lang == null)
continue;
lang = lang.ToUpper();
if (ht.TryGetValue(lang, out InternationalCodeCounter codes))
codes.Increase();
else
ht[lang] = new InternationalCodeCounter(lang);
break;
}
else
foreach (Mobile mob in World.Mobiles.Values)
if (mob.Player)
{
string lang = mob.Language;
if (lang == null)
continue;
lang = lang.ToUpper();
if (ht.TryGetValue(lang, out InternationalCodeCounter codes))
codes.Increase();
else
ht[lang] = new InternationalCodeCounter(lang);
}
writer.WriteLine(
$"Language statistics. Numbers show how many {(CountAccounts ? "accounts" : "playermobile")} use the specified language.");
writer.WriteLine(
"====================================================================================================");
writer.WriteLine();
// sort the list
List<InternationalCodeCounter> list = new List<InternationalCodeCounter>(ht.Values);
list.Sort(InternationalCodeComparer.Instance);
foreach (InternationalCodeCounter c in list)
writer.WriteLine($"{GetFormattedInfo(c.Code)} : {c.Count}");
e.Mobile.SendMessage("Languages list generated.");
}
}
private struct InternationalCode
{
private bool m_HasLocalInfo;
public string Code{ get; }
public string Language{ get; }
public string Country{ get; }
public string Language_LocalName{ get; }
public string Country_LocalName{ get; }
public InternationalCode(string code, string language, string country) : this(code, language, country, null,
null)
{
m_HasLocalInfo = false;
}
public InternationalCode(string code, string language, string country, string language_localname,
string country_localname)
{
Code = code;
Language = language;
Country = country;
Language_LocalName = language_localname;
Country_LocalName = country_localname;
m_HasLocalInfo = true;
}
public string GetName()
{
string s;
if (m_HasLocalInfo)
{
s =
$"{(DefaultLocalNames ? Language_LocalName : Language)} - {(DefaultLocalNames ? Country_LocalName : Country)}";
if (ShowAlternatives)
s +=
$" 【{(DefaultLocalNames ? Language : Language_LocalName)} - {(DefaultLocalNames ? Country : Country_LocalName)}‎】";
}
else
{
s = $"{Language} - {Country}";
}
return s;
}
}
private class InternationalCodeCounter
{
public InternationalCodeCounter(string code)
{
Code = code;
Count = 1;
}
public string Code{ get; }
public int Count{ get; private set; }
public void Increase()
{
Count++;
}
}
private class InternationalCodeComparer : IComparer<InternationalCodeCounter>
{
public static readonly InternationalCodeComparer Instance = new InternationalCodeComparer();
public int Compare(InternationalCodeCounter x, InternationalCodeCounter y)
{
string a = null, b = null;
int ca = 0, cb = 0;
a = x.Code;
ca = x.Count;
b = y.Code;
cb = y.Count;
if (ca > cb)
return -1;
if (ca < cb)
return 1;
if (a == null && b == null)
return 0;
if (a == null)
return 1;
if (b == null)
return -1;
return a.CompareTo(b);
}
}
}
}

View file

@ -0,0 +1,137 @@
using System;
using Server.Commands;
using Server.Items;
using Server.Network;
namespace Server
{
public class LightCycle
{
public const int DayLevel = 0;
public const int NightLevel = 12;
public const int DungeonLevel = 26;
public const int JailLevel = 9;
private static int m_LevelOverride = int.MinValue;
public static int LevelOverride
{
get => m_LevelOverride;
set
{
m_LevelOverride = value;
for (int i = 0; i < NetState.Instances.Count; ++i)
{
NetState ns = NetState.Instances[i];
Mobile m = ns.Mobile;
m?.CheckLightLevels(false);
}
}
}
public static void Initialize()
{
new LightCycleTimer().Start();
EventSink.Login += OnLogin;
CommandSystem.Register("GlobalLight", AccessLevel.GameMaster, Light_OnCommand);
}
[Usage("GlobalLight <value>")]
[Description("Sets the current global light level.")]
private static void Light_OnCommand(CommandEventArgs e)
{
if (e.Length >= 1)
{
LevelOverride = e.GetInt32(0);
e.Mobile.SendMessage("Global light level override has been changed to {0}.", m_LevelOverride);
}
else
{
LevelOverride = int.MinValue;
e.Mobile.SendMessage("Global light level override has been cleared.");
}
}
public static void OnLogin(LoginEventArgs args)
{
Mobile m = args.Mobile;
m.CheckLightLevels(true);
}
public static int ComputeLevelFor(Mobile from)
{
if (m_LevelOverride > int.MinValue)
return m_LevelOverride;
Clock.GetTime(from.Map, from.X, from.Y, out int hours, out int minutes);
/* OSI times:
*
* Midnight -> 3:59 AM : Night
* 4:00 AM -> 11:59 PM : Day
*
* RunUO times:
*
* 10:00 PM -> 11:59 PM : Scale to night
* Midnight -> 3:59 AM : Night
* 4:00 AM -> 5:59 AM : Scale to day
* 6:00 AM -> 9:59 PM : Day
*/
if (hours < 4)
return NightLevel;
if (hours < 6)
return NightLevel + ((hours - 4) * 60 + minutes) * (DayLevel - NightLevel) / 120;
if (hours < 22)
return DayLevel;
if (hours < 24)
return DayLevel + ((hours - 22) * 60 + minutes) * (NightLevel - DayLevel) / 120;
return NightLevel; // should never be
}
private class LightCycleTimer : Timer
{
public LightCycleTimer() : base(TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(5.0))
{
Priority = TimerPriority.FiveSeconds;
}
protected override void OnTick()
{
for (int i = 0; i < NetState.Instances.Count; ++i)
{
NetState ns = NetState.Instances[i];
Mobile m = ns.Mobile;
m?.CheckLightLevels(false);
}
}
}
public class NightSightTimer : Timer
{
private Mobile m_Owner;
public NightSightTimer(Mobile owner) : base(TimeSpan.FromMinutes(Utility.Random(15, 25)))
{
m_Owner = owner;
Priority = TimerPriority.OneMinute;
}
protected override void OnTick()
{
m_Owner.EndAction<LightCycle>();
m_Owner.LightLevel = 0;
BuffInfo.RemoveBuff(m_Owner, BuffIcon.NightSight);
}
}
}
}

View file

@ -0,0 +1,30 @@
using Server.Network;
namespace Server.Misc
{
public class LoginStats
{
public static void Initialize()
{
// Register our event handler
EventSink.Login += EventSink_Login;
}
private static void EventSink_Login(LoginEventArgs args)
{
int userCount = NetState.Instances.Count;
int itemCount = World.Items.Count;
int mobileCount = World.Mobiles.Count;
Mobile m = args.Mobile;
m.SendMessage(
"Welcome, {0}! There {1} currently {2} user{3} online, with {4} item{5} and {6} mobile{7} in the world.",
args.Mobile.Name,
userCount == 1 ? "is" : "are",
userCount, userCount == 1 ? "" : "s",
itemCount, itemCount == 1 ? "" : "s",
mobileCount, mobileCount == 1 ? "" : "s");
}
}
}

View file

@ -0,0 +1,817 @@
using System;
using Server.Items;
namespace Server
{
public class Loot
{
#region List definitions
#region Mondain's Legacy
public static Type[] MLWeaponTypes{ get; } =
{
typeof(AssassinSpike), typeof(DiamondMace), typeof(ElvenMachete),
typeof(ElvenSpellblade), typeof(Leafblade), typeof(OrnateAxe),
typeof(RadiantScimitar), typeof(RuneBlade), typeof(WarCleaver),
typeof(WildStaff)
};
public static Type[] MLRangedWeaponTypes{ get; } =
{
typeof(ElvenCompositeLongbow), typeof(MagicalShortbow)
};
public static Type[] MLArmorTypes{ get; } =
{
typeof(Circlet), typeof(GemmedCirclet), typeof(LeafTonlet),
typeof(RavenHelm), typeof(RoyalCirclet), typeof(VultureHelm),
typeof(WingedHelm), typeof(LeafArms), typeof(LeafChest),
typeof(LeafGloves), typeof(LeafGorget), typeof(LeafLegs),
typeof(WoodlandArms), typeof(WoodlandChest), typeof(WoodlandGloves),
typeof(WoodlandGorget), typeof(WoodlandLegs), typeof(HideChest),
typeof(HideGloves), typeof(HideGorget), typeof(HidePants),
typeof(HidePauldrons)
};
public static Type[] MLClothingTypes{ get; } =
{
typeof(MaleElvenRobe), typeof(FemaleElvenRobe), typeof(ElvenPants),
typeof(ElvenShirt), typeof(ElvenDarkShirt), typeof(ElvenBoots),
typeof(VultureHelm), typeof(WoodlandBelt)
};
#endregion
public static Type[] SEWeaponTypes{ get; } =
{
typeof(Bokuto), typeof(Daisho), typeof(Kama),
typeof(Lajatang), typeof(NoDachi), typeof(Nunchaku),
typeof(Sai), typeof(Tekagi), typeof(Tessen),
typeof(Tetsubo), typeof(Wakizashi)
};
public static Type[] AosWeaponTypes{ get; } =
{
typeof(Scythe), typeof(BoneHarvester), typeof(Scepter),
typeof(BladedStaff), typeof(Pike), typeof(DoubleBladedStaff),
typeof(Lance), typeof(CrescentBlade)
};
public static Type[] WeaponTypes{ get; } =
{
typeof(Axe), typeof(BattleAxe), typeof(DoubleAxe),
typeof(ExecutionersAxe), typeof(Hatchet), typeof(LargeBattleAxe),
typeof(TwoHandedAxe), typeof(WarAxe), typeof(Club),
typeof(Mace), typeof(Maul), typeof(WarHammer),
typeof(WarMace), typeof(Bardiche), typeof(Halberd),
typeof(Spear), typeof(ShortSpear), typeof(Pitchfork),
typeof(WarFork), typeof(BlackStaff), typeof(GnarledStaff),
typeof(QuarterStaff), typeof(Broadsword), typeof(Cutlass),
typeof(Katana), typeof(Kryss), typeof(Longsword),
typeof(Scimitar), typeof(VikingSword), typeof(Pickaxe),
typeof(HammerPick), typeof(ButcherKnife), typeof(Cleaver),
typeof(Dagger), typeof(SkinningKnife), typeof(ShepherdsCrook)
};
public static Type[] SERangedWeaponTypes{ get; } =
{
typeof(Yumi)
};
public static Type[] AosRangedWeaponTypes{ get; } =
{
typeof(CompositeBow), typeof(RepeatingCrossbow)
};
public static Type[] RangedWeaponTypes{ get; } =
{
typeof(Bow), typeof(Crossbow), typeof(HeavyCrossbow)
};
public static Type[] SEArmorTypes{ get; } =
{
typeof(ChainHatsuburi), typeof(LeatherDo), typeof(LeatherHaidate),
typeof(LeatherHiroSode), typeof(LeatherJingasa), typeof(LeatherMempo),
typeof(LeatherNinjaHood), typeof(LeatherNinjaJacket), typeof(LeatherNinjaMitts),
typeof(LeatherNinjaPants), typeof(LeatherSuneate), typeof(DecorativePlateKabuto),
typeof(HeavyPlateJingasa), typeof(LightPlateJingasa), typeof(PlateBattleKabuto),
typeof(PlateDo), typeof(PlateHaidate), typeof(PlateHatsuburi),
typeof(PlateHiroSode), typeof(PlateMempo), typeof(PlateSuneate),
typeof(SmallPlateJingasa), typeof(StandardPlateKabuto), typeof(StuddedDo),
typeof(StuddedHaidate), typeof(StuddedHiroSode), typeof(StuddedMempo),
typeof(StuddedSuneate)
};
public static Type[] ArmorTypes{ get; } =
{
typeof(BoneArms), typeof(BoneChest), typeof(BoneGloves),
typeof(BoneLegs), typeof(BoneHelm), typeof(ChainChest),
typeof(ChainLegs), typeof(ChainCoif), typeof(Bascinet),
typeof(CloseHelm), typeof(Helmet), typeof(NorseHelm),
typeof(OrcHelm), typeof(FemaleLeatherChest), typeof(LeatherArms),
typeof(LeatherBustierArms), typeof(LeatherChest), typeof(LeatherGloves),
typeof(LeatherGorget), typeof(LeatherLegs), typeof(LeatherShorts),
typeof(LeatherSkirt), typeof(LeatherCap), typeof(FemalePlateChest),
typeof(PlateArms), typeof(PlateChest), typeof(PlateGloves),
typeof(PlateGorget), typeof(PlateHelm), typeof(PlateLegs),
typeof(RingmailArms), typeof(RingmailChest), typeof(RingmailGloves),
typeof(RingmailLegs), typeof(FemaleStuddedChest), typeof(StuddedArms),
typeof(StuddedBustierArms), typeof(StuddedChest), typeof(StuddedGloves),
typeof(StuddedGorget), typeof(StuddedLegs)
};
public static Type[] AosShieldTypes{ get; } =
{
typeof(ChaosShield), typeof(OrderShield)
};
public static Type[] ShieldTypes{ get; } =
{
typeof(BronzeShield), typeof(Buckler), typeof(HeaterShield),
typeof(MetalShield), typeof(MetalKiteShield), typeof(WoodenKiteShield),
typeof(WoodenShield)
};
public static Type[] GemTypes{ get; } =
{
typeof(Amber), typeof(Amethyst), typeof(Citrine),
typeof(Diamond), typeof(Emerald), typeof(Ruby),
typeof(Sapphire), typeof(StarSapphire), typeof(Tourmaline)
};
public static Type[] JewelryTypes{ get; } =
{
typeof(GoldRing), typeof(GoldBracelet),
typeof(SilverRing), typeof(SilverBracelet)
};
public static Type[] RegTypes{ get; } =
{
typeof(BlackPearl), typeof(Bloodmoss), typeof(Garlic),
typeof(Ginseng), typeof(MandrakeRoot), typeof(Nightshade),
typeof(SulfurousAsh), typeof(SpidersSilk)
};
public static Type[] NecroRegTypes{ get; } =
{
typeof(BatWing), typeof(GraveDust), typeof(DaemonBlood),
typeof(NoxCrystal), typeof(PigIron)
};
public static Type[] PotionTypes{ get; } =
{
typeof(AgilityPotion), typeof(StrengthPotion), typeof(RefreshPotion),
typeof(LesserCurePotion), typeof(LesserHealPotion), typeof(LesserPoisonPotion)
};
public static Type[] SEInstrumentTypes{ get; } =
{
typeof(BambooFlute)
};
public static Type[] InstrumentTypes{ get; } =
{
typeof(Drums), typeof(Harp), typeof(LapHarp),
typeof(Lute), typeof(Tambourine), typeof(TambourineTassel)
};
public static Type[] StatueTypes{ get; } =
{
typeof(StatueSouth), typeof(StatueSouth2), typeof(StatueNorth),
typeof(StatueWest), typeof(StatueEast), typeof(StatueEast2),
typeof(StatueSouthEast), typeof(BustSouth), typeof(BustEast)
};
#region Mondain's Legacy
#endregion
public static Type[] RegularScrollTypes{ get; } =
{
typeof(ReactiveArmorScroll), typeof(ClumsyScroll), typeof(CreateFoodScroll), typeof(FeeblemindScroll),
typeof(HealScroll), typeof(MagicArrowScroll), typeof(NightSightScroll), typeof(WeakenScroll),
typeof(AgilityScroll), typeof(CunningScroll), typeof(CureScroll), typeof(HarmScroll),
typeof(MagicTrapScroll), typeof(MagicUnTrapScroll), typeof(ProtectionScroll), typeof(StrengthScroll),
typeof(BlessScroll), typeof(FireballScroll), typeof(MagicLockScroll), typeof(PoisonScroll),
typeof(TelekinisisScroll), typeof(TeleportScroll), typeof(UnlockScroll), typeof(WallOfStoneScroll),
typeof(ArchCureScroll), typeof(ArchProtectionScroll), typeof(CurseScroll), typeof(FireFieldScroll),
typeof(GreaterHealScroll), typeof(LightningScroll), typeof(ManaDrainScroll), typeof(RecallScroll),
typeof(BladeSpiritsScroll), typeof(DispelFieldScroll), typeof(IncognitoScroll), typeof(MagicReflectScroll),
typeof(MindBlastScroll), typeof(ParalyzeScroll), typeof(PoisonFieldScroll), typeof(SummonCreatureScroll),
typeof(DispelScroll), typeof(EnergyBoltScroll), typeof(ExplosionScroll), typeof(InvisibilityScroll),
typeof(MarkScroll), typeof(MassCurseScroll), typeof(ParalyzeFieldScroll), typeof(RevealScroll),
typeof(ChainLightningScroll), typeof(EnergyFieldScroll), typeof(FlamestrikeScroll), typeof(GateTravelScroll),
typeof(ManaVampireScroll), typeof(MassDispelScroll), typeof(MeteorSwarmScroll), typeof(PolymorphScroll),
typeof(EarthquakeScroll), typeof(EnergyVortexScroll), typeof(ResurrectionScroll),
typeof(SummonAirElementalScroll),
typeof(SummonDaemonScroll), typeof(SummonEarthElementalScroll), typeof(SummonFireElementalScroll),
typeof(SummonWaterElementalScroll)
};
public static Type[] NecromancyScrollTypes{ get; } =
{
typeof(AnimateDeadScroll), typeof(BloodOathScroll), typeof(CorpseSkinScroll), typeof(CurseWeaponScroll),
typeof(EvilOmenScroll), typeof(HorrificBeastScroll), typeof(LichFormScroll), typeof(MindRotScroll),
typeof(PainSpikeScroll), typeof(PoisonStrikeScroll), typeof(StrangleScroll), typeof(SummonFamiliarScroll),
typeof(VampiricEmbraceScroll), typeof(VengefulSpiritScroll), typeof(WitherScroll), typeof(WraithFormScroll)
};
public static Type[] SENecromancyScrollTypes{ get; } =
{
typeof(AnimateDeadScroll), typeof(BloodOathScroll), typeof(CorpseSkinScroll), typeof(CurseWeaponScroll),
typeof(EvilOmenScroll), typeof(HorrificBeastScroll), typeof(LichFormScroll), typeof(MindRotScroll),
typeof(PainSpikeScroll), typeof(PoisonStrikeScroll), typeof(StrangleScroll), typeof(SummonFamiliarScroll),
typeof(VampiricEmbraceScroll), typeof(VengefulSpiritScroll), typeof(WitherScroll), typeof(WraithFormScroll),
typeof(ExorcismScroll)
};
public static Type[] PaladinScrollTypes{ get; } = new Type[0];
#region Mondain's Legacy
public static Type[] ArcanistScrollTypes{ get; } =
{
typeof(ArcaneCircleScroll), typeof(GiftOfRenewalScroll), typeof(ImmolatingWeaponScroll),
typeof(AttuneWeaponScroll),
typeof(ThunderstormScroll),
typeof(NatureFuryScroll), /*typeof( SummonFeyScroll ), typeof( SummonFiendScroll ),*/
typeof(ReaperFormScroll), typeof(WildfireScroll), typeof(EssenceOfWindScroll), typeof(DryadAllureScroll),
typeof(EtherealVoyageScroll), typeof(WordOfDeathScroll), typeof(GiftOfLifeScroll),
typeof(ArcaneEmpowermentScroll)
};
#endregion
public static Type[] GrimmochJournalTypes{ get; } =
{
typeof(GrimmochJournal1), typeof(GrimmochJournal2), typeof(GrimmochJournal3),
typeof(GrimmochJournal6), typeof(GrimmochJournal7), typeof(GrimmochJournal11),
typeof(GrimmochJournal14), typeof(GrimmochJournal17), typeof(GrimmochJournal23)
};
public static Type[] LysanderNotebookTypes{ get; } =
{
typeof(LysanderNotebook1), typeof(LysanderNotebook2), typeof(LysanderNotebook3),
typeof(LysanderNotebook7), typeof(LysanderNotebook8), typeof(LysanderNotebook11)
};
public static Type[] TavarasJournalTypes{ get; } =
{
typeof(TavarasJournal1), typeof(TavarasJournal2), typeof(TavarasJournal3),
typeof(TavarasJournal6), typeof(TavarasJournal7), typeof(TavarasJournal8),
typeof(TavarasJournal9), typeof(TavarasJournal11), typeof(TavarasJournal14),
typeof(TavarasJournal16), typeof(TavarasJournal16b), typeof(TavarasJournal17),
typeof(TavarasJournal19)
};
public static Type[] NewWandTypes{ get; } =
{
typeof(FireballWand), typeof(LightningWand), typeof(MagicArrowWand),
typeof(GreaterHealWand), typeof(HarmWand), typeof(HealWand)
};
public static Type[] WandTypes{ get; } =
{
typeof(ClumsyWand), typeof(FeebleWand),
typeof(ManaDrainWand), typeof(WeaknessWand)
};
public static Type[] OldWandTypes{ get; } =
{
typeof(IDWand)
};
public static Type[] SEClothingTypes{ get; } =
{
typeof(ClothNinjaJacket), typeof(FemaleKimono), typeof(Hakama),
typeof(HakamaShita), typeof(JinBaori), typeof(Kamishimo),
typeof(MaleKimono), typeof(NinjaTabi), typeof(Obi),
typeof(SamuraiTabi), typeof(TattsukeHakama), typeof(Waraji)
};
public static Type[] AosClothingTypes{ get; } =
{
typeof(FurSarong), typeof(FurCape), typeof(FlowerGarland),
typeof(GildedDress), typeof(FurBoots), typeof(FormalShirt)
};
public static Type[] ClothingTypes{ get; } =
{
typeof(Cloak),
typeof(Bonnet), typeof(Cap), typeof(FeatheredHat),
typeof(FloppyHat), typeof(JesterHat), typeof(Surcoat),
typeof(SkullCap), typeof(StrawHat), typeof(TallStrawHat),
typeof(TricorneHat), typeof(WideBrimHat), typeof(WizardsHat),
typeof(BodySash), typeof(Doublet), typeof(Boots),
typeof(FullApron), typeof(JesterSuit), typeof(Sandals),
typeof(Tunic), typeof(Shoes), typeof(Shirt),
typeof(Kilt), typeof(Skirt), typeof(FancyShirt),
typeof(FancyDress), typeof(ThighBoots), typeof(LongPants),
typeof(PlainDress), typeof(Robe), typeof(ShortPants),
typeof(HalfApron)
};
public static Type[] SEHatTypes{ get; } =
{
typeof(ClothNinjaHood), typeof(Kasa)
};
public static Type[] AosHatTypes{ get; } =
{
typeof(FlowerGarland), typeof(BearMask),
typeof(DeerMask) //Are Bear& Deer mask inside the Pre-AoS loottables too?
};
public static Type[] HatTypes{ get; } =
{
typeof(SkullCap), typeof(Bandana), typeof(FloppyHat),
typeof(Cap), typeof(WideBrimHat), typeof(StrawHat),
typeof(TallStrawHat), typeof(WizardsHat), typeof(Bonnet),
typeof(FeatheredHat), typeof(TricorneHat), typeof(JesterHat)
};
public static Type[] LibraryBookTypes{ get; } =
{
typeof(GrammarOfOrcish), typeof(CallToAnarchy), typeof(ArmsAndWeaponsPrimer),
typeof(SongOfSamlethe), typeof(TaleOfThreeTribes), typeof(GuideToGuilds),
typeof(BirdsOfBritannia), typeof(BritannianFlora), typeof(ChildrenTalesVol2),
typeof(TalesOfVesperVol1), typeof(DeceitDungeonOfHorror), typeof(DimensionalTravel),
typeof(EthicalHedonism), typeof(MyStory), typeof(DiversityOfOurLand),
typeof(QuestOfVirtues), typeof(RegardingLlamas), typeof(TalkingToWisps),
typeof(TamingDragons), typeof(BoldStranger), typeof(BurningOfTrinsic),
typeof(TheFight), typeof(LifeOfATravellingMinstrel), typeof(MajorTradeAssociation),
typeof(RankingsOfTrades), typeof(WildGirlOfTheForest), typeof(TreatiseOnAlchemy),
typeof(VirtueBook)
};
#endregion
#region Accessors
public static BaseWand RandomWand()
{
if (Core.ML)
return Construct(NewWandTypes) as BaseWand;
if (Core.AOS)
return Construct(WandTypes, NewWandTypes) as BaseWand;
return Construct(OldWandTypes, WandTypes, NewWandTypes) as BaseWand;
}
public static BaseClothing RandomClothing()
{
return RandomClothing(false, false);
}
public static BaseClothing RandomClothing(bool inTokuno, bool isMondain)
{
#region Mondain's Legacy
if (Core.ML && isMondain)
return Construct(MLClothingTypes, AosClothingTypes, ClothingTypes) as BaseClothing;
#endregion
if (Core.SE && inTokuno)
return Construct(SEClothingTypes, AosClothingTypes, ClothingTypes) as BaseClothing;
if (Core.AOS)
return Construct(AosClothingTypes, ClothingTypes) as BaseClothing;
return Construct(ClothingTypes) as BaseClothing;
}
public static BaseWeapon RandomRangedWeapon()
{
return RandomRangedWeapon(false, false);
}
public static BaseWeapon RandomRangedWeapon(bool inTokuno, bool isMondain)
{
#region Mondain's Legacy
if (Core.ML && isMondain)
return Construct(MLRangedWeaponTypes, AosRangedWeaponTypes, RangedWeaponTypes) as BaseWeapon;
#endregion
if (Core.SE && inTokuno)
return Construct(SERangedWeaponTypes, AosRangedWeaponTypes, RangedWeaponTypes) as BaseWeapon;
if (Core.AOS)
return Construct(AosRangedWeaponTypes, RangedWeaponTypes) as BaseWeapon;
return Construct(RangedWeaponTypes) as BaseWeapon;
}
public static BaseWeapon RandomWeapon()
{
return RandomWeapon(false, false);
}
public static BaseWeapon RandomWeapon(bool inTokuno, bool isMondain)
{
#region Mondain's Legacy
if (Core.ML && isMondain)
return Construct(MLWeaponTypes, AosWeaponTypes, WeaponTypes) as BaseWeapon;
#endregion
if (Core.SE && inTokuno)
return Construct(SEWeaponTypes, AosWeaponTypes, WeaponTypes) as BaseWeapon;
if (Core.AOS)
return Construct(AosWeaponTypes, WeaponTypes) as BaseWeapon;
return Construct(WeaponTypes) as BaseWeapon;
}
public static Item RandomWeaponOrJewelry()
{
return RandomWeaponOrJewelry(false, false);
}
public static Item RandomWeaponOrJewelry(bool inTokuno, bool isMondain)
{
#region Mondain's Legacy
if (Core.ML && isMondain)
return Construct(MLWeaponTypes, AosWeaponTypes, WeaponTypes, JewelryTypes);
#endregion
if (Core.SE && inTokuno)
return Construct(SEWeaponTypes, AosWeaponTypes, WeaponTypes, JewelryTypes);
if (Core.AOS)
return Construct(AosWeaponTypes, WeaponTypes, JewelryTypes);
return Construct(WeaponTypes, JewelryTypes);
}
public static BaseJewel RandomJewelry()
{
return Construct(JewelryTypes) as BaseJewel;
}
public static BaseArmor RandomArmor()
{
return RandomArmor(false, false);
}
public static BaseArmor RandomArmor(bool inTokuno, bool isMondain)
{
#region Mondain's Legacy
if (Core.ML && isMondain)
return Construct(MLArmorTypes, ArmorTypes) as BaseArmor;
#endregion
if (Core.SE && inTokuno)
return Construct(SEArmorTypes, ArmorTypes) as BaseArmor;
return Construct(ArmorTypes) as BaseArmor;
}
public static BaseHat RandomHat()
{
return RandomHat(false);
}
public static BaseHat RandomHat(bool inTokuno)
{
if (Core.SE && inTokuno)
return Construct(SEHatTypes, AosHatTypes, HatTypes) as BaseHat;
if (Core.AOS)
return Construct(AosHatTypes, HatTypes) as BaseHat;
return Construct(HatTypes) as BaseHat;
}
public static Item RandomArmorOrHat()
{
return RandomArmorOrHat(false, false);
}
public static Item RandomArmorOrHat(bool inTokuno, bool isMondain)
{
#region Mondain's Legacy
if (Core.ML && isMondain)
return Construct(MLArmorTypes, ArmorTypes, AosHatTypes, HatTypes);
#endregion
if (Core.SE && inTokuno)
return Construct(SEArmorTypes, ArmorTypes, SEHatTypes, AosHatTypes, HatTypes);
if (Core.AOS)
return Construct(ArmorTypes, AosHatTypes, HatTypes);
return Construct(ArmorTypes, HatTypes);
}
public static BaseShield RandomShield()
{
if (Core.AOS)
return Construct(AosShieldTypes, ShieldTypes) as BaseShield;
return Construct(ShieldTypes) as BaseShield;
}
public static BaseArmor RandomArmorOrShield()
{
return RandomArmorOrShield(false, false);
}
public static BaseArmor RandomArmorOrShield(bool inTokuno, bool isMondain)
{
#region Mondain's Legacy
if (Core.ML && isMondain)
return Construct(MLArmorTypes, ArmorTypes, AosShieldTypes, ShieldTypes) as BaseArmor;
#endregion
if (Core.SE && inTokuno)
return Construct(SEArmorTypes, ArmorTypes, AosShieldTypes, ShieldTypes) as BaseArmor;
if (Core.AOS)
return Construct(ArmorTypes, AosShieldTypes, ShieldTypes) as BaseArmor;
return Construct(ArmorTypes, ShieldTypes) as BaseArmor;
}
public static Item RandomArmorOrShieldOrJewelry()
{
return RandomArmorOrShieldOrJewelry(false, false);
}
public static Item RandomArmorOrShieldOrJewelry(bool inTokuno, bool isMondain)
{
#region Mondain's Legacy
if (Core.ML && isMondain)
return Construct(MLArmorTypes, ArmorTypes, AosHatTypes, HatTypes, AosShieldTypes, ShieldTypes, JewelryTypes);
#endregion
if (Core.SE && inTokuno)
return Construct(SEArmorTypes, ArmorTypes, SEHatTypes, AosHatTypes, HatTypes, AosShieldTypes, ShieldTypes,
JewelryTypes);
if (Core.AOS)
return Construct(ArmorTypes, AosHatTypes, HatTypes, AosShieldTypes, ShieldTypes, JewelryTypes);
return Construct(ArmorTypes, HatTypes, ShieldTypes, JewelryTypes);
}
public static Item RandomArmorOrShieldOrWeapon()
{
return RandomArmorOrShieldOrWeapon(false, false);
}
public static Item RandomArmorOrShieldOrWeapon(bool inTokuno, bool isMondain)
{
#region Mondain's Legacy
if (Core.ML && isMondain)
return Construct(MLWeaponTypes, AosWeaponTypes, WeaponTypes, MLRangedWeaponTypes, AosRangedWeaponTypes,
RangedWeaponTypes, MLArmorTypes, ArmorTypes, AosHatTypes, HatTypes, AosShieldTypes, ShieldTypes);
#endregion
if (Core.SE && inTokuno)
return Construct(SEWeaponTypes, AosWeaponTypes, WeaponTypes, SERangedWeaponTypes, AosRangedWeaponTypes,
RangedWeaponTypes, SEArmorTypes, ArmorTypes, SEHatTypes, AosHatTypes, HatTypes, AosShieldTypes,
ShieldTypes);
if (Core.AOS)
return Construct(AosWeaponTypes, WeaponTypes, AosRangedWeaponTypes, RangedWeaponTypes, ArmorTypes,
AosHatTypes, HatTypes, AosShieldTypes, ShieldTypes);
return Construct(WeaponTypes, RangedWeaponTypes, ArmorTypes, HatTypes, ShieldTypes);
}
public static Item RandomArmorOrShieldOrWeaponOrJewelry()
{
return RandomArmorOrShieldOrWeaponOrJewelry(false, false);
}
public static Item RandomArmorOrShieldOrWeaponOrJewelry(bool inTokuno, bool isMondain)
{
#region Mondain's Legacy
if (Core.ML && isMondain)
return Construct(MLWeaponTypes, AosWeaponTypes, WeaponTypes, MLRangedWeaponTypes, AosRangedWeaponTypes,
RangedWeaponTypes, MLArmorTypes, ArmorTypes, AosHatTypes, HatTypes, AosShieldTypes, ShieldTypes,
JewelryTypes);
#endregion
if (Core.SE && inTokuno)
return Construct(SEWeaponTypes, AosWeaponTypes, WeaponTypes, SERangedWeaponTypes, AosRangedWeaponTypes,
RangedWeaponTypes, SEArmorTypes, ArmorTypes, SEHatTypes, AosHatTypes, HatTypes, AosShieldTypes,
ShieldTypes, JewelryTypes);
if (Core.AOS)
return Construct(AosWeaponTypes, WeaponTypes, AosRangedWeaponTypes, RangedWeaponTypes, ArmorTypes,
AosHatTypes, HatTypes, AosShieldTypes, ShieldTypes, JewelryTypes);
return Construct(WeaponTypes, RangedWeaponTypes, ArmorTypes, HatTypes, ShieldTypes, JewelryTypes);
}
#region Chest of Heirlooms
public static Item ChestOfHeirloomsContains()
{
return Construct(SEArmorTypes, SEHatTypes, SEWeaponTypes, SERangedWeaponTypes, JewelryTypes);
}
#endregion
public static Item RandomGem()
{
return Construct(GemTypes);
}
public static Item RandomReagent()
{
return Construct(RegTypes);
}
public static Item RandomNecromancyReagent()
{
return Construct(NecroRegTypes);
}
public static Item RandomPossibleReagent()
{
if (Core.AOS)
return Construct(RegTypes, NecroRegTypes);
return Construct(RegTypes);
}
public static Item RandomPotion()
{
return Construct(PotionTypes);
}
public static BaseInstrument RandomInstrument()
{
if (Core.SE)
return Construct(InstrumentTypes, SEInstrumentTypes) as BaseInstrument;
return Construct(InstrumentTypes) as BaseInstrument;
}
public static Item RandomStatue()
{
return Construct(StatueTypes);
}
public static SpellScroll RandomScroll(int minIndex, int maxIndex, SpellbookType type)
{
Type[] types;
switch (type)
{
default:
case SpellbookType.Regular:
types = RegularScrollTypes;
break;
case SpellbookType.Necromancer:
types = Core.SE ? SENecromancyScrollTypes : NecromancyScrollTypes;
break;
case SpellbookType.Paladin:
types = PaladinScrollTypes;
break;
case SpellbookType.Arcanist:
types = ArcanistScrollTypes;
break;
}
return Construct(types, Utility.RandomMinMax(minIndex, maxIndex)) as SpellScroll;
}
public static BaseBook RandomGrimmochJournal()
{
return Construct(GrimmochJournalTypes) as BaseBook;
}
public static BaseBook RandomLysanderNotebook()
{
return Construct(LysanderNotebookTypes) as BaseBook;
}
public static BaseBook RandomTavarasJournal()
{
return Construct(TavarasJournalTypes) as BaseBook;
}
public static BaseBook RandomLibraryBook()
{
return Construct(LibraryBookTypes) as BaseBook;
}
public static BaseTalisman RandomTalisman()
{
BaseTalisman talisman = new BaseTalisman(BaseTalisman.GetRandomItemID());
talisman.Summoner = BaseTalisman.GetRandomSummoner();
if (talisman.Summoner.IsEmpty)
{
talisman.Removal = BaseTalisman.GetRandomRemoval();
if (talisman.Removal != TalismanRemoval.None)
{
talisman.MaxCharges = BaseTalisman.GetRandomCharges();
talisman.MaxChargeTime = 1200;
}
}
else
{
talisman.MaxCharges = Utility.RandomMinMax(10, 50);
if (talisman.Summoner.IsItem)
talisman.MaxChargeTime = 60;
else
talisman.MaxChargeTime = 1800;
}
talisman.Blessed = BaseTalisman.GetRandomBlessed();
talisman.Slayer = BaseTalisman.GetRandomSlayer();
talisman.Protection = BaseTalisman.GetRandomProtection();
talisman.Killer = BaseTalisman.GetRandomKiller();
talisman.Skill = BaseTalisman.GetRandomSkill();
talisman.ExceptionalBonus = BaseTalisman.GetRandomExceptional();
talisman.SuccessBonus = BaseTalisman.GetRandomSuccessful();
talisman.Charges = talisman.MaxCharges;
return talisman;
}
#endregion
#region Construction methods
public static Item Construct(Type type)
{
try
{
return Activator.CreateInstance(type) as Item;
}
catch
{
return null;
}
}
public static Item Construct(Type[] types)
{
if (types.Length > 0)
return Construct(types, Utility.Random(types.Length));
return null;
}
public static Item Construct(Type[] types, int index)
{
if (index >= 0 && index < types.Length)
return Construct(types[index]);
return null;
}
public static Item Construct(params Type[][] types)
{
int totalLength = 0;
for (int i = 0; i < types.Length; ++i)
totalLength += types[i].Length;
if (totalLength > 0)
{
int index = Utility.Random(totalLength);
for (int i = 0; i < types.Length; ++i)
{
if (index >= 0 && index < types[i].Length)
return Construct(types[i][index]);
index -= types[i].Length;
}
}
return null;
}
#endregion
}
}

View file

@ -0,0 +1,971 @@
using System;
using System.Collections.Generic;
using Server.Items;
using Server.Mobiles;
namespace Server
{
public class LootPack
{
public static readonly LootPackItem[] Gold =
{
new LootPackItem(typeof(Gold), 1)
};
public static readonly LootPackItem[] Instruments =
{
new LootPackItem(typeof(BaseInstrument), 1)
};
public static readonly LootPackItem[] LowScrollItems =
{
new LootPackItem(typeof(ClumsyScroll), 1)
};
public static readonly LootPackItem[] MedScrollItems =
{
new LootPackItem(typeof(ArchCureScroll), 1)
};
public static readonly LootPackItem[] HighScrollItems =
{
new LootPackItem(typeof(SummonAirElementalScroll), 1)
};
public static readonly LootPackItem[] GemItems =
{
new LootPackItem(typeof(Amber), 1)
};
public static readonly LootPackItem[] PotionItems =
{
new LootPackItem(typeof(AgilityPotion), 1),
new LootPackItem(typeof(StrengthPotion), 1),
new LootPackItem(typeof(RefreshPotion), 1),
new LootPackItem(typeof(LesserCurePotion), 1),
new LootPackItem(typeof(LesserHealPotion), 1),
new LootPackItem(typeof(LesserPoisonPotion), 1)
};
#region Old Magic Items
public static readonly LootPackItem[] OldMagicItems =
{
new LootPackItem(typeof(BaseJewel), 1),
new LootPackItem(typeof(BaseArmor), 4),
new LootPackItem(typeof(BaseWeapon), 3),
new LootPackItem(typeof(BaseRanged), 1),
new LootPackItem(typeof(BaseShield), 1)
};
#endregion
public static readonly LootPack LowScrolls = new LootPack(new[]
{
new LootPackEntry(false, LowScrollItems, 100.00, 1)
});
public static readonly LootPack MedScrolls = new LootPack(new[]
{
new LootPackEntry(false, MedScrollItems, 100.00, 1)
});
public static readonly LootPack HighScrolls = new LootPack(new[]
{
new LootPackEntry(false, HighScrollItems, 100.00, 1)
});
public static readonly LootPack Gems = new LootPack(new[]
{
new LootPackEntry(false, GemItems, 100.00, 1)
});
public static readonly LootPack Potions = new LootPack(new[]
{
new LootPackEntry(false, PotionItems, 100.00, 1)
});
private LootPackEntry[] m_Entries;
public LootPack(LootPackEntry[] entries)
{
m_Entries = entries;
}
public static int GetLuckChance(Mobile killer, Mobile victim)
{
if (!Core.AOS)
return 0;
int luck = killer.Luck;
if (killer is PlayerMobile pmKiller && pmKiller.SentHonorContext != null &&
pmKiller.SentHonorContext.Target == victim)
luck += pmKiller.SentHonorContext.PerfectionLuckBonus;
if (luck < 0)
return 0;
if (!Core.SE && luck > 1200)
luck = 1200;
return (int)(Math.Pow(luck, 1 / 1.8) * 100);
}
public static int GetLuckChanceForKiller(Mobile dead)
{
List<DamageStore> list = BaseCreature.GetLootingRights(dead.DamageEntries, dead.HitsMax);
DamageStore highest = null;
for (int i = 0; i < list.Count; ++i)
{
DamageStore ds = list[i];
if (ds.m_HasRight && (highest == null || ds.m_Damage > highest.m_Damage))
highest = ds;
}
if (highest == null)
return 0;
return GetLuckChance(highest.m_Mobile, dead);
}
public static bool CheckLuck(int chance)
{
return chance > Utility.Random(10000);
}
public void Generate(Mobile from, Container cont, bool spawning, int luckChance)
{
if (cont == null)
return;
bool checkLuck = Core.AOS;
for (int i = 0; i < m_Entries.Length; ++i)
{
LootPackEntry entry = m_Entries[i];
bool shouldAdd = entry.Chance > Utility.Random(10000);
if (!shouldAdd && checkLuck)
{
checkLuck = false;
if (CheckLuck(luckChance))
shouldAdd = entry.Chance > Utility.Random(10000);
}
if (!shouldAdd)
continue;
Item item = entry.Construct(from, luckChance, spawning);
if (item != null)
if (!item.Stackable || !cont.TryDropItem(from, item, false))
cont.DropItem(item);
}
}
#region ML definitions
public static readonly LootPackItem[] AosMagicItemsRichType1 =
{
new LootPackItem(typeof(BaseWeapon), 211),
new LootPackItem(typeof(BaseRanged), 53),
new LootPackItem(typeof(BaseArmor), 303),
new LootPackItem(typeof(BaseShield), 39),
new LootPackItem(typeof(BaseJewel), 158)
};
public static readonly LootPack MlRich = new LootPack(new[]
{
new LootPackEntry(true, Gold, 100.00, "4d50+450"),
new LootPackEntry(false, AosMagicItemsRichType1, 100.00, 1, 3, 0, 75),
new LootPackEntry(false, AosMagicItemsRichType1, 80.00, 1, 3, 0, 75),
new LootPackEntry(false, AosMagicItemsRichType1, 60.00, 1, 5, 0, 100),
new LootPackEntry(false, Instruments, 1.00, 1)
});
#endregion
#region AOS Magic Items
public static readonly LootPackItem[] AosMagicItemsPoor =
{
new LootPackItem(typeof(BaseWeapon), 3),
new LootPackItem(typeof(BaseRanged), 1),
new LootPackItem(typeof(BaseArmor), 4),
new LootPackItem(typeof(BaseShield), 1),
new LootPackItem(typeof(BaseJewel), 2)
};
public static readonly LootPackItem[] AosMagicItemsMeagerType1 =
{
new LootPackItem(typeof(BaseWeapon), 56),
new LootPackItem(typeof(BaseRanged), 14),
new LootPackItem(typeof(BaseArmor), 81),
new LootPackItem(typeof(BaseShield), 11),
new LootPackItem(typeof(BaseJewel), 42)
};
public static readonly LootPackItem[] AosMagicItemsMeagerType2 =
{
new LootPackItem(typeof(BaseWeapon), 28),
new LootPackItem(typeof(BaseRanged), 7),
new LootPackItem(typeof(BaseArmor), 40),
new LootPackItem(typeof(BaseShield), 5),
new LootPackItem(typeof(BaseJewel), 21)
};
public static readonly LootPackItem[] AosMagicItemsAverageType1 =
{
new LootPackItem(typeof(BaseWeapon), 90),
new LootPackItem(typeof(BaseRanged), 23),
new LootPackItem(typeof(BaseArmor), 130),
new LootPackItem(typeof(BaseShield), 17),
new LootPackItem(typeof(BaseJewel), 68)
};
public static readonly LootPackItem[] AosMagicItemsAverageType2 =
{
new LootPackItem(typeof(BaseWeapon), 54),
new LootPackItem(typeof(BaseRanged), 13),
new LootPackItem(typeof(BaseArmor), 77),
new LootPackItem(typeof(BaseShield), 10),
new LootPackItem(typeof(BaseJewel), 40)
};
public static readonly LootPackItem[] AosMagicItemsRichType2 =
{
new LootPackItem(typeof(BaseWeapon), 170),
new LootPackItem(typeof(BaseRanged), 43),
new LootPackItem(typeof(BaseArmor), 245),
new LootPackItem(typeof(BaseShield), 32),
new LootPackItem(typeof(BaseJewel), 128)
};
public static readonly LootPackItem[] AosMagicItemsFilthyRichType1 =
{
new LootPackItem(typeof(BaseWeapon), 219),
new LootPackItem(typeof(BaseRanged), 55),
new LootPackItem(typeof(BaseArmor), 315),
new LootPackItem(typeof(BaseShield), 41),
new LootPackItem(typeof(BaseJewel), 164)
};
public static readonly LootPackItem[] AosMagicItemsFilthyRichType2 =
{
new LootPackItem(typeof(BaseWeapon), 239),
new LootPackItem(typeof(BaseRanged), 60),
new LootPackItem(typeof(BaseArmor), 343),
new LootPackItem(typeof(BaseShield), 90),
new LootPackItem(typeof(BaseJewel), 45)
};
public static readonly LootPackItem[] AosMagicItemsUltraRich =
{
new LootPackItem(typeof(BaseWeapon), 276),
new LootPackItem(typeof(BaseRanged), 69),
new LootPackItem(typeof(BaseArmor), 397),
new LootPackItem(typeof(BaseShield), 52),
new LootPackItem(typeof(BaseJewel), 207)
};
#endregion
#region SE definitions
public static readonly LootPack SePoor = new LootPack(new[]
{
new LootPackEntry(true, Gold, 100.00, "2d10+20"),
new LootPackEntry(false, AosMagicItemsPoor, 1.00, 1, 5, 0, 100),
new LootPackEntry(false, Instruments, 0.02, 1)
});
public static readonly LootPack SeMeager = new LootPack(new[]
{
new LootPackEntry(true, Gold, 100.00, "4d10+40"),
new LootPackEntry(false, AosMagicItemsMeagerType1, 20.40, 1, 2, 0, 50),
new LootPackEntry(false, AosMagicItemsMeagerType2, 10.20, 1, 5, 0, 100),
new LootPackEntry(false, Instruments, 0.10, 1)
});
public static readonly LootPack SeAverage = new LootPack(new[]
{
new LootPackEntry(true, Gold, 100.00, "8d10+100"),
new LootPackEntry(false, AosMagicItemsAverageType1, 32.80, 1, 3, 0, 50),
new LootPackEntry(false, AosMagicItemsAverageType1, 32.80, 1, 4, 0, 75),
new LootPackEntry(false, AosMagicItemsAverageType2, 19.50, 1, 5, 0, 100),
new LootPackEntry(false, Instruments, 0.40, 1)
});
public static readonly LootPack SeRich = new LootPack(new[]
{
new LootPackEntry(true, Gold, 100.00, "15d10+225"),
new LootPackEntry(false, AosMagicItemsRichType1, 76.30, 1, 4, 0, 75),
new LootPackEntry(false, AosMagicItemsRichType1, 76.30, 1, 4, 0, 75),
new LootPackEntry(false, AosMagicItemsRichType2, 61.70, 1, 5, 0, 100),
new LootPackEntry(false, Instruments, 1.00, 1)
});
public static readonly LootPack SeFilthyRich = new LootPack(new[]
{
new LootPackEntry(true, Gold, 100.00, "3d100+400"),
new LootPackEntry(false, AosMagicItemsFilthyRichType1, 79.50, 1, 5, 0, 100),
new LootPackEntry(false, AosMagicItemsFilthyRichType1, 79.50, 1, 5, 0, 100),
new LootPackEntry(false, AosMagicItemsFilthyRichType2, 77.60, 1, 5, 25, 100),
new LootPackEntry(false, Instruments, 2.00, 1)
});
public static readonly LootPack SeUltraRich = new LootPack(new[]
{
new LootPackEntry(true, Gold, 100.00, "6d100+600"),
new LootPackEntry(false, AosMagicItemsUltraRich, 100.00, 1, 5, 25, 100),
new LootPackEntry(false, AosMagicItemsUltraRich, 100.00, 1, 5, 25, 100),
new LootPackEntry(false, AosMagicItemsUltraRich, 100.00, 1, 5, 25, 100),
new LootPackEntry(false, AosMagicItemsUltraRich, 100.00, 1, 5, 25, 100),
new LootPackEntry(false, AosMagicItemsUltraRich, 100.00, 1, 5, 25, 100),
new LootPackEntry(false, AosMagicItemsUltraRich, 100.00, 1, 5, 33, 100),
new LootPackEntry(false, Instruments, 2.00, 1)
});
public static readonly LootPack SeSuperBoss = new LootPack(new[]
{
new LootPackEntry(true, Gold, 100.00, "10d100+800"),
new LootPackEntry(false, AosMagicItemsUltraRich, 100.00, 1, 5, 25, 100),
new LootPackEntry(false, AosMagicItemsUltraRich, 100.00, 1, 5, 25, 100),
new LootPackEntry(false, AosMagicItemsUltraRich, 100.00, 1, 5, 25, 100),
new LootPackEntry(false, AosMagicItemsUltraRich, 100.00, 1, 5, 25, 100),
new LootPackEntry(false, AosMagicItemsUltraRich, 100.00, 1, 5, 33, 100),
new LootPackEntry(false, AosMagicItemsUltraRich, 100.00, 1, 5, 33, 100),
new LootPackEntry(false, AosMagicItemsUltraRich, 100.00, 1, 5, 33, 100),
new LootPackEntry(false, AosMagicItemsUltraRich, 100.00, 1, 5, 33, 100),
new LootPackEntry(false, AosMagicItemsUltraRich, 100.00, 1, 5, 50, 100),
new LootPackEntry(false, AosMagicItemsUltraRich, 100.00, 1, 5, 50, 100),
new LootPackEntry(false, Instruments, 2.00, 1)
});
#endregion
#region AOS definitions
public static readonly LootPack AosPoor = new LootPack(new[]
{
new LootPackEntry(true, Gold, 100.00, "1d10+10"),
new LootPackEntry(false, AosMagicItemsPoor, 0.02, 1, 5, 0, 90),
new LootPackEntry(false, Instruments, 0.02, 1)
});
public static readonly LootPack AosMeager = new LootPack(new[]
{
new LootPackEntry(true, Gold, 100.00, "3d10+20"),
new LootPackEntry(false, AosMagicItemsMeagerType1, 1.00, 1, 2, 0, 10),
new LootPackEntry(false, AosMagicItemsMeagerType2, 0.20, 1, 5, 0, 90),
new LootPackEntry(false, Instruments, 0.10, 1)
});
public static readonly LootPack AosAverage = new LootPack(new[]
{
new LootPackEntry(true, Gold, 100.00, "5d10+50"),
new LootPackEntry(false, AosMagicItemsAverageType1, 5.00, 1, 4, 0, 20),
new LootPackEntry(false, AosMagicItemsAverageType1, 2.00, 1, 3, 0, 50),
new LootPackEntry(false, AosMagicItemsAverageType2, 0.50, 1, 5, 0, 90),
new LootPackEntry(false, Instruments, 0.40, 1)
});
public static readonly LootPack AosRich = new LootPack(new[]
{
new LootPackEntry(true, Gold, 100.00, "10d10+150"),
new LootPackEntry(false, AosMagicItemsRichType1, 20.00, 1, 4, 0, 40),
new LootPackEntry(false, AosMagicItemsRichType1, 10.00, 1, 5, 0, 60),
new LootPackEntry(false, AosMagicItemsRichType2, 1.00, 1, 5, 0, 90),
new LootPackEntry(false, Instruments, 1.00, 1)
});
public static readonly LootPack AosFilthyRich = new LootPack(new[]
{
new LootPackEntry(true, Gold, 100.00, "2d100+200"),
new LootPackEntry(false, AosMagicItemsFilthyRichType1, 33.00, 1, 4, 0, 50),
new LootPackEntry(false, AosMagicItemsFilthyRichType1, 33.00, 1, 4, 0, 60),
new LootPackEntry(false, AosMagicItemsFilthyRichType2, 20.00, 1, 5, 0, 75),
new LootPackEntry(false, AosMagicItemsFilthyRichType2, 5.00, 1, 5, 0, 100),
new LootPackEntry(false, Instruments, 2.00, 1)
});
public static readonly LootPack AosUltraRich = new LootPack(new[]
{
new LootPackEntry(true, Gold, 100.00, "5d100+500"),
new LootPackEntry(false, AosMagicItemsUltraRich, 100.00, 1, 5, 25, 100),
new LootPackEntry(false, AosMagicItemsUltraRich, 100.00, 1, 5, 25, 100),
new LootPackEntry(false, AosMagicItemsUltraRich, 100.00, 1, 5, 25, 100),
new LootPackEntry(false, AosMagicItemsUltraRich, 100.00, 1, 5, 25, 100),
new LootPackEntry(false, AosMagicItemsUltraRich, 100.00, 1, 5, 25, 100),
new LootPackEntry(false, AosMagicItemsUltraRich, 100.00, 1, 5, 35, 100),
new LootPackEntry(false, Instruments, 2.00, 1)
});
public static readonly LootPack AosSuperBoss = new LootPack(new[]
{
new LootPackEntry(true, Gold, 100.00, "5d100+500"),
new LootPackEntry(false, AosMagicItemsUltraRich, 100.00, 1, 5, 25, 100),
new LootPackEntry(false, AosMagicItemsUltraRich, 100.00, 1, 5, 25, 100),
new LootPackEntry(false, AosMagicItemsUltraRich, 100.00, 1, 5, 25, 100),
new LootPackEntry(false, AosMagicItemsUltraRich, 100.00, 1, 5, 25, 100),
new LootPackEntry(false, AosMagicItemsUltraRich, 100.00, 1, 5, 33, 100),
new LootPackEntry(false, AosMagicItemsUltraRich, 100.00, 1, 5, 33, 100),
new LootPackEntry(false, AosMagicItemsUltraRich, 100.00, 1, 5, 33, 100),
new LootPackEntry(false, AosMagicItemsUltraRich, 100.00, 1, 5, 33, 100),
new LootPackEntry(false, AosMagicItemsUltraRich, 100.00, 1, 5, 50, 100),
new LootPackEntry(false, AosMagicItemsUltraRich, 100.00, 1, 5, 50, 100),
new LootPackEntry(false, Instruments, 2.00, 1)
});
#endregion
#region Pre-AOS definitions
public static readonly LootPack OldPoor = new LootPack(new[]
{
new LootPackEntry(true, Gold, 100.00, "1d25"),
new LootPackEntry(false, Instruments, 0.02, 1)
});
public static readonly LootPack OldMeager = new LootPack(new[]
{
new LootPackEntry(true, Gold, 100.00, "5d10+25"),
new LootPackEntry(false, Instruments, 0.10, 1),
new LootPackEntry(false, OldMagicItems, 1.00, 1, 1, 0, 60),
new LootPackEntry(false, OldMagicItems, 0.20, 1, 1, 10, 70)
});
public static readonly LootPack OldAverage = new LootPack(new[]
{
new LootPackEntry(true, Gold, 100.00, "10d10+50"),
new LootPackEntry(false, Instruments, 0.40, 1),
new LootPackEntry(false, OldMagicItems, 5.00, 1, 1, 20, 80),
new LootPackEntry(false, OldMagicItems, 2.00, 1, 1, 30, 90),
new LootPackEntry(false, OldMagicItems, 0.50, 1, 1, 40, 100)
});
public static readonly LootPack OldRich = new LootPack(new[]
{
new LootPackEntry(true, Gold, 100.00, "10d10+250"),
new LootPackEntry(false, Instruments, 1.00, 1),
new LootPackEntry(false, OldMagicItems, 20.00, 1, 1, 60, 100),
new LootPackEntry(false, OldMagicItems, 10.00, 1, 1, 65, 100),
new LootPackEntry(false, OldMagicItems, 1.00, 1, 1, 70, 100)
});
public static readonly LootPack OldFilthyRich = new LootPack(new[]
{
new LootPackEntry(true, Gold, 100.00, "2d125+400"),
new LootPackEntry(false, Instruments, 2.00, 1),
new LootPackEntry(false, OldMagicItems, 33.00, 1, 1, 50, 100),
new LootPackEntry(false, OldMagicItems, 33.00, 1, 1, 60, 100),
new LootPackEntry(false, OldMagicItems, 20.00, 1, 1, 70, 100),
new LootPackEntry(false, OldMagicItems, 5.00, 1, 1, 80, 100)
});
public static readonly LootPack OldUltraRich = new LootPack(new[]
{
new LootPackEntry(true, Gold, 100.00, "5d100+500"),
new LootPackEntry(false, Instruments, 2.00, 1),
new LootPackEntry(false, OldMagicItems, 100.00, 1, 1, 40, 100),
new LootPackEntry(false, OldMagicItems, 100.00, 1, 1, 40, 100),
new LootPackEntry(false, OldMagicItems, 100.00, 1, 1, 50, 100),
new LootPackEntry(false, OldMagicItems, 100.00, 1, 1, 50, 100),
new LootPackEntry(false, OldMagicItems, 100.00, 1, 1, 60, 100),
new LootPackEntry(false, OldMagicItems, 100.00, 1, 1, 60, 100)
});
public static readonly LootPack OldSuperBoss = new LootPack(new[]
{
new LootPackEntry(true, Gold, 100.00, "5d100+500"),
new LootPackEntry(false, Instruments, 2.00, 1),
new LootPackEntry(false, OldMagicItems, 100.00, 1, 1, 40, 100),
new LootPackEntry(false, OldMagicItems, 100.00, 1, 1, 40, 100),
new LootPackEntry(false, OldMagicItems, 100.00, 1, 1, 40, 100),
new LootPackEntry(false, OldMagicItems, 100.00, 1, 1, 50, 100),
new LootPackEntry(false, OldMagicItems, 100.00, 1, 1, 50, 100),
new LootPackEntry(false, OldMagicItems, 100.00, 1, 1, 50, 100),
new LootPackEntry(false, OldMagicItems, 100.00, 1, 1, 60, 100),
new LootPackEntry(false, OldMagicItems, 100.00, 1, 1, 60, 100),
new LootPackEntry(false, OldMagicItems, 100.00, 1, 1, 60, 100),
new LootPackEntry(false, OldMagicItems, 100.00, 1, 1, 70, 100)
});
#endregion
#region Generic accessors
public static LootPack Poor => Core.SE ? SePoor : Core.AOS ? AosPoor : OldPoor;
public static LootPack Meager => Core.SE ? SeMeager : Core.AOS ? AosMeager : OldMeager;
public static LootPack Average => Core.SE ? SeAverage : Core.AOS ? AosAverage : OldAverage;
public static LootPack Rich => Core.SE ? SeRich : Core.AOS ? AosRich : OldRich;
public static LootPack FilthyRich => Core.SE ? SeFilthyRich : Core.AOS ? AosFilthyRich : OldFilthyRich;
public static LootPack UltraRich => Core.SE ? SeUltraRich : Core.AOS ? AosUltraRich : OldUltraRich;
public static LootPack SuperBoss => Core.SE ? SeSuperBoss : Core.AOS ? AosSuperBoss : OldSuperBoss;
#endregion
/*
// TODO: Uncomment once added
#region Mondain's Legacy
public static readonly LootPackItem[] ParrotItem = new LootPackItem[]
{
new LootPackItem( typeof( ParrotItem ), 1 )
};
public static readonly LootPack Parrot = new LootPack( new LootPackEntry[]
{
new LootPackEntry( false, ParrotItem, 10.00, 1 )
} );
#endregion
*/
}
public class LootPackEntry
{
private bool m_AtSpawnTime;
public LootPackEntry(bool atSpawnTime, LootPackItem[] items, double chance, string quantity) : this(atSpawnTime,
items, chance, new LootPackDice(quantity))
{
}
public LootPackEntry(bool atSpawnTime, LootPackItem[] items, double chance, int quantity) : this(atSpawnTime, items,
chance, new LootPackDice(0, 0, quantity))
{
}
public LootPackEntry(bool atSpawnTime, LootPackItem[] items, double chance, string quantity, int maxProps,
int minIntensity, int maxIntensity) : this(atSpawnTime, items, chance, new LootPackDice(quantity), maxProps,
minIntensity, maxIntensity)
{
}
public LootPackEntry(bool atSpawnTime, LootPackItem[] items, double chance, int quantity, int maxProps,
int minIntensity, int maxIntensity) : this(atSpawnTime, items, chance, new LootPackDice(0, 0, quantity),
maxProps, minIntensity, maxIntensity)
{
}
public LootPackEntry(bool atSpawnTime, LootPackItem[] items, double chance, LootPackDice quantity, int maxProps = 0,
int minIntensity = 0, int maxIntensity = 0)
{
m_AtSpawnTime = atSpawnTime;
Items = items;
Chance = (int)(100 * chance);
Quantity = quantity;
MaxProps = maxProps;
MinIntensity = minIntensity;
MaxIntensity = maxIntensity;
}
public int Chance{ get; set; }
public LootPackDice Quantity{ get; set; }
public int MaxProps{ get; set; }
public int MinIntensity{ get; set; }
public int MaxIntensity{ get; set; }
public LootPackItem[] Items{ get; set; }
private static bool IsInTokuno(Mobile m)
{
if (m.Region.IsPartOf("Fan Dancer's Dojo"))
return true;
if (m.Region.IsPartOf("Yomotsu Mines"))
return true;
return m.Map == Map.Tokuno;
}
#region Mondain's Legacy
private static bool IsMondain(Mobile m)
{
return MondainsLegacy.IsMLRegion(m.Region);
}
#endregion
public Item Construct(Mobile from, int luckChance, bool spawning)
{
if (m_AtSpawnTime != spawning)
return null;
int totalChance = 0;
for (int i = 0; i < Items.Length; ++i)
totalChance += Items[i].Chance;
int rnd = Utility.Random(totalChance);
for (int i = 0; i < Items.Length; ++i)
{
LootPackItem item = Items[i];
if (rnd < item.Chance)
return Mutate(from, luckChance, item.Construct(IsInTokuno(from), IsMondain(from)));
rnd -= item.Chance;
}
return null;
}
private int GetRandomOldBonus()
{
int rnd = Utility.RandomMinMax(MinIntensity, MaxIntensity);
if (50 > rnd)
return 1;
rnd -= 50;
if (25 > rnd)
return 2;
rnd -= 25;
if (14 > rnd)
return 3;
rnd -= 14;
if (8 > rnd)
return 4;
return 5;
}
public Item Mutate(Mobile from, int luckChance, Item item)
{
if (item != null)
{
if (item is BaseWeapon && 1 > Utility.Random(100))
{
item.Delete();
item = new FireHorn();
return item;
}
if (item is BaseWeapon || item is BaseArmor || item is BaseJewel || item is BaseHat)
{
if (Core.AOS)
{
int bonusProps = GetBonusProperties();
int min = MinIntensity;
int max = MaxIntensity;
if (bonusProps < MaxProps && LootPack.CheckLuck(luckChance))
++bonusProps;
int props = 1 + bonusProps;
// Make sure we're not spawning items with 6 properties.
if (props > MaxProps)
props = MaxProps;
if (item is BaseWeapon weapon)
BaseRunicTool.ApplyAttributesTo(weapon, false, luckChance, props, MinIntensity, MaxIntensity);
else if (item is BaseArmor armor)
BaseRunicTool.ApplyAttributesTo(armor, false, luckChance, props, MinIntensity, MaxIntensity);
else if (item is BaseJewel jewel)
BaseRunicTool.ApplyAttributesTo(jewel, false, luckChance, props, MinIntensity, MaxIntensity);
else
BaseRunicTool.ApplyAttributesTo((BaseHat)item, false, luckChance, props, MinIntensity,
MaxIntensity);
}
else // not aos
{
if (item is BaseWeapon weapon)
{
if (80 > Utility.Random(100))
weapon.AccuracyLevel = (WeaponAccuracyLevel)GetRandomOldBonus();
if (60 > Utility.Random(100))
weapon.DamageLevel = (WeaponDamageLevel)GetRandomOldBonus();
if (40 > Utility.Random(100))
weapon.DurabilityLevel = (WeaponDurabilityLevel)GetRandomOldBonus();
if (5 > Utility.Random(100))
weapon.Slayer = SlayerName.Silver;
if (from != null && weapon.AccuracyLevel == 0 && weapon.DamageLevel == 0 &&
weapon.DurabilityLevel == 0 && weapon.Slayer == SlayerName.None && 5 > Utility.Random(100))
weapon.Slayer = SlayerGroup.GetLootSlayerType(from.GetType());
}
else if (item is BaseArmor armor)
{
if (80 > Utility.Random(100))
armor.ProtectionLevel = (ArmorProtectionLevel)GetRandomOldBonus();
if (40 > Utility.Random(100))
armor.Durability = (ArmorDurabilityLevel)GetRandomOldBonus();
}
}
}
else if (item is BaseInstrument instr)
{
SlayerName slayer = SlayerName.None;
if (Core.AOS)
slayer = BaseRunicTool.GetRandomSlayer();
else
slayer = SlayerGroup.GetLootSlayerType(from.GetType());
if (slayer == SlayerName.None)
{
instr.Delete();
return null;
}
instr.Quality = InstrumentQuality.Regular;
instr.Slayer = slayer;
}
if (item.Stackable)
item.Amount = Quantity.Roll();
}
return item;
}
public int GetBonusProperties()
{
int p0 = 0, p1 = 0, p2 = 0, p3 = 0, p4 = 0, p5 = 0;
switch (MaxProps)
{
case 1:
p0 = 3;
p1 = 1;
break;
case 2:
p0 = 6;
p1 = 3;
p2 = 1;
break;
case 3:
p0 = 10;
p1 = 6;
p2 = 3;
p3 = 1;
break;
case 4:
p0 = 16;
p1 = 12;
p2 = 6;
p3 = 5;
p4 = 1;
break;
case 5:
p0 = 30;
p1 = 25;
p2 = 20;
p3 = 15;
p4 = 9;
p5 = 1;
break;
}
int pc = p0 + p1 + p2 + p3 + p4 + p5;
int rnd = Utility.Random(pc);
if (rnd < p5)
return 5;
rnd -= p5;
if (rnd < p4)
return 4;
rnd -= p4;
if (rnd < p3)
return 3;
rnd -= p3;
if (rnd < p2)
return 2;
return rnd - p2 < p1 ? 1 : 0;
}
}
public class LootPackItem
{
private static Type[] m_BlankTypes = { typeof(BlankScroll) };
private static Type[][] m_NecroTypes =
{
new[] // low
{
typeof(AnimateDeadScroll), typeof(BloodOathScroll), typeof(CorpseSkinScroll), typeof(CurseWeaponScroll),
typeof(EvilOmenScroll), typeof(HorrificBeastScroll), typeof(MindRotScroll), typeof(PainSpikeScroll),
typeof(SummonFamiliarScroll), typeof(WraithFormScroll)
},
new[] // med
{
typeof(LichFormScroll), typeof(PoisonStrikeScroll), typeof(StrangleScroll), typeof(WitherScroll)
},
Core.SE
? new[] // high
{
typeof(VengefulSpiritScroll), typeof(VampiricEmbraceScroll), typeof(ExorcismScroll)
}
: new[] // high
{
typeof(VengefulSpiritScroll), typeof(VampiricEmbraceScroll)
}
};
public LootPackItem(Type type, int chance)
{
Type = type;
Chance = chance;
}
public Type Type{ get; set; }
public int Chance{ get; set; }
public static Item RandomScroll(int index, int minCircle, int maxCircle)
{
--minCircle;
--maxCircle;
int scrollCount = (maxCircle - minCircle + 1) * 8;
if (index == 0)
scrollCount += m_BlankTypes.Length;
if (Core.AOS)
scrollCount += m_NecroTypes[index].Length;
int rnd = Utility.Random(scrollCount);
if (index == 0 && rnd < m_BlankTypes.Length)
return Loot.Construct(m_BlankTypes);
if (index == 0)
rnd -= m_BlankTypes.Length;
if (Core.AOS && rnd < m_NecroTypes.Length)
return Loot.Construct(m_NecroTypes[index]);
return Loot.RandomScroll(minCircle * 8, maxCircle * 8 + 7, SpellbookType.Regular);
}
public Item Construct(bool inTokuno, bool isMondain)
{
try
{
Item item;
if (Type == typeof(BaseRanged))
item = Loot.RandomRangedWeapon(inTokuno, isMondain);
else if (Type == typeof(BaseWeapon))
item = Loot.RandomWeapon(inTokuno, isMondain);
else if (Type == typeof(BaseArmor))
item = Loot.RandomArmorOrHat(inTokuno, isMondain);
else if (Type == typeof(BaseShield))
item = Loot.RandomShield();
else if (Type == typeof(BaseJewel))
item = Core.AOS ? Loot.RandomJewelry() : Loot.RandomArmorOrShieldOrWeapon();
else if (Type == typeof(BaseInstrument))
item = Loot.RandomInstrument();
else if (Type == typeof(Amber)) // gem
item = Loot.RandomGem();
else if (Type == typeof(ClumsyScroll)) // low scroll
item = RandomScroll(0, 1, 3);
else if (Type == typeof(ArchCureScroll)) // med scroll
item = RandomScroll(1, 4, 7);
else if (Type == typeof(SummonAirElementalScroll)) // high scroll
item = RandomScroll(2, 8, 8);
else
item = Activator.CreateInstance(Type) as Item;
return item;
}
catch
{
// ignored
}
return null;
}
}
public class LootPackDice
{
public LootPackDice(string str)
{
int start = 0;
int index = str.IndexOf('d', start);
if (index < start)
return;
Count = Utility.ToInt32(str.Substring(start, index - start));
start = index + 1;
index = str.IndexOf('+', start);
bool negative = index < start;
if (negative)
index = str.IndexOf('-', start);
if (index < start)
index = str.Length;
Sides = Utility.ToInt32(str.Substring(start, index - start));
if (index == str.Length)
return;
start = index + 1;
index = str.Length;
Bonus = Utility.ToInt32(str.Substring(start, index - start));
if (negative)
Bonus *= -1;
}
public LootPackDice(int count, int sides, int bonus)
{
Count = count;
Sides = sides;
Bonus = bonus;
}
public int Count{ get; set; }
public int Sides{ get; set; }
public int Bonus{ get; set; }
public int Roll()
{
int v = Bonus;
for (int i = 0; i < Count; ++i)
v += Utility.Random(1, Sides);
return v;
}
}
}

View file

@ -0,0 +1,52 @@
namespace Server.Misc
{
public class MapDefinitions
{
public static void Configure()
{
/* Here we configure all maps. Some notes:
*
* 1) The first 32 maps are reserved for core use.
* 2) Map 0x7F is reserved for core use.
* 3) Map 0xFF is reserved for core use.
* 4) Changing or removing any predefined maps may cause server instability.
*/
RegisterMap(0, 0, 0, 7168, 4096, 4, "Felucca", MapRules.FeluccaRules);
RegisterMap(1, 1, 1, 7168, 4096, 0, "Trammel", MapRules.TrammelRules);
RegisterMap(2, 2, 2, 2304, 1600, 1, "Ilshenar", MapRules.TrammelRules);
RegisterMap(3, 3, 3, 2560, 2048, 1, "Malas", MapRules.TrammelRules);
RegisterMap(4, 4, 4, 1448, 1448, 1, "Tokuno", MapRules.TrammelRules);
RegisterMap(5, 5, 5, 1280, 4096, 1, "TerMur", MapRules.TrammelRules);
RegisterMap(0x7F, 0x7F, 0x7F, Map.SectorSize, Map.SectorSize, 1, "Internal", MapRules.Internal);
/* Example of registering a custom map:
* RegisterMap( 32, 0, 0, 6144, 4096, 3, "Iceland", MapRules.FeluccaRules );
*
* Defined:
* RegisterMap( <index>, <mapID>, <fileIndex>, <width>, <height>, <season>, <name>, <rules> );
* - <index> : An unreserved unique index for this map
* - <mapID> : An identification number used in client communications. For any visible maps, this value must be from 0-5
* - <fileIndex> : A file identification number. For any visible maps, this value must be from 0-5
* - <width>, <height> : Size of the map (in tiles)
* - <season> : Season of the map. 0 = Spring, 1 = Summer, 2 = Fall, 3 = Winter, 4 = Desolation
* - <name> : Reference name for the map, used in props gump, get/set commands, region loading, etc
* - <rules> : Rules and restrictions associated with the map. See documentation for details
*/
TileMatrixPatch.Enabled = false; // OSI Client Patch 6.0.0.0
MultiComponentList.PostHSFormat = true; // OSI Client Patch 7.0.9.0
}
public static void RegisterMap(int mapIndex, int mapID, int fileIndex, int width, int height, int season,
string name, MapRules rules)
{
Map newMap = new Map(mapID, mapIndex, fileIndex, width, height, season, name, rules);
Map.Maps[mapIndex] = newMap;
Map.AllMaps.Add(newMap);
}
}
}

View file

@ -0,0 +1,130 @@
using System;
using Server.Engines.PartySystem;
using Server.Guilds;
using Server.Network;
namespace Server.Misc
{
public static class MapUO
{
public static void Initialize()
{
if (Settings.PartyTrack)
ProtocolExtensions.Register(0x00, true, OnPartyTrack);
if (Settings.GuildTrack)
ProtocolExtensions.Register(0x01, true, OnGuildTrack);
}
private static void OnPartyTrack(NetState state, PacketReader pvSrc)
{
Mobile from = state.Mobile;
Party party = Party.Get(from);
if (party != null)
{
Packets.PartyTrack packet = new Packets.PartyTrack(from, party);
if (packet.UnderlyingStream.Length > 8)
state.Send(packet);
}
}
private static void OnGuildTrack(NetState state, PacketReader pvSrc)
{
Mobile from = state.Mobile;
if (from.Guild is Guild guild)
{
bool locations = pvSrc.ReadByte() != 0;
Packets.GuildTrack packet = new Packets.GuildTrack(from, guild, locations);
if (packet.UnderlyingStream.Length > (locations ? 9 : 5))
state.Send(packet);
}
else
{
state.Send(new Packets.GuildTrack());
}
}
private static class Settings
{
public const bool PartyTrack = true;
public const bool GuildTrack = true;
public const bool GuildHitsPercent = true;
}
private static class Packets
{
public sealed class PartyTrack : ProtocolExtension
{
public PartyTrack(Mobile from, Party party) : base(0x01, (party.Members.Count - 1) * 9 + 4)
{
for (int i = 0; i < party.Members.Count; ++i)
{
PartyMemberInfo pmi = party.Members[i];
if (pmi == null || pmi.Mobile == from)
continue;
Mobile mob = pmi.Mobile;
if (Utility.InUpdateRange(from, mob) && from.CanSee(mob))
continue;
m_Stream.Write(mob.Serial);
m_Stream.Write((short)mob.X);
m_Stream.Write((short)mob.Y);
m_Stream.Write((byte)(mob.Map?.MapID ?? 0));
}
m_Stream.Write(0);
}
}
public sealed class GuildTrack : ProtocolExtension
{
public GuildTrack() : base(0x02, 5)
{
m_Stream.Write((byte)0);
m_Stream.Write(0);
}
public GuildTrack(Mobile from, Guild guild, bool locations) : base(0x02,
(guild.Members.Count - 1) * (locations ? 10 : 4) + 5)
{
m_Stream.Write((byte)(locations ? 1 : 0));
for (int i = 0; i < guild.Members.Count; ++i)
{
Mobile mob = guild.Members[i];
if (mob == null || mob == from || mob.NetState == null)
continue;
if (locations && Utility.InUpdateRange(from, mob) && from.CanSee(mob))
continue;
m_Stream.Write(mob.Serial);
if (locations)
{
m_Stream.Write((short)mob.X);
m_Stream.Write((short)mob.Y);
m_Stream.Write((byte)(mob.Map?.MapID ?? 0));
if (Settings.GuildHitsPercent && mob.Alive)
m_Stream.Write((byte)(mob.Hits / Math.Max(mob.HitsMax, 1.0) * 100));
else
m_Stream.Write((byte)0);
}
}
m_Stream.Write(0);
}
}
}
}
}

View file

@ -0,0 +1,78 @@
using System;
using Server.Items;
using Server.Mobiles;
namespace Server
{
public static class MondainsLegacy
{
public static Type[] Artifacts{ get; } =
{
typeof(AegisOfGrace), typeof(BladeDance), typeof(BloodwoodSpirit), typeof(Bonesmasher),
typeof(Boomstick), typeof(BrightsightLenses), typeof(FeyLeggings), typeof(FleshRipper),
typeof(HelmOfSwiftness), typeof(PadsOfTheCuSidhe), typeof(QuiverOfRage), typeof(QuiverOfElements),
typeof(RaedsGlory), typeof(RighteousAnger), typeof(RobeOfTheEclipse), typeof(RobeOfTheEquinox),
typeof(SoulSeeker), typeof(TalonBite), typeof(TotemOfVoid), typeof(WildfireBow),
typeof(Windsong)
};
public static bool CheckArtifactChance(Mobile m, BaseCreature bc)
{
if (!Core.ML)
return false;
return Paragon.CheckArtifactChance(m, bc);
}
public static void GiveArtifactTo(Mobile m)
{
if (!(Activator.CreateInstance(Artifacts[Utility.Random(Artifacts.Length)]) is Item item))
return;
if (m.AddToBackpack(item))
{
m.SendLocalizedMessage(1072223); // An item has been placed in your backpack.
m.SendLocalizedMessage(
1062317); // For your valor in combating the fallen beast, a special artifact has been bestowed on you.
}
else if (m.BankBox.TryDropItem(m, item, false))
{
m.SendLocalizedMessage(1072224); // An item has been placed in your bank box.
m.SendLocalizedMessage(
1062317); // For your valor in combating the fallen beast, a special artifact has been bestowed on you.
}
else
{
// Item was placed at feet by m.AddToBackpack
m.SendLocalizedMessage(1072523); // You find an artifact, but your backpack and bank are too full to hold it.
}
}
public static bool CheckML(Mobile from, bool message = true)
{
if (from?.NetState == null)
return false;
if (from.NetState.SupportsExpansion(Expansion.ML))
return true;
if (message)
from.SendLocalizedMessage(1072791); // You must upgrade to Mondain's Legacy in order to use that item.
return false;
}
public static bool IsMLRegion(Region region)
{
return region.IsPartOf("Twisted Weald")
|| region.IsPartOf("Sanctuary")
|| region.IsPartOf("The Prism of Light")
|| region.IsPartOf("The Citadel")
|| region.IsPartOf("Bedlam")
|| region.IsPartOf("Blighted Grove")
|| region.IsPartOf("The Painted Caves")
|| region.IsPartOf("The Palace of Paroxysmus")
|| region.IsPartOf("Labyrinth");
}
}
}

View file

@ -0,0 +1,100 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
namespace Server
{
public class NameList
{
public string Type { get; }
public string[] List { get; }
public bool ContainsName( string name )
{
for ( int i = 0; i < List.Length; i++ )
if ( name == List[i] )
return true;
return false;
}
public NameList( string type, XmlElement xml )
{
Type = type;
List = xml.InnerText.Split( ',' );
for ( int i = 0; i < List.Length; ++i )
List[i] = Utility.Intern( List[i].Trim() );
}
public string GetRandomName()
{
if ( List.Length > 0 )
return List[Utility.Random( List.Length )];
return "";
}
public static NameList GetNameList( string type )
{
m_Table.TryGetValue( type, out NameList n );
return n;
}
public static string RandomName( string type )
{
return GetNameList( type )?.GetRandomName() ?? "";
}
private static Dictionary<string, NameList> m_Table;
static NameList()
{
m_Table = new Dictionary<string, NameList>( StringComparer.OrdinalIgnoreCase );
string filePath = Path.Combine( Core.BaseDirectory, "Data/names.xml" );
if ( !File.Exists( filePath ) )
return;
try
{
Load( filePath );
}
catch ( Exception e )
{
Console.WriteLine( "Warning: Exception caught loading name lists:" );
Console.WriteLine( e );
}
}
private static void Load( string filePath )
{
XmlDocument doc = new XmlDocument();
doc.Load( filePath );
XmlElement root = doc["names"];
foreach ( XmlElement element in root.GetElementsByTagName( "namelist" ) )
{
string type = element.GetAttribute( "type" );
if ( string.IsNullOrEmpty( type ) )
continue;
try
{
NameList list = new NameList( type, element );
m_Table[type] = list;
}
catch
{
// ignored
}
}
}
}
}

View file

@ -0,0 +1,202 @@
using Server.Commands;
namespace Server.Misc
{
public class NameVerification
{
public static readonly char[] SpaceDashPeriodQuote =
{
' ', '-', '.', '\''
};
public static readonly char[] Empty = new char[0];
public static string[] StartDisallowed{ get; } =
{
"seer",
"counselor",
"gm",
"admin",
"lady",
"lord"
};
public static string[] Disallowed{ get; } =
{
"jigaboo",
"chigaboo",
"wop",
"kyke",
"kike",
"tit",
"spic",
"prick",
"piss",
"lezbo",
"lesbo",
"felatio",
"dyke",
"dildo",
"chinc",
"chink",
"cunnilingus",
"cum",
"cocksucker",
"cock",
"clitoris",
"clit",
"ass",
"hitler",
"penis",
"nigga",
"nigger",
"klit",
"kunt",
"jiz",
"jism",
"jerkoff",
"jackoff",
"goddamn",
"fag",
"blowjob",
"bitch",
"asshole",
"dick",
"pussy",
"snatch",
"cunt",
"twat",
"shit",
"fuck",
"tailor",
"smith",
"scholar",
"rogue",
"novice",
"neophyte",
"merchant",
"medium",
"master",
"mage",
"lb",
"journeyman",
"grandmaster",
"fisherman",
"expert",
"chef",
"carpenter",
"british",
"blackthorne",
"blackthorn",
"beggar",
"archer",
"apprentice",
"adept",
"gamemaster",
"frozen",
"squelched",
"invulnerable",
"osi",
"origin"
};
public static void Initialize()
{
CommandSystem.Register("ValidateName", AccessLevel.Administrator, ValidateName_OnCommand);
}
[Usage("ValidateName")]
[Description("Checks the result of NameValidation on the specified name.")]
public static void ValidateName_OnCommand(CommandEventArgs e)
{
if (Validate(e.ArgString, 2, 16, true, false, true, 1, SpaceDashPeriodQuote))
e.Mobile.SendMessage(0x59, "That name is considered valid.");
else
e.Mobile.SendMessage(0x22, "That name is considered invalid.");
}
public static bool Validate(string name, int minLength, int maxLength, bool allowLetters, bool allowDigits,
bool noExceptionsAtStart, int maxExceptions, char[] exceptions)
{
return Validate(name, minLength, maxLength, allowLetters, allowDigits, noExceptionsAtStart, maxExceptions,
exceptions, Disallowed, StartDisallowed);
}
public static bool Validate(string name, int minLength, int maxLength, bool allowLetters, bool allowDigits,
bool noExceptionsAtStart, int maxExceptions, char[] exceptions, string[] disallowed, string[] startDisallowed)
{
if (name == null || name.Length < minLength || name.Length > maxLength)
return false;
int exceptCount = 0;
name = name.ToLower();
if (!allowLetters || !allowDigits ||
exceptions.Length > 0 && (noExceptionsAtStart || maxExceptions < int.MaxValue))
for (int i = 0; i < name.Length; ++i)
{
char c = name[i];
if (c >= 'a' && c <= 'z')
{
if (!allowLetters)
return false;
exceptCount = 0;
}
else if (c >= '0' && c <= '9')
{
if (!allowDigits)
return false;
exceptCount = 0;
}
else
{
bool except = false;
for (int j = 0; !except && j < exceptions.Length; ++j)
if (c == exceptions[j])
except = true;
if (!except || i == 0 && noExceptionsAtStart)
return false;
if (exceptCount++ == maxExceptions)
return false;
}
}
for (int i = 0; i < disallowed.Length; ++i)
{
int indexOf = name.IndexOf(disallowed[i]);
if (indexOf == -1)
continue;
bool badPrefix = indexOf == 0;
for (int j = 0; !badPrefix && j < exceptions.Length; ++j)
badPrefix = name[indexOf - 1] == exceptions[j];
if (!badPrefix)
continue;
bool badSuffix = indexOf + disallowed[i].Length >= name.Length;
for (int j = 0; !badSuffix && j < exceptions.Length; ++j)
badSuffix = name[indexOf + disallowed[i].Length] == exceptions[j];
if (badSuffix)
return false;
}
for (int i = 0; i < startDisallowed.Length; ++i)
if (name.StartsWith(startDisallowed[i]))
return false;
return true;
}
}
}

View file

@ -0,0 +1,468 @@
using System;
using System.Collections.Generic;
using Server.Engines.ConPVP;
using Server.Engines.PartySystem;
using Server.Factions;
using Server.Guilds;
using Server.Items;
using Server.Mobiles;
using Server.Multis;
using Server.SkillHandlers;
using Server.Spells.Seventh;
namespace Server.Misc
{
public class NotorietyHandlers
{
public static void Initialize()
{
Notoriety.Hues[Notoriety.Innocent] = 0x59;
Notoriety.Hues[Notoriety.Ally] = 0x3F;
Notoriety.Hues[Notoriety.CanBeAttacked] = 0x3B2;
Notoriety.Hues[Notoriety.Criminal] = 0x3B2;
Notoriety.Hues[Notoriety.Enemy] = 0x90;
Notoriety.Hues[Notoriety.Murderer] = 0x22;
Notoriety.Hues[Notoriety.Invulnerable] = 0x35;
Notoriety.Handler = MobileNotoriety;
Mobile.AllowBeneficialHandler = Mobile_AllowBeneficial;
Mobile.AllowHarmfulHandler = Mobile_AllowHarmful;
}
private static GuildStatus GetGuildStatus(Mobile m)
{
if (m.Guild == null)
return GuildStatus.None;
if (((Guild)m.Guild).Enemies.Count == 0 && m.Guild.Type == GuildType.Regular)
return GuildStatus.Peaceful;
return GuildStatus.Waring;
}
private static bool CheckBeneficialStatus(GuildStatus from, GuildStatus target)
{
if (from == GuildStatus.Waring || target == GuildStatus.Waring)
return false;
return true;
}
/*private static bool CheckHarmfulStatus( GuildStatus from, GuildStatus target )
{
if ( from == GuildStatus.Waring && target == GuildStatus.Waring )
return true;
return false;
}*/
public static bool Mobile_AllowBeneficial(Mobile from, Mobile target)
{
if (from == null || target == null || from.AccessLevel > AccessLevel.Player ||
target.AccessLevel > AccessLevel.Player)
return true;
#region Dueling
PlayerMobile pmFrom = from as PlayerMobile;
PlayerMobile pmTarg = target as PlayerMobile;
if (pmFrom == null && from is BaseCreature bcFrom && bcFrom.Summoned)
pmFrom = bcFrom.SummonMaster as PlayerMobile;
if (pmTarg == null && target is BaseCreature bcTarg && bcTarg.Summoned)
pmTarg = bcTarg.SummonMaster as PlayerMobile;
if (pmFrom != null && pmTarg != null)
{
if (pmFrom.DuelContext != pmTarg.DuelContext &&
(pmFrom.DuelContext?.Started == true || pmTarg.DuelContext?.Started == true))
return false;
if (pmFrom.DuelContext != null && pmFrom.DuelContext == pmTarg.DuelContext &&
(pmFrom.DuelContext.StartedReadyCountdown && !pmFrom.DuelContext.Started || pmFrom.DuelContext.Tied ||
pmFrom.DuelPlayer.Eliminated || pmTarg.DuelPlayer.Eliminated))
return false;
if (pmFrom.DuelPlayer?.Eliminated == false && pmFrom.DuelContext?.IsSuddenDeath == true)
return false;
if (pmFrom.DuelContext != null && pmFrom.DuelContext == pmTarg.DuelContext &&
pmFrom.DuelContext.m_Tournament?.IsNotoRestricted == true &&
pmFrom.DuelPlayer != null && pmTarg.DuelPlayer != null &&
pmFrom.DuelPlayer.Participant != pmTarg.DuelPlayer.Participant)
return false;
if (pmFrom.DuelContext?.Started == true && pmFrom.DuelContext == pmTarg.DuelContext)
return true;
}
if (pmFrom?.DuelContext?.Started == true || pmTarg?.DuelContext?.Started == true)
return false;
if (from.Region.IsPartOf<SafeZone>() || target.Region.IsPartOf<SafeZone>())
return false;
#endregion
Map map = from.Map;
#region Factions
Faction targetFaction = Faction.Find(target, true);
if ((!Core.ML || map == Faction.Facet) && targetFaction != null)
if (Faction.Find(from, true) != targetFaction)
return false;
#endregion
if ((map?.Rules & MapRules.BeneficialRestrictions) == 0)
return true; // In felucca, anything goes
if (!from.Player)
return true; // NPCs have no restrictions
if (target is BaseCreature creature && !creature.Controlled)
return false; // Players cannot heal uncontrolled mobiles
if (pmFrom?.Young == true || pmTarg?.Young == true)
return false; // Young players cannot perform beneficial actions towards older players
if (from.Guild is Guild fromGuild && target.Guild is Guild targetGuild &&
(targetGuild == fromGuild || fromGuild.IsAlly(targetGuild)))
return true; // Guild members can be beneficial
return CheckBeneficialStatus(GetGuildStatus(from), GetGuildStatus(target));
}
public static bool Mobile_AllowHarmful(Mobile from, Mobile target)
{
if (from == null || target == null || from.AccessLevel > AccessLevel.Player ||
target.AccessLevel > AccessLevel.Player)
return true;
#region Dueling
PlayerMobile pmFrom = from as PlayerMobile;
PlayerMobile pmTarg = target as PlayerMobile;
BaseCreature bcTarg = target as BaseCreature;
if (pmFrom == null && from is BaseCreature bcFrom && bcFrom.Summoned)
pmFrom = bcFrom.SummonMaster as PlayerMobile;
if (pmTarg == null && bcTarg?.Summoned == true)
pmTarg = bcTarg.SummonMaster as PlayerMobile;
if (pmFrom != null && pmTarg != null)
{
if (pmFrom.DuelContext != pmTarg.DuelContext &&
(pmFrom.DuelContext?.Started == true || pmTarg.DuelContext?.Started == true))
return false;
if (pmFrom.DuelContext != null && pmFrom.DuelContext == pmTarg.DuelContext &&
(pmFrom.DuelContext.StartedReadyCountdown && !pmFrom.DuelContext.Started || pmFrom.DuelContext.Tied ||
pmFrom.DuelPlayer.Eliminated || pmTarg.DuelPlayer.Eliminated))
return false;
if (pmFrom.DuelContext != null && pmFrom.DuelContext == pmTarg.DuelContext &&
pmFrom.DuelContext.m_Tournament?.IsNotoRestricted == true &&
pmFrom.DuelPlayer != null && pmTarg.DuelPlayer != null &&
pmFrom.DuelPlayer.Participant == pmTarg.DuelPlayer.Participant)
return false;
if ( pmFrom.DuelContext?.Started == true && pmFrom.DuelContext == pmTarg.DuelContext)
return true;
}
if (pmFrom?.DuelContext?.Started == true || pmTarg?.DuelContext?.Started == true)
return false;
if (from.Region.IsPartOf<SafeZone>() || target.Region.IsPartOf<SafeZone>())
return false;
#endregion
Map map = from.Map;
if ((map?.Rules & MapRules.HarmfulRestrictions) == 0)
return true; // In felucca, anything goes
if (!from.Player && !(from is BaseCreature bc && bc.GetMaster() != null &&
bc.GetMaster().AccessLevel == AccessLevel.Player))
{
if (!CheckAggressor(from.Aggressors, target) && !CheckAggressed(from.Aggressed, target) &&
pmTarg?.CheckYoungProtection(from) == true)
return false;
return true; // Uncontrolled NPCs are only restricted by the young system
}
Guild fromGuild = GetGuildFor(from.Guild as Guild, from);
Guild targetGuild = GetGuildFor(target.Guild as Guild, target);
if (fromGuild != null && targetGuild != null &&
(fromGuild == targetGuild || fromGuild.IsAlly(targetGuild) || fromGuild.IsEnemy(targetGuild)))
return true; // Guild allies or enemies can be harmful
if (bcTarg?.Controlled == true || bcTarg?.Summoned == true && bcTarg?.SummonMaster != from)
return false; // Cannot harm other controlled mobiles
if (target.Player)
return false; // Cannot harm other players
return bcTarg?.InitialInnocent == true || Notoriety.Compute(from, target) != Notoriety.Innocent;
}
public static Guild GetGuildFor(Guild def, Mobile m)
{
Guild g = def;
if (m is BaseCreature c && c.Controlled && c.ControlMaster != null)
{
c.DisplayGuildTitle = false;
if (c.Map != Map.Internal && (Core.AOS || Guild.NewGuildSystem || c.ControlOrder == OrderType.Attack ||
c.ControlOrder == OrderType.Guard))
g = (Guild)(c.Guild = c.ControlMaster.Guild);
else if (c.Map == Map.Internal || c.ControlMaster.Guild == null)
g = (Guild)(c.Guild = null);
}
return g;
}
public static int CorpseNotoriety(Mobile source, Corpse target)
{
if (target.AccessLevel > AccessLevel.Player)
return Notoriety.CanBeAttacked;
Body body = target.Amount;
Guild sourceGuild = GetGuildFor(source.Guild as Guild, source);
Guild targetGuild = GetGuildFor(target.Guild, target.Owner);
Faction srcFaction = Faction.Find(source, true, true);
Faction trgFaction = Faction.Find(target.Owner, true, true);
List<Mobile> list = target.Aggressors;
if (sourceGuild != null && targetGuild != null)
{
if (sourceGuild == targetGuild || sourceGuild.IsAlly(targetGuild))
return Notoriety.Ally;
if (sourceGuild.IsEnemy(targetGuild))
return Notoriety.Enemy;
}
if (target.Owner is BaseCreature creature)
{
if (srcFaction != null && trgFaction != null && srcFaction != trgFaction && source.Map == Faction.Facet)
return Notoriety.Enemy;
if (CheckHouseFlag(source, creature, target.Location, target.Map))
return Notoriety.CanBeAttacked;
int actual = Notoriety.CanBeAttacked;
if (target.Kills >= 5 || body.IsMonster && IsSummoned(creature) || creature.AlwaysMurderer ||
creature.IsAnimatedDead)
actual = Notoriety.Murderer;
if (DateTime.UtcNow >= target.TimeOfDeath + Corpse.MonsterLootRightSacrifice)
return actual;
Party sourceParty = Party.Get(source);
for (int i = 0; i < list.Count; ++i)
if (list[i] == source || sourceParty != null && Party.Get(list[i]) == sourceParty)
return actual;
return Notoriety.Innocent;
}
if (target.Kills >= 5 || body.IsMonster)
return Notoriety.Murderer;
if (target.Criminal && target.Map != null && (target.Map.Rules & MapRules.HarmfulRestrictions) == 0)
return Notoriety.Criminal;
if (srcFaction != null && trgFaction != null && srcFaction != trgFaction && source.Map == Faction.Facet)
for (int i = 0; i < list.Count; ++i)
if (list[i] == source || list[i] is BaseFactionGuard)
return Notoriety.Enemy;
if (CheckHouseFlag(source, target.Owner, target.Location, target.Map))
return Notoriety.CanBeAttacked;
if (!(target.Owner is PlayerMobile))
return Notoriety.CanBeAttacked;
for (int i = 0; i < list.Count; ++i)
if (list[i] == source)
return Notoriety.CanBeAttacked;
return Notoriety.Innocent;
}
/* Must be thread-safe */
public static int MobileNotoriety(Mobile source, Mobile target)
{
BaseCreature bcTarg = target as BaseCreature;
if (Core.AOS && (target.Blessed || bcTarg?.IsInvulnerable == true || target is PlayerVendor ||
target is TownCrier))
return Notoriety.Invulnerable;
PlayerMobile pmFrom = source as PlayerMobile;
PlayerMobile pmTarg = target as PlayerMobile;
#region Dueling
if (pmFrom != null && pmTarg != null)
if (pmFrom.DuelContext != null && pmFrom.DuelContext.StartedBeginCountdown && !pmFrom.DuelContext.Finished &&
pmFrom.DuelContext == pmTarg.DuelContext)
return pmFrom.DuelContext.IsAlly(pmFrom, pmTarg) ? Notoriety.Ally : Notoriety.Enemy;
#endregion
if (target.AccessLevel > AccessLevel.Player)
return Notoriety.CanBeAttacked;
if (source.Player && !target.Player && pmFrom != null && bcTarg != null)
{
Mobile master = bcTarg.GetMaster();
if (master?.AccessLevel > AccessLevel.Player)
return Notoriety.CanBeAttacked;
master = bcTarg.ControlMaster;
if (Core.ML && master != null)
{
if (source == master && CheckAggressor(bcTarg.Aggressors, source) ||
CheckAggressor(source.Aggressors, bcTarg))
return Notoriety.CanBeAttacked;
return MobileNotoriety(source, master);
}
if (!bcTarg.Summoned && !bcTarg.Controlled && pmFrom.EnemyOfOneType == bcTarg.GetType())
return Notoriety.Enemy;
}
if (target.Kills >= 5 ||
target.Body.IsMonster && IsSummoned(bcTarg) && !(target is BaseFamiliar) && !(target is ArcaneFey) &&
!(target is Golem) || bcTarg?.AlwaysMurderer == true || bcTarg?.IsAnimatedDead == true)
return Notoriety.Murderer;
if (target.Criminal)
return Notoriety.Criminal;
Guild sourceGuild = GetGuildFor(source.Guild as Guild, source);
Guild targetGuild = GetGuildFor(target.Guild as Guild, target);
if (sourceGuild != null && targetGuild != null)
{
if (sourceGuild == targetGuild || sourceGuild.IsAlly(targetGuild))
return Notoriety.Ally;
if (sourceGuild.IsEnemy(targetGuild))
return Notoriety.Enemy;
}
Faction srcFaction = Faction.Find(source, true, true);
Faction trgFaction = Faction.Find(target, true, true);
if (srcFaction != null && trgFaction != null && srcFaction != trgFaction && source.Map == Faction.Facet)
return Notoriety.Enemy;
if (Stealing.ClassicMode && pmTarg?.PermaFlags.Contains(source) == true)
return Notoriety.CanBeAttacked;
if (bcTarg?.AlwaysAttackable == true)
return Notoriety.CanBeAttacked;
if (CheckHouseFlag(source, target, target.Location, target.Map))
return Notoriety.CanBeAttacked;
if (bcTarg?.InitialInnocent != true)
if (!target.Body.IsHuman && !target.Body.IsGhost && !IsPet(bcTarg) && pmTarg == null ||
!Core.ML && !target.CanBeginAction<PolymorphSpell>())
return Notoriety.CanBeAttacked;
if (CheckAggressor(source.Aggressors, target))
return Notoriety.CanBeAttacked;
if (CheckAggressed(source.Aggressed, target))
return Notoriety.CanBeAttacked;
if (bcTarg?.Controlled == true && bcTarg.ControlOrder == OrderType.Guard &&
bcTarg.ControlTarget == source)
return Notoriety.CanBeAttacked;
if (source is BaseCreature bc)
{
Mobile master = bc.GetMaster();
if (master != null && CheckAggressor(master.Aggressors, target) ||
MobileNotoriety(master, target) == Notoriety.CanBeAttacked || bcTarg != null)
return Notoriety.CanBeAttacked;
}
return Notoriety.Innocent;
}
public static bool CheckHouseFlag(Mobile from, Mobile m, Point3D p, Map map)
{
BaseHouse house = BaseHouse.FindHouseAt(p, map, 16);
if (house?.Public != false || !house.IsFriend(from))
return false;
if (m != null && house.IsFriend(m))
return false;
return !(m is BaseCreature c) || c.Deleted || !c.Controlled || c.ControlMaster == null ||
!house.IsFriend(c.ControlMaster);
}
public static bool IsPet(BaseCreature c)
{
return c?.Controlled == true;
}
public static bool IsSummoned(BaseCreature c)
{
return c?.Summoned == true;
}
public static bool CheckAggressor(List<AggressorInfo> list, Mobile target)
{
for (int i = 0; i < list.Count; ++i)
if (list[i].Attacker == target)
return true;
return false;
}
public static bool CheckAggressed(List<AggressorInfo> list, Mobile target)
{
for (int i = 0; i < list.Count; ++i)
{
AggressorInfo info = list[i];
if (!info.CriminalAggression && info.Defender == target)
return true;
}
return false;
}
private enum GuildStatus
{
None,
Peaceful,
Waring
}
}
}

View file

@ -0,0 +1,33 @@
using System.Collections.Generic;
using Server.Network;
namespace Server.Misc
{
public class Paperdoll
{
public static void Initialize()
{
EventSink.PaperdollRequest += EventSink_PaperdollRequest;
}
public static void EventSink_PaperdollRequest(PaperdollRequestEventArgs e)
{
Mobile beholder = e.Beholder;
Mobile beheld = e.Beheld;
beholder.Send(new DisplayPaperdoll(beheld, Titles.ComputeTitle(beholder, beheld),
beheld.AllowEquipFrom(beholder)));
if (ObjectPropertyList.Enabled)
{
List<Item> items = beheld.Items;
for (int i = 0; i < items.Count; ++i)
beholder.Send(items[i].OPLPacket);
// NOTE: OSI sends MobileUpdate when opening your own paperdoll.
// It has a very bad rubber-banding affect. What positive affects does it have?
}
}
}
}

View file

@ -0,0 +1,153 @@
using System;
using Server.Items;
using Server.Mobiles;
using Server.Network;
using Server.Spells;
using Server.Spells.Necromancy;
using Server.Spells.Ninjitsu;
namespace Server
{
public class PoisonImpl : Poison
{
private int m_Count, m_MessageInterval;
// Timers
private TimeSpan m_Delay;
private TimeSpan m_Interval;
// Info
// Damage
private int m_Minimum, m_Maximum;
private double m_Scalar;
public PoisonImpl(string name, int level, int min, int max, double percent, double delay, double interval, int count,
int messageInterval)
{
Name = name;
Level = level;
m_Minimum = min;
m_Maximum = max;
m_Scalar = percent * 0.01;
m_Delay = TimeSpan.FromSeconds(delay);
m_Interval = TimeSpan.FromSeconds(interval);
m_Count = count;
m_MessageInterval = messageInterval;
}
public override string Name{ get; }
public override int Level{ get; }
[CallPriority(10)]
public static void Configure()
{
if (Core.AOS)
{
Register(new PoisonImpl("Lesser", 0, 4, 16, 7.5, 3.0, 2.25, 10, 4));
Register(new PoisonImpl("Regular", 1, 8, 18, 10.0, 3.0, 3.25, 10, 3));
Register(new PoisonImpl("Greater", 2, 12, 20, 15.0, 3.0, 4.25, 10, 2));
Register(new PoisonImpl("Deadly", 3, 16, 30, 30.0, 3.0, 5.25, 15, 2));
Register(new PoisonImpl("Lethal", 4, 20, 50, 35.0, 3.0, 5.25, 20, 2));
}
else
{
Register(new PoisonImpl("Lesser", 0, 4, 26, 2.500, 3.5, 3.0, 10, 2));
Register(new PoisonImpl("Regular", 1, 5, 26, 3.125, 3.5, 3.0, 10, 2));
Register(new PoisonImpl("Greater", 2, 6, 26, 6.250, 3.5, 3.0, 10, 2));
Register(new PoisonImpl("Deadly", 3, 7, 26, 12.500, 3.5, 4.0, 10, 2));
Register(new PoisonImpl("Lethal", 4, 9, 26, 25.000, 3.5, 5.0, 10, 2));
}
}
public static Poison IncreaseLevel(Poison oldPoison)
{
Poison newPoison = oldPoison == null ? null : GetPoison(oldPoison.Level + 1);
return newPoison ?? oldPoison;
}
public override Timer ConstructTimer(Mobile m)
{
return new PoisonTimer(m, this);
}
public class PoisonTimer : Timer
{
private int m_Index;
private int m_LastDamage;
private Mobile m_Mobile;
private PoisonImpl m_Poison;
public PoisonTimer(Mobile m, PoisonImpl p) : base(p.m_Delay, p.m_Interval)
{
From = m;
m_Mobile = m;
m_Poison = p;
}
public Mobile From{ get; set; }
protected override void OnTick()
{
if (Core.AOS && m_Poison.Level < 4 &&
TransformationSpellHelper.UnderTransformation(m_Mobile, typeof(VampiricEmbraceSpell)) ||
m_Poison.Level < 3 && OrangePetals.UnderEffect(m_Mobile) ||
AnimalForm.UnderTransformation(m_Mobile, typeof(Unicorn)))
if (m_Mobile.CurePoison(m_Mobile))
{
m_Mobile.LocalOverheadMessage(MessageType.Emote, 0x3F, true,
"* You feel yourself resisting the effects of the poison *");
m_Mobile.NonlocalOverheadMessage(MessageType.Emote, 0x3F, true,
$"* {m_Mobile.Name} seems resistant to the poison *");
Stop();
return;
}
if (m_Index++ == m_Poison.m_Count)
{
m_Mobile.SendLocalizedMessage(502136); // The poison seems to have worn off.
m_Mobile.Poison = null;
Stop();
return;
}
int damage;
if (!Core.AOS && m_LastDamage != 0 && Utility.RandomBool())
{
damage = m_LastDamage;
}
else
{
damage = 1 + (int)(m_Mobile.Hits * m_Poison.m_Scalar);
if (damage < m_Poison.m_Minimum)
damage = m_Poison.m_Minimum;
else if (damage > m_Poison.m_Maximum)
damage = m_Poison.m_Maximum;
m_LastDamage = damage;
}
From?.DoHarmful(m_Mobile, true);
if (m_Mobile is IHonorTarget honorTarget)
honorTarget.ReceivedHonorContext?.OnTargetPoisoned();
AOS.Damage(m_Mobile, From, damage, 0, 0, 0, 100, 0);
if (0.60 <= Utility.RandomDouble()
) // OSI: randomly revealed between first and third damage tick, guessing 60% chance
m_Mobile.RevealingAction();
if (m_Index % m_Poison.m_MessageInterval == 0)
m_Mobile.OnPoisoned(From, m_Poison, m_Poison);
}
}
}
}

View file

@ -0,0 +1,123 @@
using Server.Network;
namespace Server.Misc
{
public enum ProfanityAction
{
None, // no action taken
Disallow, // speech is not displayed
Criminal, // makes the player criminal, not killable by guards
CriminalAction, // makes the player criminal, can be killed by guards
Disconnect, // player is kicked
Other // some other implementation
}
public class ProfanityProtection
{
private static bool Enabled = false;
private static ProfanityAction
Action = ProfanityAction.Disallow; // change here what to do when profanity is detected
public static char[] Exceptions{ get; } =
{
' ', '-', '.', '\'', '"', ',', '_', '+', '=', '~', '`', '!', '^', '*', '\\', '/', ';', ':', '<', '>', '[', ']',
'{', '}', '?', '|', '(', ')', '%', '$', '&', '#', '@'
};
public static string[] StartDisallowed{ get; } = { };
public static string[] Disallowed{ get; } =
{
"jigaboo",
"chigaboo",
"wop",
"kyke",
"kike",
"tit",
"spic",
"prick",
"piss",
"lezbo",
"lesbo",
"felatio",
"dyke",
"dildo",
"chinc",
"chink",
"cunnilingus",
"cum",
"cocksucker",
"cock",
"clitoris",
"clit",
"ass",
"hitler",
"penis",
"nigga",
"nigger",
"klit",
"kunt",
"jiz",
"jism",
"jerkoff",
"jackoff",
"goddamn",
"fag",
"blowjob",
"bitch",
"asshole",
"dick",
"pussy",
"snatch",
"cunt",
"twat",
"shit",
"fuck"
};
public static void Initialize()
{
if (Enabled)
EventSink.Speech += EventSink_Speech;
}
private static bool OnProfanityDetected(Mobile from, string speech)
{
switch (Action)
{
case ProfanityAction.None: return true;
case ProfanityAction.Disallow: return false;
case ProfanityAction.Criminal:
from.Criminal = true;
return true;
case ProfanityAction.CriminalAction:
from.CriminalAction(false);
return true;
case ProfanityAction.Disconnect:
{
from.NetState?.Dispose();
return false;
}
default:
case ProfanityAction.Other: // TODO: Provide custom implementation if this is chosen
{
return true;
}
}
}
private static void EventSink_Speech(SpeechEventArgs e)
{
Mobile from = e.Mobile;
if (from.AccessLevel > AccessLevel.Player)
return;
if (!NameVerification.Validate(e.Speech, 0, int.MaxValue, true, true, false, int.MaxValue, Exceptions,
Disallowed, StartDisallowed))
e.Blocked = !OnProfanityDetected(from, e.Speech);
}
}
}

View file

@ -0,0 +1,93 @@
using System;
using Server.Accounting;
using Server.Network;
namespace Server.Misc
{
public class Profile
{
public static void Initialize()
{
EventSink.ProfileRequest += EventSink_ProfileRequest;
EventSink.ChangeProfileRequest += EventSink_ChangeProfileRequest;
}
public static void EventSink_ChangeProfileRequest(ChangeProfileRequestEventArgs e)
{
Mobile from = e.Beholder;
if (from.ProfileLocked)
from.SendMessage("Your profile is locked. You may not change it.");
else
from.Profile = e.Text;
}
public static void EventSink_ProfileRequest(ProfileRequestEventArgs e)
{
Mobile beholder = e.Beholder;
Mobile beheld = e.Beheld;
if (!beheld.Player)
return;
if (beholder.Map != beheld.Map || !beholder.InRange(beheld, 12) || !beholder.CanSee(beheld))
return;
string header = Titles.ComputeTitle(beholder, beheld);
string footer = "";
if (beheld.ProfileLocked)
{
if (beholder == beheld)
footer = "Your profile has been locked.";
else if (beholder.AccessLevel >= AccessLevel.Counselor)
footer = "This profile has been locked.";
}
if (footer.Length == 0 && beholder == beheld)
footer = GetAccountDuration(beheld);
string body = beheld.Profile;
if (body == null || body.Length <= 0)
body = "";
beholder.Send(new DisplayProfile(beholder != beheld || !beheld.ProfileLocked, beheld, header, body, footer));
}
private static string GetAccountDuration(Mobile m)
{
if (!(m.Account is Account a))
return "";
TimeSpan ts = DateTime.UtcNow - a.Created;
if (Format(ts.TotalDays, "This account is {0} day{1} old.", out string v))
return v;
if (Format(ts.TotalHours, "This account is {0} hour{1} old.", out v))
return v;
if (Format(ts.TotalMinutes, "This account is {0} minute{1} old.", out v))
return v;
if (Format(ts.TotalSeconds, "This account is {0} second{1} old.", out v))
return v;
return "";
}
public static bool Format(double value, string format, out string op)
{
if (value >= 1.0)
{
op = string.Format(format, (int)value, (int)value != 1 ? "s" : "");
return true;
}
op = null;
return false;
}
}
}

View file

@ -0,0 +1,64 @@
using System;
using Server.Network;
namespace Server.Misc
{
public class ProtocolExtensions
{
private static PacketHandler[] m_Handlers = new PacketHandler[0x100];
public static void Initialize()
{
PacketHandlers.Register(0xF0, 0, false, DecodeBundledPacket);
}
public static void Register(int packetID, bool ingame, OnPacketReceive onReceive)
{
m_Handlers[packetID] = new PacketHandler(packetID, 0, ingame, onReceive);
}
public static PacketHandler GetHandler(int packetID)
{
if (packetID >= 0 && packetID < m_Handlers.Length)
return m_Handlers[packetID];
return null;
}
public static void DecodeBundledPacket(NetState state, PacketReader pvSrc)
{
int packetID = pvSrc.ReadByte();
PacketHandler ph = GetHandler(packetID);
if (ph != null)
{
if (ph.Ingame && state.Mobile == null)
{
Console.WriteLine(
"Client: {0}: Sent ingame packet (0xF0x{1:X2}) before having been attached to a mobile", state,
packetID);
state.Dispose();
}
else if (ph.Ingame && state.Mobile.Deleted)
{
state.Dispose();
}
else
{
ph.OnReceive(state, pvSrc);
}
}
}
}
public abstract class ProtocolExtension : Packet
{
public ProtocolExtension(int packetID, int capacity) : base(0xF0)
{
EnsureCapacity(4 + capacity);
m_Stream.Write((byte)packetID);
}
}
}

View file

@ -0,0 +1,322 @@
namespace Server.Misc
{
public class RaceDefinitions
{
public static void Configure()
{
/* Here we configure all races. Some notes:
*
* 1) The first 32 races are reserved for core use.
* 2) Race 0x7F is reserved for core use.
* 3) Race 0xFF is reserved for core use.
* 4) Changing or removing any predefined races may cause server instability.
*/
RegisterRace(new Human(0, 0));
RegisterRace(new Elf(1, 1));
RegisterRace(new Gargoyle(2, 2));
}
public static void RegisterRace(Race race)
{
Race.Races[race.RaceIndex] = race;
Race.AllRaces.Add(race);
}
private class Human : Race
{
public Human(int raceID, int raceIndex)
: base(raceID, raceIndex, "Human", "Humans", 400, 401, 402, 403, Expansion.None)
{
}
public override bool ValidateHair(bool female, int itemID)
{
if (itemID == 0)
return true;
if (female && itemID == 0x2048 || !female && itemID == 0x2046)
return false; //Buns & Receding Hair
if (itemID >= 0x203B && itemID <= 0x203D)
return true;
if (itemID >= 0x2044 && itemID <= 0x204A)
return true;
return false;
}
public override int RandomHair(bool female) //Random hair doesn't include baldness
{
switch (Utility.Random(9))
{
case 0: return 0x203B; //Short
case 1: return 0x203C; //Long
case 2: return 0x203D; //Pony Tail
case 3: return 0x2044; //Mohawk
case 4: return 0x2045; //Pageboy
case 5: return 0x2047; //Afro
case 6: return 0x2049; //Pig tails
case 7: return 0x204A; //Krisna
default: return female ? 0x2046 : 0x2048; //Buns or Receding Hair
}
}
public override bool ValidateFacialHair(bool female, int itemID)
{
if (itemID == 0)
return true;
if (female)
return false;
if (itemID >= 0x203E && itemID <= 0x2041)
return true;
if (itemID >= 0x204B && itemID <= 0x204D)
return true;
return false;
}
public override int RandomFacialHair(bool female)
{
if (female)
return 0;
int rand = Utility.Random(7);
return (rand < 4 ? 0x203E : 0x2047) + rand;
}
public override int ClipSkinHue(int hue)
{
if (hue < 1002)
return 1002;
if (hue > 1058)
return 1058;
return hue;
}
public override int RandomSkinHue()
{
return Utility.Random(1002, 57) | 0x8000;
}
public override int ClipHairHue(int hue)
{
if (hue < 1102)
return 1102;
if (hue > 1149)
return 1149;
return hue;
}
public override int RandomHairHue()
{
return Utility.Random(1102, 48);
}
}
private class Elf : Race
{
private static int[] m_SkinHues =
{
0x0BF, 0x24D, 0x24E, 0x24F, 0x353, 0x361, 0x367, 0x374,
0x375, 0x376, 0x381, 0x382, 0x383, 0x384, 0x385, 0x389,
0x3DE, 0x3E5, 0x3E6, 0x3E8, 0x3E9, 0x430, 0x4A7, 0x4DE,
0x51D, 0x53F, 0x579, 0x76B, 0x76C, 0x76D, 0x835, 0x903
};
private static int[] m_HairHues =
{
0x034, 0x035, 0x036, 0x037, 0x038, 0x039, 0x058, 0x08E,
0x08F, 0x090, 0x091, 0x092, 0x101, 0x159, 0x15A, 0x15B,
0x15C, 0x15D, 0x15E, 0x128, 0x12F, 0x1BD, 0x1E4, 0x1F3,
0x207, 0x211, 0x239, 0x251, 0x26C, 0x2C3, 0x2C9, 0x31D,
0x31E, 0x31F, 0x320, 0x321, 0x322, 0x323, 0x324, 0x325,
0x326, 0x369, 0x386, 0x387, 0x388, 0x389, 0x38A, 0x59D,
0x6B8, 0x725, 0x853
};
public Elf(int raceID, int raceIndex)
: base(raceID, raceIndex, "Elf", "Elves", 605, 606, 607, 608, Expansion.ML)
{
}
public override bool ValidateHair(bool female, int itemID)
{
if (itemID == 0)
return true;
if (female && (itemID == 0x2FCD || itemID == 0x2FBF) || !female && (itemID == 0x2FCC || itemID == 0x2FD0))
return false;
if (itemID >= 0x2FBF && itemID <= 0x2FC2)
return true;
if (itemID >= 0x2FCC && itemID <= 0x2FD1)
return true;
return false;
}
public override int RandomHair(bool female) //Random hair doesn't include baldness
{
switch (Utility.Random(8))
{
case 0: return 0x2FC0; //Long Feather
case 1: return 0x2FC1; //Short
case 2: return 0x2FC2; //Mullet
case 3: return 0x2FCE; //Knob
case 4: return 0x2FCF; //Braided
case 5: return 0x2FD1; //Spiked
case 6: return female ? 0x2FCC : 0x2FBF; //Flower or Mid-long
default: return female ? 0x2FD0 : 0x2FCD; //Bun or Long
}
}
public override bool ValidateFacialHair(bool female, int itemID)
{
return itemID == 0;
}
public override int RandomFacialHair(bool female)
{
return 0;
}
public override int ClipSkinHue(int hue)
{
for (int i = 0; i < m_SkinHues.Length; i++)
if (m_SkinHues[i] == hue)
return hue;
return m_SkinHues[0];
}
public override int RandomSkinHue()
{
return m_SkinHues[Utility.Random(m_SkinHues.Length)] | 0x8000;
}
public override int ClipHairHue(int hue)
{
for (int i = 0; i < m_HairHues.Length; i++)
if (m_HairHues[i] == hue)
return hue;
return m_HairHues[0];
}
public override int RandomHairHue()
{
return m_HairHues[Utility.Random(m_HairHues.Length)];
}
}
#region SA
private class Gargoyle : Race
{
// Todo Finish body hues
private static readonly int[] m_BodyHues =
{
0x86DB, 0x86DC, 0x86DD, 0x86DE,
0x86DF, 0x86E0, 0x86E1, 0x86E2,
0x86E3, 0x86E4, 0x86E5, 0x86E6
// 0x, 0x, 0x, 0x, // 86E7/86E8/86E9/86EA?
// 0x, 0x, 0x, 0x, // 86EB/86EC/86ED/86EE?
// 0x86F3, 0x86DB, 0x86DC, 0x86DD
};
private static readonly int[] m_HornHues =
{
0x709, 0x70B, 0x70D, 0x70F, 0x711, 0x763,
0x765, 0x768, 0x76B, 0x6F3, 0x6F1, 0x6EF,
0x6E4, 0x6E2, 0x6E0, 0x709, 0x70B, 0x70D
};
public Gargoyle(int raceID, int raceIndex)
: base(raceID, raceIndex, "Gargoyle", "Gargoyles", 666, 667, 402, 403, Expansion.SA)
{
}
public override bool ValidateHair(bool female, int itemID)
{
if (female == false) return itemID >= 0x4258 && itemID <= 0x425F;
return itemID == 0x4261 || itemID == 0x4262 || itemID >= 0x4273 && itemID <= 0x4275 || itemID == 0x42B0 ||
itemID == 0x42B1 || itemID == 0x42AA || itemID == 0x42AB;
}
public override int RandomHair(bool female)
{
if (Utility.Random(9) == 0)
return 0;
if (!female)
return 0x4258 + Utility.Random(8);
switch (Utility.Random(9))
{
case 0:
return 0x4261;
case 1:
return 0x4262;
case 2:
return 0x4273;
case 3:
return 0x4274;
case 4:
return 0x4275;
case 5:
return 0x42B0;
case 6:
return 0x42B1;
case 7:
return 0x42AA;
case 8:
return 0x42AB;
}
return 0;
}
public override bool ValidateFacialHair(bool female, int itemID)
{
return !female && itemID >= 0x42AD && itemID <= 0x42B0;
}
public override int RandomFacialHair(bool female)
{
return female ? 0 : Utility.RandomList(0, 0x42AD, 0x42AE, 0x42AF, 0x42B0);
}
public override int ClipSkinHue(int hue)
{
return hue; // for hue information gathering
}
public override int RandomSkinHue()
{
return m_BodyHues[Utility.Random(m_BodyHues.Length)] | 0x8000;
}
public override int ClipHairHue(int hue)
{
for (int i = 0; i < m_HornHues.Length; i++)
if (m_HornHues[i] == hue)
return hue;
return m_HornHues[0];
}
public override int RandomHairHue()
{
return m_HornHues[Utility.Random(m_HornHues.Length)];
}
}
#endregion
}
}

View file

@ -0,0 +1,220 @@
using System;
using Server.Items;
using Server.Mobiles;
using Server.Spells;
using Server.Spells.Necromancy;
using Server.Spells.Ninjitsu;
namespace Server.Misc
{
public class RegenRates
{
[CallPriority(10)]
public static void Configure()
{
Mobile.DefaultHitsRate = TimeSpan.FromSeconds(11.0);
Mobile.DefaultStamRate = TimeSpan.FromSeconds(7.0);
Mobile.DefaultManaRate = TimeSpan.FromSeconds(7.0);
Mobile.ManaRegenRateHandler = Mobile_ManaRegenRate;
if (Core.AOS)
{
Mobile.StamRegenRateHandler = Mobile_StamRegenRate;
Mobile.HitsRegenRateHandler = Mobile_HitsRegenRate;
}
}
private static void CheckBonusSkill(Mobile m, int cur, int max, SkillName skill)
{
if (!m.Alive)
return;
double n = (double)cur / max;
double v = Math.Sqrt(m.Skills[skill].Value * 0.005);
n *= 1.0 - v;
n += v;
m.CheckSkill(skill, n);
}
private static bool CheckTransform(Mobile m, Type type)
{
return TransformationSpellHelper.UnderTransformation(m, type);
}
private static bool CheckAnimal(Mobile m, Type type)
{
return AnimalForm.UnderTransformation(m, type);
}
private static TimeSpan Mobile_HitsRegenRate(Mobile from)
{
int points = AosAttributes.GetValue(from, AosAttribute.RegenHits);
BaseCreature bc = from as BaseCreature;
if (bc?.IsAnimatedDead == false)
points += 4;
if (bc?.IsParagon == true || from is Leviathan)
points += 40;
if (Core.ML && from.Race == Race.Human) //Is this affected by the cap?
points += 2;
if (points < 0)
points = 0;
if (Core.ML && from is PlayerMobile) //does racial bonus go before/after?
points = Math.Min(points, 18);
if (CheckTransform(from, typeof(HorrificBeastSpell)))
points += 20;
if (CheckAnimal(from, typeof(Dog)) || CheckAnimal(from, typeof(Cat)))
points += from.Skills.Ninjitsu.Fixed / 30;
return TimeSpan.FromSeconds(1.0 / (0.1 * (1 + points)));
}
private static TimeSpan Mobile_StamRegenRate(Mobile from)
{
if (from.Skills == null)
return Mobile.DefaultStamRate;
CheckBonusSkill(from, from.Stam, from.StamMax, SkillName.Focus);
int points = (int)(from.Skills.Focus.Value * 0.1);
if (from is BaseCreature creature && creature.IsParagon || from is Leviathan)
points += 40;
int cappedPoints = AosAttributes.GetValue(from, AosAttribute.RegenStam);
if (CheckTransform(from, typeof(VampiricEmbraceSpell)))
cappedPoints += 15;
if (CheckAnimal(from, typeof(Kirin)))
cappedPoints += 20;
if (Core.ML && from is PlayerMobile)
cappedPoints = Math.Min(cappedPoints, 24);
points += cappedPoints;
if (points < -1)
points = -1;
return TimeSpan.FromSeconds(1.0 / (0.1 * (2 + points)));
}
private static TimeSpan Mobile_ManaRegenRate(Mobile from)
{
if (from.Skills == null)
return Mobile.DefaultManaRate;
if (!from.Meditating)
CheckBonusSkill(from, from.Mana, from.ManaMax, SkillName.Meditation);
double rate;
double armorPenalty = GetArmorOffset(from);
if (Core.AOS)
{
double medPoints = from.Int + from.Skills.Meditation.Value * 3;
medPoints *= from.Skills.Meditation.Value < 100.0 ? 0.025 : 0.0275;
CheckBonusSkill(from, from.Mana, from.ManaMax, SkillName.Focus);
double focusPoints = from.Skills.Focus.Value * 0.05;
if (armorPenalty > 0)
medPoints = 0; // In AOS, wearing any meditation-blocking armor completely removes meditation bonus
double totalPoints = focusPoints + medPoints + (from.Meditating ? medPoints > 13.0 ? 13.0 : medPoints : 0.0);
if (from is BaseCreature creature && creature.IsParagon || from is Leviathan)
totalPoints += 40;
int cappedPoints = AosAttributes.GetValue(from, AosAttribute.RegenMana);
if (CheckTransform(from, typeof(VampiricEmbraceSpell)))
cappedPoints += 3;
else if (CheckTransform(from, typeof(LichFormSpell)))
cappedPoints += 13;
if (Core.ML && from is PlayerMobile)
cappedPoints = Math.Min(cappedPoints, 18);
totalPoints += cappedPoints;
if (totalPoints < -1)
totalPoints = -1;
if (Core.ML)
totalPoints = Math.Floor(totalPoints);
rate = 1.0 / (0.1 * (2 + totalPoints));
}
else
{
double medPoints = (from.Int + from.Skills.Meditation.Value) * 0.5;
if (medPoints <= 0)
rate = 7.0;
else if (medPoints <= 100)
rate = 7.0 - 239 * medPoints / 2400 + 19 * medPoints * medPoints / 48000;
else if (medPoints < 120)
rate = 1.0;
else
rate = 0.75;
rate += armorPenalty;
if (from.Meditating)
rate *= 0.5;
if (rate < 0.5)
rate = 0.5;
else if (rate > 7.0)
rate = 7.0;
}
return TimeSpan.FromSeconds(rate);
}
public static double GetArmorOffset(Mobile from)
{
double rating = 0.0;
if (!Core.AOS)
rating += GetArmorMeditationValue(from.ShieldArmor as BaseArmor);
rating += GetArmorMeditationValue(from.NeckArmor as BaseArmor);
rating += GetArmorMeditationValue(from.HandArmor as BaseArmor);
rating += GetArmorMeditationValue(from.HeadArmor as BaseArmor);
rating += GetArmorMeditationValue(from.ArmsArmor as BaseArmor);
rating += GetArmorMeditationValue(from.LegsArmor as BaseArmor);
rating += GetArmorMeditationValue(from.ChestArmor as BaseArmor);
return rating / 4;
}
private static double GetArmorMeditationValue(BaseArmor ar)
{
if (ar == null || ar.ArmorAttributes.MageArmor != 0 || ar.Attributes.SpellChanneling != 0)
return 0.0;
switch (ar.MeditationAllowance)
{
default:
case ArmorMeditationAllowance.None: return ar.BaseArmorRatingScaled;
case ArmorMeditationAllowance.Half: return ar.BaseArmorRatingScaled / 2.0;
case ArmorMeditationAllowance.All: return 0.0;
}
}
}
}

View file

@ -0,0 +1,47 @@
namespace Server.Misc
{
public class RenameRequests
{
public static void Initialize()
{
EventSink.RenameRequest += EventSink_RenameRequest;
}
private static void EventSink_RenameRequest(RenameRequestEventArgs e)
{
Mobile from = e.From;
Mobile targ = e.Target;
string name = e.Name;
if (from.CanSee(targ) && from.InRange(targ, 12) && targ.CanBeRenamedBy(from))
{
name = name.Trim();
if (NameVerification.Validate(name, 1, 16, true, false, true, 0, NameVerification.Empty,
NameVerification.StartDisallowed, Core.ML ? NameVerification.Disallowed : new string[] { }))
{
if (Core.ML)
{
string[] disallowed = ProfanityProtection.Disallowed;
for (int i = 0; i < disallowed.Length; i++)
if (name.IndexOf(disallowed[i]) != -1)
{
from.SendLocalizedMessage(1072622); // That name isn't very polite.
return;
}
from.SendLocalizedMessage(1072623,
$"{targ.Name}\t{name}"); // Pet ~1_OLDPETNAME~ renamed to ~2_NEWPETNAME~.
}
targ.Name = name;
}
else
{
from.SendMessage("That name is unacceptable.");
}
}
}
}
}

View file

@ -0,0 +1,686 @@
using System;
using System.Collections.Generic;
namespace Server.Items
{
public enum CraftResource
{
None = 0,
Iron = 1,
DullCopper,
ShadowIron,
Copper,
Bronze,
Gold,
Agapite,
Verite,
Valorite,
RegularLeather = 101,
SpinedLeather,
HornedLeather,
BarbedLeather,
RedScales = 201,
YellowScales,
BlackScales,
GreenScales,
WhiteScales,
BlueScales,
RegularWood = 301,
OakWood,
AshWood,
YewWood,
Heartwood,
Bloodwood,
Frostwood
}
public enum CraftResourceType
{
None,
Metal,
Leather,
Scales,
Wood
}
public class CraftAttributeInfo
{
public int WeaponFireDamage { get; set; }
public int WeaponColdDamage { get; set; }
public int WeaponPoisonDamage { get; set; }
public int WeaponEnergyDamage { get; set; }
public int WeaponChaosDamage { get; set; }
public int WeaponDirectDamage { get; set; }
public int WeaponDurability { get; set; }
public int WeaponLuck { get; set; }
public int WeaponGoldIncrease { get; set; }
public int WeaponLowerRequirements { get; set; }
public int ArmorPhysicalResist { get; set; }
public int ArmorFireResist { get; set; }
public int ArmorColdResist { get; set; }
public int ArmorPoisonResist { get; set; }
public int ArmorEnergyResist { get; set; }
public int ArmorDurability { get; set; }
public int ArmorLuck { get; set; }
public int ArmorGoldIncrease { get; set; }
public int ArmorLowerRequirements { get; set; }
public int RunicMinAttributes { get; set; }
public int RunicMaxAttributes { get; set; }
public int RunicMinIntensity { get; set; }
public int RunicMaxIntensity { get; set; }
public CraftAttributeInfo()
{
}
public static readonly CraftAttributeInfo Blank;
public static readonly CraftAttributeInfo DullCopper, ShadowIron, Copper, Bronze, Golden, Agapite, Verite, Valorite;
public static readonly CraftAttributeInfo Spined, Horned, Barbed;
public static readonly CraftAttributeInfo RedScales, YellowScales, BlackScales, GreenScales, WhiteScales, BlueScales;
public static readonly CraftAttributeInfo OakWood, AshWood, YewWood, Heartwood, Bloodwood, Frostwood;
static CraftAttributeInfo()
{
Blank = new CraftAttributeInfo();
CraftAttributeInfo dullCopper = DullCopper = new CraftAttributeInfo();
dullCopper.ArmorPhysicalResist = 6;
dullCopper.ArmorDurability = 50;
dullCopper.ArmorLowerRequirements = 20;
dullCopper.WeaponDurability = 100;
dullCopper.WeaponLowerRequirements = 50;
dullCopper.RunicMinAttributes = 1;
dullCopper.RunicMaxAttributes = 2;
if ( Core.ML )
{
dullCopper.RunicMinIntensity = 40;
dullCopper.RunicMaxIntensity = 100;
}
else
{
dullCopper.RunicMinIntensity = 10;
dullCopper.RunicMaxIntensity = 35;
}
CraftAttributeInfo shadowIron = ShadowIron = new CraftAttributeInfo();
shadowIron.ArmorPhysicalResist = 2;
shadowIron.ArmorFireResist = 1;
shadowIron.ArmorEnergyResist = 5;
shadowIron.ArmorDurability = 100;
shadowIron.WeaponColdDamage = 20;
shadowIron.WeaponDurability = 50;
shadowIron.RunicMinAttributes = 2;
shadowIron.RunicMaxAttributes = 2;
if ( Core.ML )
{
shadowIron.RunicMinIntensity = 45;
shadowIron.RunicMaxIntensity = 100;
}
else
{
shadowIron.RunicMinIntensity = 20;
shadowIron.RunicMaxIntensity = 45;
}
CraftAttributeInfo copper = Copper = new CraftAttributeInfo();
copper.ArmorPhysicalResist = 1;
copper.ArmorFireResist = 1;
copper.ArmorPoisonResist = 5;
copper.ArmorEnergyResist = 2;
copper.WeaponPoisonDamage = 10;
copper.WeaponEnergyDamage = 20;
copper.RunicMinAttributes = 2;
copper.RunicMaxAttributes = 3;
if ( Core.ML )
{
copper.RunicMinIntensity = 50;
copper.RunicMaxIntensity = 100;
}
else
{
copper.RunicMinIntensity = 25;
copper.RunicMaxIntensity = 50;
}
CraftAttributeInfo bronze = Bronze = new CraftAttributeInfo();
bronze.ArmorPhysicalResist = 3;
bronze.ArmorColdResist = 5;
bronze.ArmorPoisonResist = 1;
bronze.ArmorEnergyResist = 1;
bronze.WeaponFireDamage = 40;
bronze.RunicMinAttributes = 3;
bronze.RunicMaxAttributes = 3;
if ( Core.ML )
{
bronze.RunicMinIntensity = 55;
bronze.RunicMaxIntensity = 100;
}
else
{
bronze.RunicMinIntensity = 30;
bronze.RunicMaxIntensity = 65;
}
CraftAttributeInfo golden = Golden = new CraftAttributeInfo();
golden.ArmorPhysicalResist = 1;
golden.ArmorFireResist = 1;
golden.ArmorColdResist = 2;
golden.ArmorEnergyResist = 2;
golden.ArmorLuck = 40;
golden.ArmorLowerRequirements = 30;
golden.WeaponLuck = 40;
golden.WeaponLowerRequirements = 50;
golden.RunicMinAttributes = 3;
golden.RunicMaxAttributes = 4;
if ( Core.ML )
{
golden.RunicMinIntensity = 60;
golden.RunicMaxIntensity = 100;
}
else
{
golden.RunicMinIntensity = 35;
golden.RunicMaxIntensity = 75;
}
CraftAttributeInfo agapite = Agapite = new CraftAttributeInfo();
agapite.ArmorPhysicalResist = 2;
agapite.ArmorFireResist = 3;
agapite.ArmorColdResist = 2;
agapite.ArmorPoisonResist = 2;
agapite.ArmorEnergyResist = 2;
agapite.WeaponColdDamage = 30;
agapite.WeaponEnergyDamage = 20;
agapite.RunicMinAttributes = 4;
agapite.RunicMaxAttributes = 4;
if ( Core.ML )
{
agapite.RunicMinIntensity = 65;
agapite.RunicMaxIntensity = 100;
}
else
{
agapite.RunicMinIntensity = 40;
agapite.RunicMaxIntensity = 80;
}
CraftAttributeInfo verite = Verite = new CraftAttributeInfo();
verite.ArmorPhysicalResist = 3;
verite.ArmorFireResist = 3;
verite.ArmorColdResist = 2;
verite.ArmorPoisonResist = 3;
verite.ArmorEnergyResist = 1;
verite.WeaponPoisonDamage = 40;
verite.WeaponEnergyDamage = 20;
verite.RunicMinAttributes = 4;
verite.RunicMaxAttributes = 5;
if ( Core.ML )
{
verite.RunicMinIntensity = 70;
verite.RunicMaxIntensity = 100;
}
else
{
verite.RunicMinIntensity = 45;
verite.RunicMaxIntensity = 90;
}
CraftAttributeInfo valorite = Valorite = new CraftAttributeInfo();
valorite.ArmorPhysicalResist = 4;
valorite.ArmorColdResist = 3;
valorite.ArmorPoisonResist = 3;
valorite.ArmorEnergyResist = 3;
valorite.ArmorDurability = 50;
valorite.WeaponFireDamage = 10;
valorite.WeaponColdDamage = 20;
valorite.WeaponPoisonDamage = 10;
valorite.WeaponEnergyDamage = 20;
valorite.RunicMinAttributes = 5;
valorite.RunicMaxAttributes = 5;
if ( Core.ML )
{
valorite.RunicMinIntensity = 85;
valorite.RunicMaxIntensity = 100;
}
else
{
valorite.RunicMinIntensity = 50;
valorite.RunicMaxIntensity = 100;
}
CraftAttributeInfo spined = Spined = new CraftAttributeInfo();
spined.ArmorPhysicalResist = 5;
spined.ArmorLuck = 40;
spined.RunicMinAttributes = 1;
spined.RunicMaxAttributes = 3;
if ( Core.ML )
{
spined.RunicMinIntensity = 40;
spined.RunicMaxIntensity = 100;
}
else
{
spined.RunicMinIntensity = 20;
spined.RunicMaxIntensity = 40;
}
CraftAttributeInfo horned = Horned = new CraftAttributeInfo();
horned.ArmorPhysicalResist = 2;
horned.ArmorFireResist = 3;
horned.ArmorColdResist = 2;
horned.ArmorPoisonResist = 2;
horned.ArmorEnergyResist = 2;
horned.RunicMinAttributes = 3;
horned.RunicMaxAttributes = 4;
if ( Core.ML )
{
horned.RunicMinIntensity = 45;
horned.RunicMaxIntensity = 100;
}
else
{
horned.RunicMinIntensity = 30;
horned.RunicMaxIntensity = 70;
}
CraftAttributeInfo barbed = Barbed = new CraftAttributeInfo();
barbed.ArmorPhysicalResist = 2;
barbed.ArmorFireResist = 1;
barbed.ArmorColdResist = 2;
barbed.ArmorPoisonResist = 3;
barbed.ArmorEnergyResist = 4;
barbed.RunicMinAttributes = 4;
barbed.RunicMaxAttributes = 5;
if ( Core.ML )
{
barbed.RunicMinIntensity = 50;
barbed.RunicMaxIntensity = 100;
}
else
{
barbed.RunicMinIntensity = 40;
barbed.RunicMaxIntensity = 100;
}
CraftAttributeInfo red = RedScales = new CraftAttributeInfo();
red.ArmorFireResist = 10;
red.ArmorColdResist = -3;
CraftAttributeInfo yellow = YellowScales = new CraftAttributeInfo();
yellow.ArmorPhysicalResist = -3;
yellow.ArmorLuck = 20;
CraftAttributeInfo black = BlackScales = new CraftAttributeInfo();
black.ArmorPhysicalResist = 10;
black.ArmorEnergyResist = -3;
CraftAttributeInfo green = GreenScales = new CraftAttributeInfo();
green.ArmorFireResist = -3;
green.ArmorPoisonResist = 10;
CraftAttributeInfo white = WhiteScales = new CraftAttributeInfo();
white.ArmorPhysicalResist = -3;
white.ArmorColdResist = 10;
CraftAttributeInfo blue = BlueScales = new CraftAttributeInfo();
blue.ArmorPoisonResist = -3;
blue.ArmorEnergyResist = 10;
//public static readonly CraftAttributeInfo OakWood, AshWood, YewWood, Heartwood, Bloodwood, Frostwood;
CraftAttributeInfo oak = OakWood = new CraftAttributeInfo();
CraftAttributeInfo ash = AshWood = new CraftAttributeInfo();
CraftAttributeInfo yew = YewWood = new CraftAttributeInfo();
CraftAttributeInfo heart = Heartwood = new CraftAttributeInfo();
CraftAttributeInfo blood = Bloodwood = new CraftAttributeInfo();
CraftAttributeInfo frost = Frostwood = new CraftAttributeInfo();
}
}
public class CraftResourceInfo
{
public int Hue { get; }
public int Number { get; }
public string Name { get; }
public CraftAttributeInfo AttributeInfo { get; }
public CraftResource Resource { get; }
public Type[] ResourceTypes { get; }
public CraftResourceInfo( int hue, int number, string name, CraftAttributeInfo attributeInfo, CraftResource resource, params Type[] resourceTypes )
{
Hue = hue;
Number = number;
Name = name;
AttributeInfo = attributeInfo;
Resource = resource;
ResourceTypes = resourceTypes;
for ( int i = 0; i < resourceTypes.Length; ++i )
CraftResources.RegisterType( resourceTypes[i], resource );
}
}
public class CraftResources
{
private static CraftResourceInfo[] m_MetalInfo = {
new CraftResourceInfo( 0x000, 1053109, "Iron", CraftAttributeInfo.Blank, CraftResource.Iron, typeof( IronIngot ), typeof( IronOre ), typeof( Granite ) ),
new CraftResourceInfo( 0x973, 1053108, "Dull Copper", CraftAttributeInfo.DullCopper, CraftResource.DullCopper, typeof( DullCopperIngot ), typeof( DullCopperOre ), typeof( DullCopperGranite ) ),
new CraftResourceInfo( 0x966, 1053107, "Shadow Iron", CraftAttributeInfo.ShadowIron, CraftResource.ShadowIron, typeof( ShadowIronIngot ), typeof( ShadowIronOre ), typeof( ShadowIronGranite ) ),
new CraftResourceInfo( 0x96D, 1053106, "Copper", CraftAttributeInfo.Copper, CraftResource.Copper, typeof( CopperIngot ), typeof( CopperOre ), typeof( CopperGranite ) ),
new CraftResourceInfo( 0x972, 1053105, "Bronze", CraftAttributeInfo.Bronze, CraftResource.Bronze, typeof( BronzeIngot ), typeof( BronzeOre ), typeof( BronzeGranite ) ),
new CraftResourceInfo( 0x8A5, 1053104, "Gold", CraftAttributeInfo.Golden, CraftResource.Gold, typeof( GoldIngot ), typeof( GoldOre ), typeof( GoldGranite ) ),
new CraftResourceInfo( 0x979, 1053103, "Agapite", CraftAttributeInfo.Agapite, CraftResource.Agapite, typeof( AgapiteIngot ), typeof( AgapiteOre ), typeof( AgapiteGranite ) ),
new CraftResourceInfo( 0x89F, 1053102, "Verite", CraftAttributeInfo.Verite, CraftResource.Verite, typeof( VeriteIngot ), typeof( VeriteOre ), typeof( VeriteGranite ) ),
new CraftResourceInfo( 0x8AB, 1053101, "Valorite", CraftAttributeInfo.Valorite, CraftResource.Valorite, typeof( ValoriteIngot ), typeof( ValoriteOre ), typeof( ValoriteGranite ) )
};
private static CraftResourceInfo[] m_ScaleInfo = {
new CraftResourceInfo( 0x66D, 1053129, "Red Scales", CraftAttributeInfo.RedScales, CraftResource.RedScales, typeof( RedScales ) ),
new CraftResourceInfo( 0x8A8, 1053130, "Yellow Scales", CraftAttributeInfo.YellowScales, CraftResource.YellowScales, typeof( YellowScales ) ),
new CraftResourceInfo( 0x455, 1053131, "Black Scales", CraftAttributeInfo.BlackScales, CraftResource.BlackScales, typeof( BlackScales ) ),
new CraftResourceInfo( 0x851, 1053132, "Green Scales", CraftAttributeInfo.GreenScales, CraftResource.GreenScales, typeof( GreenScales ) ),
new CraftResourceInfo( 0x8FD, 1053133, "White Scales", CraftAttributeInfo.WhiteScales, CraftResource.WhiteScales, typeof( WhiteScales ) ),
new CraftResourceInfo( 0x8B0, 1053134, "Blue Scales", CraftAttributeInfo.BlueScales, CraftResource.BlueScales, typeof( BlueScales ) )
};
private static CraftResourceInfo[] m_LeatherInfo = {
new CraftResourceInfo( 0x000, 1049353, "Normal", CraftAttributeInfo.Blank, CraftResource.RegularLeather, typeof( Leather ), typeof( Hides ) ),
new CraftResourceInfo( 0x283, 1049354, "Spined", CraftAttributeInfo.Spined, CraftResource.SpinedLeather, typeof( SpinedLeather ), typeof( SpinedHides ) ),
new CraftResourceInfo( 0x227, 1049355, "Horned", CraftAttributeInfo.Horned, CraftResource.HornedLeather, typeof( HornedLeather ), typeof( HornedHides ) ),
new CraftResourceInfo( 0x1C1, 1049356, "Barbed", CraftAttributeInfo.Barbed, CraftResource.BarbedLeather, typeof( BarbedLeather ), typeof( BarbedHides ) )
};
private static CraftResourceInfo[] m_AOSLeatherInfo = {
new CraftResourceInfo( 0x000, 1049353, "Normal", CraftAttributeInfo.Blank, CraftResource.RegularLeather, typeof( Leather ), typeof( Hides ) ),
new CraftResourceInfo( 0x8AC, 1049354, "Spined", CraftAttributeInfo.Spined, CraftResource.SpinedLeather, typeof( SpinedLeather ), typeof( SpinedHides ) ),
new CraftResourceInfo( 0x845, 1049355, "Horned", CraftAttributeInfo.Horned, CraftResource.HornedLeather, typeof( HornedLeather ), typeof( HornedHides ) ),
new CraftResourceInfo( 0x851, 1049356, "Barbed", CraftAttributeInfo.Barbed, CraftResource.BarbedLeather, typeof( BarbedLeather ), typeof( BarbedHides ) )
};
private static CraftResourceInfo[] m_WoodInfo = {
new CraftResourceInfo( 0x000, 1011542, "Normal", CraftAttributeInfo.Blank, CraftResource.RegularWood, typeof( Log ), typeof( Board ) ),
new CraftResourceInfo( 0x7DA, 1072533, "Oak", CraftAttributeInfo.OakWood, CraftResource.OakWood, typeof( OakLog ), typeof( OakBoard ) ),
new CraftResourceInfo( 0x4A7, 1072534, "Ash", CraftAttributeInfo.AshWood, CraftResource.AshWood, typeof( AshLog ), typeof( AshBoard ) ),
new CraftResourceInfo( 0x4A8, 1072535, "Yew", CraftAttributeInfo.YewWood, CraftResource.YewWood, typeof( YewLog ), typeof( YewBoard ) ),
new CraftResourceInfo( 0x4A9, 1072536, "Heartwood", CraftAttributeInfo.Heartwood, CraftResource.Heartwood, typeof( HeartwoodLog ), typeof( HeartwoodBoard ) ),
new CraftResourceInfo( 0x4AA, 1072538, "Bloodwood", CraftAttributeInfo.Bloodwood, CraftResource.Bloodwood, typeof( BloodwoodLog ), typeof( BloodwoodBoard ) ),
new CraftResourceInfo( 0x47F, 1072539, "Frostwood", CraftAttributeInfo.Frostwood, CraftResource.Frostwood, typeof( FrostwoodLog ), typeof( FrostwoodBoard ) )
};
/// <summary>
/// Returns true if '<paramref name="resource"/>' is None, Iron, RegularLeather or RegularWood. False if otherwise.
/// </summary>
public static bool IsStandard( CraftResource resource )
{
return ( resource == CraftResource.None || resource == CraftResource.Iron || resource == CraftResource.RegularLeather || resource == CraftResource.RegularWood );
}
private static Dictionary<Type, CraftResource> m_TypeTable;
/// <summary>
/// Registers that '<paramref name="resourceType"/>' uses '<paramref name="resource"/>' so that it can later be queried by <see cref="CraftResources.GetFromType"/>
/// </summary>
public static void RegisterType( Type resourceType, CraftResource resource )
{
if ( m_TypeTable == null )
m_TypeTable = new Dictionary<Type, CraftResource>();
m_TypeTable[resourceType] = resource;
}
/// <summary>
/// Returns the <see cref="CraftResource"/> value for which '<paramref name="resourceType"/>' uses -or- CraftResource.None if an unregistered type was specified.
/// </summary>
public static CraftResource GetFromType( Type resourceType )
{
if ( m_TypeTable == null )
return CraftResource.None;
return m_TypeTable.TryGetValue(resourceType, out CraftResource res) ? res : CraftResource.None;
}
/// <summary>
/// Returns a <see cref="CraftResourceInfo"/> instance describing '<paramref name="resource"/>' -or- null if an invalid resource was specified.
/// </summary>
public static CraftResourceInfo GetInfo( CraftResource resource )
{
CraftResourceInfo[] list = null;
switch ( GetType( resource ) )
{
case CraftResourceType.Metal: list = m_MetalInfo; break;
case CraftResourceType.Leather: list = Core.AOS ? m_AOSLeatherInfo : m_LeatherInfo; break;
case CraftResourceType.Scales: list = m_ScaleInfo; break;
case CraftResourceType.Wood: list = m_WoodInfo; break;
}
if ( list != null )
{
int index = GetIndex( resource );
if ( index >= 0 && index < list.Length )
return list[index];
}
return null;
}
/// <summary>
/// Returns a <see cref="CraftResourceType"/> value indiciating the type of '<paramref name="resource"/>'.
/// </summary>
public static CraftResourceType GetType( CraftResource resource )
{
if ( resource >= CraftResource.Iron && resource <= CraftResource.Valorite )
return CraftResourceType.Metal;
if ( resource >= CraftResource.RegularLeather && resource <= CraftResource.BarbedLeather )
return CraftResourceType.Leather;
if ( resource >= CraftResource.RedScales && resource <= CraftResource.BlueScales )
return CraftResourceType.Scales;
if ( resource >= CraftResource.RegularWood && resource <= CraftResource.Frostwood )
return CraftResourceType.Wood;
return CraftResourceType.None;
}
/// <summary>
/// Returns the first <see cref="CraftResource"/> in the series of resources for which '<paramref name="resource"/>' belongs.
/// </summary>
public static CraftResource GetStart( CraftResource resource )
{
switch ( GetType( resource ) )
{
case CraftResourceType.Metal: return CraftResource.Iron;
case CraftResourceType.Leather: return CraftResource.RegularLeather;
case CraftResourceType.Scales: return CraftResource.RedScales;
case CraftResourceType.Wood: return CraftResource.RegularWood;
}
return CraftResource.None;
}
/// <summary>
/// Returns the index of '<paramref name="resource"/>' in the seriest of resources for which it belongs.
/// </summary>
public static int GetIndex( CraftResource resource )
{
CraftResource start = GetStart( resource );
if ( start == CraftResource.None )
return 0;
return resource - start;
}
/// <summary>
/// Returns the <see cref="CraftResourceInfo.Number"/> property of '<paramref name="resource"/>' -or- 0 if an invalid resource was specified.
/// </summary>
public static int GetLocalizationNumber( CraftResource resource )
{
CraftResourceInfo info = GetInfo( resource );
return info?.Number ?? 0;
}
/// <summary>
/// Returns the <see cref="CraftResourceInfo.Hue"/> property of '<paramref name="resource"/>' -or- 0 if an invalid resource was specified.
/// </summary>
public static int GetHue( CraftResource resource )
{
CraftResourceInfo info = GetInfo( resource );
return info?.Hue ?? 0;
}
/// <summary>
/// Returns the <see cref="CraftResourceInfo.Name"/> property of '<paramref name="resource"/>' -or- an empty string if the resource specified was invalid.
/// </summary>
public static string GetName( CraftResource resource )
{
CraftResourceInfo info = GetInfo( resource );
return ( info == null ? string.Empty : info.Name );
}
/// <summary>
/// Returns the <see cref="CraftResource"/> value which represents '<paramref name="info"/>' -or- CraftResource.None if unable to convert.
/// </summary>
public static CraftResource GetFromOreInfo( OreInfo info )
{
if ( info.Name.IndexOf( "Spined" ) >= 0 )
return CraftResource.SpinedLeather;
if ( info.Name.IndexOf( "Horned" ) >= 0 )
return CraftResource.HornedLeather;
if ( info.Name.IndexOf( "Barbed" ) >= 0 )
return CraftResource.BarbedLeather;
if ( info.Name.IndexOf( "Leather" ) >= 0 )
return CraftResource.RegularLeather;
if ( info.Level == 0 )
return CraftResource.Iron;
if ( info.Level == 1 )
return CraftResource.DullCopper;
if ( info.Level == 2 )
return CraftResource.ShadowIron;
if ( info.Level == 3 )
return CraftResource.Copper;
if ( info.Level == 4 )
return CraftResource.Bronze;
if ( info.Level == 5 )
return CraftResource.Gold;
if ( info.Level == 6 )
return CraftResource.Agapite;
if ( info.Level == 7 )
return CraftResource.Verite;
if ( info.Level == 8 )
return CraftResource.Valorite;
return CraftResource.None;
}
/// <summary>
/// Returns the <see cref="CraftResource"/> value which represents '<paramref name="info"/>', using '<paramref name="material"/>' to help resolve leather OreInfo instances.
/// </summary>
public static CraftResource GetFromOreInfo( OreInfo info, ArmorMaterialType material )
{
if ( material == ArmorMaterialType.Studded || material == ArmorMaterialType.Leather || material == ArmorMaterialType.Spined ||
material == ArmorMaterialType.Horned || material == ArmorMaterialType.Barbed )
{
if ( info.Level == 0 )
return CraftResource.RegularLeather;
if ( info.Level == 1 )
return CraftResource.SpinedLeather;
if ( info.Level == 2 )
return CraftResource.HornedLeather;
if ( info.Level == 3 )
return CraftResource.BarbedLeather;
return CraftResource.None;
}
return GetFromOreInfo( info );
}
}
// NOTE: This class is only for compatability with very old RunUO versions.
// No changes to it should be required for custom resources.
public class OreInfo
{
public static readonly OreInfo Iron = new OreInfo( 0, 0x000, "Iron" );
public static readonly OreInfo DullCopper = new OreInfo( 1, 0x973, "Dull Copper" );
public static readonly OreInfo ShadowIron = new OreInfo( 2, 0x966, "Shadow Iron" );
public static readonly OreInfo Copper = new OreInfo( 3, 0x96D, "Copper" );
public static readonly OreInfo Bronze = new OreInfo( 4, 0x972, "Bronze" );
public static readonly OreInfo Gold = new OreInfo( 5, 0x8A5, "Gold" );
public static readonly OreInfo Agapite = new OreInfo( 6, 0x979, "Agapite" );
public static readonly OreInfo Verite = new OreInfo( 7, 0x89F, "Verite" );
public static readonly OreInfo Valorite = new OreInfo( 8, 0x8AB, "Valorite" );
public OreInfo( int level, int hue, string name )
{
Level = level;
Hue = hue;
Name = name;
}
public int Level { get; }
public int Hue { get; }
public string Name { get; }
}
}

View file

@ -0,0 +1,210 @@
using System;
using System.IO;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using Server.Network;
namespace Server.Misc
{
public class ServerList
{
/*
* The default setting for Address, a value of 'null', will use your local IP address. If all of your local IP addresses
* are private network addresses and AutoDetect is 'true' then RunUO will attempt to discover your public IP address
* for you automatically.
*
* If you do not plan on allowing clients outside of your LAN to connect, you can set AutoDetect to 'false' and leave
* Address set to 'null'.
*
* If your public IP address cannot be determined, you must change the value of Address to your public IP address
* manually to allow clients outside of your LAN to connect to your server. Address can be either an IP address or
* a hostname that will be resolved when RunUO starts.
*
* If you want players outside your LAN to be able to connect to your server and you are behind a router, you must also
* forward TCP port 2593 to your private IP address. The procedure for doing this varies by manufacturer but generally
* involves configuration of the router through your web browser.
*
* ServerList will direct connecting clients depending on both the address they are connecting from and the address and
* port they are connecting to. If it is determined that both ends of a connection are private IP addresses, ServerList
* will direct the client to the local private IP address. If a client is connecting to a local public IP address, they
* will be directed to whichever address and port they initially connected to. This allows multihomed servers to function
* properly and fully supports listening on multiple ports. If a client with a public IP address is connecting to a
* locally private address, the server will direct the client to either the AutoDetected IP address or the manually entered
* IP address or hostname, whichever is applicable. Loopback clients will be directed to loopback.
*
* If you would like to listen on additional ports (i.e. 22, 23, 80, for clients behind highly restrictive egress
* firewalls) or specific IP adddresses you can do so by modifying the file SocketOptions.cs found in this directory.
*/
public static readonly string Address = null;
public static readonly string ServerName = "RunUO TC";
public static readonly bool AutoDetect = true;
private static IPAddress m_PublicAddress;
public static void Initialize()
{
if (Address == null)
{
if (AutoDetect)
AutoDetection();
}
else
{
Resolve(Address, out m_PublicAddress);
}
EventSink.ServerList += EventSink_ServerList;
}
private static void EventSink_ServerList(ServerListEventArgs e)
{
try
{
NetState ns = e.State;
Socket s = ns.Socket;
IPEndPoint ipep = (IPEndPoint)s.LocalEndPoint;
IPAddress localAddress = ipep.Address;
int localPort = ipep.Port;
if (IsPrivateNetwork(localAddress))
{
ipep = (IPEndPoint)s.RemoteEndPoint;
if (!IsPrivateNetwork(ipep.Address) && m_PublicAddress != null)
localAddress = m_PublicAddress;
}
e.AddServer(ServerName, new IPEndPoint(localAddress, localPort));
}
catch (Exception er)
{
Console.WriteLine(er);
e.Rejected = true;
}
}
private static void AutoDetection()
{
if (!HasPublicIPAddress())
{
Console.Write("ServerList: Auto-detecting public IP address...");
m_PublicAddress = FindPublicAddress();
if (m_PublicAddress != null)
Console.WriteLine("done ({0})", m_PublicAddress);
else
Console.WriteLine("failed");
}
}
private static void Resolve(string addr, out IPAddress outValue)
{
if (IPAddress.TryParse(addr, out outValue))
return;
try
{
IPHostEntry iphe = Dns.GetHostEntry(addr);
if (iphe.AddressList.Length > 0)
outValue = iphe.AddressList[iphe.AddressList.Length - 1];
}
catch
{
// ignored
}
}
private static bool HasPublicIPAddress()
{
NetworkInterface[] adapters = NetworkInterface.GetAllNetworkInterfaces();
foreach (NetworkInterface adapter in adapters)
{
IPInterfaceProperties properties = adapter.GetIPProperties();
foreach (IPAddressInformation unicast in properties.UnicastAddresses)
{
IPAddress ip = unicast.Address;
if (!IPAddress.IsLoopback(ip) && ip.AddressFamily != AddressFamily.InterNetworkV6 &&
!IsPrivateNetwork(ip))
return true;
}
}
return false;
/*
IPHostEntry iphe = Dns.GetHostEntry( Dns.GetHostName() );
IPAddress[] ips = iphe.AddressList;
for ( int i = 0; i < ips.Length; ++i )
{
if ( ips[i].AddressFamily != AddressFamily.InterNetworkV6 && !IsPrivateNetwork( ips[i] ) )
return true;
}
return false;
*/
}
private static bool IsPrivateNetwork(IPAddress ip)
{
// 10.0.0.0/8
// 172.16.0.0/12
// 192.168.0.0/16
// 169.254.0.0/16
// 100.64.0.0/10 RFC 6598
if (ip.AddressFamily == AddressFamily.InterNetworkV6)
return false;
if (Utility.IPMatch("192.168.*", ip))
return true;
if (Utility.IPMatch("10.*", ip))
return true;
if (Utility.IPMatch("172.16-31.*", ip))
return true;
if (Utility.IPMatch("169.254.*", ip))
return true;
if (Utility.IPMatch("100.64-127.*", ip))
return true;
return false;
}
private static IPAddress FindPublicAddress()
{
try
{
WebRequest req = WebRequest.Create("https://api.ipify.org");
req.Timeout = 15000;
WebResponse res = req.GetResponse();
Stream s = res.GetResponseStream();
StreamReader sr = new StreamReader(s);
IPAddress ip = IPAddress.Parse(sr.ReadLine());
sr.Close();
s.Close();
res.Close();
return ip;
}
catch
{
return null;
}
}
}
}

View file

@ -0,0 +1,609 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Text.RegularExpressions;
using Server.Gumps;
using Server.Network;
using Server.Prompts;
namespace Server.Misc
{
public class ShardPoller : Item
{
private static List<ShardPoller> m_ActivePollers = new List<ShardPoller>();
private bool m_Active;
private string m_Title;
[Constructible(AccessLevel.Administrator)]
public ShardPoller() : base(0x1047)
{
Duration = TimeSpan.FromHours(24.0);
Options = new ShardPollOption[0];
Addresses = new IPAddress[0];
Movable = false;
}
public ShardPoller(Serial serial) : base(serial)
{
}
public ShardPollOption[] Options{ get; set; }
public IPAddress[] Addresses{ get; set; }
[CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)]
public string Title
{
get => m_Title;
set => m_Title = ShardPollPrompt.UrlToHref(value);
}
[CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)]
public TimeSpan Duration{ get; set; }
[CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)]
public DateTime StartTime{ get; set; }
[CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)]
public TimeSpan TimeRemaining
{
get
{
if (StartTime == DateTime.MinValue || !m_Active)
return TimeSpan.Zero;
try
{
TimeSpan ts = StartTime + Duration - DateTime.UtcNow;
if (ts < TimeSpan.Zero)
return TimeSpan.Zero;
return ts;
}
catch
{
return TimeSpan.Zero;
}
}
}
[CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)]
public bool Active
{
get => m_Active;
set
{
if (m_Active == value)
return;
m_Active = value;
if (m_Active)
{
StartTime = DateTime.UtcNow;
m_ActivePollers.Add(this);
}
else
{
m_ActivePollers.Remove(this);
}
}
}
public override string DefaultName => "shard poller";
public bool HasAlreadyVoted(NetState ns)
{
for (int i = 0; i < Options.Length; ++i)
if (Options[i].HasAlreadyVoted(ns))
return true;
return false;
}
public void AddVote(NetState ns, ShardPollOption option)
{
option.AddVote(ns);
}
public void RemoveOption(ShardPollOption option)
{
int index = Array.IndexOf(Options, option);
if (index < 0)
return;
ShardPollOption[] old = Options;
Options = new ShardPollOption[old.Length - 1];
for (int i = 0; i < index; ++i)
Options[i] = old[i];
for (int i = index; i < Options.Length; ++i)
Options[i] = old[i + 1];
}
public void AddOption(ShardPollOption option)
{
ShardPollOption[] old = Options;
Options = new ShardPollOption[old.Length + 1];
for (int i = 0; i < old.Length; ++i)
Options[i] = old[i];
Options[old.Length] = option;
}
public static void Initialize()
{
EventSink.Login += EventSink_Login;
}
private static void EventSink_Login(LoginEventArgs e)
{
if (m_ActivePollers.Count == 0)
return;
Timer.DelayCall(TimeSpan.FromSeconds(1.0), EventSink_Login_Callback, e.Mobile);
}
private static void EventSink_Login_Callback(Mobile from)
{
NetState ns = from.NetState;
if (ns == null)
return;
ShardPollGump spg = null;
for (int i = 0; i < m_ActivePollers.Count; ++i)
{
ShardPoller poller = m_ActivePollers[i];
if (poller.Deleted || !poller.Active)
continue;
if (poller.TimeRemaining > TimeSpan.Zero)
{
if (poller.HasAlreadyVoted(ns))
continue;
if (spg == null)
{
spg = new ShardPollGump(from, poller, false, null);
from.SendGump(spg);
}
else
{
spg.QueuePoll(poller);
}
}
else
{
poller.Active = false;
}
}
}
public override void OnDoubleClick(Mobile from)
{
if (from.AccessLevel >= AccessLevel.Administrator)
from.SendGump(new ShardPollGump(from, this, true, null));
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
writer.Write(m_Title);
writer.Write(Duration);
writer.Write(StartTime);
writer.Write(m_Active);
writer.Write(Options.Length);
for (int i = 0; i < Options.Length; ++i)
Options[i].Serialize(writer);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 0:
{
m_Title = reader.ReadString();
Duration = reader.ReadTimeSpan();
StartTime = reader.ReadDateTime();
m_Active = reader.ReadBool();
Options = new ShardPollOption[reader.ReadInt()];
for (int i = 0; i < Options.Length; ++i)
Options[i] = new ShardPollOption(reader);
if (m_Active)
m_ActivePollers.Add(this);
break;
}
}
}
public override void OnDelete()
{
base.OnDelete();
Active = false;
}
}
public class ShardPollOption
{
private string m_Title;
public ShardPollOption(string title)
{
m_Title = title;
LineBreaks = GetBreaks(m_Title);
Voters = new IPAddress[0];
}
public ShardPollOption(GenericReader reader)
{
int version = reader.ReadInt();
switch (version)
{
case 0:
{
m_Title = reader.ReadString();
LineBreaks = GetBreaks(m_Title);
Voters = new IPAddress[reader.ReadInt()];
for (int i = 0; i < Voters.Length; ++i)
Voters[i] = Utility.Intern(reader.ReadIPAddress());
break;
}
}
}
public string Title
{
get => m_Title;
set
{
m_Title = value;
LineBreaks = GetBreaks(m_Title);
}
}
public int LineBreaks{ get; private set; }
public int Votes => Voters.Length;
public IPAddress[] Voters{ get; set; }
public bool HasAlreadyVoted(NetState ns)
{
if (ns == null)
return false;
IPAddress ipAddress = ns.Address;
for (int i = 0; i < Voters.Length; ++i)
if (Utility.IPMatchClassC(Voters[i], ipAddress))
return true;
return false;
}
public void AddVote(NetState ns)
{
if (ns == null)
return;
IPAddress[] old = Voters;
Voters = new IPAddress[old.Length + 1];
for (int i = 0; i < old.Length; ++i)
Voters[i] = old[i];
Voters[old.Length] = ns.Address;
}
public int ComputeHeight()
{
int height = LineBreaks * 18;
if (height > 30)
return height;
return 30;
}
public int GetBreaks(string title)
{
if (title == null)
return 1;
int count = 0;
int index = -1;
do
{
++count;
index = title.IndexOf("<br>", index + 1);
} while (index >= 0);
return count;
}
public void Serialize(GenericWriter writer)
{
writer.Write(0); // version
writer.Write(m_Title);
writer.Write(Voters.Length);
for (int i = 0; i < Voters.Length; ++i)
writer.Write(Voters[i]);
}
}
public class ShardPollGump : Gump
{
private const int LabelColor32 = 0xFFFFFF;
private Mobile m_From;
private ShardPoller m_Poller;
private Queue<ShardPoller> m_Polls;
public ShardPollGump(Mobile from, ShardPoller poller, bool editing, Queue<ShardPoller> polls) : base(50, 50)
{
m_From = from;
m_Poller = poller;
Editing = editing;
m_Polls = polls;
Closable = false;
AddPage(0);
int totalVotes = 0;
int totalOptionHeight = 0;
for (int i = 0; i < poller.Options.Length; ++i)
{
totalVotes += poller.Options[i].Votes;
totalOptionHeight += poller.Options[i].ComputeHeight() + 5;
}
bool isViewingResults = editing && poller.Active;
bool isCompleted = totalVotes > 0 && !poller.Active;
if (editing && !isViewingResults)
totalOptionHeight += 35;
int height = 115 + totalOptionHeight;
AddBackground(1, 1, 398, height - 2, 3600);
AddAlphaRegion(16, 15, 369, height - 31);
AddItem(308, 30, 0x1E5E);
string title;
if (editing)
title = isCompleted ? "Poll Completed" : "Poll Editor";
else
title = "Shard Poll";
AddHtml(22, 22, 294, 20, Color(Center(title), LabelColor32));
if (editing)
{
AddHtml(22, 22, 294, 20, Color($"{totalVotes} total", LabelColor32));
AddButton(287, 23, 0x2622, 0x2623, 2);
}
AddHtml(22, 50, 294, 40, Color(poller.Title, 0x99CC66));
AddImageTiled(32, 88, 264, 1, 9107);
AddImageTiled(42, 90, 264, 1, 9157);
int y = 100;
for (int i = 0; i < poller.Options.Length; ++i)
{
ShardPollOption option = poller.Options[i];
string text = option.Title;
if (editing && totalVotes > 0)
{
double perc = option.Votes / (double)totalVotes;
text = $"[{option.Votes}: {(int)(perc * 100)}%] {text}";
}
int optHeight = option.ComputeHeight();
y += optHeight / 2;
if (isViewingResults)
AddImage(24, y - 15, 0x25FE);
else
AddRadio(24, y - 15, 0x25F9, 0x25FC, false, 1 + i);
AddHtml(60, y - 9 * option.LineBreaks, 250, 18 * option.LineBreaks, Color(text, LabelColor32));
y += optHeight / 2;
y += 5;
}
if (editing && !isViewingResults)
{
AddRadio(24, y + 15 - 15, 0x25F9, 0x25FC, false, 1 + poller.Options.Length);
AddHtml(60, y + 15 - 9, 250, 18, Color("Create new option.", 0x99CC66));
}
AddButton(314, height - 73, 247, 248, 1);
AddButton(314, height - 47, 242, 241, 0);
}
public bool Editing{ get; }
public void QueuePoll(ShardPoller poller)
{
if (m_Polls == null)
m_Polls = new Queue<ShardPoller>(4);
m_Polls.Enqueue(poller);
}
public string Center(string text)
{
return $"<CENTER>{text}</CENTER>";
}
public string Color(string text, int color)
{
return $"<BASEFONT COLOR=#{color:X6}>{text}</BASEFONT>";
}
public override void OnResponse(NetState sender, RelayInfo info)
{
if (m_Polls?.Count > 0)
{
ShardPoller poller = m_Polls.Dequeue();
if (poller != null)
Timer.DelayCall(TimeSpan.FromSeconds(1.0),
() => m_From.SendGump(new ShardPollGump(m_From, poller, false, m_Polls)));
}
if (info.ButtonID == 1)
{
int[] switches = info.Switches;
if (switches.Length == 0)
return;
int switched = switches[0] - 1;
ShardPollOption opt = null;
if (switched >= 0 && switched < m_Poller.Options.Length)
opt = m_Poller.Options[switched];
if (opt == null && !Editing)
return;
if (Editing)
{
if (!m_Poller.Active)
{
m_From.SendMessage("Enter a title for the option. Escape to cancel.{0}",
opt == null ? "" : " Use \"DEL\" to delete.");
m_From.Prompt = new ShardPollPrompt(m_Poller, opt);
}
else
{
m_From.SendMessage("You may not edit an active poll. Deactivate it first.");
m_From.SendGump(new ShardPollGump(m_From, m_Poller, Editing, m_Polls));
}
}
else
{
if (!m_Poller.Active)
m_From.SendMessage("The poll has been deactivated.");
else if (m_Poller.HasAlreadyVoted(sender))
m_From.SendMessage("You have already voted on this poll.");
else
m_Poller.AddVote(sender, opt);
}
}
else if (info.ButtonID == 2 && Editing)
{
m_From.SendGump(new ShardPollGump(m_From, m_Poller, Editing, m_Polls));
m_From.SendGump(new PropertiesGump(m_From, m_Poller));
}
}
}
public class ShardPollPrompt : Prompt
{
private static Regex m_UrlRegex =
new Regex(@"\[url(?:=(.*?))?\](.*?)\[/url\]", RegexOptions.IgnoreCase | RegexOptions.Compiled);
private ShardPollOption m_Option;
private ShardPoller m_Poller;
public ShardPollPrompt(ShardPoller poller, ShardPollOption opt)
{
m_Poller = poller;
m_Option = opt;
}
public override void OnCancel(Mobile from)
{
from.SendGump(new ShardPollGump(from, m_Poller, true, null));
}
private static string UrlRegex_Match(Match m)
{
if (m.Groups[1].Success)
{
if (m.Groups[2].Success)
return $"<a href=\"{m.Groups[1].Value}\">{m.Groups[2].Value}</a>";
}
else if (m.Groups[2].Success)
{
return $"<a href=\"{m.Groups[2].Value}\">{m.Groups[2].Value}</a>";
}
return m.Value;
}
public static string UrlToHref(string text)
{
if (text == null)
return null;
return m_UrlRegex.Replace(text, UrlRegex_Match);
}
public override void OnResponse(Mobile from, string text)
{
if (m_Poller.Active)
{
from.SendMessage("You may not edit an active poll. Deactivate it first.");
}
else if (text == "DEL")
{
if (m_Option != null)
m_Poller.RemoveOption(m_Option);
}
else
{
text = UrlToHref(text);
if (m_Option == null)
m_Poller.AddOption(new ShardPollOption(text));
else
m_Option.Title = text;
}
from.SendGump(new ShardPollGump(from, m_Poller, true, null));
}
}
}

View file

@ -0,0 +1,86 @@
using System.IO;
namespace Server
{
public class ShrinkTable
{
public const int DefaultItemID = 0x1870; // Yellow virtue stone
private static int[] m_Table;
public static int Lookup(Mobile m)
{
return Lookup(m.Body.BodyID, DefaultItemID);
}
public static int Lookup(int body)
{
return Lookup(body, DefaultItemID);
}
public static int Lookup(Mobile m, int defaultValue)
{
return Lookup(m.Body.BodyID, defaultValue);
}
public static int Lookup(int body, int defaultValue)
{
if (m_Table == null)
Load();
int val = 0;
if (body >= 0 && body < m_Table.Length)
val = m_Table[body];
if (val == 0)
val = defaultValue;
return val;
}
private static void Load()
{
string path = Path.Combine(Core.BaseDirectory, "Data/shrink.cfg");
if (!File.Exists(path))
{
m_Table = new int[0];
return;
}
m_Table = new int[1000];
using (StreamReader ip = new StreamReader(path))
{
string line;
while ((line = ip.ReadLine()) != null)
{
line = line.Trim();
if (line.Length == 0 || line.StartsWith("#"))
continue;
try
{
string[] split = line.Split('\t');
if (split.Length >= 2)
{
int body = Utility.ToInt32(split[0]);
int item = Utility.ToInt32(split[1]);
if (body >= 0 && body < m_Table.Length)
m_Table[body] = item;
}
}
catch
{
// ignored
}
}
}
}
}
}

View file

@ -0,0 +1,402 @@
using System;
using Server.Factions;
using Server.Mobiles;
using Server.Regions;
namespace Server.Misc
{
public class SkillCheck
{
public enum Stat
{
Str,
Dex,
Int
}
public const int Allowance = 3; //How many times may we use the same location/target for gain
private const int
LocationSize = 5; //The size of eeach location, make this smaller so players dont have to move as far
private static readonly bool AntiMacroCode = !Core.ML; //Change this to false to disable anti-macro code
public static TimeSpan AntiMacroExpire = TimeSpan.FromMinutes(5.0); //How long do we remember targets/locations?
private static bool[] UseAntiMacro =
{
// true if this skill uses the anti-macro code, false if it does not
false, // Alchemy = 0,
true, // Anatomy = 1,
true, // AnimalLore = 2,
true, // ItemID = 3,
true, // ArmsLore = 4,
false, // Parry = 5,
true, // Begging = 6,
false, // Blacksmith = 7,
false, // Fletching = 8,
true, // Peacemaking = 9,
true, // Camping = 10,
false, // Carpentry = 11,
false, // Cartography = 12,
false, // Cooking = 13,
true, // DetectHidden = 14,
true, // Discordance = 15,
true, // EvalInt = 16,
true, // Healing = 17,
true, // Fishing = 18,
true, // Forensics = 19,
true, // Herding = 20,
true, // Hiding = 21,
true, // Provocation = 22,
false, // Inscribe = 23,
true, // Lockpicking = 24,
true, // Magery = 25,
true, // MagicResist = 26,
false, // Tactics = 27,
true, // Snooping = 28,
true, // Musicianship = 29,
true, // Poisoning = 30,
false, // Archery = 31,
true, // SpiritSpeak = 32,
true, // Stealing = 33,
false, // Tailoring = 34,
true, // AnimalTaming = 35,
true, // TasteID = 36,
false, // Tinkering = 37,
true, // Tracking = 38,
true, // Veterinary = 39,
false, // Swords = 40,
false, // Macing = 41,
false, // Fencing = 42,
false, // Wrestling = 43,
true, // Lumberjacking = 44,
true, // Mining = 45,
true, // Meditation = 46,
true, // Stealth = 47,
true, // RemoveTrap = 48,
true, // Necromancy = 49,
false, // Focus = 50,
true, // Chivalry = 51
true, // Bushido = 52
true, //Ninjitsu = 53
true // Spellweaving
};
private static TimeSpan m_StatGainDelay = TimeSpan.FromMinutes(Core.ML ? 0.05 : 15);
private static TimeSpan m_PetStatGainDelay = TimeSpan.FromMinutes(5.0);
public static void Initialize()
{
Mobile.SkillCheckLocationHandler = Mobile_SkillCheckLocation;
Mobile.SkillCheckDirectLocationHandler = Mobile_SkillCheckDirectLocation;
Mobile.SkillCheckTargetHandler = Mobile_SkillCheckTarget;
Mobile.SkillCheckDirectTargetHandler = Mobile_SkillCheckDirectTarget;
}
public static bool Mobile_SkillCheckLocation(Mobile from, SkillName skillName, double minSkill, double maxSkill)
{
Skill skill = from.Skills[skillName];
if (skill == null)
return false;
double value = skill.Value;
if (value < minSkill)
return false; // Too difficult
if (value >= maxSkill)
return true; // No challenge
double chance = (value - minSkill) / (maxSkill - minSkill);
Point2D loc = new Point2D(from.Location.X / LocationSize, from.Location.Y / LocationSize);
return CheckSkill(from, skill, loc, chance);
}
public static bool Mobile_SkillCheckDirectLocation(Mobile from, SkillName skillName, double chance)
{
Skill skill = from.Skills[skillName];
if (skill == null)
return false;
if (chance < 0.0)
return false; // Too difficult
if (chance >= 1.0)
return true; // No challenge
Point2D loc = new Point2D(from.Location.X / LocationSize, from.Location.Y / LocationSize);
return CheckSkill(from, skill, loc, chance);
}
public static bool CheckSkill(Mobile from, Skill skill, object amObj, double chance)
{
if (from.Skills.Cap == 0)
return false;
bool success = chance >= Utility.RandomDouble();
double gc = (double)(from.Skills.Cap - from.Skills.Total) / from.Skills.Cap;
gc += (skill.Cap - skill.Base) / skill.Cap;
gc /= 2;
gc += (1.0 - chance) * (success ? 0.5 : Core.AOS ? 0.0 : 0.2);
gc /= 2;
gc *= skill.Info.GainFactor;
if (gc < 0.01)
gc = 0.01;
if (from is BaseCreature creature && creature.Controlled)
gc *= 2;
if (from.Alive && (gc >= Utility.RandomDouble() && AllowGain(from, skill, amObj) || skill.Base < 10.0))
Gain(from, skill);
return success;
}
public static bool Mobile_SkillCheckTarget(Mobile from, SkillName skillName, object target, double minSkill,
double maxSkill)
{
Skill skill = from.Skills[skillName];
if (skill == null)
return false;
double value = skill.Value;
if (value < minSkill)
return false; // Too difficult
if (value >= maxSkill)
return true; // No challenge
double chance = (value - minSkill) / (maxSkill - minSkill);
return CheckSkill(from, skill, target, chance);
}
public static bool Mobile_SkillCheckDirectTarget(Mobile from, SkillName skillName, object target, double chance)
{
Skill skill = from.Skills[skillName];
if (skill == null)
return false;
if (chance < 0.0)
return false; // Too difficult
if (chance >= 1.0)
return true; // No challenge
return CheckSkill(from, skill, target, chance);
}
private static bool AllowGain(Mobile from, Skill skill, object obj)
{
if (Core.AOS && Faction.InSkillLoss(from)) //Changed some time between the introduction of AoS and SE.
return false;
if (AntiMacroCode && from is PlayerMobile mobile && UseAntiMacro[skill.Info.SkillID])
return mobile.AntiMacroCheck(skill, obj);
return true;
}
public static void Gain(Mobile from, Skill skill)
{
if (from.Region.IsPartOf<Jail>())
return;
if (from is BaseCreature creature && creature.IsDeadPet)
return;
if (skill.SkillName == SkillName.Focus && from is BaseCreature)
return;
if (skill.Base < skill.Cap && skill.Lock == SkillLock.Up)
{
int toGain = 1;
if (skill.Base <= 10.0)
toGain = Utility.Random(4) + 1;
Skills skills = from.Skills;
if (from.Player && skills.Total / skills.Cap >= Utility.RandomDouble()) //( skills.Total >= skills.Cap )
for (int i = 0; i < skills.Length; ++i)
{
Skill toLower = skills[i];
if (toLower != skill && toLower.Lock == SkillLock.Down && toLower.BaseFixedPoint >= toGain)
{
toLower.BaseFixedPoint -= toGain;
break;
}
}
#region Scroll of Alacrity
if (from is PlayerMobile pm && skill.SkillName == pm.AcceleratedSkill &&
pm.AcceleratedStart > DateTime.UtcNow)
toGain *= Utility.RandomMinMax(2, 5);
#endregion
if (!from.Player || skills.Total + toGain <= skills.Cap) skill.BaseFixedPoint += toGain;
}
if (skill.Lock == SkillLock.Up)
{
SkillInfo info = skill.Info;
if (from.StrLock == StatLockType.Up && info.StrGain / 33.3 > Utility.RandomDouble())
GainStat(from, Stat.Str);
else if (from.DexLock == StatLockType.Up && info.DexGain / 33.3 > Utility.RandomDouble())
GainStat(from, Stat.Dex);
else if (from.IntLock == StatLockType.Up && info.IntGain / 33.3 > Utility.RandomDouble())
GainStat(from, Stat.Int);
}
}
public static bool CanLower(Mobile from, Stat stat)
{
switch (stat)
{
case Stat.Str: return from.StrLock == StatLockType.Down && from.RawStr > 10;
case Stat.Dex: return from.DexLock == StatLockType.Down && from.RawDex > 10;
case Stat.Int: return from.IntLock == StatLockType.Down && from.RawInt > 10;
}
return false;
}
public static bool CanRaise(Mobile from, Stat stat)
{
if (!(from is BaseCreature creature && creature.Controlled))
if (from.RawStatTotal >= from.StatCap)
return false;
switch (stat)
{
case Stat.Str: return from.StrLock == StatLockType.Up && from.RawStr < 125;
case Stat.Dex: return from.DexLock == StatLockType.Up && from.RawDex < 125;
case Stat.Int: return from.IntLock == StatLockType.Up && from.RawInt < 125;
}
return false;
}
public static void IncreaseStat(Mobile from, Stat stat, bool atrophy)
{
atrophy = atrophy || from.RawStatTotal >= from.StatCap;
switch (stat)
{
case Stat.Str:
{
if (atrophy)
{
if (CanLower(from, Stat.Dex) && (from.RawDex < from.RawInt || !CanLower(from, Stat.Int)))
--from.RawDex;
else if (CanLower(from, Stat.Int))
--from.RawInt;
}
if (CanRaise(from, Stat.Str))
++from.RawStr;
break;
}
case Stat.Dex:
{
if (atrophy)
{
if (CanLower(from, Stat.Str) && (from.RawStr < from.RawInt || !CanLower(from, Stat.Int)))
--from.RawStr;
else if (CanLower(from, Stat.Int))
--from.RawInt;
}
if (CanRaise(from, Stat.Dex))
++from.RawDex;
break;
}
case Stat.Int:
{
if (atrophy)
{
if (CanLower(from, Stat.Str) && (from.RawStr < from.RawDex || !CanLower(from, Stat.Dex)))
--from.RawStr;
else if (CanLower(from, Stat.Dex))
--from.RawDex;
}
if (CanRaise(from, Stat.Int))
++from.RawInt;
break;
}
}
}
public static void GainStat(Mobile from, Stat stat)
{
switch (stat)
{
case Stat.Str:
{
if (from is BaseCreature creature && creature.Controlled)
{
if (creature.LastStrGain + m_PetStatGainDelay >= DateTime.UtcNow)
return;
}
else if (from.LastStrGain + m_StatGainDelay >= DateTime.UtcNow)
{
return;
}
from.LastStrGain = DateTime.UtcNow;
break;
}
case Stat.Dex:
{
if (from is BaseCreature creature && creature.Controlled)
{
if (creature.LastDexGain + m_PetStatGainDelay >= DateTime.UtcNow)
return;
}
else if (from.LastDexGain + m_StatGainDelay >= DateTime.UtcNow)
{
return;
}
from.LastDexGain = DateTime.UtcNow;
break;
}
case Stat.Int:
{
if (from is BaseCreature creature && creature.Controlled)
{
if (creature.LastIntGain + m_PetStatGainDelay >= DateTime.UtcNow)
return;
}
else if (from.LastIntGain + m_StatGainDelay >= DateTime.UtcNow)
{
return;
}
from.LastIntGain = DateTime.UtcNow;
break;
}
}
bool atrophy = from.RawStatTotal / (double)from.StatCap >= Utility.RandomDouble();
IncreaseStat(from, stat, atrophy);
}
}
}

View file

@ -0,0 +1,41 @@
using System;
using System.Net;
using System.Net.Sockets;
using Server.Network;
namespace Server
{
public class SocketOptions
{
private const bool NagleEnabled = false; // Should the Nagle algorithm be enabled? This may reduce performance
private static IPEndPoint[] m_ListenerEndPoints =
{
new IPEndPoint(IPAddress.Any, 2593) // Default: Listen on port 2593 on all IP addresses
// Examples:
// new IPEndPoint( IPAddress.Any, 80 ), // Listen on port 80 on all IP addresses
// new IPEndPoint( IPAddress.Parse( "1.2.3.4" ), 2593 ), // Listen on port 2593 on IP address 1.2.3.4
};
public static void Initialize()
{
EventSink.SocketConnect += EventSink_SocketConnect;
}
public static void RegisterListeners()
{
for (int i = 0; i < m_ListenerEndPoints.Length; i++)
Core.MessagePump.AddListener(m_ListenerEndPoints[i]);
}
private static void EventSink_SocketConnect(SocketConnectEventArgs e)
{
if (!e.AllowConnection)
return;
if (!NagleEnabled)
e.Socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, 1); // RunUO uses its own algorithm
}
}
}

View file

@ -0,0 +1,188 @@
using System.Globalization;
using Server.Gumps;
using Server.Network;
namespace Server
{
[Parsable]
public class TextDefinition
{
public TextDefinition(string text) : this(0, text)
{
}
public TextDefinition(int number = 0, string text = null)
{
Number = number;
String = text;
}
public int Number{ get; }
public string String{ get; }
public bool IsEmpty => Number <= 0 && String == null;
public override string ToString() => Number > 0 ? $"#{Number}" : String ?? "";
public string Format(bool propsGump)
{
return Number > 0 ? $"{Number} (0x{Number:X})" :
String != null ? $"\"{String}\"" : propsGump ? "-empty-" : "empty";
}
public string GetValue()
{
return Number > 0 ? Number.ToString() : String ?? "";
}
public static void Serialize(GenericWriter writer, TextDefinition def)
{
if (def == null)
{
writer.WriteEncodedInt(3);
}
else if (def.Number > 0)
{
writer.WriteEncodedInt(1);
writer.WriteEncodedInt(def.Number);
}
else if (def.String != null)
{
writer.WriteEncodedInt(2);
writer.Write(def.String);
}
else
{
writer.WriteEncodedInt(0);
}
}
public static TextDefinition Deserialize(GenericReader reader)
{
int type = reader.ReadEncodedInt();
switch (type)
{
case 0: return new TextDefinition();
case 1: return new TextDefinition(reader.ReadEncodedInt());
case 2: return new TextDefinition(reader.ReadString());
}
return null;
}
public static void AddTo(ObjectPropertyList list, TextDefinition def)
{
if (def == null)
return;
if (def.Number > 0)
list.Add(def.Number);
else if (def.String != null)
list.Add(def.String);
}
public static implicit operator TextDefinition(int v)
{
return new TextDefinition(v);
}
public static implicit operator TextDefinition(string s)
{
return new TextDefinition(s);
}
public static implicit operator int(TextDefinition m)
{
return m?.Number ?? 0;
}
public static implicit operator string(TextDefinition m)
{
return m?.String;
}
public static void AddHtmlText(Gump g, int x, int y, int width, int height, TextDefinition def, bool back,
bool scroll, int numberColor, int stringColor)
{
if (def == null)
return;
if (def.Number > 0)
{
if (numberColor >= 0) // 5 bits per RGB component (15 bit RGB)
g.AddHtmlLocalized(x, y, width, height, def.Number, numberColor, back, scroll);
else
g.AddHtmlLocalized(x, y, width, height, def.Number, back, scroll);
}
else if (def.String != null)
{
if (stringColor >= 0) // 8 bits per RGB component (24 bit RGB)
g.AddHtml(x, y, width, height, $"<BASEFONT COLOR=#{stringColor:X6}>{def.String}</BASEFONT>", back,
scroll);
else
g.AddHtml(x, y, width, height, def.String, back, scroll);
}
}
public static void AddHtmlText(Gump g, int x, int y, int width, int height, TextDefinition def, bool back,
bool scroll)
{
AddHtmlText(g, x, y, width, height, def, back, scroll, -1, -1);
}
public static void SendMessageTo(Mobile m, TextDefinition def)
{
if (def == null)
return;
if (def.Number > 0)
m.SendLocalizedMessage(def.Number);
else if (def.String != null)
m.SendMessage(def.String);
}
public static void SendMessageTo(Mobile m, TextDefinition def, int hue)
{
if (def == null)
return;
if (def.Number > 0)
m.SendLocalizedMessage(def.Number, "", hue);
else if (def.String != null)
m.SendMessage(hue, def.String);
}
public static void PublicOverheadMessage(Mobile m, MessageType messageType, int hue, TextDefinition def)
{
if (def == null)
return;
if (def.Number > 0)
m.PublicOverheadMessage(messageType, hue, def.Number);
else if (def.String != null)
m.PublicOverheadMessage(messageType, hue, false, def.String);
}
public static TextDefinition Parse(string value)
{
if (value == null)
return null;
int i;
bool isInteger;
isInteger = value.StartsWith("0x") ?
int.TryParse(value.Substring(2), NumberStyles.HexNumber, null, out i) :
int.TryParse(value, out i);
return isInteger ? new TextDefinition(i) : new TextDefinition(value);
}
public static bool IsNullOrEmpty(TextDefinition def)
{
return def == null || def.IsEmpty;
}
}
}

View file

@ -0,0 +1,406 @@
using System;
using System.Text;
using Server.Engines.CannedEvil;
using Server.Mobiles;
namespace Server.Misc
{
public class Titles
{
public const int MinFame = 0;
public const int MaxFame = 15000;
public const int MinKarma = -15000;
public const int MaxKarma = 15000;
public static string[] HarrowerTitles =
{
"Spite", "Opponent", "Hunter", "Venom", "Executioner", "Annihilator", "Champion", "Assailant", "Purifier",
"Nullifier"
};
private static string[,] m_Levels =
{
{ "Neophyte", "Neophyte", "Neophyte" },
{ "Novice", "Novice", "Novice" },
{ "Apprentice", "Apprentice", "Apprentice" },
{ "Journeyman", "Journeyman", "Journeyman" },
{ "Expert", "Expert", "Expert" },
{ "Adept", "Adept", "Adept" },
{ "Master", "Master", "Master" },
{ "Grandmaster", "Grandmaster", "Grandmaster" },
{ "Elder", "Tatsujin", "Shinobi" },
{ "Legendary", "Kengo", "Ka-ge" }
};
private static FameEntry[] m_FameEntries =
{
new FameEntry(1249, new[]
{
new KarmaEntry(-10000, "The Outcast {0}"),
new KarmaEntry(-5000, "The Despicable {0}"),
new KarmaEntry(-2500, "The Scoundrel {0}"),
new KarmaEntry(-1250, "The Unsavory {0}"),
new KarmaEntry(-625, "The Rude {0}"),
new KarmaEntry(624, "{0}"),
new KarmaEntry(1249, "The Fair {0}"),
new KarmaEntry(2499, "The Kind {0}"),
new KarmaEntry(4999, "The Good {0}"),
new KarmaEntry(9999, "The Honest {0}"),
new KarmaEntry(10000, "The Trustworthy {0}")
}),
new FameEntry(2499, new[]
{
new KarmaEntry(-10000, "The Wretched {0}"),
new KarmaEntry(-5000, "The Dastardly {0}"),
new KarmaEntry(-2500, "The Malicious {0}"),
new KarmaEntry(-1250, "The Dishonorable {0}"),
new KarmaEntry(-625, "The Disreputable {0}"),
new KarmaEntry(624, "The Notable {0}"),
new KarmaEntry(1249, "The Upstanding {0}"),
new KarmaEntry(2499, "The Respectable {0}"),
new KarmaEntry(4999, "The Honorable {0}"),
new KarmaEntry(9999, "The Commendable {0}"),
new KarmaEntry(10000, "The Estimable {0}")
}),
new FameEntry(4999, new[]
{
new KarmaEntry(-10000, "The Nefarious {0}"),
new KarmaEntry(-5000, "The Wicked {0}"),
new KarmaEntry(-2500, "The Vile {0}"),
new KarmaEntry(-1250, "The Ignoble {0}"),
new KarmaEntry(-625, "The Notorious {0}"),
new KarmaEntry(624, "The Prominent {0}"),
new KarmaEntry(1249, "The Reputable {0}"),
new KarmaEntry(2499, "The Proper {0}"),
new KarmaEntry(4999, "The Admirable {0}"),
new KarmaEntry(9999, "The Famed {0}"),
new KarmaEntry(10000, "The Great {0}")
}),
new FameEntry(9999, new[]
{
new KarmaEntry(-10000, "The Dread {0}"),
new KarmaEntry(-5000, "The Evil {0}"),
new KarmaEntry(-2500, "The Villainous {0}"),
new KarmaEntry(-1250, "The Sinister {0}"),
new KarmaEntry(-625, "The Infamous {0}"),
new KarmaEntry(624, "The Renowned {0}"),
new KarmaEntry(1249, "The Distinguished {0}"),
new KarmaEntry(2499, "The Eminent {0}"),
new KarmaEntry(4999, "The Noble {0}"),
new KarmaEntry(9999, "The Illustrious {0}"),
new KarmaEntry(10000, "The Glorious {0}")
}),
new FameEntry(10000, new[]
{
new KarmaEntry(-10000, "The Dread {1} {0}"),
new KarmaEntry(-5000, "The Evil {1} {0}"),
new KarmaEntry(-2500, "The Dark {1} {0}"),
new KarmaEntry(-1250, "The Sinister {1} {0}"),
new KarmaEntry(-625, "The Dishonored {1} {0}"),
new KarmaEntry(624, "{1} {0}"),
new KarmaEntry(1249, "The Distinguished {1} {0}"),
new KarmaEntry(2499, "The Eminent {1} {0}"),
new KarmaEntry(4999, "The Noble {1} {0}"),
new KarmaEntry(9999, "The Illustrious {1} {0}"),
new KarmaEntry(10000, "The Glorious {1} {0}")
})
};
public static void AwardFame(Mobile m, int offset, bool message)
{
if (offset > 0)
{
if (m.Fame >= MaxFame)
return;
offset -= m.Fame / 100;
if (offset < 0)
offset = 0;
}
else if (offset < 0)
{
if (m.Fame <= MinFame)
return;
offset -= m.Fame / 100;
if (offset > 0)
offset = 0;
}
if (m.Fame + offset > MaxFame)
offset = MaxFame - m.Fame;
else if (m.Fame + offset < MinFame)
offset = MinFame - m.Fame;
m.Fame += offset;
if (message)
{
if (offset > 40)
m.SendLocalizedMessage(1019054); // You have gained a lot of fame.
else if (offset > 20)
m.SendLocalizedMessage(1019053); // You have gained a good amount of fame.
else if (offset > 10)
m.SendLocalizedMessage(1019052); // You have gained some fame.
else if (offset > 0)
m.SendLocalizedMessage(1019051); // You have gained a little fame.
else if (offset < -40)
m.SendLocalizedMessage(1019058); // You have lost a lot of fame.
else if (offset < -20)
m.SendLocalizedMessage(1019057); // You have lost a good amount of fame.
else if (offset < -10)
m.SendLocalizedMessage(1019056); // You have lost some fame.
else if (offset < 0)
m.SendLocalizedMessage(1019055); // You have lost a little fame.
}
}
public static void AwardKarma(Mobile m, int offset, bool message)
{
PlayerMobile pm = m as PlayerMobile;
if (offset > 0)
{
if (pm?.KarmaLocked == true)
return;
if (m.Karma >= MaxKarma)
return;
offset -= m.Karma / 100;
if (offset < 0)
offset = 0;
}
else if (offset < 0)
{
if (m.Karma <= MinKarma)
return;
offset -= m.Karma / 100;
if (offset > 0)
offset = 0;
}
if (m.Karma + offset > MaxKarma)
offset = MaxKarma - m.Karma;
else if (m.Karma + offset < MinKarma)
offset = MinKarma - m.Karma;
bool wasPositiveKarma = m.Karma >= 0;
m.Karma += offset;
if (message)
{
if (offset > 40)
m.SendLocalizedMessage(1019062); // You have gained a lot of karma.
else if (offset > 20)
m.SendLocalizedMessage(1019061); // You have gained a good amount of karma.
else if (offset > 10)
m.SendLocalizedMessage(1019060); // You have gained some karma.
else if (offset > 0)
m.SendLocalizedMessage(1019059); // You have gained a little karma.
else if (offset < -40)
m.SendLocalizedMessage(1019066); // You have lost a lot of karma.
else if (offset < -20)
m.SendLocalizedMessage(1019065); // You have lost a good amount of karma.
else if (offset < -10)
m.SendLocalizedMessage(1019064); // You have lost some karma.
else if (offset < 0)
m.SendLocalizedMessage(1019063); // You have lost a little karma.
}
if (!Core.AOS && wasPositiveKarma && m.Karma < 0 && pm != null && !pm.KarmaLocked)
{
pm.KarmaLocked = true;
m.SendLocalizedMessage(1042511, "",
0x22); // Karma is locked. A mantra spoken at a shrine will unlock it again.
}
}
public static string ComputeTitle(Mobile beholder, Mobile beheld)
{
StringBuilder title = new StringBuilder();
int fame = beheld.Fame;
int karma = beheld.Karma;
bool showSkillTitle = beheld.ShowFameTitle && (beholder == beheld || fame >= 5000);
/*if ( beheld.Kills >= 5 )
{
title.AppendFormat( beheld.Fame >= 10000 ? "The Murderer {1} {0}" : "The Murderer {0}", beheld.Name, beheld.Female ? "Lady" : "Lord" );
}
else*/
if (beheld.ShowFameTitle || beholder == beheld)
for (int i = 0; i < m_FameEntries.Length; ++i)
{
FameEntry fe = m_FameEntries[i];
if (fame <= fe.m_Fame || i == m_FameEntries.Length - 1)
{
KarmaEntry[] karmaEntries = fe.m_Karma;
for (int j = 0; j < karmaEntries.Length; ++j)
{
KarmaEntry ke = karmaEntries[j];
if (karma <= ke.m_Karma || j == karmaEntries.Length - 1)
{
title.AppendFormat(ke.m_Title, beheld.Name, beheld.Female ? "Lady" : "Lord");
break;
}
}
break;
}
}
else
title.Append(beheld.Name);
if (beheld is PlayerMobile mobile && mobile.DisplayChampionTitle)
{
PlayerMobile.ChampionTitleInfo info = mobile.ChampionTitles;
if (info.Harrower > 0)
{
title.AppendFormat(": {0} of Evil", HarrowerTitles[Math.Min(HarrowerTitles.Length, info.Harrower) - 1]);
}
else
{
int highestValue = 0, highestType = 0;
for (int i = 0; i < ChampionSpawnInfo.Table.Length; i++)
{
int v = info.GetValue(i);
if (v > highestValue)
{
highestValue = v;
highestType = i;
}
}
int offset = 0;
if (highestValue > 800)
offset = 3;
else if (highestValue > 300)
offset = highestValue / 300;
if (offset > 0)
{
ChampionSpawnInfo champInfo = ChampionSpawnInfo.GetInfo((ChampionSpawnType)highestType);
title.AppendFormat(": {0} of the {1}",
champInfo.LevelNames[Math.Min(offset, champInfo.LevelNames.Length) - 1], champInfo.Name);
}
}
}
string customTitle = beheld.Title;
if (customTitle != null && (customTitle = customTitle.Trim()).Length > 0)
{
title.AppendFormat(" {0}", customTitle);
}
else if (showSkillTitle && beheld.Player)
{
string skillTitle = GetSkillTitle(beheld);
if (skillTitle != null) title.Append(", ").Append(skillTitle);
}
return title.ToString();
}
public static string GetSkillTitle(Mobile mob)
{
Skill highest = GetHighestSkill(mob); // beheld.Skills.Highest;
if (highest?.BaseFixedPoint >= 300)
{
string skillLevel = GetSkillLevel(highest);
string skillTitle = highest.Info.Title;
if (mob.Female && skillTitle.EndsWith("man"))
skillTitle = skillTitle.Substring(0, skillTitle.Length - 3) + "woman";
return string.Concat(skillLevel, " ", skillTitle);
}
return null;
}
private static Skill GetHighestSkill(Mobile m)
{
Skills skills = m.Skills;
if (!Core.AOS)
return skills.Highest;
Skill highest = null;
for (int i = 0; i < m.Skills.Length; ++i)
{
Skill check = m.Skills[i];
if (highest == null || check.BaseFixedPoint > highest.BaseFixedPoint)
highest = check;
else if (highest.Lock != SkillLock.Up && check.Lock == SkillLock.Up &&
check.BaseFixedPoint == highest.BaseFixedPoint)
highest = check;
}
return highest;
}
private static string GetSkillLevel(Skill skill)
{
return m_Levels[GetTableIndex(skill), GetTableType(skill)];
}
private static int GetTableType(Skill skill)
{
switch (skill.SkillName)
{
default: return 0;
case SkillName.Bushido: return 1;
case SkillName.Ninjitsu: return 2;
}
}
private static int GetTableIndex(Skill skill)
{
int fp = Math.Min(skill.BaseFixedPoint, 1200);
return (fp - 300) / 100;
}
}
public class FameEntry
{
public int m_Fame;
public KarmaEntry[] m_Karma;
public FameEntry(int fame, KarmaEntry[] karma)
{
m_Fame = fame;
m_Karma = karma;
}
}
public class KarmaEntry
{
public int m_Karma;
public string m_Title;
public KarmaEntry(int karma, string title)
{
m_Karma = karma;
m_Title = title;
}
}
}

View file

@ -0,0 +1,107 @@
using Server.Commands;
using Server.Commands.Generic;
namespace Server.Items
{
public class ToggleItem : Item
{
[Constructible]
public ToggleItem(int inactiveItemID, int activeItemID, bool playersCanToggle = false)
: base(inactiveItemID)
{
Movable = false;
InactiveItemID = inactiveItemID;
ActiveItemID = activeItemID;
PlayersCanToggle = playersCanToggle;
}
public ToggleItem(Serial serial)
: base(serial)
{
}
[CommandProperty(AccessLevel.GameMaster)]
public int InactiveItemID{ get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public int ActiveItemID{ get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public bool PlayersCanToggle{ get; set; }
public static void Initialize()
{
TargetCommands.Register(new ToggleCommand());
}
public override void OnDoubleClick(Mobile from)
{
if (from.AccessLevel >= AccessLevel.GameMaster)
{
Toggle();
}
else if (PlayersCanToggle)
{
if (from.InRange(GetWorldLocation(), 1))
Toggle();
else
from.SendLocalizedMessage(500446); // That is too far away.
}
}
public void Toggle()
{
ItemID = ItemID == ActiveItemID ? InactiveItemID : ActiveItemID;
Visible = ItemID != 0x1;
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
writer.Write(InactiveItemID);
writer.Write(ActiveItemID);
writer.Write(PlayersCanToggle);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
InactiveItemID = reader.ReadInt();
ActiveItemID = reader.ReadInt();
PlayersCanToggle = reader.ReadBool();
}
public class ToggleCommand : BaseCommand
{
public ToggleCommand()
{
AccessLevel = AccessLevel.GameMaster;
Supports = CommandSupport.AllItems;
Commands = new[] { "Toggle" };
ObjectTypes = ObjectTypes.Items;
Usage = "Toggle";
Description = "Toggles a targeted ToggleItem.";
}
public override void Execute(CommandEventArgs e, object obj)
{
if (obj is ToggleItem item)
{
item.Toggle();
AddResponse("The item has been toggled.");
}
else
{
LogFailure("That is not a ToggleItem.");
}
}
}
}
}

View file

@ -0,0 +1,75 @@
using System;
using System.IO;
using Server.Regions;
namespace Server
{
public class TreasureRegion : BaseRegion
{
private const int Range = 5; // No house may be placed within 5 tiles of the treasure
public TreasureRegion(int x, int y, Map map) : base(null, map, DefaultPriority,
new Rectangle2D(x - Range, y - Range, 1 + Range * 2, 1 + Range * 2))
{
GoLocation = new Point3D(x, y, map.GetAverageZ(x, y));
Register();
}
public static void Initialize()
{
string filePath = Path.Combine(Core.BaseDirectory, "Data/treasure.cfg");
int i = 0, x = 0, y = 0;
if (File.Exists(filePath))
using (StreamReader ip = new StreamReader(filePath))
{
string line;
while ((line = ip.ReadLine()) != null)
{
i++;
try
{
string[] split = line.Split(' ');
x = Convert.ToInt32(split[0]);
y = Convert.ToInt32(split[1]);
try
{
new TreasureRegion(x, y, Map.Felucca);
new TreasureRegion(x, y, Map.Trammel);
}
catch (Exception e)
{
Console.WriteLine("{0} {1} {2} {3}", i, x, y, e);
}
}
catch
{
Console.WriteLine("Warning: Error in Line '{0}' of Data/treasure.cfg", line);
}
}
}
}
public override bool AllowHousing(Mobile from, Point3D p)
{
return false;
}
public override void OnEnter(Mobile m)
{
if (m.AccessLevel > AccessLevel.Player)
m.SendMessage("You have entered a protected treasure map area.");
}
public override void OnExit(Mobile m)
{
if (m.AccessLevel > AccessLevel.Player)
m.SendMessage("You have left a protected treasure map area.");
}
}
}

View file

@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Reflection;
namespace Server
{
public delegate void ValidationEventHandler();
public static class ValidationQueue
{
public static event ValidationEventHandler StartValidation;
public static void Initialize()
{
StartValidation?.Invoke();
StartValidation = null;
}
}
public static class ValidationQueue<T>
{
private static List<T> m_Queue;
static ValidationQueue()
{
m_Queue = new List<T>();
ValidationQueue.StartValidation += ValidateAll;
}
public static void Add(T obj)
{
m_Queue.Add(obj);
}
private static void ValidateAll()
{
Type type = typeof(T);
MethodInfo m = type.GetMethod("Validate", BindingFlags.Instance | BindingFlags.Public);
if (m != null)
for (int i = 0; i < m_Queue.Count; ++i)
m.Invoke(m_Queue[i], null);
m_Queue.Clear();
m_Queue = null;
}
}
}

View file

@ -0,0 +1,456 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Server.Commands;
using Server.Mobiles;
namespace Server
{
public class VendorGenerator
{
private static Rectangle2D[] m_BritRegions =
{
new Rectangle2D(new Point2D(250, 750), new Point2D(775, 1330)),
new Rectangle2D(new Point2D(525, 2095), new Point2D(925, 2430)),
new Rectangle2D(new Point2D(1025, 2155), new Point2D(1265, 2310)),
new Rectangle2D(new Point2D(1635, 2430), new Point2D(1705, 2508)),
new Rectangle2D(new Point2D(1775, 2605), new Point2D(2165, 2975)),
new Rectangle2D(new Point2D(1055, 3520), new Point2D(1570, 4075)),
new Rectangle2D(new Point2D(2860, 3310), new Point2D(3120, 3630)),
new Rectangle2D(new Point2D(2470, 1855), new Point2D(3950, 3045)),
new Rectangle2D(new Point2D(3425, 990), new Point2D(3900, 1455)),
new Rectangle2D(new Point2D(4175, 735), new Point2D(4840, 1600)),
new Rectangle2D(new Point2D(2375, 330), new Point2D(3100, 1045)),
new Rectangle2D(new Point2D(2100, 1090), new Point2D(2310, 1450)),
new Rectangle2D(new Point2D(1495, 1400), new Point2D(1550, 1475)),
new Rectangle2D(new Point2D(1085, 1520), new Point2D(1415, 1910)),
new Rectangle2D(new Point2D(1410, 1500), new Point2D(1745, 1795)),
new Rectangle2D(new Point2D(5120, 2300), new Point2D(6143, 4095))
};
private static Rectangle2D[] m_IlshRegions =
{
new Rectangle2D(new Point2D(0, 0), new Point2D(288 * 8, 200 * 8))
};
private static Dictionary<Point2D, ShopInfo> m_ShopTable;
private static List<ShopInfo> m_ShopList;
public static void Initialize()
{
CommandSystem.Register("VendorGen", AccessLevel.Administrator, VendorGen_OnCommand);
}
[Usage("VendorGen")]
[Description("Generates vendors based on display cases and floor plans. Analyzes the map files, slow.")]
private static void VendorGen_OnCommand(CommandEventArgs e)
{
Process(Map.Trammel, m_BritRegions);
Process(Map.Felucca, m_BritRegions);
Process(Map.Ilshenar, m_IlshRegions);
}
private static bool GetFloorZ(Map map, int x, int y, out int z)
{
LandTile lt = map.Tiles.GetLandTile(x, y);
if (IsFloor(lt.ID) && map.CanFit(x, y, lt.Z, 16, false, false))
{
z = lt.Z;
return true;
}
StaticTile[] tiles = map.Tiles.GetStaticTiles(x, y);
for (int i = 0; i < tiles.Length; ++i)
{
StaticTile t = tiles[i];
ItemData id = TileData.ItemTable[t.ID & TileData.MaxItemValue];
if (IsStaticFloor(t.ID) && map.CanFit(x, y, t.Z + (id.Surface ? id.CalcHeight : 0), 16, false, false))
{
z = t.Z + (id.Surface ? id.CalcHeight : 0);
return true;
}
}
z = 0;
return false;
}
private static bool IsFloor(Map map, int x, int y, bool canFit)
{
LandTile lt = map.Tiles.GetLandTile(x, y);
if (IsFloor(lt.ID) && (canFit || CanFit(map, x, y, lt.Z)))
return true;
StaticTile[] tiles = map.Tiles.GetStaticTiles(x, y);
for (int i = 0; i < tiles.Length; ++i)
{
StaticTile t = tiles[i];
ItemData id = TileData.ItemTable[t.ID & TileData.MaxItemValue];
if (IsStaticFloor(t.ID) && (canFit || CanFit(map, x, y, t.Z + (id.Surface ? id.CalcHeight : 0))))
return true;
}
return false;
}
private static bool IsFloor(int itemID)
{
itemID &= TileData.MaxLandValue;
return itemID >= 0x406 && itemID <= 0x51A;
}
private static bool IsStaticFloor(int itemID)
{
return itemID >= 0x495 && itemID <= 0x514
|| itemID >= 0x519 && itemID <= 0x53A;
}
private static bool IsDisplayCase(int itemID)
{
return itemID >= 0xB00 && itemID <= 0xB02
|| itemID >= 0xB06 && itemID <= 0xB0A
|| itemID >= 0xB0D && itemID <= 0xB17;
}
private static void Process(Map map, Rectangle2D[] regions)
{
m_ShopTable = new Dictionary<Point2D, ShopInfo>();
m_ShopList = new List<ShopInfo>();
World.Broadcast(0x35, true, "Generating vendor spawns for {0}, please wait.", map);
for (int i = 0; i < regions.Length; ++i)
for (int x = 0; x < map.Width; ++x)
for (int y = 0; y < map.Height; ++y)
CheckPoint(map, regions[i].X + x, regions[i].Y + y);
for (int i = 0; i < m_ShopList.Count; ++i)
{
ShopInfo si = m_ShopList[i];
int xTotal = 0;
int yTotal = 0;
bool hasSpawner = false;
for (int j = 0; j < si.m_Floor.Count; ++j)
{
Point2D fp = si.m_Floor[j];
xTotal += fp.X;
yTotal += fp.Y;
IPooledEnumerable<Spawner> eable = map.GetItemsInRange<Spawner>(new Point3D(fp.X, fp.Y, 0), 0);
hasSpawner = eable.Any();
eable.Free();
if (hasSpawner)
break;
}
if (hasSpawner)
continue;
int xAvg = xTotal / si.m_Floor.Count;
int yAvg = yTotal / si.m_Floor.Count;
List<string> names = new List<string>();
ShopFlags flags = si.m_Flags;
if ((flags & ShopFlags.Armor) != 0)
names.Add("armorer");
if ((flags & ShopFlags.MetalWeapon) != 0)
names.Add("weaponsmith");
if ((flags & ShopFlags.ArcheryWeapon) != 0)
names.Add("bowyer");
if ((flags & ShopFlags.Scroll) != 0)
names.Add("mage");
if ((flags & ShopFlags.Spellbook) != 0)
names.Add("mage");
if ((flags & ShopFlags.Bread) != 0)
names.Add("baker");
if ((flags & ShopFlags.Jewel) != 0)
names.Add("jeweler");
if ((flags & ShopFlags.Potion) != 0)
{
names.Add("herbalist");
names.Add("alchemist");
names.Add("mage");
}
if ((flags & ShopFlags.Reagent) != 0)
{
names.Add("mage");
names.Add("herbalist");
}
if ((flags & ShopFlags.Clothes) != 0)
{
names.Add("tailor");
names.Add("weaver");
}
for (int j = 0; j < names.Count; ++j)
{
Point2D cp = Point2D.Zero;
int dist = 100000;
for (int k = 0; k < si.m_Floor.Count; ++k)
{
Point2D fp = si.m_Floor[k];
int rx = fp.X - xAvg;
int ry = fp.Y - yAvg;
int fd = (int)Math.Sqrt(rx * rx + ry * ry);
if (fd > 0 && fd < 5)
fd -= Utility.Random(10);
if (fd < dist && GetFloorZ(map, fp.X, fp.Y, out _))
{
dist = fd;
cp = fp;
}
}
if (cp == Point2D.Zero)
continue;
if (!GetFloorZ(map, cp.X, cp.Y, out int z))
continue;
new Spawner(1, 1, 1, 0, 4, names[j]).MoveToWorld(new Point3D(cp.X, cp.Y, z), map);
}
}
World.Broadcast(0x35, true, "Generation complete. {0} spawners generated.", m_ShopList.Count);
}
private static void CheckPoint(Map map, int x, int y)
{
if (IsFloor(map, x, y, true))
CheckFloor(map, x, y);
}
private static void CheckFloor(Map map, int x, int y)
{
StaticTile[] tiles = map.Tiles.GetStaticTiles(x, y);
for (int i = 0; i < tiles.Length; ++i)
if (IsDisplayCase(tiles[i].ID))
{
ProcessDisplayCase(map, tiles, x, y);
break;
}
}
private static bool IsClothes(int itemID)
{
return itemID >= 0x1515 && itemID <= 0x1518 || itemID >= 0x152E && itemID <= 0x1531 || itemID >= 0x1537
&& itemID <= 0x154C || itemID >= 0x1EFD && itemID <= 0x1F04 || itemID >= 0x170B && itemID <= 0x171C;
}
private static bool IsArmor(int itemID)
{
return itemID >= 0x13BB && itemID <= 0x13E2 || itemID >= 0x13E5 && itemID <= 0x13F2 ||
itemID >= 0x1408 && itemID <= 0x141A || itemID >= 0x144E && itemID <= 0x1457;
}
private static bool IsMetalWeapon(int itemID)
{
return itemID >= 0xF43 && itemID <= 0xF4E || itemID >= 0xF51 && itemID <= 0xF52 ||
itemID >= 0xF5C && itemID <= 0xF63 || itemID >= 0x13AF && itemID <= 0x13B0 ||
itemID >= 0x13B5 && itemID <= 0x13BA || itemID >= 0x13FA && itemID <= 0x13FB ||
itemID >= 0x13FE && itemID <= 0x1407 || itemID >= 0x1438 && itemID <= 0x1443;
}
private static bool IsArcheryWeapon(int itemID)
{
return itemID >= 0xF4F && itemID <= 0xF50 || itemID >= 0x13B1 && itemID <= 0x13B2 ||
itemID >= 0x13FC && itemID <= 0x13FD;
}
private static ShopFlags ProcessDisplayedItem(int itemID)
{
itemID &= TileData.MaxItemValue;
ShopFlags res = ShopFlags.None;
ItemData id = TileData.ItemTable[itemID];
TileFlag flags = id.Flags;
if ((flags & TileFlag.Wearable) != 0)
{
if (IsClothes(itemID))
res |= ShopFlags.Clothes;
else if (IsArmor(itemID))
res |= ShopFlags.Armor;
else if (IsMetalWeapon(itemID))
res |= ShopFlags.MetalWeapon;
else if (IsArcheryWeapon(itemID))
res |= ShopFlags.ArcheryWeapon;
}
if (itemID == 0x98C || itemID == 0x103B || itemID == 0x103C)
res |= ShopFlags.Bread;
if (itemID >= 0xF0F && itemID <= 0xF30)
res |= ShopFlags.Jewel;
if (itemID >= 0xEFB && itemID <= 0xF0D)
res |= ShopFlags.Potion;
if (itemID >= 0xF78 && itemID <= 0xF91)
res |= ShopFlags.Reagent;
if (itemID >= 0xE35 && itemID <= 0xE3A || itemID >= 0xEF4 && itemID <= 0xEF9 ||
itemID >= 0x1F2D && itemID <= 0x1F72)
res |= ShopFlags.Scroll;
if (itemID == 0xE38 || itemID == 0xEFA)
res |= ShopFlags.Spellbook;
return res;
}
private static void ProcessDisplayCase(Map map, StaticTile[] tiles, int x, int y)
{
ShopFlags flags = ShopFlags.None;
for (int i = 0; i < tiles.Length; ++i)
flags |= ProcessDisplayedItem(tiles[i].ID);
if (flags != ShopFlags.None)
{
Point2D p = new Point2D(x, y);
if (m_ShopTable.TryGetValue(p, out ShopInfo si))
si.m_Flags |= flags;
else
{
List<Point2D> floor = new List<Point2D>();
RecurseFindFloor(map, x, y, floor);
if (floor.Count == 0)
return;
si = new ShopInfo { m_Flags = flags, m_Floor = floor };
m_ShopList.Add(si);
for (int i = 0; i < floor.Count; ++i)
m_ShopTable[floor[i]] = si;
}
}
}
private static bool CanFit(Map map, int x, int y, int z)
{
bool hasSurface = false;
LandTile lt = map.Tiles.GetLandTile(x, y);
int lowZ = 0, avgZ = 0, topZ = 0;
map.GetAverageZ(x, y, ref lowZ, ref avgZ, ref topZ);
TileFlag landFlags = TileData.LandTable[lt.ID & TileData.MaxLandValue].Flags;
if ((landFlags & TileFlag.Impassable) != 0 && topZ > z && z + 16 > lowZ)
return false;
if ((landFlags & TileFlag.Impassable) == 0 && z == avgZ && !lt.Ignored)
hasSurface = true;
StaticTile[] staticTiles = map.Tiles.GetStaticTiles(x, y);
bool surface, impassable;
for (int i = 0; i < staticTiles.Length; ++i)
{
if (IsDisplayCase(staticTiles[i].ID))
continue;
ItemData id = TileData.ItemTable[staticTiles[i].ID & TileData.MaxItemValue];
surface = id.Surface;
impassable = id.Impassable;
if ((surface || impassable) && staticTiles[i].Z + id.CalcHeight > z && z + 16 > staticTiles[i].Z)
return false;
if (surface && !impassable && z == staticTiles[i].Z + id.CalcHeight)
hasSurface = true;
}
Sector sector = map.GetSector(x, y);
List<Item> items = sector.Items;
for (int i = 0; i < items.Count; ++i)
{
Item item = items[i];
if (item.AtWorldPoint(x, y))
{
ItemData id = item.ItemData;
surface = id.Surface;
impassable = id.Impassable;
if ((surface || impassable) && item.Z + id.CalcHeight > z && z + 16 > item.Z)
return false;
if (surface && !impassable && z == item.Z + id.CalcHeight)
hasSurface = true;
}
}
return hasSurface;
}
private static void RecurseFindFloor(Map map, int x, int y, List<Point2D> floor)
{
Point2D p = new Point2D(x, y);
if (floor.Contains(p))
return;
floor.Add(p);
for (int xo = -1; xo <= 1; ++xo)
for (int yo = -1; yo <= 1; ++yo)
if ((xo != 0 || yo != 0) && IsFloor(map, x + xo, y + yo, false))
RecurseFindFloor(map, x + xo, y + yo, floor);
}
[Flags]
private enum ShopFlags
{
None = 0x000,
Armor = 0x001,
MetalWeapon = 0x002,
Jewel = 0x004,
Reagent = 0x008,
Potion = 0x010,
Bread = 0x020,
Clothes = 0x040,
ArcheryWeapon = 0x080,
Scroll = 0x100,
Spellbook = 0x200
}
private class ShopInfo
{
public ShopFlags m_Flags;
public List<Point2D> m_Floor;
}
}
}

View file

@ -0,0 +1,357 @@
using System;
using System.Collections.Generic;
using Server.Items;
using Server.Network;
namespace Server.Misc
{
public class Weather
{
private static Map[] m_Facets;
private static Dictionary<Map, List<Weather>> m_WeatherByFacet = new Dictionary<Map, List<Weather>>();
public static void Initialize()
{
m_Facets = new[]{ Map.Felucca, Map.Trammel };
/* Static weather:
*
* Format:
* AddWeather( temperature, chanceOfPercipitation, chanceOfExtremeTemperature, <area ...> );
*/
// ice island
AddWeather( -15, 100, 5, new Rectangle2D( 3850, 160, 390, 320 ), new Rectangle2D( 3900, 480, 380, 180 ), new Rectangle2D( 4160, 660, 150, 110 ) );
// covetous entrance, around vesper and minoc
AddWeather( +15, 50, 5, new Rectangle2D( 2425, 725, 250, 250 ) );
// despise entrance, north of britain
AddWeather( +15, 50, 5, new Rectangle2D( 1245, 1045, 250, 250 ) );
/* Dynamic weather:
*
* Format:
* AddDynamicWeather( temperature, chanceOfPercipitation, chanceOfExtremeTemperature, moveSpeed, width, height, bounds );
*/
for ( int i = 0; i < 15; ++i )
AddDynamicWeather( +15, 100, 5, 8, 400, 400, new Rectangle2D( 0, 0, 5120, 4096 ) );
}
public static List<Weather> GetWeatherList( Map facet )
{
if ( facet == null )
return null;
if (!m_WeatherByFacet.TryGetValue( facet, out List<Weather> list ))
m_WeatherByFacet[facet] = list = new List<Weather>();
return list;
}
public static void AddDynamicWeather( int temperature, int chanceOfPercipitation, int chanceOfExtremeTemperature, int moveSpeed, int width, int height, Rectangle2D bounds )
{
for ( int i = 0; i < m_Facets.Length; ++i )
{
Rectangle2D area = new Rectangle2D();
bool isValid = false;
for ( int j = 0; j < 10; ++j )
{
area = new Rectangle2D( bounds.X + Utility.Random( bounds.Width - width ), bounds.Y + Utility.Random( bounds.Height - height ), width, height );
if ( !CheckWeatherConflict( m_Facets[i], null, area ) )
isValid = true;
if ( isValid )
break;
}
if ( !isValid )
continue;
new Weather(m_Facets[i], new[] { area }, temperature, chanceOfPercipitation, chanceOfExtremeTemperature,
TimeSpan.FromSeconds(30.0)) { Bounds = bounds, MoveSpeed = moveSpeed };
}
}
public static void AddWeather( int temperature, int chanceOfPercipitation, int chanceOfExtremeTemperature, params Rectangle2D[] area )
{
for ( int i = 0; i < m_Facets.Length; ++i )
new Weather( m_Facets[i], area, temperature, chanceOfPercipitation, chanceOfExtremeTemperature, TimeSpan.FromSeconds( 30.0 ) );
}
public static bool CheckWeatherConflict( Map facet, Weather exclude, Rectangle2D area )
{
List<Weather> list = GetWeatherList( facet );
if ( list == null )
return false;
for ( int i = 0; i < list.Count; ++i )
{
Weather w = list[i];
if ( w != exclude && w.IntersectsWith( area ) )
return true;
}
return false;
}
public Map Facet { get; }
public Rectangle2D[] Area { get; set; }
public int Temperature { get; set; }
public int ChanceOfPercipitation { get; set; }
public int ChanceOfExtremeTemperature { get; set; }
// For dynamic weather:
public Rectangle2D Bounds { get; set; }
public int MoveSpeed { get; set; }
public int MoveAngleX { get; set; }
public int MoveAngleY { get; set; }
public static bool CheckIntersection( Rectangle2D r1, Rectangle2D r2 )
{
return r1.X < r2.X + r2.Width && r2.X < r1.X + r1.Width && r1.Y < r2.Y + r2.Height && r2.Y < r1.Y + r1.Height;
}
public static bool CheckContains( Rectangle2D big, Rectangle2D small )
{
return small.X >= big.X && small.Y >= big.Y && small.X + small.Width <= big.X + big.Width
&& small.Y + small.Height <= big.Y + big.Height;
}
public virtual bool IntersectsWith( Rectangle2D area )
{
for ( int i = 0; i < Area.Length; ++i )
{
if ( CheckIntersection( area, Area[i] ) )
return true;
}
return false;
}
public Weather( Map facet, Rectangle2D[] area, int temperature, int chanceOfPercipitation, int chanceOfExtremeTemperature, TimeSpan interval )
{
Facet = facet;
Area = area;
Temperature = temperature;
ChanceOfPercipitation = chanceOfPercipitation;
ChanceOfExtremeTemperature = chanceOfExtremeTemperature;
List<Weather> list = GetWeatherList( facet );
list?.Add( this );
Timer.DelayCall( TimeSpan.FromSeconds( (0.2+Utility.RandomDouble()*0.8) * interval.TotalSeconds ), interval, OnTick );
}
public virtual void Reposition()
{
if ( Area.Length == 0 )
return;
int width = Area[0].Width;
int height = Area[0].Height;
Rectangle2D area = new Rectangle2D();
bool isValid = false;
for ( int j = 0; j < 10; ++j )
{
area = new Rectangle2D( Bounds.X + Utility.Random( Bounds.Width - width ), Bounds.Y + Utility.Random( Bounds.Height - height ), width, height );
if ( !CheckWeatherConflict( Facet, this, area ) )
isValid = true;
if ( isValid )
break;
}
if ( !isValid )
return;
Area[0] = area;
}
public virtual void RecalculateMovementAngle()
{
double angle = Utility.RandomDouble() * Math.PI * 2.0;
double cos = Math.Cos( angle );
double sin = Math.Sin( angle );
MoveAngleX = (int)(100 * cos);
MoveAngleY = (int)(100 * sin);
}
public virtual void MoveForward()
{
if ( Area.Length == 0 )
return;
for ( int i = 0; i < 5; ++i ) // try 5 times to find a valid spot
{
int xOffset = MoveSpeed * MoveAngleX / 100;
int yOffset = MoveSpeed * MoveAngleY / 100;
Rectangle2D oldArea = Area[0];
Rectangle2D newArea = new Rectangle2D( oldArea.X + xOffset, oldArea.Y + yOffset, oldArea.Width, oldArea.Height );
if ( !CheckWeatherConflict( Facet, this, newArea ) && CheckContains( Bounds, newArea ) )
{
Area[0] = newArea;
break;
}
RecalculateMovementAngle();
}
}
private int m_Stage;
private bool m_Active;
private bool m_ExtremeTemperature;
public virtual void OnTick()
{
if ( m_Stage == 0 )
{
m_Active = ChanceOfPercipitation > Utility.Random( 100 );
m_ExtremeTemperature = ChanceOfExtremeTemperature > Utility.Random( 100 );
if ( MoveSpeed > 0 )
{
Reposition();
RecalculateMovementAngle();
}
}
if ( m_Active )
{
if ( m_Stage > 0 && MoveSpeed > 0 )
MoveForward();
int type, density;
int temperature = Temperature;
if ( m_ExtremeTemperature )
temperature *= -1;
if ( m_Stage < 15 )
{
density = m_Stage * 5;
}
else
{
density = 150 - m_Stage * 5;
if ( density < 10 )
density = 10;
else if ( density > 70 )
density = 70;
}
if ( density == 0 )
type = 0xFE;
else if ( temperature > 0 )
type = 0;
else
type = 2;
List<NetState> states = NetState.Instances;
Packet weatherPacket = null;
for ( int i = 0; i < states.Count; ++i )
{
NetState ns = states[i];
Mobile mob = ns.Mobile;
if ( mob == null || mob.Map != Facet )
continue;
bool contains = Area.Length == 0;
for ( int j = 0; !contains && j < Area.Length; ++j )
contains = Area[j].Contains( mob.Location );
if ( !contains )
continue;
if ( weatherPacket == null )
weatherPacket = Packet.Acquire( new Server.Network.Weather( type, density, temperature ) );
ns.Send( weatherPacket );
}
Packet.Release( weatherPacket );
}
m_Stage++;
m_Stage %= 30;
}
}
public class WeatherMap : MapItem
{
public override string DefaultName => "weather map";
[Constructible]
public WeatherMap()
{
SetDisplay( 0, 0, 5119, 4095, 400, 400 );
}
public override void OnDoubleClick( Mobile from )
{
Map facet = from.Map;
if ( facet == null )
return;
List<Weather> list = Weather.GetWeatherList( facet );
ClearPins();
for ( int i = 0; i < list.Count; ++i )
{
Weather w = list[i];
for ( int j = 0; j < w.Area.Length; ++j )
AddWorldPin( w.Area[j].X + w.Area[j].Width/2, w.Area[j].Y + w.Area[j].Height/2 );
}
base.OnDoubleClick( from );
}
public WeatherMap( Serial serial ) : base( serial )
{
}
public override void Serialize( GenericWriter writer )
{
base.Serialize( writer );
writer.Write( 0 );
}
public override void Deserialize( GenericReader reader )
{
base.Deserialize( reader );
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,186 @@
#region References
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using Server.Guilds;
using Server.Network;
#endregion
namespace Server.Misc
{
public class StatusPage : Timer
{
public static readonly bool Enabled = false;
private static HttpListener _Listener;
private static string _StatusPage = string.Empty;
private static byte[] _StatusBuffer = new byte[0];
private static readonly object _StatusLock = new object();
public StatusPage()
: base(TimeSpan.FromSeconds(5.0), TimeSpan.FromSeconds(60.0))
{
Priority = TimerPriority.FiveSeconds;
}
public static void Initialize()
{
if (!Enabled) return;
new StatusPage().Start();
Listen();
}
private static void Listen()
{
if (!HttpListener.IsSupported) return;
if (_Listener == null)
{
_Listener = new HttpListener();
_Listener.Prefixes.Add("http://*:80/status/");
_Listener.Start();
}
else if (!_Listener.IsListening)
{
_Listener.Start();
}
if (_Listener.IsListening) _Listener.BeginGetContext(ListenerCallback, null);
}
private static void ListenerCallback(IAsyncResult result)
{
try
{
HttpListenerContext context = _Listener.EndGetContext(result);
byte[] buffer;
lock (_StatusLock)
{
buffer = _StatusBuffer;
}
context.Response.ContentLength64 = buffer.Length;
context.Response.OutputStream.Write(buffer, 0, buffer.Length);
context.Response.OutputStream.Close();
}
catch
{
// ignored
}
Listen();
}
private static string Encode(string input)
{
StringBuilder sb = new StringBuilder(input);
sb.Replace("&", "&amp;");
sb.Replace("<", "&lt;");
sb.Replace(">", "&gt;");
sb.Replace("\"", "&quot;");
sb.Replace("'", "&apos;");
return sb.ToString();
}
protected override void OnTick()
{
if (!Directory.Exists("web")) Directory.CreateDirectory("web");
using (StreamWriter op = new StreamWriter("web/status.html"))
{
op.WriteLine("<!DOCTYPE html>");
op.WriteLine("<html>");
op.WriteLine(" <head>");
op.WriteLine(" <title>" + ServerList.ServerName + " Server Status</title>");
op.WriteLine(" </head>");
op.WriteLine(" <style type=\"text/css\">");
op.WriteLine(" body { background: #999; }");
op.WriteLine(" table { width: 100%; }");
op.WriteLine(" tr.ruo-header td { background: #000; color: #FFF; }");
op.WriteLine(" tr.odd td { background: #222; color: #DDD; }");
op.WriteLine(" tr.even td { background: #DDD; color: #222; }");
op.WriteLine(" </style>");
op.WriteLine(" <body>");
op.WriteLine(" <h1>RunUO Server Status</h1>");
op.WriteLine(" <h3>Online clients</h3>");
op.WriteLine(" <table cellpadding=\"0\" cellspacing=\"0\">");
op.WriteLine(
" <tr class=\"ruo-header\"><td>Name</td><td>Location</td><td>Kills</td><td>Karma/Fame</td></tr>");
int index = 0;
foreach (Mobile m in NetState.Instances.Where(state => state.Mobile != null).Select(state => state.Mobile))
{
++index;
Guild g = m.Guild as Guild;
op.Write(" <tr class=\"ruo-result " + (index % 2 == 0 ? "even" : "odd") + "\"><td>");
if (g != null)
{
op.Write(Encode(m.Name));
op.Write(" [");
string title = m.GuildTitle;
title = title != null ? title.Trim() : string.Empty;
if (title.Length > 0)
{
op.Write(Encode(title));
op.Write(", ");
}
op.Write(Encode(g.Abbreviation));
op.Write(']');
}
else
{
op.Write(Encode(m.Name));
}
op.Write("</td><td>");
op.Write(m.X);
op.Write(", ");
op.Write(m.Y);
op.Write(", ");
op.Write(m.Z);
op.Write(" (");
op.Write(m.Map);
op.Write(")</td><td>");
op.Write(m.Kills);
op.Write("</td><td>");
op.Write(m.Karma);
op.Write(" / ");
op.Write(m.Fame);
op.WriteLine("</td></tr>");
}
op.WriteLine(" <tr>");
op.WriteLine(" </table>");
op.WriteLine(" </body>");
op.WriteLine("</html>");
}
lock (_StatusLock)
{
_StatusPage = File.ReadAllText("web/status.html");
_StatusBuffer = Encoding.UTF8.GetBytes(_StatusPage);
}
}
}
}

View file

@ -0,0 +1,126 @@
using System;
using Server.Mobiles;
using Server.Spells.Ninjitsu;
namespace Server.Misc
{
public enum DFAlgorithm
{
Standard,
PainSpike
}
public class WeightOverloading
{
public const int OverloadAllowance = 4; // We can be four stones overweight without getting fatigued
public static DFAlgorithm DFA{ get; set; }
public static void Initialize()
{
EventSink.Movement += EventSink_Movement;
}
public static void FatigueOnDamage(Mobile m, int damage)
{
double fatigue = 0.0;
switch (DFA)
{
case DFAlgorithm.Standard:
{
fatigue = damage * (100.0 / m.Hits) * ((double)m.Stam / 100) - 5.0;
break;
}
case DFAlgorithm.PainSpike:
{
fatigue = damage * (100.0 / m.Hits + (50.0 + m.Stam) / 100 - 1.0) - 5.0;
break;
}
}
if (fatigue > 0)
m.Stam -= (int)fatigue;
}
public static int GetMaxWeight(Mobile m)
{
//return ((( Core.ML && m.Race == Race.Human) ? 100 : 40 ) + (int)(3.5 * m.Str));
//Moved to core virtual method for use there
return m.MaxWeight;
}
public static void EventSink_Movement(MovementEventArgs e)
{
Mobile from = e.Mobile;
if (!from.Alive || from.AccessLevel > AccessLevel.Player)
return;
if (!from.Player)
{
// Else it won't work on monsters.
DeathStrike.AddStep(from);
return;
}
int maxWeight = GetMaxWeight(from) + OverloadAllowance;
int overWeight = Mobile.BodyWeight + from.TotalWeight - maxWeight;
if (overWeight > 0)
{
from.Stam -= GetStamLoss(from, overWeight, (e.Direction & Direction.Running) != 0);
if (from.Stam == 0)
{
from.SendLocalizedMessage(
500109); // You are too fatigued to move, because you are carrying too much weight!
e.Blocked = true;
return;
}
}
if (from.Stam * 100 / Math.Max(from.StamMax, 1) < 10)
--from.Stam;
if (from.Stam == 0)
{
from.SendLocalizedMessage(500110); // You are too fatigued to move.
e.Blocked = true;
return;
}
if (from is PlayerMobile pm)
{
int amt = pm.Mounted ? 48 : 16;
if (++pm.StepsTaken % amt == 0)
--pm.Stam;
}
DeathStrike.AddStep(from);
}
public static int GetStamLoss(Mobile from, int overWeight, bool running)
{
int loss = 5 + overWeight / 25;
if (from.Mounted)
loss /= 3;
if (running)
loss *= 2;
return loss;
}
public static bool IsOverloaded(Mobile m)
{
if (!m.Player || !m.Alive || m.AccessLevel > AccessLevel.Player)
return false;
return Mobile.BodyWeight + m.TotalWeight > GetMaxWeight(m) + OverloadAllowance;
}
}
}

View file

@ -0,0 +1,53 @@
using System;
namespace Server.Misc
{
/// <summary>
/// This timer spouts some welcome messages to a user at a set interval. It is used on character creation and login.
/// </summary>
public class WelcomeTimer : Timer
{
private static string[] m_Messages = TestCenter.Enabled
? new[]
{
"Welcome to this test shard. You are able to customize your character's stats and skills at anytime to anything you wish. To see the commands to do this just say 'help'.",
"You will find a bank check worth 1,000,000 gold in your bank!",
"A spellbook and a bag of reagents has been placed into your bank box.",
"Various tools have been placed into your bank.",
"Various raw materials like ingots, logs, feathers, hides, bottles, etc, have been placed into your bank.",
"5 unmarked recall runes, 5 Felucca moonstones and 5 Trammel moonstones have been placed into your bank box.",
"One of each level of treasure map has been placed in your bank box.",
"You will find 9000 silver pieces deposited into your bank box. Spend it as you see fit and enjoy yourself!",
"You will find 9000 gold pieces deposited into your bank box. Spend it as you see fit and enjoy yourself!",
"A bag of PowerScrolls has been placed in your bank box."
}
: new[]
{
//Yes, this message is a pathetic message, It's suggested that you change it.
"Welcome to this shard.",
"Please enjoy your stay."
};
private Mobile m_Mobile;
private int m_State, m_Count;
public WelcomeTimer(Mobile m) : this(m, m_Messages.Length)
{
}
public WelcomeTimer(Mobile m, int count) : base(TimeSpan.FromSeconds(5.0), TimeSpan.FromSeconds(10.0))
{
m_Mobile = m;
m_Count = count;
}
protected override void OnTick()
{
if (m_State < m_Count)
m_Mobile.SendMessage(0x35, m_Messages[m_State++]);
if (m_State == m_Count)
Stop();
}
}
}

View file

@ -0,0 +1,353 @@
using System;
using System.Collections.Generic;
using System.IO;
using Server.Commands;
using Server.Mobiles;
// Version 0.8
namespace Server
{
public class UOAMVendorGenerator
{
//configuration
private const int
NPCCount = 2; // 2 npcs per type (so a mage spawner will spawn 2 npcs, a alchemist and herbalist spawner will spawn 4 npcs total)
private const int HomeRange = 5; // How far should they wander?
private const bool TotalRespawn = true; // Should we spawn them up right away?
private const int Team = 0; // "team" the npcs are on
private static int m_Count;
private static TimeSpan MinTime = TimeSpan.FromMinutes(2.5); //min spawn time
private static TimeSpan MaxTime = TimeSpan.FromMinutes(10.0); //max spawn time
public static void Initialize()
{
CommandSystem.Register("UOAMVendors", AccessLevel.Administrator, Generate_OnCommand);
}
[Usage("UOAMVendors")]
[Description("Generates vendor spawners from Data/Common.MAP (taken from UOAutoMap)")]
private static void Generate_OnCommand(CommandEventArgs e)
{
Parse(e.Mobile);
}
public static void Parse(Mobile from)
{
string vendor_path = Path.Combine(Core.BaseDirectory, "Data/Common.map");
m_Count = 0;
if (!File.Exists(vendor_path))
{
from.SendMessage("{0} not found!", vendor_path);
return;
}
from.SendMessage("Generating Vendors...");
using (StreamReader ip = new StreamReader(vendor_path))
{
string line;
while ((line = ip.ReadLine()) != null)
{
int indexOf = line.IndexOf(':');
if (indexOf == -1)
continue;
string type = line.Substring(0, ++indexOf).Trim();
string sub = line.Substring(indexOf).Trim();
string[] split = sub.Split(' ');
if (split.Length < 3)
continue;
split = new[] { type, split[0], split[1], split[2] };
switch (split[0].ToLower())
{
case "-healer:":
PlaceNPC(split[1], split[2], split[3], "Healer", "HealerGuildmaster");
break;
case "-baker:":
PlaceNPC(split[1], split[2], split[3], "Baker");
break;
case "-vet:":
PlaceNPC(split[1], split[2], split[3], "Veterinarian");
break;
case "-gypsymaiden:":
PlaceNPC(split[1], split[2], split[3], "GypsyMaiden");
break;
case "-gypsybank:":
PlaceNPC(split[1], split[2], split[3], "GypsyBanker");
break;
case "-bank:":
PlaceNPC(split[1], split[2], split[3], "Banker", "Minter");
break;
case "-inn:":
PlaceNPC(split[1], split[2], split[3], "Innkeeper");
break;
case "-provisioner:":
PlaceNPC(split[1], split[2], split[3], "Provisioner", "Cobbler");
break;
case "-tailor:":
PlaceNPC(split[1], split[2], split[3], "Tailor", "Weaver", "TailorGuildmaster");
break;
case "-tavern:":
PlaceNPC(split[1], split[2], split[3], "Tavernkeeper", "Waiter", "Cook", "Barkeeper");
break;
case "-reagents:":
PlaceNPC(split[1], split[2], split[3], "Herbalist", "Alchemist", "CustomHairstylist");
break;
case "-fortuneteller:":
PlaceNPC(split[1], split[2], split[3], "FortuneTeller");
break;
case "-holymage:":
PlaceNPC(split[1], split[2], split[3], "HolyMage");
break;
case "-chivalrykeeper:":
PlaceNPC(split[1], split[2], split[3], "KeeperOfChivalry");
break;
case "-mage:":
PlaceNPC(split[1], split[2], split[3], "Mage", "Alchemist", "MageGuildmaster");
break;
case "-arms:":
PlaceNPC(split[1], split[2], split[3], "Armorer", "Weaponsmith");
break;
case "-tinker:":
PlaceNPC(split[1], split[2], split[3], "Tinker", "TinkerGuildmaster");
break;
case "-gypsystable:":
PlaceNPC(split[1], split[2], split[3], "GypsyAnimalTrainer");
break;
case "-stable:":
PlaceNPC(split[1], split[2], split[3], "AnimalTrainer");
break;
case "-blacksmith:":
PlaceNPC(split[1], split[2], split[3], "Blacksmith", "BlacksmithGuildmaster");
break;
case "-bowyer:":
case "-fletcher:":
PlaceNPC(split[1], split[2], split[3], "Bowyer");
break;
case "-carpenter:":
PlaceNPC(split[1], split[2], split[3], "Carpenter", "Architect", "RealEstateBroker");
break;
case "-butcher:":
PlaceNPC(split[1], split[2], split[3], "Butcher");
break;
case "-jeweler:":
PlaceNPC(split[1], split[2], split[3], "Jeweler");
break;
case "-tanner:":
PlaceNPC(split[1], split[2], split[3], "Tanner", "Furtrader");
break;
case "-bard:":
PlaceNPC(split[1], split[2], split[3], "Bard", "BardGuildmaster");
break;
case "-market:":
PlaceNPC(split[1], split[2], split[3], "Butcher", "Farmer");
break;
case "-library:":
PlaceNPC(split[1], split[2], split[3], "Scribe");
break;
case "-shipwright:":
PlaceNPC(split[1], split[2], split[3], "Shipwright", "Mapmaker");
break;
case "-docks:":
PlaceNPC(split[1], split[2], split[3], "Fisherman");
break;
case "-beekeeper:":
PlaceNPC(split[1], split[2], split[3], "Beekeeper");
break;
// Guilds & Misc
case "-tinkers guild:":
PlaceNPC(split[1], split[2], split[3], "TinkerGuildmaster");
break;
case "-blacksmiths guild:":
PlaceNPC(split[1], split[2], split[3], "BlacksmithGuildmaster");
break;
case "-sorcerors guild:":
PlaceNPC(split[1], split[2], split[3], "MageGuildmaster");
break;
case "-customs:": break;
case "-painter:": break;
case "-theater:": break;
case "-warriors guild:":
PlaceNPC(split[1], split[2], split[3], "WarriorGuildmaster");
break;
case "-archers guild:":
PlaceNPC(split[1], split[2], split[3], "RangerGuildmaster");
break;
case "-thieves guild:":
PlaceNPC(split[1], split[2], split[3], "ThiefGuildmaster");
break;
case "-miners guild:":
PlaceNPC(split[1], split[2], split[3], "MinerGuildmaster");
break;
case "-fishermans guild:":
PlaceNPC(split[1], split[2], split[3], "FisherGuildmaster");
break;
case "-merchants guild:":
PlaceNPC(split[1], split[2], split[3], "MerchantGuildmaster");
break;
case "-illusionists guild:": break;
case "-armourers guild:": break;
case "-sorcerers guild:": break;
case "-mages guild:":
PlaceNPC(split[1], split[2], split[3], "MageGuildmaster");
break;
case "-weapons guild:": break;
case "-bardic guild:":
PlaceNPC(split[1], split[2], split[3], "BardGuildmaster");
break;
case "-rogues guild:":
break;
// Skip
case "+landmark:":
case "-point of interest:":
case "+shrine:":
case "+moongate:":
case "+dungeon:":
case "+scenic:":
case "-gate:":
case "+Body of Water:":
case "+ruins:":
case "+teleporter:":
case "+Terrain:":
case "-exit:":
case "-bridge:":
case "-other:":
case "+champion:":
case "-stairs:":
case "-guild:":
case "+graveyard:":
case "+Island:":
case "+town:":
break;
/*default:
Console.WriteLine(split[0]);
break;*/
}
}
}
from.SendMessage("Done, added {0} spawners", m_Count);
}
public static void PlaceNPC(string sx, string sy, string sm, params string[] types)
{
if (types.Length == 0)
return;
int x = Utility.ToInt32(sx);
int y = Utility.ToInt32(sy);
int map = Utility.ToInt32(sm);
switch (map)
{
case 0: //Trammel and Felucca
MakeSpawner(types, x, y, Map.Felucca);
MakeSpawner(types, x, y, Map.Trammel);
break;
case 1: //Felucca
MakeSpawner(types, x, y, Map.Felucca);
break;
case 2:
MakeSpawner(types, x, y, Map.Trammel);
break;
case 3:
MakeSpawner(types, x, y, Map.Ilshenar);
break;
case 4:
MakeSpawner(types, x, y, Map.Malas);
break;
default:
Console.WriteLine("UOAM Vendor Parser: Warning, unknown map {0}", map);
break;
}
}
public static int GetSpawnerZ(int x, int y, Map map)
{
int z = map.GetAverageZ(x, y);
if (map.CanFit(x, y, z, 16, false, false))
return z;
for (int i = 1; i <= 5; ++i)
{
if (map.CanFit(x, y, z + i, 16, false, false))
return z + i;
if (map.CanFit(x, y, z - i, 16, false, false))
return z - i;
}
return z;
}
public static void ClearSpawners(int x, int y, int z, Map map)
{
IPooledEnumerable<Spawner> eable = map.GetItemsInRange<Spawner>(new Point3D(x, y, z), 0);
Queue<Spawner> m_ToDelete = new Queue<Spawner>();
foreach (Spawner item in eable)
if (item.Z == z)
m_ToDelete.Enqueue(item);
eable.Free();
while (m_ToDelete.Count > 0)
m_ToDelete.Dequeue().Delete();
}
private static void MakeSpawner(string[] types, int x, int y, Map map)
{
if (types.Length == 0)
return;
int z = GetSpawnerZ(x, y, map);
ClearSpawners(x, y, z, map);
Spawner sp = new Spawner
{
MinDelay = MinTime,
MaxDelay = MaxTime,
Team = Team,
HomeRange = HomeRange
};
int count = 0;
for (int i = 0; i < types.Length; ++i)
{
bool isGuildMaster = types[i].EndsWith("Guildmaster");
count += isGuildMaster ? 1 : NPCCount;
if (isGuildMaster)
count++;
sp.AddEntry(types[i], 100, isGuildMaster ? 1 : NPCCount);
}
sp.Count = count;
sp.MoveToWorld(new Point3D(x, y, z), map);
if (TotalRespawn)
{
sp.Respawn();
sp.BringToHome();
}
m_Count += types.Length;
}
}
}