Merge branch 'master' of github.com:EQEmu/Server

This commit is contained in:
KimLS 2017-07-06 18:15:02 -07:00
commit bfae4273c2
16 changed files with 96 additions and 51 deletions

View File

@ -1,5 +1,12 @@
EQEMu Changelog (Started on Sept 24, 2003 15:50) EQEMu Changelog (Started on Sept 24, 2003 15:50)
------------------------------------------------------- -------------------------------------------------------
== 7/1/2017 ==
Akkadius: Resolve issues with NPC's hopping to the ceiling in small corridors
Akkadius: Improved grounding issues with NPC's during combat
Akkadius: Improved scenarios where NPC's need to be dragged out of the ground - they should correct themselves far more consistently
- Scenarios where an NPC is coming up from the bottom floor, or from the top floor, they will correct much better
- A video of these tests can be found here: https://www.youtube.com/watch?v=HtC7bVNM7ZQ&feature=youtu.be
== 6/28/2017 == == 6/28/2017 ==
Akkadius: Fixed issues with Z correctness when NPCs are pathing on normal grids Akkadius: Fixed issues with Z correctness when NPCs are pathing on normal grids
Akkadius: Fixed issues with Z correctness when NPCs are engaged with players following Akkadius: Fixed issues with Z correctness when NPCs are engaged with players following

View File

@ -275,6 +275,7 @@ RULE_BOOL(Map, FixPathingZAtWaypoints, false) //alternative to `WhenLoading`, ac
RULE_BOOL(Map, FixPathingZWhenMoving, false) //very CPU intensive, but helps hopping with widely spaced waypoints. RULE_BOOL(Map, FixPathingZWhenMoving, false) //very CPU intensive, but helps hopping with widely spaced waypoints.
RULE_BOOL(Map, FixPathingZOnSendTo, false) //try to repair Z coords in the SendTo routine as well. RULE_BOOL(Map, FixPathingZOnSendTo, false) //try to repair Z coords in the SendTo routine as well.
RULE_BOOL(Map, FixZWhenMoving, true) // Automatically fix NPC Z coordinates when moving/pathing/engaged (Far less CPU intensive than its predecessor) RULE_BOOL(Map, FixZWhenMoving, true) // Automatically fix NPC Z coordinates when moving/pathing/engaged (Far less CPU intensive than its predecessor)
RULE_BOOL(Map, MobZVisualDebug, false) // Displays spell effects determining whether or not NPC is hitting Best Z calcs (blue for hit, red for miss)
RULE_REAL(Map, FixPathingZMaxDeltaMoving, 20) //at runtime while pathing: max change in Z to allow the BestZ code to apply. RULE_REAL(Map, FixPathingZMaxDeltaMoving, 20) //at runtime while pathing: max change in Z to allow the BestZ code to apply.
RULE_REAL(Map, FixPathingZMaxDeltaWaypoint, 20) //at runtime at each waypoint: max change in Z to allow the BestZ code to apply. RULE_REAL(Map, FixPathingZMaxDeltaWaypoint, 20) //at runtime at each waypoint: max change in Z to allow the BestZ code to apply.
RULE_REAL(Map, FixPathingZMaxDeltaSendTo, 20) //at runtime in SendTo: max change in Z to allow the BestZ code to apply. RULE_REAL(Map, FixPathingZMaxDeltaSendTo, 20) //at runtime in SendTo: max change in Z to allow the BestZ code to apply.
@ -349,6 +350,7 @@ RULE_INT(Spells, MaxTotalSlotsNPC, 60) // default to Tit's limit
RULE_INT(Spells, MaxTotalSlotsPET, 30) // default to Tit's limit RULE_INT(Spells, MaxTotalSlotsPET, 30) // default to Tit's limit
RULE_BOOL (Spells, EnableBlockedBuffs, true) RULE_BOOL (Spells, EnableBlockedBuffs, true)
RULE_INT(Spells, ReflectType, 3) //0 = disabled, 1 = single target player spells only, 2 = all player spells, 3 = all single target spells, 4 = all spells RULE_INT(Spells, ReflectType, 3) //0 = disabled, 1 = single target player spells only, 2 = all player spells, 3 = all single target spells, 4 = all spells
RULE_BOOL(Spells, ReflectMessagesClose, true) // Live functionality is for Reflect messages to show to players within close proximity, false shows just player reflecting
RULE_INT(Spells, VirusSpreadDistance, 30) // The distance a viral spell will jump to its next victim RULE_INT(Spells, VirusSpreadDistance, 30) // The distance a viral spell will jump to its next victim
RULE_BOOL(Spells, LiveLikeFocusEffects, true) // Determines whether specific healing, dmg and mana reduction focuses are randomized RULE_BOOL(Spells, LiveLikeFocusEffects, true) // Determines whether specific healing, dmg and mana reduction focuses are randomized
RULE_INT(Spells, BaseImmunityLevel, 55) // The level that targets start to be immune to stun, fear and mez spells with a max level of 0. RULE_INT(Spells, BaseImmunityLevel, 55) // The level that targets start to be immune to stun, fear and mez spells with a max level of 0.

