mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-13 14:41:28 +00:00
[Feature] Add Water Line of Sight Checks (#3408)
* [Feature] Add Water Line of Sight Checks This adds rules to enable or disable checks for spells and autofire to prevent casting or autofire from landing if the player or bot do not match their targets plane in regards to water. Currently players and bots can cast or autofire if they are not in the water but their target is and vice versa, this should not be possible. RuleB(Combat, WaterMatchRequiredForAutoFireLoS) set to True (default) checks that both parties are in or out of the water for AutoFire to work. RuleB(Spells, WaterMatchRequiredForLoS) set to True (default) checks that both parties are in or out of the water for spells to land. * Cleanup. * Cleanup. * Cleanup. --------- Co-authored-by: Kinglykrab <kinglykrab@gmail.com>
This commit is contained in:
parent
75391d96f4
commit
0cf454dc29
@ -446,6 +446,7 @@ RULE_BOOL(Spells, UseItemCastMessage, false, "Enable to use the \"item begins to
|
|||||||
RULE_BOOL(Spells, TargetsTargetRequiresCombatRange, true, "Disable to remove combat range requirement from Target's Target Spell Target Type")
|
RULE_BOOL(Spells, TargetsTargetRequiresCombatRange, true, "Disable to remove combat range requirement from Target's Target Spell Target Type")
|
||||||
RULE_BOOL(Spells, NPCBuffLevelRestrictions, false, "Impose BuffLevelRestrictions on NPCs if true")
|
RULE_BOOL(Spells, NPCBuffLevelRestrictions, false, "Impose BuffLevelRestrictions on NPCs if true")
|
||||||
RULE_INT(Spells, ResurrectionEffectBlock, 2, "0 = allow overwrites/rule disabled. If set to 1 = Block all buffs that would overwrite Resurrection Effects. If set to 2 = Will not overwrite Resurrection Effects, instead moves new buff to an empty slot if available. Default is 2.")
|
RULE_INT(Spells, ResurrectionEffectBlock, 2, "0 = allow overwrites/rule disabled. If set to 1 = Block all buffs that would overwrite Resurrection Effects. If set to 2 = Will not overwrite Resurrection Effects, instead moves new buff to an empty slot if available. Default is 2.")
|
||||||
|
RULE_BOOL(Spells, WaterMatchRequiredForLoS, true, "Enable/Disable the requirement of both the attacker/victim being both in or out of water for spells LoS to pass.")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
RULE_CATEGORY(Combat)
|
RULE_CATEGORY(Combat)
|
||||||
@ -519,6 +520,7 @@ RULE_BOOL(Combat, EnableWarriorShielding, true, "Enable or disable Warrior Shiel
|
|||||||
RULE_BOOL(Combat, BackstabIgnoresElemental, false, "Enable or disable Elemental weapon damage affecting backstab damage, false by default.")
|
RULE_BOOL(Combat, BackstabIgnoresElemental, false, "Enable or disable Elemental weapon damage affecting backstab damage, false by default.")
|
||||||
RULE_BOOL(Combat, BackstabIgnoresBane, false, "Enable or disable Bane weapon damage affecting backstab damage, false by default.")
|
RULE_BOOL(Combat, BackstabIgnoresBane, false, "Enable or disable Bane weapon damage affecting backstab damage, false by default.")
|
||||||
RULE_BOOL(Combat, SummonMeleeRange, true, "Enable or disable summoning of a player when already in melee range of the summoner.")
|
RULE_BOOL(Combat, SummonMeleeRange, true, "Enable or disable summoning of a player when already in melee range of the summoner.")
|
||||||
|
RULE_BOOL(Combat, WaterMatchRequiredForAutoFireLoS, true, "Enable/Disable the requirement of both the attacker/victim being both in or out of water for AutoFire LoS to pass.")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
RULE_CATEGORY(NPC)
|
RULE_CATEGORY(NPC)
|
||||||
|
|||||||
@ -262,7 +262,7 @@ bool Bot::BotCastDebuff(Mob* tar, uint8 botLevel, BotSpell& botSpell, bool check
|
|||||||
bool casted_spell = false;
|
bool casted_spell = false;
|
||||||
if ((tar->GetHPRatio() <= 99.0f) && (tar->GetHPRatio() > 20.0f))
|
if ((tar->GetHPRatio() <= 99.0f) && (tar->GetHPRatio() > 20.0f))
|
||||||
{
|
{
|
||||||
if (!checked_los && !CheckLosFN(tar)) {
|
if (!checked_los && (!CheckLosFN(tar) || !CheckWaterLoS(tar))) {
|
||||||
return casted_spell;
|
return casted_spell;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,7 +296,7 @@ bool Bot::BotCastSlow(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpe
|
|||||||
bool casted_spell = false;
|
bool casted_spell = false;
|
||||||
if (tar->GetHPRatio() <= 99.0f) {
|
if (tar->GetHPRatio() <= 99.0f) {
|
||||||
|
|
||||||
if (!checked_los && !CheckLosFN(tar)) {
|
if (!checked_los && (!CheckLosFN(tar) || !CheckWaterLoS(tar))) {
|
||||||
return casted_spell;
|
return casted_spell;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -387,7 +387,7 @@ bool Bot::BotCastDOT(Mob* tar, uint8 botLevel, const BotSpell& botSpell, const b
|
|||||||
bool casted_spell = false;
|
bool casted_spell = false;
|
||||||
|
|
||||||
if ((tar->GetHPRatio() <= 98.0f) && (tar->DontDotMeBefore() < Timer::GetCurrentTime()) && (tar->GetHPRatio() > 15.0f)) {
|
if ((tar->GetHPRatio() <= 98.0f) && (tar->DontDotMeBefore() < Timer::GetCurrentTime()) && (tar->GetHPRatio() > 15.0f)) {
|
||||||
if (!checked_los && !CheckLosFN(tar)) {
|
if (!checked_los && (!CheckLosFN(tar) || !CheckWaterLoS(tar))) {
|
||||||
return casted_spell;
|
return casted_spell;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -470,7 +470,7 @@ bool Bot::BotCastDOT(Mob* tar, uint8 botLevel, const BotSpell& botSpell, const b
|
|||||||
bool Bot::BotCastSnare(Mob* tar, uint8 botLevel, BotSpell& botSpell, const bool& checked_los, uint32 iSpellTypes) {
|
bool Bot::BotCastSnare(Mob* tar, uint8 botLevel, BotSpell& botSpell, const bool& checked_los, uint32 iSpellTypes) {
|
||||||
bool casted_spell = false;
|
bool casted_spell = false;
|
||||||
if (tar->DontSnareMeBefore() < Timer::GetCurrentTime()) {
|
if (tar->DontSnareMeBefore() < Timer::GetCurrentTime()) {
|
||||||
if (!checked_los && !CheckLosFN(tar)) {
|
if (!checked_los && (!CheckLosFN(tar) || !CheckWaterLoS(tar))) {
|
||||||
return casted_spell;
|
return casted_spell;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -501,7 +501,7 @@ bool Bot::BotCastSnare(Mob* tar, uint8 botLevel, BotSpell& botSpell, const bool&
|
|||||||
bool Bot::BotCastLifetap(Mob* tar, uint8 botLevel, BotSpell& botSpell, const bool& checked_los, uint32 iSpellTypes) {
|
bool Bot::BotCastLifetap(Mob* tar, uint8 botLevel, BotSpell& botSpell, const bool& checked_los, uint32 iSpellTypes) {
|
||||||
bool casted_spell = false;
|
bool casted_spell = false;
|
||||||
if (GetHPRatio() < 90.0f) {
|
if (GetHPRatio() < 90.0f) {
|
||||||
if (!checked_los && !CheckLosFN(tar)) {
|
if (!checked_los && (!CheckLosFN(tar) || !CheckWaterLoS(tar))) {
|
||||||
return casted_spell;
|
return casted_spell;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -714,7 +714,7 @@ bool Bot::BotCastDispel(Mob* tar, BotSpell& botSpell, uint32 iSpellTypes, const
|
|||||||
|
|
||||||
bool casted_spell = false;
|
bool casted_spell = false;
|
||||||
if (tar->GetHPRatio() > 95.0f) {
|
if (tar->GetHPRatio() > 95.0f) {
|
||||||
if (!checked_los && !CheckLosFN(tar)) {
|
if (!checked_los && (!CheckLosFN(tar) || !CheckWaterLoS(tar))) {
|
||||||
return casted_spell;
|
return casted_spell;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -737,7 +737,7 @@ bool Bot::BotCastNuke(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpe
|
|||||||
bool casted_spell = false;
|
bool casted_spell = false;
|
||||||
if ((tar->GetHPRatio() <= 95.0f) || ((botClass == BARD) || (botClass == SHAMAN) || (botClass == ENCHANTER) || (botClass == PALADIN) || (botClass == SHADOWKNIGHT) || (botClass == WARRIOR)))
|
if ((tar->GetHPRatio() <= 95.0f) || ((botClass == BARD) || (botClass == SHAMAN) || (botClass == ENCHANTER) || (botClass == PALADIN) || (botClass == SHADOWKNIGHT) || (botClass == WARRIOR)))
|
||||||
{
|
{
|
||||||
if (!checked_los && !CheckLosFN(tar)) {
|
if (!checked_los && (!CheckLosFN(tar) || !CheckWaterLoS(tar))) {
|
||||||
return casted_spell;
|
return casted_spell;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -965,7 +965,7 @@ bool Bot::BotCastBuff(Mob* tar, uint8 botLevel, uint8 botClass) {
|
|||||||
bool Bot::BotCastRoot(Mob* tar, uint8 botLevel, uint32 iSpellTypes, BotSpell& botSpell, const bool& checked_los) {
|
bool Bot::BotCastRoot(Mob* tar, uint8 botLevel, uint32 iSpellTypes, BotSpell& botSpell, const bool& checked_los) {
|
||||||
bool casted_spell = false;
|
bool casted_spell = false;
|
||||||
if (!tar->IsRooted() && tar->DontRootMeBefore() < Timer::GetCurrentTime()) {
|
if (!tar->IsRooted() && tar->DontRootMeBefore() < Timer::GetCurrentTime()) {
|
||||||
if (!checked_los && !CheckLosFN(tar)) {
|
if (!checked_los && (!CheckLosFN(tar) || !CheckWaterLoS(tar))) {
|
||||||
return casted_spell;
|
return casted_spell;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1189,7 +1189,7 @@ bool Bot::BotCastHeal(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpe
|
|||||||
|
|
||||||
bool Bot::BotCastMez(Mob* tar, uint8 botLevel, bool checked_los, BotSpell& botSpell, Raid* raid) {
|
bool Bot::BotCastMez(Mob* tar, uint8 botLevel, bool checked_los, BotSpell& botSpell, Raid* raid) {
|
||||||
bool casted_spell = false;
|
bool casted_spell = false;
|
||||||
if (!checked_los && !CheckLosFN(tar)) {
|
if (!checked_los && (!CheckLosFN(tar) || !CheckWaterLoS(tar))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1503,6 +1503,8 @@ public:
|
|||||||
bool GroupFollow(Client* inviter);
|
bool GroupFollow(Client* inviter);
|
||||||
inline bool GetRunMode() const { return runmode; }
|
inline bool GetRunMode() const { return runmode; }
|
||||||
|
|
||||||
|
virtual bool CheckWaterAutoFireLoS(Mob* m);
|
||||||
|
|
||||||
void SendReloadCommandMessages();
|
void SendReloadCommandMessages();
|
||||||
|
|
||||||
void SendItemRecastTimer(int32 recast_type, uint32 recast_delay = 0, bool in_ignore_casting_requirement = false);
|
void SendItemRecastTimer(int32 recast_type, uint32 recast_delay = 0, bool in_ignore_casting_requirement = false);
|
||||||
|
|||||||
@ -51,6 +51,7 @@
|
|||||||
#include "zone.h"
|
#include "zone.h"
|
||||||
#include "zonedb.h"
|
#include "zonedb.h"
|
||||||
#include "../common/events/player_event_logs.h"
|
#include "../common/events/player_event_logs.h"
|
||||||
|
#include "water_map.h"
|
||||||
|
|
||||||
extern QueryServ* QServ;
|
extern QueryServ* QServ;
|
||||||
extern Zone* zone;
|
extern Zone* zone;
|
||||||
@ -336,7 +337,7 @@ bool Client::Process() {
|
|||||||
if (ranged_timer.Check(false)) {
|
if (ranged_timer.Check(false)) {
|
||||||
if (GetTarget() && (GetTarget()->IsNPC() || GetTarget()->IsClient()) && IsAttackAllowed(GetTarget())) {
|
if (GetTarget() && (GetTarget()->IsNPC() || GetTarget()->IsClient()) && IsAttackAllowed(GetTarget())) {
|
||||||
if (GetTarget()->InFrontMob(this, GetTarget()->GetX(), GetTarget()->GetY())) {
|
if (GetTarget()->InFrontMob(this, GetTarget()->GetX(), GetTarget()->GetY())) {
|
||||||
if (CheckLosFN(GetTarget())) {
|
if (CheckLosFN(GetTarget()) && CheckWaterAutoFireLoS(GetTarget())) {
|
||||||
//client has built in los check, but auto fire does not.. done last.
|
//client has built in los check, but auto fire does not.. done last.
|
||||||
RangedAttack(GetTarget());
|
RangedAttack(GetTarget());
|
||||||
if (CheckDoubleRangedAttack())
|
if (CheckDoubleRangedAttack())
|
||||||
@ -356,7 +357,7 @@ bool Client::Process() {
|
|||||||
if (ranged_timer.Check(false)) {
|
if (ranged_timer.Check(false)) {
|
||||||
if (GetTarget() && (GetTarget()->IsNPC() || GetTarget()->IsClient()) && IsAttackAllowed(GetTarget())) {
|
if (GetTarget() && (GetTarget()->IsNPC() || GetTarget()->IsClient()) && IsAttackAllowed(GetTarget())) {
|
||||||
if (GetTarget()->InFrontMob(this, GetTarget()->GetX(), GetTarget()->GetY())) {
|
if (GetTarget()->InFrontMob(this, GetTarget()->GetX(), GetTarget()->GetY())) {
|
||||||
if (CheckLosFN(GetTarget())) {
|
if (CheckLosFN(GetTarget()) && CheckWaterAutoFireLoS(GetTarget())) {
|
||||||
//client has built in los check, but auto fire does not.. done last.
|
//client has built in los check, but auto fire does not.. done last.
|
||||||
ThrowingAttack(GetTarget());
|
ThrowingAttack(GetTarget());
|
||||||
}
|
}
|
||||||
@ -2401,3 +2402,18 @@ void Client::SendGuildLFGuildStatus()
|
|||||||
worldserver.SendPacket(pack);
|
worldserver.SendPacket(pack);
|
||||||
safe_delete(pack);
|
safe_delete(pack);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Client::CheckWaterAutoFireLoS(Mob* m)
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
!RuleB(Combat, WaterMatchRequiredForAutoFireLoS) ||
|
||||||
|
!zone->watermap
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
zone->watermap->InLiquid(GetPosition()) &&
|
||||||
|
zone->watermap->InLiquid(m->GetPosition())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@ -753,6 +753,7 @@ public:
|
|||||||
bool CheckLosFN(Mob* other);
|
bool CheckLosFN(Mob* other);
|
||||||
bool CheckLosFN(float posX, float posY, float posZ, float mobSize);
|
bool CheckLosFN(float posX, float posY, float posZ, float mobSize);
|
||||||
static bool CheckLosFN(glm::vec3 posWatcher, float sizeWatcher, glm::vec3 posTarget, float sizeTarget);
|
static bool CheckLosFN(glm::vec3 posWatcher, float sizeWatcher, glm::vec3 posTarget, float sizeTarget);
|
||||||
|
virtual bool CheckWaterLoS(Mob* m);
|
||||||
inline void SetLastLosState(bool value) { last_los_check = value; }
|
inline void SetLastLosState(bool value) { last_los_check = value; }
|
||||||
inline bool CheckLastLosState() const { return last_los_check; }
|
inline bool CheckLastLosState() const { return last_los_check; }
|
||||||
std::string GetMobDescription();
|
std::string GetMobDescription();
|
||||||
|
|||||||
@ -101,7 +101,7 @@ Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org)
|
|||||||
#include "mob_movement_manager.h"
|
#include "mob_movement_manager.h"
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
#include "mob.h"
|
#include "mob.h"
|
||||||
|
#include "water_map.h"
|
||||||
|
|
||||||
extern Zone* zone;
|
extern Zone* zone;
|
||||||
extern volatile bool is_zone_loaded;
|
extern volatile bool is_zone_loaded;
|
||||||
@ -2356,7 +2356,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, in
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check line of sight to target if it's a detrimental spell
|
// check line of sight to target if it's a detrimental spell
|
||||||
if(!spells[spell_id].npc_no_los && spell_target && IsDetrimentalSpell(spell_id) && !CheckLosFN(spell_target) && !IsHarmonySpell(spell_id) && spells[spell_id].target_type != ST_TargetOptional)
|
if (!spells[spell_id].npc_no_los && spell_target && IsDetrimentalSpell(spell_id) && (!CheckLosFN(spell_target) || !CheckWaterLoS(spell_target)) && !IsHarmonySpell(spell_id) && spells[spell_id].target_type != ST_TargetOptional)
|
||||||
{
|
{
|
||||||
LogSpells("Spell [{}]: cannot see target [{}]", spell_id, spell_target->GetName());
|
LogSpells("Spell [{}]: cannot see target [{}]", spell_id, spell_target->GetName());
|
||||||
MessageString(Chat::Red,CANT_SEE_TARGET);
|
MessageString(Chat::Red,CANT_SEE_TARGET);
|
||||||
@ -7100,3 +7100,18 @@ const CombatRecord &Mob::GetCombatRecord() const
|
|||||||
{
|
{
|
||||||
return m_combat_record;
|
return m_combat_record;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Mob::CheckWaterLoS(Mob* m)
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
!RuleB(Spells, WaterMatchRequiredForLoS) ||
|
||||||
|
!zone->watermap
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
zone->watermap->InLiquid(GetPosition()) &&
|
||||||
|
zone->watermap->InLiquid(m->GetPosition())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user