• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

Feature Auto Loot [TFS 1.3]

psychonaut

Well-Known Member
Joined
Dec 1, 2017
Messages
75
Solutions
6
Reaction score
56
I was bored so i did this code, thanks @Thexamx for the general help.

pZbGxtR.gif


In player.h, above
Code:
std::unordered_set<uint32_t> attackedSet;
Add:
Code:
std::set<uint32_t> autoLootList;

Below:
Code:
bool hasLearnedInstantSpell(const std::string& spellName) const;
Add:
Code:
void addAutoLootItem(uint16_t itemId);
void removeAutoLootItem(uint16_t itemId);
bool getAutoLootItem(uint16_t itemId);

In player.cpp, add these lines:
Code:
void Player::addAutoLootItem(uint16_t itemId)
{
    autoLootList.insert(itemId);
}

void Player::removeAutoLootItem(uint16_t itemId)
{
    autoLootList.erase(itemId);
}

bool Player::getAutoLootItem(const uint16_t itemId)
{
    return autoLootList.find(itemId) != autoLootList.end();
}

In monsters.cpp, before:
Code:
if (!owner || owner->getStaminaMinutes() > 840) {
Add:
Code:
std::string autolooted = "";

Replace:
Code:
corpse->internalAddThing(item);
With:
Code:
                if (owner->getAutoLootItem(item->getID())) {
                    g_game.internalPlayerAddItem(owner, item, true, CONST_SLOT_WHEREEVER);
                    autolooted = autolooted + ", " + item->getNameDescription();
                } else if (g_game.internalAddItem(corpse, item) != RETURNVALUE_NOERROR) {
                   corpse->internalAddThing(item);
               }

Below:
Code:
std::ostringstream ss;
Add:
Code:
std::string lootMsg = corpse->getContentDescription();

And replace
Code:
ss << "Loot of " << nameDescription << ": " << corpse->getContentDescription();
With:
Code:
            if (autolooted != "" and corpse->getContentDescription() == "nothing") {
                lootMsg = autolooted.erase(0,2) + " that was auto looted";
            } else if (autolooted != "") {
                lootMsg =  corpse->getContentDescription() + " and " + autolooted.erase(0,2) + " was auto looted.";
            }

In luascript.cpp, add these lines:
Code:
//Autoloot wrote by Psychonaut#4421

int LuaScriptInterface::luaPlayerAddAutoLootItem(lua_State* L)
{
    // player:addAutoLootItem(itemId)
    Player* player = getUserdata<Player>(L, 1);
    if (!player) {
        lua_pushnil(L);
        return 1;
    }

    uint16_t itemId;
    if (isNumber(L, 2)) {
        itemId = getNumber<uint16_t>(L, 2);
    } else {
        itemId = Item::items.getItemIdByName(getString(L, 2));
        if (itemId == 0) {
            lua_pushnil(L);
            return 1;
        }
    }
    player->addAutoLootItem(itemId);
    pushBoolean(L, true);
    return 1;
}

int LuaScriptInterface::luaPlayerRemoveAutoLootItem(lua_State* L)
{
    // player:removeAutoLootItem(itemId)
    Player* player = getUserdata<Player>(L, 1);
    if (!player) {
        lua_pushnil(L);
        return 1;
    }

    uint16_t itemId;
    if (isNumber(L, 2)) {
        itemId = getNumber<uint16_t>(L, 2);
    } else {
        itemId = Item::items.getItemIdByName(getString(L, 2));
        if (itemId == 0) {
            lua_pushnil(L);
            return 1;
        }
    }
 
    player->removeAutoLootItem(itemId);
    pushBoolean(L, true);

    return 1;
}

int LuaScriptInterface::luaPlayerGetAutoLootItem(lua_State* L)
{
    // player:getAutoLootItem(itemId)
    Player* player = getUserdata<Player>(L, 1);
    if (!player) {
        lua_pushnil(L);
        return 1;
    }

    uint16_t itemId;
    if (isNumber(L, 2)) {
        itemId = getNumber<uint16_t>(L, 2);
    } else {
        itemId = Item::items.getItemIdByName(getString(L, 2));
        if (itemId == 0) {
            lua_pushnil(L);
            return 1;
        }
    }
 
    if (player->getAutoLootItem(itemId)) {
        pushBoolean(L, true);
    } else {
        pushBoolean(L, false);
    }

    return 1;
}

int LuaScriptInterface::luaPlayerGetAutoLootList(lua_State* L)
{
    // player:getAutoLootList()
    Player* player = getUserdata<Player>(L, 1);

    if (player) {
        std::set<uint32_t> value = player->autoLootList;
    
        if (value.size() == 0) {
          lua_pushnil(L);
          return 1;
        }

        int index = 0;
        lua_createtable(L, value.size(), 0);
        for(auto i : value) {
            lua_pushnumber(L, i);
            lua_rawseti(L, -2, ++index);
        }
    
    } else {
        lua_pushnil(L);
    }

    return 1;
}

And in iologindata.cpp, below:
Code:
//load storage map
query.str(std::string());
query << "SELECT `key`, `value` FROM `player_storage` WHERE `player_id` = " << player->getGUID();
if ((result = db.storeQuery(query.str()))) {
    do {
        player->addStorageValue(result->getNumber<uint32_t>("key"), result->getNumber<int32_t>("value"), true);
    } while (result->next());
}

Add:

Code:
//load autoloot list set
query.str(std::string());
query << "SELECT `autoloot_list` FROM `player_autoloot` WHERE `player_id` = " << player->getGUID();
if ((result = db.storeQuery(query.str()))) {
    unsigned long lootlistSize;
    const char* autolootlist = result->getStream("autoloot_list", lootlistSize);
    PropStream propStreamList;
    propStreamList.init(autolootlist, lootlistSize);

    int16_t value;
    int16_t item = propStreamList.read<int16_t>(value);
    while (item) {
        player->addAutoLootItem(value);
        item = propStreamList.read<int16_t>(value);
    }
}

And above:
Code:
//save inbox items

Add:
Code:
//save autolootlist
query.str(std::string());
query << "DELETE FROM `player_autoloot` WHERE `player_id` = " << player->getGUID();
if (!db.executeQuery(query.str())) {
    return false;
}

PropWriteStream propWriteStreamAutoLoot;

for (auto i : player->autoLootList) {
    propWriteStreamAutoLoot.write<uint16_t>(i);
}

size_t lootlistSize;
const char* autolootlist = propWriteStreamAutoLoot.getStream(lootlistSize);

query.str(std::string());

DBInsert autolootQuery("INSERT INTO `player_autoloot` (`player_id`, `autoloot_list`) VALUES ");

    query << player->getGUID() << ',' << db.escapeBlob(autolootlist, lootlistSize);
    if (!autolootQuery.addRow(query)) {
        return false;
    }

if (!autolootQuery.execute()) {
    return false;
}

Note that in both you could use the same stream as conditions and clear it, but idk what you changed or not in the code.

Go to your database
and create a new table named player_autoloot with id(primary/auto increment), player_id(int) and autoloot_list(blob). Something like:

Code:
CREATE TABLE player_autoloot (
    id int NOT NULL AUTO_INCREMENT,
    player_id int NOT NULL,
    autoloot_list blob,
    PRIMARY KEY (id)
);

And then go to data/lib/compat/compat.lua(or where you wanna use it) and add these lines:
Lua:
--AutoLoot written by Psychonaut#4421
function getPlayerAutoLootItem(cid, itemId) local p = Player(cid) return p ~= nil and p:getAutoLootItem(itemId) or false end
function getPlayerAutoLootList(cid) local p = Player(cid) return p ~= nil and p:getAutoLootList() or false end
function removePlayerAutoLootItem(cid, itemId) local p = Player(cid) return p ~= nil and p:removeAutoLootItem(itemId) or false end
function addPlayerAutoLootItem(cid, itemId) local p = Player(cid) return p ~= nil and p:addAutoLootItem(itemId) or false end

These are the commands:

xml:
Code:
<talkaction words="!autoloot" script="autoloot.lua" />
<talkaction words="!add" separator=" " script="addautoloot.lua" />
<talkaction words="!remove" separator=" " script="remautoloot.lua" />

Lua:
function onSay(player, words, param)
    local list = "You're auto looting: "
    local alist = player:getAutoLootList()
 
    if alist then
        for _, item in ipairs(alist) do
            list = list .. (ItemType(item)):getName() .. ", "
        end
    else
        player:sendCancelMessage("The list is empty.")
        return false
    end
    player:sendTextMessage(MESSAGE_INFO_DESCR, list:sub(1, -3))
    return false
end
Lua:
function onSay(player, words, param)
    local itemType = ItemType(param)

    if itemType:getId() == 0 then
        itemType = ItemType(tonumber(param))
        if itemType:getName() == '' then
            player:sendCancelMessage("There is no item with that id or name.")
            return false
        end
    end
    if player:getAutoLootItem(itemType:getId()) then
        player:sendCancelMessage("You're already autolooting this item.")
        return false
    end

    player:addAutoLootItem(itemType:getId())
    player:sendTextMessage(MESSAGE_INFO_DESCR, "You're now auto looting " .. itemType:getName())
    return false
end
Lua:
function onSay(player, words, param)
    local itemType = ItemType(param)
 
    if itemType:getId() == 0 then
        itemType = ItemType(tonumber(param))
        if itemType:getName() == '' then
            player:sendCancelMessage("There is no item with that id or name.")
            return false
        end
    end

    if player:getAutoLootItem(itemType:getId()) then
        player:removeAutoLootItem(itemType:getId())
        player:sendTextMessage(MESSAGE_INFO_DESCR, "You're not auto looting " .. itemType:getName() .. " anymore.")
    else
        player:sendCancelMessage("You're not autolooting this item.")
    end

    return false
end
The function can do a check for id/names but i'm doing in lua as i already need to getting the itemType anyway.
Sometimes when i copy part of a code here the identation goes away(and i can't fix it here), so tab it yourself where it happened.

You can call these functions in a npc if you doesn't wanna use talkactions or anywhere that you want.
 
Last edited by a moderator:
if players are in party who get priority?

The owner of the corpse, who did more dmg, party members can still open the corpse but the loot will be with who's the corpse owner(if he's auto looting), but the party members will get the msg too, like:
Loot of a rat: 3 gold coins that was auto looted
 
If you wanna do something like this when in party:
2LqpX63.png


In monsters.cpp before:
Code:
 owner->getParty()->broadcastPartyLoot(ss.str());

Add:

Code:
ss << " by " << owner->getName();

But remove the dot before in "was auto looted", you can add it later if you want.
 
Looks good, nice release :)
Nice to see non profit contribution to the community.
 
