feat: New Jail System (#2215)
New Jail System for MUO ---------------------------- Commands: [jail [player] [reason] - Jail with time escalation per offense (5 to 120 minutes, GM only) [unjail [player] - Manual release from jail regardless of time (GM only) [jailinfo [player] - Check jail status and history (GM only) [jailrecord - Checks their own jail record stats (player access) Jail/Unjail can be found in the client view of a player in Admin Gump  Jail record gump, invoked with [jailrecord (30 second cooldown) 
This commit is contained in:
parent
c2c486c06e
commit
5f0561b7fc
8 changed files with 669 additions and 4 deletions
|
|
@ -1479,4 +1479,49 @@ public static partial class Utility
|
||||||
|
|
||||||
return DateTime.SpecifyKind(local - tz.GetUtcOffset(local), DateTimeKind.Utc);
|
return DateTime.SpecifyKind(local - tz.GetUtcOffset(local), DateTimeKind.Utc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string FormatTimeCompact(this TimeSpan ts, bool showSeconds = false)
|
||||||
|
{
|
||||||
|
using var sb = ValueStringBuilder.Create();
|
||||||
|
if (ts.Days >= 1)
|
||||||
|
{
|
||||||
|
sb.Append($"{ts.Days}d");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sb.Length > 0)
|
||||||
|
{
|
||||||
|
sb.Append($" {ts.Hours}h");
|
||||||
|
}
|
||||||
|
else if (ts.Hours >= 1)
|
||||||
|
{
|
||||||
|
sb.Append($"{ts.Hours}h");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sb.Length > 0)
|
||||||
|
{
|
||||||
|
sb.Append($" {ts.Minutes}m");
|
||||||
|
}
|
||||||
|
else if (ts.Minutes >= 1)
|
||||||
|
{
|
||||||
|
sb.Append($"{ts.Minutes}m");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showSeconds)
|
||||||
|
{
|
||||||
|
if (sb.Length > 0)
|
||||||
|
{
|
||||||
|
sb.Append($" {ts.Seconds}s");
|
||||||
|
}
|
||||||
|
else if (ts.Seconds >= 1)
|
||||||
|
{
|
||||||
|
sb.Append($"{ts.Seconds}s");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (sb.Length == 0)
|
||||||
|
{
|
||||||
|
sb.Append("0m");
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -92,12 +92,12 @@ namespace Server.Gumps
|
||||||
|
|
||||||
AddPage(0);
|
AddPage(0);
|
||||||
|
|
||||||
AddBackground(0, 0, 420, 440, 5054);
|
AddBackground(0, 0, 420, 480, 5054);
|
||||||
|
|
||||||
AddBlackAlpha(10, 10, 170, 100);
|
AddBlackAlpha(10, 10, 170, 100);
|
||||||
AddBlackAlpha(190, 10, 220, 100);
|
AddBlackAlpha(190, 10, 220, 100);
|
||||||
AddBlackAlpha(10, 120, 400, 260);
|
AddBlackAlpha(10, 120, 400, 300);
|
||||||
AddBlackAlpha(10, 390, 400, 40);
|
AddBlackAlpha(10, 430, 400, 40);
|
||||||
|
|
||||||
AddPageButton(
|
AddPageButton(
|
||||||
10,
|
10,
|
||||||
|
|
@ -141,7 +141,7 @@ namespace Server.Gumps
|
||||||
|
|
||||||
if (notice != null)
|
if (notice != null)
|
||||||
{
|
{
|
||||||
AddHtml(12, 392, 396, 36, notice.Color(LabelColor32));
|
AddHtml(20, 440, 396, 36, notice.Color(LabelColor32));
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (pageType)
|
switch (pageType)
|
||||||
|
|
@ -619,6 +619,14 @@ namespace Server.Gumps
|
||||||
|
|
||||||
AddButtonLabeled(20, y, GetButtonID(7, 12), "Kill");
|
AddButtonLabeled(20, y, GetButtonID(7, 12), "Kill");
|
||||||
AddButtonLabeled(200, y, GetButtonID(7, 13), "Resurrect");
|
AddButtonLabeled(200, y, GetButtonID(7, 13), "Resurrect");
|
||||||
|
y += 20;
|
||||||
|
|
||||||
|
AddButtonLabeled(20, y, GetButtonID(7, 15), "Jail");
|
||||||
|
AddButtonLabeled(200, y, GetButtonID(7, 16), "Unjail");
|
||||||
|
y += 25;
|
||||||
|
|
||||||
|
AddLabel(20, y, LabelHue, "Jail Reason:");
|
||||||
|
AddTextField(100, y, 300, 20, 1);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -3801,6 +3809,33 @@ namespace Server.Gumps
|
||||||
sendGump = false;
|
sendGump = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 15:
|
||||||
|
{
|
||||||
|
var reason = info.GetTextEntry(1)?.Trim();
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(reason))
|
||||||
|
{
|
||||||
|
reason = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandLogging.WriteLine(
|
||||||
|
from,
|
||||||
|
$"{from.AccessLevel} {CommandLogging.Format(from)} jailing {CommandLogging.Format(m)} - Reason: {reason}"
|
||||||
|
);
|
||||||
|
InvokeCommand($"Jail {m.Name} \"{reason}\"");
|
||||||
|
notice = $"Player has been sent to jail. Reason: {reason}";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 16:
|
||||||
|
{
|
||||||
|
CommandLogging.WriteLine(
|
||||||
|
from,
|
||||||
|
$"{from.AccessLevel} {CommandLogging.Format(from)} unjailing {CommandLogging.Format(m)}"
|
||||||
|
);
|
||||||
|
InvokeCommand($"Unjail {m.Name}");
|
||||||
|
notice = "Player has been unjailed.";
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sendGump)
|
if (sendGump)
|
||||||
|
|
|
||||||
43
Projects/UOContent/Migrations/Server.Systems.JailSystem.JailRecord.v0.json
generated
Normal file
43
Projects/UOContent/Migrations/Server.Systems.JailSystem.JailRecord.v0.json
generated
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
{
|
||||||
|
"version": 0,
|
||||||
|
"type": "Server.Systems.JailSystem.JailRecord",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "JailCount",
|
||||||
|
"type": "int",
|
||||||
|
"rule": "PrimitiveTypeMigrationRule",
|
||||||
|
"ruleArguments": [
|
||||||
|
""
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "LastJailed",
|
||||||
|
"type": "System.DateTime",
|
||||||
|
"rule": "PrimitiveTypeMigrationRule",
|
||||||
|
"ruleArguments": [
|
||||||
|
""
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "JailEndTime",
|
||||||
|
"type": "System.DateTime",
|
||||||
|
"rule": "PrimitiveTypeMigrationRule",
|
||||||
|
"ruleArguments": [
|
||||||
|
""
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "LastJailReason",
|
||||||
|
"type": "string",
|
||||||
|
"rule": "PrimitiveTypeMigrationRule",
|
||||||
|
"ruleArguments": [
|
||||||
|
""
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "JailedBy",
|
||||||
|
"type": "Server.Mobile",
|
||||||
|
"rule": "SerializableInterfaceMigrationRule"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
25
Projects/UOContent/Systems/JailSystem/JailRecord.cs
Normal file
25
Projects/UOContent/Systems/JailSystem/JailRecord.cs
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
using System;
|
||||||
|
using ModernUO.Serialization;
|
||||||
|
|
||||||
|
namespace Server.Systems.JailSystem;
|
||||||
|
|
||||||
|
[SerializationGenerator(0)]
|
||||||
|
public partial class JailRecord
|
||||||
|
{
|
||||||
|
[SerializableField(0)]
|
||||||
|
private int _jailCount;
|
||||||
|
|
||||||
|
[SerializableField(1)]
|
||||||
|
private DateTime _lastJailed;
|
||||||
|
|
||||||
|
[SerializableField(2)]
|
||||||
|
private DateTime _jailEndTime;
|
||||||
|
|
||||||
|
[SerializableField(3)]
|
||||||
|
private string _lastJailReason;
|
||||||
|
|
||||||
|
[SerializableField(4)]
|
||||||
|
private Mobile _jailedBy;
|
||||||
|
|
||||||
|
public bool IsCurrentlyJailed => JailEndTime > Core.Now;
|
||||||
|
}
|
||||||
90
Projects/UOContent/Systems/JailSystem/JailRecordGump.cs
Normal file
90
Projects/UOContent/Systems/JailSystem/JailRecordGump.cs
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
/*************************************************************************
|
||||||
|
* ModernUO *
|
||||||
|
* Copyright 2019-2025 - ModernUO Development Team *
|
||||||
|
* Email: hi@modernuo.com *
|
||||||
|
* File: JailRecordGump.cs *
|
||||||
|
* *
|
||||||
|
* This program is free software: you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using Server.Gumps;
|
||||||
|
using Server.Mobiles;
|
||||||
|
|
||||||
|
namespace Server.Systems.JailSystem;
|
||||||
|
|
||||||
|
public class JailRecordGump : StaticGump<JailRecordGump>
|
||||||
|
{
|
||||||
|
private readonly PlayerMobile _player;
|
||||||
|
private readonly JailRecord _record;
|
||||||
|
|
||||||
|
public JailRecordGump(PlayerMobile player, JailRecord record) : base(0, 0)
|
||||||
|
{
|
||||||
|
_player = player;
|
||||||
|
_record = record;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void BuildLayout(ref StaticGumpBuilder builder)
|
||||||
|
{
|
||||||
|
builder.SetNoResize();
|
||||||
|
|
||||||
|
builder.AddPage();
|
||||||
|
builder.AddBackground(0, 0, 330, 300, 9200);
|
||||||
|
builder.AddAlphaRegion(10, 10, 310, 280);
|
||||||
|
|
||||||
|
builder.AddHtml(20, 20, 360, 30, "<size=6 color=#FF6600>JAIL RECORD</basefont>");
|
||||||
|
builder.AddHtmlPlaceholder(20, 60, 360, 25, "playerName");
|
||||||
|
builder.AddHtmlPlaceholder(20, 80, 360, 25, "jailCount");
|
||||||
|
builder.AddHtmlPlaceholder(20, 100, 360, 25, "lastJailed");
|
||||||
|
builder.AddHtmlPlaceholder(20, 120, 290, 25, "jailReason");
|
||||||
|
|
||||||
|
builder.AddHtmlPlaceholder(20, 150, 360, 25, "jailStatus");
|
||||||
|
builder.AddHtmlPlaceholder(20, 170, 360, 25, "jailTime");
|
||||||
|
|
||||||
|
builder.AddHtml(20, 200, 360, 25, "<size=4 color=#CCCCCC>If you believe you were jailed in error,</basefont>");
|
||||||
|
builder.AddHtml(20, 220, 360, 25, "<size=4 color=#CCCCCC>please contact staff through normal channels.</basefont>");
|
||||||
|
builder.AddHtml(20, 240, 360, 25, "<size=4 color=#CCCCCC>Each time you are jailed, the wait increases.</basefont>");
|
||||||
|
builder.AddHtml(20, 260, 360, 25, "<size=4 color=#CCCCCC>Follow all shard rules to avoid future jail time.</basefont>");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void BuildStrings(ref GumpStringsBuilder builder)
|
||||||
|
{
|
||||||
|
builder.SetHtmlText("playerName", $"Player: {_player.Name}", 0xFFFF00, 5);
|
||||||
|
builder.SetHtmlText("jailCount", $"Jail Count: {_record.JailCount}", 0xFFFFFF, 5);
|
||||||
|
|
||||||
|
if (_record.LastJailed > DateTime.MinValue)
|
||||||
|
{
|
||||||
|
builder.SetHtmlText("lastJailed", $"Last Jailed: {_record.LastJailed:yyyy/MM/dd HH:mm}", 0xFFFFFF, 5);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
builder.SetHtmlText("lastJailed", "Last Jailed: Never", 0xFFFFFF, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(_record.LastJailReason))
|
||||||
|
{
|
||||||
|
builder.SetHtmlText("jailReason", "Jail Reason: None given.", 0xFFFFFF, 4);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
builder.SetHtmlText("jailReason", $"Jail Reason: {_record.LastJailReason}", 0xFFFFFF, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_record.IsCurrentlyJailed)
|
||||||
|
{
|
||||||
|
builder.SetHtmlText("jailStatus", "Status: Currently Jailed", 0xFF6666, 5);
|
||||||
|
builder.SetHtmlText("jailTime", $"Jail Time: {(_record.JailEndTime - Core.Now).FormatTimeCompact()}", 0xFF6666, 5);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
builder.SetHtmlText("jailStatus", "Status: Not Jailed", 0x00FF00, 5);
|
||||||
|
builder.SetStringSlot("jailTime", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
427
Projects/UOContent/Systems/JailSystem/JailSystem.cs
Normal file
427
Projects/UOContent/Systems/JailSystem/JailSystem.cs
Normal file
|
|
@ -0,0 +1,427 @@
|
||||||
|
/*************************************************************************
|
||||||
|
* ModernUO *
|
||||||
|
* Copyright 2019-2025 - ModernUO Development Team *
|
||||||
|
* Email: hi@modernuo.com *
|
||||||
|
* File: JailSystem.cs *
|
||||||
|
* *
|
||||||
|
* This program is free software: you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Server.Mobiles;
|
||||||
|
using Server.Network;
|
||||||
|
using Server.Commands;
|
||||||
|
using Server.Gumps;
|
||||||
|
|
||||||
|
namespace Server.Systems.JailSystem;
|
||||||
|
|
||||||
|
public class JailSystem : GenericPersistence
|
||||||
|
{
|
||||||
|
// Jail locations (modify if using custom maps)
|
||||||
|
// felucca void
|
||||||
|
private static readonly Point3D[] JailLocations =
|
||||||
|
[
|
||||||
|
new(5276, 1164, 0),
|
||||||
|
new(5286, 1164, 0),
|
||||||
|
new(5296, 1164, 0),
|
||||||
|
new(5306, 1164, 0),
|
||||||
|
new(5276, 1174, 0),
|
||||||
|
new(5286, 1174, 0),
|
||||||
|
new(5296, 1174, 0),
|
||||||
|
new(5306, 1174, 0),
|
||||||
|
new(5283, 1184, 0),
|
||||||
|
new(5304, 1184, 0)
|
||||||
|
];
|
||||||
|
|
||||||
|
// Jail map, change this for custom maps
|
||||||
|
public static readonly Map JailMap = Map.Felucca;
|
||||||
|
private static readonly JailRecord EmptyRecord = new();
|
||||||
|
|
||||||
|
private static readonly HashSet<PlayerMobile> CurrentlyBeingJailed = [];
|
||||||
|
private static readonly Dictionary<PlayerMobile, JailRecord> PlayerJailRecords = [];
|
||||||
|
private static readonly Dictionary<PlayerMobile, Timer> JailTimers = [];
|
||||||
|
|
||||||
|
// Jail time scales from 5 minutes to 12 hours based on the number of offenses
|
||||||
|
private static readonly TimeSpan MinJailTime = TimeSpan.FromMinutes(5);
|
||||||
|
private static readonly TimeSpan MaxJailTime = TimeSpan.FromHours(12);
|
||||||
|
|
||||||
|
// Release location, change this for custom maps
|
||||||
|
private static readonly Point3D ReleaseLocation = new(1444, 1697, 10); // Britain Bank
|
||||||
|
public static readonly Map ReleaseMap = Map.Felucca;
|
||||||
|
|
||||||
|
private static JailSystem Instance;
|
||||||
|
|
||||||
|
// [jail <player> [reason] - Jail with time escalation per offense (GM only)
|
||||||
|
// [unjail <player> - Manual release from jail regardless of time (GM only)
|
||||||
|
// [jailinfo <player> - Check jail status and history (GM only)
|
||||||
|
// [jailrecord - Checks their own jail record stats (player access)
|
||||||
|
|
||||||
|
public static void Configure()
|
||||||
|
{
|
||||||
|
Instance = new JailSystem();
|
||||||
|
CommandSystem.Register("Jail", AccessLevel.GameMaster, Jail_OnCommand);
|
||||||
|
CommandSystem.Register("Unjail", AccessLevel.GameMaster, Unjail_OnCommand);
|
||||||
|
CommandSystem.Register("JailInfo", AccessLevel.Counselor, JailInfo_OnCommand);
|
||||||
|
CommandSystem.Register("JailRecord", AccessLevel.Player, MyJailInfo_OnCommand);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JailSystem() : base("Jail", 3)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can be used by other systems to check player jail status
|
||||||
|
public static bool IsPlayerJailed(PlayerMobile player) =>
|
||||||
|
PlayerJailRecords.GetValueOrDefault(player)?.IsCurrentlyJailed == true;
|
||||||
|
|
||||||
|
private static TimeSpan CalculateJailTime(int jailCount)
|
||||||
|
{
|
||||||
|
var totalMinutes = MinJailTime + (jailCount - 1) * (MaxJailTime - MinJailTime) / 9.0;
|
||||||
|
|
||||||
|
return totalMinutes.Clamp(MinJailTime, MaxJailTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void JailPlayer(Mobile from, PlayerMobile player, string reason = "")
|
||||||
|
{
|
||||||
|
if (!CurrentlyBeingJailed.Add(player))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!PlayerJailRecords.TryGetValue(player, out var record))
|
||||||
|
{
|
||||||
|
PlayerJailRecords[player] = record = new JailRecord();
|
||||||
|
}
|
||||||
|
|
||||||
|
record.JailCount++;
|
||||||
|
record.LastJailed = Core.Now;
|
||||||
|
record.LastJailReason = reason;
|
||||||
|
record.JailedBy = from;
|
||||||
|
|
||||||
|
var jailTime = CalculateJailTime(record.JailCount);
|
||||||
|
record.JailEndTime = Core.Now + jailTime;
|
||||||
|
|
||||||
|
player.Frozen = true;
|
||||||
|
player.SendMessage(0x35, "You are being sent to jail!");
|
||||||
|
player.PlaySound(0x204);
|
||||||
|
|
||||||
|
CommandLogging.WriteLine(from, $"Player {player.Name} jailed for: {reason} (Offense #{record.JailCount}, {jailTime.TotalMinutes} minutes)");
|
||||||
|
|
||||||
|
foreach (var ns in NetState.Instances)
|
||||||
|
{
|
||||||
|
if (ns.Mobile is PlayerMobile staff && staff.AccessLevel >= AccessLevel.Counselor)
|
||||||
|
{
|
||||||
|
staff.SendMessage(0x35, $"Player {player.Name} has been jailed for {jailTime.TotalMinutes} minutes. Reason: {reason} (Offense #{record.JailCount})");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer.DelayCall(TimeSpan.FromSeconds(2.0), DismountPlayer, from, player, jailTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DismountPlayer(Mobile from, PlayerMobile player, TimeSpan jailTime)
|
||||||
|
{
|
||||||
|
if (player.Mount != null)
|
||||||
|
{
|
||||||
|
var mount = player.Mount;
|
||||||
|
mount.Rider = null;
|
||||||
|
player.SendMessage(0x35, "You have been dismounted.");
|
||||||
|
if (mount is BaseCreature bc)
|
||||||
|
{
|
||||||
|
if (bc.Summoned)
|
||||||
|
{
|
||||||
|
bc.Dispel(bc);
|
||||||
|
}
|
||||||
|
else // Stable the pet
|
||||||
|
{
|
||||||
|
bc.ControlTarget = null;
|
||||||
|
bc.ControlOrder = OrderType.Stay;
|
||||||
|
bc.Internalize();
|
||||||
|
|
||||||
|
bc.SetControlMaster(null);
|
||||||
|
|
||||||
|
bc.IsStabled = true;
|
||||||
|
bc.StabledBy = from;
|
||||||
|
player.AddStabled(bc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandLogging.WriteLine(from, $"Player {player.Name} dismounted before jail teleport");
|
||||||
|
|
||||||
|
Timer.DelayCall(TimeSpan.FromSeconds(3.0), TeleportToJail, from, player, jailTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void TeleportToJail(Mobile from, PlayerMobile player, TimeSpan jailTime)
|
||||||
|
{
|
||||||
|
var jailLocation = JailLocations.RandomElement();
|
||||||
|
|
||||||
|
player.MoveToWorld(jailLocation, JailMap);
|
||||||
|
|
||||||
|
player.SendMessage(0x35, "Use [jailrecord to pull up your record.");
|
||||||
|
player.SendMessage(0x35, "Please contact staff if you believe this was a mistake.");
|
||||||
|
|
||||||
|
CommandLogging.WriteLine(from, $"Player {player.Name} teleported to jail at {jailLocation}");
|
||||||
|
|
||||||
|
Timer.DelayCall(TimeSpan.FromSeconds(5.0), UnfreezePlayer, from, player, jailTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void UnfreezePlayer(Mobile from, PlayerMobile player, TimeSpan jailTime)
|
||||||
|
{
|
||||||
|
player.Frozen = false;
|
||||||
|
|
||||||
|
CommandLogging.WriteLine(from, $"Player {player.Name} unfrozen in jail");
|
||||||
|
|
||||||
|
CurrentlyBeingJailed.Remove(player);
|
||||||
|
|
||||||
|
var releaseTimer = Timer.DelayCall(jailTime, ReleasePlayer, from, player);
|
||||||
|
JailTimers[player] = releaseTimer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ReleasePlayer(Mobile from, PlayerMobile player)
|
||||||
|
{
|
||||||
|
if (PlayerJailRecords.TryGetValue(player, out var record))
|
||||||
|
{
|
||||||
|
record.JailEndTime = Core.Now;
|
||||||
|
}
|
||||||
|
|
||||||
|
JailTimers.Remove(player);
|
||||||
|
|
||||||
|
// Freeze player for release sequence
|
||||||
|
player.Frozen = true;
|
||||||
|
player.SendMessage(0x35, "You have been released from jail!");
|
||||||
|
player.PlaySound(0x1FF);
|
||||||
|
|
||||||
|
if (from != null)
|
||||||
|
{
|
||||||
|
CommandLogging.WriteLine(from, $"Player {player.Name} released from jail, starting teleport sequence");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var ns in NetState.Instances)
|
||||||
|
{
|
||||||
|
if (ns.Mobile is PlayerMobile staff && staff.AccessLevel >= AccessLevel.Counselor)
|
||||||
|
{
|
||||||
|
staff.SendMessage(0x35, $"Player {player.Name} has been released from jail.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer.DelayCall(TimeSpan.FromSeconds(5.0), TeleportFromJail, from, player);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void TeleportFromJail(Mobile from, PlayerMobile player)
|
||||||
|
{
|
||||||
|
player.MoveToWorld(ReleaseLocation, ReleaseMap);
|
||||||
|
|
||||||
|
if (from != null)
|
||||||
|
{
|
||||||
|
CommandLogging.WriteLine(player, $"Player {player.Name} teleported from jail to {ReleaseLocation}");
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer.DelayCall(TimeSpan.FromSeconds(5.0), UnfreezeFromRelease, from, player);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void UnfreezeFromRelease(Mobile from, PlayerMobile player)
|
||||||
|
{
|
||||||
|
player.Frozen = false;
|
||||||
|
player.SendMessage(0x35, "Welcome back!");
|
||||||
|
player.SendMessage(0x35, "Please follow the shard rules.");
|
||||||
|
player.SendMessage(0x35, "Have a nice day!");
|
||||||
|
|
||||||
|
if (from != null)
|
||||||
|
{
|
||||||
|
CommandLogging.WriteLine(from, $"Player {player.Name} unfrozen after jail release");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Usage("Jail <player> [reason]")]
|
||||||
|
[Description("Jails a player with freeze/teleport/unfreeze sequence.")]
|
||||||
|
private static void Jail_OnCommand(CommandEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Length < 1)
|
||||||
|
{
|
||||||
|
e.Mobile.SendMessage(0x35, "Usage: [jail <player> [reason]");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var reason = e.GetString(1);
|
||||||
|
var playerName = e.GetString(0);
|
||||||
|
var playerSerial = Serial.TryParse(playerName, null, out var serial) ? serial : Serial.MinusOne;
|
||||||
|
|
||||||
|
PlayerMobile player = null;
|
||||||
|
|
||||||
|
foreach (var m in World.Mobiles.Values)
|
||||||
|
{
|
||||||
|
if (m is PlayerMobile pm && (pm.Serial == playerSerial || m.RawName.InsensitiveEquals(playerName)))
|
||||||
|
{
|
||||||
|
player = pm;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (player == null)
|
||||||
|
{
|
||||||
|
e.Mobile.SendMessage(0x35, $"Player '{playerName}' not found.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (player.AccessLevel > AccessLevel.Player)
|
||||||
|
{
|
||||||
|
e.Mobile.SendMessage(0x35, "You cannot jail staff members.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsPlayerJailed(player))
|
||||||
|
{
|
||||||
|
e.Mobile.SendMessage(0x35, $"Player {player.Name} is already jailed.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JailPlayer(e.Mobile, player, reason);
|
||||||
|
e.Mobile.SendMessage(0x35, $"Player {player.Name} is being jailed. Reason: {reason}");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Usage("Unjail <player>")]
|
||||||
|
[Description("Manually releases a player from jail.")]
|
||||||
|
private static void Unjail_OnCommand(CommandEventArgs e)
|
||||||
|
{
|
||||||
|
var from = e.Mobile;
|
||||||
|
if (e.Length < 1)
|
||||||
|
{
|
||||||
|
from.SendMessage(0x35, "Usage: [unjail <player>");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var playerName = e.GetString(0);
|
||||||
|
var playerSerial = Serial.TryParse(playerName, null, out var serial) ? serial : Serial.MinusOne;
|
||||||
|
|
||||||
|
PlayerMobile player = null;
|
||||||
|
|
||||||
|
foreach (var m in World.Mobiles.Values)
|
||||||
|
{
|
||||||
|
if (m is PlayerMobile pm && (pm.Serial == playerSerial || m.RawName.InsensitiveEquals(playerName)))
|
||||||
|
{
|
||||||
|
player = pm;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (player == null)
|
||||||
|
{
|
||||||
|
from.SendMessage(0x35, $"Player '{playerName}' not found.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsPlayerJailed(player))
|
||||||
|
{
|
||||||
|
from.SendMessage(0x35, $"Player {player.Name} is not currently jailed.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JailTimers.Remove(player, out var value))
|
||||||
|
{
|
||||||
|
value.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
ReleasePlayer(from, player);
|
||||||
|
from.SendMessage(0x35, $"Player {player.Name} has been manually released from jail.");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Usage("JailInfo <player>")]
|
||||||
|
[Description("Shows jail information for a player.")]
|
||||||
|
private static void JailInfo_OnCommand(CommandEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Length < 1)
|
||||||
|
{
|
||||||
|
e.Mobile.SendMessage(0x35, "Usage: [jailinfo <player>");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var playerName = e.GetString(0);
|
||||||
|
var playerSerial = Serial.TryParse(playerName, null, out var serial) ? serial : Serial.MinusOne;
|
||||||
|
|
||||||
|
PlayerMobile player = null;
|
||||||
|
|
||||||
|
foreach (var m in World.Mobiles.Values)
|
||||||
|
{
|
||||||
|
if (m is PlayerMobile pm && (pm.Serial == playerSerial || m.RawName.InsensitiveEquals(playerName)))
|
||||||
|
{
|
||||||
|
player = pm;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (player == null)
|
||||||
|
{
|
||||||
|
e.Mobile.SendMessage(0x35, $"Player '{playerName}' not found.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Mobile.SendGump(new JailRecordGump(player, PlayerJailRecords.GetValueOrDefault(player, EmptyRecord)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly Dictionary<Mobile, DateTime> JailRecordCooldowns = new();
|
||||||
|
private static readonly TimeSpan JailRecordCooldown = TimeSpan.FromSeconds(30);
|
||||||
|
|
||||||
|
[Usage("JailRecord")]
|
||||||
|
[Description("Shows your own jail information.")]
|
||||||
|
private static void MyJailInfo_OnCommand(CommandEventArgs e)
|
||||||
|
{
|
||||||
|
var player = (PlayerMobile)e.Mobile;
|
||||||
|
|
||||||
|
if (JailRecordCooldowns.TryGetValue(player, out var cooldown))
|
||||||
|
{
|
||||||
|
var timeSinceLastUse = Core.Now - cooldown;
|
||||||
|
|
||||||
|
if (timeSinceLastUse < JailRecordCooldown)
|
||||||
|
{
|
||||||
|
var remaining = JailRecordCooldown - timeSinceLastUse;
|
||||||
|
e.Mobile.SendMessage(0x35, $"You must wait {remaining.TotalSeconds:F0} seconds.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JailRecordCooldowns[player] = Core.Now;
|
||||||
|
|
||||||
|
player.SendGump(new JailRecordGump(player, PlayerJailRecords.GetValueOrDefault(player, EmptyRecord)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Serialize(IGenericWriter writer)
|
||||||
|
{
|
||||||
|
writer.WriteEncodedInt(0); // version
|
||||||
|
writer.WriteEncodedInt(PlayerJailRecords.Count);
|
||||||
|
foreach (var (m, record) in PlayerJailRecords)
|
||||||
|
{
|
||||||
|
writer.Write(m);
|
||||||
|
record.Serialize(writer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Deserialize(IGenericReader reader)
|
||||||
|
{
|
||||||
|
var version = reader.ReadEncodedInt();
|
||||||
|
|
||||||
|
var count = reader.ReadEncodedInt();
|
||||||
|
for (var i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
var player = reader.ReadEntity<PlayerMobile>();
|
||||||
|
var record = new JailRecord();
|
||||||
|
record.Deserialize(reader);
|
||||||
|
|
||||||
|
if (player != null)
|
||||||
|
{
|
||||||
|
PlayerJailRecords[player] = record;
|
||||||
|
if (record.IsCurrentlyJailed)
|
||||||
|
{
|
||||||
|
CurrentlyBeingJailed.Add(player);
|
||||||
|
var jailTime = record.JailEndTime - Core.Now;
|
||||||
|
JailTimers[player] = Timer.DelayCall(jailTime, ReleasePlayer, record.JailedBy, player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue