fix(items): harden Battle Lust cleanup

This commit is contained in:
Crome696 2026-07-08 19:32:11 +02:00
parent 4d9a7f815f
commit d1580614e0
4 changed files with 184 additions and 3 deletions

View file

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using Server;
using Server.Items;
using Server.Mobiles;
using Server.Text;
using Xunit;
@ -99,6 +100,7 @@ public class BattleLustPropertyTests
var wielder = CreateMobile(hitsMax: 5000, hits: 5000);
var source = CreateMobile(location: new Point3D(6201, 500, 0));
var deletedSource = CreateMobile(location: new Point3D(6202, 500, 0));
var deadSource = CreateDeadPlayerMobile(new Point3D(6203, 500, 0));
var weapon = EquipBattleLustWeapon(wielder);
try
@ -122,6 +124,10 @@ public class BattleLustPropertyTests
ApplyDamage(wielder, deletedSource, BattleLust.DamageThreshold);
Assert.Equal(0, BattleLust.GetPoints(wielder));
Core._now = Core._now.AddSeconds(2);
ApplyDamage(wielder, deadSource, BattleLust.DamageThreshold);
Assert.Equal(0, BattleLust.GetPoints(wielder));
Core._now = Core._now.AddSeconds(2);
ApplyDamage(wielder, source, BattleLust.DamageThreshold);
Assert.Equal(1, BattleLust.GetPoints(wielder));
@ -135,6 +141,38 @@ public class BattleLustPropertyTests
wielder.Delete();
source.Delete();
deletedSource.Delete();
deadSource.Delete();
}
}
[Fact]
public void DamageTaken_DoesNotGainWhenMobileDamageAppliesNoHitPointLoss()
{
var previousExpansion = Core.Expansion;
var previousNow = Core._now;
var wielder = CreateMobile(hitsMax: 5000, hits: 5000);
var source = CreateMobile(location: new Point3D(6201, 500, 0));
var weapon = EquipBattleLustWeapon(wielder);
try
{
Core.Expansion = Expansion.SA;
Core._now = TestNow;
wielder.Blessed = true;
ApplyDamage(wielder, source, BattleLust.DamageThreshold);
Assert.Equal(5000, wielder.Hits);
Assert.Equal(0, BattleLust.GetPoints(wielder));
}
finally
{
Core.Expansion = previousExpansion;
Core._now = previousNow;
BattleLust.Clear(wielder);
weapon.Delete();
wielder.Delete();
source.Delete();
}
}
@ -257,6 +295,105 @@ public class BattleLustPropertyTests
}
}
[Fact]
public void RuntimeContext_ClearsOnWeaponLossEvenIfReequippedBeforeLazyCleanup()
{
var previousExpansion = Core.Expansion;
var previousNow = Core._now;
var wielder = CreateMobile();
var source = CreateMobile(location: new Point3D(6201, 500, 0));
var weapon = EquipBattleLustWeapon(wielder);
try
{
Core.Expansion = Expansion.SA;
Core._now = TestNow;
ApplyDamage(wielder, source, BattleLust.DamageThreshold);
Assert.Equal(1, BattleLust.GetPoints(wielder));
wielder.RemoveItem(weapon);
wielder.AddItem(weapon);
Assert.Equal(0, BattleLust.GetPoints(wielder));
}
finally
{
Core.Expansion = previousExpansion;
Core._now = previousNow;
BattleLust.Clear(wielder);
weapon.Delete();
wielder.Delete();
source.Delete();
}
}
[Fact]
public void RuntimeContext_ClearsWhenBattleLustPropertyIsRemovedEvenIfRestoredBeforeLazyCleanup()
{
var previousExpansion = Core.Expansion;
var previousNow = Core._now;
var wielder = CreateMobile();
var source = CreateMobile(location: new Point3D(6201, 500, 0));
var weapon = EquipBattleLustWeapon(wielder);
try
{
Core.Expansion = Expansion.SA;
Core._now = TestNow;
ApplyDamage(wielder, source, BattleLust.DamageThreshold);
Assert.Equal(1, BattleLust.GetPoints(wielder));
weapon.WeaponAttributes.BattleLust = 0;
weapon.WeaponAttributes.BattleLust = 1;
Assert.Equal(0, BattleLust.GetPoints(wielder));
}
finally
{
Core.Expansion = previousExpansion;
Core._now = previousNow;
BattleLust.Clear(wielder);
weapon.Delete();
wielder.Delete();
source.Delete();
}
}
[Fact]
public void RuntimeContext_ClearsOnInvalidMapEvenIfOwnerReturnsBeforeDecayTick()
{
var previousExpansion = Core.Expansion;
var previousNow = Core._now;
var wielder = CreateMobile();
var source = CreateMobile(location: new Point3D(6201, 500, 0));
var weapon = EquipBattleLustWeapon(wielder);
try
{
Core.Expansion = Expansion.SA;
Core._now = TestNow;
ApplyDamage(wielder, source, BattleLust.DamageThreshold);
Assert.Equal(1, BattleLust.GetPoints(wielder));
wielder.MoveToWorld(Point3D.Zero, Map.Internal);
wielder.MoveToWorld(new Point3D(6200, 500, 0), Map.Felucca);
Assert.Equal(0, BattleLust.GetPoints(wielder));
}
finally
{
Core.Expansion = previousExpansion;
Core._now = previousNow;
BattleLust.Clear(wielder);
weapon.Delete();
wielder.Delete();
source.Delete();
}
}
[Fact]
public void GetDamageBonus_UsesAggressedCountAndPropertySpecificPvpPvmCaps()
{
@ -400,6 +537,18 @@ public class BattleLustPropertyTests
return mobile;
}
private static PlayerMobile CreateDeadPlayerMobile(Point3D location)
{
var mobile = new PlayerMobile(World.NewMobile);
mobile.DefaultMobileInit();
mobile.Player = true;
mobile.Hits = mobile.HitsMax;
mobile.MoveToWorld(location, Map.Felucca);
mobile.Body = mobile.Race.GhostBody(mobile);
Assert.False(mobile.Alive);
return mobile;
}
private static void InitializeHits(Mobile mobile, int hitsMax, int hits)
{
mobile.RawStr = Math.Max(1, (hitsMax - 50) * 2);

View file

@ -1140,6 +1140,11 @@ public abstract partial class BaseWeapon
return;
}
if (WeaponAttributes.BattleLust != 0)
{
BattleLust.Clear(m);
}
var serial = Serial;
m.RemoveStatMod($"{serial}Str");
@ -1176,6 +1181,16 @@ public abstract partial class BaseWeapon
m.Delta(MobileDelta.WeaponDamage);
}
public override void OnMapChange()
{
base.OnMapChange();
if ((Map == null || Map == Map.Internal) && Parent is Mobile m && WeaponAttributes.BattleLust != 0)
{
BattleLust.Clear(m);
}
}
public virtual SkillName GetUsedSkill(Mobile m, bool checkSkillAttrs)
{
SkillName sk;

View file

@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using ModernUO.CodeGeneratedEvents;
using Server.Mobiles;
namespace Server.Items;
@ -86,7 +88,11 @@ public static class BattleLust
return context.Points;
}
internal static void Clear(Mobile mobile)
[OnEvent(nameof(PlayerMobile.PlayerDeathEvent))]
[OnEvent(nameof(PlayerMobile.PlayerDeletedEvent))]
[OnEvent(nameof(BaseCreature.CreatureDeathEvent))]
[OnEvent(nameof(BaseCreature.CreatureDeletedEvent))]
public static void Clear(Mobile mobile)
{
if (mobile != null && _contexts.Remove(mobile, out var context))
{

View file

@ -236,8 +236,9 @@ namespace Server
SpellHelper.DoLeech(totalDamage, from, m);
}
var oldHits = m.Hits;
m.Damage(totalDamage, from);
BattleLust.OnDamageTaken(m, from, totalDamage);
BattleLust.OnDamageTaken(m, from, Math.Max(0, oldHits - m.Hits));
return totalDamage;
}
@ -987,7 +988,17 @@ namespace Server
public int BattleLust
{
get => this[AosWeaponAttribute.BattleLust];
set => this[AosWeaponAttribute.BattleLust] = value;
set
{
var hadBattleLust = BattleLust != 0;
this[AosWeaponAttribute.BattleLust] = value;
if (hadBattleLust && value == 0 && Owner is BaseWeapon { Parent: Mobile m })
{
Server.Items.BattleLust.Clear(m);
}
}
}
public static int GetValue(Mobile m, AosWeaponAttribute attribute)