feat: implement Rising Colossus
This commit is contained in:
parent
0f2d83eb3d
commit
c83fb6fe1d
5 changed files with 579 additions and 1 deletions
|
|
@ -0,0 +1,381 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Server.Items;
|
||||
using Server.Mobiles;
|
||||
using Server.Spells;
|
||||
using Server.Spells.Mysticism;
|
||||
using Xunit;
|
||||
|
||||
namespace Server.Tests.Spells.Mysticism;
|
||||
|
||||
[Collection("Sequential UOContent Tests")]
|
||||
public class RisingColossusSpellTests
|
||||
{
|
||||
static RisingColossusSpellTests()
|
||||
{
|
||||
NPCSpeeds.RegisterSpeed(new NPCSpeeds.SpeedClassEntry
|
||||
{
|
||||
Level = SpeedLevel.Medium,
|
||||
ActiveSpeed = 0.25,
|
||||
PassiveSpeed = 0.5,
|
||||
Types = [typeof(RisingColossus)]
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SpellMetadata_MatchesRisingColossusSources()
|
||||
{
|
||||
var caster = NewCaster();
|
||||
var spell = new RisingColossusSpell(caster);
|
||||
|
||||
Assert.Equal("Rising Colossus", spell.Name);
|
||||
Assert.Equal("Kal Vas Xen Corp Ylem", spell.Mantra);
|
||||
Assert.Equal(SpellCircle.Eighth, spell.Circle);
|
||||
Assert.Equal(TimeSpan.FromSeconds(2.25), spell.CastDelayBase);
|
||||
Assert.Equal(50, spell.GetMana());
|
||||
Assert.Equal(83.0, spell.RequiredSkill);
|
||||
Assert.Equal(SkillName.Mysticism, spell.CastSkill);
|
||||
Assert.Equal(
|
||||
[Reagent.DaemonBone, Reagent.DragonsBlood, Reagent.FertileDirt, Reagent.Nightshade],
|
||||
spell.Reagents
|
||||
);
|
||||
|
||||
caster.Delete();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MysticSpellbookAndScroll_UseRisingColossusSlot()
|
||||
{
|
||||
var book = new MysticSpellbook(1UL << (692 - 677));
|
||||
var scroll = new RisingColossusScroll();
|
||||
|
||||
Assert.Equal(677, book.BookOffset);
|
||||
Assert.Equal(16, book.BookCount);
|
||||
Assert.True(book.HasSpell(692));
|
||||
Assert.Equal(692, GetSpellScrollId(scroll));
|
||||
|
||||
book.Delete();
|
||||
scroll.Delete();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RegisterMysticism_PreSaDoesNotExposeRisingColossus()
|
||||
{
|
||||
var previousExpansion = Core.Expansion;
|
||||
var caster = NewCaster();
|
||||
|
||||
try
|
||||
{
|
||||
ResetSpellRegistry();
|
||||
Core.Expansion = Expansion.ML;
|
||||
Initializer.Configure();
|
||||
|
||||
Assert.Null(SpellRegistry.NewSpell(692, caster, null));
|
||||
Assert.Equal(-1, SpellRegistry.GetRegistryNumber(typeof(RisingColossusSpell)));
|
||||
}
|
||||
finally
|
||||
{
|
||||
Core.Expansion = previousExpansion;
|
||||
ResetSpellRegistry();
|
||||
Initializer.Configure();
|
||||
caster.Delete();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RegisterMysticism_SaExposesRisingColossusAtSpellId692()
|
||||
{
|
||||
var previousExpansion = Core.Expansion;
|
||||
var caster = NewCaster();
|
||||
|
||||
try
|
||||
{
|
||||
ResetSpellRegistry();
|
||||
Core.Expansion = Expansion.SA;
|
||||
Initializer.Configure();
|
||||
|
||||
Assert.Same(typeof(RisingColossusSpell), SpellRegistry.Types[692]);
|
||||
Assert.Equal(692, SpellRegistry.GetRegistryNumber(typeof(RisingColossusSpell)));
|
||||
Assert.IsType<RisingColossusSpell>(SpellRegistry.NewSpell(692, caster, null));
|
||||
}
|
||||
finally
|
||||
{
|
||||
Core.Expansion = previousExpansion;
|
||||
ResetSpellRegistry();
|
||||
Initializer.Configure();
|
||||
caster.Delete();
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(83.0, 0.0, 27.6666666667)]
|
||||
[InlineData(120.0, 0.0, 40.0)]
|
||||
[InlineData(120.0, 120.0, 60.0)]
|
||||
public void Duration_UsesMysticismAndHigherSupportSkill(double mysticism, double supportSkill, double expectedSeconds)
|
||||
{
|
||||
Assert.Equal(TimeSpan.FromSeconds(expectedSeconds), RisingColossusSpell.GetDuration(mysticism, supportSkill));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Summon_UsesFiveFollowerSlotsAndSkillScaledPower()
|
||||
{
|
||||
var caster = NewCaster(mysticism: 120.0, focus: 120.0);
|
||||
var summon = new RisingColossus(caster, 120.0, 120.0);
|
||||
|
||||
Assert.Equal(5, summon.ControlSlots);
|
||||
Assert.Equal(780, summon.Str);
|
||||
Assert.Equal(210, summon.Dex);
|
||||
Assert.Equal(230, summon.Int);
|
||||
Assert.Equal(469, summon.HitsMax);
|
||||
Assert.Equal(20, summon.DamageMin);
|
||||
Assert.Equal(24, summon.DamageMax);
|
||||
Assert.Equal(120.0, summon.Skills.MagicResist.Value);
|
||||
Assert.Equal(120.0, summon.Skills.Tactics.Value);
|
||||
Assert.Equal(120.0, summon.Skills.Wrestling.Value);
|
||||
Assert.Equal(98.5, summon.DispelDifficulty);
|
||||
Assert.Equal(45.0, summon.DispelFocus);
|
||||
Assert.True(summon.BleedImmune);
|
||||
Assert.Equal(Poison.Lethal, summon.PoisonImmune);
|
||||
Assert.True(summon.AlwaysMurderer);
|
||||
|
||||
summon.Delete();
|
||||
caster.Delete();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckCast_RejectsInsufficientSkillManaAndFollowerCapacity()
|
||||
{
|
||||
var lowSkillCaster = NewCaster(mysticism: 82.9);
|
||||
var lowSkillSpell = new RisingColossusSpell(lowSkillCaster);
|
||||
Assert.False(lowSkillSpell.CheckCast());
|
||||
lowSkillCaster.Delete();
|
||||
|
||||
var lowManaCaster = NewCaster();
|
||||
lowManaCaster.Mana = 0;
|
||||
var lowManaSpell = new RisingColossusSpell(lowManaCaster);
|
||||
Assert.False(lowManaSpell.CheckCast());
|
||||
lowManaCaster.Delete();
|
||||
|
||||
var fullFollowerCaster = NewCaster();
|
||||
fullFollowerCaster.Followers = fullFollowerCaster.FollowersMax - 4;
|
||||
var fullFollowerSpell = new RisingColossusSpell(fullFollowerCaster);
|
||||
Assert.False(fullFollowerSpell.CheckCast());
|
||||
fullFollowerCaster.Delete();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TargetSelection_RanksIntelligentTargetsByProximity()
|
||||
{
|
||||
var caster = NewCaster();
|
||||
var summon = new RisingColossus(caster, 120.0, 120.0);
|
||||
var nearby = NewMobile(new Point3D(6201, 500, 0));
|
||||
var intelligent = NewMobile(new Point3D(6204, 500, 0));
|
||||
intelligent.Int = 120;
|
||||
intelligent.Skills.Magery.Base = 120.0;
|
||||
|
||||
Assert.True(summon.GetFightModeRanking(intelligent, FightMode.Closest, false) >
|
||||
summon.GetFightModeRanking(nearby, FightMode.Closest, false));
|
||||
|
||||
intelligent.Delete();
|
||||
nearby.Delete();
|
||||
summon.Delete();
|
||||
caster.Delete();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TargetLocation_RejectsHouseLocations()
|
||||
{
|
||||
var caster = NewCaster();
|
||||
var outside = new Point3D(6200, 500, 0);
|
||||
|
||||
Assert.False(RisingColossusSpell.IsHouseLocation(caster, outside, Map.Felucca));
|
||||
|
||||
caster.Delete();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SuccessfulTarget_ConsumesManaAndCanonicalReagentsAndCreatesSummon()
|
||||
{
|
||||
var previousExpansion = Core.Expansion;
|
||||
Core.Expansion = Expansion.SA;
|
||||
|
||||
var caster = NewCaster();
|
||||
AddReagents(caster);
|
||||
var summonLocation = FindSpawnLocation();
|
||||
caster.MoveToWorld(new Point3D(summonLocation.X - 1, summonLocation.Y, summonLocation.Z), Map.Felucca);
|
||||
var spell = new TestRisingColossusSpell(caster);
|
||||
caster.Spell = spell;
|
||||
spell.State = SpellState.Sequencing;
|
||||
|
||||
try
|
||||
{
|
||||
Assert.True(SpellHelper.CheckTown(summonLocation, caster));
|
||||
spell.Target(summonLocation);
|
||||
|
||||
var summon = FindSummon(caster);
|
||||
Assert.Equal(50, caster.Mana);
|
||||
Assert.Equal(5, caster.Followers);
|
||||
Assert.NotNull(summon);
|
||||
Assert.Equal(5, caster.Followers);
|
||||
Assert.Equal(50, caster.Mana);
|
||||
Assert.Equal(9, caster.Backpack.FindItemByType<DaemonBone>().Amount);
|
||||
Assert.Equal(9, caster.Backpack.FindItemByType<DragonsBlood>().Amount);
|
||||
Assert.Equal(9, caster.Backpack.FindItemByType<FertileDirt>().Amount);
|
||||
Assert.Equal(9, caster.Backpack.FindItemByType<Nightshade>().Amount);
|
||||
|
||||
summon.Delete();
|
||||
Assert.Equal(0, caster.Followers);
|
||||
}
|
||||
finally
|
||||
{
|
||||
spell.FinishSequence();
|
||||
caster.Delete();
|
||||
Core.Expansion = previousExpansion;
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BlockedTarget_DoesNotConsumeResourcesOrCreateSummon()
|
||||
{
|
||||
var previousExpansion = Core.Expansion;
|
||||
Core.Expansion = Expansion.SA;
|
||||
|
||||
var caster = NewCaster();
|
||||
AddReagents(caster);
|
||||
var summonLocation = FindSpawnLocation();
|
||||
var blockedLocation = FindBlockedLocation();
|
||||
caster.MoveToWorld(new Point3D(summonLocation.X - 1, summonLocation.Y, summonLocation.Z), Map.Felucca);
|
||||
var spell = new TestRisingColossusSpell(caster);
|
||||
caster.Spell = spell;
|
||||
spell.State = SpellState.Sequencing;
|
||||
|
||||
try
|
||||
{
|
||||
spell.Target(blockedLocation);
|
||||
|
||||
Assert.Equal(100, caster.Mana);
|
||||
Assert.Equal(0, caster.Followers);
|
||||
Assert.Equal(10, caster.Backpack.FindItemByType<DaemonBone>().Amount);
|
||||
Assert.Null(FindSummon(caster));
|
||||
}
|
||||
finally
|
||||
{
|
||||
spell.FinishSequence();
|
||||
caster.Delete();
|
||||
Core.Expansion = previousExpansion;
|
||||
}
|
||||
}
|
||||
|
||||
private static RisingColossus FindSummon(Mobile caster)
|
||||
{
|
||||
foreach (var mobile in caster.Map.GetMobilesInRange(caster.Location, 2))
|
||||
{
|
||||
if (mobile is RisingColossus summon && summon.SummonMaster == caster)
|
||||
{
|
||||
return summon;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static PlayerMobile NewCaster(double mysticism = 120.0, double focus = 120.0, double imbuing = 0.0)
|
||||
{
|
||||
var caster = new PlayerMobile(World.NewMobile);
|
||||
caster.DefaultMobileInit();
|
||||
caster.Player = true;
|
||||
caster.InitStats(100, 100, 100);
|
||||
caster.Mana = caster.ManaMax;
|
||||
caster.AddItem(new Backpack());
|
||||
caster.Skills.Mysticism.Base = mysticism;
|
||||
caster.Skills.Focus.Base = focus;
|
||||
caster.Skills.Imbuing.Base = imbuing;
|
||||
return caster;
|
||||
}
|
||||
|
||||
private static Mobile NewMobile(Point3D location)
|
||||
{
|
||||
var mobile = new Mobile(World.NewMobile);
|
||||
mobile.DefaultMobileInit();
|
||||
mobile.InitStats(100, 100, 100);
|
||||
mobile.MoveToWorld(location, Map.Felucca);
|
||||
return mobile;
|
||||
}
|
||||
|
||||
private static Point3D FindSpawnLocation()
|
||||
{
|
||||
for (var x = 6100; x < 6200; ++x)
|
||||
{
|
||||
for (var y = 400; y < 600; ++y)
|
||||
{
|
||||
var z = Map.Felucca.GetAverageZ(x, y);
|
||||
if (Map.Felucca.CanSpawnMobile(x, y, z))
|
||||
{
|
||||
return new Point3D(x, y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("No valid Felucca summon location was found for the test fixture.");
|
||||
}
|
||||
|
||||
private static Point3D FindBlockedLocation()
|
||||
{
|
||||
for (var x = 6200; x < 6300; ++x)
|
||||
{
|
||||
for (var y = 400; y < 600; ++y)
|
||||
{
|
||||
var z = Map.Felucca.GetAverageZ(x, y);
|
||||
if (!Map.Felucca.CanSpawnMobile(x, y, z))
|
||||
{
|
||||
return new Point3D(x, y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("No blocked Felucca summon location was found for the test fixture.");
|
||||
}
|
||||
|
||||
private static void AddReagents(Mobile caster)
|
||||
{
|
||||
caster.AddToBackpack(new DaemonBone(10));
|
||||
caster.AddToBackpack(new DragonsBlood(10));
|
||||
caster.AddToBackpack(new FertileDirt(10));
|
||||
caster.AddToBackpack(new Nightshade(10));
|
||||
}
|
||||
|
||||
private static int GetSpellScrollId(SpellScroll scroll)
|
||||
{
|
||||
var field = typeof(SpellScroll).GetField("_spellID", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
return (int)field!.GetValue(scroll)!;
|
||||
}
|
||||
|
||||
private static void ResetSpellRegistry()
|
||||
{
|
||||
var types = (Type[])typeof(SpellRegistry)
|
||||
.GetField("m_Types", BindingFlags.Static | BindingFlags.NonPublic)!
|
||||
.GetValue(null)!;
|
||||
Array.Clear(types);
|
||||
|
||||
var idsFromTypes = (Dictionary<Type, int>)typeof(SpellRegistry)
|
||||
.GetField("m_IDsFromTypes", BindingFlags.Static | BindingFlags.NonPublic)!
|
||||
.GetValue(null)!;
|
||||
idsFromTypes.Clear();
|
||||
|
||||
typeof(SpellRegistry)
|
||||
.GetField("m_Count", BindingFlags.Static | BindingFlags.NonPublic)!
|
||||
.SetValue(null, 0);
|
||||
|
||||
SpellRegistry.SpecialMoves.Clear();
|
||||
}
|
||||
|
||||
private sealed class TestRisingColossusSpell : RisingColossusSpell
|
||||
{
|
||||
public TestRisingColossusSpell(Mobile caster) : base(caster)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool CheckFizzle() => true;
|
||||
}
|
||||
}
|
||||
14
Projects/UOContent/Migrations/Server.Mobiles.RisingColossus.v0.json
generated
Normal file
14
Projects/UOContent/Migrations/Server.Mobiles.RisingColossus.v0.json
generated
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"version": 0,
|
||||
"type": "Server.Mobiles.RisingColossus",
|
||||
"properties": [
|
||||
{
|
||||
"name": "DispelDifficulty",
|
||||
"type": "double",
|
||||
"rule": "PrimitiveTypeMigrationRule",
|
||||
"ruleArguments": [
|
||||
""
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
using System;
|
||||
using ModernUO.Serialization;
|
||||
using Server.Items;
|
||||
|
||||
namespace Server.Mobiles;
|
||||
|
||||
[SerializationGenerator(0, false)]
|
||||
public partial class RisingColossus : BaseCreature
|
||||
{
|
||||
private double _dispelDifficulty;
|
||||
|
||||
[Constructible]
|
||||
public RisingColossus(Mobile caster, double mysticism, double supportSkill) :
|
||||
base(AIType.AI_Melee, FightMode.Closest, 10, 1)
|
||||
{
|
||||
var level = (int)(mysticism + supportSkill);
|
||||
var statBonus = (int)((mysticism - 83.0) / 1.3 + (supportSkill - 30.0) / 1.3 + 6.0);
|
||||
var hitsBonus = (int)((mysticism - 83.0) * 1.14 + (supportSkill - 30.0) * 1.03 + 20.0);
|
||||
var skillValue = supportSkill != 0.0 ? (mysticism + supportSkill) / 2.0 : (mysticism + 20.0) / 2.0;
|
||||
|
||||
Body = 829;
|
||||
|
||||
SetStr(677 + statBonus);
|
||||
SetDex(107 + statBonus);
|
||||
SetInt(127 + statBonus);
|
||||
|
||||
SetHits(315 + hitsBonus);
|
||||
SetStam(250);
|
||||
SetMana(0);
|
||||
SetDamage(level / 12, level / 10);
|
||||
|
||||
SetDamageType(ResistanceType.Physical, 100);
|
||||
|
||||
SetResistance(ResistanceType.Physical, 65, 70);
|
||||
SetResistance(ResistanceType.Fire, 50, 55);
|
||||
SetResistance(ResistanceType.Cold, 50, 55);
|
||||
SetResistance(ResistanceType.Poison, 100);
|
||||
SetResistance(ResistanceType.Energy, 65, 70);
|
||||
|
||||
SetSkill(SkillName.MagicResist, skillValue);
|
||||
SetSkill(SkillName.Tactics, skillValue);
|
||||
SetSkill(SkillName.Wrestling, skillValue);
|
||||
SetSkill(SkillName.Anatomy, skillValue);
|
||||
SetSkill(SkillName.EvalInt, skillValue);
|
||||
SetSkill(SkillName.DetectHidden, 70.0);
|
||||
SetSkill(SkillName.Mysticism, caster.Skills.Mysticism.Value);
|
||||
SetSkill(SkillName.Focus, caster.Skills.Focus.Value);
|
||||
SetSkill(SkillName.Imbuing, caster.Skills.Imbuing.Value);
|
||||
|
||||
Fame = 0;
|
||||
Karma = 0;
|
||||
VirtualArmor = 60;
|
||||
ControlSlots = 5;
|
||||
|
||||
// UO.com and ServUO document dispel counterplay but do not publish a
|
||||
// current numeric difficulty formula. Keep it in the normal 80-100
|
||||
// spell-skill range instead of inheriting ServUO's incompatible legacy
|
||||
// expression, which produces values above the current 0-200 scale.
|
||||
_dispelDifficulty = Math.Clamp(80.0 + (mysticism - 83.0) * 0.5, 80.0, 100.0);
|
||||
}
|
||||
|
||||
public RisingColossus(Serial serial) : base(serial)
|
||||
{
|
||||
}
|
||||
|
||||
[SerializableProperty(0, useField: nameof(_dispelDifficulty))]
|
||||
public override double DispelDifficulty => _dispelDifficulty;
|
||||
|
||||
public override double DispelFocus => 45.0;
|
||||
public override string DefaultName => "a rising colossus";
|
||||
public override string CorpseName => "a rising colossus corpse";
|
||||
public override bool DeleteCorpseOnDeath => Summoned;
|
||||
public override bool AlwaysMurderer => true;
|
||||
public override bool BleedImmune => true;
|
||||
public override Poison PoisonImmune => Poison.Lethal;
|
||||
public override bool FollowsAcquireRules =>
|
||||
Core.AOS || !Summoned || SummonMaster?.Player != true || Map != Map.Felucca;
|
||||
|
||||
public override double GetFightModeRanking(Mobile mobile, FightMode acqType, bool bPlayerOnly) =>
|
||||
(mobile.Int + mobile.Skills.Magery.Value) / Math.Max(this.GetDistanceToSqrt(mobile), 1.0);
|
||||
|
||||
public override WeaponAbility GetWeaponAbility() =>
|
||||
Utility.RandomBool() ? WeaponAbility.ArmorIgnore : WeaponAbility.CrushingBlow;
|
||||
|
||||
public override int GetAttackSound() => 0x627;
|
||||
public override int GetHurtSound() => 0x629;
|
||||
}
|
||||
|
|
@ -196,7 +196,7 @@ namespace Server.Spells
|
|||
Register(689, typeof(SpellPlagueSpell));
|
||||
Register(690, typeof(HailStormSpell));
|
||||
Register(691, typeof(NetherCycloneSpell));
|
||||
// Register(692, typeof(RisingColossusSpell));
|
||||
Register(692, typeof(RisingColossusSpell));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
96
Projects/UOContent/Spells/Mysticism/RisingColossusSpell.cs
Normal file
96
Projects/UOContent/Spells/Mysticism/RisingColossusSpell.cs
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
using System;
|
||||
using Server.Items;
|
||||
using Server.Mobiles;
|
||||
using Server.Regions;
|
||||
|
||||
namespace Server.Spells.Mysticism;
|
||||
|
||||
public class RisingColossusSpell : MysticSpell, ITargetingSpell<IPoint3D>
|
||||
{
|
||||
private const int ControlSlotCost = 5;
|
||||
|
||||
private static readonly SpellInfo _info = new(
|
||||
"Rising Colossus",
|
||||
"Kal Vas Xen Corp Ylem",
|
||||
230,
|
||||
9022,
|
||||
Reagent.DaemonBone,
|
||||
Reagent.DragonsBlood,
|
||||
Reagent.FertileDirt,
|
||||
Reagent.Nightshade
|
||||
);
|
||||
|
||||
public RisingColossusSpell(Mobile caster, Item scroll = null) : base(caster, scroll, _info)
|
||||
{
|
||||
}
|
||||
|
||||
public override SpellCircle Circle => SpellCircle.Eighth;
|
||||
|
||||
internal static TimeSpan GetDuration(double mysticism, double supportSkill)
|
||||
{
|
||||
// Public sources describe a duration of up to about 60 seconds. Capping the
|
||||
// combined-skill formula at that value avoids importing ServUO's longer
|
||||
// 120/120 duration as an undocumented RebirthUO balance decision.
|
||||
return TimeSpan.FromSeconds(Math.Min(60.0, Math.Max(0.0, mysticism + supportSkill) / 3.0));
|
||||
}
|
||||
|
||||
internal static bool IsHouseLocation(Mobile caster, Point3D location, Map map)
|
||||
{
|
||||
if (Region.Find(location, map).IsPartOf<HouseRegion>())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return caster.Map != null && Region.Find(caster.Location, caster.Map).IsPartOf<HouseRegion>();
|
||||
}
|
||||
|
||||
public override bool CheckCast()
|
||||
{
|
||||
if (!base.CheckCast())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Caster.Followers + ControlSlotCost > Caster.FollowersMax)
|
||||
{
|
||||
Caster.SendLocalizedMessage(1049645); // You have too many followers to summon that creature.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Target(IPoint3D point)
|
||||
{
|
||||
if (Caster.Followers + ControlSlotCost > Caster.FollowersMax)
|
||||
{
|
||||
Caster.SendLocalizedMessage(1049645); // You have too many followers to summon that creature.
|
||||
return;
|
||||
}
|
||||
|
||||
var map = Caster.Map;
|
||||
SpellHelper.GetSurfaceTop(ref point);
|
||||
|
||||
if (map?.CanSpawnMobile(point.X, point.Y, point.Z) != true || IsHouseLocation(Caster, new Point3D(point), map))
|
||||
{
|
||||
Caster.SendLocalizedMessage(501942); // That location is blocked.
|
||||
}
|
||||
else if (SpellHelper.CheckTown(point, Caster) && CheckSequence())
|
||||
{
|
||||
var mysticism = GetBaseSkill(Caster);
|
||||
var supportSkill = GetDamageSkill(Caster);
|
||||
var duration = GetDuration(mysticism, supportSkill);
|
||||
var summon = new RisingColossus(Caster, mysticism, supportSkill);
|
||||
|
||||
if (BaseCreature.Summon(summon, false, Caster, new Point3D(point), 0x656, duration))
|
||||
{
|
||||
Effects.SendTargetParticles(summon, 0x3728, 10, 10, 0x13AA, (EffectLayer)255);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
Caster.Target = new SpellTarget<IPoint3D>(this, allowGround: true, retryOnLos: true);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue