Merge pull request #164 from KayenEQ/Development

Updated SE_Hate (Renamed from SE_Hate2) to now properly work for instant...
This commit is contained in:
Michael Cook 2014-07-01 23:50:31 -04:00
commit bd86e70766
17 changed files with 649 additions and 266 deletions

View File

@ -1,5 +1,30 @@
EQEMu Changelog (Started on Sept 24, 2003 15:50)
-------------------------------------------------------
== 06/25/2014 ==
Kayen: Updated SE_Hate (Renamed from SE_Hate2) to now properly work for instant +/- hate spells.
Kayen: Updated SE_FadingMemories - Base value will be properly utilized to set % chance for fade effect to work.
Kayen: Implemented SE_StrikeThough (Was incorrectly defined as implemented previously) - Works same as item bonus.
Kayen: Update SE_Taunt - Limit value if present will now add instant hate.
Kayen: Implemented SE_MassGroupBuff - Allows next group buff cast to be a MGB (AA now uses this)
Kayen: Implemented SE_IllusionOther - Allows next Illusion buff (self only) cast to be cast on target. (AA now uses this)
Kayen: Update SE_AETaunt - Base value will now determine AE taunt range (This will not result in any change to currently used spells).
Kayen: Udpated SE_ReclaimPet - Correct forumla for mana returned to properly return 75% of actual pet spell mana cost.
Kayen: Implemented SE_ImprovedReclaimEnergy - Modifies % mana returned from SE_ReclaimPet.
Kayen: Implemented SE_HeadShot, SE_HeadShotLevel - Defines headshot damage and level requirements.
Revised HeadShot mechanic so damage now receives all archery bonuses, proc chance can be set to either (lives new Proc Per minute
system, or flat chance based on dex (formula updated).
Kayen: Implemented SE_Assassinate, SE_AssassinateLevel - Defines assassinate damage and level requirements.
Revised Assassinate mechanic so damage now receives all backstab bonuses, proc chance can be set to either (lives new Proc Per minute
system, or flat chance based on dex (formula updated). Assassinate can now proc from THROW if behind target, various other adjustments.
Kayen: Fix to AA Finishing Blow missing aa_effects data, update required SQL.
Revised Finishing blow so that damage now receives all melee bonus. Support also for this effect if placed on items or spells.
Kayen: Implemented SE_PetMeleeMitigation - Bonus applied to pet owner. Gives AC to owner's pet.
Required SQL: utils/sql/git/required/2014_06_25_AA_Update.sql
Optional SQL: utils/sql/git/optiional/2014_06_29_HeadShotRules.sql
== 06/17/2014 ==
Kayen: Implemented SE_AStacker, SE_BStacker, SE_CStacker, SE_DStacker.
These effects when present in buffs prevent each other from stacking,

View File

@ -333,6 +333,8 @@ RULE_REAL ( Combat, AvgProcsPerMinute, 2.0)
RULE_REAL ( Combat, ProcPerMinDexContrib, 0.075)
RULE_REAL ( Combat, BaseProcChance, 0.035)
RULE_REAL ( Combat, ProcDexDivideBy, 11000)
RULE_BOOL ( Combat, AdjustSpecialProcPerMinute, true) //Set PPM for special abilities like HeadShot (Live does this as of 4-14)
RULE_REAL ( Combat, AvgSpecialProcsPerMinute, 2.0) //Unclear what best value is atm.
RULE_REAL ( Combat, BaseHitChance, 69.0)
RULE_REAL ( Combat, NPCBonusHitChance, 26.0)
RULE_REAL ( Combat, HitFalloffMinor, 5.0) //hit will fall off up to 5% over the initial level range

View File

@ -237,7 +237,7 @@ typedef enum {
#define SE_MagnifyVision 87 // implemented - Telescope
#define SE_Succor 88 // implemented - Evacuate/Succor lines
#define SE_ModelSize 89 // implemented - Shrink, Growth
#define SE_Cloak 90 // *not implemented - Used in only 2 spells
//#define SE_Cloak 90 // *not implemented - Used in only 2 spells
#define SE_SummonCorpse 91 // implemented
#define SE_InstantHate 92 // implemented - add hate
#define SE_StopRain 93 // implemented - Wake of Karana
@ -339,32 +339,32 @@ typedef enum {
#define SE_CurrentEndurance 189 // implemented
#define SE_EndurancePool 190 // implemented
#define SE_Amnesia 191 // implemented - Silence vs Melee Effect
#define SE_Hate2 192 // implemented
#define SE_Hate 192 // implemented - Instant and hate over time.
#define SE_SkillAttack 193 // implemented
#define SE_FadingMemories 194 // implemented
#define SE_StunResist 195 // implemented
#define SE_Strikethrough 196 // implemented
#define SE_StrikeThrough 196 // implemented
#define SE_SkillDamageTaken 197 // implemented
#define SE_CurrentEnduranceOnce 198 // implemented
#define SE_Taunt 199 // implemented - % chance to taunt the target
#define SE_ProcChance 200 // implemented
#define SE_RangedProc 201 // implemented
//#define SE_IllusionOther 202 // *not implemented as bonus(Project Illusion)
//#define SE_MassGroupBuff 203 // *not implemented as bonus
#define SE_GroupFearImmunity 204 // *not implemented as bonus
#define SE_IllusionOther 202 // implemented - Project Illusion
#define SE_MassGroupBuff 203 // implemented
#define SE_GroupFearImmunity 204 // implemented - (Does not use bonus)
#define SE_Rampage 205 // implemented
#define SE_AETaunt 206 // implemented
#define SE_FleshToBone 207 // implemented
//#define SE_PurgePoison 208 // not used
#define SE_DispelBeneficial 209 // implemented
//#define SE_PetShield 210 // *not implemented
#define SE_AEMelee 211 // implemented
#define SE_AEMelee 211 // implemented TO DO: Implement to allow NPC use (client only atm).
#define SE_FrenziedDevastation 212 // implemented - increase spell criticals + all DD spells cast 2x mana.
#define SE_PetMaxHP 213 // implemented[AA] - increases the maximum hit points of your pet
#define SE_MaxHPChange 214 // implemented
#define SE_PetAvoidance 215 // implemented[AA] - increases pet ability to avoid melee damage
#define SE_Accuracy 216 // implemented
//#define SE_HeadShot 217 // not implemented as bonus - ability to head shot (base2 = damage)
#define SE_HeadShot 217 // implemented - ability to head shot (base2 = damage)
#define SE_PetCriticalHit 218 // implemented[AA] - gives pets a baseline critical hit chance
#define SE_SlayUndead 219 // implemented - Allow extra damage against undead (base1 = rate, base2 = damage mod).
#define SE_SkillDamageAmount 220 // implemented
@ -375,7 +375,7 @@ typedef enum {
#define SE_GiveDoubleAttack 225 // implemented[AA] - Allow any class to double attack with set chance.
#define SE_TwoHandBash 226 // *not implemented as bonus
#define SE_ReduceSkillTimer 227 // implemented
#define SE_ReduceFallDamage 228 // not implented as bonus - reduce the damage that you take from falling
//#define SE_ReduceFallDamage 228 // not implented as bonus - reduce the damage that you take from falling
#define SE_PersistantCasting 229 // implemented
//#define SE_ExtendedShielding 230 // not used as bonus - increase range of /shield ability
#define SE_StunBashChance 231 // implemented - increase chance to stun from bash.
@ -388,7 +388,7 @@ typedef enum {
//#define SE_IllusionPersistence 238 // *not implemented - lends persistence to your illusionary disguises, causing them to last until you die or the illusion is forcibly removed.
//#define SE_FeignedCastOnChance 239 // *not implemented as bonus - ability gives you an increasing chance for your feigned deaths to not be revealed by spells cast upon you.
//#define SE_StringUnbreakable 240 // not used [Likely related to above - you become immune to feign breaking on a resisted spell and have a good chance of feigning through a spell that successfully lands upon you.]
#define SE_ImprovedReclaimEnergy 241 // not implemented as bonus - increase the amount of mana returned to you when reclaiming your pet.
#define SE_ImprovedReclaimEnergy 241 // implemented - increase the amount of mana returned to you when reclaiming your pet.
#define SE_IncreaseChanceMemwipe 242 // implemented - increases the chance to wipe hate with memory blurr
#define SE_CharmBreakChance 243 // implemented - Total Domination
#define SE_RootBreakChance 244 // implemented[AA] reduce the chance that your root will break.
@ -410,7 +410,7 @@ typedef enum {
#define SE_AddSingingMod 260 // implemented[AA] - Instrument/Singing Mastery, base1 is the mod, base2 is the ItemType
#define SE_SongModCap 261 // implemented[AA] - Song Mod cap increase (no longer used on live)
#define SE_RaiseStatCap 262 // implemented
#define SE_TradeSkillMastery 263 // implemented - lets you raise more than one tradeskill above master.
//#define SE_TradeSkillMastery 263 // not implemented - lets you raise more than one tradeskill above master.
//#define SE_HastenedAASkill 264 // not implemented as bonus - Use redux field in aa_actions table for this effect
#define SE_MasteryofPast 265 // implemented[AA] - Spells less than effect values level can not be fizzled
#define SE_ExtraAttackChance 266 // implemented - increase chance to score an extra attack with a 2-Handed Weapon.
@ -492,8 +492,8 @@ typedef enum {
#define SE_ImmuneFleeing 342 // implemented - stop mob from fleeing
#define SE_InterruptCasting 343 // implemented - % chance to interrupt spells being cast every tic. Cacophony (8272)
#define SE_ChannelChanceItems 344 // implemented[AA] - chance to not have ITEM effects interrupted when you take damage.
//#define SE_AssassinationLevel 345 // not implemented as bonus - AA Assisination max level to kill
//#define SE_HeadShotLevel 346 // not implemented as bonus - AA HeadShot max level to kill
#define SE_AssassinateLevel 345 // implemented as bonus - AA Assisination max level to kill
#define SE_HeadShotLevel 346 // implemented[AA] - HeadShot max level to kill
#define SE_DoubleRangedAttack 347 // implemented - chance at an additional archery attack (consumes arrow)
#define SE_LimitManaMin 348 // implemented
#define SE_ShieldEquipHateMod 349 // implemented[AA] Increase melee hate when wearing a shield.
@ -506,13 +506,13 @@ typedef enum {
//#define SE_ChangeTriggerType 356 // not used
#define SE_FcMute 357 // implemented - silences casting of spells that contain specific spell effects (focus limited)
#define SE_CurrentManaOnce 358 // implemented
#define SE_Invulnerabilty 359 // *not implemented - Invulnerability (Brell's Blessing)
#define SE_SpellOnKill 360 // implemented - a buff that has a base1 % to cast spell base2 when you kill a "challenging foe" base3 min level
//#define SE_PassiveSenseTrap 359 // *not implemented - Invulnerability (Brell's Blessing)
#define SE_ProcOnKillShot 360 // implemented - a buff that has a base1 % to cast spell base2 when you kill a "challenging foe" base3 min level
#define SE_SpellOnDeath 361 // implemented - casts spell on death of buffed
//#define SE_PotionBeltSlots 362 // *not implemented[AA] 'Quick Draw' expands the potion belt by one additional available item slot per rank.
//#define SE_BandolierSlots 363 // *not implemented[AA] 'Battle Ready' expands the bandolier by one additional save slot per rank.
#define SE_TripleAttackChance 364 // implemented
#define SE_SpellOnKill2 365 // implemented - chance to trigger a spell on kill when the kill is caused by a specific spell with this effect in it (10470 Venin)
#define SE_ProcOnSpellKillShot 365 // implemented - chance to trigger a spell on kill when the kill is caused by a specific spell with this effect in it (10470 Venin)
#define SE_ShieldEquipDmgMod 366 // implemented[AA] Damage modifier to melee if shield equiped. (base1 = dmg mod , base2 = ?) ie Shield Specialist AA
#define SE_SetBodyType 367 // implemented - set body type of base1 so it can be affected by spells that are limited to that type (Plant, Animal, Undead, etc)
//#define SE_FactionMod 368 // *not implemented - increases faction with base1 (faction id, live won't match up w/ ours) by base2
@ -544,7 +544,7 @@ typedef enum {
#define SE_FcHealAmtIncoming 394 // implemented - Adds/Removes amount of healing on target by X value with foucs restrictions.
#define SE_FcHealPctCritIncoming 395 // implemented[AA] - Increases chance of having a heal crit when cast on you. [focus limited]
#define SE_FcHealAmtCrit 396 // implemented - Adds a direct healing amount to spells
//#define SE_PetMeleeMitigation 397 // *not implemented[AA] - additional mitigation to your pets.
#define SE_PetMeleeMitigation 397 // implemented[AA] - additional mitigation to your pets. Adds AC.
#define SE_SwarmPetDuration 398 // implemented - Affects the duration of swarm pets
#define SE_FcTwincast 399 // implemented - cast 2 spells for every 1
#define SE_HealGroupFromMana 400 // implemented - Drains mana and heals for each point of mana drained
@ -586,7 +586,7 @@ typedef enum {
//#define SE_BeneficialCountDownHold 436 // not used ( 23491 | ABTest Buff Hold)
//#define SE_TeleporttoAnchor 437 // *not implemented - Teleport Guild Hall Anchor(33099)
//#define SE_TranslocatetoAnchor 438 // *not implemented - Translocate Primary Anchor (27750)
//#define SE_IncreaseAssassinationLvl 439 // *not implemented[AA] - increases the maximum level of humanoid that can be affected by assassination
#define SE_Assassinate 439 // implemented[AA] - Assassinate damage
#define SE_FinishingBlowLvl 440 // implemented[AA] - Sets the level Finishing blow can be triggered on an NPC
#define SE_DistanceRemoval 441 // implemented - Buff is removed from target when target moves X amount of distance away from where initially hit.
#define SE_TriggerOnReqTarget 442 // implemented - triggers a spell which a certain criteria are met (below X amount of hp,mana,end, number of pets on hatelist)

View File

@ -0,0 +1,2 @@
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Combat:AdjustSpecialProcPerMinute', 'false', 'Allow PPM for special abilities HeadShot, Assassinate, Decap ect.');
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Combat:AvgSpecialProcsPerMinute', '2.0', 'Set PPM for special abilities HeadShot, Assassinate, Decap ect. (Unknown what value live uses) .');

View File

@ -0,0 +1,34 @@
-- AA MGB update
UPDATE altadv_vars SET spellid = 5228 WHERE skill_id = 128;
UPDATE aa_actions SET spell_id = 5228, nonspell_action = 0 WHERE aaid = 128;
-- AA Project Illusion update
UPDATE altadv_vars SET spellid = 5227 WHERE skill_id = 643;
UPDATE aa_actions SET spell_id = 5227, nonspell_action = 0 WHERE aaid = 643;
-- AA Improved Reclaim Energy
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('180', '1', '241', '95', '0');
-- AA Headshot
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('644', '1', '217', '0', '32000');
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('644', '2', '346', '46', '0');
-- AA Anatomy (Rogue Assassinate)
INSERT INTO `altadv_vars` (`skill_id`, `name`, `cost`, `max_level`, `hotkey_sid`, `hotkey_sid2`, `title_sid`, `desc_sid`, `type`, `spellid`, `prereq_skill`, `prereq_minpoints`, `spell_type`, `spell_refresh`, `classes`, `berserker`, `class_type`, `cost_inc`, `aa_expansion`, `special_category`, `sof_type`, `sof_cost_inc`, `sof_max_level`, `sof_next_skill`, `clientver`, `account_time_required`, `sof_current_level`,`sof_next_id`,`level_inc`) VALUES ('1604', 'Anatomy', '5', '3', '4294967295', '4294967295', '1604', '1604', '1', '4294967295', '0', '0', '0', '0', '512', '0', '60', '1', '10', '4294967295', '3', '0', '3', '1604', '1', '0', '0', '0', '0');
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1604', '1', '439', '0', '32000');
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1604', '2', '345', '48', '0');
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1605', '1', '439', '0', '32000');
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1605', '2', '345', '51', '0');
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1606', '1', '439', '0', '32000');
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1606', '2', '345', '53', '0');
-- AA Finishing Blow Fix
DELETE FROM aa_effects WHERE aaid = 199 AND slot = 2;
DELETE FROM aa_effects WHERE aaid = 200 AND slot = 2;
DELETE FROM aa_effects WHERE aaid = 201 AND slot = 2;
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('119', '1', '278', '500', '32000');
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('119', '2', '440', '50', '200');
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('120', '1', '278', '500', '32000');
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('120', '2', '440', '52', '200');
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('121', '1', '278', '500', '32000');
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('121', '2', '440', '54', '200');

View File

@ -352,11 +352,6 @@ void Client::HandleAAAction(aaID activate) {
entity_list.AETaunt(this);
break;
case aaActionMassBuff:
EnableAAEffect(aaEffectMassGroupBuff, 3600);
Message_StringID(MT_Disciplines, MGB_STRING); //The next group buff you cast will hit all targets in range.
break;
case aaActionFlamingArrows:
//toggle it
if(CheckAAEffect(aaEffectFlamingArrows))
@ -459,12 +454,6 @@ void Client::HandleAAAction(aaID activate) {
}
break;
case aaActionProjectIllusion:
EnableAAEffect(aaEffectProjectIllusion, 3600);
Message(10, "The power of your next illusion spell will flow to your grouped target in your place.");
break;
case aaActionEscape:
Escape();
break;

View File

@ -43,14 +43,14 @@ typedef enum {
//use these for AAs which dont cast spells, yet need effects
//if this list grows beyond 32, more work is needed in *AAEffect
typedef enum { //AA Effect IDs
aaEffectMassGroupBuff = 1,
aaEffectMassGroupBuff = 1, //unused - Handled via spell effect.
aaEffectRampage,
aaEffectSharedHealth,
aaEffectFlamingArrows,
aaEffectFrostArrows,
aaEffectWarcry,
aaEffectLeechTouch,
aaEffectProjectIllusion // seveian 2008-09-23
aaEffectProjectIllusion // unused - Handled via spell effect
} aaEffectType;

View File

@ -560,11 +560,21 @@ void Mob::MeleeMitigation(Mob *attacker, int32 &damage, int32 minhit, ExtraAttac
weight = (CastToClient()->CalcCurrentWeight() / 10.0);
} else if (IsNPC()) {
armor = CastToNPC()->GetRawAC();
int PetACBonus = 0;
if (!IsPet())
armor = (armor / RuleR(Combat, NPCACFactor));
else{
Mob *owner = nullptr;
owner = GetOwner();
if (owner){
PetACBonus = owner->aabonuses.PetMeleeMitigation
+ owner->itembonuses.PetMeleeMitigation +
owner->spellbonuses.PetMeleeMitigation;
}
}
armor += spellbonuses.AC + itembonuses.AC + 1;
armor += spellbonuses.AC + itembonuses.AC + PetACBonus + 1;
}
if (opts) {
@ -4443,27 +4453,25 @@ void Mob::TryCriticalHit(Mob *defender, uint16 skill, int32 &damage, ExtraAttack
bool Mob::TryFinishingBlow(Mob *defender, SkillUseTypes skillinuse)
{
if (defender && !defender->IsClient() && defender->GetHPRatio() < 10){
if (!defender)
return false;
uint32 FB_Dmg = aabonuses.FinishingBlow[1] + spellbonuses.FinishingBlow[1] + itembonuses.FinishingBlow[1];
uint16 FB_Level = 0;
FB_Level = aabonuses.FinishingBlowLvl[0];
if (FB_Level < spellbonuses.FinishingBlowLvl[0])
FB_Level = spellbonuses.FinishingBlowLvl[0];
else if (FB_Level < itembonuses.FinishingBlowLvl[0])
FB_Level = itembonuses.FinishingBlowLvl[0];
if (aabonuses.FinishingBlow[1] && !defender->IsClient() && defender->GetHPRatio() < 10){
//Proc Chance value of 500 = 5%
uint32 ProcChance = (aabonuses.FinishingBlow[0] + spellbonuses.FinishingBlow[0] + spellbonuses.FinishingBlow[0])/10;
uint32 chance = aabonuses.FinishingBlow[0]/10; //500 = 5% chance.
uint32 damage = aabonuses.FinishingBlow[1];
uint16 levelreq = aabonuses.FinishingBlowLvl[0];
if(defender->GetLevel() <= levelreq && (chance >= MakeRandomInt(0, 1000))){
mlog(COMBAT__ATTACKS, "Landed a finishing blow: levelreq at %d, other level %d", levelreq , defender->GetLevel());
if(FB_Level && FB_Dmg && (defender->GetLevel() <= FB_Level) && (ProcChance >= MakeRandomInt(0, 1000))){
entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, FINISHING_BLOW, GetName());
defender->Damage(this, damage, SPELL_UNKNOWN, skillinuse);
DoSpecialAttackDamage(defender, skillinuse, FB_Dmg, 1, -1, 10, false, false);
return true;
}
else
{
mlog(COMBAT__ATTACKS, "FAILED a finishing blow: levelreq at %d, other level %d", levelreq , defender->GetLevel());
return false;
}
}
return false;
}

View File

@ -637,7 +637,7 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon)
continue;
_log(AA__BONUSES, "Applying Effect %d from AA %u in slot %d (base1: %d, base2: %d) on %s", effect, aaid, slot, base1, base2, this->GetCleanName());
uint8 focus = IsFocusEffect(0, 0, true,effect);
if (focus)
{
@ -953,6 +953,8 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon)
case SE_BlockBehind:
newbon->BlockBehind += base1;
break;
case SE_StrikeThrough:
case SE_StrikeThrough2:
newbon->StrikeThrough += base1;
break;
@ -987,7 +989,7 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon)
newbon->CrippBlowChance += base1;
break;
case SE_SpellOnKill:
case SE_ProcOnKillShot:
for(int i = 0; i < MAX_SPELL_TRIGGER*3; i+=3)
{
if(!newbon->SpellOnKill[i] || ((newbon->SpellOnKill[i] == base2) && (newbon->SpellOnKill[i+1] < base1)))
@ -1202,7 +1204,6 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon)
case SE_FinishingBlow:
{
//base1 = chance, base2 = damage
if (newbon->FinishingBlow[1] < base2){
newbon->FinishingBlow[0] = base1;
@ -1268,6 +1269,52 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon)
newbon->Metabolism += base1;
break;
case SE_ImprovedReclaimEnergy:
{
if((base1 < 0) && (newbon->ImprovedReclaimEnergy > base1))
newbon->ImprovedReclaimEnergy = base1;
else if(newbon->ImprovedReclaimEnergy < base1)
newbon->ImprovedReclaimEnergy = base1;
break;
}
case SE_HeadShot:
{
if(newbon->HeadShot[1] < base2){
newbon->HeadShot[0] = base1;
newbon->HeadShot[1] = base2;
}
break;
}
case SE_HeadShotLevel:
{
if(newbon->HSLevel < base1)
newbon->HSLevel = base1;
break;
}
case SE_Assassinate:
{
if(newbon->Assassinate[1] < base2){
newbon->Assassinate[0] = base1;
newbon->Assassinate[1] = base2;
}
break;
}
case SE_AssassinateLevel:
{
if(newbon->AssassinateLevel < base1)
newbon->AssassinateLevel = base1;
break;
}
case SE_PetMeleeMitigation:
newbon->PetMeleeMitigation += base1;
break;
}
}
}
@ -2132,7 +2179,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
newbon->CriticalDoTChance += effect_value;
break;
case SE_SpellOnKill:
case SE_ProcOnKillShot:
{
for(int e = 0; e < MAX_SPELL_TRIGGER*3; e+=3)
{
@ -2469,6 +2516,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
newbon->SecondaryDmgInc = true;
break;
case SE_StrikeThrough:
case SE_StrikeThrough2:
newbon->StrikeThrough += effect_value;
break;
@ -2714,19 +2762,23 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
break;
case SE_AStacker:
newbon->AStacker = true;
newbon->AStacker[0] = 1;
newbon->AStacker[1] = effect_value;
break;
case SE_BStacker:
newbon->BStacker = true;
newbon->BStacker[0] = 1;
newbon->BStacker[1] = effect_value;
break;
case SE_CStacker:
newbon->CStacker = true;
newbon->CStacker[0] = 1;
newbon->CStacker[1] = effect_value;
break;
case SE_DStacker:
newbon->DStacker = true;
newbon->DStacker[0] = 1;
newbon->DStacker[1] = effect_value;
break;
case SE_Berserk:
@ -2738,6 +2790,72 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
newbon->Metabolism += effect_value;
break;
case SE_ImprovedReclaimEnergy:
{
if((effect_value < 0) && (newbon->ImprovedReclaimEnergy > effect_value))
newbon->ImprovedReclaimEnergy = effect_value;
else if(newbon->ImprovedReclaimEnergy < effect_value)
newbon->ImprovedReclaimEnergy = effect_value;
break;
}
case SE_HeadShot:
{
if(newbon->HeadShot[1] < base2){
newbon->HeadShot[0] = effect_value;
newbon->HeadShot[1] = base2;
}
break;
}
case SE_HeadShotLevel:
{
if(newbon->HSLevel < effect_value)
newbon->HSLevel = effect_value;
break;
}
case SE_Assassinate:
{
if(newbon->Assassinate[1] < base2){
newbon->Assassinate[0] = effect_value;
newbon->Assassinate[1] = base2;
}
break;
}
case SE_AssassinateLevel:
{
if(newbon->AssassinateLevel < effect_value)
newbon->AssassinateLevel = effect_value;
break;
}
case SE_FinishingBlow:
{
//base1 = chance, base2 = damage
if (newbon->FinishingBlow[1] < base2){
newbon->FinishingBlow[0] = effect_value;
newbon->FinishingBlow[1] = base2;
}
break;
}
case SE_FinishingBlowLvl:
{
//base1 = level, base2 = ??? (Set to 200 in AA data, possible proc rate mod?)
if (newbon->FinishingBlowLvl[0] < effect_value){
newbon->FinishingBlowLvl[0] = effect_value;
newbon->FinishingBlowLvl[1] = base2;
}
break;
}
case SE_PetMeleeMitigation:
newbon->PetMeleeMitigation += effect_value;
break;
//Special custom cases for loading effects on to NPC from 'npc_spels_effects' table
if (IsAISpellEffect) {
@ -3671,7 +3789,7 @@ void Mob::NegateSpellsBonuses(uint16 spell_id)
itembonuses.CriticalDoTChance = effect_value;
break;
case SE_SpellOnKill:
case SE_ProcOnKillShot:
{
for(int e = 0; e < MAX_SPELL_TRIGGER*3; e=3)
{
@ -3909,6 +4027,12 @@ void Mob::NegateSpellsBonuses(uint16 spell_id)
itembonuses.SecondaryDmgInc = false;
break;
case SE_StrikeThrough:
spellbonuses.StrikeThrough = effect_value;
aabonuses.StrikeThrough = effect_value;
itembonuses.StrikeThrough = effect_value;
break;
case SE_StrikeThrough2:
spellbonuses.StrikeThrough = effect_value;
aabonuses.StrikeThrough = effect_value;
@ -3963,6 +4087,12 @@ void Mob::NegateSpellsBonuses(uint16 spell_id)
itembonuses.GivePetGroupTarget = false;
break;
case SE_PetMeleeMitigation:
spellbonuses.PetMeleeMitigation = effect_value;
itembonuses.PetMeleeMitigation = effect_value;
aabonuses.PetMeleeMitigation = effect_value;
break;
case SE_RootBreakChance:
spellbonuses.RootBreakChance = effect_value;
aabonuses.RootBreakChance = effect_value;
@ -4145,7 +4275,61 @@ void Mob::NegateSpellsBonuses(uint16 spell_id)
aabonuses.Metabolism = effect_value;
itembonuses.Metabolism = effect_value;
break;
case SE_ImprovedReclaimEnergy:
spellbonuses.ImprovedReclaimEnergy = effect_value;
aabonuses.ImprovedReclaimEnergy = effect_value;
itembonuses.ImprovedReclaimEnergy = effect_value;
break;
case SE_HeadShot:
spellbonuses.HeadShot[0] = effect_value;
aabonuses.HeadShot[0] = effect_value;
itembonuses.HeadShot[0] = effect_value;
spellbonuses.HeadShot[1] = effect_value;
aabonuses.HeadShot[1] = effect_value;
itembonuses.HeadShot[1] = effect_value;
break;
case SE_HeadShotLevel:
spellbonuses.HSLevel = effect_value;
aabonuses.HSLevel = effect_value;
itembonuses.HSLevel = effect_value;
break;
case SE_Assassinate:
spellbonuses.Assassinate[0] = effect_value;
aabonuses.Assassinate[0] = effect_value;
itembonuses.Assassinate[0] = effect_value;
spellbonuses.Assassinate[1] = effect_value;
aabonuses.Assassinate[1] = effect_value;
itembonuses.Assassinate[1] = effect_value;
break;
case SE_AssassinateLevel:
spellbonuses.AssassinateLevel = effect_value;
aabonuses.AssassinateLevel = effect_value;
itembonuses.AssassinateLevel = effect_value;
break;
case SE_FinishingBlow:
spellbonuses.FinishingBlow[0] = effect_value;
aabonuses.FinishingBlow[0] = effect_value;
itembonuses.FinishingBlow[0] = effect_value;
spellbonuses.FinishingBlow[1] = effect_value;
aabonuses.FinishingBlow[1] = effect_value;
itembonuses.FinishingBlow[1] = effect_value;
break;
case SE_FinishingBlowLvl:
spellbonuses.FinishingBlowLvl[0] = effect_value;
aabonuses.FinishingBlowLvl[0] = effect_value;
itembonuses.FinishingBlowLvl[0] = effect_value;
spellbonuses.FinishingBlowLvl[1] = effect_value;
aabonuses.FinishingBlowLvl[1] = effect_value;
itembonuses.FinishingBlowLvl[1] = effect_value;
break;
}
}
}

View File

@ -1900,7 +1900,7 @@ void Bot::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon)
newbon->UnfailingDivinity += base1;
break;
case SE_SpellOnKill:
case SE_ProcOnKillShot:
for(int i = 0; i < MAX_SPELL_TRIGGER*3; i+=3)
{
if(!newbon->SpellOnKill[i] || ((newbon->SpellOnKill[i] == base2) && (newbon->SpellOnKill[i+1] < base1)))

View File

@ -1177,7 +1177,7 @@ uint32 Client::CalcCurrentWeight() {
Total += (m_pp.platinum + m_pp.gold + m_pp.silver + m_pp.copper) / 4;
}
float Packrat = (float)spellbonuses.Packrat + (float)aabonuses.Packrat;
float Packrat = (float)spellbonuses.Packrat + (float)aabonuses.Packrat + (float)itembonuses.Packrat;
if (Packrat > 0)
Total = (uint32)((float)Total * (1.0f - ((Packrat * 1.0f) / 100.0f))); //AndMetal: 1% per level, up to 5% (calculated from Titanium client). verified thru client that it reduces coin weight by the same %
//without casting to float & back to uint32, this didn't work right
@ -1864,7 +1864,7 @@ uint16 Mob::GetInstrumentMod(uint16 spell_id) const
break;
}
effectmodcap += aabonuses.songModCap + spellbonuses.songModCap;
effectmodcap += aabonuses.songModCap + spellbonuses.songModCap + itembonuses.songModCap;
if (effectmod < 10)
effectmod = 10;

View File

@ -366,10 +366,10 @@ struct StatBonuses {
bool NegateIfCombat; // Bool Drop buff if cast or melee
int8 Screech; // -1 = Will be blocked if another Screech is +(1)
int16 AlterNPCLevel; // amount of lvls +/-
bool AStacker; // For buff stack blocking
bool BStacker; // For buff stack blocking
bool CStacker; // For buff stack blocking
bool DStacker; // For buff stack blocking
int16 AStacker[1]; // For buff stack blocking 0=Exists 1=Effect_value
int16 BStacker[1]; // For buff stack blocking 0=Exists 1=Effect_value
int16 CStacker[1]; // For buff stack blocking 0=Exists 1=Effect_value
int16 DStacker[1]; // For buff stack blocking 0=Exists 1=Effect_value
bool BerserkSPA; // berserk effect
int16 Metabolism; // Food/drink consumption rates.
@ -422,6 +422,13 @@ struct StatBonuses {
int8 StunBashChance; // chance to stun with bash.
int8 IncreaseChanceMemwipe; // increases chance to memory wipe
int8 CriticalMend; // chance critical monk mend
int16 ImprovedReclaimEnergy; // Modifies amount of mana returned from reclaim energy
uint32 HeadShot[2]; // Headshot AA (Massive dmg vs humaniod w/ archery) 0= ? 1= Dmg
uint8 HSLevel; // Max Level Headshot will be effective at.
uint32 Assassinate[2]; // Assassinate AA (Massive dmg vs humaniod w/ assassinate) 0= ? 1= Dmg
uint8 AssassinateLevel; // Max Level Assassinate will be effective at.
int32 PetMeleeMitigation; // Add AC to owner's pet.
};
typedef struct

View File

@ -180,6 +180,8 @@ Mob::Mob(const char* in_name,
trackable = true;
has_shieldequiped = false;
has_numhits = false;
has_MGB = false;
has_ProjectIllusion = false;
if(in_aa_title>0)
aa_title = in_aa_title;
@ -4137,9 +4139,9 @@ void Mob::TrySpellOnKill(uint8 level, uint16 spell_id)
{
if (spell_id != SPELL_UNKNOWN)
{
if(IsEffectInSpell(spell_id, SE_SpellOnKill2)) {
if(IsEffectInSpell(spell_id, SE_ProcOnSpellKillShot)) {
for (int i = 0; i < EFFECT_COUNT; i++) {
if (spells[spell_id].effectid[i] == SE_SpellOnKill2)
if (spells[spell_id].effectid[i] == SE_ProcOnSpellKillShot)
{
if (IsValidSpell(spells[spell_id].base2[i]) && spells[spell_id].max[i] <= level)
{

View File

@ -141,7 +141,8 @@ public:
virtual void TryCriticalHit(Mob *defender, uint16 skill, int32 &damage, ExtraAttackOptions *opts = nullptr);
void TryPetCriticalHit(Mob *defender, uint16 skill, int32 &damage);
virtual bool TryFinishingBlow(Mob *defender, SkillUseTypes skillinuse);
virtual bool TryHeadShot(Mob* defender, SkillUseTypes skillInUse);
uint32 TryHeadShot(Mob* defender, SkillUseTypes skillInUse);
uint32 TryAssassinate(Mob* defender, SkillUseTypes skillInUse, uint16 ReuseTime);
virtual void DoRiposte(Mob* defender);
void ApplyMeleeDamageBonus(uint16 skill, int32 &damage);
virtual void MeleeMitigation(Mob *attacker, int32 &damage, int32 minhit, ExtraAttackOptions *opts = nullptr);
@ -268,6 +269,10 @@ public:
void CheckNumHitsRemaining(uint8 type, uint32 buff_slot=0, uint16 spell_id=SPELL_UNKNOWN);
bool HasNumhits() const { return has_numhits; }
inline void Numhits(bool val) { has_numhits = val; }
bool HasMGB() const { return has_MGB; }
inline void SetMGB(bool val) { has_MGB = val; }
bool HasProjectIllusion() const { return has_ProjectIllusion ; }
inline void SetProjectIllusion(bool val) { has_ProjectIllusion = val; }
void SpreadVirus(uint16 spell_id, uint16 casterID);
bool IsNimbusEffectActive(uint32 nimbus_effect);
void SetNimbusEffect(uint32 nimbus_effect);
@ -690,7 +695,7 @@ public:
int32 AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTic, Mob* attacker);
int32 ReduceAllDamage(int32 damage);
virtual void DoSpecialAttackDamage(Mob *who, SkillUseTypes skill, int32 max_damage, int32 min_damage = 1, int32 hate_override = -1, int ReuseTime = 10, bool HitChance=false);
virtual void DoSpecialAttackDamage(Mob *who, SkillUseTypes skill, int32 max_damage, int32 min_damage = 1, int32 hate_override = -1, int ReuseTime = 10, bool HitChance=false, bool CanAvoid=true);
virtual void DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon=nullptr, const Item_Struct* item=nullptr, uint16 weapon_damage=0, int16 chance_mod=0,int16 focus=0);
virtual void DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes skillinuse, int16 chance_mod=0, int16 focus=0, bool CanRiposte=false);
virtual void DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon=nullptr, const ItemInst* Ammo=nullptr, uint16 weapon_damage=0, int16 chance_mod=0, int16 focus=0);
@ -987,6 +992,8 @@ protected:
void ExecWeaponProc(const ItemInst* weapon, uint16 spell_id, Mob *on);
virtual float GetProcChances(float ProcBonus, uint16 weapon_speed = 30, uint16 hand = 13);
virtual float GetDefensiveProcChances(float &ProcBonus, float &ProcChance, uint16 weapon_speed = 30, uint16 hand = 13);
virtual float GetSpecialProcChances(uint16 hand);
virtual float GetAssassinateProcChances(uint16 ReuseTime);
int GetWeaponDamage(Mob *against, const Item_Struct *weapon_item);
int GetWeaponDamage(Mob *against, const ItemInst *weapon_item, uint32 *hate = nullptr);
int GetKickDamage();
@ -1097,6 +1104,8 @@ protected:
bool offhand;
bool has_shieldequiped;
bool has_numhits;
bool has_MGB;
bool has_ProjectIllusion;
// Bind wound
Timer bindwound_timer;

View File

@ -100,7 +100,8 @@ void Mob::ApplySpecialAttackMod(SkillUseTypes skill, int32 &dmg, int32 &mindmg)
}
}
void Mob::DoSpecialAttackDamage(Mob *who, SkillUseTypes skill, int32 max_damage, int32 min_damage, int32 hate_override,int ReuseTime, bool HitChance) {
void Mob::DoSpecialAttackDamage(Mob *who, SkillUseTypes skill, int32 max_damage, int32 min_damage, int32 hate_override,int ReuseTime,
bool HitChance, bool CanAvoid) {
//this really should go through the same code as normal melee damage to
//pick up all the special behavior there
@ -135,7 +136,9 @@ void Mob::DoSpecialAttackDamage(Mob *who, SkillUseTypes skill, int32 max_damage,
if(skill == SkillThrowing || skill == SkillArchery) // changed from '&&'
CanRiposte = false;
who->AvoidDamage(this, max_damage, CanRiposte);
if (CanAvoid)
who->AvoidDamage(this, max_damage, CanRiposte);
who->MeleeMitigation(this, max_damage, min_damage);
if(max_damage > 0) {
@ -373,8 +376,8 @@ void Client::OPCombatAbility(const EQApplicationPacket *app) {
if (ca_atk->m_atk != 100 || ca_atk->m_skill != SkillBackstab) {
break;
}
TryBackstab(GetTarget(), ReuseTime);
ReuseTime = BackstabReuseTime-1 - skill_reduction;
TryBackstab(GetTarget(), ReuseTime);
break;
}
default:
@ -527,64 +530,47 @@ void Mob::TryBackstab(Mob *other, int ReuseTime) {
if (FrontalBSChance && (FrontalBSChance > MakeRandomInt(0, 100)))
bCanFrontalBS = true;
}
if (bIsBehind || bCanFrontalBS){ // Player is behind other OR can do Frontal Backstab
if (bCanFrontalBS) {
if (bCanFrontalBS)
CastToClient()->Message(0,"Your fierce attack is executed with such grace, your target did not see it coming!");
}
RogueBackstab(other,false,ReuseTime);
if (level > 54) {
// solar - chance to assassinate
int chance = 10 + (GetDEX()/10) + (itembonuses.HeroicDEX/10); //18.5% chance at 85 dex 40% chance at 300 dex
if(
level >= 60 && // player is 60 or higher
other->GetLevel() <= 45 && // mob 45 or under
!other->CastToNPC()->IsEngaged() && // not aggro
other->GetHP()<=32000
&& other->IsNPC()
&& MakeRandomFloat(0, 99) < chance // chance
) {
entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, ASSASSINATES, GetName());
if(IsClient())
CastToClient()->CheckIncreaseSkill(SkillBackstab, other, 10);
RogueAssassinate(other);
}
else {
RogueBackstab(other);
if (level > 54) {
float DoubleAttackProbability = (GetSkill(SkillDoubleAttack) + GetLevel()) / 500.0f; // 62.4 max
// Check for double attack with main hand assuming maxed DA Skill (MS)
if(IsClient() && CastToClient()->CheckDoubleAttack(false))
{
if(other->GetHP() > 0)
RogueBackstab(other,false,ReuseTime);
if(MakeRandomFloat(0, 1) < DoubleAttackProbability) // Max 62.4 % chance of DA
{
if(other->GetHP() > 0)
RogueBackstab(other,false,ReuseTime);
if (tripleChance && other->GetHP() > 0 && tripleChance > MakeRandomInt(0, 100))
RogueBackstab(other,false,ReuseTime);
}
if (tripleChance && other->GetHP() > 0 && tripleChance > MakeRandomInt(0, 100))
RogueBackstab(other,false,ReuseTime);
}
if(IsClient())
CastToClient()->CheckIncreaseSkill(SkillBackstab, other, 10);
}
if(IsClient())
CastToClient()->CheckIncreaseSkill(SkillBackstab, other, 10);
}
//Live AA - Chaotic Backstab
else if(aabonuses.FrontalBackstabMinDmg || itembonuses.FrontalBackstabMinDmg || spellbonuses.FrontalBackstabMinDmg) {
//we can stab from any angle, we do min damage though.
RogueBackstab(other, true);
RogueBackstab(other, true, ReuseTime);
if (level > 54) {
float DoubleAttackProbability = (GetSkill(SkillDoubleAttack) + GetLevel()) / 500.0f; // 62.4 max
if(IsClient())
CastToClient()->CheckIncreaseSkill(SkillBackstab, other, 10);
// Check for double attack with main hand assuming maxed DA Skill (MS)
if(MakeRandomFloat(0, 1) < DoubleAttackProbability) // Max 62.4 % chance of DA
if(IsClient() && CastToClient()->CheckDoubleAttack(false))
if(other->GetHP() > 0)
RogueBackstab(other,true, ReuseTime);
if (tripleChance && other->GetHP() > 0 && tripleChance > MakeRandomInt(0, 100))
RogueBackstab(other,false,ReuseTime);
}
if(IsClient())
CastToClient()->CheckIncreaseSkill(SkillBackstab, other, 10);
}
else { //We do a single regular attack if we attack from the front without chaotic stab
Attack(other, 13);
@ -594,6 +580,9 @@ void Mob::TryBackstab(Mob *other, int ReuseTime) {
//heko: backstab
void Mob::RogueBackstab(Mob* other, bool min_damage, int ReuseTime)
{
if (!other)
return;
int32 ndamage = 0;
int32 max_hit = 0;
int32 min_hit = 0;
@ -668,12 +657,20 @@ void Mob::RogueBackstab(Mob* other, bool min_damage, int ReuseTime)
}
ndamage = mod_backstab_damage(ndamage);
uint32 Assassinate_Dmg = 0;
Assassinate_Dmg = TryAssassinate(other, SkillBackstab, ReuseTime);
if (Assassinate_Dmg) {
ndamage = Assassinate_Dmg;
entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, ASSASSINATES, GetName());
}
DoSpecialAttackDamage(other, SkillBackstab, ndamage, min_hit, hate, ReuseTime);
DoSpecialAttackDamage(other, SkillBackstab, ndamage, min_hit, hate, ReuseTime, false, false);
DoAnim(animPiercing);
}
// solar - assassinate
// solar - assassinate [Kayen: No longer used for regular assassinate 6-29-14]
void Mob::RogueAssassinate(Mob* other)
{
//can you dodge, parry, etc.. an assassinate??
@ -861,8 +858,12 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item
} else {
mlog(COMBAT__RANGED, "Ranged attack hit %s.", other->GetName());
if(!TryHeadShot(other, SkillArchery))
{
bool HeadShot = false;
uint32 HeadShot_Dmg = TryHeadShot(other, SkillArchery);
if (HeadShot_Dmg)
HeadShot = true;
int32 TotalDmg = 0;
int16 WDmg = 0;
int16 ADmg = 0;
@ -882,6 +883,9 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item
uint32 MaxDmg = (RuleR(Combat, ArcheryBaseDamageBonus)*(WDmg+ADmg)*GetDamageTable(SkillArchery)) / 100;
int32 hate = ((WDmg+ADmg));
if (HeadShot)
MaxDmg = HeadShot_Dmg;
uint16 bonusArcheryDamageModifier = aabonuses.ArcheryDamageModifier + itembonuses.ArcheryDamageModifier + spellbonuses.ArcheryDamageModifier;
MaxDmg += MaxDmg*bonusArcheryDamageModifier / 100;
@ -938,7 +942,9 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item
hate += (2*((GetLevel()-25)/3));
}
other->AvoidDamage(this, TotalDmg, false);
if (!HeadShot)
other->AvoidDamage(this, TotalDmg, false);
other->MeleeMitigation(this, TotalDmg, minDmg);
if(TotalDmg > 0)
{
@ -957,8 +963,11 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item
else
TotalDmg = -5;
if (HeadShot)
entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, FATAL_BOW_SHOT, GetName());
other->Damage(this, TotalDmg, SPELL_UNKNOWN, SkillArchery);
}
}
//try proc on hits and misses
@ -1264,13 +1273,24 @@ void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite
int32 TotalDmg = 0;
uint32 Assassinate_Dmg = 0;
if (GetClass() == ROGUE && (BehindMob(other, GetX(), GetY())))
Assassinate_Dmg = TryAssassinate(other, SkillThrowing, ranged_timer.GetDuration());
if(WDmg > 0)
{
int minDmg = 1;
uint16 MaxDmg = GetThrownDamage(WDmg, TotalDmg, minDmg);
if (Assassinate_Dmg) {
TotalDmg = Assassinate_Dmg;
entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, ASSASSINATES, GetName());
}
mlog(COMBAT__RANGED, "Item DMG %d. Max Damage %d. Hit for damage %d", WDmg, MaxDmg, TotalDmg);
other->AvoidDamage(this, TotalDmg, false); //CanRiposte=false - Can not riposte throw attacks.
if (!Assassinate_Dmg)
other->AvoidDamage(this, TotalDmg, false); //CanRiposte=false - Can not riposte throw attacks.
other->MeleeMitigation(this, TotalDmg, minDmg);
if(TotalDmg > 0)
{
@ -1854,92 +1874,6 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte)
}
}
/*
void Mob::Taunt(NPC* who, bool always_succeed, float chance_bonus) {
if (who == nullptr)
return;
if(DivineAura())
return;
if(!CombatRange(who))
return;
if(!always_succeed && IsClient())
CastToClient()->CheckIncreaseSkill(TAUNT, who, 10);
int level = GetLevel();
Mob *hate_top = who->GetHateMost();
// Check to see if we're already at the top of the target's hate list
// a mob will not be taunted if its target's health is below 20%
if ((hate_top != this)
&& (who->GetLevel() < level)
&& (hate_top == nullptr || hate_top->GetHPRatio() >= 20) ) {
int32 newhate, tauntvalue;
float tauntchance;
if(always_succeed) {
tauntchance = 101;
} else {
// no idea how taunt success is actually calculated
// TODO: chance for level 50+ mobs should be lower
int level_difference = level - who->GetLevel();
if (level_difference <= 5) {
tauntchance = 25.0; // minimum
tauntchance += tauntchance * (float)GetSkill(TAUNT) / 200.0; // skill modifier
if (tauntchance > 65.0)
tauntchance = 65.0;
}
else if (level_difference <= 10) {
tauntchance = 30.0; // minimum
tauntchance += tauntchance * (float)GetSkill(TAUNT) / 200.0; // skill modifier
if (tauntchance > 85.0)
tauntchance = 85.0;
}
else if (level_difference <= 15) {
tauntchance = 40.0; // minimum
tauntchance += tauntchance * (float)GetSkill(TAUNT) / 200.0; // skill modifier
if (tauntchance > 90.0)
tauntchance = 90.0;
}
else {
tauntchance = 50.0; // minimum
tauntchance += tauntchance * (float)GetSkill(TAUNT) / 200.0; // skill modifier
if (tauntchance > 95.0)
tauntchance = 95.0;
}
}
if (chance_bonus)
tauntchance = tauntchance + (tauntchance*chance_bonus/100.0f);
if (tauntchance > MakeRandomFloat(0, 100)) {
// this is the max additional hate added per succesfull taunt
tauntvalue = (MakeRandomInt(2, 4) * level);
//tauntvalue = (int32) ((float)level * 10.0 * (float)rand()/(float)RAND_MAX + 1);
// new hate: find diff of player's hate and whoever's at top of list, add that plus tauntvalue to players hate
newhate = who->GetNPCHate(hate_top) - who->GetNPCHate(this) + tauntvalue;
// add the hate
who->CastToNPC()->AddToHateList(this, newhate);
}
else{
//generate at least some hate reguardless of the outcome.
who->CastToNPC()->AddToHateList(this, (MakeRandomInt(2, 4)*level));
}
}
//generate at least some hate reguardless of the outcome.
who->CastToNPC()->AddToHateList(this, (MakeRandomInt(2, 4)*level));
if (HasSkillProcs()){
float chance = (float)TauntReuseTime*RuleR(Combat, AvgProcsPerMinute)/60000.0f;
TrySkillProc(who, TAUNT, chance);
}
}
*/
void Mob::Taunt(NPC* who, bool always_succeed, float chance_bonus) {
if (who == nullptr)
@ -2074,30 +2008,134 @@ void Mob::InstillDoubt(Mob *who) {
}
}
bool Mob::TryHeadShot(Mob* defender, SkillUseTypes skillInUse) {
bool Result = false;
uint32 Mob::TryHeadShot(Mob* defender, SkillUseTypes skillInUse) {
if(defender && skillInUse == SkillArchery) {
if(GetAA(aaHeadshot) && defender->GetBodyType() == BT_Humanoid) {
if((GetLevelCon(GetLevel(), defender->GetLevel()) == CON_LIGHTBLUE || GetLevelCon(GetLevel(), defender->GetLevel()) == CON_GREEN) && defender->GetLevel() <= 60 && !defender->IsClient()) {
// WildcardX: These chance formula's below are arbitrary. If someone has a better formula that is more
// consistent with live, feel free to update these.
int AttackerChance = 20 + ((GetLevel() - 51) / 2) + (itembonuses.HeroicDEX / 10);
int DefenderChance = MakeRandomInt(0, 100);
if(AttackerChance > DefenderChance) {
mlog(COMBAT__ATTACKS, "Landed a headshot: Attacker chance was %f and Defender chance was %f.", AttackerChance, DefenderChance);
entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, FATAL_BOW_SHOT, GetName());
defender->Damage(this, 32000, SPELL_UNKNOWN, skillInUse);
Result = true;
}
else {
mlog(COMBAT__ATTACKS, "FAILED a headshot: Attacker chance was %f and Defender chance was %f.", AttackerChance, DefenderChance);
}
}
//Only works on YOUR target.
if(defender && (defender->GetBodyType() == BT_Humanoid) && !defender->IsClient()
&& (skillInUse == SkillArchery) && (GetTarget() == defender)) {
uint32 HeadShot_Dmg = aabonuses.HeadShot[1] + spellbonuses.HeadShot[1] + itembonuses.HeadShot[1];
uint8 HeadShot_Level = 0; //Get Highest Headshot Level
HeadShot_Level = aabonuses.HSLevel;
if (HeadShot_Level < spellbonuses.HSLevel)
HeadShot_Level = spellbonuses.HSLevel;
else if (HeadShot_Level < itembonuses.HSLevel)
HeadShot_Level = itembonuses.HSLevel;
if(HeadShot_Dmg && HeadShot_Level && (defender->GetLevel() <= HeadShot_Level)){
float ProcChance = GetSpecialProcChances(11);
if(ProcChance > MakeRandomFloat(0,1))
return HeadShot_Dmg;
}
}
return Result;
return 0;
}
float Mob::GetSpecialProcChances(uint16 hand)
{
int mydex = GetDEX();
if (mydex > 255)
mydex = 255;
uint16 weapon_speed;
float ProcChance = 0.0f;
float ProcBonus = 0.0f;
switch (hand) {
case 13:
weapon_speed = attack_timer.GetDuration();
break;
case 14:
weapon_speed = attack_dw_timer.GetDuration();
break;
case 11:
weapon_speed = ranged_timer.GetDuration();
break;
}
if (weapon_speed < RuleI(Combat, MinHastedDelay))
weapon_speed = RuleI(Combat, MinHastedDelay);
if (RuleB(Combat, AdjustSpecialProcPerMinute)) {
ProcChance = (static_cast<float>(weapon_speed) *
RuleR(Combat, AvgSpecialProcsPerMinute) / 60000.0f);
ProcBonus += static_cast<float>(mydex/35) + static_cast<float>(itembonuses.HeroicDEX / 25);
ProcChance += ProcChance * ProcBonus / 100.0f;
} else {
/*PRE 2014 CHANGE Dev Quote - "Elidroth SOE:Proc chance is a function of your base hardcapped Dexterity / 35 + Heroic Dexterity / 25.”
Kayen: Most reports suggest a ~ 6% chance to Headshot which consistent with above.*/
ProcChance = (static_cast<float>(mydex/35) + static_cast<float>(itembonuses.HeroicDEX / 25))/100.0f;
}
return ProcChance;
}
uint32 Mob::TryAssassinate(Mob* defender, SkillUseTypes skillInUse, uint16 ReuseTime) {
if(defender && (defender->GetBodyType() == BT_Humanoid) && !defender->IsClient() &&
(skillInUse == SkillBackstab || skillInUse == SkillThrowing)) {
uint32 Assassinate_Dmg = aabonuses.Assassinate[1] + spellbonuses.Assassinate[1] + itembonuses.Assassinate[1];
uint8 Assassinate_Level = 0; //Get Highest Headshot Level
Assassinate_Level = aabonuses.AssassinateLevel;
if (Assassinate_Level < spellbonuses.AssassinateLevel)
Assassinate_Level = spellbonuses.AssassinateLevel;
else if (Assassinate_Level < itembonuses.AssassinateLevel)
Assassinate_Level = itembonuses.AssassinateLevel;
if (GetLevel() >= 60){ //Innate Assassinate Ability if client as no bonuses.
if (!Assassinate_Level)
Assassinate_Level = 45;
if (!Assassinate_Dmg)
Assassinate_Dmg = 32000;
}
if(Assassinate_Dmg && Assassinate_Level && (defender->GetLevel() <= Assassinate_Level)){
float ProcChance = 0.0f;
if (skillInUse == SkillThrowing)
ProcChance = GetSpecialProcChances(11);
else
ProcChance = GetAssassinateProcChances(ReuseTime);
if(ProcChance > MakeRandomFloat(0,1))
return Assassinate_Dmg;
}
}
return 0;
}
float Mob::GetAssassinateProcChances(uint16 ReuseTime)
{
int mydex = GetDEX();
if (mydex > 255)
mydex = 255;
float ProcChance = 0.0f;
float ProcBonus = 0.0f;
if (RuleB(Combat, AdjustSpecialProcPerMinute)) {
ProcChance = (static_cast<float>(ReuseTime*1000) *
RuleR(Combat, AvgSpecialProcsPerMinute) / 60000.0f);
ProcBonus += (10 + (static_cast<float>(mydex/10) + static_cast<float>(itembonuses.HeroicDEX /10)))/100.0f;
ProcChance += ProcChance * ProcBonus / 100.0f;
} else {
/*Kayen: Unable to find data on old proc rate of assassinate, no idea if our formula is real or made up.*/
ProcChance = (10 + (static_cast<float>(mydex/10) + static_cast<float>(itembonuses.HeroicDEX /10)))/100.0f;
}
return ProcChance;
}
void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes skillinuse, int16 chance_mod, int16 focus, bool CanRiposte)

View File

@ -1551,15 +1551,22 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
GetPetType() != petCharmed
)
{
int lvlmod = 4;
if(caster->IsClient() && caster->CastToClient()->GetAA(aaImprovedReclaimEnergy))
lvlmod = 8; //this is an unconfirmed number, I made it up
if(caster->IsClient() && caster->CastToClient()->GetAA(aaImprovedReclaimEnergy2))
lvlmod = 8; //this is an unconfirmed number, I made it up
caster->SetMana(caster->GetMana()+(GetLevel()*lvlmod));
uint16 pet_spellid = CastToNPC()->GetPetSpellID();
uint16 pet_ActSpellCost = caster->GetActSpellCost(pet_spellid, spells[pet_spellid].mana);
int16 ImprovedReclaimMod = caster->spellbonuses.ImprovedReclaimEnergy +
caster->itembonuses.ImprovedReclaimEnergy +
caster->aabonuses.ImprovedReclaimEnergy;
if (!ImprovedReclaimMod)
ImprovedReclaimMod = 75; //Reclaim Energy default is 75% of actual mana cost
pet_ActSpellCost = pet_ActSpellCost*ImprovedReclaimMod/100;
caster->SetMana(caster->GetMana() + pet_ActSpellCost);
if(caster->IsClient())
caster->CastToClient()->SetPet(0);
SetOwnerID(0); // this will kill the pet
}
break;
@ -2183,12 +2190,15 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
#ifdef SPELL_EFFECT_SPAM
snprintf(effect_desc, _EDLEN, "Fading Memories");
#endif
if(caster && caster->IsClient())
caster->CastToClient()->Escape();
else
{
entity_list.RemoveFromTargets(caster);
SetInvisible(1);
if(MakeRandomInt(0, 99) < spells[spell_id].base[i] ) {
if(caster && caster->IsClient())
caster->CastToClient()->Escape();
else
{
entity_list.RemoveFromTargets(caster);
SetInvisible(1);
}
}
break;
}
@ -2234,8 +2244,13 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
#ifdef SPELL_EFFECT_SPAM
snprintf(effect_desc, _EDLEN, "AE Taunt");
#endif
if(caster && caster->IsClient())
entity_list.AETaunt(caster->CastToClient());
if(caster && caster->IsClient()){
float range = 0.0f;
if (spells[spell_id].base2[i])
range = (float)spells[spell_id].base[i];
entity_list.AETaunt(caster->CastToClient(), range);
}
break;
}
@ -2279,12 +2294,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
#endif
//meh dupe issue with npc casting this
if(caster->IsClient()){
//this spell doesn't appear to actually contain the information on duration inside of it oddly
int dur = 60;
if(spell_id == 3269)
dur += 15;
else if(spell_id == 3270)
dur += 30;
int dur = spells[spell_id].max[i];
if (!dur)
dur = 60;
caster->WakeTheDead(spell_id, caster->GetTarget(), dur);
}
@ -2643,9 +2655,12 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
case SE_Taunt:
{
if (IsNPC())
if (IsNPC()){
caster->Taunt(this->CastToNPC(), false, spell.base[i]);
if (spell.base2[i] > 0)
CastToNPC()->SetHate(caster, (CastToNPC()->GetHateAmount(caster) + spell.base2[i]));
}
break;
}
@ -2670,9 +2685,50 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
case SE_AddHatePct:
{
if (IsNPC())
CastToNPC()->SetHate(caster, (CastToNPC()->GetHateAmount(caster) * (100 + spell.base[i]) / 100));
if (IsNPC()){
int32 new_hate = CastToNPC()->GetHateAmount(caster) * (100 + spell.base[i]) / 100;
if (new_hate <= 0)
new_hate = 1;
CastToNPC()->SetHate(caster, new_hate);
}
break;
}
case SE_Hate:{
if (buffslot >= 0)
break;
if(caster){
if(effect_value > 0){
if(caster){
if(caster->IsClient() && !caster->CastToClient()->GetFeigned())
AddToHateList(caster, effect_value);
else if(!caster->IsClient())
AddToHateList(caster, effect_value);
}
}else{
int32 newhate = GetHateAmount(caster) + effect_value;
if (newhate < 1)
SetHate(caster,1);
else
SetHate(caster,newhate);
}
}
break;
}
case SE_MassGroupBuff:{
SetMGB(true);
Message_StringID(MT_Disciplines, MGB_STRING);
break;
}
case SE_IllusionOther: {
SetProjectIllusion(true);
Message(10, "The power of your next illusion spell will flow to your grouped target in your place.");
break;
}
@ -2746,7 +2802,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
case SE_ChangeFrenzyRad:
case SE_Harmony:
case SE_ChangeAggro:
case SE_Hate2:
case SE_Identify:
case SE_InstantHate:
case SE_ReduceHate:
@ -2805,8 +2860,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
case SE_CriticalHealChance:
case SE_CriticalHealOverTime:
case SE_CriticalDoTChance:
case SE_SpellOnKill:
case SE_SpellOnKill2:
case SE_ProcOnKillShot:
case SE_ProcOnSpellKillShot:
case SE_CriticalDamageMob:
case SE_LimitSpellGroup:
case SE_ResistCorruption:
@ -2831,7 +2886,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
case SE_ACv2:
case SE_ManaRegen_v2:
case SE_FcDamagePctCrit:
case SE_FcHealAmt:
case SE_FcHealAmt:
case SE_FcHealPctIncoming:
case SE_CriticalHealDecay:
case SE_CriticalRegenDecay:
@ -2845,6 +2900,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
case SE_PetCriticalHit:
case SE_SlayUndead:
case SE_GiveDoubleAttack:
case SE_StrikeThrough:
case SE_StrikeThrough2:
case SE_SecondaryDmgInc:
case SE_ArcheryDamageModifier:
@ -2863,6 +2919,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
case SE_CombatStability:
case SE_AddSingingMod:
case SE_SongModCap:
case SE_HeadShot:
case SE_HeadShotLevel:
case SE_PetAvoidance:
case SE_GiveDoubleRiposte:
case SE_Ambidexterity:
@ -3340,7 +3398,7 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste
break;
}
case SE_Hate2:{
case SE_Hate:{
effect_value = CalcSpellEffectValue(spell_id, i, caster_level);
if(caster){
if(effect_value > 0){
@ -3536,9 +3594,13 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste
case SE_AddHateOverTimePct:
{
if (IsNPC())
CastToNPC()->SetHate(caster, (CastToNPC()->GetHateAmount(caster) * (100 + spell.base[i]) / 100));
if (IsNPC()){
int32 new_hate = CastToNPC()->GetHateAmount(caster) * (100 + spell.base[i]) / 100;
if (new_hate <= 0)
new_hate = 1;
CastToNPC()->SetHate(caster, new_hate);
}
break;
}
@ -5742,16 +5804,25 @@ uint16 Mob::GetSpellEffectResistChance(uint16 spell_id)
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
Cancel Beneficial spell effects to use a new method. The chances for those spells to
affect their targets have not changed unless otherwise noted.*/
/*This should provide a somewhat accurate conversion between pre 5/14 base values and post.
until more information is avialble - Kayen*/
if (level_modifier >= 100)
level_modifier = level_modifier/100;
//Dispels - Check level of caster agianst buffs level (level of the caster who cast the buff)
//Effect value of dispels are treated as a level modifier.
//Values for scaling were obtain from live parses, best estimates.
caster_level += level_modifier - 1;
int dispel_chance = 36; //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;
if (level_diff > 0)
dispel_chance += level_diff * 8;
dispel_chance += level_diff * 7;
else if (level_diff < 0)
dispel_chance += level_diff * 2;

View File

@ -399,7 +399,7 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
mana_cost = GetActSpellCost(spell_id, mana_cost);
}
if(IsClient() && CastToClient()->CheckAAEffect(aaEffectMassGroupBuff) && spells[spell_id].can_mgb)
if(HasMGB() && spells[spell_id].can_mgb)
mana_cost *= 2;
// mana is checked for clients on the frontend. we need to recheck it for NPCs though
@ -1371,7 +1371,7 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce
&& IsClient()
&& (IsGrouped() // still self only if not grouped
|| IsRaidGrouped())
&& CastToClient()->CheckAAEffect(aaEffectProjectIllusion)){
&& (HasProjectIllusion())){
mlog(AA__MESSAGE, "Project Illusion overwrote target caster: %s spell id: %d was ON", GetName(), spell_id);
targetType = ST_GroupClientAndPet;
}
@ -1853,7 +1853,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
range = GetActSpellRange(spell_id, range);
if(IsPlayerIllusionSpell(spell_id)
&& IsClient()
&& CastToClient()->CheckAAEffect(aaEffectProjectIllusion)){
&& (HasProjectIllusion())){
range = 100;
}
if(spell_target != nullptr && spell_target != this) {
@ -1912,9 +1912,9 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
if(IsPlayerIllusionSpell(spell_id)
&& IsClient()
&& CastToClient()->CheckAAEffect(aaEffectProjectIllusion)){
&& (HasProjectIllusion())){
mlog(AA__MESSAGE, "Effect Project Illusion for %s on spell id: %d was ON", GetName(), spell_id);
CastToClient()->DisableAAEffect(aaEffectProjectIllusion);
SetProjectIllusion(false);
}
else{
mlog(AA__MESSAGE, "Effect Project Illusion for %s on spell id: %d was OFF", GetName(), spell_id);
@ -1969,11 +1969,11 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
}
#endif //BOTS
if(spells[spell_id].can_mgb && IsClient() && CastToClient()->CheckAAEffect(aaEffectMassGroupBuff))
if(spells[spell_id].can_mgb && HasMGB())
{
SpellOnTarget(spell_id, this);
entity_list.MassGroupBuff(this, this, spell_id, true);
CastToClient()->DisableAAEffect(aaEffectMassGroupBuff);
SetMGB(false);
}
else
{
@ -2622,19 +2622,31 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2,
}
/*Buff stacking prevention spell effects (446 - 449) works as follows... If B prevent A, if C prevent B, if D prevent C.
If checking same type ie A vs A, which ever effect base value is higher will take hold.
Special check is added to make sure the buffs stack properly when applied from fade on duration effect, since the buff
is not fully removed at the time of the trgger*/
if (spellbonuses.BStacker) {
if (spellbonuses.AStacker[0]) {
if ((effect2 == SE_AStacker) && (sp2.effectid[i] <= spellbonuses.AStacker[1]))
return -1;
}
if (spellbonuses.BStacker[0]) {
if ((effect2 == SE_BStacker) && (sp2.effectid[i] <= spellbonuses.BStacker[1]))
return -1;
if ((effect2 == SE_AStacker) && (!IsCastonFadeDurationSpell(spellid1) && buffs[buffslot].ticsremaining != 1 && IsEffectInSpell(spellid1, SE_BStacker)))
return -1;
}
if (spellbonuses.CStacker) {
if (spellbonuses.CStacker[0]) {
if ((effect2 == SE_CStacker) && (sp2.effectid[i] <= spellbonuses.CStacker[1]))
return -1;
if ((effect2 == SE_BStacker) && (!IsCastonFadeDurationSpell(spellid1) && buffs[buffslot].ticsremaining != 1 && IsEffectInSpell(spellid1, SE_CStacker)))
return -1;
}
if (spellbonuses.DStacker) {
if (spellbonuses.DStacker[0]) {
if ((effect2 == SE_DStacker) && (sp2.effectid[i] <= spellbonuses.DStacker[1]))
return -1;
if ((effect2 == SE_CStacker) && (!IsCastonFadeDurationSpell(spellid1) && buffs[buffslot].ticsremaining != 1 && IsEffectInSpell(spellid1, SE_DStacker)))
return -1;
}