mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-13 10:31:29 +00:00
Merge pull request #169 from KayenEQ/Development
Updates to npc_spells and npc_types table.
This commit is contained in:
commit
d0756ff2af
@ -1,9 +1,11 @@
|
||||
EQEMu Changelog (Started on Sept 24, 2003 15:50)
|
||||
-------------------------------------------------------
|
||||
|
||||
== 07/16/2014 ==
|
||||
Uleat: Initial commit of new client/server 'dictionaries' - work in-progress... Changed equipment slot references to reflect new naming
|
||||
conventions. Lua enumerations maintain both the old and new names as to not break existing scripts..but, the old names are deprecated.
|
||||
|
||||
|
||||
== 07/14/2014 ==
|
||||
KLS: Changes to CMake build
|
||||
-Lua builds by default now
|
||||
@ -11,6 +13,45 @@ KLS: Changes to CMake build
|
||||
-Binary files will now be put into ${CMAKE_BINARY_DIR}/bin instead of ${CMAKE_BINARY_DIR}/Bin
|
||||
The last two are of note to people on non-windows systems as case sensitivity is important. Edit your scripts accordingly, thank you.
|
||||
|
||||
|
||||
== 07/10/2014 ==
|
||||
Kayen: Updated table npc_spells to now support defensive and ranged procs.
|
||||
Note: Proc rate modifier work as it does for spell effects (ie 200 = 200% baseline chance modifier)
|
||||
Table is also now contains 12 AI spell casting variables that can be set to fine tune casting behaviors per spell set.
|
||||
Global default rules have also been added that can further fine tune all content if no specific variables are set.
|
||||
|
||||
Descriptions of new AI casting fields in npc_spells
|
||||
'fail_recast' AI spell recast time(MS) when an spell is cast but fails (ie stunned)
|
||||
'engaged_no_sp_recast_min' AI spell recast time(MS) checked when no spell is cast while engaged in combat. (min time in random)
|
||||
'engaged_no_sp_recast_max' AI spell recast time(MS) checked when no spell is cast while engaged in combat. (max time in random)
|
||||
'engaged_b_self_chance' Chance during first AI Cast check to do a beneficial spell on self (ie check to heal self)
|
||||
'engaged_b_other_chance' Chance during second AI Cast check to do a beneficial spell on others.(ie check to heal others)
|
||||
'engaged_d_chance' 'Chance during third AI Cast check to do a determental spell on others (ie check to nuke others)
|
||||
'pursue_no_sp_recast_min' AI spell recast time(MS) checked when no spell is cast while chasing target. (min time in random)
|
||||
'pursue_no_sp_recast_max' AI spell recast time(MS) checked when no spell is cast while chasing target. (max time in random)
|
||||
'pursue_d_chance' Chance while chasing target to cast a detrimental spell.
|
||||
'idle_no_sp_recast_min' AI spell recast time(MS) checked when no spell is cast while idle. (min time in random)
|
||||
'idle_no_sp_recast_max' AI spell recast time(MS) checked when no spell is cast while idle. (max time in random)
|
||||
'idle_b_chance' Chance to cast a beneficial spell while idle (ie cast heal on self while out of combat).
|
||||
|
||||
Kayen: Updated table npc_types, adding field 'ranged_type' and 'ammo_idfile'
|
||||
'ranged_type' Will set what skill / animation is used when NPC uses a ranged attacked (special ability 11)
|
||||
'ammo_idfile' Will set what projectile graphic an NPC uses in a ranged attacked (special ability 11) Format IT#### (same as item 'idfile')
|
||||
(*Set to IT11118 for some fun*)
|
||||
Added parameters: SPECATK_RANGED_ATK = 11
|
||||
Param0: Min Ranged distance (default: 25)
|
||||
Param1: Max Ranged distance (default: 250)
|
||||
Param2: Percent Chance to Hit modifier
|
||||
Param3: Percent Total Damage modifier
|
||||
|
||||
Kayen: Updated to Chance to Hit code with how bonuses are applied to be consistent for all effects.
|
||||
Added field to npc_types 'Avoidance' which will modify chance to avoid melee
|
||||
Added rules to set max and min chance to hit from melee/ranged (Default 95% / 5%)
|
||||
|
||||
Required SQL: utils/sql/git/required/2014_07_10_npc_spells.sql
|
||||
Optional SQL: utils/sql/git/optional/2014_07_10_AICastingRules.sql
|
||||
|
||||
|
||||
== 07/5/2014 ==
|
||||
Kayen: Updated SE_Sanctuary - Adjust way hate lowering effect worked to be more accurate
|
||||
Kayen: Updated SE_SympatheticProc - Revised proc rate formula to be accurate to live.
|
||||
|
||||
@ -311,6 +311,19 @@ RULE_INT ( Spells, FRProjectileItem_NPC, 80684) // Item id for NPC Fire 'spell p
|
||||
RULE_BOOL ( Spells, UseLiveSpellProjectileGFX, false) // Use spell projectile graphics set in the spells_new table (player_1). Server must be using UF+ spell file.
|
||||
RULE_BOOL ( Spells, FocusCombatProcs, false) //Allow all combat procs to receive focus effects.
|
||||
RULE_BOOL ( Spells, PreNerfBardAEDoT, false) //Allow bard AOE dots to damage targets when moving.
|
||||
RULE_INT ( Spells, AI_SpellCastFinishedFailRecast, 800) // AI spell recast time(MS) when an spell is cast but fails (ie stunned).
|
||||
RULE_INT ( Spells, AI_EngagedNoSpellMinRecast, 500) // AI spell recast time(MS) check when no spell is cast while engaged. (min time in random)
|
||||
RULE_INT ( Spells, AI_EngagedNoSpellMaxRecast, 1000) // AI spell recast time(MS) check when no spell is cast engaged.(max time in random)
|
||||
RULE_INT ( Spells, AI_EngagedBeneficialSelfChance, 100) // Chance during first AI Cast check to do a beneficial spell on self.
|
||||
RULE_INT ( Spells, AI_EngagedBeneficialOtherChance, 25) // Chance during second AI Cast check to do a beneficial spell on others.
|
||||
RULE_INT ( Spells, AI_EngagedDetrimentalChance, 20) // Chance during third AI Cast check to do a determental spell on others.
|
||||
RULE_INT ( Spells, AI_PursueNoSpellMinRecast, 500) // AI spell recast time(MS) check when no spell is cast while chasing target. (min time in random)
|
||||
RULE_INT ( Spells, AI_PursueNoSpellMaxRecast, 2000) // AI spell recast time(MS) check when no spell is cast while chasing target. (max time in random)
|
||||
RULE_INT ( Spells, AI_PursueDetrimentalChance, 90) // Chance while chasing target to cast a detrimental spell.
|
||||
RULE_INT ( Spells, AI_IdleNoSpellMinRecast, 500) // AI spell recast time(MS) check when no spell is cast while idle. (min time in random)
|
||||
RULE_INT ( Spells, AI_IdleNoSpellMaxRecast, 2000) // AI spell recast time(MS) check when no spell is cast while chasing target. (max time in random)
|
||||
RULE_INT ( Spells, AI_IdleBeneficialChance, 100) // Chance while idle to do a beneficial spell on self or others.
|
||||
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY( Combat )
|
||||
@ -345,6 +358,8 @@ 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, 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, 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_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_REAL ( Combat, ArcheryBaseDamageBonus, 1) // % Modifier to Base Archery Damage (.5 = 50% base damage, 1 = 100%, 2 = 200%)
|
||||
|
||||
15
utils/sql/git/optional/2014_07_10_AICastingRules.sql
Normal file
15
utils/sql/git/optional/2014_07_10_AICastingRules.sql
Normal file
@ -0,0 +1,15 @@
|
||||
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:AI_SpellCastFinishedFailRecast','800', 'AI spell recast time(MS) when an spell is cast but fails (ie stunned)');
|
||||
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:AI_EngagedNoSpellMinRecast','500', 'AI spell recast time(MS) check when no spell is cast while engaged. (min time in random)');
|
||||
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:AI_EngagedNoSpellMaxRecast','1000','AI spell recast time(MS) check when no spell is cast engaged.(max time in random)');
|
||||
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:AI_EngagedBeneficialSelfChance,','100', 'Chance during first AI Cast check to do a beneficial spell on self.');
|
||||
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:AI_EngagedBeneficialOtherChance','25', 'Chance during second AI Cast check to do a beneficial spell on others.');
|
||||
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:AI_EngagedDetrimentalChance','20','Chance during third AI Cast check to do a determental spell on others.');
|
||||
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:AI_PursueNoSpellMinRecast','500', 'AI spell recast time(MS) check when no spell is cast while chasing target. (min time in random)');
|
||||
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:AI_PursueNoSpellMaxRecast','2000','AI spell recast time(MS) check when no spell is cast while chasing target. (max time in random)');
|
||||
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:AI_PursueDetrimentalChance','90','Chance while chasing target to cast a detrimental spell.');
|
||||
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:AI_IdleNoSpellMinRecast','500','AI spell recast time(MS) check when no spell is cast while idle. (min time in random)');
|
||||
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:AI_IdleNoSpellMaxRecast','2000','AI spell recast time(MS) check when no spell is cast while chasing target. (max time in random)');
|
||||
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:AI_IdleBeneficialChance','100','Chance while idle to do a beneficial spell on self or others.');
|
||||
|
||||
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Combat:MinChancetoHit','5','Minimum % chance to hit with regular melee/ranged.');
|
||||
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Combat:MaxChancetoHit','95','Maximum % chance to hit with regular melee/ranged.');
|
||||
@ -1,3 +1,5 @@
|
||||
ALTER TABLE `npc_types` ADD `no_target_hotkey` tinyint( 1 ) UNSIGNED NOT NULL DEFAULT '0' AFTER `healscale`;
|
||||
-- npc_types
|
||||
ALTER TABLE `npc_types` ADD `ammo_idfile` varchar( 30 ) NOT NULL DEFAULT 'IT10' AFTER `d_meele_texture2`;
|
||||
ALTER TABLE `npc_types` ADD `ranged_type` tinyint( 4 ) UNSIGNED NOT NULL DEFAULT '7' AFTER `sec_melee_type`;
|
||||
|
||||
|
||||
|
||||
22
utils/sql/git/required/2014_07_10_npc_spells.sql
Normal file
22
utils/sql/git/required/2014_07_10_npc_spells.sql
Normal file
@ -0,0 +1,22 @@
|
||||
-- npc_types
|
||||
ALTER TABLE `npc_types` ADD `ammo_idfile` varchar( 30 ) NOT NULL DEFAULT 'IT10' AFTER `d_meele_texture2`;
|
||||
ALTER TABLE `npc_types` ADD `ranged_type` tinyint( 4 ) UNSIGNED NOT NULL DEFAULT '7' AFTER `sec_melee_type`;
|
||||
ALTER TABLE `npc_types` ADD `Avoidance` mediumint(9) UNSIGNED NOT NULL DEFAULT '0' AFTER `Accuracy`;
|
||||
|
||||
-- npc spells
|
||||
ALTER TABLE `npc_spells` ADD `range_proc` smallint(5) NOT NULL DEFAULT '-1';
|
||||
ALTER TABLE `npc_spells` ADD `rproc_chance` smallint(5) NOT NULL DEFAULT '0';
|
||||
ALTER TABLE `npc_spells` ADD `defensive_proc` smallint(5) NOT NULL DEFAULT '-1';
|
||||
ALTER TABLE `npc_spells` ADD `dproc_chance` smallint(5) NOT NULL DEFAULT '0';
|
||||
ALTER TABLE `npc_spells` ADD `fail_recast` int(11) unsigned NOT NULL DEFAULT '0';
|
||||
ALTER TABLE `npc_spells` ADD `engaged_no_sp_recast_min` int(11) unsigned NOT NULL DEFAULT '0';
|
||||
ALTER TABLE `npc_spells` ADD `engaged_no_sp_recast_max` int(11) unsigned NOT NULL DEFAULT '0';
|
||||
ALTER TABLE `npc_spells` ADD `engaged_b_self_chance` tinyint(3) unsigned NOT NULL DEFAULT '0';
|
||||
ALTER TABLE `npc_spells` ADD `engaged_b_other_chance` tinyint(3) unsigned NOT NULL DEFAULT '0';
|
||||
ALTER TABLE `npc_spells` ADD `engaged_d_chance` tinyint(3) unsigned NOT NULL DEFAULT '0';
|
||||
ALTER TABLE `npc_spells` ADD `pursue_no_sp_recast_min` int(3) unsigned NOT NULL DEFAULT '0';
|
||||
ALTER TABLE `npc_spells` ADD `pursue_no_sp_recast_max` int(11) unsigned NOT NULL DEFAULT '0';
|
||||
ALTER TABLE `npc_spells` ADD `pursue_d_chance` tinyint(3) unsigned NOT NULL DEFAULT '0';
|
||||
ALTER TABLE `npc_spells` ADD `idle_no_sp_recast_min` int(11) unsigned NOT NULL DEFAULT '0';
|
||||
ALTER TABLE `npc_spells` ADD `idle_no_sp_recast_max` int(11) unsigned NOT NULL DEFAULT '0';
|
||||
ALTER TABLE `npc_spells` ADD `idle_b_chance` tinyint(11) unsigned NOT NULL DEFAULT '0';
|
||||
142
zone/MobAI.cpp
142
zone/MobAI.cpp
@ -1897,7 +1897,7 @@ void NPC::AI_Event_SpellCastFinished(bool iCastSucceeded, uint8 slot) {
|
||||
AIautocastspell_timer->Start(recovery_time, false);
|
||||
}
|
||||
else
|
||||
AIautocastspell_timer->Start(800, false);
|
||||
AIautocastspell_timer->Start(AISpellVar.fail_recast, false);
|
||||
casting_spell_AIindex = AIspells.size();
|
||||
}
|
||||
}
|
||||
@ -1910,13 +1910,13 @@ bool NPC::AI_EngagedCastCheck() {
|
||||
mlog(AI__SPELLS, "Engaged autocast check triggered. Trying to cast healing spells then maybe offensive spells.");
|
||||
|
||||
// try casting a heal or gate
|
||||
if (!AICastSpell(this, 100, SpellType_Heal | SpellType_Escape | SpellType_InCombatBuff)) {
|
||||
if (!AICastSpell(this, AISpellVar.engaged_beneficial_self_chance, SpellType_Heal | SpellType_Escape | SpellType_InCombatBuff)) {
|
||||
// try casting a heal on nearby
|
||||
if (!entity_list.AICheckCloseBeneficialSpells(this, 25, MobAISpellRange, SpellType_Heal)) {
|
||||
if (!entity_list.AICheckCloseBeneficialSpells(this, AISpellVar.engaged_beneficial_other_chance, MobAISpellRange, SpellType_Heal)) {
|
||||
//nobody to heal, try some detrimental spells.
|
||||
if(!AICastSpell(GetTarget(), 20, SpellType_Nuke | SpellType_Lifetap | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff | SpellType_Charm | SpellType_Root)) {
|
||||
if(!AICastSpell(GetTarget(), AISpellVar.engaged_detrimental_chance, SpellType_Nuke | SpellType_Lifetap | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff | SpellType_Charm | SpellType_Root)) {
|
||||
//no spell to cast, try again soon.
|
||||
AIautocastspell_timer->Start(RandomTimer(500, 1000), false);
|
||||
AIautocastspell_timer->Start(RandomTimer(AISpellVar.engaged_no_sp_recast_min, AISpellVar.engaged_no_sp_recast_max), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1931,9 +1931,9 @@ bool NPC::AI_PursueCastCheck() {
|
||||
AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting.
|
||||
|
||||
mlog(AI__SPELLS, "Engaged (pursuing) autocast check triggered. Trying to cast offensive spells.");
|
||||
if(!AICastSpell(GetTarget(), 90, SpellType_Root | SpellType_Nuke | SpellType_Lifetap | SpellType_Snare | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff)) {
|
||||
if(!AICastSpell(GetTarget(), AISpellVar.pursue_detrimental_chance, SpellType_Root | SpellType_Nuke | SpellType_Lifetap | SpellType_Snare | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff)) {
|
||||
//no spell cast, try again soon.
|
||||
AIautocastspell_timer->Start(RandomTimer(500, 2000), false);
|
||||
AIautocastspell_timer->Start(RandomTimer(AISpellVar.pursue_no_sp_recast_min, AISpellVar.pursue_no_sp_recast_max), false);
|
||||
} //else, spell casting finishing will reset the timer.
|
||||
return(true);
|
||||
}
|
||||
@ -1946,11 +1946,11 @@ bool NPC::AI_IdleCastCheck() {
|
||||
std::cout << "Non-Engaged autocast check triggered: " << this->GetName() << std::endl;
|
||||
#endif
|
||||
AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting.
|
||||
if (!AICastSpell(this, 100, SpellType_Heal | SpellType_Buff | SpellType_Pet)) {
|
||||
if (!AICastSpell(this, AISpellVar.idle_beneficial_chance, SpellType_Heal | SpellType_Buff | SpellType_Pet)) {
|
||||
if(!entity_list.AICheckCloseBeneficialSpells(this, 33, MobAISpellRange, SpellType_Heal | SpellType_Buff)) {
|
||||
//if we didnt cast any spells, our autocast timer just resets to the
|
||||
//last duration it was set to... try to put up a more reasonable timer...
|
||||
AIautocastspell_timer->Start(RandomTimer(1000, 5000), false);
|
||||
AIautocastspell_timer->Start(RandomTimer(AISpellVar.idle_no_sp_recast_min, AISpellVar.idle_no_sp_recast_max), false);
|
||||
} //else, spell casting finishing will reset the timer.
|
||||
} //else, spell casting finishing will reset the timer.
|
||||
return(true);
|
||||
@ -2340,11 +2340,44 @@ bool NPC::AI_AddNPCSpells(uint32 iDBSpellsID) {
|
||||
std::cout << " (not found)";
|
||||
std::cout << std::endl;
|
||||
#endif
|
||||
int16 attack_proc_spell = -1;
|
||||
uint16 attack_proc_spell = -1;
|
||||
int8 proc_chance = 3;
|
||||
uint16 range_proc_spell = -1;
|
||||
int16 rproc_chance = 0;
|
||||
uint16 defensive_proc_spell = -1;
|
||||
int16 dproc_chance = 0;
|
||||
uint32 _fail_recast = 0;
|
||||
uint32 _engaged_no_sp_recast_min = 0;
|
||||
uint32 _engaged_no_sp_recast_max = 0;
|
||||
uint8 _engaged_beneficial_self_chance = 0;
|
||||
uint8 _engaged_beneficial_other_chance = 0;
|
||||
uint8 _engaged_detrimental_chance = 0;
|
||||
uint32 _pursue_no_sp_recast_min = 0;
|
||||
uint32 _pursue_no_sp_recast_max = 0;
|
||||
uint8 _pursue_detrimental_chance = 0;
|
||||
uint32 _idle_no_sp_recast_min = 0;
|
||||
uint32 _idle_no_sp_recast_max = 0;
|
||||
uint8 _idle_beneficial_chance = 0;
|
||||
|
||||
if (parentlist) {
|
||||
attack_proc_spell = parentlist->attack_proc;
|
||||
proc_chance = parentlist->proc_chance;
|
||||
range_proc_spell = parentlist->range_proc;
|
||||
rproc_chance = parentlist->rproc_chance;
|
||||
defensive_proc_spell = parentlist->defensive_proc;
|
||||
dproc_chance = parentlist->dproc_chance;
|
||||
_fail_recast = parentlist->fail_recast;
|
||||
_engaged_no_sp_recast_min = parentlist->engaged_no_sp_recast_min;
|
||||
_engaged_no_sp_recast_max = parentlist->engaged_no_sp_recast_max;
|
||||
_engaged_beneficial_self_chance = parentlist->engaged_beneficial_self_chance;
|
||||
_engaged_beneficial_other_chance = parentlist->engaged_beneficial_other_chance;
|
||||
_engaged_detrimental_chance = parentlist->engaged_detrimental_chance;
|
||||
_pursue_no_sp_recast_min = parentlist->pursue_no_sp_recast_min;
|
||||
_pursue_no_sp_recast_max = parentlist->pursue_no_sp_recast_max;
|
||||
_pursue_detrimental_chance = parentlist->pursue_detrimental_chance;
|
||||
_idle_no_sp_recast_min = parentlist->idle_no_sp_recast_min;
|
||||
_idle_no_sp_recast_max = parentlist->idle_no_sp_recast_max;
|
||||
_idle_beneficial_chance = parentlist->idle_beneficial_chance;
|
||||
for (i=0; i<parentlist->numentries; i++) {
|
||||
if (GetLevel() >= parentlist->entries[i].minlevel && GetLevel() <= parentlist->entries[i].maxlevel && parentlist->entries[i].spellid > 0) {
|
||||
if (!IsSpellInList(spell_list, parentlist->entries[i].spellid))
|
||||
@ -2361,6 +2394,36 @@ bool NPC::AI_AddNPCSpells(uint32 iDBSpellsID) {
|
||||
attack_proc_spell = spell_list->attack_proc;
|
||||
proc_chance = spell_list->proc_chance;
|
||||
}
|
||||
|
||||
if (spell_list->range_proc >= 0) {
|
||||
range_proc_spell = spell_list->range_proc;
|
||||
rproc_chance = spell_list->rproc_chance;
|
||||
}
|
||||
|
||||
if (spell_list->defensive_proc >= 0) {
|
||||
defensive_proc_spell = spell_list->defensive_proc;
|
||||
dproc_chance = spell_list->dproc_chance;
|
||||
}
|
||||
|
||||
//If any casting variables are defined in the current list, ignore those in the parent list.
|
||||
if (spell_list->fail_recast || spell_list->engaged_no_sp_recast_min || spell_list->engaged_no_sp_recast_max
|
||||
|| spell_list->engaged_beneficial_self_chance || spell_list->engaged_beneficial_other_chance || spell_list->engaged_detrimental_chance
|
||||
|| spell_list->pursue_no_sp_recast_min || spell_list->pursue_no_sp_recast_max || spell_list->pursue_detrimental_chance
|
||||
|| spell_list->idle_no_sp_recast_min || spell_list->idle_no_sp_recast_max || spell_list->idle_beneficial_chance) {
|
||||
_fail_recast = spell_list->fail_recast;
|
||||
_engaged_no_sp_recast_min = spell_list->engaged_no_sp_recast_min;
|
||||
_engaged_no_sp_recast_max = spell_list->engaged_no_sp_recast_max;
|
||||
_engaged_beneficial_self_chance = spell_list->engaged_beneficial_self_chance;
|
||||
_engaged_beneficial_other_chance = spell_list->engaged_beneficial_other_chance;
|
||||
_engaged_detrimental_chance = spell_list->engaged_detrimental_chance;
|
||||
_pursue_no_sp_recast_min = spell_list->pursue_no_sp_recast_min;
|
||||
_pursue_no_sp_recast_max = spell_list->pursue_no_sp_recast_max;
|
||||
_pursue_detrimental_chance = spell_list->pursue_detrimental_chance;
|
||||
_idle_no_sp_recast_min = spell_list->idle_no_sp_recast_min;
|
||||
_idle_no_sp_recast_max = spell_list->idle_no_sp_recast_max;
|
||||
_idle_beneficial_chance = spell_list->idle_beneficial_chance;
|
||||
}
|
||||
|
||||
for (i=0; i<spell_list->numentries; i++) {
|
||||
if (GetLevel() >= spell_list->entries[i].minlevel && GetLevel() <= spell_list->entries[i].maxlevel && spell_list->entries[i].spellid > 0) {
|
||||
AddSpellToNPCList(spell_list->entries[i].priority,
|
||||
@ -2371,9 +2434,30 @@ bool NPC::AI_AddNPCSpells(uint32 iDBSpellsID) {
|
||||
}
|
||||
std::sort(AIspells.begin(), AIspells.end(), Compare_AI_Spells);
|
||||
|
||||
if (attack_proc_spell > 0)
|
||||
if (IsValidSpell(attack_proc_spell))
|
||||
AddProcToWeapon(attack_proc_spell, true, proc_chance);
|
||||
|
||||
if (IsValidSpell(range_proc_spell))
|
||||
AddRangedProc(range_proc_spell, (rproc_chance + 100));
|
||||
|
||||
if (IsValidSpell(defensive_proc_spell))
|
||||
AddDefensiveProc(defensive_proc_spell, (dproc_chance + 100));
|
||||
|
||||
//Set AI casting variables
|
||||
|
||||
AISpellVar.fail_recast = (_fail_recast) ? _fail_recast : RuleI(Spells, AI_SpellCastFinishedFailRecast);
|
||||
AISpellVar.engaged_no_sp_recast_min = (_engaged_no_sp_recast_min) ? _engaged_no_sp_recast_min : RuleI(Spells, AI_EngagedNoSpellMinRecast);
|
||||
AISpellVar.engaged_no_sp_recast_max = (_engaged_no_sp_recast_max) ? _engaged_no_sp_recast_max : RuleI(Spells, AI_EngagedNoSpellMaxRecast);
|
||||
AISpellVar.engaged_beneficial_self_chance = (_engaged_beneficial_self_chance) ? _engaged_beneficial_self_chance : RuleI(Spells, AI_EngagedBeneficialSelfChance);
|
||||
AISpellVar.engaged_beneficial_other_chance = (_engaged_beneficial_other_chance) ? _engaged_beneficial_other_chance : RuleI(Spells, AI_EngagedBeneficialOtherChance);
|
||||
AISpellVar.engaged_detrimental_chance = (_engaged_detrimental_chance) ? _engaged_detrimental_chance : RuleI(Spells, AI_EngagedDetrimentalChance);
|
||||
AISpellVar.pursue_no_sp_recast_min = (_pursue_no_sp_recast_min) ? _pursue_no_sp_recast_min : RuleI(Spells, AI_PursueNoSpellMinRecast);
|
||||
AISpellVar.pursue_no_sp_recast_max = (_pursue_no_sp_recast_max) ? _pursue_no_sp_recast_max : RuleI(Spells, AI_PursueNoSpellMaxRecast);
|
||||
AISpellVar.pursue_detrimental_chance = (_pursue_detrimental_chance) ? _pursue_detrimental_chance : RuleI(Spells, AI_PursueDetrimentalChance);
|
||||
AISpellVar.idle_no_sp_recast_min = (_idle_no_sp_recast_min) ? _idle_no_sp_recast_min : RuleI(Spells, AI_IdleNoSpellMinRecast);
|
||||
AISpellVar.idle_no_sp_recast_max = (_idle_no_sp_recast_max) ? _idle_no_sp_recast_max : RuleI(Spells, AI_IdleNoSpellMaxRecast);
|
||||
AISpellVar.idle_beneficial_chance = (_idle_beneficial_chance) ? _idle_beneficial_chance : RuleI(Spells, AI_IdleBeneficialChance);
|
||||
|
||||
if (AIspells.size() == 0)
|
||||
AIautocastspell_timer->Disable();
|
||||
else
|
||||
@ -2568,13 +2652,29 @@ DBnpcspells_Struct* ZoneDatabase::GetNPCSpells(uint32 iDBSpellsID) {
|
||||
MYSQL_RES *result;
|
||||
MYSQL_ROW row;
|
||||
|
||||
if (RunQuery(query, MakeAnyLenString(&query, "SELECT id, parent_list, attack_proc, proc_chance from npc_spells where id=%d", iDBSpellsID), errbuf, &result)) {
|
||||
if (RunQuery(query, MakeAnyLenString(&query, "SELECT id, parent_list, attack_proc, proc_chance, range_proc, rproc_chance, defensive_proc, dproc_chance, fail_recast, engaged_no_sp_recast_min, engaged_no_sp_recast_max, engaged_b_self_chance, engaged_b_other_chance, engaged_d_chance, pursue_no_sp_recast_min, pursue_no_sp_recast_max, pursue_d_chance, idle_no_sp_recast_min, idle_no_sp_recast_max, idle_b_chance from npc_spells where id=%d", iDBSpellsID), errbuf, &result)) {
|
||||
safe_delete_array(query);
|
||||
if (mysql_num_rows(result) == 1) {
|
||||
row = mysql_fetch_row(result);
|
||||
uint32 tmpparent_list = atoi(row[1]);
|
||||
int16 tmpattack_proc = atoi(row[2]);
|
||||
uint16 tmpattack_proc = atoi(row[2]);
|
||||
uint8 tmpproc_chance = atoi(row[3]);
|
||||
uint16 tmprange_proc = atoi(row[4]);
|
||||
int16 tmprproc_chance = atoi(row[5]);
|
||||
uint16 tmpdefensive_proc = atoi(row[6]);
|
||||
int16 tmpdproc_chance = atoi(row[7]);
|
||||
uint32 tmppfail_recast = atoi(row[8]);
|
||||
uint32 tmpengaged_no_sp_recast_min = atoi(row[9]);
|
||||
uint32 tmpengaged_no_sp_recast_max = atoi(row[10]);
|
||||
uint8 tmpengaged_b_self_chance = atoi(row[11]);
|
||||
uint8 tmpengaged_b_other_chance = atoi(row[12]);
|
||||
uint8 tmpengaged_d_chance = atoi(row[13]);
|
||||
uint32 tmppursue_no_sp_recast_min = atoi(row[14]);
|
||||
uint32 tmppursue_no_sp_recast_max = atoi(row[15]);
|
||||
uint8 tmppursue_d_chance = atoi(row[16]);
|
||||
uint32 tmpidle_no_sp_recast_min = atoi(row[17]);
|
||||
uint32 tmpidle_no_sp_recast_max = atoi(row[18]);
|
||||
uint8 tmpidle_b_chance = atoi(row[19]);
|
||||
mysql_free_result(result);
|
||||
if (RunQuery(query, MakeAnyLenString(&query, "SELECT spellid, type, minlevel, maxlevel, manacost, recast_delay, priority, resist_adjust from npc_spells_entries where npc_spells_id=%d ORDER BY minlevel", iDBSpellsID), errbuf, &result)) {
|
||||
safe_delete_array(query);
|
||||
@ -2584,6 +2684,22 @@ DBnpcspells_Struct* ZoneDatabase::GetNPCSpells(uint32 iDBSpellsID) {
|
||||
npc_spells_cache[iDBSpellsID]->parent_list = tmpparent_list;
|
||||
npc_spells_cache[iDBSpellsID]->attack_proc = tmpattack_proc;
|
||||
npc_spells_cache[iDBSpellsID]->proc_chance = tmpproc_chance;
|
||||
npc_spells_cache[iDBSpellsID]->range_proc = tmprange_proc;
|
||||
npc_spells_cache[iDBSpellsID]->rproc_chance = tmpdproc_chance;
|
||||
npc_spells_cache[iDBSpellsID]->defensive_proc = tmpdefensive_proc;
|
||||
npc_spells_cache[iDBSpellsID]->dproc_chance = tmpdproc_chance;
|
||||
npc_spells_cache[iDBSpellsID]->fail_recast = tmppfail_recast;
|
||||
npc_spells_cache[iDBSpellsID]->engaged_no_sp_recast_min = tmpengaged_no_sp_recast_min;
|
||||
npc_spells_cache[iDBSpellsID]->engaged_no_sp_recast_max = tmpengaged_no_sp_recast_max;
|
||||
npc_spells_cache[iDBSpellsID]->engaged_beneficial_self_chance = tmpengaged_b_self_chance;
|
||||
npc_spells_cache[iDBSpellsID]->engaged_beneficial_other_chance = tmpengaged_b_other_chance;
|
||||
npc_spells_cache[iDBSpellsID]->engaged_detrimental_chance = tmpengaged_d_chance;
|
||||
npc_spells_cache[iDBSpellsID]->pursue_no_sp_recast_min = tmppursue_no_sp_recast_min;
|
||||
npc_spells_cache[iDBSpellsID]->pursue_no_sp_recast_max = tmppursue_no_sp_recast_max;
|
||||
npc_spells_cache[iDBSpellsID]->pursue_detrimental_chance = tmppursue_d_chance;
|
||||
npc_spells_cache[iDBSpellsID]->idle_no_sp_recast_min = tmpidle_no_sp_recast_min;
|
||||
npc_spells_cache[iDBSpellsID]->idle_no_sp_recast_max = tmpidle_no_sp_recast_max;
|
||||
npc_spells_cache[iDBSpellsID]->idle_beneficial_chance = tmpidle_b_chance;
|
||||
npc_spells_cache[iDBSpellsID]->numentries = mysql_num_rows(result);
|
||||
int j = 0;
|
||||
while ((row = mysql_fetch_row(result))) {
|
||||
|
||||
289
zone/attack.cpp
289
zone/attack.cpp
@ -202,7 +202,8 @@ bool Mob::CheckHitChance(Mob* other, SkillUseTypes skillinuse, int Hand, int16 c
|
||||
if (chance_mod >= 10000)
|
||||
return true;
|
||||
|
||||
float bonus;
|
||||
float avoidanceBonus = 0;
|
||||
float hitBonus = 0;
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
// To hit calcs go here
|
||||
@ -214,6 +215,7 @@ bool Mob::CheckHitChance(Mob* other, SkillUseTypes skillinuse, int Hand, int16 c
|
||||
//Calculate the level difference
|
||||
|
||||
mlog(COMBAT__TOHIT, "Chance to hit before level diff calc %.2f", chancetohit);
|
||||
|
||||
double level_difference = attacker_level - defender_level;
|
||||
double range = defender->GetLevel();
|
||||
range = ((range / 4) + 3);
|
||||
@ -268,37 +270,32 @@ bool Mob::CheckHitChance(Mob* other, SkillUseTypes skillinuse, int Hand, int16 c
|
||||
mlog(COMBAT__TOHIT, "Applied item melee skill bonus %d, yeilding %.2f", attacker->spellbonuses.MeleeSkillCheck, chancetohit);
|
||||
}
|
||||
|
||||
//subtract off avoidance by the defender. (Live AA - Combat Agility)
|
||||
bonus = defender->spellbonuses.AvoidMeleeChance + defender->itembonuses.AvoidMeleeChance + (defender->aabonuses.AvoidMeleeChance * 10);
|
||||
//Avoidance Bonuses on defender decreases baseline hit chance by percent.
|
||||
avoidanceBonus = defender->spellbonuses.AvoidMeleeChanceEffect +
|
||||
defender->itembonuses.AvoidMeleeChanceEffect +
|
||||
defender->aabonuses.AvoidMeleeChanceEffect +
|
||||
(defender->itembonuses.AvoidMeleeChance / 10.0f); //Item Mod 'Avoidence'
|
||||
|
||||
//AA Live - Elemental Agility
|
||||
if (IsPet()) {
|
||||
Mob *owner = defender->GetOwner();
|
||||
if (!owner)return false;
|
||||
bonus += (owner->aabonuses.PetAvoidance + owner->spellbonuses.PetAvoidance + owner->itembonuses.PetAvoidance)*10;
|
||||
}
|
||||
Mob *owner = nullptr;
|
||||
if (defender->IsPet())
|
||||
owner = defender->GetOwner();
|
||||
else if ((defender->IsNPC() && defender->CastToNPC()->GetSwarmOwner()))
|
||||
owner = entity_list.GetMobID(defender->CastToNPC()->GetSwarmOwner());
|
||||
|
||||
if (owner)
|
||||
avoidanceBonus += owner->aabonuses.PetAvoidance + owner->spellbonuses.PetAvoidance + owner->itembonuses.PetAvoidance;
|
||||
|
||||
if(bonus > 0) {
|
||||
chancetohit -= ((bonus * chancetohit) / 1000);
|
||||
mlog(COMBAT__TOHIT, "Applied avoidance chance %.2f/10, yeilding %.2f", bonus, chancetohit);
|
||||
}
|
||||
|
||||
if(attacker->IsNPC())
|
||||
chancetohit += (chancetohit * attacker->CastToNPC()->GetAccuracyRating() / 1000);
|
||||
|
||||
mlog(COMBAT__TOHIT, "Chance to hit after accuracy rating calc %.2f", chancetohit);
|
||||
|
||||
float hitBonus = 0;
|
||||
|
||||
/*
|
||||
Kayen: Unknown if the HitChance and Accuracy effect's should modify 'chancetohit'
|
||||
cumulatively or successively. For now all hitBonuses are cumulative.
|
||||
*/
|
||||
if(defender->IsNPC())
|
||||
avoidanceBonus += (defender->CastToNPC()->GetAvoidanceRating() / 10.0f); //Modifier from database
|
||||
|
||||
//Hit Chance Bonuses on attacker increases baseline hit chance by percent.
|
||||
hitBonus += attacker->itembonuses.HitChanceEffect[skillinuse] +
|
||||
attacker->spellbonuses.HitChanceEffect[skillinuse]+
|
||||
attacker->aabonuses.HitChanceEffect[skillinuse]+
|
||||
attacker->itembonuses.HitChanceEffect[HIGHEST_SKILL+1] +
|
||||
attacker->spellbonuses.HitChanceEffect[HIGHEST_SKILL+1];
|
||||
attacker->spellbonuses.HitChanceEffect[HIGHEST_SKILL+1] +
|
||||
attacker->aabonuses.HitChanceEffect[HIGHEST_SKILL+1];
|
||||
|
||||
|
||||
//Accuracy = Spell Effect , HitChance = 'Accuracy' from Item Effect
|
||||
//Only AA derived accuracy can be skill limited. ie (Precision of the Pathfinder, Dead Aim)
|
||||
@ -306,26 +303,31 @@ bool Mob::CheckHitChance(Mob* other, SkillUseTypes skillinuse, int Hand, int16 c
|
||||
attacker->spellbonuses.Accuracy[HIGHEST_SKILL+1] +
|
||||
attacker->aabonuses.Accuracy[HIGHEST_SKILL+1] +
|
||||
attacker->aabonuses.Accuracy[skillinuse] +
|
||||
attacker->itembonuses.HitChance) / 15.0f;
|
||||
attacker->itembonuses.HitChance) / 15.0f; //Item Mod 'Accuracy'
|
||||
|
||||
hitBonus += chance_mod; //Modifier applied from casted/disc skill attacks.
|
||||
|
||||
chancetohit += ((chancetohit * hitBonus) / 100.0f);
|
||||
|
||||
if(attacker->IsNPC())
|
||||
hitBonus += (attacker->CastToNPC()->GetAccuracyRating() / 10.0f); //Modifier from database
|
||||
|
||||
if(skillinuse == SkillArchery)
|
||||
chancetohit -= (chancetohit * RuleR(Combat, ArcheryHitPenalty)) / 100.0f;
|
||||
hitBonus -= hitBonus*(RuleR(Combat, ArcheryHitPenalty)*100.0f);
|
||||
|
||||
//Calculate final chance to hit
|
||||
chancetohit += ((chancetohit * (hitBonus - avoidanceBonus)) / 100.0f);
|
||||
mlog(COMBAT__TOHIT, "Chance to hit %.2f after accuracy calc %.2f and avoidance calc %.2f", chancetohit, hitBonus, avoidanceBonus);
|
||||
|
||||
chancetohit = mod_hit_chance(chancetohit, skillinuse, attacker);
|
||||
|
||||
// Chance to hit; Max 95%, Min 30%
|
||||
// Chance to hit; Max 95%, Min 5% DEFAULTS
|
||||
if(chancetohit > 1000 || chancetohit < -1000) {
|
||||
//if chance to hit is crazy high, that means a discipline is in use, and let it stay there
|
||||
}
|
||||
else if(chancetohit > 95) {
|
||||
chancetohit = 95;
|
||||
else if(chancetohit > RuleR(Combat,MaxChancetoHit)) {
|
||||
chancetohit = RuleR(Combat,MaxChancetoHit);
|
||||
}
|
||||
else if(chancetohit < 5) {
|
||||
chancetohit = 5;
|
||||
else if(chancetohit < RuleR(Combat,MinChancetoHit)) {
|
||||
chancetohit = RuleR(Combat,MinChancetoHit);
|
||||
}
|
||||
|
||||
//I dont know the best way to handle a garunteed hit discipline being used
|
||||
@ -564,15 +566,15 @@ void Mob::MeleeMitigation(Mob *attacker, int32 &damage, int32 minhit, ExtraAttac
|
||||
|
||||
if (!IsPet())
|
||||
armor = (armor / RuleR(Combat, NPCACFactor));
|
||||
else{
|
||||
Mob *owner = nullptr;
|
||||
|
||||
Mob *owner = nullptr;
|
||||
if (IsPet())
|
||||
owner = GetOwner();
|
||||
if (owner){
|
||||
PetACBonus = owner->aabonuses.PetMeleeMitigation
|
||||
+ owner->itembonuses.PetMeleeMitigation +
|
||||
owner->spellbonuses.PetMeleeMitigation;
|
||||
}
|
||||
}
|
||||
else if ((CastToNPC()->GetSwarmOwner()))
|
||||
owner = entity_list.GetMobID(CastToNPC()->GetSwarmOwner());
|
||||
|
||||
if (owner)
|
||||
PetACBonus = owner->aabonuses.PetMeleeMitigation + owner->itembonuses.PetMeleeMitigation + owner->spellbonuses.PetMeleeMitigation;
|
||||
|
||||
armor += spellbonuses.AC + itembonuses.AC + PetACBonus + 1;
|
||||
}
|
||||
@ -713,7 +715,7 @@ void Mob::MeleeMitigation(Mob *attacker, int32 &damage, int32 minhit, ExtraAttac
|
||||
//reduce the damage from shielding item and aa based on the min dmg
|
||||
//spells offer pure mitigation
|
||||
damage -= (minhit * defender->itembonuses.MeleeMitigation / 100);
|
||||
damage -= (damage * defender->spellbonuses.MeleeMitigation / 100);
|
||||
damage -= (damage * (defender->spellbonuses.MeleeMitigationEffect + defender->itembonuses.MeleeMitigationEffect + defender->aabonuses.MeleeMitigationEffect) / 100);
|
||||
}
|
||||
|
||||
if (damage < 0)
|
||||
@ -755,7 +757,7 @@ int32 Mob::GetMeleeMitDmg(Mob *attacker, int32 damage, int32 minhit,
|
||||
damage -= ((int)d * interval);
|
||||
|
||||
damage -= (minhit * itembonuses.MeleeMitigation / 100);
|
||||
damage -= (damage * spellbonuses.MeleeMitigation / 100);
|
||||
damage -= (damage * (spellbonuses.MeleeMitigationEffect + itembonuses.MeleeMitigationEffect + aabonuses.MeleeMitigationEffect) / 100);
|
||||
return damage;
|
||||
}
|
||||
|
||||
@ -769,7 +771,7 @@ int32 Client::GetMeleeMitDmg(Mob *attacker, int32 damage, int32 minhit,
|
||||
// floats for the rounding issues
|
||||
float dmg_interval = (damage - minhit) / 19.0;
|
||||
float dmg_bonus = minhit - dmg_interval;
|
||||
float spellMeleeMit = spellbonuses.MeleeMitigation / 100.0;
|
||||
float spellMeleeMit = (spellbonuses.MeleeMitigationEffect + itembonuses.MeleeMitigationEffect + aabonuses.MeleeMitigationEffect) / 100.0;
|
||||
if (GetClass() == WARRIOR)
|
||||
spellMeleeMit += 0.05;
|
||||
dmg_bonus -= dmg_bonus * (itembonuses.MeleeMitigation / 100.0);
|
||||
@ -1297,11 +1299,9 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b
|
||||
} else { //we hit, try to avoid it
|
||||
other->AvoidDamage(this, damage);
|
||||
other->MeleeMitigation(this, damage, min_hit, opts);
|
||||
if(damage > 0) {
|
||||
ApplyMeleeDamageBonus(skillinuse, damage);
|
||||
damage += (itembonuses.HeroicSTR / 10) + (damage * other->GetSkillDmgTaken(skillinuse) / 100) + GetSkillDmgAmt(skillinuse);
|
||||
TryCriticalHit(other, skillinuse, damage, opts);
|
||||
}
|
||||
if(damage > 0)
|
||||
CommonOutgoingHitSuccess(other, damage, skillinuse);
|
||||
|
||||
mlog(COMBAT__DAMAGE, "Final damage after all reductions: %d", damage);
|
||||
}
|
||||
|
||||
@ -1365,39 +1365,7 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b
|
||||
TrySkillProc(other, skillinuse, 0, true, Hand);
|
||||
}
|
||||
|
||||
//break invis when you attack
|
||||
if(invisible) {
|
||||
mlog(COMBAT__ATTACKS, "Removing invisibility due to melee attack.");
|
||||
BuffFadeByEffect(SE_Invisibility);
|
||||
BuffFadeByEffect(SE_Invisibility2);
|
||||
invisible = false;
|
||||
}
|
||||
if(invisible_undead) {
|
||||
mlog(COMBAT__ATTACKS, "Removing invisibility vs. undead due to melee attack.");
|
||||
BuffFadeByEffect(SE_InvisVsUndead);
|
||||
BuffFadeByEffect(SE_InvisVsUndead2);
|
||||
invisible_undead = false;
|
||||
}
|
||||
if(invisible_animals){
|
||||
mlog(COMBAT__ATTACKS, "Removing invisibility vs. animals due to melee attack.");
|
||||
BuffFadeByEffect(SE_InvisVsAnimals);
|
||||
invisible_animals = false;
|
||||
}
|
||||
|
||||
if (spellbonuses.NegateIfCombat)
|
||||
BuffFadeByEffect(SE_NegateIfCombat);
|
||||
|
||||
if(hidden || improved_hidden){
|
||||
hidden = false;
|
||||
improved_hidden = false;
|
||||
EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct));
|
||||
SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer;
|
||||
sa_out->spawn_id = GetID();
|
||||
sa_out->type = 0x03;
|
||||
sa_out->parameter = 0;
|
||||
entity_list.QueueClients(this, outapp, true);
|
||||
safe_delete(outapp);
|
||||
}
|
||||
CommonBreakInvisible();
|
||||
|
||||
if(GetTarget())
|
||||
TriggerDefensiveProcs(weapon, other, Hand, damage);
|
||||
@ -1933,16 +1901,12 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool
|
||||
other->AvoidDamage(this, damage);
|
||||
other->MeleeMitigation(this, damage, min_dmg+eleBane, opts);
|
||||
if(damage > 0) {
|
||||
ApplyMeleeDamageBonus(skillinuse, damage);
|
||||
damage += (itembonuses.HeroicSTR / 10) + (damage * other->GetSkillDmgTaken(skillinuse) / 100) + GetSkillDmgAmt(skillinuse);
|
||||
TryCriticalHit(other, skillinuse, damage, opts);
|
||||
CommonOutgoingHitSuccess(other, damage, skillinuse);
|
||||
}
|
||||
mlog(COMBAT__HITS, "Generating hate %d towards %s", hate, GetName());
|
||||
// now add done damage to the hate list
|
||||
if(damage > 0)
|
||||
{
|
||||
other->AddToHateList(this, hate);
|
||||
}
|
||||
else
|
||||
other->AddToHateList(this, 0);
|
||||
}
|
||||
@ -1964,10 +1928,7 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool
|
||||
return false;
|
||||
}
|
||||
|
||||
int16 DeathHP = 0;
|
||||
DeathHP = other->GetDelayDeath() * -1;
|
||||
|
||||
if(GetHP() > 0 && other->GetHP() >= DeathHP) {
|
||||
if(GetHP() > 0 && !other->HasDied()) {
|
||||
other->Damage(this, damage, SPELL_UNKNOWN, skillinuse, false); // Not avoidable client already had thier chance to Avoid
|
||||
} else
|
||||
return false;
|
||||
@ -1977,59 +1938,24 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool
|
||||
|
||||
MeleeLifeTap(damage);
|
||||
|
||||
if (damage > 0)
|
||||
CheckNumHitsRemaining(NUMHIT_OutgoingHitSuccess);
|
||||
|
||||
//break invis when you attack
|
||||
if(invisible) {
|
||||
mlog(COMBAT__ATTACKS, "Removing invisibility due to melee attack.");
|
||||
BuffFadeByEffect(SE_Invisibility);
|
||||
BuffFadeByEffect(SE_Invisibility2);
|
||||
invisible = false;
|
||||
}
|
||||
if(invisible_undead) {
|
||||
mlog(COMBAT__ATTACKS, "Removing invisibility vs. undead due to melee attack.");
|
||||
BuffFadeByEffect(SE_InvisVsUndead);
|
||||
BuffFadeByEffect(SE_InvisVsUndead2);
|
||||
invisible_undead = false;
|
||||
}
|
||||
if(invisible_animals){
|
||||
mlog(COMBAT__ATTACKS, "Removing invisibility vs. animals due to melee attack.");
|
||||
BuffFadeByEffect(SE_InvisVsAnimals);
|
||||
invisible_animals = false;
|
||||
}
|
||||
|
||||
if (spellbonuses.NegateIfCombat)
|
||||
BuffFadeByEffect(SE_NegateIfCombat);
|
||||
|
||||
if(hidden || improved_hidden)
|
||||
{
|
||||
EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct));
|
||||
SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer;
|
||||
sa_out->spawn_id = GetID();
|
||||
sa_out->type = 0x03;
|
||||
sa_out->parameter = 0;
|
||||
entity_list.QueueClients(this, outapp, true);
|
||||
safe_delete(outapp);
|
||||
}
|
||||
|
||||
|
||||
hidden = false;
|
||||
improved_hidden = false;
|
||||
CommonBreakInvisible();
|
||||
|
||||
//I doubt this works...
|
||||
if (!GetTarget())
|
||||
return true; //We killed them
|
||||
|
||||
if(!bRiposte && other && other->GetHP() > 0) {
|
||||
if(!bRiposte && !other->HasDied()) {
|
||||
TryWeaponProc(nullptr, weapon, other, Hand); //no weapon
|
||||
TrySpellProc(nullptr, weapon, other, Hand);
|
||||
|
||||
if (damage > 0 && HasSkillProcSuccess())
|
||||
if (!other->HasDied())
|
||||
TrySpellProc(nullptr, weapon, other, Hand);
|
||||
|
||||
if (damage > 0 && HasSkillProcSuccess() && !other->HasDied())
|
||||
TrySkillProc(other, skillinuse, 0, true, Hand);
|
||||
}
|
||||
|
||||
TriggerDefensiveProcs(nullptr, other, Hand, damage);
|
||||
if(GetHP() > 0 && !other->HasDied())
|
||||
TriggerDefensiveProcs(nullptr, other, Hand, damage);
|
||||
|
||||
// now check ripostes
|
||||
if (damage == -3) { // riposting
|
||||
@ -3923,12 +3849,12 @@ void Mob::HealDamage(uint32 amount, Mob *caster, uint16 spell_id)
|
||||
}
|
||||
|
||||
//proc chance includes proc bonus
|
||||
float Mob::GetProcChances(float ProcBonus, uint16 weapon_speed, uint16 hand)
|
||||
float Mob::GetProcChances(float ProcBonus, uint16 hand)
|
||||
{
|
||||
int mydex = GetDEX();
|
||||
float ProcChance = 0.0f;
|
||||
|
||||
weapon_speed = GetWeaponSpeedbyHand(hand);
|
||||
uint16 weapon_speed = GetWeaponSpeedbyHand(hand);
|
||||
|
||||
if (RuleB(Combat, AdjustProcPerMinute)) {
|
||||
ProcChance = (static_cast<float>(weapon_speed) *
|
||||
@ -3945,12 +3871,16 @@ float Mob::GetProcChances(float ProcBonus, uint16 weapon_speed, uint16 hand)
|
||||
return ProcChance;
|
||||
}
|
||||
|
||||
float Mob::GetDefensiveProcChances(float &ProcBonus, float &ProcChance, uint16 weapon_speed, uint16 hand) {
|
||||
int myagi = GetAGI();
|
||||
float Mob::GetDefensiveProcChances(float &ProcBonus, float &ProcChance, uint16 hand, Mob* on) {
|
||||
|
||||
if (!on)
|
||||
return ProcChance;
|
||||
|
||||
int myagi = on->GetAGI();
|
||||
ProcBonus = 0;
|
||||
ProcChance = 0;
|
||||
|
||||
weapon_speed = GetWeaponSpeedbyHand(hand);
|
||||
uint16 weapon_speed = GetWeaponSpeedbyHand(hand);
|
||||
|
||||
ProcChance = (static_cast<float>(weapon_speed) * RuleR(Combat, AvgDefProcsPerMinute) / 60000.0f); // compensate for weapon_speed being in ms
|
||||
ProcBonus += static_cast<float>(myagi) * RuleR(Combat, DefProcPerMinAgiContrib) / 100.0f;
|
||||
@ -3960,7 +3890,7 @@ float Mob::GetDefensiveProcChances(float &ProcBonus, float &ProcChance, uint16 w
|
||||
return ProcChance;
|
||||
}
|
||||
|
||||
void Mob::TryDefensiveProc(const ItemInst* weapon, Mob *on, uint16 hand, int damage) {
|
||||
void Mob::TryDefensiveProc(const ItemInst* weapon, Mob *on, uint16 hand) {
|
||||
|
||||
if (!on) {
|
||||
SetTarget(nullptr);
|
||||
@ -3974,10 +3904,8 @@ void Mob::TryDefensiveProc(const ItemInst* weapon, Mob *on, uint16 hand, int dam
|
||||
return;
|
||||
|
||||
float ProcChance, ProcBonus;
|
||||
if(weapon!=nullptr)
|
||||
on->GetDefensiveProcChances(ProcBonus, ProcChance, weapon->GetItem()->Delay, hand);
|
||||
else
|
||||
on->GetDefensiveProcChances(ProcBonus, ProcChance);
|
||||
on->GetDefensiveProcChances(ProcBonus, ProcChance, hand , this);
|
||||
|
||||
if(hand != 13)
|
||||
ProcChance /= 2;
|
||||
|
||||
@ -4035,7 +3963,7 @@ void Mob::TryWeaponProc(const ItemInst *inst, const Item_Struct *weapon, Mob *on
|
||||
float ProcBonus = static_cast<float>(aabonuses.ProcChanceSPA +
|
||||
spellbonuses.ProcChanceSPA + itembonuses.ProcChanceSPA);
|
||||
ProcBonus += static_cast<float>(itembonuses.ProcChance) / 10.0f; // Combat Effects
|
||||
float ProcChance = GetProcChances(ProcBonus, weapon->Delay, hand);
|
||||
float ProcChance = GetProcChances(ProcBonus, hand);
|
||||
|
||||
if (hand != 13) //Is Archery intened to proc at 50% rate?
|
||||
ProcChance /= 2;
|
||||
@ -4113,10 +4041,7 @@ void Mob::TrySpellProc(const ItemInst *inst, const Item_Struct *weapon, Mob *on,
|
||||
float ProcBonus = static_cast<float>(spellbonuses.SpellProcChance +
|
||||
itembonuses.SpellProcChance + aabonuses.SpellProcChance);
|
||||
float ProcChance = 0.0f;
|
||||
if (weapon)
|
||||
ProcChance = GetProcChances(ProcBonus, weapon->Delay, hand);
|
||||
else
|
||||
ProcChance = GetProcChances(ProcBonus);
|
||||
ProcChance = GetProcChances(ProcBonus, hand);
|
||||
|
||||
if (hand != MainPrimary) //Is Archery intened to proc at 50% rate?
|
||||
ProcChance /= 2;
|
||||
@ -4130,6 +4055,9 @@ void Mob::TrySpellProc(const ItemInst *inst, const Item_Struct *weapon, Mob *on,
|
||||
rangedattk = true;
|
||||
}
|
||||
|
||||
if (!weapon && hand == 11 && GetSpecialAbility(SPECATK_RANGED_ATK))
|
||||
rangedattk = true;
|
||||
|
||||
for (uint32 i = 0; i < MAX_PROCS; i++) {
|
||||
if (IsPet() && hand != MainPrimary) //Pets can only proc spell procs from their primay hand (ie; beastlord pets)
|
||||
continue; // If pets ever can proc from off hand, this will need to change
|
||||
@ -4152,7 +4080,7 @@ void Mob::TrySpellProc(const ItemInst *inst, const Item_Struct *weapon, Mob *on,
|
||||
|
||||
// Spell procs (buffs)
|
||||
if (SpellProcs[i].spellID != SPELL_UNKNOWN) {
|
||||
float chance = ProcChance * (SpellProcs[i].chance / 100.0f);
|
||||
float chance = ProcChance * (static_cast<float>(SpellProcs[i].chance) / 100.0f);
|
||||
if (MakeRandomFloat(0, 1) <= chance) {
|
||||
mlog(COMBAT__PROCS,
|
||||
"Spell proc %d procing spell %d (%.2f percent chance)",
|
||||
@ -4168,8 +4096,8 @@ void Mob::TrySpellProc(const ItemInst *inst, const Item_Struct *weapon, Mob *on,
|
||||
} else if (rangedattk) { // ranged only
|
||||
// ranged spell procs (buffs)
|
||||
if (RangedProcs[i].spellID != SPELL_UNKNOWN) {
|
||||
float chance = ProcChance * (RangedProcs[i].chance / 100.0f);
|
||||
if (MakeRandomFloat(0, 1) <= chance) {
|
||||
float chance = ProcChance * (static_cast<float>(RangedProcs[i].chance) / 100.0f);
|
||||
if (MakeRandomFloat(0, 1) <= chance) {
|
||||
mlog(COMBAT__PROCS,
|
||||
"Ranged proc %d procing spell %d (%.2f percent chance)",
|
||||
i, RangedProcs[i].spellID, chance);
|
||||
@ -4820,3 +4748,56 @@ int32 Mob::RuneAbsorb(int32 damage, uint16 type)
|
||||
return damage;
|
||||
}
|
||||
|
||||
void Mob::CommonOutgoingHitSuccess(Mob* defender, int32 &damage, SkillUseTypes skillInUse)
|
||||
{
|
||||
if (!defender)
|
||||
return;
|
||||
|
||||
ApplyMeleeDamageBonus(skillInUse, damage);
|
||||
damage += (damage * defender->GetSkillDmgTaken(skillInUse) / 100) + (GetSkillDmgAmt(skillInUse) + defender->GetFcDamageAmtIncoming(this, 0, true, skillInUse));
|
||||
TryCriticalHit(defender, skillInUse, damage);
|
||||
CheckNumHitsRemaining(NUMHIT_OutgoingHitSuccess);
|
||||
}
|
||||
|
||||
void Mob::CommonBreakInvisible()
|
||||
{
|
||||
//break invis when you attack
|
||||
if(invisible) {
|
||||
mlog(COMBAT__ATTACKS, "Removing invisibility due to melee attack.");
|
||||
BuffFadeByEffect(SE_Invisibility);
|
||||
BuffFadeByEffect(SE_Invisibility2);
|
||||
invisible = false;
|
||||
}
|
||||
if(invisible_undead) {
|
||||
mlog(COMBAT__ATTACKS, "Removing invisibility vs. undead due to melee attack.");
|
||||
BuffFadeByEffect(SE_InvisVsUndead);
|
||||
BuffFadeByEffect(SE_InvisVsUndead2);
|
||||
invisible_undead = false;
|
||||
}
|
||||
if(invisible_animals){
|
||||
mlog(COMBAT__ATTACKS, "Removing invisibility vs. animals due to melee attack.");
|
||||
BuffFadeByEffect(SE_InvisVsAnimals);
|
||||
invisible_animals = false;
|
||||
}
|
||||
|
||||
if (spellbonuses.NegateIfCombat)
|
||||
BuffFadeByEffect(SE_NegateIfCombat);
|
||||
|
||||
if(hidden || improved_hidden){
|
||||
hidden = false;
|
||||
improved_hidden = false;
|
||||
EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct));
|
||||
SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer;
|
||||
sa_out->spawn_id = GetID();
|
||||
sa_out->type = 0x03;
|
||||
sa_out->parameter = 0;
|
||||
entity_list.QueueClients(this, outapp, true);
|
||||
safe_delete(outapp);
|
||||
}
|
||||
|
||||
if (spellbonuses.NegateIfCombat)
|
||||
BuffFadeByEffect(SE_NegateIfCombat);
|
||||
|
||||
hidden = false;
|
||||
improved_hidden = false;
|
||||
}
|
||||
|
||||
@ -879,7 +879,7 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon)
|
||||
newbon->PetMaxHP += base1;
|
||||
break;
|
||||
case SE_AvoidMeleeChance:
|
||||
newbon->AvoidMeleeChance += base1;
|
||||
newbon->AvoidMeleeChanceEffect += base1;
|
||||
break;
|
||||
case SE_CombatStability:
|
||||
newbon->CombatStability += base1;
|
||||
@ -974,7 +974,7 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon)
|
||||
newbon->FlurryChance += base1;
|
||||
break;
|
||||
case SE_PetFlurry:
|
||||
newbon->PetFlurry = base1;
|
||||
newbon->PetFlurry += base1;
|
||||
break;
|
||||
case SE_BardSongRange:
|
||||
newbon->SongRange += base1;
|
||||
@ -989,6 +989,14 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon)
|
||||
newbon->CrippBlowChance += base1;
|
||||
break;
|
||||
|
||||
case SE_HitChance:
|
||||
{
|
||||
if(base2 == -1)
|
||||
newbon->HitChanceEffect[HIGHEST_SKILL+1] += base1;
|
||||
else
|
||||
newbon->HitChanceEffect[base2] += base1;
|
||||
}
|
||||
|
||||
case SE_ProcOnKillShot:
|
||||
for(int i = 0; i < MAX_SPELL_TRIGGER*3; i+=3)
|
||||
{
|
||||
@ -1368,6 +1376,10 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon)
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_MeleeMitigation:
|
||||
newbon->MeleeMitigationEffect -= base1;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1788,7 +1800,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
|
||||
case SE_MeleeMitigation:
|
||||
//for some reason... this value is negative for increased mitigation
|
||||
newbon->MeleeMitigation -= effect_value;
|
||||
newbon->MeleeMitigationEffect -= effect_value;
|
||||
break;
|
||||
|
||||
case SE_CriticalHitChance:
|
||||
@ -1833,16 +1845,14 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
|
||||
case SE_AvoidMeleeChance:
|
||||
{
|
||||
//multiplier is to be compatible with item effects, watching for overflow too
|
||||
effect_value = effect_value<3000? effect_value * 10 : 30000;
|
||||
if (RuleB(Spells, AdditiveBonusValues) && item_bonus)
|
||||
newbon->AvoidMeleeChance += effect_value;
|
||||
newbon->AvoidMeleeChanceEffect += effect_value;
|
||||
|
||||
else if((effect_value < 0) && (newbon->AvoidMeleeChance > effect_value))
|
||||
newbon->AvoidMeleeChance = effect_value;
|
||||
else if((effect_value < 0) && (newbon->AvoidMeleeChanceEffect > effect_value))
|
||||
newbon->AvoidMeleeChanceEffect = effect_value;
|
||||
|
||||
else if(newbon->AvoidMeleeChance < effect_value)
|
||||
newbon->AvoidMeleeChance = effect_value;
|
||||
else if(newbon->AvoidMeleeChanceEffect < effect_value)
|
||||
newbon->AvoidMeleeChanceEffect = effect_value;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -3588,9 +3598,9 @@ void Mob::NegateSpellsBonuses(uint16 spell_id)
|
||||
break;
|
||||
|
||||
case SE_MeleeMitigation:
|
||||
spellbonuses.MeleeMitigation = effect_value;
|
||||
itembonuses.MeleeMitigation = effect_value;
|
||||
aabonuses.MeleeMitigation = effect_value;
|
||||
spellbonuses.MeleeMitigationEffect = effect_value;
|
||||
itembonuses.MeleeMitigationEffect = effect_value;
|
||||
aabonuses.MeleeMitigationEffect = effect_value;
|
||||
break;
|
||||
|
||||
case SE_CriticalHitChance:
|
||||
@ -3610,9 +3620,9 @@ void Mob::NegateSpellsBonuses(uint16 spell_id)
|
||||
break;
|
||||
|
||||
case SE_AvoidMeleeChance:
|
||||
spellbonuses.AvoidMeleeChance = effect_value;
|
||||
aabonuses.AvoidMeleeChance = effect_value;
|
||||
itembonuses.AvoidMeleeChance = effect_value;
|
||||
spellbonuses.AvoidMeleeChanceEffect = effect_value;
|
||||
aabonuses.AvoidMeleeChanceEffect = effect_value;
|
||||
itembonuses.AvoidMeleeChanceEffect = effect_value;
|
||||
break;
|
||||
|
||||
case SE_RiposteChance:
|
||||
|
||||
@ -7735,10 +7735,10 @@ int16 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel
|
||||
}
|
||||
|
||||
//proc chance includes proc bonus
|
||||
float Bot::GetProcChances(float ProcBonus, uint16 weapon_speed, uint16 hand) {
|
||||
float Bot::GetProcChances(float ProcBonus, uint16 hand) {
|
||||
int mydex = GetDEX();
|
||||
float ProcChance = 0.0f;
|
||||
|
||||
uint16 weapon_speed = 0;
|
||||
switch (hand) {
|
||||
case MainPrimary:
|
||||
weapon_speed = attack_timer.GetDuration();
|
||||
|
||||
@ -167,7 +167,7 @@ public:
|
||||
uint16 BotGetSpells(int spellslot) { return AIspells[spellslot].spellid; }
|
||||
uint16 BotGetSpellType(int spellslot) { return AIspells[spellslot].type; }
|
||||
uint16 BotGetSpellPriority(int spellslot) { return AIspells[spellslot].priority; }
|
||||
virtual float GetProcChances(float ProcBonus, uint16 weapon_speed, uint16 hand);
|
||||
virtual float GetProcChances(float ProcBonus, uint16 hand);
|
||||
virtual bool AvoidDamage(Mob* other, int32 &damage, bool CanRiposte);
|
||||
virtual int GetMonkHandToHandDamage(void);
|
||||
virtual bool TryFinishingBlow(Mob *defender, SkillUseTypes skillinuse);
|
||||
|
||||
@ -1967,7 +1967,7 @@ int32 Client::CalcEnduranceRegenCap() {
|
||||
|
||||
int Client::GetRawACNoShield(int &shield_ac) const
|
||||
{
|
||||
int ac = itembonuses.AC + spellbonuses.AC;
|
||||
int ac = itembonuses.AC + spellbonuses.AC + aabonuses.AC;
|
||||
shield_ac = 0;
|
||||
const ItemInst *inst = m_inv.GetItem(MainSecondary);
|
||||
if(inst)
|
||||
|
||||
@ -266,6 +266,7 @@ struct StatBonuses {
|
||||
|
||||
int16 StrikeThrough; // PoP: Strike Through %
|
||||
int16 MeleeMitigation; //i = Shielding
|
||||
int16 MeleeMitigationEffect; //i = Spell Effect Melee Mitigation
|
||||
int16 CriticalHitChance[HIGHEST_SKILL+2]; //i
|
||||
int16 CriticalSpellChance; //i
|
||||
int16 SpellCritDmgIncrease; //i
|
||||
@ -275,7 +276,8 @@ struct StatBonuses {
|
||||
int16 CriticalHealOverTime; //i
|
||||
int16 CriticalDoTChance; //i
|
||||
int16 CrippBlowChance; //
|
||||
int16 AvoidMeleeChance; //AvoidMeleeChance/10 == % chance i = Avoidance
|
||||
int16 AvoidMeleeChance; //AvoidMeleeChance/10 == % chance i = Avoidance (item mod)
|
||||
int16 AvoidMeleeChanceEffect; //AvoidMeleeChance Spell Effect
|
||||
int16 RiposteChance; //i
|
||||
int16 DodgeChance; //i
|
||||
int16 ParryChance; //i
|
||||
|
||||
36
zone/mob.cpp
36
zone/mob.cpp
@ -238,9 +238,6 @@ Mob::Mob(const char* in_name,
|
||||
RangedProcs[j].spellID = SPELL_UNKNOWN;
|
||||
RangedProcs[j].chance = 0;
|
||||
RangedProcs[j].base_spellID = SPELL_UNKNOWN;
|
||||
SkillProcs[j].spellID = SPELL_UNKNOWN;
|
||||
SkillProcs[j].chance = 0;
|
||||
SkillProcs[j].base_spellID = SPELL_UNKNOWN;
|
||||
}
|
||||
|
||||
for (i = 0; i < _MaterialCount; i++)
|
||||
@ -3033,7 +3030,7 @@ void Mob::TriggerDefensiveProcs(const ItemInst* weapon, Mob *on, uint16 hand, in
|
||||
if (!on)
|
||||
return;
|
||||
|
||||
on->TryDefensiveProc(weapon, this, hand, damage);
|
||||
on->TryDefensiveProc(weapon, this, hand);
|
||||
|
||||
//Defensive Skill Procs
|
||||
if (damage < 0 && damage >= -4) {
|
||||
@ -4702,6 +4699,11 @@ uint16 Mob::GetSkillByItemType(int ItemType)
|
||||
return Skill2HBlunt;
|
||||
case ItemType2HPiercing:
|
||||
return Skill1HPiercing; // change to 2HPiercing once activated
|
||||
case ItemTypeBow:
|
||||
return SkillArchery;
|
||||
case ItemTypeLargeThrowing:
|
||||
case ItemTypeSmallThrowing:
|
||||
return SkillThrowing;
|
||||
case ItemTypeMartial:
|
||||
return SkillHandtoHand;
|
||||
default:
|
||||
@ -4710,6 +4712,32 @@ uint16 Mob::GetSkillByItemType(int ItemType)
|
||||
return SkillHandtoHand;
|
||||
}
|
||||
|
||||
uint8 Mob::GetItemTypeBySkill(SkillUseTypes skill)
|
||||
{
|
||||
switch (skill)
|
||||
{
|
||||
case SkillThrowing:
|
||||
return ItemTypeSmallThrowing;
|
||||
case SkillArchery:
|
||||
return ItemTypeArrow;
|
||||
case Skill1HSlashing:
|
||||
return ItemType1HSlash;
|
||||
case Skill2HSlashing:
|
||||
return ItemType2HSlash;
|
||||
case Skill1HPiercing:
|
||||
return ItemType1HPiercing;
|
||||
case Skill1HBlunt:
|
||||
return ItemType1HBlunt;
|
||||
case Skill2HBlunt:
|
||||
return ItemType2HBlunt;
|
||||
case SkillHandtoHand:
|
||||
return ItemTypeMartial;
|
||||
default:
|
||||
return ItemTypeMartial;
|
||||
}
|
||||
return ItemTypeMartial;
|
||||
}
|
||||
|
||||
|
||||
bool Mob::PassLimitToSkill(uint16 spell_id, uint16 skill) {
|
||||
|
||||
|
||||
16
zone/mob.h
16
zone/mob.h
@ -150,6 +150,9 @@ public:
|
||||
bool CombatRange(Mob* other);
|
||||
virtual inline bool IsBerserk() { return false; } // only clients
|
||||
void RogueEvade(Mob *other);
|
||||
void CommonOutgoingHitSuccess(Mob* defender, int32 &damage, SkillUseTypes skillInUse);
|
||||
void CommonBreakInvisible();
|
||||
bool HasDied();
|
||||
|
||||
//Appearance
|
||||
void SendLevelAppearance();
|
||||
@ -164,7 +167,7 @@ public:
|
||||
virtual void WearChange(uint8 material_slot, uint16 texture, uint32 color);
|
||||
void DoAnim(const int animnum, int type=0, bool ackreq = true, eqFilterType filter = FilterNone);
|
||||
void ProjectileAnimation(Mob* to, int item_id, bool IsArrow = false, float speed = 0,
|
||||
float angle = 0, float tilt = 0, float arc = 0, const char *IDFile = nullptr);
|
||||
float angle = 0, float tilt = 0, float arc = 0, const char *IDFile = nullptr, SkillUseTypes skillInUse = SkillArchery);
|
||||
void ChangeSize(float in_size, bool bNoRestriction = false);
|
||||
inline uint8 SeeInvisible() const { return see_invis; }
|
||||
inline bool SeeInvisibleUndead() const { return see_invis_undead; }
|
||||
@ -480,6 +483,7 @@ public:
|
||||
static uint32 RandomTimer(int min, int max);
|
||||
static uint8 GetDefaultGender(uint16 in_race, uint8 in_gender = 0xFF);
|
||||
uint16 GetSkillByItemType(int ItemType);
|
||||
uint8 GetItemTypeBySkill(SkillUseTypes skill);
|
||||
virtual void MakePet(uint16 spell_id, const char* pettype, const char *petname = nullptr);
|
||||
virtual void MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower, const char *petname = nullptr, float in_size = 0.0f);
|
||||
bool IsWarriorClass() const;
|
||||
@ -819,7 +823,7 @@ public:
|
||||
void SetNextIncHPEvent( int inchpevent );
|
||||
|
||||
inline bool DivineAura() const { return spellbonuses.DivineAura; }
|
||||
inline bool Sanctuary() const { return spellbonuses.Sanctuary; }
|
||||
inline bool Sanctuary() const { return spellbonuses.Sanctuary; }
|
||||
|
||||
bool HasNPCSpecialAtk(const char* parse);
|
||||
int GetSpecialAbility(int ability);
|
||||
@ -989,13 +993,13 @@ protected:
|
||||
void TrySkillProc(Mob *on, uint16 skill, uint16 ReuseTime, bool Success = false, uint16 hand = 0, bool IsDefensive = false);
|
||||
bool PassLimitToSkill(uint16 spell_id, uint16 skill);
|
||||
bool PassLimitClass(uint32 Classes_, uint16 Class_);
|
||||
void TryDefensiveProc(const ItemInst* weapon, Mob *on, uint16 hand = 13, int damage=0);
|
||||
void TryDefensiveProc(const ItemInst* weapon, Mob *on, uint16 hand = 13);
|
||||
void TryWeaponProc(const ItemInst* inst, const Item_Struct* weapon, Mob *on, uint16 hand = 13);
|
||||
void TrySpellProc(const ItemInst* inst, const Item_Struct* weapon, Mob *on, uint16 hand = 13);
|
||||
void TryWeaponProc(const ItemInst* weapon, Mob *on, uint16 hand = 13);
|
||||
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 GetProcChances(float ProcBonus, uint16 hand = 13);
|
||||
virtual float GetDefensiveProcChances(float &ProcBonus, float &ProcChance, uint16 hand = 13, Mob *on = nullptr);
|
||||
virtual float GetSpecialProcChances(uint16 hand);
|
||||
virtual float GetAssassinateProcChances(uint16 ReuseTime);
|
||||
virtual float GetSkillProcChances(uint16 ReuseTime, uint16 hand = 0);
|
||||
@ -1005,7 +1009,6 @@ protected:
|
||||
int GetKickDamage();
|
||||
int GetBashDamage();
|
||||
virtual void ApplySpecialAttackMod(SkillUseTypes skill, int32 &dmg, int32 &mindmg);
|
||||
bool HasDied();
|
||||
void CalculateNewFearpoint();
|
||||
float FindGroundZ(float new_x, float new_y, float z_offset=0.0);
|
||||
Map::Vertex UpdatePath(float ToX, float ToY, float ToZ, float Speed, bool &WaypointChange, bool &NodeReached);
|
||||
@ -1018,7 +1021,6 @@ protected:
|
||||
tProc SpellProcs[MAX_PROCS];
|
||||
tProc DefensiveProcs[MAX_PROCS];
|
||||
tProc RangedProcs[MAX_PROCS];
|
||||
tProc SkillProcs[MAX_PROCS];
|
||||
|
||||
char name[64];
|
||||
char orig_name[64];
|
||||
|
||||
@ -194,6 +194,7 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, float x, float y, float z, float
|
||||
}
|
||||
|
||||
accuracy_rating = d->accuracy_rating;
|
||||
avoidance_rating = d->avoidance_rating;
|
||||
ATK = d->ATK;
|
||||
|
||||
CalcMaxMana();
|
||||
@ -259,9 +260,11 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, float x, float y, float z, float
|
||||
|
||||
d_meele_texture1 = d->d_meele_texture1;
|
||||
d_meele_texture2 = d->d_meele_texture2;
|
||||
ammo_idfile = d->ammo_idfile;
|
||||
memset(equipment, 0, sizeof(equipment));
|
||||
prim_melee_type = d->prim_melee_type;
|
||||
sec_melee_type = d->sec_melee_type;
|
||||
ranged_type = d->ranged_type;
|
||||
|
||||
// If Melee Textures are not set, set attack type to Hand to Hand as default
|
||||
if(!d_meele_texture1)
|
||||
@ -1925,6 +1928,12 @@ void NPC::ModifyNPCStat(const char *identifier, const char *newValue)
|
||||
return;
|
||||
}
|
||||
|
||||
if(id == "avoidance")
|
||||
{
|
||||
avoidance_rating = atoi(val.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if(id == "trackable")
|
||||
{
|
||||
trackable = atoi(val.c_str());
|
||||
|
||||
37
zone/npc.h
37
zone/npc.h
@ -73,6 +73,22 @@ struct AISpellsEffects_Struct {
|
||||
int32 max;
|
||||
};
|
||||
|
||||
struct AISpellsVar_Struct {
|
||||
uint32 fail_recast;
|
||||
uint32 engaged_no_sp_recast_min;
|
||||
uint32 engaged_no_sp_recast_max;
|
||||
uint8 engaged_beneficial_self_chance;
|
||||
uint8 engaged_beneficial_other_chance;
|
||||
uint8 engaged_detrimental_chance;
|
||||
uint32 pursue_no_sp_recast_min;
|
||||
uint32 pursue_no_sp_recast_max;
|
||||
uint8 pursue_detrimental_chance;
|
||||
uint32 idle_no_sp_recast_min;
|
||||
uint32 idle_no_sp_recast_max;
|
||||
uint8 idle_beneficial_chance;
|
||||
};
|
||||
|
||||
|
||||
class AA_SwarmPetInfo;
|
||||
|
||||
class NPC : public Mob
|
||||
@ -215,8 +231,10 @@ public:
|
||||
|
||||
uint8 GetPrimSkill() const { return prim_melee_type; }
|
||||
uint8 GetSecSkill() const { return sec_melee_type; }
|
||||
uint8 GetRangedSkill() const { return ranged_type; }
|
||||
void SetPrimSkill(uint8 skill_type) { prim_melee_type = skill_type; }
|
||||
void SetSecSkill(uint8 skill_type) { sec_melee_type = skill_type; }
|
||||
void SetRangedSkill(uint8 skill_type) { ranged_type = skill_type; }
|
||||
|
||||
uint32 MerchantType;
|
||||
bool merchant_open;
|
||||
@ -257,6 +275,7 @@ public:
|
||||
void CheckSignal();
|
||||
inline bool IsTargetableWithHotkey() const { return no_target_hotkey; }
|
||||
int32 GetNPCHPRegen() const { return hp_regen + itembonuses.HPRegen + spellbonuses.HPRegen; }
|
||||
inline const char* GetAmmoIDfile() const { return ammo_idfile; }
|
||||
|
||||
//waypoint crap
|
||||
int GetMaxWp() const { return max_wp; }
|
||||
@ -314,6 +333,8 @@ public:
|
||||
|
||||
int32 GetAccuracyRating() const { return (accuracy_rating); }
|
||||
void SetAccuracyRating(int32 d) { accuracy_rating = d;}
|
||||
int32 GetAvoidanceRating() const { return (avoidance_rating); }
|
||||
void SetAvoidanceRating(int32 d) { avoidance_rating = d;}
|
||||
int32 GetRawAC() const { return AC; }
|
||||
|
||||
void ModifyNPCStat(const char *identifier, const char *newValue);
|
||||
@ -413,14 +434,16 @@ protected:
|
||||
bool HasAISpell;
|
||||
virtual bool AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes);
|
||||
virtual bool AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore = 0);
|
||||
AISpellsVar_Struct AISpellVar;
|
||||
|
||||
uint32 npc_spells_effects_id;
|
||||
std::vector<AISpellsEffects_Struct> AIspellsEffects;
|
||||
bool HasAISpellEffects;
|
||||
|
||||
|
||||
uint32 max_dmg;
|
||||
uint32 min_dmg;
|
||||
int32 accuracy_rating;
|
||||
int32 avoidance_rating;
|
||||
int16 attack_count;
|
||||
uint32 npc_mana;
|
||||
float spellscale;
|
||||
@ -454,11 +477,15 @@ protected:
|
||||
uint32 roambox_min_delay;
|
||||
|
||||
uint16 skills[HIGHEST_SKILL+1];
|
||||
|
||||
uint32 equipment[EmuConstants::EQUIPMENT_SIZE]; //this is an array of item IDs
|
||||
uint16 d_meele_texture1; //this is an item Material value
|
||||
uint16 d_meele_texture2; //this is an item Material value (offhand)
|
||||
uint8 prim_melee_type; //Sets the Primary Weapon attack message and animation
|
||||
uint8 sec_melee_type; //Sets the Secondary Weapon attack message and animation
|
||||
uint16 d_meele_texture1; //this is an item Material value
|
||||
uint16 d_meele_texture2; //this is an item Material value (offhand)
|
||||
const char* ammo_idfile; //this determines projectile graphic "IT###" (see item field 'idfile')
|
||||
uint8 prim_melee_type; //Sets the Primary Weapon attack message and animation
|
||||
uint8 sec_melee_type; //Sets the Secondary Weapon attack message and animation
|
||||
uint8 ranged_type; //Sets the Ranged Weapon attack message and animation
|
||||
|
||||
AA_SwarmPetInfo *swarmInfoPtr;
|
||||
|
||||
bool ldon_trapped;
|
||||
|
||||
@ -105,6 +105,9 @@ void Mob::DoSpecialAttackDamage(Mob *who, SkillUseTypes skill, int32 max_damage,
|
||||
//this really should go through the same code as normal melee damage to
|
||||
//pick up all the special behavior there
|
||||
|
||||
if (!who)
|
||||
return;
|
||||
|
||||
int32 hate = max_damage;
|
||||
if(hate_override > -1)
|
||||
hate = hate_override;
|
||||
@ -141,26 +144,18 @@ void Mob::DoSpecialAttackDamage(Mob *who, SkillUseTypes skill, int32 max_damage,
|
||||
|
||||
who->MeleeMitigation(this, max_damage, min_damage);
|
||||
|
||||
if(max_damage > 0) {
|
||||
ApplyMeleeDamageBonus(skill, max_damage);
|
||||
max_damage += who->GetFcDamageAmtIncoming(this, 0, true, skill);
|
||||
max_damage += (itembonuses.HeroicSTR / 10) + (max_damage * who->GetSkillDmgTaken(skill) / 100) + GetSkillDmgAmt(skill);
|
||||
TryCriticalHit(who, skill, max_damage);
|
||||
}
|
||||
if(max_damage > 0)
|
||||
CommonOutgoingHitSuccess(who, max_damage, skill);
|
||||
|
||||
}
|
||||
|
||||
if(max_damage >= 0) //You should probably get aggro no matter what, but unclear why it was set like this.
|
||||
who->AddToHateList(this, hate);
|
||||
|
||||
who->AddToHateList(this, hate, 0, false);
|
||||
who->Damage(this, max_damage, SPELL_UNKNOWN, skill, false);
|
||||
|
||||
//Make sure 'this' has not killed the target and 'this' is not dead (Damage shield ect).
|
||||
if(!GetTarget())return;
|
||||
if (HasDied()) return;
|
||||
|
||||
if (max_damage > 0)
|
||||
CheckNumHitsRemaining(NUMHIT_OutgoingHitSuccess);
|
||||
|
||||
//[AA Dragon Punch] value[0] = 100 for 25%, chance value[1] = skill
|
||||
if(aabonuses.SpecialAttackKBProc[0] && aabonuses.SpecialAttackKBProc[1] == skill){
|
||||
int kb_chance = 25;
|
||||
@ -814,39 +809,7 @@ void Client::RangedAttack(Mob* other, bool CanDoubleAttack) {
|
||||
|
||||
CheckIncreaseSkill(SkillArchery, GetTarget(), -15);
|
||||
|
||||
//break invis when you attack
|
||||
if(invisible) {
|
||||
mlog(COMBAT__ATTACKS, "Removing invisibility due to melee attack.");
|
||||
BuffFadeByEffect(SE_Invisibility);
|
||||
BuffFadeByEffect(SE_Invisibility2);
|
||||
invisible = false;
|
||||
}
|
||||
if(invisible_undead) {
|
||||
mlog(COMBAT__ATTACKS, "Removing invisibility vs. undead due to melee attack.");
|
||||
BuffFadeByEffect(SE_InvisVsUndead);
|
||||
BuffFadeByEffect(SE_InvisVsUndead2);
|
||||
invisible_undead = false;
|
||||
}
|
||||
if(invisible_animals){
|
||||
mlog(COMBAT__ATTACKS, "Removing invisibility vs. animals due to melee attack.");
|
||||
BuffFadeByEffect(SE_InvisVsAnimals);
|
||||
invisible_animals = false;
|
||||
}
|
||||
|
||||
if (spellbonuses.NegateIfCombat)
|
||||
BuffFadeByEffect(SE_NegateIfCombat);
|
||||
|
||||
if(hidden || improved_hidden){
|
||||
hidden = false;
|
||||
improved_hidden = false;
|
||||
EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct));
|
||||
SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer;
|
||||
sa_out->spawn_id = GetID();
|
||||
sa_out->type = 0x03;
|
||||
sa_out->parameter = 0;
|
||||
entity_list.QueueClients(this, outapp, true);
|
||||
safe_delete(outapp);
|
||||
}
|
||||
CommonBreakInvisible();
|
||||
}
|
||||
|
||||
void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const ItemInst* Ammo, uint16 weapon_damage, int16 chance_mod, int16 focus, int ReuseTime)
|
||||
@ -1019,19 +982,30 @@ void NPC::RangedAttack(Mob* other)
|
||||
return;
|
||||
}
|
||||
|
||||
float range = 250; // needs to be longer than 200(most spells)
|
||||
mlog(COMBAT__RANGED, "Calculated bow range to be %.1f", range);
|
||||
range *= range;
|
||||
if(DistNoRootNoZ(*GetTarget()) > range) {
|
||||
mlog(COMBAT__RANGED, "Ranged attack out of range...%.2f vs %.2f", DistNoRootNoZ(*GetTarget()), range);
|
||||
int sa_min_range = GetSpecialAbilityParam(SPECATK_RANGED_ATK, 0); //Min Range of NPC attack
|
||||
int sa_max_range = GetSpecialAbilityParam(SPECATK_RANGED_ATK, 1); //Max Range of NPC attack
|
||||
|
||||
float min_range = static_cast<float>(RuleI(Combat, MinRangedAttackDist));
|
||||
float max_range = 250; // needs to be longer than 200(most spells)
|
||||
|
||||
if (sa_max_range)
|
||||
max_range = static_cast<float>(sa_max_range);
|
||||
|
||||
if (sa_min_range)
|
||||
min_range = static_cast<float>(sa_min_range);
|
||||
|
||||
mlog(COMBAT__RANGED, "Calculated bow range to be %.1f", max_range);
|
||||
max_range *= max_range;
|
||||
if(DistNoRootNoZ(*other) > max_range) {
|
||||
mlog(COMBAT__RANGED, "Ranged attack out of range...%.2f vs %.2f", DistNoRootNoZ(*other), max_range);
|
||||
//target is out of range, client does a message
|
||||
return;
|
||||
}
|
||||
else if(DistNoRootNoZ(*GetTarget()) < (RuleI(Combat, MinRangedAttackDist)*RuleI(Combat, MinRangedAttackDist))){
|
||||
else if(DistNoRootNoZ(*other) < (min_range * min_range))
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if(!IsAttackAllowed(GetTarget()) ||
|
||||
if(!other || !IsAttackAllowed(other) ||
|
||||
IsCasting() ||
|
||||
DivineAura() ||
|
||||
IsStunned() ||
|
||||
@ -1041,32 +1015,33 @@ void NPC::RangedAttack(Mob* other)
|
||||
return;
|
||||
}
|
||||
|
||||
if(!ammo)
|
||||
{
|
||||
SkillUseTypes skillinuse = SkillArchery;
|
||||
skillinuse = static_cast<SkillUseTypes>(GetRangedSkill());
|
||||
|
||||
if(!ammo && !GetAmmoIDfile())
|
||||
ammo = database.GetItem(8005);
|
||||
}
|
||||
|
||||
if(ammo)
|
||||
SendItemAnimation(GetTarget(), ammo, SkillArchery);
|
||||
SendItemAnimation(other, ammo, SkillArchery);
|
||||
else
|
||||
ProjectileAnimation(other, 0,false,0,0,0,0,GetAmmoIDfile(),skillinuse);
|
||||
|
||||
FaceTarget(other);
|
||||
|
||||
// Face the Target
|
||||
FaceTarget(GetTarget());
|
||||
|
||||
// Hit?
|
||||
if (!GetTarget()->CheckHitChance(this, SkillArchery, 13))
|
||||
if (!other->CheckHitChance(this, skillinuse, 11, GetSpecialAbilityParam(SPECATK_RANGED_ATK, 2)))
|
||||
{
|
||||
mlog(COMBAT__RANGED, "Ranged attack missed %s.", GetTarget()->GetName());
|
||||
GetTarget()->Damage(this, 0, SPELL_UNKNOWN, SkillArchery);
|
||||
mlog(COMBAT__RANGED, "Ranged attack missed %s.", other->GetName());
|
||||
other->Damage(this, 0, SPELL_UNKNOWN, skillinuse);
|
||||
}
|
||||
else
|
||||
{
|
||||
int16 WDmg = GetWeaponDamage(GetTarget(), weapon);
|
||||
int16 ADmg = GetWeaponDamage(GetTarget(), ammo);
|
||||
int16 WDmg = GetWeaponDamage(other, weapon);
|
||||
int16 ADmg = GetWeaponDamage(other, ammo);
|
||||
int32 TotalDmg = 0;
|
||||
if(WDmg > 0 || ADmg > 0)
|
||||
{
|
||||
mlog(COMBAT__RANGED, "Ranged attack hit %s.", GetTarget()->GetName());
|
||||
int32 TotalDmg = 0;
|
||||
|
||||
mlog(COMBAT__RANGED, "Ranged attack hit %s.", other->GetName());
|
||||
|
||||
int32 MaxDmg = max_dmg * RuleR(Combat, ArcheryNPCMultiplier); // should add a field to npc_types
|
||||
int32 MinDmg = min_dmg * RuleR(Combat, ArcheryNPCMultiplier);
|
||||
|
||||
@ -1075,54 +1050,36 @@ void NPC::RangedAttack(Mob* other)
|
||||
else
|
||||
TotalDmg = MakeRandomInt(MinDmg, MaxDmg);
|
||||
|
||||
int32 hate = TotalDmg;
|
||||
|
||||
GetTarget()->MeleeMitigation(this, TotalDmg, MinDmg);
|
||||
ApplyMeleeDamageBonus(SkillArchery, TotalDmg);
|
||||
TryCriticalHit(GetTarget(), SkillArchery, TotalDmg);
|
||||
GetTarget()->AddToHateList(this, hate, 0, false);
|
||||
GetTarget()->Damage(this, TotalDmg, SPELL_UNKNOWN, SkillArchery);
|
||||
CheckNumHitsRemaining(NUMHIT_OutgoingHitSuccess);
|
||||
TotalDmg += TotalDmg * GetSpecialAbilityParam(SPECATK_RANGED_ATK, 3) / 100; //Damage modifier
|
||||
|
||||
other->AvoidDamage(this, TotalDmg, false);
|
||||
other->MeleeMitigation(this, TotalDmg, MinDmg);
|
||||
if (TotalDmg > 0)
|
||||
CommonOutgoingHitSuccess(other, TotalDmg, skillinuse);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
GetTarget()->Damage(this, -5, SPELL_UNKNOWN, SkillArchery);
|
||||
}
|
||||
TotalDmg = -5;
|
||||
|
||||
if (TotalDmg > 0)
|
||||
other->AddToHateList(this, TotalDmg, 0, false);
|
||||
else
|
||||
other->AddToHateList(this, 0, 0, false);
|
||||
|
||||
other->Damage(this, TotalDmg, SPELL_UNKNOWN, skillinuse);
|
||||
|
||||
if (TotalDmg > 0 && HasSkillProcSuccess() && GetTarget() && !other->HasDied())
|
||||
TrySkillProc(other, skillinuse, 0, true, 11);
|
||||
}
|
||||
|
||||
//break invis when you attack
|
||||
if(invisible) {
|
||||
mlog(COMBAT__ATTACKS, "Removing invisibility due to melee attack.");
|
||||
BuffFadeByEffect(SE_Invisibility);
|
||||
BuffFadeByEffect(SE_Invisibility2);
|
||||
invisible = false;
|
||||
}
|
||||
if(invisible_undead) {
|
||||
mlog(COMBAT__ATTACKS, "Removing invisibility vs. undead due to melee attack.");
|
||||
BuffFadeByEffect(SE_InvisVsUndead);
|
||||
BuffFadeByEffect(SE_InvisVsUndead2);
|
||||
invisible_undead = false;
|
||||
}
|
||||
if(invisible_animals){
|
||||
mlog(COMBAT__ATTACKS, "Removing invisibility vs. animals due to melee attack.");
|
||||
BuffFadeByEffect(SE_InvisVsAnimals);
|
||||
invisible_animals = false;
|
||||
}
|
||||
//try proc on hits and misses
|
||||
if(other && !other->HasDied())
|
||||
TrySpellProc(nullptr, (const Item_Struct*)nullptr, other, 11);
|
||||
|
||||
if (spellbonuses.NegateIfCombat)
|
||||
BuffFadeByEffect(SE_NegateIfCombat);
|
||||
if (HasSkillProcs() && other && !other->HasDied())
|
||||
TrySkillProc(other, skillinuse, 0, false, 11);
|
||||
|
||||
if(hidden || improved_hidden){
|
||||
hidden = false;
|
||||
improved_hidden = false;
|
||||
EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct));
|
||||
SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer;
|
||||
sa_out->spawn_id = GetID();
|
||||
sa_out->type = 0x03;
|
||||
sa_out->parameter = 0;
|
||||
entity_list.QueueClients(this, outapp, true);
|
||||
safe_delete(outapp);
|
||||
}
|
||||
CommonBreakInvisible();
|
||||
}
|
||||
|
||||
uint16 Mob::GetThrownDamage(int16 wDmg, int32& TotalDmg, int& minDmg)
|
||||
@ -1235,39 +1192,7 @@ void Client::ThrowingAttack(Mob* other, bool CanDoubleAttack) { //old was 51
|
||||
DeleteItemInInventory(ammo_slot, 1, true);
|
||||
CheckIncreaseSkill(SkillThrowing, GetTarget());
|
||||
|
||||
//break invis when you attack
|
||||
if(invisible) {
|
||||
mlog(COMBAT__ATTACKS, "Removing invisibility due to melee attack.");
|
||||
BuffFadeByEffect(SE_Invisibility);
|
||||
BuffFadeByEffect(SE_Invisibility2);
|
||||
invisible = false;
|
||||
}
|
||||
if(invisible_undead) {
|
||||
mlog(COMBAT__ATTACKS, "Removing invisibility vs. undead due to melee attack.");
|
||||
BuffFadeByEffect(SE_InvisVsUndead);
|
||||
BuffFadeByEffect(SE_InvisVsUndead2);
|
||||
invisible_undead = false;
|
||||
}
|
||||
if(invisible_animals){
|
||||
mlog(COMBAT__ATTACKS, "Removing invisibility vs. animals due to melee attack.");
|
||||
BuffFadeByEffect(SE_InvisVsAnimals);
|
||||
invisible_animals = false;
|
||||
}
|
||||
|
||||
if (spellbonuses.NegateIfCombat)
|
||||
BuffFadeByEffect(SE_NegateIfCombat);
|
||||
|
||||
if(hidden || improved_hidden){
|
||||
hidden = false;
|
||||
improved_hidden = false;
|
||||
EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct));
|
||||
SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer;
|
||||
sa_out->spawn_id = GetID();
|
||||
sa_out->type = 0x03;
|
||||
sa_out->parameter = 0;
|
||||
entity_list.QueueClients(this, outapp, true);
|
||||
safe_delete(outapp);
|
||||
}
|
||||
CommonBreakInvisible();
|
||||
}
|
||||
|
||||
void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item_Struct* item, uint16 weapon_damage, int16 chance_mod,int16 focus, int ReuseTime)
|
||||
@ -1313,20 +1238,13 @@ void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite
|
||||
|
||||
other->MeleeMitigation(this, TotalDmg, minDmg);
|
||||
if(TotalDmg > 0)
|
||||
{
|
||||
ApplyMeleeDamageBonus(SkillThrowing, TotalDmg);
|
||||
TotalDmg += other->GetFcDamageAmtIncoming(this, 0, true, SkillThrowing);
|
||||
TotalDmg += (itembonuses.HeroicDEX / 10) + (TotalDmg * other->GetSkillDmgTaken(SkillThrowing) / 100) + GetSkillDmgAmt(SkillThrowing);
|
||||
TryCriticalHit(other, SkillThrowing, TotalDmg);
|
||||
int32 hate = (2*WDmg);
|
||||
other->AddToHateList(this, hate, 0, false);
|
||||
CheckNumHitsRemaining(NUMHIT_OutgoingHitSuccess);
|
||||
}
|
||||
CommonOutgoingHitSuccess(other, TotalDmg, SkillThrowing);
|
||||
}
|
||||
|
||||
else
|
||||
TotalDmg = -5;
|
||||
|
||||
other->AddToHateList(this, 2*WDmg, 0, false);
|
||||
other->Damage(this, TotalDmg, SPELL_UNKNOWN, SkillThrowing);
|
||||
|
||||
if (TotalDmg > 0 && HasSkillProcSuccess() && GetTarget() && other && !other->HasDied()){
|
||||
@ -1395,7 +1313,10 @@ void Mob::SendItemAnimation(Mob *to, const Item_Struct *item, SkillUseTypes skil
|
||||
safe_delete(outapp);
|
||||
}
|
||||
|
||||
void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, float angle, float tilt, float arc, const char *IDFile) {
|
||||
void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, float angle, float tilt, float arc, const char *IDFile, SkillUseTypes skillInUse) {
|
||||
|
||||
if (!to)
|
||||
return;
|
||||
|
||||
const Item_Struct* item = nullptr;
|
||||
uint8 item_type = 0;
|
||||
@ -1413,9 +1334,12 @@ void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, f
|
||||
if(IsArrow) {
|
||||
item_type = 27;
|
||||
}
|
||||
if(!item_type) {
|
||||
if(!item_type && !skillInUse) {
|
||||
item_type = item->ItemType;
|
||||
}
|
||||
else if (skillInUse)
|
||||
item_type = GetItemTypeBySkill(skillInUse);
|
||||
|
||||
if(!speed) {
|
||||
speed = 4.0;
|
||||
}
|
||||
@ -1445,7 +1369,7 @@ void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, f
|
||||
as->target_id = to->GetID();
|
||||
as->item_id = item->ID;
|
||||
as->item_type = item_type;
|
||||
as->skill = 0; // Doesn't seem to have any effect
|
||||
as->skill = skillInUse; // Doesn't seem to have any effect
|
||||
strn0cpy(as->model_name, item_IDFile, 16);
|
||||
as->velocity = speed;
|
||||
as->launch_angle = angle;
|
||||
@ -2174,7 +2098,7 @@ void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes
|
||||
skillinuse = SkillOffense;
|
||||
|
||||
int damage = 0;
|
||||
uint32 hate = 0;
|
||||
int32 hate = 0;
|
||||
int Hand = 13;
|
||||
if (hate == 0 && weapon_damage > 1) hate = weapon_damage;
|
||||
|
||||
@ -2200,6 +2124,19 @@ void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes
|
||||
hate += ucDamageBonus;
|
||||
}
|
||||
|
||||
if(skillinuse == SkillBash){
|
||||
if(IsClient()){
|
||||
ItemInst *item = CastToClient()->GetInv().GetItem(MainSecondary);
|
||||
if(item){
|
||||
if(item->GetItem()->ItemType == ItemTypeShield) {
|
||||
hate += item->GetItem()->AC;
|
||||
}
|
||||
const Item_Struct *itm = item->GetItem();
|
||||
hate = hate * (100 + GetFuriousBash(itm->Focus.Effect)) / 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ApplySpecialAttackMod(skillinuse, max_hit, min_hit);
|
||||
|
||||
min_hit += min_hit * GetMeleeMinDamageMod_SE(skillinuse) / 100;
|
||||
@ -2211,18 +2148,14 @@ void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes
|
||||
damage = max_hit;
|
||||
else
|
||||
damage = MakeRandomInt(min_hit, max_hit);
|
||||
|
||||
|
||||
if(!other->CheckHitChance(this, skillinuse, Hand, chance_mod)) {
|
||||
damage = 0;
|
||||
} else {
|
||||
other->AvoidDamage(this, damage, CanRiposte);
|
||||
other->MeleeMitigation(this, damage, min_hit);
|
||||
if(damage > 0) {
|
||||
ApplyMeleeDamageBonus(skillinuse, damage);
|
||||
damage += other->GetFcDamageAmtIncoming(this, 0, true, skillinuse);
|
||||
damage += (itembonuses.HeroicSTR / 10) + (damage * other->GetSkillDmgTaken(skillinuse) / 100) + GetSkillDmgAmt(skillinuse);
|
||||
TryCriticalHit(other, skillinuse, damage);
|
||||
}
|
||||
if(damage > 0)
|
||||
CommonOutgoingHitSuccess(other, damage, skillinuse);
|
||||
}
|
||||
|
||||
if (damage == -3) {
|
||||
@ -2235,19 +2168,6 @@ void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes
|
||||
else
|
||||
damage = -5;
|
||||
|
||||
if(skillinuse == SkillBash){
|
||||
if(IsClient()){
|
||||
ItemInst *item = CastToClient()->GetInv().GetItem(MainSecondary);
|
||||
if(item){
|
||||
if(item->GetItem()->ItemType == ItemTypeShield) {
|
||||
hate += item->GetItem()->AC;
|
||||
}
|
||||
const Item_Struct *itm = item->GetItem();
|
||||
hate = hate * (100 + GetFuriousBash(itm->Focus.Effect)) / 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
other->AddToHateList(this, hate);
|
||||
|
||||
bool CanSkillProc = true;
|
||||
@ -2256,6 +2176,7 @@ void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes
|
||||
CanSkillProc = false; //Disable skill procs
|
||||
}
|
||||
|
||||
other->AddToHateList(this, hate, 0, false);
|
||||
other->Damage(this, damage, SPELL_UNKNOWN, skillinuse);
|
||||
|
||||
if (HasDied())
|
||||
|
||||
@ -5765,8 +5765,6 @@ int32 Mob::GetFocusIncoming(focusType type, int effect, Mob *caster, uint32 spel
|
||||
int value = 0;
|
||||
|
||||
if (spellbonuses.FocusEffects[type]){
|
||||
uint32 buff_count = GetMaxTotalSlots();
|
||||
for(int i = 0; i < buff_count; i++){
|
||||
|
||||
int32 tmp_focus = 0;
|
||||
int tmp_buffslot = -1;
|
||||
@ -5799,7 +5797,7 @@ int32 Mob::GetFocusIncoming(focusType type, int effect, Mob *caster, uint32 spel
|
||||
CheckNumHitsRemaining(NUMHIT_MatchingSpells, tmp_buffslot);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
@ -5018,7 +5018,7 @@ bool Mob::IsCombatProc(uint16 spell_id) {
|
||||
|
||||
for (int i = 0; i < MAX_PROCS; i++){
|
||||
if (PermaProcs[i].spellID == spell_id || SpellProcs[i].spellID == spell_id
|
||||
|| SkillProcs[i].spellID == spell_id || RangedProcs[i].spellID == spell_id){
|
||||
|| RangedProcs[i].spellID == spell_id){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -5075,7 +5075,7 @@ bool Mob::AddDefensiveProc(uint16 spell_id, uint16 iChance, uint16 base_spell_id
|
||||
{
|
||||
if(spell_id == SPELL_UNKNOWN)
|
||||
return(false);
|
||||
|
||||
|
||||
int i;
|
||||
for (i = 0; i < MAX_PROCS; i++) {
|
||||
if (DefensiveProcs[i].spellID == SPELL_UNKNOWN) {
|
||||
|
||||
@ -1067,8 +1067,10 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
|
||||
"npc_types.npc_spells_effects_id,"
|
||||
"npc_types.d_meele_texture1,"
|
||||
"npc_types.d_meele_texture2,"
|
||||
"npc_types.ammo_idfile,"
|
||||
"npc_types.prim_melee_type,"
|
||||
"npc_types.sec_melee_type,"
|
||||
"npc_types.ranged_type,"
|
||||
"npc_types.runspeed,"
|
||||
"npc_types.findable,"
|
||||
"npc_types.trackable,"
|
||||
@ -1103,6 +1105,7 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
|
||||
"npc_types.see_improved_hide,"
|
||||
"npc_types.ATK,"
|
||||
"npc_types.Accuracy,"
|
||||
"npc_types.Avoidance,"
|
||||
"npc_types.slow_mitigation,"
|
||||
"npc_types.maxlevel,"
|
||||
"npc_types.scalerate,"
|
||||
@ -1166,8 +1169,10 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
|
||||
tmpNPCType->npc_spells_effects_id = atoi(row[r++]);
|
||||
tmpNPCType->d_meele_texture1 = atoi(row[r++]);
|
||||
tmpNPCType->d_meele_texture2 = atoi(row[r++]);
|
||||
strn0cpy(tmpNPCType->ammo_idfile, row[r++], 30);
|
||||
tmpNPCType->prim_melee_type = atoi(row[r++]);
|
||||
tmpNPCType->sec_melee_type = atoi(row[r++]);
|
||||
tmpNPCType->ranged_type = atoi(row[r++]);
|
||||
tmpNPCType->runspeed= atof(row[r++]);
|
||||
tmpNPCType->findable = atoi(row[r++]) == 0? false : true;
|
||||
tmpNPCType->trackable = atoi(row[r++]) == 0? false : true;
|
||||
@ -1286,6 +1291,7 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
|
||||
tmpNPCType->see_improved_hide = atoi(row[r++])==0?false:true;
|
||||
tmpNPCType->ATK = atoi(row[r++]);
|
||||
tmpNPCType->accuracy_rating = atoi(row[r++]);
|
||||
tmpNPCType->avoidance_rating = atoi(row[r++]);
|
||||
tmpNPCType->slow_mitigation = atoi(row[r++]);
|
||||
tmpNPCType->maxlevel = atoi(row[r++]);
|
||||
tmpNPCType->scalerate = atoi(row[r++]);
|
||||
|
||||
@ -43,9 +43,25 @@ struct DBnpcspellseffects_entries_Struct {
|
||||
|
||||
struct DBnpcspells_Struct {
|
||||
uint32 parent_list;
|
||||
int16 attack_proc;
|
||||
uint16 attack_proc;
|
||||
uint8 proc_chance;
|
||||
uint16 range_proc;
|
||||
int16 rproc_chance;
|
||||
uint16 defensive_proc;
|
||||
int16 dproc_chance;
|
||||
uint32 numentries;
|
||||
uint32 fail_recast;
|
||||
uint32 engaged_no_sp_recast_min;
|
||||
uint32 engaged_no_sp_recast_max;
|
||||
uint8 engaged_beneficial_self_chance;
|
||||
uint8 engaged_beneficial_other_chance;
|
||||
uint8 engaged_detrimental_chance;
|
||||
uint32 pursue_no_sp_recast_min;
|
||||
uint32 pursue_no_sp_recast_max;
|
||||
uint8 pursue_detrimental_chance;
|
||||
uint32 idle_no_sp_recast_min;
|
||||
uint32 idle_no_sp_recast_max;
|
||||
uint8 idle_beneficial_chance;
|
||||
DBnpcspells_entries_Struct entries[0];
|
||||
};
|
||||
|
||||
|
||||
@ -94,8 +94,10 @@ struct NPCType
|
||||
char special_abilities[512];
|
||||
uint16 d_meele_texture1;
|
||||
uint16 d_meele_texture2;
|
||||
char ammo_idfile[30];
|
||||
uint8 prim_melee_type;
|
||||
uint8 sec_melee_type;
|
||||
uint8 ranged_type;
|
||||
int32 hp_regen;
|
||||
int32 mana_regen;
|
||||
int32 aggroradius; // added for AI improvement - neotokyo
|
||||
@ -110,6 +112,7 @@ struct NPCType
|
||||
uint8 mount_color; //only used by horse class
|
||||
float attack_speed; //%+- on attack delay of the mob.
|
||||
int accuracy_rating; //10 = 1% accuracy
|
||||
int avoidance_rating; //10 = 1% avoidance
|
||||
bool findable; //can be found with find command
|
||||
bool trackable;
|
||||
int16 slow_mitigation;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user