• 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!

Player Loot Rate

zexus

Member
Joined
Oct 1, 2016
Messages
133
Reaction score
18
The only way i know and found about change loot rates is to change global loot rate
On config.lua (rateLoot = 1.0)

Is this possible to edit in a OT to change player loot rate for just one player?
There is a lot system a want to do with this...

Is anybody know how to do it?

---
obs1: I'm already know about this mod [MOD] Loot Ring
But i want to change original loot rate, not create another loot system to every kill...
obs2: Sources i'm using? Fir3element/3777
 
Solution
Since I have not worked with older tfs versions for a while, I don't have the tools set up to compile and test it, so I'd assume you will get compile errors, if you do, let me know.

As you can see, I'm not very good at styiling my posts, so take care when following the steps to minimize the possibility of silly errors.


Start:


Add this on player.h (Fir3element/3777):
C++:
uint8_t specialLootRate;


and this (Fir3element/3777):
C++:
uint8_t getSpecialLootRate() const {
    return specialLootRate;
}

void setSpecialLootRate(uint8_t newRate) {
    specialLootRate= newRate;
}


In monsters.h line 91 (Fir3element/3777) change the createChildLoot signature to this:...
First of all: what project base you use? TFS 0.2.. 0.3... 1.x...


You need work in monsters.cpp file ... Use ("owner" obj -> getStorageValue(YOUR_ID, value) && value == 1) in loot creation... :)

Create a storage value to represent your extra loot rate or something like that.
 
Last edited:
this is really a good workaround @Discovery, I was thinking in creating a whole new system with variables to store in database this new lootrate that would require a masschange in many things. The problem I can think is just which storage to use, the lastHitKiller, mostDamageKiller, the map of everyone envolved or a median of all of these above?
 
He is using TFS 0.4, which I deduced by skimming his source files. There is a function you may use to change rates:
Lua:
doPlayerSetRate(cid, type, value)

This function does not exist in TFS 1.3, if I remember correctly.
 
First of all: what project base you use? TFS 0.2.. 0.3... 1.x...


You need work in monsters.cpp file ... Use ("owner" obj -> getStorageValue(YOUR_ID, value) && value == 1) in loot creation... :)

Create a storage value to represent your extra loot rate or something like that.

I do using 0.4, but idk how to edit source code, if it's not a lot work and hard, pls show to us

He is using TFS 0.4, which I deduced by skimming his source files. There is a function you may use to change rates:
Lua:
doPlayerSetRate(cid, type, value)

This function does not exist in TFS 1.3, if I remember correctly.

This function isn't only to:
doPlayerSetRate(cid, SKILL__LEVEL, rates)
???

I don't found nothing about using it to loot

---

That's sad to is not simple to implement, searching i found a lot people looking for this, and always stuck on that mod to remade the loot
 
He is using TFS 0.4, which I deduced by skimming his source files. There is a function you may use to change rates:
Lua:
doPlayerSetRate(cid, type, value)

This function does not exist in TFS 1.3, if I remember correctly.
Sadly, loot is not one of the constants or variables rates in that server's definitions.
 
Sadly, loot is not one of the constants or variables rates in that server's definitions.

86044581_facebookemoticonsap-300x300.jpg
 
First of all: what project base you use? TFS 0.2.. 0.3... 1.x...


You need work in monsters.cpp file ... Use ("owner" obj -> getStorageValue(YOUR_ID, value) && value == 1) in loot creation... :)

Create a storage value to represent your extra loot rate or something like that.

Are u mean to create a storage to store the loot rates for each player with a value, and in the function of loot create in somewhere in the sources change something just before create loot?
 
Sadly, loot is not one of the constants or variables rates in that server's definitions.

That is correct, it doesn’t. However, it can be used to create a custom function similar to the “loot ring” that was released.
 
Are u mean to create a storage to store the loot rates for each player with a value, and in the function of loot create in somewhere in the sources change something just before create loot?

Is not about put a rate for every player.

Config.lua RATE_LOOT determines the looting but if your player have this storage... increase the loot probability.
 
Maybe that script can bring some light to this topic or make your life easier. It was tested on 0.3.6, but there is way less difference between 0.4 and 0.3.6 than between 0.4 and 1.X.

[MOD] Loot Ring
 
Maybe that script can bring some light to this topic or make your life easier. It was tested on 0.3.6, but there is way less difference between 0.4 and 0.3.6 than between 0.4 and 1.X.

[MOD] Loot Ring

That's exacly like the script he posted on first topic, it re create the loot, it make the server create 2 loots
He know and dont want, me either...

Is not about put a rate for every player.

Config.lua RATE_LOOT determines the looting but if your player have this storage... increase the loot probability.

increase where, how? sorry i just not understand, and its not about you, i'm noob, just want to do some system with this, if is possible ofc...
is that on sources right? it's to change something on: Item* MonsterType::createLoot(const LootBlock& lootBlock)
Fir3element/3777
so there is a way to set/get a storage value in source code?
 
That's exacly like the script he posted on first topic, it re create the loot, it make the server create 2 loots
He know and dont want, me either...



increase where, how? sorry i just not understand, and its not about you, i'm noob, just want to do some system with this, if is possible ofc...
is that on sources right? it's to change something on: Item* MonsterType::createLoot(const LootBlock& lootBlock)
Fir3element/3777
so there is a way to set/get a storage value in source code?


Have lootChance in monsters, right? This suppose variable will multiply more times this lootchance, like the LOOT_RATE does.. the same way
 
The only way I'd do this is via sources. I haven't been able to compile on Win10, though.
tXEt7Jz.png
 
Since I have not worked with older tfs versions for a while, I don't have the tools set up to compile and test it, so I'd assume you will get compile errors, if you do, let me know.

As you can see, I'm not very good at styiling my posts, so take care when following the steps to minimize the possibility of silly errors.


Start:


Add this on player.h (Fir3element/3777):
C++:
uint8_t specialLootRate;


and this (Fir3element/3777):
C++:
uint8_t getSpecialLootRate() const {
    return specialLootRate;
}

void setSpecialLootRate(uint8_t newRate) {
    specialLootRate= newRate;
}


In monsters.h line 91 (Fir3element/3777) change the createChildLoot signature to this:
C++:
bool createChildLoot(Container* parent, const LootBlock& lootBlock, Player* owner);


