Updates to npc_spells and npc_types table.

Implemented innate defensive and range procs
Implemented ability to fine tune AI casting behavior/timers
Global rules for AI casting behavior/timers
NPC Ranged attack updates, set skill and ammo type in npc_types
Various clean ups in attack related functions.
Other minor fixes.
See Change Log, +required, +optional SQL
This commit is contained in:
KayenEQ
2014-07-10 22:46:39 -04:00
parent a96784aa18
commit 965bb039be
18 changed files with 488 additions and 321 deletions
+129 -13
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))) {
+87 -108
View File
@@ -1297,11 +1297,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 +1363,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 +1899,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 +1926,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 +1936,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 +3847,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 +3869,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 +3888,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 +3902,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 +3961,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 +4039,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 != 13) //Is Archery intened to proc at 50% rate?
ProcChance /= 2;
@@ -4130,6 +4053,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 != 13) //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 +4078,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 +4094,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 +4746,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;
}
+2 -2
View File
@@ -7732,10 +7732,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 SLOT_PRIMARY:
weapon_speed = attack_timer.GetDuration();
+1 -1
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);
+32 -4
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) {
+9 -7
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];
+2
View File
@@ -259,9 +259,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)
+23 -1
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; }
@@ -413,11 +432,12 @@ 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;
@@ -457,8 +477,10 @@ protected:
uint32 equipment[MAX_WORN_INVENTORY]; //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)
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;
+97 -178
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;
@@ -813,39 +808,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)
@@ -1018,19 +981,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() ||
@@ -1040,32 +1014,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);
@@ -1074,54 +1049,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)
@@ -1234,39 +1191,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)
@@ -1312,20 +1237,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()){
@@ -1394,7 +1312,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;
@@ -1412,9 +1333,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;
}
@@ -1444,7 +1368,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;
@@ -2173,7 +2097,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;
@@ -2199,6 +2123,19 @@ void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes
hate += ucDamageBonus;
}
if(skillinuse == SkillBash){
if(IsClient()){
ItemInst *item = CastToClient()->GetInv().GetItem(SLOT_SECONDARY);
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;
@@ -2216,12 +2153,8 @@ void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes
} 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) {
@@ -2234,27 +2167,13 @@ void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes
else
damage = -5;
if(skillinuse == SkillBash){
if(IsClient()){
ItemInst *item = CastToClient()->GetInv().GetItem(SLOT_SECONDARY);
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;
if (skillinuse == SkillOffense){ //Hack to allow damage to display.
skillinuse = SkillTigerClaw; //'strike' your opponent - Arbitrary choice for message.
CanSkillProc = false; //Disable skill procs
}
other->AddToHateList(this, hate, 0, false);
other->Damage(this, damage, SPELL_UNKNOWN, skillinuse);
if (HasDied())
+1 -3
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;
}
+3 -3
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) {
@@ -5107,7 +5107,7 @@ bool Mob::AddRangedProc(uint16 spell_id, uint16 iChance, uint16 base_spell_id)
{
if(spell_id == SPELL_UNKNOWN)
return(false);
Shout("Add Ranged Proc %i %i %i", spell_id, iChance, base_spell_id);
int i;
for (i = 0; i < MAX_PROCS; i++) {
if (RangedProcs[i].spellID == SPELL_UNKNOWN) {
+4
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,"
@@ -1166,8 +1168,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;
+17 -1
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];
};
+2
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