fix: Fixes disarm, strangle, curse, summons, looting rights, creatures attacking, and items going to the floor (#1174)

* Fixes disarm
* Fixes strangle not refreshing
* Fixes curse not refreshing
* Fixes 125% bonus to looting rights
* Fixes mobs attacking each other, or not attacking each other
* Fixes items dropping to the floor
* Fixes protection spell
* Fixes cure sending wrong message
This commit is contained in:
mark1145 2022-09-28 15:19:15 +10:00 committed by GitHub
parent 906ec095b9
commit 060edaa76a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 127 additions and 112 deletions

View file

@ -2050,9 +2050,9 @@ namespace Server
var root = ip.RootParent;
var rpm = root as Mobile;
if (ip.IsAccessibleTo(from) &&
rpm?.CheckNonlocalDrop(from, this, ip) == true &&
(!ip.Movable || rpm == from || ip.Map == bounce.Map && root.Location == bounce.WorldLoc)
if (ip.IsAccessibleTo(from)
&& rpm?.CheckNonlocalDrop(from, this, ip) != false
&& (!ip.Movable || rpm == from || ip.Map == bounce.Map && root.Location == bounce.WorldLoc)
)
{
Location = bounce.Location;

View file

@ -44,7 +44,7 @@ namespace Server.Items
var toDisarm = defender.FindItemOnLayer(Layer.OneHanded);
if (toDisarm?.Movable != false)
if (toDisarm?.Movable != true)
{
toDisarm = defender.FindItemOnLayer(Layer.TwoHanded);
}

View file

@ -176,11 +176,12 @@ namespace Server.Misc
return true;
}
var bcFrom = from as BaseCreature;
var pmFrom = from as PlayerMobile;
var pmTarg = target as PlayerMobile;
var bcTarg = target as BaseCreature;
if (pmFrom == null && from is BaseCreature bcFrom && bcFrom.Summoned)
if (pmFrom == null && bcFrom?.Summoned == true)
{
pmFrom = bcFrom.SummonMaster as PlayerMobile;
}
@ -257,9 +258,15 @@ namespace Server.Misc
return true; // Guild allies or enemies can be harmful
}
if (bcTarg?.Controlled == true || bcTarg?.Summoned == true && bcTarg.SummonMaster != from)
if (bcTarg?.Controlled == true
|| (bcTarg?.Summoned == true && bcTarg.SummonMaster != from && bcTarg.SummonMaster.Player))
{
return false; // Cannot harm other controlled mobiles
return false; // Cannot harm other controlled mobiles from players
}
if (pmFrom == null && bcFrom != null && bcFrom.Summoned && target.Player)
{
return true; // Summons from monsters can attack players
}
if (target.Player)

View file

@ -2734,13 +2734,13 @@ public abstract class BaseAI
private bool IsHostile(Mobile from)
{
var count = Math.Max(m_Mobile.Aggressors.Count, m_Mobile.Aggressed.Count);
if (count <= 0 || m_Mobile.Combatant == from || from.Combatant == m_Mobile)
if (m_Mobile.Combatant == from || from.Combatant == m_Mobile)
{
return true;
}
var count = Math.Max(m_Mobile.Aggressors.Count, m_Mobile.Aggressed.Count);
for (var a = 0; a < count; ++a)
{
if (a < m_Mobile.Aggressed.Count && m_Mobile.Aggressed[a].Attacker == from)

View file

@ -125,7 +125,7 @@ namespace Server.Mobiles
m_Damage = damage;
}
public int CompareTo(DamageStore ds) => ds?.m_Damage ?? 0 - m_Damage;
public int CompareTo(DamageStore ds) => (ds?.m_Damage ?? 0).CompareTo(m_Damage);
}
[AttributeUsage(AttributeTargets.Class)]
@ -2977,6 +2977,7 @@ namespace Server.Mobiles
public static List<DamageStore> GetLootingRights(List<DamageEntry> damageEntries, int hitsMax)
{
var rights = new List<DamageStore>();
DamageStore firstDamager = null;
for (var i = damageEntries.Count - 1; i >= 0; --i)
{
@ -3017,12 +3018,15 @@ namespace Server.Mobiles
{
ds.m_Damage += subEntry.DamageGiven;
needNewSubEntry = false;
firstDamager = ds;
}
}
if (needNewSubEntry)
{
rights.Add(new DamageStore(master, subEntry.DamageGiven));
var ds = new DamageStore(master, subEntry.DamageGiven);
rights.Add(ds);
firstDamager = ds;
}
damage -= subEntry.DamageGiven;
@ -3030,7 +3034,7 @@ namespace Server.Mobiles
var m = de.Damager;
if (m?.Deleted != false || !m.Player)
if (m is not { Deleted: false, Player: true })
{
continue;
}
@ -3050,19 +3054,25 @@ namespace Server.Mobiles
{
ds.m_Damage += damage;
needNewEntry = false;
firstDamager = ds;
}
}
if (needNewEntry)
{
rights.Add(new DamageStore(m, damage));
var ds = new DamageStore(m, damage);
rights.Add(ds);
firstDamager = ds;
}
}
// Handle damage rights per Five on Friday: https://www.uoguide.com/Five_on_Friday_-_January_19,_2007
if (rights.Count > 0)
{
// This would be the first valid person attacking it. Gets a 25% bonus. Per 1/19/07 Five on Friday
rights[0].m_Damage = (int)(rights[0].m_Damage * 1.25);
if (firstDamager != null)
{
firstDamager.m_Damage = (int)(firstDamager.m_Damage * 1.25);
}
if (rights.Count > 1)
{

View file

@ -1143,9 +1143,9 @@ namespace Server.Mobiles
var max = base.GetMaxResistance(type);
if (type != ResistanceType.Physical && max > 60 && CurseSpell.UnderEffect(this))
if (type != ResistanceType.Physical && CurseSpell.UnderEffect(this))
{
max = 60;
max -= 10;
}
if (Core.ML && Race == Race.Elf && type == ResistanceType.Energy)

View file

@ -147,8 +147,6 @@ namespace Server.Spells
private static Mobile m_TravelCaster;
private static TravelCheckType m_TravelType;
public static bool DisableSkillCheck { get; set; }
public static TimeSpan GetDamageDelayForSpell(Spell sp) =>
!sp.DelayedDamage ? TimeSpan.Zero :
Core.AOS ? AosDamageDelay : OldDamageDelay;
@ -225,7 +223,7 @@ namespace Server.Spells
{
var info = m.Aggressors[i];
if (info.Attacker.Player && Core.Now - info.LastCombatTime < CombatHeatDelay)
if (info.Attacker.Player && info.Attacker == m && Core.Now - info.LastCombatTime < CombatHeatDelay)
{
return true;
}
@ -292,13 +290,14 @@ namespace Server.Spells
? AddStatBonus(m, m, type, offset, duration)
: offset >= 0 || AddStatCurse(m, m, type, -offset, duration);
public static bool AddStatBonus(Mobile caster, Mobile target, StatType type) => AddStatBonus(
caster,
target,
type,
GetOffset(caster, target, type, false),
GetDuration(caster, target)
);
public static bool AddStatBonus(Mobile caster, Mobile target, StatType type, TimeSpan duration, bool skillCheck = true) =>
AddStatBonus(
caster,
target,
type,
GetOffset(caster, target, type, false, skillCheck),
duration
);
public static bool AddStatBonus(Mobile caster, Mobile target, StatType type, int bonus, TimeSpan duration)
{
@ -322,13 +321,14 @@ namespace Server.Spells
return false;
}
public static bool AddStatCurse(Mobile caster, Mobile target, StatType type) => AddStatCurse(
caster,
target,
type,
GetOffset(caster, target, type, true),
GetDuration(caster, target)
);
public static bool AddStatCurse(Mobile caster, Mobile target, StatType type, TimeSpan duration, bool skillCheck = true) =>
AddStatCurse(
caster,
target,
type,
GetOffset(caster, target, type, true, skillCheck),
duration
);
public static bool AddStatCurse(Mobile caster, Mobile target, StatType type, int curse, TimeSpan duration)
{
@ -374,34 +374,32 @@ namespace Server.Spells
return Math.Max(percent, 0);
}
public static int GetOffset(Mobile caster, Mobile target, StatType type, bool curse)
public static int GetOffset(Mobile caster, Mobile target, StatType type, bool curse, bool skillCheck)
{
if (Core.AOS)
if (!Core.AOS)
{
if (!DisableSkillCheck)
return 1 + (int)(caster.Skills.Magery.Value * 0.1);
}
if (skillCheck)
{
caster.CheckSkill(SkillName.EvalInt, 0.0, 120.0);
if (curse)
{
caster.CheckSkill(SkillName.EvalInt, 0.0, 120.0);
if (curse)
{
target.CheckSkill(SkillName.MagicResist, 0.0, 120.0);
}
}
var percent = GetOffsetScalar(caster, target, curse);
switch (type)
{
case StatType.Str:
return (int)(target.RawStr * percent);
case StatType.Dex:
return (int)(target.RawDex * percent);
case StatType.Int:
return (int)(target.RawInt * percent);
target.CheckSkill(SkillName.MagicResist, 0.0, 120.0);
}
}
return 1 + (int)(caster.Skills.Magery.Value * 0.1);
var percent = GetOffsetScalar(caster, target, curse);
return type switch
{
StatType.Str => (int)(target.RawStr * percent),
StatType.Dex => (int)(target.RawDex * percent),
StatType.Int => (int)(target.RawInt * percent),
_ => 1 + (int)(caster.Skills.Magery.Value * 0.1)
};
}
public static Guild GetGuildFor(Mobile m)

View file

@ -65,7 +65,7 @@ namespace Server.Spells.Chivalry
}
else
{
m.SendLocalizedMessage(1010060); // You have failed to cure your target!
Caster.SendLocalizedMessage(1010060); // You have failed to cure your target!
}
}

View file

@ -46,7 +46,7 @@ namespace Server.Spells.Eighth
{
Expansion.None => TimeSpan.FromSeconds(Caster.Skills.Magery.Value),
// T2A -> Current
_ => TimeSpan.FromSeconds(4 * Math.Min(5, Caster.Skills.Magery.Value)),
_ => TimeSpan.FromSeconds(4 * Math.Max(5, Caster.Skills.Magery.Value)),
};
if (Core.AOS)

View file

@ -46,7 +46,7 @@ namespace Server.Spells.Eighth
{
Expansion.None => TimeSpan.FromSeconds(Caster.Skills.Magery.Value),
// T2A -> Current
_ => TimeSpan.FromSeconds(4 * Math.Min(5, Caster.Skills.Magery.Value)),
_ => TimeSpan.FromSeconds(4 * Math.Max(5, Caster.Skills.Magery.Value)),
};
if (Core.AOS)

View file

@ -47,7 +47,7 @@ namespace Server.Spells.Eighth
{
Expansion.None => TimeSpan.FromSeconds(Caster.Skills.Magery.Value),
// T2A -> Current
_ => TimeSpan.FromSeconds(4 * Math.Min(5, Caster.Skills.Magery.Value)),
_ => TimeSpan.FromSeconds(4 * Math.Max(5, Caster.Skills.Magery.Value)),
};
if (Core.AOS)

View file

@ -47,7 +47,7 @@ namespace Server.Spells.Eighth
{
Expansion.None => TimeSpan.FromSeconds(Caster.Skills.Magery.Value),
// T2A -> Current
_ => TimeSpan.FromSeconds(4 * Math.Min(5, Caster.Skills.Magery.Value)),
_ => TimeSpan.FromSeconds(4 * Math.Max(5, Caster.Skills.Magery.Value)),
};
if (Core.AOS) /* Why two diff daemons? TODO: solve this */

View file

@ -46,7 +46,7 @@ namespace Server.Spells.Eighth
{
Expansion.None => TimeSpan.FromSeconds(Caster.Skills.Magery.Value),
// T2A -> Current
_ => TimeSpan.FromSeconds(4 * Math.Min(5, Caster.Skills.Magery.Value)),
_ => TimeSpan.FromSeconds(4 * Math.Max(5, Caster.Skills.Magery.Value)),
};
if (Core.AOS)

View file

@ -27,7 +27,8 @@ namespace Server.Spells.First
SpellHelper.CheckReflect((int)Circle, Caster, ref m);
SpellHelper.AddStatCurse(Caster, m, StatType.Dex);
var length = SpellHelper.GetDuration(Caster, m);
SpellHelper.AddStatCurse(Caster, m, StatType.Dex, length, false);
m.Spell?.OnCasterHurt();
@ -37,7 +38,6 @@ namespace Server.Spells.First
m.PlaySound(0x1DF);
var percentage = (int)(SpellHelper.GetOffsetScalar(Caster, m, true) * 100);
var length = SpellHelper.GetDuration(Caster, m);
BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.Clumsy, 1075831, length, m, percentage.ToString()));

