Creditos:
Baseado no code do cbrm:
https://otland.net/threads/reward-chest-boss-reward-tfs-1-2.233397/
Instruções::
src/const.h:
Embaixo de...
ITEM_MARKET = 14405,
...Adicione:
ITEM_REWARD_BAG = 21518, ITEM_REWARD_CHEST = 21584, REWARD_CHEST_DEPOTID = 99,
src/depotchest.h:
Embaixo de...
explicit DepotChest(uint16_t _type);
...Adicione:
uint32_t getDepotId() const { return depotId; } void setDepotId(uint32_t id) { depotId = id; }
Embaixo de...
uint32_t maxDepotItems;
...Adicione:
uint32_t depotId;
src/depotchest.cpp:
Embaixo de...
maxDepotItems = 1500;
...Adicione:
depotId = 0;
Em cima de...
return Container::queryAdd(index, thing, count, flags, actor);
...Adicione:
if (actor != nullptr && getDepotId() == REWARD_CHEST_DEPOTID) { return RETURNVALUE_NOTPOSSIBLE; }
src/depotlocker.h:
Em cima de...
//cylinder implementations
...Adicione:
void setMaxLockerItems(uint32_t maxitems) { maxSize = maxitems; }
src/player.h:
Embaixo de...
DepotLocker* getDepotLocker(uint32_t depotId);
...Adicione:
DepotChest* getRewardBag(uint32_t depotId, bool autoCreate); DepotLocker* getRewardChest(uint16_t itemId);
src/player.cpp:
Em cima de...
void Player::sendCancelMessage(ReturnValue message) const
...Adicione:
DepotChest* Player::getRewardBag(uint32_t depotId, bool autoCreate) { auto it = depotChests.find(depotId); if (it != depotChests.end()) { return it->second; } if (!autoCreate) { return nullptr; } DepotChest* bagReward = new DepotChest(ITEM_REWARD_BAG); bagReward->setDepotId(depotId); bagReward->incrementReferenceCounter(); bagReward->setMaxDepotItems(getMaxDepotItems()); depotChests[depotId] = bagReward; return bagReward; } DepotLocker* Player::getRewardChest(uint16_t itemId) { auto it = depotLockerMap.find(REWARD_CHEST_DEPOTID); if (it != depotLockerMap.end()) { it->second->setID(itemId); return it->second; } DepotLocker* rewardChest = new DepotLocker(ITEM_REWARD_CHEST); rewardChest->setID(itemId); rewardChest->setDepotId(REWARD_CHEST_DEPOTID); rewardChest->setMaxLockerItems(1); rewardChest->internalAddThing(getRewardBag(REWARD_CHEST_DEPOTID, true)); depotLockerMap[REWARD_CHEST_DEPOTID] = rewardChest; return rewardChest; }
Em player.cpp, container.cpp, inbox.cpp:
Mude:
if (!item->isPickupable()) {
Por:
if (item->getID() != 21518 && !item->isPickupable()) {
src/actions.cpp:
Mude:
//depot container if (DepotLocker* depot = container->getDepotLocker()) { DepotLocker* myDepotLocker = player->getDepotLocker(depot->getDepotId()); myDepotLocker->setParent(depot->getParent()->getTile()); openContainer = myDepotLocker; player->setLastDepotId(depot->getDepotId()); } else { openContainer = container; }
Por:
//reward chest and depot container if (item->getActionId() == ITEM_REWARD_CHEST || item->getID() == ITEM_REWARD_CHEST) { DepotLocker* myRewardChest = player->getRewardChest(item->getID()); myRewardChest->setParent(item->getTile()); openContainer = myRewardChest; player->setLastDepotId(REWARD_CHEST_DEPOTID); } else if (DepotLocker* depot = container->getDepotLocker()) { DepotLocker* myDepotLocker = player->getDepotLocker(depot->getDepotId()); myDepotLocker->setParent(depot->getParent()->getTile()); openContainer = myDepotLocker; player->setLastDepotId(depot->getDepotId()); } else { openContainer = container; }
src/game.cpp:
Em cima de...
if (!item->isPushable() || item->hasAttribute(ITEM_ATTRIBUTE_UNIQUEID)) {
...Adicione:
if (item->getID() == ITEM_REWARD_BAG || item->getActionId() == ITEM_REWARD_CHEST) { player->sendCancelMessage(RETURNVALUE_NOTMOVEABLE); return; }
src/monsters.h:
Embaixo de...
int32_t subType; int32_t actionId; std::string text;
...Adicione:
bool uniquedrop;
Embaixo de...
actionId = -1;
...Adicione:
uniquedrop = false;
Embaixo de...
bool hiddenHealth;
...Adicione:
bool rewardChest;
src/monsters.cpp:
Embaixo de...
healthMax = 100;
...Adicione:
rewardChest = false;
Embaixo de...
} else if (strcasecmp(attrName, "hidehealth") == 0) { mType->hiddenHealth = attr.as_bool();
...Adicione:
} else if (strcasecmp(attrName, "rewardchest") == 0) { mType->rewardChest = attr.as_bool();
Em cima de...
if ((attr = node.attribute("actionId"))) {
...Adicione:
if ((attr = node.attribute("uniquedrop"))) { lootBlock.uniquedrop = attr.as_bool(); }
src/iologindata.cpp:
Embaixo de...
//load depot items
...Adicione:
player->getRewardChest(ITEM_REWARD_CHEST);
Mude:
if (pid >= 0 && pid < 100) { DepotChest* depotChest = player->getDepotChest(pid, true); if (depotChest) { depotChest->internalAddThing(item); }
Por:
if (pid >= 0 && pid < 100) { if (pid == REWARD_CHEST_DEPOTID) { DepotChest* depotChest = player->getRewardBag(pid, true); if (depotChest) { depotChest->internalAddThing(item); } } else { DepotChest* depotChest = player->getDepotChest(pid, true); if (depotChest) { depotChest->internalAddThing(item); } }
src/luascript.h:
Em cima de...
// Combat
...Adicione:
// Reward static int luaItemGetNameDescription(lua_State* L); static int luaMonsterTypeUseRewardChest(lua_State* L);
src/luascript.cpp:
Em cima de...
// Party registerClass("Party", "", nullptr);
...Adicione:
// Reward registerMethod("MonsterType", "useRewardChest", LuaScriptInterface::luaMonsterTypeUseRewardChest); registerMethod("Item", "getNameDescription", LuaScriptInterface::luaItemGetNameDescription);
Em cima de...
// Combat int LuaScriptInterface::luaCombatCreate(lua_State* L)
...Adicione:
int LuaScriptInterface::luaItemGetNameDescription(lua_State* L) { // item:getNameDescription() const Item* item = getUserdata<const Item>(L, 1); int32_t subType; bool addArticle = true; if (item) { subType = item->getSubType(); const ItemType& it = Item::items[item->getID()]; std::ostringstream s; const std::string& name = (item ? item->getName() : it.name); if (!name.empty()) { if (it.stackable && subType > 1) { if (it.showCount) { s << subType << ' '; } s << (item ? item->getPluralName() : it.getPluralName()); } else { if (addArticle) { const std::string& article = (item ? item->getArticle() : it.article); if (!article.empty()) { s << article << ' '; } } s << name; } } else { s << "an item of type " << it.id; } pushString(L, s.str()); } else { lua_pushnil(L); } return 1; } int LuaScriptInterface::luaMonsterTypeUseRewardChest(lua_State* L) { // monsterType:useRewardChest() MonsterType* monsterType = getUserdata<MonsterType>(L, 1); if (monsterType) { pushBoolean(L, monsterType->rewardChest); } else { lua_pushnil(L); } return 1; }
Mude:
int LuaScriptInterface::luaMonsterTypeGetLoot(lua_State* L) { // monsterType:getLoot() MonsterType* monsterType = getUserdata<MonsterType>(L, 1); if (!monsterType) { lua_pushnil(L); return 1; } static const std::function<void(const std::vector<LootBlock>&)> parseLoot = [&](const std::vector<LootBlock>& lootList) { lua_createtable(L, lootList.size(), 0); int index = 0; for (const auto& lootBlock : lootList) { lua_createtable(L, 0, 7); setField(L, "itemId", lootBlock.id); setField(L, "chance", lootBlock.chance); setField(L, "subType", lootBlock.subType); setField(L, "maxCount", lootBlock.countmax); setField(L, "actionId", lootBlock.actionId); setField(L, "text", lootBlock.text); parseLoot(lootBlock.childLoot); lua_setfield(L, -2, "childLoot"); lua_rawseti(L, -2, ++index); } }; parseLoot(monsterType->lootItems); return 1; }
Por:
int LuaScriptInterface::luaMonsterTypeGetLoot(lua_State* L) { // monsterType:getLoot() MonsterType* monsterType = getUserdata<MonsterType>(L, 1); if (!monsterType) { lua_pushnil(L); return 1; } static const std::function<void(const std::vector<LootBlock>&)> parseLoot = [&](const std::vector<LootBlock>& lootList) { lua_createtable(L, lootList.size(), 0); int index = 0; for (const auto& lootBlock : lootList) { lua_createtable(L, 0, 8); setField(L, "itemId", lootBlock.id); setField(L, "chance", lootBlock.chance); setField(L, "subType", lootBlock.subType); setField(L, "maxCount", lootBlock.countmax); setField(L, "actionId", lootBlock.actionId); setField(L, "text", lootBlock.text); pushBoolean(L, lootBlock.uniquedrop); lua_setfield(L, -2, "uniquedrop"); parseLoot(lootBlock.childLoot); lua_setfield(L, -2, "childLoot"); lua_rawseti(L, -2, ++index); } }; parseLoot(monsterType->lootItems); return 1; }
Pronto, a parte de C++ acabou agora o resto:
Adicione @ data/items/items/xml
<item id="21518" article="a" name="reward container"> <attribute key="weight" value="1800" /> <attribute key="containersize" value="100" /> <attribute key="slotType" value="backpack" /> </item> <item id="21584" article="a" name="reward chest"> <attribute key="type" value="depot" /> <attribute key="containerSize" value="1" /> <attribute key="description" value="This chest contains your rewards earned in battles." /> </item>
Adicione @ data/creaturescripts/creaturescripts.xml
<event type="kill" name="RewardLoot" script="rewardloot.lua"/>
Registre @ data/creaturescripts/scripts/login.lua
player:registerEvent("RewardLoot")
Crie data/creaturescripts/scripts/rewardloot.lua:
function sort_descending(t) local tmp = {} for k, v in pairs(t) do table.insert(tmp, {k, v}) end table.sort(tmp, function(a, b) return a[2] > b[2] end) return tmp end function table.find(t, v) for i,x in pairs(t) do if x == v then return true end end end function Player:addItemRewardBag(itemid, count) local rewardbag = self:getDepotChest(99) return rewardbag:addItem(itemid, count) end function MonsterType:getBossReward(chance, unique) local ret = {} local function randomItem(lootBlock, chance) local randvalue = math.random(0, 100000) / (getConfigInfo("rateLoot") * chance) if randvalue < lootBlock.chance then if (ItemType(lootBlock.itemId):isStackable()) then return (randvalue%lootBlock.maxCount) + 1 else return 1 end end end local lootBlockList = self:getLoot() for _, loot in pairs(lootBlockList) do local rd = randomItem(loot, chance) if rd then if loot.uniquedrop then if unique then table.insert(ret, {loot, rd}) end else table.insert(ret, {loot, rd}) end end end return ret end BossLoot = {} BossUids = {} function BossLoot:new(boss) if not table.find(BossUids, boss:getId()) then table.insert(BossUids, boss:getId()) return setmetatable({creature=boss}, {__index = BossLoot}) end end function BossLoot:updateDamage() if self.creature then local tmp = {} local totaldmg = 0 for killer, damage in pairs(self.creature:getDamageMap()) do totaldmg = totaldmg+damage.total tmp[killer] = damage.total end self.players = sort_descending(tmp) self.totaldmg = totaldmg else error("Creature not found.") end end function BossLoot:setRewards() if self.totaldmg and self.creature then if getConfigInfo("rateLoot") > 0 then local mt = MonsterType(self.creature:getName()) for i, playertab in ipairs(self.players) do local loot if i == 1 then loot = mt:getBossReward(playertab[2] / self.totaldmg, true) else loot = mt:getBossReward(playertab[2] / self.totaldmg, false) end table.insert(self.players[i], loot) end end else error("Error") end end function BossLoot:addRewards() if self.players and self.players[1] and self.players[1][3] then for i, playertab in ipairs(self.players) do local player = Player(playertab[1]) if player then local str = "The following items are available in your reward chest: " for i, lootTable in ipairs(playertab[3]) do local item = player:addItemRewardBag(lootTable[1].itemId, math.ceil(lootTable[2])) if item then str = str .. item:getNameDescription() .. ", " end end str = str:sub(1, #str-2) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, str) end end else error("Error") end end function onKill(creature, target) if (Monster(target) ~= nil) then local mt = MonsterType(target:getName()) if mt:useRewardChest() then local loot = BossLoot:new(target) if loot then local corpse = Item(doCreateItem(MonsterType(target:getName()):getCorpseId(), 1, target:getPosition())) corpse:decay() target:setDropLoot(false) loot:updateDamage() loot:setRewards() loot:addRewards() corpse:setAttribute('aid', 21584) end end end end
COMO USAR !!
Simplesmente adicione essa flag no monstro:
<flag rewardchest="1" />
Você tambem pode fazer um drop ser unique, assim somente a pessoa que fez a maior parte do dano vai pegar esse item.
<item id="5903" chance="100000" uniquedrop="1" /><!-- ferumbras' hat -->