Skip to content

Commit

Permalink
Merge branch 'dudantas/feat-highlight-loot' into dudantas/feat-soul-w…
Browse files Browse the repository at this point in the history
…ar-quest
  • Loading branch information
s2leandro155 committed Jul 5, 2024
2 parents b96a957 + 00d4cc9 commit a338c8a
Show file tree
Hide file tree
Showing 9 changed files with 154 additions and 45 deletions.
2 changes: 2 additions & 0 deletions src/creatures/creature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -830,6 +830,8 @@ bool Creature::dropCorpse(std::shared_ptr<Creature> lastHitCreature, std::shared
},
"Game::playerQuickLootCorpse");
}

corpse->sendUpdateToClient(player);
}
}

Expand Down
7 changes: 6 additions & 1 deletion src/creatures/players/player.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2644,6 +2644,11 @@ class Player final : public Creature, public Cylinder, public Bankable {

uint16_t getPlayerVocationEnum() const;

using ManagedContainerMap = std::map<ObjectCategory_t, std::pair<std::shared_ptr<Container>, std::shared_ptr<Container>>>;
const ManagedContainerMap &getManagedContainers() const {
return m_managedContainers;
}

private:
friend class PlayerLock;
std::mutex mutex;
Expand Down Expand Up @@ -2734,7 +2739,7 @@ class Player final : public Creature, public Cylinder, public Bankable {

std::map<uint64_t, std::shared_ptr<Reward>> rewardMap;

std::map<ObjectCategory_t, std::pair<std::shared_ptr<Container>, std::shared_ptr<Container>>> m_managedContainers;
ManagedContainerMap m_managedContainers;
std::vector<ForgeHistory> forgeHistoryVector;

std::vector<uint16_t> quickLootListItemIds;
Expand Down
25 changes: 25 additions & 0 deletions src/enums/container_type.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Canary - A free and open-source MMORPG server emulator
* Copyright (©) 2019-2024 OpenTibiaBR <[email protected]>
* Repository: https://github.com/opentibiabr/canary
* License: https://github.com/opentibiabr/canary/blob/main/LICENSE
* Contributors: https://github.com/opentibiabr/canary/graphs/contributors
* Website: https://docs.opentibiabr.com/
*/

#pragma once

#ifndef USE_PRECOMPILED_HEADERS
#include <cstdint>
#endif

enum class ContainerSpecial_t : uint8_t {
None = 0,
LootContainer = 1,
ContentCounter = 2,
ManagerUnknown = 3, // Maybe was used on a few test servers and removed later? Original value was not being used
LootHighlight = 4,
Obtain = 8,
Manager = 9,
QuiverLoot = 11,
};
4 changes: 4 additions & 0 deletions src/game/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2960,6 +2960,8 @@ void Game::playerQuickLootCorpse(std::shared_ptr<Player> player, std::shared_ptr
player->sendTextMessage(MESSAGE_EVENT_ADVANCE, ss.str());
}

corpse->sendUpdateToClient(player);

player->lastQuickLootNotification = OTSYS_TIME();
}

Expand Down Expand Up @@ -5478,6 +5480,8 @@ void Game::playerQuickLoot(uint32_t playerId, const Position &pos, uint16_t item
}
}
}

corpse->sendUpdateToClient(player);
}

void Game::playerLootAllCorpses(std::shared_ptr<Player> player, const Position &pos, bool lootAllCorpses) {
Expand Down
55 changes: 55 additions & 0 deletions src/items/containers/container.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -990,3 +990,58 @@ uint32_t Container::getOwnerId() const {
}
return 0;
}

ContainerSpecial_t Container::getSpecialCategory(const std::shared_ptr<Player> &player) {
if (isCorpse() && !empty() && getHoldingPlayer() != player) {
return ContainerSpecial_t::LootHighlight;
}

if (getHoldingPlayer() == player) {
if (isQuiver() && getSlotPosition() & SLOTP_RIGHT) {
return ContainerSpecial_t::QuiverLoot;
}

auto [lootFlags, obtainFlags] = getObjectCategoryFlags(player);
if (lootFlags != 0 || obtainFlags != 0) {
return ContainerSpecial_t::Manager;
}
}

return ContainerSpecial_t::None;
}

