feat: Adds drain life ability (#1181)
This commit is contained in:
parent
dc6f75c3b4
commit
1cf5d59116
12 changed files with 431 additions and 531 deletions
|
|
@ -1,40 +1,14 @@
|
|||
using System;
|
||||
using Server.Collections;
|
||||
using Server.Collections;
|
||||
|
||||
namespace Server.Mobiles;
|
||||
|
||||
public abstract class AreaEffectMonsterAbility : MonsterAbility
|
||||
{
|
||||
public override MonsterAbilityTrigger AbilityTrigger => MonsterAbilityTrigger.OnCombatAction;
|
||||
public override MonsterAbilityType AbilityType => MonsterAbilityType.MassPoison;
|
||||
|
||||
public override double ChanceToTrigger => 0.5;
|
||||
public override TimeSpan MinTriggerCooldown => TimeSpan.FromSeconds(5.0);
|
||||
public override TimeSpan MaxTriggerCooldown => TimeSpan.FromSeconds(30.0);
|
||||
|
||||
public virtual int CombatantRange => 3;
|
||||
|
||||
public virtual int AreaRange => 3;
|
||||
|
||||
public override bool CanTrigger(BaseCreature source)
|
||||
{
|
||||
if (!base.CanTrigger(source))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var combatant = source.Combatant;
|
||||
|
||||
// Only do the mass ability as a side-effect of combating a specific target
|
||||
return combatant?.Deleted == false && combatant.Map == source.Map && source.InRange(combatant.Location, CombatantRange) &&
|
||||
source.CanBeHarmful(combatant) && source.InLOS(combatant);
|
||||
}
|
||||
|
||||
public override void Trigger(BaseCreature source, Mobile target)
|
||||
{
|
||||
// target is null for OnCombatAction triggers
|
||||
|
||||
var eable = source.GetMobilesInRange(2);
|
||||
var eable = source.GetMobilesInRange(AreaRange);
|
||||
using var queue = PooledRefQueue<Mobile>.Create();
|
||||
foreach (var m in eable)
|
||||
{
|
||||
|
|
@ -54,7 +28,9 @@ public abstract class AreaEffectMonsterAbility : MonsterAbility
|
|||
}
|
||||
|
||||
protected virtual bool CanEffectTarget(BaseCreature source, Mobile defender) =>
|
||||
source != defender && defender.Alive && source.IsEnemy(defender) && source.CanBeHarmful(defender);
|
||||
source != defender && defender.Alive && source.CanBeHarmful(defender)
|
||||
&& (defender.Player || defender is BaseCreature bc && (bc.Team == source.Team || bc.Controlled || bc.Summoned))
|
||||
&& (!Core.AOS || source.InLOS(defender));
|
||||
|
||||
protected abstract void DoEffectTarget(BaseCreature source, Mobile defender);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ namespace Server.Mobiles;
|
|||
public class DeathExplosion : AreaEffectMonsterAbility
|
||||
{
|
||||
public override MonsterAbilityTrigger AbilityTrigger => MonsterAbilityTrigger.OnDeath;
|
||||
public override MonsterAbilityType AbilityType => MonsterAbilityType.DeathExplosion;
|
||||
|
||||
public override double ChanceToTrigger => 1.0;
|
||||
public override TimeSpan MinTriggerCooldown => TimeSpan.Zero;
|
||||
|
|
|
|||
33
Projects/UOContent/Mobiles/Abilities/DrainLifeAreaAttack.cs
Normal file
33
Projects/UOContent/Mobiles/Abilities/DrainLifeAreaAttack.cs
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
namespace Server.Mobiles;
|
||||
|
||||
public class DrainLifeAreaAttack : AreaEffectMonsterAbility
|
||||
{
|
||||
public override MonsterAbilityTrigger AbilityTrigger =>
|
||||
MonsterAbilityTrigger.GiveMeleeDamage | MonsterAbilityTrigger.TakeMeleeDamage;
|
||||
|
||||
public override MonsterAbilityType AbilityType => MonsterAbilityType.MassDrainLife;
|
||||
|
||||
public override double ChanceToTrigger => 0.1;
|
||||
public override int AreaRange => 2;
|
||||
|
||||
public virtual int MinDamage => 10;
|
||||
public virtual int MaxDamage => 40;
|
||||
|
||||
protected virtual void DrainLife(BaseCreature source, Mobile defender)
|
||||
{
|
||||
var toDrain = Utility.RandomMinMax(MinDamage, MaxDamage);
|
||||
|
||||
source.Hits += toDrain;
|
||||
source.DoHarmful(defender);
|
||||
defender.Damage(toDrain, source);
|
||||
}
|
||||
|
||||
protected override void DoEffectTarget(BaseCreature source, Mobile defender)
|
||||
{
|
||||
defender.FixedParticles(0x374A, 10, 15, 5013, 0x496, 0, EffectLayer.Waist);
|
||||
defender.PlaySound(0x231);
|
||||
|
||||
defender.SendMessage("You feel the life drain out of you!");
|
||||
DrainLife(source, defender);
|
||||
}
|
||||
}
|
||||
|
|
@ -26,4 +26,6 @@ public abstract partial class MonsterAbility
|
|||
public static ThrowHatchetCounter ThrowHatchetCounter => new();
|
||||
|
||||
public static DestroyEquipment DestroyEquipment => new();
|
||||
|
||||
public static DrainLifeAreaAttack DrainLifeAreaAttack => new();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,5 +10,7 @@ public enum MonsterAbilityType
|
|||
Poison,
|
||||
MassPoison,
|
||||
ThrowWeapon,
|
||||
DestroyEquipment
|
||||
DestroyEquipment,
|
||||
DeathExplosion,
|
||||
MassDrainLife
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,32 @@
|
|||
namespace Server.Mobiles;
|
||||
using System;
|
||||
|
||||
namespace Server.Mobiles;
|
||||
|
||||
public class PoisonGasAreaAttack : AreaEffectMonsterAbility
|
||||
{
|
||||
public override MonsterAbilityTrigger AbilityTrigger => MonsterAbilityTrigger.OnCombatAction;
|
||||
public override MonsterAbilityType AbilityType => MonsterAbilityType.MassPoison;
|
||||
|
||||
public override double ChanceToTrigger => 0.5;
|
||||
public override TimeSpan MinTriggerCooldown => TimeSpan.FromSeconds(5.0);
|
||||
public override TimeSpan MaxTriggerCooldown => TimeSpan.FromSeconds(30.0);
|
||||
|
||||
public virtual int CombatantRange => 3;
|
||||
|
||||
public override bool CanTrigger(BaseCreature source)
|
||||
{
|
||||
if (!base.CanTrigger(source))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var combatant = source.Combatant;
|
||||
|
||||
// Only do the mass ability as a side-effect of combating a specific target
|
||||
return combatant?.Deleted == false && combatant.Map == source.Map && source.InRange(combatant.Location, CombatantRange) &&
|
||||
source.CanBeHarmful(combatant) && source.InLOS(combatant);
|
||||
}
|
||||
|
||||
public override void Trigger(BaseCreature source, Mobile target)
|
||||
{
|
||||
source.FixedParticles(0x376A, 9, 32, 0x2539, EffectLayer.LeftHand);
|
||||
|
|
|
|||
|
|
@ -54,54 +54,8 @@ namespace Server.Mobiles
|
|||
AddLoot(LootPack.MedScrolls, 2);
|
||||
}
|
||||
|
||||
public void DrainLife()
|
||||
{
|
||||
var eable = GetMobilesInRange(2);
|
||||
|
||||
foreach (var m in eable)
|
||||
{
|
||||
if (m == this || !CanBeHarmful(m) ||
|
||||
!(m.Player || m is BaseCreature creature &&
|
||||
(creature.Controlled || creature.Summoned || creature.Team != Team)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
DoHarmful(m);
|
||||
|
||||
m.FixedParticles(0x374A, 10, 15, 5013, 0x496, 0, EffectLayer.Waist);
|
||||
m.PlaySound(0x231);
|
||||
|
||||
// m.SendMessage( "You feel the life drain out of you!" );
|
||||
|
||||
var toDrain = Utility.RandomMinMax(10, 40);
|
||||
|
||||
Hits += toDrain;
|
||||
m.Damage(toDrain, this);
|
||||
}
|
||||
|
||||
eable.Free();
|
||||
}
|
||||
|
||||
public override void OnGaveMeleeAttack(Mobile defender, int damage)
|
||||
{
|
||||
base.OnGaveMeleeAttack(defender, damage);
|
||||
|
||||
if (Utility.RandomDouble() <= 0.1)
|
||||
{
|
||||
DrainLife();
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnGotMeleeAttack(Mobile attacker, int damage)
|
||||
{
|
||||
base.OnGotMeleeAttack(attacker, damage);
|
||||
|
||||
if (Utility.RandomDouble() <= 0.1)
|
||||
{
|
||||
DrainLife();
|
||||
}
|
||||
}
|
||||
private static MonsterAbility[] _abilities = { MonsterAbility.DrainLifeAreaAttack };
|
||||
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
|
||||
|
||||
public override void Serialize(IGenericWriter writer)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,141 +1,96 @@
|
|||
using System.Collections.Generic;
|
||||
namespace Server.Mobiles;
|
||||
|
||||
namespace Server.Mobiles
|
||||
public class SirPatrick : SkeletalKnight
|
||||
{
|
||||
public class SirPatrick : SkeletalKnight
|
||||
[Constructible]
|
||||
public SirPatrick()
|
||||
{
|
||||
[Constructible]
|
||||
public SirPatrick()
|
||||
IsParagon = true;
|
||||
|
||||
Hue = 0x47E;
|
||||
|
||||
SetStr(208, 319);
|
||||
SetDex(98, 132);
|
||||
SetInt(45, 91);
|
||||
|
||||
SetHits(616, 884);
|
||||
|
||||
SetDamage(15, 25);
|
||||
|
||||
SetDamageType(ResistanceType.Physical, 40);
|
||||
SetDamageType(ResistanceType.Cold, 60);
|
||||
|
||||
SetResistance(ResistanceType.Physical, 55, 62);
|
||||
SetResistance(ResistanceType.Fire, 40, 48);
|
||||
SetResistance(ResistanceType.Cold, 71, 80);
|
||||
SetResistance(ResistanceType.Poison, 40, 50);
|
||||
SetResistance(ResistanceType.Energy, 50, 60);
|
||||
|
||||
SetSkill(SkillName.Wrestling, 126.3, 136.5);
|
||||
SetSkill(SkillName.Tactics, 128.5, 143.8);
|
||||
SetSkill(SkillName.MagicResist, 102.8, 117.9);
|
||||
SetSkill(SkillName.Anatomy, 127.5, 137.2);
|
||||
|
||||
Fame = 18000;
|
||||
Karma = -18000;
|
||||
}
|
||||
|
||||
public SirPatrick(Serial serial) : base(serial)
|
||||
{
|
||||
}
|
||||
|
||||
public override string CorpseName => "a Sir Patrick corpse";
|
||||
public override string DefaultName => "Sir Patrick";
|
||||
|
||||
/*
|
||||
// TODO: uncomment once added
|
||||
public override void OnDeath( Container c )
|
||||
{
|
||||
base.OnDeath( c );
|
||||
|
||||
if (Utility.RandomDouble() < 0.15)
|
||||
c.DropItem( new DisintegratingThesisNotes() );
|
||||
|
||||
if (Utility.RandomDouble() < 0.05)
|
||||
c.DropItem( new AssassinChest() );
|
||||
}
|
||||
*/
|
||||
|
||||
public override bool GivesMLMinorArtifact => true;
|
||||
|
||||
private static MonsterAbility[] _abilities = { new SirPatrickDrainLife() };
|
||||
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
|
||||
|
||||
public override void GenerateLoot()
|
||||
{
|
||||
AddLoot(LootPack.UltraRich, 2);
|
||||
}
|
||||
|
||||
public override void Serialize(IGenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
writer.Write(0); // version
|
||||
}
|
||||
|
||||
public override void Deserialize(IGenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
var version = reader.ReadInt();
|
||||
}
|
||||
|
||||
private class SirPatrickDrainLife : DrainLifeAreaAttack
|
||||
{
|
||||
public override int MinDamage => 14;
|
||||
public override int MaxDamage => 30;
|
||||
|
||||
protected override void DoEffectTarget(BaseCreature source, Mobile defender)
|
||||
{
|
||||
IsParagon = true;
|
||||
defender.FixedParticles(0x374A, 10, 15, 5013, 0x455, 0, EffectLayer.Waist);
|
||||
defender.PlaySound(0x1EA);
|
||||
|
||||
Hue = 0x47E;
|
||||
|
||||
SetStr(208, 319);
|
||||
SetDex(98, 132);
|
||||
SetInt(45, 91);
|
||||
|
||||
SetHits(616, 884);
|
||||
|
||||
SetDamage(15, 25);
|
||||
|
||||
SetDamageType(ResistanceType.Physical, 40);
|
||||
SetDamageType(ResistanceType.Cold, 60);
|
||||
|
||||
SetResistance(ResistanceType.Physical, 55, 62);
|
||||
SetResistance(ResistanceType.Fire, 40, 48);
|
||||
SetResistance(ResistanceType.Cold, 71, 80);
|
||||
SetResistance(ResistanceType.Poison, 40, 50);
|
||||
SetResistance(ResistanceType.Energy, 50, 60);
|
||||
|
||||
SetSkill(SkillName.Wrestling, 126.3, 136.5);
|
||||
SetSkill(SkillName.Tactics, 128.5, 143.8);
|
||||
SetSkill(SkillName.MagicResist, 102.8, 117.9);
|
||||
SetSkill(SkillName.Anatomy, 127.5, 137.2);
|
||||
|
||||
Fame = 18000;
|
||||
Karma = -18000;
|
||||
}
|
||||
|
||||
public SirPatrick(Serial serial)
|
||||
: base(serial)
|
||||
{
|
||||
}
|
||||
|
||||
public override string CorpseName => "a Sir Patrick corpse";
|
||||
public override string DefaultName => "Sir Patrick";
|
||||
|
||||
/*
|
||||
// TODO: uncomment once added
|
||||
public override void OnDeath( Container c )
|
||||
{
|
||||
base.OnDeath( c );
|
||||
|
||||
if (Utility.RandomDouble() < 0.15)
|
||||
c.DropItem( new DisintegratingThesisNotes() );
|
||||
|
||||
if (Utility.RandomDouble() < 0.05)
|
||||
c.DropItem( new AssassinChest() );
|
||||
}
|
||||
*/
|
||||
|
||||
public override bool GivesMLMinorArtifact => true;
|
||||
|
||||
public override void GenerateLoot()
|
||||
{
|
||||
AddLoot(LootPack.UltraRich, 2);
|
||||
}
|
||||
|
||||
public override void OnGaveMeleeAttack(Mobile defender, int damage)
|
||||
{
|
||||
base.OnGaveMeleeAttack(defender, damage);
|
||||
|
||||
if (Utility.RandomDouble() < 0.1)
|
||||
{
|
||||
DrainLife();
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnGotMeleeAttack(Mobile attacker, int damage)
|
||||
{
|
||||
base.OnGotMeleeAttack(attacker, damage);
|
||||
|
||||
if (Utility.RandomDouble() < 0.1)
|
||||
{
|
||||
DrainLife();
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void DrainLife()
|
||||
{
|
||||
var list = new List<Mobile>();
|
||||
|
||||
foreach (var m in GetMobilesInRange(2))
|
||||
{
|
||||
if (m == this || !CanBeHarmful(m, false) || Core.AOS && !InLOS(m))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m is BaseCreature bc)
|
||||
{
|
||||
if (bc.Controlled || bc.Summoned || bc.Team != Team)
|
||||
{
|
||||
list.Add(bc);
|
||||
}
|
||||
}
|
||||
else if (m.Player)
|
||||
{
|
||||
list.Add(m);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var m in list)
|
||||
{
|
||||
DoHarmful(m);
|
||||
|
||||
m.FixedParticles(0x374A, 10, 15, 5013, 0x455, 0, EffectLayer.Waist);
|
||||
m.PlaySound(0x1EA);
|
||||
|
||||
var drain = Utility.RandomMinMax(14, 30);
|
||||
|
||||
Hits += drain;
|
||||
m.Damage(drain, this);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Serialize(IGenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
writer.Write(0); // version
|
||||
}
|
||||
|
||||
public override void Deserialize(IGenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
var version = reader.ReadInt();
|
||||
DrainLife(source, defender);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,6 +57,12 @@ public class CopperElemental : BaseCreature
|
|||
AddLoot(LootPack.Gems, 2);
|
||||
}
|
||||
|
||||
public override void AlterMeleeDamageFrom(Mobile from, ref int damage)
|
||||
{
|
||||
base.AlterMeleeDamageFrom(from, ref damage);
|
||||
damage /= 2; // 50% melee damage
|
||||
}
|
||||
|
||||
public override void CheckReflect(Mobile caster, ref bool reflect)
|
||||
{
|
||||
reflect = true; // Every spell is reflected back to the caster
|
||||
|
|
|
|||
|
|
@ -65,12 +65,13 @@ public class ValoriteElemental : BaseCreature
|
|||
|
||||
public override void AlterMeleeDamageFrom(Mobile from, ref int damage)
|
||||
{
|
||||
if (from is BaseCreature bc)
|
||||
if (from is BaseCreature bc && (bc.Controlled || bc.BardTarget == this))
|
||||
{
|
||||
if (bc.Controlled || bc.BardTarget == this)
|
||||
{
|
||||
damage = 0; // Immune to pets and provoked creatures
|
||||
}
|
||||
damage = 0; // Immune to pets and provoked creatures
|
||||
}
|
||||
else
|
||||
{
|
||||
damage /= 2; // 50% melee damage
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,181 +1,178 @@
|
|||
using System;
|
||||
using Server.Items;
|
||||
|
||||
namespace Server.Mobiles
|
||||
namespace Server.Mobiles;
|
||||
|
||||
public class Leviathan : BaseCreature
|
||||
{
|
||||
public class Leviathan : BaseCreature
|
||||
[Constructible]
|
||||
public Leviathan(Mobile fisher = null) : base(AIType.AI_Mage)
|
||||
{
|
||||
[Constructible]
|
||||
public Leviathan(Mobile fisher = null) : base(AIType.AI_Mage)
|
||||
Fisher = fisher;
|
||||
|
||||
// May not be OSI accurate; mostly copied from krakens
|
||||
Body = 77;
|
||||
BaseSoundID = 353;
|
||||
|
||||
Hue = 0x481;
|
||||
|
||||
SetStr(1000);
|
||||
SetDex(501, 520);
|
||||
SetInt(501, 515);
|
||||
|
||||
SetHits(1500);
|
||||
|
||||
SetDamage(25, 33);
|
||||
|
||||
SetDamageType(ResistanceType.Physical, 70);
|
||||
SetDamageType(ResistanceType.Cold, 30);
|
||||
|
||||
SetResistance(ResistanceType.Physical, 55, 65);
|
||||
SetResistance(ResistanceType.Fire, 45, 55);
|
||||
SetResistance(ResistanceType.Cold, 45, 55);
|
||||
SetResistance(ResistanceType.Poison, 35, 45);
|
||||
SetResistance(ResistanceType.Energy, 25, 35);
|
||||
|
||||
SetSkill(SkillName.EvalInt, 97.6, 107.5);
|
||||
SetSkill(SkillName.Magery, 97.6, 107.5);
|
||||
SetSkill(SkillName.MagicResist, 97.6, 107.5);
|
||||
SetSkill(SkillName.Meditation, 97.6, 107.5);
|
||||
SetSkill(SkillName.Tactics, 97.6, 107.5);
|
||||
SetSkill(SkillName.Wrestling, 97.6, 107.5);
|
||||
|
||||
Fame = 24000;
|
||||
Karma = -24000;
|
||||
|
||||
VirtualArmor = 50;
|
||||
|
||||
CanSwim = true;
|
||||
CantWalk = true;
|
||||
|
||||
PackItem(new MessageInABottle());
|
||||
PackItem(new Rope { ItemID = 0x14F8 });
|
||||
PackItem(new Rope { ItemID = 0x14FA });
|
||||
}
|
||||
|
||||
public Leviathan(Serial serial) : base(serial)
|
||||
{
|
||||
}
|
||||
|
||||
public override string CorpseName => "a leviathan corpse";
|
||||
|
||||
public Mobile Fisher { get; set; }
|
||||
|
||||
public override string DefaultName => "a leviathan";
|
||||
|
||||
public override int TreasureMapLevel => 5;
|
||||
|
||||
public static Type[] Artifacts { get; } =
|
||||
{
|
||||
// Decorations
|
||||
typeof(CandelabraOfSouls),
|
||||
typeof(GhostShipAnchor),
|
||||
typeof(GoldBricks),
|
||||
typeof(PhillipsWoodenSteed),
|
||||
typeof(SeahorseStatuette),
|
||||
typeof(ShipModelOfTheHMSCape),
|
||||
typeof(AdmiralsHeartyRum),
|
||||
|
||||
// Equipment
|
||||
typeof(AlchemistsBauble),
|
||||
typeof(ArcticDeathDealer),
|
||||
typeof(BlazeOfDeath),
|
||||
typeof(BurglarsBandana),
|
||||
typeof(CaptainQuacklebushsCutlass),
|
||||
typeof(CavortingClub),
|
||||
typeof(DreadPirateHat),
|
||||
typeof(EnchantedTitanLegBone),
|
||||
typeof(GwennosHarp),
|
||||
typeof(IolosLute),
|
||||
typeof(LunaLance),
|
||||
typeof(NightsKiss),
|
||||
typeof(NoxRangersHeavyCrossbow),
|
||||
typeof(PolarBearMask),
|
||||
typeof(VioletCourage)
|
||||
};
|
||||
|
||||
private static MonsterAbility[] _abilities = { new LeviathanBreath() };
|
||||
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
|
||||
|
||||
public override void GenerateLoot()
|
||||
{
|
||||
AddLoot(LootPack.FilthyRich, 5);
|
||||
}
|
||||
|
||||
public override void Serialize(IGenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
writer.Write(0);
|
||||
}
|
||||
|
||||
public override void Deserialize(IGenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
var version = reader.ReadInt();
|
||||
}
|
||||
|
||||
public static void GiveArtifactTo(Mobile m)
|
||||
{
|
||||
var item = Loot.Construct(Artifacts);
|
||||
|
||||
if (item == null)
|
||||
{
|
||||
Fisher = fisher;
|
||||
|
||||
// May not be OSI accurate; mostly copied from krakens
|
||||
Body = 77;
|
||||
BaseSoundID = 353;
|
||||
|
||||
Hue = 0x481;
|
||||
|
||||
SetStr(1000);
|
||||
SetDex(501, 520);
|
||||
SetInt(501, 515);
|
||||
|
||||
SetHits(1500);
|
||||
|
||||
SetDamage(25, 33);
|
||||
|
||||
SetDamageType(ResistanceType.Physical, 70);
|
||||
SetDamageType(ResistanceType.Cold, 30);
|
||||
|
||||
SetResistance(ResistanceType.Physical, 55, 65);
|
||||
SetResistance(ResistanceType.Fire, 45, 55);
|
||||
SetResistance(ResistanceType.Cold, 45, 55);
|
||||
SetResistance(ResistanceType.Poison, 35, 45);
|
||||
SetResistance(ResistanceType.Energy, 25, 35);
|
||||
|
||||
SetSkill(SkillName.EvalInt, 97.6, 107.5);
|
||||
SetSkill(SkillName.Magery, 97.6, 107.5);
|
||||
SetSkill(SkillName.MagicResist, 97.6, 107.5);
|
||||
SetSkill(SkillName.Meditation, 97.6, 107.5);
|
||||
SetSkill(SkillName.Tactics, 97.6, 107.5);
|
||||
SetSkill(SkillName.Wrestling, 97.6, 107.5);
|
||||
|
||||
Fame = 24000;
|
||||
Karma = -24000;
|
||||
|
||||
VirtualArmor = 50;
|
||||
|
||||
CanSwim = true;
|
||||
CantWalk = true;
|
||||
|
||||
PackItem(new MessageInABottle());
|
||||
PackItem(new Rope { ItemID = 0x14F8 });
|
||||
PackItem(new Rope { ItemID = 0x14FA });
|
||||
return;
|
||||
}
|
||||
|
||||
public Leviathan(Serial serial) : base(serial)
|
||||
// TODO: Confirm messages
|
||||
if (m.AddToBackpack(item))
|
||||
{
|
||||
m.SendMessage("As a reward for slaying the mighty leviathan, an artifact has been placed in your backpack.");
|
||||
}
|
||||
|
||||
public override string CorpseName => "a leviathan corpse";
|
||||
|
||||
public Mobile Fisher { get; set; }
|
||||
|
||||
public override string DefaultName => "a leviathan";
|
||||
|
||||
public override int TreasureMapLevel => 5;
|
||||
|
||||
public static Type[] Artifacts { get; } =
|
||||
else
|
||||
{
|
||||
// Decorations
|
||||
typeof(CandelabraOfSouls),
|
||||
typeof(GhostShipAnchor),
|
||||
typeof(GoldBricks),
|
||||
typeof(PhillipsWoodenSteed),
|
||||
typeof(SeahorseStatuette),
|
||||
typeof(ShipModelOfTheHMSCape),
|
||||
typeof(AdmiralsHeartyRum),
|
||||
|
||||
// Equipment
|
||||
typeof(AlchemistsBauble),
|
||||
typeof(ArcticDeathDealer),
|
||||
typeof(BlazeOfDeath),
|
||||
typeof(BurglarsBandana),
|
||||
typeof(CaptainQuacklebushsCutlass),
|
||||
typeof(CavortingClub),
|
||||
typeof(DreadPirateHat),
|
||||
typeof(EnchantedTitanLegBone),
|
||||
typeof(GwennosHarp),
|
||||
typeof(IolosLute),
|
||||
typeof(LunaLance),
|
||||
typeof(NightsKiss),
|
||||
typeof(NoxRangersHeavyCrossbow),
|
||||
typeof(PolarBearMask),
|
||||
typeof(VioletCourage)
|
||||
};
|
||||
|
||||
private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath };
|
||||
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
|
||||
|
||||
public override void GenerateLoot()
|
||||
{
|
||||
AddLoot(LootPack.FilthyRich, 5);
|
||||
}
|
||||
|
||||
public override void Serialize(IGenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
writer.Write(0);
|
||||
}
|
||||
|
||||
public override void Deserialize(IGenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
var version = reader.ReadInt();
|
||||
}
|
||||
|
||||
public static void GiveArtifactTo(Mobile m)
|
||||
{
|
||||
var item = Loot.Construct(Artifacts);
|
||||
|
||||
if (item == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Confirm messages
|
||||
if (m.AddToBackpack(item))
|
||||
{
|
||||
m.SendMessage("As a reward for slaying the mighty leviathan, an artifact has been placed in your backpack.");
|
||||
}
|
||||
else
|
||||
{
|
||||
m.SendMessage(
|
||||
"As your backpack is full, your reward for destroying the legendary leviathan has been placed at your feet."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnKilledBy(Mobile mob)
|
||||
{
|
||||
base.OnKilledBy(mob);
|
||||
|
||||
if (Paragon.CheckArtifactChance(mob, this))
|
||||
{
|
||||
GiveArtifactTo(mob);
|
||||
|
||||
if (mob == Fisher)
|
||||
{
|
||||
Fisher = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnDeath(Container c)
|
||||
{
|
||||
base.OnDeath(c);
|
||||
|
||||
if (Fisher != null && Utility.Random(100) < 25)
|
||||
{
|
||||
GiveArtifactTo(Fisher);
|
||||
}
|
||||
|
||||
Fisher = null;
|
||||
}
|
||||
|
||||
private static MonsterAbility _ability = new LevianthanBreath();
|
||||
|
||||
public class LevianthanBreath : FireBreath
|
||||
{
|
||||
public override int PhysicalDamage => 70;
|
||||
public override int ColdDamage => 30;
|
||||
public override int FireDamage => 0;
|
||||
public override int BreathEffectHue => 0x1ED;
|
||||
public override double BreathDamageScalar => 0.05;
|
||||
public override TimeSpan MinTriggerCooldown => TimeSpan.FromSeconds(5.0);
|
||||
public override TimeSpan MaxTriggerCooldown => TimeSpan.FromSeconds(7.5);
|
||||
m.SendMessage(
|
||||
"As your backpack is full, your reward for destroying the legendary leviathan has been placed at your feet."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnKilledBy(Mobile mob)
|
||||
{
|
||||
base.OnKilledBy(mob);
|
||||
|
||||
if (Paragon.CheckArtifactChance(mob, this))
|
||||
{
|
||||
GiveArtifactTo(mob);
|
||||
|
||||
if (mob == Fisher)
|
||||
{
|
||||
Fisher = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnDeath(Container c)
|
||||
{
|
||||
base.OnDeath(c);
|
||||
|
||||
if (Fisher != null && Utility.Random(100) < 25)
|
||||
{
|
||||
GiveArtifactTo(Fisher);
|
||||
}
|
||||
|
||||
Fisher = null;
|
||||
}
|
||||
|
||||
private class LeviathanBreath : FireBreath
|
||||
{
|
||||
public override int PhysicalDamage => 70;
|
||||
public override int ColdDamage => 30;
|
||||
public override int FireDamage => 0;
|
||||
public override int BreathEffectHue => 0x1ED;
|
||||
public override double BreathDamageScalar => 0.05;
|
||||
public override TimeSpan MinTriggerCooldown => TimeSpan.FromSeconds(5.0);
|
||||
public override TimeSpan MaxTriggerCooldown => TimeSpan.FromSeconds(7.5);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,156 +2,106 @@ using System;
|
|||
using Server.Engines.CannedEvil;
|
||||
using Server.Items;
|
||||
|
||||
namespace Server.Mobiles
|
||||
namespace Server.Mobiles;
|
||||
|
||||
public class Semidar : BaseChampion
|
||||
{
|
||||
public class Semidar : BaseChampion
|
||||
[Constructible]
|
||||
public Semidar() : base(AIType.AI_Mage)
|
||||
{
|
||||
[Constructible]
|
||||
public Semidar() : base(AIType.AI_Mage)
|
||||
Body = 174;
|
||||
BaseSoundID = 0x4B0;
|
||||
|
||||
SetStr(502, 600);
|
||||
SetDex(102, 200);
|
||||
SetInt(601, 750);
|
||||
|
||||
SetHits(1500);
|
||||
SetStam(103, 250);
|
||||
|
||||
SetDamage(29, 35);
|
||||
|
||||
SetDamageType(ResistanceType.Physical, 75);
|
||||
SetDamageType(ResistanceType.Fire, 25);
|
||||
|
||||
SetResistance(ResistanceType.Physical, 20, 30);
|
||||
SetResistance(ResistanceType.Fire, 50, 60);
|
||||
SetResistance(ResistanceType.Cold, 20, 30);
|
||||
SetResistance(ResistanceType.Poison, 20, 30);
|
||||
SetResistance(ResistanceType.Energy, 10, 20);
|
||||
|
||||
SetSkill(SkillName.EvalInt, 95.1, 100.0);
|
||||
SetSkill(SkillName.Magery, 90.1, 105.0);
|
||||
SetSkill(SkillName.Meditation, 95.1, 100.0);
|
||||
SetSkill(SkillName.MagicResist, 120.2, 140.0);
|
||||
SetSkill(SkillName.Tactics, 90.1, 105.0);
|
||||
SetSkill(SkillName.Wrestling, 90.1, 105.0);
|
||||
|
||||
Fame = 24000;
|
||||
Karma = -24000;
|
||||
|
||||
VirtualArmor = 20;
|
||||
}
|
||||
|
||||
public Semidar(Serial serial) : base(serial)
|
||||
{
|
||||
}
|
||||
|
||||
public override ChampionSkullType SkullType => ChampionSkullType.Pain;
|
||||
|
||||
public override Type[] UniqueList => new[] { typeof(GladiatorsCollar) };
|
||||
|
||||
public override Type[] SharedList => new[]
|
||||
{ typeof(RoyalGuardSurvivalKnife), typeof(ANecromancerShroud), typeof(LieutenantOfTheBritannianRoyalGuard) };
|
||||
|
||||
public override Type[] DecorativeList => new[] { typeof(LavaTile), typeof(DemonSkull) };
|
||||
|
||||
public override MonsterStatuetteType[] StatueTypes => Array.Empty<MonsterStatuetteType>();
|
||||
|
||||
public override string DefaultName => "Semidar";
|
||||
|
||||
public override bool Unprovokable => true;
|
||||
public override Poison PoisonImmune => Poison.Lethal;
|
||||
|
||||
public override void GenerateLoot()
|
||||
{
|
||||
AddLoot(LootPack.UltraRich, 4);
|
||||
AddLoot(LootPack.FilthyRich);
|
||||
}
|
||||
|
||||
public override void CheckReflect(Mobile caster, ref bool reflect)
|
||||
{
|
||||
if (caster.Body.IsMale)
|
||||
{
|
||||
Body = 174;
|
||||
BaseSoundID = 0x4B0;
|
||||
|
||||
SetStr(502, 600);
|
||||
SetDex(102, 200);
|
||||
SetInt(601, 750);
|
||||
|
||||
SetHits(1500);
|
||||
SetStam(103, 250);
|
||||
|
||||
SetDamage(29, 35);
|
||||
|
||||
SetDamageType(ResistanceType.Physical, 75);
|
||||
SetDamageType(ResistanceType.Fire, 25);
|
||||
|
||||
SetResistance(ResistanceType.Physical, 20, 30);
|
||||
SetResistance(ResistanceType.Fire, 50, 60);
|
||||
SetResistance(ResistanceType.Cold, 20, 30);
|
||||
SetResistance(ResistanceType.Poison, 20, 30);
|
||||
SetResistance(ResistanceType.Energy, 10, 20);
|
||||
|
||||
SetSkill(SkillName.EvalInt, 95.1, 100.0);
|
||||
SetSkill(SkillName.Magery, 90.1, 105.0);
|
||||
SetSkill(SkillName.Meditation, 95.1, 100.0);
|
||||
SetSkill(SkillName.MagicResist, 120.2, 140.0);
|
||||
SetSkill(SkillName.Tactics, 90.1, 105.0);
|
||||
SetSkill(SkillName.Wrestling, 90.1, 105.0);
|
||||
|
||||
Fame = 24000;
|
||||
Karma = -24000;
|
||||
|
||||
VirtualArmor = 20;
|
||||
}
|
||||
|
||||
public Semidar(Serial serial) : base(serial)
|
||||
{
|
||||
}
|
||||
|
||||
public override ChampionSkullType SkullType => ChampionSkullType.Pain;
|
||||
|
||||
public override Type[] UniqueList => new[] { typeof(GladiatorsCollar) };
|
||||
|
||||
public override Type[] SharedList => new[]
|
||||
{ typeof(RoyalGuardSurvivalKnife), typeof(ANecromancerShroud), typeof(LieutenantOfTheBritannianRoyalGuard) };
|
||||
|
||||
public override Type[] DecorativeList => new[] { typeof(LavaTile), typeof(DemonSkull) };
|
||||
|
||||
public override MonsterStatuetteType[] StatueTypes => Array.Empty<MonsterStatuetteType>();
|
||||
|
||||
public override string DefaultName => "Semidar";
|
||||
|
||||
public override bool Unprovokable => true;
|
||||
public override Poison PoisonImmune => Poison.Lethal;
|
||||
|
||||
public override void GenerateLoot()
|
||||
{
|
||||
AddLoot(LootPack.UltraRich, 4);
|
||||
AddLoot(LootPack.FilthyRich);
|
||||
}
|
||||
|
||||
public override void CheckReflect(Mobile caster, ref bool reflect)
|
||||
{
|
||||
if (caster.Body.IsMale)
|
||||
{
|
||||
reflect = true; // Always reflect if caster isn't female
|
||||
}
|
||||
}
|
||||
|
||||
public override void AlterDamageScalarFrom(Mobile caster, ref double scalar)
|
||||
{
|
||||
if (caster.Body.IsMale)
|
||||
{
|
||||
scalar = 20; // Male bodies always reflect.. damage scaled 20x
|
||||
}
|
||||
}
|
||||
|
||||
public void DrainLife()
|
||||
{
|
||||
if (Map == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var eable = GetMobilesInRange<Mobile>(2);
|
||||
|
||||
foreach (var m in eable)
|
||||
{
|
||||
if (m == this || !(CanBeHarmful(m) || m.Player && m.Alive))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m is not BaseCreature bc || !(bc.Controlled || bc.Summoned || bc.Team != Team))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
DoHarmful(m);
|
||||
|
||||
m.FixedParticles(0x374A, 10, 15, 5013, 0x496, 0, EffectLayer.Waist);
|
||||
m.PlaySound(0x231);
|
||||
|
||||
m.SendMessage("You feel the life drain out of you!");
|
||||
|
||||
var toDrain = Utility.RandomMinMax(10, 40);
|
||||
|
||||
Hits += toDrain;
|
||||
m.Damage(toDrain, this);
|
||||
}
|
||||
|
||||
eable.Free();
|
||||
}
|
||||
|
||||
public override void OnGaveMeleeAttack(Mobile defender, int damage)
|
||||
{
|
||||
base.OnGaveMeleeAttack(defender, damage);
|
||||
|
||||
if (Utility.RandomDouble() <= 0.25)
|
||||
{
|
||||
DrainLife();
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnGotMeleeAttack(Mobile attacker, int damage)
|
||||
{
|
||||
base.OnGotMeleeAttack(attacker, damage);
|
||||
|
||||
if (Utility.RandomDouble() <= 0.25)
|
||||
{
|
||||
DrainLife();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Serialize(IGenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
writer.Write(0);
|
||||
}
|
||||
|
||||
public override void Deserialize(IGenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
var version = reader.ReadInt();
|
||||
reflect = true; // Always reflect if caster isn't female
|
||||
}
|
||||
}
|
||||
|
||||
public override void AlterDamageScalarFrom(Mobile caster, ref double scalar)
|
||||
{
|
||||
if (caster.Body.IsMale)
|
||||
{
|
||||
scalar = 20; // Male bodies always reflect.. damage scaled 20x
|
||||
}
|
||||
}
|
||||
|
||||
private static MonsterAbility[] _abilities = { new SemidarDrainLife() };
|
||||
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
|
||||
|
||||
public override void Serialize(IGenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
writer.Write(0);
|
||||
}
|
||||
|
||||
public override void Deserialize(IGenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
var version = reader.ReadInt();
|
||||
}
|
||||
|
||||
private class SemidarDrainLife : DrainLifeAreaAttack
|
||||
{
|
||||
public override double ChanceToTrigger => 0.25;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue