Skip to content

Commit

Permalink
-Rpg enchant: Enchanter bots with rpg player strategy will now enchan…
Browse files Browse the repository at this point in the history
…t items of nearby bots.
  • Loading branch information
mostlikely4r committed Sep 6, 2024
1 parent af1648d commit 52de27a
Show file tree
Hide file tree
Showing 13 changed files with 266 additions and 2 deletions.
3 changes: 3 additions & 0 deletions playerbot/strategy/actions/ActionContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ namespace ai
creators["rpg spell"] = &ActionContext::rpg_spell;
creators["rpg craft"] = &ActionContext::rpg_craft;
creators["rpg trade useful"] = &ActionContext::rpg_trade_useful;
creators["rpg enchant"] = &ActionContext::rpg_enchant;
creators["rpg duel"] = &ActionContext::rpg_duel;
creators["rpg item"] = &ActionContext::rpg_item;

Expand Down Expand Up @@ -538,6 +539,8 @@ namespace ai
static Action* rpg_spell(PlayerbotAI* ai) { return new RpgSpellAction(ai); }
static Action* rpg_craft(PlayerbotAI* ai) { return new RpgCraftAction(ai); }
static Action* rpg_trade_useful(PlayerbotAI* ai) { return new RpgTradeUsefulAction(ai); }
static Action* rpg_enchant(PlayerbotAI* ai) { return new RpgEnchantAction(ai); }

static Action* rpg_duel(PlayerbotAI* ai) { return new RpgDuelAction(ai); }
static Action* rpg_item(PlayerbotAI* ai) { return new RpgItemAction(ai); }

Expand Down
90 changes: 90 additions & 0 deletions playerbot/strategy/actions/RpgSubActions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,96 @@ bool RpgTradeUsefulAction::Execute(Event& event)
return isTrading;
}

bool RpgEnchantAction::Execute(Event& event)
{
rpg->BeforeExecute();

GuidPosition guidP = AI_VALUE(GuidPosition, "rpg target");

Player* player = guidP.GetPlayer();

if (!player)
return false;

std::list<Item*> items = AI_VALUE(std::list<Item*>, "items useful to enchant");

if (items.empty())
return false;

std::vector<uint32> enchantSpells = AI_VALUE(std::vector<uint32>, "enchant spells");

//Needs more logic to pick best enchant to apply instead of a random one that's bette than current.
std::shuffle(enchantSpells.begin(), enchantSpells.end(), *GetRandomGenerator());

for (auto& spellId : enchantSpells)
{
Item* item = PAI_VALUE2(Item*, "item for spell", spellId);

if (std::find(items.begin(), items.end(), item) == items.end())
continue;

std::ostringstream param;

param << chat->formatWorldobject(bot);
param << " ";
param << chat->formatItem(item);

ai->TellDebug(ai->GetMaster(), "enchanting" + param.str(), "debug rpg");

if (player->isRealPlayer() && !player->GetTradeData()) //Start the trade from the other side to open the window
{
ai->TellDebug(ai->GetMaster(), "open trade window", "debug rpg");
WorldPacket packet(CMSG_INITIATE_TRADE);
packet << player->GetObjectGuid();
bot->GetSession()->HandleInitiateTradeOpcode(packet);
}

if (!player->GetTradeData() || !player->GetTradeData()->HasItem(item->GetObjectGuid()))
{
ai->TellDebug(ai->GetMaster(), "starting trade", "debug rpg");
player->GetPlayerbotAI()->DoSpecificAction("trade", Event("rpg action", param.str().c_str()), true);
}

bool isTrading = bot->GetTradeData();

if (isTrading)
{
if (player->GetTradeData()->HasItem(item->GetObjectGuid())) //Did we manage to add the item to the trade?
{
uint32 duration;
Unit* target = nullptr;

ai->TellDebug(ai->GetMaster(), "set enchant spell", "debug rpg");
bool didCast = ai->CastSpell(spellId, target, item, true, &duration);

if (didCast)
{
ai->TellDebug(ai->GetMaster(), "accept trade", "debug rpg");
if (bot->GetGroup() && bot->GetGroup()->IsMember(guidP))
ai->TellPlayerNoFacing(GetMaster(), "Let me enchant this " + chat->formatItem(item) + " with " + chat->formatSpell(spellId) + " for you " + player->GetName() + ".", PlayerbotSecurityLevel::PLAYERBOT_SECURITY_ALLOW_ALL, false);
else
bot->Say("Let me enchant this " + chat->formatItem(item) + " with " + chat->formatSpell(spellId) + " for you " + player->GetName() + ".", (bot->GetTeam() == ALLIANCE ? LANG_COMMON : LANG_ORCISH));

WorldPacket p;
uint32 status = TRADE_STATUS_TRADE_ACCEPT;
p << status;
bot->GetSession()->HandleAcceptTradeOpcode(p);
}
}

ai->SetActionDuration(sPlayerbotAIConfig.rpgDelay);
}

rpg->AfterExecute(isTrading, true, isTrading ? "rpg enchant" : "rpg");

DoDelay();

return isTrading;
}

return false;
}

bool RpgDuelAction::isUseful()
{
// do not offer duel in non pvp areas
Expand Down
8 changes: 8 additions & 0 deletions playerbot/strategy/actions/RpgSubActions.h
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,14 @@ namespace ai
virtual bool Execute(Event& event);
};

class RpgEnchantAction : public RpgTradeUsefulAction
{
public:
RpgEnchantAction(PlayerbotAI* ai, std::string name = "rpg enchant") : RpgTradeUsefulAction(ai, name) {}

virtual bool Execute(Event& event);
};

class RpgDuelAction : public RpgSubAction
{
public:
Expand Down
3 changes: 3 additions & 0 deletions playerbot/strategy/actions/TradeStatusAction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,9 @@ bool TradeStatusAction::CheckTrade()
break;
}
}

if (!isGettingItem) //Enchanting an item.
isGettingItem = (bot->GetTradeData()->GetItem(TRADE_SLOT_NONTRADED) && trader->GetTradeData()->GetSpell());

if (isGettingItem)
{
Expand Down
4 changes: 4 additions & 0 deletions playerbot/strategy/generic/RpgStrategy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,10 @@ void RpgPlayerStrategy::InitNonCombatTriggers(std::list<TriggerNode*>& triggers)
"rpg trade useful",
NextAction::array(0, new NextAction("rpg trade useful", 1.030f), NULL)));

triggers.push_back(new TriggerNode(
"rpg enchant",
NextAction::array(0, new NextAction("rpg enchant", 1.029f), NULL)));

triggers.push_back(new TriggerNode(
"rpg duel",
NextAction::array(0, new NextAction("rpg duel", 1.010f), NULL)));
Expand Down
37 changes: 36 additions & 1 deletion playerbot/strategy/triggers/RpgTriggers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -609,7 +609,42 @@ bool RpgTradeUsefulTrigger::IsActive()
if (bot->GetTrader() && bot->GetTrader() != player)
return false;

if (AI_VALUE_LAZY(std::list<Item*>, "items useful to give").empty())
if (AI_VALUE(std::list<Item*>, "items useful to give").empty())
return false;

return true;
}

bool RpgEnchantTrigger::IsActive()
{
GuidPosition guidP(getGuidP());

if (!guidP.IsPlayer())
return false;

Player* player = guidP.GetPlayer();


if (!player)
return false;

//if (player->GetTrader() == bot && bot->GetTrader() == player) //Continue trading please.
// return true;

if (!isFriend(player))
return false;

if (!player->IsWithinLOSInMap(bot))
return false;

//Trading with someone else
//if (player->GetTrader() && player->GetTrader() != bot)
// return false;

//if (bot->GetTrader() && bot->GetTrader() != player)
// return false;

if (AI_VALUE(std::list<Item*>, "items useful to enchant").empty())
return false;

return true;
Expand Down
9 changes: 8 additions & 1 deletion playerbot/strategy/triggers/RpgTriggers.h
Original file line number Diff line number Diff line change
Expand Up @@ -203,10 +203,17 @@ namespace ai
public:
RpgTradeUsefulTrigger(PlayerbotAI* ai, std::string name = "rpg trade useful") : RpgTrigger(ai, name) {}
virtual bool IsActive();
private:
protected:
virtual bool isFriend(Player* player); //Move to value later.
};

