#W# Tradesman: Added citizens that stand round and 'work'

This commit is contained in:
WarrentyExpired 2026-11-04 10:08:06 -05:00
parent 7de553e357
commit 740d06a524
27 changed files with 610 additions and 459 deletions

View file

@ -1,14 +0,0 @@
TownGatheringSpot 0xFF8D
1855 2013 0
TownGatheringSpot 0xFF8D
1823 1981 0
TownGatheringSpot 0xFF8D
1798 2045 0
TownGatheringSpot 0xFF8D
1854 1967 0
TownGatheringSpot 0xFF8D
1771 2001 0

View file

@ -1,17 +0,0 @@
TownGatheringSpot 0xFF8D
3352 2402 1
TownGatheringSpot 0xFF8D
3374 2390 2
TownGatheringSpot 0xFF8D
3398 2372 2
TownGatheringSpot 0xFF8D
3374 2345 5
TownGatheringSpot 0xFF8D
3357 2347 1
TownGatheringSpot 0xFF8D
3334 2370 1

View file

@ -1,29 +0,0 @@
TownGatheringSpot 0xFF8D
1421 1188 2
TownGatheringSpot 0xFF8D
1449 1191 2
TownGatheringSpot 0xFF8D
1482 1175 2
TownGatheringSpot 0xFF8D
1527 1170 2
TownGatheringSpot 0xFF8D
1559 1158 2
TownGatheringSpot 0xFF8D
1551 1139 2
TownGatheringSpot 0xFF8D
1572 1123 2
TownGatheringSpot 0xFF8D
1612 1147 2
TownGatheringSpot 0xFF8D
1653 1146 2
TownGatheringSpot 0xFF8D
1612 1167 2

View file

@ -1,23 +0,0 @@
TownGatheringSpot 0xFF8D
1312 1758 2
TownGatheringSpot 0xFF8D
1340 1718 5
TownGatheringSpot 0xFF8D
1333 1682 0
TownGatheringSpot 0xFF8D
1416 1697 0
TownGatheringSpot 0xFF8D
1445 1689 0
TownGatheringSpot 0xFF8D
1388 1731 0
TownGatheringSpot 0xFF8D
1411 1754 0
TownGatheringSpot 0xFF8D
1445 1766 0

View file

@ -1,17 +0,0 @@
TownGatheringSpot 0xFF8D
2249 2835 0
TownGatheringSpot 0xFF8D
2258 2809 0
TownGatheringSpot 0xFF8D
2294 2805 0
TownGatheringSpot 0xFF8D
2331 2779 0
TownGatheringSpot 0xFF8D
2354 2797 0
TownGatheringSpot 0xFF8D
2352 2826 0

View file

@ -1,8 +0,0 @@
TownGatheringSpot 0xFF8D
1797 1408 2
TownGatheringSpot 0xFF8D
1779 1400 2
TownGatheringSpot 0xFF8D
1813 1370 2

View file

@ -1,17 +0,0 @@
TownGatheringSpot 0xFF8D
3351 3633 1
TownGatheringSpot 0xFF8D
3378 3614 0
TownGatheringSpot 0xFF8D
3362 3686 0
TownGatheringSpot 0xFF8D
3444 3650 0
TownGatheringSpot 0xFF8D
3453 3694 0
TownGatheringSpot 0xFF8D
3419 3670 0

View file

@ -1,17 +0,0 @@
TownGatheringSpot 0xFF8D
358 1264 0
TownGatheringSpot 0xFF8D
362 1231 1
TownGatheringSpot 0xFF8D
340 1291 0
TownGatheringSpot 0xFF8D
392 1283 0
TownGatheringSpot 0xFF8D
450 1302 0
TownGatheringSpot 0xFF8D
425 1304 0

View file

@ -1,20 +0,0 @@
TownGatheringSpot 0xFF8D
1575 2895 2
TownGatheringSpot 0xFF8D
1574 2920 2
TownGatheringSpot 0xFF8D
1530 2930 2
TownGatheringSpot 0xFF8D
1545 2997 2
TownGatheringSpot 0xFF8D
1574 2976 2
TownGatheringSpot 0xFF8D
1599 2968 2
TownGatheringSpot 0xFF8D
1549 2927 2

View file

