ID:2339307
 
Code:
//So I downloaded an Item Stack resource to use for stacking items... I was wondering how...
//I can drop more than one item and how I can use itemAdd() to buy more than one of the same...
//item through the trader in my game.

//The code I got for item stacking is here.

obj


Items
var/canStack=0

verb/Get()
set src in oview(1)
usr.itemAdd(src)

verb/Drop()
set src in usr
usr.itemDrop(src)



mob/proc

itemDrop(obj/Items/o)
if(o.Firm != 1) // The o.Firm is because I didn't want to allow people to pick up every obj they see.

if(o.canStack && o.contents.len)
var/obj/Items/theItem=pick(o.contents)
theItem.loc=src.loc
if(o.contents.len)
o.suffix="x[o.contents.len+1]"
else
o.suffix="x1"
else
o.loc=src.loc
o.suffix=""
else return 1

itemAdd(obj/Items/o)
if(o.Firm != 1)
var/list/items=list()

for(var/obj/i in src.contents)
items+=i.type

if(o.type in items) //if a similer item is found in the contents
if(o.canStack)
var/obj/Items/theItem
for(var/obj/i in src.contents) if(i.type == o.type) theItem=i
if(theItem)
theItem.contents+=o
theItem.suffix="x[theItem.contents.len+1]"
else
src.contents+=o
o.suffix="x1"
else
src.contents+=o

else
src.contents+=o
o.suffix="[o.canStack ? "x1" : "[o.suffix]"]"
else return 1



mob
Trader
icon = 'Trader.dmi'
verb
Trade()
set category = null
set src in oview(1)
start
switch(input(usr,"","") in list("Buy","Sell","Prices","Cancel"))
if("Sell")
var/obj/z = input("What would you wish to Sell?") as obj in usr.contents
var/x = alert("I can give you [z.Value] for that.","Shop","Sure","No")
if (x == "Sure")
usr.Money += z.Value
usr << "You sold [z] for [z.Value]"
usr.contents -= z
shop.Add(z)
if (x == "No")
goto start
if ("Buy")
var/obj/Items/B=input("What item would you like to buy?","Buying") as null|anything in shop
var/obj/Items/b = (input("How much do you want to buy?","Quantity Amount") as num)

//I need to know how to multiply b with B for money and also how to apply it to itemAdd()
usr.Money -= B.Value

usr.itemAdd(B)
usr << "You bought [B] for [B.Value]"



if ("Prices")
var/obj/G=input("What item would you like to examine?","Prices") as null|anything in shop
usr << "The [G] is worth [G.Value]"

var/list/shop = list(new /obj/Items/Ruby, new /obj/Items/Apple, new/obj/Items/Loaf_of_Bread, new /obj/Items/Coffee, new /obj/Items/Paper, new /obj/Items/Pack_of_Cigarettes,
new /obj/Items/Lighter, new /obj/Items/Beer/Colers_Green_Ale, new /obj/Items/Beer/Blue_River_Beer)

//If anyone can help it would be greatly appreciated. Thanks in advance.

</b>


Problem description: I want to be able to buy more than one of the same item through the trader using item stacking and I want to be able to drop more than one of the same item.
It'll save you a lot of time in the long run if you try to make your project more readable. That means always giving your variables descriptive names, like "purchase_cost" instead of "b". A lot of new developers, including me, thought that comments were there to explain to other people what you were doing when you wrote the code. The real use of comments is to explain to yourself what you want the code to do before you write it. Learn how to comment first, and you'll save yourself a lot of time and write better code.

I've rewritten the itemAdd proc to give you an example. Notice how it's a lot easier to follow, the code doesn't get indented so much, and we even removed some redundant code:

    itemAdd(obj/Items/new_item)
// Check if the item is firm. Cancel out if it is.
if(new_item.Firm == 1) return TRUE
// Check if the item is stackable. Simply add if it isn't.
if(!new_item.stackable)
src.contents.Add(new_item)
return
// Check if we already have one of the same type. Simply add if we don't.
var/obj/similar_item = locate(new_item.type) in src.contents
if(!similar_item)
src.contents.Add(new_item)
new_item.suffix = new_ite.canStack ? "x1" : new_item.suffix
return
// Now we know the item: is firm, is stackable, and we already have one.
// So add it to the stack we found earlier.
similar_item.contents.Add(new_item)
similar_item.suffix = "x[theItem.contents.len+1]"


