Merge pull request #169 from KayenEQ/Development

Updates to npc_spells and npc_types table.
This commit is contained in:
Michael Cook 2014-07-24 18:44:18 -04:00
commit d0756ff2af
22 changed files with 602 additions and 388 deletions

View File

@ -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.

View File

@ -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%)

View 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.');

View File

@ -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`;

View 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';

View File

@ -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))) {

View File

@ -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;
}

View File

@ -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:

View File

@ -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();

View File

@ -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);

View File

@ -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)

View File

@ -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

View File

@ -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) {

View File

@ -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];

View File

@ -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());

View File

@ -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;

View File

@ -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())

View File

@ -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;
}

View File

@ -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) {

View File

@ -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++]);

View File

@ -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];
};

View File

@ -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;