///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"