fix: Fixes ML Escortables and codegens (#1525)

### Summary
- [X] Adds tot.json to ignore list.
- [X] Fixes bias in escortable random quest selection.
- [X] Adds New Haven specific destination/payment messages for escortable quests.
- [X] Codegens escortables.
This commit is contained in:
Kamron Batman 2023-09-30 14:11:48 -07:00 committed by GitHub
parent 7d05e3b1cb
commit d55406337f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
40 changed files with 2382 additions and 2480 deletions

1
.gitignore vendored
View file

@ -9,6 +9,7 @@
/Distribution/Configuration/modernuo.json
/Distribution/Configuration/email-settings.json
/Distribution/Configuration/throttles.json
/Distribution/Configuration/tot.json
/Distribution/Logs
/Distribution/Archives
/Distribution/Backups

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Mobiles.Actor"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Mobiles.Artist"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Mobiles.Banker"
}

View file

@ -0,0 +1,43 @@
{
"version": 2,
"type": "Server.Mobiles.BaseEscortable",
"properties": [
{
"name": "DestinationString",
"type": "string",
"rule": "PrimitiveTypeMigrationRule",
"ruleArguments": [
""
]
},
{
"name": "DeleteTimer",
"type": "Server.Timer",
"rule": "TimerMigrationRule",
"ruleArguments": [
"@TimerDrift"
]
},
{
"name": "MlQuestType",
"type": "System.Type",
"rule": "PrimitiveTypeMigrationRule"
},
{
"name": "MlQuestDestinationMessage",
"type": "Server.TextDefinition",
"rule": "PrimitiveUOTypeMigrationRule",
"ruleArguments": [
"TextDefinition"
]
},
{
"name": "MlQuestPaymentMessage",
"type": "Server.TextDefinition",
"rule": "PrimitiveUOTypeMigrationRule",
"ruleArguments": [
"TextDefinition"
]
}
]
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Mobiles.BrideGroom"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Mobiles.EscortableMage"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Mobiles.Gypsy"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Mobiles.HarborMaster"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Mobiles.Merchant"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Mobiles.Messenger"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Mobiles.Minter"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Mobiles.Ninja"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Mobiles.Noble"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Mobiles.Peasant"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Mobiles.Samurai"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Mobiles.Sculptor"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Mobiles.SeekerOfAdventure"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Mobiles.TownCrier"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Mobiles.Townfolk.Prisoner"
}

View file

@ -1091,31 +1091,9 @@ namespace Server.Mobiles
public HonorContext ReceivedHonorContext { get; set; }
public List<MLQuest> MLQuests
{
get
{
if (m_MLQuests == null)
{
if (StaticMLQuester)
{
m_MLQuests = MLQuestSystem.FindQuestList(GetType());
}
else
{
m_MLQuests = ConstructQuestList();
}
if (m_MLQuests == null)
{
// return EmptyList, but don't cache it (run construction again next time)
return MLQuestSystem.EmptyList;
}
}
return m_MLQuests;
}
}
public List<MLQuest> MLQuests =>
// Assign the quests if we don't have one, and if it is still null, return an empty list
(m_MLQuests ??= StaticMLQuester ? MLQuestSystem.FindQuestList(GetType()) : ConstructQuestList()) ?? MLQuestSystem.EmptyList;
public virtual MonsterAbility[] GetMonsterAbilities() => null;

View file

@ -1,65 +1,48 @@
using ModernUO.Serialization;
using Server.Items;
namespace Server.Mobiles
namespace Server.Mobiles;
[SerializationGenerator(0, false)]
public partial class Actor : BaseCreature
{
public class Actor : BaseCreature
[Constructible]
public Actor() : base(AIType.AI_Animal, FightMode.None)
{
[Constructible]
public Actor() : base(AIType.AI_Animal, FightMode.None)
InitStats(31, 41, 51);
SetSpeed(0.2, 0.4);
SpeechHue = Utility.RandomDyedHue();
Hue = Race.Human.RandomSkinHue();
if (Female = Utility.RandomBool())
{
InitStats(31, 41, 51);
SetSpeed(0.2, 0.4);
SpeechHue = Utility.RandomDyedHue();
Hue = Race.Human.RandomSkinHue();
if (Female = Utility.RandomBool())
{
Body = 0x191;
Name = NameList.RandomName("female");
AddItem(new FancyDress(Utility.RandomDyedHue()));
Title = "the actress";
}
else
{
Body = 0x190;
Name = NameList.RandomName("male");
AddItem(new LongPants(Utility.RandomNeutralHue()));
AddItem(new FancyShirt(Utility.RandomDyedHue()));
Title = "the actor";
}
AddItem(new Boots(Utility.RandomNeutralHue()));
Utility.AssignRandomHair(this);
Container pack = new Backpack();
pack.DropItem(new Gold(250, 300));
pack.Movable = false;
AddItem(pack);
Body = 0x191;
Name = NameList.RandomName("female");
AddItem(new FancyDress(Utility.RandomDyedHue()));
Title = "the actress";
}
else
{
Body = 0x190;
Name = NameList.RandomName("male");
AddItem(new LongPants(Utility.RandomNeutralHue()));
AddItem(new FancyShirt(Utility.RandomDyedHue()));
Title = "the actor";
}
public Actor(Serial serial) : base(serial)
{
}
AddItem(new Boots(Utility.RandomNeutralHue()));
public override bool ClickTitle => false;
Utility.AssignRandomHair(this);
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
Container pack = new Backpack();
writer.Write(0); // version
}
pack.DropItem(new Gold(250, 300));
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
pack.Movable = false;
var version = reader.ReadInt();
}
AddItem(pack);
}
public override bool ClickTitle => false;
}

View file

@ -1,68 +1,50 @@
using ModernUO.Serialization;
using Server.Items;
namespace Server.Mobiles
namespace Server.Mobiles;
[SerializationGenerator(0, false)]
public partial class Artist : BaseCreature
{
public class Artist : BaseCreature
[Constructible]
public Artist() : base(AIType.AI_Animal, FightMode.None)
{
[Constructible]
public Artist() : base(AIType.AI_Animal, FightMode.None)
InitStats(31, 41, 51);
SetSkill(SkillName.Healing, 36, 68);
SetSpeed(0.2, 0.4);
SpeechHue = Utility.RandomDyedHue();
Title = "the artist";
Hue = Race.Human.RandomSkinHue();
if (Female = Utility.RandomBool())
{
InitStats(31, 41, 51);
SetSkill(SkillName.Healing, 36, 68);
SetSpeed(0.2, 0.4);
SpeechHue = Utility.RandomDyedHue();
Title = "the artist";
Hue = Race.Human.RandomSkinHue();
if (Female = Utility.RandomBool())
{
Body = 0x191;
Name = NameList.RandomName("female");
}
else
{
Body = 0x190;
Name = NameList.RandomName("male");
}
AddItem(new Doublet(Utility.RandomDyedHue()));
AddItem(new Sandals(Utility.RandomNeutralHue()));
AddItem(new ShortPants(Utility.RandomNeutralHue()));
AddItem(new HalfApron(Utility.RandomDyedHue()));
Utility.AssignRandomHair(this);
Container pack = new Backpack();
pack.DropItem(new Gold(250, 300));
pack.Movable = false;
AddItem(pack);
Body = 0x191;
Name = NameList.RandomName("female");
}
else
{
Body = 0x190;
Name = NameList.RandomName("male");
}
public Artist(Serial serial)
: base(serial)
{
}
AddItem(new Doublet(Utility.RandomDyedHue()));
AddItem(new Sandals(Utility.RandomNeutralHue()));
AddItem(new ShortPants(Utility.RandomNeutralHue()));
AddItem(new HalfApron(Utility.RandomDyedHue()));
public override bool CanTeach => true;
Utility.AssignRandomHair(this);
public override bool ClickTitle => false;
Container pack = new Backpack();
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
pack.DropItem(new Gold(250, 300));
writer.Write(0); // version
}
pack.Movable = false;
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadInt();
}
AddItem(pack);
}
public override bool CanTeach => true;
public override bool ClickTitle => false;
}

View file

