From 83edea35ec7fee1423c03c7c966dc7b200e2a1c7 Mon Sep 17 00:00:00 2001 From: VerySoft Date: Sat, 11 Apr 2026 01:13:14 -0400 Subject: [PATCH 1/8] dungeon maker --- code/__defines/dcs/signals.dm | 1 + code/_onclick/click.dm | 3 + code/game/Rogue Star/dungeon_maker.dm | 141 + code/game/Rogue Star/multipoint_barrier.dm | 4 +- code/game/Rogue Star/obj/keys.dm | 219 +- code/game/objects/structures/simple_doors.dm | 8 +- code/modules/admin/admin_verb_lists_vr.dm | 3 +- icons/rogue-star/dungeon_maker.dmi | Bin 0 -> 797 bytes icons/rogue-star/keys.dmi | Bin 1059 -> 1982 bytes maps/example/event_example.dmm | 3739 +++++++++--------- vorestation.dme | 1 + 11 files changed, 2216 insertions(+), 1903 deletions(-) create mode 100644 code/game/Rogue Star/dungeon_maker.dm create mode 100644 icons/rogue-star/dungeon_maker.dmi diff --git a/code/__defines/dcs/signals.dm b/code/__defines/dcs/signals.dm index f4b4048b00d..259824d9056 100644 --- a/code/__defines/dcs/signals.dm +++ b/code/__defines/dcs/signals.dm @@ -791,4 +791,5 @@ #define COMSIG_VORE_HEALTHBAR_UPDATE "vore_healthbar_update" #define COMSIG_VORE_HEALTHBAR_CLEANUP "vore_healthbar_cleanup" #define HIDE_AND_SEEK_ROUND_END "round_end" +#define COMSIG_KEY_ATTACK "key_event" //RS ADD END diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index 33729178a02..2cbb4aa6b4c 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -48,6 +48,9 @@ build_click(src, client.buildmode, params, A) return + if(client?.dungeon_maker) //RS ADD + dungeon_maker_click(src,client.dungeon_maker, params, A) //RS ADD + if(is_incorporeal()) //RS ADD START - don't shoot at or attack people while you are intangible face_atom(A) return //RS ADD END diff --git a/code/game/Rogue Star/dungeon_maker.dm b/code/game/Rogue Star/dungeon_maker.dm new file mode 100644 index 00000000000..22993981d56 --- /dev/null +++ b/code/game/Rogue Star/dungeon_maker.dm @@ -0,0 +1,141 @@ +//RS FILE +/client + var/list/utility + var/dungeon_maker = null + +/client/proc/toggle_dungeon_maker() + set name = "Toggle Dungeon Maker" + set category = "Special Verbs" + + if(!holder) + clear_utility() + return + + if(utility) + clear_utility() + return + utility = list() + show_popup_menus = FALSE + log_admin("[key_name(usr)] has entered dungeon maker.") + var/obj/effect/dungeon_maker/holder/H = new() + utility |= H + screen += H + +/client/proc/clear_utility() + for(var/thing in utility) + utility -= thing + screen -= thing + qdel(thing) + show_popup_menus = TRUE + QDEL_LIST_NULL(utility) + log_admin("[key_name(usr)] has left dungeon maker.") + +/obj/effect/dungeon_maker + density = TRUE + anchored = TRUE + layer = LAYER_HUD_BASE + plane = PLANE_PLAYER_HUD + dir = NORTH + icon = 'icons/rogue-star/dungeon_maker.dmi' + icon_state = "base" + color = "#ff9100" + +// screen_loc = "CENTER,WEST" + +/obj/effect/dungeon_maker/holder + lock_id = null + trigger_id = null + screen_loc = "WEST,CENTER" + icon = null + icon_state = null + +/obj/effect/dungeon_maker/holder/Initialize() + . = ..() + add_button(/obj/effect/dungeon_maker/button/link) + add_button(/obj/effect/dungeon_maker/button/lock) + add_button(/obj/effect/dungeon_maker/button/mechanic) + +/obj/effect/dungeon_maker/holder/Destroy() + for(var/thing in contents) + qdel(thing) + return ..() + +/obj/effect/dungeon_maker/holder/proc/add_button(var/button_type) + if(!ispath(button_type)) + log_and_message_admins("An dungeon_maker/holder was given [button_type], but that is not a type so it didn't make it.") + return + var/obj/effect/dungeon_maker/ourbutton = new button_type(src) + vis_contents += ourbutton + +/obj/effect/dungeon_maker/holder/Click(location, control, params) + var/list/pa = params2list(params) + if(pa.Find("middle")) + //??? + return + else if(pa.Find("left")) + //Close + return + else if(pa.Find("right")) + //??? + return + +/obj/effect/dungeon_maker/button + var/obj/effect/dungeon_maker/holder/master = null + +/obj/effect/dungeon_maker/button/link + name = "Link" + icon_state = "link" +// screen_loc = "CENTER,WEST" + +/obj/effect/dungeon_maker/button/link/Click(location, control, params) + to_world("link") + var/list/pa = params2list(params) + if(pa.Find("middle")) + //Pick up clicked trigger_id + return + else if(pa.Find("left")) + //Apply clicked trigger_id + return + else if(pa.Find("right")) + //Open context menu for given object + return + +/obj/effect/dungeon_maker/button/lock + name = "Lock" + icon_state = "lock" +// screen_loc = "CENTER-1,WEST" + pixel_y = -34 + +/obj/effect/dungeon_maker/button/lock/Click(location, control, params) + to_world("lock") + var/list/pa = params2list(params) + if(pa.Find("middle")) + //Pick up clicked lock_id + return + else if(pa.Find("left")) + //Add or update lock on clicked + return + else if(pa.Find("right")) + //Open context menu for given object + return + +/obj/effect/dungeon_maker/button/mechanic + name = "Mechanic" + icon_state = "mechanic" +// screen_loc = "CENTER-2,WEST" + pixel_y = -68 + +/obj/effect/dungeon_maker/button/mechanic/Click(location, control, params) + to_world("mechanic") + var/list/pa = params2list(params) + if(pa.Find("middle")) + //Pick up clicked object if it is a valid type + return + else if(pa.Find("left")) + //Place selected object if one exists + return + else if(pa.Find("right")) + //Open context menu for given object + return + +/proc/dungeon_maker_click(var/mob/user, buildmode, params, var/obj/object) diff --git a/code/game/Rogue Star/multipoint_barrier.dm b/code/game/Rogue Star/multipoint_barrier.dm index 426545a743d..8b47112f561 100644 --- a/code/game/Rogue Star/multipoint_barrier.dm +++ b/code/game/Rogue Star/multipoint_barrier.dm @@ -9,7 +9,7 @@ var/global/list/multipoint_trigger_list = list() // Used for admin-only reset v icon = 'icons/rogue-star/misc.dmi' icon_state = "box" - var/trigger_id = "REPLACE ME" //Use this to set which triggers are connected to eachother and the object they are connected to + trigger_id = "REPLACE ME" //Use this to set which triggers are connected to eachother and the object they are connected to /obj/multipoint/New(loc, ...) . = ..() @@ -178,7 +178,7 @@ var/global/list/multipoint_trigger_list = list() // Used for admin-only reset v var/triggered_key //When you press a key, you can't press another key var/static/list/trigger_list = list() //A list of our fellow triggers to iterate through - var/trigger_id = "REPLACE ME" //Customize this to set which triggers are connected to eachother and the barrier they are connected to + trigger_id = "REPLACE ME" //Customize this to set which triggers are connected to eachother and the barrier they are connected to var/triggered_state = "button-p" var/untriggered_state = "button" var/doubles = FALSE //If false, the trigger will not allow you to activate a linked trigger if you have already activated one. Any that are true will not care if you pushed another diff --git a/code/game/Rogue Star/obj/keys.dm b/code/game/Rogue Star/obj/keys.dm index ec42f677f43..8955ad3eb82 100644 --- a/code/game/Rogue Star/obj/keys.dm +++ b/code/game/Rogue Star/obj/keys.dm @@ -1,4 +1,8 @@ //RS FILE +/obj + var/lock_id = null //Used with keys + var/trigger_id = null //Used with various event things + /obj/item/key name = "key" desc = "A small key made out of some kind of metal." @@ -6,7 +10,7 @@ icon_state = "key" persist_storable = FALSE w_class = ITEMSIZE_TINY - var/lock_id = "key" + lock_id = "key" var/one_time = FALSE //If true the key will delete itself after use var/master_key = FALSE //If true then this key can open anything with a configured lock! @@ -19,10 +23,10 @@ color = "#b4cacc" /obj/item/key/resolve_attackby(atom/A, mob/user, attack_modifier, click_parameters) - if(!unlock(A,user)) + if(!lock_interact(A,user)) return ..() -/obj/item/key/proc/unlock(var/atom/A,var/mob/user) +/obj/item/key/proc/lock_interact(var/atom/A,var/mob/user) if(!A || !user) return FALSE @@ -30,17 +34,15 @@ return FALSE var/obj/O = A - if(!O.unlock_with_key(lock_id,src)) - to_chat(user,SPAN_DANGER("\The [src] doesn't fit into \the [A]...")) - return FALSE - - to_chat(user,SPAN_NOTICE("\The [src] fits cleanly into \the [A]. You give it a firm turn.")) + SEND_SIGNAL(O,COMSIG_KEY_ATTACK,src,user) + return TRUE +/obj/item/key/proc/unlocked(var/mob/user) if(one_time) - to_chat(user,SPAN_DANGER("\The [src] crumbles away to dust after being used.")) - user.drop_from_inventory(src,get_turf(user)) + if(user) + to_chat(user,SPAN_DANGER("\The [src] crumbles away to dust after being used.")) + user.drop_from_inventory(src,get_turf(user)) qdel(src) - return TRUE /obj/item/key/big name = "big key" @@ -52,43 +54,174 @@ /obj/item/key/onetime one_time = TRUE -/obj/proc/unlock_with_key(key_id,var/obj/item/key/K) - if(K) - if(K.master_key) - . = TRUE - if(!key_id) - return FALSE +/obj/item/key/scifi + desc = "A small electronic card with a plastic case, with one end bearing exposed contact points for plugging into an electronic lock." + icon_state = "scifi-a" + var/static/list/overlays_cache = list() + var/contact_color = "#f7b947" -/obj/machinery/door/airlock/unlock_with_key(key_id,var/obj/item/key/K) +/obj/item/key/scifi/Initialize() . = ..() + update_icon() + +/obj/item/key/scifi/update_icon() + cut_overlays() + if(contact_color) + var/combine_key = "[icon_state]-contacts-[contact_color]" + var/image/contact = overlays_cache[combine_key] + if(!contact) + contact = image(icon,null,"[icon_state]-contacts") + contact.color = contact_color + contact.appearance_flags = RESET_COLOR|KEEP_APART|PIXEL_SCALE + overlays_cache[combine_key] = contact + add_overlay(contact) + +/obj/item/key/scifi/big + icon_state = "scifi-b" + desc = "A broad electronic card with a solid metal case. One end has precisely machined contacts exposed for plugging into an electronic lock." + lock_id = "boss" + var/case_color = "#776f85" - if(key_id == id_tag || (. && id_tag)) - if(K && !locked) - if(K.one_time) //Don't destroy keys for doors that are already unlocked. - return FALSE - if(locked) - unlock() - open() - else - lock() - return TRUE - -/obj/structure/simple_door/unlock_with_key(key_id,var/obj/item/key/K) +/obj/item/key/scifi/big/update_icon() . = ..() + if(case_color) + var/combine_key = "[icon_state]-case-[case_color]" + var/image/case = overlays_cache[combine_key] + if(!case) + case = image(icon,null,"[icon_state]-case") + case.color = case_color + case.appearance_flags = RESET_COLOR|KEEP_APART|PIXEL_SCALE + overlays_cache[combine_key] = case + add_overlay(case) + +/obj/item/key/scifi/red + color = "#ff0000" + lock_id = "red" +/obj/item/key/scifi/blue + color = "#003cff" + lock_id = "blue" +/obj/item/key/scifi/yellow + color = "#ffd900" + lock_id = "yellow" +/obj/item/key/scifi/magenta + color = "#cc00ff" + lock_id = "magenta" + +/obj/item/key/scifi/big/red + color = "#ff0000" + case_color = "#6b5c5c" + lock_id = "red-boss" +/obj/item/key/scifi/big/blue + color = "#003cff" + case_color = "#545c5c" + lock_id = "blue-boss" +/obj/item/key/scifi/big/yellow + color = "#ffd900" + case_color = "#7e5c5c" + lock_id = "yellow-boss" +/obj/item/key/scifi/big/magenta + color = "#cc00ff" + case_color = "#5a5c5c" + lock_id = "magenta-boss" + +/obj/item/key/card + name = "key card" + desc = "A small rectangular card with a magnet strip running along one side." + icon_state = "card" + +/obj/item/key/card/red + color = "#ff0000" + lock_id = "red" +/obj/item/key/card/blue + color = "#003cff" + lock_id = "blue" +/obj/item/key/card/yellow + color = "#ffd900" + lock_id = "yellow" +/obj/item/key/card/magenta + color = "#cc00ff" + lock_id = "magenta" + + +/obj/proc/key_event(var/obj/item/key/K) + if(!K) + return FALSE + if((K.master_key && lock_id) || lock_id == K.lock_id) + return trigger_special(K.one_time) + return FALSE + +/obj/proc/trigger_special(var/one_time = FALSE,var/only_lock = FALSE) //If one_time is true, then triggering only allows the door to be unlocked. + return FALSE //Returning true and false tells one_time keys if they should delete themselves or not + +/obj/machinery/door/airlock/trigger_special(var/one_time = FALSE,var/only_lock = FALSE) + if(locked && !only_lock) + unlock() + open() + else + if(one_time) + return FALSE + lock() + return TRUE - if(key_id == lock_id || (. && lock_id)) - if(K && !locked) - if(K.one_time) //Don't destroy keys for doors that are already unlocked. - return FALSE - locked = !locked - return TRUE +/obj/structure/simple_door/trigger_special(var/one_time = FALSE,var/only_lock = FALSE) + if(one_time) + if(!locked) + return FALSE + if(locked && only_lock) + return FALSE + toggle_lock() + return TRUE + +/obj/event_obstical/trigger_special(var/one_time = FALSE,var/only_lock = FALSE) + if(one_time) + if(!density) + return FALSE + if(only_lock) + if(density) + return FALSE + post_trigger() + return TRUE + +/datum/component/lock + var/locked = TRUE + var/lock_id = "LOCK" + +/datum/component/lock/Initialize(var/our_id) + if(!isobj(parent)) + return COMPONENT_INCOMPATIBLE + var/obj/O = parent + O.trigger_special(FALSE,TRUE) + if(our_id) + lock_id = our_id + RegisterSignal(parent, COMSIG_KEY_ATTACK , PROC_REF(toggle_lock)) + +/datum/component/lock/proc/toggle_lock() + var/obj/O = parent + if(!istype(args[2],/obj/item/key)) + return + var/obj/item/key/K = args[2] + var/mob/user = args[3] + if(K.lock_id == lock_id || K.master_key) + if(user) + to_chat(user,SPAN_NOTICE("\The [K] fits cleanly into \the [O]. You give it a firm turn.")) + O.visible_message(SPAN_NOTICE("Something clicks inside of \the [O]."),runemessage = "!") + if(O.trigger_special(K.one_time)) + K.unlocked(user) + else + if(user) + to_chat(user,SPAN_DANGER("\The [K] doesn't fit into \the [O]...")) + +/obj/lock_adder + icon = 'icons/rogue-star/keys.dmi' + icon_state = "key" + lock_id = "CHANGE ME" -/obj/event_obstical/unlock_with_key(key_id,var/obj/item/key/K) +/obj/lock_adder/Initialize(mapload) . = ..() + trigger() + qdel(src) - if(key_id == id || (. && id)) - if(K && !density) - if(K.one_time) //Don't destroy keys for doors that are already unlocked. - return FALSE - post_trigger() - return TRUE +/obj/lock_adder/proc/trigger() + for(var/obj/thing in src.loc.contents) + if(istype(thing,/obj/structure) || istype(thing, /obj/machinery)) + thing.LoadComponent(/datum/component/lock,lock_id) diff --git a/code/game/objects/structures/simple_doors.dm b/code/game/objects/structures/simple_doors.dm index f032855e3af..1c1388359f4 100644 --- a/code/game/objects/structures/simple_doors.dm +++ b/code/game/objects/structures/simple_doors.dm @@ -17,7 +17,7 @@ var/knock_hammer_sound = 'sound/weapons/sonic_jackhammer.ogg' var/locked = FALSE //has the door been locked? - var/lock_id = null //does the door have an associated key? + lock_id = null //does the door have an associated key? //RS EDIT var/keysound = 'sound/items/toolbelt_equip.ogg' /obj/structure/simple_door/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) @@ -238,6 +238,12 @@ return SSradiation.radiate(src, round(material.radioactivity/3)) +//RS ADD +/obj/structure/simple_door/proc/toggle_lock() + visible_message(SPAN_NOTICE("\The [src] clunks as it is [locked ? "unlocked" : "locked"].")) + locked = !locked + playsound(src, keysound,100, 1) + /obj/structure/simple_door/iron/Initialize(mapload,var/material_name) ..(mapload, material_name || "iron") diff --git a/code/modules/admin/admin_verb_lists_vr.dm b/code/modules/admin/admin_verb_lists_vr.dm index 4ad5687ba43..d8e80b438aa 100644 --- a/code/modules/admin/admin_verb_lists_vr.dm +++ b/code/modules/admin/admin_verb_lists_vr.dm @@ -188,7 +188,8 @@ var/list/admin_verbs_fun = list( /client/proc/admin_lighting_manager, // RS ADD: New Lighting Manager Panel (Lira, October 2025) /client/proc/tag_game, //RS ADD /client/proc/report_all_objectives, //RS ADD - /client/proc/reset_multipoint_trigger //RS ADD + /client/proc/reset_multipoint_trigger, //RS ADD + /client/proc/toggle_dungeon_maker //RS ADD ) diff --git a/icons/rogue-star/dungeon_maker.dmi b/icons/rogue-star/dungeon_maker.dmi new file mode 100644 index 0000000000000000000000000000000000000000..1791d6edc40886133daef72e4628648cce5fb0aa GIT binary patch literal 797 zcmV+&1LFLNP)V=-0C=2*%CQQAFce1NIrk}kbT6&CW+{=9LEj<9YjbIhkc9U29fWSh?Hu@O z-1~|m%F*^W+K_2h;a%wvbJ<)vCqv#^8>$nG;PpvbTo3roCa3ZN1j92H3H2J}(w;DHxHkKw>uT({ zPKP7CE=%Ro13We>Ny)J;ZKQldt*QW|8hEDDOeI-78V@uK0UlCXxdIPqbN!zAzf_YC zD9IIg$a>0FoKT`F09I1Mdnn{4GG57+@Nqy%!+OAnjRR8h?q%V7z}b2LjIO|FJ^KT2 b^Zfzepkg>J4u#7>00000NkvXXu0mjfN>FVB literal 0 HcmV?d00001 diff --git a/icons/rogue-star/keys.dmi b/icons/rogue-star/keys.dmi index 3cf96cd774b438fc111b0e0dd71c7fbeb62bab1f..8a9b001888e1275cdf1fe2f658468dbd7792a78f 100644 GIT binary patch literal 1982 zcma)7c{JOJ7XF3W(r9Qi6RoZGrL84WCR#~UtgW?-mRduerKLqt4PsYIRfn3fCED7a z(pE)k-)b4u`iMa=6d@(VE|{nNZ_at=y!qqa@7#aBbH97f{q7Sh3u9sMIWPbK!louT zn`0z>3qk(l+=h1RIfk+@+gm|6&p>>D_x&Jme?I^S%}pK91Gi~F_-6h<$C(ha7K$15 zu!LEeni%|*CxYh9h)WIO2^XErs7oz7GXrY{VHe1nqO0OwUY7q-aTeq7oXd7d zQkbp(B4-thijH?rWnW7oJ;53%v}a7pN$t?Af4Dw!apawCj}81q?U9{aU(&`&&QApa zcZv@hJr}h!Ps-6%*vcHukhl<)gKG*wF`!|`J&<}xd7OXjhf)CG?=!{e+lJ<@v^62SH<*!5G)JG!j zL&ZDcEZ%oy$-vMD>oC_8YqQd08hO(IfV2{Pov%`YKEXqcV{(_WXzPq7`el-)k%Fns z8@exmec@TvozMG=JZgYF-iS>yY^tiTKdhxosd|{*g4nkhuo9f(Xywvo&p@#(1HTiR z#*k(SO(5_S&@0NT@ec}75#3IWFFW67B|B@=Q=uh}Qbw4FoywMqfCxru8Vw#8{5v$H zLEW8XLRngXNxsggjK41z?-f?79so`x%~jpMOLG*L40=PXd!1MyZyHU2B!gsSdk_<6 zK4wA>y!RfupZ)}-AB*mp=X^Q=<2TA|x}z2|$0x6L9#J*g9@fx7MAN26%86Lnn3qH9 zS@;HLe9awg)ifGNb#^$)ZEmh-U)!iLxx0okUAkSv8Q>~r+y5d>a{n@Ms(*WsZNt5y ze5*<*b%WQoy$dQzh7wcLCqC(*r!N`M6)A|%iyIwbaN+wYmc^J&c{D@v@sJqxp4w7Y zXJ>)X>G1(2hT3M+g)`ndC_ZE#6#HP^DL@6CAHeHXo?I}yQP&H#DpGZ^T7*Z*-(dGd zuZAzl%+0;M-SozQ7M{cWJ)9(1Amv)eaM#V#))nq*qX-t%>#r`>a-Tc4h|Z#q9%AVF z5D}FimzUl@BQ&wG19N|BtMRl0qQf1}ZW@8U!|}#YeE(!uz3D1$(^fkBMdc-V=uKbo znMvep8T`UH&e!Q{@D-rh10ggu^+)xn+(hH1?^()K^*pAmhKC??CXz_ZCBiGA#Z7~?5$Gytvb#vi zRX5Ms90~xh1_d2_ABzZqTPdNq{W^w%er7SX!wczSh&l1Sk9|Bt-jF`ZE8=&uw-RFd ze5{FkM^PX$sVOSd(eL2(v1bj{c@YSzmN5le{oL&$qEB!P=^& zb&XY^=sX`T8ocn_=5mJSeI5e{kb+Wn|6*#T?V$(rd~F*-+6yv0dKpNauN#2&LD!s| zvwbz9Mr%VOBKUz`f)+R2L}#`7<7WadaI5@h=guPk!x?LUdf-c-I)H$DGFda{Glg2s zn&f{)Jz{;})_o@6ceZYB#mL1l308KEP$|2*dBle=%tauCtE-2V-lyjC4Oo@b ze@#}~TGAER9p+(GxwbN<=(*u6Is!Y=x8Ivar=nNL4s-9f#?*F>j-;CHTM5AKA(!U# zmKy)T>D`rnrhwiJ4#OhaKz)WVSE9R;>&UZm^_bF?-yC=FDg^x7miXf!8!~S2+XW%btzmpaNW=U^O8>ll zc2R?cby}WR7^&So3Tdt>m3Gf73}wsR+WH~6-92&E@H$Kn(9dKmmIk=Yt%r{B7?~jX zyEY`)Y_?Jq+#?og4r;D|A3RxrtsNabO-K*`ik&cdKMo<=+uC^Z^Yi0X-7|1_07VNi zK=LbxtXNuFeypi!SzJ_FU0q$<-X>r$X!I%D?%l*z02T=6aMgMO&2er3R+W0;8pLY1 zqm%bpchU*A+j+bF!^1;$vwj=A-i*eOaQs4V{${Ov(m9?<&k6N| RSC1buU}|WAtGMPG{a<{prbhq( literal 1059 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7OGo-BlqGB`&GO$wiq3C7Jno3=9=> zg2M`mO22;zF8KKMiI%sn*10q1gExd4Tr__0Nawtd=E(pc$L!IgGahGs&TBLlba@MT z2Z`p0PT3T!J#~jguu*pxkjJyr;&PWzaj;@eis`O4mz73JUQ-PNSNU`VrFu=#GHzNm z$>?(F$ClPjO{?d3H+8R97iU=ZBgwrqzsj6}fjQXI#WAE}&fB@Md8-WsTF>99-Y&HM zFn3~QWXJr?ED_$Xeh08w2C6JDwUsTh(^Ts4Nd6;v=Dbh#QV|Zh`Ww6zaLkBMob2kt+=KQ+j% z%H&P#qkJpYr*mFg^cfh>kX)Ae$@lwne`~L*!e>8LGu^52;+)YNuP5H~xFhQIrW|`) zYsH|N|NGSct!fDBnkjj#ki||nd6wFeDG6z(i)Jl*e(_?f$H!o=-H-EUF?(rF3tDyk zwJ1Zu?z=UYi}-vr@BS^{tA0Y`_f_?l+acBD?QDdpi5;e6ekB_8i_q+_U(SQaM8K1xv!c3yy~tMUG}RX;e}fdp19Z@=Q(fU9Andc zebC5i5!mou_PwC~vwo$P24i8b1Gi4}nS7dSFn!@XIU{wKg|+`*%ihoHcamV7SN~+E zdD=FP%+H0#3ctwA6KJtuJ)5b>|I@tqx&CQ0_l}7hKZZJS6y$BcU8J&QE`LqmpS_lr z`x_0Iyfn>t4mY!$clvxzzD@DU3w9<>hB)tzJ3k#Z{Fi@FxK|b!J%v zL_d4n74|u65clqP%A5w0fSTnC_mb5~G+|zmT^*dw4By;=z2#Ol5 zzh&Zkx&Mguq)^7;Bt6$h@7nXVvvCt;Elv z0o7k0zV^2_d~ieSq*+;U$h`j?1>4@d@Gm(QeU@?mMB}rJ3*WWO{r0hVx7@@DObzE( zuUl4SsH4dG Date: Tue, 21 Apr 2026 20:54:51 -0400 Subject: [PATCH 2/8] 2054 --- code/game/Rogue Star/dehydration.dm | 53 ++++++ code/game/Rogue Star/obj/food_cubes.dm | 156 ++++++++++++++++++ code/game/Rogue Star/obj/keys.dm | 114 +++++++++++-- code/modules/food/food/snacks_vr.dm | 29 +++- .../reagents/reagents/food_drinks_vr.dm | 10 +- icons/rogue-star/foodx32.dmi | Bin 0 -> 297 bytes icons/rogue-star/misc.dmi | Bin 20701 -> 22483 bytes vorestation.dme | 2 + 8 files changed, 339 insertions(+), 25 deletions(-) create mode 100644 code/game/Rogue Star/dehydration.dm create mode 100644 code/game/Rogue Star/obj/food_cubes.dm create mode 100644 icons/rogue-star/foodx32.dmi diff --git a/code/game/Rogue Star/dehydration.dm b/code/game/Rogue Star/dehydration.dm new file mode 100644 index 00000000000..415a4b89c3b --- /dev/null +++ b/code/game/Rogue Star/dehydration.dm @@ -0,0 +1,53 @@ +//RS FILE +/datum/reagent/dry + name = "dehydrated nutrint mix" + id = "dryfood" + description = "A specialised chemical mix that, once activated will expand into a prepared food item! Ingesting this before adding water is not advised, as the chemical reaction will take water from the body and dehydrate the imbiber." + taste_description = "meat chalk" + reagent_state = SOLID + color = "#523026" + +/datum/reagent/dry/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) + . = ..() + if(M.isSynthetic(M)) + return + M.add_modifier(/datum/modifier/dehydrated) + M.bloodstr.remove_reagent(id, volume) + +/datum/modifier/dehydrated + name = "dehydrated" + desc = "You're all dried out! You really need some water!" + + on_created_text = "Your mouth and eyes are dry, and you can feel a headache forming! You feel so weak... you could really use a drink..." + on_expired_text = "That's it!!! The water was just what you needed! You're feeling much better now." + + incoming_damage_percent = 1.5 + incoming_healing_percent = 0.5 + outgoing_melee_damage_percent = 0.5 + slowdown = 4 + evasion = -50 + accuracy = -50 + accuracy_dispersion = 20 + metabolism_percent = 4.0 + attack_speed_percent = 4 + pulse_modifier = 1.5 + +/datum/modifier/dehydrated/New(new_holder, new_origin) + . = ..() + holder.throw_alert("dehydrated", /obj/screen/alert/dehydrated) +/datum/modifier/dehydrated/expire(silent) + . = ..() + holder.clear_alert("dehydrated") + +/obj/screen/alert/dehydrated + name = "Dehydrated" + desc = "You could really use some water..." + icon = 'icons/rogue-star/misc.dmi' + icon_state = "dehydrated" + +/datum/reagent/water/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) + var/datum/modifier/dehydrated/D = M.get_modifier_of_type(/datum/modifier/dehydrated) + if(D) + D.expire() + else + ..() diff --git a/code/game/Rogue Star/obj/food_cubes.dm b/code/game/Rogue Star/obj/food_cubes.dm new file mode 100644 index 00000000000..1f93f2f52b0 --- /dev/null +++ b/code/game/Rogue Star/obj/food_cubes.dm @@ -0,0 +1,156 @@ +//RS FILE +/obj/item/weapon/reagent_containers/food/snacks/cube/d_food + name = "dehydrated food cube" + desc = "Some kind dehydrated food." + icon = 'icons/rogue-star/foodx32.dmi' + icon_state = "cube" + color = "#58482f" + food_type = list( + /obj/item/weapon/reagent_containers/food/snacks/applepie, + /obj/item/weapon/reagent_containers/food/snacks/appletart, + /obj/item/weapon/reagent_containers/food/snacks/bacon_and_eggs, + /obj/item/weapon/reagent_containers/food/snacks/bacon_flatbread, + /obj/item/weapon/reagent_containers/food/snacks/bangersandmash, + /obj/item/weapon/reagent_containers/food/snacks/bearburger, + /obj/item/weapon/reagent_containers/food/snacks/bearstew, + /obj/item/weapon/reagent_containers/food/snacks/benedict, + /obj/item/weapon/reagent_containers/food/snacks/bibimbap, + /obj/item/weapon/reagent_containers/food/snacks/bigbiteburger, + /obj/item/weapon/reagent_containers/food/snacks/blackpudding, + /obj/item/weapon/reagent_containers/food/snacks/blt, + /obj/item/weapon/reagent_containers/food/snacks/breakfast_wrap, + /obj/item/weapon/reagent_containers/food/snacks/burrito, + /obj/item/weapon/reagent_containers/food/snacks/burrito_cheese, + /obj/item/weapon/reagent_containers/food/snacks/burrito_cheese_spicy, + /obj/item/weapon/reagent_containers/food/snacks/burrito_hell, + /obj/item/weapon/reagent_containers/food/snacks/burrito_mystery, + /obj/item/weapon/reagent_containers/food/snacks/burrito_spicy, + /obj/item/weapon/reagent_containers/food/snacks/burrito_vegan, + /obj/item/weapon/reagent_containers/food/snacks/cheeseburger, + /obj/item/weapon/reagent_containers/food/snacks/cheeseburrito, + /obj/item/weapon/reagent_containers/food/snacks/cherrypie, + /obj/item/weapon/reagent_containers/food/snacks/chickenfillet, + /obj/item/weapon/reagent_containers/food/snacks/chickenmomo, + /obj/item/weapon/reagent_containers/food/snacks/chickennoodlesoup, + /obj/item/weapon/reagent_containers/food/snacks/chilicheesefries, + /obj/item/weapon/reagent_containers/food/snacks/cinnamonbun, + /obj/item/weapon/reagent_containers/food/snacks/clubsandwich, + /obj/item/weapon/reagent_containers/food/snacks/crab_legs, + /obj/item/weapon/reagent_containers/food/snacks/crabmeat, + /obj/item/weapon/reagent_containers/food/snacks/cubancarp, + /obj/item/weapon/reagent_containers/food/snacks/curryrice, + /obj/item/weapon/reagent_containers/food/snacks/custardbun, + /obj/item/weapon/reagent_containers/food/snacks/donerkebab, + /obj/item/weapon/reagent_containers/food/snacks/egg_pancake, + /obj/item/weapon/reagent_containers/food/snacks/eggbowl, + /obj/item/weapon/reagent_containers/food/snacks/enchiladas, + /obj/item/weapon/reagent_containers/food/snacks/father_breakfast, + /obj/item/weapon/reagent_containers/food/snacks/fish_taco, + /obj/item/weapon/reagent_containers/food/snacks/fishandchips, + /obj/item/weapon/reagent_containers/food/snacks/fishburger, + /obj/item/weapon/reagent_containers/food/snacks/fishfingers, + /obj/item/weapon/reagent_containers/food/snacks/friedegg, + /obj/item/weapon/reagent_containers/food/snacks/friedrice, + /obj/item/weapon/reagent_containers/food/snacks/fuegoburrito, + /obj/item/weapon/reagent_containers/food/snacks/goulash, + /obj/item/weapon/reagent_containers/food/snacks/greencurry, + /obj/item/weapon/reagent_containers/food/snacks/hotandsoursoup, + /obj/item/weapon/reagent_containers/food/snacks/jellyburger, + /obj/item/weapon/reagent_containers/food/snacks/jellysandwich, + /obj/item/weapon/reagent_containers/food/snacks/kitsuneudon, + /obj/item/weapon/reagent_containers/food/snacks/lasagna, + /obj/item/weapon/reagent_containers/food/snacks/lobstercooked, + /obj/item/weapon/reagent_containers/food/snacks/macncheese, + /obj/item/weapon/reagent_containers/food/snacks/meat_pocket, + /obj/item/weapon/reagent_containers/food/snacks/meatballspagetti, + /obj/item/weapon/reagent_containers/food/snacks/meatbun, + /obj/item/weapon/reagent_containers/food/snacks/meatpie, + /obj/item/weapon/reagent_containers/food/snacks/milosoup, + /obj/item/weapon/reagent_containers/food/snacks/omelette, + /obj/item/weapon/reagent_containers/food/snacks/omurice/heart, + /obj/item/weapon/reagent_containers/food/snacks/pancakes, + /obj/item/weapon/reagent_containers/food/snacks/pastatomato, + /obj/item/weapon/reagent_containers/food/snacks/pie, + /obj/item/weapon/reagent_containers/food/snacks/poachedegg, + /obj/item/weapon/reagent_containers/food/snacks/porkbowl, + /obj/item/weapon/reagent_containers/food/snacks/red_sun_special, + /obj/item/weapon/reagent_containers/food/snacks/redcurry, + /obj/item/weapon/reagent_containers/food/snacks/ribplate, + /obj/item/weapon/reagent_containers/food/snacks/risotto, + /obj/item/weapon/reagent_containers/food/snacks/risottoballs, + /obj/item/weapon/reagent_containers/food/snacks/roastbeef, + /obj/item/weapon/reagent_containers/food/snacks/sandwich, + /obj/item/weapon/reagent_containers/food/snacks/sashimi, + /obj/item/weapon/reagent_containers/food/snacks/spicedmeatbun, + /obj/item/weapon/reagent_containers/food/snacks/stew, + /obj/item/weapon/reagent_containers/food/snacks/stuffing, + /obj/item/weapon/reagent_containers/food/snacks/superbiteburger, + /obj/item/weapon/reagent_containers/food/snacks/sweet_and_sour, + /obj/item/weapon/reagent_containers/food/snacks/tofuburger, + /obj/item/weapon/reagent_containers/food/snacks/tofupie, + /obj/item/weapon/reagent_containers/food/snacks/tofurkey, + /obj/item/weapon/reagent_containers/food/snacks/tomatosoup, + /obj/item/weapon/reagent_containers/food/snacks/vegetablesoup, + /obj/item/weapon/reagent_containers/food/snacks/yellowcurry) + +/obj/item/weapon/reagent_containers/food/snacks/cube/d_food/examine(mob/user) + . = ..() + .+= SPAN_OCCULT("There is a message pressed into the side of the cube:") + .+= SPAN_DANGER("Do not eat before adding water.") + +/obj/item/weapon/reagent_containers/food/snacks/cube/d_food/Initialize() + . = ..() + reagents.add_reagent("dryfood", 5) + +/obj/item/weapon/storage/box/wings/tray/randomfood + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/cube/d_food = 8) + +/obj/item/weapon/reagent_containers/food/snacks/cube/d_food/lasagna + name = "lasagna cube" + desc = "Lasagna dehydrated into little cubes!" + icon = 'icons/rogue-star/foodx32.dmi' + icon_state = "cube" + color = "#857a56" + food_type = /obj/item/weapon/reagent_containers/food/snacks/lasagna + +/obj/item/weapon/storage/box/wings/tray/lasagna + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/cube/d_food/lasagna = 8) + +/obj/item/weapon/reagent_containers/food/snacks/cube/d_food/breakfast + name = "breakfast cube" + desc = "Little cubes of breakfast!" + icon = 'icons/rogue-star/foodx32.dmi' + icon_state = "cube" + color = "#b9a35a" + food_type = list( + /obj/item/weapon/reagent_containers/food/snacks/appletart, + /obj/item/weapon/reagent_containers/food/snacks/bacon_and_eggs, + /obj/item/weapon/reagent_containers/food/snacks/benedict, + /obj/item/weapon/reagent_containers/food/snacks/breakfast_wrap, + /obj/item/weapon/reagent_containers/food/snacks/father_breakfast, + /obj/item/weapon/reagent_containers/food/snacks/omelette, + /obj/item/weapon/reagent_containers/food/snacks/omurice/heart, + /obj/item/weapon/reagent_containers/food/snacks/pancakes, + /obj/item/weapon/reagent_containers/food/snacks/waffles + ) + +/obj/item/weapon/storage/box/wings/tray/breakfast + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/cube/d_food/breakfast = 8) + +/obj/item/weapon/reagent_containers/food/snacks/cube/d_food/tofu + name = "tofu cube" + desc = "Cubes of tofu food!" + icon = 'icons/rogue-star/foodx32.dmi' + icon_state = "cube" + color = "#ddcc92" + food_type = list( + /obj/item/weapon/reagent_containers/food/snacks/slice/tofubread/filled, + /obj/item/weapon/reagent_containers/food/snacks/tofu, + /obj/item/weapon/reagent_containers/food/snacks/tofuburger, + /obj/item/weapon/reagent_containers/food/snacks/tofukabob, + /obj/item/weapon/reagent_containers/food/snacks/tofupie, + /obj/item/weapon/reagent_containers/food/snacks/tofurkey + ) + +/obj/item/weapon/storage/box/wings/tray/tofu + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/cube/d_food/tofu = 8) diff --git a/code/game/Rogue Star/obj/keys.dm b/code/game/Rogue Star/obj/keys.dm index 8955ad3eb82..433072d3830 100644 --- a/code/game/Rogue Star/obj/keys.dm +++ b/code/game/Rogue Star/obj/keys.dm @@ -182,8 +182,105 @@ post_trigger() return TRUE +/obj/component_adder //This base type doesn't do anything! + name = "component adder" + desc = "You shouldn't see this." + icon = 'icons/rogue-star/misc.dmi' + icon_state = "adder" + plane = PLANE_ADMIN_SECRET + color = "#1ae200" + var/component_type + var/id + var/list/valid_types = list() + var/static/list/overlays_cache = list() + +/obj/component_adder/New(loc, new_id) + . = ..() + if(new_id) + id = new_id + +/obj/component_adder/Initialize(mapload) + . = ..() + seek_valid_target() + qdel(src) + +/obj/component_adder/proc/seek_valid_target() + if(!component_type) + return + var/turf/T = get_turf(src) + for(var/atom/thing in T.contents) + if(thing == src) + continue + for(var/type_check in valid_types) + if(istype(thing,type_check)) + var/overlay_state = consider_overlay_state(thing) + if(!special_check(thing)) + add_component(thing) + do_overlay(thing,overlay_state) + +/obj/component_adder/proc/add_component(var/atom/target) + if(!target) + return + target.LoadComponent(component_type,id) + +/obj/component_adder/proc/do_overlay(var/atom/target,var/overlay_state) + if(!target) + return + if(!overlay_state) + overlay_state = "[icon_state]_s" + var/key = "[overlay_state]-[color]" + var/image/overlay = overlays_cache[key] + if(!overlay) + overlay = image(icon,null,overlay_state) + overlay.color = color + overlay.plane = PLANE_ADMIN_SECRET + overlay.appearance_flags = RESET_COLOR|KEEP_APART|PIXEL_SCALE + overlays_cache[key] = overlay + target.add_overlay(overlay) + +/obj/component_adder/proc/consider_overlay_state(var/atom/consider) + return null + +/obj/component_adder/proc/special_check(var/atom/consider) + return FALSE + +/obj/component_adder/lock + name = "lock component" + icon_state = "lock" + component_type = /datum/component/lock + id = "lock" + valid_types = list( + /obj/item/key, + /obj/machinery/door/airlock, + /obj/structure/simple_door, + /obj/event_obstical, + /obj/machinery/door/blast + ) + +/obj/component_adder/lock/consider_overlay_state(var/atom/consider) + if(istype(consider,/obj/item/key)) + return "key" + return null + +/obj/component_adder/lock/special_check(var/atom/consider) + if(istype(consider,/obj/item/key)) + var/obj/item/key/K = consider + K.lock_id = id + return TRUE + return FALSE + +/obj/component_adder/trigger + name = "trigger component" + icon_state = "trigger" +/obj/component_adder/link + name = "link component" + icon_state = "link" + + +//GetComponent(component type) + +/////////////////////////////////////// COMPONENTS BELOW HERE ////////////////////////////////////////// /datum/component/lock - var/locked = TRUE var/lock_id = "LOCK" /datum/component/lock/Initialize(var/our_id) @@ -210,18 +307,3 @@ else if(user) to_chat(user,SPAN_DANGER("\The [K] doesn't fit into \the [O]...")) - -/obj/lock_adder - icon = 'icons/rogue-star/keys.dmi' - icon_state = "key" - lock_id = "CHANGE ME" - -/obj/lock_adder/Initialize(mapload) - . = ..() - trigger() - qdel(src) - -/obj/lock_adder/proc/trigger() - for(var/obj/thing in src.loc.contents) - if(istype(thing,/obj/structure) || istype(thing, /obj/machinery)) - thing.LoadComponent(/datum/component/lock,lock_id) diff --git a/code/modules/food/food/snacks_vr.dm b/code/modules/food/food/snacks_vr.dm index 7b877d68bbd..39682ef5283 100644 --- a/code/modules/food/food/snacks_vr.dm +++ b/code/modules/food/food/snacks_vr.dm @@ -465,7 +465,14 @@ /obj/item/weapon/reagent_containers/food/snacks/cube/proc/Expand() src.visible_message("\The [src] expands!") - new food_type(get_turf(src)) + var/turf/T = get_turf(src) //RS EDIT START + var/food_actual + if(islist(food_type)) + food_actual = pickweight(food_type) + else + food_actual = food_type + new food_actual(T) + playsound(T, 'sound/effects/bubbles.ogg', 50, 1) //RS EDIT END qdel(src) /obj/item/weapon/reagent_containers/food/snacks/cube/on_reagent_change() @@ -522,8 +529,24 @@ /obj/item/weapon/reagent_containers/food/snacks/cube/protein = 4, /obj/item/weapon/reagent_containers/food/snacks/cube/nutriment = 4 ) - can_hold = list(/obj/item/weapon/reagent_containers/food/snacks/cube/protein, - /obj/item/weapon/reagent_containers/food/snacks/cube/nutriment) + can_hold = list(/obj/item/weapon/reagent_containers/food/snacks/cube) //RS EDIT + +/obj/item/weapon/storage/box/wings/tray/water_act(amount) //RS ADD START + visible_message(SPAN_DANGER("\The [src] gets wet, soaking its contents!"),runemessage = "splash") + START_PROCESSING(SSfastprocess, src) + +/obj/item/weapon/storage/box/wings/tray/process() + if(src.contents.len <= 0) + STOP_PROCESSING(SSfastprocess,src) + return + if(prob(20)) + var/howmany = contents.len + var/obj/item/weapon/reagent_containers/food/snacks/cube/C = pick(contents) + if(istype(C,/obj/item/weapon/reagent_containers/food/snacks/cube)) + C.Expand() + if(howmany == contents.len) //We have something in there that isn't expanding so let's stop trying instead of trying forever + STOP_PROCESSING(SSfastprocess,src) + update_icon() //RS ADD END /obj/item/weapon/reagent_containers/food/snacks/carpmeat/sif //Making fish meat non-toxic! As advised by Ascian! toxin_type = null diff --git a/code/modules/reagents/reagents/food_drinks_vr.dm b/code/modules/reagents/reagents/food_drinks_vr.dm index 94ae4c71e2b..b96a8f7fd43 100644 --- a/code/modules/reagents/reagents/food_drinks_vr.dm +++ b/code/modules/reagents/reagents/food_drinks_vr.dm @@ -1,20 +1,18 @@ -/datum/reagent/toxin/meatcolony +/datum/reagent/dry/meatcolony //RS EDIT name = "A colony of meat cells" id = "meatcolony" - description = "Specialised cells designed to produce a large amount of meat once activated, whilst manufacturers have managed to stop these cells from taking over the body when ingested, it's still poisonous." + description = "Specialised cells designed to produce a large amount of meat once activated, whilst manufacturers have managed to stop these cells from taking over the body when ingested, they will still pull any water they come into contact with, and will cause dehydration." //RS EDIT taste_description = "a fibrous mess" reagent_state = LIQUID color = "#ff2424" - strength = 10 -/datum/reagent/toxin/plantcolony +/datum/reagent/dry/plantcolony //RS EDIT name = "A colony of plant cells" id = "plantcolony" - description = "Specialised cells designed to produce a large amount of nutriment once activated, whilst manufacturers have managed to stop these cells from taking over the body when ingested, it's still poisonous." + description = "Specialised cells designed to produce a large amount of nutriment once activated, whilst manufacturers have managed to stop these cells from taking over the body when ingested, they will still pull any water they come into contact with, and will cause dehydration." //RS EDIT taste_description = "a fibrous mess" reagent_state = LIQUID color = "#7ce01f" - strength = 10 /datum/reagent/nutriment/grubshake name = "Grub shake" diff --git a/icons/rogue-star/foodx32.dmi b/icons/rogue-star/foodx32.dmi new file mode 100644 index 0000000000000000000000000000000000000000..6f2c56f5dbb91547613b3fbd465687605ece894a GIT binary patch literal 297 zcmeAS@N?(olHy`uVBq!ia0vp^4nVBH!VDw>HYaZfQv3lvA+84w8~`%^|Nr06&~W$e z-ABh21Au(Sk|4ie28U-i(tw=2s*s2hm(=3qqRfJl%=|nChKf1CVFg8{-@gPGeEj-E z%Uf6L+?n&i8$t~(8b5fXbKXbuBtub8?+y#&Amhu%UJ7#_O*%3uL?Kvnr;&SSx3TKx z6%r?mpFHx>_B!L+x8qc*J8%oJPh3nl_zxfyUYUG?C0s?7-G?zoFKv4 zEXT3nqvS;v3DJup91A%)G#9=~2r+XKOb9V@x_a=%lAcuuJWmRQHg+4XViHdcVdYVJ qQIlR0zWB4OBnQKn0tr?Vc7|#Jmdo06LUsX7X7F_Nb6Mw<&;$T2v26VS literal 0 HcmV?d00001 diff --git a/icons/rogue-star/misc.dmi b/icons/rogue-star/misc.dmi index 8812cdae9362ca4d2a7844e5116802c99a95835b..cbca6fae9fb1e0465317a266f2be9af80aea332d 100644 GIT binary patch literal 22483 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 diff --git a/vorestation.dme b/vorestation.dme index 5c16a5366aa..613d1fd93b4 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -1658,6 +1658,7 @@ #include "code\game\objects\structures\stool_bed_chair_nest\wheelchair.dm" #include "code\game\objects\structures\stool_bed_chair_nest\wheelchair_item.dm" #include "code\game\Rogue Star\bus_spawner.dm" +#include "code\game\Rogue Star\dehydration.dm" #include "code\game\Rogue Star\deployable_teleporter.dm" #include "code\game\Rogue Star\dungeon_maker.dm" #include "code\game\Rogue Star\easter.dm" @@ -1678,6 +1679,7 @@ #include "code\game\Rogue Star\abilities\return.dm" #include "code\game\Rogue Star\catborgs\catborgs.dm" #include "code\game\Rogue Star\fluff\sari-outfit-fluff.dm" +#include "code\game\Rogue Star\obj\food_cubes.dm" #include "code\game\Rogue Star\mob\seething.dm" #include "code\game\Rogue Star\obj\keys.dm" #include "code\game\Rogue Star\obj\listener.dm" From d0235d2d3d892b9b95f5f77d07af217a9a105911 Mon Sep 17 00:00:00 2001 From: VerySoft Date: Wed, 29 Apr 2026 12:22:13 -0400 Subject: [PATCH 3/8] 1222 --- code/__defines/dcs/signals.dm | 2 + .../Rogue Star/_Component/component_adder.dm | 116 +++++++ .../_Component/dungeon_component.dm | 130 ++++++++ code/game/Rogue Star/obj/keys.dm | 161 +--------- code/game/Rogue Star/obj/listener.dm | 6 +- code/game/machinery/doors/airlock.dm | 16 + code/game/objects/crystal_key.dm | 298 ++++++++---------- code/game/objects/structures/simple_doors.dm | 28 +- icons/rogue-star/component_adder.dmi | Bin 0 -> 489 bytes vorestation.dme | 2 + 10 files changed, 425 insertions(+), 334 deletions(-) create mode 100644 code/game/Rogue Star/_Component/component_adder.dm create mode 100644 code/game/Rogue Star/_Component/dungeon_component.dm create mode 100644 icons/rogue-star/component_adder.dmi diff --git a/code/__defines/dcs/signals.dm b/code/__defines/dcs/signals.dm index 259824d9056..7f8f23845fa 100644 --- a/code/__defines/dcs/signals.dm +++ b/code/__defines/dcs/signals.dm @@ -792,4 +792,6 @@ #define COMSIG_VORE_HEALTHBAR_CLEANUP "vore_healthbar_cleanup" #define HIDE_AND_SEEK_ROUND_END "round_end" #define COMSIG_KEY_ATTACK "key_event" +#define COMSIG_DUNGEON_TRIGGER "dungeon_trigger" +#define COMSIG_DUNGEON_UNTRIGGER "dungeon_untrigger" //RS ADD END diff --git a/code/game/Rogue Star/_Component/component_adder.dm b/code/game/Rogue Star/_Component/component_adder.dm new file mode 100644 index 00000000000..64ce478cd63 --- /dev/null +++ b/code/game/Rogue Star/_Component/component_adder.dm @@ -0,0 +1,116 @@ +//RS FILE +/obj/component_adder //This base type doesn't do anything! + name = "component adder" + desc = "You shouldn't see this." + icon = 'icons/rogue-star/component_adder.dmi' + icon_state = "adder" + plane = PLANE_ADMIN_SECRET + color = "#1ae200" + var/component_type + var/id = "REPLACE ME" + var/list/valid_types = list() + var/static/list/overlays_cache = list() + +/obj/component_adder/New(loc, new_id) + . = ..() + if(new_id) + id = new_id + +/obj/component_adder/Initialize(mapload) + . = ..() + seek_valid_target() + qdel(src) + +/obj/component_adder/proc/seek_valid_target() + if(!component_type) + return + var/turf/T = get_turf(src) + for(var/atom/thing in T.contents) + if(thing == src) + continue + for(var/type_check in valid_types) + if(istype(thing,type_check)) + var/overlay_state = consider_overlay_state(thing) + if(!special_check(thing)) + add_component(thing) + do_overlay(thing,overlay_state) + +/obj/component_adder/proc/add_component(var/atom/target) + if(!target) + return + . = target.LoadComponent(component_type,id) + +/obj/component_adder/proc/do_overlay(var/atom/target,var/overlay_state) + if(!target) + return + if(!overlay_state) + overlay_state = "[icon_state]_s" + var/key = "[overlay_state]-[color]" + var/image/overlay = overlays_cache[key] + if(!overlay) + overlay = image(icon,null,overlay_state) + overlay.color = color + overlay.plane = PLANE_ADMIN_SECRET + overlay.appearance_flags = RESET_COLOR|KEEP_APART|PIXEL_SCALE + overlays_cache[key] = overlay + target.add_overlay(overlay) + +/obj/component_adder/proc/consider_overlay_state(var/atom/consider) + return null + +/obj/component_adder/proc/special_check(var/atom/consider) + return FALSE + +/obj/component_adder/lock + name = "lock component" + icon_state = "lock" + component_type = /datum/component/dungeon_mechanic/lock + valid_types = list( + /obj/item/key, + /obj/machinery/door/airlock, + /obj/structure/simple_door, + /obj/dungeon_obstacle, + /obj/machinery/door/blast, + /obj/dungeon_switch + ) + +/obj/component_adder/lock/consider_overlay_state(var/atom/consider) + if(istype(consider,/obj/item/key)) + return "key" + return null + +/obj/component_adder/lock/special_check(var/atom/consider) + if(istype(consider,/obj/item/key)) + var/obj/item/key/K = consider + K.lock_id = id + return TRUE + return FALSE + +/obj/component_adder/trigger + name = "trigger component" + icon_state = "trigger" + component_type = /datum/component/dungeon_mechanic/trigger + valid_types = list( + /obj/dungeon_obstacle, + /obj/dungeon_switch, + /obj/machinery/door/airlock, + /obj/structure/simple_door, + /obj/machinery/door/blast + ) + var/onetime = FALSE + +/obj/component_adder/trigger/add_component() + var/datum/component/dungeon_mechanic/trigger/T = ..() + T.onetime = onetime + +/obj/component_adder/link + name = "link component" + icon_state = "link" + component_type = /datum/component/dungeon_mechanic/link + valid_types = list( + /obj/machinery/door/airlock, + /obj/structure/simple_door, + /obj/dungeon_obstacle, + /obj/machinery/door/blast, + /obj/dungeon_switch + ) diff --git a/code/game/Rogue Star/_Component/dungeon_component.dm b/code/game/Rogue Star/_Component/dungeon_component.dm new file mode 100644 index 00000000000..3fd1e70d717 --- /dev/null +++ b/code/game/Rogue Star/_Component/dungeon_component.dm @@ -0,0 +1,130 @@ +//RS FILE +var/global/list/dungeon_components = list() + +/datum/component/dungeon_mechanic + var/id = "REPLACE ME" + +/datum/component/dungeon_mechanic/Initialize(var/our_id) + if(!isobj(parent)) + return COMPONENT_INCOMPATIBLE + if(our_id) + id = our_id + + dungeon_components |= src + +//Locks// +/datum/component/dungeon_mechanic/lock + var/locked = TRUE + +/datum/component/dungeon_mechanic/lock/Initialize(var/our_id) + . = ..() + var/obj/O = parent + O.dungeon_lock() + RegisterSignal(parent, COMSIG_KEY_ATTACK , PROC_REF(key_interact)) + +/datum/component/dungeon_mechanic/lock/proc/key_interact() + var/obj/O = parent + if(!istype(args[2],/obj/item/key)) + return + var/obj/item/key/K = args[2] + var/mob/user = args[3] + if(K.lock_id == id || K.master_key) + if(!locked && K.one_time) + to_chat(user,SPAN_NOTICE("\The [O] is already unlocked! You don't need to use \the [K] on it.")) + return + if(user) + user.visible_message(SPAN_NOTICE("\The [user] inserts \the [K] into \the [O]..."),SPAN_NOTICE("You insert \the [K] into \the [O]...")) + if(toggle_lock(O)) + K.unlocked() + return + + if(user) + to_chat(user,SPAN_DANGER("\The [K] doesn't fit into \the [O]...")) + +/datum/component/dungeon_mechanic/lock/proc/toggle_lock(var/obj/O) + locked = !locked + if(locked) + return O.dungeon_lock() + else + return O.dungeon_unlock() + +//LINK// +/datum/component/dungeon_mechanic/link + +/datum/component/dungeon_mechanic/link/Initialize(var/our_id) + . = ..() + for(var/datum/component/dungeon_mechanic/trigger/T in dungeon_components) + if(T == src) + continue + if(T.type != /datum/component/dungeon_mechanic/trigger) + continue + if(T.id == id) + RegisterSignal(T,COMSIG_DUNGEON_TRIGGER,PROC_REF(link_trigger)) + RegisterSignal(T,COMSIG_DUNGEON_UNTRIGGER,PROC_REF(link_trigger)) + +/datum/component/dungeon_mechanic/link/Destroy(force, silent) + dungeon_components -= src + for(var/datum/component/dungeon_mechanic/trigger/T in dungeon_components) + if(id == T.id) + UnregisterSignal(T,COMSIG_DUNGEON_TRIGGER) + UnregisterSignal(T,COMSIG_DUNGEON_UNTRIGGER) + return ..() + +/datum/component/dungeon_mechanic/link/proc/link_trigger() + var/obj/O = parent + O.dungeon_trigger() + +//TRIGGER// +/datum/component/dungeon_mechanic/trigger + var/triggered = FALSE + var/onetime = FALSE +/datum/component/dungeon_mechanic/trigger/Initialize(our_id) + . = ..() + RegisterSignal(parent, COMSIG_DUNGEON_TRIGGER , PROC_REF(toggle_trigger)) + RegisterSignal(parent, COMSIG_DUNGEON_UNTRIGGER , PROC_REF(toggle_trigger)) + +/datum/component/dungeon_mechanic/trigger/Destroy(force, silent) + dungeon_components -= src + UnregisterSignal(parent, COMSIG_DUNGEON_TRIGGER) + UnregisterSignal(parent, COMSIG_DUNGEON_UNTRIGGER) + + for(var/datum/component/dungeon_mechanic/link/L in dungeon_components) + if(id == L.id) + L.UnregisterSignal(src,COMSIG_DUNGEON_TRIGGER) + return ..() + +/datum/component/dungeon_mechanic/trigger/proc/toggle_trigger() + if(triggered) + if(!onetime) + untrigger() + else + trigger() + +/datum/component/dungeon_mechanic/trigger/proc/trigger() + triggered = TRUE +/datum/component/dungeon_mechanic/trigger/proc/untrigger() + triggered = FALSE + +//////////RELATED OBJ PROCS////////// +/obj/proc/dungeon_lock() + return FALSE + +/obj/proc/dungeon_unlock() + return FALSE + +/obj/proc/dungeon_trigger() + return FALSE + +/obj/proc/islocked() + var/datum/component/dungeon_mechanic/lock/ourlock = GetComponent(/datum/component/dungeon_mechanic/lock) + if(ourlock?.locked) + return TRUE + return FALSE + +/obj/proc/cantrigger() + var/datum/component/dungeon_mechanic/trigger/trigger = GetComponent(/datum/component/dungeon_mechanic/trigger) + if(!trigger) + return FALSE + if(trigger.onetime && trigger.triggered) + return FALSE + return TRUE diff --git a/code/game/Rogue Star/obj/keys.dm b/code/game/Rogue Star/obj/keys.dm index 433072d3830..06981e39723 100644 --- a/code/game/Rogue Star/obj/keys.dm +++ b/code/game/Rogue Star/obj/keys.dm @@ -142,168 +142,9 @@ color = "#cc00ff" lock_id = "magenta" - /obj/proc/key_event(var/obj/item/key/K) if(!K) return FALSE if((K.master_key && lock_id) || lock_id == K.lock_id) - return trigger_special(K.one_time) + return dungeon_trigger(K.one_time) return FALSE - -/obj/proc/trigger_special(var/one_time = FALSE,var/only_lock = FALSE) //If one_time is true, then triggering only allows the door to be unlocked. - return FALSE //Returning true and false tells one_time keys if they should delete themselves or not - -/obj/machinery/door/airlock/trigger_special(var/one_time = FALSE,var/only_lock = FALSE) - if(locked && !only_lock) - unlock() - open() - else - if(one_time) - return FALSE - lock() - return TRUE - -/obj/structure/simple_door/trigger_special(var/one_time = FALSE,var/only_lock = FALSE) - if(one_time) - if(!locked) - return FALSE - if(locked && only_lock) - return FALSE - toggle_lock() - return TRUE - -/obj/event_obstical/trigger_special(var/one_time = FALSE,var/only_lock = FALSE) - if(one_time) - if(!density) - return FALSE - if(only_lock) - if(density) - return FALSE - post_trigger() - return TRUE - -/obj/component_adder //This base type doesn't do anything! - name = "component adder" - desc = "You shouldn't see this." - icon = 'icons/rogue-star/misc.dmi' - icon_state = "adder" - plane = PLANE_ADMIN_SECRET - color = "#1ae200" - var/component_type - var/id - var/list/valid_types = list() - var/static/list/overlays_cache = list() - -/obj/component_adder/New(loc, new_id) - . = ..() - if(new_id) - id = new_id - -/obj/component_adder/Initialize(mapload) - . = ..() - seek_valid_target() - qdel(src) - -/obj/component_adder/proc/seek_valid_target() - if(!component_type) - return - var/turf/T = get_turf(src) - for(var/atom/thing in T.contents) - if(thing == src) - continue - for(var/type_check in valid_types) - if(istype(thing,type_check)) - var/overlay_state = consider_overlay_state(thing) - if(!special_check(thing)) - add_component(thing) - do_overlay(thing,overlay_state) - -/obj/component_adder/proc/add_component(var/atom/target) - if(!target) - return - target.LoadComponent(component_type,id) - -/obj/component_adder/proc/do_overlay(var/atom/target,var/overlay_state) - if(!target) - return - if(!overlay_state) - overlay_state = "[icon_state]_s" - var/key = "[overlay_state]-[color]" - var/image/overlay = overlays_cache[key] - if(!overlay) - overlay = image(icon,null,overlay_state) - overlay.color = color - overlay.plane = PLANE_ADMIN_SECRET - overlay.appearance_flags = RESET_COLOR|KEEP_APART|PIXEL_SCALE - overlays_cache[key] = overlay - target.add_overlay(overlay) - -/obj/component_adder/proc/consider_overlay_state(var/atom/consider) - return null - -/obj/component_adder/proc/special_check(var/atom/consider) - return FALSE - -/obj/component_adder/lock - name = "lock component" - icon_state = "lock" - component_type = /datum/component/lock - id = "lock" - valid_types = list( - /obj/item/key, - /obj/machinery/door/airlock, - /obj/structure/simple_door, - /obj/event_obstical, - /obj/machinery/door/blast - ) - -/obj/component_adder/lock/consider_overlay_state(var/atom/consider) - if(istype(consider,/obj/item/key)) - return "key" - return null - -/obj/component_adder/lock/special_check(var/atom/consider) - if(istype(consider,/obj/item/key)) - var/obj/item/key/K = consider - K.lock_id = id - return TRUE - return FALSE - -/obj/component_adder/trigger - name = "trigger component" - icon_state = "trigger" -/obj/component_adder/link - name = "link component" - icon_state = "link" - - -//GetComponent(component type) - -/////////////////////////////////////// COMPONENTS BELOW HERE ////////////////////////////////////////// -/datum/component/lock - var/lock_id = "LOCK" - -/datum/component/lock/Initialize(var/our_id) - if(!isobj(parent)) - return COMPONENT_INCOMPATIBLE - var/obj/O = parent - O.trigger_special(FALSE,TRUE) - if(our_id) - lock_id = our_id - RegisterSignal(parent, COMSIG_KEY_ATTACK , PROC_REF(toggle_lock)) - -/datum/component/lock/proc/toggle_lock() - var/obj/O = parent - if(!istype(args[2],/obj/item/key)) - return - var/obj/item/key/K = args[2] - var/mob/user = args[3] - if(K.lock_id == lock_id || K.master_key) - if(user) - to_chat(user,SPAN_NOTICE("\The [K] fits cleanly into \the [O]. You give it a firm turn.")) - O.visible_message(SPAN_NOTICE("Something clicks inside of \the [O]."),runemessage = "!") - if(O.trigger_special(K.one_time)) - K.unlocked(user) - else - if(user) - to_chat(user,SPAN_DANGER("\The [K] doesn't fit into \the [O]...")) diff --git a/code/game/Rogue Star/obj/listener.dm b/code/game/Rogue Star/obj/listener.dm index 68f95bdbf1a..d80f81e2579 100644 --- a/code/game/Rogue Star/obj/listener.dm +++ b/code/game/Rogue Star/obj/listener.dm @@ -102,8 +102,9 @@ D.close() D.lock() continue - if(istype(thing,/obj/event_obstical)) - var/obj/event_obstical/O = thing +/* + if(istype(thing,/obj/dungeon_obstacle)) + var/obj/dungeon_obstacle/O = thing if(listener_id == O.id) if(trigger) if(O.density) @@ -112,6 +113,7 @@ if(!O.density) O.post_trigger() continue +*/ if(istype(thing,/obj/structure/simple_door)) var/obj/structure/simple_door/D = thing if(listener_id == D.lock_id) diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index b155462d70b..b999cd264da 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -1580,3 +1580,19 @@ About the new airlock wires panel: qdel(src) return TRUE return FALSE + +//RS ADD START +/obj/machinery/door/airlock/dungeon_trigger() + if(islocked()) + dungeon_unlock() + else + dungeon_lock() + return TRUE + +/obj/machinery/door/airlock/dungeon_unlock() + unlock() + open() + SEND_SIGNAL(src,COMSIG_DUNGEON_TRIGGER) +/obj/machinery/door/airlock/dungeon_lock() + lock() + SEND_SIGNAL(src,COMSIG_DUNGEON_UNTRIGGER) diff --git a/code/game/objects/crystal_key.dm b/code/game/objects/crystal_key.dm index a18663b2223..64606adaf89 100644 --- a/code/game/objects/crystal_key.dm +++ b/code/game/objects/crystal_key.dm @@ -1,209 +1,175 @@ //RS FILE - -#define DELETE_OBSTICAL 1 -#define TOGGLE_OBSTICAL 2 - -var/global/list/event_obstical_keys = list() - -/obj/event_key +/obj/dungeon_switch name = "crystal" icon = 'icons/rogue-star/misc.dmi' icon_state = "crystal_key" anchored = TRUE - var/id - var/spent = FALSE - var/spent_state = "gold_tri" - var/mob/living/link - var/reusable = FALSE - var/closed_icon = 'icons/rogue-star/misc.dmi' + var/open_state = "crystal_key" var/closed_state = "crystal_key_spent" -/obj/event_key/reusable - reusable = TRUE - -/obj/event_key/Initialize() +/obj/dungeon_switch/attack_hand(mob/living/user) . = ..() - global.event_obstical_keys += src - seek_link() + user.visible_message(SPAN_NOTICE("\The [user] touches \the [src]."),SPAN_NOTICE("You touch \the [src]."),runemessage = "tuch") + if(!dungeon_trigger()) + to_chat(user,SPAN_WARNING("\The [src] doesn't respond...")) + +/obj/dungeon_switch/dungeon_trigger() + return attempt_to_trigger() + +/obj/dungeon_switch/proc/attempt_to_trigger() + if(islocked()) + return FALSE + if(!cantrigger()) + return FALSE + if(icon_state == closed_state) + return dungeon_unlock() + else + return dungeon_lock() -/obj/event_key/Destroy() - global.event_obstical_keys -= src - unregister_mob() - return ..() +/obj/dungeon_switch/hitby(atom/movable/AM) + . = ..() + if(isobj(AM)) + dungeon_trigger() -/obj/event_key/attack_hand(mob/living/user) +/obj/dungeon_switch/bullet_act(obj/item/projectile/P, def_zone) . = ..() - if(spent) - return - if(link) - if(!link.client && (link?.ai_holder?.hostile || link?.ai_holder?.stance != STANCE_IDLE)) - to_chat(user,"A barrier prevents you from touching \the [src]. Something else must be done before you can use it.") - return - if(!src.Adjacent(link)) - to_chat(user,"A barrier prevents you from touching \the [src]. Something else must be done before you can use it.") - return - to_chat(user,"You activate \the [src].") - spent = TRUE - trigger() - -/obj/event_key/proc/trigger() - for(var/obj/thing in global.event_obstical_keys) - if(thing == src) - continue - if(istype(thing,/obj/event_key)) - var/obj/event_key/key = thing - if(id == key.id) - key.post_trigger() - continue - - if(istype(thing,/obj/event_obstical)) - var/obj/event_obstical/obstical = thing - if(id == obstical.id) - obstical.post_trigger() - continue - post_trigger() - -/obj/event_key/proc/post_trigger() + dungeon_trigger() + +/obj/dungeon_switch/dungeon_lock() + var/turf/ourturf = get_turf(src) + if(icon_state == closed_state) + return FALSE + ourturf.visible_message(SPAN_WARNING("\The [src] shimmers as it closes up!!!"),runemessage = "clink") + icon_state = closed_state + SEND_SIGNAL(src,COMSIG_DUNGEON_TRIGGER) + return TRUE + +/obj/dungeon_switch/dungeon_unlock() var/turf/ourturf = get_turf(src) - if(reusable) - ourturf.visible_message("\The [src] rumbles as something moves in the distance!!!",runemessage = "rumble rumble") - spent = FALSE + if(icon_state == open_state) + return FALSE + if(!open_state) + ourturf.visible_message("\The [src] crumbles to dust!!!",runemessage = ". . .") + qdel(src) else - unregister_mob() - if(closed_state) - ourturf.visible_message("\The [src] shimmers as it closes up!!!",runemessage = "clink") - icon = closed_icon - icon_state = closed_state - else - ourturf.visible_message("\The [src] crumbles to dust!!!",runemessage = "crumble crumble") - qdel(src) - -/obj/event_key/proc/seek_link() - for(var/mob/living/thing in get_turf(src)) - if(isliving(thing)) - register_mob(thing) - return - -/obj/event_key/proc/register_mob(var/mob/living/ourmob) - if(isliving(ourmob)) - link = ourmob - RegisterSignal(link, COMSIG_MOB_DEATH, PROC_REF(trigger),TRUE) - RegisterSignal(link, COMSIG_PARENT_QDELETING, PROC_REF(trigger), TRUE) - -/obj/event_key/proc/unregister_mob() - if(link) - UnregisterSignal(link, COMSIG_MOB_DEATH) - UnregisterSignal(link, COMSIG_PARENT_QDELETING) - -/obj/event_key/hitby(atom/movable/AM) - . = ..() - if(isobj(AM)) - trigger() + ourturf.visible_message(SPAN_WARNING("\The [src] flashes as it opens up!!!"),runemessage = "shing") + icon_state = open_state + SEND_SIGNAL(src,COMSIG_DUNGEON_UNTRIGGER) + return TRUE -/obj/event_key/bullet_act(obj/item/projectile/P, def_zone) - . = ..() - trigger() +//Obstacle// -/obj/event_obstical - name = "impassable rock" - desc = "A shiny, impassable rock!" - icon = 'icons/turf/x64.dmi' - icon_state = "rock-crystal-shiny" - var/id +/obj/dungeon_obstacle + name = "decorated pillar" + desc = "An impassable pillar made of a very hard material. It has some intricate engravings etched in its surface." + icon = 'icons/rogue-star/misc.dmi' + icon_state = "crystal_pillar" anchored = TRUE density = TRUE opacity = TRUE - pixel_x = -16 - pixel_y = -16 - var/trigger_mode = DELETE_OBSTICAL - var/closed_icon = 'icons/turf/x64.dmi' - var/closed_state = null - var/open_icon = 'icons/turf/x64.dmi' - var/open_state = null + var/closed_state = "crystal_pillar" + var/open_state = "crystal_pillar_lowered" -/obj/event_obstical/Initialize() - . = ..() - global.event_obstical_keys += src - - if(trigger_mode == TOGGLE_OBSTICAL) - opacity = density - if(density) - if(closed_state) - icon = closed_icon - icon_state = closed_state - else - closed_state = icon_state - else - if(open_state) - icon = open_icon - icon_state = open_state - -/obj/event_obstical/Destroy() - global.event_obstical_keys -= src - return ..() - -/obj/event_obstical/proc/post_trigger() +/obj/dungeon_obstacle/dungeon_lock() + if(density) + return FALSE + var/turf/ourturf = get_turf(src) + density = TRUE + opacity = TRUE + icon_state = closed_state + SEND_SIGNAL(src,COMSIG_DUNGEON_TRIGGER) + ourturf.visible_message(SPAN_WARNING("\The [src] rumbles into a closed position!"),runemessage = "rumble") + return TRUE + +/obj/dungeon_obstacle/dungeon_unlock() + if(!density) + return FALSE var/turf/ourturf = get_turf(src) - switch(trigger_mode) - if(DELETE_OBSTICAL) - ourturf.visible_message("\The [src] crumbles to dust!!!",runemessage = "crumble crumble") - qdel(src) - if(TOGGLE_OBSTICAL) - if(!closed_state) - closed_state = icon_state - density = !density - opacity = density - if(density) - icon = closed_icon - icon_state = closed_state - else - icon = open_icon - icon_state = open_state - ourturf.visible_message("\The [src] rumbles as it moves!!!",runemessage = "rumble rumble") - -/obj/event_obstical/disguised + if(!open_state) + ourturf.visible_message(SPAN_WARNING("\The [src] crumbles to dust!!!"),runemessage = ". . .") + qdel(src) + return TRUE + density = FALSE + opacity = FALSE + icon_state = open_state + SEND_SIGNAL(src,COMSIG_DUNGEON_UNTRIGGER) + ourturf.visible_message(SPAN_NOTICE("\The [src] clunks into an open position!"),runemessage = "clunk") + return TRUE + +/obj/dungeon_obstacle/dungeon_trigger() //Something somewhere is telling us to trigger, so we're just going to assume it wants us to toggle our state + if(icon_state == closed_state) + return dungeon_unlock() + else + return dungeon_lock() + +/obj/dungeon_obstacle/pillar/open + icon_state = "crystal_pillar_lowered" + density = FALSE + opacity = FALSE + +/obj/dungeon_obstacle/disguised name = "wall" icon_state = "crystal_obstical_disguised" desc = "It seems to be a section of wall plated with steel." - icon = 'icons/rogue-star/misc.dmi' - pixel_x = 0 - pixel_y = 0 - - closed_icon = 'icons/rogue-star/misc.dmi' - open_icon = 'icons/rogue-star/misc.dmi' + closed_state = "crystal_obstical_disguised" + open_state = "crystal_obstical_disguised_lowered" +/obj/dungeon_obstacle/disguised/open + icon_state = "crystal_obstical_disguised_lowered" + density = FALSE + opacity = FALSE -/obj/event_obstical/disguised/wall +/obj/dungeon_obstacle/wall + name = "wall" + desc = "It seems to be a section of wall plated with steel." icon = 'icons/turf/wall_masks.dmi' icon_state = "generic" desc = "It seems to be a section of wall plated with steel." + closed_state = "generic" + open_state = "blank" - closed_icon = 'icons/turf/wall_masks.dmi' - open_icon = 'icons/turf/wall_masks.dmi' +/obj/dungeon_obstacle/wall/open + icon_state = "blank" + density = FALSE + opacity = FALSE -/obj/event_obstical/disguised/wall/reinforced +/obj/dungeon_obstacle/wall/reinforced icon_state = "rgeneric" desc = "It seems to be a section of wall reinforced with plasteel and plated with plasteel." + closed_state = "rgeneric" + +/obj/dungeon_obstacle/wall/reinforced/open + icon_state = "blank" + density = FALSE + opacity = FALSE -/obj/event_obstical/disguised/wall/cult +/obj/dungeon_obstacle/wall/cult icon_state = "cult" desc = "Hideous images dance beneath the surface." + closed_state = "cult" -/obj/event_obstical/disguised/pillar - name = "decorated pillar" - icon_state = "crystal_pillar" +/obj/dungeon_obstacle/wall/cult/open + icon_state = "blank" + density = FALSE opacity = FALSE - closed_state = "crystal_pillar" - open_state = "crystal_pillar_lowered" -/obj/event_obstical/disguised/pillar/toggle - trigger_mode = TOGGLE_OBSTICAL - -/obj/event_obstical/disguised/obstical +/obj/dungeon_obstacle/obstical name = "decorated wall" icon_state = "crystal_obstical" closed_state = "crystal_obstical" open_state = "crystal_obstical_lowered" -/obj/event_obstical/disguised/obstical/toggle - trigger_mode = TOGGLE_OBSTICAL +/obj/dungeon_obstacle/obstical/open + icon_state = "crystal_obstical_lowered" + density = FALSE + opacity = FALSE + +/obj/dungeon_obstacle/crystal + name = "impassable rock" + desc = "A shiny, impassable rock!" + icon = 'icons/turf/x64.dmi' + icon_state = "rock-crystal-shiny" + pixel_x = -16 + pixel_y = -16 + closed_state = "rock-crystal-shiny" + open_state = null diff --git a/code/game/objects/structures/simple_doors.dm b/code/game/objects/structures/simple_doors.dm index 1c1388359f4..4d4280f8e04 100644 --- a/code/game/objects/structures/simple_doors.dm +++ b/code/game/objects/structures/simple_doors.dm @@ -238,12 +238,6 @@ return SSradiation.radiate(src, round(material.radioactivity/3)) -//RS ADD -/obj/structure/simple_door/proc/toggle_lock() - visible_message(SPAN_NOTICE("\The [src] clunks as it is [locked ? "unlocked" : "locked"].")) - locked = !locked - playsound(src, keysound,100, 1) - /obj/structure/simple_door/iron/Initialize(mapload,var/material_name) ..(mapload, material_name || "iron") @@ -287,3 +281,25 @@ if(!iscultist(L) && !istype(L, /mob/living/simple_mob/construct)) return ..() + +//RS ADD START +/obj/structure/simple_door/dungeon_trigger() + if(islocked()) + dungeon_unlock() + else + dungeon_lock() + return TRUE + +/obj/structure/simple_door/dungeon_lock() + locked = TRUE + visible_message(SPAN_NOTICE("\The [src] clunks as it is locked.")) + playsound(src, keysound,100, 1) + SEND_SIGNAL(src,COMSIG_DUNGEON_UNTRIGGER) + return TRUE + +/obj/structure/simple_door/dungeon_unlock() + locked = FALSE + visible_message(SPAN_NOTICE("\The [src] clunks as it is unlocked.")) + playsound(src, keysound,100, 1) + SEND_SIGNAL(src,COMSIG_DUNGEON_TRIGGER) + return TRUE diff --git a/icons/rogue-star/component_adder.dmi b/icons/rogue-star/component_adder.dmi new file mode 100644 index 0000000000000000000000000000000000000000..d38e792febdad94dc639e3a65c4372f35e5f34cb GIT binary patch literal 489 zcmVD@x2wg|OkOa`Kb2iBc3_Oi)>NY9(Q#GV_SA52%O;`$~#3)6+C2(g6`yZCDyG!8z ze`QP|@PN)RW8z`GJVUKjY*h#ZW1TJYo|moALas;Kw02u3-RLVKQ<@RV1BEhtT?G$_ zS0ZTP9^{tsLn&8GO+B&VRTSJK0L9fHo4F6u%riXZG3&u|T0l24^*jb0`|${xQc8SI zi*nJIqC+x&x_$5)q;&G$Np4+ayA1Z%bJ`5W3=LNc3vu za7(@^pa;f$cIV@BW Date: Sat, 23 May 2026 23:41:52 -0400 Subject: [PATCH 4/8] yip --- .../Rogue Star/_Component/component_adder.dm | 44 ++++++- .../_Component/dungeon_component.dm | 122 +++++++++++++++--- code/game/Rogue Star/multipoint_barrier.dm | 58 +++++++-- code/game/Rogue Star/obj/listener.dm | 18 ++- code/game/machinery/door_control.dm | 3 + code/game/objects/crystal_key.dm | 11 +- code/modules/multiz/portals_vr.dm | 8 +- icons/rogue-star/component_adder.dmi | Bin 489 -> 603 bytes maps/example/event_example.dmm | 36 +++--- vorestation.dme | 2 +- 10 files changed, 230 insertions(+), 72 deletions(-) diff --git a/code/game/Rogue Star/_Component/component_adder.dm b/code/game/Rogue Star/_Component/component_adder.dm index 64ce478cd63..6b9c21cb61e 100644 --- a/code/game/Rogue Star/_Component/component_adder.dm +++ b/code/game/Rogue Star/_Component/component_adder.dm @@ -71,7 +71,8 @@ /obj/structure/simple_door, /obj/dungeon_obstacle, /obj/machinery/door/blast, - /obj/dungeon_switch + /obj/dungeon_switch, + /obj/multipoint/teleporter ) /obj/component_adder/lock/consider_overlay_state(var/atom/consider) @@ -95,22 +96,51 @@ /obj/dungeon_switch, /obj/machinery/door/airlock, /obj/structure/simple_door, - /obj/machinery/door/blast + /obj/machinery/door/blast, + /obj/multipoint_trigger, + /obj/listener ) var/onetime = FALSE +/obj/component_adder/trigger/onetime + onetime = TRUE + /obj/component_adder/trigger/add_component() var/datum/component/dungeon_mechanic/trigger/T = ..() T.onetime = onetime -/obj/component_adder/link - name = "link component" - icon_state = "link" - component_type = /datum/component/dungeon_mechanic/link +/obj/component_adder/reciever + name = "reciever component" + icon_state = "reciever" + component_type = /datum/component/dungeon_mechanic/reciever valid_types = list( /obj/machinery/door/airlock, /obj/structure/simple_door, /obj/dungeon_obstacle, /obj/machinery/door/blast, - /obj/dungeon_switch + /obj/dungeon_switch, + /obj/multipoint/teleporter, + /obj/multipoint/barrier, + /obj/structure/portal_event + ) + +/obj/component_adder/pair + name = "pair component" + icon_state = "pair" + component_type = /datum/component/dungeon_mechanic/pair + valid_types = list( + /obj/multipoint/teleporter, + /obj/structure/portal_event ) + +/* +/obj/proc/add_component_overlays() + var/list/didit = list() + for(var/datum/component/C in components) + if(C in didit) + continue + didit += C + if(!hasvar(C,overlay_icon)) + + return +*/ diff --git a/code/game/Rogue Star/_Component/dungeon_component.dm b/code/game/Rogue Star/_Component/dungeon_component.dm index 3fd1e70d717..f9298c5293d 100644 --- a/code/game/Rogue Star/_Component/dungeon_component.dm +++ b/code/game/Rogue Star/_Component/dungeon_component.dm @@ -12,7 +12,7 @@ var/global/list/dungeon_components = list() dungeon_components |= src -//Locks// +//LOCK// - It won't work unless you unlock it /datum/component/dungeon_mechanic/lock var/locked = TRUE @@ -48,21 +48,21 @@ var/global/list/dungeon_components = list() else return O.dungeon_unlock() -//LINK// -/datum/component/dungeon_mechanic/link +//RECIEVER// - The thing that gets told what to do +/datum/component/dungeon_mechanic/reciever -/datum/component/dungeon_mechanic/link/Initialize(var/our_id) +/datum/component/dungeon_mechanic/reciever/Initialize(var/our_id) . = ..() for(var/datum/component/dungeon_mechanic/trigger/T in dungeon_components) if(T == src) continue - if(T.type != /datum/component/dungeon_mechanic/trigger) + if(!istype(T,/datum/component/dungeon_mechanic/trigger)) continue if(T.id == id) RegisterSignal(T,COMSIG_DUNGEON_TRIGGER,PROC_REF(link_trigger)) RegisterSignal(T,COMSIG_DUNGEON_UNTRIGGER,PROC_REF(link_trigger)) -/datum/component/dungeon_mechanic/link/Destroy(force, silent) +/datum/component/dungeon_mechanic/reciever/Destroy(force, silent) dungeon_components -= src for(var/datum/component/dungeon_mechanic/trigger/T in dungeon_components) if(id == T.id) @@ -70,14 +70,18 @@ var/global/list/dungeon_components = list() UnregisterSignal(T,COMSIG_DUNGEON_UNTRIGGER) return ..() -/datum/component/dungeon_mechanic/link/proc/link_trigger() +/datum/component/dungeon_mechanic/reciever/proc/link_trigger() var/obj/O = parent O.dungeon_trigger() -//TRIGGER// +//TRIGGER// - The thing that tells other things to do things /datum/component/dungeon_mechanic/trigger - var/triggered = FALSE - var/onetime = FALSE + var/triggered = FALSE //Are we triggered or untriggered + var/onetime = FALSE //If true we can only be triggered, once triggered, we can not be untriggered + var/solo = TRUE //If FALSE will require ALL of the triggers with the same ID to be triggered before it will send the trigger signal + var/key_lock = FALSE //If TRUE (and solo is FALSE) requires unique ckeys to hit each trigger + var/triggered_by //A recording of who triggered the trigger (only relevent if key_lock is TRUE) + /datum/component/dungeon_mechanic/trigger/Initialize(our_id) . = ..() RegisterSignal(parent, COMSIG_DUNGEON_TRIGGER , PROC_REF(toggle_trigger)) @@ -88,31 +92,101 @@ var/global/list/dungeon_components = list() UnregisterSignal(parent, COMSIG_DUNGEON_TRIGGER) UnregisterSignal(parent, COMSIG_DUNGEON_UNTRIGGER) - for(var/datum/component/dungeon_mechanic/link/L in dungeon_components) + for(var/datum/component/dungeon_mechanic/reciever/L in dungeon_components) if(id == L.id) L.UnregisterSignal(src,COMSIG_DUNGEON_TRIGGER) return ..() /datum/component/dungeon_mechanic/trigger/proc/toggle_trigger() + + var/mob/user + if(args[2]) + user = args[2] + + if(!solo) //If solo is false then we need to hit ALL of the triggers before the trigger signal will send + for(var/datum/component/dungeon_mechanic/trigger/T in dungeon_components) + if(T.id != id) + continue + if(!T.triggered) + return + if(key_lock) //If key_lock is true then we need unique ckeys for each trigger. + if(!user) + return + if(!user.ckey) + return + if(T.triggered_by == user.ckey) + to_chat(user,SPAN_WARNING("\The [parent] very unsatisfyingly does nothing when you interact with it. Perhaps someone else needs to interact with this one.")) + return + if(triggered) - if(!onetime) - untrigger() + if(onetime) + return + untrigger() else trigger() + if(user.ckey) + triggered_by = user.ckey + var/turf/T = get_turf(parent) + T.visible_message("\The [parent] clicks audibly as it is triggered...",runemessage = "click...") + +/datum/component/dungeon_mechanic/trigger/proc/should_key_trigger(var/key) + if(!key_lock) + return TRUE + + for(var/datum/component/dungeon_mechanic/trigger/T in dungeon_components) + if(T.id != id) + continue + if(!T.triggered_by) + continue + if(T.triggered_by == key) + return FALSE + + return TRUE /datum/component/dungeon_mechanic/trigger/proc/trigger() triggered = TRUE + var/obj/P = parent + P.dungeon_trigger() + SEND_SIGNAL(src,COMSIG_DUNGEON_TRIGGER) /datum/component/dungeon_mechanic/trigger/proc/untrigger() triggered = FALSE + var/obj/P = parent + P.dungeon_untrigger() + SEND_SIGNAL(src,COMSIG_DUNGEON_UNTRIGGER) + +//PAIR// - So things can know about eachother, such as teleporters +/datum/component/dungeon_mechanic/pair + var/list/partner = list() + +/datum/component/dungeon_mechanic/pair/New(list/raw_args) + . = ..() + pair_with_partners() + +/datum/component/dungeon_mechanic/pair/proc/pair_with_partners() + for(var/datum/component/dungeon_mechanic/pair/P in dungeon_components) + if(P.type != type) + continue + if(P == src) + continue + if(id == P.id) + partner |= P + P.partner |= src + +/datum/component/dungeon_mechanic/pair/proc/unpair_with_partners() + for(var/datum/component/dungeon_mechanic/pair/P in partner) + P.partner -= src + partner -= P //////////RELATED OBJ PROCS////////// -/obj/proc/dungeon_lock() +/obj/proc/dungeon_lock(var/mob/user) return FALSE -/obj/proc/dungeon_unlock() +/obj/proc/dungeon_unlock(var/mob/user) return FALSE -/obj/proc/dungeon_trigger() +/obj/proc/dungeon_trigger(var/mob/user) + return FALSE +/obj/proc/dungeon_untrigger(var/mob/user) return FALSE /obj/proc/islocked() @@ -121,10 +195,24 @@ var/global/list/dungeon_components = list() return TRUE return FALSE -/obj/proc/cantrigger() +/obj/proc/cantrigger(var/mob/user) var/datum/component/dungeon_mechanic/trigger/trigger = GetComponent(/datum/component/dungeon_mechanic/trigger) if(!trigger) return FALSE if(trigger.onetime && trigger.triggered) return FALSE + if(trigger.key_lock) + if(!user.ckey) + return FALSE + return trigger.should_key_trigger(user.ckey) + return TRUE + +/obj/proc/get_dungeon_pair() + var/datum/component/dungeon_mechanic/pair/P = GetComponent(/datum/component/dungeon_mechanic/pair) + if(!P) + return FALSE + if(P.partner) + if(P.partner.len <= 0) + return FALSE + return P.partner diff --git a/code/game/Rogue Star/multipoint_barrier.dm b/code/game/Rogue Star/multipoint_barrier.dm index 8b47112f561..802a10870ad 100644 --- a/code/game/Rogue Star/multipoint_barrier.dm +++ b/code/game/Rogue Star/multipoint_barrier.dm @@ -137,16 +137,14 @@ var/global/list/multipoint_trigger_list = list() // Used for admin-only reset v /obj/multipoint/teleporter/proc/teleport(var/to_teleport) if(!to_teleport) return - var/list/targlist = list() - for(var/obj/multipoint/teleporter/tele in teleporters_list) - if(tele == src) - continue - if(tele.teleport_id == teleport_id) - targlist |= tele - + var/list/targlist = get_dungeon_pair() + if(!targlist) + return if(targlist.len <= 0) - toggle_active() - teleport_to_opposite_side_or_randomize(to_teleport,src,pick(targlist)) + return + var/datum/component/dungeon_mechanic/pair/P = pick(targlist) + var/obj/target = P.parent + teleport_to_opposite_side_or_randomize(to_teleport,src,target) /obj/multipoint/teleporter/proc/toggle_active() density = !density @@ -157,12 +155,30 @@ var/global/list/multipoint_trigger_list = list() // Used for admin-only reset v update_icon() /obj/multipoint/teleporter/proc/assess_activity() + var/list/targs = get_dungeon_pair() + if(!targs) + return + if(targs.len <= 0) + return + + toggle_active() + +/* for(var/obj/multipoint/teleporter/tele in teleporters_list) if(tele == src) continue if(tele.teleport_id == teleport_id) toggle_active() return +*/ + +/obj/multipoint/teleporter/dungeon_trigger(mob/user) + assess_activity() + +/obj/multipoint/teleporter/dungeon_lock(mob/user) + dungeon_untrigger(user) +/obj/multipoint/teleporter/dungeon_unlock(mob/user) + dungeon_trigger(user) /////DA BUTTAN///// /obj/multipoint_trigger @@ -248,6 +264,29 @@ var/global/list/multipoint_trigger_list = list() // Used for admin-only reset v return if(!user.ckey) //Players only return + SEND_SIGNAL(src,COMSIG_DUNGEON_TRIGGER,user) + +/obj/multipoint_trigger/dungeon_trigger(mob/user) + cut_overlays() + icon_state = triggered_state + +/obj/multipoint_trigger/dungeon_untrigger(mob/user) + icon_state = untriggered_state + if(!overlay_state) + return + var/combine_key = "[overlay_state]-[barrier_color]" + var/image/our_overlay = overlays_cache[combine_key] + if(!our_overlay) + our_overlay = image(icon,null,overlay_state) + our_overlay.color = barrier_color + our_overlay.plane = PLANE_LIGHTING_ABOVE + our_overlay.appearance_flags = RESET_COLOR|KEEP_APART|PIXEL_SCALE + overlays_cache[combine_key] = our_overlay + add_overlay(our_overlay) + + +/* + var/key_detect = FALSE //If true, we discovered the user's ckey on one of the triggers we care about var/list/triggers = list() //We will gather a list of our triggers to compare to how many are triggered var/triggered_triggers = 0 //This is what we will compare triggers against. @@ -271,6 +310,7 @@ var/global/list/multipoint_trigger_list = list() // Used for admin-only reset v update_icon() if(triggers.len == triggered_triggers) //We know how many triggers are connected, and how many have been pushed! If the number is the same, then they're all pushed! trigger() //Woo! +*/ /obj/multipoint_trigger/proc/trigger() for(var/obj/multipoint/T in multipoint_triggerable_list) diff --git a/code/game/Rogue Star/obj/listener.dm b/code/game/Rogue Star/obj/listener.dm index d80f81e2579..71978fb5d87 100644 --- a/code/game/Rogue Star/obj/listener.dm +++ b/code/game/Rogue Star/obj/listener.dm @@ -70,14 +70,17 @@ var/atom/ourpass = pw pw = ourpass.name if(findtext(P.message, pw)) - trigger() + if(cantrigger(M)) + trigger(M) -/obj/listener/proc/trigger() - action() -/obj/listener/proc/untrigger() - action(FALSE) +/obj/listener/proc/trigger(var/mob/user) + SEND_SIGNAL(src,COMSIG_DUNGEON_TRIGGER,user) + +/obj/listener/proc/untrigger(var/mob/user) + SEND_SIGNAL(src,COMSIG_DUNGEON_UNTRIGGER,user) /obj/listener/proc/action(var/trigger = TRUE) +/* var/multipoint_triggered = FALSE for(var/obj/thing as obj in view(world.view,get_turf(src))) if(istype(thing,/obj/machinery/door/blast)) @@ -102,7 +105,7 @@ D.close() D.lock() continue -/* + if(istype(thing,/obj/dungeon_obstacle)) var/obj/dungeon_obstacle/O = thing if(listener_id == O.id) @@ -113,7 +116,7 @@ if(!O.density) O.post_trigger() continue -*/ + if(istype(thing,/obj/structure/simple_door)) var/obj/structure/simple_door/D = thing if(listener_id == D.lock_id) @@ -135,6 +138,7 @@ else T.untrigger() continue +*/ /obj/listener/wall icon_state = "listener" density = FALSE diff --git a/code/game/machinery/door_control.dm b/code/game/machinery/door_control.dm index 6e5618d39e0..dac967a7658 100644 --- a/code/game/machinery/door_control.dm +++ b/code/game/machinery/door_control.dm @@ -149,6 +149,9 @@ M.close() return +/obj/machinery/button/remote/blast_door/dungeon_trigger() + trigger() + /* Emitter remote control */ diff --git a/code/game/objects/crystal_key.dm b/code/game/objects/crystal_key.dm index 64606adaf89..cb9e3aa6c73 100644 --- a/code/game/objects/crystal_key.dm +++ b/code/game/objects/crystal_key.dm @@ -10,16 +10,11 @@ /obj/dungeon_switch/attack_hand(mob/living/user) . = ..() user.visible_message(SPAN_NOTICE("\The [user] touches \the [src]."),SPAN_NOTICE("You touch \the [src]."),runemessage = "tuch") - if(!dungeon_trigger()) + if(!dungeon_trigger(user)) to_chat(user,SPAN_WARNING("\The [src] doesn't respond...")) -/obj/dungeon_switch/dungeon_trigger() - return attempt_to_trigger() - -/obj/dungeon_switch/proc/attempt_to_trigger() - if(islocked()) - return FALSE - if(!cantrigger()) +/obj/dungeon_switch/dungeon_trigger(var/mob/user) + if(!cantrigger(user)) return FALSE if(icon_state == closed_state) return dungeon_unlock() diff --git a/code/modules/multiz/portals_vr.dm b/code/modules/multiz/portals_vr.dm index 9f2cfd6129b..9bd930cc4b6 100644 --- a/code/modules/multiz/portals_vr.dm +++ b/code/modules/multiz/portals_vr.dm @@ -18,7 +18,10 @@ /obj/structure/portal_event/Initialize() . = ..() event_portal_list += src + seek_link() //RS ADD +//RS EDIT START - Generalized the logic so I can use it elsewhere too +/obj/structure/portal_event/proc/seek_link() if(portal_id && !target) for(var/obj/structure/portal_event/P in event_portal_list) if(P == src) @@ -27,6 +30,7 @@ target = P target.target = src toggle_portal() //RS ADD END +//RS EDIT END /obj/structure/portal_event/Destroy() event_portal_list -= src //RS EDIT @@ -49,6 +53,7 @@ return return +/* // RS REMOVE - Do not crash the server please and thank you /obj/structure/portal_event/Crossed(AM as mob|obj) if(istype(AM,/mob) && !(istype(AM,/mob/living))) return //do not send ghosts, zshadows, ai eyes, etc @@ -56,6 +61,7 @@ src.teleport(AM) return return +*/ /obj/structure/portal_event/attack_hand(mob/user as mob) if(!istype(user)) @@ -338,5 +344,3 @@ toggle_portal() else return - -//RS ADD END diff --git a/icons/rogue-star/component_adder.dmi b/icons/rogue-star/component_adder.dmi index d38e792febdad94dc639e3a65c4372f35e5f34cb..c188010dba33bab52b578cf25ff86fe1fa49d54e 100644 GIT binary patch delta 470 zcmV;{0V)3J1KR{4iBL{Q4GJ0x0000DNk~Le0001h0001B0s{a50H_p_+L0k80iKaZ zaDPEH1&Nu29R*TEgncDNnd#|7_z|j(2osA^lQUDxh%geS5Vx7i3a)-G;D`eNPM3dX zyq@Cz00042Nkl!(GG(k3`K1M?~uU%u*AJfqW}LZ(@7Nywb|IR?Czlh%jH;E z=P*ceVGL*VI2}?n>qn(;TMX#}m_6!|_w1}mgqRXwI_3%n z7$>@hIE*!%%cJZNYlo6y5gh(qip1CDNc;suzj_(U-(M}T;PUt}`R4(}{_vdf#W*O+ zWn(@DeXBfuX1(T`-Jz5jAn0Vlu&J{{wPpNdE*%m*CNvn3o^>cii|@{rtb-2ao_~~q zB!~{a?kQvox(9``bpnB^6GVpQ{nOKYj-AvQ(}s2JXG#FzD$A56EO5d@@ugY5)KL M07*qoM6N<$f}ZNvl>h($ delta 355 zcmV-p0i6EZ1nC1IiBL{Q4GJ0x0000DNk~Le0001B0001B0s{a50H5}stC1lk0g#bL zaDQQ=GV_SA52%O;`$~#3)6+C2(g6`yZCDyG!8ze`QP|@PN)RW8z`GJVUKjY*h#ZW1TJYo|moALas;K zw02u3-RLVKQ<@RV1BEhtT?G$_S0ZTP9)IMP@k1$BOiewp;#CyfBLKzKAe*@l)66qG z<}vHRb6P+*GW9$L9sBVJno>%9PK$EUm!d;5f4W3Sy?U$Q8pWEckHU!oC`2c44-yfg zCp03UV%sD_X>UteJP^9#XGrvF<#0>BDWC_&e0JyKb7nt&^=SK%aj+lh|G~fgl{BC4 zh=iWdoag{AwG+g_snDap)cod5+|GsY?*+$HFWzpC7h1#QG#3B>002ovPDHLkV1h8k BoNE98 diff --git a/maps/example/event_example.dmm b/maps/example/event_example.dmm index 990d117212f..96e1913af8b 100644 --- a/maps/example/event_example.dmm +++ b/maps/example/event_example.dmm @@ -15,7 +15,7 @@ /turf/unsimulated/spookygrass, /area/submap/admin_upload/powlit) "bK" = ( -/obj/event_obstical/disguised, +/obj/dungeon_obstacle/disguised, /turf/unsimulated/spookygrass{ color = "#8a0000" }, @@ -77,7 +77,7 @@ }, /area/submap/admin_upload/powlit) "kn" = ( -/obj/event_obstical/disguised/pillar/toggle{ +/obj/dungeon_obstacle/pillar{ id = "listener_example" }, /turf/unsimulated/spookygrass, @@ -118,7 +118,7 @@ /turf/unsimulated/spookygrass, /area/submap/admin_upload/powlit) "nu" = ( -/obj/event_obstical/disguised/pillar{ +/obj/dungeon_obstacle/pillar{ id = "event_key_2" }, /turf/unsimulated/spookygrass, @@ -129,12 +129,6 @@ }, /turf/unsimulated/spookygrass, /area/submap/admin_upload/powlit) -"om" = ( -/obj/event_key/reusable{ - id = "event_key_1" - }, -/turf/unsimulated/spookygrass, -/area/submap/admin_upload/powlit) "oz" = ( /obj/item/stack/material/flint, /turf/unsimulated/spookygrass, @@ -149,13 +143,13 @@ }, /area/submap/admin_upload/powlit) "ru" = ( -/obj/event_obstical/disguised/pillar, +/obj/dungeon_obstacle/pillar, /turf/unsimulated/spookygrass{ color = "#8a0000" }, /area/submap/admin_upload/powlit) "rK" = ( -/obj/event_obstical{ +/obj/dungeon_obstacle{ id = "REPLACE ME WITH A STRING" }, /turf/unsimulated/spookygrass{ @@ -209,19 +203,19 @@ }, /area/submap/admin_upload/powlit) "xS" = ( -/obj/event_obstical/disguised/pillar/toggle{ +/obj/dungeon_obstacle/pillar{ id = "event_key_1" }, /turf/unsimulated/spookygrass, /area/submap/admin_upload/powlit) "yA" = ( -/obj/event_obstical/disguised/wall/cult, +/obj/dungeon_obstacle/wall/cult, /turf/unsimulated/spookygrass{ color = "#8a0000" }, /area/submap/admin_upload/powlit) "yH" = ( -/obj/event_key{ +/obj/dungeon_switch{ id = "event_key_1" }, /turf/unsimulated/spookygrass, @@ -244,7 +238,7 @@ }, /area/submap/admin_upload/powlit) "CP" = ( -/obj/event_key{ +/obj/dungeon_switch{ id = "REPLACE ME WITH A STRING" }, /turf/unsimulated/spookygrass{ @@ -257,7 +251,7 @@ }, /area/submap/admin_upload/powlit) "Ee" = ( -/obj/event_obstical/disguised/wall/reinforced, +/obj/dungeon_obstacle/wall/reinforced, /turf/unsimulated/spookygrass{ color = "#8a0000" }, @@ -336,7 +330,7 @@ }, /area/submap/admin_upload/powlit) "MH" = ( -/obj/event_key/reusable, +/obj/dungeon_switch, /turf/unsimulated/spookygrass{ color = "#8a0000" }, @@ -378,7 +372,7 @@ }, /area/submap/admin_upload/powlit) "Ur" = ( -/obj/event_obstical/disguised/wall, +/obj/dungeon_obstacle/wall, /turf/unsimulated/spookygrass{ color = "#8a0000" }, @@ -406,7 +400,7 @@ }, /area/submap/admin_upload/powlit) "YY" = ( -/obj/event_key{ +/obj/dungeon_switch{ id = "event_key_2" }, /turf/unsimulated/spookygrass, @@ -418,7 +412,7 @@ /turf/unsimulated/spookygrass, /area/submap/admin_upload/powlit) "Zi" = ( -/obj/event_obstical/disguised/obstical, +/obj/dungeon_obstacle/obstical, /turf/unsimulated/spookygrass{ color = "#8a0000" }, @@ -3392,7 +3386,7 @@ Qz Qz xS Gz -om +yH Qz jk jk diff --git a/vorestation.dme b/vorestation.dme index cde39f9b90f..e994e2517c3 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -1681,8 +1681,8 @@ #include "code\game\Rogue Star\abilities\return.dm" #include "code\game\Rogue Star\catborgs\catborgs.dm" #include "code\game\Rogue Star\fluff\sari-outfit-fluff.dm" -#include "code\game\Rogue Star\obj\food_cubes.dm" #include "code\game\Rogue Star\mob\seething.dm" +#include "code\game\Rogue Star\obj\food_cubes.dm" #include "code\game\Rogue Star\obj\keys.dm" #include "code\game\Rogue Star\obj\listener.dm" #include "code\game\Rogue Star\obj\spookytrees.dm" From c62b51b372fcf416af41e2b71ffc65041ee4df76 Mon Sep 17 00:00:00 2001 From: VerySoft Date: Sun, 24 May 2026 04:17:20 -0400 Subject: [PATCH 5/8] 0416 --- .../Rogue Star/_Component/component_adder.dm | 27 ++++- .../_Component/dungeon_component.dm | 105 +++++++++++------- code/game/Rogue Star/dungeon_maker.dm | 4 +- code/game/Rogue Star/multipoint_barrier.dm | 40 +++---- code/game/Rogue Star/obj/keys.dm | 44 ++++---- code/game/machinery/doors/airlock.dm | 16 ++- code/game/machinery/doors/blast_door.dm | 22 +++- code/game/objects/crystal_key.dm | 22 ++-- code/game/objects/structures/janicart.dm | 4 +- code/game/objects/structures/simple_doors.dm | 20 +++- code/modules/admin/admin_verb_lists_vr.dm | 2 +- icons/rogue-star/component_adder.dmi | Bin 603 -> 612 bytes maps/redgate/fantasy_dungeon.dmm | 3 + maps/redgate/fantasy_items.dm | 5 +- 14 files changed, 197 insertions(+), 117 deletions(-) diff --git a/code/game/Rogue Star/_Component/component_adder.dm b/code/game/Rogue Star/_Component/component_adder.dm index 6b9c21cb61e..685361fb303 100644 --- a/code/game/Rogue Star/_Component/component_adder.dm +++ b/code/game/Rogue Star/_Component/component_adder.dm @@ -10,6 +10,7 @@ var/id = "REPLACE ME" var/list/valid_types = list() var/static/list/overlays_cache = list() + var/late = FALSE /obj/component_adder/New(loc, new_id) . = ..() @@ -18,6 +19,12 @@ /obj/component_adder/Initialize(mapload) . = ..() + if(late) + return INITIALIZE_HINT_LATELOAD + seek_valid_target() + qdel(src) + +/obj/component_adder/LateInitialize() seek_valid_target() qdel(src) @@ -74,6 +81,10 @@ /obj/dungeon_switch, /obj/multipoint/teleporter ) + var/onetime = FALSE + +/obj/component_adder/lock/onetime + onetime = TRUE /obj/component_adder/lock/consider_overlay_state(var/atom/consider) if(istype(consider,/obj/item/key)) @@ -83,7 +94,7 @@ /obj/component_adder/lock/special_check(var/atom/consider) if(istype(consider,/obj/item/key)) var/obj/item/key/K = consider - K.lock_id = id + K.key_id = id return TRUE return FALSE @@ -101,13 +112,26 @@ /obj/listener ) var/onetime = FALSE + var/solo = TRUE + var/key_lock = FALSE /obj/component_adder/trigger/onetime onetime = TRUE +/obj/component_adder/trigger/non_solo + solo = FALSE +/obj/component_adder/trigger/puzzle + onetime = TRUE + solo = FALSE +/obj/component_adder/trigger/gather_gate + onetime = TRUE + solo = FALSE + key_lock = TRUE /obj/component_adder/trigger/add_component() var/datum/component/dungeon_mechanic/trigger/T = ..() T.onetime = onetime + T.solo = solo + T.key_lock = key_lock /obj/component_adder/reciever name = "reciever component" @@ -123,6 +147,7 @@ /obj/multipoint/barrier, /obj/structure/portal_event ) + late = TRUE /obj/component_adder/pair name = "pair component" diff --git a/code/game/Rogue Star/_Component/dungeon_component.dm b/code/game/Rogue Star/_Component/dungeon_component.dm index f9298c5293d..550827f600d 100644 --- a/code/game/Rogue Star/_Component/dungeon_component.dm +++ b/code/game/Rogue Star/_Component/dungeon_component.dm @@ -15,6 +15,7 @@ var/global/list/dungeon_components = list() //LOCK// - It won't work unless you unlock it /datum/component/dungeon_mechanic/lock var/locked = TRUE + var/onetime = FALSE /datum/component/dungeon_mechanic/lock/Initialize(var/our_id) . = ..() @@ -28,7 +29,7 @@ var/global/list/dungeon_components = list() return var/obj/item/key/K = args[2] var/mob/user = args[3] - if(K.lock_id == id || K.master_key) + if(K.key_id == id || K.master_key) if(!locked && K.one_time) to_chat(user,SPAN_NOTICE("\The [O] is already unlocked! You don't need to use \the [K] on it.")) return @@ -36,12 +37,16 @@ var/global/list/dungeon_components = list() user.visible_message(SPAN_NOTICE("\The [user] inserts \the [K] into \the [O]..."),SPAN_NOTICE("You insert \the [K] into \the [O]...")) if(toggle_lock(O)) K.unlocked() + else + to_chat(user,SPAN_WARNING("While \the [K] fits, \the [O] won't budge! The locking mechanism won't lock again.")) return if(user) to_chat(user,SPAN_DANGER("\The [K] doesn't fit into \the [O]...")) /datum/component/dungeon_mechanic/lock/proc/toggle_lock(var/obj/O) + if(onetime && !locked) + return FALSE locked = !locked if(locked) return O.dungeon_lock() @@ -98,36 +103,14 @@ var/global/list/dungeon_components = list() return ..() /datum/component/dungeon_mechanic/trigger/proc/toggle_trigger() - var/mob/user - if(args[2]) + if(args.len >= 2) user = args[2] - if(!solo) //If solo is false then we need to hit ALL of the triggers before the trigger signal will send - for(var/datum/component/dungeon_mechanic/trigger/T in dungeon_components) - if(T.id != id) - continue - if(!T.triggered) - return - if(key_lock) //If key_lock is true then we need unique ckeys for each trigger. - if(!user) - return - if(!user.ckey) - return - if(T.triggered_by == user.ckey) - to_chat(user,SPAN_WARNING("\The [parent] very unsatisfyingly does nothing when you interact with it. Perhaps someone else needs to interact with this one.")) - return - - if(triggered) - if(onetime) - return - untrigger() + if(!triggered) + trigger(user) else - trigger() - if(user.ckey) - triggered_by = user.ckey - var/turf/T = get_turf(parent) - T.visible_message("\The [parent] clicks audibly as it is triggered...",runemessage = "click...") + untrigger() /datum/component/dungeon_mechanic/trigger/proc/should_key_trigger(var/key) if(!key_lock) @@ -143,22 +126,59 @@ var/global/list/dungeon_components = list() return TRUE -/datum/component/dungeon_mechanic/trigger/proc/trigger() +/datum/component/dungeon_mechanic/trigger/proc/trigger(var/mob/user) + var/signal = TRUE + if(!solo) + for(var/datum/component/dungeon_mechanic/trigger/T in dungeon_components) + if(T == src) + continue + if(T.id != id) + continue + if(key_lock) //If key_lock is true then we need unique ckeys for each trigger. + if(!user) + return + if(!user.ckey) + return + if(T.triggered_by == user.ckey) + to_chat(user,SPAN_WARNING("\The [parent] very unsatisfyingly does nothing when you interact with it. Perhaps someone else needs to interact with this one.")) + return + if(!T.triggered) + signal = FALSE + var/obj/O = T.parent + to_world("[O] on [O.x],[O.y],[O.z] isn't triggered, so we shouldn't send the trigger signal.") + + if(user?.ckey) + triggered_by = user.ckey + triggered = TRUE var/obj/P = parent P.dungeon_trigger() - SEND_SIGNAL(src,COMSIG_DUNGEON_TRIGGER) + if(signal) + SEND_SIGNAL(src,COMSIG_DUNGEON_TRIGGER) + to_world("Should have signaled") + else + to_world("Shouldn't have signaled") + var/turf/T = get_turf(parent) + T.visible_message("\The [parent] clicks audibly as it is triggered...",runemessage = "click...") + /datum/component/dungeon_mechanic/trigger/proc/untrigger() + if(onetime) + return + SEND_SIGNAL(src,COMSIG_DUNGEON_UNTRIGGER) triggered = FALSE + triggered_by = null + var/obj/P = parent P.dungeon_untrigger() - SEND_SIGNAL(src,COMSIG_DUNGEON_UNTRIGGER) + + var/turf/T = get_turf(parent) + T.visible_message("\The [parent] clunks audibly as it is untriggered...",runemessage = "clunk...") //PAIR// - So things can know about eachother, such as teleporters /datum/component/dungeon_mechanic/pair var/list/partner = list() -/datum/component/dungeon_mechanic/pair/New(list/raw_args) +/datum/component/dungeon_mechanic/pair/Initialize(our_id) . = ..() pair_with_partners() @@ -178,16 +198,19 @@ var/global/list/dungeon_components = list() partner -= P //////////RELATED OBJ PROCS////////// -/obj/proc/dungeon_lock(var/mob/user) +/obj/proc/dungeon_trigger(var/mob/user) //This is the main trigger action, if you want something that always does the same thing, use this, all the other procs default to this return FALSE +/obj/proc/dungeon_untrigger(var/mob/user) //If you need a specific untrigger action + dungeon_trigger(user) -/obj/proc/dungeon_unlock(var/mob/user) - return FALSE +/obj/proc/dungeon_lock(var/mob/user) //If you need a specific lock action + dungeon_trigger(user) -/obj/proc/dungeon_trigger(var/mob/user) - return FALSE -/obj/proc/dungeon_untrigger(var/mob/user) - return FALSE +/obj/proc/dungeon_unlock(var/mob/user) //If you need a specific unlock action + dungeon_trigger(user) + +/obj/proc/getlock() + return GetComponent(/datum/component/dungeon_mechanic/lock) /obj/proc/islocked() var/datum/component/dungeon_mechanic/lock/ourlock = GetComponent(/datum/component/dungeon_mechanic/lock) @@ -208,6 +231,12 @@ var/global/list/dungeon_components = list() return TRUE +/obj/proc/istriggered() + var/datum/component/dungeon_mechanic/trigger/trigger = GetComponent(/datum/component/dungeon_mechanic/trigger) + if(!trigger) + return FALSE + return trigger.triggered + /obj/proc/get_dungeon_pair() var/datum/component/dungeon_mechanic/pair/P = GetComponent(/datum/component/dungeon_mechanic/pair) if(!P) diff --git a/code/game/Rogue Star/dungeon_maker.dm b/code/game/Rogue Star/dungeon_maker.dm index 22993981d56..da234c50eab 100644 --- a/code/game/Rogue Star/dungeon_maker.dm +++ b/code/game/Rogue Star/dungeon_maker.dm @@ -43,8 +43,8 @@ // screen_loc = "CENTER,WEST" /obj/effect/dungeon_maker/holder - lock_id = null - trigger_id = null + var/lock_id = null + var/trigger_id = null screen_loc = "WEST,CENTER" icon = null icon_state = null diff --git a/code/game/Rogue Star/multipoint_barrier.dm b/code/game/Rogue Star/multipoint_barrier.dm index 802a10870ad..c1e9360f4a5 100644 --- a/code/game/Rogue Star/multipoint_barrier.dm +++ b/code/game/Rogue Star/multipoint_barrier.dm @@ -9,8 +9,6 @@ var/global/list/multipoint_trigger_list = list() // Used for admin-only reset v icon = 'icons/rogue-star/misc.dmi' icon_state = "box" - trigger_id = "REPLACE ME" //Use this to set which triggers are connected to eachother and the object they are connected to - /obj/multipoint/New(loc, ...) . = ..() multipoint_triggerable_list |= src @@ -37,7 +35,6 @@ var/global/list/multipoint_trigger_list = list() // Used for admin-only reset v color = "#fc033d" plane = PLANE_LIGHTING_ABOVE - trigger_id = "barrier" //Use this to set which triggers are connected to eachother and the barrier they are connected to /obj/multipoint/barrier/Initialize() var/area/A = get_area(src) @@ -55,6 +52,12 @@ var/global/list/multipoint_trigger_list = list() // Used for admin-only reset v alpha = 255 plane = PLANE_LIGHTING_ABOVE +/obj/multipoint/barrier/dungeon_trigger(mob/user) + trigger() + +/obj/multipoint/barrier/dungeon_untrigger(mob/user) + untrigger() + /////TELEPORTER///// /obj/multipoint/teleporter name = "mysterious pad" @@ -175,11 +178,6 @@ var/global/list/multipoint_trigger_list = list() // Used for admin-only reset v /obj/multipoint/teleporter/dungeon_trigger(mob/user) assess_activity() -/obj/multipoint/teleporter/dungeon_lock(mob/user) - dungeon_untrigger(user) -/obj/multipoint/teleporter/dungeon_unlock(mob/user) - dungeon_trigger(user) - /////DA BUTTAN///// /obj/multipoint_trigger name = "mysterious switch" @@ -194,7 +192,6 @@ var/global/list/multipoint_trigger_list = list() // Used for admin-only reset v var/triggered_key //When you press a key, you can't press another key var/static/list/trigger_list = list() //A list of our fellow triggers to iterate through - trigger_id = "REPLACE ME" //Customize this to set which triggers are connected to eachother and the barrier they are connected to var/triggered_state = "button-p" var/untriggered_state = "button" var/doubles = FALSE //If false, the trigger will not allow you to activate a linked trigger if you have already activated one. Any that are true will not care if you pushed another @@ -221,7 +218,7 @@ var/global/list/multipoint_trigger_list = list() // Used for admin-only reset v /obj/multipoint_trigger/update_icon() . = ..() - if(!triggered_key) + if(!istriggered()) icon_state = untriggered_state if(!overlay_state) return @@ -267,23 +264,9 @@ var/global/list/multipoint_trigger_list = list() // Used for admin-only reset v SEND_SIGNAL(src,COMSIG_DUNGEON_TRIGGER,user) /obj/multipoint_trigger/dungeon_trigger(mob/user) - cut_overlays() - icon_state = triggered_state - + update_icon() /obj/multipoint_trigger/dungeon_untrigger(mob/user) - icon_state = untriggered_state - if(!overlay_state) - return - var/combine_key = "[overlay_state]-[barrier_color]" - var/image/our_overlay = overlays_cache[combine_key] - if(!our_overlay) - our_overlay = image(icon,null,overlay_state) - our_overlay.color = barrier_color - our_overlay.plane = PLANE_LIGHTING_ABOVE - our_overlay.appearance_flags = RESET_COLOR|KEEP_APART|PIXEL_SCALE - overlays_cache[combine_key] = our_overlay - add_overlay(our_overlay) - + update_icon() /* @@ -310,7 +293,7 @@ var/global/list/multipoint_trigger_list = list() // Used for admin-only reset v update_icon() if(triggers.len == triggered_triggers) //We know how many triggers are connected, and how many have been pushed! If the number is the same, then they're all pushed! trigger() //Woo! -*/ + /obj/multipoint_trigger/proc/trigger() for(var/obj/multipoint/T in multipoint_triggerable_list) @@ -326,13 +309,16 @@ var/global/list/multipoint_trigger_list = list() // Used for admin-only reset v if(T.trigger_id == trigger_id) T.triggered_key = null T.update_icon() +*/ /obj/multipoint_trigger/Crossed(O) //You stepped on it instead of clicking it, good work! trigger_check(O) +/* /client/proc/reset_multipoint_trigger(obj/multipoint_trigger/T in multipoint_trigger_list) //You can right click it to reset the trigger, it resets all of the ones connected to it || Tweaked for reset verb (Lira, January 2026) set category = null set name = "Reset Barrier Trigger" if(!check_rights(R_FUN, show_msg = FALSE)) return T.reset_trigger() +*/ diff --git a/code/game/Rogue Star/obj/keys.dm b/code/game/Rogue Star/obj/keys.dm index 06981e39723..ed4e62b31aa 100644 --- a/code/game/Rogue Star/obj/keys.dm +++ b/code/game/Rogue Star/obj/keys.dm @@ -1,8 +1,4 @@ //RS FILE -/obj - var/lock_id = null //Used with keys - var/trigger_id = null //Used with various event things - /obj/item/key name = "key" desc = "A small key made out of some kind of metal." @@ -10,7 +6,9 @@ icon_state = "key" persist_storable = FALSE w_class = ITEMSIZE_TINY - lock_id = "key" + drop_sound = 'sound/items/drop/ring.ogg' + pickup_sound = 'sound/items/pickup/ring.ogg' + var/key_id = "key" var/one_time = FALSE //If true the key will delete itself after use var/master_key = FALSE //If true then this key can open anything with a configured lock! @@ -49,7 +47,7 @@ desc = "It looks quite menacing! Upon very close inspection, there are some impossibly complicated and detailed engravings on this key." icon_state = "big-key" color = "#bb883b" - lock_id = "boss" + key_id = "boss" /obj/item/key/onetime one_time = TRUE @@ -57,6 +55,8 @@ /obj/item/key/scifi desc = "A small electronic card with a plastic case, with one end bearing exposed contact points for plugging into an electronic lock." icon_state = "scifi-a" + drop_sound = 'sound/items/drop/device.ogg' + pickup_sound = 'sound/items/pickup/device.ogg' var/static/list/overlays_cache = list() var/contact_color = "#f7b947" @@ -79,7 +79,7 @@ /obj/item/key/scifi/big icon_state = "scifi-b" desc = "A broad electronic card with a solid metal case. One end has precisely machined contacts exposed for plugging into an electronic lock." - lock_id = "boss" + key_id = "boss" var/case_color = "#776f85" /obj/item/key/scifi/big/update_icon() @@ -96,55 +96,59 @@ /obj/item/key/scifi/red color = "#ff0000" - lock_id = "red" + key_id = "red" /obj/item/key/scifi/blue color = "#003cff" - lock_id = "blue" + key_id = "blue" /obj/item/key/scifi/yellow color = "#ffd900" - lock_id = "yellow" + key_id = "yellow" /obj/item/key/scifi/magenta color = "#cc00ff" - lock_id = "magenta" + key_id = "magenta" /obj/item/key/scifi/big/red color = "#ff0000" case_color = "#6b5c5c" - lock_id = "red-boss" + key_id = "red-boss" /obj/item/key/scifi/big/blue color = "#003cff" case_color = "#545c5c" - lock_id = "blue-boss" + key_id = "blue-boss" /obj/item/key/scifi/big/yellow color = "#ffd900" case_color = "#7e5c5c" - lock_id = "yellow-boss" + key_id = "yellow-boss" /obj/item/key/scifi/big/magenta color = "#cc00ff" case_color = "#5a5c5c" - lock_id = "magenta-boss" + key_id = "magenta-boss" /obj/item/key/card name = "key card" desc = "A small rectangular card with a magnet strip running along one side." icon_state = "card" + drop_sound = 'sound/items/drop/card.ogg' + pickup_sound = 'sound/items/pickup/card.ogg' /obj/item/key/card/red color = "#ff0000" - lock_id = "red" + key_id = "red" /obj/item/key/card/blue color = "#003cff" - lock_id = "blue" + key_id = "blue" /obj/item/key/card/yellow color = "#ffd900" - lock_id = "yellow" + key_id = "yellow" /obj/item/key/card/magenta color = "#cc00ff" - lock_id = "magenta" + key_id = "magenta" +/* /obj/proc/key_event(var/obj/item/key/K) if(!K) return FALSE - if((K.master_key && lock_id) || lock_id == K.lock_id) + if((K.master_key && key_id) || key_id == K.key_id) return dungeon_trigger(K.one_time) return FALSE +*/ diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index b999cd264da..c559cf6fbb5 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -1582,12 +1582,18 @@ About the new airlock wires panel: return FALSE //RS ADD START -/obj/machinery/door/airlock/dungeon_trigger() - if(islocked()) - dungeon_unlock() +/obj/machinery/door/airlock/dungeon_trigger(mob/user) + var/datum/component/dungeon_mechanic/lock/ourlock = getlock() + if(ourlock) + if(ourlock.locked) + dungeon_unlock(user) + else if(!ourlock.onetime) + dungeon_lock(user) else - dungeon_lock() - return TRUE + if(locked) + dungeon_unlock(user) + else + dungeon_lock(user) /obj/machinery/door/airlock/dungeon_unlock() unlock() diff --git a/code/game/machinery/doors/blast_door.dm b/code/game/machinery/doors/blast_door.dm index 575dd81664a..241571e0de4 100644 --- a/code/game/machinery/doors/blast_door.dm +++ b/code/game/machinery/doors/blast_door.dm @@ -471,6 +471,26 @@ width = 2 dir = EAST - +//RS ADD START +/obj/machinery/door/blast/dungeon_trigger(mob/user) + var/datum/component/dungeon_mechanic/lock/ourlock = getlock() + if(ourlock) + if(ourlock.locked) + dungeon_unlock(user) + else if(!ourlock.onetime) + dungeon_lock(user) + else + if(density) + dungeon_unlock(user) + else + dungeon_lock(user) + +/obj/machinery/door/blast/dungeon_lock(mob/user) + close() + SEND_SIGNAL(src,COMSIG_DUNGEON_UNTRIGGER) +/obj/machinery/door/blast/dungeon_unlock(mob/user) + open() + SEND_SIGNAL(src,COMSIG_DUNGEON_TRIGGER) +//RS ADD END #undef BLAST_DOOR_CRUSH_DAMAGE #undef SHUTTER_CRUSH_DAMAGE diff --git a/code/game/objects/crystal_key.dm b/code/game/objects/crystal_key.dm index cb9e3aa6c73..2383fad118f 100644 --- a/code/game/objects/crystal_key.dm +++ b/code/game/objects/crystal_key.dm @@ -13,14 +13,6 @@ if(!dungeon_trigger(user)) to_chat(user,SPAN_WARNING("\The [src] doesn't respond...")) -/obj/dungeon_switch/dungeon_trigger(var/mob/user) - if(!cantrigger(user)) - return FALSE - if(icon_state == closed_state) - return dungeon_unlock() - else - return dungeon_lock() - /obj/dungeon_switch/hitby(atom/movable/AM) . = ..() if(isobj(AM)) @@ -30,14 +22,21 @@ . = ..() dungeon_trigger() +/obj/dungeon_switch/dungeon_trigger(var/mob/user) + if(!cantrigger(user)) + return FALSE + if(icon_state == closed_state) + return dungeon_unlock() + else + return dungeon_lock() + /obj/dungeon_switch/dungeon_lock() var/turf/ourturf = get_turf(src) if(icon_state == closed_state) return FALSE ourturf.visible_message(SPAN_WARNING("\The [src] shimmers as it closes up!!!"),runemessage = "clink") icon_state = closed_state - SEND_SIGNAL(src,COMSIG_DUNGEON_TRIGGER) - return TRUE + SEND_SIGNAL(src,COMSIG_DUNGEON_TRIGGER,user) /obj/dungeon_switch/dungeon_unlock() var/turf/ourturf = get_turf(src) @@ -49,8 +48,7 @@ else ourturf.visible_message(SPAN_WARNING("\The [src] flashes as it opens up!!!"),runemessage = "shing") icon_state = open_state - SEND_SIGNAL(src,COMSIG_DUNGEON_UNTRIGGER) - return TRUE + SEND_SIGNAL(src,COMSIG_DUNGEON_UNTRIGGER,user) //Obstacle// diff --git a/code/game/objects/structures/janicart.dm b/code/game/objects/structures/janicart.dm index 17dd5f489bc..aab4d17aec2 100644 --- a/code/game/objects/structures/janicart.dm +++ b/code/game/objects/structures/janicart.dm @@ -427,7 +427,7 @@ GLOBAL_LIST_BOILERPLATE(all_janitorial_carts, /obj/structure/janitorialcart) if(user.stat || user.stunned || user.weakened || user.paralysis) unbuckle_mob() var/obj/item/key/jani/J = user.get_type_in_hands(/obj/item/key) //RS EDIT - if(J.lock_id == "jani") //RS EDIT + if(J.key_id == "jani") //RS EDIT step(src, direction) update_mob() else @@ -499,4 +499,4 @@ GLOBAL_LIST_BOILERPLATE(all_janitorial_carts, /obj/structure/janitorialcart) icon = 'icons/obj/vehicles.dmi' icon_state = "keys" w_class = ITEMSIZE_TINY - lock_id = "jani" //RS ADD + key_id = "jani" //RS ADD diff --git a/code/game/objects/structures/simple_doors.dm b/code/game/objects/structures/simple_doors.dm index 4d4280f8e04..a32c905e38f 100644 --- a/code/game/objects/structures/simple_doors.dm +++ b/code/game/objects/structures/simple_doors.dm @@ -17,7 +17,7 @@ var/knock_hammer_sound = 'sound/weapons/sonic_jackhammer.ogg' var/locked = FALSE //has the door been locked? - lock_id = null //does the door have an associated key? //RS EDIT +// lock_id = null //does the door have an associated key? //RS EDIT var/keysound = 'sound/items/toolbelt_equip.ogg' /obj/structure/simple_door/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) @@ -154,6 +154,7 @@ /obj/structure/simple_door/attackby(obj/item/weapon/W as obj, mob/user as mob) user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + /*//RS REMOVE - components if(istype(W,/obj/item/weapon/simple_key)) var/obj/item/weapon/simple_key/key = W if(state) @@ -165,6 +166,7 @@ locked = !locked playsound(src, keysound,100, 1) return + */ if(istype(W,/obj/item/weapon/pickaxe) && breakable) var/obj/item/weapon/pickaxe/digTool = W visible_message("[user] starts digging [src]!") @@ -283,12 +285,18 @@ ..() //RS ADD START -/obj/structure/simple_door/dungeon_trigger() - if(islocked()) - dungeon_unlock() +/obj/structure/simple_door/dungeon_trigger(mob/user) + var/datum/component/dungeon_mechanic/lock/ourlock = getlock() + if(ourlock) + if(ourlock.locked) + dungeon_unlock(user) + else if(!ourlock.onetime) + dungeon_lock(user) else - dungeon_lock() - return TRUE + if(locked) + dungeon_unlock(user) + else + dungeon_lock(user) /obj/structure/simple_door/dungeon_lock() locked = TRUE diff --git a/code/modules/admin/admin_verb_lists_vr.dm b/code/modules/admin/admin_verb_lists_vr.dm index d8e80b438aa..eba9249f55b 100644 --- a/code/modules/admin/admin_verb_lists_vr.dm +++ b/code/modules/admin/admin_verb_lists_vr.dm @@ -188,7 +188,7 @@ var/list/admin_verbs_fun = list( /client/proc/admin_lighting_manager, // RS ADD: New Lighting Manager Panel (Lira, October 2025) /client/proc/tag_game, //RS ADD /client/proc/report_all_objectives, //RS ADD - /client/proc/reset_multipoint_trigger, //RS ADD +// /client/proc/reset_multipoint_trigger, //RS ADD /client/proc/toggle_dungeon_maker //RS ADD ) diff --git a/icons/rogue-star/component_adder.dmi b/icons/rogue-star/component_adder.dmi index c188010dba33bab52b578cf25ff86fe1fa49d54e..93249af519ce095cc24bf96ab596928cfa108d90 100644 GIT binary patch delta 381 zcmV-@0fPS91mpye@PA)PL_t(YiPcs?j>8}f#1h;=g8#5m_TEUz|G%!J|m zLV#NxBOYM7@i|Ok>dnPA>I!qOKs~IA!neb+_&g$upYSk_QGXBg{PlnUv5jv-x^GbI z5BHHo@@%=UFt<6lpH4H8)cZq$!f%HIB?NELD5oc*3qb*3g_h8Ju-F474}Kw&c=4ND zQedn09*oo@D<~oLRiOmW*WiTE6PU@KK%sglXm_(GdDJ?i%Lk7QKXS3)jKEKn<%A0b ztqvi=Nhw-_advw#We;IpP98@Czbo**(B>&anpt0XbDzL|AUZ`q*SB&7ocOu|;`)JX zn5EL-((2w66>wbjAY%CT4?y^L!ng-8M<;6hduepLnjf&Hup>ARr{ bjP!#)uqPh_$%Y8@00000NkvXXu0mjfQt`ED delta 372 zcmV-)0gL|R1lt6V@PAfGL_t(YiPe_T4uc>JMQsA_kih@2#Jx+R|Nkq~NfipU+1Rq| z?x6$A9a1#wN2PCD4Cw-xJ?fG8(FC=0{*l3@`X{#}a1*X2n31w+4j8Gp*(UoEiU^7t|N=K;n3 z@SO3*I4H_xV?G9bt2}*Xz2=(Tp_CaQ=w!jLsk1}1W&C6=9TGhzG#HScbtpxP@6MI1 zgAV1Mlz}9O4!-UwWDB|ng|l@6fvOWkhUWd#(|nGd)EU!;jf!7w*dXE=WCy8G)z^eQ9cY*zSLO+A}e+i3ZFiXc=u4gZUwn1>Xiw^V!`&9<+z2PwE=?4$UWFCAn SR3~Zx0000 Date: Wed, 27 May 2026 02:12:52 -0400 Subject: [PATCH 6/8] Wahoo! --- code/__defines/dcs/signals.dm | 1 + code/_onclick/item_attack.dm | 1 + .../Rogue Star/_Component/component_adder.dm | 75 +- .../_Component/dungeon_component.dm | 131 +- code/game/Rogue Star/multipoint_barrier.dm | 86 +- code/game/Rogue Star/obj/keys.dm | 53 - code/game/atoms.dm | 1 + code/game/machinery/doors/airlock.dm | 30 +- code/game/machinery/doors/blast_door.dm | 7 + code/game/machinery/doors/door.dm | 18 + code/game/objects/crystal_key.dm | 37 +- .../structures/crates_lockers/__closets.dm | 32 + .../closets/secure/secure_closets.dm | 37 + .../structures/crates_lockers/crates.dm | 42 +- code/game/objects/structures/janicart.dm | 5 +- code/game/objects/structures/simple_doors.dm | 41 +- code/modules/admin/admin_verb_lists_vr.dm | 3 +- code/modules/multiz/portals_vr.dm | 85 +- icons/rogue-star/component_adder.dmi | Bin 612 -> 644 bytes icons/rogue-star/misc.dmi | Bin 22483 -> 22757 bytes maps/example/event_example.dmm | 1058 +++++++++-------- maps/redgate/fantasy.dmm | 5 +- maps/redgate/fantasy_items.dm | 2 - 23 files changed, 941 insertions(+), 809 deletions(-) diff --git a/code/__defines/dcs/signals.dm b/code/__defines/dcs/signals.dm index 7f8f23845fa..10f23e89cdc 100644 --- a/code/__defines/dcs/signals.dm +++ b/code/__defines/dcs/signals.dm @@ -794,4 +794,5 @@ #define COMSIG_KEY_ATTACK "key_event" #define COMSIG_DUNGEON_TRIGGER "dungeon_trigger" #define COMSIG_DUNGEON_UNTRIGGER "dungeon_untrigger" +#define COMSIG_RESOLVE_ATTACKBY "resolve_attackby" //RS ADD END diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index e166ef2efa8..b345b8995f3 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -45,6 +45,7 @@ avoid code duplication. This includes items that may sometimes act as a standard //I would prefer to rename this to attack(), but that would involve touching hundreds of files. /obj/item/proc/resolve_attackby(atom/A, mob/user, var/attack_modifier = 1, var/click_parameters) + SEND_SIGNAL(src,COMSIG_RESOLVE_ATTACKBY,A,user) //RS ADD add_fingerprint(user) . = pre_attack(A, user, click_parameters) if(.) // We're returning the value of pre_attack, important if it has a special return. diff --git a/code/game/Rogue Star/_Component/component_adder.dm b/code/game/Rogue Star/_Component/component_adder.dm index 685361fb303..044578feb66 100644 --- a/code/game/Rogue Star/_Component/component_adder.dm +++ b/code/game/Rogue Star/_Component/component_adder.dm @@ -9,7 +9,6 @@ var/component_type var/id = "REPLACE ME" var/list/valid_types = list() - var/static/list/overlays_cache = list() var/late = FALSE /obj/component_adder/New(loc, new_id) @@ -37,30 +36,13 @@ continue for(var/type_check in valid_types) if(istype(thing,type_check)) - var/overlay_state = consider_overlay_state(thing) if(!special_check(thing)) add_component(thing) - do_overlay(thing,overlay_state) /obj/component_adder/proc/add_component(var/atom/target) if(!target) return - . = target.LoadComponent(component_type,id) - -/obj/component_adder/proc/do_overlay(var/atom/target,var/overlay_state) - if(!target) - return - if(!overlay_state) - overlay_state = "[icon_state]_s" - var/key = "[overlay_state]-[color]" - var/image/overlay = overlays_cache[key] - if(!overlay) - overlay = image(icon,null,overlay_state) - overlay.color = color - overlay.plane = PLANE_ADMIN_SECRET - overlay.appearance_flags = RESET_COLOR|KEEP_APART|PIXEL_SCALE - overlays_cache[key] = overlay - target.add_overlay(overlay) + . = target.LoadComponent(component_type,id,color) /obj/component_adder/proc/consider_overlay_state(var/atom/consider) return null @@ -73,30 +55,44 @@ icon_state = "lock" component_type = /datum/component/dungeon_mechanic/lock valid_types = list( - /obj/item/key, /obj/machinery/door/airlock, /obj/structure/simple_door, /obj/dungeon_obstacle, /obj/machinery/door/blast, /obj/dungeon_switch, - /obj/multipoint/teleporter + /obj/multipoint/teleporter, + /obj/structure/closet, + /obj/structure/portal_event ) var/onetime = FALSE -/obj/component_adder/lock/onetime - onetime = TRUE +/obj/component_adder/lock/add_component(atom/target) + var/datum/component/dungeon_mechanic/lock/L = ..() + L.onetime = onetime -/obj/component_adder/lock/consider_overlay_state(var/atom/consider) - if(istype(consider,/obj/item/key)) - return "key" - return null +/obj/component_adder/key + name = "key component" + icon_state = "key" + component_type = /datum/component/dungeon_mechanic/key + valid_types = list( + /obj/item/key, + /obj/item/weapon/card + ) + var/onetime = FALSE + var/master = FALSE -/obj/component_adder/lock/special_check(var/atom/consider) - if(istype(consider,/obj/item/key)) - var/obj/item/key/K = consider - K.key_id = id - return TRUE - return FALSE +/obj/component_adder/key/add_component(atom/target) + var/datum/component/dungeon_mechanic/key/K = ..() + K.onetime = onetime + K.master_key = master + +/obj/component_adder/key/onetime + onetime = TRUE +/obj/component_adder/key/master + master = TRUE +/obj/component_adder/key/onetime_master + onetime = TRUE + master = TRUE /obj/component_adder/trigger name = "trigger component" @@ -157,15 +153,4 @@ /obj/multipoint/teleporter, /obj/structure/portal_event ) - -/* -/obj/proc/add_component_overlays() - var/list/didit = list() - for(var/datum/component/C in components) - if(C in didit) - continue - didit += C - if(!hasvar(C,overlay_icon)) - - return -*/ + late = TRUE diff --git a/code/game/Rogue Star/_Component/dungeon_component.dm b/code/game/Rogue Star/_Component/dungeon_component.dm index 550827f600d..fbb849d0f03 100644 --- a/code/game/Rogue Star/_Component/dungeon_component.dm +++ b/code/game/Rogue Star/_Component/dungeon_component.dm @@ -3,17 +3,40 @@ var/global/list/dungeon_components = list() /datum/component/dungeon_mechanic var/id = "REPLACE ME" + var/overlay_icon = 'icons/rogue-star/component_adder.dmi' + var/overlay_state + var/overlay_color = "#1ae200" + var/static/list/overlays_cache = list() -/datum/component/dungeon_mechanic/Initialize(var/our_id) +/datum/component/dungeon_mechanic/Initialize(var/our_id,var/our_color) if(!isobj(parent)) return COMPONENT_INCOMPATIBLE if(our_id) id = our_id - + if(our_color) + overlay_color = our_color dungeon_components |= src + var/obj/O = parent + RegisterSignal(O, COMSIG_ATOM_UPDATE_ICON , PROC_REF(add_component_overlay)) + add_component_overlay() + +/datum/component/dungeon_mechanic/proc/add_component_overlay() + if(!overlay_state) + return + var/key = "[overlay_state]-[overlay_color]" + var/image/overlay = overlays_cache[key] + if(!overlay) + overlay = image(overlay_icon,null,overlay_state) + overlay.color = overlay_color + overlay.plane = PLANE_ADMIN_SECRET + overlay.appearance_flags = RESET_COLOR|KEEP_APART|PIXEL_SCALE + overlays_cache[key] = overlay + var/obj/O = parent + O.add_overlay(overlay) //LOCK// - It won't work unless you unlock it /datum/component/dungeon_mechanic/lock + overlay_state = "lock_s" var/locked = TRUE var/onetime = FALSE @@ -21,40 +44,55 @@ var/global/list/dungeon_components = list() . = ..() var/obj/O = parent O.dungeon_lock() - RegisterSignal(parent, COMSIG_KEY_ATTACK , PROC_REF(key_interact)) -/datum/component/dungeon_mechanic/lock/proc/key_interact() - var/obj/O = parent - if(!istype(args[2],/obj/item/key)) +/datum/component/dungeon_mechanic/lock/proc/toggle_lock(var/obj/O) + locked = !locked + if(locked) + O.dungeon_lock() + else + O.dungeon_unlock() + +//KEY// - The thing that asks locks to unlock +/datum/component/dungeon_mechanic/key + overlay_state = "key_s" + var/onetime = FALSE + var/master_key = FALSE + +/datum/component/dungeon_mechanic/key/Initialize(our_id, our_color) + . = ..() + RegisterSignal(parent, COMSIG_RESOLVE_ATTACKBY , PROC_REF(lock_interact)) + +/datum/component/dungeon_mechanic/key/proc/lock_interact() + var/obj/O = args[2] + if(!isobj(O)) + return + var/mob/living/user = args[3] + var/datum/component/dungeon_mechanic/lock/L = O.getlock() + if(!L) return - var/obj/item/key/K = args[2] - var/mob/user = args[3] - if(K.key_id == id || K.master_key) - if(!locked && K.one_time) - to_chat(user,SPAN_NOTICE("\The [O] is already unlocked! You don't need to use \the [K] on it.")) + + if(id == L.id || master_key) + if(!L.locked && onetime) + to_chat(user,SPAN_NOTICE("\The [O] is already unlocked! You don't need to use \the [parent] on it.")) return if(user) - user.visible_message(SPAN_NOTICE("\The [user] inserts \the [K] into \the [O]..."),SPAN_NOTICE("You insert \the [K] into \the [O]...")) - if(toggle_lock(O)) - K.unlocked() - else - to_chat(user,SPAN_WARNING("While \the [K] fits, \the [O] won't budge! The locking mechanism won't lock again.")) - return + user.visible_message(SPAN_NOTICE("\The [user] inserts \the [parent] into \the [O]..."),SPAN_NOTICE("You insert \the [parent] into \the [O]...")) + unlocked(user) + L.toggle_lock(O) - if(user) - to_chat(user,SPAN_DANGER("\The [K] doesn't fit into \the [O]...")) + else if(user) + to_chat(user,SPAN_DANGER("\The [parent] doesn't fit into \the [O]...")) -/datum/component/dungeon_mechanic/lock/proc/toggle_lock(var/obj/O) - if(onetime && !locked) - return FALSE - locked = !locked - if(locked) - return O.dungeon_lock() - else - return O.dungeon_unlock() +/datum/component/dungeon_mechanic/key/proc/unlocked(var/mob/user) + if(onetime) + if(user) + to_chat(user,SPAN_DANGER("\The [parent] crumbles away to dust after being used.")) + user.drop_from_inventory(parent,get_turf(user)) + qdel(parent) //RECIEVER// - The thing that gets told what to do /datum/component/dungeon_mechanic/reciever + overlay_state = "reciever_s" /datum/component/dungeon_mechanic/reciever/Initialize(var/our_id) . = ..() @@ -81,11 +119,13 @@ var/global/list/dungeon_components = list() //TRIGGER// - The thing that tells other things to do things /datum/component/dungeon_mechanic/trigger + overlay_state = "trigger_s" var/triggered = FALSE //Are we triggered or untriggered var/onetime = FALSE //If true we can only be triggered, once triggered, we can not be untriggered var/solo = TRUE //If FALSE will require ALL of the triggers with the same ID to be triggered before it will send the trigger signal var/key_lock = FALSE //If TRUE (and solo is FALSE) requires unique ckeys to hit each trigger var/triggered_by //A recording of who triggered the trigger (only relevent if key_lock is TRUE) + var/last_triggered = 0 /datum/component/dungeon_mechanic/trigger/Initialize(our_id) . = ..() @@ -103,6 +143,8 @@ var/global/list/dungeon_components = list() return ..() /datum/component/dungeon_mechanic/trigger/proc/toggle_trigger() + if(last_triggered + 2 > world.time) + return var/mob/user if(args.len >= 2) user = args[2] @@ -127,6 +169,7 @@ var/global/list/dungeon_components = list() return TRUE /datum/component/dungeon_mechanic/trigger/proc/trigger(var/mob/user) + last_triggered = world.time var/signal = TRUE if(!solo) for(var/datum/component/dungeon_mechanic/trigger/T in dungeon_components) @@ -144,8 +187,6 @@ var/global/list/dungeon_components = list() return if(!T.triggered) signal = FALSE - var/obj/O = T.parent - to_world("[O] on [O.x],[O.y],[O.z] isn't triggered, so we shouldn't send the trigger signal.") if(user?.ckey) triggered_by = user.ckey @@ -155,15 +196,13 @@ var/global/list/dungeon_components = list() P.dungeon_trigger() if(signal) SEND_SIGNAL(src,COMSIG_DUNGEON_TRIGGER) - to_world("Should have signaled") - else - to_world("Shouldn't have signaled") var/turf/T = get_turf(parent) T.visible_message("\The [parent] clicks audibly as it is triggered...",runemessage = "click...") /datum/component/dungeon_mechanic/trigger/proc/untrigger() if(onetime) return + last_triggered = world.time SEND_SIGNAL(src,COMSIG_DUNGEON_UNTRIGGER) triggered = FALSE triggered_by = null @@ -176,6 +215,7 @@ var/global/list/dungeon_components = list() //PAIR// - So things can know about eachother, such as teleporters /datum/component/dungeon_mechanic/pair + overlay_state = "pair_s" var/list/partner = list() /datum/component/dungeon_mechanic/pair/Initialize(our_id) @@ -183,6 +223,7 @@ var/global/list/dungeon_components = list() pair_with_partners() /datum/component/dungeon_mechanic/pair/proc/pair_with_partners() + var/paired = FALSE for(var/datum/component/dungeon_mechanic/pair/P in dungeon_components) if(P.type != type) continue @@ -191,6 +232,11 @@ var/global/list/dungeon_components = list() if(id == P.id) partner |= P P.partner |= src + var/obj/O = P.parent + O.dungeon_pair() + if(paired) + var/obj/ourparent = parent + ourparent.dungeon_pair() /datum/component/dungeon_mechanic/pair/proc/unpair_with_partners() for(var/datum/component/dungeon_mechanic/pair/P in partner) @@ -209,6 +255,13 @@ var/global/list/dungeon_components = list() /obj/proc/dungeon_unlock(var/mob/user) //If you need a specific unlock action dungeon_trigger(user) +/obj/proc/dungeon_pair() + if(islocked()) + return FALSE + if(getreciever()) + return FALSE + return TRUE + /obj/proc/getlock() return GetComponent(/datum/component/dungeon_mechanic/lock) @@ -224,6 +277,8 @@ var/global/list/dungeon_components = list() return FALSE if(trigger.onetime && trigger.triggered) return FALSE + if(trigger.last_triggered + 2 > world.time) + return FALSE if(trigger.key_lock) if(!user.ckey) return FALSE @@ -245,3 +300,15 @@ var/global/list/dungeon_components = list() if(P.partner.len <= 0) return FALSE return P.partner + +/obj/proc/getkey() + var/datum/component/dungeon_mechanic/key/K = GetComponent(/datum/component/dungeon_mechanic/key) + if(!K) + return FALSE + return K + +/obj/proc/getreciever() + var/datum/component/dungeon_mechanic/reciever/R = GetComponent(/datum/component/dungeon_mechanic/reciever) + if(!R) + return FALSE + return R diff --git a/code/game/Rogue Star/multipoint_barrier.dm b/code/game/Rogue Star/multipoint_barrier.dm index c1e9360f4a5..bae3adcf590 100644 --- a/code/game/Rogue Star/multipoint_barrier.dm +++ b/code/game/Rogue Star/multipoint_barrier.dm @@ -85,14 +85,6 @@ var/global/list/multipoint_trigger_list = list() // Used for admin-only reset v return toggle_active() -/obj/multipoint/teleporter/New(loc, ...) - . = ..() - teleporters_list |= src - -/obj/multipoint/teleporter/Destroy() - teleporters_list -= src - . = ..() - /obj/multipoint/teleporter/Bumped(AM) . = ..() @@ -116,8 +108,8 @@ var/global/list/multipoint_trigger_list = list() // Used for admin-only reset v teleport(usr) /obj/multipoint/teleporter/update_icon() - . = ..() cut_overlays() + . = ..() if(density) icon_state = active_state if(!teleporter_overlay) @@ -166,18 +158,22 @@ var/global/list/multipoint_trigger_list = list() // Used for admin-only reset v toggle_active() -/* - for(var/obj/multipoint/teleporter/tele in teleporters_list) - if(tele == src) - continue - if(tele.teleport_id == teleport_id) - toggle_active() - return -*/ - /obj/multipoint/teleporter/dungeon_trigger(mob/user) assess_activity() +/obj/multipoint/teleporter/dungeon_lock(mob/user) + if(!density) + return + density = FALSE + update_icon() + visible_message(SPAN_DANGER("\The [src] shuts down..."),runemessage = "...") + +/obj/multipoint/teleporter/dungeon_pair() + . = ..() + + if(.) + assess_activity() + /////DA BUTTAN///// /obj/multipoint_trigger name = "mysterious switch" @@ -217,6 +213,7 @@ var/global/list/multipoint_trigger_list = list() // Used for admin-only reset v . = ..() /obj/multipoint_trigger/update_icon() + cut_overlays() . = ..() if(!istriggered()) icon_state = untriggered_state @@ -232,7 +229,6 @@ var/global/list/multipoint_trigger_list = list() // Used for admin-only reset v overlays_cache[combine_key] = our_overlay add_overlay(our_overlay) else - cut_overlays() icon_state = triggered_state /obj/multipoint_trigger/Click(location, control, params) //You clicked it instead of stepping on it, what a weirdo, you don't know where it's been (it doesn't move) @@ -268,57 +264,5 @@ var/global/list/multipoint_trigger_list = list() // Used for admin-only reset v /obj/multipoint_trigger/dungeon_untrigger(mob/user) update_icon() -/* - - var/key_detect = FALSE //If true, we discovered the user's ckey on one of the triggers we care about - var/list/triggers = list() //We will gather a list of our triggers to compare to how many are triggered - var/triggered_triggers = 0 //This is what we will compare triggers against. - for(var/obj/multipoint_trigger/T in trigger_list) - if(trigger_id == T.trigger_id) //If our trigger_id is the same then we're controlling the same thing so we'll count it! - triggers |= T - if(T.triggered_key) //Someone pushed it - triggered_triggers ++ //Count it! - if(T.triggered_key == user.ckey) //It's our user!!! - key_detect = TRUE - - if(key_detect && !doubles) //We have already pushed another button. - If doubles, then we don't care if another button was pushed, it'll still push. - if(!triggered_key) //This button has not been pushed though, let's give a hint instead of doing nothing. - to_chat(user,SPAN_WARNING("\The [src] very unsatisfyingly does nothing when you interact with it. Perhaps someone else needs to interact with this one.")) - return - - if(!triggered_key) //Our button wasn't pushed already and we did the checks we needed to see if we are allowed to push the button! - visible_message("\The [src] clicks audibly as it is triggered...",runemessage = "click...") //nice - triggered_key = user.ckey //We register the ckey so that you can't do shenanigans. - triggered_triggers ++ //Don't forget to count yourself, you might be the last one! - update_icon() - if(triggers.len == triggered_triggers) //We know how many triggers are connected, and how many have been pushed! If the number is the same, then they're all pushed! - trigger() //Woo! - - -/obj/multipoint_trigger/proc/trigger() - for(var/obj/multipoint/T in multipoint_triggerable_list) - if(T.trigger_id == trigger_id) - T.trigger() - -/obj/multipoint_trigger/proc/reset_trigger() //Maybe you want to bring the barrier back up for whatever reason! || Tweaked for reset verb (Lira, January 2026) - for(var/obj/multipoint/T in multipoint_triggerable_list) - if(T.trigger_id == trigger_id) - T.untrigger() - - for(var/obj/multipoint_trigger/T in trigger_list) - if(T.trigger_id == trigger_id) - T.triggered_key = null - T.update_icon() -*/ - /obj/multipoint_trigger/Crossed(O) //You stepped on it instead of clicking it, good work! trigger_check(O) - -/* -/client/proc/reset_multipoint_trigger(obj/multipoint_trigger/T in multipoint_trigger_list) //You can right click it to reset the trigger, it resets all of the ones connected to it || Tweaked for reset verb (Lira, January 2026) - set category = null - set name = "Reset Barrier Trigger" - if(!check_rights(R_FUN, show_msg = FALSE)) - return - T.reset_trigger() -*/ diff --git a/code/game/Rogue Star/obj/keys.dm b/code/game/Rogue Star/obj/keys.dm index ed4e62b31aa..27ab9e2e305 100644 --- a/code/game/Rogue Star/obj/keys.dm +++ b/code/game/Rogue Star/obj/keys.dm @@ -8,10 +8,6 @@ w_class = ITEMSIZE_TINY drop_sound = 'sound/items/drop/ring.ogg' pickup_sound = 'sound/items/pickup/ring.ogg' - var/key_id = "key" - var/one_time = FALSE //If true the key will delete itself after use - var/master_key = FALSE //If true then this key can open anything with a configured lock! - /obj/item/key/Initialize() . = ..() pixel_x = rand(-8,8) @@ -19,38 +15,11 @@ if(icon_state == "key") icon_state = "[icon_state]-[rand(1,6)]" color = "#b4cacc" - -/obj/item/key/resolve_attackby(atom/A, mob/user, attack_modifier, click_parameters) - if(!lock_interact(A,user)) - return ..() - -/obj/item/key/proc/lock_interact(var/atom/A,var/mob/user) - if(!A || !user) - return FALSE - - if(!isobj(A)) - return FALSE - - var/obj/O = A - SEND_SIGNAL(O,COMSIG_KEY_ATTACK,src,user) - return TRUE - -/obj/item/key/proc/unlocked(var/mob/user) - if(one_time) - if(user) - to_chat(user,SPAN_DANGER("\The [src] crumbles away to dust after being used.")) - user.drop_from_inventory(src,get_turf(user)) - qdel(src) - /obj/item/key/big name = "big key" desc = "It looks quite menacing! Upon very close inspection, there are some impossibly complicated and detailed engravings on this key." icon_state = "big-key" color = "#bb883b" - key_id = "boss" - -/obj/item/key/onetime - one_time = TRUE /obj/item/key/scifi desc = "A small electronic card with a plastic case, with one end bearing exposed contact points for plugging into an electronic lock." @@ -79,7 +48,6 @@ /obj/item/key/scifi/big icon_state = "scifi-b" desc = "A broad electronic card with a solid metal case. One end has precisely machined contacts exposed for plugging into an electronic lock." - key_id = "boss" var/case_color = "#776f85" /obj/item/key/scifi/big/update_icon() @@ -96,33 +64,25 @@ /obj/item/key/scifi/red color = "#ff0000" - key_id = "red" /obj/item/key/scifi/blue color = "#003cff" - key_id = "blue" /obj/item/key/scifi/yellow color = "#ffd900" - key_id = "yellow" /obj/item/key/scifi/magenta color = "#cc00ff" - key_id = "magenta" /obj/item/key/scifi/big/red color = "#ff0000" case_color = "#6b5c5c" - key_id = "red-boss" /obj/item/key/scifi/big/blue color = "#003cff" case_color = "#545c5c" - key_id = "blue-boss" /obj/item/key/scifi/big/yellow color = "#ffd900" case_color = "#7e5c5c" - key_id = "yellow-boss" /obj/item/key/scifi/big/magenta color = "#cc00ff" case_color = "#5a5c5c" - key_id = "magenta-boss" /obj/item/key/card name = "key card" @@ -133,22 +93,9 @@ /obj/item/key/card/red color = "#ff0000" - key_id = "red" /obj/item/key/card/blue color = "#003cff" - key_id = "blue" /obj/item/key/card/yellow color = "#ffd900" - key_id = "yellow" /obj/item/key/card/magenta color = "#cc00ff" - key_id = "magenta" - -/* -/obj/proc/key_event(var/obj/item/key/K) - if(!K) - return FALSE - if((K.master_key && key_id) || key_id == K.key_id) - return dungeon_trigger(K.one_time) - return FALSE -*/ diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 57a3c483b30..27d8b71803a 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -333,6 +333,7 @@ // Previously this was defined both on /obj/ and /turf/ seperately. And that's bad. /atom/proc/update_icon() + SEND_SIGNAL(src,COMSIG_ATOM_UPDATE_ICON) //RS ADD return diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index c559cf6fbb5..a11dc7352f5 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -87,6 +87,8 @@ ..() /obj/machinery/door/airlock/attack_alien(var/mob/user) //Familiar, right? Doors. -Mechoid + if(islocked()) //RS ADD + return //RS ADD if(istype(user, /mob/living/carbon/human)) var/mob/living/carbon/human/X = user if(istype(X.species, /datum/species/xenos)) @@ -535,6 +537,8 @@ PhoronBurn(exposed_temperature) /obj/machinery/door/airlock/phoron/proc/PhoronBurn(temperature) + if(islocked()) //RS ADD + return //RS ADD for(var/turf/simulated/floor/target_tile in range(2,loc)) target_tile.assume_gas("phoron", 35, 400+T0C) spawn (0) target_tile.hotspot_expose(temperature, 400) @@ -824,6 +828,7 @@ About the new airlock wires panel: /obj/machinery/door/airlock/update_icon() cut_overlays() + SEND_SIGNAL(src,COMSIG_ATOM_UPDATE_ICON) //RS ADD if(density) if(locked && lights && src.arePowerSystemsOn()) icon_state = "door_locked" @@ -875,6 +880,8 @@ About the new airlock wires panel: return /obj/machinery/door/airlock/attack_ai(mob/user as mob) + if(islocked()) //RS ADD + return //RS ADD tgui_interact(user) /obj/machinery/door/airlock/attack_ghost(mob/user) @@ -925,6 +932,8 @@ About the new airlock wires panel: return data /obj/machinery/door/airlock/proc/hack(mob/user as mob) + if(islocked()) //RS ADD + return //RS ADD if(src.aiHacking==0) src.aiHacking=1 spawn(20) @@ -1025,6 +1034,8 @@ About the new airlock wires panel: return /obj/machinery/door/airlock/tgui_act(action, params) + if(islocked()) //RS ADD + return //RS ADD if(..()) return TRUE if(!user_allowed(usr)) @@ -1087,6 +1098,8 @@ About the new airlock wires panel: return 1 /obj/machinery/door/airlock/proc/user_allowed(mob/user) + if(islocked()) //RS ADD + return FALSE //RS ADD var/allowed = (issilicon(user) && canAIControl(user)) if(!allowed && isobserver(user)) var/mob/observer/dead/D = user @@ -1128,7 +1141,12 @@ About the new airlock wires panel: return src.p_open && (operating < 0 || (!operating && welded && !src.arePowerSystemsOn() && density && (!src.locked || (stat & BROKEN)))) /obj/machinery/door/airlock/attackby(obj/item/C, mob/user as mob) - //to_world("airlock attackby src [src] obj [C] mob [user]") + if(SEND_SIGNAL(src, COMSIG_PARENT_ATTACKBY, C, user) & COMPONENT_CANCEL_ATTACK_CHAIN) //RS ADD START + return TRUE + if(islocked()) + if(C.getkey()) + return //RS ADD END + if(!istype(usr, /mob/living/silicon)) if(src.isElectrified()) if(src.shock(user, 75)) @@ -1163,6 +1181,8 @@ About the new airlock wires panel: src.update_icon() return else + if(islocked()) //RS ADD + return //RS ADD src.p_open = TRUE playsound(src, C.usesound, 50, 1) src.update_icon() @@ -1174,6 +1194,8 @@ About the new airlock wires panel: else if(istype(C, /obj/item/device/assembly/signaler)) return src.attack_hand(user) else if(istype(C, /obj/item/weapon/pai_cable)) // -- TLE + if(islocked()) //RS ADD + return //RS ADD var/obj/item/weapon/pai_cable/cable = C cable.plugin(src, user) else if(!repairing && C.is_crowbar()) @@ -1310,6 +1332,8 @@ About the new airlock wires panel: return ..() /obj/machinery/door/airlock/can_open(var/forced=0) + if(islocked()) //RS ADD + return FALSE //RS ADD if(!forced) if(!arePowerSystemsOn() || wires.is_cut(WIRE_OPEN_DOOR)) return 0 @@ -1539,6 +1563,8 @@ About the new airlock wires panel: electronics.one_access = 1 /obj/machinery/door/airlock/emp_act(var/severity) + if(islocked()) //RS ADD + return //RS ADD if(prob(40/severity)) var/duration = world.time + SecondsToTicks(30 / severity) if(duration > electrified_until) @@ -1574,6 +1600,8 @@ About the new airlock wires panel: return FALSE /obj/machinery/door/airlock/rcd_act(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) + if(islocked()) //RS ADD + return FALSE //RS ADD switch(passed_mode) if(RCD_DECONSTRUCT) to_chat(user, span("notice", "You deconstruct \the [src].")) diff --git a/code/game/machinery/doors/blast_door.dm b/code/game/machinery/doors/blast_door.dm index 241571e0de4..bcb4656977f 100644 --- a/code/game/machinery/doors/blast_door.dm +++ b/code/game/machinery/doors/blast_door.dm @@ -60,6 +60,7 @@ // Parameters: None // Description: Updates icon of this object. Uses icon state variables. /obj/machinery/door/blast/update_icon() + SEND_SIGNAL(src,COMSIG_ATOM_UPDATE_ICON) //RS ADD if(density) icon_state = icon_state_closed else @@ -152,6 +153,12 @@ // Description: If we are clicked with crowbar, wielded fire axe, or armblade, try to manually open the door. // This only works on broken doors or doors without power. Also allows repair with Plasteel. /obj/machinery/door/blast/attackby(obj/item/weapon/C as obj, mob/user as mob) + if(SEND_SIGNAL(src, COMSIG_PARENT_ATTACKBY, C, user) & COMPONENT_CANCEL_ATTACK_CHAIN) //RS ADD START + return TRUE + if(islocked()) + if(C.getkey()) + return //RS ADD END + src.add_fingerprint(user) if(istype(C, /obj/item/weapon)) // For reasons unknown, sometimes C is actually not what it is advertised as, like a mob. if(C.pry == 1 && (user.a_intent != I_HURT || (stat & BROKEN))) // Can we pry it open with something, like a crowbar/fireaxe/lingblade? diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index ddcac46c3cd..3101a8afcbb 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -168,6 +168,8 @@ return /obj/machinery/door/bullet_act(var/obj/item/projectile/Proj) + if(islocked()) //RS ADD + return //RS ADD ..() var/damage = Proj.get_structure_damage() @@ -216,8 +218,15 @@ ..() /obj/machinery/door/attackby(obj/item/I as obj, mob/user as mob) + if(SEND_SIGNAL(src, COMSIG_PARENT_ATTACKBY, I, user) & COMPONENT_CANCEL_ATTACK_CHAIN) //RS ADD + return TRUE //RS ADD + src.add_fingerprint(user) + if(islocked()) //RS ADD START + if(I.getkey()) + return //RS ADD END + if(istype(I)) if(attackby_vr(I, user)) //VOREStation begin: Fireproofing return //VOREStation begin: Fireproofing @@ -310,6 +319,8 @@ return /obj/machinery/door/emag_act(var/remaining_charges) + if(islocked()) //RS ADD + return //RS ADD if(density && operable()) do_animate("spark") sleep(6) @@ -318,6 +329,8 @@ return 1 /obj/machinery/door/take_damage(var/damage) + if(islocked()) //RS ADD + return //RS ADD var/initialhealth = src.health src.health = max(0, src.health - damage) if(src.health <= 0 && initialhealth > 0) @@ -354,6 +367,8 @@ /obj/machinery/door/emp_act(severity) + if(islocked()) //RS ADD + return //RS ADD if(prob(20/severity) && (istype(src,/obj/machinery/door/airlock) || istype(src,/obj/machinery/door/window)) ) spawn(0) open() @@ -361,6 +376,8 @@ /obj/machinery/door/ex_act(severity) + if(islocked()) //RS ADD + return //RS ADD switch(severity) if(1.0) qdel(src) @@ -387,6 +404,7 @@ take_damage(100) /obj/machinery/door/update_icon() + SEND_SIGNAL(src,COMSIG_ATOM_UPDATE_ICON) //RS ADD if(density) icon_state = "door1" else diff --git a/code/game/objects/crystal_key.dm b/code/game/objects/crystal_key.dm index 2383fad118f..f8b4f0fdb67 100644 --- a/code/game/objects/crystal_key.dm +++ b/code/game/objects/crystal_key.dm @@ -10,45 +10,36 @@ /obj/dungeon_switch/attack_hand(mob/living/user) . = ..() user.visible_message(SPAN_NOTICE("\The [user] touches \the [src]."),SPAN_NOTICE("You touch \the [src]."),runemessage = "tuch") - if(!dungeon_trigger(user)) + if(!action(user)) to_chat(user,SPAN_WARNING("\The [src] doesn't respond...")) /obj/dungeon_switch/hitby(atom/movable/AM) . = ..() if(isobj(AM)) - dungeon_trigger() + action() /obj/dungeon_switch/bullet_act(obj/item/projectile/P, def_zone) . = ..() - dungeon_trigger() + action() -/obj/dungeon_switch/dungeon_trigger(var/mob/user) +/obj/dungeon_switch/proc/action(var/mob/user) if(!cantrigger(user)) return FALSE - if(icon_state == closed_state) - return dungeon_unlock() - else - return dungeon_lock() - -/obj/dungeon_switch/dungeon_lock() - var/turf/ourturf = get_turf(src) - if(icon_state == closed_state) - return FALSE - ourturf.visible_message(SPAN_WARNING("\The [src] shimmers as it closes up!!!"),runemessage = "clink") - icon_state = closed_state SEND_SIGNAL(src,COMSIG_DUNGEON_TRIGGER,user) + return TRUE + +/obj/dungeon_switch/dungeon_trigger(var/mob/user) + update_icon() -/obj/dungeon_switch/dungeon_unlock() +/obj/dungeon_switch/update_icon() + . = ..() var/turf/ourturf = get_turf(src) - if(icon_state == open_state) - return FALSE - if(!open_state) - ourturf.visible_message("\The [src] crumbles to dust!!!",runemessage = ". . .") - qdel(src) + if(istriggered()) + icon_state = closed_state + ourturf.visible_message(SPAN_WARNING("\The [src] shimmers as it closes up!!!"),runemessage = "clink") else - ourturf.visible_message(SPAN_WARNING("\The [src] flashes as it opens up!!!"),runemessage = "shing") icon_state = open_state - SEND_SIGNAL(src,COMSIG_DUNGEON_UNTRIGGER,user) + ourturf.visible_message(SPAN_WARNING("\The [src] flashes as it opens up!!!"),runemessage = "shing") //Obstacle// diff --git a/code/game/objects/structures/crates_lockers/__closets.dm b/code/game/objects/structures/crates_lockers/__closets.dm index 05661ccedb0..b03a2f7b936 100644 --- a/code/game/objects/structures/crates_lockers/__closets.dm +++ b/code/game/objects/structures/crates_lockers/__closets.dm @@ -105,6 +105,9 @@ /obj/structure/closet/proc/can_open() if(sealed) return 0 + if(islocked()) //RS ADD START + to_chat(usr,SPAN_WARNING("It appears to have a lock on it, which is of course, locked. It can't be opened without using whatever opens it first.")) + return FALSE //RS ADD END return 1 /obj/structure/closet/proc/can_close() @@ -232,6 +235,8 @@ // this should probably use dump_contents() /obj/structure/closet/ex_act(severity) + if(islocked()) //RS ADD + return //RS ADD switch(severity) if(1) for(var/atom/movable/A as mob|obj in src)//pulls everything out of the locker and hits it with an explosion @@ -254,6 +259,8 @@ damage(100) /obj/structure/closet/proc/damage(var/damage) + if(islocked()) //RS ADD + return //RS ADD health -= damage if(health <= 0) for(var/atom/movable/A in src) @@ -271,6 +278,8 @@ return /obj/structure/closet/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(W.getkey()) //RS ADD + return //RS ADD if(W.is_wrench()) if(opened) if(anchored) @@ -414,6 +423,8 @@ icon_state = "closed_unlocked[sealed ? "_welded" : ""]" /obj/structure/closet/attack_generic(var/mob/user, var/damage, var/attack_message = "destroys") + if(islocked()) //RS ADD + return //RS ADD if(damage < STRUCTURE_MIN_DAMAGE_THRESHOLD) return user.do_attack_animation(src) @@ -493,6 +504,8 @@ return return_air() /obj/structure/closet/take_damage(var/damage) + if(islocked()) //RS ADD + return //RS ADD if(damage < STRUCTURE_MIN_DAMAGE_THRESHOLD) return dump_contents() @@ -543,3 +556,22 @@ M.Multiply(matrix(cos(angle), 0, 0, -sin(angle) * closet_appearance.door_anim_squish, 1, 0)) M.Translate(closet_appearance.door_hinge, 0) return M + +//RS ADD START +/obj/structure/closet/dungeon_trigger(mob/user) + var/datum/component/dungeon_mechanic/lock/ourlock = getlock() + if(ourlock) + if(ourlock.locked) + dungeon_unlock(user) + else if(!ourlock.onetime) + dungeon_lock(user) + +/obj/structure/closet/dungeon_lock(mob/user) + SEND_SIGNAL(src,COMSIG_DUNGEON_UNTRIGGER) + visible_message(SPAN_WARNING("\The [src] clicks as it is locked."),runemessage = "click. . .") + playsound(src, 'sound/machines/click.ogg', 15, 1, -3) + +/obj/structure/closet/dungeon_unlock(mob/user) + SEND_SIGNAL(src,COMSIG_DUNGEON_TRIGGER) + visible_message(SPAN_NOTICE("\The [src] clunks as it is unlocked."),runemessage = "clunk. . .") + playsound(src, 'sound/machines/click.ogg', 15, 1, -3) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/secure_closets.dm b/code/game/objects/structures/crates_lockers/closets/secure/secure_closets.dm index 85b9367b7d5..9ba4f626c05 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/secure_closets.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/secure_closets.dm @@ -19,6 +19,9 @@ return ..() /obj/structure/closet/secure_closet/emp_act(severity) + if(islocked()) //RS ADD + return //RS ADD + for(var/obj/O in src) O.emp_act(severity) if(!broken) @@ -43,6 +46,9 @@ if(user.loc == src) to_chat(user, "You can't reach the lock from inside.") return + if(islocked()) //RS ADD START + to_chat(user,SPAN_WARNING("It appears to have a lock on it, which is of course, locked. It can't be opened without using whatever opens it first.")) + return //RS ADD END if(allowed(user)) locked = !locked playsound(src, 'sound/machines/click.ogg', 15, 1, -3) @@ -54,6 +60,9 @@ to_chat(user, "Access Denied") /obj/structure/closet/secure_closet/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(getlock()) //RS ADD START + if(W.getkey()) + return //RS ADD END if(W.is_wrench()) if(opened) if(anchored) @@ -84,6 +93,8 @@ if(W) W.forceMove(loc) else if(istype(W, /obj/item/weapon/melee/energy/blade)) + if(islocked()) //RS ADD + return //RS ADD if(emag_act(INFINITY, user, "The locker has been sliced open by [user] with \an [W]!", "You hear metal being sliced and sparks flying.")) var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() spark_system.set_up(5, 0, loc) @@ -96,6 +107,8 @@ togglelock(user) /obj/structure/closet/secure_closet/emag_act(var/remaining_charges, var/mob/user, var/emag_source, var/visual_feedback = "", var/audible_feedback = "") + if(islocked()) //RS ADD + return //RS ADD if(!broken) broken = 1 locked = 0 @@ -156,3 +169,27 @@ broken = 1 locked = 0 ..() + +//RS ADD START +/obj/structure/closet/secure_closet/dungeon_trigger(mob/user) + var/datum/component/dungeon_mechanic/lock/ourlock = getlock() + if(ourlock) + if(ourlock.locked) + dungeon_unlock(user) + else if(!ourlock.onetime) + dungeon_lock(user) + else + if(locked) + dungeon_unlock(user) + else + dungeon_lock(user) + +/obj/structure/closet/secure_closet/dungeon_lock(mob/user) + . = ..() + locked = TRUE + update_icon() + +/obj/structure/closet/secure_closet/dungeon_unlock(mob/user) + . = ..() + locked = FALSE + update_icon() diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm index 7dfaf181876..4f0d61f8ed4 100644 --- a/code/game/objects/structures/crates_lockers/crates.dm +++ b/code/game/objects/structures/crates_lockers/crates.dm @@ -14,6 +14,9 @@ close_sound = 'sound/effects/crate_close.ogg' /obj/structure/closet/crate/can_open() + if(islocked()) //RS ADD START + to_chat(usr,SPAN_WARNING("It appears to have a lock on it, which is of course, locked. It can't be opened without using whatever opens it first.")) + return FALSE //RS ADD END return 1 /obj/structure/closet/crate/can_close() @@ -126,6 +129,8 @@ else return attack_hand(user) /obj/structure/closet/crate/ex_act(severity) + if(islocked()) //RS ADD + return //RS ADD switch(severity) if(1.0) for(var/obj/O in src.contents) @@ -153,6 +158,9 @@ var/locked = 1 /obj/structure/closet/crate/secure/can_open() + if(islocked()) //RS ADD START + to_chat(usr,SPAN_WARNING("It appears to have a lock on it, which is of course, locked. It can't be opened without using whatever opens it first.")) + return FALSE //RS ADD END return !locked /obj/structure/closet/crate/secure/update_icon() @@ -174,6 +182,9 @@ if(src.broken) to_chat(user, "The crate appears to be broken.") return + if(islocked()) //RS ADD START + to_chat(user,SPAN_WARNING("It appears to have a lock on it, which is of course, locked. It can't be opened without using whatever opens it first.")) + return //RS ADD END if(src.allowed(user)) set_locked(!locked, user) else @@ -210,6 +221,9 @@ src.toggle(user) /obj/structure/closet/crate/secure/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(getlock()) //RS ADD START + if(W.getkey()) + return //RS ADD END if(is_type_in_list(W, list(/obj/item/weapon/packageWrap, /obj/item/stack/cable_coil, /obj/item/device/radio/electropack, /obj/item/weapon/tool/wirecutters))) return ..() if(istype(W, /obj/item/weapon/melee/energy/blade)) @@ -220,6 +234,8 @@ return ..() /obj/structure/closet/crate/secure/emag_act(var/remaining_charges, var/mob/user) + if(islocked()) //RS ADD + return //RS ADD if(!broken) playsound(src, "sparks", 60, 1) locked = 0 @@ -229,6 +245,8 @@ return 1 /obj/structure/closet/crate/secure/emp_act(severity) + if(islocked()) //RS ADD + return //RS ADD for(var/obj/O in src) O.emp_act(severity) if(!broken && !opened && prob(50/severity)) @@ -740,4 +758,26 @@ icon = 'icons/obj/closets/miningcar.dmi' closet_appearance = null open_sound = 'sound/effects/wooden_closet_open.ogg' - close_sound = 'sound/effects/wooden_closet_close.ogg' \ No newline at end of file + close_sound = 'sound/effects/wooden_closet_close.ogg' + +//RS ADD START +/obj/structure/closet/crate/secure/dungeon_trigger(mob/user) + var/datum/component/dungeon_mechanic/lock/ourlock = getlock() + if(ourlock) + if(ourlock.locked) + dungeon_unlock(user) + else if(!ourlock.onetime) + dungeon_lock(user) + else + if(locked) + dungeon_unlock(user) + else + dungeon_lock(user) + +/obj/structure/closet/crate/secure/dungeon_lock(mob/user) + . = ..() + set_locked(TRUE,user) + +/obj/structure/closet/crate/secure/dungeon_unlock(mob/user) + . = ..() + set_locked(FALSE,user) diff --git a/code/game/objects/structures/janicart.dm b/code/game/objects/structures/janicart.dm index aab4d17aec2..44e30b8e3b4 100644 --- a/code/game/objects/structures/janicart.dm +++ b/code/game/objects/structures/janicart.dm @@ -426,8 +426,8 @@ GLOBAL_LIST_BOILERPLATE(all_janitorial_carts, /obj/structure/janitorialcart) /obj/structure/bed/chair/janicart/relaymove(mob/living/user, direction) if(user.stat || user.stunned || user.weakened || user.paralysis) unbuckle_mob() - var/obj/item/key/jani/J = user.get_type_in_hands(/obj/item/key) //RS EDIT - if(J.key_id == "jani") //RS EDIT + var/obj/item/key/jani/J = user.get_type_in_hands(/obj/item/key/jani) //RS EDIT + if(J) //RS EDIT step(src, direction) update_mob() else @@ -499,4 +499,3 @@ GLOBAL_LIST_BOILERPLATE(all_janitorial_carts, /obj/structure/janitorialcart) icon = 'icons/obj/vehicles.dmi' icon_state = "keys" w_class = ITEMSIZE_TINY - key_id = "jani" //RS ADD diff --git a/code/game/objects/structures/simple_doors.dm b/code/game/objects/structures/simple_doors.dm index a32c905e38f..af6701727bb 100644 --- a/code/game/objects/structures/simple_doors.dm +++ b/code/game/objects/structures/simple_doors.dm @@ -17,15 +17,13 @@ var/knock_hammer_sound = 'sound/weapons/sonic_jackhammer.ogg' var/locked = FALSE //has the door been locked? -// lock_id = null //does the door have an associated key? //RS EDIT var/keysound = 'sound/items/toolbelt_equip.ogg' /obj/structure/simple_door/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) TemperatureAct(exposed_temperature) /obj/structure/simple_door/proc/TemperatureAct(temperature) - hardness -= material.combustion_effect(get_turf(src),temperature, 0.3) - CheckHardness() + take_damage(material.combustion_effect(get_turf(src),temperature, 0.3) * 10) //RS EDIT - Use the dang damage proc bro /obj/structure/simple_door/Initialize(mapload, var/material_name) . = ..() @@ -154,19 +152,11 @@ /obj/structure/simple_door/attackby(obj/item/weapon/W as obj, mob/user as mob) user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - /*//RS REMOVE - components - if(istype(W,/obj/item/weapon/simple_key)) - var/obj/item/weapon/simple_key/key = W - if(state) - to_chat(user,"\The [src] must be closed in order for you to lock it.") - else if(key.key_id != src.lock_id) - to_chat(user,"The [key] doesn't fit \the [src]'s lock!") - else if(key.key_id == src.lock_id) - visible_message("[user] [key.keyverb] \the [key] and [locked ? "unlocks" : "locks"] \the [src].") - locked = !locked - playsound(src, keysound,100, 1) - return - */ + if(SEND_SIGNAL(src, COMSIG_PARENT_ATTACKBY, W, user) & COMPONENT_CANCEL_ATTACK_CHAIN) //RS ADD START + return TRUE + if(islocked()) + if(W.getkey()) + return //RS ADD END if(istype(W,/obj/item/weapon/pickaxe) && breakable) var/obj/item/weapon/pickaxe/digTool = W visible_message("[user] starts digging [src]!") @@ -174,7 +164,7 @@ visible_message("[user] finished digging [src]!") Dismantle() else if(istype(W,/obj/item/weapon) && breakable) //not sure, can't not just weapons get passed to this proc? - hardness -= W.force/10 + take_damage(W.force) //RS EDIT - Use the dang damage proc bro visible_message("[user] hits [src] with [W]!") if(material == get_material_by_name("resin")) playsound(src, 'sound/effects/attackblob.ogg', 100, 1) @@ -182,7 +172,6 @@ playsound(src, 'sound/effects/woodcutting.ogg', 100, 1) else playsound(src, 'sound/weapons/smash.ogg', 50, 1) - CheckHardness() else if(istype(W,/obj/item/weapon/weldingtool) && breakable) var/obj/item/weapon/weldingtool/WT = W if(material.ignition_point && WT.remove_fuel(0, user)) @@ -193,9 +182,10 @@ /obj/structure/simple_door/bullet_act(var/obj/item/projectile/Proj) take_damage(Proj.damage/10) - CheckHardness() /obj/structure/simple_door/take_damage(var/damage) + if(islocked()) //RS ADD + return //RS ADD hardness -= damage/10 CheckHardness() @@ -208,19 +198,22 @@ else playsound(src, 'sound/weapons/smash.ogg', 50, 1) user.do_attack_animation(src) - hardness -= damage/10 - CheckHardness() + take_damage(damage) //RS EDIT - Use the dang damage proc bro /obj/structure/simple_door/proc/CheckHardness() if(hardness <= 0) Dismantle(1) /obj/structure/simple_door/proc/Dismantle(devastated = 0) + if(islocked()) //RS ADD + return //RS ADD material.place_dismantled_product(get_turf(src)) visible_message("The [src] is destroyed!") qdel(src) /obj/structure/simple_door/ex_act(severity = 1) + if(islocked()) //RS ADD + return //RS ADD switch(severity) if(1) Dismantle(1) @@ -228,11 +221,9 @@ if(prob(20)) Dismantle(1) else - hardness-- - CheckHardness() + take_damage(10) //RS EDIT - Use the dang damage proc bro if(3) - hardness -= 0.1 - CheckHardness() + take_damage(1) //RS EDIT - Use the dang damage proc bro return /obj/structure/simple_door/process() diff --git a/code/modules/admin/admin_verb_lists_vr.dm b/code/modules/admin/admin_verb_lists_vr.dm index eba9249f55b..af8aeb2aef2 100644 --- a/code/modules/admin/admin_verb_lists_vr.dm +++ b/code/modules/admin/admin_verb_lists_vr.dm @@ -188,8 +188,7 @@ var/list/admin_verbs_fun = list( /client/proc/admin_lighting_manager, // RS ADD: New Lighting Manager Panel (Lira, October 2025) /client/proc/tag_game, //RS ADD /client/proc/report_all_objectives, //RS ADD -// /client/proc/reset_multipoint_trigger, //RS ADD - /client/proc/toggle_dungeon_maker //RS ADD +// /client/proc/toggle_dungeon_maker //RS ADD ) diff --git a/code/modules/multiz/portals_vr.dm b/code/modules/multiz/portals_vr.dm index 9bd930cc4b6..d4a8f2de882 100644 --- a/code/modules/multiz/portals_vr.dm +++ b/code/modules/multiz/portals_vr.dm @@ -12,7 +12,6 @@ var/portal_enabled = FALSE var/static/list/event_portal_list = list() var/notify_ckey_once // Send a message to this person on next use - var/open = TRUE // If it's currently usable var/close_after_uses // Will autoclose after this many uses /obj/structure/portal_event/Initialize() @@ -53,25 +52,10 @@ return return -/* // RS REMOVE - Do not crash the server please and thank you -/obj/structure/portal_event/Crossed(AM as mob|obj) - if(istype(AM,/mob) && !(istype(AM,/mob/living))) - return //do not send ghosts, zshadows, ai eyes, etc - spawn(0) - src.teleport(AM) - return - return -*/ - /obj/structure/portal_event/attack_hand(mob/user as mob) if(!istype(user)) return - if(!target) - if(isliving(user)) - to_chat(user, "Your hand scatters \the [src]...") - qdel(src) //Delete portals which aren't set that people mess with. - else return //do not send ghosts, zshadows, ai eyes, etc - else if(isliving(user) || istype(user, /mob/observer/dead) && user?.client?.holder) //unless they're staff + if(isliving(user) || istype(user, /mob/observer/dead) && user?.client?.holder) //unless they're staff //RS EDIT spawn(0) src.teleport(user) @@ -143,14 +127,16 @@ if(target && istype(target, /obj/structure/portal_event) && tgui_alert(user, "Would you like portal's target portal to match this style?", "Both?", list("Yes", "No")) == "Yes") //RS EDIT target.icon_state = portal_icon_selection //RS EDIT -/obj/structure/portal_event/proc/teleport(atom/movable/M as mob|obj) - if(!portal_enabled && isliving(M)) //RS EDIT - to_chat(M, "\The [src] wavers as you pass through it... it seems to not accept you through... for now...") //RS EDIT - return //RS EDIT - if(isAI(M) || istype(M,/mob/observer/eye)) //RS EDIT - return //RS EDIT +/obj/structure/portal_event/proc/teleport(atom/movable/M as mob|obj) //RS EDIT START + if(isAI(M) || istype(M,/mob/observer/eye)) + return + + if(!portal_enabled && !isobserver(M)) + if(isliving(M)) + to_chat(M, "\The [src] wavers as you pass through it... it seems to not accept you through... for now...") + return // RS Add: Shells can't leave the station (Lira, October 2025) - if(ismob(M) && isrobot(M)) + if(isrobot(M)) var/mob/living/silicon/robot/R = M if(R.shell) var/turf/source_turf = get_turf(src) @@ -160,20 +146,21 @@ if(!source_turf || !target_turf || !isStationLevel(source_turf.z) || !isStationLevel(target_turf.z)) to_chat(R, "The [src] flares and refuses the remote shell.") return - if(istype(M, /obj/effect)) //sparks don't teleport - return - if (M.anchored&&istype(M, /obj/mecha)) - return - if (!target) - to_chat(M, "\The [src] scatters as you pass through it...") - qdel(src) - return - if (!istype(M, /atom/movable)) - return - if (!open) //RS EDIT START + + var/atom/where + if(target) + where = target + else + var/datum/component/dungeon_mechanic/pair/P = pick(get_dungeon_pair()) + if(P) + where = P.parent + if(!isatom(where)) + where = null + if (!where) to_chat(M, "\The [src] seems inert for now...") return - var/turf/ourturf = find_opposite_side_or_randomize(M,src,target) //RS EDIT START + + var/turf/ourturf = find_opposite_side_or_randomize(M,src,where) if(ourturf.check_density(TRUE,TRUE)) //Make sure there isn't a wall there return M.unbuckle_all_mobs(TRUE) @@ -196,11 +183,11 @@ return temptarg /obj/structure/portal_event/proc/close() - open = FALSE + portal_enabled = FALSE //RS EDIT alpha = 100 /obj/structure/portal_event/proc/open() - open = TRUE + portal_enabled = TRUE //RS EDIT alpha = initial(alpha) /obj/structure/portal_event/proc/post_crossed(atom/movable/M) @@ -344,3 +331,25 @@ toggle_portal() else return + +//RS ADD +/obj/structure/portal_event/dungeon_trigger(mob/user) + if(portal_enabled) + dungeon_lock(user) + else + dungeon_unlock(user) +/obj/structure/portal_event/dungeon_lock(mob/user) + portal_enabled = FALSE + density = FALSE + +/obj/structure/portal_event/dungeon_unlock(mob/user) + portal_enabled = TRUE + density = TRUE + visible_message(SPAN_NOTICE("\The [src] opens!!!"),runemessage = "! ! !") + +/obj/structure/portal_event/dungeon_pair() + . = ..() + if(!.) + dungeon_lock() + else + dungeon_unlock() diff --git a/icons/rogue-star/component_adder.dmi b/icons/rogue-star/component_adder.dmi index 93249af519ce095cc24bf96ab596928cfa108d90..2587cf36d193014d88f2857c3f60650c38835b8a 100644 GIT binary patch delta 479 zcmV<50U-Y51cU{UMggFaM{R#WVkTh=L5heltE4D1J)H>mL)8&sVo_>xW@;G`M#2;l zVPgNKEUI4TLiXT&X)WZM(0ewkCK~zYI)mA}{!ypXA65N9X|M5!M zdm@GZe`U#bNsKYLDW{ztT)Wd@;t&U8Gz47njW2K;h=Onv zaB%a_H31TJCwQ<~V~C}luHeUi;e>WsMrbD-LrV}8hVwyCsJnlfWF0=VZC_uQ>Ottk zcn9P`<#9}a@1|6HaDG)3xCj0|<~gu=!1E7LCRH^LXwwRz_Ty88%d2LD6%@+JpbJd$ zp1ejHqTJTKv?6vM&GK&Q&ZJ#yX7MoJ^!f9`f5LE7zJ>XY&wH51tcT8d{lZ7{;0LSE VBI!eQcRBz7002ovPDHLkV1nkZ;H>}v delta 447 zcmV;w0YLtQ1>^*fMgg9YM{R$0Y9&E61&Nu29R*TEgncDNnd#|7_z|j(2osA^lQUDx zh%geS5Vx7i3a)-G;D`eNPM3dXyq@Cz0004BNkltct?7!?O51B8#8!Fpg0V z^!)XJ0I`j4L%MHJ><{;mMDlF8uQ0bcxSvilk<|M`fx>Tx10@7+(I}@UqYFU+V1<^@ zd$8C8B@ccflX&r)TvC5vtM(p@)FUe>A@x7T}~cH1HUWqzR>0=Lz-D%cyph?ejqwU zKi9W%1)TW00^<6CY?!6e;L_^e6cun>^&n#S_76b#cfz;_^?fs0UlAe}ZJX=$9QEKr pRuq0!1^$8ktOx14p$d%jgFmn*9|OsT2=o8|002ovPDHLkV1hK-&kFzm diff --git a/icons/rogue-star/misc.dmi b/icons/rogue-star/misc.dmi index cbca6fae9fb1e0465317a266f2be9af80aea332d..07588c2c9c8ab2ffb6c41f8463563f1091cf62c1 100644 GIT binary patch literal 22757 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 z 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 Date: Wed, 27 May 2026 14:29:42 -0400 Subject: [PATCH 7/8] awoo --- vorestation.dme | 2 -- 1 file changed, 2 deletions(-) diff --git a/vorestation.dme b/vorestation.dme index bd8e298ce09..3fdc8bed485 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -1679,8 +1679,6 @@ #include "code\game\Rogue Star\waddle.dm" #include "code\game\Rogue Star\_Component\component_adder.dm" #include "code\game\Rogue Star\_Component\dungeon_component.dm" -#include "code\game\Rogue Star\_HELPERS\mobs.dm" -#include "code\game\Rogue Star\_HELPERS\teleport.dm" #include "code\game\Rogue Star\abilities\return.dm" #include "code\game\Rogue Star\abilities\search.dm" #include "code\game\Rogue Star\catborgs\catborgs.dm" From db563d6d83b440f07d9a83fdd582d13bb1e1cc97 Mon Sep 17 00:00:00 2001 From: VerySoft Date: Wed, 27 May 2026 18:26:27 -0400 Subject: [PATCH 8/8] 1826 --- code/game/machinery/door_control.dm | 1 + 1 file changed, 1 insertion(+) diff --git a/code/game/machinery/door_control.dm b/code/game/machinery/door_control.dm index dac967a7658..e7b1135d3ea 100644 --- a/code/game/machinery/door_control.dm +++ b/code/game/machinery/door_control.dm @@ -149,6 +149,7 @@ M.close() return +//RS ADD /obj/machinery/button/remote/blast_door/dungeon_trigger() trigger()