#W# Work: Working on innate abilities.

This commit is contained in:
WarrentyExpired 2026-10-01 13:45:10 -04:00
parent c22afc8ad6
commit a628d30ed6
3 changed files with 178 additions and 6 deletions

View file

@ -113,6 +113,7 @@
"exileBestiary.damage.markTickSeconds": "00:00:02",
"exileBestiary.damage.markTickPercent": "0.01",
"exileBestiary.drop.chance": "-1.0",
"exileBestiary.drop.pityThreshold": "40"
"exileBestiary.drop.pityThreshold": "40",
"visibleDamage": "True"
}
}

View file

@ -1,4 +1,5 @@
using System;
using System.Collections.Concurrent;
using Server.Mobiles;
using Server.Items;
@ -6,36 +7,67 @@ namespace Server.Combat
{
public static class InnateWeaponAbilities
{
// Upgraded to a ConcurrentDictionary to handle ModernUO's multithreaded combat
// Using 'long' to track Core.TickCount instead of DateTime
private static readonly ConcurrentDictionary<Mobile, long> _cooldowns = new ConcurrentDictionary<Mobile, long>();
public static void CheckProc(Mobile attacker, Mobile defender, BaseWeapon weapon)
{
if (attacker == null || defender == null || weapon == null || !defender.Alive)
return;
double procChance = 0.10;
// 1. Thread-safe Cooldown Check
if (_cooldowns.TryGetValue(attacker, out long nextAllowedProc))
{
// If the current server tick is less than the allowed tick, they are still on cooldown
if (Core.TickCount < nextAllowedProc)
return;
}
double wpnSkill = attacker.Skills[weapon.Skill].Base;
double tactics = attacker.Skills[SkillName.Tactics].Base;
if (wpnSkill < 69.9 || tactics < 69.9)
return;
// Ensure you drop this back down to 0.10 if you raised it for testing!
double procChance = 1.0;
if (Utility.RandomDouble() >= procChance)
return;
// 2. Proc Successful: Lock them out for 5000 milliseconds (5 seconds)
_cooldowns[attacker] = Core.TickCount + 5000;
bool isGrandmaster = (wpnSkill >= 99.9 && tactics >= 99.9);
bool useTierTwo = isGrandmaster && Utility.RandomBool();
switch (weapon.Skill)
{
case SkillName.Swords:
ApplyBleed(attacker, defender);
if (useTierTwo) ApplySever(attacker, defender);
else ApplyBleed(attacker, defender);
break;
case SkillName.Archery:
ApplyHinder(attacker, defender);
if (useTierTwo) ApplyHeadshot(attacker, defender);
else ApplyHinder(attacker, defender);
break;
case SkillName.Fencing:
ApplyPierce(attacker, defender);
if (useTierTwo) ApplyQuickStrike(attacker, defender);
else ApplyPierce(attacker, defender);
break;
case SkillName.Macing:
ApplyConcussion(attacker, defender);
if (useTierTwo) ApplyShatter(attacker, defender);
else ApplyConcussion(attacker, defender);
break;
}
}
// --- TIER 1 ABILITIES ---
private static void ApplyBleed(Mobile attacker, Mobile defender)
{
defender.PlaySound(0x133);
@ -45,6 +77,7 @@ namespace Server.Combat
BleedAttack.BeginBleed(defender, attacker);
}
// Note: If you see double messages here, it's because BeginBleed() natively sends its own message too!
attacker.SendMessage(0x3F, "Your blade inflicts a bleeding wound!");
defender.SendMessage(0x22, "You are bleeding severely!");
}
@ -80,5 +113,59 @@ namespace Server.Combat
attacker.SendMessage(0x35, $"Your crushing blow drained {manaDrain} mana and {stamDrain} stamina!");
}
// --- TIER 2 ABILITIES ---
private static void ApplySever(Mobile attacker, Mobile defender)
{
defender.PlaySound(0x23B);
defender.FixedParticles(0x3779, 1, 15, 9905, 32, 7, EffectLayer.Head);
// Heavy physical burst damage
int damage = Utility.RandomMinMax(15, 25);
AOS.Damage(defender, attacker, damage, 100, 0, 0, 0, 0);
attacker.SendMessage(0x21, $"You brutally sever your opponent, dealing {damage} extra damage!");
defender.SendMessage(0x22, "A brutal strike tears into your flesh!");
}
private static void ApplyHeadshot(Mobile attacker, Mobile defender)
{
defender.PlaySound(0x11C); // Heavy thud
defender.FixedParticles(0x373A, 1, 15, 9905, 33, 7, EffectLayer.Head);
// Extreme armor-ignoring damage
int directDamage = Utility.RandomMinMax(20, 30);
AOS.Damage(defender, attacker, directDamage, true, 100, 0, 0, 0, 0, 0);
attacker.SendMessage(0x481, $"HEADSHOT! You inflict {directDamage} direct damage!");
defender.SendMessage(0x22, "An arrow strikes your head, bypassing your armor entirely!");
}
private static void ApplyQuickStrike(Mobile attacker, Mobile defender)
{
defender.PlaySound(0x2B1); // Fast swish sound
defender.FixedParticles(0x37B9, 1, 14, 9905, 32, 7, EffectLayer.Waist);
// Instantly resets the weapon swing timer so the next attack happens immediately
attacker.NextCombatTime = Core.TickCount;
attacker.SendMessage(0x42, "You strike with blinding speed, granting an immediate follow-up attack!");
defender.SendMessage(0x22, "Your opponent strikes with blinding speed!");
}
private static void ApplyShatter(Mobile attacker, Mobile defender)
{
defender.PlaySound(0x308); // Breaking sound
defender.FixedParticles(0x375A, 1, 15, 9905, 12, 7, EffectLayer.Waist);
// Halves current stamina and deals moderate crushing damage
defender.Stam /= 2;
int damage = Utility.RandomMinMax(10, 20);
AOS.Damage(defender, attacker, damage, 100, 0, 0, 0, 0);
attacker.SendMessage(0x35, "You shatter your opponent's guard, decimating their stamina!");
defender.SendMessage(0x22, "A shattering blow knocks the wind out of you!");
}
}
}