@ -1,481 +1,464 @@
using System;
using System.Collections.Generic;
using ModernUO.Serialization;
using Server.Accounting;
using Server.ContextMenus;
using Server.Items;
using Server.Network;
namespace Server.Mobiles
namespace Server.Mobiles;
[SerializationGenerator(0, false)]
public partial class Banker : BaseVendor
{
public class Banker : BaseVendor
private readonly List<SBInfo> m_SBInfos = new();
[Constructible]
public Banker() : base("the banker")
{
private readonly List<SBInfo> m_SBInfos = new();
}
[Constructible]
public Banker() : base("the banker")
protected override List<SBInfo> SBInfos => m_SBInfos;
public override NpcGuild NpcGuild => NpcGuild.MerchantsGuild;
public override void InitSBInfo()
{
m_SBInfos.Add(new SBBanker());
}
public static int GetBalance(Mobile m)
{
long balance = 0;
if (AccountGold.Enabled && m.Account != null)
{
balance = m.Account.GetTotalGold();
if (balance >= int.MaxValue)
{
return int.MaxValue;
}
}
public Banker(Serial serial) : base(serial)
Container bank = m.FindBankNoCreate();
if (bank != null)
{
foreach (var gold in bank.FindItemsByType<Gold>())
{
balance += gold.Amount;
}
if (balance >= int.MaxValue)
{
return int.MaxValue;
}
foreach (var check in bank.FindItemsByType<BankCheck>())
{
balance += check.Worth;
}
}
protected override List<SBInfo> SBInfos => m_SBInfos;
return (int)Math.Clamp(balance, 0, int.MaxValue);
}
public override NpcGuild NpcGuild => NpcGuild.MerchantsGuild;
public static int GetBalance(Mobile m, out List<Item> gold, out List<Item> checks)
{
long balance = 0;
gold = checks = new List<Item>();
public override void InitSBInfo()
if (AccountGold.Enabled && m.Account != null)
{
m_SBInfos.Add(new SBBanker());
balance = m.Account.GetTotalGold();
if (balance >= int.MaxValue)
{
return int.MaxValue;
}
}
public static int GetBalance(Mobile m)
Container bank = m.FindBankNoCreate();
if (bank != null)
{
long balance = 0;
if (AccountGold.Enabled && m.Account != null)
foreach (var g in bank.FindItemsByType<Gold>())
{
balance = m.Account.GetTotalGold();
if (balance >= int.MaxValue)
{
return int.MaxValue;
}
balance += g.Amount;
gold.Add(g);
}
Container bank = m.FindBankNoCreate();
if (bank != null)
if (balance >= int.MaxValue)
{
foreach (var gold in bank.FindItemsByType<Gold>())
{
balance += gold.Amount;
}
if (balance >= int.MaxValue)
{
return int.MaxValue;
}
foreach (var check in bank.FindItemsByType<BankCheck>())
{
balance += check.Worth;
}
return int.MaxValue;
}
return (int)Math.Clamp(balance, 0, int.MaxValue);
foreach (var bc in bank.FindItemsByType<BankCheck>())
{
balance += bc.Worth;
checks.Add(bc);
}
}
public static int GetBalance(Mobile m, out List<Item> gold, out List<Item> checks)
return (int)Math.Clamp(balance, 0, int.MaxValue);
}
public static bool Withdraw(Mobile from, int amount)
{
// If for whatever reason the TOL checks fail, we should still try old methods for withdrawing currency.
if (AccountGold.Enabled && from.Account?.WithdrawGold(amount) == true)
{
long balance = 0;
gold = checks = new List<Item>();
if (AccountGold.Enabled && m.Account != null)
{
balance = m.Account.GetTotalGold();
if (balance >= int.MaxValue)
{
return int.MaxValue;
}
}
Container bank = m.FindBankNoCreate();
if (bank != null)
{
foreach (var g in bank.FindItemsByType<Gold>())
{
balance += g.Amount;
gold.Add(g);
}
if (balance >= int.MaxValue)
{
return int.MaxValue;
}
foreach (var bc in bank.FindItemsByType<BankCheck>())
{
balance += bc.Worth;
checks.Add(bc);
}
}
return (int)Math.Clamp(balance, 0, int.MaxValue);
}
public static bool Withdraw(Mobile from, int amount)
{
// If for whatever reason the TOL checks fail, we should still try old methods for withdrawing currency.
if (AccountGold.Enabled && from.Account?.WithdrawGold(amount) == true)
{
return true;
}
var balance = GetBalance(from, out var gold, out var checks);
if (balance < amount)
{
return false;
}
for (var i = 0; amount > 0 && i < gold.Count; ++i)
{
if (gold[i].Amount <= amount)
{
amount -= gold[i].Amount;
gold[i].Delete();
}
else
{
gold[i].Amount -= amount;
amount = 0;
}
}
for (var i = 0; amount > 0 && i < checks.Count; ++i)
{
var check = (BankCheck)checks[i];
if (check.Worth <= amount)
{
amount -= check.Worth;
check.Delete();
}
else
{
check.Worth -= amount;
amount = 0;
}
}
return true;
}
public static bool Deposit(Mobile from, int amount)
var balance = GetBalance(from, out var gold, out var checks);
if (balance < amount)
{
// If for whatever reason the TOL checks fail, we should still try old methods for depositing currency.
if (AccountGold.Enabled && from.Account?.DepositGold(amount) == true)
return false;
}
for (var i = 0; amount > 0 && i < gold.Count; ++i)
{
if (gold[i].Amount <= amount)
{
return true;
amount -= gold[i].Amount;
gold[i].Delete();
}
var box = from.FindBankNoCreate();
if (box == null)
else
{
return false;
gold[i].Amount -= amount;
amount = 0;
}
}
var items = new List<Item>();
for (var i = 0; amount > 0 && i < checks.Count; ++i)
{
var check = (BankCheck)checks[i];
while (amount > 0)
if (check.Worth <= amount)
{
Item item;
if (amount < 5000)
{
item = new Gold(amount);
amount = 0;
}
else if (amount <= 1000000)
{
item = new BankCheck(amount);
amount = 0;
}
else
{
item = new BankCheck(1000000);
amount -= 1000000;
}
if (box.TryDropItem(from, item, false))
{
items.Add(item);
}
else
{
item.Delete();
foreach (var curItem in items)
{
curItem.Delete();
}
return false;
}
amount -= check.Worth;
check.Delete();
}
else
{
check.Worth -= amount;
amount = 0;
}
}
return true;
}
public static bool Deposit(Mobile from, int amount)
{
// If for whatever reason the TOL checks fail, we should still try old methods for depositing currency.
if (AccountGold.Enabled && from.Account?.DepositGold(amount) == true)
{
return true;
}
public static int DepositUpTo(Mobile from, int amount)
var box = from.FindBankNoCreate();
if (box == null)
{
// If for whatever reason the TOL checks fail, we should still try old methods for depositing currency.
if (AccountGold.Enabled && from.Account?.DepositGold(amount) == true)
{
return amount;
}
var box = from.FindBankNoCreate();
if (box == null)
{
return 0;
}
var amountLeft = amount;
while (amountLeft > 0)
{
Item item;
int amountGiven;
if (amountLeft < 5000)
{
item = new Gold(amountLeft);
amountGiven = amountLeft;
}
else if (amountLeft <= 1000000)
{
item = new BankCheck(amountLeft);
amountGiven = amountLeft;
}
else
{
item = new BankCheck(1000000);
amountGiven = 1000000;
}
if (box.TryDropItem(from, item, false))
{
amountLeft -= amountGiven;
}
else
{
item.Delete();
break;
}
}
return amount - amountLeft;
return false;
}
public static void Deposit(Container cont, int amount)
var items = new List<Item>();
while (amount > 0)
{
while (amount > 0)
Item item;
if (amount < 5000)
{
Item item;
item = new Gold(amount);
amount = 0;
}
else if (amount <= 1000000)
{
item = new BankCheck(amount);
amount = 0;
}
else
{
item = new BankCheck(1000000);
amount -= 1000000;
}
if (amount < 5000)
if (box.TryDropItem(from, item, false))
{
items.Add(item);
}
else
{
item.Delete();
foreach (var curItem in items)
{
item = new Gold(amount);
amount = 0;
}
else if (amount <= 1000000)
{
item = new BankCheck(amount);
amount = 0;
}
else
{
item = new BankCheck(1000000);
amount -= 1000000;
curItem.Delete();
}
cont.DropItem(item);
return false;
}
}
public override bool HandlesOnSpeech(Mobile from) => from.InRange(Location, 12) || base.HandlesOnSpeech(from);
return true;
}
public override void OnSpeech(SpeechEventArgs e)
public static int DepositUpTo(Mobile from, int amount)
{
// If for whatever reason the TOL checks fail, we should still try old methods for depositing currency.
if (AccountGold.Enabled && from.Account?.DepositGold(amount) == true)
{
if (!e.Handled && e.Mobile.InRange(Location, 12))
return amount;
}
var box = from.FindBankNoCreate();
if (box == null)
{
return 0;
}
var amountLeft = amount;
while (amountLeft > 0)
{
Item item;
int amountGiven;
if (amountLeft < 5000)
{
for (var i = 0; i < e.Keywords.Length; ++i)
item = new Gold(amountLeft);
amountGiven = amountLeft;
}
else if (amountLeft <= 1000000)
{
item = new BankCheck(amountLeft);
amountGiven = amountLeft;
}
else
{
item = new BankCheck(1000000);
amountGiven = 1000000;
}
if (box.TryDropItem(from, item, false))
{
amountLeft -= amountGiven;
}
else
{
item.Delete();
break;
}
}
return amount - amountLeft;
}
public static void Deposit(Container cont, int amount)
{
while (amount > 0)
{
Item item;
if (amount < 5000)
{
item = new Gold(amount);
amount = 0;
}
else if (amount <= 1000000)
{
item = new BankCheck(amount);
amount = 0;
}
else
{
item = new BankCheck(1000000);
amount -= 1000000;
}
cont.DropItem(item);
}
}
public override bool HandlesOnSpeech(Mobile from) => from.InRange(Location, 12) || base.HandlesOnSpeech(from);
public override void OnSpeech(SpeechEventArgs e)
{
if (!e.Handled && e.Mobile.InRange(Location, 12))
{
for (var i = 0; i < e.Keywords.Length; ++i)
{
var keyword = e.Keywords[i];
switch (keyword)
{
var keyword = e.Keywords[i];
case 0x0000: // *withdraw*
{
e.Handled = true;
switch (keyword)
{
case 0x0000: // *withdraw*
if (e.Mobile.Criminal)
{
e.Handled = true;
if (e.Mobile.Criminal)
{
Say(500389); // I will not do business with a criminal!
break;
}
var split = e.Speech.Split(' ');
if (split.Length >= 2)
{
var pack = e.Mobile.Backpack;
if (!int.TryParse(split[1], out var amount))
{
break;
}
if (!Core.ML && amount > 5000 || Core.ML && amount > 60000)
{
Say(500381); // Thou canst not withdraw so much at one time!
}
else if (pack?.Deleted != false || !(pack.TotalWeight < pack.MaxWeight) ||
!(pack.TotalItems < pack.MaxItems))
{
Say(1048147); // Your backpack can't hold anything else.
}
else if (amount > 0)
{
var box = e.Mobile.FindBankNoCreate();
if (box == null || !Withdraw(e.Mobile, amount))
{
Say(500384); // Ah, art thou trying to fool me? Thou hast not so much gold!
}
else
{
pack.DropItem(new Gold(amount));
Say(1010005); // Thou hast withdrawn gold from thy account.
}
}
}
Say(500389); // I will not do business with a criminal!
break;
}
case 0x0001: // *balance*
var split = e.Speech.Split(' ');
if (split.Length >= 2)
{
e.Handled = true;
var pack = e.Mobile.Backpack;
if (e.Mobile.Criminal)
{
Say(500389); // I will not do business with a criminal!
break;
}
if (AccountGold.Enabled && e.Mobile.Account != null)
{
Say(
1155855,
$"{e.Mobile.Account.TotalPlat:#,0}\t{e.Mobile.Account.TotalGold:#,0}"
); // Thy current bank balance is ~1_AMOUNT~ platinum and ~2_AMOUNT~ gold.
}
else
{
// Thy current bank balance is ~1_AMOUNT~ gold.
Say(1042759, $"{GetBalance(e.Mobile):N0}");
}
break;
}
case 0x0002: // *bank*
{
e.Handled = true;
if (e.Mobile.Criminal)
{
Say(500378); // Thou art a criminal and cannot access thy bank box.
break;
}
e.Mobile.BankBox.Open();
break;
}
case 0x0003: // *check*
{
e.Handled = true;
if (AccountGold.Enabled)
if (!int.TryParse(split[1], out var amount))
{
break;
}
if (e.Mobile.Criminal)
if (!Core.ML && amount > 5000 || Core.ML && amount > 60000)
{
Say(500389); // I will not do business with a criminal!
break;
Say(500381); // Thou canst not withdraw so much at one time!
}
var split = e.Speech.Split(' ');
if (split.Length >= 2)
else if (pack?.Deleted != false || !(pack.TotalWeight < pack.MaxWeight) ||
!(pack.TotalItems < pack.MaxItems))
{
if (!int.TryParse(split[1], out var amount))
Say(1048147); // Your backpack can't hold anything else.
}
else if (amount > 0)
{
var box = e.Mobile.FindBankNoCreate();
if (box == null || !Withdraw(e.Mobile, amount))
{
break;
}
if (amount < 5000)
{
Say(1010006); // We cannot create checks for such a paltry amount of gold!
}
else if (amount > 1000000)
{
Say(1010007); // Our policies prevent us from creating checks worth that much!
Say(500384); // Ah, art thou trying to fool me? Thou hast not so much gold!
}
else
{
var check = new BankCheck(amount);
pack.DropItem(new Gold(amount));
var box = e.Mobile.BankBox;
if (!box.TryDropItem(e.Mobile, check, false))
{
Say(500386); // There's not enough room in your bankbox for the check!
check.Delete();
}
else if (!box.ConsumeTotal(typeof(Gold), amount))
{
Say(500384); // Ah, art thou trying to fool me? Thou hast not so much gold!
check.Delete();
}
else
{
Say(
1042673, // Into your bank box I have placed a check in the amount of:
AffixType.Append,
amount.ToString(),
""
);
}
Say(1010005); // Thou hast withdrawn gold from thy account.
}
}
}
break;
}
case 0x0001: // *balance*
{
e.Handled = true;
if (e.Mobile.Criminal)
{
Say(500389); // I will not do business with a criminal!
break;
}
}
if (AccountGold.Enabled && e.Mobile.Account != null)
{
Say(
1155855,
$"{e.Mobile.Account.TotalPlat:#,0}\t{e.Mobile.Account.TotalGold:#,0}"
); // Thy current bank balance is ~1_AMOUNT~ platinum and ~2_AMOUNT~ gold.
}
else
{
// Thy current bank balance is ~1_AMOUNT~ gold.
Say(1042759, $"{GetBalance(e.Mobile):N0}");
}
break;
}
case 0x0002: // *bank*
{
e.Handled = true;
if (e.Mobile.Criminal)
{
Say(500378); // Thou art a criminal and cannot access thy bank box.
break;
}
e.Mobile.BankBox.Open();
break;
}
case 0x0003: // *check*
{
e.Handled = true;
if (AccountGold.Enabled)
{
break;
}
if (e.Mobile.Criminal)
{
Say(500389); // I will not do business with a criminal!
break;
}
var split = e.Speech.Split(' ');
if (split.Length >= 2)
{
if (!int.TryParse(split[1], out var amount))
{
break;
}
if (amount < 5000)
{
Say(1010006); // We cannot create checks for such a paltry amount of gold!
}
else if (amount > 1000000)
{
Say(1010007); // Our policies prevent us from creating checks worth that much!
}
else
{
var check = new BankCheck(amount);
var box = e.Mobile.BankBox;
if (!box.TryDropItem(e.Mobile, check, false))
{
Say(500386); // There's not enough room in your bankbox for the check!
check.Delete();
}
else if (!box.ConsumeTotal(typeof(Gold), amount))
{
Say(500384); // Ah, art thou trying to fool me? Thou hast not so much gold!
check.Delete();
}
else
{
Say(
1042673, // Into your bank box I have placed a check in the amount of:
AffixType.Append,
amount.ToString(),
""
);
}
}
}
break;
}
}
}
base.OnSpeech(e);
}
public override void AddCustomContextEntries(Mobile from, List<ContextMenuEntry> list)
base.OnSpeech(e);
}
public override void AddCustomContextEntries(Mobile from, List<ContextMenuEntry> list)
{
if (from.Alive)
{
if (from.Alive)
{
list.Add(new OpenBankEntry(this));
}
base.AddCustomContextEntries(from, list);
list.Add(new OpenBankEntry(this));
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadInt();
}
base.AddCustomContextEntries(from, list);
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,93 +1,76 @@
using ModernUO.Serialization;
using Server.Items;
namespace Server.Mobiles
namespace Server.Mobiles;
[SerializationGenerator(0, false)]
public partial class BrideGroom : BaseEscortable
{
public class BrideGroom : BaseEscortable
[Constructible]
public BrideGroom()
{
[Constructible]
public BrideGroom()
if (Female)
{
if (Female)
{
Title = "the bride";
}
else
{
Title = "the groom";
}
Title = "the bride";
}
public BrideGroom(Serial serial) : base(serial)
else
{
}
public override bool CanTeach => true;
public override bool ClickTitle => false; // Do not display 'the groom' when single-clicking
private static int GetRandomHue()
{
return Utility.Random(6) switch
{
0 => 0,
1 => Utility.RandomBlueHue(),
2 => Utility.RandomGreenHue(),
3 => Utility.RandomRedHue(),
4 => Utility.RandomYellowHue(),
5 => Utility.RandomNeutralHue(),
_ => 0
};
}
public override void InitOutfit()
{
if (Female)
{
AddItem(new FancyDress());
}
else
{
AddItem(new FancyShirt());
}
var lowHue = GetRandomHue();
AddItem(new LongPants(lowHue));
if (Female)
{
AddItem(new Shoes(lowHue));
}
else
{
AddItem(new Boots(lowHue));
}
if (Utility.RandomBool())
{
HairItemID = 0x203B;
}
else
{
HairItemID = 0x203C;
}
HairHue = Race.RandomHairHue();
PackGold(200, 250);
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadInt();
Title = "the groom";
}
}
public override bool CanTeach => true;
public override bool ClickTitle => false; // Do not display 'the groom' when single-clicking
private static int GetRandomHue()
{
return Utility.Random(6) switch
{
0 => 0,
1 => Utility.RandomBlueHue(),
2 => Utility.RandomGreenHue(),
3 => Utility.RandomRedHue(),
4 => Utility.RandomYellowHue(),
5 => Utility.RandomNeutralHue(),
_ => 0
};
}
public override void InitOutfit()
{
if (Female)
{
AddItem(new FancyDress());
}
else
{
AddItem(new FancyShirt());
}
var lowHue = GetRandomHue();
AddItem(new LongPants(lowHue));
if (Female)
{
AddItem(new Shoes(lowHue));
}
else
{
AddItem(new Boots(lowHue));
}
if (Utility.RandomBool())
{
HairItemID = 0x203B;
}
else
{
HairItemID = 0x203C;
}
HairHue = Race.RandomHairHue();
PackGold(200, 250);
}
}

View file

@ -1,75 +1,58 @@
using ModernUO.Serialization;
using Server.Items;
namespace Server.Mobiles
namespace Server.Mobiles;
[SerializationGenerator(0, false)]
public partial class EscortableMage : BaseEscortable
{
public class EscortableMage : BaseEscortable
[Constructible]
public EscortableMage()
{
[Constructible]
public EscortableMage()
{
Title = "the mage";
Title = "the mage";
SetSkill(SkillName.EvalInt, 80.0, 100.0);
SetSkill(SkillName.Inscribe, 80.0, 100.0);
SetSkill(SkillName.Magery, 80.0, 100.0);
SetSkill(SkillName.Meditation, 80.0, 100.0);
SetSkill(SkillName.MagicResist, 80.0, 100.0);
SetSkill(SkillName.EvalInt, 80.0, 100.0);
SetSkill(SkillName.Inscribe, 80.0, 100.0);
SetSkill(SkillName.Magery, 80.0, 100.0);
SetSkill(SkillName.Meditation, 80.0, 100.0);
SetSkill(SkillName.MagicResist, 80.0, 100.0);
}
public override bool CanTeach => true;
public override bool ClickTitle => false; // Do not display 'the mage' when single-clicking
private static int GetRandomHue()
{
return Utility.Random(5) switch
{
0 => Utility.RandomBlueHue(),
1 => Utility.RandomGreenHue(),
2 => Utility.RandomRedHue(),
3 => Utility.RandomYellowHue(),
4 => Utility.RandomNeutralHue(),
_ => Utility.RandomBlueHue()
};
}
public override void InitOutfit()
{
AddItem(new Robe(GetRandomHue()));
var lowHue = GetRandomHue();
AddItem(new ShortPants(lowHue));
if (Female)
{
AddItem(new ThighBoots(lowHue));
}
else
{
AddItem(new Boots(lowHue));
}
public EscortableMage(Serial serial) : base(serial)
{
}
Utility.AssignRandomHair(this);
public override bool CanTeach => true;
public override bool ClickTitle => false; // Do not display 'the mage' when single-clicking
private static int GetRandomHue()
{
return Utility.Random(5) switch
{
0 => Utility.RandomBlueHue(),
1 => Utility.RandomGreenHue(),
2 => Utility.RandomRedHue(),
3 => Utility.RandomYellowHue(),
4 => Utility.RandomNeutralHue(),
_ => Utility.RandomBlueHue()
};
}
public override void InitOutfit()
{
AddItem(new Robe(GetRandomHue()));
var lowHue = GetRandomHue();
AddItem(new ShortPants(lowHue));
if (Female)
{
AddItem(new ThighBoots(lowHue));
}
else
{
AddItem(new Boots(lowHue));
}
Utility.AssignRandomHair(this);
PackGold(200, 250);
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadInt();
}
PackGold(200, 250);
}
}

View file

@ -1,74 +1,56 @@
using ModernUO.Serialization;
using Server.Items;
namespace Server.Mobiles
namespace Server.Mobiles;
[SerializationGenerator(0, false)]
public partial class Gypsy : BaseCreature
{
public class Gypsy : BaseCreature
[Constructible]
public Gypsy() : base(AIType.AI_Animal, FightMode.None)
{
[Constructible]
public Gypsy() : base(AIType.AI_Animal, FightMode.None)
InitStats(31, 41, 51);
SetSkill(SkillName.Cooking, 65, 88);
SetSkill(SkillName.Snooping, 65, 88);
SetSkill(SkillName.Stealing, 65, 88);
SetSpeed(0.2, 0.4);
SpeechHue = Utility.RandomDyedHue();
Hue = Race.Human.RandomSkinHue();
if (Female = Utility.RandomBool())
{
InitStats(31, 41, 51);
SetSkill(SkillName.Cooking, 65, 88);
SetSkill(SkillName.Snooping, 65, 88);
SetSkill(SkillName.Stealing, 65, 88);
SetSpeed(0.2, 0.4);
SpeechHue = Utility.RandomDyedHue();
Hue = Race.Human.RandomSkinHue();
if (Female = Utility.RandomBool())
{
Body = 0x191;
Name = NameList.RandomName("female");
AddItem(new Kilt(Utility.RandomDyedHue()));
AddItem(new Shirt(Utility.RandomDyedHue()));
AddItem(new ThighBoots());
Title = "the gypsy";
}
else
{
Body = 0x190;
Name = NameList.RandomName("male");
AddItem(new ShortPants(Utility.RandomNeutralHue()));
AddItem(new Shirt(Utility.RandomDyedHue()));
AddItem(new Sandals());
Title = "the gypsy";
}
AddItem(new Bandana(Utility.RandomDyedHue()));
AddItem(new Dagger());
Utility.AssignRandomHair(this);
Container pack = new Backpack();
pack.DropItem(new Gold(250, 300));
pack.Movable = false;
AddItem(pack);
Body = 0x191;
Name = NameList.RandomName("female");
AddItem(new Kilt(Utility.RandomDyedHue()));
AddItem(new Shirt(Utility.RandomDyedHue()));
AddItem(new ThighBoots());
Title = "the gypsy";
}
else
{
Body = 0x190;
Name = NameList.RandomName("male");
AddItem(new ShortPants(Utility.RandomNeutralHue()));
AddItem(new Shirt(Utility.RandomDyedHue()));
AddItem(new Sandals());
Title = "the gypsy";
}
public Gypsy(Serial serial)
: base(serial)
{
}
AddItem(new Bandana(Utility.RandomDyedHue()));
AddItem(new Dagger());
public override bool CanTeach => true;
public override bool ClickTitle => false;
Utility.AssignRandomHair(this);
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
Container pack = new Backpack();
writer.Write(0); // version
}
pack.DropItem(new Gold(250, 300));
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
pack.Movable = false;
var version = reader.ReadInt();
}
AddItem(pack);
}
public override bool CanTeach => true;
public override bool ClickTitle => false;
}

View file

@ -1,70 +1,52 @@
using ModernUO.Serialization;
using Server.Items;
namespace Server.Mobiles
namespace Server.Mobiles;
[SerializationGenerator(0, false)]
public partial class HarborMaster : BaseCreature
{
public class HarborMaster : BaseCreature
[Constructible]
public HarborMaster() : base(AIType.AI_Animal, FightMode.None)
{
[Constructible]
public HarborMaster() : base(AIType.AI_Animal, FightMode.None)
InitStats(31, 41, 51);
SetSkill(SkillName.Mining, 36, 68);
SetSpeed(0.2, 0.4);
SpeechHue = Utility.RandomDyedHue();
Hue = Race.Human.RandomSkinHue();
Blessed = true;
if (Female = Utility.RandomBool())
{
InitStats(31, 41, 51);
SetSkill(SkillName.Mining, 36, 68);
SetSpeed(0.2, 0.4);
SpeechHue = Utility.RandomDyedHue();
Hue = Race.Human.RandomSkinHue();
Blessed = true;
if (Female = Utility.RandomBool())
{
Body = 0x191;
Name = NameList.RandomName("female");
Title = "the Harbor Mistress";
}
else
{
Body = 0x190;
Name = NameList.RandomName("male");
Title = "the Harbor Master";
}
AddItem(new Shirt(Utility.RandomDyedHue()));
AddItem(new Boots());
AddItem(new LongPants(Utility.RandomNeutralHue()));
AddItem(new QuarterStaff());
Utility.AssignRandomHair(this);
Container pack = new Backpack();
pack.DropItem(new Gold(250, 300));
pack.Movable = false;
AddItem(pack);
Body = 0x191;
Name = NameList.RandomName("female");
Title = "the Harbor Mistress";
}
else
{
Body = 0x190;
Name = NameList.RandomName("male");
Title = "the Harbor Master";
}
public HarborMaster(Serial serial)
: base(serial)
{
}
AddItem(new Shirt(Utility.RandomDyedHue()));
AddItem(new Boots());
AddItem(new LongPants(Utility.RandomNeutralHue()));
AddItem(new QuarterStaff());
public override bool CanTeach => false;
Utility.AssignRandomHair(this);
public override bool ClickTitle => false;
Container pack = new Backpack();
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
pack.DropItem(new Gold(250, 300));
writer.Write(0); // version
}
pack.Movable = false;
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadInt();
}
AddItem(pack);
}
public override bool CanTeach => false;
public override bool ClickTitle => false;
}

