diff --git a/Distribution/Data/Spawns/Underworld/NewPlayerDungeon_Level_1.json b/Distribution/Data/Spawns/Underworld/NewPlayerDungeon_Level_1.json index 97a1a82fd..4e18e4770 100644 --- a/Distribution/Data/Spawns/Underworld/NewPlayerDungeon_Level_1.json +++ b/Distribution/Data/Spawns/Underworld/NewPlayerDungeon_Level_1.json @@ -254,8 +254,8 @@ "count": 1, "minDelay": "00:25:00", "maxDelay": "00:35:00", - "homeRange": 4, - "walkingRange": 4, + "homeRange": 1, + "walkingRange": 5, "entries": [ { "name": "cowardlybrigand", "probability": 25, "maxCount": 1 }, { "name": "mangyratman", "probability": 25, "maxCount": 1 }, diff --git a/Projects/UOContent/Items/Skill Items/Misc/Bandage.cs b/Projects/UOContent/Items/Skill Items/Misc/Bandage.cs index 2ad7722b9..370ce1e39 100644 --- a/Projects/UOContent/Items/Skill Items/Misc/Bandage.cs +++ b/Projects/UOContent/Items/Skill Items/Misc/Bandage.cs @@ -27,13 +27,8 @@ public partial class Bandage : Item, IDyable public virtual bool Dye(Mobile from, DyeTub sender) { - if (Deleted) - { - return false; - } - + if (Deleted) return false; Hue = sender.DyedHue; - return true; } @@ -42,9 +37,7 @@ public partial class Bandage : Item, IDyable if (from.InRange(GetWorldLocation(), Range)) { from.RevealingAction(); - from.SendLocalizedMessage(500948); // Who will you use the bandages on? - from.Target = new InternalTarget(this); } else @@ -55,10 +48,7 @@ public partial class Bandage : Item, IDyable public static void BandageTargetRequest(Mobile from, Item item, Mobile target) { - if (item is not Bandage b || b.Deleted) - { - return; - } + if (item is not Bandage b || b.Deleted) return; if (!from.InRange(b.GetWorldLocation(), Range)) { @@ -87,10 +77,7 @@ public partial class Bandage : Item, IDyable protected override void OnTarget(Mobile from, object targeted) { - if (_bandage.Deleted) - { - return; - } + if (_bandage.Deleted) return; if (targeted is Mobile mobile) { @@ -105,17 +92,12 @@ public partial class Bandage : Item, IDyable { from.SendLocalizedMessage(500295); // You are too far away to do that. } - return; } if (targeted is PlagueBeastInnard innard) { - if (innard.OnBandage(from)) - { - _bandage.Consume(); - } - + if (innard.OnBandage(from)) _bandage.Consume(); return; } @@ -126,10 +108,7 @@ public partial class Bandage : Item, IDyable { if (targeted is PlagueBeastInnard innard) { - if (innard.OnBandage(from)) - { - _bandage.Consume(); - } + if (innard.OnBandage(from)) _bandage.Consume(); } else { @@ -143,22 +122,76 @@ public class BandageContext : Timer { private static readonly Dictionary _table = new(); - public BandageContext(Mobile healer, Mobile patient, TimeSpan delay) : base(delay) + // --- MEMENTO DEFERRED HEALING VARIABLES --- + private int _currentTicks; + private readonly int _maxTicks; + private readonly int _tickHealAmount; + private int _finalHealAmount; + + public BandageContext(Mobile healer, Mobile patient, TimeSpan delay) + // Run the timer every 250 milliseconds instead of waiting the full duration + : base(TimeSpan.FromMilliseconds(250), TimeSpan.FromMilliseconds(250)) { Healer = healer; Patient = patient; + + // Calculate how many 250ms ticks fit into the total delay + _maxTicks = (int)(delay.TotalMilliseconds / 250); + + // Pre-calculate standard heal amounts for partial ticking + var primarySkill = GetPrimarySkill(patient); + var secondarySkill = GetSecondarySkill(patient); + var healingVal = healer.Skills[primarySkill].Value; + var anatomyVal = healer.Skills[secondarySkill].Value; + + double min, max; + if (Core.AOS) + { + min = anatomyVal / 8.0 + healingVal / 5.0 + 4.0; + max = anatomyVal / 6.0 + healingVal / 2.5 + 4.0; + } + else + { + min = anatomyVal / 5.0 + healingVal / 5.0 + 3.0; + max = anatomyVal / 5.0 + healingVal / 2.0 + 10.0; + } + + var toHeal = min + Utility.RandomDouble() * (max - min); + + if (patient.Body.IsMonster || patient.Body.IsAnimal) + { + toHeal += patient.HitsMax / 100.0; + } + + _finalHealAmount = (int)toHeal; + + int partialHealCount = _maxTicks / 4; // 4 ticks = 1 second + + if (partialHealCount > 0) + { + // Baseline as 33% of the total healing distributed across partial ticks, capped at 10% per tick + _tickHealAmount = Math.Min( + (int)(0.33 * _finalHealAmount) / partialHealCount, + (int)(0.1 * _finalHealAmount) + ); + + // Deduct what will be healed over time from the final burst + _finalHealAmount -= _tickHealAmount * partialHealCount; + } } public Mobile Healer { get; } - public Mobile Patient { get; } - public int Slips { get; set; } public void Slip() { Healer.SendLocalizedMessage(500961); // Your fingers slip! ++Slips; + + // --- SKILL GAIN: On Interruption --- + Healer.CheckSkill(GetSecondarySkill(Patient), 0.0, 120.0); + Healer.CheckSkill(GetPrimarySkill(Patient), 0.0, 120.0); } public void StopHeal() @@ -179,25 +212,64 @@ public class BandageContext : Timer public static SkillName GetPrimarySkill(Mobile m) { - if (!m.Player && (m.Body.IsMonster || m.Body.IsAnimal)) - { - return SkillName.Veterinary; - } - + if (!m.Player && (m.Body.IsMonster || m.Body.IsAnimal)) return SkillName.Veterinary; return SkillName.Healing; } public static SkillName GetSecondarySkill(Mobile m) { - if (!m.Player && (m.Body.IsMonster || m.Body.IsAnimal)) - { - return SkillName.AnimalLore; - } - + if (!m.Player && (m.Body.IsMonster || m.Body.IsAnimal)) return SkillName.AnimalLore; return SkillName.Anatomy; } + // --- NEW: TICKING LOGIC (MEMENTO STYLE) --- protected override void OnTick() + { + _currentTicks++; + + bool isComplete = _currentTicks >= _maxTicks; + bool canCheck = _currentTicks % 4 == 0; // Check every 1 second (4 ticks of 250ms) + + if (isComplete) + { + // Time is up! Run the standard ModernUO final heal/resurrect/cure logic + DoFinalHeal(); + return; + } + + if (canCheck) + { + int totalSeconds = _currentTicks / 4; + + // --- SKILL GAIN: Every 3 Seconds --- + if (totalSeconds % 3 == 0) + { + Healer.CheckSkill(GetSecondarySkill(Patient), 0.0, 120.0); + Healer.CheckSkill(GetPrimarySkill(Patient), 0.0, 120.0); + } + + // --- PARTIAL HEALS (HoT) --- + // Only tick HP if they are alive, unpoisoned, not bleeding, and not mortaled + if (Patient.Alive && !Patient.Poisoned && !BleedAttack.IsBleeding(Patient) && !MortalStrike.IsWounded(Patient)) + { + if (_tickHealAmount > 0) + { + var primarySkill = GetPrimarySkill(Patient); + var healing = Healer.Skills[primarySkill].Value; + var chance = (healing + 10.0) / 100.0 - Slips * 0.02; + + // Still requires a successful chance roll to land the partial tick + if (chance > Utility.RandomDouble()) + { + Patient.Heal(_tickHealAmount, Healer, false); + } + } + } + } + } + + // --- THE ORIGINAL MODERNUO ONTICK LOGIC --- + private void DoFinalHeal() { StopHeal(); @@ -227,14 +299,13 @@ public class BandageContext : Timer { if (Patient.Map?.CanFit(Patient.Location, 16, false, false) != true) { - Healer.SendLocalizedMessage(501042); // Target can not be resurrected at that location. - Patient.SendLocalizedMessage(502391); // Thou can not be resurrected there! + Healer.SendLocalizedMessage(501042); + Patient.SendLocalizedMessage(502391); return; } if (Patient.Region?.IsPartOf("Khaldun") == true) { - // The veil of death in this area is too strong and resists thy efforts to restore life. Healer.SendLocalizedMessage(1010395); return; } @@ -244,18 +315,13 @@ public class BandageContext : Timer var chance = (healing - 68.0) / 50.0 - Slips * 0.02; checkSkills = healing >= 80.0 && anatomy >= 80.0; - // TODO: Dbl check doesn't check for faction of the horse here? if (!(checkSkills && chance > Utility.RandomDouble()) && (!Core.SE || petPatient is not FactionWarHorse || petPatient.ControlMaster != Healer)) { if (petPatient?.IsDeadPet == true) - { - Healer.SendLocalizedMessage(503256); // You fail to resurrect the creature. - } + Healer.SendLocalizedMessage(503256); else - { - Healer.SendLocalizedMessage(500966); // You are unable to resurrect your patient. - } + Healer.SendLocalizedMessage(500966); return; } @@ -273,7 +339,6 @@ public class BandageContext : Timer if (master != null && Healer == master) { petPatient.ResurrectPet(); - for (var i = 0; i < petPatient.Skills.Length; ++i) { petPatient.Skills[i].Base -= 0.1; @@ -281,35 +346,27 @@ public class BandageContext : Timer } else if (master?.InRange(petPatient, 3) == true) { - healerNumber = 503255; // You are able to resurrect the creature. - + healerNumber = 503255; master.SendGump(new PetResurrectGump(Healer, petPatient)); } else { var found = false; - var friends = petPatient.Friends; for (var i = 0; i < friends?.Count; ++i) { var friend = friends[i]; - if (friend.InRange(petPatient, 3)) { - healerNumber = 503255; // You are able to resurrect the creature. - + healerNumber = 503255; friend.SendGump(new PetResurrectGump(Healer, petPatient)); - found = true; break; } } - if (!found) - { - healerNumber = 1049670; // The pet's owner must be nearby to attempt resurrection. - } + if (!found) healerNumber = 1049670; } } else @@ -334,8 +391,8 @@ public class BandageContext : Timer if (Patient.CurePoison(Healer)) { - healerNumber = Healer == Patient ? -1 : 1010058; // You have cured the target of all poisons. - patientNumber = 1010059; // You have been cured of all poisons. + healerNumber = Healer == Patient ? -1 : 1010058; + patientNumber = 1010059; } else { @@ -347,9 +404,8 @@ public class BandageContext : Timer { if (BleedAttack.IsBleeding(Patient)) { - Healer.SendLocalizedMessage(1060088); // You bind the wound and stop the bleeding - Patient.SendLocalizedMessage(1060167); // The bleeding wounds have healed, you are no longer bleeding! - + Healer.SendLocalizedMessage(1060088); + Patient.SendLocalizedMessage(1060167); BleedAttack.EndBleed(Patient, false); return; } @@ -357,13 +413,9 @@ public class BandageContext : Timer if (MortalStrike.IsWounded(Patient)) { if (Healer == Patient) - { - Healer.SendLocalizedMessage(1005000); // You cannot heal yourself in your current state. - } + Healer.SendLocalizedMessage(1005000); else - { - Healer.SendLocalizedMessage(1010398); // You cannot heal that target in their current state. - } + Healer.SendLocalizedMessage(1010398); return; } @@ -385,42 +437,25 @@ public class BandageContext : Timer { healerNumber = 500969; // You finish applying the bandages. - double min, max; + // --- INTEGRATED MEMENTO FINAL HEAL AMOUNT --- + double finalHeal = _finalHealAmount; if (Core.AOS) { - min = anatomy / 8.0 + healing / 5.0 + 4.0; - max = anatomy / 6.0 + healing / 2.5 + 4.0; + finalHeal -= finalHeal * Slips * 0.35; // Apply slip reduction to the remaining burst } else { - min = anatomy / 5.0 + healing / 5.0 + 3.0; - max = anatomy / 5.0 + healing / 2.0 + 10.0; + finalHeal -= Slips * 4; } - var toHeal = min + Utility.RandomDouble() * (max - min); - - if (Patient.Body.IsMonster || Patient.Body.IsAnimal) + if (finalHeal < 1) { - toHeal += Patient.HitsMax / 100.0; - } - - if (Core.AOS) - { - toHeal -= toHeal * Slips * 0.35; // TODO: Verify algorithm - } - else - { - toHeal -= Slips * 4; - } - - if (toHeal < 1) - { - toHeal = 1; + finalHeal = 1; healerNumber = 500968; // You apply the bandages, but they barely help. } - Patient.Heal((int)toHeal, Healer, false); + Patient.Heal((int)finalHeal, Healer, false); } else { @@ -429,21 +464,11 @@ public class BandageContext : Timer } } - if (healerNumber != -1) - { - Healer.SendLocalizedMessage(healerNumber); - } - - if (patientNumber != -1) - { - Patient.SendLocalizedMessage(patientNumber); - } - - if (playSound) - { - Patient.PlaySound(0x57); - } + if (healerNumber != -1) Healer.SendLocalizedMessage(healerNumber); + if (patientNumber != -1) Patient.SendLocalizedMessage(patientNumber); + if (playSound) Patient.PlaySound(0x57); + // --- SKILL GAIN: Final Completion --- if (checkSkills) { Healer.CheckSkill(secondarySkill, 0.0, 120.0); @@ -461,26 +486,15 @@ public class BandageContext : Timer var creature = patient as BaseCreature; if (patient is Golem) - { - healer.SendLocalizedMessage(500970); // Bandages cannot be used on that. - } + healer.SendLocalizedMessage(500970); else if (creature?.IsAnimatedDead == true) - { - healer.SendLocalizedMessage(500951); // You cannot heal that. - } - else if (!patient.Poisoned && patient.Hits == patient.HitsMax && !BleedAttack.IsBleeding(patient) && - creature?.IsDeadPet != true) - { - healer.SendLocalizedMessage(500955); // That being is not damaged! - } + healer.SendLocalizedMessage(500951); + else if (!patient.Poisoned && patient.Hits == patient.HitsMax && !BleedAttack.IsBleeding(patient) && creature?.IsDeadPet != true) + healer.SendLocalizedMessage(500955); else if (!patient.Alive && patient.Map?.CanFit(patient.Location, 16, false, false) != true) - { - healer.SendLocalizedMessage(501042); // Target cannot be resurrected at that location. - } + healer.SendLocalizedMessage(501042); else if ((healer as PlayerMobile)?.Young == true && (patient as PlayerMobile)?.Young == false) - { - healer.SendLocalizedMessage(500952); // As a young player, you may not use beneficial skills on older players. - } + healer.SendLocalizedMessage(500952); else if (healer.CanBeBeneficial(patient, true, true)) { healer.DoBeneficial(patient); @@ -494,13 +508,9 @@ public class BandageContext : Timer if (onSelf) { if (Core.AOS) - { - seconds = 5.0 + 0.5 * ((double)(120 - dex) / 10); // TODO: Verify algorithm - } + seconds = 5.0 + 0.5 * ((double)(120 - dex) / 10); else - { seconds = 9.4 + 0.6 * ((double)(120 - dex) / 10); - } } else if (Core.AOS && GetPrimarySkill(patient) == SkillName.Veterinary) { @@ -509,46 +519,39 @@ public class BandageContext : Timer else if (Core.AOS) { if (dex < 204) - { seconds = 3.2 - Math.Sin((double)dex / 130) * 2.5 + resDelay; - } else - { seconds = 0.7 + resDelay; - } } else if (dex >= 100) - { seconds = 3.0 + resDelay; - } else if (dex >= 40) - { seconds = 4.0 + resDelay; - } else - { seconds = 5.0 + resDelay; - } var context = GetContext(healer); - context?.StopHeal(); + seconds *= 1000; context = new BandageContext(healer, patient, TimeSpan.FromMilliseconds(seconds)); _table[healer] = context; context.Start(); - if (!onSelf) - { - patient.SendLocalizedMessage(1008078, false, healer.Name); // : Attempting to heal you. - } + if (!onSelf) patient.SendLocalizedMessage(1008078, false, healer.Name); + if (healer is PlayerMobile pm) { pm.AddBuff(new BuffInfo(BuffIcon.Healing, 1042971, TimeSpan.FromMilliseconds(seconds), "Healing")); } healer.SendLocalizedMessage(500956); // You begin applying the bandages. + + // --- SKILL GAIN: On Bandage Start --- + healer.CheckSkill(GetSecondarySkill(patient), 0.0, 120.0); + healer.CheckSkill(GetPrimarySkill(patient), 0.0, 120.0); + return context; } diff --git a/Projects/UOContent/Items/Skill Items/Misc/Bandage.cs.ORIG b/Projects/UOContent/Items/Skill Items/Misc/Bandage.cs.ORIG new file mode 100644 index 000000000..2ad7722b9 --- /dev/null +++ b/Projects/UOContent/Items/Skill Items/Misc/Bandage.cs.ORIG @@ -0,0 +1,557 @@ +using System; +using System.Collections.Generic; +using ModernUO.Serialization; +using Server.Engines.ConPVP; +using Server.Factions; +using Server.Gumps; +using Server.Misc; +using Server.Mobiles; +using Server.Targeting; +using Server.Engines.BuffIcons; + +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class Bandage : Item, IDyable +{ + public static int Range = Core.AOS ? 2 : 1; + + [Constructible] + public Bandage(int amount = 1) : base(0xE21) + { + Stackable = true; + Amount = amount; + } + + public override double DefaultWeight => 0.1; + + public virtual bool Dye(Mobile from, DyeTub sender) + { + if (Deleted) + { + return false; + } + + Hue = sender.DyedHue; + + return true; + } + + public override void OnDoubleClick(Mobile from) + { + if (from.InRange(GetWorldLocation(), Range)) + { + from.RevealingAction(); + + from.SendLocalizedMessage(500948); // Who will you use the bandages on? + + from.Target = new InternalTarget(this); + } + else + { + from.SendLocalizedMessage(500295); // You are too far away to do that. + } + } + + public static void BandageTargetRequest(Mobile from, Item item, Mobile target) + { + if (item is not Bandage b || b.Deleted) + { + return; + } + + if (!from.InRange(b.GetWorldLocation(), Range)) + { + from.SendLocalizedMessage(500295); // You are too far away to do that. + return; + } + + if (from.Target != null) + { + Target.Cancel(from); + from.Target = null; + } + + from.RevealingAction(); + from.SendLocalizedMessage(500948); // Who will you use the bandages on? + + new InternalTarget(b).Invoke(from, target); + } + + private class InternalTarget : Target + { + private readonly Bandage _bandage; + + public InternalTarget(Bandage bandage) : base(Bandage.Range, false, TargetFlags.Beneficial) => + _bandage = bandage; + + protected override void OnTarget(Mobile from, object targeted) + { + if (_bandage.Deleted) + { + return; + } + + if (targeted is Mobile mobile) + { + if (from.InRange(_bandage.GetWorldLocation(), Bandage.Range)) + { + if (!(BandageContext.BeginHeal(from, mobile) == null || DuelContext.IsFreeConsume(from))) + { + _bandage.Consume(); + } + } + else + { + from.SendLocalizedMessage(500295); // You are too far away to do that. + } + + return; + } + + if (targeted is PlagueBeastInnard innard) + { + if (innard.OnBandage(from)) + { + _bandage.Consume(); + } + + return; + } + + from.SendLocalizedMessage(500970); // Bandages can not be used on that. + } + + protected override void OnNonlocalTarget(Mobile from, object targeted) + { + if (targeted is PlagueBeastInnard innard) + { + if (innard.OnBandage(from)) + { + _bandage.Consume(); + } + } + else + { + base.OnNonlocalTarget(from, targeted); + } + } + } +} + +public class BandageContext : Timer +{ + private static readonly Dictionary _table = new(); + + public BandageContext(Mobile healer, Mobile patient, TimeSpan delay) : base(delay) + { + Healer = healer; + Patient = patient; + } + + public Mobile Healer { get; } + + public Mobile Patient { get; } + + public int Slips { get; set; } + + public void Slip() + { + Healer.SendLocalizedMessage(500961); // Your fingers slip! + ++Slips; + } + + public void StopHeal() + { + if (Healer is PlayerMobile pm) + { + pm.RemoveBuff(BuffIcon.Healing); + } + _table.Remove(Healer); + Stop(); + } + + public static BandageContext GetContext(Mobile healer) + { + _table.TryGetValue(healer, out var bc); + return bc; + } + + public static SkillName GetPrimarySkill(Mobile m) + { + if (!m.Player && (m.Body.IsMonster || m.Body.IsAnimal)) + { + return SkillName.Veterinary; + } + + return SkillName.Healing; + } + + public static SkillName GetSecondarySkill(Mobile m) + { + if (!m.Player && (m.Body.IsMonster || m.Body.IsAnimal)) + { + return SkillName.AnimalLore; + } + + return SkillName.Anatomy; + } + + protected override void OnTick() + { + StopHeal(); + + int healerNumber; + int patientNumber; + var playSound = true; + bool checkSkills; + + var primarySkill = GetPrimarySkill(Patient); + var secondarySkill = GetSecondarySkill(Patient); + + var petPatient = Patient as BaseCreature; + + if (!Healer.Alive) + { + Healer.SendLocalizedMessage(500962); // You were unable to finish your work before you died. + return; + } + + if (!Healer.InRange(Patient, Bandage.Range)) + { + Healer.SendLocalizedMessage(500963); // You did not stay close enough to heal your target. + return; + } + + if (!Patient.Alive || petPatient?.IsDeadPet == true) + { + if (Patient.Map?.CanFit(Patient.Location, 16, false, false) != true) + { + Healer.SendLocalizedMessage(501042); // Target can not be resurrected at that location. + Patient.SendLocalizedMessage(502391); // Thou can not be resurrected there! + return; + } + + if (Patient.Region?.IsPartOf("Khaldun") == true) + { + // The veil of death in this area is too strong and resists thy efforts to restore life. + Healer.SendLocalizedMessage(1010395); + return; + } + + var healing = Healer.Skills[primarySkill].Value; + var anatomy = Healer.Skills[secondarySkill].Value; + var chance = (healing - 68.0) / 50.0 - Slips * 0.02; + checkSkills = healing >= 80.0 && anatomy >= 80.0; + + // TODO: Dbl check doesn't check for faction of the horse here? + if (!(checkSkills && chance > Utility.RandomDouble()) + && (!Core.SE || petPatient is not FactionWarHorse || petPatient.ControlMaster != Healer)) + { + if (petPatient?.IsDeadPet == true) + { + Healer.SendLocalizedMessage(503256); // You fail to resurrect the creature. + } + else + { + Healer.SendLocalizedMessage(500966); // You are unable to resurrect your patient. + } + + return; + } + + healerNumber = 500965; // You are able to resurrect your patient. + patientNumber = -1; + + Patient.PlaySound(0x214); + Patient.FixedEffect(0x376A, 10, 16); + + if (petPatient?.IsDeadPet == true) + { + var master = petPatient.ControlMaster; + + if (master != null && Healer == master) + { + petPatient.ResurrectPet(); + + for (var i = 0; i < petPatient.Skills.Length; ++i) + { + petPatient.Skills[i].Base -= 0.1; + } + } + else if (master?.InRange(petPatient, 3) == true) + { + healerNumber = 503255; // You are able to resurrect the creature. + + master.SendGump(new PetResurrectGump(Healer, petPatient)); + } + else + { + var found = false; + + var friends = petPatient.Friends; + + for (var i = 0; i < friends?.Count; ++i) + { + var friend = friends[i]; + + if (friend.InRange(petPatient, 3)) + { + healerNumber = 503255; // You are able to resurrect the creature. + + friend.SendGump(new PetResurrectGump(Healer, petPatient)); + + found = true; + break; + } + } + + if (!found) + { + healerNumber = 1049670; // The pet's owner must be nearby to attempt resurrection. + } + } + } + else + { + Patient.SendGump(new ResurrectGump(Healer)); + } + } + else if (Patient.Poisoned) + { + Healer.SendLocalizedMessage(500969); // You finish applying the bandages. + + var healing = Healer.Skills[primarySkill].Value; + var anatomy = Healer.Skills[secondarySkill].Value; + var chance = (healing - 30.0) / 50.0 - Patient.Poison.Level * 0.1 - Slips * 0.02; + checkSkills = healing >= 60.0 && anatomy >= 60.0; + + if (!(checkSkills && chance > Utility.RandomDouble())) + { + Healer.SendLocalizedMessage(1010060); // You have failed to cure your target! + return; + } + + if (Patient.CurePoison(Healer)) + { + healerNumber = Healer == Patient ? -1 : 1010058; // You have cured the target of all poisons. + patientNumber = 1010059; // You have been cured of all poisons. + } + else + { + healerNumber = -1; + patientNumber = -1; + } + } + else + { + if (BleedAttack.IsBleeding(Patient)) + { + Healer.SendLocalizedMessage(1060088); // You bind the wound and stop the bleeding + Patient.SendLocalizedMessage(1060167); // The bleeding wounds have healed, you are no longer bleeding! + + BleedAttack.EndBleed(Patient, false); + return; + } + + if (MortalStrike.IsWounded(Patient)) + { + if (Healer == Patient) + { + Healer.SendLocalizedMessage(1005000); // You cannot heal yourself in your current state. + } + else + { + Healer.SendLocalizedMessage(1010398); // You cannot heal that target in their current state. + } + + return; + } + + if (Patient.Hits == Patient.HitsMax) + { + Healer.SendLocalizedMessage(1010395); // You heal what little damage your patient had. + return; + } + + checkSkills = true; + patientNumber = -1; + + var healing = Healer.Skills[primarySkill].Value; + var anatomy = Healer.Skills[secondarySkill].Value; + var chance = (healing + 10.0) / 100.0 - Slips * 0.02; + + if (chance > Utility.RandomDouble()) + { + healerNumber = 500969; // You finish applying the bandages. + + double min, max; + + if (Core.AOS) + { + min = anatomy / 8.0 + healing / 5.0 + 4.0; + max = anatomy / 6.0 + healing / 2.5 + 4.0; + } + else + { + min = anatomy / 5.0 + healing / 5.0 + 3.0; + max = anatomy / 5.0 + healing / 2.0 + 10.0; + } + + var toHeal = min + Utility.RandomDouble() * (max - min); + + if (Patient.Body.IsMonster || Patient.Body.IsAnimal) + { + toHeal += Patient.HitsMax / 100.0; + } + + if (Core.AOS) + { + toHeal -= toHeal * Slips * 0.35; // TODO: Verify algorithm + } + else + { + toHeal -= Slips * 4; + } + + if (toHeal < 1) + { + toHeal = 1; + healerNumber = 500968; // You apply the bandages, but they barely help. + } + + Patient.Heal((int)toHeal, Healer, false); + } + else + { + healerNumber = 500968; // You apply the bandages, but they barely help. + playSound = false; + } + } + + if (healerNumber != -1) + { + Healer.SendLocalizedMessage(healerNumber); + } + + if (patientNumber != -1) + { + Patient.SendLocalizedMessage(patientNumber); + } + + if (playSound) + { + Patient.PlaySound(0x57); + } + + if (checkSkills) + { + Healer.CheckSkill(secondarySkill, 0.0, 120.0); + Healer.CheckSkill(primarySkill, 0.0, 120.0); + } + + if (Healer != Patient && Patient.Karma > 0) + { + Titles.AwardKarma(Healer, Patient.Karma / 5, true); + } + } + + public static BandageContext BeginHeal(Mobile healer, Mobile patient) + { + var creature = patient as BaseCreature; + + if (patient is Golem) + { + healer.SendLocalizedMessage(500970); // Bandages cannot be used on that. + } + else if (creature?.IsAnimatedDead == true) + { + healer.SendLocalizedMessage(500951); // You cannot heal that. + } + else if (!patient.Poisoned && patient.Hits == patient.HitsMax && !BleedAttack.IsBleeding(patient) && + creature?.IsDeadPet != true) + { + healer.SendLocalizedMessage(500955); // That being is not damaged! + } + else if (!patient.Alive && patient.Map?.CanFit(patient.Location, 16, false, false) != true) + { + healer.SendLocalizedMessage(501042); // Target cannot be resurrected at that location. + } + else if ((healer as PlayerMobile)?.Young == true && (patient as PlayerMobile)?.Young == false) + { + healer.SendLocalizedMessage(500952); // As a young player, you may not use beneficial skills on older players. + } + else if (healer.CanBeBeneficial(patient, true, true)) + { + healer.DoBeneficial(patient); + + var onSelf = healer == patient; + var dex = healer.Dex; + + double seconds; + var resDelay = patient.Alive ? 0.0 : 5.0; + + if (onSelf) + { + if (Core.AOS) + { + seconds = 5.0 + 0.5 * ((double)(120 - dex) / 10); // TODO: Verify algorithm + } + else + { + seconds = 9.4 + 0.6 * ((double)(120 - dex) / 10); + } + } + else if (Core.AOS && GetPrimarySkill(patient) == SkillName.Veterinary) + { + seconds = 2.0; + } + else if (Core.AOS) + { + if (dex < 204) + { + seconds = 3.2 - Math.Sin((double)dex / 130) * 2.5 + resDelay; + } + else + { + seconds = 0.7 + resDelay; + } + } + else if (dex >= 100) + { + seconds = 3.0 + resDelay; + } + else if (dex >= 40) + { + seconds = 4.0 + resDelay; + } + else + { + seconds = 5.0 + resDelay; + } + + var context = GetContext(healer); + + context?.StopHeal(); + seconds *= 1000; + + context = new BandageContext(healer, patient, TimeSpan.FromMilliseconds(seconds)); + _table[healer] = context; + context.Start(); + + if (!onSelf) + { + patient.SendLocalizedMessage(1008078, false, healer.Name); // : Attempting to heal you. + } + if (healer is PlayerMobile pm) + { + pm.AddBuff(new BuffInfo(BuffIcon.Healing, 1042971, TimeSpan.FromMilliseconds(seconds), "Healing")); + } + + healer.SendLocalizedMessage(500956); // You begin applying the bandages. + return context; + } + + return null; + } +} diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/GreaterMongbat.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/GreaterMongbat.cs index e00c86a70..4f8acd731 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/GreaterMongbat.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/GreaterMongbat.cs @@ -30,7 +30,7 @@ namespace Server.Mobiles SetSkill(SkillName.Wrestling, 20.1, 35.0); Fame = 450; - Karma = -450; + Karma = 100; VirtualArmor = 10; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Mongbat.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Mongbat.cs index dfe7873c0..fd870dbd0 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Mongbat.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Mongbat.cs @@ -29,7 +29,7 @@ namespace Server.Mobiles SetSkill(SkillName.Wrestling, 5.1, 10.0); Fame = 150; - Karma = -150; + Karma = 0; VirtualArmor = 10; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/StrongMongbat.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/StrongMongbat.cs index 341277732..b87f4f947 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/StrongMongbat.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/StrongMongbat.cs @@ -29,7 +29,7 @@ namespace Server.Mobiles SetSkill(SkillName.Wrestling, 20.1, 35.0); Fame = 150; - Karma = -150; + Karma = 50; VirtualArmor = 10; diff --git a/Projects/UOContent/Skills/SkillCheck.cs b/Projects/UOContent/Skills/SkillCheck.cs index 45b518861..721f400ac 100644 --- a/Projects/UOContent/Skills/SkillCheck.cs +++ b/Projects/UOContent/Skills/SkillCheck.cs @@ -152,6 +152,10 @@ public static class SkillCheck { gc *= 2.0; } + else if (from.Region is GreetingDungeonRegion) + { + gc *= 1.5; + } if (gc >= Utility.RandomDouble()) {