#W# New Butcher Trades man.
This commit is contained in:
parent
bf549b5386
commit
08548d7234
11 changed files with 158 additions and 31 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue