/obj/structure/machinery/shieldgen name = "emergency shield projector" desc = "Used to seal minor hull breaches." icon = 'icons/obj/machinery/shielding.dmi' icon_state = "shieldoff" density = TRUE opacity = FALSE anchored = FALSE req_access = list(ACCESS_ENGINE) maxhealth = 100 var/active = FALSE /// Malfunction causes parts of the shield to slowly dissipate var/malfunction = FALSE var/list/deployed_shields = list() var/list/regenerating = list() /// Range at which it can project shields. var/range = 4 panel_open = FALSE var/locked = FALSE /// Periodically recheck if we need to rebuild a shield. var/check_delay = 30 use_power = POWER_USE_IDLE idle_power_usage = 250 WATTS /// Gets replaced by the # of shield objs its trying to maintain. active_power_usage = 1000 WATTS /obj/structure/machinery/shieldgen/mechanics_hints(mob/user, distance, is_adjacent) . += ..() . += "This machine's primary function is to project energy shields calibrated for atmospheric containment; these shields keep the air in and the vacuum out." . += "If activated while within [range] tiles of a 'space' turf, an 'open space' turf, or an otherwise 'airless' turf, it will automatically project shields there, making it extremely useful for containing hull breaches." . += "It will also project energy shields onto any engineering tape within range that has been toggled as a 'shield marker' using a multitool." . += "ALT-click the [src] to lock or unlock it (if you have the appropriate ID access)." /obj/structure/machinery/shieldgen/Destroy() collapse_shields() return ..() /obj/structure/machinery/shieldgen/proc/shields_up() if(src.active) return FALSE //If it's already turned on, how did this get called? src.active = TRUE update_icon() create_shields() var/shield_power_usage for(var/obj/structure/machinery/shield/shield_tile in deployed_shields) shield_power_usage += shield_tile.shield_sustain_power change_power_consumption(shield_power_usage, POWER_USE_ACTIVE) update_use_power(POWER_USE_ACTIVE) return TRUE /obj/structure/machinery/shieldgen/proc/shields_down() if(!src.active) return FALSE //If it's already off, how did this get called? src.active = FALSE update_icon() collapse_shields() update_use_power(POWER_USE_IDLE) /obj/structure/machinery/shieldgen/proc/create_shields() for(var/T in RANGE_TURFS(range, src)) var/turf/target_tile = T if(locate(/obj/structure/machinery/shield) in target_tile) continue var/obj/item/tape/engineering/E = locate() in target_tile if(E?.shield_marker) deploy_shield(target_tile) else if(istype(target_tile,/turf/space) || istype(target_tile,/turf/simulated/open) || istype(target_tile,/turf/simulated/floor/exoplanet/asteroid/ash) || istype(target_tile,/turf/simulated/floor/airless)) if(malfunction && prob(33) || !malfunction) deploy_shield(target_tile) /obj/structure/machinery/shieldgen/proc/deploy_shield(var/turf/T) var/obj/structure/machinery/shield/shield_tile = new /obj/structure/machinery/shield(T) src.deployed_shields += shield_tile use_power_oneoff(shield_tile.shield_generate_power) /obj/structure/machinery/shieldgen/proc/collapse_shields() for(var/obj/structure/machinery/shield/shield_tile in deployed_shields) src.deployed_shields -= shield_tile if(!QDELETED(shield_tile)) qdel(shield_tile) update_use_power(POWER_USE_IDLE) /obj/structure/machinery/shieldgen/power_change() ..() if(!active) return if(stat & NOPOWER) src.shields_down() update_icon() /obj/structure/machinery/shieldgen/process() if((stat & (BROKEN|NOPOWER)) || !anchored) if(active) visible_message(SPAN_WARNING("\The [src] shuts down."), SPAN_NOTICE("You hear a heavy droning fade out.")) shields_down() check_delay = initial(check_delay) if(malfunction) if(deployed_shields.len && prob(5)) var/obj/structure/machinery/shield/random_failing_shield = pick(deployed_shields) deployed_shields -= random_failing_shield if(!QDELETED(random_failing_shield)) qdel(random_failing_shield) else if(check_delay <= 0) if(shields_up()) var/new_power_usage = 0 for(var/obj/structure/machinery/shield/shield_tile in deployed_shields) new_power_usage += shield_tile.shield_sustain_power if(new_power_usage != active_power_usage) change_power_consumption(new_power_usage, POWER_USE_ACTIVE) use_power_oneoff(0) check_delay = initial(check_delay) else check_delay-- /obj/structure/machinery/shieldgen/proc/checkhp() if(health <= 30) src.malfunction = TRUE if(health <= 0) spawn(0) explosion(get_turf(src.loc), 0, 0, 1, 0, 0, 0) qdel(src) update_icon() return /obj/structure/machinery/shieldgen/ex_act(severity) switch(severity) if(1.0) src.health -= 75 src.checkhp() if(2.0) src.health -= 30 if (prob(15)) src.malfunction = 1 src.checkhp() if(3.0) src.health -= 10 src.checkhp() return /obj/structure/machinery/shieldgen/emp_act(severity) . = ..() switch(severity) if(EMP_HEAVY) src.health /= 2 //cut health in half malfunction = TRUE locked = pick(0,1) if(EMP_LIGHT) if(prob(50)) src.health *= 0.3 //chop off a third of the health malfunction = TRUE checkhp() /obj/structure/machinery/shieldgen/attack_hand(mob/user) if(locked) balloon_alert(user, "locked") return if(panel_open) balloon_alert(user, "panel still open") return if(stat & BROKEN) balloon_alert(user, "broken") return if(stat & NOPOWER) balloon_alert(user, "no power") return if(!anchored) balloon_alert(user, "unanchored") return if(active) user.visible_message(SPAN_NOTICE("[icon2html(src, viewers(get_turf(src)))] [user] deactivates the shield generator."), \ SPAN_NOTICE("[icon2html(src, viewers(get_turf(src)))] You deactivate the shield generator."), \ "You hear heavy droning fade out.") shields_down() else user.visible_message(SPAN_NOTICE("[icon2html(src, viewers(get_turf(src)))] [user] activate the shield generator."), \ SPAN_NOTICE("[icon2html(src, viewers(get_turf(src)))] You activate the shield generator."), \ "You hear heavy droning.") shields_up() return /obj/structure/machinery/shieldgen/emag_act(var/remaining_charges, var/mob/user) if(!malfunction) malfunction = TRUE update_icon() return TRUE /obj/structure/machinery/shieldgen/attackby(obj/item/attacking_item, mob/user) if(attacking_item.tool_behaviour == TOOL_SCREWDRIVER) update_icon() attacking_item.play_tool_sound(get_turf(src), 50) if(panel_open) balloon_alert(user, "panel closed") panel_open = FALSE else balloon_alert(user, "panel open") panel_open = TRUE else if(attacking_item.tool_behaviour == TOOL_CABLECOIL && malfunction && panel_open) var/obj/item/stack/cable_coil/coil = attacking_item to_chat(user, SPAN_NOTICE("You begin to replace the wires.")) //if(do_after(user, min(60, round( ((maxhealth/health)*10)+(malfunction*10) ))) //Take longer to repair heavier damage if(attacking_item.use_tool(src, user, 30, volume = 50)) if (coil.use(1)) health = initial(health) malfunction = FALSE to_chat(user, SPAN_NOTICE("You repair the [src]!")) update_icon() else if(attacking_item.tool_behaviour == TOOL_WRENCH) if(locked) playsound(src, 'sound/machines/terminal/terminal_error.ogg', 25, FALSE) balloon_alert(user, "locked") return if(attacking_item.use_tool(src, user, 1 SECONDS, volume = 50)) anchored = !anchored add_fingerprint(user) if(!anchored && active) shields_down() var/others_msg = anchored ? "[user] secures the external reinforcing bolts to the floor." : "[user] unsecures the external reinforcing bolts." var/self_msg = anchored ? "You secure the external reinforcing bolts to the floor." : "You unsecure the external reinforcing bolts." user.visible_message(others_msg, SPAN_NOTICE(self_msg), SPAN_NOTICE("You hear a ratcheting noise.")) update_icon() return else ..() /obj/structure/machinery/shieldgen/AltClick(mob/user) if(Adjacent(user)) add_fingerprint(user) if(allowed(user)) locked = !locked if(locked) playsound(src, 'sound/machines/terminal/terminal_button03.ogg', 35, FALSE) else playsound(src, 'sound/machines/terminal/terminal_button01.ogg', 35, FALSE) balloon_alert(user, locked ? "locked" : "unlocked") else playsound(src, 'sound/machines/terminal/terminal_error.ogg', 25, FALSE) balloon_alert(user, "access denied") /obj/structure/machinery/shieldgen/update_icon() ClearOverlays() if(anchored) AddOverlays("+bolts") if(active && !(stat & NOPOWER)) src.icon_state = malfunction ? "shieldonbr":"shieldon" else src.icon_state = malfunction ? "shieldoffbr":"shieldoff" /obj/structure/machinery/shield name = "emergency energy shield" desc = "An energy shield used to contain hull breaches." icon = 'icons/effects/effects.dmi' icon_state = "shield-old" density = TRUE opacity = FALSE anchored = TRUE unacidable = TRUE atmos_canpass = CANPASS_NEVER maxhealth = 75 /// How much power we use when first creating the shield var/shield_generate_power = 75 KILO WATTS /// How much power we use when just being sustained. var/shield_sustain_power = 30 KILO WATTS /obj/structure/machinery/shield/malfai name = "emergency forcefield" desc = "A forcefield which seems to be projected by the station's emergency atmosphere containment field." health = 100 /obj/structure/machinery/shield/malfai/New() ..() desc = "A forcefield which seems to be projected by the [station_name(TRUE)]'s emergency atmosphere containment field." /obj/structure/machinery/shield/malfai/process() health -= 0.5 // Slowly lose integrity over time check_failure() /obj/structure/machinery/shield/proc/check_failure() var/health_percentage = (health / initial(health)) * 100 switch(health_percentage) if(-INFINITY to 25) if(alpha != 150) animate(src, 1 SECOND, alpha = 150) if(26 to 50) if(alpha != 175) animate(src, 1 SECOND, alpha = 175) if(51 to 75) if(alpha != 210) animate(src, 1 SECOND, alpha = 210) if(76 to 90) if(alpha != 230) animate(src, 1 SECOND, alpha = 230) if(91 to INFINITY) if(alpha != initial(alpha)) animate(src, 1 SECOND, alpha = initial(alpha)) if(health <= 0) visible_message(SPAN_NOTICE("\The [src] dissipates!")) qdel(src) return /obj/structure/machinery/shield/New() src.set_dir(pick(1,2,3,4)) ..() update_nearby_tiles(need_rebuild=1) /obj/structure/machinery/shield/Destroy() opacity = FALSE density = FALSE update_nearby_tiles() return ..() /obj/structure/machinery/shield/CanPass(atom/movable/mover, turf/target, height, air_group) if(mover?.movement_type & PHASING) return TRUE if(!height || air_group) return FALSE else return ..() /obj/structure/machinery/shield/attackby(obj/item/attacking_item, mob/user) user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) user.do_attack_animation(src, used_item = attacking_item) //Calculate damage var/aforce = attacking_item.force if(attacking_item.damtype == DAMAGE_BRUTE || attacking_item.damtype == DAMAGE_BURN) health -= aforce //Play a fitting sound playsound(src.loc, 'sound/effects/EMPulse.ogg', 75, 1) check_failure() ..() /obj/structure/machinery/shield/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit) . = ..() if(. != BULLET_ACT_HIT) return . health -= hitting_projectile.get_structure_damage() check_failure() opacity = TRUE addtimer(CALLBACK(src, PROC_REF(update_opacity), FALSE), 2 SECONDS) /obj/structure/machinery/shield/proc/update_opacity(var/new_opacity) if(src) opacity = new_opacity /obj/structure/machinery/shield/ex_act(severity) switch(severity) if(1.0) if (prob(75)) qdel(src) if(2.0) if (prob(50)) qdel(src) if(3.0) if (prob(25)) qdel(src) return /obj/structure/machinery/shield/emp_act(severity) . = ..() switch(severity) if(EMP_HEAVY) qdel(src) if(EMP_LIGHT) if(prob(50)) qdel(src) /obj/structure/machinery/shield/hitby(atom/movable/hitting_atom, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) //Let everyone know we've been hit! visible_message(SPAN_NOTICE("\[src] was hit by [hitting_atom].")) //Super realistic, resource-intensive, real-time damage calculations. var/tforce = 0 if(ismob(hitting_atom)) tforce = 40 else if(isobj(hitting_atom)) var/obj/O = hitting_atom tforce = O.throwforce src.health -= tforce //This seemed to be the best sound for hitting a force field. playsound(src.loc, 'sound/effects/EMPulse.ogg', 100, 1) check_failure() //The shield becomes dense to absorb the blow.. purely asthetic. opacity = TRUE addtimer(CALLBACK(src, PROC_REF(update_opacity), FALSE), 2 SECONDS) ..() return