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

CreatureEvent [TFS 1.1] Random Item Stats

Where do I edited the chance of a rare item dropping?

Code:
local tiers = {
    [1] = {
        prefix = 'rare',
        showattr = true, -- attr prefix will be shown instead
        extra = {0, 0},
        chance = {
            [1] = 10000, -- chance for basic stat
            [2] = 5000 -- chance for second stat
        }
    },
 
    [2] = {
        prefix = 'epic',
        extra = {7, 20}, -- additional percent bonus
        chance = {
            [1] = 3333,
            [2] = 25000
        }
    },
 
    [3] = {
        prefix = 'legendary',
        extra = {20, 35},
        chance = {
            [1] = 1000,
            [2] = 100000 -- 2 bonuses always
        }
    },
}
 
Those are the chances for the bonuses stats isn't it? I want to edit the chance that a tiered item will even drop.
 
What do I have to do to make it so the message it displays over the dead body is the equipment tier instead of a fixed message? For example if a monster drops a legendary then over his body should read *Legendary* now if a monster drops a rare then it should read *rare*
 
I wanted it to be static values instead of % values:
Low items had no differences, and high level items were too overpowered with % based changes.

I also wanted the rarity names to show up in lootlists:

nwOc2jw.png

I might see what else I can do here...
Bold rare items maybe? different colour? hmm...


Used the source differences here: https://github.com/otbr/forsaken
To remove loot being handled in TFS source.

creature.cpp
monsters.cpp/.h
monster.cpp/.h


use notepad++ and the compare plugin, only a few lines in each.

Re-added loot being handled in LUA using lib/core/monstertype.lua as a base.
Spliced in the randomstats.lua code to execute on loot generate but before the lootlist broadcast.

Done.

iSCVyA6.png


BnmyChq.png


ExTHOjU.png


Also tweaked the randomstats code due to grammar:

Code:
            -- Replace article if item rolls as epic
             if tiers[tier].prefix == "epic" then
               it_u:setAttribute(ITEM_ATTRIBUTE_ARTICLE, "an")
             end

3he4jch.png


Because of the effort and steps needed: merge code into one + source edits to remove loot, I haven't posted the code itself.

If anyone wants this code, tell me and I'll try and sit down and write it all out.
 
Last edited:

Would be great to have a chance to get the codes to test it out.. Having chance to see the rarity in lootlist.. an amazing thing :D
i dont want to disturb but i think not only me bu others could make some good use of your code..

Thanks in advance
 
Please be aware I'm no C++ or LUA god.
Most of this is splicing existing code.

It could very much be broken down into separate and more modulated lua files.

DATA EDITS

lib/core.lua

add:
Code:
dofile('data/lib/core/lualoot.lua')

lib/core/lualoot.lua
http://pastebin.com/WEjezpxC

creaturescripts.xml
add:
Code:
<!-- RPG Items  -->
   <event type="death" name="DropMonsterLoot" script="drop_monster_loot.lua"/>

drop_monster_loot.lua
Code:
function onDeath(monster, corpse, killer, mostdamagekiller, lasthitunjustified, mostdamageunjustified)
   if not monster:isMonster() or monster:getMaster() then
     return true
   end

   if not corpse:isContainer() then
     return true
   end

   local owner = mostdamagekiller
   if killer and killer:isPlayer() then
     owner = killer
   end

   local modifier = 1
   if owner and owner:isPlayer() then
     corpse:setAttribute(ITEM_ATTRIBUTE_CORPSEOWNER, owner:getId())
   end

   monster:getType():createLoot(corpse, 1)

   return true
end

events.xml
add:
Code:
    <!-- Monster method -->
   <event class="Monster" method="onSpawn" enabled="1" />

events/scripts/monster.lua
Code:
function Monster:onSpawn(pos, forced)
   self:registerEvent("DropMonsterLoot")

   return true
end



SOURCE EDITS:


events.cpp
after:
Code:
  playerOnTradeRequest = -1;
   playerOnTradeAccept = -1;
   playerOnGainExperience = -1;
   playerOnLoseExperience = -1;
   playerOnGainSkillTries = -1;

add:
Code:
   // Monster
   monsterOnSpawn = -1;

after:
Code:
      } else if (methodName == "onGainSkillTries") {
         playerOnGainSkillTries = event;
       } else {
         std::cout << "[Warning - Events::load] Unknown player method: " << methodName << std::endl;
       }

