From f9e822072f2585f6516cc08f057afb61d2a6568d Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sat, 28 Dec 2019 17:08:34 -0600 Subject: [PATCH] AE Scanning adjustments, testing --- common/database.cpp | 1 + common/spdat.cpp | 3 +- zone/attack.cpp | 2 + zone/effects.cpp | 307 +++++++++++++++++++++++++++++++------------- zone/entity.cpp | 32 +++-- zone/entity.h | 36 +++++- zone/spells.cpp | 34 +++-- zone/zone.cpp | 27 ++-- 8 files changed, 312 insertions(+), 130 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index 4e762f917..fde091322 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -683,6 +683,7 @@ bool Database::SaveCharacterCreate(uint32 character_id, uint32 account_id, Playe pp->RestTimer // " RestTimer) " ); auto results = QueryDatabase(query); + /* Save Bind Points */ query = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, slot)" " VALUES (%u, %u, %u, %f, %f, %f, %f, %i), " diff --git a/common/spdat.cpp b/common/spdat.cpp index 3fd6d19e4..3f0ae6e41 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -87,8 +87,9 @@ bool IsTargetableAESpell(uint16 spell_id) { - if (IsValidSpell(spell_id) && spells[spell_id].targettype == ST_AETarget) + if (IsValidSpell(spell_id) && spells[spell_id].targettype == ST_AETarget) { return true; + } return false; } diff --git a/zone/attack.cpp b/zone/attack.cpp index f39fb6b90..78aecd22d 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -2153,6 +2153,8 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQEmu::skills::Skil LogCombat("Fatal blow dealt by [{}] with [{}] damage, spell [{}], skill [{}]", ((killer_mob) ? (killer_mob->GetName()) : ("[nullptr]")), damage, spell, attack_skill); + entity_list.RemoveMobFromCloseLists(CastToMob()); + Mob *oos = nullptr; if (killer_mob) { oos = killer_mob->GetOwnerOrSelf(); diff --git a/zone/effects.cpp b/zone/effects.cpp index ba82cf3f0..4b908cec6 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -697,110 +697,241 @@ void EntityList::AETaunt(Client* taunter, float range, int32 bonus_hate) } } -// causes caster to hit every mob within dist range of center with -// spell_id. -// NPC spells will only affect other NPCs with compatible faction -void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster, int16 resist_adjust, int *max_targets) +/** + * Causes caster to hit every mob within dist range of center with spell_id + * + * @param caster_mob + * @param center_mob + * @param spell_id + * @param affect_caster + * @param resist_adjust + * @param max_targets + */ +void EntityList::AESpell( + Mob *caster_mob, + Mob *center_mob, + uint16 spell_id, + bool affect_caster, + int16 resist_adjust, + int *max_targets +) { - Mob *curmob = nullptr; + Mob *current_mob = nullptr; + bool is_detrimental_spell = IsDetrimentalSpell(spell_id); + bool is_npc = caster_mob->IsNPC(); - float dist = caster->GetAOERange(spell_id); - float dist2 = dist * dist; - float min_range2 = spells[spell_id].min_range * spells[spell_id].min_range; - float dist_targ = 0; + const auto &cast_target_position = + spells[spell_id].targettype == ST_Ring ? + caster_mob->GetTargetRingLocation() : + static_cast(center_mob->GetPosition()); - const auto &position = spells[spell_id].targettype == ST_Ring ? caster->GetTargetRingLocation() : static_cast(center->GetPosition()); - glm::vec2 min = { position.x - dist, position.y - dist }; - glm::vec2 max = { position.x + dist, position.y + dist }; + /** + * If using Old Rain Targets - there is no max target limitation + */ + if (RuleB(Spells, OldRainTargets)) { + max_targets = nullptr; + } - bool bad = IsDetrimentalSpell(spell_id); - bool isnpc = caster->IsNPC(); - - if (RuleB(Spells, OldRainTargets)) - max_targets = nullptr; // ignore it! - - // if we have a passed in value, use it, otherwise default to data - // detrimental Target AEs have a default value of 4 for PCs and unlimited for NPCs + /** + * Max AOE targets + */ int max_targets_allowed = 0; // unlimited - if (max_targets) // rains pass this in since they need to preserve the count through waves + if (max_targets) { // rains pass this in since they need to preserve the count through waves max_targets_allowed = *max_targets; - else if (spells[spell_id].aemaxtargets) + } + else if (spells[spell_id].aemaxtargets) { max_targets_allowed = spells[spell_id].aemaxtargets; - else if (IsTargetableAESpell(spell_id) && bad && !isnpc) + } + else if (IsTargetableAESpell(spell_id) && is_detrimental_spell && !is_npc) { max_targets_allowed = 4; + } - int iCounter = 0; + int target_hit_counter = 0; + float distance_to_target = 0; - for (auto it = mob_list.begin(); it != mob_list.end(); ++it) { - curmob = it->second; - // test to fix possible cause of random zone crashes..external methods accessing client properties before they're initialized - if (curmob->IsClient() && !curmob->CastToClient()->ClientFinishedLoading()) - continue; - if (curmob == caster && !affect_caster) //watch for caster too - continue; - if (spells[spell_id].targettype == ST_TargetAENoPlayersPets && curmob->IsPetOwnerClient()) - continue; - if (spells[spell_id].targettype == ST_AreaClientOnly && !curmob->IsClient()) - continue; - if (spells[spell_id].targettype == ST_AreaNPCOnly && !curmob->IsNPC()) - continue; - // check PC/NPC only flag 1 = PCs, 2 = NPCs - if (spells[spell_id].pcnpc_only_flag == 1 && !curmob->IsClient() && !curmob->IsMerc()) - continue; - if (spells[spell_id].pcnpc_only_flag == 2 && (curmob->IsClient() || curmob->IsMerc())) - continue; - if (!IsWithinAxisAlignedBox(static_cast(curmob->GetPosition()), min, max)) - continue; + for (auto &it : caster_mob->close_mobs) { + current_mob = it.first; - dist_targ = DistanceSquared(curmob->GetPosition(), position); + if (!current_mob) { + continue; + } - if (dist_targ > dist2) //make sure they are in range + LogDebug("iterating [{}]", current_mob->GetCleanName()); + + if (!AESpellFilterCriteria( + current_mob, + caster_mob, + center_mob, + spell_id, + max_targets, + max_targets_allowed, + target_hit_counter, + distance_to_target, + cast_target_position, + affect_caster, + resist_adjust + )) { continue; - if (dist_targ < min_range2) //make sure they are in range - continue; - if (isnpc && curmob->IsNPC() && spells[spell_id].targettype != ST_AreaNPCOnly) { //check npc->npc casting - FACTION_VALUE f = curmob->GetReverseFactionCon(caster); - if (bad) { - //affect mobs that are on our hate list, or - //which have bad faction with us - if (!(caster->CheckAggro(curmob) || f == FACTION_THREATENLY || f == FACTION_SCOWLS) ) - continue; - } else { - //only affect mobs we would assist. - if (!(f <= FACTION_AMIABLE)) - continue; + } + + current_mob->CalcSpellPowerDistanceMod(spell_id, distance_to_target); + caster_mob->SpellOnTarget(spell_id, current_mob, false, true, resist_adjust); + } + + LogDebug("Done iterating [{}]", caster_mob->GetCleanName()); + + if (max_targets && max_targets_allowed) { + *max_targets = *max_targets - target_hit_counter; + } +} + +/** + * @param caster_mob + * @param center_mob + * @param spell_id + * @param affect_caster + * @param resist_adjust + * @param max_targets + */ +bool EntityList::AESpellFilterCriteria( + Mob *current_mob, + Mob *caster_mob, + Mob *center_mob, + uint16 spell_id, + int *max_targets, + int &max_targets_allowed, + int &target_hit_counter, + float &distance_to_target, + const glm::vec3 &cast_target_position, + bool affect_caster, + int16 resist_adjust +) { + + if (!current_mob) { + return false; + } + + bool is_npc = caster_mob->IsNPC(); + float distance = caster_mob->GetAOERange(spell_id); + float distance_squared = distance * distance; + float min_range2 = spells[spell_id].min_range * spells[spell_id].min_range; + bool is_detrimental_spell = IsDetrimentalSpell(spell_id); + glm::vec2 min = {cast_target_position.x - distance, cast_target_position.y - distance}; + glm::vec2 max = {cast_target_position.x + distance, cast_target_position.y + distance}; + + if (current_mob->IsClient() && !current_mob->CastToClient()->ClientFinishedLoading()) { + return false; + } + + if (current_mob == caster_mob && !affect_caster) { + return false; + } + + if (spells[spell_id].targettype == ST_TargetAENoPlayersPets && current_mob->IsPetOwnerClient()) { + return false; + } + + if (spells[spell_id].targettype == ST_AreaClientOnly && !current_mob->IsClient()) { + return false; + } + + if (spells[spell_id].targettype == ST_AreaNPCOnly && !current_mob->IsNPC()) { + return false; + } + + /** + * Check PC / NPC + * 1 = PC + * 2 = NPC + */ + if (spells[spell_id].pcnpc_only_flag == 1 && !current_mob->IsClient() && !current_mob->IsMerc()) { + return false; + } + + if (spells[spell_id].pcnpc_only_flag == 2 && (current_mob->IsClient() || current_mob->IsMerc())) { + return false; + } + + if (!IsWithinAxisAlignedBox(static_cast(current_mob->GetPosition()), min, max)) { + return false; + } + + distance_to_target = DistanceSquared(current_mob->GetPosition(), cast_target_position); + + if (distance_to_target > distance_squared) { + return false; + } + + if (distance_to_target < min_range2) { + return false; + } + + if (is_npc && current_mob->IsNPC() && + spells[spell_id].targettype != ST_AreaNPCOnly) { //check npc->npc casting + FACTION_VALUE faction_value = current_mob->GetReverseFactionCon(caster_mob); + if (is_detrimental_spell) { + //affect mobs that are on our hate list, or + //which have bad faction with us + if ( + !(caster_mob->CheckAggro(current_mob) || + faction_value == FACTION_THREATENLY || + faction_value == FACTION_SCOWLS)) { + return false; } } - //finally, make sure they are within range - if (bad) { - if (!caster->IsAttackAllowed(curmob, true)) - continue; - if (center && !spells[spell_id].npc_no_los && !center->CheckLosFN(curmob)) - continue; - if (!center && !spells[spell_id].npc_no_los && !caster->CheckLosFN(caster->GetTargetRingX(), caster->GetTargetRingY(), caster->GetTargetRingZ(), curmob->GetSize())) - continue; - } else { // check to stop casting beneficial ae buffs (to wit: bard songs) on enemies... - // This does not check faction for beneficial AE buffs..only agro and attackable. - // I've tested for spells that I can find without problem, but a faction-based - // check may still be needed. Any changes here should also reflect in BardAEPulse() - if (caster->IsAttackAllowed(curmob, true)) - continue; - if (caster->CheckAggro(curmob)) - continue; - } - - curmob->CalcSpellPowerDistanceMod(spell_id, dist_targ); - caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust); - - if (max_targets_allowed) { // if we have a limit, increment count - iCounter++; - if (iCounter >= max_targets_allowed) // we done - break; + else { + //only affect mobs we would assist. + if (!(faction_value <= FACTION_AMIABLE)) { + return false; + } } } - if (max_targets && max_targets_allowed) - *max_targets = *max_targets - iCounter; + /** + * Finally, make sure they are within range + */ + if (is_detrimental_spell) { + if (!caster_mob->IsAttackAllowed(current_mob, true)) { + return false; + } + if (center_mob && !spells[spell_id].npc_no_los && !center_mob->CheckLosFN(current_mob)) { + return false; + } + if (!center_mob && !spells[spell_id].npc_no_los && !caster_mob->CheckLosFN( + caster_mob->GetTargetRingX(), + caster_mob->GetTargetRingY(), + caster_mob->GetTargetRingZ(), + current_mob->GetSize())) { + return false; + } + } + else { + + /** + * Check to stop casting beneficial ae buffs (to wit: bard songs) on enemies... + * This does not check faction for beneficial AE buffs... only agro and attackable. + * I've tested for spells that I can find without problem, but a faction-based + * check may still be needed. Any changes here should also reflect in BardAEPulse() + */ + if (caster_mob->IsAttackAllowed(current_mob, true)) { + return false; + } + if (caster_mob->CheckAggro(current_mob)) { + return false; + } + } + + /** + * Increment hit count if max targets + */ + if (max_targets_allowed) { + target_hit_counter++; + if (target_hit_counter >= max_targets_allowed) { + return false; + } + } + + return true; } void EntityList::MassGroupBuff(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster) @@ -812,8 +943,8 @@ void EntityList::MassGroupBuff(Mob *caster, Mob *center, uint16 spell_id, bool a bool bad = IsDetrimentalSpell(spell_id); - for (auto it = mob_list.begin(); it != mob_list.end(); ++it) { - curmob = it->second; + for (auto & it : mob_list) { + curmob = it.second; if (curmob == center) //do not affect center continue; if (curmob == caster && !affect_caster) //watch for caster too diff --git a/zone/entity.cpp b/zone/entity.cpp index 597780878..c7ecb0f80 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -2486,30 +2486,41 @@ void EntityList::RemoveAllEncounters() } } +/** + * @param delete_id + * @return + */ bool EntityList::RemoveMob(uint16 delete_id) { - if (delete_id == 0) + if (delete_id == 0) { return true; + } auto it = mob_list.find(delete_id); if (it != mob_list.end()) { RemoveMobFromCloseLists(it->second); - if (npc_list.count(delete_id)) + if (npc_list.count(delete_id)) { entity_list.RemoveNPC(delete_id); - else if (client_list.count(delete_id)) + } + else if (client_list.count(delete_id)) { entity_list.RemoveClient(delete_id); + } safe_delete(it->second); - if (!corpse_list.count(delete_id)) + if (!corpse_list.count(delete_id)) { free_ids.push(it->first); + } mob_list.erase(it); return true; } return false; } -// This is for if the ID is deleted for some reason +/** + * @param delete_mob + * @return + */ bool EntityList::RemoveMob(Mob *delete_mob) { if (delete_mob == 0) { @@ -2533,19 +2544,19 @@ bool EntityList::RemoveMob(Mob *delete_mob) return false; } +/** + * @param delete_id + * @return + */ bool EntityList::RemoveNPC(uint16 delete_id) { auto it = npc_list.find(delete_id); if (it != npc_list.end()) { NPC *npc = it->second; - // make sure its proximity is removed RemoveProximity(delete_id); - // remove from client close lists RemoveMobFromCloseLists(npc->CastToMob()); - // remove from the list npc_list.erase(it); - // remove from limit list if needed if (npc_limit_list.count(delete_id)) { npc_limit_list.erase(delete_id); } @@ -2561,11 +2572,14 @@ bool EntityList::RemoveNPC(uint16 delete_id) */ bool EntityList::RemoveMobFromCloseLists(Mob *mob) { + LogDebug("Removing mob [{}] from close lists", mob->GetCleanName()); + auto it = mob_list.begin(); while (it != mob_list.end()) { it->second->close_mobs.erase(mob); ++it; } + return false; } diff --git a/zone/entity.h b/zone/entity.h index 561e9387a..94884167c 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -388,11 +388,37 @@ public: void QueueToGroupsForNPCHealthAA(Mob* sender, const EQApplicationPacket* app); void QueueManaged(Mob* sender, const EQApplicationPacket* app, bool ignore_sender=false, bool ackreq = true); - void AEAttack(Mob *attacker, float dist, int Hand = EQEmu::invslot::slotPrimary, int count = 0, bool IsFromSpell = false); - void AETaunt(Client *caster, float range=0, int32 bonus_hate=0); - void AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true, int16 resist_adjust = 0, int *max_targets = nullptr); - void MassGroupBuff(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true); - void AEBardPulse(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true); + void AEAttack( + Mob *attacker, + float dist, + int Hand = EQEmu::invslot::slotPrimary, + int count = 0, + bool IsFromSpell = false + ); + void AETaunt(Client *caster, float range = 0, int32 bonus_hate = 0); + void AESpell( + Mob *caster, + Mob *center, + uint16 spell_id, + bool affect_caster = true, + int16 resist_adjust = 0, + int *max_targets = nullptr + ); + static bool AESpellFilterCriteria( + Mob *current_mob, + Mob *caster_mob, + Mob *center_mob, + uint16 spell_id, + int *max_targets, + int &max_targets_allowed, + int &target_hit_counter, + float &distance_to_target, + const glm::vec3 &cast_target_position, + bool affect_caster = true, + int16 resist_adjust = 0 + ); + void MassGroupBuff(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true); + void AEBardPulse(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true); //trap stuff Mob* GetTrapTrigger(Trap* trap); diff --git a/zone/spells.cpp b/zone/spells.cpp index cec6e8142..07de6b715 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -4793,24 +4793,32 @@ int16 Mob::CalcFearResistChance() return resistchance; } -float Mob::GetAOERange(uint16 spell_id) { - float range; +/** + * @param spell_id + * @return + */ +float Mob::GetAOERange(uint16 spell_id) +{ + float range = spells[spell_id].aoerange; - range = spells[spell_id].aoerange; - if(range == 0) //for TGB spells, they prolly do not have an aoe range + /** + * For TGB + */ + if (range == 0) { range = spells[spell_id].range; - if(range == 0) - range = 10; //something.... - - if(IsBardSong(spell_id) && IsBeneficialSpell(spell_id)) { - //Live AA - Extended Notes, SionachiesCrescendo - float song_bonus = static_cast(aabonuses.SongRange + spellbonuses.SongRange + itembonuses.SongRange); - range += range*song_bonus /100.0f; } - range = GetActSpellRange(spell_id, range); + if (range == 0) { + range = 10; + } - return(range); + if (IsBardSong(spell_id) && IsBeneficialSpell(spell_id)) { + //Live AA - Extended Notes, SionachiesCrescendo + float song_bonus = static_cast(aabonuses.SongRange + spellbonuses.SongRange + itembonuses.SongRange); + range += range * song_bonus / 100.0f; + } + + return GetActSpellRange(spell_id, range); } /////////////////////////////////////////////////////////////////////////////// diff --git a/zone/zone.cpp b/zone/zone.cpp index 99c120cab..b88ddd071 100755 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -1997,7 +1997,6 @@ const char *Zone::GetSpellBlockedMessage(uint32 spell_id, const glm::vec3 &locat if (spell_id != blocked_spells[x].spellid && blocked_spells[x].spellid != 0) { continue; } - switch (blocked_spells[x].type) { case ZoneBlockedSpellTypes::ZoneWide: { return blocked_spells[x].message; @@ -2033,21 +2032,21 @@ void Zone::SetInstanceTimer(uint32 new_duration) void Zone::LoadLDoNTraps() { - const std::string query = "SELECT id, type, spell_id, skill, locked FROM ldon_trap_templates"; - auto results = database.QueryDatabase(query); - if (!results.Success()) { + const std::string query = "SELECT id, type, spell_id, skill, locked FROM ldon_trap_templates"; + auto results = database.QueryDatabase(query); + if (!results.Success()) { return; - } + } - for (auto row = results.begin();row != results.end(); ++row) { - auto lt = new LDoNTrapTemplate; - lt->id = atoi(row[0]); - lt->type = (LDoNChestTypes)atoi(row[1]); - lt->spell_id = atoi(row[2]); - lt->skill = atoi(row[3]); - lt->locked = atoi(row[4]); - ldon_trap_list[lt->id] = lt; - } + for (auto row = results.begin(); row != results.end(); ++row) { + auto lt = new LDoNTrapTemplate; + lt->id = atoi(row[0]); + lt->type = (LDoNChestTypes) atoi(row[1]); + lt->spell_id = atoi(row[2]); + lt->skill = atoi(row[3]); + lt->locked = atoi(row[4]); + ldon_trap_list[lt->id] = lt; + } }