std::pair<uint32_t, uint32_t> Container::getObjectCategoryFlags(const std::shared_ptr<Player> &player) const {
uint32_t lootFlags = 0, obtainFlags = 0;
// Cycle through all containers managed by the player
for (auto [category, containerPair] : player->getManagedContainers()) {
// Check if the category is valid before continuing
if (!isValidObjectCategory(category)) {
continue;
}

// containerPair.first refers to loot containers
if (containerPair.first == static_self_cast<Container>()) {
lootFlags |= 1 << category;
}

// containerPair.second refers to the obtain containers
if (containerPair.second == static_self_cast<Container>()) {
obtainFlags |= 1 << category;
}
}

return { lootFlags, obtainFlags };
}

uint32_t Container::getAmmoAmount(const std::shared_ptr<Player> &player) const {
uint32_t ammoTotal = 0;
if (isQuiver()) {
for (std::shared_ptr<Item> listItem : getItemList()) {
if (player->getLevel() >= Item::items[listItem->getID()].minReqLevel) {
ammoTotal += listItem->getItemAmount();
}
}
}

return ammoTotal;
}
6 changes: 6 additions & 0 deletions src/items/containers/container.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
#include "items/item.hpp"
#include "items/tile.hpp"

#include "enums/container_type.hpp"

class Container;
class DepotChest;
class DepotLocker;
Expand Down Expand Up @@ -178,6 +180,10 @@ class Container : public Item, public Cylinder {
bool isBrowseFieldAndHoldsRewardChest();
bool isInsideContainerWithId(const uint16_t id);

ContainerSpecial_t getSpecialCategory(const std::shared_ptr<Player> &player);
std::pair<uint32_t, uint32_t> getObjectCategoryFlags(const std::shared_ptr<Player> &player) const;
uint32_t getAmmoAmount(const std::shared_ptr<Player> &player) const;

protected:
std::ostringstream &getContentDescription(std::ostringstream &os, bool oldProtocol);

Expand Down
17 changes: 17 additions & 0 deletions src/items/item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "creatures/players/imbuements/imbuements.hpp"
#include "lua/creature/actions.hpp"
#include "creatures/combat/spells.hpp"
#include "map/spectators.hpp"

#define ITEM_IMBUEMENT_SLOT 500

Expand Down Expand Up @@ -3312,3 +3313,19 @@ void Item::updateTileFlags() {
tile->updateTileFlags(static_self_cast<Item>());
}
}

