/* * This file is part of the AzerothCore 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 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 General Public License along * with this program. If not, see . */ #include "Chat.h" #include "CommandScript.h" #include "CreatureAI.h" #include "CreatureGroups.h" #include "GameTime.h" #include "Language.h" #include "ObjectMgr.h" #include "Pet.h" #include "Player.h" #include "TargetedMovementGenerator.h" // for HandleNpcUnFollowCommand #include "Transport.h" #include using namespace Acore::ChatCommands; using CreatureSpawnId = Variant, ObjectGuid::LowType>; using CreatureEntry = Variant, uint32>; struct NpcFlagText { uint32 flag; int32 text; }; #define NPCFLAG_COUNT 24 NpcFlagText const npcFlagTexts[NPCFLAG_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 } }; struct MechanicImmune { uint32 flag; char const* text; }; MechanicImmune const mechanicImmunes[MAX_MECHANIC] = { { MECHANIC_NONE, "MECHANIC_NONE" }, { MECHANIC_CHARM, "MECHANIC_CHARM" }, { MECHANIC_DISORIENTED, "MECHANIC_DISORIENTED" }, { MECHANIC_DISARM, "MECHANIC_DISARM" }, { MECHANIC_DISTRACT, "MECHANIC_DISTRACT" }, { MECHANIC_FEAR, "MECHANIC_FEAR" }, { MECHANIC_GRIP, "MECHANIC_GRIP" }, { MECHANIC_ROOT, "MECHANIC_ROOT" }, { MECHANIC_SLOW_ATTACK, "MECHANIC_SLOW_ATTACK" }, { MECHANIC_SILENCE, "MECHANIC_SILENCE" }, { MECHANIC_SLEEP, "MECHANIC_SLEEP" }, { MECHANIC_SNARE, "MECHANIC_SNARE" }, { MECHANIC_STUN, "MECHANIC_STUN" }, { MECHANIC_FREEZE, "MECHANIC_FREEZE" }, { MECHANIC_KNOCKOUT, "MECHANIC_KNOCKOUT" }, { MECHANIC_BLEED, "MECHANIC_BLEED" }, { MECHANIC_BANDAGE, "MECHANIC_BANDAGE" }, { MECHANIC_POLYMORPH, "MECHANIC_POLYMORPH" }, { MECHANIC_BANISH, "MECHANIC_BANISH" }, { MECHANIC_SHIELD, "MECHANIC_SHIELD" }, { MECHANIC_SHACKLE, "MECHANIC_SHACKLE" }, { MECHANIC_MOUNT, "MECHANIC_MOUNT" }, { MECHANIC_INFECTED, "MECHANIC_INFECTED" }, { MECHANIC_TURN, "MECHANIC_TURN" }, { MECHANIC_HORROR, "MECHANIC_HORROR" }, { MECHANIC_INVULNERABILITY, "MECHANIC_INVULNERABILITY" }, { MECHANIC_INTERRUPT, "MECHANIC_INTERRUPT" }, { MECHANIC_DAZE, "MECHANIC_DAZE" }, { MECHANIC_DISCOVERY, "MECHANIC_DISCOVERY" }, { MECHANIC_IMMUNE_SHIELD, "MECHANIC_IMMUNE_SHIELD" }, { MECHANIC_SAPPED, "MECHANIC_SAPPED" }, { MECHANIC_ENRAGED, "MECHANIC_ENRAGED" }, }; struct SpellSchoolImmune { uint32 flag; char const* text; }; SpellSchoolImmune const spellSchoolImmunes[MAX_SPELL_SCHOOL] = { { SPELL_SCHOOL_NORMAL, "SPELL_SCHOOL_NORMAL" }, { SPELL_SCHOOL_HOLY, "SPELL_SCHOOL_HOLY" }, { SPELL_SCHOOL_FIRE, "SPELL_SCHOOL_FIRE" }, { SPELL_SCHOOL_NATURE, "SPELL_SCHOOL_NATURE" }, { SPELL_SCHOOL_FROST, "SPELL_SCHOOL_FROST" }, { SPELL_SCHOOL_SHADOW, "SPELL_SCHOOL_SHADOW" }, { SPELL_SCHOOL_ARCANE, "SPELL_SCHOOL_ARCANE" }, }; class npc_commandscript : public CommandScript { public: npc_commandscript() : CommandScript("npc_commandscript") { } ChatCommandTable GetCommands() const override { static ChatCommandTable npcAddCommandTable = { { "formation", HandleNpcAddFormationCommand, SEC_ADMINISTRATOR, Console::No }, { "item", HandleNpcAddVendorItemCommand, SEC_ADMINISTRATOR, Console::No }, { "move", HandleNpcAddMoveCommand, SEC_ADMINISTRATOR, Console::No }, { "temp", HandleNpcAddTempSpawnCommand, SEC_ADMINISTRATOR, Console::No }, { "", HandleNpcAddCommand, SEC_ADMINISTRATOR, Console::No } }; static ChatCommandTable npcDeleteCommandTable = { { "item", HandleNpcDeleteVendorItemCommand, SEC_ADMINISTRATOR, Console::No }, { "", HandleNpcDeleteCommand, SEC_ADMINISTRATOR, Console::No } }; static ChatCommandTable npcFollowCommandTable = { { "stop", HandleNpcUnFollowCommand, SEC_GAMEMASTER, Console::No }, { "", HandleNpcFollowCommand, SEC_GAMEMASTER, Console::No } }; static ChatCommandTable npcFactionCommandTable = { { "permanent", HandleNpcSetFactionIdCommand, SEC_ADMINISTRATOR, Console::No }, { "temp", HandleNpcSetFactionTempIdCommand, SEC_ADMINISTRATOR, Console::No }, { "original", HandleNpcSetOriginalFaction, SEC_ADMINISTRATOR, Console::No } }; static ChatCommandTable npcSetCommandTable = { { "allowmove", HandleNpcSetAllowMovementCommand, SEC_ADMINISTRATOR, Console::No }, { "entry", HandleNpcSetEntryCommand, SEC_ADMINISTRATOR, Console::No }, { "faction", npcFactionCommandTable}, { "flag", HandleNpcSetFlagCommand, SEC_ADMINISTRATOR, Console::No }, { "level", HandleNpcSetLevelCommand, SEC_ADMINISTRATOR, Console::No }, { "link", HandleNpcSetLinkCommand, SEC_ADMINISTRATOR, Console::No }, { "model", HandleNpcSetModelCommand, SEC_ADMINISTRATOR, Console::No }, { "movetype", HandleNpcSetMoveTypeCommand, SEC_ADMINISTRATOR, Console::No }, { "phase", HandleNpcSetPhaseCommand, SEC_ADMINISTRATOR, Console::No }, { "wanderdistance", HandleNpcSetWanderDistanceCommand, SEC_ADMINISTRATOR, Console::No }, { "spawntime", HandleNpcSetSpawnTimeCommand, SEC_ADMINISTRATOR, Console::No }, { "data", HandleNpcSetDataCommand, SEC_ADMINISTRATOR, Console::No } }; static ChatCommandTable npcCommandTable = { { "info", HandleNpcInfoCommand, SEC_GAMEMASTER, Console::No }, { "guid", HandleNpcGuidCommand, SEC_GAMEMASTER, Console::No }, { "near", HandleNpcNearCommand, SEC_GAMEMASTER, Console::No }, { "move", HandleNpcMoveCommand, SEC_GAMEMASTER, Console::No }, { "playemote", HandleNpcPlayEmoteCommand, SEC_GAMEMASTER, Console::No }, { "say", HandleNpcSayCommand, SEC_GAMEMASTER, Console::No }, { "textemote", HandleNpcTextEmoteCommand, SEC_GAMEMASTER, Console::No }, { "whisper", HandleNpcWhisperCommand, SEC_GAMEMASTER, Console::No }, { "yell", HandleNpcYellCommand, SEC_GAMEMASTER, Console::No }, { "tame", HandleNpcTameCommand, SEC_GAMEMASTER, Console::No }, { "add", npcAddCommandTable }, { "delete", npcDeleteCommandTable }, { "follow", npcFollowCommandTable }, { "set", npcSetCommandTable } }; static ChatCommandTable commandTable = { { "npc", npcCommandTable } }; return commandTable; } //add spawn of creature static bool HandleNpcAddCommand(ChatHandler* handler, CreatureEntry id) { if (!sObjectMgr->GetCreatureTemplate(id)) return false; Player* chr = handler->GetSession()->GetPlayer(); float x = chr->GetPositionX(); float y = chr->GetPositionY(); float z = chr->GetPositionZ(); float o = chr->GetOrientation(); Map* map = chr->GetMap(); if (Transport* tt = chr->GetTransport()) if (MotionTransport* trans = tt->ToMotionTransport()) { ObjectGuid::LowType guid = sObjectMgr->GenerateCreatureSpawnId(); CreatureData& data = sObjectMgr->NewOrExistCreatureData(guid); data.id1 = id; data.phaseMask = chr->GetPhaseMaskForSpawn(); data.posX = chr->GetTransOffsetX(); data.posY = chr->GetTransOffsetY(); data.posZ = chr->GetTransOffsetZ(); data.orientation = chr->GetTransOffsetO(); Creature* creature = trans->CreateNPCPassenger(guid, &data); creature->SaveToDB(trans->GetGOInfo()->moTransport.mapID, 1 << map->GetSpawnMode(), chr->GetPhaseMaskForSpawn()); sObjectMgr->AddCreatureToGrid(guid, &data); return true; } Creature* creature = new Creature(); if (!creature->Create(map->GenerateLowGuid(), map, chr->GetPhaseMaskForSpawn(), id, 0, x, y, z, o)) { delete creature; return false; } creature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMaskForSpawn()); ObjectGuid::LowType spawnId = 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 = new Creature(); if (!creature->LoadCreatureFromDB(spawnId, map, true, true)) { delete creature; return false; } sObjectMgr->AddCreatureToGrid(spawnId, sObjectMgr->GetCreatureData(spawnId)); return true; } //add item in vendorlist static bool HandleNpcAddVendorItemCommand(ChatHandler* handler, ItemTemplate const* item, Optional mc, Optional it, Optional ec, Optional addMulti) { if (!item) { handler->SendErrorMessage(LANG_COMMAND_NEEDITEMSEND); return false; } Creature* vendor = handler->getSelectedCreature(); if (!vendor) { handler->SendErrorMessage(LANG_SELECT_CREATURE); return false; } uint32 itemId = item->ItemId; uint32 maxcount = mc.value_or(0); uint32 incrtime = it.value_or(0); uint32 extendedcost = ec.value_or(0); uint32 vendor_entry = addMulti.value_or(false) ? handler->GetSession()->GetCurrentVendor() : vendor->GetEntry(); if (!sObjectMgr->IsVendorItemValid(vendor_entry, itemId, maxcount, incrtime, extendedcost, handler->GetSession()->GetPlayer())) { handler->SetSentErrorMessage(true); return false; } sObjectMgr->AddVendorItem(vendor_entry, itemId, maxcount, incrtime, extendedcost); handler->PSendSysMessage(LANG_ITEM_ADDED_TO_LIST, itemId, item->Name1, maxcount, incrtime, extendedcost); return true; } //add move for creature static bool HandleNpcAddMoveCommand(ChatHandler* handler, CreatureSpawnId lowGuid) { // attempt check creature existence by DB data CreatureData const* data = sObjectMgr->GetCreatureData(lowGuid); if (!data) { handler->SendErrorMessage(LANG_COMMAND_CREATGUIDNOTFOUND, uint32(lowGuid)); return false; } // Update movement type WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_MOVEMENT_TYPE); stmt->SetData(0, uint8(WAYPOINT_MOTION_TYPE)); stmt->SetData(1, uint32(lowGuid)); WorldDatabase.Execute(stmt); handler->SendSysMessage(LANG_WAYPOINT_ADDED); return true; } static bool HandleNpcSetAllowMovementCommand(ChatHandler* handler) { 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, CreatureEntry newEntryNum) { if (!newEntryNum) return false; Unit* unit = handler->getSelectedUnit(); if (!unit || !unit->IsCreature()) { handler->SendErrorMessage(LANG_SELECT_CREATURE); 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, uint8 lvl) { if (lvl < 1 || lvl > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL) + 3) { handler->SendErrorMessage(LANG_BAD_VALUE); return false; } Creature* creature = handler->getSelectedCreature(); if (!creature || creature->IsPet()) { handler->SendErrorMessage(LANG_SELECT_CREATURE); return false; } creature->SetMaxHealth(100 + 30*lvl); creature->SetHealth(100 + 30*lvl); creature->SetLevel(lvl); creature->SaveToDB(); return true; } static bool HandleNpcDeleteCommand(ChatHandler* handler, Optional lowGuid) { Creature* creature; if (lowGuid.has_value()) creature = handler->GetCreatureFromPlayerMapByDbGuid(*lowGuid); else creature = handler->getSelectedCreature(); if (!creature || creature->IsPet() || creature->IsTotem()) { handler->SendErrorMessage(LANG_SELECT_CREATURE); return false; } // 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, ItemTemplate const* item, Optional addMulti) { Creature* vendor = handler->getSelectedCreature(); if (!vendor || !vendor->IsVendor()) { handler->SendErrorMessage(LANG_COMMAND_VENDORSELECTION); return false; } if (!item) { handler->SendErrorMessage(LANG_COMMAND_NEEDITEMSEND); return false; } uint32 itemId = item->ItemId; if (!sObjectMgr->RemoveVendorItem(addMulti.value_or(false) ? handler->GetSession()->GetCurrentVendor() : vendor->GetEntry(), itemId)) { handler->SendErrorMessage(LANG_ITEM_NOT_IN_LIST, itemId); return false; } handler->PSendSysMessage(LANG_ITEM_DELETED_FROM_LIST, itemId, item->Name1); return true; } //set faction of creature static bool HandleNpcSetFactionIdCommand(ChatHandler* handler, uint32 factionId) { if (!sFactionTemplateStore.LookupEntry(factionId)) { handler->SendErrorMessage(LANG_WRONG_FACTION, factionId); return false; } Creature* creature = handler->getSelectedCreature(); if (!creature) { handler->SendErrorMessage(LANG_SELECT_CREATURE); 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(cinfo)->faction = factionId; // ..and DB WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_FACTION); stmt->SetData(0, uint16(factionId)); stmt->SetData(1, creature->GetEntry()); WorldDatabase.Execute(stmt); return true; } //set tempfaction for creature static bool HandleNpcSetFactionTempIdCommand(ChatHandler* handler, uint32 tempfaction) { Player* player = handler->GetSession()->GetPlayer(); Unit* unit = player->GetSelectedUnit(); if (!unit) return false; Creature* creature = unit->ToCreature(); if (!creature) return false; creature->SetFaction(tempfaction); return true; } //set orginal faction for npc static bool HandleNpcSetOriginalFaction(ChatHandler* handler) { Player* player = handler->GetSession()->GetPlayer(); if (!player) return false; if (!player->GetSelectedUnit()) return false; Creature* creature = player->GetSelectedUnit()->ToCreature(); if (!creature) return false; creature->RestoreFaction(); return true; } //set npcflag of creature static bool HandleNpcSetFlagCommand(ChatHandler* handler, uint32 npcFlags) { Creature* creature = handler->getSelectedCreature(); if (!creature) { handler->SendErrorMessage(LANG_SELECT_CREATURE); return false; } creature->ReplaceAllNpcFlags(NPCFlags(npcFlags)); WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_NPCFLAG); stmt->SetData(0, NPCFlags(npcFlags)); stmt->SetData(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, uint32 data_1, uint32 data_2) { Creature* creature = handler->getSelectedCreature(); if (!creature) { handler->SendErrorMessage(LANG_SELECT_CREATURE); 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(), creature->GetEntry(), creature->GetName(), data_1, data_2, AIorScript); return true; } //npc follow handling static bool HandleNpcFollowCommand(ChatHandler* handler) { Player* player = handler->GetSession()->GetPlayer(); Creature* creature = handler->getSelectedCreature(); if (!creature) { handler->SendErrorMessage(LANG_SELECT_CREATURE); 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()); return true; } static bool HandleNpcInfoCommand(ChatHandler* handler) { Creature* target = handler->getSelectedCreature(); if (!target) { handler->SendErrorMessage(LANG_SELECT_CREATURE); return false; } CreatureTemplate const* cInfo = target->GetCreatureTemplate(); uint32 faction = target->GetFaction(); uint32 npcflags = target->GetNpcFlags(); uint32 mechanicImmuneMask = cInfo->MechanicImmuneMask; uint32 spellSchoolImmuneMask = cInfo->SpellSchoolImmuneMask; uint32 displayid = target->GetDisplayId(); uint32 nativeid = target->GetNativeDisplayId(); uint32 entry = target->GetEntry(); uint32 id1 = 0; uint32 id2 = 0; uint32 id3 = 0; if (CreatureData const* cData = target->GetCreatureData()) { id1 = cData->id1; id2 = cData->id2; id3 = cData->id3; } int64 curRespawnDelay = target->GetRespawnTimeEx() - GameTime::GetGameTime().count(); if (curRespawnDelay < 0) curRespawnDelay = 0; std::string curRespawnDelayStr = secsToTimeString(uint64(curRespawnDelay), true); std::string defRespawnDelayStr = secsToTimeString(target->GetRespawnDelay(), true); handler->PSendSysMessage(LANG_NPCINFO_CHAR, target->GetSpawnId(), target->GetGUID().ToString(), entry, id1, id2, id3, displayid, nativeid, faction, npcflags); handler->PSendSysMessage(LANG_NPCINFO_LEVEL, target->GetLevel()); handler->PSendSysMessage(LANG_NPCINFO_EQUIPMENT, target->GetCurrentEquipmentId(), target->GetOriginalEquipmentId()); handler->PSendSysMessage(LANG_NPCINFO_HEALTH, target->GetCreateHealth(), target->GetMaxHealth(), target->GetHealth()); handler->PSendSysMessage(LANG_NPCINFO_FLAGS, target->GetUnitFlags(), target->GetUnitFlags2(), target->GetDynamicFlags(), target->GetFaction()); handler->PSendSysMessage(LANG_COMMAND_RAWPAWNTIMES, defRespawnDelayStr, curRespawnDelayStr); handler->PSendSysMessage(LANG_NPCINFO_LOOT, cInfo->lootid, cInfo->pickpocketLootId, cInfo->SkinLootId); handler->PSendSysMessage(LANG_NPCINFO_DUNGEON_ID, target->GetInstanceId()); handler->PSendSysMessage(LANG_NPCINFO_PHASEMASK, target->GetPhaseMask()); handler->PSendSysMessage(LANG_NPCINFO_ARMOR, target->GetArmor()); handler->PSendSysMessage(LANG_NPCINFO_POSITION, float(target->GetPositionX()), float(target->GetPositionY()), float(target->GetPositionZ())); handler->PSendSysMessage(LANG_NPCINFO_AIINFO, target->GetAIName(), target->GetScriptName()); for (uint8 i = 0; i < NPCFLAG_COUNT; i++) { if (npcflags & npcFlagTexts[i].flag) { handler->PSendSysMessage(npcFlagTexts[i].text, npcFlagTexts[i].flag); } } handler->PSendSysMessage(LANG_NPCINFO_MECHANIC_IMMUNE, mechanicImmuneMask); for (uint8 i = 1; i < MAX_MECHANIC; ++i) { if (mechanicImmuneMask & (1 << (mechanicImmunes[i].flag - 1))) { handler->PSendSysMessage(mechanicImmunes[i].text, mechanicImmunes[i].flag); } } handler->PSendSysMessage(LANG_NPCINFO_SPELL_SCHOOL_IMMUNE, spellSchoolImmuneMask); for (uint8 i = 0; i < MAX_SPELL_SCHOOL; ++i) { if (spellSchoolImmuneMask & (1 << spellSchoolImmunes[i].flag)) { handler->PSendSysMessage(spellSchoolImmunes[i].text, spellSchoolImmunes[i].flag); } } return true; } static bool HandleNpcGuidCommand(ChatHandler* handler) { Creature* target = handler->getSelectedCreature(); if (!target) { handler->SendErrorMessage(LANG_SELECT_CREATURE); return false; } uint32 faction = target->GetFaction(); uint32 npcflags = target->GetNpcFlags(); uint32 displayid = target->GetDisplayId(); uint32 nativeid = target->GetNativeDisplayId(); uint32 entry = target->GetEntry(); uint32 id1 = 0; uint32 id2 = 0; uint32 id3 = 0; if (CreatureData const* cData = target->GetCreatureData()) { id1 = cData->id1; id2 = cData->id2; id3 = cData->id3; } handler->PSendSysMessage(LANG_NPCINFO_CHAR, target->GetSpawnId(), target->GetGUID().ToString(), entry, id1, id2, id3, displayid, nativeid, faction, npcflags); return true; } static bool HandleNpcNearCommand(ChatHandler* handler, Optional dist) { float distance = dist.value_or(10.0f); uint32 count = 0; Player* player = handler->GetSession()->GetPlayer(); WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_CREATURE_NEAREST); stmt->SetData(0, player->GetPositionX()); stmt->SetData(1, player->GetPositionY()); stmt->SetData(2, player->GetPositionZ()); stmt->SetData(3, player->GetMapId()); stmt->SetData(4, player->GetPositionX()); stmt->SetData(5, player->GetPositionY()); stmt->SetData(6, player->GetPositionZ()); stmt->SetData(7, distance * distance); stmt->SetData(8, player->GetPhaseMask()); PreparedQueryResult result = WorldDatabase.Query(stmt); if (result) { do { Field* fields = result->Fetch(); ObjectGuid::LowType guid = fields[0].Get(); uint32 entry = fields[1].Get(); //uint32 entry2 = fields[2].Get(); //uint32 entry3 = fields[3].Get(); float x = fields[4].Get(); float y = fields[5].Get(); float z = fields[6].Get(); uint16 mapId = fields[7].Get(); CreatureTemplate const* creatureTemplate = sObjectMgr->GetCreatureTemplate(entry); if (!creatureTemplate) continue; handler->PSendSysMessage(LANG_CREATURE_LIST_CHAT, guid, entry, guid, creatureTemplate->Name, 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, Optional guid) { Creature* creature; if (guid.has_value()) creature = handler->GetCreatureFromPlayerMapByDbGuid(*guid); else creature = handler->getSelectedCreature(); if (!creature) return false; ObjectGuid::LowType lowGuid; if (guid.has_value()) lowGuid = *guid; else lowGuid = creature->GetSpawnId(); CreatureData const* data = sObjectMgr->GetCreatureData(lowGuid); if (!data) { handler->SendErrorMessage(LANG_COMMAND_CREATGUIDNOTFOUND, lowGuid); return false; } if (handler->GetSession()->GetPlayer()->GetMapId() != data->mapid) { handler->SendErrorMessage(LANG_COMMAND_CREATUREATSAMEMAP, lowGuid); return false; } float x = handler->GetSession()->GetPlayer()->GetPositionX(); float y = handler->GetSession()->GetPlayer()->GetPositionY(); float z = handler->GetSession()->GetPlayer()->GetPositionZ(); float o = handler->GetSession()->GetPlayer()->GetOrientation(); if (creature) { if (CreatureData const* data = sObjectMgr->GetCreatureData(creature->GetSpawnId())) { const_cast(data)->posX = x; const_cast(data)->posY = y; const_cast(data)->posZ = z; const_cast(data)->orientation = o; } creature->SetPosition(x, y, z, o); creature->GetMotionMaster()->Initialize(); if (creature->IsAlive()) // dead creature will reset movement generator at respawn { creature->setDeathState(DeathState::JustDied); creature->Respawn(); } } WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_POSITION); stmt->SetData(0, x); stmt->SetData(1, y); stmt->SetData(2, z); stmt->SetData(3, o); stmt->SetData(4, lowGuid); WorldDatabase.Execute(stmt); handler->PSendSysMessage(LANG_COMMAND_CREATUREMOVED); return true; } //play npc emote static bool HandleNpcPlayEmoteCommand(ChatHandler* handler, uint32 emote) { Creature* target = handler->getSelectedCreature(); if (!target) { handler->SendErrorMessage(LANG_SELECT_CREATURE); return false; } target->SetUInt32Value(UNIT_NPC_EMOTESTATE, emote); return true; } //set model of creature static bool HandleNpcSetModelCommand(ChatHandler* handler, uint32 displayId) { Creature* creature = handler->getSelectedCreature(); if (!creature || creature->IsPet()) { handler->SendErrorMessage(LANG_SELECT_CREATURE); return false; } if (!sCreatureDisplayInfoStore.LookupEntry(displayId)) { handler->SendErrorMessage(LANG_COMMAND_FACTION_INVPARAM, Acore::ToString(displayId).c_str()); return false; } creature->SetDisplayId(displayId); creature->SetNativeDisplayId(displayId); creature->SaveToDB(); return true; } /**HandleNpcSetMoveTypeCommand * Set the movement type for an NPC.
*
* Valid movement types are: *
    *
  • stay - NPC wont move
  • *
  • random - NPC will move randomly according to the wander_distance
  • *
  • way - NPC will move with given waypoints set
  • *
* additional parameter: NODEL - so no waypoints are deleted, if you * change the movement type */ static bool HandleNpcSetMoveTypeCommand(ChatHandler* handler, Optional lowGuid, Variant type, Optional nodel) { // 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) bool doNotDelete = nodel.has_value(); ObjectGuid::LowType lowguid = 0; Creature* creature = nullptr; if (!lowGuid) // case .setmovetype $move_type (with selected creature) { creature = handler->getSelectedCreature(); if (!creature || creature->IsPet()) return false; lowguid = creature->GetSpawnId(); } else // case .setmovetype #creature_guid $move_type (with selected creature) { lowguid = *lowGuid; if (lowguid) creature = handler->GetCreatureFromPlayerMapByDbGuid(lowguid); // attempt check creature existence by DB data if (!creature) { CreatureData const* data = sObjectMgr->GetCreatureData(lowguid); if (!data) { handler->SendErrorMessage(LANG_COMMAND_CREATGUIDNOTFOUND, lowguid); 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; switch (type.index()) { case 0: move_type = IDLE_MOTION_TYPE; break; case 1: move_type = RANDOM_MOTION_TYPE; break; case 2: move_type = WAYPOINT_MOTION_TYPE; break; default: return false; } // update movement type //if (doNotDelete == false) // WaypointMgr.DeletePath(lowguid); if (creature) { // update movement type if (!doNotDelete) creature->LoadPath(0); creature->SetDefaultMovementType(move_type); creature->GetMotionMaster()->Initialize(); if (creature->IsAlive()) // dead creature will reset movement generator at respawn { creature->setDeathState(DeathState::JustDied); creature->Respawn(); } creature->SaveToDB(); } if (!doNotDelete) { handler->PSendSysMessage(LANG_MOVE_TYPE_SET, move_type); } else { handler->PSendSysMessage(LANG_MOVE_TYPE_SET_NODEL, move_type); } return true; } //npc phasemask handling //change phasemask of creature or pet static bool HandleNpcSetPhaseCommand(ChatHandler* handler, uint32 phasemask) { if (phasemask == 0) { handler->SendErrorMessage(LANG_BAD_VALUE); return false; } Creature* creature = handler->getSelectedCreature(); if (!creature) { handler->SendErrorMessage(LANG_SELECT_CREATURE); return false; } creature->SetPhaseMask(phasemask, true); if (!creature->IsPet()) creature->SaveToDB(); return true; } //set spawn dist of creature static bool HandleNpcSetWanderDistanceCommand(ChatHandler* handler, float option) { 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 = 0; if (creature) guidLow = creature->GetSpawnId(); else return false; creature->SetWanderDistance((float)option); creature->SetDefaultMovementType(mtype); creature->GetMotionMaster()->Initialize(); if (creature->IsAlive()) // dead creature will reset movement generator at respawn { creature->setDeathState(DeathState::JustDied); creature->Respawn(); } WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_WANDER_DISTANCE); stmt->SetData(0, option); stmt->SetData(1, uint8(mtype)); stmt->SetData(2, guidLow); WorldDatabase.Execute(stmt); handler->PSendSysMessage(LANG_COMMAND_WANDER_DISTANCE, option); return true; } //spawn time handling static bool HandleNpcSetSpawnTimeCommand(ChatHandler* handler, std::string spawnTimeStr) { if (spawnTimeStr.empty()) { return false; } if (Acore::StringTo(spawnTimeStr).value_or(0) < 0) { handler->SendErrorMessage(LANG_BAD_VALUE); return false; } Creature* creature = handler->getSelectedCreature(); if (!creature) return false; int32 spawnTime = TimeStringToSecs(spawnTimeStr); if (spawnTime <= 0) { spawnTime = Acore::StringTo(spawnTimeStr).value_or(0); } if (spawnTime <= 0) { handler->SendErrorMessage(LANG_BAD_VALUE); return false; } WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_SPAWN_TIME_SECS); stmt->SetData(0, spawnTime); stmt->SetData(1, creature->GetSpawnId()); WorldDatabase.Execute(stmt); creature->SetRespawnDelay(spawnTime); handler->PSendSysMessage(LANG_COMMAND_SPAWNTIME, secsToTimeString(spawnTime, true)); return true; } static bool HandleNpcSayCommand(ChatHandler* handler, Tail text) { if (text.empty()) return false; Creature* creature = handler->getSelectedCreature(); if (!creature) { handler->SendErrorMessage(LANG_SELECT_CREATURE); return false; } creature->Say(text, LANG_UNIVERSAL); // make some emotes switch (text.back()) { 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, Tail text) { if (text.empty()) return false; Creature* creature = handler->getSelectedCreature(); if (!creature) { handler->SendErrorMessage(LANG_SELECT_CREATURE); return false; } creature->TextEmote(text); return true; } //npc unfollow handling static bool HandleNpcUnFollowCommand(ChatHandler* handler) { Player* player = handler->GetSession()->GetPlayer(); Creature* creature = handler->getSelectedCreature(); if (!creature) { handler->SendErrorMessage(LANG_SELECT_CREATURE); return false; } if (/*creature->GetMotionMaster()->empty() ||*/ creature->GetMotionMaster()->GetCurrentMovementGeneratorType() != FOLLOW_MOTION_TYPE) { handler->SendErrorMessage(LANG_CREATURE_NOT_FOLLOW_YOU, creature->GetName().c_str()); return false; } FollowMovementGenerator const* mgen = static_cast const*>((creature->GetMotionMaster()->top())); if (mgen->GetTarget() != player) { handler->SendErrorMessage(LANG_CREATURE_NOT_FOLLOW_YOU, creature->GetName().c_str()); return false; } // reset movement creature->GetMotionMaster()->MovementExpired(true); handler->PSendSysMessage(LANG_CREATURE_NOT_FOLLOW_YOU_NOW, creature->GetName()); return true; } // make npc whisper to player static bool HandleNpcWhisperCommand(ChatHandler* handler, std::string const& recv, Tail text) { if (text.empty()) return false; Creature* creature = handler->getSelectedCreature(); if (!creature) { handler->SendErrorMessage(LANG_SELECT_CREATURE); return false; } // check online security Player* receiver = ObjectAccessor::FindPlayerByName(recv); if (handler->HasLowerSecurity(receiver, ObjectGuid::Empty)) return false; creature->Whisper(text, LANG_UNIVERSAL, receiver); return true; } static bool HandleNpcYellCommand(ChatHandler* handler, Tail text) { if (text.empty()) return false; Creature* creature = handler->getSelectedCreature(); if (!creature) { handler->SendErrorMessage(LANG_SELECT_CREATURE); return false; } creature->Yell(text, LANG_UNIVERSAL); // make an emote creature->HandleEmoteCommand(EMOTE_ONESHOT_SHOUT); return true; } // add creature, temp only static bool HandleNpcAddTempSpawnCommand(ChatHandler* handler, CreatureEntry id) { Player* chr = handler->GetSession()->GetPlayer(); if (!id) return false; chr->SummonCreature(id, *chr, TEMPSUMMON_CORPSE_DESPAWN, 120); return true; } //npc tame handling static bool HandleNpcTameCommand(ChatHandler* handler) { 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->IsExistPet()) { handler->SendErrorMessage(LANG_YOU_ALREADY_HAVE_PET); 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; } if (!player->CreatePet(creatureTarget)) { handler->SendErrorMessage(LANG_CREATURE_NON_TAMEABLE, cInfo->Entry); return false; } return true; } static bool HandleNpcAddFormationCommand(ChatHandler* handler, ObjectGuid::LowType leaderGUID) { Creature* creature = handler->getSelectedCreature(); if (!creature || !creature->GetSpawnId()) { handler->SendErrorMessage(LANG_SELECT_CREATURE); return false; } ObjectGuid::LowType lowguid = creature->GetSpawnId(); if (creature->GetFormation()) { handler->PSendSysMessage("Selected creature is already member of group {}", creature->GetFormation()->GetId()); return false; } if (!lowguid) return false; Player* chr = handler->GetSession()->GetPlayer(); FormationInfo group_member; group_member.follow_angle = (creature->GetAngle(chr) - chr->GetOrientation()) * 180 / M_PI; group_member.follow_dist = sqrtf(pow(chr->GetPositionX() - creature->GetPositionX(), int(2)) + pow(chr->GetPositionY() - creature->GetPositionY(), int(2))); group_member.leaderGUID = leaderGUID; group_member.groupAI = 0; sFormationMgr->CreatureGroupMap[lowguid] = group_member; creature->SearchFormation(); WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_INS_CREATURE_FORMATION); stmt->SetData(0, leaderGUID); stmt->SetData(1, lowguid); stmt->SetData(2, group_member.follow_dist); stmt->SetData(3, group_member.follow_angle); stmt->SetData(4, uint32(group_member.groupAI)); WorldDatabase.Execute(stmt); handler->PSendSysMessage("Creature {} added to formation with leader {}", lowguid, leaderGUID); return true; } static bool HandleNpcSetLinkCommand(ChatHandler* handler, ObjectGuid::LowType linkguid) { Creature* creature = handler->getSelectedCreature(); if (!creature) { handler->SendErrorMessage(LANG_SELECT_CREATURE); return false; } if (!creature->GetSpawnId()) { handler->SendErrorMessage("Selected creature {} isn't in creature table", creature->GetGUID().ToString()); return false; } if (!sObjectMgr->SetCreatureLinkedRespawn(creature->GetSpawnId(), linkguid)) { handler->SendErrorMessage("Selected creature can't link with guid '{}'", linkguid); return false; } handler->PSendSysMessage("LinkGUID '{}' added to creature with DBTableGUID: '{}'", linkguid, creature->GetSpawnId()); return true; } }; void AddSC_npc_commandscript() { new npc_commandscript(); }