diff --git a/Projects/UOContent.Tests/Tests/Misc/CorpseNotorietyTests.cs b/Projects/UOContent.Tests/Tests/Misc/CorpseNotorietyTests.cs new file mode 100644 index 000000000..a46c56003 --- /dev/null +++ b/Projects/UOContent.Tests/Tests/Misc/CorpseNotorietyTests.cs @@ -0,0 +1,40 @@ +using Server; +using Server.Items; +using Server.Misc; +using Server.Mobiles; +using Xunit; + +namespace UOContent.Tests; + +[Collection("Sequential UOContent Tests")] +public class CorpseNotorietyTests +{ + [Theory] + [InlineData(0, Notoriety.Innocent)] // Human body + [InlineData(746, Notoriety.Innocent)] // Horrific Beast, a monster body + [InlineData(0, Notoriety.Murderer, 5)] + [InlineData(746, Notoriety.Murderer, 5)] + public void PlayerCorpseNotorietyIgnoresTransformedBody(int bodyMod, int expected, int kills = 0) + { + var victim = new PlayerMobile(World.NewMobile); + victim.DefaultMobileInit(); + victim.Kills = kills; + victim.BodyMod = bodyMod; + + var looter = new PlayerMobile(World.NewMobile); + looter.DefaultMobileInit(); + + var corpse = new Corpse(victim, []); + + try + { + Assert.Equal(expected, NotorietyHandlers.CorpseNotoriety(looter, corpse)); + } + finally + { + corpse.Delete(); + victim.Delete(); + looter.Delete(); + } + } +} diff --git a/Projects/UOContent/Misc/Notoriety.cs b/Projects/UOContent/Misc/Notoriety.cs index 27f54c18f..fc57c3462 100644 --- a/Projects/UOContent/Misc/Notoriety.cs +++ b/Projects/UOContent/Misc/Notoriety.cs @@ -270,7 +270,24 @@ namespace Server.Misc return Notoriety.CanBeAttacked; } - Body body = target.Amount; + // BaseCreatures are deleted on death, so target.Owner is null after a server restart. + // The OwnerWasBaseCreature flag is the persisted snapshot that survives the live mobile. + var creature = target.Owner as BaseCreature; + var ownerWasCreature = target.OwnerWasBaseCreature || creature != null; + + // A player's murderer and criminal status outrank guild standing, as on the live mobile. + if (!ownerWasCreature) + { + if (target.Murderer) + { + return Notoriety.Murderer; + } + + if (target.Criminal && (target.Map?.Rules & MapRules.HarmfulRestrictions) == 0) + { + return Notoriety.Criminal; + } + } var sourceGuild = GetGuildFor(source.Guild as Guild, source); var targetGuild = GetGuildFor(target.Guild, target.Owner); @@ -292,11 +309,7 @@ namespace Server.Misc } } - // BaseCreatures are deleted on death, so target.Owner is null after a server restart. - // The OwnerWasBaseCreature flag is the persisted snapshot that survives the live mobile. - var creature = target.Owner as BaseCreature; - - if (target.OwnerWasBaseCreature || creature != null) + if (ownerWasCreature) { if (srcFaction != null && trgFaction != null && srcFaction != trgFaction && source.Map == Faction.Facet) { @@ -309,6 +322,7 @@ namespace Server.Misc } var actual = Notoriety.CanBeAttacked; + Body body = target.Amount; if (target.Murderer || body.IsMonster && target.OwnerWasSummoned || target.OwnerWasAnimatedDead) { @@ -333,16 +347,6 @@ namespace Server.Misc return Notoriety.Innocent; } - if (target.Murderer || body.IsMonster) - { - return Notoriety.Murderer; - } - - if (target.Criminal && (target.Map?.Rules & MapRules.HarmfulRestrictions) == 0) - { - return Notoriety.Criminal; - } - if (srcFaction != null && trgFaction != null && srcFaction != trgFaction && source.Map == Faction.Facet) { for (var i = 0; i < list.Count; ++i)