using System; using Server; using Server.Mobiles; namespace Server.Misc { public enum Trades { Alchemy = 0, Blacksmith = 1, Carpentry = 2, Cartography = 3, Cooking = 4, Fishing = 5, Fletching = 6, Inscribe = 7, Lumberjacking = 8, Mining = 9, Tailoring = 10, Tinkering = 11, Musicianship = 12, Magery = 13 } public class SkillCheck { private static readonly bool AntiMacroCode = Server.Misc.Settings.AllowMacroing(); public static TimeSpan AntiMacroExpire = TimeSpan.FromMinutes( 5.0 ); //How long do we remember targets/locations? public const int Allowance = 3; //How many times may we use the same location/target for gain private const int LocationSize = 5; //The size of eeach location, make this smaller so players dont have to move as far private static bool[] UseAntiMacro = new bool[] { // true if this skill uses the anti-macro code, false if it does not false,// Archery false,// Bludgeoning false,// Fencing false,// Swords false,// Tactics false,// Parry false,// Concentration true,// Discordance false,// Dodging false,// Hand-to-Hand true,// Healing true,// Hiding true,// Lockpicking true,// Magery true,// MagicResist true,// Meditation true,// Musicianship true,// Peacemaking true,// Poisoning true,// Provocation true,// RemoveTrap true,// Searching true,// Stealing true,// Stealth true// Tracking }; public static void Initialize() { Mobile.SkillCheckLocationHandler = new SkillCheckLocationHandler( Mobile_SkillCheckLocation ); Mobile.SkillCheckDirectLocationHandler = new SkillCheckDirectLocationHandler( Mobile_SkillCheckDirectLocation ); Mobile.SkillCheckTargetHandler = new SkillCheckTargetHandler( Mobile_SkillCheckTarget ); Mobile.SkillCheckDirectTargetHandler = new SkillCheckDirectTargetHandler( Mobile_SkillCheckDirectTarget ); } public static string TradeName( Trades trade ) { if ( trade == Trades.Alchemy ){ return "Alchemy"; } else if ( trade == Trades.Blacksmith ){ return "Blacksmithing"; } else if ( trade == Trades.Carpentry ){ return "Carpentry"; } else if ( trade == Trades.Cartography ){ return "Cartography"; } else if ( trade == Trades.Cooking ){ return "Cooking"; } else if ( trade == Trades.Fishing ){ return "Fishing"; } else if ( trade == Trades.Fletching ){ return "Fletching"; } else if ( trade == Trades.Inscribe ){ return "Inscription"; } else if ( trade == Trades.Lumberjacking ){ return "Lumberjacking"; } else if ( trade == Trades.Mining ){ return "Mining"; } else if ( trade == Trades.Tailoring ){ return "Tailoring"; } else if ( trade == Trades.Tinkering ){ return "Tinkering"; } else if ( trade == Trades.Magery ){ return "Magery"; } else if ( trade == Trades.Musicianship ){ return "Musicianship"; } return ""; } public static double TradeSkill( Mobile m, Trades trade, bool raw ) { double value = 10.0; if ( m is PlayerMobile ) { PlayerMobile pm = (PlayerMobile)m; if ( trade == Trades.Alchemy ){ value += (m.Int*0.75)+(m.Dex*0.25); if ( !raw && pm.NpcGuild == NpcGuild.AlchemistsGuild ){ value += 20; } } else if ( trade == Trades.Blacksmith ){ value += (m.Str*0.85)+(m.Dex*0.15); if ( !raw && pm.NpcGuild == NpcGuild.BlacksmithsGuild ){ value += 20; } } else if ( trade == Trades.Carpentry ){ value += (m.Dex*0.6)+(m.Str*0.4); if ( !raw && pm.NpcGuild == NpcGuild.CarpentryGuild ){ value += 20; } } else if ( trade == Trades.Cartography ){ value += (m.Int*0.7)+(m.Dex*0.3); if ( !raw && pm.NpcGuild == NpcGuild.LibrariansGuild ){ value += 20; } } else if ( trade == Trades.Cooking ){ value += (m.Int*0.75)+(m.Dex*0.25); if ( !raw ){ value += 10; } } else if ( trade == Trades.Fishing ){ value += (m.Str*0.6)+(m.Dex*0.4); if ( !raw && pm.NpcGuild == NpcGuild.MarinersGuild ){ value += 20; } } else if ( trade == Trades.Fletching ){ value += (m.Dex*0.7)+(m.Str*0.3); if ( !raw && pm.NpcGuild == NpcGuild.RangersGuild ){ value += 20; } } else if ( trade == Trades.Inscribe ){ value += (m.Int*0.85)+(m.Dex*0.15); if ( !raw && pm.NpcGuild == NpcGuild.LibrariansGuild ){ value += 20; } } else if ( trade == Trades.Lumberjacking ){ value += (m.Str*0.8)+(m.Dex*0.2); if ( !raw && pm.NpcGuild == NpcGuild.CarpentryGuild ){ value += 20; } } else if ( trade == Trades.Mining ){ value += (double)(m.Str); if ( !raw && pm.NpcGuild == NpcGuild.BlacksmithsGuild ){ value += 20; } } else if ( trade == Trades.Tailoring ){ value += (m.Dex*0.65)+(m.Int*0.35); if ( !raw && pm.NpcGuild == NpcGuild.TailorsGuild ){ value += 20; } } else if ( trade == Trades.Tinkering ){ value += (m.Dex*0.5)+(m.Int*0.5); if ( !raw && pm.NpcGuild == NpcGuild.TinkersGuild ){ value += 20; } } } if ( value > 100 ) value = 100; return value; } public static string TradeStat( Trades trade ) { string stat = "STR"; if ( trade == Trades.Alchemy ){ if ( Utility.RandomMinMax(1,100) <= 75 ){ stat = "INT"; } else { stat = "DEX"; } } else if ( trade == Trades.Blacksmith ){ if ( Utility.RandomMinMax(1,100) <= 85 ){ stat = "STR"; } else { stat = "DEX"; } } else if ( trade == Trades.Carpentry ){ if ( Utility.RandomMinMax(1,100) <= 60 ){ stat = "DEX"; } else { stat = "STR"; } } else if ( trade == Trades.Cartography ){ if ( Utility.RandomMinMax(1,100) <= 70 ){ stat = "INT"; } else { stat = "DEX"; } } else if ( trade == Trades.Cooking ){ if ( Utility.RandomMinMax(1,100) <= 75 ){ stat = "INT"; } else { stat = "DEX"; } } else if ( trade == Trades.Fishing ){ if ( Utility.RandomMinMax(1,100) <= 60 ){ stat = "STR"; } else { stat = "DEX"; } } else if ( trade == Trades.Fletching ){ if ( Utility.RandomMinMax(1,100) <= 70 ){ stat = "DEX"; } else { stat = "STR"; } } else if ( trade == Trades.Inscribe ){ if ( Utility.RandomMinMax(1,100) <= 85 ){ stat = "INT"; } else { stat = "DEX"; } } else if ( trade == Trades.Lumberjacking ){ if ( Utility.RandomMinMax(1,100) <= 80 ){ stat = "STR"; } else { stat = "DEX"; } } else if ( trade == Trades.Mining ){ stat = "STR"; } else if ( trade == Trades.Tailoring ){ if ( Utility.RandomMinMax(1,100) <= 65 ){ stat = "DEX"; } else { stat = "INT"; } } else if ( trade == Trades.Tinkering ){ if ( Utility.RandomMinMax(1,100) <= 50 ){ stat = "DEX"; } else { stat = "INT"; } } return stat; } public static double GetRandomNumber( double minimum, double maximum ) { Random random = new Random(); return random.NextDouble() * (maximum - minimum) + minimum; } public static bool TestTrade( Mobile m, Trades trade, double min, double max ) { if ( trade == Trades.Magery ) { if ( m.Skills[SkillName.Magery].Value >= GetRandomNumber( min, max ) ) return true; else return false; } else if ( trade == Trades.Musicianship ) { if ( m.Skills[SkillName.Musicianship].Value >= GetRandomNumber( min, max ) ) return true; else return false; } else if ( TradeSkill( m, trade, false ) >= GetRandomNumber( min, max ) ) { double gc = (double)(m.Skills.Cap - m.Skills.Total) / m.Skills.Cap; gc += ( 100 - TradeSkill( m, trade, true ) ) / 100; gc /= 2; gc += 0.5; gc /= 2; gc *= 1.0; if ( gc < 0.01 ) gc = 0.01; if ( gc >= Utility.RandomDouble() ) TradeStatGain( m, TradeStat( trade ) ); return true; } return false; } public static void TradeStatGain( Mobile from, string stat ) { if ( stat == "STR" && from.StrLock == StatLockType.Up && (0.5 / 33.3) > Utility.RandomDouble() ) GainStat( from, Stat.Str ); else if ( stat == "DEX" && from.DexLock == StatLockType.Up && (0.5 / 33.3) > Utility.RandomDouble() ) GainStat( from, Stat.Dex ); else if ( stat == "INT" && from.IntLock == StatLockType.Up && (0.5 / 33.3) > Utility.RandomDouble() ) GainStat( from, Stat.Int ); } public static bool IsGuildSkill( Mobile from, SkillName skillName ) { if ( from is PlayerMobile ) { PlayerMobile pm = (PlayerMobile)from; if ( pm.NpcGuild == NpcGuild.MagesGuild ) { if ( skillName == SkillName.Concentration ){ return true; } else if ( skillName == SkillName.Magery ){ return true; } else if ( skillName == SkillName.Meditation ){ return true; } } else if ( pm.NpcGuild == NpcGuild.WarriorsGuild ) { if ( skillName == SkillName.Fencing ){ return true; } else if ( skillName == SkillName.Bludgeoning ){ return true; } else if ( skillName == SkillName.Parry ){ return true; } else if ( skillName == SkillName.Swords ){ return true; } else if ( skillName == SkillName.Tactics ){ return true; } } else if ( pm.NpcGuild == NpcGuild.ThievesGuild ) { if ( skillName == SkillName.Hiding ){ return true; } else if ( skillName == SkillName.Lockpicking ){ return true; } else if ( skillName == SkillName.Stealing ){ return true; } else if ( skillName == SkillName.Stealth ){ return true; } } else if ( pm.NpcGuild == NpcGuild.RangersGuild ) { if ( skillName == SkillName.Archery ){ return true; } else if ( skillName == SkillName.Tracking ){ return true; } else if ( skillName == SkillName.Tactics ){ return true; } } else if ( pm.NpcGuild == NpcGuild.HealersGuild ) { if ( skillName == SkillName.Healing ){ return true; } } else if ( pm.NpcGuild == NpcGuild.BardsGuild ) { if ( skillName == SkillName.Discordance ){ return true; } else if ( skillName == SkillName.Musicianship ){ return true; } else if ( skillName == SkillName.Peacemaking ){ return true; } else if ( skillName == SkillName.Provocation ){ return true; } } else if ( pm.NpcGuild == NpcGuild.AssassinsGuild ) { if ( skillName == SkillName.Fencing ){ return true; } else if ( skillName == SkillName.Hiding ){ return true; } else if ( skillName == SkillName.Poisoning ){ return true; } else if ( skillName == SkillName.Stealth ){ return true; } } } return false; } public static bool IsHealSkill( Mobile from, SkillName skillName ) { if ( from is PlayerMobile ) { PlayerMobile pm = (PlayerMobile)from; if ( pm.NpcGuild == NpcGuild.HealersGuild ) if ( skillName == SkillName.Healing ){ return true; } } return false; } public static bool Mobile_SkillCheckLocation( Mobile from, SkillName skillName, double minSkill, double maxSkill ) { Skill skill = from.Skills[skillName]; if ( skill == null ) return false; double value = skill.Value; if ( value < minSkill ) return false; // Too difficult else if ( value >= maxSkill ) return true; // No challenge double chance = (value - minSkill) / (maxSkill - minSkill); Point2D loc = new Point2D( from.Location.X / LocationSize, from.Location.Y / LocationSize ); return CheckSkill( from, skill, loc, chance ); } public static bool Mobile_SkillCheckDirectLocation( Mobile from, SkillName skillName, double chance ) { Skill skill = from.Skills[skillName]; if ( skill == null ) return false; if ( chance < 0.0 ) return false; // Too difficult else if ( chance >= 1.0 ) return true; // No challenge Point2D loc = new Point2D( from.Location.X / LocationSize, from.Location.Y / LocationSize ); return CheckSkill( from, skill, loc, chance ); } public static bool CheckSkill( Mobile from, Skill skill, object amObj, double chance ) { SkillName skillName = skill.SkillName; if ( from.Skills.Cap == 0 ) return false; double gainer = 2.0; if ( from is PlayerMobile ) { if ( IsGuildSkill( from, skillName ) ) { switch( Utility.RandomMinMax( 0, 5 ) ) { case 0: gainer = 1.5; break; case 1: gainer = 1.4; break; case 2: gainer = 1.3; break; case 3: gainer = 1.2; break; case 4: gainer = 1.1; break; case 5: gainer = 1.0; break; } } } if ( IsHealSkill( from, skillName ) ) gainer = 1.0; bool success = ( chance >= Utility.RandomDouble() ); double gc = (double)(from.Skills.Cap - from.Skills.Total) / from.Skills.Cap; gc += ( skill.Cap - skill.Base ) / skill.Cap; gc /= gainer; gc += ( 1.0 - chance ) * ( success ? 0.5 : 0.2 ); gc /= gainer; gc *= skill.Info.GainFactor; if ( gc < 0.01 ) gc = 0.01; if ( from is BaseCreature && ((BaseCreature)from).Controlled ) gc *= 2; if ( from.Alive && ( ( gc >= Utility.RandomDouble() && AllowGain( from, skill, amObj ) ) || skill.Base < 10.0 ) ) Gain( from, skill ); return success; } public static bool Mobile_SkillCheckTarget( Mobile from, SkillName skillName, object target, double minSkill, double maxSkill ) { Skill skill = from.Skills[skillName]; if ( skill == null ) return false; double value = skill.Value; if ( value < minSkill ) return false; // Too difficult else if ( value >= maxSkill ) return true; // No challenge double chance = (value - minSkill) / (maxSkill - minSkill); return CheckSkill( from, skill, target, chance ); } public static bool Mobile_SkillCheckDirectTarget( Mobile from, SkillName skillName, object target, double chance ) { Skill skill = from.Skills[skillName]; if ( skill == null ) return false; if ( chance < 0.0 ) return false; // Too difficult else if ( chance >= 1.0 ) return true; // No challenge return CheckSkill( from, skill, target, chance ); } private static bool AllowGain( Mobile from, Skill skill, object obj ) { if ( AntiMacroCode && from is PlayerMobile && UseAntiMacro[skill.Info.SkillID] ) return ((PlayerMobile)from).AntiMacroCheck( skill, obj ); else return true; } public enum Stat { Str, Dex, Int } public static void Gain( Mobile from, Skill skill ) { if ( from is BaseCreature && ((BaseCreature)from).IsDeadPet ) return; if ( skill.Base < skill.Cap && skill.Lock == SkillLock.Up ) { int toGain = 1; if ( skill.Base <= 10.0 ) toGain = Utility.Random( 4 ) + 1; Skills skills = from.Skills; if ( from.Player && ( skills.Total / skills.Cap ) >= Utility.RandomDouble() )//( skills.Total >= skills.Cap ) { for ( int i = 0; i < skills.Length; ++i ) { Skill toLower = skills[i]; if ( toLower != skill && toLower.Lock == SkillLock.Down && toLower.BaseFixedPoint >= toGain ) { toLower.BaseFixedPoint -= toGain; break; } } } if ( !from.Player || (skills.Total + toGain) <= skills.Cap ) { skill.BaseFixedPoint += toGain; } } if ( skill.Lock == SkillLock.Up ) { SkillInfo info = skill.Info; if ( from.StrLock == StatLockType.Up && (info.StrGain / 33.3) > Utility.RandomDouble() ) GainStat( from, Stat.Str ); else if ( from.DexLock == StatLockType.Up && (info.DexGain / 33.3) > Utility.RandomDouble() ) GainStat( from, Stat.Dex ); else if ( from.IntLock == StatLockType.Up && (info.IntGain / 33.3) > Utility.RandomDouble() ) GainStat( from, Stat.Int ); } } public static bool CanLower( Mobile from, Stat stat ) { switch ( stat ) { case Stat.Str: return ( from.StrLock == StatLockType.Down && from.RawStr > 10 ); case Stat.Dex: return ( from.DexLock == StatLockType.Down && from.RawDex > 10 ); case Stat.Int: return ( from.IntLock == StatLockType.Down && from.RawInt > 10 ); } return false; } public static bool CanRaise( Mobile from, Stat stat ) { if ( !(from is BaseCreature && ((BaseCreature)from).Controlled) ) { if ( from.RawStatTotal >= from.StatCap ) return false; } switch ( stat ) { case Stat.Str: return ( from.StrLock == StatLockType.Up && from.RawStr < 125 ); case Stat.Dex: return ( from.DexLock == StatLockType.Up && from.RawDex < 125 ); case Stat.Int: return ( from.IntLock == StatLockType.Up && from.RawInt < 125 ); } return false; } public static void IncreaseStat( Mobile from, Stat stat, bool atrophy ) { atrophy = atrophy || (from.RawStatTotal >= from.StatCap); switch ( stat ) { case Stat.Str: { if ( atrophy ) { if ( CanLower( from, Stat.Dex ) && (from.RawDex < from.RawInt || !CanLower( from, Stat.Int )) ) --from.RawDex; else if ( CanLower( from, Stat.Int ) ) --from.RawInt; } if ( CanRaise( from, Stat.Str ) ) ++from.RawStr; break; } case Stat.Dex: { if ( atrophy ) { if ( CanLower( from, Stat.Str ) && (from.RawStr < from.RawInt || !CanLower( from, Stat.Int )) ) --from.RawStr; else if ( CanLower( from, Stat.Int ) ) --from.RawInt; } if ( CanRaise( from, Stat.Dex ) ) ++from.RawDex; break; } case Stat.Int: { if ( atrophy ) { if ( CanLower( from, Stat.Str ) && (from.RawStr < from.RawDex || !CanLower( from, Stat.Dex )) ) --from.RawStr; else if ( CanLower( from, Stat.Dex ) ) --from.RawDex; } if ( CanRaise( from, Stat.Int ) ) ++from.RawInt; break; } } } private static TimeSpan m_StatGainDelay = TimeSpan.FromMinutes( 15.0 ); private static TimeSpan m_PetStatGainDelay = TimeSpan.FromMinutes( 5.0 ); public static void GainStat( Mobile from, Stat stat ) { switch( stat ) { case Stat.Str: { if ( from is BaseCreature && ((BaseCreature)from).Controlled ) { if ( (from.LastStrGain + m_PetStatGainDelay) >= DateTime.Now ) return; } else if( (from.LastStrGain + m_StatGainDelay) >= DateTime.Now ) return; from.LastStrGain = DateTime.Now; break; } case Stat.Dex: { if ( from is BaseCreature && ((BaseCreature)from).Controlled ) { if ( (from.LastDexGain + m_PetStatGainDelay) >= DateTime.Now ) return; } else if( (from.LastDexGain + m_StatGainDelay) >= DateTime.Now ) return; from.LastDexGain = DateTime.Now; break; } case Stat.Int: { if ( from is BaseCreature && ((BaseCreature)from).Controlled ) { if ( (from.LastIntGain + m_PetStatGainDelay) >= DateTime.Now ) return; } else if( (from.LastIntGain + m_StatGainDelay) >= DateTime.Now ) return; from.LastIntGain = DateTime.Now; break; } } bool atrophy = ( (from.RawStatTotal / (double)from.StatCap) >= Utility.RandomDouble() ); IncreaseStat( from, stat, atrophy ); } } }