ModernUO/Scripts/Spells/Base/SpellHelper.cs
2018-09-14 14:32:04 -07:00

1435 lines
38 KiB
C#

using System;
using Server.Items;
using Server.Guilds;
using Server.Multis;
using Server.Regions;
using Server.Mobiles;
using Server.Targeting;
using Server.Engines.PartySystem;
using Server.Misc;
using Server.Spells.Necromancy;
using Server.Spells.Ninjitsu;
using System.Collections.Generic;
using Server.Spells.Seventh;
using Server.Spells.Fifth;
namespace Server
{
public class DefensiveSpell
{
public static void Nullify( Mobile from )
{
if ( !from.CanBeginAction( typeof( DefensiveSpell ) ) )
new InternalTimer( from ).Start();
}
private class InternalTimer : Timer
{
private Mobile m_Mobile;
public InternalTimer( Mobile m )
: base( TimeSpan.FromMinutes( 1.0 ) )
{
m_Mobile = m;
Priority = TimerPriority.OneSecond;
}
protected override void OnTick()
{
m_Mobile.EndAction( typeof( DefensiveSpell ) );
}
}
}
}
namespace Server.Spells
{
public enum TravelCheckType
{
RecallFrom,
RecallTo,
GateFrom,
GateTo,
Mark,
TeleportFrom,
TeleportTo
}
public class SpellHelper
{
private static TimeSpan AosDamageDelay = TimeSpan.FromSeconds( 1.0 );
private static TimeSpan OldDamageDelay = TimeSpan.FromSeconds( 0.5 );
public static TimeSpan GetDamageDelayForSpell( Spell sp )
{
if ( !sp.DelayedDamage )
return TimeSpan.Zero;
return (Core.AOS ? AosDamageDelay : OldDamageDelay);
}
public static bool CheckMulti( Point3D p, Map map )
{
return CheckMulti( p, map, true, 0);
}
public static bool CheckMulti(Point3D p, Map map, bool houses)
{
return CheckMulti(p, map, houses, 0);
}
public static bool CheckMulti( Point3D p, Map map, bool houses, int housingrange )
{
if ( map == null || map == Map.Internal )
return false;
Sector sector = map.GetSector( p.X, p.Y );
for( int i = 0; i < sector.Multis.Count; ++i )
{
BaseMulti multi = sector.Multis[i];
if ( multi is BaseHouse )
{
BaseHouse bh = (BaseHouse)multi;
if ( ( houses && bh.IsInside( p, 16 ) ) || ( housingrange > 0 && bh.InRange( p, housingrange ) ) )
return true;
}
else if ( multi.Contains( p ))
{
return true;
}
}
return false;
}
public static void Turn( Mobile from, object to )
{
IPoint3D target = to as IPoint3D;
if ( target == null )
return;
if ( target is Item )
{
Item item = (Item)target;
if ( item.RootParent != from )
from.Direction = from.GetDirectionTo( item.GetWorldLocation() );
}
else if ( from != target )
{
from.Direction = from.GetDirectionTo( target );
}
}
private static TimeSpan CombatHeatDelay = TimeSpan.FromSeconds( 30.0 );
private static bool RestrictTravelCombat = true;
public static bool CheckCombat( Mobile m )
{
if ( !RestrictTravelCombat )
return false;
for( int i = 0; i < m.Aggressed.Count; ++i )
{
AggressorInfo info = m.Aggressed[i];
if ( info.Defender.Player && (DateTime.UtcNow - info.LastCombatTime) < CombatHeatDelay )
return true;
}
if ( Core.Expansion == Expansion.AOS )
{
for( int i = 0; i < m.Aggressors.Count; ++i )
{
AggressorInfo info = m.Aggressors[i];
if ( info.Attacker.Player && (DateTime.UtcNow - info.LastCombatTime) < CombatHeatDelay )
return true;
}
}
return false;
}
public static bool AdjustField( ref Point3D p, Map map, int height, bool mobsBlock )
{
if ( map == null )
return false;
for( int offset = 0; offset < 10; ++offset )
{
Point3D loc = new Point3D( p.X, p.Y, p.Z - offset );
if ( map.CanFit( loc, height, true, mobsBlock ) )
{
p = loc;
return true;
}
}
return false;
}
public static bool CanRevealCaster( Mobile m )
{
if ( m is BaseCreature )
{
BaseCreature c = (BaseCreature)m;
if ( !c.Controlled )
return true;
}
return false;
}
public static void GetSurfaceTop( ref IPoint3D p )
{
if ( p is Item )
{
p = ((Item)p).GetSurfaceTop();
}
else if ( p is StaticTarget )
{
StaticTarget t = (StaticTarget)p;
int z = t.Z;
if ( (t.Flags & TileFlag.Surface) == 0 )
z -= TileData.ItemTable[t.ItemID & TileData.MaxItemValue].CalcHeight;
p = new Point3D( t.X, t.Y, z );
}
}
public static bool AddStatOffset( Mobile m, StatType type, int offset, TimeSpan duration )
{
if ( offset > 0 )
return AddStatBonus( m, m, type, offset, duration );
else if ( offset < 0 )
return AddStatCurse( m, m, type, -offset, duration );
return true;
}
public static bool AddStatBonus( Mobile caster, Mobile target, StatType type )
{
return AddStatBonus( caster, target, type, GetOffset( caster, target, type, false ), GetDuration( caster, target ) );
}
public static bool AddStatBonus( Mobile caster, Mobile target, StatType type, int bonus, TimeSpan duration )
{
int offset = bonus;
string name = $"[Magic] {type} Offset";
StatMod mod = target.GetStatMod( name );
if ( mod != null && mod.Offset < 0 )
{
target.AddStatMod( new StatMod( type, name, mod.Offset + offset, duration ) );
return true;
}
else if ( mod == null || mod.Offset < offset )
{
target.AddStatMod( new StatMod( type, name, offset, duration ) );
return true;
}
return false;
}
public static bool AddStatCurse( Mobile caster, Mobile target, StatType type )
{
return AddStatCurse( caster, target, type, GetOffset( caster, target, type, true ), GetDuration( caster, target ) );
}
public static bool AddStatCurse( Mobile caster, Mobile target, StatType type, int curse, TimeSpan duration )
{
int offset = -curse;
string name = $"[Magic] {type} Offset";
StatMod mod = target.GetStatMod( name );
if ( mod != null && mod.Offset > 0 )
{
target.AddStatMod( new StatMod( type, name, mod.Offset + offset, duration ) );
return true;
}
else if ( mod == null || mod.Offset > offset )
{
target.AddStatMod( new StatMod( type, name, offset, duration ) );
return true;
}
return false;
}
public static TimeSpan GetDuration( Mobile caster, Mobile target )
{
if ( Core.AOS )
return TimeSpan.FromSeconds( ((6 * caster.Skills.EvalInt.Fixed) / 50) + 1 );
return TimeSpan.FromSeconds( caster.Skills[SkillName.Magery].Value * 1.2 );
}
private static bool m_DisableSkillCheck;
public static bool DisableSkillCheck
{
get => m_DisableSkillCheck;
set => m_DisableSkillCheck = value;
}
public static double GetOffsetScalar( Mobile caster, Mobile target, bool curse )
{
double percent;
if ( curse )
percent = 8 + (caster.Skills.EvalInt.Fixed / 100) - (target.Skills.MagicResist.Fixed / 100);
else
percent = 1 + (caster.Skills.EvalInt.Fixed / 100);
percent *= 0.01;
if ( percent < 0 )
percent = 0;
return percent;
}
public static int GetOffset( Mobile caster, Mobile target, StatType type, bool curse )
{
if ( Core.AOS )
{
if ( !m_DisableSkillCheck )
{
caster.CheckSkill( SkillName.EvalInt, 0.0, 120.0 );
if ( curse )
target.CheckSkill( SkillName.MagicResist, 0.0, 120.0 );
}
double 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);
}
}
return 1 + (int)(caster.Skills[SkillName.Magery].Value * 0.1);
}
public static Guild GetGuildFor( Mobile m )
{
Guild g = m.Guild as Guild;
if ( g == null && m is BaseCreature )
{
BaseCreature c = (BaseCreature)m;
m = c.ControlMaster;
if ( m != null )
g = m.Guild as Guild;
if ( g == null )
{
m = c.SummonMaster;
if ( m != null )
g = m.Guild as Guild;
}
}
return g;
}
public static bool ValidIndirectTarget( Mobile from, Mobile to )
{
if ( from == to )
return true;
if ( to.Hidden && to.AccessLevel > from.AccessLevel )
return false;
#region Dueling
PlayerMobile pmFrom = from as PlayerMobile;
PlayerMobile pmTarg = to as PlayerMobile;
if ( pmFrom == null && from is BaseCreature )
{
BaseCreature bcFrom = (BaseCreature) from;
if ( bcFrom.Summoned )
pmFrom = bcFrom.SummonMaster as PlayerMobile;
}
if ( pmTarg == null && to is BaseCreature )
{
BaseCreature bcTarg = (BaseCreature) to;
if ( bcTarg.Summoned )
pmTarg = bcTarg.SummonMaster as PlayerMobile;
}
if ( pmFrom != null && pmTarg != null )
{
if ( pmFrom.DuelContext != null && pmFrom.DuelContext == pmTarg.DuelContext && pmFrom.DuelContext.Started && pmFrom.DuelPlayer != null && pmTarg.DuelPlayer != null )
return ( pmFrom.DuelPlayer.Participant != pmTarg.DuelPlayer.Participant );
}
#endregion
Guild fromGuild = GetGuildFor( from );
Guild toGuild = GetGuildFor( to );
if ( fromGuild != null && toGuild != null && (fromGuild == toGuild || fromGuild.IsAlly( toGuild )) )
return false;
Party p = Party.Get( from );
if ( p != null && p.Contains( to ) )
return false;
if ( to is BaseCreature )
{
BaseCreature c = (BaseCreature)to;
if ( c.Controlled || c.Summoned )
{
if ( c.ControlMaster == from || c.SummonMaster == from )
return false;
if ( p != null && (p.Contains( c.ControlMaster ) || p.Contains( c.SummonMaster )) )
return false;
}
}
if ( from is BaseCreature )
{
BaseCreature c = (BaseCreature)from;
if ( c.Controlled || c.Summoned )
{
if ( c.ControlMaster == to || c.SummonMaster == to )
return false;
p = Party.Get( to );
if ( p != null && (p.Contains( c.ControlMaster ) || p.Contains( c.SummonMaster )) )
return false;
}
}
if ( to is BaseCreature && !((BaseCreature)to).Controlled && ((BaseCreature)to).InitialInnocent )
return true;
int noto = Notoriety.Compute( from, to );
return (noto != Notoriety.Innocent || from.Kills >= 5);
}
private static int[] m_Offsets = {
-1, -1,
-1, 0,
-1, 1,
0, -1,
0, 1,
1, -1,
1, 0,
1, 1
};
public static void Summon( BaseCreature creature, Mobile caster, int sound, TimeSpan duration, bool scaleDuration, bool scaleStats )
{
Map map = caster.Map;
if ( map == null )
return;
double scale = 1.0 + ((caster.Skills[SkillName.Magery].Value - 100.0) / 200.0);
if ( scaleDuration )
duration = TimeSpan.FromSeconds( duration.TotalSeconds * scale );
if ( scaleStats )
{
creature.RawStr = (int)(creature.RawStr * scale);
creature.Hits = creature.HitsMax;
creature.RawDex = (int)(creature.RawDex * scale);
creature.Stam = creature.StamMax;
creature.RawInt = (int)(creature.RawInt * scale);
creature.Mana = creature.ManaMax;
}
Point3D p = new Point3D( caster );
if ( FindValidSpawnLocation( map, ref p, true ) )
{
BaseCreature.Summon( creature, caster, p, sound, duration );
return;
}
/*
int offset = Utility.Random( 8 ) * 2;
for( int i = 0; i < m_Offsets.Length; i += 2 )
{
int x = caster.X + m_Offsets[(offset + i) % m_Offsets.Length];
int y = caster.Y + m_Offsets[(offset + i + 1) % m_Offsets.Length];
if ( map.CanSpawnMobile( x, y, caster.Z ) )
{
BaseCreature.Summon( creature, caster, new Point3D( x, y, caster.Z ), sound, duration );
return;
}
else
{
int z = map.GetAverageZ( x, y );
if ( map.CanSpawnMobile( x, y, z ) )
{
BaseCreature.Summon( creature, caster, new Point3D( x, y, z ), sound, duration );
return;
}
}
}
* */
creature.Delete();
caster.SendLocalizedMessage( 501942 ); // That location is blocked.
}
public static bool FindValidSpawnLocation( Map map, ref Point3D p, bool surroundingsOnly )
{
if ( map == null ) //sanity
return false;
if ( !surroundingsOnly )
{
if ( map.CanSpawnMobile( p ) ) //p's fine.
{
p = new Point3D( p );
return true;
}
int z = map.GetAverageZ( p.X, p.Y );
if ( map.CanSpawnMobile( p.X, p.Y, z ) )
{
p = new Point3D( p.X, p.Y, z );
return true;
}
}
int offset = Utility.Random( 8 ) * 2;
for( int i = 0; i < m_Offsets.Length; i += 2 )
{
int x = p.X + m_Offsets[(offset + i) % m_Offsets.Length];
int y = p.Y + m_Offsets[(offset + i + 1) % m_Offsets.Length];
if ( map.CanSpawnMobile( x, y, p.Z ) )
{
p = new Point3D( x, y, p.Z );
return true;
}
else
{
int z = map.GetAverageZ( x, y );
if ( map.CanSpawnMobile( x, y, z ) )
{
p = new Point3D( x, y, z );
return true;
}
}
}
return false;
}
private delegate bool TravelValidator( Map map, Point3D loc );
private static TravelValidator[] m_Validators = {
IsFeluccaT2A,
IsKhaldun,
IsIlshenar,
IsTrammelWind,
IsFeluccaWind,
IsFeluccaDungeon,
IsTrammelSolenHive,
IsFeluccaSolenHive,
IsCrystalCave,
IsDoomGauntlet,
IsDoomFerry,
IsSafeZone,
IsFactionStronghold,
IsChampionSpawn,
IsTokunoDungeon,
IsLampRoom,
IsGuardianRoom,
IsHeartwood,
IsMLDungeon
};
private static bool[,] m_Rules = {
/*T2A(Fel), Khaldun, Ilshenar, Wind(Tram), Wind(Fel), Dungeons(Fel), Solen(Tram), Solen(Fel), CrystalCave(Malas), Gauntlet(Malas), Gauntlet(Ferry), SafeZone, Stronghold, ChampionSpawn, Dungeons(Tokuno[Malas]), LampRoom(Doom), GuardianRoom(Doom), Heartwood, MLDungeons */
/* Recall From */ { false, false, true, true, false, false, true, false, false, false, false, true, true, false, true, false, false, false, false },
/* Recall To */ { false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false },
/* Gate From */ { false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false },
/* Gate To */ { false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false },
/* Mark In */ { false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false },
/* Tele From */ { true, true, true, true, true, true, true, true, false, true, true, true, false, true, true, true, true, false, true },
/* Tele To */ { true, true, true, true, true, true, true, true, false, true, false, false, false, true, true, true, true, false, false },
};
public static void SendInvalidMessage( Mobile caster, TravelCheckType type )
{
if ( type == TravelCheckType.RecallTo || type == TravelCheckType.GateTo )
caster.SendLocalizedMessage( 1019004 ); // You are not allowed to travel there.
else if ( type == TravelCheckType.TeleportTo )
caster.SendLocalizedMessage( 501035 ); // You cannot teleport from here to the destination.
else
caster.SendLocalizedMessage( 501802 ); // Thy spell doth not appear to work...
}
public static bool CheckTravel( Mobile caster, TravelCheckType type )
{
return CheckTravel( caster, caster.Map, caster.Location, type );
}
public static bool CheckTravel( Map map, Point3D loc, TravelCheckType type )
{
return CheckTravel( null, map, loc, type );
}
private static Mobile m_TravelCaster;
private static TravelCheckType m_TravelType;
public static bool CheckTravel( Mobile caster, Map map, Point3D loc, TravelCheckType type )
{
if ( IsInvalid( map, loc ) ) // null, internal, out of bounds
{
if ( caster != null )
SendInvalidMessage( caster, type );
return false;
}
if ( caster != null && caster.AccessLevel == AccessLevel.Player && caster.Region.IsPartOf( typeof( Jail ) ) )
{
caster.SendLocalizedMessage( 1114345 ); // You'll need a better jailbreak plan than that!
return false;
}
// Always allow monsters to teleport
if ( caster is BaseCreature && ( type == TravelCheckType.TeleportTo || type == TravelCheckType.TeleportFrom ) )
{
BaseCreature bc = (BaseCreature)caster;
if ( !bc.Controlled && !bc.Summoned )
return true;
}
m_TravelCaster = caster;
m_TravelType = type;
int v = (int)type;
bool isValid = true;
for( int i = 0; isValid && i < m_Validators.Length; ++i )
isValid = (m_Rules[v, i] || !m_Validators[i]( map, loc ));
if ( !isValid && caster != null )
SendInvalidMessage( caster, type );
return isValid;
}
public static bool IsWindLoc( Point3D loc )
{
int x = loc.X, y = loc.Y;
return (x >= 5120 && y >= 0 && x < 5376 && y < 256);
}
public static bool IsFeluccaWind( Map map, Point3D loc )
{
return (map == Map.Felucca && IsWindLoc( loc ));
}
public static bool IsTrammelWind( Map map, Point3D loc )
{
return (map == Map.Trammel && IsWindLoc( loc ));
}
public static bool IsIlshenar( Map map, Point3D loc )
{
return (map == Map.Ilshenar);
}
public static bool IsSolenHiveLoc( Point3D loc )
{
int x = loc.X, y = loc.Y;
return (x >= 5640 && y >= 1776 && x < 5935 && y < 2039);
}
public static bool IsTrammelSolenHive( Map map, Point3D loc )
{
return (map == Map.Trammel && IsSolenHiveLoc( loc ));
}
public static bool IsFeluccaSolenHive( Map map, Point3D loc )
{
return (map == Map.Felucca && IsSolenHiveLoc( loc ));
}
public static bool IsFeluccaT2A( Map map, Point3D loc )
{
int x = loc.X, y = loc.Y;
return (map == Map.Felucca && x >= 5120 && y >= 2304 && x < 6144 && y < 4096);
}
public static bool IsAnyT2A( Map map, Point3D loc )
{
int x = loc.X, y = loc.Y;
return ((map == Map.Trammel || map == Map.Felucca) && x >= 5120 && y >= 2304 && x < 6144 && y < 4096);
}
public static bool IsFeluccaDungeon( Map map, Point3D loc )
{
Region region = Region.Find( loc, map );
return (region.IsPartOf( typeof( DungeonRegion ) ) && region.Map == Map.Felucca);
}
public static bool IsKhaldun( Map map, Point3D loc )
{
return ( Region.Find( loc, map ).Name == "Khaldun" );
}
public static bool IsCrystalCave( Map map, Point3D loc )
{
if ( map != Map.Malas || loc.Z >= -80 )
return false;
int x = loc.X, y = loc.Y;
return ( x >= 1182 && y >= 437 && x < 1211 && y < 470 )
|| ( x >= 1156 && y >= 470 && x < 1211 && y < 503 )
|| ( x >= 1176 && y >= 503 && x < 1208 && y < 509 )
|| ( x >= 1188 && y >= 509 && x < 1201 && y < 513 );
}
public static bool IsSafeZone( Map map, Point3D loc )
{
#region Duels
if ( Region.Find( loc, map ).IsPartOf( typeof( Engines.ConPVP.SafeZone ) ) )
{
if ( m_TravelType == TravelCheckType.TeleportTo || m_TravelType == TravelCheckType.TeleportFrom )
{
PlayerMobile pm = m_TravelCaster as PlayerMobile;
if ( pm?.DuelPlayer != null && !pm.DuelPlayer.Eliminated )
return true;
}
return true;
}
#endregion
return false;
}
public static bool IsFactionStronghold( Map map, Point3D loc )
{
/*// Teleporting is allowed, but only for faction members
if ( !Core.AOS && m_TravelCaster != null && (m_TravelType == TravelCheckType.TeleportTo || m_TravelType == TravelCheckType.TeleportFrom) )
{
if ( Factions.Faction.Find( m_TravelCaster, true, true ) != null )
return false;
}*/
return (Region.Find( loc, map ).IsPartOf( typeof( Factions.StrongholdRegion ) ));
}
public static bool IsChampionSpawn( Map map, Point3D loc )
{
return (Region.Find( loc, map ).IsPartOf( typeof( Engines.CannedEvil.ChampionSpawnRegion ) ));
}
public static bool IsDoomFerry( Map map, Point3D loc )
{
if ( map != Map.Malas )
return false;
int x = loc.X, y = loc.Y;
if ( x >= 426 && y >= 314 && x <= 430 && y <= 331 )
return true;
if ( x >= 406 && y >= 247 && x <= 410 && y <= 264 )
return true;
return false;
}
public static bool IsTokunoDungeon( Map map, Point3D loc )
{
//The tokuno dungeons are really inside malas
if ( map != Map.Malas )
return false;
int x = loc.X, y = loc.Y, z = loc.Z;
bool r1 = (x >= 0 && y >= 0 && x <= 128 && y <= 128);
bool r2 = (x >= 45 && y >= 320 && x < 195 && y < 710);
return (r1 || r2);
}
public static bool IsDoomGauntlet( Map map, Point3D loc )
{
if ( map != Map.Malas )
return false;
int x = loc.X - 256, y = loc.Y - 304;
return (x >= 0 && y >= 0 && x < 256 && y < 256);
}
public static bool IsLampRoom( Map map, Point3D loc )
{
if ( map != Map.Malas )
return false;
int x = loc.X, y = loc.Y;
return ( x >= 465 && y >= 92 && x < 474 && y < 102 );
}
public static bool IsGuardianRoom( Map map, Point3D loc )
{
if ( map != Map.Malas )
return false;
int x = loc.X, y = loc.Y;
return ( x >= 356 && y >= 5 && x < 375 && y < 25 );
}
public static bool IsHeartwood( Map map, Point3D loc )
{
int x = loc.X, y = loc.Y;
return (map == Map.Trammel || map == Map.Felucca) && (x >= 6911 && y >= 254 && x < 7167 && y < 511);
}
public static bool IsMLDungeon( Map map, Point3D loc )
{
return MondainsLegacy.IsMLRegion( Region.Find( loc, map ) );
}
public static bool IsInvalid( Map map, Point3D loc )
{
if ( map == null || map == Map.Internal )
return true;
int x = loc.X, y = loc.Y;
return (x < 0 || y < 0 || x >= map.Width || y >= map.Height);
}
//towns
public static bool IsTown( IPoint3D loc, Mobile caster )
{
if ( loc is Item )
loc = ((Item)loc).GetWorldLocation();
return IsTown( new Point3D( loc ), caster );
}
public static bool IsTown( Point3D loc, Mobile caster )
{
Map map = caster.Map;
if ( map == null )
return false;
#region Dueling
Engines.ConPVP.SafeZone sz = (Engines.ConPVP.SafeZone) Region.Find( loc, map ).GetRegion( typeof( Engines.ConPVP.SafeZone ) );
if ( sz != null )
{
PlayerMobile pm = (PlayerMobile) caster;
if ( pm?.DuelContext == null || !pm.DuelContext.Started || pm.DuelPlayer == null || pm.DuelPlayer.Eliminated )
return true;
}
#endregion
GuardedRegion reg = (GuardedRegion) Region.Find( loc, map ).GetRegion( typeof( GuardedRegion ) );
return (reg != null && !reg.IsDisabled());
}
public static bool CheckTown( IPoint3D loc, Mobile caster )
{
if ( loc is Item )
loc = ((Item)loc).GetWorldLocation();
return CheckTown( new Point3D( loc ), caster );
}
public static bool CheckTown( Point3D loc, Mobile caster )
{
if ( IsTown( loc, caster ) )
{
caster.SendLocalizedMessage( 500946 ); // You cannot cast this in town!
return false;
}
return true;
}
//magic reflection
public static void CheckReflect( int circle, Mobile caster, ref Mobile target )
{
CheckReflect( circle, ref caster, ref target );
}
public static void CheckReflect( int circle, ref Mobile caster, ref Mobile target )
{
if ( target.MagicDamageAbsorb > 0 )
{
++circle;
target.MagicDamageAbsorb -= circle;
// This order isn't very intuitive, but you have to nullify reflect before target gets switched
bool reflect = (target.MagicDamageAbsorb >= 0);
(target as BaseCreature)?.CheckReflect( caster, ref reflect );
if ( target.MagicDamageAbsorb <= 0 )
{
target.MagicDamageAbsorb = 0;
DefensiveSpell.Nullify( target );
}
if ( reflect )
{
target.FixedEffect( 0x37B9, 10, 5 );
Mobile temp = caster;
caster = target;
target = temp;
}
}
else if ( target is BaseCreature )
{
bool reflect = false;
((BaseCreature)target).CheckReflect( caster, ref reflect );
if ( reflect )
{
target.FixedEffect( 0x37B9, 10, 5 );
Mobile temp = caster;
caster = target;
target = temp;
}
}
}
public static void Damage( Spell spell, Mobile target, double damage )
{
TimeSpan ts = GetDamageDelayForSpell( spell );
Damage( spell, ts, target, spell.Caster, damage );
}
public static void Damage( TimeSpan delay, Mobile target, double damage )
{
Damage( delay, target, null, damage );
}
public static void Damage( TimeSpan delay, Mobile target, Mobile from, double damage )
{
Damage( null, delay, target, from, damage );
}
public static void Damage( Spell spell, TimeSpan delay, Mobile target, Mobile from, double damage )
{
int iDamage = (int)damage;
if ( delay == TimeSpan.Zero )
{
(@from as BaseCreature)?.AlterSpellDamageTo( target, ref iDamage );
(target as BaseCreature)?.AlterSpellDamageFrom( @from, ref iDamage );
target.Damage( iDamage, from );
}
else
{
new SpellDamageTimer( spell, target, from, iDamage, delay ).Start();
}
if ( target is BaseCreature && from != null && delay == TimeSpan.Zero )
{
BaseCreature c = (BaseCreature) target;
c.OnHarmfulSpell( from );
c.OnDamagedBySpell( from );
}
}
public static void Damage( Spell spell, Mobile target, double damage, int phys, int fire, int cold, int pois, int nrgy )
{
TimeSpan ts = GetDamageDelayForSpell( spell );
Damage( spell, ts, target, spell.Caster, damage, phys, fire, cold, pois, nrgy, DFAlgorithm.Standard );
}
public static void Damage( Spell spell, Mobile target, double damage, int phys, int fire, int cold, int pois, int nrgy, DFAlgorithm dfa )
{
TimeSpan ts = GetDamageDelayForSpell( spell );
Damage( spell, ts, target, spell.Caster, damage, phys, fire, cold, pois, nrgy, dfa );
}
public static void Damage( TimeSpan delay, Mobile target, double damage, int phys, int fire, int cold, int pois, int nrgy )
{
Damage( delay, target, null, damage, phys, fire, cold, pois, nrgy );
}
public static void Damage( TimeSpan delay, Mobile target, Mobile from, double damage, int phys, int fire, int cold, int pois, int nrgy )
{
Damage( delay, target, from, damage, phys, fire, cold, pois, nrgy, DFAlgorithm.Standard );
}
public static void Damage( TimeSpan delay, Mobile target, Mobile from, double damage, int phys, int fire, int cold, int pois, int nrgy, DFAlgorithm dfa )
{
Damage( null, delay, target, from, damage, phys, fire, cold, pois, nrgy, dfa );
}
public static void Damage( Spell spell, TimeSpan delay, Mobile target, Mobile from, double damage, int phys, int fire, int cold, int pois, int nrgy, DFAlgorithm dfa )
{
int iDamage = (int)damage;
if ( delay == TimeSpan.Zero )
{
(@from as BaseCreature)?.AlterSpellDamageTo( target, ref iDamage );
(target as BaseCreature)?.AlterSpellDamageFrom( @from, ref iDamage );
WeightOverloading.DFA = dfa;
int damageGiven = AOS.Damage( target, from, iDamage, phys, fire, cold, pois, nrgy );
if ( from != null ) // sanity check
{
DoLeech( damageGiven, from, target );
}
WeightOverloading.DFA = DFAlgorithm.Standard;
}
else
{
new SpellDamageTimerAOS( spell, target, from, iDamage, phys, fire, cold, pois, nrgy, delay, dfa ).Start();
}
if ( target is BaseCreature && from != null && delay == TimeSpan.Zero )
{
BaseCreature c = (BaseCreature) target;
c.OnHarmfulSpell( from );
c.OnDamagedBySpell( from );
}
}
public static void DoLeech( int damageGiven, Mobile from, Mobile target )
{
TransformContext context = TransformationSpellHelper.GetContext( from );
if ( context != null ) /* cleanup */
{
if ( context.Type == typeof( WraithFormSpell ) )
{
int wraithLeech = ( 5 + (int)( ( 15 * from.Skills.SpiritSpeak.Value ) / 100 ) ); // Wraith form gives 5-20% mana leech
int manaLeech = AOS.Scale( damageGiven, wraithLeech );
if ( manaLeech != 0 )
{
from.Mana += manaLeech;
from.PlaySound( 0x44D );
}
}
else if ( context.Type == typeof( VampiricEmbraceSpell ) )
{
from.Hits += AOS.Scale( damageGiven, 20 );
from.PlaySound( 0x44D );
}
}
}
public static void Heal( int amount, Mobile target, Mobile from )
{
Heal( amount, target, from, true );
}
public static void Heal( int amount, Mobile target, Mobile from, bool message )
{
//TODO: All Healing *spells* go through ArcaneEmpowerment
target.Heal( amount, from, message );
}
private class SpellDamageTimer : Timer
{
private Mobile m_Target, m_From;
private int m_Damage;
private Spell m_Spell;
public SpellDamageTimer( Spell s, Mobile target, Mobile from, int damage, TimeSpan delay )
: base( delay )
{
m_Target = target;
m_From = from;
m_Damage = damage;
m_Spell = s;
if ( m_Spell != null && m_Spell.DelayedDamage && !m_Spell.DelayedDamageStacking )
m_Spell.StartDelayedDamageContext( target, this );
Priority = TimerPriority.TwentyFiveMS;
}
protected override void OnTick()
{
(m_From as BaseCreature)?.AlterSpellDamageTo( m_Target, ref m_Damage );
(m_Target as BaseCreature)?.AlterSpellDamageFrom( m_From, ref m_Damage );
m_Target.Damage( m_Damage );
m_Spell?.RemoveDelayedDamageContext( m_Target );
}
}
private class SpellDamageTimerAOS : Timer
{
private Mobile m_Target, m_From;
private int m_Damage;
private int m_Phys, m_Fire, m_Cold, m_Pois, m_Nrgy;
private DFAlgorithm m_DFA;
private Spell m_Spell;
public SpellDamageTimerAOS( Spell s, Mobile target, Mobile from, int damage, int phys, int fire, int cold, int pois, int nrgy, TimeSpan delay, DFAlgorithm dfa )
: base( delay )
{
m_Target = target;
m_From = from;
m_Damage = damage;
m_Phys = phys;
m_Fire = fire;
m_Cold = cold;
m_Pois = pois;
m_Nrgy = nrgy;
m_DFA = dfa;
m_Spell = s;
if ( m_Spell != null && m_Spell.DelayedDamage && !m_Spell.DelayedDamageStacking )
m_Spell.StartDelayedDamageContext( target, this );
Priority = TimerPriority.TwentyFiveMS;
}
protected override void OnTick()
{
if ( m_From is BaseCreature && m_Target != null )
((BaseCreature)m_From).AlterSpellDamageTo( m_Target, ref m_Damage );
if ( m_Target is BaseCreature && m_From != null )
((BaseCreature)m_Target).AlterSpellDamageFrom( m_From, ref m_Damage );
WeightOverloading.DFA = m_DFA;
int damageGiven = AOS.Damage( m_Target, m_From, m_Damage, m_Phys, m_Fire, m_Cold, m_Pois, m_Nrgy );
if ( m_From != null ) // sanity check
{
DoLeech( damageGiven, m_From, m_Target );
}
WeightOverloading.DFA = DFAlgorithm.Standard;
if ( m_Target is BaseCreature && m_From != null )
{
BaseCreature c = (BaseCreature) m_Target;
c.OnHarmfulSpell( m_From );
c.OnDamagedBySpell( m_From );
}
m_Spell?.RemoveDelayedDamageContext( m_Target );
}
}
}
public class TransformationSpellHelper
{
#region Context Stuff
private static Dictionary<Mobile, TransformContext> m_Table = new Dictionary<Mobile, TransformContext>();
public static void AddContext( Mobile m, TransformContext context )
{
m_Table[m] = context;
}
public static void RemoveContext( Mobile m, bool resetGraphics )
{
TransformContext context = GetContext( m );
if ( context != null )
RemoveContext( m, context, resetGraphics );
}
public static void RemoveContext( Mobile m, TransformContext context, bool resetGraphics )
{
if ( m_Table.ContainsKey( m ) )
{
m_Table.Remove( m );
List<ResistanceMod> mods = context.Mods;
for( int i = 0; i < mods.Count; ++i )
m.RemoveResistanceMod( mods[i] );
if ( resetGraphics )
{
m.HueMod = -1;
m.BodyMod = 0;
}
context.Timer.Stop();
context.Spell.RemoveEffect( m );
}
}
public static TransformContext GetContext( Mobile m )
{
m_Table.TryGetValue( m, out TransformContext context );
return context;
}
public static bool UnderTransformation( Mobile m )
{
return (GetContext( m ) != null);
}
public static bool UnderTransformation( Mobile m, Type type )
{
TransformContext context = GetContext( m );
return (context != null && context.Type == type);
}
#endregion
public static bool CheckCast( Mobile caster, Spell spell )
{
if ( Factions.Sigil.ExistsOn( caster ) )
{
caster.SendLocalizedMessage( 1061632 ); // You can't do that while carrying the sigil.
return false;
}
else if ( !caster.CanBeginAction( typeof( PolymorphSpell ) ) )
{
caster.SendLocalizedMessage( 1061628 ); // You can't do that while polymorphed.
return false;
}
else if ( AnimalForm.UnderTransformation( caster ) )
{
caster.SendLocalizedMessage( 1061091 ); // You cannot cast that spell in this form.
return false;
}
return true;
}
public static bool OnCast( Mobile caster, Spell spell )
{
ITransformationSpell transformSpell = spell as ITransformationSpell;
if ( transformSpell == null )
return false;
if ( Factions.Sigil.ExistsOn( caster ) )
{
caster.SendLocalizedMessage( 1061632 ); // You can't do that while carrying the sigil.
}
else if ( !caster.CanBeginAction( typeof( PolymorphSpell ) ) )
{
caster.SendLocalizedMessage( 1061628 ); // You can't do that while polymorphed.
}
else if ( DisguiseTimers.IsDisguised( caster ) )
{
caster.SendLocalizedMessage( 1061631 ); // You can't do that while disguised.
return false;
}
else if ( AnimalForm.UnderTransformation( caster ) )
{
caster.SendLocalizedMessage( 1061091 ); // You cannot cast that spell in this form.
}
else if ( !caster.CanBeginAction( typeof( IncognitoSpell ) ) || (caster.IsBodyMod && GetContext( caster ) == null) )
{
spell.DoFizzle();
}
else if ( spell.CheckSequence() )
{
TransformContext context = GetContext( caster );
Type ourType = spell.GetType();
bool wasTransformed = (context != null);
bool ourTransform = (wasTransformed && context.Type == ourType);
if ( wasTransformed )
{
RemoveContext( caster, context, ourTransform );
if ( ourTransform )
{
caster.PlaySound( 0xFA );
caster.FixedParticles( 0x3728, 1, 13, 5042, EffectLayer.Waist );
}
}
if ( !ourTransform )
{
List<ResistanceMod> mods = new List<ResistanceMod>();
if ( transformSpell.PhysResistOffset != 0 )
mods.Add( new ResistanceMod( ResistanceType.Physical, transformSpell.PhysResistOffset ) );
if ( transformSpell.FireResistOffset != 0 )
mods.Add( new ResistanceMod( ResistanceType.Fire, transformSpell.FireResistOffset ) );
if ( transformSpell.ColdResistOffset != 0 )
mods.Add( new ResistanceMod( ResistanceType.Cold, transformSpell.ColdResistOffset ) );
if ( transformSpell.PoisResistOffset != 0 )
mods.Add( new ResistanceMod( ResistanceType.Poison, transformSpell.PoisResistOffset ) );
if ( transformSpell.NrgyResistOffset != 0 )
mods.Add( new ResistanceMod( ResistanceType.Energy, transformSpell.NrgyResistOffset ) );
if ( !((Body)transformSpell.Body).IsHuman )
{
IMount mt = caster.Mount;
if ( mt != null )
mt.Rider = null;
}
caster.BodyMod = transformSpell.Body;
caster.HueMod = transformSpell.Hue;
for( int i = 0; i < mods.Count; ++i )
caster.AddResistanceMod( mods[i] );
transformSpell.DoEffect( caster );
Timer timer = new TransformTimer( caster, transformSpell );
timer.Start();
AddContext( caster, new TransformContext( timer, mods, ourType, transformSpell ) );
return true;
}
}
return false;
}
}
public interface ITransformationSpell
{
int Body { get; }
int Hue { get; }
int PhysResistOffset { get; }
int FireResistOffset { get; }
int ColdResistOffset { get; }
int PoisResistOffset { get; }
int NrgyResistOffset { get; }
double TickRate { get; }
void OnTick( Mobile m );
void DoEffect( Mobile m );
void RemoveEffect( Mobile m );
}
public class TransformContext
{
private Timer m_Timer;
private List<ResistanceMod> m_Mods;
private Type m_Type;
private ITransformationSpell m_Spell;
public Timer Timer => m_Timer;
public List<ResistanceMod> Mods => m_Mods;
public Type Type => m_Type;
public ITransformationSpell Spell => m_Spell;
public TransformContext( Timer timer, List<ResistanceMod> mods, Type type, ITransformationSpell spell )
{
m_Timer = timer;
m_Mods = mods;
m_Type = type;
m_Spell = spell;
}
}
public class TransformTimer : Timer
{
private Mobile m_Mobile;
private ITransformationSpell m_Spell;
public TransformTimer( Mobile from, ITransformationSpell spell )
: base( TimeSpan.FromSeconds( spell.TickRate ), TimeSpan.FromSeconds( spell.TickRate ) )
{
m_Mobile = from;
m_Spell = spell;
Priority = TimerPriority.TwoFiftyMS;
}
protected override void OnTick()
{
if ( m_Mobile.Deleted || !m_Mobile.Alive || m_Mobile.Body != m_Spell.Body || m_Mobile.Hue != m_Spell.Hue )
{
TransformationSpellHelper.RemoveContext( m_Mobile, true );
Stop();
}
else
{
m_Spell.OnTick( m_Mobile );
}
}
}
}