View file

@ -1,90 +1,72 @@
using ModernUO.Serialization;
using Server.Items;
namespace Server.Mobiles
namespace Server.Mobiles;
[SerializationGenerator(0, false)]
public partial class Merchant : BaseEscortable
{
public class Merchant : BaseEscortable
[Constructible]
public Merchant()
{
[Constructible]
public Merchant()
Title = "the merchant";
SetSkill(SkillName.ItemID, 55.0, 78.0);
SetSkill(SkillName.ArmsLore, 55, 78);
}
public override bool CanTeach => true;
public override bool ClickTitle => false; // Do not display 'the merchant' when single-clicking
private static int GetRandomHue()
{
return Utility.Random(6) switch
{
Title = "the merchant";
SetSkill(SkillName.ItemID, 55.0, 78.0);
SetSkill(SkillName.ArmsLore, 55, 78);
0 => 0,
1 => Utility.RandomBlueHue(),
2 => Utility.RandomGreenHue(),
3 => Utility.RandomRedHue(),
4 => Utility.RandomYellowHue(),
5 => Utility.RandomNeutralHue(),
_ => 0
};
}
public override void InitOutfit()
{
if (Female)
{
AddItem(new PlainDress());
}
else
{
AddItem(new Shirt(GetRandomHue()));
}
public Merchant(Serial serial)
: base(serial)
var lowHue = GetRandomHue();
AddItem(new ThighBoots());
if (Female)
{
AddItem(new FancyDress(lowHue));
}
else
{
AddItem(new FancyShirt(lowHue));
}
public override bool CanTeach => true;
public override bool ClickTitle => false; // Do not display 'the merchant' when single-clicking
AddItem(new LongPants(lowHue));
private static int GetRandomHue()
if (!Female)
{
return Utility.Random(6) switch
{
0 => 0,
1 => Utility.RandomBlueHue(),
2 => Utility.RandomGreenHue(),
3 => Utility.RandomRedHue(),
4 => Utility.RandomYellowHue(),
5 => Utility.RandomNeutralHue(),
_ => 0
};
AddItem(new BodySash(lowHue));
}
public override void InitOutfit()
{
if (Female)
{
AddItem(new PlainDress());
}
else
{
AddItem(new Shirt(GetRandomHue()));
}
// if (!Female)
// AddItem( new Longsword() );
var lowHue = GetRandomHue();
Utility.AssignRandomHair(this);
AddItem(new ThighBoots());
if (Female)
{
AddItem(new FancyDress(lowHue));
}
else
{
AddItem(new FancyShirt(lowHue));
}
AddItem(new LongPants(lowHue));
if (!Female)
{
AddItem(new BodySash(lowHue));
}
// if (!Female)
// AddItem( new Longsword() );
Utility.AssignRandomHair(this);
PackGold(200, 250);
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadInt();
}
PackGold(200, 250);
}
}