@ -1,8 +0,0 @@
TownGatheringSpot 0xFF8D
756 1225 0
TownGatheringSpot 0xFF8D
775 1269 0
TownGatheringSpot 0xFF8D
806 1304 0

View file

@ -1,11 +0,0 @@
TownGatheringSpot 0xFF8D
412 1856 1
TownGatheringSpot 0xFF8D
454 1835 1
TownGatheringSpot 0xFF8D
393 1899 1
TownGatheringSpot 0xFF8D
381 1886 1

View file

@ -1,17 +0,0 @@
TownGatheringSpot 0xFF8D
1211 1691 0
TownGatheringSpot 0xFF8D
1244 1758 0
TownGatheringSpot 0xFF8D
1138 1751 0
TownGatheringSpot 0xFF8D
1271 1707 2
TownGatheringSpot 0xFF8D
1270 1770 2
TownGatheringSpot 0xFF8D
1275 1747 1

View file

@ -1,11 +0,0 @@
TownGatheringSpot 0xFF8D
3501 403 2
TownGatheringSpot 0xFF8D
3521 393 2
TownGatheringSpot 0xFF8D
3510 358 2
TownGatheringSpot 0xFF8D
3485 369 2

View file

@ -512,6 +512,25 @@ namespace Server.Commands
light.ItemID = m_ItemID;
}
}
else if (item is TradesmanNode node)
{
for (var i = 0; i < m_Params.Length; ++i)
{
if (m_Params[i].StartsWithOrdinal("NodeType"))
{
var indexOf = m_Params[i].IndexOfOrdinal('=');
if (indexOf >= 0)
{
node.NodeType = (TradesmanNodeType)Enum.Parse(
typeof(TradesmanNodeType),
m_Params[i][++indexOf..],
true
);
}
}
}
}
else if (item is BaseSpawner sp)
{
sp.NextSpawn = TimeSpan.Zero;

View file

@ -1,53 +0,0 @@
using System;
using Server;
namespace Server.Mobiles
{
public class GatheringCitizen : Citizen
{
private Direction _GatherDirection;
[CommandProperty(AccessLevel.GameMaster)]
public Direction GatherDirection
{
get => _GatherDirection;
set => _GatherDirection = value;
}
[Constructible]
public GatheringCitizen() : base()
{
CantWalk = true;
}
public override void OnThink()
{
base.OnThink();
if (Direction != _GatherDirection)
{
Direction = _GatherDirection;
}
}
public GatheringCitizen(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0);
writer.Write((int)_GatherDirection);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
_GatherDirection = (Direction)reader.ReadInt();
}
}
}

View file

@ -1,53 +0,0 @@
using System;
using Server;
namespace Server.Mobiles
{
public class GatheringMonster : MonsterCitizen
{
private Direction _GatherDirection;
[CommandProperty(AccessLevel.GameMaster)]
public Direction GatherDirection
{
get => _GatherDirection;
set => _GatherDirection = value;
}
[Constructible]
public GatheringMonster() : base()
{
CantWalk = true;
}
public override void OnThink()
{
base.OnThink();
if (Direction != _GatherDirection)
{
Direction = _GatherDirection;
}
}
public GatheringMonster(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0);
writer.Write((int)_GatherDirection);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
_GatherDirection = (Direction)reader.ReadInt();
}
}
}

View file

