From d34b4a786b63e39d9705ae35f736da119ad0b03d Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sun, 21 Jun 2015 02:01:48 -0400 Subject: [PATCH 1/2] Implement duration ramp and war cry with new AA system Rampage also correctly does a full attack round for classes other than monk and ranger --- zone/aa.cpp | 43 ++++++++++++++------- zone/attack.cpp | 36 ++++++++++++++++++ zone/client.h | 3 +- zone/client_process.cpp | 84 +++-------------------------------------- zone/effects.cpp | 7 +++- zone/mob.h | 1 + zone/spell_effects.cpp | 53 ++++++++------------------ zone/spells.cpp | 16 ++++---- 8 files changed, 102 insertions(+), 141 deletions(-) diff --git a/zone/aa.cpp b/zone/aa.cpp index 0c31034c7..41be9a0cf 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -828,7 +828,7 @@ void Client::SendAlternateAdvancementRank(int aa_id, int level) { if(!CanUseAlternateAdvancementRank(rank)) { return; } - + int size = sizeof(AARankInfo_Struct) + (sizeof(AARankEffect_Struct) * rank->effects.size()) + (sizeof(AARankPrereq_Struct) * rank->prereqs.size()); EQApplicationPacket *outapp = new EQApplicationPacket(OP_SendAATable, size); AARankInfo_Struct *aai = (AARankInfo_Struct*)outapp->pBuffer; @@ -996,7 +996,7 @@ void Client::PurchaseAlternateAdvancementRank(int rank_id) { if(!CanPurchaseAlternateAdvancementRank(rank, true)) { return; } - + if(rank->base_ability->charges > 0) { uint32 charges = 0; GetAA(rank_id, &charges); @@ -1004,7 +1004,7 @@ void Client::PurchaseAlternateAdvancementRank(int rank_id) { if(charges > 0) { return; } - + SetAA(rank_id, rank->current_value, rank->base_ability->charges); } else { SetAA(rank_id, rank->current_value, 0); @@ -1022,10 +1022,10 @@ void Client::PurchaseAlternateAdvancementRank(int rank_id) { SendAlternateAdvancementStats(); if(rank->prev) { - Message_StringID(15, AA_IMPROVE, - std::to_string(rank->title_sid).c_str(), - std::to_string(rank->prev->current_value).c_str(), - std::to_string(rank->cost).c_str(), + Message_StringID(15, AA_IMPROVE, + std::to_string(rank->title_sid).c_str(), + std::to_string(rank->prev->current_value).c_str(), + std::to_string(rank->cost).c_str(), std::to_string(AA_POINTS).c_str()); /* QS: Player_Log_AA_Purchases */ @@ -1034,9 +1034,9 @@ void Client::PurchaseAlternateAdvancementRank(int rank_id) { QServ->PlayerLogEvent(Player_Log_AA_Purchases, CharacterID(), event_desc); } } else { - Message_StringID(15, AA_GAIN_ABILITY, - std::to_string(rank->title_sid).c_str(), - std::to_string(rank->cost).c_str(), + Message_StringID(15, AA_GAIN_ABILITY, + std::to_string(rank->title_sid).c_str(), + std::to_string(rank->cost).c_str(), std::to_string(AA_POINTS).c_str()); /* QS: Player_Log_AA_Purchases */ if (RuleB(QueryServ, PlayerLogAAPurchases)){ @@ -1125,7 +1125,7 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) { if(!IsValidSpell(rank->spell)) { return; } - + if(!CanUseAlternateAdvancementRank(rank)) { return; } @@ -1448,7 +1448,7 @@ bool Mob::CanPurchaseAlternateAdvancementRank(AA::Rank *rank, bool check_price) if(!ability) return false; - + if(!CanUseAlternateAdvancementRank(rank)) { return false; } @@ -1474,7 +1474,7 @@ bool Mob::CanPurchaseAlternateAdvancementRank(AA::Rank *rank, bool check_price) } //if expendable only let us purchase if we have no charges already - //not quite sure on how this functions client side atm + //not quite sure on how this functions client side atm //I intend to look into it later to make sure the behavior is right if(ability->charges > 0 && current_charges > 0) { return false; @@ -1565,7 +1565,7 @@ void Zone::LoadAlternateAdvancement() { } bool ZoneDatabase::LoadAlternateAdvancementAbilities(std::unordered_map> &abilities, - std::unordered_map> &ranks) + std::unordered_map> &ranks) { Log.Out(Logs::General, Logs::Status, "Loading Alternate Advancement Abilities..."); abilities.clear(); @@ -1727,3 +1727,18 @@ void Mob::GrantAlternateAdvancementAbility(int aa_id, int points) { c->CalcBonuses(); } } + +bool Mob::CheckAATimer(int timer) +{ + if (timer >= aaTimerMax) + return false; + if (aa_timers[timer].Enabled()) { + if (aa_timers[timer].Check(false)) { + aa_timers[timer].Disable(); + return false; + } else { + return true; + } + } + return false; +} diff --git a/zone/attack.cpp b/zone/attack.cpp index 6a8125507..202ad77b6 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -5056,3 +5056,39 @@ void NPC::SetAttackTimer() TimerToUse->SetAtTrigger(std::max(RuleI(Combat, MinHastedDelay), speed), true, true); } } + +void Client::DoAttackRounds(Mob *target, int hand, bool IsFromSpell) +{ + if (!target) + return; + + Attack(target, hand, false, false, IsFromSpell); + + if (CanThisClassDoubleAttack()) { + CheckIncreaseSkill(SkillDoubleAttack, target, -10); + if (CheckDoubleAttack()) + Attack(target, hand, false, false, IsFromSpell); + if (hand == MainPrimary && GetLevel() >= 60 && + (GetClass() == MONK || GetClass() == WARRIOR || GetClass() == RANGER || GetClass() == BERSERKER) && + CheckDoubleAttack(true)) + Attack(target, hand, false, false, IsFromSpell); + } + if (hand == MainPrimary) { + auto flurrychance = aabonuses.FlurryChance + spellbonuses.FlurryChance + itembonuses.FlurryChance; + if (flurrychance && zone->random.Roll(flurrychance)) { + Message_StringID(MT_NPCFlurry, YOU_FLURRY); + Attack(target, hand, false, false, IsFromSpell); + Attack(target, hand, false, false, IsFromSpell); + } + + auto extraattackchance = aabonuses.ExtraAttackChance + spellbonuses.ExtraAttackChance + itembonuses.ExtraAttackChance; + if (extraattackchance) { + auto wpn = GetInv().GetItem(MainPrimary); + if (wpn && (wpn->GetItem()->ItemType == ItemType2HBlunt || + wpn->GetItem()->ItemType == ItemType2HSlash || + wpn->GetItem()->ItemType == ItemType2HPiercing)) + if (zone->random.Roll(extraattackchance)) + Attack(target, hand, false, false, IsFromSpell); + } + } +} diff --git a/zone/client.h b/zone/client.h index 07d42a313..44e86f971 100644 --- a/zone/client.h +++ b/zone/client.h @@ -226,6 +226,7 @@ public: virtual int32 GetMeleeMitDmg(Mob *attacker, int32 damage, int32 minhit, float mit_rating, float atk_rating); virtual void SetAttackTimer(); float GetQuiverHaste(); + void DoAttackRounds(Mob *target, int hand, bool IsFromSpell = false); void AI_Init(); void AI_Start(uint32 iMoveDelay = 0); @@ -774,7 +775,7 @@ public: void AddAAPoints(uint32 points) { m_pp.aapoints += points; SendAlternateAdvancementStats(); } int GetAAPoints() { return m_pp.aapoints; } int GetSpentAA() { return m_pp.aapoints_spent; } - + //old AA methods that we still use void ResetAA(); void RefundAA(); diff --git a/zone/client_process.cpp b/zone/client_process.cpp index 603636a82..42c244e6b 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -391,74 +391,12 @@ bool Client::Process() { } else if (auto_attack_target->GetHP() > -10) // -10 so we can watch people bleed in PvP { - //old aa - //if(CheckAAEffect(aaEffectRampage)) - //{ - // entity_list.AEAttack(this, 30); - //} else { - Attack(auto_attack_target, MainPrimary); // Kaiyodo - added attacking hand to arguments - //} ItemInst *wpn = GetInv().GetItem(MainPrimary); TryWeaponProc(wpn, auto_attack_target, MainPrimary); - bool tripleAttackSuccess = false; - if( auto_attack_target && CanThisClassDoubleAttack() ) { - - CheckIncreaseSkill(SkillDoubleAttack, auto_attack_target, -10); - if(CheckDoubleAttack()) { - //should we allow rampage on double attack? - //if(CheckAAEffect(aaEffectRampage)) { - // entity_list.AEAttack(this, 30); - //} else { - Attack(auto_attack_target, MainPrimary, false); - //} - } - - //triple attack: rangers, monks, warriors, berserkers over level 60 - if((((GetClass() == MONK || GetClass() == WARRIOR || GetClass() == RANGER || GetClass() == BERSERKER) - && GetLevel() >= 60) || GetSpecialAbility(SPECATK_TRIPLE)) - && CheckDoubleAttack(true)) - { - tripleAttackSuccess = true; - Attack(auto_attack_target, MainPrimary, false); - } - - //quad attack, does this belong here?? - if(GetSpecialAbility(SPECATK_QUAD) && CheckDoubleAttack(true)) - { - Attack(auto_attack_target, MainPrimary, false); - } - } - - //Live AA - Flurry, Rapid Strikes ect (Flurry does not require Triple Attack). - int16 flurrychance = aabonuses.FlurryChance + spellbonuses.FlurryChance + itembonuses.FlurryChance; - - if (auto_attack_target && flurrychance) - { - if(zone->random.Int(0, 99) < flurrychance) - { - Message_StringID(MT_NPCFlurry, YOU_FLURRY); - Attack(auto_attack_target, MainPrimary, false); - Attack(auto_attack_target, MainPrimary, false); - } - } - - int16 ExtraAttackChanceBonus = spellbonuses.ExtraAttackChance + itembonuses.ExtraAttackChance + aabonuses.ExtraAttackChance; - - if (auto_attack_target && ExtraAttackChanceBonus) { - ItemInst *wpn = GetInv().GetItem(MainPrimary); - if(wpn){ - if(wpn->GetItem()->ItemType == ItemType2HSlash || - wpn->GetItem()->ItemType == ItemType2HBlunt || - wpn->GetItem()->ItemType == ItemType2HPiercing ) - { - if(zone->random.Int(0, 99) < ExtraAttackChanceBonus) - { - Attack(auto_attack_target, MainPrimary, false); - } - } - } - } + DoAttackRounds(auto_attack_target, MainPrimary); + if (CheckAATimer(aaTimerRampage)) + entity_list.AEAttack(this, 30); } } @@ -499,23 +437,11 @@ bool Client::Process() { float random = zone->random.Real(0, 1); CheckIncreaseSkill(SkillDualWield, auto_attack_target, -10); - if (random < DualWieldProbability){ // Max 78% of DW - //if(CheckAAEffect(aaEffectRampage)) { - // entity_list.AEAttack(this, 30, MainSecondary); - //} else { - Attack(auto_attack_target, MainSecondary); // Single attack with offhand - //} + if (random < DualWieldProbability) { // Max 78% of DW ItemInst *wpn = GetInv().GetItem(MainSecondary); TryWeaponProc(wpn, auto_attack_target, MainSecondary); - if( CanThisClassDoubleAttack() && CheckDoubleAttack()) { - //if(CheckAAEffect(aaEffectRampage)) { - // entity_list.AEAttack(this, 30, MainSecondary); - //} else { - // if(auto_attack_target && auto_attack_target->GetHP() > -10) - Attack(auto_attack_target, MainSecondary); // Single attack with offhand - //} - } + DoAttackRounds(auto_attack_target, MainSecondary); } } } diff --git a/zone/effects.cpp b/zone/effects.cpp index 55e56cdac..68e68e78d 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -878,7 +878,7 @@ void EntityList::AEBardPulse(Mob *caster, Mob *center, uint16 spell_id, bool aff caster->CastToClient()->CheckSongSkillIncrease(spell_id); } -//Dook- Rampage and stuff for clients. +// Rampage and stuff for clients. Normal and Duration rampages //NPCs handle it differently in Mob::Rampage void EntityList::AEAttack(Mob *attacker, float dist, int Hand, int count, bool IsFromSpell) { //Dook- Will need tweaking, currently no pets or players or horses @@ -896,7 +896,10 @@ void EntityList::AEAttack(Mob *attacker, float dist, int Hand, int count, bool I && curmob->GetRace() != 216 && curmob->GetRace() != 472 /* dont attack horses */ && (DistanceSquared(curmob->GetPosition(), attacker->GetPosition()) <= dist2) ) { - attacker->Attack(curmob, Hand, false, false, IsFromSpell); + if (!attacker->IsClient() || attacker->GetClass() == MONK || attacker->GetClass() == RANGER) + attacker->Attack(curmob, Hand, false, false, IsFromSpell); + else + attacker->CastToClient()->DoAttackRounds(curmob, Hand, IsFromSpell); hit++; if (count != 0 && hit >= count) return; diff --git a/zone/mob.h b/zone/mob.h index 8352d764e..c9ba4471d 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -971,6 +971,7 @@ public: void CalcAABonuses(StatBonuses* newbon); void ApplyAABonuses(const AA::Rank &rank, StatBonuses* newbon); void GrantAlternateAdvancementAbility(int aa_id, int points); + bool CheckAATimer(int timer); protected: void CommonDamage(Mob* other, int32 &damage, const uint16 spell_id, const SkillUseTypes attack_skill, bool &avoidable, const int8 buffslot, const bool iBuffTic); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 3c0c8fad4..5c30185aa 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -638,42 +638,23 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove snprintf(effect_desc, _EDLEN, "Group Fear Immunity"); #endif //Added client messages to give some indication this effect is active. - uint32 group_id_caster = 0; - uint32 time = spell.base[i]*10; - if(caster->IsClient()) - { - if(caster->IsGrouped()) - { - group_id_caster = GetGroup()->GetID(); - } - else if(caster->IsRaidGrouped()) - { - group_id_caster = (GetRaid()->GetGroup(CastToClient()) == 0xFFFF) ? 0 : (GetRaid()->GetGroup(CastToClient()) + 1); + // Is there a message generated? Too disgusted by raids. + uint32 time = spell.base[i] * 10 * 1000; + if (caster->IsClient()) { + if (caster->IsGrouped()) { + auto group = caster->GetGroup(); + for (int i = 0; i < 6; ++i) + if (group->members[i]) + group->members[i]->aa_timers[aaTimerWarcry].Start(time); + } else if (caster->IsRaidGrouped()) { + auto raid = caster->GetRaid(); + uint32 gid = raid->GetGroup(caster->CastToClient()); + if (gid < 12) + for (int i = 0; i < MAX_RAID_MEMBERS; ++i) + if (raid->members[i].member && raid->members[i].GroupNumber == gid) + raid->members[i].member->aa_timers[aaTimerWarcry].Start(time); } } - //old aa - //if(group_id_caster){ - // Group *g = entity_list.GetGroupByID(group_id_caster); - // uint32 time = spell.base[i]*10; - // if(g){ - // for(int gi=0; gi < 6; gi++){ - // if(g->members[gi] && g->members[gi]->IsClient()) - // { - // g->members[gi]->CastToClient()->EnableAAEffect(aaEffectWarcry , time); - // if (g->members[gi]->GetID() != caster->GetID()) - // g->members[gi]->Message(13, "You hear the war cry."); - // else - // Message(13, "You let loose a fierce war cry."); - // } - // } - // } - //} - // - //else{ - // CastToClient()->EnableAAEffect(aaEffectWarcry , time); - // Message(13, "You let loose a fierce war cry."); - //} - break; } @@ -2238,9 +2219,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Duration Rampage"); #endif - //if (caster && caster->IsClient()) { // will tidy this up later so that NPCs can duration ramp from spells too - // CastToClient()->DurationRampage(effect_value*12); - //} + aa_timers[aaTimerRampage].Start(effect_value * 10 * 1000); // Live bug, was suppose to be 1 second per value break; } diff --git a/zone/spells.cpp b/zone/spells.cpp index 4f492c343..c3e777b5c 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2280,7 +2280,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 if(rank && rank->base_ability) { ExpendAlternateAdvancementCharge(rank->base_ability->id); } - } + } else if(spell_id == casting_spell_id && casting_spell_timer != 0xFFFFFFFF) { //aa new todo: aa expendable charges here @@ -4124,13 +4124,13 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster) } return true; } - //else if (IsClient() && CastToClient()->CheckAAEffect(aaEffectWarcry)) //old aa - //{ - // Message(13, "Your are immune to fear."); - // Log.Out(Logs::Detail, Logs::Spells, "Clients has WarCry effect, immune to fear!"); - // caster->Message_StringID(MT_Shout, IMMUNE_FEAR); - // return true; - //} + else if (CheckAATimer(aaTimerWarcry)) + { + Message(13, "Your are immune to fear."); + Log.Out(Logs::Detail, Logs::Spells, "Clients has WarCry effect, immune to fear!"); + caster->Message_StringID(MT_Shout, IMMUNE_FEAR); + return true; + } } if(IsCharmSpell(spell_id)) From c0ea82f9e108a43c6bede64a6c5a05bf46917e08 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sun, 21 Jun 2015 02:58:43 -0400 Subject: [PATCH 2/2] SE_MeleeVulnerability really really is Max Mana limit Also found the cause of bard song tick increase and removed the uneeded code Also removed the IsBardSong check from GetFocusEffect, it really shouldn't be needed, but will need to keep an eye out. The focus effects should most often limit out the bard songs anyways --- common/spdat.h | 2 +- zone/bonuses.cpp | 14 -------------- zone/common.h | 1 - zone/effects.cpp | 6 ------ zone/mob.cpp | 3 --- zone/spell_effects.cpp | 21 ++++++++++++++------- 6 files changed, 15 insertions(+), 32 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index c8e039f15..938b659e1 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -544,7 +544,7 @@ typedef enum { //#define SE_SummonCorpseZone 388 // *not implemented - summons a corpse from any zone(nec AA) #define SE_FcTimerRefresh 389 // implemented - Refresh spell icons //#define SE_FcTimerLockout 390 // *not implemented - Sets recast timers to specific value, focus limited. -#define SE_MeleeVulnerability 391 // implemented [Live SPA has this as LimitManaMax however that is clearly not the effect used] +#define SE_LimitManaMax 391 // implemented #define SE_FcHealAmt 392 // implemented - Adds or removes healing from spells #define SE_FcHealPctIncoming 393 // implemented - HealRate with focus restrictions. #define SE_FcHealAmtIncoming 394 // implemented - Adds/Removes amount of healing on target by X value with foucs restrictions. diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index e90f9626a..7ff8e7fc7 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1333,10 +1333,6 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) newbon->PetMeleeMitigation += base1; break; - case SE_MeleeVulnerability: - newbon->MeleeVulnerability += base1; - break; - case SE_FactionModPct: { if ((base1 < 0) && (newbon->FactionModPct > base1)) newbon->FactionModPct = base1; @@ -3008,10 +3004,6 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne new_bonus->PetMeleeMitigation += effect_value; break; - case SE_MeleeVulnerability: - new_bonus->MeleeVulnerability += effect_value; - break; - case SE_Sanctuary: new_bonus->Sanctuary = true; break; @@ -4588,12 +4580,6 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) aabonuses.FactionModPct = effect_value; break; - case SE_MeleeVulnerability: - spellbonuses.MeleeVulnerability = effect_value; - itembonuses.MeleeVulnerability = effect_value; - aabonuses.MeleeVulnerability = effect_value; - break; - case SE_IllusionPersistence: spellbonuses.IllusionPersistence = false; itembonuses.IllusionPersistence = false; diff --git a/zone/common.h b/zone/common.h index d21a0039d..e9521cb94 100644 --- a/zone/common.h +++ b/zone/common.h @@ -400,7 +400,6 @@ struct StatBonuses { int32 Metabolism; // Food/drink consumption rates. bool Sanctuary; // Sanctuary effect, lowers place on hate list until cast on others. int32 FactionModPct; // Modifies amount of faction gained. - int32 MeleeVulnerability; // Weakness/mitigation to melee damage bool LimitToSkill[HIGHEST_SKILL+2]; // Determines if we need to search for a skill proc. uint32 SkillProc[MAX_SKILL_PROCS]; // Max number of spells containing skill_procs. uint32 SkillProcSuccess[MAX_SKILL_PROCS]; // Max number of spells containing skill_procs_success. diff --git a/zone/effects.cpp b/zone/effects.cpp index 68e68e78d..85a858c94 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -421,12 +421,6 @@ int32 Mob::GetActSpellDuration(uint16 spell_id, int32 duration) int tic_inc = 0; tic_inc = GetFocusEffect(focusSpellDurByTic, spell_id); - // unsure on the exact details, but bard songs that don't cost mana at some point get an extra tick, 60 for now - // a level 53 bard reported getting 2 tics - // bard DOTs do get this extra tick, but beneficial long bard songs don't? (invul, crescendo) - if ((IsShortDurationBuff(spell_id) || IsDetrimentalSpell(spell_id)) && IsBardSong(spell_id) && - spells[spell_id].mana == 0 && GetClass() == BARD && GetLevel() > 60) - tic_inc++; float focused = ((duration * increase) / 100.0f) + tic_inc; int ifocused = static_cast(focused); diff --git a/zone/mob.cpp b/zone/mob.cpp index 4eb87f7ad..1777bb7a3 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -3769,11 +3769,8 @@ int16 Mob::GetSkillDmgTaken(const SkillUseTypes skill_used) skilldmg_mod += itembonuses.SkillDmgTaken[HIGHEST_SKILL+1] + spellbonuses.SkillDmgTaken[HIGHEST_SKILL+1] + itembonuses.SkillDmgTaken[skill_used] + spellbonuses.SkillDmgTaken[skill_used]; - skilldmg_mod += SkillDmgTaken_Mod[skill_used] + SkillDmgTaken_Mod[HIGHEST_SKILL+1]; - skilldmg_mod += spellbonuses.MeleeVulnerability + itembonuses.MeleeVulnerability + aabonuses.MeleeVulnerability; - if(skilldmg_mod < -100) skilldmg_mod = -100; diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 5c30185aa..71740e589 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -2948,7 +2948,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove case SE_FcIncreaseNumHits: case SE_CastonFocusEffect: case SE_FcHealAmtIncoming: - case SE_MeleeVulnerability: + case SE_LimitManaMax: case SE_DoubleRangedAttack: case SE_ShieldEquipHateMod: case SE_ShieldEquipDmgMod: @@ -3356,7 +3356,7 @@ void Mob::BuffProcess() { --buffs[buffs_i].ticsremaining; - if ((buffs[buffs_i].ticsremaining == 0 && !IsShortDurationBuff(buffs[buffs_i].spellid)) || buffs[buffs_i].ticsremaining < 0) { + if ((buffs[buffs_i].ticsremaining == 0 && !(IsShortDurationBuff(buffs[buffs_i].spellid) || IsBardSong(buffs[buffs_i].spellid))) || buffs[buffs_i].ticsremaining < 0) { Log.Out(Logs::Detail, Logs::Spells, "Buff %d in slot %d has expired. Fading.", buffs[buffs_i].spellid, buffs_i); BuffFadeBySlot(buffs_i); } @@ -4301,6 +4301,11 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) LimitFailure = true; break; + case SE_LimitManaMax: + if (spell.mana > base1) + LimitFailure = true; + break; + case SE_LimitTarget: if (base1 < 0) { if (-base1 == spell.targettype) // Exclude @@ -4719,6 +4724,11 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo return 0; break; + case SE_LimitManaMax: + if (spell.mana > focus_spell.base[i]) + return 0; + break; + case SE_LimitTarget: if (focus_spell.base[i] < 0) { if (-focus_spell.base[i] == spell.targettype) // Exclude @@ -5178,11 +5188,8 @@ uint16 Client::GetSympatheticFocusEffect(focusType type, uint16 spell_id) { return 0; } -int16 Client::GetFocusEffect(focusType type, uint16 spell_id) { - - if (IsBardSong(spell_id) && type != focusFcBaseEffects) - return 0; - +int16 Client::GetFocusEffect(focusType type, uint16 spell_id) +{ int16 realTotal = 0; int16 realTotal2 = 0; int16 realTotal3 = 0;