View File

@ -2388,12 +2388,6 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQEmu::skills::Skil
entity_list.UnMarkNPC(GetID()); entity_list.UnMarkNPC(GetID());
entity_list.RemoveNPC(GetID()); entity_list.RemoveNPC(GetID());
/* Fix Z on Corpse Creation */
glm::vec3 dest(m_Position.x, m_Position.y, m_Position.z);
float new_z = zone->zonemap->FindBestZ(dest, nullptr);
corpse->SetFlyMode(1);
corpse->GMMove(m_Position.x, m_Position.y, new_z + 5, m_Position.w);
this->SetID(0); this->SetID(0);
if (killer != 0 && emoteid != 0) if (killer != 0 && emoteid != 0)

View File

@ -96,7 +96,9 @@ bool Beacon::Process()
Mob *caster = entity_list.GetMob(caster_id); Mob *caster = entity_list.GetMob(caster_id);
if(caster && spell_iterations-- && max_targets) if(caster && spell_iterations-- && max_targets)
{ {
bool affect_caster = (!caster->IsNPC() && !caster->IsAIControlled()); //NPC AE spells do not affect the NPC caster // NPCs should never be affected by an AE they cast. PB AEs shouldn't affect caster either
// I don't think any other cases that get here matter
bool affect_caster = (!caster->IsNPC() && !caster->IsAIControlled()) && spells[spell_id].targettype != ST_AECaster;
entity_list.AESpell(caster, this, spell_id, affect_caster, resist_adjust, &max_targets); entity_list.AESpell(caster, this, spell_id, affect_caster, resist_adjust, &max_targets);
} }
else else

View File

@ -48,6 +48,14 @@ void Mob::CalcBonuses()
SetAttackTimer(); SetAttackTimer();
CalcAC(); CalcAC();
/* Fast walking NPC's are prone to disappear into walls/hills
We set this here because NPC's can cast spells to change walkspeed/runspeed
*/
float get_walk_speed = static_cast<float>(0.025f * this->GetWalkspeed());
if (get_walk_speed >= 0.9 && this->fix_z_timer.GetDuration() != 100) {
this->fix_z_timer.SetTimer(100);
}
rooted = FindType(SE_Root); rooted = FindType(SE_Root);
} }

View File

@ -4242,7 +4242,7 @@ bool Client::GroupFollow(Client* inviter) {
RemoveAutoXTargets(); RemoveAutoXTargets();
} }
SetXTargetAutoMgr(GetXTargetAutoMgr()); SetXTargetAutoMgr(raid->GetXTargetAutoMgr());
if (!GetXTargetAutoMgr()->empty()) if (!GetXTargetAutoMgr()->empty())
SetDirtyAutoHaters(); SetDirtyAutoHaters();

View File

