feat: Converts PuzzleChest to generator, optimizes (#2318)

This commit is contained in:
Kamron Batman 2026-01-26 21:20:01 -06:00 committed by GitHub
parent 0404251638
commit 6b64fb3c3f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 374 additions and 330 deletions

View file

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using ModernUO.Serialization;
using Server.Collections;
using Server.Gumps;
using Server.Network;
@ -19,66 +20,61 @@ namespace Server.Items
Yellow = 0x1870
}
public class PuzzleChestSolution
[SerializationGenerator(1)]
public partial class PuzzleChestSolution
{
[SerializableField(0)]
private PuzzleChestCylinder[] _cylinders;
public const int Length = 5;
public PuzzleChestSolution()
{
for (var i = 0; i < Cylinders.Length; i++)
{
Cylinders[i] = RandomCylinder();
}
}
public PuzzleChestSolution() =>
_cylinders = [RandomCylinder(), RandomCylinder(), RandomCylinder(), RandomCylinder(), RandomCylinder()];
public PuzzleChestSolution(
PuzzleChestCylinder first, PuzzleChestCylinder second, PuzzleChestCylinder third,
PuzzleChestCylinder fourth, PuzzleChestCylinder fifth
)
{
First = first;
Second = second;
Third = third;
Fourth = fourth;
Fifth = fifth;
}
) => _cylinders = [first, second, third, fourth, fifth];
public PuzzleChestSolution(PuzzleChestSolution solution)
{
for (var i = 0; i < Cylinders.Length; i++)
{
Cylinders[i] = solution.Cylinders[i];
}
_cylinders = new PuzzleChestCylinder[Length];
solution.Cylinders.AsSpan().CopyTo(Cylinders);
}
public PuzzleChestSolution(IGenericReader reader)
private void Deserialize(IGenericReader reader, int version)
{
var version = reader.ReadEncodedInt();
var length = reader.ReadEncodedInt();
for (var i = 0;; i++)
Cylinders = new PuzzleChestCylinder[length];
for (var i = 0; i < length; i++)
{
if (i < length)
{
var cylinder = (PuzzleChestCylinder)reader.ReadInt();
if (i < Cylinders.Length)
{
Cylinders[i] = cylinder;
}
}
else if (i < Cylinders.Length)
{
Cylinders[i] = RandomCylinder();
}
else
{
break;
}
Cylinders[i] = (PuzzleChestCylinder)reader.ReadInt();
}
}
public PuzzleChestCylinder[] Cylinders { get; } = new PuzzleChestCylinder[Length];
[AfterDeserialization]
private void AfterDeserialization()
{
if (Cylinders.Length == Length)
{
return;
}
var cylinders = Cylinders;
Cylinders = new PuzzleChestCylinder[Length];
if (cylinders.Length > Length)
{
Cylinders = cylinders[..Length];
return;
}
cylinders.CopyTo(Cylinders);
for (var i = cylinders.Length; i < Length; i++)
{
Cylinders[i] = RandomCylinder();
}
}
public PuzzleChestCylinder First
{
@ -130,8 +126,8 @@ namespace Server.Items
cylinders = 0;
colors = 0;
var matchesSrc = new bool[solution.Cylinders.Length];
var matchesDst = new bool[solution.Cylinders.Length];
Span<bool> matchesSrc = stackalloc bool[solution.Cylinders.Length];
Span<bool> matchesDst = stackalloc bool[solution.Cylinders.Length];
for (var i = 0; i < Cylinders.Length; i++)
{
@ -146,124 +142,146 @@ namespace Server.Items
for (var i = 0; i < Cylinders.Length; i++)
{
if (!matchesSrc[i])
if (matchesSrc[i])
{
for (var j = 0; j < solution.Cylinders.Length; j++)
{
if (Cylinders[i] == solution.Cylinders[j] && !matchesDst[j])
{
colors++;
continue;
}
matchesDst[j] = true;
}
for (var j = 0; j < solution.Cylinders.Length; j++)
{
if (Cylinders[i] == solution.Cylinders[j] && !matchesDst[j])
{
colors++;
matchesDst[j] = true;
}
}
}
return cylinders == Cylinders.Length;
}
public virtual void Serialize(IGenericWriter writer)
{
writer.WriteEncodedInt(0); // version
writer.WriteEncodedInt(Cylinders.Length);
for (var i = 0; i < Cylinders.Length; i++)
{
writer.Write((int)Cylinders[i]);
}
}
}
public class PuzzleChestSolutionAndTime : PuzzleChestSolution
[SerializationGenerator(0)]
public partial class PuzzleChestSolutionAndTime : PuzzleChestSolution
{
public PuzzleChestSolutionAndTime(DateTime when, PuzzleChestSolution solution) : base(solution) => When = when;
[DeltaDateTime]
[SerializableField(0)]
private DateTime _when;
public PuzzleChestSolutionAndTime(IGenericReader reader) : base(reader)
public PuzzleChestSolutionAndTime(DateTime when, PuzzleChestSolution solution) : base(solution) => _when = when;
// For serialization
public PuzzleChestSolutionAndTime()
{
var version = reader.ReadEncodedInt();
When = reader.ReadDeltaTime();
}
public DateTime When { get; }
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0); // version
writer.WriteDeltaTime(When);
}
}
public abstract class PuzzleChest : BaseTreasureChest
[SerializationGenerator(1)]
public abstract partial class PuzzleChest : BaseTreasureChest
{
public const int HintsCount = 3;
public readonly TimeSpan CleanupTime = TimeSpan.FromHours(1.0);
public static readonly TimeSpan CleanupTime = TimeSpan.FromHours(1.0);
private readonly Dictionary<Mobile, PuzzleChestSolutionAndTime> m_Guesses =
new();
private TimerExecutionToken _cleanupTimerToken;
private PuzzleChestSolution m_Solution;
[SerializableField(1, setter: "private")]
private PuzzleChestCylinder[] _hints;
public PuzzleChest(int itemID) : base(itemID)
[CanBeNull]
[Tidy]
[SerializableField(2)]
private Dictionary<Mobile, PuzzleChestSolutionAndTime> _guesses;
public PuzzleChest(int itemID) : base(itemID) => _hints = new PuzzleChestCylinder[HintsCount];
[AfterDeserialization]
private void AfterDeserialization()
{
}
public PuzzleChest(Serial serial) : base(serial)
{
}
public PuzzleChestSolution Solution
{
get => m_Solution;
set
// Validate hints array length - regenerate if mismatched
if (_hints.Length != HintsCount)
{
m_Solution = value;
InitHints();
}
StartCleanupTimer();
}
private void Deserialize(IGenericReader reader, int version)
{
_solution = new PuzzleChestSolution();
_solution.Deserialize(reader);
var length = reader.ReadEncodedInt();
for (var i = 0; i < length; i++)
{
var cylinder = (PuzzleChestCylinder)reader.ReadInt();
if (length == _hints.Length)
{
_hints[i] = cylinder;
}
}
var guessCount = reader.ReadEncodedInt();
if (guessCount <= 0)
{
return;
}
_guesses = new Dictionary<Mobile, PuzzleChestSolutionAndTime>(guessCount);
for (var i = 0; i < guessCount; i++)
{
var m = reader.ReadEntity<Mobile>();
(_guesses[m] = new PuzzleChestSolutionAndTime()).Deserialize(reader);
}
}
public PuzzleChestCylinder[] Hints { get; private set; } = new PuzzleChestCylinder[HintsCount];
[SerializableProperty(0)]
public PuzzleChestSolution Solution
{
get => _solution;
set
{
_solution = value;
InitHints();
this.MarkDirty();
}
}
public PuzzleChestCylinder FirstHint
{
get => Hints[0];
set => Hints[0] = value;
get => _hints[0];
set => _hints[0] = value;
}
public PuzzleChestCylinder SecondHint
{
get => Hints[1];
set => Hints[1] = value;
get => _hints[1];
set => _hints[1] = value;
}
public PuzzleChestCylinder ThirdHint
{
get => Hints[2];
set => Hints[2] = value;
get => _hints[2];
set => _hints[2] = value;
}
public override string DefaultName => null;
private void InitHints()
{
var list = new List<PuzzleChestCylinder>(Solution.Cylinders.Length - 1);
for (var i = 1; i < Solution.Cylinders.Length; i++)
{
list.Add(Solution.Cylinders[i]);
}
Span<PuzzleChestCylinder> cylinders = stackalloc PuzzleChestCylinder[Solution.Cylinders.Length - 1];
Solution.Cylinders.AsSpan(1).CopyTo(cylinders);
Hints = new PuzzleChestCylinder[HintsCount];
cylinders.Shuffle();
_hints = cylinders[..HintsCount].ToArray();
}
for (var i = 0; i < Hints.Length; i++)
{
var random = list.RandomElement();
Hints[i] = random;
list.Remove(random);
}
public override void OnDelete()
{
StopCleanupTimer();
base.OnDelete();
}
protected override void SetLockLevel()
@ -291,23 +309,14 @@ namespace Server.Items
);
}
var gumps = from.GetGumps();
gumps.Close<StatusGump>();
gumps.Close<PuzzleGump>();
gumps.Send(new PuzzleGump(from, this, solution, 0));
from.SendGump(new PuzzleGump(from, this, solution));
return true;
}
return false;
}
public PuzzleChestSolutionAndTime GetLastGuess(Mobile m)
{
m_Guesses.TryGetValue(m, out var pcst);
return pcst;
}
public PuzzleChestSolutionAndTime GetLastGuess(Mobile m) => _guesses?.GetValueOrDefault(m);
public void SubmitSolution(Mobile m, PuzzleChestSolution solution)
{
@ -319,7 +328,8 @@ namespace Server.Items
}
else
{
m_Guesses[m] = new PuzzleChestSolutionAndTime(Core.Now, solution);
(_guesses ??= []).Add(m, new PuzzleChestSolutionAndTime(Core.Now, solution));
StartCleanupTimer();
m.SendGump(new StatusGump(correctCylinders, correctColors));
@ -327,7 +337,7 @@ namespace Server.Items
}
}
public void DoDamage(Mobile to)
public static void DoDamage(Mobile to)
{
switch (Utility.Random(4))
{
@ -378,7 +388,8 @@ namespace Server.Items
{
base.LockPick(from);
m_Guesses.Clear();
StopCleanupTimer();
_guesses = null;
}
private static void GetRandomAOSStats(out int attributeCount, out int min, out int max)
@ -421,14 +432,15 @@ namespace Server.Items
{
DropItem(new Gold(600, 900));
var gems = new List<Item>();
using var gems = PooledRefList<Item>.Create();
for (var i = 0; i < 9; i++)
{
var gem = Loot.RandomGem();
var gemType = gem.GetType();
foreach (var listGem in gems)
for (var j = 0; j < gems.Count; j++)
{
var listGem = gems[j];
if (listGem.GetType() == gemType)
{
listGem.Amount++;
@ -443,8 +455,9 @@ namespace Server.Items
}
}
foreach (var gem in gems)
for (var i = 0; i < gems.Count; i++)
{
var gem = gems[i];
DropItem(gem);
}
@ -455,16 +468,7 @@ namespace Server.Items
for (var i = 0; i < 2; i++)
{
Item item;
if (Core.AOS)
{
item = Loot.RandomArmorOrShieldOrWeaponOrJewelry();
}
else
{
item = Loot.RandomArmorOrShieldOrWeapon();
}
var item = Core.AOS ? Loot.RandomArmorOrShieldOrWeaponOrJewelry() : Loot.RandomArmorOrShieldOrWeapon();
if (item is BaseWeapon weapon)
{
@ -523,288 +527,259 @@ namespace Server.Items
Solution = new PuzzleChestSolution();
}
public void CleanupGuesses()
private void StartCleanupTimer()
{
var toDelete = new List<Mobile>();
foreach (var kvp in m_Guesses)
if (_cleanupTimerToken.Running || _guesses == null || _guesses.Count == 0)
{
if (Core.Now - kvp.Value.When > CleanupTime)
return;
}
Timer.StartTimer(CleanupTime, CleanupTime, CleanupGuesses, out _cleanupTimerToken);
}
private void StopCleanupTimer()
{
_cleanupTimerToken.Cancel();
}
private void CleanupGuesses()
{
if (_guesses == null || _guesses.Count == 0)
{
StopCleanupTimer();
return;
}
using var toDelete = PooledRefQueue<Mobile>.Create();
foreach (var (key, value) in _guesses)
{
if (Core.Now - value.When > CleanupTime)
{
toDelete.Add(kvp.Key);
toDelete.Enqueue(key);
}
}
foreach (var m in toDelete)
while (toDelete.Count > 0)
{
m_Guesses.Remove(m);
_guesses.Remove(toDelete.Dequeue());
}
if (_guesses.Count == 0)
{
_guesses = null;
StopCleanupTimer();
}
}
public override void Serialize(IGenericWriter writer)
private class PuzzleGump : DynamicGump
{
CleanupGuesses();
private int _check;
private readonly PuzzleChest _chest;
private readonly PuzzleChestSolution _solution;
private readonly double _lockpicking;
private readonly PuzzleChestSolution _lastGuess;
base.Serialize(writer);
public override bool Singleton => true;
writer.WriteEncodedInt(0); // version
m_Solution.Serialize(writer);
writer.WriteEncodedInt(Hints.Length);
for (var i = 0; i < Hints.Length; i++)
public PuzzleGump(Mobile from, PuzzleChest chest, PuzzleChestSolution solution) : base(50, 50)
{
writer.Write((int)Hints[i]);
_chest = chest;
_solution = solution;
_lockpicking = from.Skills.Lockpicking.Base;
_lastGuess = chest.GetLastGuess(from);
}
writer.WriteEncodedInt(m_Guesses.Count);
foreach (var kvp in m_Guesses)
protected override void BuildLayout(ref DynamicGumpBuilder builder)
{
writer.Write(kvp.Key);
kvp.Value.Serialize(writer);
}
}
builder.SetNoDispose();
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
builder.AddBackground(25, 0, 500, 410, 0x53);
var version = reader.ReadEncodedInt();
builder.AddImage(62, 20, 0x67);
m_Solution = new PuzzleChestSolution(reader);
var length = reader.ReadEncodedInt();
for (var i = 0; i < length; i++)
{
var cylinder = (PuzzleChestCylinder)reader.ReadInt();
if (length == Hints.Length)
{
Hints[i] = cylinder;
}
}
if (length != Hints.Length)
{
InitHints();
}
var guesses = reader.ReadEncodedInt();
for (var i = 0; i < guesses; i++)
{
var m = reader.ReadEntity<Mobile>();
var sol = new PuzzleChestSolutionAndTime(reader);
m_Guesses[m] = sol;
}
}
private class PuzzleGump : Gump
{
private readonly PuzzleChest m_Chest;
private readonly Mobile m_From;
private readonly PuzzleChestSolution m_Solution;
public PuzzleGump(Mobile from, PuzzleChest chest, PuzzleChestSolution solution, int check) : base(50, 50)
{
m_From = from;
m_Chest = chest;
m_Solution = solution;
Draggable = false;
AddBackground(25, 0, 500, 410, 0x53);
AddImage(62, 20, 0x67);
AddHtmlLocalized(80, 36, 110, 70, 1018309, true); // A Puzzle Lock
builder.AddHtmlLocalized(80, 36, 110, 70, 1018309, true); // A Puzzle Lock
/* Correctly choose the sequence of cylinders needed to open the latch. Each cylinder
* may potentially be used more than once. Beware! A false attempt could be deadly!
*/
AddHtmlLocalized(214, 26, 270, 90, 1018310, true, true);
builder.AddHtmlLocalized(214, 26, 270, 90, 1018310, true, true);
AddLeftCylinderButton(62, 130, PuzzleChestCylinder.LightBlue, 10);
AddLeftCylinderButton(62, 180, PuzzleChestCylinder.Blue, 11);
AddLeftCylinderButton(62, 230, PuzzleChestCylinder.Green, 12);
AddLeftCylinderButton(62, 280, PuzzleChestCylinder.Orange, 13);
AddLeftCylinderButton(ref builder, 62, 130, PuzzleChestCylinder.LightBlue, 10);
AddLeftCylinderButton(ref builder, 62, 180, PuzzleChestCylinder.Blue, 11);
AddLeftCylinderButton(ref builder, 62, 230, PuzzleChestCylinder.Green, 12);
AddLeftCylinderButton(ref builder, 62, 280, PuzzleChestCylinder.Orange, 13);
AddRightCylinderButton(451, 130, PuzzleChestCylinder.Purple, 14);
AddRightCylinderButton(451, 180, PuzzleChestCylinder.Red, 15);
AddRightCylinderButton(451, 230, PuzzleChestCylinder.DarkBlue, 16);
AddRightCylinderButton(451, 280, PuzzleChestCylinder.Yellow, 17);
AddRightCylinderButton(ref builder, 451, 130, PuzzleChestCylinder.Purple, 14);
AddRightCylinderButton(ref builder, 451, 180, PuzzleChestCylinder.Red, 15);
AddRightCylinderButton(ref builder, 451, 230, PuzzleChestCylinder.DarkBlue, 16);
AddRightCylinderButton(ref builder, 451, 280, PuzzleChestCylinder.Yellow, 17);
var lockpicking = from.Skills.Lockpicking.Base;
if (lockpicking >= 60.0)
if (_lockpicking >= 60.0)
{
AddHtmlLocalized(160, 125, 230, 24, 1018308); // Lockpicking hint:
builder.AddHtmlLocalized(160, 125, 230, 24, 1018308); // Lockpicking hint:
AddBackground(159, 150, 230, 95, 0x13EC);
builder.AddBackground(159, 150, 230, 95, 0x13EC);
if (lockpicking >= 80.0)
if (_lockpicking >= 80.0)
{
AddHtmlLocalized(165, 157, 200, 40, 1018312); // In the first slot:
AddCylinder(350, 165, chest.Solution.First);
builder.AddHtmlLocalized(165, 157, 200, 40, 1018312); // In the first slot:
AddCylinder(ref builder, 350, 165, _chest.Solution.First);
AddHtmlLocalized(165, 197, 200, 40, 1018313); // Used in unknown slot:
AddCylinder(350, 200, chest.FirstHint);
builder.AddHtmlLocalized(165, 197, 200, 40, 1018313); // Used in unknown slot:
AddCylinder(ref builder, 350, 200, _chest.FirstHint);
if (lockpicking >= 90.0)
if (_lockpicking >= 90.0)
{
AddCylinder(350, 212, chest.SecondHint);
AddCylinder(ref builder, 350, 212, _chest.SecondHint);
}
if (lockpicking >= 100.0)
if (_lockpicking >= 100.0)
{
AddCylinder(350, 224, chest.ThirdHint);
AddCylinder(ref builder, 350, 224, _chest.ThirdHint);
}
}
else
{
AddHtmlLocalized(165, 157, 200, 40, 1018313); // Used in unknown slot:
AddCylinder(350, 160, chest.FirstHint);
builder.AddHtmlLocalized(165, 157, 200, 40, 1018313); // Used in unknown slot:
AddCylinder(ref builder, 350, 160, _chest.FirstHint);
if (lockpicking >= 70.0)
if (_lockpicking >= 70.0)
{
AddCylinder(350, 172, chest.SecondHint);
AddCylinder(ref builder, 350, 172, _chest.SecondHint);
}
}
}
PuzzleChestSolution lastGuess = chest.GetLastGuess(from);
if (lastGuess != null)
if (_lastGuess != null)
{
AddHtmlLocalized(127, 249, 170, 20, 1018311); // Thy previous guess:
builder.AddHtmlLocalized(127, 249, 170, 20, 1018311); // Thy previous guess:
AddBackground(290, 247, 115, 25, 0x13EC);
builder.AddBackground(290, 247, 115, 25, 0x13EC);
AddCylinder(281, 254, lastGuess.First);
AddCylinder(303, 254, lastGuess.Second);
AddCylinder(325, 254, lastGuess.Third);
AddCylinder(347, 254, lastGuess.Fourth);
AddCylinder(369, 254, lastGuess.Fifth);
AddCylinder(ref builder, 281, 254, _lastGuess.First);
AddCylinder(ref builder, 303, 254, _lastGuess.Second);
AddCylinder(ref builder, 325, 254, _lastGuess.Third);
AddCylinder(ref builder, 347, 254, _lastGuess.Fourth);
AddCylinder(ref builder, 369, 254, _lastGuess.Fifth);
}
AddPedestal(140, 270, solution.First, 0, check == 0);
AddPedestal(195, 270, solution.Second, 1, check == 1);
AddPedestal(250, 270, solution.Third, 2, check == 2);
AddPedestal(305, 270, solution.Fourth, 3, check == 3);
AddPedestal(360, 270, solution.Fifth, 4, check == 4);
AddPedestal(ref builder, 140, 270, _solution.First, 0, _check == 0);
AddPedestal(ref builder, 195, 270, _solution.Second, 1, _check == 1);
AddPedestal(ref builder, 250, 270, _solution.Third, 2, _check == 2);
AddPedestal(ref builder, 305, 270, _solution.Fourth, 3, _check == 3);
AddPedestal(ref builder, 360, 270, _solution.Fifth, 4, _check == 4);
AddButton(258, 370, 0xFA5, 0xFA7, 1);
builder.AddButton(258, 370, 0xFA5, 0xFA7, 1);
}
private void AddLeftCylinderButton(int x, int y, PuzzleChestCylinder cylinder, int buttonID)
private static void AddLeftCylinderButton(
ref DynamicGumpBuilder builder, int x, int y, PuzzleChestCylinder cylinder, int buttonID
)
{
AddBackground(x, y, 30, 30, 0x13EC);
AddCylinder(x - 7, y + 10, cylinder);
AddButton(x + 38, y + 9, 0x13A8, 0x4B9, buttonID);
builder.AddBackground(x, y, 30, 30, 0x13EC);
AddCylinder(ref builder, x - 7, y + 10, cylinder);
builder.AddButton(x + 38, y + 9, 0x13A8, 0x4B9, buttonID);
}
private void AddRightCylinderButton(int x, int y, PuzzleChestCylinder cylinder, int buttonID)
private static void AddRightCylinderButton(
ref DynamicGumpBuilder builder, int x, int y, PuzzleChestCylinder cylinder, int buttonID
)
{
AddBackground(x, y, 30, 30, 0x13EC);
AddCylinder(x - 7, y + 10, cylinder);
AddButton(x - 26, y + 9, 0x13A8, 0x4B9, buttonID);
builder.AddBackground(x, y, 30, 30, 0x13EC);
AddCylinder(ref builder, x - 7, y + 10, cylinder);
builder.AddButton(x - 26, y + 9, 0x13A8, 0x4B9, buttonID);
}
private void AddPedestal(int x, int y, PuzzleChestCylinder cylinder, int switchID, bool initialState)
private static void AddPedestal(
ref DynamicGumpBuilder builder, int x, int y, PuzzleChestCylinder cylinder, int switchID, bool initialState
)
{
AddItem(x, y, 0xB10);
AddItem(x - 23, y + 12, 0xB12);
AddItem(x + 23, y + 12, 0xB13);
AddItem(x, y + 23, 0xB11);
builder.AddItem(x, y, 0xB10);
builder.AddItem(x - 23, y + 12, 0xB12);
builder.AddItem(x + 23, y + 12, 0xB13);
builder.AddItem(x, y + 23, 0xB11);
if (cylinder != PuzzleChestCylinder.None)
{
AddItem(x, y + 2, 0x51A);
AddCylinder(x - 1, y + 19, cylinder);
builder.AddItem(x, y + 2, 0x51A);
AddCylinder(ref builder, x - 1, y + 19, cylinder);
}
else
{
AddItem(x, y + 2, 0x521);
builder.AddItem(x, y + 2, 0x521);
}
AddRadio(x + 7, y + 65, 0x867, 0x86A, initialState, switchID);
builder.AddRadio(x + 7, y + 65, 0x867, 0x86A, initialState, switchID);
}
private void AddCylinder(int x, int y, PuzzleChestCylinder cylinder)
private static void AddCylinder(ref DynamicGumpBuilder builder, int x, int y, PuzzleChestCylinder cylinder)
{
if (cylinder != PuzzleChestCylinder.None)
{
AddItem(x, y, (int)cylinder);
builder.AddItem(x, y, (int)cylinder);
}
else
{
AddItem(x + 9, y, (int)cylinder);
builder.AddItem(x + 9, y, (int)cylinder);
}
}
public override void OnResponse(NetState sender, in RelayInfo info)
{
if (m_Chest.Deleted || info.ButtonID == 0 || !m_From.CheckAlive())
var from = sender.Mobile;
if (_chest.Deleted || info.ButtonID == 0 || !from.CheckAlive())
{
return;
}
if (m_From.AccessLevel == AccessLevel.Player &&
(m_From.Map != m_Chest.Map || !m_From.InRange(m_Chest.GetWorldLocation(), 2)))
if (from.AccessLevel == AccessLevel.Player &&
(from.Map != _chest.Map || !from.InRange(_chest.GetWorldLocation(), 2)))
{
m_From.LocalOverheadMessage(MessageType.Regular, 0x3B2, 500446); // That is too far away.
from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 500446); // That is too far away.
return;
}
if (info.ButtonID == 1)
{
m_Chest.SubmitSolution(m_From, m_Solution);
_chest.SubmitSolution(from, _solution);
return;
}
else
if (info.Switches.Length == 0)
{
if (info.Switches.Length == 0)
{
return;
}
var pedestal = info.Switches[0];
if (pedestal < 0 || pedestal >= m_Solution.Cylinders.Length)
{
return;
}
PuzzleChestCylinder cylinder;
switch (info.ButtonID)
{
case 10:
cylinder = PuzzleChestCylinder.LightBlue;
break;
case 11:
cylinder = PuzzleChestCylinder.Blue;
break;
case 12:
cylinder = PuzzleChestCylinder.Green;
break;
case 13:
cylinder = PuzzleChestCylinder.Orange;
break;
case 14:
cylinder = PuzzleChestCylinder.Purple;
break;
case 15:
cylinder = PuzzleChestCylinder.Red;
break;
case 16:
cylinder = PuzzleChestCylinder.DarkBlue;
break;
case 17:
cylinder = PuzzleChestCylinder.Yellow;
break;
default: return;
}
m_Solution.Cylinders[pedestal] = cylinder;
m_From.SendGump(new PuzzleGump(m_From, m_Chest, m_Solution, pedestal));
return;
}
var pedestal = info.Switches[0];
if (pedestal < 0 || pedestal >= _solution.Cylinders.Length)
{
return;
}
PuzzleChestCylinder cylinder = info.ButtonID switch
{
10 => PuzzleChestCylinder.LightBlue,
11 => PuzzleChestCylinder.Blue,
12 => PuzzleChestCylinder.Green,
13 => PuzzleChestCylinder.Orange,
14 => PuzzleChestCylinder.Purple,
15 => PuzzleChestCylinder.Red,
16 => PuzzleChestCylinder.DarkBlue,
17 => PuzzleChestCylinder.Yellow,
_ => PuzzleChestCylinder.None
};
if (cylinder == PuzzleChestCylinder.None)
{
return;
}
_solution.Cylinders[pedestal] = cylinder;
_check = pedestal;
from.SendGump(this);
}
}
@ -813,6 +788,8 @@ namespace Server.Items
private readonly int _correctCylinders;
private readonly int _correctColors;
public override bool Singleton => true;
public StatusGump(int correctCylinders, int correctColors) : base(50, 50)
{
_correctCylinders = correctCylinders;
@ -832,7 +809,7 @@ namespace Server.Items
builder.AddHtmlLocalized(35, 323, 250, 24, 1018316); // Used colors in wrong slots:
builder.AddLabelPlaceholder(285, 323, 0x44, "colors");
builder.AddButton(152, 369, 0xFA5, 0xFA7, 0);
builder.AddButton(152, 369, 0xFA5, 0xFA7, 0); // Close
}
protected override void BuildStrings(ref GumpStringsBuilder builder)

View file

@ -0,0 +1,38 @@
{
"version": 1,
"type": "Server.Items.PuzzleChest",
"properties": [
{
"name": "Solution",
"type": "Server.Items.PuzzleChestSolution",
"rule": "RawSerializableMigrationRule",
"ruleArguments": [
""
]
},
{
"name": "Hints",
"type": "Server.Items.PuzzleChestCylinder[]",
"rule": "ArrayMigrationRule",
"ruleArguments": [
"Server.Items.PuzzleChestCylinder",
"EnumMigrationRule"
]
},
{
"name": "Guesses",
"type": "System.Collections.Generic.Dictionary\u003CServer.Mobile, Server.Items.PuzzleChestSolutionAndTime\u003E",
"rule": "DictionaryMigrationRule",
"ruleArguments": [
"@Tidy",
"Server.Mobile",
"SerializableInterfaceMigrationRule",
"0",
"Server.Items.PuzzleChestSolutionAndTime",
"RawSerializableMigrationRule",
"1",
""
]
}
]
}

View file

@ -0,0 +1,15 @@
{
"version": 1,
"type": "Server.Items.PuzzleChestSolution",
"properties": [
{
"name": "Cylinders",
"type": "Server.Items.PuzzleChestCylinder[]",
"rule": "ArrayMigrationRule",
"ruleArguments": [
"Server.Items.PuzzleChestCylinder",
"EnumMigrationRule"
]
}
]
}

View file

@ -0,0 +1,14 @@
{
"version": 0,
"type": "Server.Items.PuzzleChestSolutionAndTime",
"properties": [
{
"name": "When",
"type": "System.DateTime",
"rule": "PrimitiveTypeMigrationRule",
"ruleArguments": [
"DeltaTime"
]
}
]
}