* Fixed initial offhand attack timer so the main and offhand attacks will now act blizzlike instead of getting executed at the same time Note: since some creature equipment items cannot be identified as weapon since they are only visual items there is no way to detect this automaticly (cherry picked from commit 869affa89300c24b85896a050b58241714786454)
1866 lines
68 KiB
C++
1866 lines
68 KiB
C++
/*
|
|
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2 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 General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/* ScriptData
|
|
Name: npc_commandscript
|
|
%Complete: 100
|
|
Comment: All npc related commands
|
|
Category: commandscripts
|
|
EndScriptData */
|
|
|
|
#include "ScriptMgr.h"
|
|
#include "Chat.h"
|
|
#include "CreatureAI.h"
|
|
#include "CreatureGroups.h"
|
|
#include "DatabaseEnv.h"
|
|
#include "Language.h"
|
|
#include "Log.h"
|
|
#include "Map.h"
|
|
#include "ObjectAccessor.h"
|
|
#include "ObjectMgr.h"
|
|
#include "Pet.h"
|
|
#include "PhasingHandler.h"
|
|
#include "Player.h"
|
|
#include "RBAC.h"
|
|
#include "TargetedMovementGenerator.h" // for HandleNpcUnFollowCommand
|
|
#include "Transport.h"
|
|
#include "World.h"
|
|
#include "WorldSession.h"
|
|
|
|
template<typename E, typename T = char const*>
|
|
struct EnumName
|
|
{
|
|
E Value;
|
|
T Name;
|
|
};
|
|
|
|
#define CREATE_NAMED_ENUM(VALUE) { VALUE, STRINGIZE(VALUE) }
|
|
|
|
#define NPC_FLAG_COUNT 24
|
|
#define FLAGS_EXTRA_COUNT 21
|
|
|
|
EnumName<NPCFlags, uint32> const npcFlagTexts[NPC_FLAG_COUNT] =
|
|
{
|
|
{ UNIT_NPC_FLAG_AUCTIONEER, LANG_NPCINFO_AUCTIONEER },
|
|
{ UNIT_NPC_FLAG_BANKER, LANG_NPCINFO_BANKER },
|
|
{ UNIT_NPC_FLAG_BATTLEMASTER, LANG_NPCINFO_BATTLEMASTER },
|
|
{ UNIT_NPC_FLAG_FLIGHTMASTER, LANG_NPCINFO_FLIGHTMASTER },
|
|
{ UNIT_NPC_FLAG_GOSSIP, LANG_NPCINFO_GOSSIP },
|
|
{ UNIT_NPC_FLAG_GUILD_BANKER, LANG_NPCINFO_GUILD_BANKER },
|
|
{ UNIT_NPC_FLAG_INNKEEPER, LANG_NPCINFO_INNKEEPER },
|
|
{ UNIT_NPC_FLAG_PETITIONER, LANG_NPCINFO_PETITIONER },
|
|
{ UNIT_NPC_FLAG_PLAYER_VEHICLE, LANG_NPCINFO_PLAYER_VEHICLE },
|
|
{ UNIT_NPC_FLAG_QUESTGIVER, LANG_NPCINFO_QUESTGIVER },
|
|
{ UNIT_NPC_FLAG_REPAIR, LANG_NPCINFO_REPAIR },
|
|
{ UNIT_NPC_FLAG_SPELLCLICK, LANG_NPCINFO_SPELLCLICK },
|
|
{ UNIT_NPC_FLAG_SPIRITGUIDE, LANG_NPCINFO_SPIRITGUIDE },
|
|
{ UNIT_NPC_FLAG_SPIRITHEALER, LANG_NPCINFO_SPIRITHEALER },
|
|
{ UNIT_NPC_FLAG_STABLEMASTER, LANG_NPCINFO_STABLEMASTER },
|
|
{ UNIT_NPC_FLAG_TABARDDESIGNER, LANG_NPCINFO_TABARDDESIGNER },
|
|
{ UNIT_NPC_FLAG_TRAINER, LANG_NPCINFO_TRAINER },
|
|
{ UNIT_NPC_FLAG_TRAINER_CLASS, LANG_NPCINFO_TRAINER_CLASS },
|
|
{ UNIT_NPC_FLAG_TRAINER_PROFESSION, LANG_NPCINFO_TRAINER_PROFESSION },
|
|
{ UNIT_NPC_FLAG_VENDOR, LANG_NPCINFO_VENDOR },
|
|
{ UNIT_NPC_FLAG_VENDOR_AMMO, LANG_NPCINFO_VENDOR_AMMO },
|
|
{ UNIT_NPC_FLAG_VENDOR_FOOD, LANG_NPCINFO_VENDOR_FOOD },
|
|
{ UNIT_NPC_FLAG_VENDOR_POISON, LANG_NPCINFO_VENDOR_POISON },
|
|
{ UNIT_NPC_FLAG_VENDOR_REAGENT, LANG_NPCINFO_VENDOR_REAGENT }
|
|
};
|
|
|
|
EnumName<Mechanics> const mechanicImmunes[MAX_MECHANIC] =
|
|
{
|
|
CREATE_NAMED_ENUM(MECHANIC_NONE),
|
|
CREATE_NAMED_ENUM(MECHANIC_CHARM),
|
|
CREATE_NAMED_ENUM(MECHANIC_DISORIENTED),
|
|
CREATE_NAMED_ENUM(MECHANIC_DISARM),
|
|
CREATE_NAMED_ENUM(MECHANIC_DISTRACT),
|
|
CREATE_NAMED_ENUM(MECHANIC_FEAR),
|
|
CREATE_NAMED_ENUM(MECHANIC_GRIP),
|
|
CREATE_NAMED_ENUM(MECHANIC_ROOT),
|
|
CREATE_NAMED_ENUM(MECHANIC_SLOW_ATTACK),
|
|
CREATE_NAMED_ENUM(MECHANIC_SILENCE),
|
|
CREATE_NAMED_ENUM(MECHANIC_SLEEP),
|
|
CREATE_NAMED_ENUM(MECHANIC_SNARE),
|
|
CREATE_NAMED_ENUM(MECHANIC_STUN),
|
|
CREATE_NAMED_ENUM(MECHANIC_FREEZE),
|
|
CREATE_NAMED_ENUM(MECHANIC_KNOCKOUT),
|
|
CREATE_NAMED_ENUM(MECHANIC_BLEED),
|
|
CREATE_NAMED_ENUM(MECHANIC_BANDAGE),
|
|
CREATE_NAMED_ENUM(MECHANIC_POLYMORPH),
|
|
CREATE_NAMED_ENUM(MECHANIC_BANISH),
|
|
CREATE_NAMED_ENUM(MECHANIC_SHIELD),
|
|
CREATE_NAMED_ENUM(MECHANIC_SHACKLE),
|
|
CREATE_NAMED_ENUM(MECHANIC_MOUNT),
|
|
CREATE_NAMED_ENUM(MECHANIC_INFECTED),
|
|
CREATE_NAMED_ENUM(MECHANIC_TURN),
|
|
CREATE_NAMED_ENUM(MECHANIC_HORROR),
|
|
CREATE_NAMED_ENUM(MECHANIC_INVULNERABILITY),
|
|
CREATE_NAMED_ENUM(MECHANIC_INTERRUPT),
|
|
CREATE_NAMED_ENUM(MECHANIC_DAZE),
|
|
CREATE_NAMED_ENUM(MECHANIC_DISCOVERY),
|
|
CREATE_NAMED_ENUM(MECHANIC_IMMUNE_SHIELD),
|
|
CREATE_NAMED_ENUM(MECHANIC_SAPPED),
|
|
CREATE_NAMED_ENUM(MECHANIC_ENRAGED),
|
|
CREATE_NAMED_ENUM(MECHANIC_WOUNDED)
|
|
};
|
|
|
|
EnumName<UnitFlags> const unitFlags[MAX_UNIT_FLAGS] =
|
|
{
|
|
CREATE_NAMED_ENUM(UNIT_FLAG_SERVER_CONTROLLED),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG_NON_ATTACKABLE),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG_REMOVE_CLIENT_CONTROL),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG_PVP_ATTACKABLE),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG_RENAME),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG_PREPARATION),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG_UNK_6),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG_NOT_ATTACKABLE_1),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG_IMMUNE_TO_PC),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG_IMMUNE_TO_NPC),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG_LOOTING),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG_PET_IN_COMBAT),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG_PVP),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG_SILENCED),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG_CANNOT_SWIM),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG_UNK_15),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG_UNK_16),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG_PACIFIED),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG_STUNNED),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG_IN_COMBAT),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG_TAXI_FLIGHT),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG_DISARMED),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG_CONFUSED),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG_FLEEING),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG_PLAYER_CONTROLLED),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG_NOT_SELECTABLE),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG_SKINNABLE),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG_MOUNT),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG_UNK_28),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG_UNK_29),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG_SHEATHE),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG_UNK_31)
|
|
};
|
|
|
|
EnumName<UnitFlags2> const unitFlags2[MAX_UNIT_FLAGS_2] =
|
|
{
|
|
CREATE_NAMED_ENUM(UNIT_FLAG2_FEIGN_DEATH),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG2_UNK1),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG2_IGNORE_REPUTATION),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG2_COMPREHEND_LANG),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG2_MIRROR_IMAGE),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG2_INSTANTLY_APPEAR_MODEL),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG2_FORCE_MOVEMENT),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG2_DISARM_OFFHAND),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG2_DISABLE_PRED_STATS),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG2_DISARM_RANGED),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG2_REGENERATE_POWER),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG2_RESTRICT_PARTY_INTERACTION),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG2_PREVENT_SPELL_CLICK),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG2_ALLOW_ENEMY_INTERACT),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG2_DISABLE_TURN),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG2_UNK2),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG2_PLAY_DEATH_ANIM),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG2_ALLOW_CHEAT_SPELLS),
|
|
CREATE_NAMED_ENUM(UNIT_FLAG2_NO_ACTIONS)
|
|
};
|
|
|
|
EnumName<UnitFlags3> const unitFlags3[MAX_UNIT_FLAGS_3] =
|
|
{
|
|
CREATE_NAMED_ENUM(UNIT_FLAG3_UNK1)
|
|
};
|
|
|
|
EnumName<CreatureFlagsExtra> const flagsExtra[FLAGS_EXTRA_COUNT] =
|
|
{
|
|
CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_INSTANCE_BIND),
|
|
CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_CIVILIAN),
|
|
CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_NO_PARRY),
|
|
CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_NO_PARRY_HASTEN),
|
|
CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_NO_BLOCK),
|
|
CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_NO_CRUSH),
|
|
CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_NO_XP_AT_KILL),
|
|
CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_TRIGGER),
|
|
CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_NO_TAUNT),
|
|
CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_NO_MOVE_FLAGS_UPDATE),
|
|
CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_WORLDEVENT),
|
|
CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_GUARD),
|
|
CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_NO_CRIT),
|
|
CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_NO_SKILLGAIN),
|
|
CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_TAUNT_DIMINISH),
|
|
CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_ALL_DIMINISH),
|
|
CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_NO_PLAYER_DAMAGE_REQ),
|
|
CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_DUNGEON_BOSS),
|
|
CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_IGNORE_PATHFINDING),
|
|
CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_IMMUNITY_KNOCKBACK),
|
|
CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK)
|
|
};
|
|
|
|
bool HandleNpcSpawnGroup(ChatHandler* handler, char const* args)
|
|
{
|
|
if (!*args)
|
|
return false;
|
|
|
|
bool ignoreRespawn = false;
|
|
bool force = false;
|
|
uint32 groupId = 0;
|
|
|
|
// Decode arguments
|
|
char* arg = strtok((char*)args, " ");
|
|
while (arg)
|
|
{
|
|
std::string thisArg = arg;
|
|
std::transform(thisArg.begin(), thisArg.end(), thisArg.begin(), ::tolower);
|
|
if (thisArg == "ignorerespawn")
|
|
ignoreRespawn = true;
|
|
else if (thisArg == "force")
|
|
force = true;
|
|
else if (thisArg.empty() || !(std::count_if(thisArg.begin(), thisArg.end(), ::isdigit) == (int)thisArg.size()))
|
|
return false;
|
|
else
|
|
groupId = atoi(thisArg.c_str());
|
|
|
|
arg = strtok(NULL, " ");
|
|
}
|
|
|
|
Player* player = handler->GetSession()->GetPlayer();
|
|
|
|
std::vector <WorldObject*> creatureList;
|
|
if (!player->GetMap()->SpawnGroupSpawn(groupId, ignoreRespawn, force, &creatureList))
|
|
{
|
|
handler->PSendSysMessage(LANG_SPAWNGROUP_BADGROUP, groupId);
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
handler->PSendSysMessage(LANG_SPAWNGROUP_SPAWNCOUNT, creatureList.size());
|
|
for (WorldObject* obj : creatureList)
|
|
handler->PSendSysMessage("%s (%s)", obj->GetName(), obj->GetGUID().ToString().c_str());
|
|
|
|
return true;
|
|
}
|
|
|
|
bool HandleNpcDespawnGroup(ChatHandler* handler, char const* args)
|
|
{
|
|
if (!*args)
|
|
return false;
|
|
|
|
bool deleteRespawnTimes = false;
|
|
uint32 groupId = 0;
|
|
|
|
// Decode arguments
|
|
char* arg = strtok((char*)args, " ");
|
|
while (arg)
|
|
{
|
|
std::string thisArg = arg;
|
|
std::transform(thisArg.begin(), thisArg.end(), thisArg.begin(), ::tolower);
|
|
if (thisArg == "removerespawntime")
|
|
deleteRespawnTimes = true;
|
|
else if (thisArg.empty() || !(std::count_if(thisArg.begin(), thisArg.end(), ::isdigit) == (int)thisArg.size()))
|
|
return false;
|
|
else
|
|
groupId = atoi(thisArg.c_str());
|
|
|
|
arg = strtok(nullptr, " ");
|
|
}
|
|
|
|
Player* player = handler->GetSession()->GetPlayer();
|
|
|
|
if (!player->GetMap()->SpawnGroupDespawn(groupId, deleteRespawnTimes))
|
|
{
|
|
handler->PSendSysMessage(LANG_SPAWNGROUP_BADGROUP, groupId);
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
class npc_commandscript : public CommandScript
|
|
{
|
|
public:
|
|
npc_commandscript() : CommandScript("npc_commandscript") { }
|
|
|
|
std::vector<ChatCommand> GetCommands() const override
|
|
{
|
|
static std::vector<ChatCommand> npcAddCommandTable =
|
|
{
|
|
{ "formation", rbac::RBAC_PERM_COMMAND_NPC_ADD_FORMATION, false, &HandleNpcAddFormationCommand, "" },
|
|
{ "item", rbac::RBAC_PERM_COMMAND_NPC_ADD_ITEM, false, &HandleNpcAddVendorItemCommand, "" },
|
|
{ "move", rbac::RBAC_PERM_COMMAND_NPC_ADD_MOVE, false, &HandleNpcAddMoveCommand, "" },
|
|
{ "temp", rbac::RBAC_PERM_COMMAND_NPC_ADD_TEMP, false, &HandleNpcAddTempSpawnCommand, "" },
|
|
//{ "weapon", rbac::RBAC_PERM_COMMAND_NPC_ADD_WEAPON, false, &HandleNpcAddWeaponCommand, "" },
|
|
{ "", rbac::RBAC_PERM_COMMAND_NPC_ADD, false, &HandleNpcAddCommand, "" },
|
|
};
|
|
static std::vector<ChatCommand> npcDeleteCommandTable =
|
|
{
|
|
{ "item", rbac::RBAC_PERM_COMMAND_NPC_DELETE_ITEM, false, &HandleNpcDeleteVendorItemCommand, "" },
|
|
{ "", rbac::RBAC_PERM_COMMAND_NPC_DELETE, false, &HandleNpcDeleteCommand, "" },
|
|
};
|
|
static std::vector<ChatCommand> npcFollowCommandTable =
|
|
{
|
|
{ "stop", rbac::RBAC_PERM_COMMAND_NPC_FOLLOW_STOP, false, &HandleNpcUnFollowCommand, "" },
|
|
{ "", rbac::RBAC_PERM_COMMAND_NPC_FOLLOW, false, &HandleNpcFollowCommand, "" },
|
|
};
|
|
static std::vector<ChatCommand> npcSetCommandTable =
|
|
{
|
|
{ "allowmove", rbac::RBAC_PERM_COMMAND_NPC_SET_ALLOWMOVE, false, &HandleNpcSetAllowMovementCommand, "" },
|
|
{ "entry", rbac::RBAC_PERM_COMMAND_NPC_SET_ENTRY, false, &HandleNpcSetEntryCommand, "" },
|
|
{ "factionid", rbac::RBAC_PERM_COMMAND_NPC_SET_FACTIONID, false, &HandleNpcSetFactionIdCommand, "" },
|
|
{ "flag", rbac::RBAC_PERM_COMMAND_NPC_SET_FLAG, false, &HandleNpcSetFlagCommand, "" },
|
|
{ "level", rbac::RBAC_PERM_COMMAND_NPC_SET_LEVEL, false, &HandleNpcSetLevelCommand, "" },
|
|
{ "link", rbac::RBAC_PERM_COMMAND_NPC_SET_LINK, false, &HandleNpcSetLinkCommand, "" },
|
|
{ "model", rbac::RBAC_PERM_COMMAND_NPC_SET_MODEL, false, &HandleNpcSetModelCommand, "" },
|
|
{ "movetype", rbac::RBAC_PERM_COMMAND_NPC_SET_MOVETYPE, false, &HandleNpcSetMoveTypeCommand, "" },
|
|
{ "phase", rbac::RBAC_PERM_COMMAND_NPC_SET_PHASE, false, &HandleNpcSetPhaseCommand, "" },
|
|
{ "phasegroup", rbac::RBAC_PERM_COMMAND_NPC_SET_PHASE, false, &HandleNpcSetPhaseGroup, "" },
|
|
{ "spawndist", rbac::RBAC_PERM_COMMAND_NPC_SET_SPAWNDIST, false, &HandleNpcSetSpawnDistCommand, "" },
|
|
{ "spawntime", rbac::RBAC_PERM_COMMAND_NPC_SET_SPAWNTIME, false, &HandleNpcSetSpawnTimeCommand, "" },
|
|
{ "data", rbac::RBAC_PERM_COMMAND_NPC_SET_DATA, false, &HandleNpcSetDataCommand, "" },
|
|
};
|
|
static std::vector<ChatCommand> npcCommandTable =
|
|
{
|
|
{ "info", rbac::RBAC_PERM_COMMAND_NPC_INFO, false, &HandleNpcInfoCommand, "" },
|
|
{ "near", rbac::RBAC_PERM_COMMAND_NPC_NEAR, false, &HandleNpcNearCommand, "" },
|
|
{ "move", rbac::RBAC_PERM_COMMAND_NPC_MOVE, false, &HandleNpcMoveCommand, "" },
|
|
{ "playemote", rbac::RBAC_PERM_COMMAND_NPC_PLAYEMOTE, false, &HandleNpcPlayEmoteCommand, "" },
|
|
{ "say", rbac::RBAC_PERM_COMMAND_NPC_SAY, false, &HandleNpcSayCommand, "" },
|
|
{ "textemote", rbac::RBAC_PERM_COMMAND_NPC_TEXTEMOTE, false, &HandleNpcTextEmoteCommand, "" },
|
|
{ "whisper", rbac::RBAC_PERM_COMMAND_NPC_WHISPER, false, &HandleNpcWhisperCommand, "" },
|
|
{ "yell", rbac::RBAC_PERM_COMMAND_NPC_YELL, false, &HandleNpcYellCommand, "" },
|
|
{ "tame", rbac::RBAC_PERM_COMMAND_NPC_TAME, false, &HandleNpcTameCommand, "" },
|
|
{ "spawngroup", rbac::RBAC_PERM_COMMAND_NPC_SPAWNGROUP, false, &HandleNpcSpawnGroup, "" },
|
|
{ "despawngroup", rbac::RBAC_PERM_COMMAND_NPC_DESPAWNGROUP, false, &HandleNpcDespawnGroup, "" },
|
|
{ "add", rbac::RBAC_PERM_COMMAND_NPC_ADD, false, nullptr, "", npcAddCommandTable },
|
|
{ "delete", rbac::RBAC_PERM_COMMAND_NPC_DELETE, false, nullptr, "", npcDeleteCommandTable },
|
|
{ "follow", rbac::RBAC_PERM_COMMAND_NPC_FOLLOW, false, nullptr, "", npcFollowCommandTable },
|
|
{ "set", rbac::RBAC_PERM_COMMAND_NPC_SET, false, nullptr, "", npcSetCommandTable },
|
|
{ "evade", rbac::RBAC_PERM_COMMAND_NPC_EVADE, false, &HandleNpcEvadeCommand, "" },
|
|
{ "showloot", rbac::RBAC_PERM_COMMAND_NPC_SHOWLOOT, false, &HandleNpcShowLootCommand, "" },
|
|
};
|
|
static std::vector<ChatCommand> commandTable =
|
|
{
|
|
{ "npc", rbac::RBAC_PERM_COMMAND_NPC, false, nullptr, "", npcCommandTable },
|
|
};
|
|
return commandTable;
|
|
}
|
|
|
|
//add spawn of creature
|
|
static bool HandleNpcAddCommand(ChatHandler* handler, char const* args)
|
|
{
|
|
if (!*args)
|
|
return false;
|
|
|
|
char* charID = handler->extractKeyFromLink((char*)args, "Hcreature_entry");
|
|
if (!charID)
|
|
return false;
|
|
|
|
uint32 id = atoul(charID);
|
|
if (!sObjectMgr->GetCreatureTemplate(id))
|
|
return false;
|
|
|
|
Player* chr = handler->GetSession()->GetPlayer();
|
|
Map* map = chr->GetMap();
|
|
|
|
if (Transport* trans = chr->GetTransport())
|
|
{
|
|
ObjectGuid::LowType guid = map->GenerateLowGuid<HighGuid::Creature>();
|
|
CreatureData& data = sObjectMgr->NewOrExistCreatureData(guid);
|
|
data.spawnId = guid;
|
|
data.id = id;
|
|
data.spawnPoint.Relocate(chr->GetTransOffsetX(), chr->GetTransOffsetY(), chr->GetTransOffsetZ(), chr->GetTransOffsetO());
|
|
/// @todo: add phases
|
|
|
|
Creature* creature = trans->CreateNPCPassenger(guid, &data);
|
|
|
|
creature->SaveToDB(trans->GetGOInfo()->moTransport.SpawnMap, { map->GetDifficultyID() });
|
|
|
|
sObjectMgr->AddCreatureToGrid(guid, &data);
|
|
return true;
|
|
}
|
|
|
|
Creature* creature = Creature::CreateCreature(id, map, chr->GetPosition());
|
|
if (!creature)
|
|
return false;
|
|
|
|
PhasingHandler::InheritPhaseShift(creature, chr);
|
|
creature->SaveToDB(map->GetId(), { map->GetDifficultyID() });
|
|
|
|
ObjectGuid::LowType db_guid = creature->GetSpawnId();
|
|
|
|
// To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells()
|
|
// current "creature" variable is deleted and created fresh new, otherwise old values might trigger asserts or cause undefined behavior
|
|
creature->CleanupsBeforeDelete();
|
|
delete creature;
|
|
|
|
creature = Creature::CreateCreatureFromDB(db_guid, map, true, true);
|
|
if (!creature)
|
|
return false;
|
|
|
|
sObjectMgr->AddCreatureToGrid(db_guid, sObjectMgr->GetCreatureData(db_guid));
|
|
return true;
|
|
}
|
|
|
|
//add item in vendorlist
|
|
static bool HandleNpcAddVendorItemCommand(ChatHandler* handler, char const* args)
|
|
{
|
|
if (!*args)
|
|
return false;
|
|
|
|
const uint8 type = 1; // FIXME: make type (1 item, 2 currency) an argument
|
|
|
|
char* pitem = handler->extractKeyFromLink((char*)args, "Hitem");
|
|
if (!pitem)
|
|
{
|
|
handler->SendSysMessage(LANG_COMMAND_NEEDITEMSEND);
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
uint32 itemId = atoull(pitem);
|
|
if (!itemId)
|
|
return false;
|
|
|
|
char* fmaxcount = strtok(nullptr, " "); //add maxcount, default: 0
|
|
uint32 maxcount = 0;
|
|
if (fmaxcount)
|
|
maxcount = atoul(fmaxcount);
|
|
|
|
char* fincrtime = strtok(nullptr, " "); //add incrtime, default: 0
|
|
uint32 incrtime = 0;
|
|
if (fincrtime)
|
|
incrtime = atoul(fincrtime);
|
|
|
|
char* fextendedcost = strtok(nullptr, " "); //add ExtendedCost, default: 0
|
|
uint32 extendedcost = fextendedcost ? atoul(fextendedcost) : 0;
|
|
char const* fbonuslist = strtok(nullptr, " ");
|
|
Creature* vendor = handler->getSelectedCreature();
|
|
if (!vendor)
|
|
{
|
|
handler->SendSysMessage(LANG_SELECT_CREATURE);
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
uint32 vendor_entry = vendor->GetEntry();
|
|
|
|
VendorItem vItem;
|
|
vItem.item = itemId;
|
|
vItem.maxcount = maxcount;
|
|
vItem.incrtime = incrtime;
|
|
vItem.ExtendedCost = extendedcost;
|
|
vItem.Type = type;
|
|
|
|
if (fbonuslist)
|
|
{
|
|
Tokenizer bonusListIDsTok(fbonuslist, ';');
|
|
for (char const* token : bonusListIDsTok)
|
|
vItem.BonusListIDs.push_back(int32(atol(token)));
|
|
}
|
|
|
|
if (!sObjectMgr->IsVendorItemValid(vendor_entry, vItem, handler->GetSession()->GetPlayer()))
|
|
{
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
sObjectMgr->AddVendorItem(vendor_entry, vItem);
|
|
|
|
ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(itemId);
|
|
|
|
handler->PSendSysMessage(LANG_ITEM_ADDED_TO_LIST, itemId, itemTemplate->GetDefaultLocaleName(), maxcount, incrtime, extendedcost);
|
|
return true;
|
|
}
|
|
|
|
//add move for creature
|
|
static bool HandleNpcAddMoveCommand(ChatHandler* handler, char const* args)
|
|
{
|
|
if (!*args)
|
|
return false;
|
|
|
|
char* guidStr = strtok((char*)args, " ");
|
|
char* waitStr = strtok((char*)nullptr, " ");
|
|
|
|
ObjectGuid::LowType lowGuid = atoull(guidStr);
|
|
|
|
// attempt check creature existence by DB data
|
|
CreatureData const* data = sObjectMgr->GetCreatureData(lowGuid);
|
|
if (!data)
|
|
{
|
|
handler->PSendSysMessage(LANG_COMMAND_CREATGUIDNOTFOUND, std::to_string(lowGuid).c_str());
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
int wait = waitStr ? atoi(waitStr) : 0;
|
|
|
|
if (wait < 0)
|
|
wait = 0;
|
|
|
|
// Update movement type
|
|
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_MOVEMENT_TYPE);
|
|
|
|
stmt->setUInt8(0, uint8(WAYPOINT_MOTION_TYPE));
|
|
stmt->setUInt64(1, lowGuid);
|
|
|
|
WorldDatabase.Execute(stmt);
|
|
|
|
handler->SendSysMessage(LANG_WAYPOINT_ADDED);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool HandleNpcSetAllowMovementCommand(ChatHandler* handler, char const* /*args*/)
|
|
{
|
|
if (sWorld->getAllowMovement())
|
|
{
|
|
sWorld->SetAllowMovement(false);
|
|
handler->SendSysMessage(LANG_CREATURE_MOVE_DISABLED);
|
|
}
|
|
else
|
|
{
|
|
sWorld->SetAllowMovement(true);
|
|
handler->SendSysMessage(LANG_CREATURE_MOVE_ENABLED);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool HandleNpcSetEntryCommand(ChatHandler* handler, char const* args)
|
|
{
|
|
if (!*args)
|
|
return false;
|
|
|
|
uint32 newEntryNum = atoul(args);
|
|
if (!newEntryNum)
|
|
return false;
|
|
|
|
Unit* unit = handler->getSelectedUnit();
|
|
if (!unit || unit->GetTypeId() != TYPEID_UNIT)
|
|
{
|
|
handler->SendSysMessage(LANG_SELECT_CREATURE);
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
Creature* creature = unit->ToCreature();
|
|
if (creature->UpdateEntry(newEntryNum))
|
|
handler->SendSysMessage(LANG_DONE);
|
|
else
|
|
handler->SendSysMessage(LANG_ERROR);
|
|
return true;
|
|
}
|
|
|
|
//change level of creature or pet
|
|
static bool HandleNpcSetLevelCommand(ChatHandler* handler, char const* args)
|
|
{
|
|
if (!*args)
|
|
return false;
|
|
|
|
uint8 lvl = (uint8) atoi(args);
|
|
if (lvl < 1 || lvl > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL) + 3)
|
|
{
|
|
handler->SendSysMessage(LANG_BAD_VALUE);
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
Creature* creature = handler->getSelectedCreature();
|
|
if (!creature || creature->IsPet())
|
|
{
|
|
handler->SendSysMessage(LANG_SELECT_CREATURE);
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
creature->SetMaxHealth(100 + 30*lvl);
|
|
creature->SetHealth(100 + 30*lvl);
|
|
creature->SetLevel(lvl);
|
|
creature->SaveToDB();
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool HandleNpcDeleteCommand(ChatHandler* handler, char const* args)
|
|
{
|
|
Creature* creature = nullptr;
|
|
|
|
if (*args)
|
|
{
|
|
// number or [name] Shift-click form |color|Hcreature:creature_guid|h[name]|h|r
|
|
char* cId = handler->extractKeyFromLink((char*)args, "Hcreature");
|
|
if (!cId)
|
|
return false;
|
|
|
|
ObjectGuid::LowType lowguid = atoull(cId);
|
|
if (!lowguid)
|
|
return false;
|
|
// force respawn to make sure we find something
|
|
handler->GetSession()->GetPlayer()->GetMap()->RemoveRespawnTime(SPAWN_TYPE_CREATURE, lowguid, true);
|
|
// then try to find it
|
|
creature = handler->GetCreatureFromPlayerMapByDbGuid(lowguid);
|
|
}
|
|
else
|
|
creature = handler->getSelectedCreature();
|
|
|
|
if (!creature || creature->IsPet() || creature->IsTotem())
|
|
{
|
|
handler->SendSysMessage(LANG_SELECT_CREATURE);
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
if (TempSummon* summon = creature->ToTempSummon())
|
|
summon->UnSummon();
|
|
else
|
|
{
|
|
// Delete the creature
|
|
creature->CombatStop();
|
|
creature->DeleteFromDB();
|
|
creature->AddObjectToRemoveList();
|
|
}
|
|
|
|
handler->SendSysMessage(LANG_COMMAND_DELCREATMESSAGE);
|
|
|
|
return true;
|
|
}
|
|
|
|
//del item from vendor list
|
|
static bool HandleNpcDeleteVendorItemCommand(ChatHandler* handler, char const* args)
|
|
{
|
|
if (!*args)
|
|
return false;
|
|
|
|
Creature* vendor = handler->getSelectedCreature();
|
|
if (!vendor || !vendor->IsVendor())
|
|
{
|
|
handler->SendSysMessage(LANG_COMMAND_VENDORSELECTION);
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
char* pitem = handler->extractKeyFromLink((char*)args, "Hitem");
|
|
if (!pitem)
|
|
{
|
|
handler->SendSysMessage(LANG_COMMAND_NEEDITEMSEND);
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
uint32 itemId = atoul(pitem);
|
|
|
|
const uint8 type = 1; // FIXME: make type (1 item, 2 currency) an argument
|
|
|
|
if (!sObjectMgr->RemoveVendorItem(vendor->GetEntry(), itemId, type))
|
|
{
|
|
handler->PSendSysMessage(LANG_ITEM_NOT_IN_LIST, itemId);
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(itemId);
|
|
|
|
handler->PSendSysMessage(LANG_ITEM_DELETED_FROM_LIST, itemId, itemTemplate->GetDefaultLocaleName());
|
|
return true;
|
|
}
|
|
|
|
//set faction of creature
|
|
static bool HandleNpcSetFactionIdCommand(ChatHandler* handler, char const* args)
|
|
{
|
|
if (!*args)
|
|
return false;
|
|
|
|
uint32 factionId = atoul(args);
|
|
|
|
if (!sFactionTemplateStore.LookupEntry(factionId))
|
|
{
|
|
handler->PSendSysMessage(LANG_WRONG_FACTION, factionId);
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
Creature* creature = handler->getSelectedCreature();
|
|
|
|
if (!creature)
|
|
{
|
|
handler->SendSysMessage(LANG_SELECT_CREATURE);
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
creature->SetFaction(factionId);
|
|
|
|
// Faction is set in creature_template - not inside creature
|
|
|
|
// Update in memory..
|
|
if (CreatureTemplate const* cinfo = creature->GetCreatureTemplate())
|
|
const_cast<CreatureTemplate*>(cinfo)->faction = factionId;
|
|
|
|
// ..and DB
|
|
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_FACTION);
|
|
|
|
stmt->setUInt16(0, uint16(factionId));
|
|
stmt->setUInt32(1, creature->GetEntry());
|
|
|
|
WorldDatabase.Execute(stmt);
|
|
|
|
return true;
|
|
}
|
|
|
|
//set npcflag of creature
|
|
static bool HandleNpcSetFlagCommand(ChatHandler* handler, char const* args)
|
|
{
|
|
if (!*args)
|
|
return false;
|
|
|
|
uint64 npcFlags = atoull(args);
|
|
|
|
Creature* creature = handler->getSelectedCreature();
|
|
|
|
if (!creature)
|
|
{
|
|
handler->SendSysMessage(LANG_SELECT_CREATURE);
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
uint32 raw[2];
|
|
memcpy(raw, &npcFlags, sizeof(raw));
|
|
creature->SetNpcFlags(NPCFlags(raw[0]));
|
|
creature->SetNpcFlags2(NPCFlags2(raw[1]));
|
|
|
|
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_NPCFLAG);
|
|
|
|
stmt->setUInt64(0, npcFlags);
|
|
stmt->setUInt32(1, creature->GetEntry());
|
|
|
|
WorldDatabase.Execute(stmt);
|
|
|
|
handler->SendSysMessage(LANG_VALUE_SAVED_REJOIN);
|
|
|
|
return true;
|
|
}
|
|
|
|
//set data of creature for testing scripting
|
|
static bool HandleNpcSetDataCommand(ChatHandler* handler, char const* args)
|
|
{
|
|
if (!*args)
|
|
return false;
|
|
|
|
char* arg1 = strtok((char*)args, " ");
|
|
char* arg2 = strtok((char*)nullptr, "");
|
|
|
|
if (!arg1 || !arg2)
|
|
return false;
|
|
|
|
uint32 data_1 = (uint32)atoi(arg1);
|
|
uint32 data_2 = (uint32)atoi(arg2);
|
|
|
|
if (!data_1 || !data_2)
|
|
return false;
|
|
|
|
Creature* creature = handler->getSelectedCreature();
|
|
|
|
if (!creature)
|
|
{
|
|
handler->SendSysMessage(LANG_SELECT_CREATURE);
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
creature->AI()->SetData(data_1, data_2);
|
|
std::string AIorScript = !creature->GetAIName().empty() ? "AI type: " + creature->GetAIName() : (!creature->GetScriptName().empty() ? "Script Name: " + creature->GetScriptName() : "No AI or Script Name Set");
|
|
handler->PSendSysMessage(LANG_NPC_SETDATA, creature->GetGUID().ToString().c_str(), creature->GetName().c_str(), data_1, data_2, AIorScript.c_str());
|
|
return true;
|
|
}
|
|
|
|
//npc follow handling
|
|
static bool HandleNpcFollowCommand(ChatHandler* handler, char const* /*args*/)
|
|
{
|
|
Player* player = handler->GetSession()->GetPlayer();
|
|
Creature* creature = handler->getSelectedCreature();
|
|
|
|
if (!creature)
|
|
{
|
|
handler->PSendSysMessage(LANG_SELECT_CREATURE);
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
// Follow player - Using pet's default dist and angle
|
|
creature->GetMotionMaster()->MoveFollow(player, PET_FOLLOW_DIST, creature->GetFollowAngle());
|
|
|
|
handler->PSendSysMessage(LANG_CREATURE_FOLLOW_YOU_NOW, creature->GetName().c_str());
|
|
return true;
|
|
}
|
|
|
|
static bool HandleNpcInfoCommand(ChatHandler* handler, char const* /*args*/)
|
|
{
|
|
Creature* target = handler->getSelectedCreature();
|
|
|
|
if (!target)
|
|
{
|
|
handler->SendSysMessage(LANG_SELECT_CREATURE);
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
CreatureTemplate const* cInfo = target->GetCreatureTemplate();
|
|
|
|
uint32 faction = target->GetFaction();
|
|
uint64 npcflags;
|
|
memcpy(&npcflags, target->m_unitData->NpcFlags.begin(), sizeof(npcflags));
|
|
uint32 mechanicImmuneMask = cInfo->MechanicImmuneMask;
|
|
uint32 displayid = target->GetDisplayId();
|
|
uint32 nativeid = target->GetNativeDisplayId();
|
|
uint32 Entry = target->GetEntry();
|
|
|
|
int64 curRespawnDelay = target->GetRespawnCompatibilityMode() ? target->GetRespawnTimeEx() - time(nullptr) : target->GetMap()->GetCreatureRespawnTime(target->GetSpawnId()) - time(nullptr);
|
|
|
|
if (curRespawnDelay < 0)
|
|
curRespawnDelay = 0;
|
|
std::string curRespawnDelayStr = secsToTimeString(uint64(curRespawnDelay), true);
|
|
std::string defRespawnDelayStr = secsToTimeString(target->GetRespawnDelay(), true);
|
|
|
|
handler->PSendSysMessage(LANG_NPCINFO_CHAR, std::to_string(target->GetSpawnId()).c_str(), target->GetGUID().ToString().c_str(), faction, std::to_string(npcflags).c_str(), Entry, displayid, nativeid);
|
|
if (target->GetCreatureData() && target->GetCreatureData()->spawnGroupData->groupId)
|
|
{
|
|
SpawnGroupTemplateData const* const groupData = target->GetCreatureData()->spawnGroupData;
|
|
handler->PSendSysMessage(LANG_SPAWNINFO_GROUP_ID, groupData->name.c_str(), groupData->groupId, groupData->flags, target->GetMap()->IsSpawnGroupActive(groupData->groupId));
|
|
}
|
|
handler->PSendSysMessage(LANG_SPAWNINFO_COMPATIBILITY_MODE, target->GetRespawnCompatibilityMode());
|
|
handler->PSendSysMessage(LANG_NPCINFO_LEVEL, target->getLevel());
|
|
handler->PSendSysMessage(LANG_NPCINFO_EQUIPMENT, target->GetCurrentEquipmentId(), target->GetOriginalEquipmentId());
|
|
handler->PSendSysMessage(LANG_NPCINFO_HEALTH, target->GetCreateHealth(), std::to_string(target->GetMaxHealth()).c_str(), std::to_string(target->GetHealth()).c_str());
|
|
handler->PSendSysMessage(LANG_NPCINFO_INHABIT_TYPE, cInfo->InhabitType);
|
|
|
|
handler->PSendSysMessage(LANG_NPCINFO_UNIT_FIELD_FLAGS, *target->m_unitData->Flags);
|
|
for (uint8 i = 0; i < MAX_UNIT_FLAGS; ++i)
|
|
if (target->HasUnitFlag(unitFlags[i].Value))
|
|
handler->PSendSysMessage("%s (0x%X)", unitFlags[i].Name, unitFlags[i].Value);
|
|
|
|
handler->PSendSysMessage(LANG_NPCINFO_UNIT_FIELD_FLAGS_2, *target->m_unitData->Flags2);
|
|
for (uint8 i = 0; i < MAX_UNIT_FLAGS_2; ++i)
|
|
if (target->HasUnitFlag2(unitFlags2[i].Value))
|
|
handler->PSendSysMessage("%s (0x%X)", unitFlags2[i].Name, unitFlags2[i].Value);
|
|
|
|
handler->PSendSysMessage(LANG_NPCINFO_UNIT_FIELD_FLAGS_3, *target->m_unitData->Flags3);
|
|
for (uint8 i = 0; i < MAX_UNIT_FLAGS_3; ++i)
|
|
if (target->HasUnitFlag3(unitFlags3[i].Value))
|
|
handler->PSendSysMessage("%s (0x%X)", unitFlags3[i].Name, unitFlags3[i].Value);
|
|
|
|
handler->PSendSysMessage(LANG_NPCINFO_DYNAMIC_FLAGS, target->GetDynamicFlags());
|
|
handler->PSendSysMessage(LANG_COMMAND_RAWPAWNTIMES, defRespawnDelayStr.c_str(), curRespawnDelayStr.c_str());
|
|
handler->PSendSysMessage(LANG_NPCINFO_LOOT, cInfo->lootid, cInfo->pickpocketLootId, cInfo->SkinLootId);
|
|
handler->PSendSysMessage(LANG_NPCINFO_DUNGEON_ID, target->GetInstanceId());
|
|
|
|
if (CreatureData const* data = sObjectMgr->GetCreatureData(target->GetSpawnId()))
|
|
{
|
|
handler->PSendSysMessage(LANG_NPCINFO_PHASES, data->phaseId, data->phaseGroup);
|
|
PhasingHandler::PrintToChat(handler, target->GetPhaseShift());
|
|
}
|
|
|
|
handler->PSendSysMessage(LANG_NPCINFO_ARMOR, target->GetArmor());
|
|
handler->PSendSysMessage(LANG_NPCINFO_POSITION, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ());
|
|
handler->PSendSysMessage(LANG_NPCINFO_AIINFO, target->GetAIName().c_str(), target->GetScriptName().c_str());
|
|
handler->PSendSysMessage(LANG_NPCINFO_FLAGS_EXTRA, cInfo->flags_extra);
|
|
for (uint8 i = 0; i < FLAGS_EXTRA_COUNT; ++i)
|
|
if (cInfo->flags_extra & flagsExtra[i].Value)
|
|
handler->PSendSysMessage("%s (0x%X)", flagsExtra[i].Name, flagsExtra[i].Value);
|
|
|
|
handler->PSendSysMessage(LANG_NPCINFO_NPC_FLAGS, npcflags);
|
|
for (uint8 i = 0; i < NPC_FLAG_COUNT; i++)
|
|
if (npcflags & npcFlagTexts[i].Value)
|
|
handler->PSendSysMessage(npcFlagTexts[i].Name, npcFlagTexts[i].Value);
|
|
|
|
handler->PSendSysMessage(LANG_NPCINFO_MECHANIC_IMMUNE, mechanicImmuneMask);
|
|
for (uint8 i = 1; i < MAX_MECHANIC; ++i)
|
|
if (mechanicImmuneMask & (1 << (mechanicImmunes[i].Value - 1)))
|
|
handler->PSendSysMessage("%s (0x%X)", mechanicImmunes[i].Name, mechanicImmunes[i].Value);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool HandleNpcNearCommand(ChatHandler* handler, char const* args)
|
|
{
|
|
float distance = (!*args) ? 10.0f : float((atof(args)));
|
|
uint32 count = 0;
|
|
|
|
Player* player = handler->GetSession()->GetPlayer();
|
|
|
|
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_CREATURE_NEAREST);
|
|
stmt->setFloat(0, player->GetPositionX());
|
|
stmt->setFloat(1, player->GetPositionY());
|
|
stmt->setFloat(2, player->GetPositionZ());
|
|
stmt->setUInt32(3, player->GetMapId());
|
|
stmt->setFloat(4, player->GetPositionX());
|
|
stmt->setFloat(5, player->GetPositionY());
|
|
stmt->setFloat(6, player->GetPositionZ());
|
|
stmt->setFloat(7, distance * distance);
|
|
PreparedQueryResult result = WorldDatabase.Query(stmt);
|
|
|
|
if (result)
|
|
{
|
|
do
|
|
{
|
|
Field* fields = result->Fetch();
|
|
ObjectGuid::LowType guid = fields[0].GetUInt64();
|
|
uint32 entry = fields[1].GetUInt32();
|
|
float x = fields[2].GetFloat();
|
|
float y = fields[3].GetFloat();
|
|
float z = fields[4].GetFloat();
|
|
uint16 mapId = fields[5].GetUInt16();
|
|
|
|
CreatureTemplate const* creatureTemplate = sObjectMgr->GetCreatureTemplate(entry);
|
|
if (!creatureTemplate)
|
|
continue;
|
|
|
|
handler->PSendSysMessage(LANG_CREATURE_LIST_CHAT, std::to_string(guid).c_str(), std::to_string(guid).c_str(), creatureTemplate->Name.c_str(), x, y, z, mapId, "", "");
|
|
|
|
++count;
|
|
}
|
|
while (result->NextRow());
|
|
}
|
|
|
|
handler->PSendSysMessage(LANG_COMMAND_NEAR_NPC_MESSAGE, distance, count);
|
|
|
|
return true;
|
|
}
|
|
|
|
//move selected creature
|
|
static bool HandleNpcMoveCommand(ChatHandler* handler, char const* args)
|
|
{
|
|
ObjectGuid::LowType lowguid = UI64LIT(0);
|
|
|
|
Creature* creature = handler->getSelectedCreature();
|
|
Player const* player = handler->GetSession()->GetPlayer();
|
|
if (!player)
|
|
return false;
|
|
|
|
if (creature)
|
|
lowguid = creature->GetSpawnId();
|
|
else
|
|
{
|
|
// number or [name] Shift-click form |color|Hcreature:creature_guid|h[name]|h|r
|
|
char* cId = handler->extractKeyFromLink((char*)args, "Hcreature");
|
|
if (!cId)
|
|
return false;
|
|
|
|
lowguid = atoull(cId);
|
|
}
|
|
// Attempting creature load from DB data
|
|
CreatureData const* data = sObjectMgr->GetCreatureData(lowguid);
|
|
if (!data)
|
|
{
|
|
handler->PSendSysMessage(LANG_COMMAND_CREATGUIDNOTFOUND, std::to_string(lowguid).c_str());
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
if (player->GetMapId() != data->spawnPoint.GetMapId())
|
|
{
|
|
handler->PSendSysMessage(LANG_COMMAND_CREATUREATSAMEMAP, std::to_string(lowguid).c_str());
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
// update position in memory
|
|
const_cast<CreatureData*>(data)->spawnPoint.Relocate(*player);
|
|
|
|
// update position in DB
|
|
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_POSITION);
|
|
stmt->setFloat(0, player->GetPositionX());
|
|
stmt->setFloat(1, player->GetPositionY());
|
|
stmt->setFloat(2, player->GetPositionZ());
|
|
stmt->setFloat(3, player->GetOrientation());
|
|
stmt->setUInt64(4, lowguid);
|
|
WorldDatabase.Execute(stmt);
|
|
|
|
// respawn selected creature at the new location
|
|
if (creature)
|
|
{
|
|
if (creature->IsAlive())
|
|
creature->setDeathState(JUST_DIED);
|
|
creature->Respawn(true);
|
|
if (!creature->GetRespawnCompatibilityMode())
|
|
creature->AddObjectToRemoveList();
|
|
}
|
|
|
|
handler->PSendSysMessage(LANG_COMMAND_CREATUREMOVED);
|
|
return true;
|
|
}
|
|
|
|
//play npc emote
|
|
static bool HandleNpcPlayEmoteCommand(ChatHandler* handler, char const* args)
|
|
{
|
|
uint32 emote = atoi((char*)args);
|
|
|
|
Creature* target = handler->getSelectedCreature();
|
|
if (!target)
|
|
{
|
|
handler->SendSysMessage(LANG_SELECT_CREATURE);
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
target->SetEmoteState(Emote(emote));
|
|
|
|
return true;
|
|
}
|
|
|
|
//set model of creature
|
|
static bool HandleNpcSetModelCommand(ChatHandler* handler, char const* args)
|
|
{
|
|
if (!*args)
|
|
return false;
|
|
|
|
uint32 displayId = atoul(args);
|
|
|
|
Creature* creature = handler->getSelectedCreature();
|
|
|
|
if (!creature || creature->IsPet())
|
|
{
|
|
handler->SendSysMessage(LANG_SELECT_CREATURE);
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
creature->SetDisplayId(displayId);
|
|
creature->SetNativeDisplayId(displayId);
|
|
|
|
creature->SaveToDB();
|
|
|
|
return true;
|
|
}
|
|
|
|
/**HandleNpcSetMoveTypeCommand
|
|
* Set the movement type for an NPC.<br/>
|
|
* <br/>
|
|
* Valid movement types are:
|
|
* <ul>
|
|
* <li> stay - NPC wont move </li>
|
|
* <li> random - NPC will move randomly according to the spawndist </li>
|
|
* <li> way - NPC will move with given waypoints set </li>
|
|
* </ul>
|
|
* additional parameter: NODEL - so no waypoints are deleted, if you
|
|
* change the movement type
|
|
*/
|
|
static bool HandleNpcSetMoveTypeCommand(ChatHandler* handler, char const* args)
|
|
{
|
|
if (!*args)
|
|
return false;
|
|
|
|
// 3 arguments:
|
|
// GUID (optional - you can also select the creature)
|
|
// stay|random|way (determines the kind of movement)
|
|
// NODEL (optional - tells the system NOT to delete any waypoints)
|
|
// this is very handy if you want to do waypoints, that are
|
|
// later switched on/off according to special events (like escort
|
|
// quests, etc)
|
|
char* guid_str = strtok((char*)args, " ");
|
|
char* type_str = strtok((char*)nullptr, " ");
|
|
char* dontdel_str = strtok((char*)nullptr, " ");
|
|
|
|
bool doNotDelete = false;
|
|
|
|
if (!guid_str)
|
|
return false;
|
|
|
|
ObjectGuid::LowType lowguid = UI64LIT(0);
|
|
Creature* creature = nullptr;
|
|
|
|
if (dontdel_str)
|
|
{
|
|
//TC_LOG_ERROR("misc", "DEBUG: All 3 params are set");
|
|
|
|
// All 3 params are set
|
|
// GUID
|
|
// type
|
|
// doNotDEL
|
|
if (stricmp(dontdel_str, "NODEL") == 0)
|
|
{
|
|
//TC_LOG_ERROR("misc", "DEBUG: doNotDelete = true;");
|
|
doNotDelete = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Only 2 params - but maybe NODEL is set
|
|
if (type_str)
|
|
{
|
|
TC_LOG_ERROR("misc", "DEBUG: Only 2 params ");
|
|
if (stricmp(type_str, "NODEL") == 0)
|
|
{
|
|
//TC_LOG_ERROR("misc", "DEBUG: type_str, NODEL ");
|
|
doNotDelete = true;
|
|
type_str = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!type_str) // case .setmovetype $move_type (with selected creature)
|
|
{
|
|
type_str = guid_str;
|
|
creature = handler->getSelectedCreature();
|
|
if (!creature || creature->IsPet())
|
|
return false;
|
|
lowguid = creature->GetSpawnId();
|
|
}
|
|
else // case .setmovetype #creature_guid $move_type (with selected creature)
|
|
{
|
|
lowguid = atoull(guid_str);
|
|
|
|
if (lowguid)
|
|
creature = handler->GetCreatureFromPlayerMapByDbGuid(lowguid);
|
|
|
|
// attempt check creature existence by DB data
|
|
if (!creature)
|
|
{
|
|
CreatureData const* data = sObjectMgr->GetCreatureData(lowguid);
|
|
if (!data)
|
|
{
|
|
handler->PSendSysMessage(LANG_COMMAND_CREATGUIDNOTFOUND, std::to_string(lowguid).c_str());
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lowguid = creature->GetSpawnId();
|
|
}
|
|
}
|
|
|
|
// now lowguid is low guid really existed creature
|
|
// and creature point (maybe) to this creature or nullptr
|
|
|
|
MovementGeneratorType move_type;
|
|
|
|
std::string type = type_str;
|
|
|
|
if (type == "stay")
|
|
move_type = IDLE_MOTION_TYPE;
|
|
else if (type == "random")
|
|
move_type = RANDOM_MOTION_TYPE;
|
|
else if (type == "way")
|
|
move_type = WAYPOINT_MOTION_TYPE;
|
|
else
|
|
return false;
|
|
|
|
// update movement type
|
|
//if (doNotDelete == false)
|
|
// WaypointMgr.DeletePath(lowguid);
|
|
|
|
if (creature)
|
|
{
|
|
// update movement type
|
|
if (doNotDelete == false)
|
|
creature->LoadPath(0);
|
|
|
|
creature->SetDefaultMovementType(move_type);
|
|
creature->GetMotionMaster()->Initialize();
|
|
if (creature->IsAlive()) // dead creature will reset movement generator at respawn
|
|
{
|
|
creature->setDeathState(JUST_DIED);
|
|
creature->Respawn();
|
|
}
|
|
creature->SaveToDB();
|
|
}
|
|
if (doNotDelete == false)
|
|
{
|
|
handler->PSendSysMessage(LANG_MOVE_TYPE_SET, type_str);
|
|
}
|
|
else
|
|
{
|
|
handler->PSendSysMessage(LANG_MOVE_TYPE_SET_NODEL, type_str);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//npc phase handling
|
|
//change phase of creature
|
|
static bool HandleNpcSetPhaseGroup(ChatHandler* handler, char const* args)
|
|
{
|
|
if (!*args)
|
|
return false;
|
|
|
|
int32 phaseGroupId = atoi(args);
|
|
|
|
Creature* creature = handler->getSelectedCreature();
|
|
if (!creature || creature->IsPet())
|
|
{
|
|
handler->SendSysMessage(LANG_SELECT_CREATURE);
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
PhasingHandler::ResetPhaseShift(creature);
|
|
PhasingHandler::AddPhaseGroup(creature, phaseGroupId, true);
|
|
creature->SetDBPhase(-phaseGroupId);
|
|
|
|
creature->SaveToDB();
|
|
|
|
return true;
|
|
}
|
|
|
|
//npc phase handling
|
|
//change phase of creature
|
|
static bool HandleNpcSetPhaseCommand(ChatHandler* handler, char const* args)
|
|
{
|
|
if (!*args)
|
|
return false;
|
|
|
|
uint32 phaseID = atoul(args);
|
|
if (!sPhaseStore.LookupEntry(phaseID))
|
|
{
|
|
handler->SendSysMessage(LANG_PHASE_NOTFOUND);
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
Creature* creature = handler->getSelectedCreature();
|
|
if (!creature || creature->IsPet())
|
|
{
|
|
handler->SendSysMessage(LANG_SELECT_CREATURE);
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
PhasingHandler::ResetPhaseShift(creature);
|
|
PhasingHandler::AddPhase(creature, phaseID, true);
|
|
creature->SetDBPhase(phaseID);
|
|
|
|
creature->SaveToDB();
|
|
|
|
return true;
|
|
}
|
|
|
|
//set spawn dist of creature
|
|
static bool HandleNpcSetSpawnDistCommand(ChatHandler* handler, char const* args)
|
|
{
|
|
if (!*args)
|
|
return false;
|
|
|
|
float option = (float)(atof((char*)args));
|
|
if (option < 0.0f)
|
|
{
|
|
handler->SendSysMessage(LANG_BAD_VALUE);
|
|
return false;
|
|
}
|
|
|
|
MovementGeneratorType mtype = IDLE_MOTION_TYPE;
|
|
if (option >0.0f)
|
|
mtype = RANDOM_MOTION_TYPE;
|
|
|
|
Creature* creature = handler->getSelectedCreature();
|
|
ObjectGuid::LowType guidLow = UI64LIT(0);
|
|
|
|
if (creature)
|
|
guidLow = creature->GetSpawnId();
|
|
else
|
|
return false;
|
|
|
|
creature->SetRespawnRadius(option);
|
|
creature->SetDefaultMovementType(mtype);
|
|
creature->GetMotionMaster()->Initialize();
|
|
if (creature->IsAlive()) // dead creature will reset movement generator at respawn
|
|
{
|
|
creature->setDeathState(JUST_DIED);
|
|
creature->Respawn();
|
|
}
|
|
|
|
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_SPAWN_DISTANCE);
|
|
|
|
stmt->setFloat(0, option);
|
|
stmt->setUInt8(1, uint8(mtype));
|
|
stmt->setUInt64(2, guidLow);
|
|
|
|
WorldDatabase.Execute(stmt);
|
|
|
|
handler->PSendSysMessage(LANG_COMMAND_SPAWNDIST, option);
|
|
return true;
|
|
}
|
|
|
|
//spawn time handling
|
|
static bool HandleNpcSetSpawnTimeCommand(ChatHandler* handler, char const* args)
|
|
{
|
|
if (!*args)
|
|
return false;
|
|
|
|
char* stime = strtok((char*)args, " ");
|
|
|
|
if (!stime)
|
|
return false;
|
|
|
|
uint32 spawnTime = atoul(stime);
|
|
|
|
Creature* creature = handler->getSelectedCreature();
|
|
if (!creature)
|
|
return false;
|
|
|
|
ObjectGuid::LowType guidLow = creature->GetSpawnId();
|
|
|
|
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_SPAWN_TIME_SECS);
|
|
stmt->setUInt32(0, spawnTime);
|
|
stmt->setUInt64(1, guidLow);
|
|
|
|
WorldDatabase.Execute(stmt);
|
|
|
|
creature->SetRespawnDelay(spawnTime);
|
|
handler->PSendSysMessage(LANG_COMMAND_SPAWNTIME, spawnTime);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool HandleNpcSayCommand(ChatHandler* handler, char const* args)
|
|
{
|
|
if (!*args)
|
|
return false;
|
|
|
|
Creature* creature = handler->getSelectedCreature();
|
|
if (!creature)
|
|
{
|
|
handler->SendSysMessage(LANG_SELECT_CREATURE);
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
creature->Say(args, LANG_UNIVERSAL);
|
|
|
|
// make some emotes
|
|
char lastchar = args[strlen(args) - 1];
|
|
switch (lastchar)
|
|
{
|
|
case '?': creature->HandleEmoteCommand(EMOTE_ONESHOT_QUESTION); break;
|
|
case '!': creature->HandleEmoteCommand(EMOTE_ONESHOT_EXCLAMATION); break;
|
|
default: creature->HandleEmoteCommand(EMOTE_ONESHOT_TALK); break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//show text emote by creature in chat
|
|
static bool HandleNpcTextEmoteCommand(ChatHandler* handler, char const* args)
|
|
{
|
|
if (!*args)
|
|
return false;
|
|
|
|
Creature* creature = handler->getSelectedCreature();
|
|
|
|
if (!creature)
|
|
{
|
|
handler->SendSysMessage(LANG_SELECT_CREATURE);
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
creature->TextEmote(args);
|
|
|
|
return true;
|
|
}
|
|
|
|
//npc unfollow handling
|
|
static bool HandleNpcUnFollowCommand(ChatHandler* handler, char const* /*args*/)
|
|
{
|
|
Player* player = handler->GetSession()->GetPlayer();
|
|
Creature* creature = handler->getSelectedCreature();
|
|
|
|
if (!creature)
|
|
{
|
|
handler->PSendSysMessage(LANG_SELECT_CREATURE);
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
if (/*creature->GetMotionMaster()->empty() ||*/
|
|
creature->GetMotionMaster()->GetCurrentMovementGeneratorType() != FOLLOW_MOTION_TYPE)
|
|
{
|
|
handler->PSendSysMessage(LANG_CREATURE_NOT_FOLLOW_YOU, creature->GetName().c_str());
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
FollowMovementGenerator<Creature> const* mgen = static_cast<FollowMovementGenerator<Creature> const*>((creature->GetMotionMaster()->top()));
|
|
|
|
if (mgen->GetTarget() != player)
|
|
{
|
|
handler->PSendSysMessage(LANG_CREATURE_NOT_FOLLOW_YOU, creature->GetName().c_str());
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
// reset movement
|
|
creature->GetMotionMaster()->MovementExpired(true);
|
|
|
|
handler->PSendSysMessage(LANG_CREATURE_NOT_FOLLOW_YOU_NOW, creature->GetName().c_str());
|
|
return true;
|
|
}
|
|
|
|
// make npc whisper to player
|
|
static bool HandleNpcWhisperCommand(ChatHandler* handler, char const* args)
|
|
{
|
|
if (!*args)
|
|
{
|
|
handler->SendSysMessage(LANG_CMD_SYNTAX);
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
char* receiver_str = strtok((char*)args, " ");
|
|
char* text = strtok(nullptr, "");
|
|
|
|
if (!receiver_str || !text)
|
|
{
|
|
handler->SendSysMessage(LANG_CMD_SYNTAX);
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
Creature* creature = handler->getSelectedCreature();
|
|
if (!creature)
|
|
{
|
|
handler->SendSysMessage(LANG_SELECT_CREATURE);
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
ObjectGuid receiver_guid = ObjectGuid::Create<HighGuid::Player>(strtoull(receiver_str, nullptr, 10));
|
|
|
|
// check online security
|
|
Player* receiver = ObjectAccessor::FindPlayer(receiver_guid);
|
|
if (handler->HasLowerSecurity(receiver, ObjectGuid::Empty))
|
|
return false;
|
|
|
|
creature->Whisper(text, LANG_UNIVERSAL, receiver);
|
|
return true;
|
|
}
|
|
|
|
static bool HandleNpcYellCommand(ChatHandler* handler, char const* args)
|
|
{
|
|
if (!*args)
|
|
{
|
|
handler->SendSysMessage(LANG_CMD_SYNTAX);
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
Creature* creature = handler->getSelectedCreature();
|
|
if (!creature)
|
|
{
|
|
handler->SendSysMessage(LANG_SELECT_CREATURE);
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
creature->Yell(args, LANG_UNIVERSAL);
|
|
|
|
// make an emote
|
|
creature->HandleEmoteCommand(EMOTE_ONESHOT_SHOUT);
|
|
|
|
return true;
|
|
}
|
|
|
|
// add creature, temp only
|
|
static bool HandleNpcAddTempSpawnCommand(ChatHandler* handler, char const* args)
|
|
{
|
|
if (!*args)
|
|
return false;
|
|
|
|
bool loot = false;
|
|
char const* spawntype_str = strtok((char*)args, " ");
|
|
char const* entry_str = strtok(nullptr, "");
|
|
if (stricmp(spawntype_str, "LOOT") == 0)
|
|
loot = true;
|
|
else if (stricmp(spawntype_str, "NOLOOT") == 0)
|
|
loot = false;
|
|
else
|
|
entry_str = args;
|
|
char* charID = handler->extractKeyFromLink((char*)entry_str, "Hcreature_entry");
|
|
if (!charID)
|
|
return false;
|
|
|
|
Player* chr = handler->GetSession()->GetPlayer();
|
|
|
|
uint32 id = atoul(charID);
|
|
if (!id)
|
|
return false;
|
|
|
|
if (!sObjectMgr->GetCreatureTemplate(id))
|
|
return false;
|
|
|
|
chr->SummonCreature(id, *chr, loot ? TEMPSUMMON_CORPSE_TIMED_DESPAWN : TEMPSUMMON_CORPSE_DESPAWN, 30 * IN_MILLISECONDS);
|
|
|
|
return true;
|
|
}
|
|
|
|
//npc tame handling
|
|
static bool HandleNpcTameCommand(ChatHandler* handler, char const* /*args*/)
|
|
{
|
|
Creature* creatureTarget = handler->getSelectedCreature();
|
|
if (!creatureTarget || creatureTarget->IsPet())
|
|
{
|
|
handler->PSendSysMessage (LANG_SELECT_CREATURE);
|
|
handler->SetSentErrorMessage (true);
|
|
return false;
|
|
}
|
|
|
|
Player* player = handler->GetSession()->GetPlayer();
|
|
|
|
if (!player->GetPetGUID().IsEmpty())
|
|
{
|
|
handler->SendSysMessage (LANG_YOU_ALREADY_HAVE_PET);
|
|
handler->SetSentErrorMessage (true);
|
|
return false;
|
|
}
|
|
|
|
CreatureTemplate const* cInfo = creatureTarget->GetCreatureTemplate();
|
|
|
|
if (!cInfo->IsTameable (player->CanTameExoticPets()))
|
|
{
|
|
handler->PSendSysMessage (LANG_CREATURE_NON_TAMEABLE, cInfo->Entry);
|
|
handler->SetSentErrorMessage (true);
|
|
return false;
|
|
}
|
|
|
|
// Everything looks OK, create new pet
|
|
Pet* pet = player->CreateTamedPetFrom(creatureTarget);
|
|
if (!pet)
|
|
{
|
|
handler->PSendSysMessage (LANG_CREATURE_NON_TAMEABLE, cInfo->Entry);
|
|
handler->SetSentErrorMessage (true);
|
|
return false;
|
|
}
|
|
|
|
// place pet before player
|
|
float x, y, z;
|
|
player->GetClosePoint (x, y, z, creatureTarget->GetCombatReach(), CONTACT_DISTANCE);
|
|
pet->Relocate(x, y, z, float(M_PI) - player->GetOrientation());
|
|
|
|
// set pet to defensive mode by default (some classes can't control controlled pets in fact).
|
|
pet->SetReactState(REACT_DEFENSIVE);
|
|
|
|
// calculate proper level
|
|
uint8 level = std::max<uint8>(player->getLevel()-5, creatureTarget->getLevel());
|
|
|
|
// prepare visual effect for levelup
|
|
pet->SetLevel(level - 1);
|
|
|
|
// add to world
|
|
pet->GetMap()->AddToMap(pet->ToCreature());
|
|
|
|
// visual effect for levelup
|
|
pet->SetLevel(level);
|
|
|
|
// caster have pet now
|
|
player->SetMinion(pet, true);
|
|
|
|
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
|
|
player->PetSpellInitialize();
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool HandleNpcEvadeCommand(ChatHandler* handler, char const* args)
|
|
{
|
|
Creature* creatureTarget = handler->getSelectedCreature();
|
|
if (!creatureTarget || creatureTarget->IsPet())
|
|
{
|
|
handler->PSendSysMessage(LANG_SELECT_CREATURE);
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
if (!creatureTarget->IsAIEnabled)
|
|
{
|
|
handler->PSendSysMessage(LANG_CREATURE_NOT_AI_ENABLED);
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
char* type_str = args ? strtok((char*)args, " ") : nullptr;
|
|
char* force_str = args ? strtok(nullptr, " ") : nullptr;
|
|
|
|
CreatureAI::EvadeReason why = CreatureAI::EVADE_REASON_OTHER;
|
|
bool force = false;
|
|
if (type_str)
|
|
{
|
|
if (stricmp(type_str, "NO_HOSTILES") == 0 || stricmp(type_str, "EVADE_REASON_NO_HOSTILES") == 0)
|
|
why = CreatureAI::EVADE_REASON_NO_HOSTILES;
|
|
else if (stricmp(type_str, "BOUNDARY") == 0 || stricmp(type_str, "EVADE_REASON_BOUNDARY") == 0)
|
|
why = CreatureAI::EVADE_REASON_BOUNDARY;
|
|
else if (stricmp(type_str, "SEQUENCE_BREAK") == 0 || stricmp(type_str, "EVADE_REASON_SEQUENCE_BREAK") == 0)
|
|
why = CreatureAI::EVADE_REASON_SEQUENCE_BREAK;
|
|
else if (stricmp(type_str, "FORCE") == 0)
|
|
force = true;
|
|
|
|
if (!force && force_str)
|
|
if (stricmp(force_str, "FORCE") == 0)
|
|
force = true;
|
|
}
|
|
|
|
if (force)
|
|
creatureTarget->ClearUnitState(UNIT_STATE_EVADE);
|
|
creatureTarget->AI()->EnterEvadeMode(why);
|
|
|
|
return true;
|
|
}
|
|
|
|
static void _ShowLootEntry(ChatHandler* handler, uint32 itemId, uint8 itemCount, bool alternateString = false)
|
|
{
|
|
ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(itemId);
|
|
char const* name = nullptr;
|
|
if (!itemTemplate)
|
|
name = itemTemplate->GetName(handler->GetSessionDbcLocale());
|
|
if (!name)
|
|
name = "Unknown item";
|
|
handler->PSendSysMessage(alternateString ? LANG_COMMAND_NPC_SHOWLOOT_ENTRY_2 : LANG_COMMAND_NPC_SHOWLOOT_ENTRY,
|
|
itemCount, ItemQualityColors[itemTemplate ? static_cast<ItemQualities>(itemTemplate->GetQuality()) : ITEM_QUALITY_POOR], itemId, name, itemId);
|
|
}
|
|
static void _IterateNotNormalLootMap(ChatHandler* handler, NotNormalLootItemMap const& map, std::vector<LootItem> const& items)
|
|
{
|
|
for (NotNormalLootItemMap::value_type const& pair : map)
|
|
{
|
|
if (!pair.second)
|
|
continue;
|
|
Player const* player = ObjectAccessor::FindConnectedPlayer(pair.first);
|
|
handler->PSendSysMessage(LANG_COMMAND_NPC_SHOWLOOT_SUBLABEL, player ? player->GetName() : Trinity::StringFormat("Offline player (GUID %s)", pair.first.ToString()).c_str(), pair.second->size());
|
|
|
|
for (auto it = pair.second->cbegin(); it != pair.second->cend(); ++it)
|
|
{
|
|
LootItem const& item = items[it->index];
|
|
if (!(it->is_looted) && !item.is_looted)
|
|
_ShowLootEntry(handler, item.itemid, item.count, true);
|
|
}
|
|
}
|
|
}
|
|
static bool HandleNpcShowLootCommand(ChatHandler* handler, char const* args)
|
|
{
|
|
Creature* creatureTarget = handler->getSelectedCreature();
|
|
if (!creatureTarget || creatureTarget->IsPet())
|
|
{
|
|
handler->PSendSysMessage(LANG_SELECT_CREATURE);
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
Loot const& loot = creatureTarget->loot;
|
|
if (!creatureTarget->isDead() || loot.empty())
|
|
{
|
|
handler->PSendSysMessage(LANG_COMMAND_NOT_DEAD_OR_NO_LOOT, creatureTarget->GetName());
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
handler->PSendSysMessage(LANG_COMMAND_NPC_SHOWLOOT_HEADER, creatureTarget->GetName(), creatureTarget->GetEntry());
|
|
handler->PSendSysMessage(LANG_COMMAND_NPC_SHOWLOOT_MONEY, loot.gold / GOLD, (loot.gold%GOLD) / SILVER, loot.gold%SILVER);
|
|
|
|
if (strcmp(args, "all")) // nonzero from strcmp <-> not equal
|
|
{
|
|
handler->PSendSysMessage(LANG_COMMAND_NPC_SHOWLOOT_LABEL, "Standard items", loot.items.size());
|
|
for (LootItem const& item : loot.items)
|
|
if (!item.is_looted)
|
|
_ShowLootEntry(handler, item.itemid, item.count);
|
|
|
|
handler->PSendSysMessage(LANG_COMMAND_NPC_SHOWLOOT_LABEL, "Quest items", loot.quest_items.size());
|
|
for (LootItem const& item : loot.quest_items)
|
|
if (!item.is_looted)
|
|
_ShowLootEntry(handler, item.itemid, item.count);
|
|
}
|
|
else
|
|
{
|
|
handler->PSendSysMessage(LANG_COMMAND_NPC_SHOWLOOT_LABEL, "Standard items", loot.items.size());
|
|
for (LootItem const& item : loot.items)
|
|
if (!item.is_looted && !item.freeforall && item.conditions.empty())
|
|
_ShowLootEntry(handler, item.itemid, item.count);
|
|
|
|
if (!loot.GetPlayerQuestItems().empty())
|
|
{
|
|
handler->PSendSysMessage(LANG_COMMAND_NPC_SHOWLOOT_LABEL_2, "Per-player quest items");
|
|
_IterateNotNormalLootMap(handler, loot.GetPlayerQuestItems(), loot.quest_items);
|
|
}
|
|
|
|
if (!loot.GetPlayerFFAItems().empty())
|
|
{
|
|
handler->PSendSysMessage(LANG_COMMAND_NPC_SHOWLOOT_LABEL_2, "FFA items per allowed player");
|
|
_IterateNotNormalLootMap(handler, loot.GetPlayerFFAItems(), loot.items);
|
|
}
|
|
|
|
if (!loot.GetPlayerNonQuestNonFFAConditionalItems().empty())
|
|
{
|
|
handler->PSendSysMessage(LANG_COMMAND_NPC_SHOWLOOT_LABEL_2, "Per-player conditional items");
|
|
_IterateNotNormalLootMap(handler, loot.GetPlayerNonQuestNonFFAConditionalItems(), loot.items);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool HandleNpcAddFormationCommand(ChatHandler* handler, char const* args)
|
|
{
|
|
if (!*args)
|
|
return false;
|
|
|
|
ObjectGuid::LowType leaderGUID = atoull(args);
|
|
Creature* creature = handler->getSelectedCreature();
|
|
|
|
if (!creature || !creature->GetSpawnId())
|
|
{
|
|
handler->SendSysMessage(LANG_SELECT_CREATURE);
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
ObjectGuid::LowType lowguid = creature->GetSpawnId();
|
|
if (creature->GetFormation())
|
|
{
|
|
handler->PSendSysMessage("Selected creature is already member of group " UI64FMTD, creature->GetFormation()->GetId());
|
|
return false;
|
|
}
|
|
|
|
if (!lowguid)
|
|
return false;
|
|
|
|
Player* chr = handler->GetSession()->GetPlayer();
|
|
FormationInfo* group_member;
|
|
|
|
group_member = new FormationInfo;
|
|
group_member->follow_angle = (creature->GetAngle(chr) - chr->GetOrientation()) * 180 / float(M_PI);
|
|
group_member->follow_dist = std::sqrt(std::pow(chr->GetPositionX() - creature->GetPositionX(), 2.f) + std::pow(chr->GetPositionY() - creature->GetPositionY(), 2.f));
|
|
group_member->leaderGUID = leaderGUID;
|
|
group_member->groupAI = 0;
|
|
|
|
sFormationMgr->CreatureGroupMap[lowguid] = group_member;
|
|
creature->SearchFormation();
|
|
|
|
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_INS_CREATURE_FORMATION);
|
|
|
|
stmt->setUInt64(0, leaderGUID);
|
|
stmt->setUInt64(1, lowguid);
|
|
stmt->setFloat(2, group_member->follow_dist);
|
|
stmt->setFloat(3, group_member->follow_angle);
|
|
stmt->setUInt32(4, uint32(group_member->groupAI));
|
|
|
|
WorldDatabase.Execute(stmt);
|
|
|
|
handler->PSendSysMessage("Creature " UI64FMTD " added to formation with leader " UI64FMTD, lowguid, leaderGUID);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool HandleNpcSetLinkCommand(ChatHandler* handler, char const* args)
|
|
{
|
|
if (!*args)
|
|
return false;
|
|
|
|
ObjectGuid::LowType linkguid = atoull(args);
|
|
|
|
Creature* creature = handler->getSelectedCreature();
|
|
|
|
if (!creature)
|
|
{
|
|
handler->SendSysMessage(LANG_SELECT_CREATURE);
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
if (!creature->GetSpawnId())
|
|
{
|
|
handler->PSendSysMessage("Selected %s isn't in creature table", creature->GetGUID().ToString().c_str());
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
if (!sObjectMgr->SetCreatureLinkedRespawn(creature->GetSpawnId(), linkguid))
|
|
{
|
|
handler->PSendSysMessage("Selected creature can't link with guid '" UI64FMTD "'", linkguid);
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
handler->PSendSysMessage("LinkGUID '" UI64FMTD "' added to creature with DBTableGUID: '" UI64FMTD "'", linkguid, creature->GetSpawnId());
|
|
return true;
|
|
}
|
|
|
|
/// @todo NpcCommands that need to be fixed :
|
|
static bool HandleNpcAddWeaponCommand(ChatHandler* /*handler*/, char const* /*args*/)
|
|
{
|
|
/*if (!*args)
|
|
return false;
|
|
|
|
uint64 guid = handler->GetSession()->GetPlayer()->GetSelection();
|
|
if (guid == 0)
|
|
{
|
|
handler->SendSysMessage(LANG_NO_SELECTION);
|
|
return true;
|
|
}
|
|
|
|
Creature* creature = ObjectAccessor::GetCreature(*handler->GetSession()->GetPlayer(), guid);
|
|
|
|
if (!creature)
|
|
{
|
|
handler->SendSysMessage(LANG_SELECT_CREATURE);
|
|
return true;
|
|
}
|
|
|
|
char* pSlotID = strtok((char*)args, " ");
|
|
if (!pSlotID)
|
|
return false;
|
|
|
|
char* pItemID = strtok(nullptr, " ");
|
|
if (!pItemID)
|
|
return false;
|
|
|
|
uint32 ItemID = atoi(pItemID);
|
|
uint32 SlotID = atoi(pSlotID);
|
|
|
|
ItemTemplate* tmpItem = sObjectMgr->GetItemTemplate(ItemID);
|
|
|
|
bool added = false;
|
|
if (tmpItem)
|
|
{
|
|
switch (SlotID)
|
|
{
|
|
case 1:
|
|
creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY, ItemID);
|
|
added = true;
|
|
break;
|
|
case 2:
|
|
creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY_01, ItemID);
|
|
added = true;
|
|
break;
|
|
case 3:
|
|
creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY_02, ItemID);
|
|
added = true;
|
|
break;
|
|
default:
|
|
handler->PSendSysMessage(LANG_ITEM_SLOT_NOT_EXIST, SlotID);
|
|
added = false;
|
|
break;
|
|
}
|
|
|
|
if (added)
|
|
handler->PSendSysMessage(LANG_ITEM_ADDED_TO_SLOT, ItemID, tmpItem->Name1, SlotID);
|
|
}
|
|
else
|
|
{
|
|
handler->PSendSysMessage(LANG_ITEM_NOT_FOUND, ItemID);
|
|
return true;
|
|
}
|
|
*/
|
|
return true;
|
|
}
|
|
};
|
|
|
|
void AddSC_npc_commandscript()
|
|
{
|
|
new npc_commandscript();
|
|
}
|