mirror of
https://github.com/modernuo/ModernUO
synced 2026-08-11 22:23:06 -04:00
### Summary - Adds the following configurations: ```json "stats.statMax": "125", "stats.gainChanceMultiplier": 1.0, "stats.primaryStatGainChance": 0.75, "stats.gainDelay": "00:10:00", "stats.petGainDelay": "00:05:00", "stats.usePub45StatGain": "False" ``` - Adds Pub 45 stat gain rate for UOML+. - Reduces the gain delay for legacy stat gain from 15 minutes to 10 minutes. - Legacy gain no longer requires a gain in skill to gain in a stat.
896 lines
23 KiB
C#
896 lines
23 KiB
C#
using System;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Text.Json.Serialization;
|
|
using Server.Network;
|
|
|
|
namespace Server;
|
|
|
|
public delegate TimeSpan SkillUseCallback(Mobile user);
|
|
|
|
public enum SkillLock : byte
|
|
{
|
|
Up = 0,
|
|
Down = 1,
|
|
Locked = 2
|
|
}
|
|
|
|
public enum SkillName
|
|
{
|
|
Alchemy = 0,
|
|
Anatomy = 1,
|
|
AnimalLore = 2,
|
|
ItemID = 3,
|
|
ArmsLore = 4,
|
|
Parry = 5,
|
|
Begging = 6,
|
|
Blacksmith = 7,
|
|
Fletching = 8,
|
|
Peacemaking = 9,
|
|
Camping = 10,
|
|
Carpentry = 11,
|
|
Cartography = 12,
|
|
Cooking = 13,
|
|
DetectHidden = 14,
|
|
Discordance = 15,
|
|
EvalInt = 16,
|
|
Healing = 17,
|
|
Fishing = 18,
|
|
Forensics = 19,
|
|
Herding = 20,
|
|
Hiding = 21,
|
|
Provocation = 22,
|
|
Inscribe = 23,
|
|
Lockpicking = 24,
|
|
Magery = 25,
|
|
MagicResist = 26,
|
|
Tactics = 27,
|
|
Snooping = 28,
|
|
Musicianship = 29,
|
|
Poisoning = 30,
|
|
Archery = 31,
|
|
SpiritSpeak = 32,
|
|
Stealing = 33,
|
|
Tailoring = 34,
|
|
AnimalTaming = 35,
|
|
TasteID = 36,
|
|
Tinkering = 37,
|
|
Tracking = 38,
|
|
Veterinary = 39,
|
|
Swords = 40,
|
|
Macing = 41,
|
|
Fencing = 42,
|
|
Wrestling = 43,
|
|
Lumberjacking = 44,
|
|
Mining = 45,
|
|
Meditation = 46,
|
|
Stealth = 47,
|
|
RemoveTrap = 48,
|
|
Necromancy = 49,
|
|
Focus = 50,
|
|
Chivalry = 51,
|
|
Bushido = 52,
|
|
Ninjitsu = 53,
|
|
Spellweaving = 54,
|
|
Mysticism = 55,
|
|
Imbuing = 56,
|
|
Throwing = 57
|
|
}
|
|
|
|
public enum Stat
|
|
{
|
|
Str,
|
|
Dex,
|
|
Int
|
|
}
|
|
|
|
[PropertyObject]
|
|
public class Skill
|
|
{
|
|
private ushort m_Base;
|
|
private ushort m_Cap;
|
|
|
|
public Skill(Skills owner, SkillInfo info, IGenericReader reader)
|
|
{
|
|
Owner = owner;
|
|
Info = info;
|
|
|
|
int version = reader.ReadByte();
|
|
|
|
switch (version)
|
|
{
|
|
case 0:
|
|
{
|
|
m_Base = reader.ReadUShort();
|
|
m_Cap = reader.ReadUShort();
|
|
Lock = (SkillLock)reader.ReadByte();
|
|
|
|
break;
|
|
}
|
|
case 0xFF:
|
|
{
|
|
m_Base = 0;
|
|
m_Cap = 1000;
|
|
Lock = SkillLock.Up;
|
|
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
if ((version & 0xC0) == 0x00)
|
|
{
|
|
if ((version & 0x1) != 0)
|
|
{
|
|
m_Base = reader.ReadUShort();
|
|
}
|
|
|
|
if ((version & 0x2) != 0)
|
|
{
|
|
m_Cap = reader.ReadUShort();
|
|
}
|
|
else
|
|
{
|
|
m_Cap = 1000;
|
|
}
|
|
|
|
if ((version & 0x4) != 0)
|
|
{
|
|
Lock = (SkillLock)reader.ReadByte();
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Lock > SkillLock.Locked)
|
|
{
|
|
Lock = SkillLock.Up;
|
|
}
|
|
}
|
|
|
|
public Skill(Skills owner, SkillInfo info, int baseValue, int cap, SkillLock skillLock)
|
|
{
|
|
Owner = owner;
|
|
Info = info;
|
|
m_Base = (ushort)baseValue;
|
|
m_Cap = (ushort)cap;
|
|
Lock = skillLock;
|
|
}
|
|
|
|
public Skills Owner { get; }
|
|
|
|
public SkillName SkillName => (SkillName)Info.SkillID;
|
|
|
|
public int SkillID => Info.SkillID;
|
|
|
|
[CommandProperty(AccessLevel.Counselor)]
|
|
public string Name => Info.Name;
|
|
|
|
public SkillInfo Info { get; }
|
|
|
|
[CommandProperty(AccessLevel.Counselor)]
|
|
public SkillLock Lock { get; private set; }
|
|
|
|
public int BaseFixedPoint
|
|
{
|
|
get => m_Base;
|
|
set
|
|
{
|
|
var sv = (ushort)Math.Clamp(value, 0, 0xFFFF);
|
|
|
|
int oldBase = m_Base;
|
|
|
|
if (m_Base != sv)
|
|
{
|
|
Owner.Total = Owner.Total - m_Base + sv;
|
|
|
|
m_Base = sv;
|
|
|
|
Owner.OnSkillChange(this);
|
|
|
|
var m = Owner.Owner;
|
|
|
|
m?.OnSkillChange(SkillName, (double)oldBase / 10);
|
|
}
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.Counselor, AccessLevel.GameMaster)]
|
|
public double Base
|
|
{
|
|
get => m_Base / 10.0;
|
|
set => BaseFixedPoint = (int)(value * 10.0);
|
|
}
|
|
|
|
public int CapFixedPoint
|
|
{
|
|
get => m_Cap;
|
|
set
|
|
{
|
|
var sv = (ushort)Math.Clamp(value, 0, 0xFFFF);
|
|
|
|
if (m_Cap != sv)
|
|
{
|
|
m_Cap = sv;
|
|
|
|
Owner.OnSkillChange(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.Counselor, AccessLevel.GameMaster)]
|
|
public double Cap
|
|
{
|
|
get => m_Cap / 10.0;
|
|
set => CapFixedPoint = (int)(value * 10.0);
|
|
}
|
|
|
|
public int Fixed => (int)(Value * 10);
|
|
|
|
[CommandProperty(AccessLevel.Counselor)]
|
|
public double Value
|
|
{
|
|
get
|
|
{
|
|
// There has to be this distinction between the racial values and not to account for gaining skills and these skills aren't displayed nor Totaled up.
|
|
var value = NonRacialValue;
|
|
|
|
var raceBonus = Owner.Owner.RacialSkillBonus;
|
|
|
|
if (raceBonus > value)
|
|
{
|
|
value = raceBonus;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.Counselor)]
|
|
public double NonRacialValue
|
|
{
|
|
get
|
|
{
|
|
var baseValue = Base;
|
|
var statsOffset = Owner.Owner.RawStr * Info.StrScale +
|
|
Owner.Owner.RawDex * Info.DexScale +
|
|
Owner.Owner.RawInt * Info.IntScale;
|
|
|
|
var inv = 100.0 - baseValue;
|
|
|
|
if (inv <= 0.0)
|
|
{
|
|
statsOffset = 0.0;
|
|
}
|
|
else
|
|
{
|
|
statsOffset *= inv;
|
|
|
|
if (Info.StatTotal > 0)
|
|
{
|
|
var statTotal = Info.StatTotal * inv;
|
|
|
|
if (statsOffset > statTotal)
|
|
{
|
|
statsOffset = statTotal;
|
|
}
|
|
}
|
|
}
|
|
|
|
var value = baseValue + statsOffset;
|
|
|
|
Owner.Owner.ValidateSkillMods();
|
|
|
|
double bonusObey = 0.0, bonusNotObey = 0.0;
|
|
|
|
var mods = Owner.Owner.SkillMods;
|
|
|
|
if (mods != null)
|
|
{
|
|
foreach (var mod in mods)
|
|
{
|
|
if (mod.Skill != (SkillName)Info.SkillID)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (mod.Relative)
|
|
{
|
|
if (mod.ObeyCap)
|
|
{
|
|
bonusObey += mod.Value;
|
|
}
|
|
else
|
|
{
|
|
bonusNotObey += mod.Value;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bonusObey = 0.0;
|
|
bonusNotObey = 0.0;
|
|
value = mod.Value;
|
|
}
|
|
}
|
|
}
|
|
|
|
value += bonusNotObey;
|
|
|
|
if (value < Cap)
|
|
{
|
|
value += bonusObey;
|
|
|
|
if (value > Cap)
|
|
{
|
|
value = Cap;
|
|
}
|
|
}
|
|
|
|
return value;
|
|
}
|
|
}
|
|
|
|
public override string ToString() => $"[{Name}: {Base}]";
|
|
|
|
public void SetLockNoRelay(SkillLock skillLock)
|
|
{
|
|
if (skillLock > SkillLock.Locked)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Lock = skillLock;
|
|
}
|
|
|
|
public void Serialize(IGenericWriter writer)
|
|
{
|
|
if (m_Base == 0 && m_Cap == 1000 && Lock == SkillLock.Up)
|
|
{
|
|
writer.Write((byte)0xFF); // default
|
|
}
|
|
else
|
|
{
|
|
var flags = 0x0;
|
|
|
|
if (m_Base != 0)
|
|
{
|
|
flags |= 0x1;
|
|
}
|
|
|
|
if (m_Cap != 1000)
|
|
{
|
|
flags |= 0x2;
|
|
}
|
|
|
|
if (Lock != SkillLock.Up)
|
|
{
|
|
flags |= 0x4;
|
|
}
|
|
|
|
writer.Write((byte)flags); // version
|
|
|
|
if (m_Base != 0)
|
|
{
|
|
writer.Write((short)m_Base);
|
|
}
|
|
|
|
if (m_Cap != 1000)
|
|
{
|
|
writer.Write((short)m_Cap);
|
|
}
|
|
|
|
if (Lock != SkillLock.Up)
|
|
{
|
|
writer.Write((byte)Lock);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Update()
|
|
{
|
|
Owner.OnSkillChange(this);
|
|
}
|
|
}
|
|
|
|
public class SkillInfo
|
|
{
|
|
[JsonConstructor]
|
|
public SkillInfo(
|
|
int skillID, string name, double strScale, double dexScale, double intScale, string title,
|
|
SkillUseCallback callback, double strGain, double dexGain, double intGain, double gainFactor,
|
|
string professionSkillName, Stat primaryStat, Stat secondaryStat
|
|
)
|
|
{
|
|
Name = name;
|
|
Title = title;
|
|
SkillID = skillID;
|
|
StrScale = strScale / 100.0;
|
|
DexScale = dexScale / 100.0;
|
|
IntScale = intScale / 100.0;
|
|
Callback = callback;
|
|
StrGain = strGain;
|
|
DexGain = dexGain;
|
|
IntGain = intGain;
|
|
GainFactor = gainFactor;
|
|
ProfessionSkillName = professionSkillName ?? Name.RemoveOrdinal(" ");
|
|
StatTotal = strScale + dexScale + intScale;
|
|
PrimaryStat = primaryStat;
|
|
SecondaryStat = secondaryStat;
|
|
}
|
|
|
|
public SkillUseCallback Callback { get; set; }
|
|
|
|
public int SkillID { get; }
|
|
|
|
public string Name { get; set; }
|
|
|
|
public string Title { get; set; }
|
|
|
|
public double StrScale { get; set; }
|
|
|
|
public double DexScale { get; set; }
|
|
|
|
public double IntScale { get; set; }
|
|
|
|
public double StatTotal { get; set; }
|
|
|
|
public double StrGain { get; set; }
|
|
|
|
public double DexGain { get; set; }
|
|
|
|
public double IntGain { get; set; }
|
|
|
|
public double GainFactor { get; set; }
|
|
|
|
public string ProfessionSkillName { get; set; }
|
|
|
|
public Stat PrimaryStat { get; set; }
|
|
|
|
public Stat SecondaryStat { get; set; }
|
|
|
|
public static SkillInfo[] Table { get; set; } = Array.Empty<SkillInfo>();
|
|
}
|
|
|
|
[PropertyObject]
|
|
public class Skills
|
|
{
|
|
private readonly Skill[] m_Skills;
|
|
private Skill m_Highest;
|
|
|
|
public Skills(Mobile owner)
|
|
{
|
|
Owner = owner;
|
|
Cap = 7000;
|
|
|
|
var info = SkillInfo.Table;
|
|
|
|
m_Skills = new Skill[info.Length];
|
|
}
|
|
|
|
public Skills(Mobile owner, IGenericReader reader)
|
|
{
|
|
Owner = owner;
|
|
|
|
var version = reader.ReadInt();
|
|
|
|
switch (version)
|
|
{
|
|
case 3:
|
|
case 2:
|
|
{
|
|
Cap = reader.ReadInt();
|
|
|
|
goto case 1;
|
|
}
|
|
case 1:
|
|
{
|
|
if (version < 2)
|
|
{
|
|
Cap = 7000;
|
|
}
|
|
|
|
/*m_Total =*/
|
|
if (version < 3)
|
|
{
|
|
reader.ReadInt();
|
|
}
|
|
|
|
var info = SkillInfo.Table;
|
|
|
|
m_Skills = new Skill[info.Length];
|
|
|
|
var count = reader.ReadInt();
|
|
|
|
for (var i = 0; i < count; ++i)
|
|
{
|
|
if (i < info.Length)
|
|
{
|
|
var sk = new Skill(this, info[i], reader);
|
|
|
|
if (sk.BaseFixedPoint != 0 || sk.CapFixedPoint != 1000 || sk.Lock != SkillLock.Up)
|
|
{
|
|
m_Skills[i] = sk;
|
|
Total += sk.BaseFixedPoint;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Will be discarded
|
|
_ = new Skill(this, null, reader);
|
|
}
|
|
}
|
|
|
|
// for ( int i = count; i < info.Length; ++i )
|
|
// m_Skills[i] = new Skill( this, info[i], 0, 1000, SkillLock.Up );
|
|
|
|
break;
|
|
}
|
|
case 0:
|
|
{
|
|
reader.ReadInt();
|
|
|
|
goto case 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.Counselor, AccessLevel.GameMaster)]
|
|
public int Cap { get; set; }
|
|
|
|
public int Total { get; set; }
|
|
|
|
public Mobile Owner { get; }
|
|
|
|
public int Length => m_Skills.Length;
|
|
|
|
public Skill this[SkillName name] => this[(int)name];
|
|
|
|
public Skill this[int skillID]
|
|
{
|
|
get
|
|
{
|
|
if (skillID < 0 || skillID >= m_Skills.Length)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var sk = m_Skills[skillID];
|
|
|
|
if (sk == null)
|
|
{
|
|
m_Skills[skillID] = sk = new Skill(this, SkillInfo.Table[skillID], 0, 1000, SkillLock.Up);
|
|
}
|
|
|
|
return sk;
|
|
}
|
|
}
|
|
|
|
public Skill Highest
|
|
{
|
|
get
|
|
{
|
|
if (m_Highest == null)
|
|
{
|
|
Skill highest = null;
|
|
var value = int.MinValue;
|
|
|
|
for (var i = 0; i < m_Skills.Length; ++i)
|
|
{
|
|
var sk = m_Skills[i];
|
|
|
|
if (sk?.BaseFixedPoint > value)
|
|
{
|
|
value = sk.BaseFixedPoint;
|
|
highest = sk;
|
|
}
|
|
}
|
|
|
|
m_Highest = highest == null && m_Skills.Length > 0 ? this[0] : highest;
|
|
}
|
|
|
|
return m_Highest;
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Alchemy => this[SkillName.Alchemy];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Anatomy => this[SkillName.Anatomy];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill AnimalLore => this[SkillName.AnimalLore];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill ItemID => this[SkillName.ItemID];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill ArmsLore => this[SkillName.ArmsLore];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Parry => this[SkillName.Parry];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Begging => this[SkillName.Begging];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Blacksmith => this[SkillName.Blacksmith];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Fletching => this[SkillName.Fletching];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Peacemaking => this[SkillName.Peacemaking];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Camping => this[SkillName.Camping];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Carpentry => this[SkillName.Carpentry];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Cartography => this[SkillName.Cartography];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Cooking => this[SkillName.Cooking];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill DetectHidden => this[SkillName.DetectHidden];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Discordance => this[SkillName.Discordance];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill EvalInt => this[SkillName.EvalInt];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Healing => this[SkillName.Healing];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Fishing => this[SkillName.Fishing];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Forensics => this[SkillName.Forensics];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Herding => this[SkillName.Herding];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Hiding => this[SkillName.Hiding];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Provocation => this[SkillName.Provocation];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Inscribe => this[SkillName.Inscribe];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Lockpicking => this[SkillName.Lockpicking];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Magery => this[SkillName.Magery];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill MagicResist => this[SkillName.MagicResist];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Tactics => this[SkillName.Tactics];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Snooping => this[SkillName.Snooping];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Musicianship => this[SkillName.Musicianship];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Poisoning => this[SkillName.Poisoning];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Archery => this[SkillName.Archery];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill SpiritSpeak => this[SkillName.SpiritSpeak];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Stealing => this[SkillName.Stealing];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Tailoring => this[SkillName.Tailoring];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill AnimalTaming => this[SkillName.AnimalTaming];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill TasteID => this[SkillName.TasteID];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Tinkering => this[SkillName.Tinkering];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Tracking => this[SkillName.Tracking];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Veterinary => this[SkillName.Veterinary];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Swords => this[SkillName.Swords];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Macing => this[SkillName.Macing];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Fencing => this[SkillName.Fencing];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Wrestling => this[SkillName.Wrestling];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Lumberjacking => this[SkillName.Lumberjacking];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Mining => this[SkillName.Mining];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Meditation => this[SkillName.Meditation];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Stealth => this[SkillName.Stealth];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill RemoveTrap => this[SkillName.RemoveTrap];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Necromancy => this[SkillName.Necromancy];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Focus => this[SkillName.Focus];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Chivalry => this[SkillName.Chivalry];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Bushido => this[SkillName.Bushido];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Ninjitsu => this[SkillName.Ninjitsu];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Spellweaving => this[SkillName.Spellweaving];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Mysticism => this[SkillName.Mysticism];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Imbuing => this[SkillName.Imbuing];
|
|
|
|
[CommandProperty(AccessLevel.Counselor, canModify: true)]
|
|
public Skill Throwing => this[SkillName.Throwing];
|
|
|
|
public override string ToString() => "...";
|
|
|
|
public static bool UseSkill(Mobile from, SkillName name) => UseSkill(from, (int)name);
|
|
|
|
public static bool UseSkill(Mobile from, int skillID)
|
|
{
|
|
if (!from.CheckAlive())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!from.Region.OnSkillUse(from, skillID))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!from.AllowSkillUse((SkillName)skillID))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (skillID >= 0 && skillID < SkillInfo.Table.Length)
|
|
{
|
|
var info = SkillInfo.Table[skillID];
|
|
|
|
if (info.Callback != null)
|
|
{
|
|
if (Core.TickCount - from.NextSkillTime >= 0 && from.Spell == null)
|
|
{
|
|
from.DisruptiveAction();
|
|
|
|
from.NextSkillTime = Core.TickCount + (int)info.Callback(from).TotalMilliseconds;
|
|
|
|
return true;
|
|
}
|
|
|
|
from.SendSkillMessage();
|
|
}
|
|
else
|
|
{
|
|
from.SendLocalizedMessage(500014); // That skill cannot be used directly.
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public void Serialize(IGenericWriter writer)
|
|
{
|
|
Total = 0;
|
|
|
|
writer.Write(3); // version
|
|
|
|
writer.Write(Cap);
|
|
writer.Write(m_Skills.Length);
|
|
|
|
for (var i = 0; i < m_Skills.Length; ++i)
|
|
{
|
|
var sk = m_Skills[i];
|
|
|
|
if (sk == null)
|
|
{
|
|
writer.Write((byte)0xFF);
|
|
}
|
|
else
|
|
{
|
|
sk.Serialize(writer);
|
|
Total += sk.BaseFixedPoint;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void OnSkillChange(Skill skill)
|
|
{
|
|
if (skill == m_Highest) // could be downgrading the skill, force a recalc
|
|
{
|
|
m_Highest = null;
|
|
}
|
|
else if (skill.BaseFixedPoint > m_Highest?.BaseFixedPoint)
|
|
{
|
|
m_Highest = skill;
|
|
}
|
|
|
|
Owner.OnSkillInvalidated(skill);
|
|
Owner.NetState.SendSkillChange(skill);
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Enumerator GetEnumerator() => new(m_Skills);
|
|
|
|
public ref struct Enumerator
|
|
{
|
|
private readonly Skill[] _skills;
|
|
private int _index;
|
|
private Skill _current;
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
internal Enumerator(Skill[] skills)
|
|
{
|
|
_skills = skills;
|
|
_index = 0;
|
|
_current = default;
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public bool MoveNext()
|
|
{
|
|
Skill[] localList = _skills;
|
|
|
|
while ((uint)_index < (uint)localList.Length)
|
|
{
|
|
_current = localList[_index++];
|
|
if (_current != null)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public Skill Current
|
|
{
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
get => _current;
|
|
}
|
|
}
|
|
}
|