diff --git a/changelog.txt b/changelog.txt index 1e371109d..59f39c327 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,16 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 07/5/2014 == +Kayen: Updated SE_Sanctuary - Adjust way hate lowering effect worked to be more accurate +Kayen: Updated SE_SympatheticProc - Revised proc rate formula to be accurate to live. +Sympathetic foci on items with proc rate mod will now benefit from that modifier. +Sympathetic foci can now be placed on AA's (This should always be slot1 in the AA) +Kayen: Implemented SE_IllusionPersistence- Allows illusions to last until you die or the illusion is forcibly removed. +Kayen: Added rule 'PreNerftBardAEDot' for SE_BardAEDot to allow it to once again do damage to moving targets. (Set to true) + +Required SQL: utils/sql/git/required/2014_07_04_AA_Update.sql + + == 07/2/2014 == Kayen: Implemented SE_Sanctuary - Places caster at bottom hate list, effect fades if caster cast spell on targets other than self. Kayen: Implemented SE_ResourceTap - Coverts a percent of dmg from dmg spells(DD/DoT) to hp/mana/end. diff --git a/common/ruletypes.h b/common/ruletypes.h index 6f5fe0769..8c9ac543e 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -310,6 +310,7 @@ RULE_INT ( Spells, FRProjectileItem_SOF, 80684) // Item id for SOF clients for F RULE_INT ( Spells, FRProjectileItem_NPC, 80684) // Item id for NPC Fire 'spell projectile'. RULE_BOOL ( Spells, UseLiveSpellProjectileGFX, false) // Use spell projectile graphics set in the spells_new table (player_1). Server must be using UF+ spell file. RULE_BOOL ( Spells, FocusCombatProcs, false) //Allow all combat procs to receive focus effects. +RULE_BOOL ( Spells, PreNerfBardAEDoT, false) //Allow bard AOE dots to damage targets when moving. RULE_CATEGORY_END() RULE_CATEGORY( Combat ) diff --git a/common/spdat.h b/common/spdat.h index 02dca8ff7..42091338a 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -385,7 +385,7 @@ typedef enum { #define SE_ChannelChanceSpells 235 // implemented[AA] - chance to channel from SPELLS *No longer used on live. //#define SE_FreePet 236 // not used #define SE_GivePetGroupTarget 237 // implemented[AA] - (Pet Affinity) -//#define SE_IllusionPersistence 238 // *not implemented - lends persistence to your illusionary disguises, causing them to last until you die or the illusion is forcibly removed. +#define SE_IllusionPersistence 238 // implemented - lends persistence to your illusionary disguises, causing them to last until you die or the illusion is forcibly removed. //#define SE_FeignedCastOnChance 239 // *not implemented as bonus - ability gives you an increasing chance for your feigned deaths to not be revealed by spells cast upon you. //#define SE_StringUnbreakable 240 // not used [Likely related to above - you become immune to feign breaking on a resisted spell and have a good chance of feigning through a spell that successfully lands upon you.] #define SE_ImprovedReclaimEnergy 241 // implemented - increase the amount of mana returned to you when reclaiming your pet. diff --git a/utils/sql/git/required/2014_07_04_AA_Updates.sql b/utils/sql/git/required/2014_07_04_AA_Updates.sql new file mode 100644 index 000000000..a943e8c15 --- /dev/null +++ b/utils/sql/git/required/2014_07_04_AA_Updates.sql @@ -0,0 +1,6 @@ +-- AA Permanent Illusion +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('158', '1', '238', '1', '0'); + +-- AA Sanctuary +INSERT INTO `altadv_vars` (`skill_id`, `name`, `cost`, `max_level`, `hotkey_sid`, `hotkey_sid2`, `title_sid`, `desc_sid`, `type`, `spellid`, `prereq_skill`, `prereq_minpoints`, `spell_type`, `spell_refresh`, `classes`, `berserker`, `class_type`, `cost_inc`, `aa_expansion`, `special_category`, `sof_type`, `sof_cost_inc`, `sof_max_level`, `sof_next_skill`, `clientver`, `account_time_required`, `sof_current_level`,`sof_next_id`,`level_inc`) VALUES ('1209', 'Sanctuary', '12', '1', '1209', '4294967295', '1209', '1209', '3', '5912', '0', '0', '14', '4320', '4', '0', '70', '0', '8', '4294967295', '3', '0', '1', '1209', '1', '0', '0', '0', '0'); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES ('1209', '0', '4320', '5912', '1', '0', '0', '0', '0', '0', '0', '0'); diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 48a3d95e1..b21eeaa0a 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1329,6 +1329,10 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon) break; } + case SE_IllusionPersistence: + newbon->IllusionPersistence = true; + break; + } } } @@ -2876,6 +2880,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne break; } + case SE_IllusionPersistence: + newbon->IllusionPersistence = true; + break; + //Special custom cases for loading effects on to NPC from 'npc_spels_effects' table if (IsAISpellEffect) { @@ -4356,6 +4364,17 @@ 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 = effect_value; + itembonuses.IllusionPersistence = effect_value; + aabonuses.IllusionPersistence = effect_value; + break; } } } diff --git a/zone/bot.cpp b/zone/bot.cpp index e961bd7ec..a1a31740e 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -7641,10 +7641,8 @@ int16 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel { if(bottype == BotfocusSympatheticProc) { - float ProcChance, ProcBonus; - int16 ProcRateMod = focus_spell.base[i]; //Baseline is 100 for most Sympathetic foci - int32 cast_time = GetActSpellCasttime(spell_id, spells[spell_id].cast_time); - GetSympatheticProcChances(ProcBonus, ProcChance, cast_time, ProcRateMod); + + float ProcChance = GetSympatheticProcChances(spell_id, focus_spell.base[i]); if(MakeRandomFloat(0, 1) <= ProcChance) value = focus_id; diff --git a/zone/client.h b/zone/client.h index 9e2503df2..e26465628 100644 --- a/zone/client.h +++ b/zone/client.h @@ -776,7 +776,10 @@ public: int GetSpentAA() { return m_pp.aapoints_spent; } void RefundAA(); void IncrementAA(int aa_id); - + int32 GetAAEffectDataBySlot(uint32 aa_ID, uint32 slot_id, bool GetEffect, bool GetBase1, bool GetBase2); + int32 GetAAEffectid(uint32 aa_ID, uint32 slot_id) { return GetAAEffectDataBySlot(aa_ID, slot_id, true, false,false); } + int32 GetAABase1(uint32 aa_ID, uint32 slot_id) { return GetAAEffectDataBySlot(aa_ID, slot_id, false, true,false); } + int32 GetAABase2(uint32 aa_ID, uint32 slot_id) { return GetAAEffectDataBySlot(aa_ID, slot_id, false, false,true); } int16 acmod(); // Item methods diff --git a/zone/common.h b/zone/common.h index e3a9dfb09..98e2ebcec 100644 --- a/zone/common.h +++ b/zone/common.h @@ -431,7 +431,7 @@ struct StatBonuses { uint32 Assassinate[2]; // Assassinate AA (Massive dmg vs humaniod w/ assassinate) 0= ? 1= Dmg uint8 AssassinateLevel; // Max Level Assassinate will be effective at. int32 PetMeleeMitigation; // Add AC to owner's pet. - + bool IllusionPersistence; // Causes illusions not to fade. }; typedef struct diff --git a/zone/hate_list.cpp b/zone/hate_list.cpp index 68dd2a6cb..abe32b5a7 100644 --- a/zone/hate_list.cpp +++ b/zone/hate_list.cpp @@ -308,6 +308,16 @@ Mob *HateList::GetTop(Mob *center) } } + if (cur->ent->Sanctuary()) { + if(hate == -1) + { + top = cur->ent; + hate = 1; + } + ++iterator; + continue; + } + if(cur->ent->DivineAura() || cur->ent->IsMezzed() || cur->ent->IsFeared()){ if(hate == -1) { diff --git a/zone/mob.cpp b/zone/mob.cpp index 59e02568b..93b9f5047 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -2761,6 +2761,14 @@ bool Mob::DivineAura() const return false; } +bool Mob::Sanctuary() const +{ + if (spellbonuses.Sanctuary) + return true; + + return false; +} + int16 Mob::GetResist(uint8 type) const { if (IsNPC()) diff --git a/zone/mob.h b/zone/mob.h index 0476a20dc..cb44dd80c 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -820,6 +820,7 @@ public: void SetNextIncHPEvent( int inchpevent ); bool DivineAura() const; + bool Sanctuary() const; bool HasNPCSpecialAtk(const char* parse); int GetSpecialAbility(int ability); @@ -1009,7 +1010,7 @@ protected: Map::Vertex UpdatePath(float ToX, float ToY, float ToZ, float Speed, bool &WaypointChange, bool &NodeReached); void PrintRoute(); - virtual float GetSympatheticProcChances(float &ProcBonus, float &ProcChance, int32 cast_time, int16 ProcRateMod); + virtual float GetSympatheticProcChances(uint16 spell_id, int16 ProcRateMod, int32 ItemProcRate = 0); enum {MAX_PROCS = 4}; tProc PermaProcs[MAX_PROCS]; diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 61333edfd..b92ea524c 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -1430,7 +1430,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) for(int x = 0; x < 7; x++){ SendWearChange(x); } - if(caster && caster->GetAA(aaPermanentIllusion)) + if(caster && (caster->spellbonuses.IllusionPersistence || caster->aabonuses.IllusionPersistence + || caster->itembonuses.IllusionPersistence)) buffs[buffslot].persistant_buff = 1; else buffs[buffslot].persistant_buff = 0; @@ -2329,7 +2330,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) if (buffslot >= 0) break; //This effect does no damage if target is moving. - if (IsMoving()) + if (!RuleB(Spells, PreNerfBardAEDoT) && IsMoving()) break; // for offensive spells check if we have a spell rune on @@ -2735,22 +2736,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) break; } - case SE_Sanctuary: - { - std::list npc_list; - entity_list.GetNPCList(npc_list); - - for(std::list::iterator itr = npc_list.begin(); itr != npc_list.end(); ++itr) { - - NPC* npc = *itr; - - if (npc && npc->CheckAggro(this)) - npc->SetHate(caster, 1); - - } - break; - } - // Handled Elsewhere case SE_ImmuneFleeing: case SE_NegateSpellEffect: @@ -2984,6 +2969,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) case SE_AssassinateLevel: case SE_FactionModPct: case SE_LimitSpellClass: + case SE_Sanctuary: { break; } @@ -3403,7 +3389,7 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste { effect_value = CalcSpellEffectValue(spell_id, i, caster_level, caster); - if (IsMoving() || invulnerable || /*effect_value > 0 ||*/ DivineAura()) + if ((!RuleB(Spells, PreNerfBardAEDoT) && IsMoving()) || invulnerable || /*effect_value > 0 ||*/ DivineAura()) break; if(effect_value < 0) { @@ -3630,21 +3616,6 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste break; } - case SE_Sanctuary: - { - std::list npc_list; - entity_list.GetNPCList(npc_list); - - for(std::list::iterator itr = npc_list.begin(); itr != npc_list.end(); ++itr) { - - NPC* npc = *itr; - - if (npc && npc->CheckAggro(this)) - npc->SetHate(caster, 1); - - } - break; - } default: { @@ -4084,39 +4055,41 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) CalcBonuses(); } -/* No longer used. -int16 Client::CalcAAFocusEffect(focusType type, uint16 focus_spell, uint16 spell_id) +int32 Client::GetAAEffectDataBySlot(uint32 aa_ID, uint32 slot_id, bool GetEffect, bool GetBase1, bool GetBase2) { - uint32 slots = 0; - uint32 aa_AA = 0; - uint32 aa_value = 0; + int32 aa_effects_data[3] = { 0 }; + uint32 effect = 0; + int32 base1 = 0; + int32 base2 = 0; + uint32 slot = 0; - int32 value = 0; - // Iterate through all of the client's AAs - for (int i = 0; i < MAX_PP_AA_ARRAY; i++) + + std::map >::const_iterator find_iter = aa_effects.find(aa_ID); + if(find_iter == aa_effects.end()) + return 0; + + for (std::map::const_iterator iter = aa_effects[aa_ID].begin(); iter != aa_effects[aa_ID].end(); ++iter) { - aa_AA = this->aa[i]->AA; - aa_value = this->aa[i]->value; - if (aa_AA > 0 || aa_value > 0) - { - slots = zone->GetTotalAALevels(aa_AA); - if (slots > 0) - for(int j = 1;j <= slots; j++) - { - switch (aa_effects[aa_AA][j].skill_id) - { - case SE_TriggerOnCast: - // If focus_spell matches the spell listed in the DB, load these restrictions - if(type == focusTriggerOnCast && focus_spell == aa_effects[aa_AA][j].base1) - value = CalcAAFocus(type, aa_AA, spell_id); - break; - } - } + effect = iter->second.skill_id; + base1 = iter->second.base1; + base2 = iter->second.base2; + slot = iter->second.slot; + + if (slot && slot == slot_id) { + + if (GetEffect) + return effect; + + if (GetBase1) + return base1; + + if (GetBase2) + return base2; } } - return value; + + return 0; } -*/ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id) @@ -4496,9 +4469,11 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id) value = base1; break; - case SE_SympatheticProc: - //No AA support at this time. - break; + //Note if using these as AA, make sure this is first focus used. + case SE_SympatheticProc: + if(type == focusSympatheticProc) + value = base2; + break; case SE_FcDamageAmt: if(type == focusFcDamageAmt) @@ -4962,16 +4937,7 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo case SE_SympatheticProc: if(type == focusSympatheticProc) { - float ProcChance, ProcBonus; - int16 ProcRateMod = focus_spell.base[i]; //Baseline is 100 for most Sympathetic foci - int32 cast_time = GetActSpellCasttime(spell_id, spells[spell_id].cast_time); - GetSympatheticProcChances(ProcBonus, ProcChance, cast_time, ProcRateMod); - - if(MakeRandomFloat(0, 1) <= ProcChance) - value = focus_id; - - else - value = 0; + value = focus_id; } break; @@ -5089,8 +5055,8 @@ int16 Client::GetSympatheticFocusEffect(focusType type, uint16 spell_id) { return 0; uint16 proc_spellid = 0; - uint8 SizeProcList = 0; uint8 MAX_SYMPATHETIC = 10; + float ProcChance = 0.0f; std::vector SympatheticProcList; @@ -5101,7 +5067,7 @@ int16 Client::GetSympatheticFocusEffect(focusType type, uint16 spell_id) { for(int x=0; x<=21; x++) { - if (SizeProcList > MAX_SYMPATHETIC) + if (SympatheticProcList.size() > MAX_SYMPATHETIC) continue; TempItem = nullptr; @@ -5109,20 +5075,22 @@ int16 Client::GetSympatheticFocusEffect(focusType type, uint16 spell_id) { if (!ins) continue; TempItem = ins->GetItem(); - if (TempItem && TempItem->Focus.Effect > 0 && TempItem->Focus.Effect != SPELL_UNKNOWN) { + if (TempItem && TempItem->Focus.Effect > 0 && IsValidSpell(TempItem->Focus.Effect)) { - proc_spellid = CalcFocusEffect(type, TempItem->Focus.Effect, spell_id); + proc_spellid = CalcFocusEffect(type, TempItem->Focus.Effect, spell_id); + + if (IsValidSpell(proc_spellid)){ - if (proc_spellid > 0) - { - SympatheticProcList.push_back(proc_spellid); - SizeProcList = SympatheticProcList.size(); - } + ProcChance = GetSympatheticProcChances(spell_id, spells[TempItem->Focus.Effect].base[0], TempItem->ProcRate); + + if(MakeRandomFloat(0, 1) <= ProcChance) + SympatheticProcList.push_back(proc_spellid); + } } for(int y = 0; y < MAX_AUGMENT_SLOTS; ++y) { - if (SizeProcList > MAX_SYMPATHETIC) + if (SympatheticProcList.size() > MAX_SYMPATHETIC) continue; ItemInst *aug = nullptr; @@ -5130,14 +5098,16 @@ int16 Client::GetSympatheticFocusEffect(focusType type, uint16 spell_id) { if(aug) { const Item_Struct* TempItemAug = aug->GetItem(); - if (TempItemAug && TempItemAug->Focus.Effect > 0 && TempItemAug->Focus.Effect != SPELL_UNKNOWN) { + if (TempItemAug && TempItemAug->Focus.Effect > 0 && IsValidSpell(TempItemAug->Focus.Effect)) { proc_spellid = CalcFocusEffect(type, TempItemAug->Focus.Effect, spell_id); - if (proc_spellid > 0) - { - SympatheticProcList.push_back(proc_spellid); - SizeProcList = SympatheticProcList.size(); + if (IsValidSpell(proc_spellid)){ + + ProcChance = GetSympatheticProcChances(spell_id, spells[TempItem->Focus.Effect].base[0], TempItemAug->ProcRate); + + if(MakeRandomFloat(0, 1) <= ProcChance) + SympatheticProcList.push_back(proc_spellid); } } } @@ -5152,26 +5122,57 @@ int16 Client::GetSympatheticFocusEffect(focusType type, uint16 spell_id) { uint32 buff_max = GetMaxTotalSlots(); for (buff_slot = 0; buff_slot < buff_max; buff_slot++) { - if (SizeProcList > MAX_SYMPATHETIC) + if (SympatheticProcList.size() > MAX_SYMPATHETIC) continue; focusspellid = buffs[buff_slot].spellid; - if (focusspellid == 0 || focusspellid >= SPDAT_RECORDS) + if (IsValidSpell(focusspellid)) continue; proc_spellid = CalcFocusEffect(type, focusspellid, spell_id); - if (proc_spellid > 0) - { - SympatheticProcList.push_back(proc_spellid); - SizeProcList = SympatheticProcList.size(); - } + if (IsValidSpell(proc_spellid)){ + + ProcChance = GetSympatheticProcChances(spell_id, spells[focusspellid].base[0]); + + if(MakeRandomFloat(0, 1) <= ProcChance) + SympatheticProcList.push_back(proc_spellid); + } } } - if (SizeProcList > 0) + /*Note: At present, ff designing custom AA to have a sympathetic proc effect, only use one focus + effect within the aa_effects data for each AA*[No live AA's use this effect to my knowledge]*/ + if (aabonuses.FocusEffects[type]){ + + uint32 aa_AA = 0; + uint32 aa_value = 0; + + for (int i = 0; i < MAX_PP_AA_ARRAY; i++) + { + aa_AA = this->aa[i]->AA; + aa_value = this->aa[i]->value; + if (aa_AA < 1 || aa_value < 1) + continue; + + if (SympatheticProcList.size() > MAX_SYMPATHETIC) + continue; + + proc_spellid = CalcAAFocus(type, aa_AA, spell_id); + + if (IsValidSpell(proc_spellid)){ + + ProcChance = GetSympatheticProcChances(spell_id, GetAABase1(aa_AA, 1)); + + if(MakeRandomFloat(0, 1) <= ProcChance) + SympatheticProcList.push_back(proc_spellid); + } + } + } + + if (SympatheticProcList.size() > 0) { - uint8 random = MakeRandomInt(0, SizeProcList-1); + uint8 random = MakeRandomInt(0, SympatheticProcList.size()-1); int FinalSympatheticProc = SympatheticProcList[random]; SympatheticProcList.clear(); return FinalSympatheticProc; @@ -5674,17 +5675,31 @@ bool Mob::AffectedBySpellExcludingSlot(int slot, int effect) return false; } -float Mob::GetSympatheticProcChances(float &ProcBonus, float &ProcChance, int32 cast_time, int16 ProcRateMod) { +float Mob::GetSympatheticProcChances(uint16 spell_id, int16 ProcRateMod, int32 ItemProcRate) { - ProcChance = 0; + float ProcChance = 0.0f; + int32 total_cast_time = 0; + float cast_time_mod = 0.0f; + ProcRateMod -= 100; + - if(cast_time > 0) - { - ProcChance = ((float)cast_time * RuleR(Casting, AvgSpellProcsPerMinute) / 60000.0f); - ProcChance = ProcChance * (float)(ProcRateMod/100); - } - return ProcChance; -} + if (spells[spell_id].recast_time >= spells[spell_id].recovery_time) + total_cast_time = spells[spell_id].recast_time + spells[spell_id].cast_time; + else + total_cast_time = spells[spell_id].recovery_time + spells[spell_id].cast_time; + + if (total_cast_time > 0 && total_cast_time <= 2500) + cast_time_mod = 0.25f; + else if (total_cast_time > 2500 && total_cast_time < 7000) + cast_time_mod = 0.167*((static_cast(total_cast_time) - 1000.0f)/1000.0f); + else + cast_time_mod = static_cast(total_cast_time) / 7000.0f; + + ProcChance = (RuleR(Casting, AvgSpellProcsPerMinute)/100.0f) * (static_cast(100.0f + ProcRateMod) / 10.0f) + * cast_time_mod * (static_cast(100.0f + ItemProcRate)/100.0f); + + return ProcChance; + } bool Mob::DoHPToManaCovert(uint16 mana_cost) {