1034 lines
29 KiB
C#
1034 lines
29 KiB
C#
using Server;
|
|
using System;
|
|
using Server.Items;
|
|
using Server.Mobiles;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Server.Network;
|
|
|
|
namespace Server.Engines.Shadowguard
|
|
{
|
|
public class ShadowguardBoss : BaseCreature
|
|
{
|
|
public const int MaxSummons = 3;
|
|
|
|
public List<BaseCreature> SummonedHelpers { get; set; }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public bool IsLastBoss { get; set; }
|
|
|
|
private DateTime _NextSummon;
|
|
|
|
public virtual Type[] SummonTypes { get { return null; } }
|
|
public virtual Type[] ArtifactDrops { get { return _ArtifactTypes; } }
|
|
|
|
public virtual bool CanSummon { get { return Hits <= HitsMax - (HitsMax / 4); } }
|
|
|
|
private Type[] _ArtifactTypes = new Type[]
|
|
{
|
|
typeof(AnonsBoots), typeof(AnonsSpellbook), typeof(BalakaisShamanStaff),
|
|
typeof(EnchantressCameo), typeof(GrugorsShield), typeof(WamapsBoneEarrings),
|
|
typeof(HalawasHuntingBow), typeof(HawkwindsRobe), typeof(JumusSacredHide),
|
|
typeof(JuonarsGrimoire), typeof(LereisHuntingSpear), typeof(UnstableTimeRift),
|
|
typeof(MinaxsSandles), typeof(MocapotlsObsidianSword), typeof(OzymandiasObi),
|
|
typeof(ShantysWaders), typeof(TotemOfTheTribe), typeof(BalakaisShamanStaffGargoyle)
|
|
};
|
|
|
|
public ShadowguardBoss(AIType ai) : base(ai, FightMode.Closest, 10, 1, .15, .3)
|
|
{
|
|
_NextSummon = DateTime.UtcNow;
|
|
|
|
SetHits(9500, 10000);
|
|
SetMana(4500);
|
|
SetStam(250);
|
|
|
|
SetStr(225);
|
|
SetInt(225);
|
|
SetDex(250);
|
|
|
|
Fame = 32000;
|
|
Karma = -32000;
|
|
}
|
|
|
|
public override Poison PoisonImmune{ get { return Poison.Lethal; } }
|
|
public override bool AlwaysMurderer { get { return true; } }
|
|
|
|
public override void GenerateLoot()
|
|
{
|
|
if (IsLastBoss)
|
|
{
|
|
this.AddLoot(LootPack.SuperBoss, 7);
|
|
}
|
|
}
|
|
|
|
public ShadowguardBoss(Serial serial) : base(serial)
|
|
{
|
|
}
|
|
|
|
public int TotalSummons()
|
|
{
|
|
if (SummonedHelpers == null || SummonedHelpers.Count == 0)
|
|
return 0;
|
|
|
|
return SummonedHelpers.Where(bc => bc != null && bc.Alive).Count();
|
|
}
|
|
|
|
public override void OnGotMeleeAttack(Mobile m)
|
|
{
|
|
if (m is PlayerMobile && CanSummon && !(m is GreaterDragon) && _NextSummon < DateTime.UtcNow)
|
|
Summon();
|
|
|
|
base.OnGotMeleeAttack(m);
|
|
}
|
|
|
|
public override void OnDamagedBySpell(Mobile m)
|
|
{
|
|
if (m is PlayerMobile && CanSummon && !(m is GreaterDragon) && _NextSummon < DateTime.UtcNow)
|
|
Summon();
|
|
|
|
base.OnDamagedBySpell(m);
|
|
}
|
|
|
|
public override void OnDeath(Container c)
|
|
{
|
|
if (IsLastBoss)
|
|
{
|
|
List<DamageStore> rights = GetLootingRights();
|
|
|
|
foreach (DamageStore ds in rights.Where(s => s.m_HasRight))
|
|
{
|
|
int luck = ds.m_Mobile is PlayerMobile ? ((PlayerMobile)ds.m_Mobile).RealLuck : ds.m_Mobile.Luck;
|
|
|
|
int chance = 1000 + (luck / 15);
|
|
|
|
if (chance > Utility.Random(5000))
|
|
{
|
|
Mobile m = ds.m_Mobile;
|
|
Item artifact = Loot.Construct(ArtifactDrops[Utility.Random(ArtifactDrops.Length)]);
|
|
|
|
if (artifact != null)
|
|
{
|
|
if (m.Backpack == null || !m.Backpack.TryDropItem(m, artifact, false))
|
|
{
|
|
m.BankBox.DropItem(artifact);
|
|
m.SendMessage("For your valor in combating the fallen beast, a special reward has been placed in your bank box.");
|
|
}
|
|
else
|
|
m.SendLocalizedMessage(1062317); // For your valor in combating the fallen beast, a special reward has been bestowed on you.
|
|
}
|
|
}
|
|
}
|
|
}
|
|
base.OnDeath(c);
|
|
}
|
|
|
|
public override bool OnBeforeDeath()
|
|
{
|
|
if (IsLastBoss)
|
|
{
|
|
DoGoldSpray(Map);
|
|
}
|
|
|
|
return base.OnBeforeDeath();
|
|
}
|
|
|
|
private void DoGoldSpray(Map map)
|
|
{
|
|
if (this.Map != null)
|
|
{
|
|
for (int x = -12; x <= 12; ++x)
|
|
{
|
|
for (int y = -12; y <= 12; ++y)
|
|
{
|
|
double dist = Math.Sqrt(x * x + y * y);
|
|
|
|
if (dist <= 12)
|
|
new GoodiesTimer(map, X + x, Y + y).Start();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private class GoodiesTimer : Timer
|
|
{
|
|
private Map m_Map;
|
|
private int m_X, m_Y;
|
|
|
|
public GoodiesTimer(Map map, int x, int y)
|
|
: base(TimeSpan.FromSeconds(Utility.RandomDouble() * 10.0))
|
|
{
|
|
m_Map = map;
|
|
m_X = x;
|
|
m_Y = y;
|
|
}
|
|
|
|
protected override void OnTick()
|
|
{
|
|
int z = m_Map.GetAverageZ(m_X, m_Y);
|
|
bool canFit = m_Map.CanFit(m_X, m_Y, z, 6, false, false);
|
|
|
|
for (int i = -3; !canFit && i <= 3; ++i)
|
|
{
|
|
canFit = m_Map.CanFit(m_X, m_Y, z + i, 6, false, false);
|
|
|
|
if (canFit)
|
|
z += i;
|
|
}
|
|
|
|
if (!canFit)
|
|
return;
|
|
|
|
Gold g = new Gold(500, 1000);
|
|
g.MoveToWorld(new Point3D(m_X, m_Y, z), m_Map);
|
|
|
|
if (0.5 >= Utility.RandomDouble())
|
|
{
|
|
switch (Utility.Random(3))
|
|
{
|
|
case 0: // Fire column
|
|
{
|
|
Effects.SendLocationParticles(EffectItem.Create(g.Location, g.Map, EffectItem.DefaultDuration), 0x3709, 10, 30, 5052);
|
|
Effects.PlaySound(g, g.Map, 0x208);
|
|
|
|
break;
|
|
}
|
|
case 1: // Explosion
|
|
{
|
|
Effects.SendLocationParticles(EffectItem.Create(g.Location, g.Map, EffectItem.DefaultDuration), 0x36BD, 20, 10, 5044);
|
|
Effects.PlaySound(g, g.Map, 0x307);
|
|
|
|
break;
|
|
}
|
|
case 2: // Ball of fire
|
|
{
|
|
Effects.SendLocationParticles(EffectItem.Create(g.Location, g.Map, EffectItem.DefaultDuration), 0x36FE, 10, 10, 5052);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public virtual void Summon()
|
|
{
|
|
int max = MaxSummons;
|
|
var map = Map;
|
|
|
|
ShadowguardEncounter inst = ShadowguardController.GetEncounter(this.Location, this.Map);
|
|
|
|
if(inst != null)
|
|
max += inst.PartySize() * 2;
|
|
|
|
if(map == null || this.SummonTypes == null || this.SummonTypes.Length == 0 || TotalSummons() > max)
|
|
return;
|
|
|
|
int count = Utility.RandomList(1, 2, 2, 2, 3, 3, 4, 5);
|
|
|
|
for(int i = 0; i < count; i++)
|
|
{
|
|
if (Combatant == null)
|
|
return;
|
|
|
|
Point3D p = Combatant.Location;
|
|
|
|
for(int j = 0; j < 10; j++)
|
|
{
|
|
int x = Utility.RandomMinMax(p.X - 3, p.X + 3);
|
|
int y = Utility.RandomMinMax(p.Y - 3, p.Y + 3);
|
|
int z = map.GetAverageZ(x, y);
|
|
|
|
if(map.CanSpawnMobile(x, y, z))
|
|
{
|
|
p = new Point3D(x, y, z);
|
|
break;
|
|
}
|
|
}
|
|
|
|
BaseCreature spawn = Activator.CreateInstance(SummonTypes[Utility.Random(SummonTypes.Length)]) as BaseCreature;
|
|
|
|
if(spawn != null)
|
|
{
|
|
spawn.MoveToWorld(p, map);
|
|
spawn.Team = this.Team;
|
|
spawn.SummonMaster = this;
|
|
|
|
Timer.DelayCall(TimeSpan.FromSeconds(1), (o) =>
|
|
{
|
|
BaseCreature s = o as BaseCreature;
|
|
|
|
if (s != null && s.Combatant != null)
|
|
{
|
|
if(!(s.Combatant is PlayerMobile) || !((PlayerMobile)s.Combatant).HonorActive)
|
|
s.Combatant = this.Combatant;
|
|
}
|
|
|
|
}, spawn);
|
|
|
|
AddHelper(spawn);
|
|
}
|
|
}
|
|
|
|
_NextSummon = DateTime.UtcNow + TimeSpan.FromSeconds(Utility.RandomMinMax(20, 40));
|
|
}
|
|
|
|
protected virtual void AddHelper(BaseCreature bc)
|
|
{
|
|
if (SummonedHelpers == null)
|
|
SummonedHelpers = new List<BaseCreature>();
|
|
|
|
if (!SummonedHelpers.Contains(bc))
|
|
SummonedHelpers.Add(bc);
|
|
}
|
|
|
|
public override void Delete()
|
|
{
|
|
base.Delete();
|
|
|
|
if (SummonedHelpers != null)
|
|
{
|
|
ColUtility.Free(SummonedHelpers);
|
|
}
|
|
}
|
|
|
|
public override void Serialize(GenericWriter writer)
|
|
{
|
|
base.Serialize(writer);
|
|
writer.Write(0);
|
|
|
|
writer.Write(IsLastBoss);
|
|
|
|
writer.Write(SummonedHelpers == null ? 0 : SummonedHelpers.Count);
|
|
|
|
if(SummonedHelpers != null)
|
|
SummonedHelpers.ForEach(m => writer.Write(m));
|
|
}
|
|
|
|
public override void Deserialize(GenericReader reader)
|
|
{
|
|
base.Deserialize(reader);
|
|
int version = reader.ReadInt();
|
|
|
|
IsLastBoss = reader.ReadBool();
|
|
|
|
int count = reader.ReadInt();
|
|
|
|
if(count > 0)
|
|
{
|
|
for(int i = 0; i < count; i++)
|
|
{
|
|
BaseCreature summon = reader.ReadMobile() as BaseCreature;
|
|
|
|
if(summon != null)
|
|
{
|
|
if(SummonedHelpers == null)
|
|
SummonedHelpers = new List<BaseCreature>();
|
|
|
|
SummonedHelpers.Add(summon);
|
|
}
|
|
}
|
|
}
|
|
|
|
_NextSummon = DateTime.UtcNow;
|
|
}
|
|
}
|
|
|
|
public enum Form
|
|
{
|
|
Human = 0x190,
|
|
Fire = 15,
|
|
Cold = 163,
|
|
Poison = 162,
|
|
Energy = 164
|
|
}
|
|
|
|
public class Anon : ShadowguardBoss
|
|
{
|
|
public override Type[] SummonTypes { get { return _SummonTypes; } }
|
|
private Type[] _SummonTypes = new Type[] { typeof(ElderGazer), typeof(EvilMage), typeof(Wisp) };
|
|
|
|
private DateTime _LastChange;
|
|
private Form _Form;
|
|
|
|
public bool CanChange { get { return _LastChange + TimeSpan.FromSeconds(Utility.RandomMinMax(75, 90)) < DateTime.UtcNow; } }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public Form Form
|
|
{
|
|
get { return _Form; }
|
|
set
|
|
{
|
|
Form old = _Form;
|
|
|
|
if(old != value)
|
|
{
|
|
_Form = value;
|
|
InvalidateForm();
|
|
_LastChange = DateTime.UtcNow;
|
|
}
|
|
}
|
|
}
|
|
|
|
[Constructable]
|
|
public Anon() : base(AIType.AI_Mage)
|
|
{
|
|
Name = "Anon";
|
|
Title = "the Mage";
|
|
|
|
Body = 0x190;
|
|
HairItemID = 0x203C;
|
|
|
|
Hue = Race.RandomSkinHue();
|
|
|
|
SetStam(100, 125);
|
|
SetMana(800, 900);
|
|
SetStr(150, 200);
|
|
SetInt(175, 200);
|
|
SetDex(100, 200);
|
|
|
|
SetDamage( 14, 17 );
|
|
|
|
SetDamageType(ResistanceType.Physical, 100);
|
|
|
|
SetSkill( SkillName.Wrestling, 220.0, 240.0 );
|
|
SetSkill( SkillName.Tactics, 110.0, 125.0 );
|
|
SetSkill( SkillName.MagicResist, 120.0 , 140.0);
|
|
SetSkill( SkillName.Magery, 120.0 );
|
|
SetSkill( SkillName.EvalInt, 150.0 );
|
|
SetSkill( SkillName.Meditation, 120.0 );
|
|
|
|
SetResistance(ResistanceType.Physical, 65, 70);
|
|
SetResistance(ResistanceType.Fire, 65, 70);
|
|
SetResistance(ResistanceType.Cold, 65, 70);
|
|
SetResistance(ResistanceType.Poison, 65, 70);
|
|
SetResistance(ResistanceType.Energy, 65, 70);
|
|
|
|
SetWearable(new Robe(), 1320);
|
|
SetWearable(new WizardsHat(), 1320);
|
|
SetWearable(new GnarledStaff(), 1320);
|
|
SetWearable(new LeatherGloves(), 1320);
|
|
|
|
_LastChange = DateTime.UtcNow;
|
|
}
|
|
|
|
public override void OnThink()
|
|
{
|
|
base.OnThink();
|
|
|
|
if (Form != Form.Human && _LastChange + TimeSpan.FromSeconds(60) < DateTime.UtcNow)
|
|
Form = Form.Human;
|
|
}
|
|
|
|
private void SetHighResistance(ResistanceType type)
|
|
{
|
|
//SetResistance(ResistanceType.Physical, type == ResistanceType.Physical ? 80 : 50, type == ResistanceType.Physical ? 90 : 60);
|
|
SetResistance(ResistanceType.Fire, type == ResistanceType.Fire ? 80 : 50, type == ResistanceType.Fire ? 90 : 60);
|
|
SetResistance(ResistanceType.Cold, type == ResistanceType.Cold ? 80 : 50, type == ResistanceType.Cold ? 90 : 60);
|
|
SetResistance(ResistanceType.Poison, type == ResistanceType.Poison ? 80 : 50, type == ResistanceType.Poison ? 90 : 60);
|
|
SetResistance(ResistanceType.Energy, type == ResistanceType.Energy ? 80 : 50, type == ResistanceType.Energy ? 90 : 60);
|
|
}
|
|
|
|
public void InvalidateForm()
|
|
{
|
|
switch(_Form)
|
|
{
|
|
case Form.Human:
|
|
if(Body != (int)Form.Human)
|
|
{
|
|
Body = (int)Form.Human;
|
|
HueMod = -1;
|
|
SetHighResistance(ResistanceType.Physical);
|
|
}
|
|
break;
|
|
case Form.Fire:
|
|
if(Body != (int)Form.Fire)
|
|
{
|
|
Body = (int)Form.Fire;
|
|
HueMod = 0;
|
|
SetHighResistance(ResistanceType.Fire);
|
|
}
|
|
break;
|
|
case Form.Cold:
|
|
if(Body != (int)Form.Cold)
|
|
{
|
|
Body = (int)Form.Cold;
|
|
HueMod = 0;
|
|
SetHighResistance(ResistanceType.Cold);
|
|
}
|
|
break;
|
|
case Form.Poison:
|
|
if(Body != (int)Form.Poison)
|
|
{
|
|
Body = (int)Form.Poison;
|
|
HueMod = 0;
|
|
SetHighResistance(ResistanceType.Poison);
|
|
}
|
|
break;
|
|
case Form.Energy:
|
|
if(Body != (int)Form.Energy)
|
|
{
|
|
Body = (int)Form.Energy;
|
|
HueMod = 0;
|
|
SetHighResistance(ResistanceType.Energy);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
public override void OnGotMeleeAttack(Mobile m)
|
|
{
|
|
base.OnGotMeleeAttack(m);
|
|
|
|
if(CanChange)
|
|
CheckChange(m);
|
|
}
|
|
|
|
public void CheckChange(Mobile m)
|
|
{
|
|
BaseWeapon weapon = m.Weapon as BaseWeapon;
|
|
|
|
int highest;
|
|
int type = GetHighestDamageType(weapon, out highest);
|
|
|
|
if(weapon != null)
|
|
{
|
|
switch(type)
|
|
{
|
|
case 0: if (Form != Form.Human) Form = Form.Human; break;
|
|
case 1: if (Form != Form.Fire) Form = Form.Fire; break;
|
|
case 2: if (Form != Form.Cold) Form = Form.Cold; break;
|
|
case 3: if (Form != Form.Poison) Form = Form.Poison; break;
|
|
case 4: if (Form != Form.Energy) Form = Form.Energy; break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private int GetHighestDamageType(BaseWeapon weapon, out int highest)
|
|
{
|
|
int phys, fire, cold, pois, nrgy, chaos, direct;
|
|
weapon.GetDamageTypes(null, out phys, out fire, out cold, out pois, out nrgy, out chaos, out direct);
|
|
|
|
int type = 0;
|
|
highest = phys;
|
|
|
|
if(fire > highest) { type = 1; highest = fire; }
|
|
if(cold > highest) { type = 2; highest = cold; }
|
|
if(pois > highest) { type = 3; highest = pois; }
|
|
if(nrgy > highest) { type = 4; highest = nrgy; }
|
|
|
|
return type;
|
|
}
|
|
|
|
public override void AlterMeleeDamageFrom( Mobile m, ref int damage )
|
|
{
|
|
base.AlterMeleeDamageFrom(m, ref damage);
|
|
|
|
BaseWeapon weapon = m.Weapon as BaseWeapon;
|
|
|
|
if(weapon != null)
|
|
{
|
|
SlayerEntry slayer = SlayerGroup.GetEntryByName( weapon.Slayer );
|
|
|
|
if(slayer != null && slayer.Slays(m))
|
|
{
|
|
if(slayer == slayer.Group.Super)
|
|
damage *= 2;
|
|
else
|
|
damage *= 3;
|
|
}
|
|
|
|
int highest;
|
|
int type = GetHighestDamageType(weapon, out highest);
|
|
int heal = (int)((double)damage * ((double)highest / 100.0));
|
|
|
|
switch(this.Form)
|
|
{
|
|
case Form.Human:
|
|
/*if(type == 0)
|
|
{
|
|
damage -= heal;
|
|
Hits = Math.Min(Hits + heal, HitsMax);
|
|
}*/
|
|
break;
|
|
case Form.Fire:
|
|
if(type == 1)
|
|
{
|
|
damage -= heal;
|
|
Hits = Math.Min(Hits + heal, HitsMax);
|
|
}
|
|
break;
|
|
case Form.Cold:
|
|
if(type == 2)
|
|
{
|
|
damage -= heal;
|
|
Hits = Math.Min(Hits + heal, HitsMax);
|
|
}
|
|
break;
|
|
case Form.Poison:
|
|
if(type == 3)
|
|
{
|
|
damage -= heal;
|
|
Hits = Math.Min(Hits + heal, HitsMax);
|
|
}
|
|
break;
|
|
case Form.Energy:
|
|
if(type == 4)
|
|
{
|
|
damage -= heal;
|
|
Hits = Math.Min(Hits + heal, HitsMax);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
public Anon(Serial serial) : base(serial)
|
|
{
|
|
}
|
|
|
|
public override void Serialize(GenericWriter writer)
|
|
{
|
|
base.Serialize(writer);
|
|
writer.Write(0);
|
|
}
|
|
|
|
public override void Deserialize(GenericReader reader)
|
|
{
|
|
base.Deserialize(reader);
|
|
int version = reader.ReadInt();
|
|
|
|
_LastChange = DateTime.UtcNow;
|
|
}
|
|
}
|
|
|
|
public class Juonar : ShadowguardBoss
|
|
{
|
|
public override Type[] SummonTypes { get { return _SummonTypes; } }
|
|
private Type[] _SummonTypes = new Type[] { typeof(SkeletalDragon), typeof(LichLord), typeof(WailingBanshee), typeof(FleshGolem) };
|
|
|
|
public override bool CanDiscord { get { return true; } }
|
|
public override bool PlayInstrumentSound { get { return false; } }
|
|
|
|
private DateTime _NextTeleport;
|
|
|
|
[Constructable]
|
|
public Juonar()
|
|
: base(AIType.AI_NecroMage)
|
|
{
|
|
Name = "Juo'nar";
|
|
Body = 78;
|
|
BaseSoundID = 412;
|
|
Hue = 2951;
|
|
|
|
SetStam(100, 125);
|
|
SetMana(5000, 5500);
|
|
SetStr(500, 560);
|
|
SetInt(1000, 1200);
|
|
SetDex(100, 125);
|
|
|
|
SetDamage(17, 21);
|
|
|
|
SetDamageType(ResistanceType.Physical, 20);
|
|
SetDamageType(ResistanceType.Fire, 20);
|
|
SetDamageType(ResistanceType.Cold, 20);
|
|
SetDamageType(ResistanceType.Poison, 20);
|
|
SetDamageType(ResistanceType.Energy, 20);
|
|
|
|
SetSkill(SkillName.Wrestling, 120.0);
|
|
SetSkill(SkillName.Tactics, 100.0);
|
|
SetSkill(SkillName.MagicResist, 150.0);
|
|
SetSkill(SkillName.Magery, 100.0);
|
|
SetSkill(SkillName.EvalInt, 100.0);
|
|
SetSkill(SkillName.Meditation, 120.0);
|
|
SetSkill(SkillName.Necromancy, 120.0);
|
|
SetSkill(SkillName.SpiritSpeak, 120.0);
|
|
|
|
SetSkill(SkillName.Musicianship, 120.0);
|
|
SetSkill(SkillName.Discordance, 80.0);
|
|
|
|
SetResistance(ResistanceType.Physical, 30);
|
|
SetResistance(ResistanceType.Fire, 30);
|
|
SetResistance(ResistanceType.Cold, 30);
|
|
SetResistance(ResistanceType.Poison, 30);
|
|
SetResistance(ResistanceType.Energy, 30);
|
|
|
|
_NextTeleport = DateTime.UtcNow;
|
|
}
|
|
|
|
public override void OnThink()
|
|
{
|
|
base.OnThink();
|
|
|
|
if (Combatant == null)
|
|
return;
|
|
|
|
if (Combatant is Mobile)
|
|
{
|
|
Mobile m = Combatant as Mobile;
|
|
|
|
if (InRange(m.Location, 10) && !InRange(m.Location, 2) && m.Alive && this.CanBeHarmful(m, false) && m.AccessLevel == AccessLevel.Player)
|
|
{
|
|
if (_NextTeleport < DateTime.UtcNow)
|
|
{
|
|
_NextTeleport = DateTime.UtcNow + TimeSpan.FromSeconds(Utility.RandomMinMax(20, 30)); // too much
|
|
|
|
m.MoveToWorld(GetSpawnPosition(1), Map);
|
|
m.FixedParticles(0x376A, 9, 32, 0x13AF, EffectLayer.Waist);
|
|
m.PlaySound(0x1FE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public Juonar(Serial serial) : base(serial)
|
|
{
|
|
}
|
|
|
|
public override void Serialize(GenericWriter writer)
|
|
{
|
|
base.Serialize(writer);
|
|
writer.Write(0);
|
|
}
|
|
|
|
public override void Deserialize(GenericReader reader)
|
|
{
|
|
base.Deserialize(reader);
|
|
int version = reader.ReadInt();
|
|
|
|
_NextTeleport = DateTime.UtcNow;
|
|
}
|
|
}
|
|
|
|
public class Virtuebane : ShadowguardBoss
|
|
{
|
|
public override Type[] SummonTypes { get { return _SummonTypes; } }
|
|
private Type[] _SummonTypes = new Type[] { typeof(MinotaurCaptain), typeof(Daemon), typeof(Titan) };
|
|
|
|
public override bool BardImmune { get { return true; } }
|
|
|
|
private DateTime _NextNuke;
|
|
private DateTime _NextDismount;
|
|
|
|
[Constructable]
|
|
public Virtuebane() : base(AIType.AI_Mage)
|
|
{
|
|
Name = "Virtuebane";
|
|
|
|
Body = 1071;
|
|
SpeechHue = 452;
|
|
|
|
SetStam(500, 650);
|
|
SetMana(525, 650);
|
|
SetStr(525, 500);
|
|
SetInt(525, 600);
|
|
SetDex(500, 650);
|
|
|
|
SetDamage( 24, 33 );
|
|
|
|
SetDamageType(ResistanceType.Physical, 50);
|
|
SetDamageType(ResistanceType.Energy, 50);
|
|
|
|
SetSkill( SkillName.Wrestling, 120.0, 130.0 );
|
|
SetSkill( SkillName.Tactics, 115.0, 130.0 );
|
|
SetSkill( SkillName.MagicResist, 150.0, 200.0 );
|
|
SetSkill( SkillName.Magery, 135.0, 150.0 );
|
|
SetSkill( SkillName.EvalInt, 130.0, 150.0 );
|
|
SetSkill( SkillName.Meditation, 0.0 );
|
|
|
|
SetResistance(ResistanceType.Physical, 60, 85);
|
|
SetResistance(ResistanceType.Fire, 70, 90);
|
|
SetResistance(ResistanceType.Cold, 40, 50);
|
|
SetResistance(ResistanceType.Poison, 90, 95);
|
|
SetResistance(ResistanceType.Energy, 50, 75);
|
|
|
|
_NextNuke = DateTime.UtcNow + TimeSpan.FromMinutes(1);
|
|
_NextDismount = DateTime.UtcNow + TimeSpan.FromMinutes(1);
|
|
}
|
|
|
|
public override int GetDeathSound() { return 0x596; }
|
|
public override int GetAttackSound() { return 0x597; }
|
|
public override int GetIdleSound() { return 0x598; }
|
|
public override int GetAngerSound() { return 0x599; }
|
|
public override int GetHurtSound() { return 0x59A; }
|
|
|
|
public override void OnThink()
|
|
{
|
|
base.OnThink();
|
|
|
|
if (Combatant == null)
|
|
return;
|
|
|
|
if (Combatant is Mobile && InRange(Combatant.Location, 10))
|
|
{
|
|
if(_NextNuke < DateTime.UtcNow && 0.05 > Utility.RandomDouble())
|
|
{
|
|
_NextNuke = DateTime.UtcNow + TimeSpan.FromSeconds(Utility.RandomMinMax(60, 90));
|
|
|
|
Say(1112362); // You will burn to a pile of ash! yellow hue
|
|
Point3D p = Combatant.Location;
|
|
|
|
Timer.DelayCall(TimeSpan.FromSeconds(3), () =>
|
|
{
|
|
DoNuke(this.Location);
|
|
});
|
|
}
|
|
else if (_NextDismount < DateTime.UtcNow)
|
|
{
|
|
_NextDismount = DateTime.UtcNow + TimeSpan.FromSeconds(Utility.RandomMinMax(60, 90));
|
|
|
|
DoDismount((Mobile)Combatant);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void DoNuke(Point3D p)
|
|
{
|
|
if (!this.Alive || this.Map == null)
|
|
return;
|
|
|
|
int range = 8;
|
|
|
|
//Flame Columns
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
Server.Misc.Geometry.Circle2D(this.Location, this.Map, i, (pnt, map) =>
|
|
{
|
|
Effects.SendLocationParticles(EffectItem.Create(pnt, map, EffectItem.DefaultDuration), 0x3709, 10, 30, 5052);
|
|
});
|
|
}
|
|
|
|
//Flash then boom
|
|
Timer.DelayCall(TimeSpan.FromSeconds(1.5), () =>
|
|
{
|
|
if (this.Alive && this.Map != null)
|
|
{
|
|
Packet flash = ScreenLightFlash.Instance;
|
|
IPooledEnumerable e = this.Map.GetClientsInRange(p, (range * 4) + 5);
|
|
|
|
foreach (NetState ns in e)
|
|
{
|
|
if (ns.Mobile != null)
|
|
ns.Mobile.Send(flash);
|
|
}
|
|
|
|
e.Free();
|
|
|
|
for (int i = 0; i < range; i++)
|
|
{
|
|
Server.Misc.Geometry.Circle2D(this.Location, this.Map, i, (pnt, map) =>
|
|
{
|
|
Effects.SendLocationEffect(pnt, map, 14000, 14, 10, Utility.RandomMinMax(2497, 2499), 2);
|
|
});
|
|
}
|
|
}
|
|
});
|
|
|
|
IPooledEnumerable eable = this.GetMobilesInRange(range);
|
|
|
|
foreach(Mobile m in eable)
|
|
{
|
|
if ((m is PlayerMobile || (m is BaseCreature && ((BaseCreature)m).GetMaster() is PlayerMobile)) && CanBeHarmful(m))
|
|
Timer.DelayCall(TimeSpan.FromSeconds(1.75), new TimerStateCallback(DoDamage_Callback), m);
|
|
}
|
|
|
|
eable.Free();
|
|
}
|
|
|
|
private void DoDamage_Callback(object o)
|
|
{
|
|
Mobile m = o as Mobile;
|
|
Map map = Map;
|
|
|
|
if (m != null && map != null)
|
|
{
|
|
DoHarmful(m);
|
|
AOS.Damage(m, this, Utility.RandomMinMax(100, 150), 50, 50, 0, 0, 0);
|
|
|
|
Direction d = Utility.GetDirection(this, m);
|
|
int range = 0;
|
|
int x = m.X;
|
|
int y = m.Y;
|
|
int orx = x;
|
|
int ory = y;
|
|
|
|
while (range < 12)
|
|
{
|
|
range++;
|
|
int lastx = x;
|
|
int lasty = y;
|
|
|
|
Movement.Movement.Offset(d, ref x, ref y);
|
|
|
|
if (!map.CanSpawnMobile(x, y, map.GetAverageZ(x, y)))
|
|
{
|
|
m.MoveToWorld(new Point3D(lastx, lasty, map.GetAverageZ(lastx, lasty)), this.Map);
|
|
break;
|
|
}
|
|
|
|
if (range >= 12 && (orx != x || ory != y))
|
|
{
|
|
m.MoveToWorld(new Point3D(x, y, map.GetAverageZ(x, y)), this.Map);
|
|
}
|
|
}
|
|
|
|
m.Paralyze(TimeSpan.FromSeconds(3));
|
|
}
|
|
}
|
|
|
|
public void DoDismount(Mobile m)
|
|
{
|
|
this.MovingParticles( m, 0x36D4, 7, 0, false, true, 9502, 4019, 0x160 );
|
|
this.PlaySound( 0x15E );
|
|
|
|
double range = m.GetDistanceToSqrt(this);
|
|
|
|
Timer.DelayCall(TimeSpan.FromMilliseconds(250 * range), () =>
|
|
{
|
|
IMount mount = m.Mount;
|
|
|
|
if(mount != null)
|
|
{
|
|
if(m is PlayerMobile)
|
|
((PlayerMobile)m).SetMountBlock(BlockMountType.Dazed, TimeSpan.FromSeconds(10), true);
|
|
else
|
|
mount.Rider = null;
|
|
}
|
|
else if (m.Flying)
|
|
{
|
|
((PlayerMobile)m).SetMountBlock(BlockMountType.Dazed, TimeSpan.FromSeconds(10), true);
|
|
}
|
|
|
|
AOS.Damage( m, this, Utility.RandomMinMax( 15, 25 ), 100, 0, 0, 0, 0 );
|
|
});
|
|
}
|
|
|
|
public Virtuebane(Serial serial) : base(serial)
|
|
{
|
|
}
|
|
|
|
public override void Serialize(GenericWriter writer)
|
|
{
|
|
base.Serialize(writer);
|
|
writer.Write(0);
|
|
}
|
|
|
|
public override void Deserialize(GenericReader reader)
|
|
{
|
|
base.Deserialize(reader);
|
|
int version = reader.ReadInt();
|
|
}
|
|
}
|
|
|
|
public class Ozymandias : ShadowguardBoss
|
|
{
|
|
public override Type[] SummonTypes { get { return _SummonTypes; } }
|
|
private Type[] _SummonTypes = new Type[] { typeof(LesserHiryu), typeof(EliteNinja), typeof(TsukiWolf) };
|
|
|
|
public override double WeaponAbilityChance{ get{ return 0.4; } }
|
|
|
|
[Constructable]
|
|
public Ozymandias() : base(AIType.AI_Melee)
|
|
{
|
|
Name = "Ozymandias";
|
|
Title = "the Lord of Castle Barataria";
|
|
|
|
Hue = Race.RandomSkinHue();
|
|
Body = 0x190;
|
|
FacialHairItemID = 0x2040;
|
|
|
|
SetInt(225, 250);
|
|
SetDex(225);
|
|
|
|
SetDamage( 25, 32 );
|
|
|
|
SetDamageType(ResistanceType.Physical, 100);
|
|
|
|
SetSkill( SkillName.Wrestling, 150.0 );
|
|
SetSkill( SkillName.Archery, 150.0 );
|
|
SetSkill( SkillName.Anatomy, 100.0 );
|
|
SetSkill(SkillName.Tactics, 125.0);
|
|
SetSkill( SkillName.MagicResist, 110.0 );
|
|
|
|
SetResistance(ResistanceType.Physical, 60, 70);
|
|
SetResistance(ResistanceType.Fire, 20, 30);
|
|
SetResistance(ResistanceType.Cold, 60, 70);
|
|
SetResistance(ResistanceType.Poison, 60, 70);
|
|
SetResistance(ResistanceType.Energy, 60, 70);
|
|
|
|
SetWearable(new LeatherDo());
|
|
SetWearable(new LeatherSuneate());
|
|
SetWearable(new Yumi());
|
|
SetWearable(new Waraji());
|
|
SetWearable(new BoneArms());
|
|
|
|
var scimitar = new Scimitar();
|
|
scimitar.Movable = false;
|
|
|
|
PackItem(scimitar);
|
|
PackItem(new Arrow(25));
|
|
|
|
var hiryu = new LesserHiryu();
|
|
hiryu.Rider = this;
|
|
|
|
SetWeaponAbility(WeaponAbility.Dismount);
|
|
}
|
|
|
|
private DateTime _NextWeaponSwitch;
|
|
|
|
public override void OnThink()
|
|
{
|
|
base.OnThink();
|
|
|
|
if (Combatant == null || this.Backpack == null || _NextWeaponSwitch > DateTime.UtcNow)
|
|
return;
|
|
|
|
BaseWeapon wep = Weapon as BaseWeapon;
|
|
|
|
if ((wep is Fists || wep is BaseRanged) && InRange(Combatant.Location, 1) && 0.1 > Utility.RandomDouble())
|
|
{
|
|
Item scimitar = this.Backpack.FindItemByType(typeof(Scimitar));
|
|
|
|
if (scimitar != null)
|
|
{
|
|
if (wep is BaseRanged)
|
|
this.Backpack.DropItem(wep);
|
|
|
|
SetWearable(scimitar);
|
|
|
|
_NextWeaponSwitch = DateTime.UtcNow + TimeSpan.FromSeconds(10);
|
|
}
|
|
}
|
|
else if ((wep is Fists || !(wep is BaseRanged)) && !InRange(Combatant.Location, 1) && 0.1 > Utility.RandomDouble())
|
|
{
|
|
Item yumi = this.Backpack.FindItemByType(typeof(Yumi));
|
|
|
|
if (yumi != null)
|
|
{
|
|
if (!(wep is Fists))
|
|
this.Backpack.DropItem(wep);
|
|
|
|
SetWearable(yumi);
|
|
|
|
_NextWeaponSwitch = DateTime.UtcNow + TimeSpan.FromSeconds(10);
|
|
}
|
|
}
|
|
}
|
|
|
|
public Ozymandias(Serial serial) : base(serial)
|
|
{
|
|
}
|
|
|
|
public override void Serialize(GenericWriter writer)
|
|
{
|
|
base.Serialize(writer);
|
|
writer.Write(0);
|
|
}
|
|
|
|
public override void Deserialize(GenericReader reader)
|
|
{
|
|
base.Deserialize(reader);
|
|
int version = reader.ReadInt();
|
|
}
|
|
}
|
|
}
|