In monsters.h change this line (Fir3element/3777) to this:
C++:
Item* createLoot(const LootBlock& lootBlock, Player* owner);


In monsters.h change this line (Fir3element/3777) to this:
C++:
static uint16_t getLootRandom(Player* owner);


In monsters.cpp change the createLoot function (Fir3element/3777) to this:
C++:
Item* MonsterType::createLoot(const LootBlock& lootBlock, Player* owner)
{
    uint16_t item = lootBlock.ids[0], random = Monsters::getLootRandom(owner);
    if(lootBlock.ids.size() > 1)
        item = lootBlock.ids[random_range((size_t)0, lootBlock.ids.size() - 1)];

    Item* tmpItem = NULL;
    if(Item::items[item].stackable)
    {
        if(random < lootBlock.chance)
            tmpItem = Item::CreateItem(item, (random % lootBlock.count + 1));
    }
    else if(random < lootBlock.chance)
        tmpItem = Item::CreateItem(item, 0);

    if(!tmpItem)
        return NULL;

    if(lootBlock.subType != -1)
        tmpItem->setSubType(lootBlock.subType);

    if(lootBlock.actionId != -1)
        tmpItem->setActionId(lootBlock.actionId, false);

    if(lootBlock.uniqueId != -1)
        tmpItem->setUniqueId(lootBlock.uniqueId);

    if(!lootBlock.text.empty())
        tmpItem->setText(lootBlock.text);

    return tmpItem;
}


In monsters.cpp line 89 (Fir3element/3777) change the function getLootRandom to this:
C++:
uint16_t Monsters::getLootRandom(Player* owner)
{
    if (owner && owner->getSpecialLootRate() > 0) {
        return (uint16_t)std::ceil((double)random_range(0, MAX_LOOTCHANCE) / (g_config.getDouble(ConfigManager::SPECIAL_RATE_LOOT) + owner->getSpecialLootRate()));
    }
    return (uint16_t)std::ceil((double)random_range(0, MAX_LOOTCHANCE) / g_config.getDouble(ConfigManager::RATE_LOOT));
}


Inside monsters.cpp (Fir3element/3777), replace the whole dropLoot function to this:
C++:
void MonsterType::dropLoot(Container* corpse)
{
    uint32_t ownerId = corpse->getCorpseOwner();
    Player* owner = NULL;
    if (ownerId) {
        owner = g_game.getPlayerByGuid(ownerId);
    }

    Item* tmpItem = NULL;
    for(LootItems::const_iterator it = lootItems.begin(); it != lootItems.end() && !corpse->full(); ++it)
    {
        if((tmpItem = createLoot(*it, owner)))
        {
            if(Container* container = tmpItem->getContainer())
            {
                if(createChildLoot(container, (*it), owner))
                    corpse->__internalAddThing(tmpItem);
                else
                    delete container;
            }
            else
                corpse->__internalAddThing(tmpItem);
        }
    }

    corpse->__startDecaying();

    if(!owner)
        return;

    LootMessage_t message = lootMessage;
    if(message == LOOTMSG_IGNORE)
        message = (LootMessage_t)g_config.getNumber(ConfigManager::LOOT_MESSAGE);

    if(message < LOOTMSG_PLAYER)
        return;

    std::stringstream ss;
    ss << "Loot of " << nameDescription << ": " << corpse->getContentDescription() << ".";
    if(owner->getParty() && message > LOOTMSG_PLAYER)
        owner->getParty()->broadcastMessage((MessageClasses)g_config.getNumber(ConfigManager::LOOT_MESSAGE_TYPE), ss.str());
    else if(message == LOOTMSG_PLAYER || message == LOOTMSG_BOTH)
        owner->sendTextMessage((MessageClasses)g_config.getNumber(ConfigManager::LOOT_MESSAGE_TYPE), ss.str());
}


In monsters.cpp change the function createChildLoot (Fir3element/3777) to this:
C++:
bool MonsterType::createChildLoot(Container* parent, const LootBlock& lootBlock, Player* owner)
{
    LootItems::const_iterator it = lootBlock.childLoot.begin();
    if(it == lootBlock.childLoot.end())
        return true;

    Item* tmpItem = NULL;
    for(; it != lootBlock.childLoot.end() && !parent->full(); ++it)
    {
        if((tmpItem = createLoot(*it, owner)))
        {
            if(Container* container = tmpItem->getContainer())
            {
                if(createChildLoot(container, (*it), owner))
                    parent->__internalAddThing(container);
                else
                    delete container;
            }
            else
                parent->__internalAddThing(tmpItem);
        }
    }

    return !parent->empty();
}

Thats it for the loot system changes, now we need to set the special rate to the players and save it to the database, so lets do it:


Start adding a column on the player table, execute this query on your database:
Code:
ALTER TABLE players ADD COLUMN `special_rate_loot` TINYINT NOT NULL DEFAULT 0


