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/modernuo.json
/Distribution/Configuration/email-settings.json /Distribution/Configuration/email-settings.json
/Distribution/Configuration/throttles.json /Distribution/Configuration/throttles.json
/Distribution/Configuration/tot.json
/Distribution/Logs /Distribution/Logs
/Distribution/Archives /Distribution/Archives
/Distribution/Backups /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 HonorContext ReceivedHonorContext { get; set; }
public List<MLQuest> MLQuests public List<MLQuest> MLQuests =>
{ // Assign the quests if we don't have one, and if it is still null, return an empty list
get (m_MLQuests ??= StaticMLQuester ? MLQuestSystem.FindQuestList(GetType()) : ConstructQuestList()) ?? MLQuestSystem.EmptyList;
{
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 virtual MonsterAbility[] GetMonsterAbilities() => null; public virtual MonsterAbility[] GetMonsterAbilities() => null;

View file

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

View file

@ -1,68 +1,50 @@
using ModernUO.Serialization;
using Server.Items; 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] InitStats(31, 41, 51);
public Artist() : base(AIType.AI_Animal, FightMode.None) 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); Body = 0x191;
SetSkill(SkillName.Healing, 36, 68); Name = NameList.RandomName("female");
}
SetSpeed(0.2, 0.4); else
SpeechHue = Utility.RandomDyedHue(); {
Title = "the artist"; Body = 0x190;
Hue = Race.Human.RandomSkinHue(); Name = NameList.RandomName("male");
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);
} }
public Artist(Serial serial) AddItem(new Doublet(Utility.RandomDyedHue()));
: base(serial) 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) pack.DropItem(new Gold(250, 300));
{
base.Serialize(writer);
writer.Write(0); // version pack.Movable = false;
}
public override void Deserialize(IGenericReader reader) AddItem(pack);
{
base.Deserialize(reader);
var version = reader.ReadInt();
}
} }
public override bool CanTeach => true;
public override bool ClickTitle => false;
} }

View file

@ -1,481 +1,464 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using ModernUO.Serialization;
using Server.Accounting; using Server.Accounting;
using Server.ContextMenus; using Server.ContextMenus;
using Server.Items; using Server.Items;
using Server.Network; 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] protected override List<SBInfo> SBInfos => m_SBInfos;
public Banker() : base("the banker")
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; foreach (var g in bank.FindItemsByType<Gold>())
if (AccountGold.Enabled && m.Account != null)
{ {
balance = m.Account.GetTotalGold(); balance += g.Amount;
if (balance >= int.MaxValue) gold.Add(g);
{
return int.MaxValue;
}
} }
Container bank = m.FindBankNoCreate(); if (balance >= int.MaxValue)
if (bank != null)
{ {
foreach (var gold in bank.FindItemsByType<Gold>()) return int.MaxValue;
{
balance += gold.Amount;
}
if (balance >= int.MaxValue)
{
return int.MaxValue;
}
foreach (var check in bank.FindItemsByType<BankCheck>())
{
balance += check.Worth;
}
} }
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; 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. return false;
if (AccountGold.Enabled && from.Account?.DepositGold(amount) == true) }
for (var i = 0; amount > 0 && i < gold.Count; ++i)
{
if (gold[i].Amount <= amount)
{ {
return true; amount -= gold[i].Amount;
gold[i].Delete();
} }
else
var box = from.FindBankNoCreate();
if (box == null)
{ {
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; amount -= check.Worth;
if (amount < 5000) check.Delete();
{
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;
}
} }
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; 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. return false;
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;
} }
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); curItem.Delete();
amount = 0;
}
else if (amount <= 1000000)
{
item = new BankCheck(amount);
amount = 0;
}
else
{
item = new BankCheck(1000000);
amount -= 1000000;
} }
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) if (e.Mobile.Criminal)
{
case 0x0000: // *withdraw*
{ {
e.Handled = true; Say(500389); // I will not do business with a criminal!
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.
}
}
}
break; 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) if (!int.TryParse(split[1], out var amount))
{
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; break;
} }
if (e.Mobile.Criminal) if (!Core.ML && amount > 5000 || Core.ML && amount > 60000)
{ {
Say(500389); // I will not do business with a criminal! Say(500381); // Thou canst not withdraw so much at one time!
break;
} }
else if (pack?.Deleted != false || !(pack.TotalWeight < pack.MaxWeight) ||
var split = e.Speech.Split(' '); !(pack.TotalItems < pack.MaxItems))
if (split.Length >= 2)
{ {
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; Say(500384); // Ah, art thou trying to fool me? Thou hast not so much gold!
}
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 else
{ {
var check = new BankCheck(amount); pack.DropItem(new Gold(amount));
var box = e.Mobile.BankBox; Say(1010005); // Thou hast withdrawn gold from thy account.
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;
}
case 0x0001: // *balance*
{
e.Handled = true;
if (e.Mobile.Criminal)
{
Say(500389); // I will not do business with a criminal!
break; 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));
{
list.Add(new OpenBankEntry(this));
}
base.AddCustomContextEntries(from, list);
} }
public override void Serialize(IGenericWriter writer) base.AddCustomContextEntries(from, list);
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadInt();
}
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -1,93 +1,76 @@
using ModernUO.Serialization;
using Server.Items; 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] if (Female)
public BrideGroom()
{ {
if (Female) Title = "the bride";
{
Title = "the bride";
}
else
{
Title = "the groom";
}
} }
else
public BrideGroom(Serial serial) : base(serial)
{ {
} 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);
}
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 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; 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] Title = "the mage";
public EscortableMage()
{
Title = "the mage";
SetSkill(SkillName.EvalInt, 80.0, 100.0); SetSkill(SkillName.EvalInt, 80.0, 100.0);
SetSkill(SkillName.Inscribe, 80.0, 100.0); SetSkill(SkillName.Inscribe, 80.0, 100.0);
SetSkill(SkillName.Magery, 80.0, 100.0); SetSkill(SkillName.Magery, 80.0, 100.0);
SetSkill(SkillName.Meditation, 80.0, 100.0); SetSkill(SkillName.Meditation, 80.0, 100.0);
SetSkill(SkillName.MagicResist, 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; PackGold(200, 250);
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();
}
} }
} }

View file

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

View file

@ -1,90 +1,72 @@
using ModernUO.Serialization;
using Server.Items; 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] Title = "the merchant";
public 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"; 0 => 0,
SetSkill(SkillName.ItemID, 55.0, 78.0); 1 => Utility.RandomBlueHue(),
SetSkill(SkillName.ArmsLore, 55, 78); 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) var lowHue = GetRandomHue();
: base(serial)
AddItem(new ThighBoots());
if (Female)
{ {
AddItem(new FancyDress(lowHue));
}
else
{
AddItem(new FancyShirt(lowHue));
} }
public override bool CanTeach => true; AddItem(new LongPants(lowHue));
public override bool ClickTitle => false; // Do not display 'the merchant' when single-clicking
private static int GetRandomHue() if (!Female)
{ {
return Utility.Random(6) switch AddItem(new BodySash(lowHue));
{
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 Longsword() );
if (Female)
{
AddItem(new PlainDress());
}
else
{
AddItem(new Shirt(GetRandomHue()));
}
var lowHue = GetRandomHue(); Utility.AssignRandomHair(this);
AddItem(new ThighBoots()); PackGold(200, 250);
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();
}
} }
} }

View file

@ -1,76 +1,59 @@
using ModernUO.Serialization;
using Server.Items; 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] return Utility.Random(6) switch
public Messenger() => Title = "the messenger";
public Messenger(Serial serial) : base(serial)
{ {
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; var lowHue = GetRandomHue();
public override bool ClickTitle => false; // Do not display 'the messenger' when single-clicking
private static int GetRandomHue() AddItem(new ShortPants(lowHue));
if (Female)
{ {
return Utility.Random(6) switch AddItem(new Boots(lowHue));
{ }
1 => Utility.RandomBlueHue(), else
2 => Utility.RandomGreenHue(), {
3 => Utility.RandomRedHue(), AddItem(new Shoes(lowHue));
4 => Utility.RandomYellowHue(),
5 => Utility.RandomNeutralHue(),
_ => 0
};
} }
public override void InitOutfit() var randomHair = Utility.Random(4);
{ HairItemID = randomHair == 4 ? 0x203B : 0x2048 + randomHair;
if (Female)
{
AddItem(new PlainDress());
}
else
{
AddItem(new Shirt(GetRandomHue()));
}
var lowHue = GetRandomHue(); HairHue = Race.RandomHairHue();
AddItem(new ShortPants(lowHue)); PackGold(200, 250);
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();
}
} }
} }

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 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();
}
}
} }

