diff --git a/data-canary/scripts/reward_chest/boss_death.lua b/data-canary/scripts/reward_chest/boss_death.lua index c37ef0fad93..f3f0a027297 100644 --- a/data-canary/scripts/reward_chest/boss_death.lua +++ b/data-canary/scripts/reward_chest/boss_death.lua @@ -11,7 +11,7 @@ function bossDeath.onDeath(creature, corpse, killer, mostDamageKiller, lastHitUn -- Make sure it is a boss if monsterType and monsterType:isRewardBoss() then local bossId = creature:getId() - local timestamp = os.time() + local timestamp = systemTime() ResetAndSetTargetList(creature) diff --git a/data-otservbr-global/migrations/25.lua b/data-otservbr-global/migrations/25.lua index 86a6d8ffec1..0d5352be49c 100644 --- a/data-otservbr-global/migrations/25.lua +++ b/data-otservbr-global/migrations/25.lua @@ -1,3 +1,5 @@ function onUpdateDatabase() - return false -- true = There are others migrations file | false = this is the last migration file + Spdlog.info("Updating database to version 26 (reward bag fix)") + db.query("UPDATE player_rewards SET pid = 0 WHERE itemtype = 19202;") + return true end diff --git a/data-otservbr-global/migrations/26.lua b/data-otservbr-global/migrations/26.lua new file mode 100644 index 00000000000..86a6d8ffec1 --- /dev/null +++ b/data-otservbr-global/migrations/26.lua @@ -0,0 +1,3 @@ +function onUpdateDatabase() + return false -- true = There are others migrations file | false = this is the last migration file +end diff --git a/data-otservbr-global/scripts/reward_chest/boss_death.lua b/data-otservbr-global/scripts/reward_chest/boss_death.lua index c37ef0fad93..f3f0a027297 100644 --- a/data-otservbr-global/scripts/reward_chest/boss_death.lua +++ b/data-otservbr-global/scripts/reward_chest/boss_death.lua @@ -11,7 +11,7 @@ function bossDeath.onDeath(creature, corpse, killer, mostDamageKiller, lastHitUn -- Make sure it is a boss if monsterType and monsterType:isRewardBoss() then local bossId = creature:getId() - local timestamp = os.time() + local timestamp = systemTime() ResetAndSetTargetList(creature) diff --git a/data/libs/reward_boss/reward_boss.lua b/data/libs/reward_boss/reward_boss.lua index 61f67cd08f0..a53dd6999b9 100644 --- a/data/libs/reward_boss/reward_boss.lua +++ b/data/libs/reward_boss/reward_boss.lua @@ -28,9 +28,9 @@ function InsertItems(buffer, info, parent, items, bagSid) if _ ~= 1 or parent > 100 then table.insert(buffer, ",") end - if item:getId() == ITEM_REWARD_CONTAINER then + if item:getId() == ITEM_REWARD_CONTAINER then table.insert(buffer, "(") - PushSeparated(buffer, ",", info.playerGuid, parent, bagSid, item:getId(), item:getSubType(), db.escapeString(item:serializeAttributes())) + PushSeparated(buffer, ",", info.playerGuid, 0, bagSid, item:getId(), item:getSubType(), db.escapeString(item:serializeAttributes())) table.insert(buffer, ")") else info.running = info.running + 1 @@ -56,19 +56,15 @@ function InsertItems(buffer, info, parent, items, bagSid) end function InsertRewardItems(playerGuid, timestamp, itemList) - db.asyncStoreQuery('select `pid`, `sid`, (SELECT max(`sid`) as sid from `player_rewards` where player_id = '..playerGuid..') as max_sid from `player_rewards` where `pid` = (select max(`pid`) from `player_rewards` where player_id = ' .. playerGuid .. ' and `pid` < 100);', + db.asyncStoreQuery('select max(`sid`) as max_sid from `player_rewards` where player_id = '..playerGuid..';', function(query) - local lastPid = Result.getDataInt(query, 'pid') or 0 - local bagSid = Result.getDataInt(query, 'sid') or 100 - local lastSid = Result.getDataInt(query, 'max_sid') or 101 - if lastPid ~= 0 then - db.query('UPDATE `player_rewards` SET `sid` = `sid`+1 WHERE `sid`> '..bagSid..' ORDER BY `sid` DESC') - lastSid = lastSid+1 - end + local lastSid = Result.getDataInt(query, 'max_sid') or 0; + local bagSid = lastSid + 1; + local nextSid = bagId + 1; local buffer = {'INSERT INTO `player_rewards` (`player_id`, `pid`, `sid`, `itemtype`, `count`, `attributes`) VALUES'} local info = { playerGuid = playerGuid, - running = lastSid + running = nextSid } local bag = Game.createItem(ITEM_REWARD_CONTAINER) bag:setAttribute(ITEM_ATTRIBUTE_DATE, timestamp) @@ -77,7 +73,7 @@ function InsertRewardItems(playerGuid, timestamp, itemList) bag:addItem(p[1], p[2]) end end - local total = InsertItems(buffer, info, lastPid + 1, {bag}, bagSid+1) + local total = InsertItems(buffer, info, lastPid + 1, {bag}, bagSid) table.insert(buffer, ";") if total ~= 0 then diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 822fb78cc7c..a71ed12b3e7 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -53,10 +53,6 @@ Player::~Player() { it.second->decrementReferenceCounter(); } - for (const auto &it : rewardMap) { - it.second->decrementReferenceCounter(); - } - for (const auto &it : quickLootContainers) { it.second->decrementReferenceCounter(); } @@ -469,6 +465,7 @@ void Player::updateInventoryImbuement() { bool isInProtectionZone = playerTile && playerTile->hasFlag(TILESTATE_PROTECTIONZONE); // Check if the player is in fight mode bool isInFightMode = hasCondition(CONDITION_INFIGHT); + // Iterate through all items in the player's inventory for (auto item : getAllInventoryItems()) { // Iterate through all imbuement slots on the item @@ -660,11 +657,36 @@ void Player::closeContainer(uint8_t cid) { Container* container = openContainer.container; openContainers.erase(it); - if (container && container->getID() == ITEM_BROWSEFIELD) { + if (!container) { + return; + } + + if (container->isAnykindOfRewardContainer() && !hasAnykindOfRewardContainerOpen()) { + removeEmptyRewards(); + } + + if (container->getID() == ITEM_BROWSEFIELD) { container->decrementReferenceCounter(); } } +void Player::removeEmptyRewards() { + std::erase_if(rewardMap, [this](const auto &rewardBag) { + auto [id, reward] = rewardBag; + if (reward->empty()) { + this->getRewardChest()->removeThing(reward.get(), 1); + return true; + } + return false; + }); +} + +bool Player::hasAnykindOfRewardContainerOpen() const { + return std::ranges::any_of(openContainers.begin(), openContainers.end(), [](const auto &containerPair) { + return containerPair.second.container->isAnykindOfRewardContainer(); + }); +} + void Player::setContainerIndex(uint8_t cid, uint16_t index) { auto it = openContainers.find(cid); if (it == openContainers.end()) { @@ -1034,31 +1056,28 @@ RewardChest* Player::getRewardChest() { return rewardChest; } -Reward* Player::getReward(uint32_t rewardId, bool autoCreate) { +std::shared_ptr Player::getReward(const uint64_t rewardId, const bool autoCreate) { auto it = rewardMap.find(rewardId); if (it != rewardMap.end()) { return it->second; } - if (!autoCreate) { return nullptr; } - Reward* reward = new Reward(); - reward->incrementReferenceCounter(); + const auto reward = std::make_shared(); reward->setAttribute(ItemAttribute_t::DATE, rewardId); rewardMap[rewardId] = reward; - - g_game().internalAddItem(getRewardChest(), reward, INDEX_WHEREEVER, FLAG_NOLIMIT); + g_game().internalAddItem(getRewardChest(), reward.get(), INDEX_WHEREEVER, FLAG_NOLIMIT); return reward; } -void Player::removeReward(uint32_t rewardId) { +void Player::removeReward(uint64_t rewardId) { rewardMap.erase(rewardId); } -void Player::getRewardList(std::vector &rewards) { +void Player::getRewardList(std::vector &rewards) const { rewards.reserve(rewardMap.size()); for (auto &it : rewardMap) { rewards.push_back(it.first); diff --git a/src/creatures/players/player.h b/src/creatures/players/player.h index 4280b77f59e..555f60abb91 100644 --- a/src/creatures/players/player.h +++ b/src/creatures/players/player.h @@ -654,9 +654,9 @@ class Player final : public Creature, public Cylinder { void addConditionSuppressions(uint32_t conditions); void removeConditionSuppressions(uint32_t conditions); - Reward* getReward(uint32_t rewardId, bool autoCreate); - void removeReward(uint32_t rewardId); - void getRewardList(std::vector &rewards); + std::shared_ptr getReward(const uint64_t rewardId, const bool autoCreate); + void removeReward(uint64_t rewardId); + void getRewardList(std::vector &rewards) const; RewardChest* getRewardChest(); DepotChest* getDepotChest(uint32_t depotId, bool autoCreate); @@ -2299,7 +2299,7 @@ class Player final : public Creature, public Cylinder { { SKILL_CRITICAL_HIT_CHANCE, g_configManager().getNumber(CRITICALCHANCE) } }; - std::map rewardMap; + std::map> rewardMap; std::map quickLootContainers; std::vector forgeHistoryVector; @@ -2562,6 +2562,9 @@ class Player final : public Creature, public Cylinder { void updateDamageReductionFromItemImbuement(std::array &combatReductionMap, Item* item, uint16_t combatTypeIndex) const; void updateDamageReductionFromItemAbility(std::array &combatReductionMap, const Item* item, uint16_t combatTypeIndex) const; double_t calculateDamageReduction(double_t currentTotal, int16_t resistance) const; + + void removeEmptyRewards(); + bool hasAnykindOfRewardContainerOpen() const; }; #endif // SRC_CREATURES_PLAYERS_PLAYER_H_ diff --git a/src/io/functions/iologindata_load_player.cpp b/src/io/functions/iologindata_load_player.cpp index 0816ed68c85..89ab6ad72bc 100644 --- a/src/io/functions/iologindata_load_player.cpp +++ b/src/io/functions/iologindata_load_player.cpp @@ -26,3 +26,51 @@ void IOLoginDataLoad::loadPlayerForgeHistory(Player* player, DBResult_ptr result } while (result->next()); } } + +void IOLoginDataLoad::loadRewardItems(Player* player) { + ItemMap itemMap; + std::ostringstream query; + query.str(std::string()); + query << "SELECT `pid`, `sid`, `itemtype`, `count`, `attributes` FROM `player_rewards` WHERE `player_id` = " + << player->getGUID() << " ORDER BY `pid`, `sid` ASC"; + if (auto result = Database::getInstance().storeQuery(query.str())) { + IOLoginData::loadItems(itemMap, result, *player); + bindRewardBag(player, itemMap); + insertItemsIntoRewardBag(itemMap); + } +} + +void IOLoginDataLoad::bindRewardBag(Player* player, IOLoginData::ItemMap &itemMap) { + for (auto &[id, itemPair] : itemMap) { + const auto [item, pid] = itemPair; + if (pid == 0) { + auto reward = player->getReward(item->getAttribute(ItemAttribute_t::DATE), true); + if (reward) { + itemPair = std::pair(reward->getItem(), player->getRewardChest()->getID()); + } + } else { + break; + } + } +} + +void IOLoginDataLoad::insertItemsIntoRewardBag(const IOLoginData::ItemMap &itemMap) { + for (const auto &it : std::views::reverse(itemMap)) { + const std::pair &pair = it.second; + Item* item = pair.first; + int32_t pid = pair.second; + if (pid == 0) { + break; + } + + ItemMap::const_iterator it2 = itemMap.find(pid); + if (it2 == itemMap.end()) { + continue; + } + + Container* container = it2->second.first->getContainer(); + if (container) { + container->internalAddThing(item); + } + } +} diff --git a/src/io/functions/iologindata_load_player.hpp b/src/io/functions/iologindata_load_player.hpp index ccd23a5212d..b5d9445f6be 100644 --- a/src/io/functions/iologindata_load_player.hpp +++ b/src/io/functions/iologindata_load_player.hpp @@ -15,6 +15,11 @@ class IOLoginDataLoad : public IOLoginData { public: static void loadPlayerForgeHistory(Player* player, DBResult_ptr result); + static void loadRewardItems(Player* player); + + private: + static void bindRewardBag(Player* player, ItemMap &itemMap); + static void insertItemsIntoRewardBag(const ItemMap &itemMap); }; #endif // SRC_IO_FUNCTIONS_IOLOGINDATALOAD_HPP_ diff --git a/src/io/functions/iologindata_save_player.cpp b/src/io/functions/iologindata_save_player.cpp index c4ac5b77237..2ec11d8212e 100644 --- a/src/io/functions/iologindata_save_player.cpp +++ b/src/io/functions/iologindata_save_player.cpp @@ -42,3 +42,33 @@ bool IOLoginDataSave::savePlayerForgeHistory(Player* player) { return true; } + +bool IOLoginDataSave::saveRewardItems(Player* player) { + std::ostringstream query; + query.str(std::string()); + query << "DELETE FROM `player_rewards` WHERE `player_id` = " << player->getGUID(); + + if (!Database::getInstance().executeQuery(query.str())) { + return false; + } + + std::vector rewardList; + player->getRewardList(rewardList); + + ItemBlockList itemList; + if (!rewardList.empty()) { + for (const auto &rewardId : rewardList) { + auto reward = player->getReward(rewardId, false); + if (!reward->empty() && (getTimeNow() - rewardId / 1000 <= 60 * 60 * 24 * 7)) { + itemList.emplace_back(0, reward.get()); + } + } + + DBInsert rewardQuery("INSERT INTO `player_rewards` (`player_id`, `pid`, `sid`, `itemtype`, `count`, `attributes`) VALUES "); + PropWriteStream propWriteStream; + if (!saveItems(player, itemList, rewardQuery, propWriteStream)) { + return false; + } + } + return true; +} diff --git a/src/io/functions/iologindata_save_player.hpp b/src/io/functions/iologindata_save_player.hpp index fd9476af764..0ece96cd8c5 100644 --- a/src/io/functions/iologindata_save_player.hpp +++ b/src/io/functions/iologindata_save_player.hpp @@ -15,6 +15,7 @@ class IOLoginDataSave : public IOLoginData { public: static bool savePlayerForgeHistory(Player* player); + static bool saveRewardItems(Player* player); }; #endif // SRC_IO__FUNCTIONS_IOLOGINDATASAVE_HPP_ diff --git a/src/io/iologindata.cpp b/src/io/iologindata.cpp index ec264050d21..1cde53b2009 100644 --- a/src/io/iologindata.cpp +++ b/src/io/iologindata.cpp @@ -505,85 +505,7 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result) { // load depot items itemMap.clear(); - query.str(std::string()); - query << "SELECT `pid`, `sid`, `itemtype`, `count`, `attributes` FROM `player_depotitems` WHERE `player_id` = " << player->getGUID() << " ORDER BY `sid` DESC"; - if ((result = db.storeQuery(query.str()))) { - loadItems(itemMap, result, *player); - - for (ItemMap::const_reverse_iterator it = itemMap.rbegin(), end = itemMap.rend(); it != end; ++it) { - const std::pair &pair = it->second; - Item* item = pair.first; - - int32_t pid = pair.second; - if (pid >= 0 && pid < 100) { - DepotChest* depotChest = player->getDepotChest(pid, true); - if (depotChest) { - depotChest->internalAddThing(item); - item->startDecaying(); - } - } else { - ItemMap::const_iterator it2 = itemMap.find(pid); - if (it2 == itemMap.end()) { - continue; - } - - Container* container = it2->second.first->getContainer(); - if (container) { - container->internalAddThing(item); - item->startDecaying(); - } - } - } - } - - // load reward chest items - itemMap.clear(); - - query.str(std::string()); - query << "SELECT `pid`, `sid`, `itemtype`, `count`, `attributes` FROM `player_rewards` WHERE `player_id` = " << player->getGUID() << " ORDER BY `sid` DESC"; - if ((result = db.storeQuery(query.str()))) { - loadItems(itemMap, result, *player); - - // first loop handles the reward containers to retrieve its date attribute - // for (ItemMap::iterator it = itemMap.begin(), end = itemMap.end(); it != end; ++it) { - for (auto &it : itemMap) { - const std::pair &pair = it.second; - Item* item = pair.first; - - int32_t pid = pair.second; - if (pid >= 0 && pid < 100) { - auto rewardId = item->getAttribute(ItemAttribute_t::DATE); - Reward* reward = player->getReward(rewardId, true); - if (reward) { - it.second = std::pair(reward->getItem(), pid); // update the map with the special reward container - } - } else { - break; - } - } - - // second loop (this time a reverse one) to insert the items in the correct order - // for (ItemMap::const_reverse_iterator it = itemMap.rbegin(), end = itemMap.rend(); it != end; ++it) { - for (const auto &it : std::views::reverse(itemMap)) { - const std::pair &pair = it.second; - Item* item = pair.first; - - int32_t pid = pair.second; - if (pid >= 0 && pid < 100) { - break; - } - - ItemMap::const_iterator it2 = itemMap.find(pid); - if (it2 == itemMap.end()) { - continue; - } - - Container* container = it2->second.first->getContainer(); - if (container) { - container->internalAddThing(item); - } - } - } + IOLoginDataLoad::loadRewardItems(player); // load inbox items itemMap.clear(); @@ -1120,35 +1042,11 @@ bool IOLoginData::savePlayer(Player* player) { } } - // save reward items - query.str(std::string()); - query << "DELETE FROM `player_rewards` WHERE `player_id` = " << player->getGUID(); - - if (!db.executeQuery(query.str())) { + if (!IOLoginDataSave::saveRewardItems(player)) { + SPDLOG_ERROR("[{}] failed to save reward items"); return false; } - std::vector rewardList; - player->getRewardList(rewardList); - - if (!rewardList.empty()) { - DBInsert rewardQuery("INSERT INTO `player_rewards` (`player_id`, `pid`, `sid`, `itemtype`, `count`, `attributes`) VALUES "); - itemList.clear(); - - int running = 0; - for (const auto &rewardId : rewardList) { - Reward* reward = player->getReward(rewardId, false); - // rewards that are empty or older than 7 days aren't stored - if (!reward->empty() && (time(nullptr) - rewardId <= 60 * 60 * 24 * 7)) { - itemList.emplace_back(++running, reward); - } - } - - if (!saveItems(player, itemList, rewardQuery, propWriteStream)) { - return false; - } - } - // save inbox items query.str(std::string()); query << "DELETE FROM `player_inboxitems` WHERE `player_id` = " << player->getGUID(); diff --git a/src/io/iologindata.h b/src/io/iologindata.h index 955a6d06aef..1dfec65dc9b 100644 --- a/src/io/iologindata.h +++ b/src/io/iologindata.h @@ -44,9 +44,8 @@ class IOLoginData { static void addPremiumDays(uint32_t accountId, int32_t addDays); static void removePremiumDays(uint32_t accountId, int32_t removeDays); - private: + protected: using ItemMap = std::map>; - static void loadItems(ItemMap &itemMap, DBResult_ptr result, Player &player); static bool saveItems(const Player* player, const ItemBlockList &itemList, DBInsert &query_insert, PropWriteStream &stream); }; diff --git a/src/items/containers/container.cpp b/src/items/containers/container.cpp index 755d3fa92ae..179f1d0d598 100644 --- a/src/items/containers/container.cpp +++ b/src/items/containers/container.cpp @@ -250,6 +250,24 @@ bool Container::isHoldingItem(const Item* item) const { return false; } +bool Container::isHoldingItemWithId(const uint16_t id) const { + for (ContainerIterator it = iterator(); it.hasNext(); it.advance()) { + const Item* item = *it; + if (item->getID() == id) { + return true; + } + } + return false; +} + +bool Container::isAnykindOfRewardContainer() const { + return getID() == ITEM_REWARD_CHEST || getID() == ITEM_REWARD_CONTAINER || isBrowseFieldAndHoldsRewardContainer(); +} + +bool Container::isBrowseFieldAndHoldsRewardContainer() const { + return getID() == ITEM_BROWSEFIELD && isHoldingItemWithId(ITEM_REWARD_CHEST); +} + void Container::onAddContainerItem(Item* item) { SpectatorHashSet spectators; g_game().map.getSpectators(spectators, getPosition(), false, true, 2, 2, 2, 2); diff --git a/src/items/containers/container.h b/src/items/containers/container.h index 7e6c52e2aba..5b80ca1a024 100644 --- a/src/items/containers/container.h +++ b/src/items/containers/container.h @@ -116,6 +116,7 @@ class Container : public Item, public Cylinder { StashContainerList getStowableItems() const; Item* getItemByIndex(size_t index) const; bool isHoldingItem(const Item* item) const; + bool isHoldingItemWithId(const uint16_t id) const; uint32_t getItemHoldingCount() const; uint32_t getContainerHoldingCount() const; @@ -161,6 +162,9 @@ class Container : public Item, public Cylinder { void startDecaying() override; void stopDecaying() override; + bool isAnykindOfRewardContainer() const; + bool isBrowseFieldAndHoldsRewardContainer() const; + protected: std::ostringstream &getContentDescription(std::ostringstream &os) const; diff --git a/src/items/item.cpp b/src/items/item.cpp index ed22153313f..35d1af587a5 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -446,8 +446,8 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream &propStream) { } case ATTR_WRITTENDATE: { - uint32_t writtenDate; - if (!propStream.read(writtenDate)) { + uint64_t writtenDate; + if (!propStream.read(writtenDate)) { return ATTR_READ_ERROR; } @@ -826,9 +826,9 @@ void Item::serializeAttr(PropWriteStream &propWriteStream) const { propWriteStream.writeString(text); } - if (const uint32_t writtenDate = getAttribute(ItemAttribute_t::DATE)) { + if (const uint64_t writtenDate = getAttribute(ItemAttribute_t::DATE)) { propWriteStream.write(ATTR_WRITTENDATE); - propWriteStream.write(writtenDate); + propWriteStream.write(writtenDate); } const std::string &writer = getString(ItemAttribute_t::WRITER); diff --git a/src/lua/creature/actions.cpp b/src/lua/creature/actions.cpp index e4a5df62560..36af1ff279c 100644 --- a/src/lua/creature/actions.cpp +++ b/src/lua/creature/actions.cpp @@ -304,8 +304,8 @@ ReturnValue Actions::internalUseItem(Player* player, const Position &pos, uint8_ } myRewardChest->setParent(container->getParent()->getTile()); - for (auto &it : player->rewardMap) { - it.second->setParent(myRewardChest); + for (const auto &[mapRewardId, rewardPtr] : player->rewardMap) { + rewardPtr->setParent(myRewardChest); } openContainer = myRewardChest; @@ -314,9 +314,9 @@ ReturnValue Actions::internalUseItem(Player* player, const Position &pos, uint8_ auto rewardId = container->getAttribute(ItemAttribute_t::DATE); // Reward container proxy created when the boss dies if (container->getID() == ITEM_REWARD_CONTAINER && !container->getReward()) { - if (Reward* reward = player->getReward(rewardId, false)) { + if (auto reward = player->getReward(container->getAttribute(ItemAttribute_t::DATE), false)) { reward->setParent(container->getRealParent()); - openContainer = reward; + openContainer = reward.get(); } else { return RETURNVALUE_THISISIMPOSSIBLE; } diff --git a/src/lua/functions/creatures/player/player_functions.cpp b/src/lua/functions/creatures/player/player_functions.cpp index 5c6fcb5763a..60e6342f5ce 100644 --- a/src/lua/functions/creatures/player/player_functions.cpp +++ b/src/lua/functions/creatures/player/player_functions.cpp @@ -633,11 +633,11 @@ int PlayerFunctions::luaPlayerGetReward(lua_State* L) { return 1; } - uint32_t rewardId = getNumber(L, 2); + uint64_t rewardId = getNumber(L, 2); bool autoCreate = getBoolean(L, 3, false); - if (Reward* reward = player->getReward(rewardId, autoCreate)) { - pushUserdata(L, reward); - setItemMetatable(L, -1, reward); + if (auto reward = player->getReward(rewardId, autoCreate)) { + pushUserdata(L, reward.get()); + setItemMetatable(L, -1, reward.get()); } else { pushBoolean(L, false); } @@ -660,19 +660,19 @@ int PlayerFunctions::luaPlayerRemoveReward(lua_State* L) { int PlayerFunctions::luaPlayerGetRewardList(lua_State* L) { // player:getRewardList() - Player* player = getUserdata(L, 1); + const Player* player = getUserdata(L, 1); if (!player) { lua_pushnil(L); return 1; } - std::vector rewardVec; + std::vector rewardVec; player->getRewardList(rewardVec); lua_createtable(L, rewardVec.size(), 0); int index = 0; for (const auto &rewardId : rewardVec) { - lua_pushnumber(L, rewardId); + lua_pushnumber(L, static_cast(rewardId)); lua_rawseti(L, -2, ++index); } return 1;