View file

@ -1,76 +1,59 @@
using ModernUO.Serialization;
using Server.Items;
namespace Server.Mobiles
namespace Server.Mobiles;
[SerializationGenerator(0, false)]
public partial class Messenger : BaseEscortable
{
public class Messenger : BaseEscortable
[Constructible]
public Messenger() => Title = "the messenger";
public override bool CanTeach => true;
public override bool ClickTitle => false; // Do not display 'the messenger' when single-clicking
private static int GetRandomHue()
{
[Constructible]
public Messenger() => Title = "the messenger";
public Messenger(Serial serial) : base(serial)
return Utility.Random(6) switch
{
1 => Utility.RandomBlueHue(),
2 => Utility.RandomGreenHue(),
3 => Utility.RandomRedHue(),
4 => Utility.RandomYellowHue(),
5 => Utility.RandomNeutralHue(),
_ => 0
};
}
public override void InitOutfit()
{
if (Female)
{
AddItem(new PlainDress());
}
else
{
AddItem(new Shirt(GetRandomHue()));
}
public override bool CanTeach => true;
public override bool ClickTitle => false; // Do not display 'the messenger' when single-clicking
var lowHue = GetRandomHue();
private static int GetRandomHue()
AddItem(new ShortPants(lowHue));
if (Female)
{
return Utility.Random(6) switch
{
1 => Utility.RandomBlueHue(),
2 => Utility.RandomGreenHue(),
3 => Utility.RandomRedHue(),
4 => Utility.RandomYellowHue(),
5 => Utility.RandomNeutralHue(),
_ => 0
};
AddItem(new Boots(lowHue));
}
else
{
AddItem(new Shoes(lowHue));
}
public override void InitOutfit()
{
if (Female)
{
AddItem(new PlainDress());
}
else
{
AddItem(new Shirt(GetRandomHue()));
}
var randomHair = Utility.Random(4);
HairItemID = randomHair == 4 ? 0x203B : 0x2048 + randomHair;
var lowHue = GetRandomHue();
HairHue = Race.RandomHairHue();
AddItem(new ShortPants(lowHue));
if (Female)
{
AddItem(new Boots(lowHue));
}
else
{
AddItem(new Shoes(lowHue));
}
var randomHair = Utility.Random(4);
HairItemID = randomHair == 4 ? 0x203B : 0x2048 + randomHair;
HairHue = Race.RandomHairHue();
PackGold(200, 250);
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadInt();
}
PackGold(200, 250);
}
}

View file

@ -1,28 +1,12 @@
namespace Server.Mobiles
using ModernUO.Serialization;
namespace Server.Mobiles;
[SerializationGenerator(0, false)]
public partial class Minter : Banker
{
public class Minter : Banker
{
[Constructible]
public Minter() => Title = "the minter";
[Constructible]
public Minter() => Title = "the minter";
public Minter(Serial serial) : base(serial)
{
}
public override NpcGuild NpcGuild => NpcGuild.MerchantsGuild;
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadInt();
}
}
public override NpcGuild NpcGuild => NpcGuild.MerchantsGuild;
}

View file

@ -1,78 +1,61 @@
using ModernUO.Serialization;
using Server.Items;
namespace Server.Mobiles
namespace Server.Mobiles;
[SerializationGenerator(0)]
public partial class Ninja : BaseCreature
{
public class Ninja : BaseCreature
[Constructible]
public Ninja() : base(AIType.AI_Melee, FightMode.Aggressor)
{
[Constructible]
public Ninja() : base(AIType.AI_Melee, FightMode.Aggressor)
Title = "the ninja";
InitStats(100, 100, 25);
SetSkill(SkillName.Fencing, 64.0, 80.0);
SetSkill(SkillName.Macing, 64.0, 80.0);
SetSkill(SkillName.Ninjitsu, 60.0, 80.0);
SetSkill(SkillName.Parry, 64.0, 80.0);
SetSkill(SkillName.Tactics, 64.0, 85.0);
SetSkill(SkillName.Swords, 64.0, 85.0);
SetSpeed(0.2, 0.4);
SpeechHue = Utility.RandomDyedHue();
Hue = Race.Human.RandomSkinHue();
if (Female = Utility.RandomBool())
{
Title = "the ninja";
InitStats(100, 100, 25);
SetSkill(SkillName.Fencing, 64.0, 80.0);
SetSkill(SkillName.Macing, 64.0, 80.0);
SetSkill(SkillName.Ninjitsu, 60.0, 80.0);
SetSkill(SkillName.Parry, 64.0, 80.0);
SetSkill(SkillName.Tactics, 64.0, 85.0);
SetSkill(SkillName.Swords, 64.0, 85.0);
SetSpeed(0.2, 0.4);
SpeechHue = Utility.RandomDyedHue();
Hue = Race.Human.RandomSkinHue();
if (Female = Utility.RandomBool())
{
Body = 0x191;
Name = NameList.RandomName("female");
}
else
{
Body = 0x190;
Name = NameList.RandomName("male");
}
if (!Female)
{
AddItem(new LeatherNinjaHood());
}
AddItem(new LeatherNinjaPants());
AddItem(new LeatherNinjaBelt());
AddItem(new LeatherNinjaJacket());
AddItem(new NinjaTabi());
var hairHue = Utility.RandomNondyedHue();
Utility.AssignRandomHair(this, hairHue);
if (Utility.Random(7) != 0)
{
Utility.AssignRandomFacialHair(this, hairHue);
}
PackGold(250, 300);
Body = 0x191;
Name = NameList.RandomName("female");
}
else
{
Body = 0x190;
Name = NameList.RandomName("male");
}
public Ninja(Serial serial) : base(serial)
if (!Female)
{
AddItem(new LeatherNinjaHood());
}
public override bool CanTeach => true;
public override bool ClickTitle => false;
AddItem(new LeatherNinjaPants());
AddItem(new LeatherNinjaBelt());
AddItem(new LeatherNinjaJacket());
AddItem(new NinjaTabi());
public override void Serialize(IGenericWriter writer)
var hairHue = Utility.RandomNondyedHue();
Utility.AssignRandomHair(this, hairHue);
if (Utility.Random(7) != 0)
{
base.Serialize(writer);
writer.WriteEncodedInt(0); // version
Utility.AssignRandomFacialHair(this, hairHue);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadEncodedInt();
}
PackGold(250, 300);
}
public override bool CanTeach => true;
public override bool ClickTitle => false;
}

