fix: Codegens more misc items (#1214)

This commit is contained in:
Kamron Batman 2022-10-29 00:43:30 -07:00 committed by GitHub
parent 505865e3ed
commit 212ce8bc24
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
81 changed files with 2641 additions and 3498 deletions

View file

@ -100,10 +100,12 @@ namespace Server.Commands
return;
}
var item = new EffectController();
item.SoundID = sound;
item.TriggerType = EffectTriggerType.InRange;
item.TriggerRange = range;
var item = new EffectController
{
SoundId = sound,
TriggerType = EffectTriggerType.InRange,
TriggerRange = range
};
item.MoveToWorld(new Point3D(x, y, z), Map.Felucca);
m_Count++;

View file

@ -1,232 +1,206 @@
using System;
using ModernUO.Serialization;
using Server.Targeting;
namespace Server.Items
namespace Server.Items;
[SerializationGenerator(0, false)]
public partial class ArcaneGem : Item
{
public class ArcaneGem : Item
public const int DefaultArcaneHue = 2117;
[Constructible]
public ArcaneGem() : base(0x1EA7)
{
public const int DefaultArcaneHue = 2117;
Stackable = Core.ML;
Weight = 1.0;
}
[Constructible]
public ArcaneGem() : base(0x1EA7)
public override string DefaultName => "arcane gem";
public override void OnDoubleClick(Mobile from)
{
if (!IsChildOf(from.Backpack))
{
Stackable = Core.ML;
Weight = 1.0;
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 static int GetChargesFor(Mobile m) => Math.Clamp((int)(m.Skills.Tailoring.Value / 5), 16, 24);
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;
}
public ArcaneGem(Serial serial) : base(serial)
if (obj is IArcaneEquip eq and Item item)
{
}
var clothing = item as BaseClothing;
var armor = item as BaseArmor;
var weapon = item as BaseWeapon;
public override string DefaultName => "arcane gem";
var resource = clothing?.Resource ?? armor?.Resource ?? weapon?.Resource ?? CraftResource.None;
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)
{
var v = (int)(m.Skills.Tailoring.Value / 5);
return v switch
{
< 16 => 16,
> 24 => 24,
_ => v
};
}
public void OnTarget(Mobile from, object obj)
{
if (!IsChildOf(from.Backpack))
if (!item.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)
{
var clothing = item as BaseClothing;
var armor = item as BaseArmor;
var weapon = item as BaseWeapon;
var 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;
}
var 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)
{
var 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.RawName;
}
else if (armor != null)
{
armor.Quality = ArmorQuality.Regular;
armor.Crafter = from.RawName;
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
if (item.LootType == LootType.Blessed)
{
from.SendMessage(
"You can only use this on exceptionally crafted robes, thigh boots, cloaks, or leather gloves."
);
return;
}
}
public static bool ConsumeCharges(Mobile from, int amount)
{
var items = from.Items;
var avail = 0;
for (var i = 0; i < items.Count; ++i)
if (resource != CraftResource.None && resource != CraftResource.RegularLeather)
{
var obj = items[i];
from.SendLocalizedMessage(1049690); // Arcane gems can not be used on that type of leather.
return;
}
if (obj is IArcaneEquip eq && eq.IsArcane)
var charges = GetChargesFor(from);
if (eq.IsArcane)
{
if (eq.CurArcaneCharges >= eq.MaxArcaneCharges)
{
avail += eq.CurArcaneCharges;
from.SendMessage("That item is already fully charged.");
}
}
if (avail < amount)
{
return false;
}
for (var i = 0; i < items.Count; ++i)
{
var obj = items[i];
if (obj is IArcaneEquip eq && eq.IsArcane)
else
{
if (eq.CurArcaneCharges > amount)
if (eq.CurArcaneCharges <= 0)
{
eq.CurArcaneCharges -= amount;
break;
item.Hue = DefaultArcaneHue;
}
amount -= eq.CurArcaneCharges;
eq.CurArcaneCharges = 0;
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)
{
var isExceptional = clothing?.Quality == ClothingQuality.Exceptional ||
armor?.Quality == ArmorQuality.Exceptional ||
weapon?.Quality == WeaponQuality.Exceptional;
return true;
if (isExceptional)
{
if (clothing != null)
{
clothing.Quality = ClothingQuality.Regular;
clothing.Crafter = from.RawName;
}
else if (armor != null)
{
armor.Quality = ArmorQuality.Regular;
armor.Crafter = from.RawName;
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.");
}
}
public override void Serialize(IGenericWriter writer)
else
{
base.Serialize(writer);
writer.Write(0);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadInt();
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)
{
var items = from.Items;
var avail = 0;
for (var i = 0; i < items.Count; ++i)
{
var obj = items[i];
if (obj is IArcaneEquip eq && eq.IsArcane)
{
avail += eq.CurArcaneCharges;
}
}
if (avail < amount)
{
return false;
}
for (var i = 0; i < items.Count; ++i)
{
var 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;
}
}

View file

@ -1,4 +1,5 @@
using System;
using ModernUO.Serialization;
using Server.Accounting;
using Server.Engines.Quests;
using Server.Engines.Quests.Haven;
@ -7,234 +8,196 @@ using Server.Mobiles;
using Server.Network;
using CashBankCheckObjective = Server.Engines.Quests.Necro.CashBankCheckObjective;
namespace Server.Items
namespace Server.Items;
[SerializationGenerator(0, false)]
public partial class BankCheck : Item
{
public class BankCheck : Item
[InvalidateProperties]
[SerializableField(0)]
[SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")]
private int _worth;
[Constructible]
public BankCheck(int worth) : base(0x14F0)
{
private int m_Worth;
Weight = 1.0;
Hue = 0x34;
LootType = LootType.Blessed;
public BankCheck(Serial serial) : base(serial)
_worth = worth;
}
public override bool DisplayLootType => Core.AOS;
public override int LabelNumber => 1041361; // A bank check
public override void GetProperties(IPropertyList list)
{
base.GetProperties(list);
if (Core.ML)
{
list.Add(1060738, $"{_worth:N0}"); // value: ~1_val~
}
else
{
list.Add(1060738, _worth); // value: ~1_val~
}
}
public override void OnAdded(IEntity parent)
{
base.OnAdded(parent);
if (!AccountGold.Enabled)
{
return;
}
[Constructible]
public BankCheck(int worth) : base(0x14F0)
{
Weight = 1.0;
Hue = 0x34;
LootType = LootType.Blessed;
Mobile owner = null;
SecureTradeInfo tradeInfo = null;
m_Worth = worth;
var root = parent as Container;
while (root?.Parent is Container container)
{
root = container;
}
[CommandProperty(AccessLevel.GameMaster)]
public int Worth
parent = root ?? parent;
if (parent is SecureTradeContainer trade && AccountGold.ConvertOnTrade)
{
get => m_Worth;
set
if (trade.Trade.From.Container == trade)
{
m_Worth = value;
InvalidateProperties();
tradeInfo = trade.Trade.From;
owner = tradeInfo.Mobile;
}
else if (trade.Trade.To.Container == trade)
{
tradeInfo = trade.Trade.To;
owner = tradeInfo.Mobile;
}
}
public override bool DisplayLootType => Core.AOS;
public override int LabelNumber => 1041361; // A bank check
public override void Serialize(IGenericWriter writer)
else if (parent is BankBox box && AccountGold.ConvertOnBank)
{
base.Serialize(writer);
writer.Write(0); // version
writer.Write(m_Worth);
owner = box.Owner;
}
public override void Deserialize(IGenericReader reader)
if (owner?.Account?.DepositGold(_worth) != true)
{
base.Deserialize(reader);
LootType = LootType.Blessed;
return;
}
var version = reader.ReadInt();
switch (version)
if (tradeInfo != null)
{
if (owner.NetState?.NewSecureTrading == false)
{
case 0:
{
m_Worth = reader.ReadInt();
break;
}
var plat = Math.DivRem(_worth, AccountGold.CurrencyThreshold, out var gold);
tradeInfo.Plat += plat;
tradeInfo.Gold += gold;
}
tradeInfo.VirtualCheck?.UpdateTrade(tradeInfo.Mobile);
}
public override void GetProperties(IPropertyList list)
owner.SendLocalizedMessage(1042763, $"{_worth:N0}");
Delete();
((Container)parent).UpdateTotals();
}
public override void OnSingleClick(Mobile from)
{
from.NetState.SendMessageLocalizedAffix(
Serial,
ItemID,
MessageType.Label,
0x3B2,
3,
1041361, // A bank check:
"",
AffixType.Append,
$" {_worth}"
);
}
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.
var box = AccountGold.Enabled ? from.Backpack : from.FindBankNoCreate();
if (box == null || !IsChildOf(box))
{
base.GetProperties(list);
if (Core.ML)
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;
}
var deposited = 0;
var toAdd = _worth;
if (AccountGold.Enabled && from.Account?.DepositGold(toAdd) == true)
{
deposited = toAdd;
}
while (toAdd > 0)
{
var amount = Math.Min(toAdd, 60000);
var gold = new Gold(amount);
if (box.TryDropItem(from, gold, false))
{
list.Add(1060738, $"{m_Worth:N0}"); // value: ~1_val~
toAdd -= amount;
deposited += amount;
}
else
{
list.Add(1060738, m_Worth); // value: ~1_val~
gold.Delete();
break;
}
}
public override void OnAdded(IEntity parent)
if (deposited >= _worth)
{
base.OnAdded(parent);
if (!AccountGold.Enabled)
{
return;
}
Mobile owner = null;
SecureTradeInfo tradeInfo = null;
var 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)
{
var plat = Math.DivRem(Worth, AccountGold.CurrencyThreshold, out var gold);
tradeInfo.Plat += plat;
tradeInfo.Gold += gold;
}
tradeInfo.VirtualCheck?.UpdateTrade(tradeInfo.Mobile);
}
owner.SendLocalizedMessage(1042763, $"{m_Worth:N0}");
Delete();
((Container)parent).UpdateTotals();
}
else
{
Worth -= deposited;
}
public override void OnSingleClick(Mobile from)
if (deposited > 0)
{
from.NetState.SendMessageLocalizedAffix(
Serial,
ItemID,
MessageType.Label,
0x3B2,
3,
1041361, // A bank check:
"",
AffixType.Append,
$" {m_Worth}"
);
}
// Gold was deposited in your account:
from.SendLocalizedMessage(1042672, true, $"{deposited:N0}");
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.
var box = AccountGold.Enabled ? from.Backpack : from.FindBankNoCreate();
if (box == null || !IsChildOf(box))
if (from is PlayerMobile pm)
{
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;
}
var qs = pm.Quest;
var deposited = 0;
var toAdd = m_Worth;
if (AccountGold.Enabled && from.Account?.DepositGold(toAdd) == true)
{
deposited = toAdd;
}
while (toAdd > 0)
{
var amount = Math.Min(toAdd, 60000);
var gold = new Gold(amount);
if (box.TryDropItem(from, gold, false))
if (qs is DarkTidesQuest)
{
toAdd -= amount;
deposited += amount;
}
else
{
gold.Delete();
break;
}
}
QuestObjective obj = qs.FindObjective<CashBankCheckObjective>();
if (deposited >= m_Worth)
{
Delete();
}
else
{
Worth -= deposited;
}
if (deposited > 0)
{
// Gold was deposited in your account:
from.SendLocalizedMessage(1042672, true, $"{deposited:N0}");
if (from is PlayerMobile pm)
{
var qs = pm.Quest;
if (qs is DarkTidesQuest)
if (obj?.Completed == false)
{
QuestObjective obj = qs.FindObjective<CashBankCheckObjective>();
if (obj?.Completed == false)
{
obj.Complete();
}
obj.Complete();
}
}
if (qs is UzeraanTurmoilQuest)
if (qs is UzeraanTurmoilQuest)
{
var obj = qs.FindObjective(typeof(Engines.Quests.Haven.CashBankCheckObjective));
if (obj?.Completed == false)
{
var obj = qs.FindObjective(typeof(Engines.Quests.Haven.CashBankCheckObjective));
if (obj?.Completed == false)
{
obj.Complete();
}
obj.Complete();
}
}
}

View file

@ -1,31 +1,15 @@
namespace Server.Items
using ModernUO.Serialization;
namespace Server.Items;
[SerializationGenerator(0, false)]
public partial class Beeswax : Item
{
public class Beeswax : Item
[Constructible]
public Beeswax(int amount = 1) : base(0x1422)
{
[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);
var version = reader.ReadInt();
}
Weight = 1.0;
Stackable = true;
Amount = amount;
}
}

View file

@ -1,67 +1,50 @@
using System;
using System.Buffers.Binary;
using ModernUO.Serialization;
using Server.Network;
namespace Server.Items
namespace Server.Items;
[SerializationGenerator(0, false)]
public partial class Blocker : Item
{
public class Blocker : Item
private const ushort GMItemId = 0x1183;
[Constructible]
public Blocker() : base(0x21A4) => Movable = false;
public override int LabelNumber => 503057; // Impassable!
public override void SendWorldPacketTo(NetState ns, ReadOnlySpan<byte> world = default)
{
private const ushort GMItemId = 0x1183;
[Constructible]
public Blocker() : base(0x21A4) => Movable = false;
public Blocker(Serial serial) : base(serial)
var mob = ns.Mobile;
if (AccessLevel.GameMaster >= mob?.AccessLevel)
{
base.SendWorldPacketTo(ns, world);
return;
}
public override int LabelNumber => 503057; // Impassable!
SendGMItem(ns);
}
public override void SendWorldPacketTo(NetState ns, ReadOnlySpan<byte> world = default)
private void SendGMItem(NetState ns)
{
// GM Packet
Span<byte> buffer = stackalloc byte[OutgoingEntityPackets.MaxWorldEntityPacketLength].InitializePacket();
int length;
if (ns.StygianAbyss)
{
var mob = ns.Mobile;
if (AccessLevel.GameMaster >= mob?.AccessLevel)
{
base.SendWorldPacketTo(ns, world);
return;
}
SendGMItem(ns);
length = OutgoingEntityPackets.CreateWorldEntity(buffer, this, ns.HighSeas);
BinaryPrimitives.WriteUInt16BigEndian(buffer[8..10], GMItemId);
}
else
{
length = OutgoingItemPackets.CreateWorldItem(buffer, this);
BinaryPrimitives.WriteUInt16BigEndian(buffer[7..9], GMItemId);
}
private void SendGMItem(NetState ns)
{
// GM Packet
Span<byte> buffer = stackalloc byte[OutgoingEntityPackets.MaxWorldEntityPacketLength].InitializePacket();
int length;
if (ns.StygianAbyss)
{
length = OutgoingEntityPackets.CreateWorldEntity(buffer, this, ns.HighSeas);
BinaryPrimitives.WriteUInt16BigEndian(buffer[8..10], GMItemId);
}
else
{
length = OutgoingItemPackets.CreateWorldItem(buffer, this);
BinaryPrimitives.WriteUInt16BigEndian(buffer[7..9], GMItemId);
}
ns.Send(buffer[..length]);
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadInt();
}
ns.Send(buffer[..length]);
}
}

View file

@ -1,55 +1,26 @@
using System;
using ModernUO.Serialization;
namespace Server.Items
namespace Server.Items;
[SerializationGenerator(0, false)]
public partial class Blood : Item
{
public class Blood : Item
[Constructible]
public Blood() : this(Utility.RandomList(0x1645, 0x122A, 0x122B, 0x122C, 0x122D, 0x122E, 0x122F))
{
[Constructible]
public Blood() : this(Utility.RandomList(0x1645, 0x122A, 0x122B, 0x122C, 0x122D, 0x122E, 0x122F))
{
}
}
[Constructible]
public Blood(int itemID) : base(itemID)
{
Movable = false;
[Constructible]
public Blood(int itemID) : base(itemID)
{
Movable = false;
Timer.StartTimer(TimeSpan.FromSeconds(5), Delete);
}
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);
var version = reader.ReadInt();
}
private class InternalTimer : Timer
{
private readonly Item m_Blood;
public InternalTimer(Item blood) : base(TimeSpan.FromSeconds(5.0))
{
m_Blood = blood;
}
protected override void OnTick()
{
m_Blood.Delete();
}
}
[AfterDeserialization]
private void AfterDeserialization()
{
Timer.StartTimer(TimeSpan.FromSeconds(5), Delete);
}
}

View file

@ -1,39 +1,170 @@
using System;
using ModernUO.Serialization;
using Server.Mobiles;
using Server.Network;
using Server.Spells.Ninjitsu;
using Server.Targeting;
namespace Server.Items
namespace Server.Items;
[SerializationGenerator(0, false)]
public partial class Bola : Item
{
public class Bola : Item
[Constructible]
public Bola(int amount = 1) : base(0x26AC)
{
[Constructible]
public Bola(int amount = 1) : base(0x26AC)
Weight = 4.0;
Stackable = true;
Amount = amount;
}
public override void OnDoubleClick(Mobile from)
{
if (!IsChildOf(from.Backpack))
{
Weight = 4.0;
Stackable = true;
Amount = amount;
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...*
// ~1_NAME~ begins to menacingly swing a bola...
from.NonlocalOverheadMessage(MessageType.Emote, 0x3B2, 1049633, from.Name);
}
}
private static void FinishThrow(Mobile from, Mobile to)
{
if (Core.AOS)
{
new Bola().MoveToWorld(to.Location, to.Map);
}
public Bola(Serial serial) : base(serial)
if (to is ChaosDragoon or ChaosDragoonElite)
{
from.SendLocalizedMessage(1042047); // You fail to knock the rider from its mount.
}
public override void OnDoubleClick(Mobile from)
var mt = to.Mount;
if (mt != null && !(to is ChaosDragoon or ChaosDragoonElite))
{
if (!IsChildOf(from.Backpack))
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);
}
/* only failsafe, attacker should already be dismounted */
if (Core.AOS)
{
(from as PlayerMobile)?.SetMountBlock(
BlockMountType.BolaRecovery,
TimeSpan.FromSeconds(Core.ML ? 10 : 3),
true
);
}
to.Damage(1);
Timer.StartTimer(TimeSpan.FromSeconds(2.0), from.EndAction<Bola>);
}
private static bool HasFreeHands(Mobile from)
{
var one = from.FindItemOnLayer(Layer.OneHanded);
var two = from.FindItemOnLayer(Layer.TwoHanded);
if (Core.SE)
{
var 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 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 not Mobile to)
{
from.SendLocalizedMessage(1049629); // You cannot throw a bola at that.
}
else if (!m_Bola.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
@ -46,186 +177,31 @@ namespace Server.Items
{
from.SendLocalizedMessage(1070902); // You can't use this while in an animal form!
}
else
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.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...
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.StartTimer(TimeSpan.FromSeconds(0.5), () => FinishThrow(from, to));
}
}
private static void FinishThrow(Mobile from, Mobile to)
{
if (Core.AOS)
else
{
new Bola().MoveToWorld(to.Location, to.Map);
}
if (to is ChaosDragoon or ChaosDragoonElite)
{
from.SendLocalizedMessage(1042047); // You fail to knock the rider from its mount.
}
var mt = to.Mount;
if (mt != null && !(to is ChaosDragoon or 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.StartTimer(TimeSpan.FromSeconds(2.0), from.EndAction<Bola>);
}
private static bool HasFreeHands(Mobile from)
{
var one = from.FindItemOnLayer(Layer.OneHanded);
var two = from.FindItemOnLayer(Layer.TwoHanded);
if (Core.SE)
{
var 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);
var 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.StartTimer(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.
}
// You have to wait a few moments before you can use another bola!
from.SendLocalizedMessage(1049624);
}
}
}

View file

@ -1,32 +1,16 @@
namespace Server.Items
using ModernUO.Serialization;
namespace Server.Items;
[SerializationGenerator(0, false)]
public partial class BolaBall : Item
{
public class BolaBall : Item
[Constructible]
public BolaBall(int amount = 1) : base(0xE73)
{
[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);
var version = reader.ReadInt();
}
Weight = 4.0;
Stackable = true;
Amount = amount;
Hue = 0x8AC;
}
}

View file

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

View file

@ -1,471 +1,426 @@
using System;
using System.Collections.Generic;
using ModernUO.Serialization;
using Server.Network;
using Server.Targeting;
namespace Server.Items
namespace Server.Items;
public class CrystalRechargeInfo
{
public class CrystalRechargeInfo
private static CrystalRechargeInfo[] _table =
{
public static readonly CrystalRechargeInfo[] Table =
{
new(typeof(Citrine), 500),
new(typeof(Amber), 500),
new(typeof(Tourmaline), 750),
new(typeof(Emerald), 1000),
new(typeof(Sapphire), 1000),
new(typeof(Amethyst), 1000),
new(typeof(StarSapphire), 1250),
new(typeof(Diamond), 2000)
};
new(typeof(Citrine), 500),
new(typeof(Amber), 500),
new(typeof(Tourmaline), 750),
new(typeof(Emerald), 1000),
new(typeof(Sapphire), 1000),
new(typeof(Amethyst), 1000),
new(typeof(StarSapphire), 1250),
new(typeof(Diamond), 2000)
};
private CrystalRechargeInfo(Type type, int amount)
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 (var info in _table)
{
Type = type;
Amount = amount;
if (info.Type == type)
{
return info;
}
}
public Type Type { get; }
return null;
}
}
public int Amount { get; }
[SerializationGenerator(1)]
public partial class BroadcastCrystal : Item
{
public const int MaxCharges = 2000;
public static CrystalRechargeInfo Get(Type type)
[InvalidateProperties]
[SerializableField(0)]
[SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")]
private int _charges;
[InvalidateProperties]
[SerializableField(1, getter: "private", setter: "private")]
[SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")]
private List<ReceiverCrystal> _receivers;
[Constructible]
public BroadcastCrystal(int charges = 2000) : base(0x1ED0)
{
Light = LightType.Circle150;
_charges = charges;
_receivers = new List<ReceiverCrystal>();
}
public override int LabelNumber => 1060740; // communication crystal
[CommandProperty(AccessLevel.GameMaster)]
public bool Active
{
get => ItemID == 0x1ECD;
set
{
foreach (var info in Table)
{
if (info.Type == type)
{
return info;
}
}
return null;
ItemID = value ? 0x1ECD : 0x1ED0;
InvalidateProperties();
}
}
public class BroadcastCrystal : Item
public override bool HandlesOnSpeech => true;
public override void GetProperties(IPropertyList list)
{
public static readonly int MaxCharges = 2000;
base.GetProperties(list);
private int m_Charges;
list.Add(Active ? 1060742 : 1060743); // active / inactive
list.Add(1060745); // broadcast
list.Add(1060741, Charges); // charges: ~1_val~
[Constructible]
public BroadcastCrystal(int charges = 2000) : base(0x1ED0)
if (_receivers.Count > 0)
{
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(IPropertyList list)
{
base.GetProperties(list);
list.Add(Active ? 1060742 : 1060743); // active / inactive
list.Add(1060745); // broadcast
list.Add(1060741, Charges); // charges: ~1_val~
if (Receivers.Count > 0)
{
list.Add(1060746, Receivers.Count); // 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 not Mobile)
{
return;
}
if (e.Type == MessageType.Emote)
{
return;
}
var from = e.Mobile;
var speech = e.Speech;
foreach (var 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.Write(Receivers);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadEncodedInt();
m_Charges = reader.ReadEncodedInt();
Receivers = reader.ReadEntityList<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 (var 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))
{
var 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.
}
}
list.Add(1060746, _receivers.Count); // links: ~1_val~
}
}
public class ReceiverCrystal : Item
public override void OnSingleClick(Mobile from)
{
private BroadcastCrystal m_Sender;
base.OnSingleClick(from);
[Constructible]
public ReceiverCrystal() : base(0x1ED0) => Light = LightType.Circle150;
LabelTo(from, Active ? 1060742 : 1060743); // active / inactive
LabelTo(from, 1060745); // broadcast
LabelTo(from, 1060741, Charges.ToString()); // charges: ~1_val~
public ReceiverCrystal(Serial serial) : base(serial)
if (_receivers.Count > 0)
{
LabelTo(from, 1060746, _receivers.Count.ToString()); // links: ~1_val~
}
}
private void Deserialize(IGenericReader reader, int version)
{
_charges = reader.ReadEncodedInt();
_receivers = reader.ReadEntityList<ReceiverCrystal>();
}
public override void OnSpeech(SpeechEventArgs e)
{
if (!Active || Receivers.Count == 0 || RootParent != null && RootParent is not Mobile || e.Type == MessageType.Emote)
{
return;
}
public override int LabelNumber => 1060740; // communication crystal
var from = e.Mobile;
var speech = e.Speech;
[CommandProperty(AccessLevel.GameMaster)]
public bool Active
foreach (var receiver in new List<ReceiverCrystal>(Receivers))
{
get => ItemID == 0x1ED1;
set
if (receiver.Deleted)
{
ItemID = value ? 0x1ED1 : 0x1ED0;
InvalidateProperties();
RemoveReceiver(receiver);
}
}
[CommandProperty(AccessLevel.GameMaster)]
public BroadcastCrystal Sender
{
get => m_Sender;
set
else if (Charges > 0)
{
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(IPropertyList 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;
}
var 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}");
receiver.TransmitMessage(from, speech);
Charges--;
}
else
{
PublicOverheadMessage(MessageType.Regular, 0x2B2, false, text);
Active = false;
break;
}
}
}
public override void OnDoubleClick(Mobile from)
public void AddReceiver(ReceiverCrystal receiver)
{
this.Add(Receivers, receiver);
InvalidateProperties();
}
public void RemoveReceiver(ReceiverCrystal receiver)
{
this.Remove(Receivers, receiver);
InvalidateProperties();
}
public override void OnDoubleClick(Mobile from)
{
if (!from.InRange(GetWorldLocation(), 2))
{
if (!from.InRange(GetWorldLocation(), 2))
from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that.
return;
}
from.Target = new InternalTarget(this);
}
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;
}
from.Target = new InternalTarget(this);
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0); // version
writer.Write(m_Sender);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadEncodedInt();
m_Sender = reader.ReadEntity<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 (targeted == m_Crystal)
{
if (!m_Crystal.IsAccessibleTo(from))
if (m_Crystal.Active)
{
return;
m_Crystal.Active = false;
from.SendLocalizedMessage(500672); // You turn the crystal off.
}
if (from.Map != m_Crystal.Map || !from.InRange(m_Crystal.GetWorldLocation(), 2))
else
{
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 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.
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)
{
// That receiver crystal is already linked to another broadcast crystal.
from.SendLocalizedMessage(1010043);
}
else
{
if (targeted is Item targItem && targItem.VerifyMove(from))
receiver.Sender = m_Crystal;
from.SendLocalizedMessage(500675); // That crystal has been linked to this crystal.
}
}
else if (targeted == from)
{
foreach (var 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 not Item targItem || !targItem.VerifyMove(from))
{
from.SendLocalizedMessage(500681); // You cannot use this crystal on that.
}
else
{
var info = CrystalRechargeInfo.Get(targItem.GetType());
if (info == null)
{
from.SendLocalizedMessage(500681); // You cannot use this crystal on that.
}
else if (m_Crystal.Charges >= MaxCharges)
{
from.SendLocalizedMessage(500678); // This crystal is already fully charged.
}
else
{
targItem.Consume();
if (m_Crystal.Charges + info.Amount >= MaxCharges)
{
var info = CrystalRechargeInfo.Get(targItem.GetType());
if (info != null)
{
from.SendLocalizedMessage(500677); // This crystal cannot be recharged.
return;
}
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.
}
from.SendLocalizedMessage(1010045); // That receiver crystal is not linked.
}
}
}
}
}
[SerializationGenerator(0)]
public partial class ReceiverCrystal : Item
{
private BroadcastCrystal _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();
}
}
[SerializableProperty(0, useField: nameof(_sender))]
[CommandProperty(AccessLevel.GameMaster)]
public BroadcastCrystal Sender
{
get => _sender;
set
{
_sender?.RemoveReceiver(this);
_sender = value;
value?.AddReceiver(this);
this.MarkDirty();
}
}
public override void GetProperties(IPropertyList 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;
}
var 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);
}
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))
{
var 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.
}
}
}
}

