feat: Adds Monster Abilities (#1179)
* Adds ability to create monster abilities * Adds Fire/Cold/Chaos Breath * Adds Grasping Claw * Adds Summon Skeletons & Summon Undead w/ Polymorph
This commit is contained in:
parent
8a2c0ead3d
commit
43e7fe29c2
91 changed files with 1262 additions and 977 deletions
|
|
@ -1062,6 +1062,9 @@ public static class Utility
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool RandomBool() => RandomSources.Source.NextBool();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static TimeSpan RandomMinMax(TimeSpan min, TimeSpan max) => new(RandomMinMax(min.Ticks, max.Ticks));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static double RandomMinMax(double min, double max)
|
||||
{
|
||||
|
|
@ -1540,4 +1543,67 @@ public static class Utility
|
|||
|
||||
return combined;
|
||||
}
|
||||
|
||||
public static Point3D GetValidLocation(Map map, Point3D center, int range, int retries = 10)
|
||||
{
|
||||
if (map == null)
|
||||
{
|
||||
return center;
|
||||
}
|
||||
|
||||
var loc = new Point3D(center.Z, center.Y, center.Z);
|
||||
|
||||
for (var i = 0; i < retries; i++)
|
||||
{
|
||||
loc.X = center.X + (Random(range * 2 + 1) - range);
|
||||
loc.Y = center.Y + (Random(range * 2 + 1) - range);
|
||||
loc.Z = center.Z;
|
||||
|
||||
if (map.CanSpawnMobile(loc))
|
||||
{
|
||||
return loc;
|
||||
}
|
||||
|
||||
loc.Z = map.GetAverageZ(loc.X, loc.Y);
|
||||
|
||||
if (map.CanSpawnMobile(loc))
|
||||
{
|
||||
return loc;
|
||||
}
|
||||
}
|
||||
|
||||
return center;
|
||||
}
|
||||
|
||||
public static Point3D GetValidLocationInLOS(Map map, Mobile from, int range, int retries = 10)
|
||||
{
|
||||
if (map == null)
|
||||
{
|
||||
return from.Location;
|
||||
}
|
||||
|
||||
var center = from.Location;
|
||||
var loc = center;
|
||||
|
||||
for (var i = 0; i < retries; i++)
|
||||
{
|
||||
loc.X = center.X + (Random(range * 2 + 1) - range);
|
||||
loc.Y = center.Y + (Random(range * 2 + 1) - range);
|
||||
loc.Z = center.Z;
|
||||
|
||||
if (map.CanSpawnMobile(loc) && map.LineOfSight(from, loc))
|
||||
{
|
||||
return loc;
|
||||
}
|
||||
|
||||
loc.Z = map.GetAverageZ(loc.X, loc.Y);
|
||||
|
||||
if (map.CanSpawnMobile(loc) && map.LineOfSight(from, loc))
|
||||
{
|
||||
return loc;
|
||||
}
|
||||
}
|
||||
|
||||
return center;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,32 +39,26 @@ namespace Server.Mobiles
|
|||
ControlSlots = 1;
|
||||
}
|
||||
|
||||
public UnholySteed(Serial serial)
|
||||
: base(serial)
|
||||
public UnholySteed(Serial serial) : base(serial)
|
||||
{
|
||||
}
|
||||
|
||||
public override string CorpseName => "an unholy corpse";
|
||||
public override bool IsDispellable => false;
|
||||
public override bool IsBondable => false;
|
||||
|
||||
public override bool HasBreath => true;
|
||||
public override bool CanBreath => true;
|
||||
|
||||
public override FoodType FavoriteFood => FoodType.FruitsAndVegies | FoodType.GrainsAndHay;
|
||||
|
||||
private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath };
|
||||
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
|
||||
|
||||
public override string ApplyNameSuffix(string suffix)
|
||||
{
|
||||
if (suffix.Length == 0)
|
||||
{
|
||||
suffix = Ethic.Evil.Definition.Adjunct.String;
|
||||
}
|
||||
else
|
||||
{
|
||||
suffix = $"{suffix} {Ethic.Evil.Definition.Adjunct.String}";
|
||||
return base.ApplyNameSuffix(Ethic.Evil.Definition.Adjunct.String);
|
||||
}
|
||||
|
||||
return base.ApplyNameSuffix(suffix);
|
||||
return base.ApplyNameSuffix($"{suffix} {Ethic.Evil.Definition.Adjunct.String}");
|
||||
}
|
||||
|
||||
public override void OnDoubleClick(Mobile from)
|
||||
|
|
|
|||
|
|
@ -39,32 +39,26 @@ namespace Server.Mobiles
|
|||
ControlSlots = 1;
|
||||
}
|
||||
|
||||
public HolySteed(Serial serial)
|
||||
: base(serial)
|
||||
public HolySteed(Serial serial) : base(serial)
|
||||
{
|
||||
}
|
||||
|
||||
public override string CorpseName => "a holy corpse";
|
||||
public override bool IsDispellable => false;
|
||||
public override bool IsBondable => false;
|
||||
|
||||
public override bool HasBreath => true;
|
||||
public override bool CanBreath => true;
|
||||
|
||||
public override FoodType FavoriteFood => FoodType.FruitsAndVegies | FoodType.GrainsAndHay;
|
||||
|
||||
private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath };
|
||||
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
|
||||
|
||||
public override string ApplyNameSuffix(string suffix)
|
||||
{
|
||||
if (suffix.Length == 0)
|
||||
{
|
||||
suffix = Ethic.Hero.Definition.Adjunct.String;
|
||||
}
|
||||
else
|
||||
{
|
||||
suffix = $"{suffix} {Ethic.Hero.Definition.Adjunct.String}";
|
||||
return base.ApplyNameSuffix(Ethic.Hero.Definition.Adjunct.String);
|
||||
}
|
||||
|
||||
return base.ApplyNameSuffix(suffix);
|
||||
return base.ApplyNameSuffix($"{suffix} {Ethic.Hero.Definition.Adjunct.String}");
|
||||
}
|
||||
|
||||
public override void OnDoubleClick(Mobile from)
|
||||
|
|
|
|||
|
|
@ -247,7 +247,7 @@ namespace Server.Items
|
|||
|
||||
public static bool IsMageryCreature(BaseCreature bc) => bc?.AI == AIType.AI_Mage && bc.Skills.Magery.Base > 5.0;
|
||||
|
||||
public static bool IsFireBreathingCreature(BaseCreature bc) => bc?.HasBreath == true;
|
||||
public static bool IsFireBreathingCreature(BaseCreature bc) => bc?.HasAbility(MonsterAbilityType.FireBreath) == true;
|
||||
|
||||
public static bool IsPoisonImmune(BaseCreature bc) => bc?.PoisonImmune != null;
|
||||
|
||||
|
|
|
|||
|
|
@ -83,8 +83,7 @@ namespace Server.Items
|
|||
public static readonly WeaponAbility InfusedThrow = Abilities[30];
|
||||
public static readonly WeaponAbility MysticArc = Abilities[31];
|
||||
|
||||
private static readonly Dictionary<Mobile, WeaponAbilityContext> m_PlayersTable =
|
||||
new();
|
||||
private static readonly Dictionary<Mobile, WeaponAbilityContext> m_PlayersTable = new();
|
||||
|
||||
public virtual int BaseMana => 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -2085,8 +2085,8 @@ namespace Server.Items
|
|||
}
|
||||
}
|
||||
|
||||
bcAtt?.OnGaveMeleeAttack(defender);
|
||||
bcDef?.OnGotMeleeAttack(attacker);
|
||||
bcAtt?.OnGaveMeleeAttack(defender, damage);
|
||||
bcDef?.OnGotMeleeAttack(attacker, damage);
|
||||
|
||||
a?.OnHit(attacker, defender, damage);
|
||||
move?.OnHit(attacker, defender, damage);
|
||||
|
|
|
|||
|
|
@ -37,14 +37,12 @@ namespace Server
|
|||
public static int Damage(
|
||||
Mobile m, Mobile from, int damage, int phys, int fire, int cold, int pois, int nrgy,
|
||||
int chaos
|
||||
) =>
|
||||
Damage(m, from, damage, false, phys, fire, cold, pois, nrgy, chaos);
|
||||
) => Damage(m, from, damage, false, phys, fire, cold, pois, nrgy, chaos);
|
||||
|
||||
public static int Damage(
|
||||
Mobile m, Mobile from, int damage, int phys, int fire, int cold, int pois, int nrgy,
|
||||
bool keepAlive
|
||||
) =>
|
||||
Damage(m, from, damage, false, phys, fire, cold, pois, nrgy, 0, 0, keepAlive);
|
||||
) => Damage(m, from, damage, false, phys, fire, cold, pois, nrgy, 0, 0, keepAlive);
|
||||
|
||||
public static int Damage(
|
||||
Mobile m, Mobile from, int damage, bool ignoreArmor, int phys, int fire, int cold, int pois,
|
||||
|
|
@ -80,20 +78,30 @@ namespace Server
|
|||
switch (Utility.Random(5))
|
||||
{
|
||||
case 0:
|
||||
phys += chaos;
|
||||
break;
|
||||
{
|
||||
phys += chaos;
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
fire += chaos;
|
||||
break;
|
||||
{
|
||||
fire += chaos;
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
cold += chaos;
|
||||
break;
|
||||
{
|
||||
cold += chaos;
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
pois += chaos;
|
||||
break;
|
||||
{
|
||||
pois += chaos;
|
||||
break;
|
||||
}
|
||||
case 4:
|
||||
nrgy += chaos;
|
||||
break;
|
||||
{
|
||||
nrgy += chaos;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
8
Projects/UOContent/Mobiles/Abilities/ChaosBreath.cs
Normal file
8
Projects/UOContent/Mobiles/Abilities/ChaosBreath.cs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
namespace Server.Mobiles;
|
||||
|
||||
public class ChaosBreath : FireBreath
|
||||
{
|
||||
public override int BreathChaosDamage => 100;
|
||||
|
||||
public override int BreathFireDamage => 0;
|
||||
}
|
||||
8
Projects/UOContent/Mobiles/Abilities/ColdBreath.cs
Normal file
8
Projects/UOContent/Mobiles/Abilities/ColdBreath.cs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
namespace Server.Mobiles;
|
||||
|
||||
public class ColdBreath : FireBreath
|
||||
{
|
||||
public override int BreathColdDamage => 100;
|
||||
public override int BreathFireDamage => 0;
|
||||
public override int BreathEffectHue => 0x480;
|
||||
}
|
||||
218
Projects/UOContent/Mobiles/Abilities/FireBreath.cs
Normal file
218
Projects/UOContent/Mobiles/Abilities/FireBreath.cs
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
using System;
|
||||
using Server.Spells.Bushido;
|
||||
|
||||
namespace Server.Mobiles;
|
||||
|
||||
public class FireBreath : MonsterAbility
|
||||
{
|
||||
public override MonsterAbilityTrigger AbilityTrigger => MonsterAbilityTrigger.Action;
|
||||
public override MonsterAbilityType AbilityType => MonsterAbilityType.FireBreath;
|
||||
public override double ChanceToTrigger => 0.5;
|
||||
|
||||
// Min/max seconds until next breath
|
||||
public override TimeSpan MinTriggerCooldown => TimeSpan.FromSeconds(30.0);
|
||||
public override TimeSpan MaxTriggerCooldown => TimeSpan.FromSeconds(45.0);
|
||||
|
||||
// Base damage given is: CurrentHitPoints * BreathDamageScalar
|
||||
public virtual double BreathDamageScalar => Core.AOS ? 0.16 : 0.05;
|
||||
|
||||
// Creature stops moving for 1.0 seconds while breathing
|
||||
public virtual double BreathStallTime => 1.0;
|
||||
|
||||
// Effect is sent 1.3 seconds after BreathAngerSound and BreathAngerAnimation is played
|
||||
public virtual double BreathEffectDelay => 1.3;
|
||||
|
||||
// Damage is given 1.0 seconds after effect is sent
|
||||
public virtual double BreathDamageDelay => 1.0;
|
||||
public virtual int BreathRange(BaseCreature source) => source.RangePerception;
|
||||
|
||||
// Damage Types
|
||||
public virtual int BreathChaosDamage => 0;
|
||||
public virtual int BreathPhysicalDamage => 0;
|
||||
public virtual int BreathFireDamage => 100;
|
||||
public virtual int BreathColdDamage => 0;
|
||||
public virtual int BreathPoisonDamage => 0;
|
||||
public virtual int BreathEnergyDamage => 0;
|
||||
|
||||
// Effect details and sound
|
||||
public virtual int BreathEffectItemID => 0x36D4;
|
||||
public virtual int BreathEffectSpeed => 5;
|
||||
public virtual int BreathEffectDuration => 0;
|
||||
public virtual bool BreathEffectExplodes => false;
|
||||
public virtual bool BreathEffectFixedDir => false;
|
||||
public virtual int BreathEffectHue => 0;
|
||||
public virtual int BreathEffectRenderMode => 0;
|
||||
|
||||
public virtual int BreathEffectSound => 0x227;
|
||||
|
||||
public override bool CanTrigger(BaseCreature source) => !source.Summoned && base.CanTrigger(source);
|
||||
|
||||
public override void Trigger(BaseCreature source, Mobile target)
|
||||
{
|
||||
if (CanFireBreathTarget(source, target))
|
||||
{
|
||||
BreathStallMovement(source);
|
||||
BreathPlayAngerSound(source);
|
||||
BreathPlayAngerAnimation(source);
|
||||
|
||||
source.Direction = source.GetDirectionTo(target);
|
||||
|
||||
Timer.StartTimer(TimeSpan.FromSeconds(BreathEffectDelay), () => BreathEffect_Callback(source, target));
|
||||
|
||||
base.Trigger(source, target);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool CanFireBreathTarget(BaseCreature source, Mobile target)
|
||||
{
|
||||
var range = BreathRange(source);
|
||||
|
||||
return !source.IsDeadBondedPet && !source.BardPacified && target is { Alive: true, IsDeadBondedPet: false } &&
|
||||
target.Map == source.Map && source.CanBeHarmful(target) && target.InRange(source, range) &&
|
||||
source.InLOS(target);
|
||||
}
|
||||
|
||||
// Anger sound/animations
|
||||
public virtual int BreathAngerSound(BaseCreature source) => source.GetAngerSound();
|
||||
public virtual int BreathAngerAnimation => 12;
|
||||
|
||||
public virtual void BreathStallMovement(BaseCreature source)
|
||||
{
|
||||
if (source.AIObject != null)
|
||||
{
|
||||
source.AIObject.NextMove = Core.TickCount + (int)(BreathStallTime * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void BreathPlayAngerSound(BaseCreature source)
|
||||
{
|
||||
var sound = BreathAngerSound(source);
|
||||
source.PlaySound(sound);
|
||||
}
|
||||
|
||||
public virtual void BreathPlayAngerAnimation(BaseCreature source)
|
||||
{
|
||||
source.Animate(BreathAngerAnimation, 5, 1, true, false, 0);
|
||||
}
|
||||
|
||||
public virtual void BreathEffect_Callback(BaseCreature source, Mobile target)
|
||||
{
|
||||
if (!target.Alive || !source.CanBeHarmful(target))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BreathPlayEffectSound(source);
|
||||
BreathPlayEffect(source, target);
|
||||
|
||||
Timer.StartTimer(TimeSpan.FromSeconds(BreathDamageDelay), () => BreathDamage_Callback(source, target));
|
||||
}
|
||||
|
||||
public virtual void BreathPlayEffectSound(BaseCreature source)
|
||||
{
|
||||
source.PlaySound(BreathEffectSound);
|
||||
}
|
||||
|
||||
public virtual void BreathPlayEffect(BaseCreature source, Mobile target)
|
||||
{
|
||||
Effects.SendMovingEffect(
|
||||
source,
|
||||
target,
|
||||
BreathEffectItemID,
|
||||
BreathEffectSpeed,
|
||||
BreathEffectDuration,
|
||||
BreathEffectFixedDir,
|
||||
BreathEffectExplodes,
|
||||
BreathEffectHue,
|
||||
BreathEffectRenderMode
|
||||
);
|
||||
}
|
||||
|
||||
public virtual void BreathDamage_Callback(BaseCreature source, Mobile target)
|
||||
{
|
||||
if (target is BaseCreature creature && creature.BreathImmune)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (source.CanBeHarmful(target))
|
||||
{
|
||||
source.DoHarmful(target);
|
||||
BreathDealDamage(source, target);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void BreathDealDamage(BaseCreature source, Mobile target)
|
||||
{
|
||||
if (!Evasion.CheckSpellEvasion(target))
|
||||
{
|
||||
var physDamage = BreathPhysicalDamage;
|
||||
var fireDamage = BreathFireDamage;
|
||||
var coldDamage = BreathColdDamage;
|
||||
var poisDamage = BreathPoisonDamage;
|
||||
var nrgyDamage = BreathEnergyDamage;
|
||||
|
||||
if (BreathChaosDamage > 0)
|
||||
{
|
||||
switch (Utility.Random(5))
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
physDamage += BreathChaosDamage;
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
fireDamage += BreathChaosDamage;
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
coldDamage += BreathChaosDamage;
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
poisDamage += BreathChaosDamage;
|
||||
break;
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
nrgyDamage += BreathChaosDamage;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (physDamage == 0 && fireDamage == 0 && coldDamage == 0 && poisDamage == 0 && nrgyDamage == 0)
|
||||
{
|
||||
target.Damage(BreathComputeDamage(source), source); // Unresistable damage even in AOS
|
||||
}
|
||||
else
|
||||
{
|
||||
AOS.Damage(
|
||||
target,
|
||||
source,
|
||||
BreathComputeDamage(source),
|
||||
physDamage,
|
||||
fireDamage,
|
||||
coldDamage,
|
||||
poisDamage,
|
||||
nrgyDamage
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual int BreathComputeDamage(BaseCreature source)
|
||||
{
|
||||
var damage = (int)(source.Hits * BreathDamageScalar);
|
||||
|
||||
if (source.IsParagon)
|
||||
{
|
||||
damage = (int)(damage / Paragon.HitsBuff);
|
||||
}
|
||||
|
||||
return Math.Min(damage, 200);
|
||||
}
|
||||
}
|
||||
87
Projects/UOContent/Mobiles/Abilities/GraspingClaw.cs
Normal file
87
Projects/UOContent/Mobiles/Abilities/GraspingClaw.cs
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Server.Mobiles;
|
||||
|
||||
public class GraspingClaw : MonsterAbility
|
||||
{
|
||||
private Dictionary<Mobile, ExpireTimer> _table;
|
||||
|
||||
public override MonsterAbilityType AbilityType => MonsterAbilityType.GraspingClaw;
|
||||
public override MonsterAbilityTrigger AbilityTrigger => MonsterAbilityTrigger.GiveMeleeDamage;
|
||||
public override double ChanceToTrigger => 0.10;
|
||||
|
||||
public override void Trigger(BaseCreature source, Mobile target)
|
||||
{
|
||||
if (_table.Remove(target, out var timer))
|
||||
{
|
||||
timer.DoExpire();
|
||||
target.SendLocalizedMessage(1070837); // The creature lands another blow in your weakened state.
|
||||
}
|
||||
else
|
||||
{
|
||||
// The blow from the creature's claws has made you more susceptible to physical attacks.
|
||||
target.SendLocalizedMessage(1070836);
|
||||
}
|
||||
|
||||
/**
|
||||
* Grasping Claw
|
||||
* Start cliloc: 1070836
|
||||
* Effect: Physical resistance -15% for 5 seconds
|
||||
* Refresh Cliloc: 1070837
|
||||
* End cliloc: 1070838
|
||||
* Effect:
|
||||
* - Type: "3"
|
||||
* - From: Player
|
||||
* - To: 0x0
|
||||
* - ItemId: "0x37B9"
|
||||
* - ItemIdName: "glow"
|
||||
* - FromLocation: "(1149 808, 32)"
|
||||
* - ToLocation: "(1149 808, 32)"
|
||||
* - Speed: "10" - Duration: "5"
|
||||
* - FixedDirection: "True"
|
||||
* - Explode: "False"
|
||||
*/
|
||||
var effect = -(target.PhysicalResistance * 15 / 100);
|
||||
|
||||
var mod = new ResistanceMod(ResistanceType.Physical, effect);
|
||||
|
||||
target.FixedEffect(0x37B9, 10, 5);
|
||||
target.AddResistanceMod(mod);
|
||||
|
||||
timer = new ExpireTimer(this, target, mod, TimeSpan.FromSeconds(5.0));
|
||||
timer.Start();
|
||||
|
||||
_table ??= new Dictionary<Mobile, ExpireTimer>();
|
||||
_table[target] = timer;
|
||||
|
||||
base.Trigger(source, target);
|
||||
}
|
||||
|
||||
private class ExpireTimer : Timer
|
||||
{
|
||||
private GraspingClaw _graspingClaw;
|
||||
private Mobile _mobile;
|
||||
private ResistanceMod _mod;
|
||||
|
||||
public ExpireTimer(GraspingClaw claw, Mobile m, ResistanceMod mod, TimeSpan delay) : base(delay)
|
||||
{
|
||||
_mobile = m;
|
||||
_mod = mod;
|
||||
_graspingClaw = claw;
|
||||
}
|
||||
|
||||
public void DoExpire()
|
||||
{
|
||||
_mobile.RemoveResistanceMod(_mod);
|
||||
Stop();
|
||||
_graspingClaw._table?.Remove(_mobile);
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
_mobile.SendLocalizedMessage(1070838); // Your resistance to physical attacks has returned.
|
||||
DoExpire();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
namespace Server.Mobiles;
|
||||
|
||||
public abstract partial class MonsterAbility
|
||||
{
|
||||
// Fire Breath
|
||||
public static FireBreath FireBreath => new();
|
||||
public static ChaosBreath ChaosBreath => new();
|
||||
public static ColdBreath ColdBreath => new();
|
||||
|
||||
public static GraspingClaw GraspingClaw => new();
|
||||
|
||||
// Summon Undead
|
||||
public static SummonSkeletons SummonSkeletons => new();
|
||||
public static SummonLesserUndead SummonLesserUndead => new();
|
||||
}
|
||||
73
Projects/UOContent/Mobiles/Abilities/MonsterAbility.cs
Normal file
73
Projects/UOContent/Mobiles/Abilities/MonsterAbility.cs
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Server.Mobiles;
|
||||
|
||||
/// <summary>
|
||||
/// Abstract class used to build singletons for managing a specific monster ability.
|
||||
/// </summary>
|
||||
public abstract partial class MonsterAbility
|
||||
{
|
||||
private Dictionary<BaseCreature, long> _nextTriggerTicks;
|
||||
|
||||
public virtual MonsterAbilityType AbilityType => MonsterAbilityType.Generic;
|
||||
|
||||
public virtual MonsterAbilityTrigger AbilityTrigger => MonsterAbilityTrigger.None;
|
||||
|
||||
public virtual double ChanceToTrigger => 1.0;
|
||||
public virtual TimeSpan MinTriggerCooldown => TimeSpan.Zero;
|
||||
public virtual TimeSpan MaxTriggerCooldown => TimeSpan.Zero;
|
||||
|
||||
public bool WillTrigger(MonsterAbilityTrigger trigger) => (AbilityTrigger & trigger) != 0;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if ability is not on cooldown, and the change to trigger succeeds.
|
||||
/// </summary>
|
||||
/// <param name="source">The mobile this ability is attached to</param>
|
||||
/// <returns>Boolean indicating the ability can trigger.</returns>
|
||||
public virtual bool CanTrigger(BaseCreature source)
|
||||
{
|
||||
if (_nextTriggerTicks?.TryGetValue(source, out var nextTrigger) == true && Core.TickCount < nextTrigger)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var c = ChanceToTrigger;
|
||||
|
||||
if (c >= 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (c <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var rnd = Utility.RandomDouble();
|
||||
|
||||
return c > rnd;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triggers the monster's ability. Override this and call `base.Trigger(source);` to make sure
|
||||
/// the cooldown is tracked.
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
public virtual void Trigger(BaseCreature source, Mobile target)
|
||||
{
|
||||
if (MinTriggerCooldown <= TimeSpan.Zero && MaxTriggerCooldown <= TimeSpan.Zero)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var randomAmount = MinTriggerCooldown == MaxTriggerCooldown
|
||||
? MinTriggerCooldown
|
||||
: Utility.RandomMinMax(MinTriggerCooldown, MaxTriggerCooldown);
|
||||
|
||||
var nextTrigger = Core.TickCount + (long)randomAmount.TotalMilliseconds;
|
||||
|
||||
_nextTriggerTicks ??= new Dictionary<BaseCreature, long>();
|
||||
_nextTriggerTicks[source] = nextTrigger;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
using System;
|
||||
|
||||
namespace Server.Mobiles;
|
||||
|
||||
[Flags]
|
||||
public enum MonsterAbilityTrigger
|
||||
{
|
||||
None = 0x00000000, // Passive, or some custom event.
|
||||
Action = 0x00000001, // On Think
|
||||
TakeMeleeDamage = 0x00000002,
|
||||
GiveMeleeDamage = 0x00000004, // Includes firing a bow
|
||||
TakeSpellDamage = 0x00000008,
|
||||
GiveSpellDamage = 0x00000010,
|
||||
|
||||
TakeDamage = TakeMeleeDamage | TakeSpellDamage
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
namespace Server.Mobiles;
|
||||
|
||||
public enum MonsterAbilityType
|
||||
{
|
||||
Generic,
|
||||
FireBreath,
|
||||
GraspingClaw,
|
||||
SummonUndead
|
||||
}
|
||||
33
Projects/UOContent/Mobiles/Abilities/SummonLesserUndead.cs
Normal file
33
Projects/UOContent/Mobiles/Abilities/SummonLesserUndead.cs
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
namespace Server.Mobiles;
|
||||
|
||||
public class SummonLesserUndead : SummonUndead
|
||||
{
|
||||
private int _group;
|
||||
|
||||
public override void Trigger(BaseCreature source, Mobile target)
|
||||
{
|
||||
_group = Utility.Random(2);
|
||||
base.Trigger(source, target);
|
||||
}
|
||||
|
||||
public override BaseCreature CreateSummon(BaseCreature source)
|
||||
{
|
||||
return _group switch
|
||||
{
|
||||
0 => Utility.Random(4) switch
|
||||
{
|
||||
0 => new Skeleton(),
|
||||
1 => new SkeletalMage(),
|
||||
2 => new Mummy(),
|
||||
3 => new Zombie(),
|
||||
},
|
||||
_ => Utility.Random(4) switch
|
||||
{
|
||||
0 => new Ghoul(),
|
||||
1 => new Spectre(),
|
||||
2 => new Shade(),
|
||||
3 => new Bogle()
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
6
Projects/UOContent/Mobiles/Abilities/SummonSkeletons.cs
Normal file
6
Projects/UOContent/Mobiles/Abilities/SummonSkeletons.cs
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
namespace Server.Mobiles;
|
||||
|
||||
public class SummonSkeletons : SummonUndead
|
||||
{
|
||||
public override BaseCreature CreateSummon(BaseCreature source) => new Skeleton();
|
||||
}
|
||||
69
Projects/UOContent/Mobiles/Abilities/SummonUndead.cs
Normal file
69
Projects/UOContent/Mobiles/Abilities/SummonUndead.cs
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
using System;
|
||||
|
||||
namespace Server.Mobiles;
|
||||
|
||||
public abstract class SummonUndead : MonsterAbility
|
||||
{
|
||||
public override MonsterAbilityType AbilityType => MonsterAbilityType.SummonUndead;
|
||||
public override MonsterAbilityTrigger AbilityTrigger => MonsterAbilityTrigger.TakeDamage;
|
||||
|
||||
public override double ChanceToTrigger => 0.05;
|
||||
public override TimeSpan MinTriggerCooldown => TimeSpan.FromMinutes(5);
|
||||
public override TimeSpan MaxTriggerCooldown => TimeSpan.FromMinutes(5);
|
||||
|
||||
public virtual double ChanceToPolymorph => 0.25;
|
||||
public virtual TimeSpan PolymorphDuration => TimeSpan.FromMinutes(5);
|
||||
public virtual int GetTransformBodyValue() => Utility.RandomBool() ? 50 : 56;
|
||||
public virtual int AmountToSummon => 4;
|
||||
public virtual int SummonRange => 4;
|
||||
|
||||
public abstract BaseCreature CreateSummon(BaseCreature source);
|
||||
|
||||
public virtual bool CanTrigger(BaseCreature source) => source.Followers < AmountToSummon && CanTrigger(source);
|
||||
|
||||
public override void Trigger(BaseCreature source, Mobile target)
|
||||
{
|
||||
var amount = AmountToSummon - source.Followers;
|
||||
var distance = SummonRange;
|
||||
|
||||
int willTransform = !source.Paralyzed && ChanceToPolymorph > 0 && ChanceToPolymorph > Utility.RandomDouble()
|
||||
? Utility.Random(amount)
|
||||
: -1;
|
||||
|
||||
for (var i = 0; i < amount; i++)
|
||||
{
|
||||
var loc = Utility.GetValidLocationInLOS(source.Map, source, distance);
|
||||
BaseCreature summon;
|
||||
if (i == willTransform)
|
||||
{
|
||||
summon = source;
|
||||
source.BodyMod = GetTransformBodyValue();
|
||||
source.HueMod = 0;
|
||||
// Name is not changed
|
||||
Timer.DelayCall(PolymorphDuration, EndPolymorph, source);
|
||||
}
|
||||
else
|
||||
{
|
||||
summon = CreateSummon(source);
|
||||
summon.Team = source.Team;
|
||||
summon.Summoned = true;
|
||||
summon.SummonMaster = source;
|
||||
summon.FightMode = FightMode.Closest;
|
||||
summon.Combatant = target;
|
||||
}
|
||||
|
||||
summon.MoveToWorld(loc, source.Map);
|
||||
Effects.SendLocationEffect(summon.Location, summon.Map, 0x3728, 10);
|
||||
summon.PlaySound(0x48F);
|
||||
summon.PlaySound(summon.GetAttackSound());
|
||||
}
|
||||
|
||||
base.Trigger(source, target);
|
||||
}
|
||||
|
||||
private static void EndPolymorph(BaseCreature source)
|
||||
{
|
||||
source.BodyMod = 0;
|
||||
source.HueMod = -1;
|
||||
}
|
||||
}
|
||||
|
|
@ -46,12 +46,14 @@ namespace Server.Mobiles
|
|||
public override string CorpseName => "a hell cat corpse";
|
||||
public override string DefaultName => "a hell cat";
|
||||
|
||||
public override bool HasBreath => true; // fire breath enabled
|
||||
public override int Hides => 10;
|
||||
public override HideType HideType => HideType.Spined;
|
||||
public override FoodType FavoriteFood => FoodType.Meat;
|
||||
public override PackInstinct PackInstinct => PackInstinct.Feline;
|
||||
|
||||
private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath };
|
||||
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
|
||||
|
||||
public override void GenerateLoot()
|
||||
{
|
||||
AddLoot(LootPack.Meager);
|
||||
|
|
|
|||
|
|
@ -44,13 +44,14 @@ namespace Server.Mobiles
|
|||
|
||||
public override string CorpseName => "a hell cat corpse";
|
||||
public override string DefaultName => "a hell cat";
|
||||
|
||||
public override bool HasBreath => true; // fire breath enabled
|
||||
public override int Hides => 10;
|
||||
public override HideType HideType => HideType.Spined;
|
||||
public override FoodType FavoriteFood => FoodType.Meat;
|
||||
public override PackInstinct PackInstinct => PackInstinct.Feline;
|
||||
|
||||
private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath };
|
||||
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
|
||||
|
||||
public override void GenerateLoot()
|
||||
{
|
||||
AddLoot(LootPack.Average);
|
||||
|
|
|
|||
|
|
@ -44,11 +44,12 @@ namespace Server.Mobiles
|
|||
}
|
||||
|
||||
public override string CorpseName => "a fire steed corpse";
|
||||
|
||||
public override bool HasBreath => true; // fire breath enabled
|
||||
public override FoodType FavoriteFood => FoodType.Meat;
|
||||
public override PackInstinct PackInstinct => PackInstinct.Daemon | PackInstinct.Equine;
|
||||
|
||||
private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath };
|
||||
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
|
||||
|
||||
public override void GenerateLoot()
|
||||
{
|
||||
PackItem(new SulfurousAsh(Utility.RandomMinMax(151, 300)));
|
||||
|
|
|
|||
|
|
@ -13,10 +13,11 @@ namespace Server.Mobiles
|
|||
}
|
||||
|
||||
public override string CorpseName => "a hellsteed corpse";
|
||||
public override bool HasBreath => true;
|
||||
public override int BreathChaosDamage => 100;
|
||||
public override Poison PoisonImmune => Poison.Lethal;
|
||||
|
||||
private static MonsterAbility[] _abilities = { MonsterAbility.ChaosBreath };
|
||||
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
|
||||
|
||||
public static void SetStats(BaseCreature steed)
|
||||
{
|
||||
steed.SetStr(201, 210);
|
||||
|
|
|
|||
|
|
@ -1,280 +1,161 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Server.Engines.Plants;
|
||||
using Server.Items;
|
||||
|
||||
namespace Server.Mobiles
|
||||
namespace Server.Mobiles;
|
||||
|
||||
public class Hiryu : BaseMount
|
||||
{
|
||||
public class Hiryu : BaseMount
|
||||
[Constructible]
|
||||
public Hiryu() : base("a hiryu", 243, 0x3E94, AIType.AI_Melee)
|
||||
{
|
||||
private static readonly Dictionary<Mobile, ExpireTimer> m_Table = new();
|
||||
Hue = GetHue();
|
||||
|
||||
[Constructible]
|
||||
public Hiryu() : base("a hiryu", 243, 0x3E94, AIType.AI_Melee)
|
||||
SetStr(1201, 1410);
|
||||
SetDex(171, 270);
|
||||
SetInt(301, 325);
|
||||
|
||||
SetHits(901, 1100);
|
||||
SetMana(60);
|
||||
|
||||
SetDamage(20, 30);
|
||||
|
||||
SetDamageType(ResistanceType.Physical, 100);
|
||||
|
||||
SetResistance(ResistanceType.Physical, 55, 70);
|
||||
SetResistance(ResistanceType.Fire, 70, 90);
|
||||
SetResistance(ResistanceType.Cold, 15, 25);
|
||||
SetResistance(ResistanceType.Poison, 40, 50);
|
||||
SetResistance(ResistanceType.Energy, 40, 50);
|
||||
|
||||
SetSkill(SkillName.Anatomy, 75.1, 80.0);
|
||||
SetSkill(SkillName.MagicResist, 85.1, 100.0);
|
||||
SetSkill(SkillName.Tactics, 100.1, 110.0);
|
||||
SetSkill(SkillName.Wrestling, 100.1, 120.0);
|
||||
|
||||
Fame = 18000;
|
||||
Karma = -18000;
|
||||
|
||||
Tamable = true;
|
||||
ControlSlots = 4;
|
||||
MinTameSkill = 98.7;
|
||||
|
||||
if (Utility.RandomDouble() < .33)
|
||||
{
|
||||
Hue = GetHue();
|
||||
|
||||
SetStr(1201, 1410);
|
||||
SetDex(171, 270);
|
||||
SetInt(301, 325);
|
||||
|
||||
SetHits(901, 1100);
|
||||
SetMana(60);
|
||||
|
||||
SetDamage(20, 30);
|
||||
|
||||
SetDamageType(ResistanceType.Physical, 100);
|
||||
|
||||
SetResistance(ResistanceType.Physical, 55, 70);
|
||||
SetResistance(ResistanceType.Fire, 70, 90);
|
||||
SetResistance(ResistanceType.Cold, 15, 25);
|
||||
SetResistance(ResistanceType.Poison, 40, 50);
|
||||
SetResistance(ResistanceType.Energy, 40, 50);
|
||||
|
||||
SetSkill(SkillName.Anatomy, 75.1, 80.0);
|
||||
SetSkill(SkillName.MagicResist, 85.1, 100.0);
|
||||
SetSkill(SkillName.Tactics, 100.1, 110.0);
|
||||
SetSkill(SkillName.Wrestling, 100.1, 120.0);
|
||||
|
||||
Fame = 18000;
|
||||
Karma = -18000;
|
||||
|
||||
Tamable = true;
|
||||
ControlSlots = 4;
|
||||
MinTameSkill = 98.7;
|
||||
|
||||
if (Utility.RandomDouble() < .33)
|
||||
{
|
||||
PackItem(Seed.RandomBonsaiSeed());
|
||||
}
|
||||
|
||||
if (Core.ML && Utility.RandomDouble() < .33)
|
||||
{
|
||||
PackItem(Seed.RandomPeculiarSeed(3));
|
||||
}
|
||||
PackItem(Seed.RandomBonsaiSeed());
|
||||
}
|
||||
|
||||
public Hiryu(Serial serial)
|
||||
: base(serial)
|
||||
if (Core.ML && Utility.RandomDouble() < .33)
|
||||
{
|
||||
PackItem(Seed.RandomPeculiarSeed(3));
|
||||
}
|
||||
}
|
||||
|
||||
public Hiryu(Serial serial) : base(serial)
|
||||
{
|
||||
}
|
||||
|
||||
public override string CorpseName => "a hiryu corpse";
|
||||
public override double WeaponAbilityChance => 0.07; /* 1 in 15 chance of using per landed hit */
|
||||
|
||||
public override bool StatLossAfterTame => true;
|
||||
|
||||
public override int TreasureMapLevel => 5;
|
||||
public override int Meat => 16;
|
||||
public override int Hides => 60;
|
||||
public override FoodType FavoriteFood => FoodType.Meat;
|
||||
public override bool CanAngerOnTame => true;
|
||||
|
||||
private static MonsterAbility[] _abilities = { MonsterAbility.GraspingClaw };
|
||||
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
|
||||
|
||||
public override WeaponAbility GetWeaponAbility() => WeaponAbility.Dismount;
|
||||
|
||||
private static int GetHue()
|
||||
{
|
||||
return Utility.Random(1075) switch
|
||||
{
|
||||
1074 => 0x85C, // Strong Green 0.09%
|
||||
1073 => 0x490, // Strong Purple 0.09%
|
||||
>= 1071 => 0x030, // Green 0.19%
|
||||
>= 1069 => 0x037, // Strong Yellow 0.19%
|
||||
>= 1066 => 0x295, // Light Green 0.28%
|
||||
>= 1063 => 0x123, // Cyan 0.28%
|
||||
>= 1058 => 0x482, // Ice Blue 0.47%
|
||||
>= 1050 => 0x487, // Blue/Yellow 0.74%
|
||||
>= 1040 => CraftResources.GetHue(CraftResource.Gold), // Gold 0.93%
|
||||
>= 1030 => CraftResources.GetHue(CraftResource.Agapite), // Agapite 0.93%
|
||||
>= 1020 => 0x495, // Strong Cyan 0.93%
|
||||
>= 1010 => 0x48D, // Light Blue 0.93%
|
||||
>= 1000 => 0x47F, // Ice Green 0.93%
|
||||
_ => 0 // No Hue 93.02%
|
||||
} | 0x8000;
|
||||
}
|
||||
|
||||
public override int GetAngerSound() => 0x4FE;
|
||||
|
||||
public override int GetIdleSound() => 0x4FD;
|
||||
|
||||
public override int GetAttackSound() => 0x4FC;
|
||||
|
||||
public override int GetHurtSound() => 0x4FF;
|
||||
|
||||
public override int GetDeathSound() => 0x4FB;
|
||||
|
||||
public override void GenerateLoot()
|
||||
{
|
||||
AddLoot(LootPack.FilthyRich, 3);
|
||||
AddLoot(LootPack.Gems, 4);
|
||||
}
|
||||
|
||||
public override void Serialize(IGenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
writer.Write(2);
|
||||
}
|
||||
|
||||
public override void Deserialize(IGenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
var version = reader.ReadInt();
|
||||
|
||||
if (version <= 1)
|
||||
{
|
||||
Timer.StartTimer(() => Fix(version));
|
||||
}
|
||||
|
||||
public override string CorpseName => "a hiryu corpse";
|
||||
public override double WeaponAbilityChance => 0.07; /* 1 in 15 chance of using per landed hit */
|
||||
|
||||
public override bool StatLossAfterTame => true;
|
||||
|
||||
public override int TreasureMapLevel => 5;
|
||||
public override int Meat => 16;
|
||||
public override int Hides => 60;
|
||||
public override FoodType FavoriteFood => FoodType.Meat;
|
||||
public override bool CanAngerOnTame => true;
|
||||
|
||||
public override WeaponAbility GetWeaponAbility() => WeaponAbility.Dismount;
|
||||
|
||||
private static int GetHue()
|
||||
if (version < 2)
|
||||
{
|
||||
var rand = Utility.Random(1075);
|
||||
|
||||
if (rand <= 0)
|
||||
for (var i = 0; i < Skills.Length; ++i)
|
||||
{
|
||||
return 0x855C;
|
||||
}
|
||||
Skills[i].Cap = Math.Max(100.0, Skills[i].Cap * 0.9);
|
||||
|
||||
if (rand <= 1)
|
||||
{
|
||||
return 0x8490;
|
||||
}
|
||||
|
||||
if (rand <= 3)
|
||||
{
|
||||
return 0x8030;
|
||||
}
|
||||
|
||||
if (rand <= 5)
|
||||
{
|
||||
return 0x8037;
|
||||
}
|
||||
|
||||
if (rand <= 8)
|
||||
{
|
||||
return 0x8295;
|
||||
}
|
||||
|
||||
if (rand <= 11)
|
||||
{
|
||||
return 0x8123;
|
||||
}
|
||||
|
||||
if (rand <= 16)
|
||||
{
|
||||
return 0x8482;
|
||||
}
|
||||
|
||||
if (rand <= 24)
|
||||
{
|
||||
return 0x8487;
|
||||
}
|
||||
|
||||
if (rand <= 34)
|
||||
{
|
||||
return 0x8032;
|
||||
}
|
||||
|
||||
if (rand <= 44)
|
||||
{
|
||||
return 0x8899;
|
||||
}
|
||||
|
||||
if (rand <= 54)
|
||||
{
|
||||
return 0x8495;
|
||||
}
|
||||
|
||||
if (rand <= 64)
|
||||
{
|
||||
return 0x848D;
|
||||
}
|
||||
|
||||
if (rand <= 74)
|
||||
{
|
||||
return 0x847F;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public override int GetAngerSound() => 0x4FE;
|
||||
|
||||
public override int GetIdleSound() => 0x4FD;
|
||||
|
||||
public override int GetAttackSound() => 0x4FC;
|
||||
|
||||
public override int GetHurtSound() => 0x4FF;
|
||||
|
||||
public override int GetDeathSound() => 0x4FB;
|
||||
|
||||
public override void GenerateLoot()
|
||||
{
|
||||
AddLoot(LootPack.FilthyRich, 3);
|
||||
AddLoot(LootPack.Gems, 4);
|
||||
}
|
||||
|
||||
public override void OnGaveMeleeAttack(Mobile defender)
|
||||
{
|
||||
base.OnGaveMeleeAttack(defender);
|
||||
|
||||
if (Utility.RandomDouble() >= 0.1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Grasping Claw
|
||||
* Start cliloc: 1070836
|
||||
* Effect: Physical resistance -15% for 5 seconds
|
||||
* End cliloc: 1070838
|
||||
* Effect: Type: "3" - From: "0x57D4F5B" (player) - To: "0x0" - ItemId: "0x37B9" - ItemIdName: "glow" - FromLocation: "(1149 808, 32)" - ToLocation: "(1149 808, 32)" - Speed: "10" - Duration: "5" - FixedDirection: "True" - Explode: "False"
|
||||
*/
|
||||
|
||||
if (m_Table.TryGetValue(defender, out var timer))
|
||||
{
|
||||
timer.DoExpire();
|
||||
defender.SendLocalizedMessage(1070837); // The creature lands another blow in your weakened state.
|
||||
}
|
||||
else
|
||||
{
|
||||
defender.SendLocalizedMessage(
|
||||
1070836
|
||||
); // The blow from the creature's claws has made you more susceptible to physical attacks.
|
||||
}
|
||||
|
||||
var effect = -(defender.PhysicalResistance * 15 / 100);
|
||||
|
||||
var mod = new ResistanceMod(ResistanceType.Physical, effect);
|
||||
|
||||
defender.FixedEffect(0x37B9, 10, 5);
|
||||
defender.AddResistanceMod(mod);
|
||||
|
||||
timer = new ExpireTimer(defender, mod, TimeSpan.FromSeconds(5.0));
|
||||
timer.Start();
|
||||
m_Table[defender] = timer;
|
||||
}
|
||||
|
||||
public override void Serialize(IGenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
writer.Write(2);
|
||||
}
|
||||
|
||||
public override void Deserialize(IGenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
var version = reader.ReadInt();
|
||||
|
||||
if (version <= 1)
|
||||
{
|
||||
Timer.StartTimer(() => Fix(version));
|
||||
}
|
||||
|
||||
if (version < 2)
|
||||
{
|
||||
for (var i = 0; i < Skills.Length; ++i)
|
||||
if (Skills[i].Base > Skills[i].Cap)
|
||||
{
|
||||
Skills[i].Cap = Math.Max(100.0, Skills[i].Cap * 0.9);
|
||||
|
||||
if (Skills[i].Base > Skills[i].Cap)
|
||||
{
|
||||
Skills[i].Base = Skills[i].Cap;
|
||||
}
|
||||
Skills[i].Base = Skills[i].Cap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Fix(int version)
|
||||
private void Fix(int version)
|
||||
{
|
||||
switch (version)
|
||||
{
|
||||
switch (version)
|
||||
{
|
||||
case 1:
|
||||
case 1:
|
||||
{
|
||||
if (InternalItem != null)
|
||||
{
|
||||
if (InternalItem != null)
|
||||
{
|
||||
InternalItem.Hue = Hue;
|
||||
}
|
||||
|
||||
goto case 0;
|
||||
InternalItem.Hue = Hue;
|
||||
}
|
||||
case 0:
|
||||
{
|
||||
Hue = GetHue();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ExpireTimer : Timer
|
||||
{
|
||||
private readonly Mobile m_Mobile;
|
||||
private readonly ResistanceMod m_Mod;
|
||||
|
||||
public ExpireTimer(Mobile m, ResistanceMod mod, TimeSpan delay)
|
||||
: base(delay)
|
||||
{
|
||||
m_Mobile = m;
|
||||
m_Mod = mod;
|
||||
}
|
||||
|
||||
public void DoExpire()
|
||||
{
|
||||
m_Mobile.RemoveResistanceMod(m_Mod);
|
||||
Stop();
|
||||
m_Table.Remove(m_Mobile);
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
m_Mobile.SendLocalizedMessage(1070838); // Your resistance to physical attacks has returned.
|
||||
DoExpire();
|
||||
}
|
||||
goto case 0;
|
||||
}
|
||||
case 0:
|
||||
{
|
||||
Hue = GetHue();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,282 +1,176 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Server.Engines.Plants;
|
||||
using Server.Items;
|
||||
|
||||
namespace Server.Mobiles
|
||||
namespace Server.Mobiles;
|
||||
|
||||
public class LesserHiryu : BaseMount
|
||||
{
|
||||
public class LesserHiryu : BaseMount
|
||||
[Constructible]
|
||||
public LesserHiryu() : base("a lesser hiryu", 243, 0x3E94, AIType.AI_Melee)
|
||||
{
|
||||
private static readonly Dictionary<Mobile, ExpireTimer> m_Table = new();
|
||||
Hue = GetHue();
|
||||
|
||||
[Constructible]
|
||||
public LesserHiryu() : base("a lesser hiryu", 243, 0x3E94, AIType.AI_Melee)
|
||||
SetStr(301, 410);
|
||||
SetDex(171, 270);
|
||||
SetInt(301, 325);
|
||||
|
||||
SetHits(401, 600);
|
||||
SetMana(60);
|
||||
|
||||
SetDamage(18, 23);
|
||||
|
||||
SetDamageType(ResistanceType.Physical, 100);
|
||||
|
||||
SetResistance(ResistanceType.Physical, 45, 70);
|
||||
SetResistance(ResistanceType.Fire, 60, 80);
|
||||
SetResistance(ResistanceType.Cold, 5, 15);
|
||||
SetResistance(ResistanceType.Poison, 30, 40);
|
||||
SetResistance(ResistanceType.Energy, 30, 40);
|
||||
|
||||
SetSkill(SkillName.Anatomy, 75.1, 80.0);
|
||||
SetSkill(SkillName.MagicResist, 85.1, 100.0);
|
||||
SetSkill(SkillName.Tactics, 100.1, 110.0);
|
||||
SetSkill(SkillName.Wrestling, 100.1, 120.0);
|
||||
|
||||
Fame = 10000;
|
||||
Karma = -10000;
|
||||
|
||||
Tamable = true;
|
||||
ControlSlots = 3;
|
||||
MinTameSkill = 98.7;
|
||||
|
||||
if (Utility.RandomDouble() < .33)
|
||||
{
|
||||
Hue = GetHue();
|
||||
PackItem(Seed.RandomBonsaiSeed());
|
||||
}
|
||||
}
|
||||
|
||||
SetStr(301, 410);
|
||||
SetDex(171, 270);
|
||||
SetInt(301, 325);
|
||||
public LesserHiryu(Serial serial) : base(serial)
|
||||
{
|
||||
}
|
||||
|
||||
SetHits(401, 600);
|
||||
SetMana(60);
|
||||
public override string CorpseName => "a hiryu corpse";
|
||||
public override double WeaponAbilityChance => 0.07; /* 1 in 15 chance of using; 1 in 5 chance of success */
|
||||
|
||||
SetDamage(18, 23);
|
||||
public override bool StatLossAfterTame => true;
|
||||
|
||||
SetDamageType(ResistanceType.Physical, 100);
|
||||
public override int TreasureMapLevel => 3;
|
||||
public override int Meat => 16;
|
||||
public override int Hides => 60;
|
||||
public override FoodType FavoriteFood => FoodType.Meat;
|
||||
public override bool CanAngerOnTame => true;
|
||||
|
||||
SetResistance(ResistanceType.Physical, 45, 70);
|
||||
SetResistance(ResistanceType.Fire, 60, 80);
|
||||
SetResistance(ResistanceType.Cold, 5, 15);
|
||||
SetResistance(ResistanceType.Poison, 30, 40);
|
||||
SetResistance(ResistanceType.Energy, 30, 40);
|
||||
public override WeaponAbility GetWeaponAbility() => WeaponAbility.Dismount;
|
||||
|
||||
SetSkill(SkillName.Anatomy, 75.1, 80.0);
|
||||
SetSkill(SkillName.MagicResist, 85.1, 100.0);
|
||||
SetSkill(SkillName.Tactics, 100.1, 110.0);
|
||||
SetSkill(SkillName.Wrestling, 100.1, 120.0);
|
||||
private static MonsterAbility[] _abilities = { MonsterAbility.GraspingClaw };
|
||||
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
|
||||
|
||||
Fame = 10000;
|
||||
Karma = -10000;
|
||||
private static int GetHue()
|
||||
{
|
||||
return Utility.Random(525) switch
|
||||
{
|
||||
524 => 0x258, // Midnight Blue 0.19%
|
||||
523 => CraftResources.GetHue(CraftResource.Valorite), // Valorite 0.19%
|
||||
>= 520 => 0x7D4, // Dark Green 0.57%
|
||||
>= 510 => 0x163, // Green 1.90%
|
||||
>= 500 => 0x295, // Green 1.90%
|
||||
_ => 0 // No Hue 95.24%
|
||||
} | 0x8000;
|
||||
}
|
||||
|
||||
Tamable = true;
|
||||
ControlSlots = 3;
|
||||
MinTameSkill = 98.7;
|
||||
public override bool OverrideBondingReqs() => ControlMaster.Skills.Bushido.Base >= 90.0;
|
||||
|
||||
if (Utility.RandomDouble() < .33)
|
||||
{
|
||||
PackItem(Seed.RandomBonsaiSeed());
|
||||
}
|
||||
public override int GetAngerSound() => 0x4FE;
|
||||
|
||||
public override int GetIdleSound() => 0x4FD;
|
||||
|
||||
public override int GetAttackSound() => 0x4FC;
|
||||
|
||||
public override int GetHurtSound() => 0x4FF;
|
||||
|
||||
public override int GetDeathSound() => 0x4FB;
|
||||
|
||||
public override void GenerateLoot()
|
||||
{
|
||||
AddLoot(LootPack.FilthyRich, 2);
|
||||
AddLoot(LootPack.Gems, 4);
|
||||
}
|
||||
|
||||
public override double GetControlChance(Mobile m, bool useBaseSkill = false)
|
||||
{
|
||||
var tamingChance = base.GetControlChance(m, useBaseSkill);
|
||||
|
||||
if (tamingChance >= 0.95)
|
||||
{
|
||||
return tamingChance;
|
||||
}
|
||||
|
||||
public LesserHiryu(Serial serial)
|
||||
: base(serial)
|
||||
var skill = useBaseSkill ? m.Skills.Bushido.Base : m.Skills.Bushido.Value;
|
||||
|
||||
if (skill < 90.0)
|
||||
{
|
||||
return tamingChance;
|
||||
}
|
||||
|
||||
public override string CorpseName => "a hiryu corpse";
|
||||
public override double WeaponAbilityChance => 0.07; /* 1 in 15 chance of using; 1 in 5 chance of success */
|
||||
var bushidoChance = (skill - 30.0) / 100;
|
||||
|
||||
public override bool StatLossAfterTame => true;
|
||||
|
||||
public override int TreasureMapLevel => 3;
|
||||
public override int Meat => 16;
|
||||
public override int Hides => 60;
|
||||
public override FoodType FavoriteFood => FoodType.Meat;
|
||||
public override bool CanAngerOnTame => true;
|
||||
|
||||
public override WeaponAbility GetWeaponAbility() => WeaponAbility.Dismount;
|
||||
|
||||
private static int GetHue()
|
||||
if (m.Skills.Bushido.Base >= 120)
|
||||
{
|
||||
var rand = Utility.Random(527);
|
||||
|
||||
/*
|
||||
|
||||
500 527 No Hue Color 94.88% 0
|
||||
10 527 Green 1.90% 0x8295
|
||||
10 527 Green 1.90% 0x8163 (Very Close to Above Green) //this one is an approximation
|
||||
5 527 Dark Green 0.95% 0x87D4
|
||||
1 527 Valorite 0.19% 0x88AB
|
||||
1 527 Midnight Blue 0.19% 0x8258
|
||||
|
||||
* */
|
||||
|
||||
if (rand <= 0)
|
||||
{
|
||||
return 0x8258;
|
||||
}
|
||||
|
||||
if (rand <= 1)
|
||||
{
|
||||
return 0x88AB;
|
||||
}
|
||||
|
||||
if (rand <= 6)
|
||||
{
|
||||
return 0x87D4;
|
||||
}
|
||||
|
||||
if (rand <= 16)
|
||||
{
|
||||
return 0x8163;
|
||||
}
|
||||
|
||||
if (rand <= 26)
|
||||
{
|
||||
return 0x8295;
|
||||
}
|
||||
|
||||
return 0;
|
||||
bushidoChance += 0.05;
|
||||
}
|
||||
|
||||
public override bool OverrideBondingReqs()
|
||||
{
|
||||
if (ControlMaster.Skills.Bushido.Base >= 90.0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return bushidoChance > tamingChance ? bushidoChance : tamingChance;
|
||||
}
|
||||
|
||||
return false;
|
||||
public override void Serialize(IGenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
writer.Write(2);
|
||||
}
|
||||
|
||||
public override void Deserialize(IGenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
var version = reader.ReadInt();
|
||||
|
||||
if (version <= 1)
|
||||
{
|
||||
Timer.StartTimer(() => Fix(version));
|
||||
}
|
||||
|
||||
public override int GetAngerSound() => 0x4FE;
|
||||
|
||||
public override int GetIdleSound() => 0x4FD;
|
||||
|
||||
public override int GetAttackSound() => 0x4FC;
|
||||
|
||||
public override int GetHurtSound() => 0x4FF;
|
||||
|
||||
public override int GetDeathSound() => 0x4FB;
|
||||
|
||||
public override void GenerateLoot()
|
||||
if (version < 2)
|
||||
{
|
||||
AddLoot(LootPack.FilthyRich, 2);
|
||||
AddLoot(LootPack.Gems, 4);
|
||||
}
|
||||
|
||||
public override double GetControlChance(Mobile m, bool useBaseSkill = false)
|
||||
{
|
||||
var tamingChance = base.GetControlChance(m, useBaseSkill);
|
||||
|
||||
if (tamingChance >= 0.95)
|
||||
for (var i = 0; i < Skills.Length; ++i)
|
||||
{
|
||||
return tamingChance;
|
||||
}
|
||||
Skills[i].Cap = Math.Max(100.0, Skills[i].Cap * 0.9);
|
||||
|
||||
var skill = useBaseSkill ? m.Skills.Bushido.Base : m.Skills.Bushido.Value;
|
||||
|
||||
if (skill < 90.0)
|
||||
{
|
||||
return tamingChance;
|
||||
}
|
||||
|
||||
var bushidoChance = (skill - 30.0) / 100;
|
||||
|
||||
if (m.Skills.Bushido.Base >= 120)
|
||||
{
|
||||
bushidoChance += 0.05;
|
||||
}
|
||||
|
||||
return bushidoChance > tamingChance ? bushidoChance : tamingChance;
|
||||
}
|
||||
|
||||
public override void OnGaveMeleeAttack(Mobile defender)
|
||||
{
|
||||
base.OnGaveMeleeAttack(defender);
|
||||
|
||||
if (Utility.RandomDouble() >= 0.1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Grasping Claw
|
||||
* Start cliloc: 1070836
|
||||
* Effect: Physical resistance -15% for 5 seconds
|
||||
* End cliloc: 1070838
|
||||
* Effect: Type: "3" - From: "0x57D4F5B" (player) - To: "0x0" - ItemId: "0x37B9" - ItemIdName: "glow" - FromLocation: "(1149 808, 32)" - ToLocation: "(1149 808, 32)" - Speed: "10" - Duration: "5" - FixedDirection: "True" - Explode: "False"
|
||||
*/
|
||||
|
||||
if (m_Table.TryGetValue(defender, out var timer))
|
||||
{
|
||||
timer.DoExpire();
|
||||
defender.SendLocalizedMessage(1070837); // The creature lands another blow in your weakened state.
|
||||
}
|
||||
else
|
||||
{
|
||||
defender.SendLocalizedMessage(
|
||||
1070836
|
||||
); // The blow from the creature's claws has made you more susceptible to physical attacks.
|
||||
}
|
||||
|
||||
var effect = -(defender.PhysicalResistance * 15 / 100);
|
||||
|
||||
var mod = new ResistanceMod(ResistanceType.Physical, effect);
|
||||
|
||||
defender.FixedEffect(0x37B9, 10, 5);
|
||||
defender.AddResistanceMod(mod);
|
||||
|
||||
timer = new ExpireTimer(defender, mod, TimeSpan.FromSeconds(5.0));
|
||||
timer.Start();
|
||||
m_Table[defender] = timer;
|
||||
}
|
||||
|
||||
public override void Serialize(IGenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
writer.Write(2);
|
||||
}
|
||||
|
||||
public override void Deserialize(IGenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
var version = reader.ReadInt();
|
||||
|
||||
if (version <= 1)
|
||||
{
|
||||
Timer.StartTimer(() => Fix(version));
|
||||
}
|
||||
|
||||
if (version < 2)
|
||||
{
|
||||
for (var i = 0; i < Skills.Length; ++i)
|
||||
if (Skills[i].Base > Skills[i].Cap)
|
||||
{
|
||||
Skills[i].Cap = Math.Max(100.0, Skills[i].Cap * 0.9);
|
||||
|
||||
if (Skills[i].Base > Skills[i].Cap)
|
||||
{
|
||||
Skills[i].Base = Skills[i].Cap;
|
||||
}
|
||||
Skills[i].Base = Skills[i].Cap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Fix(int version)
|
||||
private void Fix(int version)
|
||||
{
|
||||
switch (version)
|
||||
{
|
||||
switch (version)
|
||||
{
|
||||
case 1:
|
||||
case 1:
|
||||
{
|
||||
if (InternalItem != null)
|
||||
{
|
||||
if (InternalItem != null)
|
||||
{
|
||||
InternalItem.Hue = Hue;
|
||||
}
|
||||
|
||||
goto case 0;
|
||||
InternalItem.Hue = Hue;
|
||||
}
|
||||
case 0:
|
||||
{
|
||||
Hue = GetHue();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ExpireTimer : Timer
|
||||
{
|
||||
private readonly Mobile m_Mobile;
|
||||
private readonly ResistanceMod m_Mod;
|
||||
|
||||
public ExpireTimer(Mobile m, ResistanceMod mod, TimeSpan delay)
|
||||
: base(delay)
|
||||
{
|
||||
m_Mobile = m;
|
||||
m_Mod = mod;
|
||||
}
|
||||
|
||||
public void DoExpire()
|
||||
{
|
||||
m_Mobile.RemoveResistanceMod(m_Mod);
|
||||
Stop();
|
||||
m_Table.Remove(m_Mobile);
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
m_Mobile.SendLocalizedMessage(1070838); // Your resistance to physical attacks has returned.
|
||||
DoExpire();
|
||||
}
|
||||
goto case 0;
|
||||
}
|
||||
case 0:
|
||||
{
|
||||
Hue = GetHue();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,14 +95,15 @@ namespace Server.Mobiles
|
|||
}
|
||||
|
||||
public override string CorpseName => "a nightmare corpse";
|
||||
|
||||
public override bool HasBreath => true; // fire breath enabled
|
||||
public override int Meat => 5;
|
||||
public override int Hides => 10;
|
||||
public override HideType HideType => HideType.Barbed;
|
||||
public override FoodType FavoriteFood => FoodType.Meat;
|
||||
public override bool CanAngerOnTame => true;
|
||||
|
||||
private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath };
|
||||
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
|
||||
|
||||
public override void GenerateLoot()
|
||||
{
|
||||
AddLoot(LootPack.Rich);
|
||||
|
|
|
|||
|
|
@ -50,11 +50,12 @@ namespace Server.Mobiles
|
|||
|
||||
public override string CorpseName => "a lava lizard corpse";
|
||||
public override string DefaultName => "a lava lizard";
|
||||
|
||||
public override bool HasBreath => true; // fire breath enabled
|
||||
public override int Hides => 12;
|
||||
public override HideType HideType => HideType.Spined;
|
||||
|
||||
private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath };
|
||||
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
|
||||
|
||||
public override void GenerateLoot()
|
||||
{
|
||||
AddLoot(LootPack.Meager);
|
||||
|
|
|
|||
|
|
@ -50,12 +50,13 @@ namespace Server.Mobiles
|
|||
public override string DefaultName => "a lava serpent";
|
||||
|
||||
public override bool DeathAdderCharmable => true;
|
||||
|
||||
public override bool HasBreath => true; // fire breath enabled
|
||||
public override int Meat => 4;
|
||||
public override int Hides => 15;
|
||||
public override HideType HideType => HideType.Spined;
|
||||
|
||||
private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath };
|
||||
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
|
||||
|
||||
public override void GenerateLoot()
|
||||
{
|
||||
AddLoot(LootPack.Average);
|
||||
|
|
|
|||
|
|
@ -48,10 +48,11 @@ namespace Server.Mobiles
|
|||
public override string DefaultName => "a lava snake";
|
||||
|
||||
public override bool DeathAdderCharmable => true;
|
||||
|
||||
public override bool HasBreath => true; // fire breath enabled
|
||||
public override int Meat => 1;
|
||||
|
||||
private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath };
|
||||
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
|
||||
|
||||
public override void GenerateLoot()
|
||||
{
|
||||
AddLoot(LootPack.Poor);
|
||||
|
|
|
|||
|
|
@ -292,7 +292,6 @@ namespace Server.Mobiles
|
|||
|
||||
private long m_NextAura;
|
||||
|
||||
private long m_NextBreathTime;
|
||||
private long m_NextHealOwnerTime = Core.TickCount;
|
||||
|
||||
private long m_NextHealTime = Core.TickCount;
|
||||
|
|
@ -866,7 +865,6 @@ namespace Server.Mobiles
|
|||
|
||||
public static bool Summoning { get; set; }
|
||||
|
||||
public virtual bool CanBreath => HasBreath && !Summoned;
|
||||
public virtual bool IsDispellable => Summoned && !IsAnimatedDead;
|
||||
|
||||
// If they are following a waypoint, they'll continue to follow it even if players aren't around
|
||||
|
|
@ -1033,53 +1031,9 @@ namespace Server.Mobiles
|
|||
[CommandProperty(AccessLevel.GameMaster)]
|
||||
public int DirectDamage { get; set; }
|
||||
|
||||
// Must be overridden in subclass to enable
|
||||
public virtual bool HasBreath => false;
|
||||
|
||||
// Base damage given is: CurrentHitPoints * BreathDamageScalar
|
||||
public virtual double BreathDamageScalar => Core.AOS ? 0.16 : 0.05;
|
||||
|
||||
// Min/max seconds until next breath
|
||||
public virtual double BreathMinDelay => 30.0;
|
||||
public virtual double BreathMaxDelay => 45.0;
|
||||
|
||||
// Creature stops moving for 1.0 seconds while breathing
|
||||
public virtual double BreathStallTime => 1.0;
|
||||
|
||||
// Effect is sent 1.3 seconds after BreathAngerSound and BreathAngerAnimation is played
|
||||
public virtual double BreathEffectDelay => 1.3;
|
||||
|
||||
// Damage is given 1.0 seconds after effect is sent
|
||||
public virtual double BreathDamageDelay => 1.0;
|
||||
|
||||
public virtual int BreathRange => RangePerception;
|
||||
|
||||
// Damage types
|
||||
public virtual int BreathChaosDamage => 0;
|
||||
public virtual int BreathPhysicalDamage => 0;
|
||||
public virtual int BreathFireDamage => 100;
|
||||
public virtual int BreathColdDamage => 0;
|
||||
public virtual int BreathPoisonDamage => 0;
|
||||
public virtual int BreathEnergyDamage => 0;
|
||||
|
||||
// Is immune to breath damages
|
||||
public virtual bool BreathImmune => false;
|
||||
|
||||
// Effect details and sound
|
||||
public virtual int BreathEffectItemID => 0x36D4;
|
||||
public virtual int BreathEffectSpeed => 5;
|
||||
public virtual int BreathEffectDuration => 0;
|
||||
public virtual bool BreathEffectExplodes => false;
|
||||
public virtual bool BreathEffectFixedDir => false;
|
||||
public virtual int BreathEffectHue => 0;
|
||||
public virtual int BreathEffectRenderMode => 0;
|
||||
|
||||
public virtual int BreathEffectSound => 0x227;
|
||||
|
||||
// Anger sound/animations
|
||||
public virtual int BreathAngerSound => GetAngerSound();
|
||||
public virtual int BreathAngerAnimation => 12;
|
||||
|
||||
public virtual bool CanFlee => !m_Paragon;
|
||||
|
||||
public DateTime EndFleeTime { get; set; }
|
||||
|
|
@ -1162,6 +1116,67 @@ namespace Server.Mobiles
|
|||
}
|
||||
}
|
||||
|
||||
public virtual MonsterAbility[] GetMonsterAbilities() => null;
|
||||
|
||||
public virtual bool HasAbility(MonsterAbilityType type)
|
||||
{
|
||||
var abilities = GetMonsterAbilities();
|
||||
|
||||
if (abilities == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < abilities.Length; i++)
|
||||
{
|
||||
if (abilities[i].AbilityType == type)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual bool HasAbility(MonsterAbility ability)
|
||||
{
|
||||
var abilities = GetMonsterAbilities();
|
||||
|
||||
if (abilities == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < abilities.Length; i++)
|
||||
{
|
||||
if (abilities[i] == ability)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual void TriggerAbility(MonsterAbilityTrigger trigger, Mobile defender)
|
||||
{
|
||||
var abilities = GetMonsterAbilities();
|
||||
|
||||
if (abilities == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < abilities.Length; i++)
|
||||
{
|
||||
var ability = abilities[i];
|
||||
if (ability.WillTrigger(trigger) && ability.CanTrigger(this))
|
||||
{
|
||||
ability.Trigger(this, defender);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual WeaponAbility GetWeaponAbility() => null;
|
||||
|
||||
public virtual bool IsEnemy(Mobile m)
|
||||
|
|
@ -1478,12 +1493,19 @@ namespace Server.Mobiles
|
|||
base.OnDamage(amount, from, willKill);
|
||||
}
|
||||
|
||||
public virtual void OnDamagedBySpell(Mobile from)
|
||||
public virtual void OnDamagedBySpell(Mobile from, int damage)
|
||||
{
|
||||
if (CanBeDistracted && ControlOrder == OrderType.Follow)
|
||||
{
|
||||
CheckDistracted(from);
|
||||
}
|
||||
|
||||
TriggerAbility(MonsterAbilityTrigger.TakeSpellDamage, from);
|
||||
}
|
||||
|
||||
public virtual void OnDamageSpell(Mobile defender, int damage)
|
||||
{
|
||||
TriggerAbility(MonsterAbilityTrigger.GiveSpellDamage, defender);
|
||||
}
|
||||
|
||||
public virtual void OnHarmfulSpell(Mobile from)
|
||||
|
|
@ -2199,13 +2221,15 @@ namespace Server.Mobiles
|
|||
}
|
||||
}
|
||||
|
||||
public virtual void OnGotMeleeAttack(Mobile attacker)
|
||||
public virtual void OnGotMeleeAttack(Mobile attacker, int damage)
|
||||
{
|
||||
if (AutoDispel && attacker is BaseCreature creature && creature.IsDispellable &&
|
||||
AutoDispelChance > Utility.RandomDouble())
|
||||
{
|
||||
Dispel(creature);
|
||||
}
|
||||
|
||||
TriggerAbility(MonsterAbilityTrigger.TakeMeleeDamage, attacker);
|
||||
}
|
||||
|
||||
public virtual void Dispel(Mobile m)
|
||||
|
|
@ -2222,7 +2246,7 @@ namespace Server.Mobiles
|
|||
m.Delete();
|
||||
}
|
||||
|
||||
public virtual void OnGaveMeleeAttack(Mobile defender)
|
||||
public virtual void OnGaveMeleeAttack(Mobile defender, int damage)
|
||||
{
|
||||
var p = m_Paragon ? PoisonImpl.IncreaseLevel(HitPoison) : HitPoison;
|
||||
|
||||
|
|
@ -2241,6 +2265,8 @@ namespace Server.Mobiles
|
|||
{
|
||||
Dispel(creature);
|
||||
}
|
||||
|
||||
TriggerAbility(MonsterAbilityTrigger.GiveMeleeDamage, defender);
|
||||
}
|
||||
|
||||
public override void OnAfterDelete()
|
||||
|
|
@ -3488,24 +3514,8 @@ namespace Server.Mobiles
|
|||
m_NextRummageTime = tc + (int)TimeSpan.FromMinutes(delay).TotalMilliseconds;
|
||||
}
|
||||
|
||||
// tested: controlled dragons do breath fire, what about summoned skeletal dragons?
|
||||
if (CanBreath && tc - m_NextBreathTime >= 0)
|
||||
{
|
||||
var target = Combatant;
|
||||
|
||||
if (target?.Alive == true && !target.IsDeadBondedPet && CanBeHarmful(target) && target.Map == Map &&
|
||||
!IsDeadBondedPet && target.InRange(this, BreathRange) && InLOS(target) && !BardPacified)
|
||||
{
|
||||
if (Core.TickCount - m_NextBreathTime < 30000 && Utility.RandomBool())
|
||||
{
|
||||
BreathStart(target);
|
||||
}
|
||||
|
||||
m_NextBreathTime = tc + (int)TimeSpan
|
||||
.FromSeconds(BreathMinDelay + Utility.RandomDouble() * (BreathMaxDelay - BreathMinDelay))
|
||||
.TotalMilliseconds;
|
||||
}
|
||||
}
|
||||
// Fire breath, etc.
|
||||
TriggerAbility(MonsterAbilityTrigger.Action, Combatant);
|
||||
|
||||
if ((CanHeal || CanHealOwner) && Alive && !IsHealing && !BardPacified)
|
||||
{
|
||||
|
|
@ -3893,161 +3903,6 @@ namespace Server.Mobiles
|
|||
}
|
||||
}
|
||||
|
||||
public virtual void BreathStart(Mobile target)
|
||||
{
|
||||
BreathStallMovement();
|
||||
BreathPlayAngerSound();
|
||||
BreathPlayAngerAnimation();
|
||||
|
||||
Direction = GetDirectionTo(target);
|
||||
|
||||
Timer.StartTimer(TimeSpan.FromSeconds(BreathEffectDelay), () => BreathEffect_Callback(target));
|
||||
}
|
||||
|
||||
public virtual void BreathStallMovement()
|
||||
{
|
||||
if (AIObject != null)
|
||||
{
|
||||
AIObject.NextMove = Core.TickCount + (int)(BreathStallTime * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void BreathPlayAngerSound()
|
||||
{
|
||||
PlaySound(BreathAngerSound);
|
||||
}
|
||||
|
||||
public virtual void BreathPlayAngerAnimation()
|
||||
{
|
||||
Animate(BreathAngerAnimation, 5, 1, true, false, 0);
|
||||
}
|
||||
|
||||
public virtual void BreathEffect_Callback(Mobile target)
|
||||
{
|
||||
if (!target.Alive || !CanBeHarmful(target))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BreathPlayEffectSound();
|
||||
BreathPlayEffect(target);
|
||||
|
||||
Timer.StartTimer(TimeSpan.FromSeconds(BreathDamageDelay), () => BreathDamage_Callback(target));
|
||||
}
|
||||
|
||||
public virtual void BreathPlayEffectSound()
|
||||
{
|
||||
PlaySound(BreathEffectSound);
|
||||
}
|
||||
|
||||
public virtual void BreathPlayEffect(Mobile target)
|
||||
{
|
||||
Effects.SendMovingEffect(
|
||||
this,
|
||||
target,
|
||||
BreathEffectItemID,
|
||||
BreathEffectSpeed,
|
||||
BreathEffectDuration,
|
||||
BreathEffectFixedDir,
|
||||
BreathEffectExplodes,
|
||||
BreathEffectHue,
|
||||
BreathEffectRenderMode
|
||||
);
|
||||
}
|
||||
|
||||
public virtual void BreathDamage_Callback(Mobile target)
|
||||
{
|
||||
if (target is BaseCreature creature && creature.BreathImmune)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (CanBeHarmful(target))
|
||||
{
|
||||
DoHarmful(target);
|
||||
BreathDealDamage(target);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void BreathDealDamage(Mobile target)
|
||||
{
|
||||
if (!Evasion.CheckSpellEvasion(target))
|
||||
{
|
||||
var physDamage = BreathPhysicalDamage;
|
||||
var fireDamage = BreathFireDamage;
|
||||
var coldDamage = BreathColdDamage;
|
||||
var poisDamage = BreathPoisonDamage;
|
||||
var nrgyDamage = BreathEnergyDamage;
|
||||
|
||||
if (BreathChaosDamage > 0)
|
||||
{
|
||||
switch (Utility.Random(5))
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
physDamage += BreathChaosDamage;
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
fireDamage += BreathChaosDamage;
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
coldDamage += BreathChaosDamage;
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
poisDamage += BreathChaosDamage;
|
||||
break;
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
nrgyDamage += BreathChaosDamage;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (physDamage == 0 && fireDamage == 0 && coldDamage == 0 && poisDamage == 0 && nrgyDamage == 0)
|
||||
{
|
||||
target.Damage(BreathComputeDamage(), this); // Unresistable damage even in AOS
|
||||
}
|
||||
else
|
||||
{
|
||||
AOS.Damage(
|
||||
target,
|
||||
this,
|
||||
BreathComputeDamage(),
|
||||
physDamage,
|
||||
fireDamage,
|
||||
coldDamage,
|
||||
poisDamage,
|
||||
nrgyDamage
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual int BreathComputeDamage()
|
||||
{
|
||||
var damage = (int)(Hits * BreathDamageScalar);
|
||||
|
||||
if (IsParagon)
|
||||
{
|
||||
damage = (int)(damage / Paragon.HitsBuff);
|
||||
}
|
||||
|
||||
if (damage > 200)
|
||||
{
|
||||
damage = 200;
|
||||
}
|
||||
|
||||
return damage;
|
||||
}
|
||||
|
||||
public void SpillAcid(int amount)
|
||||
{
|
||||
SpillAcid(null, amount);
|
||||
|
|
|
|||
|
|
@ -73,6 +73,9 @@ namespace Server.Mobiles
|
|||
AddLoot(LootPack.MedScrolls, 2);
|
||||
}
|
||||
|
||||
private static MonsterAbility[] _abilities = { MonsterAbility.SummonLesserUndead };
|
||||
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
|
||||
|
||||
public override void Serialize(IGenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
|
|
|||
|
|
@ -106,9 +106,9 @@ namespace Server.Mobiles
|
|||
AddLoot(LootPack.Gems, 1);
|
||||
}
|
||||
|
||||
public override void OnGaveMeleeAttack(Mobile defender)
|
||||
public override void OnGaveMeleeAttack(Mobile defender, int damage)
|
||||
{
|
||||
base.OnGaveMeleeAttack(defender);
|
||||
base.OnGaveMeleeAttack(defender, damage);
|
||||
|
||||
if (!m_Stunning && Utility.RandomDouble() < 0.3)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -45,11 +45,13 @@ namespace Server.Mobiles
|
|||
|
||||
public override string CorpseName => "a charred corpse";
|
||||
|
||||
public override bool HasBreath => true; // fire breath enabled
|
||||
public override int TreasureMapLevel => 1;
|
||||
public override int Meat => 1;
|
||||
public override bool CanFly => true;
|
||||
|
||||
private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath };
|
||||
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
|
||||
|
||||
public override void GenerateLoot()
|
||||
{
|
||||
AddLoot(LootPack.Rich);
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ namespace Server.Mobiles
|
|||
AddLoot(LootPack.Gems, 2);
|
||||
}
|
||||
|
||||
public override void OnDamagedBySpell(Mobile from)
|
||||
public override void OnDamagedBySpell(Mobile from, int damage)
|
||||
{
|
||||
if (from?.Alive == true && Utility.RandomDouble() < 0.4)
|
||||
{
|
||||
|
|
@ -73,9 +73,9 @@ namespace Server.Mobiles
|
|||
}
|
||||
}
|
||||
|
||||
public override void OnGotMeleeAttack(Mobile attacker)
|
||||
public override void OnGotMeleeAttack(Mobile attacker, int damage)
|
||||
{
|
||||
base.OnGotMeleeAttack(attacker);
|
||||
base.OnGotMeleeAttack(attacker, damage);
|
||||
|
||||
if (attacker?.Alive == true && attacker.Weapon is BaseRanged && Utility.RandomDouble() < 0.4)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -59,6 +59,9 @@ namespace Server.Mobiles
|
|||
public override Poison PoisonImmune => Poison.Lethal;
|
||||
public override int TreasureMapLevel => 4;
|
||||
|
||||
private static MonsterAbility[] _abilities = { MonsterAbility.SummonSkeletons };
|
||||
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
|
||||
|
||||
public override void GenerateLoot()
|
||||
{
|
||||
AddLoot(LootPack.FilthyRich);
|
||||
|
|
|
|||
|
|
@ -116,9 +116,9 @@ namespace Server.Mobiles
|
|||
}
|
||||
}
|
||||
|
||||
public override void OnGotMeleeAttack(Mobile attacker)
|
||||
public override void OnGotMeleeAttack(Mobile attacker, int damage)
|
||||
{
|
||||
base.OnGotMeleeAttack(attacker);
|
||||
base.OnGotMeleeAttack(attacker, damage);
|
||||
|
||||
if (Utility.RandomDouble() < 0.1)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -83,9 +83,9 @@ namespace Server.Mobiles
|
|||
eable.Free();
|
||||
}
|
||||
|
||||
public override void OnGaveMeleeAttack(Mobile defender)
|
||||
public override void OnGaveMeleeAttack(Mobile defender, int damage)
|
||||
{
|
||||
base.OnGaveMeleeAttack(defender);
|
||||
base.OnGaveMeleeAttack(defender, damage);
|
||||
|
||||
if (Utility.RandomDouble() <= 0.1)
|
||||
{
|
||||
|
|
@ -93,9 +93,9 @@ namespace Server.Mobiles
|
|||
}
|
||||
}
|
||||
|
||||
public override void OnGotMeleeAttack(Mobile attacker)
|
||||
public override void OnGotMeleeAttack(Mobile attacker, int damage)
|
||||
{
|
||||
base.OnGotMeleeAttack(attacker);
|
||||
base.OnGotMeleeAttack(attacker, damage);
|
||||
|
||||
if (Utility.RandomDouble() <= 0.1)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -103,9 +103,9 @@ namespace Server.Mobiles
|
|||
|
||||
public override int GetHurtSound() => 0x140;
|
||||
|
||||
public override void OnGaveMeleeAttack(Mobile defender)
|
||||
public override void OnGaveMeleeAttack(Mobile defender, int damage)
|
||||
{
|
||||
base.OnGaveMeleeAttack(defender);
|
||||
base.OnGaveMeleeAttack(defender, damage);
|
||||
|
||||
if (!m_Stunning && Utility.RandomDouble() < 0.3)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ namespace Server.Mobiles
|
|||
}
|
||||
}
|
||||
|
||||
public override void OnDamagedBySpell(Mobile caster)
|
||||
public override void OnDamagedBySpell(Mobile caster, int damage)
|
||||
{
|
||||
if (caster == this)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -38,20 +38,30 @@ namespace Server.Mobiles
|
|||
switch (Utility.Random(5))
|
||||
{
|
||||
case 0:
|
||||
PackItem(new BoneArms());
|
||||
break;
|
||||
{
|
||||
PackItem(new BoneArms());
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
PackItem(new BoneChest());
|
||||
break;
|
||||
{
|
||||
PackItem(new BoneChest());
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
PackItem(new BoneGloves());
|
||||
break;
|
||||
{
|
||||
PackItem(new BoneGloves());
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
PackItem(new BoneLegs());
|
||||
break;
|
||||
{
|
||||
PackItem(new BoneLegs());
|
||||
break;
|
||||
}
|
||||
case 4:
|
||||
PackItem(new BoneHelm());
|
||||
break;
|
||||
{
|
||||
PackItem(new BoneHelm());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ namespace Server.Mobiles
|
|||
}
|
||||
}
|
||||
|
||||
public override void OnDamagedBySpell(Mobile from)
|
||||
public override void OnDamagedBySpell(Mobile from, int damage)
|
||||
{
|
||||
if (from?.Alive == true && Utility.RandomDouble() < 0.4)
|
||||
{
|
||||
|
|
@ -123,9 +123,9 @@ namespace Server.Mobiles
|
|||
}
|
||||
}
|
||||
|
||||
public override void OnGotMeleeAttack(Mobile attacker)
|
||||
public override void OnGotMeleeAttack(Mobile attacker, int damage)
|
||||
{
|
||||
base.OnGotMeleeAttack(attacker);
|
||||
base.OnGotMeleeAttack(attacker, damage);
|
||||
|
||||
if (FieldActive)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ namespace Server.Mobiles
|
|||
}
|
||||
}
|
||||
|
||||
public override void OnDamagedBySpell(Mobile from)
|
||||
public override void OnDamagedBySpell(Mobile from, int damage)
|
||||
{
|
||||
if (from?.Alive == true && Utility.RandomDouble() < 0.4)
|
||||
{
|
||||
|
|
@ -117,9 +117,9 @@ namespace Server.Mobiles
|
|||
}
|
||||
}
|
||||
|
||||
public override void OnGotMeleeAttack(Mobile attacker)
|
||||
public override void OnGotMeleeAttack(Mobile attacker, int damage)
|
||||
{
|
||||
base.OnGotMeleeAttack(attacker);
|
||||
base.OnGotMeleeAttack(attacker, damage);
|
||||
|
||||
if (FieldActive)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -96,13 +96,15 @@ namespace Server.Mobiles
|
|||
public override string CorpseName => "a chaos dragoon corpse";
|
||||
public override string DefaultName => "a chaos dragoon";
|
||||
|
||||
public override bool HasBreath => true;
|
||||
public override bool AutoDispel => true;
|
||||
public override bool BardImmune => !Core.AOS;
|
||||
public override bool CanRummageCorpses => true;
|
||||
public override bool AlwaysMurderer => true;
|
||||
public override bool ShowFameTitle => false;
|
||||
|
||||
private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath };
|
||||
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
|
||||
|
||||
public override int GetIdleSound() => 0x2CE;
|
||||
|
||||
public override int GetDeathSound() => 0x2CC;
|
||||
|
|
|
|||
|
|
@ -40,9 +40,7 @@ namespace Server.Mobiles
|
|||
Fame = 8000;
|
||||
Karma = -8000;
|
||||
|
||||
CraftResource res;
|
||||
|
||||
res = Utility.Random(6) switch
|
||||
var res = Utility.Random(6) switch
|
||||
{
|
||||
0 => CraftResource.BlackScales,
|
||||
1 => CraftResource.RedScales,
|
||||
|
|
@ -111,14 +109,15 @@ namespace Server.Mobiles
|
|||
|
||||
public override string CorpseName => "a chaos dragoon elite corpse";
|
||||
public override string DefaultName => "a chaos dragoon elite";
|
||||
|
||||
public override bool HasBreath => true;
|
||||
public override bool AutoDispel => true;
|
||||
public override bool BardImmune => !Core.AOS;
|
||||
public override bool CanRummageCorpses => true;
|
||||
public override bool AlwaysMurderer => true;
|
||||
public override bool ShowFameTitle => false;
|
||||
|
||||
private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath };
|
||||
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
|
||||
|
||||
public override int GetIdleSound() => 0x2CE;
|
||||
|
||||
public override int GetDeathSound() => 0x2CC;
|
||||
|
|
|
|||
|
|
@ -71,9 +71,9 @@ namespace Server.Mobiles
|
|||
|
||||
public override int GetDeathSound() => 0x28D;
|
||||
|
||||
public override void OnGaveMeleeAttack(Mobile defender)
|
||||
public override void OnGaveMeleeAttack(Mobile defender, int damage)
|
||||
{
|
||||
base.OnGaveMeleeAttack(defender);
|
||||
base.OnGaveMeleeAttack(defender, damage);
|
||||
|
||||
if (Utility.RandomDouble() > 0.2)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -69,9 +69,9 @@ namespace Server.Mobiles
|
|||
AddLoot(LootPack.UltraRich, 3);
|
||||
}
|
||||
|
||||
public override void OnGaveMeleeAttack(Mobile defender)
|
||||
public override void OnGaveMeleeAttack(Mobile defender, int damage)
|
||||
{
|
||||
base.OnGaveMeleeAttack(defender);
|
||||
base.OnGaveMeleeAttack(defender, damage);
|
||||
|
||||
if (Utility.RandomDouble() >= 0.1)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -5,8 +5,7 @@ namespace Server.Mobiles
|
|||
public class RedDeath : SkeletalMount
|
||||
{
|
||||
[Constructible]
|
||||
public RedDeath()
|
||||
: base("Red Death")
|
||||
public RedDeath() : base("Red Death")
|
||||
{
|
||||
IsParagon = true;
|
||||
|
||||
|
|
@ -52,8 +51,7 @@ namespace Server.Mobiles
|
|||
}
|
||||
}
|
||||
|
||||
public RedDeath(Serial serial)
|
||||
: base(serial)
|
||||
public RedDeath(Serial serial) : base(serial)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -61,9 +59,9 @@ namespace Server.Mobiles
|
|||
|
||||
public override bool GivesMLMinorArtifact => true;
|
||||
public override bool AlwaysMurderer => true;
|
||||
public override bool HasBreath => true;
|
||||
public override int BreathChaosDamage => 100;
|
||||
public override int BreathFireDamage => 0;
|
||||
|
||||
private static MonsterAbility[] _abilities = { MonsterAbility.ChaosBreath };
|
||||
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
|
||||
|
||||
public override void GenerateLoot()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -50,10 +50,10 @@ namespace Server.Mobiles
|
|||
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() );
|
||||
}
|
||||
|
|
@ -66,9 +66,9 @@ namespace Server.Mobiles
|
|||
AddLoot(LootPack.UltraRich, 2);
|
||||
}
|
||||
|
||||
public override void OnGaveMeleeAttack(Mobile defender)
|
||||
public override void OnGaveMeleeAttack(Mobile defender, int damage)
|
||||
{
|
||||
base.OnGaveMeleeAttack(defender);
|
||||
base.OnGaveMeleeAttack(defender, damage);
|
||||
|
||||
if (Utility.RandomDouble() < 0.1)
|
||||
{
|
||||
|
|
@ -76,9 +76,9 @@ namespace Server.Mobiles
|
|||
}
|
||||
}
|
||||
|
||||
public override void OnGotMeleeAttack(Mobile attacker)
|
||||
public override void OnGotMeleeAttack(Mobile attacker, int damage)
|
||||
{
|
||||
base.OnGotMeleeAttack(attacker);
|
||||
base.OnGotMeleeAttack(attacker, damage);
|
||||
|
||||
if (Utility.RandomDouble() < 0.1)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -38,19 +38,19 @@ namespace Server.Mobiles
|
|||
// TODO: Fame/Karma
|
||||
}
|
||||
|
||||
public Hydra(Serial serial)
|
||||
: base(serial)
|
||||
public Hydra(Serial serial) : base(serial)
|
||||
{
|
||||
}
|
||||
|
||||
public override string CorpseName => "a hydra corpse";
|
||||
public override string DefaultName => "a hydra";
|
||||
|
||||
public override bool HasBreath => true;
|
||||
public override int Hides => 40;
|
||||
public override int Meat => 19;
|
||||
public override int TreasureMapLevel => 5;
|
||||
|
||||
private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath };
|
||||
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
|
||||
|
||||
public override void GenerateLoot()
|
||||
{
|
||||
AddLoot(LootPack.UltraRich, 3);
|
||||
|
|
|
|||
|
|
@ -54,9 +54,7 @@ namespace Server.Mobiles
|
|||
public override string CorpseName => "a dragon corpse";
|
||||
public override bool StatLossAfterTame => true;
|
||||
public override string DefaultName => "a greater dragon";
|
||||
|
||||
public override bool ReacquireOnMovement => !Controlled;
|
||||
public override bool HasBreath => true; // fire breath enabled
|
||||
public override bool AutoDispel => !Controlled;
|
||||
public override int TreasureMapLevel => 5;
|
||||
public override int Meat => 19;
|
||||
|
|
@ -68,6 +66,9 @@ namespace Server.Mobiles
|
|||
public override bool CanAngerOnTame => true;
|
||||
public override bool CanFly => true;
|
||||
|
||||
private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath };
|
||||
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
|
||||
|
||||
public override void GenerateLoot()
|
||||
{
|
||||
AddLoot(LootPack.FilthyRich, 4);
|
||||
|
|
|
|||
|
|
@ -46,12 +46,14 @@ namespace Server.Mobiles
|
|||
public override int TreasureMapLevel => 5;
|
||||
public override int Meat => 5;
|
||||
public override int Hides => 10;
|
||||
public override bool CanBreath => true;
|
||||
public override bool CanAngerOnTame => true;
|
||||
public override bool StatLossAfterTame => true;
|
||||
public override FoodType FavoriteFood => FoodType.Meat;
|
||||
public override bool CanFly => true;
|
||||
|
||||
private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath };
|
||||
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
|
||||
|
||||
public override void GenerateLoot()
|
||||
{
|
||||
AddLoot(LootPack.AosUltraRich, 3);
|
||||
|
|
|
|||
|
|
@ -72,9 +72,9 @@ namespace Server.Mobiles
|
|||
AddLoot(LootPack.Gems);
|
||||
}
|
||||
|
||||
public override void OnGaveMeleeAttack(Mobile defender)
|
||||
public override void OnGaveMeleeAttack(Mobile defender, int damage)
|
||||
{
|
||||
base.OnGaveMeleeAttack(defender);
|
||||
base.OnGaveMeleeAttack(defender, damage);
|
||||
|
||||
if (Utility.RandomDouble() < 0.1)
|
||||
{
|
||||
|
|
@ -82,9 +82,9 @@ namespace Server.Mobiles
|
|||
}
|
||||
}
|
||||
|
||||
public override void OnGotMeleeAttack(Mobile attacker)
|
||||
public override void OnGotMeleeAttack(Mobile attacker, int damage)
|
||||
{
|
||||
base.OnGotMeleeAttack(attacker);
|
||||
base.OnGotMeleeAttack(attacker, damage);
|
||||
|
||||
if (Utility.RandomDouble() < 0.1)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -172,9 +172,9 @@ namespace Server.Mobiles
|
|||
}
|
||||
}
|
||||
|
||||
public override void OnGaveMeleeAttack(Mobile defender)
|
||||
public override void OnGaveMeleeAttack(Mobile defender, int damage)
|
||||
{
|
||||
base.OnGaveMeleeAttack(defender);
|
||||
base.OnGaveMeleeAttack(defender, damage);
|
||||
|
||||
if (Utility.RandomDouble() < 0.25)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -163,9 +163,9 @@ namespace Server.Mobiles
|
|||
|
||||
public override int GetDeathSound() => 0x59c;
|
||||
|
||||
public override void OnGaveMeleeAttack(Mobile defender)
|
||||
public override void OnGaveMeleeAttack(Mobile defender, int damage)
|
||||
{
|
||||
base.OnGaveMeleeAttack(defender);
|
||||
base.OnGaveMeleeAttack(defender, damage);
|
||||
|
||||
if (Utility.RandomDouble() <= 0.2)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -109,18 +109,18 @@ namespace Server.Mobiles
|
|||
}
|
||||
}
|
||||
|
||||
public override void OnGaveMeleeAttack(Mobile defender)
|
||||
public override void OnGaveMeleeAttack(Mobile defender, int damage)
|
||||
{
|
||||
base.OnGaveMeleeAttack(defender);
|
||||
base.OnGaveMeleeAttack(defender, damage);
|
||||
|
||||
defender.Damage(Utility.Random(20, 10), this);
|
||||
defender.Stam -= Utility.Random(20, 10);
|
||||
defender.Mana -= Utility.Random(20, 10);
|
||||
}
|
||||
|
||||
public override void OnGotMeleeAttack(Mobile attacker)
|
||||
public override void OnGotMeleeAttack(Mobile attacker, int damage)
|
||||
{
|
||||
base.OnGotMeleeAttack(attacker);
|
||||
base.OnGotMeleeAttack(attacker, damage);
|
||||
|
||||
if (Utility.RandomDouble() <= 0.1)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -49,9 +49,9 @@ namespace Server.Mobiles
|
|||
|
||||
public override bool GivesMLMinorArtifact => true;
|
||||
|
||||
public override void OnGaveMeleeAttack(Mobile defender)
|
||||
public override void OnGaveMeleeAttack(Mobile defender, int damage)
|
||||
{
|
||||
base.OnGaveMeleeAttack(defender);
|
||||
base.OnGaveMeleeAttack(defender, damage);
|
||||
|
||||
if (Utility.RandomBool())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -96,9 +96,9 @@ namespace Server.Mobiles
|
|||
}
|
||||
|
||||
// TODO: Put this attack shared with Hiryu and Lesser Hiryu in one place
|
||||
public override void OnGaveMeleeAttack(Mobile defender)
|
||||
public override void OnGaveMeleeAttack(Mobile defender, int damage)
|
||||
{
|
||||
base.OnGaveMeleeAttack(defender);
|
||||
base.OnGaveMeleeAttack(defender, damage);
|
||||
|
||||
if (Utility.RandomDouble() >= 0.1)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -44,12 +44,13 @@ namespace Server.Mobiles
|
|||
|
||||
public override string CorpseName => "a hell hound corpse";
|
||||
public override string DefaultName => "a hell hound";
|
||||
|
||||
public override bool HasBreath => true; // fire breath enabled
|
||||
public override int Meat => 1;
|
||||
public override FoodType FavoriteFood => FoodType.Meat;
|
||||
public override PackInstinct PackInstinct => PackInstinct.Canine;
|
||||
|
||||
private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath };
|
||||
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
|
||||
|
||||
public override void GenerateLoot()
|
||||
{
|
||||
AddLoot(LootPack.Average);
|
||||
|
|
|
|||
|
|
@ -97,18 +97,18 @@ namespace Server.Mobiles
|
|||
|
||||
public override int GetDeathSound() => 0x2F7;
|
||||
|
||||
public override void OnGaveMeleeAttack(Mobile defender)
|
||||
public override void OnGaveMeleeAttack(Mobile defender, int damage)
|
||||
{
|
||||
base.OnGaveMeleeAttack(defender);
|
||||
base.OnGaveMeleeAttack(defender, damage);
|
||||
|
||||
defender.Damage(Utility.Random(10, 10), this);
|
||||
defender.Stam -= Utility.Random(10, 10);
|
||||
defender.Mana -= Utility.Random(10, 10);
|
||||
}
|
||||
|
||||
public override void OnGotMeleeAttack(Mobile attacker)
|
||||
public override void OnGotMeleeAttack(Mobile attacker, int damage)
|
||||
{
|
||||
base.OnGotMeleeAttack(attacker);
|
||||
base.OnGotMeleeAttack(attacker, damage);
|
||||
|
||||
attacker.Damage(Utility.Random(10, 10), this);
|
||||
attacker.Stam -= Utility.Random(10, 10);
|
||||
|
|
|
|||
|
|
@ -169,9 +169,9 @@ namespace Server.Mobiles
|
|||
return base.GetHurtSound();
|
||||
}
|
||||
|
||||
public override void OnGaveMeleeAttack(Mobile defender)
|
||||
public override void OnGaveMeleeAttack(Mobile defender, int damage)
|
||||
{
|
||||
base.OnGaveMeleeAttack(defender);
|
||||
base.OnGaveMeleeAttack(defender, damage);
|
||||
|
||||
if (!m_Stunning && Utility.RandomDouble() < 0.3)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -115,16 +115,16 @@ namespace Server.Mobiles
|
|||
// TODO: dungeon chest, healthy gland
|
||||
}
|
||||
|
||||
public override void OnGaveMeleeAttack(Mobile defender)
|
||||
public override void OnGaveMeleeAttack(Mobile defender, int damage)
|
||||
{
|
||||
base.OnGaveMeleeAttack(defender);
|
||||
base.OnGaveMeleeAttack(defender, damage);
|
||||
|
||||
defender.ApplyPoison(this, IsParagon ? Poison.Lethal : Poison.Deadly);
|
||||
defender.FixedParticles(0x374A, 10, 15, 5021, EffectLayer.Waist);
|
||||
defender.PlaySound(0x1CB);
|
||||
}
|
||||
|
||||
public override void OnDamagedBySpell(Mobile caster)
|
||||
public override void OnDamagedBySpell(Mobile caster, int damage)
|
||||
{
|
||||
if (Map != null && caster != this && Utility.RandomDouble() < 0.25)
|
||||
{
|
||||
|
|
@ -137,10 +137,10 @@ namespace Server.Mobiles
|
|||
Say(1053034); // * The plague beast creates another beast from its flesh! *
|
||||
}
|
||||
|
||||
base.OnDamagedBySpell(caster);
|
||||
base.OnDamagedBySpell(caster, damage);
|
||||
}
|
||||
|
||||
public override void OnGotMeleeAttack(Mobile attacker)
|
||||
public override void OnGotMeleeAttack(Mobile attacker, int damage)
|
||||
{
|
||||
if (Map != null && attacker != this && Utility.RandomDouble() < 0.25)
|
||||
{
|
||||
|
|
@ -153,7 +153,7 @@ namespace Server.Mobiles
|
|||
Say(1053034); // * The plague beast creates another beast from its flesh! *
|
||||
}
|
||||
|
||||
base.OnGotMeleeAttack(attacker);
|
||||
base.OnGotMeleeAttack(attacker, damage);
|
||||
}
|
||||
|
||||
public override int GetIdleSound() => 0x1BF;
|
||||
|
|
|
|||
|
|
@ -118,9 +118,9 @@ namespace Server.Mobiles
|
|||
eable.Free();
|
||||
}
|
||||
|
||||
public override void OnGotMeleeAttack(Mobile attacker)
|
||||
public override void OnGotMeleeAttack(Mobile attacker, int damage)
|
||||
{
|
||||
base.OnGotMeleeAttack(attacker);
|
||||
base.OnGotMeleeAttack(attacker, damage);
|
||||
|
||||
if (Hits > HitsMax / 4)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -46,7 +46,6 @@ namespace Server.Mobiles
|
|||
public override string DefaultName => "an ancient wyrm";
|
||||
|
||||
public override bool ReacquireOnMovement => true;
|
||||
public override bool HasBreath => true; // fire breath enabled
|
||||
public override bool AutoDispel => true;
|
||||
public override HideType HideType => HideType.Barbed;
|
||||
public override int Hides => 40;
|
||||
|
|
@ -58,6 +57,9 @@ namespace Server.Mobiles
|
|||
public override int TreasureMapLevel => 5;
|
||||
public override bool CanFly => true;
|
||||
|
||||
private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath };
|
||||
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
|
||||
|
||||
public override void GenerateLoot()
|
||||
{
|
||||
AddLoot(LootPack.FilthyRich, 3);
|
||||
|
|
|
|||
|
|
@ -57,12 +57,13 @@ namespace Server.Mobiles
|
|||
|
||||
public override string CorpseName => "a deep sea serpents corpse";
|
||||
public override string DefaultName => "a deep sea serpent";
|
||||
|
||||
public override bool HasBreath => true;
|
||||
public override int Meat => 1;
|
||||
public override int Scales => 8;
|
||||
public override ScaleType ScaleType => ScaleType.Blue;
|
||||
|
||||
private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath };
|
||||
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
|
||||
|
||||
public override void GenerateLoot()
|
||||
{
|
||||
AddLoot(LootPack.Meager);
|
||||
|
|
|
|||
|
|
@ -48,7 +48,6 @@ namespace Server.Mobiles
|
|||
public override string DefaultName => "a dragon";
|
||||
|
||||
public override bool ReacquireOnMovement => !Controlled;
|
||||
public override bool HasBreath => true; // fire breath enabled
|
||||
public override bool AutoDispel => !Controlled;
|
||||
public override int TreasureMapLevel => 4;
|
||||
public override int Meat => 19;
|
||||
|
|
@ -60,6 +59,9 @@ namespace Server.Mobiles
|
|||
public override bool CanAngerOnTame => true;
|
||||
public override bool CanFly => true;
|
||||
|
||||
private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath };
|
||||
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
|
||||
|
||||
public override void GenerateLoot()
|
||||
{
|
||||
AddLoot(LootPack.FilthyRich, 2);
|
||||
|
|
|
|||
|
|
@ -49,14 +49,8 @@ namespace Server.Mobiles
|
|||
CantWalk = true;
|
||||
|
||||
PackItem(new MessageInABottle());
|
||||
|
||||
var rope = new Rope();
|
||||
rope.ItemID = 0x14F8;
|
||||
PackItem(rope);
|
||||
|
||||
rope = new Rope();
|
||||
rope.ItemID = 0x14FA;
|
||||
PackItem(rope);
|
||||
PackItem(new Rope { ItemID = 0x14F8 });
|
||||
PackItem(new Rope { ItemID = 0x14FA });
|
||||
}
|
||||
|
||||
public Leviathan(Serial serial) : base(serial)
|
||||
|
|
@ -69,15 +63,6 @@ namespace Server.Mobiles
|
|||
|
||||
public override string DefaultName => "a leviathan";
|
||||
|
||||
public override bool HasBreath => true;
|
||||
public override int BreathPhysicalDamage => 70; // TODO: Verify damage type
|
||||
public override int BreathColdDamage => 30;
|
||||
public override int BreathFireDamage => 0;
|
||||
public override int BreathEffectHue => 0x1ED;
|
||||
public override double BreathDamageScalar => 0.05;
|
||||
public override double BreathMinDelay => 5.0;
|
||||
public override double BreathMaxDelay => 7.5;
|
||||
|
||||
public override int TreasureMapLevel => 5;
|
||||
|
||||
public static Type[] Artifacts { get; } =
|
||||
|
|
@ -109,6 +94,9 @@ namespace Server.Mobiles
|
|||
typeof(VioletCourage)
|
||||
};
|
||||
|
||||
private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath };
|
||||
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
|
||||
|
||||
public override void GenerateLoot()
|
||||
{
|
||||
AddLoot(LootPack.FilthyRich, 5);
|
||||
|
|
@ -176,5 +164,18 @@ namespace Server.Mobiles
|
|||
|
||||
Fisher = null;
|
||||
}
|
||||
|
||||
private static MonsterAbility _ability = new LevianthanBreath();
|
||||
|
||||
public class LevianthanBreath : FireBreath
|
||||
{
|
||||
public override int BreathPhysicalDamage => 70;
|
||||
public override int BreathColdDamage => 30;
|
||||
public override int BreathFireDamage => 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,8 +60,6 @@ namespace Server.Mobiles
|
|||
|
||||
public override string CorpseName => "a sea serpents corpse";
|
||||
public override string DefaultName => "a sea serpent";
|
||||
|
||||
public override bool HasBreath => true;
|
||||
public override int TreasureMapLevel => 2;
|
||||
|
||||
public override int Hides => 10;
|
||||
|
|
@ -69,6 +67,9 @@ namespace Server.Mobiles
|
|||
public override int Scales => 8;
|
||||
public override ScaleType ScaleType => ScaleType.Blue;
|
||||
|
||||
private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath };
|
||||
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
|
||||
|
||||
public override void GenerateLoot()
|
||||
{
|
||||
AddLoot(LootPack.Meager);
|
||||
|
|
|
|||
|
|
@ -51,9 +51,7 @@ namespace Server.Mobiles
|
|||
|
||||
public override string CorpseName => "a dragon corpse";
|
||||
public override string DefaultName => "a serpentine dragon";
|
||||
|
||||
public override bool ReacquireOnMovement => true;
|
||||
public override bool HasBreath => true; // fire breath enabled
|
||||
public override double BonusPetDamageScalar => Core.SE ? 3.0 : 1.0;
|
||||
|
||||
public override bool AutoDispel => true;
|
||||
|
|
@ -64,6 +62,9 @@ namespace Server.Mobiles
|
|||
public override ScaleType ScaleType => Utility.RandomBool() ? ScaleType.Black : ScaleType.White;
|
||||
public override int TreasureMapLevel => 4;
|
||||
|
||||
private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath };
|
||||
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
|
||||
|
||||
public override void GenerateLoot()
|
||||
{
|
||||
AddLoot(LootPack.FilthyRich, 2);
|
||||
|
|
@ -80,9 +81,9 @@ namespace Server.Mobiles
|
|||
|
||||
public override int GetHurtSound() => 0x2C3;
|
||||
|
||||
public override void OnGotMeleeAttack(Mobile attacker)
|
||||
public override void OnGotMeleeAttack(Mobile attacker, int damage)
|
||||
{
|
||||
base.OnGotMeleeAttack(attacker);
|
||||
base.OnGotMeleeAttack(attacker, damage);
|
||||
|
||||
if (!Core.SE && Utility.RandomDouble() < 0.2 && attacker is BaseCreature c && c.Controlled &&
|
||||
c.ControlMaster != null)
|
||||
|
|
|
|||
|
|
@ -44,9 +44,7 @@ namespace Server.Mobiles
|
|||
|
||||
public override string CorpseName => "a shadow wyrm corpse";
|
||||
public override string DefaultName => "a shadow wyrm";
|
||||
|
||||
public override bool ReacquireOnMovement => true;
|
||||
public override bool HasBreath => true; // fire breath enabled
|
||||
public override bool AutoDispel => true;
|
||||
public override Poison PoisonImmune => Poison.Deadly;
|
||||
public override Poison HitPoison => Poison.Deadly;
|
||||
|
|
@ -59,6 +57,9 @@ namespace Server.Mobiles
|
|||
public override HideType HideType => HideType.Barbed;
|
||||
public override bool CanFly => true;
|
||||
|
||||
private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath };
|
||||
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
|
||||
|
||||
public override void GenerateLoot()
|
||||
{
|
||||
AddLoot(LootPack.FilthyRich, 3);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Server.Mobiles
|
||||
{
|
||||
public class SkeletalDragon : BaseCreature
|
||||
|
|
@ -47,10 +49,6 @@ namespace Server.Mobiles
|
|||
public override string DefaultName => "a skeletal dragon";
|
||||
|
||||
public override bool ReacquireOnMovement => true;
|
||||
public override bool HasBreath => true; // fire breath enabled
|
||||
public override int BreathFireDamage => 0;
|
||||
public override int BreathColdDamage => 100;
|
||||
public override int BreathEffectHue => 0x480;
|
||||
|
||||
public override double BonusPetDamageScalar => Core.SE ? 3.0 : 1.0;
|
||||
// TODO: Undead summoning?
|
||||
|
|
@ -62,6 +60,9 @@ namespace Server.Mobiles
|
|||
public override int Hides => 20;
|
||||
public override HideType HideType => HideType.Barbed;
|
||||
|
||||
private static MonsterAbility[] _abilities = { MonsterAbility.ColdBreath };
|
||||
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
|
||||
|
||||
public override void GenerateLoot()
|
||||
{
|
||||
AddLoot(LootPack.FilthyRich, 4);
|
||||
|
|
|
|||
|
|
@ -49,7 +49,6 @@ namespace Server.Mobiles
|
|||
public override string DefaultName => "a drake";
|
||||
|
||||
public override bool ReacquireOnMovement => true;
|
||||
public override bool HasBreath => true; // fire breath enabled
|
||||
public override int TreasureMapLevel => 2;
|
||||
public override int Meat => 10;
|
||||
public override int Hides => 20;
|
||||
|
|
@ -59,6 +58,9 @@ namespace Server.Mobiles
|
|||
public override FoodType FavoriteFood => FoodType.Meat | FoodType.Fish;
|
||||
public override bool CanFly => true;
|
||||
|
||||
private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath };
|
||||
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
|
||||
|
||||
public override void GenerateLoot()
|
||||
{
|
||||
AddLoot(LootPack.Rich);
|
||||
|
|
|
|||
|
|
@ -89,9 +89,9 @@ namespace Server.Mobiles
|
|||
return base.OnBeforeDeath();
|
||||
}
|
||||
|
||||
public override void OnGaveMeleeAttack(Mobile defender)
|
||||
public override void OnGaveMeleeAttack(Mobile defender, int damage)
|
||||
{
|
||||
base.OnGaveMeleeAttack(defender);
|
||||
base.OnGaveMeleeAttack(defender, damage);
|
||||
|
||||
if (Utility.RandomDouble() >= 0.1)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -101,21 +101,21 @@ namespace Server.Mobiles
|
|||
AOS.Damage(to, this, Utility.RandomMinMax(50, 65), 100, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
public override void OnDamagedBySpell(Mobile attacker)
|
||||
public override void OnDamagedBySpell(Mobile attacker, int damage)
|
||||
{
|
||||
base.OnDamagedBySpell(attacker);
|
||||
base.OnDamagedBySpell(attacker, damage);
|
||||
ThrowFan(attacker);
|
||||
}
|
||||
|
||||
public override void OnGotMeleeAttack(Mobile attacker)
|
||||
public override void OnGotMeleeAttack(Mobile attacker, int damage)
|
||||
{
|
||||
base.OnGotMeleeAttack(attacker);
|
||||
base.OnGotMeleeAttack(attacker, damage);
|
||||
ThrowFan(attacker);
|
||||
}
|
||||
|
||||
public override void OnGaveMeleeAttack(Mobile defender)
|
||||
public override void OnGaveMeleeAttack(Mobile defender, int damage)
|
||||
{
|
||||
base.OnGaveMeleeAttack(defender);
|
||||
base.OnGaveMeleeAttack(defender, damage);
|
||||
|
||||
if (!IsFanned(defender) && Utility.RandomDouble() < 0.05)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -85,9 +85,9 @@ namespace Server.Mobiles
|
|||
|
||||
public override int GetDeathSound() => 0x508;
|
||||
|
||||
public override void OnGaveMeleeAttack(Mobile defender)
|
||||
public override void OnGaveMeleeAttack(Mobile defender, int damage)
|
||||
{
|
||||
base.OnGaveMeleeAttack(defender);
|
||||
base.OnGaveMeleeAttack(defender, damage);
|
||||
|
||||
if (Utility.RandomBool())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -59,9 +59,9 @@ namespace Server.Mobiles
|
|||
AddLoot(LootPack.Rich, 3);
|
||||
}
|
||||
|
||||
public override void OnGaveMeleeAttack(Mobile defender)
|
||||
public override void OnGaveMeleeAttack(Mobile defender, int damage)
|
||||
{
|
||||
base.OnGaveMeleeAttack(defender);
|
||||
base.OnGaveMeleeAttack(defender, damage);
|
||||
|
||||
if (Utility.RandomDouble() < 0.1)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -73,9 +73,9 @@ namespace Server.Mobiles
|
|||
|
||||
// TODO: Snowball
|
||||
|
||||
public override void OnGaveMeleeAttack(Mobile defender)
|
||||
public override void OnGaveMeleeAttack(Mobile defender, int damage)
|
||||
{
|
||||
base.OnGaveMeleeAttack(defender);
|
||||
base.OnGaveMeleeAttack(defender, damage);
|
||||
|
||||
if (Utility.RandomDouble() >= 0.1)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -56,9 +56,9 @@ namespace Server.Mobiles
|
|||
AddLoot(LootPack.Gems, 2);
|
||||
}
|
||||
|
||||
public override void OnGaveMeleeAttack(Mobile defender)
|
||||
public override void OnGaveMeleeAttack(Mobile defender, int damage)
|
||||
{
|
||||
base.OnGaveMeleeAttack(defender);
|
||||
base.OnGaveMeleeAttack(defender, damage);
|
||||
|
||||
if (Utility.RandomDouble() >= 0.1 || m_Table.Contains(defender))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -95,9 +95,9 @@ namespace Server.Mobiles
|
|||
AddLoot(LootPack.MedScrolls, 1);
|
||||
}
|
||||
|
||||
public override void OnGaveMeleeAttack(Mobile defender)
|
||||
public override void OnGaveMeleeAttack(Mobile defender, int damage)
|
||||
{
|
||||
base.OnGaveMeleeAttack(defender);
|
||||
base.OnGaveMeleeAttack(defender, damage);
|
||||
|
||||
if (Utility.RandomDouble() >= 0.05)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -78,9 +78,9 @@ namespace Server.Mobiles
|
|||
AddLoot(LootPack.Rich);
|
||||
}
|
||||
|
||||
public override void OnGaveMeleeAttack(Mobile defender)
|
||||
public override void OnGaveMeleeAttack(Mobile defender, int damage)
|
||||
{
|
||||
base.OnGaveMeleeAttack(defender);
|
||||
base.OnGaveMeleeAttack(defender, damage);
|
||||
|
||||
if (Utility.RandomDouble() >= 0.1)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -68,16 +68,16 @@ namespace Server.Mobiles
|
|||
AddLoot(LootPack.Gems, 6);
|
||||
}
|
||||
|
||||
public override void OnDamagedBySpell(Mobile attacker)
|
||||
public override void OnDamagedBySpell(Mobile attacker, int damage)
|
||||
{
|
||||
base.OnDamagedBySpell(attacker);
|
||||
base.OnDamagedBySpell(attacker, damage);
|
||||
|
||||
DoCounter(attacker);
|
||||
}
|
||||
|
||||
public override void OnGotMeleeAttack(Mobile attacker)
|
||||
public override void OnGotMeleeAttack(Mobile attacker, int damage)
|
||||
{
|
||||
base.OnGotMeleeAttack(attacker);
|
||||
base.OnGotMeleeAttack(attacker, damage);
|
||||
|
||||
DoCounter(attacker);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,9 +89,9 @@ namespace Server.Mobiles
|
|||
|
||||
// TODO: Axe Throw
|
||||
|
||||
public override void OnGaveMeleeAttack(Mobile defender)
|
||||
public override void OnGaveMeleeAttack(Mobile defender, int damage)
|
||||
{
|
||||
base.OnGaveMeleeAttack(defender);
|
||||
base.OnGaveMeleeAttack(defender, damage);
|
||||
|
||||
if (Utility.RandomDouble() < 0.1)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -97,9 +97,9 @@ namespace Server.Mobiles
|
|||
|
||||
// TODO: Body Transformation
|
||||
|
||||
public override void OnGaveMeleeAttack(Mobile defender)
|
||||
public override void OnGaveMeleeAttack(Mobile defender, int damage)
|
||||
{
|
||||
base.OnGaveMeleeAttack(defender);
|
||||
base.OnGaveMeleeAttack(defender, damage);
|
||||
|
||||
if (Utility.RandomDouble() < 0.1)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -95,9 +95,9 @@ namespace Server.Mobiles
|
|||
|
||||
// TODO: Throwing Dagger
|
||||
|
||||
public override void OnGaveMeleeAttack(Mobile defender)
|
||||
public override void OnGaveMeleeAttack(Mobile defender, int damage)
|
||||
{
|
||||
base.OnGaveMeleeAttack(defender);
|
||||
base.OnGaveMeleeAttack(defender, damage);
|
||||
|
||||
if (Utility.RandomDouble() < 0.1)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -201,16 +201,16 @@ namespace Server.Mobiles
|
|||
}
|
||||
}
|
||||
|
||||
public override void OnGotMeleeAttack(Mobile attacker)
|
||||
public override void OnGotMeleeAttack(Mobile attacker, int damage)
|
||||
{
|
||||
base.OnGotMeleeAttack(attacker);
|
||||
base.OnGotMeleeAttack(attacker, damage);
|
||||
|
||||
DoSpecialAbility(attacker);
|
||||
}
|
||||
|
||||
public override void OnGaveMeleeAttack(Mobile defender)
|
||||
public override void OnGaveMeleeAttack(Mobile defender, int damage)
|
||||
{
|
||||
base.OnGaveMeleeAttack(defender);
|
||||
base.OnGaveMeleeAttack(defender, damage);
|
||||
|
||||
DoSpecialAbility(defender);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -158,18 +158,18 @@ namespace Server.Mobiles
|
|||
}
|
||||
}
|
||||
|
||||
public override void OnGaveMeleeAttack(Mobile defender)
|
||||
public override void OnGaveMeleeAttack(Mobile defender, int damage)
|
||||
{
|
||||
base.OnGaveMeleeAttack(defender);
|
||||
base.OnGaveMeleeAttack(defender, damage);
|
||||
|
||||
defender.Damage(Utility.Random(20, 10), this);
|
||||
defender.Stam -= Utility.Random(20, 10);
|
||||
defender.Mana -= Utility.Random(20, 10);
|
||||
}
|
||||
|
||||
public override void OnGotMeleeAttack(Mobile attacker)
|
||||
public override void OnGotMeleeAttack(Mobile attacker, int damage)
|
||||
{
|
||||
base.OnGotMeleeAttack(attacker);
|
||||
base.OnGotMeleeAttack(attacker, damage);
|
||||
|
||||
CheckQueen();
|
||||
|
||||
|
|
|
|||
|
|
@ -68,9 +68,9 @@ namespace Server.Mobiles
|
|||
AddLoot(LootPack.UltraRich, 4);
|
||||
}
|
||||
|
||||
public override void OnGotMeleeAttack(Mobile attacker)
|
||||
public override void OnGotMeleeAttack(Mobile attacker, int damage)
|
||||
{
|
||||
base.OnGotMeleeAttack(attacker);
|
||||
base.OnGotMeleeAttack(attacker, damage);
|
||||
|
||||
// TODO: Web ability
|
||||
}
|
||||
|
|
|
|||
|
|
@ -140,9 +140,9 @@ namespace Server.Mobiles
|
|||
}
|
||||
}
|
||||
|
||||
public override void OnGaveMeleeAttack(Mobile defender)
|
||||
public override void OnGaveMeleeAttack(Mobile defender, int damage)
|
||||
{
|
||||
base.OnGaveMeleeAttack(defender);
|
||||
base.OnGaveMeleeAttack(defender, damage);
|
||||
|
||||
if (Utility.RandomDouble() <= 0.1) // 10% chance to drop or throw an unholy bone
|
||||
{
|
||||
|
|
@ -152,9 +152,9 @@ namespace Server.Mobiles
|
|||
CheckSpeedBoost();
|
||||
}
|
||||
|
||||
public override void OnGotMeleeAttack(Mobile attacker)
|
||||
public override void OnGotMeleeAttack(Mobile attacker, int damage)
|
||||
{
|
||||
base.OnGotMeleeAttack(attacker);
|
||||
base.OnGotMeleeAttack(attacker, damage);
|
||||
|
||||
if (Utility.RandomDouble() <= 0.1) // 10% chance to drop or throw an unholy bone
|
||||
{
|
||||
|
|
|
|||
|
|
@ -79,9 +79,9 @@ namespace Server.Mobiles
|
|||
AddLoot(LootPack.UltraRich, 4);
|
||||
}
|
||||
|
||||
public override void OnGaveMeleeAttack(Mobile defender)
|
||||
public override void OnGaveMeleeAttack(Mobile defender, int damage)
|
||||
{
|
||||
base.OnGaveMeleeAttack(defender);
|
||||
base.OnGaveMeleeAttack(defender, damage);
|
||||
|
||||
if (Utility.RandomDouble() <= 0.2)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -122,9 +122,9 @@ namespace Server.Mobiles
|
|||
eable.Free();
|
||||
}
|
||||
|
||||
public override void OnGaveMeleeAttack(Mobile defender)
|
||||
public override void OnGaveMeleeAttack(Mobile defender, int damage)
|
||||
{
|
||||
base.OnGaveMeleeAttack(defender);
|
||||
base.OnGaveMeleeAttack(defender, damage);
|
||||
|
||||
if (Utility.RandomDouble() <= 0.25)
|
||||
{
|
||||
|
|
@ -132,9 +132,9 @@ namespace Server.Mobiles
|
|||
}
|
||||
}
|
||||
|
||||
public override void OnGotMeleeAttack(Mobile attacker)
|
||||
public override void OnGotMeleeAttack(Mobile attacker, int damage)
|
||||
{
|
||||
base.OnGotMeleeAttack(attacker);
|
||||
base.OnGotMeleeAttack(attacker, damage);
|
||||
|
||||
if (Utility.RandomDouble() <= 0.25)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -89,17 +89,17 @@ namespace Server.Mobiles
|
|||
|
||||
// TODO: Hit Lightning Area
|
||||
|
||||
public override void OnDamagedBySpell(Mobile attacker)
|
||||
public override void OnDamagedBySpell(Mobile attacker, int damage)
|
||||
{
|
||||
base.OnDamagedBySpell(attacker);
|
||||
base.OnDamagedBySpell(attacker, damage);
|
||||
|
||||
ScaleResistances();
|
||||
DoCounter(attacker);
|
||||
}
|
||||
|
||||
public override void OnGotMeleeAttack(Mobile attacker)
|
||||
public override void OnGotMeleeAttack(Mobile attacker, int damage)
|
||||
{
|
||||
base.OnGotMeleeAttack(attacker);
|
||||
base.OnGotMeleeAttack(attacker, damage);
|
||||
|
||||
ScaleResistances();
|
||||
DoCounter(attacker);
|
||||
|
|
|
|||
|
|
@ -84,18 +84,18 @@ namespace Server.Mobiles
|
|||
}
|
||||
}
|
||||
|
||||
public override void OnGaveMeleeAttack(Mobile defender)
|
||||
public override void OnGaveMeleeAttack(Mobile defender, int damage)
|
||||
{
|
||||
base.OnGaveMeleeAttack(defender);
|
||||
base.OnGaveMeleeAttack(defender, damage);
|
||||
|
||||
defender.Damage(Utility.Random(20, 10), this);
|
||||
defender.Stam -= Utility.Random(20, 10);
|
||||
defender.Mana -= Utility.Random(20, 10);
|
||||
}
|
||||
|
||||
public override void OnGotMeleeAttack(Mobile attacker)
|
||||
public override void OnGotMeleeAttack(Mobile attacker, int damage)
|
||||
{
|
||||
base.OnGotMeleeAttack(attacker);
|
||||
base.OnGotMeleeAttack(attacker, damage);
|
||||
|
||||
if (Utility.RandomDouble() <= 0.1)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -935,26 +935,29 @@ namespace Server.Spells
|
|||
|
||||
public static void Damage(Spell spell, TimeSpan delay, Mobile target, Mobile from, double damage)
|
||||
{
|
||||
var iDamage = (int)damage;
|
||||
var damageGiven = (int)damage;
|
||||
|
||||
if (delay == TimeSpan.Zero)
|
||||
{
|
||||
(from as BaseCreature)?.AlterSpellDamageTo(target, ref iDamage);
|
||||
|
||||
var bcFrom = from as BaseCreature;
|
||||
var bcTarget = target as BaseCreature;
|
||||
bcTarget?.AlterSpellDamageFrom(from, ref iDamage);
|
||||
|
||||
target.Damage(iDamage, from);
|
||||
bcFrom?.AlterSpellDamageTo(target, ref damageGiven);
|
||||
bcTarget?.AlterSpellDamageFrom(from, ref damageGiven);
|
||||
|
||||
target.Damage(damageGiven, from);
|
||||
|
||||
bcFrom?.OnDamageSpell(target, damageGiven);
|
||||
|
||||
if (from != null)
|
||||
{
|
||||
bcTarget?.OnHarmfulSpell(from);
|
||||
bcTarget?.OnDamagedBySpell(from);
|
||||
bcTarget?.OnDamagedBySpell(from, damageGiven);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
new SpellDamageTimer(spell, target, from, iDamage, delay).Start();
|
||||
new SpellDamageTimer(spell, target, from, damageGiven, delay).Start();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1004,26 +1007,30 @@ namespace Server.Spells
|
|||
|
||||
if (delay == TimeSpan.Zero)
|
||||
{
|
||||
(from as BaseCreature)?.AlterSpellDamageTo(target, ref dmg);
|
||||
var bcFrom = from as BaseCreature;
|
||||
var bcTarget = target as BaseCreature;
|
||||
bcFrom?.AlterSpellDamageTo(target, ref dmg);
|
||||
|
||||
(target as BaseCreature)?.AlterSpellDamageFrom(from, ref dmg);
|
||||
bcTarget?.AlterSpellDamageFrom(from, ref dmg);
|
||||
|
||||
WeightOverloading.DFA = dfa;
|
||||
|
||||
var damageGiven = AOS.Damage(target, from, dmg, phys, fire, cold, pois, nrgy, chaos);
|
||||
|
||||
WeightOverloading.DFA = DFAlgorithm.Standard;
|
||||
|
||||
bcFrom?.OnDamageSpell(target, damageGiven);
|
||||
|
||||
if (bcTarget != null && from != null && delay == TimeSpan.Zero)
|
||||
{
|
||||
bcTarget.OnHarmfulSpell(from);
|
||||
bcTarget.OnDamagedBySpell(from, damageGiven);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
new SpellDamageTimerAOS(spell, delay, target, from, dmg, phys, fire, cold, pois, nrgy, chaos, dfa).Start();
|
||||
}
|
||||
|
||||
if (target is BaseCreature c && from != null && delay == TimeSpan.Zero)
|
||||
{
|
||||
c.OnHarmfulSpell(from);
|
||||
c.OnDamagedBySpell(from);
|
||||
}
|
||||
}
|
||||
|
||||
public static void DoLeech(int damageGiven, Mobile from, Mobile target)
|
||||
|
|
@ -1127,16 +1134,17 @@ namespace Server.Spells
|
|||
|
||||
protected override void OnTick()
|
||||
{
|
||||
var bcFrom = m_From as BaseCreature;
|
||||
var bcTarg = m_Target as BaseCreature;
|
||||
|
||||
if (m_From is BaseCreature bcFrom && m_Target != null)
|
||||
if (m_Target != null)
|
||||
{
|
||||
bcFrom.AlterSpellDamageTo(m_Target, ref m_Damage);
|
||||
bcFrom?.AlterSpellDamageTo(m_Target, ref m_Damage);
|
||||
}
|
||||
|
||||
if (bcTarg != null && m_From != null)
|
||||
if (m_From != null)
|
||||
{
|
||||
bcTarg.AlterSpellDamageFrom(m_From, ref m_Damage);
|
||||
bcTarg?.AlterSpellDamageFrom(m_From, ref m_Damage);
|
||||
}
|
||||
|
||||
WeightOverloading.DFA = m_DFA;
|
||||
|
|
@ -1145,10 +1153,12 @@ namespace Server.Spells
|
|||
|
||||
WeightOverloading.DFA = DFAlgorithm.Standard;
|
||||
|
||||
if (bcTarg != null && m_From != null)
|
||||
bcFrom?.OnDamageSpell(m_Target, damageGiven);
|
||||
|
||||
if (m_From != null)
|
||||
{
|
||||
bcTarg.OnHarmfulSpell(m_From);
|
||||
bcTarg.OnDamagedBySpell(m_From);
|
||||
bcTarg?.OnHarmfulSpell(m_From);
|
||||
bcTarg?.OnDamagedBySpell(m_From, damageGiven);
|
||||
}
|
||||
|
||||
m_Spell?.RemoveDelayedDamageContext(m_Target);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue