ModernUO/Projects/UOContent/Mobiles/Townfolk/Citizens/CitizenNode.cs

213 lines
6.1 KiB
C#

using System;
using System.Collections.Generic;
using Server;
using Server.Mobiles;
using ModernUO.Serialization;
namespace Server.Items
{
public enum CitizenNodeType
{
Butcher,
Smith,
Smelter,
MeetingSpot
}
[SerializationGenerator(0, false)]
public partial class CitizenNode : Item
{
[SerializableField(0, fieldChanged: nameof(OnNodeTypeChanged))]
[SerializedCommandProperty(AccessLevel.GameMaster)]
private CitizenNodeType _nodeType;
[SerializableField(1)]
private List<Mobile> _workers = new();
[SerializableField(2)]
private Item _workstation;
[SerializableField(3)]
private DateTime _nextRespawn;
public override bool HandlesOnMovement => true;
private void OnNodeTypeChanged(CitizenNodeType oldValue, CitizenNodeType newValue)
{
ClearNode();
}
[Constructible]
public CitizenNode() : this(CitizenNodeType.Smith)
{
}
[Constructible]
public CitizenNode(CitizenNodeType type) : base(0x1BCE)
{
Name = "citizen node";
Movable = false;
Visible = false;
_nodeType = type;
_nextRespawn = DateTime.UtcNow;
}
public override void OnMovement(Mobile m, Point3D oldLocation)
{
if (Deleted || m == null || !m.Player || m.Hidden || m.Map != Map)
return;
if (Utility.InRange(m.Location, Location, 15))
{
CheckAndSpawn();
}
}
private void CheckAndSpawn()
{
if (Deleted || Map == null || Map == Map.Internal)
return;
_workers.RemoveAll(w => w == null || w.Deleted || !w.Alive);
bool workersMissing = _workers.Count == 0;
bool stationMissing = (_workstation == null || _workstation.Deleted);
if ((workersMissing || stationMissing) && DateTime.UtcNow >= _nextRespawn)
{
SpawnNode();
_nextRespawn = DateTime.UtcNow + TimeSpan.FromMinutes(10);
}
}
public void SpawnNode()
{
ClearNode();
switch (_nodeType)
{
case CitizenNodeType.Smith:
_workstation = new AnvilHit();
AddWorker(new TradesmanSmith(), GetSingleWorkerSpot());
break;
case CitizenNodeType.Smelter:
_workstation = new ForgeHit();
AddWorker(new TradesmanSmelter(), GetSingleWorkerSpot());
break;
case CitizenNodeType.MeetingSpot:
_workstation = new CampfireHit();
SpawnMeetingSpotCitizens();
break;
case CitizenNodeType.Butcher:
var butcherMeat = new ButcherHit();
_workstation = butcherMeat;
AddWorker(new TradesmanButcher(), butcherMeat.GetWorkerPlacement(Location));
break;
}
if (_workstation != null)
{
_workstation.MoveToWorld(Location, Map);
}
}
public void RefreshNode()
{
ClearNode();
_nextRespawn = DateTime.UtcNow; // Ready to spawn fresh NPCs as soon as a player is nearby
}
private (Point3D loc, Direction dir) GetSingleWorkerSpot()
{
if (Utility.RandomBool())
{
return (new Point3D(X - 1, Y, Z), Direction.East);
}
return (new Point3D(X, Y - 1, Z), Direction.South);
}
private void AddWorker(Mobile worker, (Point3D loc, Direction dir) spawnData)
{
if (worker == null) return;
worker.MoveToWorld(spawnData.loc, Map);
worker.Direction = spawnData.dir;
_workers.Add(worker);
}
private void SpawnMeetingSpotCitizens()
{
(Point3D loc, Direction dir)[] potentialSpots = new[]
{
(new Point3D(X - 2, Y, Z), Direction.East), // West spot -> Face East
(new Point3D(X + 2, Y, Z), Direction.West), // East spot -> Face West
(new Point3D(X, Y - 2, Z), Direction.South), // North spot -> Face South
(new Point3D(X, Y + 2, Z), Direction.North) // South spot -> Face North
};
Utility.Shuffle(potentialSpots);
int countToSpawn = Utility.RandomMinMax(2, 4);
for (int i = 0; i < countToSpawn && i < potentialSpots.Length; i++)
{
var spot = potentialSpots[i];
Mobile attendee;
if (Utility.RandomBool())
{
var gc = new GatheringCitizen();
gc.GatherDirection = spot.dir;
attendee = gc;
}
else
{
var gm = new GatheringMonster();
gm.GatherDirection = spot.dir;
attendee = gm;
}
AddWorker(attendee, spot);
}
}
public void ClearNode()
{
foreach (var worker in _workers)
{
if (worker != null && !worker.Deleted)
{
worker.Delete();
}
}
_workers.Clear();
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.");
}
}
public override void OnDelete()
{
ClearNode();
base.OnDelete();
}
public CitizenNode(Serial serial) : base(serial)
{
}
}
}