View file

@ -1221,10 +1221,7 @@ namespace Server.Items
{
private readonly Corpse m_Corpse;
public InternalTimer(Corpse c, TimeSpan delay) : base(delay)
{
m_Corpse = c;
}
public InternalTimer(Corpse c, TimeSpan delay) : base(delay) => m_Corpse = c;
protected override void OnTick()
{

View file

@ -97,10 +97,7 @@ namespace Server.Items
{
private readonly DecayedCorpse m_Corpse;
public InternalTimer(DecayedCorpse c, TimeSpan delay) : base(delay)
{
m_Corpse = c;
}
public InternalTimer(DecayedCorpse c, TimeSpan delay) : base(delay) => m_Corpse = c;
protected override void OnTick()
{

View file

@ -1,206 +1,179 @@
using System;
using ModernUO.Serialization;
using Server.Mobiles;
using Server.Network;
using Server.Utilities;
namespace Server.Items
namespace Server.Items;
[SerializationGenerator(0, false)]
public partial class DeceitBrazier : Item
{
public class DeceitBrazier : Item
private TimerExecutionToken _timerToken;
[SerializableField(0)]
[SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")]
private int _spawnRange;
[SerializableField(1)]
[SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")]
private TimeSpan _nextSpawnDelay;
[Constructible]
public DeceitBrazier() : base(0xE31)
{
private TimerExecutionToken _timerToken;
Movable = false;
Light = LightType.Circle225;
NextSpawn = Core.Now;
NextSpawnDelay = TimeSpan.FromMinutes(15.0);
SpawnRange = 5;
}
[Constructible]
public DeceitBrazier() : base(0xE31)
private 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; }
public override int LabelNumber => 1023633; // Brazier
public override bool HandlesOnMovement => true;
[AfterDeserialization]
private void AfterDeserialization()
{
NextSpawn = Core.Now;
}
public virtual void HeedWarning()
{
// Heed this warning well, and use this brazier at your own peril.
PublicOverheadMessage(MessageType.Regular, 0x3B2, 500761);
_timerToken.Cancel();
}
public override void OnMovement(Mobile m, Point3D oldLocation)
{
// means we haven't spawned anything if the next spawn is below
if (NextSpawn < Core.Now &&
Utility.InRange(m.Location, Location, 1) &&
!Utility.InRange(oldLocation, Location, 1) &&
m.Player && !(m.AccessLevel > AccessLevel.Player || m.Hidden) && !_timerToken.Running)
{
Movable = false;
Light = LightType.Circle225;
NextSpawn = Core.Now;
NextSpawnDelay = TimeSpan.FromMinutes(15.0);
SpawnRange = 5;
Timer.StartTimer(TimeSpan.FromSeconds(2), HeedWarning, out _timerToken);
}
public DeceitBrazier(Serial serial) : base(serial)
base.OnMovement(m, oldLocation);
}
public Point3D GetSpawnPosition()
{
var map = Map;
if (map == null)
{
}
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);
var version = reader.ReadInt();
if (version >= 0)
{
SpawnRange = reader.ReadInt();
NextSpawnDelay = reader.ReadTimeSpan();
}
NextSpawn = Core.Now;
}
public virtual void HeedWarning()
{
PublicOverheadMessage(
MessageType.Regular,
0x3B2,
500761 // Heed this warning well, and use this brazier at your own peril.
);
_timerToken.Cancel();
}
public override void OnMovement(Mobile m, Point3D oldLocation)
{
// means we haven't spawned anything if the next spawn is below
if (NextSpawn < Core.Now &&
Utility.InRange(m.Location, Location, 1) &&
!Utility.InRange(oldLocation, Location, 1) &&
m.Player && !(m.AccessLevel > AccessLevel.Player || m.Hidden) && !_timerToken.Running)
{
Timer.StartTimer(TimeSpan.FromSeconds(2), HeedWarning, out _timerToken);
}
base.OnMovement(m, oldLocation);
}
public Point3D GetSpawnPosition()
{
var map = Map;
if (map == null)
{
return Location;
}
// Try 10 times to find a Spawnable location.
for (var i = 0; i < 10; i++)
{
var x = Location.X + (Utility.Random(SpawnRange * 2 + 1) - SpawnRange);
var y = Location.Y + (Utility.Random(SpawnRange * 2 + 1) - SpawnRange);
var 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)
// Try 10 times to find a Spawnable location.
for (var i = 0; i < 10; i++)
{
Effects.SendLocationParticles(EffectItem.Create(loc, map, EffectItem.DefaultDuration), 0x3709, 10, 30, 5052);
Effects.PlaySound(loc, map, 0x225);
}
var x = Location.X + (Utility.Random(SpawnRange * 2 + 1) - SpawnRange);
var y = Location.Y + (Utility.Random(SpawnRange * 2 + 1) - SpawnRange);
var z = Map.GetAverageZ(x, y);
private void SummonCreatureToWorld(BaseCreature bc, Point3D spawnLoc, Map map)
{
bc.Home = Location;
bc.RangeHome = SpawnRange;
bc.FightMode = FightMode.Closest;
bc.MoveToWorld(spawnLoc, map);
DoEffect(spawnLoc, map);
bc.ForceReacquire();
}
public override void OnDoubleClick(Mobile from)
{
if (Utility.InRange(from.Location, Location, 2))
if (Map.CanSpawnMobile(new Point2D(x, y), Z))
{
try
{
if (NextSpawn < Core.Now)
{
var map = Map;
var bc = Creatures.RandomElement().CreateInstance<BaseCreature>();
return new Point3D(x, y, Z);
}
var spawnLoc = GetSpawnPosition();
if (Map.CanSpawnMobile(new Point2D(x, y), z))
{
return new Point3D(x, y, z);
}
}
DoEffect(spawnLoc, map);
return Location;
}
Timer.StartTimer(TimeSpan.FromSeconds(1), () => SummonCreatureToWorld(bc, spawnLoc, map));
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);
}
NextSpawn = Core.Now + NextSpawnDelay;
}
else
{
PublicOverheadMessage(
MessageType.Regular,
0x3B2,
500760
); // The brazier fizzes and pops, but nothing seems to happen.
}
}
catch
{
// ignored
}
private void SummonCreatureToWorld(BaseCreature bc, Point3D spawnLoc, Map map)
{
bc.Home = Location;
bc.RangeHome = SpawnRange;
bc.FightMode = FightMode.Closest;
bc.MoveToWorld(spawnLoc, map);
DoEffect(spawnLoc, map);
bc.ForceReacquire();
}
public override void OnDoubleClick(Mobile from)
{
if (!Utility.InRange(from.Location, Location, 2))
{
from.SendLocalizedMessage(500446); // That is too far away.
return;
}
try
{
if (NextSpawn < Core.Now)
{
var map = Map;
var bc = _creatures.RandomElement().CreateInstance<BaseCreature>();
var spawnLoc = GetSpawnPosition();
DoEffect(spawnLoc, map);
Timer.StartTimer(TimeSpan.FromSeconds(1), () => SummonCreatureToWorld(bc, spawnLoc, map));
NextSpawn = Core.Now + NextSpawnDelay;
}
else
{
from.SendLocalizedMessage(500446); // That is too far away.
// The brazier fizzes and pops, but nothing seems to happen.
PublicOverheadMessage(MessageType.Regular, 0x3B2, 500760);
}
}
catch
{
// ignored
}
}
}

View file

@ -1,382 +1,315 @@
using System;
using ModernUO.Serialization;
namespace Server.Items
namespace Server.Items;
public enum ECEffectType
{
public enum ECEffectType
None,
Moving,
Location,
Target,
Lightning
}
public enum EffectTriggerType
{
None,
Sequenced,
DoubleClick,
InRange
}
[SerializationGenerator(0, false)]
public partial class EffectController : Item
{
[SerializableField(0)]
[SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")]
private TimeSpan _effectDelay;
[SerializableField(1)]
[SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")]
private TimeSpan _triggerDelay;
[SerializableField(2)]
[SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")]
private TimeSpan _soundDelay;
[SerializableField(3)]
private IEntity _source;
[SerializableField(4)]
private IEntity _target;
[SerializableField(5)]
[SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")]
private EffectController _sequence;
[SerializableField(6)]
[SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")]
private bool _fixedDirection;
[SerializableField(7)]
[SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")]
private bool _explodes;
[SerializableField(8)]
[SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")]
private bool _playSoundAtTrigger;
[SerializableField(9)]
[SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")]
private ECEffectType _effectType;
[SerializableField(10)]
[SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")]
private EffectLayer _effectLayer;
[SerializableField(11)]
[SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")]
private EffectTriggerType _triggerType;
[SerializableField(12)]
[SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")]
private int _effectItemId;
[SerializableField(13)]
[SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")]
private int _effectHue;
[SerializableField(14)]
[SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")]
private int _renderMode;
[SerializableField(15)]
[SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")]
private int _speed;
[SerializableField(16)]
[SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")]
private int _duration;
[SerializableField(17)]
[SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")]
private int _particleEffect;
[SerializableField(18)]
[SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")]
private int _explodeParticleEffect;
[SerializableField(19)]
[SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")]
private int _explodeSound;
[SerializableField(20)]
[SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")]
private int _unknown;
[SerializableField(21)]
[SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")]
private int _soundId;
[SerializableField(22)]
[SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")]
private int _triggerRange;
[Constructible]
public EffectController() : base(0x1B72)
{
None,
Moving,
Location,
Target,
Lightning
Movable = false;
Visible = false;
TriggerType = EffectTriggerType.Sequenced;
EffectLayer = (EffectLayer)255;
}
public enum EffectTriggerType
[CommandProperty(AccessLevel.GameMaster)]
public Item SourceItem
{
None,
Sequenced,
DoubleClick,
InRange
get => _source as Item;
set => _source = value;
}
public class EffectController : Item
[CommandProperty(AccessLevel.GameMaster)]
public Mobile SourceMobile
{
private IEntity m_Source;
private IEntity m_Target;
get => _source as Mobile;
set => _source = value;
}
[Constructible]
public EffectController() : base(0x1B72)
[CommandProperty(AccessLevel.GameMaster)]
public bool SourceNull
{
get => _source == null;
set
{
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)
{
if (value)
_source = null;
}
}
}
[CommandProperty(AccessLevel.GameMaster)]
public Item TargetItem
{
get => _target as Item;
set => _target = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public Mobile TargetMobile
{
get => _target as Mobile;
set => _target = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public bool TargetNull
{
get => _target == null;
set
{
if (value)
{
_target = null;
}
}
}
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)
{
return;
}
var worldLocation = GetWorldLocation();
if (Utility.InRange(worldLocation, m.Location, _triggerRange) &&
!Utility.InRange(worldLocation, oldLocation, _triggerRange))
{
DoEffect(m);
}
}
public void PlaySound(IEntity trigger)
{
var 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 { Hidden: true, AccessLevel: > AccessLevel.Player })
{
return;
}
if (_soundId > 0)
{
Timer.StartTimer(SoundDelay, () => PlaySound(trigger));
}
if (Sequence != null)
{
Timer.StartTimer(TriggerDelay, () => Sequence.DoEffect(trigger));
}
if (EffectType != ECEffectType.None)
{
Timer.StartTimer(EffectDelay, () => InternalDoEffect(trigger));
}
}
public void InternalDoEffect(IEntity trigger)
{
var from = _source ?? trigger;
var to = _target ?? trigger;
switch (EffectType)
{
case ECEffectType.Lightning:
{
m_Source = null;
Effects.SendBoltEffect(from, false, EffectHue);
break;
}
}
}
[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)
case ECEffectType.Location:
{
m_Target = null;
Effects.SendLocationParticles(
EffectItem.Create(from.Location, from.Map, EffectItem.DefaultDuration),
_effectItemId,
_speed,
_duration,
_effectHue,
_renderMode,
_particleEffect,
_unknown
);
break;
}
}
}
[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);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadInt();
switch (version)
{
case 0:
case ECEffectType.Moving:
{
if (from == this)
{
EffectDelay = reader.ReadTimeSpan();
TriggerDelay = reader.ReadTimeSpan();
SoundDelay = reader.ReadTimeSpan();
m_Source = reader.ReadEntity<IEntity>();
m_Target = reader.ReadEntity<IEntity>();
Sequence = reader.ReadEntity<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;
from = EffectItem.Create(from.Location, from.Map, EffectItem.DefaultDuration);
}
}
}
public void PlaySound(IEntity trigger)
{
var 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 { Hidden: true } mobile && mobile.AccessLevel > AccessLevel.Player)
{
return;
}
if (SoundID > 0)
{
Timer.StartTimer(SoundDelay, () => PlaySound(trigger));
}
if (Sequence != null)
{
Timer.StartTimer(TriggerDelay, () => Sequence.DoEffect(trigger));
}
if (EffectType != ECEffectType.None)
{
Timer.StartTimer(EffectDelay, () => InternalDoEffect(trigger));
}
}
public void InternalDoEffect(IEntity trigger)
{
var from = m_Source ?? trigger;
var to = m_Target ?? trigger;
switch (EffectType)
{
case ECEffectType.Lightning:
if (to == this)
{
Effects.SendBoltEffect(from, false, EffectHue);
break;
to = EffectItem.Create(to.Location, to.Map, EffectItem.DefaultDuration);
}
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;
}
}
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

@ -1,91 +1,67 @@
using System;
using System.Collections.Generic;
using ModernUO.Serialization;
namespace Server.Items
namespace Server.Items;
[SerializationGenerator(0, false)]
public partial class EffectItem : Item
{
public class EffectItem : Item
private static Queue<EffectItem> _free = new(); // List of available EffectItems
public static readonly TimeSpan DefaultDuration = TimeSpan.FromSeconds(5.0);
private EffectItem() : base(1) => Movable = false;
public override bool Decays => true;
public static EffectItem Create(Point3D p, Map map, TimeSpan duration)
{
private static readonly List<EffectItem> m_Free = new(); // List of available EffectItems
EffectItem item = null;
public static readonly TimeSpan DefaultDuration = TimeSpan.FromSeconds(5.0);
private EffectItem() : base(1) // nodraw
=>
Movable = false;
public EffectItem(Serial serial) : base(serial)
while (_free.Count > 0)
{
var free = _free.Dequeue();
if (!free.Deleted && free.Map == Map.Internal)
{
item = free;
break;
}
}
public override bool Decays => true;
public static EffectItem Create(Point3D p, Map map, TimeSpan duration)
if (item == null)
{
EffectItem item = null;
for (var i = m_Free.Count - 1; item == null && i >= 0; --i) // We reuse new entries first so decay works better
{
var 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;
item = new EffectItem();
}
else
{
item.ItemID = 1;
}
public void BeginFree(TimeSpan duration)
item.MoveToWorld(p, map);
item.BeginFree(duration);
return item;
}
public void BeginFree(TimeSpan duration) => new FreeTimer(this, duration).Start();
[AfterDeserialization(false)]
private void AfterDeserialization()
{
Delete();
}
private class FreeTimer : Timer
{
private EffectItem _item;
public FreeTimer(EffectItem item, TimeSpan delay) : base(delay) => _item = item;
protected override void OnTick()
{
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);
var version = reader.ReadInt();
Delete();
}
private class FreeTimer : Timer
{
private readonly EffectItem m_Item;
public FreeTimer(EffectItem item, TimeSpan delay) : base(delay)
{
m_Item = item;
}
protected override void OnTick()
{
m_Item.Internalize();
m_Free.Add(m_Item);
}
_item.Internalize();
_free.Enqueue(_item);
}
}
}

View file

@ -1,26 +1,10 @@
namespace Server.Items
using ModernUO.Serialization;
namespace Server.Items;
[SerializationGenerator(0, false)]
public partial class ExecutionersCap : Item
{
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);
var version = reader.ReadInt();
}
}
[Constructible]
public ExecutionersCap() : base(0xF83) => Weight = 1.0;
}

View file

@ -1,228 +1,208 @@
using System;
using System.Collections.Generic;
using ModernUO.Serialization;
using Server.Collections;
using Server.Network;
using Server.Spells;
using Server.Spells.Fourth;
using Server.Targeting;
namespace Server.Items
namespace Server.Items;
[SerializationGenerator(0, false)]
public partial class Firebomb : Item
{
public class Firebomb : Item
private Mobile m_LitBy;
private Point3D _thrownFromLocation;
private int _ticks;
private TimerExecutionToken _timerToken;
private List<Mobile> _users;
[Constructible]
public Firebomb(int itemID = 0x99B) : base(itemID)
{
private Mobile m_LitBy;
private Point3D _thrownFromLocation;
private int m_Ticks;
private TimerExecutionToken _timerToken;
private List<Mobile> m_Users;
// Name = "a firebomb";
Weight = 2.0;
Hue = 1260;
}
[Constructible]
public Firebomb(int itemID = 0x99B) : base(itemID)
public override void OnDoubleClick(Mobile from)
{
if (!IsChildOf(from.Backpack))
{
// Name = "a firebomb";
Weight = 2.0;
Hue = 1260;
from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it.
return;
}
public Firebomb(Serial serial) : base(serial)
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;
}
public override void Serialize(IGenericWriter writer)
if (_timerToken.Running)
{
base.Serialize(writer);
writer.WriteEncodedInt(0); // version
from.SendLocalizedMessage(1060581); // You've already lit it! Better throw it now!
}
else
{
Timer.StartTimer(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1), OnFirebombTimerTick, out _timerToken);
m_LitBy = from;
from.SendLocalizedMessage(1060582); // You light the firebomb. Throw it now!
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
_users ??= new List<Mobile>();
var version = reader.ReadEncodedInt();
if (!_users.Contains(from))
{
_users.Add(from);
}
public override void OnDoubleClick(Mobile from)
from.Target = new ThrowTarget(this);
}
private void OnFirebombTimerTick()
{
if (Deleted)
{
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 (_timerToken.Running)
{
from.SendLocalizedMessage(1060581); // You've already lit it! Better throw it now!
}
else
{
Timer.StartTimer(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1), OnFirebombTimerTick, out _timerToken);
m_LitBy = from;
from.SendLocalizedMessage(1060582); // You light the firebomb. Throw it now!
}
m_Users ??= new List<Mobile>();
if (!m_Users.Contains(from))
{
m_Users.Add(from);
}
from.Target = new ThrowTarget(this);
_timerToken.Cancel();
return;
}
private void OnFirebombTimerTick()
if (Map == Map.Internal && HeldBy == null)
{
if (Deleted)
{
_timerToken.Cancel();
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 (var 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)
{
var eable = Map.GetMobilesInRange(Location, 1);
using var targets = PooledRefQueue<Mobile>.Create();
foreach (var m in eable)
{
if (m_LitBy == null || SpellHelper.ValidIndirectTarget(m_LitBy, m) &&
m_LitBy.CanBeHarmful(m, false))
{
targets.Enqueue(m);
}
}
eable.Free();
while (targets.Count > 0)
{
var victim = targets.Dequeue();
m_LitBy?.DoHarmful(victim);
AOS.Damage(victim, m_LitBy, Utility.Random(3) + 4, 0, 100, 0, 0, 0);
}
var loc = _thrownFromLocation;
var eastToWest = SpellHelper.GetEastToWest(loc, Location);
Effects.PlaySound(loc, Map, 0x20C);
var itemID = eastToWest ? 0x398C : 0x3996;
for (var i = -2; i <= 2; ++i)
{
var targetLoc = new Point3D(eastToWest ? loc.X + i : loc.X, eastToWest ? loc.Y : loc.Y + i, loc.Z);
new FireFieldSpell.FireFieldItem(itemID, targetLoc, m_LitBy, Map, TimeSpan.FromSeconds(9), i);
}
}
_timerToken.Cancel();
Delete();
break;
}
}
return;
}
private void OnFirebombTarget(Mobile from, object obj)
switch (_ticks)
{
if (Deleted || Map == Map.Internal || !IsChildOf(from.Backpack))
{
return;
}
if (obj is not IPoint3D p)
{
return;
}
SpellHelper.GetSurfaceTop(ref p);
_thrownFromLocation = new Point3D(p);
var map = Map;
from.RevealingAction();
var to = p as IEntity ?? new Entity(Serial.Zero, _thrownFromLocation, map);
Effects.SendMovingEffect(from, to, ItemID, 7, 0, false, false, Hue);
Timer.StartTimer(TimeSpan.FromSeconds(1.0),
() =>
case 0:
case 1:
case 2:
{
if (Deleted)
++_ticks;
if (HeldBy != null)
{
return;
HeldBy.PublicOverheadMessage(MessageType.Regular, 957, false, _ticks.ToString());
}
else if (RootParent == null)
{
PublicOverheadMessage(MessageType.Regular, 957, false, _ticks.ToString());
}
else if (RootParent is Mobile mobile)
{
mobile.PublicOverheadMessage(MessageType.Regular, 957, false, _ticks.ToString());
}
MoveToWorld(_thrownFromLocation, map);
break;
}
);
Internalize();
}
default:
{
HeldBy?.DropHolding();
private class ThrowTarget : Target
{
public ThrowTarget(Firebomb bomb) : base(12, true, TargetFlags.None) => Bomb = bomb;
if (_users != null)
{
foreach (var m in _users)
{
if (m.Target is ThrowTarget targ && targ.Bomb == this)
{
Target.Cancel(m);
}
}
public Firebomb Bomb { get; }
_users.Clear();
_users = null;
}
protected override void OnTarget(Mobile from, object targeted)
{
Bomb.OnFirebombTarget(from, targeted);
}
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)
{
var eable = Map.GetMobilesInRange(Location, 1);
using var targets = PooledRefQueue<Mobile>.Create();
foreach (var m in eable)
{
if (m_LitBy == null || SpellHelper.ValidIndirectTarget(m_LitBy, m) &&
m_LitBy.CanBeHarmful(m, false))
{
targets.Enqueue(m);
}
}
eable.Free();
while (targets.Count > 0)
{
var victim = targets.Dequeue();
m_LitBy?.DoHarmful(victim);
AOS.Damage(victim, m_LitBy, Utility.Random(3) + 4, 0, 100, 0, 0, 0);
}
var loc = _thrownFromLocation;
var eastToWest = SpellHelper.GetEastToWest(loc, Location);
Effects.PlaySound(loc, Map, 0x20C);
var itemID = eastToWest ? 0x398C : 0x3996;
for (var i = -2; i <= 2; ++i)
{
var targetLoc = new Point3D(eastToWest ? loc.X + i : loc.X, eastToWest ? loc.Y : loc.Y + i, loc.Z);
new FireFieldSpell.FireFieldItem(itemID, targetLoc, m_LitBy, Map, TimeSpan.FromSeconds(9), i);
}
}
_timerToken.Cancel();
Delete();
break;
}
}
}
private void OnFirebombTarget(Mobile from, object obj)
{
if (Deleted || Map == Map.Internal || !IsChildOf(from.Backpack))
{
return;
}
if (obj is not IPoint3D p)
{
return;
}
SpellHelper.GetSurfaceTop(ref p);
_thrownFromLocation = new Point3D(p);
var map = Map;
from.RevealingAction();
var to = p as IEntity ?? new Entity(Serial.Zero, _thrownFromLocation, map);
Effects.SendMovingEffect(from, to, ItemID, 7, 0, false, false, Hue);
Timer.StartTimer(TimeSpan.FromSeconds(1.0),
() =>
{
if (Deleted)
{
return;
}
MoveToWorld(_thrownFromLocation, map);
}
);
Internalize();
}
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);
}
}

View file

@ -1,137 +1,140 @@
using System;
using Server.Multis;
namespace Server.Items
namespace Server.Items;
[AttributeUsage(AttributeTargets.Class)]
public class FlippableAddonAttribute : Attribute
{
[AttributeUsage(AttributeTargets.Class)]
public class FlippableAddonAttribute : Attribute
private static readonly string m_MethodName = "Flip";
private static readonly Type[] m_Params =
{
private static readonly string m_MethodName = "Flip";
typeof(Mobile), typeof(Direction)
};
private static readonly Type[] m_Params =
public FlippableAddonAttribute(params Direction[] directions) => Directions = directions;
public Direction[] Directions { get; }
public virtual void Flip(Mobile from, Item addon)
{
if (!(Directions?.Length > 1))
{
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
{
var flipMethod = addon.GetType().GetMethod(m_MethodName, m_Params);
if (flipMethod != null)
{
var index = 0;
for (var 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;
var 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[] { 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
}
}
return;
}
private void ClearComponents(Item item)
try
{
if (item is BaseAddon addon)
var flipMethod = addon.GetType().GetMethod(m_MethodName, m_Params);
if (flipMethod == null)
{
foreach (var c in addon.Components)
return;
}
var index = 0;
for (var i = 0; i < Directions.Length; i++)
{
if (addon.Direction == Directions[i])
{
c.Addon = null;
c.Delete();
index = i + 1;
break;
}
}
if (index >= Directions.Length)
{
index = 0;
}
ClearComponents(addon);
flipMethod.Invoke(addon, new object[] { from, Directions[index] });
BaseHouse house = null;
var 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;
}
addon.Clear(addon.Components);
}
else if (item is BaseAddonContainer addonContainer)
{
foreach (var c in addonContainer.Components)
{
c.Addon = null;
c.Delete();
}
ClearComponents(addon);
addonContainer.Clear(addonContainer.Components);
flipMethod.Invoke(addon, new object[] { 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 (var c in addon.Components)
{
c.Addon = null;
c.Delete();
}
addon.Clear(addon.Components);
}
else if (item is BaseAddonContainer addonContainer)
{
foreach (var c in addonContainer.Components)
{
c.Addon = null;
c.Delete();
}
addonContainer.Clear(addonContainer.Components);
}
}
}

View file

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

@ -76,10 +76,7 @@ namespace Server.Items
{
private readonly Item m_Item;
public InternalTimer(Item item) : base(TimeSpan.FromSeconds(30.0))
{
m_Item = item;
}
public InternalTimer(Item item) : base(TimeSpan.FromSeconds(30.0)) => m_Item = item;
protected override void OnTick()
{

View file

@ -145,10 +145,7 @@ namespace Server.Items
{
private readonly TrashBarrel m_Barrel;
public EmptyTimer(TrashBarrel barrel) : base(TimeSpan.FromMinutes(3.0))
{
m_Barrel = barrel;
}
public EmptyTimer(TrashBarrel barrel) : base(TimeSpan.FromMinutes(3.0)) => m_Barrel = barrel;
protected override void OnTick()
{

View file

@ -81,11 +81,7 @@ namespace Server.Items
{
private readonly Item m_Item;
public SpawnTimer(Item item) : base(TimeSpan.FromSeconds(Utility.RandomMinMax(5, 10)))
{
m_Item = item;
}
public SpawnTimer(Item item) : base(TimeSpan.FromSeconds(Utility.RandomMinMax(5, 10))) => m_Item = item;
protected override void OnTick()
{

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.AniLargeVioletFlask"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.AniRedRibbedFlask"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.AniSmallBlueFlask"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.ArcaneGem"
}

View file

@ -0,0 +1,14 @@
{
"version": 0,
"type": "Server.Items.BankCheck",
"properties": [
{
"name": "Worth",
"type": "int",
"rule": "PrimitiveTypeMigrationRule",
"ruleArguments": [
""
]
}
]
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.Beeswax"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.Blocker"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.Blood"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.BlueBeaker"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.BlueCurvedFlask"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.Bola"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.BolaBall"
}

View file

@ -0,0 +1,24 @@
{
"version": 1,
"type": "Server.Items.BroadcastCrystal",
"properties": [
{
"name": "Charges",
"type": "int",
"rule": "PrimitiveTypeMigrationRule",
"ruleArguments": [
""
]
},
{
"name": "Receivers",
"type": "System.Collections.Generic.List\u003CServer.Items.ReceiverCrystal\u003E",
"rule": "ListMigrationRule",
"ruleArguments": [
"",
"Server.Items.ReceiverCrystal",
"SerializableInterfaceMigrationRule"
]
}
]
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.ClockworkAssembly"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.CurvedFlask"
}

View file

@ -0,0 +1,19 @@
{
"version": 0,
"type": "Server.Items.DeceitBrazier",
"properties": [
{
"name": "SpawnRange",
"type": "int",
"rule": "PrimitiveTypeMigrationRule",
"ruleArguments": [
""
]
},
{
"name": "NextSpawnDelay",
"type": "System.TimeSpan",
"rule": "PrimitiveTypeMigrationRule"
}
]
}

View file

@ -0,0 +1,163 @@
{
"version": 0,
"type": "Server.Items.EffectController",
"properties": [
{
"name": "EffectDelay",
"type": "System.TimeSpan",
"rule": "PrimitiveTypeMigrationRule"
},
{
"name": "TriggerDelay",
"type": "System.TimeSpan",
"rule": "PrimitiveTypeMigrationRule"
},
{
"name": "SoundDelay",
"type": "System.TimeSpan",
"rule": "PrimitiveTypeMigrationRule"
},
{
"name": "Source",
"type": "Server.IEntity",
"rule": "SerializableInterfaceMigrationRule"
},
{
"name": "Target",
"type": "Server.IEntity",
"rule": "SerializableInterfaceMigrationRule"
},
{
"name": "Sequence",
"type": "Server.Items.EffectController",
"rule": "SerializableInterfaceMigrationRule"
},
{
"name": "FixedDirection",
"type": "bool",
"rule": "PrimitiveTypeMigrationRule",
"ruleArguments": [
""
]
},
{
"name": "Explodes",
"type": "bool",
"rule": "PrimitiveTypeMigrationRule",
"ruleArguments": [
""
]
},
{
"name": "PlaySoundAtTrigger",
"type": "bool",
"rule": "PrimitiveTypeMigrationRule",
"ruleArguments": [
""
]
},
{
"name": "EffectType",
"type": "Server.Items.ECEffectType",
"rule": "EnumMigrationRule"
},
{
"name": "EffectLayer",
"type": "Server.EffectLayer",
"rule": "EnumMigrationRule"
},
{
"name": "TriggerType",
"type": "Server.Items.EffectTriggerType",
"rule": "EnumMigrationRule"
},
{
"name": "EffectItemId",
"type": "int",
"rule": "PrimitiveTypeMigrationRule",
"ruleArguments": [
""
]
},
{
"name": "EffectHue",
"type": "int",
"rule": "PrimitiveTypeMigrationRule",
"ruleArguments": [
""
]
},
{
"name": "RenderMode",
"type": "int",
"rule": "PrimitiveTypeMigrationRule",
"ruleArguments": [
""
]
},
{
"name": "Speed",
"type": "int",
"rule": "PrimitiveTypeMigrationRule",
"ruleArguments": [
""
]
},
{
"name": "Duration",
"type": "int",
"rule": "PrimitiveTypeMigrationRule",
"ruleArguments": [
""
]
},
{
"name": "ParticleEffect",
"type": "int",
"rule": "PrimitiveTypeMigrationRule",
"ruleArguments": [
""
]
},
{
"name": "ExplodeParticleEffect",
"type": "int",
"rule": "PrimitiveTypeMigrationRule",
"ruleArguments": [
""
]
},
{
"name": "ExplodeSound",
"type": "int",
"rule": "PrimitiveTypeMigrationRule",
"ruleArguments": [
""
]
},
{
"name": "Unknown",
"type": "int",
"rule": "PrimitiveTypeMigrationRule",
"ruleArguments": [
""
]
},
{
"name": "SoundId",
"type": "int",
"rule": "PrimitiveTypeMigrationRule",
"ruleArguments": [
""
]
},
{
"name": "TriggerRange",
"type": "int",
"rule": "PrimitiveTypeMigrationRule",
"ruleArguments": [
""
]
}
]
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.EffectItem"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.EmptyCurvedFlaskE"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.EmptyCurvedFlaskW"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.EmptyRibbedFlask"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.EmptyVial"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.EmptyVialsWRack"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.ExecutionersCap"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.Firebomb"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.FullVialsWRack"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.GreenBeaker"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.GreenBottle"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.GreenCurvedFlask"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.Hourglass"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.HourglassAni"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.LargeEmptyFlask"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.LargeFlask"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.LargeVioletFlask"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.LargeYellowFlask"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.LongFlask"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.LtBlueCurvedFlask"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.MediumFlask"
}

View file

@ -0,0 +1,11 @@
{
"version": 0,
"type": "Server.Items.ReceiverCrystal",
"properties": [
{
"name": "Sender",
"type": "Server.Items.BroadcastCrystal",
"rule": "SerializableInterfaceMigrationRule"
}
]
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.RedBeaker"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.RedBottle"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.RedCurvedFlask"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.RedRibbedFlask"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.SmallBlueBottle"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.SmallBlueFlask"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.SmallBrownBottle"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.SmallEmptyFlask"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.SmallFlask"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.SmallGreenBottle"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.SmallGreenBottle2"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.SmallRedFlask"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.SmallVioletBottle"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.SmallYellowFlask"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.SpinningHourglass"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.TinyRedBottle"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.TinyYellowBottle"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.VioletRibbedFlask"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.YellowBeaker"
}