@ -1,144 +0,0 @@
using System;
using Server;
using Server.Mobiles;
using System.Collections.Generic;
namespace Server.Items
{
public class TownGatheringSpot : Item
{
private List<Mobile> _Attendees = new List<Mobile>();
[Constructible]
public TownGatheringSpot() : base(0xFF8D)
{
Name = "Town Gathering Spot";
Visible = true;
Movable = false;
Timer.DelayCall(TimeSpan.FromSeconds(1.0), CheckAndSpawn);
}
private void CheckAndSpawn()
{
if (Deleted || Map == null || Map == Map.Internal)
return;
_Attendees.RemoveAll(m => m == null || m.Deleted || !m.Alive);
if (_Attendees.Count == 0)
{
SpawnGathering();
}
}
public override void OnDoubleClick(Mobile from)
{
if (from.AccessLevel >= AccessLevel.GameMaster)
{
SpawnGathering();
from.SendMessage("Gathering forcefully respawned and reset!");
}
}
public void SpawnGathering()
{
foreach(Mobile m in _Attendees)
{
if (m != null && !m.Deleted)
m.Delete();
}
_Attendees.Clear();
Mobile east = RandomAttendee();
Mobile south = RandomAttendee();
Mobile west = RandomAttendee();
Mobile north = RandomAttendee();
PlaceAndFace(east, Location.X + 2, Location.Y, Location.Z, Direction.West);
PlaceAndFace(west, Location.X - 2, Location.Y, Location.Z, Direction.East);
PlaceAndFace(south, Location.X, Location.Y + 2, Location.Z, Direction.North);
PlaceAndFace(north, Location.X, Location.Y - 2, Location.Z, Direction.South);
}
private Mobile RandomAttendee()
{
if (Utility.RandomBool())
{
return new GatheringCitizen();
}
else
{
return new GatheringMonster();
}
}
private void PlaceAndFace(Mobile m, int x, int y, int z, Direction facing)
{
m.MoveToWorld(new Point3D(x, y, z), Map);
m.CantWalk = true;
if (m is GatheringCitizen gc)
{
gc.GatherDirection = facing;
}
else if (m is GatheringMonster gm)
{
gm.GatherDirection = facing;
}
m.Direction = facing;
_Attendees.Add(m);
}
public override void OnDelete()
{
foreach (Mobile m in _Attendees)
{
if (m != null && !m.Deleted)
{
m.Delete();
}
}
_Attendees.Clear();
base.OnDelete();
}
public TownGatheringSpot(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0);
writer.Write(_Attendees.Count);
foreach (Mobile m in _Attendees)
{
writer.Write(m);
}
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
int count = reader.ReadInt();
for (int i = 0; i < count; i++)
{
Mobile m = reader.ReadEntity<Mobile>();
if (m != null && !m.Deleted)
{
_Attendees.Add(m);
}
}
Timer.DelayCall(TimeSpan.FromSeconds(1.0), CheckAndSpawn);
}
}
}

View file

@ -0,0 +1,92 @@
using System;
using Server;
using ModernUO.Serialization;
namespace Server.Items
{
[SerializationGenerator(0, false)]
public partial class ForgeHit : Item
{
[SerializableField(0)]
private Item _smeltedItem;
[Constructible]
public ForgeHit() : base(0xFB1)
{
Name = "forge";
Movable = false;
}
public void Smelt(Mobile from)
{
if (from == null || from.Deleted) return;
// Snap the NPC to face the forge directly
from.Direction = from.GetDirectionTo(Location);
// Play the blunt swing animation
from.Animate(11, 5, 1, true, false, 0);
if (_smeltedItem == null || _smeltedItem.Deleted)
{
// Create Ore on the first strike
_smeltedItem = new Item(0x19B8);
_smeltedItem.Movable = false;
_smeltedItem.Name = "smelting ore";
// Elevate it so it sits on top of the forge
_smeltedItem.MoveToWorld(new Point3D(X, Y, Z + 5), Map);
from.PlaySound(0x2B); // Metal clink
}
else
{
// Toggle between Ore and Ingots
if (_smeltedItem.ItemID == 0x19B8)
{
_smeltedItem.ItemID = 0x1BF3;
_smeltedItem.Name = "fresh ingots";
from.PlaySound(0x208); // Fire sizzle/quench sound
}
else
{
_smeltedItem.ItemID = 0x19B8;
_smeltedItem.Name = "smelting ore";
from.PlaySound(0x2B); // Metal clink
}
}
}
// Cleanup: If a GM deletes the forge, delete the visual ore
public override void OnDelete()
{
if (_smeltedItem != null && !_smeltedItem.Deleted)
{
_smeltedItem.Delete();
}
base.OnDelete();
}
// QoL: If a GM moves the forge, bring the visual ore with it
public override void OnLocationChange(Point3D oldLocation)
{
base.OnLocationChange(oldLocation);
if (_smeltedItem != null && !_smeltedItem.Deleted)
{
_smeltedItem.Location = new Point3D(X, Y, Z + 5);
}
}
public override void OnMapChange()
{
base.OnMapChange();
if (_smeltedItem != null && !_smeltedItem.Deleted)
{
_smeltedItem.Map = Map;
}
}
public ForgeHit(Serial serial) : base(serial)
{
}
}
}

