Implement mob and client melee push

New rules:
Combat:MeleePush turns melee push on/off
Combat:MeleePushChance is the chance that an NPC will be pushed
Clients are pushed every successful hit, need to verify or disprove this
This commit is contained in:
Michael Cook (mackal) 2015-04-30 19:36:21 -04:00
parent 8b4d601027
commit 06f4fd49ef
21 changed files with 257 additions and 178 deletions

View File

@ -1,5 +1,10 @@
EQEMu Changelog (Started on Sept 24, 2003 15:50) EQEMu Changelog (Started on Sept 24, 2003 15:50)
------------------------------------------------------- -------------------------------------------------------
== 04/30/2015 ==
demonstar55: Implement mob and client melee push
You can set Combat:MeleePush to false to turn off or change Combat:MeleePushChance to increase the chance an NPC can be pushed
PCs are always pushed, need to do more testing to verify.
== 04/22/2015 == == 04/22/2015 ==
Uleat: Probable fix for 'Debug Assertion Failure' in Client::GarbleMessage() when calling the 'isalpha' macro. Uleat: Probable fix for 'Debug Assertion Failure' in Client::GarbleMessage() when calling the 'isalpha' macro.
ref: https://connect.microsoft.com/VisualStudio/feedback/details/932876/calling-isdigit-with-a-signed-char-1-results-in-a-assert-failure-in-debug-compiles ref: https://connect.microsoft.com/VisualStudio/feedback/details/932876/calling-isdigit-with-a-signed-char-1-results-in-a-assert-failure-in-debug-compiles

View File

@ -1318,9 +1318,9 @@ struct CombatDamage_Struct
/* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells /* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells
/* 05 */ uint16 spellid; /* 05 */ uint16 spellid;
/* 07 */ uint32 damage; /* 07 */ uint32 damage;
/* 11 */ uint32 unknown11; /* 11 */ float force;
/* 15 */ uint32 sequence; // see above notes in Action_Struct /* 15 */ float meleepush_xy; // see above notes in Action_Struct
/* 19 */ uint32 unknown19; /* 19 */ float meleepush_z;
/* 23 */ /* 23 */
}; };

View File

@ -658,7 +658,9 @@ namespace RoF
OUT(type); OUT(type);
OUT(spellid); OUT(spellid);
OUT(damage); OUT(damage);
eq->sequence = emu->sequence; OUT(force)
OUT(meleepush_xy);
OUT(meleepush_z)
FINISH_ENCODE(); FINISH_ENCODE();
} }
@ -4389,7 +4391,7 @@ namespace RoF
IN(type); IN(type);
IN(spellid); IN(spellid);
IN(damage); IN(damage);
emu->sequence = eq->sequence; IN(meleepush_xy);
FINISH_DIRECT_DECODE(); FINISH_DIRECT_DECODE();
} }

View File

@ -729,7 +729,9 @@ namespace RoF2
OUT(type); OUT(type);
OUT(spellid); OUT(spellid);
OUT(damage); OUT(damage);
eq->sequence = emu->sequence; OUT(force)
OUT(meleepush_xy);
OUT(meleepush_z)
FINISH_ENCODE(); FINISH_ENCODE();
} }
@ -4538,7 +4540,7 @@ namespace RoF2
IN(type); IN(type);
IN(spellid); IN(spellid);
IN(damage); IN(damage);
emu->sequence = eq->sequence; IN(meleepush_xy);
FINISH_DIRECT_DECODE(); FINISH_DIRECT_DECODE();
} }

View File

@ -1484,9 +1484,10 @@ struct CombatDamage_Struct
/* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells /* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells
/* 05 */ uint32 spellid; /* 05 */ uint32 spellid;
/* 09 */ int32 damage; /* 09 */ int32 damage;
/* 13 */ float unknown11; // cd cc cc 3d /* 13 */ float force; // cd cc cc 3d
/* 17 */ float sequence; // see above notes in Action_Struct /* 17 */ float meleepush_xy; // see above notes in Action_Struct
/* 21 */ uint8 unknown19[9]; // was [9] /* 21 */ float meleepush_z;
/* 25 */ uint8 unknown25[5]; // was [9]
/* 30 */ /* 30 */
}; };

View File

@ -1514,9 +1514,10 @@ struct CombatDamage_Struct
/* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells /* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells
/* 05 */ uint32 spellid; /* 05 */ uint32 spellid;
/* 09 */ int32 damage; /* 09 */ int32 damage;
/* 13 */ float unknown11; // cd cc cc 3d /* 13 */ float force; // cd cc cc 3d
/* 17 */ float sequence; // see above notes in Action_Struct /* 17 */ float meleepush_xy; // see above notes in Action_Struct
/* 21 */ uint8 unknown19[9]; // was [9] /* 21 */ float meleepush_z;
/* 25 */ uint8 unknown25[5]; // was [9]
/* 30 */ /* 30 */
}; };

View File

@ -446,7 +446,9 @@ namespace SoD
OUT(type); OUT(type);
OUT(spellid); OUT(spellid);
OUT(damage); OUT(damage);
eq->sequence = emu->sequence; OUT(force)
OUT(meleepush_xy);
OUT(meleepush_z)
FINISH_ENCODE(); FINISH_ENCODE();
} }

View File

@ -1272,9 +1272,10 @@ struct CombatDamage_Struct
/* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells /* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells
/* 05 */ uint16 spellid; /* 05 */ uint16 spellid;
/* 07 */ int32 damage; /* 07 */ int32 damage;
/* 11 */ float unknown11; // cd cc cc 3d /* 11 */ float force; // cd cc cc 3d
/* 15 */ float sequence; // see above notes in Action_Struct /* 15 */ float meleepush_xy; // see above notes in Action_Struct
/* 19 */ uint8 unknown19[9]; // was [9] /* 19 */ float meleepush_z;
/* 23 */ uint8 unknown23[5]; // was [9]
/* 28 */ /* 28 */
}; };

View File

@ -426,7 +426,9 @@ namespace SoF
OUT(type); OUT(type);
OUT(spellid); OUT(spellid);
OUT(damage); OUT(damage);
eq->sequence = emu->sequence; OUT(force)
OUT(meleepush_xy);
OUT(meleepush_z)
FINISH_ENCODE(); FINISH_ENCODE();
} }

View File

@ -1249,9 +1249,10 @@ struct CombatDamage_Struct
/* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells /* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells
/* 05 */ uint16 spellid; /* 05 */ uint16 spellid;
/* 07 */ int32 damage; /* 07 */ int32 damage;
/* 11 */ float unknown11; // cd cc cc 3d /* 11 */ float force; // cd cc cc 3d
/* 15 */ float sequence; // see above notes in Action_Struct /* 15 */ float meleepush_xy; // see above notes in Action_Struct
/* 19 */ uint8 unknown19[9]; // was [9] /* 19 */ float meleepush_z;
/* 23 */ uint8 unknown23[5]; // was [9]
/* 28 */ /* 28 */
}; };

View File

@ -1101,9 +1101,9 @@ struct CombatDamage_Struct
/* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells /* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells
/* 05 */ uint16 spellid; /* 05 */ uint16 spellid;
/* 07 */ uint32 damage; /* 07 */ uint32 damage;
/* 11 */ uint32 unknown11; /* 11 */ float force;
/* 15 */ uint32 sequence; // see above notes in Action_Struct /* 15 */ float meleepush_xy; // see above notes in Action_Struct
/* 19 */ uint32 unknown19; /* 19 */ float meleepush_z;
/* 23 */ /* 23 */
}; };

View File

@ -581,7 +581,9 @@ namespace UF
OUT(type); OUT(type);
OUT(spellid); OUT(spellid);
OUT(damage); OUT(damage);
eq->sequence = emu->sequence; OUT(force)
OUT(meleepush_xy);
OUT(meleepush_z)
FINISH_ENCODE(); FINISH_ENCODE();
} }
@ -3356,7 +3358,7 @@ namespace UF
IN(type); IN(type);
IN(spellid); IN(spellid);
IN(damage); IN(damage);
emu->sequence = eq->sequence; IN(meleepush_xy);
FINISH_DIRECT_DECODE(); FINISH_DIRECT_DECODE();
} }