View file

@ -0,0 +1,84 @@
using System;
using Server.Mobiles;
using Server.Items;
namespace Server.Combat
{
public static class InnateWeaponAbilities
{
public static void CheckProc(Mobile attacker, Mobile defender, BaseWeapon weapon)
{
if (attacker == null || defender == null || weapon == null || !defender.Alive)
return;
double procChance = 0.10;
if (Utility.RandomDouble() >= procChance)
return;
switch (weapon.Skill)
{
case SkillName.Swords:
ApplyBleed(attacker, defender);
break;
case SkillName.Archery:
ApplyHinder(attacker, defender);
break;
case SkillName.Fencing:
ApplyPierce(attacker, defender);
break;
case SkillName.Macing:
ApplyConcussion(attacker, defender);
break;
}
}
private static void ApplyBleed(Mobile attacker, Mobile defender)
{
defender.PlaySound(0x133);
if (typeof(BaseWeapon).Assembly.GetType("Server.Items.BleedAttack") != null)
{
BleedAttack.BeginBleed(defender, attacker);
}
attacker.SendMessage(0x3F, "Your blade inflicts a bleeding wound!");
defender.SendMessage(0x22, "You are bleeding severely!");
}
private static void ApplyHinder(Mobile attacker, Mobile defender)
{
defender.PlaySound(0x204);
defender.FixedEffect(0x376A, 9, 32);
defender.Freeze(TimeSpan.FromSeconds(3.0));
attacker.SendMessage(0x481, "Your arrow pins the target in place!");
defender.SendMessage(0x22, "You have been pinned and cannot move!");
}
private static void ApplyPierce(Mobile attacker, Mobile defender)
{
defender.PlaySound(0x56);
int directDamage = (int)(attacker.Skills[SkillName.Tactics].Value * 0.2) + Utility.RandomMinMax(2, 5);
AOS.Damage(defender, attacker, directDamage, true, 100, 0, 0, 0, 0, 0);
attacker.SendMessage(0x42, $"Your precise thrust pierces through their armor for {directDamage} direct damage!");
defender.SendMessage(0x22, $"You are pierced for {directDamage} direct damage, bypassing your armor!");
}
private static void ApplyConcussion(Mobile attacker, Mobile defender)
{
defender.PlaySound(0x213);
int manaDrain = Utility.RandomMinMax(10, 15);
int stamDrain = Utility.RandomMinMax(10, 15);
defender.Mana -= manaDrain;
defender.Stam -= stamDrain;
attacker.SendMessage(0x35, $"Your crushing blow drained {manaDrain} mana and {stamDrain} stamina!");
}
}
}