Feature Auto Loot [TFS 1.3]

Piquenoelmal

I'm in an eternal depression
Joined
Dec 1, 2009
Messages
28
Reaction score
4
Location
Void
Ok, the way to adjust the function is to add this lines in the luascript.h:

C++:
static int luaPlayerAddAutoLootItem(lua_State* L);
        static int luaPlayerRemoveAutoLootItem(lua_State* L);
        static int luaPlayerGetAutoLootItem(lua_State* L);
        static int luaPlayerGetAutoLootList(lua_State* L);
And after that, you have to change this piece of code in monsters.cpp:

C++:
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.";
            }
To this one:

Code:
if (autolooted != "" && 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.";
            }
Well, with this changes mine compiled ok!

Now, I have a problem too.

As I tried to use the talk action commands, an error is reported:

Code:
Lua Script Error: [TalkAction Interface]
data/talkactions/scripts/autoloot.lua:onSay
data/talkactions/scripts/autoloot.lua:23: attempt to call method 'getAutoLootList' (a nil value)
stack traceback:
        [C]: in function 'getAutoLootList'
        data/talkactions/scripts/autoloot.lua:23: in function <data/talkactions/scripts/autoloot.lua:21>
I understand that the method "getAutoLootList" is returning a nil value, so I think that the functions add to compat.lua aren't being loaded.
I already registered the talk action and their scripts.

Anyone can help here?
 

samco

4x4 Developer.
Joined
Jul 3, 2007
Messages
889
Reaction score
176
Location
Spain
C++:
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.";
            }
To this one:

C++:
if (autolooted != "" && 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.";
            }
What's the difference here?

Code:
Lua Script Error: [TalkAction Interface]
data/talkactions/scripts/autoloot.lua:onSay
data/talkactions/scripts/autoloot.lua:23: attempt to call method 'getAutoLootList' (a nil value)
stack traceback:
        [C]: in function 'getAutoLootList'
        data/talkactions/scripts/autoloot.lua:23: in function <data/talkactions/scripts/autoloot.lua:21>
I understand that the method "getAutoLootList" is returning a nil value, so I think that the functions add to compat.lua aren't being loaded.
I already registered the talk action and their scripts.

Anyone can help here?
That error means the program cannot find a function called getAutoLootList, you did something wrong with this one.
 

Stigma

Veteran OT User
Joined
Feb 14, 2015
Messages
4,488
Reaction score
2,044
no, he needs to registerMethod the functions in the main post because psychonaut left them out
in luascript.cpp where the rest of the player registermethods are, add these:
C++:
registerMethod("Player", "addAutoLootItem", LuaScriptInterface::luaPlayerAddAutoLootItem);
registerMethod("Player", "removeAutoLootItem", LuaScriptInterface::luaPlayerRemoveAutoLootItem);
registerMethod("Player", "getAutoLootItem", LuaScriptInterface::luaPlayerGetAutoLootItem);
registerMethod("Player", "getAutoLootList", LuaScriptInterface::luaPlayerGetAutoLootList);
 

Piquenoelmal

I'm in an eternal depression
Joined
Dec 1, 2009
Messages
28
Reaction score
4
Location
Void
What's the difference here?
That error means the program cannot find a function called getAutoLootList, you did something wrong with this one.
The difference is the "&&" instead of the "and". I'm not that good in C++, but I think that the Boolean operator that represents "AND" is "&&", as the "OR" is "||".

no, he needs to registerMethod the functions in the main post because psychonaut left them out
in luascript.cpp where the rest of the player registermethods are, add these:
C++:
registerMethod("Player", "addAutoLootItem", LuaScriptInterface::luaPlayerAddAutoLootItem);
registerMethod("Player", "removeAutoLootItem", LuaScriptInterface::luaPlayerRemoveAutoLootItem);
registerMethod("Player", "getAutoLootItem", LuaScriptInterface::luaPlayerGetAutoLootItem);
registerMethod("Player", "getAutoLootList", LuaScriptInterface::luaPlayerGetAutoLootList);
Thanks, @Vulcan_ . It worked! :)
 

samco

4x4 Developer.
Joined
Jul 3, 2007
Messages
889
Reaction score
176
Location
Spain
The difference is the "&&" instead of the "and". I'm not that good in C++, but I think that the Boolean operator that represents "AND" is "&&", as the "OR" is "||".


Thanks, @Vulcan_ . It worked! :)
I'm sorry, you are right. I was not able to see the difference by reading it lol.
 

_M4G0_

Active Member
Joined
Feb 6, 2016
Messages
388
Reaction score
67

