diff --git a/sql/mangos.sql b/sql/mangos.sql index b6e6d06fd..9418ba09d 100644 --- a/sql/mangos.sql +++ b/sql/mangos.sql @@ -3406,8 +3406,8 @@ INSERT INTO `mangos_string` VALUES (206,'Item \'%i\' \'%s\' added to list with maxcount \'%i\' and incrtime \'%i\' and extendedcost \'%i\'',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (207,'Item \'%i\' not found in database.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (208,'Item \'%i\' \'%s\' deleted from vendor list',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), -(209,'Item \'%i\' not found in vendor list.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), -(210,'Item \'%i\' (with extended cost %i) already in vendor list.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(209,'Item \'%i\' (isCurrency: %u) not found in vendor list.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(210,'Item \'%i\' (isCurrency: %u, with extended cost %i) already in vendor list.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (211,'Spells of %s reset.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (212,'Spells of %s will reset at next login.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (213,'Talents of %s reset.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), @@ -3466,6 +3466,7 @@ INSERT INTO `mangos_string` VALUES (266,'Nothing found!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (267,'Object not found!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (268,'Creature not found!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(269,'Currency \'%i\' not found.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (270,'Creature Removed',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (271,'Creature moved.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (272,'Creature (GUID:%u) must be on the same map as player!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), @@ -3479,6 +3480,7 @@ INSERT INTO `mangos_string` VALUES (280,'Vendor has too many items (max 128)',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (281,'You can\'t kick self, logout instead',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (282,'Player %s kicked.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(283,'Meta currency \'%i\' is not allowed in vendors.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (284,'Accepting Whisper: %s',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (285,'Accepting Whisper: ON',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (286,'Accepting Whisper: OFF',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), @@ -4080,6 +4082,8 @@ INSERT INTO `mangos_string` VALUES (1506,'Current phase = %u',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (1507,'Combat-Movement is %s',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (1508,'Melee attacking is %s',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(1509,'Can\'t add item %u to vendor with unknown item type %u',,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(1510,'Currency %u has maxCount = 0, but for currencies maxCount = buyCount, so it can\'t be 0 or less than that\'s currency precision (%u).',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (1600,'|cffffff00Northpass Tower has been taken by the Horde!|r',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (1601,'|cffffff00Northpass Tower has been taken by the Alliance!|r',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (1602,'|cffffff00Crown Guard Tower has been taken by the Horde!|r',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), @@ -4346,8 +4350,8 @@ UNLOCK TABLES; DROP TABLE IF EXISTS `npc_vendor`; CREATE TABLE `npc_vendor` ( `entry` mediumint(8) unsigned NOT NULL default '0', - `item` mediumint(8) unsigned NOT NULL default '0', - `maxcount` tinyint(3) unsigned NOT NULL default '0', + `item` mediumint(8) NOT NULL default '0', + `maxcount` smallint(5) unsigned NOT NULL default '0', `incrtime` int(10) unsigned NOT NULL default '0', `ExtendedCost` mediumint(8) unsigned NOT NULL default '0', PRIMARY KEY (`entry`,`item`,`ExtendedCost`) @@ -4369,8 +4373,8 @@ UNLOCK TABLES; DROP TABLE IF EXISTS `npc_vendor_template`; CREATE TABLE `npc_vendor_template` ( `entry` mediumint(8) unsigned NOT NULL default '0', - `item` mediumint(8) unsigned NOT NULL default '0', - `maxcount` tinyint(3) unsigned NOT NULL default '0', + `item` mediumint(8) NOT NULL default '0', + `maxcount` smallint(3) unsigned NOT NULL default '0', `incrtime` int(10) unsigned NOT NULL default '0', `ExtendedCost` mediumint(8) unsigned NOT NULL default '0', PRIMARY KEY (`entry`,`item`,`ExtendedCost`) diff --git a/sql/updates/0169_xxxxx_01_mangos_npc_vendor.sql b/sql/updates/0169_xxxxx_01_mangos_npc_vendor.sql new file mode 100644 index 000000000..c235ccd7c --- /dev/null +++ b/sql/updates/0169_xxxxx_01_mangos_npc_vendor.sql @@ -0,0 +1,4 @@ +ALTER TABLE db_version CHANGE COLUMN required_0168_xxxxx_01_mangos_playercreateinfo_spell required_0169_xxxxx_01_mangos_npc_vendor bit; + +ALTER TABLE `npc_vendor` MODIFY COLUMN `item` mediumint(8) NOT NULL DEFAULT '0'; +ALTER TABLE `npc_vendor` MODIFY COLUMN `maxcount` smallint(5) unsigned NOT NULL default '0'; diff --git a/sql/updates/0169_xxxxx_02_mangos_npc_vendor_template.sql b/sql/updates/0169_xxxxx_02_mangos_npc_vendor_template.sql new file mode 100644 index 000000000..74b2d9a52 --- /dev/null +++ b/sql/updates/0169_xxxxx_02_mangos_npc_vendor_template.sql @@ -0,0 +1,4 @@ +ALTER TABLE db_version CHANGE COLUMN required_0169_xxxxx_01_mangos_npc_vendor required_0169_xxxxx_02_mangos_npc_vendor_template bit; + +ALTER TABLE `npc_vendor_template` MODIFY COLUMN `item` mediumint(8) NOT NULL DEFAULT '0'; +ALTER TABLE `npc_vendor_template` MODIFY COLUMN `maxcount` smallint(5) unsigned NOT NULL default '0'; \ No newline at end of file diff --git a/sql/updates/0169_xxxxx_03_mangos_mangos_string.sql b/sql/updates/0169_xxxxx_03_mangos_mangos_string.sql new file mode 100644 index 000000000..c6a879be8 --- /dev/null +++ b/sql/updates/0169_xxxxx_03_mangos_mangos_string.sql @@ -0,0 +1,9 @@ +ALTER TABLE db_version CHANGE COLUMN required_0169_xxxxx_02_mangos_npc_vendor_template required_0169_xxxxx_03_mangos_mangos_string bit; + +REPLACE INTO `mangos_string` (`entry`, `content_default`) VALUES +(209, 'Item \'%i\' (isCurrency: %u) not found in vendor list.'), +(210, 'Item \'%i\' (isCurrency: %u, with extended cost %i) already in vendor list.'), +(269, 'Currency \'%i\' not found.'), +(283, 'Meta currency \'%i\' is not allowed in vendors.'), +(1509, 'Can\'t add item %u to vendor with unknown item type %u'), +(1510, 'Currency %u has maxCount = 0, but for currencies maxCount = buyCount, so it can\'t be 0 or less than that\'s currency precision (%u).'); \ No newline at end of file diff --git a/sql/updates/0169_xxxxx_04_mangos_command.sql b/sql/updates/0169_xxxxx_04_mangos_command.sql new file mode 100644 index 000000000..4b5dcfe32 --- /dev/null +++ b/sql/updates/0169_xxxxx_04_mangos_command.sql @@ -0,0 +1,6 @@ +ALTER TABLE db_version CHANGE COLUMN required_0169_xxxxx_03_mangos_mangos_string required_0169_xxxxx_04_mangos_command bit; + +DELETE FROM `command` WHERE `name` IN ('npc addcurrency', 'npc delcurrency'); +INSERT INTO `command` VALUES +('npc addcurrency',2,'Syntax: .npc addcurrency #currencyId #buycount #extendedcost\r\n\r\nAdd currency #currencyId to item list of selected vendor. '), +('npc delcurrency',2,'Syntax: .npc delcurrency #currencyId\r\n\r\nRemove currency #currencyId from item list of selected vendor.'); \ No newline at end of file diff --git a/src/game/Chat.cpp b/src/game/Chat.cpp index 8378e39db..eb53f40ea 100644 --- a/src/game/Chat.cpp +++ b/src/game/Chat.cpp @@ -45,6 +45,7 @@ // |color|Hareatrigger_target:id|h[name]|h|r // |color|Hcreature:creature_guid|h[name]|h|r // |color|Hcreature_entry:creature_id|h[name]|h|r +// |color|Hcurrency:currency_id||h[name]|h|r // |color|Henchant:recipe_spell_id|h[prof_name: recipe_name]|h|r - client, at shift click in recipes list dialog // |color|Hgameevent:id|h[name]|h|r // |color|Hgameobject:go_guid|h[name]|h|r @@ -438,6 +439,7 @@ ChatCommand* ChatHandler::getCommandTable() static ChatCommand npcCommandTable[] = { { "add", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcAddCommand, "", NULL }, + { "addcurrency", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcAddVendorCurrencyCommand,"", NULL }, { "additem", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcAddVendorItemCommand, "", NULL }, { "addmove", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcAddMoveCommand, "", NULL }, { "aiinfo", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcAIInfoCommand, "", NULL }, @@ -445,6 +447,7 @@ ChatCommand* ChatHandler::getCommandTable() { "changeentry", SEC_ADMINISTRATOR, false, &ChatHandler::HandleNpcChangeEntryCommand, "", NULL }, { "changelevel", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcChangeLevelCommand, "", NULL }, { "delete", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcDeleteCommand, "", NULL }, + { "delcurrency", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcDelVendorCurrencyCommand,"", NULL }, { "delitem", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcDelVendorItemCommand, "", NULL }, { "factionid", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcFactionIdCommand, "", NULL }, { "flag", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcFlagCommand, "", NULL }, @@ -1447,6 +1450,7 @@ bool ChatHandler::isValidChatMessage(const char* message) /* valid examples: + |cff00aa00|Hcurrency:391|h[Рекомендательный значок Тол Барада]|h|r |cffa335ee|Hitem:812:0:0:0:0:0:0:0:70|h[Glowing Brightwood Staff]|h|r |cff808080|Hquest:2278:47|h[The Platinum Discs]|h|r |cffffd000|Htrade:4037:1:150:1:6AAAAAAAAAAAAAAAAAAAAAAOAADAAAAAAAAAAAAAAAAIAAAAAAAAA|h[Engineering]|h|r @@ -1506,6 +1510,7 @@ bool ChatHandler::isValidChatMessage(const char* message) uint32 color = 0; + CurrencyTypesEntry const* linkedCurrency = NULL; ItemPrototype const* linkedItem = NULL; Quest const* linkedQuest = NULL; SpellEntry const* linkedSpell = NULL; @@ -1517,6 +1522,7 @@ bool ChatHandler::isValidChatMessage(const char* message) { if (validSequence == validSequenceIterator) { + linkedCurrency = NULL; linkedItem = NULL; linkedQuest = NULL; linkedSpell = NULL; @@ -1606,7 +1612,26 @@ bool ChatHandler::isValidChatMessage(const char* message) if (reader.eof()) // : must be return false; - if (strcmp(buffer, "item") == 0) + if (strcmp(buffer, "currency") == 0) + { + if (color != CHAT_LINK_COLOR_CURRENCY) + return false; + + uint32 currencyEntry = 0; + // read currency entry + char c = reader.peek(); + while (c >= '0' && c <= '9') + { + reader.ignore(1); + currencyEntry *= 10; + currencyEntry += c - '0'; + c = reader.peek(); + } + linkedCurrency = sCurrencyTypesStore.LookupEntry(currencyEntry); + if (!linkedCurrency) + return false; + } + else if (strcmp(buffer, "item") == 0) { // read item entry reader.getline(buffer, 256, ':'); @@ -1886,7 +1911,24 @@ bool ChatHandler::isValidChatMessage(const char* message) return false; // verify the link name - if (linkedSpell) + if (linkedCurrency) + { + if (linkedCurrency->ID == CURRENCY_CONQUEST_ARENA_META || linkedCurrency->ID == CURRENCY_CONQUEST_BG_META) + return false; + + bool foundName = false; + for (uint8 i = 0; i < MAX_LOCALE; ++i) + { + if (*linkedCurrency->name[i] && strcmp(linkedCurrency->name[i], buffer) == 0) + { + foundName = true; + break; + } + } + if (!foundName) + return false; + } + else if (linkedSpell) { // spells with that flag have a prefix of "$PROFESSION: " if (linkedSpell->HasAttribute(SPELL_ATTR_TRADESPELL)) diff --git a/src/game/Chat.h b/src/game/Chat.h index 27134096c..51c891659 100644 --- a/src/game/Chat.h +++ b/src/game/Chat.h @@ -339,12 +339,14 @@ class MANGOS_DLL_SPEC ChatHandler //-----------------------Npc Commands----------------------- bool HandleNpcAddCommand(char* args); bool HandleNpcAddMoveCommand(char* args); + bool HandleNpcAddVendorCurrencyCommand(char* args); bool HandleNpcAddVendorItemCommand(char* args); bool HandleNpcAIInfoCommand(char* args); bool HandleNpcAllowMovementCommand(char* args); bool HandleNpcChangeEntryCommand(char* args); bool HandleNpcChangeLevelCommand(char* args); bool HandleNpcDeleteCommand(char* args); + bool HandleNpcDelVendorCurrencyCommand(char* args); bool HandleNpcDelVendorItemCommand(char* args); bool HandleNpcFactionIdCommand(char* args); bool HandleNpcFlagCommand(char* args); diff --git a/src/game/Creature.cpp b/src/game/Creature.cpp index 15de2daff..b5f4f81f2 100644 --- a/src/game/Creature.cpp +++ b/src/game/Creature.cpp @@ -67,13 +67,13 @@ TrainerSpell const* TrainerSpellData::Find(uint32 spell_id) const return NULL; } -bool VendorItemData::RemoveItem(uint32 item_id) +bool VendorItemData::RemoveItem(uint32 item_id, uint8 type) { bool found = false; for (VendorItemList::iterator i = m_items.begin(); i != m_items.end();) { // can have many examples - if ((*i)->item == item_id) + if ((*i)->item == item_id && (*i)->type == type) { i = m_items.erase(i); found = true; @@ -85,10 +85,10 @@ bool VendorItemData::RemoveItem(uint32 item_id) return found; } -VendorItem const* VendorItemData::FindItemCostPair(uint32 item_id, uint32 extendedCost) const +VendorItem const* VendorItemData::FindItemCostPair(uint32 item_id, uint8 type, uint32 extendedCost) const { for (VendorItemList::const_iterator i = m_items.begin(); i != m_items.end(); ++i) - if ((*i)->item == item_id && (*i)->ExtendedCost == extendedCost) + if ((*i)->item == item_id && (*i)->ExtendedCost == extendedCost && (*i)->type == type) return *i; return NULL; } @@ -2544,19 +2544,22 @@ void Creature::SetWalk(bool enable) else m_movementInfo.RemoveMovementFlag(MOVEFLAG_WALK_MODE); - WorldPacket data(enable ? SMSG_SPLINE_MOVE_SET_WALK_MODE : SMSG_SPLINE_MOVE_SET_RUN_MODE, 9); - if (enable) + if (IsInWorld()) { - data.WriteGuidMask<7, 6, 5, 1, 3, 4, 2, 0>(GetObjectGuid()); - data.WriteGuidBytes<4, 2, 1, 6, 5, 0, 7, 3>(GetObjectGuid()); - } - else - { - data.WriteGuidMask<5, 6, 3, 7, 2, 0, 4, 1>(GetObjectGuid()); - data.WriteGuidBytes<7, 0, 4, 6, 5, 1, 2, 3>(GetObjectGuid()); - } + WorldPacket data(enable ? SMSG_SPLINE_MOVE_SET_WALK_MODE : SMSG_SPLINE_MOVE_SET_RUN_MODE, 9); + if (enable) + { + data.WriteGuidMask<7, 6, 5, 1, 3, 4, 2, 0>(GetObjectGuid()); + data.WriteGuidBytes<4, 2, 1, 6, 5, 0, 7, 3>(GetObjectGuid()); + } + else + { + data.WriteGuidMask<5, 6, 3, 7, 2, 0, 4, 1>(GetObjectGuid()); + data.WriteGuidBytes<7, 0, 4, 6, 5, 1, 2, 3>(GetObjectGuid()); + } - SendMessageToSet(&data, true); + SendMessageToSet(&data, true); + } } void Creature::SetLevitate(bool enable) @@ -2566,19 +2569,22 @@ void Creature::SetLevitate(bool enable) else m_movementInfo.RemoveMovementFlag(MOVEFLAG_LEVITATING); - WorldPacket data(enable ? SMSG_SPLINE_MOVE_GRAVITY_DISABLE : SMSG_SPLINE_MOVE_GRAVITY_ENABLE, 9); - if (enable) + if (IsInWorld()) { - data.WriteGuidMask<7, 3, 4, 2, 5, 1, 0, 6>(GetObjectGuid()); - data.WriteGuidBytes<7, 1, 3, 4, 6, 2, 5, 0>(GetObjectGuid()); - } - else - { - data.WriteGuidMask<5, 4, 7, 1, 3, 6, 2, 0>(GetObjectGuid()); - data.WriteGuidBytes<7, 3, 4, 2, 1, 6, 0, 5>(GetObjectGuid()); - } + WorldPacket data(enable ? SMSG_SPLINE_MOVE_GRAVITY_DISABLE : SMSG_SPLINE_MOVE_GRAVITY_ENABLE, 9); + if (enable) + { + data.WriteGuidMask<7, 3, 4, 2, 5, 1, 0, 6>(GetObjectGuid()); + data.WriteGuidBytes<7, 1, 3, 4, 6, 2, 5, 0>(GetObjectGuid()); + } + else + { + data.WriteGuidMask<5, 4, 7, 1, 3, 6, 2, 0>(GetObjectGuid()); + data.WriteGuidBytes<7, 3, 4, 2, 1, 6, 0, 5>(GetObjectGuid()); + } - SendMessageToSet(&data, true); + SendMessageToSet(&data, true); + } } void Creature::SetRoot(bool enable) @@ -2588,19 +2594,22 @@ void Creature::SetRoot(bool enable) else m_movementInfo.RemoveMovementFlag(MOVEFLAG_ROOT); - WorldPacket data(enable ? SMSG_SPLINE_MOVE_ROOT : SMSG_SPLINE_MOVE_UNROOT, 9); - if (enable) + if (IsInWorld()) { - data.WriteGuidMask<5, 4, 6, 1, 3, 7, 2, 0>(GetObjectGuid()); - data.WriteGuidBytes<2, 1, 7, 3, 5, 0, 6, 4>(GetObjectGuid()); - } - else - { - data.WriteGuidMask<0, 1, 6, 5, 3, 2, 7, 4>(GetObjectGuid()); - data.WriteGuidBytes<6, 3, 1, 5, 2, 0, 7, 4>(GetObjectGuid()); - } + WorldPacket data(enable ? SMSG_SPLINE_MOVE_ROOT : SMSG_SPLINE_MOVE_UNROOT, 9); + if (enable) + { + data.WriteGuidMask<5, 4, 6, 1, 3, 7, 2, 0>(GetObjectGuid()); + data.WriteGuidBytes<2, 1, 7, 3, 5, 0, 6, 4>(GetObjectGuid()); + } + else + { + data.WriteGuidMask<0, 1, 6, 5, 3, 2, 7, 4>(GetObjectGuid()); + data.WriteGuidBytes<6, 3, 1, 5, 2, 0, 7, 4>(GetObjectGuid()); + } - SendMessageToSet(&data, true); + SendMessageToSet(&data, true); + } } void Creature::SetWaterWalk(bool enable) @@ -2610,18 +2619,20 @@ void Creature::SetWaterWalk(bool enable) else m_movementInfo.RemoveMovementFlag(MOVEFLAG_WATERWALKING); - WorldPacket data(enable ? SMSG_SPLINE_MOVE_WATER_WALK : SMSG_SPLINE_MOVE_LAND_WALK, 9); - if (enable) + if (IsInWorld()) { - data.WriteGuidMask<6, 1, 4, 2, 3, 7, 5, 0>(GetObjectGuid()); - data.WriteGuidBytes<0, 6, 3, 7, 4, 2, 5, 1>(GetObjectGuid()); - } - else - { - data.WriteGuidMask<5, 0, 4, 6, 7, 2, 3, 1>(GetObjectGuid()); - data.WriteGuidBytes<5, 7, 3, 4, 1, 2, 0, 6>(GetObjectGuid()); - } + WorldPacket data(enable ? SMSG_SPLINE_MOVE_WATER_WALK : SMSG_SPLINE_MOVE_LAND_WALK, 9); + if (enable) + { + data.WriteGuidMask<6, 1, 4, 2, 3, 7, 5, 0>(GetObjectGuid()); + data.WriteGuidBytes<0, 6, 3, 7, 4, 2, 5, 1>(GetObjectGuid()); + } + else + { + data.WriteGuidMask<5, 0, 4, 6, 7, 2, 3, 1>(GetObjectGuid()); + data.WriteGuidBytes<5, 7, 3, 4, 1, 2, 0, 6>(GetObjectGuid()); + } - SendMessageToSet(&data, true); + SendMessageToSet(&data, true); + } } - diff --git a/src/game/Creature.h b/src/game/Creature.h index a3cd20aa1..aca3bc6a4 100644 --- a/src/game/Creature.h +++ b/src/game/Creature.h @@ -310,15 +310,27 @@ enum SelectFlags }; // Vendors + +enum +{ + VENDOR_ITEM_TYPE_NONE = 0, + VENDOR_ITEM_TYPE_ITEM = 1, + VENDOR_ITEM_TYPE_CURRENCY = 2, + VENDOR_ITEM_TYPE_MAX = 3, +}; + struct VendorItem { - VendorItem(uint32 _item, uint32 _maxcount, uint32 _incrtime, uint32 _ExtendedCost) - : item(_item), maxcount(_maxcount), incrtime(_incrtime), ExtendedCost(_ExtendedCost) {} + VendorItem(uint32 _item, uint8 _type, uint32 _maxcount, uint32 _incrtime, uint32 _ExtendedCost) + : item(_item), type(_type), maxcount(_maxcount), incrtime(_incrtime), ExtendedCost(_ExtendedCost) {} uint32 item; - uint32 maxcount; // 0 for infinity item amount + uint8 type; + uint32 maxcount; // 0 for infinity item amount, for type = VENDOR_ITEM_TYPE_CURRENCY, maxcount = currency count uint32 incrtime; // time for restore items amount if maxcount != 0 uint32 ExtendedCost; // index in ItemExtendedCost.dbc + + bool IsCurrency() const { return type == VENDOR_ITEM_TYPE_CURRENCY; } }; typedef std::vector VendorItemList; @@ -333,12 +345,12 @@ struct VendorItemData } bool Empty() const { return m_items.empty(); } uint8 GetItemCount() const { return m_items.size(); } - void AddItem(uint32 item, uint32 maxcount, uint32 ptime, uint32 ExtendedCost) + void AddItem(uint32 item, uint8 type, uint32 maxcount, uint32 ptime, uint32 ExtendedCost) { - m_items.push_back(new VendorItem(item, maxcount, ptime, ExtendedCost)); + m_items.push_back(new VendorItem(item, type, maxcount, ptime, ExtendedCost)); } - bool RemoveItem(uint32 item_id); - VendorItem const* FindItemCostPair(uint32 item_id, uint32 extendedCost) const; + bool RemoveItem(uint32 item_id, uint8 type); + VendorItem const* FindItemCostPair(uint32 item_id, uint8 type, uint32 extendedCost) const; void Clear() { diff --git a/src/game/DBCEnums.h b/src/game/DBCEnums.h index 76fdcf5da..0b77ac773 100644 --- a/src/game/DBCEnums.h +++ b/src/game/DBCEnums.h @@ -386,6 +386,17 @@ enum CurrencyFlags CURRENCY_FLAG_HAS_SEASON_COUNT = 0x80, // guessed }; +enum Currencies +{ + CURRENCY_NONE = 0, + CURRENCY_CONQUEST_POINTS = 390, + CURRENCY_HONOR_POINTS = 392, + CURRENCY_JUSTICE_POINTS = 395, + CURRENCY_VALOR_POINTS = 396, + CURRENCY_CONQUEST_ARENA_META = 483, + CURRENCY_CONQUEST_BG_META = 484, +}; + enum ItemEnchantmentType { ITEM_ENCHANTMENT_TYPE_NONE = 0, diff --git a/src/game/DBCStructure.h b/src/game/DBCStructure.h index 30329ec73..0484cebce 100644 --- a/src/game/DBCStructure.h +++ b/src/game/DBCStructure.h @@ -811,7 +811,7 @@ struct CurrencyTypesEntry { uint32 ID; // 0 //uint32 Category; // 1 - //char* name; // 2 + DBCString name; // 2 //char* iconName; // 3 //uint32 unk4; // 4 //uint32 unk5; // 5 @@ -819,7 +819,7 @@ struct CurrencyTypesEntry uint32 TotalCount; // 7 uint32 WeekCount; // 8 uint32 Flags; // 9 - //char* description; // 10 + //DBCString description; // 10 bool HasPrecision() const { return Flags & CURRENCY_FLAG_HAS_PRECISION; } bool HasSeasonCount() const { return Flags & CURRENCY_FLAG_HAS_SEASON_COUNT; } diff --git a/src/game/DBCfmt.h b/src/game/DBCfmt.h index 95bc175dc..f545e5a62 100644 --- a/src/game/DBCfmt.h +++ b/src/game/DBCfmt.h @@ -43,7 +43,7 @@ const char CreatureFamilyfmt[]="nfifiiiiixsx"; const char CreatureSpellDatafmt[]="niiiixxxx"; const char DungeonEncounterfmt[]="niiiisxx"; const char CreatureTypefmt[]="nxx"; -const char CurrencyTypesfmt[]="nxxxxxxiiix"; +const char CurrencyTypesfmt[]="nxsxxxxiiix"; const char DurabilityCostsfmt[]="niiiiiiiiiiiiiiiiiiiiiiiiiiiii"; const char DurabilityQualityfmt[]="nf"; const char EmotesEntryfmt[]="nxxiiixx"; diff --git a/src/game/ItemHandler.cpp b/src/game/ItemHandler.cpp index ffa5c2aef..d9c19c3b7 100644 --- a/src/game/ItemHandler.cpp +++ b/src/game/ItemHandler.cpp @@ -483,57 +483,15 @@ void WorldSession::HandleBuybackItem(WorldPacket& recv_data) _player->SendBuyError(BUY_ERR_CANT_FIND_ITEM, pCreature, 0, 0); } -void WorldSession::HandleBuyItemInSlotOpcode(WorldPacket& recv_data) -{ - DEBUG_LOG("WORLD: Received CMSG_BUY_ITEM_IN_SLOT"); - ObjectGuid vendorGuid; - ObjectGuid bagGuid; - uint32 item, slot, count; - uint8 bagslot; - - recv_data >> vendorGuid >> item >> slot >> bagGuid >> bagslot >> count; - - // client side expected counting from 1, and we send to client vendorslot+1 already - if (slot > 0) - --slot; - else - return; // cheating - - uint8 bag = NULL_BAG; // init for case invalid bagGUID - - // find bag slot by bag guid - if (bagGuid == _player->GetObjectGuid()) - bag = INVENTORY_SLOT_BAG_0; - else - { - for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) - { - if (Bag* pBag = (Bag*)_player->GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - { - if (bagGuid == pBag->GetObjectGuid()) - { - bag = i; - break; - } - } - } - } - - // bag not found, cheating? - if (bag == NULL_BAG) - return; - - GetPlayer()->BuyItemFromVendorSlot(vendorGuid, slot, item, count, bag, bagslot); -} - void WorldSession::HandleBuyItemOpcode(WorldPacket& recv_data) { - DEBUG_LOG("WORLD: Received CMSG_BUY_ITEM"); - ObjectGuid vendorGuid, otherGuid; + ObjectGuid vendorGuid, bagGuid; uint32 item, slot, count; - uint8 unk1, unk2; + uint8 type, bagSlot; - recv_data >> vendorGuid >> unk1 >> item >> slot >> count >> otherGuid >> unk2; + recv_data >> vendorGuid >> type >> item >> slot >> count >> bagGuid >> bagSlot; + DEBUG_LOG("WORLD: Received CMSG_BUY_ITEM, vendorguid: %s, type: %u, item: %u, slot: %u, count: %u, bagGuid: %s, bagSlog: %u", + vendorGuid.GetString().c_str(), type, item, slot, count, bagGuid.GetString().c_str(), bagSlot); // client side expected counting from 1, and we send to client vendorslot+1 already if (slot > 0) @@ -541,7 +499,41 @@ void WorldSession::HandleBuyItemOpcode(WorldPacket& recv_data) else return; // cheating - GetPlayer()->BuyItemFromVendorSlot(vendorGuid, slot, item, count, NULL_BAG, NULL_SLOT); + switch(type) + { + case VENDOR_ITEM_TYPE_NONE: + break; + case VENDOR_ITEM_TYPE_ITEM: + { + uint8 bag = NULL_BAG; // init for case invalid bagGUID + + // find bag slot by bag guid + if (bagGuid == _player->GetObjectGuid()) + bag = INVENTORY_SLOT_BAG_0; + else + { + for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) + { + if (Bag* pBag = (Bag*)_player->GetItemByPos(INVENTORY_SLOT_BAG_0, i)) + { + if (bagGuid == pBag->GetObjectGuid()) + { + bag = i; + break; + } + } + } + } + + GetPlayer()->BuyItemFromVendorSlot(vendorGuid, slot, item, count, bag, bagSlot); + break; + } + case VENDOR_ITEM_TYPE_CURRENCY: + { + GetPlayer()->BuyCurrencyFromVendorSlot(vendorGuid, slot, item, count); + break; + } + } } void WorldSession::HandleListInventoryOpcode(WorldPacket& recv_data) @@ -597,10 +589,15 @@ void WorldSession::SendListInventory(ObjectGuid vendorguid) if (crItem) { - uint32 itemId = crItem->item; - ItemPrototype const* pProto = ObjectMgr::GetItemPrototype(itemId); - if (pProto) + uint32 price, displayId, buyCount, maxDurability; + int32 maxCount; + + if (crItem->type == VENDOR_ITEM_TYPE_ITEM) { + ItemPrototype const * pProto = ObjectMgr::GetItemPrototype(crItem->item); + if (!pProto) + continue; + if (!_player->isGameMaster()) { // class wrong item skip only for bindable case @@ -625,35 +622,60 @@ void WorldSession::SendListInventory(ObjectGuid vendorguid) if (pProto->RequiredReputationFaction && uint32(_player->GetReputationRank(pProto->RequiredReputationFaction)) >= pProto->RequiredReputationRank) { // checked at convert data loading as existed - if (uint32 newItemId = sObjectMgr.GetItemConvert(itemId, _player->getRaceMask())) + if (uint32 newItemId = sObjectMgr.GetItemConvert(crItem->item, _player->getRaceMask())) pProto = ObjectMgr::GetItemPrototype(newItemId); } } ++count; - if (count >= MAX_VENDOR_ITEMS) break; - bitFlags.push_back(crItem->ExtendedCost == 0); - bitFlags.push_back(true); // unk - // reputation discount - uint32 price = (crItem->ExtendedCost == 0 || pProto->Flags2 & ITEM_FLAG2_EXT_COST_REQUIRES_GOLD) ? uint32(floor(pProto->BuyPrice * discountMod)) : 0; + maxDurability = pProto->MaxDurability; + price = (crItem->ExtendedCost == 0 || pProto->Flags2 & ITEM_FLAG2_EXT_COST_REQUIRES_GOLD) ? uint32(floor(pProto->BuyPrice * discountMod)) : 0; + displayId = pProto->DisplayInfoID; + maxCount = crItem->maxcount <= 0 ? -1 : int32(pCreature->GetVendorItemCurrentCount(crItem)); + buyCount = pProto->BuyCount; - buffer << uint32(vendorslot + 1); // client size expected counting from 1 - buffer << uint32(pProto->MaxDurability); - - if (crItem->ExtendedCost) - buffer << uint32(crItem->ExtendedCost); - - buffer << uint32(itemId); - buffer << uint32(1); // type == item - buffer << uint32(price); - buffer << uint32(pProto->DisplayInfoID); - buffer << uint32(crItem->maxcount <= 0 ? 0xFFFFFFFF : pCreature->GetVendorItemCurrentCount(crItem)); - buffer << uint32(pProto->BuyCount); } + else if (crItem->type == VENDOR_ITEM_TYPE_CURRENCY) + { + CurrencyTypesEntry const * pCurrency = sCurrencyTypesStore.LookupEntry(crItem->item); + if (!pCurrency) + continue; + + if (pCurrency->ID == CURRENCY_CONQUEST_ARENA_META || pCurrency->ID == CURRENCY_CONQUEST_BG_META) + continue; + + ++count; + if (count >= MAX_VENDOR_ITEMS) + break; + + maxDurability = 0; + price = 0; + displayId = 0; + maxCount = -1; + buyCount = crItem->maxcount; + } + else + continue; + + bitFlags.push_back(crItem->ExtendedCost == 0); + bitFlags.push_back(true); // unk + + buffer << uint32(vendorslot + 1); // client size expected counting from 1 + buffer << uint32(maxDurability); + + if (crItem->ExtendedCost) + buffer << uint32(crItem->ExtendedCost); + + buffer << uint32(crItem->item); + buffer << uint32(crItem->type); + buffer << uint32(price); + buffer << uint32(displayId); + buffer << int32(maxCount); + buffer << uint32(buyCount); } } diff --git a/src/game/Language.h b/src/game/Language.h index cb0c260ce..fe78a11bd 100644 --- a/src/game/Language.h +++ b/src/game/Language.h @@ -257,7 +257,7 @@ enum MangosStrings LANG_COMMAND_TARGETOBJNOTFOUND = 266, LANG_COMMAND_GOOBJNOTFOUND = 267, LANG_COMMAND_GOCREATNOTFOUND = 268, - // 269, not used + LANG_CURRENCY_NOT_FOUND = 269, LANG_COMMAND_DELCREATMESSAGE = 270, LANG_COMMAND_CREATUREMOVED = 271, LANG_COMMAND_CREATUREATSAMEMAP = 272, @@ -271,7 +271,7 @@ enum MangosStrings LANG_COMMAND_ADDVENDORITEMITEMS = 280, LANG_COMMAND_KICKSELF = 281, LANG_COMMAND_KICKMESSAGE = 282, - // 283, not used + LANG_VENDOR_META_CURRENCY_NOT_ALLOWED = 283, LANG_COMMAND_WHISPERACCEPTING = 284, LANG_COMMAND_WHISPERON = 285, LANG_COMMAND_WHISPEROFF = 286, @@ -965,6 +965,8 @@ enum MangosStrings LANG_NPC_EVENTAI_PHASE = 1506, LANG_NPC_EVENTAI_MOVE = 1507, LANG_NPC_EVENTAI_COMBAT = 1508, + LANG_VENDOR_WRONG_ITEM_TYPE = 1509, + LANG_VENDOR_WRONG_CURRENCY_MAXCOUNT = 1510, // Room for more Level 2 1509-1599 not used // Outdoor PvP diff --git a/src/game/Level2.cpp b/src/game/Level2.cpp index e4ded3806..db91bea5c 100644 --- a/src/game/Level2.cpp +++ b/src/game/Level2.cpp @@ -1600,6 +1600,43 @@ bool ChatHandler::HandleNpcAddCommand(char* args) return true; } +// add currency in vendorlist +bool ChatHandler::HandleNpcAddVendorCurrencyCommand(char* args) +{ + uint32 currencyId; + if (!ExtractUint32KeyFromLink(&args, "Hcurrency", currencyId)) + { + SendSysMessage(LANG_COMMAND_NEEDITEMSEND); + SetSentErrorMessage(true); + return false; + } + + uint32 maxcount; + if (!ExtractUInt32(&args, maxcount)) + return false; + + uint32 extendedcost; + if (!ExtractUInt32(&args, extendedcost)) + return false; + + Creature* vendor = getSelectedCreature(); + + uint32 vendor_entry = vendor ? vendor->GetEntry() : 0; + + if (!sObjectMgr.IsVendorItemValid(false, "npc_vendor", vendor_entry, currencyId, VENDOR_ITEM_TYPE_CURRENCY, maxcount, 0, extendedcost, m_session->GetPlayer())) + { + SetSentErrorMessage(true); + return false; + } + + sObjectMgr.AddVendorItem(vendor_entry, currencyId, VENDOR_ITEM_TYPE_CURRENCY, maxcount, 0, extendedcost); + + std::string name = sCurrencyTypesStore.LookupEntry(currencyId)->name[0]; + + PSendSysMessage(LANG_ITEM_ADDED_TO_LIST, currencyId, name.c_str(), maxcount, 0, extendedcost); + return true; +} + // add item in vendorlist bool ChatHandler::HandleNpcAddVendorItemCommand(char* args) { @@ -1627,17 +1664,52 @@ bool ChatHandler::HandleNpcAddVendorItemCommand(char* args) uint32 vendor_entry = vendor ? vendor->GetEntry() : 0; - if (!sObjectMgr.IsVendorItemValid(false, "npc_vendor", vendor_entry, itemId, maxcount, incrtime, extendedcost, m_session->GetPlayer())) + if (!sObjectMgr.IsVendorItemValid(false, "npc_vendor", vendor_entry, itemId, VENDOR_ITEM_TYPE_ITEM, maxcount, incrtime, extendedcost, m_session->GetPlayer())) { SetSentErrorMessage(true); return false; } - sObjectMgr.AddVendorItem(vendor_entry, itemId, maxcount, incrtime, extendedcost); + sObjectMgr.AddVendorItem(vendor_entry, itemId, VENDOR_ITEM_TYPE_ITEM, maxcount, incrtime, extendedcost); - ItemPrototype const* pProto = ObjectMgr::GetItemPrototype(itemId); + std::string name = ObjectMgr::GetItemPrototype(itemId)->Name1; - PSendSysMessage(LANG_ITEM_ADDED_TO_LIST, itemId, pProto->Name1, maxcount, incrtime, extendedcost); + PSendSysMessage(LANG_ITEM_ADDED_TO_LIST, itemId, name.c_str(), maxcount, incrtime, extendedcost); + return true; +} + +// del currency from vendor list +bool ChatHandler::HandleNpcDelVendorCurrencyCommand(char* args) +{ + if (!*args) + return false; + + Creature* vendor = getSelectedCreature(); + if (!vendor || !vendor->isVendor()) + { + SendSysMessage(LANG_COMMAND_VENDORSELECTION); + SetSentErrorMessage(true); + return false; + } + + uint32 itemId; + if (!ExtractUint32KeyFromLink(&args, "Hcurrency", itemId)) + { + SendSysMessage(LANG_COMMAND_NEEDITEMSEND); + SetSentErrorMessage(true); + return false; + } + + if (!sObjectMgr.RemoveVendorItem(vendor->GetEntry(), itemId, VENDOR_ITEM_TYPE_CURRENCY)) + { + PSendSysMessage(LANG_ITEM_NOT_IN_LIST, itemId, true); + SetSentErrorMessage(true); + return false; + } + + std::string name = sCurrencyTypesStore.LookupEntry(itemId)->name[0]; + + PSendSysMessage(LANG_ITEM_DELETED_FROM_LIST, itemId, name.c_str()); return true; } @@ -1663,16 +1735,16 @@ bool ChatHandler::HandleNpcDelVendorItemCommand(char* args) return false; } - if (!sObjectMgr.RemoveVendorItem(vendor->GetEntry(), itemId)) + if (!sObjectMgr.RemoveVendorItem(vendor->GetEntry(), itemId, VENDOR_ITEM_TYPE_ITEM)) { - PSendSysMessage(LANG_ITEM_NOT_IN_LIST, itemId); + PSendSysMessage(LANG_ITEM_NOT_IN_LIST, itemId, false); SetSentErrorMessage(true); return false; } - ItemPrototype const* pProto = ObjectMgr::GetItemPrototype(itemId); + std::string name = ObjectMgr::GetItemPrototype(itemId)->Name1; - PSendSysMessage(LANG_ITEM_DELETED_FROM_LIST, itemId, pProto->Name1); + PSendSysMessage(LANG_ITEM_DELETED_FROM_LIST, itemId, name.c_str()); return true; } diff --git a/src/game/ObjectMgr.cpp b/src/game/ObjectMgr.cpp index acea0f93c..d9c3dff2f 100755 --- a/src/game/ObjectMgr.cpp +++ b/src/game/ObjectMgr.cpp @@ -8746,17 +8746,18 @@ void ObjectMgr::LoadVendors(char const* tableName, bool isTemplates) Field* fields = result->Fetch(); uint32 entry = fields[0].GetUInt32(); - uint32 item_id = fields[1].GetUInt32(); + uint32 item_id = abs(fields[1].GetInt32()); + uint8 type = fields[1].GetInt32() > 0 ? VENDOR_ITEM_TYPE_ITEM : VENDOR_ITEM_TYPE_CURRENCY; uint32 maxcount = fields[2].GetUInt32(); uint32 incrtime = fields[3].GetUInt32(); uint32 ExtendedCost = fields[4].GetUInt32(); - if (!IsVendorItemValid(isTemplates, tableName, entry, item_id, maxcount, incrtime, ExtendedCost, NULL, &skip_vendors)) + if (!IsVendorItemValid(isTemplates, tableName, entry, item_id, type, maxcount, incrtime, ExtendedCost, NULL, &skip_vendors)) continue; VendorItemData& vList = vendorList[entry]; - vList.AddItem(item_id, maxcount, incrtime, ExtendedCost); + vList.AddItem(item_id, type, maxcount, incrtime, ExtendedCost); ++count; } @@ -9183,30 +9184,31 @@ void ObjectMgr::LoadGossipMenus() sLog.outErrorDb("Table `gossip_scripts` contains unused script, id %u.", *itr); } -void ObjectMgr::AddVendorItem(uint32 entry, uint32 item, uint32 maxcount, uint32 incrtime, uint32 extendedcost) +void ObjectMgr::AddVendorItem(uint32 entry, uint32 item, uint8 type, uint32 maxcount, uint32 incrtime, uint32 extendedcost) { VendorItemData& vList = m_mCacheVendorItemMap[entry]; - vList.AddItem(item, maxcount, incrtime, extendedcost); + vList.AddItem(item, type, maxcount, incrtime, extendedcost); - WorldDatabase.PExecuteLog("INSERT INTO npc_vendor (entry,item,maxcount,incrtime,extendedcost) VALUES('%u','%u','%u','%u','%u')", entry, item, maxcount, incrtime, extendedcost); + WorldDatabase.PExecuteLog("INSERT INTO npc_vendor (entry,item,maxcount,incrtime,extendedcost) VALUES('%u','%i','%u','%u','%u')", entry, type == VENDOR_ITEM_TYPE_CURRENCY ? -int32(item) : item, maxcount, incrtime, extendedcost); } -bool ObjectMgr::RemoveVendorItem(uint32 entry, uint32 item) +bool ObjectMgr::RemoveVendorItem(uint32 entry, uint32 item, uint8 type) { CacheVendorItemMap::iterator iter = m_mCacheVendorItemMap.find(entry); if (iter == m_mCacheVendorItemMap.end()) return false; - if (!iter->second.RemoveItem(item)) + if (!iter->second.RemoveItem(item, type)) return false; - WorldDatabase.PExecuteLog("DELETE FROM npc_vendor WHERE entry='%u' AND item='%u'", entry, item); + WorldDatabase.PExecuteLog("DELETE FROM npc_vendor WHERE entry='%u' AND item='%i'", entry, type == VENDOR_ITEM_TYPE_CURRENCY ? -int32(item) : item); return true; } -bool ObjectMgr::IsVendorItemValid(bool isTemplate, char const* tableName, uint32 vendor_entry, uint32 item_id, uint32 maxcount, uint32 incrtime, uint32 ExtendedCost, Player* pl, std::set* skip_vendors) const +bool ObjectMgr::IsVendorItemValid(bool isTemplate, char const* tableName, uint32 vendor_entry, uint32 item_id, uint8 type, uint32 maxcount, uint32 incrtime, uint32 ExtendedCost, Player* pl, std::set* skip_vendors) const { char const* idStr = isTemplate ? "vendor template" : "vendor"; + char const* nameStr = type == VENDOR_ITEM_TYPE_CURRENCY ? "Currency" : "Item"; CreatureInfo const* cInfo = NULL; if (!isTemplate) @@ -9237,14 +9239,51 @@ bool ObjectMgr::IsVendorItemValid(bool isTemplate, char const* tableName, uint32 } } - if (!GetItemPrototype(item_id)) + CurrencyTypesEntry const * currencyEntry; + if (type == VENDOR_ITEM_TYPE_ITEM) + { + if (!GetItemPrototype(item_id)) + { + if (pl) + ChatHandler(pl).PSendSysMessage(LANG_ITEM_NOT_FOUND, item_id); + else + sLog.outErrorDb("Table `%s` for %s %u contains nonexistent item (%u), ignoring", + tableName, idStr, vendor_entry, item_id); + return false; + } + } + else if (type == VENDOR_ITEM_TYPE_CURRENCY) + { + currencyEntry = sCurrencyTypesStore.LookupEntry(item_id); + if (!currencyEntry) + { + if (pl) + ChatHandler(pl).PSendSysMessage(LANG_CURRENCY_NOT_FOUND, item_id); + else + sLog.outErrorDb("Table `%s` for %s %u contains nonexistent currency (%u), ignoring", + tableName, idStr, vendor_entry, item_id); + return false; + } + else + { + if (currencyEntry->ID == CURRENCY_CONQUEST_ARENA_META || currencyEntry->ID == CURRENCY_CONQUEST_BG_META) + { + if (pl) + ChatHandler(pl).PSendSysMessage(LANG_VENDOR_META_CURRENCY_NOT_ALLOWED, item_id); + else + sLog.outErrorDb("Table `%s` for %s %u contains not allowed meta currency (%u), ignoring", + tableName, idStr, vendor_entry, item_id); + return false; + } + } + } + else { if (pl) - ChatHandler(pl).PSendSysMessage(LANG_ITEM_NOT_FOUND, item_id); + ChatHandler(pl).PSendSysMessage(LANG_VENDOR_WRONG_ITEM_TYPE, item_id, type); else - sLog.outErrorDb("Table `%s` for %s %u contain nonexistent item (%u), ignoring", - tableName, idStr, vendor_entry, item_id); - return false; + sLog.outErrorDb("Table `%s` for %s %u contains nonexistent vendor item type %u (entry %u), ignoring", + tableName, idStr, vendor_entry, type, item_id); } if (ExtendedCost && !sItemExtendedCostStore.LookupEntry(ExtendedCost)) @@ -9252,28 +9291,43 @@ bool ObjectMgr::IsVendorItemValid(bool isTemplate, char const* tableName, uint32 if (pl) ChatHandler(pl).PSendSysMessage(LANG_EXTENDED_COST_NOT_EXIST, ExtendedCost); else - sLog.outErrorDb("Table `%s` contain item (Entry: %u) with wrong ExtendedCost (%u) for %s %u, ignoring", - tableName, item_id, ExtendedCost, idStr, vendor_entry); + sLog.outErrorDb("Table `%s` contains %s (Entry: %u) with wrong ExtendedCost (%u) for %s %u, ignoring", + tableName, nameStr, item_id, ExtendedCost, idStr, vendor_entry); return false; } - if (maxcount > 0 && incrtime == 0) + if (type == VENDOR_ITEM_TYPE_ITEM) { - if (pl) - ChatHandler(pl).PSendSysMessage("MaxCount!=0 (%u) but IncrTime==0", maxcount); - else - sLog.outErrorDb("Table `%s` has `maxcount` (%u) for item %u of %s %u but `incrtime`=0, ignoring", - tableName, maxcount, item_id, idStr, vendor_entry); - return false; + if (maxcount > 0 && incrtime == 0) + { + if (pl) + ChatHandler(pl).PSendSysMessage("MaxCount!=0 (%u) but IncrTime==0", maxcount); + else + sLog.outErrorDb("Table `%s` has `maxcount` (%u) for %s %u of %s %u but `incrtime`=0, ignoring", + tableName, maxcount, nameStr, item_id, idStr, vendor_entry); + return false; + } + else if (maxcount == 0 && incrtime > 0) + { + if (pl) + ChatHandler(pl).PSendSysMessage("MaxCount==0 but IncrTime<>=0"); + else + sLog.outErrorDb("Table `%s` has `maxcount`=0 for %s %u of %s %u but `incrtime`<>0, ignoring", + tableName, nameStr, item_id, idStr, vendor_entry); + return false; + } } - else if (maxcount == 0 && incrtime > 0) + else if (type == VENDOR_ITEM_TYPE_CURRENCY) { - if (pl) - ChatHandler(pl).PSendSysMessage("MaxCount==0 but IncrTime<>=0"); - else - sLog.outErrorDb("Table `%s` has `maxcount`=0 for item %u of %s %u but `incrtime`<>0, ignoring", - tableName, item_id, idStr, vendor_entry); - return false; + if (maxcount < uint32(currencyEntry->GetPrecision())) + { + if (pl) + ChatHandler(pl).PSendSysMessage(LANG_VENDOR_WRONG_CURRENCY_MAXCOUNT, item_id, uint32(currencyEntry->GetPrecision())); + else + sLog.outErrorDb("Table `%s` contains %s (Entry: %u) with too low maxcount. Maxcount for currencies is buycount, so it can't be 0 or less than that's currency precision (%u), ignoring", + tableName, nameStr, item_id, uint32(currencyEntry->GetPrecision())); + return false; + } } VendorItemData const* vItems = isTemplate ? GetNpcVendorTemplateItemList(vendor_entry) : GetNpcVendorItemList(vendor_entry); @@ -9282,30 +9336,30 @@ bool ObjectMgr::IsVendorItemValid(bool isTemplate, char const* tableName, uint32 if (!vItems && !tItems) return true; // later checks for non-empty lists - if (vItems && vItems->FindItemCostPair(item_id, ExtendedCost)) + if (vItems && vItems->FindItemCostPair(item_id, type, ExtendedCost)) { if (pl) - ChatHandler(pl).PSendSysMessage(LANG_ITEM_ALREADY_IN_LIST, item_id, ExtendedCost); + ChatHandler(pl).PSendSysMessage(LANG_ITEM_ALREADY_IN_LIST, item_id, type == VENDOR_ITEM_TYPE_CURRENCY, ExtendedCost); else - sLog.outErrorDb("Table `%s` has duplicate items %u (with extended cost %u) for %s %u, ignoring", - tableName, item_id, ExtendedCost, idStr, vendor_entry); + sLog.outErrorDb("Table `%s` has duplicate %s %u (with extended cost %u) for %s %u, ignoring", + tableName, nameStr, item_id, ExtendedCost, idStr, vendor_entry); return false; } if (!isTemplate) { - if (tItems && tItems->FindItemCostPair(item_id, ExtendedCost)) + if (tItems && tItems->FindItemCostPair(item_id, type, ExtendedCost)) { if (pl) - ChatHandler(pl).PSendSysMessage(LANG_ITEM_ALREADY_IN_LIST, item_id, ExtendedCost); + ChatHandler(pl).PSendSysMessage(LANG_ITEM_ALREADY_IN_LIST, item_id, type == VENDOR_ITEM_TYPE_CURRENCY, ExtendedCost); else { if (!cInfo->vendorId) - sLog.outErrorDb("Table `%s` has duplicate items %u (with extended cost %u) for %s %u, ignoring", - tableName, item_id, ExtendedCost, idStr, vendor_entry); + sLog.outErrorDb("Table `%s` has duplicate %s %u (with extended cost %u) for %s %u, ignoring", + tableName, nameStr, item_id, ExtendedCost, idStr, vendor_entry); else - sLog.outErrorDb("Table `%s` has duplicate items %u (with extended cost %u) for %s %u (or possible in vendor template %u), ignoring", - tableName, item_id, ExtendedCost, idStr, vendor_entry, cInfo->vendorId); + sLog.outErrorDb("Table `%s` has duplicate %s %u (with extended cost %u) for %s %u (or possible in vendor template %u), ignoring", + tableName, nameStr, item_id, ExtendedCost, idStr, vendor_entry, cInfo->vendorId); } return false; } @@ -9319,7 +9373,7 @@ bool ObjectMgr::IsVendorItemValid(bool isTemplate, char const* tableName, uint32 if (pl) ChatHandler(pl).SendSysMessage(LANG_COMMAND_ADDVENDORITEMITEMS); else - sLog.outErrorDb("Table `%s` has too many items (%u >= %i) for %s %u, ignoring", + sLog.outErrorDb("Table `%s` has too many entries (%u >= %i) for %s %u, ignoring", tableName, countItems, MAX_VENDOR_ITEMS, idStr, vendor_entry); return false; } diff --git a/src/game/ObjectMgr.h b/src/game/ObjectMgr.h index 4b1513662..8e8aa41b5 100755 --- a/src/game/ObjectMgr.h +++ b/src/game/ObjectMgr.h @@ -1017,9 +1017,9 @@ class ObjectMgr return &iter->second; } - void AddVendorItem(uint32 entry, uint32 item, uint32 maxcount, uint32 incrtime, uint32 ExtendedCost); - bool RemoveVendorItem(uint32 entry, uint32 item); - bool IsVendorItemValid(bool isTemplate, char const* tableName, uint32 vendor_entry, uint32 item, uint32 maxcount, uint32 ptime, uint32 ExtendedCost, Player* pl = NULL, std::set* skip_vendors = NULL) const; + void AddVendorItem(uint32 entry, uint32 item, uint8 type, uint32 maxcount, uint32 incrtime, uint32 ExtendedCost); + bool RemoveVendorItem(uint32 entry, uint32 item, uint8 type); + bool IsVendorItemValid(bool isTemplate, char const* tableName, uint32 vendor_entry, uint32 item, uint8 type, uint32 maxcount, uint32 ptime, uint32 ExtendedCost, Player* pl = NULL, std::set* skip_vendors = NULL) const; int GetOrNewIndexForLocale(LocaleConstant loc); diff --git a/src/game/Player.cpp b/src/game/Player.cpp index 541f2d60b..b764a3956 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -19635,6 +19635,106 @@ bool Player::BuyItemFromVendorSlot(ObjectGuid vendorGuid, uint32 vendorslot, uin return crItem->maxcount != 0; } +bool Player::BuyCurrencyFromVendorSlot(ObjectGuid vendorGuid, uint32 vendorslot, uint32 currencyId, uint8 count) +{ + // cheating attempt + if (count < 1) count = 1; + + if (!isAlive()) + return false; + + CurrencyTypesEntry const* pCurrency = sCurrencyTypesStore.LookupEntry(currencyId); + if (!pCurrency) + return false; + + if (currencyId == CURRENCY_CONQUEST_ARENA_META || currencyId == CURRENCY_CONQUEST_BG_META) + return false; + + Creature* pCreature = GetNPCIfCanInteractWith(vendorGuid, UNIT_NPC_FLAG_VENDOR); + if (!pCreature) + { + DEBUG_LOG("WORLD: BuyCurrencyFromVendorSlot - %s not found or you can't interact with him.", vendorGuid.GetString().c_str()); + return false; + } + + VendorItemData const* vItems = pCreature->GetVendorItems(); + VendorItemData const* tItems = pCreature->GetVendorTemplateItems(); + if ((!vItems || vItems->Empty()) && (!tItems || tItems->Empty())) + return false; + + uint32 vCount = vItems ? vItems->GetItemCount() : 0; + uint32 tCount = tItems ? tItems->GetItemCount() : 0; + + if (vendorslot >= vCount + tCount) + return false; + + VendorItem const* crItem = vendorslot < vCount ? vItems->GetItem(vendorslot) : tItems->GetItem(vendorslot - vCount); + if (!crItem) // store diff item (cheating) + return false; + + if (crItem->item != currencyId) // store diff item (cheating) + return false; + + if (crItem->maxcount != count) + { + DEBUG_LOG("WORLD: BuyCurrencyFromVendorSlot - %s: count (%u) != crItem->maxcount (%u) for currency %u and player %s.", + vendorGuid.GetString().c_str(), count, crItem->maxcount, currencyId, GetGuidStr().c_str()); + + count = crItem->maxcount; + } + + if (uint32 extendedCostId = crItem->ExtendedCost) + { + ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(extendedCostId); + if (!iece) + { + sLog.outError("WORLD: BuyCurrencyFromVendorSlot: Currency %u have wrong ExtendedCost field value %u for %s", currencyId, extendedCostId, vendorGuid.GetString().c_str()); + return false; + } + + // honor points price + if (GetHonorPoints() < (iece->reqhonorpoints * count)) + { + SendEquipError(EQUIP_ERR_NOT_ENOUGH_HONOR_POINTS, NULL, NULL); + return false; + } + + // arena points price + if (GetArenaPoints() < (iece->reqarenapoints * count)) + { + SendEquipError(EQUIP_ERR_NOT_ENOUGH_ARENA_POINTS, NULL, NULL); + return false; + } + + // item base price + for (uint8 i = 0; i < MAX_EXTENDED_COST_ITEMS; ++i) + { + if (iece->reqitem[i] && !HasItemCount(iece->reqitem[i], iece->reqitemcount[i] * count)) + { + SendEquipError(EQUIP_ERR_VENDOR_MISSING_TURNINS, NULL, NULL); + return false; + } + } + + // check for personal arena rating requirement + if (GetMaxPersonalArenaRatingRequirement(iece->reqarenaslot) < iece->reqpersonalarenarating) + { + // probably not the proper equip err + SendEquipError(EQUIP_ERR_CANT_EQUIP_RANK, NULL, NULL); + return false; + } + } + + // TODO: check if player already has maximum currency + + // TODO: modify currency + + DEBUG_LOG("WORLD: BuyCurrencyFromVendorSlot - %s: Player %s buys currency %u amount %u.", + vendorGuid.GetString().c_str(), GetGuidStr().c_str(), currencyId, count); + + return true; +} + uint32 Player::GetMaxPersonalArenaRatingRequirement(uint32 minarenaslot) { // returns the maximal personal arena rating that can be used to purchase items requiring this condition diff --git a/src/game/Player.h b/src/game/Player.h index 39cb72d9b..060d363bf 100644 --- a/src/game/Player.h +++ b/src/game/Player.h @@ -1286,6 +1286,7 @@ class MANGOS_DLL_SPEC Player : public Unit } void SendNewItem(Item* item, uint32 count, bool received, bool created, bool broadcast = false); bool BuyItemFromVendorSlot(ObjectGuid vendorGuid, uint32 vendorslot, uint32 item, uint8 count, uint8 bag, uint8 slot); + bool BuyCurrencyFromVendorSlot(ObjectGuid vendorGuid, uint32 vendorslot, uint32 currencyId, uint8 count); float GetReputationPriceDiscount(Creature const* pCreature) const; diff --git a/src/game/SharedDefines.h b/src/game/SharedDefines.h index 8cb657aa9..975712436 100644 --- a/src/game/SharedDefines.h +++ b/src/game/SharedDefines.h @@ -2758,6 +2758,7 @@ enum ChatMsg enum ChatLinkColors { + CHAT_LINK_COLOR_CURRENCY = 0xff00aa00, // green CHAT_LINK_COLOR_TRADE = 0xffffd000, // orange CHAT_LINK_COLOR_TALENT = 0xff4e96f7, // blue CHAT_LINK_COLOR_SPELL = 0xff71d5ff, // bright blue diff --git a/src/game/WorldSession.h b/src/game/WorldSession.h index 042ff86ae..dc8c95413 100644 --- a/src/game/WorldSession.h +++ b/src/game/WorldSession.h @@ -639,7 +639,6 @@ class MANGOS_DLL_SPEC WorldSession void HandleDestroyItemOpcode(WorldPacket& recvPacket); void HandleAutoEquipItemOpcode(WorldPacket& recvPacket); void HandleSellItemOpcode(WorldPacket& recvPacket); - void HandleBuyItemInSlotOpcode(WorldPacket& recvPacket); void HandleBuyItemOpcode(WorldPacket& recvPacket); void HandleListInventoryOpcode(WorldPacket& recvPacket); void HandleAutoStoreBagItemOpcode(WorldPacket& recvPacket); diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index f12779a5e..432fa658f 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ -#define REVISION_NR "0168" +#define REVISION_NR "0169" #endif // __REVISION_NR_H__ diff --git a/src/shared/revision_sql.h b/src/shared/revision_sql.h index d19347972..2b56d523a 100644 --- a/src/shared/revision_sql.h +++ b/src/shared/revision_sql.h @@ -1,6 +1,6 @@ #ifndef __REVISION_SQL_H__ #define __REVISION_SQL_H__ #define REVISION_DB_CHARACTERS "required_0099_xxxxx_01_characters_character_phase_data" - #define REVISION_DB_MANGOS "required_0168_xxxxx_01_mangos_playercreateinfo_spell" + #define REVISION_DB_MANGOS "required_0169_xxxxx_04_mangos_command" #define REVISION_DB_REALMD "required_0014_xxxxx_01_realmd_account_access" #endif // __REVISION_SQL_H__