View file

@ -1,92 +1,75 @@
using ModernUO.Serialization;
using Server.Items;
namespace Server.Mobiles
namespace Server.Mobiles;
[SerializationGenerator(0, false)]
public partial class Noble : BaseEscortable
{
public class Noble : BaseEscortable
[Constructible]
public Noble()
{
[Constructible]
public Noble()
{
Title = "the noble";
Title = "the noble";
SetSkill(SkillName.Parry, 80.0, 100.0);
SetSkill(SkillName.Swords, 80.0, 100.0);
SetSkill(SkillName.Tactics, 80.0, 100.0);
SetSkill(SkillName.Parry, 80.0, 100.0);
SetSkill(SkillName.Swords, 80.0, 100.0);
SetSkill(SkillName.Tactics, 80.0, 100.0);
}
public override bool CanTeach => true;
public override bool ClickTitle => false; // Do not display 'the noble' when single-clicking
private static int GetRandomHue()
{
return Utility.Random(6) switch
{
1 => Utility.RandomBlueHue(),
2 => Utility.RandomGreenHue(),
3 => Utility.RandomRedHue(),
4 => Utility.RandomYellowHue(),
5 => Utility.RandomNeutralHue(),
_ => 0
};
}
public override void InitOutfit()
{
if (Female)
{
AddItem(new FancyDress());
}
else
{
AddItem(new FancyShirt(GetRandomHue()));
}
public Noble(Serial serial) : base(serial)
var lowHue = GetRandomHue();
AddItem(new ShortPants(lowHue));
if (Female)
{
AddItem(new ThighBoots(lowHue));
}
else
{
AddItem(new Boots(lowHue));
}
public override bool CanTeach => true;
public override bool ClickTitle => false; // Do not display 'the noble' when single-clicking
private static int GetRandomHue()
if (!Female)
{
return Utility.Random(6) switch
{
1 => Utility.RandomBlueHue(),
2 => Utility.RandomGreenHue(),
3 => Utility.RandomRedHue(),
4 => Utility.RandomYellowHue(),
5 => Utility.RandomNeutralHue(),
_ => 0
};
AddItem(new BodySash(lowHue));
}
public override void InitOutfit()
AddItem(new Cloak(GetRandomHue()));
if (!Female)
{
if (Female)
{
AddItem(new FancyDress());
}
else
{
AddItem(new FancyShirt(GetRandomHue()));
}
var lowHue = GetRandomHue();
AddItem(new ShortPants(lowHue));
if (Female)
{
AddItem(new ThighBoots(lowHue));
}
else
{
AddItem(new Boots(lowHue));
}
if (!Female)
{
AddItem(new BodySash(lowHue));
}
AddItem(new Cloak(GetRandomHue()));
if (!Female)
{
AddItem(new Longsword());
}
Utility.AssignRandomHair(this);
PackGold(200, 250);
AddItem(new Longsword());
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
Utility.AssignRandomHair(this);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadInt();
}
PackGold(200, 250);
}
}

View file

@ -1,82 +1,65 @@
using ModernUO.Serialization;
using Server.Items;
namespace Server.Mobiles
namespace Server.Mobiles;
[SerializationGenerator(0, false)]
public partial class Peasant : BaseEscortable
{
public class Peasant : BaseEscortable
[Constructible]
public Peasant() => Title = "the peasant";
public override bool CanTeach => true;
public override bool ClickTitle => false; // Do not display 'the peasant' when single-clicking
private static int GetRandomHue()
{
[Constructible]
public Peasant() => Title = "the peasant";
public Peasant(Serial serial) : base(serial)
return Utility.Random(6) switch
{
0 => 0,
1 => Utility.RandomBlueHue(),
2 => Utility.RandomGreenHue(),
3 => Utility.RandomRedHue(),
4 => Utility.RandomYellowHue(),
5 => Utility.RandomNeutralHue(),
_ => 0
};
}
public override void InitOutfit()
{
if (Female)
{
AddItem(new PlainDress());
}
else
{
AddItem(new Shirt(GetRandomHue()));
}
public override bool CanTeach => true;
public override bool ClickTitle => false; // Do not display 'the peasant' when single-clicking
var lowHue = GetRandomHue();
private static int GetRandomHue()
AddItem(new ShortPants(lowHue));
if (Female)
{
return Utility.Random(6) switch
{
0 => 0,
1 => Utility.RandomBlueHue(),
2 => Utility.RandomGreenHue(),
3 => Utility.RandomRedHue(),
4 => Utility.RandomYellowHue(),
5 => Utility.RandomNeutralHue(),
_ => 0
};
AddItem(new Boots(lowHue));
}
else
{
AddItem(new Shoes(lowHue));
}
public override void InitOutfit()
{
if (Female)
{
AddItem(new PlainDress());
}
else
{
AddItem(new Shirt(GetRandomHue()));
}
// if (!Female)
// AddItem( new BodySash( lowHue ) );
var lowHue = GetRandomHue();
// AddItem( new Cloak( GetRandomHue() ) );
AddItem(new ShortPants(lowHue));
// if (!Female)
// AddItem( new Longsword() );
if (Female)
{
AddItem(new Boots(lowHue));
}
else
{
AddItem(new Shoes(lowHue));
}
Utility.AssignRandomHair(this);
// if (!Female)
// AddItem( new BodySash( lowHue ) );
// AddItem( new Cloak( GetRandomHue() ) );
// if (!Female)
// AddItem( new Longsword() );
Utility.AssignRandomHair(this);
PackGold(200, 250);
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadInt();
}
PackGold(200, 250);
}
}

View file

@ -1,88 +1,70 @@
using Server.Engines.MLQuests;
using ModernUO.Serialization;
using Server.Engines.MLQuests;
using Server.Items;
namespace Server.Mobiles.Townfolk
namespace Server.Mobiles.Townfolk;
[SerializationGenerator(0, false)]
public partial class Prisoner : BaseEscortable
{
public class Prisoner : BaseEscortable
[Constructible]
public Prisoner()
{
[Constructible]
public Prisoner()
{
Title = Female ? "the noblewoman" : "the nobleman";
Title = Female ? "the noblewoman" : "the nobleman";
CantWalk = true;
IsPrisoner = true;
CantWalk = true;
IsPrisoner = true;
}
public override bool CanTeach => true;
public override bool ClickTitle => false;
public override void InitOutfit()
{
if (Female)
{
AddItem(new FancyDress(Utility.RandomNondyedHue()));
}
else
{
AddItem(new FancyShirt(Utility.RandomNondyedHue()));
AddItem(new LongPants(Utility.RandomNondyedHue()));
}
public Prisoner(Serial serial)
: base(serial)
if (Utility.RandomBool())
{
AddItem(new Boots());
}
else
{
AddItem(new ThighBoots());
}
public override bool CanTeach => true;
public override bool ClickTitle => false;
Utility.AssignRandomHair(this);
Utility.AssignRandomFacialHair(this, HairHue);
}
public override void InitOutfit()
public override void Shout(PlayerMobile pm)
{
/*
* 502261 - HELP!
* 502262 - Help me!
* 502263 - Canst thou aid me?!
* 502264 - Help a poor prisoner!
* 502265 - Help! Please!
* 502266 - Aaah! Help me!
* 502267 - Go and get some help!
*/
MLQuestSystem.Tell(this, pm, Utility.Random(502261, 7));
}
public override void OnMovement(Mobile m, Point3D oldLocation)
{
base.OnMovement(m, oldLocation);
if (CantWalk && InRange(m, 1) && !InRange(oldLocation, 1) && (!m.Hidden || m.AccessLevel == AccessLevel.Player))
{
if (Female)
{
AddItem(new FancyDress(Utility.RandomNondyedHue()));
}
else
{
AddItem(new FancyShirt(Utility.RandomNondyedHue()));
AddItem(new LongPants(Utility.RandomNondyedHue()));
}
if (Utility.RandomBool())
{
AddItem(new Boots());
}
else
{
AddItem(new ThighBoots());
}
Utility.AssignRandomHair(this);
Utility.AssignRandomFacialHair(this, HairHue);
}
public override void Shout(PlayerMobile pm)
{
/*
* 502261 - HELP!
* 502262 - Help me!
* 502263 - Canst thou aid me?!
* 502264 - Help a poor prisoner!
* 502265 - Help! Please!
* 502266 - Aaah! Help me!
* 502267 - Go and get some help!
*/
MLQuestSystem.Tell(this, pm, Utility.Random(502261, 7));
}
public override void OnMovement(Mobile m, Point3D oldLocation)
{
base.OnMovement(m, oldLocation);
if (CantWalk && InRange(m, 1) && !InRange(oldLocation, 1) && (!m.Hidden || m.AccessLevel == AccessLevel.Player))
{
Say(502268); // Quickly, I beg thee! Unlock my chains! If thou dost look at me close thou canst see them.
}
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadInt();
Say(502268); // Quickly, I beg thee! Unlock my chains! If thou dost look at me close thou canst see them.
}
}
}

View file

@ -1,113 +1,116 @@
using ModernUO.Serialization;
using Server.Items;
namespace Server.Mobiles
namespace Server.Mobiles;
[SerializationGenerator(0)]
public partial class Samurai : BaseCreature
{
public class Samurai : BaseCreature
[Constructible]
public Samurai() : base(AIType.AI_Melee, FightMode.Aggressor)
{
[Constructible]
public Samurai() : base(AIType.AI_Melee, FightMode.Aggressor)
Title = "the samurai";
InitStats(100, 100, 25);
SetSkill(SkillName.ArmsLore, 64.0, 80.0);
SetSkill(SkillName.Bushido, 64.0, 85.0);
SetSkill(SkillName.Parry, 64.0, 80.0);
SetSkill(SkillName.Swords, 64.0, 85.0);
SetSpeed(0.2, 0.4);
SpeechHue = Utility.RandomDyedHue();
Hue = Race.Human.RandomSkinHue();
if (Female = Utility.RandomBool())
{
Title = "the samurai";
Body = 0x191;
Name = NameList.RandomName("female");
}
else
{
Body = 0x190;
Name = NameList.RandomName("male");
}
InitStats(100, 100, 25);
SetSkill(SkillName.ArmsLore, 64.0, 80.0);
SetSkill(SkillName.Bushido, 64.0, 85.0);
SetSkill(SkillName.Parry, 64.0, 80.0);
SetSkill(SkillName.Swords, 64.0, 85.0);
SetSpeed(0.2, 0.4);
SpeechHue = Utility.RandomDyedHue();
Hue = Race.Human.RandomSkinHue();
if (Female = Utility.RandomBool())
{
Body = 0x191;
Name = NameList.RandomName("female");
}
else
{
Body = 0x190;
Name = NameList.RandomName("male");
}
switch (Utility.Random(3))
{
case 0:
switch (Utility.Random(3))
{
case 0:
{
AddItem(new Lajatang());
break;
case 1:
}
case 1:
{
AddItem(new Wakizashi());
break;
case 2:
}
case 2:
{
AddItem(new NoDachi());
break;
}
}
}
switch (Utility.Random(3))
{
case 0:
switch (Utility.Random(3))
{
case 0:
{
AddItem(new LeatherSuneate());
break;
case 1:
}
case 1:
{
AddItem(new PlateSuneate());
break;
case 2:
}
case 2:
{
AddItem(new StuddedHaidate());
break;
}
}
}
switch (Utility.Random(4))
{
case 0:
switch (Utility.Random(4))
{
case 0:
{
AddItem(new LeatherJingasa());
break;
case 1:
}
case 1:
{
AddItem(new ChainHatsuburi());
break;
case 2:
}
case 2:
{
AddItem(new HeavyPlateJingasa());
break;
case 3:
}
case 3:
{
AddItem(new DecorativePlateKabuto());
break;
}
AddItem(new LeatherDo());
AddItem(new LeatherHiroSode());
AddItem(new SamuraiTabi(Utility.RandomNondyedHue())); // TODO: Hue
var hairHue = Utility.RandomNondyedHue();
Utility.AssignRandomHair(this, hairHue);
if (Utility.Random(7) != 0)
{
Utility.AssignRandomFacialHair(this, hairHue);
}
PackGold(250, 300);
}
}
public Samurai(Serial serial) : base(serial)
AddItem(new LeatherDo());
AddItem(new LeatherHiroSode());
AddItem(new SamuraiTabi(Utility.RandomNondyedHue())); // TODO: Hue
var hairHue = Utility.RandomNondyedHue();
Utility.AssignRandomHair(this, hairHue);
if (Utility.Random(7) != 0)
{
Utility.AssignRandomFacialHair(this, hairHue);
}
public override bool CanTeach => true;
public override bool ClickTitle => false;
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadEncodedInt();
}
PackGold(250, 300);
}
public override bool CanTeach => true;
public override bool ClickTitle => false;
}

View file

@ -1,65 +1,47 @@
using ModernUO.Serialization;
using Server.Items;
namespace Server.Mobiles
namespace Server.Mobiles;
[SerializationGenerator(0, false)]
public partial class Sculptor : BaseCreature
{
public class Sculptor : BaseCreature
[Constructible]
public Sculptor() : base(AIType.AI_Animal, FightMode.None)
{
[Constructible]
public Sculptor() : base(AIType.AI_Animal, FightMode.None)
InitStats(31, 41, 51);
SetSpeed(0.2, 0.4);
SpeechHue = Utility.RandomDyedHue();
Title = "the sculptor";
Hue = Race.Human.RandomSkinHue();
if (Female = Utility.RandomBool())
{
InitStats(31, 41, 51);
SetSpeed(0.2, 0.4);
SpeechHue = Utility.RandomDyedHue();
Title = "the sculptor";
Hue = Race.Human.RandomSkinHue();
if (Female = Utility.RandomBool())
{
Body = 0x191;
Name = NameList.RandomName("female");
AddItem(new Kilt(Utility.RandomNeutralHue()));
}
else
{
Body = 0x190;
Name = NameList.RandomName("male");
AddItem(new LongPants(Utility.RandomNeutralHue()));
}
AddItem(new Doublet(Utility.RandomNeutralHue()));
AddItem(new HalfApron());
Utility.AssignRandomHair(this);
Container pack = new Backpack();
pack.DropItem(new Gold(250, 300));
pack.Movable = false;
AddItem(pack);
Body = 0x191;
Name = NameList.RandomName("female");
AddItem(new Kilt(Utility.RandomNeutralHue()));
}
else
{
Body = 0x190;
Name = NameList.RandomName("male");
AddItem(new LongPants(Utility.RandomNeutralHue()));
}
public Sculptor(Serial serial)
: base(serial)
{
}
AddItem(new Doublet(Utility.RandomNeutralHue()));
AddItem(new HalfApron());
public override bool ClickTitle => false;
Utility.AssignRandomHair(this);
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
Container pack = new Backpack();
writer.Write(0); // version
}
pack.DropItem(new Gold(250, 300));
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
pack.Movable = false;
var version = reader.ReadInt();
}
AddItem(pack);
}
public override bool ClickTitle => false;
}