In iologindata.cpp (Fir3element/3777) change the loadPlayer to this:
C++:
bool IOLoginData::loadPlayer(Player* player, const std::string& name, bool preLoad /*= false*/)
{
    Database* db = Database::getInstance();
    DBQuery query;
    query << "SELECT `id`, `account_id`, `group_id`, `world_id`, `sex`, `vocation`, `experience`, `level`, "
    << "`maglevel`, `health`, `healthmax`, `blessings`, `mana`, `manamax`, `manaspent`, `soul`, `lookbody`, "
    << "`lookfeet`, `lookhead`, `looklegs`, `looktype`, `lookaddons`, `posx`, `posy`, `posz`, `cap`, "
    << "`lastlogin`, `lastlogout`, `lastip`, `conditions`, `skull`, `skulltime`, `guildnick`, `rank_id`, "
    << "`town_id`, `balance`, `stamina`, `direction`, `loss_experience`, `loss_mana`, `loss_skills`, "
    << "`loss_containers`, `loss_items`, `marriage`, `promotion`, `description`, `special_rate_loot` FROM `players` WHERE "
    << "`name` " << db->getStringComparer() << db->escapeString(name) << " AND `world_id` = "
    << g_config.getNumber(ConfigManager::WORLD_ID) << " AND `deleted` = 0 LIMIT 1";

    DBResult* result;
    if(!(result = db->storeQuery(query.str())))
        return false;

    uint32_t accountId = result->getDataInt("account_id");
    if(accountId < 1)
    {
        result->free();
        return false;
    }

    Account account = loadAccount(accountId, true);
    player->account = account.name;
    player->accountId = accountId;

    Group* group = Groups::getInstance()->getGroup(result->getDataInt("group_id"));
    player->setGroup(group);

    player->setGUID(result->getDataInt("id"));
    player->premiumDays = account.premiumDays;

    nameCacheMap[player->getGUID()] = name;
    guidCacheMap[name] = player->getGUID();
    if(preLoad)
    {
        //only loading basic info
        result->free();
        return true;
    }

    player->setHasSpecialLootRate(result->getDataInt("special_rate_loot"));
    player->nameDescription += result->getDataString("description");
    player->setSex(result->getDataInt("sex"));
    if(g_config.getBool(ConfigManager::STORE_DIRECTION))
        player->setDirection((Direction)result->getDataInt("direction"));

    player->level = std::max((uint32_t)1, (uint32_t)result->getDataInt("level"));
    uint64_t currExpCount = Player::getExpForLevel(player->level), nextExpCount = Player::getExpForLevel(
        player->level + 1), experience = (uint64_t)result->getDataLong("experience");
    if(experience < currExpCount || experience > nextExpCount)
        experience = currExpCount;

    player->experience = experience;
    player->levelPercent = 0;
    if(currExpCount < nextExpCount)
        player->levelPercent = Player::getPercentLevel(player->experience - currExpCount, nextExpCount - currExpCount);

    player->soul = result->getDataInt("soul");
    player->capacity = result->getDataInt("cap");
    player->setStamina(result->getDataLong("stamina"));
    player->marriage = result->getDataInt("marriage");

    player->balance = result->getDataLong("balance");
    if(g_config.getBool(ConfigManager::BLESSINGS) && (player->isPremium()
        || !g_config.getBool(ConfigManager::BLESSING_ONLY_PREMIUM)))
        player->blessings = result->getDataInt("blessings");

    uint64_t conditionsSize = 0;
    const char* conditions = result->getDataStream("conditions", conditionsSize);

    PropStream propStream;
    propStream.init(conditions, conditionsSize);

    Condition* condition;
    while((condition = Condition::createCondition(propStream)))
    {
        if(condition->unserialize(propStream))
            player->storedConditionList.push_back(condition);
        else
            delete condition;
    }

    player->vocationId = result->getDataInt("vocation");
    player->setPromotionLevel(result->getDataInt("promotion"));

    player->health = result->getDataInt("health");
    player->healthMax = result->getDataInt("healthmax");
    player->mana = result->getDataInt("mana");
    player->manaMax = result->getDataInt("manamax");

    player->magLevel = result->getDataInt("maglevel");
    uint64_t nextManaCount = player->vocation->getReqMana(player->magLevel + 1), manaSpent = result->getDataLong("manaspent");
    if(manaSpent > nextManaCount)
        manaSpent = 0;

    player->manaSpent = manaSpent;
    player->magLevelPercent = Player::getPercentLevel(player->manaSpent, nextManaCount);
    if(!group || !group->getOutfit())
    {
        player->defaultOutfit.lookType = result->getDataInt("looktype");
        uint32_t outfitId = Outfits::getInstance()->getOutfitId(player->defaultOutfit.lookType);

        bool wearable = true;
        if(outfitId > 0)
        {
            Outfit outfit;
            wearable = Outfits::getInstance()->getOutfit(outfitId, player->getSex(true), outfit);
            if(wearable && player->defaultOutfit.lookType != outfit.lookType)
                player->defaultOutfit.lookType = outfit.lookType;
        }

        if(!wearable) //just pick the first default outfit we can find
        {
            const OutfitMap& defaultOutfits = Outfits::getInstance()->getOutfits(player->getSex(true));
            if(!defaultOutfits.empty())
            {
                Outfit newOutfit = (*defaultOutfits.begin()).second;
                player->defaultOutfit.lookType = newOutfit.lookType;
            }
        }
    }
    else
        player->defaultOutfit.lookType = group->getOutfit();

    player->defaultOutfit.lookHead = result->getDataInt("lookhead");
    player->defaultOutfit.lookBody = result->getDataInt("lookbody");
    player->defaultOutfit.lookLegs = result->getDataInt("looklegs");
    player->defaultOutfit.lookFeet = result->getDataInt("lookfeet");
    player->defaultOutfit.lookAddons = result->getDataInt("lookaddons");

    player->currentOutfit = player->defaultOutfit;
    Skulls_t skull = SKULL_RED;
    if(g_config.getBool(ConfigManager::USE_BLACK_SKULL))
        skull = (Skulls_t)result->getDataInt("skull");

    player->setSkullEnd((time_t)result->getDataInt("skulltime"), true, skull);
    player->town = result->getDataInt("town_id");
    if(Town* town = Towns::getInstance()->getTown(player->town))
        player->setMasterPosition(town->getPosition());

    player->setLossPercent(LOSS_EXPERIENCE, result->getDataInt("loss_experience"));
    player->setLossPercent(LOSS_MANA, result->getDataInt("loss_mana"));
    player->setLossPercent(LOSS_SKILLS, result->getDataInt("loss_skills"));
    player->setLossPercent(LOSS_CONTAINERS, result->getDataInt("loss_containers"));
    player->setLossPercent(LOSS_ITEMS, result->getDataInt("loss_items"));

    player->loginPosition = Position(result->getDataInt("posx"), result->getDataInt("posy"), result->getDataInt("posz"));
    player->lastLogin = result->getDataLong("lastlogin");
    player->lastLogout = result->getDataLong("lastlogout");

    player->lastIP = result->getDataInt("lastip");
    if(!player->loginPosition.x || !player->loginPosition.y)
        player->loginPosition = player->getMasterPosition();

    const uint32_t rankId = result->getDataInt("rank_id");
    const std::string nick = result->getDataString("guildnick");

    result->free();
    if(rankId > 0)
    {
        query.str("");
        query << "SELECT `guild_ranks`.`name` AS `rank`, `guild_ranks`.`guild_id` AS `guildid`, `guild_ranks`.`level` AS `level`, `guilds`.`name` AS `guildname` FROM `guild_ranks`, `guilds` WHERE `guild_ranks`.`id` = " << rankId << " AND `guild_ranks`.`guild_id` = `guilds`.`id` LIMIT 1";
        if((result = db->storeQuery(query.str())))
        {
            player->guildId = result->getDataInt("guildid");
            player->guildName = result->getDataString("guildname");
            player->guildLevel = (GuildLevel_t)result->getDataInt("level");

            player->rankId = rankId;
            player->rankName = result->getDataString("rank");
            player->guildNick = nick;
            result->free();

            query.str("");
            query << "SELECT `id`, `guild_id`, `enemy_id` FROM `guild_wars` WHERE (`guild_id` = "
                << player->guildId << " OR `enemy_id` = " << player->guildId << ") AND `status` IN (1,4)";
            if((result = db->storeQuery(query.str())))
            {
                War_t war;
                do
                {
                    uint32_t guild = result->getDataInt("guild_id");
                    if(player->guildId == guild)
                    {
                        war.type = WAR_ENEMY;
                        war.war = result->getDataInt("id");
                        player->addEnemy(result->getDataInt("enemy_id"), war);
                    }
                    else
                    {
                        war.type = WAR_GUILD;
                        war.war = result->getDataInt("id");
                        player->addEnemy(guild, war);
                    }
                }
                while(result->next());
                result->free();
            }
        }
    }
    else if(g_config.getBool(ConfigManager::INGAME_GUILD_MANAGEMENT))
    {
        query.str("");
        query << "SELECT `guild_id` FROM `guild_invites` WHERE `player_id` = " << player->getGUID();
        if((result = db->storeQuery(query.str())))
        {
            do
                player->invitedToGuildsList.push_back((uint32_t)result->getDataInt("guild_id"));
            while(result->next());
            result->free();
        }
    }

    query.str("");
    query << "SELECT `password` FROM `accounts` WHERE `id` = " << accountId << " LIMIT 1";
    if(!(result = db->storeQuery(query.str())))
        return false;

    player->password = result->getDataString("password");
    result->free();

    // we need to find out our skills
    // so we query the skill table
    query.str("");
    query << "SELECT `skillid`, `value`, `count` FROM `player_skills` WHERE `player_id` = " << player->getGUID();
    if((result = db->storeQuery(query.str())))
    {
        //now iterate over the skills
        do
        {
            int16_t skillId = result->getDataInt("skillid");
            if(skillId < SKILL_FIRST || skillId > SKILL_LAST)
                continue;

            uint32_t skillLevel = result->getDataInt("value");
            uint64_t nextSkillCount = player->vocation->getReqSkillTries(
                skillId, skillLevel + 1), skillCount = result->getDataLong("count");
            if(skillCount > nextSkillCount)
                skillCount = 0;

            player->skills[skillId][SKILL_LEVEL] = skillLevel;
            player->skills[skillId][SKILL_TRIES] = skillCount;
            player->skills[skillId][SKILL_PERCENT] = Player::getPercentLevel(skillCount, nextSkillCount);
        }
        while(result->next());
        result->free();
    }

    query.str("");
    query << "SELECT `player_id`, `name` FROM `player_spells` WHERE `player_id` = " << player->getGUID();
    if((result = db->storeQuery(query.str())))
    {
        do
            player->learnedInstantSpellList.push_back(result->getDataString("name"));
        while(result->next());
        result->free();
    }

    ItemMap itemMap;
    ItemMap::iterator it;

    //load inventory items
    query.str("");
    query << "SELECT `pid`, `sid`, `itemtype`, `count`, `attributes` FROM `player_items` WHERE `player_id` = " << player->getGUID() << " ORDER BY `sid` DESC";
    if((result = db->storeQuery(query.str())))
    {
        loadItems(itemMap, result);
        for(ItemMap::reverse_iterator rit = itemMap.rbegin(); rit != itemMap.rend(); ++rit)
        {
            Item* item = rit->second.first;
            int32_t pid = rit->second.second;
            if(pid > 0 && pid < 11)
                player->__internalAddThing(pid, item);
            else
            {
                it = itemMap.find(pid);
                if(it != itemMap.end())
                {
                    if(Container* container = it->second.first->getContainer())
                        container->__internalAddThing(item);
                }
            }
        }

        result->free();
        itemMap.clear();
    }

    //load depot items
    query.str("");
    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);
        for(ItemMap::reverse_iterator rit = itemMap.rbegin(); rit != itemMap.rend(); ++rit)
        {
            Item* item = rit->second.first;
            int32_t pid = rit->second.second;
            if(pid >= 0 && pid < 100)
            {
                if(Container* c = item->getContainer())
                {
                    if(Depot* depot = c->getDepot())
                        player->addDepot(depot, pid);
                    else
                        std::clog << "[Error - IOLoginData::loadPlayer] Cannot load depot " << pid << " for player " << name << std::endl;
                }
                else
                    std::clog << "[Error - IOLoginData::loadPlayer] Cannot load depot " << pid << " for player " << name << std::endl;
            }
            else
            {
                it = itemMap.find(pid);
                if(it != itemMap.end())
                {
                    if(Container* container = it->second.first->getContainer())
                        container->__internalAddThing(item);
                }
            }
        }

        result->free();
        itemMap.clear();
    }

    //load storage map
    query.str("");
    query << "SELECT `key`, `value` FROM `player_storage` WHERE `player_id` = " << player->getGUID();
    if((result = db->storeQuery(query.str())))
    {
        do
            player->setStorage((uint32_t)result->getDataInt("key"), result->getDataString("value"));
        while(result->next());
        result->free();
    }

    //load vip
    query.str("");
    if(!g_config.getBool(ConfigManager::VIPLIST_PER_PLAYER))
        query << "SELECT `player_id` AS `vip` FROM `account_viplist` WHERE `account_id` = " << account.number << " AND `world_id` = " << g_config.getNumber(ConfigManager::WORLD_ID);
    else
        query << "SELECT `vip_id` AS `vip` FROM `player_viplist` WHERE `player_id` = " << player->getGUID();

    if((result = db->storeQuery(query.str())))
    {
        std::string dummy;
        do
        {
            uint32_t vid = result->getDataInt("vip");
            if(storeNameByGuid(vid))
                player->addVIP(vid, dummy, false, true);
        }
        while(result->next());
        result->free();
    }

    player->updateInventoryWeight();
    player->updateItemsLight(true);
    player->updateBaseSpeed();
    return true;
}