If you wanna do something like this when in party:
2LqpX63.png


In monsters.cpp before:
Code:
 owner->getParty()->broadcastPartyLoot(ss.str());

Add:

Code:
ss << " by " << owner->getName();

But remove the dot before in "was auto looted", you can add it later if you want.
I am saying include it because of DEBUG you could get or Cloning of items!
 
if 2 ppl enable sharing loot and both are loot owners because of party who gets the loot? with the first code you made.
If you believe this to be the case then provide the op with proof so that he/she may address the issue.
 
if 2 ppl enable sharing loot and both are loot owners because of party who gets the loot? with the first code you made.
If one player loots the item, 2nd cant, because it doesnt exist. Its created only once in monsters.cpp while creating loot. Also owner of corpse is only one and to this player item is transfered.
 
Cannot find the monster.cpp part
Code:
if (!owner || owner->getStaminaMinutes() > 840) {
Only code regarding "owner" is
Code:
Item* Monster::getCorpse(Creature* lastHitCreature, Creature* mostDamageCreature)
{
    Item* corpse = Creature::getCorpse(lastHitCreature, mostDamageCreature);
    if (corpse) {
        if (mostDamageCreature) {
            if (mostDamageCreature->getPlayer()) {
                corpse->setCorpseOwner(mostDamageCreature->getID());
            } else {
                const Creature* mostDamageCreatureMaster = mostDamageCreature->getMaster();
                if (mostDamageCreatureMaster && mostDamageCreatureMaster->getPlayer()) {
                    corpse->setCorpseOwner(mostDamageCreatureMaster->getID());
                }
            }
        }
    }
    return corpse;
}

TFS 1.3 compiled today
 
Cant eddit sorry for spam was looking at monster.cpp not monsters.cpp
New to source edit sorry
 
@psychonaut


[ 1%] Building CXX object CMakeFiles/tfs.dir/src/luascript.cpp.o
/root/Otxserver-New/src/luascript.cpp:56:62: error: no ‘int LuaScriptInterface::luaPlayerAddAutoLootItem(lua_State*)’ member function declared in class ‘LuaScriptInterface’
int LuaScriptInterface::luaPlayerAddAutoLootItem(lua_State* L)
^
/root/Otxserver-New/src/luascript.cpp:80:65: error: no ‘int LuaScriptInterface::luaPlayerRemoveAutoLootItem(lua_State*)’ member function declared in class ‘LuaScriptInterface’
int LuaScriptInterface::luaPlayerRemoveAutoLootItem(lua_State* L)
^
/root/Otxserver-New/src/luascript.cpp:106:62: error: no ‘int LuaScriptInterface::luaPlayerGetAutoLootItem(lua_State*)’ member function declared in class ‘LuaScriptInterface’
int LuaScriptInterface::luaPlayerGetAutoLootItem(lua_State* L)
^
/root/Otxserver-New/src/luascript.cpp:135:62: error: no ‘int LuaScriptInterface::luaPlayerGetAutoLootList(lua_State*)’ member function declared in class ‘LuaScriptInterface’
int LuaScriptInterface::luaPlayerGetAutoLootList(lua_State* L)
 
a have this problem too, you have solution ?

[ 1%] Building CXX object CMakeFiles/tfs.dir/src/luascript.cpp.o
/root/Otxserver-New/src/luascript.cpp:56:62: error: no ‘int LuaScriptInterface::luaPlayerAddAutoLootItem(lua_State*)’ member function declared in class ‘LuaScriptInterface’
int LuaScriptInterface::luaPlayerAddAutoLootItem(lua_State* L)
^
/root/Otxserver-New/src/luascript.cpp:80:65: error: no ‘int LuaScriptInterface::luaPlayerRemoveAutoLootItem(lua_State*)’ member function declared in class ‘LuaScriptInterface’
int LuaScriptInterface::luaPlayerRemoveAutoLootItem(lua_State* L)
^
/root/Otxserver-New/src/luascript.cpp:106:62: error: no ‘int LuaScriptInterface::luaPlayerGetAutoLootItem(lua_State*)’ member function declared in class ‘LuaScriptInterface’
int LuaScriptInterface::luaPlayerGetAutoLootItem(lua_State* L)
^
/root/Otxserver-New/src/luascript.cpp:135:62: error: no ‘int LuaScriptInterface::luaPlayerGetAutoLootList(lua_State*)’ member function declared in class ‘LuaScriptInterface’
int LuaScriptInterface::luaPlayerGetAutoLootList(lua_State* L)

a have this problem too, you have solution ?
 
Last edited by a moderator:
@psychonaut
[ 1%] Building CXX object CMakeFiles/tfs.dir/src/luascript.cpp.o
/root/Otxserver-New/src/luascript.cpp:56:62: error: no ‘int LuaScriptInterface::luaPlayerAddAutoLootItem(lua_State*)’ member function declared in class ‘LuaScriptInterface’
int LuaScriptInterface::luaPlayerAddAutoLootItem(lua_State* L)
^
/root/Otxserver-New/src/luascript.cpp:80:65: error: no ‘int LuaScriptInterface::luaPlayerRemoveAutoLootItem(lua_State*)’ member function declared in class ‘LuaScriptInterface’
int LuaScriptInterface::luaPlayerRemoveAutoLootItem(lua_State* L)
^
/root/Otxserver-New/src/luascript.cpp:106:62: error: no ‘int LuaScriptInterface::luaPlayerGetAutoLootItem(lua_State*)’ member function declared in class ‘LuaScriptInterface’
int LuaScriptInterface::luaPlayerGetAutoLootItem(lua_State* L)
^
/root/Otxserver-New/src/luascript.cpp:135:62: error: no ‘int LuaScriptInterface::luaPlayerGetAutoLootList(lua_State*)’ member function declared in class ‘LuaScriptInterface’
int LuaScriptInterface::luaPlayerGetAutoLootList(lua_State* L)
a have this problem too, you have solution ?
a have this problem too, you have solution ?
In luascript.cpp, above:
C++:
registerMethod("Player", "getIdleTime", LuaScriptInterface::luaPlayerGetIdleTime);
Add:
C++:
    registerMethod("Player", "AddAutoLootItem", LuaScriptInterface::luaPlayerAddAutoLootItem);
    registerMethod("Player", "RemoveAutoLootItem", LuaScriptInterface::luaPlayerRemoveAutoLootItem);
    registerMethod("Player", "GetAutoLootItem", LuaScriptInterface::luaPlayerGetAutoLootItem);
    registerMethod("Player", "GetAutoLootList", LuaScriptInterface::luaPlayerGetAutoLootList);
 
I will test a doubt the loot will go to the backpack that owns the item or will always go to the main backpack
 
Back
Top