/* Stack type objects! * Contains: * Stacks * Recipe datum * Recipe list datum */ /* * Stacks */ /obj/item/stack gender = PLURAL origin_tech = "materials=1" var/list/datum/stack_recipe/recipes var/singular_name var/amount = 1 var/max_amount //also see stack recipes initialisation, param "max_res_amount" must be equal to this max_amount var/stacktype //determines whether different stack types can merge var/uses_charge = 0 var/list/charge_costs = null var/list/datum/matter_synth/synths = null /obj/item/stack/New(var/loc, var/amount=null) ..() if (!stacktype) stacktype = type if (amount) src.amount = amount return /obj/item/stack/Destroy() if(uses_charge) return 1 if (src && usr && usr.machine == src) usr << browse(null, "window=stack") return ..() /obj/item/stack/examine(mob/user) if(..(user, 1)) if(!uses_charge) user << "There are [src.amount] [src.singular_name]\s in the stack." else user << "There is enough charge for [get_amount()]." /obj/item/stack/attack_self(mob/user as mob) list_recipes(user) /obj/item/stack/proc/list_recipes(mob/user as mob, recipes_sublist) if (!recipes) return if (!src || get_amount() <= 0) user << browse(null, "window=stack") user.set_machine(src) //for correct work of onclose var/list/recipe_list = recipes if (recipes_sublist && recipe_list[recipes_sublist] && istype(recipe_list[recipes_sublist], /datum/stack_recipe_list)) var/datum/stack_recipe_list/srl = recipe_list[recipes_sublist] recipe_list = srl.recipes var/t1 = text("Constructions from []Amount Left: []
", src, src.get_amount()) for(var/i=1;i<=recipe_list.len,i++) var/E = recipe_list[i] if (isnull(E)) t1 += "
" continue if (i>1 && !isnull(recipe_list[i-1])) t1+="
" if (istype(E, /datum/stack_recipe_list)) var/datum/stack_recipe_list/srl = E if (src.get_amount() >= srl.req_amount) t1 += "[srl.title] ([srl.req_amount] [src.singular_name]\s)" else t1 += "[srl.title] ([srl.req_amount] [src.singular_name]\s)
" if (istype(E, /datum/stack_recipe)) var/datum/stack_recipe/R = E var/max_multiplier = round(src.get_amount() / R.req_amount) var/title as text var/can_build = 1 can_build = can_build && (max_multiplier>0) if (R.res_amount>1) title+= "[R.res_amount]x [R.title]\s" else title+= "[R.title]" title+= " ([R.req_amount] [src.singular_name]\s)" if (can_build) t1 += text("[title] ") else t1 += text("[]", title) continue if (R.max_res_amount>1 && max_multiplier>1) max_multiplier = min(max_multiplier, round(R.max_res_amount/R.res_amount)) t1 += " |" var/list/multipliers = list(5,10,25) for (var/n in multipliers) if (max_multiplier>=n) t1 += " [n*R.res_amount]x" if (!(max_multiplier in multipliers)) t1 += " [max_multiplier*R.res_amount]x" t1 += "
" user << browse(t1, "window=stack") onclose(user, "stack") return /obj/item/stack/proc/produce_recipe(datum/stack_recipe/recipe, var/quantity, mob/user) var/required = quantity*recipe.req_amount var/produced = min(quantity*recipe.res_amount, recipe.max_res_amount) if (!can_use(required)) if (produced>1) user << "\red You haven't got enough [src] to build \the [produced] [recipe.title]\s!" else user << "\red You haven't got enough [src] to build \the [recipe.title]!" return if (recipe.one_per_turf && (locate(recipe.result_type) in user.loc)) user << "\red There is another [recipe.title] here!" return if (recipe.on_floor && !isfloor(user.loc)) user << "\red \The [recipe.title] must be constructed on the floor!" return if (recipe.time) user << "\blue Building [recipe.title] ..." if (!do_after(user, recipe.time)) return if (use(required)) var/atom/O if(recipe.use_material) O = new recipe.result_type(user.loc, recipe.use_material) else O = new recipe.result_type(user.loc) O.set_dir(user.dir) O.add_fingerprint(user) if (istype(O, /obj/item/stack)) var/obj/item/stack/S = O S.amount = produced if (istype(O, /obj/item/weapon/storage)) //BubbleWrap - so newly formed boxes are empty for (var/obj/item/I in O) qdel(I) /obj/item/stack/Topic(href, href_list) ..() if ((usr.restrained() || usr.stat || usr.get_active_hand() != src)) return if (href_list["sublist"] && !href_list["make"]) list_recipes(usr, text2num(href_list["sublist"])) if (href_list["make"]) if (src.get_amount() < 1) qdel(src) //Never should happen var/list/recipes_list = recipes if (href_list["sublist"]) var/datum/stack_recipe_list/srl = recipes_list[text2num(href_list["sublist"])] recipes_list = srl.recipes var/datum/stack_recipe/R = recipes_list[text2num(href_list["make"])] var/multiplier = text2num(href_list["multiplier"]) if (!multiplier || (multiplier <= 0)) //href exploit protection return src.produce_recipe(R, multiplier, usr) if (src && usr.machine==src) //do not reopen closed window spawn( 0 ) src.interact(usr) return return //Return 1 if an immediate subsequent call to use() would succeed. //Ensures that code dealing with stacks uses the same logic /obj/item/stack/proc/can_use(var/used) if (get_amount() < used) return 0 return 1 /obj/item/stack/proc/use(var/used) if (!can_use(used)) return 0 if(!uses_charge) amount -= used if (amount <= 0) spawn(0) //delete the empty stack once the current context yields if (amount <= 0) //check again in case someone transferred stuff to us if(usr) usr.remove_from_mob(src) qdel(src) return 1 else if(get_amount() < used) return 0 for(var/i = 1 to uses_charge) var/datum/matter_synth/S = synths[i] S.use_charge(charge_costs[i] * used) // Doesn't need to be deleted return 1 return 0 /obj/item/stack/proc/add(var/extra) if(!uses_charge) if(amount + extra > get_max_amount()) return 0 else amount += extra return 1 else if(!synths || synths.len < uses_charge) return 0 else for(var/i = 1 to uses_charge) var/datum/matter_synth/S = synths[i] S.add_charge(charge_costs[i] * extra) /* The transfer and split procs work differently than use() and add(). Whereas those procs take no action if the desired amount cannot be added or removed these procs will try to transfer whatever they can. They also remove an equal amount from the source stack. */ //attempts to transfer amount to S, and returns the amount actually transferred /obj/item/stack/proc/transfer_to(obj/item/stack/S, var/tamount=null, var/type_verified) if (!get_amount()) return 0 if ((stacktype != S.stacktype) && !type_verified) return 0 if (isnull(tamount)) tamount = src.get_amount() var/transfer = max(min(tamount, src.get_amount(), (S.get_max_amount() - S.get_amount())), 0) var/orig_amount = src.get_amount() if (transfer && src.use(transfer)) S.add(transfer) if (prob(transfer/orig_amount * 100)) transfer_fingerprints_to(S) if(blood_DNA) S.blood_DNA |= blood_DNA return transfer return 0 //creates a new stack with the specified amount /obj/item/stack/proc/split(var/tamount) if (!amount) return null if(uses_charge) return null var/transfer = max(min(tamount, src.amount, initial(max_amount)), 0) var/orig_amount = src.amount if (transfer && src.use(transfer)) var/obj/item/stack/newstack = new src.type(loc, transfer) newstack.color = color if (prob(transfer/orig_amount * 100)) transfer_fingerprints_to(newstack) if(blood_DNA) newstack.blood_DNA |= blood_DNA return newstack return null /obj/item/stack/proc/get_amount() if(uses_charge) if(!synths || synths.len < uses_charge) return 0 var/datum/matter_synth/S = synths[1] . = round(S.get_charge() / charge_costs[1]) if(uses_charge > 1) for(var/i = 2 to uses_charge) S = synths[i] . = min(., round(S.get_charge() / charge_costs[i])) return return amount /obj/item/stack/proc/get_max_amount() if(uses_charge) if(!synths || synths.len < uses_charge) return 0 var/datum/matter_synth/S = synths[1] . = round(S.max_energy / charge_costs[1]) if(uses_charge > 1) for(var/i = 2 to uses_charge) S = synths[i] . = min(., round(S.max_energy / charge_costs[i])) return return max_amount /obj/item/stack/proc/add_to_stacks(mob/usr as mob) for (var/obj/item/stack/item in usr.loc) if (item==src) continue var/transfer = src.transfer_to(item) if (transfer) usr << "You add a new [item.singular_name] to the stack. It now contains [item.amount] [item.singular_name]\s." if(!amount) break /obj/item/stack/attack_hand(mob/user as mob) if (user.get_inactive_hand() == src) var/obj/item/stack/F = src.split(1) if (F) user.put_in_hands(F) src.add_fingerprint(user) F.add_fingerprint(user) spawn(0) if (src && usr.machine==src) src.interact(usr) else ..() return /obj/item/stack/attackby(obj/item/W as obj, mob/user as mob) if (istype(W, /obj/item/stack)) var/obj/item/stack/S = W if (user.get_inactive_hand()==src) src.transfer_to(S, 1) else src.transfer_to(S) spawn(0) //give the stacks a chance to delete themselves if necessary if (S && usr.machine==S) S.interact(usr) if (src && usr.machine==src) src.interact(usr) else return ..() /* * Recipe datum */ /datum/stack_recipe var/title = "ERROR" var/result_type var/req_amount = 1 //amount of material needed for this recipe var/res_amount = 1 //amount of stuff that is produced in one batch (e.g. 4 for floor tiles) var/max_res_amount = 1 var/time = 0 var/one_per_turf = 0 var/on_floor = 0 var/use_material New(title, result_type, req_amount = 1, res_amount = 1, max_res_amount = 1, time = 0, one_per_turf = 0, on_floor = 0, supplied_material = null) src.title = title src.result_type = result_type src.req_amount = req_amount src.res_amount = res_amount src.max_res_amount = max_res_amount src.time = time src.one_per_turf = one_per_turf src.on_floor = on_floor src.use_material = supplied_material /* * Recipe list datum */ /datum/stack_recipe_list var/title = "ERROR" var/list/recipes = null var/req_amount = 1 New(title, recipes, req_amount = 1) src.title = title src.recipes = recipes src.req_amount = req_amount