ModernUO/Projects/UOContent/Mobiles/Abilities/FireBreath.cs
Kamron Batman 3c636b04fd
feat: Adds magical barrier and ebolt counter ability (#1182)
* Adds magical barrier ability (Pre-UOML)
* Adds ebolt counter (Post-UOML)
2022-10-02 20:48:53 -07:00

218 lines
7 KiB
C#

using System;
using Server.Spells.Bushido;
namespace Server.Mobiles;
public class FireBreath : MonsterAbility
{
public override MonsterAbilityTrigger AbilityTrigger => MonsterAbilityTrigger.Think;
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 ChaosDamage => 0;
public virtual int PhysicalDamage => 0;
public virtual int FireDamage => 100;
public virtual int ColdDamage => 0;
public virtual int PoisonDamage => 0;
public virtual int EnergyDamage => 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, MonsterAbilityTrigger trigger) => !source.Summoned && base.CanTrigger(source, trigger);
public override void Trigger(MonsterAbilityTrigger 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(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 = PhysicalDamage;
var fireDamage = FireDamage;
var coldDamage = ColdDamage;
var poisDamage = PoisonDamage;
var nrgyDamage = EnergyDamage;
if (ChaosDamage > 0)
{
switch (Utility.Random(5))
{
case 0:
{
physDamage += ChaosDamage;
break;
}
case 1:
{
fireDamage += ChaosDamage;
break;
}
case 2:
{
coldDamage += ChaosDamage;
break;
}
case 3:
{
poisDamage += ChaosDamage;
break;
}
case 4:
{
nrgyDamage += ChaosDamage;
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);
}
}