Bard instrument mods should be more consistent with live

Changes:
	Mods are now saved for in the DB so they are loaded on zone
	This allows long duration buffs from bards that get mods to keep their mods
	Ex. Selo's, Symphony of Battle

	Instrument mods are applied to basically anything that is an instrument skill
	The only exception to this is discs (ex. Puretone is Singing but always 10)

	Singing spells from procs (Ex. Storm Blade) that are instrument skills should
	inherit their buffs instrument mod. Doom effects should also. This isn't
	implemented yet.
This commit is contained in:
Michael Cook (mackal) 2015-05-20 02:01:43 -04:00
parent 2ef0fc9342
commit ea5a1dd6f1
18 changed files with 493 additions and 510 deletions

View File

@ -1,5 +1,9 @@
EQEMu Changelog (Started on Sept 24, 2003 15:50) EQEMu Changelog (Started on Sept 24, 2003 15:50)
------------------------------------------------------- -------------------------------------------------------
== 05/20/2015 ==
demonstar55: Bard instrument mods should be more consistent with live. Zoning will keep instrument mod for long duration buffs (selo's)
Still need to have procs/doom effects to inherit the instrument mods from their source buff/whatever
== 05/18/2015 == == 05/18/2015 ==
KLS: Changed how fishing locates water to hopefully be a bit more accurate at the expense of a bit more cpu power per line cast. KLS: Changed how fishing locates water to hopefully be a bit more accurate at the expense of a bit more cpu power per line cast.

View File

@ -92,3 +92,17 @@ float EQEmu::GetSkillMeleePushForce(SkillUseTypes skill)
return 0.0f; return 0.0f;
} }
} }
bool EQEmu::IsBardInstrumentSkill(SkillUseTypes skill)
{
switch (skill) {
case SkillBrassInstruments:
case SkillSinging:
case SkillStringedInstruments:
case SkillWindInstruments:
case SkillPercussionInstruments:
return true;
default:
return false;
}
}

View File

@ -271,6 +271,7 @@ namespace EQEmu {
bool IsTradeskill(SkillUseTypes skill); bool IsTradeskill(SkillUseTypes skill);
bool IsSpecializedSkill(SkillUseTypes skill); bool IsSpecializedSkill(SkillUseTypes skill);
float GetSkillMeleePushForce(SkillUseTypes skill); float GetSkillMeleePushForce(SkillUseTypes skill);
bool IsBardInstrumentSkill(SkillUseTypes skill);
} }
#endif #endif

View File

@ -447,7 +447,7 @@ bool IsTGBCompatibleSpell(uint16 spell_id)
bool IsBardSong(uint16 spell_id) bool IsBardSong(uint16 spell_id)
{ {
if (IsValidSpell(spell_id) && spells[spell_id].classes[BARD - 1] < 255) if (IsValidSpell(spell_id) && spells[spell_id].classes[BARD - 1] < 127 && !spells[spell_id].IsDisciplineBuff)
return true; return true;
return false; return false;

View File

@ -30,7 +30,7 @@
Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
*/ */
#define CURRENT_BINARY_DATABASE_VERSION 9077 #define CURRENT_BINARY_DATABASE_VERSION 9078
#define COMPILE_DATE __DATE__ #define COMPILE_DATE __DATE__
#define COMPILE_TIME __TIME__ #define COMPILE_TIME __TIME__
#ifndef WIN32 #ifndef WIN32

View File

@ -331,6 +331,7 @@
9075|2015_02_02_logsys_packet_logs_with_dump.sql|SELECT * FROM `logsys_categories` WHERE `log_category_description` LIKE 'Packet: Server -> Client With Dump'|empty| 9075|2015_02_02_logsys_packet_logs_with_dump.sql|SELECT * FROM `logsys_categories` WHERE `log_category_description` LIKE 'Packet: Server -> Client With Dump'|empty|
9076|2015_02_04_average_coin.sql|SHOW COLUMNS FROM `loottable` WHERE Field = 'avgcoin'|contains|smallint 9076|2015_02_04_average_coin.sql|SHOW COLUMNS FROM `loottable` WHERE Field = 'avgcoin'|contains|smallint
9077|2015_02_12_zone_gravity.sql|SHOW COLUMNS FROM `zone` LIKE 'gravity'|empty| 9077|2015_02_12_zone_gravity.sql|SHOW COLUMNS FROM `zone` LIKE 'gravity'|empty|
9078|2015_05_20_BuffInstrumentMod.sql|SHOW COLUMNS FROM `character_buffs` LIKE `instrument_mod`|empty|
# Upgrade conditions: # Upgrade conditions:
# This won't be needed after this system is implemented, but it is used database that are not # This won't be needed after this system is implemented, but it is used database that are not

View File

@ -0,0 +1 @@
ALTER TABLE `character_buffs` ADD COLUMN `instrument_mod` int(10) DEFAULT 10 NOT NULL;

View File

@ -1449,7 +1449,7 @@ void Mob::CalcSpellBonuses(StatBonuses* newbon)
int buff_count = GetMaxTotalSlots(); int buff_count = GetMaxTotalSlots();
for(i = 0; i < buff_count; i++) { for(i = 0; i < buff_count; i++) {
if(buffs[i].spellid != SPELL_UNKNOWN){ if(buffs[i].spellid != SPELL_UNKNOWN){
ApplySpellsBonuses(buffs[i].spellid, buffs[i].casterlevel, newbon, buffs[i].casterid, 0, buffs[i].ticsremaining,i); ApplySpellsBonuses(buffs[i].spellid, buffs[i].casterlevel, newbon, buffs[i].casterid, 0, buffs[i].ticsremaining, i, buffs[i].instrument_mod);
if (buffs[i].numhits > 0) if (buffs[i].numhits > 0)
Numhits(true); Numhits(true);
@ -1472,8 +1472,9 @@ void Mob::CalcSpellBonuses(StatBonuses* newbon)
if (GetClass() == BARD) newbon->ManaRegen = 0; // Bards do not get mana regen from spells. if (GetClass() == BARD) newbon->ManaRegen = 0; // Bards do not get mana regen from spells.
} }
void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* new_bonus, uint16 casterId, uint8 WornType, uint32 ticsremaining, int buffslot, void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *new_bonus, uint16 casterId,
bool IsAISpellEffect, uint16 effect_id, int32 se_base, int32 se_limit, int32 se_max) uint8 WornType, uint32 ticsremaining, int buffslot, int instrument_mod,
bool IsAISpellEffect, uint16 effect_id, int32 se_base, int32 se_limit, int32 se_max)
{ {
int i, effect_value, base2, max, effectid; int i, effect_value, base2, max, effectid;
bool AdditiveWornBonus = false; bool AdditiveWornBonus = false;
@ -1511,7 +1512,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
AdditiveWornBonus = true; AdditiveWornBonus = true;
effectid = spells[spell_id].effectid[i]; effectid = spells[spell_id].effectid[i];
effect_value = CalcSpellEffectValue(spell_id, i, casterlevel, caster, ticsremaining); effect_value = CalcSpellEffectValue(spell_id, i, casterlevel, instrument_mod, caster, ticsremaining);
base2 = spells[spell_id].base2[i]; base2 = spells[spell_id].base2[i];
max = spells[spell_id].max[i]; max = spells[spell_id].max[i];
} }

View File

@ -9092,8 +9092,8 @@ bool Bot::SpellEffect(Mob* caster, uint16 spell_id, float partial) {
return Result; return Result;
} }
void Bot::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caster_level, Mob* caster) { void Bot::DoBuffTic(const Buffs_Struct &buff, int slot, Mob* caster) {
Mob::DoBuffTic(spell_id, slot, ticsremaining, caster_level, caster); Mob::DoBuffTic(buff, slot, caster);
} }
bool Bot::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot, int32 cast_time, int32 mana_cost, uint32* oSpellWillFinish, uint32 item_slot, int16 *resist_adjust) { bool Bot::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot, int32 cast_time, int32 mana_cost, uint32* oSpellWillFinish, uint32 item_slot, int16 *resist_adjust) {

View File

@ -310,7 +310,7 @@ public:
virtual int32 GetActSpellDuration(uint16 spell_id, int32 duration); virtual int32 GetActSpellDuration(uint16 spell_id, int32 duration);
virtual float GetAOERange(uint16 spell_id); virtual float GetAOERange(uint16 spell_id);
virtual bool SpellEffect(Mob* caster, uint16 spell_id, float partial = 100); virtual bool SpellEffect(Mob* caster, uint16 spell_id, float partial = 100);
virtual void DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caster_level, Mob* caster = 0); virtual void DoBuffTic(const Buffs_Struct &buff, int slot, Mob* caster = nullptr);
virtual bool CastSpell(uint16 spell_id, uint16 target_id, uint16 slot = USE_ITEM_SPELL_SLOT, int32 casttime = -1, int32 mana_cost = -1, uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF, int16 *resist_adjust = nullptr); virtual bool CastSpell(uint16 spell_id, uint16 target_id, uint16 slot = USE_ITEM_SPELL_SLOT, int32 casttime = -1, int32 mana_cost = -1, uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF, int16 *resist_adjust = nullptr);
virtual bool SpellOnTarget(uint16 spell_id, Mob* spelltar); virtual bool SpellOnTarget(uint16 spell_id, Mob* spelltar);
virtual bool IsImmuneToSpell(uint16 spell_id, Mob *caster); virtual bool IsImmuneToSpell(uint16 spell_id, Mob *caster);

View File

@ -1974,101 +1974,87 @@ int32 Client::CalcATK()
uint32 Mob::GetInstrumentMod(uint16 spell_id) const uint32 Mob::GetInstrumentMod(uint16 spell_id) const
{ {
if (GetClass() != BARD) { if (GetClass() != BARD || spells[spell_id].IsDisciplineBuff) // Puretone is Singing but doesn't get any mod
return 10; return 10;
}
uint32 effectmod = 10; uint32 effectmod = 10;
int effectmodcap = RuleI(Character, BaseInstrumentSoftCap); int effectmodcap = RuleI(Character, BaseInstrumentSoftCap);
//this should never use spell modifiers... // this should never use spell modifiers...
//if a spell grants better modifers, they are copied into the item mods // if a spell grants better modifers, they are copied into the item mods
//because the spells are supposed to act just like having the intrument. // because the spells are supposed to act just like having the intrument.
//item mods are in 10ths of percent increases // item mods are in 10ths of percent increases
// clickies (Symphony of Battle) that have a song skill don't get AA bonus for some reason
// but clickies that are songs (selo's on Composers Greaves) do get AA mod as well
switch (spells[spell_id].skill) { switch (spells[spell_id].skill) {
case SkillPercussionInstruments: case SkillPercussionInstruments:
if (itembonuses.percussionMod == 0 && spellbonuses.percussionMod == 0) { if (itembonuses.percussionMod == 0 && spellbonuses.percussionMod == 0)
effectmod = 10;
}
else if (GetSkill(SkillPercussionInstruments) == 0) {
effectmod = 10;
}
else if (itembonuses.percussionMod > spellbonuses.percussionMod) {
effectmod = itembonuses.percussionMod;
}
else {
effectmod = spellbonuses.percussionMod;
}
effectmod += aabonuses.percussionMod;
break;
case SkillStringedInstruments:
if (itembonuses.stringedMod == 0 && spellbonuses.stringedMod == 0) {
effectmod = 10;
}
else if (GetSkill(SkillStringedInstruments) == 0) {
effectmod = 10;
}
else if (itembonuses.stringedMod > spellbonuses.stringedMod) {
effectmod = itembonuses.stringedMod;
}
else {
effectmod = spellbonuses.stringedMod;
}
effectmod += aabonuses.stringedMod;
break;
case SkillWindInstruments:
if (itembonuses.windMod == 0 && spellbonuses.windMod == 0) {
effectmod = 10;
}
else if (GetSkill(SkillWindInstruments) == 0) {
effectmod = 10;
}
else if (itembonuses.windMod > spellbonuses.windMod) {
effectmod = itembonuses.windMod;
}
else {
effectmod = spellbonuses.windMod;
}
effectmod += aabonuses.windMod;
break;
case SkillBrassInstruments:
if (itembonuses.brassMod == 0 && spellbonuses.brassMod == 0) {
effectmod = 10;
}
else if (GetSkill(SkillBrassInstruments) == 0) {
effectmod = 10;
}
else if (itembonuses.brassMod > spellbonuses.brassMod) {
effectmod = itembonuses.brassMod;
}
else {
effectmod = spellbonuses.brassMod;
}
effectmod += aabonuses.brassMod;
break;
case SkillSinging:
if (itembonuses.singingMod == 0 && spellbonuses.singingMod == 0) {
effectmod = 10;
}
else if (itembonuses.singingMod > spellbonuses.singingMod) {
effectmod = itembonuses.singingMod;
}
else {
effectmod = spellbonuses.singingMod;
}
effectmod += aabonuses.singingMod + spellbonuses.Amplification;
break;
default:
effectmod = 10; effectmod = 10;
break; else if (GetSkill(SkillPercussionInstruments) == 0)
effectmod = 10;
else if (itembonuses.percussionMod > spellbonuses.percussionMod)
effectmod = itembonuses.percussionMod;
else
effectmod = spellbonuses.percussionMod;
if (IsBardSong(spell_id))
effectmod += aabonuses.percussionMod;
break;
case SkillStringedInstruments:
if (itembonuses.stringedMod == 0 && spellbonuses.stringedMod == 0)
effectmod = 10;
else if (GetSkill(SkillStringedInstruments) == 0)
effectmod = 10;
else if (itembonuses.stringedMod > spellbonuses.stringedMod)
effectmod = itembonuses.stringedMod;
else
effectmod = spellbonuses.stringedMod;
if (IsBardSong(spell_id))
effectmod += aabonuses.stringedMod;
break;
case SkillWindInstruments:
if (itembonuses.windMod == 0 && spellbonuses.windMod == 0)
effectmod = 10;
else if (GetSkill(SkillWindInstruments) == 0)
effectmod = 10;
else if (itembonuses.windMod > spellbonuses.windMod)
effectmod = itembonuses.windMod;
else
effectmod = spellbonuses.windMod;
if (IsBardSong(spell_id))
effectmod += aabonuses.windMod;
break;
case SkillBrassInstruments:
if (itembonuses.brassMod == 0 && spellbonuses.brassMod == 0)
effectmod = 10;
else if (GetSkill(SkillBrassInstruments) == 0)
effectmod = 10;
else if (itembonuses.brassMod > spellbonuses.brassMod)
effectmod = itembonuses.brassMod;
else
effectmod = spellbonuses.brassMod;
if (IsBardSong(spell_id))
effectmod += aabonuses.brassMod;
break;
case SkillSinging:
if (itembonuses.singingMod == 0 && spellbonuses.singingMod == 0)
effectmod = 10;
else if (itembonuses.singingMod > spellbonuses.singingMod)
effectmod = itembonuses.singingMod;
else
effectmod = spellbonuses.singingMod;
if (IsBardSong(spell_id))
effectmod += aabonuses.singingMod + spellbonuses.Amplification;
break;
default:
effectmod = 10;
return effectmod;
} }
effectmodcap += aabonuses.songModCap + spellbonuses.songModCap + itembonuses.songModCap; effectmodcap += aabonuses.songModCap + spellbonuses.songModCap + itembonuses.songModCap;
if (effectmod < 10) { if (effectmod < 10)
effectmod = 10; effectmod = 10;
} if (effectmod > effectmodcap)
if (effectmod > effectmodcap) {
effectmod = effectmodcap; effectmod = effectmodcap;
} Log.Out(Logs::Detail, Logs::Spells, "%s::GetInstrumentMod() spell=%d mod=%d modcap=%d\n", GetName(), spell_id,
Log.Out(Logs::Detail, Logs::Spells, "%s::GetInstrumentMod() spell=%d mod=%d modcap=%d\n", effectmod, effectmodcap);
GetName(), spell_id, effectmod, effectmodcap);
return effectmod; return effectmod;
} }

