///amount of health regained per stack amount used #define DOOR_REPAIR_AMOUNT 50 /obj/machinery/door name = "Door" desc = "It opens and closes." icon = 'icons/obj/doors/Doorint.dmi' icon_state = "door1" anchored = 1 opacity = 1 density = 1 CanAtmosPass = ATMOS_PASS_PROC layer = DOOR_OPEN_LAYER var/open_layer = DOOR_OPEN_LAYER var/closed_layer = DOOR_CLOSED_LAYER var/visible = 1 var/p_open = 0//[bool]is the door open? var/operating = 0//[bool]Is the door opening or closing? var/autoclose = 0//[bool]should the door close automaticly var/glass = 0 var/normalspeed = 1 var/heat_proof = 0 // For glass airlocks/opacity firedoors var/air_properties_vary_with_direction = 0 var/maxhealth = 300 var/health var/destroy_hits = 10 //How many strong hits it takes to destroy the door 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/repairing = 0 var/block_air_zones = 1 //If set, air zones cannot merge across the door even when it is opened. var/close_door_at = 0 //When to automatically close the door, if possible //Multi-tile doors dir = EAST var/width = 1 // turf animation var/atom/movable/overlay/c_animation = null /obj/machinery/door/attack_generic(var/mob/user, var/damage) if(isanimal(user)) var/mob/living/simple_mob/S = user if(damage >= STRUCTURE_MIN_DAMAGE_THRESHOLD) visible_message("\The [user] smashes into the [src]!") playsound(src, S.attack_sound, 75, 1) take_damage(damage) else visible_message("\The [user] bonks \the [src] harmlessly.") user.do_attack_animation(src) /obj/machinery/door/Initialize(mapload, newdir) . = ..() if(density) layer = closed_layer explosion_resistance = initial(explosion_resistance) update_heat_protection(get_turf(src)) else layer = open_layer explosion_resistance = 0 if(width > 1) if(dir in list(EAST, WEST)) bound_width = width * world.icon_size bound_height = world.icon_size else bound_width = world.icon_size bound_height = width * world.icon_size health = maxhealth update_icon() update_nearby_tiles(need_rebuild=1) /obj/machinery/door/Destroy() density = FALSE update_nearby_tiles() . = ..() /obj/machinery/door/process(delta_time) if(close_door_at && world.time >= close_door_at) if(autoclose) close_door_at = next_close_time() spawn(0) close() else close_door_at = 0 /obj/machinery/door/proc/can_open() if(!density || operating || !SSticker) return 0 return 1 /obj/machinery/door/proc/can_close() if(density || operating || !SSticker) return 0 return 1 /obj/machinery/door/Bumped(atom/AM) . = ..() if(p_open || operating) return if(ismob(AM)) var/mob/M = AM if(world.time - M.last_bumped <= 10) return //Can bump-open one airlock per second. This is to prevent shock spam. M.last_bumped = world.time if(M.restrained() && !check_access(null)) return else if(istype(M, /mob/living/simple_mob/animal/passive/mouse) && !(M.ckey)) return else bumpopen(M) if(istype(AM, /mob/living/bot)) var/mob/living/bot/bot = AM if(src.check_access(bot.botcard)) if(density) open() return if(istype(AM, /obj/mecha)) var/obj/mecha/mecha = AM if(density) if(mecha.occupant && (src.allowed(mecha.occupant) || src.check_access_list(mecha.operation_req_access))) open() else do_animate("deny") return if(istype(AM, /obj/structure/bed/chair/wheelchair)) var/obj/structure/bed/chair/wheelchair/wheel = AM if(density) if(wheel.pulling && (src.allowed(wheel.pulling))) open() else do_animate("deny") /obj/machinery/door/CanAllowThrough(atom/movable/mover, turf/target) if(istype(mover) && mover.checkpass(PASSGLASS)) return !opacity return !density /obj/machinery/door/CanAtmosPass(turf/T, d) if(density) return ATMOS_PASS_AIR_BLOCKED if(block_air_zones) return ATMOS_PASS_ZONE_BLOCKED return ATMOS_PASS_NOT_BLOCKED /obj/machinery/door/proc/bumpopen(mob/user as mob) CACHE_VSC_PROP(atmos_vsc, /atmos/airflow/retrigger_delay, airflow_delay) if(operating) return if(user.last_airflow > world.time - airflow_delay) return src.add_fingerprint(user) if(density) if(istype(user, /mob/living/simple_mob) && !(user.ckey)) do_animate("smdeny") else if(allowed(user)) open() else do_animate("deny") /obj/machinery/door/bullet_act(var/obj/item/projectile/Proj) ..() var/damage = Proj.get_structure_damage() // Emitter Blasts - these will eventually completely destroy the door, given enough time. if (damage > 90) destroy_hits-- if (destroy_hits <= 0) visible_message("\The [src.name] disintegrates!") switch (Proj.damage_type) if(BRUTE) new /obj/item/stack/material/steel(src.loc, 2) new /obj/item/stack/rods(src.loc, 3) if(BURN) new /obj/effect/decal/cleanable/ash(src.loc) // Turn it to ashes! qdel(src) if(damage) //cap projectile damage so that there's still a minimum number of hits required to break the door take_damage(min(damage, 100)) /obj/machinery/door/hitby(AM as mob|obj, var/speed=5) ..() visible_message("[src.name] was hit by [AM].") var/tforce = 0 if(ismob(AM)) tforce = 15 * (speed/5) else tforce = AM:throwforce * (speed/5) playsound(src.loc, hitsound, 100, 1) take_damage(tforce) return /obj/machinery/door/attack_ai(mob/user as mob) return src.attack_hand(user) /obj/machinery/door/attack_hand(mob/user as mob) return src.attackby(user, user) /obj/machinery/door/attack_tk(mob/user as mob) if(requiresID() && !allowed(null)) return ..() /obj/machinery/door/attackby(obj/item/I as obj, mob/user as mob) src.add_fingerprint(user, 0, I) if(istype(I)) if(attackby_vr(I, user)) return if(istype(I, /obj/item/stack/material) && I.get_material_name() == src.get_material_name()) if(machine_stat & BROKEN) to_chat(user, "It looks like \the [src] is pretty busted. It's going to need more than just patching up now.") return if(health >= maxhealth) to_chat(user, "Nothing to fix!") return if(!density) to_chat(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/stack = I var/amount_given = amount_needed - repairing var/mats_given = stack.get_amount() if(repairing && amount_given <= 0) to_chat(user, "You must weld or remove \the [get_material_name()] from \the [src] before you can add anything else.") else if(mats_given >= amount_given) if(stack.use(amount_given)) repairing += amount_given else if(stack.use(mats_given)) repairing += mats_given amount_given = mats_given if(amount_given) to_chat(user, "You fit [amount_given] [stack.singular_name]\s to damaged and broken parts on \the [src].") return if(repairing && istype(I, /obj/item/weldingtool)) if(!density) to_chat(user, "\The [src] must be closed before you can repair it.") return var/obj/item/weldingtool/welder = I if(welder.remove_fuel(0,user)) to_chat(user, "You start to fix dents and weld \the [get_material_name()] into place.") playsound(src, welder.usesound, 50, 1) if(do_after(user, (5 * repairing) * welder.toolspeed) && welder && welder.isOn()) to_chat(user, "You finish repairing the damage to \the [src].") health = between(health, health + repairing*DOOR_REPAIR_AMOUNT, maxhealth) update_icon() repairing = 0 return if(repairing && I.is_crowbar()) var/datum/material/M = get_material() var/obj/item/stack/material/repairing_sheet = M.place_sheet(loc) repairing_sheet.amount += repairing-1 repairing = 0 to_chat(user, "You remove \the [repairing_sheet].") playsound(src, I.usesound, 100, 1) return //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) && user.a_intent == INTENT_HARM && !istype(I, /obj/item/card)) var/obj/item/W = I user.setClickCooldown(user.get_attack_speed(W)) if(W.damtype == BRUTE || W.damtype == BURN) user.do_attack_animation(src) if(W.force < min_force) user.visible_message("\The [user] hits \the [src] with \the [W] with no visible effect.") else user.visible_message("\The [user] forcefully strikes \the [src] with \the [W]!") playsound(src.loc, hitsound, 100, 1) take_damage(W.force) return if(src.operating > 0 || isrobot(user)) return //borgs can't attack doors open because it conflicts with their AI-like interaction with them. if(src.operating) return if(src.allowed(user) && operable()) if(src.density) open() else close() return if(src.density) do_animate("deny") return /obj/machinery/door/emag_act(var/remaining_charges) if(density && operable()) do_animate("spark") sleep(6) open() operating = -1 return 1 /obj/machinery/door/take_damage(var/damage) var/initialhealth = src.health src.health = max(0, src.health - damage) if(src.health <= 0 && initialhealth > 0) src.set_broken() else if(src.health < src.maxhealth / 4 && initialhealth >= src.maxhealth / 4) visible_message("\The [src] looks like it's about to break!" ) else if(src.health < src.maxhealth / 2 && initialhealth >= src.maxhealth / 2) visible_message("\The [src] looks seriously damaged!" ) else if(src.health < src.maxhealth * 3/4 && initialhealth >= src.maxhealth * 3/4) visible_message("\The [src] shows signs of damage!" ) update_icon() return /obj/machinery/door/examine(mob/user) . = ..() if(src.health <= 0) . += "The [src] is broken!" if(src.health < src.maxhealth / 4) . += "The [src] looks like it's about to break!" else if(src.health < src.maxhealth / 2) . += "The [src] looks seriously damaged!" else if(src.health < src.maxhealth * 3/4) . += "The [src] shows signs of damage!" /obj/machinery/door/proc/set_broken() machine_stat |= BROKEN for (var/mob/O in viewers(src, null)) if ((O.client && !( O.blinded ))) O.show_message("[src.name] breaks!" ) update_icon() return /obj/machinery/door/emp_act(severity) if(prob(20/severity) && (istype(src,/obj/machinery/door/airlock) || istype(src,/obj/machinery/door/window)) ) spawn(0) open() ..() /obj/machinery/door/ex_act(severity) switch(severity) if(1.0) qdel(src) if(2.0) if(prob(25)) qdel(src) else take_damage(300) if(3.0) if(prob(80)) var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread s.set_up(2, 1, src) s.start() else take_damage(150) return /obj/machinery/door/blob_act() if(density) // If it's closed. if(machine_stat & BROKEN) spawn(0) open(1) else take_damage(100) /obj/machinery/door/update_icon() if(density) icon_state = "door1" else icon_state = "door0" SSradiation.resistance_cache.Remove(get_turf(src)) return /obj/machinery/door/proc/do_animate(animation) switch(animation) if("opening") if(p_open) flick("o_doorc0", src) else flick("doorc0", src) if("closing") if(p_open) flick("o_doorc1", src) else flick("doorc1", src) if("spark") if(density) flick("door_spark", src) if("deny") if(density && !(machine_stat & (NOPOWER|BROKEN))) flick("door_deny", src) playsound(src.loc, 'sound/machines/buzz-two.ogg', 50, 0) if("smdeny") if(density && !(machine_stat & (NOPOWER|BROKEN))) flick("door_deny", src) return /obj/machinery/door/proc/open(var/forced = 0) if(!can_open(forced)) return operating = 1 do_animate("opening") icon_state = "door0" set_opacity(0) sleep(3) src.density = 0 update_nearby_tiles() sleep(7) src.layer = open_layer explosion_resistance = 0 update_icon() set_opacity(0) operating = 0 if(autoclose) close_door_at = next_close_time() return 1 /obj/machinery/door/proc/next_close_time() return world.time + (normalspeed ? 150 : 5) /obj/machinery/door/proc/close(var/forced = 0) if(!can_close(forced)) return operating = 1 close_door_at = 0 do_animate("closing") sleep(3) src.density = 1 explosion_resistance = initial(explosion_resistance) src.layer = closed_layer update_nearby_tiles() sleep(7) update_icon() if(visible && !glass) set_opacity(1) //caaaaarn! operating = 0 //I shall not add a check every x ticks if a door has closed over some fire. var/atom/movable/fire/fire = locate() in loc if(fire) qdel(fire) return 1 /obj/machinery/door/proc/toggle_open(var/forced) if(density) open(forced) else close(forced) /obj/machinery/door/proc/requiresID() return 1 /obj/machinery/door/allowed(mob/M) if(!requiresID()) return ..(null) //don't care who they are or what they have, act as if they're NOTHING return ..(M) /obj/machinery/door/update_nearby_tiles(need_rebuild) if(!air_master) return 0 for(var/turf/simulated/turf in locs) update_heat_protection(turf) air_master.mark_for_update(turf) return 1 /obj/machinery/door/proc/update_heat_protection(var/turf/simulated/source) if(istype(source)) if(src.density && (src.opacity || src.heat_proof)) source.thermal_conductivity = DOOR_HEAT_TRANSFER_COEFFICIENT else source.thermal_conductivity = initial(source.thermal_conductivity) /obj/machinery/door/Move(new_loc, new_dir) //update_nearby_tiles() . = ..() if(width > 1) if(dir in list(EAST, WEST)) bound_width = width * world.icon_size bound_height = world.icon_size else bound_width = world.icon_size bound_height = width * world.icon_size update_nearby_tiles() /obj/machinery/door/morgue icon = 'icons/obj/doors/doormorgue.dmi' //Flesh Door /obj/machinery/door/flesh_door name = "flesh door" desc = "This door pulses and twitches as if it's alive. It is." icon = 'icons/turf/stomach_vr.dmi' icon_state = "fleshclosed"