Still in iologindata.cpp add this to the savePlayer function (Fir3element/3777):
C++:
query << "`special_rate_loot` = " << player->getSpecialLootRate()  << ", ";

Now we are saving and getting from the database if the player has the special loot rate, lets create a lua function so that you can set that special attribute to players:

In luascript.cpp add this function below the LuaInterface::luaDoPlayerSetCastState func (Fir3element/3777):
C++:
int32_t LuaInterface::luaDoPlayerSetSpecialRateLoot(lua_State* L)
{
    //doPlayerSetSpecialRateLoot(cid, newRate)[/SIZE]
    uint8_t newRate = (uint8_t)popNumber(L);
[SIZE=4]    ScriptEnviroment* env = getEnv();
    if(Player* player = env->getPlayerByUID(popNumber(L)))
    {
        player->setSpecialLootRate(newRate);
        lua_pushboolean(L, true);
    }
    else
    {
        errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND));
        lua_pushboolean(L, false);
    }

    return 1;
}

in luascript.cpp add this (here Fir3element/3777):
C++:
//doPlayerSetSpecialRateLoot(cid, newRate)

    lua_register(m_luaState, "doPlayerSetSpecialRateLoot", LuaInterface::luaDoPlayerSetSpecialRateLoot);


in luascript.h add this line (Fir3element/3777):

C++:
static int32_t luaDoPlayerSetSpecialRateLoot(lua_State* L);

The edits are done. Finally.

Now just use the function doPlayerSetSpecialRateLoot(cid, newRate) on a lua function (a talkaction or an action when the player uses an item for example) and this will set "newRate" as the players extra loot rate.


If the player has this attribute set to higher than 0, we will add this number to the default loot rate.

If the default loot rate is 3 and you say, for example, doPlayerSetSpecialRateLoot(cid, 2), that players loot rate will be 5.
 
Last edited:
Solution
From what I've read on your posts on this topic, you just want to set another global in config.lua, and if the player has some sort of storage (I would use an attribute on the player), it will use that new rate instead of the normal rate.

Since I have not worked with older tfs versions I don't have the tools set up to compile and test it, so I'd assume you will get compile errors, if you do, let me know.

As you can see, I'm not very good at styiling my posts, so take care when following the steps to minimize the possibility of silly errors.


Start:

Add this on your config.lua (Fir3element/3777):
Lua:
specialRateLoot = 5
(this has to be higher than rateLoot for this whole change to make sense, ofc)


Add this on configmanager.cpp (Fir3element/3777):
C++:
m_confDouble[SPECIAL_RATE_LOOT] = getGlobalDouble("specialRateLoot", 2);


Add this on configmanager.h (Fir3element/3777):
(inside enum double_config_t):
C++:
SPECIAL_RATE_LOOT,


Add this on player.h (Fir3element/3777):
C++:
bool hasSpecialLootRate;


and this (Fir3element/3777):
C++:
bool getHasSpecialLootRate() const {
    return hasSpecialLootRate;
}

void setHasSpecialLootRate(bool newVal) {
    hasSpecialLootRate = newVal;
}


In monsters.h line 91 (Fir3element/3777) change the createChildLoot signature to this:
C++:
bool createChildLoot(Container* parent, const LootBlock& lootBlock, Player* owner);


In monsters.h change this line (Fir3element/3777) to this:
C++:
Item* createLoot(const LootBlock& lootBlock, Player* owner);


In monsters.h change this line (Fir3element/3777) to this:
C++:
static uint16_t getLootRandom(Player* owner);


In monsters.cpp change the createLoot function (Fir3element/3777) to this:
C++:
Item* MonsterType::createLoot(const LootBlock& lootBlock, Player* owner)
{
    uint16_t item = lootBlock.ids[0], random = Monsters::getLootRandom(owner);
    if(lootBlock.ids.size() > 1)
        item = lootBlock.ids[random_range((size_t)0, lootBlock.ids.size() - 1)];

    Item* tmpItem = NULL;
    if(Item::items[item].stackable)
    {
        if(random < lootBlock.chance)
            tmpItem = Item::CreateItem(item, (random % lootBlock.count + 1));
    }
    else if(random < lootBlock.chance)
        tmpItem = Item::CreateItem(item, 0);

    if(!tmpItem)
        return NULL;

    if(lootBlock.subType != -1)
        tmpItem->setSubType(lootBlock.subType);

    if(lootBlock.actionId != -1)
        tmpItem->setActionId(lootBlock.actionId, false);

    if(lootBlock.uniqueId != -1)
        tmpItem->setUniqueId(lootBlock.uniqueId);

    if(!lootBlock.text.empty())
        tmpItem->setText(lootBlock.text);

    return tmpItem;
}


In monsters.cpp line 89 (Fir3element/3777) change the function getLootRandom to this:
C++:
uint16_t Monsters::getLootRandom(Player* owner)
{
    if (owner && owner->getHasSpecialLootRate()) {
        return (uint16_t)std::ceil((double)random_range(0, MAX_LOOTCHANCE) / g_config.getDouble(ConfigManager::SPECIAL_RATE_LOOT));
    }
    return (uint16_t)std::ceil((double)random_range(0, MAX_LOOTCHANCE) / g_config.getDouble(ConfigManager::RATE_LOOT));
}


Inside monsters.cpp (Fir3element/3777), replace the whole dropLoot function to this:
C++:
void MonsterType::dropLoot(Container* corpse)
{
    uint32_t ownerId = corpse->getCorpseOwner();
    Player* owner = NULL;
    if (ownerId) {
        owner = g_game.getPlayerByGuid(ownerId);
    }

    Item* tmpItem = NULL;
    for(LootItems::const_iterator it = lootItems.begin(); it != lootItems.end() && !corpse->full(); ++it)
    {
        if((tmpItem = createLoot(*it, owner)))
        {
            if(Container* container = tmpItem->getContainer())
            {
                if(createChildLoot(container, (*it), owner))
                    corpse->__internalAddThing(tmpItem);
                else
                    delete container;
            }
            else
                corpse->__internalAddThing(tmpItem);
        }
    }

    corpse->__startDecaying();

    if(!owner)
        return;

    LootMessage_t message = lootMessage;
    if(message == LOOTMSG_IGNORE)
        message = (LootMessage_t)g_config.getNumber(ConfigManager::LOOT_MESSAGE);

    if(message < LOOTMSG_PLAYER)
        return;

    std::stringstream ss;
    ss << "Loot of " << nameDescription << ": " << corpse->getContentDescription() << ".";
    if(owner->getParty() && message > LOOTMSG_PLAYER)
        owner->getParty()->broadcastMessage((MessageClasses)g_config.getNumber(ConfigManager::LOOT_MESSAGE_TYPE), ss.str());
    else if(message == LOOTMSG_PLAYER || message == LOOTMSG_BOTH)
        owner->sendTextMessage((MessageClasses)g_config.getNumber(ConfigManager::LOOT_MESSAGE_TYPE), ss.str());
}


In monsters.cpp change the function createChildLoot (Fir3element/3777) to this:
C++:
bool MonsterType::createChildLoot(Container* parent, const LootBlock& lootBlock, Player* owner)
{
    LootItems::const_iterator it = lootBlock.childLoot.begin();
    if(it == lootBlock.childLoot.end())
        return true;

    Item* tmpItem = NULL;
    for(; it != lootBlock.childLoot.end() && !parent->full(); ++it)
    {
        if((tmpItem = createLoot(*it, owner)))
        {
            if(Container* container = tmpItem->getContainer())
            {
                if(createChildLoot(container, (*it), owner))
                    parent->__internalAddThing(container);
                else
                    delete container;
            }
            else
                parent->__internalAddThing(tmpItem);
        }
    }

    return !parent->empty();
}

Thats it for the loot system changes, now we need to set the special rate to the players and save it to the database, so lets do it:


Start adding a column on the player table, execute this query on your database:
Code:
ALTER TABLE players ADD COLUMN `special_rate_loot` TINYINT NOT NULL DEFAULT 0,