View file

@ -1,78 +1,61 @@
using ModernUO.Serialization;
using Server.Items; 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] Title = "the ninja";
public Ninja() : base(AIType.AI_Melee, FightMode.Aggressor) 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"; Body = 0x191;
InitStats(100, 100, 25); Name = NameList.RandomName("female");
}
SetSkill(SkillName.Fencing, 64.0, 80.0); else
SetSkill(SkillName.Macing, 64.0, 80.0); {
SetSkill(SkillName.Ninjitsu, 60.0, 80.0); Body = 0x190;
SetSkill(SkillName.Parry, 64.0, 80.0); Name = NameList.RandomName("male");
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);
} }
public Ninja(Serial serial) : base(serial) if (!Female)
{ {
AddItem(new LeatherNinjaHood());
} }
public override bool CanTeach => true; AddItem(new LeatherNinjaPants());
public override bool ClickTitle => false; 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); Utility.AssignRandomFacialHair(this, hairHue);
writer.WriteEncodedInt(0); // version
} }
public override void Deserialize(IGenericReader reader) PackGold(250, 300);
{
base.Deserialize(reader);
var version = reader.ReadEncodedInt();
}
} }
public override bool CanTeach => true;
public override bool ClickTitle => false;
} }

View file

@ -1,92 +1,75 @@
using ModernUO.Serialization;
using Server.Items; 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] Title = "the noble";
public Noble()
{
Title = "the noble";
SetSkill(SkillName.Parry, 80.0, 100.0); SetSkill(SkillName.Parry, 80.0, 100.0);
SetSkill(SkillName.Swords, 80.0, 100.0); SetSkill(SkillName.Swords, 80.0, 100.0);
SetSkill(SkillName.Tactics, 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; if (!Female)
public override bool ClickTitle => false; // Do not display 'the noble' when single-clicking
private static int GetRandomHue()
{ {
return Utility.Random(6) switch AddItem(new BodySash(lowHue));
{
1 => Utility.RandomBlueHue(),
2 => Utility.RandomGreenHue(),
3 => Utility.RandomRedHue(),
4 => Utility.RandomYellowHue(),
5 => Utility.RandomNeutralHue(),
_ => 0
};
} }
public override void InitOutfit() AddItem(new Cloak(GetRandomHue()));
if (!Female)
{ {
if (Female) AddItem(new Longsword());
{
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);
} }
public override void Serialize(IGenericWriter writer) Utility.AssignRandomHair(this);
{
base.Serialize(writer);
writer.Write(0); // version PackGold(200, 250);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadInt();
}
} }
} }

View file

@ -1,82 +1,65 @@
using ModernUO.Serialization;
using Server.Items; 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] return Utility.Random(6) switch
public Peasant() => Title = "the peasant";
public Peasant(Serial serial) : base(serial)
{ {
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; var lowHue = GetRandomHue();
public override bool ClickTitle => false; // Do not display 'the peasant' when single-clicking
private static int GetRandomHue() AddItem(new ShortPants(lowHue));
if (Female)
{ {
return Utility.Random(6) switch AddItem(new Boots(lowHue));
{ }
0 => 0, else
1 => Utility.RandomBlueHue(), {
2 => Utility.RandomGreenHue(), AddItem(new Shoes(lowHue));
3 => Utility.RandomRedHue(),
4 => Utility.RandomYellowHue(),
5 => Utility.RandomNeutralHue(),
_ => 0
};
} }
public override void InitOutfit() // if (!Female)
{ // AddItem( new BodySash( lowHue ) );
if (Female)
{
AddItem(new PlainDress());
}
else
{
AddItem(new Shirt(GetRandomHue()));
}
var lowHue = GetRandomHue(); // AddItem( new Cloak( GetRandomHue() ) );
AddItem(new ShortPants(lowHue)); // if (!Female)
// AddItem( new Longsword() );
if (Female) Utility.AssignRandomHair(this);
{
AddItem(new Boots(lowHue));
}
else
{
AddItem(new Shoes(lowHue));
}
// if (!Female) PackGold(200, 250);
// 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();
}
} }
} }