View File

@ -1330,9 +1330,10 @@ struct CombatDamage_Struct
/* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells /* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells
/* 05 */ uint16 spellid; /* 05 */ uint16 spellid;
/* 07 */ int32 damage; /* 07 */ int32 damage;
/* 11 */ float unknown11; // cd cc cc 3d /* 11 */ float force; // cd cc cc 3d
/* 15 */ float sequence; // see above notes in Action_Struct /* 15 */ float meleepush_xy; // see above notes in Action_Struct
/* 19 */ uint8 unknown19[9]; // was [9] /* 19 */ float meleepush_z;
/* 23 */ uint8 unknown23[5]; // was [9]
/* 28 */ /* 28 */
}; };

View File

@ -377,7 +377,7 @@ RULE_REAL ( Combat, HitBonusPerLevel, 1.2) //You gain this % of hit for every le
RULE_REAL ( Combat, WeaponSkillFalloff, 0.33) //For every weapon skill point that's not maxed you lose this % of hit RULE_REAL ( Combat, WeaponSkillFalloff, 0.33) //For every weapon skill point that's not maxed you lose this % of hit
RULE_REAL ( Combat, ArcheryHitPenalty, 0.25) //Archery has a hit penalty to try to help balance it with the plethora of long term +hit modifiers for it RULE_REAL ( Combat, ArcheryHitPenalty, 0.25) //Archery has a hit penalty to try to help balance it with the plethora of long term +hit modifiers for it
RULE_REAL ( Combat, AgiHitFactor, 0.01) RULE_REAL ( Combat, AgiHitFactor, 0.01)
RULE_REAL ( Combat, MinChancetoHit, 5.0) //Minimum % chance to hit with regular melee/ranged RULE_REAL ( Combat, MinChancetoHit, 5.0) //Minimum % chance to hit with regular melee/ranged
RULE_REAL ( Combat, MaxChancetoHit, 95.0) //Maximum % chance to hit with regular melee/ranged RULE_REAL ( Combat, MaxChancetoHit, 95.0) //Maximum % chance to hit with regular melee/ranged
RULE_INT ( Combat, MinRangedAttackDist, 25) //Minimum Distance to use Ranged Attacks RULE_INT ( Combat, MinRangedAttackDist, 25) //Minimum Distance to use Ranged Attacks
RULE_BOOL ( Combat, ArcheryBonusRequiresStationary, true) //does the 2x archery bonus chance require a stationary npc RULE_BOOL ( Combat, ArcheryBonusRequiresStationary, true) //does the 2x archery bonus chance require a stationary npc
@ -438,6 +438,8 @@ RULE_INT ( Combat, BerserkerFrenzyStart, 35)
RULE_INT ( Combat, BerserkerFrenzyEnd, 45) RULE_INT ( Combat, BerserkerFrenzyEnd, 45)
RULE_BOOL ( Combat, OneProcPerWeapon, true) //If enabled, One proc per weapon per round RULE_BOOL ( Combat, OneProcPerWeapon, true) //If enabled, One proc per weapon per round
RULE_BOOL ( Combat, ProjectileDmgOnImpact, true) //If enabled, projectiles (ie arrows) will hit on impact, instead of instantly. RULE_BOOL ( Combat, ProjectileDmgOnImpact, true) //If enabled, projectiles (ie arrows) will hit on impact, instead of instantly.
RULE_BOOL ( Combat, MeleePush, true) // enable melee push
RULE_INT ( Combat, MeleePushChance, 50) // (NPCs) chance the target will be pushed. Made up, 100 actually isn't that bad
RULE_CATEGORY_END() RULE_CATEGORY_END()
RULE_CATEGORY( NPC ) RULE_CATEGORY( NPC )
@ -577,7 +579,7 @@ RULE_INT ( Console, SessionTimeOut, 600000 ) // Amount of time in ms for the con
RULE_CATEGORY_END() RULE_CATEGORY_END()
RULE_CATEGORY( QueryServ ) RULE_CATEGORY( QueryServ )
RULE_BOOL( QueryServ, PlayerLogChat, false) // Logs Player Chat RULE_BOOL( QueryServ, PlayerLogChat, false) // Logs Player Chat
RULE_BOOL( QueryServ, PlayerLogTrades, false) // Logs Player Trades RULE_BOOL( QueryServ, PlayerLogTrades, false) // Logs Player Trades
RULE_BOOL( QueryServ, PlayerLogHandins, false) // Logs Player Handins RULE_BOOL( QueryServ, PlayerLogHandins, false) // Logs Player Handins
RULE_BOOL( QueryServ, PlayerLogNPCKills, false) // Logs Player NPC Kills RULE_BOOL( QueryServ, PlayerLogNPCKills, false) // Logs Player NPC Kills
@ -590,7 +592,7 @@ RULE_BOOL( QueryServ, PlayerLogZone, false) // Logs Player Zone Events
RULE_BOOL( QueryServ, PlayerLogDeaths, false) // Logs Player Deaths RULE_BOOL( QueryServ, PlayerLogDeaths, false) // Logs Player Deaths
RULE_BOOL( QueryServ, PlayerLogConnectDisconnect, false) // Logs Player Connect Disconnect State RULE_BOOL( QueryServ, PlayerLogConnectDisconnect, false) // Logs Player Connect Disconnect State
RULE_BOOL( QueryServ, PlayerLogLevels, false) // Logs Player Leveling/Deleveling RULE_BOOL( QueryServ, PlayerLogLevels, false) // Logs Player Leveling/Deleveling
RULE_BOOL( QueryServ, PlayerLogAARate, false) // Logs Player AA Experience Rates RULE_BOOL( QueryServ, PlayerLogAARate, false) // Logs Player AA Experience Rates
RULE_BOOL( QueryServ, PlayerLogQGlobalUpdate, false) // Logs Player QGlobal Updates RULE_BOOL( QueryServ, PlayerLogQGlobalUpdate, false) // Logs Player QGlobal Updates
RULE_BOOL( QueryServ, PlayerLogTaskUpdates, false) // Logs Player Task Updates RULE_BOOL( QueryServ, PlayerLogTaskUpdates, false) // Logs Player Task Updates
RULE_BOOL( QueryServ, PlayerLogKeyringAddition, false) // Log PLayer Keyring additions RULE_BOOL( QueryServ, PlayerLogKeyringAddition, false) // Log PLayer Keyring additions

View File

@ -55,3 +55,40 @@ bool EQEmu::IsSpecializedSkill(SkillUseTypes skill)
return false; return false;
} }
} }
float EQEmu::GetSkillMeleePushForce(SkillUseTypes skill)
{
// This is the force/magnitude of the push from an attack of this skill type
// You can find these numbers in the clients skill struct
switch (skill) {
case Skill1HBlunt:
case Skill1HSlashing:
case SkillHandtoHand:
case SkillThrowing:
return 0.1f;
case Skill2HBlunt:
case Skill2HSlashing:
case SkillEagleStrike:
case SkillKick:
case SkillTigerClaw:
//case Skill2HPiercing:
return 0.2f;
case SkillArchery:
return 0.15f;
case SkillBackstab:
case SkillBash:
return 0.3f;
case SkillDragonPunch:
case SkillRoundKick:
return 0.25f;
case SkillFlyingKick:
return 0.4f;
case Skill1HPiercing:
case SkillFrenzy:
return 0.05f;
case SkillIntimidation:
return 2.5f;
default:
return 0.0f;
}
}

View File

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

View File

@ -0,0 +1,2 @@
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Combat:MeleePush', 'true', 'Turns on Melee Push.');
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Combat:MeleePushChance', '50', 'Chance that an NPC can be pushed from melee.');

View File

