AE Scanning adjustments, testing

This commit is contained in:
Akkadius 2019-12-28 17:08:34 -06:00
parent 8cb51eb253
commit f9e822072f
8 changed files with 312 additions and 130 deletions

View File

@ -683,6 +683,7 @@ bool Database::SaveCharacterCreate(uint32 character_id, uint32 account_id, Playe
pp->RestTimer // " RestTimer) " pp->RestTimer // " RestTimer) "
); );
auto results = QueryDatabase(query); auto results = QueryDatabase(query);
/* Save Bind Points */ /* Save Bind Points */
query = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, slot)" 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), " " VALUES (%u, %u, %u, %f, %f, %f, %f, %i), "

View File

@ -87,8 +87,9 @@
bool IsTargetableAESpell(uint16 spell_id) 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 true;
}
return false; return false;
} }

View File

@ -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 [{}]", LogCombat("Fatal blow dealt by [{}] with [{}] damage, spell [{}], skill [{}]",
((killer_mob) ? (killer_mob->GetName()) : ("[nullptr]")), damage, spell, attack_skill); ((killer_mob) ? (killer_mob->GetName()) : ("[nullptr]")), damage, spell, attack_skill);
entity_list.RemoveMobFromCloseLists(CastToMob());
Mob *oos = nullptr; Mob *oos = nullptr;
if (killer_mob) { if (killer_mob) {
oos = killer_mob->GetOwnerOrSelf(); oos = killer_mob->GetOwnerOrSelf();

View File

@ -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. * 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) * @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); const auto &cast_target_position =
float dist2 = dist * dist; spells[spell_id].targettype == ST_Ring ?
float min_range2 = spells[spell_id].min_range * spells[spell_id].min_range; caster_mob->GetTargetRingLocation() :
float dist_targ = 0; static_cast<glm::vec3>(center_mob->GetPosition());
const auto &position = spells[spell_id].targettype == ST_Ring ? caster->GetTargetRingLocation() : static_cast<glm::vec3>(center->GetPosition()); /**
glm::vec2 min = { position.x - dist, position.y - dist }; * If using Old Rain Targets - there is no max target limitation
glm::vec2 max = { position.x + dist, position.y + dist }; */
if (RuleB(Spells, OldRainTargets)) {
max_targets = nullptr;
}
bool bad = IsDetrimentalSpell(spell_id); /**
bool isnpc = caster->IsNPC(); * Max AOE targets
*/
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
int max_targets_allowed = 0; // unlimited 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; max_targets_allowed = *max_targets;
else if (spells[spell_id].aemaxtargets) }
else if (spells[spell_id].aemaxtargets) {
max_targets_allowed = 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; 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) { for (auto &it : caster_mob->close_mobs) {
curmob = it->second; current_mob = it.first;
// 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<glm::vec2>(curmob->GetPosition()), min, max))
continue;
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; 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 current_mob->CalcSpellPowerDistanceMod(spell_id, distance_to_target);
FACTION_VALUE f = curmob->GetReverseFactionCon(caster); caster_mob->SpellOnTarget(spell_id, current_mob, false, true, resist_adjust);
if (bad) { }
//affect mobs that are on our hate list, or
//which have bad faction with us LogDebug("Done iterating [{}]", caster_mob->GetCleanName());
if (!(caster->CheckAggro(curmob) || f == FACTION_THREATENLY || f == FACTION_SCOWLS) )
continue; if (max_targets && max_targets_allowed) {
} else { *max_targets = *max_targets - target_hit_counter;
//only affect mobs we would assist. }
if (!(f <= FACTION_AMIABLE)) }
continue;
/**
* @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<glm::vec2>(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 else {
if (bad) { //only affect mobs we would assist.
if (!caster->IsAttackAllowed(curmob, true)) if (!(faction_value <= FACTION_AMIABLE)) {
continue; return false;
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;
} }
} }
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) 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); bool bad = IsDetrimentalSpell(spell_id);
for (auto it = mob_list.begin(); it != mob_list.end(); ++it) { for (auto & it : mob_list) {
curmob = it->second; curmob = it.second;
if (curmob == center) //do not affect center if (curmob == center) //do not affect center
continue; continue;
if (curmob == caster && !affect_caster) //watch for caster too if (curmob == caster && !affect_caster) //watch for caster too

View File

@ -2486,30 +2486,41 @@ void EntityList::RemoveAllEncounters()
} }
} }
/**
* @param delete_id
* @return
*/
bool EntityList::RemoveMob(uint16 delete_id) bool EntityList::RemoveMob(uint16 delete_id)
{ {
if (delete_id == 0) if (delete_id == 0) {
return true; return true;
}
auto it = mob_list.find(delete_id); auto it = mob_list.find(delete_id);
if (it != mob_list.end()) { if (it != mob_list.end()) {
RemoveMobFromCloseLists(it->second); RemoveMobFromCloseLists(it->second);
if (npc_list.count(delete_id)) if (npc_list.count(delete_id)) {
entity_list.RemoveNPC(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); entity_list.RemoveClient(delete_id);
}
safe_delete(it->second); safe_delete(it->second);
if (!corpse_list.count(delete_id)) if (!corpse_list.count(delete_id)) {
free_ids.push(it->first); free_ids.push(it->first);
}
mob_list.erase(it); mob_list.erase(it);
return true; return true;
} }
return false; return false;
} }
// This is for if the ID is deleted for some reason /**
* @param delete_mob
* @return
*/
bool EntityList::RemoveMob(Mob *delete_mob) bool EntityList::RemoveMob(Mob *delete_mob)
{ {
if (delete_mob == 0) { if (delete_mob == 0) {
@ -2533,19 +2544,19 @@ bool EntityList::RemoveMob(Mob *delete_mob)
return false; return false;
} }
/**
* @param delete_id
* @return
*/
bool EntityList::RemoveNPC(uint16 delete_id) bool EntityList::RemoveNPC(uint16 delete_id)
{ {
auto it = npc_list.find(delete_id); auto it = npc_list.find(delete_id);
if (it != npc_list.end()) { if (it != npc_list.end()) {
NPC *npc = it->second; NPC *npc = it->second;
// make sure its proximity is removed
RemoveProximity(delete_id); RemoveProximity(delete_id);
// remove from client close lists
RemoveMobFromCloseLists(npc->CastToMob()); RemoveMobFromCloseLists(npc->CastToMob());
// remove from the list
npc_list.erase(it); npc_list.erase(it);
// remove from limit list if needed
if (npc_limit_list.count(delete_id)) { if (npc_limit_list.count(delete_id)) {
npc_limit_list.erase(delete_id); npc_limit_list.erase(delete_id);
} }
@ -2561,11 +2572,14 @@ bool EntityList::RemoveNPC(uint16 delete_id)
*/ */
bool EntityList::RemoveMobFromCloseLists(Mob *mob) bool EntityList::RemoveMobFromCloseLists(Mob *mob)
{ {
LogDebug("Removing mob [{}] from close lists", mob->GetCleanName());
auto it = mob_list.begin(); auto it = mob_list.begin();
while (it != mob_list.end()) { while (it != mob_list.end()) {
it->second->close_mobs.erase(mob); it->second->close_mobs.erase(mob);
++it; ++it;
} }
return false; return false;
} }

View File

@ -388,11 +388,37 @@ public:
void QueueToGroupsForNPCHealthAA(Mob* sender, const EQApplicationPacket* app); void QueueToGroupsForNPCHealthAA(Mob* sender, const EQApplicationPacket* app);
void QueueManaged(Mob* sender, const EQApplicationPacket* app, bool ignore_sender=false, bool ackreq = true); 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 AEAttack(
void AETaunt(Client *caster, float range=0, int32 bonus_hate=0); Mob *attacker,
void AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true, int16 resist_adjust = 0, int *max_targets = nullptr); float dist,
void MassGroupBuff(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true); int Hand = EQEmu::invslot::slotPrimary,
void AEBardPulse(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true); 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 //trap stuff
Mob* GetTrapTrigger(Trap* trap); Mob* GetTrapTrigger(Trap* trap);

View File

@ -4793,24 +4793,32 @@ int16 Mob::CalcFearResistChance()
return resistchance; 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; 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<float>(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<float>(aabonuses.SongRange + spellbonuses.SongRange + itembonuses.SongRange);
range += range * song_bonus / 100.0f;
}
return GetActSpellRange(spell_id, range);
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////

View File

@ -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) { if (spell_id != blocked_spells[x].spellid && blocked_spells[x].spellid != 0) {
continue; continue;
} }
switch (blocked_spells[x].type) { switch (blocked_spells[x].type) {
case ZoneBlockedSpellTypes::ZoneWide: { case ZoneBlockedSpellTypes::ZoneWide: {
return blocked_spells[x].message; return blocked_spells[x].message;
@ -2033,21 +2032,21 @@ void Zone::SetInstanceTimer(uint32 new_duration)
void Zone::LoadLDoNTraps() void Zone::LoadLDoNTraps()
{ {
const std::string query = "SELECT id, type, spell_id, skill, locked FROM ldon_trap_templates"; const std::string query = "SELECT id, type, spell_id, skill, locked FROM ldon_trap_templates";
auto results = database.QueryDatabase(query); auto results = database.QueryDatabase(query);
if (!results.Success()) { if (!results.Success()) {
return; return;
} }
for (auto row = results.begin();row != results.end(); ++row) { for (auto row = results.begin(); row != results.end(); ++row) {
auto lt = new LDoNTrapTemplate; auto lt = new LDoNTrapTemplate;
lt->id = atoi(row[0]); lt->id = atoi(row[0]);
lt->type = (LDoNChestTypes)atoi(row[1]); lt->type = (LDoNChestTypes) atoi(row[1]);
lt->spell_id = atoi(row[2]); lt->spell_id = atoi(row[2]);
lt->skill = atoi(row[3]); lt->skill = atoi(row[3]);
lt->locked = atoi(row[4]); lt->locked = atoi(row[4]);
ldon_trap_list[lt->id] = lt; ldon_trap_list[lt->id] = lt;
} }
} }