using System; using ModernUO.CodeGeneratedEvents; namespace Server.Mobiles; /// /// Abstract class used to build singletons for managing a specific monster ability. /// public abstract class MonsterAbility { 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; /// /// Returns true if ability is not on cooldown, and the chance to trigger succeeds. /// /// Boolean indicating the ability can trigger. public virtual bool CanTrigger(BaseCreature source, MonsterAbilityTrigger trigger) { if ((AbilityTrigger & trigger) == 0 || source is not { Alive: true, Deleted: false }) { return false; } if (source.IsAbilityOnCooldown(this)) { return false; } var c = ChanceToTrigger; return c >= 1 || c > 0 && c > Utility.RandomDouble(); } /// /// Triggers the monster's ability. Override this and call `base.Trigger(source);` to make sure /// the cooldown is tracked. /// Note: This can fire after a monster is killed/deleted (map is null) /// public virtual void Trigger(MonsterAbilityTrigger trigger, BaseCreature source, Mobile target) { if (MinTriggerCooldown <= TimeSpan.Zero && MaxTriggerCooldown <= TimeSpan.Zero) { return; } var randomAmount = MinTriggerCooldown == MaxTriggerCooldown ? MinTriggerCooldown : Utility.RandomMinMax(MinTriggerCooldown, MaxTriggerCooldown); source.SetAbilityCooldown(this, randomAmount); } public virtual void AlterMeleeDamageFrom(BaseCreature source, Mobile target, ref int damage) { } public virtual void AlterMeleeDamageTo(BaseCreature source, Mobile target, ref int damage) { } public virtual void AlterSpellDamageScalarFrom(BaseCreature source, Mobile target, ref double scalar) { } public virtual void AlterSpellDamageScalarTo(BaseCreature source, Mobile target, ref double scalar) { } public virtual void AlterSpellDamageFrom(BaseCreature source, Mobile target, ref int damage) { } public virtual void AlterSpellDamageTo(BaseCreature source, Mobile target, ref int damage) { } public virtual void Move(BaseCreature source, Direction d) { } [OnEvent(nameof(CreatureEvents.CreatureDeathEvent))] public static void ResetCooldownsOnDeath(BaseCreature source) => source.ClearAbilityCooldowns(); }