diff --git a/Projects/UOContent.Tests/Tests/Mobiles/BaseCreatureSerializationTests.cs b/Projects/UOContent.Tests/Tests/Mobiles/BaseCreatureSerializationTests.cs
index 51a4de573..3b95f452e 100644
--- a/Projects/UOContent.Tests/Tests/Mobiles/BaseCreatureSerializationTests.cs
+++ b/Projects/UOContent.Tests/Tests/Mobiles/BaseCreatureSerializationTests.cs
@@ -182,22 +182,50 @@ public class BaseCreatureSerializationTests : IDisposable
Assert.Equal(master, copy.LastOwner);
}
- [Fact]
- public void UncontrolledSummon_KeepsItsSummonMaster()
+ // One serialized master; the Controlled and Summoned flags decide which views expose it.
+ [Theory]
+ [InlineData(true, false)] // controlled pet
+ [InlineData(true, true)] // controlled summon
+ [InlineData(false, true)] // energy vortex: summoned, never controlled
+ [InlineData(false, false)] // EnragedCreature: a summon master without Summoned
+ public void MasterShapes_RoundTrip(bool controlled, bool summoned)
{
var bc = NewCreature();
var master = NewMaster();
- // Energy vortex-style: summoned with a master, never controlled.
- bc.Summoned = true;
- bc.SummonMaster = master;
+ bc.Summoned = summoned;
+
+ if (controlled)
+ {
+ bc.SetControlMaster(master);
+ }
+ else
+ {
+ 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);
+ Assert.Equal(controlled, copy.Controlled);
+ Assert.Equal(summoned, copy.Summoned);
+ Assert.Equal(controlled ? master : null, copy.ControlMaster);
+ Assert.Equal(controlled && !summoned ? null : master, copy.SummonMaster);
+ }
+
+ [Fact]
+ public void SummonMaster_OnPlainPet_KeepsTheOwner()
+ {
+ var bc = NewCreature();
+ var owner = NewMaster();
+ var other = NewMaster();
+
+ bc.SetControlMaster(owner);
+ bc.SummonMaster = other;
+
+ Assert.Equal(owner, bc.ControlMaster);
+ Assert.Null(bc.SummonMaster);
+ Assert.Equal(bc.ControlSlots, owner.Followers);
+ Assert.Equal(0, other.Followers);
}
[Fact]
@@ -418,8 +446,8 @@ public class BaseCreatureSerializationTests : IDisposable
Assert.Equal(0.6, copy.CurrentMoveSpeed); // passive mode, inheriting
}
- // ControlMaster and SummonMaster are independent references: a summon master can
- // exist without Summoned (EnragedCreature), and a controlled summon carries both.
+ // A summon master can exist without Summoned (EnragedCreature), and a controlled summon
+ // reports the same mobile as both.
[Theory]
[InlineData(true, true, false, false)] // controlled pet
[InlineData(true, true, true, true)] // controlled summon, SummonEnd on the wire
@@ -452,4 +480,79 @@ public class BaseCreatureSerializationTests : IDisposable
Assert.InRange(copy.SummonEndValue, summonEnd - TimeSpan.FromSeconds(1), summonEnd + TimeSpan.FromSeconds(1));
}
}
+
+ // Before summon ownership followed transfers, a traded summon kept its caster as summon master.
+ [Fact]
+ public void LegacyV22Stream_DifferingMasters_OwnerWins()
+ {
+ var owner = NewMaster();
+ var caster = NewMaster();
+
+ var copy = LoadLegacyV22(true, owner, true, caster, Core.Now + TimeSpan.FromMinutes(5));
+
+ Assert.Equal(owner, copy.ControlMaster);
+ Assert.Equal(owner, copy.SummonMaster);
+ }
+
+ // v23 bit positions: every save-flagged field in schema order (DefaultAI is not flagged).
+ private const int V23Controlled = 13;
+ private const int V23ControlMaster = 14;
+ private const int V23Summoned = 20;
+ private const int V23SummonEnd = 21;
+ private const int V23SummonMaster = 22;
+ private const int V23RemoveStep = 50;
+ private const int V23CorpseNameOverride = 52;
+
+ // v23 wrote the owner and the summoner to separate slots; v24 keeps one master.
+ [Fact]
+ public void V23Stream_MigratesToOneMaster_AndDefaultsAbsentFields()
+ {
+ var owner = NewMaster();
+ var caster = NewMaster();
+ var summonEnd = Core.Now + TimeSpan.FromMinutes(5);
+
+ var donor = new MobileStub();
+ donor.DefaultMobileInit();
+ _created.Add(donor);
+
+ var writer = new BufferWriter(true);
+ donor.Serialize(writer);
+
+ writer.Write(23); // version
+ writer.Write(
+ 1UL << V23Controlled | 1UL << V23ControlMaster | 1UL << V23Summoned | 1UL << V23SummonEnd |
+ 1UL << V23SummonMaster | 1UL << V23RemoveStep | 1UL << V23CorpseNameOverride
+ );
+ writer.WriteEncodedInt((int)AIType.AI_Melee); // DefaultAI
+ writer.Write(owner); // ControlMaster
+ writer.WriteAnchoredTime(summonEnd); // SummonEnd
+ writer.Write(caster); // SummonMaster
+ writer.WriteEncodedInt(3); // RemoveStep
+ writer.Write("a migrated corpse"); // CorpseNameOverride
+
+ var buffer = new byte[writer.Position];
+ writer.Buffer.AsSpan(0, (int)writer.Position).CopyTo(buffer);
+
+ var copy = Load(buffer);
+
+ Assert.True(copy.Controlled);
+ Assert.True(copy.Summoned);
+ Assert.Equal(owner, copy.ControlMaster);
+ Assert.Equal(owner, copy.SummonMaster);
+ Assert.InRange(copy.SummonEndValue, summonEnd - TimeSpan.FromSeconds(1), summonEnd + TimeSpan.FromSeconds(1));
+ Assert.Equal(3, copy.RemoveStep);
+ Assert.Equal("a migrated corpse", copy.CorpseNameOverride);
+
+ // Absent fields take the class defaults, not default(T).
+ Assert.Equal(AIType.AI_Melee, copy.AI);
+ Assert.Equal(-1, copy.HitsMaxSeed);
+ Assert.Equal(1, copy.ControlSlots);
+ Assert.Equal(BaseCreature.MaxLoyalty, copy.Loyalty);
+ Assert.Equal(100, copy.PhysicalDamage);
+ Assert.Equal(10, copy.RangeHome);
+ Assert.Equal(FightMode.Closest, copy.FightMode);
+ Assert.Equal(0.3, copy.ActiveSpeed);
+ Assert.Equal(0.6, copy.PassiveSpeed);
+ Assert.NotNull(copy.Owners);
+ }
}
diff --git a/Projects/UOContent.Tests/UOContent.Tests.csproj b/Projects/UOContent.Tests/UOContent.Tests.csproj
index b8c675127..8b83f7992 100644
--- a/Projects/UOContent.Tests/UOContent.Tests.csproj
+++ b/Projects/UOContent.Tests/UOContent.Tests.csproj
@@ -11,7 +11,7 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/Projects/UOContent/Items/Special/Solen Items/BallOfSummoning.cs b/Projects/UOContent/Items/Special/Solen Items/BallOfSummoning.cs
index bf0953041..2a881d97f 100644
--- a/Projects/UOContent/Items/Special/Solen Items/BallOfSummoning.cs
+++ b/Projects/UOContent/Items/Special/Solen Items/BallOfSummoning.cs
@@ -228,11 +228,6 @@ public partial class BallOfSummoning : Item, TranslocationItem
{
pet.SetControlMaster(from);
- if (pet.SummonMaster != null)
- {
- pet.SummonMaster = from;
- }
-
pet.ControlTarget = from;
pet.ControlOrder = OrderType.Follow;
diff --git a/Projects/UOContent/Migrations/Server.Mobiles.BaseCreature.v24.json b/Projects/UOContent/Migrations/Server.Mobiles.BaseCreature.v24.json
new file mode 100644
index 000000000..b1f4fe2fd
--- /dev/null
+++ b/Projects/UOContent/Migrations/Server.Mobiles.BaseCreature.v24.json
@@ -0,0 +1,465 @@
+{
+ "version": 24,
+ "type": "Server.Mobiles.BaseCreature",
+ "properties": [
+ {
+ "name": "DefaultAI",
+ "type": "Server.Mobiles.AIType",
+ "rule": "EnumMigrationRule"
+ },
+ {
+ "name": "CurrentAI",
+ "type": "Server.Mobiles.AIType",
+ "usesSaveFlag": true,
+ "rule": "EnumMigrationRule"
+ },
+ {
+ "name": "RangePerception",
+ "type": "int",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "EncodedInt"
+ ]
+ },
+ {
+ "name": "RangeFight",
+ "type": "int",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "EncodedInt"
+ ]
+ },
+ {
+ "name": "RangeHome",
+ "type": "int",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "EncodedInt"
+ ]
+ },
+ {
+ "name": "Team",
+ "type": "int",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "EncodedInt"
+ ]
+ },
+ {
+ "name": "FightMode",
+ "type": "Server.Mobiles.FightMode",
+ "usesSaveFlag": true,
+ "rule": "EnumMigrationRule"
+ },
+ {
+ "name": "ActiveSpeed",
+ "type": "double",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "PassiveSpeed",
+ "type": "double",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "CurrentSpeed",
+ "type": "double",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "ActiveMoveSpeed",
+ "type": "double",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "PassiveMoveSpeed",
+ "type": "double",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "Home",
+ "type": "Server.Point3D",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveUOTypeMigrationRule",
+ "ruleArguments": [
+ "Point3D"
+ ]
+ },
+ {
+ "name": "HomeMap",
+ "type": "Server.Map",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveUOTypeMigrationRule",
+ "ruleArguments": [
+ "Map"
+ ]
+ },
+ {
+ "name": "Controlled",
+ "type": "bool",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "Master",
+ "type": "Server.Mobile",
+ "usesSaveFlag": true,
+ "rule": "SerializableInterfaceMigrationRule"
+ },
+ {
+ "name": "ControlTarget",
+ "type": "Server.Mobile",
+ "usesSaveFlag": true,
+ "rule": "SerializableInterfaceMigrationRule"
+ },
+ {
+ "name": "ControlDest",
+ "type": "Server.Point3D",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveUOTypeMigrationRule",
+ "ruleArguments": [
+ "Point3D"
+ ]
+ },
+ {
+ "name": "ControlOrder",
+ "type": "Server.Mobiles.OrderType",
+ "usesSaveFlag": true,
+ "rule": "EnumMigrationRule"
+ },
+ {
+ "name": "MinTameSkill",
+ "type": "double",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "Tamable",
+ "type": "bool",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "Summoned",
+ "type": "bool",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "SummonEnd",
+ "type": "System.DateTime",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "AnchoredTime"
+ ]
+ },
+ {
+ "name": "ControlSlots",
+ "type": "int",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "EncodedInt"
+ ]
+ },
+ {
+ "name": "Loyalty",
+ "type": "int",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "EncodedInt"
+ ]
+ },
+ {
+ "name": "CurrentWayPoint",
+ "type": "Server.Items.WayPoint",
+ "usesSaveFlag": true,
+ "rule": "SerializableInterfaceMigrationRule"
+ },
+ {
+ "name": "HitsMaxSeed",
+ "type": "int",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "EncodedInt"
+ ]
+ },
+ {
+ "name": "StamMaxSeed",
+ "type": "int",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "EncodedInt"
+ ]
+ },
+ {
+ "name": "ManaMaxSeed",
+ "type": "int",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "EncodedInt"
+ ]
+ },
+ {
+ "name": "DamageMin",
+ "type": "int",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "EncodedInt"
+ ]
+ },
+ {
+ "name": "DamageMax",
+ "type": "int",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "EncodedInt"
+ ]
+ },
+ {
+ "name": "PhysicalResistanceSeed",
+ "type": "int",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "EncodedInt"
+ ]
+ },
+ {
+ "name": "FireResistSeed",
+ "type": "int",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "EncodedInt"
+ ]
+ },
+ {
+ "name": "ColdResistSeed",
+ "type": "int",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "EncodedInt"
+ ]
+ },
+ {
+ "name": "PoisonResistSeed",
+ "type": "int",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "EncodedInt"
+ ]
+ },
+ {
+ "name": "EnergyResistSeed",
+ "type": "int",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "EncodedInt"
+ ]
+ },
+ {
+ "name": "PhysicalDamage",
+ "type": "int",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "EncodedInt"
+ ]
+ },
+ {
+ "name": "FireDamage",
+ "type": "int",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "EncodedInt"
+ ]
+ },
+ {
+ "name": "ColdDamage",
+ "type": "int",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "EncodedInt"
+ ]
+ },
+ {
+ "name": "PoisonDamage",
+ "type": "int",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "EncodedInt"
+ ]
+ },
+ {
+ "name": "EnergyDamage",
+ "type": "int",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "EncodedInt"
+ ]
+ },
+ {
+ "name": "Owners",
+ "type": "System.Collections.Generic.List\u003CServer.Mobile\u003E",
+ "usesSaveFlag": true,
+ "rule": "ListMigrationRule",
+ "ruleArguments": [
+ "@Tidy",
+ "Server.Mobile",
+ "SerializableInterfaceMigrationRule"
+ ]
+ },
+ {
+ "name": "IsDeadPet",
+ "type": "bool",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "IsBonded",
+ "type": "bool",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "BondingBegin",
+ "type": "System.DateTime",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "OwnerAbandonTime",
+ "type": "System.DateTime",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "HasGeneratedLoot",
+ "type": "bool",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "IsParagon",
+ "type": "bool",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "Friends",
+ "type": "System.Collections.Generic.List\u003CServer.Mobile\u003E",
+ "usesSaveFlag": true,
+ "rule": "ListMigrationRule",
+ "ruleArguments": [
+ "@Tidy",
+ "Server.Mobile",
+ "SerializableInterfaceMigrationRule"
+ ]
+ },
+ {
+ "name": "RemoveIfUntamed",
+ "type": "bool",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "RemoveStep",
+ "type": "int",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "EncodedInt"
+ ]
+ },
+ {
+ "name": "PendingDeleteTimer",
+ "type": "Server.Timer",
+ "usesSaveFlag": true,
+ "rule": "TimerMigrationRule",
+ "ruleArguments": [
+ "@AnchoredTimer"
+ ]
+ },
+ {
+ "name": "CorpseNameOverride",
+ "type": "string",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/OnSpeech.cs b/Projects/UOContent/Mobiles/AI/BaseAI/OnSpeech.cs
index 7ce0994f6..deda86702 100644
--- a/Projects/UOContent/Mobiles/AI/BaseAI/OnSpeech.cs
+++ b/Projects/UOContent/Mobiles/AI/BaseAI/OnSpeech.cs
@@ -451,11 +451,6 @@ public abstract partial class BaseAI
Mobile.SetControlMaster(e.Mobile);
- if (Mobile.SummonMaster != null)
- {
- Mobile.SummonMaster = e.Mobile;
- }
-
return true;
}
}
diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/TransferItem.cs b/Projects/UOContent/Mobiles/AI/BaseAI/TransferItem.cs
index 7eac17ae5..08dcda911 100644
--- a/Projects/UOContent/Mobiles/AI/BaseAI/TransferItem.cs
+++ b/Projects/UOContent/Mobiles/AI/BaseAI/TransferItem.cs
@@ -156,11 +156,6 @@ internal sealed partial class TransferItem : Item
private void TransferPetOwnership(Mobile from, Mobile to)
{
- if (_creature.SummonMaster != null)
- {
- _creature.SummonMaster = to;
- }
-
_creature.ClearPetFriends();
_creature.IssueOrder(OrderType.Follow, null, to);
_creature.BondingBegin = DateTime.MinValue;
diff --git a/Projects/UOContent/Mobiles/BaseCreature.cs b/Projects/UOContent/Mobiles/BaseCreature.cs
index 18743579d..d37465796 100644
--- a/Projects/UOContent/Mobiles/BaseCreature.cs
+++ b/Projects/UOContent/Mobiles/BaseCreature.cs
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
-using ModernUO.CodeGeneratedEvents;
using ModernUO.Serialization;
using Server.Collections;
using Server.ContextMenus;
@@ -135,7 +134,7 @@ namespace Server.Mobiles
public int CompareTo(DamageStore ds) => (ds?.m_Damage ?? 0).CompareTo(m_Damage);
}
- [SerializationGenerator(23, false)]
+ [SerializationGenerator(24, false)]
public abstract partial class BaseCreature : Mobile, IHonorTarget, IQuestGiver
{
private static readonly ILogger logger = LogFactory.GetLogger(typeof(BaseCreature));
@@ -446,10 +445,12 @@ namespace Server.Mobiles
InvalidateProperties();
}
- // Follower bookkeeping brackets the assignment, so the property is hand-written.
- private Mobile _controlMaster;
+ // ControlMaster and SummonMaster are views of this one reference, gated by Controlled and Summoned.
+ [SerializableField(15, getter: "private", setter: "private")]
+ [SaveFlag(nameof(ShouldSerializeMaster))]
+ private Mobile _master;
- private bool ShouldSerializeControlMaster() => _controlMaster != null;
+ private bool ShouldSerializeMaster() => _master != null;
[SerializableField(16)]
[SaveFlag(nameof(ShouldSerializeControlTarget))]
@@ -503,13 +504,8 @@ namespace Server.Mobiles
private bool ShouldSerializeSummonEnd() => _summoned;
- // Follower bookkeeping brackets the assignment, so the property is hand-written.
- private Mobile _summonMaster;
-
- private bool ShouldSerializeSummonMaster() => _summonMaster != null;
-
[EncodedInt]
- [SerializableField(24)]
+ [SerializableField(23)]
[SaveFlag(nameof(ShouldSerializeControlSlots), nameof(ControlSlotsDefaultValue))]
[SerializedCommandProperty(AccessLevel.Administrator)]
private int _controlSlots = 1;
@@ -519,7 +515,7 @@ namespace Server.Mobiles
private int ControlSlotsDefaultValue() => 1;
[EncodedInt]
- [SerializableField(25, allowFieldChange: nameof(ClampLoyalty))]
+ [SerializableField(24, allowFieldChange: nameof(ClampLoyalty))]
[SaveFlag(nameof(ShouldSerializeLoyalty), nameof(LoyaltyDefaultValue))]
[SerializedCommandProperty(AccessLevel.GameMaster)]
private int _loyalty;
@@ -534,7 +530,7 @@ namespace Server.Mobiles
return true;
}
- [SerializableField(26)]
+ [SerializableField(25)]
[SaveFlag(nameof(ShouldSerializeCurrentWayPoint))]
[SerializedCommandProperty(AccessLevel.GameMaster)]
private WayPoint _currentWayPoint;
@@ -542,7 +538,7 @@ namespace Server.Mobiles
private bool ShouldSerializeCurrentWayPoint() => _currentWayPoint != null;
[EncodedInt]
- [SerializableField(27)]
+ [SerializableField(26)]
[SaveFlag(nameof(ShouldSerializeHitsMaxSeed), nameof(HitsMaxSeedDefaultValue))]
[SerializedCommandProperty(AccessLevel.GameMaster)]
private int _hitsMaxSeed = -1;
@@ -552,7 +548,7 @@ namespace Server.Mobiles
private int HitsMaxSeedDefaultValue() => -1;
[EncodedInt]
- [SerializableField(28)]
+ [SerializableField(27)]
[SaveFlag(nameof(ShouldSerializeStamMaxSeed), nameof(StamMaxSeedDefaultValue))]
[SerializedCommandProperty(AccessLevel.GameMaster)]
private int _stamMaxSeed = -1;
@@ -562,7 +558,7 @@ namespace Server.Mobiles
private int StamMaxSeedDefaultValue() => -1;
[EncodedInt]
- [SerializableField(29)]
+ [SerializableField(28)]
[SaveFlag(nameof(ShouldSerializeManaMaxSeed), nameof(ManaMaxSeedDefaultValue))]
[SerializedCommandProperty(AccessLevel.GameMaster)]
private int _manaMaxSeed = -1;
@@ -572,7 +568,7 @@ namespace Server.Mobiles
private int ManaMaxSeedDefaultValue() => -1;
[EncodedInt]
- [SerializableField(30)]
+ [SerializableField(29)]
[SaveFlag(nameof(ShouldSerializeDamageMin), nameof(DamageMinDefaultValue))]
[SerializedCommandProperty(AccessLevel.GameMaster)]
private int _damageMin = -1;
@@ -582,7 +578,7 @@ namespace Server.Mobiles
private int DamageMinDefaultValue() => -1;
[EncodedInt]
- [SerializableField(31)]
+ [SerializableField(30)]
[SaveFlag(nameof(ShouldSerializeDamageMax), nameof(DamageMaxDefaultValue))]
[SerializedCommandProperty(AccessLevel.GameMaster)]
private int _damageMax = -1;
@@ -592,7 +588,7 @@ namespace Server.Mobiles
private int DamageMaxDefaultValue() => -1;
[EncodedInt]
- [SerializableField(32, fieldChanged: nameof(OnResistanceSeedChange))]
+ [SerializableField(31, fieldChanged: nameof(OnResistanceSeedChange))]
[SaveFlag(nameof(ShouldSerializePhysicalResistanceSeed))]
[SerializedCommandProperty(AccessLevel.GameMaster)]
private int _physicalResistanceSeed;
@@ -602,7 +598,7 @@ namespace Server.Mobiles
private void OnResistanceSeedChange(int oldValue, int newValue) => UpdateResistances();
[EncodedInt]
- [SerializableField(33, fieldChanged: nameof(OnResistanceSeedChange))]
+ [SerializableField(32, fieldChanged: nameof(OnResistanceSeedChange))]
[SaveFlag(nameof(ShouldSerializeFireResistSeed))]
[SerializedCommandProperty(AccessLevel.GameMaster)]
private int _fireResistSeed;
@@ -610,7 +606,7 @@ namespace Server.Mobiles
private bool ShouldSerializeFireResistSeed() => _fireResistSeed != 0;
[EncodedInt]
- [SerializableField(34, fieldChanged: nameof(OnResistanceSeedChange))]
+ [SerializableField(33, fieldChanged: nameof(OnResistanceSeedChange))]
[SaveFlag(nameof(ShouldSerializeColdResistSeed))]
[SerializedCommandProperty(AccessLevel.GameMaster)]
private int _coldResistSeed;
@@ -618,7 +614,7 @@ namespace Server.Mobiles
private bool ShouldSerializeColdResistSeed() => _coldResistSeed != 0;
[EncodedInt]
- [SerializableField(35, fieldChanged: nameof(OnResistanceSeedChange))]
+ [SerializableField(34, fieldChanged: nameof(OnResistanceSeedChange))]
[SaveFlag(nameof(ShouldSerializePoisonResistSeed))]
[SerializedCommandProperty(AccessLevel.GameMaster)]
private int _poisonResistSeed;
@@ -626,7 +622,7 @@ namespace Server.Mobiles
private bool ShouldSerializePoisonResistSeed() => _poisonResistSeed != 0;
[EncodedInt]
- [SerializableField(36, fieldChanged: nameof(OnResistanceSeedChange))]
+ [SerializableField(35, fieldChanged: nameof(OnResistanceSeedChange))]
[SaveFlag(nameof(ShouldSerializeEnergyResistSeed))]
[SerializedCommandProperty(AccessLevel.GameMaster)]
private int _energyResistSeed;
@@ -634,7 +630,7 @@ namespace Server.Mobiles
private bool ShouldSerializeEnergyResistSeed() => _energyResistSeed != 0;
[EncodedInt]
- [SerializableField(37)]
+ [SerializableField(36)]
[SaveFlag(nameof(ShouldSerializePhysicalDamage), nameof(PhysicalDamageDefaultValue))]
[SerializedCommandProperty(AccessLevel.GameMaster)]
private int _physicalDamage = 100;
@@ -644,7 +640,7 @@ namespace Server.Mobiles
private int PhysicalDamageDefaultValue() => 100;
[EncodedInt]
- [SerializableField(38)]
+ [SerializableField(37)]
[SaveFlag(nameof(ShouldSerializeFireDamage))]
[SerializedCommandProperty(AccessLevel.GameMaster)]
private int _fireDamage;
@@ -652,7 +648,7 @@ namespace Server.Mobiles
private bool ShouldSerializeFireDamage() => _fireDamage != 0;
[EncodedInt]
- [SerializableField(39)]
+ [SerializableField(38)]
[SaveFlag(nameof(ShouldSerializeColdDamage))]
[SerializedCommandProperty(AccessLevel.GameMaster)]
private int _coldDamage;
@@ -660,7 +656,7 @@ namespace Server.Mobiles
private bool ShouldSerializeColdDamage() => _coldDamage != 0;
[EncodedInt]
- [SerializableField(40)]
+ [SerializableField(39)]
[SaveFlag(nameof(ShouldSerializePoisonDamage))]
[SerializedCommandProperty(AccessLevel.GameMaster)]
private int _poisonDamage;
@@ -668,7 +664,7 @@ namespace Server.Mobiles
private bool ShouldSerializePoisonDamage() => _poisonDamage != 0;
[EncodedInt]
- [SerializableField(41)]
+ [SerializableField(40)]
[SaveFlag(nameof(ShouldSerializeEnergyDamage))]
[SerializedCommandProperty(AccessLevel.GameMaster)]
private int _energyDamage;
@@ -676,7 +672,7 @@ namespace Server.Mobiles
private bool ShouldSerializeEnergyDamage() => _energyDamage != 0;
[Tidy]
- [SerializableField(42, setter: "private")]
+ [SerializableField(41, setter: "private")]
[SaveFlag(nameof(ShouldSerializeOwners), nameof(OwnersDefaultValue))]
private List _owners;
@@ -688,13 +684,13 @@ namespace Server.Mobiles
private List OwnersDefaultValue() => new();
- [SerializableField(43)]
+ [SerializableField(42)]
[SaveFlag(nameof(ShouldSerializeIsDeadPet))]
private bool _isDeadPet;
private bool ShouldSerializeIsDeadPet() => _isDeadPet;
- [SerializableField(44, fieldChanged: nameof(OnBondedChange))]
+ [SerializableField(43, fieldChanged: nameof(OnBondedChange))]
[SaveFlag(nameof(ShouldSerializeIsBonded))]
[SerializedCommandProperty(AccessLevel.GameMaster)]
private bool _isBonded;
@@ -703,21 +699,21 @@ namespace Server.Mobiles
private void OnBondedChange(bool oldValue, bool newValue) => InvalidateProperties();
- [SerializableField(45)]
+ [SerializableField(44)]
[SaveFlag(nameof(ShouldSerializeBondingBegin))]
[SerializedCommandProperty(AccessLevel.GameMaster)]
private DateTime _bondingBegin;
private bool ShouldSerializeBondingBegin() => _bondingBegin != DateTime.MinValue;
- [SerializableField(46)]
+ [SerializableField(45)]
[SaveFlag(nameof(ShouldSerializeOwnerAbandonTime))]
[SerializedCommandProperty(AccessLevel.GameMaster)]
private DateTime _ownerAbandonTime;
private bool ShouldSerializeOwnerAbandonTime() => _ownerAbandonTime != DateTime.MinValue;
- [SerializableField(47)]
+ [SerializableField(46)]
[SaveFlag(nameof(ShouldSerializeHasGeneratedLoot))]
private bool _hasGeneratedLoot;
@@ -729,7 +725,7 @@ namespace Server.Mobiles
private bool ShouldSerializeIsParagon() => _isParagon;
[Tidy]
- [SerializableField(49, setter: "private")]
+ [SerializableField(48, setter: "private")]
[SaveFlag(nameof(ShouldSerializeFriends))]
private List _friends;
@@ -739,7 +735,7 @@ namespace Server.Mobiles
return _friends?.Count > 0;
}
- [SerializableField(50)]
+ [SerializableField(49)]
[SaveFlag(nameof(ShouldSerializeRemoveIfUntamed))]
[SerializedCommandProperty(AccessLevel.GameMaster)]
private bool _removeIfUntamed;
@@ -747,21 +743,21 @@ namespace Server.Mobiles
private bool ShouldSerializeRemoveIfUntamed() => _removeIfUntamed;
[EncodedInt]
- [SerializableField(51)]
+ [SerializableField(50)]
[SaveFlag(nameof(ShouldSerializeRemoveStep))]
[SerializedCommandProperty(AccessLevel.GameMaster)]
private int _removeStep;
private bool ShouldSerializeRemoveStep() => _removeStep != 0;
- [SerializableField(52, setter: "private")]
+ [SerializableField(51, setter: "private")]
[SaveFlag(nameof(ShouldSerializePendingDeleteTimer))]
[DeserializeTimer(nameof(DeserializePendingDeleteTimer))]
private Timer _pendingDeleteTimer;
// Stabled and controlled pets never resume a delete countdown.
private bool ShouldSerializePendingDeleteTimer() =>
- _pendingDeleteTimer?.Running == true && !IsStabled && !(_controlled && _controlMaster != null);
+ _pendingDeleteTimer?.Running == true && !IsStabled && ControlMaster == null;
private void DeserializePendingDeleteTimer(TimeSpan delay)
{
@@ -769,7 +765,7 @@ namespace Server.Mobiles
_pendingDeleteTimer.Start();
}
- [SerializableField(53)]
+ [SerializableField(52)]
[SaveFlag(nameof(ShouldSerializeCorpseNameOverride))]
[SerializedCommandProperty(AccessLevel.GameMaster)]
private string _corpseNameOverride;
@@ -847,7 +843,6 @@ namespace Server.Mobiles
Debug = false;
_controlled = false;
- _controlMaster = null;
ControlTarget = null;
_controlOrder = OrderType.None;
@@ -931,7 +926,7 @@ namespace Server.Mobiles
public virtual double WeaponAbilityChance => 0.4;
- [SerializableProperty(48, useField: nameof(_isParagon))]
+ [SerializableProperty(47, useField: nameof(_isParagon))]
[SaveFlag(nameof(ShouldSerializeIsParagon))]
[CommandProperty(AccessLevel.GameMaster)]
public bool IsParagon
@@ -1028,8 +1023,8 @@ namespace Server.Mobiles
}
public virtual bool IsNecroFamiliar =>
- Summoned && _controlMaster != null &&
- SummonFamiliarSpell.Table.TryGetValue(_controlMaster, out var bc) && bc == this;
+ Summoned && ControlMaster != null &&
+ SummonFamiliarSpell.Table.TryGetValue(ControlMaster, out var bc) && bc == this;
public virtual bool DeleteCorpseOnDeath => !Core.AOS && _summoned;
@@ -1157,54 +1152,53 @@ namespace Server.Mobiles
}
}
- [SerializableProperty(15, useField: nameof(_controlMaster))]
- [SaveFlag(nameof(ShouldSerializeControlMaster))]
+ // Assigned before Controlled is raised (SetControlMaster), so the setter cannot be gated.
[CommandProperty(AccessLevel.GameMaster)]
public Mobile ControlMaster
{
- get => _controlMaster;
+ get => _controlled ? _master : null;
set
{
- if (_controlMaster == value || this == value)
+ if (ControlMaster == value || this == value)
{
return;
}
- RemoveFollowers();
- _controlMaster = value;
- AddFollowers();
- if (_controlMaster != null)
+ SetMaster(value);
+
+ if (value != null)
{
StopDeleteTimer();
}
-
- Delta(MobileDelta.Noto);
- this.MarkDirty();
}
}
- [SerializableProperty(23, useField: nameof(_summonMaster))]
- [SaveFlag(nameof(ShouldSerializeSummonMaster))]
+ // The summoner, controlled or not. Enraged creatures have one without being Summoned.
+ // A plain pet has no summoner, so assigning one would overwrite its owner.
[CommandProperty(AccessLevel.GameMaster)]
public Mobile SummonMaster
{
- get => _summonMaster;
+ get => _controlled && !_summoned ? null : _master;
set
{
- if (_summonMaster == value || this == value)
+ if (SummonMaster == value || this == value || _controlled && !_summoned)
{
return;
}
- RemoveFollowers();
- _summonMaster = value;
- AddFollowers();
-
- Delta(MobileDelta.Noto);
- this.MarkDirty();
+ SetMaster(value);
}
}
+ private void SetMaster(Mobile value)
+ {
+ RemoveFollowers();
+ Master = value;
+ AddFollowers();
+
+ Delta(MobileDelta.Noto);
+ }
+
// Fires on every assignment, not only changes: a reissued order is a command (retarget, re-anchor).
// A raw assignment is system-issued; player commands go through IssueOrder.
[SerializableProperty(18, useField: nameof(_controlOrder))]
@@ -1271,7 +1265,7 @@ namespace Server.Mobiles
}
InvalidateProperties();
- _controlMaster?.InvalidateProperties();
+ ControlMaster?.InvalidateProperties();
this.MarkDirty();
}
@@ -2195,9 +2189,75 @@ namespace Server.Mobiles
}
}
+ // Absent fields resolve through the same default-value methods the live reader uses.
+ private void MigrateFrom(V23Content content)
+ {
+ _defaultAI = content.DefaultAI;
+ _currentAI = content.CurrentAI ?? CurrentAIDefaultValue();
+ _rangePerception = content.RangePerception ?? RangePerceptionDefaultValue();
+ _rangeFight = content.RangeFight ?? RangeFightDefaultValue();
+ _rangeHome = content.RangeHome ?? RangeHomeDefaultValue();
+ _team = content.Team ?? 0;
+ _fightMode = content.FightMode ?? FightModeDefaultValue();
+ _activeSpeed = content.ActiveSpeed ?? ActiveSpeedDefaultValue();
+ _passiveSpeed = content.PassiveSpeed ?? PassiveSpeedDefaultValue();
+ _currentSpeed = content.CurrentSpeed ?? CurrentSpeedDefaultValue();
+ _activeMoveSpeed = content.ActiveMoveSpeed ?? ActiveMoveSpeedDefaultValue();
+ _passiveMoveSpeed = content.PassiveMoveSpeed ?? PassiveMoveSpeedDefaultValue();
+ _home = content.Home ?? Point3D.Zero;
+ _homeMap = content.HomeMap;
+ _controlled = content.Controlled;
+ // A traded summon could still carry its caster as summon master; the owner wins.
+ _master = content.ControlMaster ?? content.SummonMaster;
+ _controlTarget = content.ControlTarget;
+ _controlDest = content.ControlDest ?? Point3D.Zero;
+ _controlOrder = content.ControlOrder ?? OrderType.None;
+ _minTameSkill = content.MinTameSkill ?? 0;
+ _tamable = content.Tamable;
+ _summoned = content.Summoned;
+ _summonEnd = content.SummonEnd ?? DateTime.MinValue;
+ _controlSlots = content.ControlSlots ?? ControlSlotsDefaultValue();
+ _loyalty = content.Loyalty ?? LoyaltyDefaultValue();
+ _currentWayPoint = content.CurrentWayPoint;
+ _hitsMaxSeed = content.HitsMaxSeed ?? HitsMaxSeedDefaultValue();
+ _stamMaxSeed = content.StamMaxSeed ?? StamMaxSeedDefaultValue();
+ _manaMaxSeed = content.ManaMaxSeed ?? ManaMaxSeedDefaultValue();
+ _damageMin = content.DamageMin ?? DamageMinDefaultValue();
+ _damageMax = content.DamageMax ?? DamageMaxDefaultValue();
+ _physicalResistanceSeed = content.PhysicalResistanceSeed ?? 0;
+ _fireResistSeed = content.FireResistSeed ?? 0;
+ _coldResistSeed = content.ColdResistSeed ?? 0;
+ _poisonResistSeed = content.PoisonResistSeed ?? 0;
+ _energyResistSeed = content.EnergyResistSeed ?? 0;
+ _physicalDamage = content.PhysicalDamage ?? PhysicalDamageDefaultValue();
+ _fireDamage = content.FireDamage ?? 0;
+ _coldDamage = content.ColdDamage ?? 0;
+ _poisonDamage = content.PoisonDamage ?? 0;
+ _energyDamage = content.EnergyDamage ?? 0;
+ _owners = content.Owners ?? OwnersDefaultValue();
+ _isDeadPet = content.IsDeadPet;
+ _isBonded = content.IsBonded;
+ _bondingBegin = content.BondingBegin ?? DateTime.MinValue;
+ _ownerAbandonTime = content.OwnerAbandonTime ?? DateTime.MinValue;
+ _hasGeneratedLoot = content.HasGeneratedLoot;
+ _isParagon = content.IsParagon;
+ _friends = content.Friends;
+ _removeIfUntamed = content.RemoveIfUntamed;
+ _removeStep = content.RemoveStep ?? 0;
+
+ if (content.PendingDeleteTimerDelay != TimeSpan.MinValue)
+ {
+ DeserializePendingDeleteTimer(content.PendingDeleteTimerDelay);
+ }
+
+ _corpseNameOverride = content.CorpseNameOverride;
+ }
+
// Pre-codegen loads only (versions 0-22); post-codegen bumps use MigrateFrom.
private void Deserialize(IGenericReader reader, int version)
{
+ Mobile controlMaster = null;
+
_currentAI = (AIType)reader.ReadInt();
_defaultAI = (AIType)reader.ReadInt();
@@ -2245,7 +2305,7 @@ namespace Server.Mobiles
_fightMode = (FightMode)reader.ReadInt();
_controlled = reader.ReadBool();
- _controlMaster = reader.ReadEntity();
+ controlMaster = reader.ReadEntity();
_controlTarget = reader.ReadEntity();
_controlDest = reader.ReadPoint3D();
_controlOrder = (OrderType)reader.ReadInt();
@@ -2273,7 +2333,6 @@ namespace Server.Mobiles
_fightMode = FightMode.Closest;
_controlled = false;
- _controlMaster = null;
_controlTarget = null;
_controlOrder = OrderType.None;
}
@@ -2294,9 +2353,12 @@ namespace Server.Mobiles
if (version >= 5)
{
- _summonMaster = reader.ReadEntity();
+ _master = reader.ReadEntity();
}
+ // A transferred summon kept its caster as summon master; the owner wins.
+ _master = controlMaster ?? _master;
+
if (version >= 6)
{
_hitsMaxSeed = reader.ReadInt();
@@ -2457,7 +2519,7 @@ namespace Server.Mobiles
if (IsAnimatedDead)
{
- AnimateDeadSpell.Register(_summonMaster, this);
+ AnimateDeadSpell.Register(SummonMaster, this);
}
}
@@ -2566,7 +2628,7 @@ namespace Server.Mobiles
public void RemoveFollowers()
{
- var master = _controlMaster ?? _summonMaster;
+ var master = _master;
if (master != null)
{
master.Followers -= Math.Min(ControlSlots, master.Followers);
@@ -2580,7 +2642,7 @@ namespace Server.Mobiles
public void AddFollowers()
{
- var master = _controlMaster ?? _summonMaster;
+ var master = _master;
if (master != null)
{
master.Followers += ControlSlots;
@@ -2661,7 +2723,7 @@ namespace Server.Mobiles
if (IsAnimatedDead)
{
- AnimateDeadSpell.Unregister(_summonMaster, this);
+ AnimateDeadSpell.Unregister(SummonMaster, this);
}
if (Summoned && SummonMaster != null)
@@ -2842,7 +2904,7 @@ namespace Server.Mobiles
}
public override bool IsHarmfulCriminal(Mobile target) =>
- (!Controlled || target != _controlMaster) && (!Summoned || target != _summonMaster) &&
+ target != ControlMaster && (!Summoned || target != SummonMaster) &&
(target is not BaseCreature { InitialInnocent: true } creature || creature.Controlled) &&
(target is not PlayerMobile mobile || mobile.PermaFlags.Count <= 0) && base.IsHarmfulCriminal(target);
@@ -2850,16 +2912,9 @@ namespace Server.Mobiles
{
base.CriminalAction(message);
- if (Controlled || Summoned)
+ if ((Controlled || Summoned) && _master?.Player == true)
{
- if (_controlMaster?.Player == true)
- {
- _controlMaster.CriminalAction(false);
- }
- else if (_summonMaster?.Player == true)
- {
- _summonMaster.CriminalAction(false);
- }
+ _master.CriminalAction(false);
}
}
@@ -2867,7 +2922,7 @@ namespace Server.Mobiles
{
base.DoHarmful(target, indirect);
- if (target == this || target == _controlMaster || target == _summonMaster || !Controlled && !Summoned)
+ if (target == this || target == _master || !Controlled && !Summoned)
{
return;
}
@@ -3644,7 +3699,7 @@ namespace Server.Mobiles
{
CreatureEvents.CreatureDeletedEvent(this);
- var m = _controlMaster;
+ var m = ControlMaster;
SetControlMaster(null);
SummonMaster = null;
@@ -3951,14 +4006,9 @@ namespace Server.Mobiles
return BardMaster;
}
- if (_controlled && _controlMaster != null)
+ if ((_controlled || _summoned) && _master != null)
{
- return _controlMaster;
- }
-
- if (_summoned && _summonMaster != null)
- {
- return _summonMaster;
+ return _master;
}
return base.GetDamageMaster(damagee);
@@ -4549,7 +4599,7 @@ namespace Server.Mobiles
if (IsBondable && !IsBonded)
{
- var master = _controlMaster;
+ var master = ControlMaster;
if (master != null && master == from) // So friends can't start the bonding process
{
diff --git a/Projects/UOContent/Mobiles/PlayerMobile.cs b/Projects/UOContent/Mobiles/PlayerMobile.cs
index cf7fc000b..f1a81a53a 100644
--- a/Projects/UOContent/Mobiles/PlayerMobile.cs
+++ b/Projects/UOContent/Mobiles/PlayerMobile.cs
@@ -3553,7 +3553,6 @@ namespace Server.Mobiles
pet.Internalize();
pet.SetControlMaster(null);
- pet.SummonMaster = null;
pet.IsStabled = true;
pet.StabledBy = this;
@@ -3602,11 +3601,6 @@ namespace Server.Mobiles
{
pet.SetControlMaster(this);
- if (pet.SummonMaster != null)
- {
- pet.SummonMaster = this;
- }
-
pet.ControlTarget = this;
pet.ControlOrder = OrderType.Follow;
diff --git a/Projects/UOContent/Mobiles/Vendors/NPC/AnimalTrainer.cs b/Projects/UOContent/Mobiles/Vendors/NPC/AnimalTrainer.cs
index b3fb4d721..81a000710 100644
--- a/Projects/UOContent/Mobiles/Vendors/NPC/AnimalTrainer.cs
+++ b/Projects/UOContent/Mobiles/Vendors/NPC/AnimalTrainer.cs
@@ -255,7 +255,6 @@ namespace Server.Mobiles
pet.Internalize();
pet.SetControlMaster(null);
- pet.SummonMaster = null;
pet.IsStabled = true;
pet.StabledBy = from;
@@ -357,11 +356,6 @@ namespace Server.Mobiles
{
pet.SetControlMaster(from);
- if (pet.SummonMaster != null)
- {
- pet.SummonMaster = from;
- }
-
pet.ControlTarget = from;
pet.ControlOrder = OrderType.Follow;
diff --git a/Projects/UOContent/Systems/JailSystem/JailSystem.cs b/Projects/UOContent/Systems/JailSystem/JailSystem.cs
index 8da7319c9..8802d0487 100644
--- a/Projects/UOContent/Systems/JailSystem/JailSystem.cs
+++ b/Projects/UOContent/Systems/JailSystem/JailSystem.cs
@@ -144,7 +144,6 @@ public class JailSystem : GenericPersistence
bc.Internalize();
bc.SetControlMaster(null);
- bc.SummonMaster = null;
bc.IsStabled = true;
bc.StabledBy = from;