@ -173,6 +173,7 @@ int command_init(void)
command_add("checklos", "- Check for line of sight to your target", 50, command_checklos) || command_add("checklos", "- Check for line of sight to your target", 50, command_checklos) ||
command_add("clearinvsnapshots", "[use rule] - Clear inventory snapshot history (true - elapsed entries, false - all entries)", 200, command_clearinvsnapshots) || command_add("clearinvsnapshots", "[use rule] - Clear inventory snapshot history (true - elapsed entries, false - all entries)", 200, command_clearinvsnapshots) ||
command_add("corpse", "- Manipulate corpses, use with no arguments for help", 50, command_corpse) || command_add("corpse", "- Manipulate corpses, use with no arguments for help", 50, command_corpse) ||
command_add("corpsefix", "Attempts to bring corpses from underneath the ground within close proximity of the player", 0, command_corpsefix) ||
command_add("crashtest", "- Crash the zoneserver", 255, command_crashtest) || command_add("crashtest", "- Crash the zoneserver", 255, command_crashtest) ||
command_add("cvs", "- Summary of client versions currently online.", 200, command_cvs) || command_add("cvs", "- Summary of client versions currently online.", 200, command_cvs) ||
command_add("damage", "[amount] - Damage your target", 100, command_damage) || command_add("damage", "[amount] - Damage your target", 100, command_damage) ||
@ -2977,6 +2978,11 @@ void command_reloadqst(Client *c, const Seperator *sep)
} }
void command_corpsefix(Client *c, const Seperator *sep)
{
entity_list.CorpseFix(c);
}
void command_reloadworld(Client *c, const Seperator *sep) void command_reloadworld(Client *c, const Seperator *sep)
{ {
c->Message(0, "Reloading quest cache and repopping zones worldwide."); c->Message(0, "Reloading quest cache and repopping zones worldwide.");

View File

@ -72,6 +72,7 @@ void command_checklos(Client *c, const Seperator *sep);
void command_clearinvsnapshots(Client *c, const Seperator *sep); void command_clearinvsnapshots(Client *c, const Seperator *sep);
void command_connectworldserver(Client *c, const Seperator *sep); void command_connectworldserver(Client *c, const Seperator *sep);
void command_corpse(Client *c, const Seperator *sep); void command_corpse(Client *c, const Seperator *sep);
void command_corpsefix(Client *c, const Seperator *sep);
void command_crashtest(Client *c, const Seperator *sep); void command_crashtest(Client *c, const Seperator *sep);
void command_cvs(Client *c, const Seperator *sep); void command_cvs(Client *c, const Seperator *sep);
void command_d1(Client *c, const Seperator *sep); void command_d1(Client *c, const Seperator *sep);

View File

@ -730,8 +730,6 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_
// test to fix possible cause of random zone crashes..external methods accessing client properties before they're initialized // test to fix possible cause of random zone crashes..external methods accessing client properties before they're initialized
if (curmob->IsClient() && !curmob->CastToClient()->ClientFinishedLoading()) if (curmob->IsClient() && !curmob->CastToClient()->ClientFinishedLoading())
continue; continue;
if (curmob == center) //do not affect center
continue;
if (curmob == caster && !affect_caster) //watch for caster too if (curmob == caster && !affect_caster) //watch for caster too
continue; continue;
if (spells[spell_id].targettype == ST_TargetAENoPlayersPets && curmob->IsPetOwnerClient()) if (spells[spell_id].targettype == ST_TargetAENoPlayersPets && curmob->IsPetOwnerClient())

View File

@ -2845,6 +2845,22 @@ int32 EntityList::DeleteNPCCorpses()
return x; return x;
} }
void EntityList::CorpseFix(Client* c)
{
auto it = corpse_list.begin();
while (it != corpse_list.end()) {
Corpse* corpse = it->second;
if (corpse->IsNPCCorpse()) {
if (DistanceNoZ(c->GetPosition(), corpse->GetPosition()) < 100) {
c->Message(15, "Attempting to fix %s", it->second->GetCleanName());
corpse->GMMove(corpse->GetX(), corpse->GetY(), c->GetZ() + 2, 0);
}
}
++it;
}
}
// returns the number of corpses deleted. A negative number indicates an error code. // returns the number of corpses deleted. A negative number indicates an error code.
int32 EntityList::DeletePlayerCorpses() int32 EntityList::DeletePlayerCorpses()
{ {

View File

@ -386,6 +386,7 @@ public:
void FindPathsToAllNPCs(); void FindPathsToAllNPCs();
int32 DeleteNPCCorpses(); int32 DeleteNPCCorpses();
int32 DeletePlayerCorpses(); int32 DeletePlayerCorpses();
void CorpseFix(Client* c);
void WriteEntityIDs(); void WriteEntityIDs();
void HalveAggro(Mob* who); void HalveAggro(Mob* who);
void DoubleAggro(Mob* who); void DoubleAggro(Mob* who);

View File

@ -113,7 +113,7 @@ Mob::Mob(const char* in_name,
tmHidden(-1), tmHidden(-1),
mitigation_ac(0), mitigation_ac(0),
m_specialattacks(eSpecialAttacks::None), m_specialattacks(eSpecialAttacks::None),
fix_z_timer(1000), fix_z_timer(300),
fix_z_timer_engaged(100) fix_z_timer_engaged(100)
{ {
targeted = 0; targeted = 0;
@ -121,6 +121,8 @@ Mob::Mob(const char* in_name,
tar_vector=0; tar_vector=0;
currently_fleeing = false; currently_fleeing = false;
last_z = 0;
AI_Init(); AI_Init();
SetMoving(false); SetMoving(false);
moved=false; moved=false;

View File

@ -492,6 +492,7 @@ public:
inline const float GetTarVZ() const { return m_TargetV.z; } inline const float GetTarVZ() const { return m_TargetV.z; }
inline const float GetTarVector() const { return tar_vector; } inline const float GetTarVector() const { return tar_vector; }
inline const uint8 GetTarNDX() const { return tar_ndx; } inline const uint8 GetTarNDX() const { return tar_ndx; }
inline const int8 GetFlyMode() const { return flymode; }
bool IsBoat() const; bool IsBoat() const;
//Group //Group
@ -1067,6 +1068,8 @@ public:
int GetWeaponDamage(Mob *against, const EQEmu::ItemData *weapon_item); int GetWeaponDamage(Mob *against, const EQEmu::ItemData *weapon_item);
int GetWeaponDamage(Mob *against, const EQEmu::ItemInstance *weapon_item, uint32 *hate = nullptr); int GetWeaponDamage(Mob *against, const EQEmu::ItemInstance *weapon_item, uint32 *hate = nullptr);
float last_z;
// Bots HealRotation methods // Bots HealRotation methods
#ifdef BOTS #ifdef BOTS
bool IsHealRotationTarget() { return (m_target_of_heal_rotation.use_count() && m_target_of_heal_rotation.get()); } bool IsHealRotationTarget() { return (m_target_of_heal_rotation.use_count() && m_target_of_heal_rotation.get()); }

View File

@ -998,9 +998,16 @@ void Mob::AI_Process() {
/* Fix Z when following during pull, not when engaged and stationary */ /* Fix Z when following during pull, not when engaged and stationary */
if (moving && fix_z_timer_engaged.Check()) if (moving && fix_z_timer_engaged.Check())
if(this->GetTarget()) if (this->GetTarget()) {
if(DistanceNoZ(this->GetPosition(), this->GetTarget()->GetPosition()) > 50) /* If we are engaged, moving and following client, let's look for best Z more often */
if (DistanceNoZ(this->GetPosition(), this->GetTarget()->GetPosition()) > 50) {
this->FixZ(); this->FixZ();
}
/* If we are close to client and our Z differences aren't big, match the client */
else if (std::abs(this->GetZ() - this->GetTarget()->GetZ()) <= 5 && this->GetTarget()->IsClient()) {
this->m_Position.z = this->GetTarget()->GetZ();
}
}
if (!(m_PlayerState & static_cast<uint32>(PlayerState::Aggressive))) if (!(m_PlayerState & static_cast<uint32>(PlayerState::Aggressive)))
SendAddPlayerState(PlayerState::Aggressive); SendAddPlayerState(PlayerState::Aggressive);

View File

@ -2241,7 +2241,9 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui
if(ae_center && ae_center == this && IsBeneficialSpell(spell_id)) if(ae_center && ae_center == this && IsBeneficialSpell(spell_id))
SpellOnTarget(spell_id, this); SpellOnTarget(spell_id, this);
bool affect_caster = !IsNPC(); //NPC AE spells do not affect the NPC caster // NPCs should never be affected by an AE they cast. PB AEs shouldn't affect caster either
// I don't think any other cases that get here matter
bool affect_caster = !IsNPC() && spells[spell_id].targettype != ST_AECaster;
if (spells[spell_id].targettype == ST_AETargetHateList) if (spells[spell_id].targettype == ST_AETargetHateList)
hate_list.SpellCast(this, spell_id, spells[spell_id].aoerange, ae_center); hate_list.SpellCast(this, spell_id, spells[spell_id].aoerange, ae_center);
@ -3804,8 +3806,22 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r
break; break;
} }
if (reflect_chance) { if (reflect_chance) {
entity_list.MessageClose_StringID(this, false, RuleI(Range, SpellMessages), MT_Spells,
SPELL_REFLECT, GetCleanName(), spelltar->GetCleanName()); if (RuleB(Spells, ReflectMessagesClose)) {
entity_list.MessageClose_StringID(
this, /* Sender */
false, /* Skip Sender */
RuleI(Range, SpellMessages), /* Range */
MT_Spells, /* Type */
SPELL_REFLECT, /* String ID */
GetCleanName(), /* Message 1 */
spelltar->GetCleanName() /* Message 2 */
);
}
else {
Message_StringID(MT_Spells, SPELL_REFLECT, GetCleanName(), spelltar->GetCleanName());
}
CheckNumHitsRemaining(NumHit::ReflectSpell); CheckNumHitsRemaining(NumHit::ReflectSpell);
// caster actually appears to change // caster actually appears to change
// ex. During OMM fight you click your reflect mask and you get the recourse from the reflected // ex. During OMM fight you click your reflect mask and you get the recourse from the reflected

View File

@ -212,22 +212,6 @@ void NPC::UpdateWaypoint(int wp_index)
cur_wp_pause = cur->pause; cur_wp_pause = cur->pause;
Log(Logs::Detail, Logs::AI, "Next waypoint %d: (%.3f, %.3f, %.3f, %.3f)", wp_index, m_CurrentWayPoint.x, m_CurrentWayPoint.y, m_CurrentWayPoint.z, m_CurrentWayPoint.w); Log(Logs::Detail, Logs::AI, "Next waypoint %d: (%.3f, %.3f, %.3f, %.3f)", wp_index, m_CurrentWayPoint.x, m_CurrentWayPoint.y, m_CurrentWayPoint.z, m_CurrentWayPoint.w);
//fix up pathing Z
if (zone->HasMap() && RuleB(Map, FixPathingZAtWaypoints) && !IsBoat())
{
if (!RuleB(Watermap, CheckForWaterAtWaypoints) || !zone->HasWaterMap() ||
(zone->HasWaterMap() && !zone->watermap->InWater(glm::vec3(m_CurrentWayPoint))))
{
glm::vec3 dest(m_CurrentWayPoint.x, m_CurrentWayPoint.y, m_CurrentWayPoint.z);
float newz = zone->zonemap->FindBestZ(dest, nullptr);
if ((newz > -2000) && std::abs(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaWaypoint))
m_CurrentWayPoint.z = newz + 1;
}
}
} }
void NPC::CalculateNewWaypoint() void NPC::CalculateNewWaypoint()
@ -780,20 +764,6 @@ void NPC::AssignWaypoints(int32 grid)
newwp.y = atof(row[1]); newwp.y = atof(row[1]);
newwp.z = atof(row[2]); newwp.z = atof(row[2]);
if (zone->HasMap() && RuleB(Map, FixPathingZWhenLoading))
{
auto positon = glm::vec3(newwp.x, newwp.y, newwp.z);
if (!RuleB(Watermap, CheckWaypointsInWaterWhenLoading) || !zone->HasWaterMap() ||
(zone->HasWaterMap() && !zone->watermap->InWater(positon)))
{
glm::vec3 dest(newwp.x, newwp.y, newwp.z);
float newz = zone->zonemap->FindBestZ(dest, nullptr);
if ((newz > -2000) && std::abs(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaLoading))
newwp.z = newz + 1;
}
}
newwp.pause = atoi(row[3]); newwp.pause = atoi(row[3]);
newwp.heading = atof(row[4]); newwp.heading = atof(row[4]);
Waypoints.push_back(newwp); Waypoints.push_back(newwp);
@ -870,6 +840,7 @@ void Mob::SendToFixZ(float new_x, float new_y, float new_z) {
} }
void Mob::FixZ() { void Mob::FixZ() {
BenchTimer timer; BenchTimer timer;
timer.reset(); timer.reset();
@ -878,9 +849,8 @@ void Mob::FixZ() {
if (!RuleB(Watermap, CheckForWaterWhenMoving) || !zone->HasWaterMap() || if (!RuleB(Watermap, CheckForWaterWhenMoving) || !zone->HasWaterMap() ||
(zone->HasWaterMap() && !zone->watermap->InWater(glm::vec3(m_Position)))) (zone->HasWaterMap() && !zone->watermap->InWater(glm::vec3(m_Position))))
{ {
glm::vec3 dest(m_Position.x, m_Position.y, m_Position.z); /* Any more than 5 in the offset makes NPC's hop/snap to ceiling in small corridors */
float new_z = this->FindGroundZ(m_Position.x, m_Position.y, 5);
float new_z = zone->zonemap->FindBestZ(dest, nullptr);
auto duration = timer.elapsed(); auto duration = timer.elapsed();
@ -896,8 +866,20 @@ void Mob::FixZ() {
duration duration
); );
if ((new_z > -2000) && std::abs(new_z - dest.z) < RuleR(Map, FixPathingZMaxDeltaMoving)) if ((new_z > -2000) && std::abs(m_Position.z - new_z) < 35) {
m_Position.z = new_z + 1; if (RuleB(Map, MobZVisualDebug))
this->SendAppearanceEffect(78, 0, 0, 0, 0);
m_Position.z = new_z;
}
else {
if (RuleB(Map, MobZVisualDebug))
this->SendAppearanceEffect(103, 0, 0, 0, 0);
Log(Logs::General, Logs::Debug, "%s is failing to find Z %f", this->GetCleanName(), std::abs(m_Position.z - new_z));
}
last_z = m_Position.z;
} }
} }
} }