451 lines
14 KiB
C#
451 lines
14 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
using Server;
|
|
using Server.Items;
|
|
using Server.Engines.SeasonalEvents;
|
|
using Server.Spells;
|
|
|
|
namespace Server.Mobiles
|
|
{
|
|
[CorpseName("the corpse of krampus")]
|
|
public class Krampus : BaseCreature
|
|
{
|
|
public override bool TeleportsTo { get { return true; } }
|
|
|
|
public List<BaseCreature> SummonedHelpers { get; set; }
|
|
public List<BaseCreature> InitialSpawn { get; set; }
|
|
|
|
private DateTime _NextSpecial;
|
|
public const int MaxSummons = 24;
|
|
|
|
private DateTime _LastActivity;
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public Point3D SpawnLocation { get; set; }
|
|
|
|
public bool IsKrampusEncounter { get { return KrampusEncounter.Encounter != null && KrampusEncounter.Encounter.Krampus == this; } }
|
|
|
|
[Constructable]
|
|
public Krampus()
|
|
: base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4)
|
|
{
|
|
Name = "Krampus";
|
|
Body = 1484;
|
|
|
|
SetStr(700, 750);
|
|
SetDex(800, 900);
|
|
SetInt(500, 600);
|
|
|
|
SetHits(40000);
|
|
|
|
SetDamage(28, 35);
|
|
|
|
SetDamageType(ResistanceType.Physical, 50);
|
|
SetDamageType(ResistanceType.Cold, 50);
|
|
|
|
SetResistance(ResistanceType.Physical, 80, 90);
|
|
SetResistance(ResistanceType.Fire, 70, 80);
|
|
SetResistance(ResistanceType.Cold, 80, 90);
|
|
SetResistance(ResistanceType.Poison, 70, 80);
|
|
SetResistance(ResistanceType.Energy, 70, 80);
|
|
|
|
SetSkill(SkillName.MagicResist, 150);
|
|
SetSkill(SkillName.Tactics, 110, 120);
|
|
SetSkill(SkillName.Wrestling, 120.0, 125.0);
|
|
SetSkill(SkillName.DetectHidden, 60.0, 70.0);
|
|
SetSkill(SkillName.Parry, 60, 70);
|
|
|
|
Fame = 30000;
|
|
Karma = -30000;
|
|
|
|
switch (Utility.Random(3))
|
|
{
|
|
case 0:
|
|
SetMagicalAbility(MagicalAbility.Piercing); break;
|
|
case 1:
|
|
SetMagicalAbility(MagicalAbility.Bashing); break;
|
|
case 2:
|
|
SetMagicalAbility(MagicalAbility.Slashing); break;
|
|
}
|
|
|
|
_NextSpecial = DateTime.UtcNow;
|
|
}
|
|
|
|
public int TotalSummons()
|
|
{
|
|
if (SummonedHelpers == null)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return SummonedHelpers.Where(bc => bc != null && bc.Alive).Count();
|
|
}
|
|
|
|
public void Summon(Mobile target, bool initial = false)
|
|
{
|
|
if (target == null || (!initial && InitialSpawn != null && InitialSpawn.Count > 0))
|
|
return;
|
|
|
|
var map = Map;
|
|
|
|
if (map == null || TotalSummons() > MaxSummons)
|
|
return;
|
|
|
|
if (!initial)
|
|
{
|
|
MovingEffect(target, 0xA271, 10, 0, false, false, 0, 0);
|
|
}
|
|
|
|
Timer.DelayCall(TimeSpan.FromSeconds(initial ? 0.25 : 1.0), com =>
|
|
{
|
|
if (!com.Alive)
|
|
return;
|
|
|
|
int count = Utility.RandomMinMax(3, 5);
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
Point3D p = com.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 = new KrampusMinion();
|
|
|
|
if (spawn != null)
|
|
{
|
|
spawn.MoveToWorld(p, map);
|
|
spawn.Home = p;
|
|
spawn.RangeHome = 5;
|
|
spawn.Team = this.Team;
|
|
spawn.SummonMaster = this;
|
|
|
|
if (!initial)
|
|
{
|
|
if (spawn.Combatant != null)
|
|
{
|
|
if (!(spawn.Combatant is PlayerMobile) || !((PlayerMobile)spawn.Combatant).HonorActive)
|
|
spawn.Combatant = com;
|
|
}
|
|
else
|
|
{
|
|
spawn.Combatant = com;
|
|
}
|
|
}
|
|
|
|
AddHelper(spawn, initial);
|
|
}
|
|
}
|
|
|
|
if (initial)
|
|
{
|
|
Blessed = true;
|
|
}
|
|
}, target);
|
|
|
|
_NextSpecial = DateTime.UtcNow + TimeSpan.FromSeconds(Utility.RandomMinMax(20, 40));
|
|
}
|
|
|
|
protected virtual void AddHelper(BaseCreature bc, bool initial)
|
|
{
|
|
if (initial)
|
|
{
|
|
if (InitialSpawn == null)
|
|
InitialSpawn = new List<BaseCreature>();
|
|
|
|
if (!InitialSpawn.Contains(bc))
|
|
InitialSpawn.Add(bc);
|
|
}
|
|
else
|
|
{
|
|
if (SummonedHelpers == null)
|
|
SummonedHelpers = new List<BaseCreature>();
|
|
|
|
if (!SummonedHelpers.Contains(bc))
|
|
SummonedHelpers.Add(bc);
|
|
}
|
|
}
|
|
|
|
private Mobile _LastTeleported;
|
|
|
|
public override void OnAfterTeleport(Mobile m)
|
|
{
|
|
m.SendLocalizedMessage(1072195); // Without warning, you are magically transported closer to your enemy!
|
|
}
|
|
|
|
public override void OnGaveMeleeAttack(Mobile defender)
|
|
{
|
|
base.OnGaveMeleeAttack(defender);
|
|
|
|
if (defender == _LastTeleported)
|
|
{
|
|
BoneBreakerContext.CheckHit(this, defender);
|
|
}
|
|
|
|
_LastTeleported = null;
|
|
}
|
|
|
|
public override int GetIdleSound()
|
|
{
|
|
return 1589;
|
|
}
|
|
|
|
public override int GetAngerSound()
|
|
{
|
|
return 1586;
|
|
}
|
|
|
|
public override int GetHurtSound()
|
|
{
|
|
return 1588;
|
|
}
|
|
|
|
public override int GetDeathSound()
|
|
{
|
|
return 1587;
|
|
}
|
|
|
|
public Krampus(Serial serial)
|
|
: base(serial)
|
|
{
|
|
}
|
|
|
|
public override void GenerateLoot()
|
|
{
|
|
AddLoot(LootPack.SuperBoss, 5);
|
|
}
|
|
|
|
public override void OnThink()
|
|
{
|
|
base.OnThink();
|
|
|
|
if (Blessed)
|
|
{
|
|
if (InitialSpawn != null)
|
|
{
|
|
if (InitialSpawn.All(s => s.Deleted))
|
|
{
|
|
ColUtility.Free(InitialSpawn);
|
|
InitialSpawn = null;
|
|
|
|
Blessed = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Blessed = false;
|
|
}
|
|
}
|
|
|
|
if (Combatant == null)
|
|
{
|
|
if (SpawnLocation != Point3D.Zero &&
|
|
_LastActivity > DateTime.MinValue &&
|
|
_LastActivity + TimeSpan.FromHours(2) < DateTime.UtcNow)
|
|
{
|
|
Delete();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
_LastActivity = DateTime.UtcNow;
|
|
|
|
if (_NextSpecial < DateTime.UtcNow && 0.25 > Utility.RandomDouble())
|
|
{
|
|
if (Utility.RandomBool())
|
|
{
|
|
var target = GetTeleportTarget();
|
|
|
|
if (target != null)
|
|
{
|
|
Summon(target);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int baseDamage = 100;
|
|
bool switched = false;
|
|
|
|
PlaySound(0x20D);
|
|
|
|
foreach (var m in SpellHelper.AcquireIndirectTargets(this, Location, Map, 10).OfType<Mobile>())
|
|
{
|
|
var range = (int)GetDistanceToSqrt(m);
|
|
|
|
if (range < 1) range = 1;
|
|
if (range > 4) range = 4;
|
|
|
|
m.PlaySound(0x20D);
|
|
AOS.Damage(this, m, baseDamage / range, 0, 0, 0, 0, 0, 100, 0);
|
|
|
|
if (!switched && 0.2 > Utility.RandomDouble())
|
|
{
|
|
Combatant = m;
|
|
switched = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
_NextSpecial = DateTime.UtcNow + TimeSpan.FromSeconds(Utility.RandomMinMax(30, 60));
|
|
}
|
|
|
|
if (Map != null && SpawnLocation != Point3D.Zero && !Utility.InRange(Location, SpawnLocation, 25))
|
|
{
|
|
MoveToWorld(SpawnLocation, Map);
|
|
}
|
|
}
|
|
|
|
public override void OnDeath(Container c)
|
|
{
|
|
if (IsKrampusEncounter)
|
|
{
|
|
var rights = GetLootingRights();
|
|
|
|
foreach (var ds in rights.Where(s => s.m_Mobile is PlayerMobile && s.m_HasRight))
|
|
{
|
|
var m = ds.m_Mobile as PlayerMobile;
|
|
int ordersComplete = 0;
|
|
|
|
if (KrampusEncounter.Encounter.CompleteTable.ContainsKey(m))
|
|
{
|
|
ordersComplete = KrampusEncounter.Encounter.CompleteTable[m];
|
|
}
|
|
|
|
if (ordersComplete >= 3 || Utility.RandomMinMax(0, 8) <= ordersComplete)
|
|
{
|
|
Item item = null;
|
|
|
|
switch (Utility.Random(13))
|
|
{
|
|
case 0: item = new KrampusCoinPurse(m.Karma); break;
|
|
case 1: item = new CardOfSemidar(Utility.RandomMinMax(0, 6)); break;
|
|
case 2: item = new NiceTitleDeed(); break;
|
|
case 3: item = new NaughtyTitleDeed(); break;
|
|
case 4: item = new PunisherTitleDeed(); break;
|
|
case 5: item = new RecipeScroll(586); break; // minion hat
|
|
case 6: item = new RecipeScroll(587); break; // minion boots
|
|
case 7: item = new KrampusCoinPurse(463); break; // minion talons
|
|
case 8: item = new KrampusCoinPurse(588); break; // minion earrings
|
|
case 9: item = new KrampusPunishinList(m.Name); break;
|
|
case 10: item = new SpikedWhip(); break;
|
|
case 11: item = new BarbedWhip(); break;
|
|
case 12: item = new BladedWhip(); break;
|
|
}
|
|
|
|
if (item != null)
|
|
{
|
|
m.SendLocalizedMessage(1156269); // For your valor in defeating your foe a specialty item has been awarded to you!
|
|
|
|
if (m.Backpack == null || !m.Backpack.TryDropItem(m, item, false))
|
|
{
|
|
m.BankBox.DropItem(item);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
base.OnDeath(c);
|
|
}
|
|
|
|
public override void Delete()
|
|
{
|
|
base.Delete();
|
|
|
|
if (IsKrampusEncounter)
|
|
{
|
|
KrampusEncounter.Encounter.OnKrampusKilled();
|
|
}
|
|
|
|
if (SummonedHelpers != null)
|
|
{
|
|
ColUtility.SafeDelete(SummonedHelpers);
|
|
ColUtility.Free(SummonedHelpers);
|
|
}
|
|
|
|
if (InitialSpawn != null)
|
|
{
|
|
ColUtility.SafeDelete(InitialSpawn);
|
|
ColUtility.Free(InitialSpawn);
|
|
}
|
|
}
|
|
|
|
public override void Serialize(GenericWriter writer)
|
|
{
|
|
base.Serialize(writer);
|
|
writer.Write((int)0);
|
|
|
|
writer.Write(SpawnLocation);
|
|
|
|
writer.Write(SummonedHelpers == null ? 0 : SummonedHelpers.Count);
|
|
|
|
if (SummonedHelpers != null)
|
|
SummonedHelpers.ForEach(m => writer.Write(m));
|
|
|
|
writer.Write(InitialSpawn == null ? 0 : InitialSpawn.Count);
|
|
|
|
if (InitialSpawn != null)
|
|
InitialSpawn.ForEach(m => writer.Write(m));
|
|
}
|
|
|
|
public override void Deserialize(GenericReader reader)
|
|
{
|
|
base.Deserialize(reader);
|
|
int version = reader.ReadInt();
|
|
|
|
SpawnLocation = reader.ReadPoint3D();
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
count = reader.ReadInt();
|
|
|
|
if (count > 0)
|
|
{
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
BaseCreature summon = reader.ReadMobile() as BaseCreature;
|
|
|
|
if (summon != null)
|
|
{
|
|
if (InitialSpawn == null)
|
|
InitialSpawn = new List<BaseCreature>();
|
|
|
|
InitialSpawn.Add(summon);
|
|
}
|
|
}
|
|
}
|
|
|
|
_NextSpecial = DateTime.UtcNow;
|
|
}
|
|
}
|
|
}
|