class RpgEnchantTrigger : public RpgTradeUsefulTrigger
{
public:
RpgEnchantTrigger(PlayerbotAI* ai, std::string name = "rpg enchant") : RpgTradeUsefulTrigger(ai, name) {}
virtual bool IsActive();
};

class RpgDuelTrigger : public RpgTrigger
{
public:
Expand Down
2 changes: 2 additions & 0 deletions playerbot/strategy/triggers/TriggerContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ namespace ai
creators["rpg spell"] = &TriggerContext::rpg_spell;
creators["rpg craft"] = &TriggerContext::rpg_craft;
creators["rpg trade useful"] = &TriggerContext::rpg_trade_useful;
creators["rpg enchant"] = &TriggerContext::rpg_enchant;
creators["rpg duel"] = &TriggerContext::rpg_duel;
creators["rpg item"] = &TriggerContext::rpg_item;

Expand Down Expand Up @@ -494,6 +495,7 @@ namespace ai
static Trigger* rpg_spell(PlayerbotAI* ai) { return new RpgSpellTrigger(ai); }
static Trigger* rpg_craft(PlayerbotAI* ai) { return new RpgCraftTrigger(ai); }
static Trigger* rpg_trade_useful(PlayerbotAI* ai) { return new RpgTradeUsefulTrigger(ai); }
static Trigger* rpg_enchant(PlayerbotAI* ai) { return new RpgEnchantTrigger(ai); }
static Trigger* rpg_duel(PlayerbotAI* ai) { return new RpgDuelTrigger(ai); }
static Trigger* rpg_item(PlayerbotAI* ai) { return new RpgItemTrigger(ai); }

Expand Down
28 changes: 28 additions & 0 deletions playerbot/strategy/values/CraftValues.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,34 @@ std::vector<uint32> CraftSpellsValue::Calculate()
return spellIds;
}

std::vector<uint32> EnchantSpellsValue::Calculate()
{
std::vector<uint32> spellIds;

PlayerSpellMap const& spellMap = bot->GetSpellMap();

for (auto& spell : spellMap)
{
uint32 spellId = spell.first;

if (spell.second.state == PLAYERSPELL_REMOVED || spell.second.disabled || IsPassiveSpell(spellId))
continue;

const SpellEntry* pSpellInfo = sServerFacade.LookupSpellInfo(spellId);
if (!pSpellInfo)
continue;

if (pSpellInfo->Effect[0] != SPELL_EFFECT_ENCHANT_ITEM || !pSpellInfo->ReagentCount[0])
continue;


spellIds.push_back(spellId);
break;
}

return spellIds;
}

uint32 HasReagentsForValue::Calculate()
{
if (ai->HasCheat(BotCheatMask::item))
Expand Down
7 changes: 7 additions & 0 deletions playerbot/strategy/values/CraftValues.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,13 @@ namespace ai
virtual std::vector<uint32> Calculate() override;
};

class EnchantSpellsValue : public CalculatedValue<std::vector<uint32>> //All enchanting spells
{
public:
EnchantSpellsValue(PlayerbotAI* ai, std::string name = "enchant spells", int checkInterval = 10) : CalculatedValue<std::vector<uint32>>(ai, name, checkInterval) {}
virtual std::vector<uint32> Calculate() override;
};

class HasReagentsForValue : public Uint32CalculatedValue, public Qualified //Does the bot have reagents to cast this craft spell?
{
public:
Expand Down
68 changes: 68 additions & 0 deletions playerbot/strategy/values/TradeValues.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include "TradeValues.h"
#include "ItemUsageValue.h"
#include "playerbot/TravelMgr.h"
#include "playerbot/RandomItemMgr.h"
#include "playerbot/ServerFacade.h"

using namespace ai;

Expand Down Expand Up @@ -75,3 +77,69 @@ std::list<Item*> ItemsUsefulToGiveValue::Calculate()

return giveItems;
}