add:
Code:
    } else if (className == "Monster") {
       if (methodName == "onSpawn") {
         monsterOnSpawn = event;
       } else {
         std::cout << "[Warning - Events::load] Unknown monster method: " << methodName << std::endl;
       }

after:
Code:
  if (scriptInterface.protectedCall(L, 3, 1) != 0) {
     LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L));
   } else {
     tries = LuaScriptInterface::getNumber<uint64_t>(L, -1);
     lua_pop(L, 1);
   }

   scriptInterface.resetScriptEnv();
}

add:
Code:
// Monster
bool Events::eventMonsterOnSpawn(Monster* monster, const Position& position, bool forced) {
   // Monster:onSpawn(pos, forced)
   if (monsterOnSpawn == -1) {
     return true;
   }

   if (!scriptInterface.reserveScriptEnv()) {
     std::cout << "[Error - Events::eventMonsterOnSpawn] Call stack overflow" << std::endl;
     return false;
   }

   ScriptEnvironment* env = scriptInterface.getScriptEnv();
   env->setScriptId(monsterOnSpawn, &scriptInterface);

   lua_State* L = scriptInterface.getLuaState();
   scriptInterface.pushFunction(monsterOnSpawn);

   LuaScriptInterface::pushUserdata<Monster>(L, monster);
   LuaScriptInterface::setMetatable(L, -1, "Monster");

   LuaScriptInterface::pushPosition(L, position);

   LuaScriptInterface::pushBoolean(L, forced);

   return scriptInterface.callFunction(3);
}

events.h

after:
Code:
    void eventPlayerOnGainExperience(Player* player, Creature* source, uint64_t& exp, uint64_t rawExp);
     void eventPlayerOnLoseExperience(Player* player, uint64_t& exp);
     void eventPlayerOnGainSkillTries(Player* player, skills_t skill, uint64_t& tries);

add:
Code:
    // Monster
     bool eventMonsterOnSpawn(Monster* monster, const Position& pos, bool forced);

after:
Code:
    int32_t playerOnTradeAccept;
     int32_t playerOnGainExperience;
     int32_t playerOnLoseExperience;
     int32_t playerOnGainSkillTries;

add:
Code:
    // Monster
     int32_t monsterOnSpawn;

luascript.cpp

after:
Code:
  registerMethod("Container", "hasItem", LuaScriptInterface::luaContainerHasItem);
   registerMethod("Container", "addItem", LuaScriptInterface::luaContainerAddItem);
   registerMethod("Container", "addItemEx", LuaScriptInterface::luaContainerAddItemEx);

add:
Code:
registerMethod("Container", "getContentDescription", LuaScriptInterface::luaContainerGetContentDescription);

after:
Code:
registerMethod("Party", "isSharedExperienceEnabled", LuaScriptInterface::luaPartyIsSharedExperienceEnabled);
   registerMethod("Party", "shareExperience", LuaScriptInterface::luaPartyShareExperience);
   registerMethod("Party", "setSharedExperience", LuaScriptInterface::luaPartySetSharedExperience);

add:
Code:
registerMethod("Party", "broadcastLoot", LuaScriptInterface::luaPartyBroadcastLoot);

after:
Code:
  int32_t index = getNumber<int32_t>(L, 3, INDEX_WHEREEVER);
   uint32_t flags = getNumber<uint32_t>(L, 4, 0);
   ReturnValue ret = g_game.internalAddItem(container, item, index, flags);
   if (ret == RETURNVALUE_NOERROR) {
     ScriptEnvironment::removeTempItem(item);
   }
   lua_pushnumber(L, ret);
   return 1;
}

add:
Code:
int LuaScriptInterface::luaContainerGetContentDescription(lua_State* L)
{
   // container:getContentDescription()
   Container* container = getUserdata<Container>(L, 1);
   if (container) {
     pushString(L, container->getContentDescription());
   } else {
     lua_pushnil(L);
   }
   return 1;
}

after:
Code:
int LuaScriptInterface::luaPartySetSharedExperience(lua_State* L)
{
   // party:setSharedExperience(active)
   bool active = getBoolean(L, 2);
   Party* party = getUserdata<Party>(L, 1);
   if (party) {
     pushBoolean(L, party->setSharedExperience(party->getLeader(), active));
   } else {
     lua_pushnil(L);
   }
   return 1;
}

add:
Code:
int LuaScriptInterface::luaPartyBroadcastLoot(lua_State* L)
{
   // party:broadcastLoot(lootMessage)
   const std::string& lootMessage = getString(L, 2);
   Party* party = getUserdata<Party>(L, 1);
   if (party) {
     party->broadcastPartyLoot(lootMessage);
     pushBoolean(L, true);
   } else {
     lua_pushnil(L);
   }
   return 1;
}

