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) "
);
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), "

View File

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

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 [{}]",
((killer_mob) ? (killer_mob->GetName()) : ("[nullptr]")), damage, spell, attack_skill);
entity_list.RemoveMobFromCloseLists(CastToMob());
Mob *oos = nullptr;
if (killer_mob) {
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.
// 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<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 };
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<glm::vec2>(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<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
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

View File

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

View File

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

View File

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