From f3f034d94875e696802717332b50c3bcf05d5dd2 Mon Sep 17 00:00:00 2001 From: Uleat Date: Sun, 12 Mar 2017 21:46:16 -0400 Subject: [PATCH 1/7] Fix for a combat ability targeting error --- zone/special_attacks.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 40836c67c..d65d5ad35 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -1791,8 +1791,8 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte) } while(AtkRounds > 0) { - if (GetTarget()) - DoSpecialAttackDamage(GetTarget(), EQEmu::skills::SkillFrenzy, dmg, 0, dmg, ReuseTime); + if (ca_target!=this) + DoSpecialAttackDamage(ca_target, EQEmu::skills::SkillFrenzy, dmg, 0, dmg, ReuseTime); AtkRounds--; } From 3fa72218a421fe7429f48cb66d287c8eab7a1921 Mon Sep 17 00:00:00 2001 From: E Spause Date: Sun, 12 Mar 2017 23:08:59 -0400 Subject: [PATCH 2/7] Remove delay death from Mercs so they function like normal NPCs at 0 health. --- zone/merc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/merc.h b/zone/merc.h index e3eb6c029..18256faf8 100644 --- a/zone/merc.h +++ b/zone/merc.h @@ -250,7 +250,7 @@ public: inline virtual int32 GetStringMod() const { return itembonuses.stringedMod; } inline virtual int32 GetWindMod() const { return itembonuses.windMod; } - inline virtual int32 GetDelayDeath() const { return aabonuses.DelayDeath + spellbonuses.DelayDeath + itembonuses.DelayDeath + 11; } + inline virtual int32 GetDelayDeath() const { return aabonuses.DelayDeath + spellbonuses.DelayDeath + itembonuses.DelayDeath; } // "SET" Class Methods void SetMercData (uint32 templateID ); From 3dd0d43e9606cf8498340de8c1357b4e94ebfed5 Mon Sep 17 00:00:00 2001 From: E Spause Date: Sun, 12 Mar 2017 23:19:24 -0400 Subject: [PATCH 3/7] Add a rule, MaxClientsSimplifiedLogic which allows for P99-style IP restrictions if enabled. Opcode handler for OP_WorldLogout in char sel. Only uses ExemptAccountLimitStatus and MaxClientsPerIP. Also adds in an opcode handler so CLEs are cleaned up at char select when ESC is pressed. --- common/servertalk.h | 1 + world/client.cpp | 6 ++++++ world/clientlist.cpp | 19 ++++++++++++++++++- world/clientlist.h | 1 + world/login_server.cpp | 15 +++++++++++++++ 5 files changed, 41 insertions(+), 1 deletion(-) diff --git a/common/servertalk.h b/common/servertalk.h index b0197985f..528abfb4b 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -663,6 +663,7 @@ struct UsertoWorldRequest_Struct { uint32 worldid; uint32 FromID; uint32 ToID; + char IPAddr[64]; }; struct UsertoWorldResponse_Struct { diff --git a/world/client.cpp b/world/client.cpp index 45d65e494..c7385b41c 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -1022,6 +1022,12 @@ bool Client::HandlePacket(const EQApplicationPacket *app) { eqs->Close(); return true; } + case OP_WorldLogout: + { + eqs->Close(); + cle->SetOnline(CLE_Status_Offline); //allows this player to log in again without an ip restriction. + return false; + } case OP_ZoneChange: { // HoT sends this to world while zoning and wants it echoed back. diff --git a/world/clientlist.cpp b/world/clientlist.cpp index fd92051b3..23fb44fb0 100644 --- a/world/clientlist.cpp +++ b/world/clientlist.cpp @@ -214,6 +214,24 @@ void ClientList::GetCLEIP(uint32 iIP) { } } +uint32 ClientList::GetCLEIPCount(uint32 iIP) { + ClientListEntry* countCLEIPs = 0; + LinkedListIterator iterator(clientlist); + + int IPInstances = 0; + iterator.Reset(); + + while (iterator.MoreElements()) { + countCLEIPs = iterator.GetData(); + if ((countCLEIPs->GetIP() == iIP) && ((countCLEIPs->Admin() < (RuleI(World, ExemptMaxClientsStatus))) || (RuleI(World, ExemptMaxClientsStatus) < 0)) && countCLEIPs->Online() >= CLE_Status_Online) { // If the IP matches, and the connection admin status is below the exempt status, or exempt status is less than 0 (no-one is exempt) + IPInstances++; // Increment the occurences of this IP address + } + iterator.Advance(); + } + + return IPInstances; +} + void ClientList::DisconnectByIP(uint32 iIP) { ClientListEntry* countCLEIPs = 0; LinkedListIterator iterator(clientlist); @@ -252,7 +270,6 @@ ClientListEntry* ClientList::FindCharacter(const char* name) { return 0; } - ClientListEntry* ClientList::FindCLEByAccountID(uint32 iAccID) { LinkedListIterator iterator(clientlist); diff --git a/world/clientlist.h b/world/clientlist.h index 1d5fc6ab8..44ed70a08 100644 --- a/world/clientlist.h +++ b/world/clientlist.h @@ -56,6 +56,7 @@ public: ClientListEntry* FindCLEByCharacterID(uint32 iCharID); ClientListEntry* GetCLE(uint32 iID); void GetCLEIP(uint32 iIP); + uint32 GetCLEIPCount(uint32 iLSAccountID); void DisconnectByIP(uint32 iIP); void EnforceSessionLimit(uint32 iLSAccountID); void CLCheckStale(); diff --git a/world/login_server.cpp b/world/login_server.cpp index b8c23d573..ad5f4ec88 100644 --- a/world/login_server.cpp +++ b/world/login_server.cpp @@ -134,11 +134,26 @@ bool LoginServer::Process() { if( (int32)numplayers >= x && x != -1 && x != 255 && status < 80) utwrs->response = -3; + + if (pack->size == sizeof(UsertoWorldRequest_Struct)) + { + uint32 decimalIP = inet_addr(utwr->IPAddr); + + if (RuleB(World, MaxClientsSimplifiedLogic)) { + if (client_list.GetCLEIPCount(decimalIP) >= (RuleI(World, MaxClientsPerIP))) { + if ((status < (RuleI(World, ExemptMaxClientsStatus))) || (RuleI(World, ExemptMaxClientsStatus) < 0)) { + utwrs->response = -4; + } + } + } + } + if(status == -1) utwrs->response = -1; if(status == -2) utwrs->response = -2; + utwrs->worldid = utwr->worldid; SendPacket(outpack); delete outpack; From 09b9d398e2e089cec48d01cda515f32ef0fca89f Mon Sep 17 00:00:00 2001 From: E Spause Date: Sun, 12 Mar 2017 23:19:50 -0400 Subject: [PATCH 4/7] Rule itself for MaxClientsSimplifiedLogic. Defaults to disabled (false) --- common/ruletypes.h | 1 + 1 file changed, 1 insertion(+) diff --git a/common/ruletypes.h b/common/ruletypes.h index 1e280bbcc..002e2d829 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -225,6 +225,7 @@ RULE_INT(World, PVPSettings, 0) // Sets the PVP settings for the server, 1 = Ral RULE_BOOL (World, IsGMPetitionWindowEnabled, false) RULE_INT (World, FVNoDropFlag, 0) // Sets the Firiona Vie settings on the client. If set to 2, the flag will be set for GMs only, allowing trading of no-drop items. RULE_BOOL (World, IPLimitDisconnectAll, false) +RULE_BOOL(World, MaxClientsSimplifiedLogic, false) // New logic that only uses ExemptMaxClientsStatus and MaxClientsPerIP. Done on the loginserver. This mimics the P99-style special IP rules. RULE_INT (World, TellQueueSize, 20) RULE_BOOL(World, StartZoneSameAsBindOnCreation, true) //Should the start zone ALWAYS be the same location as your bind? RULE_CATEGORY_END() From 79f9433dfad274f3b73272bda2a6fd11ae673194 Mon Sep 17 00:00:00 2001 From: E Spause Date: Mon, 13 Mar 2017 06:26:48 -0400 Subject: [PATCH 5/7] Merc logic fix for heal spells - allows mercs to select fast healing spells as a 'standard' healing spell & new rule for unsuspend --- common/ruletypes.h | 1 + common/spdat.cpp | 2 +- zone/merc.cpp | 11 ++++++++++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index d0e2ff2be..7e461071f 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -163,6 +163,7 @@ RULE_INT(Mercs, AggroRadius, 100) // Determines the distance from which a merc RULE_INT(Mercs, AggroRadiusPuller, 25) // Determines the distance from which a merc will aggro group member's target, if they have the group role of puller (also used to determine the distance at which a healer merc will begin healing a group member, if they have the group role of puller) RULE_INT(Mercs, ResurrectRadius, 50) // Determines the distance from which a healer merc will attempt to resurrect a group member's corpse RULE_INT(Mercs, ScaleRate, 100) +RULE_BOOL(Mercs, AllowMercSuspendInCombat, true) RULE_CATEGORY_END() RULE_CATEGORY(Guild) diff --git a/common/spdat.cpp b/common/spdat.cpp index fd4e3c5ea..e88240ef9 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -940,7 +940,7 @@ bool IsRegularSingleTargetHealSpell(uint16 spell_id) { if(spells[spell_id].effectid[0] == 0 && spells[spell_id].base[0] > 0 && spells[spell_id].targettype == ST_Target && spells[spell_id].buffduration == 0 && - !IsFastHealSpell(spell_id) && !IsCompleteHealSpell(spell_id) && + !IsCompleteHealSpell(spell_id) && !IsHealOverTimeSpell(spell_id) && !IsGroupSpell(spell_id)) return true; diff --git a/zone/merc.cpp b/zone/merc.cpp index 3e3dfae93..8196d2ba2 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -5468,7 +5468,7 @@ void Client::SuspendMercCommand() { Merc* merc = Merc::LoadMerc(this, &zone->merc_templates[GetMercInfo().MercTemplateID], 0, true); if(merc) { - SpawnMerc(merc, true); + SpawnMerc(merc, false); Log.Out(Logs::General, Logs::Mercenaries, "SuspendMercCommand Successful Unsuspend for %s.", GetName()); } else @@ -5482,6 +5482,15 @@ void Client::SuspendMercCommand() { { Merc* CurrentMerc = GetMerc(); + + if (!RuleB(Mercs, AllowMercSuspendInCombat)) + { + if (!CheckCanSpawnMerc(GetMercInfo().MercTemplateID)) + { + return; + } + } + if(CurrentMerc && GetMercID()) { CurrentMerc->Suspend(); From 31cc6f63d65374f0fb1aba440c1f54ea19d104f9 Mon Sep 17 00:00:00 2001 From: Uleat Date: Tue, 14 Mar 2017 16:32:10 -0400 Subject: [PATCH 6/7] Added evade code to rogue mercs --- zone/bot.h | 2 +- zone/merc.cpp | 79 +++++++++++++++++++++++++++++++++++---------------- zone/merc.h | 2 ++ 3 files changed, 58 insertions(+), 25 deletions(-) diff --git a/zone/bot.h b/zone/bot.h index ca5a7e9ad..6c1dfc53f 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -737,7 +737,7 @@ private: bool _hasBeenSummoned; glm::vec3 m_PreSummonLocation; - Timer evade_timer; + Timer evade_timer; // can be moved to pTimers at some point BotCastingRoles m_CastingRoles; diff --git a/zone/merc.cpp b/zone/merc.cpp index 3e3dfae93..6a8e8af40 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -69,6 +69,9 @@ Merc::Merc(const NPCType* d, float x, float y, float z, float heading) SetStance(MercStanceBalanced); rest_timer.Disable(); + if (GetClass() == ROGUE) + evade_timer.Start(); + int r; for (r = 0; r <= EQEmu::skills::HIGHEST_SKILL; r++) { skills[r] = database.GetSkillCap(GetClass(), (EQEmu::skills::SkillType)r, GetLevel()); @@ -1508,32 +1511,60 @@ void Merc::AI_Process() { } } - if(AI_movement_timer->Check()) - { - if(!IsMoving() && GetClass() == ROGUE && !BehindMob(GetTarget(), GetX(), GetY())) - { - // Move the rogue to behind the mob - float newX = 0; - float newY = 0; - float newZ = 0; + if(AI_movement_timer->Check()) { + if (!IsMoving()) { + if (GetClass() == ROGUE) { + if (HasTargetReflection() && !GetTarget()->IsFeared() && !GetTarget()->IsStunned()) { + // Hate redux actions + if (evade_timer.Check(false)) { + // Attempt to evade + int timer_duration = (HideReuseTime - GetSkillReuseTime(EQEmu::skills::SkillHide)) * 1000; + if (timer_duration < 0) + timer_duration = 0; + evade_timer.Start(timer_duration); - if(PlotPositionAroundTarget(GetTarget(), newX, newY, newZ)) - { - CalculateNewPosition2(newX, newY, newZ, GetRunspeed()); - return; + if (zone->random.Int(0, 260) < (int)GetSkill(EQEmu::skills::SkillHide)) + RogueEvade(GetTarget()); + + return; + } + else if (GetTarget()->IsRooted()) { + // Move rogue back from rooted mob - out of combat range, if necessary + float melee_distance = GetMaxMeleeRangeToTarget(GetTarget()); + float current_distance = DistanceSquared(static_cast(m_Position), static_cast(GetTarget()->GetPosition())); + + if (current_distance <= melee_distance) { + float newX = 0; + float newY = 0; + float newZ = 0; + FaceTarget(GetTarget()); + if (PlotPositionAroundTarget(this, newX, newY, newZ)) { + CalculateNewPosition2(newX, newY, newZ, GetRunspeed()); + return; + } + } + } + } + else if (!BehindMob(GetTarget(), GetX(), GetY())) { + // Move the rogue to behind the mob + float newX = 0; + float newY = 0; + float newZ = 0; + if (PlotPositionAroundTarget(GetTarget(), newX, newY, newZ)) { + CalculateNewPosition2(newX, newY, newZ, GetRunspeed()); + return; + } + } } - } - else if(!IsMoving() && GetClass() != ROGUE && (DistanceSquaredNoZ(m_Position, GetTarget()->GetPosition()) < GetTarget()->GetSize())) - { - // If we are not a rogue trying to backstab, let's try to adjust our melee range so we don't appear to be bunched up - float newX = 0; - float newY = 0; - float newZ = 0; - - if(PlotPositionAroundTarget(GetTarget(), newX, newY, newZ, false) && GetArchetype() != ARCHETYPE_CASTER) - { - CalculateNewPosition2(newX, newY, newZ, GetRunspeed()); - return; + else if (GetClass() != ROGUE && (DistanceSquaredNoZ(m_Position, GetTarget()->GetPosition()) < GetTarget()->GetSize())) { + // If we are not a rogue trying to backstab, let's try to adjust our melee range so we don't appear to be bunched up + float newX = 0; + float newY = 0; + float newZ = 0; + if (PlotPositionAroundTarget(GetTarget(), newX, newY, newZ, false) && GetArchetype() != ARCHETYPE_CASTER) { + CalculateNewPosition2(newX, newY, newZ, GetRunspeed()); + return; + } } } diff --git a/zone/merc.h b/zone/merc.h index e3eb6c029..23e4b2f03 100644 --- a/zone/merc.h +++ b/zone/merc.h @@ -290,6 +290,8 @@ protected: std::vector merc_spells; std::map timers; + Timer evade_timer; // can be moved to pTimers at some point + uint16 skills[EQEmu::skills::HIGHEST_SKILL + 1]; uint32 equipment[EQEmu::legacy::EQUIPMENT_SIZE]; //this is an array of item IDs uint16 d_melee_texture1; //this is an item Material value From 2d24237aacbcaa98eb7a39fd5b2687d0e2dc71aa Mon Sep 17 00:00:00 2001 From: Uleat Date: Tue, 14 Mar 2017 23:23:42 -0400 Subject: [PATCH 7/7] Added los movement logic to combat and follow code (los is rule-based and can by disabled by applying the optional 2017_03_14_mercs_use_pathing_rule.sql) --- common/ruletypes.h | 1 + .../2017_03_14_mercs_use_pathing_rule.sql | 1 + zone/merc.cpp | 64 +++++++++++++------ 3 files changed, 48 insertions(+), 18 deletions(-) create mode 100644 utils/sql/git/optional/2017_03_14_mercs_use_pathing_rule.sql diff --git a/common/ruletypes.h b/common/ruletypes.h index 7e461071f..0bac4d6df 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -163,6 +163,7 @@ RULE_INT(Mercs, AggroRadius, 100) // Determines the distance from which a merc RULE_INT(Mercs, AggroRadiusPuller, 25) // Determines the distance from which a merc will aggro group member's target, if they have the group role of puller (also used to determine the distance at which a healer merc will begin healing a group member, if they have the group role of puller) RULE_INT(Mercs, ResurrectRadius, 50) // Determines the distance from which a healer merc will attempt to resurrect a group member's corpse RULE_INT(Mercs, ScaleRate, 100) +RULE_BOOL(Mercs, MercsUsePathing, true) // Mercs will use node pathing when moving RULE_BOOL(Mercs, AllowMercSuspendInCombat, true) RULE_CATEGORY_END() diff --git a/utils/sql/git/optional/2017_03_14_mercs_use_pathing_rule.sql b/utils/sql/git/optional/2017_03_14_mercs_use_pathing_rule.sql new file mode 100644 index 000000000..7393facdd --- /dev/null +++ b/utils/sql/git/optional/2017_03_14_mercs_use_pathing_rule.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Mercs:MercsUsePathing', 'false', 'Mercs will use node pathing when moving'); diff --git a/zone/merc.cpp b/zone/merc.cpp index 155cb4841..5f8ea59ea 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -1462,7 +1462,7 @@ void Merc::AI_Process() { // Let's check if we have a los with our target. // If we don't, our hate_list is wiped. // Else, it was causing the merc to aggro behind wall etc... causing massive trains. - if(!CheckLosFN(GetTarget()) || GetTarget()->IsMezzed() || !IsAttackAllowed(GetTarget())) { + if(GetTarget()->IsMezzed() || !IsAttackAllowed(GetTarget())) { WipeHateList(); if(IsMoving()) { @@ -1477,6 +1477,26 @@ void Merc::AI_Process() { return; } + else if (!CheckLosFN(GetTarget())) { + if (RuleB(Mercs, MercsUsePathing) && zone->pathing) { + bool WaypointChanged, NodeReached; + + glm::vec3 Goal = UpdatePath(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(), + GetRunspeed(), WaypointChanged, NodeReached); + + if (WaypointChanged) + tar_ndx = 20; + + CalculateNewPosition2(Goal.x, Goal.y, Goal.z, GetRunspeed()); + } + else { + Mob* follow = entity_list.GetMob(GetFollowID()); + if (follow) + CalculateNewPosition2(follow->GetX(), follow->GetY(), follow->GetZ(), GetRunspeed()); + } + + return; + } if (!(m_PlayerState & static_cast(PlayerState::Aggressive))) SendAddPlayerState(PlayerState::Aggressive); @@ -1738,34 +1758,42 @@ void Merc::AI_Process() { } } - if(AI_movement_timer->Check()) - { - if(GetFollowID()) - { + if(AI_movement_timer->Check()) { + if(GetFollowID()) { Mob* follow = entity_list.GetMob(GetFollowID()); - if(follow) - { + if (follow) { float dist = DistanceSquared(m_Position, follow->GetPosition()); int speed = GetRunspeed(); - if(dist < GetFollowDistance() + 1000) + if (dist < GetFollowDistance() + 1000) speed = GetWalkspeed(); SetRunAnimSpeed(0); - if(dist > GetFollowDistance()) { - CalculateNewPosition2(follow->GetX(), follow->GetY(), follow->GetZ(), speed); - if(rest_timer.Enabled()) + if (dist > GetFollowDistance()) { + if (RuleB(Mercs, MercsUsePathing) && zone->pathing) { + bool WaypointChanged, NodeReached; + + glm::vec3 Goal = UpdatePath(follow->GetX(), follow->GetY(), follow->GetZ(), + speed, WaypointChanged, NodeReached); + + if (WaypointChanged) + tar_ndx = 20; + + CalculateNewPosition2(Goal.x, Goal.y, Goal.z, speed); + } + else { + CalculateNewPosition2(follow->GetX(), follow->GetY(), follow->GetZ(), speed); + } + + if (rest_timer.Enabled()) rest_timer.Disable(); - return; } - else - { - if(moved) - { - SetCurrentSpeed(0); - moved = false; + else { + if (moved) { + moved = false; + SetCurrentSpeed(0); } } }