Now we can tackle the trader mob. You were using what we call an anti-pattern. Anti-patterns are common ways to try and solve a problem that seem like good solutions, but lead to bigger problem. The anti-pattern here was putting everything into one verb and using goto for control flow. Using goto is a sure sign that you should restructure your code. In my entire time programming I've never had to use a goto because something else wouldn't work better.

Here I've factored out the buy, sell, and price behaviors into their own procs. When you talk to the trader, he calls the trade_menu() proc. This farms out the user response into the trade_buy(), trade_sell(), and trade_prices() procs, which can then call trade_menu() again when they're done. Notice how everything is a lot easier to follow now that it's been separated out and structured?

mob/Trader
icon = 'Trader.dmi'
verb/Trade()
set category = null
set src in oview(1)
trade_menu(usr)

proc/trade_menu(mob/player)
var/player_action = input(player, "", "") in list("Buy", "Sell", "Prices", "Cancel")
switch(player_action)
if("Buy")
trade_buy(usr)
if("Sell")
trade_sell(usr)
if("Prices")
trade_prices(usr)

proc/trade_buy(mob/player)
// Ask the player which item and how many they'd like to buy.
var/obj/Items/buy_item = input(player, "What item would you like to buy?","Buying") as null|anything in shop
var/obj/Items/buy_amount = input(player, "How many do you want to buy?","Quantity Amount") as num
buy_amount = round(buy_amount) // The player could have entered 1.7, etc
// Cancel out if they canceled at item selection or entered 0 amount.
if(!buy_item || !buy_amount)
player << "You bought nothing."
trade_menu(player)
return
// Determine the cost of the items. Cancel if it's negative (cheating player)
var/purchase_cost = buy_item.value * buy_amount
if(purchase_cost < 0)
player << "You can't do that."
trade_menu(player)
return
// If they don't have enough money, cancel out.
if(player.Money < purchase_cost)
player << "You don't have enough money."
trade_menu(player)
return
// Take away cost from player's money, and give them items.
player.Money -= purchase_cost
for(var/purchaseIndex = 1 to buy_quantity)
var/obj/Items/stack_item = new buy_item.type()
player.itemAdd(stack_item)
player << "You bought [B]x[buy_quantity] for [purchase_cost]"
trade_menu(player)

proc/trade_sell(mob/player)
// Ask the player which item they want to sell.
var/obj/sell_item = input("What would you wish to Sell?") as obj|null in usr.contents
// Cancel if they chose null.
if(!sell_item)
trade_menu(player)
return
// Ask for confirmation. Cancel if they don't confirm.
var/confirm = alert(player, "I can give you [z.Value] for that.", "Shop", "Sure", "No")
if (confirm != "Sure")
trade_menu(player)
return
// Remove the item and add its value to the player's money.
player.Money += z.Value
player << "You sold [z] for [z.Value]"
player.itemDrop(z) // We should have a separate itemRemove() for places like this.
shop.Add(z)
// Direct player back to shop menu.
trade_menu(player)

proc/trade_prices(mob/player)
// Ask the player what item they want to know about.
var/obj/price_item = input(player "What item would you like to examine?","Prices") as null|anything in shop
// If they didn't pick null, Show them the price.
if(price_item)
player << "The [price_item] is worth [price_item.Value]"
trade_menu(player)

var/list/shop = list(
new /obj/Items/Ruby,
new /obj/Items/Apple,
new /obj/Items/Loaf_of_Bread,
new /obj/Items/Coffee,
new /obj/Items/Paper,
new /obj/Items/Pack_of_Cigarettes,
new /obj/Items/Lighter,
new /obj/Items/Beer/Colers_Green_Ale,
new /obj/Items/Beer/Blue_River_Beer
)


From here you could also add dropping and selling more than one item. Just ask the user how many they want to drop or sell, like we did in the trade_buy() proc. I would also suggest adding an itemRemove() proc that handles unstacking, and changing itemDrop() to use that proc:

mob/proc
itemRemove(obj/Items/oldItem)
// check if the item is in inventory
// check if it's in a stack with similar items
// remove it from any stacks, changing suffixes
// remove it from inventory
// if it really was in the inventory, return it.
itemDrop(obj/Items/oldItem)
// check if we can remove it from inventory
var/success = itemRemove(oldItem)
// If it wasn't in inventory, it'll return null, so we should cancel out.
if(!success) return
// Now place it on the ground
oldItem.Move(loc)