unknownRanger
Banned User
- Joined
- Aug 7, 2016
- Messages
- 4
- Reaction score
- 14
Ever wanted to use an alternate currency at an npc instead of lugging around all sorts of tokens and knickknacks?
Well now you can, I call this the new npc currency system because it uses both the regular cash in the game but an alternate currency of your choosing.
How does it work? instead of writing a script with a million storage values for each npc, the npc determines the currency based on its currency id.
Using Lailane here as an example.
If any player has a storage value of 123456 set with some value greater than 0, then they can purchase things from Lailene's shop, if the npc does not have a currency id then no worries, the npc will simply use the normal currency.
Basically what you are doing is using the value stored in storage as the currency, the storage value is just to determine what type of currency and is pretty much upto you what to call it.
Now you can have 100's of npc's with 100's of different ways to pay for things.
game.cpp
Find this
Replace the whole function with this.
Next find this
Replace the whole function with this
-----------------------------------------------------
npc.cpp
look for this
Right underneath that you are going to place this.
--------------------------------------------
npc.h
look for this
Place this right underneath
Look for this
Place this right underneath
---------------------------
player.cpp
find this
and replace that function with this
next find this
and replace that function with this
Next you are going to look for
Now right underneath that function you are going to place these.
----------------------------------
player.h
look for this
place this right underneath
find this
and place this right underneath
find this
place this underneath
-----------------------------------------
protocolgame.cpp
Now find this function
and place this right underneath
--------------------------------------------------
protocolgame.h
Find this
place this right underneath
You are going to ban me anyway which I think is highly unfair...
New Npc Currency System by Codex NG xD
Well now you can, I call this the new npc currency system because it uses both the regular cash in the game but an alternate currency of your choosing.
How does it work? instead of writing a script with a million storage values for each npc, the npc determines the currency based on its currency id.
Using Lailane here as an example.
Code:
<?xml version="1.0" encoding="UTF-8"?>
<npc name="Lailene" currency="123456" script="lailene.lua" walkinterval="2000" floorchange="0" speechbubble="2">
<health now="100" max="100"/>
<look type="279" head="114" body="94" legs="113" feet="114" addons="0"/>
</npc>
If any player has a storage value of 123456 set with some value greater than 0, then they can purchase things from Lailene's shop, if the npc does not have a currency id then no worries, the npc will simply use the normal currency.
Basically what you are doing is using the value stored in storage as the currency, the storage value is just to determine what type of currency and is pretty much upto you what to call it.
Now you can have 100's of npc's with 100's of different ways to pay for things.
game.cpp
Find this
Code:
bool Game::removeMoney(Cylinder* cylinder, uint64_t money, uint32_t flags /*= 0*/)
Code:
bool Game::removeMoney(Cylinder* cylinder, uint64_t money, uint32_t flags /*= 0*/)
{
if (cylinder == nullptr) {
return false;
}
if (money == 0) {
return true;
}
uint32_t currencyId = 0;
Player* player;
if (Creature* creature = cylinder->getCreature()) {
if (Player* p = creature->getPlayer()) {
currencyId = p->getNpcCurrencyId();
player = p;
}
}
if (!currencyId) {
std::vector<Container*> containers;
std::multimap<uint32_t, Item*> moneyMap;
uint64_t moneyCount = 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->getWorth();
if (worth != 0) {
moneyCount += worth;
moneyMap.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->getWorth();
if (worth != 0) {
moneyCount += worth;
moneyMap.emplace(worth, item);
}
}
}
}
if (moneyCount < money) {
return false;
}
for (const auto& moneyEntry : moneyMap) {
Item* item = moneyEntry.second;
if (moneyEntry.first < money) {
internalRemoveItem(item);
money -= moneyEntry.first;
}
else if (moneyEntry.first > money) {
const uint32_t worth = moneyEntry.first / item->getItemCount();
const uint32_t removeCount = (money / worth) + 1;
addMoney(cylinder, (worth * removeCount) - money, flags);
internalRemoveItem(item, removeCount);
break;
}
else {
internalRemoveItem(item);
break;
}
}
}
else {
int32_t value;
player->getStorageValue(currencyId, value);
if (value < money) {
return false;
}
player->addStorageValue(currencyId, value - money);
}
return true;
}
Next find this
Code:
void Game::addMoney(Cylinder* cylinder, uint64_t money, uint32_t flags /*= 0*/)
Code:
void Game::addMoney(Cylinder* cylinder, uint64_t money, uint32_t flags /*= 0*/)
{
if (money == 0) {
return;
}
if (Creature* creature = cylinder->getCreature()) {
if (Player* player = creature->getPlayer()) {
if(uint32_t currencyId = player->getNpcCurrencyId()){
int32_t value;
player->getStorageValue(currencyId, value);
player->addStorageValue(currencyId, value + money);
return;
}
}
}
uint32_t crystalCoins = money / 10000;
money -= crystalCoins * 10000;
while (crystalCoins > 0) {
const uint16_t count = std::min<uint32_t>(100, crystalCoins);
Item* remaindItem = Item::CreateItem(ITEM_CRYSTAL_COIN, count);
ReturnValue ret = internalAddItem(cylinder, remaindItem, INDEX_WHEREEVER, flags);
if (ret != RETURNVALUE_NOERROR) {
internalAddItem(cylinder->getTile(), remaindItem, INDEX_WHEREEVER, FLAG_NOLIMIT);
}
crystalCoins -= count;
}
uint16_t platinumCoins = money / 100;
if (platinumCoins != 0) {
Item* remaindItem = Item::CreateItem(ITEM_PLATINUM_COIN, platinumCoins);
ReturnValue ret = internalAddItem(cylinder, remaindItem, INDEX_WHEREEVER, flags);
if (ret != RETURNVALUE_NOERROR) {
internalAddItem(cylinder->getTile(), remaindItem, INDEX_WHEREEVER, FLAG_NOLIMIT);
}
money -= platinumCoins * 100;
}
if (money != 0) {
Item* remaindItem = Item::CreateItem(ITEM_GOLD_COIN, money);
ReturnValue ret = internalAddItem(cylinder, remaindItem, INDEX_WHEREEVER, flags);
if (ret != RETURNVALUE_NOERROR) {
internalAddItem(cylinder->getTile(), remaindItem, INDEX_WHEREEVER, FLAG_NOLIMIT);
}
}
}
npc.cpp
look for this
Code:
pugi::xml_attribute attr;
if ((attr = npcNode.attribute("speed"))) {
baseSpeed = pugi::cast<uint32_t>(attr.value());
} else {
baseSpeed = 100;
}
Right underneath that you are going to place this.
Code:
if ((attr = npcNode.attribute("currency"))) {
currency = pugi::cast<uint32_t>(attr.value());
}
npc.h
look for this
Code:
bool isPushable() const final {
return walkTicks > 0;
}
Code:
uint32_t getCurrencyId() const {
return currency;
}
Look for this
Code:
uint32_t walkTicks;
Code:
uint32_t currency;
player.cpp
find this
Code:
void Player::openShopWindow(Npc* npc, const std::list<ShopInfo>& shop)
Code:
void Player::openShopWindow(Npc* npc, const std::list<ShopInfo>& shop)
{
shopItemList = shop;
sendShop(npc);
sendSaleItemList(npc);
}
next find this
Code:
bool Player::updateSaleShopList(const Item* item)
and replace that function with this
Code:
bool Player::updateSaleShopList(const Item* item)
{
uint16_t itemId = item->getID();
if (itemId != ITEM_GOLD_COIN && itemId != ITEM_PLATINUM_COIN && itemId != ITEM_CRYSTAL_COIN) {
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 updateSaleShopList(containerItem);
});
}
}
if (client) {
client->sendSaleItemList(shopOwner, shopItemList);
}
return true;
}
Next you are going to look for
Code:
uint64_t Player::getMoney() const
Code:
uint64_t Player::getMoney(Npc* npc) const
{
uint64_t cash;
setNpcCurrencyId(npc);
uint32_t currencyId = getNpcCurrencyId();
if (currencyId) {
int32_t value;
getStorageValue(currencyId, value);
cash = (uint64_t)value;
}
else {
cash = getMoney();
}
return cash;
}
void Player::setNpcCurrencyId(Npc* npc) const{
currencyId = npc->getCurrencyId();
}
uint32_t Player::getNpcCurrencyId() const {
return currencyId;
}
player.h
look for this
Code:
uint64_t getMoney() const;
place this right underneath
Code:
uint64_t getMoney(Npc*) const;
void setNpcCurrencyId(Npc*) const;
uint32_t getNpcCurrencyId() const;
Code:
void sendShop(Npc* npc) const {
if (client) {
client->sendShop(npc, shopItemList);
}
}
and place this right underneath
Code:
void sendSaleItemList(Npc* npc) const {
if (client) {
client->sendSaleItemList(npc, shopItemList);
}
}
Code:
uint32_t manaMax;
Code:
mutable uint32_t currencyId;
protocolgame.cpp
Now find this function
Code:
void ProtocolGame::sendSaleItemList(const std::list<ShopInfo>& shop)
and place this right underneath
Code:
void ProtocolGame::sendSaleItemList(Npc* npc, const std::list<ShopInfo>& shop)
{
NetworkMessage msg;
msg.addByte(0x7B);
msg.add<uint64_t>(player->getMoney(npc));
std::map<uint16_t, uint32_t> saleMap;
if (shop.size() <= 5) {
// For very small shops it's not worth it to create the complete map
for (const ShopInfo& shopInfo : shop) {
if (shopInfo.sellPrice == 0) {
continue;
}
int8_t subtype = -1;
const ItemType& itemType = Item::items[shopInfo.itemId];
if (itemType.hasSubType() && !itemType.stackable) {
subtype = (shopInfo.subType == 0 ? -1 : shopInfo.subType);
}
uint32_t count = player->getItemTypeCount(shopInfo.itemId, subtype);
if (count > 0) {
saleMap[shopInfo.itemId] = count;
}
}
} else {
// Large shop, it's better to get a cached map of all item counts and use it
// We need a temporary map since the finished map should only contain items
// available in the shop
std::map<uint32_t, uint32_t> tempSaleMap;
player->getAllItemTypeCount(tempSaleMap);
// We must still check manually for the special items that require subtype matches
// (That is, fluids such as potions etc., actually these items are very few since
// health potions now use their own ID)
for (const ShopInfo& shopInfo : shop) {
if (shopInfo.sellPrice == 0) {
continue;
}
int8_t subtype = -1;
const ItemType& itemType = Item::items[shopInfo.itemId];
if (itemType.hasSubType() && !itemType.stackable) {
subtype = (shopInfo.subType == 0 ? -1 : shopInfo.subType);
}
if (subtype != -1) {
uint32_t count;
if (!itemType.isFluidContainer() && !itemType.isSplash()) {
count = player->getItemTypeCount(shopInfo.itemId, subtype); // This shop item requires extra checks
} else {
count = subtype;
}
if (count > 0) {
saleMap[shopInfo.itemId] = count;
}
} else {
std::map<uint32_t, uint32_t>::const_iterator findIt = tempSaleMap.find(shopInfo.itemId);
if (findIt != tempSaleMap.end() && findIt->second > 0) {
saleMap[shopInfo.itemId] = findIt->second;
}
}
}
}
uint8_t itemsToSend = std::min<size_t>(saleMap.size(), std::numeric_limits<uint8_t>::max());
msg.addByte(itemsToSend);
uint8_t i = 0;
for (std::map<uint16_t, uint32_t>::const_iterator it = saleMap.begin(); i < itemsToSend; ++it, ++i) {
msg.addItemId(it->first);
msg.addByte(std::min<uint32_t>(it->second, std::numeric_limits<uint8_t>::max()));
}
writeToOutputBuffer(msg);
}
protocolgame.h
Find this
Code:
void sendSaleItemList(const std::list<ShopInfo>& shop);
place this right underneath
Code:
void sendSaleItemList(Npc* npc, const std::list<ShopInfo>& shop);
You are going to ban me anyway which I think is highly unfair...
New Npc Currency System by Codex NG xD
Last edited: