Adds assemblies config, fixes crash bugs. (#134)

This commit is contained in:
Kamron Batman 2020-05-09 12:55:56 -07:00 • committed by GitHub
parent 0140eb73b4
commit 8ec166bcd0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3223 changed files with 191 additions and 191 deletions

View file

@ -0,0 +1,96 @@
using System;
using System.Collections.Generic;
using Server.Mobiles;
namespace Server.Items
{
public class AcidSlime : Item
{
private readonly DateTime m_Created;
private bool m_Drying;
private readonly TimeSpan m_Duration;
private readonly int m_MaxDamage;
private readonly int m_MinDamage;
private readonly Timer m_Timer;
[Constructible]
public AcidSlime() : this(TimeSpan.FromSeconds(10.0), 5, 10)
{
}
[Constructible]
public AcidSlime(TimeSpan duration, int minDamage, int maxDamage)
: base(0x122A)
{
Hue = 0x3F;
Movable = false;
m_MinDamage = minDamage;
m_MaxDamage = maxDamage;
m_Created = DateTime.UtcNow;
m_Duration = duration;
m_Timer = Timer.DelayCall(TimeSpan.Zero, TimeSpan.FromSeconds(1), OnTick);
}
public AcidSlime(Serial serial) : base(serial)
{
}
public override string DefaultName => "slime";
public override void OnAfterDelete()
{
m_Timer?.Stop();
}
private void OnTick()
{
DateTime now = DateTime.UtcNow;
TimeSpan age = now - m_Created;
if (age > m_Duration)
{
Delete();
}
else
{
if (!m_Drying && age > m_Duration - age)
{
m_Drying = true;
ItemID = 0x122B;
}
List<Mobile> toDamage = new List<Mobile>();
foreach (Mobile m in GetMobilesInRange(0))
if (m.Alive && !m.IsDeadBondedPet && (!(m is BaseCreature bc) || bc.Controlled || bc.Summoned))
toDamage.Add(m);
for (int i = 0; i < toDamage.Count; i++)
Damage(toDamage[i]);
}
}
public override bool OnMoveOver(Mobile m)
{
Damage(m);
return true;
}
public void Damage(Mobile m)
{
int damage = Utility.RandomMinMax(m_MinDamage, m_MaxDamage);
if (Core.AOS)
AOS.Damage(m, damage, 0, 0, 0, 100, 0);
else
m.Damage(damage);
}
public override void Serialize(IGenericWriter writer)
{
}
public override void Deserialize(IGenericReader reader)
{
}
}
}

View file

@ -0,0 +1,211 @@
using System.Collections.Generic;
using Server.Targeting;
namespace Server.Items
{
public class ArcaneGem : Item
{
public const int DefaultArcaneHue = 2117;
[Constructible]
public ArcaneGem() : base(0x1EA7)
{
Stackable = Core.ML;
Weight = 1.0;
}
public ArcaneGem(Serial serial) : base(serial)
{
}
public override string DefaultName => "arcane gem";
public override void OnDoubleClick(Mobile from)
{
if (!IsChildOf(from.Backpack))
{
from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it.
}
else
{
from.BeginTarget(2, false, TargetFlags.None, OnTarget);
from.SendMessage("What do you wish to use the gem on?");
}
}
public int GetChargesFor(Mobile m)
{
int v = (int)(m.Skills.Tailoring.Value / 5);
if (v < 16)
return 16;
if (v > 24)
return 24;
return v;
}
public void OnTarget(Mobile from, object obj)
{
if (!IsChildOf(from.Backpack))
{
from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it.
return;
}
if (obj is IArcaneEquip eq && eq is Item item)
{
BaseClothing clothing = item as BaseClothing;
BaseArmor armor = item as BaseArmor;
BaseWeapon weapon = item as BaseWeapon;
CraftResource resource = clothing?.Resource ?? armor?.Resource ?? weapon?.Resource ?? CraftResource.None;
if (!item.IsChildOf(from.Backpack))
{
from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it.
return;
}
if (item.LootType == LootType.Blessed)
{
from.SendMessage(
"You can only use this on exceptionally crafted robes, thigh boots, cloaks, or leather gloves.");
return;
}
if (resource != CraftResource.None && resource != CraftResource.RegularLeather)
{
from.SendLocalizedMessage(1049690); // Arcane gems can not be used on that type of leather.
return;
}
int charges = GetChargesFor(from);
if (eq.IsArcane)
{
if (eq.CurArcaneCharges >= eq.MaxArcaneCharges)
{
from.SendMessage("That item is already fully charged.");
}
else
{
if (eq.CurArcaneCharges <= 0)
item.Hue = DefaultArcaneHue;
if (eq.CurArcaneCharges + charges > eq.MaxArcaneCharges)
eq.CurArcaneCharges = eq.MaxArcaneCharges;
else
eq.CurArcaneCharges += charges;
from.SendMessage("You recharge the item.");
if (Amount <= 1)
Delete();
else Amount--;
}
}
else if (from.Skills.Tailoring.Value >= 80.0)
{
bool isExceptional = clothing?.Quality == ClothingQuality.Exceptional ||
armor?.Quality == ArmorQuality.Exceptional ||
weapon?.Quality == WeaponQuality.Exceptional;
if (isExceptional)
{
if (clothing != null)
{
clothing.Quality = ClothingQuality.Regular;
clothing.Crafter = from;
}
else if (armor != null)
{
armor.Quality = ArmorQuality.Regular;
armor.Crafter = from;
armor.PhysicalBonus =
armor.FireBonus =
armor.ColdBonus =
armor.PoisonBonus = armor.EnergyBonus = 0; // Is there a method to remove bonuses?
}
else
{
weapon.Quality = WeaponQuality.Regular;
weapon.Crafter = from;
}
eq.CurArcaneCharges = eq.MaxArcaneCharges = charges;
item.Hue = DefaultArcaneHue;
from.SendMessage("You enhance the item with your gem.");
if (Amount <= 1)
Delete();
else Amount--;
}
else
{
from.SendMessage("Only exceptional items can be enhanced with the gem.");
}
}
else
{
from.SendMessage("You do not have enough skill in tailoring to enhance the item.");
}
}
else
{
from.SendMessage(
"You can only use this on exceptionally crafted robes, thigh boots, cloaks, or leather gloves.");
}
}
public static bool ConsumeCharges(Mobile from, int amount)
{
List<Item> items = from.Items;
int avail = 0;
for (int i = 0; i < items.Count; ++i)
{
Item obj = items[i];
if (obj is IArcaneEquip eq && eq.IsArcane)
avail += eq.CurArcaneCharges;
}
if (avail < amount)
return false;
for (int i = 0; i < items.Count; ++i)
{
Item obj = items[i];
if (obj is IArcaneEquip eq && eq.IsArcane)
{
if (eq.CurArcaneCharges > amount)
{
eq.CurArcaneCharges -= amount;
break;
}
amount -= eq.CurArcaneCharges;
eq.CurArcaneCharges = 0;
}
}
return true;
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,247 @@
using System;
using System.Globalization;
using Server.Accounting;
using Server.Engines.Quests;
using Server.Engines.Quests.Haven;
using Server.Engines.Quests.Necro;
using Server.Mobiles;
using Server.Network;
using CashBankCheckObjective = Server.Engines.Quests.Necro.CashBankCheckObjective;
namespace Server.Items
{
public class BankCheck : Item
{
private int m_Worth;
public BankCheck(Serial serial) : base(serial)
{
}
[Constructible]
public BankCheck(int worth) : base(0x14F0)
{
Weight = 1.0;
Hue = 0x34;
LootType = LootType.Blessed;
m_Worth = worth;
}
[CommandProperty(AccessLevel.GameMaster)]
public int Worth
{
get => m_Worth;
set
{
m_Worth = value;
InvalidateProperties();
}
}
public override bool DisplayLootType => Core.AOS;
public override int LabelNumber => 1041361; // A bank check
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
writer.Write(m_Worth);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
LootType = LootType.Blessed;
int version = reader.ReadInt();
switch (version)
{
case 0:
{
m_Worth = reader.ReadInt();
break;
}
}
}
public override void GetProperties(ObjectPropertyList list)
{
base.GetProperties(list);
string worth;
if (Core.ML)
worth = m_Worth.ToString("N0", CultureInfo.GetCultureInfo("en-US"));
else
worth = m_Worth.ToString();
list.Add(1060738, worth); // value: ~1_val~
}
public override void OnAdded(IEntity parent)
{
base.OnAdded(parent);
if (!AccountGold.Enabled) return;
Mobile owner = null;
SecureTradeInfo tradeInfo = null;
Container root = parent as Container;
while (root?.Parent is Container container)
root = container;
parent = root ?? parent;
if (parent is SecureTradeContainer trade && AccountGold.ConvertOnTrade)
{
if (trade.Trade.From.Container == trade)
{
tradeInfo = trade.Trade.From;
owner = tradeInfo.Mobile;
}
else if (trade.Trade.To.Container == trade)
{
tradeInfo = trade.Trade.To;
owner = tradeInfo.Mobile;
}
}
else if (parent is BankBox box && AccountGold.ConvertOnBank)
{
owner = box.Owner;
}
if (owner?.Account?.DepositGold(Worth) != true) return;
if (tradeInfo != null)
{
if (owner.NetState?.NewSecureTrading == false)
{
int plat = Math.DivRem(Worth, AccountGold.CurrencyThreshold, out int gold);
tradeInfo.Plat += plat;
tradeInfo.Gold += gold;
}
tradeInfo.VirtualCheck?.UpdateTrade(tradeInfo.Mobile);
}
owner.SendLocalizedMessage(1042763, Worth.ToString("#,0"));
Delete();
((Container)parent).UpdateTotals();
}
public override void OnSingleClick(Mobile from)
{
from.Send(
new MessageLocalizedAffix(
Serial,
ItemID,
MessageType.Label,
0x3B2,
3,
1041361,
"",
AffixType.Append,
$" {m_Worth}",
"")); // A bank check:
}
public override void OnDoubleClick(Mobile from)
{
// This probably isn't OSI accurate, but we can't just make the quests redundant.
// Double-clicking the BankCheck in your pack will now credit your account.
Container box = AccountGold.Enabled ? from.Backpack : from.FindBankNoCreate();
if (box == null || !IsChildOf(box))
{
from.SendLocalizedMessage(AccountGold.Enabled ? 1080058 : 1047026);
// This must be in your backpack to use it. : That must be in your bank box to use it.
return;
}
Delete();
int deposited = 0;
int toAdd = m_Worth;
if (AccountGold.Enabled && from.Account?.DepositGold(toAdd) == true)
{
deposited = toAdd;
toAdd = 0;
}
if (toAdd > 0)
{
Gold gold;
while (toAdd > 60000)
{
gold = new Gold(60000);
if (box.TryDropItem(from, gold, false))
{
toAdd -= 60000;
deposited += 60000;
}
else
{
gold.Delete();
from.AddToBackpack(new BankCheck(toAdd));
toAdd = 0;
break;
}
}
if (toAdd > 0)
{
gold = new Gold(toAdd);
if (box.TryDropItem(from, gold, false))
{
deposited += toAdd;
}
else
{
gold.Delete();
from.AddToBackpack(new BankCheck(toAdd));
}
}
}
// Gold was deposited in your account:
from.SendLocalizedMessage(1042672, true, deposited.ToString("#,0"));
if (from is PlayerMobile pm)
{
QuestSystem qs = pm.Quest;
if (qs is DarkTidesQuest)
{
QuestObjective obj = qs.FindObjective<CashBankCheckObjective>();
if (obj?.Completed == false) obj.Complete();
}
if (qs is UzeraanTurmoilQuest)
{
QuestObjective obj = qs.FindObjective(typeof(Engines.Quests.Haven.CashBankCheckObjective));
if (obj?.Completed == false) obj.Complete();
}
}
}
}
}

View file

@ -0,0 +1,30 @@
namespace Server.Items
{
public class GlobOfMonstreousInterredGrizzle : Item
{
[Constructible]
public GlobOfMonstreousInterredGrizzle() : base(0x2F3)
{
}
public GlobOfMonstreousInterredGrizzle(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1072117; // Glob of Monsterous Interred Grizzle
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,30 @@
namespace Server.Items
{
public class GrizzledSkullCollection : Item
{
[Constructible]
public GrizzledSkullCollection() : base(0x21FC)
{
}
public GrizzledSkullCollection(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1072116; // Grizzled Skull collection
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,30 @@
namespace Server.Items
{
public class MonsterousInterredGrizzleMaggots : Item
{
[Constructible]
public MonsterousInterredGrizzleMaggots() : base(0x2633)
{
}
public MonsterousInterredGrizzleMaggots(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1075090; // Monsterous Interred Grizzle Maggots
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,30 @@
namespace Server.Items
{
public class ResolvesBridle : Item
{
[Constructible]
public ResolvesBridle() : base(0x1374)
{
}
public ResolvesBridle(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1074761; // Resolve's Bridle
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,30 @@
namespace Server.Items
{
public class TombstoneOfTheDamned : Item
{
[Constructible]
public TombstoneOfTheDamned() : base(Utility.RandomMinMax(0xED7, 0xEDE))
{
}
public TombstoneOfTheDamned(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1072123; // Tombstone of the Damned
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,31 @@
namespace Server.Items
{
public class Beeswax : Item
{
[Constructible]
public Beeswax(int amount = 1) : base(0x1422)
{
Weight = 1.0;
Stackable = true;
Amount = amount;
}
public Beeswax(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,32 @@
namespace Server.Items
{
public class AbscessTail : Item
{
[Constructible]
public AbscessTail() : base(0x1A9D)
{
LootType = LootType.Blessed;
Hue = 0x51D; // TODO check
}
public AbscessTail(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1074231; // Abscess' Tail
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,32 @@
namespace Server.Items
{
public class CoilsFang : Item
{
[Constructible]
public CoilsFang() : base(0x10E8)
{
LootType = LootType.Blessed;
Hue = 0x487;
}
public CoilsFang(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1074229; // Coil's Fang
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,28 @@
namespace Server.Items
{
public class EternallyCorruptTree : Item
{
[Constructible]
public EternallyCorruptTree() : base(0x20FA) => Hue = Utility.RandomMinMax(0x899, 0x8B0);
public EternallyCorruptTree(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1072093; // Eternally Corrupt Tree
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,32 @@
namespace Server.Items
{
public class HydraScale : Item
{
[Constructible]
public HydraScale() : base(0x26B4)
{
LootType = LootType.Blessed;
Hue = 0xC2; // TODO check
}
public HydraScale(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1074760; // A hydra scale.
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,47 @@
namespace Server.Items
{
public class MelisandesFermentedWine : GreaterExplosionPotion
{
[Constructible]
public MelisandesFermentedWine()
{
Stackable = false;
ItemID = 0x99B;
Hue = Utility.RandomList(0xB, 0xF, 0x48D); // TODO update
}
public MelisandesFermentedWine(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1072114; // Melisande's Fermented Wine
public override void Drink(Mobile from)
{
if (MondainsLegacy.CheckML(from))
base.Drink(from);
}
public override void GetProperties(ObjectPropertyList list)
{
base.GetProperties(list);
list.Add(1074502); // It looks explosive.
list.Add(1075085); // Requirement: Mondain's Legacy
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,89 @@
using Server.Gumps;
namespace Server.Items
{
public class MelisandesHairDye : Item
{
[Constructible]
public MelisandesHairDye() : base(0xEFF) => Hue = Utility.RandomMinMax(0x47E, 0x499);
public MelisandesHairDye(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1041088; // Hair Dye
public override void OnDoubleClick(Mobile from)
{
if (IsChildOf(from.Backpack))
{
if (MondainsLegacy.CheckML(from))
from.SendGump(new ConfirmGump(this));
}
else
{
from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it.
}
}
public override void GetProperties(ObjectPropertyList list)
{
base.GetProperties(list);
list.Add(1075085); // Requirement: Mondain's Legacy
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
private class ConfirmGump : BaseConfirmGump
{
private readonly Item m_Item;
public ConfirmGump(Item item) => m_Item = item;
public override int TitleNumber => 1074395; // <div align=right>Use Permanent Hair Dye</div>
public override int LabelNumber =>
1074396; // This special hair dye is made of a unique mixture of leaves, permanently changing one's hair color until another dye is used.
public override void Confirm(Mobile from)
{
if (m_Item?.Deleted == false && m_Item.IsChildOf(from.Backpack))
{
if (from.HairItemID != 0)
{
from.HairHue = m_Item.Hue;
from.PlaySound(0x240);
from.SendLocalizedMessage(502622); // You dye your hair.
m_Item.Delete();
}
else
{
from.SendLocalizedMessage(502623); // You have no hair to dye and you cannot use this.
}
}
else
{
from.SendLocalizedMessage(1073461); // You don't have enough dye.
}
}
public override void Refuse(Mobile from)
{
from.SendLocalizedMessage(502620); // You decide not to dye your hair.
}
}
}
}

View file

@ -0,0 +1,32 @@
namespace Server.Items
{
public class SalivasFeather : Item
{
[Constructible]
public SalivasFeather() : base(0x1020)
{
LootType = LootType.Blessed;
Hue = 0x5C;
}
public SalivasFeather(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1074234; // Saliva's Feather
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,28 @@
namespace Server.Items
{
public class SamplesOfCorruptedWater : Item
{
[Constructible]
public SamplesOfCorruptedWater() : base(0xEFE) => LootType = LootType.Blessed;
public SamplesOfCorruptedWater(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1074999; // samples of corrupted water
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,32 @@
namespace Server.Items
{
public class TaintedSeeds : Item
{
[Constructible]
public TaintedSeeds() : base(0xDFA)
{
LootType = LootType.Blessed;
Hue = 0x48; // TODO check
}
public TaintedSeeds(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1074233; // Tainted Seeds
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,32 @@
namespace Server.Items
{
public class ThorvaldsMedallion : Item
{
[Constructible]
public ThorvaldsMedallion() : base(0x2AAA)
{
LootType = LootType.Blessed;
Hue = 0x47F; // TODO check
}
public ThorvaldsMedallion(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1074232; // Thorvald's Medallion
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,32 @@
namespace Server.Items
{
public class ThrashersTail : Item
{
[Constructible]
public ThrashersTail() : base(0x1A9D)
{
LootType = LootType.Blessed;
Hue = 0x455;
}
public ThrashersTail(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1074230; // Thrasher's Tail
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,102 @@
using Server.Network;
namespace Server.Items
{
public class Blocker : Item
{
[Constructible]
public Blocker() : base(0x21A4) => Movable = false;
public Blocker(Serial serial) : base(serial)
{
}
public override int LabelNumber => 503057; // Impassable!
protected override Packet GetWorldPacketFor(NetState state)
{
Mobile mob = state.Mobile;
if (mob?.AccessLevel >= AccessLevel.GameMaster)
return new GMItemPacket(this);
return base.GetWorldPacketFor(state);
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
public sealed class GMItemPacket : Packet
{
public GMItemPacket(Item item) : base(0x1A)
{
EnsureCapacity(20);
// 14 base length
// +2 - Amount
// +2 - Hue
// +1 - Flags
uint serial = item.Serial.Value;
int itemID = 0x1183;
int amount = item.Amount;
Point3D loc = item.Location;
int x = loc.X;
int y = loc.Y;
int hue = item.Hue;
int flags = item.GetPacketFlags();
int direction = (int)item.Direction;
if (amount != 0)
serial |= 0x80000000;
else
serial &= 0x7FFFFFFF;
Stream.Write(serial);
Stream.Write((short)(itemID & 0x7FFF));
if (amount != 0)
Stream.Write((short)amount);
x &= 0x7FFF;
if (direction != 0)
x |= 0x8000;
Stream.Write((short)x);
y &= 0x3FFF;
if (hue != 0)
y |= 0x8000;
if (flags != 0)
y |= 0x4000;
Stream.Write((short)y);
if (direction != 0)
Stream.Write((byte)direction);
Stream.Write((sbyte)loc.Z);
if (hue != 0)
Stream.Write((ushort)hue);
if (flags != 0)
Stream.Write((byte)flags);
}
}
}
}

View file

@ -0,0 +1,56 @@
using System;
namespace Server.Items
{
public class Blood : Item
{
[Constructible]
public Blood() : this(Utility.RandomList(0x1645, 0x122A, 0x122B, 0x122C, 0x122D, 0x122E, 0x122F))
{
}
[Constructible]
public Blood(int itemID) : base(itemID)
{
Movable = false;
new InternalTimer(this).Start();
}
public Blood(Serial serial) : base(serial)
{
new InternalTimer(this).Start();
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
private class InternalTimer : Timer
{
private readonly Item m_Blood;
public InternalTimer(Item blood) : base(TimeSpan.FromSeconds(5.0))
{
Priority = TimerPriority.OneSecond;
m_Blood = blood;
}
protected override void OnTick()
{
m_Blood.Delete();
}
}
}
}

View file

@ -0,0 +1,209 @@
using System;
using Server.Mobiles;
using Server.Network;
using Server.Spells.Ninjitsu;
using Server.Targeting;
namespace Server.Items
{
public class Bola : Item
{
[Constructible]
public Bola(int amount = 1) : base(0x26AC)
{
Weight = 4.0;
Stackable = true;
Amount = amount;
}
public Bola(Serial serial) : base(serial)
{
}
public override void OnDoubleClick(Mobile from)
{
if (!IsChildOf(from.Backpack))
{
from.SendLocalizedMessage(1040019); // The bola must be in your pack to use it.
}
else if (!from.CanBeginAction<Bola>())
{
from.SendLocalizedMessage(1049624); // You have to wait a few moments before you can use another bola!
}
else if (from.Target is BolaTarget)
{
from.SendLocalizedMessage(1049631); // This bola is already being used.
}
else if (!HasFreeHands(from))
{
from.SendLocalizedMessage(1040015); // Your hands must be free to use this
}
else if (from.Mounted)
{
from.SendLocalizedMessage(1040016); // You cannot use this while riding a mount
}
else if (AnimalForm.UnderTransformation(from))
{
from.SendLocalizedMessage(1070902); // You can't use this while in an animal form!
}
else
{
EtherealMount.StopMounting(from);
from.Target = new BolaTarget(this);
from.LocalOverheadMessage(MessageType.Emote, 0x3B2, 1049632); // * You begin to swing the bola...*
from.NonlocalOverheadMessage(MessageType.Emote, 0x3B2, 1049633,
from.Name); // ~1_NAME~ begins to menacingly swing a bola...
}
}
private static void FinishThrow(Mobile from, Mobile to)
{
if (Core.AOS)
new Bola().MoveToWorld(to.Location, to.Map);
if (to is ChaosDragoon || to is ChaosDragoonElite)
from.SendLocalizedMessage(1042047); // You fail to knock the rider from its mount.
IMount mt = to.Mount;
if (mt != null && !(to is ChaosDragoon || to is ChaosDragoonElite))
mt.Rider = null;
if (to is PlayerMobile mobile)
{
if (AnimalForm.UnderTransformation(mobile))
mobile.SendLocalizedMessage(1114066, from.Name); // ~1_NAME~ knocked you out of animal form!
else if (mobile.Mounted) mobile.SendLocalizedMessage(1040023); // You have been knocked off of your mount!
mobile.SetMountBlock(BlockMountType.Dazed, TimeSpan.FromSeconds(Core.ML ? 10 : 3), true);
}
if (Core.AOS) /* only failsafe, attacker should already be dismounted */
(from as PlayerMobile)?.SetMountBlock(BlockMountType.BolaRecovery, TimeSpan.FromSeconds(Core.ML ? 10 : 3),
true);
to.Damage(1);
Timer.DelayCall(TimeSpan.FromSeconds(2.0), () => from.EndAction<Bola>());
}
private static bool HasFreeHands(Mobile from)
{
Item one = from.FindItemOnLayer(Layer.OneHanded);
Item two = from.FindItemOnLayer(Layer.TwoHanded);
if (Core.SE)
{
Container pack = from.Backpack;
if (pack != null)
{
if (one?.Movable == true)
{
pack.DropItem(one);
one = null;
}
if (two?.Movable == true)
{
pack.DropItem(two);
two = null;
}
}
}
else if (Core.AOS)
{
if (one?.Movable == true)
{
from.AddToBackpack(one);
one = null;
}
if (two?.Movable == true)
{
from.AddToBackpack(two);
two = null;
}
}
return one == null && two == null;
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
public class BolaTarget : Target
{
private readonly Bola m_Bola;
public BolaTarget(Bola bola) : base(8, false, TargetFlags.Harmful) => m_Bola = bola;
protected override void OnTarget(Mobile from, object obj)
{
if (m_Bola.Deleted)
return;
if (obj is Mobile to)
{
if (!m_Bola.IsChildOf(from.Backpack))
{
from.SendLocalizedMessage(1040019); // The bola must be in your pack to use it.
}
else if (!HasFreeHands(from))
{
from.SendLocalizedMessage(1040015); // Your hands must be free to use this
}
else if (from.Mounted)
{
from.SendLocalizedMessage(1040016); // You cannot use this while riding a mount
}
else if (AnimalForm.UnderTransformation(from))
{
from.SendLocalizedMessage(1070902); // You can't use this while in an animal form!
}
else if (!to.Mounted && !AnimalForm.UnderTransformation(to))
{
from.SendLocalizedMessage(1049628); // You have no reason to throw a bola at that.
}
else if (!from.CanBeHarmful(to))
{
}
else if (from.BeginAction<Bola>())
{
EtherealMount.StopMounting(from);
from.DoHarmful(to);
m_Bola.Consume();
from.Direction = from.GetDirectionTo(to);
from.Animate(11, 5, 1, true, false, 0);
from.MovingEffect(to, 0x26AC, 10, 0, false, false);
Timer.DelayCall(TimeSpan.FromSeconds(0.5), () => FinishThrow(from, to));
}
else
{
from.SendLocalizedMessage(
1049624); // You have to wait a few moments before you can use another bola!
}
}
else
{
from.SendLocalizedMessage(1049629); // You cannot throw a bola at that.
}
}
}
}
}

View file

@ -0,0 +1,32 @@
namespace Server.Items
{
public class BolaBall : Item
{
[Constructible]
public BolaBall(int amount = 1) : base(0xE73)
{
Weight = 4.0;
Stackable = true;
Amount = amount;
Hue = 0x8AC;
}
public BolaBall(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,600 @@
using System;
using System.Collections.Generic;
using Server.Network;
using Server.Targeting;
namespace Server.Items
{
[Flippable(0x1E5E, 0x1E5F)]
public class BulletinBoard : BaseBulletinBoard
{
[Constructible]
public BulletinBoard() : base(0x1E5E)
{
}
public BulletinBoard(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
public abstract class BaseBulletinBoard : Item
{
// Threads will be removed six hours after the last post was made
private static readonly TimeSpan ThreadDeletionTime = TimeSpan.FromHours(6.0);
// A player may only create a thread once every two minutes
private static readonly TimeSpan ThreadCreateTime = TimeSpan.FromMinutes(2.0);
// A player may only reply once every thirty seconds
private static readonly TimeSpan ThreadReplyTime = TimeSpan.FromSeconds(30.0);
public BaseBulletinBoard(int itemID) : base(itemID)
{
BoardName = "bulletin board";
Movable = false;
}
public BaseBulletinBoard(Serial serial) : base(serial)
{
}
[CommandProperty(AccessLevel.GameMaster)]
public string BoardName { get; set; }
public static bool CheckTime(DateTime time, TimeSpan range) => time + range < DateTime.UtcNow;
public static string FormatTS(TimeSpan ts)
{
int totalSeconds = (int)ts.TotalSeconds;
int seconds = totalSeconds % 60;
int minutes = totalSeconds / 60;
if (minutes != 0 && seconds != 0)
return $"{minutes} minute{(minutes == 1 ? "" : "s")} and {seconds} second{(seconds == 1 ? "" : "s")}";
if (minutes != 0)
return $"{minutes} minute{(minutes == 1 ? "" : "s")}";
return $"{seconds} second{(seconds == 1 ? "" : "s")}";
}
public virtual void Cleanup()
{
List<Item> items = Items;
for (int i = items.Count - 1; i >= 0; --i)
{
if (i >= items.Count)
continue;
if (!(items[i] is BulletinMessage msg))
continue;
if (msg.Thread == null && CheckTime(msg.LastPostTime, ThreadDeletionTime))
{
msg.Delete();
RecurseDelete(msg); // A root-level thread has expired
}
}
}
private void RecurseDelete(BulletinMessage msg)
{
List<Item> found = new List<Item>();
List<Item> items = Items;
for (int i = items.Count - 1; i >= 0; --i)
{
if (i >= items.Count)
continue;
if (!(items[i] is BulletinMessage check))
continue;
if (check.Thread == msg)
{
check.Delete();
found.Add(check);
}
}
for (int i = 0; i < found.Count; ++i)
RecurseDelete((BulletinMessage)found[i]);
}
public virtual bool GetLastPostTime(Mobile poster, bool onlyCheckRoot, ref DateTime lastPostTime)
{
List<Item> items = Items;
bool wasSet = false;
for (int i = 0; i < items.Count; ++i)
{
if (!(items[i] is BulletinMessage msg) || msg.Poster != poster)
continue;
if (onlyCheckRoot && msg.Thread != null)
continue;
if (msg.Time > lastPostTime)
{
wasSet = true;
lastPostTime = msg.Time;
}
}
return wasSet;
}
public override void OnDoubleClick(Mobile from)
{
if (CheckRange(from))
{
Cleanup();
NetState state = from.NetState;
state.Send(new BBDisplayBoard(this));
if (state.ContainerGridLines)
state.Send(new ContainerContent6017(from, this));
else
state.Send(new ContainerContent(from, this));
}
else
{
from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that.
}
}
public virtual bool CheckRange(Mobile from)
{
if (from.AccessLevel >= AccessLevel.GameMaster)
return true;
return from.Map == Map && from.InRange(GetWorldLocation(), 2);
}
public void PostMessage(Mobile from, BulletinMessage thread, string subject, string[] lines)
{
if (thread != null)
thread.LastPostTime = DateTime.UtcNow;
AddItem(new BulletinMessage(from, thread, subject, lines));
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
writer.Write(BoardName);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 0:
{
BoardName = reader.ReadString();
break;
}
}
}
public static void Initialize()
{
PacketHandlers.Register(0x71, 0, true, BBClientRequest);
}
public static void BBClientRequest(NetState state, PacketReader pvSrc)
{
Mobile from = state.Mobile;
int packetID = pvSrc.ReadByte();
if (!(World.FindItem(pvSrc.ReadUInt32()) is BaseBulletinBoard board) || !board.CheckRange(from))
return;
switch (packetID)
{
case 3:
BBRequestContent(from, board, pvSrc);
break;
case 4:
BBRequestHeader(from, board, pvSrc);
break;
case 5:
BBPostMessage(from, board, pvSrc);
break;
case 6:
BBRemoveMessage(from, board, pvSrc);
break;
}
}
public static void BBRequestContent(Mobile from, BaseBulletinBoard board, PacketReader pvSrc)
{
if (!(World.FindItem(pvSrc.ReadUInt32()) is BulletinMessage msg) || msg.Parent != board)
return;
from.Send(new BBMessageContent(board, msg));
}
public static void BBRequestHeader(Mobile from, BaseBulletinBoard board, PacketReader pvSrc)
{
if (!(World.FindItem(pvSrc.ReadUInt32()) is BulletinMessage msg) || msg.Parent != board)
return;
from.Send(new BBMessageHeader(board, msg));
}
public static void BBPostMessage(Mobile from, BaseBulletinBoard board, PacketReader pvSrc)
{
BulletinMessage thread = World.FindItem(pvSrc.ReadUInt32()) as BulletinMessage;
if (thread != null && thread.Parent != board)
thread = null;
int breakout = 0;
while (thread?.Thread != null && breakout++ < 10)
thread = thread.Thread;
DateTime lastPostTime = DateTime.MinValue;
if (board.GetLastPostTime(from, thread == null, ref lastPostTime))
if (!CheckTime(lastPostTime, thread == null ? ThreadCreateTime : ThreadReplyTime))
{
if (thread == null)
from.SendMessage("You must wait {0} before creating a new thread.", FormatTS(ThreadCreateTime));
else
from.SendMessage("You must wait {0} before replying to another thread.", FormatTS(ThreadReplyTime));
return;
}
string subject = pvSrc.ReadUTF8StringSafe(pvSrc.ReadByte());
if (subject.Length == 0)
return;
string[] lines = new string[pvSrc.ReadByte()];
if (lines.Length == 0)
return;
for (int i = 0; i < lines.Length; ++i)
lines[i] = pvSrc.ReadUTF8StringSafe(pvSrc.ReadByte());
board.PostMessage(from, thread, subject, lines);
}
public static void BBRemoveMessage(Mobile from, BaseBulletinBoard board, PacketReader pvSrc)
{
if (!(World.FindItem(pvSrc.ReadUInt32()) is BulletinMessage msg) || msg.Parent != board)
return;
if (from.AccessLevel < AccessLevel.GameMaster && msg.Poster != from)
return;
msg.Delete();
}
}
public struct BulletinEquip
{
public int itemID;
public int hue;
public BulletinEquip(int itemID, int hue)
{
this.itemID = itemID;
this.hue = hue;
}
}
public class BulletinMessage : Item
{
public BulletinMessage(Mobile poster, BulletinMessage thread, string subject, string[] lines) : base(0xEB0)
{
Movable = false;
Poster = poster;
Subject = subject;
Time = DateTime.UtcNow;
LastPostTime = Time;
Thread = thread;
PostedName = Poster.Name;
PostedBody = Poster.Body;
PostedHue = Poster.Hue;
Lines = lines;
List<BulletinEquip> list = new List<BulletinEquip>();
for (int i = 0; i < poster.Items.Count; ++i)
{
Item item = poster.Items[i];
if (item.Layer >= Layer.OneHanded && item.Layer <= Layer.Mount)
list.Add(new BulletinEquip(item.ItemID, item.Hue));
}
PostedEquip = list.ToArray();
}
public BulletinMessage(Serial serial) : base(serial)
{
}
public Mobile Poster { get; private set; }
public BulletinMessage Thread { get; private set; }
public string Subject { get; private set; }
public DateTime Time { get; private set; }
public DateTime LastPostTime { get; set; }
public string PostedName { get; private set; }
public int PostedBody { get; private set; }
public int PostedHue { get; private set; }
public BulletinEquip[] PostedEquip { get; private set; }
public string[] Lines { get; private set; }
public string GetTimeAsString() => Time.ToString("MMM dd, yyyy");
public override bool CheckTarget(Mobile from, Target targ, object targeted) => false;
public override bool IsAccessibleTo(Mobile check) => false;
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(1); // version
writer.Write(Poster);
writer.Write(Subject);
writer.Write(Time);
writer.Write(LastPostTime);
writer.Write(Thread != null);
writer.Write(Thread);
writer.Write(PostedName);
writer.Write(PostedBody);
writer.Write(PostedHue);
writer.Write(PostedEquip.Length);
for (int i = 0; i < PostedEquip.Length; ++i)
{
writer.Write(PostedEquip[i].itemID);
writer.Write(PostedEquip[i].hue);
}
writer.Write(Lines.Length);
for (int i = 0; i < Lines.Length; ++i)
writer.Write(Lines[i]);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 1:
case 0:
{
Poster = reader.ReadMobile();
Subject = reader.ReadString();
Time = reader.ReadDateTime();
LastPostTime = reader.ReadDateTime();
bool hasThread = reader.ReadBool();
Thread = reader.ReadItem() as BulletinMessage;
PostedName = reader.ReadString();
PostedBody = reader.ReadInt();
PostedHue = reader.ReadInt();
PostedEquip = new BulletinEquip[reader.ReadInt()];
for (int i = 0; i < PostedEquip.Length; ++i)
{
PostedEquip[i].itemID = reader.ReadInt();
PostedEquip[i].hue = reader.ReadInt();
}
Lines = new string[reader.ReadInt()];
for (int i = 0; i < Lines.Length; ++i)
Lines[i] = reader.ReadString();
if (hasThread && Thread == null)
Delete();
if (version == 0)
ValidationQueue<BulletinMessage>.Add(this);
break;
}
}
}
public void Validate()
{
if ((Parent as BulletinBoard)?.Items.Contains(this) == false)
Delete();
}
}
public class BBDisplayBoard : Packet
{
public BBDisplayBoard(BaseBulletinBoard board) : base(0x71)
{
EnsureCapacity(38);
byte[] buffer = Utility.UTF8.GetBytes(board.BoardName ?? "");
Stream.Write((byte)0x00); // PacketID
Stream.Write(board.Serial); // Bulletin board serial
// Bulletin board name
if (buffer.Length >= 29)
{
Stream.Write(buffer, 0, 29);
Stream.Write((byte)0);
}
else
{
Stream.Write(buffer, 0, buffer.Length);
Stream.Fill(30 - buffer.Length);
}
}
}
public class BBMessageHeader : Packet
{
public BBMessageHeader(BaseBulletinBoard board, BulletinMessage msg) : base(0x71)
{
string poster = SafeString(msg.PostedName);
string subject = SafeString(msg.Subject);
string time = SafeString(msg.GetTimeAsString());
EnsureCapacity(22 + poster.Length + subject.Length + time.Length);
Stream.Write((byte)0x01); // PacketID
Stream.Write(board.Serial); // Bulletin board serial
Stream.Write(msg.Serial); // Message serial
BulletinMessage thread = msg.Thread;
if (thread == null)
Stream.Write(0); // Thread serial--root
else
Stream.Write(thread.Serial); // Thread serial--parent
WriteString(poster);
WriteString(subject);
WriteString(time);
}
public void WriteString(string v)
{
byte[] buffer = Utility.UTF8.GetBytes(v);
int len = buffer.Length + 1;
if (len > 255)
len = 255;
Stream.Write((byte)len);
Stream.Write(buffer, 0, len - 1);
Stream.Write((byte)0);
}
public string SafeString(string v) => v ?? string.Empty;
}
public class BBMessageContent : Packet
{
public BBMessageContent(BaseBulletinBoard board, BulletinMessage msg) : base(0x71)
{
string poster = SafeString(msg.PostedName);
string subject = SafeString(msg.Subject);
string time = SafeString(msg.GetTimeAsString());
EnsureCapacity(22 + poster.Length + subject.Length + time.Length);
Stream.Write((byte)0x02); // PacketID
Stream.Write(board.Serial); // Bulletin board serial
Stream.Write(msg.Serial); // Message serial
WriteString(poster);
WriteString(subject);
WriteString(time);
Stream.Write((short)msg.PostedBody);
Stream.Write((short)msg.PostedHue);
int len = msg.PostedEquip.Length;
if (len > 255)
len = 255;
Stream.Write((byte)len);
for (int i = 0; i < len; ++i)
{
BulletinEquip eq = msg.PostedEquip[i];
Stream.Write((short)eq.itemID);
Stream.Write((short)eq.hue);
}
len = msg.Lines.Length;
if (len > 255)
len = 255;
Stream.Write((byte)len);
for (int i = 0; i < len; ++i)
WriteString(msg.Lines[i], true);
}
public void WriteString(string v)
{
WriteString(v, false);
}
public void WriteString(string v, bool padding)
{
byte[] buffer = Utility.UTF8.GetBytes(v);
int tail = padding ? 2 : 1;
int len = buffer.Length + tail;
if (len > 255)
len = 255;
Stream.Write((byte)len);
Stream.Write(buffer, 0, len - tail);
if (padding)
Stream.Write((short)0); // padding compensates for a client bug
else
Stream.Write((byte)0);
}
public string SafeString(string v)
{
if (v == null)
return string.Empty;
return v;
}
}
}

View file

@ -0,0 +1,129 @@
using Server.Mobiles;
namespace Server.Items
{
public class ClockworkAssembly : Item
{
[Constructible]
public ClockworkAssembly() : base(0x1EA8)
{
Weight = 5.0;
Hue = 1102;
}
public ClockworkAssembly(Serial serial) : base(serial)
{
}
public override string DefaultName => "clockwork assembly";
public override void OnDoubleClick(Mobile from)
{
if (!IsChildOf(from.Backpack))
{
from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it.
return;
}
double tinkerSkill = from.Skills.Tinkering.Value;
if (tinkerSkill < 60.0)
{
from.SendMessage("You must have at least 60.0 skill in tinkering to construct a golem.");
return;
}
if (from.Followers + 4 > from.FollowersMax)
{
from.SendLocalizedMessage(1049607); // You have too many followers to control that creature.
return;
}
double scalar;
if (tinkerSkill >= 100.0)
scalar = 1.0;
else if (tinkerSkill >= 90.0)
scalar = 0.9;
else if (tinkerSkill >= 80.0)
scalar = 0.8;
else if (tinkerSkill >= 70.0)
scalar = 0.7;
else
scalar = 0.6;
Container pack = from.Backpack;
if (pack == null)
return;
int res = pack.ConsumeTotal(
new[]
{
typeof(PowerCrystal),
typeof(IronIngot),
typeof(BronzeIngot),
typeof(Gears)
},
new[]
{
1,
50,
50,
5
});
switch (res)
{
case 0:
{
from.SendMessage("You must have a power crystal to construct the golem.");
break;
}
case 1:
{
from.SendMessage("You must have 50 iron ingots to construct the golem.");
break;
}
case 2:
{
from.SendMessage("You must have 50 bronze ingots to construct the golem.");
break;
}
case 3:
{
from.SendMessage("You must have 5 gears to construct the golem.");
break;
}
default:
{
Golem g = new Golem(true, scalar);
if (g.SetControlMaster(from))
{
Delete();
g.MoveToWorld(from.Location, from.Map);
from.PlaySound(0x241);
}
break;
}
}
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,441 @@
using System;
using System.Collections.Generic;
using Server.Network;
using Server.Targeting;
namespace Server.Items
{
public class CrystalRechargeInfo
{
public static readonly CrystalRechargeInfo[] Table =
{
new CrystalRechargeInfo(typeof(Citrine), 500),
new CrystalRechargeInfo(typeof(Amber), 500),
new CrystalRechargeInfo(typeof(Tourmaline), 750),
new CrystalRechargeInfo(typeof(Emerald), 1000),
new CrystalRechargeInfo(typeof(Sapphire), 1000),
new CrystalRechargeInfo(typeof(Amethyst), 1000),
new CrystalRechargeInfo(typeof(StarSapphire), 1250),
new CrystalRechargeInfo(typeof(Diamond), 2000)
};
private CrystalRechargeInfo(Type type, int amount)
{
Type = type;
Amount = amount;
}
public Type Type { get; }
public int Amount { get; }
public static CrystalRechargeInfo Get(Type type)
{
foreach (CrystalRechargeInfo info in Table)
if (info.Type == type)
return info;
return null;
}
}
public class BroadcastCrystal : Item
{
public static readonly int MaxCharges = 2000;
private int m_Charges;
[Constructible]
public BroadcastCrystal(int charges = 2000) : base(0x1ED0)
{
Light = LightType.Circle150;
m_Charges = charges;
Receivers = new List<ReceiverCrystal>();
}
public BroadcastCrystal(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1060740; // communication crystal
[CommandProperty(AccessLevel.GameMaster)]
public bool Active
{
get => ItemID == 0x1ECD;
set
{
ItemID = value ? 0x1ECD : 0x1ED0;
InvalidateProperties();
}
}
[CommandProperty(AccessLevel.GameMaster)]
public int Charges
{
get => m_Charges;
set
{
m_Charges = value;
InvalidateProperties();
}
}
public List<ReceiverCrystal> Receivers { get; private set; }
public override bool HandlesOnSpeech => true;
public override void GetProperties(ObjectPropertyList list)
{
base.GetProperties(list);
list.Add(Active ? 1060742 : 1060743); // active / inactive
list.Add(1060745); // broadcast
list.Add(1060741, Charges.ToString()); // charges: ~1_val~
if (Receivers.Count > 0)
list.Add(1060746, Receivers.Count.ToString()); // links: ~1_val~
}
public override void OnSingleClick(Mobile from)
{
base.OnSingleClick(from);
LabelTo(from, Active ? 1060742 : 1060743); // active / inactive
LabelTo(from, 1060745); // broadcast
LabelTo(from, 1060741, Charges.ToString()); // charges: ~1_val~
if (Receivers.Count > 0)
LabelTo(from, 1060746, Receivers.Count.ToString()); // links: ~1_val~
}
public override void OnSpeech(SpeechEventArgs e)
{
if (!Active || Receivers.Count == 0 || (RootParent != null && !(RootParent is Mobile)))
return;
if (e.Type == MessageType.Emote)
return;
Mobile from = e.Mobile;
string speech = e.Speech;
foreach (ReceiverCrystal receiver in new List<ReceiverCrystal>(Receivers))
if (receiver.Deleted)
{
Receivers.Remove(receiver);
}
else if (Charges > 0)
{
receiver.TransmitMessage(from, speech);
Charges--;
}
else
{
Active = false;
break;
}
}
public override void OnDoubleClick(Mobile from)
{
if (!from.InRange(GetWorldLocation(), 2))
{
from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that.
return;
}
from.Target = new InternalTarget(this);
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0); // version
writer.WriteEncodedInt(m_Charges);
writer.WriteItemList(Receivers);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadEncodedInt();
m_Charges = reader.ReadEncodedInt();
Receivers = reader.ReadStrongItemList<ReceiverCrystal>();
}
private class InternalTarget : Target
{
private readonly BroadcastCrystal m_Crystal;
public InternalTarget(BroadcastCrystal crystal) : base(2, false, TargetFlags.None) => m_Crystal = crystal;
protected override void OnTarget(Mobile from, object targeted)
{
if (!m_Crystal.IsAccessibleTo(from))
return;
if (from.Map != m_Crystal.Map || !from.InRange(m_Crystal.GetWorldLocation(), 2))
{
from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that.
return;
}
if (targeted == m_Crystal)
{
if (m_Crystal.Active)
{
m_Crystal.Active = false;
from.SendLocalizedMessage(500672); // You turn the crystal off.
}
else
{
if (m_Crystal.Charges > 0)
{
m_Crystal.Active = true;
from.SendLocalizedMessage(500673); // You turn the crystal on.
}
else
{
from.SendLocalizedMessage(500676); // This crystal is out of charges.
}
}
}
else if (targeted is ReceiverCrystal receiver)
{
if (m_Crystal.Receivers.Count >= 10)
{
from.SendLocalizedMessage(1010042); // This broadcast crystal is already linked to 10 receivers.
}
else if (receiver.Sender == m_Crystal)
{
from.SendLocalizedMessage(500674); // This crystal is already linked with that crystal.
}
else if (receiver.Sender != null)
{
from.SendLocalizedMessage(
1010043); // That receiver crystal is already linked to another broadcast crystal.
}
else
{
receiver.Sender = m_Crystal;
from.SendLocalizedMessage(500675); // That crystal has been linked to this crystal.
}
}
else if (targeted == from)
{
foreach (ReceiverCrystal rc in new List<ReceiverCrystal>(m_Crystal.Receivers)) rc.Sender = null;
from.SendLocalizedMessage(1010046); // You unlink the broadcast crystal from all of its receivers.
}
else
{
if (targeted is Item targItem && targItem.VerifyMove(from))
{
CrystalRechargeInfo info = CrystalRechargeInfo.Get(targItem.GetType());
if (info != null)
{
if (m_Crystal.Charges >= MaxCharges)
{
from.SendLocalizedMessage(500678); // This crystal is already fully charged.
}
else
{
targItem.Consume();
if (m_Crystal.Charges + info.Amount >= MaxCharges)
{
m_Crystal.Charges = MaxCharges;
from.SendLocalizedMessage(500679); // You completely recharge the crystal.
}
else
{
m_Crystal.Charges += info.Amount;
from.SendLocalizedMessage(500680); // You recharge the crystal.
}
}
return;
}
}
from.SendLocalizedMessage(500681); // You cannot use this crystal on that.
}
}
}
}
public class ReceiverCrystal : Item
{
private BroadcastCrystal m_Sender;
[Constructible]
public ReceiverCrystal() : base(0x1ED0) => Light = LightType.Circle150;
public ReceiverCrystal(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1060740; // communication crystal
[CommandProperty(AccessLevel.GameMaster)]
public bool Active
{
get => ItemID == 0x1ED1;
set
{
ItemID = value ? 0x1ED1 : 0x1ED0;
InvalidateProperties();
}
}
[CommandProperty(AccessLevel.GameMaster)]
public BroadcastCrystal Sender
{
get => m_Sender;
set
{
if (m_Sender != null)
{
m_Sender.Receivers.Remove(this);
m_Sender.InvalidateProperties();
}
m_Sender = value;
if (value != null)
{
value.Receivers.Add(this);
value.InvalidateProperties();
}
}
}
public override void GetProperties(ObjectPropertyList list)
{
base.GetProperties(list);
list.Add(Active ? 1060742 : 1060743); // active / inactive
list.Add(1060744); // receiver
}
public override void OnSingleClick(Mobile from)
{
base.OnSingleClick(from);
LabelTo(from, Active ? 1060742 : 1060743); // active / inactive
LabelTo(from, 1060744); // receiver
}
public void TransmitMessage(Mobile from, string message)
{
if (!Active)
return;
string text = $"{from.Name} says {message}";
if (RootParent is Mobile mobile)
mobile.SendMessage(0x2B2, $"Crystal: {text}");
else if (RootParent is Item item)
item.PublicOverheadMessage(MessageType.Regular, 0x2B2, false, $"Crystal: {text}");
else
PublicOverheadMessage(MessageType.Regular, 0x2B2, false, text);
}
public override void OnDoubleClick(Mobile from)
{
if (!from.InRange(GetWorldLocation(), 2))
{
from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that.
return;
}
from.Target = new InternalTarget(this);
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0); // version
writer.WriteItem(m_Sender);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadEncodedInt();
m_Sender = reader.ReadItem<BroadcastCrystal>();
}
private class InternalTarget : Target
{
private readonly ReceiverCrystal m_Crystal;
public InternalTarget(ReceiverCrystal crystal) : base(-1, false, TargetFlags.None) => m_Crystal = crystal;
protected override void OnTarget(Mobile from, object targeted)
{
if (!m_Crystal.IsAccessibleTo(from))
return;
if (from.Map != m_Crystal.Map || !from.InRange(m_Crystal.GetWorldLocation(), 2))
{
from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that.
return;
}
if (targeted == m_Crystal)
{
if (m_Crystal.Active)
{
m_Crystal.Active = false;
from.SendLocalizedMessage(500672); // You turn the crystal off.
}
else
{
m_Crystal.Active = true;
from.SendLocalizedMessage(500673); // You turn the crystal on.
}
}
else if (targeted == from)
{
if (m_Crystal.Sender != null)
{
m_Crystal.Sender = null;
from.SendLocalizedMessage(1010044); // You unlink the receiver crystal.
}
else
{
from.SendLocalizedMessage(1010045); // That receiver crystal is not linked.
}
}
else
{
if (targeted is Item targItem && targItem.VerifyMove(from))
{
CrystalRechargeInfo info = CrystalRechargeInfo.Get(targItem.GetType());
if (info != null)
{
from.SendLocalizedMessage(500677); // This crystal cannot be recharged.
return;
}
}
from.SendLocalizedMessage(1010045); // That receiver crystal is not linked.
}
}
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,12 @@
using System;
namespace Server
{
[AttributeUsage(AttributeTargets.Class)]
public class CorpseNameAttribute : Attribute
{
public CorpseNameAttribute(string name) => Name = name;
public string Name { get; }
}
}

View file

@ -0,0 +1,108 @@
using System;
namespace Server.Items
{
public class DecayedCorpse : Container
{
private static readonly TimeSpan m_DefaultDecayTime = TimeSpan.FromMinutes(7.0);
private DateTime m_DecayTime;
private Timer m_DecayTimer;
public DecayedCorpse(string name) : base(Utility.Random(0xECA, 9))
{
Movable = false;
Name = name;
BeginDecay(m_DefaultDecayTime);
}
public DecayedCorpse(Serial serial) : base(serial)
{
}
// Do not display (x items, y stones)
public override bool DisplaysContent => false;
public void BeginDecay(TimeSpan delay)
{
m_DecayTimer?.Stop();
m_DecayTime = DateTime.UtcNow + delay;
m_DecayTimer = new InternalTimer(this, delay);
m_DecayTimer.Start();
}
public override void OnAfterDelete()
{
m_DecayTimer?.Stop();
m_DecayTimer = null;
}
// Do not display (x items, y stones)
public override bool CheckContentDisplay(Mobile from) => false;
public override void AddNameProperty(ObjectPropertyList list)
{
list.Add(1046414, Name); // the remains of ~1_NAME~
}
public override void OnSingleClick(Mobile from)
{
LabelTo(from, 1046414, Name); // the remains of ~1_NAME~
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(1); // version
writer.Write(m_DecayTimer != null);
if (m_DecayTimer != null)
writer.WriteDeltaTime(m_DecayTime);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 0:
{
BeginDecay(m_DefaultDecayTime);
break;
}
case 1:
{
if (reader.ReadBool())
BeginDecay(reader.ReadDeltaTime() - DateTime.UtcNow);
break;
}
}
}
private class InternalTimer : Timer
{
private readonly DecayedCorpse m_Corpse;
public InternalTimer(DecayedCorpse c, TimeSpan delay) : base(delay)
{
m_Corpse = c;
Priority = TimerPriority.FiveSeconds;
}
protected override void OnTick()
{
m_Corpse.Delete();
}
}
}
}

View file

@ -0,0 +1,198 @@
using System.Collections.Generic;
using System.IO;
using Server.Items;
namespace Server.Network
{
public sealed class CorpseEquip : Packet
{
public CorpseEquip(Mobile beholder, Corpse beheld) : base(0x89)
{
List<Item> list = beheld.EquipItems;
int count = list.Count;
if (beheld.Hair?.ItemID > 0)
count++;
if (beheld.FacialHair?.ItemID > 0)
count++;
EnsureCapacity(8 + count * 5);
Stream.Write(beheld.Serial);
for (int i = 0; i < list.Count; ++i)
{
Item item = list[i];
if (!item.Deleted && beholder.CanSee(item) && item.Parent == beheld)
{
Stream.Write((byte)(item.Layer + 1));
Stream.Write(item.Serial);
}
}
if (beheld.Hair?.ItemID > 0)
{
Stream.Write((byte)(Layer.Hair + 1));
Stream.Write(HairInfo.FakeSerial(beheld.Owner) - 2);
}
if (beheld.FacialHair?.ItemID > 0)
{
Stream.Write((byte)(Layer.FacialHair + 1));
Stream.Write(FacialHairInfo.FakeSerial(beheld.Owner) - 2);
}
Stream.Write((byte)Layer.Invalid);
}
}
public sealed class CorpseContent : Packet
{
public CorpseContent(Mobile beholder, Corpse beheld)
: base(0x3C)
{
List<Item> items = beheld.EquipItems;
int count = items.Count;
if (beheld.Hair?.ItemID > 0)
count++;
if (beheld.FacialHair != null && beheld.FacialHair.ItemID > 0)
count++;
EnsureCapacity(5 + count * 19);
long pos = Stream.Position;
int written = 0;
Stream.Write((ushort)0);
for (int i = 0; i < items.Count; ++i)
{
Item child = items[i];
if (!child.Deleted && child.Parent == beheld && beholder.CanSee(child))
{
Stream.Write(child.Serial);
Stream.Write((ushort)child.ItemID);
Stream.Write((byte)0); // signed, itemID offset
Stream.Write((ushort)child.Amount);
Stream.Write((short)child.X);
Stream.Write((short)child.Y);
Stream.Write(beheld.Serial);
Stream.Write((ushort)child.Hue);
++written;
}
}
if (beheld.Hair?.ItemID > 0)
{
Stream.Write(HairInfo.FakeSerial(beheld.Owner) - 2);
Stream.Write((ushort)beheld.Hair.ItemID);
Stream.Write((byte)0); // signed, itemID offset
Stream.Write((ushort)1);
Stream.Write((short)0);
Stream.Write((short)0);
Stream.Write(beheld.Serial);
Stream.Write((ushort)beheld.Hair.Hue);
++written;
}
if (beheld.FacialHair != null && beheld.FacialHair.ItemID > 0)
{
Stream.Write(FacialHairInfo.FakeSerial(beheld.Owner) - 2);
Stream.Write((ushort)beheld.FacialHair.ItemID);
Stream.Write((byte)0); // signed, itemID offset
Stream.Write((ushort)1);
Stream.Write((short)0);
Stream.Write((short)0);
Stream.Write(beheld.Serial);
Stream.Write((ushort)beheld.FacialHair.Hue);
++written;
}
Stream.Seek(pos, SeekOrigin.Begin);
Stream.Write((ushort)written);
}
}
public sealed class CorpseContent6017 : Packet
{
public CorpseContent6017(Mobile beholder, Corpse beheld)
: base(0x3C)
{
List<Item> items = beheld.EquipItems;
int count = items.Count;
if (beheld.Hair?.ItemID > 0)
count++;
if (beheld.FacialHair?.ItemID > 0)
count++;
EnsureCapacity(5 + count * 20);
long pos = Stream.Position;
int written = 0;
Stream.Write((ushort)0);
for (int i = 0; i < items.Count; ++i)
{
Item child = items[i];
if (!child.Deleted && child.Parent == beheld && beholder.CanSee(child))
{
Stream.Write(child.Serial);
Stream.Write((ushort)child.ItemID);
Stream.Write((byte)0); // signed, itemID offset
Stream.Write((ushort)child.Amount);
Stream.Write((short)child.X);
Stream.Write((short)child.Y);
Stream.Write((byte)0); // Grid Location?
Stream.Write(beheld.Serial);
Stream.Write((ushort)child.Hue);
++written;
}
}
if (beheld.Hair?.ItemID > 0)
{
Stream.Write(HairInfo.FakeSerial(beheld.Owner) - 2);
Stream.Write((ushort)beheld.Hair.ItemID);
Stream.Write((byte)0); // signed, itemID offset
Stream.Write((ushort)1);
Stream.Write((short)0);
Stream.Write((short)0);
Stream.Write((byte)0); // Grid Location?
Stream.Write(beheld.Serial);
Stream.Write((ushort)beheld.Hair.Hue);
++written;
}
if (beheld.FacialHair?.ItemID > 0)
{
Stream.Write(FacialHairInfo.FakeSerial(beheld.Owner) - 2);
Stream.Write((ushort)beheld.FacialHair.ItemID);
Stream.Write((byte)0); // signed, itemID offset
Stream.Write((ushort)1);
Stream.Write((short)0);
Stream.Write((short)0);
Stream.Write((byte)0); // Grid Location?
Stream.Write(beheld.Serial);
Stream.Write((ushort)beheld.FacialHair.Hue);
++written;
}
Stream.Seek(pos, SeekOrigin.Begin);
Stream.Write((ushort)written);
}
}
}

View file

@ -0,0 +1,178 @@
using System;
using Server.Mobiles;
using Server.Network;
using Server.Utilities;
namespace Server.Items
{
public class DeceitBrazier : Item
{
private Timer m_Timer;
[Constructible]
public DeceitBrazier() : base(0xE31)
{
Movable = false;
Light = LightType.Circle225;
NextSpawn = DateTime.UtcNow;
NextSpawnDelay = TimeSpan.FromMinutes(15.0);
SpawnRange = 5;
}
public DeceitBrazier(Serial serial) : base(serial)
{
}
public static Type[] Creatures { get; } =
{
typeof(FireSteed), // Set the tents up people!
typeof(Skeleton), typeof(SkeletalKnight), typeof(SkeletalMage), typeof(Mummy),
typeof(BoneKnight), typeof(Lich), typeof(LichLord), typeof(BoneMagi),
typeof(Wraith), typeof(Shade), typeof(Spectre), typeof(Zombie),
typeof(RottingCorpse), typeof(Ghoul), typeof(Balron), typeof(Daemon), typeof(Imp), typeof(GreaterMongbat),
typeof(Mongbat), typeof(IceFiend), typeof(Gargoyle), typeof(StoneGargoyle),
typeof(FireGargoyle), typeof(HordeMinion), typeof(Gazer), typeof(ElderGazer), typeof(GazerLarva), typeof(Harpy), typeof(StoneHarpy), typeof(HeadlessOne), typeof(HellHound),
typeof(HellCat), typeof(Phoenix), typeof(LavaLizard), typeof(SandVortex),
typeof(ShadowWisp), typeof(SwampTentacle), typeof(PredatorHellCat), typeof(Wisp), typeof(GiantSpider), typeof(DreadSpider), typeof(FrostSpider), typeof(Scorpion), typeof(ArcticOgreLord), typeof(Cyclops), typeof(Ettin), typeof(EvilMage),
typeof(FrostTroll), typeof(Ogre), typeof(OgreLord), typeof(Orc),
typeof(OrcishLord), typeof(OrcishMage), typeof(OrcBrute), typeof(Ratman),
typeof(RatmanMage), typeof(OrcCaptain), typeof(Troll), typeof(Titan),
typeof(EvilMageLord), typeof(OrcBomber), typeof(RatmanArcher), typeof(Dragon), typeof(Drake), typeof(Snake), typeof(GreaterDragon),
typeof(IceSerpent), typeof(GiantSerpent), typeof(IceSnake), typeof(LavaSerpent),
typeof(Lizardman), typeof(Wyvern), typeof(WhiteWyrm),
typeof(ShadowWyrm), typeof(SilverSerpent), typeof(LavaSnake), typeof(EarthElemental), typeof(PoisonElemental), typeof(FireElemental), typeof(SnowElemental),
typeof(IceElemental), typeof(AcidElemental), typeof(WaterElemental), typeof(Efreet),
typeof(AirElemental), typeof(Golem), typeof(SewerRat), typeof(GiantRat), typeof(DireWolf), typeof(TimberWolf),
typeof(Cougar), typeof(Alligator)
};
[CommandProperty(AccessLevel.GameMaster)]
public DateTime NextSpawn { get; private set; }
[CommandProperty(AccessLevel.GameMaster)]
public int SpawnRange { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public TimeSpan NextSpawnDelay { get; set; }
public override int LabelNumber => 1023633; // Brazier
public override bool HandlesOnMovement => true;
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
writer.Write(SpawnRange);
writer.Write(NextSpawnDelay);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
if (version >= 0)
{
SpawnRange = reader.ReadInt();
NextSpawnDelay = reader.ReadTimeSpan();
}
NextSpawn = DateTime.UtcNow;
}
public virtual void HeedWarning()
{
PublicOverheadMessage(MessageType.Regular, 0x3B2,
500761); // Heed this warning well, and use this brazier at your own peril.
}
public override void OnMovement(Mobile m, Point3D oldLocation)
{
if (NextSpawn < DateTime.UtcNow) // means we haven't spawned anything if the next spawn is below
if (Utility.InRange(m.Location, Location, 1) && !Utility.InRange(oldLocation, Location, 1) && m.Player &&
!(m.AccessLevel > AccessLevel.Player || m.Hidden))
if (m_Timer?.Running != true)
m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(2), HeedWarning);
base.OnMovement(m, oldLocation);
}
public Point3D GetSpawnPosition()
{
Map map = Map;
if (map == null)
return Location;
// Try 10 times to find a Spawnable location.
for (int i = 0; i < 10; i++)
{
int x = Location.X + (Utility.Random(SpawnRange * 2 + 1) - SpawnRange);
int y = Location.Y + (Utility.Random(SpawnRange * 2 + 1) - SpawnRange);
int z = Map.GetAverageZ(x, y);
if (Map.CanSpawnMobile(new Point2D(x, y), Z))
return new Point3D(x, y, Z);
if (Map.CanSpawnMobile(new Point2D(x, y), z))
return new Point3D(x, y, z);
}
return Location;
}
public virtual void DoEffect(Point3D loc, Map map)
{
Effects.SendLocationParticles(EffectItem.Create(loc, map, EffectItem.DefaultDuration), 0x3709, 10, 30, 5052);
Effects.PlaySound(loc, map, 0x225);
}
public override void OnDoubleClick(Mobile from)
{
if (Utility.InRange(from.Location, Location, 2))
try
{
if (NextSpawn < DateTime.UtcNow)
{
Map map = Map;
BaseCreature bc =
(BaseCreature)ActivatorUtil.CreateInstance(Creatures[Utility.Random(Creatures.Length)]);
Point3D spawnLoc = GetSpawnPosition();
DoEffect(spawnLoc, map);
Timer.DelayCall(TimeSpan.FromSeconds(1), () =>
{
bc.Home = Location;
bc.RangeHome = SpawnRange;
bc.FightMode = FightMode.Closest;
bc.MoveToWorld(spawnLoc, map);
DoEffect(spawnLoc, map);
bc.ForceReacquire();
});
NextSpawn = DateTime.UtcNow + NextSpawnDelay;
}
else
{
PublicOverheadMessage(MessageType.Regular, 0x3B2,
500760); // The brazier fizzes and pops, but nothing seems to happen.
}
}
catch
{
// ignored
}
else
from.SendLocalizedMessage(500446); // That is too far away.
}
}
}

View file

@ -0,0 +1,321 @@
using System;
namespace Server.Items
{
public enum ECEffectType
{
None,
Moving,
Location,
Target,
Lightning
}
public enum EffectTriggerType
{
None,
Sequenced,
DoubleClick,
InRange
}
public class EffectController : Item
{
private IEntity m_Source;
private IEntity m_Target;
[Constructible]
public EffectController() : base(0x1B72)
{
Movable = false;
Visible = false;
TriggerType = EffectTriggerType.Sequenced;
EffectLayer = (EffectLayer)255;
}
public EffectController(Serial serial) : base(serial)
{
}
[CommandProperty(AccessLevel.GameMaster)]
public ECEffectType EffectType { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public EffectTriggerType TriggerType { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public EffectLayer EffectLayer { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public TimeSpan EffectDelay { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public TimeSpan TriggerDelay { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public TimeSpan SoundDelay { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public Item SourceItem
{
get => m_Source as Item;
set => m_Source = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public Mobile SourceMobile
{
get => m_Source as Mobile;
set => m_Source = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public bool SourceNull
{
get => m_Source == null;
set
{
if (value) m_Source = null;
}
}
[CommandProperty(AccessLevel.GameMaster)]
public Item TargetItem
{
get => m_Target as Item;
set => m_Target = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public Mobile TargetMobile
{
get => m_Target as Mobile;
set => m_Target = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public bool TargetNull
{
get => m_Target == null;
set
{
if (value) m_Target = null;
}
}
[CommandProperty(AccessLevel.GameMaster)]
public EffectController Sequence { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
private bool FixedDirection { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
private bool Explodes { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
private bool PlaySoundAtTrigger { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public int EffectItemID { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public int EffectHue { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public int RenderMode { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public int Speed { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public int Duration { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public int ParticleEffect { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public int ExplodeParticleEffect { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public int ExplodeSound { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public int Unknown { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public int SoundID { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public int TriggerRange { get; set; }
public override string DefaultName => "Effect Controller";
public override bool HandlesOnMovement => TriggerType == EffectTriggerType.InRange;
public override void OnDoubleClick(Mobile from)
{
if (TriggerType == EffectTriggerType.DoubleClick)
DoEffect(from);
}
public override void OnMovement(Mobile m, Point3D oldLocation)
{
if (m.Location != oldLocation && TriggerType == EffectTriggerType.InRange &&
Utility.InRange(GetWorldLocation(), m.Location, TriggerRange) &&
!Utility.InRange(GetWorldLocation(), oldLocation, TriggerRange))
DoEffect(m);
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
writer.Write(EffectDelay);
writer.Write(TriggerDelay);
writer.Write(SoundDelay);
if (m_Source is Item srcItem)
writer.Write(srcItem);
else
writer.Write(m_Source as Mobile);
if (m_Target is Item targItem)
writer.Write(targItem);
else
writer.Write(m_Target as Mobile);
writer.Write(Sequence);
writer.Write(FixedDirection);
writer.Write(Explodes);
writer.Write(PlaySoundAtTrigger);
writer.WriteEncodedInt((int)EffectType);
writer.WriteEncodedInt((int)EffectLayer);
writer.WriteEncodedInt((int)TriggerType);
writer.WriteEncodedInt(EffectItemID);
writer.WriteEncodedInt(EffectHue);
writer.WriteEncodedInt(RenderMode);
writer.WriteEncodedInt(Speed);
writer.WriteEncodedInt(Duration);
writer.WriteEncodedInt(ParticleEffect);
writer.WriteEncodedInt(ExplodeParticleEffect);
writer.WriteEncodedInt(ExplodeSound);
writer.WriteEncodedInt(Unknown);
writer.WriteEncodedInt(SoundID);
writer.WriteEncodedInt(TriggerRange);
}
private IEntity ReadEntity(IGenericReader reader) => World.FindEntity(reader.ReadUInt());
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 0:
{
EffectDelay = reader.ReadTimeSpan();
TriggerDelay = reader.ReadTimeSpan();
SoundDelay = reader.ReadTimeSpan();
m_Source = ReadEntity(reader);
m_Target = ReadEntity(reader);
Sequence = reader.ReadItem() as EffectController;
FixedDirection = reader.ReadBool();
Explodes = reader.ReadBool();
PlaySoundAtTrigger = reader.ReadBool();
EffectType = (ECEffectType)reader.ReadEncodedInt();
EffectLayer = (EffectLayer)reader.ReadEncodedInt();
TriggerType = (EffectTriggerType)reader.ReadEncodedInt();
EffectItemID = reader.ReadEncodedInt();
EffectHue = reader.ReadEncodedInt();
RenderMode = reader.ReadEncodedInt();
Speed = reader.ReadEncodedInt();
Duration = reader.ReadEncodedInt();
ParticleEffect = reader.ReadEncodedInt();
ExplodeParticleEffect = reader.ReadEncodedInt();
ExplodeSound = reader.ReadEncodedInt();
Unknown = reader.ReadEncodedInt();
SoundID = reader.ReadEncodedInt();
TriggerRange = reader.ReadEncodedInt();
break;
}
}
}
public void PlaySound(IEntity trigger)
{
IEntity ent = PlaySoundAtTrigger ? trigger : this;
Effects.PlaySound((ent as Item)?.GetWorldLocation() ?? ent.Location, ent.Map, SoundID);
}
public void DoEffect(IEntity trigger)
{
if (Deleted || TriggerType == EffectTriggerType.None)
return;
if (trigger is Mobile mobile && mobile.Hidden && mobile.AccessLevel > AccessLevel.Player)
return;
if (SoundID > 0)
Timer.DelayCall(SoundDelay, PlaySound, trigger);
if (Sequence != null)
Timer.DelayCall(TriggerDelay, Sequence.DoEffect, trigger);
if (EffectType != ECEffectType.None)
Timer.DelayCall(EffectDelay, InternalDoEffect, trigger);
}
public void InternalDoEffect(IEntity trigger)
{
IEntity from = m_Source ?? trigger;
IEntity to = m_Target ?? trigger;
switch (EffectType)
{
case ECEffectType.Lightning:
{
Effects.SendBoltEffect(from, false, EffectHue);
break;
}
case ECEffectType.Location:
{
Effects.SendLocationParticles(EffectItem.Create(from.Location, from.Map, EffectItem.DefaultDuration),
EffectItemID, Speed, Duration, EffectHue, RenderMode, ParticleEffect, Unknown);
break;
}
case ECEffectType.Moving:
{
if (from == this)
from = EffectItem.Create(from.Location, from.Map, EffectItem.DefaultDuration);
if (to == this)
to = EffectItem.Create(to.Location, to.Map, EffectItem.DefaultDuration);
Effects.SendMovingParticles(from, to, EffectItemID, Speed, Duration, FixedDirection, Explodes, EffectHue,
RenderMode, ParticleEffect, ExplodeParticleEffect, ExplodeSound, EffectLayer, Unknown);
break;
}
case ECEffectType.Target:
{
Effects.SendTargetParticles(from, EffectItemID, Speed, Duration, EffectHue, RenderMode, ParticleEffect,
EffectLayer, Unknown);
break;
}
}
}
}
}

View file

@ -0,0 +1,86 @@
using System;
using System.Collections.Generic;
namespace Server.Items
{
public class EffectItem : Item
{
private static readonly List<EffectItem> m_Free = new List<EffectItem>(); // List of available EffectItems
public static readonly TimeSpan DefaultDuration = TimeSpan.FromSeconds(5.0);
private EffectItem() : base(1) // nodraw
=>
Movable = false;
public EffectItem(Serial serial) : base(serial)
{
}
public override bool Decays => true;
public static EffectItem Create(Point3D p, Map map, TimeSpan duration)
{
EffectItem item = null;
for (int i = m_Free.Count - 1; item == null && i >= 0; --i) // We reuse new entries first so decay works better
{
EffectItem free = m_Free[i];
m_Free.RemoveAt(i);
if (!free.Deleted && free.Map == Map.Internal)
item = free;
}
if (item == null)
item = new EffectItem();
else
item.ItemID = 1;
item.MoveToWorld(p, map);
item.BeginFree(duration);
return item;
}
public void BeginFree(TimeSpan duration)
{
new FreeTimer(this, duration).Start();
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
Delete();
}
private class FreeTimer : Timer
{
private readonly EffectItem m_Item;
public FreeTimer(EffectItem item, TimeSpan delay) : base(delay)
{
m_Item = item;
Priority = TimerPriority.OneSecond;
}
protected override void OnTick()
{
m_Item.Internalize();
m_Free.Add(m_Item);
}
}
}
}

View file

@ -0,0 +1,26 @@
namespace Server.Items
{
public class ExecutionersCap : Item
{
[Constructible]
public ExecutionersCap() : base(0xF83) => Weight = 1.0;
public ExecutionersCap(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,283 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Server.Network;
using Server.Spells;
using Server.Targeting;
namespace Server.Items
{
public class Firebomb : Item
{
private Mobile m_LitBy;
private int m_Ticks;
private Timer m_Timer;
private List<Mobile> m_Users;
[Constructible]
public Firebomb(int itemID = 0x99B) : base(itemID)
{
// Name = "a firebomb";
Weight = 2.0;
Hue = 1260;
}
public Firebomb(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadEncodedInt();
}
public override void OnDoubleClick(Mobile from)
{
if (!IsChildOf(from.Backpack))
{
from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it.
return;
}
if (Core.AOS && (from.Paralyzed || from.Frozen || from.Spell?.IsCasting == true))
{
// to prevent exploiting for pvp
from.SendLocalizedMessage(1075857); // You cannot use that while paralyzed.
return;
}
if (m_Timer == null)
{
m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1), OnFirebombTimerTick);
m_LitBy = from;
from.SendLocalizedMessage(1060582); // You light the firebomb. Throw it now!
}
else
{
from.SendLocalizedMessage(1060581); // You've already lit it! Better throw it now!
}
m_Users ??= new List<Mobile>();
if (!m_Users.Contains(from))
m_Users.Add(from);
from.Target = new ThrowTarget(this);
}
private void OnFirebombTimerTick()
{
if (Deleted)
{
m_Timer.Stop();
return;
}
if (Map == Map.Internal && HeldBy == null)
return;
switch (m_Ticks)
{
case 0:
case 1:
case 2:
{
++m_Ticks;
if (HeldBy != null)
HeldBy.PublicOverheadMessage(MessageType.Regular, 957, false, m_Ticks.ToString());
else if (RootParent == null)
PublicOverheadMessage(MessageType.Regular, 957, false, m_Ticks.ToString());
else if (RootParent is Mobile mobile)
mobile.PublicOverheadMessage(MessageType.Regular, 957, false, m_Ticks.ToString());
break;
}
default:
{
HeldBy?.DropHolding();
if (m_Users != null)
{
foreach (Mobile m in m_Users)
if (m.Target is ThrowTarget targ && targ.Bomb == this)
Target.Cancel(m);
m_Users.Clear();
m_Users = null;
}
if (RootParent is Mobile parent)
{
parent.SendLocalizedMessage(1060583); // The firebomb explodes in your hand!
AOS.Damage(parent, Utility.Random(3) + 4, 0, 100, 0, 0, 0);
}
else if (RootParent == null)
{
IPooledEnumerable<Mobile> eable = Map.GetMobilesInRange(Location, 1);
List<Mobile> toDamage = eable.ToList();
eable.Free();
for (int i = 0; i < toDamage.Count; ++i)
{
Mobile victim = toDamage[i];
if (m_LitBy == null || (SpellHelper.ValidIndirectTarget(m_LitBy, victim) &&
m_LitBy.CanBeHarmful(victim, false)))
{
m_LitBy?.DoHarmful(victim);
AOS.Damage(victim, m_LitBy, Utility.Random(3) + 4, 0, 100, 0, 0, 0);
}
}
new FirebombField(m_LitBy, toDamage).MoveToWorld(Location, Map);
}
m_Timer.Stop();
Delete();
break;
}
}
}
private void OnFirebombTarget(Mobile from, object obj)
{
if (Deleted || Map == Map.Internal || !IsChildOf(from.Backpack))
return;
if (!(obj is IPoint3D p))
return;
SpellHelper.GetSurfaceTop(ref p);
from.RevealingAction();
IEntity to = p as IEntity ?? new Entity(Serial.Zero, new Point3D(p), Map);
Effects.SendMovingEffect(from, to, ItemID, 7, 0, false, false, Hue);
Timer.DelayCall(TimeSpan.FromSeconds(1.0), () => FirebombReposition_OnTick(p, Map));
Internalize();
}
private void FirebombReposition_OnTick(IPoint3D p, Map map)
{
if (Deleted)
return;
MoveToWorld(new Point3D(p), map);
}
private class ThrowTarget : Target
{
public ThrowTarget(Firebomb bomb)
: base(12, true, TargetFlags.None) =>
Bomb = bomb;
public Firebomb Bomb { get; }
protected override void OnTarget(Mobile from, object targeted)
{
Bomb.OnFirebombTarget(from, targeted);
}
}
}
public class FirebombField : Item
{
private readonly List<Mobile> m_Burning;
private readonly DateTime m_Expire;
private readonly Mobile m_LitBy;
private readonly Timer m_Timer;
public FirebombField(Mobile litBy, List<Mobile> toDamage) : base(0x376A)
{
Movable = false;
m_LitBy = litBy;
m_Expire = DateTime.UtcNow + TimeSpan.FromSeconds(10);
m_Burning = toDamage;
m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0), OnFirebombFieldTimerTick);
}
public FirebombField(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
// Don't serialize these...
}
public override void Deserialize(IGenericReader reader)
{
}
public override bool OnMoveOver(Mobile m)
{
if ((ItemID == 0x398C && m_LitBy == null) ||
(SpellHelper.ValidIndirectTarget(m_LitBy, m) && m_LitBy.CanBeHarmful(m, false)))
{
m_LitBy?.DoHarmful(m);
AOS.Damage(m, m_LitBy, 2, 0, 100, 0, 0, 0);
m.PlaySound(0x208);
if (!m_Burning.Contains(m))
m_Burning.Add(m);
}
return true;
}
private void OnFirebombFieldTimerTick()
{
if (Deleted)
{
m_Timer.Stop();
return;
}
if (ItemID == 0x376A)
{
ItemID = 0x398C;
return;
}
for (int i = 0; i < m_Burning.Count;)
{
Mobile victim = m_Burning[i];
if (victim.Location == Location && victim.Map == Map &&
(m_LitBy == null || (SpellHelper.ValidIndirectTarget(m_LitBy, victim) &&
m_LitBy.CanBeHarmful(victim, false))))
{
m_LitBy?.DoHarmful(victim);
AOS.Damage(victim, m_LitBy, Utility.Random(3) + 4, 0, 100, 0, 0, 0);
++i;
}
else
{
m_Burning.RemoveAt(i);
}
}
if (DateTime.UtcNow >= m_Expire)
{
m_Timer.Stop();
Delete();
}
}
}
}

View file

@ -0,0 +1,114 @@
using System;
using System.Reflection;
using Server.Multis;
namespace Server.Items
{
[AttributeUsage(AttributeTargets.Class)]
public class FlippableAddonAttribute : Attribute
{
private static readonly string m_MethodName = "Flip";
private static readonly Type[] m_Params =
{
typeof(Mobile), typeof(Direction)
};
public FlippableAddonAttribute(params Direction[] directions) => Directions = directions;
public Direction[] Directions { get; }
public virtual void Flip(Mobile from, Item addon)
{
if (Directions?.Length > 1)
try
{
MethodInfo flipMethod = addon.GetType().GetMethod(m_MethodName, m_Params);
if (flipMethod != null)
{
int index = 0;
for (int i = 0; i < Directions.Length; i++)
if (addon.Direction == Directions[i])
{
index = i + 1;
break;
}
if (index >= Directions.Length)
index = 0;
ClearComponents(addon);
flipMethod.Invoke(addon, new object[] { from, Directions[index] });
BaseHouse house = null;
AddonFitResult result = AddonFitResult.Valid;
addon.Map = Map.Internal;
if (addon is BaseAddon baseAddon)
result = baseAddon.CouldFit(baseAddon.Location, from.Map, from, ref house);
else if (addon is BaseAddonContainer container)
result = container.CouldFit(container.Location, from.Map, from, ref house);
addon.Map = from.Map;
if (result != AddonFitResult.Valid)
{
if (index == 0)
index = Directions.Length - 1;
else
index -= 1;
ClearComponents(addon);
flipMethod.Invoke(addon, new object[2] { from, Directions[index] });
if (result == AddonFitResult.Blocked)
from.SendLocalizedMessage(500269); // You cannot build that there.
else if (result == AddonFitResult.NotInHouse)
from.SendLocalizedMessage(500274); // You can only place this in a house that you own!
else if (result == AddonFitResult.DoorsNotClosed)
from.SendMessage("You must close all house doors before placing this.");
else if (result == AddonFitResult.DoorTooClose)
from.SendLocalizedMessage(500271); // You cannot build near the door.
else if (result == AddonFitResult.NoWall)
from.SendLocalizedMessage(500268); // This object needs to be mounted on something.
}
addon.Direction = Directions[index];
}
}
catch
{
// ignored
}
}
private void ClearComponents(Item item)
{
if (item is BaseAddon addon)
{
foreach (AddonComponent c in addon.Components)
{
c.Addon = null;
c.Delete();
}
addon.Components.Clear();
}
else if (item is BaseAddonContainer addonContainer)
{
foreach (AddonContainerComponent c in addonContainer.Components)
{
c.Addon = null;
c.Delete();
}
addonContainer.Components.Clear();
}
}
}
}

View file

@ -0,0 +1,91 @@
using System;
using Server.Targeting;
namespace Server.Items
{
public class FlipCommandHandlers
{
public static void Initialize()
{
CommandSystem.Register("Flip", AccessLevel.GameMaster, Flip_OnCommand);
}
[Usage("Flip")]
[Description("Turns an item.")]
public static void Flip_OnCommand(CommandEventArgs e)
{
e.Mobile.Target = new FlipTarget();
}
private class FlipTarget : Target
{
public FlipTarget()
: base(-1, false, TargetFlags.None)
{
}
protected override void OnTarget(Mobile from, object targeted)
{
if (targeted is Item item)
{
if (item.Movable == false && from.AccessLevel == AccessLevel.Player)
return;
Type type = item.GetType();
FlippableAttribute[] AttributeArray =
(FlippableAttribute[])type.GetCustomAttributes(typeof(FlippableAttribute), false);
if (AttributeArray.Length == 0) return;
FlippableAttribute fa = AttributeArray[0];
fa.Flip(item);
}
}
}
}
[AttributeUsage(AttributeTargets.Class)]
public class DynamicFlipingAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Class)]
public class FlippableAttribute : Attribute
{
public FlippableAttribute(params int[] itemIDs) => ItemIDs = itemIDs;
public int[] ItemIDs { get; }
public virtual void Flip(Item item)
{
if (ItemIDs == null)
{
try
{
item.GetType().GetMethod("Flip", Type.EmptyTypes)?.Invoke(item, Array.Empty<object>());
}
catch
{
// ignored
}
}
else
{
int index = 0;
for (int i = 0; i < ItemIDs.Length; i++)
if (item.ItemID == ItemIDs[i])
{
index = i + 1;
break;
}
if (index > ItemIDs.Length - 1)
index = 0;
item.ItemID = ItemIDs[index];
}
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,122 @@
using System;
using Server.Accounting;
namespace Server.Items
{
public class Gold : Item
{
[Constructible]
public Gold(int amountFrom, int amountTo) : this(Utility.RandomMinMax(amountFrom, amountTo))
{
}
[Constructible]
public Gold(int amount = 1) : base(0xEED)
{
Stackable = true;
Amount = amount;
}
public Gold(Serial serial) : base(serial)
{
}
public override double DefaultWeight => Core.ML ? 0.02 / 3 : 0.02;
public override int GetDropSound()
{
if (Amount <= 1)
return 0x2E4;
if (Amount <= 5)
return 0x2E5;
return 0x2E6;
}
protected override void OnAmountChange(int oldValue)
{
int newValue = Amount;
UpdateTotal(this, TotalType.Gold, newValue - oldValue);
}
public override void OnAdded(IEntity parent)
{
base.OnAdded(parent);
if (!AccountGold.Enabled) return;
Mobile owner = null;
SecureTradeInfo tradeInfo = null;
Container root = parent as Container;
while (root?.Parent is Container container)
root = container;
parent = root ?? parent;
if (parent is SecureTradeContainer trade && AccountGold.ConvertOnTrade)
{
if (trade.Trade.From.Container == trade)
{
tradeInfo = trade.Trade.From;
owner = tradeInfo.Mobile;
}
else if (trade.Trade.To.Container == trade)
{
tradeInfo = trade.Trade.To;
owner = tradeInfo.Mobile;
}
}
else if (parent is BankBox box && AccountGold.ConvertOnBank)
{
owner = box.Owner;
}
if (owner?.Account?.DepositGold(Amount) != true) return;
if (tradeInfo != null)
{
if (owner.NetState?.NewSecureTrading == false)
{
int plat = Math.DivRem(Amount, AccountGold.CurrencyThreshold, out int gold);
tradeInfo.Plat += plat;
tradeInfo.Gold += gold;
}
tradeInfo.VirtualCheck?.UpdateTrade(tradeInfo.Mobile);
}
owner.SendLocalizedMessage(1042763, Amount.ToString("#,0"));
Delete();
((Container)parent).UpdateTotals();
}
public override int GetTotal(TotalType type)
{
int baseTotal = base.GetTotal(type);
if (type == TotalType.Gold)
baseTotal += Amount;
return baseTotal;
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,113 @@
using System;
using Server.Network;
using Server.Spells;
namespace Server.Items
{
public class Guillotine : Item
{
private DateTime m_NextUse;
[Constructible]
public Guillotine()
: base(4656) =>
Movable = false;
public Guillotine(Serial serial)
: base(serial)
{
}
public override void OnDoubleClick(Mobile from)
{
if (!from.InRange(GetWorldLocation(), 2) || !from.InLOS(this))
{
from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that
}
else if (Visible && (ItemID == 4656 || ItemID == 4702) && DateTime.UtcNow >= m_NextUse)
{
Point3D p = GetWorldLocation();
if (Utility.Random(Math.Max(Math.Abs(from.X - p.X), Math.Abs(from.Y - p.Y))) < 1)
{
Effects.PlaySound(from.Location, from.Map, from.GetHurtSound());
from.PublicOverheadMessage(MessageType.Regular, from.SpeechHue, true, "Ouch!");
SpellHelper.Damage(TimeSpan.FromSeconds(0.5), from, Utility.Dice(2, 10, 5));
}
Effects.PlaySound(GetWorldLocation(), Map, 0x387);
Timer.DelayCall(TimeSpan.FromSeconds(0.25), Down1);
Timer.DelayCall(TimeSpan.FromSeconds(0.50), Down2);
Timer.DelayCall(TimeSpan.FromSeconds(5.00), BackUp);
m_NextUse = DateTime.UtcNow + TimeSpan.FromSeconds(10.0);
}
}
private void Down1()
{
ItemID = ItemID == 4656 ? 4678 : 4712;
}
private void Down2()
{
ItemID = ItemID == 4678 ? 4679 : 4713;
Point3D p = GetWorldLocation();
Map f = Map;
if (f == null)
return;
new Blood(4650).MoveToWorld(p, f);
for (int i = 0; i < 4; ++i)
{
int x = p.X - 2 + Utility.Random(5);
int y = p.Y - 2 + Utility.Random(5);
int z = p.Z;
if (!f.CanFit(x, y, z, 1, false, false))
{
z = f.GetAverageZ(x, y);
if (!f.CanFit(x, y, z, 1, false, false))
continue;
}
Point3D loc = f.GetRandomNearbyLocation(p, 2, -2, 4, 1);
new Blood().MoveToWorld(loc, f);
}
}
private void BackUp()
{
if (ItemID == 4678 || ItemID == 4679)
ItemID = 4656;
else if (ItemID == 4712 || ItemID == 4713)
ItemID = 4702;
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write((byte)0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadByte();
if (ItemID == 4678 || ItemID == 4679)
ItemID = 4656;
else if (ItemID == 4712 || ItemID == 4713)
ItemID = 4702;
}
}
}

View file

@ -0,0 +1,166 @@
using Server.Gumps;
using Server.Network;
namespace Server.Items
{
public class HairDye : Item
{
[Constructible]
public HairDye() : base(0xEFF) => Weight = 1.0;
public HairDye(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1041060; // Hair Dye
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
public override void OnDoubleClick(Mobile from)
{
if (from.InRange(GetWorldLocation(), 1))
{
from.CloseGump<HairDyeGump>();
from.SendGump(new HairDyeGump(this));
}
else
{
from.LocalOverheadMessage(MessageType.Regular, 906, 1019045); // I can't reach that.
}
}
}
public class HairDyeGump : Gump
{
private static readonly HairDyeEntry[] m_Entries =
{
new HairDyeEntry("*****", 1602, 26),
new HairDyeEntry("*****", 1628, 27),
new HairDyeEntry("*****", 1502, 32),
new HairDyeEntry("*****", 1302, 32),
new HairDyeEntry("*****", 1402, 32),
new HairDyeEntry("*****", 1202, 24),
new HairDyeEntry("*****", 2402, 29),
new HairDyeEntry("*****", 2213, 6),
new HairDyeEntry("*****", 1102, 8),
new HairDyeEntry("*****", 1110, 8),
new HairDyeEntry("*****", 1118, 16),
new HairDyeEntry("*****", 1134, 16)
};
private readonly HairDye m_HairDye;
public HairDyeGump(HairDye dye) : base(50, 50)
{
m_HairDye = dye;
AddPage(0);
AddBackground(100, 10, 350, 355, 2600);
AddBackground(120, 54, 110, 270, 5100);
AddHtmlLocalized(70, 25, 400, 35, 1011013); // <center>Hair Color Selection Menu</center>
AddButton(149, 328, 4005, 4007, 1);
AddHtmlLocalized(185, 329, 250, 35, 1011014); // Dye my hair this color!
for (int i = 0; i < m_Entries.Length; ++i)
{
AddLabel(130, 59 + i * 22, m_Entries[i].HueStart - 1, m_Entries[i].Name);
AddButton(207, 60 + i * 22, 5224, 5224, 0, GumpButtonType.Page, i + 1);
}
for (int i = 0; i < m_Entries.Length; ++i)
{
HairDyeEntry e = m_Entries[i];
AddPage(i + 1);
for (int j = 0; j < e.HueCount; ++j)
{
AddLabel(278 + j / 16 * 80, 52 + j % 16 * 17, e.HueStart + j - 1, "*****");
AddRadio(260 + j / 16 * 80, 52 + j % 16 * 17, 210, 211, false, i * 100 + j);
}
}
}
public override void OnResponse(NetState from, RelayInfo info)
{
if (m_HairDye.Deleted)
return;
Mobile m = from.Mobile;
int[] switches = info.Switches;
if (!m_HairDye.IsChildOf(m.Backpack))
{
m.SendLocalizedMessage(1042010); // You must have the objectin your backpack to use it.
return;
}
if (info.ButtonID != 0 && switches.Length > 0)
{
if (m.HairItemID == 0 && m.FacialHairItemID == 0)
{
m.SendLocalizedMessage(502623); // You have no hair to dye and cannot use this
}
else
{
// To prevent this from being exploited, the hue is abstracted into an internal list
int entryIndex = switches[0] / 100;
int hueOffset = switches[0] % 100;
if (entryIndex >= 0 && entryIndex < m_Entries.Length)
{
HairDyeEntry e = m_Entries[entryIndex];
if (hueOffset >= 0 && hueOffset < e.HueCount)
{
int hue = e.HueStart + hueOffset;
m.HairHue = hue;
m.FacialHairHue = hue;
m.SendLocalizedMessage(501199); // You dye your hair
m_HairDye.Delete();
m.PlaySound(0x4E);
}
}
}
}
else
{
m.SendLocalizedMessage(501200); // You decide not to dye your hair
}
}
private class HairDyeEntry
{
public HairDyeEntry(string name, int hueStart, int hueCount)
{
Name = name;
HueStart = hueStart;
HueCount = hueCount;
}
public string Name { get; }
public int HueStart { get; }
public int HueCount { get; }
}
}
}

View file

@ -0,0 +1,30 @@
namespace Server.Items
{
public class HoveringWisp : Item
{
[Constructible]
public HoveringWisp() : base(0x2100)
{
}
public HoveringWisp(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1072881; // hovering wisp
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,21 @@
namespace Server.Items
{
internal interface IDurability
{
bool CanFortify { get; }
int InitMinHits { get; }
int InitMaxHits { get; }
int HitPoints { get; set; }
int MaxHitPoints { get; set; }
void ScaleDurability();
void UnscaleDurability();
}
internal interface IWearableDurability : IDurability
{
int OnHit(BaseWeapon weapon, int damageTaken);
}
}

View file

@ -0,0 +1,329 @@
using Server.Gumps;
using Server.Multis;
using Server.Network;
using Server.Targeting;
namespace Server.Items
{
public enum DecorateCommand
{
None,
Turn,
Up,
Down
}
public class InteriorDecorator : Item
{
private DecorateCommand m_Command;
[Constructible]
public InteriorDecorator() : base(0xFC1)
{
Weight = 1.0;
LootType = LootType.Blessed;
}
public InteriorDecorator(Serial serial) : base(serial)
{
}
[CommandProperty(AccessLevel.GameMaster)]
public DecorateCommand Command
{
get => m_Command;
set
{
m_Command = value;
InvalidateProperties();
}
}
public override int LabelNumber => 1041280; // an interior decorator
public override void GetProperties(ObjectPropertyList list)
{
base.GetProperties(list);
if (m_Command != DecorateCommand.None)
list.Add(1018322 + (int)m_Command); // Turn/Up/Down
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
public override void OnDoubleClick(Mobile from)
{
if (!CheckUse(this, from))
return;
if (!from.HasGump<InternalGump>())
from.SendGump(new InternalGump(this));
if (m_Command != DecorateCommand.None)
from.Target = new InternalTarget(this);
}
public static bool InHouse(Mobile from)
{
BaseHouse house = BaseHouse.FindHouseAt(from);
return house?.IsCoOwner(from) == true;
}
public static bool CheckUse(InteriorDecorator tool, Mobile from)
{
/*if (tool.Deleted || !tool.IsChildOf( from.Backpack ))
from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it.
else*/
if (!InHouse(from))
from.SendLocalizedMessage(502092); // You must be in your house to do this.
else
return true;
return false;
}
private class InternalGump : Gump
{
private readonly InteriorDecorator m_Decorator;
public InternalGump(InteriorDecorator decorator) : base(150, 50)
{
m_Decorator = decorator;
AddBackground(0, 0, 200, 200, 2600);
AddButton(50, 45, decorator.Command == DecorateCommand.Turn ? 2154 : 2152, 2154, 1);
AddHtmlLocalized(90, 50, 70, 40, 1018323); // Turn
AddButton(50, 95, decorator.Command == DecorateCommand.Up ? 2154 : 2152, 2154, 2);
AddHtmlLocalized(90, 100, 70, 40, 1018324); // Up
AddButton(50, 145, decorator.Command == DecorateCommand.Down ? 2154 : 2152, 2154, 3);
AddHtmlLocalized(90, 150, 70, 40, 1018325); // Down
}
public override void OnResponse(NetState sender, RelayInfo info)
{
var command = info.ButtonID switch
{
1 => DecorateCommand.Turn,
2 => DecorateCommand.Up,
3 => DecorateCommand.Down,
_ => DecorateCommand.None
};
if (command != DecorateCommand.None)
{
m_Decorator.Command = command;
sender.Mobile.SendGump(new InternalGump(m_Decorator));
sender.Mobile.Target = new InternalTarget(m_Decorator);
}
else
{
Target.Cancel(sender.Mobile);
}
}
}
private class InternalTarget : Target
{
private readonly InteriorDecorator m_Decorator;
public InternalTarget(InteriorDecorator decorator) : base(-1, false, TargetFlags.None)
{
CheckLOS = false;
m_Decorator = decorator;
}
protected override void OnTargetNotAccessible(Mobile from, object targeted)
{
OnTarget(from, targeted);
}
protected override void OnTarget(Mobile from, object targeted)
{
if (targeted is Item item && CheckUse(m_Decorator, from))
{
BaseHouse house = BaseHouse.FindHouseAt(from);
bool isDecorableComponent = false;
object addon = null;
int count = 0;
if (item is AddonComponent component)
{
count = component.Addon.Components.Count;
addon = component.Addon;
}
else if (item is AddonContainerComponent containerComponent)
{
count = containerComponent.Addon.Components.Count;
addon = containerComponent.Addon;
}
else if (item is BaseAddonContainer container)
{
count = container.Components.Count;
addon = container;
}
if (addon != null)
{
if (count == 1 && Core.SE)
isDecorableComponent = true;
if (m_Decorator.Command == DecorateCommand.Turn)
{
FlippableAddonAttribute[] attributes =
(FlippableAddonAttribute[])addon.GetType()
.GetCustomAttributes(typeof(FlippableAddonAttribute), false);
if (attributes.Length > 0)
isDecorableComponent = true;
}
}
if (house?.IsCoOwner(from) != true)
{
from.SendLocalizedMessage(502092); // You must be in your house to do this.
}
else if (item.Parent != null || !house.IsInside(item))
{
from.SendLocalizedMessage(1042270); // That is not in your house.
}
else if (!house.HasLockedDownItem(item) && !house.HasSecureItem(item) && !isDecorableComponent)
{
if (item is AddonComponent && m_Decorator.Command == DecorateCommand.Up)
from.SendLocalizedMessage(1042274); // You cannot raise it up any higher.
else if (item is AddonComponent && m_Decorator.Command == DecorateCommand.Down)
from.SendLocalizedMessage(1042275); // You cannot lower it down any further.
else
from.SendLocalizedMessage(1042271); // That is not locked down.
}
else if (item is VendorRentalContract)
{
from.SendLocalizedMessage(1062491); // You cannot use the house decorator on that object.
}
else if (item.TotalWeight + item.PileWeight > 100)
{
from.SendLocalizedMessage(1042272); // That is too heavy.
}
else
{
switch (m_Decorator.Command)
{
case DecorateCommand.Up:
Up(item, from);
break;
case DecorateCommand.Down:
Down(item, from);
break;
case DecorateCommand.Turn:
Turn(item, from);
break;
}
}
}
from.Target = new InternalTarget(m_Decorator);
}
protected override void OnTargetCancel(Mobile from, TargetCancelType cancelType)
{
if (cancelType == TargetCancelType.Canceled)
from.CloseGump<InternalGump>();
}
private static void Turn(Item item, Mobile from)
{
object addon = null;
if (item is AddonComponent component)
addon = component.Addon;
else if (item is AddonContainerComponent containerComponent)
addon = containerComponent.Addon;
else if (item is BaseAddonContainer container)
addon = container;
if (addon != null)
{
FlippableAddonAttribute[] aAttributes =
(FlippableAddonAttribute[])addon.GetType()
.GetCustomAttributes(typeof(FlippableAddonAttribute), false);
if (aAttributes.Length > 0)
{
aAttributes[0].Flip(from, (Item)addon);
return;
}
}
FlippableAttribute[] attributes =
(FlippableAttribute[])item.GetType().GetCustomAttributes(typeof(FlippableAttribute), false);
if (attributes.Length > 0)
attributes[0].Flip(item);
else
from.SendLocalizedMessage(1042273); // You cannot turn that.
}
private static void Up(Item item, Mobile from)
{
int floorZ = GetFloorZ(item);
if (floorZ > int.MinValue && item.Z < floorZ + 15) // Confirmed : no height checks here
item.Location = new Point3D(item.Location, item.Z + 1);
else
from.SendLocalizedMessage(1042274); // You cannot raise it up any higher.
}
private static void Down(Item item, Mobile from)
{
int floorZ = GetFloorZ(item);
if (floorZ > int.MinValue && item.Z > GetFloorZ(item))
item.Location = new Point3D(item.Location, item.Z - 1);
else
from.SendLocalizedMessage(1042275); // You cannot lower it down any further.
}
private static int GetFloorZ(Item item)
{
Map map = item.Map;
if (map == null)
return int.MinValue;
StaticTile[] tiles = map.Tiles.GetStaticTiles(item.X, item.Y, true);
int z = int.MinValue;
for (int i = 0; i < tiles.Length; ++i)
{
StaticTile tile = tiles[i];
ItemData id = TileData.ItemTable[tile.ID & TileData.MaxItemValue];
int top = tile.Z; // Confirmed : no height checks here
if (id.Surface && !id.Impassable && top > z && top <= item.Z)
z = top;
}
return z;
}
}
}
}

View file

@ -0,0 +1,382 @@
using Server.Network;
using Server.Prompts;
using Server.Targeting;
namespace Server.Items
{
public enum KeyType
{
Copper = 0x100E,
Gold = 0x100F,
Iron = 0x1010,
Rusty = 0x1013
}
public interface ILockable
{
bool Locked { get; set; }
uint KeyValue { get; set; }
}
public class Key : Item
{
private string m_Description;
private uint m_KeyVal;
[Constructible]
public Key(uint val = 0) : this(KeyType.Iron, val)
{
}
public Key(KeyType type, uint val = 0, Item link = null) : base((int)type)
{
Weight = 1.0;
MaxRange = 3;
m_KeyVal = val;
Link = link;
}
public Key(Serial serial) : base(serial)
{
}
[CommandProperty(AccessLevel.GameMaster)]
public string Description
{
get => m_Description;
set
{
m_Description = value;
InvalidateProperties();
}
}
[CommandProperty(AccessLevel.GameMaster)]
public int MaxRange { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public uint KeyValue
{
get => m_KeyVal;
set
{
m_KeyVal = value;
InvalidateProperties();
}
}
[CommandProperty(AccessLevel.GameMaster)]
public Item Link { get; set; }
public static uint RandomValue() => (uint)(0xFFFFFFFE * Utility.RandomDouble()) + 1;
public static void RemoveKeys(Mobile m, uint keyValue)
{
if (keyValue == 0)
return;
RemoveKeys(m.Backpack, keyValue);
RemoveKeys(m.BankBox, keyValue);
}
public static void RemoveKeys(Container cont, uint keyValue)
{
if (cont == null || keyValue == 0)
return;
Item[] items = cont.FindItemsByType(new[] { typeof(Key), typeof(KeyRing) });
foreach (Item item in items)
if (item is Key key)
{
if (key.KeyValue == keyValue)
key.Delete();
}
else
{
KeyRing keyRing = (KeyRing)item;
keyRing.RemoveKeys(keyValue);
}
}
public static bool ContainsKey(Container cont, uint keyValue)
{
if (cont == null)
return false;
Item[] items = cont.FindItemsByType(new[] { typeof(Key), typeof(KeyRing) });
foreach (Item item in items)
if (item is Key key)
{
if (key.KeyValue == keyValue)
return true;
}
else
{
KeyRing keyRing = (KeyRing)item;
if (keyRing.ContainsKey(keyValue))
return true;
}
return false;
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(2); // version
writer.Write(MaxRange);
writer.Write(Link);
writer.Write(m_Description);
writer.Write(m_KeyVal);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 2:
{
MaxRange = reader.ReadInt();
goto case 1;
}
case 1:
{
Link = reader.ReadItem();
goto case 0;
}
case 0:
{
if (version < 2 || MaxRange == 0)
MaxRange = 3;
m_Description = reader.ReadString();
m_KeyVal = reader.ReadUInt();
break;
}
}
}
public override void OnDoubleClick(Mobile from)
{
if (!IsChildOf(from.Backpack))
{
from.SendLocalizedMessage(501661); // That key is unreachable.
return;
}
Target t;
int number;
if (m_KeyVal != 0)
{
number = 501662; // What shall I use this key on?
t = new UnlockTarget(this);
}
else
{
number = 501663; // This key is a key blank. Which key would you like to make a copy of?
t = new CopyTarget(this);
}
from.SendLocalizedMessage(number);
from.Target = t;
}
public override void GetProperties(ObjectPropertyList list)
{
base.GetProperties(list);
string desc;
if (m_KeyVal == 0)
desc = "(blank)";
else if ((desc = m_Description) == null || (desc = desc.Trim()).Length <= 0)
desc = null;
if (desc != null)
list.Add(desc);
}
public override void OnSingleClick(Mobile from)
{
base.OnSingleClick(from);
string desc;
if (m_KeyVal == 0)
desc = "(blank)";
else if ((desc = m_Description) == null || (desc = desc.Trim()).Length <= 0)
desc = "";
if (desc.Length > 0)
from.Send(new UnicodeMessage(Serial, ItemID, MessageType.Regular, 0x3B2, 3, "ENU", "", desc));
}
public bool UseOn(Mobile from, ILockable o)
{
if (o.KeyValue == KeyValue)
{
if (o is BaseDoor door && !door.UseLocks())
return false;
o.Locked = !o.Locked;
if (o is LockableContainer cont1)
if (cont1.LockLevel == -255)
cont1.LockLevel = cont1.RequiredSkill - 10;
if (o is Item item)
{
if (o.Locked)
item.SendLocalizedMessageTo(from, 1048000); // You lock it.
else
item.SendLocalizedMessageTo(from, 1048001); // You unlock it.
if (item is LockableContainer cont && cont.TrapType != TrapType.None && cont.TrapOnLockpick)
{
if (o.Locked)
cont.SendLocalizedMessageTo(from, 501673); // You re-enable the trap.
else
cont.SendLocalizedMessageTo(from,
501672); // You disable the trap temporarily. Lock it again to re-enable it.
}
}
return true;
}
return false;
}
private class RenamePrompt : Prompt
{
private readonly Key m_Key;
public RenamePrompt(Key key) => m_Key = key;
public override void OnResponse(Mobile from, string text)
{
if (m_Key.Deleted || !m_Key.IsChildOf(from.Backpack))
{
from.SendLocalizedMessage(501661); // That key is unreachable.
return;
}
m_Key.Description = Utility.FixHtml(text);
}
}
private class UnlockTarget : Target
{
private readonly Key m_Key;
public UnlockTarget(Key key) : base(key.MaxRange, false, TargetFlags.None)
{
m_Key = key;
CheckLOS = false;
}
protected override void OnTarget(Mobile from, object targeted)
{
if (m_Key.Deleted || !m_Key.IsChildOf(from.Backpack))
{
from.SendLocalizedMessage(501661); // That key is unreachable.
return;
}
int number;
if (targeted == m_Key)
{
number = 501665; // Enter a description for this key.
from.Prompt = new RenamePrompt(m_Key);
}
else if (targeted is ILockable lockable)
{
if (m_Key.UseOn(from, lockable))
number = -1;
else
number = 501668; // This key doesn't seem to unlock that.
}
else
{
number = 501666; // You can't unlock that!
}
if (number != -1) from.SendLocalizedMessage(number);
}
}
private class CopyTarget : Target
{
private readonly Key m_Key;
public CopyTarget(Key key) : base(3, false, TargetFlags.None) => m_Key = key;
protected override void OnTarget(Mobile from, object targeted)
{
if (m_Key.Deleted || !m_Key.IsChildOf(from.Backpack))
{
from.SendLocalizedMessage(501661); // That key is unreachable.
return;
}
int number;
if (targeted is Key k)
{
if (k.m_KeyVal == 0)
{
number = 501675; // This key is also blank.
}
else if (from.CheckTargetSkill(SkillName.Tinkering, k, 0, 75.0))
{
number = 501676; // You make a copy of the key.
m_Key.Description = k.Description;
m_Key.KeyValue = k.KeyValue;
m_Key.Link = k.Link;
m_Key.MaxRange = k.MaxRange;
}
else if (Utility.RandomDouble() <= 0.1) // 10% chance to destroy the key
{
from.SendLocalizedMessage(501677); // You fail to make a copy of the key.
number = 501678; // The key was destroyed in the attempt.
m_Key.Delete();
}
else
{
number = 501677; // You fail to make a copy of the key.
}
}
else
{
number = 501688; // Not a key.
}
from.SendLocalizedMessage(number);
}
}
}
}

View file

@ -0,0 +1,185 @@
using System.Collections.Generic;
using Server.Targeting;
namespace Server.Items
{
public class KeyRing : Item
{
public static readonly int MaxKeys = 20;
[Constructible]
public KeyRing() : base(0x1011)
{
Weight = 1.0; // They seem to have no weight on OSI ?!
Keys = new List<Key>();
}
public KeyRing(Serial serial) : base(serial)
{
}
public List<Key> Keys { get; private set; }
public override bool OnDragDrop(Mobile from, Item dropped)
{
if (!IsChildOf(from.Backpack))
{
from.SendLocalizedMessage(1060640); // The item must be in your backpack to use it.
return false;
}
if (!(dropped is Key key) || key.KeyValue == 0)
{
from.SendLocalizedMessage(501689); // Only non-blank keys can be put on a keyring.
return false;
}
if (Keys.Count >= MaxKeys)
{
from.SendLocalizedMessage(1008138); // This keyring is full.
return false;
}
Add(key);
from.SendLocalizedMessage(501691); // You put the key on the keyring.
return true;
}
public override void OnDoubleClick(Mobile from)
{
if (!IsChildOf(from.Backpack))
{
from.SendLocalizedMessage(1060640); // The item must be in your backpack to use it.
return;
}
from.SendLocalizedMessage(501680); // What do you want to unlock?
from.Target = new InternalTarget(this);
}
public override void OnDelete()
{
base.OnDelete();
foreach (Key key in Keys) key.Delete();
Keys.Clear();
}
public void Add(Key key)
{
key.Internalize();
Keys.Add(key);
UpdateItemID();
}
public void Open(Mobile from)
{
if (!(Parent is Container cont))
return;
for (int i = Keys.Count - 1; i >= 0; i--)
{
Key key = Keys[i];
if (!key.Deleted && !cont.TryDropItem(from, key, true))
break;
Keys.RemoveAt(i);
}
UpdateItemID();
}
public void RemoveKeys(uint keyValue)
{
for (int i = Keys.Count - 1; i >= 0; i--)
{
Key key = Keys[i];
if (key.KeyValue == keyValue)
{
key.Delete();
Keys.RemoveAt(i);
}
}
UpdateItemID();
}
public bool ContainsKey(uint keyValue)
{
foreach (Key key in Keys)
if (key.KeyValue == keyValue)
return true;
return false;
}
private void UpdateItemID()
{
if (Keys.Count < 1)
ItemID = 0x1011;
else if (Keys.Count < 3)
ItemID = 0x1769;
else if (Keys.Count < 5)
ItemID = 0x176A;
else
ItemID = 0x176B;
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0); // version
writer.WriteItemList(Keys);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadEncodedInt();
Keys = reader.ReadStrongItemList<Key>();
}
private class InternalTarget : Target
{
private readonly KeyRing m_KeyRing;
public InternalTarget(KeyRing keyRing) : base(-1, false, TargetFlags.None) => m_KeyRing = keyRing;
protected override void OnTarget(Mobile from, object targeted)
{
if (m_KeyRing.Deleted || !m_KeyRing.IsChildOf(from.Backpack))
{
from.SendLocalizedMessage(1060640); // The item must be in your backpack to use it.
return;
}
if (m_KeyRing == targeted)
{
m_KeyRing.Open(from);
from.SendLocalizedMessage(501685); // You open the keyring.
}
else if (targeted is ILockable o)
{
foreach (Key key in m_KeyRing.Keys)
if (key.UseOn(from, o))
return;
from.SendLocalizedMessage(1008140); // You do not have a key for that.
}
else
{
from.SendLocalizedMessage(501666); // You can't unlock that!
}
}
}
}
}

View file

@ -0,0 +1,110 @@
using Server.Network;
namespace Server.Items
{
public class LOSBlocker : Item
{
[Constructible]
public LOSBlocker() : base(0x21A2) => Movable = false;
public LOSBlocker(Serial serial) : base(serial)
{
}
public override string DefaultName => "no line of sight";
public static void Initialize()
{
TileData.ItemTable[0x21A2].Flags = TileFlag.Wall | TileFlag.NoShoot;
TileData.ItemTable[0x21A2].Height = 20;
}
protected override Packet GetWorldPacketFor(NetState state)
{
Mobile mob = state.Mobile;
if (mob?.AccessLevel >= AccessLevel.GameMaster) return new GMItemPacket(this);
return base.GetWorldPacketFor(state);
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(1);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
if (version < 1 && ItemID == 0x2199)
ItemID = 0x21A2;
}
public sealed class GMItemPacket : Packet
{
public GMItemPacket(Item item) : base(0x1A)
{
EnsureCapacity(20);
// 14 base length
// +2 - Amount
// +2 - Hue
// +1 - Flags
uint serial = item.Serial.Value;
int itemID = 0x36FF;
int amount = item.Amount;
Point3D loc = item.Location;
int x = loc.X;
int y = loc.Y;
int hue = item.Hue;
int flags = item.GetPacketFlags();
int direction = (int)item.Direction;
if (amount != 0)
serial |= 0x80000000;
else
serial &= 0x7FFFFFFF;
Stream.Write(serial);
Stream.Write((short)(itemID & 0x7FFF));
if (amount != 0)
Stream.Write((short)amount);
x &= 0x7FFF;
if (direction != 0)
x |= 0x8000;
Stream.Write((short)x);
y &= 0x3FFF;
if (hue != 0)
y |= 0x8000;
if (flags != 0)
y |= 0x4000;
Stream.Write((short)y);
if (direction != 0)
Stream.Write((byte)direction);
Stream.Write((sbyte)loc.Z);
if (hue != 0)
Stream.Write((ushort)hue);
if (flags != 0)
Stream.Write((byte)flags);
}
}
}
}

View file

@ -0,0 +1,36 @@
namespace Server.Items
{
public class MinotaurArtifact : Item
{
[Constructible]
public MinotaurArtifact() : base(Utility.RandomList(0xB46, 0xB48, 0x9ED))
{
if (ItemID == 0x9ED)
Weight = 30;
LootType = LootType.Blessed;
Hue = 0x100;
}
public MinotaurArtifact(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1074826; // Minotaur Artifact
public override double DefaultWeight => 5.0;
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,203 @@
using System;
using Server.Factions;
using Server.Mobiles;
using Server.Network;
namespace Server.Items
{
public enum MoonstoneType
{
Felucca,
Trammel
}
public class Moonstone : Item
{
private MoonstoneType m_Type;
[Constructible]
public Moonstone(MoonstoneType type) : base(0xF8B)
{
Weight = 1.0;
m_Type = type;
}
public Moonstone(Serial serial) : base(serial)
{
}
[CommandProperty(AccessLevel.GameMaster)]
public MoonstoneType Type
{
get => m_Type;
set
{
m_Type = value;
InvalidateProperties();
}
}
public override int LabelNumber => 1041490 + (int)m_Type;
public override void OnSingleClick(Mobile from)
{
if (IsChildOf(from.Backpack))
{
Hue = Utility.RandomBirdHue();
ProcessDelta();
from.SendLocalizedMessage(1005398); // The stone's substance shifts as you examine it.
}
base.OnSingleClick(from);
}
public override void OnDoubleClick(Mobile from)
{
if (!IsChildOf(from.Backpack))
{
from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it.
}
else if (from.Mounted)
{
from.SendLocalizedMessage(1005399); // You can not bury a stone while you sit on a mount.
}
else if (!from.Body.IsHuman)
{
from.SendLocalizedMessage(1005400); // You can not bury a stone in this form.
}
else if (Sigil.ExistsOn(from))
{
from.SendLocalizedMessage(1061632); // You can't do that while carrying the sigil.
}
else if (from.Map == GetTargetMap() || (from.Map != Map.Trammel && from.Map != Map.Felucca))
{
from.SendLocalizedMessage(1005401); // You cannot bury the stone here.
}
else if (from is PlayerMobile mobile && mobile.Young)
{
mobile.SendLocalizedMessage(1049543); // You decide against traveling to Felucca while you are still young.
}
else if (from.Kills >= 5)
{
from.SendLocalizedMessage(
1005402); // The magic of the stone cannot be evoked by someone with blood on their hands.
}
else if (from.Criminal)
{
from.SendLocalizedMessage(1005403); // The magic of the stone cannot be evoked by the lawless.
}
else if (!Region.Find(from.Location, from.Map).IsDefault ||
!Region.Find(from.Location, GetTargetMap()).IsDefault)
{
from.SendLocalizedMessage(1005401); // You cannot bury the stone here.
}
else if (!GetTargetMap().CanFit(from.Location, 16))
{
from.SendLocalizedMessage(1005408); // Something is blocking the facet gate exit.
}
else
{
Movable = false;
MoveToWorld(from.Location, from.Map);
from.Animate(32, 5, 1, true, false, 0);
new SettleTimer(this, from.Location, from.Map, GetTargetMap(), from).Start();
}
}
public Map GetTargetMap() => m_Type == MoonstoneType.Felucca ? Map.Felucca : Map.Trammel;
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
writer.Write((int)m_Type);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 0:
{
m_Type = (MoonstoneType)reader.ReadInt();
break;
}
}
}
private class SettleTimer : Timer
{
private readonly Mobile m_Caster;
private int m_Count;
private readonly Point3D m_Location;
private readonly Map m_Map;
private readonly Map m_TargetMap;
private readonly Item m_Stone;
public SettleTimer(Item stone, Point3D loc, Map map, Map targetMap, Mobile caster) : base(
TimeSpan.FromSeconds(2.5), TimeSpan.FromSeconds(1.0))
{
m_Stone = stone;
m_Location = loc;
m_Map = map;
m_TargetMap = targetMap;
m_Caster = caster;
}
protected override void OnTick()
{
++m_Count;
if (m_Count == 1)
{
m_Stone.PublicOverheadMessage(MessageType.Regular, 0x3B2, 1005414); // The stone settles into the ground.
}
else if (m_Count >= 10)
{
m_Stone.Location = new Point3D(m_Stone.X, m_Stone.Y, m_Stone.Z - 1);
if (m_Count == 16)
{
if (!Region.Find(m_Location, m_Map).IsDefault || !Region.Find(m_Location, m_TargetMap).IsDefault)
{
m_Stone.Movable = true;
m_Caster.AddToBackpack(m_Stone);
Stop();
return;
}
if (!m_TargetMap.CanFit(m_Location, 16))
{
m_Stone.Movable = true;
m_Caster.AddToBackpack(m_Stone);
Stop();
return;
}
int hue = m_Stone.Hue;
if (hue == 0)
hue = Utility.RandomBirdHue();
new MoonstoneGate(m_Location, m_TargetMap, m_Map, m_Caster, hue);
new MoonstoneGate(m_Location, m_Map, m_TargetMap, m_Caster, hue);
m_Stone.Delete();
Stop();
}
}
}
}
}
}

View file

@ -0,0 +1,83 @@
using System;
using Server.Engines.PartySystem;
namespace Server.Items
{
public class MoonstoneGate : Moongate
{
private readonly Mobile m_Caster;
public MoonstoneGate(Point3D loc, Map map, Map targetMap, Mobile caster, int hue) : base(loc, targetMap)
{
MoveToWorld(loc, map);
Dispellable = false;
Hue = hue;
m_Caster = caster;
new InternalTimer(this).Start();
Effects.PlaySound(loc, map, 0x20E);
}
public MoonstoneGate(Serial serial) : base(serial)
{
}
public override void CheckGate(Mobile m, int range)
{
if (m.Kills >= 5)
return;
Party casterParty = Party.Get(m_Caster);
Party userParty = Party.Get(m);
if (m == m_Caster || (casterParty != null && userParty == casterParty))
base.CheckGate(m, range);
}
public override void UseGate(Mobile m)
{
if (m.Kills >= 5)
return;
Party casterParty = Party.Get(m_Caster);
Party userParty = Party.Get(m);
if (m == m_Caster || (casterParty != null && userParty == casterParty))
base.UseGate(m);
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
Delete();
}
private class InternalTimer : Timer
{
private readonly Item m_Item;
public InternalTimer(Item item) : base(TimeSpan.FromSeconds(30.0))
{
m_Item = item;
Priority = TimerPriority.OneSecond;
}
protected override void OnTick()
{
m_Item.Delete();
}
}
}
}

View file

@ -0,0 +1,124 @@
using System;
using System.Linq;
namespace Server.Items
{
public class MorphItem : Item
{
private int m_InsideRange;
private int m_OutsideRange;
[Constructible]
public MorphItem(int inactiveItemID, int activeItemID, int range) : this(inactiveItemID, activeItemID, range, range)
{
}
[Constructible]
public MorphItem(int inactiveItemID, int activeItemID, int inRange, int outRange) : base(inactiveItemID)
{
Movable = false;
InactiveItemID = inactiveItemID;
ActiveItemID = activeItemID;
InsideRange = inRange;
OutsideRange = outRange;
}
public MorphItem(Serial serial) : base(serial)
{
}
[CommandProperty(AccessLevel.GameMaster)]
public int InactiveItemID { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public int ActiveItemID { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public int InsideRange
{
get => m_InsideRange;
set => m_InsideRange = value > 18 ? 18 : value < 0 ? 0 : value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int OutsideRange
{
get => m_OutsideRange;
set => m_OutsideRange = value > 18 ? 18 : value < 0 ? 0 : value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int CurrentRange => ItemID == InactiveItemID ? InsideRange : OutsideRange;
public override bool HandlesOnMovement => true;
public override void OnMovement(Mobile m, Point3D oldLocation)
{
if (Utility.InRange(m.Location, Location, CurrentRange) || Utility.InRange(oldLocation, Location, CurrentRange))
Refresh();
}
public override void OnMapChange()
{
if (!Deleted)
Refresh();
}
public override void OnLocationChange(Point3D oldLoc)
{
if (!Deleted)
Refresh();
}
public void Refresh()
{
bool found = GetMobilesInRange(CurrentRange).Any(mob => !mob.Hidden || mob.AccessLevel <= AccessLevel.Player);
ItemID = found ? ActiveItemID : InactiveItemID;
Visible = ItemID != 0x1;
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(1); // version
writer.Write(m_OutsideRange);
writer.Write(InactiveItemID);
writer.Write(ActiveItemID);
writer.Write(m_InsideRange);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 1:
{
m_OutsideRange = reader.ReadInt();
goto case 0;
}
case 0:
{
InactiveItemID = reader.ReadInt();
ActiveItemID = reader.ReadInt();
m_InsideRange = reader.ReadInt();
if (version < 1)
m_OutsideRange = m_InsideRange;
break;
}
}
Timer.DelayCall(TimeSpan.Zero, Refresh);
}
}
}

View file

@ -0,0 +1,142 @@
using System;
using Server.Mobiles;
using Server.Network;
using Server.Targeting;
namespace Server.Items
{
public class OilCloth : Item, IScissorable, IDyable
{
[Constructible]
public OilCloth() : base(0x175D) => Hue = 2001;
public OilCloth(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1041498; // oil cloth
public override double DefaultWeight => 1.0;
public bool Dye(Mobile from, DyeTub sender)
{
if (Deleted)
return false;
Hue = sender.DyedHue;
return true;
}
public bool Scissor(Mobile from, Scissors scissors)
{
if (Deleted || !from.CanSee(this))
return false;
this.ScissorHelper(from, new Bandage(), 1);
return true;
}
public override void OnDoubleClick(Mobile from)
{
if (IsChildOf(from.Backpack))
{
from.BeginTarget(-1, false, TargetFlags.None, OnTarget);
from.SendLocalizedMessage(1005424); // Select the weapon or armor you wish to use the cloth on.
}
else
{
from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it.
}
}
public void OnTarget(Mobile from, object obj)
{
// TODO: Need details on how oil cloths should get consumed here
if (!IsChildOf(from.Backpack))
{
from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it.
}
else if (obj is Item item && item.RootParent != from)
{
from.SendLocalizedMessage(1005425); // You may only wipe down items you are holding or carrying.
}
else if (obj is BaseWeapon weapon)
{
if (weapon.Poison == null || weapon.PoisonCharges <= 0)
{
from.LocalOverheadMessage(MessageType.Regular, 0x3B2,
1005422); // Hmmmm... this does not need to be cleaned.
}
else
{
if (weapon.PoisonCharges < 2)
weapon.PoisonCharges = 0;
else
weapon.PoisonCharges -= 2;
if (weapon.PoisonCharges > 0)
from.SendLocalizedMessage(1005423); // You have removed some of the caustic substance, but not all.
else
from.SendLocalizedMessage(1010497); // You have cleaned the item.
}
}
else if (obj == from && obj is PlayerMobile pm)
{
if (pm.BodyMod == 183 || pm.BodyMod == 184)
{
pm.SavagePaintExpiration = TimeSpan.Zero;
pm.BodyMod = 0;
pm.HueMod = -1;
from.SendLocalizedMessage(1040006); // You wipe away all of your body paint.
Consume();
}
else
{
from.LocalOverheadMessage(MessageType.Regular, 0x3B2,
1005422); // Hmmmm... this does not need to be cleaned.
}
}
else if (obj is BaseBeverage beverage)
{
if (beverage.Content == BeverageType.Liquor)
{
Firebomb bomb = new Firebomb(beverage.ItemID);
bomb.Name = beverage.Name;
beverage.ReplaceWith(bomb);
from.SendLocalizedMessage(1060580); // You prepare a firebomb.
Consume();
}
}
else if (obj is Firebomb)
{
from.SendLocalizedMessage(1060579); // That is already a firebomb!
}
else
{
from.SendLocalizedMessage(1005426); // The cloth will not work on that.
}
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,214 @@
namespace Server.Items
{
public class OrigamiPaper : Item
{
[Constructible]
public OrigamiPaper() : base(0x2830)
{
}
public OrigamiPaper(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1030288; // origami paper
public override void OnDoubleClick(Mobile from)
{
if (!IsChildOf(from.Backpack))
{
from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it.
}
else
{
Delete();
var i = Utility.Random(from.BAC >= 5 ? 6 : 5) switch
{
0 => (Item)new OrigamiButterfly(),
1 => new OrigamiSwan(),
2 => new OrigamiFrog(),
3 => new OrigamiShape(),
4 => new OrigamiSongbird(),
5 => new OrigamiFish(),
_ => null
};
if (i != null)
from.AddToBackpack(i);
from.SendLocalizedMessage(1070822); // You fold the paper into an interesting shape.
}
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadEncodedInt();
}
}
public class OrigamiButterfly : Item
{
[Constructible]
public OrigamiButterfly() : base(0x2838) => LootType = LootType.Blessed;
public OrigamiButterfly(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1030296; // a delicate origami butterfly
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadEncodedInt();
}
}
public class OrigamiSwan : Item
{
[Constructible]
public OrigamiSwan() : base(0x2839) => LootType = LootType.Blessed;
public OrigamiSwan(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1030297; // a delicate origami swan
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadEncodedInt();
}
}
public class OrigamiFrog : Item
{
[Constructible]
public OrigamiFrog() : base(0x283A) => LootType = LootType.Blessed;
public OrigamiFrog(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1030298; // a delicate origami frog
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadEncodedInt();
}
}
public class OrigamiShape : Item
{
[Constructible]
public OrigamiShape() : base(0x283B) => LootType = LootType.Blessed;
public OrigamiShape(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1030299; // an intricate geometric origami shape
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadEncodedInt();
}
}
public class OrigamiSongbird : Item
{
[Constructible]
public OrigamiSongbird() : base(0x283C) => LootType = LootType.Blessed;
public OrigamiSongbird(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1030300; // a delicate origami songbird
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadEncodedInt();
}
}
public class OrigamiFish : Item
{
[Constructible]
public OrigamiFish() : base(0x283D) => LootType = LootType.Blessed;
public OrigamiFish(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1030301; // a delicate origami fish
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadEncodedInt();
}
}
}

View file

@ -0,0 +1,32 @@
namespace Server.Items
{
public class GrobusFur : Item
{
[Constructible]
public GrobusFur() : base(0x11F4)
{
LootType = LootType.Blessed;
Hue = 0x455;
}
public GrobusFur(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1074676; // Grobu's Fur
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,32 @@
namespace Server.Items
{
public class PrimitiveFetish : Item
{
[Constructible]
public PrimitiveFetish() : base(0x23F)
{
LootType = LootType.Blessed;
Hue = 0x244;
}
public PrimitiveFetish(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1074675; // Primitive Fetish
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,28 @@
namespace Server.Items
{
public class AcidProofRope : Item
{
[Constructible]
public AcidProofRope() : base(0x20D) => Hue = 0x3D1;
public AcidProofRope(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1074886; // Acid Proof Rope
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,30 @@
namespace Server.Items
{
public class ParoxysmusCorrodedStein : Item
{
[Constructible]
public ParoxysmusCorrodedStein() : base(0x9D6)
{
}
public ParoxysmusCorrodedStein(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1072083; // Paroxysmus' Corroded Stein
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,30 @@
namespace Server.Items
{
public class ParoxysmusDinner : Item
{
[Constructible]
public ParoxysmusDinner() : base(0x1E95)
{
}
public ParoxysmusDinner(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1072086; // Paroxysmus' Dinner
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,30 @@
namespace Server.Items
{
public class StringOfPartsOfParoxysmusVictims : Item
{
[Constructible]
public StringOfPartsOfParoxysmusVictims() : base(0xFD2)
{
}
public StringOfPartsOfParoxysmusVictims(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1072082; // String of Parts of Paroxysmus' Victims
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,30 @@
namespace Server.Items
{
public class SweatOfParoxysmus : Item
{
[Constructible]
public SweatOfParoxysmus() : base(0xF01)
{
}
public SweatOfParoxysmus(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1072081; // Sweat of Paroxysmus
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,591 @@
using System;
using System.Collections.Generic;
using Server.Gumps;
using Server.Multis;
using Server.Prompts;
using Server.Mobiles;
using Server.Network;
using Server.ContextMenus;
namespace Server.Items
{
public class PlayerBBSouth : BasePlayerBB
{
public override int LabelNumber => 1062421; // bulletin board (south)
[Constructible]
public PlayerBBSouth() : base(0x2311) => Weight = 15.0;
public PlayerBBSouth(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
public class PlayerBBEast : BasePlayerBB
{
public override int LabelNumber => 1062420; // bulletin board (east)
[Constructible]
public PlayerBBEast() : base(0x2312) => Weight = 15.0;
public PlayerBBEast(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
public abstract class BasePlayerBB : Item, ISecurable
{
public List<PlayerBBMessage> Messages { get; private set; }
public PlayerBBMessage Greeting { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public string Title { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public SecureLevel Level { get; set; }
public BasePlayerBB(int itemID) : base(itemID)
{
Messages = new List<PlayerBBMessage>();
Level = SecureLevel.Anyone;
}
public BasePlayerBB(Serial serial) : base(serial)
{
}
public override void GetContextMenuEntries(Mobile from, List<ContextMenuEntry> list)
{
base.GetContextMenuEntries(from, list);
SetSecureLevelEntry.AddTo(from, this, list);
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(1);
writer.Write((int)Level);
writer.Write(Title);
if (Greeting != null)
{
writer.Write(true);
Greeting.Serialize(writer);
}
else
{
writer.Write(false);
}
writer.WriteEncodedInt(Messages.Count);
for (int i = 0; i < Messages.Count; ++i)
Messages[i].Serialize(writer);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 1:
{
Level = (SecureLevel)reader.ReadInt();
goto case 0;
}
case 0:
{
if (version < 1)
Level = SecureLevel.Anyone;
Title = reader.ReadString();
if (reader.ReadBool())
Greeting = new PlayerBBMessage(reader);
int count = reader.ReadEncodedInt();
Messages = new List<PlayerBBMessage>(count);
for (int i = 0; i < count; ++i)
Messages.Add(new PlayerBBMessage(reader));
break;
}
}
}
public static bool CheckAccess(BaseHouse house, Mobile from)
{
if (house.Public || !house.IsAosRules)
return !house.IsBanned(from);
return house.HasAccess(from);
}
public override void OnDoubleClick(Mobile from)
{
BaseHouse house = BaseHouse.FindHouseAt(this);
if (house?.HasLockedDownItem(this) != true)
from.SendLocalizedMessage(1062396); // This bulletin board must be locked down in a house to be usable.
else if (!from.InRange(GetWorldLocation(), 2) || !from.InLOS(this))
from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that.
else if (CheckAccess(house, from))
from.SendGump(new PlayerBBGump(from, house, this, 0));
}
public class PostPrompt : Prompt
{
private readonly int m_Page;
private readonly BaseHouse m_House;
private readonly BasePlayerBB m_Board;
private readonly bool m_Greeting;
public PostPrompt(int page, BaseHouse house, BasePlayerBB board, bool greeting)
{
m_Page = page;
m_House = house;
m_Board = board;
m_Greeting = greeting;
}
public override void OnCancel(Mobile from)
{
OnResponse(from, "");
}
public override void OnResponse(Mobile from, string text)
{
int page = m_Page;
BaseHouse house = m_House;
BasePlayerBB board = m_Board;
if (house?.HasLockedDownItem(board) != true)
{
from.SendLocalizedMessage(1062396); // This bulletin board must be locked down in a house to be usable.
return;
}
if (!from.InRange(board.GetWorldLocation(), 2) || !from.InLOS(board))
{
from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that.
return;
}
if (!CheckAccess(house, from))
{
from.SendLocalizedMessage(1062398); // You are not allowed to post to this bulletin board.
return;
}
if (m_Greeting && !house.IsOwner(from)) return;
text = text.Trim();
if (text.Length > 255)
text = text.Substring(0, 255);
if (text.Length > 0)
{
PlayerBBMessage message = new PlayerBBMessage(DateTime.UtcNow, from, text);
if (m_Greeting)
{
board.Greeting = message;
}
else
{
board.Messages.Add(message);
if (board.Messages.Count > 50)
{
board.Messages.RemoveAt(0);
if (page > 0)
--page;
}
}
}
from.SendGump(new PlayerBBGump(from, house, board, page));
}
}
public class SetTitlePrompt : Prompt
{
private readonly int m_Page;
private readonly BaseHouse m_House;
private readonly BasePlayerBB m_Board;
public SetTitlePrompt(int page, BaseHouse house, BasePlayerBB board)
{
m_Page = page;
m_House = house;
m_Board = board;
}
public override void OnCancel(Mobile from)
{
OnResponse(from, "");
}
public override void OnResponse(Mobile from, string text)
{
int page = m_Page;
BaseHouse house = m_House;
BasePlayerBB board = m_Board;
if (house?.HasLockedDownItem(board) != true)
{
from.SendLocalizedMessage(1062396); // This bulletin board must be locked down in a house to be usable.
return;
}
if (!from.InRange(board.GetWorldLocation(), 2) || !from.InLOS(board))
{
from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that.
return;
}
if (!CheckAccess(house, from))
{
from.SendLocalizedMessage(1062398); // You are not allowed to post to this bulletin board.
return;
}
text = text.Trim();
if (text.Length > 255)
text = text.Substring(0, 255);
if (text.Length > 0)
board.Title = text;
from.SendGump(new PlayerBBGump(from, house, board, page));
}
}
}
public class PlayerBBMessage
{
[CommandProperty(AccessLevel.GameMaster)]
public DateTime Time { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public Mobile Poster { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public string Message { get; set; }
public PlayerBBMessage(DateTime time, Mobile poster, string message)
{
Time = time;
Poster = poster;
Message = message;
}
public PlayerBBMessage(IGenericReader reader)
{
int version = reader.ReadEncodedInt();
switch (version)
{
case 0:
{
Time = reader.ReadDateTime();
Poster = reader.ReadMobile();
Message = reader.ReadString();
break;
}
}
}
public void Serialize(IGenericWriter writer)
{
writer.WriteEncodedInt(0); // version
writer.Write(Time);
writer.Write(Poster);
writer.Write(Message);
}
}
public class PlayerBBGump : Gump
{
private readonly int m_Page;
private readonly Mobile m_From;
private readonly BaseHouse m_House;
private readonly BasePlayerBB m_Board;
private const int LabelColor = 0x7FFF;
private const int LabelHue = 1153;
public override void OnResponse(NetState sender, RelayInfo info)
{
int page = m_Page;
Mobile from = m_From;
BaseHouse house = m_House;
BasePlayerBB board = m_Board;
if (house?.HasLockedDownItem(board) != true)
{
from.SendLocalizedMessage(1062396); // This bulletin board must be locked down in a house to be usable.
return;
}
if (!from.InRange(board.GetWorldLocation(), 2) || !from.InLOS(board))
{
from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that.
return;
}
if (!BasePlayerBB.CheckAccess(house, from))
{
from.SendLocalizedMessage(1062398); // You are not allowed to post to this bulletin board.
return;
}
switch (info.ButtonID)
{
case 1: // Post message
{
from.Prompt = new BasePlayerBB.PostPrompt(page, house, board, false);
from.SendLocalizedMessage(1062397); // Please enter your message:
break;
}
case 2: // Set title
{
if (house.IsOwner(from))
{
from.Prompt = new BasePlayerBB.SetTitlePrompt(page, house, board);
from.SendLocalizedMessage(1062402); // Enter new title:
}
break;
}
case 3: // Post greeting
{
if (house.IsOwner(from))
{
from.Prompt = new BasePlayerBB.PostPrompt(page, house, board, true);
from.SendLocalizedMessage(1062404); // Enter new greeting (this will always be the first post):
}
break;
}
case 4: // Scroll up
{
if (page == 0)
page = board.Messages.Count;
else
page -= 1;
from.SendGump(new PlayerBBGump(from, house, board, page));
break;
}
case 5: // Scroll down
{
page += 1;
page %= board.Messages.Count + 1;
from.SendGump(new PlayerBBGump(from, house, board, page));
break;
}
case 6: // Banish poster
{
if (house.IsOwner(from))
{
if (page >= 1 && page <= board.Messages.Count)
{
PlayerBBMessage message = board.Messages[page - 1];
Mobile poster = message.Poster;
if (poster == null)
{
from.SendGump(new PlayerBBGump(from, house, board, page));
return;
}
if (poster.AccessLevel > AccessLevel.Player && from.AccessLevel <= poster.AccessLevel)
{
from.SendLocalizedMessage(501354); // Uh oh...a bigger boot may be required.
}
else if (house.IsFriend(poster))
{
from.SendLocalizedMessage(1060750); // That person is a friend, co-owner, or owner of this house, and therefore cannot be banished!
}
else if (poster is PlayerVendor)
{
from.SendLocalizedMessage(501351); // You cannot eject a vendor.
}
else if (house.Bans.Count >= BaseHouse.MaxBans)
{
from.SendLocalizedMessage(501355); // The ban limit for this house has been reached!
}
else if (house.IsBanned(poster))
{
from.SendLocalizedMessage(501356); // This person is already banned!
}
else if (poster is BaseCreature creature && creature.NoHouseRestrictions)
{
from.SendLocalizedMessage(1062040); // You cannot ban that.
}
else
{
if (!house.Bans.Contains(poster))
house.Bans.Add(poster);
from.SendLocalizedMessage(1062417); // That person has been banned from this house.
if (house.IsInside(poster) && !BasePlayerBB.CheckAccess(house, poster))
poster.MoveToWorld(house.BanLocation, house.Map);
}
}
from.SendGump(new PlayerBBGump(from, house, board, page));
}
break;
}
case 7: // Delete message
{
if (house.IsOwner(from))
{
if (page >= 1 && page <= board.Messages.Count)
board.Messages.RemoveAt(page - 1);
from.SendGump(new PlayerBBGump(from, house, board, 0));
}
break;
}
case 8: // Post props
{
if (from.AccessLevel >= AccessLevel.GameMaster)
{
PlayerBBMessage message = board.Greeting;
if (page >= 1 && page <= board.Messages.Count)
message = board.Messages[page - 1];
from.SendGump(new PlayerBBGump(from, house, board, page));
from.SendGump(new PropertiesGump(from, message));
}
break;
}
}
}
public PlayerBBGump(Mobile from, BaseHouse house, BasePlayerBB board, int page) : base(50, 10)
{
from.CloseGump<PlayerBBGump>();
m_Page = page;
m_From = from;
m_House = house;
m_Board = board;
AddPage(0);
AddImage(30, 30, 5400);
AddButton(393, 145, 2084, 2084, 4); // Scroll up
AddButton(390, 371, 2085, 2085, 5); // Scroll down
AddButton(32, 183, 5412, 5413, 1); // Post message
if (house.IsOwner(from))
{
AddButton(63, 90, 5601, 5605, 2);
AddHtmlLocalized(81, 89, 230, 20, 1062400, LabelColor); // Set title
AddButton(63, 109, 5601, 5605, 3);
AddHtmlLocalized(81, 108, 230, 20, 1062401, LabelColor); // Post greeting
}
string title = board.Title;
if (title != null)
AddHtml(183, 68, 180, 23, title);
AddHtmlLocalized(385, 89, 60, 20, 1062409, LabelColor); // Post
AddLabel(440, 89, LabelHue, page.ToString());
AddLabel(455, 89, LabelHue, "/");
AddLabel(470, 89, LabelHue, board.Messages.Count.ToString());
PlayerBBMessage message = board.Greeting;
if (page >= 1 && page <= board.Messages.Count)
message = board.Messages[page - 1];
AddImageTiled(150, 220, 240, 1, 2700); // Separator
AddHtmlLocalized(150, 180, 100, 20, 1062405, 16715); // Posted On:
AddHtmlLocalized(150, 200, 100, 20, 1062406, 16715); // Posted By:
if (message != null)
{
AddHtml(255, 180, 150, 20, message.Time.ToString("yyyy-MM-dd HH:mm:ss"));
Mobile poster = message.Poster;
string name = poster?.Name;
if (name == null || (name = name.Trim()).Length == 0)
name = "Someone";
AddHtml(255, 200, 150, 20, name);
AddHtml(150, 240, 250, 100, message.Message ?? "");
if (message != board.Greeting && house.IsOwner(from))
{
AddButton(130, 395, 1209, 1210, 6);
AddHtmlLocalized(150, 393, 150, 20, 1062410, LabelColor); // Banish Poster
AddButton(310, 395, 1209, 1210, 7);
AddHtmlLocalized(330, 393, 150, 20, 1062411, LabelColor); // Delete Message
}
if (from.AccessLevel >= AccessLevel.GameMaster)
AddButton(135, 242, 1209, 1210, 8); // Post props
}
}
}
}

View file

@ -0,0 +1,101 @@
using Server.Mobiles;
using Server.Multis;
namespace Server.Items
{
public class ContractOfEmployment : Item
{
[Constructible]
public ContractOfEmployment() : base(0x14F0) => Weight = 1.0;
public ContractOfEmployment(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1041243; // a contract of employment
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
public override void OnDoubleClick(Mobile from)
{
if (!IsChildOf(from.Backpack))
{
from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it.
}
else if (from.AccessLevel >= AccessLevel.GameMaster)
{
from.SendLocalizedMessage(503248); // Your godly powers allow you to place this vendor whereever you wish.
Mobile v = new PlayerVendor(from, BaseHouse.FindHouseAt(from));
v.Direction = from.Direction & Direction.Mask;
v.MoveToWorld(from.Location, from.Map);
v.SayTo(from, 503246); // Ah! it feels good to be working again.
Delete();
}
else
{
BaseHouse house = BaseHouse.FindHouseAt(from);
if (house == null)
{
from.SendLocalizedMessage(503240); // Vendors can only be placed in houses.
}
else if (!BaseHouse.NewVendorSystem && !house.IsFriend(from))
{
from.SendLocalizedMessage(
503242); // You must ask the owner of this building to name you a friend of the household in order to place a vendor here.
}
else if (BaseHouse.NewVendorSystem && !house.IsOwner(from))
{
from.SendLocalizedMessage(
1062423); // Only the house owner can directly place vendors. Please ask the house owner to offer you a vendor contract so that you may place a vendor in this house.
}
else if (!house.Public || !house.CanPlaceNewVendor())
{
from.SendLocalizedMessage(
503241); // You cannot place this vendor or barkeep. Make sure the house is public and has sufficient storage available.
}
else
{
BaseHouse.IsThereVendor(from.Location, from.Map, out bool vendor, out bool contract);
if (vendor)
{
from.SendLocalizedMessage(1062677); // You cannot place a vendor or barkeep at this location.
}
else if (contract)
{
from.SendLocalizedMessage(
1062678); // You cannot place a vendor or barkeep on top of a rental contract!
}
else
{
Mobile v = new PlayerVendor(from, house);
v.Direction = from.Direction & Direction.Mask;
v.MoveToWorld(from.Location, from.Map);
v.SayTo(from, 503246); // Ah! it feels good to be working again.
Delete();
}
}
}
}
}
}

View file

@ -0,0 +1,94 @@
using System;
using System.Collections.Generic;
using Server.Mobiles;
namespace Server.Items
{
public class PoolOfAcid : Item
{
private readonly TimeSpan m_Duration;
private readonly int m_MinDamage;
private readonly int m_MaxDamage;
private readonly DateTime m_Created;
private bool m_Drying;
private readonly Timer m_Timer;
[Constructible]
public PoolOfAcid() : this(TimeSpan.FromSeconds(10.0), 2, 5)
{
}
public override string DefaultName => "a pool of acid";
[Constructible]
public PoolOfAcid(TimeSpan duration, int minDamage, int maxDamage)
: base(0x122A)
{
Hue = 0x3F;
Movable = false;
m_MinDamage = minDamage;
m_MaxDamage = maxDamage;
m_Created = DateTime.UtcNow;
m_Duration = duration;
m_Timer = Timer.DelayCall(TimeSpan.Zero, TimeSpan.FromSeconds(1), OnTick);
}
public override void OnAfterDelete()
{
m_Timer?.Stop();
}
private void OnTick()
{
DateTime now = DateTime.UtcNow;
TimeSpan age = now - m_Created;
if (age > m_Duration)
{
Delete();
}
else
{
if (!m_Drying && age > m_Duration - age)
{
m_Drying = true;
ItemID = 0x122B;
}
List<Mobile> toDamage = new List<Mobile>();
foreach (Mobile m in GetMobilesInRange(0))
if (m.Alive && !m.IsDeadBondedPet && (!(m is BaseCreature bc) || bc.Controlled || bc.Summoned))
toDamage.Add(m);
for (int i = 0; i < toDamage.Count; i++)
Damage(toDamage[i]);
}
}
public override bool OnMoveOver(Mobile m)
{
Damage(m);
return true;
}
public void Damage(Mobile m)
{
m.Damage(Utility.RandomMinMax(m_MinDamage, m_MaxDamage));
}
public PoolOfAcid(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
// Don't serialize these
}
public override void Deserialize(IGenericReader reader)
{
}
}
}

View file

@ -0,0 +1,38 @@
using Server.Network;
namespace Server.Items
{
public class PowerCrystal : Item
{
[Constructible]
public PowerCrystal() : base(0x1F1C) => Weight = 1.0;
public PowerCrystal(Serial serial) : base(serial)
{
}
public override string DefaultName => "power crystal";
public override void OnDoubleClick(Mobile from)
{
if (!from.InRange(GetWorldLocation(), 3))
from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that.
else
from.SendAsciiMessage("This looks like part of a larger contraption.");
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,534 @@
using System;
using System.Collections.Generic;
using Server.Gumps;
using Server.Network;
namespace Server.Items
{
public class PowerGenerator : BaseAddon
{
[Constructible]
public PowerGenerator() : this(Utility.RandomMinMax(3, 6))
{
}
[Constructible]
public PowerGenerator(int sideLength)
{
AddGeneratorComponent(0x4FA1, 0, 0, 0);
AddGeneratorComponent(0x76, -1, 0, 0);
AddGeneratorComponent(0x75, 0, -1, 0);
AddGeneratorComponent(0x37F4, 0, 0, 13);
AddComponent(new ControlPanel(sideLength), 1, 0, -2);
}
public PowerGenerator(Serial serial) : base(serial)
{
}
public override bool ShareHue => false;
private void AddGeneratorComponent(int itemID, int x, int y, int z)
{
AddonComponent component = new AddonComponent(itemID);
component.Name = "a power generator";
component.Hue = 0x451;
AddComponent(component, x, y, z);
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadEncodedInt();
}
}
public class ControlPanel : AddonComponent
{
private static readonly TimeSpan m_UseTimeout = TimeSpan.FromMinutes(2.0);
private readonly HashSet<Mobile> m_DamageTable = new HashSet<Mobile>();
private DateTime m_LastUse;
private int m_SideLength;
private Mobile m_User;
public ControlPanel(int sideLength) : base(0xBDC)
{
Hue = 0x835;
SideLength = sideLength;
}
public ControlPanel(Serial serial) : base(serial)
{
}
[CommandProperty(AccessLevel.GameMaster)]
public int SideLength
{
get => m_SideLength;
set
{
if (value < 3)
value = 3;
else if (value > 6)
value = 6;
if (m_SideLength != value)
{
m_SideLength = value;
InitPath();
}
}
}
public Node[] Path { get; private set; }
public override string DefaultName => "a control panel";
public void InitPath()
{
// Depth-First Search algorithm
int totalNodes = SideLength * SideLength;
Node[] stack = new Node[totalNodes];
Node current = stack[0] = new Node(0, 0);
int stackSize = 1;
bool[,] visited = new bool[SideLength, SideLength];
visited[0, 0] = true;
while (true)
{
PathDirection[] choices = new PathDirection[4];
int count = 0;
if (current.X > 0 && !visited[current.X - 1, current.Y])
choices[count++] = PathDirection.Left;
if (current.Y > 0 && !visited[current.X, current.Y - 1])
choices[count++] = PathDirection.Up;
if (current.X < SideLength - 1 && !visited[current.X + 1, current.Y])
choices[count++] = PathDirection.Right;
if (current.Y < SideLength - 1 && !visited[current.X, current.Y + 1])
choices[count++] = PathDirection.Down;
if (count > 0)
{
PathDirection dir = choices[Utility.Random(count)];
current = dir switch
{
PathDirection.Left => new Node(current.X - 1, current.Y),
PathDirection.Up => new Node(current.X, current.Y - 1),
PathDirection.Right => new Node(current.X + 1, current.Y),
_ => new Node(current.X, current.Y + 1)
};
stack[stackSize++] = current;
if (current.X == SideLength - 1 && current.Y == SideLength - 1)
break;
visited[current.X, current.Y] = true;
}
else
{
current = stack[--stackSize - 1];
}
}
Path = new Node[stackSize];
for (int i = 0; i < stackSize; i++) Path[i] = stack[i];
if (m_User != null)
{
m_User.CloseGump<GameGump>();
m_User = null;
}
}
public override void OnDoubleClick(Mobile from)
{
if (!from.InRange(this, 3))
{
from.SendLocalizedMessage(500446); // That is too far away.
return;
}
if (m_User != null)
{
if (m_User == from)
return;
if (m_User.Deleted || m_User.Map != Map || !m_User.InRange(this, 3)
|| m_User.NetState == null || DateTime.UtcNow - m_LastUse >= m_UseTimeout)
{
m_User.CloseGump<GameGump>();
}
else
{
from.SendMessage("Someone is currently using the control panel.");
return;
}
}
m_User = from;
m_LastUse = DateTime.UtcNow;
from.SendGump(new GameGump(this, from, 0, false));
}
public void DoDamage(Mobile to)
{
to.Send(new UnicodeMessage(Serial, ItemID, MessageType.Regular, 0x3B2, 3, "", "",
"The generator shoots an arc of electricity at you!"));
to.BoltEffect(0);
to.LocalOverheadMessage(MessageType.Regular, 0xC9, true, "* Your body convulses from electric shock *");
to.NonlocalOverheadMessage(MessageType.Regular, 0xC9, true, $"* {to.Name} spasms from electric shock *");
AOS.Damage(to, to, 60, 0, 0, 0, 0, 100);
if (!to.Alive)
return;
if (!m_DamageTable.Contains(to))
{
to.Frozen = true;
DamageTimer timer = new DamageTimer(this, to);
m_DamageTable.Add(to);
timer.Start();
}
}
public void Solve(Mobile from)
{
Effects.PlaySound(Location, Map, 0x211);
Effects.PlaySound(Location, Map, 0x1F3);
Effects.SendLocationEffect(Location, Map, 0x36B0, 4, 4);
Effects.SendLocationEffect(new Point3D(X - 1, Y - 1, Z + 2), Map, 0x36B0, 4, 4);
Effects.SendLocationEffect(new Point3D(X - 2, Y - 1, Z + 2), Map, 0x36B0, 4, 4);
from.SendMessage("You scrounge some gems from the wreckage.");
for (int i = 0; i < SideLength; i++) from.AddToBackpack(new ArcaneGem());
from.AddToBackpack(new Diamond(SideLength));
Item ore = new ShadowIronOre(9);
ore.MoveToWorld(new Point3D(X - 1, Y, Z + 2), Map);
ore = new ShadowIronOre(14);
ore.MoveToWorld(new Point3D(X - 2, Y - 1, Z + 2), Map);
Delete();
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0); // version
writer.WriteEncodedInt(m_SideLength);
writer.WriteEncodedInt(Path.Length);
for (int i = 0; i < Path.Length; i++)
{
Node cur = Path[i];
writer.WriteEncodedInt(cur.X);
writer.WriteEncodedInt(cur.Y);
}
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadEncodedInt();
m_SideLength = reader.ReadEncodedInt();
Path = new Node[reader.ReadEncodedInt()];
for (int i = 0; i < Path.Length; i++) Path[i] = new Node(reader.ReadEncodedInt(), reader.ReadEncodedInt());
}
public struct Node
{
public int X { get; set; }
public int Y { get; set; }
public Node(int x, int y)
{
X = x;
Y = y;
}
}
private enum PathDirection
{
Left,
Up,
Right,
Down
}
private class GameGump : Gump
{
private readonly Mobile m_From;
private readonly ControlPanel m_Panel;
private readonly int m_Step;
public GameGump(ControlPanel panel, Mobile from, int step, bool hint) : base(5, 30)
{
m_Panel = panel;
m_From = from;
m_Step = step;
int sideLength = panel.SideLength;
AddBackground(50, 0, 530, 410, 0xA28);
AddImage(0, 0, 0x28C8);
AddImage(547, 0, 0x28C9);
AddBackground(95, 20, 442, 90, 0xA28);
AddHtml(229, 35, 300, 45, "GENERATOR CONTROL PANEL");
AddHtml(223, 60, 300, 70, "Use the Directional Controls to");
AddHtml(253, 75, 300, 85, "Close the Grid Circuit");
AddImage(140, 40, 0x28D3);
AddImage(420, 40, 0x28D3);
AddBackground(365, 120, 178, 210, 0x1400);
AddImage(365, 115, 0x28D4);
AddImage(365, 288, 0x28D4);
AddImage(414, 189, 0x589);
AddImage(435, 210, 0xA52);
AddButton(408, 222, 0x29EA, 0x29EC, 1); // Left
AddButton(448, 185, 0x29CC, 0x29CE, 2); // Up
AddButton(473, 222, 0x29D6, 0x29D8, 3); // Right
AddButton(448, 243, 0x29E0, 0x29E2, 4); // Down
AddBackground(90, 115, 30 + 40 * sideLength, 30 + 40 * sideLength, 0xA28);
AddBackground(100, 125, 10 + 40 * sideLength, 10 + 40 * sideLength, 0x1400);
for (int i = 0; i < sideLength; i++)
for (int j = 0; j < sideLength - 1; j++)
AddImage(120 + 40 * i, 162 + 40 * j, 0x13F9);
for (int i = 0; i < sideLength - 1; i++)
for (int j = 0; j < sideLength; j++)
AddImage(138 + 40 * i, 147 + 40 * j, 0x13FD);
Node[] path = panel.Path;
NodeHue[,] hues = new NodeHue[sideLength, sideLength];
for (int i = 0; i <= step; i++)
{
Node n = path[i];
hues[n.X, n.Y] = NodeHue.Blue;
}
Node lastNode = path[path.Length - 1];
hues[lastNode.X, lastNode.Y] = NodeHue.Red;
for (int i = 0; i < sideLength; i++)
for (int j = 0; j < sideLength; j++)
AddNode(110 + 40 * i, 135 + 40 * j, hues[i, j]);
Node curNode = path[step];
AddImage(118 + 40 * curNode.X, 143 + 40 * curNode.Y, 0x13A8);
if (hint)
{
Node nextNode = path[step + 1];
AddImage(119 + 40 * nextNode.X, 143 + 40 * nextNode.Y, 0x939);
}
if (from.Skills.Lockpicking.Value >= 65.0)
{
AddButton(365, 350, 0xFA6, 0xFA7, 5);
AddHtml(405, 345, 140, 40, "Attempt to Decipher the Circuit Path");
}
}
private void AddNode(int x, int y, NodeHue hue)
{
var id = hue switch
{
NodeHue.Gray => 0x25F8,
NodeHue.Blue => 0x868,
_ => 0x9A8
};
AddImage(x, y, id);
}
public override void OnResponse(NetState sender, RelayInfo info)
{
if (m_Panel.Deleted || info.ButtonID == 0 || !m_From.CheckAlive())
{
m_Panel.m_User = null;
return;
}
if (m_From.Map != m_Panel.Map || !m_From.InRange(m_Panel, 3))
{
m_From.SendLocalizedMessage(500446); // That is too far away.
m_Panel.m_User = null;
return;
}
Node nextNode = m_Panel.Path[m_Step + 1];
if (info.ButtonID == 5) // Attempt to Decipher
{
double lockpicking = m_From.Skills.Lockpicking.Value;
if (lockpicking < 65.0)
return;
m_From.PlaySound(0x241);
if (40.0 + Utility.RandomDouble() * 80.0 < lockpicking)
{
m_From.SendGump(new GameGump(m_Panel, m_From, m_Step, true));
m_Panel.m_LastUse = DateTime.UtcNow;
}
else
{
m_Panel.DoDamage(m_From);
m_Panel.m_User = null;
}
}
else
{
Node curNode = m_Panel.Path[m_Step];
int newX, newY;
switch (info.ButtonID)
{
case 1: // Left
newX = curNode.X - 1;
newY = curNode.Y;
break;
case 2: // Up
newX = curNode.X;
newY = curNode.Y - 1;
break;
case 3: // Right
newX = curNode.X + 1;
newY = curNode.Y;
break;
case 4: // Down
newX = curNode.X;
newY = curNode.Y + 1;
break;
default:
return;
}
if (nextNode.X == newX && nextNode.Y == newY)
{
if (m_Step + 1 == m_Panel.Path.Length - 1)
{
m_Panel.Solve(m_From);
m_Panel.m_User = null;
}
else
{
m_From.PlaySound(0x1F4);
m_From.SendGump(new GameGump(m_Panel, m_From, m_Step + 1, false));
m_Panel.m_LastUse = DateTime.UtcNow;
}
}
else
{
m_Panel.DoDamage(m_From);
m_Panel.m_User = null;
}
}
}
private enum NodeHue
{
Gray,
Blue,
Red
}
}
private class DamageTimer : Timer
{
private readonly ControlPanel m_Panel;
private int m_Step;
private readonly Mobile m_To;
public DamageTimer(ControlPanel panel, Mobile to) : base(TimeSpan.FromSeconds(5.0), TimeSpan.FromSeconds(5.0))
{
m_Panel = panel;
m_To = to;
m_Step = 0;
Priority = TimerPriority.TwoFiftyMS;
}
protected override void OnTick()
{
if (m_Panel.Deleted || m_To.Deleted || !m_To.Alive)
{
End();
return;
}
m_To.PlaySound(0x28);
m_To.LocalOverheadMessage(MessageType.Regular, 0xC9, true, "* Your body convulses from electric shock *");
m_To.NonlocalOverheadMessage(MessageType.Regular, 0xC9, true,
$"* {m_To.Name} spasms from electric shock *");
AOS.Damage(m_To, m_To, 20, 0, 0, 0, 0, 100);
if (++m_Step >= 3 || !m_To.Alive) End();
}
private void End()
{
m_Panel.m_DamageTable.Remove(m_To);
m_To.Frozen = false;
Stop();
}
}
}
}

View file

@ -0,0 +1,32 @@
namespace Server.Items
{
public class CrystallineFragments : Item
{
[Constructible]
public CrystallineFragments() : base(0x223B)
{
LootType = LootType.Blessed;
Hue = 0x47E;
}
public CrystallineFragments(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1073160; // Crystalline Fragments
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,30 @@
namespace Server.Items
{
public class IcyHeart : Item
{
[Constructible]
public IcyHeart() : base(0x24B)
{
}
public IcyHeart(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1073162; // Icy Heart
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,26 @@
namespace Server.Items
{
public class LuckyDagger : Item
{
[Constructible]
public LuckyDagger() : base(0xF52) => Hue = 0x8A5;
public LuckyDagger(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,30 @@
namespace Server.Items
{
public class ProtectorsEssence : Item
{
[Constructible]
public ProtectorsEssence() : base(0x23F)
{
}
public ProtectorsEssence(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1073159; // Protector's Essence
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,39 @@
namespace Server.Items
{
public class ShimmeringCrystals : Item
{
private static readonly int[] m_ItemIDs =
{
0x2206, 0x2207, 0x2208, 0x2209, 0x220A, 0x220B, 0x220C, 0x220D, 0x220E,
0x2210, 0x2211, 0x2212, 0x2213, 0x2214, 0x2215, 0x2216, 0x2217, 0x2218,
0x221A, 0x221B, 0x221C, 0x221D, 0x221E, 0x221F, 0x2220, 0x2221, 0x2222,
0x2224, 0x2225, 0x2226, 0x2227, 0x2228, 0x2229, 0x222A, 0x222B, 0x222C
};
[Constructible]
public ShimmeringCrystals() : base(Utility.RandomList(m_ItemIDs))
{
}
public ShimmeringCrystals(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1075095; // Shimmering Crystals
public override bool ForceShowProperties => true;
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,154 @@
using Server.Gumps;
using Server.Network;
namespace Server.Items
{
public abstract class PromotionalToken : Item
{
public abstract Item CreateItemFor(Mobile from);
public abstract TextDefinition ItemName { get; }
public abstract TextDefinition ItemReceiveMessage { get; }
public abstract TextDefinition ItemGumpName { get; }
public PromotionalToken() : base(0x2AAA)
{
LootType = LootType.Blessed;
Light = LightType.Circle300;
Weight = 5.0;
}
public PromotionalToken(Serial serial) : base(serial)
{
}
public override void GetProperties(ObjectPropertyList list)
{
base.GetProperties(list);
list.Add(1070998, ItemName.ToString()); // Use this to redeem<br>your ~1_PROMO~
}
public override void OnDoubleClick(Mobile from)
{
if (!IsChildOf(from.Backpack))
{
from.SendLocalizedMessage(1062334); // This item must be in your backpack to be used.
}
else
{
from.CloseGump<PromotionalTokenGump>();
from.SendGump(new PromotionalTokenGump(this));
}
}
public override void OnRemoved(IEntity parent)
{
Mobile m = null;
if (parent is Item item)
m = item.RootParent as Mobile;
else if (parent is Mobile mobile)
m = mobile;
m?.CloseGump<PromotionalTokenGump>();
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
public override int LabelNumber => 1070997; // A promotional token
private class PromotionalTokenGump : Gump
{
private readonly PromotionalToken m_Token;
public PromotionalTokenGump(PromotionalToken token) : base(10, 10)
{
m_Token = token;
AddPage(0);
AddBackground(0, 0, 240, 135, 0x2422);
AddHtmlLocalized(15, 15, 210, 75, 1070972, 0x0, true); // Click "OKAY" to redeem the following promotional item:
TextDefinition.AddHtmlText(this, 15, 60, 210, 75, m_Token.ItemGumpName, false, false);
AddButton(160, 95, 0xF7, 0xF8, 1); // Okay
AddButton(90, 95, 0xF2, 0xF1, 0); // Cancel
}
public override void OnResponse(NetState sender, RelayInfo info)
{
if (info.ButtonID != 1)
return;
Mobile from = sender.Mobile;
if (!m_Token.IsChildOf(from.Backpack))
{
from.SendLocalizedMessage(1062334); // This item must be in your backpack to be used.
}
else
{
Item i = m_Token.CreateItemFor(from);
if (i != null)
{
from.BankBox.AddItem(i);
TextDefinition.SendMessageTo(from, m_Token.ItemReceiveMessage);
m_Token.Delete();
}
}
}
}
}
public class SoulstoneFragmentToken : PromotionalToken
{
public override Item CreateItemFor(Mobile from)
{
if (from?.Account != null)
return new SoulstoneFragment(from.Account.ToString());
return null;
}
public override TextDefinition ItemGumpName => 1070999;// <center>Soulstone Fragment</center>
public override TextDefinition ItemName => 1071000;// soulstone fragment
public override TextDefinition ItemReceiveMessage => 1070976; // A soulstone fragment has been created in your bank box.
[Constructible]
public SoulstoneFragmentToken()
{
}
public SoulstoneFragmentToken(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,420 @@
using System;
using System.Collections.Generic;
using Server.Factions;
using Server.Gumps;
using Server.Mobiles;
using Server.Network;
using Server.Spells;
namespace Server.Items
{
public class PublicMoongate : Item
{
[Constructible]
public PublicMoongate() : base(0xF6C)
{
Movable = false;
Light = LightType.Circle300;
}
public PublicMoongate(Serial serial) : base(serial)
{
}
public override bool ForceShowProperties => ObjectPropertyList.Enabled;
public override bool HandlesOnMovement => true;
public override void OnDoubleClick(Mobile from)
{
if (!from.Player)
return;
if (from.InRange(GetWorldLocation(), 1))
UseGate(from);
else
from.SendLocalizedMessage(500446); // That is too far away.
}
public override bool OnMoveOver(Mobile m)
{
// Changed so criminals are not blocked by it.
if (m.Player)
UseGate(m);
return true;
}
public override void OnMovement(Mobile m, Point3D oldLocation)
{
if (m is PlayerMobile)
if (!Utility.InRange(m.Location, Location, 1) && Utility.InRange(oldLocation, Location, 1))
m.CloseGump<MoongateGump>();
}
public bool UseGate(Mobile m)
{
if (m.Criminal)
{
m.SendLocalizedMessage(1005561, "", 0x22); // Thou'rt a criminal and cannot escape so easily.
return false;
}
if (SpellHelper.CheckCombat(m))
{
m.SendLocalizedMessage(1005564, "", 0x22); // Wouldst thou flee during the heat of battle??
return false;
}
if (m.Spell != null)
{
m.SendLocalizedMessage(1049616); // You are too busy to do that at the moment.
return false;
}
m.CloseGump<MoongateGump>();
m.SendGump(new MoongateGump(m, this));
if (!m.Hidden || m.AccessLevel == AccessLevel.Player)
Effects.PlaySound(m.Location, m.Map, 0x20E);
return true;
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
public static void Initialize()
{
CommandSystem.Register("MoonGen", AccessLevel.Administrator, MoonGen_OnCommand);
}
[Usage("MoonGen")]
[Description("Generates public moongates. Removes all old moongates.")]
public static void MoonGen_OnCommand(CommandEventArgs e)
{
DeleteAll();
int count = 0;
count += MoonGen(PMList.Trammel);
count += MoonGen(PMList.Felucca);
count += MoonGen(PMList.Ilshenar);
count += MoonGen(PMList.Malas);
count += MoonGen(PMList.Tokuno);
World.Broadcast(0x35, true, "{0} moongates generated.", count);
}
private static void DeleteAll()
{
List<Item> list = new List<Item>();
foreach (Item item in World.Items.Values)
if (item is PublicMoongate)
list.Add(item);
foreach (Item item in list)
item.Delete();
if (list.Count > 0)
World.Broadcast(0x35, true, "{0} moongates removed.", list.Count);
}
private static int MoonGen(PMList list)
{
foreach (PMEntry entry in list.Entries)
{
Item item = new PublicMoongate();
item.MoveToWorld(entry.Location, list.Map);
if (entry.Number == 1060642) // Umbra
item.Hue = 0x497;
}
return list.Entries.Length;
}
}
public class PMEntry
{
public PMEntry(Point3D loc, int number)
{
Location = loc;
Number = number;
}
public Point3D Location { get; }
public int Number { get; }
}
public class PMList
{
public static readonly PMList Trammel =
new PMList(1012000, 1012012, Map.Trammel, new[]
{
new PMEntry(new Point3D(4467, 1283, 5), 1012003), // Moonglow
new PMEntry(new Point3D(1336, 1997, 5), 1012004), // Britain
new PMEntry(new Point3D(1499, 3771, 5), 1012005), // Jhelom
new PMEntry(new Point3D(771, 752, 5), 1012006), // Yew
new PMEntry(new Point3D(2701, 692, 5), 1012007), // Minoc
new PMEntry(new Point3D(1828, 2948, -20), 1012008), // Trinsic
new PMEntry(new Point3D(643, 2067, 5), 1012009), // Skara Brae
/* Dynamic Z for Magincia to support both old and new maps. */
new PMEntry(new Point3D(3563, 2139, Map.Trammel.GetAverageZ(3563, 2139)), 1012010), // (New) Magincia
new PMEntry(new Point3D(3450, 2677, 25), 1078098) // New Haven
});
public static readonly PMList Felucca =
new PMList(1012001, 1012013, Map.Felucca, new[]
{
new PMEntry(new Point3D(4467, 1283, 5), 1012003), // Moonglow
new PMEntry(new Point3D(1336, 1997, 5), 1012004), // Britain
new PMEntry(new Point3D(1499, 3771, 5), 1012005), // Jhelom
new PMEntry(new Point3D(771, 752, 5), 1012006), // Yew
new PMEntry(new Point3D(2701, 692, 5), 1012007), // Minoc
new PMEntry(new Point3D(1828, 2948, -20), 1012008), // Trinsic
new PMEntry(new Point3D(643, 2067, 5), 1012009), // Skara Brae
/* Dynamic Z for Magincia to support both old and new maps. */
new PMEntry(new Point3D(3563, 2139, Map.Felucca.GetAverageZ(3563, 2139)), 1012010), // (New) Magincia
new PMEntry(new Point3D(2711, 2234, 0), 1019001) // Buccaneer's Den
});
public static readonly PMList Ilshenar =
new PMList(1012002, 1012014, Map.Ilshenar, new[]
{
new PMEntry(new Point3D(1215, 467, -13), 1012015), // Compassion
new PMEntry(new Point3D(722, 1366, -60), 1012016), // Honesty
new PMEntry(new Point3D(744, 724, -28), 1012017), // Honor
new PMEntry(new Point3D(281, 1016, 0), 1012018), // Humility
new PMEntry(new Point3D(987, 1011, -32), 1012019), // Justice
new PMEntry(new Point3D(1174, 1286, -30), 1012020), // Sacrifice
new PMEntry(new Point3D(1532, 1340, -3), 1012021), // Spirituality
new PMEntry(new Point3D(528, 216, -45), 1012022), // Valor
new PMEntry(new Point3D(1721, 218, 96), 1019000) // Chaos
});
public static readonly PMList Malas =
new PMList(1060643, 1062039, Map.Malas, new[]
{
new PMEntry(new Point3D(1015, 527, -65), 1060641), // Luna
new PMEntry(new Point3D(1997, 1386, -85), 1060642) // Umbra
});
public static readonly PMList Tokuno =
new PMList(1063258, 1063415, Map.Tokuno, new[]
{
new PMEntry(new Point3D(1169, 998, 41), 1063412), // Isamu-Jima
new PMEntry(new Point3D(802, 1204, 25), 1063413), // Makoto-Jima
new PMEntry(new Point3D(270, 628, 15), 1063414) // Homare-Jima
});
public static readonly PMList[] UORLists = { Trammel, Felucca };
public static readonly PMList[] UORListsYoung = { Trammel };
public static readonly PMList[] LBRLists = { Trammel, Felucca, Ilshenar };
public static readonly PMList[] LBRListsYoung = { Trammel, Ilshenar };
public static readonly PMList[] AOSLists = { Trammel, Felucca, Ilshenar, Malas };
public static readonly PMList[] AOSListsYoung = { Trammel, Ilshenar, Malas };
public static readonly PMList[] SELists = { Trammel, Felucca, Ilshenar, Malas, Tokuno };
public static readonly PMList[] SEListsYoung = { Trammel, Ilshenar, Malas, Tokuno };
public static readonly PMList[] RedLists = { Felucca };
public static readonly PMList[] SigilLists = { Felucca };
public PMList(int number, int selNumber, Map map, PMEntry[] entries)
{
Number = number;
SelNumber = selNumber;
Map = map;
Entries = entries;
}
public int Number { get; }
public int SelNumber { get; }
public Map Map { get; }
public PMEntry[] Entries { get; }
}
public class MoongateGump : Gump
{
private readonly PMList[] m_Lists;
private readonly Mobile m_Mobile;
private readonly Item m_Moongate;
public MoongateGump(Mobile mobile, Item moongate) : base(100, 100)
{
m_Mobile = mobile;
m_Moongate = moongate;
PMList[] checkLists;
if (mobile.Player)
{
if (Sigil.ExistsOn(mobile))
{
checkLists = PMList.SigilLists;
}
else if (mobile.Kills >= 5)
{
checkLists = PMList.RedLists;
}
else
{
ClientFlags flags = mobile.NetState?.Flags ?? ClientFlags.None;
bool young = mobile is PlayerMobile playerMobile && playerMobile.Young;
if (Core.SE && (flags & ClientFlags.Tokuno) != 0)
checkLists = young ? PMList.SEListsYoung : PMList.SELists;
else if (Core.AOS && (flags & ClientFlags.Malas) != 0)
checkLists = young ? PMList.AOSListsYoung : PMList.AOSLists;
else if ((flags & ClientFlags.Ilshenar) != 0)
checkLists = young ? PMList.LBRListsYoung : PMList.LBRLists;
else
checkLists = young ? PMList.UORListsYoung : PMList.UORLists;
}
}
else
{
checkLists = PMList.SELists;
}
m_Lists = new PMList[checkLists.Length];
for (int i = 0; i < m_Lists.Length; ++i)
m_Lists[i] = checkLists[i];
for (int i = 0; i < m_Lists.Length; ++i)
if (m_Lists[i].Map == mobile.Map)
{
PMList temp = m_Lists[i];
m_Lists[i] = m_Lists[0];
m_Lists[0] = temp;
break;
}
AddPage(0);
AddBackground(0, 0, 380, 280, 5054);
AddButton(10, 210, 4005, 4007, 1);
AddHtmlLocalized(45, 210, 140, 25, 1011036); // OKAY
AddButton(10, 235, 4005, 4007, 0);
AddHtmlLocalized(45, 235, 140, 25, 1011012); // CANCEL
AddHtmlLocalized(5, 5, 200, 20, 1012011); // Pick your destination:
for (int i = 0; i < checkLists.Length; ++i)
{
AddButton(10, 35 + i * 25, 2117, 2118, 0, GumpButtonType.Page, Array.IndexOf(m_Lists, checkLists[i]) + 1);
AddHtmlLocalized(30, 35 + i * 25, 150, 20, checkLists[i].Number);
}
for (int i = 0; i < m_Lists.Length; ++i)
RenderPage(i, Array.IndexOf(checkLists, m_Lists[i]));
}
private void RenderPage(int index, int offset)
{
PMList list = m_Lists[index];
AddPage(index + 1);
AddButton(10, 35 + offset * 25, 2117, 2118, 0, GumpButtonType.Page, index + 1);
AddHtmlLocalized(30, 35 + offset * 25, 150, 20, list.SelNumber);
PMEntry[] entries = list.Entries;
for (int i = 0; i < entries.Length; ++i)
{
AddRadio(200, 35 + i * 25, 210, 211, false, index * 100 + i);
AddHtmlLocalized(225, 35 + i * 25, 150, 20, entries[i].Number);
}
}
public override void OnResponse(NetState state, RelayInfo info)
{
if (info.ButtonID == 0) // Cancel
return;
if (m_Mobile.Deleted || m_Moongate.Deleted || m_Mobile.Map == null)
return;
int[] switches = info.Switches;
if (switches.Length == 0)
return;
int switchID = switches[0];
int listIndex = switchID / 100;
int listEntry = switchID % 100;
if (listIndex < 0 || listIndex >= m_Lists.Length)
return;
PMList list = m_Lists[listIndex];
if (listEntry < 0 || listEntry >= list.Entries.Length)
return;
PMEntry entry = list.Entries[listEntry];
if (!m_Mobile.InRange(m_Moongate.GetWorldLocation(), 1) || m_Mobile.Map != m_Moongate.Map)
{
m_Mobile.SendLocalizedMessage(1019002); // You are too far away to use the gate.
}
else if (m_Mobile.Player && m_Mobile.Kills >= 5 && list.Map != Map.Felucca)
{
m_Mobile.SendLocalizedMessage(1019004); // You are not allowed to travel there.
}
else if (Sigil.ExistsOn(m_Mobile) && list.Map != Faction.Facet)
{
m_Mobile.SendLocalizedMessage(1019004); // You are not allowed to travel there.
}
else if (m_Mobile.Criminal)
{
m_Mobile.SendLocalizedMessage(1005561, "", 0x22); // Thou'rt a criminal and cannot escape so easily.
}
else if (SpellHelper.CheckCombat(m_Mobile))
{
m_Mobile.SendLocalizedMessage(1005564, "", 0x22); // Wouldst thou flee during the heat of battle??
}
else if (m_Mobile.Spell != null)
{
m_Mobile.SendLocalizedMessage(1049616); // You are too busy to do that at the moment.
}
else if (m_Mobile.Map == list.Map && m_Mobile.InRange(entry.Location, 1))
{
m_Mobile.SendLocalizedMessage(1019003); // You are already there.
}
else
{
BaseCreature.TeleportPets(m_Mobile, entry.Location, list.Map);
m_Mobile.Combatant = null;
m_Mobile.Warmode = false;
m_Mobile.Hidden = true;
m_Mobile.MoveToWorld(entry.Location, list.Map);
Effects.PlaySound(entry.Location, list.Map, 0x1FE);
}
}
}
}

View file

@ -0,0 +1,611 @@
namespace Server.Items
{
public class Rope : Item
{
[Constructible]
public Rope(int amount = 1) : base(0x14F8)
{
Stackable = true;
Weight = 1.0;
Amount = amount;
}
public Rope(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
public class IronWire : Item
{
[Constructible]
public IronWire(int amount = 1) : base(0x1876)
{
Stackable = true;
Weight = 5.0;
Amount = amount;
}
public IronWire(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
if (version < 1 && Weight == 2.0)
Weight = 5.0;
}
}
public class SilverWire : Item
{
[Constructible]
public SilverWire(int amount = 1) : base(0x1877)
{
Stackable = true;
Weight = 5.0;
Amount = amount;
}
public SilverWire(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(1);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
if (version < 1 && Weight == 2.0)
Weight = 5.0;
}
}
public class GoldWire : Item
{
[Constructible]
public GoldWire(int amount = 1) : base(0x1878)
{
Stackable = true;
Weight = 5.0;
Amount = amount;
}
public GoldWire(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(1);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
if (version < 1 && Weight == 2.0)
Weight = 5.0;
}
}
public class CopperWire : Item
{
[Constructible]
public CopperWire(int amount = 1) : base(0x1879)
{
Stackable = true;
Weight = 5.0;
Amount = amount;
}
public CopperWire(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(1);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
if (version < 1 && Weight == 2.0)
Weight = 5.0;
}
}
public class WhiteDriedFlowers : Item
{
[Constructible]
public WhiteDriedFlowers(int amount = 1) : base(0xC3C)
{
Stackable = true;
Weight = 1.0;
Amount = amount;
}
public WhiteDriedFlowers(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
public class GreenDriedFlowers : Item
{
[Constructible]
public GreenDriedFlowers(int amount = 1) : base(0xC3E)
{
Stackable = true;
Weight = 1.0;
Amount = amount;
}
public GreenDriedFlowers(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
public class DriedOnions : Item
{
[Constructible]
public DriedOnions(int amount = 1) : base(0xC40)
{
Stackable = true;
Weight = 1.0;
Amount = amount;
}
public DriedOnions(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
public class DriedHerbs : Item
{
[Constructible]
public DriedHerbs(int amount = 1) : base(0xC42)
{
Stackable = true;
Weight = 1.0;
Amount = amount;
}
public DriedHerbs(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
public class HorseShoes : Item
{
[Constructible]
public HorseShoes() : base(0xFB6) => Weight = 3.0;
public HorseShoes(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
public class ForgedMetal : Item
{
[Constructible]
public ForgedMetal() : base(0xFB8) => Weight = 5.0;
public ForgedMetal(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
public class Whip : Item
{
[Constructible]
public Whip() : base(0x166E) => Weight = 1.0;
public Whip(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
public class PaintsAndBrush : Item
{
[Constructible]
public PaintsAndBrush() : base(0xFC1) => Weight = 1.0;
public PaintsAndBrush(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
public class PenAndInk : Item
{
[Constructible]
public PenAndInk() : base(0xFBF) => Weight = 1.0;
public PenAndInk(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
public class ChiselsNorth : Item
{
[Constructible]
public ChiselsNorth() : base(0x1026) => Weight = 1.0;
public ChiselsNorth(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadEncodedInt();
}
}
public class ChiselsWest : Item
{
[Constructible]
public ChiselsWest() : base(0x1027) => Weight = 1.0;
public ChiselsWest(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadEncodedInt();
}
}
public class DirtyPan : Item
{
[Constructible]
public DirtyPan() : base(0x9E8) => Weight = 1.0;
public DirtyPan(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadEncodedInt();
}
}
public class DirtySmallRoundPot : Item
{
[Constructible]
public DirtySmallRoundPot() : base(0x9E7) => Weight = 1.0;
public DirtySmallRoundPot(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadEncodedInt();
}
}
public class DirtyPot : Item
{
[Constructible]
public DirtyPot() : base(0x9E6) => Weight = 1.0;
public DirtyPot(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadEncodedInt();
}
}
public class DirtyRoundPot : Item
{
[Constructible]
public DirtyRoundPot() : base(0x9DF) => Weight = 1.0;
public DirtyRoundPot(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadEncodedInt();
}
}
public class DirtyFrypan : Item
{
[Constructible]
public DirtyFrypan() : base(0x9DE) => Weight = 1.0;
public DirtyFrypan(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadEncodedInt();
}
}
public class DirtySmallPot : Item
{
[Constructible]
public DirtySmallPot() : base(0x9DD) => Weight = 1.0;
public DirtySmallPot(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadEncodedInt();
}
}
public class DirtyKettle : Item
{
[Constructible]
public DirtyKettle() : base(0x9DC) => Weight = 1.0;
public DirtyKettle(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadEncodedInt();
}
}
}

View file

@ -0,0 +1,81 @@
using Server.Targeting;
namespace Server.Items
{
public class Scales : Item
{
[Constructible]
public Scales() : base(0x1852) => Weight = 4.0;
public Scales(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
public override void OnDoubleClick(Mobile from)
{
from.SendLocalizedMessage(502431); // What would you like to weigh?
from.Target = new InternalTarget(this);
}
private class InternalTarget : Target
{
private readonly Scales m_Item;
public InternalTarget(Scales item) : base(1, false, TargetFlags.None) => m_Item = item;
protected override void OnTarget(Mobile from, object targeted)
{
string message;
if (targeted == m_Item)
{
message = "It cannot weight itself.";
}
else if (targeted is Item item)
{
IEntity root = item.RootParent;
if ((root != null && root != from) || item.Parent == from)
{
message = "You decide that item's current location is too awkward to get an accurate result.";
}
else if (item.Movable)
{
message = item.Amount > 1 ? "You place one item on the scale. " : "You place that item on the scale. ";
double weight = item.Weight;
if (weight <= 0.0)
message += "It is lighter than a feather.";
else
message += $"It weighs {weight} stones.";
}
else
{
message = "You cannot weigh that object.";
}
}
else
{
message = "You cannot weigh that object.";
}
from.SendMessage(message);
}
}
}
}

View file

@ -0,0 +1,103 @@
using Server.Multis;
namespace Server.Items
{
public class SerpentPillar : Item
{
[Constructible]
public SerpentPillar() : this(null, new Rectangle2D(), false)
{
}
public SerpentPillar(string word, Rectangle2D destination, bool active = true) : base(0x233F)
{
Movable = false;
Active = active;
Word = word;
Destination = destination;
}
public SerpentPillar(Serial serial) : base(serial)
{
}
[CommandProperty(AccessLevel.GameMaster)]
public bool Active { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public string Word { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public Rectangle2D Destination { get; set; }
public override bool HandlesOnSpeech => true;
public override void OnSpeech(SpeechEventArgs e)
{
Mobile from = e.Mobile;
if (!e.Handled && from.InRange(this, 10) && e.Speech.ToLower() == Word)
{
BaseBoat boat = BaseBoat.FindBoatAt(from, from.Map);
if (boat == null)
return;
if (!Active)
{
boat.TillerMan
?.Say(502507); // Ar, Legend has it that these pillars are inactive! No man knows how it might be undone!
return;
}
Map map = from.Map;
for (int i = 0; i < 5; i++) // Try 5 times
{
int x = Utility.Random(Destination.X, Destination.Width);
int y = Utility.Random(Destination.Y, Destination.Height);
int z = map.GetAverageZ(x, y);
Point3D dest = new Point3D(x, y, z);
if (boat.CanFit(dest, map, boat.ItemID))
{
int xOffset = x - boat.X;
int yOffset = y - boat.Y;
int zOffset = z - boat.Z;
boat.Teleport(xOffset, yOffset, zOffset);
return;
}
}
boat.TillerMan?.Say(502508); // Ar, I refuse to take that matey through here!
}
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0); // version
writer.Write(Active);
writer.Write(Word);
writer.Write(Destination);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadEncodedInt();
Active = reader.ReadBool();
Word = reader.ReadString();
Destination = reader.ReadRect2D();
}
}
}

View file

@ -0,0 +1,162 @@
using Server.Gumps;
using Server.Network;
namespace Server.Items
{
public class SpecialBeardDye : Item
{
[Constructible]
public SpecialBeardDye() : base(0xE26)
{
Weight = 1.0;
LootType = LootType.Newbied;
}
public SpecialBeardDye(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1041087; // Special Beard Dye
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
public override void OnDoubleClick(Mobile from)
{
if (from.InRange(GetWorldLocation(), 1))
{
from.CloseGump<SpecialBeardDyeGump>();
from.SendGump(new SpecialBeardDyeGump(this));
}
else
{
from.LocalOverheadMessage(MessageType.Regular, 906, 1019045); // I can't reach that.
}
}
}
public class SpecialBeardDyeGump : Gump
{
private static readonly SpecialBeardDyeEntry[] m_Entries =
{
new SpecialBeardDyeEntry("*****", 12, 10),
new SpecialBeardDyeEntry("*****", 32, 5),
new SpecialBeardDyeEntry("*****", 38, 8),
new SpecialBeardDyeEntry("*****", 54, 3),
new SpecialBeardDyeEntry("*****", 62, 10),
new SpecialBeardDyeEntry("*****", 81, 2),
new SpecialBeardDyeEntry("*****", 89, 2),
new SpecialBeardDyeEntry("*****", 1153, 2)
};
private readonly SpecialBeardDye m_SpecialBeardDye;
public SpecialBeardDyeGump(SpecialBeardDye dye) : base(0, 0)
{
m_SpecialBeardDye = dye;
AddPage(0);
AddBackground(150, 60, 350, 358, 2600);
AddBackground(170, 104, 110, 270, 5100);
AddHtmlLocalized(230, 75, 200, 20, 1011013); // Hair Color Selection Menu
AddHtmlLocalized(235, 380, 300, 20, 1013007); // Dye my beard this color!
AddButton(200, 380, 0xFA5, 0xFA7, 1); // DYE HAIR
for (int i = 0; i < m_Entries.Length; ++i)
{
AddLabel(180, 109 + i * 22, m_Entries[i].HueStart - 1, m_Entries[i].Name);
AddButton(257, 110 + i * 22, 5224, 5224, 0, GumpButtonType.Page, i + 1);
}
for (int i = 0; i < m_Entries.Length; ++i)
{
SpecialBeardDyeEntry e = m_Entries[i];
AddPage(i + 1);
for (int j = 0; j < e.HueCount; ++j)
{
AddLabel(328 + j / 16 * 80, 102 + j % 16 * 17, e.HueStart + j - 1, "*****");
AddRadio(310 + j / 16 * 80, 102 + j % 16 * 17, 210, 211, false, i * 100 + j);
}
}
}
public override void OnResponse(NetState from, RelayInfo info)
{
if (m_SpecialBeardDye.Deleted)
return;
Mobile m = from.Mobile;
int[] switches = info.Switches;
if (!m_SpecialBeardDye.IsChildOf(m.Backpack))
{
m.SendLocalizedMessage(1042010); // You must have the objectin your backpack to use it.
return;
}
if (info.ButtonID != 0 && switches.Length > 0)
{
if (m.FacialHairItemID == 0)
{
m.SendLocalizedMessage(502623); // You have no hair to dye and cannot use this
}
else
{
// To prevent this from being exploited, the hue is abstracted into an internal list
int entryIndex = switches[0] / 100;
int hueOffset = switches[0] % 100;
if (entryIndex >= 0 && entryIndex < m_Entries.Length)
{
SpecialBeardDyeEntry e = m_Entries[entryIndex];
if (hueOffset >= 0 && hueOffset < e.HueCount)
{
int hue = e.HueStart + hueOffset;
m.FacialHairHue = hue;
m.SendLocalizedMessage(501199); // You dye your hair
m_SpecialBeardDye.Delete();
m.PlaySound(0x4E);
}
}
}
}
else
{
m.SendLocalizedMessage(501200); // You decide not to dye your hair
}
}
private class SpecialBeardDyeEntry
{
public SpecialBeardDyeEntry(string name, int hueStart, int hueCount)
{
Name = name;
HueStart = hueStart;
HueCount = hueCount;
}
public string Name { get; }
public int HueStart { get; }
public int HueCount { get; }
}
}
}

View file

@ -0,0 +1,163 @@
using Server.Gumps;
using Server.Network;
namespace Server.Items
{
public class SpecialHairDye : Item
{
[Constructible]
public SpecialHairDye() : base(0xE26)
{
Weight = 1.0;
LootType = LootType.Newbied;
}
public SpecialHairDye(Serial serial) : base(serial)
{
}
public override string DefaultName => "Special Hair Dye";
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
public override void OnDoubleClick(Mobile from)
{
if (from.InRange(GetWorldLocation(), 1))
{
from.CloseGump<SpecialHairDyeGump>();
from.SendGump(new SpecialHairDyeGump(this));
}
else
{
from.LocalOverheadMessage(MessageType.Regular, 906, 1019045); // I can't reach that.
}
}
}
public class SpecialHairDyeGump : Gump
{
private static readonly SpecialHairDyeEntry[] m_Entries =
{
new SpecialHairDyeEntry("*****", 12, 10),
new SpecialHairDyeEntry("*****", 32, 5),
new SpecialHairDyeEntry("*****", 38, 8),
new SpecialHairDyeEntry("*****", 54, 3),
new SpecialHairDyeEntry("*****", 62, 10),
new SpecialHairDyeEntry("*****", 81, 2),
new SpecialHairDyeEntry("*****", 89, 2),
new SpecialHairDyeEntry("*****", 1153, 2)
};
private readonly SpecialHairDye m_SpecialHairDye;
public SpecialHairDyeGump(SpecialHairDye dye) : base(0, 0)
{
m_SpecialHairDye = dye;
AddPage(0);
AddBackground(150, 60, 350, 358, 2600);
AddBackground(170, 104, 110, 270, 5100);
AddHtmlLocalized(230, 75, 200, 20, 1011013); // Hair Color Selection Menu
AddHtmlLocalized(235, 380, 300, 20, 1011014); // Dye my hair this color!
AddButton(200, 380, 0xFA5, 0xFA7, 1); // DYE HAIR
for (int i = 0; i < m_Entries.Length; ++i)
{
AddLabel(180, 109 + i * 22, m_Entries[i].HueStart - 1, m_Entries[i].Name);
AddButton(257, 110 + i * 22, 5224, 5224, 0, GumpButtonType.Page, i + 1);
}
for (int i = 0; i < m_Entries.Length; ++i)
{
SpecialHairDyeEntry e = m_Entries[i];
AddPage(i + 1);
for (int j = 0; j < e.HueCount; ++j)
{
AddLabel(328 + j / 16 * 80, 102 + j % 16 * 17, e.HueStart + j - 1, "*****");
AddRadio(310 + j / 16 * 80, 102 + j % 16 * 17, 210, 211, false, i * 100 + j);
}
}
}
public override void OnResponse(NetState from, RelayInfo info)
{
if (m_SpecialHairDye.Deleted)
return;
Mobile m = from.Mobile;
int[] switches = info.Switches;
if (!m_SpecialHairDye.IsChildOf(m.Backpack))
{
m.SendLocalizedMessage(1042010); // You must have the objectin your backpack to use it.
return;
}
if (info.ButtonID != 0 && switches.Length > 0)
{
if (m.HairItemID == 0)
{
m.SendLocalizedMessage(502623); // You have no hair to dye and cannot use this
}
else
{
// To prevent this from being exploited, the hue is abstracted into an internal list
int entryIndex = switches[0] / 100;
int hueOffset = switches[0] % 100;
if (entryIndex >= 0 && entryIndex < m_Entries.Length)
{
SpecialHairDyeEntry e = m_Entries[entryIndex];
if (hueOffset >= 0 && hueOffset < e.HueCount)
{
m_SpecialHairDye.Delete();
int hue = e.HueStart + hueOffset;
m.HairHue = hue;
m.SendLocalizedMessage(501199); // You dye your hair
m.PlaySound(0x4E);
}
}
}
}
else
{
m.SendLocalizedMessage(501200); // You decide not to dye your hair
}
}
private class SpecialHairDyeEntry
{
public SpecialHairDyeEntry(string name, int hueStart, int hueCount)
{
Name = name;
HueStart = hueStart;
HueCount = hueCount;
}
public string Name { get; }
public int HueStart { get; }
public int HueCount { get; }
}
}
}

View file

@ -0,0 +1,90 @@
namespace Server.Items
{
public class Static : Item
{
public Static() : base(0x80) => Movable = false;
[Constructible]
public Static(int itemID) : base(itemID) => Movable = false;
[Constructible]
public Static(int itemID, int count) : this(Utility.Random(itemID, count))
{
}
public Static(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(1); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
if (version == 0 && Weight == 0)
Weight = -1;
}
}
public class LocalizedStatic : Static
{
private int m_LabelNumber;
[Constructible]
public LocalizedStatic(int itemID) : this(itemID, itemID < 0x4000 ? 1020000 + itemID : 1078872 + itemID)
{
}
[Constructible]
public LocalizedStatic(int itemID, int labelNumber) : base(itemID) => m_LabelNumber = labelNumber;
public LocalizedStatic(Serial serial) : base(serial)
{
}
[CommandProperty(AccessLevel.GameMaster)]
public int Number
{
get => m_LabelNumber;
set
{
m_LabelNumber = value;
InvalidateProperties();
}
}
public override int LabelNumber => m_LabelNumber;
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write((byte)0); // version
writer.WriteEncodedInt(m_LabelNumber);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadByte();
switch (version)
{
case 0:
{
m_LabelNumber = reader.ReadEncodedInt();
break;
}
}
}
}
}

View file

@ -0,0 +1,32 @@
namespace Server.Items
{
public class SwarmOfFlies : Item
{
[Constructible]
public SwarmOfFlies() : base(0x91B)
{
Hue = 1;
Movable = false;
}
public SwarmOfFlies(Serial serial) : base(serial)
{
}
public override string DefaultName => "a swarm of flies";
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,28 @@
namespace Server.Items
{
public class DragonFlameSectBadge : Item
{
[Constructible]
public DragonFlameSectBadge() : base(0x23E) => LootType = LootType.Blessed;
public DragonFlameSectBadge(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1073141; // A Dragon Flame Sect Badge
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,28 @@
namespace Server.Items
{
public class OrdersFromMinax : Item
{
[Constructible]
public OrdersFromMinax() : base(0x2279) => LootType = LootType.Blessed;
public OrdersFromMinax(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1074639; // Orders from Minax
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,28 @@
namespace Server.Items
{
public class SerpentFangSectBadge : Item
{
[Constructible]
public SerpentFangSectBadge() : base(0x23C) => LootType = LootType.Blessed;
public SerpentFangSectBadge(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1073139; // A Serpent Fang Sect Badge
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,28 @@
namespace Server.Items
{
public class TigerClawSectBadge : Item
{
[Constructible]
public TigerClawSectBadge() : base(0x23D) => LootType = LootType.Blessed;
public TigerClawSectBadge(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1073140; // A Tiger Claw Sect Badge
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,30 @@
namespace Server.Items
{
public class TravestysCollectionOfShells : Item
{
[Constructible]
public TravestysCollectionOfShells() : base(0xFD3)
{
}
public TravestysCollectionOfShells(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1072090; // Travesty's Collection of Shells
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,30 @@
namespace Server.Items
{
public class TravestysFineTeakwoodTray : Item
{
[Constructible]
public TravestysFineTeakwoodTray() : base(Utility.Random(0x991, 2))
{
}
public TravestysFineTeakwoodTray(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1075094; // Travesty's Fine Teakwood Tray
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,30 @@
namespace Server.Items
{
public class TravestysSushiPreparations : Item
{
[Constructible]
public TravestysSushiPreparations() : base(Utility.Random(0x1E15, 2))
{
}
public TravestysSushiPreparations(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1075093; // Travesty's Sushi Preparations
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,147 @@
using System;
using System.Collections.Generic;
using Server.Multis;
using Server.Network;
namespace Server.Items
{
public class TrashBarrel : Container, IChoppable
{
private Timer m_Timer;
[Constructible]
public TrashBarrel() : base(0xE77)
{
Hue = 0x3B2;
Movable = false;
}
public TrashBarrel(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1041064; // a trash barrel
public override int DefaultMaxWeight => 0; // A value of 0 signals unlimited weight
public override bool IsDecoContainer => false;
public void OnChop(Mobile from)
{
BaseHouse house = BaseHouse.FindHouseAt(from);
if (house?.IsCoOwner(from) == true)
{
Effects.PlaySound(Location, Map, 0x3B3);
from.SendLocalizedMessage(500461); // You destroy the item.
Destroy();
}
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
if (Items.Count > 0)
{
m_Timer = new EmptyTimer(this);
m_Timer.Start();
}
}
public override bool OnDragDrop(Mobile from, Item dropped)
{
if (!base.OnDragDrop(from, dropped))
return false;
if (TotalItems >= 50)
{
Empty(501478); // The trash is full! Emptying!
}
else
{
SendLocalizedMessageTo(from, 1010442); // The item will be deleted in three minutes
if (m_Timer != null)
m_Timer.Stop();
else
m_Timer = new EmptyTimer(this);
m_Timer.Start();
}
return true;
}
public override bool OnDragDropInto(Mobile from, Item item, Point3D p)
{
if (!base.OnDragDropInto(from, item, p))
return false;
if (TotalItems >= 50)
{
Empty(501478); // The trash is full! Emptying!
}
else
{
SendLocalizedMessageTo(from, 1010442); // The item will be deleted in three minutes
if (m_Timer != null)
m_Timer.Stop();
else
m_Timer = new EmptyTimer(this);
m_Timer.Start();
}
return true;
}
public void Empty(int message)
{
List<Item> items = Items;
if (items.Count > 0)
{
PublicOverheadMessage(MessageType.Regular, 0x3B2, message, "");
for (int i = items.Count - 1; i >= 0; --i)
{
if (i >= items.Count)
continue;
items[i].Delete();
}
}
m_Timer?.Stop();
m_Timer = null;
}
private class EmptyTimer : Timer
{
private readonly TrashBarrel m_Barrel;
public EmptyTimer(TrashBarrel barrel) : base(TimeSpan.FromMinutes(3.0))
{
m_Barrel = barrel;
Priority = TimerPriority.FiveSeconds;
}
protected override void OnTick()
{
m_Barrel.Empty(501479); // Emptying the trashcan!
}
}
}
}

View file

@ -0,0 +1,55 @@
using Server.Network;
namespace Server.Items
{
[Flippable(0xE41, 0xE40)]
public class TrashChest : Container
{
[Constructible]
public TrashChest() : base(0xE41) => Movable = false;
public TrashChest(Serial serial) : base(serial)
{
}
public override int DefaultMaxWeight => 0; // A value of 0 signals unlimited weight
public override bool IsDecoContainer => false;
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
public override bool OnDragDrop(Mobile from, Item dropped)
{
if (!base.OnDragDrop(from, dropped))
return false;
PublicOverheadMessage(MessageType.Regular, 0x3B2, Utility.Random(1042891, 8));
dropped.Delete();
return true;
}
public override bool OnDragDropInto(Mobile from, Item item, Point3D p)
{
if (!base.OnDragDropInto(from, item, p))
return false;
PublicOverheadMessage(MessageType.Regular, 0x3B2, Utility.Random(1042891, 8));
item.Delete();
return true;
}
}
}

View file

@ -0,0 +1,37 @@
namespace Server.Items
{
public class TribalBerry : Item
{
[Constructible]
public TribalBerry(int amount = 1) : base(0x9D0)
{
Weight = 1.0;
Stackable = true;
Amount = amount;
Hue = 6;
}
public TribalBerry(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1040001; // tribal berry
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
if (Hue == 4)
Hue = 6;
}
}
}

View file

@ -0,0 +1,89 @@
using System;
using Server.Factions;
using Server.Mobiles;
using Server.Spells;
using Server.Spells.Fifth;
using Server.Spells.Ninjitsu;
using Server.Spells.Seventh;
namespace Server.Items
{
public class TribalPaint : Item
{
[Constructible]
public TribalPaint() : base(0x9EC)
{
Hue = 2101;
Weight = 2.0;
Stackable = Core.ML;
}
public TribalPaint(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1040000; // savage kin paint
public override void OnDoubleClick(Mobile from)
{
if (IsChildOf(from.Backpack))
{
if (Sigil.ExistsOn(from))
{
from.SendLocalizedMessage(1010465); // You cannot disguise yourself while holding a sigil.
}
else if (!from.CanBeginAction<IncognitoSpell>())
{
from.SendLocalizedMessage(501698); // You cannot disguise yourself while incognitoed.
}
else if (!from.CanBeginAction<PolymorphSpell>())
{
from.SendLocalizedMessage(501699); // You cannot disguise yourself while polymorphed.
}
else if (TransformationSpellHelper.UnderTransformation(from))
{
from.SendLocalizedMessage(501699); // You cannot disguise yourself while polymorphed.
}
else if (AnimalForm.UnderTransformation(from))
{
from.SendLocalizedMessage(1061634); // You cannot disguise yourself while in that form.
}
else if (from.IsBodyMod || from.FindItemOnLayer(Layer.Helm) is OrcishKinMask)
{
from.SendLocalizedMessage(501605); // You are already disguised.
}
else
{
from.BodyMod = from.Female ? 184 : 183;
from.HueMod = 0;
if (from is PlayerMobile mobile)
mobile.SavagePaintExpiration = TimeSpan.FromDays(7.0);
from.SendLocalizedMessage(
1042537); // You now bear the markings of the savage tribe. Your body paint will last about a week or you can remove it with an oil cloth.
Consume();
}
}
else
{
from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it.
}
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,31 @@
namespace Server.Items
{
[Flippable(0x315C, 0x315D)]
public class HornOfTheDreadhorn : Item
{
[Constructible]
public HornOfTheDreadhorn() : base(0x315C)
{
}
public HornOfTheDreadhorn(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1072089; // Horn of the Dread
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,31 @@
namespace Server.Items
{
[Flippable(0x3156, 0x3157)]
public class MangledHeadOfDreadhorn : Item
{
[Constructible]
public MangledHeadOfDreadhorn() : base(0x3156)
{
}
public MangledHeadOfDreadhorn(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1072088; // The Mangled Head of Dread Horn
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,31 @@
namespace Server.Items
{
public class TaintedMushroom : Item
{
[Constructible]
public TaintedMushroom() : base(Utility.RandomMinMax(0x222E, 0x2231))
{
}
public TaintedMushroom(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1075088; // Dread Horn Tainted Mushroom
public override bool ForceShowProperties => true;
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,111 @@
using System;
using Server.Mobiles;
namespace Server.Items
{
public class UnholyBone : Item, ICarvable
{
private SpawnTimer m_Timer;
[Constructible]
public UnholyBone() : base(0xF7E)
{
Movable = false;
Hue = 0x497;
m_Timer = new SpawnTimer(this);
m_Timer.Start();
}
public UnholyBone(Serial serial) : base(serial)
{
}
public override string DefaultName => "unholy bone";
public void Carve(Mobile from, Item item)
{
Effects.PlaySound(GetWorldLocation(), Map, 0x48F);
Effects.SendLocationEffect(GetWorldLocation(), Map, 0x3728, 10, 10, 0, 0);
if (Utility.RandomDouble() < 0.3)
{
if (ItemID == 0xF7E)
from.SendMessage("You destroy the bone.");
else
from.SendMessage("You destroy the bone pile.");
Gold gold = new Gold(25, 100);
gold.MoveToWorld(GetWorldLocation(), Map);
Delete();
m_Timer.Stop();
}
else
{
if (ItemID == 0xF7E)
from.SendMessage("You damage the bone.");
else
from.SendMessage("You damage the bone pile.");
}
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
m_Timer = new SpawnTimer(this);
m_Timer.Start();
}
private class SpawnTimer : Timer
{
private readonly Item m_Item;
public SpawnTimer(Item item) : base(TimeSpan.FromSeconds(Utility.RandomMinMax(5, 10)))
{
Priority = TimerPriority.FiftyMS;
m_Item = item;
}
protected override void OnTick()
{
if (m_Item.Deleted)
return;
var spawn = Utility.Random(12) switch
{
0 => (Mobile)new Skeleton(),
1 => new Zombie(),
2 => new Wraith(),
3 => new Spectre(),
4 => new Ghoul(),
5 => new Mummy(),
6 => new Bogle(),
7 => new RottingCorpse(),
8 => new BoneKnight(),
9 => new SkeletalKnight(),
10 => new Lich(),
11 => new LichLord(),
_ => new Skeleton()
};
spawn.MoveToWorld(m_Item.Location, m_Item.Map);
m_Item.Delete();
}
}
}
}

View file

@ -0,0 +1,210 @@
using System;
using System.Collections.Generic;
using Server.Network;
namespace Server.Items
{
public class WarningItem : Item
{
private bool m_Broadcasting;
private DateTime m_LastBroadcast;
private int m_Range;
[Constructible]
public WarningItem(int itemID, int range, int warning) : base(itemID)
{
if (range > 18)
range = 18;
Movable = false;
WarningNumber = warning;
m_Range = range;
}
[Constructible]
public WarningItem(int itemID, int range, string warning) : base(itemID)
{
if (range > 18)
range = 18;
Movable = false;
WarningString = warning;
m_Range = range;
}
public WarningItem(Serial serial) : base(serial)
{
}
[CommandProperty(AccessLevel.GameMaster)]
public string WarningString { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public int WarningNumber { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public int Range
{
get => m_Range;
set
{
if (value > 18) value = 18;
m_Range = value;
}
}
[CommandProperty(AccessLevel.GameMaster)]
public TimeSpan ResetDelay { get; set; }
public virtual bool OnlyToTriggerer => false;
public virtual int NeighborRange => 5;
public override bool HandlesOnMovement => true;
public virtual void SendMessage(Mobile triggerer, bool onlyToTriggerer, string messageString, int messageNumber)
{
if (onlyToTriggerer)
{
if (messageString != null)
triggerer.SendMessage(messageString);
else
triggerer.SendLocalizedMessage(messageNumber);
}
else
{
if (messageString != null)
PublicOverheadMessage(MessageType.Regular, 0x3B2, false, messageString);
else
PublicOverheadMessage(MessageType.Regular, 0x3B2, messageNumber);
}
}
public virtual void Broadcast(Mobile triggerer)
{
if (m_Broadcasting || DateTime.UtcNow < m_LastBroadcast + ResetDelay)
return;
m_LastBroadcast = DateTime.UtcNow;
m_Broadcasting = true;
SendMessage(triggerer, OnlyToTriggerer, WarningString, WarningNumber);
if (NeighborRange >= 0)
{
List<WarningItem> list = new List<WarningItem>();
foreach (Item item in GetItemsInRange(NeighborRange))
if (item != this && item is WarningItem warningItem)
list.Add(warningItem);
for (int i = 0; i < list.Count; i++)
list[i].Broadcast(triggerer);
}
Timer.DelayCall(TimeSpan.Zero, InternalCallback);
}
private void InternalCallback()
{
m_Broadcasting = false;
}
public override void OnMovement(Mobile m, Point3D oldLocation)
{
if (m.Player && Utility.InRange(m.Location, Location, m_Range) &&
!Utility.InRange(oldLocation, Location, m_Range))
Broadcast(m);
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0);
writer.Write(WarningString);
writer.Write(WarningNumber);
writer.Write(m_Range);
writer.Write(ResetDelay);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 0:
{
WarningString = reader.ReadString();
WarningNumber = reader.ReadInt();
m_Range = reader.ReadInt();
ResetDelay = reader.ReadTimeSpan();
break;
}
}
}
}
public class HintItem : WarningItem
{
[Constructible]
public HintItem(int itemID, int range, int warning, int hint) : base(itemID, range, warning) => HintNumber = hint;
[Constructible]
public HintItem(int itemID, int range, string warning, string hint) : base(itemID, range, warning) => HintString = hint;
public HintItem(Serial serial) : base(serial)
{
}
[CommandProperty(AccessLevel.GameMaster)]
public string HintString { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public int HintNumber { get; set; }
public override bool OnlyToTriggerer => true;
public override void OnDoubleClick(Mobile from)
{
SendMessage(from, true, HintString, HintNumber);
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0);
writer.Write(HintString);
writer.Write(HintNumber);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 0:
{
HintString = reader.ReadString();
HintNumber = reader.ReadInt();
break;
}
}
}
}
}

View file

@ -0,0 +1,139 @@
using Server.Targeting;
namespace Server.Items
{
[Flippable(0x1f14, 0x1f15, 0x1f16, 0x1f17)]
public class WayPoint : Item
{
private WayPoint m_Next;
[Constructible]
public WayPoint(WayPoint prev = null) : base(0x1f14)
{
Hue = 0x498;
Visible = false;
// this.Movable = false;
if (prev != null)
prev.NextPoint = this;
}
public WayPoint(Serial serial) : base(serial)
{
}
public override string DefaultName => "AI Way Point";
[CommandProperty(AccessLevel.GameMaster)]
public WayPoint NextPoint
{
get => m_Next;
set
{
if (m_Next != this)
m_Next = value;
}
}
public static void Initialize()
{
CommandSystem.Register("WayPointSeq", AccessLevel.GameMaster, WayPointSeq_OnCommand);
}
public static void WayPointSeq_OnCommand(CommandEventArgs arg)
{
arg.Mobile.SendMessage("Target the position of the first way point.");
arg.Mobile.Target = new WayPointSeqTarget(null);
}
public override void OnDoubleClick(Mobile from)
{
if (from.AccessLevel >= AccessLevel.GameMaster)
{
from.SendMessage("Target the next way point in the sequence.");
from.Target = new NextPointTarget(this);
}
}
public override void OnSingleClick(Mobile from)
{
base.OnSingleClick(from);
if (m_Next == null)
LabelTo(from, "(Unlinked)");
else
LabelTo(from, "(Linked: {0})", m_Next.Location);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 0:
{
m_Next = reader.ReadItem() as WayPoint;
break;
}
}
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0);
writer.Write(m_Next);
}
}
public class NextPointTarget : Target
{
private readonly WayPoint m_Point;
public NextPointTarget(WayPoint pt) : base(-1, false, TargetFlags.None) => m_Point = pt;
protected override void OnTarget(Mobile from, object target)
{
if (target is WayPoint point && m_Point != null)
m_Point.NextPoint = point;
else
from.SendMessage("Target a way point.");
}
}
public class WayPointSeqTarget : Target
{
private readonly WayPoint m_Last;
public WayPointSeqTarget(WayPoint last) : base(-1, true, TargetFlags.None) => m_Last = last;
protected override void OnTarget(Mobile from, object targeted)
{
if (targeted is WayPoint wayPoint)
{
if (m_Last != null)
m_Last.NextPoint = wayPoint;
}
else if (targeted is IPoint3D d)
{
Point3D p = new Point3D(d);
WayPoint point = new WayPoint(m_Last);
point.MoveToWorld(p, from.Map);
from.Target = new WayPointSeqTarget(point);
from.SendMessage(
"Target the position of the next way point in the sequence, or target a way point link the newest way point to.");
}
else
{
from.SendMessage("Target a position, or another way point.");
}
}
}
}

View file

@ -0,0 +1,176 @@
using Server.Gumps;
using Server.Multis;
using Server.Network;
namespace Server.Items
{
public abstract class BaseWindChimes : Item
{
private bool m_TurnedOn;
public BaseWindChimes(int itemID) : base(itemID)
{
}
public BaseWindChimes(Serial serial) : base(serial)
{
}
[CommandProperty(AccessLevel.GameMaster)]
public bool TurnedOn
{
get => m_TurnedOn;
set
{
m_TurnedOn = value;
InvalidateProperties();
}
}
public static int[] Sounds { get; } = { 0x505, 0x506, 0x507 };
public override bool HandlesOnMovement => m_TurnedOn && IsLockedDown;
public override void OnMovement(Mobile m, Point3D oldLocation)
{
if (m_TurnedOn && IsLockedDown && (!m.Hidden || m.AccessLevel == AccessLevel.Player) &&
Utility.InRange(m.Location, Location, 2) && !Utility.InRange(oldLocation, Location, 2))
Effects.PlaySound(Location, Map, Sounds[Utility.Random(Sounds.Length)]);
base.OnMovement(m, oldLocation);
}
public override void GetProperties(ObjectPropertyList list)
{
base.GetProperties(list);
if (m_TurnedOn)
list.Add(502695); // turned on
else
list.Add(502696); // turned off
}
public bool IsOwner(Mobile mob) => BaseHouse.FindHouseAt(this)?.IsOwner(mob) == true;
public override void OnDoubleClick(Mobile from)
{
if (IsOwner(from))
from.SendGump(new OnOffGump(this));
else
from.SendLocalizedMessage(502691); // You must be the owner to use this.
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
writer.Write(m_TurnedOn);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 0:
{
m_TurnedOn = reader.ReadBool();
break;
}
}
}
private class OnOffGump : Gump
{
private readonly BaseWindChimes m_Chimes;
public OnOffGump(BaseWindChimes chimes) : base(150, 200)
{
m_Chimes = chimes;
AddBackground(0, 0, 300, 150, 0xA28);
AddHtmlLocalized(45, 20, 300, 35, chimes.TurnedOn ? 1011035 : 1011034); // [De]Activate this item
AddButton(40, 53, 0xFA5, 0xFA7, 1);
AddHtmlLocalized(80, 55, 65, 35, 1011036); // OKAY
AddButton(150, 53, 0xFA5, 0xFA7, 0);
AddHtmlLocalized(190, 55, 100, 35, 1011012); // CANCEL
}
public override void OnResponse(NetState sender, RelayInfo info)
{
Mobile from = sender.Mobile;
if (info.ButtonID == 1)
{
bool newValue = !m_Chimes.TurnedOn;
m_Chimes.TurnedOn = newValue;
if (newValue && !m_Chimes.IsLockedDown)
from.SendLocalizedMessage(502693); // Remember, this only works when locked down.
}
else
{
from.SendLocalizedMessage(502694); // Cancelled action.
}
}
}
}
public class WindChimes : BaseWindChimes
{
[Constructible]
public WindChimes() : base(0x2832)
{
}
public WindChimes(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1030290;
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
public class FancyWindChimes : BaseWindChimes
{
[Constructible]
public FancyWindChimes() : base(0x2833)
{
}
public FancyWindChimes(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1030291;
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}