Compare commits

...

18 Commits

Author SHA1 Message Date
Alex King 7d194083aa [Release] 22.41.0 (#3939)
### Bug

* DI Buff Fade ([#3919](https://github.com/EQEmu/Server/pull/3919)) @fryguy503 2024-01-08
* NPCs will now only proc on hit ([#3913](https://github.com/EQEmu/Server/pull/3913)) @fryguy503 2024-01-08
* Pets should not ignore Z axis ([#3912](https://github.com/EQEmu/Server/pull/3912)) @fryguy503 2024-01-08

### Fixes

* Disciplines should show when someone casts them. ([#3901](https://github.com/EQEmu/Server/pull/3901)) @fryguy503 2024-01-08
* Fix Typo in Character Skills loading ([#3937](https://github.com/EQEmu/Server/pull/3937)) @Kinglykrab 2024-01-09
* Fix for HasLockoutByCharacterID ([#3927](https://github.com/EQEmu/Server/pull/3927)) @fryguy503 2024-01-08
* Harm Touch, Improved Harm Touch, and Unholy Touch ([#3904](https://github.com/EQEmu/Server/pull/3904)) @fryguy503 2024-01-08
* Legacy Manaburn should have hard cap. ([#3905](https://github.com/EQEmu/Server/pull/3905)) @fryguy503 2024-01-08
* TGB - Added logic to stop bard errors on group songs. ([#3906](https://github.com/EQEmu/Server/pull/3906)) @fryguy503 2024-01-08
* World Shutdown Filter ([#3930](https://github.com/EQEmu/Server/pull/3930)) @fryguy503 2024-01-08

### Info

* Adding textual feedback when trying to sell alt items back to … ([#3917](https://github.com/EQEmu/Server/pull/3917)) @fryguy503 2024-01-08

### Rules

* Backstab Damage Modifier ([#3908](https://github.com/EQEmu/Server/pull/3908)) @fryguy503 2024-01-08
* Classic Tradeskill Skill Clamp ([#3914](https://github.com/EQEmu/Server/pull/3914)) @fryguy503 2024-01-08
* Classic Triple Attack ([#3903](https://github.com/EQEmu/Server/pull/3903)) @fryguy503 2024-01-08
* Ensure mana taps only effect NPC's that have mana. ([#3907](https://github.com/EQEmu/Server/pull/3907)) @fryguy503 2024-01-08
* Over Taunt Hate ([#3900](https://github.com/EQEmu/Server/pull/3900)) @fryguy503 2024-01-08
* Stun Chance Percent Rule ([#3922](https://github.com/EQEmu/Server/pull/3922)) @fryguy503 2024-01-08
2024-01-08 20:31:13 -06:00
Alex King dd41fc5fcd [Bug Fix] Fix Typo in Character Skills loading (#3937) 2024-01-08 21:18:55 -05:00
Fryguy 6bf36f3e77 [Bug Fix] World Shutdown Filter (#3930)
Moved World Shutdown to the System filter from base yellow.
2024-01-08 12:14:08 -05:00
Fryguy dfb06db17b [Rule] Over Taunt Hate (#3900)
* [Rule] Over taunt hate rule

Rule to add additional hate when taunt succeeds. This can help tune snap aggro on taunt classes. Only works when taunt succeeds and not already top hate.

* Requested Adjustments

* Add default +1 on rule

* Revert back to 0 default on rule with +1 on the standard formula

* formatting fix
2024-01-08 12:11:04 -05:00
Fryguy 3e958c575b [Info] Adding textual feedback when trying to sell alt items back to … (#3917)
* [Info] Adding textual feedback when trying to sell alt items back to vendor when disabled.

* requested changes
2024-01-08 11:34:58 -05:00
Fryguy e5db19965f [Rule] Ensure mana taps only effect NPC's that have mana. (#3907)
* [Rule] Ensure mana taps only effect NPC's that have mana.

* Requested Changes

* fail at the beginning of a cast.

* Fixes
2024-01-08 06:23:54 -05:00
Fryguy ce73f6bfe1 [Bug Fix] Fix for HasLockoutByCharacterID (#3927)
Per HG, this will resolve issues with HasLockoutByCharacterID
2024-01-08 02:45:19 -06:00
Fryguy 700f4645e2 [Rule] Stun Chance Percent Rule (#3922)
Added a rule for server operators to tune the Stun Chance from players when mobs are behind.

DEFAULT: 12
2024-01-08 02:40:01 -06:00
Fryguy 5a7d544c5b [Bug] DI Buff Fade (#3919)
Mistakenly removed BuffFadeBySlot(buffSlot);
2024-01-08 02:06:41 -06:00
Fryguy 0f7f71334a [Rules] Backstab Damage Modifier (#3908)
* [Rules] Backstab Damage Modifier

Added NPCBackstabMod - Default 1.9 - Lower mod = higher backstab.

* Update special_attacks.cpp

corrected math to npc base_damage
2024-01-08 01:58:48 -06:00
Fryguy c731f3f560 [Rule] Classic Tradeskill Skill Clamp (#3914)
Legacy Tradeskills had a 252 clamp regardless of modifier and skill.

Default to 0 to bypass clamp
2024-01-08 01:48:40 -06:00
Fryguy b30fbc70a3 [Bug] NPCs will now only proc on hit (#3913) 2024-01-08 01:47:55 -06:00
Fryguy 195cb80d56 [Bug] Pets should not ignore Z axis (#3912)
Ignoring the Z axis allows you to attack mobs on target from wildly inappropriate Z distances.

Example you can pull mobs to the cy in bothunder from the towers you are under when many 1000's of units away by Z only.
2024-01-08 01:41:51 -06:00
Fryguy b2d5007466 [Bug Fix] TGB - Added logic to stop bard errors on group songs. (#3906)
* [Bug Fix] TGB - Added logic to stop bard errors on group songs.

Occasionally bards would get an error when singing songs with another group member targetted.

* Logic Fix

* Fixed missing brace
2024-01-08 01:27:45 -06:00
Fryguy 47e2eb0acf [Bug Fix] Harm Touch, Improved Harm Touch, and Unholy Touch (#3904)
* [bug] HT / Imp HT / Unholy Touch

Adjusted logic order for HT/Improved HT/Unholy Touch

* Requested Changes
2024-01-07 22:57:34 -05:00
Fryguy c36b3f030b [Bug Fix] Disciplines should show when someone casts them. (#3901)
* [Bug] Discs should show when someone casts them.

* Correction to location

* requested changes

* Additional fixes
2024-01-07 22:17:30 -05:00
Fryguy 2b821e50ff [Rule] Classic Triple Attack (#3903)
* [Rule] Classic Triple Attack

Classic Triple attack pre-dates skill based triple attack.

Originally it was only for a few classes but was expanded to Warrior, Monks, Berserkers and finally rangers for Dragons of Norrath. After which it was converted to a skill based feature.

These were innate starting level 60 and had a flat % to trigger.

* Requested Changes
2024-01-07 22:06:13 -05:00
Fryguy 0aa07e9529 [Bug Fix] Legacy Manaburn should have hard cap. (#3905)
Legacy Manaburn can crit, however normal or crit has same hard cap.

Created a rule to better manage the cap for server owners.
2024-01-07 21:54:14 -05:00
17 changed files with 277 additions and 131 deletions
+31
View File
@@ -1,3 +1,34 @@
## [22.41.0] - 1/8/2024
### Bug
* DI Buff Fade ([#3919](https://github.com/EQEmu/Server/pull/3919)) @fryguy503 2024-01-08
* NPCs will now only proc on hit ([#3913](https://github.com/EQEmu/Server/pull/3913)) @fryguy503 2024-01-08
* Pets should not ignore Z axis ([#3912](https://github.com/EQEmu/Server/pull/3912)) @fryguy503 2024-01-08
### Fixes
* Disciplines should show when someone casts them. ([#3901](https://github.com/EQEmu/Server/pull/3901)) @fryguy503 2024-01-08
* Fix Typo in Character Skills loading ([#3937](https://github.com/EQEmu/Server/pull/3937)) @Kinglykrab 2024-01-09
* Fix for HasLockoutByCharacterID ([#3927](https://github.com/EQEmu/Server/pull/3927)) @fryguy503 2024-01-08
* Harm Touch, Improved Harm Touch, and Unholy Touch ([#3904](https://github.com/EQEmu/Server/pull/3904)) @fryguy503 2024-01-08
* Legacy Manaburn should have hard cap. ([#3905](https://github.com/EQEmu/Server/pull/3905)) @fryguy503 2024-01-08
* TGB - Added logic to stop bard errors on group songs. ([#3906](https://github.com/EQEmu/Server/pull/3906)) @fryguy503 2024-01-08
* World Shutdown Filter ([#3930](https://github.com/EQEmu/Server/pull/3930)) @fryguy503 2024-01-08
### Info
* Adding textual feedback when trying to sell alt items back to … ([#3917](https://github.com/EQEmu/Server/pull/3917)) @fryguy503 2024-01-08
### Rules
* Backstab Damage Modifier ([#3908](https://github.com/EQEmu/Server/pull/3908)) @fryguy503 2024-01-08
* Classic Tradeskill Skill Clamp ([#3914](https://github.com/EQEmu/Server/pull/3914)) @fryguy503 2024-01-08
* Classic Triple Attack ([#3903](https://github.com/EQEmu/Server/pull/3903)) @fryguy503 2024-01-08
* Ensure mana taps only effect NPC's that have mana. ([#3907](https://github.com/EQEmu/Server/pull/3907)) @fryguy503 2024-01-08
* Over Taunt Hate ([#3900](https://github.com/EQEmu/Server/pull/3900)) @fryguy503 2024-01-08
* Stun Chance Percent Rule ([#3922](https://github.com/EQEmu/Server/pull/3922)) @fryguy503 2024-01-08
## [22.40.0] - 1/7/2024
### Account
+11
View File
@@ -254,6 +254,7 @@ RULE_BOOL(Skills, TrainSenseHeading, false, "Switch whether SenseHeading is trai
RULE_INT(Skills, SenseHeadingStartValue, 200, "Start value of sense heading skill")
RULE_BOOL(Skills, SelfLanguageLearning, true, "Enabling self-learning of languages")
RULE_BOOL(Skills, RequireTomeHandin, false, "Disable click-to-learn and force hand in to Guild Master")
RULE_INT(Skills, TradeSkillClamp, 0, "Legacy tradeskills would clamp at 252 regardless of item modifiers and skill combination. DEFAULT: 0 will bypass clamp. Legacy value 252")
RULE_CATEGORY_END()
RULE_CATEGORY(Pets)
@@ -471,10 +472,12 @@ RULE_BOOL(Spells, DOTBonusDamageSplitOverDuration, true, "Disable to have Damage
RULE_BOOL(Spells, HOTBonusHealingSplitOverDuration, true, "Disable to have Heal Over Time total bonus healing added to each tick instead of divided across duration")
RULE_BOOL(Spells, UseLegacyFizzleCode, false, "Enable will turn on the legacy fizzle code which is far stricter and more accurate to 2001/2002 testing.")
RULE_BOOL(Spells, LegacyManaburn, false, "Enable to have the legacy manaburn system from 2003 and earlier.")
RULE_INT(Spells, LegacyManaburnCap, 9492, "Adjusted the hard cap (Normal or Crit) for the Legacy Manaburn system. DEFAULT: 9492")
RULE_BOOL(Spells, EvacClearAggroInSameZone, false, "Enable to clear aggro on clients when evacing in same zone.")
RULE_BOOL(Spells, CharmAggroOverLevel, false, "Enabling this rule will cause Charm casts over level to show resisted and cause aggro. Early EQ style.")
RULE_BOOL(Spells, RequireMnemonicRetention, true, "Enabling will require spell slots 9-12 to have the appropriate Mnemonic Retention AA learned.")
RULE_BOOL(Spells, EvacClearCharmPet, false, "Enable to have evac in zone clear charm from charm pets and detach buffs.")
RULE_BOOL(Spells, ManaTapsRequireNPCMana, false, "Enabling will require target to have mana to tap. Default off as many npc's are caster class with 0 mana and need fixed.")
RULE_CATEGORY_END()
RULE_CATEGORY(Combat)
@@ -512,6 +515,7 @@ RULE_REAL(Combat, AvgDefProcsPerMinute, 2.0, "Average defense procs per minute")
RULE_REAL(Combat, DefProcPerMinAgiContrib, 0.075, "How much agility contributes to defensive proc rate")
RULE_INT(Combat, NPCFlurryChance, 20, "Chance for NPC to flurry")
RULE_BOOL(Combat, TauntOverLevel, 1, "Allows you to taunt NPC's over warriors level")
RULE_INT(Combat, TauntOverAggro, 0, "+ amount over hate_top it will add before any bonus hate.")
RULE_REAL(Combat, TauntSkillFalloff, 0.33, "For every taunt skill point that's not maxed you lose this percentage chance to taunt")
RULE_BOOL(Combat, EXPFromDmgShield, false, "Determine if damage from a damage shield counts for experience gain")
RULE_INT(Combat, QuiverHasteCap, 1000, "Quiver haste cap 1000 on live for a while, currently 700 on live")
@@ -555,6 +559,12 @@ RULE_BOOL(Combat, WaterMatchRequiredForAutoFireLoS, true, "Enable/Disable the re
RULE_INT(Combat, ExtraAllowedKickClassesBitmask, 0, "Bitmask for allowing extra classes beyond Warrior, Ranger, Beastlord, and Berserker to kick, No Extra Classes (0) by default")
RULE_INT(Combat, MaxProcs, 4, "Adjustable maximum number of procs per round, the hard cap is MAX_PROCS (11). Requires mob repop or client zone when changed")
RULE_BOOL(Combat, FinishingBlowOnlyWhenFleeing, false, "Enable to only allow Finishing Blow when fleeing (Original Style Finishing Blow)")
RULE_BOOL(Combat, ClassicTripleAttack, false, "enable to use non-skill based classic triple attack. Originally it was Warrior Only but was expanded, can use the TripleAttackChance to tune the classes out.")
RULE_INT(Combat, ClassicTripleAttackChanceWarrior, 100, "Innate Chance for Warrior to Triple Attack after a Double Attack (125 = 12.5%). DEFAULT: 100")
RULE_INT(Combat, ClassicTripleAttackChanceMonk, 100, "Innate Chance for Monk to Triple Attack after a Double Attack (200 = 20%). DEFAULT: 100")
RULE_INT(Combat, ClassicTripleAttackChanceBerserker, 100, "Innate Chance for Berserker to Triple Attack after a Double Attack (200 = 20%). DEFAULT: 100")
RULE_INT(Combat, ClassicTripleAttackChanceRanger, 100, "Innate Chance for Ranger to Triple Attack after a Double Attack (200 = 20%). DEFAULT: 100")
RULE_INT(Combat, StunChance, 12, "Percent chance that client will be stunned when mob is behind player. DEFAULT: 12")
RULE_CATEGORY_END()
RULE_CATEGORY(NPC)
@@ -580,6 +590,7 @@ RULE_INT(NPC, NPCToNPCAggroTimerMin, 500, "Minimum time span after which one NPC
RULE_INT(NPC, NPCToNPCAggroTimerMax, 6000, "Maximum time span after which one NPC aggro another NPC (milliseconds)")
RULE_BOOL(NPC, UseClassAsLastName, true, "Uses class archetype as LastName for NPC with none")
RULE_BOOL(NPC, NewLevelScaling, true, "Better level scaling, use old if new formulas would break your server")
RULE_REAL(NPC,NPCBackstabMod, 1.9, "Multiplier for NPC Backstab, Higher = Lower backstab amount")
RULE_INT(NPC, NPCGatePercent, 20, " Percentage at which the NPC Will attempt to gate at")
RULE_BOOL(NPC, NPCGateNearBind, false, "Will NPC attempt to gate when near bind location?")
RULE_INT(NPC, NPCGateDistanceBind, 75, "Distance from bind before NPC will attempt to gate")
+1 -1
View File
@@ -25,7 +25,7 @@
// Build variables
// these get injected during the build pipeline
#define CURRENT_VERSION "22.40.0-dev" // always append -dev to the current version for custom-builds
#define CURRENT_VERSION "22.41.0-dev" // always append -dev to the current version for custom-builds
#define LOGIN_VERSION "0.8.0"
#define COMPILE_DATE __DATE__
#define COMPILE_TIME __TIME__
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "eqemu-server",
"version": "22.40.0",
"version": "22.41.0",
"repository": {
"type": "git",
"url": "https://github.com/EQEmu/Server.git"
+2 -2
View File
@@ -111,7 +111,7 @@ void ZSList::Process() {
0,
0,
AccountStatus::Player,
Chat::Yellow,
Chat::System,
fmt::format(
"[SYSTEM] World will be shutting down in {} minutes.",
((shutdowntimer->GetRemainingTime() / 1000) / 60)
@@ -759,7 +759,7 @@ void ZSList::WorldShutDown(uint32 time, uint32 interval)
0,
0,
AccountStatus::Player,
Chat::Yellow,
Chat::System,
fmt::format(
"[SYSTEM] World will be shutting down in {} minutes.",
(time / 60)
+71 -29
View File
@@ -1419,7 +1419,7 @@ void Mob::DoAttack(Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts, boo
if (other->CheckHitChance(this, hit)) {
if (IsNPC() && other->IsClient() && other->animation > 0 && GetLevel() >= 5 && BehindMob(other, GetX(), GetY())) {
// ~ 12% chance
if (zone->random.Roll(12)) {
if (zone->random.Roll(RuleI(Combat, StunChance))) {
int stun_resist2 = other->spellbonuses.FrontalStunResist + other->itembonuses.FrontalStunResist + other->aabonuses.FrontalStunResist;
int stun_resist = other->spellbonuses.StunResist + other->itembonuses.StunResist + other->aabonuses.StunResist;
if (zone->random.Roll(stun_resist2)) {
@@ -2281,49 +2281,51 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool
if (other->IsClient() && IsPet() && GetOwner()->IsClient()) {
//pets do half damage to clients in pvp
my_hit.damage_done /= 2;
if (my_hit.damage_done < 1)
if (my_hit.damage_done < 1) {
my_hit.damage_done = 1;
}
}
}
else {
} else {
my_hit.damage_done = DMG_INVULNERABLE;
}
if (GetHP() > 0 && !other->HasDied()) {
other->Damage(this, my_hit.damage_done, SPELL_UNKNOWN, my_hit.skill, true, -1, false, m_specialattacks); // Not avoidable client already had thier chance to Avoid
} else {
return false;
}
else
return false;
if (HasDied()) //killed by damage shield ect
if (HasDied()) { //killed by damage shield ect
return false;
}
MeleeLifeTap(my_hit.damage_done);
CommonBreakInvisibleFromCombat();
//I doubt this works...
if (!GetTarget())
if (!GetTarget()) {
return true; //We killed them
if (!bRiposte && !other->HasDied()) {
TryWeaponProc(nullptr, weapon, other, Hand); //no weapon
if (!other->HasDied())
TrySpellProc(nullptr, weapon, other, Hand);
if (my_hit.damage_done > 0 && HasSkillProcSuccess() && !other->HasDied())
TrySkillProc(other, my_hit.skill, 0, true, Hand);
}
if (GetHP() > 0 && !other->HasDied())
bool has_hit = my_hit.damage_done > 0;
if (has_hit && !bRiposte && !other->HasDied()) {
TryWeaponProc(nullptr, weapon, other, Hand);
if (!other->HasDied()) {
TrySpellProc(nullptr, weapon, other, Hand);
}
if (HasSkillProcSuccess() && !other->HasDied()) {
TrySkillProc(other, my_hit.skill, 0, true, Hand);
}
}
if (GetHP() > 0 && !other->HasDied()) {
TriggerDefensiveProcs(other, Hand, true, my_hit.damage_done);
}
if (my_hit.damage_done > 0)
return true;
else
return false;
return has_hit;
}
void NPC::Damage(Mob* other, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, bool avoidable, int8 buffslot, bool iBuffTic, eSpecialAttacks special) {
@@ -3724,9 +3726,43 @@ bool Client::CheckDoubleAttack()
// with varying triple attack skill (1-3% error at least)
bool Client::CheckTripleAttack()
{
int chance = GetSkill(EQ::skills::SkillTripleAttack);
if (chance < 1)
int chance;
if (RuleB(Combat, ClassicTripleAttack)) {
if (
IsClient() &&
GetLevel() >= 60 &&
(
GetClass() == Class::Warrior ||
GetClass() == Class::Ranger ||
GetClass() == Class::Monk ||
GetClass() == Class::Berserker
)
) {
switch (GetClass()) {
case Class::Warrior:
chance = RuleI(Combat, ClassicTripleAttackChanceWarrior);
break;
case Class::Ranger:
chance = RuleI(Combat, ClassicTripleAttackChanceRanger);
break;
case Class::Monk:
chance = RuleI(Combat, ClassicTripleAttackChanceMonk);
break;
case Class::Berserker:
chance = RuleI(Combat, ClassicTripleAttackChanceBerserker);
break;
default:
break;
}
}
} else {
chance = GetSkill(EQ::skills::SkillTripleAttack);
}
if (chance < 1) {
return false;
}
int inc = aabonuses.TripleAttackChance + spellbonuses.TripleAttackChance + itembonuses.TripleAttackChance;
chance = static_cast<int>(chance * (1 + inc / 100.0f));
@@ -6320,15 +6356,21 @@ void Client::DoAttackRounds(Mob *target, int hand, bool IsFromSpell)
// you can only triple from the main hand
if (hand == EQ::invslot::slotPrimary && CanThisClassTripleAttack()) {
CheckIncreaseSkill(EQ::skills::SkillTripleAttack, target, -10);
if (!RuleB(Combat, ClassicTripleAttack)) {
CheckIncreaseSkill(EQ::skills::SkillTripleAttack, target, -10);
}
if (CheckTripleAttack()) {
Attack(target, hand, false, false, IsFromSpell);
auto flurrychance = aabonuses.FlurryChance + spellbonuses.FlurryChance +
int flurry_chance = aabonuses.FlurryChance + spellbonuses.FlurryChance +
itembonuses.FlurryChance;
if (flurrychance && zone->random.Roll(flurrychance)) {
if (flurry_chance && zone->random.Roll(flurry_chance)) {
Attack(target, hand, false, false, IsFromSpell);
if (zone->random.Roll(flurrychance))
if (zone->random.Roll(flurry_chance)) {
Attack(target, hand, false, false, IsFromSpell);
}
MessageString(Chat::NPCFlurry, YOU_FLURRY);
}
}
+2 -1
View File
@@ -2740,6 +2740,7 @@ void Client::Handle_OP_AltCurrencySell(const EQApplicationPacket *app)
}
if (!RuleB(Merchant, EnableAltCurrencySell)) {
Message(Chat::Red, "Selling alternate currency items is disabled.");
return;
}
@@ -10766,7 +10767,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app)
}
if ((mypet->GetPetType() == petAnimation && aabonuses.PetCommands[PetCommand]) || mypet->GetPetType() != petAnimation) {
if (target != this && DistanceSquaredNoZ(mypet->GetPosition(), target->GetPosition()) <= (RuleR(Pets, AttackCommandRange)*RuleR(Pets, AttackCommandRange))) {
if (target != this && DistanceSquared(mypet->GetPosition(), target->GetPosition()) <= (RuleR(Pets, AttackCommandRange)*RuleR(Pets, AttackCommandRange))) {
mypet->SetFeigned(false);
if (mypet->IsPetStop()) {
mypet->SetPetStop(false);
+27 -10
View File
@@ -60,15 +60,7 @@ int64 Mob::GetActSpellDamage(uint16 spell_id, int64 value, Mob* target) {
bool Critical = false;
int64 base_value = value;
int chance = 0;
// Need to scale HT damage differently after level 40! It no longer scales by the constant value in the spell file. It scales differently, instead of 10 more damage per level, it does 30 more damage per level. So we multiply the level minus 40 times 20 if they are over level 40.
if ((spell_id == SPELL_HARM_TOUCH || spell_id == SPELL_HARM_TOUCH2 || spell_id == SPELL_IMP_HARM_TOUCH ) && GetLevel() > 40)
value -= (GetLevel() - 40) * 20;
//This adds the extra damage from the AA Unholy Touch, 450 per level to the AA Improved Harm TOuch.
if (spell_id == SPELL_IMP_HARM_TOUCH && IsOfClientBot()) { //Improved Harm Touch
value -= GetAA(aaUnholyTouch) * 450; //Unholy Touch
}
int legacy_manaburn_cap = RuleI(Spells, LegacyManaburnCap);
chance = RuleI(Spells, BaseCritChance); //Wizard base critical chance is 2% (Does not scale with level)
chance += itembonuses.CriticalSpellChance + spellbonuses.CriticalSpellChance + aabonuses.CriticalSpellChance;
@@ -138,12 +130,30 @@ int64 Mob::GetActSpellDamage(uint16 spell_id, int64 value, Mob* target) {
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, base_value) * ratio / 100;
}
// legacy manaburn can crit, but is still held to the same cap
if (RuleB(Spells, LegacyManaburn) && spell_id == SPELL_MANA_BURN) {
if (value < -legacy_manaburn_cap) {
value = -legacy_manaburn_cap;
}
}
entity_list.FilteredMessageCloseString(
this, true, 100, Chat::SpellCrit, FilterSpellCrits,
OTHER_CRIT_BLAST, nullptr, GetName(), itoa(-value));
if (IsClient())
if (IsClient()) {
MessageString(Chat::SpellCrit, YOU_CRIT_BLAST, itoa(-value));
}
// Need to scale HT damage differently after level 40! It no longer scales by the constant value in the spell file. It scales differently, instead of 10 more damage per level, it does 30 more damage per level. So we multiply the level minus 40 times 20 if they are over level 40.
if ((spell_id == SPELL_HARM_TOUCH || spell_id == SPELL_HARM_TOUCH2 || spell_id == SPELL_IMP_HARM_TOUCH) && GetLevel() > 40) {
value -= (GetLevel() - 40) * 20;
}
//This adds the extra damage from the AA Unholy Touch, 450 per level to the AA Improved Harm Touch.
if (spell_id == SPELL_IMP_HARM_TOUCH && IsOfClientBot()) { //Improved Harm Touch
value -= GetAA(aaUnholyTouch) * 450; //Unholy Touch
}
return value;
}
@@ -183,6 +193,13 @@ int64 Mob::GetActSpellDamage(uint16 spell_id, int64 value, Mob* target) {
value -= GetExtraSpellAmt(spell_id, GetSpellDmg(), base_value);
}
// Apply Manaburn Damage Cap
if (RuleB(Spells, LegacyManaburn) && spell_id == SPELL_MANA_BURN) {
if (value < -legacy_manaburn_cap) {
value = -legacy_manaburn_cap;
}
}
return value;
}
+1 -1
View File
@@ -1171,7 +1171,7 @@ bool Expedition::HasLockoutByCharacterID(
{
auto lockouts = Expedition::GetExpeditionLockoutsByCharacterID(character_id);
return std::any_of(lockouts.begin(), lockouts.end(), [&](const ExpeditionLockoutTimer& lockout) {
return lockout.IsSameLockout(expedition_name, event_name);
return !lockout.IsExpired() && lockout.IsSameLockout(expedition_name, event_name);
});
}
+1 -1
View File
@@ -17,7 +17,7 @@ void command_worldshutdown(Client *c, const Seperator *sep)
) {
int time_minutes = (time / 60);
quest_manager.WorldWideMessage(
Chat::Yellow,
Chat::System,
fmt::format(
"[SYSTEM] World will be shutting down in {} minutes.",
time_minutes
+16 -3
View File
@@ -4539,10 +4539,23 @@ bool Mob::CanThisClassDoubleAttack(void) const
bool Mob::CanThisClassTripleAttack() const
{
if (!IsClient())
if (!IsClient()) {
return false; // When they added the real triple attack skill, mobs lost the ability to triple
else
return CastToClient()->HasSkill(EQ::skills::SkillTripleAttack);
} else {
if (RuleB(Combat, ClassicTripleAttack)) {
return (
GetLevel() >= 60 &&
(
GetClass() == Class::Warrior ||
GetClass() == Class::Ranger ||
GetClass() == Class::Monk ||
GetClass() == Class::Berserker
)
);
} else {
return CastToClient()->HasSkill(EQ::skills::SkillTripleAttack);
}
}
}
bool Mob::IsWarriorClass(void) const
+1 -1
View File
@@ -1147,7 +1147,7 @@ public:
void StartEnrage();
void ProcessEnrage();
bool IsEnraged();
void Taunt(NPC *who, bool always_succeed, int chance_bonus = 0, bool FromSpell = false, int32 bonus_hate = 0);
void Taunt(NPC *who, bool always_succeed, int chance_bonus = 0, bool from_spell = false, int32 bonus_hate = 0);
virtual void AI_Init();
virtual void AI_Start(uint32 iMoveDelay = 0);
+53 -43
View File
@@ -24,6 +24,7 @@
#include "mob.h"
#include "string_ids.h"
#include "lua_parser.h"
#include "npc.h"
#include <string.h>
@@ -182,7 +183,7 @@ int Mob::GetBaseSkillDamage(EQ::skills::SkillType skill, Mob *target)
}
} else if (IsNPC()) {
auto *npc = CastToNPC();
base = std::max(base, npc->GetBaseDamage());
base = round((npc->GetMaxDMG() - npc->GetMinDMG()) / RuleR(NPC, NPCBackstabMod));
// parses show relatively low BS mods from lots of NPCs, so either their BS skill is super low
// or their mod is divided again, this is probably not the right mod, but it's better
skill_bonus /= 3.0f;
@@ -2127,19 +2128,15 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte)
}
}
void Mob::Taunt(NPC *who, bool always_succeed, int chance_bonus, bool FromSpell, int32 bonus_hate)
void Mob::Taunt(NPC *who, bool always_succeed, int chance_bonus, bool from_spell, int32 bonus_hate)
{
if (who == nullptr)
if (!who || DivineAura() || (!from_spell && !CombatRange(who))) {
return;
}
if (DivineAura())
return;
if (!FromSpell && !CombatRange(who))
return;
if (!always_succeed && IsClient())
if (!always_succeed && IsClient()) {
CastToClient()->CheckIncreaseSkill(EQ::skills::SkillTaunt, who, 10);
}
Mob *hate_top = who->GetHateMost();
@@ -2147,57 +2144,63 @@ void Mob::Taunt(NPC *who, bool always_succeed, int chance_bonus, bool FromSpell,
bool success = false;
// Support for how taunt worked pre 2000 on LIVE - Can not taunt NPC over your level.
if ((RuleB(Combat, TauntOverLevel) == false) && (level_difference < 0) ||
who->GetSpecialAbility(IMMUNE_TAUNT)) {
if (
!RuleB(Combat, TauntOverLevel) &&
level_difference < 0 ||
who->GetSpecialAbility(IMMUNE_TAUNT)
) {
MessageString(Chat::SpellFailure, FAILED_TAUNT);
return;
}
// All values used based on live parses after taunt was updated in 2006.
if ((hate_top && hate_top->GetHPRatio() >= 20) || hate_top == nullptr || chance_bonus) {
if (
(hate_top && hate_top->GetHPRatio() >= 20) ||
!hate_top ||
chance_bonus
) {
// SE_Taunt this is flat chance
if (chance_bonus) {
success = zone->random.Roll(chance_bonus);
} else {
float tauntchance = 50.0f;
if (always_succeed)
tauntchance = 101.0f;
else {
float taunt_chance = 50.0f;
if (always_succeed) {
taunt_chance = 101.0f;
} else {
if (level_difference < 0) {
tauntchance += static_cast<float>(level_difference) * 3.0f;
if (tauntchance < 20)
tauntchance = 20.0f;
}
else {
tauntchance += static_cast<float>(level_difference) * 5.0f;
if (tauntchance > 65)
tauntchance = 65.0f;
taunt_chance += static_cast<float>(level_difference) * 3.0f;
if (taunt_chance < 20) {
taunt_chance = 20.0f;
}
} else {
taunt_chance += static_cast<float>(level_difference) * 5.0f;
if (taunt_chance > 65) {
taunt_chance = 65.0f;
}
}
}
// TauntSkillFalloff rate is not based on any real data. Default of 33% gives a reasonable
// result.
if (IsClient() && !always_succeed)
tauntchance -= (RuleR(Combat, TauntSkillFalloff) *
if (IsClient() && !always_succeed) {
taunt_chance -= (RuleR(Combat, TauntSkillFalloff) *
(CastToClient()->MaxSkill(EQ::skills::SkillTaunt) -
GetSkill(EQ::skills::SkillTaunt)));
}
if (tauntchance < 1)
tauntchance = 1.0f;
if (taunt_chance < 1) {
taunt_chance = 1.0f;
}
tauntchance /= 100.0f;
success = tauntchance > zone->random.Real(0, 1);
taunt_chance /= 100.0f;
success = taunt_chance > zone->random.Real(0, 1);
LogHate(
"Taunter mob {} target npc {} tauntchance [{}] success [{}] hate_top [{}]",
"Taunter mob {} target npc {} taunt_chance [{}] success [{}] hate_top [{}]",
GetMobDescription(),
who->GetMobDescription(),
tauntchance,
taunt_chance,
success ? "true" : "false",
hate_top ? hate_top->GetMobDescription() : "not found"
);
@@ -2205,27 +2208,34 @@ void Mob::Taunt(NPC *who, bool always_succeed, int chance_bonus, bool FromSpell,
if (success) {
if (hate_top && hate_top != this) {
int64 newhate = (who->GetNPCHate(hate_top) - who->GetNPCHate(this)) + 1 + bonus_hate;
int64 new_hate = (
(who->GetNPCHate(hate_top) - who->GetNPCHate(this)) +
bonus_hate +
RuleI(Combat, TauntOverAggro) +
1
);
LogHate(
"Taunter mob {} target npc {} newhate [{}] hated_top {} hate_of_top [{}] this_hate [{}] bonus_hate [{}]",
"Not Top Hate - Taunter [{}] Target [{}] Hated Top [{}] Hate Top Amt [{}] This Character Amt [{}] Bonus_Hate Amt [{}] TauntOverAggro Amt [{}] - Total [{}]",
GetMobDescription(),
who->GetMobDescription(),
newhate,
hate_top->GetMobDescription(),
who->GetNPCHate(hate_top),
who->GetNPCHate(this),
bonus_hate
bonus_hate,
RuleI(Combat, TauntOverAggro),
new_hate
);
who->CastToNPC()->AddToHateList(this, newhate);
who->CastToNPC()->AddToHateList(this, new_hate);
success = true;
} else {
who->CastToNPC()->AddToHateList(this, 12);
}
if (who->CanTalk())
if (who->CanTalk()) {
who->SayString(SUCCESSFUL_TAUNT, GetCleanName());
}
} else {
MessageString(Chat::SpellFailure, FAILED_TAUNT);
}
+1
View File
@@ -7094,6 +7094,7 @@ bool Mob::TryDeathSave() {
}
SendHPUpdate();
BuffFadeBySlot(buffSlot);
return true;
}
else if (UD_HealMod) {
+49 -35
View File
@@ -220,6 +220,23 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot,
BuffFadeByEffect(SE_NegateIfCombat);
}
// check to see if target is a caster mob before performing a mana tap
if(GetTarget() && IsManaTapSpell(spell_id)) {
if (
GetTarget()->GetCasterClass() == 'N' &&
(
!RuleB(Spells, ManaTapsRequireNPCMana) ||
(
RuleB(Spells, ManaTapsRequireNPCMana) &&
GetTarget()->GetMana() == 0
)
)
) {
InterruptSpell(TARGET_NO_MANA, 0x121, spell_id);
return false;
}
}
//Casting a spell from an item click will also stop bard pulse.
if (HasActiveSong() && (IsBardSong(spell_id) || slot == CastingSlot::Item)) {
LogSpells("Casting a new song while singing a song. Killing old song [{}]", bardsong);
@@ -2481,14 +2498,6 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, in
return false;
}
// check to see if target is a caster mob before performing a mana tap
if(spell_target && IsManaTapSpell(spell_id)) {
if(spell_target->GetCasterClass() == 'N') {
MessageString(Chat::Red, TARGET_NO_MANA);
return false;
}
}
//range check our target, if we have one and it is not us
float range = spells[spell_id].range + GetRangeDistTargetSizeMod(spell_target);
if(IsClient() && CastToClient()->TGB() && IsTGBCompatibleSpell(spell_id) && IsGroupSpell(spell_id))
@@ -2619,74 +2628,75 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, in
case GroupSpell:
{
if(IsBot()) {
bool StopLogic = false;
if(!CastToBot()->DoFinishedSpellGroupTarget(spell_id, spell_target, slot, StopLogic))
if (IsBot()) {
bool stop_logic = false;
if (!CastToBot()->DoFinishedSpellGroupTarget(spell_id, spell_target, slot, stop_logic)) {
return false;
if(StopLogic)
}
if(stop_logic) {
break;
}
}
// We hold off turning MBG off so we can still use it to calc the mana cost
if(spells[spell_id].can_mgb && HasMGB())
{
if (spells[spell_id].can_mgb && HasMGB()) {
SpellOnTarget(spell_id, this);
entity_list.MassGroupBuff(this, this, spell_id, true);
}
else
{
} else {
// at this point spell_target is a member of the other group, or the
// caster if they're not using TGB
// NOTE: this will always hit the caster, plus the target's group so
// it can affect up to 7 people if the targeted group is not our own
// Allow pets who cast group spells to affect the group.
if (spell_target->IsPetOwnerClient() && IsPetOwnerClient()){
if (spell_target->IsPetOwnerClient() && IsPetOwnerClient()) {
Mob* owner = spell_target->GetOwner();
if (owner)
if (owner) {
spell_target = owner;
}
}
if(spell_target->IsGrouped())
{
if (spell_target->IsGrouped()) {
Group *target_group = entity_list.GetGroupByMob(spell_target);
if(target_group)
{
if (target_group) {
target_group->CastGroupSpell(this, spell_id);
if (GetClass() != Class::Bard) {
SpellOnTarget(spell_id, this);
}
}
}
else if(spell_target->IsRaidGrouped() && spell_target->IsClient())
{
} else if (spell_target->IsRaidGrouped() && spell_target->IsClient()) {
Raid *target_raid = entity_list.GetRaidByClient(spell_target->CastToClient());
uint32 gid = 0xFFFFFFFF;
if(target_raid){
if (target_raid) {
gid = target_raid->GetGroup(spell_target->GetName());
if(gid < 12)
if (gid < 12) {
target_raid->CastGroupSpell(this, spell_id, gid);
else
} else {
SpellOnTarget(spell_id, spell_target);
}
}
}
else
{
} else {
// if target is grouped, CastGroupSpell will cast it on the caster
// too, but if not then we have to do that here.
if(spell_target != this){
if (spell_target != this) {
SpellOnTarget(spell_id, this);
#ifdef GROUP_BUFF_PETS
//pet too
if (spells[spell_id].target_type != ST_GroupNoPets && GetPet() && HasPetAffinity() && !GetPet()->IsCharmed())
if (spells[spell_id].target_type != ST_GroupNoPets && GetPet() && HasPetAffinity() && !GetPet()->IsCharmed()) {
SpellOnTarget(spell_id, GetPet());
}
#endif
}
SpellOnTarget(spell_id, spell_target);
#ifdef GROUP_BUFF_PETS
//pet too
if (spells[spell_id].target_type != ST_GroupNoPets && spell_target->GetPet() && spell_target->HasPetAffinity() && !spell_target->GetPet()->IsCharmed())
if (spells[spell_id].target_type != ST_GroupNoPets && spell_target->GetPet() && spell_target->HasPetAffinity() && !spell_target->GetPet()->IsCharmed()) {
SpellOnTarget(spell_id, spell_target->GetPet());
}
#endif
}
}
@@ -3797,6 +3807,10 @@ bool Mob::SpellOnTarget(
LogSpells("Casting spell [{}] on [{}] with effective caster level [{}]", spell_id, spelltar->GetName(), caster_level);
if (IsClient() && (IsDiscipline(spell_id) || spells[spell_id].is_discipline)) {
entity_list.MessageClose(this, false, 200, 0, fmt::format("{}{}", GetCleanName(), spells[spell_id].cast_on_other).c_str());
}
// Actual cast action - this causes the caster animation and the particles
// around the target
// we do this first, that way we get the particles even if the spell
+8 -2
View File
@@ -952,10 +952,16 @@ void Client::SendTradeskillDetails(uint32 recipe_id) {
//returns true on success
bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) {
if(spec == nullptr)
return(false);
if (!spec) {
return false;
}
uint16 user_skill = GetSkill(spec->tradeskill);
if (RuleI(Skills, TradeSkillClamp) != 0 && user_skill > RuleI(Skills, TradeSkillClamp)) {
user_skill = RuleI(Skills, TradeSkillClamp);
}
float chance = 0.0;
float skillup_modifier = 0.0;
int16 thirdstat = 0;
+1 -1
View File
@@ -800,7 +800,7 @@ bool ZoneDatabase::LoadCharacterSkills(uint32 character_id, PlayerProfile_Struct
const auto& l = CharacterSkillsRepository::GetWhere(
*this,
fmt::format(
"`id` = {} ORDER BY `skill_id",
"`id` = {} ORDER BY `skill_id`",
character_id
)
);