@ -3708,6 +3708,23 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons
a->type = SkillDamageTypes[skill_used]; // was 0x1c a->type = SkillDamageTypes[skill_used]; // was 0x1c
a->damage = damage; a->damage = damage;
a->spellid = spell_id; a->spellid = spell_id;
a->meleepush_xy = attacker->GetHeading() * 2.0f;
if (RuleB(Combat, MeleePush) && damage > 0 && !IsRooted() &&
(IsClient() || zone->random.Roll(RuleI(Combat, MeleePushChance)))) {
a->force = EQEmu::GetSkillMeleePushForce(skill_used);
// update NPC stuff
auto new_pos = glm::vec3(m_Position.x + (a->force * std::sin(a->meleepush_xy) + m_Delta.x),
m_Position.y + (a->force * std::cos(a->meleepush_xy) + m_Delta.y), m_Position.z);
if (zone->zonemap->CheckLoS(glm::vec3(m_Position), new_pos)) { // If we have LoS on the new loc it should be reachable.
if (IsNPC()) {
// Is this adequate?
Teleport(new_pos);
SendPosUpdate();
}
} else {
a->force = 0.0f; // we couldn't move there, so lets not
}
}
//Note: if players can become pets, they will not receive damage messages of their own //Note: if players can become pets, they will not receive damage messages of their own
//this was done to simplify the code here (since we can only effectively skip one mob on queue) //this was done to simplify the code here (since we can only effectively skip one mob on queue)

View File