In iologindata.cpp (Fir3element/3777) change the loadPlayer to this:
C++:
bool IOLoginData::loadPlayer(Player* player, const std::string& name, bool preLoad /*= false*/)
{
    Database* db = Database::getInstance();
    DBQuery query;
    query << "SELECT `id`, `account_id`, `group_id`, `world_id`, `sex`, `vocation`, `experience`, `level`, "
    << "`maglevel`, `health`, `healthmax`, `blessings`, `mana`, `manamax`, `manaspent`, `soul`, `lookbody`, "
    << "`lookfeet`, `lookhead`, `looklegs`, `looktype`, `lookaddons`, `posx`, `posy`, `posz`, `cap`, "
    << "`lastlogin`, `lastlogout`, `lastip`, `conditions`, `skull`, `skulltime`, `guildnick`, `rank_id`, "
    << "`town_id`, `balance`, `stamina`, `direction`, `loss_experience`, `loss_mana`, `loss_skills`, "
    << "`loss_containers`, `loss_items`, `marriage`, `promotion`, `description`, `special_rate_loot` FROM `players` WHERE "
    << "`name` " << db->getStringComparer() << db->escapeString(name) << " AND `world_id` = "
    << g_config.getNumber(ConfigManager::WORLD_ID) << " AND `deleted` = 0 LIMIT 1";

    DBResult* result;
    if(!(result = db->storeQuery(query.str())))
        return false;

    uint32_t accountId = result->getDataInt("account_id");
    if(accountId < 1)
    {
        result->free();
        return false;
    }

    Account account = loadAccount(accountId, true);
    player->account = account.name;
    player->accountId = accountId;

    Group* group = Groups::getInstance()->getGroup(result->getDataInt("group_id"));
    player->setGroup(group);

    player->setGUID(result->getDataInt("id"));
    player->premiumDays = account.premiumDays;

    nameCacheMap[player->getGUID()] = name;
    guidCacheMap[name] = player->getGUID();
    if(preLoad)
    {
        //only loading basic info
        result->free();
        return true;
    }

    player->setHasSpecialLootRate(result->getDataInt("special_rate_loot"));
    player->nameDescription += result->getDataString("description");
    player->setSex(result->getDataInt("sex"));
    if(g_config.getBool(ConfigManager::STORE_DIRECTION))
        player->setDirection((Direction)result->getDataInt("direction"));

    player->level = std::max((uint32_t)1, (uint32_t)result->getDataInt("level"));
    uint64_t currExpCount = Player::getExpForLevel(player->level), nextExpCount = Player::getExpForLevel(
        player->level + 1), experience = (uint64_t)result->getDataLong("experience");
    if(experience < currExpCount || experience > nextExpCount)
        experience = currExpCount;

    player->experience = experience;
    player->levelPercent = 0;
    if(currExpCount < nextExpCount)
        player->levelPercent = Player::getPercentLevel(player->experience - currExpCount, nextExpCount - currExpCount);

    player->soul = result->getDataInt("soul");
    player->capacity = result->getDataInt("cap");
    player->setStamina(result->getDataLong("stamina"));
    player->marriage = result->getDataInt("marriage");

    player->balance = result->getDataLong("balance");
    if(g_config.getBool(ConfigManager::BLESSINGS) && (player->isPremium()
        || !g_config.getBool(ConfigManager::BLESSING_ONLY_PREMIUM)))
        player->blessings = result->getDataInt("blessings");

    uint64_t conditionsSize = 0;
    const char* conditions = result->getDataStream("conditions", conditionsSize);

    PropStream propStream;
    propStream.init(conditions, conditionsSize);

    Condition* condition;
    while((condition = Condition::createCondition(propStream)))
    {
        if(condition->unserialize(propStream))
            player->storedConditionList.push_back(condition);
        else
            delete condition;
    }

    player->vocationId = result->getDataInt("vocation");
    player->setPromotionLevel(result->getDataInt("promotion"));

    player->health = result->getDataInt("health");
    player->healthMax = result->getDataInt("healthmax");
    player->mana = result->getDataInt("mana");
    player->manaMax = result->getDataInt("manamax");

    player->magLevel = result->getDataInt("maglevel");
    uint64_t nextManaCount = player->vocation->getReqMana(player->magLevel + 1), manaSpent = result->getDataLong("manaspent");
    if(manaSpent > nextManaCount)
        manaSpent = 0;

    player->manaSpent = manaSpent;
    player->magLevelPercent = Player::getPercentLevel(player->manaSpent, nextManaCount);
    if(!group || !group->getOutfit())
    {
        player->defaultOutfit.lookType = result->getDataInt("looktype");
        uint32_t outfitId = Outfits::getInstance()->getOutfitId(player->defaultOutfit.lookType);

        bool wearable = true;
        if(outfitId > 0)
        {
            Outfit outfit;
            wearable = Outfits::getInstance()->getOutfit(outfitId, player->getSex(true), outfit);
            if(wearable && player->defaultOutfit.lookType != outfit.lookType)
                player->defaultOutfit.lookType = outfit.lookType;
        }

        if(!wearable) //just pick the first default outfit we can find
        {
            const OutfitMap& defaultOutfits = Outfits::getInstance()->getOutfits(player->getSex(true));
            if(!defaultOutfits.empty())
            {
                Outfit newOutfit = (*defaultOutfits.begin()).second;
                player->defaultOutfit.lookType = newOutfit.lookType;
            }
        }
    }
    else
        player->defaultOutfit.lookType = group->getOutfit();

    player->defaultOutfit.lookHead = result->getDataInt("lookhead");
    player->defaultOutfit.lookBody = result->getDataInt("lookbody");
    player->defaultOutfit.lookLegs = result->getDataInt("looklegs");
    player->defaultOutfit.lookFeet = result->getDataInt("lookfeet");
    player->defaultOutfit.lookAddons = result->getDataInt("lookaddons");

    player->currentOutfit = player->defaultOutfit;
    Skulls_t skull = SKULL_RED;
    if(g_config.getBool(ConfigManager::USE_BLACK_SKULL))
        skull = (Skulls_t)result->getDataInt("skull");

    player->setSkullEnd((time_t)result->getDataInt("skulltime"), true, skull);
    player->town = result->getDataInt("town_id");
    if(Town* town = Towns::getInstance()->getTown(player->town))
        player->setMasterPosition(town->getPosition());

    player->setLossPercent(LOSS_EXPERIENCE, result->getDataInt("loss_experience"));
    player->setLossPercent(LOSS_MANA, result->getDataInt("loss_mana"));
    player->setLossPercent(LOSS_SKILLS, result->getDataInt("loss_skills"));
    player->setLossPercent(LOSS_CONTAINERS, result->getDataInt("loss_containers"));
    player->setLossPercent(LOSS_ITEMS, result->getDataInt("loss_items"));

    player->loginPosition = Position(result->getDataInt("posx"), result->getDataInt("posy"), result->getDataInt("posz"));
    player->lastLogin = result->getDataLong("lastlogin");
    player->lastLogout = result->getDataLong("lastlogout");

    player->lastIP = result->getDataInt("lastip");
    if(!player->loginPosition.x || !player->loginPosition.y)
        player->loginPosition = player->getMasterPosition();

    const uint32_t rankId = result->getDataInt("rank_id");
    const std::string nick = result->getDataString("guildnick");

    result->free();
    if(rankId > 0)
    {
        query.str("");
        query << "SELECT `guild_ranks`.`name` AS `rank`, `guild_ranks`.`guild_id` AS `guildid`, `guild_ranks`.`level` AS `level`, `guilds`.`name` AS `guildname` FROM `guild_ranks`, `guilds` WHERE `guild_ranks`.`id` = " << rankId << " AND `guild_ranks`.`guild_id` = `guilds`.`id` LIMIT 1";
        if((result = db->storeQuery(query.str())))
        {
            player->guildId = result->getDataInt("guildid");
            player->guildName = result->getDataString("guildname");
            player->guildLevel = (GuildLevel_t)result->getDataInt("level");

            player->rankId = rankId;
            player->rankName = result->getDataString("rank");
            player->guildNick = nick;
            result->free();

            query.str("");
            query << "SELECT `id`, `guild_id`, `enemy_id` FROM `guild_wars` WHERE (`guild_id` = "
                << player->guildId << " OR `enemy_id` = " << player->guildId << ") AND `status` IN (1,4)";
            if((result = db->storeQuery(query.str())))
            {
                War_t war;
                do
                {
                    uint32_t guild = result->getDataInt("guild_id");
                    if(player->guildId == guild)
                    {
                        war.type = WAR_ENEMY;
                        war.war = result->getDataInt("id");
                        player->addEnemy(result->getDataInt("enemy_id"), war);
                    }
                    else
                    {
                        war.type = WAR_GUILD;
                        war.war = result->getDataInt("id");
                        player->addEnemy(guild, war);
                    }
                }
                while(result->next());
                result->free();
            }
        }
    }
    else if(g_config.getBool(ConfigManager::INGAME_GUILD_MANAGEMENT))
    {
        query.str("");
        query << "SELECT `guild_id` FROM `guild_invites` WHERE `player_id` = " << player->getGUID();
        if((result = db->storeQuery(query.str())))
        {
            do
                player->invitedToGuildsList.push_back((uint32_t)result->getDataInt("guild_id"));
            while(result->next());
            result->free();
        }
    }

    query.str("");
    query << "SELECT `password` FROM `accounts` WHERE `id` = " << accountId << " LIMIT 1";
    if(!(result = db->storeQuery(query.str())))
        return false;

    player->password = result->getDataString("password");
    result->free();

    // we need to find out our skills
    // so we query the skill table
    query.str("");
    query << "SELECT `skillid`, `value`, `count` FROM `player_skills` WHERE `player_id` = " << player->getGUID();
    if((result = db->storeQuery(query.str())))
    {
        //now iterate over the skills
        do
        {
            int16_t skillId = result->getDataInt("skillid");
            if(skillId < SKILL_FIRST || skillId > SKILL_LAST)
                continue;

            uint32_t skillLevel = result->getDataInt("value");
            uint64_t nextSkillCount = player->vocation->getReqSkillTries(
                skillId, skillLevel + 1), skillCount = result->getDataLong("count");
            if(skillCount > nextSkillCount)
                skillCount = 0;

            player->skills[skillId][SKILL_LEVEL] = skillLevel;
            player->skills[skillId][SKILL_TRIES] = skillCount;
            player->skills[skillId][SKILL_PERCENT] = Player::getPercentLevel(skillCount, nextSkillCount);
        }
        while(result->next());
        result->free();
    }

    query.str("");
    query << "SELECT `player_id`, `name` FROM `player_spells` WHERE `player_id` = " << player->getGUID();
    if((result = db->storeQuery(query.str())))
    {
        do
            player->learnedInstantSpellList.push_back(result->getDataString("name"));
        while(result->next());
        result->free();
    }

    ItemMap itemMap;
    ItemMap::iterator it;

    //load inventory items
    query.str("");
    query << "SELECT `pid`, `sid`, `itemtype`, `count`, `attributes` FROM `player_items` WHERE `player_id` = " << player->getGUID() << " ORDER BY `sid` DESC";
    if((result = db->storeQuery(query.str())))
    {
        loadItems(itemMap, result);
        for(ItemMap::reverse_iterator rit = itemMap.rbegin(); rit != itemMap.rend(); ++rit)
        {
            Item* item = rit->second.first;
            int32_t pid = rit->second.second;
            if(pid > 0 && pid < 11)
                player->__internalAddThing(pid, item);
            else
            {
                it = itemMap.find(pid);
                if(it != itemMap.end())
                {
                    if(Container* container = it->second.first->getContainer())
                        container->__internalAddThing(item);
                }
            }
        }

        result->free();
        itemMap.clear();
    }

    //load depot items
    query.str("");
    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);
        for(ItemMap::reverse_iterator rit = itemMap.rbegin(); rit != itemMap.rend(); ++rit)
        {
            Item* item = rit->second.first;
            int32_t pid = rit->second.second;
            if(pid >= 0 && pid < 100)
            {
                if(Container* c = item->getContainer())
                {
                    if(Depot* depot = c->getDepot())
                        player->addDepot(depot, pid);
                    else
                        std::clog << "[Error - IOLoginData::loadPlayer] Cannot load depot " << pid << " for player " << name << std::endl;
                }
                else
                    std::clog << "[Error - IOLoginData::loadPlayer] Cannot load depot " << pid << " for player " << name << std::endl;
            }
            else
            {
                it = itemMap.find(pid);
                if(it != itemMap.end())
                {
                    if(Container* container = it->second.first->getContainer())
                        container->__internalAddThing(item);
                }
            }
        }

        result->free();
        itemMap.clear();
    }

    //load storage map
    query.str("");
    query << "SELECT `key`, `value` FROM `player_storage` WHERE `player_id` = " << player->getGUID();
    if((result = db->storeQuery(query.str())))
    {
        do
            player->setStorage((uint32_t)result->getDataInt("key"), result->getDataString("value"));
        while(result->next());
        result->free();
    }

    //load vip
    query.str("");
    if(!g_config.getBool(ConfigManager::VIPLIST_PER_PLAYER))
        query << "SELECT `player_id` AS `vip` FROM `account_viplist` WHERE `account_id` = " << account.number << " AND `world_id` = " << g_config.getNumber(ConfigManager::WORLD_ID);
    else
        query << "SELECT `vip_id` AS `vip` FROM `player_viplist` WHERE `player_id` = " << player->getGUID();

    if((result = db->storeQuery(query.str())))
    {
        std::string dummy;
        do
        {
            uint32_t vid = result->getDataInt("vip");
            if(storeNameByGuid(vid))
                player->addVIP(vid, dummy, false, true);
        }
        while(result->next());
        result->free();
    }

    player->updateInventoryWeight();
    player->updateItemsLight(true);
    player->updateBaseSpeed();
    return true;
}


Still in iologindata.cpp add this to the savePlayer function (Fir3element/3777):
C++:
query << "`special_rate_loot` = " << player->getStamina() ? 1 : 0 << ", ";

Now we are saving and getting from the database if the player has the special loot rate, lets create a lua function so that you can set that special attribute to players:

In luascript.cpp add this function below the LuaInterface::luaDoPlayerSetCastState func (Fir3element/3777):
C++:
int32_t LuaInterface::luaDoPlayerToggleSpecialRateLoot(lua_State* L)
{
    //doPlayerToggleSpecialRateLoot(cid)
    ScriptEnviroment* env = getEnv();
    if(Player* player = env->getPlayerByUID(popNumber(L)))
    {
        player->setHasSpecialLootRate(!player->getHasSpecialLootRate());
        lua_pushboolean(L, true);
    }
    else
    {
        errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND));
        lua_pushboolean(L, false);
    }

    return 1;
}

in luascript.cpp add this (here Fir3element/3777):
C++:
//doPlayerToggleSpecialRateLoot(cid)

    lua_register(m_luaState, "doPlayerToggleSpecialRateLoot", LuaInterface::luaDoPlayerToggleSpecialRateLoot);


in luascript.h add this line (Fir3element/3777):

C++:
static int32_t luaDoPlayerToggleSpecialRateLoot(lua_State* L);

The edits are done. Finally.

Now just use the function doPlayerToggleSpecialRateLoot(cid) on a lua function (a talkaction or an action when the player uses an item for example) and this will turn on/off the special rate loot on the player.

If the player has this attribute set to true, the loot function will use the special loot rate set in the config.lua instead of the standard loot rate.

Holy! Someone who did it to us <3
I need to know if it is possible to use the storage value instead of a static value in config.lua...
I mean, storage value (GetStorageValue(cid,storagenumber,value))
I mean when create the new loot check LootRates + storagevalue
For example, if player have storage value = 2, so his loot rates = 3 (suposing that lootratedefaul = 1)
Because i wanna use this in so many ways, potions, itens, events and with a variable value should be better then a constant...
 
Holy! Someone who did it to us <3
I need to know if it is possible to use the storage value instead of a static value in config.lua...
I mean, storage value (GetStorageValue(cid,storagenumber,value))
I mean when create the new loot check LootRates + storagevalue
For example, if player have storage value = 2, so his loot rates = 3 (suposing that lootratedefaul = 1)
Because i wanna use this in so many ways, potions, itens, events and with a variable value should be better then a constant...


Yeye, I can do that. I'm busy right now but I can do it in 3~4 hours.

Edit--
hey @supergt i've edited my original post, check it out.
 
Last edited:
Back
Top