Merge pull request #7534 from mwerezak/doors

Doors and stacks
This commit is contained in:
Zuhayr
2014-12-28 13:14:23 +10:30
7 changed files with 194 additions and 95 deletions

View File

@@ -7,4 +7,7 @@
return mloc return mloc
/proc/iswall(turf/T) /proc/iswall(turf/T)
return (istype(T, /turf/simulated/wall) || istype(T, /turf/unsimulated/wall) || istype(T, /turf/simulated/shuttle/wall)) 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))

View File

@@ -855,7 +855,7 @@ About the new airlock wires panel:
return return
src.add_fingerprint(user) 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 var/obj/item/weapon/weldingtool/W = C
if(W.remove_fuel(0,user)) if(W.remove_fuel(0,user))
if(!src.welded) if(!src.welded)
@@ -869,7 +869,7 @@ About the new airlock wires panel:
else if(istype(C, /obj/item/weapon/screwdriver)) else if(istype(C, /obj/item/weapon/screwdriver))
if (src.p_open) if (src.p_open)
if (stat & BROKEN) if (stat & BROKEN)
usr << "The airlock control panel is too damaged to be closed!" usr << "<span class='warning'>The panel is broken and cannot be closed.</span>"
else else
src.p_open = 0 src.p_open = 0
else else
@@ -884,7 +884,7 @@ About the new airlock wires panel:
else if(istype(C, /obj/item/weapon/pai_cable)) // -- TLE else if(istype(C, /obj/item/weapon/pai_cable)) // -- TLE
var/obj/item/weapon/pai_cable/cable = C var/obj/item/weapon/pai_cable/cable = C
cable.plugin(src, user) cable.plugin(src, user)
else if(istype(C, /obj/item/weapon/crowbar)) else if(!repairing && istype(C, /obj/item/weapon/crowbar))
var/beingcrowbarred = null var/beingcrowbarred = null
if(istype(C, /obj/item/weapon/crowbar) ) if(istype(C, /obj/item/weapon/crowbar) )
beingcrowbarred = 1 //derp, Agouri beingcrowbarred = 1 //derp, Agouri
@@ -1121,7 +1121,7 @@ About the new airlock wires panel:
update_icon() update_icon()
/obj/machinery/door/airlock/proc/hasPower() /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() /obj/machinery/door/airlock/proc/prison_open()
src.unlock() src.unlock()

View File

@@ -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_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_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 /obj/machinery/door
name = "Door" name = "Door"
desc = "It opens and closes." desc = "It opens and closes."
@@ -29,6 +31,7 @@
var/emitter_resistance = 10 // Amount of emitter hits doors whistand 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/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/hitsound = 'sound/weapons/smash.ogg' //sound door makes when hit with a weapon
var/obj/item/stack/sheet/metal/repairing
//Multi-tile doors //Multi-tile doors
dir = EAST dir = EAST
@@ -162,7 +165,6 @@
tforce = AM:throwforce tforce = AM:throwforce
playsound(src.loc, hitsound, 100, 1) playsound(src.loc, hitsound, 100, 1)
take_damage(tforce) take_damage(tforce)
//..() //Does this really need to be here twice? The parent proc doesn't even do anything yet. - Nodrak
return return
/obj/machinery/door/attack_ai(mob/user as mob) /obj/machinery/door/attack_ai(mob/user as mob)
@@ -185,27 +187,69 @@
user = null user = null
if(!src.requiresID()) if(!src.requiresID())
user = null user = null
if(istype(I, /obj/item/stack/sheet/metal)) if(istype(I, /obj/item/stack/sheet/metal))
if(stat & BROKEN) if(stat & BROKEN)
user << "\blue [src.name] is damaged beyond repair and must be reconstructed!" user << "<span class='notice'>It looks like \the [src] is pretty busted. It's going to need more than just patching up now.</span>"
return return
if(health >= maxhealth) if(health >= maxhealth)
user << "\blue Nothing to fix!" user << "<span class='notice'>Nothing to fix!</span>"
return return
if(!density)
user << "<span class='warning'>\The [src] must be closed before you can repair it.</span>"
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/obj/item/stack/sheet/metal/metalstack = I
var/health_per_sheet = 50 var/transfer
var/initialhealth = health if (repairing)
src.health = min(maxhealth, health + 100, health + (metalstack.amount * health_per_sheet)) transfer = metalstack.transfer_to(repairing, amount_needed - repairing.amount)
user.visible_message("\The [user] patches some dents on \the [src] with \the [metalstack].") if (!transfer)
metalstack.use(round((health - initialhealth)/health_per_sheet)) user << "<span class='warning'>You must weld or remove \the [repairing] from \the [src] before you can add anything else.</span>"
else
repairing = metalstack.split(amount_needed)
if (repairing)
repairing.loc = src
transfer = repairing.amount
if (transfer)
user << "<span class='notice'>You fit [transfer] [metalstack.singular_name]\s to damaged and broken parts on \the [src].</span>"
return 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 << "<span class='warning'>\The [src] must be closed before you can repair it.</span>"
return
var/obj/item/weapon/weldingtool/welder = I
if(welder.remove_fuel(0,user))
user << "<span class='notice'>You start to fix dents and weld \the [repairing] into place.</span>"
playsound(src, 'sound/items/Welder.ogg', 100, 1)
if(do_after(user, 5 * repairing.amount) && welder && welder.isOn())
user << "<span class='notice'>You finish repairing the damage to \the [src].</span>"
health = between(health, health + repairing.amount*DOOR_REPAIR_AMOUNT, maxhealth)
update_icon()
del(repairing)
return
if(repairing && istype(I, /obj/item/weapon/crowbar))
user << "<span class='notice'>You remove \the [repairing].</span>"
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) flick("door_spark", src)
sleep(6) sleep(6)
open() open()
operating = -1 operating = -1
return 1 return 1
//psa to whoever coded this, there are plenty of objects that need to call attack() on doors without bludgeoning them. //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)) if(src.density && istype(I, /obj/item/weapon) && user.a_intent == "hurt" && !istype(I, /obj/item/weapon/card))
var/obj/item/weapon/W = I var/obj/item/weapon/W = I
@@ -213,16 +257,18 @@
if(W.force < min_force) if(W.force < min_force)
user.visible_message("\red <B>\The [user] hits \the [src] with \the [W] with no visible effect.</B>" ) user.visible_message("\red <B>\The [user] hits \the [src] with \the [W] with no visible effect.</B>" )
else else
user.visible_message("\red <B>\The [user] forcefully slams \the [src] with \the [W]!</B>" ) user.visible_message("\red <B>\The [user] forcefully strikes \the [src] with \the [W]!</B>" )
playsound(src.loc, hitsound, 100, 1) playsound(src.loc, hitsound, 100, 1)
take_damage(W.force) take_damage(W.force)
return return
if(src.allowed(user) && operable()) if(src.allowed(user) && operable())
if(src.density) if(src.density)
open() open()
else else
close() close()
return return
if(src.density && !(stat & (NOPOWER|BROKEN))) if(src.density && !(stat & (NOPOWER|BROKEN)))
flick("door_deny", src) flick("door_deny", src)
return return

