Merge branch 'master' of github.com:EQEmu/Server

This commit is contained in:
KimLS
2014-02-22 15:04:18 -08:00
43 changed files with 1292 additions and 710 deletions
+57 -69
View File
@@ -1054,7 +1054,8 @@ void Mob::AI_Process() {
SetTarget(hate_list.GetTop(this));
}
} else {
SetTarget(hate_list.GetTop(this));
if (!ImprovedTaunt())
SetTarget(hate_list.GetTop(this));
}
}
@@ -1141,30 +1142,26 @@ void Mob::AI_Process() {
Attack(target, 13);
}
if (target)
{
if (target) {
//we use this random value in three comparisons with different
//thresholds, and if its truely random, then this should work
//out reasonably and will save us compute resources.
int32 RandRoll = MakeRandomInt(0, 99);
if (CanThisClassDoubleAttack()
//check double attack, this is NOT the same rules that clients use...
&& RandRoll < (GetLevel() + NPCDualAttackModifier))
{
if ((CanThisClassDoubleAttack() || GetSpecialAbility(SPECATK_TRIPLE)
|| GetSpecialAbility(SPECATK_QUAD))
//check double attack, this is NOT the same rules that clients use...
&& RandRoll < (GetLevel() + NPCDualAttackModifier)) {
Attack(target, 13);
// lets see if we can do a triple attack with the main hand
//pets are excluded from triple and quads...
if (GetSpecialAbility(SPECATK_TRIPLE)
&& !IsPet() && RandRoll < (GetLevel()+NPCTripleAttackModifier))
{
if ((GetSpecialAbility(SPECATK_TRIPLE) || GetSpecialAbility(SPECATK_QUAD))
&& !IsPet() && RandRoll < (GetLevel() + NPCTripleAttackModifier)) {
Attack(target, 13);
// now lets check the quad attack
if (GetSpecialAbility(SPECATK_QUAD)
&& RandRoll < (GetLevel() + NPCQuadAttackModifier))
{
&& RandRoll < (GetLevel() + NPCQuadAttackModifier)) {
Attack(target, 13);
}
}
}
}
@@ -1173,48 +1170,41 @@ void Mob::AI_Process() {
int flurry_chance = GetSpecialAbilityParam(SPECATK_FLURRY, 0);
flurry_chance = flurry_chance > 0 ? flurry_chance : RuleI(Combat, NPCFlurryChance);
ExtraAttackOptions opts;
int cur = GetSpecialAbilityParam(SPECATK_FLURRY, 2);
if(cur > 0) {
opts.damage_percent = cur / 100.0f;
}
if (MakeRandomInt(0, 99) < flurry_chance) {
ExtraAttackOptions opts;
int cur = GetSpecialAbilityParam(SPECATK_FLURRY, 2);
if (cur > 0)
opts.damage_percent = cur / 100.0f;
cur = GetSpecialAbilityParam(SPECATK_FLURRY, 3);
if(cur > 0) {
opts.damage_flat = cur;
}
cur = GetSpecialAbilityParam(SPECATK_FLURRY, 3);
if (cur > 0)
opts.damage_flat = cur;
cur = GetSpecialAbilityParam(SPECATK_FLURRY, 4);
if(cur > 0) {
opts.armor_pen_percent = cur / 100.0f;
}
cur = GetSpecialAbilityParam(SPECATK_FLURRY, 4);
if (cur > 0)
opts.armor_pen_percent = cur / 100.0f;
cur = GetSpecialAbilityParam(SPECATK_FLURRY, 5);
if(cur > 0) {
opts.armor_pen_flat = cur;
}
cur = GetSpecialAbilityParam(SPECATK_FLURRY, 5);
if (cur > 0)
opts.armor_pen_flat = cur;
cur = GetSpecialAbilityParam(SPECATK_FLURRY, 6);
if(cur > 0) {
opts.crit_percent = cur / 100.0f;
}
cur = GetSpecialAbilityParam(SPECATK_FLURRY, 6);
if (cur > 0)
opts.crit_percent = cur / 100.0f;
cur = GetSpecialAbilityParam(SPECATK_FLURRY, 7);
if(cur > 0) {
opts.crit_flat = cur;
}
cur = GetSpecialAbilityParam(SPECATK_FLURRY, 7);
if (cur > 0)
opts.crit_flat = cur;
if (MakeRandomInt(0, 99) < flurry_chance)
Flurry(&opts);
}
}
if (IsPet()) {
Mob *owner = GetOwner();
if (owner){
int16 flurry_chance = owner->aabonuses.PetFlurry + owner->spellbonuses.PetFlurry + owner->itembonuses.PetFlurry;
if (owner) {
int16 flurry_chance = owner->aabonuses.PetFlurry +
owner->spellbonuses.PetFlurry + owner->itembonuses.PetFlurry;
if (flurry_chance && (MakeRandomInt(0, 99) < flurry_chance))
Flurry(nullptr);
}
@@ -1294,7 +1284,7 @@ void Mob::AI_Process() {
if(cur > 0) {
opts.crit_flat = cur;
}
AreaRampage(&opts);
}
}
@@ -1997,59 +1987,57 @@ bool Mob::Flurry(ExtraAttackOptions *opts)
bool Mob::AddRampage(Mob *mob)
{
if(!mob)
if (!mob)
return false;
if (!GetSpecialAbility(SPECATK_RAMPAGE))
return false;
for (int i = 0; i < RampageArray.size(); i++)
{
// if name is already on the list dont add it again
if (strcasecmp(mob->GetName(), RampageArray[i].c_str()) == 0)
for (int i = 0; i < RampageArray.size(); i++) {
// if Entity ID is already on the list don't add it again
if (mob->GetID() == RampageArray[i])
return false;
}
std::string r_name = mob->GetName();
RampageArray.push_back(r_name);
RampageArray.push_back(mob->GetID());
return true;
}
void Mob::ClearRampage(){
void Mob::ClearRampage()
{
RampageArray.clear();
}
bool Mob::Rampage(ExtraAttackOptions *opts)
{
int index_hit = 0;
if (!IsPet()) {
if (!IsPet())
entity_list.MessageClose_StringID(this, true, 200, MT_NPCRampage, NPC_RAMPAGE, GetCleanName());
} else {
else
entity_list.MessageClose_StringID(this, true, 200, MT_PetFlurry, NPC_RAMPAGE, GetCleanName());
}
int rampage_targets = GetSpecialAbilityParam(SPECATK_RAMPAGE, 1);
rampage_targets = rampage_targets > 0 ? rampage_targets : RuleI(Combat, MaxRampageTargets);
for (int i = 0; i < RampageArray.size(); i++)
{
if(index_hit >= rampage_targets)
if (rampage_targets == 0) // if set to 0 or not set in the DB
rampage_targets = RuleI(Combat, DefaultRampageTargets);
if (rampage_targets > RuleI(Combat, MaxRampageTargets))
rampage_targets = RuleI(Combat, MaxRampageTargets);
for (int i = 0; i < RampageArray.size(); i++) {
if (index_hit >= rampage_targets)
break;
// range is important
Mob *m_target = entity_list.GetMob(RampageArray[i].c_str());
if(m_target)
{
if(m_target == GetTarget())
Mob *m_target = entity_list.GetMob(RampageArray[i]);
if (m_target) {
if (m_target == GetTarget())
continue;
if (CombatRange(m_target))
{
if (CombatRange(m_target)) {
Attack(m_target, 13, false, false, false, opts);
index_hit++;
}
}
}
if(index_hit < rampage_targets) {
if (RuleB(Combat, RampageHitsTarget) && index_hit < rampage_targets)
Attack(GetTarget(), 13, false, false, false, opts);
}
return true;
}
+6
View File
@@ -46,6 +46,12 @@ void QuestParserCollection::RegisterQuestInterface(QuestInterface *qi, std::stri
_load_precedence.push_back(qi);
}
void QuestParserCollection::ClearInterfaces() {
_interfaces.clear();
_extensions.clear();
_load_precedence.clear();
}
void QuestParserCollection::AddVar(std::string name, std::string val) {
std::list<QuestInterface*>::iterator iter = _load_precedence.begin();
while(iter != _load_precedence.end()) {
+2 -1
View File
@@ -39,7 +39,8 @@ public:
~QuestParserCollection();
void RegisterQuestInterface(QuestInterface *qi, std::string ext);
void UnRegisterQuestInterface(QuestInterface *qi, std::string ext);
void ClearInterfaces();
void AddVar(std::string name, std::string val);
void Init();
void ReloadQuests(bool reset_timers = true);
+3 -6
View File
@@ -1257,7 +1257,7 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, bool isproc)
break;
}
case SE_ReduceHate:
case SE_Calm: {
case SE_InstantHate: {
nonModifiedAggro = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id);
break;
}
@@ -1282,9 +1282,6 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, bool isproc)
if (IsClient())
HateMod += CastToClient()->GetFocusEffect(focusSpellHateMod, spell_id);
//Live AA - Spell casting subtlety
HateMod += aabonuses.hatemod + spellbonuses.hatemod + itembonuses.hatemod;
AggroAmount = (AggroAmount * HateMod) / 100;
//made up number probably scales a bit differently on live but it seems like it will be close enough
@@ -1405,7 +1402,7 @@ bool Mob::PassCharismaCheck(Mob* caster, Mob* spellTarget, uint16 spell_id) {
return true;
//1: The mob has a default 25% chance of being allowed a resistance check against the charm.
if (MakeRandomInt(0, 100) > RuleI(Spells, CharmBreakCheckChance))
if (MakeRandomInt(0, 99) > RuleI(Spells, CharmBreakCheckChance))
return true;
//2: The mob makes a resistance check against the charm
@@ -1419,7 +1416,7 @@ bool Mob::PassCharismaCheck(Mob* caster, Mob* spellTarget, uint16 spell_id) {
//3: At maxed ability, Total Domination has a 50% chance of preventing the charm break that otherwise would have occurred.
uint16 TotalDominationBonus = caster->aabonuses.CharmBreakChance + caster->spellbonuses.CharmBreakChance + caster->itembonuses.CharmBreakChance;
if (MakeRandomInt(0, 100) < TotalDominationBonus)
if (MakeRandomInt(0, 99) < TotalDominationBonus)
return true;
}
+217 -153
View File
@@ -199,11 +199,6 @@ bool Mob::CheckHitChance(Mob* other, SkillUseTypes skillinuse, int Hand, int16 c
if(IsClient() && other->IsClient())
pvpmode = true;
CheckNumHitsRemaining(1);
if (attacker)
attacker->CheckNumHitsRemaining(2);
if (chance_mod >= 10000)
return true;
@@ -541,17 +536,16 @@ bool Mob::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte)
void Mob::MeleeMitigation(Mob *attacker, int32 &damage, int32 minhit, ExtraAttackOptions *opts)
{
if(damage <= 0)
if (damage <= 0)
return;
Mob* defender = this;
float aa_mit = 0;
float aa_mit = (aabonuses.CombatStability + itembonuses.CombatStability +
spellbonuses.CombatStability) / 100.0f;
aa_mit = (aabonuses.CombatStability + itembonuses.CombatStability + spellbonuses.CombatStability)/100.0f;
if(RuleB(Combat, UseIntervalAC))
{
float softcap = 0.0;
if (RuleB(Combat, UseIntervalAC)) {
float softcap = (GetSkill(SkillDefense) + GetLevel()) *
RuleR(Combat, SoftcapFactor) * (1.0 + aa_mit);
float mitigation_rating = 0.0;
float attack_rating = 0.0;
int shield_ac = 0;
@@ -561,150 +555,101 @@ void Mob::MeleeMitigation(Mob *attacker, int32 &damage, int32 minhit, ExtraAttac
float monkweight = RuleI(Combat, MonkACBonusWeight);
monkweight = mod_monk_weight(monkweight, attacker);
if(IsClient())
{
if (IsClient()) {
armor = CastToClient()->GetRawACNoShield(shield_ac);
weight = (CastToClient()->CalcCurrentWeight() / 10.0);
}
else if(IsNPC())
{
} else if (IsNPC()) {
armor = CastToNPC()->GetRawAC();
if(!IsPet())
{
if (!IsPet())
armor = (armor / RuleR(Combat, NPCACFactor));
}
armor += spellbonuses.AC + itembonuses.AC + 1;
}
if(opts) {
if (opts) {
armor *= (1.0f - opts->armor_pen_percent);
armor -= opts->armor_pen_flat;
}
if(GetClass() == WIZARD || GetClass() == MAGICIAN || GetClass() == NECROMANCER || GetClass() == ENCHANTER)
{
softcap = RuleI(Combat, ClothACSoftcap);
}
else if(GetClass() == MONK && weight <= monkweight)
{
softcap = RuleI(Combat, MonkACSoftcap);
}
else if(GetClass() == DRUID || GetClass() == BEASTLORD || GetClass() == MONK)
{
softcap = RuleI(Combat, LeatherACSoftcap);
}
else if(GetClass() == SHAMAN || GetClass() == ROGUE || GetClass() == BERSERKER || GetClass() == RANGER)
{
softcap = RuleI(Combat, ChainACSoftcap);
}
else
{
softcap = RuleI(Combat, PlateACSoftcap);
if (RuleB(Combat, OldACSoftcapRules)) {
if (GetClass() == WIZARD || GetClass() == MAGICIAN ||
GetClass() == NECROMANCER || GetClass() == ENCHANTER)
softcap = RuleI(Combat, ClothACSoftcap);
else if (GetClass() == MONK && weight <= monkweight)
softcap = RuleI(Combat, MonkACSoftcap);
else if(GetClass() == DRUID || GetClass() == BEASTLORD || GetClass() == MONK)
softcap = RuleI(Combat, LeatherACSoftcap);
else if(GetClass() == SHAMAN || GetClass() == ROGUE ||
GetClass() == BERSERKER || GetClass() == RANGER)
softcap = RuleI(Combat, ChainACSoftcap);
else
softcap = RuleI(Combat, PlateACSoftcap);
}
softcap += shield_ac;
armor += shield_ac;
softcap += (softcap * (aa_mit * RuleR(Combat, AAMitigationACFactor)));
if(armor > softcap)
{
if (RuleB(Combat, OldACSoftcapRules))
softcap += (softcap * (aa_mit * RuleR(Combat, AAMitigationACFactor)));
if (armor > softcap) {
int softcap_armor = armor - softcap;
if(GetClass() == WARRIOR)
{
softcap_armor = softcap_armor * RuleR(Combat, WarriorACSoftcapReturn);
}
else if(GetClass() == SHADOWKNIGHT || GetClass() == PALADIN || (GetClass() == MONK && weight <= monkweight))
{
softcap_armor = softcap_armor * RuleR(Combat, KnightACSoftcapReturn);
}
else if(GetClass() == CLERIC || GetClass() == BARD || GetClass() == BERSERKER || GetClass() == ROGUE || GetClass() == SHAMAN || GetClass() == MONK)
{
softcap_armor = softcap_armor * RuleR(Combat, LowPlateChainACSoftcapReturn);
}
else if(GetClass() == RANGER || GetClass() == BEASTLORD)
{
softcap_armor = softcap_armor * RuleR(Combat, LowChainLeatherACSoftcapReturn);
}
else if(GetClass() == WIZARD || GetClass() == MAGICIAN || GetClass() == NECROMANCER || GetClass() == ENCHANTER || GetClass() == DRUID)
{
softcap_armor = softcap_armor * RuleR(Combat, CasterACSoftcapReturn);
}
else
{
softcap_armor = softcap_armor * RuleR(Combat, MiscACSoftcapReturn);
if (RuleB(Combat, OldACSoftcapRules)) {
if (GetClass() == WARRIOR)
softcap_armor = softcap_armor * RuleR(Combat, WarriorACSoftcapReturn);
else if (GetClass() == SHADOWKNIGHT || GetClass() == PALADIN ||
(GetClass() == MONK && weight <= monkweight))
softcap_armor = softcap_armor * RuleR(Combat, KnightACSoftcapReturn);
else if (GetClass() == CLERIC || GetClass() == BARD ||
GetClass() == BERSERKER || GetClass() == ROGUE ||
GetClass() == SHAMAN || GetClass() == MONK)
softcap_armor = softcap_armor * RuleR(Combat, LowPlateChainACSoftcapReturn);
else if (GetClass() == RANGER || GetClass() == BEASTLORD)
softcap_armor = softcap_armor * RuleR(Combat, LowChainLeatherACSoftcapReturn);
else if (GetClass() == WIZARD || GetClass() == MAGICIAN ||
GetClass() == NECROMANCER || GetClass() == ENCHANTER ||
GetClass() == DRUID)
softcap_armor = softcap_armor * RuleR(Combat, CasterACSoftcapReturn);
else
softcap_armor = softcap_armor * RuleR(Combat, MiscACSoftcapReturn);
} else {
if (GetClass() == WARRIOR)
softcap_armor *= RuleR(Combat, WarACSoftcapReturn);
else if (GetClass() == PALADIN || GetClass() == SHADOWKNIGHT)
softcap_armor *= RuleR(Combat, PalShdACSoftcapReturn);
else if (GetClass() == CLERIC || GetClass() == RANGER ||
GetClass() == MONK || GetClass() == BARD)
softcap_armor *= RuleR(Combat, ClrRngMnkBrdACSoftcapReturn);
else if (GetClass() == DRUID || GetClass() == NECROMANCER ||
GetClass() == WIZARD || GetClass() == ENCHANTER ||
GetClass() == MAGICIAN)
softcap_armor *= RuleR(Combat, DruNecWizEncMagACSoftcapReturn);
else if (GetClass() == ROGUE || GetClass() == SHAMAN ||
GetClass() == BEASTLORD || GetClass() == BERSERKER)
softcap_armor *= RuleR(Combat, RogShmBstBerACSoftcapReturn);
else
softcap_armor *= RuleR(Combat, MiscACSoftcapReturn);
}
armor = softcap + softcap_armor;
}
mitigation_rating = 0.0;
if(GetClass() == WIZARD || GetClass() == MAGICIAN || GetClass() == NECROMANCER || GetClass() == ENCHANTER)
{
if (GetClass() == WIZARD || GetClass() == MAGICIAN ||
GetClass() == NECROMANCER || GetClass() == ENCHANTER)
mitigation_rating = ((GetSkill(SkillDefense) + itembonuses.HeroicAGI/10) / 4.0) + armor + 1;
}
else
{
mitigation_rating = ((GetSkill(SkillDefense) + itembonuses.HeroicAGI/10) / 3.0) + (armor * 1.333333) + 1;
}
mitigation_rating *= 0.847;
mitigation_rating = mod_mitigation_rating(mitigation_rating, attacker);
if(attacker->IsClient())
{
if (attacker->IsClient())
attack_rating = (attacker->CastToClient()->CalcATK() + ((attacker->GetSTR()-66) * 0.9) + (attacker->GetSkill(SkillOffense)*1.345));
}
else
{
attack_rating = (attacker->GetATK() + (attacker->GetSkill(SkillOffense)*1.345) + ((attacker->GetSTR()-66) * 0.9));
}
attack_rating = attacker->mod_attack_rating(attack_rating, this);
float d = 10.0;
float mit_roll = MakeRandomFloat(0, mitigation_rating);
float atk_roll = MakeRandomFloat(0, attack_rating);
if(atk_roll > mit_roll)
{
float a_diff = (atk_roll - mit_roll);
float thac0 = attack_rating * RuleR(Combat, ACthac0Factor);
float thac0cap = ((attacker->GetLevel() * 9) + 20);
if(thac0 > thac0cap)
{
thac0 = thac0cap;
}
d -= 10.0 * (a_diff / thac0);
}
else if(mit_roll > atk_roll)
{
float m_diff = (mit_roll - atk_roll);
float thac20 = mitigation_rating * RuleR(Combat, ACthac20Factor);
float thac20cap = ((defender->GetLevel() * 9) + 20);
if(thac20 > thac20cap)
{
thac20 = thac20cap;
}
d += 10 * (m_diff / thac20);
}
if(d < 0.0)
{
d = 0.0;
}
if(d > 20)
{
d = 20.0;
}
float interval = (damage - minhit) / 20.0;
damage = damage - ((int)d * interval);
}
else{
damage = GetMeleeMitDmg(attacker, damage, minhit, mitigation_rating, attack_rating);
} else {
////////////////////////////////////////////////////////
// Scorpious2k: Include AC in the calculation
// use serverop variables to set values
@@ -755,17 +700,100 @@ void Mob::MeleeMitigation(Mob *attacker, int32 &damage, int32 minhit, ExtraAttac
if(damage != 0 && damage < minhit)
damage = minhit;
//reduce the damage from shielding item and aa based on the min dmg
//spells offer pure mitigation
damage -= (minhit * defender->itembonuses.MeleeMitigation / 100);
damage -= (damage * defender->spellbonuses.MeleeMitigation / 100);
}
//reduce the damage from shielding item and aa based on the min dmg
//spells offer pure mitigation
damage -= (minhit * defender->itembonuses.MeleeMitigation / 100);
damage -= (damage * defender->spellbonuses.MeleeMitigation / 100);
if(damage < 0)
if (damage < 0)
damage = 0;
}
// This is called when the Mob is the one being hit
int32 Mob::GetMeleeMitDmg(Mob *attacker, int32 damage, int32 minhit,
float mit_rating, float atk_rating)
{
float d = 10.0;
float mit_roll = MakeRandomFloat(0, mit_rating);
float atk_roll = MakeRandomFloat(0, atk_rating);
if (atk_roll > mit_roll) {
float a_diff = atk_roll - mit_roll;
float thac0 = atk_rating * RuleR(Combat, ACthac0Factor);
float thac0cap = attacker->GetLevel() * 9 + 20;
if (thac0 > thac0cap)
thac0 = thac0cap;
d -= 10.0 * (a_diff / thac0);
} else if (mit_roll > atk_roll) {
float m_diff = mit_roll - atk_roll;
float thac20 = mit_rating * RuleR(Combat, ACthac20Factor);
float thac20cap = GetLevel() * 9 + 20;
if (thac20 > thac20cap)
thac20 = thac20cap;
d += 10.0 * (m_diff / thac20);
}
if (d < 0.0)
d = 0.0;
else if (d > 20.0)
d = 20.0;
float interval = (damage - minhit) / 20.0;
damage -= ((int)d * interval);
damage -= (minhit * itembonuses.MeleeMitigation / 100);
damage -= (damage * spellbonuses.MeleeMitigation / 100);
return damage;
}
// This is called when the Client is the one being hit
int32 Client::GetMeleeMitDmg(Mob *attacker, int32 damage, int32 minhit,
float mit_rating, float atk_rating)
{
if (!attacker->IsNPC() || RuleB(Combat, UseOldDamageIntervalRules))
return Mob::GetMeleeMitDmg(attacker, damage, minhit, mit_rating, atk_rating);
int d = 10;
// floats for the rounding issues
float dmg_interval = (damage - minhit) / 19.0;
float dmg_bonus = minhit - dmg_interval;
float spellMeleeMit = spellbonuses.MeleeMitigation / 100.0;
if (GetClass() == WARRIOR)
spellMeleeMit += 0.05;
dmg_bonus -= dmg_bonus * (itembonuses.MeleeMitigation / 100.0);
dmg_interval -= dmg_interval * spellMeleeMit;
float mit_roll = MakeRandomFloat(0, mit_rating);
float atk_roll = MakeRandomFloat(0, atk_rating);
if (atk_roll > mit_roll) {
float a_diff = atk_roll - mit_roll;
float thac0 = atk_rating * RuleR(Combat, ACthac0Factor);
float thac0cap = attacker->GetLevel() * 9 + 20;
if (thac0 > thac0cap)
thac0 = thac0cap;
d += 10 * (a_diff / thac0);
} else if (mit_roll > atk_roll) {
float m_diff = mit_roll - atk_roll;
float thac20 = mit_rating * RuleR(Combat, ACthac20Factor);
float thac20cap = GetLevel() * 9 + 20;
if (thac20 > thac20cap)
thac20 = thac20cap;
d -= 10 * (m_diff / thac20);
}
if (d < 1)
d = 1;
else if (d > 20)
d = 20;
return static_cast<int32>((dmg_bonus + dmg_interval * d));
}
//Returns the weapon damage against the input mob
//if we cannot hit the mob with the current weapon we will get a value less than or equal to zero
//Else we know we can hit.
@@ -1251,7 +1279,7 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b
hate *= opts->hate_percent;
hate += opts->hate_flat;
}
//check to see if we hit..
if(!other->CheckHitChance(this, skillinuse, Hand)) {
mlog(COMBAT__ATTACKS, "Attack missed. Damage set to 0.");
@@ -1308,6 +1336,7 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b
// Hate Generation is on a per swing basis, regardless of a hit, miss, or block, its always the same.
// If we are this far, this means we are atleast making a swing.
if (!bRiposte) // Ripostes never generate any aggro.
other->AddToHateList(this, hate);
@@ -1318,19 +1347,10 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b
if (IsDead()) return false;
if(damage > 0 && (spellbonuses.MeleeLifetap || itembonuses.MeleeLifetap))
{
int lifetap_amt = spellbonuses.MeleeLifetap + itembonuses.MeleeLifetap;
if(lifetap_amt > 100)
lifetap_amt = 100;
MeleeLifeTap(damage);
lifetap_amt = damage * lifetap_amt / 100;
mlog(COMBAT__DAMAGE, "Melee lifetap healing for %d damage.", damage);
//heal self for damage done..
HealDamage(lifetap_amt);
}
if (damage > 0)
CheckNumHitsRemaining(5);
//break invis when you attack
if(invisible) {
@@ -1882,6 +1902,7 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool
mlog(COMBAT__HITS, "Generating hate %d towards %s", hate, GetName());
// now add done damage to the hate list
other->AddToHateList(this, hate);
} else {
if(opts) {
damage *= opts->damage_percent;
@@ -1938,6 +1959,11 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool
if (HasDied()) //killed by damage shield ect
return false;
MeleeLifeTap(damage);
if (damage > 0)
CheckNumHitsRemaining(5);
//break invis when you attack
if(invisible) {
mlog(COMBAT__ATTACKS, "Removing invisibility due to melee attack.");
@@ -2411,7 +2437,9 @@ bool NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes attack
}
void Mob::AddToHateList(Mob* other, int32 hate, int32 damage, bool iYellForHelp, bool bFrenzy, bool iBuffTic) {
assert(other != nullptr);
if (other == this)
return;
@@ -2492,6 +2520,10 @@ void Mob::AddToHateList(Mob* other, int32 hate, int32 damage, bool iYellForHelp,
if(damage > GetHP())
damage = GetHP();
if (spellbonuses.ImprovedTaunt[1] && (GetLevel() < spellbonuses.ImprovedTaunt[0])
&& other && (buffs[spellbonuses.ImprovedTaunt[2]].casterid != other->GetID()))
hate = (hate*spellbonuses.ImprovedTaunt[1])/100;
hate_list.Add(other, hate, damage, bFrenzy, !iBuffTic);
if(other->IsClient())
@@ -3241,8 +3273,28 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi
}
// If this is a DoT, use DoT Shielding...
if(iBuffTic)
damage -= (damage * itembonuses.DoTShielding / 100);
if(iBuffTic) {
damage -= (damage * itembonuses.DoTShielding / 100);
if (spellbonuses.MitigateDotRune[0]){
slot = spellbonuses.MitigateDotRune[1];
if(slot >= 0)
{
int damage_to_reduce = damage * spellbonuses.MitigateDotRune[0] / 100;
if(damage_to_reduce > buffs[slot].dot_rune)
{
damage -= damage_to_reduce;
if(!TryFadeEffect(slot))
BuffFadeBySlot(slot);
}
else
{
buffs[slot].dot_rune = (buffs[slot].dot_rune - damage_to_reduce);
damage -= damage_to_reduce;
}
}
}
}
// This must be a DD then so lets apply Spell Shielding and runes.
else
@@ -3310,7 +3362,7 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi
BuffFadeBySlot(slot);
}
else{
buffs[slot].melee_rune = (buffs[slot].magic_rune - damage);
buffs[slot].magic_rune = (buffs[slot].magic_rune - damage);
}
}
}
@@ -3434,7 +3486,7 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons
mlog(COMBAT__DAMAGE, "Avoiding %d damage due to invulnerability.", damage);
damage = -5;
}
if( spell_id != SPELL_UNKNOWN || attacker == nullptr )
avoidable = false;
@@ -3444,6 +3496,13 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons
DamageShield(attacker);
}
if (spell_id == SPELL_UNKNOWN && skill_used) {
CheckNumHitsRemaining(1); //Incoming Hit Attempts
if (attacker)
attacker->CheckNumHitsRemaining(2); //Outgoing Hit Attempts
}
if(attacker){
if(attacker->IsClient()){
if(!RuleB(Combat, EXPFromDmgShield)) {
@@ -3514,16 +3573,20 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons
}
ReduceAllDamage(damage);
if (skill_used)
CheckNumHitsRemaining(6); //Incomming Hit Success on Defender
if(IsClient() && CastToClient()->sneaking){
CastToClient()->sneaking = false;
SendAppearancePacket(AT_Sneak, 0);
}
if(attacker && attacker->IsClient() && attacker->CastToClient()->sneaking){
attacker->CastToClient()->sneaking = false;
attacker->SendAppearancePacket(AT_Sneak, 0);
}
ReduceAllDamage(damage);
if(IsClient() && CastToClient()->sneaking){
CastToClient()->sneaking = false;
SendAppearancePacket(AT_Sneak, 0);
}
if(attacker && attacker->IsClient() && attacker->CastToClient()->sneaking){
attacker->CastToClient()->sneaking = false;
attacker->SendAppearancePacket(AT_Sneak, 0);
}
//final damage has been determined.
SetHP(GetHP() - damage);
@@ -3770,6 +3833,7 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons
}
}
} //end packet sending
}
+63 -13
View File
@@ -1216,6 +1216,18 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon)
case SE_HealRate:
newbon->HealRate += base1;
break;
case SE_MeleeLifetap:
{
if((base1 < 0) && (newbon->MeleeLifetap > base1))
newbon->MeleeLifetap = base1;
else if(newbon->MeleeLifetap < base1)
newbon->MeleeLifetap = base1;
break;
}
}
}
}
@@ -1230,8 +1242,12 @@ void Mob::CalcSpellBonuses(StatBonuses* newbon)
uint32 buff_count = GetMaxTotalSlots();
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, false, buffs[i].ticsremaining,i);
if (buffs[i].numhits > 0)
Numhits(true);
}
}
//Removes the spell bonuses that are effected by a 'negate' debuff.
@@ -1591,12 +1607,9 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
newbon->reflect_chance += effect_value;
break;
case SE_SingingSkill:
{
if(effect_value > newbon->singingMod)
newbon->singingMod = effect_value;
case SE_Amplification:
newbon->Amplification += effect_value;
break;
}
case SE_ChangeAggro:
newbon->hatemod += effect_value;
@@ -1750,7 +1763,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
else if((effect_value < 0) && (newbon->MeleeLifetap > effect_value))
newbon->MeleeLifetap = spells[spell_id].base[i];
if(newbon->MeleeLifetap < spells[spell_id].base[i])
else if(newbon->MeleeLifetap < spells[spell_id].base[i])
newbon->MeleeLifetap = spells[spell_id].base[i];
break;
}
@@ -2223,6 +2236,15 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
}
break;
}
case SE_MitigateDotDamage:
{
if (newbon->MitigateDotRune[0] < effect_value){
newbon->MitigateDotRune[0] = effect_value;
newbon->MitigateDotRune[1] = buffslot;
}
break;
}
case SE_ManaAbsorbPercentDamage:
{
@@ -2480,7 +2502,8 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
break;
}
case SE_TriggerOnValueAmount:
case SE_TriggerOnReqTarget:
case SE_TriggerOnReqCaster:
newbon->TriggerOnValueAmount = true;
break;
@@ -2488,6 +2511,19 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
newbon->DivineAura = true;
break;
case SE_ImprovedTaunt:
if (newbon->ImprovedTaunt[0] < effect_value) {
newbon->ImprovedTaunt[0] = effect_value;
newbon->ImprovedTaunt[1] = spells[spell_id].base2[i];
newbon->ImprovedTaunt[2] = buffslot;
}
break;
case SE_DistanceRemoval:
newbon->DistanceRemoval = true;
break;
}
}
}
@@ -3099,10 +3135,10 @@ void Mob::NegateSpellsBonuses(uint16 spell_id)
itembonuses.reflect_chance = effect_value;
break;
case SE_SingingSkill:
spellbonuses.singingMod = effect_value;
itembonuses.singingMod = effect_value;
aabonuses.singingMod = effect_value;
case SE_Amplification:
spellbonuses.Amplification = effect_value;
itembonuses.Amplification = effect_value;
aabonuses.Amplification = effect_value;
break;
case SE_ChangeAggro:
@@ -3533,6 +3569,11 @@ void Mob::NegateSpellsBonuses(uint16 spell_id)
spellbonuses.MitigateSpellRune[1] = -1;
break;
case SE_MitigateDotDamage:
spellbonuses.MitigateDotRune[0] = effect_value;
spellbonuses.MitigateDotRune[1] = -1;
break;
case SE_ManaAbsorbPercentDamage:
spellbonuses.ManaAbsorbPercentDamage[0] = effect_value;
spellbonuses.ManaAbsorbPercentDamage[1] = -1;
@@ -3816,7 +3857,16 @@ void Mob::NegateSpellsBonuses(uint16 spell_id)
itembonuses.CriticalMend = effect_value;
aabonuses.CriticalMend = effect_value;
break;
case SE_DistanceRemoval:
spellbonuses.DistanceRemoval = effect_value;
break;
case SE_ImprovedTaunt:
spellbonuses.ImprovedTaunt[0] = effect_value;
spellbonuses.ImprovedTaunt[1] = effect_value;
spellbonuses.ImprovedTaunt[2] = -1;
}
}
}
+26 -34
View File
@@ -2476,7 +2476,7 @@ void Bot::SaveBuffs() {
if(!database.RunQuery(Query, MakeAnyLenString(&Query, "INSERT INTO botbuffs (BotId, SpellId, CasterLevel, DurationFormula, "
"TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, CorruptionCounters, HitCount, MeleeRune, MagicRune, "
"DeathSaveSuccessChance, CasterAARank, Persistent) VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u);",
"dot_rune, caston_x, Persistent, caston_y, caston_z, ExtraDIChance) VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %i, %u, %i, %i, %i);",
GetBotID(), buffs[BuffCount].spellid, buffs[BuffCount].casterlevel, spells[buffs[BuffCount].spellid].buffdurationformula,
buffs[BuffCount].ticsremaining,
CalculatePoisonCounters(buffs[BuffCount].spellid) > 0 ? buffs[BuffCount].counters : 0,
@@ -2484,8 +2484,12 @@ void Bot::SaveBuffs() {
CalculateCurseCounters(buffs[BuffCount].spellid) > 0 ? buffs[BuffCount].counters : 0,
CalculateCorruptionCounters(buffs[BuffCount].spellid) > 0 ? buffs[BuffCount].counters : 0,
buffs[BuffCount].numhits, buffs[BuffCount].melee_rune, buffs[BuffCount].magic_rune,
buffs[BuffCount].deathSaveSuccessChance,
buffs[BuffCount].deathsaveCasterAARank, IsPersistent), TempErrorMessageBuffer)) {
buffs[BuffCount].dot_rune,
buffs[BuffCount].caston_x,
IsPersistent,
buffs[BuffCount].caston_y,
buffs[BuffCount].caston_z,
buffs[BuffCount].ExtraDIChance), TempErrorMessageBuffer)) {
errorMessage = std::string(TempErrorMessageBuffer);
safe_delete(Query);
Query = 0;
@@ -2515,7 +2519,7 @@ void Bot::LoadBuffs() {
bool BuffsLoaded = false;
if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT SpellId, CasterLevel, DurationFormula, TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, CorruptionCounters, HitCount, MeleeRune, MagicRune, DeathSaveSuccessChance, CasterAARank, Persistent FROM botbuffs WHERE BotId = %u", GetBotID()), TempErrorMessageBuffer, &DatasetResult)) {
if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT SpellId, CasterLevel, DurationFormula, TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, CorruptionCounters, HitCount, MeleeRune, MagicRune, dot_rune, caston_x, Persistent, caston_y, caston_z, ExtraDIChance FROM botbuffs WHERE BotId = %u", GetBotID()), TempErrorMessageBuffer, &DatasetResult)) {
errorMessage = std::string(TempErrorMessageBuffer);
}
else {
@@ -2541,8 +2545,8 @@ void Bot::LoadBuffs() {
buffs[BuffCount].numhits = atoi(DataRow[8]);
buffs[BuffCount].melee_rune = atoi(DataRow[9]);
buffs[BuffCount].magic_rune = atoi(DataRow[10]);
buffs[BuffCount].deathSaveSuccessChance = atoi(DataRow[11]);
buffs[BuffCount].deathsaveCasterAARank = atoi(DataRow[12]);
buffs[BuffCount].dot_rune = atoi(DataRow[11]);
buffs[BuffCount].caston_x = atoi(DataRow[12]);
buffs[BuffCount].casterid = 0;
bool IsPersistent = false;
@@ -2550,6 +2554,10 @@ void Bot::LoadBuffs() {
if(atoi(DataRow[13]))
IsPersistent = true;
buffs[BuffCount].caston_y = atoi(DataRow[14]);
buffs[BuffCount].caston_z = atoi(DataRow[15]);
buffs[BuffCount].ExtraDIChance = atoi(DataRow[16]);
buffs[BuffCount].persistant_buff = IsPersistent;
BuffCount++;
@@ -3377,6 +3385,9 @@ void Bot::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes
if (HasDied())
return;
if (damage > 0)
CheckNumHitsRemaining(5);
if((skillinuse == SkillDragonPunch) && GetAA(aaDragonPunch) && MakeRandomInt(0, 99) < 25){
SpellFinished(904, other, 10, 0, -1, spells[904].ResistDiff);
other->Stun(100);
@@ -6593,18 +6604,10 @@ bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, b
if (GetHP() < 0) return false;
if(damage > 0 && (spellbonuses.MeleeLifetap || itembonuses.MeleeLifetap))
{
int lifetap_amt = spellbonuses.MeleeLifetap + itembonuses.MeleeLifetap;
if(lifetap_amt > 100)
lifetap_amt = 100;
MeleeLifeTap(damage);
lifetap_amt = damage * lifetap_amt / 100;
mlog(COMBAT__DAMAGE, "Melee lifetap healing for %d damage.", damage);
//heal self for damage done..
HealDamage(lifetap_amt);
}
if (damage > 0)
CheckNumHitsRemaining(5);
//break invis when you attack
if(invisible) {
@@ -6823,13 +6826,6 @@ int16 Bot::CalcBotAAFocus(BotfocusType type, uint32 aa_ID, uint16 spell_id)
SpellSkill_Found = true;
break;
case SE_LimitSpellSubclass:{
int16 spell_skill = spell.skill * -1;
if(base1 == spell_skill)
LimitFound = true;
break;
}
case SE_LimitClass:
//Do not use this limit more then once per spell. If multiple class, treat value like items would.
if (!PassLimitClass(base1, GetClass()))
@@ -7428,13 +7424,6 @@ int16 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel
SpellSkill_Found = true;
break;
case SE_LimitSpellSubclass:{
int16 spell_skill = spell.skill * -1;
if(focus_spell.base[i] == spell_skill)
return 0;
break;
}
case SE_LimitClass:
//Do not use this limit more then once per spell. If multiple class, treat value like items would.
if (!PassLimitClass(focus_spell.base[i], GetClass()))
@@ -8083,6 +8072,9 @@ void Bot::DoSpecialAttackDamage(Mob *who, SkillUseTypes skill, int32 max_damage,
if(!GetTarget())return;
if (HasDied()) return;
if (max_damage > 0)
CheckNumHitsRemaining(5);
//[AA Dragon Punch] value[0] = 100 for 25%, chance value[1] = skill
if(aabonuses.SpecialAttackKBProc[0] && aabonuses.SpecialAttackKBProc[1] == skill){
int kb_chance = 25;
@@ -12975,7 +12967,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) {
pacer->Say("Trying to pacify %s \n", target->GetCleanName());
if(pacer->Bot_Command_CalmTarget(target)) {
if(target->FindType(SE_Lull) || target->FindType(SE_Harmony) || target->FindType(SE_Calm))
if(target->FindType(SE_Lull) || target->FindType(SE_Harmony) || target->FindType(SE_InstantHate))
//if(pacer->IsPacified(target))
c->Message(0, "I have successfully pacified %s.", target->GetCleanName());
return;
@@ -12991,7 +12983,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) {
pacer->Say("Trying to pacify %s \n", target->GetCleanName());
if(pacer->Bot_Command_CalmTarget(target)) {
if(target->FindType(SE_Lull) || target->FindType(SE_Harmony) || target->FindType(SE_Calm))
if(target->FindType(SE_Lull) || target->FindType(SE_Harmony) || target->FindType(SE_InstantHate))
//if(pacer->IsPacified(target))
c->Message(0, "I have successfully pacified %s.", target->GetCleanName());
return;
@@ -16315,7 +16307,7 @@ void EntityList::ShowSpawnWindow(Client* client, int Distance, bool NamedOnly) {
std::string WindowText;
int LastCon = -1;
int CurrentCon = 0;
Mob* curMob = NULL;
Mob* curMob = nullptr;
uint32 array_counter = 0;
+12 -11
View File
@@ -6045,11 +6045,10 @@ void Client::NPCSpawn(NPC *target_npc, const char *identifier, uint32 extra)
}
}
bool Client::IsDraggingCorpse(const char *CorpseName)
bool Client::IsDraggingCorpse(uint16 CorpseID)
{
for(std::list<std::string>::iterator Iterator = DraggedCorpses.begin(); Iterator != DraggedCorpses.end(); ++Iterator)
{
if(!strcasecmp((*Iterator).c_str(), CorpseName))
for (auto It = DraggedCorpses.begin(); It != DraggedCorpses.end(); ++It) {
if (It->second == CorpseID)
return true;
}
@@ -6058,20 +6057,22 @@ bool Client::IsDraggingCorpse(const char *CorpseName)
void Client::DragCorpses()
{
for(std::list<std::string>::iterator Iterator = DraggedCorpses.begin(); Iterator != DraggedCorpses.end(); ++Iterator)
{
Mob* corpse = entity_list.GetMob((*Iterator).c_str());
for (auto It = DraggedCorpses.begin(); It != DraggedCorpses.end(); ++It) {
Mob *corpse = entity_list.GetMob(It->second);
if(corpse && corpse->IsPlayerCorpse() && (DistNoRootNoZ(*corpse) <= RuleR(Character, DragCorpseDistance)))
if (corpse && corpse->IsPlayerCorpse() &&
(DistNoRootNoZ(*corpse) <= RuleR(Character, DragCorpseDistance)))
continue;
if(!corpse || !corpse->IsPlayerCorpse() || corpse->CastToCorpse()->IsBeingLooted() || !corpse->CastToCorpse()->Summon(this, false, false))
{
if (!corpse || !corpse->IsPlayerCorpse() ||
corpse->CastToCorpse()->IsBeingLooted() ||
!corpse->CastToCorpse()->Summon(this, false, false)) {
Message_StringID(MT_DefaultText, CORPSEDRAG_STOP);
Iterator = DraggedCorpses.erase(Iterator);
It = DraggedCorpses.erase(It);
}
}
}
void Client::Doppelganger(uint16 spell_id, Mob *target, const char *name_override, int pet_count, int pet_duration)
{
if(!target || !IsValidSpell(spell_id) || this->GetID() == target->GetID())
+3 -2
View File
@@ -221,6 +221,7 @@ public:
virtual Raid* GetRaid() { return entity_list.GetRaidByClient(this); }
virtual Group* GetGroup() { return entity_list.GetGroupByClient(this); }
virtual inline bool IsBerserk() { return berserk; }
virtual int32 GetMeleeMitDmg(Mob *attacker, int32 damage, int32 minhit, float mit_rating, float atk_rating);
void AI_Init();
void AI_Start(uint32 iMoveDelay = 0);
@@ -1077,7 +1078,7 @@ public:
void ClearHover();
inline bool IsBlockedBuff(int16 SpellID) { return PlayerBlockedBuffs.find(SpellID) != PlayerBlockedBuffs.end(); }
inline bool IsBlockedPetBuff(int16 SpellID) { return PetBlockedBuffs.find(SpellID) != PetBlockedBuffs.end(); }
bool IsDraggingCorpse(const char* CorpseName);
bool IsDraggingCorpse(uint16 CorpseID);
inline bool IsDraggingCorpse() { return (DraggedCorpses.size() > 0); }
void DragCorpses();
inline void ClearDraggedCorpses() { DraggedCorpses.clear(); }
@@ -1480,7 +1481,7 @@ private:
std::set<uint32> PlayerBlockedBuffs;
std::set<uint32> PetBlockedBuffs;
std::list<std::string> DraggedCorpses;
std::list<std::pair<std::string, uint16> > DraggedCorpses;
uint8 MaxXTargets;
bool XTargetAutoAddHaters;
+1 -1
View File
@@ -1857,7 +1857,7 @@ uint16 Mob::GetInstrumentMod(uint16 spell_id) const
effectmod = itembonuses.singingMod;
else
effectmod = spellbonuses.singingMod;
effectmod += aabonuses.singingMod;
effectmod += aabonuses.singingMod + spellbonuses.Amplification;
break;
default:
effectmod = 10;
+5 -5
View File
@@ -9466,7 +9466,7 @@ void Client::CompleteConnect()
case SE_AddMeleeProc:
case SE_WeaponProc:
{
AddProcToWeapon(GetProcID(buffs[j1].spellid, x1), false, 100+spells[buffs[j1].spellid].base2[x1]);
AddProcToWeapon(GetProcID(buffs[j1].spellid, x1), false, 100+spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid);
break;
}
case SE_DefensiveProc:
@@ -12379,7 +12379,7 @@ void Client::Handle_OP_CorpseDrag(const EQApplicationPacket *app)
if(!corpse || !corpse->IsPlayerCorpse() || corpse->CastToCorpse()->IsBeingLooted())
return;
Client *c = entity_list.FindCorpseDragger(cds->CorpseName);
Client *c = entity_list.FindCorpseDragger(corpse->GetID());
if(c)
{
@@ -12394,7 +12394,7 @@ void Client::Handle_OP_CorpseDrag(const EQApplicationPacket *app)
if(!corpse->CastToCorpse()->Summon(this, false, true))
return;
DraggedCorpses.push_back(cds->CorpseName);
DraggedCorpses.push_back(std::pair<std::string, uint16>(cds->CorpseName, corpse->GetID()));
Message_StringID(MT_DefaultText, CORPSEDRAG_BEGIN, cds->CorpseName);
}
@@ -12408,9 +12408,9 @@ void Client::Handle_OP_CorpseDrop(const EQApplicationPacket *app)
return;
}
for(std::list<std::string>::iterator Iterator = DraggedCorpses.begin(); Iterator != DraggedCorpses.end(); ++Iterator)
for (auto Iterator = DraggedCorpses.begin(); Iterator != DraggedCorpses.end(); ++Iterator)
{
if(!strcasecmp((*Iterator).c_str(), (const char *)app->pBuffer))
if(!strcasecmp(Iterator->first.c_str(), (const char *)app->pBuffer))
{
Message_StringID(MT_DefaultText, CORPSEDRAG_STOP);
Iterator = DraggedCorpses.erase(Iterator);
-3
View File
@@ -11047,9 +11047,6 @@ void command_showbonusstats(Client *c, const Seperator *sep)
c->Message(0, " Target Spell Bonuses:");
c->Message(0, " Accuracy: %i%% Divine Save: %i%%",c->GetTarget()->GetSpellBonuses().Accuracy, c->GetTarget()->GetSpellBonuses().DivineSaveChance);
c->Message(0, " Flurry: %i%% HitChance: %i%% ",c->GetTarget()->GetSpellBonuses().FlurryChance, c->GetTarget()->GetSpellBonuses().HitChance / 15);
int deathsaveslot = c->GetTarget()->GetBuffSlotFromType(SE_DeathSave);
int dschance = deathsaveslot >= 0 ? c->GetTarget()->GetBuffs()[deathsaveslot].deathSaveSuccessChance : 0;
c->Message(0, " Death Save: %i%%",dschance);
}
c->Message(0, " Effective Casting Level: %i",c->GetTarget()->GetCasterLevel(0));
}
+9 -2
View File
@@ -155,8 +155,11 @@ struct Buffs_Struct {
uint32 numhits; //the number of physical hits this buff can take before it fades away, lots of druid armor spells take advantage of this mixed with powerful effects
uint32 melee_rune;
uint32 magic_rune;
uint8 deathSaveSuccessChance;
uint8 deathsaveCasterAARank;
uint32 dot_rune;
int32 caston_x;
int32 caston_y;
int32 caston_z;
int32 ExtraDIChance;
bool persistant_buff;
bool client; //True if the caster is a client
bool UpdateClient;
@@ -231,6 +234,7 @@ struct StatBonuses {
int effective_casting_level;
int reflect_chance; // chance to reflect incoming spell
uint16 singingMod;
uint16 Amplification; // stacks with singingMod
uint16 brassMod;
uint16 percussionMod;
uint16 windMod;
@@ -319,6 +323,7 @@ struct StatBonuses {
uint16 MeleeThresholdGuard[3]; // 0 = Mitigation value 1 = Buff Slot 2 = Min damage to trigger.
uint16 SpellThresholdGuard[3]; // 0 = Mitigation value 1 = Buff Slot 2 = Min damage to trigger.
uint16 MitigateSpellRune[2]; // 0 = Mitigation value 1 = Buff Slot
uint16 MitigateDotRune[2]; // 0 = Mitigation value 1 = Buff Slot
uint32 TriggerMeleeThreshold[3]; // 0 = Spell Effect ID 1 = Buff slot 2 = Damage Amount to Trigger
uint32 TriggerSpellThreshold[3]; // 0 = Spell Effect ID 1 = Buff slot 2 = Damage Amount to Trigger
uint16 ManaAbsorbPercentDamage[2]; // 0 = Mitigation value 1 = Buff Slot
@@ -328,6 +333,8 @@ struct StatBonuses {
bool CriticalHealDecay; // increase critical heal chance, decays based on spell level cast
bool CriticalDotDecay; // increase critical dot chance, decays based on spell level cast
bool DivineAura; // invulnerability
bool DistanceRemoval; // Check if Cancle if Moved effect is present
int16 ImprovedTaunt[3]; // 0 = Max Level 1 = Aggro modifier 2 = buffid
//bool AbsorbMagicAtt; // Magic Rune *Need to be implemented for NegateEffect
//bool MeleeRune; // Melee Rune *Need to be implemented for NegateEffect
+2 -2
View File
@@ -4512,11 +4512,11 @@ void EntityList::GetTargetsForConeArea(Mob *start, uint32 radius, uint32 height,
}
}
Client *EntityList::FindCorpseDragger(const char *CorpseName)
Client *EntityList::FindCorpseDragger(uint16 CorpseID)
{
auto it = client_list.begin();
while (it != client_list.end()) {
if (it->second->IsDraggingCorpse(CorpseName))
if (it->second->IsDraggingCorpse(CorpseID))
return it->second;
++it;
}
+1 -1
View File
@@ -167,7 +167,7 @@ public:
Spawn2* GetSpawnByID(uint32 id);
Client* FindCorpseDragger(const char *CorpseName);
Client* FindCorpseDragger(uint16 CorpseID);
inline Object *GetObjectByID(uint16 id)
{ return object_list.count(id) ? object_list.at(id) : nullptr; }
+10 -6
View File
@@ -268,6 +268,9 @@ Mob *HateList::GetTop(Mob *center)
Mob* top = nullptr;
int32 hate = -1;
if(center == nullptr)
return nullptr;
if (RuleB(Aggro,SmartAggroList)){
Mob* topClientTypeInRange = nullptr;
int32 hateClientTypeInRange = -1;
@@ -380,15 +383,15 @@ Mob *HateList::GetTop(Mob *center)
}
if(!isTopClientType)
return topClientTypeInRange;
return topClientTypeInRange ? topClientTypeInRange : nullptr;
return top;
return top ? top : nullptr;
}
else {
if(top == nullptr && skipped_count > 0) {
return center->GetTarget();
return center->GetTarget() ? center->GetTarget() : nullptr;
}
return top;
return top ? top : nullptr;
}
}
else{
@@ -413,10 +416,11 @@ Mob *HateList::GetTop(Mob *center)
++iterator;
}
if(top == nullptr && skipped_count > 0) {
return center->GetTarget();
return center->GetTarget() ? center->GetTarget() : nullptr;
}
return top;
return top ? top : nullptr;
}
return nullptr;
}
Mob *HateList::GetMostHate(){
+12
View File
@@ -908,6 +908,16 @@ void Lua_Mob::SetHate(Lua_Mob other, int hate, int damage) {
self->SetHate(other, hate, damage);
}
void Lua_Mob::HalveAggro(Lua_Mob other) {
Lua_Safe_Call_Void();
self->HalveAggro(other);
}
void Lua_Mob::DoubleAggro(Lua_Mob other) {
Lua_Safe_Call_Void();
self->DoubleAggro(other);
}
uint32 Lua_Mob::GetHateAmount(Lua_Mob target) {
Lua_Safe_Call_Int();
return self->GetHateAmount(target);
@@ -1967,6 +1977,8 @@ luabind::scope lua_register_mob() {
.def("SetHate", (void(Lua_Mob::*)(Lua_Mob))&Lua_Mob::SetHate)
.def("SetHate", (void(Lua_Mob::*)(Lua_Mob,int))&Lua_Mob::SetHate)
.def("SetHate", (void(Lua_Mob::*)(Lua_Mob,int,int))&Lua_Mob::SetHate)
.def("HalveAggro", &Lua_Mob::HalveAggro)
.def("DoubleAggro", &Lua_Mob::DoubleAggro)
.def("GetHateAmount", (uint32(Lua_Mob::*)(Lua_Mob))&Lua_Mob::GetHateAmount)
.def("GetHateAmount", (uint32(Lua_Mob::*)(Lua_Mob,bool))&Lua_Mob::GetHateAmount)
.def("GetDamageAmount", (uint32(Lua_Mob::*)(Lua_Mob))&Lua_Mob::GetDamageAmount)
+3 -1
View File
@@ -192,6 +192,8 @@ public:
void SetHate(Lua_Mob other);
void SetHate(Lua_Mob other, int hate);
void SetHate(Lua_Mob other, int hate, int damage);
void HalveAggro(Lua_Mob other);
void DoubleAggro(Lua_Mob other);
uint32 GetHateAmount(Lua_Mob target);
uint32 GetHateAmount(Lua_Mob target, bool is_damage);
uint32 GetDamageAmount(Lua_Mob target);
@@ -346,4 +348,4 @@ public:
};
#endif
#endif
#endif
+1 -1
View File
@@ -3670,7 +3670,7 @@ MercSpell Merc::GetBestMercSpellForHate(Merc* caster) {
result.time_cancast = 0;
if(caster) {
std::list<MercSpell> mercSpellList = GetMercSpellsForSpellEffect(caster, SE_Calm);
std::list<MercSpell> mercSpellList = GetMercSpellsForSpellEffect(caster, SE_InstantHate);
for(std::list<MercSpell>::iterator mercSpellListItr = mercSpellList.begin(); mercSpellListItr != mercSpellList.end(); ++mercSpellListItr) {
// Assuming all the spells have been loaded into this list by level and in descending order
+44 -16
View File
@@ -3228,11 +3228,20 @@ void Mob::TryApplyEffect(Mob *target, uint32 spell_id)
void Mob::TryTriggerOnValueAmount(bool IsHP, bool IsMana, bool IsEndur, bool IsPet)
{
/*
At present time there is no obvious difference between ReqTarget and ReqCaster
ReqTarget is typically used in spells cast on a target where the trigger occurs on that target.
ReqCaster is typically self only spells where the triggers on self.
Regardless both trigger on the owner of the buff.
*/
/*
Base2 Range: 1004 = Below < 80% HP
Base2 Range: 500-520 = Below (base2 - 500)*5 HP
Base2 Range: 521 = Below (?) Mana UKNOWN - Will assume its 20% unless proven otherwise
Base2 Range: 522 = Below (40%) Endurance
Base2 Range: 523 = Below (40%) Mana
Base2 Range: 220-? = Number of pets on hatelist to trigger (base2 - 220) (Set at 30 pets max for now)
38311 = < 10% mana;
*/
if (!spellbonuses.TriggerOnValueAmount)
@@ -3250,21 +3259,25 @@ void Mob::TryTriggerOnValueAmount(bool IsHP, bool IsMana, bool IsEndur, bool IsP
for(int i = 0; i < EFFECT_COUNT; i++){
if (spells[spell_id].effectid[i] == SE_TriggerOnValueAmount){
if ((spells[spell_id].effectid[i] == SE_TriggerOnReqTarget) || (spells[spell_id].effectid[i] == SE_TriggerOnReqCaster)) {
int base2 = spells[spell_id].base2[i];
bool use_spell = false;
if (IsHP){
if ((base2 >= 500 && base2 <= 520) && GetHPRatio() < (base2 - 500)*5){
if ((base2 >= 500 && base2 <= 520) && GetHPRatio() < (base2 - 500)*5)
use_spell = true;
else if (base2 = 1004 && GetHPRatio() < 80)
use_spell = true;
}
}
else if (IsMana){
if ( (base2 = 521 && GetManaRatio() < 20) || (base2 = 523 && GetManaRatio() < 40)) {
if ( (base2 = 521 && GetManaRatio() < 20) || (base2 = 523 && GetManaRatio() < 40))
use_spell = true;
else if (base2 = 38311 && GetManaRatio() < 10)
use_spell = true;
}
}
else if (IsEndur){
@@ -3283,10 +3296,6 @@ void Mob::TryTriggerOnValueAmount(bool IsHP, bool IsMana, bool IsEndur, bool IsP
if (use_spell){
SpellFinished(spells[spell_id].base[i], this, 10, 0, -1, spells[spell_id].ResistDiff);
/*Note, spell data shows numhits values of 0 or 1, however many descriptions of these spells indicate they should
be fading when consumed even with numhits of 0 (It makes sense they should fade...).
Unless proven otherwise, they should fade when triggered. */
if(!TryFadeEffect(e))
BuffFadeBySlot(e);
}
@@ -3412,8 +3421,6 @@ int16 Mob::GetSkillDmgTaken(const SkillUseTypes skill_used)
if(skilldmg_mod < -100)
skilldmg_mod = -100;
CheckNumHitsRemaining(6);
return skilldmg_mod;
}
@@ -4315,17 +4322,38 @@ int16 Mob::GetSkillDmgAmt(uint16 skill)
skill_dmg += spellbonuses.SkillDamageAmount2[HIGHEST_SKILL+1] + itembonuses.SkillDamageAmount2[HIGHEST_SKILL+1]
+ itembonuses.SkillDamageAmount2[skill] + spellbonuses.SkillDamageAmount2[skill];
CheckNumHitsRemaining(5);
return skill_dmg;
}
void Mob::MeleeLifeTap(int32 damage) {
if(damage > 0 && (spellbonuses.MeleeLifetap || itembonuses.MeleeLifetap || aabonuses.MeleeLifetap ))
{
int lifetap_amt = spellbonuses.MeleeLifetap + itembonuses.MeleeLifetap + aabonuses.MeleeLifetap;
if(lifetap_amt > 100)
lifetap_amt = 100;
else if (lifetap_amt < -99)
lifetap_amt = -99;
lifetap_amt = damage * lifetap_amt / 100;
mlog(COMBAT__DAMAGE, "Melee lifetap healing for %d damage.", damage);
//heal self for damage done..
HealDamage(lifetap_amt);
}
}
bool Mob::TryReflectSpell(uint32 spell_id)
{
if(!GetTarget())
return false;
if (!spells[spell_id].reflectable)
return false;
if(MakeRandomInt(0, 99) < (GetTarget()->itembonuses.reflect_chance + GetTarget()->spellbonuses.reflect_chance))
int chance = itembonuses.reflect_chance + spellbonuses.reflect_chance + aabonuses.reflect_chance;
if(chance && MakeRandomInt(0, 99) < chance)
return true;
return false;
+8 -2
View File
@@ -143,6 +143,7 @@ public:
virtual void DoRiposte(Mob* defender);
void ApplyMeleeDamageBonus(uint16 skill, int32 &damage);
virtual void MeleeMitigation(Mob *attacker, int32 &damage, int32 minhit, ExtraAttackOptions *opts = nullptr);
virtual int32 GetMeleeMitDmg(Mob *attacker, int32 damage, int32 minhit, float mit_rating, float atk_rating);
bool CombatRange(Mob* other);
virtual inline bool IsBerserk() { return false; } // only clients
@@ -427,6 +428,8 @@ public:
bool bFrenzy = false, bool iBuffTic = false);
bool RemoveFromHateList(Mob* mob);
void SetHate(Mob* other, int32 hate = 0, int32 damage = 0) { hate_list.Set(other,hate,damage);}
void HalveAggro(Mob *other) { uint32 in_hate = GetHateAmount(other); SetHate(other, (in_hate > 1 ? in_hate / 2 : 1)); }
void DoubleAggro(Mob *other) { uint32 in_hate = GetHateAmount(other); SetHate(other, (in_hate ? in_hate * 2 : 1)); }
uint32 GetHateAmount(Mob* tmob, bool is_dam = false) { return hate_list.GetEntHate(tmob,is_dam);}
uint32 GetDamageAmount(Mob* tmob) { return hate_list.GetEntHate(tmob, true);}
Mob* GetHateTop() { return hate_list.GetTop(this);}
@@ -497,7 +500,7 @@ public:
bool AddSkillProc(uint16 spell_id, uint16 iChance = 3, uint16 base_spell_id = SPELL_UNKNOWN);
bool RemoveSkillProc(uint16 spell_id, bool bAll = false);
bool HasSkillProcs() const;
bool AddProcToWeapon(uint16 spell_id, bool bPerma = false, uint16 iChance = 3);
bool AddProcToWeapon(uint16 spell_id, bool bPerma = false, uint16 iChance = 3, uint16 base_spell_id = SPELL_UNKNOWN);
bool RemoveProcFromWeapon(uint16 spell_id, bool bAll = false);
bool HasProcs() const;
@@ -587,6 +590,9 @@ public:
int32 ApplySpellEffectiveness(Mob* caster, int16 spell_id, int32 value, bool IsBard = false);
int8 GetDecayEffectValue(uint16 spell_id, uint16 spelleffect);
int32 GetExtraSpellAmt(uint16 spell_id, int32 extra_spell_amt, int32 base_spell_dmg);
void MeleeLifeTap(int32 damage);
bool PassCastRestriction(bool UseCastRestriction = true, int16 value = 0, bool IsDamage = true);
bool ImprovedTaunt();
void ModSkillDmgTaken(SkillUseTypes skill_num, int value);
int16 GetModSkillDmgTaken(const SkillUseTypes skill_num);
@@ -878,7 +884,7 @@ protected:
bool IsFullHP;
bool moved;
std::vector<std::string> RampageArray;
std::vector<uint16> RampageArray;
std::map<std::string, std::string> m_EntityVariables;
int16 SkillDmgTaken_Mod[HIGHEST_SKILL+2];
+1 -2
View File
@@ -183,8 +183,7 @@ int Mob::mod_spell_stack(uint16 spellid1, int caster_level1, Mob* caster1, uint1
//Sum of various resists rolled against a value of 200.
int Mob::mod_spell_resist(int resist_chance, int level_mod, int resist_modifier, int target_resist, uint8 resist_type, uint16 spell_id, Mob* caster) {
int final = resist_chance + level_mod + resist_modifier + target_resist;
return(final);
return(resist_chance);
}
//Spell is cast by this on spelltar, called from spellontarget after the event_cast_on NPC event
+3 -1
View File
@@ -476,6 +476,8 @@ int main(int argc, char** argv) {
entity_list.Clear();
parse->ClearInterfaces();
#ifdef EMBPERL
safe_delete(perl_parser);
#endif
@@ -496,7 +498,7 @@ int main(int argc, char** argv) {
dbasync->StopThread();
safe_delete(taskmanager);
command_deinit();
safe_delete(parse);
CheckEQEMuErrorAndPause();
_log(ZONE__INIT, "Proper zone shutdown complete.");
return 0;
+68
View File
@@ -5341,6 +5341,72 @@ XS(XS_Mob_SetHate)
XSRETURN_EMPTY;
}
XS(XS_Mob_HalveAggro);
XS(XS_Mob_HalveAggro)
{
dXSARGS;
if (items != 2)
Perl_croak(aTHX_ "Usage: Mob::HalveAggro(THIS, other)");
{
Mob * THIS;
Mob * other;
if (sv_derived_from(ST(0), "Mob")) {
IV tmp = SvIV((SV*)SvRV(ST(0)));
THIS = INT2PTR(Mob *,tmp);
}
else
Perl_croak(aTHX_ "THIS is not of type Mob");
if(THIS == nullptr)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
if (sv_derived_from(ST(1), "Mob")) {
IV tmp = SvIV((SV*)SvRV(ST(1)));
other = INT2PTR(Mob *,tmp);
}
else
Perl_croak(aTHX_ "other is not of type Mob");
if(other == nullptr)
Perl_croak(aTHX_ "other is nullptr, avoiding crash.");
THIS->HalveAggro(other);
}
XSRETURN_EMPTY;
}
XS(XS_Mob_DoubleAggro);
XS(XS_Mob_DoubleAggro)
{
dXSARGS;
if (items != 2)
Perl_croak(aTHX_ "Usage: Mob::DoubleAggro(THIS, other)");
{
Mob * THIS;
Mob * other;
if (sv_derived_from(ST(0), "Mob")) {
IV tmp = SvIV((SV*)SvRV(ST(0)));
THIS = INT2PTR(Mob *,tmp);
}
else
Perl_croak(aTHX_ "THIS is not of type Mob");
if(THIS == nullptr)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
if (sv_derived_from(ST(1), "Mob")) {
IV tmp = SvIV((SV*)SvRV(ST(1)));
other = INT2PTR(Mob *,tmp);
}
else
Perl_croak(aTHX_ "other is not of type Mob");
if(other == nullptr)
Perl_croak(aTHX_ "other is nullptr, avoiding crash.");
THIS->DoubleAggro(other);
}
XSRETURN_EMPTY;
}
XS(XS_Mob_GetHateAmount); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_GetHateAmount)
{
@@ -8274,6 +8340,8 @@ XS(boot_Mob)
newXSproto(strcpy(buf, "IsRooted"), XS_Mob_IsRooted, file, "$");
newXSproto(strcpy(buf, "AddToHateList"), XS_Mob_AddToHateList, file, "$$;$$$$$");
newXSproto(strcpy(buf, "SetHate"), XS_Mob_SetHate, file, "$$;$$");
newXSproto(strcpy(buf, "HalveAggro"), XS_Mob_HalveAggro, file, "$$");
newXSproto(strcpy(buf, "DoubleAggro"), XS_Mob_DoubleAggro, file, "$$");
newXSproto(strcpy(buf, "GetHateAmount"), XS_Mob_GetHateAmount, file, "$$;$");
newXSproto(strcpy(buf, "GetDamageAmount"), XS_Mob_GetDamageAmount, file, "$$");
newXSproto(strcpy(buf, "GetHateTop"), XS_Mob_GetHateTop, file, "$");
+2 -2
View File
@@ -605,9 +605,9 @@ void NPC::SetPetState(SpellBuff_Struct *pet_buffs, uint32 *items) {
// We need to reapply buff based procs
// We need to do this here so suspended pets also regain their procs.
if (spells[buffs[j1].spellid].base2[x1] == 0) {
AddProcToWeapon(GetProcID(buffs[j1].spellid,x1), false, 100);
AddProcToWeapon(GetProcID(buffs[j1].spellid,x1), false, 100, buffs[j1].spellid);
} else {
AddProcToWeapon(GetProcID(buffs[j1].spellid,x1), false, 100+spells[buffs[j1].spellid].base2[x1]);
AddProcToWeapon(GetProcID(buffs[j1].spellid,x1), false, 100+spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid);
}
break;
case SE_Charm:
+17 -7
View File
@@ -155,6 +155,9 @@ void Mob::DoSpecialAttackDamage(Mob *who, SkillUseTypes skill, int32 max_damage,
if(!GetTarget())return;
if (HasDied()) return;
if (max_damage > 0)
CheckNumHitsRemaining(5);
//[AA Dragon Punch] value[0] = 100 for 25%, chance value[1] = skill
if(aabonuses.SpecialAttackKBProc[0] && aabonuses.SpecialAttackKBProc[1] == skill){
int kb_chance = 25;
@@ -606,14 +609,16 @@ void Mob::RogueBackstab(Mob* other, bool min_damage, int ReuseTime)
if(IsClient()){
const ItemInst *wpn = nullptr;
wpn = CastToClient()->GetInv().GetItem(SLOT_PRIMARY);
primaryweapondamage = GetWeaponDamage(other, wpn);
backstab_dmg = wpn->GetItem()->BackstabDmg;
for(int i = 0; i < MAX_AUGMENT_SLOTS; ++i)
{
ItemInst *aug = wpn->GetAugment(i);
if(aug)
if(wpn) {
primaryweapondamage = GetWeaponDamage(other, wpn);
backstab_dmg = wpn->GetItem()->BackstabDmg;
for(int i = 0; i < MAX_AUGMENT_SLOTS; ++i)
{
backstab_dmg += aug->GetItem()->BackstabDmg;
ItemInst *aug = wpn->GetAugment(i);
if(aug)
{
backstab_dmg += aug->GetItem()->BackstabDmg;
}
}
}
}
@@ -948,6 +953,7 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item
TryCriticalHit(other, SkillArchery, TotalDmg);
other->AddToHateList(this, hate, 0, false);
CheckNumHitsRemaining(5);
}
}
else
@@ -1052,6 +1058,7 @@ void NPC::RangedAttack(Mob* other)
TryCriticalHit(GetTarget(), SkillArchery, TotalDmg);
GetTarget()->AddToHateList(this, hate, 0, false);
GetTarget()->Damage(this, TotalDmg, SPELL_UNKNOWN, SkillArchery);
CheckNumHitsRemaining(5);
}
else
{
@@ -1270,6 +1277,7 @@ void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite
TryCriticalHit(other, SkillThrowing, TotalDmg);
int32 hate = (2*WDmg);
other->AddToHateList(this, hate, 0, false);
CheckNumHitsRemaining(5);
}
}
@@ -2181,6 +2189,8 @@ void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes
if (HasDied())
return;
CheckNumHitsRemaining(5);
if(aabonuses.SpecialAttackKBProc[0] && aabonuses.SpecialAttackKBProc[1] == skillinuse){
int kb_chance = 25;
kb_chance += kb_chance*(100-aabonuses.SpecialAttackKBProc[0])/100;
+436 -174
View File
@@ -183,7 +183,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
numhit += caster->CastToClient()->GetFocusEffect(focusIncreaseNumHits, spell_id);
}
Numhits(true);
buffs[buffslot].numhits = numhit;
}
@@ -218,83 +217,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
int32 dmg = effect_value;
if(dmg < 0)
{
/*Special Cases where Base2 is defined
Range 105 : Plant
Range 120 : Undead
Range 123 : Humanoid
Range 190 : No Raid boss flag *not implemented
Range 191 : This spell will deal less damage to 'exceptionally strong targets' - Raid boss flag *not implemented
Range 201 : Damage if HP > 75%
Range 221 - 299 : Causing damage dependent on how many pets/swarmpets are attacking your target.
Range 300 - 303 : UNKOWN *not implemented
Range 399 - 499 : Heal if HP within a specified range (400 = 0-25% 401 = 25 - 35% 402 = 35-45% ect)
Range 500 - 599 : Heal if HP less than a specified value
Range 600 - 699 : Limit to Body Type [base2 - 600 = Body]
Range 818 - 819 : If Undead/If Not Undead
Range 835 - : Unknown *not implemented
Range 836 - 837 : Progression Server / Live Server *not implemented
Range 839 - : Unknown *not implemented
Range 10000+ : Limit to Race [base2 - 10000 = Race] (*Not on live: Too useful a function to not implement)
*/
if (spells[spell_id].base2[i] > 0){
//It is unlikely these effects would give a fail message (Need to confirm)
if (spells[spell_id].base2[i] == 105){
if (GetBodyType() != BT_Plant)
break;
}
else if (spells[spell_id].base2[i] == 120){
if (GetBodyType() != BT_Undead)
break;
}
else if (spells[spell_id].base2[i] == 123){
if (GetBodyType() != BT_Humanoid)
break;
}
//Limit to Body Type.
else if (spells[spell_id].base2[i] >= 600 && spells[spell_id].base2[i] <= 699){
if (GetBodyType() != (spells[spell_id].base2[i] - 600)){
//caster->Message_StringID(13,CANNOT_AFFECT_NPC);
break;
}
}
else if (spells[spell_id].base2[i] == 201){
if (GetHPRatio() < 75)
break;
}
//Limit to Race. *Not implemented on live
else if (spells[spell_id].base2[i] >= 10000 && spells[spell_id].base2[i] <= 11000){
if (GetRace() != (spells[spell_id].base2[i] - 10000)){
break;
}
}
//Limit to amount of pets
else if (spells[spell_id].base2[i] >= 221 && spells[spell_id].base2[i] <= 299){
bool allow_spell = false;
int count = hate_list.SummonedPetCount(this);
for (int base2_value = 221; base2_value <= 233; ++base2_value){
if (spells[spell_id].base2[i] == base2_value){
if (count >= (base2_value - 220)){
allow_spell = true;
break;
}
}
}
if (!allow_spell)
break;
}
}
if (!PassCastRestriction(false, spells[spell_id].base2[i], true))
break;
// take partial damage into account
dmg = (int32) (dmg * partial / 100);
@@ -308,60 +232,10 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
}
else if(dmg > 0) {
//healing spell...
if (spells[spell_id].base2[i] > 0)
{
bool allow_spell = false;
//Heal only if HP within specified range. [Doesn't follow a set forumla for all values...]
if (spells[spell_id].base2[i] >= 400 && spells[spell_id].base2[i] <= 408){
for (int base2_value = 400; base2_value <= 408; ++base2_value){
if (spells[spell_id].base2[i] == base2_value){
if (spells[spell_id].base2[i] == 400){
if (GetHPRatio() <= 25){
allow_spell = true;
break;
}
}
else if (spells[spell_id].base2[i] == base2_value){
if (GetHPRatio() > 25+((base2_value - 401)*10) && GetHPRatio() <= 35+((base2_value - 401)*10)){
allow_spell = true;
break;
}
}
}
}
}
else if (spells[spell_id].base2[i] >= 500 && spells[spell_id].base2[i] <= 520){
for (int base2_value = 500; base2_value <= 520; ++base2_value){
if (spells[spell_id].base2[i] == base2_value){
if (spells[spell_id].base2[i] == base2_value){
if (GetHPRatio() < (base2_value - 500)*5) {
allow_spell = true;
break;
}
}
}
}
}
if (!PassCastRestriction(false, spells[spell_id].base2[i], false))
break;
else if (spells[spell_id].base2[i] == 399){
if (GetHPRatio() > 15 && GetHPRatio() <= 25){
allow_spell = true;
break;
}
}
if(!allow_spell)
break;
}
if(caster)
dmg = caster->GetActSpellHealing(spell_id, dmg, this);
@@ -1410,6 +1284,12 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
break;
}
case SE_MitigateDotDamage:
{
buffs[buffslot].dot_rune = spells[spell_id].max[i];
break;
}
case SE_TriggerMeleeThreshold:
{
buffs[buffslot].melee_rune = spells[spell_id].base2[i];
@@ -1422,7 +1302,14 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
break;
}
case SE_DistanceRemoval:
{
buffs[buffslot].caston_x = int(GetX());
buffs[buffslot].caston_y = int(GetY());
buffs[buffslot].caston_z = int(GetZ());
break;
}
case SE_Levitate:
{
#ifdef SPELL_EFFECT_SPAM
@@ -1434,6 +1321,20 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
break;
}
case SE_DeathSave: {
int16 mod = 0;
if(caster) {
mod = caster->aabonuses.UnfailingDivinity +
caster->itembonuses.UnfailingDivinity +
caster->spellbonuses.UnfailingDivinity;
}
buffs[buffslot].ExtraDIChance = mod;
break;
}
case SE_Illusion:
{
#ifdef SPELL_EFFECT_SPAM
@@ -1545,10 +1446,17 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
snprintf(effect_desc, _EDLEN, "Memory Blur: %d", effect_value);
#endif
int wipechance = spells[spell_id].base[i];
int bonus = spellbonuses.IncreaseChanceMemwipe + itembonuses.IncreaseChanceMemwipe + aabonuses.IncreaseChanceMemwipe;
int bonus = 0;
if (caster){
bonus = caster->spellbonuses.IncreaseChanceMemwipe +
caster->itembonuses.IncreaseChanceMemwipe +
caster->aabonuses.IncreaseChanceMemwipe;
}
wipechance += wipechance*bonus/100;
if(MakeRandomInt(0, 100) < wipechance)
if(MakeRandomInt(0, 99) < wipechance)
{
if(IsAIControlled())
{
@@ -1821,9 +1729,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
#endif
if(spells[spell_id].base2[i] == 0)
AddProcToWeapon(procid, false, 100);
AddProcToWeapon(procid, false, 100, spell_id);
else
AddProcToWeapon(procid, false, spells[spell_id].base2[i]+100);
AddProcToWeapon(procid, false, spells[spell_id].base2[i]+100, spell_id);
break;
}
@@ -2487,30 +2395,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
break;
}
case SE_DeathSave: {
#ifdef SPELL_EFFECT_SPAM
snprintf(effect_desc, _EDLEN, "Death Save: %+i", effect_value);
#endif
uint8 BonusChance = 0;
if(caster) {
BonusChance = caster->aabonuses.UnfailingDivinity +
caster->itembonuses.UnfailingDivinity +
caster->spellbonuses.UnfailingDivinity;
}
#ifdef SPELL_EFFECT_SPAM
//snprintf(effect_desc, _EDLEN, "Death Save Chance: %+i", SuccessChance);
#endif
//buffs[buffslot].deathSaveSuccessChance = SuccessChance;
//buffs[buffslot].deathsaveCasterAARank = caster->GetAA(aaUnfailingDivinity);
buffs[buffslot].deathsaveCasterAARank = BonusChance;
//SetDeathSaveChance(true);
break;
}
case SE_SummonAndResAllCorpses:
{
if(IsClient())
@@ -2757,7 +2641,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
case SE_MeleeMitigation:
case SE_Reflect:
case SE_Screech:
case SE_SingingSkill:
case SE_Amplification:
case SE_MagicWeapon:
case SE_Hunger:
case SE_MagnifyVision:
@@ -2793,7 +2677,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
case SE_ChangeAggro:
case SE_Hate2:
case SE_Identify:
case SE_Calm:
case SE_InstantHate:
case SE_ReduceHate:
case SE_SpellDamageShield:
case SE_ReverseDS:
@@ -2889,7 +2773,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
case SE_MitigateDamageShield:
case SE_FcBaseEffects:
case SE_LimitClass:
case SE_LimitSpellSubclass:
case SE_BlockBehind:
case SE_ShieldBlock:
case SE_PetCriticalHit:
@@ -2932,7 +2815,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
case SE_DoubleRangedAttack:
case SE_ShieldEquipHateMod:
case SE_ShieldEquipDmgMod:
case SE_TriggerOnValueAmount:
case SE_TriggerOnReqTarget:
case SE_LimitRace:
case SE_FcLimitUse:
case SE_FcMute:
@@ -2942,6 +2825,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
case SE_IncreaseChanceMemwipe:
case SE_CriticalMend:
case SE_LimitCastTimeMax:
case SE_TriggerOnReqCaster:
{
break;
}
@@ -3403,6 +3287,31 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste
break;
}
case SE_WipeHateList:
{
int wipechance = spells[spell_id].base[i];
int bonus = 0;
if (caster){
bonus = caster->spellbonuses.IncreaseChanceMemwipe +
caster->itembonuses.IncreaseChanceMemwipe +
caster->aabonuses.IncreaseChanceMemwipe;
}
wipechance += wipechance*bonus/100;
if(MakeRandomInt(0, 99) < 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, this, spell_id)) {
BuffFadeByEffect(SE_Charm);
@@ -3504,6 +3413,26 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste
}
break;
}
case SE_DistanceRemoval:
{
if (spellbonuses.DistanceRemoval){
int distance = sqrt(
((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]){
if(!TryFadeEffect(slot))
BuffFadeBySlot(slot , true);
}
break;
}
}
default:
{
// do we need to do anyting here?
@@ -4152,13 +4081,12 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id)
}
break;
case SE_LimitCombatSkills:
if (base1 == 0){
if((spell.cast_time == 0) && (spell.recast_time == 0) && (spell.recovery_time == 0)) //Exclude procs
LimitFailure = true;
}
break;
if (base1 == 0 && IsCombatSkill(spell_id)) //Exclude Discs
LimitFailure = true;
else if (base1 == 1 && !IsCombatSkill(spell_id)) //Exclude Spells
LimitFailure = true;
break;
case SE_LimitSpellGroup:
if(base1 < 0) {
@@ -4555,10 +4483,10 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo
break;
case SE_LimitCombatSkills:
if (focus_spell.base[i] == 0){
if((spell.cast_time == 0) && (spell.recast_time == 0) && (spell.recovery_time == 0)) //Exclude procs
return 0;
}
if (focus_spell.base[i] == 0 && IsCombatSkill(spell_id)) //Exclude Disc
return 0;
else if (focus_spell.base[i] == 1 && !IsCombatSkill(spell_id)) //Include Spells
return 0;
break;
case SE_LimitSpellGroup:
@@ -5376,11 +5304,13 @@ bool Mob::TryDeathSave() {
int SuccessChance = 0;
int buffSlot = spellbonuses.DeathSave[1];
uint8 UD_HealMod = buffs[buffSlot].deathsaveCasterAARank; //Contains value of UD heal modifier.
int16 UD_HealMod = 0;
uint32 HealAmt = 300; //Death Pact max Heal
if(buffSlot >= 0){
UD_HealMod = buffs[buffSlot].ExtraDIChance;
SuccessChance = ( (GetCHA() * (RuleI(Spells, DeathSaveCharismaMod))) + 1) / 10; //(CHA Mod Default = 3)
if (SuccessChance > 95)
@@ -5445,6 +5375,8 @@ bool Mob::TryDeathSave() {
}
}
}
BuffFadeBySlot(buffSlot);
}
return false;
}
@@ -5704,3 +5636,333 @@ bool Mob::TryDispel(uint8 caster_level, uint8 buff_level, int level_modifier){
return false;
}
bool Mob::ImprovedTaunt(){
if (spellbonuses.ImprovedTaunt[0]){
if (GetLevel() > spellbonuses.ImprovedTaunt[0])
return false;
if (spellbonuses.ImprovedTaunt[2] >= 0){
target = entity_list.GetMob(buffs[spellbonuses.ImprovedTaunt[2]].casterid);
if (target){
SetTarget(target);
return true;
}
else {
if(!TryFadeEffect(spellbonuses.ImprovedTaunt[2]))
BuffFadeBySlot(spellbonuses.ImprovedTaunt[2], true); //If caster killed removed effect.
}
}
}
return false;
}
bool Mob::PassCastRestriction(bool UseCastRestriction, int16 value, bool IsDamage)
{
/*If return TRUE spell met all restrictions and can continue (this = target).
This check is used when the spell_new field CastRestriction is defined OR spell effect '0'(DD/Heal) has a defined limit
Range 1 : UNKNOWN
Range 100 : *Animal OR Humanoid
Range 101 : *Dragon
Range 102 : *Animal OR Insect
Range 103 : NOT USED
Range 104 : *Animal
Range 105 : Plant
Range 106 : *Giant
Range 107 : NOT USED
Range 108 : NOT Animal or Humaniod
Range 109 : *Bixie
Range 111 : *Harpy
Range 112 : *Sporali
Range 113 : *Kobold
Range 114 : *Shade Giant
Range 115 : *Drakkin
Range 116 : NOT USED
Range 117 : *Animal OR Plant
Range 118 : *Summoned
Range 119 : *Firepet
Range 120 : Undead
Range 121 : *Living (NOT Undead)
Range 122 : *Fairy
Range 123 : Humanoid
Range 124 : *Undead HP < 10%
Range 125 : *Clockwork HP < 10%
Range 126 : *Wisp HP < 10%
Range 127-130 : UNKNOWN
Range 150 : UNKNOWN
Range 190 : No Raid boss flag *not implemented
Range 191 : This spell will deal less damage to 'exceptionally strong targets' - Raid boss flag *not implemented
Range 201 : Damage if HP > 75%
Range 203 : Damage if HP < 20%
Range 216 : TARGET NOT IN COMBAT
Range 221 - 249 : Causing damage dependent on how many pets/swarmpets are attacking your target.
Range 250 : Damage if HP < 35%
Range 300 - 303 : UNKOWN *not implemented
Range 304 : Chain + Plate class (buffs)
Range 399 - 409 : Heal if HP within a specified range (400 = 0-25% 401 = 25 - 35% 402 = 35-45% ect)
Range 410 - 411 : UNKOWN
Range 500 - 599 : Heal if HP less than a specified value
Range 600 - 699 : Limit to Body Type [base2 - 600 = Body]
Range 700 : UNKNOWN
Range 701 : NOT PET
Range 800 : UKNOWN
Range 818 - 819 : If Undead/If Not Undead
Range 820 - 822 : UKNOWN
Range 835 : Unknown *not implemented
Range 836 - 837 : Progression Server / Live Server *not implemented
Range 839 : Unknown *not implemented
Range 842 - 844 : Humaniod lv MAX ((842 - 800) * 2)
Range 845 - 847 : UNKNOWN
Range 10000 - 11000 : Limit to Race [base2 - 10000 = Race] (*Not on live: Too useful a function to not implement)
THIS IS A WORK IN PROGRESS
*/
if (value <= 0)
return true;
if (IsDamage || UseCastRestriction) {
switch(value)
{
case 100:
if ((GetBodyType() == BT_Animal) || (GetBodyType() == BT_Humanoid))
return true;
break;
case 101:
if (GetBodyType() == BT_Dragon || GetBodyType() == BT_VeliousDragon || GetBodyType() == BT_Dragon3)
return true;
break;
case 102:
if ((GetBodyType() == BT_Animal) || (GetBodyType() == BT_Insect))
return true;
break;
case 104:
if (GetBodyType() == BT_Animal)
return true;
break;
case 105:
if (GetBodyType() == BT_Plant)
return true;
break;
case 106:
if (GetBodyType() == BT_Giant)
return true;
break;
case 108:
if ((GetBodyType() != BT_Animal) || (GetBodyType() != BT_Humanoid))
return true;
break;
case 109:
if ((GetRace() == 520) ||(GetRace() == 79))
return true;
break;
case 111:
if ((GetRace() == 527) ||(GetRace() == 11))
return true;
break;
case 112:
if ((GetRace() == 456) ||(GetRace() == 28))
return true;
break;
case 113:
if ((GetRace() == 456) ||(GetRace() == 48))
return true;
break;
case 114:
if (GetRace() == 526)
return true;
break;
case 115:
if (GetRace() == 522)
return true;
break;
case 117:
if ((GetBodyType() == BT_Animal) || (GetBodyType() == BT_Plant))
return true;
break;
case 118:
if (GetBodyType() == BT_Summoned)
return true;
break;
case 119:
if (IsPet() && ((GetRace() == 212) || ((GetRace() == 75) && GetTexture() == 1)))
return true;
break;
case 120:
if (GetBodyType() == BT_Undead)
return true;
break;
case 121:
if (GetBodyType() != BT_Undead)
return true;
break;
case 122:
if ((GetRace() == 473) || (GetRace() == 425))
return true;
break;
case 123:
if (GetBodyType() == BT_Humanoid)
return true;
break;
case 124:
if ((GetBodyType() == BT_Undead) && (GetHPRatio() < 10))
return true;
break;
case 125:
if ((GetRace() == 457 || GetRace() == 88) && (GetHPRatio() < 10))
return true;
break;
case 126:
if ((GetRace() == 581 || GetRace() == 69) && (GetHPRatio() < 10))
return true;
break;
case 201:
if (GetHPRatio() > 75)
return true;
break;
case 204:
if (GetHPRatio() < 20)
return true;
break;
case 216:
if (!IsEngaged())
return true;
break;
case 250:
if (GetHPRatio() < 35)
return true;
break;
case 304:
if (IsClient() &&
((GetClass() == WARRIOR) || (GetClass() == BARD) || (GetClass() == SHADOWKNIGHT) || (GetClass() == PALADIN) || (GetClass() == CLERIC)
|| (GetClass() == RANGER) || (GetClass() == SHAMAN) || (GetClass() == ROGUE) || (GetClass() == BERSERKER)))
return true;
break;
case 701:
if (!IsPet())
return true;
break;
case 818:
if (GetBodyType() == BT_Undead)
return true;
break;
case 819:
if (GetBodyType() != BT_Undead)
return true;
break;
case 842:
if (GetBodyType() == BT_Humanoid && GetLevel() <= 84)
return true;
break;
case 843:
if (GetBodyType() == BT_Humanoid && GetLevel() <= 86)
return true;
break;
case 844:
if (GetBodyType() == BT_Humanoid && GetLevel() <= 88)
return true;
break;
}
//Limit to amount of pets
if (value >= 221 && value <= 249){
int count = hate_list.SummonedPetCount(this);
for (int base2_value = 221; base2_value <= 249; ++base2_value){
if (value == base2_value){
if (count >= (base2_value - 220)){
return true;
}
}
}
}
//Limit to Body Type
if (value >= 600 && value <= 699){
if (GetBodyType() == (value - 600))
return true;
}
//Limit to Race. *Not implemented on live
if (value >= 10000 && value <= 11000){
if (GetRace() == (value - 10000))
return true;
}
} //End Damage
if (!IsDamage || UseCastRestriction) {
//Heal only if HP within specified range. [Doesn't follow a set forumla for all values...]
if (value >= 400 && value <= 408){
for (int base2_value = 400; base2_value <= 408; ++base2_value){
if (value == base2_value){
if (value == 400 && GetHPRatio() <= 25)
return true;
else if (value == base2_value){
if (GetHPRatio() > 25+((base2_value - 401)*10) && GetHPRatio() <= 35+((base2_value - 401)*10))
return true;
}
}
}
}
else if (value >= 500 && value <= 549){
for (int base2_value = 500; base2_value <= 520; ++base2_value){
if (value == base2_value){
if (GetHPRatio() < (base2_value - 500)*5)
return true;
}
}
}
else if (value == 399) {
if (GetHPRatio() > 15 && GetHPRatio() <= 25)
return true;
}
} // End Heal
return false;
}
+21 -10
View File
@@ -1361,6 +1361,11 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce
mlog(AA__MESSAGE, "Project Illusion overwrote target caster: %s spell id: %d was ON", GetName(), spell_id);
targetType = ST_GroupClientAndPet;
}
if (spell_target && !spell_target->PassCastRestriction(true, spells[spell_id].CastRestriction)){
Message_StringID(13,SPELL_NEED_TAR);
return false;
}
switch (targetType)
{
@@ -2948,8 +2953,11 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid
buffs[emptyslot].numhits = spells[spell_id].numhits;
buffs[emptyslot].client = caster ? caster->IsClient() : 0;
buffs[emptyslot].persistant_buff = 0;
buffs[emptyslot].deathsaveCasterAARank = 0;
buffs[emptyslot].deathSaveSuccessChance = 0;
buffs[emptyslot].caston_x = 0;
buffs[emptyslot].caston_y = 0;
buffs[emptyslot].caston_z = 0;
buffs[emptyslot].dot_rune = 0;
buffs[emptyslot].ExtraDIChance = 0;
if (level_override > 0) {
buffs[emptyslot].UpdateClient = true;
@@ -3355,7 +3363,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r
}
}
// Reflect
if(TryReflectSpell(spell_id) && spelltar && !reflect && IsDetrimentalSpell(spell_id) && this != spelltar) {
if(spelltar && spelltar->TryReflectSpell(spell_id) && !reflect && IsDetrimentalSpell(spell_id) && this != spelltar) {
int reflect_chance = 0;
switch(RuleI(Spells, ReflectType))
{
@@ -3402,9 +3410,6 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r
}
}
if (spelltar && IsDetrimentalSpell(spell_id))
spelltar->CheckNumHitsRemaining(3);
// resist check - every spell can be resisted, beneficial or not
// add: ok this isn't true, eqlive's spell data is fucked up, buffs are
// not all unresistable, so changing this to only check certain spells
@@ -3438,6 +3443,8 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r
}
}
spelltar->CheckNumHitsRemaining(3);
safe_delete(action_packet);
return false;
}
@@ -3575,7 +3582,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r
safe_delete(action_packet);
return false;
}
// cause the effects to the target
if(!spelltar->SpellEffect(this, spell_id, spell_effectiveness))
{
@@ -3588,6 +3595,10 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r
return false;
}
if (spelltar && IsDetrimentalSpell(spell_id))
spelltar->CheckNumHitsRemaining(3); //Incoming spells
// send the action packet again now that the spell is successful
// NOTE: this is what causes the buff icon to appear on the client, if
// this is a buff - but it sortof relies on the first packet.
@@ -4816,7 +4827,7 @@ bool Mob::FindType(uint16 type, bool bOffensive, uint16 threshold) {
return false;
}
bool Mob::AddProcToWeapon(uint16 spell_id, bool bPerma, uint16 iChance) {
bool Mob::AddProcToWeapon(uint16 spell_id, bool bPerma, uint16 iChance, uint16 base_spell_id) {
if(spell_id == SPELL_UNKNOWN)
return(false);
@@ -4826,7 +4837,7 @@ bool Mob::AddProcToWeapon(uint16 spell_id, bool bPerma, uint16 iChance) {
if (PermaProcs[i].spellID == SPELL_UNKNOWN) {
PermaProcs[i].spellID = spell_id;
PermaProcs[i].chance = iChance;
PermaProcs[i].base_spellID = SPELL_UNKNOWN;
PermaProcs[i].base_spellID = base_spell_id;
mlog(SPELLS__PROCS, "Added permanent proc spell %d with chance %d to slot %d", spell_id, iChance, i);
return true;
@@ -4838,7 +4849,7 @@ bool Mob::AddProcToWeapon(uint16 spell_id, bool bPerma, uint16 iChance) {
if (SpellProcs[i].spellID == SPELL_UNKNOWN) {
SpellProcs[i].spellID = spell_id;
SpellProcs[i].chance = iChance;
SpellProcs[i].base_spellID = SPELL_UNKNOWN;;
SpellProcs[i].base_spellID = base_spell_id;;
mlog(SPELLS__PROCS, "Added spell-granted proc spell %d with chance %d to slot %d", spell_id, iChance, i);
return true;
}
+31 -22
View File
@@ -1815,7 +1815,7 @@ void ZoneDatabase::SaveMercBuffs(Merc *merc) {
if(!database.RunQuery(Query, MakeAnyLenString(&Query, "INSERT INTO merc_buffs (MercId, SpellId, CasterLevel, DurationFormula, "
"TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, CorruptionCounters, HitCount, MeleeRune, MagicRune, "
"DeathSaveSuccessChance, CasterAARank, Persistent) VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u);",
"dot_rune, caston_x, Persistent, caston_y, caston_z, ExtraDIChance) VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %i, %u, %i, %i, %i);",
merc->GetMercID(), buffs[BuffCount].spellid, buffs[BuffCount].casterlevel, spells[buffs[BuffCount].spellid].buffdurationformula,
buffs[BuffCount].ticsremaining,
CalculatePoisonCounters(buffs[BuffCount].spellid) > 0 ? buffs[BuffCount].counters : 0,
@@ -1823,8 +1823,12 @@ void ZoneDatabase::SaveMercBuffs(Merc *merc) {
CalculateCurseCounters(buffs[BuffCount].spellid) > 0 ? buffs[BuffCount].counters : 0,
CalculateCorruptionCounters(buffs[BuffCount].spellid) > 0 ? buffs[BuffCount].counters : 0,
buffs[BuffCount].numhits, buffs[BuffCount].melee_rune, buffs[BuffCount].magic_rune,
buffs[BuffCount].deathSaveSuccessChance,
buffs[BuffCount].deathsaveCasterAARank, IsPersistent), TempErrorMessageBuffer)) {
buffs[BuffCount].dot_rune,
buffs[BuffCount].caston_x,
IsPersistent,
buffs[BuffCount].caston_y,
buffs[BuffCount].caston_z,
buffs[BuffCount].ExtraDIChance), TempErrorMessageBuffer)) {
errorMessage = std::string(TempErrorMessageBuffer);
safe_delete(Query);
Query = 0;
@@ -1856,7 +1860,7 @@ void ZoneDatabase::LoadMercBuffs(Merc *merc) {
bool BuffsLoaded = false;
if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT SpellId, CasterLevel, DurationFormula, TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, CorruptionCounters, HitCount, MeleeRune, MagicRune, DeathSaveSuccessChance, CasterAARank, Persistent FROM merc_buffs WHERE MercId = %u", merc->GetMercID()), TempErrorMessageBuffer, &DatasetResult)) {
if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT SpellId, CasterLevel, DurationFormula, TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, CorruptionCounters, HitCount, MeleeRune, MagicRune, dot_rune, caston_x, Persistent, caston_y, caston_z, ExtraDIChance FROM merc_buffs WHERE MercId = %u", merc->GetMercID()), TempErrorMessageBuffer, &DatasetResult)) {
errorMessage = std::string(TempErrorMessageBuffer);
}
else {
@@ -1882,8 +1886,8 @@ void ZoneDatabase::LoadMercBuffs(Merc *merc) {
buffs[BuffCount].numhits = atoi(DataRow[8]);
buffs[BuffCount].melee_rune = atoi(DataRow[9]);
buffs[BuffCount].magic_rune = atoi(DataRow[10]);
buffs[BuffCount].deathSaveSuccessChance = atoi(DataRow[11]);
buffs[BuffCount].deathsaveCasterAARank = atoi(DataRow[12]);
buffs[BuffCount].dot_rune = atoi(DataRow[11]);
buffs[BuffCount].caston_x = atoi(DataRow[12]);
buffs[BuffCount].casterid = 0;
bool IsPersistent = false;
@@ -1891,6 +1895,10 @@ void ZoneDatabase::LoadMercBuffs(Merc *merc) {
if(atoi(DataRow[13]))
IsPersistent = true;
buffs[BuffCount].caston_y = atoi(DataRow[13]);
buffs[BuffCount].caston_z = atoi(DataRow[14]);
buffs[BuffCount].ExtraDIChance = atoi(DataRow[15]);
buffs[BuffCount].persistant_buff = IsPersistent;
BuffCount++;
@@ -2484,9 +2492,9 @@ void ZoneDatabase::ListAllInstances(Client* c, uint32 charid)
MYSQL_ROW row;
if (RunQuery(query,MakeAnyLenString(&query, "SELECT instance_lockout.id, zone, version FROM instance_lockout JOIN"
" instance_lockout_player ON instance_lockout.id = instance_lockout_player.id"
" WHERE instance_lockout_player.charid=%lu", (unsigned long)charid),errbuf,&result))
if (RunQuery(query,MakeAnyLenString(&query, "SELECT instance_list.id, zone, version FROM instance_list JOIN"
" instance_list_player ON instance_list.id = instance_list_player.id"
" WHERE instance_list_player.charid=%lu", (unsigned long)charid),errbuf,&result))
{
safe_delete_array(query);
@@ -2566,11 +2574,11 @@ void ZoneDatabase::SaveBuffs(Client *c) {
for (int i = 0; i < buff_count; i++) {
if(buffs[i].spellid != SPELL_UNKNOWN) {
if(!database.RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `character_buffs` (character_id, slot_id, spell_id, "
"caster_level, caster_name, ticsremaining, counters, numhits, melee_rune, magic_rune, persistent, death_save_chance, "
"death_save_aa_chance) VALUES('%u', '%u', '%u', '%u', '%s', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u')",
"caster_level, caster_name, ticsremaining, counters, numhits, melee_rune, magic_rune, persistent, dot_rune, "
"caston_x, caston_y, caston_z, ExtraDIChance) VALUES('%u', '%u', '%u', '%u', '%s', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%i', '%i', '%i', '%i')",
c->CharacterID(), i, buffs[i].spellid, buffs[i].casterlevel, buffs[i].caster_name, buffs[i].ticsremaining,
buffs[i].counters, buffs[i].numhits, buffs[i].melee_rune, buffs[i].magic_rune, buffs[i].persistant_buff,
buffs[i].deathSaveSuccessChance, buffs[i].deathsaveCasterAARank),
buffs[i].dot_rune, buffs[i].caston_x, buffs[i].caston_y, buffs[i].caston_z, buffs[i].ExtraDIChance),
errbuf)) {
LogFile->write(EQEMuLog::Error, "Error in SaveBuffs query '%s': %s", query, errbuf);
}
@@ -2592,7 +2600,7 @@ void ZoneDatabase::LoadBuffs(Client *c) {
MYSQL_RES *result;
MYSQL_ROW row;
if (RunQuery(query, MakeAnyLenString(&query, "SELECT spell_id, slot_id, caster_level, caster_name, ticsremaining, counters, "
"numhits, melee_rune, magic_rune, persistent, death_save_chance, death_save_aa_chance FROM `character_buffs` WHERE "
"numhits, melee_rune, magic_rune, persistent, dot_rune, caston_x, caston_y, caston_z, ExtraDIChance FROM `character_buffs` WHERE "
"`character_id`='%u'",
c->CharacterID()), errbuf, &result))
{
@@ -2617,8 +2625,11 @@ void ZoneDatabase::LoadBuffs(Client *c) {
uint32 melee_rune = atoul(row[7]);
uint32 magic_rune = atoul(row[8]);
uint8 persistent = atoul(row[9]);
uint32 death_save_chance = atoul(row[10]);
uint32 death_save_aa_chance = atoul(row[11]);
uint32 dot_rune = atoul(row[10]);
int32 caston_x = atoul(row[11]);
int32 caston_y = atoul(row[12]);
int32 caston_z = atoul(row[13]);
int32 ExtraDIChance = atoul(row[14]);
buffs[slot_id].spellid = spell_id;
buffs[slot_id].casterlevel = caster_level;
@@ -2638,8 +2649,11 @@ void ZoneDatabase::LoadBuffs(Client *c) {
buffs[slot_id].melee_rune = melee_rune;
buffs[slot_id].magic_rune = magic_rune;
buffs[slot_id].persistant_buff = persistent ? true : false;
buffs[slot_id].deathSaveSuccessChance = death_save_chance;
buffs[slot_id].deathsaveCasterAARank = death_save_aa_chance;
buffs[slot_id].dot_rune = dot_rune;
buffs[slot_id].caston_x = caston_x;
buffs[slot_id].caston_y = caston_y;
buffs[slot_id].caston_z = caston_z;
buffs[slot_id].ExtraDIChance = ExtraDIChance;
buffs[slot_id].UpdateClient = false;
if(IsRuneSpell(spell_id)) {
c->SetHasRune(true);
@@ -2648,11 +2662,6 @@ void ZoneDatabase::LoadBuffs(Client *c) {
c->SetHasSpellRune(true);
}
/*
if(IsDeathSaveSpell(spell_id)) {
c->SetDeathSaveChance(true);
}
*/
}
mysql_free_result(result);
}