CreatureEvent [TFS 1.1] Random Item Stats

Sir Knighter

ArchlightOnline.com
Premium User
Joined
Jun 29, 2009
Messages
4,146
Best answers
0
Reaction score
1,212
Location
Canada
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
        }
    },
}
 

luigilc

Lua Learner
Joined
Mar 24, 2010
Messages
863
Best answers
0
Reaction score
36
Location
A music box
Those are the chances for the bonuses stats isn't it? I want to edit the chance that a tiered item will even drop.
 

luigilc

Lua Learner
Joined
Mar 24, 2010
Messages
863
Best answers
0
Reaction score
36
Location
A music box
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*
 

Leo32

Getting back into it...
Joined
Sep 21, 2007
Messages
730
Best answers
1
Reaction score
124
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:


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.







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


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:

dkangel83

Member
Joined
Mar 31, 2010
Messages
102
Best answers
0
Reaction score
8
Location
Brazil
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
 

Leo32

Getting back into it...
Joined
Sep 21, 2007
Messages
730
Best answers
1
Reaction score
124
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:

Leo32

Getting back into it...
Joined
Sep 21, 2007
Messages
730
Best answers
1
Reaction score
124
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:

Leo32

Getting back into it...
Joined
Sep 21, 2007
Messages
730
Best answers
1
Reaction score
124
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.
 

Kungenn

Advanced OT User
Joined
Jun 10, 2007
Messages
1,511
Best answers
0
Reaction score
219
Location
USA California
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
 

Leo32

Getting back into it...
Joined
Sep 21, 2007
Messages
730
Best answers
1
Reaction score
124
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.
 

Kungenn

Advanced OT User
Joined
Jun 10, 2007
Messages
1,511
Best answers
0
Reaction score
219
Location
USA California
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 :/
 

Leo32

Getting back into it...
Joined
Sep 21, 2007
Messages
730
Best answers
1
Reaction score
124
Then you've done the SOURCE EDITS section correctly.
No loot tells me you havnt done the DATA EDITS changes correctly.
 

Kungenn

Advanced OT User
Joined
Jun 10, 2007
Messages
1,511
Best answers
0
Reaction score
219
Location
USA California
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 :)
 

Kungenn

Advanced OT User
Joined
Jun 10, 2007
Messages
1,511
Best answers
0
Reaction score
219
Location
USA California
I just readded everything you wrote and it's still not working :(
There are no errors in the console either
 

Kungenn

Advanced OT User
Joined
Jun 10, 2007
Messages
1,511
Best answers
0
Reaction score
219
Location
USA California
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?
 

Leo32

Getting back into it...
Joined
Sep 21, 2007
Messages
730
Best answers
1
Reaction score
124
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.
 
Top