View file

@ -1,97 +1,84 @@
using ModernUO.Serialization;
using Server.Items;
namespace Server.Mobiles
namespace Server.Mobiles;
[SerializationGenerator(0, false)]
public partial class SeekerOfAdventure : BaseEscortable
{
public class SeekerOfAdventure : BaseEscortable
private static readonly string[] _dungeons =
{
private static readonly string[] m_Dungeons =
"Covetous", "Deceit", "Despise",
"Destard", "Hythloth", "Shame", // Old Code for Pre-ML shards.
"Wrong"
};
private static readonly string[] _mlDestinations =
{
"Cove", "Serpent's Hold", "Jhelom", // ML List
"Nujel'm"
};
[Constructible]
public SeekerOfAdventure() => Title = "the seeker of adventure";
public SeekerOfAdventure(Serial serial) : base(serial)
{
}
public override bool ClickTitle => false; // Do not display 'the seeker of adventure' when single-clicking
public override string[] GetPossibleDestinations() => Core.ML ? _mlDestinations : _dungeons;
private static int GetRandomHue()
{
return Utility.Random(6) switch
{
"Covetous", "Deceit", "Despise",
"Destard", "Hythloth", "Shame", // Old Code for Pre-ML shards.
"Wrong"
0 => 0,
1 => Utility.RandomBlueHue(),
2 => Utility.RandomGreenHue(),
3 => Utility.RandomRedHue(),
4 => Utility.RandomYellowHue(),
5 => Utility.RandomNeutralHue(),
_ => 0
};
}
private static readonly string[] m_MLDestinations =
public override void InitOutfit()
{
if (Female)
{
"Cove", "Serpent's Hold", "Jhelom", // ML List
"Nujel'm"
};
[Constructible]
public SeekerOfAdventure() => Title = "the seeker of adventure";
public SeekerOfAdventure(Serial serial) : base(serial)
AddItem(new FancyDress(GetRandomHue()));
}
else
{
AddItem(new FancyShirt(GetRandomHue()));
}
public override bool ClickTitle => false; // Do not display 'the seeker of adventure' when single-clicking
var lowHue = GetRandomHue();
public override string[] GetPossibleDestinations() => Core.ML ? m_MLDestinations : m_Dungeons;
AddItem(new ShortPants(lowHue));
private static int GetRandomHue()
if (Female)
{
return Utility.Random(6) switch
{
0 => 0,
1 => Utility.RandomBlueHue(),
2 => Utility.RandomGreenHue(),
3 => Utility.RandomRedHue(),
4 => Utility.RandomYellowHue(),
5 => Utility.RandomNeutralHue(),
_ => 0
};
AddItem(new ThighBoots(lowHue));
}
else
{
AddItem(new Boots(lowHue));
}
public override void InitOutfit()
if (!Female)
{
if (Female)
{
AddItem(new FancyDress(GetRandomHue()));
}
else
{
AddItem(new FancyShirt(GetRandomHue()));
}
var lowHue = GetRandomHue();
AddItem(new ShortPants(lowHue));
if (Female)
{
AddItem(new ThighBoots(lowHue));
}
else
{
AddItem(new Boots(lowHue));
}
if (!Female)
{
AddItem(new BodySash(lowHue));
}
AddItem(new Cloak(GetRandomHue()));
AddItem(new Longsword());
Utility.AssignRandomHair(this);
PackGold(100, 150);
AddItem(new BodySash(lowHue));
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
AddItem(new Cloak(GetRandomHue()));
writer.Write(0); // version
}
AddItem(new Longsword());
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
Utility.AssignRandomHair(this);
var version = reader.ReadInt();
}
PackGold(100, 150);
}
}

View file

