diff --git a/AC/NPCBots.patch b/AC/NPCBots.patch index 07e6f5a..ccab2c1 100644 --- a/AC/NPCBots.patch +++ b/AC/NPCBots.patch @@ -66,8 +66,8 @@ src/server/game/AI/CreatureAI.h | 5 + src/server/game/AI/NpcBots/bot_Events.h | 74 + src/server/game/AI/NpcBots/bot_GridNotifiers.h | 1408 ++ - src/server/game/AI/NpcBots/bot_ai.cpp | 19735 +++++++++++++++++++ - src/server/game/AI/NpcBots/bot_ai.h | 804 + + src/server/game/AI/NpcBots/bot_ai.cpp | 19766 +++++++++++++++++++ + src/server/game/AI/NpcBots/bot_ai.h | 806 + src/server/game/AI/NpcBots/bot_archmage_ai.cpp | 417 + src/server/game/AI/NpcBots/bot_bm_ai.cpp | 962 + src/server/game/AI/NpcBots/bot_crypt_lord_ai.cpp | 844 + @@ -85,8 +85,8 @@ src/server/game/AI/NpcBots/bot_shaman_ai.cpp | 2844 +++ src/server/game/AI/NpcBots/bot_spellbreaker_ai.cpp | 630 + src/server/game/AI/NpcBots/bot_sphynx_ai.cpp | 568 + - src/server/game/AI/NpcBots/bot_warlock_ai.cpp | 2101 ++ - src/server/game/AI/NpcBots/bot_warrior_ai.cpp | 2194 +++ + src/server/game/AI/NpcBots/bot_warlock_ai.cpp | 2102 ++ + src/server/game/AI/NpcBots/bot_warrior_ai.cpp | 2194 ++ src/server/game/AI/NpcBots/botcommands.cpp | 4194 ++++ src/server/game/AI/NpcBots/botcommon.h | 583 + src/server/game/AI/NpcBots/botdatamgr.cpp | 3080 +++ @@ -99,8 +99,8 @@ src/server/game/AI/NpcBots/botgearscore.h | 16 + src/server/game/AI/NpcBots/botgiver.cpp | 287 + src/server/game/AI/NpcBots/botgossip.h | 141 + - src/server/game/AI/NpcBots/botmgr.cpp | 2826 +++ - src/server/game/AI/NpcBots/botmgr.h | 336 + + src/server/game/AI/NpcBots/botmgr.cpp | 2831 +++ + src/server/game/AI/NpcBots/botmgr.h | 337 + src/server/game/AI/NpcBots/botspell.cpp | 1899 ++ src/server/game/AI/NpcBots/botspell.h | 303 + src/server/game/AI/NpcBots/bottext.h | 414 + @@ -145,7 +145,7 @@ src/server/game/Entities/GameObject/GameObject.cpp | 79 + src/server/game/Entities/Object/Object.cpp | 45 +- src/server/game/Entities/Object/Object.h | 13 +- - src/server/game/Entities/Player/KillRewarder.cpp | 19 + + src/server/game/Entities/Player/KillRewarder.cpp | 29 + src/server/game/Entities/Player/Player.cpp | 169 + src/server/game/Entities/Player/Player.h | 24 + src/server/game/Entities/Player/PlayerStorage.cpp | 8 + @@ -153,7 +153,7 @@ src/server/game/Entities/Totem/Totem.cpp | 25 + src/server/game/Entities/Transport/Transport.cpp | 11 + src/server/game/Entities/Unit/StatSystem.cpp | 50 + - src/server/game/Entities/Unit/Unit.cpp | 1507 +- + src/server/game/Entities/Unit/Unit.cpp | 1512 +- src/server/game/Entities/Unit/Unit.h | 67 +- src/server/game/Entities/Vehicle/Vehicle.cpp | 44 + src/server/game/Entities/Vehicle/Vehicle.h | 6 + @@ -212,7 +212,7 @@ src/server/shared/DataStores/DBCStructure.h | 6 +- src/server/shared/DataStores/DBCfmt.h | 2 +- src/server/shared/SharedDefines.h | 23 + - 214 files changed, 92208 insertions(+), 69 deletions(-) + 214 files changed, 92263 insertions(+), 69 deletions(-) create mode 100644 data/sql/Bots/locales/esES/npc_text_locale.sql create mode 100644 data/sql/Bots/locales/esMX/npc_text_locale.sql create mode 100644 data/sql/Bots/locales/ruRU/npc_text_locale.sql @@ -13762,10 +13762,10 @@ index 0000000000..fdda703289 +#endif diff --git a/src/server/game/AI/NpcBots/bot_ai.cpp b/src/server/game/AI/NpcBots/bot_ai.cpp new file mode 100644 -index 0000000000..94a8f5a483 +index 0000000000..8773986bd5 --- /dev/null +++ b/src/server/game/AI/NpcBots/bot_ai.cpp -@@ -0,0 +1,19735 @@ +@@ -0,0 +1,19766 @@ +#include "Battleground.h" +#include "BattlegroundAB.h" +#include "BattlegroundAV.h" @@ -14028,6 +14028,8 @@ index 0000000000..94a8f5a483 + _lastAreaId = 0; + _lastWMOAreaId = 0; + ++ _selfrez_spell_id = 0; ++ + _wmoAreaUpdateTimer = 0; + + _contestedPvPTimer = 0; @@ -29222,6 +29224,29 @@ index 0000000000..94a8f5a483 + + ++_deathsCount; +} ++//This is triggered before SetDeathState(JUST_DIED) call ++//attacker may be NULL ++void bot_ai::OnDeath([[maybe_unused]] Unit* attacker/* = nullptr*/) ++{ ++ if (AuraEffect const* sstone = me->GetDummyAuraEffect(SPELLFAMILY_GENERIC, 92, 0)) ++ { ++ uint32 spell_id; ++ switch (sstone->GetBase()->GetId()) ++ { ++ case 20707: spell_id = 3026; break; // rank 1 ++ case 20762: spell_id = 20758; break; // rank 2 ++ case 20763: spell_id = 20759; break; // rank 3 ++ case 20764: spell_id = 20760; break; // rank 4 ++ case 20765: spell_id = 20761; break; // rank 5 ++ case 27239: spell_id = 27240; break; // rank 6 ++ case 47883: spell_id = 47882; break; // rank 7 ++ default: spell_id = 0; break; ++ } ++ _selfrez_spell_id = spell_id; ++ } ++ else ++ _selfrez_spell_id = 0; ++} + +void bot_ai::KilledUnit(Unit* u) +{ @@ -29350,6 +29375,8 @@ index 0000000000..94a8f5a483 + StartPotionTimer(); + if (curInfo->Id == ACTIVATE_SPEC) + SetSpec(_newspec); ++ if (curInfo->Id == _selfrez_spell_id) ++ OnSpellHit(me, curInfo); + + OnClassSpellGo(curInfo); + } @@ -30659,6 +30686,10 @@ index 0000000000..94a8f5a483 + // group update + if (_groupUpdateTimer <= diff) + SendUpdateToOutOfRangeBotGroupMembers(); ++ ++ // soulstone ++ if (_selfrez_spell_id && (IAmFree() || !master->GetBotMgr()->IsPartyInCombat()) && Rand() < 15) ++ me->CastSpell(me, _selfrez_spell_id); +} +//opponent unsafe +bool bot_ai::GlobalUpdate(uint32 diff) @@ -33503,10 +33534,10 @@ index 0000000000..94a8f5a483 +#endif diff --git a/src/server/game/AI/NpcBots/bot_ai.h b/src/server/game/AI/NpcBots/bot_ai.h new file mode 100644 -index 0000000000..ec861a9f77 +index 0000000000..f4912052d5 --- /dev/null +++ b/src/server/game/AI/NpcBots/bot_ai.h -@@ -0,0 +1,804 @@ +@@ -0,0 +1,806 @@ +#ifndef _BOT_AI_H +#define _BOT_AI_H + @@ -33575,6 +33606,7 @@ index 0000000000..ec861a9f77 + //void LeavingWorld() override { } + void OnSpellStart(SpellInfo const* spellInfo) override { OnBotSpellStart(spellInfo); } + bool CanRespawn() override { return IAmFree(); } ++ void OnDeath(Unit* attacker = nullptr); + + virtual void OnBotSummon(Creature* /*summon*/) {} + virtual void OnBotDespawn(Creature* /*summon*/) {} @@ -34200,6 +34232,7 @@ index 0000000000..ec861a9f77 + uint32 _saveDisabledSpellsTimer; + + uint32 _lastZoneId, _lastAreaId, _lastWMOAreaId; ++ uint32 _selfrez_spell_id; + + uint8 _unreachableCount, _jumpCount, _evadeCount; + uint8 _healHpPctThreshold; @@ -59210,10 +59243,10 @@ index 0000000000..402864a71c +} diff --git a/src/server/game/AI/NpcBots/bot_warlock_ai.cpp b/src/server/game/AI/NpcBots/bot_warlock_ai.cpp new file mode 100644 -index 0000000000..b1da245c0d +index 0000000000..289ac9c948 --- /dev/null +++ b/src/server/game/AI/NpcBots/bot_warlock_ai.cpp -@@ -0,0 +1,2101 @@ +@@ -0,0 +1,2102 @@ +#include "bot_ai.h" +#include "botmgr.h" +#include "botspell.h" @@ -59493,7 +59526,7 @@ index 0000000000..b1da245c0d + return; + } + -+ if (!hasSoulstone && !IAmFree() && GetSpell(CREATE_SOULSTONE_1)) ++ if (!hasSoulstone && GetSpell(CREATE_SOULSTONE_1)) + { + if (doCast(me, GetSpell(CREATE_SOULSTONE_1))) + return; @@ -59508,40 +59541,41 @@ index 0000000000..b1da245c0d + + //TODO: soulstone on self/bots + //BUG: players cannot accept this buff if they are below lvl 20 (should be 8) -+ if (!IAmFree() && hasSoulstone && soulstoneTimer <= diff && GetSpell(CREATE_SOULSTONE_1)) ++ if (hasSoulstone && soulstoneTimer <= diff && GetSpell(CREATE_SOULSTONE_1)) + { -+ Group const* gr = master->GetGroup(); -+ std::set targets; -+ if (!gr) -+ { -+ if (master->IsAlive() && !master->isPossessed() && !master->IsCharmed() && -+ me->GetDistance(master) < 30 && !master->GetDummyAuraEffect(SPELLFAMILY_GENERIC, 92, 0)) -+ targets.insert(master); -+ } -+ else ++ std::vector targets; ++ ++ if (!IAmFree()) + { -+ for (uint8 i = 0; i < 2 && !targets.empty(); ++i) ++ std::vector all_members = BotMgr::GetAllGroupMembers(master->GetGroup()); ++ for (uint8 i = 0; i < 3; ++i) + { -+ for (Unit* member : BotMgr::GetAllGroupMembers(gr)) ++ if (i > 0 && !targets.empty()) ++ break; ++ for (Unit* member : all_members) + { -+ if ((i == 0 ? member->IsPlayer() : member->IsNPCBot()) && me->GetMap() == member->FindMap() && ++ if ((i >= 2 || (i == 0 ? member->IsPlayer() : member->IsNPCBot())) && me->GetMap() == member->FindMap() && + member->IsAlive() && !member->isPossessed() && !member->IsCharmed() && + !(member->IsNPCBot() && member->ToCreature()->IsTempBot()) && + me->GetDistance(member) < 30 && !member->GetDummyAuraEffect(SPELLFAMILY_GENERIC, 92, 0)) + { -+ if (i > 0 || member->GetClass() == CLASS_PRIEST || member->GetClass() == CLASS_PALADIN || ++ if (i >= 2 || member->GetClass() == CLASS_PRIEST || member->GetClass() == CLASS_PALADIN || + member->GetClass() == CLASS_DRUID || member->GetClass() == CLASS_SHAMAN) + { -+ targets.insert(member); ++ targets.push_back(member); + } + } + } + } + } + ++ if (targets.empty() && master->IsAlive() && !master->isPossessed() && !master->IsCharmed() && ++ me->GetDistance(master) < 30 && !master->GetDummyAuraEffect(SPELLFAMILY_GENERIC, 92, 0)) ++ targets.push_back(master); ++ + if (!targets.empty()) + { -+ Unit* target = targets.size() == 1 ? *targets.begin() : Acore::Containers::SelectRandomContainerElement(targets); ++ Unit* target = targets.size() == 1 ? targets.front() : Acore::Containers::SelectRandomContainerElement(targets); + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(CREATE_SOULSTONE_1); + uint32 rank = spellInfo->GetRank(); + @@ -73504,10 +73538,10 @@ index 0000000000..96bb60f9db +#endif //BOTGOSSIP_H diff --git a/src/server/game/AI/NpcBots/botmgr.cpp b/src/server/game/AI/NpcBots/botmgr.cpp new file mode 100644 -index 0000000000..4152eb1f5d +index 0000000000..1f6fd64f57 --- /dev/null +++ b/src/server/game/AI/NpcBots/botmgr.cpp -@@ -0,0 +1,2826 @@ +@@ -0,0 +1,2831 @@ +#include "Battleground.h" +#include "BattlegroundMgr.h" +#include "bot_ai.h" @@ -75974,6 +76008,11 @@ index 0000000000..4152eb1f5d + } +} + ++void BotMgr::OnBotKilled(Creature const* bot, Unit* attacker/* = nullptr*/) ++{ ++ bot->GetBotAI()->OnDeath(attacker); ++} ++ +void BotMgr::OnBotSpellInterrupt(Unit const* caster, CurrentSpellTypes spellType) +{ + if (spellType == CURRENT_AUTOREPEAT_SPELL) @@ -76336,10 +76375,10 @@ index 0000000000..4152eb1f5d +#endif diff --git a/src/server/game/AI/NpcBots/botmgr.h b/src/server/game/AI/NpcBots/botmgr.h new file mode 100644 -index 0000000000..7d73939778 +index 0000000000..c46ec49537 --- /dev/null +++ b/src/server/game/AI/NpcBots/botmgr.h -@@ -0,0 +1,336 @@ +@@ -0,0 +1,337 @@ +#ifndef _BOTMGR_H +#define _BOTMGR_H + @@ -76500,6 +76539,7 @@ index 0000000000..7d73939778 + //onEvent hooks + static void OnBotWandererKilled(Creature const* bot, Player* looter); + static void OnBotWandererKilled(GameObject* go); ++ static void OnBotKilled(Creature const* bot, Unit* attacker = nullptr); + static void OnBotSpellInterrupt(Unit const* caster, CurrentSpellTypes spellType); + static void OnBotSpellGo(Unit const* caster, Spell const* spell, bool ok = true); + static void OnBotOwnerSpellGo(Unit const* caster, Spell const* spell, bool ok = true); @@ -90409,7 +90449,7 @@ index 3547b36cee..8e3c46a20c 100644 [[nodiscard]] int8 GetTransSeat() const { return m_movementInfo.transport.seat; } [[nodiscard]] virtual ObjectGuid GetTransGUID() const; diff --git a/src/server/game/Entities/Player/KillRewarder.cpp b/src/server/game/Entities/Player/KillRewarder.cpp -index 04ba9ca1d1..f7666b16c2 100644 +index 04ba9ca1d1..94c295e0dd 100644 --- a/src/server/game/Entities/Player/KillRewarder.cpp +++ b/src/server/game/Entities/Player/KillRewarder.cpp @@ -23,6 +23,10 @@ @@ -90434,12 +90474,22 @@ index 04ba9ca1d1..f7666b16c2 100644 // or if its owned by player and its not a vehicle else if (victim->GetCharmerOrOwnerGUID().IsPlayer()) _isPvP = !victim->IsVehicle(); -@@ -166,6 +174,17 @@ void KillRewarder::_RewardXP(Player* player, float rate) +@@ -166,6 +174,27 @@ void KillRewarder::_RewardXP(Player* player, float rate) for (Unit::AuraEffectList::const_iterator i = auras.begin(); i != auras.end(); ++i) AddPct(xp, (*i)->GetAmount()); + //npcbot 4.2.2.1. Apply NpcBot XP reduction -+ uint8 bots_count = player->GetNpcBotsCount(); ++ uint8 bots_count = 0; ++ if (_group) ++ { ++ for (GroupReference const* itr = _group->GetFirstMember(); itr != nullptr; itr = itr->next()) ++ { ++ if (Player const* gPlayer = itr->GetSource()) ++ bots_count = std::max(bots_count, gPlayer->GetNpcBotsCount()); ++ } ++ } ++ else ++ bots_count = player->GetNpcBotsCount(); + uint8 xp_reduction = BotMgr::GetNpcBotXpReduction(); + uint8 xp_reduction_start = BotMgr::GetNpcBotXpReductionStartingNumber(); + if (xp_reduction_start > 0 && xp_reduction > 0 && bots_count >= xp_reduction_start) @@ -90994,7 +91044,7 @@ index f2191b39ef..f1cce90a48 100644 float totalPct = addTotalPct ? GetModifierValue(unitMod, TOTAL_PCT) : 1.0f; float dmgMultiplier = GetCreatureTemplate()->DamageModifier; // = DamageModifier * _GetDamageMod(rank); diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp -index f7576fbdf5..05b67f50f1 100644 +index f7576fbdf5..1e25643d4f 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -72,6 +72,11 @@ @@ -92925,7 +92975,19 @@ index f7576fbdf5..05b67f50f1 100644 if (group) { if (hasLooterGuid) -@@ -18158,6 +19295,12 @@ void Unit::Kill(Unit* killer, Unit* victim, bool durabilityLoss, WeaponAttackTyp +@@ -18071,6 +19208,11 @@ void Unit::Kill(Unit* killer, Unit* victim, bool durabilityLoss, WeaponAttackTyp + player->RewardPlayerAndGroupAtKill(victim, false); + } + ++ //npcbot: spawn wandering bot kill reward ++ if (creature && creature->IsNPCBot()) ++ BotMgr::OnBotKilled(creature, killer); ++ //end npcbot ++ + // Do KILL and KILLED procs. KILL proc is called only for the unit who landed the killing blow (and its owner - for pets and totems) regardless of who tapped the victim + if (killer && (killer->IsPet() || killer->IsTotem())) + if (Unit* owner = killer->GetOwner()) +@@ -18158,6 +19300,12 @@ void Unit::Kill(Unit* killer, Unit* victim, bool durabilityLoss, WeaponAttackTyp // at original death (not at SpiritOfRedemtionTalent timeout) plrVictim->SetPvPDeath(player != nullptr); @@ -92938,7 +93000,7 @@ index f7576fbdf5..05b67f50f1 100644 // only if not player and not controlled by player pet. And not at BG if ((durabilityLoss && !player && !plrVictim->InBattleground()) || (player && sWorld->getBoolConfig(CONFIG_DURABILITY_LOSS_IN_PVP))) { -@@ -18257,6 +19400,10 @@ void Unit::Kill(Unit* killer, Unit* victim, bool durabilityLoss, WeaponAttackTyp +@@ -18257,6 +19405,10 @@ void Unit::Kill(Unit* killer, Unit* victim, bool durabilityLoss, WeaponAttackTyp { if (victim->GetTypeId() == TYPEID_PLAYER) bg->HandleKillPlayer(victim->ToPlayer(), player); @@ -92949,7 +93011,7 @@ index f7576fbdf5..05b67f50f1 100644 else bg->HandleKillUnit(victim->ToCreature(), player); } -@@ -18284,6 +19431,14 @@ void Unit::Kill(Unit* killer, Unit* victim, bool durabilityLoss, WeaponAttackTyp +@@ -18284,6 +19436,14 @@ void Unit::Kill(Unit* killer, Unit* victim, bool durabilityLoss, WeaponAttackTyp { if (Player* killed = victim->ToPlayer()) sScriptMgr->OnPlayerKilledByCreature(killerCre, killed); @@ -92964,7 +93026,7 @@ index f7576fbdf5..05b67f50f1 100644 } } -@@ -19049,6 +20204,14 @@ bool Unit::IsInPartyWith(Unit const* unit) const +@@ -19049,6 +20209,14 @@ bool Unit::IsInPartyWith(Unit const* unit) const else if ((u2->GetTypeId() == TYPEID_PLAYER && u1->GetTypeId() == TYPEID_UNIT && (u1->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT) && u2->ToPlayer()->GetGroup() && !u2->ToPlayer()->GetGroup()->isRaidGroup()) || (u1->GetTypeId() == TYPEID_PLAYER && u2->GetTypeId() == TYPEID_UNIT && (u2->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT) && u1->ToPlayer()->GetGroup() && !u1->ToPlayer()->GetGroup()->isRaidGroup())) return true; @@ -92979,7 +93041,7 @@ index f7576fbdf5..05b67f50f1 100644 else return false; } -@@ -19071,6 +20234,14 @@ bool Unit::IsInRaidWith(Unit const* unit) const +@@ -19071,6 +20239,14 @@ bool Unit::IsInRaidWith(Unit const* unit) const else if ((u2->GetTypeId() == TYPEID_PLAYER && u1->GetTypeId() == TYPEID_UNIT && u1->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT) || (u1->GetTypeId() == TYPEID_PLAYER && u2->GetTypeId() == TYPEID_UNIT && u2->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT)) return true; @@ -92994,7 +93056,7 @@ index f7576fbdf5..05b67f50f1 100644 else return false; } -@@ -19082,9 +20253,21 @@ void Unit::GetPartyMembers(std::list& TagUnitMap) +@@ -19082,9 +20258,21 @@ void Unit::GetPartyMembers(std::list& TagUnitMap) if (owner->GetTypeId() == TYPEID_PLAYER) group = owner->ToPlayer()->GetGroup(); @@ -93016,7 +93078,7 @@ index f7576fbdf5..05b67f50f1 100644 for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) { -@@ -19104,6 +20287,14 @@ void Unit::GetPartyMembers(std::list& TagUnitMap) +@@ -19104,6 +20292,14 @@ void Unit::GetPartyMembers(std::list& TagUnitMap) } } } @@ -93031,7 +93093,7 @@ index f7576fbdf5..05b67f50f1 100644 } else { -@@ -19116,6 +20307,18 @@ void Unit::GetPartyMembers(std::list& TagUnitMap) +@@ -19116,6 +20312,18 @@ void Unit::GetPartyMembers(std::list& TagUnitMap) if (pet->IsGuardian() && pet->IsAlive()) TagUnitMap.push_back(pet); } @@ -93050,7 +93112,7 @@ index f7576fbdf5..05b67f50f1 100644 } } -@@ -19128,6 +20331,10 @@ Aura* Unit::AddAura(uint32 spellId, Unit* target) +@@ -19128,6 +20336,10 @@ Aura* Unit::AddAura(uint32 spellId, Unit* target) if (!spellInfo) return nullptr; @@ -93061,7 +93123,7 @@ index f7576fbdf5..05b67f50f1 100644 if (!target->IsAlive() && !spellInfo->HasAttribute(SPELL_ATTR0_PASSIVE) && !spellInfo->HasAttribute(SPELL_ATTR2_ALLOW_DEAD_TARGET)) return nullptr; -@@ -19183,6 +20390,13 @@ void Unit::SendPlaySpellImpact(ObjectGuid guid, uint32 id) +@@ -19183,6 +20395,13 @@ void Unit::SendPlaySpellImpact(ObjectGuid guid, uint32 id) SendMessageToSet(&data, true); } @@ -93075,7 +93137,7 @@ index f7576fbdf5..05b67f50f1 100644 void Unit::ApplyResilience(Unit const* victim, float* crit, int32* damage, bool isCrit, CombatRating type) { // player mounted on multi-passenger mount is also classified as vehicle -@@ -19247,6 +20461,11 @@ void Unit::ApplyResilience(Unit const* victim, float* crit, int32* damage, bool +@@ -19247,6 +20466,11 @@ void Unit::ApplyResilience(Unit const* victim, float* crit, int32* damage, bool float Unit::MeleeSpellMissChance(Unit const* victim, WeaponAttackType attType, int32 skillDiff, uint32 spellId) const { SpellInfo const* spellInfo = spellId ? sSpellMgr->GetSpellInfo(spellId) : nullptr; @@ -93087,7 +93149,7 @@ index f7576fbdf5..05b67f50f1 100644 if (spellInfo && spellInfo->HasAttribute(SPELL_ATTR7_NO_ATTACK_MISS)) { return 0.0f; -@@ -19255,6 +20474,16 @@ float Unit::MeleeSpellMissChance(Unit const* victim, WeaponAttackType attType, i +@@ -19255,6 +20479,16 @@ float Unit::MeleeSpellMissChance(Unit const* victim, WeaponAttackType attType, i //calculate miss chance float missChance = victim->GetUnitMissChance(attType); @@ -93104,7 +93166,7 @@ index f7576fbdf5..05b67f50f1 100644 // Check if dual wielding, add additional miss penalty - when mainhand has on next swing spell, offhand doesnt suffer penalty if (!spellId && (attType != RANGED_ATTACK) && haveOffhandWeapon() && (!m_currentSpells[CURRENT_MELEE_SPELL] || !m_currentSpells[CURRENT_MELEE_SPELL]->IsNextMeleeSwingSpell())) { -@@ -19369,6 +20598,11 @@ void Unit::SetPhaseMask(uint32 newPhaseMask, bool update) +@@ -19369,6 +20603,11 @@ void Unit::SetPhaseMask(uint32 newPhaseMask, bool update) } } @@ -93116,7 +93178,7 @@ index f7576fbdf5..05b67f50f1 100644 for (uint8 i = 0; i < MAX_SUMMON_SLOT; ++i) { if (m_SummonSlot[i]) -@@ -19442,6 +20676,10 @@ float Unit::GetCombatRatingReduction(CombatRating cr) const +@@ -19442,6 +20681,10 @@ float Unit::GetCombatRatingReduction(CombatRating cr) const { if (Player const* player = ToPlayer()) return player->GetRatingBonusValue(cr); @@ -93127,7 +93189,7 @@ index f7576fbdf5..05b67f50f1 100644 // Player's pet get resilience from owner else if (IsPet() && GetOwner()) if (Player* owner = GetOwner()->ToPlayer()) -@@ -19643,6 +20881,183 @@ uint32 Unit::GetModelForForm(ShapeshiftForm form, uint32 spellId) const +@@ -19643,6 +20886,183 @@ uint32 Unit::GetModelForForm(ShapeshiftForm form, uint32 spellId) const break; } } @@ -93311,7 +93373,7 @@ index f7576fbdf5..05b67f50f1 100644 uint32 modelid = 0; SpellShapeshiftEntry const* formEntry = sSpellShapeshiftStore.LookupEntry(form); -@@ -20234,6 +21649,10 @@ bool Unit::CanSwim() const +@@ -20234,6 +21654,10 @@ bool Unit::CanSwim() const return false; if (HasUnitFlag(UNIT_FLAG_PET_IN_COMBAT)) return true; @@ -93322,7 +93384,7 @@ index f7576fbdf5..05b67f50f1 100644 return HasUnitFlag(UNIT_FLAG_RENAME | UNIT_FLAG_SWIMMING); } -@@ -20317,6 +21736,11 @@ bool Unit::UpdatePosition(float x, float y, float z, float orientation, bool tel +@@ -20317,6 +21741,11 @@ bool Unit::UpdatePosition(float x, float y, float z, float orientation, bool tel UpdateObjectVisibility(false); } @@ -93334,7 +93396,7 @@ index f7576fbdf5..05b67f50f1 100644 return (relocated || turn); } -@@ -20457,6 +21881,19 @@ void Unit::StopAttackFaction(uint32 faction_id) +@@ -20457,6 +21886,19 @@ void Unit::StopAttackFaction(uint32 faction_id) for (ControlSet::const_iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr) (*itr)->StopAttackFaction(faction_id); @@ -93354,7 +93416,7 @@ index f7576fbdf5..05b67f50f1 100644 } void Unit::StopAttackingInvalidTarget() -@@ -21060,6 +22497,10 @@ void Unit::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* target) +@@ -21060,6 +22502,10 @@ void Unit::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* target) if (plr && plr->IsInSameRaidWith(target)) visibleFlag |= UF_FLAG_PARTY_MEMBER; @@ -93365,7 +93427,7 @@ index f7576fbdf5..05b67f50f1 100644 Creature const* creature = ToCreature(); for (uint16 index = 0; index < m_valuesCount; ++index) -@@ -21219,6 +22660,24 @@ void Unit::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* target) +@@ -21219,6 +22665,24 @@ void Unit::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* target) else fieldBuffer << (uint32)target->GetFaction(); } @@ -93390,7 +93452,7 @@ index f7576fbdf5..05b67f50f1 100644 else if (!sScriptMgr->IsCustomBuildValuesUpdate(this, updateType, fieldBuffer, target, index)) { -@@ -21558,6 +23017,52 @@ bool Unit::IsInDisallowedMountForm() const +@@ -21558,6 +23022,52 @@ bool Unit::IsInDisallowedMountForm() const return false; } diff --git a/NPCBots.patch b/NPCBots.patch index 222aeaa..939401b 100644 --- a/NPCBots.patch +++ b/NPCBots.patch @@ -118,8 +118,8 @@ src/server/game/AI/CoreAI/UnitAI.cpp | 3 + src/server/game/AI/NpcBots/bot_Events.h | 74 + src/server/game/AI/NpcBots/bot_GridNotifiers.h | 1408 ++ - src/server/game/AI/NpcBots/bot_ai.cpp | 19694 +++++++++++++++++++ - src/server/game/AI/NpcBots/bot_ai.h | 803 + + src/server/game/AI/NpcBots/bot_ai.cpp | 19725 +++++++++++++++++++ + src/server/game/AI/NpcBots/bot_ai.h | 805 + src/server/game/AI/NpcBots/bot_archmage_ai.cpp | 398 + src/server/game/AI/NpcBots/bot_bm_ai.cpp | 941 + src/server/game/AI/NpcBots/bot_crypt_lord_ai.cpp | 845 + @@ -137,7 +137,7 @@ src/server/game/AI/NpcBots/bot_shaman_ai.cpp | 2836 +++ src/server/game/AI/NpcBots/bot_spellbreaker_ai.cpp | 612 + src/server/game/AI/NpcBots/bot_sphynx_ai.cpp | 569 + - src/server/game/AI/NpcBots/bot_warlock_ai.cpp | 2100 ++ + src/server/game/AI/NpcBots/bot_warlock_ai.cpp | 2101 ++ src/server/game/AI/NpcBots/bot_warrior_ai.cpp | 2194 +++ src/server/game/AI/NpcBots/botcommands.cpp | 4175 ++++ src/server/game/AI/NpcBots/botcommon.h | 582 + @@ -151,8 +151,8 @@ src/server/game/AI/NpcBots/botgearscore.h | 16 + src/server/game/AI/NpcBots/botgiver.cpp | 286 + src/server/game/AI/NpcBots/botgossip.h | 141 + - src/server/game/AI/NpcBots/botmgr.cpp | 2825 +++ - src/server/game/AI/NpcBots/botmgr.h | 335 + + src/server/game/AI/NpcBots/botmgr.cpp | 2830 +++ + src/server/game/AI/NpcBots/botmgr.h | 336 + src/server/game/AI/NpcBots/botspell.cpp | 2086 ++ src/server/game/AI/NpcBots/botspell.h | 303 + src/server/game/AI/NpcBots/bottext.h | 414 + @@ -201,12 +201,12 @@ src/server/game/Entities/GameObject/GameObject.cpp | 79 + src/server/game/Entities/Object/Object.cpp | 228 +- src/server/game/Entities/Object/Object.h | 10 +- - src/server/game/Entities/Player/KillRewarder.cpp | 19 + + src/server/game/Entities/Player/KillRewarder.cpp | 29 + src/server/game/Entities/Player/Player.cpp | 182 + src/server/game/Entities/Player/Player.h | 24 + src/server/game/Entities/Totem/Totem.cpp | 25 + src/server/game/Entities/Unit/StatSystem.cpp | 46 +- - src/server/game/Entities/Unit/Unit.cpp | 906 + + src/server/game/Entities/Unit/Unit.cpp | 911 + src/server/game/Entities/Unit/Unit.h | 15 +- src/server/game/Entities/Vehicle/Vehicle.cpp | 42 + src/server/game/Globals/ObjectMgr.cpp | 97 + @@ -262,7 +262,7 @@ src/server/shared/DataStores/DBCfmt.h | 4 +- src/server/shared/SharedDefines.h | 7 + src/server/worldserver/worldserver.conf.dist | 510 + - 264 files changed, 92624 insertions(+), 77 deletions(-) + 264 files changed, 92679 insertions(+), 77 deletions(-) create mode 100644 sql/Bots/1_world_bot_appearance.sql create mode 100644 sql/Bots/2_world_bot_extras.sql create mode 100644 sql/Bots/3_world_bots.sql @@ -14281,10 +14281,10 @@ index 000000000..9db8c9da3 +#endif diff --git a/src/server/game/AI/NpcBots/bot_ai.cpp b/src/server/game/AI/NpcBots/bot_ai.cpp new file mode 100644 -index 000000000..acf1968b5 +index 000000000..9799da78a --- /dev/null +++ b/src/server/game/AI/NpcBots/bot_ai.cpp -@@ -0,0 +1,19694 @@ +@@ -0,0 +1,19725 @@ +#include "Battleground.h" +#include "BattlegroundAB.h" +#include "BattlegroundAV.h" @@ -14551,6 +14551,8 @@ index 000000000..acf1968b5 + _lastAreaId = 0; + _lastWMOAreaId = 0; + ++ _selfrez_spell_id = 0; ++ + _wmoAreaUpdateTimer = 0; + + _contestedPvPTimer = 0; @@ -29711,6 +29713,29 @@ index 000000000..acf1968b5 + + ++_deathsCount; +} ++//This is triggered before SetDeathState(JUST_DIED) call ++//attacker may be NULL ++void bot_ai::OnDeath([[maybe_unused]] Unit* attacker/* = nullptr*/) ++{ ++ if (AuraEffect const* sstone = me->GetDummyAuraEffect(SPELLFAMILY_GENERIC, 92, 0)) ++ { ++ uint32 spell_id; ++ switch (sstone->GetBase()->GetId()) ++ { ++ case 20707: spell_id = 3026; break; // rank 1 ++ case 20762: spell_id = 20758; break; // rank 2 ++ case 20763: spell_id = 20759; break; // rank 3 ++ case 20764: spell_id = 20760; break; // rank 4 ++ case 20765: spell_id = 20761; break; // rank 5 ++ case 27239: spell_id = 27240; break; // rank 6 ++ case 47883: spell_id = 47882; break; // rank 7 ++ default: spell_id = 0; break; ++ } ++ _selfrez_spell_id = spell_id; ++ } ++ else ++ _selfrez_spell_id = 0; ++} + +void bot_ai::KilledUnit(Unit* u) +{ @@ -29839,6 +29864,8 @@ index 000000000..acf1968b5 + StartPotionTimer(); + if (curInfo->Id == ACTIVATE_SPEC) + SetSpec(_newspec); ++ if (curInfo->Id == _selfrez_spell_id) ++ OnSpellHit(me, curInfo); + + OnClassSpellGo(curInfo); + } @@ -31146,6 +31173,10 @@ index 000000000..acf1968b5 + // group update + if (_groupUpdateTimer <= diff) + SendUpdateToOutOfRangeBotGroupMembers(); ++ ++ // soulstone ++ if (_selfrez_spell_id && (IAmFree() || !master->GetBotMgr()->IsPartyInCombat()) && Rand() < 15) ++ me->CastSpell(me, _selfrez_spell_id); +} +//opponent unsafe +bool bot_ai::GlobalUpdate(uint32 diff) @@ -33981,10 +34012,10 @@ index 000000000..acf1968b5 +#endif diff --git a/src/server/game/AI/NpcBots/bot_ai.h b/src/server/game/AI/NpcBots/bot_ai.h new file mode 100644 -index 000000000..d70bac7fd +index 000000000..7cf8de459 --- /dev/null +++ b/src/server/game/AI/NpcBots/bot_ai.h -@@ -0,0 +1,803 @@ +@@ -0,0 +1,805 @@ +#ifndef _BOT_AI_H +#define _BOT_AI_H + @@ -34051,6 +34082,7 @@ index 000000000..d70bac7fd + void EnterEvadeMode(EvadeReason/* why*/ = EVADE_REASON_OTHER) override { } + //void LeavingWorld() override { } + void OnSpellStart(SpellInfo const* spellInfo) override { OnBotSpellStart(spellInfo); } ++ void OnDeath(Unit* attacker = nullptr); + //bool CanRespawn() override { return IAmFree(); } + + virtual void OnBotSummon(Creature* /*summon*/) {} @@ -34677,6 +34709,7 @@ index 000000000..d70bac7fd + uint32 _saveDisabledSpellsTimer; + + uint32 _lastZoneId, _lastAreaId, _lastWMOAreaId; ++ uint32 _selfrez_spell_id; + + uint8 _unreachableCount, _jumpCount, _evadeCount; + uint8 _healHpPctThreshold; @@ -59562,10 +59595,10 @@ index 000000000..8714ec1bb +} diff --git a/src/server/game/AI/NpcBots/bot_warlock_ai.cpp b/src/server/game/AI/NpcBots/bot_warlock_ai.cpp new file mode 100644 -index 000000000..6a7ae4727 +index 000000000..9aee822ff --- /dev/null +++ b/src/server/game/AI/NpcBots/bot_warlock_ai.cpp -@@ -0,0 +1,2100 @@ +@@ -0,0 +1,2101 @@ +#include "bot_ai.h" +#include "botmgr.h" +#include "botspell.h" @@ -59846,7 +59879,7 @@ index 000000000..6a7ae4727 + return; + } + -+ if (!hasSoulstone && !IAmFree() && GetSpell(CREATE_SOULSTONE_1)) ++ if (!hasSoulstone && GetSpell(CREATE_SOULSTONE_1)) + { + if (doCast(me, GetSpell(CREATE_SOULSTONE_1))) + return; @@ -59861,40 +59894,41 @@ index 000000000..6a7ae4727 + + //TODO: soulstone on self/bots + //BUG: players cannot accept this buff if they are below lvl 20 (should be 8) -+ if (!IAmFree() && hasSoulstone && soulstoneTimer <= diff && GetSpell(CREATE_SOULSTONE_1)) ++ if (hasSoulstone && soulstoneTimer <= diff && GetSpell(CREATE_SOULSTONE_1)) + { -+ Group const* gr = master->GetGroup(); -+ std::set targets; -+ if (!gr) -+ { -+ if (master->IsAlive() && !master->isPossessed() && !master->IsCharmed() && -+ me->GetDistance(master) < 30 && !master->GetDummyAuraEffect(SPELLFAMILY_GENERIC, 92, 0)) -+ targets.insert(master); -+ } -+ else ++ std::vector targets; ++ ++ if (!IAmFree()) + { -+ for (uint8 i = 0; i < 2 && !targets.empty(); ++i) ++ std::vector all_members = BotMgr::GetAllGroupMembers(master->GetGroup()); ++ for (uint8 i = 0; i < 3; ++i) + { -+ for (Unit* member : BotMgr::GetAllGroupMembers(gr)) ++ if (i > 0 && !targets.empty()) ++ break; ++ for (Unit* member : all_members) + { -+ if ((i == 0 ? member->IsPlayer() : member->IsNPCBot()) && me->GetMap() == member->FindMap() && ++ if ((i >= 2 || (i == 0 ? member->IsPlayer() : member->IsNPCBot())) && me->GetMap() == member->FindMap() && + member->IsAlive() && !member->isPossessed() && !member->IsCharmed() && + !(member->IsNPCBot() && member->ToCreature()->IsTempBot()) && + me->GetDistance(member) < 30 && !member->GetDummyAuraEffect(SPELLFAMILY_GENERIC, 92, 0)) + { -+ if (i > 0 || member->GetClass() == CLASS_PRIEST || member->GetClass() == CLASS_PALADIN || ++ if (i >= 2 || member->GetClass() == CLASS_PRIEST || member->GetClass() == CLASS_PALADIN || + member->GetClass() == CLASS_DRUID || member->GetClass() == CLASS_SHAMAN) + { -+ targets.insert(member); ++ targets.push_back(member); + } + } + } + } + } + ++ if (targets.empty() && master->IsAlive() && !master->isPossessed() && !master->IsCharmed() && ++ me->GetDistance(master) < 30 && !master->GetDummyAuraEffect(SPELLFAMILY_GENERIC, 92, 0)) ++ targets.push_back(master); ++ + if (!targets.empty()) + { -+ Unit* target = targets.size() == 1 ? *targets.begin() : Trinity::Containers::SelectRandomContainerElement(targets); ++ Unit* target = targets.size() == 1 ? targets.front() : Trinity::Containers::SelectRandomContainerElement(targets); + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(CREATE_SOULSTONE_1); + uint32 rank = spellInfo->GetRank(); + @@ -73822,10 +73856,10 @@ index 000000000..96bb60f9d +#endif //BOTGOSSIP_H diff --git a/src/server/game/AI/NpcBots/botmgr.cpp b/src/server/game/AI/NpcBots/botmgr.cpp new file mode 100644 -index 000000000..c832aaac9 +index 000000000..5b0e4d746 --- /dev/null +++ b/src/server/game/AI/NpcBots/botmgr.cpp -@@ -0,0 +1,2825 @@ +@@ -0,0 +1,2830 @@ +#include "Battleground.h" +#include "BattlegroundMgr.h" +#include "bot_ai.h" @@ -76291,6 +76325,11 @@ index 000000000..c832aaac9 + } +} + ++void BotMgr::OnBotKilled(Creature const* bot, Unit* attacker/* = nullptr*/) ++{ ++ bot->GetBotAI()->OnDeath(attacker); ++} ++ +void BotMgr::OnBotSpellInterrupt(Unit const* caster, CurrentSpellTypes spellType) +{ + if (spellType == CURRENT_AUTOREPEAT_SPELL) @@ -76653,10 +76692,10 @@ index 000000000..c832aaac9 +#endif diff --git a/src/server/game/AI/NpcBots/botmgr.h b/src/server/game/AI/NpcBots/botmgr.h new file mode 100644 -index 000000000..4201854e6 +index 000000000..1c1a347b3 --- /dev/null +++ b/src/server/game/AI/NpcBots/botmgr.h -@@ -0,0 +1,335 @@ +@@ -0,0 +1,336 @@ +#ifndef _BOTMGR_H +#define _BOTMGR_H + @@ -76816,6 +76855,7 @@ index 000000000..4201854e6 + //onEvent hooks + static void OnBotWandererKilled(Creature const* bot, Player* looter); + static void OnBotWandererKilled(GameObject* go); ++ static void OnBotKilled(Creature const* bot, Unit* attacker = nullptr); + static void OnBotSpellInterrupt(Unit const* caster, CurrentSpellTypes spellType); + static void OnBotSpellGo(Unit const* caster, Spell const* spell, bool ok = true); + static void OnBotOwnerSpellGo(Unit const* caster, Spell const* spell, bool ok = true); @@ -91526,7 +91566,7 @@ index 3a72759bb..f9194ee41 100644 void GetContactPoint(WorldObject const* obj, float& x, float& y, float& z, float distance2d = CONTACT_DISTANCE) const; diff --git a/src/server/game/Entities/Player/KillRewarder.cpp b/src/server/game/Entities/Player/KillRewarder.cpp -index 65b9fffc0..477d6ebed 100644 +index 65b9fffc0..3450733a2 100644 --- a/src/server/game/Entities/Player/KillRewarder.cpp +++ b/src/server/game/Entities/Player/KillRewarder.cpp @@ -26,6 +26,10 @@ @@ -91551,12 +91591,22 @@ index 65b9fffc0..477d6ebed 100644 // or if its owned by player and its not a vehicle else if (victim->GetCharmerOrOwnerGUID().IsPlayer()) _isPvP = !victim->IsVehicle(); -@@ -153,6 +161,17 @@ inline void KillRewarder::_RewardXP(Player* player, float rate) +@@ -153,6 +161,27 @@ inline void KillRewarder::_RewardXP(Player* player, float rate) // 4.2.2. Apply auras modifying rewarded XP (SPELL_AURA_MOD_XP_PCT). xp *= player->GetTotalAuraMultiplier(SPELL_AURA_MOD_XP_PCT); + //npcbot 4.2.2.1. Apply NpcBot XP reduction -+ uint8 bots_count = player->GetNpcBotsCount(); ++ uint8 bots_count = 0; ++ if (_group) ++ { ++ for (GroupReference const* itr = _group->GetFirstMember(); itr != nullptr; itr = itr->next()) ++ { ++ if (Player const* gPlayer = itr->GetSource()) ++ bots_count = std::max(bots_count, gPlayer->GetNpcBotsCount()); ++ } ++ } ++ else ++ bots_count = player->GetNpcBotsCount(); + uint8 xp_reduction = BotMgr::GetNpcBotXpReduction(); + uint8 xp_reduction_start = BotMgr::GetNpcBotXpReductionStartingNumber(); + if (xp_reduction_start > 0 && xp_reduction > 0 && bots_count >= xp_reduction_start) @@ -92039,7 +92089,7 @@ index cb467b8de..23d9eac31 100644 float totalPct = addTotalPct ? GetPctModifierValue(unitMod, TOTAL_PCT) : 1.0f; float dmgMultiplier = GetCreatureTemplate()->ModDamage; // = ModDamage * _GetDamageMod(rank); diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp -index d31025c40..61104ef47 100644 +index d31025c40..969e88893 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -81,6 +81,10 @@ @@ -93096,7 +93146,19 @@ index d31025c40..61104ef47 100644 if (group) { if (hasLooterGuid) -@@ -11096,6 +11682,12 @@ bool Unit::InitTamedPet(Pet* pet, uint8 level, uint32 spell_id) +@@ -11029,6 +11615,11 @@ bool Unit::InitTamedPet(Pet* pet, uint8 level, uint32 spell_id) + player->RewardPlayerAndGroupAtKill(victim, false); + } + ++ //npcbot: spawn wandering bot kill reward ++ if (creature && creature->IsNPCBot()) ++ BotMgr::OnBotKilled(creature, attacker); ++ //end npcbot ++ + // Do KILL and KILLED procs. KILL proc is called only for the unit who landed the killing blow (and its owner - for pets and totems) regardless of who tapped the victim + if (attacker && (attacker->IsPet() || attacker->IsTotem())) + { +@@ -11096,6 +11687,12 @@ bool Unit::InitTamedPet(Pet* pet, uint8 level, uint32 spell_id) // at original death (not at SpiritOfRedemtionTalent timeout) plrVictim->SetPvPDeath(player != nullptr); @@ -93109,7 +93171,7 @@ index d31025c40..61104ef47 100644 // only if not player and not controlled by player pet. And not at BG if ((durabilityLoss && !player && !victim->ToPlayer()->InBattleground()) || (player && sWorld->getBoolConfig(CONFIG_DURABILITY_LOSS_IN_PVP))) { -@@ -11197,6 +11789,10 @@ bool Unit::InitTamedPet(Pet* pet, uint8 level, uint32 spell_id) +@@ -11197,6 +11794,10 @@ bool Unit::InitTamedPet(Pet* pet, uint8 level, uint32 spell_id) { if (Player* playerVictim = victim->ToPlayer()) bg->HandleKillPlayer(playerVictim, player); @@ -93120,7 +93182,7 @@ index d31025c40..61104ef47 100644 else bg->HandleKillUnit(victim->ToCreature(), player); } -@@ -11225,6 +11821,14 @@ bool Unit::InitTamedPet(Pet* pet, uint8 level, uint32 spell_id) +@@ -11225,6 +11826,14 @@ bool Unit::InitTamedPet(Pet* pet, uint8 level, uint32 spell_id) { if (Player* killed = victim->ToPlayer()) sScriptMgr->OnPlayerKilledByCreature(killerCre, killed); @@ -93135,7 +93197,7 @@ index d31025c40..61104ef47 100644 } } } -@@ -11898,6 +12502,14 @@ bool Unit::IsInPartyWith(Unit const* unit) const +@@ -11898,6 +12507,14 @@ bool Unit::IsInPartyWith(Unit const* unit) const else if ((u2->GetTypeId() == TYPEID_PLAYER && u1->GetTypeId() == TYPEID_UNIT && u1->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT) || (u1->GetTypeId() == TYPEID_PLAYER && u2->GetTypeId() == TYPEID_UNIT && u2->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT)) return true; @@ -93150,7 +93212,7 @@ index d31025c40..61104ef47 100644 return u1->GetTypeId() == TYPEID_UNIT && u2->GetTypeId() == TYPEID_UNIT && u1->GetFaction() == u2->GetFaction(); } -@@ -11917,6 +12529,14 @@ bool Unit::IsInRaidWith(Unit const* unit) const +@@ -11917,6 +12534,14 @@ bool Unit::IsInRaidWith(Unit const* unit) const else if ((u2->GetTypeId() == TYPEID_PLAYER && u1->GetTypeId() == TYPEID_UNIT && u1->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT) || (u1->GetTypeId() == TYPEID_PLAYER && u2->GetTypeId() == TYPEID_UNIT && u2->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT)) return true; @@ -93165,7 +93227,7 @@ index d31025c40..61104ef47 100644 return u1->GetTypeId() == TYPEID_UNIT && u2->GetTypeId() == TYPEID_UNIT && u1->GetFaction() == u2->GetFaction(); } -@@ -11928,9 +12548,21 @@ void Unit::GetPartyMembers(std::list &TagUnitMap) +@@ -11928,9 +12553,21 @@ void Unit::GetPartyMembers(std::list &TagUnitMap) if (owner->GetTypeId() == TYPEID_PLAYER) group = owner->ToPlayer()->GetGroup(); @@ -93187,7 +93249,7 @@ index d31025c40..61104ef47 100644 for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) { -@@ -11947,6 +12579,14 @@ void Unit::GetPartyMembers(std::list &TagUnitMap) +@@ -11947,6 +12584,14 @@ void Unit::GetPartyMembers(std::list &TagUnitMap) TagUnitMap.push_back(pet); } } @@ -93202,7 +93264,7 @@ index d31025c40..61104ef47 100644 } else { -@@ -11955,6 +12595,18 @@ void Unit::GetPartyMembers(std::list &TagUnitMap) +@@ -11955,6 +12600,18 @@ void Unit::GetPartyMembers(std::list &TagUnitMap) if (Guardian* pet = owner->GetGuardianPet()) if ((pet == this || IsInMap(pet)) && pet->IsAlive()) TagUnitMap.push_back(pet); @@ -93221,7 +93283,7 @@ index d31025c40..61104ef47 100644 } } -@@ -11983,6 +12635,10 @@ Aura* Unit::AddAura(uint32 spellId, Unit* target) +@@ -11983,6 +12640,10 @@ Aura* Unit::AddAura(uint32 spellId, Unit* target) if (!spellInfo) return nullptr; @@ -93232,7 +93294,7 @@ index d31025c40..61104ef47 100644 return AddAura(spellInfo, MAX_EFFECT_MASK, target); } -@@ -12047,6 +12703,11 @@ void Unit::SendPlaySpellImpact(ObjectGuid guid, uint32 id) const +@@ -12047,6 +12708,11 @@ void Unit::SendPlaySpellImpact(ObjectGuid guid, uint32 id) const bool Unit::CanApplyResilience() const { @@ -93244,7 +93306,7 @@ index d31025c40..61104ef47 100644 return !IsVehicle() && GetOwnerGUID().IsPlayer(); } -@@ -12123,12 +12784,27 @@ int32 Unit::CalculateAOEAvoidance(int32 damage, uint32 schoolMask, ObjectGuid co +@@ -12123,12 +12789,27 @@ int32 Unit::CalculateAOEAvoidance(int32 damage, uint32 schoolMask, ObjectGuid co float Unit::MeleeSpellMissChance(Unit const* victim, WeaponAttackType attType, int32 skillDiff, uint32 spellId) const { SpellInfo const* spellInfo = spellId ? sSpellMgr->GetSpellInfo(spellId) : nullptr; @@ -93272,7 +93334,7 @@ index d31025c40..61104ef47 100644 // melee attacks while dual wielding have +19% chance to miss if (!spellId && haveOffhandWeapon()) missChance += 19.0f; -@@ -12185,6 +12861,11 @@ void Unit::SetPhaseMask(uint32 newPhaseMask, bool update) +@@ -12185,6 +12866,11 @@ void Unit::SetPhaseMask(uint32 newPhaseMask, bool update) if ((*itr)->GetTypeId() == TYPEID_UNIT) (*itr)->SetPhaseMask(newPhaseMask, true); @@ -93284,7 +93346,7 @@ index d31025c40..61104ef47 100644 for (uint8 i = 0; i < MAX_SUMMON_SLOT; ++i) if (m_SummonSlot[i]) if (Creature* summon = GetMap()->GetCreature(m_SummonSlot[i])) -@@ -12240,6 +12921,10 @@ float Unit::GetCombatRatingReduction(CombatRating cr) const +@@ -12240,6 +12926,10 @@ float Unit::GetCombatRatingReduction(CombatRating cr) const { if (Player const* player = ToPlayer()) return player->GetRatingBonusValue(cr); @@ -93295,7 +93357,7 @@ index d31025c40..61104ef47 100644 // Player's pet get resilience from owner else if (IsPet() && GetOwner()) if (Player* owner = GetOwner()->ToPlayer()) -@@ -12439,6 +13124,183 @@ uint32 Unit::GetModelForForm(ShapeshiftForm form, uint32 spellId) const +@@ -12439,6 +13129,183 @@ uint32 Unit::GetModelForForm(ShapeshiftForm form, uint32 spellId) const break; } } @@ -93479,7 +93541,7 @@ index d31025c40..61104ef47 100644 uint32 modelid = 0; SpellShapeshiftFormEntry const* formEntry = sSpellShapeshiftFormStore.LookupEntry(form); -@@ -12846,6 +13708,10 @@ bool Unit::CanSwim() const +@@ -12846,6 +13713,10 @@ bool Unit::CanSwim() const return false; if (HasUnitFlag(UNIT_FLAG_PET_IN_COMBAT)) return true; @@ -93490,7 +93552,7 @@ index d31025c40..61104ef47 100644 return HasUnitFlag(UNIT_FLAG_RENAME | UNIT_FLAG_CAN_SWIM); } -@@ -12939,6 +13805,11 @@ bool Unit::UpdatePosition(float x, float y, float z, float orientation, bool tel +@@ -12939,6 +13810,11 @@ bool Unit::UpdatePosition(float x, float y, float z, float orientation, bool tel UpdatePositionData(); @@ -93502,7 +93564,7 @@ index d31025c40..61104ef47 100644 _positionUpdateInfo.Relocated = relocated; _positionUpdateInfo.Turned = turn; -@@ -13129,6 +14000,19 @@ void Unit::StopAttackFaction(uint32 faction_id) +@@ -13129,6 +14005,19 @@ void Unit::StopAttackFaction(uint32 faction_id) for (Unit* minion : m_Controlled) minion->StopAttackFaction(faction_id); @@ -93522,7 +93584,7 @@ index d31025c40..61104ef47 100644 } void Unit::OutDebugInfo() const -@@ -13436,6 +14320,10 @@ void Unit::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player const* t +@@ -13436,6 +14325,10 @@ void Unit::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player const* t if (plr && plr->IsInSameRaidWith(target)) visibleFlag |= UF_FLAG_PARTY_MEMBER; @@ -93533,7 +93595,7 @@ index d31025c40..61104ef47 100644 Creature const* creature = ToCreature(); for (uint16 index = 0; index < m_valuesCount; ++index) -@@ -13560,6 +14448,24 @@ void Unit::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player const* t +@@ -13560,6 +14453,24 @@ void Unit::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player const* t else fieldBuffer << m_uint32Values[index]; } diff --git a/README.md b/README.md index 294db96..2457a18 100644 --- a/README.md +++ b/README.md @@ -732,7 +732,7 @@ If some config settings look ambiguous this section may be of help to you - Not saved between log-ins Explanation. Bots group around you in a formation where tanks are in front, melee are on the sides and ranged are in the back. The distance they keep from you is not changed if value of this parameter is 30 or less. Past 30 it is increased linearly up to additional 10 yards between you and a your bots. The distance at which bots start attacking incoming enemies is determined by this parameter as well. This is distance between *player* (or *bot*, if stationed) and the enemy and is about 75% of this paramenter's value. If bot's attack target moves outside of this range bot stops attacking it (unless you attack this target as well) and retreats. **This means** that if this parameter is set low bot actions and chase movement may become erratic. If this parameter is set to **0** bots will act passively unless you point an attack targets (with your melee attack, only works in combat); this may be useful in case you want bots to attack and retreat, or in situation where blind attack is dangerous and you need bots to attack only targets you want them to. **Auto-attack spells like Autoshot or Shoot Wand cancel your melee attack** - **`NpcBot.XpReduction`** - - This parameter allows you to set XP gain percent penalty for players using bots during leveling + - This parameter allows you to set XP gain percent penalty for players using bots during leveling. When there is more than one player in a group the maximum number of bots hired by any player in group is used for XP reduction amount calculation - Value range: **0-90** Explanation. XP amount is reduced by a percentage for every used bot after first one (it doesn't matter if bots are in group with player or not). Two bots are able to do much more damage than one player, especially at low levels. Bots also open great potential for grind. So you may want to punish your players a little. The formula is: **(100 - X \* (Y - 1))%** XP gained, where **X** is XP reduction and **Y** is bots count. *Example: XP reduction is 10, bots count = 4; XP gained: 100 - 10 * (4 - 1) = 70% XP gained*. In any case, overall XP reduction from this parameter will never exceed 90%. **This penalty only applies to bots' owner** - **`NpcBot.HealTargetIconsMask`**