fix: Adds missing ML Abilities (#799)

This commit is contained in:
IAmDanielDinner 2021-10-04 08:04:06 +02:00 • committed by GitHub
parent 4893094bb4
commit 5725fba373
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 931 additions and 243 deletions

View file

@ -10,18 +10,7 @@ namespace Server.Items
public override double DamageScalar => 1.5;
public override bool RequiresSE => true;
public override bool CheckSkills(Mobile from)
{
if (GetSkill(from, SkillName.Ninjitsu) < 50.0 && GetSkill(from, SkillName.Bushido) < 50.0)
{
// You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack!
from.SendLocalizedMessage(1063347, "50");
return false;
}
return base.CheckSkills(from);
}
public override bool RequiresSecondarySkill(Mobile from) => true;
public override void OnHit(Mobile attacker, Mobile defender, int damage)
{

View file

@ -0,0 +1,106 @@
using Server.Mobiles;
using System;
using System.Collections.Generic;
namespace Server.Items
{
public class Bladeweave : WeaponAbility
{
private class BladeWeaveRedirect
{
public readonly WeaponAbility NewAbility;
public readonly int ClilocEntry;
public BladeWeaveRedirect(WeaponAbility ability, int cliloc)
{
NewAbility = ability;
ClilocEntry = cliloc;
}
}
private static readonly Dictionary<Mobile, BladeWeaveRedirect> _newAttack = new();
public static bool BladeWeaving(Mobile attacker, out WeaponAbility a)
{
if (_newAttack.TryGetValue(attacker, out var bwr))
{
a = bwr.NewAbility;
return true;
}
a = null;
return false;
}
public override int BaseMana => 30;
public override bool OnBeforeSwing(Mobile attacker, Mobile defender)
{
if (!Validate(attacker) || !CheckMana(attacker, false))
{
return false;
}
// Bladeweave is only from 90 fighting and tactics skill, so you need only to check bushido/ninjitsu value over 50 to perform block and feint.
var requiredBushido = Math.Max(attacker.Skills.Bushido.Value, attacker.Skills.Ninjitsu.Value);
var ran = Utility.Random(requiredBushido >= 50 ? 9 : 7);
var newAttack = _newAttack[attacker] = GetBladeWeaveRedirect(ran);
return newAttack.NewAbility.OnBeforeSwing(attacker, defender);
}
private static BladeWeaveRedirect GetBladeWeaveRedirect(int number) =>
number switch
{
0 => new BladeWeaveRedirect(ArmorIgnore, 1028838),
1 => new BladeWeaveRedirect(BleedAttack, 1028839),
2 => new BladeWeaveRedirect(ConcussionBlow, 1028840),
3 => new BladeWeaveRedirect(CrushingBlow, 1028841),
4 => new BladeWeaveRedirect(DoubleStrike, 1028844),
5 => new BladeWeaveRedirect(MortalStrike, 1028846),
6 => new BladeWeaveRedirect(ParalyzingBlow, 1028848),
7 => new BladeWeaveRedirect(Block, 1028853),
_ => new BladeWeaveRedirect(Feint, 1028857) //8
};
public override bool OnBeforeDamage(Mobile attacker, Mobile defender) =>
_newAttack.TryGetValue(attacker, out var bwr)
? bwr.NewAbility.OnBeforeDamage(attacker, defender)
: base.OnBeforeDamage(attacker, defender);
public override void OnHit(Mobile attacker, Mobile defender, int damage)
{
if (CheckMana(attacker, false))
{
if (_newAttack.TryGetValue(attacker, out var bwr))
{
attacker.SendLocalizedMessage(1072841, $"#{bwr.ClilocEntry}");
bwr.NewAbility.OnHit(attacker, defender, damage);
}
else
{
base.OnHit(attacker, defender, damage);
}
_newAttack.Remove(attacker);
ClearCurrentAbility(attacker);
}
}
public override void OnMiss(Mobile attacker, Mobile defender)
{
if (_newAttack.TryGetValue(attacker, out var bwr))
{
bwr.NewAbility.OnMiss(attacker, defender);
}
else
{
base.OnMiss(attacker, defender);
}
_newAttack.Remove(attacker);
}
}
}

View file

@ -11,18 +11,7 @@ namespace Server.Items
private static readonly Dictionary<Mobile, InternalTimer> _table = new();
public override int BaseMana => 30;
public override bool CheckSkills(Mobile from)
{
if (GetSkill(from, SkillName.Ninjitsu) < 50.0 && GetSkill(from, SkillName.Bushido) < 50.0)
{
// You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack!
from.SendLocalizedMessage(1063347, "50");
return false;
}
return base.CheckSkills(from);
}
public override bool RequiresSecondarySkill(Mobile from) => true;
public override void OnHit(Mobile attacker, Mobile defender, int damage)
{

View file

@ -8,6 +8,24 @@ namespace Server.Items
public override int BaseMana => 25;
public override double DamageScalar => 1.5;
public virtual double GetRequiredTactics(Mobile from)
{
if (from.Weapon is BaseWeapon weapon)
{
if (weapon.PrimaryAbility == this)
{
return 30.0;
}
if (weapon.SecondaryAbility == this)
{
return 60.0;
}
}
return 200.0;
}
public override void OnHit(Mobile attacker, Mobile defender, int damage)
{
if (!Validate(attacker) || !CheckMana(attacker, true))

View file

@ -12,18 +12,7 @@ namespace Server.Items
private static readonly Dictionary<Mobile, DefenseMasteryInfo> _table = new();
public override int BaseMana => 30;
public override bool CheckSkills(Mobile from)
{
if (GetSkill(from, SkillName.Ninjitsu) < 50.0 && GetSkill(from, SkillName.Bushido) < 50.0)
{
// You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack!
from.SendLocalizedMessage(1063347, "50");
return false;
}
return base.CheckSkills(from);
}
public override bool RequiresSecondarySkill(Mobile from) => true;
public override void OnHit(Mobile attacker, Mobile defender, int damage)
{
@ -40,8 +29,7 @@ namespace Server.Items
var modifier =
(int)(30.0 *
((Math.Max(attacker.Skills.Bushido.Value, attacker.Skills.Ninjitsu.Value) -
50.0) / 70.0));
((Math.Max(attacker.Skills.Bushido.Value, attacker.Skills.Ninjitsu.Value) - 50.0) / 70.0));
if (_table.TryGetValue(attacker, out var info))
{

View file

@ -12,33 +12,25 @@ namespace Server.Items
public override int BaseMana => 20;
// No longer active in pub21:
/*public override bool CheckSkills( Mobile from )
public override bool RequiresTactics(Mobile from) => from.Weapon is not BaseWeapon { Skill: SkillName.Wrestling };
// Disarm is special. Doesnt need tactics when wresling and tactics need is lower than fighting skill.
public virtual double GetRequiredTactics(Mobile from)
{
if (!base.CheckSkills( from ))
return false;
if (!(from.Weapon is Fists))
return true;
Skill skill = from.Skills.ArmsLore;
if (skill?.Base >= 80.0)
return true;
from.SendLocalizedMessage( 1061812 ); // You lack the required skill in armslore to perform that attack!
return false;
}*/
public override bool RequiresTactics(Mobile from)
{
if (!(from.Weapon is BaseWeapon weapon))
if (from.Weapon is BaseWeapon weapon)
{
return false;
if (weapon.PrimaryAbility == this)
{
return 30.0;
}
if (weapon.SecondaryAbility == this)
{
return 60.0;
}
}
return weapon.Skill != SkillName.Wrestling;
return 200.0;
}
public override void OnHit(Mobile attacker, Mobile defender, int damage)
@ -52,7 +44,7 @@ namespace Server.Items
var toDisarm = defender.FindItemOnLayer(Layer.OneHanded);
if (toDisarm?.Movable == false)
if (toDisarm?.Movable != false)
{
toDisarm = defender.FindItemOnLayer(Layer.TwoHanded);
}
@ -63,7 +55,7 @@ namespace Server.Items
{
attacker.SendLocalizedMessage(1004001); // You cannot disarm your opponent.
}
else if (!Core.ML && toDisarm == null || toDisarm is BaseShield || toDisarm is Spellbook)
else if (!Core.ML && toDisarm == null || toDisarm is BaseShield or Spellbook)
{
attacker.SendLocalizedMessage(1060849); // Your target is already unarmed!
}
@ -77,6 +69,8 @@ namespace Server.Items
pack.DropItem(toDisarm);
BuffInfo.AddBuff(defender, new BuffInfo(BuffIcon.NoRearm, 1075637, BlockEquipDuration, defender));
BaseWeapon.BlockEquip(defender, BlockEquipDuration);
}
}

View file

@ -44,8 +44,8 @@ namespace Server.Items
return;
}
if (attacker.Mounted && (!(attacker.Weapon is Lance) || !(defender.Weapon is Lance))
) // TODO: Should there be a message here?
// TODO: Should there be a message here?
if (attacker.Mounted && (attacker.Weapon is not Lance || defender.Weapon is not Lance))
{
return;
}
@ -101,12 +101,9 @@ namespace Server.Items
{
playerMobile.SetMountBlock(BlockMountType.DismountRecovery, RemountDelay, true);
}
else if (Core.ML && attacker is BaseCreature bc)
else if (Core.ML && attacker is BaseCreature { ControlMaster: PlayerMobile pm })
{
if (bc.ControlMaster is PlayerMobile pm)
{
pm.SetMountBlock(BlockMountType.DismountRecovery, RemountDelay, false);
}
pm.SetMountBlock(BlockMountType.DismountRecovery, RemountDelay, false);
}
if (!attacker.Mounted)

View file

@ -1,3 +1,5 @@
using System;
namespace Server.Items
{
/// <summary>
@ -7,17 +9,8 @@ namespace Server.Items
{
public override int BaseMana => 30;
public override bool CheckSkills(Mobile from)
{
if (GetSkill(from, SkillName.Ninjitsu) < 50.0 && GetSkill(from, SkillName.Bushido) < 50.0)
{
// You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack!
from.SendLocalizedMessage(1063347, "50");
return false;
}
return base.CheckSkills(from);
}
public override bool RequiresTactics(Mobile from) => false;
public override bool RequiresSecondarySkill(Mobile from) => true;
public override void OnHit(Mobile attacker, Mobile defender, int damage)
{

View file

@ -12,17 +12,8 @@ namespace Server.Items
public override int BaseMana => 30;
public override bool CheckSkills(Mobile from)
{
if (GetSkill(from, SkillName.Ninjitsu) < 50.0)
{
// You need ~1_SKILL_REQUIREMENT~ Ninjitsu skill to perform that attack!
from.SendLocalizedMessage(1063352, "50");
return false;
}
return base.CheckSkills(from);
}
public override bool RequiresSecondarySkill(Mobile from) => true;
public override SkillName GetSecondarySkillName(Mobile from) => SkillName.Ninjitsu;
public override void OnHit(Mobile attacker, Mobile defender, int damage)
{
@ -44,8 +35,8 @@ namespace Server.Items
timer = new DualWieldTimer(
attacker,
(int)(20.0 + 3.0 * (attacker.Skills.Ninjitsu.Value - 50.0) / 7.0)
); // 20-50 % increase
(int)(20.0 + 3.0 * (attacker.Skills.Ninjitsu.Value - 50.0) / 7.0) // 20-50 % increase
);
timer.Start();
Registry.Add(attacker, timer);
@ -55,8 +46,7 @@ namespace Server.Items
{
private readonly Mobile m_Owner;
public DualWieldTimer(Mobile owner, int bonusSwingSpeed)
: base(TimeSpan.FromSeconds(6.0))
public DualWieldTimer(Mobile owner, int bonusSwingSpeed) : base(TimeSpan.FromSeconds(6.0))
{
m_Owner = owner;
BonusSwingSpeed = bonusSwingSpeed;

View file

@ -11,18 +11,7 @@ namespace Server.Items
public static Dictionary<Mobile, FeintTimer> Registry { get; } = new();
public override int BaseMana => 30;
public override bool CheckSkills(Mobile from)
{
if (GetSkill(from, SkillName.Ninjitsu) < 50.0 && GetSkill(from, SkillName.Bushido) < 50.0)
{
// You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack!
from.SendLocalizedMessage(1063347, "50");
return false;
}
return base.CheckSkills(from);
}
public override bool RequiresSecondarySkill(Mobile from) => true;
public override void OnHit(Mobile attacker, Mobile defender, int damage)
{
@ -46,10 +35,8 @@ namespace Server.Items
timer = new FeintTimer(
defender,
(int)(20.0 + 3.0 * (Math.Max(
attacker.Skills.Ninjitsu.Value,
attacker.Skills.Bushido.Value
) - 50.0) / 7.0)
(int)(20.0 + 3.0 *
(Math.Max(attacker.Skills.Ninjitsu.Value, attacker.Skills.Bushido.Value) - 50.0) / 7.0)
); // 20-50 % decrease
timer.Start();

View file

@ -0,0 +1,164 @@
using Server.Spells;
using System;
using System.Collections.Generic;
namespace Server.Items
{
public class ForceArrow : WeaponAbility
{
public override int BaseMana => 20;
public override bool RequiresTactics(Mobile from) => false;
public override void OnHit(Mobile attacker, Mobile defender, int damage)
{
if (!Validate(attacker) || !CheckMana(attacker, true))
{
return;
}
ClearCurrentAbility(attacker);
attacker.SendLocalizedMessage(1074381); // You fire an arrow of pure force.
defender.SendLocalizedMessage(1074382); // You are struck by a force arrow!
if (0.4 > Utility.RandomDouble())
{
defender.Combatant = null;
defender.Warmode = false;
}
ForceArrowInfo info = GetInfo(attacker, defender);
if (info == null)
{
BeginForceArrow(attacker, defender);
}
else if (info.Timer.Running)
{
info.Timer.IncreaseExpiration();
BuffInfo.RemoveBuff(defender, BuffIcon.ForceArrow);
BuffInfo.AddBuff(defender, new BuffInfo(BuffIcon.ForceArrow, 1151285, 1151286, info.DefenseChanceMalus.ToString()));
}
if (defender.Spell is Spell spell && spell.IsCasting)
{
spell.Disturb(DisturbType.Hurt, false, true);
}
}
private static readonly Dictionary<Mobile, List<ForceArrowInfo>> _table = new Dictionary<Mobile, List<ForceArrowInfo>>();
public static void BeginForceArrow(Mobile attacker, Mobile defender)
{
ForceArrowInfo info = new ForceArrowInfo(attacker, defender);
info.Timer = new ForceArrowTimer(info);
if (_table.TryGetValue(attacker, out var list))
{
list.Add(info);
}
else
{
_table.Add(attacker, new List<ForceArrowInfo>() { info });
}
BuffInfo.AddBuff(defender, new BuffInfo(BuffIcon.ForceArrow, 1151285, 1151286, info.DefenseChanceMalus.ToString()));
}
public static void EndForceArrow(ForceArrowInfo info)
{
if (info == null)
{
return;
}
Mobile attacker = info.Attacker;
if (_table.TryGetValue(attacker, out var list) && list.Remove(info) && list.Count == 0)
{
_table.Remove(attacker);
}
BuffInfo.RemoveBuff(info.Defender, BuffIcon.ForceArrow);
}
public static bool HasForceArrow(Mobile attacker, Mobile defender)
{
if (_table.TryGetValue(attacker, out var list))
{
foreach (ForceArrowInfo info in list)
{
if (info.Defender == defender)
{
return true;
}
}
}
return false;
}
public static ForceArrowInfo GetInfo(Mobile attacker, Mobile defender)
{
if (_table.TryGetValue(attacker,out var list))
{
foreach (ForceArrowInfo info in list)
{
if (info.Defender == defender)
{
return info;
}
}
}
return null;
}
public class ForceArrowInfo
{
public Mobile Attacker { get; }
public Mobile Defender { get; }
public ForceArrowTimer Timer { get; set; }
public int DefenseChanceMalus { get; set; }
public ForceArrowInfo(Mobile attacker, Mobile defender)
{
Attacker = attacker;
Defender = defender;
DefenseChanceMalus = 10;
}
}
public class ForceArrowTimer : Timer
{
private readonly ForceArrowInfo _info;
private DateTime _expires;
public ForceArrowTimer(ForceArrowInfo info)
: base(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1))
{
_info = info;
_expires = Core.Now + TimeSpan.FromSeconds(10);
Start();
}
protected override void OnTick()
{
if (_expires < Core.Now)
{
Stop();
EndForceArrow(_info);
}
}
public void IncreaseExpiration()
{
_expires += TimeSpan.FromSeconds(2);
_info.DefenseChanceMalus += 5;
}
}
}
}

View file

@ -0,0 +1,111 @@
using System;
using System.Collections.Generic;
namespace Server.Items
{
public class ForceOfNature : WeaponAbility
{
public override int BaseMana => 35;
public override void OnHit(Mobile attacker, Mobile defender, int damage)
{
if (!Validate(attacker) || !CheckMana(attacker, true))
{
return;
}
ClearCurrentAbility(attacker);
attacker.SendLocalizedMessage(1074374); // You attack your enemy with the force of nature!
defender.SendLocalizedMessage(1074375); // You are assaulted with great force!
defender.PlaySound(0x22F);
defender.FixedParticles(0x36CB, 1, 9, 9911, 67, 5, EffectLayer.Head);
defender.FixedParticles(0x374A, 1, 17, 9502, 1108, 4, (EffectLayer)255);
Remove(attacker);
ForceOfNatureTimer t = new ForceOfNatureTimer(attacker, defender);
t.Start();
_table[attacker] = t;
}
private static readonly Dictionary<Mobile, ForceOfNatureTimer> _table = new Dictionary<Mobile, ForceOfNatureTimer>();
public static void Remove(Mobile m)
{
if (_table.Remove(m, out var timer))
{
timer.Stop();
}
}
public static void OnHit(Mobile from, Mobile target)
{
if (_table.TryGetValue(from,out var t))
{
t.Hits++;
t.LastHit = Core.Now;
if (t.Hits % 12 == 0)
{
int duration = target.Skills[SkillName.MagicResist].Value >= 90.0 ? 1 : 2;
target.Paralyze(TimeSpan.FromSeconds(duration));
target.FixedEffect(0x376A, 9, 32);
target.PlaySound(0x204);
t.Hits = 0;
from.SendLocalizedMessage(1004013); // You successfully stun your opponent!
target.SendLocalizedMessage(1004014); // You have been stunned!
}
}
}
public static double GetDamageScalar(Mobile from, Mobile target)
{
if (_table.TryGetValue(from, out var t) && t.Target == target)
{
var bonus = Math.Min(100, Math.Max(50, from.Str - 50));
return (100.0 + bonus) / 100.0;
}
return 1.0;
}
private class ForceOfNatureTimer : Timer
{
public Mobile Target { get; }
public Mobile From { get; }
public int Hits { get; set; }
public DateTime LastHit { get; set; }
public ForceOfNatureTimer(Mobile from, Mobile target)
: base(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1))
{
Target = target;
From = from;
Hits = 1;
LastHit = Core.Now;
}
protected override void OnTick()
{
if (!From.Alive || !Target.Alive || Target.Map != From.Map || Target.GetDistanceToSqrt(From.Location) > 10 || LastHit + TimeSpan.FromSeconds(20) < Core.Now || Index > 36)
{
Remove(From);
return;
}
if (Index == 1)
{
int damage = Utility.RandomMinMax(15, 35);
AOS.Damage(From, From, damage, false, 0, 0, 0, 0, 0, 0, 100, false, false, false);
}
}
}
}
}

View file

@ -12,18 +12,8 @@ namespace Server.Items
public override int BaseMana => 30;
public static Dictionary<Mobile, FrenziedWirlwindTimer> Registry { get; } = new();
public override bool CheckSkills(Mobile from)
{
if (GetSkill(from, SkillName.Ninjitsu) < 50.0 && GetSkill(from, SkillName.Bushido) < 50.0)
{
// You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack!
from.SendLocalizedMessage(1063347, "50");
return false;
}
return base.CheckSkills(from);
}
public override bool RequiresTactics(Mobile from) => false;
public override bool RequiresSecondarySkill(Mobile from) => true;
public override void OnHit(Mobile attacker, Mobile defender, int damage)
{

View file

@ -30,7 +30,7 @@ namespace Server.Items
ClearCurrentAbility(attacker);
if (!(attacker.Weapon is BaseWeapon weapon))
if (attacker.Weapon is not BaseWeapon weapon)
{
return;
}

View file

@ -0,0 +1,58 @@
using Server.Spells;
using System;
using System.Collections.Generic;
namespace Server.Items
{
public class LightningArrow : WeaponAbility
{
public override int BaseMana => 20;
//TODO - add ConsumeAmmo on weaponabilities
// public override bool ConsumeAmmo => false;
public override void OnHit(Mobile attacker, Mobile defender, int damage)
{
if (!Validate(attacker))
{
return;
}
ClearCurrentAbility(attacker);
Map map = attacker.Map;
if (map == null || attacker.Weapon is not BaseWeapon weapon || !CheckMana(attacker, true))
{
return;
}
List<Mobile> targets = new List<Mobile>();
IPooledEnumerable eable = defender.GetMobilesInRange(5);
foreach (Mobile m in eable)
{
if (m != defender && m != attacker && SpellHelper.ValidIndirectTarget(attacker, m) && m?.Deleted == false &&
m.Map == attacker.Map && m.Alive && attacker.CanSee(m) && attacker.CanBeHarmful(m) &&
attacker.InRange(m, weapon.MaxRange) && attacker.InLOS(m))
{
targets.Add(m);
}
}
eable.Free();
defender.BoltEffect(0);
var mobilesLeft = Math.Min(targets.Count, 2);
while (mobilesLeft-- > 0)
{
var index = Utility.Random(targets.Count);
var m = targets[index];
targets.RemoveAt(index);
m.BoltEffect(0);
AOS.Damage(m, attacker, Utility.RandomMinMax(29, 40), 0, 0, 0, 0, 100);
}
}
}
}

View file

@ -9,17 +9,8 @@ namespace Server.Items
{
public override int BaseMana => 30;
public override bool CheckSkills(Mobile from)
{
if (GetSkill(from, SkillName.Bushido) < 50.0)
{
// You need ~1_SKILL_REQUIREMENT~ Bushido skill to perform that attack!
from.SendLocalizedMessage(1070768, "50");
return false;
}
return base.CheckSkills(from);
}
public override bool RequiresSecondarySkill(Mobile from) => true;
public override SkillName GetSecondarySkillName(Mobile from) => SkillName.Bushido;
public override bool OnBeforeSwing(Mobile attacker, Mobile defender)
{

View file

@ -17,25 +17,7 @@ namespace Server.Items
public override int BaseMana => 30;
// No longer active in pub21:
/*public override bool CheckSkills( Mobile from )
{
if (!base.CheckSkills( from ))
return false;
if (!(from.Weapon is Fists))
return true;
Skill skill = from.Skills.Anatomy;
if (skill?.Base >= 80.0)
return true;
from.SendLocalizedMessage( 1061811 ); // You lack the required anatomy skill to perform that attack!
return false;
}*/
// When using Wrestling, tactics isnt needed.
public override bool RequiresTactics(Mobile from) =>
!(from.Weapon is BaseWeapon weapon && weapon.Skill == SkillName.Wrestling);

View file

@ -0,0 +1,93 @@
using System;
using System.Collections.Generic;
namespace Server.Items
{
public class PsychicAttack : WeaponAbility
{
public static Dictionary<Mobile, PsychicAttackTimer> Registry { get; } = new();
public override int BaseMana => 30;
public override void OnHit(Mobile attacker, Mobile defender, int damage)
{
if (!Validate(attacker) || !CheckMana(attacker, true))
{
return;
}
ClearCurrentAbility(attacker);
attacker.SendLocalizedMessage(1074383); // Your shot sends forth a wave of psychic energy.
defender.SendLocalizedMessage(1074384); // Your mind is attacked by psychic force!
defender.FixedParticles(0x3789, 10, 25, 5032, EffectLayer.Head);
defender.PlaySound(0x1F8);
if (Registry.TryGetValue(defender, out var timer))
{
if (!timer.DoneIncrease)
{
timer.SpellDamageMalus *= 2;
timer.ManaCostMalus *= 2;
}
}
else
{
timer = new PsychicAttackTimer(defender);
timer.Start();
Registry.Add(defender, timer);
}
BuffInfo.RemoveBuff(defender, BuffIcon.PsychicAttack);
string args = $"{timer.SpellDamageMalus}\t{timer.ManaCostMalus}";
BuffInfo.AddBuff(defender, new BuffInfo(BuffIcon.PsychicAttack, 1151296, 1151297, args));
}
public static void RemoveEffects(Mobile defender)
{
if (defender == null)
{
return;
}
BuffInfo.RemoveBuff(defender, BuffIcon.PsychicAttack);
Registry.Remove(defender);
defender.SendLocalizedMessage(1150292); // You recover from the effects of the psychic attack.
}
public class PsychicAttackTimer : Timer
{
private readonly Mobile _defender;
private int _spellDamageMalus;
private int _manaCostMalus;
public int SpellDamageMalus
{
get => _spellDamageMalus;
set { _spellDamageMalus = value; DoneIncrease = true; }
}
public int ManaCostMalus
{
get => _manaCostMalus;
set { _manaCostMalus = value; DoneIncrease = true; }
}
public bool DoneIncrease { get; private set; }
public PsychicAttackTimer(Mobile defender) : base(TimeSpan.FromSeconds(10))
{
_defender = defender;
_spellDamageMalus = 15;
_manaCostMalus = 15;
DoneIncrease = false;
}
protected override void OnTick()
{
RemoveEffects(_defender);
}
}
}
}

View file

@ -13,18 +13,8 @@ namespace Server.Items
public override int BaseMana => 30;
public override bool RequiresSE => true;
public override bool CheckSkills(Mobile from)
{
if (GetSkill(from, SkillName.Bushido) < 50.0)
{
// You need ~1_SKILL_REQUIREMENT~ Bushido skill to perform that attack!
from.SendLocalizedMessage(1070768, "50");
return false;
}
return base.CheckSkills(from);
}
public override bool RequiresSecondarySkill(Mobile from) => true;
public override SkillName GetSecondarySkillName(Mobile from) => SkillName.Bushido;
public override void OnHit(Mobile attacker, Mobile defender, int damage)
{

View file

@ -0,0 +1,46 @@
namespace Server.Items
{
public class SerpentArrow : WeaponAbility
{
public override int BaseMana => 25;
// Serpent arrow is used only as secondary ability on "Elven composite longbow" so needed skill is always 60, but for sure we will use GetRequiredSecondarySkill
public override bool RequiresSecondarySkill(Mobile from) => true;
public override double GetRequiredSecondarySkill(Mobile from) => 60;
public override SkillName GetSecondarySkillName(Mobile from) => SkillName.Poisoning;
public override void OnHit(Mobile attacker, Mobile defender, int damage)
{
if (!Validate(attacker) || !CheckMana(attacker, true))
{
return;
}
ClearCurrentAbility(attacker);
defender.SendLocalizedMessage(1112369); // You have been poisoned by a lethal arrow!
int level;
if (attacker.InRange(defender, 2))
{
level = attacker.Skills.Poisoning.Fixed / 2 switch
{
>= 1000 => 3,
> 850 => 2,
> 650 => 1,
_ => 0
};
}
else
{
level = 0;
}
defender.ApplyPoison(attacker, Poison.GetPoison(level));
defender.FixedParticles(0x374A, 10, 15, 5021, EffectLayer.Waist);
defender.PlaySound(0x474);
}
}
}

View file

@ -12,6 +12,10 @@ namespace Server.Items
public override bool RequiresTactics(Mobile from) => false;
public override bool RequiresSecondarySkill(Mobile from) => true;
public override double GetRequiredSecondarySkill(Mobile from) => 80;
public override SkillName GetSecondarySkillName(Mobile from) => SkillName.Stealth;
public override bool CheckSkills(Mobile from)
{
if (!base.CheckSkills(from))

View file

@ -13,17 +13,9 @@ namespace Server.Items
public override int BaseMana => 30;
public override double DamageScalar => 1.2;
public override bool CheckSkills(Mobile from)
{
if (GetSkill(from, SkillName.Ninjitsu) < 50.0)
{
// You need ~1_SKILL_REQUIREMENT~ Ninjitsu skill to perform that attack!
from.SendLocalizedMessage(1063352, "50");
return false;
}
public override bool RequiresSecondarySkill(Mobile from) => true;
public override SkillName GetSecondarySkillName(Mobile from) => SkillName.Ninjitsu;
return base.CheckSkills(from);
}
public override void OnHit(Mobile attacker, Mobile defender, int damage)
{

View file

@ -38,12 +38,12 @@ namespace Server.Items
new DualWield(),
new DoubleShot(),
new ArmorPierce(),
null,
null,
null,
null,
null,
null,
new Bladeweave(),
new ForceArrow(),
new LightningArrow(),
new PsychicAttack(),
new SerpentArrow(),
new ForceOfNature(),
new Disrobe()
};
@ -109,16 +109,60 @@ namespace Server.Items
public virtual bool RequiresTactics(Mobile from) => true;
public virtual bool RequiresSecondarySkill(Mobile from) => false;
/// <summary>
/// Return primary skill for ability. Default 70/90
/// </summary>
/// <param name="from"></param>
/// <returns></returns>
public virtual double GetRequiredSkill(Mobile from)
{
if (from.Weapon is BaseWeapon weapon)
{
if (weapon.PrimaryAbility == this)
if (weapon.PrimaryAbility == this || weapon.PrimaryAbility == Bladeweave)
{
return 70.0;
}
if (weapon.SecondaryAbility == this)
if (weapon.SecondaryAbility == this || weapon.SecondaryAbility == Bladeweave)
{
return 90.0;
}
}
return 200.0;
}
/// <summary>
/// Returns secondary skill for Ability. Default 50.
/// </summary>
/// <param name="from"></param>
/// <returns></returns>
public virtual double GetRequiredSecondarySkill(Mobile from) => 50.0;
/// <summary>
/// Return secondary skillName. Default bushido/ninjitsu
/// </summary>
/// <param name="from"></param>
/// <returns></returns>
public virtual SkillName GetSecondarySkillName(Mobile from) => from.Skills[SkillName.Ninjitsu].Base > from.Skills[SkillName.Bushido].Base ? SkillName.Ninjitsu : SkillName.Bushido;
/// <summary>
/// Returns tactics needed for Ability. Default 70/90.
/// </summary>
/// <param name="from"></param>
/// <returns></returns>
public virtual double GetRequiredTactics(Mobile from)
{
if (from.Weapon is BaseWeapon weapon)
{
if (weapon.PrimaryAbility == this || weapon.PrimaryAbility == Bladeweave)
{
return 70.0;
}
if (weapon.SecondaryAbility == this || weapon.SecondaryAbility == Bladeweave)
{
return 90.0;
}
@ -184,13 +228,32 @@ namespace Server.Items
var reqSkill = GetRequiredSkill(from);
var reqTactics = Core.ML && RequiresTactics(from);
if (Core.ML && reqTactics && from.Skills.Tactics.Base < reqSkill)
if (reqTactics && from.Skills.Tactics.Base < GetRequiredTactics(from))
{
// You need ~1_SKILL_REQUIREMENT~ weapon and tactics skill to perform that attack
from.SendLocalizedMessage(1079308, reqSkill.ToString());
return false;
}
var reqSecondarySkill = GetRequiredSecondarySkill(from);
SkillName secondarySkill = GetSecondarySkillName(from);
if (RequiresSecondarySkill(from) && from.Skills[secondarySkill].Base < reqSecondarySkill)
{
int loc = GetSkillLocalization(secondarySkill);
if (loc == 1060184)
{
from.SendLocalizedMessage(loc);
}
else
{
from.SendLocalizedMessage(loc, reqSecondarySkill.ToString());
}
return false;
}
if (skill?.Base >= reqSkill)
{
return true;
@ -207,19 +270,22 @@ namespace Server.Items
}
/* </UBWS> */
if (reqTactics)
{
// You need ~1_SKILL_REQUIREMENT~ weapon and tactics skill to perform that attack
from.SendLocalizedMessage(1079308, reqSkill.ToString());
}
else
{
// You need ~1_SKILL_REQUIREMENT~ weapon skill to perform that attack
from.SendLocalizedMessage(1060182, reqSkill.ToString());
}
// You need ~1_SKILL_REQUIREMENT~ weapon skill to perform that attack
from.SendLocalizedMessage(1060182, reqSkill.ToString());
return false;
}
private int GetSkillLocalization(SkillName skill)
{
return skill switch
{
SkillName.Bushido => 1063347, // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack!
SkillName.Ninjitsu => 1063347, // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack!
SkillName.Poisoning => 1060184, // You lack the required poisoning to perform that attack
_ => 1157351 // You need ~1_SKILL_REQUIREMENT~ weapon and tactics skill to perform that attack
};
}
public virtual bool CheckSkills(Mobile from) => CheckWeaponSkill(from);
@ -420,10 +486,7 @@ namespace Server.Items
{
private readonly Mobile m_Mobile;
public WeaponAbilityTimer(Mobile from) : base(TimeSpan.FromSeconds(3.0))
{
m_Mobile = from;
}
public WeaponAbilityTimer(Mobile from) : base(TimeSpan.FromSeconds(3.0)) => m_Mobile = from;
protected override void OnTick()
{

View file

@ -28,7 +28,7 @@ namespace Server.Items
return;
}
if (!(attacker.Weapon is BaseWeapon weapon))
if (attacker.Weapon is not BaseWeapon weapon)
{
return;
}

View file

@ -1024,6 +1024,7 @@ namespace Server.Items
}
ImmolatingWeaponSpell.StopImmolating(this);
ForceOfNature.Remove(m);
m.CheckStatTimers();
@ -1157,6 +1158,13 @@ namespace Server.Items
bonus = AosAttributes.GetValue(defender, AosAttribute.DefendChance);
var info = ForceArrow.GetInfo(attacker, defender);
if (info != null && info.Defender == defender)
{
bonus -= info.DefenseChanceMalus;
}
if (DivineFurySpell.UnderEffect(defender))
{
bonus -= 20; // defender loses 20% bonus when they're under divine fury
@ -1662,6 +1670,8 @@ namespace Server.Items
percentageBonus += (int)(move.GetDamageScalar(attacker, defender) * 100) - 100;
}
percentageBonus += (int)(ForceOfNature.GetDamageScalar(attacker, defender) * 100) - 100;
percentageBonus += (int)(damageBonus * 100) - 100;
var cs = CheckSlayers(attacker, defender);
@ -1864,7 +1874,10 @@ namespace Server.Items
move = null;
}
var ignoreArmor = a is ArmorIgnore || move?.IgnoreArmor(attacker) == true;
var ignoreArmor = a is ArmorIgnore ||
move?.IgnoreArmor(attacker) == true ||
Bladeweave.BladeWeaving(attacker, out var bladeweavingAbi) &&
bladeweavingAbi is ArmorIgnore;
var damageGiven = AOS.Damage(
defender,
@ -1981,8 +1994,8 @@ namespace Server.Items
mobile.LocalOverheadMessage(
MessageType.Regular,
0x3B2,
1061121
); // Your equipment is severely damaged.
1061121 // Your equipment is severely damaged.
);
}
}
else
@ -2101,6 +2114,8 @@ namespace Server.Items
a?.OnHit(attacker, defender, damage);
move?.OnHit(attacker, defender, damage);
ForceOfNature.OnHit(attacker, defender);
if (defender is IHonorTarget it)
{
it.ReceivedHonorContext?.OnTargetHit(attacker);
@ -2142,6 +2157,11 @@ namespace Server.Items
// SDI bonus
damageBonus += AosAttributes.GetValue(attacker, AosAttribute.SpellDamage);
if(PsychicAttack.Registry.TryGetValue(attacker,out var timer))
{
damageBonus -= timer.SpellDamageMalus;
}
var context = TransformationSpellHelper.GetContext(attacker);
if (context?.Spell is ReaperFormSpell spell)

View file

@ -8,7 +8,7 @@ namespace Server.Items
public GnarledStaff() : base(0x13F8) => Weight = 3.0;
public override WeaponAbility PrimaryAbility => WeaponAbility.ConcussionBlow;
public override WeaponAbility SecondaryAbility => WeaponAbility.ParalyzingBlow;
public override WeaponAbility SecondaryAbility => WeaponAbility.ForceOfNature;
public override int AosStrengthReq => 20;
public override int AosMinDamage => 15;