#W# New Butcher Trades man.

This commit is contained in:
WarrentyExpired 2026-11-04 15:08:23 -05:00
parent bf549b5386
commit 08548d7234
11 changed files with 158 additions and 31 deletions

View file

@ -32,6 +32,7 @@ Command
- [IS - Import Spawn "[IS Data/Spawns/Dungeon/Shame.json"
- [EAS - ExportAllSpawners will export all spawners on the current map to
Data/Spawns/Exported/<timestamp>.json
- [ECN - Exports all the CitizenNodes on the current map.
- [DOTD - Tells you what the current Dungeon of the Day is
- [BuildWorld - Opens a new buldworld gump
- [bandself - Applies a bandage to the player. Checks for bandages in the players pack

View file

@ -8,6 +8,7 @@ namespace Server.Items
{
public enum CitizenNodeType
{
Butcher,
Smith,
Smelter,
MeetingSpot
@ -99,6 +100,11 @@ namespace Server.Items
_workstation = new CampfireHit();
SpawnMeetingSpotCitizens();
break;
case CitizenNodeType.Butcher:
var butcherMeat = new ButcherHit();
_workstation = butcherMeat;
AddWorker(new TradesmanButcher(), butcherMeat.GetWorkerPlacement(Location));
break;
}
if (_workstation != null)

View file

@ -27,9 +27,6 @@ namespace Server.Items
public void Respawn()
{
// Proxy spawners do not handle their own respawns.
// When the monster dies, the proxy deletes itself,
// and the parent Master Spawner drops a new proxy.
}
public bool IsInSpawnBounds(Point3D loc)

View file

@ -39,7 +39,6 @@ namespace Server.Mobiles
"They say the magical energies are shifting around {activedungeon} today."
};
// Add your custom titles here!
private static readonly string[] _FemaleTitles = new string[]
{
"the citizen", "the wanderer", "the peasant",
@ -55,7 +54,7 @@ namespace Server.Mobiles
[Constructible]
public Citizen() : base(AIType.AI_Animal, FightMode.None)
{
SetSpeed(0.2, 0.4);
//SetSpeed(0.2, 0.4);
Hue = Race.Human.RandomSkinHue();
SpeechHue = 48;
//Blessed = true;
@ -142,7 +141,7 @@ namespace Server.Mobiles
Utility.AssignRandomFacialHair(this);
}
_NextChatter = DateTime.UtcNow + TimeSpan.FromSeconds(Utility.RandomMinMax(5, 15));
_NextChatter = DateTime.UtcNow + TimeSpan.FromSeconds(Utility.RandomMinMax(0, 10));
}
public override void OnThink()
@ -193,7 +192,7 @@ namespace Server.Mobiles
Say(line);
}
_NextChatter = DateTime.UtcNow + TimeSpan.FromSeconds(Utility.RandomMinMax(30, 60));
_NextChatter = DateTime.UtcNow + TimeSpan.FromSeconds(Utility.RandomMinMax(10, 15));
}
}

View file

@ -54,16 +54,14 @@ namespace Server.Mobiles
if (playerNearby)
{
// Fetch the correct lines based on their current body type
string[] lines = GetChatterLines();
Say(lines[Utility.Random(lines.Length)]);
}
_NextChatter = DateTime.UtcNow + TimeSpan.FromSeconds(Utility.RandomMinMax(30, 60));
_NextChatter = DateTime.UtcNow + TimeSpan.FromSeconds(Utility.RandomMinMax(10, 15));
}
}
// --- NEW: Helper method to organize dialogue by body ID ---
private string[] GetChatterLines()
{
switch ((int)Body)

View file

@ -0,0 +1,69 @@
using System;
using Server;
using ModernUO.Serialization;
namespace Server.Items
{
[SerializationGenerator(0, false)]
public partial class ButcherHit : Item
{
private static readonly int[] _MeatGraphics = new int[]
{
0x1871, // Meat faces south
0x1872, // Meat faces east
0x1873, // Meat faces south
0x1874 // Meat faces east
};
[Constructible]
public ButcherHit() : base(0x1874)
{
ItemID = _MeatGraphics[Utility.Random(_MeatGraphics.Length)];
Name = "meat";
Movable = false;
}
[Constructible]
public ButcherHit(int itemID) : base(itemID)
{
Name = "meat";
Movable = false;
}
public (Point3D loc, Direction dir) GetWorkerPlacement(Point3D baseLoc)
{
switch (ItemID)
{
case 0x1871:
return (new Point3D(baseLoc.X, baseLoc.Y + 1, baseLoc.Z), Direction.North);
case 0x1872:
return (new Point3D(baseLoc.X + 1, baseLoc.Y, baseLoc.Z), Direction.East);
case 0x1873:
return (new Point3D(baseLoc.X, baseLoc.Y + 1, baseLoc.Z), Direction.North);
case 0x1874:
return (new Point3D(baseLoc.X + 1, baseLoc.Y, baseLoc.Z), Direction.East);
default:
if (Utility.RandomBool())
{
return (new Point3D(baseLoc.X - 1, baseLoc.Y, baseLoc.Z), Direction.East);
}
return (new Point3D(baseLoc.X, baseLoc.Y - 1, baseLoc.Z), Direction.South);
}
}
public void Strike(Mobile from)
{
if (from == null || from.Deleted) return;
from.Direction = from.GetDirectionTo(Location);
from.Animate(11, 5, 1, true, false, 0);
from.PlaySound(0x13F);
}
public ButcherHit(Serial serial) : base(serial)
{
}
}
}

View file

@ -21,26 +21,21 @@ namespace Server.Items
{
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;
@ -56,7 +51,6 @@ namespace Server.Items
}
}
// Cleanup: If a GM deletes the forge, delete the visual ore
public override void OnDelete()
{
if (_smeltedItem != null && !_smeltedItem.Deleted)
@ -66,7 +60,6 @@ namespace Server.Items
base.OnDelete();
}
// QoL: If a GM moves the forge, bring the visual ore with it
public override void OnLocationChange(Point3D oldLocation)
{
base.OnLocationChange(oldLocation);

View file

@ -7,11 +7,9 @@ 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
@ -35,32 +33,25 @@ namespace Server.Items
{
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)
@ -70,7 +61,6 @@ namespace Server.Items
base.OnDelete();
}
// QoL: If a GM moves the anvil, bring the forged weapon with it
public override void OnLocationChange(Point3D oldLocation)
{
base.OnLocationChange(oldLocation);

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 TradesmanButcher : TradesmanCitizen
{
private DateTime _NextWork;
private ButcherHit _MyMeat;
[Constructible]
public TradesmanButcher()
{
Title = "the butcher";
CantWalk = true;
_NextWork = DateTime.UtcNow + TimeSpan.FromSeconds(2);
}
public override void DressTradesman()
{
AddItem(new LongPants(Utility.RandomNeutralHue()));
AddItem(new Boots());
AddItem(new FullApron(Utility.RandomList(0x906, 0x8A5, 0x89A)));
Item cleaver = new Cleaver();
cleaver.Movable = false;
AddItem(cleaver);
}
public override void SayRandomChatter()
{
string[] lines = {
"This should last a while.",
"AhHh! Hit bone again.",
"Thankless work, but someone has to do it.",
"Need some quality ribs?"
};
Say(lines[Utility.Random(lines.Length)]);
}
public override void OnThink()
{
base.OnThink();
if (DateTime.UtcNow >= _NextWork)
{
if (_MyMeat == null || _MyMeat.Deleted)
{
foreach (Item item in GetItemsInRange(1))
{
if (item is ButcherHit meat)
{
_MyMeat = meat;
break;
}
}
}
if (_MyMeat != null && !_MyMeat.Deleted)
{
_MyMeat.Strike(this);
}
_NextWork = DateTime.UtcNow + TimeSpan.FromSeconds(Utility.RandomMinMax(2, 5));
}
}
public TradesmanButcher(Serial serial) : base(serial)
{
}
}
}

View file

@ -25,10 +25,8 @@ namespace Server.Mobiles
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);
@ -68,7 +66,6 @@ namespace Server.Mobiles
_myForge.Smelt(this);
}
// Randomize the pause between smelts
_nextWork = DateTime.UtcNow + TimeSpan.FromSeconds(Utility.RandomMinMax(3, 6));
}
}

View file

@ -43,7 +43,7 @@ namespace Server.Mobiles
DressTradesman();
CantWalk = false; // Allow them to wander
CantWalk = false;
_NextChatter = DateTime.UtcNow + TimeSpan.FromSeconds(Utility.RandomMinMax(10, 25));
}