View file

@ -29,7 +29,8 @@ namespace Server.Spells.First
// TODO: StoneForm immunity
SpellHelper.AddStatCurse(Caster, m, StatType.Int);
var length = SpellHelper.GetDuration(Caster, m);
SpellHelper.AddStatCurse(Caster, m, StatType.Int, length, false);
m.Spell?.OnCasterHurt();
@ -39,7 +40,6 @@ namespace Server.Spells.First
m.PlaySound(0x1E4);
var percentage = (int)(SpellHelper.GetOffsetScalar(Caster, m, true) * 100);
var length = SpellHelper.GetDuration(Caster, m);
BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.FeebleMind, 1075833, length, m, percentage.ToString()));

View file

@ -27,7 +27,8 @@ namespace Server.Spells.First
SpellHelper.CheckReflect((int)Circle, Caster, ref m);
SpellHelper.AddStatCurse(Caster, m, StatType.Str);
var length = SpellHelper.GetDuration(Caster, m);
SpellHelper.AddStatCurse(Caster, m, StatType.Str, length, false);
m.Spell?.OnCasterHurt();
@ -37,7 +38,6 @@ namespace Server.Spells.First
m.PlaySound(0x1E6);
var percentage = (int)(SpellHelper.GetOffsetScalar(Caster, m, true) * 100);
var length = SpellHelper.GetDuration(Caster, m);
BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.Weaken, 1075837, length, m, percentage.ToString()));