luascript.h

after:
Code:
    static int luaContainerGetItem(lua_State* L);
     static int luaContainerHasItem(lua_State* L);
     static int luaContainerAddItem(lua_State* L);
     static int luaContainerAddItemEx(lua_State* L);

add:
Code:
static int luaContainerGetContentDescription(lua_State* L);

after:
Code:
    static int luaPartyIsSharedExperienceActive(lua_State* L);
     static int luaPartyIsSharedExperienceEnabled(lua_State* L);
     static int luaPartyShareExperience(lua_State* L);
     static int luaPartySetSharedExperience(lua_State* L);

add:
Code:
static int luaPartyBroadcastLoot(lua_State* L);

This is tested and worked in my little sandbox.
However this hasn't been tested on a live server.
 
Last edited:
Removing loot from TFS source (is now handled using lualoot.lua above):

Either do this - or find another creative way to stop monsters dropping loot.
(Theres not a config option for this is there? lol)

creature.cpp

after:
Code:
    //scripting event - onDeath
     for (CreatureEvent* deathEvent : getCreatureEvents(CREATURE_EVENT_DEATH)) {
       deathEvent->executeOnDeath(this, corpse, _lastHitCreature, mostDamageCreature, lastHitUnjustified, mostDamageUnjustified);
     }

remove:
Code:
    if (corpse) {
       dropLoot(corpse->getContainer(), _lastHitCreature);
     }

monster.cpp

before:
Code:
void Monster::setNormalCreatureLight()
{
   internalLight.level = mType->lightLevel;
   internalLight.color = mType->lightColor;
}

remove:
Code:
void Monster::dropLoot(Container* corpse, Creature*)
{
   if (corpse && lootDrop) {
     mType->createLoot(corpse);
   }
}

monster.h

after:
Code:
    uint16_t getLookCorpse() const final {
       return mType->lookcorpse;
     }

remove:
Code:
void dropLoot(Container* corpse, Creature* _lastHitCreature) final;

monsters.cpp

before:
Code:
Monsters::Monsters()
{
   loaded = false;
}

remove:
Code:
uint32_t Monsters::getLootRandom()
{
   return uniform_random(0, MAX_LOOTCHANCE) / g_config.getNumber(ConfigManager::RATE_LOOT);
}

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

   Player* owner = g_game.getPlayerByID(corpse->getCorpseOwner());
   if (!owner || owner->getStaminaMinutes() > 840) {
     for (auto it = lootItems.rbegin(), end = 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 (g_game.internalAddItem(corpse, item) != RETURNVALUE_NOERROR) {
           corpse->internalAddThing(item);
         }
       }
     }

     if (owner) {
       std::ostringstream ss;
       ss << "Loot of " << nameDescription << ": " << corpse->getContentDescription();

       if (owner->getParty()) {
         owner->getParty()->broadcastPartyLoot(ss.str());
       } else {
         owner->sendTextMessage(MESSAGE_INFO_DESCR, 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_INFO_DESCR, ss.str());
     }
   }

   corpse->startDecaying();
}

std::vector<Item*> MonsterType::createLootItem(const LootBlock& lootBlock)
{
   int32_t itemCount = 0;

   uint32_t randvalue = Monsters::getLootRandom();
   if (randvalue < lootBlock.chance) {
     if (Item::items[lootBlock.id].stackable) {
       itemCount = randvalue % lootBlock.countmax + 1;
     } else {
       itemCount = 1;
     }
   }

   std::vector<Item*> itemList;
   while (itemCount > 0) {
     uint16_t n = static_cast<uint16_t>(std::min<int32_t>(itemCount, 100));
     Item* tmpItem = Item::CreateItem(lootBlock.id, n);
     if (!tmpItem) {
       break;
     }

     itemCount -= n;

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

     if (lootBlock.actionId != -1) {
       tmpItem->setActionId(lootBlock.actionId);
     }

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

     itemList.push_back(tmpItem);
   }
   return itemList;
}

bool MonsterType::createLootContainer(Container* parent, const LootBlock& lootblock)
{
   auto it = lootblock.childLoot.begin(), end = lootblock.childLoot.end();
   if (it == end) {
     return true;
   }

   for (; it != end && parent->size() < parent->capacity(); ++it) {
     auto itemList = createLootItem(*it);
     for (Item* tmpItem : itemList) {
       if (Container* container = tmpItem->getContainer()) {
         if (!createLootContainer(container, *it)) {
           delete container;
         } else {
           parent->internalAddThing(container);
         }
       } else {
         parent->internalAddThing(tmpItem);
       }
     }
   }
   return !parent->empty();
}

monsters.h

after:

Code:
    bool canPushItems;
     bool canPushCreatures;
     bool pushable;
     bool isSummonable;
     bool isIllusionable;
     bool isConvinceable;
     bool isAttackable;
     bool isHostile;
     bool hiddenHealth;

remove:
Code:
void createLoot(Container* corpse);
     bool createLootContainer(Container* parent, const LootBlock& lootblock);
     std::vector<Item*> createLootItem(const LootBlock& lootBlock);

after:
Code:
MonsterType* getMonsterType(const std::string& name);
     uint32_t getIdByName(const std::string& name);

remove:
Code:
static uint32_t getLootRandom();
 
Last edited:
Does this apply to ever single weapon dropped?
If you read the code, it runs on every corpse - checks its contents and runs the rarity rolls off each item.
It won't roll on NPC bought or quest items, would need to be modified for that.
 
If you read the code, it runs on every corpse - checks its contents and runs the rarity rolls off each item.
It won't roll on NPC bought or quest items, would need to be modified for that.

I tried the code you posted but the following error occurs

Code:
In file included from /home/otsmanager/forgottenserver/src/baseevents.h:23:0,
                 from /home/otsmanager/forgottenserver/src/actions.h:23,
                 from /home/otsmanager/forgottenserver/src/actions.cpp:22:
/home/otsmanager/forgottenserver/src/luascript.h:347:12: error: ‘T’ does not name a type
     static T popNumber(lua_State* L) {
            ^
/home/otsmanager/forgottenserver/src/luascript.h:359:12: error: declaration of ‘class T’
   template<typename T>
            ^
/home/otsmanager/forgottenserver/src/luascript.h:357:12: error:  shadows template parm ‘class T’
   template<typename T>
            ^
/home/otsmanager/forgottenserver/src/luascript.h:360:12: error: too many template-parameter-lists
   static T getField(lua_State* L, int32_t arg, const std::string& key)
            ^
make[2]: *** [CMakeFiles/tfs.dir/src/actions.cpp.o] Error 1
make[1]: *** [CMakeFiles/tfs.dir/all] Error 2
make: *** [all] Error 2
 
I tried the code you posted but the following error occurs

Code:
In file included from /home/otsmanager/forgottenserver/src/baseevents.h:23:0,
                 from /home/otsmanager/forgottenserver/src/actions.h:23,
                 from /home/otsmanager/forgottenserver/src/actions.cpp:22:
/home/otsmanager/forgottenserver/src/luascript.h:347:12: error: ‘T’ does not name a type
     static T popNumber(lua_State* L) {
            ^
/home/otsmanager/forgottenserver/src/luascript.h:359:12: error: declaration of ‘class T’
   template<typename T>
            ^
/home/otsmanager/forgottenserver/src/luascript.h:357:12: error:  shadows template parm ‘class T’
   template<typename T>
            ^
/home/otsmanager/forgottenserver/src/luascript.h:360:12: error: too many template-parameter-lists
   static T getField(lua_State* L, int32_t arg, const std::string& key)
            ^
make[2]: *** [CMakeFiles/tfs.dir/src/actions.cpp.o] Error 1
make[1]: *** [CMakeFiles/tfs.dir/all] Error 2
make: *** [all] Error 2

I've edited my original post.
Replace your LUASCRIPT.H file with the original, before attempting these edits.

Then edit it again using the instructions on my original post.
 
I've edited my original post.
Replace your LUASCRIPT.H file with the original, before attempting these edits.

Then edit it again using the instructions on my original post.
It works to compile but all monsters are completely empty :/
 
Then you've done the SOURCE EDITS section correctly.
No loot tells me you havnt done the DATA EDITS changes correctly.
 
Or you've written them incorrectly, not saying that you have... But there is a chance since correct me if im wrong

You wrote:

lib/core.lua
add:
Code:
dofile('data/lib/core/lualoot.lua')

But isn't it suppose to be in

lib/lib.lua<----------
add:
Code:
dofile('data/lib/core/lualoot.lua')

I atleast dont have a core.lua in my lib folder

Im gonna try once again to test the data part but I am confident I entered it just like you wrote

By the way, I am very thankful that you are trying to help me so don't get me wrong :)
 
This might be a stupid question and it might be reason why it doesn\t work, but is the code you wrote only for TFS 1.1?
 
This might be a stupid question and it might be reason why it doesn\t work, but is the code you wrote only for TFS 1.1?

Haven't tested on anything lower.
So, maybe.
 
Back
Top