feat(items): implement searing weapon property
This commit is contained in:
parent
ed19b8f4fd
commit
4556adcd10
16 changed files with 559 additions and 24 deletions
|
|
@ -31,6 +31,7 @@ public class ExtendedWeaponAttributesTests
|
|||
Assert.Equal(0x00000040, (int)ExtendedWeaponAttribute.Focus);
|
||||
Assert.Equal(0x00000080, (int)ExtendedWeaponAttribute.BoneBreaker);
|
||||
Assert.Equal(0x00000100, (int)ExtendedWeaponAttribute.AssassinHoned);
|
||||
Assert.Equal(0x00000200, (int)ExtendedWeaponAttribute.Searing);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -51,6 +52,8 @@ public class ExtendedWeaponAttributesTests
|
|||
Assert.Equal(0, weapon.ExtendedWeaponAttributes.Focus);
|
||||
Assert.Equal(0, weapon.ExtendedWeaponAttributes.BoneBreaker);
|
||||
Assert.Equal(0, weapon.ExtendedWeaponAttributes.AssassinHoned);
|
||||
Assert.Equal(0, weapon.ExtendedWeaponAttributes.Searing);
|
||||
Assert.False(weapon.SearingIgnited);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
@ -91,6 +94,7 @@ public class ExtendedWeaponAttributesTests
|
|||
AssertStaffCommandProperty(nameof(ExtendedWeaponAttributes.Focus));
|
||||
AssertStaffCommandProperty(nameof(ExtendedWeaponAttributes.BoneBreaker));
|
||||
AssertStaffCommandProperty(nameof(ExtendedWeaponAttributes.AssassinHoned));
|
||||
AssertStaffCommandProperty(nameof(ExtendedWeaponAttributes.Searing));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -112,6 +116,8 @@ public class ExtendedWeaponAttributesTests
|
|||
weapon.ExtendedWeaponAttributes.Focus = 1;
|
||||
weapon.ExtendedWeaponAttributes.BoneBreaker = 1;
|
||||
weapon.ExtendedWeaponAttributes.AssassinHoned = 1;
|
||||
weapon.ExtendedWeaponAttributes.Searing = 1;
|
||||
weapon.SearingIgnited = true;
|
||||
|
||||
var writer = new BufferWriter(true);
|
||||
weapon.Serialize(writer);
|
||||
|
|
@ -131,6 +137,8 @@ public class ExtendedWeaponAttributesTests
|
|||
Assert.Equal(1, deserialized.ExtendedWeaponAttributes.Focus);
|
||||
Assert.Equal(1, deserialized.ExtendedWeaponAttributes.BoneBreaker);
|
||||
Assert.Equal(1, deserialized.ExtendedWeaponAttributes.AssassinHoned);
|
||||
Assert.Equal(1, deserialized.ExtendedWeaponAttributes.Searing);
|
||||
Assert.True(deserialized.SearingIgnited);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
|
|||
|
|
@ -108,9 +108,9 @@ namespace Server.Items
|
|||
}
|
||||
}
|
||||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, double damageBonus = 1)
|
||||
public override void OnHit(Mobile attacker, Mobile defender, double damageBonus = 1, bool searingProcEligible = false)
|
||||
{
|
||||
base.OnHit(attacker, defender, damageBonus);
|
||||
base.OnHit(attacker, defender, damageBonus, searingProcEligible);
|
||||
|
||||
if (!Core.AOS && Core.UOR && (attacker.Player || attacker.Body.IsHuman) && Layer == Layer.TwoHanded &&
|
||||
attacker.Skills.Anatomy.Value >= 80 &&
|
||||
|
|
|
|||
|
|
@ -152,4 +152,44 @@ public abstract partial class BaseWeapon
|
|||
_unwieldyOriginalWeight = content.UnwieldyOriginalWeight;
|
||||
_absorptionAttributes = AbsorptionAttributesDefaultValue();
|
||||
}
|
||||
|
||||
private void MigrateFrom(V14Content content)
|
||||
{
|
||||
_damageLevel = content.DamageLevel ?? WeaponDamageLevel.Regular;
|
||||
_accuracyLevel = content.AccuracyLevel ?? WeaponAccuracyLevel.Regular;
|
||||
_durabilityLevel = content.DurabilityLevel ?? WeaponDurabilityLevel.Regular;
|
||||
_quality = content.Quality ?? WeaponQuality.Regular;
|
||||
_hitPoints = content.HitPoints ?? 0;
|
||||
_maxHitPoints = content.MaxHitPoints ?? 0;
|
||||
_slayer = content.Slayer ?? SlayerName.None;
|
||||
_poison = content.Poison;
|
||||
_poisonCharges = content.PoisonCharges ?? 0;
|
||||
_crafter = content.Crafter;
|
||||
_identified = content.Identified;
|
||||
_strRequirement = content.StrRequirement ?? -1;
|
||||
_dexRequirement = content.DexRequirement ?? -1;
|
||||
_intRequirement = content.IntRequirement ?? -1;
|
||||
_minDamage = content.MinDamage ?? -1;
|
||||
_maxDamage = content.MaxDamage ?? -1;
|
||||
_hitSound = content.HitSound ?? -1;
|
||||
_missSound = content.MissSound ?? -1;
|
||||
_speed = content.Speed ?? -1;
|
||||
_maxRange = content.MaxRange ?? -1;
|
||||
_skill = content.Skill ?? (SkillName)(-1);
|
||||
_type = content.Type ?? (WeaponType)(-1);
|
||||
_animation = content.Animation ?? (WeaponAnimation)(-1);
|
||||
_resource = content.Resource ?? CraftResource.Iron;
|
||||
_attributes = content.Attributes ?? AttributesDefaultValue();
|
||||
_weaponAttributes = content.WeaponAttributes ?? WeaponAttributesDefaultValue();
|
||||
_playerConstructed = content.PlayerConstructed;
|
||||
_skillBonuses = content.SkillBonuses ?? SkillBonusesDefaultValue();
|
||||
_slayer2 = content.Slayer2 ?? SlayerName.None;
|
||||
_aosElementDamages = content.AosElementDamages ?? AosElementAttributesDefaultValue();
|
||||
_engravedText = content.EngravedText;
|
||||
_extendedWeaponAttributes = content.ExtendedWeaponAttributes ?? ExtendedWeaponAttributesDefaultValue();
|
||||
_negativeAttributes = content.NegativeAttributes ?? NegativeAttributesDefaultValue();
|
||||
_unwieldyOriginalWeight = content.UnwieldyOriginalWeight;
|
||||
_absorptionAttributes = content.AbsorptionAttributes ?? AbsorptionAttributesDefaultValue();
|
||||
_searingIgnited = false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||
using System.Runtime.CompilerServices;
|
||||
using ModernUO.Serialization;
|
||||
using Server.Collections;
|
||||
using Server.ContextMenus;
|
||||
using Server.Engines.Craft;
|
||||
using Server.Engines.Virtues;
|
||||
using Server.Ethics;
|
||||
|
|
@ -28,7 +29,7 @@ public interface ISlayer
|
|||
SlayerName Slayer2 { get; set; }
|
||||
}
|
||||
|
||||
[SerializationGenerator(14, false)]
|
||||
[SerializationGenerator(15, false)]
|
||||
public abstract partial class BaseWeapon
|
||||
: Item, IWeapon, IFactionItem, ICraftable, ISlayer, IDurability, IAosItem, IIdentifiable
|
||||
{
|
||||
|
|
@ -223,6 +224,12 @@ public abstract partial class BaseWeapon
|
|||
[SerializableFieldDefault(34)]
|
||||
private AbsorptionAttributes AbsorptionAttributesDefaultValue() => new(this);
|
||||
|
||||
// Field 35 intentionally has no save flag; BaseWeapon's legacy save-flag mask is already at the high bit.
|
||||
[InvalidateProperties]
|
||||
[SerializableField(35)]
|
||||
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
||||
private bool _searingIgnited;
|
||||
|
||||
private FactionItem m_FactionState;
|
||||
private SkillMod m_SkillMod, m_MageMod;
|
||||
private int _lastParryChance;
|
||||
|
|
@ -973,9 +980,11 @@ public abstract partial class BaseWeapon
|
|||
}
|
||||
}
|
||||
|
||||
var searingProcEligible = Searing.BeginAttack(this, attacker);
|
||||
|
||||
if (CheckHit(attacker, defender))
|
||||
{
|
||||
OnHit(attacker, defender, damageBonus);
|
||||
OnHit(attacker, defender, damageBonus, searingProcEligible);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1203,10 +1212,21 @@ public abstract partial class BaseWeapon
|
|||
}
|
||||
}
|
||||
|
||||
public override void GetContextMenuEntries(Mobile from, ref PooledRefList<ContextMenuEntry> list)
|
||||
{
|
||||
base.GetContextMenuEntries(from, ref list);
|
||||
|
||||
if (Core.HS && ExtendedWeaponAttributes.Searing != 0 && Parent == from && from.Weapon == this)
|
||||
{
|
||||
list.Add(new ToggleSearingEntry(SearingIgnited));
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnRemoved(IEntity parent)
|
||||
{
|
||||
ClearLastParryChance();
|
||||
FocusContext.Clear(this);
|
||||
SearingIgnited = false;
|
||||
EnchantSpell.StopEffect(this);
|
||||
|
||||
if (parent is not Mobile m)
|
||||
|
|
@ -1258,10 +1278,23 @@ public abstract partial class BaseWeapon
|
|||
public override void OnAfterDelete()
|
||||
{
|
||||
FocusContext.Clear(this);
|
||||
SearingIgnited = false;
|
||||
EnchantSpell.StopEffect(this);
|
||||
base.OnAfterDelete();
|
||||
}
|
||||
|
||||
private sealed class ToggleSearingEntry(bool ignited) : ContextMenuEntry(ignited ? 1151174 : 1151173)
|
||||
{
|
||||
public override void OnClick(Mobile from, IEntity target)
|
||||
{
|
||||
if (Core.HS && target is BaseWeapon { Deleted: false, ExtendedWeaponAttributes.Searing: not 0 } weapon &&
|
||||
weapon.Parent == from && from.Weapon == weapon)
|
||||
{
|
||||
weapon.SearingIgnited = !weapon.SearingIgnited;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnMapChange()
|
||||
{
|
||||
base.OnMapChange();
|
||||
|
|
@ -1270,6 +1303,7 @@ public abstract partial class BaseWeapon
|
|||
if (Map == null || Map == Map.Internal)
|
||||
{
|
||||
FocusContext.Clear(this);
|
||||
SearingIgnited = false;
|
||||
}
|
||||
|
||||
if ((Map == null || Map == Map.Internal) && Parent is Mobile m && ExtendedWeaponAttributes.BattleLust != 0)
|
||||
|
|
@ -1920,7 +1954,7 @@ public abstract partial class BaseWeapon
|
|||
};
|
||||
}
|
||||
|
||||
public virtual void OnHit(Mobile attacker, Mobile defender, double damageBonus = 1.0)
|
||||
public virtual void OnHit(Mobile attacker, Mobile defender, double damageBonus = 1.0, bool searingProcEligible = false)
|
||||
{
|
||||
if (MirrorImage.HasClone(defender) && defender.Skills.Ninjitsu.Value / 150.0 > Utility.RandomDouble())
|
||||
{
|
||||
|
|
@ -2432,6 +2466,7 @@ public abstract partial class BaseWeapon
|
|||
Sparks.TryProcOnNormalHit(attacker, defender, ExtendedWeaponAttributes.HitSparks);
|
||||
Swarm.TryProcOnNormalHit(attacker, defender, ExtendedWeaponAttributes.HitSwarm);
|
||||
BoneBreaker.TryProcOnNormalHit(attacker, defender, ExtendedWeaponAttributes.BoneBreaker);
|
||||
Searing.TryProcOnNormalHit(this, attacker, defender, searingProcEligible);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,9 +24,9 @@ namespace Server.Items
|
|||
from.Target = new BladedItemTarget(this);
|
||||
}
|
||||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, double damageBonus = 1)
|
||||
public override void OnHit(Mobile attacker, Mobile defender, double damageBonus = 1, bool searingProcEligible = false)
|
||||
{
|
||||
base.OnHit(attacker, defender, damageBonus);
|
||||
base.OnHit(attacker, defender, damageBonus, searingProcEligible);
|
||||
|
||||
if (!Core.AOS && Poison != null && PoisonCharges > 0)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -17,9 +17,9 @@ namespace Server.Items
|
|||
public override WeaponType DefType => WeaponType.Bashing;
|
||||
public override WeaponAnimation DefAnimation => WeaponAnimation.Bash1H;
|
||||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, double damageBonus = 1)
|
||||
public override void OnHit(Mobile attacker, Mobile defender, double damageBonus = 1, bool searingProcEligible = false)
|
||||
{
|
||||
base.OnHit(attacker, defender, damageBonus);
|
||||
base.OnHit(attacker, defender, damageBonus, searingProcEligible);
|
||||
|
||||
defender.Stam -= Utility.Random(3, 3); // 3-5 points of stamina loss
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,9 +58,9 @@ namespace Server.Items
|
|||
}
|
||||
}
|
||||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, double damageBonus = 1)
|
||||
public override void OnHit(Mobile attacker, Mobile defender, double damageBonus = 1, bool searingProcEligible = false)
|
||||
{
|
||||
base.OnHit(attacker, defender, damageBonus);
|
||||
base.OnHit(attacker, defender, damageBonus, searingProcEligible);
|
||||
|
||||
if (!Core.AOS && (attacker.Player || attacker.Body.IsHuman) && Layer == Layer.TwoHanded &&
|
||||
attacker.Skills.Anatomy.Value >= 80 &&
|
||||
|
|
|
|||
|
|
@ -75,9 +75,11 @@ namespace Server.Items
|
|||
|
||||
if (OnFired(attacker, defender))
|
||||
{
|
||||
var searingProcEligible = Searing.BeginAttack(this, attacker);
|
||||
|
||||
if (CheckHit(attacker, defender))
|
||||
{
|
||||
OnHit(attacker, defender);
|
||||
OnHit(attacker, defender, 1.0, searingProcEligible);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -102,7 +104,7 @@ namespace Server.Items
|
|||
return TimeSpan.FromSeconds(0.25);
|
||||
}
|
||||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, double damageBonus = 1)
|
||||
public override void OnHit(Mobile attacker, Mobile defender, double damageBonus = 1, bool searingProcEligible = false)
|
||||
{
|
||||
if (attacker.Player && !defender.Player && (defender.Body.IsAnimal || defender.Body.IsMonster) &&
|
||||
Utility.RandomDouble() < 0.4 && Ammo is { } ammo && !defender.AddToBackpack(ammo))
|
||||
|
|
@ -130,7 +132,7 @@ namespace Server.Items
|
|||
}
|
||||
}
|
||||
|
||||
base.OnHit(attacker, defender, damageBonus);
|
||||
base.OnHit(attacker, defender, damageBonus, searingProcEligible);
|
||||
}
|
||||
|
||||
public override void OnMiss(Mobile attacker, Mobile defender)
|
||||
|
|
|
|||
134
Projects/UOContent/Items/Weapons/Searing.cs
Normal file
134
Projects/UOContent/Items/Weapons/Searing.cs
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ModernUO.CodeGeneratedEvents;
|
||||
using Server.Mobiles;
|
||||
|
||||
namespace Server.Items;
|
||||
|
||||
public static class Searing
|
||||
{
|
||||
// 10-15 fire damage is the issue's explicit ServUO-compatibility policy, not an EA-verified value.
|
||||
internal const int MinimumFireDamage = 10;
|
||||
internal const int MaximumFireDamage = 15;
|
||||
internal const int SelfDamage = 4;
|
||||
internal const int MeleeProcChance = 20;
|
||||
internal const int RangedProcChance = 10;
|
||||
internal const int PlayerRegenPenalty = 20;
|
||||
internal const int NpcRegenPenalty = 60;
|
||||
internal static readonly TimeSpan RegenPenaltyDuration = TimeSpan.FromSeconds(4);
|
||||
|
||||
private static readonly Dictionary<Mobile, Context> _contexts = [];
|
||||
|
||||
public static void Configure()
|
||||
{
|
||||
EventSink.Logout += ClearTarget;
|
||||
}
|
||||
|
||||
internal static int ActiveContextCount => _contexts.Count;
|
||||
|
||||
internal static bool BeginAttack(BaseWeapon weapon, Mobile attacker)
|
||||
{
|
||||
if (!CanUse(weapon, attacker))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (attacker.Mana <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
attacker.Mana--;
|
||||
return true;
|
||||
}
|
||||
|
||||
internal static void TryProcOnNormalHit(BaseWeapon weapon, Mobile attacker, Mobile defender, bool procEligible)
|
||||
{
|
||||
if (!procEligible || !CanUse(weapon, attacker) || !IsValidTarget(defender))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var chance = weapon is BaseRanged ? RangedProcChance : MeleeProcChance;
|
||||
|
||||
if (chance <= Utility.Random(100))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
attacker.Damage(SelfDamage, attacker);
|
||||
AOS.Damage(defender, attacker, Utility.RandomMinMax(MinimumFireDamage, MaximumFireDamage), false, 0, 100, 0, 0, 0);
|
||||
ApplyRegenPenalty(defender);
|
||||
}
|
||||
|
||||
internal static int GetHitRegenPenalty(Mobile mobile) =>
|
||||
mobile != null && _contexts.ContainsKey(mobile) ? mobile.Player ? -PlayerRegenPenalty : -NpcRegenPenalty : 0;
|
||||
|
||||
internal static bool HasContext(Mobile mobile) => mobile != null && _contexts.ContainsKey(mobile);
|
||||
|
||||
internal static void ExpireForTests(Mobile mobile) => Clear(mobile);
|
||||
|
||||
internal static void ClearAll()
|
||||
{
|
||||
foreach (var context in _contexts.Values)
|
||||
{
|
||||
context.TimerToken.Cancel();
|
||||
}
|
||||
|
||||
_contexts.Clear();
|
||||
}
|
||||
|
||||
private static void ClearTarget(Mobile mobile)
|
||||
{
|
||||
if (mobile != null && _contexts.Remove(mobile, out var context))
|
||||
{
|
||||
context.TimerToken.Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
[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)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_contexts.Remove(mobile, out var context))
|
||||
{
|
||||
context.TimerToken.Cancel();
|
||||
}
|
||||
|
||||
if (mobile.Weapon is BaseWeapon weapon)
|
||||
{
|
||||
weapon.SearingIgnited = false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void ApplyRegenPenalty(Mobile defender)
|
||||
{
|
||||
Clear(defender);
|
||||
Timer.StartTimer(RegenPenaltyDuration, () => Clear(defender), out var token);
|
||||
_contexts[defender] = new Context(token);
|
||||
defender.CheckStatTimers();
|
||||
}
|
||||
|
||||
private static bool CanUse(BaseWeapon weapon, Mobile attacker) =>
|
||||
Core.HS &&
|
||||
weapon is { Deleted: false, SearingIgnited: true } &&
|
||||
weapon.ExtendedWeaponAttributes.Searing != 0 &&
|
||||
attacker?.Deleted == false &&
|
||||
attacker.Alive &&
|
||||
attacker.Map != null &&
|
||||
attacker.Map != Map.Internal &&
|
||||
attacker.Weapon == weapon &&
|
||||
weapon.Parent == attacker;
|
||||
|
||||
private static bool IsValidTarget(Mobile mobile) =>
|
||||
mobile is { Deleted: false, Alive: true } && mobile.Map != null && mobile.Map != Map.Internal;
|
||||
|
||||
private readonly record struct Context(TimerExecutionToken TimerToken);
|
||||
}
|
||||
|
|
@ -18,9 +18,9 @@ namespace Server.Items
|
|||
public override WeaponType DefType => WeaponType.Piercing;
|
||||
public override WeaponAnimation DefAnimation => WeaponAnimation.Pierce2H;
|
||||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, double damageBonus = 1)
|
||||
public override void OnHit(Mobile attacker, Mobile defender, double damageBonus = 1, bool searingProcEligible = false)
|
||||
{
|
||||
base.OnHit(attacker, defender, damageBonus);
|
||||
base.OnHit(attacker, defender, damageBonus, searingProcEligible);
|
||||
|
||||
if (!Core.AOS && Core.UOR && Layer == Layer.TwoHanded &&
|
||||
attacker.Skills.Anatomy.Value / 400.0 >= Utility.RandomDouble() &&
|
||||
|
|
|
|||
|
|
@ -16,9 +16,9 @@ namespace Server.Items
|
|||
public override WeaponType DefType => WeaponType.Staff;
|
||||
public override WeaponAnimation DefAnimation => WeaponAnimation.Bash2H;
|
||||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, double damageBonus = 1)
|
||||
public override void OnHit(Mobile attacker, Mobile defender, double damageBonus = 1, bool searingProcEligible = false)
|
||||
{
|
||||
base.OnHit(attacker, defender, damageBonus);
|
||||
base.OnHit(attacker, defender, damageBonus, searingProcEligible);
|
||||
|
||||
defender.Stam -= Utility.Random(3, 3); // 3-5 points of stamina loss
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,9 +21,9 @@ namespace Server.Items
|
|||
from.Target = new BladedItemTarget(this);
|
||||
}
|
||||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, double damageBonus = 1)
|
||||
public override void OnHit(Mobile attacker, Mobile defender, double damageBonus = 1, bool searingProcEligible = false)
|
||||
{
|
||||
base.OnHit(attacker, defender, damageBonus);
|
||||
base.OnHit(attacker, defender, damageBonus, searingProcEligible);
|
||||
|
||||
if (!Core.AOS && Poison != null && PoisonCharges > 0)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ public abstract partial class BaseThrown : BaseRanged
|
|||
return true;
|
||||
}
|
||||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, double damageBonus = 1)
|
||||
public override void OnHit(Mobile attacker, Mobile defender, double damageBonus = 1, bool searingProcEligible = false)
|
||||
{
|
||||
if (WeaponAbility.GetCurrentAbility(attacker) is not MysticArc)
|
||||
{
|
||||
|
|
@ -118,7 +118,7 @@ public abstract partial class BaseThrown : BaseRanged
|
|||
Timer.StartTimer(TimeSpan.FromSeconds(0.3), () => Return(attacker, defender, location));
|
||||
}
|
||||
|
||||
base.OnHit(attacker, defender, damageBonus);
|
||||
base.OnHit(attacker, defender, damageBonus, searingProcEligible);
|
||||
}
|
||||
|
||||
public override void OnMiss(Mobile attacker, Mobile defender)
|
||||
|
|
|
|||
295
Projects/UOContent/Migrations/Server.Items.BaseWeapon.v15.json
generated
Normal file
295
Projects/UOContent/Migrations/Server.Items.BaseWeapon.v15.json
generated
Normal file
|
|
@ -0,0 +1,295 @@
|
|||
{
|
||||
"version": 15,
|
||||
"type": "Server.Items.BaseWeapon",
|
||||
"properties": [
|
||||
{
|
||||
"name": "DamageLevel",
|
||||
"type": "Server.Items.WeaponDamageLevel",
|
||||
"usesSaveFlag": true,
|
||||
"rule": "EnumMigrationRule"
|
||||
},
|
||||
{
|
||||
"name": "AccuracyLevel",
|
||||
"type": "Server.Items.WeaponAccuracyLevel",
|
||||
"usesSaveFlag": true,
|
||||
"rule": "EnumMigrationRule"
|
||||
},
|
||||
{
|
||||
"name": "DurabilityLevel",
|
||||
"type": "Server.Items.WeaponDurabilityLevel",
|
||||
"usesSaveFlag": true,
|
||||
"rule": "EnumMigrationRule"
|
||||
},
|
||||
{
|
||||
"name": "Quality",
|
||||
"type": "Server.Items.WeaponQuality",
|
||||
"usesSaveFlag": true,
|
||||
"rule": "EnumMigrationRule"
|
||||
},
|
||||
{
|
||||
"name": "HitPoints",
|
||||
"type": "int",
|
||||
"usesSaveFlag": true,
|
||||
"rule": "PrimitiveTypeMigrationRule",
|
||||
"ruleArguments": [
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "MaxHitPoints",
|
||||
"type": "int",
|
||||
"usesSaveFlag": true,
|
||||
"rule": "PrimitiveTypeMigrationRule",
|
||||
"ruleArguments": [
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Slayer",
|
||||
"type": "Server.Items.SlayerName",
|
||||
"usesSaveFlag": true,
|
||||
"rule": "EnumMigrationRule"
|
||||
},
|
||||
{
|
||||
"name": "Poison",
|
||||
"type": "Server.Poison",
|
||||
"usesSaveFlag": true,
|
||||
"rule": "PrimitiveUOTypeMigrationRule",
|
||||
"ruleArguments": [
|
||||
"Poison"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "PoisonCharges",
|
||||
"type": "int",
|
||||
"usesSaveFlag": true,
|
||||
"rule": "PrimitiveTypeMigrationRule",
|
||||
"ruleArguments": [
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Crafter",
|
||||
"type": "string",
|
||||
"usesSaveFlag": true,
|
||||
"rule": "PrimitiveTypeMigrationRule",
|
||||
"ruleArguments": [
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Identified",
|
||||
"type": "bool",
|
||||
"usesSaveFlag": true,
|
||||
"rule": "PrimitiveTypeMigrationRule",
|
||||
"ruleArguments": [
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "StrRequirement",
|
||||
"type": "int",
|
||||
"usesSaveFlag": true,
|
||||
"rule": "PrimitiveTypeMigrationRule",
|
||||
"ruleArguments": [
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "DexRequirement",
|
||||
"type": "int",
|
||||
"usesSaveFlag": true,
|
||||
"rule": "PrimitiveTypeMigrationRule",
|
||||
"ruleArguments": [
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "IntRequirement",
|
||||
"type": "int",
|
||||
"usesSaveFlag": true,
|
||||
"rule": "PrimitiveTypeMigrationRule",
|
||||
"ruleArguments": [
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "MinDamage",
|
||||
"type": "int",
|
||||
"usesSaveFlag": true,
|
||||
"rule": "PrimitiveTypeMigrationRule",
|
||||
"ruleArguments": [
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "MaxDamage",
|
||||
"type": "int",
|
||||
"usesSaveFlag": true,
|
||||
"rule": "PrimitiveTypeMigrationRule",
|
||||
"ruleArguments": [
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "HitSound",
|
||||
"type": "int",
|
||||
"usesSaveFlag": true,
|
||||
"rule": "PrimitiveTypeMigrationRule",
|
||||
"ruleArguments": [
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "MissSound",
|
||||
"type": "int",
|
||||
"usesSaveFlag": true,
|
||||
"rule": "PrimitiveTypeMigrationRule",
|
||||
"ruleArguments": [
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Speed",
|
||||
"type": "float",
|
||||
"usesSaveFlag": true,
|
||||
"rule": "PrimitiveTypeMigrationRule",
|
||||
"ruleArguments": [
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "MaxRange",
|
||||
"type": "int",
|
||||
"usesSaveFlag": true,
|
||||
"rule": "PrimitiveTypeMigrationRule",
|
||||
"ruleArguments": [
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Skill",
|
||||
"type": "Server.SkillName",
|
||||
"usesSaveFlag": true,
|
||||
"rule": "EnumMigrationRule"
|
||||
},
|
||||
{
|
||||
"name": "Type",
|
||||
"type": "Server.Items.WeaponType",
|
||||
"usesSaveFlag": true,
|
||||
"rule": "EnumMigrationRule"
|
||||
},
|
||||
{
|
||||
"name": "Animation",
|
||||
"type": "Server.Items.WeaponAnimation",
|
||||
"usesSaveFlag": true,
|
||||
"rule": "EnumMigrationRule"
|
||||
},
|
||||
{
|
||||
"name": "Resource",
|
||||
"type": "Server.Items.CraftResource",
|
||||
"usesSaveFlag": true,
|
||||
"rule": "EnumMigrationRule"
|
||||
},
|
||||
{
|
||||
"name": "Attributes",
|
||||
"type": "Server.AosAttributes",
|
||||
"usesSaveFlag": true,
|
||||
"rule": "RawSerializableMigrationRule",
|
||||
"ruleArguments": [
|
||||
"DeserializationRequiresParent"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "WeaponAttributes",
|
||||
"type": "Server.AosWeaponAttributes",
|
||||
"usesSaveFlag": true,
|
||||
"rule": "RawSerializableMigrationRule",
|
||||
"ruleArguments": [
|
||||
"DeserializationRequiresParent"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "PlayerConstructed",
|
||||
"type": "bool",
|
||||
"usesSaveFlag": true,
|
||||
"rule": "PrimitiveTypeMigrationRule",
|
||||
"ruleArguments": [
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "SkillBonuses",
|
||||
"type": "Server.AosSkillBonuses",
|
||||
"usesSaveFlag": true,
|
||||
"rule": "RawSerializableMigrationRule",
|
||||
"ruleArguments": [
|
||||
"DeserializationRequiresParent"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Slayer2",
|
||||
"type": "Server.Items.SlayerName",
|
||||
"usesSaveFlag": true,
|
||||
"rule": "EnumMigrationRule"
|
||||
},
|
||||
{
|
||||
"name": "AosElementDamages",
|
||||
"type": "Server.AosElementAttributes",
|
||||
"usesSaveFlag": true,
|
||||
"rule": "RawSerializableMigrationRule",
|
||||
"ruleArguments": [
|
||||
"DeserializationRequiresParent"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "EngravedText",
|
||||
"type": "string",
|
||||
"usesSaveFlag": true,
|
||||
"rule": "PrimitiveTypeMigrationRule",
|
||||
"ruleArguments": [
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "ExtendedWeaponAttributes",
|
||||
"type": "Server.ExtendedWeaponAttributes",
|
||||
"rule": "RawSerializableMigrationRule",
|
||||
"ruleArguments": [
|
||||
"DeserializationRequiresParent"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "NegativeAttributes",
|
||||
"type": "Server.NegativeAttributes",
|
||||
"rule": "RawSerializableMigrationRule",
|
||||
"ruleArguments": [
|
||||
"DeserializationRequiresParent"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "UnwieldyOriginalWeight",
|
||||
"type": "double",
|
||||
"rule": "PrimitiveTypeMigrationRule",
|
||||
"ruleArguments": [
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "AbsorptionAttributes",
|
||||
"type": "Server.AbsorptionAttributes",
|
||||
"rule": "RawSerializableMigrationRule",
|
||||
"ruleArguments": [
|
||||
"DeserializationRequiresParent"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "SearingIgnited",
|
||||
"type": "bool",
|
||||
"rule": "PrimitiveTypeMigrationRule",
|
||||
"ruleArguments": [
|
||||
""
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1275,7 +1275,8 @@ namespace Server
|
|||
SplinteringWeapon = 0x00000020,
|
||||
Focus = 0x00000040,
|
||||
BoneBreaker = 0x00000080,
|
||||
AssassinHoned = 0x00000100
|
||||
AssassinHoned = 0x00000100,
|
||||
Searing = 0x00000200
|
||||
}
|
||||
|
||||
public sealed class ExtendedWeaponAttributes : BaseAttributes
|
||||
|
|
@ -1377,6 +1378,21 @@ namespace Server
|
|||
set => this[ExtendedWeaponAttribute.AssassinHoned] = value;
|
||||
}
|
||||
|
||||
[CommandProperty(AccessLevel.GameMaster)]
|
||||
public int Searing
|
||||
{
|
||||
get => this[ExtendedWeaponAttribute.Searing];
|
||||
set
|
||||
{
|
||||
this[ExtendedWeaponAttribute.Searing] = value;
|
||||
|
||||
if (value == 0 && Owner is BaseWeapon weapon)
|
||||
{
|
||||
weapon.SearingIgnited = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void GetProperties(IPropertyList list)
|
||||
{
|
||||
if (Core.HS && Bane != 0)
|
||||
|
|
@ -1389,6 +1405,11 @@ namespace Server
|
|||
list.Add(1150018); // Focus
|
||||
}
|
||||
|
||||
if (Core.HS && Searing != 0)
|
||||
{
|
||||
list.Add(1151172); // Searing
|
||||
}
|
||||
|
||||
if (Core.SA && BattleLust != 0)
|
||||
{
|
||||
list.Add(1113710); // Battle Lust
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ namespace Server.Misc
|
|||
|
||||
private static TimeSpan Mobile_HitsRegenRate(Mobile from)
|
||||
{
|
||||
var points = AosAttributes.GetValue(from, AosAttribute.RegenHits);
|
||||
var points = AosAttributes.GetValue(from, AosAttribute.RegenHits) + Searing.GetHitRegenPenalty(from);
|
||||
|
||||
var bc = from as BaseCreature;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue