diff options
Diffstat (limited to 'src')
22 files changed, 418 insertions, 78 deletions
diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp index f3c20750069..035d9af4369 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp @@ -573,6 +573,14 @@ void AuctionHouseObject::Update() if (AuctionsMap.empty()) return; + // Clear expired throttled players + for (PlayerGetAllThrottleMap::const_iterator itr = GetAllThrottleMap.begin(); itr != GetAllThrottleMap.end();) + { + if (itr->second <= curTime) + itr = GetAllThrottleMap.erase(itr); + else + ++itr; + } SQLTransaction trans = CharacterDatabase.BeginTransaction(); @@ -648,13 +656,40 @@ void AuctionHouseObject::BuildListOwnerItems(WorldPacket& data, Player* player, void AuctionHouseObject::BuildListAuctionItems(WorldPacket& data, Player* player, std::wstring const& wsearchedname, uint32 listfrom, uint8 levelmin, uint8 levelmax, uint8 usable, uint32 inventoryType, uint32 itemClass, uint32 itemSubClass, uint32 quality, - uint32& count, uint32& totalcount) + uint32& count, uint32& totalcount, bool getall) { int loc_idx = player->GetSession()->GetSessionDbLocaleIndex(); int locdbc_idx = player->GetSession()->GetSessionDbcLocale(); time_t curTime = sWorld->GetGameTime(); + PlayerGetAllThrottleMap::const_iterator itr = GetAllThrottleMap.find(player->GetGUID()); + time_t throttleTime = itr != GetAllThrottleMap.end() ? itr->second : curTime; + + if (getall && throttleTime <= curTime) + { + for (AuctionEntryMap::const_iterator itr = AuctionsMap.begin(); itr != AuctionsMap.end(); ++itr) + { + AuctionEntry* Aentry = itr->second; + // Skip expired auctions + if (Aentry->expire_time < curTime) + continue; + + Item* item = sAuctionMgr->GetAItem(Aentry->itemGUIDLow); + if (!item) + continue; + + ++count; + ++totalcount; + Aentry->BuildAuctionInfo(data, item); + + if (count >= MAX_GETALL_RETURN) + break; + } + GetAllThrottleMap[player->GetGUID()] = curTime + sWorld->getIntConfig(CONFIG_AUCTION_GETALL_DELAY); + return; + } + for (AuctionEntryMap::const_iterator itr = AuctionsMap.begin(); itr != AuctionsMap.end(); ++itr) { AuctionEntry* Aentry = itr->second; @@ -744,16 +779,16 @@ void AuctionHouseObject::BuildListAuctionItems(WorldPacket& data, Player* player if (count < 50 && totalcount >= listfrom) { ++count; - Aentry->BuildAuctionInfo(data); + Aentry->BuildAuctionInfo(data, item); } ++totalcount; } } //this function inserts to WorldPacket auction's data -bool AuctionEntry::BuildAuctionInfo(WorldPacket& data) const +bool AuctionEntry::BuildAuctionInfo(WorldPacket& data, Item* sourceItem) const { - Item* item = sAuctionMgr->GetAItem(itemGUIDLow); + Item* item = (sourceItem) ? sourceItem : sAuctionMgr->GetAItem(itemGUIDLow); if (!item) { TC_LOG_ERROR("misc", "AuctionEntry::BuildAuctionInfo: Auction %u has a non-existent item: %u", Id, itemGUIDLow); diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.h b/src/server/game/AuctionHouse/AuctionHouseMgr.h index 4a2b79ab170..fe4b9ed07de 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.h +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.h @@ -30,6 +30,7 @@ class WorldPacket; #define MIN_AUCTION_TIME (12*HOUR) #define MAX_AUCTION_ITEMS 160 +#define MAX_GETALL_RETURN 55000 enum AuctionError { @@ -90,7 +91,7 @@ struct AuctionEntry uint8 GetHouseId() const { return houseId; } uint32 GetAuctionCut() const; uint32 GetAuctionOutBid() const; - bool BuildAuctionInfo(WorldPacket & data) const; + bool BuildAuctionInfo(WorldPacket & data, Item* sourceItem = nullptr) const; void DeleteFromDB(SQLTransaction& trans) const; void SaveToDB(SQLTransaction& trans) const; bool LoadFromDB(Field* fields); @@ -110,6 +111,7 @@ class AuctionHouseObject } typedef std::map<uint32, AuctionEntry*> AuctionEntryMap; + typedef std::unordered_map<ObjectGuid, time_t> PlayerGetAllThrottleMap; uint32 Getcount() const { return AuctionsMap.size(); } @@ -133,10 +135,15 @@ class AuctionHouseObject void BuildListAuctionItems(WorldPacket& data, Player* player, std::wstring const& searchedname, uint32 listfrom, uint8 levelmin, uint8 levelmax, uint8 usable, uint32 inventoryType, uint32 itemClass, uint32 itemSubClass, uint32 quality, - uint32& count, uint32& totalcount); + uint32& count, uint32& totalcount, bool getall = false); private: AuctionEntryMap AuctionsMap; + + // Map of throttled players for GetAll, and throttle expiry time + // Stored here, rather than player object to maintain persistence after logout + PlayerGetAllThrottleMap GetAllThrottleMap; + }; class AuctionHouseMgr diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp index 5f1de673294..c73350872cc 100644 --- a/src/server/game/DataStores/DBCStores.cpp +++ b/src/server/game/DataStores/DBCStores.cpp @@ -91,6 +91,9 @@ DBCStorage <DurabilityCostsEntry> sDurabilityCostsStore(DurabilityCostsfmt); DBCStorage <EmotesEntry> sEmotesStore(EmotesEntryfmt); DBCStorage <EmotesTextEntry> sEmotesTextStore(EmotesTextEntryfmt); +typedef std::tuple<uint32, uint32, uint32> EmotesTextSoundKey; +static std::map<EmotesTextSoundKey, EmotesTextSoundEntry const*> sEmotesTextSoundMap; +DBCStorage <EmotesTextSoundEntry> sEmotesTextSoundStore(EmotesTextSoundEntryfmt); typedef std::map<uint32, SimpleFactionsList> FactionTeamMap; static FactionTeamMap sFactionTeamMap; @@ -338,6 +341,10 @@ void LoadDBCStores(const std::string& dataPath) LoadDBC(availableDbcLocales, bad_dbc_files, sDurabilityQualityStore, dbcPath, "DurabilityQuality.dbc"); LoadDBC(availableDbcLocales, bad_dbc_files, sEmotesStore, dbcPath, "Emotes.dbc"); LoadDBC(availableDbcLocales, bad_dbc_files, sEmotesTextStore, dbcPath, "EmotesText.dbc"); + LoadDBC(availableDbcLocales, bad_dbc_files, sEmotesTextSoundStore, dbcPath, "EmotesTextSound.dbc"); + for (uint32 i = 0; i < sEmotesTextSoundStore.GetNumRows(); ++i) + if (EmotesTextSoundEntry const* entry = sEmotesTextSoundStore.LookupEntry(i)) + sEmotesTextSoundMap[EmotesTextSoundKey(entry->EmotesTextId, entry->RaceId, entry->SexId)] = entry; LoadDBC(availableDbcLocales, bad_dbc_files, sFactionStore, dbcPath, "Faction.dbc"); for (uint32 i=0; i<sFactionStore.GetNumRows(); ++i) { @@ -1060,3 +1067,8 @@ ResponseCodes ValidateName(std::string const& name, LocaleConstant locale) return CHAR_NAME_SUCCESS; } + +EmotesTextSoundEntry const* FindTextSoundEmoteFor(uint32 emote, uint32 race, uint32 gender) +{ + return sEmotesTextSoundMap[EmotesTextSoundKey(emote, race, gender)]; +} diff --git a/src/server/game/DataStores/DBCStores.h b/src/server/game/DataStores/DBCStores.h index 41a97382b0b..14f59b04e6d 100644 --- a/src/server/game/DataStores/DBCStores.h +++ b/src/server/game/DataStores/DBCStores.h @@ -82,6 +82,8 @@ SkillRaceClassInfoEntry const* GetSkillRaceClassInfo(uint32 skill, uint8 race, u ResponseCodes ValidateName(std::string const& name, LocaleConstant locale); +EmotesTextSoundEntry const* FindTextSoundEmoteFor(uint32 emote, uint32 race, uint32 gender); + extern DBCStorage <AchievementEntry> sAchievementStore; extern DBCStorage <AchievementCriteriaEntry> sAchievementCriteriaStore; extern DBCStorage <AreaTableEntry> sAreaStore;// recommend access using functions @@ -113,6 +115,7 @@ extern DBCStorage <DurabilityCostsEntry> sDurabilityCostsStore; extern DBCStorage <DurabilityQualityEntry> sDurabilityQualityStore; extern DBCStorage <EmotesEntry> sEmotesStore; extern DBCStorage <EmotesTextEntry> sEmotesTextStore; +extern DBCStorage <EmotesTextSoundEntry> sEmotesTextSoundStore; extern DBCStorage <FactionEntry> sFactionStore; extern DBCStorage <FactionTemplateEntry> sFactionTemplateStore; extern DBCStorage <GameObjectDisplayInfoEntry> sGameObjectDisplayInfoStore; diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h index d1794a0ea90..2f4820d0353 100644 --- a/src/server/game/DataStores/DBCStructure.h +++ b/src/server/game/DataStores/DBCStructure.h @@ -926,6 +926,15 @@ struct EmotesTextEntry uint32 textid; }; +struct EmotesTextSoundEntry +{ + uint32 Id; // 0 + uint32 EmotesTextId; // 1 + uint32 RaceId; // 2 + uint32 SexId; // 3, 0 male / 1 female + uint32 SoundId; // 4 +}; + struct FactionEntry { uint32 ID; // 0 m_ID diff --git a/src/server/game/DataStores/DBCfmt.h b/src/server/game/DataStores/DBCfmt.h index aade6d91d61..22a01dae9f0 100644 --- a/src/server/game/DataStores/DBCfmt.h +++ b/src/server/game/DataStores/DBCfmt.h @@ -52,6 +52,7 @@ char const DurabilityCostsfmt[] = "niiiiiiiiiiiiiiiiiiiiiiiiiiiii"; char const DurabilityQualityfmt[] = "nf"; char const EmotesEntryfmt[] = "nxxiiix"; char const EmotesTextEntryfmt[] = "nxixxxxxxxxxxxxxxxx"; +char const EmotesTextSoundEntryfmt[] = "niiii"; char const FactionEntryfmt[] = "niiiiiiiiiiiiiiiiiiffixssssssssssssssssxxxxxxxxxxxxxxxxxx"; char const FactionTemplateEntryfmt[] = "niiiiiiiiiiiii"; char const GameObjectDisplayInfofmt[] = "nsxxxxxxxxxxffffffx"; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 95e76a25763..8ce78f88906 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -2406,7 +2406,7 @@ bool Player::CanInteractWithQuestGiver(Object* questGiver) return false; } -Creature* Player::GetNPCIfCanInteractWith(ObjectGuid guid, uint32 npcflagmask) +Creature* Player::GetNPCIfCanInteractWith(ObjectGuid const& guid, uint32 npcflagmask) { // unit checks if (!guid) @@ -2450,7 +2450,21 @@ Creature* Player::GetNPCIfCanInteractWith(ObjectGuid guid, uint32 npcflagmask) return creature; } -GameObject* Player::GetGameObjectIfCanInteractWith(ObjectGuid guid, GameobjectTypes type) const +GameObject* Player::GetGameObjectIfCanInteractWith(ObjectGuid const& guid) const +{ + if (GameObject* go = GetMap()->GetGameObject(guid)) + { + if (go->IsWithinDistInMap(this, go->GetInteractionDistance())) + return go; + + TC_LOG_DEBUG("maps", "GetGameObjectIfCanInteractWith: GameObject '%s' [GUID: %u] is too far away from player %s [GUID: %u] to be used by him (distance=%f, maximal %f is allowed)", go->GetGOInfo()->name.c_str(), + go->GetGUID().GetCounter(), GetName().c_str(), GetGUID().GetCounter(), go->GetDistance(this), go->GetInteractionDistance()); + } + + return nullptr; +} + +GameObject* Player::GetGameObjectIfCanInteractWith(ObjectGuid const& guid, GameobjectTypes type) const { if (GameObject* go = GetMap()->GetGameObject(guid)) { @@ -2459,12 +2473,12 @@ GameObject* Player::GetGameObjectIfCanInteractWith(ObjectGuid guid, GameobjectTy if (go->IsWithinDistInMap(this, go->GetInteractionDistance())) return go; - TC_LOG_DEBUG("maps", "GetGameObjectIfCanInteractWith: GameObject '%s' [GUID: %u] is too far away from player %s [GUID: %u] to be used by him (distance=%f, maximal 10 is allowed)", go->GetGOInfo()->name.c_str(), - go->GetGUID().GetCounter(), GetName().c_str(), GetGUID().GetCounter(), go->GetDistance(this)); + TC_LOG_DEBUG("maps", "GetGameObjectIfCanInteractWith: GameObject '%s' [GUID: %u] is too far away from player %s [GUID: %u] to be used by him (distance=%f, maximal %f is allowed)", go->GetGOInfo()->name.c_str(), + go->GetGUID().GetCounter(), GetName().c_str(), GetGUID().GetCounter(), go->GetDistance(this), go->GetInteractionDistance()); } } - return NULL; + return nullptr; } bool Player::IsUnderWater() const diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index e7af827e9c7..9b64023698c 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1050,8 +1050,9 @@ class Player : public Unit, public GridObject<Player> void SendInstanceResetWarning(uint32 mapid, Difficulty difficulty, uint32 time, bool welcome); bool CanInteractWithQuestGiver(Object* questGiver); - Creature* GetNPCIfCanInteractWith(ObjectGuid guid, uint32 npcflagmask); - GameObject* GetGameObjectIfCanInteractWith(ObjectGuid guid, GameobjectTypes type) const; + Creature* GetNPCIfCanInteractWith(ObjectGuid const& guid, uint32 npcflagmask); + GameObject* GetGameObjectIfCanInteractWith(ObjectGuid const& guid) const; + GameObject* GetGameObjectIfCanInteractWith(ObjectGuid const& guid, GameobjectTypes type) const; void ToggleAFK(); void ToggleDND(); diff --git a/src/server/game/Handlers/AuctionHouseHandler.cpp b/src/server/game/Handlers/AuctionHouseHandler.cpp index f23888cab03..efe0526baae 100644 --- a/src/server/game/Handlers/AuctionHouseHandler.cpp +++ b/src/server/game/Handlers/AuctionHouseHandler.cpp @@ -706,7 +706,7 @@ void WorldSession::HandleAuctionListItems(WorldPacket& recvData) TC_LOG_DEBUG("network", "WORLD: Received CMSG_AUCTION_LIST_ITEMS"); std::string searchedname; - uint8 levelmin, levelmax, usable; + uint8 levelmin, levelmax, usable, getAll; uint32 listfrom, auctionSlotID, auctionMainCategory, auctionSubCategory, quality; ObjectGuid guid; @@ -718,7 +718,7 @@ void WorldSession::HandleAuctionListItems(WorldPacket& recvData) recvData >> auctionSlotID >> auctionMainCategory >> auctionSubCategory; recvData >> quality >> usable; - recvData.read_skip<uint8>(); // unk + recvData >> getAll; // this block looks like it uses some lame byte packing or similar... uint8 unkCnt; @@ -760,11 +760,11 @@ void WorldSession::HandleAuctionListItems(WorldPacket& recvData) auctionHouse->BuildListAuctionItems(data, _player, wsearchedname, listfrom, levelmin, levelmax, usable, auctionSlotID, auctionMainCategory, auctionSubCategory, quality, - count, totalcount); + count, totalcount, (getAll != 0 && sWorld->getIntConfig(CONFIG_AUCTION_GETALL_DELAY) != 0)); data.put<uint32>(0, count); data << (uint32) totalcount; - data << (uint32) 300; // unk 2.3.0 const? + data << (uint32) sWorld->getIntConfig(CONFIG_AUCTION_SEARCH_DELAY); SendPacket(&data); } diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index 758d5af83f7..6cd8bfc014e 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -105,7 +105,7 @@ void WorldSession::HandleGossipSelectOptionOpcode(WorldPacket& recvData) GameObject* go = NULL; if (guid.IsCreatureOrVehicle()) { - unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_NONE); + unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_GOSSIP); if (!unit) { TC_LOG_DEBUG("network", "WORLD: HandleGossipSelectOptionOpcode - %s not found or you can't interact with him.", guid.ToString().c_str()); @@ -114,10 +114,10 @@ void WorldSession::HandleGossipSelectOptionOpcode(WorldPacket& recvData) } else if (guid.IsGameObject()) { - go = _player->GetMap()->GetGameObject(guid); + go = _player->GetGameObjectIfCanInteractWith(guid); if (!go) { - TC_LOG_DEBUG("network", "WORLD: HandleGossipSelectOptionOpcode - %s not found.", guid.ToString().c_str()); + TC_LOG_DEBUG("network", "WORLD: HandleGossipSelectOptionOpcode - %s not found or you can't interact with it.", guid.ToString().c_str()); return; } } diff --git a/src/server/game/Handlers/NPCHandler.cpp b/src/server/game/Handlers/NPCHandler.cpp index f272cd4a034..1e00c25a0c3 100644 --- a/src/server/game/Handlers/NPCHandler.cpp +++ b/src/server/game/Handlers/NPCHandler.cpp @@ -292,7 +292,7 @@ void WorldSession::HandleGossipHelloOpcode(WorldPacket& recvData) ObjectGuid guid; recvData >> guid; - Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_NONE); + Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_GOSSIP); if (!unit) { TC_LOG_DEBUG("network", "WORLD: HandleGossipHelloOpcode - %s not found or you can not interact with him.", guid.ToString().c_str()); @@ -334,47 +334,6 @@ void WorldSession::HandleGossipHelloOpcode(WorldPacket& recvData) unit->AI()->sGossipHello(_player); } -/*void WorldSession::HandleGossipSelectOptionOpcode(WorldPacket& recvData) -{ - TC_LOG_DEBUG("network", "WORLD: CMSG_GOSSIP_SELECT_OPTION"); - - uint32 option; - uint32 unk; - uint64 guid; - std::string code = ""; - - recvData >> guid >> unk >> option; - - if (_player->PlayerTalkClass->GossipOptionCoded(option)) - { - TC_LOG_DEBUG("network", "reading string"); - recvData >> code; - TC_LOG_DEBUG("network", "string read: %s", code.c_str()); - } - - Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_NONE); - if (!unit) - { - TC_LOG_DEBUG("network", "WORLD: HandleGossipSelectOptionOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid))); - return; - } - - // remove fake death - if (GetPlayer()->HasUnitState(UNIT_STATE_DIED)) - GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); - - if (!code.empty()) - { - if (!Script->GossipSelectWithCode(_player, unit, _player->PlayerTalkClass->GossipOptionSender (option), _player->PlayerTalkClass->GossipOptionAction(option), code.c_str())) - unit->OnGossipSelect (_player, option); - } - else - { - if (!Script->OnGossipSelect (_player, unit, _player->PlayerTalkClass->GossipOptionSender (option), _player->PlayerTalkClass->GossipOptionAction (option))) - unit->OnGossipSelect (_player, option); - } -}*/ - void WorldSession::HandleSpiritHealerActivateOpcode(WorldPacket& recvData) { TC_LOG_DEBUG("network", "WORLD: CMSG_SPIRIT_HEALER_ACTIVATE"); diff --git a/src/server/game/Handlers/QuestHandler.cpp b/src/server/game/Handlers/QuestHandler.cpp index 002adc19a86..a7db18deddb 100644 --- a/src/server/game/Handlers/QuestHandler.cpp +++ b/src/server/game/Handlers/QuestHandler.cpp @@ -75,7 +75,7 @@ void WorldSession::HandleQuestgiverHelloOpcode(WorldPacket& recvData) TC_LOG_DEBUG("network", "WORLD: Received CMSG_QUESTGIVER_HELLO %s", guid.ToString().c_str()); - Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_NONE); + Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_QUESTGIVER); if (!creature) { TC_LOG_DEBUG("network", "WORLD: HandleQuestgiverHelloOpcode - %s not found or you can't interact with him.", diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp index 0943d9db26a..6be1fd30ae3 100644 --- a/src/server/game/Handlers/SpellHandler.cpp +++ b/src/server/game/Handlers/SpellHandler.cpp @@ -268,11 +268,8 @@ void WorldSession::HandleGameObjectUseOpcode(WorldPacket& recvData) TC_LOG_DEBUG("network", "WORLD: Recvd CMSG_GAMEOBJ_USE Message [%s]", guid.ToString().c_str()); - if (GameObject* obj = GetPlayer()->GetMap()->GetGameObject(guid)) + if (GameObject* obj = GetPlayer()->GetGameObjectIfCanInteractWith(guid)) { - if (!obj->IsWithinDistInMap(GetPlayer(), obj->GetInteractionDistance())) - return; - // ignore for remote control state if (GetPlayer()->m_mover != GetPlayer()) if (!(GetPlayer()->IsOnVehicle(GetPlayer()->m_mover) || GetPlayer()->IsMounted()) && !obj->GetGOInfo()->IsUsableMounted()) @@ -293,17 +290,13 @@ void WorldSession::HandleGameobjectReportUse(WorldPacket& recvPacket) if (_player->m_mover != _player) return; - GameObject* go = GetPlayer()->GetMap()->GetGameObject(guid); - if (!go) - return; - - if (!go->IsWithinDistInMap(_player, INTERACTION_DISTANCE)) - return; - - if (go->AI()->GossipHello(_player)) - return; + if (GameObject* go = GetPlayer()->GetGameObjectIfCanInteractWith(guid)) + { + if (go->AI()->GossipHello(_player)) + return; - _player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT, go->GetEntry()); + _player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT, go->GetEntry()); + } } void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket) diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 09cc9180a57..289a4d47666 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -575,6 +575,13 @@ void World::LoadConfigSettings(bool reload) m_bool_configs[CONFIG_ADDON_CHANNEL] = sConfigMgr->GetBoolDefault("AddonChannel", true); m_bool_configs[CONFIG_CLEAN_CHARACTER_DB] = sConfigMgr->GetBoolDefault("CleanCharacterDB", false); m_int_configs[CONFIG_PERSISTENT_CHARACTER_CLEAN_FLAGS] = sConfigMgr->GetIntDefault("PersistentCharacterCleanFlags", 0); + m_int_configs[CONFIG_AUCTION_GETALL_DELAY] = sConfigMgr->GetIntDefault("Auction.GetAllScanDelay", 900); + m_int_configs[CONFIG_AUCTION_SEARCH_DELAY] = sConfigMgr->GetIntDefault("Auction.SearchDelay", 300); + if (m_int_configs[CONFIG_AUCTION_SEARCH_DELAY] < 100 || m_int_configs[CONFIG_AUCTION_SEARCH_DELAY] > 10000) + { + TC_LOG_ERROR("server.loading", "Auction.SearchDelay (%i) must be between 100 and 10000. Using default of 300ms", m_int_configs[CONFIG_AUCTION_SEARCH_DELAY]); + m_int_configs[CONFIG_AUCTION_SEARCH_DELAY] = 300; + } m_int_configs[CONFIG_CHAT_CHANNEL_LEVEL_REQ] = sConfigMgr->GetIntDefault("ChatLevelReq.Channel", 1); m_int_configs[CONFIG_CHAT_WHISPER_LEVEL_REQ] = sConfigMgr->GetIntDefault("ChatLevelReq.Whisper", 1); m_int_configs[CONFIG_CHAT_SAY_LEVEL_REQ] = sConfigMgr->GetIntDefault("ChatLevelReq.Say", 1); diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index a624fd6e6a7..00b244c9efb 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -356,6 +356,8 @@ enum WorldIntConfigs CONFIG_CHARTER_COST_ARENA_5v5, CONFIG_NO_GRAY_AGGRO_ABOVE, CONFIG_NO_GRAY_AGGRO_BELOW, + CONFIG_AUCTION_GETALL_DELAY, + CONFIG_AUCTION_SEARCH_DELAY, INT_CONFIG_VALUE_COUNT }; diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp index dbb00fa252e..fd945db4604 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp @@ -979,6 +979,9 @@ class go_celestial_planetarium_access : public GameObjectScript bool GossipHello(Player* player) override { + if (go->HasFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE)) + return false; + bool hasKey = true; if (LockEntry const* lock = sLockStore.LookupEntry(go->GetGOInfo()->goober.lockId)) { diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp index c77f5b2bce3..01c4704592f 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp @@ -1186,6 +1186,7 @@ class npc_brann_bronzebeard_ulduar_intro : public CreatureScript { if (menuId == GOSSIP_MENU_BRANN_BRONZEBEARD && gossipListId == GOSSIP_OPTION_BRANN_BRONZEBEARD) { + me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); player->PlayerTalkClass->SendCloseGossip(); if (Creature* loreKeeper = _instance->GetCreature(DATA_LORE_KEEPER_OF_NORGANNON)) loreKeeper->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); @@ -1238,6 +1239,7 @@ class npc_lorekeeper : public CreatureScript { if (menuId == GOSSIP_MENU_LORE_KEEPER && gossipListId == GOSSIP_OPTION_LORE_KEEPER) { + me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); player->PlayerTalkClass->SendCloseGossip(); _instance->instance->LoadGrid(364, -16); // make sure leviathan is loaded @@ -1250,6 +1252,7 @@ class npc_lorekeeper : public CreatureScript { if (Creature* brann = _instance->GetCreature(DATA_BRANN_BRONZEBEARD_INTRO)) { + brann->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); delorah->GetMotionMaster()->MovePoint(0, brann->GetPositionX() - 4, brann->GetPositionY(), brann->GetPositionZ()); /// @todo delorah->AI()->Talk(xxxx, brann->GetGUID()); when reached at branz } diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp index 820332791c8..4d6aa046d10 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp @@ -1634,8 +1634,11 @@ class go_mimiron_hardmode_button : public GameObjectScript public: go_mimiron_hardmode_button() : GameObjectScript("go_mimiron_hardmode_button") { } - bool OnGossipHello(Player* /*player*/, GameObject* go) + bool OnGossipHello(Player* /*player*/, GameObject* go) override { + if (go->HasFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE)) + return false; + InstanceScript* instance = go->GetInstanceScript(); if (!instance) return false; diff --git a/src/server/scripts/Spells/spell_item.cpp b/src/server/scripts/Spells/spell_item.cpp index 9475da91a77..0abff255e4b 100644 --- a/src/server/scripts/Spells/spell_item.cpp +++ b/src/server/scripts/Spells/spell_item.cpp @@ -29,6 +29,7 @@ #include "SpellAuraEffects.h" #include "SkillDiscovery.h" #include "Battleground.h" +#include "DBCStores.h" // Generic script for handling item dummy effects which trigger another spell. class spell_item_trigger_spell : public SpellScriptLoader @@ -2630,6 +2631,43 @@ public: } }; +class spell_item_toy_train_set_pulse : public SpellScriptLoader +{ +public: + spell_item_toy_train_set_pulse() : SpellScriptLoader("spell_item_toy_train_set_pulse") { } + + class spell_item_toy_train_set_pulse_SpellScript : public SpellScript + { + PrepareSpellScript(spell_item_toy_train_set_pulse_SpellScript); + + void HandleDummy(SpellEffIndex /*index*/) + { + if (Player* target = GetHitUnit()->ToPlayer()) + { + target->HandleEmoteCommand(EMOTE_ONESHOT_TRAIN); + if (EmotesTextSoundEntry const* soundEntry = FindTextSoundEmoteFor(TEXT_EMOTE_TRAIN, target->getRace(), target->getGender())) + target->PlayDistanceSound(soundEntry->SoundId); + } + } + + void HandleTargets(std::list<WorldObject*>& targetList) + { + targetList.remove_if([](WorldObject const* obj) { return obj->GetTypeId() != TYPEID_PLAYER; }); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_item_toy_train_set_pulse_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_item_toy_train_set_pulse_SpellScript::HandleTargets, EFFECT_ALL, TARGET_UNIT_SRC_AREA_ALLY); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_item_toy_train_set_pulse_SpellScript(); + } +}; + void AddSC_item_spell_scripts() { // 23074 Arcanite Dragonling @@ -2698,4 +2736,5 @@ void AddSC_item_spell_scripts() new spell_item_chicken_cover(); new spell_item_muisek_vessel(); new spell_item_greatmothers_soulcatcher(); + new spell_item_toy_train_set_pulse(); } diff --git a/src/server/scripts/World/go_scripts.cpp b/src/server/scripts/World/go_scripts.cpp index ef4a2b0e32f..b90839f50c5 100644 --- a/src/server/scripts/World/go_scripts.cpp +++ b/src/server/scripts/World/go_scripts.cpp @@ -41,6 +41,7 @@ go_tadpole_cage go_amberpine_outhouse go_hive_pod go_veil_skith_cage +go_toy_train_set EndContentData */ #include "ScriptMgr.h" @@ -1196,6 +1197,48 @@ public: } }; + +enum ToyTrainSpells +{ + SPELL_TOY_TRAIN_PULSE = 61551, +}; + +class go_toy_train_set : public GameObjectScript +{ + public: + go_toy_train_set() : GameObjectScript("go_toy_train_set") { } + + struct go_toy_train_setAI : public GameObjectAI + { + go_toy_train_setAI(GameObject* go) : GameObjectAI(go), _pulseTimer(3 * IN_MILLISECONDS) { } + + void UpdateAI(uint32 diff) override + { + if (diff < _pulseTimer) + _pulseTimer -= diff; + else + { + go->CastSpell(nullptr, SPELL_TOY_TRAIN_PULSE, true); + _pulseTimer = 6 * IN_MILLISECONDS; + } + } + + // triggered on wrecker'd + void DoAction(int32 /*action*/) override + { + go->Delete(); + } + + private: + uint32 _pulseTimer; + }; + + GameObjectAI* GetAI(GameObject* go) const override + { + return new go_toy_train_setAI(go); + } +}; + void AddSC_go_scripts() { new go_cat_figurine(); @@ -1231,4 +1274,5 @@ void AddSC_go_scripts() new go_veil_skith_cage(); new go_frostblade_shrine(); new go_midsummer_bonfire(); + new go_toy_train_set(); } diff --git a/src/server/scripts/World/npcs_special.cpp b/src/server/scripts/World/npcs_special.cpp index 16b95e555bb..2afcd4857bf 100644 --- a/src/server/scripts/World/npcs_special.cpp +++ b/src/server/scripts/World/npcs_special.cpp @@ -38,6 +38,7 @@ npc_snake_trap_serpents 80% AI for snakes that summoned by Snake Trap npc_shadowfiend 100% restore 5% of owner's mana when shadowfiend die from damage npc_locksmith 75% list of keys needs to be confirmed npc_firework 100% NPC's summoned by rockets and rocket clusters, for making them cast visual +npc_train_wrecker 100% Wind-Up Train Wrecker that kills train set EndContentData */ #include "ScriptMgr.h" @@ -519,6 +520,67 @@ public: }; /*###### +## npc_torch_tossing_target_bunny_controller +######*/ + +enum TorchTossingTarget +{ + NPC_TORCH_TOSSING_TARGET_BUNNY = 25535, + SPELL_TARGET_INDICATOR = 45723 +}; + +class npc_torch_tossing_target_bunny_controller : public CreatureScript +{ +public: + npc_torch_tossing_target_bunny_controller() : CreatureScript("npc_torch_tossing_target_bunny_controller") { } + + struct npc_torch_tossing_target_bunny_controllerAI : public ScriptedAI + { + npc_torch_tossing_target_bunny_controllerAI(Creature* creature) : ScriptedAI(creature) + { + _targetTimer = 3000; + } + + ObjectGuid DoSearchForTargets(ObjectGuid lastTargetGUID) + { + std::list<Creature*> targets; + me->GetCreatureListWithEntryInGrid(targets, NPC_TORCH_TOSSING_TARGET_BUNNY, 60.0f); + targets.remove_if([lastTargetGUID](Creature* creature) { return creature->GetGUID() == lastTargetGUID; }); + + if (!targets.empty()) + { + _lastTargetGUID = Trinity::Containers::SelectRandomContainerElement(targets)->GetGUID(); + + return _lastTargetGUID; + } + return ObjectGuid::Empty; + } + + void UpdateAI(uint32 diff) override + { + if (_targetTimer < diff) + { + if (Unit* target = ObjectAccessor::GetUnit(*me, DoSearchForTargets(_lastTargetGUID))) + target->CastSpell(target, SPELL_TARGET_INDICATOR, true); + + _targetTimer = 3000; + } + else + _targetTimer -= diff; + } + + private: + uint32 _targetTimer; + ObjectGuid _lastTargetGUID; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return new npc_torch_tossing_target_bunny_controllerAI(creature); + } +}; + +/*###### ## Triage quest ######*/ @@ -2387,12 +2449,136 @@ class npc_stable_master : public CreatureScript } }; +enum TrainWrecker +{ + GO_TOY_TRAIN = 193963, + SPELL_TOY_TRAIN_PULSE = 61551, + SPELL_WRECK_TRAIN = 62943, + ACTION_WRECKED = 1, + EVENT_DO_JUMP = 1, + EVENT_DO_FACING = 2, + EVENT_DO_WRECK = 3, + EVENT_DO_DANCE = 4, + MOVEID_CHASE = 1, + MOVEID_JUMP = 2 +}; +class npc_train_wrecker : public CreatureScript +{ + public: + npc_train_wrecker() : CreatureScript("npc_train_wrecker") { } + + struct npc_train_wreckerAI : public NullCreatureAI + { + npc_train_wreckerAI(Creature* creature) : NullCreatureAI(creature), _isSearching(true), _nextAction(0), _timer(1 * IN_MILLISECONDS) { } + + GameObject* VerifyTarget() const + { + if (GameObject* target = ObjectAccessor::GetGameObject(*me, _target)) + return target; + me->HandleEmoteCommand(EMOTE_ONESHOT_RUDE); + me->DespawnOrUnsummon(3 * IN_MILLISECONDS); + return nullptr; + } + + void UpdateAI(uint32 diff) override + { + if (_isSearching) + { + if (diff < _timer) + _timer -= diff; + else + { + if (GameObject* target = me->FindNearestGameObject(GO_TOY_TRAIN, 15.0f)) + { + _isSearching = false; + _target = target->GetGUID(); + me->SetWalk(true); + me->GetMotionMaster()->MovePoint(MOVEID_CHASE, target->GetNearPosition(3.0f, target->GetAngle(me))); + } + else + _timer = 3 * IN_MILLISECONDS; + } + } + else + { + switch (_nextAction) + { + case EVENT_DO_JUMP: + if (GameObject* target = VerifyTarget()) + me->GetMotionMaster()->MoveJump(*target, 5.0, 10.0, MOVEID_JUMP); + _nextAction = 0; + break; + case EVENT_DO_FACING: + if (GameObject* target = VerifyTarget()) + { + me->SetFacingTo(target->GetOrientation()); + me->HandleEmoteCommand(EMOTE_ONESHOT_ATTACK1H); + _timer = 1.5 * IN_MILLISECONDS; + _nextAction = EVENT_DO_WRECK; + } + else + _nextAction = 0; + break; + case EVENT_DO_WRECK: + if (diff < _timer) + { + _timer -= diff; + break; + } + if (GameObject* target = VerifyTarget()) + { + me->CastSpell(target, SPELL_WRECK_TRAIN, false); + target->AI()->DoAction(ACTION_WRECKED); + _timer = 2 * IN_MILLISECONDS; + _nextAction = EVENT_DO_DANCE; + } + else + _nextAction = 0; + break; + case EVENT_DO_DANCE: + if (diff < _timer) + { + _timer -= diff; + break; + } + me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_DANCE); + me->DespawnOrUnsummon(5 * IN_MILLISECONDS); + _nextAction = 0; + break; + default: + break; + } + } + } + + void MovementInform(uint32 /*type*/, uint32 id) + { + if (id == MOVEID_CHASE) + _nextAction = EVENT_DO_JUMP; + else if (id == MOVEID_JUMP) + _nextAction = EVENT_DO_FACING; + } + + private: + bool _isSearching; + uint8 _nextAction; + uint32 _timer; + ObjectGuid _target; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return new npc_train_wreckerAI(creature); + } +}; + void AddSC_npcs_special() { new npc_air_force_bots(); new npc_lunaclaw_spirit(); new npc_chicken_cluck(); new npc_dancing_flames(); + new npc_torch_tossing_target_bunny_controller(); new npc_doctor(); new npc_injured_patient(); new npc_garments_of_quests(); @@ -2410,4 +2596,5 @@ void AddSC_npcs_special() new npc_spring_rabbit(); new npc_imp_in_a_ball(); new npc_stable_master(); + new npc_train_wrecker(); } diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index 5b07dde22bb..217995cb88a 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -406,6 +406,24 @@ CleanCharacterDB = 0 PersistentCharacterCleanFlags = 0 # +# Auction.GetAllScanDelay +# Description: Sets the minimum time in seconds, a single player character can perform a getall scan. +# The value is only held in memory so a server restart will clear it. +# Setting this to zero, will disable GetAll functions completely. +# Default: 900 - (GetAll scan limited to once every 15mins per player character) + +Auction.GetAllScanDelay = 900 + +# +# Auction.SearchDelay +# Description: Sets the minimum time in milliseconds (seconds x 1000), that the client must wait between +# auction search operations. This can be increased if somehow Auction House activity is causing +# too much load. +# Default: 300 - (Time delay between auction searches set to 0.3secs) + +Auction.SearchDelay = 300 + +# ################################################################################################### ################################################################################################### |