diff --git a/code/__HELPERS/turfs.dm b/code/__HELPERS/turfs.dm
index 3e26d117b2..b9b4225a41 100644
--- a/code/__HELPERS/turfs.dm
+++ b/code/__HELPERS/turfs.dm
@@ -7,4 +7,7 @@
return mloc
/proc/iswall(turf/T)
- return (istype(T, /turf/simulated/wall) || istype(T, /turf/unsimulated/wall) || istype(T, /turf/simulated/shuttle/wall))
\ No newline at end of file
+ return (istype(T, /turf/simulated/wall) || istype(T, /turf/unsimulated/wall) || istype(T, /turf/simulated/shuttle/wall))
+
+/proc/isfloor(turf/T)
+ return (istype(T, /turf/simulated/floor) || istype(T, /turf/unsimulated/floor) || istype(T, /turf/simulated/shuttle/floor))
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index 49252eaac6..866e2c1b9f 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -855,7 +855,7 @@ About the new airlock wires panel:
return
src.add_fingerprint(user)
- if((istype(C, /obj/item/weapon/weldingtool) && !( src.operating > 0 ) && src.density))
+ if(!repairing && (istype(C, /obj/item/weapon/weldingtool) && !( src.operating > 0 ) && src.density))
var/obj/item/weapon/weldingtool/W = C
if(W.remove_fuel(0,user))
if(!src.welded)
@@ -869,7 +869,7 @@ About the new airlock wires panel:
else if(istype(C, /obj/item/weapon/screwdriver))
if (src.p_open)
if (stat & BROKEN)
- usr << "The airlock control panel is too damaged to be closed!"
+ usr << "The panel is broken and cannot be closed."
else
src.p_open = 0
else
@@ -884,7 +884,7 @@ About the new airlock wires panel:
else if(istype(C, /obj/item/weapon/pai_cable)) // -- TLE
var/obj/item/weapon/pai_cable/cable = C
cable.plugin(src, user)
- else if(istype(C, /obj/item/weapon/crowbar))
+ else if(!repairing && istype(C, /obj/item/weapon/crowbar))
var/beingcrowbarred = null
if(istype(C, /obj/item/weapon/crowbar) )
beingcrowbarred = 1 //derp, Agouri
@@ -1121,7 +1121,7 @@ About the new airlock wires panel:
update_icon()
/obj/machinery/door/airlock/proc/hasPower()
- return ((src.secondsMainPowerLost==0 || src.secondsBackupPowerLost==0) && !(stat & NOPOWER))
+ return ((src.secondsMainPowerLost==0 || src.secondsBackupPowerLost==0) && !(stat & NOPOWER|BROKEN))
/obj/machinery/door/airlock/proc/prison_open()
src.unlock()
diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm
index c3ab77d74a..306af92f37 100644
--- a/code/game/machinery/doors/door.dm
+++ b/code/game/machinery/doors/door.dm
@@ -2,6 +2,8 @@
#define DOOR_OPEN_LAYER 2.7 //Under all objects if opened. 2.7 due to tables being at 2.6
#define DOOR_CLOSED_LAYER 3.1 //Above most items if closed
+#define DOOR_REPAIR_AMOUNT 50 //amount of health regained per stack amount used
+
/obj/machinery/door
name = "Door"
desc = "It opens and closes."
@@ -29,6 +31,7 @@
var/emitter_resistance = 10 // Amount of emitter hits doors whistand
var/min_force = 10 //minimum amount of force needed to damage the door with a melee weapon
var/hitsound = 'sound/weapons/smash.ogg' //sound door makes when hit with a weapon
+ var/obj/item/stack/sheet/metal/repairing
//Multi-tile doors
dir = EAST
@@ -162,7 +165,6 @@
tforce = AM:throwforce
playsound(src.loc, hitsound, 100, 1)
take_damage(tforce)
- //..() //Does this really need to be here twice? The parent proc doesn't even do anything yet. - Nodrak
return
/obj/machinery/door/attack_ai(mob/user as mob)
@@ -185,27 +187,69 @@
user = null
if(!src.requiresID())
user = null
+
if(istype(I, /obj/item/stack/sheet/metal))
if(stat & BROKEN)
- user << "\blue [src.name] is damaged beyond repair and must be reconstructed!"
+ user << "It looks like \the [src] is pretty busted. It's going to need more than just patching up now."
return
if(health >= maxhealth)
- user << "\blue Nothing to fix!"
+ user << "Nothing to fix!"
return
+ if(!density)
+ user << "\The [src] must be closed before you can repair it."
+ return
+
+ //figure out how much metal we need
+ var/amount_needed = (maxhealth - health) / DOOR_REPAIR_AMOUNT
+ amount_needed = (round(amount_needed) == amount_needed)? amount_needed : round(amount_needed) + 1 //Why does BYOND not have a ceiling proc?
+
var/obj/item/stack/sheet/metal/metalstack = I
- var/health_per_sheet = 50
- var/initialhealth = health
- src.health = min(maxhealth, health + 100, health + (metalstack.amount * health_per_sheet))
- user.visible_message("\The [user] patches some dents on \the [src] with \the [metalstack].")
- metalstack.use(round((health - initialhealth)/health_per_sheet))
+ var/transfer
+ if (repairing)
+ transfer = metalstack.transfer_to(repairing, amount_needed - repairing.amount)
+ if (!transfer)
+ user << "You must weld or remove \the [repairing] from \the [src] before you can add anything else."
+ else
+ repairing = metalstack.split(amount_needed)
+ if (repairing)
+ repairing.loc = src
+ transfer = repairing.amount
+
+ if (transfer)
+ user << "You fit [transfer] [metalstack.singular_name]\s to damaged and broken parts on \the [src]."
+
return
- if(src.density && ((operable() && istype(I, /obj/item/weapon/card/emag)) || istype(I, /obj/item/weapon/melee/energy/blade)))
+ if(repairing && istype(I, /obj/item/weapon/weldingtool))
+ if(!density)
+ user << "\The [src] must be closed before you can repair it."
+ return
+
+ var/obj/item/weapon/weldingtool/welder = I
+ if(welder.remove_fuel(0,user))
+ user << "You start to fix dents and weld \the [repairing] into place."
+ playsound(src, 'sound/items/Welder.ogg', 100, 1)
+ if(do_after(user, 5 * repairing.amount) && welder && welder.isOn())
+ user << "You finish repairing the damage to \the [src]."
+ health = between(health, health + repairing.amount*DOOR_REPAIR_AMOUNT, maxhealth)
+ update_icon()
+ del(repairing)
+ return
+
+ if(repairing && istype(I, /obj/item/weapon/crowbar))
+ user << "You remove \the [repairing]."
+ playsound(src.loc, 'sound/items/Crowbar.ogg', 100, 1)
+ repairing.loc = user.loc
+ repairing = null
+ return
+
+ if(src.density && (operable() && istype(I, /obj/item/weapon/card/emag)))
flick("door_spark", src)
sleep(6)
open()
operating = -1
return 1
+
//psa to whoever coded this, there are plenty of objects that need to call attack() on doors without bludgeoning them.
if(src.density && istype(I, /obj/item/weapon) && user.a_intent == "hurt" && !istype(I, /obj/item/weapon/card))
var/obj/item/weapon/W = I
@@ -213,16 +257,18 @@
if(W.force < min_force)
user.visible_message("\red \The [user] hits \the [src] with \the [W] with no visible effect." )
else
- user.visible_message("\red \The [user] forcefully slams \the [src] with \the [W]!" )
+ user.visible_message("\red \The [user] forcefully strikes \the [src] with \the [W]!" )
playsound(src.loc, hitsound, 100, 1)
take_damage(W.force)
return
+
if(src.allowed(user) && operable())
if(src.density)
open()
else
close()
return
+
if(src.density && !(stat & (NOPOWER|BROKEN)))
flick("door_deny", src)
return
diff --git a/code/game/objects/items/stacks/sheets/glass.dm b/code/game/objects/items/stacks/sheets/glass.dm
index ee0aa22a70..71fb87f9ad 100644
--- a/code/game/objects/items/stacks/sheets/glass.dm
+++ b/code/game/objects/items/stacks/sheets/glass.dm
@@ -28,6 +28,7 @@
icon_state = "sheet-glass"
matter = null
created_window = /obj/structure/window/basic
+ stacktype = /obj/item/stack/sheet/glass
/obj/item/stack/sheet/glass/attack_self(mob/user as mob)
construct_window(user)
diff --git a/code/game/objects/items/stacks/sheets/mineral.dm b/code/game/objects/items/stacks/sheets/mineral.dm
index f3b8face4e..fccce08dc2 100644
--- a/code/game/objects/items/stacks/sheets/mineral.dm
+++ b/code/game/objects/items/stacks/sheets/mineral.dm
@@ -140,6 +140,7 @@ obj/item/stack/sheet/mineral/iron/New()
name = "plastic sheets"
icon_state = "sheet-plastic"
perunit = 2000
+ stacktype = /obj/item/stack/sheet/mineral/plastic
/obj/item/stack/sheet/mineral/gold
name = "gold"
diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm
index 3a3fe552fc..a5336c0797 100644
--- a/code/game/objects/items/stacks/sheets/sheet_types.dm
+++ b/code/game/objects/items/stacks/sheets/sheet_types.dm
@@ -92,6 +92,7 @@ var/global/list/datum/stack_recipe/metal_recipes = list ( \
icon_state = "sheet-metal"
throwforce = 14.0
flags = FPRINT | TABLEPASS | CONDUCT
+ stacktype = /obj/item/stack/sheet/metal
/obj/item/stack/sheet/metal/New(var/loc, var/amount=null)
recipes = metal_recipes
@@ -152,6 +153,7 @@ var/global/list/datum/stack_recipe/wood_recipes = list ( \
desc = "One can only guess that this is a bunch of wood."
singular_name = "wood plank"
icon_state = "sheet-wood"
+ stacktype = /obj/item/stack/sheet/wood
/obj/item/stack/sheet/wood/New(var/loc, var/amount=null)
recipes = wood_recipes
diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm
index e14c7f2b1e..2ab4f72bd2 100644
--- a/code/game/objects/items/stacks/stack.dm
+++ b/code/game/objects/items/stacks/stack.dm
@@ -16,9 +16,12 @@
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
/obj/item/stack/New(var/loc, var/amount=null)
..()
+ if (!stacktype)
+ stacktype = type
if (amount)
src.amount = amount
return
@@ -93,6 +96,43 @@
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 = 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)
+ del(I)
+
/obj/item/stack/Topic(href, href_list)
..()
if ((usr.restrained() || usr.stat || usr.get_active_hand() != src))
@@ -108,64 +148,37 @@
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
- if (src.amount < R.req_amount*multiplier)
- if (R.req_amount*multiplier>1)
- usr << "\red You haven't got enough [src] to build \the [R.req_amount*multiplier] [R.title]\s!"
- else
- usr << "\red You haven't got enough [src] to build \the [R.title]!"
- return
- if (R.one_per_turf && (locate(R.result_type) in usr.loc))
- usr << "\red There is another [R.title] here!"
- return
- if (R.on_floor && !istype(usr.loc, /turf/simulated/floor))
- usr << "\red \The [R.title] must be constructed on the floor!"
- return
- if (R.time)
- usr << "\blue Building [R.title] ..."
- if (!do_after(usr, R.time))
- return
- if (src.amount < R.req_amount*multiplier)
- return
- var/atom/O = new R.result_type( usr.loc )
- O.set_dir(usr.dir)
- if (R.max_res_amount>1)
- var/obj/item/stack/new_item = O
- new_item.amount = R.res_amount*multiplier
- //new_item.add_to_stacks(usr)
- src.amount-=R.req_amount*multiplier
- if (src.amount<=0)
- var/oldsrc = src
- src = null //dont kill proc after del()
- usr.before_take_item(oldsrc)
- del(oldsrc)
- if (istype(O,/obj/item) && istype(usr,/mob/living/carbon))
- usr.put_in_hands(O)
- O.add_fingerprint(usr)
- //BubbleWrap - so newly formed boxes are empty
- if ( istype(O, /obj/item/weapon/storage) )
- for (var/obj/item/I in O)
- del(I)
- //BubbleWrap END
+
+ src.produce_recipe(R, multiplier, usr)
+
if (src && usr.machine==src) //do not reopen closed window
spawn( 0 )
src.interact(usr)
return
return
-/obj/item/stack/proc/use(var/used)
+//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 (amount < used)
return 0
+ return 1
+
+/obj/item/stack/proc/use(var/used)
+ if (!can_use(used))
+ return 0
amount -= used
if (amount <= 0)
- var/oldsrc = src
- src = null //dont kill proc after del()
- if(usr)
- usr.before_take_item(oldsrc)
- del(oldsrc)
+ 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.before_take_item(src)
+ del(src)
return 1
/obj/item/stack/proc/add(var/extra)
@@ -175,62 +188,95 @@
amount += extra
return 1
+/*
+ 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)
+ if (!amount)
+ return 0
+ if (stacktype != S.stacktype)
+ return 0
+ if (isnull(tamount))
+ tamount = src.amount
+
+ var/transfer = max(min(tamount, src.amount, (S.max_amount - S.amount)), 0)
+
+ var/orig_amount = src.amount
+ if (transfer && src.use(transfer))
+ S.add(transfer)
+ if (prob(transfer/orig_amount * 100))
+ S.copy_evidences(src)
+ return transfer
+ return 0
+
+//creates a new stack with the specified amount
+/obj/item/stack/proc/split(var/tamount)
+ if (!amount)
+ 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)
+ if (prob(transfer/orig_amount * 100))
+ newstack.copy_evidences(src)
+ return newstack
+ return null
+
/obj/item/stack/proc/get_amount()
return amount
/obj/item/stack/proc/add_to_stacks(mob/usr as mob)
- var/obj/item/stack/oldsrc = src
- src = null
for (var/obj/item/stack/item in usr.loc)
- if (item==oldsrc)
+ if (item==src)
continue
- if (!istype(item, oldsrc.type))
- continue
- if (item.amount>=item.max_amount)
- continue
- oldsrc.attackby(item, usr)
- usr << "You add new [item.singular_name] to the stack. It now contains [item.amount] [item.singular_name]\s."
- if(!oldsrc)
+ 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 = new src.type(user, 1)
- F.copy_evidences(src)
- user.put_in_hands(F)
- src.add_fingerprint(user)
- F.add_fingerprint(user)
- use(1)
- if (src && usr.machine==src)
- spawn(0) src.interact(usr)
+ 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, src.type))
+ if (istype(W, /obj/item/stack))
var/obj/item/stack/S = W
- if (S.amount >= max_amount)
- return 1
- var/to_transfer as num
+
if (user.get_inactive_hand()==src)
- to_transfer = 1
+ src.transfer_to(S, 1)
else
- to_transfer = min(src.amount, S.max_amount-S.amount)
- S.add(to_transfer)
- if (S && usr.machine==S)
- spawn(0) S.interact(usr)
- src.use(to_transfer)
- if (src && usr.machine==src)
- spawn(0) src.interact(usr)
+ 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 ..()
/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
+ src.blood_DNA |= from.blood_DNA
+ src.fingerprints |= from.fingerprints
+ src.fingerprintshidden |= from.fingerprintshidden
+ src.fingerprintslast = from.fingerprintslast
//TODO bloody overlay
/*
@@ -239,8 +285,8 @@
/datum/stack_recipe
var/title = "ERROR"
var/result_type
- var/req_amount = 1
- var/res_amount = 1
+ 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