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