#W# Added: Town gathering spots for citizens.

This commit is contained in:
WarrentyExpired 2026-11-01 10:53:29 -05:00
parent d05a274ba6
commit 55aada83d4
6 changed files with 475 additions and 3 deletions

View file

@ -0,0 +1,127 @@
using System;
using Server;
using System.Collections;
using Server.Misc;
using Server.Network;
using Server.Commands;
using Server.Commands.Generic;
using Server.Items;
using Server.Regions;
namespace Server.Misc
{
class RandomThings
{
public static string GetRandomRealDungeon()
{
string rDungeon = "Unknown";
switch( Utility.RandomMinMax( 0, 8 ))
{
case 0: rDungeon = "Dungeon Covetous"; break;
case 1: rDungeon = "Dungeon Despice"; break;
case 2: rDungeon = "Dungeon Deceit"; break;
case 3: rDungeon = "Dungeon Destard"; break;
case 4: rDungeon = "Dungeon Despise"; break;
case 5: rDungeon = "Dungeon Shame"; break;
case 6: rDungeon = "Dungeon Hythloth"; break;
case 7: rDungeon = "Dungeon Terathan Keep"; break;
case 8: rDungeon = "Dungeon of Ice"; break;
}
return rDungeon;
}
public static string GetRandomRealTown()
{
string rTown = "Unknown";
switch( Utility.RandomMinMax( 0, 12 ))
{
case 0: rTown = "Bastion"; break;
case 1: rTown = "Mireport"; break;
case 2: rTown = "Myrish"; break;
case 3: rTown = "Teareek"; break;
case 4: rTown = "Landow"; break;
case 5: rTown = "Rivermouth"; break;
case 6: rTown = "Wharf's End"; break;
case 7: rTown = "Coldbarrow"; break;
case 8: rTown = "Fairwinds"; break;
case 9: rTown = "Stonehollow"; break;
case 10: rTown = "Cresthaven"; break;
case 11: rTown = "Port Grim"; break;
case 12: rTown = "Maerid"; break;
}
return rTown;
}
public static string GetRandomFakeDungeon()
{
string fDungeon = "Unknown";
switch( Utility.RandomMinMax( 0, 2))
{
case 0: fDungeon = "Scourge Keep"; break;
case 1: fDungeon = "Eliffs Cave"; break;
case 2: fDungeon = "Crypts Elbow"; break;
}
return fDungeon;
}
public static string GetRandomFakeTown()
{
string fTown = "Unknown";
switch( Utility.RandomMinMax( 0, 2))
{
case 0: fTown = "Klydeport"; break;
case 1: fTown = "Longshore"; break;
case 2: fTown = "Tessburg"; break;
}
return fTown;
}
public static string GetRandomAdjective()
{
string adj = "an odd";
switch( Utility.RandomMinMax( 0, 6 ))
{
case 0: adj = "an odd"; break;
case 1: adj = "a weirdly crafted"; break;
case 2: adj = "a suspiciously shiny"; break;
case 3: adj = "a blood-stained"; break;
case 4: adj = "a masterfully forged"; break;
case 5: adj = "an ancient, cursed"; break;
case 6: adj = "a rusted but reliable"; break;
}
return adj;
}
public static string GetRandomName()
{
string name = "John";
switch( Utility.RandomMinMax( 0, 7 ))
{
case 0: name = "Balthazar"; break;
case 1: name = "Kaelen"; break;
case 2: name = "Elias"; break;
case 3: name = "Gwendolyn"; break;
case 4: name = "Darius"; break;
case 5: name = "Seraphina"; break;
case 6: name = "Vander"; break;
case 7: name = "Isolde"; break;
}
return name;
}
public static string GetRandomTitle()
{
string title = "King";
switch( Utility.RandomMinMax( 0, 5 ))
{
case 0: title = "King"; break;
case 1: title = "Queen"; break;
case 2: title = "Baron"; break;
case 3: title = "Duke"; break;
case 4: title = "Lord"; break;
case 5: title = "Lady"; break;
}
return title;
}
}
}

View file

