diff --git a/data/XML/mounts.xml b/data/XML/mounts.xml index 0731871ed4..c604442e4f 100644 --- a/data/XML/mounts.xml +++ b/data/XML/mounts.xml @@ -200,4 +200,6 @@ + + diff --git a/data/XML/outfits.xml b/data/XML/outfits.xml index 3d97ac4655..4041d2c1dc 100644 --- a/data/XML/outfits.xml +++ b/data/XML/outfits.xml @@ -107,6 +107,8 @@ + + @@ -215,4 +217,6 @@ + + diff --git a/data/items/items.otb b/data/items/items.otb index 3def2d5c66..a76ae7874a 100644 Binary files a/data/items/items.otb and b/data/items/items.otb differ diff --git a/data/items/items.xml b/data/items/items.xml index 6eed1612a2..42fd8ec1bf 100644 --- a/data/items/items.xml +++ b/data/items/items.xml @@ -4420,7 +4420,7 @@ - + @@ -42934,4 +42934,10 @@ + + + + + + diff --git a/data/lib/core/achievements.lua b/data/lib/core/achievements.lua index 2cf952c6b1..4f763c5aa2 100644 --- a/data/lib/core/achievements.lua +++ b/data/lib/core/achievements.lua @@ -589,6 +589,16 @@ achievements = { [494] = {clientId = 528, name = "Shell we take a Ride", grade = 1, points = 3, description = "Equipped with the shell of a tortoise and claws of a lobster this insect like companion will help you through every hardship."}, [495] = {clientId = 530, name = "Some Like It Hot", grade = 1, points = 2, description = "You have braved the searing heat in the tunnels deep below Kazordoon and vanquished the Brainstealer. The voices inside your head are finally silenced."}, [496] = {clientId = 427, name = "Woodcarver", grade = 1, points = 3, secret = true, description = "You defeated Megasylvan Yselda in the wake of the sleeping carnisylvan menace deep under Bounac."}, + + -- 12.90 + [502] = {clientId = 534, name = "Friendly Fire", grade = 1, points = 1, description = "You mastered the fire and tamed a supervulcano!"}, + [503] = {clientId = 535, name = "Wedding Planner", grade = 1, points = 3, description = "Alas! What could be more beautiful and satisfying than bringing two loving hearts together? So romantic!"}, + [504] = {clientId = 536, name = "Beaver Away", grade = 1, points = 1, description = "You really were as busy as a beaver in order to help the nagas. Enjoy some eager company!"}, + [505] = {clientId = 537, name = "Snake Pit", grade = 1, points = 1, description = "Mysterious nagas, a vibrant jungle and a sinking island - you really know every corner of Marapur now."}, + [506] = {clientId = 538, name = "Royalty of Hazard", grade = 1, points = 1, description = "For some it can't be hazardous enough."}, + [507] = {clientId = 539, name = "Measuring the World", grade = 1, points = 3, description = "Step by step you discovered many of the secrets hidden in the world, thus gaining the right to wear the Discoverer outfit and hat. Made-to-measure for a brave traveller of the wilds."}, + [508] = {clientId = 540, name = "Ripp-Ripp Hooray!", grade = 1, points = 3, description = "Don't get carried away by your success. Get carried away by your ripptor."}, + [509] = {clientId = 531, name = "First Achievement", grade = 1, points = 1, secret = true, description = "Congratulations to your very first achievement! ... Well, not really. But imagine, it is. Because at this point during your journey into the past, achievements have been introduced."}, } ACHIEVEMENT_FIRST = 1 diff --git a/data/scripts/actions/others/taming.lua b/data/scripts/actions/others/taming.lua index b98abd99d3..8341f3bda7 100644 --- a/data/scripts/actions/others/taming.lua +++ b/data/scripts/actions/others/taming.lua @@ -359,6 +359,14 @@ local config = { {sound = "GRRRRRRRRR", text = "The noble lion majestically rejects your amulet."} }, success = {sound = "Grrr", text = "The noble lion will now accompany you as a friend and ally."} + }, + [44579] = { -- colourful water lily + name = "giant beaver", + id = 201, + type = TYPE_MONSTER, + achievement = "Beaver Away", + chance = 100, + success = {sound = "Grrr!", text = "You tamed the giant beaver."} } } diff --git a/src/const.h b/src/const.h index 6dbc04df45..56484b1949 100644 --- a/src/const.h +++ b/src/const.h @@ -172,6 +172,7 @@ enum MagicEffectClasses : uint8_t CONST_ME_HORESTIS = 238, CONST_ME_DEVOVORGA = 239, CONST_ME_FERUMBRAS_2 = 240, + CONST_ME_FOAM = 241, }; enum ShootType_t : uint8_t @@ -581,6 +582,8 @@ enum item_t : uint16_t ITEM_DEPOT_BOX_XVI = 25468, ITEM_DEPOT_BOX_XVII = 25469, ITEM_DEPOT_BOX_XVIII = 34571, + ITEM_DEPOT_BOX_XIX = 44714, + ITEM_DEPOT_BOX_XX = 44715, ITEM_MALE_CORPSE = 3058, ITEM_FEMALE_CORPSE = 3065, diff --git a/src/definitions.h b/src/definitions.h index d65a657dcb..c7aafc934d 100644 --- a/src/definitions.h +++ b/src/definitions.h @@ -8,9 +8,9 @@ static constexpr auto STATUS_SERVER_NAME = "The Forgotten Server"; static constexpr auto STATUS_SERVER_VERSION = "1.5"; static constexpr auto STATUS_SERVER_DEVELOPERS = "The Forgotten Server Team"; -static constexpr auto CLIENT_VERSION_MIN = 1280; -static constexpr auto CLIENT_VERSION_MAX = 1288; -static constexpr auto CLIENT_VERSION_STR = "12.87"; +static constexpr auto CLIENT_VERSION_MIN = 1290; +static constexpr auto CLIENT_VERSION_MAX = 1291; +static constexpr auto CLIENT_VERSION_STR = "12.90"; static constexpr auto AUTHENTICATOR_DIGITS = 6U; static constexpr auto AUTHENTICATOR_PERIOD = 30U; diff --git a/src/game.cpp b/src/game.cpp index cc4e587cb9..a77b7c5385 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -3045,7 +3045,7 @@ void Game::internalCloseTrade(Player* player, bool sendCancel /* = true*/) } } -void Game::playerPurchaseItem(uint32_t playerId, uint16_t spriteId, uint8_t count, uint8_t amount, +void Game::playerPurchaseItem(uint32_t playerId, uint16_t spriteId, uint8_t count, uint16_t amount, bool ignoreCap /* = false*/, bool inBackpacks /* = false*/) { if (amount == 0 || amount > ITEM_STACK_SIZE) { @@ -3083,7 +3083,7 @@ void Game::playerPurchaseItem(uint32_t playerId, uint16_t spriteId, uint8_t coun merchant->onPlayerTrade(player, onBuy, it.id, subType, amount, ignoreCap, inBackpacks); } -void Game::playerSellItem(uint32_t playerId, uint16_t spriteId, uint8_t count, uint8_t amount, bool ignoreEquipped) +void Game::playerSellItem(uint32_t playerId, uint16_t spriteId, uint8_t count, uint16_t amount, bool ignoreEquipped) { if (amount == 0 || amount > ITEM_STACK_SIZE) { return; diff --git a/src/game.h b/src/game.h index b2b215d167..3eaab7e2f9 100644 --- a/src/game.h +++ b/src/game.h @@ -354,9 +354,9 @@ class Game uint16_t spriteId); void playerAcceptTrade(uint32_t playerId); void playerLookInTrade(uint32_t playerId, bool lookAtCounterOffer, uint8_t index); - void playerPurchaseItem(uint32_t playerId, uint16_t spriteId, uint8_t count, uint8_t amount, bool ignoreCap = false, - bool inBackpacks = false); - void playerSellItem(uint32_t playerId, uint16_t spriteId, uint8_t count, uint8_t amount, + void playerPurchaseItem(uint32_t playerId, uint16_t spriteId, uint8_t count, uint16_t amount, + bool ignoreCap = false, bool inBackpacks = false); + void playerSellItem(uint32_t playerId, uint16_t spriteId, uint8_t count, uint16_t amount, bool ignoreEquipped = false); void playerCloseShop(uint32_t playerId); void playerLookInShop(uint32_t playerId, uint16_t spriteId, uint8_t count); diff --git a/src/itemloader.h b/src/itemloader.h index c419870e4d..b593782068 100644 --- a/src/itemloader.h +++ b/src/itemloader.h @@ -96,8 +96,9 @@ enum clientVersion_t CLIENT_VERSION_1285 = 61, CLIENT_VERSION_1286 = 62, CLIENT_VERSION_1287 = 63, + CLIENT_VERSION_1290 = 64, - CLIENT_VERSION_LAST = CLIENT_VERSION_1287 + CLIENT_VERSION_LAST = CLIENT_VERSION_1290 }; enum rootattrib_ @@ -169,8 +170,8 @@ enum itemflags_t FLAG_HORIZONTAL = 1 << 18, FLAG_CANNOTDECAY = 1 << 19, // unused FLAG_ALLOWDISTREAD = 1 << 20, - FLAG_UNUSED = 1 << 21, // unused - FLAG_CLIENTCHARGES = 1 << 22, /* deprecated */ + FLAG_CLIENTDURATION = 1 << 21, + FLAG_CLIENTCHARGES = 1 << 22, FLAG_LOOKTHROUGH = 1 << 23, FLAG_ANIMATION = 1 << 24, FLAG_FULLTILE = 1 << 25, // unused diff --git a/src/items.cpp b/src/items.cpp index 0c589377b1..62befdc18b 100644 --- a/src/items.cpp +++ b/src/items.cpp @@ -560,6 +560,8 @@ bool Items::loadFromOtb(const std::string& file) iType.isAnimation = hasBitSet(FLAG_ANIMATION, flags); // iType.walkStack = !hasBitSet(FLAG_FULLTILE, flags); iType.forceUse = hasBitSet(FLAG_FORCEUSE, flags); + iType.showClientCharges = hasBitSet(FLAG_CLIENTCHARGES, flags); + iType.showClientDuration = hasBitSet(FLAG_CLIENTDURATION, flags); iType.id = serverId; iType.clientId = clientId; diff --git a/src/items.h b/src/items.h index 3a89980c5b..0f33e2061a 100644 --- a/src/items.h +++ b/src/items.h @@ -412,6 +412,8 @@ class ItemType bool stopTime = false; bool showCount = true; bool supply = false; + bool showClientCharges = false; + bool showClientDuration = false; }; class Items diff --git a/src/luascript.cpp b/src/luascript.cpp index 6a65a46b4a..54650e96d7 100644 --- a/src/luascript.cpp +++ b/src/luascript.cpp @@ -1465,6 +1465,7 @@ void LuaScriptInterface::registerFunctions() registerEnum(CONST_ME_HORESTIS); registerEnum(CONST_ME_DEVOVORGA); registerEnum(CONST_ME_FERUMBRAS_2); + registerEnum(CONST_ME_FOAM); registerEnum(CONST_ANI_NONE); registerEnum(CONST_ANI_SPEAR); diff --git a/src/networkmessage.cpp b/src/networkmessage.cpp index 5bfc3e5be8..882fb2ddc0 100644 --- a/src/networkmessage.cpp +++ b/src/networkmessage.cpp @@ -95,6 +95,12 @@ void NetworkMessage::addItem(uint16_t id, uint8_t count) addByte(0x00); // quiver ammo count } else if (it.classification > 0) { addByte(0x00); // item tier (0-10) + } else if (it.showClientCharges) { + add(it.charges); + addByte(0x00); // boolean (is brand new) + } else if (it.showClientDuration) { + add(it.decayTime); + addByte(0x00); // boolean (is brand new) } if (it.isPodium()) { @@ -119,6 +125,14 @@ void NetworkMessage::addItem(const Item* item) addByte(0x00); // item tier (0-10) } + if (it.showClientCharges) { + add(item->getCharges()); + addByte(0); // boolean (is brand new) + } else if (it.showClientDuration) { + add(item->getDuration() / 1000); + addByte(0); // boolean (is brand new) + } + if (it.isContainer()) { addByte(0x00); // assigned loot container icon // quiver ammo count diff --git a/src/npc.cpp b/src/npc.cpp index 70523e332f..5c98f87a57 100644 --- a/src/npc.cpp +++ b/src/npc.cpp @@ -344,7 +344,7 @@ void Npc::doSayToPlayer(Player* player, const std::string& text) } } -void Npc::onPlayerTrade(Player* player, int32_t callback, uint16_t itemId, uint8_t count, uint8_t amount, +void Npc::onPlayerTrade(Player* player, int32_t callback, uint16_t itemId, uint8_t count, uint16_t amount, bool ignore /* = false*/, bool inBackpacks /* = false*/) { if (npcEventHandler) { @@ -1176,7 +1176,7 @@ void NpcEventsHandler::onCreatureSay(Creature* creature, SpeakClasses type, cons scriptInterface->callFunction(3); } -void NpcEventsHandler::onPlayerTrade(Player* player, int32_t callback, uint16_t itemId, uint8_t count, uint8_t amount, +void NpcEventsHandler::onPlayerTrade(Player* player, int32_t callback, uint16_t itemId, uint8_t count, uint16_t amount, bool ignore, bool inBackpacks) { if (callback == -1) { diff --git a/src/npc.h b/src/npc.h index f2abf09509..75c4e5df54 100644 --- a/src/npc.h +++ b/src/npc.h @@ -62,7 +62,7 @@ class NpcEventsHandler void onCreatureDisappear(Creature* creature); void onCreatureMove(Creature* creature, const Position& oldPos, const Position& newPos); void onCreatureSay(Creature* creature, SpeakClasses, const std::string& text); - void onPlayerTrade(Player* player, int32_t callback, uint16_t itemId, uint8_t count, uint8_t amount, + void onPlayerTrade(Player* player, int32_t callback, uint16_t itemId, uint8_t count, uint16_t amount, bool ignore = false, bool inBackpacks = false); void onPlayerCloseChannel(Player* player); void onPlayerEndTrade(Player* player); @@ -143,7 +143,7 @@ class Npc final : public Creature } void onPlayerCloseChannel(Player* player); - void onPlayerTrade(Player* player, int32_t callback, uint16_t itemId, uint8_t count, uint8_t amount, + void onPlayerTrade(Player* player, int32_t callback, uint16_t itemId, uint8_t count, uint16_t amount, bool ignore = false, bool inBackpacks = false); void onPlayerEndTrade(Player* player, int32_t buyCallback, int32_t sellCallback); diff --git a/src/protocolgame.cpp b/src/protocolgame.cpp index 3b87a70165..a01b76716b 100644 --- a/src/protocolgame.cpp +++ b/src/protocolgame.cpp @@ -1370,7 +1370,7 @@ void ProtocolGame::parsePlayerPurchase(NetworkMessage& msg) { uint16_t id = msg.get(); uint8_t count = msg.getByte(); - uint8_t amount = msg.getByte(); + uint16_t amount = msg.get(); bool ignoreCap = msg.getByte() != 0; bool inBackpacks = msg.getByte() != 0; g_dispatcher.addTask(DISPATCHER_TASK_EXPIRATION, [=, playerID = player->getID()]() { @@ -1382,7 +1382,7 @@ void ProtocolGame::parsePlayerSale(NetworkMessage& msg) { uint16_t id = msg.get(); uint8_t count = msg.getByte(); - uint8_t amount = msg.getByte(); + uint16_t amount = msg.get(); bool ignoreEquipped = msg.getByte() != 0; g_dispatcher.addTask(DISPATCHER_TASK_EXPIRATION, [=, playerID = player->getID()]() { g_game.playerSellItem(playerID, id, count, amount, ignoreEquipped); @@ -2022,7 +2022,6 @@ void ProtocolGame::sendSaleItemList(const std::list& shop) NetworkMessage msg; msg.addByte(0x7B); - msg.add(playerBank + playerMoney); // deprecated and ignored by QT client. OTClient still uses it. std::map saleMap; @@ -2092,7 +2091,7 @@ void ProtocolGame::sendSaleItemList(const std::list& shop) uint8_t i = 0; for (std::map::const_iterator it = saleMap.begin(); i < itemsToSend; ++it, ++i) { msg.addItemId(it->first); - msg.addByte(std::min(it->second, std::numeric_limits::max())); + msg.add(std::min(it->second, std::numeric_limits::max())); } writeToOutputBuffer(msg); diff --git a/src/tools.cpp b/src/tools.cpp index a8d6652645..a047b690ba 100644 --- a/src/tools.cpp +++ b/src/tools.cpp @@ -539,6 +539,7 @@ MagicEffectNames magicEffectNames = { {"horestis", CONST_ME_HORESTIS}, {"devovorga", CONST_ME_DEVOVORGA}, {"ferumbras2", CONST_ME_FERUMBRAS_2}, + {"foam", CONST_ME_FOAM}, }; ShootTypeNames shootTypeNames = { @@ -656,11 +657,12 @@ SkullNames skullNames = { {"red", SKULL_RED}, {"black", SKULL_BLACK}, {"orange", SKULL_ORANGE}, }; -std::vector depotBoxes = {ITEM_DEPOT_BOX_I, ITEM_DEPOT_BOX_II, ITEM_DEPOT_BOX_III, ITEM_DEPOT_BOX_IV, - ITEM_DEPOT_BOX_V, ITEM_DEPOT_BOX_VI, ITEM_DEPOT_BOX_VII, ITEM_DEPOT_BOX_VIII, - ITEM_DEPOT_BOX_IX, ITEM_DEPOT_BOX_X, ITEM_DEPOT_BOX_XI, ITEM_DEPOT_BOX_XII, - ITEM_DEPOT_BOX_XIII, ITEM_DEPOT_BOX_XIV, ITEM_DEPOT_BOX_XV, ITEM_DEPOT_BOX_XVI, - ITEM_DEPOT_BOX_XVII, ITEM_DEPOT_BOX_XVIII}; +std::vector depotBoxes = { + ITEM_DEPOT_BOX_I, ITEM_DEPOT_BOX_II, ITEM_DEPOT_BOX_III, ITEM_DEPOT_BOX_IV, ITEM_DEPOT_BOX_V, + ITEM_DEPOT_BOX_VI, ITEM_DEPOT_BOX_VII, ITEM_DEPOT_BOX_VIII, ITEM_DEPOT_BOX_IX, ITEM_DEPOT_BOX_X, + ITEM_DEPOT_BOX_XI, ITEM_DEPOT_BOX_XII, ITEM_DEPOT_BOX_XIII, ITEM_DEPOT_BOX_XIV, ITEM_DEPOT_BOX_XV, + ITEM_DEPOT_BOX_XVI, ITEM_DEPOT_BOX_XVII, ITEM_DEPOT_BOX_XVIII, ITEM_DEPOT_BOX_XIX, ITEM_DEPOT_BOX_XX, +}; uint16_t getDepotBoxId(uint16_t index) {