View File

@ -1494,7 +1494,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
for (int i = 0; i < max_slots; i++) { for (int i = 0; i < max_slots; i++) {
if (buffs[i].spellid != SPELL_UNKNOWN) { if (buffs[i].spellid != SPELL_UNKNOWN) {
m_pp.buffs[i].spellid = buffs[i].spellid; m_pp.buffs[i].spellid = buffs[i].spellid;
m_pp.buffs[i].bard_modifier = 10; m_pp.buffs[i].bard_modifier = buffs[i].instrument_mod;
m_pp.buffs[i].slotid = 2; m_pp.buffs[i].slotid = 2;
m_pp.buffs[i].player_id = 0x2211; m_pp.buffs[i].player_id = 0x2211;
m_pp.buffs[i].level = buffs[i].casterlevel; m_pp.buffs[i].level = buffs[i].casterlevel;

View File

@ -202,6 +202,7 @@ struct Buffs_Struct {
int32 caston_z; int32 caston_z;
int32 ExtraDIChance; int32 ExtraDIChance;
int16 RootBreakChance; //Not saved to dbase int16 RootBreakChance; //Not saved to dbase
uint32 instrument_mod;
bool persistant_buff; bool persistant_buff;
bool client; //True if the caster is a client bool client; //True if the caster is a client
bool UpdateClient; bool UpdateClient;

View File

@ -200,7 +200,7 @@ public:
bool IsBeneficialAllowed(Mob *target); bool IsBeneficialAllowed(Mob *target);
virtual int GetCasterLevel(uint16 spell_id); virtual int GetCasterLevel(uint16 spell_id);
void ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* newbon, uint16 casterID = 0, void ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* newbon, uint16 casterID = 0,
uint8 WornType = 0, uint32 ticsremaining = 0, int buffslot = -1, uint8 WornType = 0, uint32 ticsremaining = 0, int buffslot = -1, int instrument_mod = 10,
bool IsAISpellEffect = false, uint16 effect_id = 0, int32 se_base = 0, int32 se_limit = 0, int32 se_max = 0); bool IsAISpellEffect = false, uint16 effect_id = 0, int32 se_base = 0, int32 se_limit = 0, int32 se_max = 0);
void NegateSpellsBonuses(uint16 spell_id); void NegateSpellsBonuses(uint16 spell_id);
virtual float GetActSpellRange(uint16 spell_id, float range, bool IsBard = false); virtual float GetActSpellRange(uint16 spell_id, float range, bool IsBard = false);
@ -253,7 +253,7 @@ public:
//Buff //Buff
void BuffProcess(); void BuffProcess();
virtual void DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caster_level, Mob* caster = 0); virtual void DoBuffTic(const Buffs_Struct &buff, int slot, Mob* caster = nullptr);
void BuffFadeBySpellID(uint16 spell_id); void BuffFadeBySpellID(uint16 spell_id);
void BuffFadeByEffect(int effectid, int skipslot = -1); void BuffFadeByEffect(int effectid, int skipslot = -1);
void BuffFadeAll(); void BuffFadeAll();
@ -857,7 +857,7 @@ public:
virtual uint32 GetAA(uint32 aa_id) const { return(0); } virtual uint32 GetAA(uint32 aa_id) const { return(0); }
uint32 GetInstrumentMod(uint16 spell_id) const; uint32 GetInstrumentMod(uint16 spell_id) const;
int CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level = 1, Mob *caster = nullptr, int ticsremaining = 0); int CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level = 1, uint32 instrument_mod = 10, Mob *caster = nullptr, int ticsremaining = 0);
int CalcSpellEffectValue_formula(int formula, int base, int max, int caster_level, uint16 spell_id, int ticsremaining = 0); int CalcSpellEffectValue_formula(int formula, int base, int max, int caster_level, uint16 spell_id, int ticsremaining = 0);
virtual int CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2, int caster_level2, Mob* caster1 = nullptr, Mob* caster2 = nullptr, int buffslot = -1); virtual int CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2, int caster_level2, Mob* caster1 = nullptr, Mob* caster2 = nullptr, int buffslot = -1);
uint32 GetCastedSpellInvSlot() const { return casting_spell_inventory_slot; } uint32 GetCastedSpellInvSlot() const { return casting_spell_inventory_slot; }

