From 5ac23a2f8f5bff69f13dcc0470dfb392ddb806b0 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 20 Dec 2013 17:47:28 -0500 Subject: [PATCH] Added facing check to auto attack LoS code The Mob::InFrontMob check uses 56 degrees which is where the client will generate the "You cannon see your target." message. There were still a few weird angles that I was able to still attack, but in like 99% of the cases it should work ... --- zone/client.cpp | 2 ++ zone/client.h | 2 ++ zone/client_packet.cpp | 10 ++++++---- zone/client_process.cpp | 35 +++++++++++++++++++---------------- zone/mob.cpp | 40 ++++++++++++++++++++++++---------------- zone/mob.h | 8 +++++++- 6 files changed, 60 insertions(+), 37 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index a9ac4968c..97f389667 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -284,11 +284,13 @@ Client::Client(EQStreamInterface* ieqs) aa_los_me.x = 0; aa_los_me.y = 0; aa_los_me.z = 0; + aa_los_me_heading = 0; aa_los_them.x = 0; aa_los_them.y = 0; aa_los_them.z = 0; aa_los_them_mob = nullptr; los_status = false; + los_status_facing = false; qGlobals = nullptr; HideCorpseMode = HideCorpseNone; PendingGuildInvitation = false; diff --git a/zone/client.h b/zone/client.h index 8317c0011..bf3ad7bd2 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1200,6 +1200,8 @@ protected: VERTEX aa_los_them; Mob *aa_los_them_mob; bool los_status; + float aa_los_me_heading; + bool los_status_facing; QGlobalCache *qGlobals; /** Adventure Variables **/ diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 97c7e1277..25b638b52 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1303,6 +1303,7 @@ void Client::Handle_OP_AutoAttack(const EQApplicationPacket *app) aa_los_me.x = 0; aa_los_me.y = 0; aa_los_me.z = 0; + aa_los_me_heading = 0; aa_los_them.x = 0; aa_los_them.y = 0; aa_los_them.z = 0; @@ -1322,24 +1323,25 @@ void Client::Handle_OP_AutoAttack(const EQApplicationPacket *app) aa_los_me.x = GetX(); aa_los_me.y = GetY(); aa_los_me.z = GetZ(); + aa_los_me_heading = GetHeading(); aa_los_them.x = aa_los_them_mob->GetX(); aa_los_them.y = aa_los_them_mob->GetY(); aa_los_them.z = aa_los_them_mob->GetZ(); - if(CheckLosFN(aa_los_them_mob)) - los_status = true; - else - los_status = false; + los_status = CheckLosFN(aa_los_them_mob); + los_status_facing = aa_los_them_mob->InFrontMob(this, aa_los_them.x, aa_los_them.y); } else { aa_los_me.x = GetX(); aa_los_me.y = GetY(); aa_los_me.z = GetZ(); + aa_los_me_heading = GetHeading(); aa_los_them.x = 0; aa_los_them.y = 0; aa_los_them.z = 0; aa_los_them_mob = nullptr; los_status = false; + los_status_facing = false; } } } diff --git a/zone/client_process.cpp b/zone/client_process.cpp index a5da141ce..0462089e5 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -299,7 +299,7 @@ bool Client::Process() { if(ranged->GetItem() && ranged->GetItem()->ItemType == ItemTypeBow){ if(ranged_timer.Check(false)){ if(GetTarget() && (GetTarget()->IsNPC() || GetTarget()->IsClient())){ - if(!GetTarget()->BehindMob(this, GetTarget()->GetX(), GetTarget()->GetY())){ + if(GetTarget()->InFrontMob(this, GetTarget()->GetX(), GetTarget()->GetY())){ if(CheckLosFN(GetTarget())){ //client has built in los check, but auto fire does not.. done last. RangedAttack(GetTarget()); @@ -319,7 +319,7 @@ bool Client::Process() { else if(ranged->GetItem() && (ranged->GetItem()->ItemType == ItemTypeLargeThrowing || ranged->GetItem()->ItemType == ItemTypeSmallThrowing)){ if(ranged_timer.Check(false)){ if(GetTarget() && (GetTarget()->IsNPC() || GetTarget()->IsClient())){ - if(!GetTarget()->BehindMob(this, GetTarget()->GetX(), GetTarget()->GetY())){ + if(GetTarget()->InFrontMob(this, GetTarget()->GetX(), GetTarget()->GetY())){ if(CheckLosFN(GetTarget())){ //client has built in los check, but auto fire does not.. done last. ThrowingAttack(GetTarget()); @@ -359,10 +359,15 @@ bool Client::Process() { aa_los_them.x = aa_los_them_mob->GetX(); aa_los_them.y = aa_los_them_mob->GetY(); aa_los_them.z = aa_los_them_mob->GetZ(); - if(CheckLosFN(auto_attack_target)) - los_status = true; - else - los_status = false; + los_status = CheckLosFN(auto_attack_target); + aa_los_me_heading = GetHeading(); + los_status_facing = aa_los_them_mob->InFrontMob(this, aa_los_them.x, aa_los_them.y); + } + // If only our heading changes, we can skip the CheckLosFN call + // but above we still need to update los_status_facing + if (aa_los_me_heading != GetHeading()) { + aa_los_me_heading = GetHeading(); + los_status_facing = aa_los_them_mob->InFrontMob(this, aa_los_them.x, aa_los_them.y); } } else @@ -371,25 +376,23 @@ bool Client::Process() { aa_los_me.x = GetX(); aa_los_me.y = GetY(); aa_los_me.z = GetZ(); + aa_los_me_heading = GetHeading(); aa_los_them.x = aa_los_them_mob->GetX(); aa_los_them.y = aa_los_them_mob->GetY(); aa_los_them.z = aa_los_them_mob->GetZ(); - if(CheckLosFN(auto_attack_target)) - los_status = true; - else - los_status = false; + los_status = CheckLosFN(auto_attack_target); + los_status_facing = aa_los_them_mob->InFrontMob(this, aa_los_them.x, aa_los_them.y); } if (!CombatRange(auto_attack_target)) { - //duplicate message not wanting to see it. - //Message_StringID(MT_TooFarAway,TARGET_TOO_FAR); + Message_StringID(MT_TooFarAway,TARGET_TOO_FAR); } else if (auto_attack_target == this) { Message_StringID(MT_TooFarAway,TRY_ATTACKING_SOMEONE); } - else if (!los_status) + else if (!los_status || !los_status_facing) { //you can't see your target } @@ -482,13 +485,13 @@ bool Client::Process() { // Range check if(!CombatRange(auto_attack_target)) { // this is a duplicate message don't use it. - Message_StringID(MT_TooFarAway,TARGET_TOO_FAR); + //Message_StringID(MT_TooFarAway,TARGET_TOO_FAR); } // Don't attack yourself else if(auto_attack_target == this) { - Message_StringID(MT_TooFarAway,TRY_ATTACKING_SOMEONE); + //Message_StringID(MT_TooFarAway,TRY_ATTACKING_SOMEONE); } - else if (!los_status) + else if (!los_status || !los_status_facing) { //you can't see your target } diff --git a/zone/mob.cpp b/zone/mob.cpp index f38b2a48b..d0c12586b 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -1876,35 +1876,43 @@ void Mob::SetOwnerID(uint16 NewOwnerID) { this->Depop(); } -//heko: for backstab -bool Mob::BehindMob(Mob* other, float playerx, float playery) const { - if (!other) - return true; // sure your behind your invisible friend?? (fall thru for sneak) - //see if player is behind mob - float angle, lengthb, vectorx, vectory; - float mobx = -(other->GetX()); // mob xlocation (inverse because eq is confused) - float moby = other->GetY(); // mobylocation +// used in checking for behind (backstab) and checking in front (melee LoS) +float Mob::MobAngle(Mob *other, float ourx, float oury) const { + if (!other || other == this) + return 0.0f; + + float angle, lengthb, vectorx, vectory, dotp; + float mobx = -(other->GetX()); // mob xloc (inverse because eq) + float moby = other->GetY(); // mob yloc float heading = other->GetHeading(); // mob heading heading = (heading * 360.0f) / 256.0f; // convert to degrees if (heading < 270) heading += 90; else heading -= 270; + heading = heading * 3.1415f / 180.0f; // convert to radians vectorx = mobx + (10.0f * cosf(heading)); // create a vector based on heading vectory = moby + (10.0f * sinf(heading)); // of mob length 10 - //length of mob to player vector - //lengthb = (float)sqrtf(pow((-playerx-mobx),2) + pow((playery-moby),2)); - lengthb = (float) sqrtf( ( (-playerx-mobx) * (-playerx-mobx) ) + ( (playery-moby) * (playery-moby) ) ); + // length of mob to player vector + lengthb = (float) sqrtf(((-ourx - mobx) * (-ourx - mobx)) + ((oury - moby) * (oury - moby))); // calculate dot product to get angle - angle = acosf(((vectorx-mobx)*(-playerx-mobx)+(vectory-moby)*(playery-moby)) / (10 * lengthb)); + // Handle acos domain errors due to floating point rounding errors + dotp = ((vectorx - mobx) * (-ourx - mobx) + + (vectory - moby) * (oury - moby)) / (10 * lengthb); + // I haven't seen any errors that cause problems that weren't slightly + // larger/smaller than 1/-1, so only handle these cases for now + if (dotp > 1) + return 0.0f; + else if (dotp < -1) + return 180.0f; + + angle = acosf(dotp); angle = angle * 180.0f / 3.1415f; - if (angle > 90.0f) //not sure what value to use (90*2=180 degrees is front) - return true; - else - return false; + + return angle; } void Mob::SetZone(uint32 zone_id, uint32 instance_id) diff --git a/zone/mob.h b/zone/mob.h index 6aea08b5c..acb42c10e 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -106,7 +106,13 @@ public: //Attack virtual void RogueBackstab(Mob* other, bool min_damage = false, int ReuseTime = 10); virtual void RogueAssassinate(Mob* other); // solar - bool BehindMob(Mob* other = 0, float playerx = 0.0f, float playery = 0.0f) const; + float MobAngle(Mob *other = 0, float ourx = 0.0f, float oury = 0.0f) const; + // greater than 90 is behind + inline bool BehindMob(Mob *other = 0, float ourx = 0.0f, float oury = 0.0f) const + { return (!other || other == this) ? true : MobAngle(other, ourx, oury) > 90.0f; } + // less than 56 is in front, greater than 56 is usually where the client generates the messages + inline bool InFrontMob(Mob *other = 0, float ourx = 0.0f, float oury = 0.0f) const + { return (!other || other == this) ? true : MobAngle(other, ourx, oury) < 56.0f; } virtual void RangedAttack(Mob* other) { } virtual void ThrowingAttack(Mob* other) { } uint16 GetThrownDamage(int16 wDmg, int32& TotalDmg, int& minDmg);