refactor: serialize one master reference; fold the SummonMaster lockstep
ControlMaster and SummonMaster, when both set, are always the same mobile: BaseCreature.Summon assigns both to the caster, and every pet management flow (transfer, stable, claim, ball of summoning, GM obey, login overflow) followed SetControlMaster with an identical SummonMaster assignment. They differ only in presence - uncontrolled summons carry only a summon master, pets only a control master. So one _master reference serializes (refreshed at save, fanned back out through the Controlled/Summoned flags in AfterDeserialization; legacy loads feed the same path), and SetControlMaster now keeps SummonMaster in lockstep itself, deleting the six hand-rolled copies of that boilerplate. Also: a creature constructed without speeds (missing npc-speeds.json) now logs debug and defaults to Medium (0.25/0.5) instead of throwing - this is the place a sane default belongs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
6d7eb24cc1
commit
a082202e98
8 changed files with 102 additions and 91 deletions
|
|
@ -164,6 +164,27 @@ public class BaseCreatureSerializationTests : IDisposable
|
||||||
Assert.Equal(master, copy.LastOwner);
|
Assert.Equal(master, copy.LastOwner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void UncontrolledSummon_KeepsItsSummonMaster()
|
||||||
|
{
|
||||||
|
var bc = NewCreature();
|
||||||
|
var master = new PlayerMobile(World.NewMobile);
|
||||||
|
master.DefaultMobileInit();
|
||||||
|
World.AddEntity(master);
|
||||||
|
_created.Add(master);
|
||||||
|
|
||||||
|
// Energy vortex-style: summoned with a master, never controlled.
|
||||||
|
bc.Summoned = true;
|
||||||
|
bc.SummonMaster = master;
|
||||||
|
|
||||||
|
var copy = Load(Snapshot(bc));
|
||||||
|
|
||||||
|
Assert.True(copy.Summoned);
|
||||||
|
Assert.False(copy.Controlled);
|
||||||
|
Assert.Equal(master, copy.SummonMaster);
|
||||||
|
Assert.Null(copy.ControlMaster);
|
||||||
|
}
|
||||||
|
|
||||||
private sealed class BucketStub : BaseCreature
|
private sealed class BucketStub : BaseCreature
|
||||||
{
|
{
|
||||||
public BucketStub() : base(AIType.AI_Melee) => Body = 0xC9;
|
public BucketStub() : base(AIType.AI_Melee) => Body = 0xC9;
|
||||||
|
|
|
||||||
|
|
@ -227,12 +227,6 @@ public partial class BallOfSummoning : Item, TranslocationItem
|
||||||
if (pet.IsStabled)
|
if (pet.IsStabled)
|
||||||
{
|
{
|
||||||
pet.SetControlMaster(from);
|
pet.SetControlMaster(from);
|
||||||
|
|
||||||
if (pet.Summoned)
|
|
||||||
{
|
|
||||||
pet.SummonMaster = from;
|
|
||||||
}
|
|
||||||
|
|
||||||
pet.ControlTarget = from;
|
pet.ControlTarget = from;
|
||||||
pet.ControlOrder = OrderType.Follow;
|
pet.ControlOrder = OrderType.Follow;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -133,12 +133,6 @@
|
||||||
""
|
""
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "ControlMaster",
|
|
||||||
"type": "Server.Mobile",
|
|
||||||
"usesSaveFlag": true,
|
|
||||||
"rule": "SerializableInterfaceMigrationRule"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "ControlTarget",
|
"name": "ControlTarget",
|
||||||
"type": "Server.Mobile",
|
"type": "Server.Mobile",
|
||||||
|
|
@ -197,7 +191,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "SummonMaster",
|
"name": "Master",
|
||||||
"type": "Server.Mobile",
|
"type": "Server.Mobile",
|
||||||
"usesSaveFlag": true,
|
"usesSaveFlag": true,
|
||||||
"rule": "SerializableInterfaceMigrationRule"
|
"rule": "SerializableInterfaceMigrationRule"
|
||||||
|
|
|
||||||
|
|
@ -447,11 +447,6 @@ public abstract partial class BaseAI
|
||||||
if (Mobile.FindMyName(e.Speech, true) && e.Speech.InsensitiveContains("obey"))
|
if (Mobile.FindMyName(e.Speech, true) && e.Speech.InsensitiveContains("obey"))
|
||||||
{
|
{
|
||||||
Mobile.SetControlMaster(e.Mobile);
|
Mobile.SetControlMaster(e.Mobile);
|
||||||
|
|
||||||
if (Mobile.Summoned)
|
|
||||||
{
|
|
||||||
Mobile.SummonMaster = e.Mobile;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -156,11 +156,6 @@ internal sealed partial class TransferItem : Item
|
||||||
|
|
||||||
private void TransferPetOwnership(Mobile from, Mobile to)
|
private void TransferPetOwnership(Mobile from, Mobile to)
|
||||||
{
|
{
|
||||||
if (_creature.Summoned)
|
|
||||||
{
|
|
||||||
_creature.SummonMaster = to;
|
|
||||||
}
|
|
||||||
|
|
||||||
_creature.ControlTarget = to;
|
_creature.ControlTarget = to;
|
||||||
_creature.ControlOrder = OrderType.Follow;
|
_creature.ControlOrder = OrderType.Follow;
|
||||||
_creature.BondingBegin = DateTime.MinValue;
|
_creature.BondingBegin = DateTime.MinValue;
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ using Server.Engines.Virtues;
|
||||||
using Server.Ethics;
|
using Server.Ethics;
|
||||||
using Server.Factions;
|
using Server.Factions;
|
||||||
using Server.Items;
|
using Server.Items;
|
||||||
|
using Server.Logging;
|
||||||
using Server.Misc;
|
using Server.Misc;
|
||||||
using Server.Multis;
|
using Server.Multis;
|
||||||
using Server.Network;
|
using Server.Network;
|
||||||
|
|
@ -136,6 +137,8 @@ namespace Server.Mobiles
|
||||||
[SerializationGenerator(23, false)]
|
[SerializationGenerator(23, false)]
|
||||||
public abstract partial class BaseCreature : Mobile, IHonorTarget, IQuestGiver
|
public abstract partial class BaseCreature : Mobile, IHonorTarget, IQuestGiver
|
||||||
{
|
{
|
||||||
|
private static readonly ILogger logger = LogFactory.GetLogger(typeof(BaseCreature));
|
||||||
|
|
||||||
public enum Allegiance
|
public enum Allegiance
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
|
|
@ -491,43 +494,41 @@ namespace Server.Mobiles
|
||||||
InvalidateProperties();
|
InvalidateProperties();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Field 16: ControlMaster (hand-written property; follower bookkeeping brackets the assignment)
|
// ControlMaster and SummonMaster serialize as one master reference (Master below).
|
||||||
private Mobile _controlMaster;
|
private Mobile _controlMaster;
|
||||||
|
|
||||||
private bool ShouldSerializeControlMaster() => _controlMaster != null;
|
[SerializableField(16)]
|
||||||
|
|
||||||
[SerializableField(17)]
|
|
||||||
[SaveFlag(nameof(ShouldSerializeControlTarget))]
|
[SaveFlag(nameof(ShouldSerializeControlTarget))]
|
||||||
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
||||||
private Mobile _controlTarget;
|
private Mobile _controlTarget;
|
||||||
|
|
||||||
private bool ShouldSerializeControlTarget() => _controlTarget != null;
|
private bool ShouldSerializeControlTarget() => _controlTarget != null;
|
||||||
|
|
||||||
[SerializableField(18)]
|
[SerializableField(17)]
|
||||||
[SaveFlag(nameof(ShouldSerializeControlDest))]
|
[SaveFlag(nameof(ShouldSerializeControlDest))]
|
||||||
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
||||||
private Point3D _controlDest;
|
private Point3D _controlDest;
|
||||||
|
|
||||||
private bool ShouldSerializeControlDest() => _controlDest != Point3D.Zero;
|
private bool ShouldSerializeControlDest() => _controlDest != Point3D.Zero;
|
||||||
|
|
||||||
// Field 19: ControlOrder (hand-written property; order logic must run on equal re-assignment)
|
// Field 18: ControlOrder (hand-written property; order logic must run on equal re-assignment)
|
||||||
private OrderType _controlOrder;
|
private OrderType _controlOrder;
|
||||||
|
|
||||||
private bool ShouldSerializeControlOrder() => _controlOrder != OrderType.None;
|
private bool ShouldSerializeControlOrder() => _controlOrder != OrderType.None;
|
||||||
|
|
||||||
[SerializableField(20)]
|
[SerializableField(19)]
|
||||||
[SaveFlag(nameof(ShouldSerializeMinTameSkill))]
|
[SaveFlag(nameof(ShouldSerializeMinTameSkill))]
|
||||||
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
||||||
private double _minTameSkill;
|
private double _minTameSkill;
|
||||||
|
|
||||||
private bool ShouldSerializeMinTameSkill() => _minTameSkill != 0;
|
private bool ShouldSerializeMinTameSkill() => _minTameSkill != 0;
|
||||||
|
|
||||||
// Field 21: Tamable (hand-written property; custom getter masks paragons)
|
// Field 20: Tamable (hand-written property; custom getter masks paragons)
|
||||||
private bool _tamable;
|
private bool _tamable;
|
||||||
|
|
||||||
private bool ShouldSerializeTamable() => _tamable;
|
private bool ShouldSerializeTamable() => _tamable;
|
||||||
|
|
||||||
[SerializableField(22, fieldChanged: nameof(OnSummonedChange))]
|
[SerializableField(21, fieldChanged: nameof(OnSummonedChange))]
|
||||||
[SaveFlag(nameof(ShouldSerializeSummoned))]
|
[SaveFlag(nameof(ShouldSerializeSummoned))]
|
||||||
[SerializedCommandProperty(AccessLevel.Administrator)]
|
[SerializedCommandProperty(AccessLevel.Administrator)]
|
||||||
private bool _summoned;
|
private bool _summoned;
|
||||||
|
|
@ -542,19 +543,30 @@ namespace Server.Mobiles
|
||||||
}
|
}
|
||||||
|
|
||||||
[AnchoredDateTime]
|
[AnchoredDateTime]
|
||||||
[SerializableField(23, getter: "protected", setter: "protected")]
|
[SerializableField(22, getter: "protected", setter: "protected")]
|
||||||
[SaveFlag(nameof(ShouldSerializeSummonEnd))]
|
[SaveFlag(nameof(ShouldSerializeSummonEnd))]
|
||||||
private DateTime _summonEnd;
|
private DateTime _summonEnd;
|
||||||
|
|
||||||
private bool ShouldSerializeSummonEnd() => _summoned;
|
private bool ShouldSerializeSummonEnd() => _summoned;
|
||||||
|
|
||||||
// Field 24: SummonMaster (hand-written property; follower bookkeeping brackets the assignment)
|
|
||||||
private Mobile _summonMaster;
|
private Mobile _summonMaster;
|
||||||
|
|
||||||
private bool ShouldSerializeSummonMaster() => _summonMaster != null;
|
// When both roles are set they are always the same mobile (every management flow
|
||||||
|
// assigns them in lockstep via SetControlMaster), so one reference serializes -
|
||||||
|
// refreshed here at save - and fans back out through the Controlled/Summoned
|
||||||
|
// flags in AfterDeserialization.
|
||||||
|
[SerializableField(23, getter: "private", setter: "private")]
|
||||||
|
[SaveFlag(nameof(ShouldSerializeMaster))]
|
||||||
|
private Mobile _master;
|
||||||
|
|
||||||
|
private bool ShouldSerializeMaster()
|
||||||
|
{
|
||||||
|
_master = _controlMaster ?? _summonMaster;
|
||||||
|
return _master != null;
|
||||||
|
}
|
||||||
|
|
||||||
[EncodedInt]
|
[EncodedInt]
|
||||||
[SerializableField(25)]
|
[SerializableField(24)]
|
||||||
[SaveFlag(nameof(ShouldSerializeControlSlots), nameof(ControlSlotsDefaultValue))]
|
[SaveFlag(nameof(ShouldSerializeControlSlots), nameof(ControlSlotsDefaultValue))]
|
||||||
[SerializedCommandProperty(AccessLevel.Administrator)]
|
[SerializedCommandProperty(AccessLevel.Administrator)]
|
||||||
private int _controlSlots = 1;
|
private int _controlSlots = 1;
|
||||||
|
|
@ -564,7 +576,7 @@ namespace Server.Mobiles
|
||||||
private int ControlSlotsDefaultValue() => 1;
|
private int ControlSlotsDefaultValue() => 1;
|
||||||
|
|
||||||
[EncodedInt]
|
[EncodedInt]
|
||||||
[SerializableField(26, allowFieldChange: nameof(ClampLoyalty))]
|
[SerializableField(25, allowFieldChange: nameof(ClampLoyalty))]
|
||||||
[SaveFlag(nameof(ShouldSerializeLoyalty), nameof(LoyaltyDefaultValue))]
|
[SaveFlag(nameof(ShouldSerializeLoyalty), nameof(LoyaltyDefaultValue))]
|
||||||
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
||||||
private int _loyalty;
|
private int _loyalty;
|
||||||
|
|
@ -579,7 +591,7 @@ namespace Server.Mobiles
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
[SerializableField(27)]
|
[SerializableField(26)]
|
||||||
[SaveFlag(nameof(ShouldSerializeCurrentWayPoint))]
|
[SaveFlag(nameof(ShouldSerializeCurrentWayPoint))]
|
||||||
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
||||||
private WayPoint _currentWayPoint;
|
private WayPoint _currentWayPoint;
|
||||||
|
|
@ -587,7 +599,7 @@ namespace Server.Mobiles
|
||||||
private bool ShouldSerializeCurrentWayPoint() => _currentWayPoint != null;
|
private bool ShouldSerializeCurrentWayPoint() => _currentWayPoint != null;
|
||||||
|
|
||||||
[EncodedInt]
|
[EncodedInt]
|
||||||
[SerializableField(28)]
|
[SerializableField(27)]
|
||||||
[SaveFlag(nameof(ShouldSerializeHitsMaxSeed), nameof(HitsMaxSeedDefaultValue))]
|
[SaveFlag(nameof(ShouldSerializeHitsMaxSeed), nameof(HitsMaxSeedDefaultValue))]
|
||||||
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
||||||
private int _hitsMaxSeed = -1;
|
private int _hitsMaxSeed = -1;
|
||||||
|
|
@ -597,7 +609,7 @@ namespace Server.Mobiles
|
||||||
private int HitsMaxSeedDefaultValue() => -1;
|
private int HitsMaxSeedDefaultValue() => -1;
|
||||||
|
|
||||||
[EncodedInt]
|
[EncodedInt]
|
||||||
[SerializableField(29)]
|
[SerializableField(28)]
|
||||||
[SaveFlag(nameof(ShouldSerializeStamMaxSeed), nameof(StamMaxSeedDefaultValue))]
|
[SaveFlag(nameof(ShouldSerializeStamMaxSeed), nameof(StamMaxSeedDefaultValue))]
|
||||||
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
||||||
private int _stamMaxSeed = -1;
|
private int _stamMaxSeed = -1;
|
||||||
|
|
@ -607,7 +619,7 @@ namespace Server.Mobiles
|
||||||
private int StamMaxSeedDefaultValue() => -1;
|
private int StamMaxSeedDefaultValue() => -1;
|
||||||
|
|
||||||
[EncodedInt]
|
[EncodedInt]
|
||||||
[SerializableField(30)]
|
[SerializableField(29)]
|
||||||
[SaveFlag(nameof(ShouldSerializeManaMaxSeed), nameof(ManaMaxSeedDefaultValue))]
|
[SaveFlag(nameof(ShouldSerializeManaMaxSeed), nameof(ManaMaxSeedDefaultValue))]
|
||||||
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
||||||
private int _manaMaxSeed = -1;
|
private int _manaMaxSeed = -1;
|
||||||
|
|
@ -617,7 +629,7 @@ namespace Server.Mobiles
|
||||||
private int ManaMaxSeedDefaultValue() => -1;
|
private int ManaMaxSeedDefaultValue() => -1;
|
||||||
|
|
||||||
[EncodedInt]
|
[EncodedInt]
|
||||||
[SerializableField(31)]
|
[SerializableField(30)]
|
||||||
[SaveFlag(nameof(ShouldSerializeDamageMin), nameof(DamageMinDefaultValue))]
|
[SaveFlag(nameof(ShouldSerializeDamageMin), nameof(DamageMinDefaultValue))]
|
||||||
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
||||||
private int _damageMin = -1;
|
private int _damageMin = -1;
|
||||||
|
|
@ -627,7 +639,7 @@ namespace Server.Mobiles
|
||||||
private int DamageMinDefaultValue() => -1;
|
private int DamageMinDefaultValue() => -1;
|
||||||
|
|
||||||
[EncodedInt]
|
[EncodedInt]
|
||||||
[SerializableField(32)]
|
[SerializableField(31)]
|
||||||
[SaveFlag(nameof(ShouldSerializeDamageMax), nameof(DamageMaxDefaultValue))]
|
[SaveFlag(nameof(ShouldSerializeDamageMax), nameof(DamageMaxDefaultValue))]
|
||||||
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
||||||
private int _damageMax = -1;
|
private int _damageMax = -1;
|
||||||
|
|
@ -637,7 +649,7 @@ namespace Server.Mobiles
|
||||||
private int DamageMaxDefaultValue() => -1;
|
private int DamageMaxDefaultValue() => -1;
|
||||||
|
|
||||||
[EncodedInt]
|
[EncodedInt]
|
||||||
[SerializableField(33, fieldChanged: nameof(OnResistanceSeedChange))]
|
[SerializableField(32, fieldChanged: nameof(OnResistanceSeedChange))]
|
||||||
[SaveFlag(nameof(ShouldSerializePhysicalResistanceSeed))]
|
[SaveFlag(nameof(ShouldSerializePhysicalResistanceSeed))]
|
||||||
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
||||||
private int _physicalResistanceSeed;
|
private int _physicalResistanceSeed;
|
||||||
|
|
@ -647,7 +659,7 @@ namespace Server.Mobiles
|
||||||
private void OnResistanceSeedChange(int oldValue, int newValue) => UpdateResistances();
|
private void OnResistanceSeedChange(int oldValue, int newValue) => UpdateResistances();
|
||||||
|
|
||||||
[EncodedInt]
|
[EncodedInt]
|
||||||
[SerializableField(34, fieldChanged: nameof(OnResistanceSeedChange))]
|
[SerializableField(33, fieldChanged: nameof(OnResistanceSeedChange))]
|
||||||
[SaveFlag(nameof(ShouldSerializeFireResistSeed))]
|
[SaveFlag(nameof(ShouldSerializeFireResistSeed))]
|
||||||
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
||||||
private int _fireResistSeed;
|
private int _fireResistSeed;
|
||||||
|
|
@ -655,7 +667,7 @@ namespace Server.Mobiles
|
||||||
private bool ShouldSerializeFireResistSeed() => _fireResistSeed != 0;
|
private bool ShouldSerializeFireResistSeed() => _fireResistSeed != 0;
|
||||||
|
|
||||||
[EncodedInt]
|
[EncodedInt]
|
||||||
[SerializableField(35, fieldChanged: nameof(OnResistanceSeedChange))]
|
[SerializableField(34, fieldChanged: nameof(OnResistanceSeedChange))]
|
||||||
[SaveFlag(nameof(ShouldSerializeColdResistSeed))]
|
[SaveFlag(nameof(ShouldSerializeColdResistSeed))]
|
||||||
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
||||||
private int _coldResistSeed;
|
private int _coldResistSeed;
|
||||||
|
|
@ -663,7 +675,7 @@ namespace Server.Mobiles
|
||||||
private bool ShouldSerializeColdResistSeed() => _coldResistSeed != 0;
|
private bool ShouldSerializeColdResistSeed() => _coldResistSeed != 0;
|
||||||
|
|
||||||
[EncodedInt]
|
[EncodedInt]
|
||||||
[SerializableField(36, fieldChanged: nameof(OnResistanceSeedChange))]
|
[SerializableField(35, fieldChanged: nameof(OnResistanceSeedChange))]
|
||||||
[SaveFlag(nameof(ShouldSerializePoisonResistSeed))]
|
[SaveFlag(nameof(ShouldSerializePoisonResistSeed))]
|
||||||
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
||||||
private int _poisonResistSeed;
|
private int _poisonResistSeed;
|
||||||
|
|
@ -671,7 +683,7 @@ namespace Server.Mobiles
|
||||||
private bool ShouldSerializePoisonResistSeed() => _poisonResistSeed != 0;
|
private bool ShouldSerializePoisonResistSeed() => _poisonResistSeed != 0;
|
||||||
|
|
||||||
[EncodedInt]
|
[EncodedInt]
|
||||||
[SerializableField(37, fieldChanged: nameof(OnResistanceSeedChange))]
|
[SerializableField(36, fieldChanged: nameof(OnResistanceSeedChange))]
|
||||||
[SaveFlag(nameof(ShouldSerializeEnergyResistSeed))]
|
[SaveFlag(nameof(ShouldSerializeEnergyResistSeed))]
|
||||||
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
||||||
private int _energyResistSeed;
|
private int _energyResistSeed;
|
||||||
|
|
@ -679,7 +691,7 @@ namespace Server.Mobiles
|
||||||
private bool ShouldSerializeEnergyResistSeed() => _energyResistSeed != 0;
|
private bool ShouldSerializeEnergyResistSeed() => _energyResistSeed != 0;
|
||||||
|
|
||||||
[EncodedInt]
|
[EncodedInt]
|
||||||
[SerializableField(38)]
|
[SerializableField(37)]
|
||||||
[SaveFlag(nameof(ShouldSerializePhysicalDamage), nameof(PhysicalDamageDefaultValue))]
|
[SaveFlag(nameof(ShouldSerializePhysicalDamage), nameof(PhysicalDamageDefaultValue))]
|
||||||
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
||||||
private int _physicalDamage = 100;
|
private int _physicalDamage = 100;
|
||||||
|
|
@ -689,7 +701,7 @@ namespace Server.Mobiles
|
||||||
private int PhysicalDamageDefaultValue() => 100;
|
private int PhysicalDamageDefaultValue() => 100;
|
||||||
|
|
||||||
[EncodedInt]
|
[EncodedInt]
|
||||||
[SerializableField(39)]
|
[SerializableField(38)]
|
||||||
[SaveFlag(nameof(ShouldSerializeFireDamage))]
|
[SaveFlag(nameof(ShouldSerializeFireDamage))]
|
||||||
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
||||||
private int _fireDamage;
|
private int _fireDamage;
|
||||||
|
|
@ -697,7 +709,7 @@ namespace Server.Mobiles
|
||||||
private bool ShouldSerializeFireDamage() => _fireDamage != 0;
|
private bool ShouldSerializeFireDamage() => _fireDamage != 0;
|
||||||
|
|
||||||
[EncodedInt]
|
[EncodedInt]
|
||||||
[SerializableField(40)]
|
[SerializableField(39)]
|
||||||
[SaveFlag(nameof(ShouldSerializeColdDamage))]
|
[SaveFlag(nameof(ShouldSerializeColdDamage))]
|
||||||
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
||||||
private int _coldDamage;
|
private int _coldDamage;
|
||||||
|
|
@ -705,7 +717,7 @@ namespace Server.Mobiles
|
||||||
private bool ShouldSerializeColdDamage() => _coldDamage != 0;
|
private bool ShouldSerializeColdDamage() => _coldDamage != 0;
|
||||||
|
|
||||||
[EncodedInt]
|
[EncodedInt]
|
||||||
[SerializableField(41)]
|
[SerializableField(40)]
|
||||||
[SaveFlag(nameof(ShouldSerializePoisonDamage))]
|
[SaveFlag(nameof(ShouldSerializePoisonDamage))]
|
||||||
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
||||||
private int _poisonDamage;
|
private int _poisonDamage;
|
||||||
|
|
@ -713,7 +725,7 @@ namespace Server.Mobiles
|
||||||
private bool ShouldSerializePoisonDamage() => _poisonDamage != 0;
|
private bool ShouldSerializePoisonDamage() => _poisonDamage != 0;
|
||||||
|
|
||||||
[EncodedInt]
|
[EncodedInt]
|
||||||
[SerializableField(42)]
|
[SerializableField(41)]
|
||||||
[SaveFlag(nameof(ShouldSerializeEnergyDamage))]
|
[SaveFlag(nameof(ShouldSerializeEnergyDamage))]
|
||||||
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
||||||
private int _energyDamage;
|
private int _energyDamage;
|
||||||
|
|
@ -721,7 +733,7 @@ namespace Server.Mobiles
|
||||||
private bool ShouldSerializeEnergyDamage() => _energyDamage != 0;
|
private bool ShouldSerializeEnergyDamage() => _energyDamage != 0;
|
||||||
|
|
||||||
[Tidy]
|
[Tidy]
|
||||||
[SerializableField(43, setter: "private")]
|
[SerializableField(42, setter: "private")]
|
||||||
[SaveFlag(nameof(ShouldSerializeOwners), nameof(OwnersDefaultValue))]
|
[SaveFlag(nameof(ShouldSerializeOwners), nameof(OwnersDefaultValue))]
|
||||||
private List<Mobile> _owners;
|
private List<Mobile> _owners;
|
||||||
|
|
||||||
|
|
@ -733,13 +745,13 @@ namespace Server.Mobiles
|
||||||
|
|
||||||
private List<Mobile> OwnersDefaultValue() => new();
|
private List<Mobile> OwnersDefaultValue() => new();
|
||||||
|
|
||||||
[SerializableField(44)]
|
[SerializableField(43)]
|
||||||
[SaveFlag(nameof(ShouldSerializeIsDeadPet))]
|
[SaveFlag(nameof(ShouldSerializeIsDeadPet))]
|
||||||
private bool _isDeadPet;
|
private bool _isDeadPet;
|
||||||
|
|
||||||
private bool ShouldSerializeIsDeadPet() => _isDeadPet;
|
private bool ShouldSerializeIsDeadPet() => _isDeadPet;
|
||||||
|
|
||||||
[SerializableField(45, fieldChanged: nameof(OnBondedChange))]
|
[SerializableField(44, fieldChanged: nameof(OnBondedChange))]
|
||||||
[SaveFlag(nameof(ShouldSerializeIsBonded))]
|
[SaveFlag(nameof(ShouldSerializeIsBonded))]
|
||||||
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
||||||
private bool _isBonded;
|
private bool _isBonded;
|
||||||
|
|
@ -748,33 +760,33 @@ namespace Server.Mobiles
|
||||||
|
|
||||||
private void OnBondedChange(bool oldValue, bool newValue) => InvalidateProperties();
|
private void OnBondedChange(bool oldValue, bool newValue) => InvalidateProperties();
|
||||||
|
|
||||||
[SerializableField(46)]
|
[SerializableField(45)]
|
||||||
[SaveFlag(nameof(ShouldSerializeBondingBegin))]
|
[SaveFlag(nameof(ShouldSerializeBondingBegin))]
|
||||||
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
||||||
private DateTime _bondingBegin;
|
private DateTime _bondingBegin;
|
||||||
|
|
||||||
private bool ShouldSerializeBondingBegin() => _bondingBegin != DateTime.MinValue;
|
private bool ShouldSerializeBondingBegin() => _bondingBegin != DateTime.MinValue;
|
||||||
|
|
||||||
[SerializableField(47)]
|
[SerializableField(46)]
|
||||||
[SaveFlag(nameof(ShouldSerializeOwnerAbandonTime))]
|
[SaveFlag(nameof(ShouldSerializeOwnerAbandonTime))]
|
||||||
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
||||||
private DateTime _ownerAbandonTime;
|
private DateTime _ownerAbandonTime;
|
||||||
|
|
||||||
private bool ShouldSerializeOwnerAbandonTime() => _ownerAbandonTime != DateTime.MinValue;
|
private bool ShouldSerializeOwnerAbandonTime() => _ownerAbandonTime != DateTime.MinValue;
|
||||||
|
|
||||||
[SerializableField(48)]
|
[SerializableField(47)]
|
||||||
[SaveFlag(nameof(ShouldSerializeHasGeneratedLoot))]
|
[SaveFlag(nameof(ShouldSerializeHasGeneratedLoot))]
|
||||||
private bool _hasGeneratedLoot;
|
private bool _hasGeneratedLoot;
|
||||||
|
|
||||||
private bool ShouldSerializeHasGeneratedLoot() => _hasGeneratedLoot;
|
private bool ShouldSerializeHasGeneratedLoot() => _hasGeneratedLoot;
|
||||||
|
|
||||||
// Field 49: IsParagon (hand-written property; the setter converts, which must not run at load)
|
// Field 48: IsParagon (hand-written property; the setter converts, which must not run at load)
|
||||||
private bool _isParagon;
|
private bool _isParagon;
|
||||||
|
|
||||||
private bool ShouldSerializeIsParagon() => _isParagon;
|
private bool ShouldSerializeIsParagon() => _isParagon;
|
||||||
|
|
||||||
[Tidy]
|
[Tidy]
|
||||||
[SerializableField(50, setter: "private")]
|
[SerializableField(49, setter: "private")]
|
||||||
[SaveFlag(nameof(ShouldSerializeFriends))]
|
[SaveFlag(nameof(ShouldSerializeFriends))]
|
||||||
private List<Mobile> _friends;
|
private List<Mobile> _friends;
|
||||||
|
|
||||||
|
|
@ -784,7 +796,7 @@ namespace Server.Mobiles
|
||||||
return _friends?.Count > 0;
|
return _friends?.Count > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
[SerializableField(51)]
|
[SerializableField(50)]
|
||||||
[SaveFlag(nameof(ShouldSerializeRemoveIfUntamed))]
|
[SaveFlag(nameof(ShouldSerializeRemoveIfUntamed))]
|
||||||
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
||||||
private bool _removeIfUntamed;
|
private bool _removeIfUntamed;
|
||||||
|
|
@ -792,14 +804,14 @@ namespace Server.Mobiles
|
||||||
private bool ShouldSerializeRemoveIfUntamed() => _removeIfUntamed;
|
private bool ShouldSerializeRemoveIfUntamed() => _removeIfUntamed;
|
||||||
|
|
||||||
[EncodedInt]
|
[EncodedInt]
|
||||||
[SerializableField(52)]
|
[SerializableField(51)]
|
||||||
[SaveFlag(nameof(ShouldSerializeRemoveStep))]
|
[SaveFlag(nameof(ShouldSerializeRemoveStep))]
|
||||||
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
||||||
private int _removeStep;
|
private int _removeStep;
|
||||||
|
|
||||||
private bool ShouldSerializeRemoveStep() => _removeStep != 0;
|
private bool ShouldSerializeRemoveStep() => _removeStep != 0;
|
||||||
|
|
||||||
[SerializableField(53, setter: "private")]
|
[SerializableField(52, setter: "private")]
|
||||||
[SaveFlag(nameof(ShouldSerializePendingDeleteTimer))]
|
[SaveFlag(nameof(ShouldSerializePendingDeleteTimer))]
|
||||||
[DeserializeTimer(nameof(DeserializePendingDeleteTimer))]
|
[DeserializeTimer(nameof(DeserializePendingDeleteTimer))]
|
||||||
private Timer _pendingDeleteTimer;
|
private Timer _pendingDeleteTimer;
|
||||||
|
|
@ -814,7 +826,7 @@ namespace Server.Mobiles
|
||||||
_pendingDeleteTimer.Start();
|
_pendingDeleteTimer.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
[SerializableField(54)]
|
[SerializableField(53)]
|
||||||
[SaveFlag(nameof(ShouldSerializeCorpseNameOverride))]
|
[SaveFlag(nameof(ShouldSerializeCorpseNameOverride))]
|
||||||
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
||||||
private string _corpseNameOverride;
|
private string _corpseNameOverride;
|
||||||
|
|
@ -880,10 +892,11 @@ namespace Server.Mobiles
|
||||||
|
|
||||||
if (_activeSpeed <= 0 || _passiveSpeed <= 0)
|
if (_activeSpeed <= 0 || _passiveSpeed <= 0)
|
||||||
{
|
{
|
||||||
// A 0-delay creature spins its AI timer at wheel resolution.
|
// A 0-delay creature would spin its AI timer at wheel resolution.
|
||||||
throw new InvalidOperationException(
|
logger.Debug("{Type} constructed without speeds - is Data/npc-speeds.json missing? Defaulting to Medium.", GetType());
|
||||||
$"{GetType()} constructed without speeds - is {"Data/npc-speeds.json"} missing?"
|
_activeSpeed = 0.25;
|
||||||
);
|
_passiveSpeed = 0.5;
|
||||||
|
_currentSpeed = _passiveSpeed;
|
||||||
}
|
}
|
||||||
|
|
||||||
_team = 0;
|
_team = 0;
|
||||||
|
|
@ -972,7 +985,7 @@ namespace Server.Mobiles
|
||||||
|
|
||||||
public virtual double WeaponAbilityChance => 0.4;
|
public virtual double WeaponAbilityChance => 0.4;
|
||||||
|
|
||||||
[SerializableProperty(49, useField: nameof(_isParagon))]
|
[SerializableProperty(48, useField: nameof(_isParagon))]
|
||||||
[SaveFlag(nameof(ShouldSerializeIsParagon))]
|
[SaveFlag(nameof(ShouldSerializeIsParagon))]
|
||||||
[CommandProperty(AccessLevel.GameMaster)]
|
[CommandProperty(AccessLevel.GameMaster)]
|
||||||
public bool IsParagon
|
public bool IsParagon
|
||||||
|
|
@ -1183,8 +1196,6 @@ namespace Server.Mobiles
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[SerializableProperty(16, useField: nameof(_controlMaster))]
|
|
||||||
[SaveFlag(nameof(ShouldSerializeControlMaster))]
|
|
||||||
[CommandProperty(AccessLevel.GameMaster)]
|
[CommandProperty(AccessLevel.GameMaster)]
|
||||||
public Mobile ControlMaster
|
public Mobile ControlMaster
|
||||||
{
|
{
|
||||||
|
|
@ -1209,8 +1220,6 @@ namespace Server.Mobiles
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[SerializableProperty(24, useField: nameof(_summonMaster))]
|
|
||||||
[SaveFlag(nameof(ShouldSerializeSummonMaster))]
|
|
||||||
[CommandProperty(AccessLevel.GameMaster)]
|
[CommandProperty(AccessLevel.GameMaster)]
|
||||||
public Mobile SummonMaster
|
public Mobile SummonMaster
|
||||||
{
|
{
|
||||||
|
|
@ -1233,7 +1242,7 @@ namespace Server.Mobiles
|
||||||
|
|
||||||
// Re-issuing the current order must still run the order logic (pet commands), so
|
// Re-issuing the current order must still run the order logic (pet commands), so
|
||||||
// this keeps a hand-written setter with no equality skip.
|
// this keeps a hand-written setter with no equality skip.
|
||||||
[SerializableProperty(19, useField: nameof(_controlOrder))]
|
[SerializableProperty(18, useField: nameof(_controlOrder))]
|
||||||
[SaveFlag(nameof(ShouldSerializeControlOrder))]
|
[SaveFlag(nameof(ShouldSerializeControlOrder))]
|
||||||
[CommandProperty(AccessLevel.GameMaster)]
|
[CommandProperty(AccessLevel.GameMaster)]
|
||||||
public OrderType ControlOrder
|
public OrderType ControlOrder
|
||||||
|
|
@ -1268,7 +1277,7 @@ namespace Server.Mobiles
|
||||||
[CommandProperty(AccessLevel.GameMaster)]
|
[CommandProperty(AccessLevel.GameMaster)]
|
||||||
public DateTime BardEndTime { get; set; }
|
public DateTime BardEndTime { get; set; }
|
||||||
|
|
||||||
[SerializableProperty(21, useField: nameof(_tamable))]
|
[SerializableProperty(20, useField: nameof(_tamable))]
|
||||||
[SaveFlag(nameof(ShouldSerializeTamable))]
|
[SaveFlag(nameof(ShouldSerializeTamable))]
|
||||||
[CommandProperty(AccessLevel.GameMaster)]
|
[CommandProperty(AccessLevel.GameMaster)]
|
||||||
public bool Tamable
|
public bool Tamable
|
||||||
|
|
@ -2368,6 +2377,10 @@ namespace Server.Mobiles
|
||||||
_speedEntry = null;
|
_speedEntry = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Feed the masters through the consolidated reference so the
|
||||||
|
// AfterDeserialization fan-out is uniform across both load paths.
|
||||||
|
_master = _controlMaster ?? _summonMaster;
|
||||||
|
|
||||||
if (version <= 14 && _isParagon && Hue == 0x31)
|
if (version <= 14 && _isParagon && Hue == 0x31)
|
||||||
{
|
{
|
||||||
Hue = Paragon.Hue; // Paragon hue fixed, should now be 0x501.
|
Hue = Paragon.Hue; // Paragon hue fixed, should now be 0x501.
|
||||||
|
|
@ -2377,6 +2390,9 @@ namespace Server.Mobiles
|
||||||
[AfterDeserialization]
|
[AfterDeserialization]
|
||||||
private void AfterDeserialization()
|
private void AfterDeserialization()
|
||||||
{
|
{
|
||||||
|
_controlMaster = _controlled ? _master : null;
|
||||||
|
_summonMaster = _summoned ? _master : null;
|
||||||
|
|
||||||
if (Core.AOS && NameHue == 0x35)
|
if (Core.AOS && NameHue == 0x35)
|
||||||
{
|
{
|
||||||
NameHue = -1;
|
NameHue = -1;
|
||||||
|
|
@ -3622,8 +3638,8 @@ namespace Server.Mobiles
|
||||||
|
|
||||||
var m = _controlMaster;
|
var m = _controlMaster;
|
||||||
SetControlMaster(null);
|
SetControlMaster(null);
|
||||||
|
SummonMaster = null; // uncontrolled summons have no control master to clear through
|
||||||
|
|
||||||
SummonMaster = null;
|
|
||||||
ReceivedHonorContext?.Cancel();
|
ReceivedHonorContext?.Cancel();
|
||||||
|
|
||||||
base.OnDelete();
|
base.OnDelete();
|
||||||
|
|
@ -3669,6 +3685,11 @@ namespace Server.Mobiles
|
||||||
Controlled = false;
|
Controlled = false;
|
||||||
ControlTarget = null;
|
ControlTarget = null;
|
||||||
ControlOrder = OrderType.None;
|
ControlOrder = OrderType.None;
|
||||||
|
|
||||||
|
if (_summoned)
|
||||||
|
{
|
||||||
|
SummonMaster = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -3693,6 +3714,11 @@ namespace Server.Mobiles
|
||||||
ControlTarget = null;
|
ControlTarget = null;
|
||||||
ControlOrder = OrderType.Come;
|
ControlOrder = OrderType.Come;
|
||||||
|
|
||||||
|
if (_summoned)
|
||||||
|
{
|
||||||
|
SummonMaster = m;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (_pendingDeleteTimer != null)
|
if (_pendingDeleteTimer != null)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -3553,7 +3553,6 @@ namespace Server.Mobiles
|
||||||
pet.Internalize();
|
pet.Internalize();
|
||||||
|
|
||||||
pet.SetControlMaster(null);
|
pet.SetControlMaster(null);
|
||||||
pet.SummonMaster = null;
|
|
||||||
|
|
||||||
pet.IsStabled = true;
|
pet.IsStabled = true;
|
||||||
pet.StabledBy = this;
|
pet.StabledBy = this;
|
||||||
|
|
@ -3601,12 +3600,6 @@ namespace Server.Mobiles
|
||||||
if (Followers + pet.ControlSlots <= FollowersMax)
|
if (Followers + pet.ControlSlots <= FollowersMax)
|
||||||
{
|
{
|
||||||
pet.SetControlMaster(this);
|
pet.SetControlMaster(this);
|
||||||
|
|
||||||
if (pet.Summoned)
|
|
||||||
{
|
|
||||||
pet.SummonMaster = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
pet.ControlTarget = this;
|
pet.ControlTarget = this;
|
||||||
pet.ControlOrder = OrderType.Follow;
|
pet.ControlOrder = OrderType.Follow;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -255,7 +255,6 @@ namespace Server.Mobiles
|
||||||
pet.Internalize();
|
pet.Internalize();
|
||||||
|
|
||||||
pet.SetControlMaster(null);
|
pet.SetControlMaster(null);
|
||||||
pet.SummonMaster = null;
|
|
||||||
|
|
||||||
pet.IsStabled = true;
|
pet.IsStabled = true;
|
||||||
pet.StabledBy = from;
|
pet.StabledBy = from;
|
||||||
|
|
@ -356,12 +355,6 @@ namespace Server.Mobiles
|
||||||
private void DoClaim(Mobile from, BaseCreature pet)
|
private void DoClaim(Mobile from, BaseCreature pet)
|
||||||
{
|
{
|
||||||
pet.SetControlMaster(from);
|
pet.SetControlMaster(from);
|
||||||
|
|
||||||
if (pet.Summoned)
|
|
||||||
{
|
|
||||||
pet.SummonMaster = from;
|
|
||||||
}
|
|
||||||
|
|
||||||
pet.ControlTarget = from;
|
pet.ControlTarget = from;
|
||||||
pet.ControlOrder = OrderType.Follow;
|
pet.ControlOrder = OrderType.Follow;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue