Aeluu
Member
- Joined
- Apr 12, 2017
- Messages
- 47
- Reaction score
- 22
Let's say you wanted to add a new in-game token as a separate economy from gold/platinum/crystal.
First, pick a stackable item that you want to use as the token.
Changes to TFS Source:
items.xml:
Now add the token in the c++ source as a constant.
const.h:
Then, add the functions to remove/add the new tokens.
1. Search for "removeMoney"
2. Add this after the function:
game.cpp:
Add declarations in the header:
game.h:
Inside item, add function to get the worth of the token.
1. Search for "getWorth"
2. Add this after the function.
item.cpp:
Add declaration in header.
item.h:
Register token enum for lua script.
luascript.cpp:
Register methods.
luascript.cpp:
and then add functions for above declarations
luascript.cpp:
Add in header.
luascript.h:
In player, we add a few tweaks to some functions:
player.cpp (postAddNotification):
player.cpp (postRemoveNotification):
Add this function:
player.cpp:
as well as this
player.cpp:
add functions to header.
player.h:
Continued....
Changes to TFS Data Folder:
Search for "getPlayerMoney" and add this underneath:
compat.lua:
search for "doPlayerAddMoney" and add these underneath:
compat.lua:
Next, go to player.lua and add edit "canCarryMoney" to this.
player.lua:
search for "removeTotalTokens" and add this above:
player.lua:
Next, the NPC additions.
Go to npc.lua and search for "doPlayerSellItem" and add this under the function.
npc.lua:
right under the function you just added, you'll see "getCount", add this under neath that function.
npc.lua:
in modules, add the Token Shop Module.
modules.lua (too big to post):
and on the npc handler side, add some messages:
npchandler.lua:
further into npchandler, write messages.
npchandler.lua:
Now, just edit a NPC to buy/sell with the new Token Shop Module:
Riona.xml:
And that's it!
First, pick a stackable item that you want to use as the token.
Changes to TFS Source:
items.xml:
XML:
<item id="6527" article="a" name="Tharian token">
<attribute key="weight" value="5" />
</item>
Now add the token in the c++ source as a constant.
const.h:
C++:
ITEM_GOLD_COIN = 2148,
ITEM_PLATINUM_COIN = 2152,
ITEM_CRYSTAL_COIN = 2160,
ITEM_THARIAN_TOKEN = 6527 // Token to add "Tharian Token"
Then, add the functions to remove/add the new tokens.
1. Search for "removeMoney"
2. Add this after the function:
game.cpp:
C++:
bool Game::removeTokens(Cylinder* cylinder, uint64_t tokens, uint32_t flags /*= 0*/)
{
if (cylinder == nullptr) {
return false;
}
if (tokens == 0) {
return true;
}
std::vector<Container*> containers;
std::multimap<uint32_t, Item*> tokenMap;
uint64_t tokenCount = 0;
for (size_t i = cylinder->getFirstIndex(), j = cylinder->getLastIndex(); i < j; ++i) {
Thing* thing = cylinder->getThing(i);
if (!thing) {
continue;
}
Item* item = thing->getItem();
if (!item) {
continue;
}
Container* container = item->getContainer();
if (container) {
containers.push_back(container);
} else {
const uint32_t worth = item->getTokenWorth();
if (worth != 0) {
tokenCount += worth;
tokenMap.emplace(worth, item);
}
}
}
size_t i = 0;
while (i < containers.size()) {
Container* container = containers[i++];
for (Item* item : container->getItemList()) {
Container* tmpContainer = item->getContainer();
if (tmpContainer) {
containers.push_back(tmpContainer);
} else {
const uint32_t worth = item->getTokenWorth();
if (worth != 0) {
tokenCount += worth;
tokenMap.emplace(worth, item);
}
}
}
}
if (tokenCount < tokens) {
return false;
}
for (const auto& tokenEntry : tokenMap) {
Item* item = tokenEntry.second;
if (tokenEntry.first < tokens) {
internalRemoveItem(item);
tokens -= tokenEntry.first;
} else if (tokenEntry.first > tokens) {
const uint32_t worth = tokenEntry.first / item->getItemCount();
const uint32_t removeCount = std::ceil(tokens / static_cast<double>(worth));
addTokens(cylinder, (worth * removeCount) - tokens, flags);
internalRemoveItem(item, removeCount);
break;
} else {
internalRemoveItem(item);
break;
}
}
return true;
}
void Game::addTokens(Cylinder *cylinder, uint64_t tokens, uint32_t flags /*= 0*/)
{
if (tokens == 0)
{
return;
}
if (tokens != 0)
{
Item *remaindItem = Item::CreateItem(ITEM_THARIAN_TOKEN, tokens);
ReturnValue ret = internalAddItem(cylinder, remaindItem, INDEX_WHEREEVER, flags);
if (ret != RETURNVALUE_NOERROR)
{
internalAddItem(cylinder->getTile(), remaindItem, INDEX_WHEREEVER, FLAG_NOLIMIT);
}
}
}
Add declarations in the header:
game.h:
C++:
bool removeTokens(Cylinder* cylinder, uint64_t tokens, uint32_t flags = 0);
void addTokens(Cylinder* cylinder, uint64_t tokens, uint32_t flags = 0);
Inside item, add function to get the worth of the token.
1. Search for "getWorth"
2. Add this after the function.
item.cpp:
C++:
uint32_t Item::getTokenWorth() const
{
switch (id) {
case ITEM_THARIAN_TOKEN:
return count;
default:
return 0;
}
}
Add declaration in header.
item.h:
C++:
uint32_t getTokenWorth() const;
Register token enum for lua script.
luascript.cpp:
C++:
registerEnum(ITEM_THARIAN_TOKEN)
Register methods.
luascript.cpp:
C++:
registerMethod("Player", "getTokens", LuaScriptInterface::luaPlayerGetTokens);
registerMethod("Player", "addTokens", LuaScriptInterface::luaPlayerAddTokens);
registerMethod("Player", "removeTokens", LuaScriptInterface::luaPlayerRemoveTokens);
and then add functions for above declarations
luascript.cpp:
C++:
int LuaScriptInterface::luaPlayerGetTokens(lua_State* L)
{
// player:getTokens()
Player* player = getUserdata<Player>(L, 1);
if (player) {
lua_pushnumber(L, player->getTokens());
} else {
lua_pushnil(L);
}
return 1;
}
int LuaScriptInterface::luaPlayerAddTokens(lua_State* L)
{
// player:addTokens(tokens)
uint64_t tokens = getNumber<uint64_t>(L, 2);
Player* player = getUserdata<Player>(L, 1);
if (player) {
g_game.addTokens(player, tokens);
pushBoolean(L, true);
} else {
lua_pushnil(L);
}
return 1;
}
int LuaScriptInterface::luaPlayerRemoveTokens(lua_State* L)
{
// player:removeTokens(tokens)
Player* player = getUserdata<Player>(L, 1);
if (player) {
uint64_t tokens = getNumber<uint64_t>(L, 2);
pushBoolean(L, g_game.removeTokens(player, tokens));
} else {
lua_pushnil(L);
}
return 1;
}
Add in header.
luascript.h:
C++:
static int luaPlayerGetTokens(lua_State* L);
static int luaPlayerAddTokens(lua_State* L);
static int luaPlayerRemoveTokens(lua_State* L);
In player, we add a few tweaks to some functions:
player.cpp (postAddNotification):
C++:
...
if (shopOwner && requireListUpdate) {
updateSaleShopList(item);
updateTokenShopList(item); // <--- Add this
}
...
player.cpp (postRemoveNotification):
C++:
...
if (shopOwner && requireListUpdate) {
updateSaleShopList(item);
updateTokenShopList(item); // <--- Add this
}
...
Add this function:
player.cpp:
C++:
bool Player::updateTokenShopList(const Item* item)
{
uint16_t itemId = item->getID();
if (itemId != ITEM_THARIAN_TOKEN) {
auto it = std::find_if(shopItemList.begin(), shopItemList.end(), [itemId](const ShopInfo& shopInfo) { return shopInfo.itemId == itemId && shopInfo.sellPrice != 0; });
if (it == shopItemList.end()) {
const Container* container = item->getContainer();
if (!container) {
return false;
}
const auto& items = container->getItemList();
return std::any_of(items.begin(), items.end(), [this](const Item* containerItem) {
return updateTokenShopList(containerItem);
});
}
}
if (client) {
client->sendSaleItemList(shopItemList);
}
return true;
}
as well as this
player.cpp:
C++:
uint64_t Player::getTokens() const
{
std::vector<const Container*> containers;
uint64_t tokenCount = 0;
for (int32_t i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; ++i) {
Item* item = inventory[i];
if (!item) {
continue;
}
const Container* container = item->getContainer();
if (container) {
containers.push_back(container);
} else {
tokenCount += item->getTokenWorth();
}
}
size_t i = 0;
while (i < containers.size()) {
const Container* container = containers[i++];
for (const Item* item : container->getItemList()) {
const Container* tmpContainer = item->getContainer();
if (tmpContainer) {
containers.push_back(tmpContainer);
} else {
tokenCount += item->getTokenWorth();
}
}
}
return tokenCount;
}
add functions to header.
player.h:
C++:
uint64_t getTokens() const;
bool updateTokenShopList(const Item* item);
Continued....
Post automatically merged:
Changes to TFS Data Folder:
Search for "getPlayerMoney" and add this underneath:
compat.lua:
Lua:
function getPlayerTokens(cid) local p = Player(cid) return p and p:getTokens() or false end
search for "doPlayerAddMoney" and add these underneath:
compat.lua:
Lua:
function doPlayerAddTokens(cid, tokens) local p = Player(cid) return p and p:addTokens(tokens) or false end
function doPlayerRemoveTokens(cid, tokens) local p = Player(cid) return p and p:removeTokens(tokens) or false end
Next, go to player.lua and add edit "canCarryMoney" to this.
player.lua:
Lua:
function Player.canCarryMoney(self, amount)
-- Anyone can carry as much imaginary money as they desire
if amount == 0 then
return true
end
-- The 3 below loops will populate these local variables
local totalWeight = 0
local inventorySlots = 0
-- Add crystal coins to totalWeight and inventorySlots
local type_crystal = ItemType(ITEM_CRYSTAL_COIN)
local crystalCoins = math.floor(amount / 10000)
if crystalCoins > 0 then
amount = amount - (crystalCoins * 10000)
while crystalCoins > 0 do
local count = math.min(100, crystalCoins)
totalWeight = totalWeight + type_crystal:getWeight(count)
crystalCoins = crystalCoins - count
inventorySlots = inventorySlots + 1
end
end
-- Add platinum coins to totalWeight and inventorySlots
local type_platinum = ItemType(ITEM_PLATINUM_COIN)
local platinumCoins = math.floor(amount / 100)
if platinumCoins > 0 then
amount = amount - (platinumCoins * 100)
while platinumCoins > 0 do
local count = math.min(100, platinumCoins)
totalWeight = totalWeight + type_platinum:getWeight(count)
platinumCoins = platinumCoins - count
inventorySlots = inventorySlots + 1
end
end
-- Add gold coins to totalWeight and inventorySlots
local type_gold = ItemType(ITEM_GOLD_COIN)
if amount > 0 then
while amount > 0 do
local count = math.min(100, amount)
totalWeight = totalWeight + type_gold:getWeight(count)
amount = amount - count
inventorySlots = inventorySlots + 1
end
end
-- Add Tharian tokens to totalWeight and inventorySlots
local type_tharian_token = ItemType(ITEM_THARIAN_TOKEN)
if amount > 0 then
while amount > 0 do
local count = math.min(100, amount)
totalWeight = totalWeight + type_tharian_token:getWeight(count)
amount = amount - count
inventorySlots = inventorySlots + 1
end
end
-- If player don't have enough capacity to carry this money
if self:getFreeCapacity() < totalWeight then
return false
end
-- If player don't have enough available inventory slots to carry this money
local backpack = self:getSlotItem(CONST_SLOT_BACKPACK)
if not backpack or backpack:getEmptySlots(true) < inventorySlots then
return false
end
return true
end
search for "removeTotalTokens" and add this above:
player.lua:
Lua:
function Player.removeTotalTokens(self, amount)
local tokensCount = self:getTokens()
if amount <= tokensCount then
self:removeTokens(amount)
return true
end
return false
end
Next, the NPC additions.
Go to npc.lua and search for "doPlayerSellItem" and add this under the function.
npc.lua:
Lua:
function doPlayerBuyTokenItemContainer(cid, containerid, itemid, count, cost, charges)
local player = Player(cid)
if not player:removeTotalTokens(cost) then
return false
end
for i = 1, count do
local container = Game.createItem(containerid, 1)
for x = 1, ItemType(containerid):getCapacity() do
container:addItem(itemid, charges)
end
if player:addItemEx(container, true) ~= RETURNVALUE_NOERROR then
return false
end
end
return true
end
right under the function you just added, you'll see "getCount", add this under neath that function.
npc.lua:
Lua:
function Player.getTotalTokens(self)
return self:getTokens()
end
function isValidTokens(tokens)
return isNumber(tokens) and tokens > 0
end
function getTokensWeight(tokens)
local t = tokens
local tharian = math.floor(t / 100)
t = t - tharian * 100
return (ItemType(ITEM_THARIAN_TOKEN):getWeight() * t)
end
function getTokensCount(string)
local b, e = string:find("%d+")
local tokens = b and e and tonumber(string:sub(b, e)) or -1
if isValidTokens(tokens) then
return tokens
end
return -1
end
Post automatically merged:
in modules, add the Token Shop Module.
modules.lua (too big to post):
TokenShopModule = { npcHandler = nil, yesNode = nil, noNode = nil, - Pastebin.com
Pastebin.com is the number one paste tool since 2002. Pastebin is a website where you can store text online for a set period of time.
pastebin.com
and on the npc handler side, add some messages:
npchandler.lua:
Lua:
MESSAGE_MISSINGTOKENS = 24 -- When the player does not have enough tokens.
MESSAGE_NEEDTOKENS = 25 -- Same as above, used for shop window.
MESSAGE_TS_BUY = 26 -- When the npc asks the player if he wants to buy something.
MESSAGE_TS_ONBUY = 27 -- When the player successfully buys something via talk.
MESSAGE_TS_BOUGHT = 28 -- When the player bought something through the shop window.
MESSAGE_TS_SELL = 29 -- When the npc asks the player if he wants to sell something.
MESSAGE_TS_ONSELL = 30 -- When the player successfully sells something via talk.
MESSAGE_TS_SOLD = 31 -- When the player sold something through the shop window.
further into npchandler, write messages.
npchandler.lua:
Lua:
[MESSAGE_MISSINGTOKENS] = "You don't have enough tokens.",
[MESSAGE_NEEDTOKENS] = "You don't have enough tokens.",
[MESSAGE_TS_BUY] = "Do you want to buy |ITEMCOUNT| |ITEMNAME| for |TOTALCOST| tokens?",
[MESSAGE_TS_ONBUY] = "Here you are.",
[MESSAGE_TS_BOUGHT] = "Bought |ITEMCOUNT|x |ITEMNAME| for |TOTALCOST| tokens.",
[MESSAGE_TS_SELL] = "Do you want to sell |ITEMCOUNT| |ITEMNAME| for |TOTALCOST| tokens?",
[MESSAGE_TS_ONSELL] = "Here you are, |TOTALCOST| tokens.",
[MESSAGE_TS_SOLD] = "Sold |ITEMCOUNT|x |ITEMNAME| for |TOTALCOST| tokens.",
Now, just edit a NPC to buy/sell with the new Token Shop Module:
Riona.xml:
XML:
<?xml version="1.0" encoding="UTF-8"?>
<npc name="Riona" script="default.lua" walkinterval="2000" floorchange="0">
<health now="100" max="100" />
<look type="138" head="57" body="59" legs="40" feet="76" addons="0" />
<parameters>
<parameter key="module_token_shop" value="1" />
<parameter key="token_shop_buyable" value="backpack,1988,20;pick,2553,10;shovel,2554,20;rope,2120,50" />
<parameter key="token_shop_sellable" value="backpack,1988,20;pick,2553,10;shovel,2554,20;rope,2120,50" />
</parameters>
</npc>
And that's it!