Skip to content

Commit

Permalink
implement playerbot from blueboy (originally from TC) with small corr…
Browse files Browse the repository at this point in the history
…ects.

- default state - full disabled.
  • Loading branch information
blueboy authored and rsa committed Apr 25, 2011
1 parent 6df6746 commit bd270fb
Show file tree
Hide file tree
Showing 48 changed files with 13,131 additions and 18 deletions.
229 changes: 229 additions & 0 deletions README.PlayerBot
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
What it is:
===========

Playerbot lets you add another character from your account as a bot that you can control and which will hopefully help you. Only characters from your account can be used, so you can have a maximum of 9 bots at one time.

This was taken from the Trinity site, and modified slightly by me to get some of the kinks out. I reworked the priest class and also added a mage class and a warrior class, which are still in crude form. Any class can be used as a bot - just don't expect much in the way of spells or abilities until someone writes the code for them.

Bots will only use abilities that they have - for example, a priest will only use the renew spell if it has been trained. Also, bot's equipment will lose durability like any other character. So every so often you'll need to log in and repair and train your bot.

For Mangos 7800+

Commands:
=========

/s .bot add BOTNAME (add character to world)
/s .bot remove BOTNAME
/s .bot co|combatorder BOTNAME COMBATORDER [TARGET]
/invite BOTNAME (bot will auto accept invite)
/t BOTNAME attack (bot will attack selected target, similar to the way a pet can attack)
/t BOTNAME follow (orders bot to follow player; will also revive bot if dead or teleport bot if far away)
/t BOTNAME stay
/t BOTNAME assist (you'll need to be attacking something and the bot only does melee atm)
/t BOTNAME spells (replies with all spells known to bot)
/t BOTNAME cast <SPELLID | (part of) SPELLNAME | SPELLLINK>
/t BOTNAME use <ITEM LINK>
/t BOTNAME equip <ITEM LINK>
/t BOTNAME reset (will reset states, orders and loot list)
/t BOTNAME report (bot reports all items needed to finish quests)
/t BOTNAME stats (bot shows available money, free inventory space and estimated item repair costs)
/t BOTNAME survey (bot shows all available gameobjects, within a local perimeter around the bot)
/t BOTNAME find <GAMEOBJECT LINK> (bot will travel to the gameobject location and then wait)
/t BOTNAME get <GAMEOBJECT LINK> (bot will fetch the selected gameobject and then return to the player)
/t BOTNAME quests (List bot's current quests)
/t BOTNAME drop <QUESTLINK> (Drop a quest)
/t BOTNAME orders (Shows bot's combat orders)
/t BOTNAME pet spells (Shows spells known to bot's pet. Autocast spells will be shown in green)
/t BOTNAME pet cast <SPELLID | (part of) SPELLNAME | SPELLLINK>
/t BOTNAME pet toggle <SPELLID | (part of) SPELLNAME | SPELLLINK> (Toggle autocast for a given spell)
/t BOTNAME pet state (Shows current react mode of bot's pet)
/t BOTNAME pet react <(a)ggressive | (d)efensive | (p)assive> (Set bot's pet reaction mode)

Shortcuts:
c = cast
e = equip
u = use

Gameobject interaction with bots:
=================================

The bot(s) can now interact with gameobjects. This is particularly useful, in order to complete 'gather' type
quests (e.g Milly's harvest in Northshire). The bot(s) can also now, harvest 'ore deposits' and 'herbs'

Three new commands have been introduced 'survey, 'find' & 'get', to facilitate this new feature.

The 'survey' command provides the means for bot(s) to detect gameobjects in the world. It can be used to detect
available gameobjects local to a single bot, or more effectively (wider area) those for a party of bots.

Suggestion: setup the 'survey' command as an assigned macro button, on the client (e.g /p survey). You can
then quickly refresh the gameobject list.

Gameobject list <GAMEOBJECT LINK> (Currently bots can only interact with ore, herb and needed quest items)
---------------

[Copper Vein][Silverleaf][Earthroot][Milly's Harvest][Battered Chest][Food Crate]

Then, use the 'find' or 'get' commands to interect with the gameobject.

Using the gameobject list information, it is possible to locate and/or fetch each of the gameobjects. To select
a <GAMEOBJECT LINK>, hold down the shift key and click on the relevant link with your mouse.

Repair with bots:
=================

The bot(s) can now be repaired, as the player repairs. You can decide whether you wish the bot(s) to pay for
their own repair or if available, use the guild bank. Choose the appropriate repair 'Anvil' at your local
NPC. Only group bot(s) members can be repaired. If you wish to exclude certain bot(s) from repair, then
temporarily uninvite bot(s) from the group.

Limitations: Bot(s) cannot repair individual items.
If the player does not require repair, you cannot repair bot(s).

The new 'stats' command provides useful information to help in the repair decision.

First: Money available to bot(s)
Second: Free inventory slots for bot(s)
Third: Estimated (excludes NPC reputation discount) item damage cost for bot(s).

Combat Orders explained:
========================

There are primary and secondary commands which can be combined. In this way it is
possible to define a bot to assist the main tank and also protect the healer, making
combat management much easier.
The commands assist and protect require a target parameter or a friendly player
selected by bots master.
Available Combat Orders:
tank pri try to bind all targets involved in combat by gaining highest threat
assist pri do damage on selected targets attacker without getting highest threat
heal pri concentrate on healing - no offensive spells, try to keep threat low
protect sec if target of protect get's directly attacked gain higher threat on attacker
reset - clear out assist and protect targets and set combat order to nothing
Examples:
.bot co TheTank tank
.bot co MyHealer heal
.bot co TheBrutal assist TheTank
.bot co TheBrutal protect MyHealer

Trading with bots:
==================

To trade items/money with your bot simply initiate a trade and the bot will tell you how much money and items are available. To request an item simple whisper the bot and shift click the link of the item you would like. You can link multiple items on the same line. You can also request money in the following manner when the trade window is open:
/w BOTNAME 10g <-- request that the bot give you 10 gold
/w BOTNAME 6g500s25c <-- request 6 gold, 500 silver, and 25 cooper

A bot is also able to show an item in its 'Will not be traded' slot. The item can be either
in its bags or be equipped and even be soulbound. By this you can cast spells/enchantments
on soulbound items ('nt' stands for 'not trading').
/w BOTNAME nt [Powerful Soulbound Item]

More Information:
=================

If specifying a spell substring, the spell chosen will be in priority of exact name match, highest spell rank, and spell using no reagents. Case does not matter. Here's some examples:
/t BOTNAME c greater heal
/t BOTNAME cast pain
/w BOTNAME c poly
/w BOTNAME cast fort
/t BOTNAME cast <SPELLID>
- OR -
/w BOTNAME c <SPELLID>

Also all commands can be broadcast to the party. For example:
/p follow
/p spells

To use or equip items for your bot say:
/w BOTNAME use <ITEMLINK1> <ITEMLINK2>
/w BOTNAME equip <ITEMLINK1> <ITEMLINK2>
- OR -
/w BOTNAME u <ITEMLINK1> <ITEMLINK2>
/w BOTNAME e <ITEMLINK1> <ITEMLINK2>

If you inspect your bot, your bot will tell you what items you have in your inventory that you can equip. To create a link in the chat window, hold the shift key and press the left mouse button when clicking the link.


Changes from Trinity to Mangos:
===============================

I added the following in SharedDefines.h.

enum SpellCategory
{
SPELL_CATEGORY_FOOD = 11,
SPELL_CATEGORY_DRINK = 59
};

I also had to add the following to Player.h:

enum PlayerStateType
{
PLAYER_STATE_NONE = 0,
PLAYER_STATE_SIT = 1
};


Configuration variables:
========================
Also see src/mangosd/mangosd.conf.dist for configuration variables!

PlayerbotAI.DebugWhisper
Enable debug output by whispering master
Default: 0 - off
1 - on

PlayerbotAI.FollowDistanceMin
PlayerbotAI.FollowDistanceMax
Min. and max. follow distance for bots
Default: 0.5 / 1.0


Some Problems:
==============

The bots don't always face in the right direction. Sometimes when a bot makes the kill, the corpse is not lootable. The mage bot sometimes get stuck when he begins to cast a spell (but this is corrected the next time he enters combat).

-- BotGuy
What it is:
===========

The new revised'botguy' utilizes NPCs already distributed throughout the world, to allow players to
summon and dismiss bots at will, from their own account.

This is a revised (more stable) version that utilizes the new 'GOSSIP MENU SYSTEM' to modify the menus of existing NPCs
(e.g Trainers etc) to include the bot Recruit/Dismiss menu. (No GameMaster account necessary).

Install (Server administrators only)
=======
Please apply 'mangos_botguy.sql' once to the world database to update the 'gossip_menu_option' table.

-- ToDo
[DONE] ///---Quest---///
[DONE] Bot can accept quest.
[DONE] Bot can join quest.

[DONE] ///---Loot---///
[DONE] Bot can loot.
[DONE] Bot can loot --- maybe only needed q item.

//---Instance teleport Problems---///
[DONE] Needs some fix.

///---Combat and Movement Orders---///
[DONE] Movement orders (stay, follow)
[DONE] Combat orders (protect, assist)
Combat order TANK
Combat order HEAL

///---Temporary item enchantments---///
[DONE] Rogue : Poison
Warrior : Sharpening Stone, Rune of Warding, Rune of Shielding.

//---After fear bot's lose target---///
Needs some fix.

///---Move behind target---///
Rogue : some abilitys require Rogue to be stealthed and behind target.

///---Implement locale independet way of getting spellIDs---///
[DONE] Hardcode lowest rank spellID, use function to get highest rank
1 change: 1 addition & 0 deletions addition/744_mangos_botguy.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
INSERT INTO `gossip_menu_option` VALUES('0','16','0','GOSSIP_OPTION_BOT','18','1','0','0','0','0','0',NULL,'0','0','0','0','0','0','0','0','0');
3 changes: 3 additions & 0 deletions src/game/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ file(GLOB_RECURSE game_SRCS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp *.h)
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/vmap
${CMAKE_CURRENT_SOURCE_DIR}/playerbot
${CMAKE_SOURCE_DIR}/dep/include/g3dlite
${CMAKE_SOURCE_DIR}/dep/include
${CMAKE_SOURCE_DIR}/src/shared
Expand Down Expand Up @@ -106,3 +107,5 @@ if(PCH)
endif()
endif()
endif()

install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/playerbot/playerbot.conf.dist.in DESTINATION ${CONF_DIR} RENAME playerbot.conf.dist)
51 changes: 50 additions & 1 deletion src/game/CharacterHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@
#include "ArenaTeam.h"
#include "Language.h"

// Playerbot mod:
#include "playerbot/PlayerbotMgr.h"

// config option SkipCinematics supported values
enum CinematicsSkipMode
{
Expand Down Expand Up @@ -134,6 +137,31 @@ class CharacterHandler
}
session->HandlePlayerLogin((LoginQueryHolder*)holder);
}
// Playerbot mod: is different from the normal HandlePlayerLoginCallback in that it
// sets up the bot's world session and also stores the pointer to the bot player in the master's
// world session m_playerBots map
void HandlePlayerBotLoginCallback(QueryResult * /*dummy*/, SqlQueryHolder * holder)
{
if (!holder)
return;

LoginQueryHolder* lqh = (LoginQueryHolder*) holder;

WorldSession* masterSession = sWorld.FindSession(lqh->GetAccountId());

if (! masterSession || sObjectMgr.GetPlayer(lqh->GetGuid()))
{
delete holder;
return;
}

// The bot's WorldSession is owned by the bot's Player object
// The bot's WorldSession is deleted by PlayerbotMgr::LogoutPlayerBot
WorldSession *botSession = new WorldSession(lqh->GetAccountId(), NULL, SEC_PLAYER, masterSession->Expansion(), 0, LOCALE_enUS);
botSession->m_Address = "bot";
botSession->HandlePlayerLogin(lqh); // will delete lqh
masterSession->GetPlayer()->GetPlayerbotMgr()->OnBotLogin(botSession->GetPlayer());
}
} chrHandler;

void WorldSession::HandleCharEnum(QueryResult * result)
Expand Down Expand Up @@ -570,6 +598,27 @@ void WorldSession::HandlePlayerLoginOpcode( WorldPacket & recv_data )
CharacterDatabase.DelayQueryHolder(&chrHandler, &CharacterHandler::HandlePlayerLoginCallback, holder);
}

// Playerbot mod. Can't easily reuse HandlePlayerLoginOpcode for logging in bots because it assumes
// a WorldSession exists for the bot. The WorldSession for a bot is created after the character is loaded.
void PlayerbotMgr::AddPlayerBot(uint64 playerGuid)
{
// has bot already been added?
if (sObjectMgr.GetPlayer(playerGuid))
return;

uint32 accountId = sObjectMgr.GetPlayerAccountIdByGUID(playerGuid);
if (accountId == 0)
return;

LoginQueryHolder *holder = new LoginQueryHolder(accountId, playerGuid);
if(!holder->Initialize())
{
delete holder; // delete all unprocessed queries
return;
}
CharacterDatabase.DelayQueryHolder(&chrHandler, &CharacterHandler::HandlePlayerBotLoginCallback, holder);
}

void WorldSession::HandlePlayerLogin(LoginQueryHolder *holder)
{
ObjectGuid playerGuid = holder->GetGuid();
Expand Down Expand Up @@ -1324,6 +1373,6 @@ void WorldSession::HandleEquipmentSetUseOpcode(WorldPacket &recv_data)
}

WorldPacket data(SMSG_USE_EQUIPMENT_SET_RESULT, 1);
data << uint8(0); // 4 - equipment swap failed - inventory is full
data << uint8(0); // 4 - equipment swap failed - inventory is full
SendPacket(&data);
}
2 changes: 2 additions & 0 deletions src/game/Chat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,8 @@ ChatCommand * ChatHandler::getCommandTable()
{ "repairitems", SEC_GAMEMASTER, true, &ChatHandler::HandleRepairitemsCommand, "", NULL },
{ "stable", SEC_ADMINISTRATOR, false, &ChatHandler::HandleStableCommand, "", NULL },
{ "waterwalk", SEC_GAMEMASTER, false, &ChatHandler::HandleWaterwalkCommand, "", NULL },
//Playerbot mod
{ "bot", SEC_PLAYER, false, &ChatHandler::HandlePlayerbotCommand, "", NULL },
{ "quit", SEC_CONSOLE, true, &ChatHandler::HandleQuitCommand, "", NULL },

{ NULL, 0, false, NULL, "", NULL }
Expand Down
1 change: 1 addition & 0 deletions src/game/Chat.h
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,7 @@ class ChatHandler
bool HandleRepairitemsCommand(char* args);
bool HandleStableCommand(char* args);
bool HandleWaterwalkCommand(char* args);
bool HandlePlayerbotCommand(char* args);
bool HandleQuitCommand(char* args);

//! Development Commands
Expand Down
26 changes: 25 additions & 1 deletion src/game/ChatHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@
#include "GridNotifiersImpl.h"
#include "CellImpl.h"

// Playerbot mod
#include "playerbot/PlayerbotAI.h"

bool WorldSession::processChatmessageFurtherAfterSecurityChecks(std::string& msg, uint32 lang)
{
if (lang != LANG_ADDON)
Expand Down Expand Up @@ -234,7 +237,15 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data )
}
}

GetPlayer()->Whisper(msg, lang, player->GetGUID());
// Playerbot mod: handle whispered command to bot
if (player->GetPlayerbotAI())
{
player->GetPlayerbotAI()->HandleCommand(msg, *GetPlayer());
GetPlayer()->m_speakTime = 0;
GetPlayer()->m_speakCount = 0;
}
else
GetPlayer()->Whisper(msg, lang, player->GetGUID());
} break;