View file

@ -1,88 +1,70 @@
using Server.Engines.MLQuests; using ModernUO.Serialization;
using Server.Engines.MLQuests;
using Server.Items; 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] Title = Female ? "the noblewoman" : "the nobleman";
public Prisoner()
{
Title = Female ? "the noblewoman" : "the nobleman";
CantWalk = true; CantWalk = true;
IsPrisoner = 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) if (Utility.RandomBool())
: base(serial)
{ {
AddItem(new Boots());
}
else
{
AddItem(new ThighBoots());
} }
public override bool CanTeach => true; Utility.AssignRandomHair(this);
public override bool ClickTitle => false; 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) Say(502268); // Quickly, I beg thee! Unlock my chains! If thou dost look at me close thou canst see them.
{
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();
} }
} }
} }

View file

@ -1,113 +1,116 @@
using ModernUO.Serialization;
using Server.Items; 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] Title = "the samurai";
public Samurai() : base(AIType.AI_Melee, FightMode.Aggressor)
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); switch (Utility.Random(3))
{
SetSkill(SkillName.ArmsLore, 64.0, 80.0); case 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:
AddItem(new Lajatang()); AddItem(new Lajatang());
break; break;
case 1: }
case 1:
{
AddItem(new Wakizashi()); AddItem(new Wakizashi());
break; break;
case 2: }
case 2:
{
AddItem(new NoDachi()); AddItem(new NoDachi());
break; break;
} }
}
switch (Utility.Random(3)) switch (Utility.Random(3))
{ {
case 0: case 0:
{
AddItem(new LeatherSuneate()); AddItem(new LeatherSuneate());
break; break;
case 1: }
case 1:
{
AddItem(new PlateSuneate()); AddItem(new PlateSuneate());
break; break;
case 2: }
case 2:
{
AddItem(new StuddedHaidate()); AddItem(new StuddedHaidate());
break; break;
} }
}
switch (Utility.Random(4)) switch (Utility.Random(4))
{ {
case 0: case 0:
{
AddItem(new LeatherJingasa()); AddItem(new LeatherJingasa());
break; break;
case 1: }
case 1:
{
AddItem(new ChainHatsuburi()); AddItem(new ChainHatsuburi());
break; break;
case 2: }
case 2:
{
AddItem(new HeavyPlateJingasa()); AddItem(new HeavyPlateJingasa());
break; break;
case 3: }
case 3:
{
AddItem(new DecorativePlateKabuto()); AddItem(new DecorativePlateKabuto());
break; 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; PackGold(250, 300);
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();
}
} }
public override bool CanTeach => true;
public override bool ClickTitle => false;
} }

View file

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

View file

@ -1,97 +1,84 @@
using ModernUO.Serialization;
using Server.Items; 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", 0 => 0,
"Destard", "Hythloth", "Shame", // Old Code for Pre-ML shards. 1 => Utility.RandomBlueHue(),
"Wrong" 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 AddItem(new FancyDress(GetRandomHue()));
"Nujel'm" }
}; else
[Constructible]
public SeekerOfAdventure() => Title = "the seeker of adventure";
public SeekerOfAdventure(Serial serial) : base(serial)
{ {
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 AddItem(new ThighBoots(lowHue));
{ }
0 => 0, else
1 => Utility.RandomBlueHue(), {
2 => Utility.RandomGreenHue(), AddItem(new Boots(lowHue));
3 => Utility.RandomRedHue(),
4 => Utility.RandomYellowHue(),
5 => Utility.RandomNeutralHue(),
_ => 0
};
} }
public override void InitOutfit() if (!Female)
{ {
if (Female) AddItem(new BodySash(lowHue));
{
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);
} }
public override void Serialize(IGenericWriter writer) AddItem(new Cloak(GetRandomHue()));
{
base.Serialize(writer);
writer.Write(0); // version AddItem(new Longsword());
}
public override void Deserialize(IGenericReader reader) Utility.AssignRandomHair(this);
{
base.Deserialize(reader);
var version = reader.ReadInt(); PackGold(100, 150);
}
} }
} }