void Item::sendUpdateToClient(const std::shared_ptr<Player> &player /* = nullptr*/) {
if (!player) {
auto spectators = Spectators().find<Creature>(getPosition(), true);
for (const auto &spectator : spectators) {
spectator->getPlayer()->sendUpdateTileItem(getTile(), getPosition(), static_self_cast<Item>());
}

return;
}

auto participants = player->getParty() ? player->getParty()->getPlayers() : std::vector<std::shared_ptr<Player>> { player };
for (const auto &participant : participants) {
participant->sendUpdateTileItem(getTile(), getPosition(), static_self_cast<Item>());
}
}
13 changes: 13 additions & 0 deletions src/items/item.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,19 @@ class Item : virtual public Thing, public ItemProperties, public SharedObject {
virtual void startDecaying();
virtual void stopDecaying();

/**
* @brief Send "AddItem" update to the specified player or to all nearby players if none specified.
*
* This function sends updates about the item's state to a client. If a specific player is provided,
* the update is directed to that player and possibly their party members depending on the game logic.
* If no player is specified, the update is broadcast to all nearby players who are capable of viewing
* the item update, such as spectators around the item's location.
*
* @param player Optional shared pointer to a Player object. If provided, the update is directed to this player
* and their associated viewers or party members. If nullptr, the update goes to all nearby spectators.
*/
void sendUpdateToClient(const std::shared_ptr<Player> &player = nullptr);

std::shared_ptr<Item> transform(uint16_t itemId, uint16_t itemCount = -1);

bool isLoadedFromMap() const {
Expand Down
70 changes: 26 additions & 44 deletions src/server/network/protocol/protocolgame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#include "enums/account_type.hpp"
#include "enums/account_group_type.hpp"
#include "enums/account_coins.hpp"
#include "enums/container_type.hpp"

#include "creatures/players/highscore_category.hpp"

Expand Down Expand Up @@ -340,48 +341,34 @@ void ProtocolGame::AddItem(NetworkMessage &msg, std::shared_ptr<Item> item) {
return;
}

if (it.isContainer()) {
uint8_t containerType = 0;

std::shared_ptr<Container> container = item->getContainer();
if (container && containerType == 0 && container->getHoldingPlayer() == player) {
uint32_t lootFlags = 0;
uint32_t obtainFlags = 0;
for (auto [category, containerMap] : player->m_managedContainers) {
if (!isValidObjectCategory(category)) {
continue;
}
if (containerMap.first == container) {
lootFlags |= 1 << category;
}
if (containerMap.second == container) {
obtainFlags |= 1 << category;
}
}

if (lootFlags != 0 || obtainFlags != 0) {
containerType = 9;
msg.addByte(containerType);
const auto &container = item->getContainer();
if (it.isContainer() && container) {
ContainerSpecial_t containerType = container->getSpecialCategory(player);
msg.addByte(enumToValue(containerType));
switch (containerType) {
case ContainerSpecial_t::LootHighlight:
break;
case ContainerSpecial_t::Manager: {
auto [lootFlags, obtainFlags] = container->getObjectCategoryFlags(player);
msg.add<uint32_t>(lootFlags);
msg.add<uint32_t>(obtainFlags);
break;
}
}

// Quiver ammo count
if (container && containerType == 0 && item->isQuiver() && player->getThing(CONST_SLOT_RIGHT) == item) {
uint16_t ammoTotal = 0;
for (std::shared_ptr<Item> listItem : container->getItemList()) {
if (player->getLevel() >= Item::items[listItem->getID()].minReqLevel) {
ammoTotal += listItem->getItemCount();
}
case ContainerSpecial_t::ContentCounter: {
auto ammoTotal = container->getAmmoAmount(player);
msg.add<uint32_t>(ammoTotal);
break;
}
containerType = 2;
msg.addByte(containerType);
msg.add<uint32_t>(ammoTotal);
}

if (containerType == 0) {
msg.addByte(0x00);
case ContainerSpecial_t::QuiverLoot: {
auto ammoTotal = container->getAmmoAmount(player);
auto [lootFlags, obtainFlags] = container->getObjectCategoryFlags(player);
msg.add<uint32_t>(lootFlags);
msg.add<uint32_t>(ammoTotal);
msg.add<uint32_t>(obtainFlags);
break;
}
default:
break;
}
}

Expand Down Expand Up @@ -4815,22 +4802,17 @@ void ProtocolGame::sendMarketEnter(uint32_t depotId) {
// Only use here locker items, itemVector is for use of Game::createMarketOffer
auto [itemVector, lockerItems] = player->requestLockerItems(depotLocker, true);
auto totalItemsCountPosition = msg.getBufferPosition();
msg.skipBytes(2); // Total items count

uint16_t totalItemsCount = 0;
msg.add<uint16_t>(lockerItems.size());
for (const auto &[itemId, tierAndCountMap] : lockerItems) {
for (const auto &[tier, count] : tierAndCountMap) {
msg.add<uint16_t>(itemId);
if (!oldProtocol && Item::items[itemId].upgradeClassification > 0) {
msg.addByte(tier);
}
msg.add<uint16_t>(static_cast<uint16_t>(count));
totalItemsCount++;
}
}

msg.setBufferPosition(totalItemsCountPosition);
msg.add<uint16_t>(totalItemsCount);
writeToOutputBuffer(msg);

updateCoinBalance();
Expand Down

0 comments on commit a338c8a

Please sign in to comment.