@ -189,8 +189,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
if (!IsPowerDistModSpell(spell_id)) if (!IsPowerDistModSpell(spell_id))
SetSpellPowerDistanceMod(0); SetSpellPowerDistanceMod(0);
bool SE_SpellTrigger_HasCast = false; bool SE_SpellTrigger_HasCast = false;
// iterate through the effects in the spell // iterate through the effects in the spell
for (i = 0; i < EFFECT_COUNT; i++) for (i = 0; i < EFFECT_COUNT; i++)
@ -424,11 +424,11 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
} }
case SE_Succor: case SE_Succor:
{ {
float x, y, z, heading; float x, y, z, heading;
const char *target_zone; const char *target_zone;
x = static_cast<float>(spell.base[1]); x = static_cast<float>(spell.base[1]);
y = static_cast<float>(spell.base[0]); y = static_cast<float>(spell.base[0]);
z = static_cast<float>(spell.base[2]); z = static_cast<float>(spell.base[2]);
@ -872,7 +872,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
if(buffs[buffslot].ticsremaining > RuleI(Character, MaxFearDurationForPlayerCharacter)) if(buffs[buffslot].ticsremaining > RuleI(Character, MaxFearDurationForPlayerCharacter))
buffs[buffslot].ticsremaining = RuleI(Character, MaxFearDurationForPlayerCharacter); buffs[buffslot].ticsremaining = RuleI(Character, MaxFearDurationForPlayerCharacter);
} }
if(RuleB(Combat, EnableFearPathing)){ if(RuleB(Combat, EnableFearPathing)){
if(IsClient()) if(IsClient())
@ -921,7 +921,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
cd->source = action->source; cd->source = action->source;
cd->type = action->type; cd->type = action->type;
cd->spellid = action->spell; cd->spellid = action->spell;
cd->sequence = action->sequence; cd->meleepush_xy = action->sequence;
CastToClient()->QueuePacket(action_packet); CastToClient()->QueuePacket(action_packet);
if(caster->IsClient() && caster != this) if(caster->IsClient() && caster != this)
@ -970,7 +970,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
cd->source = action->source; cd->source = action->source;
cd->type = action->type; cd->type = action->type;
cd->spellid = action->spell; cd->spellid = action->spell;
cd->sequence = action->sequence; cd->meleepush_xy = action->sequence;
CastToClient()->QueuePacket(action_packet); CastToClient()->QueuePacket(action_packet);
if(caster->IsClient() && caster != this) if(caster->IsClient() && caster != this)
@ -1006,7 +1006,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
cd->source = action->source; cd->source = action->source;
cd->type = action->type; cd->type = action->type;
cd->spellid = action->spell; cd->spellid = action->spell;
cd->sequence = action->sequence; cd->meleepush_xy = action->sequence;
CastToClient()->QueuePacket(action_packet); CastToClient()->QueuePacket(action_packet);
if(caster->IsClient() && caster != this) if(caster->IsClient() && caster != this)
@ -1291,7 +1291,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
#ifdef SPELL_EFFECT_SPAM #ifdef SPELL_EFFECT_SPAM
snprintf(effect_desc, _EDLEN, "Spell Absorb Rune: %+i", effect_value); snprintf(effect_desc, _EDLEN, "Spell Absorb Rune: %+i", effect_value);
#endif #endif
if(effect_value > 0) if(effect_value > 0)
buffs[buffslot].magic_rune = effect_value; buffs[buffslot].magic_rune = effect_value;
break; break;
@ -1329,12 +1329,12 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
case SE_DistanceRemoval: case SE_DistanceRemoval:
{ {
buffs[buffslot].caston_x = int(GetX()); buffs[buffslot].caston_x = int(GetX());
buffs[buffslot].caston_y = int(GetY()); buffs[buffslot].caston_y = int(GetY());
buffs[buffslot].caston_z = int(GetZ()); buffs[buffslot].caston_z = int(GetZ());
break; break;
} }
case SE_Levitate: case SE_Levitate:
{ {
#ifdef SPELL_EFFECT_SPAM #ifdef SPELL_EFFECT_SPAM
@ -1349,13 +1349,13 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
case SE_DeathSave: { case SE_DeathSave: {
int16 mod = 0; int16 mod = 0;
if(caster) { if(caster) {
mod = caster->aabonuses.UnfailingDivinity + mod = caster->aabonuses.UnfailingDivinity +
caster->itembonuses.UnfailingDivinity + caster->itembonuses.UnfailingDivinity +
caster->spellbonuses.UnfailingDivinity; caster->spellbonuses.UnfailingDivinity;
} }
buffs[buffslot].ExtraDIChance = mod; buffs[buffslot].ExtraDIChance = mod;
break; break;
} }
@ -1440,7 +1440,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
for(int x = EmuConstants::MATERIAL_BEGIN; x <= EmuConstants::MATERIAL_TINT_END; x++) for(int x = EmuConstants::MATERIAL_BEGIN; x <= EmuConstants::MATERIAL_TINT_END; x++)
SendWearChange(x); SendWearChange(x);
if(caster && (caster->spellbonuses.IllusionPersistence || caster->aabonuses.IllusionPersistence if(caster && (caster->spellbonuses.IllusionPersistence || caster->aabonuses.IllusionPersistence
|| caster->itembonuses.IllusionPersistence)) || caster->itembonuses.IllusionPersistence))
buffs[buffslot].persistant_buff = 1; buffs[buffslot].persistant_buff = 1;
@ -1555,8 +1555,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
{ {
uint16 pet_spellid = CastToNPC()->GetPetSpellID(); uint16 pet_spellid = CastToNPC()->GetPetSpellID();
uint16 pet_ActSpellCost = caster->GetActSpellCost(pet_spellid, spells[pet_spellid].mana); uint16 pet_ActSpellCost = caster->GetActSpellCost(pet_spellid, spells[pet_spellid].mana);
int16 ImprovedReclaimMod = caster->spellbonuses.ImprovedReclaimEnergy + int16 ImprovedReclaimMod = caster->spellbonuses.ImprovedReclaimEnergy +
caster->itembonuses.ImprovedReclaimEnergy + caster->itembonuses.ImprovedReclaimEnergy +
caster->aabonuses.ImprovedReclaimEnergy; caster->aabonuses.ImprovedReclaimEnergy;
if (!ImprovedReclaimMod) if (!ImprovedReclaimMod)
@ -1666,9 +1666,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
// Only allow 2 size changes from Base Size // Only allow 2 size changes from Base Size
float modifyAmount = (static_cast<float>(effect_value) / 100.0f); float modifyAmount = (static_cast<float>(effect_value) / 100.0f);
float maxModAmount = GetBaseSize() * modifyAmount * modifyAmount; float maxModAmount = GetBaseSize() * modifyAmount * modifyAmount;
if ((GetSize() <= GetBaseSize() && GetSize() > maxModAmount) || if ((GetSize() <= GetBaseSize() && GetSize() > maxModAmount) ||
(GetSize() >= GetBaseSize() && GetSize() < maxModAmount) || (GetSize() >= GetBaseSize() && GetSize() < maxModAmount) ||
(GetSize() <= GetBaseSize() && maxModAmount > 1.0f) || (GetSize() <= GetBaseSize() && maxModAmount > 1.0f) ||
(GetSize() >= GetBaseSize() && maxModAmount < 1.0f)) (GetSize() >= GetBaseSize() && maxModAmount < 1.0f))
{ {
ChangeSize(GetSize() * modifyAmount); ChangeSize(GetSize() * modifyAmount);
@ -1684,7 +1684,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
rooted = true; rooted = true;
if (caster){ if (caster){
buffs[buffslot].RootBreakChance = caster->aabonuses.RootBreakChance + buffs[buffslot].RootBreakChance = caster->aabonuses.RootBreakChance +
caster->itembonuses.RootBreakChance + caster->itembonuses.RootBreakChance +
caster->spellbonuses.RootBreakChance; caster->spellbonuses.RootBreakChance;
} }
@ -2249,7 +2249,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
float range = 0.0f; float range = 0.0f;
if (spells[spell_id].base2[i]) if (spells[spell_id].base2[i])
range = (float)spells[spell_id].base[i]; range = (float)spells[spell_id].base[i];
entity_list.AETaunt(caster->CastToClient(), range); entity_list.AETaunt(caster->CastToClient(), range);
} }
break; break;
@ -2540,7 +2540,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
int mana_damage = 0; int mana_damage = 0;
int32 mana_to_use = GetMana() - spell.base[i]; int32 mana_to_use = GetMana() - spell.base[i];
if(mana_to_use > -1) { if(mana_to_use > -1) {
SetMana(GetMana() - spell.base[i]); SetMana(GetMana() - spell.base[i]);
TryTriggerOnValueAmount(false, true); TryTriggerOnValueAmount(false, true);
// we take full dmg(-10 to make the damage the right sign) // we take full dmg(-10 to make the damage the right sign)
mana_damage = spell.base[i] / -10 * spell.base2[i]; mana_damage = spell.base[i] / -10 * spell.base2[i];
@ -2661,7 +2661,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
{ {
if (IsNPC()){ if (IsNPC()){
caster->Taunt(this->CastToNPC(), false, static_cast<float>(spell.base[i])); caster->Taunt(this->CastToNPC(), false, static_cast<float>(spell.base[i]));
if (spell.base2[i] > 0) if (spell.base2[i] > 0)
CastToNPC()->SetHateAmountOnEnt(caster, (CastToNPC()->GetHateAmount(caster) + spell.base2[i])); CastToNPC()->SetHateAmountOnEnt(caster, (CastToNPC()->GetHateAmount(caster) + spell.base2[i]));
} }
@ -2972,7 +2972,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
case SE_TriggerOnReqTarget: case SE_TriggerOnReqTarget:
case SE_LimitRace: case SE_LimitRace:
case SE_FcLimitUse: case SE_FcLimitUse:
case SE_FcMute: case SE_FcMute:
case SE_LimitUseType: case SE_LimitUseType:
case SE_FcStunTimeMod: case SE_FcStunTimeMod:
case SE_StunBashChance: case SE_StunBashChance:
@ -2981,9 +2981,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
case SE_LimitCastTimeMax: case SE_LimitCastTimeMax:
case SE_TriggerOnReqCaster: case SE_TriggerOnReqCaster:
case SE_FrenziedDevastation: case SE_FrenziedDevastation:
case SE_AStacker: case SE_AStacker:
case SE_BStacker: case SE_BStacker:
case SE_CStacker: case SE_CStacker:
case SE_DStacker: case SE_DStacker:
case SE_DoubleRiposte: case SE_DoubleRiposte:
case SE_Berserk: case SE_Berserk:
@ -3473,7 +3473,7 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste
effect_value = CalcSpellEffectValue(spell_id, i, caster_level, caster, 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(spell_id)){
if (caster->IsClient()){ if (caster->IsClient()){
if (!caster->CastToClient()->GetFeigned()) if (!caster->CastToClient()->GetFeigned())
@ -3715,7 +3715,7 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste
{ {
if (spellbonuses.DistanceRemoval){ if (spellbonuses.DistanceRemoval){
int distance = ((int(GetX()) - buffs[slot].caston_x) * (int(GetX()) - buffs[slot].caston_x)) + 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(GetY()) - buffs[slot].caston_y) * (int(GetY()) - buffs[slot].caston_y)) +
((int(GetZ()) - buffs[slot].caston_z) * (int(GetZ()) - buffs[slot].caston_z)); ((int(GetZ()) - buffs[slot].caston_z) * (int(GetZ()) - buffs[slot].caston_z));
@ -3729,12 +3729,12 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste
} }
case SE_AddHateOverTimePct: case SE_AddHateOverTimePct:
{ {
if (IsNPC()){ if (IsNPC()){
uint32 new_hate = CastToNPC()->GetHateAmount(caster) * (100 + spell.base[i]) / 100; uint32 new_hate = CastToNPC()->GetHateAmount(caster) * (100 + spell.base[i]) / 100;
if (new_hate <= 0) if (new_hate <= 0)
new_hate = 1; new_hate = 1;
CastToNPC()->SetHateAmountOnEnt(caster, new_hate); CastToNPC()->SetHateAmountOnEnt(caster, new_hate);
} }
break; break;
@ -4136,9 +4136,9 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses)
uint32 buff_max = GetMaxTotalSlots(); uint32 buff_max = GetMaxTotalSlots();
bool found_numhits = false; bool found_numhits = false;
for(uint32 d = 0; d < buff_max; d++) { for(uint32 d = 0; d < buff_max; d++) {
if(IsValidSpell(buffs[d].spellid) && (buffs[d].numhits > 0)) { if(IsValidSpell(buffs[d].spellid) && (buffs[d].numhits > 0)) {
Numhits(true); Numhits(true);
found_numhits = true; found_numhits = true;
@ -4148,7 +4148,7 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses)
if (!found_numhits) if (!found_numhits)
Numhits(false); Numhits(false);
} }
if (spells[buffs[slot].spellid].NimbusEffect > 0) if (spells[buffs[slot].spellid].NimbusEffect > 0)
RemoveNimbusEffect(spells[buffs[slot].spellid].NimbusEffect); RemoveNimbusEffect(spells[buffs[slot].spellid].NimbusEffect);
@ -4204,15 +4204,15 @@ int32 Client::GetAAEffectDataBySlot(uint32 aa_ID, uint32 slot_id, bool GetEffect
base1 = iter->second.base1; base1 = iter->second.base1;
base2 = iter->second.base2; base2 = iter->second.base2;
slot = iter->second.slot; slot = iter->second.slot;
if (slot && slot == slot_id) { if (slot && slot == slot_id) {
if (GetEffect) if (GetEffect)
return effect; return effect;
if (GetBase1) if (GetBase1)
return base1; return base1;
if (GetBase2) if (GetBase2)
return base2; return base2;
} }
@ -4236,7 +4236,7 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id)
uint32 slot = 0; uint32 slot = 0;
bool LimitFailure = false; bool LimitFailure = false;
bool LimitInclude[MaxLimitInclude] = { false }; bool LimitInclude[MaxLimitInclude] = { false };
/* Certain limits require only one of several Include conditions to be true. Ie. Add damage to fire OR ice spells. /* Certain limits require only one of several Include conditions to be true. Ie. Add damage to fire OR ice spells.
0/1 SE_LimitResist 0/1 SE_LimitResist
2/3 SE_LimitSpell 2/3 SE_LimitSpell
@ -4247,7 +4247,7 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id)
12/13 SE_LimitSpellClass: 12/13 SE_LimitSpellClass:
14/15 SE_LimitSpellSubClass: 14/15 SE_LimitSpellSubClass:
Remember: Update MaxLimitInclude in spdat.h if adding new limits that require Includes Remember: Update MaxLimitInclude in spdat.h if adding new limits that require Includes
*/ */
int FocusCount = 0; int FocusCount = 0;
std::map<uint32, std::map<uint32, AA_Ability> >::const_iterator find_iter = aa_effects.find(aa_ID); std::map<uint32, std::map<uint32, AA_Ability> >::const_iterator find_iter = aa_effects.find(aa_ID);
@ -4262,7 +4262,7 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id)
base1 = iter->second.base1; base1 = iter->second.base1;
base2 = iter->second.base2; base2 = iter->second.base2;
slot = iter->second.slot; slot = iter->second.slot;
/* /*
AA Foci's can contain multiple focus effects within the same AA. AA Foci's can contain multiple focus effects within the same AA.
To handle this we will not automatically return zero if a limit is found. To handle this we will not automatically return zero if a limit is found.
@ -4283,7 +4283,7 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id)
if (LimitFailure){ if (LimitFailure){
value = 0; value = 0;
LimitFailure = false; LimitFailure = false;
for(int e = 0; e < MaxLimitInclude; e++) { for(int e = 0; e < MaxLimitInclude; e++) {
LimitInclude[e] = false; //Reset array LimitInclude[e] = false; //Reset array
} }
@ -4322,7 +4322,7 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id)
LimitFailure = true; LimitFailure = true;
break; break;
case SE_LimitMaxLevel: case SE_LimitMaxLevel:
spell_level = spell.classes[(GetClass()%16) - 1]; spell_level = spell.classes[(GetClass()%16) - 1];
lvldiff = spell_level - base1; lvldiff = spell_level - base1;
@ -4333,7 +4333,7 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id)
if(lvlModifier < 1) if(lvlModifier < 1)
LimitFailure = true; LimitFailure = true;
} }
else else
LimitFailure = true; LimitFailure = true;
} }
break; break;
@ -4357,7 +4357,7 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id)
if(base1 < 0) { //Exclude if(base1 < 0) { //Exclude
if (spell_id == -base1) if (spell_id == -base1)
LimitFailure = true; LimitFailure = true;
} }
else { else {
LimitInclude[2] = true; LimitInclude[2] = true;
if (spell_id == base1) //Include if (spell_id == base1) //Include
@ -4450,7 +4450,7 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id)
if(base1 < 0) { //Exclude if(base1 < 0) { //Exclude
if (CheckSpellCategory(spell_id, base1, SE_LimitSpellClass)) if (CheckSpellCategory(spell_id, base1, SE_LimitSpellClass))
return(0); return(0);
} }
else { else {
LimitInclude[12] = true; LimitInclude[12] = true;
if (CheckSpellCategory(spell_id, base1, SE_LimitSpellClass)) //Include if (CheckSpellCategory(spell_id, base1, SE_LimitSpellClass)) //Include
@ -4462,7 +4462,7 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id)
if(base1 < 0) { //Exclude if(base1 < 0) { //Exclude
if (CheckSpellCategory(spell_id, base1, SE_LimitSpellSubclass)) if (CheckSpellCategory(spell_id, base1, SE_LimitSpellSubclass))
return(0); return(0);
} }
else { else {
LimitInclude[14] = true; LimitInclude[14] = true;
if (CheckSpellCategory(spell_id, base1, SE_LimitSpellSubclass)) //Include if (CheckSpellCategory(spell_id, base1, SE_LimitSpellSubclass)) //Include
@ -4599,7 +4599,7 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id)
//Note if using these as AA, make sure this is first focus used. //Note if using these as AA, make sure this is first focus used.
case SE_SympatheticProc: case SE_SympatheticProc:
if(type == focusSympatheticProc) if(type == focusSympatheticProc)
value = base2; value = base2;
break; break;
@ -4657,12 +4657,12 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id)
if(type == focusIncreaseNumHits) if(type == focusIncreaseNumHits)
value = base1; value = base1;
break; break;
case SE_FcLimitUse: case SE_FcLimitUse:
if(type == focusFcLimitUse) if(type == focusFcLimitUse)
value = base1; value = base1;
break; break;
case SE_FcMute: case SE_FcMute:
if(type == focusFcMute) if(type == focusFcMute)
value = base1; value = base1;
@ -4683,7 +4683,7 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id)
if (LimitFailure) if (LimitFailure)
return 0; return 0;
return(value*lvlModifier/100); return(value*lvlModifier/100);
} }
@ -4694,7 +4694,7 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo
if(!IsValidSpell(focus_id) || !IsValidSpell(spell_id)) if(!IsValidSpell(focus_id) || !IsValidSpell(spell_id))
return 0; return 0;
const SPDat_Spell_Struct &focus_spell = spells[focus_id]; const SPDat_Spell_Struct &focus_spell = spells[focus_id];
const SPDat_Spell_Struct &spell = spells[spell_id]; const SPDat_Spell_Struct &spell = spells[spell_id];
@ -4704,7 +4704,7 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo
int lvldiff = 0; int lvldiff = 0;
uint32 Caston_spell_id = 0; uint32 Caston_spell_id = 0;
bool LimitInclude[MaxLimitInclude] = { false }; bool LimitInclude[MaxLimitInclude] = { false };
/* Certain limits require only one of several Include conditions to be true. Ie. Add damage to fire OR ice spells. /* Certain limits require only one of several Include conditions to be true. Ie. Add damage to fire OR ice spells.
0/1 SE_LimitResist 0/1 SE_LimitResist
2/3 SE_LimitSpell 2/3 SE_LimitSpell
@ -4715,15 +4715,15 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo
12/13 SE_LimitSpellClass: 12/13 SE_LimitSpellClass:
14/15 SE_LimitSpellSubClass: 14/15 SE_LimitSpellSubClass:
Remember: Update MaxLimitInclude in spdat.h if adding new limits that require Includes Remember: Update MaxLimitInclude in spdat.h if adding new limits that require Includes
*/ */
for (int i = 0; i < EFFECT_COUNT; i++) { for (int i = 0; i < EFFECT_COUNT; i++) {
switch (focus_spell.effectid[i]) { switch (focus_spell.effectid[i]) {
case SE_Blank: case SE_Blank:
break; break;
case SE_LimitResist: case SE_LimitResist:
if(focus_spell.base[i] < 0){ if(focus_spell.base[i] < 0){
if (spell.resisttype == -focus_spell.base[i]) //Exclude if (spell.resisttype == -focus_spell.base[i]) //Exclude
@ -4735,7 +4735,7 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo
LimitInclude[1] = true; LimitInclude[1] = true;
} }
break; break;
case SE_LimitInstant: case SE_LimitInstant:
if(focus_spell.base[i] == 1 && spell.buffduration) //Fail if not instant if(focus_spell.base[i] == 1 && spell.buffduration) //Fail if not instant
return 0; return 0;
@ -4760,7 +4760,7 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo
return 0; return 0;
} }
break; break;
case SE_LimitMinLevel: case SE_LimitMinLevel:
if (IsNPC()) if (IsNPC())
break; break;
@ -4777,12 +4777,12 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo
if (spells[spell_id].cast_time > (uint16)focus_spell.base[i]) if (spells[spell_id].cast_time > (uint16)focus_spell.base[i])
return(0); return(0);
break; break;
case SE_LimitSpell: case SE_LimitSpell:
if(focus_spell.base[i] < 0) { //Exclude if(focus_spell.base[i] < 0) { //Exclude
if (spell_id == -focus_spell.base[i]) if (spell_id == -focus_spell.base[i])
return(0); return(0);
} }
else { else {
LimitInclude[2] = true; LimitInclude[2] = true;
if (spell_id == focus_spell.base[i]) //Include if (spell_id == focus_spell.base[i]) //Include
@ -4871,7 +4871,7 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo
LimitInclude[11] = true; LimitInclude[11] = true;
} }
break; break;
case SE_LimitClass: case SE_LimitClass:
//Do not use this limit more then once per spell. If multiple class, treat value like items would. //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())) if (!PassLimitClass(focus_spell.base[i], GetClass()))
@ -4989,7 +4989,7 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo
if (type == focusSpellHaste && focus_spell.base[i] > value) if (type == focusSpellHaste && focus_spell.base[i] > value)
value = focus_spell.base[i]; value = focus_spell.base[i];
break; break;
case SE_IncreaseSpellDuration: case SE_IncreaseSpellDuration:
if (type == focusSpellDuration && focus_spell.base[i] > value) if (type == focusSpellDuration && focus_spell.base[i] > value)
value = focus_spell.base[i]; value = focus_spell.base[i];
@ -5161,14 +5161,14 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo
Log.Out(Logs::General, Logs::Normal, "CalcFocusEffect: unknown effectid %d", focus_spell.effectid[i]); Log.Out(Logs::General, Logs::Normal, "CalcFocusEffect: unknown effectid %d", focus_spell.effectid[i]);
#endif #endif
} }
} }
for(int e = 0; e < MaxLimitInclude; e+=2) { for(int e = 0; e < MaxLimitInclude; e+=2) {
if (LimitInclude[e] && !LimitInclude[e+1]) if (LimitInclude[e] && !LimitInclude[e+1])
return 0; return 0;
} }
if (Caston_spell_id){ if (Caston_spell_id){
if(IsValidSpell(Caston_spell_id) && (Caston_spell_id != spell_id)) if(IsValidSpell(Caston_spell_id) && (Caston_spell_id != spell_id))
SpellFinished(Caston_spell_id, this, 10, 0, -1, spells[Caston_spell_id].ResistDiff); SpellFinished(Caston_spell_id, this, 10, 0, -1, spells[Caston_spell_id].ResistDiff);
@ -5260,7 +5260,7 @@ uint16 Client::GetSympatheticFocusEffect(focusType type, uint16 spell_id) {
} }
} }
/*Note: At present, ff designing custom AA to have a sympathetic proc effect, only use one focus /*Note: At present, ff designing custom AA to have a sympathetic proc effect, only use one focus
effect within the aa_effects data for each AA*[No live AA's use this effect to my knowledge]*/ effect within the aa_effects data for each AA*[No live AA's use this effect to my knowledge]*/
if (aabonuses.FocusEffects[type]){ if (aabonuses.FocusEffects[type]){
@ -5557,10 +5557,10 @@ int16 NPC::GetFocusEffect(focusType type, uint16 spell_id) {
//item focus //item focus
for(int i = 0; i < EmuConstants::EQUIPMENT_SIZE; i++){ for(int i = 0; i < EmuConstants::EQUIPMENT_SIZE; i++){
const Item_Struct *cur = database.GetItem(equipment[i]); const Item_Struct *cur = database.GetItem(equipment[i]);
if(!cur) if(!cur)
continue; continue;
TempItem = cur; TempItem = cur;
if (TempItem && TempItem->Focus.Effect > 0 && TempItem->Focus.Effect != SPELL_UNKNOWN) { if (TempItem && TempItem->Focus.Effect > 0 && TempItem->Focus.Effect != SPELL_UNKNOWN) {
@ -5590,7 +5590,7 @@ int16 NPC::GetFocusEffect(focusType type, uint16 spell_id) {
} }
} }
} }
if(UsedItem && rand_effectiveness && focus_max_real != 0) if(UsedItem && rand_effectiveness && focus_max_real != 0)
realTotal = CalcFocusEffect(type, UsedFocusID, spell_id); realTotal = CalcFocusEffect(type, UsedFocusID, spell_id);
} }
@ -5909,21 +5909,21 @@ float Mob::GetSympatheticProcChances(uint16 spell_id, int16 ProcRateMod, int32 I
int32 total_cast_time = 0; int32 total_cast_time = 0;
float cast_time_mod = 0.0f; float cast_time_mod = 0.0f;
ProcRateMod -= 100; ProcRateMod -= 100;
if (spells[spell_id].recast_time >= spells[spell_id].recovery_time) if (spells[spell_id].recast_time >= spells[spell_id].recovery_time)
total_cast_time = spells[spell_id].recast_time + spells[spell_id].cast_time; total_cast_time = spells[spell_id].recast_time + spells[spell_id].cast_time;
else else
total_cast_time = spells[spell_id].recovery_time + spells[spell_id].cast_time; total_cast_time = spells[spell_id].recovery_time + spells[spell_id].cast_time;
if (total_cast_time > 0 && total_cast_time <= 2500)
cast_time_mod = 0.25f;
else if (total_cast_time > 2500 && total_cast_time < 7000)
cast_time_mod = 0.167f*((static_cast<float>(total_cast_time) - 1000.0f)/1000.0f);
else
cast_time_mod = static_cast<float>(total_cast_time) / 7000.0f;
ProcChance = (RuleR(Casting, AvgSpellProcsPerMinute)/100.0f) * (static_cast<float>(100.0f + ProcRateMod) / 10.0f) if (total_cast_time > 0 && total_cast_time <= 2500)
cast_time_mod = 0.25f;
else if (total_cast_time > 2500 && total_cast_time < 7000)
cast_time_mod = 0.167f*((static_cast<float>(total_cast_time) - 1000.0f)/1000.0f);
else
cast_time_mod = static_cast<float>(total_cast_time) / 7000.0f;
ProcChance = (RuleR(Casting, AvgSpellProcsPerMinute)/100.0f) * (static_cast<float>(100.0f + ProcRateMod) / 10.0f)
* cast_time_mod * (static_cast<float>(100.0f + ItemProcRate)/100.0f); * cast_time_mod * (static_cast<float>(100.0f + ItemProcRate)/100.0f);
return ProcChance; return ProcChance;
@ -6063,7 +6063,7 @@ int32 Mob::GetFocusIncoming(focusType type, int effect, Mob *caster, uint32 spel
CheckNumHitsRemaining(NumHit::MatchingSpells, tmp_buffslot); CheckNumHitsRemaining(NumHit::MatchingSpells, tmp_buffslot);
} }
return value; return value;
} }
@ -6147,8 +6147,8 @@ uint16 Mob::GetSpellEffectResistChance(uint16 spell_id)
bool Mob::TryDispel(uint8 caster_level, uint8 buff_level, int level_modifier){ bool Mob::TryDispel(uint8 caster_level, uint8 buff_level, int level_modifier){
/*Live 5-20-14 Patch Note: Updated all spells which use Remove Detrimental and /*Live 5-20-14 Patch Note: Updated all spells which use Remove Detrimental and
Cancel Beneficial spell effects to use a new method. The chances for those spells to Cancel Beneficial spell effects to use a new method. The chances for those spells to
affect their targets have not changed unless otherwise noted.*/ affect their targets have not changed unless otherwise noted.*/
/*This should provide a somewhat accurate conversion between pre 5/14 base values and post. /*This should provide a somewhat accurate conversion between pre 5/14 base values and post.
@ -6160,7 +6160,7 @@ bool Mob::TryDispel(uint8 caster_level, uint8 buff_level, int level_modifier){
//Effect value of dispels are treated as a level modifier. //Effect value of dispels are treated as a level modifier.
//Values for scaling were obtain from live parses, best estimates. //Values for scaling were obtain from live parses, best estimates.
caster_level += level_modifier - 1; caster_level += level_modifier - 1;
int dispel_chance = 32; //Baseline chance if no level difference and no modifier int dispel_chance = 32; //Baseline chance if no level difference and no modifier
int level_diff = caster_level - buff_level; int level_diff = caster_level - buff_level;
@ -6201,7 +6201,7 @@ bool Mob::ImprovedTaunt(){
else { else {
if(!TryFadeEffect(spellbonuses.ImprovedTaunt[2])) if(!TryFadeEffect(spellbonuses.ImprovedTaunt[2]))
BuffFadeBySlot(spellbonuses.ImprovedTaunt[2], true); //If caster killed removed effect. BuffFadeBySlot(spellbonuses.ImprovedTaunt[2], true); //If caster killed removed effect.
} }
} }
} }
@ -6267,7 +6267,7 @@ bool Mob::PassCastRestriction(bool UseCastRestriction, int16 value, bool IsDama
Range 845 - 847 : UNKNOWN Range 845 - 847 : UNKNOWN
Range 10000 - 11000 : Limit to Race [base2 - 10000 = Race] (*Not on live: Too useful a function to not implement) 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 THIS IS A WORK IN PROGRESS
*/ */
if (value <= 0) if (value <= 0)
return true; return true;
@ -6276,174 +6276,174 @@ bool Mob::PassCastRestriction(bool UseCastRestriction, int16 value, bool IsDama
switch(value) switch(value)
{ {
case 100: case 100:
if ((GetBodyType() == BT_Animal) || (GetBodyType() == BT_Humanoid)) if ((GetBodyType() == BT_Animal) || (GetBodyType() == BT_Humanoid))
return true; return true;
break; break;
case 101: case 101:
if (GetBodyType() == BT_Dragon || GetBodyType() == BT_VeliousDragon || GetBodyType() == BT_Dragon3) if (GetBodyType() == BT_Dragon || GetBodyType() == BT_VeliousDragon || GetBodyType() == BT_Dragon3)
return true; return true;
break; break;
case 102: case 102:
if ((GetBodyType() == BT_Animal) || (GetBodyType() == BT_Insect)) if ((GetBodyType() == BT_Animal) || (GetBodyType() == BT_Insect))
return true; return true;
break; break;
case 104: case 104:
if (GetBodyType() == BT_Animal) if (GetBodyType() == BT_Animal)
return true; return true;
break; break;
case 105: case 105:
if (GetBodyType() == BT_Plant) if (GetBodyType() == BT_Plant)
return true; return true;
break; break;
case 106: case 106:
if (GetBodyType() == BT_Giant) if (GetBodyType() == BT_Giant)
return true; return true;
break; break;
case 108: case 108:
if ((GetBodyType() != BT_Animal) || (GetBodyType() != BT_Humanoid)) if ((GetBodyType() != BT_Animal) || (GetBodyType() != BT_Humanoid))
return true; return true;
break; break;
case 109: case 109:
if ((GetRace() == 520) ||(GetRace() == 79)) if ((GetRace() == 520) ||(GetRace() == 79))
return true; return true;
break; break;
case 111: case 111:
if ((GetRace() == 527) ||(GetRace() == 11)) if ((GetRace() == 527) ||(GetRace() == 11))
return true; return true;
break; break;
case 112: case 112:
if ((GetRace() == 456) ||(GetRace() == 28)) if ((GetRace() == 456) ||(GetRace() == 28))
return true; return true;
break; break;
case 113: case 113:
if ((GetRace() == 456) ||(GetRace() == 48)) if ((GetRace() == 456) ||(GetRace() == 48))
return true; return true;
break; break;
case 114: case 114:
if (GetRace() == 526) if (GetRace() == 526)
return true; return true;
break; break;
case 115: case 115:
if (GetRace() == 522) if (GetRace() == 522)
return true; return true;
break; break;
case 117: case 117:
if ((GetBodyType() == BT_Animal) || (GetBodyType() == BT_Plant)) if ((GetBodyType() == BT_Animal) || (GetBodyType() == BT_Plant))
return true; return true;
break; break;
case 118: case 118:
if (GetBodyType() == BT_Summoned) if (GetBodyType() == BT_Summoned)
return true; return true;
break; break;
case 119: case 119:
if (IsPet() && ((GetRace() == 212) || ((GetRace() == 75) && GetTexture() == 1))) if (IsPet() && ((GetRace() == 212) || ((GetRace() == 75) && GetTexture() == 1)))
return true; return true;
break; break;
case 120: case 120:
if (GetBodyType() == BT_Undead) if (GetBodyType() == BT_Undead)
return true; return true;
break; break;
case 121: case 121:
if (GetBodyType() != BT_Undead) if (GetBodyType() != BT_Undead)
return true; return true;
break; break;
case 122: case 122:
if ((GetRace() == 473) || (GetRace() == 425)) if ((GetRace() == 473) || (GetRace() == 425))
return true; return true;
break; break;
case 123: case 123:
if (GetBodyType() == BT_Humanoid) if (GetBodyType() == BT_Humanoid)
return true; return true;
break; break;
case 124: case 124:
if ((GetBodyType() == BT_Undead) && (GetHPRatio() < 10)) if ((GetBodyType() == BT_Undead) && (GetHPRatio() < 10))
return true; return true;
break; break;
case 125: case 125:
if ((GetRace() == 457 || GetRace() == 88) && (GetHPRatio() < 10)) if ((GetRace() == 457 || GetRace() == 88) && (GetHPRatio() < 10))
return true; return true;
break; break;
case 126: case 126:
if ((GetRace() == 581 || GetRace() == 69) && (GetHPRatio() < 10)) if ((GetRace() == 581 || GetRace() == 69) && (GetHPRatio() < 10))
return true; return true;
break; break;
case 201: case 201:
if (GetHPRatio() > 75) if (GetHPRatio() > 75)
return true; return true;
break; break;
case 204: case 204:
if (GetHPRatio() < 20) if (GetHPRatio() < 20)
return true; return true;
break; break;
case 216: case 216:
if (!IsEngaged()) if (!IsEngaged())
return true; return true;
break; break;
case 250: case 250:
if (GetHPRatio() < 35) if (GetHPRatio() < 35)
return true; return true;
break; break;
case 304: case 304:
if (IsClient() && if (IsClient() &&
((GetClass() == WARRIOR) || (GetClass() == BARD) || (GetClass() == SHADOWKNIGHT) || (GetClass() == PALADIN) || (GetClass() == CLERIC) ((GetClass() == WARRIOR) || (GetClass() == BARD) || (GetClass() == SHADOWKNIGHT) || (GetClass() == PALADIN) || (GetClass() == CLERIC)
|| (GetClass() == RANGER) || (GetClass() == SHAMAN) || (GetClass() == ROGUE) || (GetClass() == BERSERKER))) || (GetClass() == RANGER) || (GetClass() == SHAMAN) || (GetClass() == ROGUE) || (GetClass() == BERSERKER)))
return true; return true;
break; break;
case 701: case 701:
if (!IsPet()) if (!IsPet())
return true; return true;
break; break;
case 818: case 818:
if (GetBodyType() == BT_Undead) if (GetBodyType() == BT_Undead)
return true; return true;
break; break;
case 819: case 819:
if (GetBodyType() != BT_Undead) if (GetBodyType() != BT_Undead)
return true; return true;
break; break;
case 842: case 842:
if (GetBodyType() == BT_Humanoid && GetLevel() <= 84) if (GetBodyType() == BT_Humanoid && GetLevel() <= 84)
return true; return true;
break; break;
case 843: case 843:
if (GetBodyType() == BT_Humanoid && GetLevel() <= 86) if (GetBodyType() == BT_Humanoid && GetLevel() <= 86)
return true; return true;
break; break;
case 844: case 844:
if (GetBodyType() == BT_Humanoid && GetLevel() <= 88) if (GetBodyType() == BT_Humanoid && GetLevel() <= 88)
return true; return true;
break; break;
@ -6452,7 +6452,7 @@ bool Mob::PassCastRestriction(bool UseCastRestriction, int16 value, bool IsDama
//Limit to amount of pets //Limit to amount of pets
if (value >= 221 && value <= 249){ if (value >= 221 && value <= 249){
int count = hate_list.GetSummonedPetCountOnHateList(this); int count = hate_list.GetSummonedPetCountOnHateList(this);
for (int base2_value = 221; base2_value <= 249; ++base2_value){ for (int base2_value = 221; base2_value <= 249; ++base2_value){
if (value == base2_value){ if (value == base2_value){
if (count >= (base2_value - 220)){ if (count >= (base2_value - 220)){
@ -6476,12 +6476,12 @@ bool Mob::PassCastRestriction(bool UseCastRestriction, int16 value, bool IsDama
} //End Damage } //End Damage
if (!IsDamage || UseCastRestriction) { if (!IsDamage || UseCastRestriction) {
//Heal only if HP within specified range. [Doesn't follow a set forumla for all values...] //Heal only if HP within specified range. [Doesn't follow a set forumla for all values...]
if (value >= 400 && value <= 408){ if (value >= 400 && value <= 408){
for (int base2_value = 400; base2_value <= 408; ++base2_value){ for (int base2_value = 400; base2_value <= 408; ++base2_value){
if (value == base2_value){ if (value == base2_value){
if (value == 400 && GetHPRatio() <= 25) if (value == 400 && GetHPRatio() <= 25)
return true; return true;
@ -6492,11 +6492,11 @@ bool Mob::PassCastRestriction(bool UseCastRestriction, int16 value, bool IsDama
} }
} }
} }
else if (value >= 500 && value <= 549){ else if (value >= 500 && value <= 549){
for (int base2_value = 500; base2_value <= 520; ++base2_value){ for (int base2_value = 500; base2_value <= 520; ++base2_value){
if (value == base2_value){ if (value == base2_value){
if (GetHPRatio() < (base2_value - 500)*5) if (GetHPRatio() < (base2_value - 500)*5)
return true; return true;
} }
} }
@ -6507,8 +6507,8 @@ bool Mob::PassCastRestriction(bool UseCastRestriction, int16 value, bool IsDama
return true; return true;
} }
} // End Heal } // End Heal
return false; return false;
} }
@ -6521,18 +6521,18 @@ bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id, float speed){
and you lose your mana. If there is LOS the bolt will lock onto your target and the damage is applied when it hits the target. and you lose your mana. If there is LOS the bolt will lock onto your target and the damage is applied when it hits the target.
-If your target moves the bolt moves with it in any direction or angle (consistent with other projectiles). -If your target moves the bolt moves with it in any direction or angle (consistent with other projectiles).
-The way this is written once a bolt is cast a the distance from the initial cast to the target repeatedly -The way this is written once a bolt is cast a the distance from the initial cast to the target repeatedly
check and if target is moving recalculates at what predicted time the bolt should hit that target in client_process check and if target is moving recalculates at what predicted time the bolt should hit that target in client_process
When bolt hits its predicted point the damage is then done to target. When bolt hits its predicted point the damage is then done to target.
Note: Projectile speed of 1 takes 3 seconds to go 100 distance units. Calculations are based on this constant. Note: Projectile speed of 1 takes 3 seconds to go 100 distance units. Calculations are based on this constant.
Live Bolt speed: Projectile speed of X takes 5 seconds to go 300 distance units. Live Bolt speed: Projectile speed of X takes 5 seconds to go 300 distance units.
Pending Implementation: What this code can not do is prevent damage if the bolt hits a barrier after passing the initial LOS check Pending Implementation: What this code can not do is prevent damage if the bolt hits a barrier after passing the initial LOS check
because the target has moved while the bolt is in motion. (it is rare to actual get this to occur on live in normal game play) because the target has moved while the bolt is in motion. (it is rare to actual get this to occur on live in normal game play)
*/ */
if (!spell_target) if (!spell_target)
return false; return false;
uint8 anim = spells[spell_id].CastingAnim; uint8 anim = spells[spell_id].CastingAnim;
int slot = -1; int slot = -1;
//Make sure there is an avialable bolt to be cast. //Make sure there is an avialable bolt to be cast.
@ -6545,9 +6545,9 @@ bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id, float speed){
if (slot < 0) if (slot < 0)
return false; return false;
if (CheckLosFN(spell_target)) { if (CheckLosFN(spell_target)) {
float speed_mod = speed * 0.45f; //Constant for adjusting speeds to match calculated impact time. float speed_mod = speed * 0.45f; //Constant for adjusting speeds to match calculated impact time.
float distance = spell_target->CalculateDistance(GetX(), GetY(), GetZ()); float distance = spell_target->CalculateDistance(GetX(), GetY(), GetZ());
float hit = 60.0f + (distance / speed_mod); float hit = 60.0f + (distance / speed_mod);
@ -6570,18 +6570,18 @@ bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id, float speed){
ProjectileAnimation(spell_target,0, false, speed,0,0,0, spells[spell_id].player_1); ProjectileAnimation(spell_target,0, false, speed,0,0,0, spells[spell_id].player_1);
} }
//This allows limited support for server using older spell files that do not contain data for bolt graphics. //This allows limited support for server using older spell files that do not contain data for bolt graphics.
else { else {
//Only use fire graphic for fire spells. //Only use fire graphic for fire spells.
if (spells[spell_id].resisttype == RESIST_FIRE) { if (spells[spell_id].resisttype == RESIST_FIRE) {
if (IsClient()){ if (IsClient()){
if (CastToClient()->GetClientVersionBit() <= 4) //Titanium needs alternate graphic. if (CastToClient()->GetClientVersionBit() <= 4) //Titanium needs alternate graphic.
ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_Titanium)), false, speed); ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_Titanium)), false, speed);
else else
ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_SOF)), false, speed); ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_SOF)), false, speed);
} }
else else
ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_NPC)), false, speed); ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_NPC)), false, speed);
} }
@ -6627,7 +6627,7 @@ void Mob::ResourceTap(int32 damage, uint16 spellid)
} }
void Mob::TryTriggerThreshHold(int32 damage, int effect_id, Mob* attacker){ void Mob::TryTriggerThreshHold(int32 damage, int effect_id, Mob* attacker){
if (damage <= 0) if (damage <= 0)
return; return;
@ -6649,15 +6649,15 @@ void Mob::TryTriggerThreshHold(int32 damage, int effect_id, Mob* attacker){
uint16 spell_id = spells[buffs[slot].spellid].base[i]; uint16 spell_id = spells[buffs[slot].spellid].base[i];
if (damage > spells[buffs[slot].spellid].base2[i]){ if (damage > spells[buffs[slot].spellid].base2[i]){
BuffFadeBySlot(slot); BuffFadeBySlot(slot);
if (IsValidSpell(spell_id)) { if (IsValidSpell(spell_id)) {
if (IsBeneficialSpell(spell_id)) if (IsBeneficialSpell(spell_id))
SpellFinished(spell_id, this, 10, 0, -1, spells[spell_id].ResistDiff); SpellFinished(spell_id, this, 10, 0, -1, spells[spell_id].ResistDiff);
else if(attacker) else if(attacker)
SpellFinished(spell_id, attacker, 10, 0, -1, spells[spell_id].ResistDiff); SpellFinished(spell_id, attacker, 10, 0, -1, spells[spell_id].ResistDiff);
} }
} }
@ -6734,7 +6734,7 @@ void Mob::CalcSpellPowerDistanceMod(uint16 spell_id, float range, Mob* caster)
float dist_from_min = distance - spells[spell_id].min_dist; float dist_from_min = distance - spells[spell_id].min_dist;
float mod = spells[spell_id].min_dist_mod + (dist_from_min * (dm_mod_interval/dm_range)); float mod = spells[spell_id].min_dist_mod + (dist_from_min * (dm_mod_interval/dm_range));
mod *= 100.0f; mod *= 100.0f;
SetSpellPowerDistanceMod(static_cast<int>(mod)); SetSpellPowerDistanceMod(static_cast<int>(mod));
} }
} }

View File

@ -2563,7 +2563,7 @@ void Mob::BardPulse(uint16 spell_id, Mob *caster) {
cd->source = action->source; cd->source = action->source;
cd->type = DamageTypeSpell; cd->type = DamageTypeSpell;
cd->spellid = action->spell; cd->spellid = action->spell;
cd->sequence = action->sequence; cd->meleepush_xy = action->sequence;
cd->damage = 0; cd->damage = 0;
if(!IsEffectInSpell(spell_id, SE_BindAffinity)) if(!IsEffectInSpell(spell_id, SE_BindAffinity))
{ {
@ -3827,7 +3827,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r
cd->source = action->source; cd->source = action->source;
cd->type = action->type; cd->type = action->type;
cd->spellid = action->spell; cd->spellid = action->spell;
cd->sequence = action->sequence; cd->meleepush_xy = action->sequence;
cd->damage = 0; cd->damage = 0;
if(!IsEffectInSpell(spell_id, SE_BindAffinity)) if(!IsEffectInSpell(spell_id, SE_BindAffinity))
{ {

View File

@ -192,7 +192,7 @@ void Trap::Trigger(Mob* trigger)
int dmg = zone->random.Int(effectvalue, effectvalue2); int dmg = zone->random.Int(effectvalue, effectvalue2);
trigger->SetHP(trigger->GetHP() - dmg); trigger->SetHP(trigger->GetHP() - dmg);
a->damage = dmg; a->damage = dmg;
a->sequence = zone->random.Int(0, 1234567); a->meleepush_xy = zone->random.Int(0, 1234567);
a->source = GetHiddenTrigger()!=nullptr ? GetHiddenTrigger()->GetID() : trigger->GetID(); a->source = GetHiddenTrigger()!=nullptr ? GetHiddenTrigger()->GetID() : trigger->GetID();
a->spellid = 0; a->spellid = 0;
a->target = trigger->GetID(); a->target = trigger->GetID();