View File

@ -2565,11 +2565,9 @@ void NPC::ApplyAISpellEffects(StatBonuses* newbon)
if (!AI_HasSpellsEffects()) if (!AI_HasSpellsEffects())
return; return;
for(int i=0; i < AIspellsEffects.size(); i++) for (int i = 0; i < AIspellsEffects.size(); i++)
{ ApplySpellsBonuses(0, 0, newbon, 0, 0, 0, -1, 10, true, AIspellsEffects[i].spelleffectid,
ApplySpellsBonuses(0, 0, newbon, 0, 0, 0,-1, AIspellsEffects[i].base, AIspellsEffects[i].limit, AIspellsEffects[i].max);
true, AIspellsEffects[i].spelleffectid, AIspellsEffects[i].base, AIspellsEffects[i].limit,AIspellsEffects[i].max);
}
return; return;
} }

View File

@ -199,7 +199,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
continue; continue;
effect = spell.effectid[i]; effect = spell.effectid[i];
effect_value = CalcSpellEffectValue(spell_id, i, caster_level, caster ? caster : this); effect_value = CalcSpellEffectValue(spell_id, i, caster_level, buffslot > -1 ? buffs[buffslot].instrument_mod : 10, caster ? caster : this);
if(spell_id == SPELL_LAY_ON_HANDS && caster && caster->GetAA(aaImprovedLayOnHands)) if(spell_id == SPELL_LAY_ON_HANDS && caster && caster->GetAA(aaImprovedLayOnHands))
effect_value = GetMaxHP(); effect_value = GetMaxHP();
@ -3029,48 +3029,44 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
return true; return true;
} }
int Mob::CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level, Mob *caster, int ticsremaining) int Mob::CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level, uint32 instrument_mod, Mob *caster,
int ticsremaining)
{ {
int formula, base, max, effect_value; int formula, base, max, effect_value;
if if (!IsValidSpell(spell_id) || effect_id < 0 || effect_id >= EFFECT_COUNT)
(
!IsValidSpell(spell_id) ||
effect_id < 0 ||
effect_id >= EFFECT_COUNT
)
return 0; return 0;
formula = spells[spell_id].formula[effect_id]; formula = spells[spell_id].formula[effect_id];
base = spells[spell_id].base[effect_id]; base = spells[spell_id].base[effect_id];
max = spells[spell_id].max[effect_id]; max = spells[spell_id].max[effect_id];
if(IsBlankSpellEffect(spell_id, effect_id)) if (IsBlankSpellEffect(spell_id, effect_id))
return 0; return 0;
effect_value = CalcSpellEffectValue_formula(formula, base, max, caster_level, spell_id, ticsremaining); effect_value = CalcSpellEffectValue_formula(formula, base, max, caster_level, spell_id, ticsremaining);
if(caster && IsBardSong(spell_id) && // this doesn't actually need to be a song to get mods, just the right skill
(spells[spell_id].effectid[effect_id] != SE_AttackSpeed) && if (EQEmu::IsBardInstrumentSkill(spells[spell_id].skill) &&
(spells[spell_id].effectid[effect_id] != SE_AttackSpeed2) && spells[spell_id].effectid[effect_id] != SE_AttackSpeed &&
(spells[spell_id].effectid[effect_id] != SE_AttackSpeed3) && spells[spell_id].effectid[effect_id] != SE_AttackSpeed2 &&
(spells[spell_id].effectid[effect_id] != SE_Lull) && spells[spell_id].effectid[effect_id] != SE_AttackSpeed3 &&
(spells[spell_id].effectid[effect_id] != SE_ChangeFrenzyRad) && spells[spell_id].effectid[effect_id] != SE_Lull &&
(spells[spell_id].effectid[effect_id] != SE_Harmony) && spells[spell_id].effectid[effect_id] != SE_ChangeFrenzyRad &&
(spells[spell_id].effectid[effect_id] != SE_CurrentMana)&& spells[spell_id].effectid[effect_id] != SE_Harmony &&
(spells[spell_id].effectid[effect_id] != SE_ManaRegen_v2)) spells[spell_id].effectid[effect_id] != SE_CurrentMana &&
{ spells[spell_id].effectid[effect_id] != SE_ManaRegen_v2) {
int oval = effect_value; int oval = effect_value;
int mod = caster->GetInstrumentMod(spell_id); int mod = ApplySpellEffectiveness(caster, spell_id, instrument_mod, true);
mod = ApplySpellEffectiveness(caster, spell_id, mod, true);
effect_value = effect_value * mod / 10; effect_value = effect_value * mod / 10;
Log.Out(Logs::Detail, Logs::Spells, "Effect value %d altered with bard modifier of %d to yeild %d", oval, mod, effect_value); Log.Out(Logs::Detail, Logs::Spells, "Effect value %d altered with bard modifier of %d to yeild %d",
oval, mod, effect_value);
} }
effect_value = mod_effect_value(effect_value, spell_id, spells[spell_id].effectid[effect_id], caster); effect_value = mod_effect_value(effect_value, spell_id, spells[spell_id].effectid[effect_id], caster);
return(effect_value); return effect_value;
} }
// generic formula calculations // generic formula calculations
@ -3365,7 +3361,7 @@ void Mob::BuffProcess()
{ {
if (buffs[buffs_i].spellid != SPELL_UNKNOWN) if (buffs[buffs_i].spellid != SPELL_UNKNOWN)
{ {
DoBuffTic(buffs[buffs_i].spellid, buffs_i, buffs[buffs_i].ticsremaining, buffs[buffs_i].casterlevel, entity_list.GetMob(buffs[buffs_i].casterid)); DoBuffTic(buffs[buffs_i], buffs_i, entity_list.GetMob(buffs[buffs_i].casterid));
// If the Mob died during DoBuffTic, then the buff we are currently processing will have been removed // If the Mob died during DoBuffTic, then the buff we are currently processing will have been removed
if(buffs[buffs_i].spellid == SPELL_UNKNOWN) if(buffs[buffs_i].spellid == SPELL_UNKNOWN)
continue; continue;
@ -3418,333 +3414,308 @@ void Mob::BuffProcess()
} }
} }
void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caster_level, Mob* caster) { void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster)
{
int effect, effect_value; int effect, effect_value;
if(!IsValidSpell(spell_id)) if (!IsValidSpell(buff.spellid))
return; return;
const SPDat_Spell_Struct &spell = spells[spell_id]; const SPDat_Spell_Struct &spell = spells[buff.spellid];
if (spell_id == SPELL_UNKNOWN) if (IsNPC()) {
return;
if(IsNPC())
{
std::vector<EQEmu::Any> args; std::vector<EQEmu::Any> args;
args.push_back(&ticsremaining); args.push_back(&buff.ticsremaining);
args.push_back(&caster_level); args.push_back(&buff.casterlevel);
args.push_back(&slot); args.push_back(&slot);
int i = parse->EventSpell(EVENT_SPELL_BUFF_TIC_NPC, CastToNPC(), nullptr, spell_id, caster ? caster->GetID() : 0, &args); int i = parse->EventSpell(EVENT_SPELL_BUFF_TIC_NPC, CastToNPC(), nullptr, buff.spellid,
if(i != 0) { caster ? caster->GetID() : 0, &args);
if (i != 0) {
return; return;
} }
} } else {
else
{
std::vector<EQEmu::Any> args; std::vector<EQEmu::Any> args;
args.push_back(&ticsremaining); args.push_back(&buff.ticsremaining);
args.push_back(&caster_level); args.push_back(&buff.casterlevel);
args.push_back(&slot); args.push_back(&slot);
int i = parse->EventSpell(EVENT_SPELL_BUFF_TIC_CLIENT, nullptr, CastToClient(), spell_id, caster ? caster->GetID() : 0, &args); int i = parse->EventSpell(EVENT_SPELL_BUFF_TIC_CLIENT, nullptr, CastToClient(), buff.spellid,
if(i != 0) { caster ? caster->GetID() : 0, &args);
if (i != 0) {
return; return;
} }
} }
// Check for non buff spell effects to fade // Check for non buff spell effects to fade
// AE melee effects // AE melee effects
if(IsClient()) if (IsClient())
CastToClient()->CheckAAEffect(aaEffectRampage); CastToClient()->CheckAAEffect(aaEffectRampage);
for (int i = 0; i < EFFECT_COUNT; i++) for (int i = 0; i < EFFECT_COUNT; i++) {
{ if (IsBlankSpellEffect(buff.spellid, i))
if(IsBlankSpellEffect(spell_id, i))
continue; continue;
effect = spell.effectid[i]; effect = spell.effectid[i];
//I copied the calculation into each case which needed it instead of // I copied the calculation into each case which needed it instead of
//doing it every time up here, since most buff effects dont need it // doing it every time up here, since most buff effects dont need it
switch(effect) switch (effect) {
{ case SE_CurrentHP: {
case SE_CurrentHP: effect_value = CalcSpellEffectValue(buff.spellid, i, buff.casterlevel, buff.instrument_mod,
{ caster, buff.ticsremaining);
effect_value = CalcSpellEffectValue(spell_id, i, caster_level, caster, ticsremaining); // Handle client cast DOTs here.
//Handle client cast DOTs here. if (caster && effect_value < 0) {
if (caster && effect_value < 0){
if (IsDetrimentalSpell(spell_id)){ if (IsDetrimentalSpell(buff.spellid)) {
if (caster->IsClient()){ if (caster->IsClient()) {
if (!caster->CastToClient()->GetFeigned()) if (!caster->CastToClient()->GetFeigned())
AddToHateList(caster, -effect_value);
}
else if (!IsClient()) //Allow NPC's to generate hate if casted on other NPC's.
AddToHateList(caster, -effect_value); AddToHateList(caster, -effect_value);
} } else if (!IsClient()) // Allow NPC's to generate hate if casted on other
// NPC's.
effect_value = caster->GetActDoTDamage(spell_id, effect_value, this); AddToHateList(caster, -effect_value);
caster->ResourceTap(-effect_value, spell_id);
effect_value = -effect_value;
Damage(caster, effect_value, spell_id, spell.skill, false, i, true);
} else if(effect_value > 0) {
// Regen spell...
// handled with bonuses
} }
break;
effect_value = caster->GetActDoTDamage(buff.spellid, effect_value, this);
caster->ResourceTap(-effect_value, buff.spellid);
effect_value = -effect_value;
Damage(caster, effect_value, buff.spellid, spell.skill, false, i, true);
} else if (effect_value > 0) {
// Regen spell...
// handled with bonuses
} }
case SE_HealOverTime: break;
{ }
effect_value = CalcSpellEffectValue(spell_id, i, caster_level); case SE_HealOverTime: {
if(caster) effect_value = CalcSpellEffectValue(buff.spellid, i, buff.casterlevel, buff.instrument_mod);
effect_value = caster->GetActSpellHealing(spell_id, effect_value); if (caster)
effect_value = caster->GetActSpellHealing(buff.spellid, effect_value);
HealDamage(effect_value, caster, spell_id); HealDamage(effect_value, caster, buff.spellid);
//healing aggro would go here; removed for now // healing aggro would go here; removed for now
break;
}
case SE_CurrentEndurance: {
// Handled with bonuses
break;
}
case SE_BardAEDot: {
effect_value =
CalcSpellEffectValue(buff.spellid, i, buff.casterlevel, buff.instrument_mod, caster);
if ((!RuleB(Spells, PreNerfBardAEDoT) && IsMoving()) || invulnerable ||
/*effect_value > 0 ||*/ DivineAura())
break; break;
if (effect_value < 0) {
effect_value = -effect_value;
if (caster) {
if (caster->IsClient() && !caster->CastToClient()->GetFeigned()) {
AddToHateList(caster, effect_value);
} else if (!caster->IsClient())
AddToHateList(caster, effect_value);
}
Damage(caster, effect_value, buff.spellid, spell.skill, false, i, true);
} else if (effect_value > 0) {
// healing spell...
HealDamage(effect_value, caster);
// healing aggro would go here; removed for now
} }
break;
}
case SE_CurrentEndurance: { case SE_Hate: {
// Handled with bonuses effect_value = CalcSpellEffectValue(buff.spellid, i, buff.casterlevel, buff.instrument_mod);
break; if (caster) {
} if (effect_value > 0) {
if (caster) {
case SE_BardAEDot: if (caster->IsClient() && !caster->CastToClient()->GetFeigned()) {
{
effect_value = CalcSpellEffectValue(spell_id, i, caster_level, caster);
if ((!RuleB(Spells, PreNerfBardAEDoT) && IsMoving()) || invulnerable || /*effect_value > 0 ||*/ DivineAura())
break;
if(effect_value < 0) {
effect_value = -effect_value;
if(caster){
if(caster->IsClient() && !caster->CastToClient()->GetFeigned()){
AddToHateList(caster, effect_value); AddToHateList(caster, effect_value);
} } else if (!caster->IsClient())
else if(!caster->IsClient())
AddToHateList(caster, effect_value); AddToHateList(caster, effect_value);
} }
Damage(caster, effect_value, spell_id, spell.skill, false, i, true); } else {
} else if(effect_value > 0) { int32 newhate = GetHateAmount(caster) + effect_value;
//healing spell... if (newhate < 1) {
HealDamage(effect_value, caster); SetHateAmountOnEnt(caster, 1);
//healing aggro would go here; removed for now } else {
} SetHateAmountOnEnt(caster, newhate);
break;
}
case SE_Hate:{
effect_value = CalcSpellEffectValue(spell_id, i, caster_level);
if(caster){
if(effect_value > 0){
if(caster){
if(caster->IsClient() && !caster->CastToClient()->GetFeigned()){
AddToHateList(caster, effect_value);
}
else if(!caster->IsClient())
AddToHateList(caster, effect_value);
}
}else{
int32 newhate = GetHateAmount(caster) + effect_value;
if (newhate < 1) {
SetHateAmountOnEnt(caster,1);
} else {
SetHateAmountOnEnt(caster,newhate);
}
} }
} }
}
break;
}
case SE_WipeHateList: {
if (IsMezSpell(buff.spellid))
break; break;
int wipechance = spells[buff.spellid].base[i];
int bonus = 0;
if (caster) {
bonus = caster->spellbonuses.IncreaseChanceMemwipe +
caster->itembonuses.IncreaseChanceMemwipe +
caster->aabonuses.IncreaseChanceMemwipe;
} }
case SE_WipeHateList: wipechance += wipechance * bonus / 100;
{
if (IsMezSpell(spell_id)) if (zone->random.Roll(wipechance)) {
if (IsAIControlled()) {
WipeHateList();
}
Message(13, "Your mind fogs. Who are my friends? Who are my enemies?... it was all so "
"clear a moment ago...");
}
break;
}
case SE_Charm: {
if (!caster || !PassCharismaCheck(caster, buff.spellid)) {
BuffFadeByEffect(SE_Charm);
}
break;
}
case SE_Root: {
/* Root formula derived from extensive personal live parses - Kayen
ROOT has a 70% chance to do a resist check to break.
*/
if (zone->random.Roll(RuleI(Spells, RootBreakCheckChance))) {
float resist_check =
ResistSpell(spells[buff.spellid].resisttype, buff.spellid, caster, 0, 0, 0, 0, true);
if (resist_check == 100)
break; break;
else if (!TryFadeEffect(slot))
int wipechance = spells[spell_id].base[i]; BuffFadeBySlot(slot);
int bonus = 0;
if (caster){
bonus = caster->spellbonuses.IncreaseChanceMemwipe +
caster->itembonuses.IncreaseChanceMemwipe +
caster->aabonuses.IncreaseChanceMemwipe;
}
wipechance += wipechance*bonus/100;
if(zone->random.Roll(wipechance))
{
if(IsAIControlled())
{
WipeHateList();
}
Message(13, "Your mind fogs. Who are my friends? Who are my enemies?... it was all so clear a moment ago...");
}
break;
} }
case SE_Charm: { break;
if (!caster || !PassCharismaCheck(caster, spell_id)) { }
BuffFadeByEffect(SE_Charm);
}
break; case SE_Fear: {
} if (zone->random.Roll(RuleI(Spells, FearBreakCheckChance))) {
float resist_check = ResistSpell(spells[buff.spellid].resisttype, buff.spellid, caster);
case SE_Root: { if (resist_check == 100)
/* Root formula derived from extensive personal live parses - Kayen
ROOT has a 70% chance to do a resist check to break.
*/
if (zone->random.Roll(RuleI(Spells, RootBreakCheckChance))) {
float resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster, 0,0,0,0,true);
if(resist_check == 100)
break;
else
if(!TryFadeEffect(slot))
BuffFadeBySlot(slot);
}
break;
}
case SE_Fear:
{
if (zone->random.Roll(RuleI(Spells, FearBreakCheckChance))) {
float resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster);
if(resist_check == 100)
break;
else
if(!TryFadeEffect(slot))
BuffFadeBySlot(slot);
}
break;
}
case SE_Hunger: {
// this procedure gets called 7 times for every once that the stamina update occurs so we add 1/7 of the subtraction.
// It's far from perfect, but works without any unnecessary buff checks to bog down the server.
if(IsClient()) {
CastToClient()->m_pp.hunger_level += 5;
CastToClient()->m_pp.thirst_level += 5;
}
break;
}
case SE_Invisibility:
case SE_InvisVsAnimals:
case SE_InvisVsUndead:
{
if(ticsremaining > 3)
{
if(!IsBardSong(spell_id))
{
double break_chance = 2.0;
if(caster)
{
break_chance -= (2 * (((double)caster->GetSkill(SkillDivination) + ((double)caster->GetLevel() * 3.0)) / 650.0));
}
else
{
break_chance -= (2 * (((double)GetSkill(SkillDivination) + ((double)GetLevel() * 3.0)) / 650.0));
}
if(zone->random.Real(0.0, 100.0) < break_chance)
{
BuffModifyDurationBySpellID(spell_id, 3);
}
}
}
}
case SE_Invisibility2:
case SE_InvisVsUndead2:
{
if(ticsremaining <= 3 && ticsremaining > 1)
{
Message_StringID(MT_Spells, INVIS_BEGIN_BREAK);
}
break;
}
case SE_InterruptCasting:
{
if(IsCasting())
{
if(zone->random.Roll(spells[spell_id].base[i]))
{
InterruptSpell();
}
}
break;
}
// These effects always trigger when they fade.
case SE_CastOnFadeEffect:
case SE_CastOnFadeEffectNPC:
case SE_CastOnFadeEffectAlways:
{
if (ticsremaining == 1)
{
SpellOnTarget(spells[spell_id].base[i], this);
}
break;
}
case SE_LocateCorpse:
{
// This is handled by the client prior to SoD.
if(IsClient() && (CastToClient()->GetClientVersionBit() & BIT_SoDAndLater))
CastToClient()->LocateCorpse();
}
case SE_TotalHP:
{
if (spell.formula[i] > 1000 && spell.formula[i] < 1999)
{
// These formulas can affect Max HP each tick
// Maybe there is a more efficient way to recalculate this for just Max HP each tic...
//CalcBonuses();
CalcSpellBonuses(&spellbonuses);
CalcMaxHP();
}
break;
}
case SE_DistanceRemoval:
{
if (spellbonuses.DistanceRemoval){
int distance = ((int(GetX()) - buffs[slot].caston_x) * (int(GetX()) - buffs[slot].caston_x)) +
((int(GetY()) - buffs[slot].caston_y) * (int(GetY()) - buffs[slot].caston_y)) +
((int(GetZ()) - buffs[slot].caston_z) * (int(GetZ()) - buffs[slot].caston_z));
if (distance > (spells[spell_id].base[i] * spells[spell_id].base[i])){
if(!TryFadeEffect(slot))
BuffFadeBySlot(slot , true);
}
break; break;
} else if (!TryFadeEffect(slot))
BuffFadeBySlot(slot);
} }
case SE_AddHateOverTimePct: break;
{ }
if (IsNPC()){
uint32 new_hate = CastToNPC()->GetHateAmount(caster) * (100 + spell.base[i]) / 100;
if (new_hate <= 0)
new_hate = 1;
CastToNPC()->SetHateAmountOnEnt(caster, new_hate); case SE_Hunger: {
// this procedure gets called 7 times for every once that the stamina update occurs so we add
// 1/7 of the subtraction.
// It's far from perfect, but works without any unnecessary buff checks to bog down the server.
if (IsClient()) {
CastToClient()->m_pp.hunger_level += 5;
CastToClient()->m_pp.thirst_level += 5;
}
break;
}
case SE_Invisibility:
case SE_InvisVsAnimals:
case SE_InvisVsUndead: {
if (buff.ticsremaining > 3) {
if (!IsBardSong(buff.spellid)) {
double break_chance = 2.0;
if (caster) {
break_chance -= (2 * (((double)caster->GetSkill(SkillDivination) +
((double)caster->GetLevel() * 3.0)) /
650.0));
} else {
break_chance -=
(2 *
(((double)GetSkill(SkillDivination) + ((double)GetLevel() * 3.0)) /
650.0));
}
if (zone->random.Real(0.0, 100.0) < break_chance) {
BuffModifyDurationBySpellID(buff.spellid, 3);
}
}
}
}
case SE_Invisibility2:
case SE_InvisVsUndead2: {
if (buff.ticsremaining <= 3 && buff.ticsremaining > 1) {
Message_StringID(MT_Spells, INVIS_BEGIN_BREAK);
}
break;
}
case SE_InterruptCasting: {
if (IsCasting()) {
if (zone->random.Roll(spells[buff.spellid].base[i])) {
InterruptSpell();
}
}
break;
}
// These effects always trigger when they fade.
case SE_CastOnFadeEffect:
case SE_CastOnFadeEffectNPC:
case SE_CastOnFadeEffectAlways: {
if (buff.ticsremaining == 1) {
SpellOnTarget(spells[buff.spellid].base[i], this);
}
break;
}
case SE_LocateCorpse: {
// This is handled by the client prior to SoD.
if (IsClient() && (CastToClient()->GetClientVersionBit() & BIT_SoDAndLater))
CastToClient()->LocateCorpse();
}
case SE_TotalHP: {
if (spell.formula[i] > 1000 && spell.formula[i] < 1999) {
// These formulas can affect Max HP each tick
// Maybe there is a more efficient way to recalculate this for just Max HP each tic...
// CalcBonuses();
CalcSpellBonuses(&spellbonuses);
CalcMaxHP();
}
break;
}
case SE_DistanceRemoval: {
if (spellbonuses.DistanceRemoval) {
int distance =
((int(GetX()) - buff.caston_x) * (int(GetX()) - buff.caston_x)) +
((int(GetY()) - buff.caston_y) * (int(GetY()) - buff.caston_y)) +
((int(GetZ()) - buff.caston_z) * (int(GetZ()) - buff.caston_z));
if (distance > (spells[buff.spellid].base[i] * spells[buff.spellid].base[i])) {
if (!TryFadeEffect(slot))
BuffFadeBySlot(slot, true);
} }
break; break;
} }
}
case SE_AddHateOverTimePct: {
if (IsNPC()) {
uint32 new_hate = CastToNPC()->GetHateAmount(caster) * (100 + spell.base[i]) / 100;
if (new_hate <= 0)
new_hate = 1;
default: CastToNPC()->SetHateAmountOnEnt(caster, new_hate);
{
// do we need to do anyting here?
} }
break;
}
default: {
// do we need to do anyting here?
}
} }
} }
} }

