ModernUO/Projects/UOContent/Misc/AOS.cs
mark1145 1e50ff9966
fix: Blood Oath, Wraith form, & Strangle (#1153)
Fixes blood oath mechanic, and wraith form not properly leeching.
2022-09-01 09:19:40 -07:00

1512 lines
43 KiB
C#

using System;
using System.Collections.Generic;
using ModernUO.Serialization;
using Server.Items;
using Server.Mobiles;
using Server.Spells;
using Server.Spells.Fifth;
using Server.Spells.Ninjitsu;
using Server.Spells.Seventh;
namespace Server
{
public static class AOS
{
public static void DisableStatInfluences()
{
for (var i = 0; i < SkillInfo.Table.Length; ++i)
{
var info = SkillInfo.Table[i];
info.StrScale = 0.0;
info.DexScale = 0.0;
info.IntScale = 0.0;
info.StatTotal = 0.0;
}
}
public static int Damage(Mobile m, int damage, bool ignoreArmor, int phys, int fire, int cold, int pois, int nrgy) =>
Damage(m, null, damage, ignoreArmor, phys, fire, cold, pois, nrgy);
public static int Damage(Mobile m, int damage, int phys, int fire, int cold, int pois, int nrgy) =>
Damage(m, null, damage, phys, fire, cold, pois, nrgy);
public static int Damage(Mobile m, Mobile from, int damage, int phys, int fire, int cold, int pois, int nrgy) =>
Damage(m, from, damage, false, phys, fire, cold, pois, nrgy);
public static int Damage(
Mobile m, Mobile from, int damage, int phys, int fire, int cold, int pois, int nrgy,
int chaos
) =>
Damage(m, from, damage, false, phys, fire, cold, pois, nrgy, chaos);
public static int Damage(
Mobile m, Mobile from, int damage, int phys, int fire, int cold, int pois, int nrgy,
bool keepAlive
) =>
Damage(m, from, damage, false, phys, fire, cold, pois, nrgy, 0, 0, keepAlive);
public static int Damage(
Mobile m, Mobile from, int damage, bool ignoreArmor, int phys, int fire, int cold, int pois,
int nrgy, int chaos = 0, int direct = 0, bool keepAlive = false, bool archer = false, bool deathStrike = false
)
{
if (m?.Deleted != false || !m.Alive || damage <= 0)
{
return 0;
}
if (phys == 0 && fire == 100 && cold == 0 && pois == 0 && nrgy == 0)
{
MeerMage.StopEffect(m, true);
}
if (!Core.AOS)
{
m.Damage(damage, from);
return damage;
}
Fix(ref phys);
Fix(ref fire);
Fix(ref cold);
Fix(ref pois);
Fix(ref nrgy);
Fix(ref chaos);
Fix(ref direct);
if (Core.ML && chaos > 0)
{
switch (Utility.Random(5))
{
case 0:
phys += chaos;
break;
case 1:
fire += chaos;
break;
case 2:
cold += chaos;
break;
case 3:
pois += chaos;
break;
case 4:
nrgy += chaos;
break;
}
}
BaseQuiver quiver = null;
if (archer && from != null)
{
quiver = from.FindItemOnLayer(Layer.Cloak) as BaseQuiver;
}
int totalDamage;
if (!ignoreArmor)
{
// Armor Ignore on OSI ignores all defenses, not just physical.
var resPhys = m.PhysicalResistance;
var resFire = m.FireResistance;
var resCold = m.ColdResistance;
var resPois = m.PoisonResistance;
var resNrgy = m.EnergyResistance;
totalDamage = damage * phys * (100 - resPhys);
totalDamage += damage * fire * (100 - resFire);
totalDamage += damage * cold * (100 - resCold);
totalDamage += damage * pois * (100 - resPois);
totalDamage += damage * nrgy * (100 - resNrgy);
totalDamage /= 10000;
if (Core.ML)
{
totalDamage += damage * direct / 100;
if (quiver != null)
{
totalDamage += totalDamage * quiver.DamageIncrease / 100;
}
}
if (totalDamage < 1)
{
totalDamage = 1;
}
}
else if (Core.ML && m is PlayerMobile && from is PlayerMobile)
{
if (quiver != null)
{
damage += damage * quiver.DamageIncrease / 100;
}
if (!deathStrike)
{
totalDamage = Math.Min(damage, 35); // Direct Damage cap of 35
}
else
{
totalDamage = Math.Min(damage, 70); // Direct Damage cap of 70
}
}
else
{
totalDamage = damage;
if (Core.ML && quiver != null)
{
totalDamage += totalDamage * quiver.DamageIncrease / 100;
}
}
if (from?.Player != true && m.Player && m.Mount is SwampDragon { HasBarding: true } pet)
{
var percent = pet.BardingExceptional ? 20 : 10;
var absorbed = Scale(totalDamage, percent);
totalDamage -= absorbed;
pet.BardingHP -= absorbed;
if (pet.BardingHP < 0)
{
pet.HasBarding = false;
pet.BardingHP = 0;
m.SendLocalizedMessage(1053031); // Your dragon's barding has been destroyed!
}
}
if (keepAlive && totalDamage > m.Hits)
{
totalDamage = m.Hits;
}
if (from?.Deleted == false && from.Alive)
{
var reflectPhys = AosAttributes.GetValue(m, AosAttribute.ReflectPhysical);
if (reflectPhys != 0)
{
if ((from as ExodusMinion)?.FieldActive == true ||
(from as ExodusOverseer)?.FieldActive == true)
{
from.FixedParticles(0x376A, 20, 10, 0x2530, EffectLayer.Waist);
from.PlaySound(0x2F4);
m.SendAsciiMessage("Your weapon cannot penetrate the creature's magical barrier");
}
else
{
from.Damage(
Scale(damage * phys * (100 - (ignoreArmor ? 0 : m.PhysicalResistance)) / 10000, reflectPhys),
m
);
}
}
}
if (totalDamage <= 0)
{
return 0;
}
if (from != null) // sanity check
{
SpellHelper.DoLeech(totalDamage, from, m);
}
m.Damage(totalDamage, from);
return totalDamage;
}
public static void Fix(ref int val)
{
if (val < 0)
{
val = 0;
}
}
public static int Scale(int input, int percent) => input * percent / 100;
public static int GetStatus(Mobile from, int index)
{
return index switch
{
// TODO: Account for buffs/debuffs
0 => from.GetMaxResistance(ResistanceType.Physical),
1 => from.GetMaxResistance(ResistanceType.Fire),
2 => from.GetMaxResistance(ResistanceType.Cold),
3 => from.GetMaxResistance(ResistanceType.Poison),
4 => from.GetMaxResistance(ResistanceType.Energy),
5 => AosAttributes.GetValue(from, AosAttribute.DefendChance),
6 => 45,
7 => AosAttributes.GetValue(from, AosAttribute.AttackChance),
8 => AosAttributes.GetValue(from, AosAttribute.WeaponSpeed),
9 => AosAttributes.GetValue(from, AosAttribute.WeaponDamage),
10 => AosAttributes.GetValue(from, AosAttribute.LowerRegCost),
11 => AosAttributes.GetValue(from, AosAttribute.SpellDamage),
12 => AosAttributes.GetValue(from, AosAttribute.CastRecovery),
13 => AosAttributes.GetValue(from, AosAttribute.CastSpeed),
14 => AosAttributes.GetValue(from, AosAttribute.LowerManaCost),
_ => 0
};
}
}
[Flags]
public enum AosAttribute
{
RegenHits = 0x00000001,
RegenStam = 0x00000002,
RegenMana = 0x00000004,
DefendChance = 0x00000008,
AttackChance = 0x00000010,
BonusStr = 0x00000020,
BonusDex = 0x00000040,
BonusInt = 0x00000080,
BonusHits = 0x00000100,
BonusStam = 0x00000200,
BonusMana = 0x00000400,
WeaponDamage = 0x00000800,
WeaponSpeed = 0x00001000,
SpellDamage = 0x00002000,
CastRecovery = 0x00004000,
CastSpeed = 0x00008000,
LowerManaCost = 0x00010000,
LowerRegCost = 0x00020000,
ReflectPhysical = 0x00040000,
EnhancePotions = 0x00080000,
Luck = 0x00100000,
SpellChanneling = 0x00200000,
NightSight = 0x00400000,
IncreasedKarmaLoss = 0x00800000
}
public sealed class AosAttributes : BaseAttributes
{
public AosAttributes(Item owner) : base(owner)
{
}
public AosAttributes(Item owner, AosAttributes other) : base(owner, other)
{
}
public int this[AosAttribute attribute]
{
get => GetValue((int)attribute);
set => SetValue((int)attribute, value);
}
[CommandProperty(AccessLevel.GameMaster)]
public int RegenHits
{
get => this[AosAttribute.RegenHits];
set => this[AosAttribute.RegenHits] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int RegenStam
{
get => this[AosAttribute.RegenStam];
set => this[AosAttribute.RegenStam] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int RegenMana
{
get => this[AosAttribute.RegenMana];
set => this[AosAttribute.RegenMana] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int DefendChance
{
get => this[AosAttribute.DefendChance];
set => this[AosAttribute.DefendChance] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int AttackChance
{
get => this[AosAttribute.AttackChance];
set => this[AosAttribute.AttackChance] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int BonusStr
{
get => this[AosAttribute.BonusStr];
set => this[AosAttribute.BonusStr] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int BonusDex
{
get => this[AosAttribute.BonusDex];
set => this[AosAttribute.BonusDex] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int BonusInt
{
get => this[AosAttribute.BonusInt];
set => this[AosAttribute.BonusInt] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int BonusHits
{
get => this[AosAttribute.BonusHits];
set => this[AosAttribute.BonusHits] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int BonusStam
{
get => this[AosAttribute.BonusStam];
set => this[AosAttribute.BonusStam] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int BonusMana
{
get => this[AosAttribute.BonusMana];
set => this[AosAttribute.BonusMana] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int WeaponDamage
{
get => this[AosAttribute.WeaponDamage];
set => this[AosAttribute.WeaponDamage] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int WeaponSpeed
{
get => this[AosAttribute.WeaponSpeed];
set => this[AosAttribute.WeaponSpeed] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int SpellDamage
{
get => this[AosAttribute.SpellDamage];
set => this[AosAttribute.SpellDamage] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int CastRecovery
{
get => this[AosAttribute.CastRecovery];
set => this[AosAttribute.CastRecovery] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int CastSpeed
{
get => this[AosAttribute.CastSpeed];
set => this[AosAttribute.CastSpeed] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int LowerManaCost
{
get => this[AosAttribute.LowerManaCost];
set => this[AosAttribute.LowerManaCost] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int LowerRegCost
{
get => this[AosAttribute.LowerRegCost];
set => this[AosAttribute.LowerRegCost] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int ReflectPhysical
{
get => this[AosAttribute.ReflectPhysical];
set => this[AosAttribute.ReflectPhysical] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int EnhancePotions
{
get => this[AosAttribute.EnhancePotions];
set => this[AosAttribute.EnhancePotions] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int Luck
{
get => this[AosAttribute.Luck];
set => this[AosAttribute.Luck] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int SpellChanneling
{
get => this[AosAttribute.SpellChanneling];
set => this[AosAttribute.SpellChanneling] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int NightSight
{
get => this[AosAttribute.NightSight];
set => this[AosAttribute.NightSight] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int IncreasedKarmaLoss
{
get => this[AosAttribute.IncreasedKarmaLoss];
set => this[AosAttribute.IncreasedKarmaLoss] = value;
}
public static int GetValue(Mobile m, AosAttribute attribute)
{
if (!Core.AOS)
{
return 0;
}
var items = m.Items;
var value = 0;
for (var i = 0; i < items.Count; ++i)
{
var obj = items[i];
if (obj is BaseWeapon weapon)
{
var attrs = weapon.Attributes;
if (attrs != null)
{
value += attrs[attribute];
}
if (attribute == AosAttribute.Luck)
{
value += weapon.GetLuckBonus();
}
}
else if (obj is BaseArmor armor)
{
var attrs = armor.Attributes;
if (attrs != null)
{
value += attrs[attribute];
}
if (attribute == AosAttribute.Luck)
{
value += armor.GetLuckBonus();
}
}
else if (obj is BaseJewel jewel)
{
var attrs = jewel.Attributes;
if (attrs != null)
{
value += attrs[attribute];
}
}
else if (obj is BaseClothing clothing)
{
var attrs = clothing.Attributes;
if (attrs != null)
{
value += attrs[attribute];
}
}
else if (obj is Spellbook spellbook)
{
var attrs = spellbook.Attributes;
if (attrs != null)
{
value += attrs[attribute];
}
}
else if (obj is BaseQuiver quiver)
{
var attrs = quiver.Attributes;
if (attrs != null)
{
value += attrs[attribute];
}
}
else if (obj is BaseTalisman talisman)
{
var attrs = talisman.Attributes;
if (attrs != null)
{
value += attrs[attribute];
}
}
}
return value;
}
public override string ToString() => "...";
public void AddStatBonuses(Mobile to)
{
var strBonus = BonusStr;
var dexBonus = BonusDex;
var intBonus = BonusInt;
if (strBonus != 0 || dexBonus != 0 || intBonus != 0)
{
var serial = Owner.Serial;
if (strBonus != 0)
{
to.AddStatMod(new StatMod(StatType.Str, $"{serial}Str", strBonus, TimeSpan.Zero));
}
if (dexBonus != 0)
{
to.AddStatMod(new StatMod(StatType.Dex, $"{serial}Dex", dexBonus, TimeSpan.Zero));
}
if (intBonus != 0)
{
to.AddStatMod(new StatMod(StatType.Int, $"{serial}Int", intBonus, TimeSpan.Zero));
}
}
to.CheckStatTimers();
}
public void RemoveStatBonuses(Mobile from)
{
var serial = Owner.Serial;
from.RemoveStatMod($"{serial}Str");
from.RemoveStatMod($"{serial}Dex");
from.RemoveStatMod($"{serial}Int");
from.CheckStatTimers();
}
}
[Flags]
public enum AosWeaponAttribute
{
LowerStatReq = 0x00000001,
SelfRepair = 0x00000002,
HitLeechHits = 0x00000004,
HitLeechStam = 0x00000008,
HitLeechMana = 0x00000010,
HitLowerAttack = 0x00000020,
HitLowerDefend = 0x00000040,
HitMagicArrow = 0x00000080,
HitHarm = 0x00000100,
HitFireball = 0x00000200,
HitLightning = 0x00000400,
HitDispel = 0x00000800,
HitColdArea = 0x00001000,
HitFireArea = 0x00002000,
HitPoisonArea = 0x00004000,
HitEnergyArea = 0x00008000,
HitPhysicalArea = 0x00010000,
ResistPhysicalBonus = 0x00020000,
ResistFireBonus = 0x00040000,
ResistColdBonus = 0x00080000,
ResistPoisonBonus = 0x00100000,
ResistEnergyBonus = 0x00200000,
UseBestSkill = 0x00400000,
MageWeapon = 0x00800000,
DurabilityBonus = 0x01000000
}
public sealed class AosWeaponAttributes : BaseAttributes
{
public AosWeaponAttributes(Item owner) : base(owner)
{
}
public AosWeaponAttributes(Item owner, AosWeaponAttributes other) : base(owner, other)
{
}
public int this[AosWeaponAttribute attribute]
{
get => GetValue((int)attribute);
set => SetValue((int)attribute, value);
}
[CommandProperty(AccessLevel.GameMaster)]
public int LowerStatReq
{
get => this[AosWeaponAttribute.LowerStatReq];
set => this[AosWeaponAttribute.LowerStatReq] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int SelfRepair
{
get => this[AosWeaponAttribute.SelfRepair];
set => this[AosWeaponAttribute.SelfRepair] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int HitLeechHits
{
get => this[AosWeaponAttribute.HitLeechHits];
set => this[AosWeaponAttribute.HitLeechHits] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int HitLeechStam
{
get => this[AosWeaponAttribute.HitLeechStam];
set => this[AosWeaponAttribute.HitLeechStam] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int HitLeechMana
{
get => this[AosWeaponAttribute.HitLeechMana];
set => this[AosWeaponAttribute.HitLeechMana] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int HitLowerAttack
{
get => this[AosWeaponAttribute.HitLowerAttack];
set => this[AosWeaponAttribute.HitLowerAttack] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int HitLowerDefend
{
get => this[AosWeaponAttribute.HitLowerDefend];
set => this[AosWeaponAttribute.HitLowerDefend] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int HitMagicArrow
{
get => this[AosWeaponAttribute.HitMagicArrow];
set => this[AosWeaponAttribute.HitMagicArrow] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int HitHarm
{
get => this[AosWeaponAttribute.HitHarm];
set => this[AosWeaponAttribute.HitHarm] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int HitFireball
{
get => this[AosWeaponAttribute.HitFireball];
set => this[AosWeaponAttribute.HitFireball] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int HitLightning
{
get => this[AosWeaponAttribute.HitLightning];
set => this[AosWeaponAttribute.HitLightning] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int HitDispel
{
get => this[AosWeaponAttribute.HitDispel];
set => this[AosWeaponAttribute.HitDispel] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int HitColdArea
{
get => this[AosWeaponAttribute.HitColdArea];
set => this[AosWeaponAttribute.HitColdArea] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int HitFireArea
{
get => this[AosWeaponAttribute.HitFireArea];
set => this[AosWeaponAttribute.HitFireArea] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int HitPoisonArea
{
get => this[AosWeaponAttribute.HitPoisonArea];
set => this[AosWeaponAttribute.HitPoisonArea] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int HitEnergyArea
{
get => this[AosWeaponAttribute.HitEnergyArea];
set => this[AosWeaponAttribute.HitEnergyArea] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int HitPhysicalArea
{
get => this[AosWeaponAttribute.HitPhysicalArea];
set => this[AosWeaponAttribute.HitPhysicalArea] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int ResistPhysicalBonus
{
get => this[AosWeaponAttribute.ResistPhysicalBonus];
set => this[AosWeaponAttribute.ResistPhysicalBonus] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int ResistFireBonus
{
get => this[AosWeaponAttribute.ResistFireBonus];
set => this[AosWeaponAttribute.ResistFireBonus] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int ResistColdBonus
{
get => this[AosWeaponAttribute.ResistColdBonus];
set => this[AosWeaponAttribute.ResistColdBonus] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int ResistPoisonBonus
{
get => this[AosWeaponAttribute.ResistPoisonBonus];
set => this[AosWeaponAttribute.ResistPoisonBonus] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int ResistEnergyBonus
{
get => this[AosWeaponAttribute.ResistEnergyBonus];
set => this[AosWeaponAttribute.ResistEnergyBonus] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int UseBestSkill
{
get => this[AosWeaponAttribute.UseBestSkill];
set => this[AosWeaponAttribute.UseBestSkill] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int MageWeapon
{
get => this[AosWeaponAttribute.MageWeapon];
set => this[AosWeaponAttribute.MageWeapon] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int DurabilityBonus
{
get => this[AosWeaponAttribute.DurabilityBonus];
set => this[AosWeaponAttribute.DurabilityBonus] = value;
}
public static int GetValue(Mobile m, AosWeaponAttribute attribute)
{
if (!Core.AOS)
{
return 0;
}
var items = m.Items;
var value = 0;
for (var i = 0; i < items.Count; ++i)
{
var obj = items[i];
if (obj is BaseWeapon weapon)
{
var attrs = weapon.WeaponAttributes;
if (attrs != null)
{
value += attrs[attribute];
}
}
else if (obj is ElvenGlasses glasses)
{
var attrs = glasses._weaponAttributes;
if (attrs != null)
{
value += attrs[attribute];
}
}
}
return value;
}
public override string ToString() => "...";
}
[Flags]
public enum AosArmorAttribute
{
LowerStatReq = 0x00000001,
SelfRepair = 0x00000002,
MageArmor = 0x00000004,
DurabilityBonus = 0x00000008
}
public sealed class AosArmorAttributes : BaseAttributes
{
public AosArmorAttributes(Item owner) : base(owner)
{
}
public AosArmorAttributes(Item owner, AosArmorAttributes other) : base(owner, other)
{
}
public int this[AosArmorAttribute attribute]
{
get => GetValue((int)attribute);
set => SetValue((int)attribute, value);
}
[CommandProperty(AccessLevel.GameMaster)]
public int LowerStatReq
{
get => this[AosArmorAttribute.LowerStatReq];
set => this[AosArmorAttribute.LowerStatReq] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int SelfRepair
{
get => this[AosArmorAttribute.SelfRepair];
set => this[AosArmorAttribute.SelfRepair] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int MageArmor
{
get => this[AosArmorAttribute.MageArmor];
set => this[AosArmorAttribute.MageArmor] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int DurabilityBonus
{
get => this[AosArmorAttribute.DurabilityBonus];
set => this[AosArmorAttribute.DurabilityBonus] = value;
}
public static int GetValue(Mobile m, AosArmorAttribute attribute)
{
if (!Core.AOS)
{
return 0;
}
var items = m.Items;
var value = 0;
for (var i = 0; i < items.Count; ++i)
{
var obj = items[i];
if (obj is BaseArmor armor)
{
var attrs = armor.ArmorAttributes;
if (attrs != null)
{
value += attrs[attribute];
}
}
else if (obj is BaseClothing clothing)
{
var attrs = clothing.ClothingAttributes;
if (attrs != null)
{
value += attrs[attribute];
}
}
}
return value;
}
public override string ToString() => "...";
}
public sealed class AosSkillBonuses : BaseAttributes
{
private HashSet<SkillMod> m_Mods;
public AosSkillBonuses(Item owner) : base(owner)
{
}
public AosSkillBonuses(Item owner, AosSkillBonuses other) : base(owner, other)
{
}
[CommandProperty(AccessLevel.GameMaster)]
public double Skill_1_Value
{
get => GetBonus(0);
set => SetBonus(0, value);
}
[CommandProperty(AccessLevel.GameMaster)]
public SkillName Skill_1_Name
{
get => GetSkill(0);
set => SetSkill(0, value);
}
[CommandProperty(AccessLevel.GameMaster)]
public double Skill_2_Value
{
get => GetBonus(1);
set => SetBonus(1, value);
}
[CommandProperty(AccessLevel.GameMaster)]
public SkillName Skill_2_Name
{
get => GetSkill(1);
set => SetSkill(1, value);
}
[CommandProperty(AccessLevel.GameMaster)]
public double Skill_3_Value
{
get => GetBonus(2);
set => SetBonus(2, value);
}
[CommandProperty(AccessLevel.GameMaster)]
public SkillName Skill_3_Name
{
get => GetSkill(2);
set => SetSkill(2, value);
}
[CommandProperty(AccessLevel.GameMaster)]
public double Skill_4_Value
{
get => GetBonus(3);
set => SetBonus(3, value);
}
[CommandProperty(AccessLevel.GameMaster)]
public SkillName Skill_4_Name
{
get => GetSkill(3);
set => SetSkill(3, value);
}
[CommandProperty(AccessLevel.GameMaster)]
public double Skill_5_Value
{
get => GetBonus(4);
set => SetBonus(4, value);
}
[CommandProperty(AccessLevel.GameMaster)]
public SkillName Skill_5_Name
{
get => GetSkill(4);
set => SetSkill(4, value);
}
public void GetProperties(IPropertyList list)
{
for (var i = 0; i < 5; ++i)
{
if (GetValues(i, out var skill, out var bonus))
{
list.Add(1060451 + i, $"{GetLabel(skill):#}\t{bonus}");
}
}
}
public static int GetLabel(SkillName skill)
{
return skill switch
{
SkillName.EvalInt => 1002070, // Evaluate Intelligence
SkillName.Forensics => 1002078, // Forensic Evaluation
SkillName.Lockpicking => 1002097, // Lockpicking
_ => 1044060 + (int)skill
};
}
public void AddTo(Mobile m)
{
Remove();
for (var i = 0; i < 5; ++i)
{
if (!GetValues(i, out var skill, out var bonus))
{
continue;
}
m_Mods ??= new HashSet<SkillMod>();
SkillMod sk = new DefaultSkillMod(skill, true, bonus);
sk.ObeyCap = true;
m.AddSkillMod(sk);
m_Mods.Add(sk);
}
}
public void Remove()
{
if (m_Mods == null)
{
return;
}
foreach (var mod in m_Mods)
{
var m = mod.Owner;
mod.Remove();
if (Core.ML)
{
CheckCancelMorph(m);
}
}
m_Mods = null;
}
public bool GetValues(int index, out SkillName skill, out double bonus)
{
var v = GetValue(1 << index);
var vSkill = 0;
var vBonus = 0;
for (var i = 0; i < 16; ++i)
{
vSkill <<= 1;
vSkill |= v & 1;
v >>= 1;
vBonus <<= 1;
vBonus |= v & 1;
v >>= 1;
}
skill = (SkillName)vSkill;
bonus = (double)vBonus / 10;
return bonus != 0;
}
public void SetValues(int index, SkillName skill, double bonus)
{
var v = 0;
var vSkill = (int)skill;
var vBonus = (int)(bonus * 10);
for (var i = 0; i < 16; ++i)
{
v <<= 1;
v |= vBonus & 1;
vBonus >>= 1;
v <<= 1;
v |= vSkill & 1;
vSkill >>= 1;
}
SetValue(1 << index, v);
}
public SkillName GetSkill(int index)
{
GetValues(index, out var skill, out var _);
return skill;
}
public void SetSkill(int index, SkillName skill)
{
SetValues(index, skill, GetBonus(index));
}
public double GetBonus(int index)
{
GetValues(index, out var _, out var bonus);
return bonus;
}
public void SetBonus(int index, double bonus)
{
SetValues(index, GetSkill(index), bonus);
}
public override string ToString() => "...";
public void CheckCancelMorph(Mobile m)
{
if (m == null)
{
return;
}
var acontext = AnimalForm.GetContext(m);
var context = TransformationSpellHelper.GetContext(m);
if (context?.Spell is Spell spell)
{
spell.GetCastSkills(out var minSkill, out _);
if (m.Skills[spell.CastSkill].Value < minSkill)
{
TransformationSpellHelper.RemoveContext(m, context, true);
}
}
if (acontext != null)
{
int i;
for (i = 0; i < AnimalForm.Entries.Length; ++i)
{
if (AnimalForm.Entries[i].Type == acontext.Type)
{
break;
}
}
if (m.Skills.Ninjitsu.Value < AnimalForm.Entries[i].ReqSkill)
{
AnimalForm.RemoveContext(m, true);
}
}
if (m.Skills.Magery.Value < 66.1)
{
PolymorphSpell.EndPolymorph(m);
}
if (m.Skills.Magery.Value < 38.1)
{
IncognitoSpell.EndIncognito(m);
}
}
}
[Flags]
public enum AosElementAttribute
{
Physical = 0x00000001,
Fire = 0x00000002,
Cold = 0x00000004,
Poison = 0x00000008,
Energy = 0x00000010,
Chaos = 0x00000020,
Direct = 0x00000040
}
public sealed class AosElementAttributes : BaseAttributes
{
public AosElementAttributes(Item owner) : base(owner)
{
}
public AosElementAttributes(Item owner, AosElementAttributes other) : base(owner, other)
{
}
public int this[AosElementAttribute attribute]
{
get => GetValue((int)attribute);
set => SetValue((int)attribute, value);
}
[CommandProperty(AccessLevel.GameMaster)]
public int Physical
{
get => this[AosElementAttribute.Physical];
set => this[AosElementAttribute.Physical] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int Fire
{
get => this[AosElementAttribute.Fire];
set => this[AosElementAttribute.Fire] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int Cold
{
get => this[AosElementAttribute.Cold];
set => this[AosElementAttribute.Cold] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int Poison
{
get => this[AosElementAttribute.Poison];
set => this[AosElementAttribute.Poison] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int Energy
{
get => this[AosElementAttribute.Energy];
set => this[AosElementAttribute.Energy] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int Chaos
{
get => this[AosElementAttribute.Chaos];
set => this[AosElementAttribute.Chaos] = value;
}
[CommandProperty(AccessLevel.GameMaster)]
public int Direct
{
get => this[AosElementAttribute.Direct];
set => this[AosElementAttribute.Direct] = value;
}
public override string ToString() => "...";
}
[PropertyObject]
[SerializationGenerator(0)]
public abstract partial class BaseAttributes
{
[SerializableField(0, setter: "private")]
private uint _names;
[EncodedInt]
[SerializableField(1, setter: "private")]
private int[] _values;
public BaseAttributes(Item owner)
{
_owner = owner;
_values = Array.Empty<int>();
}
public BaseAttributes(Item owner, BaseAttributes other)
{
_owner = owner;
_values = new int[other._values.Length];
other._values.CopyTo(_values, 0);
_names = other._names;
}
public bool IsEmpty => _names == 0;
private Item _owner;
[DirtyTrackingEntity]
public Item Owner => _owner;
public int GetValue(int bitmask)
{
if (!Core.AOS)
{
return 0;
}
var mask = (uint)bitmask;
if ((_names & mask) == 0)
{
return 0;
}
var index = GetIndex(mask);
if (index >= 0 && index < _values.Length)
{
return _values[index];
}
return 0;
}
public void SetValue(int bitmask, int value)
{
if (bitmask == (int)AosWeaponAttribute.DurabilityBonus && this is AosWeaponAttributes)
{
if (Owner is BaseWeapon weapon)
{
weapon.UnscaleDurability();
}
}
else if (bitmask == (int)AosArmorAttribute.DurabilityBonus && this is AosArmorAttributes)
{
if (Owner is BaseArmor armor)
{
armor.UnscaleDurability();
}
else if (Owner is BaseClothing clothing)
{
clothing.UnscaleDurability();
}
}
var mask = (uint)bitmask;
if (value != 0)
{
if ((_names & mask) != 0)
{
var index = GetIndex(mask);
if (index >= 0 && index < _values.Length)
{
_values[index] = value;
}
}
else
{
var index = GetIndex(mask);
if (index >= 0 && index <= _values.Length)
{
var old = _values;
_values = new int[old.Length + 1];
for (var i = 0; i < index; ++i)
{
_values[i] = old[i];
}
_values[index] = value;
for (var i = index; i < old.Length; ++i)
{
_values[i + 1] = old[i];
}
_names |= mask;
}
}
}
else if ((_names & mask) != 0)
{
var index = GetIndex(mask);
if (index >= 0 && index < _values.Length)
{
_names &= ~mask;
if (_values.Length == 1)
{
_values = Array.Empty<int>();
}
else
{
var old = _values;
_values = new int[old.Length - 1];
for (var i = 0; i < index; ++i)
{
_values[i] = old[i];
}
for (var i = index + 1; i < old.Length; ++i)
{
_values[i - 1] = old[i];
}
}
}
}
if (bitmask == (int)AosWeaponAttribute.DurabilityBonus && this is AosWeaponAttributes)
{
if (Owner is BaseWeapon weapon)
{
weapon.ScaleDurability();
}
}
else if (bitmask == (int)AosArmorAttribute.DurabilityBonus && this is AosArmorAttributes)
{
if (Owner is BaseArmor armor)
{
armor.ScaleDurability();
}
else if (Owner is BaseClothing clothing)
{
clothing.ScaleDurability();
}
}
if (Owner?.Parent is Mobile m)
{
m.CheckStatTimers();
m.UpdateResistances();
m.Delta(
MobileDelta.Stat | MobileDelta.WeaponDamage | MobileDelta.Hits | MobileDelta.Stam |
MobileDelta.Mana
);
if (this is AosSkillBonuses)
{
((AosSkillBonuses)this).Remove();
((AosSkillBonuses)this).AddTo(m);
}
}
Owner?.InvalidateProperties();
}
private int GetIndex(uint mask)
{
var index = 0;
var ourNames = _names;
uint currentBit = 1;
while (currentBit != mask)
{
if ((ourNames & currentBit) != 0)
{
++index;
}
if (currentBit == 0x80000000)
{
return -1;
}
currentBit <<= 1;
}
return index;
}
}
}