feat: Adds drain life ability (#1181)

This commit is contained in:
Kamron Batman 2022-10-02 11:36:15 -07:00 committed by GitHub
parent dc6f75c3b4
commit 1cf5d59116
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 431 additions and 531 deletions

View file

@ -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);
}

View file

@ -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;

View 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);
}
}

View file

@ -26,4 +26,6 @@ public abstract partial class MonsterAbility
public static ThrowHatchetCounter ThrowHatchetCounter => new();
public static DestroyEquipment DestroyEquipment => new();
public static DrainLifeAreaAttack DrainLifeAreaAttack => new();
}

View file

@ -10,5 +10,7 @@ public enum MonsterAbilityType
Poison,
MassPoison,
ThrowWeapon,
DestroyEquipment
DestroyEquipment,
DeathExplosion,
MassDrainLife
}

View file

@ -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);

View file

@ -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)
{

View file

@ -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);
}
}
}

View file

@ -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

View file

@ -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
}
}

View file

@ -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);
}
}

View file

@ -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;
}
}