Compare commits

..

3 Commits

Author SHA1 Message Date
bash
bcd6f5bc06
Removed bot freezing at startup and system message, not relevant anymore (#1519) 2025-08-10 19:11:39 +02:00
bash
15f138aab0
Don't apply XPRate multiplier when bot is in group with real player (#1495)
* dont apply XPRate if bot is in group with real player

https://github.com/liyunfan1223/mod-playerbots/issues/1490

* Optimize code

* Oops minor correction

* Defense check on the player itself

* Safer way to check the leader is real player.

* Added abit more defense programming, should be needed still ..why not
2025-08-10 18:28:39 +02:00
ThePenguinMan96
5759a98d5a
Warlock Soul Shard Cap Increase / Firestone and Spellstone Modes (#1514)
Hello everyone,

This is a small change to warlocks that accomplishes 2 things:

1. Changes the firestone and spellstone weapon enchants so only one of them can be active - players reported to me that both strategies could be present before, resulting in a bug where the bot repeatedly applied the enchant.
2. Changes the soul shard deletion cap from 6 or more to 26 or more - players will now be able to stockpile soul shards up to 25 in a bot's inventory before the bot starts deleting them one at a time back down 25. I chose 25 because if it was higher, drain soul would get multiple shards above the 32 unique cap, and spam the player "I can't carry any more of those". It was super annoying, and with testing, I have not seen this error at 25. This aims to address issue #1502 .
2025-08-10 11:13:01 +02:00
6 changed files with 43 additions and 41 deletions

View File

@ -4214,19 +4214,6 @@ bool PlayerbotAI::AllowActive(ActivityType activityType)
} }
} }
// only keep updating till initializing time has completed,
// which prevents unneeded expensive GameTime calls.
if (_isBotInitializing)
{
_isBotInitializing = GameTime::GetUptime().count() < sPlayerbotAIConfig->maxRandomBots * 0.11;
// no activity allowed during bot initialization
if (_isBotInitializing)
{
return false;
}
}
// General exceptions // General exceptions
if (activityType == PACKET_ACTIVITY) if (activityType == PACKET_ACTIVITY)
{ {

View File

@ -611,7 +611,6 @@ private:
Item* FindItemInInventory(std::function<bool(ItemTemplate const*)> checkItem) const; Item* FindItemInInventory(std::function<bool(ItemTemplate const*)> checkItem) const;
void HandleCommands(); void HandleCommands();
void HandleCommand(uint32 type, const std::string& text, Player& fromPlayer, const uint32 lang = LANG_UNIVERSAL); void HandleCommand(uint32 type, const std::string& text, Player& fromPlayer, const uint32 lang = LANG_UNIVERSAL);
bool _isBotInitializing = false;
protected: protected:
Player* bot; Player* bot;

View File

@ -108,7 +108,7 @@ public:
"|cffcccccchttps://github.com/liyunfan1223/mod-playerbots|r"); "|cffcccccchttps://github.com/liyunfan1223/mod-playerbots|r");
} }
if (sPlayerbotAIConfig->enabled || sPlayerbotAIConfig->randomBotAutologin) /*if (sPlayerbotAIConfig->enabled || sPlayerbotAIConfig->randomBotAutologin)
{ {
std::string roundedTime = std::string roundedTime =
std::to_string(std::ceil((sPlayerbotAIConfig->maxRandomBots * 0.11 / 60) * 10) / 10.0); std::to_string(std::ceil((sPlayerbotAIConfig->maxRandomBots * 0.11 / 60) * 10) / 10.0);
@ -117,7 +117,7 @@ public:
ChatHandler(player->GetSession()).SendSysMessage( ChatHandler(player->GetSession()).SendSysMessage(
"|cff00ff00Playerbots:|r bot initialization at server startup takes about '" "|cff00ff00Playerbots:|r bot initialization at server startup takes about '"
+ roundedTime + "' minutes."); + roundedTime + "' minutes.");
} }*/
} }
} }
@ -209,16 +209,30 @@ public:
void OnPlayerGiveXP(Player* player, uint32& amount, Unit* /*victim*/, uint8 /*xpSource*/) override void OnPlayerGiveXP(Player* player, uint32& amount, Unit* /*victim*/, uint8 /*xpSource*/) override
{ {
if (!player->GetSession()->IsBot()) // early return
if (sPlayerbotAIConfig->randomBotXPRate == 1.0 || !player)
return; return;
if (!sRandomPlayerbotMgr->IsRandomBot(player)) // no XP multiplier, when player is no bot.
if (!player->GetSession()->IsBot() || !sRandomPlayerbotMgr->IsRandomBot(player))
return; return;
if (sPlayerbotAIConfig->randomBotXPRate != 1.0) // no XP multiplier, when bot has group where leader is a real player.
if (Group* group = player->GetGroup())
{ {
amount = static_cast<uint32>(std::round(static_cast<float>(amount) * sPlayerbotAIConfig->randomBotXPRate)); Player* leader = group->GetLeader();
if (leader && leader != player)
{
if (PlayerbotAI* leaderBotAI = GET_PLAYERBOT_AI(leader))
{
if (leaderBotAI->HasRealPlayerMaster())
return;
}
}
} }
// otherwise apply bot XP multiplier.
amount = static_cast<uint32>(std::round(static_cast<float>(amount) * sPlayerbotAIConfig->randomBotXPRate));
} }
}; };

View File

@ -21,8 +21,8 @@
const int ITEM_SOUL_SHARD = 6265; const int ITEM_SOUL_SHARD = 6265;
// Checks if the bot has less than 20 soul shards, and if so, allows casting Drain Soul // Checks if the bot has less than 26 soul shards, and if so, allows casting Drain Soul
bool CastDrainSoulAction::isUseful() { return AI_VALUE2(uint32, "item count", "soul shard") < 20; } bool CastDrainSoulAction::isUseful() { return AI_VALUE2(uint32, "item count", "soul shard") < 26; }
// Checks if the bot's health is above a certain threshold, and if so, allows casting Life Tap // Checks if the bot's health is above a certain threshold, and if so, allows casting Life Tap
bool CastLifeTapAction::isUseful() { return AI_VALUE2(uint8, "health", "self target") > sPlayerbotAIConfig->lowHealth; } bool CastLifeTapAction::isUseful() { return AI_VALUE2(uint8, "health", "self target") > sPlayerbotAIConfig->lowHealth; }

View File

@ -29,8 +29,6 @@ public:
creators["boost"] = &WarlockStrategyFactoryInternal::boost; creators["boost"] = &WarlockStrategyFactoryInternal::boost;
creators["cc"] = &WarlockStrategyFactoryInternal::cc; creators["cc"] = &WarlockStrategyFactoryInternal::cc;
creators["pet"] = &WarlockStrategyFactoryInternal::pet; creators["pet"] = &WarlockStrategyFactoryInternal::pet;
creators["spellstone"] = &WarlockStrategyFactoryInternal::spellstone;
creators["firestone"] = &WarlockStrategyFactoryInternal::firestone;
creators["meta melee"] = &WarlockStrategyFactoryInternal::meta_melee_aoe; creators["meta melee"] = &WarlockStrategyFactoryInternal::meta_melee_aoe;
creators["tank"] = &WarlockStrategyFactoryInternal::tank; creators["tank"] = &WarlockStrategyFactoryInternal::tank;
creators["aoe"] = &WarlockStrategyFactoryInternal::aoe; creators["aoe"] = &WarlockStrategyFactoryInternal::aoe;
@ -42,8 +40,6 @@ private:
static Strategy* boost(PlayerbotAI* botAI) { return new WarlockBoostStrategy(botAI); } static Strategy* boost(PlayerbotAI* botAI) { return new WarlockBoostStrategy(botAI); }
static Strategy* cc(PlayerbotAI* botAI) { return new WarlockCcStrategy(botAI); } static Strategy* cc(PlayerbotAI* botAI) { return new WarlockCcStrategy(botAI); }
static Strategy* pet(PlayerbotAI* botAI) { return new WarlockPetStrategy(botAI); } static Strategy* pet(PlayerbotAI* botAI) { return new WarlockPetStrategy(botAI); }
static Strategy* spellstone(PlayerbotAI* botAI) { return new UseSpellstoneStrategy(botAI); }
static Strategy* firestone(PlayerbotAI* botAI) { return new UseFirestoneStrategy(botAI); }
static Strategy* meta_melee_aoe(PlayerbotAI* botAI) { return new MetaMeleeAoeStrategy(botAI); } static Strategy* meta_melee_aoe(PlayerbotAI* botAI) { return new MetaMeleeAoeStrategy(botAI); }
static Strategy* tank(PlayerbotAI* botAI) { return new TankWarlockStrategy(botAI); } static Strategy* tank(PlayerbotAI* botAI) { return new TankWarlockStrategy(botAI); }
static Strategy* aoe(PlayerbotAI* botAI) { return new AoEWarlockStrategy(botAI); } static Strategy* aoe(PlayerbotAI* botAI) { return new AoEWarlockStrategy(botAI); }
@ -125,6 +121,20 @@ private:
static Strategy* curse_of_weakness(PlayerbotAI* botAI) { return new WarlockCurseOfWeaknessStrategy(botAI); } static Strategy* curse_of_weakness(PlayerbotAI* botAI) { return new WarlockCurseOfWeaknessStrategy(botAI); }
}; };
class WarlockWeaponStoneStrategyFactoryInternal : public NamedObjectContext<Strategy>
{
public:
WarlockWeaponStoneStrategyFactoryInternal() : NamedObjectContext<Strategy>(false, true)
{
creators["firestone"] = &WarlockWeaponStoneStrategyFactoryInternal::firestone;
creators["spellstone"] = &WarlockWeaponStoneStrategyFactoryInternal::spellstone;
}
private:
static Strategy* firestone(PlayerbotAI* ai) { return new UseFirestoneStrategy(ai); }
static Strategy* spellstone(PlayerbotAI* ai) { return new UseSpellstoneStrategy(ai); }
};
class WarlockTriggerFactoryInternal : public NamedObjectContext<Trigger> class WarlockTriggerFactoryInternal : public NamedObjectContext<Trigger>
{ {
public: public:
@ -333,19 +343,13 @@ private:
static Action* devour_magic_purge(PlayerbotAI* botAI) { return new CastDevourMagicPurgeAction(botAI); } static Action* devour_magic_purge(PlayerbotAI* botAI) { return new CastDevourMagicPurgeAction(botAI); }
static Action* devour_magic_cleanse(PlayerbotAI* botAI) { return new CastDevourMagicCleanseAction(botAI); } static Action* devour_magic_cleanse(PlayerbotAI* botAI) { return new CastDevourMagicCleanseAction(botAI); }
static Action* seed_of_corruption(PlayerbotAI* botAI) { return new CastSeedOfCorruptionAction(botAI); } static Action* seed_of_corruption(PlayerbotAI* botAI) { return new CastSeedOfCorruptionAction(botAI); }
static Action* seed_of_corruption_on_attacker(PlayerbotAI* botAI) static Action* seed_of_corruption_on_attacker(PlayerbotAI* botAI) { return new CastSeedOfCorruptionOnAttackerAction(botAI); }
{
return new CastSeedOfCorruptionOnAttackerAction(botAI);
}
static Action* rain_of_fire(PlayerbotAI* botAI) { return new CastRainOfFireAction(botAI); } static Action* rain_of_fire(PlayerbotAI* botAI) { return new CastRainOfFireAction(botAI); }
static Action* hellfire(PlayerbotAI* botAI) { return new CastHellfireAction(botAI); } static Action* hellfire(PlayerbotAI* botAI) { return new CastHellfireAction(botAI); }
static Action* shadowfury(PlayerbotAI* botAI) { return new CastShadowfuryAction(botAI); } static Action* shadowfury(PlayerbotAI* botAI) { return new CastShadowfuryAction(botAI); }
static Action* life_tap(PlayerbotAI* botAI) { return new CastLifeTapAction(botAI); } static Action* life_tap(PlayerbotAI* botAI) { return new CastLifeTapAction(botAI); }
static Action* unstable_affliction(PlayerbotAI* ai) { return new CastUnstableAfflictionAction(ai); } static Action* unstable_affliction(PlayerbotAI* ai) { return new CastUnstableAfflictionAction(ai); }
static Action* unstable_affliction_on_attacker(PlayerbotAI* ai) static Action* unstable_affliction_on_attacker(PlayerbotAI* ai) { return new CastUnstableAfflictionOnAttackerAction(ai); }
{
return new CastUnstableAfflictionOnAttackerAction(ai);
}
static Action* haunt(PlayerbotAI* ai) { return new CastHauntAction(ai); } static Action* haunt(PlayerbotAI* ai) { return new CastHauntAction(ai); }
static Action* demonic_empowerment(PlayerbotAI* ai) { return new CastDemonicEmpowermentAction(ai); } static Action* demonic_empowerment(PlayerbotAI* ai) { return new CastDemonicEmpowermentAction(ai); }
static Action* metamorphosis(PlayerbotAI* ai) { return new CastMetamorphosisAction(ai); } static Action* metamorphosis(PlayerbotAI* ai) { return new CastMetamorphosisAction(ai); }
@ -360,10 +364,7 @@ private:
static Action* searing_pain(PlayerbotAI* botAI) { return new CastSearingPainAction(botAI); } static Action* searing_pain(PlayerbotAI* botAI) { return new CastSearingPainAction(botAI); }
static Action* shadow_ward(PlayerbotAI* botAI) { return new CastShadowWardAction(botAI); } static Action* shadow_ward(PlayerbotAI* botAI) { return new CastShadowWardAction(botAI); }
static Action* curse_of_agony(PlayerbotAI* botAI) { return new CastCurseOfAgonyAction(botAI); } static Action* curse_of_agony(PlayerbotAI* botAI) { return new CastCurseOfAgonyAction(botAI); }
static Action* curse_of_agony_on_attacker(PlayerbotAI* botAI) static Action* curse_of_agony_on_attacker(PlayerbotAI* botAI) { return new CastCurseOfAgonyOnAttackerAction(botAI); }
{
return new CastCurseOfAgonyOnAttackerAction(botAI);
}
static Action* curse_of_the_elements(PlayerbotAI* ai) { return new CastCurseOfTheElementsAction(ai); } static Action* curse_of_the_elements(PlayerbotAI* ai) { return new CastCurseOfTheElementsAction(ai); }
static Action* curse_of_doom(PlayerbotAI* ai) { return new CastCurseOfDoomAction(ai); } static Action* curse_of_doom(PlayerbotAI* ai) { return new CastCurseOfDoomAction(ai); }
static Action* curse_of_exhaustion(PlayerbotAI* ai) { return new CastCurseOfExhaustionAction(ai); } static Action* curse_of_exhaustion(PlayerbotAI* ai) { return new CastCurseOfExhaustionAction(ai); }
@ -397,6 +398,7 @@ void WarlockAiObjectContext::BuildSharedStrategyContexts(SharedNamedObjectContex
strategyContexts.Add(new WarlockPetStrategyFactoryInternal()); strategyContexts.Add(new WarlockPetStrategyFactoryInternal());
strategyContexts.Add(new WarlockSoulstoneStrategyFactoryInternal()); strategyContexts.Add(new WarlockSoulstoneStrategyFactoryInternal());
strategyContexts.Add(new WarlockCurseStrategyFactoryInternal()); strategyContexts.Add(new WarlockCurseStrategyFactoryInternal());
strategyContexts.Add(new WarlockWeaponStoneStrategyFactoryInternal());
} }
void WarlockAiObjectContext::BuildSharedActionContexts(SharedNamedObjectContextList<Action>& actionContexts) void WarlockAiObjectContext::BuildSharedActionContexts(SharedNamedObjectContextList<Action>& actionContexts)

View File

@ -46,7 +46,7 @@ bool WarlockConjuredItemTrigger::IsActive()
bool OutOfSoulShardsTrigger::IsActive() { return GetSoulShardCount(botAI->GetBot()) == 0; } bool OutOfSoulShardsTrigger::IsActive() { return GetSoulShardCount(botAI->GetBot()) == 0; }
bool TooManySoulShardsTrigger::IsActive() { return GetSoulShardCount(botAI->GetBot()) >= 6; } bool TooManySoulShardsTrigger::IsActive() { return GetSoulShardCount(botAI->GetBot()) >= 26; }
bool OutOfSoulstoneTrigger::IsActive() { return GetSoulstoneCount(botAI->GetBot()) == 0; } bool OutOfSoulstoneTrigger::IsActive() { return GetSoulstoneCount(botAI->GetBot()) == 0; }