1054 lines
32 KiB
C#
1054 lines
32 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using ModernUO.Serialization;
|
|
using Server.Engines.Craft;
|
|
using Server.Ethics;
|
|
using Server.Factions;
|
|
using Server.Network;
|
|
|
|
namespace Server.Items
|
|
{
|
|
public enum ClothingQuality
|
|
{
|
|
Low,
|
|
Regular,
|
|
Exceptional
|
|
}
|
|
|
|
public interface IArcaneEquip
|
|
{
|
|
bool IsArcane { get; }
|
|
int CurArcaneCharges { get; set; }
|
|
int MaxArcaneCharges { get; set; }
|
|
}
|
|
|
|
[SerializationGenerator(7, false)]
|
|
public abstract partial class BaseClothing
|
|
: Item, IDyable, IScissorable, IFactionItem, ICraftable, IWearableDurability, IAosItem
|
|
{
|
|
[SerializableFieldSaveFlag(0)]
|
|
private bool ShouldSerializeResource() => _resource != DefaultResource;
|
|
|
|
[SerializedIgnoreDupe]
|
|
[SerializableField(1, setter: "private")]
|
|
[SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)]
|
|
private AosAttributes _attributes;
|
|
|
|
[SerializableFieldSaveFlag(1)]
|
|
private bool ShouldSerializeAttributes() => !_attributes.IsEmpty;
|
|
|
|
[SerializableFieldDefault(1)]
|
|
private AosAttributes AttributesDefaultValue() => new(this);
|
|
|
|
[SerializedIgnoreDupe]
|
|
[SerializableField(2, setter: "private")]
|
|
[SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)]
|
|
private AosArmorAttributes _clothingAttributes;
|
|
|
|
[SerializableFieldSaveFlag(2)]
|
|
private bool ShouldSerializeClothingAttributes() => !_clothingAttributes.IsEmpty;
|
|
|
|
[SerializableFieldDefault(2)]
|
|
private AosArmorAttributes ClothingAttributesDefaultValue() => new(this);
|
|
|
|
[SerializedIgnoreDupe]
|
|
[SerializableField(3, setter: "private")]
|
|
[SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)]
|
|
private AosSkillBonuses _skillBonuses;
|
|
|
|
[SerializableFieldSaveFlag(3)]
|
|
private bool ShouldSerializeSkillBonuses() => !_skillBonuses.IsEmpty;
|
|
|
|
[SerializableFieldDefault(3)]
|
|
private AosSkillBonuses SkillBonusesDefaultValue() => new(this);
|
|
|
|
[SerializedIgnoreDupe]
|
|
[SerializableField(4, setter: "private")]
|
|
[SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)]
|
|
private AosElementAttributes _resistances;
|
|
|
|
[SerializableFieldSaveFlag(4)]
|
|
private bool ShouldSerializeResistances() => !_resistances.IsEmpty;
|
|
|
|
[SerializableFieldDefault(4)]
|
|
private AosElementAttributes ResistancesDefaultValue() => new(this);
|
|
|
|
[EncodedInt]
|
|
[InvalidateProperties]
|
|
[SerializableField(5)]
|
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
|
private int _maxHitPoints;
|
|
|
|
[SerializableFieldSaveFlag(5)]
|
|
private bool ShouldSerializeMaxHitPoints() => _maxHitPoints != 0;
|
|
|
|
[SerializableField(7)]
|
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
|
private bool _playerConstructed;
|
|
|
|
[SerializableFieldSaveFlag(7)]
|
|
private bool ShouldSerializePlayerConstructed() => _playerConstructed;
|
|
|
|
[InvalidateProperties]
|
|
[SerializableField(8)]
|
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
|
private string _crafter;
|
|
|
|
[SerializableFieldSaveFlag(8)]
|
|
private bool ShouldSerializeCrafter() => !string.IsNullOrEmpty(_crafter);
|
|
|
|
[InvalidateProperties]
|
|
[SerializableField(9)]
|
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
|
private ClothingQuality _quality = ClothingQuality.Regular;
|
|
|
|
[SerializableFieldSaveFlag(9)]
|
|
private bool ShouldSerializeQuality() => _quality != ClothingQuality.Regular;
|
|
|
|
// Field 10
|
|
private int _strReq = -1;
|
|
|
|
private FactionItem _factionState;
|
|
|
|
public BaseClothing(int itemID, Layer layer, int hue = 0) : base(itemID)
|
|
{
|
|
Layer = layer;
|
|
Hue = hue;
|
|
|
|
_resource = DefaultResource;
|
|
|
|
_hitPoints = _maxHitPoints = Utility.RandomMinMax(InitMinHits, InitMaxHits);
|
|
|
|
Attributes = new AosAttributes(this);
|
|
ClothingAttributes = new AosArmorAttributes(this);
|
|
SkillBonuses = new AosSkillBonuses(this);
|
|
Resistances = new AosElementAttributes(this);
|
|
}
|
|
|
|
[SerializableProperty(0)]
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public CraftResource Resource
|
|
{
|
|
get => _resource;
|
|
set
|
|
{
|
|
_resource = value;
|
|
Hue = CraftResources.GetHue(_resource);
|
|
InvalidateProperties();
|
|
this.MarkDirty();
|
|
}
|
|
}
|
|
|
|
[SerializableProperty(10, useField: nameof(_strReq))]
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int StrRequirement
|
|
{
|
|
get => _strReq == -1 ? Core.AOS ? AosStrReq : OldStrReq : _strReq;
|
|
set
|
|
{
|
|
_strReq = value;
|
|
InvalidateProperties();
|
|
this.MarkDirty();
|
|
}
|
|
}
|
|
|
|
[SerializableFieldSaveFlag(10)]
|
|
private bool ShouldSerializeStrReq() => _strReq != -1;
|
|
|
|
public virtual CraftResource DefaultResource => CraftResource.None;
|
|
|
|
public virtual int BasePhysicalResistance => 0;
|
|
public virtual int BaseFireResistance => 0;
|
|
public virtual int BaseColdResistance => 0;
|
|
public virtual int BasePoisonResistance => 0;
|
|
public virtual int BaseEnergyResistance => 0;
|
|
|
|
public override int PhysicalResistance => BasePhysicalResistance + Resistances.Physical;
|
|
public override int FireResistance => BaseFireResistance + Resistances.Fire;
|
|
public override int ColdResistance => BaseColdResistance + Resistances.Cold;
|
|
public override int PoisonResistance => BasePoisonResistance + Resistances.Poison;
|
|
public override int EnergyResistance => BaseEnergyResistance + Resistances.Energy;
|
|
|
|
public virtual int ArtifactRarity => 0;
|
|
|
|
public virtual int BaseStrBonus => 0;
|
|
public virtual int BaseDexBonus => 0;
|
|
public virtual int BaseIntBonus => 0;
|
|
|
|
public virtual Race RequiredRace => null;
|
|
|
|
public virtual int AosStrReq => 10;
|
|
public virtual int OldStrReq => 0;
|
|
|
|
public virtual bool AllowMaleWearer => true;
|
|
public virtual bool AllowFemaleWearer => true;
|
|
public virtual bool CanBeBlessed => true;
|
|
|
|
public virtual int OnCraft(
|
|
int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes,
|
|
BaseTool tool, CraftItem craftItem, int resHue
|
|
)
|
|
{
|
|
Quality = (ClothingQuality)quality;
|
|
|
|
if (makersMark)
|
|
{
|
|
Crafter = from.RawName;
|
|
}
|
|
|
|
if (DefaultResource != CraftResource.None)
|
|
{
|
|
var resourceType = typeRes ?? craftItem.Resources[0].ItemType;
|
|
|
|
Resource = CraftResources.GetFromType(resourceType);
|
|
}
|
|
else
|
|
{
|
|
Hue = resHue;
|
|
}
|
|
|
|
PlayerConstructed = true;
|
|
|
|
var context = craftSystem.GetContext(from);
|
|
|
|
if (context?.DoNotColor == true)
|
|
{
|
|
Hue = 0;
|
|
}
|
|
|
|
return quality;
|
|
}
|
|
|
|
public virtual bool Dye(Mobile from, DyeTub sender)
|
|
{
|
|
if (Deleted)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (RootParent is Mobile && from != RootParent)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Hue = sender.DyedHue;
|
|
|
|
return true;
|
|
}
|
|
|
|
public FactionItem FactionItemState
|
|
{
|
|
get => _factionState;
|
|
set
|
|
{
|
|
_factionState = value;
|
|
|
|
if (_factionState == null)
|
|
{
|
|
Hue = 0;
|
|
}
|
|
|
|
LootType = _factionState == null ? LootType.Regular : LootType.Blessed;
|
|
}
|
|
}
|
|
|
|
public virtual bool Scissor(Mobile from, Scissors scissors)
|
|
{
|
|
if (!IsChildOf(from.Backpack))
|
|
{
|
|
from.SendLocalizedMessage(502437); // Items you wish to cut must be in your backpack.
|
|
return false;
|
|
}
|
|
|
|
if (Ethic.IsImbued(this))
|
|
{
|
|
from.SendLocalizedMessage(502440); // Scissors can not be used on that to produce anything.
|
|
return false;
|
|
}
|
|
|
|
var system = DefTailoring.CraftSystem;
|
|
|
|
var item = system.CraftItems.SearchFor(GetType());
|
|
|
|
if (item?.Resources.Count == 1)
|
|
{
|
|
var resource = item.Resources[0];
|
|
if (resource.Amount >= 2)
|
|
{
|
|
try
|
|
{
|
|
var info = CraftResources.GetInfo(_resource);
|
|
|
|
Type resourceType = null;
|
|
if (info?.ResourceTypes.Length > 0)
|
|
{
|
|
resourceType = info.ResourceTypes[0];
|
|
}
|
|
|
|
var res = (resourceType ?? resource.ItemType).CreateInstance<Item>();
|
|
|
|
ScissorHelper(from, res, PlayerConstructed ? resource.Amount / 2 : 1);
|
|
|
|
res.LootType = LootType.Regular;
|
|
|
|
return true;
|
|
}
|
|
catch
|
|
{
|
|
// ignored
|
|
}
|
|
}
|
|
}
|
|
|
|
from.SendLocalizedMessage(502440); // Scissors can not be used on that to produce anything.
|
|
return false;
|
|
}
|
|
|
|
public virtual bool CanFortify => true;
|
|
|
|
[EncodedInt]
|
|
[SerializableProperty(6)]
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int HitPoints
|
|
{
|
|
get => _hitPoints;
|
|
set
|
|
{
|
|
if (value != _hitPoints && MaxHitPoints > 0)
|
|
{
|
|
_hitPoints = value;
|
|
|
|
if (_hitPoints < 0)
|
|
{
|
|
Delete();
|
|
}
|
|
else if (_hitPoints > MaxHitPoints)
|
|
{
|
|
_hitPoints = MaxHitPoints;
|
|
}
|
|
|
|
InvalidateProperties();
|
|
this.MarkDirty();
|
|
}
|
|
}
|
|
}
|
|
|
|
[SerializableFieldSaveFlag(6)]
|
|
private bool ShouldSerializeHitPoints() => _hitPoints != 0;
|
|
|
|
public virtual int InitMinHits => 0;
|
|
public virtual int InitMaxHits => 0;
|
|
|
|
public virtual int OnHit(BaseWeapon weapon, int damageTaken)
|
|
{
|
|
var absorbed = Utility.RandomMinMax(1, 4);
|
|
|
|
// Don't go below zero
|
|
damageTaken = Math.Min(absorbed, damageTaken);
|
|
|
|
if (Utility.Random(100) < 25) // 25% chance to lower durability
|
|
{
|
|
if (Core.AOS && ClothingAttributes.SelfRepair > Utility.Random(10))
|
|
{
|
|
HitPoints += 2;
|
|
}
|
|
else
|
|
{
|
|
int wear;
|
|
|
|
if (weapon.Type == WeaponType.Bashing)
|
|
{
|
|
wear = absorbed / 2;
|
|
}
|
|
else
|
|
{
|
|
wear = Utility.Random(2);
|
|
}
|
|
|
|
if (wear > 0 && _maxHitPoints > 0)
|
|
{
|
|
if (_hitPoints >= wear)
|
|
{
|
|
HitPoints -= wear;
|
|
wear = 0;
|
|
}
|
|
else
|
|
{
|
|
wear -= HitPoints;
|
|
HitPoints = 0;
|
|
}
|
|
|
|
if (wear > 0)
|
|
{
|
|
if (_maxHitPoints > wear)
|
|
{
|
|
MaxHitPoints -= wear;
|
|
|
|
(Parent as Mobile)?.LocalOverheadMessage(
|
|
MessageType.Regular,
|
|
0x3B2,
|
|
1061121
|
|
); // Your equipment is severely damaged.
|
|
}
|
|
else
|
|
{
|
|
Delete();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return damageTaken;
|
|
}
|
|
|
|
public void UnscaleDurability()
|
|
{
|
|
var scale = 100 + ClothingAttributes.DurabilityBonus;
|
|
|
|
_hitPoints = (_hitPoints * 100 + (scale - 1)) / scale;
|
|
_maxHitPoints = (_maxHitPoints * 100 + (scale - 1)) / scale;
|
|
|
|
InvalidateProperties();
|
|
this.MarkDirty();
|
|
}
|
|
|
|
public void ScaleDurability()
|
|
{
|
|
var scale = 100 + ClothingAttributes.DurabilityBonus;
|
|
|
|
_hitPoints = (_hitPoints * scale + 99) / 100;
|
|
_maxHitPoints = (_maxHitPoints * scale + 99) / 100;
|
|
|
|
InvalidateProperties();
|
|
this.MarkDirty();
|
|
}
|
|
|
|
public override bool AllowSecureTrade(Mobile from, Mobile to, Mobile newOwner, bool accepted) =>
|
|
Ethic.CheckTrade(from, to, newOwner, this) && base.AllowSecureTrade(from, to, newOwner, accepted);
|
|
|
|
public override bool CanEquip(Mobile from)
|
|
{
|
|
if (!Ethic.CheckEquip(from, this))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (from.AccessLevel < AccessLevel.GameMaster)
|
|
{
|
|
if (RequiredRace != null && from.Race != RequiredRace)
|
|
{
|
|
if (RequiredRace == Race.Elf)
|
|
{
|
|
from.SendLocalizedMessage(1072203); // Only Elves may use this.
|
|
}
|
|
else
|
|
{
|
|
from.SendMessage($"Only {RequiredRace.PluralName} may use this.");
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
if (!AllowMaleWearer && !from.Female)
|
|
{
|
|
if (AllowFemaleWearer)
|
|
{
|
|
from.SendLocalizedMessage(1010388); // Only females can wear this.
|
|
}
|
|
else
|
|
{
|
|
from.SendMessage("You may not wear this.");
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
if (!AllowFemaleWearer && from.Female)
|
|
{
|
|
if (AllowMaleWearer)
|
|
{
|
|
from.SendLocalizedMessage(1063343); // Only males can wear this.
|
|
}
|
|
else
|
|
{
|
|
from.SendMessage("You may not wear this.");
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
var strBonus = ComputeStatBonus(StatType.Str);
|
|
var strReq = ComputeStatReq(StatType.Str);
|
|
|
|
if (from.Str < strReq || from.Str + strBonus < 1)
|
|
{
|
|
from.SendLocalizedMessage(500213); // You are not strong enough to equip that.
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return base.CanEquip(from);
|
|
}
|
|
|
|
public int ComputeStatReq(StatType type) => AOS.Scale(StrRequirement, 100 - GetLowerStatReq());
|
|
|
|
public int ComputeStatBonus(StatType type) =>
|
|
type switch
|
|
{
|
|
StatType.Str => BaseStrBonus + Attributes.BonusStr,
|
|
StatType.Dex => BaseDexBonus + Attributes.BonusDex,
|
|
_ => BaseIntBonus + Attributes.BonusInt
|
|
};
|
|
|
|
public virtual void AddStatBonuses(Mobile parent)
|
|
{
|
|
if (parent == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var strBonus = ComputeStatBonus(StatType.Str);
|
|
var dexBonus = ComputeStatBonus(StatType.Dex);
|
|
var intBonus = ComputeStatBonus(StatType.Int);
|
|
|
|
if (strBonus == 0 && dexBonus == 0 && intBonus == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var serial = Serial;
|
|
|
|
if (strBonus != 0)
|
|
{
|
|
parent.AddStatMod(new StatMod(StatType.Str, $"{serial}Str", strBonus, TimeSpan.Zero));
|
|
}
|
|
|
|
if (dexBonus != 0)
|
|
{
|
|
parent.AddStatMod(new StatMod(StatType.Dex, $"{serial}Dex", dexBonus, TimeSpan.Zero));
|
|
}
|
|
|
|
if (intBonus != 0)
|
|
{
|
|
parent.AddStatMod(new StatMod(StatType.Int, $"{serial}Int", intBonus, TimeSpan.Zero));
|
|
}
|
|
}
|
|
|
|
public static void ValidateMobile(Mobile m)
|
|
{
|
|
for (var i = m.Items.Count - 1; i >= 0; --i)
|
|
{
|
|
if (i >= m.Items.Count)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var item = m.Items[i];
|
|
|
|
if (item is BaseClothing clothing)
|
|
{
|
|
if (clothing.RequiredRace != null && m.Race != clothing.RequiredRace)
|
|
{
|
|
if (clothing.RequiredRace == Race.Elf)
|
|
{
|
|
m.SendLocalizedMessage(1072203); // Only Elves may use this.
|
|
}
|
|
else
|
|
{
|
|
m.SendMessage($"Only {clothing.RequiredRace.PluralName} may use this.");
|
|
}
|
|
|
|
m.AddToBackpack(clothing);
|
|
}
|
|
else if (!clothing.AllowMaleWearer && !m.Female && m.AccessLevel < AccessLevel.GameMaster)
|
|
{
|
|
if (clothing.AllowFemaleWearer)
|
|
{
|
|
m.SendLocalizedMessage(1010388); // Only females can wear this.
|
|
}
|
|
else
|
|
{
|
|
m.SendMessage("You may not wear this.");
|
|
}
|
|
|
|
m.AddToBackpack(clothing);
|
|
}
|
|
else if (!clothing.AllowFemaleWearer && m.Female && m.AccessLevel < AccessLevel.GameMaster)
|
|
{
|
|
if (clothing.AllowMaleWearer)
|
|
{
|
|
m.SendLocalizedMessage(1063343); // Only males can wear this.
|
|
}
|
|
else
|
|
{
|
|
m.SendMessage("You may not wear this.");
|
|
}
|
|
|
|
m.AddToBackpack(clothing);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public int GetLowerStatReq() => !Core.AOS ? 0 : ClothingAttributes.LowerStatReq;
|
|
|
|
public override void OnAdded(IEntity parent)
|
|
{
|
|
if (parent is Mobile mob)
|
|
{
|
|
if (Core.AOS)
|
|
{
|
|
SkillBonuses.AddTo(mob);
|
|
}
|
|
|
|
AddStatBonuses(mob);
|
|
mob.CheckStatTimers();
|
|
}
|
|
|
|
base.OnAdded(parent);
|
|
}
|
|
|
|
public override void OnRemoved(IEntity parent)
|
|
{
|
|
if (parent is Mobile mob)
|
|
{
|
|
if (Core.AOS)
|
|
{
|
|
SkillBonuses.Remove();
|
|
}
|
|
|
|
var serial = Serial;
|
|
|
|
mob.RemoveStatMod($"{serial}Str");
|
|
mob.RemoveStatMod($"{serial}Dex");
|
|
mob.RemoveStatMod($"{serial}Int");
|
|
|
|
mob.CheckStatTimers();
|
|
}
|
|
|
|
base.OnRemoved(parent);
|
|
}
|
|
|
|
public override void OnAfterDuped(Item newItem)
|
|
{
|
|
if (newItem is not BaseClothing clothing)
|
|
{
|
|
return;
|
|
}
|
|
|
|
clothing.Attributes = new AosAttributes(newItem, Attributes);
|
|
clothing.Resistances = new AosElementAttributes(newItem, Resistances);
|
|
clothing.SkillBonuses = new AosSkillBonuses(newItem, SkillBonuses);
|
|
clothing.ClothingAttributes = new AosArmorAttributes(newItem, ClothingAttributes);
|
|
|
|
// Set hue again because of resource
|
|
clothing.Hue = Hue;
|
|
// Set HP/Max again because of durability
|
|
clothing.HitPoints = HitPoints;
|
|
clothing.MaxHitPoints = MaxHitPoints;
|
|
}
|
|
|
|
public override bool AllowEquippedCast(Mobile from) =>
|
|
base.AllowEquippedCast(from) || Attributes.SpellChanneling != 0;
|
|
|
|
public override bool CheckPropertyConflict(Mobile m)
|
|
{
|
|
if (base.CheckPropertyConflict(m))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return Layer switch
|
|
{
|
|
Layer.Pants => m.FindItemOnLayer(Layer.InnerLegs) != null,
|
|
Layer.Shirt => m.FindItemOnLayer(Layer.InnerTorso) != null,
|
|
_ => false
|
|
};
|
|
}
|
|
|
|
public override void AddNameProperty(IPropertyList list)
|
|
{
|
|
var oreType = _resource switch
|
|
{
|
|
CraftResource.DullCopper => 1053108,
|
|
CraftResource.ShadowIron => 1053107,
|
|
CraftResource.Copper => 1053106,
|
|
CraftResource.Bronze => 1053105,
|
|
CraftResource.Gold => 1053104,
|
|
CraftResource.Agapite => 1053103,
|
|
CraftResource.Verite => 1053102,
|
|
CraftResource.Valorite => 1053101,
|
|
CraftResource.SpinedLeather => 1061118,
|
|
CraftResource.HornedLeather => 1061117,
|
|
CraftResource.BarbedLeather => 1061116,
|
|
CraftResource.RedScales => 1060814,
|
|
CraftResource.YellowScales => 1060818,
|
|
CraftResource.BlackScales => 1060820,
|
|
CraftResource.GreenScales => 1060819,
|
|
CraftResource.WhiteScales => 1060821,
|
|
CraftResource.BlueScales => 1060815,
|
|
_ => 0
|
|
};
|
|
|
|
var name = Name;
|
|
|
|
if (oreType != 0)
|
|
{
|
|
if (name != null)
|
|
{
|
|
list.Add(1053099, $"{oreType:#}\t{name}"); // ~1_oretype~ ~2_armortype~
|
|
}
|
|
else
|
|
{
|
|
list.Add(1053099, $"{oreType:#}\t{LabelNumber:#}"); // ~1_oretype~ ~2_armortype~
|
|
}
|
|
}
|
|
else if (name == null)
|
|
{
|
|
list.Add(LabelNumber);
|
|
}
|
|
else
|
|
{
|
|
list.Add(name);
|
|
}
|
|
}
|
|
|
|
public override void GetProperties(IPropertyList list)
|
|
{
|
|
base.GetProperties(list);
|
|
|
|
if (_crafter != null)
|
|
{
|
|
list.Add(1050043, _crafter); // crafted by ~1_NAME~
|
|
}
|
|
|
|
if (_factionState != null)
|
|
{
|
|
list.Add(1041350); // faction item
|
|
}
|
|
|
|
if (_quality == ClothingQuality.Exceptional)
|
|
{
|
|
list.Add(1060636); // exceptional
|
|
}
|
|
|
|
if (RequiredRaces == Race.AllowElvesOnly)
|
|
{
|
|
list.Add(1075086); // Elves Only
|
|
}
|
|
|
|
if (RequiredRaces == Race.AllowGargoylesOnly)
|
|
{
|
|
list.Add(1111709); // Gargoyles Only
|
|
}
|
|
|
|
SkillBonuses?.GetProperties(list);
|
|
|
|
int prop;
|
|
|
|
if ((prop = ArtifactRarity) > 0)
|
|
{
|
|
list.Add(1061078, prop); // artifact rarity ~1_val~
|
|
}
|
|
|
|
if ((prop = Attributes.WeaponDamage) != 0)
|
|
{
|
|
list.Add(1060401, prop); // damage increase ~1_val~%
|
|
}
|
|
|
|
if ((prop = Attributes.DefendChance) != 0)
|
|
{
|
|
list.Add(1060408, prop); // defense chance increase ~1_val~%
|
|
}
|
|
|
|
if ((prop = Attributes.BonusDex) != 0)
|
|
{
|
|
list.Add(1060409, prop); // dexterity bonus ~1_val~
|
|
}
|
|
|
|
if ((prop = Attributes.EnhancePotions) != 0)
|
|
{
|
|
list.Add(1060411, prop); // enhance potions ~1_val~%
|
|
}
|
|
|
|
if ((prop = Attributes.CastRecovery) != 0)
|
|
{
|
|
list.Add(1060412, prop); // faster cast recovery ~1_val~
|
|
}
|
|
|
|
if ((prop = Attributes.CastSpeed) != 0)
|
|
{
|
|
list.Add(1060413, prop); // faster casting ~1_val~
|
|
}
|
|
|
|
if ((prop = Attributes.AttackChance) != 0)
|
|
{
|
|
list.Add(1060415, prop); // hit chance increase ~1_val~%
|
|
}
|
|
|
|
if ((prop = Attributes.BonusHits) != 0)
|
|
{
|
|
list.Add(1060431, prop); // hit point increase ~1_val~
|
|
}
|
|
|
|
if ((prop = Attributes.BonusInt) != 0)
|
|
{
|
|
list.Add(1060432, prop); // intelligence bonus ~1_val~
|
|
}
|
|
|
|
if ((prop = Attributes.LowerManaCost) != 0)
|
|
{
|
|
list.Add(1060433, prop); // lower mana cost ~1_val~%
|
|
}
|
|
|
|
if ((prop = Attributes.LowerRegCost) != 0)
|
|
{
|
|
list.Add(1060434, prop); // lower reagent cost ~1_val~%
|
|
}
|
|
|
|
if ((prop = ClothingAttributes.LowerStatReq) != 0)
|
|
{
|
|
list.Add(1060435, prop); // lower requirements ~1_val~%
|
|
}
|
|
|
|
if ((prop = Attributes.Luck) != 0)
|
|
{
|
|
list.Add(1060436, prop); // luck ~1_val~
|
|
}
|
|
|
|
if (ClothingAttributes.MageArmor != 0)
|
|
{
|
|
list.Add(1060437); // mage armor
|
|
}
|
|
|
|
if ((prop = Attributes.BonusMana) != 0)
|
|
{
|
|
list.Add(1060439, prop); // mana increase ~1_val~
|
|
}
|
|
|
|
if ((prop = Attributes.RegenMana) != 0)
|
|
{
|
|
list.Add(1060440, prop); // mana regeneration ~1_val~
|
|
}
|
|
|
|
if (Attributes.NightSight != 0)
|
|
{
|
|
list.Add(1060441); // night sight
|
|
}
|
|
|
|
if ((prop = Attributes.ReflectPhysical) != 0)
|
|
{
|
|
list.Add(1060442, prop); // reflect physical damage ~1_val~%
|
|
}
|
|
|
|
if ((prop = Attributes.RegenStam) != 0)
|
|
{
|
|
list.Add(1060443, prop); // stamina regeneration ~1_val~
|
|
}
|
|
|
|
if ((prop = Attributes.RegenHits) != 0)
|
|
{
|
|
list.Add(1060444, prop); // hit point regeneration ~1_val~
|
|
}
|
|
|
|
if ((prop = ClothingAttributes.SelfRepair) != 0)
|
|
{
|
|
list.Add(1060450, prop); // self repair ~1_val~
|
|
}
|
|
|
|
if (Attributes.SpellChanneling != 0)
|
|
{
|
|
list.Add(1060482); // spell channeling
|
|
}
|
|
|
|
if ((prop = Attributes.SpellDamage) != 0)
|
|
{
|
|
list.Add(1060483, prop); // spell damage increase ~1_val~%
|
|
}
|
|
|
|
if ((prop = Attributes.BonusStam) != 0)
|
|
{
|
|
list.Add(1060484, prop); // stamina increase ~1_val~
|
|
}
|
|
|
|
if ((prop = Attributes.BonusStr) != 0)
|
|
{
|
|
list.Add(1060485, prop); // strength bonus ~1_val~
|
|
}
|
|
|
|
if ((prop = Attributes.WeaponSpeed) != 0)
|
|
{
|
|
list.Add(1060486, prop); // swing speed increase ~1_val~%
|
|
}
|
|
|
|
if (Core.ML && (prop = Attributes.IncreasedKarmaLoss) != 0)
|
|
{
|
|
list.Add(1075210, prop); // Increased Karma Loss ~1val~%
|
|
}
|
|
|
|
AddResistanceProperties(list);
|
|
|
|
if ((prop = ClothingAttributes.DurabilityBonus) > 0)
|
|
{
|
|
list.Add(1060410, prop); // durability ~1_val~%
|
|
}
|
|
|
|
if ((prop = ComputeStatReq(StatType.Str)) > 0)
|
|
{
|
|
list.Add(1061170, prop); // strength requirement ~1_val~
|
|
}
|
|
|
|
if (_hitPoints >= 0 && _maxHitPoints > 0)
|
|
{
|
|
list.Add(1060639, $"{_hitPoints}\t{_maxHitPoints}"); // durability ~1_val~ / ~2_val~
|
|
}
|
|
}
|
|
|
|
public override void OnSingleClick(Mobile from)
|
|
{
|
|
if (!Core.UOTD)
|
|
{
|
|
OnSingleClickPreUOTD(from);
|
|
return;
|
|
}
|
|
|
|
var attrs = new List<EquipInfoAttribute>();
|
|
AddEquipInfoAttributes(from, attrs);
|
|
|
|
int number;
|
|
|
|
if (Name == null)
|
|
{
|
|
number = LabelNumber;
|
|
}
|
|
else
|
|
{
|
|
LabelTo(from, Name);
|
|
number = 1041000;
|
|
}
|
|
|
|
if (attrs.Count == 0 && Crafter == null && Name != null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
from.NetState.SendDisplayEquipmentInfo(Serial, number, _crafter, false, attrs);
|
|
}
|
|
|
|
public void OnSingleClickPreUOTD(Mobile from)
|
|
{
|
|
var name = Name;
|
|
|
|
if (name == null)
|
|
{
|
|
var articleAnName = (TileData.ItemTable[ItemID].Flags & TileFlag.ArticleAn) != 0;
|
|
name = $"{(articleAnName ? "an" : "a")} {Localization.GetText(LabelNumber).ToLowerInvariant()}";
|
|
}
|
|
|
|
if (Crafter == null)
|
|
{
|
|
LabelTo(from, Quality == ClothingQuality.Exceptional ? $"{name} of exceptional quality" : name);
|
|
return;
|
|
}
|
|
|
|
LabelTo(
|
|
from,
|
|
Quality == ClothingQuality.Exceptional
|
|
? $"{name} crafted with exceptional quality by {Crafter}"
|
|
: $"{name} crafted by {Crafter}"
|
|
);
|
|
}
|
|
|
|
public virtual void AddEquipInfoAttributes(Mobile from, List<EquipInfoAttribute> attrs)
|
|
{
|
|
if (DisplayLootType)
|
|
{
|
|
if (LootType == LootType.Blessed)
|
|
{
|
|
attrs.Add(new EquipInfoAttribute(1038021)); // blessed
|
|
}
|
|
else if (LootType == LootType.Cursed)
|
|
{
|
|
attrs.Add(new EquipInfoAttribute(1049643)); // cursed
|
|
}
|
|
}
|
|
|
|
if (_factionState != null)
|
|
{
|
|
attrs.Add(new EquipInfoAttribute(1041350)); // faction item
|
|
}
|
|
|
|
if (_quality == ClothingQuality.Exceptional)
|
|
{
|
|
attrs.Add(new EquipInfoAttribute(1018305 - (int)_quality));
|
|
}
|
|
}
|
|
|
|
public void DistributeBonuses(int amount)
|
|
{
|
|
for (var i = 0; i < amount; ++i)
|
|
{
|
|
switch (Utility.Random(5))
|
|
{
|
|
case 0:
|
|
++Resistances.Physical;
|
|
break;
|
|
case 1:
|
|
++Resistances.Fire;
|
|
break;
|
|
case 2:
|
|
++Resistances.Cold;
|
|
break;
|
|
case 3:
|
|
++Resistances.Poison;
|
|
break;
|
|
case 4:
|
|
++Resistances.Energy;
|
|
break;
|
|
}
|
|
}
|
|
|
|
InvalidateProperties();
|
|
}
|
|
|
|
private static bool GetSaveFlag(OldSaveFlag flags, OldSaveFlag toGet) => (flags & toGet) != 0;
|
|
|
|
[AfterDeserialization]
|
|
private void AfterDeserialization()
|
|
{
|
|
if (_maxHitPoints == 0 && _hitPoints == 0)
|
|
{
|
|
_hitPoints = _maxHitPoints = Utility.RandomMinMax(InitMinHits, InitMaxHits);
|
|
}
|
|
|
|
if (Parent is Mobile parent)
|
|
{
|
|
if (Core.AOS)
|
|
{
|
|
SkillBonuses.AddTo(parent);
|
|
}
|
|
|
|
AddStatBonuses(parent);
|
|
parent.CheckStatTimers();
|
|
}
|
|
}
|
|
|
|
[Flags]
|
|
private enum OldSaveFlag
|
|
{
|
|
None = 0x00000000,
|
|
Resource = 0x00000001,
|
|
Attributes = 0x00000002,
|
|
ClothingAttributes = 0x00000004,
|
|
SkillBonuses = 0x00000008,
|
|
Resistances = 0x00000010,
|
|
MaxHitPoints = 0x00000020,
|
|
HitPoints = 0x00000040,
|
|
PlayerConstructed = 0x00000080,
|
|
Crafter = 0x00000100,
|
|
Quality = 0x00000200,
|
|
StrReq = 0x00000400
|
|
}
|
|
}
|
|
}
|