From ce39d59356a0e57e9a0eeecfc9137bf1d00784a3 Mon Sep 17 00:00:00 2001 From: zorman2000 Date: Thu, 9 Feb 2017 11:38:14 -0500 Subject: [PATCH] Trade: Full support for dedicated trading. --- trade/prices.lua | 4 +- trade/trade.lua | 145 ++++++++++++++++++++++++++++++++++------------- 2 files changed, 108 insertions(+), 41 deletions(-) diff --git a/trade/prices.lua b/trade/prices.lua index 6c6ef7d..16a0809 100644 --- a/trade/prices.lua +++ b/trade/prices.lua @@ -82,7 +82,7 @@ end -- Gets all the item for a specified budget function npc.trade.prices.get_items_for_currency_count(tier, count, price_factor) local result = {} - minetest.log("Currency quantity: "..dump(count)) + --minetest.log("Currency quantity: "..dump(count)) for item_name, price in pairs(npc.trade.prices.table) do -- Check price currency is of the same tier if price.tier == tier and price.count <= count then @@ -116,7 +116,7 @@ function npc.trade.prices.get_items_for_currency_count(tier, count, price_factor result[item_name].max_buyable_item_count = max_buying_item_count end end - minetest.log("Final result: "..dump(result)) + --minetest.log("Final result: "..dump(result)) return result end diff --git a/trade/trade.lua b/trade/trade.lua index 936fdd4..0e22a46 100644 --- a/trade/trade.lua +++ b/trade/trade.lua @@ -9,6 +9,10 @@ npc.trade.NONE = "none" npc.trade.OFFER_BUY = "buy" npc.trade.OFFER_SELL = "sell" +-- This variable establishes how much items a dedicated +-- trader will buy until retiring the offer +npc.trade.DEDICATED_MAX_BUY_AMOUNT = 5 + -- This table holds all responses for trades npc.trade.results = { single_trade_offer = {}, @@ -193,6 +197,9 @@ function npc.trade.generate_trade_offers_by_status(self) [1] = npc.trade.get_casual_trade_offer(self, npc.trade.OFFER_SELL) } elseif status == npc.trade.TRADER then + -- Clear current offers + self.trader_data.buy_offers = {} + self.trader_data.sell_offers = {} -- Get trade offers for a dedicated trader local offers = npc.trade.get_dedicated_trade_offers(self) -- Store buy offers @@ -291,49 +298,70 @@ function npc.trade.get_dedicated_trade_offers(self) local trade_list = self.trader_data.trade_list.both for item_name, trade_info in pairs(trade_list) do + -- Abort if more than 12 buy or sell offers are made + if table.getn(offers.sell) >= 12 or table.getn(offers.buy) >= 12 then + break + end -- For each item on the trader list, check if it is in the NPC inventory. -- If it is, create a sell offer, else create a buy offer if possible. + -- Also, avoid creating sell offers immediately if the item was just bought local item = npc.inventory_contains(self, item_name) - if item ~= nil then - -- Create sell offer for this item. Currently, traders will offer to sell only - -- of their items to allow the fine control for players to buy what they want. - -- This requires, however, that the trade offers are re-generated everytime a - -- sell is made. - table.insert(offers.sell, npc.trade.create_offer( - npc.trade.OFFER_SELL, - item_name, - nil, - nil, - 1) - ) - -- Set last offer type - trade_info.last_offer_type = npc.trade.OFFER_SELL + if item ~= nil and trade_info.last_offer_type ~= npc.trade.OFFER_BUY then + + -- Create sell offer for this item. Currently, traders will offer to sell only + -- of their items to allow the fine control for players to buy what they want. + -- This requires, however, that the trade offers are re-generated everytime a + -- sell is made. + table.insert(offers.sell, npc.trade.create_offer( + npc.trade.OFFER_SELL, + item_name, + nil, + nil, + 1) + ) + -- Set last offer type + trade_info.last_offer_type = npc.trade.OFFER_SELL + else -- Avoid flipping an item to the buy side if the stock was just depleted if trade_info.last_offer_type ~= npc.trade.OFFER_SELL then -- Create buy offer for this item -- Only do if the NPC can actually afford the items. local currencies = npc.trade.get_currencies_in_inventory(self) - -- Choose a random currency - local chosen_tier = currencies[math.random(#currencies)] - -- Get items for this currency - local buyable_items = - npc.trade.prices.get_items_for_currency_count(chosen_tier.name, chosen_tier.count, 0.5) - -- Check if the item from trader list is present in the buyable items list - for buyable_item, price_info in pairs(buyable_items) do - if buyable_item == item_name then - -- If item found, create a buy offer for this item - -- Again, offers are created for one item only. Buy offers should be removed - -- after the NPC has bought a certain quantity, say, 5 items. - table.insert(offers.buy, npc.trade.create_offer( - npc.trade.OFFER_BUY, - item_name, - price_info.price, - price_info.min_buyable_item_price, - price_info.min_buyable_item_count) - ) - -- Set last offer type - trade_info.last_offer_type = npc.trade.OFFER_BUY + -- Check if currency isn't empty + if #currencies > 0 then + -- Choose a random currency + local chosen_tier = currencies[math.random(#currencies)] + -- Get items for this currency + local buyable_items = + npc.trade.prices.get_items_for_currency_count(chosen_tier.name, chosen_tier.count, 0.5) + -- Check if the item from trader list is present in the buyable items list + for buyable_item, price_info in pairs(buyable_items) do + if buyable_item == item_name then + -- If item found, create a buy offer for this item + -- Again, offers are created for one item only. Buy offers should be removed + -- after the NPC has bought a certain quantity, say, 5 items. + minetest.log("Item: "..item_name) + minetest.log("Trade info: "..dump(trade_info)) + minetest.log("Logic: "..dump(trade_info.item_bought_count == nil + or (trade_info.item_bought_count ~= nil and trade_info.item_bought_count <= npc.trade.DEDICATED_MAX_BUY_AMOUNT))) + if trade_info.item_bought_count == nil + or (trade_info.item_bought_count ~= nil and trade_info.item_bought_count <= npc.trade.DEDICATED_MAX_BUY_AMOUNT) then + -- Create trade offer for this item + table.insert(offers.buy, npc.trade.create_offer( + npc.trade.OFFER_BUY, + item_name, + price_info.price, + price_info.min_buyable_item_price, + price_info.min_buyable_item_count) + ) + -- Set last offer type + trade_info.last_offer_type = npc.trade.OFFER_BUY + else + -- Clear the trade info for this item + trade_info.item_bought_count = 0 + end + end end end end @@ -382,8 +410,9 @@ function npc.trade.create_offer(offer_type, item, price, min_price_item_count, c end --- TODO: THis method needs to be refactored to be able to manage --- both NPC inventories and chest inventories +-- TODO: This method needs to be refactored to be able to manage +-- both NPC inventories and chest inventories. +-- Returns true if trade was possible, else returns false. function npc.trade.perform_trade(self, player_name, offer) local item_stack = ItemStack(offer.item) @@ -397,20 +426,25 @@ function npc.trade.perform_trade(self, player_name, offer) if inv:contains_item("main", item_stack) then -- Check if there is enough room to add the price item to player if inv:room_for_item("main", price_stack) then - -- Remove item from player + -- Remove item from player inv:remove_item("main", item_stack) + -- Remove price item from NPC + npc.take_item_from_inventory_itemstring(self, offer.price) -- Add item to NPC's inventory npc.add_item_to_inventory_itemstring(self, offer.item) -- Add price items to player inv:add_item("main", price_stack) -- Send message to player minetest.chat_send_player(player_name, "Thank you!") + return true else minetest.chat_send_player(player_name, "Looks like you can't get what I'm giving you for payment!") + return false end else minetest.chat_send_player(player_name, "Looks like you don't have what I want to buy...") + return false end else -- If NPC is selling to the player, then player gives price and gets @@ -419,19 +453,24 @@ function npc.trade.perform_trade(self, player_name, offer) if inv:contains_item("main", price_stack) then -- Check if there is enough room to add the item to player if inv:room_for_item("main", item_stack) then - -- Remove item from player + -- Remove price item from player inv:remove_item("main", price_stack) + -- Remove sell item from NPC + npc.take_item_from_inventory_itemstring(self, offer.item) -- Add item to NPC's inventory npc.add_item_to_inventory_itemstring(self, offer.price) -- Add price items to player inv:add_item("main", item_stack) -- Send message to player minetest.chat_send_player(player_name, "Thank you!") + return true else minetest.chat_send_player(player_name, "Looks like you can't carry anything else...") + return false end else minetest.chat_send_player(player_name, "Looks like you don't have what I'm asking for!") + return false end end end @@ -469,7 +508,35 @@ minetest.register_on_player_receive_fields(function (player, formname, fields) for i = 1, #trade_offers do local price_button = "price"..tostring(i) if fields[price_button] then - npc.trade.perform_trade(player_response.npc, player_name, trade_offers[i]) + local trade_result = npc.trade.perform_trade(player_response.npc, player_name, trade_offers[i]) + if trade_result == true then + -- Lock actions + npc.lock_actions(player_response.npc) + -- Account for buyed items + if player_response.offers_type == npc.trade.OFFER_BUY then + -- Increase the item bought count + local offer_item_name = npc.get_item_name(trade_offers[i].item) + minetest.log("Bought item name: "..dump(offer_item_name)) + minetest.log(dump(player_response.npc.trader_data.trade_list.both[offer_item_name])) + -- Check if this item has been bought before + if player_response.npc.trader_data.trade_list.both[offer_item_name].item_bought_count == nil then + -- Set first count to 1 + player_response.npc.trader_data.trade_list.both[offer_item_name].item_bought_count = 1 + else + -- Increase count + player_response.npc.trader_data.trade_list.both[offer_item_name].item_bought_count + = player_response.npc.trader_data.trade_list.both[offer_item_name].item_bought_count + 1 + end + end + -- Re-generate trade offers + npc.trade.generate_trade_offers_by_status(player_response.npc) + -- Show refreshed formspec again to player + npc.trade.show_dedicated_trade_formspec(player_response.npc, player, player_response.offers_type) + return true + else + minetest.close_formspec(player_name, "advanced_npc:dedicated_trading_offers") + return false + end --minetest.log("Player selected: "..dump(trade_offers[i])) end end