View file

@ -15,7 +15,7 @@ namespace Server.Spells.Fourth
Reagent.SulfurousAsh
);
private static readonly HashSet<Mobile> _underEffect = new();
private static readonly Dictionary<Mobile, Timer> _table = new();
public CurseSpell(Mobile caster, Item scroll = null) : base(caster, scroll, _info)
{
@ -30,20 +30,16 @@ namespace Server.Spells.Fourth
SpellHelper.Turn(Caster, m);
SpellHelper.CheckReflect((int)Circle, Caster, ref m);
var length = SpellHelper.GetDuration(Caster, m);
SpellHelper.AddStatCurse(Caster, m, StatType.Str);
SpellHelper.DisableSkillCheck = true;
SpellHelper.AddStatCurse(Caster, m, StatType.Dex);
SpellHelper.AddStatCurse(Caster, m, StatType.Int);
SpellHelper.DisableSkillCheck = false;
SpellHelper.AddStatCurse(Caster, m, StatType.Str, length, false);
SpellHelper.AddStatCurse(Caster, m, StatType.Dex, length);
SpellHelper.AddStatCurse(Caster, m, StatType.Int, length);
// On OSI you CAN curse yourself and get this effect.
if (Caster.Player && m.Player /*&& Caster != m */ && !UnderEffect(m))
if (Caster.Player && m.Player)
{
var duration = SpellHelper.GetDuration(Caster, m);
_underEffect.Add(m);
Timer.StartTimer(duration, () => RemoveEffect(m));
m.UpdateResistances();
RemoveEffect(m);
_table[m] = Timer.DelayCall(length, () => RemoveEffect(m));
}
m.Spell?.OnCasterHurt();
@ -54,8 +50,6 @@ namespace Server.Spells.Fourth
m.PlaySound(0x1E1);
var percentage = (int)(SpellHelper.GetOffsetScalar(Caster, m, true) * 100);
var length = SpellHelper.GetDuration(Caster, m);
var args = $"{percentage}\t{percentage}\t{percentage}\t{10}\t{10}\t{10}\t{10}";
BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.Curse, 1075835, 1075836, length, m, args));
@ -73,11 +67,16 @@ namespace Server.Spells.Fourth
public static bool RemoveEffect(Mobile m)
{
var effectRemoved = _underEffect.Remove(m);
m.UpdateResistances();
return effectRemoved;
if (_table.Remove(m, out var timer))
{
timer.Stop();
m.UpdateResistances();
return true;
}
return false;
}
public static bool UnderEffect(Mobile m) => _underEffect.Contains(m);
public static bool UnderEffect(Mobile m) => _table.ContainsKey(m);
}
}

