mxwow modules
This commit is contained in:
parent
93073b0be2
commit
e87efbaf6e
@ -18,7 +18,7 @@ public:
|
|||||||
|
|
||||||
void OnPlayerLogin(Player* player) override
|
void OnPlayerLogin(Player* player) override
|
||||||
{
|
{
|
||||||
ChatHandler(player->GetSession()).SendSysMessage("This server is running the |cff4CFF00MxW Boss Kill |rmodule.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnPlayerCreatureKill(Player* player, Creature* boss) {
|
void OnPlayerCreatureKill(Player* player, Creature* boss) {
|
||||||
|
@ -18,8 +18,7 @@ mxwow_bounty() : PlayerScript("mxwow_bounty") { }
|
|||||||
|
|
||||||
void OnPlayerLogin(Player* player) override
|
void OnPlayerLogin(Player* player) override
|
||||||
{
|
{
|
||||||
if (sConfigMgr->GetOption<bool>("MxWoW_Bounty.Enabled", true)) { return; }
|
|
||||||
ChatHandler(player->GetSession()).SendSysMessage("This server is running the |cff4CFF00MxW Bounty |rmodule.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnPlayerCreatureKill(Player* player, Creature* creature) {
|
void OnPlayerCreatureKill(Player* player, Creature* creature) {
|
||||||
@ -76,9 +75,8 @@ mxwow_bounty() : PlayerScript("mxwow_bounty") { }
|
|||||||
if (formatedAmountGold == 0 && formatedAmountSilver == 0 && formatedAmountCopper > 0) {
|
if (formatedAmountGold == 0 && formatedAmountSilver == 0 && formatedAmountCopper > 0) {
|
||||||
ss << "|cffabeeff[MxW] Vous obtenez une récompense de |cffb87333" << formatedAmountCopper << "c |cffabeeffpour avoir tué " << cName << " (" << cLevel << ").";
|
ss << "|cffabeeff[MxW] Vous obtenez une récompense de |cffb87333" << formatedAmountCopper << "c |cffabeeffpour avoir tué " << cName << " (" << cLevel << ").";
|
||||||
}
|
}
|
||||||
ChatHandler(player->GetSession()).PSendSysMessage(ss.str().c_str());
|
|
||||||
|
|
||||||
player->ModifyMoney(+int32(giveAmount));
|
player->ModifyMoney(+int32(giveAmount));
|
||||||
|
ChatHandler(player->GetSession()).PSendSysMessage(ss.str().c_str());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Group* group = player->GetGroup();
|
Group* group = player->GetGroup();
|
||||||
@ -95,9 +93,8 @@ mxwow_bounty() : PlayerScript("mxwow_bounty") { }
|
|||||||
if (formatedAmountGold == 0 && formatedAmountSilver == 0 && formatedAmountCopper > 0) {
|
if (formatedAmountGold == 0 && formatedAmountSilver == 0 && formatedAmountCopper > 0) {
|
||||||
ss << "|cffabeeff[MxW] Vous obtenez une récompense de |cffb87333" << formatedAmountCopper << "c |cffabeeffpour avoir tué " << cName << " (" << cLevel << ").";
|
ss << "|cffabeeff[MxW] Vous obtenez une récompense de |cffb87333" << formatedAmountCopper << "c |cffabeeffpour avoir tué " << cName << " (" << cLevel << ").";
|
||||||
}
|
}
|
||||||
ChatHandler(p->GetSession()).PSendSysMessage(ss.str().c_str());
|
|
||||||
|
|
||||||
p->ModifyMoney(+int32(giveAmount));
|
p->ModifyMoney(+int32(giveAmount));
|
||||||
|
ChatHandler(p->GetSession()).PSendSysMessage(ss.str().c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,8 +19,7 @@ mxwow_honorable() : PlayerScript("mxwow_honorable") { }
|
|||||||
|
|
||||||
void OnPlayerLogin(Player* player) override
|
void OnPlayerLogin(Player* player) override
|
||||||
{
|
{
|
||||||
if (!sConfigMgr->GetOption<bool>("MxWoW_Honorable.Enabled", true)) { return; }
|
|
||||||
ChatHandler(player->GetSession()).SendSysMessage("This server is running the |cff4CFF00MxW Honorable |rmodule.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnPlayerPVPKill(Player* player, Player* victim) {
|
void OnPlayerPVPKill(Player* player, Player* victim) {
|
||||||
@ -41,7 +40,7 @@ mxwow_honorable() : PlayerScript("mxwow_honorable") { }
|
|||||||
std::string pName = player->GetName();
|
std::string pName = player->GetName();
|
||||||
uint32 pLevel = player->GetLevel();
|
uint32 pLevel = player->GetLevel();
|
||||||
uint32 piLevel = player->GetAverageItemLevelForDF();
|
uint32 piLevel = player->GetAverageItemLevelForDF();
|
||||||
float pLife = player->GetHealthPct();
|
float pLife = round(player->GetHealthPct());
|
||||||
bool pGrouped = player->GetGroup();
|
bool pGrouped = player->GetGroup();
|
||||||
|
|
||||||
std::string vName = victim->GetName();
|
std::string vName = victim->GetName();
|
||||||
@ -56,6 +55,7 @@ mxwow_honorable() : PlayerScript("mxwow_honorable") { }
|
|||||||
else {
|
else {
|
||||||
ss << "|cffabeeff[MxW][|cfffc0303PvP|cffabeeff][|cfffc0303" << mapName << "|cffabeeff] [" << GetPlayerFactionByRace(player) << "|cffabeeff][" << pLevel << "][" << GetPlayerColor(player) << pName << "|cffabeeff][iL" << piLevel << "][" << pLife << "%] |cfffc0303a tué|cffabeeff [" << GetPlayerFactionByRace(victim) << "|cffabeeff][" << vLevel << "][" << GetPlayerColor(victim) << vName << "|cffabeeff][iL" << viLevel << "].";
|
ss << "|cffabeeff[MxW][|cfffc0303PvP|cffabeeff][|cfffc0303" << mapName << "|cffabeeff] [" << GetPlayerFactionByRace(player) << "|cffabeeff][" << pLevel << "][" << GetPlayerColor(player) << pName << "|cffabeeff][iL" << piLevel << "][" << pLife << "%] |cfffc0303a tué|cffabeeff [" << GetPlayerFactionByRace(victim) << "|cffabeeff][" << vLevel << "][" << GetPlayerColor(victim) << vName << "|cffabeeff][iL" << viLevel << "].";
|
||||||
player->ModifyMoney(+int32(base));
|
player->ModifyMoney(+int32(base));
|
||||||
|
ManageHonorableKill(player, victim);
|
||||||
}
|
}
|
||||||
|
|
||||||
sWorldSessionMgr->SendServerMessage(SERVER_MSG_STRING, ss.str().c_str());
|
sWorldSessionMgr->SendServerMessage(SERVER_MSG_STRING, ss.str().c_str());
|
||||||
@ -127,6 +127,43 @@ mxwow_honorable() : PlayerScript("mxwow_honorable") { }
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ManageHonorableKill(Player* player, Player* victim) {
|
||||||
|
uint32 base = sConfigMgr->GetOption<int>("MxWoW_Honorable.Kill.Copper", true);
|
||||||
|
uint32 killQty;
|
||||||
|
std::ostringstream ss;
|
||||||
|
std::ostringstream ssV;
|
||||||
|
uint32 kId = player->GetGUID().GetRawValue();
|
||||||
|
std::string kName = player->GetName();
|
||||||
|
uint32 kGuildId = 0;
|
||||||
|
std::string kGuildName = "";
|
||||||
|
uint32 vId = victim->GetGUID().GetRawValue();
|
||||||
|
std::string vName = victim->GetName();
|
||||||
|
uint32 vGuildId = 0;
|
||||||
|
std::string vGuildName = "";
|
||||||
|
uint32 kLevel = player->GetLevel();
|
||||||
|
uint32 vLevel = victim->GetLevel();
|
||||||
|
|
||||||
|
QueryResult queryPlayerHonorable = LoginDatabase.Query("SELECT * FROM mxw_honorable_kill WHERE kguid = {} AND vguid = {}", kId, vId);
|
||||||
|
if (queryPlayerHonorable) {
|
||||||
|
killQty = (*queryPlayerHonorable)[4].Get<int>();
|
||||||
|
killQty++;
|
||||||
|
if (player->GetGuild()) { kGuildId = player->GetGuildId(); kGuildName = player->GetGuildName(); }
|
||||||
|
if (victim->GetGuild()) { vGuildId = victim->GetGuildId(); vGuildName = victim->GetGuildName(); }
|
||||||
|
LoginDatabase.Execute("UPDATE mxw_honorable_kill SET kguildid='{}',vguildid='{}',killcount='{}',last_kill_klevel='{}',last_kill_vlevel='{}' WHERE kguid='{}' AND vguid='{}'", kGuildId, vGuildId, killQty, kLevel, vLevel, kId, vId);
|
||||||
|
ss << "|cffabeeff[MxW][|cfffc0303PvP|cffabeeff] Vous avez tué " << GetPlayerColor(victim) << vName << " |cffabeeffde façon honorable à |cfffc0303" << killQty << " |cffabeeffreprises. Ça vous rapporte "<< FormatMoney(base) << "g.";
|
||||||
|
ssV << "|cffabeeff[MxW][|cfffc0303PvP|cffabeeff] Vous avez été tué |cfffc0303" << killQty << " |cffabeefffois par " << GetPlayerColor(player) << vName << "|cffabeeffde.";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if(player->GetGuild()){ kGuildId = player->GetGuildId(); kGuildName = player->GetGuildName(); }
|
||||||
|
if (victim->GetGuild()) { vGuildId = victim->GetGuildId(); vGuildName = victim->GetGuildName(); }
|
||||||
|
LoginDatabase.Execute("INSERT INTO mxw_honorable_kill (kguid, kguildid, vguid, vguildid, killcount, last_kill_klevel, last_kill_vlevel) VALUES ({}, {}, {}, {}, {}, {}, {})", kId, kGuildId, vId, vGuildId, 1, kLevel, vLevel);
|
||||||
|
ss << "|cffabeeff[MxW][|cfffc0303PvP|cffabeeff] Vous avez tué " << GetPlayerColor(victim) << vName << " |cffabeeffde façon honorable à |cfffc03031 |cffabeeffreprise. Ça vous rapporte " << FormatMoney(base) << "g.";
|
||||||
|
ssV << "|cffabeeff[MxW][|cfffc0303PvP|cffabeeff] Vous avez été tué |cfffc03031 |cffabeefffois par " << GetPlayerColor(player) << vName << "|cffabeeffde.";
|
||||||
|
}
|
||||||
|
ChatHandler(player->GetSession()).PSendSysMessage(ss.str().c_str());
|
||||||
|
ChatHandler(victim->GetSession()).PSendSysMessage(ssV.str().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
void ManageDishonorableKill(Player* player, const Player* victim)
|
void ManageDishonorableKill(Player* player, const Player* victim)
|
||||||
{
|
{
|
||||||
uint32 killQty;
|
uint32 killQty;
|
||||||
@ -136,18 +173,18 @@ mxwow_honorable() : PlayerScript("mxwow_honorable") { }
|
|||||||
uint32 vId = victim->GetGUID().GetRawValue();
|
uint32 vId = victim->GetGUID().GetRawValue();
|
||||||
uint64 kCopper = GetCharCopper(kId);
|
uint64 kCopper = GetCharCopper(kId);
|
||||||
uint32 loss;
|
uint32 loss;
|
||||||
QueryResult queryPlayerDishonorable = LoginDatabase.Query("SELECT * FROM mxw_dishonorable_penalty WHERE kguid = {} AND vguid = {}", kId, vId);
|
QueryResult queryPlayerDishonorable = LoginDatabase.Query("SELECT * FROM mxw_dishonorable_kill WHERE kguid = {} AND vguid = {}", kId, vId);
|
||||||
if (queryPlayerDishonorable)
|
if (queryPlayerDishonorable)
|
||||||
{
|
{
|
||||||
killQty = (*queryPlayerDishonorable)[2].Get<int>();
|
killQty = (*queryPlayerDishonorable)[2].Get<int>();
|
||||||
killQty++;
|
killQty++;
|
||||||
loss = (kCopper * killQty) / 100;
|
loss = (kCopper * killQty) / 100;
|
||||||
LoginDatabase.Execute("UPDATE mxw_dishonorable_penalty SET killcount='{}' WHERE kguid='{}' AND vguid='{}'", killQty, kId, vId);
|
LoginDatabase.Execute("UPDATE mxw_dishonorable_kill SET killcount='{}' WHERE kguid='{}' AND vguid='{}'", killQty, kId, vId);
|
||||||
ss << "|cffabeeff[MxW] Vous avez tué "<< GetPlayerColor(victim) << vName << " |cffabeeffde façon déshonorable à |cfffc0303"<< killQty <<" |cffabeeffreprises. Vous subissez une perte de " << FormatMoney(loss) << "g";
|
ss << "|cffabeeff[MxW] Vous avez tué "<< GetPlayerColor(victim) << vName << " |cffabeeffde façon déshonorable à |cfffc0303"<< killQty <<" |cffabeeffreprises. Vous subissez une perte de " << FormatMoney(loss) << "g";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
loss = (kCopper * 1) / 100;
|
loss = (kCopper * 1) / 100;
|
||||||
LoginDatabase.Execute("INSERT INTO mxw_dishonorable_penalty (kguid, vguid, killcount) VALUES ({}, {}, {})", kId, vId, 1);
|
LoginDatabase.Execute("INSERT INTO mxw_dishonorable_kill (kguid, vguid, killcount) VALUES ({}, {}, {})", kId, vId, 1);
|
||||||
ss << "|cffabeeff[MxW] Vous avez tué " << GetPlayerColor(victim) << vName << " |cffabeeffde façon déshonorable à |cfffc03031 |cffabeeffreprise. Vous subissez une perte de " << FormatMoney(loss) << "g";
|
ss << "|cffabeeff[MxW] Vous avez tué " << GetPlayerColor(victim) << vName << " |cffabeeffde façon déshonorable à |cfffc03031 |cffabeeffreprise. Vous subissez une perte de " << FormatMoney(loss) << "g";
|
||||||
}
|
}
|
||||||
player->ModifyMoney(-int32(loss));
|
player->ModifyMoney(-int32(loss));
|
||||||
|
@ -18,7 +18,7 @@ public:
|
|||||||
|
|
||||||
void OnPlayerLogin(Player* player) override
|
void OnPlayerLogin(Player* player) override
|
||||||
{
|
{
|
||||||
ChatHandler(player->GetSession()).SendSysMessage("This server is running the |cff4CFF00MxW Level Up |rmodule.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnPlayerLevelChanged(Player * player, uint8 oldLevel)
|
void OnPlayerLevelChanged(Player * player, uint8 oldLevel)
|
||||||
|
@ -20,8 +20,7 @@ class mxwow_portalmasterAnnounce : public PlayerScript
|
|||||||
mxwow_portalmasterAnnounce() : PlayerScript("mxwow_portalmasterAnnounce") {}
|
mxwow_portalmasterAnnounce() : PlayerScript("mxwow_portalmasterAnnounce") {}
|
||||||
void OnPlayerLogin(Player* player) override
|
void OnPlayerLogin(Player* player) override
|
||||||
{
|
{
|
||||||
// Announce Module
|
|
||||||
ChatHandler(player->GetSession()).SendSysMessage("This server is running the |cff4CFF00MxW Portal Master|r module.");
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ public:
|
|||||||
|
|
||||||
void OnPlayerLogin(Player* player) override
|
void OnPlayerLogin(Player* player) override
|
||||||
{
|
{
|
||||||
ChatHandler(player->GetSession()).SendSysMessage("This server is running the |cff4CFF00MxW Servant |rmodule.");
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -47,14 +47,15 @@ public:
|
|||||||
|
|
||||||
AddGossipItemFor(player, 10, "|TInterface/Icons/inv_hammer_08:30:30:-18|t Réparer (Tout)", GOSSIP_SENDER_MAIN, 1);
|
AddGossipItemFor(player, 10, "|TInterface/Icons/inv_hammer_08:30:30:-18|t Réparer (Tout)", GOSSIP_SENDER_MAIN, 1);
|
||||||
AddGossipItemFor(player, 10, "|TInterface/Icons/inv_misc_enggizmos_18:30:30:-18|t Banque", GOSSIP_SENDER_MAIN, 2);
|
AddGossipItemFor(player, 10, "|TInterface/Icons/inv_misc_enggizmos_18:30:30:-18|t Banque", GOSSIP_SENDER_MAIN, 2);
|
||||||
AddGossipItemFor(player, 10, "|TInterface/Icons/INV_Letter_11:30:30:-18|t Courrier", GOSSIP_SENDER_MAIN, 3);
|
AddGossipItemFor(player, 10, "|TInterface/Icons/inv_misc_enggizmos_18:30:30:-18|t Banque de Guilde", GOSSIP_SENDER_MAIN, 3);
|
||||||
AddGossipItemFor(player, 10, "|TInterface/Icons/spell_shadow_teleport:30:30:-18|t Buff", GOSSIP_SENDER_MAIN, 4);
|
AddGossipItemFor(player, 10, "|TInterface/Icons/INV_Letter_11:30:30:-18|t Courrier", GOSSIP_SENDER_MAIN, 4);
|
||||||
AddGossipItemFor(player, 10, "|TInterface/Icons/Achievement_BG_AB_defendflags:30:30:-18|t Mettre fin au combat", GOSSIP_SENDER_MAIN, 5);
|
AddGossipItemFor(player, 10, "|TInterface/Icons/spell_shadow_teleport:30:30:-18|t Buff", GOSSIP_SENDER_MAIN, 5);
|
||||||
AddGossipItemFor(player, 10, "|TInterface/Icons/inv_misc_bag_11:30:30:-18|t Marchant", GOSSIP_SENDER_MAIN, 6);
|
AddGossipItemFor(player, 10, "|TInterface/Icons/Achievement_BG_AB_defendflags:30:30:-18|t Mettre fin au combat", GOSSIP_SENDER_MAIN, 6);
|
||||||
AddGossipItemFor(player, 10, "|TInterface/Icons/inv_misc_coin_17:30:30:-18|t Enchère", GOSSIP_SENDER_MAIN, 7);
|
AddGossipItemFor(player, 10, "|TInterface/Icons/inv_misc_bag_11:30:30:-18|t Marchant", GOSSIP_SENDER_MAIN, 7);
|
||||||
AddGossipItemFor(player, 10, "|TInterface/Icons/Ability_paladin_beaconoflight:30:30:-18|t Transmo", GOSSIP_SENDER_MAIN, 8);
|
AddGossipItemFor(player, 10, "|TInterface/Icons/inv_misc_coin_17:30:30:-18|t Enchère", GOSSIP_SENDER_MAIN, 8);
|
||||||
AddGossipItemFor(player, 10, "|TInterface/Icons/inv_crate_01:30:30:-18|t Matériaux", GOSSIP_SENDER_MAIN, 9);
|
AddGossipItemFor(player, 10, "|TInterface/Icons/Ability_paladin_beaconoflight:30:30:-18|t Transmo", GOSSIP_SENDER_MAIN, 9);
|
||||||
AddGossipItemFor(player, 10, "|TInterface/Icons/spell_nature_polymorph:30:30:-18|t Étable", GOSSIP_SENDER_MAIN, 10);
|
AddGossipItemFor(player, 10, "|TInterface/Icons/inv_crate_01:30:30:-18|t Matériaux", GOSSIP_SENDER_MAIN, 10);
|
||||||
|
AddGossipItemFor(player, 10, "|TInterface/Icons/spell_nature_polymorph:30:30:-18|t Étable", GOSSIP_SENDER_MAIN, 11);
|
||||||
|
|
||||||
SendGossipMenuFor(player, 20000000, creature->GetGUID());
|
SendGossipMenuFor(player, 20000000, creature->GetGUID());
|
||||||
|
|
||||||
@ -80,9 +81,14 @@ public:
|
|||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
CloseGossipMenuFor(player);
|
CloseGossipMenuFor(player);
|
||||||
player->GetSession()->SendShowMailBox(player->GetGUID());
|
SummonTempNPC(player, 9000003, 300000);
|
||||||
|
player->CastSpell(player, 31726);
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
|
CloseGossipMenuFor(player);
|
||||||
|
player->GetSession()->SendShowMailBox(player->GetGUID());
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
CloseGossipMenuFor(player);
|
CloseGossipMenuFor(player);
|
||||||
if (player->GetMap()->IsDungeon() || player->GetMap()->IsRaid()) {
|
if (player->GetMap()->IsDungeon() || player->GetMap()->IsRaid()) {
|
||||||
if (pLevel < 10)
|
if (pLevel < 10)
|
||||||
@ -173,33 +179,33 @@ public:
|
|||||||
}
|
}
|
||||||
player->CastSpell(player, 31726);
|
player->CastSpell(player, 31726);
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 6:
|
||||||
CloseGossipMenuFor(player);
|
CloseGossipMenuFor(player);
|
||||||
player->CombatStop();
|
player->CombatStop();
|
||||||
player->CastSpell(player, 31726);
|
player->CastSpell(player, 31726);
|
||||||
break;
|
break;
|
||||||
case 6:
|
case 7:
|
||||||
CloseGossipMenuFor(player);
|
CloseGossipMenuFor(player);
|
||||||
player->GetSession()->SendListInventory(creature->GetGUID());
|
player->GetSession()->SendListInventory(creature->GetGUID());
|
||||||
player->CastSpell(player, 31726);
|
player->CastSpell(player, 31726);
|
||||||
break;
|
break;
|
||||||
case 7:
|
case 8:
|
||||||
CloseGossipMenuFor(player);
|
CloseGossipMenuFor(player);
|
||||||
//SummonTempNPC(player, 8670, 300000);
|
//SummonTempNPC(player, 8670, 300000);
|
||||||
player->GetSession()->SendAuctionHello(creature->GetGUID(), creature);
|
player->GetSession()->SendAuctionHello(creature->GetGUID(), creature);
|
||||||
player->CastSpell(player, 31726);
|
player->CastSpell(player, 31726);
|
||||||
break;
|
break;
|
||||||
case 8:
|
case 9:
|
||||||
CloseGossipMenuFor(player);
|
CloseGossipMenuFor(player);
|
||||||
SummonTempNPC(player, 190010, 300000);
|
SummonTempNPC(player, 190010, 300000);
|
||||||
player->CastSpell(player, 31726);
|
player->CastSpell(player, 31726);
|
||||||
break;
|
break;
|
||||||
case 9:
|
case 10:
|
||||||
CloseGossipMenuFor(player);
|
CloseGossipMenuFor(player);
|
||||||
SummonTempNPC(player, 190011, 300000);
|
SummonTempNPC(player, 290011, 300000);
|
||||||
player->CastSpell(player, 31726);
|
player->CastSpell(player, 31726);
|
||||||
break;
|
break;
|
||||||
case 10:
|
case 11:
|
||||||
CloseGossipMenuFor(player);
|
CloseGossipMenuFor(player);
|
||||||
player->GetSession()->SendStablePet(creature->GetGUID());
|
player->GetSession()->SendStablePet(creature->GetGUID());
|
||||||
player->CastSpell(player, 31726);
|
player->CastSpell(player, 31726);
|
||||||
|
@ -19,13 +19,7 @@ class mxwow_toonmasterAnnounce : public PlayerScript
|
|||||||
mxwow_toonmasterAnnounce() : PlayerScript("mxwow_toonmasterAnnounce") {}
|
mxwow_toonmasterAnnounce() : PlayerScript("mxwow_toonmasterAnnounce") {}
|
||||||
void OnPlayerLogin(Player* player) override
|
void OnPlayerLogin(Player* player) override
|
||||||
{
|
{
|
||||||
// Announce Module
|
|
||||||
if (sConfigMgr->GetOption<bool>("MxWoW_ToonMaster.Enabled", true))
|
|
||||||
{
|
|
||||||
std::ostringstream ss;
|
|
||||||
ss << "This server is running the |cff4CFF00MxW Toon Master|r module.";
|
|
||||||
ChatHandler(player->GetSession()).SendSysMessage(ss.str().c_str());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -82,8 +76,8 @@ public:
|
|||||||
amount = amount * expMulti;
|
amount = amount * expMulti;
|
||||||
if (sConfigMgr->GetOption<bool>("MxWoW_ToonMaster.Verbose", true) && plevel < 80)
|
if (sConfigMgr->GetOption<bool>("MxWoW_ToonMaster.Verbose", true) && plevel < 80)
|
||||||
{
|
{
|
||||||
ss << "|cffabeeff[MxW][ToonMaster][%ixNiv.80] Bonus: EXPx%i";
|
ss << "|cffabeeff[MxW][ToonMaster]["<< cMPL <<"xNiv.80] Bonus: EXPx"<<expMulti;
|
||||||
ChatHandler(player->GetSession()).PSendSysMessage(ss.str().c_str(), cMPL, expMulti);
|
ChatHandler(player->GetSession()).PSendSysMessage(ss.str().c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
8
modules/mod-mxwow-webhelper/.editorconfig
Normal file
8
modules/mod-mxwow-webhelper/.editorconfig
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
tab_width = 4
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
max_line_length = 80
|
105
modules/mod-mxwow-webhelper/.gitattributes
vendored
Normal file
105
modules/mod-mxwow-webhelper/.gitattributes
vendored
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
## AUTO-DETECT
|
||||||
|
## Handle line endings automatically for files detected as
|
||||||
|
## text and leave all files detected as binary untouched.
|
||||||
|
## This will handle all files NOT defined below.
|
||||||
|
* text=auto eol=lf
|
||||||
|
|
||||||
|
# Text
|
||||||
|
*.conf text
|
||||||
|
*.conf.dist text
|
||||||
|
*.cmake text
|
||||||
|
|
||||||
|
## Scripts
|
||||||
|
*.sh text
|
||||||
|
*.fish text
|
||||||
|
*.lua text
|
||||||
|
|
||||||
|
## SQL
|
||||||
|
*.sql text
|
||||||
|
|
||||||
|
## C++
|
||||||
|
*.c text
|
||||||
|
*.cc text
|
||||||
|
*.cxx text
|
||||||
|
*.cpp text
|
||||||
|
*.c++ text
|
||||||
|
*.hpp text
|
||||||
|
*.h text
|
||||||
|
*.h++ text
|
||||||
|
*.hh text
|
||||||
|
|
||||||
|
|
||||||
|
## For documentation
|
||||||
|
|
||||||
|
# Documents
|
||||||
|
*.doc diff=astextplain
|
||||||
|
*.DOC diff=astextplain
|
||||||
|
*.docx diff=astextplain
|
||||||
|
*.DOCX diff=astextplain
|
||||||
|
*.dot diff=astextplain
|
||||||
|
*.DOT diff=astextplain
|
||||||
|
*.pdf diff=astextplain
|
||||||
|
*.PDF diff=astextplain
|
||||||
|
*.rtf diff=astextplain
|
||||||
|
*.RTF diff=astextplain
|
||||||
|
|
||||||
|
## DOCUMENTATION
|
||||||
|
*.markdown text
|
||||||
|
*.md text
|
||||||
|
*.mdwn text
|
||||||
|
*.mdown text
|
||||||
|
*.mkd text
|
||||||
|
*.mkdn text
|
||||||
|
*.mdtxt text
|
||||||
|
*.mdtext text
|
||||||
|
*.txt text
|
||||||
|
AUTHORS text
|
||||||
|
CHANGELOG text
|
||||||
|
CHANGES text
|
||||||
|
CONTRIBUTING text
|
||||||
|
COPYING text
|
||||||
|
copyright text
|
||||||
|
*COPYRIGHT* text
|
||||||
|
INSTALL text
|
||||||
|
license text
|
||||||
|
LICENSE text
|
||||||
|
NEWS text
|
||||||
|
readme text
|
||||||
|
*README* text
|
||||||
|
TODO text
|
||||||
|
|
||||||
|
## GRAPHICS
|
||||||
|
*.ai binary
|
||||||
|
*.bmp binary
|
||||||
|
*.eps binary
|
||||||
|
*.gif binary
|
||||||
|
*.ico binary
|
||||||
|
*.jng binary
|
||||||
|
*.jp2 binary
|
||||||
|
*.jpg binary
|
||||||
|
*.jpeg binary
|
||||||
|
*.jpx binary
|
||||||
|
*.jxr binary
|
||||||
|
*.pdf binary
|
||||||
|
*.png binary
|
||||||
|
*.psb binary
|
||||||
|
*.psd binary
|
||||||
|
*.svg text
|
||||||
|
*.svgz binary
|
||||||
|
*.tif binary
|
||||||
|
*.tiff binary
|
||||||
|
*.wbmp binary
|
||||||
|
*.webp binary
|
||||||
|
|
||||||
|
|
||||||
|
## ARCHIVES
|
||||||
|
*.7z binary
|
||||||
|
*.gz binary
|
||||||
|
*.jar binary
|
||||||
|
*.rar binary
|
||||||
|
*.tar binary
|
||||||
|
*.zip binary
|
||||||
|
|
||||||
|
## EXECUTABLES
|
||||||
|
*.exe binary
|
||||||
|
*.pyc binary
|
12
modules/mod-mxwow-webhelper/.github/workflows/core-build.yml
vendored
Normal file
12
modules/mod-mxwow-webhelper/.github/workflows/core-build.yml
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
name: core-build
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- 'master'
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
uses: azerothcore/reusable-workflows/.github/workflows/core_build_modules.yml@main
|
||||||
|
with:
|
||||||
|
module_repo: ${{ github.event.repository.name }}
|
48
modules/mod-mxwow-webhelper/.gitignore
vendored
Normal file
48
modules/mod-mxwow-webhelper/.gitignore
vendored
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
!.gitignore
|
||||||
|
|
||||||
|
#
|
||||||
|
#Generic
|
||||||
|
#
|
||||||
|
|
||||||
|
.directory
|
||||||
|
.mailmap
|
||||||
|
*.orig
|
||||||
|
*.rej
|
||||||
|
*.*~
|
||||||
|
.hg/
|
||||||
|
*.kdev*
|
||||||
|
.DS_Store
|
||||||
|
CMakeLists.txt.user
|
||||||
|
*.bak
|
||||||
|
*.patch
|
||||||
|
*.diff
|
||||||
|
*.REMOTE.*
|
||||||
|
*.BACKUP.*
|
||||||
|
*.BASE.*
|
||||||
|
*.LOCAL.*
|
||||||
|
|
||||||
|
#
|
||||||
|
# IDE & other softwares
|
||||||
|
#
|
||||||
|
/.settings/
|
||||||
|
/.externalToolBuilders/*
|
||||||
|
# exclude in all levels
|
||||||
|
nbproject/
|
||||||
|
.sync.ffs_db
|
||||||
|
*.kate-swp
|
||||||
|
|
||||||
|
#
|
||||||
|
# Eclipse
|
||||||
|
#
|
||||||
|
*.pydevproject
|
||||||
|
.metadata
|
||||||
|
.gradle
|
||||||
|
tmp/
|
||||||
|
*.tmp
|
||||||
|
*.swp
|
||||||
|
*~.nib
|
||||||
|
local.properties
|
||||||
|
.settings/
|
||||||
|
.loadpath
|
||||||
|
.project
|
||||||
|
.cproject
|
661
modules/mod-mxwow-webhelper/LICENSE
Normal file
661
modules/mod-mxwow-webhelper/LICENSE
Normal file
@ -0,0 +1,661 @@
|
|||||||
|
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 19 November 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The GNU Affero General Public License is a free, copyleft license for
|
||||||
|
software and other kinds of works, specifically designed to ensure
|
||||||
|
cooperation with the community in the case of network server software.
|
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed
|
||||||
|
to take away your freedom to share and change the works. By contrast,
|
||||||
|
our General Public Licenses are intended to guarantee your freedom to
|
||||||
|
share and change all versions of a program--to make sure it remains free
|
||||||
|
software for all its users.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
them if you wish), that you receive source code or can get it if you
|
||||||
|
want it, that you can change the software or use pieces of it in new
|
||||||
|
free programs, and that you know you can do these things.
|
||||||
|
|
||||||
|
Developers that use our General Public Licenses protect your rights
|
||||||
|
with two steps: (1) assert copyright on the software, and (2) offer
|
||||||
|
you this License which gives you legal permission to copy, distribute
|
||||||
|
and/or modify the software.
|
||||||
|
|
||||||
|
A secondary benefit of defending all users' freedom is that
|
||||||
|
improvements made in alternate versions of the program, if they
|
||||||
|
receive widespread use, become available for other developers to
|
||||||
|
incorporate. Many developers of free software are heartened and
|
||||||
|
encouraged by the resulting cooperation. However, in the case of
|
||||||
|
software used on network servers, this result may fail to come about.
|
||||||
|
The GNU General Public License permits making a modified version and
|
||||||
|
letting the public access it on a server without ever releasing its
|
||||||
|
source code to the public.
|
||||||
|
|
||||||
|
The GNU Affero General Public License is designed specifically to
|
||||||
|
ensure that, in such cases, the modified source code becomes available
|
||||||
|
to the community. It requires the operator of a network server to
|
||||||
|
provide the source code of the modified version running there to the
|
||||||
|
users of that server. Therefore, public use of a modified version, on
|
||||||
|
a publicly accessible server, gives the public access to the source
|
||||||
|
code of the modified version.
|
||||||
|
|
||||||
|
An older license, called the Affero General Public License and
|
||||||
|
published by Affero, was designed to accomplish similar goals. This is
|
||||||
|
a different license, not a version of the Affero GPL, but Affero has
|
||||||
|
released a new version of the Affero GPL which permits relicensing under
|
||||||
|
this license.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
|
works, such as semiconductor masks.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this
|
||||||
|
License. Each licensee is addressed as "you". "Licensees" and
|
||||||
|
"recipients" may be individuals or organizations.
|
||||||
|
|
||||||
|
To "modify" a work means to copy from or adapt all or part of the work
|
||||||
|
in a fashion requiring copyright permission, other than the making of an
|
||||||
|
exact copy. The resulting work is called a "modified version" of the
|
||||||
|
earlier work or a work "based on" the earlier work.
|
||||||
|
|
||||||
|
A "covered work" means either the unmodified Program or a work based
|
||||||
|
on the Program.
|
||||||
|
|
||||||
|
To "propagate" a work means to do anything with it that, without
|
||||||
|
permission, would make you directly or secondarily liable for
|
||||||
|
infringement under applicable copyright law, except executing it on a
|
||||||
|
computer or modifying a private copy. Propagation includes copying,
|
||||||
|
distribution (with or without modification), making available to the
|
||||||
|
public, and in some countries other activities as well.
|
||||||
|
|
||||||
|
To "convey" a work means any kind of propagation that enables other
|
||||||
|
parties to make or receive copies. Mere interaction with a user through
|
||||||
|
a computer network, with no transfer of a copy, is not conveying.
|
||||||
|
|
||||||
|
An interactive user interface displays "Appropriate Legal Notices"
|
||||||
|
to the extent that it includes a convenient and prominently visible
|
||||||
|
feature that (1) displays an appropriate copyright notice, and (2)
|
||||||
|
tells the user that there is no warranty for the work (except to the
|
||||||
|
extent that warranties are provided), that licensees may convey the
|
||||||
|
work under this License, and how to view a copy of this License. If
|
||||||
|
the interface presents a list of user commands or options, such as a
|
||||||
|
menu, a prominent item in the list meets this criterion.
|
||||||
|
|
||||||
|
1. Source Code.
|
||||||
|
|
||||||
|
The "source code" for a work means the preferred form of the work
|
||||||
|
for making modifications to it. "Object code" means any non-source
|
||||||
|
form of a work.
|
||||||
|
|
||||||
|
A "Standard Interface" means an interface that either is an official
|
||||||
|
standard defined by a recognized standards body, or, in the case of
|
||||||
|
interfaces specified for a particular programming language, one that
|
||||||
|
is widely used among developers working in that language.
|
||||||
|
|
||||||
|
The "System Libraries" of an executable work include anything, other
|
||||||
|
than the work as a whole, that (a) is included in the normal form of
|
||||||
|
packaging a Major Component, but which is not part of that Major
|
||||||
|
Component, and (b) serves only to enable use of the work with that
|
||||||
|
Major Component, or to implement a Standard Interface for which an
|
||||||
|
implementation is available to the public in source code form. A
|
||||||
|
"Major Component", in this context, means a major essential component
|
||||||
|
(kernel, window system, and so on) of the specific operating system
|
||||||
|
(if any) on which the executable work runs, or a compiler used to
|
||||||
|
produce the work, or an object code interpreter used to run it.
|
||||||
|
|
||||||
|
The "Corresponding Source" for a work in object code form means all
|
||||||
|
the source code needed to generate, install, and (for an executable
|
||||||
|
work) run the object code and to modify the work, including scripts to
|
||||||
|
control those activities. However, it does not include the work's
|
||||||
|
System Libraries, or general-purpose tools or generally available free
|
||||||
|
programs which are used unmodified in performing those activities but
|
||||||
|
which are not part of the work. For example, Corresponding Source
|
||||||
|
includes interface definition files associated with source files for
|
||||||
|
the work, and the source code for shared libraries and dynamically
|
||||||
|
linked subprograms that the work is specifically designed to require,
|
||||||
|
such as by intimate data communication or control flow between those
|
||||||
|
subprograms and other parts of the work.
|
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users
|
||||||
|
can regenerate automatically from other parts of the Corresponding
|
||||||
|
Source.
|
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that
|
||||||
|
same work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of
|
||||||
|
copyright on the Program, and are irrevocable provided the stated
|
||||||
|
conditions are met. This License explicitly affirms your unlimited
|
||||||
|
permission to run the unmodified Program. The output from running a
|
||||||
|
covered work is covered by this License only if the output, given its
|
||||||
|
content, constitutes a covered work. This License acknowledges your
|
||||||
|
rights of fair use or other equivalent, as provided by copyright law.
|
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not
|
||||||
|
convey, without conditions so long as your license otherwise remains
|
||||||
|
in force. You may convey covered works to others for the sole purpose
|
||||||
|
of having them make modifications exclusively for you, or provide you
|
||||||
|
with facilities for running those works, provided that you comply with
|
||||||
|
the terms of this License in conveying all material for which you do
|
||||||
|
not control copyright. Those thus making or running the covered works
|
||||||
|
for you must do so exclusively on your behalf, under your direction
|
||||||
|
and control, on terms that prohibit them from making any copies of
|
||||||
|
your copyrighted material outside their relationship with you.
|
||||||
|
|
||||||
|
Conveying under any other circumstances is permitted solely under
|
||||||
|
the conditions stated below. Sublicensing is not allowed; section 10
|
||||||
|
makes it unnecessary.
|
||||||
|
|
||||||
|
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
|
||||||
|
No covered work shall be deemed part of an effective technological
|
||||||
|
measure under any applicable law fulfilling obligations under article
|
||||||
|
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||||
|
similar laws prohibiting or restricting circumvention of such
|
||||||
|
measures.
|
||||||
|
|
||||||
|
When you convey a covered work, you waive any legal power to forbid
|
||||||
|
circumvention of technological measures to the extent such circumvention
|
||||||
|
is effected by exercising rights under this License with respect to
|
||||||
|
the covered work, and you disclaim any intention to limit operation or
|
||||||
|
modification of the work as a means of enforcing, against the work's
|
||||||
|
users, your or third parties' legal rights to forbid circumvention of
|
||||||
|
technological measures.
|
||||||
|
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you
|
||||||
|
receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice;
|
||||||
|
keep intact all notices stating that this License and any
|
||||||
|
non-permissive terms added in accord with section 7 apply to the code;
|
||||||
|
keep intact all notices of the absence of any warranty; and give all
|
||||||
|
recipients a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge any price or no price for each copy that you convey,
|
||||||
|
and you may offer support or warranty protection for a fee.
|
||||||
|
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
|
You may convey a work based on the Program, or the modifications to
|
||||||
|
produce it from the Program, in the form of source code under the
|
||||||
|
terms of section 4, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The work must carry prominent notices stating that you modified
|
||||||
|
it, and giving a relevant date.
|
||||||
|
|
||||||
|
b) The work must carry prominent notices stating that it is
|
||||||
|
released under this License and any conditions added under section
|
||||||
|
7. This requirement modifies the requirement in section 4 to
|
||||||
|
"keep intact all notices".
|
||||||
|
|
||||||
|
c) You must license the entire work, as a whole, under this
|
||||||
|
License to anyone who comes into possession of a copy. This
|
||||||
|
License will therefore apply, along with any applicable section 7
|
||||||
|
additional terms, to the whole of the work, and all its parts,
|
||||||
|
regardless of how they are packaged. This License gives no
|
||||||
|
permission to license the work in any other way, but it does not
|
||||||
|
invalidate such permission if you have separately received it.
|
||||||
|
|
||||||
|
d) If the work has interactive user interfaces, each must display
|
||||||
|
Appropriate Legal Notices; however, if the Program has interactive
|
||||||
|
interfaces that do not display Appropriate Legal Notices, your
|
||||||
|
work need not make them do so.
|
||||||
|
|
||||||
|
A compilation of a covered work with other separate and independent
|
||||||
|
works, which are not by their nature extensions of the covered work,
|
||||||
|
and which are not combined with it such as to form a larger program,
|
||||||
|
in or on a volume of a storage or distribution medium, is called an
|
||||||
|
"aggregate" if the compilation and its resulting copyright are not
|
||||||
|
used to limit the access or legal rights of the compilation's users
|
||||||
|
beyond what the individual works permit. Inclusion of a covered work
|
||||||
|
in an aggregate does not cause this License to apply to the other
|
||||||
|
parts of the aggregate.
|
||||||
|
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
|
You may convey a covered work in object code form under the terms
|
||||||
|
of sections 4 and 5, provided that you also convey the
|
||||||
|
machine-readable Corresponding Source under the terms of this License,
|
||||||
|
in one of these ways:
|
||||||
|
|
||||||
|
a) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by the
|
||||||
|
Corresponding Source fixed on a durable physical medium
|
||||||
|
customarily used for software interchange.
|
||||||
|
|
||||||
|
b) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by a
|
||||||
|
written offer, valid for at least three years and valid for as
|
||||||
|
long as you offer spare parts or customer support for that product
|
||||||
|
model, to give anyone who possesses the object code either (1) a
|
||||||
|
copy of the Corresponding Source for all the software in the
|
||||||
|
product that is covered by this License, on a durable physical
|
||||||
|
medium customarily used for software interchange, for a price no
|
||||||
|
more than your reasonable cost of physically performing this
|
||||||
|
conveying of source, or (2) access to copy the
|
||||||
|
Corresponding Source from a network server at no charge.
|
||||||
|
|
||||||
|
c) Convey individual copies of the object code with a copy of the
|
||||||
|
written offer to provide the Corresponding Source. This
|
||||||
|
alternative is allowed only occasionally and noncommercially, and
|
||||||
|
only if you received the object code with such an offer, in accord
|
||||||
|
with subsection 6b.
|
||||||
|
|
||||||
|
d) Convey the object code by offering access from a designated
|
||||||
|
place (gratis or for a charge), and offer equivalent access to the
|
||||||
|
Corresponding Source in the same way through the same place at no
|
||||||
|
further charge. You need not require recipients to copy the
|
||||||
|
Corresponding Source along with the object code. If the place to
|
||||||
|
copy the object code is a network server, the Corresponding Source
|
||||||
|
may be on a different server (operated by you or a third party)
|
||||||
|
that supports equivalent copying facilities, provided you maintain
|
||||||
|
clear directions next to the object code saying where to find the
|
||||||
|
Corresponding Source. Regardless of what server hosts the
|
||||||
|
Corresponding Source, you remain obligated to ensure that it is
|
||||||
|
available for as long as needed to satisfy these requirements.
|
||||||
|
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided
|
||||||
|
you inform other peers where the object code and Corresponding
|
||||||
|
Source of the work are being offered to the general public at no
|
||||||
|
charge under subsection 6d.
|
||||||
|
|
||||||
|
A separable portion of the object code, whose source code is excluded
|
||||||
|
from the Corresponding Source as a System Library, need not be
|
||||||
|
included in conveying the object code work.
|
||||||
|
|
||||||
|
A "User Product" is either (1) a "consumer product", which means any
|
||||||
|
tangible personal property which is normally used for personal, family,
|
||||||
|
or household purposes, or (2) anything designed or sold for incorporation
|
||||||
|
into a dwelling. In determining whether a product is a consumer product,
|
||||||
|
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||||
|
product received by a particular user, "normally used" refers to a
|
||||||
|
typical or common use of that class of product, regardless of the status
|
||||||
|
of the particular user or of the way in which the particular user
|
||||||
|
actually uses, or expects or is expected to use, the product. A product
|
||||||
|
is a consumer product regardless of whether the product has substantial
|
||||||
|
commercial, industrial or non-consumer uses, unless such uses represent
|
||||||
|
the only significant mode of use of the product.
|
||||||
|
|
||||||
|
"Installation Information" for a User Product means any methods,
|
||||||
|
procedures, authorization keys, or other information required to install
|
||||||
|
and execute modified versions of a covered work in that User Product from
|
||||||
|
a modified version of its Corresponding Source. The information must
|
||||||
|
suffice to ensure that the continued functioning of the modified object
|
||||||
|
code is in no case prevented or interfered with solely because
|
||||||
|
modification has been made.
|
||||||
|
|
||||||
|
If you convey an object code work under this section in, or with, or
|
||||||
|
specifically for use in, a User Product, and the conveying occurs as
|
||||||
|
part of a transaction in which the right of possession and use of the
|
||||||
|
User Product is transferred to the recipient in perpetuity or for a
|
||||||
|
fixed term (regardless of how the transaction is characterized), the
|
||||||
|
Corresponding Source conveyed under this section must be accompanied
|
||||||
|
by the Installation Information. But this requirement does not apply
|
||||||
|
if neither you nor any third party retains the ability to install
|
||||||
|
modified object code on the User Product (for example, the work has
|
||||||
|
been installed in ROM).
|
||||||
|
|
||||||
|
The requirement to provide Installation Information does not include a
|
||||||
|
requirement to continue to provide support service, warranty, or updates
|
||||||
|
for a work that has been modified or installed by the recipient, or for
|
||||||
|
the User Product in which it has been modified or installed. Access to a
|
||||||
|
network may be denied when the modification itself materially and
|
||||||
|
adversely affects the operation of the network or violates the rules and
|
||||||
|
protocols for communication across the network.
|
||||||
|
|
||||||
|
Corresponding Source conveyed, and Installation Information provided,
|
||||||
|
in accord with this section must be in a format that is publicly
|
||||||
|
documented (and with an implementation available to the public in
|
||||||
|
source code form), and must require no special password or key for
|
||||||
|
unpacking, reading or copying.
|
||||||
|
|
||||||
|
7. Additional Terms.
|
||||||
|
|
||||||
|
"Additional permissions" are terms that supplement the terms of this
|
||||||
|
License by making exceptions from one or more of its conditions.
|
||||||
|
Additional permissions that are applicable to the entire Program shall
|
||||||
|
be treated as though they were included in this License, to the extent
|
||||||
|
that they are valid under applicable law. If additional permissions
|
||||||
|
apply only to part of the Program, that part may be used separately
|
||||||
|
under those permissions, but the entire Program remains governed by
|
||||||
|
this License without regard to the additional permissions.
|
||||||
|
|
||||||
|
When you convey a copy of a covered work, you may at your option
|
||||||
|
remove any additional permissions from that copy, or from any part of
|
||||||
|
it. (Additional permissions may be written to require their own
|
||||||
|
removal in certain cases when you modify the work.) You may place
|
||||||
|
additional permissions on material, added by you to a covered work,
|
||||||
|
for which you have or can give appropriate copyright permission.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, for material you
|
||||||
|
add to a covered work, you may (if authorized by the copyright holders of
|
||||||
|
that material) supplement the terms of this License with terms:
|
||||||
|
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the
|
||||||
|
terms of sections 15 and 16 of this License; or
|
||||||
|
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or
|
||||||
|
author attributions in that material or in the Appropriate Legal
|
||||||
|
Notices displayed by works containing it; or
|
||||||
|
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or
|
||||||
|
requiring that modified versions of such material be marked in
|
||||||
|
reasonable ways as different from the original version; or
|
||||||
|
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or
|
||||||
|
authors of the material; or
|
||||||
|
|
||||||
|
e) Declining to grant rights under trademark law for use of some
|
||||||
|
trade names, trademarks, or service marks; or
|
||||||
|
|
||||||
|
f) Requiring indemnification of licensors and authors of that
|
||||||
|
material by anyone who conveys the material (or modified versions of
|
||||||
|
it) with contractual assumptions of liability to the recipient, for
|
||||||
|
any liability that these contractual assumptions directly impose on
|
||||||
|
those licensors and authors.
|
||||||
|
|
||||||
|
All other non-permissive additional terms are considered "further
|
||||||
|
restrictions" within the meaning of section 10. If the Program as you
|
||||||
|
received it, or any part of it, contains a notice stating that it is
|
||||||
|
governed by this License along with a term that is a further
|
||||||
|
restriction, you may remove that term. If a license document contains
|
||||||
|
a further restriction but permits relicensing or conveying under this
|
||||||
|
License, you may add to a covered work material governed by the terms
|
||||||
|
of that license document, provided that the further restriction does
|
||||||
|
not survive such relicensing or conveying.
|
||||||
|
|
||||||
|
If you add terms to a covered work in accord with this section, you
|
||||||
|
must place, in the relevant source files, a statement of the
|
||||||
|
additional terms that apply to those files, or a notice indicating
|
||||||
|
where to find the applicable terms.
|
||||||
|
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the
|
||||||
|
form of a separately written license, or stated as exceptions;
|
||||||
|
the above requirements apply either way.
|
||||||
|
|
||||||
|
8. Termination.
|
||||||
|
|
||||||
|
You may not propagate or modify a covered work except as expressly
|
||||||
|
provided under this License. Any attempt otherwise to propagate or
|
||||||
|
modify it is void, and will automatically terminate your rights under
|
||||||
|
this License (including any patent licenses granted under the third
|
||||||
|
paragraph of section 11).
|
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your
|
||||||
|
license from a particular copyright holder is reinstated (a)
|
||||||
|
provisionally, unless and until the copyright holder explicitly and
|
||||||
|
finally terminates your license, and (b) permanently, if the copyright
|
||||||
|
holder fails to notify you of the violation by some reasonable means
|
||||||
|
prior to 60 days after the cessation.
|
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is
|
||||||
|
reinstated permanently if the copyright holder notifies you of the
|
||||||
|
violation by some reasonable means, this is the first time you have
|
||||||
|
received notice of violation of this License (for any work) from that
|
||||||
|
copyright holder, and you cure the violation prior to 30 days after
|
||||||
|
your receipt of the notice.
|
||||||
|
|
||||||
|
Termination of your rights under this section does not terminate the
|
||||||
|
licenses of parties who have received copies or rights from you under
|
||||||
|
this License. If your rights have been terminated and not permanently
|
||||||
|
reinstated, you do not qualify to receive new licenses for the same
|
||||||
|
material under section 10.
|
||||||
|
|
||||||
|
9. Acceptance Not Required for Having Copies.
|
||||||
|
|
||||||
|
You are not required to accept this License in order to receive or
|
||||||
|
run a copy of the Program. Ancillary propagation of a covered work
|
||||||
|
occurring solely as a consequence of using peer-to-peer transmission
|
||||||
|
to receive a copy likewise does not require acceptance. However,
|
||||||
|
nothing other than this License grants you permission to propagate or
|
||||||
|
modify any covered work. These actions infringe copyright if you do
|
||||||
|
not accept this License. Therefore, by modifying or propagating a
|
||||||
|
covered work, you indicate your acceptance of this License to do so.
|
||||||
|
|
||||||
|
10. Automatic Licensing of Downstream Recipients.
|
||||||
|
|
||||||
|
Each time you convey a covered work, the recipient automatically
|
||||||
|
receives a license from the original licensors, to run, modify and
|
||||||
|
propagate that work, subject to this License. You are not responsible
|
||||||
|
for enforcing compliance by third parties with this License.
|
||||||
|
|
||||||
|
An "entity transaction" is a transaction transferring control of an
|
||||||
|
organization, or substantially all assets of one, or subdividing an
|
||||||
|
organization, or merging organizations. If propagation of a covered
|
||||||
|
work results from an entity transaction, each party to that
|
||||||
|
transaction who receives a copy of the work also receives whatever
|
||||||
|
licenses to the work the party's predecessor in interest had or could
|
||||||
|
give under the previous paragraph, plus a right to possession of the
|
||||||
|
Corresponding Source of the work from the predecessor in interest, if
|
||||||
|
the predecessor has it or can get it with reasonable efforts.
|
||||||
|
|
||||||
|
You may not impose any further restrictions on the exercise of the
|
||||||
|
rights granted or affirmed under this License. For example, you may
|
||||||
|
not impose a license fee, royalty, or other charge for exercise of
|
||||||
|
rights granted under this License, and you may not initiate litigation
|
||||||
|
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||||
|
any patent claim is infringed by making, using, selling, offering for
|
||||||
|
sale, or importing the Program or any portion of it.
|
||||||
|
|
||||||
|
11. Patents.
|
||||||
|
|
||||||
|
A "contributor" is a copyright holder who authorizes use under this
|
||||||
|
License of the Program or a work on which the Program is based. The
|
||||||
|
work thus licensed is called the contributor's "contributor version".
|
||||||
|
|
||||||
|
A contributor's "essential patent claims" are all patent claims
|
||||||
|
owned or controlled by the contributor, whether already acquired or
|
||||||
|
hereafter acquired, that would be infringed by some manner, permitted
|
||||||
|
by this License, of making, using, or selling its contributor version,
|
||||||
|
but do not include claims that would be infringed only as a
|
||||||
|
consequence of further modification of the contributor version. For
|
||||||
|
purposes of this definition, "control" includes the right to grant
|
||||||
|
patent sublicenses in a manner consistent with the requirements of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||||
|
patent license under the contributor's essential patent claims, to
|
||||||
|
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||||
|
propagate the contents of its contributor version.
|
||||||
|
|
||||||
|
In the following three paragraphs, a "patent license" is any express
|
||||||
|
agreement or commitment, however denominated, not to enforce a patent
|
||||||
|
(such as an express permission to practice a patent or covenant not to
|
||||||
|
sue for patent infringement). To "grant" such a patent license to a
|
||||||
|
party means to make such an agreement or commitment not to enforce a
|
||||||
|
patent against the party.
|
||||||
|
|
||||||
|
If you convey a covered work, knowingly relying on a patent license,
|
||||||
|
and the Corresponding Source of the work is not available for anyone
|
||||||
|
to copy, free of charge and under the terms of this License, through a
|
||||||
|
publicly available network server or other readily accessible means,
|
||||||
|
then you must either (1) cause the Corresponding Source to be so
|
||||||
|
available, or (2) arrange to deprive yourself of the benefit of the
|
||||||
|
patent license for this particular work, or (3) arrange, in a manner
|
||||||
|
consistent with the requirements of this License, to extend the patent
|
||||||
|
license to downstream recipients. "Knowingly relying" means you have
|
||||||
|
actual knowledge that, but for the patent license, your conveying the
|
||||||
|
covered work in a country, or your recipient's use of the covered work
|
||||||
|
in a country, would infringe one or more identifiable patents in that
|
||||||
|
country that you have reason to believe are valid.
|
||||||
|
|
||||||
|
If, pursuant to or in connection with a single transaction or
|
||||||
|
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||||
|
covered work, and grant a patent license to some of the parties
|
||||||
|
receiving the covered work authorizing them to use, propagate, modify
|
||||||
|
or convey a specific copy of the covered work, then the patent license
|
||||||
|
you grant is automatically extended to all recipients of the covered
|
||||||
|
work and works based on it.
|
||||||
|
|
||||||
|
A patent license is "discriminatory" if it does not include within
|
||||||
|
the scope of its coverage, prohibits the exercise of, or is
|
||||||
|
conditioned on the non-exercise of one or more of the rights that are
|
||||||
|
specifically granted under this License. You may not convey a covered
|
||||||
|
work if you are a party to an arrangement with a third party that is
|
||||||
|
in the business of distributing software, under which you make payment
|
||||||
|
to the third party based on the extent of your activity of conveying
|
||||||
|
the work, and under which the third party grants, to any of the
|
||||||
|
parties who would receive the covered work from you, a discriminatory
|
||||||
|
patent license (a) in connection with copies of the covered work
|
||||||
|
conveyed by you (or copies made from those copies), or (b) primarily
|
||||||
|
for and in connection with specific products or compilations that
|
||||||
|
contain the covered work, unless you entered into that arrangement,
|
||||||
|
or that patent license was granted, prior to 28 March 2007.
|
||||||
|
|
||||||
|
Nothing in this License shall be construed as excluding or limiting
|
||||||
|
any implied license or other defenses to infringement that may
|
||||||
|
otherwise be available to you under applicable patent law.
|
||||||
|
|
||||||
|
12. No Surrender of Others' Freedom.
|
||||||
|
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot convey a
|
||||||
|
covered work so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you may
|
||||||
|
not convey it at all. For example, if you agree to terms that obligate you
|
||||||
|
to collect a royalty for further conveying from those to whom you convey
|
||||||
|
the Program, the only way you could satisfy both those terms and this
|
||||||
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
|
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, if you modify the
|
||||||
|
Program, your modified version must prominently offer all users
|
||||||
|
interacting with it remotely through a computer network (if your version
|
||||||
|
supports such interaction) an opportunity to receive the Corresponding
|
||||||
|
Source of your version by providing access to the Corresponding Source
|
||||||
|
from a network server at no charge, through some standard or customary
|
||||||
|
means of facilitating copying of software. This Corresponding Source
|
||||||
|
shall include the Corresponding Source for any work covered by version 3
|
||||||
|
of the GNU General Public License that is incorporated pursuant to the
|
||||||
|
following paragraph.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, you have
|
||||||
|
permission to link or combine any covered work with a work licensed
|
||||||
|
under version 3 of the GNU General Public License into a single
|
||||||
|
combined work, and to convey the resulting work. The terms of this
|
||||||
|
License will continue to apply to the part which is the covered work,
|
||||||
|
but the work with which it is combined will remain governed by version
|
||||||
|
3 of the GNU General Public License.
|
||||||
|
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the GNU Affero General Public License from time to time. Such new versions
|
||||||
|
will be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Program specifies that a certain numbered version of the GNU Affero General
|
||||||
|
Public License "or any later version" applies to it, you have the
|
||||||
|
option of following the terms and conditions either of that numbered
|
||||||
|
version or of any later version published by the Free Software
|
||||||
|
Foundation. If the Program does not specify a version number of the
|
||||||
|
GNU Affero General Public License, you may choose any version ever published
|
||||||
|
by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Program specifies that a proxy can decide which future
|
||||||
|
versions of the GNU Affero General Public License can be used, that proxy's
|
||||||
|
public statement of acceptance of a version permanently authorizes you
|
||||||
|
to choose that version for the Program.
|
||||||
|
|
||||||
|
Later license versions may give you additional or different
|
||||||
|
permissions. However, no additional obligations are imposed on any
|
||||||
|
author or copyright holder as a result of your choosing to follow a
|
||||||
|
later version.
|
||||||
|
|
||||||
|
15. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||||
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||||
|
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||||
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||||
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. Limitation of Liability.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||||
|
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||||
|
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||||
|
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||||
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGES.
|
||||||
|
|
||||||
|
17. Interpretation of Sections 15 and 16.
|
||||||
|
|
||||||
|
If the disclaimer of warranty and limitation of liability provided
|
||||||
|
above cannot be given local legal effect according to their terms,
|
||||||
|
reviewing courts shall apply local law that most closely approximates
|
||||||
|
an absolute waiver of all civil liability in connection with the
|
||||||
|
Program, unless a warranty or assumption of liability accompanies a
|
||||||
|
copy of the Program in return for a fee.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
state the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published
|
||||||
|
by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If your software can interact with users remotely through a computer
|
||||||
|
network, you should also make sure that it provides a way for users to
|
||||||
|
get its source. For example, if your program is a web application, its
|
||||||
|
interface could display a "Source" link that leads users to an archive
|
||||||
|
of the code. There are many ways you could offer source, and different
|
||||||
|
solutions will be better for different programs; see section 13 for the
|
||||||
|
specific requirements.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
|
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||||
|
<http://www.gnu.org/licenses/>.
|
11
modules/mod-mxwow-webhelper/README.md
Normal file
11
modules/mod-mxwow-webhelper/README.md
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#  AzerothCore
|
||||||
|
## MxWoW - Portal Master
|
||||||
|
(https://mxgit.ovh/MxWoW/mod_mxwow_portalmaster)
|
||||||
|
|
||||||
|
|
||||||
|
This is a module for [AzerothCore](http://www.azerothcore.org)
|
||||||
|
|
||||||
|
## Dev(s)
|
||||||
|
- mikx
|
||||||
|
## Features
|
||||||
|
- Cast portals to different key location.
|
0
modules/mod-mxwow-webhelper/include.sh
Normal file
0
modules/mod-mxwow-webhelper/include.sh
Normal file
3
modules/mod-mxwow-webhelper/sql/world/world.sql
Normal file
3
modules/mod-mxwow-webhelper/sql/world/world.sql
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
DELETE FROM `item_template` WHERE (`entry` = 701001);
|
||||||
|
INSERT INTO `item_template` (`entry`, `class`, `subclass`, `SoundOverrideSubclass`, `name`, `displayid`, `Quality`, `Flags`, `FlagsExtra`, `BuyCount`, `BuyPrice`, `SellPrice`, `InventoryType`, `AllowableClass`, `AllowableRace`, `ItemLevel`, `RequiredLevel`, `RequiredSkill`, `RequiredSkillRank`, `requiredspell`, `requiredhonorrank`, `RequiredCityRank`, `RequiredReputationFaction`, `RequiredReputationRank`, `maxcount`, `stackable`, `ContainerSlots`, `StatsCount`, `stat_type1`, `stat_value1`, `stat_type2`, `stat_value2`, `stat_type3`, `stat_value3`, `stat_type4`, `stat_value4`, `stat_type5`, `stat_value5`, `stat_type6`, `stat_value6`, `stat_type7`, `stat_value7`, `stat_type8`, `stat_value8`, `stat_type9`, `stat_value9`, `stat_type10`, `stat_value10`, `ScalingStatDistribution`, `ScalingStatValue`, `dmg_min1`, `dmg_max1`, `dmg_type1`, `dmg_min2`, `dmg_max2`, `dmg_type2`, `armor`, `holy_res`, `fire_res`, `nature_res`, `frost_res`, `shadow_res`, `arcane_res`, `delay`, `ammo_type`, `RangedModRange`, `spellid_1`, `spelltrigger_1`, `spellcharges_1`, `spellppmRate_1`, `spellcooldown_1`, `spellcategory_1`, `spellcategorycooldown_1`, `spellid_2`, `spelltrigger_2`, `spellcharges_2`, `spellppmRate_2`, `spellcooldown_2`, `spellcategory_2`, `spellcategorycooldown_2`, `spellid_3`, `spelltrigger_3`, `spellcharges_3`, `spellppmRate_3`, `spellcooldown_3`, `spellcategory_3`, `spellcategorycooldown_3`, `spellid_4`, `spelltrigger_4`, `spellcharges_4`, `spellppmRate_4`, `spellcooldown_4`, `spellcategory_4`, `spellcategorycooldown_4`, `spellid_5`, `spelltrigger_5`, `spellcharges_5`, `spellppmRate_5`, `spellcooldown_5`, `spellcategory_5`, `spellcategorycooldown_5`, `bonding`, `description`, `PageText`, `LanguageID`, `PageMaterial`, `startquest`, `lockid`, `Material`, `sheath`, `RandomProperty`, `RandomSuffix`, `block`, `itemset`, `MaxDurability`, `area`, `Map`, `BagFamily`, `TotemCategory`, `socketColor_1`, `socketContent_1`, `socketColor_2`, `socketContent_2`, `socketColor_3`, `socketContent_3`, `socketBonus`, `GemProperties`, `RequiredDisenchantSkill`, `ArmorDamageModifier`, `duration`, `ItemLimitCategory`, `HolidayId`, `ScriptName`, `DisenchantID`, `FoodType`, `minMoneyLoot`, `maxMoneyLoot`, `flagsCustom`, `VerifiedBuild`) VALUES
|
||||||
|
(701001, 15, 4, -1, 'MxWoW Portal Master', 28862, 4, 64, 0, 1, 50000000, 10000000, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1000, 0, 0, 0, 0, 0, 0, -1, 0, -1, 0, 0, 0, 0, -1, 0, -1, 0, 0, 0, 0, -1, 0, -1, 0, 0, 0, 0, -1, 0, -1, 0, 0, 0, 0, -1, 0, -1, 1, 'Used to summon portals.', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 'mxwow_portalmaster', 0, 0, 0, 0, 0, 0);
|
57
modules/mod-mxwow-webhelper/src/mxwow-webhelper.cpp
Normal file
57
modules/mod-mxwow-webhelper/src/mxwow-webhelper.cpp
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
//// MxWoW Official Module
|
||||||
|
//// Web Helper
|
||||||
|
//// Dev: mikx
|
||||||
|
//// Git: https://mxgit.ovh/MxWoW/mod-mxwow-webhelper
|
||||||
|
|
||||||
|
#include "Define.h"
|
||||||
|
#include "GossipDef.h"
|
||||||
|
#include "Item.h"
|
||||||
|
#include "Player.h"
|
||||||
|
#include "ScriptedGossip.h"
|
||||||
|
#include "ScriptMgr.h"
|
||||||
|
#include "Spell.h"
|
||||||
|
#include "Configuration/Config.h"
|
||||||
|
#include "Chat.h"
|
||||||
|
|
||||||
|
class mxwow_webhelper : public PlayerScript
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
mxwow_webhelper() : PlayerScript("mxwow_webhelper") {}
|
||||||
|
|
||||||
|
void OnPlayerLogin(Player* player) override
|
||||||
|
{
|
||||||
|
QueryResult queryAccount = LoginDatabase.Query("SELECT * FROM account WHERE id = {}", player->GetSession()->GetAccountId());
|
||||||
|
if(queryAccount){
|
||||||
|
std::string userName = (*queryAccount)[1].Get<std::string>();
|
||||||
|
if(userName.find("RNDBOT") != std::string::npos){
|
||||||
|
// Is a bot
|
||||||
|
LoginDatabase.Execute("INSERT INTO mxw_web_online (aid, cid, isBot) VALUES ({}, {}, {})", player->GetSession()->GetAccountId(), player->GetGUID().GetRawValue(), 1);
|
||||||
|
} else {
|
||||||
|
// Is a real player
|
||||||
|
LoginDatabase.Execute("INSERT INTO mxw_web_online (aid, cid, isBot) VALUES ({}, {}, {})", player->GetSession()->GetAccountId(), player->GetGUID().GetRawValue(), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnPlayerLogout(Player* player) override
|
||||||
|
{
|
||||||
|
LoginDatabase.Execute("DELETE FROM mxw_web_online WHERE aid = {} AND cid = {}", player->GetSession()->GetAccountId(), player->GetGUID().GetRawValue());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class mxwow_webhelperWorld : public WorldScript
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
mxwow_webhelperWorld() : WorldScript("mxwow_webhelperWorld") {}
|
||||||
|
|
||||||
|
void OnShutdown() override
|
||||||
|
{
|
||||||
|
LoginDatabase.Execute("DELETE FROM mxw_web_online");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void AddMxWoWWebHelperScripts()
|
||||||
|
{
|
||||||
|
new mxwow_webhelper();
|
||||||
|
new mxwow_webhelperWorld();
|
||||||
|
}
|
12
modules/mod-mxwow-webhelper/src/mxwow-webhelper_loader.cpp
Normal file
12
modules/mod-mxwow-webhelper/src/mxwow-webhelper_loader.cpp
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
//// MxWoW Official Module
|
||||||
|
//// QoL Master
|
||||||
|
//// Dev: mikx
|
||||||
|
//// Git: https://mxgit.ovh/MxWoW/mod_mxwow_portalmaster
|
||||||
|
//// Credits: Based on https://github.com/azerothcore/mod-character-tools
|
||||||
|
|
||||||
|
void AddMxWoWWebHelperScripts();
|
||||||
|
|
||||||
|
void Addmod_mxwow_webhelperScripts()
|
||||||
|
{
|
||||||
|
AddMxWoWWebHelperScripts();
|
||||||
|
}
|
8
modules/mod-no-hearthstone-cooldown/.editorconfig
Normal file
8
modules/mod-no-hearthstone-cooldown/.editorconfig
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
tab_width = 4
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
max_line_length = 80
|
45
modules/mod-no-hearthstone-cooldown/.git_commit_template.txt
Normal file
45
modules/mod-no-hearthstone-cooldown/.git_commit_template.txt
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
### TITLE
|
||||||
|
## Type(Scope/Subscope): Commit ultra short explanation
|
||||||
|
## |---- Write below the examples with a maximum of 50 characters ----|
|
||||||
|
## Example 1: fix(DB/SAI): Missing spell to NPC Hogger
|
||||||
|
## Example 2: fix(CORE/Raid): Phase 2 of Ragnaros
|
||||||
|
## Example 3: feat(CORE/Commands): New GM command to do something
|
||||||
|
|
||||||
|
### DESCRIPTION
|
||||||
|
## Explain why this change is being made, what does it fix etc...
|
||||||
|
## |---- Write below the examples with a maximum of 72 characters per lines ----|
|
||||||
|
## Example: Hogger (id: 492) was not charging player when being engaged.
|
||||||
|
|
||||||
|
## Provide links to any issue, commit, pull request or other resource
|
||||||
|
## Example 1: Closes issue #23
|
||||||
|
## Example 2: Ported from other project's commit (link)
|
||||||
|
## Example 3: References taken from wowpedia / wowhead / wowwiki / https://wowgaming.altervista.org/aowow/
|
||||||
|
|
||||||
|
## =======================================================
|
||||||
|
## EXTRA INFOS
|
||||||
|
## =======================================================
|
||||||
|
## "Type" can be:
|
||||||
|
## feat (new feature)
|
||||||
|
## fix (bug fix)
|
||||||
|
## refactor (refactoring production code)
|
||||||
|
## style (formatting, missing semi colons, etc; no code change)
|
||||||
|
## docs (changes to documentation)
|
||||||
|
## test (adding or refactoring tests; no production code change)
|
||||||
|
## chore (updating bash scripts, git files etc; no production code change)
|
||||||
|
## --------------------
|
||||||
|
## Remember to
|
||||||
|
## Capitalize the subject line
|
||||||
|
## Use the imperative mood in the subject line
|
||||||
|
## Do not end the subject line with a period
|
||||||
|
## Separate subject from body with a blank line
|
||||||
|
## Use the body to explain what and why rather than how
|
||||||
|
## Can use multiple lines with "-" for bullet points in body
|
||||||
|
## --------------------
|
||||||
|
## More info here https://www.conventionalcommits.org/en/v1.0.0-beta.2/
|
||||||
|
## =======================================================
|
||||||
|
## "Scope" can be:
|
||||||
|
## CORE (core related, c++)
|
||||||
|
## DB (database related, sql)
|
||||||
|
## =======================================================
|
||||||
|
## "Subscope" is optional and depends on the nature of the commit.
|
||||||
|
## =======================================================
|
105
modules/mod-no-hearthstone-cooldown/.gitattributes
vendored
Normal file
105
modules/mod-no-hearthstone-cooldown/.gitattributes
vendored
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
## AUTO-DETECT
|
||||||
|
## Handle line endings automatically for files detected as
|
||||||
|
## text and leave all files detected as binary untouched.
|
||||||
|
## This will handle all files NOT defined below.
|
||||||
|
* text=auto eol=lf
|
||||||
|
|
||||||
|
# Text
|
||||||
|
*.conf text
|
||||||
|
*.conf.dist text
|
||||||
|
*.cmake text
|
||||||
|
|
||||||
|
## Scripts
|
||||||
|
*.sh text
|
||||||
|
*.fish text
|
||||||
|
*.lua text
|
||||||
|
|
||||||
|
## SQL
|
||||||
|
*.sql text
|
||||||
|
|
||||||
|
## C++
|
||||||
|
*.c text
|
||||||
|
*.cc text
|
||||||
|
*.cxx text
|
||||||
|
*.cpp text
|
||||||
|
*.c++ text
|
||||||
|
*.hpp text
|
||||||
|
*.h text
|
||||||
|
*.h++ text
|
||||||
|
*.hh text
|
||||||
|
|
||||||
|
|
||||||
|
## For documentation
|
||||||
|
|
||||||
|
# Documents
|
||||||
|
*.doc diff=astextplain
|
||||||
|
*.DOC diff=astextplain
|
||||||
|
*.docx diff=astextplain
|
||||||
|
*.DOCX diff=astextplain
|
||||||
|
*.dot diff=astextplain
|
||||||
|
*.DOT diff=astextplain
|
||||||
|
*.pdf diff=astextplain
|
||||||
|
*.PDF diff=astextplain
|
||||||
|
*.rtf diff=astextplain
|
||||||
|
*.RTF diff=astextplain
|
||||||
|
|
||||||
|
## DOCUMENTATION
|
||||||
|
*.markdown text
|
||||||
|
*.md text
|
||||||
|
*.mdwn text
|
||||||
|
*.mdown text
|
||||||
|
*.mkd text
|
||||||
|
*.mkdn text
|
||||||
|
*.mdtxt text
|
||||||
|
*.mdtext text
|
||||||
|
*.txt text
|
||||||
|
AUTHORS text
|
||||||
|
CHANGELOG text
|
||||||
|
CHANGES text
|
||||||
|
CONTRIBUTING text
|
||||||
|
COPYING text
|
||||||
|
copyright text
|
||||||
|
*COPYRIGHT* text
|
||||||
|
INSTALL text
|
||||||
|
license text
|
||||||
|
LICENSE text
|
||||||
|
NEWS text
|
||||||
|
readme text
|
||||||
|
*README* text
|
||||||
|
TODO text
|
||||||
|
|
||||||
|
## GRAPHICS
|
||||||
|
*.ai binary
|
||||||
|
*.bmp binary
|
||||||
|
*.eps binary
|
||||||
|
*.gif binary
|
||||||
|
*.ico binary
|
||||||
|
*.jng binary
|
||||||
|
*.jp2 binary
|
||||||
|
*.jpg binary
|
||||||
|
*.jpeg binary
|
||||||
|
*.jpx binary
|
||||||
|
*.jxr binary
|
||||||
|
*.pdf binary
|
||||||
|
*.png binary
|
||||||
|
*.psb binary
|
||||||
|
*.psd binary
|
||||||
|
*.svg text
|
||||||
|
*.svgz binary
|
||||||
|
*.tif binary
|
||||||
|
*.tiff binary
|
||||||
|
*.wbmp binary
|
||||||
|
*.webp binary
|
||||||
|
|
||||||
|
|
||||||
|
## ARCHIVES
|
||||||
|
*.7z binary
|
||||||
|
*.gz binary
|
||||||
|
*.jar binary
|
||||||
|
*.rar binary
|
||||||
|
*.tar binary
|
||||||
|
*.zip binary
|
||||||
|
|
||||||
|
## EXECUTABLES
|
||||||
|
*.exe binary
|
||||||
|
*.pyc binary
|
10
modules/mod-no-hearthstone-cooldown/.github/workflows/core-build.yml
vendored
Normal file
10
modules/mod-no-hearthstone-cooldown/.github/workflows/core-build.yml
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
name: core-build
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
uses: azerothcore/reusable-workflows/.github/workflows/core_build_modules.yml@main
|
||||||
|
with:
|
||||||
|
module_repo: ${{ github.event.repository.name }}
|
48
modules/mod-no-hearthstone-cooldown/.gitignore
vendored
Normal file
48
modules/mod-no-hearthstone-cooldown/.gitignore
vendored
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
!.gitignore
|
||||||
|
|
||||||
|
#
|
||||||
|
#Generic
|
||||||
|
#
|
||||||
|
|
||||||
|
.directory
|
||||||
|
.mailmap
|
||||||
|
*.orig
|
||||||
|
*.rej
|
||||||
|
*.*~
|
||||||
|
.hg/
|
||||||
|
*.kdev*
|
||||||
|
.DS_Store
|
||||||
|
CMakeLists.txt.user
|
||||||
|
*.bak
|
||||||
|
*.patch
|
||||||
|
*.diff
|
||||||
|
*.REMOTE.*
|
||||||
|
*.BACKUP.*
|
||||||
|
*.BASE.*
|
||||||
|
*.LOCAL.*
|
||||||
|
|
||||||
|
#
|
||||||
|
# IDE & other softwares
|
||||||
|
#
|
||||||
|
/.settings/
|
||||||
|
/.externalToolBuilders/*
|
||||||
|
# exclude in all levels
|
||||||
|
nbproject/
|
||||||
|
.sync.ffs_db
|
||||||
|
*.kate-swp
|
||||||
|
|
||||||
|
#
|
||||||
|
# Eclipse
|
||||||
|
#
|
||||||
|
*.pydevproject
|
||||||
|
.metadata
|
||||||
|
.gradle
|
||||||
|
tmp/
|
||||||
|
*.tmp
|
||||||
|
*.swp
|
||||||
|
*~.nib
|
||||||
|
local.properties
|
||||||
|
.settings/
|
||||||
|
.loadpath
|
||||||
|
.project
|
||||||
|
.cproject
|
661
modules/mod-no-hearthstone-cooldown/LICENSE
Normal file
661
modules/mod-no-hearthstone-cooldown/LICENSE
Normal file
@ -0,0 +1,661 @@
|
|||||||
|
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 19 November 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The GNU Affero General Public License is a free, copyleft license for
|
||||||
|
software and other kinds of works, specifically designed to ensure
|
||||||
|
cooperation with the community in the case of network server software.
|
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed
|
||||||
|
to take away your freedom to share and change the works. By contrast,
|
||||||
|
our General Public Licenses are intended to guarantee your freedom to
|
||||||
|
share and change all versions of a program--to make sure it remains free
|
||||||
|
software for all its users.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
them if you wish), that you receive source code or can get it if you
|
||||||
|
want it, that you can change the software or use pieces of it in new
|
||||||
|
free programs, and that you know you can do these things.
|
||||||
|
|
||||||
|
Developers that use our General Public Licenses protect your rights
|
||||||
|
with two steps: (1) assert copyright on the software, and (2) offer
|
||||||
|
you this License which gives you legal permission to copy, distribute
|
||||||
|
and/or modify the software.
|
||||||
|
|
||||||
|
A secondary benefit of defending all users' freedom is that
|
||||||
|
improvements made in alternate versions of the program, if they
|
||||||
|
receive widespread use, become available for other developers to
|
||||||
|
incorporate. Many developers of free software are heartened and
|
||||||
|
encouraged by the resulting cooperation. However, in the case of
|
||||||
|
software used on network servers, this result may fail to come about.
|
||||||
|
The GNU General Public License permits making a modified version and
|
||||||
|
letting the public access it on a server without ever releasing its
|
||||||
|
source code to the public.
|
||||||
|
|
||||||
|
The GNU Affero General Public License is designed specifically to
|
||||||
|
ensure that, in such cases, the modified source code becomes available
|
||||||
|
to the community. It requires the operator of a network server to
|
||||||
|
provide the source code of the modified version running there to the
|
||||||
|
users of that server. Therefore, public use of a modified version, on
|
||||||
|
a publicly accessible server, gives the public access to the source
|
||||||
|
code of the modified version.
|
||||||
|
|
||||||
|
An older license, called the Affero General Public License and
|
||||||
|
published by Affero, was designed to accomplish similar goals. This is
|
||||||
|
a different license, not a version of the Affero GPL, but Affero has
|
||||||
|
released a new version of the Affero GPL which permits relicensing under
|
||||||
|
this license.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
|
works, such as semiconductor masks.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this
|
||||||
|
License. Each licensee is addressed as "you". "Licensees" and
|
||||||
|
"recipients" may be individuals or organizations.
|
||||||
|
|
||||||
|
To "modify" a work means to copy from or adapt all or part of the work
|
||||||
|
in a fashion requiring copyright permission, other than the making of an
|
||||||
|
exact copy. The resulting work is called a "modified version" of the
|
||||||
|
earlier work or a work "based on" the earlier work.
|
||||||
|
|
||||||
|
A "covered work" means either the unmodified Program or a work based
|
||||||
|
on the Program.
|
||||||
|
|
||||||
|
To "propagate" a work means to do anything with it that, without
|
||||||
|
permission, would make you directly or secondarily liable for
|
||||||
|
infringement under applicable copyright law, except executing it on a
|
||||||
|
computer or modifying a private copy. Propagation includes copying,
|
||||||
|
distribution (with or without modification), making available to the
|
||||||
|
public, and in some countries other activities as well.
|
||||||
|
|
||||||
|
To "convey" a work means any kind of propagation that enables other
|
||||||
|
parties to make or receive copies. Mere interaction with a user through
|
||||||
|
a computer network, with no transfer of a copy, is not conveying.
|
||||||
|
|
||||||
|
An interactive user interface displays "Appropriate Legal Notices"
|
||||||
|
to the extent that it includes a convenient and prominently visible
|
||||||
|
feature that (1) displays an appropriate copyright notice, and (2)
|
||||||
|
tells the user that there is no warranty for the work (except to the
|
||||||
|
extent that warranties are provided), that licensees may convey the
|
||||||
|
work under this License, and how to view a copy of this License. If
|
||||||
|
the interface presents a list of user commands or options, such as a
|
||||||
|
menu, a prominent item in the list meets this criterion.
|
||||||
|
|
||||||
|
1. Source Code.
|
||||||
|
|
||||||
|
The "source code" for a work means the preferred form of the work
|
||||||
|
for making modifications to it. "Object code" means any non-source
|
||||||
|
form of a work.
|
||||||
|
|
||||||
|
A "Standard Interface" means an interface that either is an official
|
||||||
|
standard defined by a recognized standards body, or, in the case of
|
||||||
|
interfaces specified for a particular programming language, one that
|
||||||
|
is widely used among developers working in that language.
|
||||||
|
|
||||||
|
The "System Libraries" of an executable work include anything, other
|
||||||
|
than the work as a whole, that (a) is included in the normal form of
|
||||||
|
packaging a Major Component, but which is not part of that Major
|
||||||
|
Component, and (b) serves only to enable use of the work with that
|
||||||
|
Major Component, or to implement a Standard Interface for which an
|
||||||
|
implementation is available to the public in source code form. A
|
||||||
|
"Major Component", in this context, means a major essential component
|
||||||
|
(kernel, window system, and so on) of the specific operating system
|
||||||
|
(if any) on which the executable work runs, or a compiler used to
|
||||||
|
produce the work, or an object code interpreter used to run it.
|
||||||
|
|
||||||
|
The "Corresponding Source" for a work in object code form means all
|
||||||
|
the source code needed to generate, install, and (for an executable
|
||||||
|
work) run the object code and to modify the work, including scripts to
|
||||||
|
control those activities. However, it does not include the work's
|
||||||
|
System Libraries, or general-purpose tools or generally available free
|
||||||
|
programs which are used unmodified in performing those activities but
|
||||||
|
which are not part of the work. For example, Corresponding Source
|
||||||
|
includes interface definition files associated with source files for
|
||||||
|
the work, and the source code for shared libraries and dynamically
|
||||||
|
linked subprograms that the work is specifically designed to require,
|
||||||
|
such as by intimate data communication or control flow between those
|
||||||
|
subprograms and other parts of the work.
|
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users
|
||||||
|
can regenerate automatically from other parts of the Corresponding
|
||||||
|
Source.
|
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that
|
||||||
|
same work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of
|
||||||
|
copyright on the Program, and are irrevocable provided the stated
|
||||||
|
conditions are met. This License explicitly affirms your unlimited
|
||||||
|
permission to run the unmodified Program. The output from running a
|
||||||
|
covered work is covered by this License only if the output, given its
|
||||||
|
content, constitutes a covered work. This License acknowledges your
|
||||||
|
rights of fair use or other equivalent, as provided by copyright law.
|
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not
|
||||||
|
convey, without conditions so long as your license otherwise remains
|
||||||
|
in force. You may convey covered works to others for the sole purpose
|
||||||
|
of having them make modifications exclusively for you, or provide you
|
||||||
|
with facilities for running those works, provided that you comply with
|
||||||
|
the terms of this License in conveying all material for which you do
|
||||||
|
not control copyright. Those thus making or running the covered works
|
||||||
|
for you must do so exclusively on your behalf, under your direction
|
||||||
|
and control, on terms that prohibit them from making any copies of
|
||||||
|
your copyrighted material outside their relationship with you.
|
||||||
|
|
||||||
|
Conveying under any other circumstances is permitted solely under
|
||||||
|
the conditions stated below. Sublicensing is not allowed; section 10
|
||||||
|
makes it unnecessary.
|
||||||
|
|
||||||
|
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
|
||||||
|
No covered work shall be deemed part of an effective technological
|
||||||
|
measure under any applicable law fulfilling obligations under article
|
||||||
|
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||||
|
similar laws prohibiting or restricting circumvention of such
|
||||||
|
measures.
|
||||||
|
|
||||||
|
When you convey a covered work, you waive any legal power to forbid
|
||||||
|
circumvention of technological measures to the extent such circumvention
|
||||||
|
is effected by exercising rights under this License with respect to
|
||||||
|
the covered work, and you disclaim any intention to limit operation or
|
||||||
|
modification of the work as a means of enforcing, against the work's
|
||||||
|
users, your or third parties' legal rights to forbid circumvention of
|
||||||
|
technological measures.
|
||||||
|
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you
|
||||||
|
receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice;
|
||||||
|
keep intact all notices stating that this License and any
|
||||||
|
non-permissive terms added in accord with section 7 apply to the code;
|
||||||
|
keep intact all notices of the absence of any warranty; and give all
|
||||||
|
recipients a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge any price or no price for each copy that you convey,
|
||||||
|
and you may offer support or warranty protection for a fee.
|
||||||
|
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
|
You may convey a work based on the Program, or the modifications to
|
||||||
|
produce it from the Program, in the form of source code under the
|
||||||
|
terms of section 4, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The work must carry prominent notices stating that you modified
|
||||||
|
it, and giving a relevant date.
|
||||||
|
|
||||||
|
b) The work must carry prominent notices stating that it is
|
||||||
|
released under this License and any conditions added under section
|
||||||
|
7. This requirement modifies the requirement in section 4 to
|
||||||
|
"keep intact all notices".
|
||||||
|
|
||||||
|
c) You must license the entire work, as a whole, under this
|
||||||
|
License to anyone who comes into possession of a copy. This
|
||||||
|
License will therefore apply, along with any applicable section 7
|
||||||
|
additional terms, to the whole of the work, and all its parts,
|
||||||
|
regardless of how they are packaged. This License gives no
|
||||||
|
permission to license the work in any other way, but it does not
|
||||||
|
invalidate such permission if you have separately received it.
|
||||||
|
|
||||||
|
d) If the work has interactive user interfaces, each must display
|
||||||
|
Appropriate Legal Notices; however, if the Program has interactive
|
||||||
|
interfaces that do not display Appropriate Legal Notices, your
|
||||||
|
work need not make them do so.
|
||||||
|
|
||||||
|
A compilation of a covered work with other separate and independent
|
||||||
|
works, which are not by their nature extensions of the covered work,
|
||||||
|
and which are not combined with it such as to form a larger program,
|
||||||
|
in or on a volume of a storage or distribution medium, is called an
|
||||||
|
"aggregate" if the compilation and its resulting copyright are not
|
||||||
|
used to limit the access or legal rights of the compilation's users
|
||||||
|
beyond what the individual works permit. Inclusion of a covered work
|
||||||
|
in an aggregate does not cause this License to apply to the other
|
||||||
|
parts of the aggregate.
|
||||||
|
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
|
You may convey a covered work in object code form under the terms
|
||||||
|
of sections 4 and 5, provided that you also convey the
|
||||||
|
machine-readable Corresponding Source under the terms of this License,
|
||||||
|
in one of these ways:
|
||||||
|
|
||||||
|
a) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by the
|
||||||
|
Corresponding Source fixed on a durable physical medium
|
||||||
|
customarily used for software interchange.
|
||||||
|
|
||||||
|
b) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by a
|
||||||
|
written offer, valid for at least three years and valid for as
|
||||||
|
long as you offer spare parts or customer support for that product
|
||||||
|
model, to give anyone who possesses the object code either (1) a
|
||||||
|
copy of the Corresponding Source for all the software in the
|
||||||
|
product that is covered by this License, on a durable physical
|
||||||
|
medium customarily used for software interchange, for a price no
|
||||||
|
more than your reasonable cost of physically performing this
|
||||||
|
conveying of source, or (2) access to copy the
|
||||||
|
Corresponding Source from a network server at no charge.
|
||||||
|
|
||||||
|
c) Convey individual copies of the object code with a copy of the
|
||||||
|
written offer to provide the Corresponding Source. This
|
||||||
|
alternative is allowed only occasionally and noncommercially, and
|
||||||
|
only if you received the object code with such an offer, in accord
|
||||||
|
with subsection 6b.
|
||||||
|
|
||||||
|
d) Convey the object code by offering access from a designated
|
||||||
|
place (gratis or for a charge), and offer equivalent access to the
|
||||||
|
Corresponding Source in the same way through the same place at no
|
||||||
|
further charge. You need not require recipients to copy the
|
||||||
|
Corresponding Source along with the object code. If the place to
|
||||||
|
copy the object code is a network server, the Corresponding Source
|
||||||
|
may be on a different server (operated by you or a third party)
|
||||||
|
that supports equivalent copying facilities, provided you maintain
|
||||||
|
clear directions next to the object code saying where to find the
|
||||||
|
Corresponding Source. Regardless of what server hosts the
|
||||||
|
Corresponding Source, you remain obligated to ensure that it is
|
||||||
|
available for as long as needed to satisfy these requirements.
|
||||||
|
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided
|
||||||
|
you inform other peers where the object code and Corresponding
|
||||||
|
Source of the work are being offered to the general public at no
|
||||||
|
charge under subsection 6d.
|
||||||
|
|
||||||
|
A separable portion of the object code, whose source code is excluded
|
||||||
|
from the Corresponding Source as a System Library, need not be
|
||||||
|
included in conveying the object code work.
|
||||||
|
|
||||||
|
A "User Product" is either (1) a "consumer product", which means any
|
||||||
|
tangible personal property which is normally used for personal, family,
|
||||||
|
or household purposes, or (2) anything designed or sold for incorporation
|
||||||
|
into a dwelling. In determining whether a product is a consumer product,
|
||||||
|
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||||
|
product received by a particular user, "normally used" refers to a
|
||||||
|
typical or common use of that class of product, regardless of the status
|
||||||
|
of the particular user or of the way in which the particular user
|
||||||
|
actually uses, or expects or is expected to use, the product. A product
|
||||||
|
is a consumer product regardless of whether the product has substantial
|
||||||
|
commercial, industrial or non-consumer uses, unless such uses represent
|
||||||
|
the only significant mode of use of the product.
|
||||||
|
|
||||||
|
"Installation Information" for a User Product means any methods,
|
||||||
|
procedures, authorization keys, or other information required to install
|
||||||
|
and execute modified versions of a covered work in that User Product from
|
||||||
|
a modified version of its Corresponding Source. The information must
|
||||||
|
suffice to ensure that the continued functioning of the modified object
|
||||||
|
code is in no case prevented or interfered with solely because
|
||||||
|
modification has been made.
|
||||||
|
|
||||||
|
If you convey an object code work under this section in, or with, or
|
||||||
|
specifically for use in, a User Product, and the conveying occurs as
|
||||||
|
part of a transaction in which the right of possession and use of the
|
||||||
|
User Product is transferred to the recipient in perpetuity or for a
|
||||||
|
fixed term (regardless of how the transaction is characterized), the
|
||||||
|
Corresponding Source conveyed under this section must be accompanied
|
||||||
|
by the Installation Information. But this requirement does not apply
|
||||||
|
if neither you nor any third party retains the ability to install
|
||||||
|
modified object code on the User Product (for example, the work has
|
||||||
|
been installed in ROM).
|
||||||
|
|
||||||
|
The requirement to provide Installation Information does not include a
|
||||||
|
requirement to continue to provide support service, warranty, or updates
|
||||||
|
for a work that has been modified or installed by the recipient, or for
|
||||||
|
the User Product in which it has been modified or installed. Access to a
|
||||||
|
network may be denied when the modification itself materially and
|
||||||
|
adversely affects the operation of the network or violates the rules and
|
||||||
|
protocols for communication across the network.
|
||||||
|
|
||||||
|
Corresponding Source conveyed, and Installation Information provided,
|
||||||
|
in accord with this section must be in a format that is publicly
|
||||||
|
documented (and with an implementation available to the public in
|
||||||
|
source code form), and must require no special password or key for
|
||||||
|
unpacking, reading or copying.
|
||||||
|
|
||||||
|
7. Additional Terms.
|
||||||
|
|
||||||
|
"Additional permissions" are terms that supplement the terms of this
|
||||||
|
License by making exceptions from one or more of its conditions.
|
||||||
|
Additional permissions that are applicable to the entire Program shall
|
||||||
|
be treated as though they were included in this License, to the extent
|
||||||
|
that they are valid under applicable law. If additional permissions
|
||||||
|
apply only to part of the Program, that part may be used separately
|
||||||
|
under those permissions, but the entire Program remains governed by
|
||||||
|
this License without regard to the additional permissions.
|
||||||
|
|
||||||
|
When you convey a copy of a covered work, you may at your option
|
||||||
|
remove any additional permissions from that copy, or from any part of
|
||||||
|
it. (Additional permissions may be written to require their own
|
||||||
|
removal in certain cases when you modify the work.) You may place
|
||||||
|
additional permissions on material, added by you to a covered work,
|
||||||
|
for which you have or can give appropriate copyright permission.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, for material you
|
||||||
|
add to a covered work, you may (if authorized by the copyright holders of
|
||||||
|
that material) supplement the terms of this License with terms:
|
||||||
|
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the
|
||||||
|
terms of sections 15 and 16 of this License; or
|
||||||
|
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or
|
||||||
|
author attributions in that material or in the Appropriate Legal
|
||||||
|
Notices displayed by works containing it; or
|
||||||
|
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or
|
||||||
|
requiring that modified versions of such material be marked in
|
||||||
|
reasonable ways as different from the original version; or
|
||||||
|
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or
|
||||||
|
authors of the material; or
|
||||||
|
|
||||||
|
e) Declining to grant rights under trademark law for use of some
|
||||||
|
trade names, trademarks, or service marks; or
|
||||||
|
|
||||||
|
f) Requiring indemnification of licensors and authors of that
|
||||||
|
material by anyone who conveys the material (or modified versions of
|
||||||
|
it) with contractual assumptions of liability to the recipient, for
|
||||||
|
any liability that these contractual assumptions directly impose on
|
||||||
|
those licensors and authors.
|
||||||
|
|
||||||
|
All other non-permissive additional terms are considered "further
|
||||||
|
restrictions" within the meaning of section 10. If the Program as you
|
||||||
|
received it, or any part of it, contains a notice stating that it is
|
||||||
|
governed by this License along with a term that is a further
|
||||||
|
restriction, you may remove that term. If a license document contains
|
||||||
|
a further restriction but permits relicensing or conveying under this
|
||||||
|
License, you may add to a covered work material governed by the terms
|
||||||
|
of that license document, provided that the further restriction does
|
||||||
|
not survive such relicensing or conveying.
|
||||||
|
|
||||||
|
If you add terms to a covered work in accord with this section, you
|
||||||
|
must place, in the relevant source files, a statement of the
|
||||||
|
additional terms that apply to those files, or a notice indicating
|
||||||
|
where to find the applicable terms.
|
||||||
|
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the
|
||||||
|
form of a separately written license, or stated as exceptions;
|
||||||
|
the above requirements apply either way.
|
||||||
|
|
||||||
|
8. Termination.
|
||||||
|
|
||||||
|
You may not propagate or modify a covered work except as expressly
|
||||||
|
provided under this License. Any attempt otherwise to propagate or
|
||||||
|
modify it is void, and will automatically terminate your rights under
|
||||||
|
this License (including any patent licenses granted under the third
|
||||||
|
paragraph of section 11).
|
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your
|
||||||
|
license from a particular copyright holder is reinstated (a)
|
||||||
|
provisionally, unless and until the copyright holder explicitly and
|
||||||
|
finally terminates your license, and (b) permanently, if the copyright
|
||||||
|
holder fails to notify you of the violation by some reasonable means
|
||||||
|
prior to 60 days after the cessation.
|
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is
|
||||||
|
reinstated permanently if the copyright holder notifies you of the
|
||||||
|
violation by some reasonable means, this is the first time you have
|
||||||
|
received notice of violation of this License (for any work) from that
|
||||||
|
copyright holder, and you cure the violation prior to 30 days after
|
||||||
|
your receipt of the notice.
|
||||||
|
|
||||||
|
Termination of your rights under this section does not terminate the
|
||||||
|
licenses of parties who have received copies or rights from you under
|
||||||
|
this License. If your rights have been terminated and not permanently
|
||||||
|
reinstated, you do not qualify to receive new licenses for the same
|
||||||
|
material under section 10.
|
||||||
|
|
||||||
|
9. Acceptance Not Required for Having Copies.
|
||||||
|
|
||||||
|
You are not required to accept this License in order to receive or
|
||||||
|
run a copy of the Program. Ancillary propagation of a covered work
|
||||||
|
occurring solely as a consequence of using peer-to-peer transmission
|
||||||
|
to receive a copy likewise does not require acceptance. However,
|
||||||
|
nothing other than this License grants you permission to propagate or
|
||||||
|
modify any covered work. These actions infringe copyright if you do
|
||||||
|
not accept this License. Therefore, by modifying or propagating a
|
||||||
|
covered work, you indicate your acceptance of this License to do so.
|
||||||
|
|
||||||
|
10. Automatic Licensing of Downstream Recipients.
|
||||||
|
|
||||||
|
Each time you convey a covered work, the recipient automatically
|
||||||
|
receives a license from the original licensors, to run, modify and
|
||||||
|
propagate that work, subject to this License. You are not responsible
|
||||||
|
for enforcing compliance by third parties with this License.
|
||||||
|
|
||||||
|
An "entity transaction" is a transaction transferring control of an
|
||||||
|
organization, or substantially all assets of one, or subdividing an
|
||||||
|
organization, or merging organizations. If propagation of a covered
|
||||||
|
work results from an entity transaction, each party to that
|
||||||
|
transaction who receives a copy of the work also receives whatever
|
||||||
|
licenses to the work the party's predecessor in interest had or could
|
||||||
|
give under the previous paragraph, plus a right to possession of the
|
||||||
|
Corresponding Source of the work from the predecessor in interest, if
|
||||||
|
the predecessor has it or can get it with reasonable efforts.
|
||||||
|
|
||||||
|
You may not impose any further restrictions on the exercise of the
|
||||||
|
rights granted or affirmed under this License. For example, you may
|
||||||
|
not impose a license fee, royalty, or other charge for exercise of
|
||||||
|
rights granted under this License, and you may not initiate litigation
|
||||||
|
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||||
|
any patent claim is infringed by making, using, selling, offering for
|
||||||
|
sale, or importing the Program or any portion of it.
|
||||||
|
|
||||||
|
11. Patents.
|
||||||
|
|
||||||
|
A "contributor" is a copyright holder who authorizes use under this
|
||||||
|
License of the Program or a work on which the Program is based. The
|
||||||
|
work thus licensed is called the contributor's "contributor version".
|
||||||
|
|
||||||
|
A contributor's "essential patent claims" are all patent claims
|
||||||
|
owned or controlled by the contributor, whether already acquired or
|
||||||
|
hereafter acquired, that would be infringed by some manner, permitted
|
||||||
|
by this License, of making, using, or selling its contributor version,
|
||||||
|
but do not include claims that would be infringed only as a
|
||||||
|
consequence of further modification of the contributor version. For
|
||||||
|
purposes of this definition, "control" includes the right to grant
|
||||||
|
patent sublicenses in a manner consistent with the requirements of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||||
|
patent license under the contributor's essential patent claims, to
|
||||||
|
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||||
|
propagate the contents of its contributor version.
|
||||||
|
|
||||||
|
In the following three paragraphs, a "patent license" is any express
|
||||||
|
agreement or commitment, however denominated, not to enforce a patent
|
||||||
|
(such as an express permission to practice a patent or covenant not to
|
||||||
|
sue for patent infringement). To "grant" such a patent license to a
|
||||||
|
party means to make such an agreement or commitment not to enforce a
|
||||||
|
patent against the party.
|
||||||
|
|
||||||
|
If you convey a covered work, knowingly relying on a patent license,
|
||||||
|
and the Corresponding Source of the work is not available for anyone
|
||||||
|
to copy, free of charge and under the terms of this License, through a
|
||||||
|
publicly available network server or other readily accessible means,
|
||||||
|
then you must either (1) cause the Corresponding Source to be so
|
||||||
|
available, or (2) arrange to deprive yourself of the benefit of the
|
||||||
|
patent license for this particular work, or (3) arrange, in a manner
|
||||||
|
consistent with the requirements of this License, to extend the patent
|
||||||
|
license to downstream recipients. "Knowingly relying" means you have
|
||||||
|
actual knowledge that, but for the patent license, your conveying the
|
||||||
|
covered work in a country, or your recipient's use of the covered work
|
||||||
|
in a country, would infringe one or more identifiable patents in that
|
||||||
|
country that you have reason to believe are valid.
|
||||||
|
|
||||||
|
If, pursuant to or in connection with a single transaction or
|
||||||
|
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||||
|
covered work, and grant a patent license to some of the parties
|
||||||
|
receiving the covered work authorizing them to use, propagate, modify
|
||||||
|
or convey a specific copy of the covered work, then the patent license
|
||||||
|
you grant is automatically extended to all recipients of the covered
|
||||||
|
work and works based on it.
|
||||||
|
|
||||||
|
A patent license is "discriminatory" if it does not include within
|
||||||
|
the scope of its coverage, prohibits the exercise of, or is
|
||||||
|
conditioned on the non-exercise of one or more of the rights that are
|
||||||
|
specifically granted under this License. You may not convey a covered
|
||||||
|
work if you are a party to an arrangement with a third party that is
|
||||||
|
in the business of distributing software, under which you make payment
|
||||||
|
to the third party based on the extent of your activity of conveying
|
||||||
|
the work, and under which the third party grants, to any of the
|
||||||
|
parties who would receive the covered work from you, a discriminatory
|
||||||
|
patent license (a) in connection with copies of the covered work
|
||||||
|
conveyed by you (or copies made from those copies), or (b) primarily
|
||||||
|
for and in connection with specific products or compilations that
|
||||||
|
contain the covered work, unless you entered into that arrangement,
|
||||||
|
or that patent license was granted, prior to 28 March 2007.
|
||||||
|
|
||||||
|
Nothing in this License shall be construed as excluding or limiting
|
||||||
|
any implied license or other defenses to infringement that may
|
||||||
|
otherwise be available to you under applicable patent law.
|
||||||
|
|
||||||
|
12. No Surrender of Others' Freedom.
|
||||||
|
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot convey a
|
||||||
|
covered work so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you may
|
||||||
|
not convey it at all. For example, if you agree to terms that obligate you
|
||||||
|
to collect a royalty for further conveying from those to whom you convey
|
||||||
|
the Program, the only way you could satisfy both those terms and this
|
||||||
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
|
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, if you modify the
|
||||||
|
Program, your modified version must prominently offer all users
|
||||||
|
interacting with it remotely through a computer network (if your version
|
||||||
|
supports such interaction) an opportunity to receive the Corresponding
|
||||||
|
Source of your version by providing access to the Corresponding Source
|
||||||
|
from a network server at no charge, through some standard or customary
|
||||||
|
means of facilitating copying of software. This Corresponding Source
|
||||||
|
shall include the Corresponding Source for any work covered by version 3
|
||||||
|
of the GNU General Public License that is incorporated pursuant to the
|
||||||
|
following paragraph.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, you have
|
||||||
|
permission to link or combine any covered work with a work licensed
|
||||||
|
under version 3 of the GNU General Public License into a single
|
||||||
|
combined work, and to convey the resulting work. The terms of this
|
||||||
|
License will continue to apply to the part which is the covered work,
|
||||||
|
but the work with which it is combined will remain governed by version
|
||||||
|
3 of the GNU General Public License.
|
||||||
|
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the GNU Affero General Public License from time to time. Such new versions
|
||||||
|
will be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Program specifies that a certain numbered version of the GNU Affero General
|
||||||
|
Public License "or any later version" applies to it, you have the
|
||||||
|
option of following the terms and conditions either of that numbered
|
||||||
|
version or of any later version published by the Free Software
|
||||||
|
Foundation. If the Program does not specify a version number of the
|
||||||
|
GNU Affero General Public License, you may choose any version ever published
|
||||||
|
by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Program specifies that a proxy can decide which future
|
||||||
|
versions of the GNU Affero General Public License can be used, that proxy's
|
||||||
|
public statement of acceptance of a version permanently authorizes you
|
||||||
|
to choose that version for the Program.
|
||||||
|
|
||||||
|
Later license versions may give you additional or different
|
||||||
|
permissions. However, no additional obligations are imposed on any
|
||||||
|
author or copyright holder as a result of your choosing to follow a
|
||||||
|
later version.
|
||||||
|
|
||||||
|
15. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||||
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||||
|
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||||
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||||
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. Limitation of Liability.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||||
|
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||||
|
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||||
|
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||||
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGES.
|
||||||
|
|
||||||
|
17. Interpretation of Sections 15 and 16.
|
||||||
|
|
||||||
|
If the disclaimer of warranty and limitation of liability provided
|
||||||
|
above cannot be given local legal effect according to their terms,
|
||||||
|
reviewing courts shall apply local law that most closely approximates
|
||||||
|
an absolute waiver of all civil liability in connection with the
|
||||||
|
Program, unless a warranty or assumption of liability accompanies a
|
||||||
|
copy of the Program in return for a fee.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
state the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published
|
||||||
|
by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If your software can interact with users remotely through a computer
|
||||||
|
network, you should also make sure that it provides a way for users to
|
||||||
|
get its source. For example, if your program is a web application, its
|
||||||
|
interface could display a "Source" link that leads users to an archive
|
||||||
|
of the code. There are many ways you could offer source, and different
|
||||||
|
solutions will be better for different programs; see section 13 for the
|
||||||
|
specific requirements.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
|
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||||
|
<https://www.gnu.org/licenses/>.
|
44
modules/mod-no-hearthstone-cooldown/README.md
Normal file
44
modules/mod-no-hearthstone-cooldown/README.md
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#  AzerothCore
|
||||||
|
## mod-no-hearthstone-cooldown
|
||||||
|
### This is a module for [AzerothCore](http://www.azerothcore.org)
|
||||||
|
|
||||||
|
# Module info
|
||||||
|
|
||||||
|
- Name: No Hearthstone cooldown
|
||||||
|
- Author: BytesGalore
|
||||||
|
- Module:
|
||||||
|
+ Repository: https://github.com/BytesGalore/mod-no-hearthstone-cooldown
|
||||||
|
+ Download: https://github.com/BytesGalore/mod-no-hearthstone-cooldown/archive/refs/heads/main.zip
|
||||||
|
- License: AGPL
|
||||||
|
|
||||||
|
# Module integration
|
||||||
|
|
||||||
|
- **AzerothCore hash/commit compliance:** [5af98783](https://github.com/azerothcore/azerothcore-wotlk/commit/5af98783c9f61f059914b3304bb26785502a6924)
|
||||||
|
- Includes configuration (.conf)?: Yes, copied by CMake
|
||||||
|
- Includes SQL patches?: No
|
||||||
|
- Core hooks used:
|
||||||
|
+ PlayerScript: OnLogin
|
||||||
|
+ PlayerScript: OnBeforeTeleport
|
||||||
|
+ WorldScript: OnAfterConfigLoad
|
||||||
|
|
||||||
|
# Description
|
||||||
|
A module that simply skips the cooldown of the Hearthstone
|
||||||
|
#### Features:
|
||||||
|
- It resets the Hearthstone cooldown immediately
|
||||||
|
|
||||||
|
### This module currently requires:
|
||||||
|
- AzerothCore v4.0.0+
|
||||||
|
|
||||||
|
### How to install
|
||||||
|
1. Simply place the module under the `modules` folder of your AzerothCore source folder.
|
||||||
|
2. Re-run cmake and launch a clean build of AzerothCore
|
||||||
|
3. that's all
|
||||||
|
|
||||||
|
### (Optional) Edit module configuration
|
||||||
|
- You can turn the module **on** and **off**, default it is **on**
|
||||||
|
- You can also turn the announcement of the module on player-login **on** and **off**, default it is **on**
|
||||||
|
- If you want change the settings, go to your server configuration folder (e.g. **etc**), copy `mod_no_hearthstone_cooldown.conf.dist` to `mod_no_hearthstone_cooldown.conf` and edit the settings
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
* [BytesGalore](https://github.com/BytesGalore) (author of the module)
|
||||||
|
* AzerothCore: [repository](https://github.com/azerothcore) - [website](http://azerothcore.org/) - [discord chat community](https://discord.gg/PaqQRkd)
|
32
modules/mod-no-hearthstone-cooldown/conf/conf.sh.dist
Normal file
32
modules/mod-no-hearthstone-cooldown/conf/conf.sh.dist
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
## CUSTOM SQL - Important file used by the db_assembler.sh
|
||||||
|
## Keep only the required variables (base sql files or updates, depending on the DB)
|
||||||
|
|
||||||
|
## BASE SQL
|
||||||
|
|
||||||
|
DB_AUTH_CUSTOM_PATHS+=(
|
||||||
|
$MOD_NO_HEARTHSTONE_COOLDOWN_ROOT"/sql/auth/base/"
|
||||||
|
)
|
||||||
|
|
||||||
|
DB_CHARACTERS_CUSTOM_PATHS+=(
|
||||||
|
$MOD_NO_HEARTHSTONE_COOLDOWN_ROOT"/sql/characters/base/"
|
||||||
|
)
|
||||||
|
|
||||||
|
DB_WORLD_CUSTOM_PATHS+=(
|
||||||
|
$MOD_NO_HEARTHSTONE_COOLDOWN_ROOT"/sql/world/base/"
|
||||||
|
)
|
||||||
|
|
||||||
|
## UPDATES
|
||||||
|
|
||||||
|
DB_AUTH_UPDATES_PATHS+=(
|
||||||
|
$MOD_NO_HEARTHSTONE_COOLDOWN_ROOT"/sql/auth/updates/"
|
||||||
|
)
|
||||||
|
|
||||||
|
DB_CHARACTERS_UPDATES_PATHS+=(
|
||||||
|
$MOD_NO_HEARTHSTONE_COOLDOWN_ROOT"/sql/characters/updates/"
|
||||||
|
)
|
||||||
|
|
||||||
|
DB_WORLD_UPDATES_PATHS+=(
|
||||||
|
$MOD_NO_HEARTHSTONE_COOLDOWN_ROOT"/sql/world/updates/"
|
||||||
|
)
|
@ -0,0 +1,24 @@
|
|||||||
|
#
|
||||||
|
# Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
|
||||||
|
#
|
||||||
|
|
||||||
|
[worldserver]
|
||||||
|
|
||||||
|
#####################################################
|
||||||
|
# No Hearthstone cooldown module configuration
|
||||||
|
#####################################################
|
||||||
|
#
|
||||||
|
# NoHearthstoneCooldownConfig.Enable
|
||||||
|
# Description: Enable to skip the Hearthstone cooldown
|
||||||
|
# Default: 1 - Enabled
|
||||||
|
# 0 - Disabled
|
||||||
|
#
|
||||||
|
# NoHearthstoneCooldownConfig.Announce
|
||||||
|
# Description: Inform the player about the loaded module
|
||||||
|
# Default: 1 - Enabled
|
||||||
|
# 0 - Disabled
|
||||||
|
#
|
||||||
|
|
||||||
|
NoHearthstoneCooldown.Enable = 1
|
||||||
|
|
||||||
|
NoHearthstoneCooldown.Announce = 1
|
10
modules/mod-no-hearthstone-cooldown/include.sh
Normal file
10
modules/mod-no-hearthstone-cooldown/include.sh
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
## GETS THE CURRENT MODULE ROOT DIRECTORY
|
||||||
|
MOD_NO_HEARTHSTONE_COOLDOWN_ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )/" && pwd )"
|
||||||
|
|
||||||
|
source $MOD_NO_HEARTHSTONE_COOLDOWN_ROOT"/conf/conf.sh.dist"
|
||||||
|
|
||||||
|
if [ -f $MOD_NO_HEARTHSTONE_COOLDOWN_ROOT"/conf/conf.sh" ]; then
|
||||||
|
source $MOD_NO_HEARTHSTONE_COOLDOWN_ROOT"/conf/conf.sh"
|
||||||
|
fi
|
@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
## Set a local git commit template
|
||||||
|
git config --local commit.template ".git_commit_template.txt" ;
|
12
modules/mod-no-hearthstone-cooldown/src/NHC_loader.cpp
Normal file
12
modules/mod-no-hearthstone-cooldown/src/NHC_loader.cpp
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
|
||||||
|
*/
|
||||||
|
|
||||||
|
// From SC
|
||||||
|
void AddNoHearthstoneCooldownScripts();
|
||||||
|
|
||||||
|
// Add all
|
||||||
|
void Addmod_no_hearthstone_cooldownScripts()
|
||||||
|
{
|
||||||
|
AddNoHearthstoneCooldownScripts();
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ScriptMgr.h"
|
||||||
|
#include "Player.h"
|
||||||
|
#include "Config.h"
|
||||||
|
#include "Chat.h"
|
||||||
|
|
||||||
|
// Add player scripts
|
||||||
|
class NoHearthstoneCooldown : public PlayerScript, public WorldScript
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NoHearthstoneCooldown(): PlayerScript("NoHearthstoneCooldown"), WorldScript("NoHearthstoneCooldown") {}
|
||||||
|
|
||||||
|
bool OnPlayerBeforeTeleport(Player* player, uint32 /*mapid*/, float /*x*/, float /*y*/, float /*z*/, float /*orientation*/, uint32 /*options*/, Unit* /*target*/) override
|
||||||
|
{
|
||||||
|
ClearHearthstoneCooldown(player);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnPlayerLogin(Player* player) override
|
||||||
|
{
|
||||||
|
if (sConfigMgr->GetOption<bool>("NoHearthstoneCooldown.Announce", true))
|
||||||
|
{
|
||||||
|
ChatHandler(player->GetSession()).SendSysMessage("This server is running the |cff4CFF00No Hearthstone cooldown|r module.");
|
||||||
|
}
|
||||||
|
ClearHearthstoneCooldown(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnAfterConfigLoad(bool /*reload*/) override
|
||||||
|
{
|
||||||
|
m_bNHCModuleEnabled = sConfigMgr->GetOption<bool>("NoHearthstoneCooldown.Enable", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void ClearHearthstoneCooldown(Player* player)
|
||||||
|
{
|
||||||
|
if (m_bNHCModuleEnabled)
|
||||||
|
{
|
||||||
|
// only clear the cooldown if the player actually casted the Hearthstone spell
|
||||||
|
if (player->FindCurrentSpellBySpellId(m_nSpellIDHearthstone))
|
||||||
|
{
|
||||||
|
// we remove the cooldown from the list of the player current cooldowns
|
||||||
|
player->RemoveSpellCooldown(m_nSpellIDHearthstone, true);
|
||||||
|
player->RemoveSpellCooldown(m_nSpellIDNoPlaceLikeHome, true);
|
||||||
|
// then we clear the cooldown and send an update to the player client
|
||||||
|
player->SendClearCooldown(m_nSpellIDHearthstone, player);
|
||||||
|
player->SendClearCooldown(m_nSpellIDNoPlaceLikeHome, player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool m_bNHCModuleEnabled = false;
|
||||||
|
// https://www.wowhead.com/spell=8690/hearthstone
|
||||||
|
const uint32 m_nSpellIDHearthstone = 8690;
|
||||||
|
// https://tbc.wowhead.com/spell=39937/theres-no-place-like-home
|
||||||
|
const uint32 m_nSpellIDNoPlaceLikeHome = 39937;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add all scripts in one
|
||||||
|
void AddNoHearthstoneCooldownScripts()
|
||||||
|
{
|
||||||
|
new NoHearthstoneCooldown();
|
||||||
|
}
|
@ -1,12 +1,22 @@
|
|||||||
INSERT INTO `playerbots_travelnode` (`id`, `name`, `map_id`, `x`, `y`, `z`, `linked`) VALUES
|
-- Delete existing entries from playerbots_travelnode, playerbots_travelnode_path, and playerbots_travelnode_link tables
|
||||||
(3780, 'Highlord Mograine', 533, 2524.32, -2951.28, 245.633, 1);
|
DELETE FROM `playerbots_travelnode_link` WHERE `node_id` = 3780;
|
||||||
|
DELETE FROM `playerbots_travelnode_path` WHERE `node_id` = 3780;
|
||||||
|
DELETE FROM `playerbots_travelnode` WHERE `id` = 3780;
|
||||||
|
|
||||||
INSERT INTO `playerbots_travelnode_path` (`node_id`, `to_node_id`, `nr`, `map_id`, `x`, `y`, `z`) VALUES
|
-- Insert new entries into playerbots_travelnode
|
||||||
|
INSERT INTO `playerbots_travelnode` (`id`, `name`, `map_id`, `x`, `y`, `z`, `linked`)
|
||||||
|
VALUES (3780, 'Highlord Mograine', 533, 2524.32, -2951.28, 245.633, 1);
|
||||||
|
|
||||||
|
-- Insert new entries into playerbots_travelnode_path
|
||||||
|
INSERT INTO `playerbots_travelnode_path` (`node_id`, `to_node_id`, `nr`, `map_id`, `x`, `y`, `z`)
|
||||||
|
VALUES
|
||||||
(3780, 472, 0, 533, 2524.32, -2951.28, 245.633),
|
(3780, 472, 0, 533, 2524.32, -2951.28, 245.633),
|
||||||
(3780, 472, 1, 533, 2528.79, -2948.58, 245.633),
|
(3780, 472, 1, 533, 2528.79, -2948.58, 245.633),
|
||||||
(3780, 757, 0, 533, 2524.32, -2951.28, 245.633),
|
(3780, 757, 0, 533, 2524.32, -2951.28, 245.633),
|
||||||
(3780, 757, 1, 533, 2517.62, -2959.38, 245.636);
|
(3780, 757, 1, 533, 2517.62, -2959.38, 245.636);
|
||||||
|
|
||||||
INSERT INTO `playerbots_travelnode_link` (`node_id`, `to_node_id`, `type`, `object`, `distance`, `swim_distance`, `extra_cost`, `calculated`, `max_creature_0`, `max_creature_1`, `max_creature_2`) VALUES
|
-- Insert new entries into playerbots_travelnode_link
|
||||||
|
INSERT INTO `playerbots_travelnode_link` (`node_id`, `to_node_id`, `type`, `object`, `distance`, `swim_distance`, `extra_cost`, `calculated`, `max_creature_0`, `max_creature_1`, `max_creature_2`)
|
||||||
|
VALUES
|
||||||
(3780, 472, 1, 0, 5.3221, 0, 0, 1, 83, 0, 0),
|
(3780, 472, 1, 0, 5.3221, 0, 0, 1, 83, 0, 0),
|
||||||
(3780, 757, 1, 0, 10.6118, 0, 0, 1, 83, 0, 0);
|
(3780, 757, 1, 0, 10.6118, 0, 0, 1, 83, 0, 0);
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "Unit.h"
|
#include "Unit.h"
|
||||||
|
|
||||||
#define MAX_LOOT_OBJECT_COUNT 10
|
#define MAX_LOOT_OBJECT_COUNT 200
|
||||||
|
|
||||||
LootTarget::LootTarget(ObjectGuid guid) : guid(guid), asOfTime(time(nullptr)) {}
|
LootTarget::LootTarget(ObjectGuid guid) : guid(guid), asOfTime(time(nullptr)) {}
|
||||||
|
|
||||||
|
@ -14,10 +14,13 @@
|
|||||||
#include "BudgetValues.h"
|
#include "BudgetValues.h"
|
||||||
#include "ChannelMgr.h"
|
#include "ChannelMgr.h"
|
||||||
#include "CharacterPackets.h"
|
#include "CharacterPackets.h"
|
||||||
|
#include "Common.h"
|
||||||
#include "CreatureAIImpl.h"
|
#include "CreatureAIImpl.h"
|
||||||
|
#include "CreatureData.h"
|
||||||
#include "EmoteAction.h"
|
#include "EmoteAction.h"
|
||||||
#include "Engine.h"
|
#include "Engine.h"
|
||||||
#include "ExternalEventHelper.h"
|
#include "ExternalEventHelper.h"
|
||||||
|
#include "GameObjectData.h"
|
||||||
#include "GameTime.h"
|
#include "GameTime.h"
|
||||||
#include "GuildMgr.h"
|
#include "GuildMgr.h"
|
||||||
#include "GuildTaskMgr.h"
|
#include "GuildTaskMgr.h"
|
||||||
@ -32,6 +35,7 @@
|
|||||||
#include "MoveSplineInit.h"
|
#include "MoveSplineInit.h"
|
||||||
#include "NewRpgStrategy.h"
|
#include "NewRpgStrategy.h"
|
||||||
#include "ObjectGuid.h"
|
#include "ObjectGuid.h"
|
||||||
|
#include "ObjectMgr.h"
|
||||||
#include "PerformanceMonitor.h"
|
#include "PerformanceMonitor.h"
|
||||||
#include "Player.h"
|
#include "Player.h"
|
||||||
#include "PlayerbotAIConfig.h"
|
#include "PlayerbotAIConfig.h"
|
||||||
@ -174,6 +178,7 @@ PlayerbotAI::PlayerbotAI(Player* bot)
|
|||||||
botOutgoingPacketHandlers.AddHandler(SMSG_RESURRECT_REQUEST, "resurrect request");
|
botOutgoingPacketHandlers.AddHandler(SMSG_RESURRECT_REQUEST, "resurrect request");
|
||||||
botOutgoingPacketHandlers.AddHandler(SMSG_INVENTORY_CHANGE_FAILURE, "cannot equip");
|
botOutgoingPacketHandlers.AddHandler(SMSG_INVENTORY_CHANGE_FAILURE, "cannot equip");
|
||||||
botOutgoingPacketHandlers.AddHandler(SMSG_TRADE_STATUS, "trade status");
|
botOutgoingPacketHandlers.AddHandler(SMSG_TRADE_STATUS, "trade status");
|
||||||
|
botOutgoingPacketHandlers.AddHandler(SMSG_TRADE_STATUS_EXTENDED, "trade status extended");
|
||||||
botOutgoingPacketHandlers.AddHandler(SMSG_LOOT_RESPONSE, "loot response");
|
botOutgoingPacketHandlers.AddHandler(SMSG_LOOT_RESPONSE, "loot response");
|
||||||
botOutgoingPacketHandlers.AddHandler(SMSG_ITEM_PUSH_RESULT, "item push result");
|
botOutgoingPacketHandlers.AddHandler(SMSG_ITEM_PUSH_RESULT, "item push result");
|
||||||
botOutgoingPacketHandlers.AddHandler(SMSG_PARTY_COMMAND_RESULT, "party command");
|
botOutgoingPacketHandlers.AddHandler(SMSG_PARTY_COMMAND_RESULT, "party command");
|
||||||
@ -204,7 +209,7 @@ PlayerbotAI::PlayerbotAI(Player* bot)
|
|||||||
masterIncomingPacketHandlers.AddHandler(CMSG_PUSHQUESTTOPARTY, "quest share");
|
masterIncomingPacketHandlers.AddHandler(CMSG_PUSHQUESTTOPARTY, "quest share");
|
||||||
botOutgoingPacketHandlers.AddHandler(SMSG_QUESTUPDATE_COMPLETE, "quest update complete");
|
botOutgoingPacketHandlers.AddHandler(SMSG_QUESTUPDATE_COMPLETE, "quest update complete");
|
||||||
botOutgoingPacketHandlers.AddHandler(SMSG_QUESTUPDATE_ADD_KILL, "quest update add kill");
|
botOutgoingPacketHandlers.AddHandler(SMSG_QUESTUPDATE_ADD_KILL, "quest update add kill");
|
||||||
botOutgoingPacketHandlers.AddHandler(SMSG_QUESTUPDATE_ADD_ITEM, "quest update add item");
|
// botOutgoingPacketHandlers.AddHandler(SMSG_QUESTUPDATE_ADD_ITEM, "quest update add item"); // SMSG_QUESTUPDATE_ADD_ITEM no longer used
|
||||||
botOutgoingPacketHandlers.AddHandler(SMSG_QUEST_CONFIRM_ACCEPT, "confirm quest");
|
botOutgoingPacketHandlers.AddHandler(SMSG_QUEST_CONFIRM_ACCEPT, "confirm quest");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1100,9 +1105,9 @@ void PlayerbotAI::HandleBotOutgoingPacket(WorldPacket const& packet)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QueueChatResponse(
|
QueueChatResponse(ChatQueuedReply{msgtype, guid1.GetCounter(), guid2.GetCounter(), message,
|
||||||
ChatQueuedReply{msgtype, guid1.GetCounter(), guid2.GetCounter(), message, chanName,
|
chanName, name,
|
||||||
name, time(nullptr) + urand(inCombat ? 10 : 5, inCombat ? 25 : 15)});
|
time(nullptr) + urand(inCombat ? 10 : 5, inCombat ? 25 : 15)});
|
||||||
GetAiObjectContext()->GetValue<time_t>("last said", "chat")->Set(time(0) + urand(5, 25));
|
GetAiObjectContext()->GetValue<time_t>("last said", "chat")->Set(time(0) + urand(5, 25));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1239,10 +1244,10 @@ void PlayerbotAI::ChangeEngine(BotState type)
|
|||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case BOT_STATE_COMBAT:
|
case BOT_STATE_COMBAT:
|
||||||
// LOG_DEBUG("playerbots", "=== {} COMBAT ===", bot->GetName().c_str());
|
ChangeEngineOnCombat();
|
||||||
break;
|
break;
|
||||||
case BOT_STATE_NON_COMBAT:
|
case BOT_STATE_NON_COMBAT:
|
||||||
// LOG_DEBUG("playerbots", "=== {} NON-COMBAT ===", bot->GetName().c_str());
|
ChangeEngineOnNonCombat();
|
||||||
break;
|
break;
|
||||||
case BOT_STATE_DEAD:
|
case BOT_STATE_DEAD:
|
||||||
// LOG_DEBUG("playerbots", "=== {} DEAD ===", bot->GetName().c_str());
|
// LOG_DEBUG("playerbots", "=== {} DEAD ===", bot->GetName().c_str());
|
||||||
@ -1253,6 +1258,23 @@ void PlayerbotAI::ChangeEngine(BotState type)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PlayerbotAI::ChangeEngineOnCombat()
|
||||||
|
{
|
||||||
|
if (HasStrategy("stay", BOT_STATE_COMBAT))
|
||||||
|
{
|
||||||
|
aiObjectContext->GetValue<PositionInfo>("pos", "stay")
|
||||||
|
->Set(PositionInfo(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), bot->GetMapId()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerbotAI::ChangeEngineOnNonCombat()
|
||||||
|
{
|
||||||
|
if (HasStrategy("stay", BOT_STATE_NON_COMBAT))
|
||||||
|
{
|
||||||
|
aiObjectContext->GetValue<PositionInfo>("pos", "stay")->Reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void PlayerbotAI::DoNextAction(bool min)
|
void PlayerbotAI::DoNextAction(bool min)
|
||||||
{
|
{
|
||||||
if (!bot->IsInWorld() || bot->IsBeingTeleported() || (GetMaster() && GetMaster()->IsBeingTeleported()))
|
if (!bot->IsInWorld() || bot->IsBeingTeleported() || (GetMaster() && GetMaster()->IsBeingTeleported()))
|
||||||
@ -2287,10 +2309,46 @@ const AreaTableEntry* PlayerbotAI::GetCurrentZone()
|
|||||||
|
|
||||||
std::string PlayerbotAI::GetLocalizedAreaName(const AreaTableEntry* entry)
|
std::string PlayerbotAI::GetLocalizedAreaName(const AreaTableEntry* entry)
|
||||||
{
|
{
|
||||||
|
std::string name;
|
||||||
if (entry)
|
if (entry)
|
||||||
return entry->area_name[sWorld->GetDefaultDbcLocale()];
|
{
|
||||||
|
name = entry->area_name[sWorld->GetDefaultDbcLocale()];
|
||||||
|
if (name.empty())
|
||||||
|
name = entry->area_name[LOCALE_enUS];
|
||||||
|
}
|
||||||
|
|
||||||
return "";
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string PlayerbotAI::GetLocalizedCreatureName(uint32 entry)
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
const CreatureLocale* cl = sObjectMgr->GetCreatureLocale(entry);
|
||||||
|
if (cl)
|
||||||
|
ObjectMgr::GetLocaleString(cl->Name, sWorld->GetDefaultDbcLocale(), name);
|
||||||
|
if (name.empty())
|
||||||
|
{
|
||||||
|
CreatureTemplate const* ct = sObjectMgr->GetCreatureTemplate(entry);
|
||||||
|
if (ct)
|
||||||
|
name = ct->Name;
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string PlayerbotAI::GetLocalizedGameObjectName(uint32 entry)
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
const GameObjectLocale* gl = sObjectMgr->GetGameObjectLocale(entry);
|
||||||
|
if (gl)
|
||||||
|
ObjectMgr::GetLocaleString(gl->Name, sWorld->GetDefaultDbcLocale(), name);
|
||||||
|
if (name.empty())
|
||||||
|
{
|
||||||
|
GameObjectTemplate const* gt = sObjectMgr->GetGameObjectTemplate(entry);
|
||||||
|
if (gt)
|
||||||
|
name = gt->name;
|
||||||
|
}
|
||||||
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Player*> PlayerbotAI::GetPlayersInGroup()
|
std::vector<Player*> PlayerbotAI::GetPlayersInGroup()
|
||||||
@ -2807,7 +2865,6 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, Unit* target, bool checkHasSpell,
|
|||||||
if (!target)
|
if (!target)
|
||||||
target = bot;
|
target = bot;
|
||||||
|
|
||||||
|
|
||||||
if (Pet* pet = bot->GetPet())
|
if (Pet* pet = bot->GetPet())
|
||||||
if (pet->HasSpell(spellid))
|
if (pet->HasSpell(spellid))
|
||||||
return true;
|
return true;
|
||||||
@ -3000,8 +3057,7 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, GameObject* goTarget, bool checkH
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PlayerbotAI::CanCastSpell(uint32 spellid, float x, float y, float z, bool checkHasSpell,
|
bool PlayerbotAI::CanCastSpell(uint32 spellid, float x, float y, float z, bool checkHasSpell, Item* itemTarget)
|
||||||
Item* itemTarget)
|
|
||||||
{
|
{
|
||||||
if (!spellid)
|
if (!spellid)
|
||||||
return false;
|
return false;
|
||||||
@ -3133,7 +3189,8 @@ bool PlayerbotAI::CastSpell(uint32 spellId, Unit* target, Item* itemTarget)
|
|||||||
Spell* spell = new Spell(bot, spellInfo, TRIGGERED_NONE);
|
Spell* spell = new Spell(bot, spellInfo, TRIGGERED_NONE);
|
||||||
|
|
||||||
SpellCastTargets targets;
|
SpellCastTargets targets;
|
||||||
if (spellInfo->Targets & TARGET_FLAG_ITEM)
|
if (spellInfo->Effects[0].Effect != SPELL_EFFECT_OPEN_LOCK &&
|
||||||
|
(spellInfo->Targets & TARGET_FLAG_ITEM || spellInfo->Targets & TARGET_FLAG_GAMEOBJECT_ITEM))
|
||||||
{
|
{
|
||||||
Item* item = itemTarget ? itemTarget : aiObjectContext->GetValue<Item*>("item for spell", spellId)->Get();
|
Item* item = itemTarget ? itemTarget : aiObjectContext->GetValue<Item*>("item for spell", spellId)->Get();
|
||||||
targets.SetItemTarget(item);
|
targets.SetItemTarget(item);
|
||||||
@ -3176,6 +3233,20 @@ bool PlayerbotAI::CastSpell(uint32 spellId, Unit* target, Item* itemTarget)
|
|||||||
targets.SetGOTarget(go);
|
targets.SetGOTarget(go);
|
||||||
faceTo = go;
|
faceTo = go;
|
||||||
}
|
}
|
||||||
|
else if (itemTarget)
|
||||||
|
{
|
||||||
|
Player* trader = bot->GetTrader();
|
||||||
|
if (trader)
|
||||||
|
{
|
||||||
|
targets.SetTradeItemTarget(bot);
|
||||||
|
targets.SetUnitTarget(bot);
|
||||||
|
faceTo = trader;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
targets.SetItemTarget(itemTarget);
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (Unit* creature = GetUnit(loot.guid))
|
if (Unit* creature = GetUnit(loot.guid))
|
||||||
@ -3212,6 +3283,58 @@ bool PlayerbotAI::CastSpell(uint32 spellId, Unit* target, Item* itemTarget)
|
|||||||
// LOG_DEBUG("playerbots", "Spell cast failed. - target name: {}, spellid: {}, bot name: {}, result: {}",
|
// LOG_DEBUG("playerbots", "Spell cast failed. - target name: {}, spellid: {}, bot name: {}, result: {}",
|
||||||
// target->GetName(), spellId, bot->GetName(), result);
|
// target->GetName(), spellId, bot->GetName(), result);
|
||||||
// }
|
// }
|
||||||
|
if (HasStrategy("debug spell", BOT_STATE_NON_COMBAT))
|
||||||
|
{
|
||||||
|
std::ostringstream out;
|
||||||
|
out << "Spell cast failed - ";
|
||||||
|
out << "Spell ID: " << spellId << " (" << ChatHelper::FormatSpell(spellInfo) << "), ";
|
||||||
|
out << "Error Code: " << static_cast<int>(result) << " (0x" << std::hex << static_cast<int>(result) << std::dec << "), ";
|
||||||
|
out << "Bot: " << bot->GetName() << ", ";
|
||||||
|
|
||||||
|
// Check spell target type
|
||||||
|
if (targets.GetUnitTarget())
|
||||||
|
{
|
||||||
|
out << "Target: Unit (" << targets.GetUnitTarget()->GetName()
|
||||||
|
<< ", Low GUID: " << targets.GetUnitTarget()->GetGUID().GetCounter()
|
||||||
|
<< ", High GUID: " << static_cast<uint32>(targets.GetUnitTarget()->GetGUID().GetHigh()) << "), ";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targets.GetGOTarget())
|
||||||
|
{
|
||||||
|
out << "Target: GameObject (Low GUID: " << targets.GetGOTarget()->GetGUID().GetCounter()
|
||||||
|
<< ", High GUID: " << static_cast<uint32>(targets.GetGOTarget()->GetGUID().GetHigh()) << "), ";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targets.GetItemTarget())
|
||||||
|
{
|
||||||
|
out << "Target: Item (Low GUID: " << targets.GetItemTarget()->GetGUID().GetCounter()
|
||||||
|
<< ", High GUID: " << static_cast<uint32>(targets.GetItemTarget()->GetGUID().GetHigh()) << "), ";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if bot is in trade mode
|
||||||
|
if (bot->GetTradeData())
|
||||||
|
{
|
||||||
|
out << "Trade Mode: Active, ";
|
||||||
|
Item* tradeItem = bot->GetTradeData()->GetTraderData()->GetItem(TRADE_SLOT_NONTRADED);
|
||||||
|
if (tradeItem)
|
||||||
|
{
|
||||||
|
out << "Trade Item: " << tradeItem->GetEntry()
|
||||||
|
<< " (Low GUID: " << tradeItem->GetGUID().GetCounter()
|
||||||
|
<< ", High GUID: " << static_cast<uint32>(tradeItem->GetGUID().GetHigh()) << "), ";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
out << "Trade Item: None, ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
out << "Trade Mode: Inactive, ";
|
||||||
|
}
|
||||||
|
|
||||||
|
TellMasterNoFacing(out);
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// if (spellInfo->Effects[0].Effect == SPELL_EFFECT_OPEN_LOCK || spellInfo->Effects[0].Effect ==
|
// if (spellInfo->Effects[0].Effect == SPELL_EFFECT_OPEN_LOCK || spellInfo->Effects[0].Effect ==
|
||||||
@ -3308,7 +3431,7 @@ bool PlayerbotAI::CastSpell(uint32 spellId, float x, float y, float z, Item* ite
|
|||||||
Spell* spell = new Spell(bot, spellInfo, TRIGGERED_NONE);
|
Spell* spell = new Spell(bot, spellInfo, TRIGGERED_NONE);
|
||||||
|
|
||||||
SpellCastTargets targets;
|
SpellCastTargets targets;
|
||||||
if (spellInfo->Targets & TARGET_FLAG_ITEM)
|
if (spellInfo->Targets & TARGET_FLAG_ITEM || spellInfo->Targets & TARGET_FLAG_GAMEOBJECT_ITEM)
|
||||||
{
|
{
|
||||||
Item* item = itemTarget ? itemTarget : aiObjectContext->GetValue<Item*>("item for spell", spellId)->Get();
|
Item* item = itemTarget ? itemTarget : aiObjectContext->GetValue<Item*>("item for spell", spellId)->Get();
|
||||||
targets.SetItemTarget(item);
|
targets.SetItemTarget(item);
|
||||||
@ -4188,8 +4311,8 @@ bool PlayerbotAI::AllowActive(ActivityType activityType)
|
|||||||
mod = AutoScaleActivity(mod);
|
mod = AutoScaleActivity(mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 ActivityNumber = GetFixedBotNumer(100,
|
uint32 ActivityNumber =
|
||||||
sPlayerbotAIConfig->botActiveAlone * static_cast<float>(mod) / 100 * 0.01f);
|
GetFixedBotNumer(100, sPlayerbotAIConfig->botActiveAlone * static_cast<float>(mod) / 100 * 0.01f);
|
||||||
|
|
||||||
return ActivityNumber <=
|
return ActivityNumber <=
|
||||||
(sPlayerbotAIConfig->botActiveAlone * mod) /
|
(sPlayerbotAIConfig->botActiveAlone * mod) /
|
||||||
@ -4248,7 +4371,8 @@ void PlayerbotAI::RemoveShapeshift()
|
|||||||
RemoveAura("moonkin form");
|
RemoveAura("moonkin form");
|
||||||
RemoveAura("travel form");
|
RemoveAura("travel form");
|
||||||
RemoveAura("cat form");
|
RemoveAura("cat form");
|
||||||
RemoveAura("flight form"); bot->RemoveAura(33943); // The latter added for now as RemoveAura("flight form") currently does not work.
|
RemoveAura("flight form");
|
||||||
|
bot->RemoveAura(33943); // The latter added for now as RemoveAura("flight form") currently does not work.
|
||||||
RemoveAura("swift flight form");
|
RemoveAura("swift flight form");
|
||||||
RemoveAura("aquatic form");
|
RemoveAura("aquatic form");
|
||||||
RemoveAura("ghost wolf");
|
RemoveAura("ghost wolf");
|
||||||
@ -4835,11 +4959,9 @@ Item* PlayerbotAI::FindAmmo() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Search inventory for the correct ammo type
|
// Search inventory for the correct ammo type
|
||||||
return FindItemInInventory([requiredAmmoType](ItemTemplate const* pItemProto) -> bool
|
return FindItemInInventory(
|
||||||
{
|
[requiredAmmoType](ItemTemplate const* pItemProto) -> bool
|
||||||
return pItemProto->Class == ITEM_CLASS_PROJECTILE &&
|
{ return pItemProto->Class == ITEM_CLASS_PROJECTILE && pItemProto->SubClass == requiredAmmoType; });
|
||||||
pItemProto->SubClass == requiredAmmoType;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr; // No ranged weapon equipped
|
return nullptr; // No ranged weapon equipped
|
||||||
@ -4864,6 +4986,52 @@ Item* PlayerbotAI::FindBandage() const
|
|||||||
{ return pItemProto->Class == ITEM_CLASS_CONSUMABLE && pItemProto->SubClass == ITEM_SUBCLASS_BANDAGE; });
|
{ return pItemProto->Class == ITEM_CLASS_CONSUMABLE && pItemProto->SubClass == ITEM_SUBCLASS_BANDAGE; });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Item* PlayerbotAI::FindOpenableItem() const
|
||||||
|
{
|
||||||
|
return FindItemInInventory([this](ItemTemplate const* itemTemplate) -> bool
|
||||||
|
{
|
||||||
|
return (itemTemplate->Flags & ITEM_FLAG_HAS_LOOT) &&
|
||||||
|
(itemTemplate->LockID == 0 || !this->bot->GetItemByEntry(itemTemplate->ItemId)->IsLocked());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Item* PlayerbotAI::FindLockedItem() const
|
||||||
|
{
|
||||||
|
return FindItemInInventory([this](ItemTemplate const* itemTemplate) -> bool
|
||||||
|
{
|
||||||
|
if (!this->bot->HasSkill(SKILL_LOCKPICKING)) // Ensure bot has Lockpicking skill
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (itemTemplate->LockID == 0) // Ensure the item is actually locked
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Item* item = this->bot->GetItemByEntry(itemTemplate->ItemId);
|
||||||
|
if (!item || !item->IsLocked()) // Ensure item instance is locked
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Check if bot has enough Lockpicking skill
|
||||||
|
LockEntry const* lockInfo = sLockStore.LookupEntry(itemTemplate->LockID);
|
||||||
|
if (!lockInfo)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (uint8 j = 0; j < 8; ++j)
|
||||||
|
{
|
||||||
|
if (lockInfo->Type[j] == LOCK_KEY_SKILL)
|
||||||
|
{
|
||||||
|
uint32 skillId = SkillByLockType(LockType(lockInfo->Index[j]));
|
||||||
|
if (skillId == SKILL_LOCKPICKING)
|
||||||
|
{
|
||||||
|
uint32 requiredSkill = lockInfo->Skill[j];
|
||||||
|
uint32 botSkill = this->bot->GetSkillValue(SKILL_LOCKPICKING);
|
||||||
|
return botSkill >= requiredSkill;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
static const uint32 uPriorizedSharpStoneIds[8] = {ADAMANTITE_SHARPENING_DISPLAYID, FEL_SHARPENING_DISPLAYID,
|
static const uint32 uPriorizedSharpStoneIds[8] = {ADAMANTITE_SHARPENING_DISPLAYID, FEL_SHARPENING_DISPLAYID,
|
||||||
ELEMENTAL_SHARPENING_DISPLAYID, DENSE_SHARPENING_DISPLAYID,
|
ELEMENTAL_SHARPENING_DISPLAYID, DENSE_SHARPENING_DISPLAYID,
|
||||||
SOLID_SHARPENING_DISPLAYID, HEAVY_SHARPENING_DISPLAYID,
|
SOLID_SHARPENING_DISPLAYID, HEAVY_SHARPENING_DISPLAYID,
|
||||||
@ -6024,9 +6192,10 @@ bool PlayerbotAI::IsHealingSpell(uint32 spellFamilyName, flag96 spellFalimyFlags
|
|||||||
return spellFalimyFlags & healingFlags;
|
return spellFalimyFlags & healingFlags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SpellFamilyNames PlayerbotAI::Class2SpellFamilyName(uint8 cls)
|
||||||
SpellFamilyNames PlayerbotAI::Class2SpellFamilyName(uint8 cls) {
|
{
|
||||||
switch (cls) {
|
switch (cls)
|
||||||
|
{
|
||||||
case CLASS_WARRIOR:
|
case CLASS_WARRIOR:
|
||||||
return SPELLFAMILY_WARRIOR;
|
return SPELLFAMILY_WARRIOR;
|
||||||
case CLASS_PALADIN:
|
case CLASS_PALADIN:
|
||||||
|
@ -13,8 +13,10 @@
|
|||||||
#include "ChatFilter.h"
|
#include "ChatFilter.h"
|
||||||
#include "ChatHelper.h"
|
#include "ChatHelper.h"
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
|
#include "CreatureData.h"
|
||||||
#include "Event.h"
|
#include "Event.h"
|
||||||
#include "Item.h"
|
#include "Item.h"
|
||||||
|
#include "NewRpgInfo.h"
|
||||||
#include "NewRpgStrategy.h"
|
#include "NewRpgStrategy.h"
|
||||||
#include "PlayerbotAIBase.h"
|
#include "PlayerbotAIBase.h"
|
||||||
#include "PlayerbotAIConfig.h"
|
#include "PlayerbotAIConfig.h"
|
||||||
@ -390,6 +392,8 @@ public:
|
|||||||
void HandleMasterOutgoingPacket(WorldPacket const& packet);
|
void HandleMasterOutgoingPacket(WorldPacket const& packet);
|
||||||
void HandleTeleportAck();
|
void HandleTeleportAck();
|
||||||
void ChangeEngine(BotState type);
|
void ChangeEngine(BotState type);
|
||||||
|
void ChangeEngineOnCombat();
|
||||||
|
void ChangeEngineOnNonCombat();
|
||||||
void DoNextAction(bool minimal = false);
|
void DoNextAction(bool minimal = false);
|
||||||
virtual bool DoSpecificAction(std::string const name, Event event = Event(), bool silent = false,
|
virtual bool DoSpecificAction(std::string const name, Event event = Event(), bool silent = false,
|
||||||
std::string const qualifier = "");
|
std::string const qualifier = "");
|
||||||
@ -435,7 +439,8 @@ public:
|
|||||||
const AreaTableEntry* GetCurrentArea();
|
const AreaTableEntry* GetCurrentArea();
|
||||||
const AreaTableEntry* GetCurrentZone();
|
const AreaTableEntry* GetCurrentZone();
|
||||||
static std::string GetLocalizedAreaName(const AreaTableEntry* entry);
|
static std::string GetLocalizedAreaName(const AreaTableEntry* entry);
|
||||||
|
static std::string GetLocalizedCreatureName(uint32 entry);
|
||||||
|
static std::string GetLocalizedGameObjectName(uint32 entry);
|
||||||
bool TellMaster(std::ostringstream& stream, PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL);
|
bool TellMaster(std::ostringstream& stream, PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL);
|
||||||
bool TellMaster(std::string const text, PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL);
|
bool TellMaster(std::string const text, PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL);
|
||||||
bool TellMasterNoFacing(std::ostringstream& stream,
|
bool TellMasterNoFacing(std::ostringstream& stream,
|
||||||
@ -464,6 +469,8 @@ public:
|
|||||||
Item* FindPoison() const;
|
Item* FindPoison() const;
|
||||||
Item* FindAmmo() const;
|
Item* FindAmmo() const;
|
||||||
Item* FindBandage() const;
|
Item* FindBandage() const;
|
||||||
|
Item* FindOpenableItem() const;
|
||||||
|
Item* FindLockedItem() const;
|
||||||
Item* FindConsumable(uint32 displayId) const;
|
Item* FindConsumable(uint32 displayId) const;
|
||||||
Item* FindStoneFor(Item* weapon) const;
|
Item* FindStoneFor(Item* weapon) const;
|
||||||
Item* FindOilFor(Item* weapon) const;
|
Item* FindOilFor(Item* weapon) const;
|
||||||
@ -579,6 +586,8 @@ public:
|
|||||||
static bool IsHealingSpell(uint32 spellFamilyName, flag96 spelFalimyFlags);
|
static bool IsHealingSpell(uint32 spellFamilyName, flag96 spelFalimyFlags);
|
||||||
static SpellFamilyNames Class2SpellFamilyName(uint8 cls);
|
static SpellFamilyNames Class2SpellFamilyName(uint8 cls);
|
||||||
NewRpgInfo rpgInfo;
|
NewRpgInfo rpgInfo;
|
||||||
|
NewRpgStatistic rpgStatistic;
|
||||||
|
std::unordered_set<uint32> lowPriorityQuest;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void _fillGearScoreData(Player* player, Item* item, std::vector<uint32>* gearScore, uint32& twoHandScore,
|
static void _fillGearScoreData(Player* player, Item* item, std::vector<uint32>* gearScore, uint32& twoHandScore,
|
||||||
|
@ -91,7 +91,7 @@ void PlayerbotHolder::AddPlayerBot(ObjectGuid playerGuid, uint32 masterAccountId
|
|||||||
LOG_DEBUG("playerbots", "PlayerbotMgr not found for master player with GUID: {}", masterPlayer->GetGUID().GetRawValue());
|
LOG_DEBUG("playerbots", "PlayerbotMgr not found for master player with GUID: {}", masterPlayer->GetGUID().GetRawValue());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
uint32 count = mgr->GetPlayerbotsCount();
|
uint32 count = mgr->GetPlayerbotsCount() + botLoading.size();
|
||||||
if (count >= sPlayerbotAIConfig->maxAddedBots)
|
if (count >= sPlayerbotAIConfig->maxAddedBots)
|
||||||
{
|
{
|
||||||
allowed = false;
|
allowed = false;
|
||||||
@ -638,11 +638,22 @@ std::string const PlayerbotHolder::ProcessBotCommand(std::string const cmd, Obje
|
|||||||
bool isRandomAccount = sPlayerbotAIConfig->IsInRandomAccountList(botAccount);
|
bool isRandomAccount = sPlayerbotAIConfig->IsInRandomAccountList(botAccount);
|
||||||
bool isMasterAccount = (masterAccountId == botAccount);
|
bool isMasterAccount = (masterAccountId == botAccount);
|
||||||
|
|
||||||
if (cmd == "add" || cmd == "login")
|
if (cmd == "add" || cmd == "addaccount" || cmd == "login")
|
||||||
{
|
{
|
||||||
if (ObjectAccessor::FindPlayer(guid))
|
if (ObjectAccessor::FindPlayer(guid))
|
||||||
return "player already logged in";
|
return "player already logged in";
|
||||||
|
|
||||||
|
// For addaccount command, verify it's an account name
|
||||||
|
if (cmd == "addaccount")
|
||||||
|
{
|
||||||
|
uint32 accountId = sCharacterCache->GetCharacterAccountIdByGuid(guid);
|
||||||
|
if (!accountId)
|
||||||
|
return "character not found";
|
||||||
|
|
||||||
|
if (!sPlayerbotAIConfig->allowAccountBots && accountId != masterAccountId)
|
||||||
|
return "you can only add bots from your own account";
|
||||||
|
}
|
||||||
|
|
||||||
AddPlayerBot(guid, masterAccountId);
|
AddPlayerBot(guid, masterAccountId);
|
||||||
return "ok";
|
return "ok";
|
||||||
}
|
}
|
||||||
@ -816,7 +827,7 @@ std::vector<std::string> PlayerbotHolder::HandlePlayerbotCommand(char const* arg
|
|||||||
|
|
||||||
if (!*args)
|
if (!*args)
|
||||||
{
|
{
|
||||||
messages.push_back("usage: list/reload/tweak/self or add/init/remove PLAYERNAME\n");
|
messages.push_back("usage: list/reload/tweak/self or add/addaccount/init/remove PLAYERNAME\n");
|
||||||
messages.push_back("usage: addclass CLASSNAME");
|
messages.push_back("usage: addclass CLASSNAME");
|
||||||
return messages;
|
return messages;
|
||||||
}
|
}
|
||||||
@ -1130,12 +1141,26 @@ std::vector<std::string> PlayerbotHolder::HandlePlayerbotCommand(char const* arg
|
|||||||
{
|
{
|
||||||
std::string const s = *i;
|
std::string const s = *i;
|
||||||
|
|
||||||
|
if (!strcmp(cmd, "addaccount"))
|
||||||
|
{
|
||||||
|
// When using addaccount, first try to get account ID directly
|
||||||
uint32 accountId = GetAccountId(s);
|
uint32 accountId = GetAccountId(s);
|
||||||
if (!accountId)
|
if (!accountId)
|
||||||
{
|
{
|
||||||
bots.insert(s);
|
// If not found, try to get account ID from character name
|
||||||
|
ObjectGuid charGuid = sCharacterCache->GetCharacterGuidByName(s);
|
||||||
|
if (!charGuid)
|
||||||
|
{
|
||||||
|
messages.push_back("Neither account nor character '" + s + "' found");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
accountId = sCharacterCache->GetCharacterAccountIdByGuid(charGuid);
|
||||||
|
if (!accountId)
|
||||||
|
{
|
||||||
|
messages.push_back("Could not find account for character '" + s + "'");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QueryResult results = CharacterDatabase.Query("SELECT name FROM characters WHERE account = {}", accountId);
|
QueryResult results = CharacterDatabase.Query("SELECT name FROM characters WHERE account = {}", accountId);
|
||||||
if (results)
|
if (results)
|
||||||
@ -1148,6 +1173,18 @@ std::vector<std::string> PlayerbotHolder::HandlePlayerbotCommand(char const* arg
|
|||||||
} while (results->NextRow());
|
} while (results->NextRow());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// For regular add command, only add the specific character
|
||||||
|
ObjectGuid charGuid = sCharacterCache->GetCharacterGuidByName(s);
|
||||||
|
if (!charGuid)
|
||||||
|
{
|
||||||
|
messages.push_back("Character '" + s + "' not found");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
bots.insert(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (auto i = bots.begin(); i != bots.end(); ++i)
|
for (auto i = bots.begin(); i != bots.end(); ++i)
|
||||||
{
|
{
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
#include "GuildTaskMgr.h"
|
#include "GuildTaskMgr.h"
|
||||||
#include "LFGMgr.h"
|
#include "LFGMgr.h"
|
||||||
#include "MapMgr.h"
|
#include "MapMgr.h"
|
||||||
|
#include "NewRpgInfo.h"
|
||||||
#include "NewRpgStrategy.h"
|
#include "NewRpgStrategy.h"
|
||||||
#include "PerformanceMonitor.h"
|
#include "PerformanceMonitor.h"
|
||||||
#include "Player.h"
|
#include "Player.h"
|
||||||
@ -49,6 +50,12 @@
|
|||||||
#include "World.h"
|
#include "World.h"
|
||||||
#include "RandomPlayerbotFactory.h"
|
#include "RandomPlayerbotFactory.h"
|
||||||
|
|
||||||
|
struct GuidClassRaceInfo {
|
||||||
|
ObjectGuid::LowType guid;
|
||||||
|
uint32 rClass;
|
||||||
|
uint32 rRace;
|
||||||
|
};
|
||||||
|
|
||||||
void PrintStatsThread() { sRandomPlayerbotMgr->PrintStats(); }
|
void PrintStatsThread() { sRandomPlayerbotMgr->PrintStats(); }
|
||||||
|
|
||||||
void activatePrintStatsThread()
|
void activatePrintStatsThread()
|
||||||
@ -371,7 +378,7 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/)
|
|||||||
sRandomPlayerbotMgr->CheckLfgQueue();
|
sRandomPlayerbotMgr->CheckLfgQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (time(nullptr) > (printStatsTimer + 300))
|
if (sPlayerbotAIConfig->randomBotAutologin && time(nullptr) > (printStatsTimer + 300))
|
||||||
{
|
{
|
||||||
if (!printStatsTimer)
|
if (!printStatsTimer)
|
||||||
{
|
{
|
||||||
@ -465,8 +472,16 @@ uint32 RandomPlayerbotMgr::AddRandomBots()
|
|||||||
maxAllowedBotCount -= currentBots.size();
|
maxAllowedBotCount -= currentBots.size();
|
||||||
maxAllowedBotCount = std::min(sPlayerbotAIConfig->randomBotsPerInterval, maxAllowedBotCount);
|
maxAllowedBotCount = std::min(sPlayerbotAIConfig->randomBotsPerInterval, maxAllowedBotCount);
|
||||||
|
|
||||||
uint32 allowedAllianceCount = maxAllowedBotCount * (sPlayerbotAIConfig->randomBotAllianceRatio) /
|
uint32 totalRatio = sPlayerbotAIConfig->randomBotAllianceRatio + sPlayerbotAIConfig->randomBotHordeRatio;
|
||||||
(sPlayerbotAIConfig->randomBotAllianceRatio + sPlayerbotAIConfig->randomBotHordeRatio);
|
uint32 allowedAllianceCount = maxAllowedBotCount * (sPlayerbotAIConfig->randomBotAllianceRatio) / totalRatio;
|
||||||
|
|
||||||
|
uint32 remainder = maxAllowedBotCount * (sPlayerbotAIConfig->randomBotAllianceRatio) % totalRatio;
|
||||||
|
|
||||||
|
// Fix #1082: Randomly add one based on reminder
|
||||||
|
if (remainder && urand(1, totalRatio) <= remainder) {
|
||||||
|
allowedAllianceCount++;
|
||||||
|
}
|
||||||
|
|
||||||
uint32 allowedHordeCount = maxAllowedBotCount - allowedAllianceCount;
|
uint32 allowedHordeCount = maxAllowedBotCount - allowedAllianceCount;
|
||||||
|
|
||||||
for (std::vector<uint32>::iterator i = sPlayerbotAIConfig->randomBotAccounts.begin();
|
for (std::vector<uint32>::iterator i = sPlayerbotAIConfig->randomBotAccounts.begin();
|
||||||
@ -492,11 +507,28 @@ uint32 RandomPlayerbotMgr::AddRandomBots()
|
|||||||
PreparedQueryResult result = CharacterDatabase.Query(stmt);
|
PreparedQueryResult result = CharacterDatabase.Query(stmt);
|
||||||
if (!result)
|
if (!result)
|
||||||
continue;
|
continue;
|
||||||
std::vector<uint32> guids;
|
|
||||||
do
|
std::vector<GuidClassRaceInfo> allGuidInfos;
|
||||||
{
|
|
||||||
|
do {
|
||||||
Field* fields = result->Fetch();
|
Field* fields = result->Fetch();
|
||||||
ObjectGuid::LowType guid = fields[0].Get<uint32>();
|
GuidClassRaceInfo info;
|
||||||
|
info.guid = fields[0].Get<uint32>();
|
||||||
|
info.rClass = fields[1].Get<uint8>();
|
||||||
|
info.rRace = fields[2].Get<uint8>();
|
||||||
|
allGuidInfos.push_back(info);
|
||||||
|
} while (result->NextRow());
|
||||||
|
|
||||||
|
// random shuffle for class balance
|
||||||
|
std::mt19937 rnd(time(0));
|
||||||
|
std::shuffle(allGuidInfos.begin(), allGuidInfos.end(), rnd);
|
||||||
|
|
||||||
|
std::vector<uint32> guids;
|
||||||
|
for (const auto& info : allGuidInfos) {
|
||||||
|
ObjectGuid::LowType guid = info.guid;
|
||||||
|
uint32 rClass = info.rClass;
|
||||||
|
uint32 rRace = info.rRace;
|
||||||
|
|
||||||
if (GetEventValue(guid, "add"))
|
if (GetEventValue(guid, "add"))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -509,40 +541,24 @@ uint32 RandomPlayerbotMgr::AddRandomBots()
|
|||||||
if (std::find(currentBots.begin(), currentBots.end(), guid) != currentBots.end())
|
if (std::find(currentBots.begin(), currentBots.end(), guid) != currentBots.end())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (sPlayerbotAIConfig->disableDeathKnightLogin)
|
if (sPlayerbotAIConfig->disableDeathKnightLogin) {
|
||||||
{
|
if (rClass == CLASS_DEATH_KNIGHT) {
|
||||||
uint32 rClass = fields[1].Get<uint8>();
|
|
||||||
if (rClass == CLASS_DEATH_KNIGHT)
|
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
uint32 rRace = fields[2].Get<uint8>();
|
|
||||||
uint32 isAlliance = IsAlliance(rRace);
|
uint32 isAlliance = IsAlliance(rRace);
|
||||||
if (!allowedAllianceCount && isAlliance)
|
bool factionNotAllowed = (!allowedAllianceCount && isAlliance) || (!allowedHordeCount && !isAlliance);
|
||||||
{
|
|
||||||
|
if (factionNotAllowed)
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
if (!allowedHordeCount && !isAlliance)
|
if (isAlliance) {
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (isAlliance)
|
|
||||||
{
|
|
||||||
allowedAllianceCount--;
|
allowedAllianceCount--;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
allowedHordeCount--;
|
allowedHordeCount--;
|
||||||
}
|
}
|
||||||
guids.push_back(guid);
|
|
||||||
} while (result->NextRow());
|
|
||||||
|
|
||||||
std::mt19937 rnd(time(0));
|
|
||||||
std::shuffle(guids.begin(), guids.end(), rnd);
|
|
||||||
|
|
||||||
for (uint32& guid : guids)
|
|
||||||
{
|
|
||||||
uint32 add_time = sPlayerbotAIConfig->enablePeriodicOnlineOffline
|
uint32 add_time = sPlayerbotAIConfig->enablePeriodicOnlineOffline
|
||||||
? urand(sPlayerbotAIConfig->minRandomBotInWorldTime,
|
? urand(sPlayerbotAIConfig->minRandomBotInWorldTime,
|
||||||
sPlayerbotAIConfig->maxRandomBotInWorldTime)
|
sPlayerbotAIConfig->maxRandomBotInWorldTime)
|
||||||
@ -1466,6 +1482,82 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot, std::vector<WorldLocation>&
|
|||||||
tlocs.size());
|
tlocs.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RandomPlayerbotMgr::PrepareZone2LevelBracket()
|
||||||
|
{
|
||||||
|
// Classic WoW - Low - level zones
|
||||||
|
zone2LevelBracket[1] = {5, 12}; // Dun Morogh
|
||||||
|
zone2LevelBracket[12] = {5, 12}; // Elwynn Forest
|
||||||
|
zone2LevelBracket[14] = {5, 12}; // Durotar
|
||||||
|
zone2LevelBracket[85] = {5, 12}; // Tirisfal Glades
|
||||||
|
zone2LevelBracket[141] = {5, 12}; // Teldrassil
|
||||||
|
zone2LevelBracket[215] = {5, 12}; // Mulgore
|
||||||
|
zone2LevelBracket[3430] = {5, 12}; // Eversong Woods
|
||||||
|
zone2LevelBracket[3524] = {5, 12}; // Azuremyst Isle
|
||||||
|
|
||||||
|
// Classic WoW - Mid - level zones
|
||||||
|
zone2LevelBracket[17] = {10, 25}; // Barrens
|
||||||
|
zone2LevelBracket[38] = {10, 20}; // Loch Modan
|
||||||
|
zone2LevelBracket[40] = {10, 21}; // Westfall
|
||||||
|
zone2LevelBracket[130] = {10, 23}; // Silverpine Forest
|
||||||
|
zone2LevelBracket[148] = {10, 21}; // Darkshore
|
||||||
|
zone2LevelBracket[3433] = {10, 22}; // Ghostlands
|
||||||
|
zone2LevelBracket[3525] = {10, 21}; // Bloodmyst Isle
|
||||||
|
|
||||||
|
// Classic WoW - High - level zones
|
||||||
|
zone2LevelBracket[10] = {19, 33}; // Deadwind Pass
|
||||||
|
zone2LevelBracket[11] = {21, 30}; // Wetlands
|
||||||
|
zone2LevelBracket[44] = {16, 28}; // Redridge Mountains
|
||||||
|
zone2LevelBracket[267] = {20, 34}; // Hillsbrad Foothills
|
||||||
|
zone2LevelBracket[331] = {18, 33}; // Ashenvale
|
||||||
|
zone2LevelBracket[400] = {24, 36}; // Thousand Needles
|
||||||
|
zone2LevelBracket[406] = {16, 29}; // Stonetalon Mountains
|
||||||
|
|
||||||
|
// Classic WoW - Higher - level zones
|
||||||
|
zone2LevelBracket[3] = {36, 46}; // Badlands
|
||||||
|
zone2LevelBracket[8] = {36, 46}; // Swamp of Sorrows
|
||||||
|
zone2LevelBracket[15] = {35, 46}; // Dustwallow Marsh
|
||||||
|
zone2LevelBracket[16] = {45, 52}; // Azshara
|
||||||
|
zone2LevelBracket[33] = {32, 47}; // Stranglethorn Vale
|
||||||
|
zone2LevelBracket[45] = {30, 42}; // Arathi Highlands
|
||||||
|
zone2LevelBracket[47] = {42, 51}; // Hinterlands
|
||||||
|
zone2LevelBracket[51] = {45, 51}; // Searing Gorge
|
||||||
|
zone2LevelBracket[357] = {40, 52}; // Feralas
|
||||||
|
zone2LevelBracket[405] = {30, 41}; // Desolace
|
||||||
|
zone2LevelBracket[440] = {41, 52}; // Tanaris
|
||||||
|
|
||||||
|
// Classic WoW - Top - level zones
|
||||||
|
zone2LevelBracket[4] = {52, 57}; // Blasted Lands
|
||||||
|
zone2LevelBracket[28] = {50, 60}; // Western Plaguelands
|
||||||
|
zone2LevelBracket[46] = {51, 60}; // Burning Steppes
|
||||||
|
zone2LevelBracket[139] = {54, 62}; // Eastern Plaguelands
|
||||||
|
zone2LevelBracket[361] = {47, 57}; // Felwood
|
||||||
|
zone2LevelBracket[490] = {49, 56}; // Un'Goro Crater
|
||||||
|
zone2LevelBracket[618] = {54, 61}; // Winterspring
|
||||||
|
zone2LevelBracket[1377] = {54, 63}; // Silithus
|
||||||
|
|
||||||
|
// The Burning Crusade - Zones
|
||||||
|
zone2LevelBracket[3483] = {58, 66}; // Hellfire Peninsula
|
||||||
|
zone2LevelBracket[3518] = {64, 70}; // Nagrand
|
||||||
|
zone2LevelBracket[3519] = {62, 73}; // Terokkar Forest
|
||||||
|
zone2LevelBracket[3520] = {66, 73}; // Shadowmoon Valley
|
||||||
|
zone2LevelBracket[3521] = {60, 67}; // Zangarmarsh
|
||||||
|
zone2LevelBracket[3522] = {64, 73}; // Blade's Edge Mountains
|
||||||
|
zone2LevelBracket[3523] = {67, 73}; // Netherstorm
|
||||||
|
zone2LevelBracket[4080] = {68, 73}; // Isle of Quel'Danas
|
||||||
|
|
||||||
|
// Wrath of the Lich King - Zones
|
||||||
|
zone2LevelBracket[65] = {71, 77}; // Dragonblight
|
||||||
|
zone2LevelBracket[66] = {74, 80}; // Zul'Drak
|
||||||
|
zone2LevelBracket[67] = {77, 80}; // Storm Peaks
|
||||||
|
zone2LevelBracket[210] = {77, 80}; // Icecrown Glacier
|
||||||
|
zone2LevelBracket[394] = {72, 78}; // Grizzly Hills
|
||||||
|
zone2LevelBracket[495] = {68, 74}; // Howling Fjord
|
||||||
|
zone2LevelBracket[2817] = {77, 80}; // Crystalsong Forest
|
||||||
|
zone2LevelBracket[3537] = {68, 75}; // Borean Tundra
|
||||||
|
zone2LevelBracket[3711] = {75, 80}; // Sholazar Basin
|
||||||
|
zone2LevelBracket[4197] = {79, 80}; // Wintergrasp
|
||||||
|
}
|
||||||
|
|
||||||
void RandomPlayerbotMgr::PrepareTeleportCache()
|
void RandomPlayerbotMgr::PrepareTeleportCache()
|
||||||
{
|
{
|
||||||
uint32 maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
|
uint32 maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
|
||||||
@ -1542,6 +1634,7 @@ void RandomPlayerbotMgr::PrepareTeleportCache()
|
|||||||
|
|
||||||
if (sPlayerbotAIConfig->enableNewRpgStrategy)
|
if (sPlayerbotAIConfig->enableNewRpgStrategy)
|
||||||
{
|
{
|
||||||
|
PrepareZone2LevelBracket();
|
||||||
LOG_INFO("playerbots", "Preparing innkeepers locations for level...");
|
LOG_INFO("playerbots", "Preparing innkeepers locations for level...");
|
||||||
results = WorldDatabase.Query(
|
results = WorldDatabase.Query(
|
||||||
"SELECT "
|
"SELECT "
|
||||||
@ -1550,8 +1643,7 @@ void RandomPlayerbotMgr::PrepareTeleportCache()
|
|||||||
"position_y, "
|
"position_y, "
|
||||||
"position_z, "
|
"position_z, "
|
||||||
"orientation, "
|
"orientation, "
|
||||||
"t.faction, "
|
"t.faction "
|
||||||
"t.entry "
|
|
||||||
"FROM "
|
"FROM "
|
||||||
"creature c "
|
"creature c "
|
||||||
"INNER JOIN creature_template t on c.id1 = t.entry "
|
"INNER JOIN creature_template t on c.id1 = t.entry "
|
||||||
@ -1573,7 +1665,6 @@ void RandomPlayerbotMgr::PrepareTeleportCache()
|
|||||||
float z = fields[3].Get<float>();
|
float z = fields[3].Get<float>();
|
||||||
float orient = fields[4].Get<float>();
|
float orient = fields[4].Get<float>();
|
||||||
uint32 faction = fields[5].Get<uint32>();
|
uint32 faction = fields[5].Get<uint32>();
|
||||||
uint32 c_entry = fields[6].Get<uint32>();
|
|
||||||
const FactionTemplateEntry* entry = sFactionTemplateStore.LookupEntry(faction);
|
const FactionTemplateEntry* entry = sFactionTemplateStore.LookupEntry(faction);
|
||||||
|
|
||||||
WorldLocation loc(mapId, x + cos(orient) * 5.0f, y + sin(orient) * 5.0f, z + 0.5f, orient + M_PI);
|
WorldLocation loc(mapId, x + cos(orient) * 5.0f, y + sin(orient) * 5.0f, z + 0.5f, orient + M_PI);
|
||||||
@ -1581,35 +1672,13 @@ void RandomPlayerbotMgr::PrepareTeleportCache()
|
|||||||
Map* map = sMapMgr->FindMap(loc.GetMapId(), 0);
|
Map* map = sMapMgr->FindMap(loc.GetMapId(), 0);
|
||||||
if (!map)
|
if (!map)
|
||||||
continue;
|
continue;
|
||||||
const AreaTableEntry* area = sAreaTableStore.LookupEntry(map->GetAreaId(1, x, y, z));
|
const AreaTableEntry* area = sAreaTableStore.LookupEntry(map->GetAreaId(PHASEMASK_NORMAL, x, y, z));
|
||||||
uint32 zoneId = area->zone ? area->zone : area->ID;
|
uint32 zoneId = area->zone ? area->zone : area->ID;
|
||||||
uint32 level = area->area_level;
|
if (zone2LevelBracket.find(zoneId) == zone2LevelBracket.end())
|
||||||
for (int i = 5; i <= maxLevel; i++)
|
continue;
|
||||||
|
LevelBracket bracket = zone2LevelBracket[zoneId];
|
||||||
|
for (int i = bracket.low; i <= bracket.high; i++)
|
||||||
{
|
{
|
||||||
std::vector<WorldLocation>& locs = locsPerLevelCache[i];
|
|
||||||
int counter = 0;
|
|
||||||
WorldLocation levelLoc;
|
|
||||||
for (auto& checkLoc : locs)
|
|
||||||
{
|
|
||||||
if (loc.GetMapId() != checkLoc.GetMapId())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (loc.GetExactDist(checkLoc) > 1500.0f)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (zoneId !=
|
|
||||||
map->GetZoneId(1, checkLoc.GetPositionX(), checkLoc.GetPositionY(), checkLoc.GetPositionZ()))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
counter++;
|
|
||||||
levelLoc = checkLoc;
|
|
||||||
if (counter >= 15)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (counter < 15)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!(entry->hostileMask & 4))
|
if (!(entry->hostileMask & 4))
|
||||||
{
|
{
|
||||||
hordeStarterPerLevelCache[i].push_back(loc);
|
hordeStarterPerLevelCache[i].push_back(loc);
|
||||||
@ -1618,12 +1687,10 @@ void RandomPlayerbotMgr::PrepareTeleportCache()
|
|||||||
{
|
{
|
||||||
allianceStarterPerLevelCache[i].push_back(loc);
|
allianceStarterPerLevelCache[i].push_back(loc);
|
||||||
}
|
}
|
||||||
LOG_DEBUG("playerbots", "Area: {} Level: {} creature_entry: {} add to: {} {}({},{},{},{})", area->ID,
|
|
||||||
level, c_entry, i, counter, levelLoc.GetPositionX(), levelLoc.GetPositionY(),
|
|
||||||
levelLoc.GetPositionZ(), levelLoc.GetMapId());
|
|
||||||
}
|
}
|
||||||
} while (results->NextRow());
|
} while (results->NextRow());
|
||||||
}
|
}
|
||||||
|
|
||||||
// add all initial position
|
// add all initial position
|
||||||
for (uint32 i = 1; i < MAX_RACES; i++)
|
for (uint32 i = 1; i < MAX_RACES; i++)
|
||||||
{
|
{
|
||||||
@ -2629,9 +2696,8 @@ void RandomPlayerbotMgr::PrintStats()
|
|||||||
uint32 engine_noncombat = 0;
|
uint32 engine_noncombat = 0;
|
||||||
uint32 engine_combat = 0;
|
uint32 engine_combat = 0;
|
||||||
uint32 engine_dead = 0;
|
uint32 engine_dead = 0;
|
||||||
uint32 stateCount[MAX_TRAVEL_STATE + 1] = {0};
|
|
||||||
std::vector<std::pair<Quest const*, int32>> questCount;
|
|
||||||
std::unordered_map<NewRpgStatus, int> rpgStatusCount;
|
std::unordered_map<NewRpgStatus, int> rpgStatusCount;
|
||||||
|
NewRpgStatistic rpgStasticTotal;
|
||||||
std::unordered_map<uint32, int> zoneCount;
|
std::unordered_map<uint32, int> zoneCount;
|
||||||
uint8 maxBotLevel = 0;
|
uint8 maxBotLevel = 0;
|
||||||
for (PlayerBotMap::iterator i = playerBots.begin(); i != playerBots.end(); ++i)
|
for (PlayerBotMap::iterator i = playerBots.begin(); i != playerBots.end(); ++i)
|
||||||
@ -2706,41 +2772,19 @@ void RandomPlayerbotMgr::PrintStats()
|
|||||||
zoneCount[bot->GetZoneId()]++;
|
zoneCount[bot->GetZoneId()]++;
|
||||||
|
|
||||||
if (sPlayerbotAIConfig->enableNewRpgStrategy)
|
if (sPlayerbotAIConfig->enableNewRpgStrategy)
|
||||||
|
{
|
||||||
rpgStatusCount[botAI->rpgInfo.status]++;
|
rpgStatusCount[botAI->rpgInfo.status]++;
|
||||||
|
rpgStasticTotal += botAI->rpgStatistic;
|
||||||
if (TravelTarget* target = botAI->GetAiObjectContext()->GetValue<TravelTarget*>("travel target")->Get())
|
|
||||||
{
|
|
||||||
TravelState state = target->getTravelState();
|
|
||||||
stateCount[state]++;
|
|
||||||
|
|
||||||
Quest const* quest;
|
|
||||||
if (target->getDestination())
|
|
||||||
quest = target->getDestination()->GetQuestTemplate();
|
|
||||||
|
|
||||||
if (quest)
|
|
||||||
{
|
|
||||||
bool found = false;
|
|
||||||
|
|
||||||
for (auto& q : questCount)
|
|
||||||
{
|
|
||||||
if (q.first != quest)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
q.second++;
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found)
|
|
||||||
questCount.push_back(std::make_pair(quest, 1));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
LOG_INFO("playerbots", "Bots level:");
|
LOG_INFO("playerbots", "Bots level:");
|
||||||
// uint32 maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
|
// uint32 maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
|
||||||
uint32 currentAlliance = 0, currentHorde = 0;
|
uint32_t currentAlliance = 0, currentHorde = 0;
|
||||||
uint32 step = std::max(1, (maxBotLevel + 4) / 8);
|
uint32_t step = std::max(1, static_cast<int>((maxBotLevel + 4) / 8));
|
||||||
uint32 from = 1;
|
uint32_t from = 1;
|
||||||
|
|
||||||
for (uint8 i = 1; i <= maxBotLevel; ++i)
|
for (uint8 i = 1; i <= maxBotLevel; ++i)
|
||||||
{
|
{
|
||||||
currentAlliance += alliance[i];
|
currentAlliance += alliance[i];
|
||||||
@ -2800,19 +2844,18 @@ void RandomPlayerbotMgr::PrintStats()
|
|||||||
|
|
||||||
if (sPlayerbotAIConfig->enableNewRpgStrategy)
|
if (sPlayerbotAIConfig->enableNewRpgStrategy)
|
||||||
{
|
{
|
||||||
LOG_INFO("playerbots", "Bots rpg status:", dead);
|
LOG_INFO("playerbots", "Bots rpg status:");
|
||||||
LOG_INFO("playerbots", " IDLE: {}", rpgStatusCount[NewRpgStatus::IDLE]);
|
LOG_INFO("playerbots", " Idle: {}, Rest: {}, GoGrind: {}, GoInnkeeper: {}, MoveRandom: {}, MoveNpc: {}, DoQuest: {}",
|
||||||
LOG_INFO("playerbots", " REST: {}", rpgStatusCount[NewRpgStatus::REST]);
|
rpgStatusCount[RPG_IDLE], rpgStatusCount[RPG_REST], rpgStatusCount[RPG_GO_GRIND], rpgStatusCount[RPG_GO_INNKEEPER],
|
||||||
LOG_INFO("playerbots", " GO_GRIND: {}", rpgStatusCount[NewRpgStatus::GO_GRIND]);
|
rpgStatusCount[RPG_NEAR_RANDOM], rpgStatusCount[RPG_NEAR_NPC], rpgStatusCount[RPG_DO_QUEST]);
|
||||||
LOG_INFO("playerbots", " GO_INNKEEPER: {}", rpgStatusCount[NewRpgStatus::GO_INNKEEPER]);
|
|
||||||
LOG_INFO("playerbots", " NEAR_RANDOM: {}", rpgStatusCount[NewRpgStatus::NEAR_RANDOM]);
|
LOG_INFO("playerbots", "Bots total quests:");
|
||||||
LOG_INFO("playerbots", " NEAR_NPC: {}", rpgStatusCount[NewRpgStatus::NEAR_NPC]);
|
LOG_INFO("playerbots", " Accepted: {}, Rewarded: {}, Dropped: {}",
|
||||||
|
rpgStasticTotal.questAccepted, rpgStasticTotal.questRewarded, rpgStasticTotal.questDropped);
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_INFO("playerbots", "Bots engine:", dead);
|
LOG_INFO("playerbots", "Bots engine:", dead);
|
||||||
LOG_INFO("playerbots", " Non-combat: {}", engine_noncombat);
|
LOG_INFO("playerbots", " Non-combat: {}, Combat: {}, Dead: {}", engine_noncombat, engine_combat, engine_dead);
|
||||||
LOG_INFO("playerbots", " Combat: {}", engine_combat);
|
|
||||||
LOG_INFO("playerbots", " Dead: {}", engine_dead);
|
|
||||||
|
|
||||||
// LOG_INFO("playerbots", "Bots questing:");
|
// LOG_INFO("playerbots", "Bots questing:");
|
||||||
// LOG_INFO("playerbots", " Picking quests: {}",
|
// LOG_INFO("playerbots", " Picking quests: {}",
|
||||||
|
@ -171,12 +171,19 @@ public:
|
|||||||
static uint8 GetTeamClassIdx(bool isAlliance, uint8 claz) { return isAlliance * 20 + claz; }
|
static uint8 GetTeamClassIdx(bool isAlliance, uint8 claz) { return isAlliance * 20 + claz; }
|
||||||
|
|
||||||
void PrepareAddclassCache();
|
void PrepareAddclassCache();
|
||||||
|
void PrepareZone2LevelBracket();
|
||||||
void PrepareTeleportCache();
|
void PrepareTeleportCache();
|
||||||
void Init();
|
void Init();
|
||||||
std::map<uint8, std::unordered_set<ObjectGuid>> addclassCache;
|
std::map<uint8, std::unordered_set<ObjectGuid>> addclassCache;
|
||||||
std::map<uint8, std::vector<WorldLocation>> locsPerLevelCache;
|
std::map<uint8, std::vector<WorldLocation>> locsPerLevelCache;
|
||||||
std::map<uint8, std::vector<WorldLocation>> allianceStarterPerLevelCache;
|
std::map<uint8, std::vector<WorldLocation>> allianceStarterPerLevelCache;
|
||||||
std::map<uint8, std::vector<WorldLocation>> hordeStarterPerLevelCache;
|
std::map<uint8, std::vector<WorldLocation>> hordeStarterPerLevelCache;
|
||||||
|
struct LevelBracket {
|
||||||
|
uint32 low;
|
||||||
|
uint32 high;
|
||||||
|
bool InsideBracket(uint32 val) { return val >= low && val <= high; }
|
||||||
|
};
|
||||||
|
std::map<uint32, LevelBracket> zone2LevelBracket;
|
||||||
std::map<uint8, std::vector<WorldLocation>> bankerLocsPerLevelCache;
|
std::map<uint8, std::vector<WorldLocation>> bankerLocsPerLevelCache;
|
||||||
protected:
|
protected:
|
||||||
void OnBotLoginInternal(Player* const bot) override;
|
void OnBotLoginInternal(Player* const bot) override;
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
#include "PlayerbotAIConfig.h"
|
#include "PlayerbotAIConfig.h"
|
||||||
#include "PlayerbotDbStore.h"
|
#include "PlayerbotDbStore.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
#include "QuestDef.h"
|
||||||
#include "RandomItemMgr.h"
|
#include "RandomItemMgr.h"
|
||||||
#include "RandomPlayerbotFactory.h"
|
#include "RandomPlayerbotFactory.h"
|
||||||
#include "ReputationMgr.h"
|
#include "ReputationMgr.h"
|
||||||
@ -979,30 +980,23 @@ void PlayerbotFactory::ClearSpells()
|
|||||||
|
|
||||||
void PlayerbotFactory::ResetQuests()
|
void PlayerbotFactory::ResetQuests()
|
||||||
{
|
{
|
||||||
|
for (uint8 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot)
|
||||||
|
{
|
||||||
|
bot->SetQuestSlot(slot, 0);
|
||||||
|
}
|
||||||
ObjectMgr::QuestMap const& questTemplates = sObjectMgr->GetQuestTemplates();
|
ObjectMgr::QuestMap const& questTemplates = sObjectMgr->GetQuestTemplates();
|
||||||
for (ObjectMgr::QuestMap::const_iterator i = questTemplates.begin(); i != questTemplates.end(); ++i)
|
for (ObjectMgr::QuestMap::const_iterator i = questTemplates.begin(); i != questTemplates.end(); ++i)
|
||||||
{
|
{
|
||||||
Quest const* quest = i->second;
|
Quest const* quest = i->second;
|
||||||
|
|
||||||
uint32 entry = quest->GetQuestId();
|
uint32 entry = quest->GetQuestId();
|
||||||
|
if (bot->GetQuestStatus(entry) == QUEST_STATUS_NONE)
|
||||||
|
continue;
|
||||||
|
|
||||||
// remove all quest entries for 'entry' from quest log
|
bot->RemoveRewardedQuest(entry);
|
||||||
for (uint8 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot)
|
bot->RemoveActiveQuest(entry, false);
|
||||||
{
|
|
||||||
uint32 quest = bot->GetQuestSlotQuestId(slot);
|
|
||||||
if (quest == entry)
|
|
||||||
{
|
|
||||||
bot->SetQuestSlot(slot, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// reset rewarded for restart repeatable quest
|
|
||||||
bot->getQuestStatusMap().erase(entry);
|
|
||||||
// bot->getQuestStatusMap()[entry].m_rewarded = false;
|
|
||||||
// bot->getQuestStatusMap()[entry].m_status = QUEST_STATUS_NONE;
|
|
||||||
}
|
}
|
||||||
// bot->UpdateForQuestWorldObjects();
|
|
||||||
CharacterDatabase.Execute("DELETE FROM character_queststatus WHERE guid = {}", bot->GetGUID().GetCounter());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerbotFactory::InitSpells() { InitAvailableSpells(); }
|
void PlayerbotFactory::InitSpells() { InitAvailableSpells(); }
|
||||||
@ -1618,6 +1612,11 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance)
|
|||||||
if (level < 20 && (slot == EQUIPMENT_SLOT_FINGER1 || slot == EQUIPMENT_SLOT_FINGER2))
|
if (level < 20 && (slot == EQUIPMENT_SLOT_FINGER1 || slot == EQUIPMENT_SLOT_FINGER2))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (level < 5 && (slot != EQUIPMENT_SLOT_MAINHAND) && (slot != EQUIPMENT_SLOT_OFFHAND) &&
|
||||||
|
(slot != EQUIPMENT_SLOT_FEET) && (slot != EQUIPMENT_SLOT_LEGS) && (slot != EQUIPMENT_SLOT_CHEST) &&
|
||||||
|
(slot != EQUIPMENT_SLOT_RANGED))
|
||||||
|
continue;
|
||||||
|
|
||||||
Item* oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
|
Item* oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
|
||||||
|
|
||||||
if (second_chance && oldItem)
|
if (second_chance && oldItem)
|
||||||
@ -1783,6 +1782,10 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance)
|
|||||||
if (level < 20 && (slot == EQUIPMENT_SLOT_FINGER1 || slot == EQUIPMENT_SLOT_FINGER2))
|
if (level < 20 && (slot == EQUIPMENT_SLOT_FINGER1 || slot == EQUIPMENT_SLOT_FINGER2))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (level < 5 && (slot != EQUIPMENT_SLOT_MAINHAND) && (slot != EQUIPMENT_SLOT_OFFHAND) &&
|
||||||
|
(slot != EQUIPMENT_SLOT_FEET) && (slot != EQUIPMENT_SLOT_LEGS) && (slot != EQUIPMENT_SLOT_CHEST))
|
||||||
|
continue;
|
||||||
|
|
||||||
if (Item* oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
|
if (Item* oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
|
||||||
bot->DestroyItem(INVENTORY_SLOT_BAG_0, slot, true);
|
bot->DestroyItem(INVENTORY_SLOT_BAG_0, slot, true);
|
||||||
|
|
||||||
|
@ -87,7 +87,7 @@ bool AcceptQuestAction::Execute(Event event)
|
|||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "AcceptQuestAction [" << qInfo->GetTitle() << "] - [" << std::to_string(qInfo->GetQuestId()) << "]";
|
ss << "AcceptQuestAction [" << qInfo->GetTitle() << "] - [" << std::to_string(qInfo->GetQuestId()) << "]";
|
||||||
LOG_INFO("playerbots", "{}", ss.str().c_str());
|
LOG_DEBUG("playerbots", "{}", ss.str().c_str());
|
||||||
// botAI->TellMaster(ss.str());
|
// botAI->TellMaster(ss.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,6 +135,7 @@ public:
|
|||||||
creators["move to loot"] = &ActionContext::move_to_loot;
|
creators["move to loot"] = &ActionContext::move_to_loot;
|
||||||
creators["open loot"] = &ActionContext::open_loot;
|
creators["open loot"] = &ActionContext::open_loot;
|
||||||
creators["guard"] = &ActionContext::guard;
|
creators["guard"] = &ActionContext::guard;
|
||||||
|
creators["return to stay position"] = &ActionContext::return_to_stay_position;
|
||||||
creators["move out of enemy contact"] = &ActionContext::move_out_of_enemy_contact;
|
creators["move out of enemy contact"] = &ActionContext::move_out_of_enemy_contact;
|
||||||
creators["set facing"] = &ActionContext::set_facing;
|
creators["set facing"] = &ActionContext::set_facing;
|
||||||
creators["set behind"] = &ActionContext::set_behind;
|
creators["set behind"] = &ActionContext::set_behind;
|
||||||
@ -247,6 +248,7 @@ public:
|
|||||||
creators["new rpg go innkeeper"] = &ActionContext::new_rpg_go_innkeeper;
|
creators["new rpg go innkeeper"] = &ActionContext::new_rpg_go_innkeeper;
|
||||||
creators["new rpg move random"] = &ActionContext::new_rpg_move_random;
|
creators["new rpg move random"] = &ActionContext::new_rpg_move_random;
|
||||||
creators["new rpg move npc"] = &ActionContext::new_rpg_move_npc;
|
creators["new rpg move npc"] = &ActionContext::new_rpg_move_npc;
|
||||||
|
creators["new rpg do quest"] = &ActionContext::new_rpg_do_quest;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -270,6 +272,7 @@ private:
|
|||||||
static Action* drop_target(PlayerbotAI* botAI) { return new DropTargetAction(botAI); }
|
static Action* drop_target(PlayerbotAI* botAI) { return new DropTargetAction(botAI); }
|
||||||
static Action* attack_duel_opponent(PlayerbotAI* botAI) { return new AttackDuelOpponentAction(botAI); }
|
static Action* attack_duel_opponent(PlayerbotAI* botAI) { return new AttackDuelOpponentAction(botAI); }
|
||||||
static Action* guard(PlayerbotAI* botAI) { return new GuardAction(botAI); }
|
static Action* guard(PlayerbotAI* botAI) { return new GuardAction(botAI); }
|
||||||
|
static Action* return_to_stay_position(PlayerbotAI* botAI) { return new ReturnToStayPositionAction(botAI); }
|
||||||
static Action* open_loot(PlayerbotAI* botAI) { return new OpenLootAction(botAI); }
|
static Action* open_loot(PlayerbotAI* botAI) { return new OpenLootAction(botAI); }
|
||||||
static Action* move_to_loot(PlayerbotAI* botAI) { return new MoveToLootAction(botAI); }
|
static Action* move_to_loot(PlayerbotAI* botAI) { return new MoveToLootAction(botAI); }
|
||||||
static Action* _return(PlayerbotAI* botAI) { return new ReturnAction(botAI); }
|
static Action* _return(PlayerbotAI* botAI) { return new ReturnAction(botAI); }
|
||||||
@ -428,6 +431,7 @@ private:
|
|||||||
static Action* new_rpg_go_innkeeper(PlayerbotAI* ai) { return new NewRpgGoInnKeeperAction(ai); }
|
static Action* new_rpg_go_innkeeper(PlayerbotAI* ai) { return new NewRpgGoInnKeeperAction(ai); }
|
||||||
static Action* new_rpg_move_random(PlayerbotAI* ai) { return new NewRpgMoveRandomAction(ai); }
|
static Action* new_rpg_move_random(PlayerbotAI* ai) { return new NewRpgMoveRandomAction(ai); }
|
||||||
static Action* new_rpg_move_npc(PlayerbotAI* ai) { return new NewRpgMoveNpcAction(ai); }
|
static Action* new_rpg_move_npc(PlayerbotAI* ai) { return new NewRpgMoveNpcAction(ai); }
|
||||||
|
static Action* new_rpg_do_quest(PlayerbotAI* ai) { return new NewRpgDoQuestAction(ai); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -75,6 +75,8 @@
|
|||||||
#include "WhoAction.h"
|
#include "WhoAction.h"
|
||||||
#include "WtsAction.h"
|
#include "WtsAction.h"
|
||||||
#include "OpenItemAction.h"
|
#include "OpenItemAction.h"
|
||||||
|
#include "UnlockItemAction.h"
|
||||||
|
#include "UnlockTradedItemAction.h"
|
||||||
|
|
||||||
class ChatActionContext : public NamedObjectContext<Action>
|
class ChatActionContext : public NamedObjectContext<Action>
|
||||||
{
|
{
|
||||||
@ -82,6 +84,8 @@ public:
|
|||||||
ChatActionContext()
|
ChatActionContext()
|
||||||
{
|
{
|
||||||
creators["open items"] = &ChatActionContext::open_items;
|
creators["open items"] = &ChatActionContext::open_items;
|
||||||
|
creators["unlock items"] = &ChatActionContext::unlock_items;
|
||||||
|
creators["unlock traded item"] = &ChatActionContext::unlock_traded_item;
|
||||||
creators["range"] = &ChatActionContext::range;
|
creators["range"] = &ChatActionContext::range;
|
||||||
creators["stats"] = &ChatActionContext::stats;
|
creators["stats"] = &ChatActionContext::stats;
|
||||||
creators["quests"] = &ChatActionContext::quests;
|
creators["quests"] = &ChatActionContext::quests;
|
||||||
@ -90,6 +94,7 @@ public:
|
|||||||
creators["log"] = &ChatActionContext::log;
|
creators["log"] = &ChatActionContext::log;
|
||||||
creators["los"] = &ChatActionContext::los;
|
creators["los"] = &ChatActionContext::los;
|
||||||
creators["rpg status"] = &ChatActionContext::rpg_status;
|
creators["rpg status"] = &ChatActionContext::rpg_status;
|
||||||
|
creators["rpg do quest"] = &ChatActionContext::rpg_do_quest;
|
||||||
creators["aura"] = &ChatActionContext::aura;
|
creators["aura"] = &ChatActionContext::aura;
|
||||||
creators["drop"] = &ChatActionContext::drop;
|
creators["drop"] = &ChatActionContext::drop;
|
||||||
creators["clean quest log"] = &ChatActionContext::clean_quest_log;
|
creators["clean quest log"] = &ChatActionContext::clean_quest_log;
|
||||||
@ -183,6 +188,8 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
static Action* open_items(PlayerbotAI* botAI) { return new OpenItemAction(botAI); }
|
static Action* open_items(PlayerbotAI* botAI) { return new OpenItemAction(botAI); }
|
||||||
|
static Action* unlock_items(PlayerbotAI* botAI) { return new UnlockItemAction(botAI); }
|
||||||
|
static Action* unlock_traded_item(PlayerbotAI* botAI) { return new UnlockTradedItemAction(botAI); }
|
||||||
static Action* range(PlayerbotAI* botAI) { return new RangeAction(botAI); }
|
static Action* range(PlayerbotAI* botAI) { return new RangeAction(botAI); }
|
||||||
static Action* flag(PlayerbotAI* botAI) { return new FlagAction(botAI); }
|
static Action* flag(PlayerbotAI* botAI) { return new FlagAction(botAI); }
|
||||||
static Action* craft(PlayerbotAI* botAI) { return new SetCraftAction(botAI); }
|
static Action* craft(PlayerbotAI* botAI) { return new SetCraftAction(botAI); }
|
||||||
@ -261,6 +268,7 @@ private:
|
|||||||
static Action* log(PlayerbotAI* botAI) { return new LogLevelAction(botAI); }
|
static Action* log(PlayerbotAI* botAI) { return new LogLevelAction(botAI); }
|
||||||
static Action* los(PlayerbotAI* botAI) { return new TellLosAction(botAI); }
|
static Action* los(PlayerbotAI* botAI) { return new TellLosAction(botAI); }
|
||||||
static Action* rpg_status(PlayerbotAI* botAI) { return new TellRpgStatusAction(botAI); }
|
static Action* rpg_status(PlayerbotAI* botAI) { return new TellRpgStatusAction(botAI); }
|
||||||
|
static Action* rpg_do_quest(PlayerbotAI* botAI) { return new StartRpgDoQuestAction(botAI); }
|
||||||
static Action* aura(PlayerbotAI* ai) { return new TellAuraAction(ai); }
|
static Action* aura(PlayerbotAI* ai) { return new TellAuraAction(ai); }
|
||||||
static Action* ll(PlayerbotAI* botAI) { return new LootStrategyAction(botAI); }
|
static Action* ll(PlayerbotAI* botAI) { return new LootStrategyAction(botAI); }
|
||||||
static Action* ss(PlayerbotAI* botAI) { return new SkipSpellsListAction(botAI); }
|
static Action* ss(PlayerbotAI* botAI) { return new SkipSpellsListAction(botAI); }
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "PositionValue.h"
|
#include "PositionValue.h"
|
||||||
|
|
||||||
void ReturnPositionResetAction::ResetReturnPosition()
|
void PositionsResetAction::ResetReturnPosition()
|
||||||
{
|
{
|
||||||
PositionMap& posMap = context->GetValue<PositionMap&>("position")->Get();
|
PositionMap& posMap = context->GetValue<PositionMap&>("position")->Get();
|
||||||
PositionInfo pos = posMap["return"];
|
PositionInfo pos = posMap["return"];
|
||||||
@ -18,7 +18,7 @@ void ReturnPositionResetAction::ResetReturnPosition()
|
|||||||
posMap["return"] = pos;
|
posMap["return"] = pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReturnPositionResetAction::SetReturnPosition(float x, float y, float z)
|
void PositionsResetAction::SetReturnPosition(float x, float y, float z)
|
||||||
{
|
{
|
||||||
PositionMap& posMap = context->GetValue<PositionMap&>("position")->Get();
|
PositionMap& posMap = context->GetValue<PositionMap&>("position")->Get();
|
||||||
PositionInfo pos = posMap["return"];
|
PositionInfo pos = posMap["return"];
|
||||||
@ -26,6 +26,22 @@ void ReturnPositionResetAction::SetReturnPosition(float x, float y, float z)
|
|||||||
posMap["return"] = pos;
|
posMap["return"] = pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PositionsResetAction::ResetStayPosition()
|
||||||
|
{
|
||||||
|
PositionMap& posMap = context->GetValue<PositionMap&>("position")->Get();
|
||||||
|
PositionInfo pos = posMap["stay"];
|
||||||
|
pos.Reset();
|
||||||
|
posMap["stay"] = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PositionsResetAction::SetStayPosition(float x, float y, float z)
|
||||||
|
{
|
||||||
|
PositionMap& posMap = context->GetValue<PositionMap&>("position")->Get();
|
||||||
|
PositionInfo pos = posMap["stay"];
|
||||||
|
pos.Set(x, y, z, botAI->GetBot()->GetMapId());
|
||||||
|
posMap["stay"] = pos;
|
||||||
|
}
|
||||||
|
|
||||||
bool FollowChatShortcutAction::Execute(Event event)
|
bool FollowChatShortcutAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
Player* master = GetMaster();
|
Player* master = GetMaster();
|
||||||
@ -34,7 +50,7 @@ bool FollowChatShortcutAction::Execute(Event event)
|
|||||||
|
|
||||||
// botAI->Reset();
|
// botAI->Reset();
|
||||||
botAI->ChangeStrategy("+follow,-passive,-grind,-move from group", BOT_STATE_NON_COMBAT);
|
botAI->ChangeStrategy("+follow,-passive,-grind,-move from group", BOT_STATE_NON_COMBAT);
|
||||||
botAI->ChangeStrategy("-follow,-passive,-grind,-move from group", BOT_STATE_COMBAT);
|
botAI->ChangeStrategy("-stay,-follow,-passive,-grind,-move from group", BOT_STATE_COMBAT);
|
||||||
botAI->GetAiObjectContext()->GetValue<GuidVector>("prioritized targets")->Reset();
|
botAI->GetAiObjectContext()->GetValue<GuidVector>("prioritized targets")->Reset();
|
||||||
|
|
||||||
PositionMap& posMap = context->GetValue<PositionMap&>("position")->Get();
|
PositionMap& posMap = context->GetValue<PositionMap&>("position")->Get();
|
||||||
@ -42,6 +58,10 @@ bool FollowChatShortcutAction::Execute(Event event)
|
|||||||
pos.Reset();
|
pos.Reset();
|
||||||
posMap["return"] = pos;
|
posMap["return"] = pos;
|
||||||
|
|
||||||
|
pos = posMap["stay"];
|
||||||
|
pos.Reset();
|
||||||
|
posMap["stay"] = pos;
|
||||||
|
|
||||||
if (bot->IsInCombat())
|
if (bot->IsInCombat())
|
||||||
{
|
{
|
||||||
Formation* formation = AI_VALUE(Formation*, "formation");
|
Formation* formation = AI_VALUE(Formation*, "formation");
|
||||||
@ -103,9 +123,10 @@ bool StayChatShortcutAction::Execute(Event event)
|
|||||||
|
|
||||||
botAI->Reset();
|
botAI->Reset();
|
||||||
botAI->ChangeStrategy("+stay,-passive,-move from group", BOT_STATE_NON_COMBAT);
|
botAI->ChangeStrategy("+stay,-passive,-move from group", BOT_STATE_NON_COMBAT);
|
||||||
botAI->ChangeStrategy("-follow,-passive,-move from group", BOT_STATE_COMBAT);
|
botAI->ChangeStrategy("+stay,-follow,-passive,-move from group", BOT_STATE_COMBAT);
|
||||||
|
|
||||||
SetReturnPosition(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ());
|
SetReturnPosition(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ());
|
||||||
|
SetStayPosition(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ());
|
||||||
|
|
||||||
botAI->TellMaster("Staying");
|
botAI->TellMaster("Staying");
|
||||||
return true;
|
return true;
|
||||||
@ -133,10 +154,11 @@ bool FleeChatShortcutAction::Execute(Event event)
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
botAI->Reset();
|
botAI->Reset();
|
||||||
botAI->ChangeStrategy("+follow,+passive", BOT_STATE_NON_COMBAT);
|
botAI->ChangeStrategy("+follow,-stay,+passive", BOT_STATE_NON_COMBAT);
|
||||||
botAI->ChangeStrategy("+follow,+passive", BOT_STATE_COMBAT);
|
botAI->ChangeStrategy("+follow,-stay,+passive", BOT_STATE_COMBAT);
|
||||||
|
|
||||||
ResetReturnPosition();
|
ResetReturnPosition();
|
||||||
|
ResetStayPosition();
|
||||||
|
|
||||||
if (bot->GetMapId() != master->GetMapId() || bot->GetDistance(master) > sPlayerbotAIConfig->sightDistance)
|
if (bot->GetMapId() != master->GetMapId() || bot->GetDistance(master) > sPlayerbotAIConfig->sightDistance)
|
||||||
{
|
{
|
||||||
@ -155,10 +177,11 @@ bool GoawayChatShortcutAction::Execute(Event event)
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
botAI->Reset();
|
botAI->Reset();
|
||||||
botAI->ChangeStrategy("+runaway", BOT_STATE_NON_COMBAT);
|
botAI->ChangeStrategy("+runaway,-stay", BOT_STATE_NON_COMBAT);
|
||||||
botAI->ChangeStrategy("+runaway", BOT_STATE_COMBAT);
|
botAI->ChangeStrategy("+runaway,-stay", BOT_STATE_COMBAT);
|
||||||
|
|
||||||
ResetReturnPosition();
|
ResetReturnPosition();
|
||||||
|
ResetStayPosition();
|
||||||
|
|
||||||
botAI->TellMaster("Running away");
|
botAI->TellMaster("Running away");
|
||||||
return true;
|
return true;
|
||||||
@ -171,9 +194,10 @@ bool GrindChatShortcutAction::Execute(Event event)
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
botAI->Reset();
|
botAI->Reset();
|
||||||
botAI->ChangeStrategy("+grind,-passive", BOT_STATE_NON_COMBAT);
|
botAI->ChangeStrategy("+grind,-passive,-stay", BOT_STATE_NON_COMBAT);
|
||||||
|
|
||||||
ResetReturnPosition();
|
ResetReturnPosition();
|
||||||
|
ResetStayPosition();
|
||||||
|
|
||||||
botAI->TellMaster("Grinding");
|
botAI->TellMaster("Grinding");
|
||||||
return true;
|
return true;
|
||||||
@ -193,6 +217,7 @@ bool TankAttackChatShortcutAction::Execute(Event event)
|
|||||||
botAI->ChangeStrategy("-passive", BOT_STATE_COMBAT);
|
botAI->ChangeStrategy("-passive", BOT_STATE_COMBAT);
|
||||||
|
|
||||||
ResetReturnPosition();
|
ResetReturnPosition();
|
||||||
|
ResetStayPosition();
|
||||||
|
|
||||||
botAI->TellMaster("Attacking");
|
botAI->TellMaster("Attacking");
|
||||||
return true;
|
return true;
|
||||||
|
@ -10,13 +10,15 @@
|
|||||||
|
|
||||||
class PlayerbotAI;
|
class PlayerbotAI;
|
||||||
|
|
||||||
class ReturnPositionResetAction : public Action
|
class PositionsResetAction : public Action
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ReturnPositionResetAction(PlayerbotAI* botAI, std::string const name) : Action(botAI, name) {}
|
PositionsResetAction(PlayerbotAI* botAI, std::string const name) : Action(botAI, name) {}
|
||||||
|
|
||||||
void ResetReturnPosition();
|
void ResetReturnPosition();
|
||||||
void SetReturnPosition(float x, float y, float z);
|
void SetReturnPosition(float x, float y, float z);
|
||||||
|
void ResetStayPosition();
|
||||||
|
void SetStayPosition(float x, float y, float z);
|
||||||
};
|
};
|
||||||
|
|
||||||
class FollowChatShortcutAction : public MovementAction
|
class FollowChatShortcutAction : public MovementAction
|
||||||
@ -27,10 +29,10 @@ public:
|
|||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class StayChatShortcutAction : public ReturnPositionResetAction
|
class StayChatShortcutAction : public PositionsResetAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
StayChatShortcutAction(PlayerbotAI* botAI) : ReturnPositionResetAction(botAI, "stay chat shortcut") {}
|
StayChatShortcutAction(PlayerbotAI* botAI) : PositionsResetAction(botAI, "stay chat shortcut") {}
|
||||||
|
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
};
|
};
|
||||||
@ -43,34 +45,34 @@ public:
|
|||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FleeChatShortcutAction : public ReturnPositionResetAction
|
class FleeChatShortcutAction : public PositionsResetAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FleeChatShortcutAction(PlayerbotAI* botAI) : ReturnPositionResetAction(botAI, "flee chat shortcut") {}
|
FleeChatShortcutAction(PlayerbotAI* botAI) : PositionsResetAction(botAI, "flee chat shortcut") {}
|
||||||
|
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class GoawayChatShortcutAction : public ReturnPositionResetAction
|
class GoawayChatShortcutAction : public PositionsResetAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
GoawayChatShortcutAction(PlayerbotAI* botAI) : ReturnPositionResetAction(botAI, "runaway chat shortcut") {}
|
GoawayChatShortcutAction(PlayerbotAI* botAI) : PositionsResetAction(botAI, "runaway chat shortcut") {}
|
||||||
|
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class GrindChatShortcutAction : public ReturnPositionResetAction
|
class GrindChatShortcutAction : public PositionsResetAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
GrindChatShortcutAction(PlayerbotAI* botAI) : ReturnPositionResetAction(botAI, "grind chat shortcut") {}
|
GrindChatShortcutAction(PlayerbotAI* botAI) : PositionsResetAction(botAI, "grind chat shortcut") {}
|
||||||
|
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class TankAttackChatShortcutAction : public ReturnPositionResetAction
|
class TankAttackChatShortcutAction : public PositionsResetAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TankAttackChatShortcutAction(PlayerbotAI* botAI) : ReturnPositionResetAction(botAI, "tank attack chat shortcut") {}
|
TankAttackChatShortcutAction(PlayerbotAI* botAI) : PositionsResetAction(botAI, "tank attack chat shortcut") {}
|
||||||
|
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
};
|
};
|
||||||
|
@ -34,28 +34,17 @@ bool AttackAnythingAction::isUseful()
|
|||||||
if (!botAI->AllowActivity(GRIND_ACTIVITY)) // Bot not allowed to be active
|
if (!botAI->AllowActivity(GRIND_ACTIVITY)) // Bot not allowed to be active
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!AI_VALUE(bool, "can move around"))
|
if (botAI->HasStrategy("stay", BOT_STATE_NON_COMBAT))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (bot->IsInCombat())
|
||||||
// if (context->GetValue<TravelTarget*>("travel target")->Get()->isTraveling() &&
|
return false;
|
||||||
// ChooseRpgTargetAction::isFollowValid(
|
|
||||||
// bot, *context->GetValue<TravelTarget*>("travel target")->Get()->getPosition())) // Bot is traveling
|
|
||||||
// return false;
|
|
||||||
|
|
||||||
Unit* target = GetTarget();
|
Unit* target = GetTarget();
|
||||||
|
|
||||||
if (!target)
|
if (!target)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
bool inactiveGrindStatus = botAI->rpgInfo.status == NewRpgStatus::GO_GRIND ||
|
|
||||||
botAI->rpgInfo.status == NewRpgStatus::NEAR_NPC ||
|
|
||||||
botAI->rpgInfo.status == NewRpgStatus::REST ||
|
|
||||||
botAI->rpgInfo.status == NewRpgStatus::GO_INNKEEPER;
|
|
||||||
|
|
||||||
if (inactiveGrindStatus && bot->GetDistance(target) > 25.0f)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
std::string const name = std::string(target->GetName());
|
std::string const name = std::string(target->GetName());
|
||||||
// Check for invalid targets: Dummy, Charge Target, Melee Target, Ranged Target
|
// Check for invalid targets: Dummy, Charge Target, Melee Target, Ranged Target
|
||||||
if (!name.empty() &&
|
if (!name.empty() &&
|
||||||
|
@ -132,6 +132,7 @@ bool CleanQuestLogAction::Execute(Event event)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove quest
|
// Remove quest
|
||||||
|
botAI->rpgStatistic.questDropped++;
|
||||||
bot->SetQuestSlot(slot, 0);
|
bot->SetQuestSlot(slot, 0);
|
||||||
bot->TakeQuestSourceItem(questId, false);
|
bot->TakeQuestSourceItem(questId, false);
|
||||||
bot->SetQuestStatus(questId, QUEST_STATUS_NONE);
|
bot->SetQuestStatus(questId, QUEST_STATUS_NONE);
|
||||||
|
@ -4,64 +4,23 @@
|
|||||||
#include "WorldPacket.h"
|
#include "WorldPacket.h"
|
||||||
#include "Player.h"
|
#include "Player.h"
|
||||||
#include "ObjectMgr.h"
|
#include "ObjectMgr.h"
|
||||||
|
|
||||||
bool OpenItemAction::Execute(Event event)
|
bool OpenItemAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
bool foundOpenable = false;
|
bool foundOpenable = false;
|
||||||
|
|
||||||
// Check main inventory slots
|
Item* item = botAI->FindOpenableItem();
|
||||||
for (uint8 slot = EQUIPMENT_SLOT_START; slot < INVENTORY_SLOT_ITEM_END; ++slot)
|
if (item)
|
||||||
{
|
{
|
||||||
Item* item = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
|
uint8 bag = item->GetBagSlot(); // Retrieves the bag slot (0 for main inventory)
|
||||||
|
uint8 slot = item->GetSlot(); // Retrieves the actual slot inside the bag
|
||||||
|
|
||||||
if (item && CanOpenItem(item))
|
|
||||||
{
|
|
||||||
OpenItem(item, INVENTORY_SLOT_BAG_0, slot);
|
|
||||||
foundOpenable = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check items in the bags
|
|
||||||
for (uint8 bag = INVENTORY_SLOT_BAG_START; bag < INVENTORY_SLOT_BAG_END; ++bag)
|
|
||||||
{
|
|
||||||
Bag* bagItem = bot->GetBagByPos(bag);
|
|
||||||
if (!bagItem)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
for (uint32 slot = 0; slot < bagItem->GetBagSize(); ++slot)
|
|
||||||
{
|
|
||||||
Item* item = bot->GetItemByPos(bag, slot);
|
|
||||||
|
|
||||||
if (item && CanOpenItem(item))
|
|
||||||
{
|
|
||||||
OpenItem(item, bag, slot);
|
OpenItem(item, bag, slot);
|
||||||
foundOpenable = true;
|
foundOpenable = true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no openable items found
|
|
||||||
if (!foundOpenable)
|
|
||||||
{
|
|
||||||
botAI->TellError("No openable items in inventory.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return foundOpenable;
|
return foundOpenable;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenItemAction::CanOpenItem(Item* item)
|
|
||||||
{
|
|
||||||
if (!item)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
ItemTemplate const* itemTemplate = item->GetTemplate();
|
|
||||||
if (!itemTemplate)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Check if the item has the openable flag
|
|
||||||
return itemTemplate->Flags & ITEM_FLAG_HAS_LOOT;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OpenItemAction::OpenItem(Item* item, uint8 bag, uint8 slot)
|
void OpenItemAction::OpenItem(Item* item, uint8 bag, uint8 slot)
|
||||||
{
|
{
|
||||||
WorldPacket packet(CMSG_OPEN_ITEM);
|
WorldPacket packet(CMSG_OPEN_ITEM);
|
||||||
|
@ -21,9 +21,6 @@ public:
|
|||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Checks if the given item can be opened (i.e., has the openable flag)
|
|
||||||
bool CanOpenItem(Item* item);
|
|
||||||
|
|
||||||
// Performs the action of opening the item
|
// Performs the action of opening the item
|
||||||
void OpenItem(Item* item, uint8 bag, uint8 slot);
|
void OpenItem(Item* item, uint8 bag, uint8 slot);
|
||||||
};
|
};
|
||||||
|
@ -159,3 +159,25 @@ bool ReturnAction::isUseful()
|
|||||||
PositionInfo pos = context->GetValue<PositionMap&>("position")->Get()[qualifier];
|
PositionInfo pos = context->GetValue<PositionMap&>("position")->Get()[qualifier];
|
||||||
return pos.isSet() && AI_VALUE2(float, "distance", "position_random") > sPlayerbotAIConfig->followDistance;
|
return pos.isSet() && AI_VALUE2(float, "distance", "position_random") > sPlayerbotAIConfig->followDistance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ReturnToStayPositionAction::isPossible()
|
||||||
|
{
|
||||||
|
PositionMap& posMap = AI_VALUE(PositionMap&, "position");
|
||||||
|
PositionInfo stayPosition = posMap["stay"];
|
||||||
|
if (stayPosition.isSet())
|
||||||
|
{
|
||||||
|
const float distance = bot->GetDistance(stayPosition.x, stayPosition.y, stayPosition.z);
|
||||||
|
if (distance > sPlayerbotAIConfig->reactDistance)
|
||||||
|
{
|
||||||
|
botAI->TellMaster("The stay position is too far to return. I am going to stay where I am now");
|
||||||
|
|
||||||
|
// Set the stay position to current position
|
||||||
|
stayPosition.Set(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), bot->GetMapId());
|
||||||
|
posMap["stay"] = stayPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
@ -40,6 +40,13 @@ public:
|
|||||||
GuardAction(PlayerbotAI* botAI) : MoveToPositionAction(botAI, "move to position", "guard") {}
|
GuardAction(PlayerbotAI* botAI) : MoveToPositionAction(botAI, "move to position", "guard") {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ReturnToStayPositionAction : public MoveToPositionAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ReturnToStayPositionAction(PlayerbotAI* ai) : MoveToPositionAction(ai, "move to position", "stay") {}
|
||||||
|
virtual bool isPossible();
|
||||||
|
};
|
||||||
|
|
||||||
class SetReturnPositionAction : public Action
|
class SetReturnPositionAction : public Action
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -4,9 +4,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "QuestAction.h"
|
#include "QuestAction.h"
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#include "Chat.h"
|
||||||
#include "ChatHelper.h"
|
#include "ChatHelper.h"
|
||||||
#include "Event.h"
|
#include "Event.h"
|
||||||
|
#include "ItemTemplate.h"
|
||||||
|
#include "ObjectGuid.h"
|
||||||
|
#include "ObjectMgr.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "ReputationMgr.h"
|
#include "ReputationMgr.h"
|
||||||
#include "ServerFacade.h"
|
#include "ServerFacade.h"
|
||||||
@ -258,6 +263,7 @@ bool QuestAction::AcceptQuest(Quest const* quest, ObjectGuid questGiver)
|
|||||||
|
|
||||||
bool QuestUpdateCompleteAction::Execute(Event event)
|
bool QuestUpdateCompleteAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
|
// the action can hardly be triggered
|
||||||
WorldPacket p(event.getPacket());
|
WorldPacket p(event.getPacket());
|
||||||
p.rpos(0);
|
p.rpos(0);
|
||||||
|
|
||||||
@ -265,22 +271,26 @@ bool QuestUpdateCompleteAction::Execute(Event event)
|
|||||||
p >> questId;
|
p >> questId;
|
||||||
|
|
||||||
p.print_storage();
|
p.print_storage();
|
||||||
LOG_INFO("playerbots", "Packet: empty{} questId{}", p.empty(), questId);
|
// LOG_INFO("playerbots", "Packet: empty{} questId{}", p.empty(), questId);
|
||||||
|
|
||||||
Quest const* qInfo = sObjectMgr->GetQuestTemplate(questId);
|
Quest const* qInfo = sObjectMgr->GetQuestTemplate(questId);
|
||||||
if (qInfo)
|
if (qInfo)
|
||||||
{
|
{
|
||||||
std::map<std::string, std::string> placeholders;
|
// std::map<std::string, std::string> placeholders;
|
||||||
const auto format = ChatHelper::FormatQuest(qInfo);
|
// placeholders["%quest_link"] = format;
|
||||||
placeholders["%quest_link"] = format;
|
|
||||||
|
|
||||||
if (botAI->HasStrategy("debug quest", BotState::BOT_STATE_NON_COMBAT) || botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT))
|
// if (botAI->HasStrategy("debug quest", BotState::BOT_STATE_NON_COMBAT) || botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT))
|
||||||
{
|
// {
|
||||||
LOG_INFO("playerbots", "{} => Quest [ {} ] completed", bot->GetName(), qInfo->GetTitle());
|
// LOG_INFO("playerbots", "{} => Quest [ {} ] completed", bot->GetName(), qInfo->GetTitle());
|
||||||
bot->Say("Quest [ " + format + " ] completed", LANG_UNIVERSAL);
|
// bot->Say("Quest [ " + format + " ] completed", LANG_UNIVERSAL);
|
||||||
}
|
// }
|
||||||
|
const auto format = ChatHelper::FormatQuest(qInfo);
|
||||||
|
if (botAI->GetMaster())
|
||||||
botAI->TellMasterNoFacing("Quest completed " + format);
|
botAI->TellMasterNoFacing("Quest completed " + format);
|
||||||
BroadcastHelper::BroadcastQuestUpdateComplete(botAI, bot, qInfo);
|
BroadcastHelper::BroadcastQuestUpdateComplete(botAI, bot, qInfo);
|
||||||
|
botAI->rpgStatistic.questCompleted++;
|
||||||
|
// LOG_DEBUG("playerbots", "[New rpg] {} complete quest {}", bot->GetName(), qInfo->GetQuestId());
|
||||||
|
// botAI->rpgStatistic.questCompleted++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -296,39 +306,39 @@ bool QuestUpdateAddKillAction::Execute(Event event)
|
|||||||
|
|
||||||
uint32 entry, questId, available, required;
|
uint32 entry, questId, available, required;
|
||||||
p >> questId >> entry >> available >> required;
|
p >> questId >> entry >> available >> required;
|
||||||
|
// LOG_INFO("playerbots", "[New rpg] Quest {} -> Creature {} ({}/{})", questId, entry, available, required);
|
||||||
const Quest* qInfo = sObjectMgr->GetQuestTemplate(questId);
|
const Quest* qInfo = sObjectMgr->GetQuestTemplate(questId);
|
||||||
if (qInfo && (entry & 0x80000000))
|
if (qInfo && (entry & 0x80000000))
|
||||||
{
|
{
|
||||||
entry &= 0x7FFFFFFF;
|
entry &= 0x7FFFFFFF;
|
||||||
const GameObjectTemplate* info = sObjectMgr->GetGameObjectTemplate(entry);
|
const GameObjectTemplate* info = sObjectMgr->GetGameObjectTemplate(entry);
|
||||||
if (info)
|
if (info)
|
||||||
BroadcastHelper::BroadcastQuestUpdateAddKill(botAI, bot, qInfo, available, required, info->name);
|
{
|
||||||
|
std::string infoName = botAI->GetLocalizedGameObjectName(entry);
|
||||||
|
BroadcastHelper::BroadcastQuestUpdateAddKill(botAI, bot, qInfo, available, required, infoName);
|
||||||
|
if (botAI->GetMaster())
|
||||||
|
{
|
||||||
|
std::ostringstream out;
|
||||||
|
out << infoName << " " << available << "/" << required << " " << ChatHelper::FormatQuest(qInfo);
|
||||||
|
botAI->TellMasterNoFacing(out.str());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (qInfo)
|
else if (qInfo)
|
||||||
{
|
{
|
||||||
CreatureTemplate const* info = sObjectMgr->GetCreatureTemplate(entry);
|
CreatureTemplate const* info = sObjectMgr->GetCreatureTemplate(entry);
|
||||||
if (info)
|
if (info)
|
||||||
{
|
{
|
||||||
BroadcastHelper::BroadcastQuestUpdateAddKill(botAI, bot, qInfo, available, required, info->Name);
|
std::string infoName = botAI->GetLocalizedCreatureName(entry);
|
||||||
}
|
BroadcastHelper::BroadcastQuestUpdateAddKill(botAI, bot, qInfo, available, required, infoName);
|
||||||
}
|
if (botAI->GetMaster())
|
||||||
else
|
|
||||||
{
|
{
|
||||||
std::map<std::string, std::string> placeholders;
|
std::ostringstream out;
|
||||||
placeholders["%quest_id"] = questId;
|
out << infoName << " " << available << "/" << required << " " << ChatHelper::FormatQuest(qInfo);
|
||||||
placeholders["%available"] = available;
|
botAI->TellMasterNoFacing(out.str());
|
||||||
placeholders["%required"] = required;
|
}
|
||||||
|
|
||||||
if (botAI->HasStrategy("debug quest", BotState::BOT_STATE_COMBAT) || botAI->HasStrategy("debug quest", BotState::BOT_STATE_NON_COMBAT))
|
|
||||||
{
|
|
||||||
LOG_INFO("playerbots", "{} => {}", bot->GetName(), BOT_TEXT2("%available/%required for questId: %quest_id", placeholders));
|
|
||||||
botAI->Say(BOT_TEXT2("%available/%required for questId: %quest_id", placeholders));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
botAI->TellMasterNoFacing(BOT_TEXT2("%available/%required for questId: %quest_id", placeholders));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -364,6 +374,60 @@ bool QuestUpdateAddItemAction::Execute(Event event)
|
|||||||
BroadcastHelper::BroadcastQuestUpdateAddItem(botAI, bot, pair.first, availableItemsCount, requiredItemsCount, itemPrototype);
|
BroadcastHelper::BroadcastQuestUpdateAddItem(botAI, bot, pair.first, availableItemsCount, requiredItemsCount, itemPrototype);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QuestItemPushResultAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
WorldPacket packet = event.getPacket();
|
||||||
|
ObjectGuid guid;
|
||||||
|
uint32 received, created, sendChatMessage, itemSlot, itemEntry, itemSuffixFactory, count, itemCount;
|
||||||
|
uint8 bagSlot;
|
||||||
|
int32 itemRandomPropertyId;
|
||||||
|
packet >> guid >> received >> created >> sendChatMessage;
|
||||||
|
packet >> bagSlot >> itemSlot >> itemEntry >> itemSuffixFactory >> itemRandomPropertyId;
|
||||||
|
packet >> count >> itemCount;
|
||||||
|
|
||||||
|
if (guid != bot->GetGUID())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const ItemTemplate* proto = sObjectMgr->GetItemTemplate(itemEntry);
|
||||||
|
if (!proto)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (uint16 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
|
||||||
|
{
|
||||||
|
uint32 questId = bot->GetQuestSlotQuestId(i);
|
||||||
|
if (!questId)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const Quest* quest = sObjectMgr->GetQuestTemplate(questId);
|
||||||
|
if (!quest)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const QuestStatusData& q_status = bot->getQuestStatusMap().at(questId);
|
||||||
|
|
||||||
|
for (int i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; i++)
|
||||||
|
{
|
||||||
|
uint32 itemId = quest->RequiredItemId[i];
|
||||||
|
if (!itemId)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int32 previousCount = itemCount - count;
|
||||||
|
if (itemId == itemEntry && previousCount < quest->RequiredItemCount[i])
|
||||||
|
{
|
||||||
|
if (botAI->GetMaster())
|
||||||
|
{
|
||||||
|
std::string itemLink = ChatHelper::FormatItem(proto);
|
||||||
|
std::ostringstream out;
|
||||||
|
int32 required = quest->RequiredItemCount[i];
|
||||||
|
int32 available = std::min((int32)itemCount, required);
|
||||||
|
out << itemLink << " " << available << "/" << required << " " << ChatHelper::FormatQuest(quest);
|
||||||
|
botAI->TellMasterNoFacing(out.str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -66,4 +66,11 @@ public:
|
|||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class QuestItemPushResultAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QuestItemPushResultAction(PlayerbotAI* ai) : Action(ai, "quest item push result") {}
|
||||||
|
bool Execute(Event event) override;;
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -14,6 +14,12 @@ bool ReachTargetAction::Execute(Event event) { return ReachCombatTo(AI_VALUE(Uni
|
|||||||
|
|
||||||
bool ReachTargetAction::isUseful()
|
bool ReachTargetAction::isUseful()
|
||||||
{
|
{
|
||||||
|
// do not move while staying
|
||||||
|
if (botAI->HasStrategy("stay", botAI->GetState()))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// do not move while casting
|
// do not move while casting
|
||||||
if (bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL) != nullptr)
|
if (bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL) != nullptr)
|
||||||
{
|
{
|
||||||
@ -30,6 +36,12 @@ std::string const ReachTargetAction::GetTargetName() { return "current target";
|
|||||||
|
|
||||||
bool CastReachTargetSpellAction::isUseful()
|
bool CastReachTargetSpellAction::isUseful()
|
||||||
{
|
{
|
||||||
|
// do not move while staying
|
||||||
|
if (botAI->HasStrategy("stay", botAI->GetState()))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", "current target"),
|
return sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", "current target"),
|
||||||
(distance + sPlayerbotAIConfig->contactDistance));
|
(distance + sPlayerbotAIConfig->contactDistance));
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "RTSCValues.h"
|
#include "RTSCValues.h"
|
||||||
#include "RtscAction.h"
|
#include "RtscAction.h"
|
||||||
|
#include "PositionValue.h"
|
||||||
|
|
||||||
Creature* SeeSpellAction::CreateWps(Player* wpOwner, float x, float y, float z, float o, uint32 entry, Creature* lastWp,
|
Creature* SeeSpellAction::CreateWps(Player* wpOwner, float x, float y, float z, float o, uint32 entry, Creature* lastWp,
|
||||||
bool important)
|
bool important)
|
||||||
@ -123,6 +124,15 @@ bool SeeSpellAction::MoveToSpell(WorldPosition& spellPosition, bool inFormation)
|
|||||||
if (inFormation)
|
if (inFormation)
|
||||||
SetFormationOffset(spellPosition);
|
SetFormationOffset(spellPosition);
|
||||||
|
|
||||||
|
if (botAI->HasStrategy("stay", botAI->GetState()))
|
||||||
|
{
|
||||||
|
PositionMap& posMap = AI_VALUE(PositionMap&, "position");
|
||||||
|
PositionInfo stayPosition = posMap["stay"];
|
||||||
|
|
||||||
|
stayPosition.Set(spellPosition.getX(), spellPosition.getY(), spellPosition.getZ(), spellPosition.getMapId());
|
||||||
|
posMap["stay"] = stayPosition;
|
||||||
|
}
|
||||||
|
|
||||||
if (bot->IsWithinLOS(spellPosition.getX(), spellPosition.getY(), spellPosition.getZ()))
|
if (bot->IsWithinLOS(spellPosition.getX(), spellPosition.getY(), spellPosition.getZ()))
|
||||||
return MoveNear(spellPosition.getMapId(), spellPosition.getX(), spellPosition.getY(), spellPosition.getZ(), 0);
|
return MoveNear(spellPosition.getMapId(), spellPosition.getX(), spellPosition.getY(), spellPosition.getZ(), 0);
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include "Event.h"
|
#include "Event.h"
|
||||||
#include "LastMovementValue.h"
|
#include "LastMovementValue.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
#include "PositionValue.h"
|
||||||
|
|
||||||
bool StayActionBase::Stay()
|
bool StayActionBase::Stay()
|
||||||
{
|
{
|
||||||
@ -42,6 +43,17 @@ bool StayAction::Execute(Event event) { return Stay(); }
|
|||||||
|
|
||||||
bool StayAction::isUseful()
|
bool StayAction::isUseful()
|
||||||
{
|
{
|
||||||
|
// Check if the bots is in stay position
|
||||||
|
PositionInfo stayPosition = AI_VALUE(PositionMap&, "position")["stay"];
|
||||||
|
if (stayPosition.isSet())
|
||||||
|
{
|
||||||
|
const float distance = bot->GetDistance(stayPosition.x, stayPosition.y, stayPosition.z);
|
||||||
|
if (sPlayerbotAIConfig->followDistance)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// move from group takes priority over stay as it's added and removed automatically
|
// move from group takes priority over stay as it's added and removed automatically
|
||||||
// (without removing/adding stay)
|
// (without removing/adding stay)
|
||||||
if (botAI->HasStrategy("move from group", BOT_STATE_COMBAT) ||
|
if (botAI->HasStrategy("move from group", BOT_STATE_COMBAT) ||
|
||||||
|
@ -173,7 +173,6 @@ void TalkToQuestGiverAction::RewardMultipleItem(Quest const* quest, Object* ques
|
|||||||
std::ostringstream outid;
|
std::ostringstream outid;
|
||||||
if (!botAI->IsAlt() || sPlayerbotAIConfig->autoPickReward == "yes")
|
if (!botAI->IsAlt() || sPlayerbotAIConfig->autoPickReward == "yes")
|
||||||
{
|
{
|
||||||
// Pick the first item of the best rewards.
|
|
||||||
bestIds = BestRewards(quest);
|
bestIds = BestRewards(quest);
|
||||||
if (!bestIds.empty())
|
if (!bestIds.empty())
|
||||||
{
|
{
|
||||||
|
@ -24,12 +24,17 @@ bool TradeStatusAction::Execute(Event event)
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
PlayerbotAI* traderBotAI = GET_PLAYERBOT_AI(trader);
|
PlayerbotAI* traderBotAI = GET_PLAYERBOT_AI(trader);
|
||||||
if (trader != master && !traderBotAI)
|
|
||||||
|
// Allow the master and group members to trade
|
||||||
|
if (trader != master && !traderBotAI && (!bot->GetGroup() || !bot->GetGroup()->IsMember(trader->GetGUID())))
|
||||||
{
|
{
|
||||||
bot->Whisper("I'm kind of busy now", LANG_UNIVERSAL, trader);
|
bot->Whisper("I'm kind of busy now", LANG_UNIVERSAL, trader);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((trader != master || !botAI->GetSecurity()->CheckLevelFor(PLAYERBOT_SECURITY_ALLOW_ALL, true, master)) &&
|
// Allow trades from group members or bots
|
||||||
|
if ((!bot->GetGroup() || !bot->GetGroup()->IsMember(trader->GetGUID())) &&
|
||||||
|
(trader != master || !botAI->GetSecurity()->CheckLevelFor(PLAYERBOT_SECURITY_ALLOW_ALL, true, master)) &&
|
||||||
!traderBotAI)
|
!traderBotAI)
|
||||||
{
|
{
|
||||||
WorldPacket p;
|
WorldPacket p;
|
||||||
@ -109,9 +114,9 @@ bool TradeStatusAction::Execute(Event event)
|
|||||||
bot->SetFacingToObject(trader);
|
bot->SetFacingToObject(trader);
|
||||||
|
|
||||||
BeginTrade();
|
BeginTrade();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,84 @@
|
|||||||
|
#include "TradeStatusExtendedAction.h"
|
||||||
|
#include "Event.h"
|
||||||
|
#include "Player.h"
|
||||||
|
#include "PlayerbotAI.h"
|
||||||
|
#include "WorldPacket.h"
|
||||||
|
#include "TradeData.h"
|
||||||
|
|
||||||
|
bool TradeStatusExtendedAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
Player* trader = bot->GetTrader();
|
||||||
|
if (!trader)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
TradeData* tradeData = trader->GetTradeData();
|
||||||
|
if (!tradeData)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
WorldPacket p(event.getPacket());
|
||||||
|
p.rpos(0);
|
||||||
|
|
||||||
|
uint8 isTraderData;
|
||||||
|
uint32 unknown1, slotCount1, slotCount2, tradeGold, spellCast;
|
||||||
|
p >> isTraderData;
|
||||||
|
p >> unknown1;
|
||||||
|
p >> slotCount1;
|
||||||
|
p >> slotCount2;
|
||||||
|
p >> tradeGold;
|
||||||
|
p >> spellCast;
|
||||||
|
|
||||||
|
for (uint8 i = 0; i < TRADE_SLOT_COUNT; ++i)
|
||||||
|
{
|
||||||
|
uint8 tradeSlot;
|
||||||
|
p >> tradeSlot;
|
||||||
|
|
||||||
|
if (tradeSlot >= TRADE_SLOT_COUNT)
|
||||||
|
break; // End of packet
|
||||||
|
|
||||||
|
uint32 itemId, displayId, count, wrapped, lockId;
|
||||||
|
uint64 giftCreator, creator;
|
||||||
|
uint32 permEnchant, gem1, gem2, gem3;
|
||||||
|
uint32 spellCharges, suffixFactor, randomProp, maxDurability, durability;
|
||||||
|
|
||||||
|
p >> itemId;
|
||||||
|
p >> displayId;
|
||||||
|
p >> count;
|
||||||
|
p >> wrapped;
|
||||||
|
p >> giftCreator;
|
||||||
|
p >> permEnchant;
|
||||||
|
p >> gem1;
|
||||||
|
p >> gem2;
|
||||||
|
p >> gem3;
|
||||||
|
p >> creator;
|
||||||
|
p >> spellCharges;
|
||||||
|
p >> suffixFactor;
|
||||||
|
p >> randomProp;
|
||||||
|
p >> lockId;
|
||||||
|
p >> maxDurability;
|
||||||
|
p >> durability;
|
||||||
|
|
||||||
|
// Check for locked items in "Do Not Trade" slot
|
||||||
|
if (tradeSlot == TRADE_SLOT_NONTRADED && lockId > 0)
|
||||||
|
{
|
||||||
|
// Get the actual item reference from TradeData
|
||||||
|
Item* lockbox = tradeData->GetItem(TRADE_SLOT_NONTRADED);
|
||||||
|
if (!lockbox)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bot->getClass() == CLASS_ROGUE && bot->HasSpell(1804) && lockbox->IsLocked()) // Pick Lock spell
|
||||||
|
{
|
||||||
|
// botAI->CastSpell(1804, bot, lockbox); // Attempt to cast Pick Lock on the lockbox
|
||||||
|
botAI->DoSpecificAction("unlock traded item");
|
||||||
|
botAI->SetNextCheckDelay(4000); // Delay before accepting trade
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
botAI->TellMaster("I can't unlock this item.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
#ifndef _PLAYERBOT_TRADESTATUSEXTENDEDACTION_H
|
||||||
|
#define _PLAYERBOT_TRADESTATUSEXTENDEDACTION_H
|
||||||
|
|
||||||
|
#include "QueryItemUsageAction.h"
|
||||||
|
|
||||||
|
class Player;
|
||||||
|
class PlayerbotAI;
|
||||||
|
|
||||||
|
class TradeStatusExtendedAction : public QueryItemUsageAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TradeStatusExtendedAction(PlayerbotAI* botAI) : QueryItemUsageAction(botAI, "trade status extended") {}
|
||||||
|
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,38 @@
|
|||||||
|
#include "UnlockItemAction.h"
|
||||||
|
#include "PlayerbotAI.h"
|
||||||
|
#include "ItemTemplate.h"
|
||||||
|
#include "WorldPacket.h"
|
||||||
|
#include "Player.h"
|
||||||
|
#include "ObjectMgr.h"
|
||||||
|
#include "SpellInfo.h"
|
||||||
|
|
||||||
|
#define PICK_LOCK_SPELL_ID 1804
|
||||||
|
|
||||||
|
bool UnlockItemAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
bool foundLockedItem = false;
|
||||||
|
|
||||||
|
Item* item = botAI->FindLockedItem();
|
||||||
|
if (item)
|
||||||
|
{
|
||||||
|
UnlockItem(item);
|
||||||
|
foundLockedItem = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return foundLockedItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnlockItemAction::UnlockItem(Item* item)
|
||||||
|
{
|
||||||
|
// Use CastSpell to unlock the item
|
||||||
|
if (botAI->CastSpell(PICK_LOCK_SPELL_ID, bot, item))
|
||||||
|
{
|
||||||
|
std::ostringstream out;
|
||||||
|
out << "Used Pick Lock on: " << item->GetTemplate()->Name1;
|
||||||
|
botAI->TellMaster(out.str());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
botAI->TellError("Failed to cast Pick Lock.");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
#ifndef _PLAYERBOT_UNLOCKITEMACTION_H
|
||||||
|
#define _PLAYERBOT_UNLOCKITEMACTION_H
|
||||||
|
|
||||||
|
#include "Action.h"
|
||||||
|
|
||||||
|
class PlayerbotAI;
|
||||||
|
|
||||||
|
class UnlockItemAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
UnlockItemAction(PlayerbotAI* botAI) : Action(botAI, "unlock item") { }
|
||||||
|
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void UnlockItem(Item* item);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,98 @@
|
|||||||
|
#include "UnlockTradedItemAction.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
#include "TradeData.h"
|
||||||
|
#include "SpellInfo.h"
|
||||||
|
|
||||||
|
#define PICK_LOCK_SPELL_ID 1804
|
||||||
|
|
||||||
|
bool UnlockTradedItemAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
Player* trader = bot->GetTrader();
|
||||||
|
if (!trader)
|
||||||
|
return false; // No active trade session
|
||||||
|
|
||||||
|
TradeData* tradeData = trader->GetTradeData();
|
||||||
|
if (!tradeData)
|
||||||
|
return false; // No trade data available
|
||||||
|
|
||||||
|
Item* lockbox = tradeData->GetItem(TRADE_SLOT_NONTRADED);
|
||||||
|
if (!lockbox)
|
||||||
|
{
|
||||||
|
botAI->TellError("No item in the Do Not Trade slot.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CanUnlockItem(lockbox))
|
||||||
|
{
|
||||||
|
botAI->TellError("Cannot unlock this item.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
UnlockItem(lockbox);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UnlockTradedItemAction::CanUnlockItem(Item* item)
|
||||||
|
{
|
||||||
|
if (!item)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ItemTemplate const* itemTemplate = item->GetTemplate();
|
||||||
|
if (!itemTemplate)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Ensure the bot is a rogue and has Lockpicking skill
|
||||||
|
if (bot->getClass() != CLASS_ROGUE || !botAI->HasSkill(SKILL_LOCKPICKING))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Ensure the item is actually locked
|
||||||
|
if (itemTemplate->LockID == 0 || !item->IsLocked())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Check if the bot's Lockpicking skill is high enough
|
||||||
|
uint32 lockId = itemTemplate->LockID;
|
||||||
|
LockEntry const* lockInfo = sLockStore.LookupEntry(lockId);
|
||||||
|
if (!lockInfo)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
uint32 botSkill = bot->GetSkillValue(SKILL_LOCKPICKING);
|
||||||
|
for (uint8 j = 0; j < 8; ++j)
|
||||||
|
{
|
||||||
|
if (lockInfo->Type[j] == LOCK_KEY_SKILL && SkillByLockType(LockType(lockInfo->Index[j])) == SKILL_LOCKPICKING)
|
||||||
|
{
|
||||||
|
uint32 requiredSkill = lockInfo->Skill[j];
|
||||||
|
if (botSkill >= requiredSkill)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::ostringstream out;
|
||||||
|
out << "Lockpicking skill too low (" << botSkill << "/" << requiredSkill << ") to unlock: "
|
||||||
|
<< item->GetTemplate()->Name1;
|
||||||
|
botAI->TellMaster(out.str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnlockTradedItemAction::UnlockItem(Item* item)
|
||||||
|
{
|
||||||
|
if (!bot->HasSpell(PICK_LOCK_SPELL_ID))
|
||||||
|
{
|
||||||
|
botAI->TellError("Cannot unlock, Pick Lock spell is missing.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use CastSpell to unlock the item
|
||||||
|
if (botAI->CastSpell(PICK_LOCK_SPELL_ID, bot->GetTrader(), item)) // Unit target is trader
|
||||||
|
{
|
||||||
|
std::ostringstream out;
|
||||||
|
out << "Picking Lock on traded item: " << item->GetTemplate()->Name1;
|
||||||
|
botAI->TellMaster(out.str());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
botAI->TellError("Failed to cast Pick Lock.");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
#ifndef _PLAYERBOT_UNLOCKTRADEDITEMACTION_H
|
||||||
|
#define _PLAYERBOT_UNLOCKTRADEDITEMACTION_H
|
||||||
|
|
||||||
|
#include "Action.h"
|
||||||
|
|
||||||
|
class PlayerbotAI;
|
||||||
|
|
||||||
|
class UnlockTradedItemAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
UnlockTradedItemAction(PlayerbotAI* botAI) : Action(botAI, "unlock traded item") {}
|
||||||
|
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool CanUnlockItem(Item* item);
|
||||||
|
void UnlockItem(Item* item);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -11,6 +11,7 @@
|
|||||||
#include "GridNotifiersImpl.h"
|
#include "GridNotifiersImpl.h"
|
||||||
#include "PlayerbotAIConfig.h"
|
#include "PlayerbotAIConfig.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
#include "PositionValue.h"
|
||||||
|
|
||||||
bool UseMeetingStoneAction::Execute(Event event)
|
bool UseMeetingStoneAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
@ -224,6 +225,16 @@ bool SummonAction::Teleport(Player* summoner, Player* player)
|
|||||||
player->GetMotionMaster()->Clear();
|
player->GetMotionMaster()->Clear();
|
||||||
AI_VALUE(LastMovement&, "last movement").clear();
|
AI_VALUE(LastMovement&, "last movement").clear();
|
||||||
player->TeleportTo(mapId, x, y, z, 0);
|
player->TeleportTo(mapId, x, y, z, 0);
|
||||||
|
|
||||||
|
if (botAI->HasStrategy("stay", botAI->GetState()))
|
||||||
|
{
|
||||||
|
PositionMap& posMap = AI_VALUE(PositionMap&, "position");
|
||||||
|
PositionInfo stayPosition = posMap["stay"];
|
||||||
|
|
||||||
|
stayPosition.Set(x,y, z, mapId);
|
||||||
|
posMap["stay"] = stayPosition;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
#include "TellCastFailedAction.h"
|
#include "TellCastFailedAction.h"
|
||||||
#include "TellMasterAction.h"
|
#include "TellMasterAction.h"
|
||||||
#include "TradeStatusAction.h"
|
#include "TradeStatusAction.h"
|
||||||
|
#include "TradeStatusExtendedAction.h"
|
||||||
#include "UseMeetingStoneAction.h"
|
#include "UseMeetingStoneAction.h"
|
||||||
#include "NamedObjectContext.h"
|
#include "NamedObjectContext.h"
|
||||||
|
|
||||||
@ -65,6 +66,7 @@ public:
|
|||||||
creators["check mount state"] = &WorldPacketActionContext::check_mount_state;
|
creators["check mount state"] = &WorldPacketActionContext::check_mount_state;
|
||||||
creators["remember taxi"] = &WorldPacketActionContext::remember_taxi;
|
creators["remember taxi"] = &WorldPacketActionContext::remember_taxi;
|
||||||
creators["accept trade"] = &WorldPacketActionContext::accept_trade;
|
creators["accept trade"] = &WorldPacketActionContext::accept_trade;
|
||||||
|
creators["trade status extended"] = &WorldPacketActionContext::trade_status_extended;
|
||||||
creators["store loot"] = &WorldPacketActionContext::store_loot;
|
creators["store loot"] = &WorldPacketActionContext::store_loot;
|
||||||
|
|
||||||
// quest
|
// quest
|
||||||
@ -79,6 +81,7 @@ public:
|
|||||||
creators["quest update failed timer"] = &WorldPacketActionContext::quest_update_failed_timer;
|
creators["quest update failed timer"] = &WorldPacketActionContext::quest_update_failed_timer;
|
||||||
creators["quest update complete"] = &WorldPacketActionContext::quest_update_complete;
|
creators["quest update complete"] = &WorldPacketActionContext::quest_update_complete;
|
||||||
creators["turn in query quest"] = &WorldPacketActionContext::turn_in_query_quest;
|
creators["turn in query quest"] = &WorldPacketActionContext::turn_in_query_quest;
|
||||||
|
creators["quest item push result"] = &WorldPacketActionContext::quest_item_push_result;
|
||||||
|
|
||||||
creators["party command"] = &WorldPacketActionContext::party_command;
|
creators["party command"] = &WorldPacketActionContext::party_command;
|
||||||
creators["tell cast failed"] = &WorldPacketActionContext::tell_cast_failed;
|
creators["tell cast failed"] = &WorldPacketActionContext::tell_cast_failed;
|
||||||
@ -117,6 +120,7 @@ private:
|
|||||||
static Action* party_command(PlayerbotAI* botAI) { return new PartyCommandAction(botAI); }
|
static Action* party_command(PlayerbotAI* botAI) { return new PartyCommandAction(botAI); }
|
||||||
static Action* store_loot(PlayerbotAI* botAI) { return new StoreLootAction(botAI); }
|
static Action* store_loot(PlayerbotAI* botAI) { return new StoreLootAction(botAI); }
|
||||||
static Action* accept_trade(PlayerbotAI* botAI) { return new TradeStatusAction(botAI); }
|
static Action* accept_trade(PlayerbotAI* botAI) { return new TradeStatusAction(botAI); }
|
||||||
|
static Action* trade_status_extended(PlayerbotAI* botAI) { return new TradeStatusExtendedAction(botAI); }
|
||||||
static Action* remember_taxi(PlayerbotAI* botAI) { return new RememberTaxiAction(botAI); }
|
static Action* remember_taxi(PlayerbotAI* botAI) { return new RememberTaxiAction(botAI); }
|
||||||
static Action* check_mount_state(PlayerbotAI* botAI) { return new CheckMountStateAction(botAI); }
|
static Action* check_mount_state(PlayerbotAI* botAI) { return new CheckMountStateAction(botAI); }
|
||||||
static Action* area_trigger(PlayerbotAI* botAI) { return new AreaTriggerAction(botAI); }
|
static Action* area_trigger(PlayerbotAI* botAI) { return new AreaTriggerAction(botAI); }
|
||||||
@ -139,6 +143,7 @@ private:
|
|||||||
static Action* quest_update_failed(PlayerbotAI* ai) { return new QuestUpdateFailedAction(ai); }
|
static Action* quest_update_failed(PlayerbotAI* ai) { return new QuestUpdateFailedAction(ai); }
|
||||||
static Action* quest_update_failed_timer(PlayerbotAI* ai) { return new QuestUpdateFailedTimerAction(ai); }
|
static Action* quest_update_failed_timer(PlayerbotAI* ai) { return new QuestUpdateFailedTimerAction(ai); }
|
||||||
static Action* quest_update_complete(PlayerbotAI* botAI) { return new QuestUpdateCompleteAction(botAI); }
|
static Action* quest_update_complete(PlayerbotAI* botAI) { return new QuestUpdateCompleteAction(botAI); }
|
||||||
|
static Action* quest_item_push_result(PlayerbotAI* ai) { return new QuestItemPushResultAction(ai); }
|
||||||
|
|
||||||
static Action* turn_in_quest(PlayerbotAI* botAI) { return new TalkToQuestGiverAction(botAI); }
|
static Action* turn_in_quest(PlayerbotAI* botAI) { return new TalkToQuestGiverAction(botAI); }
|
||||||
static Action* accept_quest(PlayerbotAI* botAI) { return new AcceptQuestAction(botAI); }
|
static Action* accept_quest(PlayerbotAI* botAI) { return new AcceptQuestAction(botAI); }
|
||||||
|
@ -96,6 +96,10 @@ void ChatCommandHandlerStrategy::InitTriggers(std::vector<TriggerNode*>& trigger
|
|||||||
new TriggerNode("open items", NextAction::array(0, new NextAction("open items", relevance), nullptr)));
|
new TriggerNode("open items", NextAction::array(0, new NextAction("open items", relevance), nullptr)));
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("qi", NextAction::array(0, new NextAction("query item usage", relevance), nullptr)));
|
new TriggerNode("qi", NextAction::array(0, new NextAction("query item usage", relevance), nullptr)));
|
||||||
|
triggers.push_back(
|
||||||
|
new TriggerNode("unlock items", NextAction::array(0, new NextAction("unlock items", relevance), nullptr)));
|
||||||
|
triggers.push_back(
|
||||||
|
new TriggerNode("unlock traded item", NextAction::array(0, new NextAction("unlock traded item", relevance), nullptr)));
|
||||||
}
|
}
|
||||||
|
|
||||||
ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : PassTroughStrategy(botAI)
|
ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : PassTroughStrategy(botAI)
|
||||||
@ -109,6 +113,7 @@ ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : Pas
|
|||||||
supported.push_back("log");
|
supported.push_back("log");
|
||||||
supported.push_back("los");
|
supported.push_back("los");
|
||||||
supported.push_back("rpg status");
|
supported.push_back("rpg status");
|
||||||
|
supported.push_back("rpg do quest");
|
||||||
supported.push_back("aura");
|
supported.push_back("aura");
|
||||||
supported.push_back("drop");
|
supported.push_back("drop");
|
||||||
supported.push_back("share");
|
supported.push_back("share");
|
||||||
@ -171,4 +176,6 @@ ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : Pas
|
|||||||
supported.push_back("calc");
|
supported.push_back("calc");
|
||||||
supported.push_back("open items");
|
supported.push_back("open items");
|
||||||
supported.push_back("qi");
|
supported.push_back("qi");
|
||||||
|
supported.push_back("unlock items");
|
||||||
|
supported.push_back("unlock traded item");
|
||||||
}
|
}
|
||||||
|
@ -12,8 +12,9 @@ void CombatStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
{
|
{
|
||||||
triggers.push_back(new TriggerNode("enemy out of spell",
|
triggers.push_back(new TriggerNode("enemy out of spell",
|
||||||
NextAction::array(0, new NextAction("reach spell", ACTION_HIGH), nullptr)));
|
NextAction::array(0, new NextAction("reach spell", ACTION_HIGH), nullptr)));
|
||||||
|
// drop target relevance 99 (lower than Worldpacket triggers)
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("invalid target", NextAction::array(0, new NextAction("drop target", 100), nullptr)));
|
new TriggerNode("invalid target", NextAction::array(0, new NextAction("drop target", 99), nullptr)));
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("mounted", NextAction::array(0, new NextAction("check mount state", 54), nullptr)));
|
new TriggerNode("mounted", NextAction::array(0, new NextAction("check mount state", 54), nullptr)));
|
||||||
// triggers.push_back(new TriggerNode("out of react range", NextAction::array(0, new NextAction("flee to master",
|
// triggers.push_back(new TriggerNode("out of react range", NextAction::array(0, new NextAction("flee to master",
|
||||||
|
@ -7,14 +7,19 @@
|
|||||||
|
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
|
||||||
NextAction** GrindingStrategy::getDefaultActions() { return nullptr; }
|
NextAction** GrindingStrategy::getDefaultActions()
|
||||||
|
{
|
||||||
|
return NextAction::array(0,
|
||||||
|
new NextAction("drink", 4.2f),
|
||||||
|
new NextAction("food", 4.1f),
|
||||||
|
nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
void GrindingStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void GrindingStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
{
|
{
|
||||||
triggers.push_back(new TriggerNode("timer", NextAction::array(0, new NextAction("drink", 10.2f), nullptr)));
|
// reduce lower than loot
|
||||||
triggers.push_back(new TriggerNode("timer", NextAction::array(0, new NextAction("food", 10.1f), nullptr)));
|
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("no target", NextAction::array(0, new NextAction("attack anything", 10.0f), nullptr)));
|
new TriggerNode("no target", NextAction::array(0, new NextAction("attack anything", 4.0f), nullptr)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MoveRandomStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void MoveRandomStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
|
@ -13,13 +13,13 @@ void LootNonCombatStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("far from loot target", NextAction::array(0, new NextAction("move to loot", 7.0f), nullptr)));
|
new TriggerNode("far from loot target", NextAction::array(0, new NextAction("move to loot", 7.0f), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("can loot", NextAction::array(0, new NextAction("open loot", 8.0f), nullptr)));
|
triggers.push_back(new TriggerNode("can loot", NextAction::array(0, new NextAction("open loot", 8.0f), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("add all loot", 1.0f), nullptr)));
|
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("add all loot", 5.0f), nullptr)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void GatherStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void GatherStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
{
|
{
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("timer", NextAction::array(0, new NextAction("add gathering loot", 2.0f), nullptr)));
|
new TriggerNode("timer", NextAction::array(0, new NextAction("add gathering loot", 5.0f), nullptr)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void RevealStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void RevealStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
|
@ -7,6 +7,13 @@
|
|||||||
|
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
|
||||||
|
void StayStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
|
{
|
||||||
|
triggers.push_back(new TriggerNode(
|
||||||
|
"return to stay position",
|
||||||
|
NextAction::array(0, new NextAction("return to stay position", ACTION_MOVE), nullptr)));
|
||||||
|
}
|
||||||
|
|
||||||
NextAction** StayStrategy::getDefaultActions() { return NextAction::array(0, new NextAction("stay", 1.0f), nullptr); }
|
NextAction** StayStrategy::getDefaultActions() { return NextAction::array(0, new NextAction("stay", 1.0f), nullptr); }
|
||||||
|
|
||||||
void SitStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void SitStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
|
@ -10,12 +10,13 @@
|
|||||||
|
|
||||||
class PlayerbotAI;
|
class PlayerbotAI;
|
||||||
|
|
||||||
class StayStrategy : public NonCombatStrategy
|
class StayStrategy : public Strategy
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
StayStrategy(PlayerbotAI* botAI) : NonCombatStrategy(botAI) {}
|
StayStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
|
||||||
|
|
||||||
std::string const getName() override { return "stay"; }
|
std::string const getName() override { return "stay"; }
|
||||||
|
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||||
NextAction** getDefaultActions() override;
|
NextAction** getDefaultActions() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -35,10 +35,15 @@ void WorldPacketHandlerStrategy::InitTriggers(std::vector<TriggerNode*>& trigger
|
|||||||
new NextAction("taxi", relevance), nullptr)));
|
new NextAction("taxi", relevance), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("taxi done", NextAction::array(0, new NextAction("taxi", relevance), nullptr)));
|
triggers.push_back(new TriggerNode("taxi done", NextAction::array(0, new NextAction("taxi", relevance), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("trade status", NextAction::array(0, new NextAction("accept trade", relevance), new NextAction("equip upgrades", relevance), nullptr)));
|
triggers.push_back(new TriggerNode("trade status", NextAction::array(0, new NextAction("accept trade", relevance), new NextAction("equip upgrades", relevance), nullptr)));
|
||||||
|
triggers.push_back(new TriggerNode("trade status extended", NextAction::array(0, new NextAction("trade status extended", relevance), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("area trigger", NextAction::array(0, new NextAction("reach area trigger", relevance), nullptr)));
|
triggers.push_back(new TriggerNode("area trigger", NextAction::array(0, new NextAction("reach area trigger", relevance), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("within area trigger", NextAction::array(0, new NextAction("area trigger", relevance), nullptr)));
|
triggers.push_back(new TriggerNode("within area trigger", NextAction::array(0, new NextAction("area trigger", relevance), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("loot response", NextAction::array(0, new NextAction("store loot", relevance), nullptr)));
|
triggers.push_back(new TriggerNode("loot response", NextAction::array(0, new NextAction("store loot", relevance), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("item push result", NextAction::array(0, new NextAction("query item usage", relevance), new NextAction("equip upgrades", relevance), nullptr)));
|
triggers.push_back(new TriggerNode("item push result", NextAction::array(0, new NextAction("unlock items", relevance),
|
||||||
|
new NextAction("open items", relevance),
|
||||||
|
new NextAction("query item usage", relevance),
|
||||||
|
new NextAction("equip upgrades", relevance), nullptr)));
|
||||||
|
triggers.push_back(new TriggerNode("item push result", NextAction::array(0, new NextAction("quest item push result", relevance), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("ready check finished", NextAction::array(0, new NextAction("finish ready check", relevance), nullptr)));
|
triggers.push_back(new TriggerNode("ready check finished", NextAction::array(0, new NextAction("finish ready check", relevance), nullptr)));
|
||||||
// triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("security check", relevance), new NextAction("check mail", relevance), nullptr)));
|
// triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("security check", relevance), new NextAction("check mail", relevance), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("guild invite", NextAction::array(0, new NextAction("guild accept", relevance), nullptr)));
|
triggers.push_back(new TriggerNode("guild invite", NextAction::array(0, new NextAction("guild accept", relevance), nullptr)));
|
||||||
@ -79,7 +84,7 @@ WorldPacketHandlerStrategy::WorldPacketHandlerStrategy(PlayerbotAI* botAI) : Pas
|
|||||||
|
|
||||||
// quests
|
// quests
|
||||||
supported.push_back("quest update add kill");
|
supported.push_back("quest update add kill");
|
||||||
supported.push_back("quest update add item");
|
// supported.push_back("quest update add item");
|
||||||
supported.push_back("quest update failed");
|
supported.push_back("quest update failed");
|
||||||
supported.push_back("quest update failed timer");
|
supported.push_back("quest update failed timer");
|
||||||
supported.push_back("quest update complete");
|
supported.push_back("quest update complete");
|
||||||
|
@ -8,8 +8,18 @@
|
|||||||
#include "Event.h"
|
#include "Event.h"
|
||||||
#include "ObjectGuid.h"
|
#include "ObjectGuid.h"
|
||||||
#include "Player.h"
|
#include "Player.h"
|
||||||
|
#include "PlayerbotAIConfig.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
|
||||||
|
bool CastStealthAction::isUseful()
|
||||||
|
{
|
||||||
|
Unit* target = AI_VALUE(Unit*, "current target");
|
||||||
|
if (target && bot->GetDistance(target) >= sPlayerbotAIConfig->spellDistance)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool CastStealthAction::isPossible()
|
bool CastStealthAction::isPossible()
|
||||||
{
|
{
|
||||||
// do not use with WSG flag or EYE flag
|
// do not use with WSG flag or EYE flag
|
||||||
|
@ -44,7 +44,7 @@ public:
|
|||||||
CastStealthAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "stealth") {}
|
CastStealthAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "stealth") {}
|
||||||
|
|
||||||
std::string const GetTargetName() override { return "self target"; }
|
std::string const GetTargetName() override { return "self target"; }
|
||||||
|
bool isUseful() override;
|
||||||
bool isPossible() override;
|
bool isPossible() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2,18 +2,32 @@
|
|||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
#include "ChatHelper.h"
|
||||||
|
#include "G3D/Vector2.h"
|
||||||
|
#include "GossipDef.h"
|
||||||
|
#include "IVMapMgr.h"
|
||||||
|
#include "NewRpgInfo.h"
|
||||||
#include "NewRpgStrategy.h"
|
#include "NewRpgStrategy.h"
|
||||||
|
#include "Object.h"
|
||||||
|
#include "ObjectAccessor.h"
|
||||||
#include "ObjectDefines.h"
|
#include "ObjectDefines.h"
|
||||||
#include "ObjectGuid.h"
|
#include "ObjectGuid.h"
|
||||||
|
#include "ObjectMgr.h"
|
||||||
#include "PathGenerator.h"
|
#include "PathGenerator.h"
|
||||||
#include "Player.h"
|
#include "Player.h"
|
||||||
#include "PlayerbotAI.h"
|
#include "PlayerbotAI.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
#include "Position.h"
|
||||||
|
#include "QuestDef.h"
|
||||||
#include "Random.h"
|
#include "Random.h"
|
||||||
#include "RandomPlayerbotMgr.h"
|
#include "RandomPlayerbotMgr.h"
|
||||||
|
#include "SharedDefines.h"
|
||||||
|
#include "StatsWeightCalculator.h"
|
||||||
#include "Timer.h"
|
#include "Timer.h"
|
||||||
#include "TravelMgr.h"
|
#include "TravelMgr.h"
|
||||||
|
#include "BroadcastHelper.h"
|
||||||
#include "World.h"
|
#include "World.h"
|
||||||
|
|
||||||
bool TellRpgStatusAction::Execute(Event event)
|
bool TellRpgStatusAction::Execute(Event event)
|
||||||
@ -26,118 +40,156 @@ bool TellRpgStatusAction::Execute(Event event)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool StartRpgDoQuestAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
Player* owner = event.getOwner();
|
||||||
|
if (!owner)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::string const text = event.getParam();
|
||||||
|
PlayerbotChatHandler ch(owner);
|
||||||
|
uint32 questId = ch.extractQuestId(text);
|
||||||
|
const Quest* quest = sObjectMgr->GetQuestTemplate(questId);
|
||||||
|
if (quest)
|
||||||
|
{
|
||||||
|
botAI->rpgInfo.ChangeToDoQuest(questId, quest);
|
||||||
|
bot->Whisper("Start to do quest " + std::to_string(questId), LANG_UNIVERSAL, owner);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bot->Whisper("Invalid quest " + text, LANG_UNIVERSAL, owner);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool NewRpgStatusUpdateAction::Execute(Event event)
|
bool NewRpgStatusUpdateAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
NewRpgInfo& info = botAI->rpgInfo;
|
NewRpgInfo& info = botAI->rpgInfo;
|
||||||
|
/// @TODO: Refactor by transition probability
|
||||||
switch (info.status)
|
switch (info.status)
|
||||||
{
|
{
|
||||||
case NewRpgStatus::IDLE:
|
case RPG_IDLE:
|
||||||
{
|
{
|
||||||
uint32 roll = urand(1, 100);
|
uint32 roll = urand(1, 100);
|
||||||
// IDLE -> NEAR_NPC
|
// IDLE -> NEAR_NPC
|
||||||
// if ((!info.lastNearNpc || info.lastNearNpc + setNpcInterval < getMSTime()) && roll <= 30)
|
|
||||||
if (roll <= 30)
|
if (roll <= 30)
|
||||||
{
|
{
|
||||||
GuidVector possibleTargets = AI_VALUE(GuidVector, "possible rpg targets");
|
GuidVector possibleTargets = AI_VALUE(GuidVector, "possible new rpg targets");
|
||||||
if (!possibleTargets.empty())
|
if (possibleTargets.size() >= 3)
|
||||||
{
|
{
|
||||||
info.Reset();
|
info.ChangeToNearNpc();
|
||||||
info.lastNearNpc = getMSTime();
|
|
||||||
info.status = NewRpgStatus::NEAR_NPC;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// IDLE -> GO_INNKEEPER
|
// IDLE -> GO_INNKEEPER
|
||||||
else if (roll <= 45)
|
else if (roll <= 45)
|
||||||
{
|
{
|
||||||
WorldPosition pos = SelectRandomInnKeeperPos();
|
WorldPosition pos = SelectRandomInnKeeperPos(bot);
|
||||||
if (pos != WorldPosition() && bot->GetExactDist(pos) > 50.0f)
|
if (pos != WorldPosition() && bot->GetExactDist(pos) > 50.0f)
|
||||||
{
|
{
|
||||||
info.Reset();
|
info.ChangeToGoInnkeeper(pos);
|
||||||
info.lastGoInnKeeper = getMSTime();
|
|
||||||
info.status = NewRpgStatus::GO_INNKEEPER;
|
|
||||||
info.innKeeperPos = pos;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// IDLE -> GO_GRIND
|
// IDLE -> GO_GRIND
|
||||||
else if (roll <= 90)
|
else if (roll <= 100)
|
||||||
{
|
{
|
||||||
WorldPosition pos = SelectRandomGrindPos();
|
if (roll >= 60)
|
||||||
|
{
|
||||||
|
std::vector<uint32> availableQuests;
|
||||||
|
for (uint8 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot)
|
||||||
|
{
|
||||||
|
uint32 questId = bot->GetQuestSlotQuestId(slot);
|
||||||
|
if (botAI->lowPriorityQuest.find(questId) != botAI->lowPriorityQuest.end())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::vector<POIInfo> poiInfo;
|
||||||
|
if (GetQuestPOIPosAndObjectiveIdx(questId, poiInfo, true))
|
||||||
|
{
|
||||||
|
availableQuests.push_back(questId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (availableQuests.size())
|
||||||
|
{
|
||||||
|
uint32 questId = availableQuests[urand(0, availableQuests.size() - 1)];
|
||||||
|
const Quest* quest = sObjectMgr->GetQuestTemplate(questId);
|
||||||
|
if (quest)
|
||||||
|
{
|
||||||
|
// IDLE -> DO_QUEST
|
||||||
|
info.ChangeToDoQuest(questId, quest);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WorldPosition pos = SelectRandomGrindPos(bot);
|
||||||
if (pos != WorldPosition())
|
if (pos != WorldPosition())
|
||||||
{
|
{
|
||||||
info.Reset();
|
info.ChangeToGoGrind(pos);
|
||||||
info.lastGoGrind = getMSTime();
|
|
||||||
info.status = NewRpgStatus::GO_GRIND;
|
|
||||||
info.grindPos = pos;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// IDLE -> REST
|
// IDLE -> REST
|
||||||
info.Reset();
|
info.ChangeToRest();
|
||||||
info.status = NewRpgStatus::REST;
|
|
||||||
info.lastRest = getMSTime();
|
|
||||||
bot->SetStandState(UNIT_STAND_STATE_SIT);
|
bot->SetStandState(UNIT_STAND_STATE_SIT);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case NewRpgStatus::GO_GRIND:
|
case RPG_GO_GRIND:
|
||||||
{
|
{
|
||||||
WorldPosition& originalPos = info.grindPos;
|
WorldPosition& originalPos = info.go_grind.pos;
|
||||||
assert(info.grindPos != WorldPosition());
|
assert(info.go_grind.pos != WorldPosition());
|
||||||
// GO_GRIND -> NEAR_RANDOM
|
// GO_GRIND -> NEAR_RANDOM
|
||||||
if (bot->GetExactDist(originalPos) < 10.0f)
|
if (bot->GetExactDist(originalPos) < 10.0f)
|
||||||
{
|
{
|
||||||
info.Reset();
|
info.ChangeToNearRandom();
|
||||||
info.status = NewRpgStatus::NEAR_RANDOM;
|
|
||||||
info.lastNearRandom = getMSTime();
|
|
||||||
info.grindPos = WorldPosition();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case NewRpgStatus::GO_INNKEEPER:
|
case RPG_GO_INNKEEPER:
|
||||||
{
|
{
|
||||||
WorldPosition& originalPos = info.innKeeperPos;
|
WorldPosition& originalPos = info.go_innkeeper.pos;
|
||||||
assert(info.innKeeperPos != WorldPosition());
|
assert(info.go_innkeeper.pos != WorldPosition());
|
||||||
// GO_INNKEEPER -> NEAR_NPC
|
// GO_INNKEEPER -> NEAR_NPC
|
||||||
if (bot->GetExactDist(originalPos) < 10.0f)
|
if (bot->GetExactDist(originalPos) < 10.0f)
|
||||||
{
|
{
|
||||||
info.Reset();
|
info.ChangeToNearNpc();
|
||||||
info.lastNearNpc = getMSTime();
|
|
||||||
info.status = NewRpgStatus::NEAR_NPC;
|
|
||||||
info.innKeeperPos = WorldPosition();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case NewRpgStatus::NEAR_RANDOM:
|
case RPG_NEAR_RANDOM:
|
||||||
{
|
{
|
||||||
// NEAR_RANDOM -> IDLE
|
// NEAR_RANDOM -> IDLE
|
||||||
if (info.lastNearRandom + statusNearRandomDuration < getMSTime())
|
if (info.HasStatusPersisted(statusNearRandomDuration))
|
||||||
{
|
{
|
||||||
info.Reset();
|
info.ChangeToIdle();
|
||||||
info.status = NewRpgStatus::IDLE;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case NewRpgStatus::NEAR_NPC:
|
case RPG_DO_QUEST:
|
||||||
{
|
{
|
||||||
if (info.lastNearNpc + statusNearNpcDuration < getMSTime())
|
// DO_QUEST -> IDLE
|
||||||
|
if (info.HasStatusPersisted(statusDoQuestDuration))
|
||||||
{
|
{
|
||||||
info.Reset();
|
info.ChangeToIdle();
|
||||||
info.status = NewRpgStatus::IDLE;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case NewRpgStatus::REST:
|
case RPG_NEAR_NPC:
|
||||||
|
{
|
||||||
|
if (info.HasStatusPersisted(statusNearNpcDuration))
|
||||||
|
{
|
||||||
|
info.ChangeToIdle();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case RPG_REST:
|
||||||
{
|
{
|
||||||
// REST -> IDLE
|
// REST -> IDLE
|
||||||
if (info.lastRest + statusRestDuration < getMSTime())
|
if (info.HasStatusPersisted(statusRestDuration))
|
||||||
{
|
{
|
||||||
info.Reset();
|
info.ChangeToIdle();
|
||||||
info.status = NewRpgStatus::IDLE;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -148,258 +200,260 @@ bool NewRpgStatusUpdateAction::Execute(Event event)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
WorldPosition NewRpgStatusUpdateAction::SelectRandomGrindPos()
|
bool NewRpgGoGrindAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
const std::vector<WorldLocation>& locs = sRandomPlayerbotMgr->locsPerLevelCache[bot->GetLevel()];
|
if (SearchQuestGiverAndAcceptOrReward())
|
||||||
std::vector<WorldLocation> lo_prepared_locs, hi_prepared_locs;
|
return true;
|
||||||
for (auto& loc : locs)
|
|
||||||
{
|
|
||||||
if (bot->GetMapId() != loc.GetMapId())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (bot->GetMap()->GetZoneId(bot->GetPhaseMask(), loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ()) !=
|
return MoveFarTo(botAI->rpgInfo.go_grind.pos);
|
||||||
bot->GetZoneId())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (bot->GetExactDist(loc) < 500.0f)
|
|
||||||
{
|
|
||||||
hi_prepared_locs.push_back(loc);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bot->GetExactDist(loc) < 2500.0f)
|
|
||||||
{
|
|
||||||
lo_prepared_locs.push_back(loc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
WorldPosition dest{};
|
|
||||||
if (urand(1, 100) <= 50 && !hi_prepared_locs.empty())
|
|
||||||
{
|
|
||||||
uint32 idx = urand(0, hi_prepared_locs.size() - 1);
|
|
||||||
dest = hi_prepared_locs[idx];
|
|
||||||
}
|
|
||||||
else if (!lo_prepared_locs.empty())
|
|
||||||
{
|
|
||||||
uint32 idx = urand(0, lo_prepared_locs.size() - 1);
|
|
||||||
dest = lo_prepared_locs[idx];
|
|
||||||
}
|
|
||||||
LOG_DEBUG("playerbots", "[New Rpg] Bot {} select random grind pos Map:{} X:{} Y:{} Z:{} ({}+{} available in {})",
|
|
||||||
bot->GetName(), dest.GetMapId(), dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ(),
|
|
||||||
hi_prepared_locs.size(), lo_prepared_locs.size() - hi_prepared_locs.size(), locs.size());
|
|
||||||
return dest;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WorldPosition NewRpgStatusUpdateAction::SelectRandomInnKeeperPos()
|
bool NewRpgGoInnKeeperAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
const std::vector<WorldLocation>& locs = IsAlliance(bot->getRace())
|
if (SearchQuestGiverAndAcceptOrReward())
|
||||||
? sRandomPlayerbotMgr->allianceStarterPerLevelCache[bot->GetLevel()]
|
return true;
|
||||||
: sRandomPlayerbotMgr->hordeStarterPerLevelCache[bot->GetLevel()];
|
|
||||||
std::vector<WorldLocation> prepared_locs;
|
|
||||||
for (auto& loc : locs)
|
|
||||||
{
|
|
||||||
if (bot->GetMapId() != loc.GetMapId())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (bot->GetMap()->GetZoneId(bot->GetPhaseMask(), loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ()) !=
|
return MoveFarTo(botAI->rpgInfo.go_innkeeper.pos);
|
||||||
bot->GetZoneId())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
float range = bot->GetLevel() <= 5 ? 500.0f : 2500.0f;
|
|
||||||
if (bot->GetExactDist(loc) < range)
|
|
||||||
{
|
|
||||||
prepared_locs.push_back(loc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
WorldPosition dest{};
|
|
||||||
if (!prepared_locs.empty())
|
|
||||||
{
|
|
||||||
uint32 idx = urand(0, prepared_locs.size() - 1);
|
|
||||||
dest = prepared_locs[idx];
|
|
||||||
}
|
|
||||||
LOG_DEBUG("playerbots", "[New Rpg] Bot {} select random inn keeper pos Map:{} X:{} Y:{} Z:{} ({} available in {})",
|
|
||||||
bot->GetName(), dest.GetMapId(), dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ(),
|
|
||||||
prepared_locs.size(), locs.size());
|
|
||||||
return dest;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NewRpgGoFarAwayPosAction::MoveFarTo(WorldPosition dest)
|
|
||||||
{
|
|
||||||
if (dest == WorldPosition())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
float dis = bot->GetExactDist(dest);
|
|
||||||
if (dis < pathFinderDis)
|
|
||||||
{
|
|
||||||
return MoveTo(dest.getMapId(), dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ(), false, false,
|
|
||||||
false, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// performance optimization
|
|
||||||
if (IsWaitingForLastMove(MovementPriority::MOVEMENT_NORMAL))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// stuck check
|
|
||||||
float disToDest = bot->GetDistance(dest);
|
|
||||||
if (disToDest + 1.0f < botAI->rpgInfo.nearestMoveFarDis)
|
|
||||||
{
|
|
||||||
botAI->rpgInfo.nearestMoveFarDis = disToDest;
|
|
||||||
botAI->rpgInfo.stuckTs = getMSTime();
|
|
||||||
botAI->rpgInfo.stuckAttempts = 0;
|
|
||||||
}
|
|
||||||
else if (++botAI->rpgInfo.stuckAttempts >= 10 && botAI->rpgInfo.stuckTs + stuckTime < getMSTime())
|
|
||||||
{
|
|
||||||
// Unfortunately we've been stuck here for over 5 mins, fallback to teleporting directly to the destination
|
|
||||||
botAI->rpgInfo.stuckTs = getMSTime();
|
|
||||||
botAI->rpgInfo.stuckAttempts = 0;
|
|
||||||
const AreaTableEntry* entry = sAreaTableStore.LookupEntry(bot->GetZoneId());
|
|
||||||
std::string zone_name = PlayerbotAI::GetLocalizedAreaName(entry);
|
|
||||||
LOG_DEBUG("playerbots", "[New Rpg] Teleport {} from ({},{},{},{}) to ({},{},{},{}) as it stuck when moving far - Zone: {} ({})", bot->GetName(),
|
|
||||||
bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), bot->GetMapId(),
|
|
||||||
dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ(), dest.getMapId(), bot->GetZoneId(), zone_name);
|
|
||||||
return bot->TeleportTo(dest);
|
|
||||||
}
|
|
||||||
|
|
||||||
float minDelta = M_PI;
|
|
||||||
const float x = bot->GetPositionX();
|
|
||||||
const float y = bot->GetPositionY();
|
|
||||||
const float z = bot->GetPositionZ();
|
|
||||||
float rx, ry, rz;
|
|
||||||
bool found = false;
|
|
||||||
int attempt = 3;
|
|
||||||
while (--attempt)
|
|
||||||
{
|
|
||||||
float angle = bot->GetAngle(&dest);
|
|
||||||
float delta = urand(1, 100) <= 75 ? (rand_norm() - 0.5) * M_PI * 0.5 : (rand_norm() - 0.5) * M_PI * 2;
|
|
||||||
angle += delta;
|
|
||||||
float dis = rand_norm() * pathFinderDis;
|
|
||||||
float dx = x + cos(angle) * dis;
|
|
||||||
float dy = y + sin(angle) * dis;
|
|
||||||
float dz = z + 0.5f;
|
|
||||||
PathGenerator path(bot);
|
|
||||||
path.CalculatePath(dx, dy, dz);
|
|
||||||
PathType type = path.GetPathType();
|
|
||||||
uint32 typeOk = PATHFIND_NORMAL | PATHFIND_INCOMPLETE | PATHFIND_FARFROMPOLY;
|
|
||||||
bool canReach = !(type & (~typeOk));
|
|
||||||
|
|
||||||
if (canReach && fabs(delta) <= minDelta)
|
|
||||||
{
|
|
||||||
found = true;
|
|
||||||
const G3D::Vector3& endPos = path.GetActualEndPosition();
|
|
||||||
rx = endPos.x;
|
|
||||||
ry = endPos.y;
|
|
||||||
rz = endPos.z;
|
|
||||||
minDelta = fabs(delta);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (found)
|
|
||||||
{
|
|
||||||
return MoveTo(bot->GetMapId(), rx, ry, rz, false, false, false, true);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NewRpgGoGrindAction::Execute(Event event) { return MoveFarTo(botAI->rpgInfo.grindPos); }
|
|
||||||
|
|
||||||
bool NewRpgGoInnKeeperAction::Execute(Event event) { return MoveFarTo(botAI->rpgInfo.innKeeperPos); }
|
|
||||||
|
|
||||||
bool NewRpgMoveRandomAction::Execute(Event event)
|
bool NewRpgMoveRandomAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
float distance = rand_norm() * moveStep;
|
if (SearchQuestGiverAndAcceptOrReward())
|
||||||
Map* map = bot->GetMap();
|
|
||||||
const float x = bot->GetPositionX();
|
|
||||||
const float y = bot->GetPositionY();
|
|
||||||
const float z = bot->GetPositionZ();
|
|
||||||
int attempts = 5;
|
|
||||||
while (--attempts)
|
|
||||||
{
|
|
||||||
float angle = (float)rand_norm() * 2 * static_cast<float>(M_PI);
|
|
||||||
float dx = x + distance * cos(angle);
|
|
||||||
float dy = y + distance * sin(angle);
|
|
||||||
float dz = z;
|
|
||||||
if (!map->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(),
|
|
||||||
dx, dy, dz))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (map->IsInWater(bot->GetPhaseMask(), dx, dy, dz, bot->GetCollisionHeight()))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
bool moved = MoveTo(bot->GetMapId(), dx, dy, dz, false, false, false, true);
|
|
||||||
if (moved)
|
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return MoveRandomNear();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NewRpgMoveNpcAction::Execute(Event event)
|
bool NewRpgMoveNpcAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
NewRpgInfo& info = botAI->rpgInfo;
|
NewRpgInfo& info = botAI->rpgInfo;
|
||||||
if (!info.npcPos)
|
if (!info.near_npc.npcOrGo)
|
||||||
{
|
{
|
||||||
GuidVector possibleTargets = AI_VALUE(GuidVector, "possible rpg targets");
|
// No npc can be found, switch to IDLE
|
||||||
if (possibleTargets.empty())
|
ObjectGuid npcOrGo = ChooseNpcOrGameObjectToInteract();
|
||||||
return false;
|
if (npcOrGo.IsEmpty())
|
||||||
int idx = urand(0, possibleTargets.size() - 1);
|
|
||||||
ObjectGuid guid = possibleTargets[idx];
|
|
||||||
Unit* unit = botAI->GetUnit(guid);
|
|
||||||
if (unit)
|
|
||||||
{
|
{
|
||||||
info.npcPos = GuidPosition(unit);
|
info.ChangeToIdle();
|
||||||
info.lastReachNpc = 0;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
info.near_npc.npcOrGo = npcOrGo;
|
||||||
return false;
|
info.near_npc.lastReach = 0;
|
||||||
}
|
|
||||||
|
|
||||||
if (bot->GetDistance(info.npcPos) <= INTERACTION_DISTANCE)
|
|
||||||
{
|
|
||||||
if (!info.lastReachNpc)
|
|
||||||
{
|
|
||||||
info.lastReachNpc = getMSTime();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info.lastReachNpc && info.lastReachNpc + stayTime > getMSTime())
|
WorldObject* object = ObjectAccessor::GetWorldObject(*bot, info.near_npc.npcOrGo);
|
||||||
|
if (object && bot->GetDistance(object) <= INTERACTION_DISTANCE)
|
||||||
|
{
|
||||||
|
if (!info.near_npc.lastReach)
|
||||||
|
{
|
||||||
|
info.near_npc.lastReach = getMSTime();
|
||||||
|
if (bot->CanInteractWithQuestGiver(object))
|
||||||
|
InteractWithNpcOrGameObjectForQuest(info.near_npc.npcOrGo);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.near_npc.lastReach && GetMSTimeDiffToNow(info.near_npc.lastReach) < npcStayTime)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
info.npcPos = GuidPosition();
|
// has reached the npc for more than `npcStayTime`, select the next target
|
||||||
info.lastReachNpc = 0;
|
info.near_npc.npcOrGo = ObjectGuid();
|
||||||
|
info.near_npc.lastReach = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
assert(info.npcPos);
|
return MoveWorldObjectTo(info.near_npc.npcOrGo);
|
||||||
Unit* unit = botAI->GetUnit(info.npcPos);
|
|
||||||
if (!unit)
|
|
||||||
return false;
|
|
||||||
float x = unit->GetPositionX();
|
|
||||||
float y = unit->GetPositionY();
|
|
||||||
float z = unit->GetPositionZ();
|
|
||||||
float mapId = unit->GetMapId();
|
|
||||||
float angle = 0.f;
|
|
||||||
if (bot->IsWithinLOS(x, y, z))
|
|
||||||
{
|
|
||||||
if (!unit->isMoving())
|
|
||||||
angle = unit->GetAngle(bot) + (M_PI * irand(-25, 25) / 100.0); // Closest 45 degrees towards the target
|
|
||||||
else
|
|
||||||
angle = unit->GetOrientation() +
|
|
||||||
(M_PI * irand(-25, 25) / 100.0); // 45 degrees infront of target (leading it's movement)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
angle = 2 * M_PI * rand_norm(); // A circle around the target.
|
|
||||||
float rnd = rand_norm();
|
|
||||||
x += cos(angle) * INTERACTION_DISTANCE * rnd;
|
|
||||||
y += sin(angle) * INTERACTION_DISTANCE * rnd;
|
|
||||||
// bool exact = true;
|
|
||||||
if (!unit->GetMap()->CheckCollisionAndGetValidCoords(unit, unit->GetPositionX(), unit->GetPositionY(),
|
|
||||||
unit->GetPositionZ(), x, y, z))
|
|
||||||
{
|
|
||||||
x = unit->GetPositionX();
|
|
||||||
y = unit->GetPositionY();
|
|
||||||
z = unit->GetPositionZ();
|
|
||||||
// exact = false;
|
|
||||||
}
|
|
||||||
return MoveTo(mapId, x, y, z, false, false, false, true);
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool NewRpgDoQuestAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
if (SearchQuestGiverAndAcceptOrReward())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
NewRpgInfo& info = botAI->rpgInfo;
|
||||||
|
uint32 questId = RPG_INFO(quest, questId);
|
||||||
|
const Quest* quest = RPG_INFO(quest, quest);
|
||||||
|
uint8 questStatus = bot->GetQuestStatus(questId);
|
||||||
|
switch (questStatus)
|
||||||
|
{
|
||||||
|
case QUEST_STATUS_INCOMPLETE:
|
||||||
|
return DoIncompleteQuest();
|
||||||
|
case QUEST_STATUS_COMPLETE:
|
||||||
|
return DoCompletedQuest();
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
botAI->rpgInfo.ChangeToIdle();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NewRpgDoQuestAction::DoIncompleteQuest()
|
||||||
|
{
|
||||||
|
uint32 questId = RPG_INFO(do_quest, questId);
|
||||||
|
if (botAI->rpgInfo.do_quest.pos != WorldPosition())
|
||||||
|
{
|
||||||
|
/// @TODO: extract to a new function
|
||||||
|
int32 currentObjective = botAI->rpgInfo.do_quest.objectiveIdx;
|
||||||
|
// check if the objective has completed
|
||||||
|
Quest const* quest = sObjectMgr->GetQuestTemplate(questId);
|
||||||
|
const QuestStatusData& q_status = bot->getQuestStatusMap().at(questId);
|
||||||
|
bool completed = true;
|
||||||
|
if (currentObjective < QUEST_OBJECTIVES_COUNT)
|
||||||
|
{
|
||||||
|
if (q_status.CreatureOrGOCount[currentObjective] < quest->RequiredNpcOrGoCount[currentObjective])
|
||||||
|
completed = false;
|
||||||
|
}
|
||||||
|
else if (currentObjective < QUEST_OBJECTIVES_COUNT + QUEST_ITEM_OBJECTIVES_COUNT)
|
||||||
|
{
|
||||||
|
if (q_status.ItemCount[currentObjective - QUEST_OBJECTIVES_COUNT] <
|
||||||
|
quest->RequiredItemCount[currentObjective - QUEST_OBJECTIVES_COUNT])
|
||||||
|
completed = false;
|
||||||
|
}
|
||||||
|
// the current objective is completed, clear and find a new objective later
|
||||||
|
if (completed)
|
||||||
|
{
|
||||||
|
botAI->rpgInfo.do_quest.lastReachPOI = 0;
|
||||||
|
botAI->rpgInfo.do_quest.pos = WorldPosition();
|
||||||
|
botAI->rpgInfo.do_quest.objectiveIdx = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (botAI->rpgInfo.do_quest.pos == WorldPosition())
|
||||||
|
{
|
||||||
|
std::vector<POIInfo> poiInfo;
|
||||||
|
if (!GetQuestPOIPosAndObjectiveIdx(questId, poiInfo))
|
||||||
|
{
|
||||||
|
// can't find a poi pos to go, stop doing quest for now
|
||||||
|
botAI->rpgInfo.ChangeToIdle();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
uint32 rndIdx = urand(0, poiInfo.size() - 1);
|
||||||
|
G3D::Vector2 nearestPoi = poiInfo[rndIdx].pos;
|
||||||
|
int32 objectiveIdx = poiInfo[rndIdx].objectiveIdx;
|
||||||
|
|
||||||
|
float dx = nearestPoi.x, dy = nearestPoi.y;
|
||||||
|
|
||||||
|
// z = MAX_HEIGHT as we do not know accurate z
|
||||||
|
float dz = std::max(bot->GetMap()->GetHeight(dx, dy, MAX_HEIGHT), bot->GetMap()->GetWaterLevel(dx, dy));
|
||||||
|
|
||||||
|
// double check for GetQuestPOIPosAndObjectiveIdx
|
||||||
|
if (dz == INVALID_HEIGHT || dz == VMAP_INVALID_HEIGHT_VALUE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
WorldPosition pos(bot->GetMapId(), dx, dy, dz);
|
||||||
|
botAI->rpgInfo.do_quest.lastReachPOI = 0;
|
||||||
|
botAI->rpgInfo.do_quest.pos = pos;
|
||||||
|
botAI->rpgInfo.do_quest.objectiveIdx = objectiveIdx;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bot->GetDistance(botAI->rpgInfo.do_quest.pos) > 10.0f && !botAI->rpgInfo.do_quest.lastReachPOI)
|
||||||
|
{
|
||||||
|
return MoveFarTo(botAI->rpgInfo.do_quest.pos);
|
||||||
|
}
|
||||||
|
// Now we are near the quest objective
|
||||||
|
// kill mobs and looting quest should be done automatically by grind strategy
|
||||||
|
|
||||||
|
if (!botAI->rpgInfo.do_quest.lastReachPOI)
|
||||||
|
{
|
||||||
|
botAI->rpgInfo.do_quest.lastReachPOI = getMSTime();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// stayed at this POI for more than 5 minutes
|
||||||
|
if (GetMSTimeDiffToNow(botAI->rpgInfo.do_quest.lastReachPOI) >= poiStayTime)
|
||||||
|
{
|
||||||
|
bool hasProgression = false;
|
||||||
|
int32 currentObjective = botAI->rpgInfo.do_quest.objectiveIdx;
|
||||||
|
// check if the objective has progression
|
||||||
|
Quest const* quest = sObjectMgr->GetQuestTemplate(questId);
|
||||||
|
const QuestStatusData& q_status = bot->getQuestStatusMap().at(questId);
|
||||||
|
if (currentObjective < QUEST_OBJECTIVES_COUNT)
|
||||||
|
{
|
||||||
|
if (q_status.CreatureOrGOCount[currentObjective] != 0 && quest->RequiredNpcOrGoCount[currentObjective])
|
||||||
|
hasProgression = true;
|
||||||
|
}
|
||||||
|
else if (currentObjective < QUEST_OBJECTIVES_COUNT + QUEST_ITEM_OBJECTIVES_COUNT)
|
||||||
|
{
|
||||||
|
if (q_status.ItemCount[currentObjective - QUEST_OBJECTIVES_COUNT] != 0 &&
|
||||||
|
quest->RequiredItemCount[currentObjective - QUEST_OBJECTIVES_COUNT])
|
||||||
|
hasProgression = true;
|
||||||
|
}
|
||||||
|
if (!hasProgression)
|
||||||
|
{
|
||||||
|
// we has reach the poi for more than 5 mins but no progession
|
||||||
|
// may not be able to complete this quest, marked as abandoned
|
||||||
|
/// @TODO: It may be better to make lowPriorityQuest a global set shared by all bots (or saved in db)
|
||||||
|
botAI->lowPriorityQuest.insert(questId);
|
||||||
|
botAI->rpgStatistic.questAbandoned++;
|
||||||
|
LOG_DEBUG("playerbots", "[New rpg] {} marked as abandoned quest {}", bot->GetName(), questId);
|
||||||
|
botAI->rpgInfo.ChangeToIdle();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// clear and select another poi later
|
||||||
|
botAI->rpgInfo.do_quest.lastReachPOI = 0;
|
||||||
|
botAI->rpgInfo.do_quest.pos = WorldPosition();
|
||||||
|
botAI->rpgInfo.do_quest.objectiveIdx = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MoveRandomNear(20.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NewRpgDoQuestAction::DoCompletedQuest()
|
||||||
|
{
|
||||||
|
uint32 questId = RPG_INFO(quest, questId);
|
||||||
|
const Quest* quest = RPG_INFO(quest, quest);
|
||||||
|
|
||||||
|
if (RPG_INFO(quest, objectiveIdx) != -1)
|
||||||
|
{
|
||||||
|
// if quest is completed, back to poi with -1 idx to reward
|
||||||
|
BroadcastHelper::BroadcastQuestUpdateComplete(botAI, bot, quest);
|
||||||
|
botAI->rpgStatistic.questCompleted++;
|
||||||
|
std::vector<POIInfo> poiInfo;
|
||||||
|
if (!GetQuestPOIPosAndObjectiveIdx(questId, poiInfo, true))
|
||||||
|
{
|
||||||
|
// can't find a poi pos to reward, stop doing quest for now
|
||||||
|
botAI->rpgInfo.ChangeToIdle();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
assert(poiInfo.size() > 0);
|
||||||
|
// now we get the place to get rewarded
|
||||||
|
float dx = poiInfo[0].pos.x, dy = poiInfo[0].pos.y;
|
||||||
|
// z = MAX_HEIGHT as we do not know accurate z
|
||||||
|
float dz = std::max(bot->GetMap()->GetHeight(dx, dy, MAX_HEIGHT), bot->GetMap()->GetWaterLevel(dx, dy));
|
||||||
|
|
||||||
|
// double check for GetQuestPOIPosAndObjectiveIdx
|
||||||
|
if (dz == INVALID_HEIGHT || dz == VMAP_INVALID_HEIGHT_VALUE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
WorldPosition pos(bot->GetMapId(), dx, dy, dz);
|
||||||
|
botAI->rpgInfo.do_quest.lastReachPOI = 0;
|
||||||
|
botAI->rpgInfo.do_quest.pos = pos;
|
||||||
|
botAI->rpgInfo.do_quest.objectiveIdx = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (botAI->rpgInfo.do_quest.pos == WorldPosition())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (bot->GetDistance(botAI->rpgInfo.do_quest.pos) > 10.0f && !botAI->rpgInfo.do_quest.lastReachPOI)
|
||||||
|
return MoveFarTo(botAI->rpgInfo.do_quest.pos);
|
||||||
|
|
||||||
|
// Now we are near the qoi of reward
|
||||||
|
// the quest should be rewarded by SearchQuestGiverAndAcceptOrReward
|
||||||
|
if (!botAI->rpgInfo.do_quest.lastReachPOI)
|
||||||
|
{
|
||||||
|
botAI->rpgInfo.do_quest.lastReachPOI = getMSTime();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// stayed at this POI for more than 5 minutes
|
||||||
|
if (GetMSTimeDiffToNow(botAI->rpgInfo.do_quest.lastReachPOI) >= poiStayTime)
|
||||||
|
{
|
||||||
|
// e.g. Can not reward quest to gameobjects
|
||||||
|
/// @TODO: It may be better to make lowPriorityQuest a global set shared by all bots (or saved in db)
|
||||||
|
botAI->lowPriorityQuest.insert(questId);
|
||||||
|
botAI->rpgStatistic.questAbandoned++;
|
||||||
|
LOG_DEBUG("playerbots", "[New rpg] {} marked as abandoned quest {}", bot->GetName(), questId);
|
||||||
|
botAI->rpgInfo.ChangeToIdle();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
@ -3,9 +3,15 @@
|
|||||||
|
|
||||||
#include "Duration.h"
|
#include "Duration.h"
|
||||||
#include "MovementActions.h"
|
#include "MovementActions.h"
|
||||||
|
#include "NewRpgInfo.h"
|
||||||
#include "NewRpgStrategy.h"
|
#include "NewRpgStrategy.h"
|
||||||
|
#include "Object.h"
|
||||||
|
#include "ObjectDefines.h"
|
||||||
|
#include "ObjectGuid.h"
|
||||||
|
#include "QuestDef.h"
|
||||||
#include "TravelMgr.h"
|
#include "TravelMgr.h"
|
||||||
#include "PlayerbotAI.h"
|
#include "PlayerbotAI.h"
|
||||||
|
#include "NewRpgBaseAction.h"
|
||||||
|
|
||||||
class TellRpgStatusAction : public Action
|
class TellRpgStatusAction : public Action
|
||||||
{
|
{
|
||||||
@ -15,65 +21,78 @@ public:
|
|||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class NewRpgStatusUpdateAction : public Action
|
class StartRpgDoQuestAction : public Action
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
NewRpgStatusUpdateAction(PlayerbotAI* botAI) : Action(botAI, "new rpg status update") {}
|
StartRpgDoQuestAction(PlayerbotAI* botAI) : Action(botAI, "start rpg do quest") {}
|
||||||
|
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NewRpgStatusUpdateAction : public NewRpgBaseAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NewRpgStatusUpdateAction(PlayerbotAI* botAI) : NewRpgBaseAction(botAI, "new rpg status update")
|
||||||
|
{
|
||||||
|
// int statusCount = RPG_STATUS_END - 1;
|
||||||
|
|
||||||
|
// transitionMat.resize(statusCount, std::vector<int>(statusCount, 0));
|
||||||
|
|
||||||
|
// transitionMat[RPG_IDLE][RPG_GO_GRIND] = 20;
|
||||||
|
// transitionMat[RPG_IDLE][RPG_GO_INNKEEPER] = 15;
|
||||||
|
// transitionMat[RPG_IDLE][RPG_NEAR_NPC] = 30;
|
||||||
|
// transitionMat[RPG_IDLE][RPG_DO_QUEST] = 35;
|
||||||
|
}
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
protected:
|
protected:
|
||||||
// const int32 setGrindInterval = 5 * 60 * 1000;
|
// static NewRpgStatusTransitionProb transitionMat;
|
||||||
// const int32 setNpcInterval = 1 * 60 * 1000;
|
|
||||||
const int32 statusNearNpcDuration = 5 * 60 * 1000;
|
const int32 statusNearNpcDuration = 5 * 60 * 1000;
|
||||||
const int32 statusNearRandomDuration = 5 * 60 * 1000;
|
const int32 statusNearRandomDuration = 5 * 60 * 1000;
|
||||||
const int32 statusRestDuration = 30 * 1000;
|
const int32 statusRestDuration = 30 * 1000;
|
||||||
WorldPosition SelectRandomGrindPos();
|
const int32 statusDoQuestDuration = 30 * 60 * 1000;
|
||||||
WorldPosition SelectRandomInnKeeperPos();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class NewRpgGoFarAwayPosAction : public MovementAction
|
class NewRpgGoGrindAction : public NewRpgBaseAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
NewRpgGoFarAwayPosAction(PlayerbotAI* botAI, std::string name) : MovementAction(botAI, name) {}
|
NewRpgGoGrindAction(PlayerbotAI* botAI) : NewRpgBaseAction(botAI, "new rpg go grind") {}
|
||||||
// bool Execute(Event event) override;
|
|
||||||
bool MoveFarTo(WorldPosition dest);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// WorldPosition dest;
|
|
||||||
const float pathFinderDis = 70.0f; // path finder
|
|
||||||
const uint32 stuckTime = 5 * 60 * 1000;
|
|
||||||
};
|
|
||||||
|
|
||||||
class NewRpgGoGrindAction : public NewRpgGoFarAwayPosAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NewRpgGoGrindAction(PlayerbotAI* botAI) : NewRpgGoFarAwayPosAction(botAI, "new rpg go grind") {}
|
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class NewRpgGoInnKeeperAction : public NewRpgGoFarAwayPosAction
|
class NewRpgGoInnKeeperAction : public NewRpgBaseAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
NewRpgGoInnKeeperAction(PlayerbotAI* botAI) : NewRpgGoFarAwayPosAction(botAI, "new rpg go innkeeper") {}
|
NewRpgGoInnKeeperAction(PlayerbotAI* botAI) : NewRpgBaseAction(botAI, "new rpg go innkeeper") {}
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class NewRpgMoveRandomAction : public MovementAction
|
class NewRpgMoveRandomAction : public NewRpgBaseAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
NewRpgMoveRandomAction(PlayerbotAI* botAI) : MovementAction(botAI, "new rpg move random") {}
|
NewRpgMoveRandomAction(PlayerbotAI* botAI) : NewRpgBaseAction(botAI, "new rpg move random") {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NewRpgMoveNpcAction : public NewRpgBaseAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NewRpgMoveNpcAction(PlayerbotAI* botAI) : NewRpgBaseAction(botAI, "new rpg move npcs") {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
|
||||||
|
const uint32 npcStayTime = 8 * 1000;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NewRpgDoQuestAction : public NewRpgBaseAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NewRpgDoQuestAction(PlayerbotAI* botAI) : NewRpgBaseAction(botAI, "new rpg do quest") {}
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
protected:
|
protected:
|
||||||
const float moveStep = 50.0f;
|
bool DoIncompleteQuest();
|
||||||
};
|
bool DoCompletedQuest();
|
||||||
|
|
||||||
class NewRpgMoveNpcAction : public MovementAction
|
const uint32 poiStayTime = 5 * 60 * 1000;
|
||||||
{
|
|
||||||
public:
|
|
||||||
NewRpgMoveNpcAction(PlayerbotAI* botAI) : MovementAction(botAI, "new rpg move npcs") {}
|
|
||||||
bool Execute(Event event) override;
|
|
||||||
protected:
|
|
||||||
const uint32 stayTime = 8 * 1000;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
801
modules/mod-playerbots/src/strategy/rpg/NewRpgBaseAction.cpp
Normal file
801
modules/mod-playerbots/src/strategy/rpg/NewRpgBaseAction.cpp
Normal file
@ -0,0 +1,801 @@
|
|||||||
|
#include "NewRpgBaseAction.h"
|
||||||
|
#include "ChatHelper.h"
|
||||||
|
#include "G3D/Vector2.h"
|
||||||
|
#include "GameObject.h"
|
||||||
|
#include "GossipDef.h"
|
||||||
|
#include "GridTerrainData.h"
|
||||||
|
#include "IVMapMgr.h"
|
||||||
|
#include "NewRpgInfo.h"
|
||||||
|
#include "NewRpgStrategy.h"
|
||||||
|
#include "Object.h"
|
||||||
|
#include "ObjectAccessor.h"
|
||||||
|
#include "ObjectDefines.h"
|
||||||
|
#include "ObjectGuid.h"
|
||||||
|
#include "ObjectMgr.h"
|
||||||
|
#include "PathGenerator.h"
|
||||||
|
#include "Player.h"
|
||||||
|
#include "PlayerbotAI.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
#include "Position.h"
|
||||||
|
#include "QuestDef.h"
|
||||||
|
#include "Random.h"
|
||||||
|
#include "RandomPlayerbotMgr.h"
|
||||||
|
#include "SharedDefines.h"
|
||||||
|
#include "StatsWeightCalculator.h"
|
||||||
|
#include "Timer.h"
|
||||||
|
#include "TravelMgr.h"
|
||||||
|
#include "BroadcastHelper.h"
|
||||||
|
|
||||||
|
bool NewRpgBaseAction::MoveFarTo(WorldPosition dest)
|
||||||
|
{
|
||||||
|
if (dest == WorldPosition())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (dest != botAI->rpgInfo.moveFarPos)
|
||||||
|
{
|
||||||
|
// clear stuck information if it's a new dest
|
||||||
|
botAI->rpgInfo.SetMoveFarTo(dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
float dis = bot->GetExactDist(dest);
|
||||||
|
if (dis < pathFinderDis)
|
||||||
|
{
|
||||||
|
return MoveTo(dest.getMapId(), dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ(), false, false,
|
||||||
|
false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// performance optimization
|
||||||
|
if (IsWaitingForLastMove(MovementPriority::MOVEMENT_NORMAL))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// stuck check
|
||||||
|
float disToDest = bot->GetDistance(dest);
|
||||||
|
if (disToDest + 1.0f < botAI->rpgInfo.nearestMoveFarDis)
|
||||||
|
{
|
||||||
|
botAI->rpgInfo.nearestMoveFarDis = disToDest;
|
||||||
|
botAI->rpgInfo.stuckTs = getMSTime();
|
||||||
|
botAI->rpgInfo.stuckAttempts = 0;
|
||||||
|
}
|
||||||
|
else if (++botAI->rpgInfo.stuckAttempts >= 10 && GetMSTimeDiffToNow(botAI->rpgInfo.stuckTs) >= stuckTime)
|
||||||
|
{
|
||||||
|
// Unfortunately we've been stuck here for over 5 mins, fallback to teleporting directly to the destination
|
||||||
|
botAI->rpgInfo.stuckTs = getMSTime();
|
||||||
|
botAI->rpgInfo.stuckAttempts = 0;
|
||||||
|
const AreaTableEntry* entry = sAreaTableStore.LookupEntry(bot->GetZoneId());
|
||||||
|
std::string zone_name = PlayerbotAI::GetLocalizedAreaName(entry);
|
||||||
|
LOG_DEBUG("playerbots", "[New Rpg] Teleport {} from ({},{},{},{}) to ({},{},{},{}) as it stuck when moving far - Zone: {} ({})", bot->GetName(),
|
||||||
|
bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), bot->GetMapId(),
|
||||||
|
dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ(), dest.getMapId(), bot->GetZoneId(), zone_name);
|
||||||
|
return bot->TeleportTo(dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
float minDelta = M_PI;
|
||||||
|
const float x = bot->GetPositionX();
|
||||||
|
const float y = bot->GetPositionY();
|
||||||
|
const float z = bot->GetPositionZ();
|
||||||
|
float rx, ry, rz;
|
||||||
|
bool found = false;
|
||||||
|
int attempt = 3;
|
||||||
|
while (--attempt)
|
||||||
|
{
|
||||||
|
float angle = bot->GetAngle(&dest);
|
||||||
|
float delta = urand(1, 100) <= 75 ? (rand_norm() - 0.5) * M_PI * 0.5 : (rand_norm() - 0.5) * M_PI * 2;
|
||||||
|
angle += delta;
|
||||||
|
float dis = rand_norm() * pathFinderDis;
|
||||||
|
float dx = x + cos(angle) * dis;
|
||||||
|
float dy = y + sin(angle) * dis;
|
||||||
|
float dz = z + 0.5f;
|
||||||
|
PathGenerator path(bot);
|
||||||
|
path.CalculatePath(dx, dy, dz);
|
||||||
|
PathType type = path.GetPathType();
|
||||||
|
uint32 typeOk = PATHFIND_NORMAL | PATHFIND_INCOMPLETE | PATHFIND_FARFROMPOLY;
|
||||||
|
bool canReach = !(type & (~typeOk));
|
||||||
|
|
||||||
|
if (canReach && fabs(delta) <= minDelta)
|
||||||
|
{
|
||||||
|
found = true;
|
||||||
|
const G3D::Vector3& endPos = path.GetActualEndPosition();
|
||||||
|
rx = endPos.x;
|
||||||
|
ry = endPos.y;
|
||||||
|
rz = endPos.z;
|
||||||
|
minDelta = fabs(delta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
return MoveTo(bot->GetMapId(), rx, ry, rz, false, false, false, true);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NewRpgBaseAction::MoveWorldObjectTo(ObjectGuid guid, float distance)
|
||||||
|
{
|
||||||
|
if (IsWaitingForLastMove(MovementPriority::MOVEMENT_NORMAL))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
WorldObject* object = botAI->GetWorldObject(guid);
|
||||||
|
if (!object)
|
||||||
|
return false;
|
||||||
|
float x = object->GetPositionX();
|
||||||
|
float y = object->GetPositionY();
|
||||||
|
float z = object->GetPositionZ();
|
||||||
|
float mapId = object->GetMapId();
|
||||||
|
float angle = 0.f;
|
||||||
|
|
||||||
|
if (!object->ToUnit() || !object->ToUnit()->isMoving())
|
||||||
|
angle = object->GetAngle(bot) + (M_PI * irand(-25, 25) / 100.0); // Closest 45 degrees towards the target
|
||||||
|
else
|
||||||
|
angle = object->GetOrientation() +
|
||||||
|
(M_PI * irand(-25, 25) / 100.0); // 45 degrees infront of target (leading it's movement)
|
||||||
|
|
||||||
|
float rnd = rand_norm();
|
||||||
|
x += cos(angle) * distance * rnd;
|
||||||
|
y += sin(angle) * distance * rnd;
|
||||||
|
if (!object->GetMap()->CheckCollisionAndGetValidCoords(object, object->GetPositionX(), object->GetPositionY(),
|
||||||
|
object->GetPositionZ(), x, y, z))
|
||||||
|
{
|
||||||
|
x = object->GetPositionX();
|
||||||
|
y = object->GetPositionY();
|
||||||
|
z = object->GetPositionZ();
|
||||||
|
}
|
||||||
|
return MoveTo(mapId, x, y, z, false, false, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NewRpgBaseAction::MoveRandomNear(float moveStep, MovementPriority priority)
|
||||||
|
{
|
||||||
|
if (IsWaitingForLastMove(priority))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
float distance = rand_norm() * moveStep;
|
||||||
|
Map* map = bot->GetMap();
|
||||||
|
const float x = bot->GetPositionX();
|
||||||
|
const float y = bot->GetPositionY();
|
||||||
|
const float z = bot->GetPositionZ();
|
||||||
|
int attempts = 5;
|
||||||
|
while (--attempts)
|
||||||
|
{
|
||||||
|
float angle = (float)rand_norm() * 2 * static_cast<float>(M_PI);
|
||||||
|
float dx = x + distance * cos(angle);
|
||||||
|
float dy = y + distance * sin(angle);
|
||||||
|
float dz = z;
|
||||||
|
|
||||||
|
PathGenerator path(bot);
|
||||||
|
path.CalculatePath(dx, dy, dz);
|
||||||
|
PathType type = path.GetPathType();
|
||||||
|
uint32 typeOk = PATHFIND_NORMAL | PATHFIND_INCOMPLETE | PATHFIND_FARFROMPOLY;
|
||||||
|
bool canReach = !(type & (~typeOk));
|
||||||
|
|
||||||
|
if (!canReach)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!map->CanReachPositionAndGetValidCoords(bot, dx, dy, dz))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (map->IsInWater(bot->GetPhaseMask(), dx, dy, dz, bot->GetCollisionHeight()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
bool moved = MoveTo(bot->GetMapId(), dx, dy, dz, false, false, false, true, priority);
|
||||||
|
if (moved)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NewRpgBaseAction::ForceToWait(uint32 duration, MovementPriority priority)
|
||||||
|
{
|
||||||
|
AI_VALUE(LastMovement&, "last movement").Set(bot->GetMapId(),
|
||||||
|
bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), bot->GetOrientation(), duration, priority);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @TODO: Fix redundant code
|
||||||
|
/// Quest related method refer to TalkToQuestGiverAction.h
|
||||||
|
bool NewRpgBaseAction::InteractWithNpcOrGameObjectForQuest(ObjectGuid guid)
|
||||||
|
{
|
||||||
|
WorldObject* object = ObjectAccessor::GetWorldObject(*bot, guid);
|
||||||
|
if (!object || !bot->CanInteractWithQuestGiver(object))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Creature* creature = bot->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_NONE);
|
||||||
|
// if (creature)
|
||||||
|
// {
|
||||||
|
// WorldPacket packet(CMSG_GOSSIP_HELLO);
|
||||||
|
// packet << guid;
|
||||||
|
// bot->GetSession()->HandleGossipHelloOpcode(packet);
|
||||||
|
// }
|
||||||
|
|
||||||
|
bot->PrepareQuestMenu(guid);
|
||||||
|
const QuestMenu &menu = bot->PlayerTalkClass->GetQuestMenu();
|
||||||
|
if (menu.Empty())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
for (uint8 idx = 0; idx < menu.GetMenuItemCount(); idx++)
|
||||||
|
{
|
||||||
|
const QuestMenuItem &item = menu.GetItem(idx);
|
||||||
|
const Quest* quest = sObjectMgr->GetQuestTemplate(item.QuestId);
|
||||||
|
if (!quest)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const QuestStatus &status = bot->GetQuestStatus(item.QuestId);
|
||||||
|
if (status == QUEST_STATUS_NONE && bot->CanTakeQuest(quest, false) &&
|
||||||
|
bot->CanAddQuest(quest, false) && IsQuestWorthDoing(quest) && IsQuestCapableDoing(quest))
|
||||||
|
{
|
||||||
|
AcceptQuest(quest, guid);
|
||||||
|
if (botAI->GetMaster())
|
||||||
|
botAI->TellMasterNoFacing("Quest accepted " + ChatHelper::FormatQuest(quest));
|
||||||
|
BroadcastHelper::BroadcastQuestAccepted(botAI, bot, quest);
|
||||||
|
botAI->rpgStatistic.questAccepted++;
|
||||||
|
LOG_DEBUG("playerbots", "[New rpg] {} accept quest {}", bot->GetName(), quest->GetQuestId());
|
||||||
|
}
|
||||||
|
if (status == QUEST_STATUS_COMPLETE && bot->CanRewardQuest(quest, 0, false))
|
||||||
|
{
|
||||||
|
TurnInQuest(quest, guid);
|
||||||
|
if (botAI->GetMaster())
|
||||||
|
botAI->TellMasterNoFacing("Quest rewarded " + ChatHelper::FormatQuest(quest));
|
||||||
|
BroadcastHelper::BroadcastQuestTurnedIn(botAI, bot, quest);
|
||||||
|
botAI->rpgStatistic.questRewarded++;
|
||||||
|
LOG_DEBUG("playerbots", "[New rpg] {} turned in quest {}", bot->GetName(), quest->GetQuestId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NewRpgBaseAction::AcceptQuest(Quest const* quest, ObjectGuid guid)
|
||||||
|
{
|
||||||
|
WorldPacket p(CMSG_QUESTGIVER_ACCEPT_QUEST);
|
||||||
|
uint32 unk1 = 0;
|
||||||
|
p << guid << quest->GetQuestId() << unk1;
|
||||||
|
p.rpos(0);
|
||||||
|
bot->GetSession()->HandleQuestgiverAcceptQuestOpcode(p);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NewRpgBaseAction::TurnInQuest(Quest const* quest, ObjectGuid guid)
|
||||||
|
{
|
||||||
|
uint32 questID = quest->GetQuestId();
|
||||||
|
|
||||||
|
if (bot->GetQuestRewardStatus(questID))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bot->CanRewardQuest(quest, false))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bot->PlayDistanceSound(621);
|
||||||
|
|
||||||
|
WorldPacket p(CMSG_QUESTGIVER_CHOOSE_REWARD);
|
||||||
|
p << guid << quest->GetQuestId();
|
||||||
|
if (quest->GetRewChoiceItemsCount() <= 1)
|
||||||
|
{
|
||||||
|
p << 0;
|
||||||
|
bot->GetSession()->HandleQuestgiverChooseRewardOpcode(p);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uint32 bestId = BestReward(quest);
|
||||||
|
p << bestId;
|
||||||
|
bot->GetSession()->HandleQuestgiverChooseRewardOpcode(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 NewRpgBaseAction::BestReward(Quest const* quest)
|
||||||
|
{
|
||||||
|
ItemIds returnIds;
|
||||||
|
ItemUsage bestUsage = ITEM_USAGE_NONE;
|
||||||
|
if (quest->GetRewChoiceItemsCount() <= 1)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (uint8 i = 0; i < quest->GetRewChoiceItemsCount(); ++i)
|
||||||
|
{
|
||||||
|
ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", quest->RewardChoiceItemId[i]);
|
||||||
|
if (usage == ITEM_USAGE_EQUIP || usage == ITEM_USAGE_REPLACE)
|
||||||
|
bestUsage = ITEM_USAGE_EQUIP;
|
||||||
|
else if (usage == ITEM_USAGE_BAD_EQUIP && bestUsage != ITEM_USAGE_EQUIP)
|
||||||
|
bestUsage = usage;
|
||||||
|
else if (usage != ITEM_USAGE_NONE && bestUsage == ITEM_USAGE_NONE)
|
||||||
|
bestUsage = usage;
|
||||||
|
}
|
||||||
|
StatsWeightCalculator calc(bot);
|
||||||
|
uint32 best = 0;
|
||||||
|
float bestScore = 0;
|
||||||
|
for (uint8 i = 0; i < quest->GetRewChoiceItemsCount(); ++i)
|
||||||
|
{
|
||||||
|
ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", quest->RewardChoiceItemId[i]);
|
||||||
|
if (usage == bestUsage || usage == ITEM_USAGE_REPLACE)
|
||||||
|
{
|
||||||
|
float score = calc.CalculateItem(quest->RewardChoiceItemId[i]);
|
||||||
|
if (score > bestScore)
|
||||||
|
{
|
||||||
|
bestScore = score;
|
||||||
|
best = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NewRpgBaseAction::IsQuestWorthDoing(Quest const* quest)
|
||||||
|
{
|
||||||
|
bool isLowLevelQuest = bot->GetLevel() > (bot->GetQuestLevel(quest) + sWorld->getIntConfig(CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF));
|
||||||
|
|
||||||
|
if (isLowLevelQuest)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (quest->IsRepeatable())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (quest->IsSeasonal())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NewRpgBaseAction::IsQuestCapableDoing(Quest const* quest)
|
||||||
|
{
|
||||||
|
bool highLevelQuest = bot->GetLevel() + 3 < bot->GetQuestLevel(quest);
|
||||||
|
if (highLevelQuest)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Elite quest and dungeon quest etc
|
||||||
|
if (quest->GetType() != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// now we only capable of doing solo quests
|
||||||
|
if (quest->GetSuggestedPlayers() >= 2)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NewRpgBaseAction::OrganizeQuestLog()
|
||||||
|
{
|
||||||
|
int32 freeSlotNum = 0;
|
||||||
|
|
||||||
|
for (uint16 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
|
||||||
|
{
|
||||||
|
uint32 questId = bot->GetQuestSlotQuestId(i);
|
||||||
|
if (!questId)
|
||||||
|
freeSlotNum++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// it's ok if we have two more free slots
|
||||||
|
if (freeSlotNum >= 2)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int32 dropped = 0;
|
||||||
|
// remove quests that not worth doing or not capable of doing
|
||||||
|
for (uint16 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
|
||||||
|
{
|
||||||
|
uint32 questId = bot->GetQuestSlotQuestId(i);
|
||||||
|
if (!questId)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const Quest* quest = sObjectMgr->GetQuestTemplate(questId);
|
||||||
|
if (!IsQuestWorthDoing(quest) ||
|
||||||
|
!IsQuestCapableDoing(quest) ||
|
||||||
|
bot->GetQuestStatus(questId) == QUEST_STATUS_FAILED)
|
||||||
|
{
|
||||||
|
LOG_DEBUG("playerbots", "[New rpg] {} drop quest {}", bot->GetName(), questId);
|
||||||
|
WorldPacket packet(CMSG_QUESTLOG_REMOVE_QUEST);
|
||||||
|
packet << (uint8)i;
|
||||||
|
bot->GetSession()->HandleQuestLogRemoveQuest(packet);
|
||||||
|
if (botAI->GetMaster())
|
||||||
|
botAI->TellMasterNoFacing("Quest dropped " + ChatHelper::FormatQuest(quest));
|
||||||
|
botAI->rpgStatistic.questDropped++;
|
||||||
|
dropped++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// drop more than 8 quests at once to avoid repeated accept and drop
|
||||||
|
if (dropped >= 8)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// remove festival/class quests and quests in different zone
|
||||||
|
for (uint16 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
|
||||||
|
{
|
||||||
|
uint32 questId = bot->GetQuestSlotQuestId(i);
|
||||||
|
if (!questId)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const Quest* quest = sObjectMgr->GetQuestTemplate(questId);
|
||||||
|
if (quest->GetZoneOrSort() < 0 ||
|
||||||
|
(quest->GetZoneOrSort() > 0 && quest->GetZoneOrSort() != bot->GetZoneId()))
|
||||||
|
{
|
||||||
|
LOG_DEBUG("playerbots", "[New rpg] {} drop quest {}", bot->GetName(), questId);
|
||||||
|
WorldPacket packet(CMSG_QUESTLOG_REMOVE_QUEST);
|
||||||
|
packet << (uint8)i;
|
||||||
|
bot->GetSession()->HandleQuestLogRemoveQuest(packet);
|
||||||
|
if (botAI->GetMaster())
|
||||||
|
botAI->TellMasterNoFacing("Quest dropped " + ChatHelper::FormatQuest(quest));
|
||||||
|
botAI->rpgStatistic.questDropped++;
|
||||||
|
dropped++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dropped >= 8)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// clear quests log
|
||||||
|
for (uint16 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
|
||||||
|
{
|
||||||
|
uint32 questId = bot->GetQuestSlotQuestId(i);
|
||||||
|
if (!questId)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const Quest* quest = sObjectMgr->GetQuestTemplate(questId);
|
||||||
|
LOG_DEBUG("playerbots", "[New rpg] {} drop quest {}", bot->GetName(), questId);
|
||||||
|
WorldPacket packet(CMSG_QUESTLOG_REMOVE_QUEST);
|
||||||
|
packet << (uint8)i;
|
||||||
|
bot->GetSession()->HandleQuestLogRemoveQuest(packet);
|
||||||
|
if (botAI->GetMaster())
|
||||||
|
botAI->TellMasterNoFacing("Quest dropped " + ChatHelper::FormatQuest(quest));
|
||||||
|
botAI->rpgStatistic.questDropped++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NewRpgBaseAction::SearchQuestGiverAndAcceptOrReward()
|
||||||
|
{
|
||||||
|
OrganizeQuestLog();
|
||||||
|
if (ObjectGuid npcOrGo = ChooseNpcOrGameObjectToInteract(true, 80.0f))
|
||||||
|
{
|
||||||
|
WorldObject* object = ObjectAccessor::GetWorldObject(*bot, npcOrGo);
|
||||||
|
if (bot->CanInteractWithQuestGiver(object))
|
||||||
|
{
|
||||||
|
InteractWithNpcOrGameObjectForQuest(npcOrGo);
|
||||||
|
ForceToWait(5000);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return MoveWorldObjectTo(npcOrGo);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectGuid NewRpgBaseAction::ChooseNpcOrGameObjectToInteract(bool questgiverOnly, float distanceLimit)
|
||||||
|
{
|
||||||
|
GuidVector possibleTargets = AI_VALUE(GuidVector, "possible new rpg targets");
|
||||||
|
GuidVector possibleGameObjects = AI_VALUE(GuidVector, "possible new rpg game objects");
|
||||||
|
|
||||||
|
if (possibleTargets.empty() && possibleGameObjects.empty())
|
||||||
|
return ObjectGuid();
|
||||||
|
|
||||||
|
WorldObject* nearestObject = nullptr;
|
||||||
|
for (ObjectGuid& guid: possibleTargets)
|
||||||
|
{
|
||||||
|
WorldObject* object = ObjectAccessor::GetWorldObject(*bot, guid);
|
||||||
|
|
||||||
|
if (!object || !object->IsInWorld())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (distanceLimit && bot->GetDistance(object) > distanceLimit)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (HasQuestToAcceptOrReward(object))
|
||||||
|
{
|
||||||
|
if (!nearestObject || bot->GetExactDist(nearestObject) > bot->GetExactDist(object))
|
||||||
|
nearestObject = object;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ObjectGuid& guid: possibleGameObjects)
|
||||||
|
{
|
||||||
|
WorldObject* object = ObjectAccessor::GetWorldObject(*bot, guid);
|
||||||
|
|
||||||
|
if (!object || !object->IsInWorld())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (distanceLimit && bot->GetDistance(object) > distanceLimit)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (HasQuestToAcceptOrReward(object))
|
||||||
|
{
|
||||||
|
if (!nearestObject || bot->GetExactDist(nearestObject) > bot->GetExactDist(object))
|
||||||
|
nearestObject = object;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nearestObject)
|
||||||
|
return nearestObject->GetGUID();
|
||||||
|
|
||||||
|
// No questgiver to accept or reward
|
||||||
|
if (questgiverOnly)
|
||||||
|
return ObjectGuid();
|
||||||
|
|
||||||
|
if (possibleTargets.empty())
|
||||||
|
return ObjectGuid();
|
||||||
|
|
||||||
|
int idx = urand(0, possibleTargets.size() - 1);
|
||||||
|
ObjectGuid guid = possibleTargets[idx];
|
||||||
|
WorldObject* object = ObjectAccessor::GetCreatureOrPetOrVehicle(*bot, guid);
|
||||||
|
if (!object)
|
||||||
|
object = ObjectAccessor::GetGameObject(*bot, guid);
|
||||||
|
|
||||||
|
if (object && object->IsInWorld())
|
||||||
|
{
|
||||||
|
return object->GetGUID();
|
||||||
|
}
|
||||||
|
return ObjectGuid();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NewRpgBaseAction::HasQuestToAcceptOrReward(WorldObject* object)
|
||||||
|
{
|
||||||
|
ObjectGuid guid = object->GetGUID();
|
||||||
|
bot->PrepareQuestMenu(guid);
|
||||||
|
const QuestMenu &menu = bot->PlayerTalkClass->GetQuestMenu();
|
||||||
|
if (menu.Empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (uint8 idx = 0; idx < menu.GetMenuItemCount(); idx++)
|
||||||
|
{
|
||||||
|
const QuestMenuItem &item = menu.GetItem(idx);
|
||||||
|
const Quest* quest = sObjectMgr->GetQuestTemplate(item.QuestId);
|
||||||
|
if (!quest)
|
||||||
|
continue;
|
||||||
|
const QuestStatus &status = bot->GetQuestStatus(item.QuestId);
|
||||||
|
if (status == QUEST_STATUS_COMPLETE && bot->CanRewardQuest(quest, 0, false))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (uint8 idx = 0; idx < menu.GetMenuItemCount(); idx++)
|
||||||
|
{
|
||||||
|
const QuestMenuItem &item = menu.GetItem(idx);
|
||||||
|
const Quest* quest = sObjectMgr->GetQuestTemplate(item.QuestId);
|
||||||
|
if (!quest)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const QuestStatus &status = bot->GetQuestStatus(item.QuestId);
|
||||||
|
if (status == QUEST_STATUS_NONE && bot->CanTakeQuest(quest, false) &&
|
||||||
|
bot->CanAddQuest(quest, false) && IsQuestWorthDoing(quest) && IsQuestCapableDoing(quest))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<float> GenerateRandomWeights(int n) {
|
||||||
|
std::vector<float> weights(n);
|
||||||
|
float sum = 0.0;
|
||||||
|
|
||||||
|
for (int i = 0; i < n; ++i) {
|
||||||
|
weights[i] = rand_norm();
|
||||||
|
sum += weights[i];
|
||||||
|
}
|
||||||
|
for (int i = 0; i < n; ++i) {
|
||||||
|
weights[i] /= sum;
|
||||||
|
}
|
||||||
|
return weights;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NewRpgBaseAction::GetQuestPOIPosAndObjectiveIdx(uint32 questId, std::vector<POIInfo> &poiInfo, bool toComplete)
|
||||||
|
{
|
||||||
|
Quest const* quest = sObjectMgr->GetQuestTemplate(questId);
|
||||||
|
if (!quest)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const QuestPOIVector* poiVector = sObjectMgr->GetQuestPOIVector(questId);
|
||||||
|
if (!poiVector)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QuestStatusData& q_status = bot->getQuestStatusMap().at(questId);
|
||||||
|
|
||||||
|
if (toComplete && q_status.Status == QUEST_STATUS_COMPLETE)
|
||||||
|
{
|
||||||
|
for (const QuestPOI &qPoi : *poiVector)
|
||||||
|
{
|
||||||
|
if (qPoi.MapId != bot->GetMapId())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// not the poi pos to reward quest
|
||||||
|
if (qPoi.ObjectiveIndex != -1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (qPoi.points.size() == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
float dx = 0, dy = 0;
|
||||||
|
std::vector<float> weights = GenerateRandomWeights(qPoi.points.size());
|
||||||
|
for (size_t i = 0; i < qPoi.points.size(); i++)
|
||||||
|
{
|
||||||
|
const QuestPOIPoint &point = qPoi.points[i];
|
||||||
|
dx += point.x * weights[i];
|
||||||
|
dy += point.y * weights[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bot->GetDistance2d(dx, dy) >= 1500.0f)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
float dz = std::max(bot->GetMap()->GetHeight(dx, dy, MAX_HEIGHT), bot->GetMap()->GetWaterLevel(dx, dy));
|
||||||
|
|
||||||
|
if (dz == INVALID_HEIGHT || dz == VMAP_INVALID_HEIGHT_VALUE)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (bot->GetZoneId() != bot->GetMap()->GetZoneId(bot->GetPhaseMask(), dx, dy, dz))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
poiInfo.push_back({{dx, dy}, qPoi.ObjectiveIndex});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (poiInfo.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (q_status.Status != QUEST_STATUS_INCOMPLETE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Get incomplete quest objective index
|
||||||
|
std::vector<int32> incompleteObjectiveIdx;
|
||||||
|
for (int i = 0; i < QUEST_OBJECTIVES_COUNT; i++)
|
||||||
|
{
|
||||||
|
int32 npcOrGo = quest->RequiredNpcOrGo[i];
|
||||||
|
if (!npcOrGo)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (q_status.CreatureOrGOCount[i] < quest->RequiredNpcOrGoCount[i])
|
||||||
|
incompleteObjectiveIdx.push_back(i);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; i++)
|
||||||
|
{
|
||||||
|
uint32 itemId = quest->RequiredItemId[i];
|
||||||
|
if (!itemId)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (q_status.ItemCount[i] < quest->RequiredItemCount[i])
|
||||||
|
incompleteObjectiveIdx.push_back(QUEST_OBJECTIVES_COUNT + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get POIs to go
|
||||||
|
for (const QuestPOI &qPoi : *poiVector)
|
||||||
|
{
|
||||||
|
if (qPoi.MapId != bot->GetMapId())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
bool inComplete = false;
|
||||||
|
for (uint32 objective : incompleteObjectiveIdx)
|
||||||
|
{
|
||||||
|
if (qPoi.ObjectiveIndex == objective)
|
||||||
|
{
|
||||||
|
inComplete = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!inComplete)
|
||||||
|
continue;
|
||||||
|
if (qPoi.points.size() == 0)
|
||||||
|
continue;
|
||||||
|
float dx = 0, dy = 0;
|
||||||
|
std::vector<float> weights = GenerateRandomWeights(qPoi.points.size());
|
||||||
|
for (size_t i = 0; i < qPoi.points.size(); i++)
|
||||||
|
{
|
||||||
|
const QuestPOIPoint &point = qPoi.points[i];
|
||||||
|
dx += point.x * weights[i];
|
||||||
|
dy += point.y * weights[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bot->GetDistance2d(dx, dy) >= 1500.0f)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
float dz = std::max(bot->GetMap()->GetHeight(dx, dy, MAX_HEIGHT), bot->GetMap()->GetWaterLevel(dx, dy));
|
||||||
|
|
||||||
|
if (dz == INVALID_HEIGHT || dz == VMAP_INVALID_HEIGHT_VALUE)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (bot->GetZoneId() != bot->GetMap()->GetZoneId(bot->GetPhaseMask(), dx, dy, dz))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
poiInfo.push_back({{dx, dy}, qPoi.ObjectiveIndex});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (poiInfo.size() == 0) {
|
||||||
|
// LOG_DEBUG("playerbots", "[New rpg] {}: No available poi can be found for quest {}", bot->GetName(), questId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
WorldPosition NewRpgBaseAction::SelectRandomGrindPos(Player* bot)
|
||||||
|
{
|
||||||
|
const std::vector<WorldLocation>& locs = sRandomPlayerbotMgr->locsPerLevelCache[bot->GetLevel()];
|
||||||
|
float hiRange = 500.0f;
|
||||||
|
float loRange = 2500.0f;
|
||||||
|
if (bot->GetLevel() < 5)
|
||||||
|
{
|
||||||
|
hiRange /= 10;
|
||||||
|
loRange /= 10;
|
||||||
|
}
|
||||||
|
std::vector<WorldLocation> lo_prepared_locs, hi_prepared_locs;
|
||||||
|
for (auto& loc : locs)
|
||||||
|
{
|
||||||
|
if (bot->GetMapId() != loc.GetMapId())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (bot->GetExactDist(loc) > 2500.0f)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (bot->GetMap()->GetZoneId(bot->GetPhaseMask(), loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ()) !=
|
||||||
|
bot->GetZoneId())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (bot->GetExactDist(loc) < 500.0f)
|
||||||
|
{
|
||||||
|
hi_prepared_locs.push_back(loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bot->GetExactDist(loc) < 2500.0f)
|
||||||
|
{
|
||||||
|
lo_prepared_locs.push_back(loc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WorldPosition dest{};
|
||||||
|
if (urand(1, 100) <= 50 && !hi_prepared_locs.empty())
|
||||||
|
{
|
||||||
|
uint32 idx = urand(0, hi_prepared_locs.size() - 1);
|
||||||
|
dest = hi_prepared_locs[idx];
|
||||||
|
}
|
||||||
|
else if (!lo_prepared_locs.empty())
|
||||||
|
{
|
||||||
|
uint32 idx = urand(0, lo_prepared_locs.size() - 1);
|
||||||
|
dest = lo_prepared_locs[idx];
|
||||||
|
}
|
||||||
|
LOG_DEBUG("playerbots", "[New Rpg] Bot {} select random grind pos Map:{} X:{} Y:{} Z:{} ({}+{} available in {})",
|
||||||
|
bot->GetName(), dest.GetMapId(), dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ(),
|
||||||
|
hi_prepared_locs.size(), lo_prepared_locs.size() - hi_prepared_locs.size(), locs.size());
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
WorldPosition NewRpgBaseAction::SelectRandomInnKeeperPos(Player* bot)
|
||||||
|
{
|
||||||
|
const std::vector<WorldLocation>& locs = IsAlliance(bot->getRace())
|
||||||
|
? sRandomPlayerbotMgr->allianceStarterPerLevelCache[bot->GetLevel()]
|
||||||
|
: sRandomPlayerbotMgr->hordeStarterPerLevelCache[bot->GetLevel()];
|
||||||
|
std::vector<WorldLocation> prepared_locs;
|
||||||
|
for (auto& loc : locs)
|
||||||
|
{
|
||||||
|
if (bot->GetMapId() != loc.GetMapId())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
float range = bot->GetLevel() <= 5 ? 500.0f : 2500.0f;
|
||||||
|
if (bot->GetExactDist(loc) > range)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (bot->GetMap()->GetZoneId(bot->GetPhaseMask(), loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ()) !=
|
||||||
|
bot->GetZoneId())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
prepared_locs.push_back(loc);
|
||||||
|
}
|
||||||
|
WorldPosition dest{};
|
||||||
|
if (!prepared_locs.empty())
|
||||||
|
{
|
||||||
|
uint32 idx = urand(0, prepared_locs.size() - 1);
|
||||||
|
dest = prepared_locs[idx];
|
||||||
|
}
|
||||||
|
LOG_DEBUG("playerbots", "[New Rpg] Bot {} select random inn keeper pos Map:{} X:{} Y:{} Z:{} ({} available in {})",
|
||||||
|
bot->GetName(), dest.GetMapId(), dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ(),
|
||||||
|
prepared_locs.size(), locs.size());
|
||||||
|
return dest;
|
||||||
|
}
|
58
modules/mod-playerbots/src/strategy/rpg/NewRpgBaseAction.h
Normal file
58
modules/mod-playerbots/src/strategy/rpg/NewRpgBaseAction.h
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
#ifndef _PLAYERBOT_NEWRPGBASEACTION_H
|
||||||
|
#define _PLAYERBOT_NEWRPGBASEACTION_H
|
||||||
|
|
||||||
|
#include "Duration.h"
|
||||||
|
#include "LastMovementValue.h"
|
||||||
|
#include "MovementActions.h"
|
||||||
|
#include "NewRpgStrategy.h"
|
||||||
|
#include "Object.h"
|
||||||
|
#include "ObjectDefines.h"
|
||||||
|
#include "ObjectGuid.h"
|
||||||
|
#include "QuestDef.h"
|
||||||
|
#include "TravelMgr.h"
|
||||||
|
#include "PlayerbotAI.h"
|
||||||
|
|
||||||
|
struct POIInfo {
|
||||||
|
G3D::Vector2 pos;
|
||||||
|
int32 objectiveIdx;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A base (composition) class for all new rpg actions
|
||||||
|
/// All functions that may be shared by multiple actions should be declared here
|
||||||
|
/// And we should make all actions composable instead of inheritable
|
||||||
|
class NewRpgBaseAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NewRpgBaseAction(PlayerbotAI* botAI, std::string name) : MovementAction(botAI, name) {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// MOVEMENT RELATED
|
||||||
|
bool MoveFarTo(WorldPosition dest);
|
||||||
|
bool MoveWorldObjectTo(ObjectGuid guid, float distance = INTERACTION_DISTANCE);
|
||||||
|
bool MoveRandomNear(float moveStep = 50.0f, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL);
|
||||||
|
bool ForceToWait(uint32 duration, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL);
|
||||||
|
|
||||||
|
// QUEST RELATED
|
||||||
|
bool SearchQuestGiverAndAcceptOrReward();
|
||||||
|
ObjectGuid ChooseNpcOrGameObjectToInteract(bool questgiverOnly = false, float distanceLimit = 0.0f);
|
||||||
|
bool HasQuestToAcceptOrReward(WorldObject* object);
|
||||||
|
bool InteractWithNpcOrGameObjectForQuest(ObjectGuid guid);
|
||||||
|
bool AcceptQuest(Quest const* quest, ObjectGuid guid);
|
||||||
|
bool TurnInQuest(Quest const* quest, ObjectGuid guid);
|
||||||
|
uint32 BestReward(Quest const* quest);
|
||||||
|
bool IsQuestWorthDoing(Quest const* quest);
|
||||||
|
bool IsQuestCapableDoing(Quest const* quest);
|
||||||
|
bool OrganizeQuestLog();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool GetQuestPOIPosAndObjectiveIdx(uint32 questId, std::vector<POIInfo> &poiInfo, bool toComplete = false);
|
||||||
|
static WorldPosition SelectRandomGrindPos(Player* bot);
|
||||||
|
static WorldPosition SelectRandomInnKeeperPos(Player* bot);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// WorldPosition dest;
|
||||||
|
const float pathFinderDis = 70.0f; // path finder
|
||||||
|
const uint32 stuckTime = 5 * 60 * 1000;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
119
modules/mod-playerbots/src/strategy/rpg/NewRpgInfo.cpp
Normal file
119
modules/mod-playerbots/src/strategy/rpg/NewRpgInfo.cpp
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
#include "NewRpgInfo.h"
|
||||||
|
#include "Timer.h"
|
||||||
|
|
||||||
|
void NewRpgInfo::ChangeToGoGrind(WorldPosition pos)
|
||||||
|
{
|
||||||
|
Reset();
|
||||||
|
status = RPG_GO_GRIND;
|
||||||
|
go_grind = GoGrind();
|
||||||
|
go_grind.pos = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NewRpgInfo::ChangeToGoInnkeeper(WorldPosition pos)
|
||||||
|
{
|
||||||
|
Reset();
|
||||||
|
status = RPG_GO_INNKEEPER;
|
||||||
|
go_innkeeper = GoInnkeeper();
|
||||||
|
go_innkeeper.pos = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NewRpgInfo::ChangeToNearNpc()
|
||||||
|
{
|
||||||
|
Reset();
|
||||||
|
status = RPG_NEAR_NPC;
|
||||||
|
near_npc = NearNpc();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NewRpgInfo::ChangeToNearRandom()
|
||||||
|
{
|
||||||
|
Reset();
|
||||||
|
status = RPG_NEAR_RANDOM;
|
||||||
|
near_random = NearRandom();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NewRpgInfo::ChangeToDoQuest(uint32 questId, const Quest* quest)
|
||||||
|
{
|
||||||
|
Reset();
|
||||||
|
status = RPG_DO_QUEST;
|
||||||
|
do_quest = DoQuest();
|
||||||
|
do_quest.questId = questId;
|
||||||
|
do_quest.quest = quest;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NewRpgInfo::ChangeToRest()
|
||||||
|
{
|
||||||
|
Reset();
|
||||||
|
status = RPG_REST;
|
||||||
|
rest = Rest();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NewRpgInfo::ChangeToIdle()
|
||||||
|
{
|
||||||
|
Reset();
|
||||||
|
status = RPG_IDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NewRpgInfo::CanChangeTo(NewRpgStatus status)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NewRpgInfo::Reset()
|
||||||
|
{
|
||||||
|
*this = NewRpgInfo();
|
||||||
|
startT = getMSTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NewRpgInfo::SetMoveFarTo(WorldPosition pos)
|
||||||
|
{
|
||||||
|
nearestMoveFarDis = FLT_MAX;
|
||||||
|
stuckTs = 0;
|
||||||
|
stuckAttempts = 0;
|
||||||
|
moveFarPos = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string NewRpgInfo::ToString()
|
||||||
|
{
|
||||||
|
std::stringstream out;
|
||||||
|
out << "Status: ";
|
||||||
|
switch (status)
|
||||||
|
{
|
||||||
|
case RPG_GO_GRIND:
|
||||||
|
out << "GO_GRIND";
|
||||||
|
out << "\nGrindPos: " << go_grind.pos.GetMapId() << " " << go_grind.pos.GetPositionX() << " " << go_grind.pos.GetPositionY() << " " << go_grind.pos.GetPositionZ();
|
||||||
|
out << "\nlastGoGrind: " << startT;
|
||||||
|
break;
|
||||||
|
case RPG_GO_INNKEEPER:
|
||||||
|
out << "GO_INNKEEPER";
|
||||||
|
out << "\nInnKeeperPos: " << go_innkeeper.pos.GetMapId() << " " << go_innkeeper.pos.GetPositionX() << " " << go_innkeeper.pos.GetPositionY() << " " << go_innkeeper.pos.GetPositionZ();
|
||||||
|
out << "\nlastGoInnKeeper: " << startT;
|
||||||
|
break;
|
||||||
|
case RPG_NEAR_NPC:
|
||||||
|
out << "NEAR_NPC";
|
||||||
|
out << "\nnpcOrGoEntry: " << near_npc.npcOrGo.GetCounter();
|
||||||
|
out << "\nlastNearNpc: " << startT;
|
||||||
|
out << "\nlastReachNpcOrGo: " << near_npc.lastReach;
|
||||||
|
break;
|
||||||
|
case RPG_NEAR_RANDOM:
|
||||||
|
out << "NEAR_RANDOM";
|
||||||
|
out << "\nlastNearRandom: " << startT;
|
||||||
|
break;
|
||||||
|
case RPG_IDLE:
|
||||||
|
out << "IDLE";
|
||||||
|
break;
|
||||||
|
case RPG_REST:
|
||||||
|
out << "REST";
|
||||||
|
out << "\nlastRest: " << startT;
|
||||||
|
break;
|
||||||
|
case RPG_DO_QUEST:
|
||||||
|
out << "DO_QUEST";
|
||||||
|
out << "\nquestId: " << do_quest.questId;
|
||||||
|
out << "\nobjectiveIdx: " << do_quest.objectiveIdx;
|
||||||
|
out << "\npoiPos: " << do_quest.pos.GetMapId() << " " << do_quest.pos.GetPositionX() << " " << do_quest.pos.GetPositionY() << " " << do_quest.pos.GetPositionZ();
|
||||||
|
out << "\nlastReachPOI: " << do_quest.lastReachPOI;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
out << "UNKNOWN";
|
||||||
|
}
|
||||||
|
return out.str();
|
||||||
|
}
|
136
modules/mod-playerbots/src/strategy/rpg/NewRpgInfo.h
Normal file
136
modules/mod-playerbots/src/strategy/rpg/NewRpgInfo.h
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
#ifndef _PLAYERBOT_NEWRPGINFO_H
|
||||||
|
#define _PLAYERBOT_NEWRPGINFO_H
|
||||||
|
|
||||||
|
#include "Define.h"
|
||||||
|
#include "ObjectGuid.h"
|
||||||
|
#include "ObjectMgr.h"
|
||||||
|
#include "QuestDef.h"
|
||||||
|
#include "Strategy.h"
|
||||||
|
#include "Timer.h"
|
||||||
|
#include "TravelMgr.h"
|
||||||
|
|
||||||
|
enum NewRpgStatus: int
|
||||||
|
{
|
||||||
|
RPG_STATUS_START = 0,
|
||||||
|
// Going to far away place
|
||||||
|
RPG_GO_GRIND = 0,
|
||||||
|
RPG_GO_INNKEEPER = 1,
|
||||||
|
// Exploring nearby
|
||||||
|
RPG_NEAR_RANDOM = 2,
|
||||||
|
RPG_NEAR_NPC = 3,
|
||||||
|
// Do Quest (based on quest status)
|
||||||
|
RPG_DO_QUEST = 4,
|
||||||
|
// Taking a break
|
||||||
|
RPG_REST = 5,
|
||||||
|
// Initial status
|
||||||
|
RPG_IDLE = 6,
|
||||||
|
RPG_STATUS_END = 7
|
||||||
|
};
|
||||||
|
|
||||||
|
using NewRpgStatusTransitionProb = std::vector<std::vector<int>>;
|
||||||
|
|
||||||
|
struct NewRpgInfo
|
||||||
|
{
|
||||||
|
NewRpgInfo() {}
|
||||||
|
|
||||||
|
// RPG_GO_GRIND
|
||||||
|
struct GoGrind {
|
||||||
|
GoGrind() = default;
|
||||||
|
WorldPosition pos{};
|
||||||
|
};
|
||||||
|
// RPG_GO_INNKEEPER
|
||||||
|
struct GoInnkeeper {
|
||||||
|
GoInnkeeper() = default;
|
||||||
|
WorldPosition pos{};
|
||||||
|
};
|
||||||
|
// RPG_NEAR_NPC
|
||||||
|
struct NearNpc {
|
||||||
|
NearNpc() = default;
|
||||||
|
ObjectGuid npcOrGo{};
|
||||||
|
uint32 lastReach{0};
|
||||||
|
};
|
||||||
|
// RPG_NEAR_RANDOM
|
||||||
|
struct NearRandom {
|
||||||
|
NearRandom() = default;
|
||||||
|
};
|
||||||
|
// NewRpgStatus::QUESTING
|
||||||
|
struct DoQuest {
|
||||||
|
const Quest* quest{nullptr};
|
||||||
|
uint32 questId{0};
|
||||||
|
int32 objectiveIdx{0};
|
||||||
|
WorldPosition pos{};
|
||||||
|
uint32 lastReachPOI{0};
|
||||||
|
};
|
||||||
|
// RPG_REST
|
||||||
|
struct Rest {
|
||||||
|
Rest() = default;
|
||||||
|
};
|
||||||
|
struct Idle {
|
||||||
|
};
|
||||||
|
NewRpgStatus status{RPG_IDLE};
|
||||||
|
|
||||||
|
uint32 startT{0}; // start timestamp of the current status
|
||||||
|
|
||||||
|
// MOVE_FAR
|
||||||
|
float nearestMoveFarDis{FLT_MAX};
|
||||||
|
uint32 stuckTs{0};
|
||||||
|
uint32 stuckAttempts{0};
|
||||||
|
WorldPosition moveFarPos;
|
||||||
|
// END MOVE_FAR
|
||||||
|
|
||||||
|
union {
|
||||||
|
GoGrind go_grind;
|
||||||
|
GoInnkeeper go_innkeeper;
|
||||||
|
NearNpc near_npc;
|
||||||
|
NearRandom near_random;
|
||||||
|
DoQuest do_quest;
|
||||||
|
Rest rest;
|
||||||
|
DoQuest quest;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool HasStatusPersisted(uint32 maxDuration) { return GetMSTimeDiffToNow(startT) > maxDuration; }
|
||||||
|
void ChangeToGoGrind(WorldPosition pos);
|
||||||
|
void ChangeToGoInnkeeper(WorldPosition pos);
|
||||||
|
void ChangeToNearNpc();
|
||||||
|
void ChangeToNearRandom();
|
||||||
|
void ChangeToDoQuest(uint32 questId, const Quest* quest);
|
||||||
|
void ChangeToRest();
|
||||||
|
void ChangeToIdle();
|
||||||
|
bool CanChangeTo(NewRpgStatus status);
|
||||||
|
void Reset();
|
||||||
|
void SetMoveFarTo(WorldPosition pos);
|
||||||
|
std::string ToString();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NewRpgStatistic
|
||||||
|
{
|
||||||
|
uint32 questAccepted{0};
|
||||||
|
uint32 questCompleted{0};
|
||||||
|
uint32 questAbandoned{0};
|
||||||
|
uint32 questRewarded{0};
|
||||||
|
uint32 questDropped{0};
|
||||||
|
NewRpgStatistic operator+(const NewRpgStatistic& other) const
|
||||||
|
{
|
||||||
|
NewRpgStatistic result;
|
||||||
|
result.questAccepted = this->questAccepted + other.questAccepted;
|
||||||
|
result.questCompleted = this->questCompleted + other.questCompleted;
|
||||||
|
result.questAbandoned = this->questAbandoned + other.questAbandoned;
|
||||||
|
result.questRewarded = this->questRewarded + other.questRewarded;
|
||||||
|
result.questDropped = this->questDropped + other.questDropped;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
NewRpgStatistic& operator+=(const NewRpgStatistic& other)
|
||||||
|
{
|
||||||
|
this->questAccepted += other.questAccepted;
|
||||||
|
this->questCompleted += other.questCompleted;
|
||||||
|
this->questAbandoned += other.questAbandoned;
|
||||||
|
this->questRewarded += other.questRewarded;
|
||||||
|
this->questDropped += other.questDropped;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// not sure is it necessary but keep it for now
|
||||||
|
#define RPG_INFO(x, y) botAI->rpgInfo.x.y
|
||||||
|
|
||||||
|
#endif
|
@ -11,22 +11,28 @@ NewRpgStrategy::NewRpgStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
|
|||||||
|
|
||||||
NextAction** NewRpgStrategy::getDefaultActions()
|
NextAction** NewRpgStrategy::getDefaultActions()
|
||||||
{
|
{
|
||||||
return NextAction::array(0, new NextAction("new rpg status update", 5.0f), nullptr);
|
// the releavance should be greater than grind
|
||||||
|
return NextAction::array(0,
|
||||||
|
new NextAction("new rpg status update", 11.0f),
|
||||||
|
nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NewRpgStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void NewRpgStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
{
|
{
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("go grind status", NextAction::array(0, new NextAction("new rpg go grind", 1.0f), nullptr)));
|
new TriggerNode("go grind status", NextAction::array(0, new NextAction("new rpg go grind", 3.0f), nullptr)));
|
||||||
|
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("go innkeeper status", NextAction::array(0, new NextAction("new rpg go innkeeper", 1.0f), nullptr)));
|
new TriggerNode("go innkeeper status", NextAction::array(0, new NextAction("new rpg go innkeeper", 3.0f), nullptr)));
|
||||||
|
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("near random status", NextAction::array(0, new NextAction("new rpg move random", 1.0f), nullptr)));
|
new TriggerNode("near random status", NextAction::array(0, new NextAction("new rpg move random", 3.0f), nullptr)));
|
||||||
|
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("near npc status", NextAction::array(0, new NextAction("new rpg move npc", 1.0f), nullptr)));
|
new TriggerNode("near npc status", NextAction::array(0, new NextAction("new rpg move npc", 3.0f), nullptr)));
|
||||||
|
|
||||||
|
triggers.push_back(
|
||||||
|
new TriggerNode("do quest status", NextAction::array(0, new NextAction("new rpg do quest", 3.0f), nullptr)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void NewRpgStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
void NewRpgStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
||||||
|
@ -6,91 +6,12 @@
|
|||||||
#ifndef _PLAYERBOT_NEWRPGSTRATEGY_H
|
#ifndef _PLAYERBOT_NEWRPGSTRATEGY_H
|
||||||
#define _PLAYERBOT_NEWRPGSTRATEGY_H
|
#define _PLAYERBOT_NEWRPGSTRATEGY_H
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include "Strategy.h"
|
#include "Strategy.h"
|
||||||
#include "TravelMgr.h"
|
#include "TravelMgr.h"
|
||||||
|
#include "NewRpgInfo.h"
|
||||||
|
|
||||||
class PlayerbotAI;
|
class PlayerbotAI;
|
||||||
|
|
||||||
enum class NewRpgStatus
|
|
||||||
{
|
|
||||||
// Going to far away place
|
|
||||||
GO_GRIND,
|
|
||||||
GO_INNKEEPER,
|
|
||||||
// Exploring nearby
|
|
||||||
NEAR_RANDOM,
|
|
||||||
NEAR_NPC,
|
|
||||||
// Taking a break
|
|
||||||
REST,
|
|
||||||
// Initial status
|
|
||||||
IDLE
|
|
||||||
};
|
|
||||||
|
|
||||||
struct NewRpgInfo
|
|
||||||
{
|
|
||||||
NewRpgStatus status{NewRpgStatus::IDLE};
|
|
||||||
// NewRpgStatus::GO_GRIND
|
|
||||||
WorldPosition grindPos{};
|
|
||||||
uint32 lastGoGrind{0};
|
|
||||||
// NewRpgStatus::GO_INNKEEPER
|
|
||||||
WorldPosition innKeeperPos{};
|
|
||||||
uint32 lastGoInnKeeper{0};
|
|
||||||
// NewRpgStatus::NEAR_NPC
|
|
||||||
GuidPosition npcPos{};
|
|
||||||
uint32 lastNearNpc{0};
|
|
||||||
uint32 lastReachNpc{0};
|
|
||||||
// NewRpgStatus::NEAR_RANDOM
|
|
||||||
uint32 lastNearRandom{0};
|
|
||||||
// NewRpgStatus::REST
|
|
||||||
uint32 lastRest{0};
|
|
||||||
// MOVE_FAR
|
|
||||||
float nearestMoveFarDis{FLT_MAX};
|
|
||||||
uint32 stuckTs{0};
|
|
||||||
uint32 stuckAttempts{0};
|
|
||||||
std::string ToString()
|
|
||||||
{
|
|
||||||
std::stringstream out;
|
|
||||||
out << "Status: ";
|
|
||||||
switch (status)
|
|
||||||
{
|
|
||||||
case NewRpgStatus::GO_GRIND:
|
|
||||||
out << "GO_GRIND";
|
|
||||||
out << "\nGrindPos: " << grindPos.GetMapId() << " " << grindPos.GetPositionX() << " " << grindPos.GetPositionY() << " " << grindPos.GetPositionZ();
|
|
||||||
out << "\nlastGoGrind: " << lastGoGrind;
|
|
||||||
break;
|
|
||||||
case NewRpgStatus::GO_INNKEEPER:
|
|
||||||
out << "GO_INNKEEPER";
|
|
||||||
out << "\nInnKeeperPos: " << innKeeperPos.GetMapId() << " " << innKeeperPos.GetPositionX() << " " << innKeeperPos.GetPositionY() << " " << innKeeperPos.GetPositionZ();
|
|
||||||
out << "\nlastGoInnKeeper: " << lastGoInnKeeper;
|
|
||||||
break;
|
|
||||||
case NewRpgStatus::NEAR_NPC:
|
|
||||||
out << "NEAR_NPC";
|
|
||||||
out << "\nNpcPos: " << npcPos.GetMapId() << " " << npcPos.GetPositionX() << " " << npcPos.GetPositionY() << " " << npcPos.GetPositionZ();
|
|
||||||
out << "\nlastNearNpc: " << lastNearNpc;
|
|
||||||
out << "\nlastReachNpc: " << lastReachNpc;
|
|
||||||
break;
|
|
||||||
case NewRpgStatus::NEAR_RANDOM:
|
|
||||||
out << "NEAR_RANDOM";
|
|
||||||
out << "\nlastNearRandom: " << lastNearRandom;
|
|
||||||
break;
|
|
||||||
case NewRpgStatus::IDLE:
|
|
||||||
out << "IDLE";
|
|
||||||
break;
|
|
||||||
case NewRpgStatus::REST:
|
|
||||||
out << "REST";
|
|
||||||
out << "\nlastRest: " << lastRest;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
out << "UNKNOWN";
|
|
||||||
}
|
|
||||||
return out.str();
|
|
||||||
}
|
|
||||||
void Reset()
|
|
||||||
{
|
|
||||||
*this = NewRpgInfo();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class NewRpgStrategy : public Strategy
|
class NewRpgStrategy : public Strategy
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
class NewRpgStatusTrigger : public Trigger
|
class NewRpgStatusTrigger : public Trigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
NewRpgStatusTrigger(PlayerbotAI* botAI, NewRpgStatus status = NewRpgStatus::IDLE)
|
NewRpgStatusTrigger(PlayerbotAI* botAI, NewRpgStatus status = RPG_IDLE)
|
||||||
: Trigger(botAI, "new rpg status"), status(status)
|
: Trigger(botAI, "new rpg status"), status(status)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,8 @@ public:
|
|||||||
ChatTriggerContext()
|
ChatTriggerContext()
|
||||||
{
|
{
|
||||||
creators["open items"] = &ChatTriggerContext::open_items;
|
creators["open items"] = &ChatTriggerContext::open_items;
|
||||||
|
creators["unlock items"] = &ChatTriggerContext::unlock_items;
|
||||||
|
creators["unlock traded item"] = &ChatTriggerContext::unlock_traded_item;
|
||||||
creators["quests"] = &ChatTriggerContext::quests;
|
creators["quests"] = &ChatTriggerContext::quests;
|
||||||
creators["stats"] = &ChatTriggerContext::stats;
|
creators["stats"] = &ChatTriggerContext::stats;
|
||||||
creators["leave"] = &ChatTriggerContext::leave;
|
creators["leave"] = &ChatTriggerContext::leave;
|
||||||
@ -25,6 +27,7 @@ public:
|
|||||||
creators["log"] = &ChatTriggerContext::log;
|
creators["log"] = &ChatTriggerContext::log;
|
||||||
creators["los"] = &ChatTriggerContext::los;
|
creators["los"] = &ChatTriggerContext::los;
|
||||||
creators["rpg status"] = &ChatTriggerContext::rpg_status;
|
creators["rpg status"] = &ChatTriggerContext::rpg_status;
|
||||||
|
creators["rpg do quest"] = &ChatTriggerContext::rpg_do_quest;
|
||||||
creators["aura"] = &ChatTriggerContext::aura;
|
creators["aura"] = &ChatTriggerContext::aura;
|
||||||
creators["drop"] = &ChatTriggerContext::drop;
|
creators["drop"] = &ChatTriggerContext::drop;
|
||||||
creators["share"] = &ChatTriggerContext::share;
|
creators["share"] = &ChatTriggerContext::share;
|
||||||
@ -132,6 +135,8 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
static Trigger* open_items(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "open items"); }
|
static Trigger* open_items(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "open items"); }
|
||||||
|
static Trigger* unlock_items(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "unlock items"); }
|
||||||
|
static Trigger* unlock_traded_item(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "unlock traded item"); }
|
||||||
static Trigger* ra(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "ra"); }
|
static Trigger* ra(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "ra"); }
|
||||||
static Trigger* range(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "range"); }
|
static Trigger* range(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "range"); }
|
||||||
static Trigger* flag(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "flag"); }
|
static Trigger* flag(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "flag"); }
|
||||||
@ -214,6 +219,7 @@ private:
|
|||||||
static Trigger* log(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "log"); }
|
static Trigger* log(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "log"); }
|
||||||
static Trigger* los(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "los"); }
|
static Trigger* los(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "los"); }
|
||||||
static Trigger* rpg_status(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "rpg status"); }
|
static Trigger* rpg_status(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "rpg status"); }
|
||||||
|
static Trigger* rpg_do_quest(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "rpg do quest"); }
|
||||||
static Trigger* aura(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "aura"); }
|
static Trigger* aura(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "aura"); }
|
||||||
static Trigger* loot_all(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "add all loot"); }
|
static Trigger* loot_all(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "add all loot"); }
|
||||||
static Trigger* release(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "release"); }
|
static Trigger* release(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "release"); }
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include "ObjectGuid.h"
|
#include "ObjectGuid.h"
|
||||||
#include "PlayerbotAIConfig.h"
|
#include "PlayerbotAIConfig.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
#include "PositionValue.h"
|
||||||
#include "SharedDefines.h"
|
#include "SharedDefines.h"
|
||||||
#include "TemporarySummon.h"
|
#include "TemporarySummon.h"
|
||||||
#include "ThreatMgr.h"
|
#include "ThreatMgr.h"
|
||||||
@ -507,11 +508,22 @@ bool IsBehindTargetTrigger::IsActive()
|
|||||||
|
|
||||||
bool IsNotBehindTargetTrigger::IsActive()
|
bool IsNotBehindTargetTrigger::IsActive()
|
||||||
{
|
{
|
||||||
|
if (botAI->HasStrategy("stay", botAI->GetState()))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
Unit* target = AI_VALUE(Unit*, "current target");
|
Unit* target = AI_VALUE(Unit*, "current target");
|
||||||
return target && !AI_VALUE2(bool, "behind", "current target");
|
return target && !AI_VALUE2(bool, "behind", "current target");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsNotFacingTargetTrigger::IsActive() { return !AI_VALUE2(bool, "facing", "current target"); }
|
bool IsNotFacingTargetTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (botAI->HasStrategy("stay", botAI->GetState()))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !AI_VALUE2(bool, "facing", "current target");
|
||||||
|
}
|
||||||
|
|
||||||
bool HasCcTargetTrigger::IsActive()
|
bool HasCcTargetTrigger::IsActive()
|
||||||
{
|
{
|
||||||
@ -599,6 +611,18 @@ bool NewPlayerNearbyTrigger::IsActive() { return AI_VALUE(ObjectGuid, "new playe
|
|||||||
|
|
||||||
bool CollisionTrigger::IsActive() { return AI_VALUE2(bool, "collision", "self target"); }
|
bool CollisionTrigger::IsActive() { return AI_VALUE2(bool, "collision", "self target"); }
|
||||||
|
|
||||||
|
bool ReturnToStayPositionTrigger::IsActive()
|
||||||
|
{
|
||||||
|
PositionInfo stayPosition = AI_VALUE(PositionMap&, "position")["stay"];
|
||||||
|
if (stayPosition.isSet())
|
||||||
|
{
|
||||||
|
const float distance = bot->GetDistance(stayPosition.x, stayPosition.y, stayPosition.z);
|
||||||
|
return distance > sPlayerbotAIConfig->followDistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool GiveItemTrigger::IsActive()
|
bool GiveItemTrigger::IsActive()
|
||||||
{
|
{
|
||||||
return AI_VALUE2(Unit*, "party member without item", item) && AI_VALUE2(uint32, "item count", item);
|
return AI_VALUE2(Unit*, "party member without item", item) && AI_VALUE2(uint32, "item count", item);
|
||||||
|
@ -827,6 +827,14 @@ public:
|
|||||||
SitTrigger(PlayerbotAI* botAI) : StayTimeTrigger(botAI, sPlayerbotAIConfig->sitDelay, "sit") {}
|
SitTrigger(PlayerbotAI* botAI) : StayTimeTrigger(botAI, sPlayerbotAIConfig->sitDelay, "sit") {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ReturnToStayPositionTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ReturnToStayPositionTrigger(PlayerbotAI* ai) : Trigger(ai, "return to stay position", 2) {}
|
||||||
|
|
||||||
|
virtual bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
class ReturnTrigger : public StayTimeTrigger
|
class ReturnTrigger : public StayTimeTrigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -12,10 +12,10 @@
|
|||||||
bool LootAvailableTrigger::IsActive()
|
bool LootAvailableTrigger::IsActive()
|
||||||
{
|
{
|
||||||
return AI_VALUE(bool, "has available loot") &&
|
return AI_VALUE(bool, "has available loot") &&
|
||||||
|
// if loot target if empty, always pass distance check
|
||||||
(sServerFacade->IsDistanceLessOrEqualThan(AI_VALUE2(float, "distance", "loot target"),
|
(sServerFacade->IsDistanceLessOrEqualThan(AI_VALUE2(float, "distance", "loot target"),
|
||||||
INTERACTION_DISTANCE - 2.0f) ||
|
INTERACTION_DISTANCE - 2.0f) ||
|
||||||
AI_VALUE(GuidVector, "all targets").empty()) &&
|
AI_VALUE(GuidVector, "all targets").empty());
|
||||||
!AI_VALUE2(bool, "combat", "self target");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FarFromCurrentLootTrigger::IsActive()
|
bool FarFromCurrentLootTrigger::IsActive()
|
||||||
|
@ -30,6 +30,7 @@ public:
|
|||||||
{
|
{
|
||||||
creators["return"] = &TriggerContext::_return;
|
creators["return"] = &TriggerContext::_return;
|
||||||
creators["sit"] = &TriggerContext::sit;
|
creators["sit"] = &TriggerContext::sit;
|
||||||
|
creators["return to stay position"] = &TriggerContext::return_to_stay_position;
|
||||||
creators["collision"] = &TriggerContext::collision;
|
creators["collision"] = &TriggerContext::collision;
|
||||||
|
|
||||||
creators["timer"] = &TriggerContext::Timer;
|
creators["timer"] = &TriggerContext::Timer;
|
||||||
@ -220,6 +221,7 @@ public:
|
|||||||
creators["go innkeeper status"] = &TriggerContext::go_innkeeper_status;
|
creators["go innkeeper status"] = &TriggerContext::go_innkeeper_status;
|
||||||
creators["near random status"] = &TriggerContext::near_random_status;
|
creators["near random status"] = &TriggerContext::near_random_status;
|
||||||
creators["near npc status"] = &TriggerContext::near_npc_status;
|
creators["near npc status"] = &TriggerContext::near_npc_status;
|
||||||
|
creators["do quest status"] = &TriggerContext::do_quest_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -227,6 +229,7 @@ private:
|
|||||||
static Trigger* give_water(PlayerbotAI* botAI) { return new GiveWaterTrigger(botAI); }
|
static Trigger* give_water(PlayerbotAI* botAI) { return new GiveWaterTrigger(botAI); }
|
||||||
static Trigger* no_rti(PlayerbotAI* botAI) { return new NoRtiTrigger(botAI); }
|
static Trigger* no_rti(PlayerbotAI* botAI) { return new NoRtiTrigger(botAI); }
|
||||||
static Trigger* _return(PlayerbotAI* botAI) { return new ReturnTrigger(botAI); }
|
static Trigger* _return(PlayerbotAI* botAI) { return new ReturnTrigger(botAI); }
|
||||||
|
static Trigger* return_to_stay_position(PlayerbotAI* ai) { return new ReturnToStayPositionTrigger(ai); }
|
||||||
static Trigger* sit(PlayerbotAI* botAI) { return new SitTrigger(botAI); }
|
static Trigger* sit(PlayerbotAI* botAI) { return new SitTrigger(botAI); }
|
||||||
static Trigger* far_from_rpg_target(PlayerbotAI* botAI) { return new FarFromRpgTargetTrigger(botAI); }
|
static Trigger* far_from_rpg_target(PlayerbotAI* botAI) { return new FarFromRpgTargetTrigger(botAI); }
|
||||||
static Trigger* near_rpg_target(PlayerbotAI* botAI) { return new NearRpgTargetTrigger(botAI); }
|
static Trigger* near_rpg_target(PlayerbotAI* botAI) { return new NearRpgTargetTrigger(botAI); }
|
||||||
@ -410,10 +413,11 @@ private:
|
|||||||
static Trigger* rpg_craft(PlayerbotAI* botAI) { return new RpgCraftTrigger(botAI); }
|
static Trigger* rpg_craft(PlayerbotAI* botAI) { return new RpgCraftTrigger(botAI); }
|
||||||
static Trigger* rpg_trade_useful(PlayerbotAI* botAI) { return new RpgTradeUsefulTrigger(botAI); }
|
static Trigger* rpg_trade_useful(PlayerbotAI* botAI) { return new RpgTradeUsefulTrigger(botAI); }
|
||||||
static Trigger* rpg_duel(PlayerbotAI* botAI) { return new RpgDuelTrigger(botAI); }
|
static Trigger* rpg_duel(PlayerbotAI* botAI) { return new RpgDuelTrigger(botAI); }
|
||||||
static Trigger* go_grind_status(PlayerbotAI* botAI) { return new NewRpgStatusTrigger(botAI, NewRpgStatus::GO_GRIND); }
|
static Trigger* go_grind_status(PlayerbotAI* botAI) { return new NewRpgStatusTrigger(botAI, RPG_GO_GRIND); }
|
||||||
static Trigger* go_innkeeper_status(PlayerbotAI* botAI) { return new NewRpgStatusTrigger(botAI, NewRpgStatus::GO_INNKEEPER); }
|
static Trigger* go_innkeeper_status(PlayerbotAI* botAI) { return new NewRpgStatusTrigger(botAI, RPG_GO_INNKEEPER); }
|
||||||
static Trigger* near_random_status(PlayerbotAI* botAI) { return new NewRpgStatusTrigger(botAI, NewRpgStatus::NEAR_RANDOM); }
|
static Trigger* near_random_status(PlayerbotAI* botAI) { return new NewRpgStatusTrigger(botAI, RPG_NEAR_RANDOM); }
|
||||||
static Trigger* near_npc_status(PlayerbotAI* botAI) { return new NewRpgStatusTrigger(botAI, NewRpgStatus::NEAR_NPC); }
|
static Trigger* near_npc_status(PlayerbotAI* botAI) { return new NewRpgStatusTrigger(botAI, RPG_NEAR_NPC); }
|
||||||
|
static Trigger* do_quest_status(PlayerbotAI* botAI) { return new NewRpgStatusTrigger(botAI, RPG_DO_QUEST); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -29,6 +29,7 @@ public:
|
|||||||
creators["check mount state"] = &WorldPacketTriggerContext::check_mount_state;
|
creators["check mount state"] = &WorldPacketTriggerContext::check_mount_state;
|
||||||
creators["activate taxi"] = &WorldPacketTriggerContext::taxi;
|
creators["activate taxi"] = &WorldPacketTriggerContext::taxi;
|
||||||
creators["trade status"] = &WorldPacketTriggerContext::trade_status;
|
creators["trade status"] = &WorldPacketTriggerContext::trade_status;
|
||||||
|
creators["trade status extended"] = &WorldPacketTriggerContext::trade_status_extended;
|
||||||
creators["loot response"] = &WorldPacketTriggerContext::loot_response;
|
creators["loot response"] = &WorldPacketTriggerContext::loot_response;
|
||||||
creators["out of react range"] = &WorldPacketTriggerContext::out_of_react_range;
|
creators["out of react range"] = &WorldPacketTriggerContext::out_of_react_range;
|
||||||
|
|
||||||
@ -108,6 +109,7 @@ private:
|
|||||||
static Trigger* out_of_react_range(PlayerbotAI* botAI) { return new OutOfReactRangeTrigger(botAI); }
|
static Trigger* out_of_react_range(PlayerbotAI* botAI) { return new OutOfReactRangeTrigger(botAI); }
|
||||||
static Trigger* loot_response(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "loot response"); }
|
static Trigger* loot_response(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "loot response"); }
|
||||||
static Trigger* trade_status(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "trade status"); }
|
static Trigger* trade_status(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "trade status"); }
|
||||||
|
static Trigger* trade_status_extended(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "trade status extended"); }
|
||||||
static Trigger* cannot_equip(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "cannot equip"); }
|
static Trigger* cannot_equip(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "cannot equip"); }
|
||||||
static Trigger* check_mount_state(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "check mount state"); }
|
static Trigger* check_mount_state(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "check mount state"); }
|
||||||
static Trigger* area_trigger(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "area trigger"); }
|
static Trigger* area_trigger(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "area trigger"); }
|
||||||
|
@ -26,5 +26,5 @@ bool CanLootValue::Calculate()
|
|||||||
{
|
{
|
||||||
LootObject loot = AI_VALUE(LootObject, "loot target");
|
LootObject loot = AI_VALUE(LootObject, "loot target");
|
||||||
return !loot.IsEmpty() && loot.GetWorldObject(bot) && loot.IsLootPossible(bot) &&
|
return !loot.IsEmpty() && loot.GetWorldObject(bot) && loot.IsLootPossible(bot) &&
|
||||||
sServerFacade->IsDistanceLessOrEqualThan(AI_VALUE2(float, "distance", "loot target"), INTERACTION_DISTANCE);
|
sServerFacade->IsDistanceLessOrEqualThan(AI_VALUE2(float, "distance", "loot target"), INTERACTION_DISTANCE - 2);
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include "GrindTargetValue.h"
|
#include "GrindTargetValue.h"
|
||||||
|
|
||||||
|
#include "NewRpgInfo.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "ReputationMgr.h"
|
#include "ReputationMgr.h"
|
||||||
#include "SharedDefines.h"
|
#include "SharedDefines.h"
|
||||||
@ -52,7 +53,7 @@ Unit* GrindTargetValue::FindTargetForGrinding(uint32 assistCount)
|
|||||||
|
|
||||||
float distance = 0;
|
float distance = 0;
|
||||||
Unit* result = nullptr;
|
Unit* result = nullptr;
|
||||||
// std::unordered_map<uint32, bool> needForQuestMap;
|
std::unordered_map<uint32, bool> needForQuestMap;
|
||||||
|
|
||||||
for (ObjectGuid const guid : targets)
|
for (ObjectGuid const guid : targets)
|
||||||
{
|
{
|
||||||
@ -99,19 +100,6 @@ Unit* GrindTargetValue::FindTargetForGrinding(uint32 assistCount)
|
|||||||
if (!bot->InBattleground() && (int)unit->GetLevel() - (int)bot->GetLevel() > 4 && !unit->GetGUID().IsPlayer())
|
if (!bot->InBattleground() && (int)unit->GetLevel() - (int)bot->GetLevel() > 4 && !unit->GetGUID().IsPlayer())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// if (needForQuestMap.find(unit->GetEntry()) == needForQuestMap.end())
|
|
||||||
// needForQuestMap[unit->GetEntry()] = needForQuest(unit);
|
|
||||||
|
|
||||||
// if (!needForQuestMap[unit->GetEntry()])
|
|
||||||
// {
|
|
||||||
// Creature* creature = dynamic_cast<Creature*>(unit);
|
|
||||||
// if ((urand(0, 100) < 60 || (context->GetValue<TravelTarget*>("travel target")->Get()->isWorking() &&
|
|
||||||
// context->GetValue<TravelTarget*>("travel target")->Get()->getDestination()->getName() != "GrindTravelDestination")))
|
|
||||||
// {
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (Creature* creature = unit->ToCreature())
|
if (Creature* creature = unit->ToCreature())
|
||||||
if (CreatureTemplate const* CreatureTemplate = creature->GetCreatureTemplate())
|
if (CreatureTemplate const* CreatureTemplate = creature->GetCreatureTemplate())
|
||||||
if (CreatureTemplate->rank > CREATURE_ELITE_NORMAL && !AI_VALUE(bool, "can fight elite"))
|
if (CreatureTemplate->rank > CREATURE_ELITE_NORMAL && !AI_VALUE(bool, "can fight elite"))
|
||||||
@ -122,6 +110,26 @@ Unit* GrindTargetValue::FindTargetForGrinding(uint32 assistCount)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool inactiveGrindStatus = botAI->rpgInfo.status == RPG_GO_GRIND ||
|
||||||
|
botAI->rpgInfo.status == RPG_NEAR_NPC ||
|
||||||
|
botAI->rpgInfo.status == RPG_REST ||
|
||||||
|
botAI->rpgInfo.status == RPG_GO_INNKEEPER ||
|
||||||
|
botAI->rpgInfo.status == RPG_DO_QUEST;
|
||||||
|
|
||||||
|
bool notHostile = !bot->IsHostileTo(unit); /*|| (unit->ToCreature() && unit->ToCreature()->IsCivilian());*/
|
||||||
|
float aggroRange = 30.0f;
|
||||||
|
if (unit->ToCreature())
|
||||||
|
aggroRange = std::min(30.0f, unit->ToCreature()->GetAggroRange(bot) + 10.0f);
|
||||||
|
bool outOfAggro = unit->ToCreature() && bot->GetDistance(unit) > aggroRange;
|
||||||
|
if (inactiveGrindStatus && (outOfAggro || notHostile))
|
||||||
|
{
|
||||||
|
if (needForQuestMap.find(unit->GetEntry()) == needForQuestMap.end())
|
||||||
|
needForQuestMap[unit->GetEntry()] = needForQuest(unit);
|
||||||
|
|
||||||
|
if (!needForQuestMap[unit->GetEntry()])
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (group)
|
if (group)
|
||||||
{
|
{
|
||||||
Group::MemberSlotList const& groupSlot = group->GetMemberSlots();
|
Group::MemberSlotList const& groupSlot = group->GetMemberSlots();
|
||||||
@ -155,8 +163,6 @@ Unit* GrindTargetValue::FindTargetForGrinding(uint32 assistCount)
|
|||||||
|
|
||||||
bool GrindTargetValue::needForQuest(Unit* target)
|
bool GrindTargetValue::needForQuest(Unit* target)
|
||||||
{
|
{
|
||||||
bool justCheck = (bot->GetGUID() == target->GetGUID());
|
|
||||||
|
|
||||||
QuestStatusMap& questMap = bot->getQuestStatusMap();
|
QuestStatusMap& questMap = bot->getQuestStatusMap();
|
||||||
for (auto& quest : questMap)
|
for (auto& quest : questMap)
|
||||||
{
|
{
|
||||||
@ -170,16 +176,9 @@ bool GrindTargetValue::needForQuest(Unit* target)
|
|||||||
|
|
||||||
QuestStatus status = bot->GetQuestStatus(questId);
|
QuestStatus status = bot->GetQuestStatus(questId);
|
||||||
|
|
||||||
if ((status == QUEST_STATUS_COMPLETE && !bot->GetQuestRewardStatus(questId)))
|
if (status == QUEST_STATUS_INCOMPLETE)
|
||||||
{
|
{
|
||||||
if (!justCheck && !target->hasInvolvedQuest(questId))
|
const QuestStatusData* questStatus = &bot->getQuestStatusMap()[questId];
|
||||||
continue;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (status == QUEST_STATUS_INCOMPLETE)
|
|
||||||
{
|
|
||||||
QuestStatusData* questStatus = sTravelMgr->getQuestStatus(bot, questId);
|
|
||||||
|
|
||||||
if (questTemplate->GetQuestLevel() > bot->GetLevel() + 5)
|
if (questTemplate->GetQuestLevel() > bot->GetLevel() + 5)
|
||||||
continue;
|
continue;
|
||||||
@ -193,32 +192,17 @@ bool GrindTargetValue::needForQuest(Unit* target)
|
|||||||
int required = questTemplate->RequiredNpcOrGoCount[j];
|
int required = questTemplate->RequiredNpcOrGoCount[j];
|
||||||
int available = questStatus->CreatureOrGOCount[j];
|
int available = questStatus->CreatureOrGOCount[j];
|
||||||
|
|
||||||
if (required && available < required && (target->GetEntry() == entry || justCheck))
|
if (required && available < required && target->GetEntry() == entry)
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (justCheck)
|
|
||||||
{
|
|
||||||
int32 itemId = questTemplate->RequiredItemId[j];
|
|
||||||
|
|
||||||
if (itemId && itemId > 0)
|
|
||||||
{
|
|
||||||
uint32 required = questTemplate->RequiredItemCount[j];
|
|
||||||
uint32 available = questStatus->ItemCount[j];
|
|
||||||
|
|
||||||
if (required && available < required)
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!justCheck)
|
|
||||||
{
|
|
||||||
if (CreatureTemplate const* data = sObjectMgr->GetCreatureTemplate(target->GetEntry()))
|
if (CreatureTemplate const* data = sObjectMgr->GetCreatureTemplate(target->GetEntry()))
|
||||||
{
|
{
|
||||||
if (uint32 lootId = data->lootid)
|
if (uint32 lootId = data->lootid)
|
||||||
{
|
{
|
||||||
if (LootTemplates_Creature.HaveQuestLootForPlayer(lootId, bot))
|
if (LootTemplates_Creature.HaveQuestLootForPlayer(lootId, bot))
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,6 +71,9 @@ Item* ItemForSpellValue::Calculate()
|
|||||||
if (!strcmpi(spellInfo->SpellName[0], "disenchant"))
|
if (!strcmpi(spellInfo->SpellName[0], "disenchant"))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
if (!strcmpi(spellInfo->SpellName[0], "pick lock"))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; slot++)
|
for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; slot++)
|
||||||
{
|
{
|
||||||
itemForSpell = GetItemFitsToSpellRequirements(slot, spellInfo);
|
itemForSpell = GetItemFitsToSpellRequirements(slot, spellInfo);
|
||||||
|
@ -17,6 +17,7 @@ class Unit;
|
|||||||
enum class MovementPriority
|
enum class MovementPriority
|
||||||
{
|
{
|
||||||
MOVEMENT_IDLE,
|
MOVEMENT_IDLE,
|
||||||
|
MOVEMENT_WANDER,
|
||||||
MOVEMENT_NORMAL,
|
MOVEMENT_NORMAL,
|
||||||
MOVEMENT_COMBAT,
|
MOVEMENT_COMBAT,
|
||||||
MOVEMENT_FORCED
|
MOVEMENT_FORCED
|
||||||
|
@ -12,24 +12,6 @@
|
|||||||
#include "SharedDefines.h"
|
#include "SharedDefines.h"
|
||||||
#include "SpellMgr.h"
|
#include "SpellMgr.h"
|
||||||
|
|
||||||
class AnyGameObjectInObjectRangeCheck
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
AnyGameObjectInObjectRangeCheck(WorldObject const* obj, float range) : i_obj(obj), i_range(range) {}
|
|
||||||
WorldObject const& GetFocusObject() const { return *i_obj; }
|
|
||||||
bool operator()(GameObject* u)
|
|
||||||
{
|
|
||||||
if (u && i_obj->IsWithinDistInMap(u, i_range) && u->isSpawned() && u->GetGOInfo())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
WorldObject const* i_obj;
|
|
||||||
float i_range;
|
|
||||||
};
|
|
||||||
|
|
||||||
GuidVector NearestGameObjects::Calculate()
|
GuidVector NearestGameObjects::Calculate()
|
||||||
{
|
{
|
||||||
std::list<GameObject*> targets;
|
std::list<GameObject*> targets;
|
||||||
|
@ -8,15 +8,34 @@
|
|||||||
|
|
||||||
#include "PlayerbotAIConfig.h"
|
#include "PlayerbotAIConfig.h"
|
||||||
#include "Value.h"
|
#include "Value.h"
|
||||||
|
#include "GameObject.h"
|
||||||
|
|
||||||
class PlayerbotAI;
|
class PlayerbotAI;
|
||||||
|
|
||||||
|
class AnyGameObjectInObjectRangeCheck
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AnyGameObjectInObjectRangeCheck(WorldObject const* obj, float range) : i_obj(obj), i_range(range) {}
|
||||||
|
WorldObject const& GetFocusObject() const { return *i_obj; }
|
||||||
|
bool operator()(GameObject* u)
|
||||||
|
{
|
||||||
|
if (u && i_obj->IsWithinDistInMap(u, i_range) && u->isSpawned() && u->GetGOInfo())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
WorldObject const* i_obj;
|
||||||
|
float i_range;
|
||||||
|
};
|
||||||
|
|
||||||
class NearestGameObjects : public ObjectGuidListCalculatedValue
|
class NearestGameObjects : public ObjectGuidListCalculatedValue
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
NearestGameObjects(PlayerbotAI* botAI, float range = sPlayerbotAIConfig->sightDistance, bool ignoreLos = false,
|
NearestGameObjects(PlayerbotAI* botAI, float range = sPlayerbotAIConfig->sightDistance, bool ignoreLos = false,
|
||||||
std::string const name = "nearest game objects")
|
std::string const name = "nearest game objects")
|
||||||
: ObjectGuidListCalculatedValue(botAI, name, 2 * 1000), range(range), ignoreLos(ignoreLos)
|
: ObjectGuidListCalculatedValue(botAI, name, 1 * 1000), range(range), ignoreLos(ignoreLos)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,3 +63,25 @@ bool PositionValue::Load(std::string const text)
|
|||||||
}
|
}
|
||||||
|
|
||||||
WorldPosition CurrentPositionValue::Calculate() { return WorldPosition(bot); }
|
WorldPosition CurrentPositionValue::Calculate() { return WorldPosition(bot); }
|
||||||
|
|
||||||
|
PositionInfo SinglePositionValue::Calculate()
|
||||||
|
{
|
||||||
|
PositionMap& posMap = AI_VALUE(PositionMap&, "position");
|
||||||
|
return posMap[getQualifier()];
|
||||||
|
}
|
||||||
|
|
||||||
|
void SinglePositionValue::Set(PositionInfo value)
|
||||||
|
{
|
||||||
|
PositionMap& posMap = AI_VALUE(PositionMap&, "position");
|
||||||
|
PositionInfo pos = posMap[getQualifier()];
|
||||||
|
pos = value;
|
||||||
|
posMap[getQualifier()] = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SinglePositionValue::Reset()
|
||||||
|
{
|
||||||
|
PositionMap& posMap = AI_VALUE(PositionMap&, "position");
|
||||||
|
PositionInfo pos = posMap[getQualifier()];
|
||||||
|
pos.Reset();
|
||||||
|
posMap[getQualifier()] = pos;
|
||||||
|
}
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#ifndef _PLAYERBOT_POSITIONVALUE_H
|
#ifndef _PLAYERBOT_POSITIONVALUE_H
|
||||||
#define _PLAYERBOT_POSITIONVALUE_H
|
#define _PLAYERBOT_POSITIONVALUE_H
|
||||||
|
|
||||||
|
#include "NamedObjectContext.h"
|
||||||
#include "TravelMgr.h"
|
#include "TravelMgr.h"
|
||||||
#include "Value.h"
|
#include "Value.h"
|
||||||
|
|
||||||
@ -15,6 +16,10 @@ class PositionInfo
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PositionInfo() : valueSet(false), x(0), y(0), z(0), mapId(0) {}
|
PositionInfo() : valueSet(false), x(0), y(0), z(0), mapId(0) {}
|
||||||
|
PositionInfo(float x, float y, float z, uint32 mapId, bool valueSet = true)
|
||||||
|
: valueSet(valueSet), x(x), y(y), z(z), mapId(mapId)
|
||||||
|
{
|
||||||
|
}
|
||||||
PositionInfo(PositionInfo const& other)
|
PositionInfo(PositionInfo const& other)
|
||||||
: valueSet(other.valueSet), x(other.x), y(other.y), z(other.z), mapId(other.mapId)
|
: valueSet(other.valueSet), x(other.x), y(other.y), z(other.z), mapId(other.mapId)
|
||||||
{
|
{
|
||||||
@ -72,4 +77,13 @@ public:
|
|||||||
WorldPosition Calculate() override;
|
WorldPosition Calculate() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class SinglePositionValue : public CalculatedValue<PositionInfo>, public Qualified
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SinglePositionValue(PlayerbotAI* ai, std::string name = "pos") : CalculatedValue(ai, name), Qualified() {};
|
||||||
|
virtual PositionInfo Calculate() override;
|
||||||
|
virtual void Set(PositionInfo value) override;
|
||||||
|
virtual void Reset() override;
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -8,8 +8,11 @@
|
|||||||
#include "CellImpl.h"
|
#include "CellImpl.h"
|
||||||
#include "GridNotifiers.h"
|
#include "GridNotifiers.h"
|
||||||
#include "GridNotifiersImpl.h"
|
#include "GridNotifiersImpl.h"
|
||||||
|
#include "ObjectGuid.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "ServerFacade.h"
|
#include "ServerFacade.h"
|
||||||
|
#include "SharedDefines.h"
|
||||||
|
#include "NearestGameObjects.h"
|
||||||
|
|
||||||
std::vector<uint32> PossibleRpgTargetsValue::allowedNpcFlags;
|
std::vector<uint32> PossibleRpgTargetsValue::allowedNpcFlags;
|
||||||
|
|
||||||
@ -78,3 +81,125 @@ bool PossibleRpgTargetsValue::AcceptUnit(Unit* unit)
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<uint32> PossibleNewRpgTargetsValue::allowedNpcFlags;
|
||||||
|
|
||||||
|
PossibleNewRpgTargetsValue::PossibleNewRpgTargetsValue(PlayerbotAI* botAI, float range)
|
||||||
|
: NearestUnitsValue(botAI, "possible new rpg targets", range, true)
|
||||||
|
{
|
||||||
|
if (allowedNpcFlags.empty())
|
||||||
|
{
|
||||||
|
allowedNpcFlags.push_back(UNIT_NPC_FLAG_INNKEEPER);
|
||||||
|
allowedNpcFlags.push_back(UNIT_NPC_FLAG_GOSSIP);
|
||||||
|
allowedNpcFlags.push_back(UNIT_NPC_FLAG_QUESTGIVER);
|
||||||
|
allowedNpcFlags.push_back(UNIT_NPC_FLAG_FLIGHTMASTER);
|
||||||
|
allowedNpcFlags.push_back(UNIT_NPC_FLAG_BANKER);
|
||||||
|
allowedNpcFlags.push_back(UNIT_NPC_FLAG_GUILD_BANKER);
|
||||||
|
allowedNpcFlags.push_back(UNIT_NPC_FLAG_TRAINER_CLASS);
|
||||||
|
allowedNpcFlags.push_back(UNIT_NPC_FLAG_TRAINER_PROFESSION);
|
||||||
|
allowedNpcFlags.push_back(UNIT_NPC_FLAG_VENDOR_AMMO);
|
||||||
|
allowedNpcFlags.push_back(UNIT_NPC_FLAG_VENDOR_FOOD);
|
||||||
|
allowedNpcFlags.push_back(UNIT_NPC_FLAG_VENDOR_POISON);
|
||||||
|
allowedNpcFlags.push_back(UNIT_NPC_FLAG_VENDOR_REAGENT);
|
||||||
|
allowedNpcFlags.push_back(UNIT_NPC_FLAG_AUCTIONEER);
|
||||||
|
allowedNpcFlags.push_back(UNIT_NPC_FLAG_STABLEMASTER);
|
||||||
|
allowedNpcFlags.push_back(UNIT_NPC_FLAG_PETITIONER);
|
||||||
|
allowedNpcFlags.push_back(UNIT_NPC_FLAG_TABARDDESIGNER);
|
||||||
|
allowedNpcFlags.push_back(UNIT_NPC_FLAG_BATTLEMASTER);
|
||||||
|
|
||||||
|
allowedNpcFlags.push_back(UNIT_NPC_FLAG_TRAINER);
|
||||||
|
allowedNpcFlags.push_back(UNIT_NPC_FLAG_VENDOR);
|
||||||
|
allowedNpcFlags.push_back(UNIT_NPC_FLAG_REPAIR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GuidVector PossibleNewRpgTargetsValue::Calculate()
|
||||||
|
{
|
||||||
|
std::list<Unit*> targets;
|
||||||
|
FindUnits(targets);
|
||||||
|
|
||||||
|
GuidVector results;
|
||||||
|
std::vector<std::pair<ObjectGuid, float>> guidDistancePairs;
|
||||||
|
for (Unit* unit : targets)
|
||||||
|
{
|
||||||
|
if (AcceptUnit(unit) && (ignoreLos || bot->IsWithinLOSInMap(unit)))
|
||||||
|
guidDistancePairs.push_back({unit->GetGUID(), bot->GetExactDist(unit)});
|
||||||
|
}
|
||||||
|
// Override to sort by distance
|
||||||
|
std::sort(guidDistancePairs.begin(), guidDistancePairs.end(), [](const auto& a, const auto& b) {
|
||||||
|
return a.second < b.second;
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const auto& pair : guidDistancePairs) {
|
||||||
|
results.push_back(pair.first);
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PossibleNewRpgTargetsValue::FindUnits(std::list<Unit*>& targets)
|
||||||
|
{
|
||||||
|
Acore::AnyUnitInObjectRangeCheck u_check(bot, range);
|
||||||
|
Acore::UnitListSearcher<Acore::AnyUnitInObjectRangeCheck> searcher(bot, targets, u_check);
|
||||||
|
Cell::VisitAllObjects(bot, searcher, range);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PossibleNewRpgTargetsValue::AcceptUnit(Unit* unit)
|
||||||
|
{
|
||||||
|
if (unit->IsHostileTo(bot) || unit->GetTypeId() == TYPEID_PLAYER)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (unit->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPIRITHEALER))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (uint32 npcFlag : allowedNpcFlags)
|
||||||
|
{
|
||||||
|
if (unit->HasFlag(UNIT_NPC_FLAGS, npcFlag))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<GameobjectTypes> PossibleNewRpgGameObjectsValue::allowedGOFlags;
|
||||||
|
|
||||||
|
GuidVector PossibleNewRpgGameObjectsValue::Calculate()
|
||||||
|
{
|
||||||
|
std::list<GameObject*> targets;
|
||||||
|
AnyGameObjectInObjectRangeCheck u_check(bot, range);
|
||||||
|
Acore::GameObjectListSearcher<AnyGameObjectInObjectRangeCheck> searcher(bot, targets, u_check);
|
||||||
|
Cell::VisitAllObjects(bot, searcher, range);
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<std::pair<ObjectGuid, float>> guidDistancePairs;
|
||||||
|
for (GameObject* go : targets)
|
||||||
|
{
|
||||||
|
bool flagCheck = false;
|
||||||
|
for (uint32 goFlag : allowedGOFlags)
|
||||||
|
{
|
||||||
|
if (go->GetGoType() == goFlag)
|
||||||
|
{
|
||||||
|
flagCheck = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!flagCheck)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!ignoreLos && !bot->IsWithinLOSInMap(go))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
guidDistancePairs.push_back({go->GetGUID(), bot->GetExactDist(go)});
|
||||||
|
}
|
||||||
|
GuidVector results;
|
||||||
|
|
||||||
|
// Sort by distance
|
||||||
|
std::sort(guidDistancePairs.begin(), guidDistancePairs.end(), [](const auto& a, const auto& b) {
|
||||||
|
return a.second < b.second;
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const auto& pair : guidDistancePairs) {
|
||||||
|
results.push_back(pair.first);
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
@ -6,8 +6,10 @@
|
|||||||
#ifndef _PLAYERBOT_POSSIBLERPGTARGETSVALUE_H
|
#ifndef _PLAYERBOT_POSSIBLERPGTARGETSVALUE_H
|
||||||
#define _PLAYERBOT_POSSIBLERPGTARGETSVALUE_H
|
#define _PLAYERBOT_POSSIBLERPGTARGETSVALUE_H
|
||||||
|
|
||||||
|
#include "NearestGameObjects.h"
|
||||||
#include "NearestUnitsValue.h"
|
#include "NearestUnitsValue.h"
|
||||||
#include "PlayerbotAIConfig.h"
|
#include "PlayerbotAIConfig.h"
|
||||||
|
#include "SharedDefines.h"
|
||||||
|
|
||||||
class PlayerbotAI;
|
class PlayerbotAI;
|
||||||
|
|
||||||
@ -23,4 +25,36 @@ protected:
|
|||||||
bool AcceptUnit(Unit* unit) override;
|
bool AcceptUnit(Unit* unit) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class PossibleNewRpgTargetsValue : public NearestUnitsValue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PossibleNewRpgTargetsValue(PlayerbotAI* botAI, float range = 150.0f);
|
||||||
|
|
||||||
|
static std::vector<uint32> allowedNpcFlags;
|
||||||
|
GuidVector Calculate() override;
|
||||||
|
protected:
|
||||||
|
void FindUnits(std::list<Unit*>& targets) override;
|
||||||
|
bool AcceptUnit(Unit* unit) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PossibleNewRpgGameObjectsValue : public ObjectGuidListCalculatedValue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PossibleNewRpgGameObjectsValue(PlayerbotAI* botAI, float range = 150.0f, bool ignoreLos = true)
|
||||||
|
: ObjectGuidListCalculatedValue(botAI, "possible new rpg game objects"), range(range), ignoreLos(ignoreLos)
|
||||||
|
{
|
||||||
|
if (allowedGOFlags.empty())
|
||||||
|
{
|
||||||
|
allowedGOFlags.push_back(GAMEOBJECT_TYPE_QUESTGIVER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<GameobjectTypes> allowedGOFlags;
|
||||||
|
GuidVector Calculate() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
float range;
|
||||||
|
bool ignoreLos;
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -118,6 +118,8 @@ public:
|
|||||||
creators["prioritized targets"] = &ValueContext::prioritized_targets;
|
creators["prioritized targets"] = &ValueContext::prioritized_targets;
|
||||||
creators["all targets"] = &ValueContext::all_targets;
|
creators["all targets"] = &ValueContext::all_targets;
|
||||||
creators["possible rpg targets"] = &ValueContext::possible_rpg_targets;
|
creators["possible rpg targets"] = &ValueContext::possible_rpg_targets;
|
||||||
|
creators["possible new rpg targets"] = &ValueContext::possible_new_rpg_targets;
|
||||||
|
creators["possible new rpg game objects"] = &ValueContext::possible_new_rpg_game_objects;
|
||||||
creators["nearest adds"] = &ValueContext::nearest_adds;
|
creators["nearest adds"] = &ValueContext::nearest_adds;
|
||||||
creators["nearest corpses"] = &ValueContext::nearest_corpses;
|
creators["nearest corpses"] = &ValueContext::nearest_corpses;
|
||||||
creators["log level"] = &ValueContext::log_level;
|
creators["log level"] = &ValueContext::log_level;
|
||||||
@ -192,6 +194,7 @@ public:
|
|||||||
creators["rti cc"] = &ValueContext::rti_cc;
|
creators["rti cc"] = &ValueContext::rti_cc;
|
||||||
creators["rti"] = &ValueContext::rti;
|
creators["rti"] = &ValueContext::rti;
|
||||||
creators["position"] = &ValueContext::position;
|
creators["position"] = &ValueContext::position;
|
||||||
|
creators["pos"] = &ValueContext::pos;
|
||||||
creators["current position"] = &ValueContext::current_position;
|
creators["current position"] = &ValueContext::current_position;
|
||||||
creators["threat"] = &ValueContext::threat;
|
creators["threat"] = &ValueContext::threat;
|
||||||
|
|
||||||
@ -340,6 +343,7 @@ private:
|
|||||||
static UntypedValue* attackers(PlayerbotAI* botAI) { return new AttackersValue(botAI); }
|
static UntypedValue* attackers(PlayerbotAI* botAI) { return new AttackersValue(botAI); }
|
||||||
|
|
||||||
static UntypedValue* position(PlayerbotAI* botAI) { return new PositionValue(botAI); }
|
static UntypedValue* position(PlayerbotAI* botAI) { return new PositionValue(botAI); }
|
||||||
|
static UntypedValue* pos(PlayerbotAI* ai) { return new SinglePositionValue(ai); }
|
||||||
static UntypedValue* current_position(PlayerbotAI* botAI) { return new CurrentPositionValue(botAI); }
|
static UntypedValue* current_position(PlayerbotAI* botAI) { return new CurrentPositionValue(botAI); }
|
||||||
static UntypedValue* rti(PlayerbotAI* botAI) { return new RtiValue(botAI); }
|
static UntypedValue* rti(PlayerbotAI* botAI) { return new RtiValue(botAI); }
|
||||||
static UntypedValue* rti_cc(PlayerbotAI* botAI) { return new RtiCcValue(botAI); }
|
static UntypedValue* rti_cc(PlayerbotAI* botAI) { return new RtiCcValue(botAI); }
|
||||||
@ -406,6 +410,8 @@ private:
|
|||||||
static UntypedValue* nearest_enemy_players(PlayerbotAI* botAI) { return new NearestEnemyPlayersValue(botAI); }
|
static UntypedValue* nearest_enemy_players(PlayerbotAI* botAI) { return new NearestEnemyPlayersValue(botAI); }
|
||||||
static UntypedValue* nearest_corpses(PlayerbotAI* botAI) { return new NearestCorpsesValue(botAI); }
|
static UntypedValue* nearest_corpses(PlayerbotAI* botAI) { return new NearestCorpsesValue(botAI); }
|
||||||
static UntypedValue* possible_rpg_targets(PlayerbotAI* botAI) { return new PossibleRpgTargetsValue(botAI); }
|
static UntypedValue* possible_rpg_targets(PlayerbotAI* botAI) { return new PossibleRpgTargetsValue(botAI); }
|
||||||
|
static UntypedValue* possible_new_rpg_targets(PlayerbotAI* botAI) { return new PossibleNewRpgTargetsValue(botAI); }
|
||||||
|
static UntypedValue* possible_new_rpg_game_objects(PlayerbotAI* botAI) { return new PossibleNewRpgGameObjectsValue(botAI); }
|
||||||
static UntypedValue* possible_targets(PlayerbotAI* botAI) { return new PossibleTargetsValue(botAI); }
|
static UntypedValue* possible_targets(PlayerbotAI* botAI) { return new PossibleTargetsValue(botAI); }
|
||||||
static UntypedValue* possible_triggers(PlayerbotAI* botAI) { return new PossibleTriggersValue(botAI); }
|
static UntypedValue* possible_triggers(PlayerbotAI* botAI) { return new PossibleTriggersValue(botAI); }
|
||||||
static UntypedValue* possible_targets_no_los(PlayerbotAI* botAI)
|
static UntypedValue* possible_targets_no_los(PlayerbotAI* botAI)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user