Skip to content

Commit

Permalink
feat: protocol 13.21 (#1523)
Browse files Browse the repository at this point in the history
New features:
• StoreInbox pagination
• StoreInbox showing items inside wrap container
• StoreInbox (containers) filters.

Clients for tests:
https://github.com/dudantas/tibia-client/releases/tag/13.21.13839

Assets editor: Arch-Mina/Assets-Editor#7

Thanks, @marcosvf132, for providing bytes.

Co-authored-by: Luan Santos <[email protected]>
  • Loading branch information
dudantas and luan authored Sep 12, 2023
1 parent 1bc3667 commit 4275713
Show file tree
Hide file tree
Showing 38 changed files with 717 additions and 164 deletions.
2 changes: 1 addition & 1 deletion data-otservbr-global/npc/emael.lua
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ local function creatureSayCallback(npc, creature, type, message)
if player:removeMoney(1000000) then
npcHandler:say("Ah, I see you killed a lot of dangerous creatures. Here's your podium of vigour!", npc, creature)
local inbox = player:getSlotItem(CONST_SLOT_STORE_INBOX)
if inbox and inbox:getEmptySlots() > 0 then
if inbox then
local decoKit = inbox:addItem(23398, 1)
if decoKit then
decoKit:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, "Unwrap it in your own house to create a <" .. ItemType(38707):getName() .. ">.")
Expand Down
2 changes: 1 addition & 1 deletion data-otservbr-global/npc/emperor_kruzak.lua
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ local function creatureSayCallback(npc, creature, type, message)
if player:getStorageValue(Storage.OutfitQuest.GoldenOutfit) < 1 then
if player:getMoney() + player:getBankBalance() >= 500000000 then
local inbox = player:getSlotItem(CONST_SLOT_STORE_INBOX)
if inbox and inbox:getEmptySlots() > 0 then
if inbox then
local decoKit = inbox:addItem(23398, 1)
local decoItemName = ItemType(31510):getName()
decoKit:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, "You bought this item in the Store.\nUnwrap it in your own house to create a " .. decoItemName .. ".")
Expand Down
2 changes: 1 addition & 1 deletion data-otservbr-global/npc/hireling.lua
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,7 @@ function createHirelingType(HirelingName)

if player:getFreeCapacity() < itType:getWeight(1) then
npcHandler:say("Sorry, but you don't have enough capacity.", npc, creature)
elseif not inbox or inbox:getEmptySlots() == 0 then
elseif not inbox then
player:getPosition():sendMagicEffect(CONST_ME_POFF)
npcHandler:say("Sorry, you don't have enough room on your inbox", npc, creature)
elseif not player:removeMoneyBank(15000) then
Expand Down
2 changes: 1 addition & 1 deletion data-otservbr-global/npc/king_tibianus.lua
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ local function creatureSayCallback(npc, creature, type, message)
if player:getStorageValue(Storage.OutfitQuest.GoldenOutfit) < 1 then
if player:getMoney() + player:getBankBalance() >= 500000000 then
local inbox = player:getSlotItem(CONST_SLOT_STORE_INBOX)
if inbox and inbox:getEmptySlots() > 0 then
if inbox then
local decoKit = inbox:addItem(23398, 1)
local decoItemName = ItemType(31510):getName()
decoKit:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, "Unwrap it in your own house to create a " .. decoItemName .. ".")
Expand Down
2 changes: 1 addition & 1 deletion data-otservbr-global/npc/queen_eloise.lua
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ local function creatureSayCallback(npc, creature, type, message)
if player:getStorageValue(Storage.OutfitQuest.GoldenOutfit) < 1 then
if player:getMoney() + player:getBankBalance() >= 500000000 then
local inbox = player:getSlotItem(CONST_SLOT_STORE_INBOX)
if inbox and inbox:getEmptySlots() > 0 then
if inbox then
local decoKit = inbox:addItem(23398, 1)
local decoItemName = ItemType(31510):getName()
decoKit:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, "You bought this item in the Store.\nUnwrap it in your own house to create a " .. decoItemName .. ".")
Expand Down
2 changes: 1 addition & 1 deletion data-otservbr-global/npc/walter_jaeger.lua
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ local function processItemInboxPurchase(player, name, id)
end

local inbox = player:getSlotItem(CONST_SLOT_STORE_INBOX)
if inbox and inbox:getEmptySlots() > 0 then
if inbox then
local decoKit = inbox:addItem(23398, 1)
if decoKit then
decoKit:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, "You bought this item with the Walter Jaeger.\nUnwrap it in your own house to create a <" .. name .. ">.")
Expand Down
11 changes: 1 addition & 10 deletions data/events/scripts/player.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
CONTAINER_WEIGHT_CHECK = true -- true = enable / false = disable
CONTAINER_WEIGHT_MAX = 1000000 -- 1000000 = 10k = 10000.00 oz

local storeItemID = {
-- registered item ids here are not tradable with players
-- these items can be set to moveable at items.xml
Expand Down Expand Up @@ -240,19 +237,13 @@ function Player:onMoveItem(item, count, fromPosition, toPosition, fromCylinder,
return false
end

-- No move if item count > 20 items
-- No move if tile item count > 20 items
local tile = Tile(toPosition)
if tile and tile:getItemCount() > 20 then
self:sendCancelMessage(RETURNVALUE_NOTPOSSIBLE)
return false
end

-- No move parcel very heavy
if CONTAINER_WEIGHT_CHECK and ItemType(item:getId()):isContainer() and item:getWeight() > CONTAINER_WEIGHT_MAX then
self:sendCancelMessage("Your cannot move this item too heavy.")
return false
end

-- Players cannot throw items on teleports
if blockTeleportTrashing and toPosition.x ~= CONTAINER_POSITION then
local thing = Tile(toPosition):getItemByType(ITEM_TYPE_TELEPORT)
Expand Down
Binary file modified data/items/appearances.dat
Binary file not shown.
4 changes: 2 additions & 2 deletions data/libs/hireling_lib.lua
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ function Hireling:returnToLamp(player_id)
end

local inbox = owner:getSlotItem(CONST_SLOT_STORE_INBOX)
if not inbox or inbox:getEmptySlots() < 1 then
if not inbox then
owner:getPosition():sendMagicEffect(CONST_ME_POFF)
return owner:sendTextMessage(MESSAGE_FAILURE, "You don't have enough room in your inbox.")
end
Expand Down Expand Up @@ -553,7 +553,7 @@ function Player:addNewHireling(name, sex)
end

local inbox = self:getSlotItem(CONST_SLOT_STORE_INBOX)
if not inbox or inbox:getEmptySlots() < 1 then
if not inbox then
self:getPosition():sendMagicEffect(CONST_ME_POFF)
self:sendTextMessage(MESSAGE_FAILURE, "You don't have enough room in your inbox.")
return false
Expand Down
2 changes: 1 addition & 1 deletion data/modules/scripts/daily_reward/daily_reward.lua
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@ function Player.selectDailyReward(self, msg)

-- Adding items to store inbox
local inbox = self:getSlotItem(CONST_SLOT_STORE_INBOX)
if inbox and inbox:getEmptySlots() < columnsPicked then
if inbox then
self:sendError("You do not have enough space in your store inbox.")
return false
end
Expand Down
9 changes: 5 additions & 4 deletions data/modules/scripts/gamestore/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1503,7 +1503,7 @@ function GameStore.processItemPurchase(player, offerId, offerCount, moveable)
end

local inbox = player:getSlotItem(CONST_SLOT_STORE_INBOX)
if inbox and inbox:getEmptySlots() > offerCount then
if inbox then
for t = 1, offerCount do
local inboxItem = inbox:addItem(offerId, offerCount or 1)
if moveable ~= true and inboxItem then
Expand All @@ -1521,7 +1521,7 @@ function GameStore.processChargesPurchase(player, itemtype, name, charges, movea
end

local inbox = player:getSlotItem(CONST_SLOT_STORE_INBOX)
if inbox and inbox:getEmptySlots() > 1 then
if inbox then
local inboxItem = inbox:addItem(itemtype, charges)

if moveable ~= true and inboxItem then
Expand Down Expand Up @@ -1582,7 +1582,7 @@ function GameStore.processStackablePurchase(player, offerId, offerCount, offerNa
end

local inbox = player:getSlotItem(CONST_SLOT_STORE_INBOX)
if inbox and inbox:getEmptySlots() > 0 then
if inbox then
if (isKeg and offerCount > 500) or offerCount > 100 then
local parcel = inbox:addItem(PARCEL_ID, 1)
parcel:setAttribute(ITEM_ATTRIBUTE_STORE, systemTime())
Expand Down Expand Up @@ -1637,7 +1637,7 @@ function GameStore.processHouseRelatedPurchase(player, offer)
if type(itemIds) ~= "table" then
itemIds = { itemIds }
end
if inbox and inbox:getEmptySlots() >= #itemIds then
if inbox then
for _, itemId in ipairs(itemIds) do
local decoKit = inbox:addItem(23398, 1)
if decoKit then
Expand All @@ -1652,6 +1652,7 @@ function GameStore.processHouseRelatedPurchase(player, offer)
end
end
end
player:sendUpdateContainer(inbox)
else
return error({ code = 0, message = "Please make sure you have free slots in your store inbox." })
end
Expand Down
2 changes: 1 addition & 1 deletion data/scripts/eventcallbacks/player/on_rotate_item.lua
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
local callback = EventCallback()

function callback.playerOnRotateItem(player, item, position)
if item:getActionId() == 100 then
if item:getActionId() == IMMOVABLE_ACTION_ID then
player:sendCancelMessage(RETURNVALUE_NOTPOSSIBLE)
return false
end
Expand Down
28 changes: 28 additions & 0 deletions data/scripts/talkactions/god/goto_house.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
local sellHouse = TalkAction("/gotohouse")

function sellHouse.onSay(player, words, param)
local targetPlayer = Player(param)
if targetPlayer then
local targetHouse = targetPlayer:getHouse()
if not targetHouse then
targetPlayer:sendCancelMessage(string.format("The player %s not have house.", player:getName()))
return
end

targetPlayer:teleportTo(targetHouse:getExitPosition())
else
local house = player:getHouse()
if not house then
player:sendCancelMessage("You not have house. For goto house of one player use the player name param, usage: /gotohouse playername")
return
end

player:teleportTo(house:getExitPosition())
end

return false
end

sellHouse:separator(" ")
sellHouse:groupType("god")
sellHouse:register()
2 changes: 1 addition & 1 deletion src/config/config_definitions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ enum integerConfig_t {
STATUS_PORT,
STAIRHOP_DELAY,
MAX_CONTAINER,
MAX_ITEM,
MAX_CONTAINER_ITEM,
MARKET_OFFER_DURATION,
DEPOT_BOXES,
FREE_DEPOT_LIMIT,
Expand Down
2 changes: 1 addition & 1 deletion src/config/configmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ bool ConfigManager::load() {
integer[WHITE_SKULL_TIME] = getGlobalNumber(L, "whiteSkullTime", 15 * 60 * 1000);
integer[STAIRHOP_DELAY] = getGlobalNumber(L, "stairJumpExhaustion", 2000);
integer[MAX_CONTAINER] = getGlobalNumber(L, "maxContainer", 500);
integer[MAX_ITEM] = getGlobalNumber(L, "maxItem", 10000);
integer[MAX_CONTAINER_ITEM] = getGlobalNumber(L, "maxItem", 5000);
integer[EXP_FROM_PLAYERS_LEVEL_RANGE] = getGlobalNumber(L, "expFromPlayersLevelRange", 75);
integer[CHECK_EXPIRED_MARKET_OFFERS_EACH_MINUTES] = getGlobalNumber(L, "checkExpiredMarketOffersEachMinutes", 60);
integer[MAX_MARKET_OFFERS_AT_A_TIME_PER_PLAYER] = getGlobalNumber(L, "maxMarketOffersAtATimePerPlayer", 100);
Expand Down
2 changes: 1 addition & 1 deletion src/core.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ static constexpr auto STATUS_SERVER_DEVELOPERS = "OpenTibiaBR Organization";
static constexpr auto AUTHENTICATOR_DIGITS = 6U;
static constexpr auto AUTHENTICATOR_PERIOD = 30U;

static constexpr auto CLIENT_VERSION = 1320;
static constexpr auto CLIENT_VERSION = 1321;

#define CLIENT_VERSION_UPPER (CLIENT_VERSION / 100)
#define CLIENT_VERSION_LOWER (CLIENT_VERSION % 100)
2 changes: 1 addition & 1 deletion src/creatures/players/player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1928,7 +1928,7 @@ void Player::onCloseContainer(const Container* container) {
}

void Player::onSendContainer(const Container* container) {
if (!client) {
if (!client || !container) {
return;
}

Expand Down
1 change: 1 addition & 0 deletions src/enums/item_attribute.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ enum ItemAttribute_t : uint64_t {
STORE = 31,
CUSTOM = 32,
LOOTMESSAGE_SUFFIX = 33,
STORE_INBOX_CATEGORY = 34,
};

enum ItemDecayState_t : uint8_t {
Expand Down
38 changes: 30 additions & 8 deletions src/game/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -616,7 +616,12 @@ Thing* Game::internalGetThing(Player* player, const Position &pos, int32_t index
}

uint8_t slot = pos.z;
return parentContainer->getItemByIndex(player->getContainerIndex(fromCid) + slot);
auto containerIndex = player->getContainerIndex(fromCid) + slot;
if (parentContainer->isStoreInboxFiltered()) {
return parentContainer->getFilteredItemByIndex(containerIndex);
}

return parentContainer->getItemByIndex(containerIndex);
} else if (pos.y == 0x20 || pos.y == 0x21) {
// '0x20' -> From depot.
// '0x21' -> From inbox.
Expand Down Expand Up @@ -1590,8 +1595,9 @@ ReturnValue Game::checkMoveItemToCylinder(Player* player, Cylinder* fromCylinder
}

const Container* topParentContainer = toCylinderContainer->getRootContainer();

if (!item->isStoreItem() && (containerID == ITEM_STORE_INBOX || topParentContainer->getParent() && topParentContainer->getParent()->getContainer() && topParentContainer->getParent()->getContainer()->getID() == ITEM_STORE_INBOX)) {
const auto parentContainer = topParentContainer->getParent() ? topParentContainer->getParent()->getContainer() : nullptr;
auto isStoreInbox = parentContainer && parentContainer->isStoreInbox();
if (!item->isStoreItem() && (containerID == ITEM_STORE_INBOX || isStoreInbox)) {
return RETURNVALUE_CONTAINERNOTENOUGHROOM;
}

Expand All @@ -1605,7 +1611,7 @@ ReturnValue Game::checkMoveItemToCylinder(Player* player, Cylinder* fromCylinder
isValidMoveItem = true;
}

if (topParentContainer->getParent() && topParentContainer->getParent()->getContainer() && (topParentContainer->getParent()->getContainer()->isDepotChest() || topParentContainer->getParent()->getContainer()->getID() == ITEM_STORE_INBOX)) {
if (parentContainer && (parentContainer->isDepotChest() || isStoreInbox)) {
isValidMoveItem = true;
}

Expand Down Expand Up @@ -1859,9 +1865,18 @@ ReturnValue Game::internalMoveItem(Cylinder* fromCylinder, Cylinder* toCylinder,
return retMaxCount;
}

// looting analyser from this point forward
auto fromContainer = fromCylinder ? fromCylinder->getContainer() : nullptr;
auto toContainer = toCylinder ? toCylinder->getContainer() : nullptr;
auto player = actor ? actor->getPlayer() : nullptr;
if (player) {
// Update containers
player->onSendContainer(toContainer);
player->onSendContainer(fromContainer);
}

// Actor related actions
if (fromCylinder && actor && toCylinder) {
if (!fromCylinder->getContainer() || !actor->getPlayer() || !toCylinder->getContainer()) {
if (!fromContainer || !toContainer || !player) {
return ret;
}

Expand All @@ -1876,7 +1891,8 @@ ReturnValue Game::internalMoveItem(Cylinder* fromCylinder, Cylinder* toCylinder,
return ret;
}

if (it.isCorpse && toCylinder->getContainer()->getTopParent() == player && item->getIsLootTrackeable()) {
// Looting analyser
if (it.isCorpse && toContainer->getTopParent() == player && item->getIsLootTrackeable()) {
player->sendLootStats(item, static_cast<uint8_t>(item->getItemCount()));
}
}
Expand Down Expand Up @@ -4114,7 +4130,7 @@ void Game::playerStashWithdraw(uint32_t playerId, uint16_t itemId, uint32_t coun
}
}

void Game::playerSeekInContainer(uint32_t playerId, uint8_t containerId, uint16_t index) {
void Game::playerSeekInContainer(uint32_t playerId, uint8_t containerId, uint16_t index, uint8_t containerCategory) {
Player* player = getPlayerByID(playerId);
if (!player) {
return;
Expand All @@ -4125,6 +4141,12 @@ void Game::playerSeekInContainer(uint32_t playerId, uint8_t containerId, uint16_
return;
}

if (container->isStoreInbox()) {
auto enumName = magic_enum::enum_name(static_cast<ContainerCategory_t>(containerCategory)).data();
container->setAttribute(ItemAttribute_t::STORE_INBOX_CATEGORY, enumName);
g_logger().debug("Setting new container with store inbox category name {}", enumName);
}

if ((index % container->capacity()) != 0 || index >= container->size()) {
return;
}
Expand Down
2 changes: 1 addition & 1 deletion src/game/game.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ class Game {
void playerWrapableItem(uint32_t playerId, const Position &pos, uint8_t stackPos, const uint16_t itemId);
void playerWriteItem(uint32_t playerId, uint32_t windowTextId, const std::string &text);
void playerBrowseField(uint32_t playerId, const Position &pos);
void playerSeekInContainer(uint32_t playerId, uint8_t containerId, uint16_t index);
void playerSeekInContainer(uint32_t playerId, uint8_t containerId, uint16_t index, uint8_t containerCategory);
void playerUpdateHouseWindow(uint32_t playerId, uint8_t listId, uint32_t windowTextId, const std::string &text);
void playerRequestTrade(uint32_t playerId, const Position &pos, uint8_t stackPos, uint32_t tradePlayerId, uint16_t itemId);
void playerAcceptTrade(uint32_t playerId);
Expand Down
Loading

0 comments on commit 4275713

Please sign in to comment.