View file

@ -0,0 +1,96 @@
using System;
using Server;
using ModernUO.Serialization;
namespace Server.Items
{
[SerializationGenerator(0, false)]
public partial class AnvilHit : Item
{
// Tells ModernUO to automatically save our forged item reference during server saves
[SerializableField(0)]
private Item _ForgedItem;
// A registry of item graphics to cycle through on the anvil
private static readonly int[] _ForgedGraphics =
{
0x1BEF, // Iron Ingot
0x1B73, // Buckler
0x1BEF, // Iron Ingot
0x13B9, // Viking Sword
0x1BEF,
0x1401, // Kryss
0x1BEF,
0x1B76 // Heater Shield
};
[Constructible]
public AnvilHit() : base(0xFAF)
{
Name = "anvil";
Movable = false;
}
public void Strike(Mobile from)
{
if (from == null || from.Deleted) return;
// Snap the NPC to face the anvil directly
from.Direction = from.GetDirectionTo(Location);
// Action 11 is the human animation for swinging a 1-handed blunt weapon
from.Animate(11, 5, 1, true, false, 0);
from.PlaySound(0x2A);
// Handle the visual forged item on top of the anvil
if (_ForgedItem == null || _ForgedItem.Deleted)
{
// Create it on the first strike
_ForgedItem = new Item(_ForgedGraphics[Utility.Random(_ForgedGraphics.Length)]);
_ForgedItem.Movable = false;
_ForgedItem.Name = "a forged item";
// Elevate it by 5 Z-levels so it sits on top of the anvil mesh
_ForgedItem.MoveToWorld(new Point3D(X, Y, Z + 5), Map);
}
else
{
// Cycle to a new random graphic with each subsequent strike
_ForgedItem.ItemID = _ForgedGraphics[Utility.Random(_ForgedGraphics.Length)];
}
}
// Cleanup: If a GM deletes the anvil, delete the forged weapon too
public override void OnDelete()
{
if (_ForgedItem != null && !_ForgedItem.Deleted)
{
_ForgedItem.Delete();
}
base.OnDelete();
}
// QoL: If a GM moves the anvil, bring the forged weapon with it
public override void OnLocationChange(Point3D oldLocation)
{
base.OnLocationChange(oldLocation);
if (_ForgedItem != null && !_ForgedItem.Deleted)
{
_ForgedItem.Location = new Point3D(X, Y, Z + 5);
}
}
public override void OnMapChange()
{
base.OnMapChange();
if (_ForgedItem != null && !_ForgedItem.Deleted)
{
_ForgedItem.Map = Map;
}
}
public AnvilHit(Serial serial) : base(serial)
{
}
}
}

View file

@ -0,0 +1,87 @@
using System;
using Server;
using Server.Items;
using Server.Mobiles;
using ModernUO.Serialization;
namespace Server.Mobiles
{
[SerializationGenerator(0, false)]
public abstract partial class TradesmanCitizen : BaseCreature
{
private DateTime _NextChatter;
public override bool InitialInnocent => true;
public override bool CanTeach => false;
public override bool ClickTitle => true;
[Constructible]
public TradesmanCitizen() : base(AIType.AI_Animal, FightMode.None)
{
SpeechHue = 48;
Hue = Race.Human.RandomSkinHue();
if (Utility.RandomBool())
{
Body = 0x191; // Female
Name = NameList.RandomName("female");
}
else
{
Body = 0x190; // Male
Name = NameList.RandomName("male");
Utility.AssignRandomFacialHair(this, HairHue);
}
Utility.AssignRandomHair(this);
Title = "the tradesman";
SetStr(100);
SetDex(100);
SetInt(100);
SetHits(200, 300);
DressTradesman();
CantWalk = false; // Allow them to wander
_NextChatter = DateTime.UtcNow + TimeSpan.FromSeconds(Utility.RandomMinMax(10, 25));
}
public virtual void DressTradesman()
{
AddItem(new Shirt(Utility.RandomNeutralHue()));
AddItem(new LongPants(Utility.RandomNeutralHue()));
AddItem(new Boots(Utility.RandomNeutralHue()));
}
public override void OnMovement(Mobile m, Point3D oldLocation)
{
base.OnMovement(m, oldLocation);
if (m.Player && m.Alive && !m.Hidden && DateTime.UtcNow >= _NextChatter && InRange(m, 8))
{
if (Utility.RandomBool())
{
SayRandomChatter();
}
_NextChatter = DateTime.UtcNow + TimeSpan.FromSeconds(Utility.RandomMinMax(30, 60));
}
}
public virtual void SayRandomChatter()
{
string[] lines = {
"Lovely weather today.",
"Always so much work to do...",
"Have you heard any news from the capital?",
"I need to visit the market later."
};
Say(lines[Utility.Random(lines.Length)]);
}
public TradesmanCitizen(Serial serial) : base(serial)
{
}
}
}

View file

@ -0,0 +1,159 @@
using System;
using Server;
using Server.Mobiles;
using ModernUO.Serialization;
namespace Server.Items
{
public enum TradesmanNodeType
{
Smith,
Smelter
}
[SerializationGenerator(0, false)]
public partial class TradesmanNode : Item
{
// The generator automatically creates 'public TradesmanNodeType NodeType { get; set; }'
// and triggers OnNodeTypeChanged whenever a GM changes it via the [Props gump.
[SerializableField(0, fieldChanged: nameof(OnNodeTypeChanged))]
[SerializedCommandProperty(AccessLevel.GameMaster)]
private TradesmanNodeType _nodeType;
[SerializableField(1)]
private Mobile _worker;
[SerializableField(2)]
private Item _workstation;
[SerializableField(3)]
private DateTime _nextRespawn;
public override bool HandlesOnMovement => true;
private void OnNodeTypeChanged(TradesmanNodeType oldValue, TradesmanNodeType newValue)
{
ClearNode(); // Instantly reset the scene if a GM changes the type
}
[Constructible]
public TradesmanNode() : this(TradesmanNodeType.Smith)
{
}
[Constructible]
public TradesmanNode(TradesmanNodeType type) : base(0x1BCE)
{
Name = "tradesman node";
Movable = false;
Visible = false; // Completely invisible to players
_nodeType = type;
_nextRespawn = DateTime.UtcNow;
}
public override void OnMovement(Mobile m, Point3D oldLocation)
{
// Ignore deleted items, NPCs, hidden staff, or players on other maps
if (Deleted || m == null || !m.Player || m.Hidden || m.Map != Map)
return;
// Trigger the spawn if a player walks within 15 tiles
if (Utility.InRange(m.Location, Location, 15))
{
CheckAndSpawn();
}
}
private void CheckAndSpawn()
{
if (Deleted || Map == null || Map == Map.Internal)
return;
bool workerMissing = (_worker == null || _worker.Deleted || !_worker.Alive);
bool stationMissing = (_workstation == null || _workstation.Deleted);
// Only spawn if the scene is missing pieces AND the cooldown is finished
if ((workerMissing || stationMissing) && DateTime.UtcNow >= _nextRespawn)
{
SpawnNode();
// 10-minute cooldown so players can't spam-farm the NPC if they kill them
_nextRespawn = DateTime.UtcNow + TimeSpan.FromMinutes(10);
}
}
public void SpawnNode()
{
ClearNode(); // Clean up any lingering pieces before spawning fresh
switch (_nodeType)
{
case TradesmanNodeType.Smith:
_workstation = new AnvilHit();
_worker = new TradesmanSmith();
break;
case TradesmanNodeType.Smelter:
_workstation = new ForgeHit();
_worker = new TradesmanSmelter();
break;
}
if (_workstation != null)
{
// Place workstation exactly on the node
_workstation.MoveToWorld(Location, Map);
}
if (_worker != null)
{
if (Utility.RandomBool())
{
// Place West, face East
_worker.MoveToWorld(new Point3D(X - 1, Y, Z), Map);
_worker.Direction = Direction.East;
}
else
{
// Place North, face South
_worker.MoveToWorld(new Point3D(X, Y - 1, Z), Map);
_worker.Direction = Direction.South;
}
}
}
public void ClearNode()
{
if (_worker != null && !_worker.Deleted)
{
_worker.Delete();
}
_worker = null;
if (_workstation != null && !_workstation.Deleted)
{
_workstation.Delete();
}
_workstation = null;
}
public override void OnDoubleClick(Mobile from)
{
if (from.AccessLevel >= AccessLevel.GameMaster)
{
SpawnNode();
from.SendMessage("Citizen node forcefully respawned.");
}
}
// Automatic Garbage Collection: Deleting the node deletes the NPC and workstation
public override void OnDelete()
{
ClearNode();
base.OnDelete();
}
public TradesmanNode(Serial serial) : base(serial)
{
}
}
}