case CHAT_MSG_PARTY:
Expand Down Expand Up @@ -269,6 +280,19 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data )
if ((type == CHAT_MSG_PARTY_LEADER) && !group->IsLeader(_player->GetObjectGuid()))
return;

// Playerbot mod: broadcast message to bot members
for(GroupReference* itr = group->GetFirstMember(); itr != NULL; itr=itr->next())
{
Player* player = itr->getSource();
if (player && player->GetPlayerbotAI())
{
player->GetPlayerbotAI()->HandleCommand(msg, *GetPlayer());
GetPlayer()->m_speakTime = 0;
GetPlayer()->m_speakCount = 0;
}
}
// END Playerbot mod

WorldPacket data;
ChatHandler::FillMessageData(&data, this, type, lang, NULL, 0, msg.c_str(), NULL);
group->BroadcastPacket(&data, false, group->GetMemberGroup(GetPlayer()->GetObjectGuid()));
Expand Down
3 changes: 3 additions & 0 deletions src/game/Creature.h
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,9 @@ class MANGOS_DLL_SPEC Creature : public Unit
bool IsTotem() const { return m_subtype == CREATURE_SUBTYPE_TOTEM; }
bool IsTemporarySummon() const { return m_subtype == CREATURE_SUBTYPE_TEMPORARY_SUMMON; }

// Playerbot mod - adds functionality to load/unload bots from NPC, also need to apply SQL scripts
void LoadBotMenu(Player *pPlayer);

bool IsCorpse() const { return getDeathState() == CORPSE; }
bool IsDespawned() const { return getDeathState() == DEAD; }
void SetCorpseDelay(uint32 delay) { m_corpseDelay = delay; }
Expand Down
Loading

0 comments on commit bd270fb

Please sign in to comment.