View File

@@ -28,6 +28,7 @@
icon_state = "sheet-glass" icon_state = "sheet-glass"
matter = null matter = null
created_window = /obj/structure/window/basic created_window = /obj/structure/window/basic
stacktype = /obj/item/stack/sheet/glass
/obj/item/stack/sheet/glass/attack_self(mob/user as mob) /obj/item/stack/sheet/glass/attack_self(mob/user as mob)
construct_window(user) construct_window(user)

View File

@@ -140,6 +140,7 @@ obj/item/stack/sheet/mineral/iron/New()
name = "plastic sheets" name = "plastic sheets"
icon_state = "sheet-plastic" icon_state = "sheet-plastic"
perunit = 2000 perunit = 2000
stacktype = /obj/item/stack/sheet/mineral/plastic
/obj/item/stack/sheet/mineral/gold /obj/item/stack/sheet/mineral/gold
name = "gold" name = "gold"

View File

@@ -92,6 +92,7 @@ var/global/list/datum/stack_recipe/metal_recipes = list ( \
icon_state = "sheet-metal" icon_state = "sheet-metal"
throwforce = 14.0 throwforce = 14.0
flags = FPRINT | TABLEPASS | CONDUCT flags = FPRINT | TABLEPASS | CONDUCT
stacktype = /obj/item/stack/sheet/metal
/obj/item/stack/sheet/metal/New(var/loc, var/amount=null) /obj/item/stack/sheet/metal/New(var/loc, var/amount=null)
recipes = metal_recipes 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." desc = "One can only guess that this is a bunch of wood."
singular_name = "wood plank" singular_name = "wood plank"
icon_state = "sheet-wood" icon_state = "sheet-wood"
stacktype = /obj/item/stack/sheet/wood
/obj/item/stack/sheet/wood/New(var/loc, var/amount=null) /obj/item/stack/sheet/wood/New(var/loc, var/amount=null)
recipes = wood_recipes recipes = wood_recipes

View File

@@ -16,9 +16,12 @@
var/singular_name var/singular_name
var/amount = 1 var/amount = 1
var/max_amount //also see stack recipes initialisation, param "max_res_amount" must be equal to this max_amount 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) /obj/item/stack/New(var/loc, var/amount=null)
..() ..()
if (!stacktype)
stacktype = type
if (amount) if (amount)
src.amount = amount src.amount = amount
return return
@@ -93,6 +96,43 @@
onclose(user, "stack") onclose(user, "stack")
return 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) /obj/item/stack/Topic(href, href_list)
..() ..()
if ((usr.restrained() || usr.stat || usr.get_active_hand() != src)) if ((usr.restrained() || usr.stat || usr.get_active_hand() != src))
@@ -108,64 +148,37 @@
if (href_list["sublist"]) if (href_list["sublist"])
var/datum/stack_recipe_list/srl = recipes_list[text2num(href_list["sublist"])] var/datum/stack_recipe_list/srl = recipes_list[text2num(href_list["sublist"])]
recipes_list = srl.recipes recipes_list = srl.recipes
var/datum/stack_recipe/R = recipes_list[text2num(href_list["make"])] var/datum/stack_recipe/R = recipes_list[text2num(href_list["make"])]
var/multiplier = text2num(href_list["multiplier"]) var/multiplier = text2num(href_list["multiplier"])
if (!multiplier || (multiplier <= 0)) //href exploit protection if (!multiplier || (multiplier <= 0)) //href exploit protection
return return
if (src.amount < R.req_amount*multiplier)
if (R.req_amount*multiplier>1) src.produce_recipe(R, multiplier, usr)
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
if (src && usr.machine==src) //do not reopen closed window if (src && usr.machine==src) //do not reopen closed window
spawn( 0 ) spawn( 0 )
src.interact(usr) src.interact(usr)
return return
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) if (amount < used)
return 0 return 0
return 1
/obj/item/stack/proc/use(var/used)
if (!can_use(used))
return 0
amount -= used amount -= used
if (amount <= 0) if (amount <= 0)
var/oldsrc = src spawn(0) //delete the empty stack once the current context yields
src = null //dont kill proc after del() if (amount <= 0) //check again in case someone transferred stuff to us
if(usr) if(usr)
usr.before_take_item(oldsrc) usr.before_take_item(src)
del(oldsrc) del(src)
return 1 return 1
/obj/item/stack/proc/add(var/extra) /obj/item/stack/proc/add(var/extra)
@@ -175,62 +188,95 @@
amount += extra amount += extra
return 1 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() /obj/item/stack/proc/get_amount()
return amount return amount
/obj/item/stack/proc/add_to_stacks(mob/usr as mob) /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) for (var/obj/item/stack/item in usr.loc)
if (item==oldsrc) if (item==src)
continue continue
if (!istype(item, oldsrc.type)) var/transfer = src.transfer_to(item)
continue if (transfer)
if (item.amount>=item.max_amount) usr << "You add a new [item.singular_name] to the stack. It now contains [item.amount] [item.singular_name]\s."
continue if(!amount)
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)
break break
/obj/item/stack/attack_hand(mob/user as mob) /obj/item/stack/attack_hand(mob/user as mob)
if (user.get_inactive_hand() == src) if (user.get_inactive_hand() == src)
var/obj/item/stack/F = new src.type(user, 1) var/obj/item/stack/F = src.split(1)
F.copy_evidences(src) if (F)
user.put_in_hands(F) user.put_in_hands(F)
src.add_fingerprint(user) src.add_fingerprint(user)
F.add_fingerprint(user) F.add_fingerprint(user)
use(1) spawn(0)
if (src && usr.machine==src) if (src && usr.machine==src)
spawn(0) src.interact(usr) src.interact(usr)
else else
..() ..()
return return
/obj/item/stack/attackby(obj/item/W as obj, mob/user as mob) /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 var/obj/item/stack/S = W
if (S.amount >= max_amount)
return 1
var/to_transfer as num
if (user.get_inactive_hand()==src) if (user.get_inactive_hand()==src)
to_transfer = 1 src.transfer_to(S, 1)
else else
to_transfer = min(src.amount, S.max_amount-S.amount) src.transfer_to(S)
S.add(to_transfer)
if (S && usr.machine==S) spawn(0) //give the stacks a chance to delete themselves if necessary
spawn(0) S.interact(usr) if (S && usr.machine==S)
src.use(to_transfer) S.interact(usr)
if (src && usr.machine==src) if (src && usr.machine==src)
spawn(0) src.interact(usr) src.interact(usr)
else return ..() else return ..()
/obj/item/stack/proc/copy_evidences(obj/item/stack/from as obj) /obj/item/stack/proc/copy_evidences(obj/item/stack/from as obj)
src.blood_DNA = from.blood_DNA src.blood_DNA |= from.blood_DNA
src.fingerprints = from.fingerprints src.fingerprints |= from.fingerprints
src.fingerprintshidden = from.fingerprintshidden src.fingerprintshidden |= from.fingerprintshidden
src.fingerprintslast = from.fingerprintslast src.fingerprintslast = from.fingerprintslast
//TODO bloody overlay //TODO bloody overlay
/* /*
@@ -239,8 +285,8 @@
/datum/stack_recipe /datum/stack_recipe
var/title = "ERROR" var/title = "ERROR"
var/result_type var/result_type
var/req_amount = 1 var/req_amount = 1 //amount of material needed for this recipe
var/res_amount = 1 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/max_res_amount = 1
var/time = 0 var/time = 0
var/one_per_turf = 0 var/one_per_turf = 0