View File

@ -3189,6 +3189,7 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid
buffs[emptyslot].dot_rune = 0; buffs[emptyslot].dot_rune = 0;
buffs[emptyslot].ExtraDIChance = 0; buffs[emptyslot].ExtraDIChance = 0;
buffs[emptyslot].RootBreakChance = 0; buffs[emptyslot].RootBreakChance = 0;
buffs[emptyslot].instrument_mod = caster ? caster->GetInstrumentMod(spell_id) : 10;
if (level_override > 0) { if (level_override > 0) {
buffs[emptyslot].UpdateClient = true; buffs[emptyslot].UpdateClient = true;

View File

@ -2980,45 +2980,48 @@ void ZoneDatabase::SaveBuffs(Client *client) {
query = StringFormat("INSERT INTO `character_buffs` (character_id, slot_id, spell_id, " query = StringFormat("INSERT INTO `character_buffs` (character_id, slot_id, spell_id, "
"caster_level, caster_name, ticsremaining, counters, numhits, melee_rune, " "caster_level, caster_name, ticsremaining, counters, numhits, melee_rune, "
"magic_rune, persistent, dot_rune, caston_x, caston_y, caston_z, ExtraDIChance) " "magic_rune, persistent, dot_rune, caston_x, caston_y, caston_z, ExtraDIChance, "
"instrument_mod) "
"VALUES('%u', '%u', '%u', '%u', '%s', '%u', '%u', '%u', '%u', '%u', '%u', '%u', " "VALUES('%u', '%u', '%u', '%u', '%s', '%u', '%u', '%u', '%u', '%u', '%u', '%u', "
"'%i', '%i', '%i', '%i')", client->CharacterID(), index, buffs[index].spellid, "'%i', '%i', '%i', '%i', '%i')", client->CharacterID(), index, buffs[index].spellid,
buffs[index].casterlevel, buffs[index].caster_name, buffs[index].ticsremaining, buffs[index].casterlevel, buffs[index].caster_name, buffs[index].ticsremaining,
buffs[index].counters, buffs[index].numhits, buffs[index].melee_rune, buffs[index].counters, buffs[index].numhits, buffs[index].melee_rune,
buffs[index].magic_rune, buffs[index].persistant_buff, buffs[index].dot_rune, buffs[index].magic_rune, buffs[index].persistant_buff, buffs[index].dot_rune,
buffs[index].caston_x, buffs[index].caston_y, buffs[index].caston_z, buffs[index].caston_x, buffs[index].caston_y, buffs[index].caston_z,
buffs[index].ExtraDIChance); buffs[index].ExtraDIChance, buffs[index].instrument_mod);
QueryDatabase(query); QueryDatabase(query);
} }
} }
void ZoneDatabase::LoadBuffs(Client *client) { void ZoneDatabase::LoadBuffs(Client *client)
{
Buffs_Struct *buffs = client->GetBuffs(); Buffs_Struct *buffs = client->GetBuffs();
uint32 max_slots = client->GetMaxBuffSlots(); uint32 max_slots = client->GetMaxBuffSlots();
for(int index = 0; index < max_slots; ++index) for (int index = 0; index < max_slots; ++index)
buffs[index].spellid = SPELL_UNKNOWN; buffs[index].spellid = SPELL_UNKNOWN;
std::string query = StringFormat("SELECT spell_id, slot_id, caster_level, caster_name, ticsremaining, " std::string query = StringFormat("SELECT spell_id, slot_id, caster_level, caster_name, ticsremaining, "
"counters, numhits, melee_rune, magic_rune, persistent, dot_rune, " "counters, numhits, melee_rune, magic_rune, persistent, dot_rune, "
"caston_x, caston_y, caston_z, ExtraDIChance " "caston_x, caston_y, caston_z, ExtraDIChance, instrument_mod "
"FROM `character_buffs` WHERE `character_id` = '%u'", client->CharacterID()); "FROM `character_buffs` WHERE `character_id` = '%u'",
auto results = QueryDatabase(query); client->CharacterID());
if (!results.Success()) { auto results = QueryDatabase(query);
if (!results.Success()) {
return; return;
} }
for (auto row = results.begin(); row != results.end(); ++row) { for (auto row = results.begin(); row != results.end(); ++row) {
uint32 slot_id = atoul(row[1]); uint32 slot_id = atoul(row[1]);
if(slot_id >= client->GetMaxBuffSlots()) if (slot_id >= client->GetMaxBuffSlots())
continue; continue;
uint32 spell_id = atoul(row[0]); uint32 spell_id = atoul(row[0]);
if(!IsValidSpell(spell_id)) if (!IsValidSpell(spell_id))
continue; continue;
Client *caster = entity_list.GetClientByName(row[3]); Client *caster = entity_list.GetClientByName(row[3]);
uint32 caster_level = atoi(row[2]); uint32 caster_level = atoi(row[2]);
uint32 ticsremaining = atoul(row[4]); uint32 ticsremaining = atoul(row[4]);
uint32 counters = atoul(row[5]); uint32 counters = atoul(row[5]);
@ -3031,53 +3034,54 @@ void ZoneDatabase::LoadBuffs(Client *client) {
int32 caston_y = atoul(row[12]); int32 caston_y = atoul(row[12]);
int32 caston_z = atoul(row[13]); int32 caston_z = atoul(row[13]);
int32 ExtraDIChance = atoul(row[14]); int32 ExtraDIChance = atoul(row[14]);
uint32 instrument_mod = atoul(row[15]);
buffs[slot_id].spellid = spell_id; buffs[slot_id].spellid = spell_id;
buffs[slot_id].casterlevel = caster_level; buffs[slot_id].casterlevel = caster_level;
if(caster) { if (caster) {
buffs[slot_id].casterid = caster->GetID(); buffs[slot_id].casterid = caster->GetID();
strcpy(buffs[slot_id].caster_name, caster->GetName()); strcpy(buffs[slot_id].caster_name, caster->GetName());
buffs[slot_id].client = true; buffs[slot_id].client = true;
} else { } else {
buffs[slot_id].casterid = 0; buffs[slot_id].casterid = 0;
strcpy(buffs[slot_id].caster_name, ""); strcpy(buffs[slot_id].caster_name, "");
buffs[slot_id].client = false; buffs[slot_id].client = false;
} }
buffs[slot_id].ticsremaining = ticsremaining; buffs[slot_id].ticsremaining = ticsremaining;
buffs[slot_id].counters = counters; buffs[slot_id].counters = counters;
buffs[slot_id].numhits = numhits; buffs[slot_id].numhits = numhits;
buffs[slot_id].melee_rune = melee_rune; buffs[slot_id].melee_rune = melee_rune;
buffs[slot_id].magic_rune = magic_rune; buffs[slot_id].magic_rune = magic_rune;
buffs[slot_id].persistant_buff = persistent? true: false; buffs[slot_id].persistant_buff = persistent ? true : false;
buffs[slot_id].dot_rune = dot_rune; buffs[slot_id].dot_rune = dot_rune;
buffs[slot_id].caston_x = caston_x; buffs[slot_id].caston_x = caston_x;
buffs[slot_id].caston_y = caston_y; buffs[slot_id].caston_y = caston_y;
buffs[slot_id].caston_z = caston_z; buffs[slot_id].caston_z = caston_z;
buffs[slot_id].ExtraDIChance = ExtraDIChance; buffs[slot_id].ExtraDIChance = ExtraDIChance;
buffs[slot_id].RootBreakChance = 0; buffs[slot_id].RootBreakChance = 0;
buffs[slot_id].UpdateClient = false; buffs[slot_id].UpdateClient = false;
buffs[slot_id].instrument_mod = instrument_mod;
} }
max_slots = client->GetMaxBuffSlots(); max_slots = client->GetMaxBuffSlots();
for(int index = 0; index < max_slots; ++index) { for (int index = 0; index < max_slots; ++index) {
if(!IsValidSpell(buffs[index].spellid)) if (!IsValidSpell(buffs[index].spellid))
continue; continue;
for(int effectIndex = 0; effectIndex < 12; ++effectIndex) { for (int effectIndex = 0; effectIndex < 12; ++effectIndex) {
if (spells[buffs[index].spellid].effectid[effectIndex] == SE_Charm) { if (spells[buffs[index].spellid].effectid[effectIndex] == SE_Charm) {
buffs[index].spellid = SPELL_UNKNOWN; buffs[index].spellid = SPELL_UNKNOWN;
break; break;
} }
if (spells[buffs[index].spellid].effectid[effectIndex] == SE_Illusion) { if (spells[buffs[index].spellid].effectid[effectIndex] == SE_Illusion) {
if(buffs[index].persistant_buff) if (buffs[index].persistant_buff)
break; break;
buffs[index].spellid = SPELL_UNKNOWN; buffs[index].spellid = SPELL_UNKNOWN;
break; break;
} }
} }