1943 lines
56 KiB
C#
1943 lines
56 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<BaseQuiver>(Layer.Cloak);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
var bcFrom = from as BaseCreature;
|
|
|
|
if (from is { Deleted: false, Alive: true })
|
|
{
|
|
var reflectPhys = AosAttributes.GetValue(m, AosAttribute.ReflectPhysical);
|
|
var reflectPhysAbility = bcFrom
|
|
?.GetAbility(MonsterAbilityType.ReflectPhysicalDamage) as ReflectPhysicalDamage;
|
|
|
|
if (reflectPhysAbility?.CanTrigger(bcFrom, MonsterAbilityTrigger.CombatAction) == true)
|
|
{
|
|
reflectPhys += reflectPhysAbility.PercentReflected;
|
|
m.SendLocalizedMessage(1070844); // The creature repels the attack back at you.
|
|
}
|
|
|
|
if (reflectPhys > 0)
|
|
{
|
|
var reflectDamage = Scale(
|
|
damage * phys * (100 - (ignoreArmor ? 0 : m.PhysicalResistance)) / 10000,
|
|
reflectPhys
|
|
);
|
|
|
|
bcFrom
|
|
?.GetAbility(MonsterAbilityType.MagicalBarrier)
|
|
?.AlterMeleeDamageFrom(bcFrom, m, ref reflectDamage);
|
|
|
|
if (reflectDamage > 0)
|
|
{
|
|
from.Damage(reflectDamage, m);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (totalDamage <= 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (from != null) // sanity check
|
|
{
|
|
SpellHelper.DoLeech(totalDamage, from, m);
|
|
}
|
|
|
|
var oldHits = m.Hits;
|
|
m.Damage(totalDamage, from);
|
|
BattleLust.OnDamageTaken(m, from, Math.Max(0, oldHits - m.Hits));
|
|
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 interface IAosItem
|
|
{
|
|
public AosAttributes Attributes { get; }
|
|
}
|
|
|
|
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 void GetProperties(IPropertyList list, int damageBonus = 0, int hitChanceBonus = 0, int luckBonus = 0)
|
|
{
|
|
int prop;
|
|
|
|
if ((prop = WeaponDamage + damageBonus) != 0)
|
|
{
|
|
list.Add(1060401, prop); // damage increase ~1_val~%
|
|
}
|
|
|
|
if ((prop = DefendChance) != 0)
|
|
{
|
|
list.Add(1060408, prop); // defense chance increase ~1_val~%
|
|
}
|
|
|
|
if ((prop = BonusDex) != 0)
|
|
{
|
|
list.Add(1060409, prop); // dexterity bonus ~1_val~
|
|
}
|
|
|
|
if ((prop = EnhancePotions) != 0)
|
|
{
|
|
list.Add(1060411, prop); // enhance potions ~1_val~%
|
|
}
|
|
|
|
if ((prop = CastRecovery) != 0)
|
|
{
|
|
list.Add(1060412, prop); // faster cast recovery ~1_val~
|
|
}
|
|
|
|
if ((prop = CastSpeed) != 0)
|
|
{
|
|
list.Add(1060413, prop); // faster casting ~1_val~
|
|
}
|
|
|
|
if ((prop = AttackChance + hitChanceBonus) != 0)
|
|
{
|
|
list.Add(1060415, prop); // hit chance increase ~1_val~%
|
|
}
|
|
|
|
if ((prop = BonusHits) != 0)
|
|
{
|
|
list.Add(1060431, prop); // hit point increase ~1_val~
|
|
}
|
|
|
|
if ((prop = BonusInt) != 0)
|
|
{
|
|
list.Add(1060432, prop); // intelligence bonus ~1_val~
|
|
}
|
|
|
|
if ((prop = LowerManaCost) != 0)
|
|
{
|
|
list.Add(1060433, prop); // lower mana cost ~1_val~%
|
|
}
|
|
|
|
if ((prop = LowerRegCost) != 0)
|
|
{
|
|
list.Add(1060434, prop); // lower reagent cost ~1_val~%
|
|
}
|
|
|
|
if ((prop = Luck + luckBonus) != 0)
|
|
{
|
|
list.Add(1060436, prop); // luck ~1_val~
|
|
}
|
|
|
|
if ((prop = BonusMana) != 0)
|
|
{
|
|
list.Add(1060439, prop); // mana increase ~1_val~
|
|
}
|
|
|
|
if ((prop = RegenMana) != 0)
|
|
{
|
|
list.Add(1060440, prop); // mana regeneration ~1_val~
|
|
}
|
|
|
|
if (NightSight != 0)
|
|
{
|
|
list.Add(1060441); // night sight
|
|
}
|
|
|
|
if ((prop = ReflectPhysical) != 0)
|
|
{
|
|
list.Add(1060442, prop); // reflect physical damage ~1_val~%
|
|
}
|
|
|
|
if ((prop = RegenStam) != 0)
|
|
{
|
|
list.Add(1060443, prop); // stamina regeneration ~1_val~
|
|
}
|
|
|
|
if ((prop = RegenHits) != 0)
|
|
{
|
|
list.Add(1060444, prop); // hit point regeneration ~1_val~
|
|
}
|
|
|
|
if (SpellChanneling != 0)
|
|
{
|
|
list.Add(1060482); // spell channeling
|
|
}
|
|
|
|
if ((prop = SpellDamage) != 0)
|
|
{
|
|
list.Add(1060483, prop); // spell damage increase ~1_val~%
|
|
}
|
|
|
|
if ((prop = BonusStam) != 0)
|
|
{
|
|
list.Add(1060484, prop); // stamina increase ~1_val~
|
|
}
|
|
|
|
if ((prop = BonusStr) != 0)
|
|
{
|
|
list.Add(1060485, prop); // strength bonus ~1_val~
|
|
}
|
|
|
|
if ((prop = WeaponSpeed) != 0)
|
|
{
|
|
list.Add(1060486, prop); // swing speed increase ~1_val~%
|
|
}
|
|
|
|
if (Core.ML && (prop = IncreasedKarmaLoss) != 0)
|
|
{
|
|
list.Add(1075210, prop); // Increased Karma Loss ~1val~%
|
|
}
|
|
}
|
|
|
|
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 hashCode = GetHashCode();
|
|
if (strBonus != 0)
|
|
{
|
|
to.AddStatMod(new StatMod(StatType.Str, $"{hashCode}Str", strBonus, TimeSpan.Zero));
|
|
}
|
|
|
|
if (dexBonus != 0)
|
|
{
|
|
to.AddStatMod(new StatMod(StatType.Dex, $"{hashCode}Dex", dexBonus, TimeSpan.Zero));
|
|
}
|
|
|
|
if (intBonus != 0)
|
|
{
|
|
to.AddStatMod(new StatMod(StatType.Int, $"{hashCode}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 void GetProperties(IPropertyList list)
|
|
{
|
|
int prop;
|
|
|
|
if (UseBestSkill != 0)
|
|
{
|
|
list.Add(1060400); // use best weapon skill
|
|
}
|
|
|
|
if ((prop = HitColdArea) != 0)
|
|
{
|
|
list.Add(1060416, prop); // hit cold area ~1_val~%
|
|
}
|
|
|
|
if ((prop = HitDispel) != 0)
|
|
{
|
|
list.Add(1060417, prop); // hit dispel ~1_val~%
|
|
}
|
|
|
|
if ((prop = HitEnergyArea) != 0)
|
|
{
|
|
list.Add(1060418, prop); // hit energy area ~1_val~%
|
|
}
|
|
|
|
if ((prop = HitFireArea) != 0)
|
|
{
|
|
list.Add(1060419, prop); // hit fire area ~1_val~%
|
|
}
|
|
|
|
if ((prop = HitFireball) != 0)
|
|
{
|
|
list.Add(1060420, prop); // hit fireball ~1_val~%
|
|
}
|
|
|
|
if ((prop = HitHarm) != 0)
|
|
{
|
|
list.Add(1060421, prop); // hit harm ~1_val~%
|
|
}
|
|
|
|
if ((prop = HitLeechHits) != 0)
|
|
{
|
|
list.Add(1060422, prop); // hit life leech ~1_val~%
|
|
}
|
|
|
|
if ((prop = HitLightning) != 0)
|
|
{
|
|
list.Add(1060423, prop); // hit lightning ~1_val~%
|
|
}
|
|
|
|
if ((prop = HitLowerAttack) != 0)
|
|
{
|
|
list.Add(1060424, prop); // hit lower attack ~1_val~%
|
|
}
|
|
|
|
if ((prop = HitLowerDefend) != 0)
|
|
{
|
|
list.Add(1060425, prop); // hit lower defense ~1_val~%
|
|
}
|
|
|
|
if ((prop = HitMagicArrow) != 0)
|
|
{
|
|
list.Add(1060426, prop); // hit magic arrow ~1_val~%
|
|
}
|
|
|
|
if ((prop = HitLeechMana) != 0)
|
|
{
|
|
list.Add(1060427, prop); // hit mana leech ~1_val~%
|
|
}
|
|
|
|
if ((prop = HitPhysicalArea) != 0)
|
|
{
|
|
list.Add(1060428, prop); // hit physical area ~1_val~%
|
|
}
|
|
|
|
if ((prop = HitPoisonArea) != 0)
|
|
{
|
|
list.Add(1060429, prop); // hit poison area ~1_val~%
|
|
}
|
|
|
|
if ((prop = HitLeechStam) != 0)
|
|
{
|
|
list.Add(1060430, prop); // hit stamina leech ~1_val~%
|
|
}
|
|
|
|
if ((prop = MageWeapon) != 0)
|
|
{
|
|
list.Add(1060438, 30 - prop); // mage weapon -~1_val~ skill
|
|
}
|
|
|
|
if ((prop = SelfRepair) != 0)
|
|
{
|
|
list.Add(1060450, prop); // self repair ~1_val~
|
|
}
|
|
}
|
|
|
|
public override string ToString() => "...";
|
|
}
|
|
|
|
[Flags]
|
|
public enum ExtendedWeaponAttribute
|
|
{
|
|
Bane = 0x00000001,
|
|
BattleLust = 0x00000002,
|
|
HitSparks = 0x00000004,
|
|
BloodDrinker = 0x00000008
|
|
}
|
|
|
|
public sealed class ExtendedWeaponAttributes : BaseAttributes
|
|
{
|
|
public ExtendedWeaponAttributes(Item owner) : base(owner)
|
|
{
|
|
}
|
|
|
|
public ExtendedWeaponAttributes(Item owner, ExtendedWeaponAttributes other) : base(owner, other)
|
|
{
|
|
}
|
|
|
|
public int this[ExtendedWeaponAttribute attribute]
|
|
{
|
|
get => GetValue((int)attribute);
|
|
set => SetValue((int)attribute, value);
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int Bane
|
|
{
|
|
get => this[ExtendedWeaponAttribute.Bane];
|
|
set => this[ExtendedWeaponAttribute.Bane] = value;
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int BattleLust
|
|
{
|
|
get => this[ExtendedWeaponAttribute.BattleLust];
|
|
set
|
|
{
|
|
var hadBattleLust = BattleLust != 0;
|
|
|
|
this[ExtendedWeaponAttribute.BattleLust] = value;
|
|
|
|
if (hadBattleLust && value == 0 && Owner is BaseWeapon { Parent: Mobile m })
|
|
{
|
|
Server.Items.BattleLust.Clear(m);
|
|
}
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int HitSparks
|
|
{
|
|
get => this[ExtendedWeaponAttribute.HitSparks];
|
|
set => this[ExtendedWeaponAttribute.HitSparks] = value;
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int BloodDrinker
|
|
{
|
|
get => this[ExtendedWeaponAttribute.BloodDrinker];
|
|
set => this[ExtendedWeaponAttribute.BloodDrinker] = value;
|
|
}
|
|
|
|
public void GetProperties(IPropertyList list)
|
|
{
|
|
if (Core.HS && Bane != 0)
|
|
{
|
|
list.Add(1154671); // Bane
|
|
}
|
|
|
|
if (Core.SA && BattleLust != 0)
|
|
{
|
|
list.Add(1113710); // Battle Lust
|
|
}
|
|
|
|
if (Core.SA && BloodDrinker != 0)
|
|
{
|
|
list.Add(1113591); // Blood Drinker
|
|
}
|
|
|
|
if (Core.TOL && HitSparks != 0)
|
|
{
|
|
list.Add(1157326, HitSparks); // Sparks ~1_val~%
|
|
}
|
|
}
|
|
|
|
public override string ToString() => "...";
|
|
}
|
|
|
|
[Flags]
|
|
public enum NegativeAttribute
|
|
{
|
|
Prized = 0x00000001
|
|
}
|
|
|
|
public sealed class NegativeAttributes : BaseAttributes
|
|
{
|
|
public NegativeAttributes(Item owner) : base(owner)
|
|
{
|
|
}
|
|
|
|
public NegativeAttributes(Item owner, NegativeAttributes other) : base(owner, other)
|
|
{
|
|
}
|
|
|
|
public int this[NegativeAttribute attribute]
|
|
{
|
|
get => GetValue((int)attribute);
|
|
set => SetValue((int)attribute, value);
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int Prized
|
|
{
|
|
get => this[NegativeAttribute.Prized];
|
|
set => this[NegativeAttribute.Prized] = value;
|
|
}
|
|
|
|
public static bool IsPrized(Item item)
|
|
{
|
|
if (!Core.HS)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return item switch
|
|
{
|
|
BaseWeapon weapon => weapon.NegativeAttributes.Prized != 0,
|
|
BaseArmor armor => armor.NegativeAttributes.Prized != 0,
|
|
BaseJewel jewel => jewel.NegativeAttributes.Prized != 0,
|
|
_ => false
|
|
};
|
|
}
|
|
|
|
public void GetProperties(IPropertyList list)
|
|
{
|
|
if (Core.HS && Prized != 0)
|
|
{
|
|
list.Add(1154910); // Prized
|
|
}
|
|
}
|
|
|
|
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 void GetProperties(IPropertyList list)
|
|
{
|
|
int prop;
|
|
|
|
if (MageArmor != 0)
|
|
{
|
|
list.Add(1060437); // mage armor
|
|
}
|
|
|
|
if ((prop = SelfRepair) != 0)
|
|
{
|
|
list.Add(1060450, prop); // self repair ~1_val~
|
|
}
|
|
}
|
|
|
|
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 static int GetLowercaseLabel(SkillName skill)
|
|
{
|
|
return skill switch
|
|
{
|
|
SkillName.Provocation => 1049473, // provocation
|
|
SkillName.MagicResist => 1049471, // resisting spells
|
|
SkillName.AnimalTaming => 1049472, // animal taming
|
|
SkillName.Macing => 1049470, // mace fighting
|
|
SkillName.Necromancy => 1060842, // necromancy
|
|
SkillName.Focus => 1061613, // focus
|
|
SkillName.Chivalry => 1061615, // chivalry
|
|
SkillName.Bushido => 1062935, // bushido
|
|
SkillName.Ninjitsu => 1062936, // ninjitsu
|
|
SkillName.Spellweaving => 1074397, // spellweaving
|
|
SkillName.Mysticism => 1112544, // mysticism
|
|
SkillName.Imbuing => 1112545, // imbuing
|
|
SkillName.Throwing => 1112553, // throwing
|
|
_ => 1042347 + (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, $"{GetHashCode()}{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 static 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);
|
|
}
|
|
}
|
|
|
|
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 IEntity _owner;
|
|
|
|
[DirtyTrackingEntity]
|
|
public IEntity 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 is Item item)
|
|
{
|
|
if (item.Parent is Mobile m)
|
|
{
|
|
m.CheckStatTimers();
|
|
m.UpdateResistances();
|
|
m.Delta(
|
|
MobileDelta.Stat | MobileDelta.WeaponDamage | MobileDelta.Hits | MobileDelta.Stam |
|
|
MobileDelta.Mana
|
|
);
|
|
|
|
if (this is AosSkillBonuses skillBonuses)
|
|
{
|
|
skillBonuses.Remove();
|
|
skillBonuses.AddTo(m);
|
|
}
|
|
}
|
|
|
|
item.InvalidateProperties();
|
|
}
|
|
else if (Owner is Mobile mob)
|
|
{
|
|
mob.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;
|
|
}
|
|
}
|
|
}
|