std::list<Item*> ItemsUsefulToEnchantValue::Calculate()
{
GuidPosition guidP = AI_VALUE(GuidPosition, "rpg target");

Player* player = guidP.GetPlayer();

std::list<Item*> enchantItems;

if (ai->HasActivePlayerMaster() || !player->GetPlayerbotAI())
return enchantItems;

std::vector<uint32> enchantSpells = AI_VALUE(std::vector<uint32>, "enchant spells");

for (auto& spellId : enchantSpells)
{
const SpellEntry* pSpellInfo = sServerFacade.LookupSpellInfo(spellId);

if (!pSpellInfo)
continue;

uint32 castCount = AI_VALUE2(uint32, "has reagents for", spellId);

if (!castCount)
continue;

Item* item = PAI_VALUE2(Item*, "item for spell", spellId);

if (!item)
continue;

if (PHAS_AI_VALUE2("force item usage", item->GetProto()->ItemId))
continue;

// Only trade equipped items
if (!item->IsEquipped())
continue;

TradeData* trade = bot->GetTradeData();

if (trade)
{

if (trade->HasItem(item->GetObjectGuid())) //This specific item isn't being traded.
continue;

if (std::any_of(enchantItems.begin(), enchantItems.end(), [item](Item* i) {return i->GetEntry() == item->GetEntry(); })) //We didn't already add a simular item to this list.
continue;
}

uint32 enchant_id = pSpellInfo->EffectMiscValue[0];

uint32 currentEnchnatWeight = 0;
if (item->GetEnchantmentId(PERM_ENCHANTMENT_SLOT))
currentEnchnatWeight = sRandomItemMgr.CalculateEnchantWeight(bot->getClass(), sRandomItemMgr.GetPlayerSpecId(bot), item->GetEnchantmentId(PERM_ENCHANTMENT_SLOT));

uint32 newEnchantWeight = sRandomItemMgr.CalculateEnchantWeight(bot->getClass(), sRandomItemMgr.GetPlayerSpecId(bot), enchant_id);

if (!item->GetEnchantmentId(PERM_ENCHANTMENT_SLOT) || currentEnchnatWeight >= newEnchantWeight)
continue;

enchantItems.push_back(item);
}

return enchantItems;
}
7 changes: 7 additions & 0 deletions playerbot/strategy/values/TradeValues.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,11 @@ namespace ai
private:
bool IsTradingItem(uint32 entry);
};

class ItemsUsefulToEnchantValue : public CalculatedValue< std::list<Item*>>, public Qualified
{
public:
ItemsUsefulToEnchantValue(PlayerbotAI* ai, std::string name = "useful to enchant") : CalculatedValue(ai, name), Qualified() {}
std::list<Item*> Calculate();
};
}
2 changes: 2 additions & 0 deletions playerbot/strategy/values/ValueContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ namespace ai

creators["vendor has useful item"] = [](PlayerbotAI* ai) { return new VendorHasUsefulItemValue(ai); };
creators["craft spells"] = [](PlayerbotAI* ai) { return new CraftSpellsValue(ai); };
creators["enchant spells"] = [](PlayerbotAI* ai) { return new EnchantSpellsValue(ai); };
creators["has reagents for"] = [](PlayerbotAI* ai) { return new HasReagentsForValue(ai); };
creators["can craft spell"] = [](PlayerbotAI* ai) { return new CanCraftSpellValue(ai); };
creators["should craft spell"] = [](PlayerbotAI* ai) { return new ShouldCraftSpellValue(ai); };
Expand All @@ -366,6 +367,7 @@ namespace ai
creators["entry loot usage"] = [](PlayerbotAI* ai) { return new EntryLootUsageValue(ai); };
creators["has upgrade"] = [](PlayerbotAI* ai) { return new HasUpgradeValue(ai); };
creators["items useful to give"] = [](PlayerbotAI* ai) { return new ItemsUsefulToGiveValue(ai); };
creators["items useful to enchant"] = [](PlayerbotAI* ai) { return new ItemsUsefulToEnchantValue(ai); };

creators["see spell location"] = [](PlayerbotAI* ai) { return new SeeSpellLocationValue(ai); };
creators["RTSC selected"] = [](PlayerbotAI* ai) { return new RTSCSelectedValue(ai); };
Expand Down

2 comments on commit 52de27a

@al3xc1985
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does they do it automaticaly? I yes
What about the players, are they doing that automatically for the players 2?

@mostlikely4r
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does they do it automaticaly? I yes What about the players, are they doing that automatically for the players 2?

See:

if (ai->HasActivePlayerMaster() || !player->GetPlayerbotAI())

Only bots.

Please sign in to comment.