fix: Fixes dirty-tracking gaps in generated content: setters, sub-object owners, BaseVendor (#2609)
## Why Delta world saves re-serialize an entity only when it has been marked dirty. An audit of the generated classes found three ways serialized state changes without a mark; this PR closes the ones that do not need a new generator package. ## What - **Custom `[SerializableProperty]` setters mark dirty.** Twelve hand-written setters assigned their backing field without `this.MarkDirty()`. `PlagueBeastLord.OpenedBy` was an auto-property carrying the attribute with no backing field at all; it is now a generated field with the same name, order and command-property exposure. - **Generated sub-objects are linked to their owner.** Without a `[DirtyTrackingEntity]` member the generator emits setters that mark nothing. Eight entity-owned sub-objects now carry the link and receive the owner through the constructor the generator calls: `BOBFilter` (owner is `IEntity`: a `PlayerMobile` or a `BulkOrderBook`), `BOBLargeSubEntry`, `PuzzleChestSolution` and `PuzzleChestSolutionAndTime`, `TalismanAttribute` (the random factories now take the talisman), `VendorItem`, `PlayerBBMessage`, `RaffleEntry`, `ShardPollOption`. Migration schemas were regenerated with the pinned tool; the only change is the value rule argument becoming `DeserializationRequiresParent`. - **BaseVendor uses the generator; restock amounts are no longer persisted.** The only state it wrote was which buy entries had grown restock amounts, packed by index into the live `SBInfos` tables. Restock is transient now and rebuilds on load; version 1 records are read and discarded through the legacy path. Every vendor subclass now serializes through a generated chain. ## Generator 4.1.0 This PR adopts SerializationGenerator 4.1.0 (modernuo/SerializationGenerator#55): SG3019/SG3020 diagnostics, `[VolatileSerializedState]`, `StopXxx()` timer helpers, and owner-constructor preference for sub-objects (so dictionary values are constructed with their owner; the 4.0.0 relink fallback is gone). `PlayerVendor.v3.json` gains `DeserializationRequiresParent` for its `VendorItem` values so the migration content struct constructs them with their vendor. SG3019 is an error under `TreatWarningsAsErrors` and generator diagnostics ignore pragmas, so the five contexts that live in whole-file player-keyed persistences (`ChampionTitle`, `ChampionTitleContext`, `MurderContext`, `VirtueContext`, `JailRecord`) now carry a `[DirtyTrackingEntity]` link to their `PlayerMobile`, which is where they will live once those blobs move onto the player record. `JailSystem.EmptyRecord` keeps a null player (`[CanBeNull]`). `ChampionTitle` needs both a context and a player constructor because the generator resolves the rule against the containing type but emits the call with the parent field; a comment in the file records this. ## Wire format Unchanged. No version bumps except `BaseVendor` 1 to 2 (which now writes nothing of its own). Schema diffs are rule-argument only (`DeserializationRequiresParent`). ## Testing Server.Tests 848 passed, UOContent.Tests 759 passed.
This commit is contained in:
parent
a2c232f4a8
commit
84153fba58
49 changed files with 223 additions and 214 deletions
|
|
@ -3,7 +3,7 @@
|
||||||
"isRoot": true,
|
"isRoot": true,
|
||||||
"tools": {
|
"tools": {
|
||||||
"modernuoschemagenerator": {
|
"modernuoschemagenerator": {
|
||||||
"version": "4.0.0",
|
"version": "4.1.0",
|
||||||
"commands": [
|
"commands": [
|
||||||
"ModernUOSchemaGenerator"
|
"ModernUOSchemaGenerator"
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -39,8 +39,8 @@
|
||||||
<PackageReference Include="LibDeflate.Bindings" Version="1.0.4" />
|
<PackageReference Include="LibDeflate.Bindings" Version="1.0.4" />
|
||||||
<PackageReference Include="System.IO.Hashing" Version="10.0.11" />
|
<PackageReference Include="System.IO.Hashing" Version="10.0.11" />
|
||||||
|
|
||||||
<PackageReference Include="ModernUO.Serialization.Annotations" Version="4.0.0" />
|
<PackageReference Include="ModernUO.Serialization.Annotations" Version="4.1.0" />
|
||||||
<PackageReference Include="ModernUO.Serialization.Generator" Version="4.0.0" PrivateAssets="all" />
|
<PackageReference Include="ModernUO.Serialization.Generator" Version="4.1.0" PrivateAssets="all" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AdditionalFiles Include="Migrations/*.v*.json" />
|
<AdditionalFiles Include="Migrations/*.v*.json" />
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,11 @@ namespace Server.Engines.BulkOrders;
|
||||||
[SerializationGenerator(2)]
|
[SerializationGenerator(2)]
|
||||||
public partial class BOBFilter
|
public partial class BOBFilter
|
||||||
{
|
{
|
||||||
|
[DirtyTrackingEntity]
|
||||||
|
private IEntity _owner;
|
||||||
|
|
||||||
|
public BOBFilter(IEntity owner) => _owner = owner;
|
||||||
|
|
||||||
[SerializableField(0)]
|
[SerializableField(0)]
|
||||||
[SaveFlag(nameof(ShouldSerializeType))]
|
[SaveFlag(nameof(ShouldSerializeType))]
|
||||||
private int _type;
|
private int _type;
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ public partial class BOBLargeEntry : BaseBOBEntry
|
||||||
|
|
||||||
for (var i = 0; i < _entries.Length; ++i)
|
for (var i = 0; i < _entries.Length; ++i)
|
||||||
{
|
{
|
||||||
_entries[i] = new BOBLargeSubEntry(bod.Entries[i]);
|
_entries[i] = new BOBLargeSubEntry(this, bod.Entries[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -76,7 +76,7 @@ public partial class BOBLargeEntry : BaseBOBEntry
|
||||||
|
|
||||||
for (var i = 0; i < Entries.Length; ++i)
|
for (var i = 0; i < Entries.Length; ++i)
|
||||||
{
|
{
|
||||||
_entries[i] = new BOBLargeSubEntry();
|
_entries[i] = new BOBLargeSubEntry(this);
|
||||||
_entries[i].Deserialize(reader);
|
_entries[i].Deserialize(reader);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,9 @@ namespace Server.Engines.BulkOrders;
|
||||||
[SerializationGenerator(0)]
|
[SerializationGenerator(0)]
|
||||||
public partial class BOBLargeSubEntry
|
public partial class BOBLargeSubEntry
|
||||||
{
|
{
|
||||||
|
[DirtyTrackingEntity]
|
||||||
|
private BOBLargeEntry _parent;
|
||||||
|
|
||||||
[SerializableField(0, setter: "private")]
|
[SerializableField(0, setter: "private")]
|
||||||
private Type _itemType;
|
private Type _itemType;
|
||||||
|
|
||||||
|
|
@ -21,12 +24,11 @@ public partial class BOBLargeSubEntry
|
||||||
[SerializableField(3, setter: "private")]
|
[SerializableField(3, setter: "private")]
|
||||||
private int _graphic;
|
private int _graphic;
|
||||||
|
|
||||||
public BOBLargeSubEntry()
|
public BOBLargeSubEntry(BOBLargeEntry parent) => _parent = parent;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public BOBLargeSubEntry(LargeBulkEntry lbe)
|
public BOBLargeSubEntry(BOBLargeEntry parent, LargeBulkEntry lbe)
|
||||||
{
|
{
|
||||||
|
_parent = parent;
|
||||||
_itemType = lbe.Details.Type;
|
_itemType = lbe.Details.Type;
|
||||||
_amountCur = lbe.Amount;
|
_amountCur = lbe.Amount;
|
||||||
_number = lbe.Details.Number;
|
_number = lbe.Details.Number;
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ public partial class BulkOrderBook : Item, ISecurable
|
||||||
LootType = LootType.Blessed;
|
LootType = LootType.Blessed;
|
||||||
|
|
||||||
_entries = [];
|
_entries = [];
|
||||||
_filter = new BOBFilter();
|
_filter = new BOBFilter(this);
|
||||||
|
|
||||||
_level = SecureLevel.CoOwners;
|
_level = SecureLevel.CoOwners;
|
||||||
}
|
}
|
||||||
|
|
@ -224,7 +224,7 @@ public partial class BulkOrderBook : Item, ISecurable
|
||||||
|
|
||||||
_bookName = reader.ReadString();
|
_bookName = reader.ReadString();
|
||||||
|
|
||||||
_filter = new BOBFilter();
|
_filter = new BOBFilter(this);
|
||||||
_filter.Deserialize(reader);
|
_filter.Deserialize(reader);
|
||||||
|
|
||||||
var count = reader.ReadEncodedInt();
|
var count = reader.ReadEncodedInt();
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,23 @@
|
||||||
using System;
|
using System;
|
||||||
using ModernUO.Serialization;
|
using ModernUO.Serialization;
|
||||||
|
using Server.Mobiles;
|
||||||
|
|
||||||
namespace Server.Engines.CannedEvil;
|
namespace Server.Engines.CannedEvil;
|
||||||
|
|
||||||
[SerializationGenerator(0)]
|
[SerializationGenerator(0)]
|
||||||
public partial class ChampionTitle
|
public partial class ChampionTitle
|
||||||
{
|
{
|
||||||
|
[DirtyTrackingEntity]
|
||||||
|
private PlayerMobile _player;
|
||||||
|
|
||||||
|
public ChampionTitle(ChampionTitleContext context) : this(context?.Player)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// The generator resolves the deserialization constructor against the owning type, but emits the
|
||||||
|
// owner's own dirty-tracking reference (the player) at the call site, so both overloads exist.
|
||||||
|
public ChampionTitle(PlayerMobile player) => _player = player;
|
||||||
|
|
||||||
[EncodedInt]
|
[EncodedInt]
|
||||||
[SerializableField(0)]
|
[SerializableField(0)]
|
||||||
private int _value;
|
private int _value;
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ public partial class ChampionTitleContext
|
||||||
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
||||||
private int _harrower;
|
private int _harrower;
|
||||||
|
|
||||||
|
[DirtyTrackingEntity]
|
||||||
private PlayerMobile _player;
|
private PlayerMobile _player;
|
||||||
|
|
||||||
public PlayerMobile Player => _player;
|
public PlayerMobile Player => _player;
|
||||||
|
|
@ -45,7 +46,7 @@ public partial class ChampionTitleContext
|
||||||
throw new NotImplementedException($"Cannot find ChampionSpawnType value {type}.");
|
throw new NotImplementedException($"Cannot find ChampionSpawnType value {type}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
title = new ChampionTitle();
|
title = new ChampionTitle(this);
|
||||||
title.Deserialize(reader);
|
title.Deserialize(reader);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -290,7 +291,7 @@ public partial class ChampionTitleContext
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return title ??= new ChampionTitle();
|
return title ??= new ChampionTitle(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetValue(ChampionSpawnType type, int value)
|
public void SetValue(ChampionSpawnType type, int value)
|
||||||
|
|
@ -313,7 +314,7 @@ public partial class ChampionTitleContext
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
title = new ChampionTitle();
|
title = new ChampionTitle(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
title.Value = value;
|
title.Value = value;
|
||||||
|
|
|
||||||
|
|
@ -23,11 +23,19 @@ namespace Server.Items
|
||||||
[SerializationGenerator(1)]
|
[SerializationGenerator(1)]
|
||||||
public partial class PuzzleChestSolution
|
public partial class PuzzleChestSolution
|
||||||
{
|
{
|
||||||
|
[DirtyTrackingEntity]
|
||||||
|
private PuzzleChest _chest;
|
||||||
|
|
||||||
[SerializableField(0)]
|
[SerializableField(0)]
|
||||||
private PuzzleChestCylinder[] _cylinders;
|
private PuzzleChestCylinder[] _cylinders;
|
||||||
|
|
||||||
public const int Length = 5;
|
public const int Length = 5;
|
||||||
|
|
||||||
|
// Declared first: the generator picks the first matching constructor, and a deserialized
|
||||||
|
// solution must know its chest to mark it dirty.
|
||||||
|
public PuzzleChestSolution(PuzzleChest chest) : this() => _chest = chest;
|
||||||
|
|
||||||
|
// Transient solutions (player guesses being edited in a gump) have no owning chest.
|
||||||
public PuzzleChestSolution() =>
|
public PuzzleChestSolution() =>
|
||||||
_cylinders = [RandomCylinder(), RandomCylinder(), RandomCylinder(), RandomCylinder(), RandomCylinder()];
|
_cylinders = [RandomCylinder(), RandomCylinder(), RandomCylinder(), RandomCylinder(), RandomCylinder()];
|
||||||
|
|
||||||
|
|
@ -42,6 +50,9 @@ namespace Server.Items
|
||||||
solution.Cylinders.AsSpan().CopyTo(Cylinders);
|
solution.Cylinders.AsSpan().CopyTo(Cylinders);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected PuzzleChestSolution(PuzzleChest chest, PuzzleChestSolution solution) : this(solution) =>
|
||||||
|
_chest = chest;
|
||||||
|
|
||||||
private void Deserialize(IGenericReader reader, int version)
|
private void Deserialize(IGenericReader reader, int version)
|
||||||
{
|
{
|
||||||
var length = reader.ReadEncodedInt();
|
var length = reader.ReadEncodedInt();
|
||||||
|
|
@ -174,10 +185,11 @@ namespace Server.Items
|
||||||
[SerializableField(0)]
|
[SerializableField(0)]
|
||||||
private DateTime _when;
|
private DateTime _when;
|
||||||
|
|
||||||
public PuzzleChestSolutionAndTime(DateTime when, PuzzleChestSolution solution) : base(solution) => _when = when;
|
public PuzzleChestSolutionAndTime(PuzzleChest chest, DateTime when, PuzzleChestSolution solution)
|
||||||
|
: base(chest, solution) => _when = when;
|
||||||
|
|
||||||
// For serialization
|
// The generator deserializes guesses through this constructor so each one knows its chest.
|
||||||
public PuzzleChestSolutionAndTime()
|
public PuzzleChestSolutionAndTime(PuzzleChest chest) : base(chest)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -214,7 +226,7 @@ namespace Server.Items
|
||||||
|
|
||||||
private void Deserialize(IGenericReader reader, int version)
|
private void Deserialize(IGenericReader reader, int version)
|
||||||
{
|
{
|
||||||
_solution = new PuzzleChestSolution();
|
_solution = new PuzzleChestSolution(this);
|
||||||
_solution.Deserialize(reader);
|
_solution.Deserialize(reader);
|
||||||
|
|
||||||
var length = reader.ReadEncodedInt();
|
var length = reader.ReadEncodedInt();
|
||||||
|
|
@ -238,7 +250,7 @@ namespace Server.Items
|
||||||
for (var i = 0; i < guessCount; i++)
|
for (var i = 0; i < guessCount; i++)
|
||||||
{
|
{
|
||||||
var m = reader.ReadEntity<Mobile>();
|
var m = reader.ReadEntity<Mobile>();
|
||||||
(_guesses[m] = new PuzzleChestSolutionAndTime()).Deserialize(reader);
|
(_guesses[m] = new PuzzleChestSolutionAndTime(this)).Deserialize(reader);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -329,7 +341,7 @@ namespace Server.Items
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
(_guesses ??= []).Add(m, new PuzzleChestSolutionAndTime(Core.Now, solution));
|
(_guesses ??= []).Add(m, new PuzzleChestSolutionAndTime(this, Core.Now, solution));
|
||||||
StartCleanupTimer();
|
StartCleanupTimer();
|
||||||
|
|
||||||
m.SendGump(new StatusGump(correctCylinders, correctColors));
|
m.SendGump(new StatusGump(correctCylinders, correctColors));
|
||||||
|
|
@ -525,7 +537,7 @@ namespace Server.Items
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Solution = new PuzzleChestSolution();
|
Solution = new PuzzleChestSolution(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void StartCleanupTimer()
|
private void StartCleanupTimer()
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,7 @@ public partial class PlantItem : Item, ISecurable
|
||||||
var ratio = PlantSystem != null ? (double)PlantSystem.Hits / PlantSystem.MaxHits : 1.0;
|
var ratio = PlantSystem != null ? (double)PlantSystem.Hits / PlantSystem.MaxHits : 1.0;
|
||||||
|
|
||||||
_plantStatus = value;
|
_plantStatus = value;
|
||||||
|
this.MarkDirty();
|
||||||
|
|
||||||
if (_plantStatus >= PlantStatus.DecorativePlant)
|
if (_plantStatus >= PlantStatus.DecorativePlant)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ public partial class MurderContext
|
||||||
_lastMurderTime = Core.Now;
|
_lastMurderTime = Core.Now;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[DirtyTrackingEntity]
|
||||||
public PlayerMobile _player;
|
public PlayerMobile _player;
|
||||||
|
|
||||||
public PlayerMobile Player => _player;
|
public PlayerMobile Player => _player;
|
||||||
|
|
|
||||||
|
|
@ -308,6 +308,7 @@ public abstract partial class BaseSpawner : Item, ISpawner
|
||||||
{
|
{
|
||||||
_walkingRange = value;
|
_walkingRange = value;
|
||||||
InvalidateProperties();
|
InvalidateProperties();
|
||||||
|
this.MarkDirty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,13 @@ namespace Server.Engines.Virtues;
|
||||||
[SerializationGenerator(1)]
|
[SerializationGenerator(1)]
|
||||||
public partial class VirtueContext
|
public partial class VirtueContext
|
||||||
{
|
{
|
||||||
|
[DirtyTrackingEntity]
|
||||||
|
private PlayerMobile _player;
|
||||||
|
|
||||||
|
public PlayerMobile Player => _player;
|
||||||
|
|
||||||
|
public VirtueContext(PlayerMobile player) => _player = player;
|
||||||
|
|
||||||
private void MigrateFrom(V0Content content)
|
private void MigrateFrom(V0Content content)
|
||||||
{
|
{
|
||||||
// Save-flagged values arrive as nullables; unset flags fall back to the same
|
// Save-flagged values arrive as nullables; unset flags fall back to the same
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@ public class VirtueSystem : GenericPersistence
|
||||||
for (var i = 0; i < contextCount; i++)
|
for (var i = 0; i < contextCount; i++)
|
||||||
{
|
{
|
||||||
var player = reader.ReadEntity<PlayerMobile>();
|
var player = reader.ReadEntity<PlayerMobile>();
|
||||||
var virtues = new VirtueContext();
|
var virtues = new VirtueContext(player);
|
||||||
virtues.Deserialize(reader);
|
virtues.Deserialize(reader);
|
||||||
|
|
||||||
if (player != null && virtues.IsUsed())
|
if (player != null && virtues.IsUsed())
|
||||||
|
|
@ -122,7 +122,7 @@ public class VirtueSystem : GenericPersistence
|
||||||
ref var context = ref CollectionsMarshal.GetValueRefOrAddDefault(_playerVirtues, from, out var exists);
|
ref var context = ref CollectionsMarshal.GetValueRefOrAddDefault(_playerVirtues, from, out var exists);
|
||||||
if (!exists)
|
if (!exists)
|
||||||
{
|
{
|
||||||
context = new VirtueContext();
|
context = new VirtueContext(from);
|
||||||
}
|
}
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
|
|
|
||||||
|
|
@ -195,6 +195,7 @@ namespace Server.Items
|
||||||
{
|
{
|
||||||
UnscaleDurability();
|
UnscaleDurability();
|
||||||
_quality = value;
|
_quality = value;
|
||||||
|
this.MarkDirty();
|
||||||
ScaleDurability();
|
ScaleDurability();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -213,6 +214,7 @@ namespace Server.Items
|
||||||
{
|
{
|
||||||
UnscaleDurability();
|
UnscaleDurability();
|
||||||
_durability = value;
|
_durability = value;
|
||||||
|
this.MarkDirty();
|
||||||
ScaleDurability();
|
ScaleDurability();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -246,6 +248,7 @@ namespace Server.Items
|
||||||
UnscaleDurability();
|
UnscaleDurability();
|
||||||
|
|
||||||
_resource = value;
|
_resource = value;
|
||||||
|
this.MarkDirty();
|
||||||
|
|
||||||
if (CraftItem.RetainsColor(GetType()))
|
if (CraftItem.RetainsColor(GetType()))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ public abstract partial class FillableContainer : LockableContainer
|
||||||
|
|
||||||
ClearContents();
|
ClearContents();
|
||||||
_contentType = value;
|
_contentType = value;
|
||||||
|
this.MarkDirty();
|
||||||
Respawn();
|
Respawn();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -350,6 +350,7 @@ public abstract partial class BaseBeverage : Item, IHasQuantity
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_quantity = Math.Clamp(value, 0, MaxQuantity);
|
_quantity = Math.Clamp(value, 0, MaxQuantity);
|
||||||
|
this.MarkDirty();
|
||||||
|
|
||||||
InvalidateProperties();
|
InvalidateProperties();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ public partial class BloodwoodSpirit : BaseTalisman
|
||||||
|
|
||||||
Removal = TalismanRemoval.Damage;
|
Removal = TalismanRemoval.Damage;
|
||||||
Blessed = GetRandomBlessed();
|
Blessed = GetRandomBlessed();
|
||||||
Protection = GetRandomProtection(false);
|
Protection = GetRandomProtection(this, false);
|
||||||
|
|
||||||
SkillBonuses.SetValues(0, SkillName.SpiritSpeak, 10.0);
|
SkillBonuses.SetValues(0, SkillName.SpiritSpeak, 10.0);
|
||||||
SkillBonuses.SetValues(1, SkillName.Necromancy, 5.0);
|
SkillBonuses.SetValues(1, SkillName.Necromancy, 5.0);
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ public partial class TotemOfVoid : BaseTalisman
|
||||||
MaxChargeTime = 1800;
|
MaxChargeTime = 1800;
|
||||||
|
|
||||||
Blessed = GetRandomBlessed();
|
Blessed = GetRandomBlessed();
|
||||||
Protection = GetRandomProtection(false);
|
Protection = GetRandomProtection(this, false);
|
||||||
|
|
||||||
Attributes.RegenHits = 2;
|
Attributes.RegenHits = 2;
|
||||||
Attributes.LowerManaCost = 10;
|
Attributes.LowerManaCost = 10;
|
||||||
|
|
|
||||||
|
|
@ -74,13 +74,13 @@ public abstract partial class BasePlayerBB : Item, ISecurable
|
||||||
|
|
||||||
if (_greeting != null)
|
if (_greeting != null)
|
||||||
{
|
{
|
||||||
board.Greeting = new PlayerBBMessage(_greeting.Time, _greeting.Poster, _greeting.Message);
|
board.Greeting = new PlayerBBMessage(board, _greeting.Time, _greeting.Poster, _greeting.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < _messages.Count; i++)
|
for (var i = 0; i < _messages.Count; i++)
|
||||||
{
|
{
|
||||||
var message = _messages[i];
|
var message = _messages[i];
|
||||||
board.AddToMessages(new PlayerBBMessage(message.Time, message.Poster, message.Message));
|
board.AddToMessages(new PlayerBBMessage(board, message.Time, message.Poster, message.Message));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -176,7 +176,7 @@ public abstract partial class BasePlayerBB : Item, ISecurable
|
||||||
|
|
||||||
if (text.Length > 0)
|
if (text.Length > 0)
|
||||||
{
|
{
|
||||||
var message = new PlayerBBMessage(Core.Now, from, text);
|
var message = new PlayerBBMessage(board, Core.Now, from, text);
|
||||||
|
|
||||||
if (_greeting)
|
if (_greeting)
|
||||||
{
|
{
|
||||||
|
|
@ -265,6 +265,9 @@ public abstract partial class BasePlayerBB : Item, ISecurable
|
||||||
[SerializationGenerator(0)]
|
[SerializationGenerator(0)]
|
||||||
public partial class PlayerBBMessage
|
public partial class PlayerBBMessage
|
||||||
{
|
{
|
||||||
|
[DirtyTrackingEntity]
|
||||||
|
private BasePlayerBB _board;
|
||||||
|
|
||||||
[SerializableField(0)]
|
[SerializableField(0)]
|
||||||
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
||||||
private DateTime _time;
|
private DateTime _time;
|
||||||
|
|
@ -277,12 +280,11 @@ public partial class PlayerBBMessage
|
||||||
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
||||||
private string _message;
|
private string _message;
|
||||||
|
|
||||||
public PlayerBBMessage()
|
public PlayerBBMessage(BasePlayerBB board) => _board = board;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public PlayerBBMessage(DateTime time, Mobile poster, string message)
|
public PlayerBBMessage(BasePlayerBB board, DateTime time, Mobile poster, string message)
|
||||||
{
|
{
|
||||||
|
_board = board;
|
||||||
_time = time;
|
_time = time;
|
||||||
_poster = poster;
|
_poster = poster;
|
||||||
_message = message;
|
_message = message;
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ public partial class RecallRune : Item
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_house = value;
|
_house = value;
|
||||||
|
this.MarkDirty();
|
||||||
CalculateHue();
|
CalculateHue();
|
||||||
InvalidateProperties();
|
InvalidateProperties();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,7 @@ public abstract partial class BaseInstrument : Item, ICraftable, ISlayer
|
||||||
{
|
{
|
||||||
UnscaleUses();
|
UnscaleUses();
|
||||||
_quality = value;
|
_quality = value;
|
||||||
|
this.MarkDirty();
|
||||||
InvalidateProperties();
|
InvalidateProperties();
|
||||||
ScaleUses();
|
ScaleUses();
|
||||||
}
|
}
|
||||||
|
|
@ -109,6 +110,7 @@ public abstract partial class BaseInstrument : Item, ICraftable, ISlayer
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_usesRemaining = value;
|
_usesRemaining = value;
|
||||||
|
this.MarkDirty();
|
||||||
InvalidateProperties();
|
InvalidateProperties();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,9 @@ namespace Server.Items;
|
||||||
[SerializationGenerator(0)]
|
[SerializationGenerator(0)]
|
||||||
public partial class RaffleEntry
|
public partial class RaffleEntry
|
||||||
{
|
{
|
||||||
|
[DirtyTrackingEntity]
|
||||||
|
private HouseRaffleStone _stone;
|
||||||
|
|
||||||
[SerializableField(0, setter: "private")]
|
[SerializableField(0, setter: "private")]
|
||||||
private Mobile _from;
|
private Mobile _from;
|
||||||
|
|
||||||
|
|
@ -25,15 +28,17 @@ public partial class RaffleEntry
|
||||||
[SerializableField(2, setter: "private")]
|
[SerializableField(2, setter: "private")]
|
||||||
private DateTime _date;
|
private DateTime _date;
|
||||||
|
|
||||||
public RaffleEntry(Mobile from)
|
public RaffleEntry(HouseRaffleStone stone, Mobile from)
|
||||||
{
|
{
|
||||||
|
_stone = stone;
|
||||||
_from = from;
|
_from = from;
|
||||||
_address = from?.NetState?.Address ?? IPAddress.None;
|
_address = from?.NetState?.Address ?? IPAddress.None;
|
||||||
_date = Core.Now;
|
_date = Core.Now;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RaffleEntry()
|
public RaffleEntry(HouseRaffleStone stone)
|
||||||
{
|
{
|
||||||
|
_stone = stone;
|
||||||
_from = null;
|
_from = null;
|
||||||
_address = null;
|
_address = null;
|
||||||
_date = Core.Now;
|
_date = Core.Now;
|
||||||
|
|
@ -454,7 +459,7 @@ public partial class HouseRaffleStone : Item
|
||||||
if (_ticketPrice == 0 || from.Backpack?.ConsumeTotal(typeof(Gold), _ticketPrice) == true ||
|
if (_ticketPrice == 0 || from.Backpack?.ConsumeTotal(typeof(Gold), _ticketPrice) == true ||
|
||||||
Banker.Withdraw(from, _ticketPrice))
|
Banker.Withdraw(from, _ticketPrice))
|
||||||
{
|
{
|
||||||
AddToEntries(new RaffleEntry(from));
|
AddToEntries(new RaffleEntry(this, from));
|
||||||
|
|
||||||
from.SendMessage(MessageHue, "You have successfully entered the plot's raffle.");
|
from.SendMessage(MessageHue, "You have successfully entered the plot's raffle.");
|
||||||
}
|
}
|
||||||
|
|
@ -539,7 +544,7 @@ public partial class HouseRaffleStone : Item
|
||||||
|
|
||||||
for (var i = 0; i < entryCount; i++)
|
for (var i = 0; i < entryCount; i++)
|
||||||
{
|
{
|
||||||
var entry = new RaffleEntry();
|
var entry = new RaffleEntry(this);
|
||||||
entry.Deserialize(reader);
|
entry.Deserialize(reader);
|
||||||
|
|
||||||
if (entry.From == null)
|
if (entry.From == null)
|
||||||
|
|
|
||||||
|
|
@ -26,19 +26,19 @@ public partial class BaseTalisman
|
||||||
BlessedFor = reader.ReadEntity<Mobile>();
|
BlessedFor = reader.ReadEntity<Mobile>();
|
||||||
}
|
}
|
||||||
|
|
||||||
_protection = new TalismanAttribute();
|
_protection = new TalismanAttribute(this);
|
||||||
if (GetOldSaveFlag(flags, OldSaveFlag.Protection))
|
if (GetOldSaveFlag(flags, OldSaveFlag.Protection))
|
||||||
{
|
{
|
||||||
_protection.Deserialize(reader);
|
_protection.Deserialize(reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
_killer = new TalismanAttribute();
|
_killer = new TalismanAttribute(this);
|
||||||
if (GetOldSaveFlag(flags, OldSaveFlag.Killer))
|
if (GetOldSaveFlag(flags, OldSaveFlag.Killer))
|
||||||
{
|
{
|
||||||
_killer.Deserialize(reader);
|
_killer.Deserialize(reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
_summoner = new TalismanAttribute();
|
_summoner = new TalismanAttribute(this);
|
||||||
if (GetOldSaveFlag(flags, OldSaveFlag.Summoner))
|
if (GetOldSaveFlag(flags, OldSaveFlag.Summoner))
|
||||||
{
|
{
|
||||||
_summoner.Deserialize(reader);
|
_summoner.Deserialize(reader);
|
||||||
|
|
|
||||||
|
|
@ -155,7 +155,7 @@ public partial class BaseTalisman : Item, IAosItem
|
||||||
|
|
||||||
public bool ShouldSerializeProtection() => !_protection.IsEmpty;
|
public bool ShouldSerializeProtection() => !_protection.IsEmpty;
|
||||||
|
|
||||||
private TalismanAttribute ProtectionDefaultValue() => new();
|
private TalismanAttribute ProtectionDefaultValue() => new(this);
|
||||||
|
|
||||||
[SerializedIgnoreDupe]
|
[SerializedIgnoreDupe]
|
||||||
[InvalidateProperties]
|
[InvalidateProperties]
|
||||||
|
|
@ -166,7 +166,7 @@ public partial class BaseTalisman : Item, IAosItem
|
||||||
|
|
||||||
public bool ShouldSerializeKiller() => !_killer.IsEmpty;
|
public bool ShouldSerializeKiller() => !_killer.IsEmpty;
|
||||||
|
|
||||||
private TalismanAttribute KillerDefaultValue() => new();
|
private TalismanAttribute KillerDefaultValue() => new(this);
|
||||||
|
|
||||||
[SerializedIgnoreDupe]
|
[SerializedIgnoreDupe]
|
||||||
[InvalidateProperties]
|
[InvalidateProperties]
|
||||||
|
|
@ -177,7 +177,7 @@ public partial class BaseTalisman : Item, IAosItem
|
||||||
|
|
||||||
public bool ShouldSerializeSummoner() => !_summoner.IsEmpty;
|
public bool ShouldSerializeSummoner() => !_summoner.IsEmpty;
|
||||||
|
|
||||||
private TalismanAttribute SummonerDefaultValue() => new();
|
private TalismanAttribute SummonerDefaultValue() => new(this);
|
||||||
|
|
||||||
[InvalidateProperties]
|
[InvalidateProperties]
|
||||||
[SerializableField(5)]
|
[SerializableField(5)]
|
||||||
|
|
@ -267,9 +267,9 @@ public partial class BaseTalisman : Item, IAosItem
|
||||||
{
|
{
|
||||||
Layer = Layer.Talisman;
|
Layer = Layer.Talisman;
|
||||||
|
|
||||||
_protection = new TalismanAttribute();
|
_protection = new TalismanAttribute(this);
|
||||||
_killer = new TalismanAttribute();
|
_killer = new TalismanAttribute(this);
|
||||||
_summoner = new TalismanAttribute();
|
_summoner = new TalismanAttribute(this);
|
||||||
Attributes = new AosAttributes(this);
|
Attributes = new AosAttributes(this);
|
||||||
SkillBonuses = new AosSkillBonuses(this);
|
SkillBonuses = new AosSkillBonuses(this);
|
||||||
}
|
}
|
||||||
|
|
@ -319,9 +319,9 @@ public partial class BaseTalisman : Item, IAosItem
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
talisman._summoner = new TalismanAttribute(_summoner);
|
talisman._summoner = new TalismanAttribute(talisman, _summoner);
|
||||||
talisman._protection = new TalismanAttribute(_protection);
|
talisman._protection = new TalismanAttribute(talisman, _protection);
|
||||||
talisman._killer = new TalismanAttribute(_killer);
|
talisman._killer = new TalismanAttribute(talisman, _killer);
|
||||||
talisman.Attributes = new AosAttributes(newItem, Attributes);
|
talisman.Attributes = new AosAttributes(newItem, Attributes);
|
||||||
talisman.SkillBonuses = new AosSkillBonuses(newItem, SkillBonuses);
|
talisman.SkillBonuses = new AosSkillBonuses(newItem, SkillBonuses);
|
||||||
}
|
}
|
||||||
|
|
@ -660,17 +660,17 @@ public partial class BaseTalisman : Item, IAosItem
|
||||||
|
|
||||||
public virtual void SetSummoner(Type type, TextDefinition name)
|
public virtual void SetSummoner(Type type, TextDefinition name)
|
||||||
{
|
{
|
||||||
_summoner = new TalismanAttribute(type, name);
|
_summoner = new TalismanAttribute(this, type, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void SetProtection(Type type, TextDefinition name, int amount)
|
public virtual void SetProtection(Type type, TextDefinition name, int amount)
|
||||||
{
|
{
|
||||||
_protection = new TalismanAttribute(type, name, amount);
|
_protection = new TalismanAttribute(this, type, name, amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void SetKiller(Type type, TextDefinition name, int amount)
|
public virtual void SetKiller(Type type, TextDefinition name, int amount)
|
||||||
{
|
{
|
||||||
_killer = new TalismanAttribute(type, name, amount);
|
_killer = new TalismanAttribute(this, type, name, amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void StartTimer()
|
public virtual void StartTimer()
|
||||||
|
|
@ -712,18 +712,18 @@ public partial class BaseTalisman : Item, IAosItem
|
||||||
|
|
||||||
public static Type GetRandomSummonType() => _summons.RandomElement();
|
public static Type GetRandomSummonType() => _summons.RandomElement();
|
||||||
|
|
||||||
public static TalismanAttribute GetRandomSummoner()
|
public static TalismanAttribute GetRandomSummoner(BaseTalisman owner)
|
||||||
{
|
{
|
||||||
if (Utility.RandomDouble() < 0.975)
|
if (Utility.RandomDouble() < 0.975)
|
||||||
{
|
{
|
||||||
return new TalismanAttribute();
|
return new TalismanAttribute(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
var num = Utility.Random(_summons.Length);
|
var num = Utility.Random(_summons.Length);
|
||||||
|
|
||||||
return num > 14
|
return num > 14
|
||||||
? new TalismanAttribute(_summons[num], _summonLabels[num], 10)
|
? new TalismanAttribute(owner, _summons[num], _summonLabels[num], 10)
|
||||||
: new TalismanAttribute(_summons[num], _summonLabels[num]);
|
: new TalismanAttribute(owner, _summons[num], _summonLabels[num]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TalismanRemoval GetRandomRemoval()
|
public static TalismanRemoval GetRandomRemoval()
|
||||||
|
|
@ -736,32 +736,32 @@ public partial class BaseTalisman : Item, IAosItem
|
||||||
return TalismanRemoval.None;
|
return TalismanRemoval.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TalismanAttribute GetRandomKiller() => GetRandomKiller(true);
|
public static TalismanAttribute GetRandomKiller(BaseTalisman owner) => GetRandomKiller(owner, true);
|
||||||
|
|
||||||
public static TalismanAttribute GetRandomKiller(bool includingNone)
|
public static TalismanAttribute GetRandomKiller(BaseTalisman owner, bool includingNone)
|
||||||
{
|
{
|
||||||
if (includingNone && Utility.RandomBool())
|
if (includingNone && Utility.RandomBool())
|
||||||
{
|
{
|
||||||
return new TalismanAttribute();
|
return new TalismanAttribute(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
var num = Utility.Random(_killers.Length);
|
var num = Utility.Random(_killers.Length);
|
||||||
|
|
||||||
return new TalismanAttribute(_killers[num], _killerLabels[num], Utility.RandomMinMax(10, 100));
|
return new TalismanAttribute(owner, _killers[num], _killerLabels[num], Utility.RandomMinMax(10, 100));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TalismanAttribute GetRandomProtection() => GetRandomProtection(true);
|
public static TalismanAttribute GetRandomProtection(BaseTalisman owner) => GetRandomProtection(owner, true);
|
||||||
|
|
||||||
public static TalismanAttribute GetRandomProtection(bool includingNone)
|
public static TalismanAttribute GetRandomProtection(BaseTalisman owner, bool includingNone)
|
||||||
{
|
{
|
||||||
if (includingNone && Utility.RandomBool())
|
if (includingNone && Utility.RandomBool())
|
||||||
{
|
{
|
||||||
return new TalismanAttribute();
|
return new TalismanAttribute(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
var num = Utility.Random(_killers.Length);
|
var num = Utility.Random(_killers.Length);
|
||||||
|
|
||||||
return new TalismanAttribute(_killers[num], _killerLabels[num], Utility.RandomMinMax(5, 60));
|
return new TalismanAttribute(owner, _killers[num], _killerLabels[num], Utility.RandomMinMax(5, 60));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SkillName GetRandomSkill() => _skills.RandomElement();
|
public static SkillName GetRandomSkill() => _skills.RandomElement();
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ public partial class RandomTalisman : BaseTalisman
|
||||||
[Constructible]
|
[Constructible]
|
||||||
public RandomTalisman() : base(GetRandomItemID())
|
public RandomTalisman() : base(GetRandomItemID())
|
||||||
{
|
{
|
||||||
Summoner = GetRandomSummoner();
|
Summoner = GetRandomSummoner(this);
|
||||||
|
|
||||||
if (Summoner.IsEmpty)
|
if (Summoner.IsEmpty)
|
||||||
{
|
{
|
||||||
|
|
@ -36,8 +36,8 @@ public partial class RandomTalisman : BaseTalisman
|
||||||
|
|
||||||
Blessed = GetRandomBlessed();
|
Blessed = GetRandomBlessed();
|
||||||
Slayer = GetRandomSlayer();
|
Slayer = GetRandomSlayer();
|
||||||
Protection = GetRandomProtection();
|
Protection = GetRandomProtection(this);
|
||||||
Killer = GetRandomKiller();
|
Killer = GetRandomKiller(this);
|
||||||
Skill = GetRandomSkill();
|
Skill = GetRandomSkill();
|
||||||
ExceptionalBonus = GetRandomExceptional();
|
ExceptionalBonus = GetRandomExceptional();
|
||||||
SuccessBonus = GetRandomSuccessful();
|
SuccessBonus = GetRandomSuccessful();
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,9 @@ namespace Server.Items;
|
||||||
[SerializationGenerator(1, false)]
|
[SerializationGenerator(1, false)]
|
||||||
public partial class TalismanAttribute
|
public partial class TalismanAttribute
|
||||||
{
|
{
|
||||||
|
[DirtyTrackingEntity]
|
||||||
|
private BaseTalisman _owner;
|
||||||
|
|
||||||
[SerializableField(0)]
|
[SerializableField(0)]
|
||||||
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
||||||
private Type _type;
|
private Type _type;
|
||||||
|
|
@ -19,12 +22,12 @@ public partial class TalismanAttribute
|
||||||
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
||||||
private int _amount;
|
private int _amount;
|
||||||
|
|
||||||
public TalismanAttribute() : this(null, null)
|
public TalismanAttribute(BaseTalisman owner) => _owner = owner;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public TalismanAttribute(TalismanAttribute copy)
|
public TalismanAttribute(BaseTalisman owner, TalismanAttribute copy)
|
||||||
{
|
{
|
||||||
|
_owner = owner;
|
||||||
|
|
||||||
if (copy != null)
|
if (copy != null)
|
||||||
{
|
{
|
||||||
_type = copy.Type;
|
_type = copy.Type;
|
||||||
|
|
@ -33,8 +36,9 @@ public partial class TalismanAttribute
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public TalismanAttribute(Type type, TextDefinition name, int amount = 0)
|
public TalismanAttribute(BaseTalisman owner, Type type, TextDefinition name, int amount = 0)
|
||||||
{
|
{
|
||||||
|
_owner = owner;
|
||||||
_type = type;
|
_type = type;
|
||||||
_name = name;
|
_name = name;
|
||||||
_amount = amount;
|
_amount = amount;
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
"ruleArguments": [
|
"ruleArguments": [
|
||||||
"Server.Engines.BulkOrders.BOBLargeSubEntry",
|
"Server.Engines.BulkOrders.BOBLargeSubEntry",
|
||||||
"RawSerializableMigrationRule",
|
"RawSerializableMigrationRule",
|
||||||
""
|
"DeserializationRequiresParent"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@
|
||||||
"type": "Server.Engines.BulkOrders.BOBFilter",
|
"type": "Server.Engines.BulkOrders.BOBFilter",
|
||||||
"rule": "RawSerializableMigrationRule",
|
"rule": "RawSerializableMigrationRule",
|
||||||
"ruleArguments": [
|
"ruleArguments": [
|
||||||
""
|
"DeserializationRequiresParent"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
"usesSaveFlag": true,
|
"usesSaveFlag": true,
|
||||||
"rule": "RawSerializableMigrationRule",
|
"rule": "RawSerializableMigrationRule",
|
||||||
"ruleArguments": [
|
"ruleArguments": [
|
||||||
""
|
"DeserializationRequiresParent"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -25,7 +25,7 @@
|
||||||
"usesSaveFlag": true,
|
"usesSaveFlag": true,
|
||||||
"rule": "RawSerializableMigrationRule",
|
"rule": "RawSerializableMigrationRule",
|
||||||
"ruleArguments": [
|
"ruleArguments": [
|
||||||
""
|
"DeserializationRequiresParent"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -34,7 +34,7 @@
|
||||||
"usesSaveFlag": true,
|
"usesSaveFlag": true,
|
||||||
"rule": "RawSerializableMigrationRule",
|
"rule": "RawSerializableMigrationRule",
|
||||||
"ruleArguments": [
|
"ruleArguments": [
|
||||||
""
|
"DeserializationRequiresParent"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -43,7 +43,7 @@
|
||||||
"usesSaveFlag": true,
|
"usesSaveFlag": true,
|
||||||
"rule": "RawSerializableMigrationRule",
|
"rule": "RawSerializableMigrationRule",
|
||||||
"ruleArguments": [
|
"ruleArguments": [
|
||||||
""
|
"DeserializationRequiresParent"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -52,7 +52,7 @@
|
||||||
"usesSaveFlag": true,
|
"usesSaveFlag": true,
|
||||||
"rule": "RawSerializableMigrationRule",
|
"rule": "RawSerializableMigrationRule",
|
||||||
"ruleArguments": [
|
"ruleArguments": [
|
||||||
""
|
"DeserializationRequiresParent"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -61,7 +61,7 @@
|
||||||
"usesSaveFlag": true,
|
"usesSaveFlag": true,
|
||||||
"rule": "RawSerializableMigrationRule",
|
"rule": "RawSerializableMigrationRule",
|
||||||
"ruleArguments": [
|
"ruleArguments": [
|
||||||
""
|
"DeserializationRequiresParent"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -70,7 +70,7 @@
|
||||||
"usesSaveFlag": true,
|
"usesSaveFlag": true,
|
||||||
"rule": "RawSerializableMigrationRule",
|
"rule": "RawSerializableMigrationRule",
|
||||||
"ruleArguments": [
|
"ruleArguments": [
|
||||||
""
|
"DeserializationRequiresParent"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -79,7 +79,7 @@
|
||||||
"usesSaveFlag": true,
|
"usesSaveFlag": true,
|
||||||
"rule": "RawSerializableMigrationRule",
|
"rule": "RawSerializableMigrationRule",
|
||||||
"ruleArguments": [
|
"ruleArguments": [
|
||||||
""
|
"DeserializationRequiresParent"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -88,7 +88,7 @@
|
||||||
"usesSaveFlag": true,
|
"usesSaveFlag": true,
|
||||||
"rule": "RawSerializableMigrationRule",
|
"rule": "RawSerializableMigrationRule",
|
||||||
"ruleArguments": [
|
"ruleArguments": [
|
||||||
""
|
"DeserializationRequiresParent"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
"type": "Server.Items.PlayerBBMessage",
|
"type": "Server.Items.PlayerBBMessage",
|
||||||
"rule": "RawSerializableMigrationRule",
|
"rule": "RawSerializableMigrationRule",
|
||||||
"ruleArguments": [
|
"ruleArguments": [
|
||||||
"",
|
"DeserializationRequiresParent",
|
||||||
"@CanBeNull"
|
"@CanBeNull"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
@ -31,7 +31,7 @@
|
||||||
"ruleArguments": [
|
"ruleArguments": [
|
||||||
"Server.Items.PlayerBBMessage",
|
"Server.Items.PlayerBBMessage",
|
||||||
"RawSerializableMigrationRule",
|
"RawSerializableMigrationRule",
|
||||||
""
|
"DeserializationRequiresParent"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@
|
||||||
"usesSaveFlag": true,
|
"usesSaveFlag": true,
|
||||||
"rule": "RawSerializableMigrationRule",
|
"rule": "RawSerializableMigrationRule",
|
||||||
"ruleArguments": [
|
"ruleArguments": [
|
||||||
""
|
"DeserializationRequiresParent"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"usesSaveFlag": true,
|
"usesSaveFlag": true,
|
||||||
"rule": "RawSerializableMigrationRule",
|
"rule": "RawSerializableMigrationRule",
|
||||||
"ruleArguments": [
|
"ruleArguments": [
|
||||||
""
|
"DeserializationRequiresParent"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -44,7 +44,7 @@
|
||||||
"usesSaveFlag": true,
|
"usesSaveFlag": true,
|
||||||
"rule": "RawSerializableMigrationRule",
|
"rule": "RawSerializableMigrationRule",
|
||||||
"ruleArguments": [
|
"ruleArguments": [
|
||||||
""
|
"DeserializationRequiresParent"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@
|
||||||
"ruleArguments": [
|
"ruleArguments": [
|
||||||
"Server.Items.RaffleEntry",
|
"Server.Items.RaffleEntry",
|
||||||
"RawSerializableMigrationRule",
|
"RawSerializableMigrationRule",
|
||||||
""
|
"DeserializationRequiresParent"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
"type": "Server.Items.PuzzleChestSolution",
|
"type": "Server.Items.PuzzleChestSolution",
|
||||||
"rule": "RawSerializableMigrationRule",
|
"rule": "RawSerializableMigrationRule",
|
||||||
"ruleArguments": [
|
"ruleArguments": [
|
||||||
""
|
"DeserializationRequiresParent"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -32,7 +32,7 @@
|
||||||
"Server.Items.PuzzleChestSolutionAndTime",
|
"Server.Items.PuzzleChestSolutionAndTime",
|
||||||
"RawSerializableMigrationRule",
|
"RawSerializableMigrationRule",
|
||||||
"2",
|
"2",
|
||||||
"",
|
"DeserializationRequiresParent",
|
||||||
"@CanBeNull"
|
"@CanBeNull"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@
|
||||||
"ruleArguments": [
|
"ruleArguments": [
|
||||||
"Server.Misc.ShardPollOption",
|
"Server.Misc.ShardPollOption",
|
||||||
"RawSerializableMigrationRule",
|
"RawSerializableMigrationRule",
|
||||||
""
|
"DeserializationRequiresParent"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
4
Projects/UOContent/Migrations/Server.Mobiles.BaseVendor.v2.json
generated
Normal file
4
Projects/UOContent/Migrations/Server.Mobiles.BaseVendor.v2.json
generated
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"version": 2,
|
||||||
|
"type": "Server.Mobiles.BaseVendor"
|
||||||
|
}
|
||||||
|
|
@ -55,7 +55,7 @@
|
||||||
"Server.Mobiles.VendorItem",
|
"Server.Mobiles.VendorItem",
|
||||||
"RawSerializableMigrationRule",
|
"RawSerializableMigrationRule",
|
||||||
"1",
|
"1",
|
||||||
""
|
"DeserializationRequiresParent"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@
|
||||||
"Server.Mobiles.VendorItem",
|
"Server.Mobiles.VendorItem",
|
||||||
"RawSerializableMigrationRule",
|
"RawSerializableMigrationRule",
|
||||||
"1",
|
"1",
|
||||||
""
|
"DeserializationRequiresParent"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -738,7 +738,7 @@ namespace Server
|
||||||
{
|
{
|
||||||
var talisman = new BaseTalisman(BaseTalisman.GetRandomItemID());
|
var talisman = new BaseTalisman(BaseTalisman.GetRandomItemID());
|
||||||
|
|
||||||
talisman.Summoner = BaseTalisman.GetRandomSummoner();
|
talisman.Summoner = BaseTalisman.GetRandomSummoner(talisman);
|
||||||
|
|
||||||
if (talisman.Summoner.IsEmpty)
|
if (talisman.Summoner.IsEmpty)
|
||||||
{
|
{
|
||||||
|
|
@ -758,8 +758,8 @@ namespace Server
|
||||||
|
|
||||||
talisman.Blessed = BaseTalisman.GetRandomBlessed();
|
talisman.Blessed = BaseTalisman.GetRandomBlessed();
|
||||||
talisman.Slayer = BaseTalisman.GetRandomSlayer();
|
talisman.Slayer = BaseTalisman.GetRandomSlayer();
|
||||||
talisman.Protection = BaseTalisman.GetRandomProtection();
|
talisman.Protection = BaseTalisman.GetRandomProtection(talisman);
|
||||||
talisman.Killer = BaseTalisman.GetRandomKiller();
|
talisman.Killer = BaseTalisman.GetRandomKiller(talisman);
|
||||||
talisman.Skill = BaseTalisman.GetRandomSkill();
|
talisman.Skill = BaseTalisman.GetRandomSkill();
|
||||||
talisman.ExceptionalBonus = BaseTalisman.GetRandomExceptional();
|
talisman.ExceptionalBonus = BaseTalisman.GetRandomExceptional();
|
||||||
talisman.SuccessBonus = BaseTalisman.GetRandomSuccessful();
|
talisman.SuccessBonus = BaseTalisman.GetRandomSuccessful();
|
||||||
|
|
|
||||||
|
|
@ -187,7 +187,7 @@ public partial class ShardPoller : Item
|
||||||
|
|
||||||
for (var i = 0; i < _options.Length; ++i)
|
for (var i = 0; i < _options.Length; ++i)
|
||||||
{
|
{
|
||||||
var option = _options[i] = new ShardPollOption();
|
var option = _options[i] = new ShardPollOption(this);
|
||||||
option.Deserialize(reader);
|
option.Deserialize(reader);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -212,15 +212,23 @@ public partial class ShardPoller : Item
|
||||||
[SerializationGenerator(1, false)]
|
[SerializationGenerator(1, false)]
|
||||||
public partial class ShardPollOption
|
public partial class ShardPollOption
|
||||||
{
|
{
|
||||||
|
[DirtyTrackingEntity]
|
||||||
|
private ShardPoller _poller;
|
||||||
|
|
||||||
private int _lineBreaks = -1;
|
private int _lineBreaks = -1;
|
||||||
|
|
||||||
[SerializableField(1)]
|
[SerializableField(1)]
|
||||||
private IPAddress[] _voters;
|
private IPAddress[] _voters;
|
||||||
|
|
||||||
public ShardPollOption() => _voters = [];
|
public ShardPollOption(ShardPoller poller)
|
||||||
|
|
||||||
public ShardPollOption(string title)
|
|
||||||
{
|
{
|
||||||
|
_poller = poller;
|
||||||
|
_voters = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShardPollOption(ShardPoller poller, string title)
|
||||||
|
{
|
||||||
|
_poller = poller;
|
||||||
_title = title;
|
_title = title;
|
||||||
_voters = [];
|
_voters = [];
|
||||||
}
|
}
|
||||||
|
|
@ -600,7 +608,7 @@ public partial class ShardPollPrompt : Prompt
|
||||||
|
|
||||||
if (_option == null)
|
if (_option == null)
|
||||||
{
|
{
|
||||||
_poller.AddOption(new ShardPollOption(text));
|
_poller.AddOption(new ShardPollOption(_poller, text));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,11 @@ namespace Server.Mobiles
|
||||||
public int DevourGoal
|
public int DevourGoal
|
||||||
{
|
{
|
||||||
get => IsParagon ? _devourGoal + 25 : _devourGoal;
|
get => IsParagon ? _devourGoal + 25 : _devourGoal;
|
||||||
set => _devourGoal = value;
|
set
|
||||||
|
{
|
||||||
|
_devourGoal = value;
|
||||||
|
this.MarkDirty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Constructible]
|
[Constructible]
|
||||||
|
|
|
||||||
|
|
@ -52,9 +52,9 @@ namespace Server.Mobiles
|
||||||
VirtualArmor = 50;
|
VirtualArmor = 50;
|
||||||
}
|
}
|
||||||
|
|
||||||
[CommandProperty(AccessLevel.GameMaster)]
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
||||||
[SerializableProperty(3)]
|
[SerializableField(3)]
|
||||||
public Mobile OpenedBy { get; set; }
|
private Mobile _openedBy;
|
||||||
|
|
||||||
|
|
||||||
[CommandProperty(AccessLevel.GameMaster)]
|
[CommandProperty(AccessLevel.GameMaster)]
|
||||||
|
|
|
||||||
|
|
@ -199,7 +199,7 @@ namespace Server.Mobiles
|
||||||
VisibilityList = new List<Mobile>();
|
VisibilityList = new List<Mobile>();
|
||||||
PermaFlags = new List<Mobile>();
|
PermaFlags = new List<Mobile>();
|
||||||
|
|
||||||
BOBFilter = new BOBFilter();
|
BOBFilter = new BOBFilter(this);
|
||||||
|
|
||||||
m_GameTime = TimeSpan.Zero;
|
m_GameTime = TimeSpan.Zero;
|
||||||
m_GuildRank = RankDefinition.Lowest;
|
m_GuildRank = RankDefinition.Lowest;
|
||||||
|
|
@ -2958,7 +2958,7 @@ namespace Server.Mobiles
|
||||||
case 13: // just removed m_PaidInsurance list
|
case 13: // just removed m_PaidInsurance list
|
||||||
case 12:
|
case 12:
|
||||||
{
|
{
|
||||||
BOBFilter = new BOBFilter();
|
BOBFilter = new BOBFilter(this);
|
||||||
BOBFilter.Deserialize(reader);
|
BOBFilter.Deserialize(reader);
|
||||||
goto case 11;
|
goto case 11;
|
||||||
}
|
}
|
||||||
|
|
@ -3081,7 +3081,7 @@ namespace Server.Mobiles
|
||||||
}
|
}
|
||||||
|
|
||||||
PermaFlags ??= new List<Mobile>();
|
PermaFlags ??= new List<Mobile>();
|
||||||
BOBFilter ??= new BOBFilter();
|
BOBFilter ??= new BOBFilter(this);
|
||||||
|
|
||||||
// Default to member if going from older version to new version (only time it should be null)
|
// Default to member if going from older version to new version (only time it should be null)
|
||||||
m_GuildRank ??= RankDefinition.Member;
|
m_GuildRank ??= RankDefinition.Member;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using ModernUO.Serialization;
|
||||||
using Server.Collections;
|
using Server.Collections;
|
||||||
using Server.ContextMenus;
|
using Server.ContextMenus;
|
||||||
using Server.Engines.BulkOrders;
|
using Server.Engines.BulkOrders;
|
||||||
|
|
@ -24,7 +25,8 @@ namespace Server.Mobiles
|
||||||
ThighBoots
|
ThighBoots
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class BaseVendor : BaseCreature, IVendor
|
[SerializationGenerator(2, false)]
|
||||||
|
public abstract partial class BaseVendor : BaseCreature, IVendor
|
||||||
{
|
{
|
||||||
private static readonly ILogger logger = LogFactory.GetLogger(typeof(BaseVendor));
|
private static readonly ILogger logger = LogFactory.GetLogger(typeof(BaseVendor));
|
||||||
private const int MaxSell = 500;
|
private const int MaxSell = 500;
|
||||||
|
|
@ -1297,109 +1299,28 @@ namespace Server.Mobiles
|
||||||
Region.GetRegion<GuardedRegion>()?.CheckVendorAccess(this, from) != false ||
|
Region.GetRegion<GuardedRegion>()?.CheckVendorAccess(this, from) != false ||
|
||||||
Region != from.Region && from.Region.GetRegion<GuardedRegion>()?.CheckVendorAccess(this, from) != false;
|
Region != from.Region && from.Region.GetRegion<GuardedRegion>()?.CheckVendorAccess(this, from) != false;
|
||||||
|
|
||||||
public override void Serialize(IGenericWriter writer)
|
[AfterDeserialization]
|
||||||
|
private void AfterDeserialization()
|
||||||
{
|
{
|
||||||
base.Serialize(writer);
|
|
||||||
|
|
||||||
writer.Write(1); // version
|
|
||||||
|
|
||||||
var sbInfos = SBInfos;
|
|
||||||
|
|
||||||
for (var i = 0; i < sbInfos?.Count; ++i)
|
|
||||||
{
|
|
||||||
var sbInfo = sbInfos[i];
|
|
||||||
var buyInfo = sbInfo.BuyInfo;
|
|
||||||
|
|
||||||
for (var j = 0; j < buyInfo?.Count; ++j)
|
|
||||||
{
|
|
||||||
var gbi = buyInfo[j];
|
|
||||||
|
|
||||||
var maxAmount = gbi.MaxAmount;
|
|
||||||
|
|
||||||
var doubled = maxAmount switch
|
|
||||||
{
|
|
||||||
40 => 1,
|
|
||||||
80 => 2,
|
|
||||||
160 => 3,
|
|
||||||
320 => 4,
|
|
||||||
640 => 5,
|
|
||||||
999 => 6,
|
|
||||||
_ => 0
|
|
||||||
};
|
|
||||||
|
|
||||||
if (doubled > 0)
|
|
||||||
{
|
|
||||||
writer.WriteEncodedInt(1 + j * sbInfos.Count + i);
|
|
||||||
writer.WriteEncodedInt(doubled);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.WriteEncodedInt(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Deserialize(IGenericReader reader)
|
|
||||||
{
|
|
||||||
base.Deserialize(reader);
|
|
||||||
|
|
||||||
var version = reader.ReadInt();
|
|
||||||
|
|
||||||
LoadSBInfo();
|
LoadSBInfo();
|
||||||
|
|
||||||
var sbInfos = SBInfos;
|
|
||||||
|
|
||||||
switch (version)
|
|
||||||
{
|
|
||||||
case 1:
|
|
||||||
{
|
|
||||||
int index;
|
|
||||||
|
|
||||||
while ((index = reader.ReadEncodedInt()) > 0)
|
|
||||||
{
|
|
||||||
var doubled = reader.ReadEncodedInt();
|
|
||||||
|
|
||||||
if (sbInfos != null)
|
|
||||||
{
|
|
||||||
index -= 1;
|
|
||||||
var sbInfoIndex = index % sbInfos.Count;
|
|
||||||
var buyInfoIndex = index / sbInfos.Count;
|
|
||||||
|
|
||||||
if (sbInfoIndex >= 0 && sbInfoIndex < sbInfos.Count)
|
|
||||||
{
|
|
||||||
var sbInfo = sbInfos[sbInfoIndex];
|
|
||||||
var buyInfo = sbInfo.BuyInfo;
|
|
||||||
|
|
||||||
if (buyInfo != null && buyInfoIndex >= 0 && buyInfoIndex < buyInfo.Count)
|
|
||||||
{
|
|
||||||
var gbi = buyInfo[buyInfoIndex];
|
|
||||||
|
|
||||||
var amount = doubled switch
|
|
||||||
{
|
|
||||||
1 => 40,
|
|
||||||
2 => 80,
|
|
||||||
3 => 160,
|
|
||||||
4 => 320,
|
|
||||||
5 => 640,
|
|
||||||
6 => 999,
|
|
||||||
_ => 20
|
|
||||||
};
|
|
||||||
|
|
||||||
gbi.Amount = gbi.MaxAmount = amount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsParagon)
|
if (IsParagon)
|
||||||
{
|
{
|
||||||
IsParagon = false;
|
IsParagon = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Version 1 persisted which buy entries had grown restock amounts, packed by index into
|
||||||
|
// the live SBInfos tables. Restock is transient now: it rebuilds from SBInfos on load, so
|
||||||
|
// the pairs are read and discarded.
|
||||||
|
private void Deserialize(IGenericReader reader, int version)
|
||||||
|
{
|
||||||
|
while (reader.ReadEncodedInt() > 0)
|
||||||
|
{
|
||||||
|
reader.ReadEncodedInt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override void AddCustomContextEntries(Mobile from, ref PooledRefList<ContextMenuEntry> list)
|
public override void AddCustomContextEntries(Mobile from, ref PooledRefList<ContextMenuEntry> list)
|
||||||
{
|
{
|
||||||
if (from.Alive && IsActiveVendor)
|
if (from.Alive && IsActiveVendor)
|
||||||
|
|
|
||||||
|
|
@ -188,7 +188,7 @@ public partial class PlayerVendor : Mobile
|
||||||
for (var i = 0; i < count; i++)
|
for (var i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
var item = reader.ReadEntity<Item>();
|
var item = reader.ReadEntity<Item>();
|
||||||
var vi = new VendorItem();
|
var vi = new VendorItem(this);
|
||||||
vi.Deserialize(reader);
|
vi.Deserialize(reader);
|
||||||
_sellItems[item] = vi;
|
_sellItems[item] = vi;
|
||||||
}
|
}
|
||||||
|
|
@ -447,7 +447,7 @@ public partial class PlayerVendor : Mobile
|
||||||
{
|
{
|
||||||
RemoveVendorItem(item);
|
RemoveVendorItem(item);
|
||||||
|
|
||||||
var vi = new VendorItem(item, price, description, created);
|
var vi = new VendorItem(this, item, price, description, created);
|
||||||
ReplaceInSellItems(item, vi);
|
ReplaceInSellItems(item, vi);
|
||||||
|
|
||||||
item.InvalidateProperties();
|
item.InvalidateProperties();
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,9 @@ namespace Server.Mobiles;
|
||||||
[SerializationGenerator(0, false)]
|
[SerializationGenerator(0, false)]
|
||||||
public partial class VendorItem
|
public partial class VendorItem
|
||||||
{
|
{
|
||||||
|
[DirtyTrackingEntity]
|
||||||
|
private PlayerVendor _vendor;
|
||||||
|
|
||||||
[SerializableField(0)]
|
[SerializableField(0)]
|
||||||
private Item _item;
|
private Item _item;
|
||||||
|
|
||||||
|
|
@ -16,12 +19,13 @@ public partial class VendorItem
|
||||||
[SerializableField(3)]
|
[SerializableField(3)]
|
||||||
private DateTime _created;
|
private DateTime _created;
|
||||||
|
|
||||||
public VendorItem()
|
// The generator deserializes dictionary values through this constructor so every entry
|
||||||
{
|
// knows its vendor.
|
||||||
}
|
public VendorItem(PlayerVendor vendor) => _vendor = vendor;
|
||||||
|
|
||||||
public VendorItem(Item item, int price, string description, DateTime created)
|
public VendorItem(PlayerVendor vendor, Item item, int price, string description, DateTime created)
|
||||||
{
|
{
|
||||||
|
_vendor = vendor;
|
||||||
_item = item;
|
_item = item;
|
||||||
_price = price;
|
_price = price;
|
||||||
_description = description ?? "";
|
_description = description ?? "";
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,18 @@
|
||||||
using System;
|
using System;
|
||||||
using ModernUO.Serialization;
|
using ModernUO.Serialization;
|
||||||
|
using Server.Mobiles;
|
||||||
|
|
||||||
namespace Server.Systems.JailSystem;
|
namespace Server.Systems.JailSystem;
|
||||||
|
|
||||||
[SerializationGenerator(0)]
|
[SerializationGenerator(0)]
|
||||||
public partial class JailRecord
|
public partial class JailRecord
|
||||||
{
|
{
|
||||||
|
[DirtyTrackingEntity]
|
||||||
|
[CanBeNull]
|
||||||
|
private PlayerMobile _player;
|
||||||
|
|
||||||
|
public JailRecord(PlayerMobile player) => _player = player;
|
||||||
|
|
||||||
[SerializableField(0)]
|
[SerializableField(0)]
|
||||||
private int _jailCount;
|
private int _jailCount;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ public class JailSystem : GenericPersistence
|
||||||
|
|
||||||
// Jail map, change this for custom maps
|
// Jail map, change this for custom maps
|
||||||
public static readonly Map JailMap = Map.Felucca;
|
public static readonly Map JailMap = Map.Felucca;
|
||||||
private static readonly JailRecord EmptyRecord = new();
|
private static readonly JailRecord EmptyRecord = new(null);
|
||||||
|
|
||||||
private static readonly HashSet<PlayerMobile> CurrentlyBeingJailed = [];
|
private static readonly HashSet<PlayerMobile> CurrentlyBeingJailed = [];
|
||||||
private static readonly Dictionary<PlayerMobile, JailRecord> PlayerJailRecords = [];
|
private static readonly Dictionary<PlayerMobile, JailRecord> PlayerJailRecords = [];
|
||||||
|
|
@ -96,7 +96,7 @@ public class JailSystem : GenericPersistence
|
||||||
|
|
||||||
if (!PlayerJailRecords.TryGetValue(player, out var record))
|
if (!PlayerJailRecords.TryGetValue(player, out var record))
|
||||||
{
|
{
|
||||||
PlayerJailRecords[player] = record = new JailRecord();
|
PlayerJailRecords[player] = record = new JailRecord(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
record.JailCount++;
|
record.JailCount++;
|
||||||
|
|
@ -409,7 +409,7 @@ public class JailSystem : GenericPersistence
|
||||||
for (var i = 0; i < count; i++)
|
for (var i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
var player = reader.ReadEntity<PlayerMobile>();
|
var player = reader.ReadEntity<PlayerMobile>();
|
||||||
var record = new JailRecord();
|
var record = new JailRecord(player);
|
||||||
record.Deserialize(reader);
|
record.Deserialize(reader);
|
||||||
|
|
||||||
if (player != null)
|
if (player != null)
|
||||||
|
|
|
||||||
|
|
@ -50,8 +50,8 @@
|
||||||
<PackageReference Include="ModernUO.CodeGeneratedEvents.Generator" Version="1.0.3.2" PrivateAssets="all" />
|
<PackageReference Include="ModernUO.CodeGeneratedEvents.Generator" Version="1.0.3.2" PrivateAssets="all" />
|
||||||
<PackageReference Include="ZstdNet" Version="1.5.7" />
|
<PackageReference Include="ZstdNet" Version="1.5.7" />
|
||||||
|
|
||||||
<PackageReference Include="ModernUO.Serialization.Annotations" Version="4.0.0" />
|
<PackageReference Include="ModernUO.Serialization.Annotations" Version="4.1.0" />
|
||||||
<PackageReference Include="ModernUO.Serialization.Generator" Version="4.0.0" PrivateAssets="all" />
|
<PackageReference Include="ModernUO.Serialization.Generator" Version="4.1.0" PrivateAssets="all" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AdditionalFiles Include="Migrations/*.v*.json" />
|
<AdditionalFiles Include="Migrations/*.v*.json" />
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue