From 4a9ff163c1124228ed087cb32fadcccfea894934 Mon Sep 17 00:00:00 2001 From: TC <55299415+Pockets-byte@users.noreply.github.com> Date: Mon, 30 Mar 2026 15:19:58 -0400 Subject: [PATCH 01/28] f---- --- nsv13.dme | 1 + nsv13/code/game/machinery/atmos_field_gen.dm | 328 ++++++++++++++++++ nsv13/icons/obj/machines/atmosshieldgen.dmi | Bin 0 -> 2072 bytes .../obj/smooth_structures/atmosshield.dmi | Bin 0 -> 2849 bytes 4 files changed, 329 insertions(+) create mode 100644 nsv13/code/game/machinery/atmos_field_gen.dm create mode 100644 nsv13/icons/obj/machines/atmosshieldgen.dmi create mode 100644 nsv13/icons/obj/smooth_structures/atmosshield.dmi diff --git a/nsv13.dme b/nsv13.dme index 8c7b1b5e809..460cbb60bdc 100644 --- a/nsv13.dme +++ b/nsv13.dme @@ -3797,6 +3797,7 @@ #include "nsv13\code\game\general_quarters\damage.dm" #include "nsv13\code\game\general_quarters\dropship.dm" #include "nsv13\code\game\general_quarters\dropship_types.dm" +#include "nsv13\code\game\machinery\atmos_field_gen.dm" #include "nsv13\code\game\machinery\autodoc.dm" #include "nsv13\code\game\machinery\blast_shower.dm" #include "nsv13\code\game\machinery\boarding_harpoon.dm" diff --git a/nsv13/code/game/machinery/atmos_field_gen.dm b/nsv13/code/game/machinery/atmos_field_gen.dm new file mode 100644 index 00000000000..2771d13d60c --- /dev/null +++ b/nsv13/code/game/machinery/atmos_field_gen.dm @@ -0,0 +1,328 @@ +/// off +#define GENERATOR_INACTIVE 0 +/// waiting for power/retrying, will activate and do things if powered +#define GENERATOR_WANTPOWER 1 +/// active +#define GENERATOR_ACTIVE 2 + +/obj/machinery/atmos_shield_gen + name = "atmospheric shield generator" + desc = "Produces an atmos shield in a line between itself and another generator with both facing the other, while active. Powered by APC. Field must not be obstructed by wall, or an atmos shield field. Will turn on after gaining power if turned off due to power loss." + icon = 'nsv13/icons/obj/machines/atmosshieldgen.dmi' + icon_state = "atmosshield" + density = FALSE + layer = ABOVE_MOB_LAYER + active_power_usage = BASE_MACHINE_ACTIVE_CONSUMPTION + circuit = /obj/item/circuitboard/machine/atmos_shield_gen + req_one_access = list(ACCESS_EXTERNAL_AIRLOCKS, ACCESS_ENGINE_EQUIP) + can_atmos_pass = ATMOS_PASS_PROC + power_channel = AREA_USAGE_ENVIRON + armor_type = /datum/armor/atmos_shield_gen + + /// are we locked + var/locked = FALSE + /// are we on + var/on = GENERATOR_INACTIVE + /// Max tiles between this generator and another generator. 0 would mean the generator can only do its thing if both are on the same tile. + var/max_range = 2 + /// our shields + var/list/fields = list() + /// the shield generator we belong to that is actually making shields + var/obj/machinery/atmos_shield_gen/master = null + +/datum/armor/atmos_shield_gen + melee = 50 + fire = 100 + acid = 50 + +/obj/machinery/atmos_shield_gen/Initialize(mapload) + . = ..() + register_context() + AddElement(/datum/element/simple_rotation) + set_wires(new /datum/wires/atmosshieldgen(src)) + SSmachines.processing_early += src + if(on) + return INITIALIZE_HINT_LATELOAD + +/obj/machinery/atmos_shield_gen/post_machine_initialize() + . = ..() + process_early() // not risking processing falling behind and letting gas out + +/obj/machinery/atmos_shield_gen/Destroy(force) + QDEL_LIST(fields) + master?.turn_off() + master = null + return ..() + +/obj/machinery/atmos_shield_gen/add_context(atom/source, list/context, obj/item/held_item, mob/user) + . = NONE + + if(!isnull(held_item)) + if(istype(held_item, /obj/item/card/id)) + context[SCREENTIP_CONTEXT_LMB] = (locked ? "Unlock" : "Lock") + return CONTEXTUAL_SCREENTIP_SET + if(locked) + return + if(held_item.tool_behaviour == TOOL_SCREWDRIVER) + context[SCREENTIP_CONTEXT_LMB] = "[panel_open ? "Close" : "Open"] panel" + return CONTEXTUAL_SCREENTIP_SET + else if(held_item.tool_behaviour == TOOL_CROWBAR && panel_open) + context[SCREENTIP_CONTEXT_LMB] = "Deconstruct" + return CONTEXTUAL_SCREENTIP_SET + else + context[SCREENTIP_CONTEXT_LMB] = "Toggle" + context[SCREENTIP_CONTEXT_RMB] = (locked ? "Unlock" : "Lock") + return CONTEXTUAL_SCREENTIP_SET + +/obj/machinery/atmos_shield_gen/examine(mob/user) + . += ..() + if(!in_range(user, src) && !isobserver(user)) + return + + . += span_notice("The status display reads:") + . += span_notice("Currently [on ? "" : "in"]active.") + if(locked) + . += span_boldwarning("LOCKED") + return + . += span_notice("Maximum field length: [max_range] tiles.") + . += span_notice("Its maintenance panel can be [EXAMINE_HINT("screwed")] [panel_open ? "close" : "open"].") + if(panel_open) + . += span_notice("It can be [EXAMINE_HINT("pried")] apart.") + +/obj/machinery/atmos_shield_gen/RefreshParts() + . = ..() + var/datum/stock_part/capacitor/capacitor = locate() in component_parts + active_power_usage = initial(active_power_usage) / capacitor.tier // 0.25kw per tile at tier 4 + var/datum/stock_part/micro_laser/laser = locate() in component_parts + max_range = laser.tier + 2 + +/obj/machinery/atmos_shield_gen/update_overlays() + . = ..() + if(machine_stat & NOPOWER) + return + if(!on) + return + . += (on == GENERATOR_ACTIVE ? "active" : "wantpower") + . += emissive_appearance(icon, on == GENERATOR_ACTIVE ? "active" : "wantpower", src, alpha = src.alpha) + +/obj/machinery/atmos_shield_gen/screwdriver_act(mob/user, obj/item/tool) + if(!panel_open && locked) + balloon_alert(user, "locked!") + return ITEM_INTERACT_FAILURE + return default_deconstruction_screwdriver(user, icon_state, icon_state, tool) + +/obj/machinery/atmos_shield_gen/crowbar_act(mob/user, obj/item/tool) + if(on) + balloon_alert(user, "turn off first!") + return ITEM_INTERACT_FAILURE + return default_deconstruction_crowbar(tool) + +/obj/machinery/atmos_shield_gen/wrench_act(mob/living/user, obj/item/tool) + if(on) + balloon_alert(user, "turn off first!") + return ITEM_INTERACT_FAILURE + if(locked) + balloon_alert(user, "unlock first!") + return ITEM_INTERACT_FAILURE + if(default_unfasten_wrench(user, tool) && !anchored) + turn_off() + return ITEM_INTERACT_SUCCESS + +/obj/machinery/atmos_shield_gen/attack_hand(mob/living/user, list/modifiers) + . = ..() + if(!anchored) + balloon_alert(user, "not anchored!") + return + if(locked && !issilicon(user)) + balloon_alert(user, "locked!") + return + toggle(user) + +/obj/machinery/atmos_shield_gen/attack_hand_secondary(mob/user, list/modifiers) + . = ..() + if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN) + return + if(allowed(user)) + locked = !locked + balloon_alert(user, "[locked ? "" : "un"]locked!") + return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN + balloon_alert(user, "no access!") + return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN + +/obj/machinery/atmos_shield_gen/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + . = NONE + add_fingerprint(user) + if(is_wire_tool(tool) && panel_open) + wires.interact(user) + return ITEM_INTERACT_SUCCESS + if(istype(tool, /obj/item/card/id) && check_access(tool)) + locked = !locked + balloon_alert(user, "[locked ? "" : "un"]locked!") + return ITEM_INTERACT_SUCCESS + +/obj/machinery/atmos_shield_gen/process_early() + if(on == GENERATOR_ACTIVE && !powered()) + turn_off(power_failure = TRUE) + else if(on == GENERATOR_WANTPOWER && powered()) + on = GENERATOR_ACTIVE + update_appearance(UPDATE_OVERLAYS) + + if(on != GENERATOR_ACTIVE) + if(length(fields)) + QDEL_LIST(fields) + return + + if(!isnull(master)) + if(master.on != GENERATOR_ACTIVE) // FUCK + turn_off() + return + var/field_len = length(fields) + if(field_len) + use_energy(active_power_usage * field_len) + return + var/turf/current_turf = get_turf(src) + var/found_slave = null + for(var/i in 1 to max_range) + if(isclosedturf(current_turf)) // we were blocked by a closed turf + return + if(isnull(found_slave)) + for(var/obj/machinery/atmos_shield_gen/generator in current_turf) + if(generator == src || !generator.powered() || generator.dir != REVERSE_DIR(dir) || !isnull(generator.master) || !generator.anchored) + continue + found_slave = generator + generator.change_master(src) + break + if(!isnull(found_slave)) + break + current_turf = get_step(current_turf, dir) // advance + + if(isnull(found_slave)) + on = GENERATOR_WANTPOWER + update_appearance(UPDATE_OVERLAYS) + return + + var/turf/line = get_line(src, found_slave) + for(var/turf/line_turf as anything in line) + fields += new /obj/effect/atmos_shield(line_turf, src) + +/obj/machinery/atmos_shield_gen/proc/toggle(mob/user) + if(on) + turn_off() + else + on = GENERATOR_WANTPOWER + update_appearance(UPDATE_OVERLAYS) + if(!isnull(user)) + balloon_alert(user, "turned [on ? "on" : "off"]") + +/obj/machinery/atmos_shield_gen/proc/turn_off(power_failure = FALSE) + if(!on) + return + if(power_failure) + balloon_alert_to_viewers("no power!") + playsound(src, 'sound/machines/cryo_warning.ogg', 65) + on = power_failure ? GENERATOR_WANTPOWER : GENERATOR_INACTIVE + master?.turn_off(power_failure) + change_master(null) + if(!power_failure) // so that it will get cleaned up on the next process so they can react + QDEL_LIST(fields) + update_appearance(UPDATE_OVERLAYS) + +/// Changes our master variable and (un)registers signals. Does not check whether active and stuff. Will activate generator if new_master is not null +/obj/machinery/atmos_shield_gen/proc/change_master(new_master) + if(master == new_master) + return + if(!isnull(master) && master != new_master) + UnregisterSignal(master, COMSIG_QDELETING) + if(isnull(new_master)) + master = null + on = GENERATOR_INACTIVE + update_appearance(UPDATE_OVERLAYS) + return + master = new_master + RegisterSignal(master, COMSIG_QDELETING, PROC_REF(master_deleted)) + on = GENERATOR_ACTIVE + update_appearance(UPDATE_OVERLAYS) + +/obj/machinery/atmos_shield_gen/proc/master_deleted(datum/source) + SIGNAL_HANDLER + turn_off() + +/obj/machinery/atmos_shield_gen/attack_ai(mob/user) + return attack_hand(user) + +/obj/machinery/atmos_shield_gen/attack_ai_secondary(mob/user) + return attack_hand_secondary(user) + +/obj/machinery/atmos_shield_gen/attack_robot(mob/user) + return attack_hand(user) + +/obj/machinery/atmos_shield_gen/attack_robot_secondary(mob/user) + return attack_hand_secondary(user) + +/obj/machinery/atmos_shield_gen/active + on = GENERATOR_WANTPOWER + locked = TRUE + +/obj/effect/atmos_shield + name = "" + icon = 'nsv13/icons/obj/smooth_structures/atmosshield.dmi' + icon_state = "atmosshield-0" + base_icon_state = "atmosshield" + density = FALSE + anchored = TRUE + can_atmos_pass = ATMOS_PASS_NO + alpha = 80 + rad_insulation = RAD_LIGHT_INSULATION + resistance_flags = FIRE_PROOF | FREEZE_PROOF + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_ATMOS_SHIELD + canSmoothWith = SMOOTH_GROUP_ATMOS_SHIELD + light_on = TRUE + light_range = 1.8 + light_power = 2 + light_color = COLOR_ENGINEERING_ORANGE + /// the atmos shield generator that made us, used to turn it off sometimes + var/obj/machinery/atmos_shield_gen/owner + +/obj/effect/atmos_shield/Initialize(mapload, owner) + . = ..() + src.owner = owner + QUEUE_SMOOTH(src) + QUEUE_SMOOTH_NEIGHBORS(src) + update_appearance(UPDATE_OVERLAYS) + air_update_turf(TRUE, TRUE) + RegisterSignal(loc, COMSIG_TURF_CHANGE, PROC_REF(turf_changed)) // Wont ever be spawned inside something hopefully + AddElement(/datum/element/give_turf_traits, string_list(list(TRAIT_FIREDOOR_STOP))) + +/obj/effect/atmos_shield/block_superconductivity() + return TRUE + +/obj/effect/atmos_shield/update_overlays() + . = ..() + . += emissive_appearance(icon, icon_state, src, alpha = src.alpha) + +/obj/effect/atmos_shield/Destroy(force) + owner = null + return ..() + +/obj/effect/atmos_shield/atom_destruction(damage_flag) + owner?.turn_off() + return ..() + +/obj/effect/atmos_shield/singularity_pull(atom/singularity, current_size) + owner?.turn_off() + qdel(src) + +/obj/effect/atmos_shield/singularity_act() + owner?.turn_off() + qdel(src) + +/obj/effect/atmos_shield/proc/turf_changed(datum/source, path, new_baseturfs, flags, post_change_callback) + SIGNAL_HANDLER + if(ispath(path, /turf/closed)) + atom_destruction(ENERGY) + +#undef GENERATOR_ACTIVE +#undef GENERATOR_WANTPOWER +#undef GENERATOR_INACTIVE diff --git a/nsv13/icons/obj/machines/atmosshieldgen.dmi b/nsv13/icons/obj/machines/atmosshieldgen.dmi new file mode 100644 index 0000000000000000000000000000000000000000..d5da8a8c7cf320462307d3a41d1e86b3a0ca01fc GIT binary patch literal 2072 zcma)+eKeG5AIGnIZf3?XW6IksZDtiwS);rS+9nOMO;&_5Wk`rdRFc=+j0QzS*~v@I zcBHbqlZhIdFk>Q82T4S>dfHH%RMxv@o*R3f=bz_~=RW7UuXA7Ly1&=CKELnxm+j^0 zP9zu-002afZA@=;7A_1t7JZ-T2)9J1(!^c6W0|4HLXJkn#70CP1|S|fl_w`O(a6k> z=Qr`AzV{<<29~=fR;kF3BK7;31y|*bC)nEcx(@@x_zy@v@7YOXiG$9L0-6~ikaIl6 zIql(r=h=0oTj=GIH;o=#imN{VL4Lui6
=>8Ft97f;V**UWSh 2ptSwO3?h&FpA
z87)iAezX9({Bf2JV@cJx4ae{WL{vNyg xN#B#|`<>M4u7s*`k>
ze==!n!>LR9smJvEqst{o8a+<7(yC6SOndjldD`H f(n2SGu|31DMFYE1H$3xpeC>VFO
z!alq8DcXEVA`P&nzJQ8M@}gA`)!iGBr_QZe4kl(>c>EhMgoj=8H8YjW&CTD8VW49!W+5X3Cn4<%*uCW*n}JUa7F}fT
z)i+rp6`twqM=0FHL(a$gJbM50=ZmfbI~Nz_yYw9k3mhTs9FUIURC4|%Lzvrfy86ke
zK;4Wxx!#KxA&_~2WT>%#gc&7KE*?WQ@zn`W=xl}<^6XvCMFOv$J2?}-0(6XzjErn#
zZmv{vDK`NU%_brkv)vS%ChC-W`}o6;TTfM0&pc2_1dPpZOX$buLN%BYaOd~HJ_0(6
z_%)#E0%rH~R&{kX03f#%!F2_y-3-+vR}pe<`?;Ncg975@x$x0T0#yq4Nkn@^zJg5F
zO$iOvFrBD6A4Ib-raC9}(z8HiV#r1OPPTPscB(Qj_?Gs>1=uFtCglZLw%R2Ev8UCd
z^b8EaD5Tc75E&!?646-BlaFyKw_MK;?PMeq-a##knky{=YirCx8C
I^QhGpveR7KH`+KzdFRqXh{si=ut7XuI{dhZI<
za0`R*dwrbKu+#DV7P|(!Ur(L=oU8IaM|XFhjLyw{u8@JkI2)Py@8T(OPo5~~Ds61f
z{o$?v`_0-IPYcc~Z4dScR5UBI=o3Wo&tMtnVh_SZSV;bS0;gnU&3UdwhiDNdbIeea
znuWODGgORD@zPr2{pD|OT_nTmGRNzy+*+VCoPD-TeS0GRihY>t(s#FkR1ws^ZNW2a
z1W|*2Oi}`U_EeViK7vf_Jd@P@nCB4MKw0
N(-6lga$ivuR}{f?4=~zHt2~
z;jYJtz)}KkE&9V-s%9)&b{Z6uZWi7o+H1Tl3krGjn{
yyGD+LiakMu@7j#tMh(0%E|2c^K57Y#X%P~xnuKOsY7X%zFom?WI
zT;K@;y4-A)SA!RSpN)+zx!}n+Z>wU87DHNVR{y}~znC&{TJD%oVXJ;+?oQ_4-ZO5@
z@dB)V1rDFs{hFl_e%@5HQ7mbPW)7?qH7&`n4WpM%s1Lg0j$zJE;hLovFvt@o-eY<^
zvzH*cOo1-cXK>~MBmCS6%W6P-et+ikRG~5E&QCbfXC~5jOBh9K>FwPzmBi9$s4W0^
z6+!(IVRE=(Mh!tqCd9f}Xjks@&E5h4{t<6Y7`3ixZz|3y0EfZO_LN;+xn$6lX*L9)
zF|3Iy;L(die|^*i3QR!9_HS-e)P>}-mSSOHxqvs_CH@jXhLVz#kMoM}I!Co|-fnY2K(OV#
zF@I#l3y`q(?zpD|M|ZSC1N^@YP@#I{!)#aHIiD(EXT^!&FjHOfn6DCfyfZK3^{HRM
zYbc9xrIqkjT=LYrqp=6|wEq;IxS18%{+#n!*eQjdSR&|GZN~~9lH*xQ>xX5^%%5fa
zATR*vDj;uwg~|o=G^OV?3W2`9zApmDs~%=R5~3l(Xzk8o(%-%p6ks}67akAK5S!)3
z#b2Rud(d}4JMQoxETNL=$hn7MNzZF}s`LKQtDR6MU8>WaSW912TdE)0Yl3nfyc{LN|FVzibnuH)0wKLB;C%Z#|&
zmUhvYt5ql!8?tF=BSfR9y)&8;TvH8w+2CTGf4jV+m=|xK(`g?FRpQ&z@m6lle`j-)-W1efK**}R2h%YW%fC5dm>we(kVZ%s_NR6^%pQLK&fX6|
zigVje8wWo!M!#Y7g
zwaX$DCu#C}TM+)-!pAmX?$pP*Llk5}MD1(RpahPH)z|w6IXAk0%4CP$(UT*HD$Xd_
z+&1^LDAI&IQ1m^LpA4%>D)(rk4%0>+yl(yh3h?s|e89SV1Q-nW=-5~T`WM3xheY(e
z)CMOfr><9eZ#reX8W+HBfpKA!>Poue=Amvr0*3*O+Y3@Dkrm)ap|5ZR4D0+cUrMx;
z3woTtZPG5(ZB!Zbzkn#T196ne6FX<~mU{3-;*i#sfenq?&%a@9n~J)s6dLt2*P8QA
z`uDJcGUwm$QT{gO2ffjku`N>(8c^9iHNLpM%}it3p|qnV2#<8U>bUz9-c%u(lNb*d
zmgg#8d>DD75@1XKsjP~AGW%!iM^
UwLoZ+sv=kML6>v%dh=7de?9e6xoB;rB8wi8#^>u>%rEAB0$
z>8im0p?Du&e2YD%6b4Vs3)*N198+%n7_|9-$_LJww2)nb2Glk96f;<|F{N0_zK<`I
z?JEwUQkr=bud{vx$yhuS67PuG)-}O_n-s}hed*Z3*#lhhOl|A+&TqaByLhq?EUw17
zs%kPv5K2LoiDv9>piAO%#0L!`Cxmp`hX;Zuv>dtzpQ_K}g0JW7`n76Ry6Q0*zu%Wi
zbq+!JsPQD-G`#hh0oK;Ek*NN_z_hzuawx?8Mc)g793kdhqEYDD2gN!UNInHwSPiPA
zF<2o&6sX>(NBU2&C4nl4+(X8c9SNBx1~FkfT#@TFnBQN3cr@Ayv>gwW-tm$^K3(wj
zrS%$u?%ROVyNeb3aZkC_b_=CGdUJ@ucM}cLlK^@=c!6V2Cm%E&Gb7A%tKT|hKpJa^
zXEsy1CbfzDs;GW7Ab6TYG;?X D2!CXPN-UE8TJDD@P%?MZwz_Tf
zw*ujNK%x@aImq>X%7-+1K}pvtT$O;L!j7XBPKsuBoN&XYADyY$>k;wwnx6-|zP_2Q
zgbi%&ct^j{H^;rm135LHu>&Eesy$ZSRsq3t>% ~YZ$8zU60ylC_
ziG)Q*d~bqoLNp-Tyy0)Pv|p_K!W=GSH{t5>%Ro6uSwfp`#|01_k2ccxz&nV7cy(TP
z*