View file

@ -1,440 +1,495 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using ModernUO.Serialization;
using Server.Gumps; using Server.Gumps;
using Server.Items; using Server.Items;
using Server.Network; using Server.Network;
using Server.Prompts; 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; } for (var i = (Entries?.Count ?? 0) - 1; i >= 0; --i)
TownCrierEntry GetRandomEntry(); {
TownCrierEntry AddEntry(string[] lines, TimeSpan duration); if (i >= Entries!.Count)
void RemoveEntry(TownCrierEntry entry); {
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) instances[i].ForceBeginAutoShout();
{
if (i >= Entries!.Count)
{
continue;
}
var tce = Entries[i];
if (tce.Expired)
{
RemoveEntry(tce);
}
}
return Entries?.RandomElement();
} }
public TownCrierEntry AddEntry(string[] lines, TimeSpan duration) return tce;
}
public void RemoveEntry(TownCrierEntry tce)
{
if (Entries == null)
{ {
Entries ??= new List<TownCrierEntry>(); return;
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;
} }
public void RemoveEntry(TownCrierEntry tce) Entries.Remove(tce);
if (Entries.Count == 0)
{ {
if (Entries == null) 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));
} }
} }
public class TownCrierEntry public static void Initialize()
{ {
public TownCrierEntry(string[] lines, TimeSpan duration) CommandSystem.Register("TownCriers", AccessLevel.GameMaster, TownCriers_OnCommand);
{
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;
} }
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.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.SendGump(new TownCrierGump(from, m_Owner)); 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; from.SendLocalizedMessage(502980); // Message entry cancelled.
private readonly TownCrierEntry m_Entry; from.SendGump(new TownCrierGump(from, m_Owner));
private readonly List<string> m_Lines; }
private readonly ITownCrierEntryList 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_Owner.RemoveEntry(m_Entry);
m_Entry = entry;
m_Lines = lines;
m_Duration = duration;
} }
public override void OnResponse(Mobile from, string text) if (m_Lines.Count > 0)
{ {
m_Lines.Add(text); m_Owner.AddEntry(m_Lines.ToArray(), m_Duration);
from.SendMessage("Message has been set.");
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);
} }
else
public override void OnCancel(Mobile from)
{ {
if (m_Entry != null) if (m_Entry != null)
{ {
m_Owner.RemoveEntry(m_Entry); from.SendMessage("Message deleted.");
}
if (m_Lines.Count > 0)
{
m_Owner.AddEntry(m_Lines.ToArray(), m_Duration);
from.SendMessage("Message has been set.");
} }
else 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 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 sb.Append("] ");
{
private readonly Mobile m_From;
private readonly ITownCrierEntryList m_Owner;
public TownCrierGump(Mobile from, ITownCrierEntryList owner) : base(50, 50) for (var j = 0; j < tce.Lines.Length; ++j)
{
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]; if (j > 0)
var toExpire = Utility.Max(tce.ExpireTime - Core.Now, TimeSpan.Zero);
var sb = new StringBuilder();
sb.Append("[Expires: ");
if (toExpire.TotalHours >= 1)
{ {
sb.Append((int)toExpire.TotalHours); sb.Append("<br>");
sb.Append(':');
sb.Append(toExpire.Minutes.ToString("D2"));
}
else
{
sb.Append(toExpire.Minutes);
} }
sb.Append(':'); sb.Append(tce.Lines[j]);
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);
} }
}
}
public override void OnResponse(NetState sender, RelayInfo info) AddHtml(8, 35 + i * 85, 254, 80, sb.ToString(), true, true);
{
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;
if (index < entries?.Count) AddButton(300 - 8 - 26, 35 + i * 85, 0x15E1, 0x15E5, 2 + i);
{
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);
}
} }
} }
} }
public class TownCrier : Mobile, ITownCrierEntryList public override void OnResponse(NetState sender, RelayInfo info)
{ {
private Timer _autoShoutTimer; if (info.ButtonID == 1)
private Timer _newsTimer;
[Constructible]
public TownCrier()
{ {
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); if (index < entries?.Count)
Title = "the town crier";
Hue = Race.Human.RandomSkinHue();
if (!Core.AOS)
{ {
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()) [SerializationGenerator(0, false)]
{ public partial class TownCrier : Mobile, ITownCrierEntryList
Body = 0x191; {
Name = NameList.RandomName("female"); private Timer _autoShoutTimer;
} private Timer _newsTimer;
else
{
Body = 0x190;
Name = NameList.RandomName("male");
}
AddItem(new FancyShirt(Utility.RandomBlueHue())); [Constructible]
public TownCrier()
{
Instances.Add(this);
var skirt = Utility.Random(2) switch InitStats(100, 100, 25);
{
0 => (Item)new Skirt(),
1 => new Kilt(),
_ => new Kilt()
};
skirt.Hue = Utility.RandomGreenHue(); Title = "the town crier";
Hue = Race.Human.RandomSkinHue();
AddItem(skirt); if (!Core.AOS)
{
AddItem(new FeatheredHat(Utility.RandomGreenHue())); NameHue = 0x35;
var boots = Utility.Random(2) switch
{
0 => (Item)new Boots(),
1 => new ThighBoots(),
_ => new ThighBoots()
};
AddItem(boots);
Utility.AssignRandomHair(this);
} }
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; } var skirt = Utility.Random(2) switch
public TownCrierEntry GetRandomEntry()
{ {
var count = Entries?.Count ?? 0; 0 => (Item)new Skirt(),
for (var i = count - 1; i >= 0; --i) 1 => new Kilt(),
{ _ => new Kilt()
if (i >= Entries!.Count) };
{
continue;
}
var tce = Entries[i]; skirt.Hue = Utility.RandomGreenHue();
if (tce.Expired) AddItem(skirt);
{
RemoveEntry(tce);
}
}
var entry = GlobalTownCrierEntryList.Instance.GetRandomEntry(); AddItem(new FeatheredHat(Utility.RandomGreenHue()));
if (count > 0 && entry != null && Utility.RandomBool())
{
return entry;
}
return Entries?.RandomElement(); var boots = Utility.Random(2) switch
}
public TownCrierEntry AddEntry(string[] lines, TimeSpan duration)
{ {
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; RemoveEntry(tce);
}
if (Entries == null && GlobalTownCrierEntryList.Instance.IsEmpty)
{
_autoShoutTimer?.Stop();
_autoShoutTimer = null;
} }
} }
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(); var tce = GetRandomEntry();
if (tce == null) if (tce == null)
{ {
_autoShoutTimer.Stop(); PublicOverheadMessage(MessageType.Regular, 0x3B2, 1005643); // I have no news at this time.
_autoShoutTimer = null;
} }
else if (_newsTimer == null) else
{ {
_newsTimer = Timer.DelayCall( _newsTimer = Timer.DelayCall(
TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0),
@ -443,89 +498,16 @@ namespace Server.Mobiles
() => ShoutNews_Callback(tce) () => ShoutNews_Callback(tce)
); );
PublicOverheadMessage(MessageType.Regular, 0x3B2, 502976); // Hear ye! Hear ye! PublicOverheadMessage(MessageType.Regular, 0x3B2, 502978); // Some of the latest news!
}
}
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;
} }
} }
} }
public override bool CanBeDamaged() => false;
public override void OnDelete()
{
Instances.Remove(this);
base.OnDelete();
}
} }