@ -1,440 +1,495 @@
using System;
using System.Collections.Generic;
using System.Text;
using ModernUO.Serialization;
using Server.Gumps;
using Server.Items;
using Server.Network;
using Server.Prompts;
namespace Server.Mobiles
namespace Server.Mobiles;
public interface ITownCrierEntryList
{
public interface ITownCrierEntryList
List<TownCrierEntry> Entries { get; }
TownCrierEntry GetRandomEntry();
TownCrierEntry AddEntry(string[] lines, TimeSpan duration);
void RemoveEntry(TownCrierEntry entry);
}
public class GlobalTownCrierEntryList : ITownCrierEntryList
{
private static GlobalTownCrierEntryList m_Instance;
public static GlobalTownCrierEntryList Instance => m_Instance ??= new GlobalTownCrierEntryList();
public bool IsEmpty => Entries == null || Entries.Count == 0;
public List<TownCrierEntry> Entries { get; private set; }
public TownCrierEntry GetRandomEntry()
{
List<TownCrierEntry> Entries { get; }
TownCrierEntry GetRandomEntry();
TownCrierEntry AddEntry(string[] lines, TimeSpan duration);
void RemoveEntry(TownCrierEntry entry);
for (var i = (Entries?.Count ?? 0) - 1; i >= 0; --i)
{
if (i >= Entries!.Count)
{
continue;
}
var tce = Entries[i];
if (tce.Expired)
{
RemoveEntry(tce);
}
}
return Entries?.RandomElement();
}
public class GlobalTownCrierEntryList : ITownCrierEntryList
public TownCrierEntry AddEntry(string[] lines, TimeSpan duration)
{
private static GlobalTownCrierEntryList m_Instance;
Entries ??= new List<TownCrierEntry>();
public static GlobalTownCrierEntryList Instance => m_Instance ??= new GlobalTownCrierEntryList();
var tce = new TownCrierEntry(lines, duration);
public bool IsEmpty => Entries == null || Entries.Count == 0;
Entries.Add(tce);
public List<TownCrierEntry> Entries { get; private set; }
var instances = TownCrier.Instances;
public TownCrierEntry GetRandomEntry()
for (var i = 0; i < instances.Count; ++i)
{
for (var i = (Entries?.Count ?? 0) - 1; i >= 0; --i)
{
if (i >= Entries!.Count)
{
continue;
}
var tce = Entries[i];
if (tce.Expired)
{
RemoveEntry(tce);
}
}
return Entries?.RandomElement();
instances[i].ForceBeginAutoShout();
}
public TownCrierEntry AddEntry(string[] lines, TimeSpan duration)
return tce;
}
public void RemoveEntry(TownCrierEntry tce)
{
if (Entries == null)
{
Entries ??= new List<TownCrierEntry>();
var tce = new TownCrierEntry(lines, duration);
Entries.Add(tce);
var instances = TownCrier.Instances;
for (var i = 0; i < instances.Count; ++i)
{
instances[i].ForceBeginAutoShout();
}
return tce;
return;
}
public void RemoveEntry(TownCrierEntry tce)
Entries.Remove(tce);
if (Entries.Count == 0)
{
if (Entries == null)
{
return;
}
Entries.Remove(tce);
if (Entries.Count == 0)
{
Entries = null;
}
}
public static void Initialize()
{
CommandSystem.Register("TownCriers", AccessLevel.GameMaster, TownCriers_OnCommand);
}
[Usage("TownCriers"), Description("Manages the global town crier list.")]
public static void TownCriers_OnCommand(CommandEventArgs e)
{
e.Mobile.SendGump(new TownCrierGump(e.Mobile, Instance));
Entries = null;
}
}
public class TownCrierEntry
public static void Initialize()
{
public TownCrierEntry(string[] lines, TimeSpan duration)
{
Lines = lines;
ExpireTime = Core.Now + duration.Clamp(TimeSpan.Zero, TimeSpan.FromDays(365));
}
public string[] Lines { get; }
public DateTime ExpireTime { get; }
public bool Expired => Core.Now >= ExpireTime;
CommandSystem.Register("TownCriers", AccessLevel.GameMaster, TownCriers_OnCommand);
}
public class TownCrierDurationPrompt : Prompt
[Usage("TownCriers"), Description("Manages the global town crier list.")]
public static void TownCriers_OnCommand(CommandEventArgs e)
{
private readonly ITownCrierEntryList m_Owner;
e.Mobile.SendGump(new TownCrierGump(e.Mobile, Instance));
}
}
public TownCrierDurationPrompt(ITownCrierEntryList owner) => m_Owner = owner;
public class TownCrierEntry
{
public TownCrierEntry(string[] lines, TimeSpan duration)
{
Lines = lines;
public override void OnResponse(Mobile from, string text)
ExpireTime = Core.Now + duration.Clamp(TimeSpan.Zero, TimeSpan.FromDays(365));
}
public string[] Lines { get; }
public DateTime ExpireTime { get; }
public bool Expired => Core.Now >= ExpireTime;
}
public class TownCrierDurationPrompt : Prompt
{
private readonly ITownCrierEntryList m_Owner;
public TownCrierDurationPrompt(ITownCrierEntryList owner) => m_Owner = owner;
public override void OnResponse(Mobile from, string text)
{
if (!TimeSpan.TryParse(text, out var ts))
{
if (!TimeSpan.TryParse(text, out var ts))
{
from.SendMessage("Value was not properly formatted. Use: <hours:minutes:seconds>");
from.SendGump(new TownCrierGump(from, m_Owner));
return;
}
if (ts < TimeSpan.Zero)
{
ts = TimeSpan.Zero;
}
from.SendMessage($"Duration set to: {ts}");
from.SendMessage("Enter the first line to shout:");
from.Prompt = new TownCrierLinesPrompt(m_Owner, null, new List<string>(), ts);
}
public override void OnCancel(Mobile from)
{
from.SendLocalizedMessage(502980); // Message entry cancelled.
from.SendMessage("Value was not properly formatted. Use: <hours:minutes:seconds>");
from.SendGump(new TownCrierGump(from, m_Owner));
return;
}
if (ts < TimeSpan.Zero)
{
ts = TimeSpan.Zero;
}
from.SendMessage($"Duration set to: {ts}");
from.SendMessage("Enter the first line to shout:");
from.Prompt = new TownCrierLinesPrompt(m_Owner, null, new List<string>(), ts);
}
public class TownCrierLinesPrompt : Prompt
public override void OnCancel(Mobile from)
{
private readonly TimeSpan m_Duration;
private readonly TownCrierEntry m_Entry;
private readonly List<string> m_Lines;
private readonly ITownCrierEntryList m_Owner;
from.SendLocalizedMessage(502980); // Message entry cancelled.
from.SendGump(new TownCrierGump(from, m_Owner));
}
}
public TownCrierLinesPrompt(ITownCrierEntryList owner, TownCrierEntry entry, List<string> lines, TimeSpan duration)
public class TownCrierLinesPrompt : Prompt
{
private readonly TimeSpan m_Duration;
private readonly TownCrierEntry m_Entry;
private readonly List<string> m_Lines;
private readonly ITownCrierEntryList m_Owner;
public TownCrierLinesPrompt(ITownCrierEntryList owner, TownCrierEntry entry, List<string> lines, TimeSpan duration)
{
m_Owner = owner;
m_Entry = entry;
m_Lines = lines;
m_Duration = duration;
}
public override void OnResponse(Mobile from, string text)
{
m_Lines.Add(text);
from.SendMessage("Enter the next line to shout, or press <ESC> if the message is finished.");
from.Prompt = new TownCrierLinesPrompt(m_Owner, m_Entry, m_Lines, m_Duration);
}
public override void OnCancel(Mobile from)
{
if (m_Entry != null)
{
m_Owner = owner;
m_Entry = entry;
m_Lines = lines;
m_Duration = duration;
m_Owner.RemoveEntry(m_Entry);
}
public override void OnResponse(Mobile from, string text)
if (m_Lines.Count > 0)
{
m_Lines.Add(text);
from.SendMessage("Enter the next line to shout, or press <ESC> if the message is finished.");
from.Prompt = new TownCrierLinesPrompt(m_Owner, m_Entry, m_Lines, m_Duration);
m_Owner.AddEntry(m_Lines.ToArray(), m_Duration);
from.SendMessage("Message has been set.");
}
public override void OnCancel(Mobile from)
else
{
if (m_Entry != null)
{
m_Owner.RemoveEntry(m_Entry);
}
if (m_Lines.Count > 0)
{
m_Owner.AddEntry(m_Lines.ToArray(), m_Duration);
from.SendMessage("Message has been set.");
from.SendMessage("Message deleted.");
}
else
{
if (m_Entry != null)
from.SendLocalizedMessage(502980); // Message entry cancelled.
}
}
from.SendGump(new TownCrierGump(from, m_Owner));
}
}
public class TownCrierGump : Gump
{
private readonly Mobile m_From;
private readonly ITownCrierEntryList m_Owner;
public TownCrierGump(Mobile from, ITownCrierEntryList owner) : base(50, 50)
{
m_From = from;
m_Owner = owner;
from.CloseGump<TownCrierGump>();
AddPage(0);
var entries = owner.Entries;
owner.GetRandomEntry(); // force expiration checks
var count = entries?.Count ?? 0;
AddImageTiled(0, 0, 300, 38 + (count == 0 ? 20 : count * 85), 0xA40);
AddAlphaRegion(1, 1, 298, 36 + (count == 0 ? 20 : count * 85));
AddHtml(8, 8, 300 - 8 - 30, 20, "<basefont color=#FFFFFF><center>TOWN CRIER MESSAGES</center></basefont>");
AddButton(300 - 8 - 30, 8, 0xFAB, 0xFAD, 1);
if (count == 0)
{
AddHtml(8, 30, 284, 20, "<basefont color=#FFFFFF>The crier has no news.</basefont>");
}
else
{
for (var i = 0; i < entries!.Count; ++i)
{
var tce = entries[i];
var toExpire = Utility.Max(tce.ExpireTime - Core.Now, TimeSpan.Zero);
var sb = new StringBuilder();
sb.Append("[Expires: ");
if (toExpire.TotalHours >= 1)
{
from.SendMessage("Message deleted.");
sb.Append((int)toExpire.TotalHours);
sb.Append(':');
sb.Append(toExpire.Minutes.ToString("D2"));
}
else
{
from.SendLocalizedMessage(502980); // Message entry cancelled.
sb.Append(toExpire.Minutes);
}
}
from.SendGump(new TownCrierGump(from, m_Owner));
}
}
sb.Append(':');
sb.Append(toExpire.Seconds.ToString("D2"));
public class TownCrierGump : Gump
{
private readonly Mobile m_From;
private readonly ITownCrierEntryList m_Owner;
sb.Append("] ");
public TownCrierGump(Mobile from, ITownCrierEntryList owner) : base(50, 50)
{
m_From = from;
m_Owner = owner;
from.CloseGump<TownCrierGump>();
AddPage(0);
var entries = owner.Entries;
owner.GetRandomEntry(); // force expiration checks
var count = entries?.Count ?? 0;
AddImageTiled(0, 0, 300, 38 + (count == 0 ? 20 : count * 85), 0xA40);
AddAlphaRegion(1, 1, 298, 36 + (count == 0 ? 20 : count * 85));
AddHtml(8, 8, 300 - 8 - 30, 20, "<basefont color=#FFFFFF><center>TOWN CRIER MESSAGES</center></basefont>");
AddButton(300 - 8 - 30, 8, 0xFAB, 0xFAD, 1);
if (count == 0)
{
AddHtml(8, 30, 284, 20, "<basefont color=#FFFFFF>The crier has no news.</basefont>");
}
else
{
for (var i = 0; i < entries!.Count; ++i)
for (var j = 0; j < tce.Lines.Length; ++j)
{
var tce = entries[i];
var toExpire = Utility.Max(tce.ExpireTime - Core.Now, TimeSpan.Zero);
var sb = new StringBuilder();
sb.Append("[Expires: ");
if (toExpire.TotalHours >= 1)
if (j > 0)
{
sb.Append((int)toExpire.TotalHours);
sb.Append(':');
sb.Append(toExpire.Minutes.ToString("D2"));
}
else
{
sb.Append(toExpire.Minutes);
sb.Append("<br>");
}
sb.Append(':');
sb.Append(toExpire.Seconds.ToString("D2"));
sb.Append("] ");
for (var j = 0; j < tce.Lines.Length; ++j)
{
if (j > 0)
{
sb.Append("<br>");
}
sb.Append(tce.Lines[j]);
}
AddHtml(8, 35 + i * 85, 254, 80, sb.ToString(), true, true);
AddButton(300 - 8 - 26, 35 + i * 85, 0x15E1, 0x15E5, 2 + i);
sb.Append(tce.Lines[j]);
}
}
}
public override void OnResponse(NetState sender, RelayInfo info)
{
if (info.ButtonID == 1)
{
m_From.SendMessage("Enter the duration for the new message. Format: <hours:minutes:seconds>");
m_From.Prompt = new TownCrierDurationPrompt(m_Owner);
}
else if (info.ButtonID > 1)
{
var entries = m_Owner.Entries;
var index = info.ButtonID - 2;
AddHtml(8, 35 + i * 85, 254, 80, sb.ToString(), true, true);
if (index < entries?.Count)
{
var tce = entries[index];
var ts = Utility.Max(tce.ExpireTime - Core.Now, TimeSpan.Zero);
m_From.SendMessage($"Editing entry #{index + 1}.");
m_From.SendMessage("Enter the first line to shout:");
m_From.Prompt = new TownCrierLinesPrompt(m_Owner, tce, new List<string>(), ts);
}
AddButton(300 - 8 - 26, 35 + i * 85, 0x15E1, 0x15E5, 2 + i);
}
}
}
public class TownCrier : Mobile, ITownCrierEntryList
public override void OnResponse(NetState sender, RelayInfo info)
{
private Timer _autoShoutTimer;
private Timer _newsTimer;
[Constructible]
public TownCrier()
if (info.ButtonID == 1)
{
Instances.Add(this);
m_From.SendMessage("Enter the duration for the new message. Format: <hours:minutes:seconds>");
m_From.Prompt = new TownCrierDurationPrompt(m_Owner);
}
else if (info.ButtonID > 1)
{
var entries = m_Owner.Entries;
var index = info.ButtonID - 2;
InitStats(100, 100, 25);
Title = "the town crier";
Hue = Race.Human.RandomSkinHue();
if (!Core.AOS)
if (index < entries?.Count)
{
NameHue = 0x35;
var tce = entries[index];
var ts = Utility.Max(tce.ExpireTime - Core.Now, TimeSpan.Zero);
m_From.SendMessage($"Editing entry #{index + 1}.");
m_From.SendMessage("Enter the first line to shout:");
m_From.Prompt = new TownCrierLinesPrompt(m_Owner, tce, new List<string>(), ts);
}
}
}
}
if (Female = Utility.RandomBool())
{
Body = 0x191;
Name = NameList.RandomName("female");
}
else
{
Body = 0x190;
Name = NameList.RandomName("male");
}
[SerializationGenerator(0, false)]
public partial class TownCrier : Mobile, ITownCrierEntryList
{
private Timer _autoShoutTimer;
private Timer _newsTimer;
AddItem(new FancyShirt(Utility.RandomBlueHue()));
[Constructible]
public TownCrier()
{
Instances.Add(this);
var skirt = Utility.Random(2) switch
{
0 => (Item)new Skirt(),
1 => new Kilt(),
_ => new Kilt()
};
InitStats(100, 100, 25);
skirt.Hue = Utility.RandomGreenHue();
Title = "the town crier";
Hue = Race.Human.RandomSkinHue();
AddItem(skirt);
AddItem(new FeatheredHat(Utility.RandomGreenHue()));
var boots = Utility.Random(2) switch
{
0 => (Item)new Boots(),
1 => new ThighBoots(),
_ => new ThighBoots()
};
AddItem(boots);
Utility.AssignRandomHair(this);
if (!Core.AOS)
{
NameHue = 0x35;
}
public TownCrier(Serial serial) : base(serial)
if (Female = Utility.RandomBool())
{
Instances.Add(this);
Body = 0x191;
Name = NameList.RandomName("female");
}
else
{
Body = 0x190;
Name = NameList.RandomName("male");
}
public static List<TownCrier> Instances { get; } = new();
AddItem(new FancyShirt(Utility.RandomBlueHue()));
public List<TownCrierEntry> Entries { get; private set; }
public TownCrierEntry GetRandomEntry()
var skirt = Utility.Random(2) switch
{
var count = Entries?.Count ?? 0;
for (var i = count - 1; i >= 0; --i)
{
if (i >= Entries!.Count)
{
continue;
}
0 => (Item)new Skirt(),
1 => new Kilt(),
_ => new Kilt()
};
var tce = Entries[i];
skirt.Hue = Utility.RandomGreenHue();
if (tce.Expired)
{
RemoveEntry(tce);
}
}
AddItem(skirt);
var entry = GlobalTownCrierEntryList.Instance.GetRandomEntry();
if (count > 0 && entry != null && Utility.RandomBool())
{
return entry;
}
AddItem(new FeatheredHat(Utility.RandomGreenHue()));
return Entries?.RandomElement();
}
public TownCrierEntry AddEntry(string[] lines, TimeSpan duration)
var boots = Utility.Random(2) switch
{
Entries ??= new List<TownCrierEntry>();
0 => (Item)new Boots(),
1 => new ThighBoots(),
_ => new ThighBoots()
};
var tce = new TownCrierEntry(lines, duration);
AddItem(boots);
Entries.Add(tce);
Utility.AssignRandomHair(this);
}
ForceBeginAutoShout();
public TownCrier(Serial serial) : base(serial)
{
Instances.Add(this);
}
return tce;
}
public static List<TownCrier> Instances { get; } = new();
public void RemoveEntry(TownCrierEntry tce)
public List<TownCrierEntry> Entries { get; private set; }
public TownCrierEntry GetRandomEntry()
{
var count = Entries?.Count ?? 0;
for (var i = count - 1; i >= 0; --i)
{
if (Entries == null)
if (i >= Entries!.Count)
{
return;
continue;
}
Entries.Remove(tce);
var tce = Entries[i];
if (Entries.Count == 0)
if (tce.Expired)
{
Entries = null;
}
if (Entries == null && GlobalTownCrierEntryList.Instance.IsEmpty)
{
_autoShoutTimer?.Stop();
_autoShoutTimer = null;
RemoveEntry(tce);
}
}
public void ForceBeginAutoShout()
var entry = GlobalTownCrierEntryList.Instance.GetRandomEntry();
if (count > 0 && entry != null && Utility.RandomBool())
{
_autoShoutTimer ??= Timer.DelayCall(TimeSpan.FromSeconds(5.0), TimeSpan.FromMinutes(1.0), AutoShout_Callback);
return entry;
}
private void AutoShout_Callback()
return Entries?.RandomElement();
}
public TownCrierEntry AddEntry(string[] lines, TimeSpan duration)
{
Entries ??= new List<TownCrierEntry>();
var tce = new TownCrierEntry(lines, duration);
Entries.Add(tce);
ForceBeginAutoShout();
return tce;
}
public void RemoveEntry(TownCrierEntry tce)
{
if (Entries == null)
{
return;
}
Entries.Remove(tce);
if (Entries.Count == 0)
{
Entries = null;
}
if (Entries == null && GlobalTownCrierEntryList.Instance.IsEmpty)
{
_autoShoutTimer?.Stop();
_autoShoutTimer = null;
}
}
public void ForceBeginAutoShout()
{
_autoShoutTimer ??= Timer.DelayCall(TimeSpan.FromSeconds(5.0), TimeSpan.FromMinutes(1.0), AutoShout_Callback);
}
private void AutoShout_Callback()
{
var tce = GetRandomEntry();
if (tce == null)
{
_autoShoutTimer.Stop();
_autoShoutTimer = null;
}
else if (_newsTimer == null)
{
_newsTimer = Timer.DelayCall(
TimeSpan.FromSeconds(1.0),
TimeSpan.FromSeconds(3.0),
tce.Lines.Length,
() => ShoutNews_Callback(tce)
);
PublicOverheadMessage(MessageType.Regular, 0x3B2, 502976); // Hear ye! Hear ye!
}
}
private void ShoutNews_Callback(TownCrierEntry tce)
{
var index = _newsTimer.Index;
if (index >= tce.Lines.Length)
{
_newsTimer.Stop();
_newsTimer = null;
}
else
{
PublicOverheadMessage(MessageType.Regular, 0x3B2, false, tce.Lines[index]);
}
}
public override void OnDoubleClick(Mobile from)
{
if (from.AccessLevel >= AccessLevel.GameMaster)
{
from.SendGump(new TownCrierGump(from, this));
}
else
{
base.OnDoubleClick(from);
}
}
public override bool HandlesOnSpeech(Mobile from) => _newsTimer == null && from.Alive && InRange(from, 12);
public override void OnSpeech(SpeechEventArgs e)
{
if (_newsTimer == null && e.HasKeyword(0x30) && e.Mobile.Alive && InRange(e.Mobile, 12)) // *news*
{
Direction = GetDirectionTo(e.Mobile);
var tce = GetRandomEntry();
if (tce == null)
{
_autoShoutTimer.Stop();
_autoShoutTimer = null;
PublicOverheadMessage(MessageType.Regular, 0x3B2, 1005643); // I have no news at this time.
}
else if (_newsTimer == null)
else
{
_newsTimer = Timer.DelayCall(
TimeSpan.FromSeconds(1.0),
@ -443,89 +498,16 @@ namespace Server.Mobiles
() => ShoutNews_Callback(tce)
);
PublicOverheadMessage(MessageType.Regular, 0x3B2, 502976); // Hear ye! Hear ye!
}
}
private void ShoutNews_Callback(TownCrierEntry tce)
{
var index = _newsTimer.Index;
if (index >= tce.Lines.Length)
{
_newsTimer.Stop();
_newsTimer = null;
}
else
{
PublicOverheadMessage(MessageType.Regular, 0x3B2, false, tce.Lines[index]);
}
}
public override void OnDoubleClick(Mobile from)
{
if (from.AccessLevel >= AccessLevel.GameMaster)
{
from.SendGump(new TownCrierGump(from, this));
}
else
{
base.OnDoubleClick(from);
}
}
public override bool HandlesOnSpeech(Mobile from) => _newsTimer == null && from.Alive && InRange(from, 12);
public override void OnSpeech(SpeechEventArgs e)
{
if (_newsTimer == null && e.HasKeyword(0x30) && e.Mobile.Alive && InRange(e.Mobile, 12)) // *news*
{
Direction = GetDirectionTo(e.Mobile);
var tce = GetRandomEntry();
if (tce == null)
{
PublicOverheadMessage(MessageType.Regular, 0x3B2, 1005643); // I have no news at this time.
}
else
{
_newsTimer = Timer.DelayCall(
TimeSpan.FromSeconds(1.0),
TimeSpan.FromSeconds(3.0),
tce.Lines.Length,
() => ShoutNews_Callback(tce)
);
PublicOverheadMessage(MessageType.Regular, 0x3B2, 502978); // Some of the latest news!
}
}
}
public override bool CanBeDamaged() => false;
public override void OnDelete()
{
Instances.Remove(this);
base.OnDelete();
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadInt();
if (Core.AOS && NameHue == 0x35)
{
NameHue = -1;
PublicOverheadMessage(MessageType.Regular, 0x3B2, 502978); // Some of the latest news!
}
}
}
public override bool CanBeDamaged() => false;
public override void OnDelete()
{
Instances.Remove(this);
base.OnDelete();
}
}