Fix for this?
in player.h used codes
C++:
std::unordered_set<uint32_t> autoLootList;
> No error
player.h
C++:
std::set<uint32_t> autoLootList;
> compiler error
player.h
C++:
std::forward_list<uint32_t> autoLootList;
> compiler error
 

Pedrook

Intermediate OT User
Joined
May 24, 2009
Messages
322
Reaction score
63

Fix for this?
in player.h used codes
C++:
std::unordered_set<uint32_t> autoLootList;
> No error
player.h
C++:
std::set<uint32_t> autoLootList;
> compiler error
player.h
C++:
std::forward_list<uint32_t> autoLootList;
> compiler error
same error here.
 

flaviiojr

Active Member
Joined
Jan 20, 2017
Messages
230
Reaction score
35

Fix for this?
in player.h used codes
C++:
std::unordered_set<uint32_t> autoLootList;
> No error
player.h
C++:
std::set<uint32_t> autoLootList;
> compiler error
player.h
C++:
std::forward_list<uint32_t> autoLootList;
> compiler error
Post your monsters.cpp
 

Raiden

Developer
Joined
Sep 16, 2008
Messages
485
Reaction score
28
Location
Venezuela
This is a mess, I won't point to anyone, but remember that developers who wrote this did it as a hobby and FOR FREE, okay, the code from @psychonaut have some errors, but they might be because he did this because he was bored, and coding is fun except when you have to deal with cry babies complaining about some very simple lines not working. Really guys, if you come to the C++ codes section, try to read and understand code, creating a server is not just to copy and paste other people's systems.

Now, I'm going to TRY to explain every error I think the system have.

first, psychonaut didn't post the code to register the lua functions, and you need to do that so your lua code will recognize the addloot functions.

As @flaviiojr said, on luascript.cpp search for:

Lua:
registerMethod("Player", "getFightMode", LuaScriptInterface::luaPlayerGetFightMode);
That should be your last player method if you are using a recent TFS 1.3, below that add:

Code:
registerMethod("Player", "addAutoLootItem", LuaScriptInterface::luaPlayerAddAutoLootItem);
registerMethod("Player", "removeAutoLootItem", LuaScriptInterface::luaPlayerRemoveAutoLootItem);
registerMethod("Player", "getAutoLootItem", LuaScriptInterface::luaPlayerGetAutoLootItem);
registerMethod("Player", "getAutoLootList", LuaScriptInterface::luaPlayerGetAutoLootList);
Now, go to luascript.h and search for:

Code:
static int luaPlayerGetFightMode(lua_State* L);
And add below that:

Code:
static int luaPlayerAddAutoLootItem(lua_State* L);
static int luaPlayerRemoveAutoLootItem(lua_State* L);
static int luaPlayerGetAutoLootItem(lua_State* L);
static int luaPlayerGetAutoLootList(lua_State* L);
Now, the next error is the one of people claiming that there is no loot message anymore, first there is a typo mistake on this part of code that the OP told everyone to add

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.";            }
If you read carefully, it says "if (autolooted != "" and corpse->....." the AND on C++ is &&, so you have to change that, it will look like this:

Code:
  if (autolooted != "" && 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.";            }
Now, the next mistake is, that you were told to REPLACE the line with "ss << "Loot of " << nameDescription..." so after that last if block you need to add it again but with the lootMsg variable, so one line below add:

Code:
ss << "Loot of " << nameDescription << ": " << lootMsg;
Now the last mistake is that after all those, the system at all doesn't works (it doesn't take the item), that because you where told to REPLACE the line with "corpse->internalAddThing(item);" to the following:

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);
               }
Which is wrong, what you really have to reaplce is the entire if block, so.

REPLACE:

Code:
if (g_game.internalAddItem(corpse, item) != RETURNVALUE_NOERROR) {
                    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);
               }
So your function create loot over monsters.cpp should look like this:

