mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-11 12:41:30 +00:00
[Bots] Line of Sight and Mez optimizations and cleanup (#4746)
* [Bots] Line of Sight and Mez optimizations and cleanup - Renames `Map:CheckForLoSCheat` to `Map:CheckForDoorLoSCheat` to better reflect what it does. - Renames `Map:RangeCheckForLoSCheat` to `Map:RangeCheckForDoorLoSCheat` to better reflect what it does. - Adds the rule `Pets:PetsRequireLoS` to determine whether or not commanded pet attacks require an addition layer of LoS checks for edge-cases. - Adds the rule `Bots:BotsRequireLoS` to determine whether or not bots require LoS to `^attack`, `^pull` and `^precombat`. - Adds the rule `Map:ZonesToCheckDoorCheat` to control what if any zones will be checked.. - Corrects, removes and adds LoS checks where necessary. - Improves door checking logic for locked or triggered doors that could be blocking LoS. - Cleans up false positives for door cheat checks. - Adds `drawbox` option to `#door` command. This will spawn points at the center and each corner of the door's "box". It will also spawn points at your and your target's location. - Improves Mez and AE Mez logic - Adds more details to the rule `Bots:EpicPetSpellName` * Remove leftover debugging * Change return to continue for GetFirstIncomingMobToMez checks * Move mez chance fail to beginning of cast process
This commit is contained in:
parent
d554eb3423
commit
444d688ad2
@ -291,6 +291,7 @@ RULE_BOOL(Pets, ClientPetsUseOwnerNameInLastName, true, "Disable this to keep cl
|
||||
RULE_BOOL(Pets, CanTakeNoDrop, false, "Setting whether anyone can give no-drop items to pets")
|
||||
RULE_INT(Pets, PetTauntRange, 150, "Range at which a pet will taunt targets.")
|
||||
RULE_BOOL(Pets, AlwaysAllowPetRename, false, "Enable this option to allow /changepetname to work without enabling a pet name change via scripts.")
|
||||
RULE_BOOL(Pets, PetsRequireLoS, false, "Whether or not pets require line of sight to be told to attack their target")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(GM)
|
||||
@ -390,9 +391,10 @@ RULE_BOOL(Map, MobZVisualDebug, false, "Displays spell effects determining wheth
|
||||
RULE_BOOL(Map, MobPathingVisualDebug, false, "Displays nodes in pathing points in realtime to help with visual debugging")
|
||||
RULE_REAL(Map, FixPathingZMaxDeltaSendTo, 20, "At runtime in SendTo: maximum change in Z to allow the BestZ code to apply")
|
||||
RULE_INT(Map, FindBestZHeightAdjust, 1, "Adds this to the current Z before seeking the best Z position")
|
||||
RULE_BOOL(Map, CheckForLoSCheat, false, "Runs predefined zone checks to check for LoS cheating through doors and such.")
|
||||
RULE_BOOL(Map, EnableLoSCheatExemptions, false, "Enables exemptions for the LoS Cheat check.")
|
||||
RULE_REAL(Map, RangeCheckForLoSCheat, 20.0, "Default 20.0. Range to check if one is within range of a door.")
|
||||
RULE_BOOL(Map, CheckForDoorLoSCheat, true, "Runs LoS checks to prevent cheating through doors.")
|
||||
RULE_BOOL(Map, EnableLoSCheatExemptions, false, "Enables exemptions for the LoS Cheat check. Must modify source to create these.")
|
||||
RULE_REAL(Map, RangeCheckForDoorLoSCheat, 250.0, "Default 250.0. Range to check if a door is blocking LoS from the target.")
|
||||
RULE_STRING(Map, ZonesToCheckDoorCheat, "89,103", "Zones that will check for the door LoS cheat. You can leave it blank to disable, 'all' to check all zones or use a comma-delimited list of zones. Default Sebilis & Chardok")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Pathing)
|
||||
@ -816,6 +818,7 @@ RULE_INT(Bots, PercentChanceToCastDispel, 75, "The chance for a bot to attempt t
|
||||
RULE_INT(Bots, PercentChanceToCastInCombatBuff, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
||||
RULE_INT(Bots, PercentChanceToCastHateLine, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
||||
RULE_INT(Bots, PercentChanceToCastMez, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
||||
RULE_INT(Bots, PercentChanceToCastAEMez, 40, "The chance for a bot to attempt to cast the given spell type in combat. Default 40%.")
|
||||
RULE_INT(Bots, PercentChanceToCastSlow, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
||||
RULE_INT(Bots, PercentChanceToCastDebuff, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
||||
RULE_INT(Bots, PercentChanceToCastCure, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
||||
@ -827,8 +830,6 @@ RULE_INT(Bots, MinDelayBetweenInCombatCastAttempts, 500, "The minimum delay in m
|
||||
RULE_INT(Bots, MaxDelayBetweenInCombatCastAttempts, 2000, "The maximum delay in milliseconds between cast attempts while in-combat. This is rolled between the min and max. Default 2500ms.")
|
||||
RULE_INT(Bots, MinDelayBetweenOutCombatCastAttempts, 1000, "The minimum delay in milliseconds between cast attempts while out-of-combat. This is rolled between the min and max. Default 1000ms.")
|
||||
RULE_INT(Bots, MaxDelayBetweenOutCombatCastAttempts, 2500, "The maximum delay in milliseconds between cast attempts while out-of-combat. This is rolled between the min and max. Default 2500ms.")
|
||||
RULE_INT(Bots, MezChance, 60, "60 Default. Chance for a bot to attempt to Mez a target after validating it is eligible.")
|
||||
RULE_INT(Bots, AEMezChance, 35, "35 Default. Chance for a bot to attempt to AE Mez targets after validating they are eligible.")
|
||||
RULE_INT(Bots, MezSuccessDelay, 2500, "2500 (2.5 sec) Default. Delay between successful Mez attempts.")
|
||||
RULE_INT(Bots, AEMezSuccessDelay, 5000, "5000 (5 sec) Default. Delay between successful AEMez attempts.")
|
||||
RULE_INT(Bots, MezFailDelay, 1250, "1250 (1.25 sec) Default. Delay between failed Mez attempts.")
|
||||
@ -845,7 +846,7 @@ RULE_BOOL(Bots, AllowCastAAs, true, "If enabled, players can use ^cast aa to cas
|
||||
RULE_BOOL(Bots, AllowMagicianEpicPet, false, "If enabled, magician bots can summon their epic pets following the rules AllowMagicianEpicPetLevel")
|
||||
RULE_INT(Bots, AllowMagicianEpicPetLevel, 50, "If AllowMagicianEpicPet is enabled, bots can start using their epic pets at this level")
|
||||
RULE_INT(Bots, RequiredMagicianEpicPetItemID, 28034, "If AllowMagicianEpicPet is enabled and this is set, bots will be required to have this item ID equipped to cast their epic. Takes in to account AllowMagicianEpicPetLevel as well. Set to 0 to disable requirement")
|
||||
RULE_STRING(Bots, EpicPetSpellName, "", "'teleport_zone' in the spell to be cast for epic pets. This must be in their spell list to cast.")
|
||||
RULE_STRING(Bots, EpicPetSpellName, "", "'teleport_zone' in the spell to be cast for epic pets. This must be in their usable spell list to cast. Empty uses Manifest Elements - 'SumMageMultiElement'")
|
||||
RULE_INT(Bots, ReclaimEnergySpellID, 331, "Spell ID for reclaim energy when using ^petsettype. Default 331")
|
||||
RULE_BOOL(Bots, UseSpellPulling, true, "If enabled bots will use a spell to pull when within range. Uses PullSpellID.")
|
||||
RULE_INT(Bots, PullSpellID, 5225, "Default 5225 - Throw Stone. Spell that will be cast to pull by bots")
|
||||
@ -903,6 +904,7 @@ RULE_STRING(Bots, ZonesWithForcedSpawnLimits, "", "Comma-delimited list of zones
|
||||
RULE_STRING(Bots, ZoneForcedSpawnLimits, "", "Comma-delimited list of forced spawn limits for zones.")
|
||||
RULE_INT(Bots, AICastSpellTypeDelay, 100, "Delay in milliseconds between AI cast attempts for each spell type. Default 100ms")
|
||||
RULE_INT(Bots, AICastSpellTypeHeldDelay, 2500, "Delay in milliseconds between AI cast attempts for each spell type that is held or disabled. Default 2500ms (2.5s)")
|
||||
RULE_BOOL(Bots, BotsRequireLoS, true, "Whether or not bots require line of sight to be told to attack their target")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Chat)
|
||||
|
||||
@ -2808,18 +2808,6 @@ bool IsLichSpell(uint16 spell_id)
|
||||
);
|
||||
}
|
||||
|
||||
bool IsValidSpellAndLoS(uint32 spell_id, bool has_los) {
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!has_los && IsTargetRequiredForSpell(spell_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsInstantHealSpell(uint32 spell_id) {
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
return false;
|
||||
|
||||
@ -918,6 +918,7 @@ bool IsPullingBotSpellType(uint16 spell_type);
|
||||
uint16 GetCorrectBotSpellType(uint16 spell_type, uint16 spell_id);
|
||||
uint16 GetPetBotSpellType(uint16 spell_type);
|
||||
bool IsBotBuffSpellType(uint16 spell_type);
|
||||
bool BotRequiresLoSToCast(uint16 spell_type, uint16 spell_id);
|
||||
|
||||
// These should not be used to determine spell category..
|
||||
// They are a graphical affects (effects?) index only
|
||||
@ -1815,7 +1816,6 @@ bool IsEffectInSpell(uint16 spell_id, int effect_id);
|
||||
uint16 GetSpellTriggerSpellID(uint16 spell_id, int effect_id);
|
||||
bool IsBlankSpellEffect(uint16 spell_id, int effect_index);
|
||||
bool IsValidSpell(uint32 spell_id);
|
||||
bool IsValidSpellAndLoS(uint32 spell_id, bool has_los = true);
|
||||
bool IsSummonSpell(uint16 spell_id);
|
||||
bool IsDamageSpell(uint16 spell_id);
|
||||
bool IsAnyDamageSpell(uint16 spell_id);
|
||||
|
||||
@ -484,3 +484,15 @@ bool IsBotBuffSpellType(uint16 spell_type) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BotRequiresLoSToCast(uint16 spell_type, uint16 spell_id) {
|
||||
if (!BotSpellTypeRequiresTarget(spell_type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsTargetRequiredForSpell(spell_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
17
zone/bot.cpp
17
zone/bot.cpp
@ -2314,7 +2314,7 @@ void Bot::AI_Process()
|
||||
if (PULLING_BOT || RETURNING_BOT) {
|
||||
if (!TargetValidation(tar)) { return; }
|
||||
|
||||
if (!DoLosChecks(tar)) {
|
||||
if (RuleB(Bots, BotsRequireLoS) && !HasLoS()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -3175,14 +3175,17 @@ bool Bot::IsValidTarget(
|
||||
return false;
|
||||
}
|
||||
|
||||
SetHasLoS(DoLosChecks(tar));
|
||||
|
||||
bool invalid_target_state = false;
|
||||
|
||||
if (HOLDING ||
|
||||
!tar->IsNPC() ||
|
||||
(tar->IsMezzed() && !HasBotAttackFlag(tar)) ||
|
||||
(!Charmed() && tar->GetUltimateOwner()->IsOfClientBotMerc()) ||
|
||||
lo_distance > leash_distance ||
|
||||
tar_distance > leash_distance ||
|
||||
(!GetAttackingFlag() && !CheckLosCheat(tar) && !leash_owner->CheckLosCheat(tar)) ||
|
||||
(!GetAttackingFlag() && !HasLoS()) ||
|
||||
!IsAttackAllowed(tar)
|
||||
) {
|
||||
invalid_target_state = true;
|
||||
@ -11413,7 +11416,7 @@ bool Bot::AttemptForcedCastSpell(Mob* tar, uint16 spell_id, bool is_disc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!DoLosChecks(tar)) {
|
||||
if (!HasLoS() && !DoLosChecks(tar)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -12197,7 +12200,7 @@ bool Bot::HasRequiredLoSForPositioning(Mob* tar) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (RequiresLoSForPositioning() && !DoLosChecks(tar)) {
|
||||
if (RequiresLoSForPositioning() && !HasLoS()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -12212,10 +12215,6 @@ bool Bot::HasValidAETarget(Bot* caster, uint16 spell_id, uint16 spell_type, Mob*
|
||||
for (auto& close_mob : caster->m_close_mobs) {
|
||||
Mob* m = close_mob.second;
|
||||
|
||||
if (tar == m) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (spell_type) {
|
||||
case BotSpellTypes::AELull:
|
||||
if (m->GetSpecialAbility(SpecialAbility::PacifyImmunity)) {
|
||||
@ -12307,8 +12306,6 @@ bool Bot::HasValidAETarget(Bot* caster, uint16 spell_id, uint16 spell_type, Mob*
|
||||
return false;
|
||||
}
|
||||
|
||||
SetHasLoS(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -22,7 +22,7 @@ void bot_command_attack(Client *c, const Seperator *sep)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!c->DoLosChecks(target_mob)) {
|
||||
if (RuleB(Bots, BotsRequireLoS) && !c->DoLosChecks(target_mob)) {
|
||||
c->Message(Chat::Red, "You must have Line of Sight to use this command.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -525,6 +525,9 @@ void bot_command_cast(Client* c, const Seperator* sep)
|
||||
continue;
|
||||
}
|
||||
|
||||
bool requires_los = !(IsAnyHealSpell(spell_id) && !IsPBAESpell(spell_id));
|
||||
bot_iter->SetHasLoS(requires_los ? bot_iter->DoLosChecks(new_tar) : true);
|
||||
|
||||
if (!bot_iter->AttemptAACastSpell(tar, spell_id, rank)) {
|
||||
continue;
|
||||
}
|
||||
@ -543,6 +546,9 @@ void bot_command_cast(Client* c, const Seperator* sep)
|
||||
tar = bot_iter;
|
||||
}
|
||||
|
||||
bool los_required = bot_iter != tar && !IsAnyHealSpell(chosen_spell_id) && !IsPBAESpell(chosen_spell_id);
|
||||
bot_iter->SetHasLoS(los_required ? bot_iter->DoLosChecks(new_tar) : true);
|
||||
|
||||
if (bot_iter->AttemptForcedCastSpell(tar, chosen_spell_id)) {
|
||||
if (!first_found) {
|
||||
first_found = bot_iter;
|
||||
@ -556,7 +562,8 @@ void bot_command_cast(Client* c, const Seperator* sep)
|
||||
}
|
||||
else {
|
||||
bot_iter->SetCommandedSpell(true);
|
||||
|
||||
bot_iter->SetHasLoS(BotSpellTypeRequiresLoS(spell_type) ? bot_iter->DoLosChecks(new_tar) : true);
|
||||
|
||||
if (bot_iter->AICastSpell(new_tar, 100, spell_type, sub_target_type, sub_type)) {
|
||||
if (!first_found) {
|
||||
first_found = bot_iter;
|
||||
|
||||
@ -197,7 +197,11 @@ void bot_command_depart(Client* c, const Seperator* sep)
|
||||
|
||||
bot_iter->SetCommandedSpell(true);
|
||||
|
||||
if (!IsValidSpellAndLoS(itr->SpellId, bot_iter->HasLoS())) {
|
||||
if (!IsValidSpell(itr->SpellId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (BotRequiresLoSToCast(BotSpellTypes::Teleport, itr->SpellId) && !bot_iter->HasLoS()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@ void bot_command_precombat(Client* c, const Seperator* sep)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!c->DoLosChecks(c->GetTarget())) {
|
||||
if (RuleB(Bots, BotsRequireLoS) && !c->DoLosChecks(c->GetTarget())) {
|
||||
c->Message(Chat::Red, "You must have Line of Sight to use this command.");
|
||||
|
||||
return;
|
||||
|
||||
@ -48,7 +48,7 @@ void bot_command_pull(Client *c, const Seperator *sep)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!c->DoLosChecks(target_mob)) {
|
||||
if (RuleB(Bots, BotsRequireLoS) && !c->DoLosChecks(target_mob)) {
|
||||
c->Message(Chat::Red, "You must have Line of Sight to use this command.");
|
||||
|
||||
return;
|
||||
|
||||
@ -41,7 +41,10 @@ bool Bot::AICastSpell(Mob* tar, uint8 chance, uint16 spell_type, uint16 sub_targ
|
||||
return false;
|
||||
}
|
||||
|
||||
if (chance < 100 && zone->random.Int(0, 100) > chance) {
|
||||
if (
|
||||
!IsCommandedSpell() &&
|
||||
zone->random.Int(0, 100) > chance
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -61,10 +64,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 chance, uint16 spell_type, uint16 sub_targ
|
||||
bot_spell.SpellIndex = 0;
|
||||
bot_spell.ManaCost = 0;
|
||||
|
||||
if (BotSpellTypeRequiresLoS(spell_type) && tar != this) {
|
||||
SetHasLoS(DoLosChecks(tar));
|
||||
}
|
||||
else {
|
||||
if (!BotSpellTypeRequiresLoS(spell_type) || tar == this) {
|
||||
SetHasLoS(true);
|
||||
}
|
||||
|
||||
@ -218,8 +218,11 @@ bool Bot::AICastSpell(Mob* tar, uint8 chance, uint16 spell_type, uint16 sub_targ
|
||||
std::vector<BotSpell_wPriority> bot_spell_list = GetPrioritizedBotSpellsBySpellType(this, spell_type, tar, (IsAEBotSpellType(spell_type) || sub_target_type == CommandedSubTypes::AETarget), sub_target_type, sub_type);
|
||||
|
||||
for (const auto& s : bot_spell_list) {
|
||||
if (!IsValidSpell(s.SpellId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!IsValidSpellAndLoS(s.SpellId, HasLoS())) {
|
||||
if (BotRequiresLoSToCast(spell_type, s.SpellId) && !HasLoS()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -273,7 +276,11 @@ bool Bot::BotCastMez(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spel
|
||||
std::vector<BotSpell_wPriority> bot_spell_list = GetPrioritizedBotSpellsBySpellType(this, spell_type, tar, IsAEBotSpellType(spell_type));
|
||||
|
||||
for (const auto& s : bot_spell_list) {
|
||||
if (!IsValidSpellAndLoS(s.SpellId, HasLoS())) {
|
||||
if (!IsValidSpell(s.SpellId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (BotRequiresLoSToCast(spell_type, s.SpellId) && !HasLoS()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -281,7 +288,7 @@ bool Bot::BotCastMez(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spel
|
||||
Mob* add_mob = GetFirstIncomingMobToMez(this, s.SpellId, spell_type, IsAEBotSpellType(spell_type));
|
||||
|
||||
if (!add_mob) {
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
|
||||
tar = add_mob;
|
||||
@ -327,7 +334,11 @@ bool Bot::BotCastCure(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spe
|
||||
|
||||
bot_spell = GetBestBotSpellForCure(this, tar, spell_type);
|
||||
|
||||
if (!IsValidSpellAndLoS(bot_spell.SpellId, HasLoS())) {
|
||||
if (!IsValidSpell(bot_spell.SpellId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (BotRequiresLoSToCast(spell_type, bot_spell.SpellId) && !HasLoS()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -397,7 +408,11 @@ bool Bot::BotCastPet(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spel
|
||||
bot_spell = GetFirstBotSpellBySpellType(this, spell_type);
|
||||
}
|
||||
|
||||
if (!IsValidSpellAndLoS(bot_spell.SpellId, HasLoS())) {
|
||||
if (!IsValidSpell(bot_spell.SpellId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (BotRequiresLoSToCast(spell_type, bot_spell.SpellId) && !HasLoS()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -418,6 +433,10 @@ bool Bot::BotCastPet(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spel
|
||||
}
|
||||
|
||||
bool Bot::BotCastNuke(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spell_type) {
|
||||
if (BotRequiresLoSToCast(spell_type, bot_spell.SpellId) && !HasLoS()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (spell_type == BotSpellTypes::Stun || spell_type == BotSpellTypes::AEStun) {
|
||||
uint8 stun_chance = (tar->IsCasting() ? RuleI(Bots, StunCastChanceIfCasting) : RuleI(Bots, StunCastChanceNormal));
|
||||
|
||||
@ -434,25 +453,25 @@ bool Bot::BotCastNuke(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spe
|
||||
) {
|
||||
bot_spell = GetBestBotSpellForStunByTargetType(this, ST_TargetOptional, spell_type, IsAEBotSpellType(spell_type), tar);
|
||||
}
|
||||
|
||||
if (!IsValidSpellAndLoS(bot_spell.SpellId, HasLoS())) {
|
||||
|
||||
if (!IsValidSpell(bot_spell.SpellId)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!IsValidSpellAndLoS(bot_spell.SpellId, HasLoS())) {
|
||||
if (!IsValidSpell(bot_spell.SpellId)) {
|
||||
bot_spell = GetBestBotSpellForNukeByBodyType(this, tar->GetBodyType(), spell_type, IsAEBotSpellType(spell_type), tar);
|
||||
}
|
||||
|
||||
if (!IsValidSpellAndLoS(bot_spell.SpellId, HasLoS()) && spell_type == BotSpellTypes::Nuke && bot_class == Class::Wizard) {
|
||||
if (spell_type == BotSpellTypes::Nuke && bot_class == Class::Wizard && !IsValidSpell(bot_spell.SpellId)) {
|
||||
bot_spell = GetBestBotWizardNukeSpellByTargetResists(this, tar, spell_type);
|
||||
}
|
||||
|
||||
if (!IsValidSpellAndLoS(bot_spell.SpellId, HasLoS())) {
|
||||
if (!IsValidSpell(bot_spell.SpellId)) {
|
||||
std::vector<BotSpell_wPriority> bot_spell_list = GetPrioritizedBotSpellsBySpellType(this, spell_type, tar, IsAEBotSpellType(spell_type));
|
||||
|
||||
for (const auto& s : bot_spell_list) {
|
||||
if (!IsValidSpellAndLoS(s.SpellId, HasLoS())) {
|
||||
if (!IsValidSpell(s.SpellId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -910,7 +929,11 @@ std::list<BotSpell> Bot::GetBotSpellsForSpellEffect(Bot* caster, uint16 spell_ty
|
||||
const std::vector<BotSpells_wIndex>& bot_spell_list = caster->BotGetSpellsByType(spell_type);
|
||||
|
||||
for (int i = bot_spell_list.size() - 1; i >= 0; i--) {
|
||||
if (!IsValidSpellAndLoS(bot_spell_list[i].spellid, caster->HasLoS())) {
|
||||
if (!IsValidSpell(bot_spell_list[i].spellid)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (BotRequiresLoSToCast(spell_type, bot_spell_list[i].spellid) && !caster->HasLoS()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -948,7 +971,11 @@ std::list<BotSpell> Bot::GetBotSpellsForSpellEffectAndTargetType(Bot* caster, ui
|
||||
const std::vector<BotSpells_wIndex>& bot_spell_list = caster->BotGetSpellsByType(spell_type);
|
||||
|
||||
for (int i = bot_spell_list.size() - 1; i >= 0; i--) {
|
||||
if (!IsValidSpellAndLoS(bot_spell_list[i].spellid, caster->HasLoS())) {
|
||||
if (!IsValidSpell(bot_spell_list[i].spellid)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (BotRequiresLoSToCast(spell_type, bot_spell_list[i].spellid) && !caster->HasLoS()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -989,7 +1016,11 @@ std::list<BotSpell> Bot::GetBotSpellsBySpellType(Bot* caster, uint16 spell_type)
|
||||
const std::vector<BotSpells_wIndex>& bot_spell_list = caster->BotGetSpellsByType(spell_type);
|
||||
|
||||
for (int i = bot_spell_list.size() - 1; i >= 0; i--) {
|
||||
if (!IsValidSpellAndLoS(bot_spell_list[i].spellid, caster->HasLoS())) {
|
||||
if (!IsValidSpell(bot_spell_list[i].spellid)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (BotRequiresLoSToCast(spell_type, bot_spell_list[i].spellid) && !caster->HasLoS()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -1018,7 +1049,11 @@ std::vector<BotSpell_wPriority> Bot::GetPrioritizedBotSpellsBySpellType(Bot* cas
|
||||
const std::vector<BotSpells_wIndex>& bot_spell_list = caster->BotGetSpellsByType(spell_type);
|
||||
|
||||
for (int i = bot_spell_list.size() - 1; i >= 0; i--) {
|
||||
if (!IsValidSpellAndLoS(bot_spell_list[i].spellid, caster->HasLoS())) {
|
||||
if (!IsValidSpell(bot_spell_list[i].spellid)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (BotRequiresLoSToCast(spell_type, bot_spell_list[i].spellid) && !caster->HasLoS()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -1105,7 +1140,11 @@ BotSpell Bot::GetFirstBotSpellBySpellType(Bot* caster, uint16 spell_type) {
|
||||
const std::vector<BotSpells_wIndex>& bot_spell_list = caster->BotGetSpellsByType(spell_type);
|
||||
|
||||
for (int i = bot_spell_list.size() - 1; i >= 0; i--) {
|
||||
if (!IsValidSpellAndLoS(bot_spell_list[i].spellid, caster->HasLoS())) {
|
||||
if (!IsValidSpell(bot_spell_list[i].spellid)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (BotRequiresLoSToCast(spell_type, bot_spell_list[i].spellid) && !caster->HasLoS()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -1137,7 +1176,6 @@ BotSpell Bot::GetBestBotSpellForVeryFastHeal(Bot* caster, Mob* tar, uint16 spell
|
||||
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_CurrentHP);
|
||||
|
||||
for (auto bot_spell_list_itr : bot_spell_list) {
|
||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
||||
if (
|
||||
IsVeryFastHealSpell(bot_spell_list_itr.SpellId) && caster->CastChecks(bot_spell_list_itr.SpellId, tar, spell_type)) {
|
||||
result.SpellId = bot_spell_list_itr.SpellId;
|
||||
@ -1163,7 +1201,6 @@ BotSpell Bot::GetBestBotSpellForFastHeal(Bot* caster, Mob* tar, uint16 spell_typ
|
||||
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_CurrentHP);
|
||||
|
||||
for (auto bot_spell_list_itr : bot_spell_list) {
|
||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
||||
if (IsFastHealSpell(bot_spell_list_itr.SpellId) && caster->CastChecks(bot_spell_list_itr.SpellId, tar, spell_type)) {
|
||||
result.SpellId = bot_spell_list_itr.SpellId;
|
||||
result.SpellIndex = bot_spell_list_itr.SpellIndex;
|
||||
@ -1188,7 +1225,6 @@ BotSpell Bot::GetBestBotSpellForHealOverTime(Bot* caster, Mob* tar, uint16 spell
|
||||
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_HealOverTime);
|
||||
|
||||
for (auto bot_spell_list_itr : bot_spell_list) {
|
||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
||||
if (IsHealOverTimeSpell(bot_spell_list_itr.SpellId) && caster->CastChecks(bot_spell_list_itr.SpellId, tar, spell_type)) {
|
||||
result.SpellId = bot_spell_list_itr.SpellId;
|
||||
result.SpellIndex = bot_spell_list_itr.SpellIndex;
|
||||
@ -1245,7 +1281,6 @@ BotSpell Bot::GetBestBotSpellForRegularSingleTargetHeal(Bot* caster, Mob* tar, u
|
||||
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_CurrentHP);
|
||||
|
||||
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
||||
if (IsRegularSingleTargetHealSpell(bot_spell_list_itr->SpellId) && caster->CastChecks(bot_spell_list_itr->SpellId, tar, spell_type)) {
|
||||
result.SpellId = bot_spell_list_itr->SpellId;
|
||||
result.SpellIndex = bot_spell_list_itr->SpellIndex;
|
||||
@ -1270,7 +1305,6 @@ BotSpell Bot::GetFirstBotSpellForSingleTargetHeal(Bot* caster, Mob* tar, uint16
|
||||
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_CurrentHP);
|
||||
|
||||
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
||||
if (IsRegularSingleTargetHealSpell(bot_spell_list_itr->SpellId) && caster->CastChecks(bot_spell_list_itr->SpellId, tar, spell_type)) {
|
||||
result.SpellId = bot_spell_list_itr->SpellId;
|
||||
result.SpellIndex = bot_spell_list_itr->SpellIndex;
|
||||
@ -1300,7 +1334,6 @@ BotSpell Bot::GetBestBotSpellForGroupHeal(Bot* caster, Mob* tar, uint16 spell_ty
|
||||
int required_count = caster->GetSpellTypeAEOrGroupTargetCount(spell_type);
|
||||
|
||||
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
||||
if (IsRegularGroupHealSpell(bot_spell_list_itr->SpellId)) {
|
||||
uint16 spell_id = bot_spell_list_itr->SpellId;
|
||||
|
||||
@ -1339,7 +1372,6 @@ BotSpell Bot::GetBestBotSpellForGroupHealOverTime(Bot* caster, Mob* tar, uint16
|
||||
int required_count = caster->GetSpellTypeAEOrGroupTargetCount(spell_type);
|
||||
|
||||
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
||||
if (IsGroupHealOverTimeSpell(bot_spell_list_itr->SpellId)) {
|
||||
uint16 spell_id = bot_spell_list_itr->SpellId;
|
||||
|
||||
@ -1378,7 +1410,6 @@ BotSpell Bot::GetBestBotSpellForGroupCompleteHeal(Bot* caster, Mob* tar, uint16
|
||||
int required_count = caster->GetSpellTypeAEOrGroupTargetCount(spell_type);
|
||||
|
||||
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
||||
if (IsGroupCompleteHealSpell(bot_spell_list_itr->SpellId)) {
|
||||
uint16 spell_id = bot_spell_list_itr->SpellId;
|
||||
|
||||
@ -1412,7 +1443,6 @@ BotSpell Bot::GetBestBotSpellForMez(Bot* caster, uint16 spell_type) {
|
||||
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_Mez);
|
||||
|
||||
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
||||
if (
|
||||
IsMesmerizeSpell(bot_spell_list_itr->SpellId) &&
|
||||
caster->CheckSpellRecastTimer(bot_spell_list_itr->SpellId)
|
||||
@ -1435,11 +1465,10 @@ Mob* Bot::GetFirstIncomingMobToMez(Bot* caster, int16 spell_id, uint16 spell_typ
|
||||
if (caster && caster->GetOwner()) {
|
||||
int spell_range = caster->GetActSpellRange(spell_id, spells[spell_id].range);
|
||||
int spell_ae_range = caster->GetAOERange(spell_id);
|
||||
int buff_count = 0;
|
||||
bool is_pbae_spell = IsPBAESpell(spell_id);
|
||||
NPC* npc = nullptr;
|
||||
|
||||
for (auto& close_mob : caster->m_close_mobs) {
|
||||
buff_count = 0;
|
||||
npc = close_mob.second->CastToNPC();
|
||||
|
||||
if (!npc) {
|
||||
@ -1450,29 +1479,29 @@ Mob* Bot::GetFirstIncomingMobToMez(Bot* caster, int16 spell_id, uint16 spell_typ
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_pbae_spell) {
|
||||
if (spell_ae_range < Distance(caster->GetPosition(), npc->GetPosition())) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (spell_range < Distance(caster->GetPosition(), npc->GetPosition())) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (AE) {
|
||||
int target_count = 0;
|
||||
|
||||
for (auto& close_mob : caster->m_close_mobs) {
|
||||
Mob* m = close_mob.second;
|
||||
|
||||
if (npc == m) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!caster->IsValidMezTarget(caster->GetOwner(), m, spell_id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IsPBAESpell(spell_id)) {
|
||||
if (spell_ae_range < Distance(caster->GetPosition(), m->GetPosition())) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (spell_range < Distance(m->GetPosition(), npc->GetPosition())) {
|
||||
continue;
|
||||
}
|
||||
if (spell_ae_range < Distance(npc->GetPosition(), m->GetPosition())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (caster->CastChecks(spell_id, m, spell_type, true, true)) {
|
||||
@ -1488,11 +1517,6 @@ Mob* Bot::GetFirstIncomingMobToMez(Bot* caster, int16 spell_id, uint16 spell_typ
|
||||
continue;
|
||||
}
|
||||
|
||||
if (zone->random.Int(1, 100) < RuleI(Bots, AEMezChance)) {
|
||||
caster->SetSpellTypeRecastTimer(spell_type, RuleI(Bots, MezFailDelay));
|
||||
return result;
|
||||
}
|
||||
|
||||
result = npc;
|
||||
}
|
||||
else {
|
||||
@ -1504,18 +1528,10 @@ Mob* Bot::GetFirstIncomingMobToMez(Bot* caster, int16 spell_id, uint16 spell_typ
|
||||
continue;
|
||||
}
|
||||
|
||||
if (zone->random.Int(1, 100) < RuleI(Bots, MezChance)) {
|
||||
caster->SetSpellTypeRecastTimer(spell_type, RuleI(Bots, MezAEFailDelay));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
result = npc;
|
||||
}
|
||||
|
||||
if (result) {
|
||||
caster->SetHasLoS(true);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@ -1536,7 +1552,6 @@ BotSpell Bot::GetBestBotMagicianPetSpell(Bot* caster, uint16 spell_type) {
|
||||
std::string pet_type = GetBotMagicianPetType(caster);
|
||||
|
||||
for(std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
||||
if (
|
||||
IsSummonPetSpell(bot_spell_list_itr->SpellId) &&
|
||||
caster->CheckSpellRecastTimer(bot_spell_list_itr->SpellId) &&
|
||||
@ -1718,7 +1733,6 @@ BotSpell Bot::GetBestBotSpellForNukeByTargetType(Bot* caster, SpellTargetType ta
|
||||
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffectAndTargetType(caster, spell_type, SE_CurrentHP, target_type);
|
||||
|
||||
for(std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
||||
if (IsPureNukeSpell(bot_spell_list_itr->SpellId) || IsDamageSpell(bot_spell_list_itr->SpellId)) {
|
||||
if (!AE && IsAnyAESpell(bot_spell_list_itr->SpellId) && !IsGroupSpell(bot_spell_list_itr->SpellId)) {
|
||||
continue;
|
||||
@ -1731,7 +1745,6 @@ BotSpell Bot::GetBestBotSpellForNukeByTargetType(Bot* caster, SpellTargetType ta
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (
|
||||
caster->IsCommandedSpell() ||
|
||||
!AE ||
|
||||
@ -1768,7 +1781,6 @@ BotSpell Bot::GetBestBotSpellForStunByTargetType(Bot* caster, SpellTargetType ta
|
||||
|
||||
for(std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr)
|
||||
{
|
||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
||||
if (IsStunSpell(bot_spell_list_itr->SpellId)) {
|
||||
if (!AE && IsAnyAESpell(bot_spell_list_itr->SpellId) && !IsGroupSpell(bot_spell_list_itr->SpellId)) {
|
||||
continue;
|
||||
@ -1832,7 +1844,6 @@ BotSpell Bot::GetBestBotWizardNukeSpellByTargetResists(Bot* caster, Mob* target,
|
||||
bool spell_selected = false;
|
||||
|
||||
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
||||
if (!caster->IsValidSpellRange(bot_spell_list_itr->SpellId, target)) {
|
||||
continue;
|
||||
}
|
||||
@ -1889,8 +1900,6 @@ BotSpell Bot::GetBestBotWizardNukeSpellByTargetResists(Bot* caster, Mob* target,
|
||||
|
||||
if (!spell_selected) {
|
||||
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
||||
|
||||
if (caster->CheckSpellRecastTimer(bot_spell_list_itr->SpellId)) {
|
||||
if (caster->CastChecks(bot_spell_list_itr->SpellId, target, spell_type)) {
|
||||
spell_selected = true;
|
||||
@ -1928,7 +1937,11 @@ BotSpell Bot::GetDebuffBotSpell(Bot* caster, Mob *tar, uint16 spell_type) {
|
||||
const std::vector<BotSpells_wIndex>& bot_spell_list = caster->BotGetSpellsByType(spell_type);
|
||||
|
||||
for (int i = bot_spell_list.size() - 1; i >= 0; i--) {
|
||||
if (!IsValidSpellAndLoS(bot_spell_list[i].spellid, caster->HasLoS())) {
|
||||
if (!IsValidSpell(bot_spell_list[i].spellid)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (BotRequiresLoSToCast(spell_type, bot_spell_list[i].spellid) && !caster->HasLoS()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -1974,7 +1987,11 @@ BotSpell Bot::GetBestBotSpellForResistDebuff(Bot* caster, Mob *tar, uint16 spell
|
||||
const std::vector<BotSpells_wIndex>& bot_spell_list = caster->BotGetSpellsByType(spell_type);
|
||||
|
||||
for (int i = bot_spell_list.size() - 1; i >= 0; i--) {
|
||||
if (!IsValidSpellAndLoS(bot_spell_list[i].spellid, caster->HasLoS())) {
|
||||
if (!IsValidSpell(bot_spell_list[i].spellid)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (BotRequiresLoSToCast(spell_type, bot_spell_list[i].spellid) && !caster->HasLoS()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -2093,7 +2110,6 @@ uint8 Bot::GetChanceToCastBySpellType(uint16 spell_type)
|
||||
case BotSpellTypes::AERains:
|
||||
case BotSpellTypes::AEStun:
|
||||
case BotSpellTypes::AESnare:
|
||||
case BotSpellTypes::AEMez:
|
||||
case BotSpellTypes::AESlow:
|
||||
case BotSpellTypes::AEDebuff:
|
||||
case BotSpellTypes::AEFear:
|
||||
@ -2159,6 +2175,8 @@ uint8 Bot::GetChanceToCastBySpellType(uint16 spell_type)
|
||||
case BotSpellTypes::PetVeryFastHeals:
|
||||
case BotSpellTypes::PetHoTHeals:
|
||||
return RuleI(Bots, PercentChanceToCastHeal);
|
||||
case BotSpellTypes::AEMez:
|
||||
return RuleI(Bots, PercentChanceToCastAEMez);
|
||||
default:
|
||||
return RuleI(Bots, PercentChanceToCastOtherType);
|
||||
}
|
||||
@ -2823,7 +2841,6 @@ BotSpell Bot::GetBestBotSpellForRez(Bot* caster, Mob* target, uint16 spell_type)
|
||||
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_Revive);
|
||||
|
||||
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
||||
if (
|
||||
IsResurrectSpell(bot_spell_list_itr->SpellId) &&
|
||||
caster->CheckSpellRecastTimer(bot_spell_list_itr->SpellId)
|
||||
@ -2851,7 +2868,6 @@ BotSpell Bot::GetBestBotSpellForCharm(Bot* caster, Mob* target, uint16 spell_typ
|
||||
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_Charm);
|
||||
|
||||
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
||||
if (
|
||||
IsCharmSpell(bot_spell_list_itr->SpellId) &&
|
||||
caster->CastChecks(bot_spell_list_itr->SpellId, target, spell_type)
|
||||
|
||||
@ -11066,7 +11066,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app)
|
||||
if (!target)
|
||||
break;
|
||||
|
||||
if (RuleB(Map, CheckForLoSCheat) && (!DoLosChecks(target) || !CheckLosCheat(target))) {
|
||||
if (RuleB(Pets, PetsRequireLoS) && !DoLosChecks(target)) {
|
||||
mypet->SayString(this, NOT_LEGAL_TARGET);
|
||||
break;
|
||||
}
|
||||
@ -11134,7 +11134,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app)
|
||||
break;
|
||||
}
|
||||
|
||||
if (RuleB(Map, CheckForLoSCheat) && (!DoLosChecks(GetTarget()) || !CheckLosCheat(GetTarget()))) {
|
||||
if (RuleB(Pets, PetsRequireLoS) && !DoLosChecks(GetTarget())) {
|
||||
mypet->SayString(this, NOT_LEGAL_TARGET);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -35,6 +35,9 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <glm/ext/matrix_transform.hpp>
|
||||
#include <numbers>
|
||||
|
||||
#define OPEN_DOOR 0x02
|
||||
#define CLOSE_DOOR 0x03
|
||||
#define OPEN_INVDOOR 0x03
|
||||
@ -970,3 +973,68 @@ bool Doors::GetIsDoorBlacklisted()
|
||||
bool Doors::IsDoorBlacklisted() {
|
||||
return m_is_blacklisted_to_open;
|
||||
}
|
||||
|
||||
bool Doors::IsDoorBetween(glm::vec4 loc_a, glm::vec4 loc_c, uint16 door_size, float door_depth, bool draw_box) {
|
||||
glm::vec4 door_loc = GetPosition();
|
||||
float half_size = door_size * 0.5f;
|
||||
float half_depth = door_depth * 0.5f;
|
||||
float normalized_heading = std::fmod(door_loc.w, 512.0f);
|
||||
float heading_radians = normalized_heading * (std::numbers::pi / 256.0f);
|
||||
glm::mat4 door_rotation = glm::rotate(glm::mat4(1.0f), -heading_radians, glm::vec3(0.0f, 0.0f, 1.0f));
|
||||
glm::vec3 box_corner_one = glm::vec3(door_size, -half_depth, 0.0f);
|
||||
glm::vec3 box_corner_two = glm::vec3(-door_size, -half_depth, 0.0f);
|
||||
glm::vec3 box_corner_three = glm::vec3(-door_size, half_depth, 0.0f);
|
||||
glm::vec3 box_corner_four = glm::vec3(door_size, half_depth, 0.0f);
|
||||
glm::vec3 door_center_offset = glm::vec3(-(door_size * 0.75f), half_depth * 0.5f, 0.0f);
|
||||
glm::vec3 door_center = glm::vec3(door_loc) + glm::vec3(door_rotation * glm::vec4(door_center_offset, 1.0f));
|
||||
glm::mat4 transform = glm::translate(glm::mat4(1.0f), door_center) * door_rotation;
|
||||
|
||||
box_corner_one = glm::vec3(transform * glm::vec4(box_corner_one, 1.0f));
|
||||
box_corner_two = glm::vec3(transform * glm::vec4(box_corner_two, 1.0f));
|
||||
box_corner_three = glm::vec3(transform * glm::vec4(box_corner_three, 1.0f));
|
||||
box_corner_four = glm::vec3(transform * glm::vec4(box_corner_four, 1.0f));
|
||||
|
||||
if (draw_box) {
|
||||
NPC::SpawnZonePointNodeNPC("loc_a", loc_a);
|
||||
NPC::SpawnZonePointNodeNPC("door_anchor", door_loc);
|
||||
NPC::SpawnZonePointNodeNPC("loc_c", loc_c);
|
||||
NPC::SpawnZonePointNodeNPC("box_corner_one", glm::vec4(box_corner_one.x, box_corner_one.y, box_corner_one.z, 0));
|
||||
NPC::SpawnZonePointNodeNPC("box_corner_two", glm::vec4(box_corner_two.x, box_corner_two.y, box_corner_two.z, 0));
|
||||
NPC::SpawnZonePointNodeNPC("box_corner_three", glm::vec4(box_corner_three.x, box_corner_three.y, box_corner_three.z, 0));
|
||||
NPC::SpawnZonePointNodeNPC("box_corner_four", glm::vec4(box_corner_four.x, box_corner_four.y, box_corner_four.z, 0));
|
||||
NPC::SpawnZonePointNodeNPC("box_center", glm::vec4(door_center.x, door_center.y, door_center.z, 0));
|
||||
}
|
||||
|
||||
// Check if LoS intersects box
|
||||
auto intersects_box = [](const glm::vec3& a, const glm::vec3& b, const glm::vec3& p1, const glm::vec3& p2) {
|
||||
glm::vec3 ab = b - a;
|
||||
glm::vec3 p1p2 = p2 - p1;
|
||||
|
||||
glm::vec3 cross = glm::cross(ab, p1p2);
|
||||
float cross_magnitude_squared = glm::dot(cross, cross);
|
||||
|
||||
if (cross_magnitude_squared < 1e-6f) {
|
||||
return false; // Lines are parallel or coincident
|
||||
}
|
||||
|
||||
float t = glm::dot(glm::cross(p1 - a, p1p2), cross) / cross_magnitude_squared;
|
||||
float u = glm::dot(glm::cross(p1 - a, ab), cross) / cross_magnitude_squared;
|
||||
|
||||
return (t >= 0.0f && t <= 1.0f && u >= 0.0f && u <= 1.0f);
|
||||
};
|
||||
|
||||
// Check intersection with each edge of the door bounding box
|
||||
glm::vec3 loc_a_vec3(loc_a.x, loc_a.y, loc_a.z);
|
||||
glm::vec3 loc_c_vec3(loc_c.x, loc_c.y, loc_c.z);
|
||||
|
||||
if (
|
||||
intersects_box(loc_a_vec3, loc_c_vec3, box_corner_one, box_corner_two) ||
|
||||
intersects_box(loc_a_vec3, loc_c_vec3, box_corner_two, box_corner_three) ||
|
||||
intersects_box(loc_a_vec3, loc_c_vec3, box_corner_three, box_corner_four) ||
|
||||
intersects_box(loc_a_vec3, loc_c_vec3, box_corner_four, box_corner_one)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -76,6 +76,7 @@ public:
|
||||
bool IsDestinationZoneSame() const;
|
||||
|
||||
bool IsDoorBlacklisted();
|
||||
bool IsDoorBetween(glm::vec4 loc_a, glm::vec4 loc_c, uint16 door_size = 15, float door_depth = 5.0f, bool draw_box = false);
|
||||
|
||||
const char* GetDoorZone() const { return m_zone_name; }
|
||||
|
||||
|
||||
@ -35,6 +35,26 @@ void DoorManipulation::CommandHandler(Client *c, const Seperator *sep)
|
||||
);
|
||||
}
|
||||
|
||||
if (arg1 == "drawbox") {
|
||||
Doors *door = entity_list.GetDoorsByID(c->GetDoorToolEntityId());
|
||||
|
||||
if (door) {
|
||||
uint16 door_size = 15;
|
||||
float door_depth = 5.0f;
|
||||
|
||||
if (sep->IsNumber(2) && atof(sep->arg[2]) > 0) {
|
||||
door_size = atof(sep->arg[2]);
|
||||
}
|
||||
|
||||
if (sep->IsNumber(3) && atof(sep->arg[3]) > 0) {
|
||||
door_depth = atof(sep->arg[3]);
|
||||
}
|
||||
|
||||
|
||||
door->IsDoorBetween(c->GetPosition(), (c->GetTarget() ? c->GetTarget()->GetPosition() : c->GetPosition()), door_size, door_depth, true);
|
||||
}
|
||||
}
|
||||
|
||||
// edit menu
|
||||
if (arg1 == "edit") {
|
||||
Doors *door = entity_list.GetDoorsByID(c->GetDoorToolEntityId());
|
||||
@ -544,6 +564,13 @@ void DoorManipulation::CommandHandler(Client *c, const Seperator *sep)
|
||||
c->Message(Chat::White, "#door setinvertstate [0|1] | Sets selected door invert state");
|
||||
c->Message(Chat::White, "#door setincline <incline> | Sets selected door incline");
|
||||
c->Message(Chat::White, "#door opentype <opentype> | Sets selected door opentype");
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"{} <door_size> <door_depth> | Draws a box for the door, default size = 15, depth = 5 if none defined",
|
||||
Saylink::Silent("#door drawbox")
|
||||
).c_str()
|
||||
);
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
|
||||
82
zone/mob.cpp
82
zone/mob.cpp
@ -8675,56 +8675,54 @@ bool Mob::IsInGroupOrRaid(Mob* other, bool same_raid_group) {
|
||||
|
||||
bool Mob::DoLosChecks(Mob* other) {
|
||||
if (!CheckLosFN(other) || !CheckWaterLoS(other)) {
|
||||
if (CheckLosCheatExempt(other)) {
|
||||
if (RuleB(Map, EnableLoSCheatExemptions) && CheckLosCheatExempt(other)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CheckLosCheat(other)) {
|
||||
if (RuleB(Map, CheckForDoorLoSCheat) && !CheckDoorLoSCheat(other)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Mob::CheckLosCheat(Mob* other) {
|
||||
if (RuleB(Map, CheckForLoSCheat)) {
|
||||
for (auto itr : entity_list.GetDoorsList()) {
|
||||
Doors* d = itr.second;
|
||||
bool Mob::CheckDoorLoSCheat(Mob* other) {
|
||||
if (!other->IsOfClientBotMerc() && other->CastToNPC()->IsOnHatelist(this)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::string& zones_to_check = RuleS(Map, ZonesToCheckDoorCheat);
|
||||
|
||||
if (zones_to_check.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto& v = Strings::Split(zones_to_check, ",");
|
||||
|
||||
if (zones_to_check == "all" || std::find(v.begin(), v.end(), std::to_string(zone->GetZoneID())) != v.end()) {
|
||||
for (auto itr: entity_list.GetDoorsList()) {
|
||||
Doors *d = itr.second;
|
||||
|
||||
if (
|
||||
!d->IsDoorOpen() &&
|
||||
(
|
||||
d->GetKeyItem() ||
|
||||
d->GetLockpick() ||
|
||||
d->IsDoorOpen() ||
|
||||
d->IsDoorBlacklisted() ||
|
||||
d->GetNoKeyring() != 0 ||
|
||||
d->GetDoorParam() > 0
|
||||
d->GetNoKeyring() != 0
|
||||
)
|
||||
) {
|
||||
// If the door is a trigger door, check if the trigger door is open
|
||||
if (d->GetTriggerDoorID() > 0) {
|
||||
auto td = entity_list.GetDoorsByDoorID(d->GetTriggerDoorID());
|
||||
) {
|
||||
float distance = Distance(m_Position, d->GetPosition());
|
||||
|
||||
if (td) {
|
||||
if (Strings::RemoveNumbers(d->GetDoorName()) != Strings::RemoveNumbers(td->GetDoorName())) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (distance > RuleR(Map, RangeCheckForDoorLoSCheat) || !CheckLosFN(d->GetX(), d->GetY(), d->GetZ(), GetSize())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (DistanceNoZ(GetPosition(), d->GetPosition()) <= 50) {
|
||||
auto who_to_door = DistanceNoZ(GetPosition(), d->GetPosition());
|
||||
auto other_to_door = DistanceNoZ(other->GetPosition(), d->GetPosition());
|
||||
auto who_to_other = DistanceNoZ(GetPosition(), other->GetPosition());
|
||||
auto distance_difference = who_to_other - (who_to_door + other_to_door);
|
||||
|
||||
if (distance_difference >= (-1 * RuleR(Maps, RangeCheckForLoSCheat)) && distance_difference <= RuleR(Maps, RangeCheckForLoSCheat)) {
|
||||
return false;
|
||||
}
|
||||
if (d->IsDoorBetween(GetPosition(), other->GetPosition(), d->GetSize())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -8733,26 +8731,18 @@ bool Mob::CheckLosCheat(Mob* other) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Mob::CheckLosCheatExempt(Mob* other)
|
||||
{
|
||||
if (RuleB(Map, EnableLoSCheatExemptions)) {
|
||||
/* This is an exmaple of how to configure exemptions for LoS checks.
|
||||
glm::vec4 exempt_check_who;
|
||||
glm::vec4 exempt_check_other;
|
||||
bool Mob::CheckLosCheatExempt(Mob* other) {
|
||||
glm::vec4 exempt_check_who;
|
||||
|
||||
switch (zone->GetZoneID()) {
|
||||
case POEARTHB:
|
||||
exempt_check_who.x = 2051; exempt_check_who.y = 407; exempt_check_who.z = -219; //Middle of councilman spawns
|
||||
//exempt_check_other.x = 1455; exempt_check_other.y = 415; exempt_check_other.z = -242;
|
||||
//check to be sure the player and the target are outside of the councilman area
|
||||
//if the player is inside the cove they cannot be higher than the ceiling (no exploiting from uptop)
|
||||
if (GetZ() <= -171 && other->GetZ() <= -171 && DistanceNoZ(other->GetPosition(), exempt_check_who) <= 800 && DistanceNoZ(GetPosition(), exempt_check_who) <= 800) {
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
switch (zone->GetZoneID()) {
|
||||
case Zones::POEARTHB:
|
||||
exempt_check_who.x = 2053; exempt_check_who.y = 408; exempt_check_who.z = -219; //Middle of councilman spawns
|
||||
//if the player is inside the cove they cannot be higher than the ceiling (no exploiting from uptop) --- 800 from center of council to furthest corner in cove
|
||||
if (GetZ() <= -171 && other->GetZ() <= -171 && DistanceNoZ(other->GetPosition(), exempt_check_who) <= 800 && DistanceNoZ(GetPosition(), exempt_check_who) <= 800) {
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@ -798,7 +798,7 @@ public:
|
||||
static bool CheckLosFN(glm::vec3 posWatcher, float sizeWatcher, glm::vec3 posTarget, float sizeTarget);
|
||||
virtual bool CheckWaterLoS(Mob* m);
|
||||
bool CheckPositioningLosFN(Mob* other, float posX, float posY, float posZ);
|
||||
bool CheckLosCheat(Mob* other); //door skipping checks for LoS
|
||||
bool CheckDoorLoSCheat(Mob* other); //door skipping checks for LoS
|
||||
bool CheckLosCheatExempt(Mob* other); //exemptions to bypass los
|
||||
bool DoLosChecks(Mob* other);
|
||||
inline void SetLastLosState(bool value) { last_los_check = value; }
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user