From 5cb9ff7b8a014ebcb1bae22a8ac513d143a7b83d Mon Sep 17 00:00:00 2001 From: omeranha <53233626+omeranha@users.noreply.github.com> Date: Wed, 9 Mar 2022 22:41:00 -0300 Subject: [PATCH] [Feature/Enhancement/Custom] - Auto Loot, Quick Looting in stack and Auto Bank function (#184) Auto loot using your manage loot containers quick loot categories and items filter, can be enabled in config.lua! Quick loot in stack, up to 30 corpses, enabled using client option. Auto Bank function, the dropped coins from monsters will be automatically deposited to your bank account, can be enabled in config.lua. --- config.lua.dist | 4 ++ src/config/config_definitions.hpp | 2 + src/config/configmanager.cpp | 2 + src/creatures/creature.cpp | 28 +++++++++ src/creatures/players/player.cpp | 2 +- src/game/game.cpp | 65 ++++++++++++++++++-- src/game/game.h | 6 +- src/server/network/protocol/protocolgame.cpp | 4 +- 8 files changed, 103 insertions(+), 10 deletions(-) diff --git a/config.lua.dist b/config.lua.dist index 6dc9f890faa..90bb009fbd5 100644 --- a/config.lua.dist +++ b/config.lua.dist @@ -65,6 +65,10 @@ allConsoleLog = false -- the non-stackable items will be moved to the selected depot chest(I - XVIII). stashMoving = false depotChest = 4 +autoLoot = false +-- autoBank = true, the dropped coins from monsters will be automatically +-- deposited to your bank account. +autoBank = false -- Stamina in Trainers staminaTrainer = false diff --git a/src/config/config_definitions.hpp b/src/config/config_definitions.hpp index a70c4558570..0bae89a6b5f 100644 --- a/src/config/config_definitions.hpp +++ b/src/config/config_definitions.hpp @@ -67,6 +67,8 @@ enum booleanConfig_t { TOGLE_SAVE_INTERVAL_CLEAN_MAP, STASH_MOVING, TOGLE_IMBUEMENT_SHRINE_STORAGE, + AUTOLOOT, + AUTOBANK, LAST_BOOLEAN_CONFIG }; diff --git a/src/config/configmanager.cpp b/src/config/configmanager.cpp index df9c89aa0db..4c1f8d30b1e 100644 --- a/src/config/configmanager.cpp +++ b/src/config/configmanager.cpp @@ -178,6 +178,8 @@ bool ConfigManager::load() boolean[WEATHER_THUNDER] = getGlobalBoolean(L, "thunderEffect", false); boolean[ALL_CONSOLE_LOG] = getGlobalBoolean(L, "allConsoleLog", false); boolean[TOGGLE_FREE_QUEST] = getGlobalBoolean(L, "toggleFreeQuest", true); + boolean[AUTOLOOT] = getGlobalBoolean(L, "autoLoot", false); + boolean[AUTOBANK] = getGlobalBoolean(L, "autoBank", false); boolean[STAMINA_TRAINER] = getGlobalBoolean(L, "staminaTrainer", false); boolean[STAMINA_PZ] = getGlobalBoolean(L, "staminaPz", false); boolean[SORT_LOOT_BY_CHANCE] = getGlobalBoolean(L, "sortLootByChance", false); diff --git a/src/creatures/creature.cpp b/src/creatures/creature.cpp index 9688fc4f013..46fb3aec5ce 100644 --- a/src/creatures/creature.cpp +++ b/src/creatures/creature.cpp @@ -649,6 +649,7 @@ void Creature::onDeath() Party* party = attackerPlayer->getParty(); if (party && party->getLeader() && party->isSharedExperienceActive() && party->isSharedExperienceEnabled()) { attacker = party->getLeader(); + mostDamageCreature = attacker; } } @@ -726,6 +727,33 @@ bool Creature::dropCorpse(Creature* lastHitCreature, Creature* mostDamageCreatur g_game.internalAddItem(tile, corpse, INDEX_WHEREEVER, FLAG_NOLIMIT); dropLoot(corpse->getContainer(), lastHitCreature); corpse->startDecaying(); + bool corpses = corpse->isRewardCorpse() && (corpse->getID() == ITEM_MALE_CORPSE || corpse->getID() == ITEM_FEMALE_CORPSE); + if (mostDamageCreature && mostDamageCreature->getPlayer() && !corpses) { + Player* player = mostDamageCreature->getPlayer(); + if (g_configManager().getBoolean(AUTOBANK)) { + int32_t money = 0; + if (!corpse->getContainer()) { + return true; + } + + for (Item* item : corpse->getContainer()->getItems()) { + money += item->getWorth(); + g_game.internalRemoveItem(item, money); + } + + if (money > 0) { + player->setBankBalance(player->getBankBalance() + money); + std::ostringstream ss; + ss << "Added " << money << " gold coins to your bank account."; + player->sendTextMessage(MESSAGE_STATUS, ss.str()); + } + } + + if (g_configManager().getBoolean(AUTOLOOT)) { + int32_t pos = tile->getStackposOfItem(player, corpse); + g_dispatcher.addTask(createTask(std::bind(&Game::playerQuickLoot, &g_game, mostDamageCreature->getID(), this->getPosition(), corpse->getClientID(), pos - 1, nullptr, false, true))); + } + } } // Scripting event onDeath diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 881ff0111d5..eacde8e9cce 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -5599,7 +5599,7 @@ void Player::stowItem(Item* item, uint32_t count, bool allItems) { } } else if (item->getContainer()) { itemDict = item->getContainer()->getStowableItems(); - for (Item* containerItem : item->getContainer()->getItems()) { + for (Item* containerItem : item->getContainer()->getItems(true)) { uint32_t depotChest = g_configManager().getNumber(DEPOTCHEST); bool validDepot = depotChest > 0 && depotChest < 19; if (g_configManager().getBoolean(STASH_MOVING) && containerItem && !containerItem->isStackable() && validDepot) { diff --git a/src/game/game.cpp b/src/game/game.cpp index b5048c505cc..8059b6cfa01 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -4455,7 +4455,7 @@ void Game::playerLookInBattleList(uint32_t playerId, uint32_t creatureId) g_events->eventPlayerOnLookInBattleList(player, creature, lookDistance); } -void Game::playerQuickLoot(uint32_t playerId, const Position& pos, uint16_t spriteId, uint8_t stackPos, Item* defaultItem) +void Game::playerQuickLoot(uint32_t playerId, const Position& pos, uint16_t spriteId, uint8_t stackPos, Item* defaultItem, bool lootAllCorpses, bool autoLoot) { Player* player = getPlayerByID(playerId); if (!player) { @@ -4467,12 +4467,12 @@ void Game::playerQuickLoot(uint32_t playerId, const Position& pos, uint16_t spri SchedulerTask* task = createSchedulerTask(delay, std::bind( &Game::playerQuickLoot, this, player->getID(), pos, - spriteId, stackPos, defaultItem)); + spriteId, stackPos, defaultItem, lootAllCorpses, autoLoot)); player->setNextActionTask(task); return; } - if (pos.x != 0xffff) { + if (!autoLoot && pos.x != 0xffff) { if (!Position::areInRange<1, 1, 0>(pos, player->getPosition())) { //need to walk to the corpse first before looting it std::forward_list listDir; @@ -4481,7 +4481,7 @@ void Game::playerQuickLoot(uint32_t playerId, const Position& pos, uint16_t spri SchedulerTask* task = createSchedulerTask(0, std::bind( &Game::playerQuickLoot, this, player->getID(), pos, - spriteId, stackPos, defaultItem)); + spriteId, stackPos, defaultItem, lootAllCorpses, autoLoot)); player->setNextWalkActionTask(task); } else { player->sendCancelMessage(RETURNVALUE_THEREISNOWAY); @@ -4517,6 +4517,10 @@ void Game::playerQuickLoot(uint32_t playerId, const Position& pos, uint16_t spri Container* corpse = nullptr; if (pos.x == 0xffff) { corpse = item->getParent()->getContainer(); + if (corpse && corpse->getID() == ITEM_BROWSEFIELD) { + corpse = item->getContainer(); + browseField = true; + } } else { corpse = item->getContainer(); } @@ -4534,7 +4538,7 @@ void Game::playerQuickLoot(uint32_t playerId, const Position& pos, uint16_t spri } } - if (pos.x == 0xffff) { + if (pos.x == 0xffff && !browseField) { uint32_t worth = item->getWorth(); ObjectCategory_t category = getObjectCategory(item); ReturnValue ret = internalQuickLootItem(player, item, category); @@ -4573,13 +4577,62 @@ void Game::playerQuickLoot(uint32_t playerId, const Position& pos, uint16_t spri if (corpse->isRewardCorpse()) { g_actions->useItem(player, pos, 0, corpse, false); } else { - internalQuickLootCorpse(player, corpse); + if (!lootAllCorpses) { + internalQuickLootCorpse(player, corpse); + } else { + playerLootAllCorpses(player, pos, lootAllCorpses); + } } } return; } +void Game::playerLootAllCorpses(Player* player, const Position& pos, bool lootAllCorpses) { + if (lootAllCorpses) { + Tile *tile = g_game.map.getTile(pos.x, pos.y, pos.z); + if (!tile) { + player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); + return; + } + + const TileItemVector *itemVector = tile->getItemList(); + uint16_t corpses = 0; + for (Item *tileItem: *itemVector) { + if (!tileItem) { + continue; + } + + Container *tileCorpse = tileItem->getContainer(); + if (!tileCorpse || !tileCorpse->isCorpse() || tileCorpse->hasAttribute(ITEM_ATTRIBUTE_UNIQUEID) || tileCorpse->hasAttribute(ITEM_ATTRIBUTE_ACTIONID)) { + continue; + } + + if (!tileCorpse->isRewardCorpse() && !player->canOpenCorpse(tileCorpse->getCorpseOwner())) { + continue; + } + + corpses++; + internalQuickLootCorpse(player, tileCorpse); + if (corpses >= 30) { + break; + } + } + + if (corpses > 0) { + if (corpses > 1) { + std::stringstream string; + string << "You looted " << corpses << " corpses."; + player->sendTextMessage(MESSAGE_LOOT, string.str()); + } + + return; + } + } + + browseField = false; +} + void Game::playerSetLootContainer(uint32_t playerId, ObjectCategory_t category, const Position& pos, uint16_t spriteId, uint8_t stackPos) { Player* player = getPlayerByID(playerId); diff --git a/src/game/game.h b/src/game/game.h index 12d0d7f69f5..fca790a2eb9 100644 --- a/src/game/game.h +++ b/src/game/game.h @@ -315,8 +315,9 @@ class Game void playerSetFightModes(uint32_t playerId, FightMode_t fightMode, bool chaseMode, bool secureMode); void playerLookAt(uint32_t playerId, const Position& pos, uint8_t stackPos); void playerLookInBattleList(uint32_t playerId, uint32_t creatureId); - void playerQuickLoot(uint32_t playerId, const Position& pos, - uint16_t spriteId, uint8_t stackPos, Item* defaultItem = nullptr); + void playerQuickLoot(uint32_t playerId, const Position& pos, uint16_t spriteId, uint8_t stackPos, + Item* defaultItem = nullptr, bool lootAllCorpses = false, bool autoLoot = false); + void playerLootAllCorpses(Player* player, const Position& pos, bool lootAllCorpses); void playerSetLootContainer(uint32_t playerId, ObjectCategory_t category, const Position& pos, uint16_t spriteId, uint8_t stackPos); void playerClearLootContainer(uint32_t playerId, ObjectCategory_t category);; @@ -608,6 +609,7 @@ class Game static constexpr int32_t SUNRISE = 360; bool isDay = false; + bool browseField = false; GameState_t gameState = GAME_STATE_NORMAL; WorldType_t worldType = WORLD_TYPE_PVP; diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index c57c05942a3..da853f7a2f5 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -1269,7 +1269,9 @@ void ProtocolGame::parseQuickLoot(NetworkMessage &msg) Position pos = msg.getPosition(); uint16_t spriteId = msg.get(); uint8_t stackpos = msg.getByte(); - addGameTask(&Game::playerQuickLoot, player->getID(), pos, spriteId, stackpos, nullptr); + bool lootAllCorpses = msg.getByte(); + bool autoLoot = msg.getByte(); + addGameTask(&Game::playerQuickLoot, player->getID(), pos, spriteId, stackpos, nullptr, lootAllCorpses, autoLoot); } void ProtocolGame::parseLootContainer(NetworkMessage &msg)