Code:
void MonsterType::createLoot(Container* corpse)
{
    if (g_config.getNumber(ConfigManager::RATE_LOOT) == 0) {
        corpse->startDecaying();
        return;
    }

    Player* owner = g_game.getPlayerByID(corpse->getCorpseOwner());
    std::string autolooted = "";
    if (!owner || owner->getStaminaMinutes() > 840) {
        for (auto it = info.lootItems.rbegin(), end = info.lootItems.rend(); it != end; ++it) {
            auto itemList = createLootItem(*it);
            if (itemList.empty()) {
                continue;
            }

            for (Item* item : itemList) {
                //check containers
                if (Container* container = item->getContainer()) {
                    if (!createLootContainer(container, *it)) {
                        delete container;
                        continue;
                    }
                }

                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);
                }
            }
        }

        if (owner) {
            std::ostringstream ss;
            std::string lootMsg = corpse->getContentDescription();
            //ss << "Loot of " << nameDescription << ": " << corpse->getContentDescription(); -- change for autoloot system
            if (autolooted != "" && corpse->getContentDescription() == "nothing"){
                lootMsg = autolooted.erase(0,2) + " that was autolooted";
            } else if (autolooted != ""){
                lootMsg = corpse->getContentDescription() + " and " + autolooted.erase(0,2) + " was auto looted";
            }

            ss << "Loot of " << nameDescription << ": " << lootMsg;

            if (owner->getParty()) {
                ss << " by " << owner->getName();
                owner->getParty()->broadcastPartyLoot(ss.str());
            } else {
                owner->sendTextMessage(MESSAGE_LOOT, ss.str());
            }
        }
    } else {
        std::ostringstream ss;
        ss << "Loot of " << nameDescription << ": nothing (due to low stamina)";

        if (owner->getParty()) {
            owner->getParty()->broadcastPartyLoot(ss.str());
        } else {
            owner->sendTextMessage(MESSAGE_LOOT, ss.str());
        }
    }

    corpse->startDecaying();
}
Be sure to read my post slowly and step by step to understand what you have to do, and remember, we are helping with hope to receive no more than a thanks in exchange, nobody is perfect, and nobody will like to help a crybaby just because they don't understand the code, maybe I'm missing something too, I read the whole thread and I was able to figure out how to make it works (I'm a developer, but I'm not a C++ developer).

Best regards
 

Pedrook

Intermediate OT User
Joined
May 24, 2009
Messages
322
Reaction score
63
@Raiden thank you very much for fixing the system, the most current and functional, grateful indeed, anyone who has no knowledge was catching, like me, then only gratitude!
Working perfectly.

 

Angel Of Death™

New Member
Joined
Jul 20, 2017
Messages
15
Reaction score
2
what is wrong?
C++:
/home/.ots/src/player.h:1378:8: error: ‘set’ in namespace ‘std’ does not name a                                                                                         template type
   std::set<uint32_t>autoLootList;
        ^
CMakeFiles/tfs.dir/build.make:119: recipe for target 'CMakeFiles/tfs.dir/src/act                                                                                        ions.cpp.o' failed
make[2]: *** [CMakeFiles/tfs.dir/src/actions.cpp.o] Error 1
CMakeFiles/Makefile2:122: recipe for target 'CMakeFiles/tfs.dir/all' failed
make[1]: *** [CMakeFiles/tfs.dir/all] Error 2
Makefile:76: recipe for target 'all' failed
make: *** [all] Error 2
It's happening the same here.

I think the problem is in:
C++:
std::set<uint32_t> autoLootList;
because:
God Of Pain!
Code:
use forward_list<uint32_t>

Fix for this?
in player.h used codes
C++:
std::unordered_set<uint32_t> autoLootList;
> No error
player.h
C++:
std::set<uint32_t> autoLootList;
> compiler error
player.h
C++:
std::forward_list<uint32_t> autoLootList;
> compiler error

go otpch.h
and add this

#include <set>
 

flaviiojr

Active Member
Joined
Jan 20, 2017
Messages
230
Reaction score
35
This is a mess, I won't point to anyone, but remember that developers who wrote this did it as a hobby and FOR FREE, okay, the code from @psychonaut have some errors, but they might be because he did this because he was bored, and coding is fun except when you have to deal with cry babies complaining about some very simple lines not working. Really guys, if you come to the C++ codes section, try to read and understand code, creating a server is not just to copy and paste other people's systems.

Now, I'm going to TRY to explain every error I think the system have.

first, psychonaut didn't post the code to register the lua functions, and you need to do that so your lua code will recognize the addloot functions.

As @flaviiojr said, on luascript.cpp search for:

Lua:
registerMethod("Player", "getFightMode", LuaScriptInterface::luaPlayerGetFightMode);
That should be your last player method if you are using a recent TFS 1.3, below that add:

Code:
registerMethod("Player", "addAutoLootItem", LuaScriptInterface::luaPlayerAddAutoLootItem);
registerMethod("Player", "removeAutoLootItem", LuaScriptInterface::luaPlayerRemoveAutoLootItem);
registerMethod("Player", "getAutoLootItem", LuaScriptInterface::luaPlayerGetAutoLootItem);
registerMethod("Player", "getAutoLootList", LuaScriptInterface::luaPlayerGetAutoLootList);
Now, go to luascript.h and search for:

Code:
static int luaPlayerGetFightMode(lua_State* L);
And add below that:

Code:
static int luaPlayerAddAutoLootItem(lua_State* L);
static int luaPlayerRemoveAutoLootItem(lua_State* L);
static int luaPlayerGetAutoLootItem(lua_State* L);
static int luaPlayerGetAutoLootList(lua_State* L);
Now, the next error is the one of people claiming that there is no loot message anymore, first there is a typo mistake on this part of code that the OP told everyone to add

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.";            }
If you read carefully, it says "if (autolooted != "" and corpse->....." the AND on C++ is &&, so you have to change that, it will look like this:

Code:
  if (autolooted != "" && 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.";            }
Now, the next mistake is, that you were told to REPLACE the line with "ss << "Loot of " << nameDescription..." so after that last if block you need to add it again but with the lootMsg variable, so one line below add:

Code:
ss << "Loot of " << nameDescription << ": " << lootMsg;
Now the last mistake is that after all those, the system at all doesn't works (it doesn't take the item), that because you where told to REPLACE the line with "corpse->internalAddThing(item);" to the following:

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);
               }
Which is wrong, what you really have to reaplce is the entire if block, so.

REPLACE:

Code:
if (g_game.internalAddItem(corpse, item) != RETURNVALUE_NOERROR) {
                    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);
               }
So your function create loot over monsters.cpp should look like this:

Code:
void MonsterType::createLoot(Container* corpse)
{
    if (g_config.getNumber(ConfigManager::RATE_LOOT) == 0) {
        corpse->startDecaying();
        return;
    }

    Player* owner = g_game.getPlayerByID(corpse->getCorpseOwner());
    std::string autolooted = "";
    if (!owner || owner->getStaminaMinutes() > 840) {
        for (auto it = info.lootItems.rbegin(), end = info.lootItems.rend(); it != end; ++it) {
            auto itemList = createLootItem(*it);
            if (itemList.empty()) {
                continue;
            }

            for (Item* item : itemList) {
                //check containers
                if (Container* container = item->getContainer()) {
                    if (!createLootContainer(container, *it)) {
                        delete container;
                        continue;
                    }
                }

                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);
                }
            }
        }

        if (owner) {
            std::ostringstream ss;
            std::string lootMsg = corpse->getContentDescription();
            //ss << "Loot of " << nameDescription << ": " << corpse->getContentDescription(); -- change for autoloot system
            if (autolooted != "" && corpse->getContentDescription() == "nothing"){
                lootMsg = autolooted.erase(0,2) + " that was autolooted";
            } else if (autolooted != ""){
                lootMsg = corpse->getContentDescription() + " and " + autolooted.erase(0,2) + " was auto looted";
            }

            ss << "Loot of " << nameDescription << ": " << lootMsg;

            if (owner->getParty()) {
                ss << " by " << owner->getName();
                owner->getParty()->broadcastPartyLoot(ss.str());
            } else {
                owner->sendTextMessage(MESSAGE_LOOT, ss.str());
            }
        }
    } else {
        std::ostringstream ss;
        ss << "Loot of " << nameDescription << ": nothing (due to low stamina)";

        if (owner->getParty()) {
            owner->getParty()->broadcastPartyLoot(ss.str());
        } else {
            owner->sendTextMessage(MESSAGE_LOOT, ss.str());
        }
    }

    corpse->startDecaying();
}
Be sure to read my post slowly and step by step to understand what you have to do, and remember, we are helping with hope to receive no more than a thanks in exchange, nobody is perfect, and nobody will like to help a crybaby just because they don't understand the code, maybe I'm missing something too, I read the whole thread and I was able to figure out how to make it works (I'm a developer, but I'm not a C++ developer).

Best regards
very good! However there is a missing condition that is enabling a possible crash ...

C++:
                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);
                }
 

flaviiojr

Active Member
Joined
Jan 20, 2017
Messages
230
Reaction score
35
-- Can not edit ...
C++:
                if (owner && 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);
                }
 

Raiden

Developer
Joined
Sep 16, 2008
Messages
485
Reaction score
28
Location
Venezuela
-- Can not edit ...
C++:
                if (owner && 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);
                }
As I said, maybe I'm missing something too, thank you, but, please explain why without the "owner" condition it could crash the server, I don't know if I'm being lazy but I don't get it :p
 

flaviiojr

Active Member
Joined
Jan 20, 2017
Messages
230
Reaction score
35
The player kills the creature, then dies before the function is called, but the owner has already been stored in the variable, the function will search the player and will not find..
 

Togu

Active Member
Joined
Jun 22, 2018
Messages
244
Reaction score
97
Location
Brazil
It took 6 months but it is finally working here kkk

Thanks @Raiden and @flaviiojr

Now I'm gonna develop a button in OTClient that show "Enable auto loot" or "Disable auto loot" when you ctrl + click on item.

Edit: :)

34682
 
Last edited:
Top