View file

@ -38,9 +38,9 @@ namespace Server.Spells.Necromancy
SpellHelper.Turn(Caster, m);
// SpellHelper.CheckReflect( (int)this.Circle, Caster, ref m );
// Irrelevent after AoS
// Irrelevant after AoS
/* Temporarily chokes off the air suply of the target with poisonous fumes.
/* Temporarily chokes off the air supply of the target with poisonous fumes.
* The target is inflicted with poison damage over time.
* The amount of damage dealt each "hit" is based off of the caster's Spirit Speak skill and the Target's current Stamina.
* The less Stamina the target has, the more damage is done by Strangle.
@ -60,12 +60,16 @@ namespace Server.Spells.Necromancy
m.FixedParticles(0x36CB, 1, 9, 9911, 67, 5, EffectLayer.Head);
m.FixedParticles(0x374A, 1, 17, 9502, 1108, 4, (EffectLayer)255);
if (!_table.TryGetValue(m, out var timer))
// According to testing on OSI, it is refreshed.
if (_table.TryGetValue(m, out var timer))
{
_table[m] = timer = new InternalTimer(m, Caster);
timer.Start();
timer.Stop();
}
timer = new InternalTimer(m, Caster);
_table[m] = timer;
timer.Start();
HarmfulSpell(m);
}

View file

@ -26,13 +26,13 @@ namespace Server.Spells.Second
{
SpellHelper.Turn(Caster, m);
SpellHelper.AddStatBonus(Caster, m, StatType.Dex);
var length = SpellHelper.GetDuration(Caster, m);
SpellHelper.AddStatBonus(Caster, m, StatType.Dex, length, false);
m.FixedParticles(0x375A, 10, 15, 5010, EffectLayer.Waist);
m.PlaySound(0x1e7);
var percentage = (int)(SpellHelper.GetOffsetScalar(Caster, m, false) * 100);
var length = SpellHelper.GetDuration(Caster, m);
BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.Agility, 1075841, length, m, percentage.ToString()));
}

View file

@ -26,13 +26,13 @@ namespace Server.Spells.Second
{
SpellHelper.Turn(Caster, m);
SpellHelper.AddStatBonus(Caster, m, StatType.Int);
var length = SpellHelper.GetDuration(Caster, m);
SpellHelper.AddStatBonus(Caster, m, StatType.Int, length, false);
m.FixedParticles(0x375A, 10, 15, 5011, EffectLayer.Head);
m.PlaySound(0x1EB);
var percentage = (int)(SpellHelper.GetOffsetScalar(Caster, m, false) * 100);
var length = SpellHelper.GetDuration(Caster, m);
BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.Cunning, 1075843, length, m, percentage.ToString()));
}

View file

@ -48,7 +48,7 @@ namespace Server.Spells.Second
}
else
{
m.SendLocalizedMessage(1010060); // You have failed to cure your target!
Caster.SendLocalizedMessage(1010060); // You have failed to cure your target!
}
}

View file

@ -77,8 +77,8 @@ namespace Server.Spells.Second
target.PlaySound(0x1E9);
target.FixedParticles(0x375A, 9, 20, 5016, EffectLayer.Waist);
var physLoss = Math.Max(0, -15 + (int)(caster.Skills.Inscribe.Value / 20));
var resistLoss = Math.Max(0, -35 + (int)(caster.Skills.Inscribe.Value / 20));
var physLoss = -15 + (int)(caster.Skills.Inscribe.Value / 20);
var resistLoss = -35 + (int)(caster.Skills.Inscribe.Value / 20);
var physMod = new ResistanceMod(ResistanceType.Physical, physLoss);
var resistMod = new DefaultSkillMod(SkillName.MagicResist, true, resistLoss);

View file

@ -26,13 +26,13 @@ namespace Server.Spells.Second
{
SpellHelper.Turn(Caster, m);
SpellHelper.AddStatBonus(Caster, m, StatType.Str);
var length = SpellHelper.GetDuration(Caster, m);
SpellHelper.AddStatBonus(Caster, m, StatType.Str, length, false);
m.FixedParticles(0x375A, 10, 15, 5017, EffectLayer.Waist);
m.PlaySound(0x1EE);
var percentage = (int)(SpellHelper.GetOffsetScalar(Caster, m, false) * 100);
var length = SpellHelper.GetDuration(Caster, m);
BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.Strength, 1075845, length, m, percentage.ToString()));
}

View file

@ -44,11 +44,10 @@ namespace Server.Spells.Sixth
Caster.DoHarmful(m);
SpellHelper.AddStatCurse(Caster, m, StatType.Str);
SpellHelper.DisableSkillCheck = true;
SpellHelper.AddStatCurse(Caster, m, StatType.Dex);
SpellHelper.AddStatCurse(Caster, m, StatType.Int);
SpellHelper.DisableSkillCheck = false;
var length = SpellHelper.GetDuration(Caster, m);
SpellHelper.AddStatCurse(Caster, m, StatType.Str, length, false);
SpellHelper.AddStatCurse(Caster, m, StatType.Dex, length);
SpellHelper.AddStatCurse(Caster, m, StatType.Int, length);
m.FixedParticles(0x374A, 10, 15, 5028, EffectLayer.Waist);
m.PlaySound(0x1FB);

View file

@ -26,17 +26,15 @@ namespace Server.Spells.Third
{
SpellHelper.Turn(Caster, m);
SpellHelper.AddStatBonus(Caster, m, StatType.Str);
SpellHelper.DisableSkillCheck = true;
SpellHelper.AddStatBonus(Caster, m, StatType.Dex);
SpellHelper.AddStatBonus(Caster, m, StatType.Int);
SpellHelper.DisableSkillCheck = false;
var length = SpellHelper.GetDuration(Caster, m);
SpellHelper.AddStatBonus(Caster, m, StatType.Str, length, false);
SpellHelper.AddStatBonus(Caster, m, StatType.Dex, length);
SpellHelper.AddStatBonus(Caster, m, StatType.Int, length);
m.FixedParticles(0x373A, 10, 15, 5018, EffectLayer.Waist);
m.PlaySound(0x1EA);
var percentage = (int)(SpellHelper.GetOffsetScalar(Caster, m, false) * 100);
var length = SpellHelper.GetDuration(Caster, m);
var args = $"{percentage}\t{percentage}\t{percentage}";