View file

@ -0,0 +1,80 @@
using System;
using Server;
using Server.Items;
using ModernUO.Serialization;
namespace Server.Mobiles
{
[SerializationGenerator(0, false)]
public partial class TradesmanSmelter : TradesmanCitizen
{
private DateTime _nextWork;
private ForgeHit _myForge;
[Constructible]
public TradesmanSmelter()
{
Title = "the smelter";
CantWalk = true;
_nextWork = DateTime.UtcNow + TimeSpan.FromSeconds(2);
}
public override void DressTradesman()
{
AddItem(new Shirt(Utility.RandomNeutralHue()));
AddItem(new ShortPants(Utility.RandomNeutralHue()));
AddItem(new Boots());
// Full apron for heat protection
AddItem(new FullApron(Utility.RandomList(0x906, 0x8A5, 0x89A)));
// Tongs instead of a hammer
Item hammer = new SmithHammer();
hammer.Movable = false;
AddItem(hammer);
}
public override void SayRandomChatter()
{
string[] lines = {
"Hot enough to melt dragon scales today.",
"More ore, always more ore.",
"Careful, don't burn yourself.",
"Nothing beats the glow of fresh ingots."
};
Say(lines[Utility.Random(lines.Length)]);
}
public override void OnThink()
{
base.OnThink();
if (DateTime.UtcNow >= _nextWork)
{
if (_myForge == null || _myForge.Deleted)
{
foreach (Item item in GetItemsInRange(1))
{
if (item is ForgeHit forge)
{
_myForge = forge;
break;
}
}
}
if (_myForge != null && !_myForge.Deleted)
{
_myForge.Smelt(this);
}
// Randomize the pause between smelts
_nextWork = DateTime.UtcNow + TimeSpan.FromSeconds(Utility.RandomMinMax(3, 6));
}
}
public TradesmanSmelter(Serial serial) : base(serial)
{
}
}
}

View file

@ -0,0 +1,77 @@
using System;
using Server;
using Server.Items;
using ModernUO.Serialization;
namespace Server.Mobiles
{
[SerializationGenerator(0, false)]
public partial class TradesmanSmith : TradesmanCitizen
{
private DateTime _NextWork;
private AnvilHit _MyAnvil;
[Constructible]
public TradesmanSmith()
{
Title = "the blacksmith";
CantWalk = true;
_NextWork = DateTime.UtcNow + TimeSpan.FromSeconds(2);
}
public override void DressTradesman()
{
AddItem(new LongPants(Utility.RandomNeutralHue()));
AddItem(new Boots());
AddItem(new HalfApron(Utility.RandomList(0x906, 0x8A5, 0x89A)));
Item hammer = new SmithHammer();
hammer.Movable = false;
AddItem(hammer);
}
public override void SayRandomChatter()
{
string[] lines = {
"This iron is stubborn today.",
"A good blade takes time and patience.",
"Hot work, but someone has to do it.",
"Need your armor patched up?"
};
Say(lines[Utility.Random(lines.Length)]);
}
public override void OnThink()
{
base.OnThink();
if (DateTime.UtcNow >= _NextWork)
{
if (_MyAnvil == null || _MyAnvil.Deleted)
{
foreach (Item item in GetItemsInRange(1))
{
if (item is AnvilHit anvil)
{
_MyAnvil = anvil;
break;
}
}
}
if (_MyAnvil != null && !_MyAnvil.Deleted)
{
_MyAnvil.Strike(this);
}
_NextWork = DateTime.UtcNow + TimeSpan.FromSeconds(Utility.RandomMinMax(2, 5));
}
}
public TradesmanSmith(Serial serial) : base(serial)
{
}
}
}