@ -2,6 +2,8 @@ using System;
using Server;
using Server.Items;
using ModernUO.Serialization;
using Server.Misc;
using Server.Custom;
namespace Server.Mobiles
{
@ -12,12 +14,29 @@ namespace Server.Mobiles
private static readonly string[] _ChatterLines = new string[]
{
// General
"Lovely weather we're having today.",
"I heard the dungeons are more dangerous than usual...",
"Just another quiet day in town.",
"I need to visit the provisioner soon.",
"Have you seen my lost dog?",
"The guards seem on edge lately."
"The guards seem on edge lately.",
"Just another quiet day in town.",
// Real Dungeon
"I heard the {rdungeon} is more dangerous than usual...",
"Stay away from {rdungeon}, I lost a good friend there.",
// Real Town
"Did you hear the recent news from {rtown}?",
// Fake Dungeon
"Did you hear that {fdungeon} has been destroyed?",
// Fake Town
"I'm thinking of packing up and moving to {ftown}.",
// Active Dungeon of the Day
"I saw a huge group of adventurers heading toward {activedungeon} this morning.",
"They say the magical energies are shifting around {activedungeon} today."
};
// Add your custom titles here!
@ -148,6 +167,29 @@ namespace Server.Mobiles
if (playerNearby)
{
string line = _ChatterLines[Utility.Random(_ChatterLines.Length)];
if (line.Contains("{rdungeon}"))
line = line.Replace("{rdungeon}", Server.Misc.RandomThings.GetRandomRealDungeon());
if (line.Contains("{rtown}"))
line = line.Replace("{rtown}", Server.Misc.RandomThings.GetRandomRealTown());
if (line.Contains("{fdungeon}"))
line = line.Replace("{fdungeon}", Server.Misc.RandomThings.GetRandomFakeDungeon());
if (line.Contains("{ftown}"))
line = line.Replace("{ftown}", Server.Misc.RandomThings.GetRandomFakeTown());
if (line.Contains("{activedungeon}"))
{
string dotd = Server.Custom.DailyDungeonManager.ActiveDungeon;
if (string.IsNullOrEmpty(dotd))
dotd = "one of the local dungeons";
line = line.Replace("{activedungeon}", dotd);
}
Say(line);
}

View file

@ -0,0 +1,65 @@
using System;
using Server;
using Server.Mobiles;
using ModernUO.Serialization;
namespace Server.Mobiles
{
[SerializationGenerator(0, false)]
public partial class FriendlyTownMonster : BaseCreature
{
private DateTime _NextChatter;
public override bool InitialInnocent => true;
[Constructible]
public FriendlyTownMonster() : base(AIType.AI_Animal, FightMode.None)
{
Name = NameList.RandomName("orc");
Title = "the civilized";
Body = Utility.RandomList(17, 1075, 1072);
BaseSoundID = 0x45A;
Karma = 1000;
Fame = 1000;
_NextChatter = DateTime.UtcNow + TimeSpan.FromSeconds(Utility.RandomMinMax(10, 20));
}
public override void OnThink()
{
base.OnThink();
if (Deleted) return;
if (DateTime.UtcNow > _NextChatter)
{
bool playerNearby = false;
foreach (Mobile m in GetMobilesInRange(8))
{
if (m != null && m.Player && m.Alive)
{
playerNearby = true;
break;
}
}
if (playerNearby)
{
string[] lines = {
"*grunts amicably*",
"Me like town. No heroes trying to stab me.",
"Guards no chase me here.",
"You have shiny coins for trading?"
};
Say(lines[Utility.Random(lines.Length)]);
}
_NextChatter = DateTime.UtcNow + TimeSpan.FromSeconds(Utility.RandomMinMax(30, 60));
}
}
public override bool IsHarmfulCriminal(Mobile target) => true;
public FriendlyTownMonster(Serial serial) : base(serial)
{
}
}
}

View file

@ -0,0 +1,53 @@
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

@ -0,0 +1,53 @@
using System;
using Server;
namespace Server.Mobiles
{
public class GatheringMonster : FriendlyTownMonster
{
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

@ -0,0 +1,132 @@
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 = new GatheringCitizen();
Mobile south = new GatheringCitizen();
Mobile west = new GatheringMonster();
Mobile north = new GatheringMonster();
PlaceAndFace(east, Location.X + 3, Location.Y, Location.Z, Direction.West);
PlaceAndFace(west, Location.X - 3, Location.Y, Location.Z, Direction.East);
PlaceAndFace(south, Location.X, Location.Y + 3, Location.Z, Direction.North);
PlaceAndFace(north, Location.X, Location.Y - 3, Location.Z, Direction.South);
}
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);
}
}
}