/* Stack type objects!
* Contains:
* Stacks
* Recipe datum
*/
/*
* Stacks
*/
/obj/item/stack
gender = PLURAL
origin_tech = Tc_MATERIALS + "=1"
icon = 'icons/obj/stacks_sheets.dmi'
var/list/datum/stack_recipe/recipes
var/singular_name
var/irregular_plural //"Teeth", for example. Without this, you'd see "There are 30 tooths in the stack."
var/amount = 1
var/perunit = 3750
var/max_amount //also see stack recipes initialisation, param "max_res_amount" must be equal to this max_amount
var/redeemed = 0 // For selling minerals to central command via supply shuttle.
var/restock_amount = 0 //For borg chargers restocking.
var/sheettype = null //this is used for girders in the creation of walls/false walls. Used by both tiles and sheets.
var/last_work = 0 //rounded last world.time at which a crafting began
/obj/item/stack/New(var/loc, var/amount=null)
..()
if (amount)
src.amount=amount
update_materials()
update_icon()
//forceMove(loc) // So that Crossed gets called, so that stacks can be merged
initial_thermal_mass = thermal_mass
thermal_mass = initial_thermal_mass * src.amount
/obj/item/stack/Destroy()
if (usr && usr.machine==src)
usr << browse(null, "window=stack")
..()
/obj/item/stack/examine(mob/user)
..()
var/be = "are"
if(amount == 1)
be = "is"
to_chat(user, "There [be] [src.amount] [correct_name()] in the stack.")
/obj/item/stack/proc/correct_name()
return "[irregular_plural && amount > 1 ? irregular_plural : "[singular_name]"][amount == 1 || irregular_plural ? "" : "s"]"
/obj/item/stack/attack_self(mob/user as mob)
list_recipes(user)
/obj/item/stack/proc/list_recipes(mob/user as mob, recipes_sublist)
ASSERT(isnum(amount))
if(!recipes)
return
if(!src || 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 = "
[name] recipesAmount of [name] available: [src.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(istext(E))
t1 += "[E]"
if(istype(E, /datum/stack_recipe_list))
var/datum/stack_recipe_list/srl = E
var/stack_name = (irregular_plural && srl.req_amount > 1) ? irregular_plural : "[singular_name]\s"
if(src.amount >= srl.req_amount)
t1 += "[srl.title] ([srl.req_amount] [stack_name])"
else
t1 += "[srl.title] ([srl.req_amount] [stack_name]\s)
"
if(istype(E, /datum/stack_recipe))
var/datum/stack_recipe/R = E
var/turf/T = get_turf(src)
if(!T || (R.z_up_required && !HasAbove(T.z)) || (R.z_down_required && !HasBelow(T.z)))
continue
var/max_multiplier = round(src.amount / R.req_amount)
var/title
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] [correct_name()])"
if(R.other_reqs.len)
for(var/ii = 1 to R.other_reqs.len)
can_build = 0
var/obj/looking_for = R.other_reqs[ii]
var/req_amount
if(ispath(looking_for, /obj/item/stack))
var/obj/item/stack/S = new looking_for
req_amount = R.other_reqs[looking_for]
title += ", [req_amount] [S.correct_name()]"
else
title += ", [initial(looking_for.name)] required in vicinity"
if(ispath(user.get_inactive_hand(), looking_for))
if(req_amount)
var/obj/item/stack/S = user.get_inactive_hand()
if(S.amount >= req_amount)
can_build = 1
continue
if(!can_build)
for(var/obj/I in range(get_turf(src),1))
if(ispath(looking_for, I))
if(req_amount) //It's of a stack/sheet subtype
var/obj/item/stack/S = I
if(S.amount >= req_amount)
can_build = 1
continue
else
can_build = 1
continue
break
if(can_build)
t1 += "[title]"
else
t1 += "[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 += extra_message()
t1 += ""
user << browse(t1, "window=stack")
onclose(user, "stack")
return
/obj/item/stack/proc/extra_message()
return null
/obj/item/stack/proc/time_modifier(var/_time)
return _time
/obj/item/stack/proc/loc_override()
return null
/obj/item/stack/proc/allow_use(var/mob/living/user)
return (user.get_active_hand() == src)
/obj/item/stack/proc/stop_build(var/_last_crafting = FALSE)
return
/obj/item/stack/useThermalMass(var/used_mass)
..()
var/used_amount = round(initial_thermal_mass * amount - thermal_mass)
use(used_amount)
/obj/item/stack/Topic(href, href_list)
..()
if ((usr.restrained() || usr.stat || !allow_use(usr)))
return
if (href_list["sublist"] && !href_list["make"])
list_recipes(usr, text2num(href_list["sublist"]))
if (href_list["make"])
if (src.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"])
R.build(usr, src, multiplier, loc_override())
if (src && usr.machine==src) //do not reopen closed window
spawn( 0 )
src.interact(usr)
return
return
/obj/item/stack/proc/use(var/amount)
ASSERT(isnum(src.amount))
if (src.amount <= 0)
qdel(src) // We don't have anything left
return
if(src.amount>=amount)
var/thermal_mass_used = thermal_mass/src.amount
src.amount-=amount
thermal_mass -= thermal_mass_used
update_materials()
else
return 0
. = 1
if (src.amount<=0) //If the stack is empty after removing the required amount of items!
on_empty()
spawn()
qdel(src)
/obj/item/stack/proc/on_empty()
if(usr)
if(istype(usr,/mob/living/silicon/robot))
var/mob/living/silicon/robot/R=usr
if(R.module)
R.module.modules -= src
if(R.module_active == src)
R.module_active = null
if(R.module_state_1 == src)
R.uneq_module(R.module_state_1)
R.module_state_1 = null
R.inv1.icon_state = "inv1"
else if(R.module_state_2 == src)
R.uneq_module(R.module_state_2)
R.module_state_2 = null
R.inv2.icon_state = "inv2"
else if(R.module_state_3 == src)
R.uneq_module(R.module_state_3)
R.module_state_3 = null
R.inv3.icon_state = "inv3"
usr.before_take_item(src)
/obj/item/stack/proc/add(var/amount)
src.amount += amount
if(thermal_mass)
thermal_mass += initial_thermal_mass * amount
update_materials()
/obj/item/stack/proc/set_amount(new_amount)
amount = new_amount
update_materials()
/obj/item/stack/proc/merge(obj/item/stack/S) //Merge src into S, as much as possible
if(src == S) //We need to check this because items can cross themselves for some fucked up reason
return
if(!can_stack_with(S))
return
var/transfer = min(amount, S.max_amount - S.amount)
if(transfer <= 0)
return
if(pulledby)
pulledby.start_pulling(S)
S.copy_evidences(src)
S.transfer_data_from(src,transfer)
update_icon()
S.update_icon()
use(transfer)
S.add(transfer)
/obj/item/stack/proc/update_materials()
if(amount && starting_materials)
for(var/matID in starting_materials)
materials.storage[matID] = max(0, starting_materials[matID]*amount)
if(amount < 2)
gender = NEUTER
else
gender = PLURAL
/obj/item/stack/proc/can_stack_with(obj/item/other_stack)
if(ispath(other_stack))
return (src.type == other_stack)
return (src.type == other_stack.type)
/obj/item/stack/attack_hand(mob/user as mob)
if (user.get_inactive_hand() == src)
var/obj/item/stack/F = new src.type( user, amount=1)
F.copy_evidences(src)
F.material_type = material_type
user.put_in_hands(F)
src.add_fingerprint(user)
F.add_fingerprint(user)
F.transfer_data_from(src,1)
use(1)
update_icon()
F.update_icon()
if (src && usr.machine==src)
spawn(0) src.interact(usr)
else
..()
return
/obj/item/stack/proc/transfer_data_from(var/obj/item/stack/S, var/amount)
return
/obj/item/stack/preattack(atom/target, mob/user, proximity_flag, click_parameters)
if (!proximity_flag)
return 0
if (can_stack_with(target))
var/obj/item/stack/S = target
if (amount >= max_amount)
to_chat(user, "\The [src] cannot hold anymore [correct_name()].")
return 1
var/to_transfer
if (user.get_inactive_hand()==S)
to_transfer = 1
else
to_transfer = min(S.amount, max_amount-amount)
add(to_transfer)
transfer_data_from(S,to_transfer)
to_chat(user, "You add [to_transfer] [((to_transfer > 1) && S.irregular_plural) ? S.irregular_plural : "[S.singular_name]\s"] to \the [src]. It now contains [amount] [correct_name()].")
if (S && user.machine==S)
spawn(0) interact(user)
S.use(to_transfer)
if (src && user.machine==src)
spawn(0) src.interact(user)
update_icon()
S.update_icon()
return 1
return ..()
//Ported from -tg-station/#10973, credit to MrPerson
/obj/item/stack/Crossed(obj/o)
if(src != o && istype(o, src.type) && !o.throwing)
merge(o)
return ..()
/obj/item/stack/hitby(atom/movable/AM) //Doesn't seem to ever be called since stacks are not dense but whatever
. = ..()
if(.)
return
if(src != AM && istype(AM, src.type))
merge(AM)
/obj/item/stack/proc/copy_evidences(obj/item/stack/from as obj)
src.blood_DNA = from.blood_DNA
src.fingerprints = from.fingerprints
src.fingerprintshidden = from.fingerprintshidden
src.fingerprintslast = from.fingerprintslast
//TODO bloody overlay
/*
drop_stack() helper proc
Arguments:
- new_stack_type = type of stack to spawn (for example /obj/item/stack/tile/light)
- loc = where to spawn the stack
- add_amount = how much items to create in the stack
- user = non-essential, whom to send the messages to
This proc sees if there are any stacks of the same type in *loc. If there are, and it's possible to add *amount items to them,
add *amount items to them and return.
If unable to add to any already existing stack, create a new instance of *new_stack_type
Returns stack
*/
/proc/drop_stack(new_stack_type = /obj/item/stack, atom/loc, add_amount = 1, mob/user)
if(!ispath(new_stack_type, /obj/item/stack))
return new new_stack_type(loc)
for(var/obj/item/stack/S in loc)
if(S.can_stack_with(new_stack_type))
if(S.max_amount >= S.amount + add_amount)
S.add(add_amount)
if(user)
to_chat(user, "You add [add_amount] item\s to the stack. It now contains [S.amount] [S.correct_name()].")
return S
var/obj/item/stack/S = new_stack_type
for(var/i = 0 to round(add_amount/initial(S.max_amount)))
if (add_amount <= 0)
continue
S = new new_stack_type(loc)
S.amount = min(add_amount, S.max_amount)
add_amount -= S.amount
S.update_materials()
S.update_icon()
return S
/obj/item/stack/verb_pickup(mob/living/user)
var/obj/item/I = user.get_active_hand()
if(I && can_stack_with(I))
I.preattack(src, user, 1)
return
return ..()
/obj/item/stack/restock()
if(!restock_amount)
return //Do not restock this stack type
if(amount < max_amount)
amount += restock_amount
if(amount > max_amount)
amount = max_amount